Redux下Immer循环引用报错及多选变更处理问题
Redux架构下解决Immer循环引用报错实现多选题选项变更
报错原因
抛出 Error: [Immer] Immer forbids circular references 的直接原因是代码里人为制造了循环引用:
const data = newFormValues.find((x) => x.id === id); data.label = target.value; // 问题代码:data本身就是newFormValues数组中的元素,又被赋值给同数组项的动态属性,形成对象自引用 newFormValues[index][id] = data;
这段代码给选项对象添加了一个值为自身的属性,形成了循环结构,Immer在做不可变数据转换时检测到循环结构就会直接抛错。
除此之外现有代码还存在两个不符合状态更新规范的问题:
- 仅对
multiOptions数组做了浅拷贝,之后直接修改数组内对象的label属性,属于直接修改原状态引用的突变操作 - 本地状态
multiOptions和Redux中存储的题目多选选项数据重复维护,很容易出现状态不一致问题
修复方案
修正handleMultiChange逻辑
删除制造循环引用的冗余代码,全程遵循不可变数据更新规则:
const handleMultiChange = (event, id, index, questionId) => { const targetValue = event.target.value; // 用map做不可变更新,返回全新的选项数组,不修改原对象 const newFormValues = multiOptions.map((option, idx) => { if (idx === index) { return { ...option, label: targetValue } } return option; }); setMultiOptions(newFormValues); // 此时传入produce的newFormValues是无循环引用的纯数据,可被Immer正常处理 const next = produce(survey.questions, (draft) => { const question = draft.find((question) => question.qid === questionId); if (question) { question.multiChoiceOptions = newFormValues; } }); dispatch({ type: "SET_QUESTIONS", value: next }); };
可选优化项
- 如果项目使用Redux Toolkit,框架已内置Immer能力,不需要在组件层手动调用
produce,可以直接把更新逻辑收敛到reducer中,组件层只需要派发对应action传入修改参数即可 - 建议移除本地
multiOptions状态,多选选项直接从Redux的store中取数渲染,所有修改操作直接通过action更新Redux,避免两份同源数据不同步 - 不要给业务对象添加无实际用途的动态属性,本次报错的根源就是无业务意义的冗余赋值代码。
修复后传入Immer的状态是纯扁平结构,不存在循环引用,多选选项的输入修改、状态同步都可以正常运行。
内容的提问来源于stack exchange,提问作者vini
相关产品推荐
相关产品推荐

