使用Immer过滤草稿数组未影响基础状态的问题排查
Immer数组过滤不生效的问题解析
问题描述
我尝试使用Immer过滤一个数组,但似乎没有生效。打开控制台可见草稿已被正确过滤,但新状态未反映这些变化。编辑后发现返回draft可解决问题,但根据Immer文档,本无需强制返回draft。请问这是怎么回事?
问题复现代码
const { produce } = immer; const baseState = [ { todo: "One", done: false, }, { todo: "Two", done: false, }, { todo: "Three", done: true, }, { todo: "Four", done: true, }, { todo: "Five", done: false, }, ]; const filterState = produce(baseState, (draft) => { draft = draft.filter((record) => { return record.done === true; }); console.log("DRAFT: ", draft); // 输出正确的过滤结果 return draft; }); console.log("FILTER STATE: ", filterState); // 未返回预期结果(注:原代码中如果去掉return draft,结果会不对)
引入的Immer版本:
<script src="https://unpkg.com/immer@6.0.3/dist/immer.umd.production.min.js"></script>
问题原因与解决方案
核心原因
Immer的工作机制是追踪对draft对象/数组的直接修改操作(比如数组的splice、push,对象属性赋值等),而不是追踪draft变量的引用替换。当你执行draft = draft.filter(...)时,只是把draft变量指向了一个新的数组,但Immer默认还是会基于最初的那个draft(未修改的原数组)来生成最终状态。只有当你手动返回这个新数组时,Immer才会优先使用你返回的值作为最终状态。
正确写法
写法一:直接修改原draft数组(符合Immer设计习惯)
通过splice删除不需要的元素,Immer能自动追踪这个修改:
const filterState = produce(baseState, (draft) => { // 反向遍历避免删除元素后索引偏移 for (let i = draft.length - 1; i >= 0; i--) { if (!draft[i].done) { draft.splice(i, 1); } } });
写法二:直接返回过滤后的数组
如果更习惯用filter,可以直接返回新数组,Immer会接受这个返回值作为最终状态:
const filterState = produce(baseState, (draft) => { return draft.filter(record => record.done === true); });
关于文档中“无需强制返回”的说明
文档中说无需返回,是针对修改draft内部内容的场景,比如修改数组元素的属性:
produce(baseState, draft => { draft[0].done = true; // 直接修改元素,无需返回 });
但当你需要完全替换整个状态集合(比如把原数组换成一个新的过滤后的数组),就需要手动返回新值,或者用修改原数组的方式来触发Immer的追踪。
内容的提问来源于stack exchange,提问作者Shadow1349
相关产品推荐
相关产品推荐

