Next.js 12中使用含useState的Hook出现SSR Hydration错误原因咨询
为什么含useState的自定义Hook会导致SSR Hydration不匹配?
场景说明
App和path由ctx中的URL确定,通过getServerSideProps作为props传递给组件。
出现Hydration错误的自定义Hook代码
// Hook export default function usePath(app, path) { const [currentPath, setCurrentPath] = useState('') useEffect(() => { const config = globalConfig[app] if (config) { const { paths } = globalConfig const isMatching = paths.find(({ pattern }) => pattern.test(path)) if (isMatching) { setCurrentPath(isMatching.path) } } }, [app, path]) return currentPath }
组件使用该Hook的代码
const currentPath = usePath(app, path) // Hydration error here: <p>{app && currentPath ? t(`sentence.${currentPath}`) : t(`sentence.defaultMessage`)}</p> /* Server renders: sentence.defaultMessage, while the client renders: sentence.${currentPath}. */
可正常运行的Hook代码(移除useState和useEffect)
export default function useChainPath(app, path) { const config = globalConfig[app] let currentPath = '' if (config) { const { paths } = config const isMatching = paths.find(({ pattern }) => pattern.test(path)) if (isMatching) { currentPath = isMatching.path } } return currentPath }
底层原因解析
Hydration不匹配的核心是服务端渲染的DOM结构与客户端首次渲染的DOM结构不一致,具体到这个场景:
服务端渲染阶段:
- 服务端执行组件代码时,
useState初始化currentPath为'' - React的副作用钩子(比如
useEffect)在服务端完全不执行 - 最终服务端渲染出的是
t('sentence.defaultMessage')对应的内容
- 服务端执行组件代码时,
客户端Hydration阶段:
- 客户端首次渲染时,
useState同样初始化currentPath为'',此时渲染内容和服务端一致,Hydration初期无问题 - 但Hydration完成后,React会立即执行
useEffect钩子:根据app和path匹配到目标currentPath,调用setCurrentPath更新状态 - 状态更新触发组件重新渲染,此时渲染出
t(sentence.${currentPath})的内容,和服务端最初输出的DOM产生差异,触发Hydration错误
- 客户端首次渲染时,
对比修复后的Hook:
- 修复后的Hook直接在函数体内同步计算
currentPath,服务端和客户端执行时会得到完全相同的结果 - 没有依赖副作用钩子延迟更新状态,服务端和客户端首次渲染的内容完全一致,不会触发不匹配问题
- 修复后的Hook直接在函数体内同步计算
本质上,你把本该同步计算的逻辑放到了客户端副作用里,导致服务端与客户端的首次渲染结果产生偏差,而Hydration要求两者输出的DOM必须严格一致。
内容的提问来源于stack exchange,提问作者octavemirbeau
相关产品推荐
相关产品推荐

