Reducer更新状态未触发子组件重渲染的原因排查
为什么修改Reducer状态后没触发组件重渲染?
我写了一个处理数据添加的Reducer,包含SELECT_MODEL动作,但修改selectedModelId时没法触发组件树重渲染,搞不懂原因。
修改状态但未触发重渲染的实现
export const dataReducer = (dataState: DataState, action: dataAction): DataState => { switch (action.type) { case 'ADD_MODELS': action.models.forEach((dataModel) => { if(!dataState.models.find(x => x.model.id === dataModel.model.id)){ dataState.models.push(dataModel); } }); if(dataState.models.length === 1){ dataState.selectedModelId = dataState.models[0].model.id; } return dataState; case 'SELECT_MODEL': dataState.selectedModelId = action.id; return dataState; default: return dataState; } };
修改状态且触发重渲染的实现
export const dataReducer = (dataState: DataState, action: dataAction): DataState => { switch (action.type) { case 'ADD_MODELS': action.models.forEach((dataModel) => { if(!dataState.models.find(x => x.model.id === dataModel.model.id)){ dataState.models.push(dataModel); } }); if(dataState.models.length === 1){ dataState.selectedModelId = dataState.models[0].model.id; } return dataState; case 'SELECT_MODEL': return {...dataState, selectedModelId: action.id}; default: return dataState; } };
核心原因:状态不可变性的要求
React和Redux这类状态管理库,是通过对比状态对象的引用是否变化来判断是否需要触发组件重渲染的。
- 第一个版本里,你直接修改了原状态对象的
selectedModelId属性,然后返回的还是原来的对象引用。库会认为状态没有发生变化,自然不会触发重渲染。 - 第二个版本用对象展开运算符
{...dataState, selectedModelId: action.id}创建了一个全新的状态对象,新对象和原状态的引用完全不同。库检测到引用变化,就会通知依赖该状态的组件重新渲染。
另外提一句,你的ADD_MODELS分支也存在同样的隐患:直接用dataState.models.push()修改原数组属于可变操作,后续如果有组件依赖models数组,也可能出现不更新的问题。正确的写法应该是创建新数组,比如:
case 'ADD_MODELS': // 创建原数组的副本,避免修改原状态 const newModels = [...dataState.models]; action.models.forEach((dataModel) => { if(!newModels.find(x => x.model.id === dataModel.model.id)){ newModels.push(dataModel); } }); // 创建新的状态对象 const newState = {...dataState, models: newModels}; if(newState.models.length === 1){ newState.selectedModelId = newState.models[0].model.id; } return newState;
内容的提问来源于stack exchange,提问作者Christopher Brøndholt
相关产品推荐
相关产品推荐

