Как заставить пакетную обработку работать с асинхронными вызовами?

Начиная с React 18, обновления состояния автоматически группируются . При использовании Redux также используются батчи. Однако, похоже, это не работает при использовании асинхронных вызовов. Например, для thunk:

const incrementAsync = createAsyncThunk("incrementAsync", (_, { dispatch }) => {
  setTimeout(() => {
    dispatch(increment());
  }, 1000);
});

Следующее заставит компонент перерендериться три раза (но визуально счетчик изменится только один раз):

const increment = () => {
  dispatch(incrementAsync());
  dispatch(incrementAsync());
  dispatch(incrementAsync());
};

Демонстрация CodeSandbox

Я не знаю, является ли это ожидаемым поведением, но как мне сделать так, чтобы эти обновления были пакетными? Я хотел бы, чтобы мой компонент перерисовывался только один раз, независимо от количества обновлений.


Обратите внимание:
Я сделал эти фрагменты только для вопроса, мой код не имеет ничего общего с простыми счетчиками, и мне нужно, чтобы вызовы были асинхронными.

🤔 А знаете ли вы, что...
JavaScript был первоначально создан для улучшения интерактивности веб-страниц.


52
1

Ответ:

Решено

@reduxjs/инструментарий: ^ 1.9.5

Вы можете использовать autoBatchEnhancer

Улучшитель хранилища Redux, который ищет одно или несколько отправленных действий с «низким приоритетом» подряд и ставит в очередь обратный вызов для запуска уведомлений подписчика с задержкой. Затем он уведомляет подписчиков либо о выполнении обратного вызова в очереди, либо об отправке следующего действия с «нормальным приоритетом», в зависимости от того, что произойдет раньше.

counterSlice.js:

import { createSlice, createAsyncThunk, prepareAutoBatched, SHOULD_AUTOBATCH } from '@reduxjs/toolkit';

export const incrementAsync = createAsyncThunk('incrementAsync', (_, { dispatch }) => {
    setTimeout(() => {
        dispatch(increment());
    }, 1000);
});

export const counterSlice = createSlice({
    name: 'counter',
    initialState: {
        value: 0,
    },
    reducers: {
        increment: {
            reducer: (state, action) => {
                console.info('action: ', action);
                state.value += 1;
            },
            prepare: prepareAutoBatched(),
        },
    }
});

export const { increment } = counterSlice.actions;

export const selectCount = (state) => state.counter.value;

export default counterSlice.reducer;

store.js:

import { configureStore, autoBatchEnhancer } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';

export default configureStore({
    reducer: {
        counter: counterReducer,
    },
    enhancers: (existingEnhancers) => {
        // Add the autobatch enhancer to the store setup
        return existingEnhancers.concat(autoBatchEnhancer());
    },
});

коды и ящик