You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:21:11