expo: [SDK46][Web] ReactDOM.render is no longer supported in React 18

Summary

When I upgrade an existing projet to SDK 46 (React 18) or when I create a new blank project, and running on web I have this error: ‘Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it’s running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

managed

What platform(s) does this occur on?

Web

SDK Version (managed workflow only)

46

Environment

expo-env-info 1.0.5 environment info: System: OS: macOS 11.6 Shell: 5.8 - /bin/zsh Binaries: Node: 16.14.0 - ~/.nvm/versions/node/v16.14.0/bin/node Yarn: 1.22.19 - ~/.nvm/versions/node/v16.14.0/bin/yarn npm: 8.3.1 - ~/.nvm/versions/node/v16.14.0/bin/npm Watchman: 2022.03.21.00 - /usr/local/bin/watchman Managers: CocoaPods: 1.11.3 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: DriverKit 21.2, iOS 15.2, macOS 12.1, tvOS 15.2, watchOS 8.3 IDEs: Android Studio: 4.2 AI-202.7660.26.42.7486908 Xcode: 13.2.1/13C100 - /usr/bin/xcodebuild npmPackages: @expo/webpack-config: ^0.17.0 => 0.17.0 expo: ~46.0.2 => 46.0.2 react: 18.0.0 => 18.0.0 react-dom: 18.0.0 => 18.0.0 react-native: 0.69.3 => 0.69.3 react-native-web: ~0.18.7 => 0.18.7 Expo Workflow: managed

Reproducible demo

Juste clone the default tabs or blank template and run on web.

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 34
  • Comments: 24 (6 by maintainers)

Commits related to this issue

Most upvoted comments

Giving credit to all those above who started me on this solution, i did this:

  • Change “main” in package.json from "node_modules/expo/AppEntry.js" to a custom file: "main": "entry.js"
  • Created entry.js
import "expo/build/Expo.fx";
import { Platform } from "react-native";
import { registerRootComponent } from "expo";
import { activateKeepAwake } from "expo-keep-awake";
import { createRoot } from "react-dom/client";
import App from "./App"; /* CHANGE THE PATH BASED ON WHERE YOURS IS LOCATED */

if (__DEV__) {
  activateKeepAwake();
}

if (Platform.OS === "web") {
  const root = createRoot(
    document.getElementById("root") ?? document.getElementById("main")
  );
  root.render(<App />);
} else {
  registerRootComponent(App);
}

I haven’t tested it too too much but what i’ve tested thus far has worked. I’m doing an ios/android/web build and all 3 seem to be working on SDK 47. Hope this helps!

Basically identical to @JamesHemery’s solution, here’s what I did for my web-only app (may or may not work fine for non-web apps):

  • Change “main” in package.json from "node_modules/expo/AppEntry.js" to a custom file: "main": "entry.js"

  • Created entry.js

import 'expo/build/Expo.fx';
import { AppRegistry, Platform } from 'react-native';
import withExpoRoot from 'expo/build/launch/withExpoRoot';

import App from './src/App';
import { createRoot } from "react-dom/client";

AppRegistry.registerComponent('main', () => withExpoRoot(App));
if (Platform.OS === 'web') {
  const rootTag = createRoot(document.getElementById('root') ?? document.getElementById('main'));
  const RootComponent = withExpoRoot(App);
  rootTag.render(<RootComponent />);
}
  • wrapped my app with <React.StrictMode>

Temporary fixed by creating a custom index file or by patching registerRootComponent function (following React doc), but IDK if it’s the good way for an Expo patch :

import 'expo/build/Expo.fx';
import { AppRegistry, Platform } from 'react-native';
import { createRoot } from 'react-dom/client';
import { activateKeepAwake } from 'expo-keep-awake';
import * as Sentry from 'sentry-expo';
import withExpoRoot from 'expo/build/launch/withExpoRoot';
import App from './src/App';

if (__DEV__) {
  activateKeepAwake();
}

AppRegistry.registerComponent('main', () => withExpoRoot(App));
if ('web' === Platform.OS) {
  const rootTag = createRoot(document.getElementById('root') ?? document.getElementById('main'));

  const RootComponent = withExpoRoot(App);
  rootTag.render(<RootComponent />);
}

Update for SDK 47 :

import 'expo/build/Expo.fx';
import registerRootComponent from 'expo/build/launch/registerRootComponent';
import { createRoot } from 'react-dom/client';
import { createElement } from 'react';
import { Platform } from 'react-native';
import App from './src/App';

// @see https://github.com/expo/expo/issues/18485
if ('web' === Platform.OS) {
  const rootTag = createRoot(document.getElementById('root') ?? document.getElementById('main'));
  rootTag.render(createElement(App));
} else {
  registerRootComponent(App);
}

same issue +1 Look forward to the official solution for the warning.

The workaround from above no longer works in SDK 47 because withExpoRoot no longer exists.

Also related to this PR to fully embrace the concurrent React

https://github.com/necolas/react-native-web/pull/2330

==============

Update 9/Nov/2022

And it seems like the above PR is deprecated according to the latest comment. image

I am really sorry to at you( @necolas ), I just want to know if I am tracking the correct PR, is this https://github.com/necolas/react-native-web/pull/2330 the key to addressing this issue? or i should look at something else? 😃 Thanks in advance.

This is not an error, it’s a warning. The API still works. React Native and React Native for Web do not officially support the new APIs in React 18 yet

same issue here, thanks for help, I’ll wait for the solution here, thanks

@JamesHemery Hehe. I am bootstrapping a new project - and 30m after you posted this, I was thinking about doing the same thing. ^ But I realized the amount of foreign code/imports I would have to fork into my app and decided I would live with the annoying error until it gets fixed upstream. Thank you for the diligence in reporting!

Update for SDK 47 :

import 'expo/build/Expo.fx';
import registerRootComponent from 'expo/build/launch/registerRootComponent';
import { createRoot } from 'react-dom/client';
import { createElement } from 'react';
import { Platform } from 'react-native';
import App from './src/App';

// @see https://github.com/expo/expo/issues/18485
if ('web' === Platform.OS) {
  const rootTag = createRoot(document.getElementById('root') ?? document.getElementById('main'));
  rootTag.render(createElement(App));
} else {
  registerRootComponent(App);
}

Thank you James. This worked for me.

same issue +1