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

Next.js使用react-bootstrap Navbar刷新后CSS异常报错如何解决

Next.js项目使用react-bootstrap Navbar刷新后样式错乱问题

最近我在Next.js项目中使用react-bootstrap的Navbar组件时遇到异常,不清楚问题根源:
页面首次加载时Navbar展示正常:
正常加载效果图
但刷新页面后就会出现CSS样式错乱问题:
样式错乱效果图
同时控制台会抛出对应的报错信息:
控制台报错截图
我的Navbar组件代码如下:

<Navbar collapseOnSelect expand="lg" bg="danger" variant="dark">
  <Container>
    <Link href="/" passHref>
      <Navbar.Brand>Tecnologia Única</Navbar.Brand>
    </Link>
    <Navbar.Toggle aria-controls="responsive-navbar-nav" />
    <Navbar.Collapse id="responsive-navbar-nav">
      {auth.isLoggedIn && (
        <>
          <Nav className="me-auto">
            <NavDropdown title="Usuários" id="collasible-nav-dropdown">
              <Link href="/users/" passHref>
                <NavDropdown.Item>Listagem</NavDropdown.Item>
              </Link>
              <Link href="/users/create" passHref>
                <NavDropdown.Item>Novo</NavDropdown.Item>
              </Link>
            </NavDropdown>
          </Nav>
          <Nav className={styles.center}>
            <Navbar.Text>Bem-vindo, {auth.user.name}</Navbar.Text>
            <Nav.Link onClick={logoutHandler}>
              <Button variant="outline-light">Sair</Button>
            </Nav.Link>
          </Nav>
        </>
      )}

      {!auth.isLoggedIn && (
        <Nav>
          <Link href="/login" passHref>
            <Nav.Link>
              <Button variant="outline-light">Entrar</Button>
            </Nav.Link>
          </Link>
        </Nav>
      )}
    </Navbar.Collapse>
  </Container>
</Navbar>

补充:auth.isLoggedIn的实现代码如下:

import { createContext, useState, ReactNode } from "react";
import { useRouter } from "next/router";

interface IAuthContext {
  isLoggedIn: boolean;
  token: string | null;
  user: IUserData;
  onLogout: () => void;
  onLogin: (data: LoginData) => void;
}

interface IUserData {
  name: string;
  email: string;
  document: string;
  perfil: number;
}

const AuthContext = createContext<IAuthContext>({
  isLoggedIn: false,
  token: "",
  user: { document: "", email: "", name: "", perfil: 0 },
  onLogout: () => {},
  onLogin: (data: LoginData) => {},
});

type AuthContextProviderProps = {
  children: ReactNode;
};

type LoginData = {
  Id: number | null;
  IsUsuarioAtivo: boolean;
  TipoPerfil: number;
  Token: string;
  Nome: string;
  CPF: string;
  Email: string;
  Login: string | null;
  Perfis: string | null;
  UserIdFB: string | null;
  AccessTokenFB: string | null;
};

export const AuthContextProvider = (props: AuthContextProviderProps) => {
  const router = useRouter();

  let initialToken: string | null = "";
  if (typeof window !== "undefined") {
    initialToken = localStorage.getItem("token");
  }

  const [token, setToken]: any = useState(initialToken);
  const isUserLoggedIn = !!token;

  const [user, setUser]: any = useState<IUserData>({
    document: "",
    email: "",
    name: "",
    perfil: 0,
  });

  const logoutHandler = () => {
    setToken(null);
    localStorage.removeItem("token");
    router.push("/login");
  };

  const loginHandler = (data: LoginData) => {
    console.log(data);
    setToken(data.Token);
    localStorage.setItem("token", data.Token);
    setUser({
      name: data.Nome,
      email: data.Email,
      document: data.CPF,
      perfil: data.TipoPerfil,
    });
    router.push("/");
  };

  return (
    <AuthContext.Provider
      value={{
        user: user,
        token: token,
        isLoggedIn: isUserLoggedIn,
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

export default AuthContext;

问题原因

该问题是Next.js服务端渲染(SSR)与客户端hydration结构不匹配导致的:

  1. 服务端渲染阶段没有window对象,你代码里typeof window !== "undefined"判断为false,initialToken为空字符串,isUserLoggedIn值为false,所以服务端返回的HTML是未登录状态的Navbar结构,仅显示「Entrar」按钮。
  2. 客户端hydration阶段会读取localStorage里存储的token,此时isUserLoggedIn变为true,客户端渲染的是登录后带用户菜单的Navbar结构,和服务端返回的HTML结构不一致,触发hydration mismatch错误,react-bootstrap的样式依赖组件结构生成,结构不匹配就会出现样式错乱。

解决方案

方案1:延迟登录状态判断,保证hydration一致性

在AuthContext里添加挂载状态判断,等客户端组件挂载完成后再读取localStorage的token,确保服务端和客户端首次渲染结构完全一致:

// 修改AuthContextProvider代码
export const AuthContextProvider = (props: AuthContextProviderProps) => {
  const router = useRouter();
  // 初始token设为null,服务端和客户端首次渲染保持一致
  const [token, setToken] = useState<string | null>(null);
  const [isHydrated, setIsHydrated] = useState(false);

  // 仅在客户端挂载后读取本地存储的token
  useEffect(() => {
    const storedToken = localStorage.getItem("token");
    setToken(storedToken);
    setIsHydrated(true);
  }, []);

  const isUserLoggedIn = !!token;
  // 其余代码保持不变...

  // 未完成hydration时返回占位内容,避免结构差异
  if (!isHydrated) return <div style={{ height: '56px' }} />; // 高度和Navbar保持一致即可

  return (
    <AuthContext.Provider
      value={{
        user: user,
        token: token,
        isLoggedIn: isUserLoggedIn,
        onLogout: logoutHandler,
        onLogin: loginHandler,
      }}
    >
      {props.children}
    </AuthContext.Provider>
  );
};

方案2:禁用Navbar组件的SSR

使用Next.js的dynamic导入,指定Navbar组件仅在客户端渲染,直接规避服务端和客户端结构不一致的问题:

// 在引入Navbar的页面/组件中修改导入方式
import dynamic from 'next/dynamic';
const CustomNavbar = dynamic(() => import('../components/Navbar'), { ssr: false });

// 直接使用<CustomNavbar />即可

额外检查

确保你在项目的_app.tsx/_app.jsx中全局引入了bootstrap样式,避免样式加载顺序问题:

import 'bootstrap/dist/css/bootstrap.min.css';

内容的提问来源于stack exchange,提问作者Matheus Foresto Moselli

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 05:57:02