【NextJS】 水合错误(Hydration Error)解决方案
备注:在开源云操作系统Sealos(github 1.59w 🌟)的工单支持系统Tentix中遇到并解决此错误
报错场景
在基于 Next.js + React Hook Form 构建的数据库自动备份表单中,我希望为用户默认随机生成一个每日备份的小时(14 ~ 24 点之间),即:
const getRandomHour14to24 = () => {
const hour = Math.floor(Math.random() * (10)) + 14;
return hour.toString().padStart(2, '0');
};
// ...
export const defaultDBEditValue = {
autoBackup: {
hour: getRandomHour14to24(), // 这里触发水合不一致
...
}
}
但很快就出现了一个经典问题 —— 水合错误(Hydration Error)
报错信息
Unhandled Runtime Error
Error: There was an error while hydrating. Because the error happened outside of a Suspense boundary, the entire root will switch to client rendering.
解读
-
There was an error while hydrating这表明在 React 尝试将服务器渲染的 HTML 转换为可交互的 React 组件时发生了错误。
-
Hydration failed because the initial UI does not match what was rendered on the server这意味着服务器和客户端渲染的 HTML 内容不一致,导致水合失败。
-
Text content does not match server-rendered HTML这通常发生在服务器和客户端渲染的文本内容不同,例如使用
new Date()或Math.random()等动态值。
报错修复
可以使用 useEffect 来确保逻辑只在客户端执行
import { useEffect, useState } from 'react';
const getRandomHour14to24 = () => {
const hour = Math.floor(Math.random() * (10)) + 14;
return hour.toString().padStart(2, '0');
};
const BackupModal = () => {
const [randomHour, setRandomHour] = useState<string>('14'); // 提供默认值,避免 undefined
useEffect(() => {
setRandomHour(getRandomHour14to24()); // 只在客户端调用
}, []);// 空依赖数组,只在页面第一次渲染时执行
再将它传入 React Hook Form 或表单的默认值中:
const form = useForm({
defaultValues: {
hour: randomHour,
...
}
});
总结:如何彻底避免 Hydration Error
Next.js 是“服务端优先”的框架,一定要意识到哪些代码会运行在服务端,哪些只在浏览器运行。利用 useEffect 做到“客户端逻辑客户端跑”,可以避免绝大多数这类陷阱。
| 场景 | 正确做法 |
|---|---|
| 需要用随机数 / 当前时间等动态值 | 在 useEffect 中生成,不在 SSR 阶段使用 |
| 需要在服务端和客户端共享的默认值 | 写死在 .ts 文件中或通过接口获取 |
| 调用 React hooks(useState, useEffect) | 只能放在组件内,不能出现在 constants 或 util.ts 中直接执行 |
781

被折叠的 条评论
为什么被折叠?



