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

React自定义导航中需点击两次返回按钮才能返回的问题

React全局导航状态同步问题:返回按钮需两次点击的修复方案

问题根源

  1. 冗余历史记录添加:初始加载时从localStorage恢复状态后,调用pushState向历史栈添加了一条与当前页面重复的记录,导致历史栈存在冗余条目。
  2. 循环触发历史更新:每次st1/st2更新(包括浏览器返回触发的popState状态更新)都会调用pushState,点击返回时,popState更新状态后又会新增一条历史记录,因此需要第二次点击才能真正回到上一状态。
  3. 函数调用笔误:popState处理函数中错误使用了setApps/setLoc,而非正确的setSt1/setSt2,导致状态无法正确同步。

修复方案

通过区分状态更新的来源(初始加载、用户操作、浏览器历史操作),避免冗余的历史记录添加,同时修正笔误:

const useMyHook = (key) => {
  const [st1, setSt1] = useState(null);
  const [st2, setSt2] = useState(null);
  const [initialLoad, setInitialLoad] = useState(true);
  // 标记是否由popState触发的状态更新,防止循环添加历史记录
  const [isPopStateUpdate, setIsPopStateUpdate] = useState(false);

  // 初始加载:从localStorage恢复状态
  useEffect(() => {
    const storedFilters = JSON.parse(localStorage.getItem(key));
    if (storedFilters) {
      setSt1(storedFilters.st1);
      setSt2(storedFilters.st2);
    }
    setInitialLoad(false);
  }, [key]);

  // 同步状态到URL与localStorage
  useEffect(() => {
    // 初始加载或popState触发的更新,不添加新历史记录
    if (initialLoad || isPopStateUpdate) {
      setIsPopStateUpdate(false);
      return;
    }

    const queryParams = new URLSearchParams();
    if (st1) queryParams.set('state1', st1);
    if (st2) queryParams.set('state2', st2);
    const queryString = queryParams.toString();
    const newUrl = `${window.location.pathname}?${queryString}`;

    // 仅用户主动修改状态时,添加新历史记录
    window.history.pushState({ path: newUrl }, '', newUrl);
    localStorage.setItem(key, JSON.stringify({ st1, st2 }));
  }, [st1, st2, key, initialLoad, isPopStateUpdate]);

  // 处理浏览器前进/后退事件
  useEffect(() => {
    const handlePopState = () => {
      const queryParams = new URLSearchParams(window.location.search);
      const newSt1 = queryParams.get('state1') ? parseInt(queryParams.get('state1')) : null;
      const newSt2 = queryParams.get('state2') ? parseInt(queryParams.get('state2')) : null;
      // 标记为popState触发的更新,避免触发pushState
      setIsPopStateUpdate(true);
      setSt1(newSt1);
      setSt2(newSt2);
    };

    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, []);

  return { st1, setSt1, st2, setSt2 };
};

关键修改说明

  • 新增isPopStateUpdate状态:用于标记状态更新是否来自浏览器历史操作,此时跳过pushState调用,避免循环生成历史记录。
  • 初始加载优化:初始加载仅恢复状态,不调用pushState,避免添加冗余的初始历史条目。
  • 修正状态更新函数:将popState中的setApps/setLoc替换为正确的setSt1/setSt2,确保状态与URL同步。
  • 条件触发pushState:仅当用户主动修改状态时(非初始加载、非历史操作触发),才添加新的历史记录,保证历史栈的逻辑正确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 16:22:14