React添加新日期后原有日期绑定的作业本地state被重置该如何解决
问题根因
- 核心原因是列表渲染的key设置错误:你在
DatesDisplay组件遍历日期数组时,直接用uuidv4()作为key值,每次dates数组更新触发组件重渲染时,所有日期项的key都会重新生成,React会判定所有旧的Date组件需要销毁并重新创建,每个Date组件内部存储作业数据的本地state自然会被清空。同理AssignmentDisplay组件遍历作业列表时也用了动态生成的uuid作为key,也存在作业状态丢失的隐患。 - 次要隐患是作业数据存放在子组件本地state:如果后续组件结构调整、或者需要做数据持久化、跨组件操作作业数据,子组件本地state的灵活性会比较差。
解决方案
1. 快速修复(仅解决新增日期作业丢失问题)
直接将DatesDisplay组件的key替换为固定值,比如当前的日期字符串(保证日期不重复的前提下):
// DatesDisplay组件修改后代码 import React from 'react' import Date from './Date'; const DatesDisplay = ({ dates }) => { return ( <div className='assignment-display'> {dates.map((date) => { return ( <div key={date}> <Date date={date}/> </div> ) })} </div> ) } export default DatesDisplay
同时修改AssignmentDisplay的key,避免新增作业时旧作业状态异常,需要先调整作业提交逻辑,给每个作业生成固定id:
首先修改AssignmentInput的提交逻辑,导入uuid,给作业加id:
// AssignmentInput组件修改部分 import { v4 as uuidv4 } from 'uuid'; // ...其他代码不变 const submitNewAssignment = (e) => { e.preventDefault(); addAssignment({ ...assignment, id: uuidv4() // 新增唯一id }); setAssignment({ name: '', subject: '', }); }
然后修改AssignmentDisplay用作业id作为key:
// AssignmentDisplay组件修改后代码 import React from 'react' import Assignment from './Assignment'; const AssignmentDisplay = ({ assignments }) => { return ( <div> {assignments.map((assignment) => { return ( <div key={assignment.id}> <Assignment name={assignment.name} subject={assignment.subject}/> </div> ) })} </div> ) } export default AssignmentDisplay
2. 优化方案(状态提升,便于后续扩展)
如果后续需要实现作业删除、批量修改、数据持久化等功能,建议把所有数据提到顶层AssignmentCalendar组件统一管理,每个日期对象自带作业列表:
首先修改AssignmentCalendar组件:
import { useState } from 'react'; import { v4 as uuidv4 } from 'uuid'; import Button from './Button'; import DatesInput from './DatesInput'; import DatesDisplay from './DatesDisplay'; const AssignmentCalendar = () => { const [dates, setDates] = useState([]); const addDate = (dateStr) => { // 每个日期存储为带唯一id、日期字符串、作业列表的对象 setDates(dates.concat({ id: uuidv4(), dateStr: dateStr, assignments: [] })) } // 新增修改指定日期作业的方法 const updateAssignments = (dateId, newAssignment) => { setDates(dates.map(date => { if(date.id === dateId) { return { ...date, assignments: [...date.assignments, newAssignment] } } return date })) } return ( <main className="main-display"> <h3>Assignment Calendar</h3> <h4 className="dates-Title">Dates</h4> <Button text='Add Date' color='green' /> <DatesInput dates={dates} addDate={addDate}/> <DatesDisplay dates={dates} updateAssignments={updateAssignments} /> </main> ) } export default AssignmentCalendar
然后调整DatesDisplay传递参数:
const DatesDisplay = ({ dates, updateAssignments }) => { return ( <div className='assignment-display'> {dates.map((date) => { return ( <div key={date.id}> <Date date={date.dateStr} dateId={date.id} assignments={date.assignments} updateAssignments={updateAssignments} /> </div> ) })} </div> ) }
再修改Date组件,移除内部的作业state,用父组件传递的数据和方法:
import React from 'react'; import Assignments from './Assignments'; import Button from './Button'; const Date = ({ date, dateId, assignments, updateAssignments }) => { const addAssignment = (assignment) => { updateAssignments(dateId, assignment) } return ( <div> <h5>{date} <Button text='Add Assignment'/></h5> <div className='assignment-display'> <Assignments assignments={assignments} addAssignment={addAssignment} /> </div> </div> ) } export default Date
关于localStorage的说明
你当前的问题属于代码逻辑bug,不需要用localStorage就能解决。只有你需要实现「关闭页面/刷新页面后,之前录入的日期和作业记录依然留存」的需求时,才需要用到localStorage:只需要在顶层AssignmentCalendar组件的dates状态更新时,将dates数组序列化后存入localStorage,组件初始化时从localStorage读取数据反序列化作为初始值即可。
内容的提问来源于stack exchange,提问作者amherro
相关产品推荐
相关产品推荐

