【问题标题】:React 18: Hydration failed because the initial UI does not match what was rendered on the serverReact 18: Hydration 失败,因为初始 UI 与服务器上呈现的不匹配
【发布时间】:2022-06-19 15:45:04
【问题描述】:

我正在尝试让 SSR 在我的应用中运行,但出现错误:

水化失败,因为初始 UI 与之前的不匹配 在服务器上渲染。

现场演示代码为here

问题的现场演示是here(打开开发工具控制台查看错误):

// App.js

 import React from "react";
    
  class App extends React.Component {

  head() {
    return (
      <head>
        <meta charSet="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, initial-scale=1, shrink-to-fit=no"
        />
        <meta name="theme-color" content="#000000" />
        <title>React App</title>
      </head>
    );
  }

  body() {
    return (
      <body>
        <div className="App">
          <h1>Client says Hello World</h1>
        </div>
      </body>
    );
  }

  render() {
    return (
      <React.Fragment>
        {this.head()}
        {this.body()}
      </React.Fragment>
    )
  }
}
export default App;

// index.js

import React from "react";
import * as ReactDOM from "react-dom/client";
import { StrictMode } from "react";

import App from "./App";


// const container = document.getElementById("root");
const container = document.getElementsByTagName("html")[0]

ReactDOM.hydrateRoot(
  container,
  <StrictMode>
    <App />
  </StrictMode>
);

现场演示中显示的 Html 模板由后端提供,并使用以下代码生成:

const ReactDOMServer = require('react-dom/server');

const clientHtml = ReactDOMServer.renderToString(
<StrictMode>
    <App />
</StrictMode>
)

// 将clientHtml 提供给客户端

我需要动态生成&lt;head&gt;&lt;/head&gt; and &lt;body&gt;&lt;/body&gt;部分,如App类所示

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    我最近在使用 NextJS 时遇到了同样的问题,我不确定我的观察是否适用于其他库。我一直在用不正确的标签包装我的组件,即 NextJS 不喜欢用 p 标签包装你的 divs 部分 等等,所以它会大喊“水化失败,因为初始 UI 与服务器上呈现的内容不匹配”。所以我通过检查我的元素如何相互包裹来解决这个问题。使用 ma​​terial UI 您需要谨慎,例如,如果您使用 Typography 组件作为包装器,则组件属性的默认值为“p”,因此您会体验到如果您不将组件值更改为某种语义,则会出现错误。因此,根据我个人的经验,我个人认为问题是由 html 元素的排列不当引起的,要在 NextJS 的上下文中解决问题,必须重新评估他们如何排列 html 元素。

    import Image from 'next/image'
    /**
     * This might give that error
     */
    export const IncorrectComponent = ()=>{
      return(
        <p>
          <div>This is not correct and should never be done because the p tag has been abused</div>
          <Image src='/vercel.svg' alt='' width='30' height='30'/>
        </p>
      )
    }
    
    /**
     * This will work
     */
    export const CorrectComponent = ()=>{
      return(
        <div>
          <div>This is correct and should work because a div is really good for this task.</div>
          <Image src='/vercel.svg' alt='' width='30' height='30'/>
        </div>
      )
    }

    【讨论】:

    • lul,这解决了我的问题。我有
      作为

      的孩子,感谢您成为野兽 @Motsi +1

    • 我使用 Sanity 作为我的 CMS,并且我将 组件包装在

      标记中。删除

      标签解决了我的问题。谢谢!

    • 这可能是由于 Nextjs 中 &lt;HTML&gt; 标签的错误使用方式造成的。对我来说,&lt;main&gt; 中的 &lt;section&gt; 太多了
    • 对我来说,它是&lt;div id="_next"&gt; 中的&lt;footer&gt; 元素。将_document.jsx 中的页脚元素作为&lt;body&gt; 的子元素移动以修复它。
    【解决方案2】:

    这个解决方案对我有用:

    export default function MyApp({ Component, pageProps }: AppProps) {
      const [showChild, setShowChild] = useState(false);
      useEffect(() => {
        setShowChild(true);
      }, []);
    
      if (!showChild) {
        return null;
      }
    
      if (typeof window === 'undefined') {
        return <></>;
      } else {
        return (
          <Provider store={store}>
            <Component {...pageProps} />
          </Provider>
        );
      }
    }
    

    我也在使用 NextJS、Redux Toolkit

    【讨论】:

    • 我面临同样的问题,使用下一个身份验证,触发该错误的行是 const {data: session} = useSession();知道我该如何解决吗?
    • @user3174311 for next auth 如果您使用这样的代码会产生错误if(typeof window !== "undefined" &amp;&amp; loading) { return null; } 您可以将其更改为if(typeof window === "undefined" || loading) { return null; }
    【解决方案3】:

    删除 &lt;p&gt; 标签解决了我与 NEXTJS 的类似问题..

    【讨论】:

    • 我认为这没有任何意义。
    【解决方案4】:

    它会起作用的:

     function MyApp({ Component, pageProps }) {
          const [showing, setShowing] = useState(false);
    
          useEffect(() => {
            setShowing(true);
          }, []);
        
          if (!showing) {
            return null;
          }
        
          if (typeof window === 'undefined') {
            return <></>;
          } else {
            return (
               <RecoilRoot>
                 <MainLayout>
                     <Component {...pageProps} />
                  </MainLayout>
               </RecoilRoot>
            );
          }
        }
    
     export default MyApp;
    

    这里我使用 recoil 进行状态管理。

    【讨论】:

    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    【解决方案5】:

    所以我的是 NEXT JS 应用程序。

    我正在使用react-use-cart 模块,它似乎与react @18.0.0 存在问题。

    我不确定这怎么可能,但降级到 react @17.0.2 消除了我的错误。

    之前我用的是react @18.0.0

    我只是运行npm uninstall react react-dom 并安装了版本@17.0.2

    Wahala,现在一切正常。

    【讨论】:

      【解决方案6】:

      我也有类似的问题。恢复到一个稳定版本的 react 会起作用

      考虑恢复到 React 17

      这是 github 讨论 (https://github.com/vercel/next.js/discussions/35773)

      【讨论】:

      • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
      【解决方案7】:

      如果您使用 NextJS 和带有情感的 Material UI 作为样式引擎,那么您可能需要检查组件的语义。您可以在记录到浏览器控制台的错误中找到提示。

      例子:在Iconbutton里面添加Box组件会报错

      无需创建自定义 NextJS _document.js 文件,因为 @emotion/react 版本 10 及更高版本默认使用 NextJS。

      【讨论】:

        【解决方案8】:

        我在尝试将 div 放入 React-Bootstrap 的卡片文本时遇到了同样的问题。

        错误可以通过以下方式重现:

        import type { NextPage } from 'next'
        import { Card } from 'react-bootstrap';
        ...
        const testPage : NextPage = () => {
        ...
        return (
        ...
        <Card.Text>
            <div>It's an error</div>
        </Card.Text>
        ...
        )}
        
        export default testPage
        

        为了修复它,我刚刚删除了 html 标签。

        我觉得有些 react 组件里面不接受 html 标签。

        【讨论】:

          【解决方案9】:

          导入和运行某些包也可能导致此错误。例如,当我使用 Swiper.js 包时,我遇到了这个问题。这主要是因为包在某处使用了 Window 对象。

          由于修改包本身的内容不是一个好习惯,解决此类问题的最佳方法是仅在加载 DOM 后渲染组件。所以你可以试试这个。

          const Index = () => {
            const [domLoaded, setDomLoaded] = useState(false);
          
            useEffect(() => {
              setDomLoaded(true);
            }, []);
          
            return (
              <>
                {domLoaded && (
                  <Swiper>
                    <div>Test</div>
                  </Swiper>
                )}
              </>
            );
          };
          
          export default Index;
          

          【讨论】:

            【解决方案10】:

            我对 Next.js 和 Faker.js 也有同样的问题,我只是使用条件渲染,它就解决了。我认为这是因为 faker.js 的值在页面首次加载时更改了两次。下面的代码可能会对您有所帮助。

            `

                export default function Story() {
                const [avatar, setAvatar] = useState(null);
                const [name, setName] = useState(null);
            
                useEffect(() => {
                setAvatar(faker.image.avatar());
                setName(faker.name.findName());
                }, []);
            
                return (
                <>
                  {avatar && name && (
                  <div>
                    <Image
                      src={avatar}
                      alt="Story Profile Picture"
                      width={50}
                      height={50}
                  />
                    <p>{name}</p>
                  </div>
                  )}
                </>
               );
            }
            

            `

            【讨论】:

              【解决方案11】:

              您可以使用来自 mui(material-ui) 的 Nossr 包装导致此错误的组件

              import NoSsr from "@mui/material/NoSsr";
              
              <NoSsr> {your contents} </NoSsr>
              

              获取更多信息:https://mui.com/material-ui/react-no-ssr/

              【讨论】:

                【解决方案12】:

                确保将您导入的惰性模块包含在 Suspense 中。

                就我而言,我导入了

                const Footer = React.lazy(() => import('../Footer/Index'));
                

                但我使用它就像一个普通的模块

                <Footer />
                

                我将它包裹在Suspense 中,错误消失了。

                <Suspense fallback={<div>Loading...</div>}>
                  <Footer />
                </Suspense>
                

                底线

                如果首页给你这个错误,请尝试评论一些你使用的组件,直到你找到错误的来源。

                【讨论】:

                  【解决方案13】:

                  在我的例子中,当我在映射列表之前使用 reverse() 函数时,我遇到了类似的错误。

                  【讨论】:

                  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
                  【解决方案14】:

                  如果你正在使用 nextjs ,你可以在这里尝试官方解决方案:https://nextjs.org/docs/messages/react-hydration-error

                  【讨论】:

                    【解决方案15】:

                    在我的例子中,既没有使 HTML 更具语义,也没有修改 _app.tsx 以检查 window 是否已加载。

                    目前唯一的解决方案是降级 React 版本和 React DOM。

                    我正在使用 NextJS。

                    我切换到版本 17.0.2。

                    【讨论】:

                      【解决方案16】:

                      如果您使用的是table,您可能错过了&lt;tbody&gt;

                      不正确:

                      <table>
                        <tr>
                          <td>a</td>
                          <td>b</td>
                        </tr>
                      </table>
                      

                      正确:

                      <table>
                        <tbody>
                          <tr>
                            <td>a</td>
                            <td>b</td>
                          </tr>
                        </tbody>
                      </table>
                      

                      【讨论】:

                        【解决方案17】:

                        就我而言,这是嵌套列表中的用户错误。我忘了在li 中添加ul,所以它只是嵌套了lis。

                        【讨论】:

                          【解决方案18】:

                          通过将标签更改为标签解决了我的问题

                          【讨论】:

                            猜你喜欢
                            • 2022-10-24
                            • 2023-01-30
                            • 2023-02-19
                            • 2023-01-10
                            • 2023-02-04
                            • 2023-01-30
                            • 2022-09-23
                            • 2022-08-20
                            • 2022-10-09
                            相关资源
                            最近更新 更多