You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next13布局中用户状态条件渲染引发Hydration错误的SSG兼容解决方案

解决方案:避免Hydration错误同时保留SSG与无CLS的用户状态切换

问题核心

服务端静态生成(SSG)时无用户Cookie,默认输出访客版UI;客户端Hydration阶段,初始渲染仍为访客UI,但useEffect读取Cookie后立刻更新状态为登录用户,导致Hydration前后UI不匹配,触发错误。我们需要让客户端Hydration过程与服务端输出完全一致,挂载完成后再切换状态。

调整后的代码

'use client';

import React, { Suspense, useEffect, useState } from 'react';
import { hookstate, useHookstate } from '@hookstate/core';
import { getUserCookie } from 'helpers/cookieHelper';
import RegisterModal from '../RegisterModal';
import Link from 'next/link';
import UserAvatar from '@shared/user/UserAvatar';
import Image from 'next/image';

export const globalUserState = hookstate(false);

export default function NavRegisterOrProfile() {
  const [showRegister, setShowRegister] = useState(false);
  const [hasMounted, setHasMounted] = React.useState(false);

  const userState = useHookstate(globalUserState);

  useEffect(() => {
    // Hydration完成后再标记挂载状态并读取Cookie
    setHasMounted(true);
    const user = getUserCookie(true);
    if (user) {
      userState.set(() => user);
    }
  }, []);

  const user = userState.get();

  // Hydration阶段强制渲染与服务端一致的访客UI
  if (!hasMounted) {
    return (
      <strong
        className="mx-3 px-1 cursor-pointer hover:underline"
        data-cy="header-register"
        id="desktop-nav-register"
        onClick={() => setShowRegister(true)}
      >
        Join Us
      </strong>
    );
  }

  return (
    <>
      {user?.name ? (
        <>
          <Link href="/profile" id="desktop-nav-profile">
            <UserAvatar user={user} />
          </Link>
          {user?.isAdmin && (
            <div className="shadow-md h-12 w-12 ml-4 flex justify-center items-center rounded-full">
              <Link href="/admin">
                <Image
                  src="/images/heart.svg"
                  width={30}
                  height={26}
                  alt="admin"
                  priority
                />
              </Link>
            </div>
          )}
        </>
      ) : (
        <>
          <strong
            className="mx-3 px-1 cursor-pointer hover:underline"
            data-cy="header-register"
            id="desktop-nav-register"
            onClick={() => setShowRegister(true)}
          >
            Join Us
          </strong>

          {showRegister && (
            <Suspense>
              <RegisterModal
                closeModalCallback={() => {
                  setShowRegister(false);
                }}
              />
            </Suspense>
          )}
        </>
      )}
    </>
  );
}

方案有效性说明

  1. SSG兼容:服务端渲染时hasMounted为false,输出访客版UI,完全支持静态站点生成,不会触发全站SSR。
  2. 消除Hydration错误:客户端Hydration阶段,初始渲染与服务端输出完全一致(访客UI),避免了前后UI不匹配的问题。
  3. 无CLS问题:始终有内容显示,不会出现空白等待,符合SEO和用户体验要求。
  4. 状态更新时机正确:useEffect在Hydration完成后执行,读取Cookie并更新用户状态,此时的UI切换属于客户端挂载后的正常更新,不会触发Hydration错误。

注意事项

  • 确保getUserCookie是纯客户端方法,仅在浏览器环境中读取Cookie,不依赖服务端逻辑。
  • 若使用全局状态同步(如hookstate),需确保状态更新不会影响其他组件的Hydration流程。
  • 可根据需求添加UI切换的过渡动画,进一步提升用户体验。

内容的提问来源于stack exchange,提问作者Tomer Almog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 01:42:50