React自定义导航中需点击两次返回按钮才能返回的问题
React全局导航状态同步问题:返回按钮需两次点击的修复方案
问题根源
- 冗余历史记录添加:初始加载时从localStorage恢复状态后,调用
pushState向历史栈添加了一条与当前页面重复的记录,导致历史栈存在冗余条目。 - 循环触发历史更新:每次
st1/st2更新(包括浏览器返回触发的popState状态更新)都会调用pushState,点击返回时,popState更新状态后又会新增一条历史记录,因此需要第二次点击才能真正回到上一状态。 - 函数调用笔误:
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
相关产品推荐
相关产品推荐

