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

我试图让 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>
)

// serve clientHtml to client

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

【问题讨论】:

    标签: reactjs


    【解决方案1】:

    我最近遇到了同样的问题NextJS我不确定我的观察是否适用于其他图书馆。我一直在用不正确的标签包装我的组件,也就是说,NextJS不舒服p包装你的标签div,部分等等,所以它会大喊大叫“水化失败,因为初始 UI 与服务器上呈现的内容不匹配”.所以我通过检查我的元素如何相互包裹来解决这个问题。和材质界面例如,如果您使用排版作为包装器的组件,组件属性的默认值为“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,这解决了我的问题。我有 <hr /> 作为 <p> 的孩子,感谢您成为野兽@Motsi +1
    • 我使用 Sanity 作为我的 CMS,并且我将 <BlockContent ... /> 组件包装在 <p> 标记中。删除 <p> 标签解决了我的问题。谢谢!
    • 这可能是由于在 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

    【讨论】:

    • 我面临同样的问题,使用 next-auth,触发该错误的行是 const {data: session} = useSession();知道我该如何解决吗?
    • @user3174311 用于下一个身份验证,如果您使用这样的代码会产生错误 if(typeof window !== "undefined" &amp;&amp; loading) { return null; } 您可以将其更改为 if(typeof window === "undefined" || loading) { return null; }
    • 我在 Urql Query 中使用 pause: true 时遇到了同样的错误。所以,我添加了if(typeof window ==== "undefined") return null; ,它起作用了。
    • 我不认为这是一个解决方案,你阻止服务器渲染组件。您还跳过了第一个前端渲染。您的解决方案只是跳过补水。
    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

      如果您使用的是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>
      

      【讨论】:

      • 正是我的问题,谢谢。
      【解决方案5】:

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

      【讨论】:

      • 我认为这没有任何意义。
      • 这个属于评论区
      • 好吧,如果它看起来很愚蠢但它有效,那它并不愚蠢哈哈
      【解决方案6】:

      它会起作用:

       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;
      

      在这里,我使用反冲来进行状态管理。

      【讨论】:

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

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

      示例:在Iconbutton 中添加Box 组件会导致错误

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

      【讨论】:

        【解决方案8】:

        我有反应 18.2.0下一个 12.2.4我用这段代码修复了水合作用

        import { useEffect, useState } from 'react'
        import { Breakpoint, BreakpointProvider } from 'react-socks';
        import '../styles/globals.scss'
        
        function MyApp({ Component, pageProps }) {
          const [showChild, setShowChild] = useState(false)
        
          useEffect(() => {
            setShowChild(true)
          }, [])
        
          if (!showChild) {
            return null
          }
        
          return (
            <BreakpointProvider>
              <Component {...pageProps} />
            </BreakpointProvider>
          )
        }
        
        export default MyApp
        

        【讨论】:

        • 像魅力一样工作❤️‍?
        【解决方案9】:

        所以我的是一个 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,现在一切都按预期进行。

        【讨论】:

        • 嘿!无需降级,只需重新格式化 react-use-cart 中使用的对象。为了更清楚地说明,项目对象应该是 Json 字符串化,然后 json 在另一个变量中解析,并且该变量代替,: /// const { items } = useCart() const [allItems, setallItems] = useState([{}]) useEffect( () => { setallItems(JSON.parse(JSON.stringify(items))) }, [items]) /// 购物车总数也是如此,只需将其设置在 useState const 中即可
        【解决方案10】:

        当我尝试在 React-Bootstrap 的卡片文本中放置一个 div 时,我遇到了同样的问题。

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

        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 标签。

        我认为一些反应组件不接受里面的 html 标签。

        【讨论】:

          【解决方案11】:

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

          就我而言,我进口了

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

          但我像普通模块一样使用它

          <Footer />
          

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

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

          底线

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

          【讨论】:

            【解决方案12】:

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

            【讨论】:

              【解决方案13】:

              只需转到浏览器,chrome->右上角的三栏按钮->更多工具->清除浏览历史记录->删除cookie。

              没有更多的错误

              【讨论】:

                【解决方案14】:

                这个问题来自nextjshtml标签字符串附加到其他 div 请为此使用 div

                 <div dangerouslySetInnerHTML={{__html:data.description}}></div>
                

                【讨论】:

                  【解决方案15】:

                  我对 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>
                        )}
                      </>
                     );
                  }
                  

                  `

                  【讨论】:

                    【解决方案16】:

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

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

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

                    【讨论】:

                      【解决方案17】:

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

                      【讨论】:

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

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

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

                      我正在使用 NextJS。

                      我切换到版本 17.0.2。

                      【讨论】:

                        【解决方案19】:

                        通过将标签更改为标签来解决问题

                        【讨论】:

                          【解决方案20】:

                          确保你没有嵌套next/Link,我需要重构代码并在包装图像之前忘记了我有一个next/Link

                          例如

                                  <CompanyCardStyle className={className}>
                                      //Open Link
                                      <Link href={route('companyDetail', { slug: company.slug })}>
                                          <a className='d-flex align-items-center'> 
                                              <div className='company-card'>
                                                  <div className='d-flex align-items-center col-name-logo'>
                                                      <div className='company-logo'>
                                                          //Remove link and let the <a> child
                                                          <Link href={route('companyDetail', { slug: company.slug })}>
                                                              <a><img src={company.logoUrl} width={'100%'} /></a>
                                                          </Link>
                                                      </div>
                                                      <h6 className='mb-0'>{company.name}</h6>
                                                  </div>
                                                  .....
                                              </div>
                                          </a>
                                      </Link>
                                  </CompanyCardStyle>
                          

                          【讨论】:

                            【解决方案21】:

                            它发生在 Nextjs 中,我发现我不小心将 index.js 文件移动到另一个文件夹中。所以我把它放回了 pages 文件夹,一切都很好。

                            【讨论】:

                              【解决方案22】:

                              水化失败,因为初始 UI 与服务器上呈现的内容不匹配

                              您应该检查控制台是否存在以下错误:

                              Warning: Expected server HTML to contain a matching <div> in <div>.
                              

                              并修复它们。

                              复制自https://github.com/vercel/next.js/discussions/35773

                              【讨论】:

                                【解决方案23】:

                                我刚刚遇到了类似的错误。在我的情况下,我也在使用 Nextjs,基本上问题是我没有在标签内使用标签。通常,浏览器会自动添加 if 我只是放在标签内,而不是在 table 标签内指定 tbody 或 thead 标签。

                                我相信这可以适用于所有其他类似的 HTML 标记,并且它在错误中告诉您,在 DOM 上呈现了您在代码中没有提到的东西。

                                还有一件事,我首先在该特定部分中使用嵌套循环来映射表行,然后是某个表数据字段。当我删除嵌套地图时,它在不添加 tbody 的情况下工作,并且在我添加 tboday 但保留嵌套循环时也工作。所以,我不知道为什么它很奇怪。但是,它的工作原理如此之好。

                                【讨论】:

                                  【解决方案24】:

                                  这对我来说可以在整个应用程序中进行客户端渲染。无需再担心在整个应用程序的其余部分使用 window

                                  import useSSR from 'use-ssr'
                                  
                                  function MyApp(props) {
                                    const { Component, pageProps } = props
                                  
                                    return (
                                      <>
                                        <Head>
                                          <title>title</title>
                                          <meta name='description' content='Generated by create next app' />
                                          <link rel='icon' href='/favicon.ico' />
                                        </Head>
                                        <ClientSideRenderedNextJS>
                                          <Component {...pageProps} />
                                        </ClientSideRenderedNextJS>
                                      </>
                                    )
                                  }
                                  
                                  function ClientSideRenderedNextJS({ children }) {
                                    const [domLoaded, setDomLoaded] = useState(false)
                                    
                                    useEffect(() => {
                                      setDomLoaded(true)
                                    }, [])
                                  
                                    const { isServer } = useSSR()
                                    if (!domLoaded) return null
                                    if (isServer) return <></>
                                  
                                    return children
                                  }
                                  

                                  【讨论】:

                                    【解决方案25】:

                                    就我而言,我遇到了这个问题,因为我使用了 &lt;img /&gt; 标签而不是 Next.js &lt;Image /&gt; 标签,因为我不能使用带有 &lt;Image /&gt; 标签的尾风类。用 Next.js &lt;Image /&gt; 标签替换 &lt;img /&gt; 标签为我解决了这个问题。然后我用div 标记包装&lt;Image /&gt; 标记并将类添加到其中。

                                    【讨论】:

                                      【解决方案26】:

                                      当我在 NextJs 中移动 pages 目录时遇到了这个问题。我通过删除 .next 文件夹并使用 yarn build 重建它来解决它。

                                      【讨论】:

                                        【解决方案27】:

                                        我在使用带有 Material UI 的 NextJS 时遇到了同样的问题。 实际的解决方案是使用 NextJS 动态导入。 在下面的示例中,不要将标头组件导入为:

                                        import Header from "../components/header"
                                        

                                        而是这样做:

                                        import dynamic from 'next/dynamic'
                                        
                                        const DynamicHeader = dynamic(() => import('../components/header'), {
                                          suspense: true,
                                        })
                                        

                                        检查下面的 NextJS 文档:

                                        https://nextjs.org/docs/advanced-features/dynamic-import

                                        【讨论】:

                                          【解决方案28】:

                                          这对我有用,Nextjs。

                                          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 client={state}>
                                               <Component {...pageProps} />
                                             </Provider>
                                           );
                                          }
                                          

                                          }

                                          【讨论】:

                                            【解决方案29】:

                                            我通过 NextJs 解决了这个问题动态的导入ssr:假

                                            import dynamic from 'next/dynamic'
                                            import { Suspense } from 'react'
                                            
                                            const DynamicHeader = dynamic(() => import('../components/header'), {
                                                ssr: false,
                                            })
                                            
                                            export default function Home() {
                                                return (
                                                    <DynamicHeader />
                                                )
                                            }
                                            

                                            【讨论】:

                                              【解决方案30】:

                                              即使使用 create-next-app 没有任何更改并尝试使用不同的浏览器和不同的配置文件,我也遇到了这个问题。我发现它们没有问题,所以我调查了我的扩展,但它没有解决问题。最后,我通过调查 chrome 开发工具设置解决了这个问题。我的问题与“启用本地覆盖”有关。所以我没有选中它,它解决了。

                                              在 Chrome 设置上启用本地覆盖

                                              【讨论】:

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