Immer produce中字符串属性状态变更未被保留问题排查
问题:非目标产品的isVendorSelectionDone状态被意外修改
我把状态calculationResultsSw传入下方的addUserInputUsContentSw函数,初始状态里有两个agreement:
- 第一个产品:
isVendorSelectionDone为"1",包含1个agreementRow - 第二个产品:
isVendorSelectionDone为"1",没有agreementRow
我通过POST请求获取目标产品的更新后agreement,用Immer的produce方法更新状态:先移除原目标产品的agreement,再插入新获取的agreement并排序。按预期非目标产品的isVendorSelectionDone应该还是"1",但执行setCalculationResultsSw之后,非目标产品的这个字段变成了"0",只有agreementRow保持正常。
函数代码
const addUserInputUsContentSw = ( calculationResultsSw: any, id: string, productId: string, productNo: string, addUscAgreements: any, onSuccess: () => void, successNotification?: INotification, failureNotification?: INotification) => { console.log("Inside Add User Input Us Content: CalculationResultsSw"); console.log(calculationResultsSw); var calculatedAgreements = []; calculatedAgreements = addUscAgreements.filter((a:any) => a.boQItem.id == productId); post<ISwCalculationResult>( `exportRequest/${id}/adduserconinputsw`, { productId, productNo, calculatedAgreements, isCalculated: "false" }, (data: ISwCalculationResult) => { setCalculationResultsSw(produce((draftState: any) =>{ var indices: any = []; draftState.sw.agreements.forEach(function(value:any,pos: number){ if(value.boQItem.id == productId) { indices.push(pos); } }); for(var index = indices.length-1;index>=0;index--) { draftState.sw.agreements.splice(indices[index],1); } draftState.sw.agreements.push(...data.sw.agreements); draftState.sw.agreements.sort(comparesrlorder); draftState.sw.agreements.sort(compare); },calculationResultsSw)); }, onSuccess && onSuccess(), successNotification, failureNotification ); };
排查方向及解决办法
1. 检查后端返回数据
先确认POST请求返回的data.sw.agreements是不是只包含目标产品的agreement。如果后端错误返回了所有产品的数据,且非目标产品的isVendorSelectionDone被设为"0",插入后就会覆盖原有状态。
- 解决:要求后端接口仅返回目标产品的更新后agreement,不要返回全量数据。
2. 避免引用共享导致的意外修改
Immer的草稿是基于原状态创建的,但如果返回的data.sw.agreements里的对象和原状态有引用关联,可能会污染非目标产品的属性。
- 解决:插入前对返回的新agreement做深拷贝:
// 替换原push语句 const copiedAgreements = JSON.parse(JSON.stringify(data.sw.agreements)); draftState.sw.agreements.push(...copiedAgreements);
3. 检查排序函数的副作用
看看comparesrlorder和compare这两个排序函数,有没有在排序过程中修改了agreement的isVendorSelectionDone属性。排序函数必须是纯函数,只能返回比较结果,不能修改原对象。
- 解决:确保排序函数只做比较,比如:
const compare = (a: any, b: any) => { // 仅返回比较数值,不修改a或b的属性 return a.srlOrder - b.srlOrder; };
4. 调整状态更新后的回调时机
当前代码里onSuccess是在状态更新前执行的,虽然不会直接修改状态,但可能导致组件提前渲染旧数据。可以把onSuccess移到状态更新完成后执行:
- 解决:如果是React的
setState,可以用回调函数:setCalculationResultsSw( produce((draftState: any) => { // 原有更新逻辑 }, calculationResultsSw), () => { onSuccess && onSuccess(); } );
内容的提问来源于stack exchange,提问作者Raida Adn
相关产品推荐
相关产品推荐

