客户端组件条件渲染触发Hydration错误求助
当在Next.js客户端组件中直接读取Cookie值并渲染时,刷新页面会抛出以下错误:
react-dom.development.js:7076 Uncaught Error: Hydration failed because the initial UI does not match what was rendered on the server.
问题根源
你误以为服务器无法感知Cookie状态,但实际上Next.js的SSR机制会在服务器端渲染所有组件(包括客户端组件)的初始HTML。服务器能通过请求头拿到Cookie,所以渲染客户端组件时会使用Cookie值生成对应内容;而客户端在hydration阶段(首次渲染)会对比服务器生成的HTML和自己渲染的DOM,一旦两者不一致就会触发错误——哪怕你觉得这种差异是合理的,Next.js的hydration机制也会严格要求两端初始渲染结果一致。
解决方案
方案1:延迟客户端Cookie读取(避免hydration不匹配)
通过useState设置初始默认值,在useEffect中延迟读取Cookie,确保客户端hydration完成后再更新UI。这样服务器端渲染的初始HTML和客户端首次渲染结果一致,后续再更新为正确的Cookie值。
修改后的客户端组件代码:
"use client"; import { useState, useEffect } from "react"; import { getCookie } from "cookies-next"; export default function ClientComponent() { const [version, setVersion] = useState(null); useEffect(() => { // 仅在客户端hydration完成后执行Cookie读取 const cookieVal = getCookie("version"); setVersion(cookieVal); }, []); return ( <p className="text-black"> The cookies is currently set to: {version ?? "undefined"} </p> ); }
如果不想依赖第三方库,也可以直接用原生方式读取Cookie:
"use client"; import { useState, useEffect } from "react"; export default function ClientComponent() { const [version, setVersion] = useState(null); useEffect(() => { const getCookie = (name) => { return document.cookie.split('; ') .find(row => row.startsWith(`${name}=`)) ?.split('=')[1]; }; setVersion(getCookie("version")); }, []); return ( <p className="text-black"> The cookies is currently set to: {version ?? "undefined"} </p> ); }
方案2:服务器传递Cookie值给客户端组件(避免UI闪烁)
如果不想出现客户端hydration后UI更新的闪烁,可以让服务器组件先读取Cookie,再将值传递给客户端组件。这样服务器端渲染的HTML已经包含正确的Cookie值,客户端hydration时初始渲染和服务器一致,后续再同步客户端Cookie变化。
修改首页(服务器组件)代码:
import { cookies } from "next/headers"; import ClientComponent from "@/components/client-component"; export default function Home() { const cookieStore = cookies(); const serverVersion = cookieStore.get("version")?.value; return ( <main className="p-24"> <form action={async function() { "use server"; const cookieStore = cookies(); cookieStore.set("version", "a"); }} > <button type="submit">Set version to "a"</button> </form> <form action={async function() { "use server"; const cookieStore = cookies(); cookieStore.set("version", "b"); }} > <button type="submit">Set version to "b"</button> </form> {/* 传递服务器读取的Cookie值 */} <ClientComponent initialVersion={serverVersion} /> </main> ); }
修改客户端组件:
"use client"; import { useState, useEffect } from "react"; import { getCookie } from "cookies-next"; export default function ClientComponent({ initialVersion }) { const [version, setVersion] = useState(initialVersion); useEffect(() => { // 监听Cookie变化,确保客户端值始终最新 const updateCookie = () => { const cookieVal = getCookie("version"); setVersion(cookieVal); }; // 初始同步一次 updateCookie(); // 定时检查Cookie变化(可根据需求调整间隔) const interval = setInterval(updateCookie, 1000); return () => clearInterval(interval); }, []); return ( <p className="text-black"> The cookies is currently set to: {version ?? "undefined"} </p> ); }
内容的提问来源于stack exchange,提问作者IObert

