Можно ли делать асинхронные запросы внутри редюсера Redux и почему
Что проверяют
Понимаете ли вы, почему редюсер должен быть чистой синхронной функцией и что сломается, если это нарушить. Следом спрашивают, где тогда живут запросы в Redux Toolkit и чем createAsyncThunk отличается от RTK Query.
Как отвечать
Нельзя. Редюсер это функция (state, action) => newState, которая обязана синхронно вернуть новое состояние без побочных эффектов. Почему:
- механика:
dispatchсинхронен, store забирает результат редюсера сразу. Асинхронная функция вернётPromise, он и станет состоянием, а ответ сервера некуда будет положить; - предсказуемость: одно и то же действие на одном и том же состоянии должно давать одинаковый результат. На этом держатся тесты редюсеров и Redux DevTools, которые переигрывают историю действий. С запросом внутри каждый повтор снова ходил бы в сеть;
- прямой запрет: вызов
dispatchизнутри редюсера Redux прерывает ошибкой «Reducers may not dispatch actions».
Где делать запросы:
createAsyncThunk: генерирует действияpending,fulfilled,rejected, которые обрабатываются вextraReducersчерезbuilder.addCase. Мидлвар thunk уже подключён вconfigureStoreпо умолчанию;- RTK Query:
createApiс эндпоинтами, кэшем, дедупликацией запросов и инвалидацией по тегам. Для загрузки данных с сервера это рекомендуемый способ; - listener middleware:
createListenerMiddlewareдля реакций на действия, например отмены предыдущего запроса; - redux-saga: для сложных сценариев, в новых проектах её берут всё реже.
Упомяните Immer в createSlice: запись state.items.push(x) чистоту не нарушает, Immer строит новое состояние из черновика.
Где спотыкаются
Запускают fetch из редюсера без await и считают, что раз состояние вернулось синхронно, всё хорошо. Запрос улетает при каждом повторе действия в DevTools, а результат теряется.
Генерируют в редюсере Date.now(), Math.random() или nanoid(). Это та же нечистота. Такие значения готовят в prepare-колбэке createSlice или в месте диспатча.
Кладут в стор Error или Promise. RTK в режиме разработки предупреждает о несериализуемых значениях, а createAsyncThunk сам превращает ошибку в простой объект.