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

React部署版本导航栏响应式异常问题求助

React响应式导航栏部署后异常排查

问题描述

本地运行时导航栏响应式表现正常:移动端显示汉堡菜单、PC端显示完整导航栏;但部署后出现反转——PC端显示移动端汉堡菜单,移动端反而显示PC端的完整导航栏。

相关代码

Header组件

function Header({scrolling}) {

  const [isBurger, setIsBurger] = useState(false);
  const [isNav, setIsNav] = useState(false);
  const [isSearchBar, setIsSearchBar] = useState(false);
  const [isScreenSize, setIsScreenSize] = useState(0);

  const { width } = useWindowSize();

  // 汉堡菜单点击事件
  const handleClickOnBurger = () => {
    setIsBurger((prevIsBurger) => !prevIsBurger);
    setIsNav((prevIsNav) => !prevIsNav);
  }

  // 搜索栏点击事件
  const handleClickOnSearch = () => {
    setIsSearchBar((prevIsSearchbar) => !prevIsSearchbar)
  }

  function checkWidth() {
    if (isScreenSize >= 768) {
      setIsSearchBar(false);
      setIsBurger(false);
      setIsNav(true);
    }
  }

  useEffect(() => {
    setIsScreenSize(width);
    checkWidth();
  });

  return (
    <div className="d-flex flex-row flex-wrap justify-content-between align-items-center py-md-4 px-md-5 my-3 shadow-sm headerBody">
      <Navbar callFunc={handleClickOnBurger} btnHook={isBurger} navHook={isNav} scroller={scrolling} />
      {!isSearchBar && <Logo />}
      <Searchbar callFunc={handleClickOnSearch} btnHook={isSearchBar} />
    </div>
  );
}

function useWindowSize() {
  // 初始状态设为undefined,保证服务端与客户端渲染匹配
  const [windowSize, setWindowSize] = useState({
    width: undefined,
    height: undefined,
  });

  useEffect(() => {
    // 窗口尺寸变化处理函数
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }

    window.addEventListener("resize", handleResize);
    // 立即执行一次,获取初始窗口尺寸
    handleResize();

    // 清除监听
    return () => window.removeEventListener("resize", handleResize);
  }, []); // 空数组确保仅在挂载时执行

  return windowSize;
}

export default Header;
import NavigationLinks from "./NavigationLinks";
import { Icon } from "@iconify/react";

function Navbar(props) {

  let icon;
  const burgerIcon = "iconamoon:menu-burger-horizontal-bold";
  const xIcon = "tabler:x";

  const navBarLinks = [
    { text: "home", path: "/#heroHome" },
    { text: "about", path: "/#about" },
    { text: "for partners", path: "/#partners" }
  ]
  const styleMid = "visibilityHiddenMid";
  const styleSmall = "visibilityHiddenSmall navbarSmall";
  let styleFinal;

  return (
    <>
      <span className="visually-hidden">
        {styleFinal = props.btnHook ? styleSmall : styleMid}
        {icon = props.btnHook ? xIcon : burgerIcon}
      </span>
      <button className="bg bg-transparent ms-2 border border-0 visibilityHiddenSmall" onClick={props.callFunc}>
        <Icon icon={icon} className="color text-black fs-1 fw-lighter bg-transparent" />
      </button>
      <div className={"col-12 col-md-4 " + styleFinal}>
        <div className="d-flex flex-column flex-md-row justify-content-start justify-content-md-left mx-3 my-4 my-md-0 align-items-strat align-items-md-center">
          {navBarLinks.map((link, index) => {
            return (<NavigationLinks text={link.text} path={link.path} key={index} />)
          })}
        </div>
      </div>
    </>
  );
}

export default Navbar;

问题根源与修复方案

核心问题

  1. useEffect依赖缺失:Header组件中的useEffect没有指定依赖数组,会触发无限重渲染,部署环境(尤其是SSR/SSG场景)下会导致状态混乱。
  2. 状态同步延迟:额外维护isScreenSize状态,导致窗口尺寸变化时状态更新存在延迟,与实际尺寸不匹配。
  3. SSR适配不足:useWindowSize初始状态为undefined,部署时服务端渲染无window对象,初始尺寸无法正确获取,与本地客户端渲染的初始状态不一致。

修复步骤

1. 优化useWindowSize钩子,适配SSR

function useWindowSize() {
  // 客户端环境直接获取初始尺寸,服务端环境设为undefined
  const [windowSize, setWindowSize] = useState({
    width: typeof window !== 'undefined' ? window.innerWidth : undefined,
    height: typeof window !== 'undefined' ? window.innerHeight : undefined,
  });

  useEffect(() => {
    function handleResize() {
      setWindowSize({
        width: window.innerWidth,
        height: window.innerHeight,
      });
    }

    window.addEventListener("resize", handleResize);
    handleResize();

    return () => window.removeEventListener("resize", handleResize);
  }, []);

  return windowSize;
}

2. 修正Header组件的状态逻辑与useEffect依赖

function Header({scrolling}) {
  const [isBurger, setIsBurger] = useState(false);
  const [isNav, setIsNav] = useState(false);
  const [isSearchBar, setIsSearchBar] = useState(false);

  const { width } = useWindowSize();

  const handleClickOnBurger = () => {
    setIsBurger(prev => !prev);
    setIsNav(prev => !prev);
  }

  const handleClickOnSearch = () => {
    setIsSearchBar(prev => !prev);
  }

  useEffect(() => {
    // 处理服务端渲染时width为undefined的情况
    if (width === undefined) return;
    
    if (width >= 768) {
      // PC端:显示完整导航,隐藏汉堡按钮与搜索栏
      setIsSearchBar(false);
      setIsBurger(false);
      setIsNav(true);
    } else {
      // 移动端:隐藏导航,显示汉堡按钮
      setIsNav(false);
      setIsBurger(false);
    }
  }, [width]); // 仅在width变化时执行

  return (
    <div className="d-flex flex-row flex-wrap justify-content-between align-items-center py-md-4 px-md-5 my-3 shadow-sm headerBody">
      <Navbar callFunc={handleClickOnBurger} btnHook={isBurger} navHook={isNav} scroller={scrolling} />
      {!isSearchBar && <Logo />}
      <Searchbar callFunc={handleClickOnSearch} btnHook={isSearchBar} />
    </div>
  );
}

3. 简化Navbar组件的样式逻辑

import NavigationLinks from "./NavigationLinks";
import { Icon } from "@iconify/react";

function Navbar(props) {
  const burgerIcon = "iconamoon:menu-burger-horizontal-bold";
  const xIcon = "tabler:x";

  const navBarLinks = [
    { text: "home", path: "/#heroHome" },
    { text: "about", path: "/#about" },
    { text: "for partners", path: "/#partners" }
  ];

  // 直接在渲染时判断样式与图标,避免JSX中的副作用赋值
  const styleFinal = props.btnHook ? "visibilityHiddenSmall navbarSmall" : "visibilityHiddenMid";
  const icon = props.btnHook ? xIcon : burgerIcon;

  return (
    <>
      <button className="bg bg-transparent ms-2 border border-0 visibilityHiddenSmall" onClick={props.callFunc}>
        <Icon icon={icon} className="color text-black fs-1 fw-lighter bg-transparent" />
      </button>
      <div className={`col-12 col-md-4 ${styleFinal}`}>
        <div className="d-flex flex-column flex-md-row justify-content-start justify-content-md-left mx-3 my-4 my-md-0 align-items-start align-items-md-center">
          {navBarLinks.map((link, index) => (
            <NavigationLinks text={link.text} path={link.path} key={index} />
          ))}
        </div>
      </div>
    </>
  );
}

export default Navbar;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 23:35:54