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

React中localStorage页面刷新后数据丢失问题排查

问题根因

你遇到的刷新后数据丢失问题,本质是useEffect执行时序和React状态异步更新的竞态问题导致的:

  • 组件首次渲染时,boxes会被直接初始化为硬编码的示例Box 1数组
  • 渲染完成后,负责写入localStorage的useEffect会在读取localStorage的逻辑完成状态更新前,先把初始的示例数组写入localStorage,直接覆盖掉用户之前存储的自定义内容
  • 如果你用的是React 18+版本且开发环境开启了严格模式,组件会执行挂载->卸载->重新挂载的双调用流程,这个覆盖问题会稳定复现:第一次挂载时写副作用已经把localStorage覆盖成初始示例数组,第二次挂载时读副作用只能读到被覆盖后的示例数据,最终刷新后永远只显示Box 1。
  • 额外体验问题:把读localStorage的逻辑放在空依赖的useEffect里,会导致首屏先渲染默认的Box 1,再跳转到实际存储的内容,出现内容闪烁。
修复方案

直接调整状态初始化逻辑,移除多余的读副作用,从根源避免竞态:

  1. 利用useState的懒初始化能力,组件首次创建状态时直接从localStorage读取存储值,没有已存数据时才返回默认示例数组
  2. 删除单独用来读localStorage的useEffect,只保留监听boxes变化同步写入localStorage的副作用即可
  3. 修正代码首行手滑打错的导入语句(原代码第一行mport缺了首字母i)

修复后的完整代码如下:

import React, { Fragment, useState, useRef, useEffect } from 'react';
import { v4 as uuidv4} from 'uuid';
import { BoxList } from './components/BoxList';
import { SearchBox } from './components/SearchBox';

const KEY = "boxApp.boxes";

export function App() {
  // 懒初始化状态:仅首次挂载时执行一次读localStorage逻辑
  const [boxes, setBoxes] = useState(() => {
    const storedData = localStorage.getItem(KEY);
    return storedData ? JSON.parse(storedData) : [ {id: 1, title: 'Box 1', content: 'This is just an example of a box, remove it and create yours!' } ];
  })
  
  const boxTitleRef = useRef();
  const boxContentRef = useRef();

  // 仅在boxes变化时同步写入localStorage
  useEffect(() =>{
    localStorage.setItem(KEY, JSON.stringify(boxes));
  }, [boxes]);

  const removeBox = (id) => {
    const remainingBoxes = boxes.filter((box) => box.id !== id);
    setBoxes(remainingBoxes);
  }

  const addNewBox = (evt) => {
    evt.preventDefault();
    const boxTitle = boxTitleRef.current.value;
    const boxContent = boxContentRef.current.value;
    if (boxTitle === '' || boxContent === '') return;
    setBoxes( previousBoxes => {
      return ([...previousBoxes, { id: uuidv4(), title: boxTitle, content: boxContent}])
    });

    boxTitleRef.current.value = null;
    boxContentRef.current.value = null;
  }

  return (
    <Fragment>
      <SearchBox  />
      <div>
        <h2>Add a box</h2>
        <form>
         <input type='text' placeholder='Title' ref={boxTitleRef}></input>
          <input type='text' placeholder='Content' ref={boxContentRef}></input>
          <button type='submit' onClick={addNewBox}>➕</button>
        </form>
      </div>
      <BoxList boxes={boxes} removeBox={removeBox}/>
    </Fragment>
  );
}

export default App;
修复逻辑说明
  • 状态懒初始化只会在组件首次挂载时执行一次,不会出现硬编码初始值提前覆盖localStorage的问题,同时首屏会直接渲染存储的内容,不会出现默认内容闪烁
  • 移除多余的读副作用后,不存在两个副作用的执行顺序竞态,所有对boxes的增删操作都会自动同步到localStorage,刷新页面后数据不会丢失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 06:30:38