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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 22:02:43