React浏览器刷新时状态持久化不生效 计数重置为0问题排查
问题根因
持久化失效的核心原因是useEffect执行顺序+闭包值捕获机制导致存储的有效值被初始值意外覆盖,同时存在一处空值未兜底的隐患:
- 组件首次渲染时,
count的初始值硬编码为0,两个useEffect会按代码声明顺序在挂载阶段依次执行 - 第一个负责读取存储的useEffect虽然调用
setCount拿到了之前存的有效值,但React的setState是异步调度的,不会立刻修改当前渲染闭包里的count值 - 紧接着执行的第二个负责写入存储的useEffect,捕获的还是首次渲染闭包里的
count=0,会直接把sessionStorage里之前存的有效值覆盖为0,等后续重渲染把count更新为正确值时,存储已经被错误改写,刷新后自然读不到之前的状态 - 额外隐患:首次访问应用时sessionStorage中不存在
count字段,getItem返回null,直接执行JSON.parse(null)会得到null,没有兜底值的话会导致count变成非数字类型,引发渲染、计算异常
修复方案
不需要单独用空依赖的useEffect做初始化读取,直接用React.useState的懒初始化能力,在声明state的时候就同步读取sessionStorage的值做初始值,从根源上避免初始值覆盖存储的问题:
- 删掉原来负责读取存储的空依赖useEffect
- 改写count的state声明,加入空值兜底
- 保留原来监听count变化写入sessionStorage的逻辑即可
修复后的核心代码:
// state初始化时直接读取存储,逻辑仅在组件首次渲染时执行一次 const [count, setCount] = useState(() => { const storedCount = sessionStorage.getItem('count'); // 无历史存储值时兜底为初始值0 return storedCount ? JSON.parse(storedCount) : 0; }); // count变化时同步写入存储 useEffect(() => { sessionStorage.setItem('count', JSON.stringify(count)); }, [count]); // 点击处理逻辑无需修改可正常运行,也可以简化写法 const plusHandler = () => setCount(count + 1); const minusHandler = () => setCount(count - 1);
补充说明:写入sessionStorage时直接传入数字虽然会被自动转成字符串,但显式用
JSON.stringify包裹是更稳妥的写法,后续如果要持久化对象、布尔值等非字符串类型也不需要改写逻辑。
内容的提问来源于stack exchange,提问作者Steen Toons
相关产品推荐
相关产品推荐

