React任务勾选后无法显示在已完成区域及持久化问题
问题修复方案
核心问题分析
- 已完成任务存储为嵌套数组:
handlerCompleted中用slice获取任务,返回的是数组而非单个字符串,导致completed数组元素是嵌套结构,渲染时无法正确显示文本。 - 子组件状态未同步父组件props:
Completed组件的completedTasks仅在初始化时接收父组件的completed,后续父组件更新时未同步,导致新添加的已完成任务不触发子组件渲染。 - 全局状态未持久化:父组件
AddItem的tasks和completed状态未从localStorage初始化,页面刷新后父组件数据丢失,仅子组件读取存储会导致数据不一致。
分步修复代码
1. 修复父组件AddItem的任务处理与状态持久化
import { useState, useEffect } from "react"; import Completed from "../completed/Completed"; import TodoList from "../todoList/TodoList"; import "./additems.css"; const AddItem = () => { // 从localStorage初始化状态,避免刷新丢失 const [tasks, setTasks] = useState(() => { return JSON.parse(localStorage.getItem("tasks")) || []; }); const [inputValue, setInputValue] = useState(""); const [editingIndex, setEditingIndex] = useState(null); const [completed, setCompleted] = useState(() => { return JSON.parse(localStorage.getItem("completedTasks")) || []; }); const currentDate = new Date().toLocaleString('en-US', { weekday: 'long' }); // 监听tasks变化,同步到localStorage useEffect(() => { localStorage.setItem("tasks", JSON.stringify(tasks)); }, [tasks]); // 监听completed变化,同步到localStorage useEffect(() => { localStorage.setItem("completedTasks", JSON.stringify(completed)); }, [completed]); const handlerInputChange = (e) => { let textInp = e.target.value; if (textInp.length <= 70) { setInputValue(textInp); } } const handlerAdd = (e) => { e.preventDefault(); if (inputValue.trim() === "") { return; } setTasks([...tasks, `${inputValue} - ${currentDate}`]); setInputValue(""); setEditingIndex(tasks.length); } const handlerDelete = (index) => { const newTasks = [...tasks]; newTasks.splice(index, 1); setTasks(newTasks); } const handlerCompleted = (index) => { const newTasks = [...tasks]; // 直接取单个任务,而非slice返回的数组 const completedTask = newTasks[index]; setCompleted([...completed, completedTask]); setTasks(newTasks.slice(0, index).concat(newTasks.slice(index + 1))); } const renderItems = (arr) => { const items = arr.map((item, index) => { if (index === editingIndex) { return ( <li key={index} className="list_save"> <div className="date_number"> <p>{index + 1}</p> </div> <input maxLength={50} className="task_value" type="text" defaultValue={item} onChange={(e) => { const newTasks = [...tasks]; newTasks[index] = e.target.value; setTasks(newTasks); }} /> <button onClick={() => setEditingIndex(null)} className="save_task" >Save</button> </li> ) } else { return ( <li key={index} className="list_edit"> <input type="checkbox" className="checkbox_completed" onClick={() => handlerCompleted(index)} /> <p>{item}</p> <button onClick={() => setEditingIndex(index)} className="edit_task" >Edit</button> <button onClick={() => handlerDelete(index)} className="delete_task" >Delete</button> </li> ); } }) return ( <ol className="items_list"> {items} </ol> ) } const items = renderItems(tasks); return ( <div> <form className="form_add" onSubmit={handlerAdd} > <input className="input_add" maxLength={50} type="text" value={inputValue} onChange={handlerInputChange} /> <button className="btn_add" type="submit">Add</button> </form> <TodoList task={items} /> <Completed completed={completed} /> </div> ) } export default AddItem;
2. 修复子组件Completed的状态同步逻辑
import { useState, useEffect } from "react"; import "./completed.css"; const Completed = (props) => { const { completed } = props; const [completedTasks, setCompletedTasks] = useState(completed); // 监听父组件props变化,同步更新子组件状态 useEffect(() => { setCompletedTasks(completed); }, [completed]); return ( <div className="completed_list"> <h3>Completed</h3> <div className="line_completed"></div> {completedTasks.map((task, index) => { return ( <ul key={index}> <li>{task}</li> </ul> ) })} </div> ) } export default Completed;
修复说明
- 嵌套数组问题:将
newTasks.slice(index, index + 1)改为newTasks[index],直接获取单个任务字符串,确保completed数组存储的是文本而非嵌套数组。 - props同步问题:在
Completed组件中添加监听completedprops的useEffect,父组件更新已完成任务时,子组件自动同步状态并重新渲染。 - 数据持久化统一:将localStorage的读写逻辑移到父组件,初始化状态时从存储读取,状态变化时同步存储,确保父组件和子组件的数据一致,刷新后不会丢失。
内容的提问来源于stack exchange,提问作者Denis Dev
相关产品推荐
相关产品推荐

