【问题标题】:Upgrading to react@18 and react-dom@18 fails升级到 react@18 和 react-dom@18 失败
【发布时间】:2022-07-01 19:31:31
【问题描述】:

我有一个现有的 React 17 应用程序,它以带有 TypeScript 选项的 create-react-app 输出开始。我想更改 package.json 以包含 react@18.0.0react-dom@18.0.0 然后启动应用程序,这一步工作正常。

然后我尝试按照 React 18 迁移说明告诉您的方式更改 index.tsx 文件,方法是将 index.tsx 中的相关段更改为:

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

npm run buildnpm run start 给我编译错误,说 createRoot 没有定义。这可能是因为我依赖于@types/react@17.0.43@types/react-dom@17.0.14。对于 React 18,DefinitelyTyped 中的这些文件似乎还没有任何更新版本。

所以,我回去用npx create-react-app react18 --template typescript 创建了一个全新的应用程序。它现在需要react@18react-dom@18,但仍然有旧的@types/ 依赖项。当您从 render() 切换到 createRoot() 时,它也会给出相同的编译错误。

作为进一步的步骤,我问自己“如果我们省略 TypeScript 会发生什么?”使用npx create-react-app react18js 创建了另一个应用程序。正如预期的那样,它依赖于react@18.0.0react-dom@18.0.0,但仍会在其index.js 输出中生成ReactDOM.render()

这可行,但将ReactDOM.render 更改为ReactDOM.createRoot 不会出现编译错误,但如果尝试npm run start,也不会出现可见输出。它似乎根本没有渲染 &lt;App/&gt; 组件。

我错过了什么吗?说明还不够充分吗?

【问题讨论】:

  • 可以尝试在原始文件中删除并重新生成您的 package-lock.json 文件。
  • 请注意,这实际上并不是您使用 createRoot 的方式:reactjs.org/blog/2022/03/08/…
  • @Ross 试过了,也删除了 node_modules -- 没有变化。
  • @jonrsharpe - 将逻辑更改为此使其在 JS 版本中工作。在打字稿版本中,构建仍然失败,并在导入时显示“TS2305:模块'“react-dom”'没有导出的成员'createRoot'”。
  • 不,它没有。正确的导入来自"react-dom/client",它是:github.com/DefinitelyTyped/DefinitelyTyped/blob/…

标签: reactjs typescript


【解决方案1】:

React 文档解释了如何使用ReactDOM.createRoot,它不是ReactDOM.render 的直接/直接替代品。

文档没有涵盖的是 TypeScript 的使用方式。

我知道这个解决方案与其他在 DOM 元素上使用类型断言 (as HTMLElement) 的解决方案相比有点矫枉过正,但它充分利用了严格类型的好处(例如,如果有人不小心删除了 #root 元素index.html)

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>
)

【讨论】:

    【解决方案2】:

    您需要先从react-dom/client 导入ReactDOM 然后使用createRoot从元素id=root创建根元素,然后使用.render在创建的根元素中渲染&lt;App/&gt;组件的内容,如下所示:

    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>,
    );
    

    【讨论】:

      【解决方案3】:

      更改 import 语句是让它工作的关键第一步,其次是 createRoot() 的参数不同。作为记录,您需要将 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>
      );
      

      下一步是清理测试,@testing-library/react 中的 render() 方法仍然使用旧方法 - 但这是一个单独的主题。

      【讨论】:

        【解决方案4】:

        试试这个代码

        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>
        );
        

        【讨论】:

          【解决方案5】:

          与此同时,@types/react@types/react-dom 更新 React 18,我们需要手动加载类型。

          要加载实际项目中声明的类型,有三种方式。最简单的, 如果您的tsconfig.json"compilerOptions" 部分中已经有一个"types" 数组, 就是将"react/next"添加到"types"数组中。

          或者,可以从打字稿文件中使用特定的导入语法。 该模块在现实中并不存在,这就是 {} 很重要的原因:

          import {} from 'react/next'
          

          也可以通过三斜杠引用来包含它:

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

          导入或引用只需要在项目中的任何位置出现一次。

          【讨论】:

            【解决方案6】:

            如何升级到 React 18

            • React 18 引入了一个新的根 API,它提供了更好的人体工程学 用于管理根。新的根 API 还启用了新的并发 渲染器,允许您选择加入并发功能
            • 正在安装

            要安装最新版本的 React:

            npm install react react-dom
            

            或者如果您使用的是纱线:

            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

            【讨论】:

              【解决方案7】:

              official reactjs blog 解释了如何将您的代码转换为 React 18,但它缺少关于如何为 TypeScript 更新绝对类型资源的说明。我不得不更新 @types/react-dom 包:

              npm install @types/react-dom@17.0.14
              

              这个更新的类型定义将包含 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>
              );
              

              【讨论】:

                【解决方案8】:

                React 在他们的博客How to Upgrade to React 18 上正式发布,但缺少说明。

                首先,你必须安装最新版本的 React:

                npm install react react-dom
                

                或者如果你使用纱线:(我个人认为纱线更好)

                yarn add react react-dom
                

                然后去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>
                );
                

                【讨论】:

                  【解决方案9】:

                  我遇到了同样的问题。原因是 package.json 中的 react 版本是 ^18,但我们在 node_modules/@types/react 中使用的文件内容是针对 React V17 的。

                  试试:

                  1. 删除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

                  这对我很有用:)

                  【讨论】:

                    【解决方案10】:

                    因为你使用的是 typescript 你也需要升级 @types/react @types/react-dom。

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

                    或者您可以直接将它们更新到最新版本

                    然后更新你的 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>
                    );
                    
                    

                    如果需要,还可以考虑关闭并打开您的编辑器

                    【讨论】:

                      【解决方案11】:

                      您必须使用 createRoot 定义根元素并渲染组件。

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

                      【讨论】:

                        猜你喜欢
                        • 2022-06-15
                        • 2022-12-06
                        • 2012-05-16
                        • 2023-02-10
                        • 2022-11-14
                        • 2017-09-26
                        • 1970-01-01
                        • 2022-07-06
                        • 2023-02-19
                        相关资源
                        最近更新 更多