Pushstate和popstate在条件渲染组件场景下无法正常工作如何解决
修复方案
原有代码的核心问题如下:
- useState的状态更新是异步操作,在handleClick回调中刚执行setShow2(true)就判断if(show2),此时读取到的show2仍是更新前的旧值,永远无法触发show3的显示逻辑
- 仅在组件挂载时调用了一次history.pushState,后续每次切换页面都没有将新的页面状态存入浏览器历史栈,导致前进后退时读取不到对应页面的状态
- 监听popstate的useEffect没有添加空依赖数组,会在每次组件渲染时重复绑定事件;同时移除事件时传入的回调和绑定时的回调不是同一个引用,无法正确移除事件,会导致内存泄漏和逻辑冲突
- popstate的状态判断逻辑仅覆盖了从show2切回show1的场景,没有处理show3切回show2等其余场景
- 初始化的stateForHistory全为false,和组件挂载时show1=true的实际状态不匹配
下面是可直接运行的修复后代码:
import { useState, useEffect } from 'react'; import Screen1 from './Screen1'; import Screen2 from './Screen2'; import Screen3 from './Screen3'; export default function App() { // 用单个变量管理当前步骤,替代三个独立的show状态,更易维护 const [currentStep, setCurrentStep] = useState(1); // 组件挂载时,存入初始页面状态 useEffect(() => { window.history.pushState({ currentStep: 1 }, "", ""); }, []); // 绑定popstate监听,仅挂载一次,卸载时正确移除 useEffect(() => { const handlePopstate = (e) => { if (e.state?.currentStep) { setCurrentStep(e.state.currentStep); } }; window.addEventListener("popstate", handlePopstate); return () => { window.removeEventListener("popstate", handlePopstate); }; }, []); const handleNext = () => { const nextStep = currentStep + 1; if (nextStep > 3) return; // 更新页面状态 setCurrentStep(nextStep); // 把新的步骤状态推入历史栈 window.history.pushState({ currentStep: nextStep }, "", ""); }; return ( <div className="App"> <div id="screen-1"> {currentStep === 1 && <Screen1 />} </div> <div id="screen-2"> {currentStep === 2 && <Screen2 />} </div> <div id="screen-3"> {currentStep === 3 && <Screen3 />} </div> {currentStep < 3 && ( <button onClick={handleNext} id="btn"> 下一步 </button> )} </div> ); }
核心实现逻辑说明:
- 用单个currentStep变量管理当前展示的页面,替换三个独立的show状态,大幅简化状态判断逻辑
- 每次点击下一步切换页面时,同步将新的步骤状态推入浏览器历史栈,保证历史栈的每一条记录都对应一个页面状态
- popstate事件触发时,直接读取历史记录中存储的currentStep值更新状态即可,不需要额外写多分支判断
- 事件监听逻辑仅在组件挂载时执行一次,卸载时用同一个回调引用移除事件,避免内存泄漏
内容的提问来源于stack exchange,提问作者Shahir Riaz
相关产品推荐
相关产品推荐

