为何在useState中使用Math.random()会引发服务端渲染问题?
问题原因
直接在useState的初始值里用Math.random()会触发Hydration失败,核心原因是服务端渲染(SSR)和客户端首次生成的随机值不一致。
SSR过程中,服务器会执行组件代码生成初始UI,此时Math.random()会生成一个随机结果;当客户端加载页面进行Hydration时,会再次执行这段代码生成新的随机值——两次结果大概率不同,导致服务器输出的UI和客户端初始渲染的UI不匹配,触发报错。
而直接传固定的true或false时,服务端和客户端的初始状态完全一致,自然不会有这个问题。
解决方案
方案1:用useEffect在客户端挂载后设置状态
把随机逻辑放到useEffect中,这段代码只会在客户端组件挂载后执行,避免服务端和客户端的结果冲突:
import { useState, useEffect } from 'react'; function YourComponent() { const [hasPrime, setHasPrime] = useState(false); // 服务端和客户端共用同一个初始值 useEffect(() => { // 仅在客户端执行随机判断 setHasPrime(Math.random() < 0.5); }, []); return ( <> {hasPrime && ( <div> <p>Free Next-day delivery</p> </div> )} </> ); }
方案2:用useLayoutEffect同步更新UI(可选)
如果需要在DOM更新前就完成状态设置,避免UI闪烁,可替换成useLayoutEffect,逻辑和上面一致:
import { useState, useLayoutEffect } from 'react'; function YourComponent() { const [hasPrime, setHasPrime] = useState(false); useLayoutEffect(() => { setHasPrime(Math.random() < 0.5); }, []); // 渲染逻辑和方案1一致 }
方案3:客户端环境判断(快速适配)
通过typeof window !== 'undefined'判断是否在客户端,确保服务端用固定初始值,客户端再生成随机值:
const [hasPrime] = useState(typeof window !== 'undefined' ? Math.random() < 0.5 : false);
注意:这种方式下服务端渲染的UI不会显示该元素,客户端挂载后会突然出现,可能有闪烁,适合对UI连贯性要求不高的场景。
内容的提问来源于stack exchange,提问作者JohnPix
相关产品推荐
相关产品推荐

