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

Gab*_*ira 10 javascript reactjs next.js

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

\n

这是我正在使用的代码:

\n

\r\n
\r\n
import numeros from "../../functions/numberGenerators.js"\n\nexport default function teste(){\n    let number = numeros()\n    return number.map(n =>  \n    <div key={n}>\n        Number: {n}\n    </div>)\n}
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n

和:

\n

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

\n

我遇到以下错误:

\n

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

\n

2\xc2\xb0 - 错误:文本内容与服务器呈现的 HTML 不匹配。

\n

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

\n

4\xc2\xb0 - 错误:补水时出现错误。由于错误发生在 Suspense 边界之外,因此整个根将切换到客户端渲染。

\n

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

\n

twi*_*wiz 24

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

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

为什么会发生这种情况?

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

这意味着每次 teste渲染时都会显示不同的数字列表。

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

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

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

  1. 服务器需要生成 HTML 文件。
  2. 它调用teste,生成一个随机数字数组并渲染它。
  3. 服务器将此 HTML 发送到客户端。
  4. 客户端应用程序调用teste,生成一个新的随机数字数组并呈现它。
  5. 它尝试“水合”应用程序,但 HTML 和客户端渲染不匹配。
  6. 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>);
}
Run Code Online (Sandbox Code Playgroud)

这是为了确保第一次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>);
}
Run Code Online (Sandbox Code Playgroud)

这只会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>
    ));
}
Run Code Online (Sandbox Code Playgroud)

这将允许您的应用程序最初为每个数字呈现一条加载消息,然后在应用程序完成水合过程后将其替换为随机数字。


mjo*_*osh 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;
Run Code Online (Sandbox Code Playgroud)