能否在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
相关产品推荐
相关产品推荐

