React子组件调用props触发页面重渲染异常问题问询
React状态更新后未触发重渲染问题排查
问题背景
我编写了一个任务列表示例,功能分为四个模块,各模块颜色标识如下:
- 浅蓝色:标题区
- 红色:添加任务区
- 珊瑚色:批量更新任务区
- 浅灰色:单日任务区
输入任务名称、时间、日期后,点击create new task可将任务添加到task状态中,任务会逐层传递并按日期排序,最终在子组件的下拉列表展示。目前已实现单日任务批量更新,但调用props.setTask后未触发页面重渲染;添加新任务的操作却能正常触发重渲染,不清楚问题出在哪,怀疑对React状态生命周期的理解有误。
问题复现步骤
- 创建任务
- 打开任务所属日期的下拉列表
- 修改日期下拉选项
- 页面未触发重渲染
核心原因
React触发重渲染的前提是状态引用发生变化(浅比较机制)。当前代码的问题出在WeekdayTable组件的updateAllWeekDay函数中:
- 直接获取了
props.task的引用(let masterTask = props.task),然后修改了数组内元素的taskDay属性,属于直接修改原状态的引用类型数据 - 最后调用
props.setTask(masterTask)时,传入的还是原数组的引用,React浅比较后认为状态没有变化,因此不触发重渲染 - 而添加任务时用了
task.concat(tempArr)生成新数组,状态引用发生变化,所以能正常触发重渲染
另外AllTaskList组件中sortedTask是每次渲染都重新声明的对象,但依赖props的useEffect排序后,由于状态未正确更新,也无法同步最新的任务数据。
修复方案
遵循React状态不可变性原则,更新状态时创建新的数组和对象,避免直接修改原状态的引用:
- 在更新任务时,先复制原数组,再修改新数组内的元素
- 确保传递给
setTask的是全新的数组引用
代码修改示例
修改WeekdayTable.js中的updateAllWeekDay函数
const updateAllWeekDay = (e) => { // 创建原数组的深拷贝,避免修改原状态 const newMasterTask = masterTask.map(task => ({...task})); const targetDay = e.target.value; for(let taskPos = 0; taskPos < todaysTask.length; taskPos++){ for(let masterTaskPos = 0; masterTaskPos < newMasterTask.length; masterTaskPos++){ if(todaysTask[taskPos].taskName === newMasterTask[masterTaskPos].taskName && todaysTask[taskPos].taskDay === newMasterTask[masterTaskPos].taskDay){ newMasterTask[masterTaskPos].taskDay = targetDay; } } } props.setTask(newMasterTask); }
优化AllTaskList.js的sortedTask处理(可选,让排序逻辑更合理)
把sortedTask改成组件状态,避免每次渲染重新声明,同时依赖props.taskList触发排序:
export default function AllTaskList(props){ const [sortedTask, setSortedTask] = useState({ Monday:[],Tuesday:[],Wednsday:[],Thursday:[],Friday:[],Saturday:[],Sunday:[] }); const weekDays = ['Monday','Tuesday','Wednsday','Thursday','Friday','Saturday','Sunday']; const [blanketTaskDay, setBlanketTaskDay] = useState(false); useEffect(()=>{ const newSortedTask = { Monday:[],Tuesday:[],Wednsday:[],Thursday:[],Friday:[],Saturday:[],Sunday:[] }; if(Array.isArray(props.taskList)){ props.taskList.forEach(task => { if(newSortedTask[task.taskDay]){ newSortedTask[task.taskDay].push(task); } }); } setSortedTask(newSortedTask); },[props.taskList]) const updateAllTaskDay = (e) =>{ setBlanketTaskDay(e.target.value); } const resetAllTaskDay = () =>{ setBlanketTaskDay(false); } // 其余return部分不变 }
补充说明
修改后,更新任务时会创建新的数组引用,React检测到状态变化后会触发重渲染,任务的日期变更也会同步到UI上。同时优化sortedTask的处理后,能确保每次状态更新后都重新排序任务,保证UI展示的是最新数据。
内容的提问来源于stack exchange,提问作者boredProjects
相关产品推荐
相关产品推荐

