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

React TodoList问题:localStorage刷新不保存+map key报错求助

问题修复:TodoList数据持久化与key警告

一、localStorage无法保存数据的原因及修复

你的代码存在两个核心问题导致数据无法持久化:

  • 初始状态设置为[{}],组件挂载后第二个useEffect会立刻将这个带空对象的数组存入localStorage,直接覆盖原本存储的有效数据
  • 读取localStorage时未处理空值边界,首次打开网站时localStorage为空,会导致无效的状态更新

修复方案:

  • 将todos初始状态改为空数组[]
  • 优化第一个useEffect,仅当storedTodos为有效数组时才更新状态

二、map方法key错误修复

初始状态里的空对象{}没有id属性,导致map时key={todo.id}为undefined,触发React的key警告。同时规范props命名(复数形式更符合语义):

  • 把TodoList组件接收的props从todo改为todos,同步修改App组件的传参
  • 确保所有todo项都通过uuid生成唯一id(已实现,只需移除初始空对象)

修改后的完整代码

App.js

import TodoList from "./TodoList";
import { useState, useRef, useEffect } from 'react'; // 补全缺失的钩子导入
import {v4 as uuid} from 'uuid'

function App() {
 const [todos,setTodos] = useState([]); // 初始状态改为空数组
 const inputRef = useRef();

 const LOCAL_STORAGE_KEY = "todoapp"

 useEffect(() =>{
  const storedTodos = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY))
  // 确保读取到的是有效数组再更新状态
  if(storedTodos && Array.isArray(storedTodos)){
    setTodos(storedTodos)
  }
 }, [])
 
 useEffect(() => {
  // 仅当todos非空时存入localStorage,避免覆盖初始空值
  if(todos.length > 0){
    localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(todos))
  }
 }, [todos])
 

function toggleTodo(id){
  // 改用函数式更新,避免闭包导致的状态陈旧问题
  setTodos(prevTodos => {
    return prevTodos.map(todo => {
      if(todo.id === id){
        return {...todo, complete: !todo.complete}
      }
      return todo
    })
  })
}

 function handleAdd(e) {
 const name = inputRef.current.value.trim(); // 去除首尾空格,避免空内容
 if(name === "")return 
 setTodos(prevTodos => {
    return [...prevTodos,{id:uuid(),name:name,complete:false}]
 })

 inputRef.current.value = "";
  }
 
function handleClearTodos(){
 setTodos(prevTodos => prevTodos.filter(todo=>!todo.complete))
}
  

  return (
  <>
  <h1>Chores!!</h1>
    <TodoList todos={todos} toggleTodo={toggleTodo} /> {/* 同步修改props名称 */}
  <input ref={inputRef} type="text" />
  <button onClick={handleAdd}>Add todo</button>
  <button onClick={handleClearTodos}>Clear completed todos</button>
  <div> {todos.filter(todo => !todo.complete).length} todos left</div>
       
</>
  )

}
export default App;

TodoList.js

import Todo from './Todo'

// 修改props名为todos,语义更清晰
export default function TodoList({todos, toggleTodo}) {
  return (
    todos.map((todo)=> {
      return <Todo key={todo.id} todo={todo} toggleTodo={toggleTodo} />
    })
  )
}

额外优化说明

  • 补全了useState和useEffect的导入(原代码遗漏,会导致运行错误)
  • 优化toggleTodo函数,使用函数式更新避免状态陈旧问题
  • 给输入值添加trim(),防止创建空内容的todo
  • 调整按钮文字为更明确的“Clear completed todos”

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 02:30:57