You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用RTK createAsyncThunk结合Promise.all时如何批量更新Redux Store以避免频繁重渲染

RTK createAsyncThunk结合Promise.all时如何批量更新Redux Store以避免频繁重渲染

看起来你遇到的核心问题是:每个createAsyncThunkfulfilled事件触发后,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

火山引擎 最新活动