简体   繁体   English

升级到 react@18 和 react-dom@18 失败

[英]Upgrading to react@18 and react-dom@18 fails

I have an existing React 17 app that started life as a create-react-app output with the TypeScript option.我有一个现有的 React 17 应用程序,它以带有 TypeScript 选项的 create-react-app 输出开始使用。 I wanted to change package.json to include react@18.0.0 and react-dom@18.0.0 and then start the app, and that step worked fine.我想更改 package.json 以包含react@18.0.0react-dom@18.0.0 ,然后启动应用程序,该步骤运行良好。

Then I tried to change the index.tsx file the way that the React 18 migration instructions tell you to do it, by changing the relevant segment in index.tsx to:然后我尝试按照 React 18 迁移说明告诉您的方式更改index.tsx文件,方法是将index.tsx中的相关段更改为:

ReactDOM.createRoot(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

and either npm run build or npm run start gives me compile errors, saying that createRoot is not defined.并且npm run buildnpm run start给我编译错误,说 createRoot 未定义。 This is probably due to the fact that I have dependencies on @types/react@17.0.43 and @types/react-dom@17.0.14 .这可能是因为我依赖于@types/react@17.0.43@types/react-dom@17.0.14 There do not seem to be any updated versions of these files from DefinitelyTyped for React 18 yet. DefinitelyTyped for React 18 似乎还没有这些文件的任何更新版本。

So, I went back and created a brand new app with npx create-react-app react18 --template typescript .所以,我回去用npx create-react-app react18 --template typescript创建了一个全新的应用程序。 It now wants react@18 and react-dom@18 , but still has the old @types/ dependencies.它现在需要react@18react-dom@18 ,但仍然有旧的@types/依赖项。 It also gives the same compile error when you switch from render() to createRoot().当您从 render() 切换到 createRoot() 时,它也会给出相同的编译错误。

As one further step, I asked myself "what happens if we leave out TypeScript?"作为下一步,我问自己“如果我们不使用 TypeScript 会怎样?” Created yet another app with npx create-react-app react18js .使用npx create-react-app react18js创建了另一个应用程序。 It depends on react@18.0.0 and react-dom@18.0.0 as expected, but still generates ReactDOM.render() in its index.js output.它按预期依赖于react@18.0.0react-dom@18.0.0 ,但仍会在其index.js输出中生成ReactDOM.render()

That works, but change ReactDOM.render to ReactDOM.createRoot and you get no compile errors, but you also get no visible output if you try npm run start .这行得通,但是将ReactDOM.render更改为ReactDOM.createRoot并且不会出现编译错误,但是如果您尝试npm run start也看不到输出。 It doesn't seem to render the <App/> component at all.它似乎根本不呈现<App/>组件。

Am I missing something?我错过了什么吗? Are the instructions not quite adequate?说明还不够充分吗?

The React documentation explains how to use ReactDOM.createRoot , which is not a direct/drop-in replacement for ReactDOM.render . React 文档解释了如何使用ReactDOM.createRoot ,它不是ReactDOM.render的直接/直接替代品。

What the doc doesn't cover is the TypeScript way of using it.文档没有涵盖的是 TypeScript 的使用方式。

I know this solution is a bit overkill compared to others here that uses type assertion ( as HTMLElement ) on the DOM element, but it fully utilizes the benefit of strict typing (eg if someone accidentally removed the #root element in index.html)我知道这个解决方案与在 DOM 元素上使用类型断言( as HTMLElement )的其他解决方案相比有点矫枉过正,但它充分利用了严格类型的好处(例如,如果有人不小心删除了 index.html 中的#root元素)

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'

const el = document.getElementById('root')
if (el === null) throw new Error('Root container missing in index.html')

const root = ReactDOM.createRoot(el)
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
)

You need first to import ReactDOM from react-dom/client and then create the root element from the element id=root using createRoot and then use .render to render the content of the <App/> component inside the created root element like this:您需要首先从react-dom/client导入ReactDOM ,然后使用createRoot从元素id=root创建根元素,然后使用.render在创建的根元素中呈现<App/>组件的内容,如下所示:

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

let root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>,
);

Changing the import statement is the crucial first step in getting this to work, followed by the fact that the parameters for createRoot() are different.更改 import 语句是使其工作的关键第一步,其次是 createRoot() 的参数不同。 For the record, this is what you need to change your create-react-app src/index.tsx (or src/index.jsx if not using Typescript) to look like:作为记录,您需要将 create-react-app src/index.tsx (或src/index.jsx ,如果不使用 Typescript)更改为如下所示:

import React from 'react';
import ReactDOMClient from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOMClient.createRoot(document.getElementById("root") as HTMLElement);
root.render(
    <React.StrictMode>
        <App />
    </React.StrictMode>
);

Next step is to clean up tests, where the render() method from @testing-library/react still uses the old approach -- but that's a separate topic.下一步是清理测试,@testing-library/react 中的 render() 方法仍然使用旧方法——但这是一个单独的主题。

Try this code试试这个代码

import React from 'react';
import ReactDOM from "react-dom/client";

import App from './App';

const root = ReactDOM.createRoot(document.getElementById("root"));

root.render(
  <StrictMode>
    <App />
  </StrictMode>
);

In the meantime @types/react and @types/react-dom updates for React 18, we need to load the types manually.同时@types/react@types/react-dom更新 React 18,我们需要手动加载类型。

To load the types declared in an actual project, there are three ways.加载实际项目中声明的类型,有以下三种方式。 The easiest one, if your tsconfig.json already has a "types" array in the "compilerOptions" section, is to add "react/next" to the "types" array.最简单的方法是,如果您的tsconfig.json"compilerOptions"部分中已经有一个"types"数组,则将"react/next"添加到"types"数组中。

Alternatively, a specific import syntax can to be used from a typescript file.或者,可以从 typescript 文件中使用特定的导入语法。 This module does not exist in reality, which is why the {} is important:这个模块在现实中并不存在,这就是为什么 {} 很重要:

import {} from 'react/next'

It is also possible to include it through a triple-slash reference:也可以通过三斜杠引用来包含它:

/// <reference types="react/next" />

Either the import or the reference only needs to appear once, anywhere in the project.导入或引用只需要在项目中的任何位置出现一次。

How to Upgrade to React 18如何升级到 React 18

  • React 18 introduces a new root API which provides better ergonomics for managing roots. React 18 引入了一个新的根 API,它为管理根提供了更好的人体工程学。 The new root API also enables the new concurrent renderer, which allows you to opt-into concurrent features新的根 API 还启用了新的并发渲染器,它允许您选择加入并发功能
  • Installing安装

To install the latest version of React:要安装最新版本的 React:

npm install react react-dom

Or if you're using yarn:或者,如果您使用的是纱线:

yarn add react react-dom
// Before
import { render } from 'react-dom';
const container = document.getElementById('app');
render( 
      <React.StrictMode>
        <App />
      </React.StrictMode>
     ,container);

// After
import { createRoot } from 'react-dom/client';
const container = document.getElementById('root') as HTMLElement;
const root = ReactDOM.createRoot(container); 

root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>,
);

React 18 update guide React 18 更新指南

import React, { StrictMode, useEffect } from "react";
import App from "./components/shared/App";
import reportWebVitals from "./reportWebVitals";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom";


function AppWithCallbackAfterRender() {
  useEffect(() => {
    console.log("rendered");
  });
  return (
    <StrictMode>
      <App />
    </StrictMode>
  );
}

const container = document.getElementById("app")
if (container === null) throw new Error("Root container missing in index.html");
const root = createRoot(container);
root.render(<AppWithCallbackAfterRender />);// createRoot(container!) if you use TypeScript

The official reactjs blog explains how to convert your code to React 18, but it lacks instruction on how to update your DefinitelyTyped resources for TypeScript. 官方 reactjs 博客解释了如何将您的代码转换为 React 18,但它缺少有关如何更新 TypeScript 资源的说明。 I had to update the @types/react-dom package:我不得不更新@types/react-dom package:

npm install @types/react-dom@17.0.14

This updated type definition will contain the updated declarations for React 18. Here's my updated index.tsx file:这个更新的类型定义将包含 React 18 的更新声明。这是我更新的index.tsx文件:

import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App';

// React 18        
const container = document.getElementById('root') as HTMLElement;
const root = createRoot(container);
root.render(
  <React.StrictMode>
      <App />
  </React.StrictMode>
);

React officially post on their blogHow to Upgrade to React 18 but there have missing instructions. React 在其博客How to Upgrade to React 18上正式发布,但缺少说明。

First of all, you have to install the latest version of React:首先,你必须安装最新版本的 React:

npm install react react-dom

Or if you're using yarn: (my personal opinion yarn is better)或者如果您使用纱线:(我个人认为纱线更好)

yarn add react react-dom

then go to index.js remove old code and paste it.然后 go 到index.js删除旧代码并粘贴。

import React from "react";
import { createRoot } from "react-dom/client";

import App from "./App";

const container = document.getElementById("root");
const root = createRoot(container);
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

I met the same question with you.我和你遇到了同样的问题。 The reason is that react version in package.json is ^18, but file content for our use in node_modules/@types/react is for React V17.原因是 package.json 中的 react 版本是 ^18,但我们在 node_modules/@types/react 中使用的文件内容是针对 React V17 的。

try:尝试:

  1. delete node_modules/@types/react, node_modules/@types/react-dom two files删除 node_modules/@types/react, node_modules/@types/react-dom 两个文件
  2. npm i --save-dev @types/react@latest npm i --save-dev @types/react-dom@latest npm i --save-dev @types/react@latest npm i --save-dev @types/react-dom@latest

It's useful for me:)这对我有用:)

Because you are using typescript You need to upgrade @types/react @types/react-dom as well.因为您使用的是 typescript 您还需要升级 @types/react @types/react-dom。

npm uninstall @types/react @types/react-dom


npm i -D @types/react @types/react-dom


Or you can update them directly to the latest version或者您可以直接将它们更新到最新版本

Then update your index.js as follow:然后更新你的 index.js 如下:

import React from 'react';

import { createRoot } from 'react-dom/client';


const root = createRoot(document.getElementById('root') as HTMLDivElement);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
);

Also consider close and open your editor if it is needed如果需要,还可以考虑关闭并打开您的编辑器

You have to define root element with createRoot and render the component.您必须使用 createRoot 定义根元素并渲染组件。

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM