React待办应用Local Storage存储复选框失效及报错问题
解决React待办应用Local Storage持久化的对象渲染错误
这个错误的核心原因很明确:你把React组件元素直接存在了state的toDo数组里,然后把整个数组序列化存到Local Storage。当你从Local Storage取出来的时候,原来的React元素已经被JSON转换成了普通的JavaScript对象(就是错误提示里的{key, ref, props, _owner, _store}),React根本没法把这个对象当作子元素渲染,所以就抛出了这个错误。
正确的思路:存数据,而非组件
React state里应该存储的是纯数据(比如字符串、数字、普通对象),而不是React组件本身。组件是用来渲染数据的,数据才是需要持久化的内容。我们需要重构你的代码,把任务的状态数据存在state里,渲染时再根据数据生成组件。
修改后的完整代码
import React, { useState, useEffect } from "react"; import { Delete, Refresh, Add } from "../components/Actions"; import { Header } from "../components/Header"; import { PriorityLists } from "../components/PriorityLists"; import { v4 as uuidv4 } from 'uuid'; function Task() { // 初始化时从Local Storage取纯数据,而不是带组件的对象 const [toDo, setToDo] = useState(() => { const storedTasks = localStorage.getItem('tasks'); return storedTasks ? JSON.parse(storedTasks) : []; }); const [idsToRefresh, setIdsToRefresh] = useState([]); const [filter_items, setFilterItems] = useState(false); // 当toDo数据变化时,持久化到Local Storage useEffect(() => { localStorage.setItem('tasks', JSON.stringify(toDo)); }, [toDo]); // 删除已勾选的任务逻辑 useEffect(() => { if (filter_items) { setToDo(prev => prev.filter(item => !item.checked)); setFilterItems(false); // 重置过滤状态 } }, [filter_items]); // 更新任务的勾选状态 function toggleTaskChecked(id, checked) { setToDo(prev => prev.map(task => task._Id === id ? { ...task, checked } : task )); } // 添加新任务:存纯数据,而非组件 function addToDos() { const id = uuidv4(); // 这里只存任务的基础数据:ID、勾选状态、优先级(如果需要的话) setToDo(prev => [...prev, { _Id: id, checked: false }]); setIdsToRefresh(prev => [...prev, id]); } // 刷新(删除刚添加的任务) function refresh() { setToDo(prev => prev.filter(item => !idsToRefresh.includes(item._Id))); setIdsToRefresh([]); // 清空刷新ID列表 } return ( <div className="main-content"> <div className="container-fluid"> <div className="row underline"> <div className="col"> <div className="row"> <div className="col-3 pt-2"> <Refresh _refresh={refresh} /> </div> <div className="col-6 text-center"> <Header header={"Tasks"} /> </div> <div className="col-3 pt-2"> <button className="float-right"> <Delete setFilterItems={setFilterItems} filter={filter_items} /> </button> </div> </div> </div> </div> <div className="row"> <div className="col"> {toDo.map(item => ( <div key={item._Id}> <ul> <li> {/* 渲染时根据数据生成PriorityLists组件 */} <PriorityLists id={item._Id} checked={item.checked} addIds={toggleTaskChecked} // 注意这里改了函数名,更语义化 /> </li> </ul> </div> ))} </div> </div> <div className="row"> <div className="col pr-4"> <Add addToDos={addToDos} /> </div> </div> </div> </div> ) } export default Task;
关键修改点说明
- state存储纯数据:
toDo数组里的每个元素现在是普通对象,只包含_Id和checked状态(如果你的PriorityLists需要其他数据,比如优先级,可以在这里添加对应的字段)。 - 渲染时生成组件:在
map循环里,我们根据每个任务的数据渲染<PriorityLists>组件,而不是直接渲染存储的组件元素。 - 修正状态更新逻辑:把原来的
addIds改成toggleTaskChecked,直接更新toDo数组中对应任务的checked状态,这样状态变化会触发Local Storage的持久化。 - 优化Local Storage初始化:用函数式初始化
useState,避免每次组件渲染都执行JSON.parse。 - 简化删除逻辑:删除已勾选任务时,直接过滤
toDo数组中checked为true的项,不需要额外维护ids数组。
这样修改后,你存到Local Storage的就是纯JSON数据,不会再出现React元素被序列化的问题,页面跳转后数据也能正确从Local Storage读取并渲染。
内容的提问来源于stack exchange,提问作者Ron
相关产品推荐
相关产品推荐

