【NextJS】 水合错误(Hydration Error)解决方案

【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)只能放在组件内,不能出现在 constantsutil.ts 中直接执行
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值