这些错误信息本质上都是在说同一件事情:
服务器返回的HTML与你的应用程序渲染的不匹配。
为什么会出现这种情况?
当调用teste
时,它并不总是返回相同的内容。它被设计成显示随机数字。
这意味着它将每次渲染都显示不同的数字列表。
使用像NextJS这样的框架进行服务端渲染(SSR)时,它使用React hydration。这只是一个花哨的词,意思是React正在找出如何将从HTML文件渲染的DOM结构转换为运行中的单页应用程序(SPA)。
为了使其正常工作,React Hydration要求来自服务器的HTML和客户端应用程序渲染的内容完全匹配。
因此,在您的场景中,发生了以下情况:
- 服务器需要生成HTML文件。
- 它调用
teste
,生成一个随机数组并呈现它。
- 服务器将此HTML发送给客户端。
- 客户端应用程序调用
teste
,生成一个新的随机数组并呈现它。
- 它试图“水化”应用程序,但HTML和客户端渲染不匹配。
- NextJS/React不知道如何处理这种不匹配,因此记录了您看到的错误。
如何解决这个问题?
修复很简单,但需要您注意何时需要进行修复。
常见的方法是利用useEffect()
确保服务器和客户端在水化过程中呈现相同的内容,并仅在之后在客户端呈现动态内容。
最简单的方法是什么都不渲染。使用您的代码,它看起来像这样:
import React from "react";
export default function Teste() {
const [hydrated, setHydrated] = React.useState(false);
React.useEffect(() => {
setHydrated(true);
}, []);
if (!hydrated) {
return null;
}
let number = numeros();
return number.map((n) => <div key={n}>Number: {n}</div>);
}
这段代码确保第一次呈现
Teste
将返回
null
。
这个首次呈现是服务器用来生成HTML文件的,也是客户端应用程序在"hydration"(注水)过程中使用的。
在此第一次运行期间,
hydrated
将具有默认值
false
,这将导致组件返回
null
。同时,在此第一次运行期间,
useEffect()
将调用
setHydrated(true)
,这会在第一个完成后触发第二次呈现。
当第二次呈现运行时,应用程序已经被hydrate了,因此不需要再担心错误发生了。此时,
hydrated
将是
true
,因此随机数将正常呈现。
更多信息
如果您想了解有关React hydration的更多信息,我写了一篇
博文,介绍如何修复这些类型的错误。
我还发布了一个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>
));
}
这将使您的应用程序在开始时为每个数字呈现加载消息,然后在应用程序完成水合过程后用随机数字替换它。
useEffect
内部)。 - juliomalves