使用RTK createAsyncThunk结合Promise.all时如何批量更新Redux Store以避免频繁重渲染
看起来你遇到的核心问题是:每个createAsyncThunk的fulfilled事件触发后,middleware里的多个独立dispatch会依次触发store更新,哪怕用Promise.all并行执行两个thunk,它们的完成回调还是各自独立的,自然会导致组件频繁重渲染。Redux Toolkit的batch函数确实能解决这个问题,但关键是要在正确的时机使用它,我给你几个针对性的解决方案:
方案一:在Middleware中用batch包裹同一批dispatch操作
这是最贴合你现有代码结构的方案。你需要把每个fulfilled分支里的多个store.dispatch调用用batch函数包裹起来,这样同一批的dispatch会被合并成一次store更新,组件只会重渲染一次。
首先确保你从Redux Toolkit导入batch:
import { batch } from '@reduxjs/toolkit';
然后修改你的两个middleware:
修改tokenMiddleware
export const tokenMiddleware = (store: any) => (next: any) => (action: any) => { const {type, payload, meta} = action; switch (type) { case 'getToken/fulfilled': // 用batch包裹所有需要同步更新的dispatch batch(() => { store.dispatch({ type: 'anothetStore/update', payload: payload.data1 }); store.dispatch({ type: 'anothetStore2/update', payload: payload.data2 }); // 其他需要同步更新的dispatch也放在这里面 }); break; // 其他case... } return next(action); };
修改permissionMiddleware
export const permissionMiddleware = (store: any) => (next: any) => (action: any) => { const {type, payload, meta} = action; switch (type) { case 'getPermissions/fulfilled': batch(() => { store.dispatch({ type: 'anothetStore/update', payload: payload.data1 }); store.dispatch({ type: 'anothetStore2/update', payload: payload.data2 }); }); break; // 其他case... } return next(action); };
这样修改后,每个fulfilled事件触发的多个状态更新会被合并成一次store变更,组件只会重渲染一次;而Promise.all并行执行的两个thunk,各自完成时的更新还是独立的,但至少每个thunk对应的多次更新被合并了。如果想要两个thunk完成后再一次性更新所有状态,那可以看方案二。
方案二:合并异步逻辑到单个createAsyncThunk中
如果你的业务允许,可以把两个异步请求合并到同一个thunk里,这样就能在同一个地方用batch处理所有状态更新,彻底避免两次独立的更新触发:
import { createAsyncThunk, batch } from '@reduxjs/toolkit'; // 合并token和权限请求的thunk export const fetchAuthData = createAsyncThunk('auth/fetchData', async (_, api) => { try { // 并行发起两个请求 const [tokenResult, permissionResult] = await Promise.all([ axios.get('/token'), axios.get('/permissions') ]); // 在同一个batch里完成所有状态更新 batch(() => { api.dispatch({ type: 'anothetStore/update', payload: tokenResult.data.data1 }); api.dispatch({ type: 'anothetStore2/update', payload: tokenResult.data.data2 }); api.dispatch({ type: 'anothetStore/update', payload: permissionResult.data.data1 }); api.dispatch({ type: 'anothetStore2/update', payload: permissionResult.data.data2 }); // 其他状态更新 }); return { token: tokenResult.data, permissions: permissionResult.data }; } catch (err) { // 错误处理 throw err; } });
这种方式的好处是两个请求完成后才会触发一次批量更新,组件只会重渲染一次,同时也简化了middleware的逻辑(甚至可以不用middleware,直接在thunk里处理状态更新)。不过要注意你提到的“必须按顺序更新store”——这里的dispatch顺序还是严格按照你写的顺序执行的,state会按顺序更新,只是合并了更新通知,不影响业务逻辑的顺序要求。
为什么之前的batch没生效?
你之前尝试用batch但没效果,大概率是因为没有把同一事件循环内的多个dispatch包裹起来。比如如果你只是在Promise.all外面包batch,那两个thunk的fulfilled事件是在不同的异步回调里触发的,batch无法跨异步事件合并更新。只有把同一个异步回调里的多个dispatch用batch包裹,才能起到合并更新的作用。
备注:内容来源于stack exchange,提问作者raccoon_ko




