错误:文本内容与服务器渲染的 HTML 不匹配

问题描述 投票:0回答:4

我尝试在 next.js 中执行一个简单的算法,但遇到了水合错误。

这是我正在使用的代码:

import numeros from "../../functions/numberGenerators.js"

export default function teste(){
    let number = numeros()
    return number.map(n =>  
    <div key={n}>
        Number: {n}
    </div>)
}

并且:

export default function megaSena(qtde = 6){
    let listNumbers = []
    while(listNumbers.length <= qtde - 1){
        const numeroRandom = parseInt(Math.random() * 60) + 1
        if (!listNumbers.includes(numeroRandom)){
            listNumbers.push(numeroRandom)
        }
    }
    return listNumbers
}

我遇到以下错误:

1° - 错误:水合失败,因为初始 UI 与服务器上呈现的内容不匹配。

2° - 错误:文本内容与服务器渲染的 HTML 不匹配。

3° - 错误:水合失败,因为初始 UI 与服务器上呈现的内容不匹配。

4° - 错误:补水时出现错误。因为错误发生在 Suspense 边界之外,所以整个根将切换到客户端渲染。

我可以做什么来解决这个问题?

javascript reactjs next.js
4个回答
24
投票

这些错误消息本质上都说了同样的事情:

服务器中的 HTML 与您的应用程序呈现的内容不匹配。

为什么会出现这种情况?

当调用

teste
时,它并不总是返回相同的东西。它旨在显示随机数。

这意味着它将显示不同的数字列表,每次

teste
被渲染。

当将服务器端渲染(SSR)与 NextJS 等框架一起使用时,它正在使用 React Hydration。这只是一个花哨的词,表示 React 正在研究如何获取从 HTML 文件渲染的 DOM 结构并将其转换为正在运行的单页应用程序 (SPA)。

为了使其正常工作,React Hydration 要求服务器中的 HTML 与客户端应用程序呈现的内容完全匹配。

所以在你的场景中,这就是正在发生的事情:

    服务器需要生成HTML文件。
  1. 它调用
  2. teste
    ,生成随机数字数组并渲染它。
  3. 服务器将此 HTML 发送到客户端。
  4. 客户端应用程序调用
  5. teste
    ,它生成一个 
    NEW 随机数字数组并呈现它。
  6. 它尝试“水合”应用程序,但 HTML 和客户端渲染不匹配。
  7. NextJS/React 不知道如何处理这种不匹配,因此它会记录您看到的错误。
如何解决这个问题?

修复方法很简单,但需要您注意何时需要或不需要。

常见的方法是利用

useEffect()

 来确保服务器和客户端在水合过程中渲染相同的内容,并且仅在客户端上渲染动态内容。

最简单的方法就是不渲染任何内容。使用您的代码,看起来像这样:

import React from "react"; export default function Teste() { const [hydrated, setHydrated] = React.useState(false); React.useEffect(() => { setHydrated(true); }, []); if (!hydrated) { // Returns null on first render, so the client and server match return null; } let number = numeros(); return number.map((n) => <div key={n}>Number: {n}</div>); }
这样做是为了确保第一次 

Teste

 渲染时会返回 
null

第一个渲染是服务器用来生成 HTML 文件的内容,也是客户端应用程序将用于“水化”过程的内容。

在第一次运行期间,

hydrated

 将具有默认值 
false
,这将导致组件返回 
null
。同样,在第一次运行中,
useEffect()
 将调用 
setHydrated(true)
,这将在第一次渲染完成后触发第二次渲染。

当第二次渲染运行时,应用程序已经水合了,因此无需再担心发生错误。此时

hydrated

将变为
true
,因此随机数将正常渲染。

更多信息

如果您想了解有关 React 水合的更多信息,我写了一篇

关于修复这些类型的错误的博客文章

我还发布了一个 NPM 包,有助于简化处理这些类型的水合错误:

react-Hydration-provider

要使用

react-hydration-provider

 修复错误,您的代码将如下所示:

import { HydrationProvider, Client } from "react-hydration-provider"; function App() { return ( <HydrationProvider> <Client> <Teste /> </Client> </HydrationProvider> ); } function Teste() { let number = numeros(); return number.map((n) => <div key={n}>Number: {n}</div>); }
这将使 

Teste

 仅在应用程序水合后才在客户端呈现。

您还可以做一些更复杂的事情,如下所示:

import { HydrationProvider, Server, Client } from "react-hydration-provider"; function App() { return ( <HydrationProvider> <Teste /> </HydrationProvider> ); } function Teste() { let number = numeros(); return number.map((n) => ( <div key={n}> <span>Number: </span> <Client>{n}</Client> <Server>Loading...</Server> </div> )); }
这将允许您的应用程序最初为每个数字呈现一条加载消息,然后在应用程序完成水合过程后将其替换为随机数字。


9
投票
正如@milad-amirvafa 所说,

注释掉代码的某些部分以获取导致水合错误的元素,然后添加一个以 false 作为初始状态的状态钩子,如下所示,

const [hydrated, setHydrated] = useState(false)

;

然后在 useEffect 钩子中将该状态设置为 true,将水合状态添加到导致错误的组件中。您的代码应如下所示:

const Component = () => { const [hydrated, setHydrated] = useState(false); useEffect(() => { setHydrated(true); },[]) return ( <> {hydrated && <div>The element which throws error</div>} </> )} export default Component;
    

0
投票
请按照以下解决方案解决问题。 这使得组件不会在第一次渲染。

const Component = () => { const [showComponent, setShowComponent] = useState(false); useEffect(() => { setShowComponent(true); },[]) return ( <> showComponent && <div>The component which throws error</div> </> ) } export default Component
    

0
投票
如果写next.js建议使用

next.js official documentation

nextjs.org. 中提供了解决许多此类问题的简单方法 这个问题的解决方案简单解决方案在这里:
https://nextjs.org/docs/messages/react-Hydration-error

© www.soinside.com 2019 - 2024. All rights reserved.