Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Compiler Bug]: Cyclic dependencies when using the react-compiler and metro bundler can result in sometimes undefined hook imports #32440

Open
1 of 4 tasks
the-simian opened this issue Feb 21, 2025 · 3 comments
Assignees
Labels
Component: Optimizing Compiler Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug Type: Bug

Comments

@the-simian
Copy link

What kind of issue is this?

  • React Compiler core (the JS output is incorrect, or your app works incorrectly after optimization)
  • babel-plugin-react-compiler (build issue installing or using the Babel plugin)
  • eslint-plugin-react-compiler (build issue installing or using the eslint plugin)
  • react-compiler-healthcheck (build issue installing or using the healthcheck script)

Link to repro

will update

Repro steps

This particular issue can be difficult to reproduce.

The short version is that in some situations, when there is a cyclic dependency between two files using the metro bundler, when you enable the React compiler it can cause hooks to be imported as 'undefined'.

To reproduce this an expo Project is needed:

babel.config.js

module.exports = (api) => {
  api.cache(true);

  return {
    presets: [['babel-preset-expo', { jsxImportSource: 'nativewind' }], 'nativewind/babel'],
    plugins: [
      [
        'babel-plugin-react-docgen-typescript',
        {
          exclude: 'node_modules',
          include: 'components.*\\.tsx$',
        },
      ],
    ],
  };
};

Here is an example babel config, aside from nativewind it is a basic setup:

app.config.ts

  web: {
    bundler: 'metro',
    output: 'single',
    favicon: './assets/images/favicon.png',
  },
  experiments: {
    typedRoutes: true,
    reactCompiler: true,
  },

In the experiements section, you'll want to setReactCompiler:true. You can see the issue when you use the web output of the expo project.

If you've enabled the compiler, you'll see additional output in the console:

Experimental React Compiler is enabled.
Starting Metro Bundler
warning: Bundler cache is empty, rebuilding (this may take a minute)

You should run the development build, and not expo go if possible.

Here are the versions of critical dependencies that can reproduce the issue currently:

package.json

  "expo": "~52.0.36",
  "react": "18.3.1",
  "react-compiler-runtime": "^19.0.0-beta-21e868a-20250216",
  "react-dom": "18.3.1",

at the time of creating this issue, this should be the latest version of the react-compiler-runtime and also the latest version of expo 52, react is at 18.

Now, to show some code that can express the issue:

in one file you want to write a function that uses a hook , this may or may not be a hook or provider or just a simple function:

external-function-file.tsx

import { useMyHook } from './SomeFile';

export function externalFunction() {
  const { datum } = useMyHook();
  return datum ;
}

in the other file, you need to call this hook in some way, but you've also defined the same hook in this file

SomeFile.tsx

type MyContext = { datum: string; }

export const myContext = createContext<MyContext | undefined>(undefined);

export useMyHook() {
  const context = useContext(myContext);
  return context;
}

export function SomeComponent(){
  const _datum = externalFunction();
   return <View><Text>{_datum}</Text></View>
}

This is the gist of what can get a reliable break. You will see an error like:

Uncaught Error
useMyHook is not a function

however, when you inspect the source code, its clearly defined (even in the source output in the browser), and also passes linters, such as eslint-plugin-react-compiler, as well as type checks in ts.

How often does this bug happen?

Sometimes

What version of React are you using?

18.3.1

What version of React Compiler are you using?

^19.0.0-beta-21e868a-20250216

@the-simian the-simian added Component: Optimizing Compiler Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug Type: Bug labels Feb 21, 2025
@the-simian
Copy link
Author

the-simian commented Feb 21, 2025

Note: I wanted to get a straightforward explanation of what was observed based on @poteto 's suggestion, I'm aware that an issue like this will require a repro repo, with minimal deps. I'll try to stay active on this issue and help with this, but wanted to open the issue earnestly to get it documented.

Also a point of clarity on "how often does this bug happen? "sometimes"?

When there is an Uncaught Error, where an imported hook is undefined, it will always break reliably; however, not every cyclic dependency will always break in my experience. Sometimes in one place the import will work as normally expected, but in another it will cause an Uncaught Error. Disabling the react compiler via the app config will result in the expected behavior where you can import hooks anywhere and everything works as expected.

Another thing to know: putting all the necessary function declarations in a single, monolithic file can make the issue go away. The issue is occurs when you break things up and move them to multiple files via import/export

@poteto poteto self-assigned this Feb 21, 2025
@poteto poteto pinned this issue Feb 21, 2025
@poteto poteto unpinned this issue Feb 21, 2025
@poteto
Copy link
Member

poteto commented Feb 21, 2025

I was able to get a minimal repro: https://github.com/poteto/repro-32440. Not sure if it's an Expo or Metro bug, but I'll file an issue with Expo first so they can triage.

@poteto
Copy link
Member

poteto commented Feb 21, 2025

expo/expo#35100

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component: Optimizing Compiler Status: Unconfirmed A potential issue that we haven't yet confirmed as a bug Type: Bug
Projects
None yet
Development

No branches or pull requests

2 participants