Metro: ReferenceError: Property 'React' doesn't exist after upgrade react-native to 0.64.0

Created on 14 Mar 2021  路  9Comments  路  Source: facebook/metro

Do you want to request a feature or report a bug?
Report a bug and suggestion to fix it.

What is the current behavior?
I try to upgrade to 0.64.0 from 0.63.4 which include react 17 with the New JSX Transform.
After I remove React import from all my component files.
I got below error when running the app yarn android with yarn start --reset-cache

ReferenceError: Property 'React' doesn't exist

This error is located at:
    in App (at renderApplication.js:47)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:107)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:134)
    in AppContainer (at renderApplication.js:40), js engine: hermes
ReferenceError: Property 'React' doesn't exist

This error is located at:
    in App (at renderApplication.js:47)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:107)
    in RCTView (at View.js:34)
    in View (at AppContainer.js:134)
    in AppContainer (at renderApplication.js:40), js engine: hermes

If the current behavior is a bug, please provide the steps to reproduce and a minimal repository on GitHub that we can yarn install and yarn test.

  1. initialize fresh react-native 0.63.4 npx react-native init AwesomeTSProject --template react-native-template-typescript
  2. use upgrade-helper to upgrade to react-native 0.64.0
  3. remove any import React from 'react'; from component file.
  4. error will show ReferenceError: Property 'React' doesn't exist

What is the expected behavior?
As from React blog about New JSX Transform, it needs additional config in babel.config.js

// If you're using @babel/plugin-transform-react-jsx
{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "runtime": "automatic"
    }]
  ]
}
  • After I add this into my babel.config.js and yarn start --reset-cache and yarn android the error was gone.

Please provide your exact Metro configuration and mention your Metro, node, yarn/npm version and operating system.

  • metro-react-native-babel-preset 0.64.0
  • node 14.16.0
  • yarn 1.22.5
/**
 * Metro configuration for React Native
 * https://github.com/facebook/react-native
 *
 * @format
 */

module.exports = {
  transformer: {
    getTransformOptions: async () => ({
      transform: {
        experimentalImportSupport: false,
        inlineRequires: true,
      },
    }),
  },
};

Most helpful comment

Because, metro-react-native-babel-preset includes @babel/plugin-transform-react-jsx by default. If you add it to plugins, it would be duplicated.
https://github.com/facebook/metro/blob/6cb3ec88f707b1beb78462be11195b5d496e3aad/packages/metro-react-native-babel-preset/src/configs/main.js#L59-L61

But set useTransformReactJSXExperimental: true is not enough. To use new JSX transformation, you have to pass runtime: 'automatic' to @babel/plugin-transform-react-jsx too (runtime is classic by default).
https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#manual-babel-setup

So you might want to change your babel.config.js to:

module.exports = {
  presets: [['module:metro-react-native-babel-preset', { useTransformReactJSXExperimental: true }]],
  plugins: [
    [
      '@babel/plugin-transform-react-jsx',
      {
        runtime: 'automatic',
      },
    ],
  ]
}

All 9 comments

I'm having a similar issue using typescript and "jsx": "react-jsx", in tsconfig.json.

Setting:

{
  "plugins": [
    ["@babel/plugin-transform-react-jsx", {
      "runtime": "automatic"
    }]
  ]
}

Gives me the error:
Duplicate __self prop found. You are most likely using the deprecated transform-react-jsx-self Babel plugin. Both __source and __self are automatically set when using the automatic runtime. Please remove transform-react-jsx-source and transform-react-jsx-self from your Babel config.

Because, metro-react-native-babel-preset includes @babel/plugin-transform-react-jsx by default. If you add it to plugins, it would be duplicated.
https://github.com/facebook/metro/blob/6cb3ec88f707b1beb78462be11195b5d496e3aad/packages/metro-react-native-babel-preset/src/configs/main.js#L59-L61

But set useTransformReactJSXExperimental: true is not enough. To use new JSX transformation, you have to pass runtime: 'automatic' to @babel/plugin-transform-react-jsx too (runtime is classic by default).
https://reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html#manual-babel-setup

So you might want to change your babel.config.js to:

module.exports = {
  presets: [['module:metro-react-native-babel-preset', { useTransformReactJSXExperimental: true }]],
  plugins: [
    [
      '@babel/plugin-transform-react-jsx',
      {
        runtime: 'automatic',
      },
    ],
  ]
}

I'm having the same issue:

ReferenceError: Can't find variable: React

Thanks for the suggestion, @moonlight8978, but I unfortunately still can't get mine working. I originally started my project with Expo but have since ejected to bare workflow. My babel.config.js, up until now, looked like this:

module.exports = function (api) {
 api.cache(true)

 const presets = ['babel-preset-expo']
 const plugins = ['react-native-reanimated/plugin']

 return {
   presets,
   plugins,
}

Since updating to React Native 0.64 this morning, and following the steps in the New JSX Transform docs (including running the codemod), I've had this issue. I've tried the following in my babel.config.js (after adding the metro-react-native-babel-preset package, which I didn't have):

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['react-native-reanimated/plugin'],
}

Despite this, I still get the _ReferenceError: Can't find variable: React_ error. Hopefully there's something I'm missing but I'm relatively new to this and it's already been a little confusing since ejecting from Expo a while ago.

_Update: I was missing the [] around the individual preset. Adding that made it work so thanks again._

Thanks @moonlight8978. Your suggestion of adding useTransformReactJSXExperimental: true to the metro preset configs worked like a charm.

module.exports = function (api) {
 api.cache(true)

 const presets = ['babel-preset-expo']
 const plugins = ['react-native-reanimated/plugin']

 return {
   presets,
   plugins,
}

@darrylyoung I'm not familiar with expo, but I tried to take a look at babel-preset-expo.
It seems like there is no way that I can pass useTransformReactJSXExperimental option to metro-react-native-babel-preset.
https://github.com/expo/expo/blob/d9ce4c1eceaeaadebb578764d53fcec76fa51b11/packages/babel-preset-expo/index.js#L32-L48

I believe that the only way for using React 17 new JSX transformation with expo is remove babel-preset-expo and add other plugins/presets manually.

Hi, @moonlight8978. 馃憢

I believe that the only way for using React 17 new JSX transformation with expo is remove babel-preset-expo and add other plugins/presets manually.

Thanks for the message. I checked babel-preset-expo and it looks like it extends metro-react-native-babel-preset but adds a couple of Expo-specific features. Despite having ejected to the bare workflow, I'm still using @expo/vector-icons. The problem, I think, is that it's pinning an old version of metro-react-native-babel-preset but Expo SDK 41 is due out at the end of the month so hopefully they'll update it.

Regardless, I tried removing babel-preset-expo and had trouble with loading icon fonts (they mention here how the preset handles that). What I ended up doing is this:

module.exports = {
  presets: [
    ['babel-preset-expo'],
    ['module:metro-react-native-babel-preset', { useTransformReactJSXExperimental: true }],
  ],
  plugins: [
    ['react-native-reanimated/plugin'],
    [
      '@babel/plugin-transform-react-jsx',
      {
        runtime: 'automatic',
      },
    ],
  ],
}

I don't have that much experience with Babel so I'm not sure how _acceptable_ this is but I can say that by doing this, I no longer get the _Can't find variable: React_ error and I have no problems with the icon fonts, etc. Maybe, once babel-preset-expo is updated to support the New JSX Transform, I can remove one of the two. Thanks again for your message.

Solution from https://github.com/facebook/metro/issues/646#issuecomment-799174473 is working on development for me, but when I make production build it doesn't work with error "Can't find variable: React"

Hello, I updated my babel.config.js using the prescribed method in #646 (comment) but I'm getting the following error:

error: index.js: [BABEL] /Users/jmf/LIVAFORTIS/<AppDirName>/index.js: Unknown option: .useTransformReactJSXExperimental. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.
- Maybe you meant to use
"preset": [
  ["module:metro-react-native-babel-preset", {
  "useTransformReactJSXExperimental": true
}]
]
To be a valid preset, its name and options should be wrapped in a pair of brackets

Here's my babel.config.js for reference:

module.exports = {
  presets: [
    'module:metro-react-native-babel-preset',
    {
      'useTransformReactJSXExperimental': true,
    },
  ],
  plugins: [
    'babel-plugin-styled-components',
    [
      'module:react-native-dotenv',
      {
        moduleName: '@env',
        path: '.env',
        blacklist: null,
        whitelist: null,
        safe: true,
        allowUndefined: true,
      },
    ],
    [
      require.resolve('babel-plugin-module-resolver'),
      {
        root: ['.'],
        alias: {
          assets: './src/common/assets',
          components: './src/common/components',
          localization: './src/common/localization',
          navigation: './src/navigation',
          screens: './src/screens',
          utils: './src/utils',
        },
      },
    ],
    [
      '@babel/plugin-transform-react-jsx',
      {
        runtime: 'automatic',
      },
    ],
  ],
  env: {
    production: {},
  },
}

I tried:

  • editing the file in a basic text editor
  • adding babel.config.js to my .eslintignore and .prettierignore
  • resetting transform cache
  • deleting node_modules, yarn.lock, Podfile.lock, Pods/ and reinstalling
  • completely deleting/reinstalling app from simulator (npx react-native run-ios)
  • updating "metro-react-native-babel-preset": "^0.66.0" (previously 0.64.0)

Any help would be appreciated!

@jmfigueroa Your presets is in wrong format. If you want to pass options to a preset, you have to use the array syntax with:

  • the first element is the name of the preset
  • the second element is the options
presets: [
  [
    'module:metro-react-native-babel-preset',
    {
      'useTransformReactJSXExperimental': true,
    }
  ]
]

not

presets: [
  'module:metro-react-native-babel-preset',
  {
    'useTransformReactJSXExperimental': true,
  },
]

it makes babel think that you are using 2 presets

  • module:metro-react-native-babel-preset
  • and another one is { 'useTransformReactJSXExperimental': true }
Was this page helpful?
0 / 5 - 0 ratings