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

能否在Immer的produce()执行中途更新React状态?求可行方案

问题解答:在Immer produce异步回调中中途更新状态是否可行?

首先直接给结论:你期望的那种在同一个async produce回调里中途触发状态更新的写法是不可行的。

为什么不行?

Immer的produce函数如果传入的回调是异步函数,它会等待整个异步函数执行完毕后,才会基于draft的最终修改生成新的状态对象。也就是说,你在回调里先设置draft.item.is_loading = true,然后await异步操作,最后修改name和is_loading的所有步骤,会被合并成一个单一的状态快照,只会触发一次setState。这样is_loading = true的状态永远不会被渲染出来,因为它直接被后续的修改覆盖了。

可行的优雅方案

虽然不能在同一个produce回调里完成中途更新,但我们可以把逻辑封装成连贯的异步块,既保持代码可读性,又能复用变量。这里有两种推荐的写法:

方案1:用立即执行异步函数包裹逻辑

把所有代码放在一个IIFE里,看起来是一个整体,同时完成两次状态更新:

(async () => {
  // 第一步:立刻更新loading状态,触发重渲染显示加载中
  this.setState(prevState => produce(prevState, draft => {
    draft.item.is_loading = true;
  }));

  // 执行异步操作,这里可以复用变量
  const name = await get_name();

  // 第二步:异步完成后更新结果并关闭loading
  this.setState(prevState => produce(prevState, draft => {
    draft.item.name = name;
    draft.item.is_loading = false;
  }));
})();

方案2:抽离异步逻辑并处理错误(更严谨)

如果需要处理异步操作的错误,这种写法更清晰,同时也能保持代码的连贯性:

const fetchAndUpdateName = async () => {
  // 先开启loading
  this.setState(prevState => produce(prevState, draft => {
    draft.item.is_loading = true;
  }));

  try {
    const name = await get_name();
    // 更新结果
    this.setState(prevState => produce(prevState, draft => {
      draft.item.name = name;
      draft.item.is_loading = false;
    }));
  } catch (error) {
    // 错误处理:关闭loading,可额外添加错误提示
    this.setState(prevState => produce(prevState, draft => {
      draft.item.is_loading = false;
    }));
    console.error('获取名称失败:', error);
  }
};

// 调用函数
fetchAndUpdateName();

重要提醒

在类组件中使用setState时,尽量用函数式更新(即传入一个接收prevState的函数),而不是直接依赖this.state。因为this.state可能不是最新的状态(setState是异步的),函数式更新可以确保我们基于最新的状态进行修改,避免出现状态不一致的问题。

内容的提问来源于stack exchange,提问作者left click

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:28:35