Skip to content

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

Open
@the-simian

Description

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions