Redux状态更新后React组件未重新渲染问题求助
无法检测Redux状态变化导致React不渲染重排后的任务列表
我花了一整天尝试解决这个问题,始终找不到方案。尝试对任务列表重排并渲染变化,但无论操作多少次,React都检测不到状态变化。Redux日志显示状态已经更新,但useEffect里监听tasks的逻辑没触发,组件也不重新渲染。
React组件相关代码
const tasks = useSelector((state)=>{ return state.taskReducer.tasks; }); React.useEffect(()=>{ //....some code },[tasks]) -------------- <div className='App' style={{ border: '1px solid', marginTop: '5%', minHeight: '20em', borderBottom: '16px solid', borderRight: '12px solid'}}> {(tasks === undefined || tasks.length === 0) ? <></> : tasks.map((task, index)=> <Taskbar desc={task} key={index} index={index}/> ) } </div>
Redux Store配置
import logger from 'redux-logger'; import { configureStore } from '@reduxjs/toolkit'; import taskReducer from '../reducer/reducer'; import userReducer from '../reducer/userReducer'; export default configureStore({ reducer: {taskReducer, userReducer}, middleware: (getDefaultMiddleware) => getDefaultMiddleware({serializableCheck: false}).concat(logger), devTools: process.env.NODE_ENV !== 'production', });
Task Reducer代码(重点为arrange分支)
const taskReducer = createReducer(initialState, async (builder) => { builder .addCase(getAll, (state, action)=>{ const tasks = [...action.payload]; tasks.forEach((ele, index)=>{ ele.index = index }); return {...state, tasks: [...tasks]} }) .addCase(increment, (state, action) => { action.playoad.index = state.tasks.length const tasks = [...state.tasks, action.playoad] return {...state, tasks: [...tasks]} }) .addCase(decrement, (state, action) => { try{ const data = state.tasks.filter(item=>{ return item._id !== action.payload._id }) return {...state, tasks: data} } catch(err){ console.log(err) } }) .addCase(editing, (state, action)=>{ const data = action.payload; let tasks = state.tasks; for(let i=0; i < tasks.length; i ++){ if(tasks[i]._id === data._id){ tasks[i].text = data.text; break; } } return {...state, tasks: [...tasks]} }) // 负责修改任务顺序的分支 .addCase(arrange, (state, action)=>{ const tasks = [...state.tasks]; const currIndex = action.payload.currIndex; const dropIndex = action.payload.dropIndex; const curr = {...tasks[currIndex], index: dropIndex, text: tasks[currIndex].text} const next = {...tasks[dropIndex], index: currIndex}; tasks.splice(dropIndex, 1, curr); tasks.splice(currIndex, 1, next); tasks.sort((a, b)=>a.index - b.index); return {...state, tasks: [...tasks]} }) .addDefaultCase((state, action) => {return {...state}}) })
问题根源与修复方案
问题1:key使用index导致渲染不更新
React通过key识别元素身份,用数组index作为key时,元素顺序变化后,index和对应元素的映射关系不会改变,React会认为元素没有变化,因此不触发重渲染。
修复:将Taskbar的key替换为任务的唯一标识(比如task._id):
tasks.map((task)=> <Taskbar desc={task} key={task._id} index={task.index}/> )
问题2:reducer的async修饰符是错误用法
createReducer的builder回调不能是异步函数,这会导致reducer逻辑执行异常,破坏Redux的同步状态更新规则。
修复:移除async修饰符:
const taskReducer = createReducer(initialState, (builder) => { // ... 原有逻辑 })
问题3:arrange逻辑可优化以确保引用更新
当前的splice+sort逻辑存在潜在的引用问题,可简化逻辑确保数组和元素引用都正确更新:
.addCase(arrange, (state, action)=>{ const { currIndex, dropIndex } = action.payload; // 拷贝原数组 const newTasks = [...state.tasks]; // 取出要移动的元素并拷贝,确保引用更新 const movedTask = {...newTasks[currIndex]}; // 移除原位置元素 newTasks.splice(currIndex, 1); // 插入到目标位置 newTasks.splice(dropIndex, 0, movedTask); // 重新更新所有元素的index newTasks.forEach((task, idx) => { task.index = idx; }); return {...state, tasks: newTasks}; })
问题4:可选优化useSelector的比较逻辑
默认情况下useSelector使用严格相等比较,若仍存在检测不到变化的情况,可使用shallowEqual做浅比较:
import { useSelector, shallowEqual } from 'react-redux'; const tasks = useSelector((state)=> state.taskReducer.tasks, shallowEqual);
内容的提问来源于stack exchange,提问作者Chase
相关产品推荐
相关产品推荐

