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

React Todo列表:页面刷新时localStorage被Context空数组覆盖求助

解决React Context Todo列表刷新数据丢失问题

问题根源

  1. Context的useState初始值固定为空数组,页面刷新时会先以空数组作为初始状态,后续组件内的存储逻辑可能会将空数组写入localStorage,直接覆盖原有数据。
  2. Form和List组件重复实现了读取/写入localStorage的逻辑,导致状态同步行为重复触发,加剧了数据覆盖的问题。

修复方案

1. 重构Context Provider,统一管理状态与存储逻辑

将localStorage的读取和写入逻辑集中在Context内部,避免组件重复处理,同时确保初始状态直接从localStorage加载:

import React from "react";

export const TodoContext = React.createContext();

export function TodoComponent({ children }) {
  // 初始化时优先读取localStorage中的数据,无数据则用空数组
  const [todoItems, setTodoItems] = React.useState(() => {
    const savedTodos = localStorage.getItem("todos");
    return savedTodos ? JSON.parse(savedTodos) : [];
  });

  // 封装状态更新方法,自动同步到localStorage
  const updateTodoItems = (newTodos) => {
    setTodoItems(newTodos);
    localStorage.setItem("todos", JSON.stringify(newTodos));
  };

  return (
    <TodoContext.Provider value={[todoItems, updateTodoItems]}>
      {children}
    </TodoContext.Provider>
  );
}

2. 简化Form组件,移除重复存储逻辑

组件仅专注于表单交互,状态更新直接调用Context提供的方法:

function Form() {
  const [todoItems, updateTodoItems] = useContext(TodoContext);
  const [todoInput, setTodoInput] = useState("");

  const handleSubmit = (e) => {
    e.preventDefault();

    const trimmedInput = todoInput.trim();
    if (trimmedInput.length >= 1) {
      updateTodoItems([
        ...todoItems,
        // 用时间戳生成唯一ID,避免数组长度变化导致ID重复
        { id: Date.now(), name: trimmedInput },
      ]);
      setTodoInput("");
    }
  };

  return (
    <>
      <FormWrapper>
        <form action="" onSubmit={handleSubmit}>
          <input
            type="text"
            placeholder="Todo"
            value={todoInput}
            onChange={(e) => setTodoInput(e.target.value)}
          />
          <input type="submit" value="添加" />
        </form>
      </FormWrapper>
    </>
  );
}

3. 简化List组件,移除重复存储逻辑

组件仅负责渲染列表和删除操作:

function List() {
  const [todoItems, updateTodoItems] = useContext(TodoContext);

  const deleteButton = (e, id) => {
    e.preventDefault();
    const newArray = todoItems.filter(item => item.id !== id);
    updateTodoItems(newArray);
  };

  return (
    <>
      <ListWrapper>
        <ul className="list">
          {todoItems?.map((item) => (
            <li className="list__item" key={item.id}>
              <div className="todo">
                <span className="todo__label">{item.name}</span>
                <button
                  className="todo__delete"
                  onClick={(e) => deleteButton(e, item.id)}
                >
                  删除
                </button>
              </div>
            </li>
          ))}
        </ul>
      </ListWrapper>
    </>
  );
}

额外优化说明

  • 用Date.now()生成Todo的唯一ID,比依赖数组长度更可靠,避免删除后新增项出现ID重复问题。
  • 提交时对输入内容做trim()处理,过滤纯空格的无效输入。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 15:55:14