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

React Todo List页面刷新时Local Storage被清空的问题求助

问题解决:React Todo List刷新后Local Storage被清空的问题

问题根源

你的代码存在执行顺序导致的覆盖问题:

  1. 组件初始化时,useState将todos设为空数组[]。
  2. 初始渲染完成后,第二个依赖todos和status的useEffect会先执行,此时todos是空数组,saveLocalTodos会把空数组存入Local Storage,直接覆盖原本存储的数据。
  3. 之后第一个空依赖的useEffect才执行getLocalTodos,但此时Local Storage已经被清空,只能读取到空数组,最终导致每次刷新都丢失数据。

解决方案

方案1:直接从Local Storage初始化useState

这是最简洁的方式,跳过初始空数组阶段,避免不必要的存储操作:

// 直接从Local Storage读取初始值,不存在则设为空数组
const [todos, setTodos] = useState(() => {
  const savedTodos = localStorage.getItem('todos');
  return savedTodos ? JSON.parse(savedTodos) : [];
});

// 只保留处理过滤和存储的useEffect
useEffect(() => {
  filterHandler();
  saveLocalTodos();
}, [todos, status]);

const filterHandler = () => {
  switch(status){
    case 'completed':
      setFilteredTodos(todos.filter(todo => todo.completed))
      break;
    case 'todo':
      setFilteredTodos(todos.filter(todo => !todo.completed))
      break;
    default:
      setFilteredTodos(todos);
      break;
  }
}

const saveLocalTodos = () => {
  localStorage.setItem("todos", JSON.stringify(todos))
}

// 移除原来的getLocalTodos和对应的useEffect,因为初始值已从Local Storage获取

方案2:调整useEffect执行逻辑(保留原结构)

如果不想修改useState初始化方式,可以给第二个useEffect加判断,避免初始空数组覆盖Local Storage:

const [todos, setTodos] = useState([]);

useEffect(()=>{
  getLocalTodos();
}, [])

useEffect(() => {
  filterHandler();
  // 只有当todos不是初始空数组时才存储,避免覆盖原有数据
  if (todos.length > 0) {
    saveLocalTodos();
  }
}, [todos, status]);

// 其余函数保持不变
const filterHandler = () => {
  switch(status){
    case 'completed':
      setFilteredTodos(todos.filter(todo => todo.completed))
      break;
    case 'todo':
      setFilteredTodos(todos.filter(todo => !todo.completed))
      break;
    default:
      setFilteredTodos(todos);
      break;
  }
}

const saveLocalTodos = () => {
  localStorage.setItem("todos", JSON.stringify(todos))
}

const getLocalTodos = () => {
  if(localStorage.getItem('todos') === null){
    localStorage.setItem("todos", JSON.stringify([]));
  }else{
    let todoLocal = JSON.parse(localStorage.getItem("todos"))
    setTodos(todoLocal)
  }
}

额外优化建议

filterHandler里调用setFilteredTodos会触发组件重新渲染,你可以用useMemo计算过滤后的列表,减少不必要的渲染:

const filteredTodos = useMemo(() => {
  switch(status){
    case 'completed':
      return todos.filter(todo => todo.completed);
    case 'todo':
      return todos.filter(todo => !todo.completed);
    default:
      return todos;
  }
}, [todos, status]);

这样就不需要单独的filteredTodos状态和filterHandler函数了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 02:15:36