> Можно ли делать асинхронные запросы внутри редюсера Redux и почему (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Домклик

Стек: JavaScript

> Пример ответа

Короткий ответ

Нет, делать асинхронные запросы внутри редюсера Redux нельзя. Редюсеры должны быть чистыми функциями: они принимают предыдущее состояние и action, синхронно возвращают новое состояние без побочных эффектов. Асинхронные операции (fetch, setTimeout) нарушают этот принцип, делая редюсер непредсказуемым и затрудняя тестирование, отладку и воспроизведение состояний.

Подробное объяснение

Redux построен на концепции чистых функций для редюсеров. Чистая функция:

  • Всегда возвращает одинаковый результат для одинаковых аргументов
  • Не вызывает побочных эффектов (сетевые запросы, мутации внешних данных, таймеры)
  • Не зависит от внешнего состояния (глобальные переменные, время, случайность)

Асинхронные запросы - это побочные эффекты по определению. Они:

  • Непредсказуемы по времени выполнения
  • Могут завершиться ошибкой
  • Зависят от сети, сервера, внешних данных
  • Нарушают детерминированность: вызов редюсера с одинаковыми state и action может привести к разным результатам

Redux требует, чтобы редюсеры были синхронными и предсказуемыми. Это обеспечивает:

  • Возможность воспроизведения состояний (time-travel debugging)
  • Простоту тестирования
  • Предсказуемость потока данных
  • Возможность сериализации состояния

На практике

Асинхронные операции обрабатываются через middleware - слой между dispatch и редюсером. Самые популярные решения:

  • Redux Thunk - позволяет диспатчить функции вместо объектов action; внутри функции можно выполнять асинхронные запросы и диспатчить синхронные actions
  • Redux Saga - использует генераторы для управления побочными эффектами
  • Redux Observable - основан на RxJS
  • RTK Query - встроенное решение в Redux Toolkit для работы с API

Типичный паттерн: диспатчится action начала запроса (например, FETCH_USER_REQUEST), редюсер устанавливает loading: true, затем после ответа сервера диспатчится action успеха (FETCH_USER_SUCCESS) или ошибки (FETCH_USER_FAILURE), редюсер обновляет данные или ошибку.

Пример кода

JAVASCRIPT
// ❌ Неправильно: асинхронный запрос в редюсере
function userReducer(state, action) {
switch (action.type) {
case 'FETCH_USER':
fetch('/api/user') // побочный эффект!
.then(res => res.json())
.then(data => {
// нельзя мутировать state вне return
state.user = data; // нарушение
});
return state;
default:
return state;
}
}
// ✅ Правильно: через middleware (Redux Thunk)
const fetchUser = (userId) => async (dispatch) => {
dispatch({ type: 'FETCH_USER_REQUEST' });
try {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
dispatch({ type: 'FETCH_USER_SUCCESS', payload: data });
} catch (error) {
dispatch({ type: 'FETCH_USER_FAILURE', error: error.message });
}
};
// Редюсер остаётся чистым
function userReducer(state = initialState, action) {
switch (action.type) {
case 'FETCH_USER_REQUEST':
return { ...state, loading: true, error: null };
case 'FETCH_USER_SUCCESS':
return { ...state, loading: false, user: action.payload };
case 'FETCH_USER_FAILURE':
return { ...state, loading: false, error: action.error };
default:
return state;
}
}

Как отвечать на собеседовании

Начни с чёткого "нет" и объясни причину: чистота редюсеров - фундаментальный принцип Redux. Затем опиши, как правильно обрабатывать асинхронность (через middleware). Упомяни конкретные инструменты (Thunk, Saga, RTK Query) и объясни, почему это важно для предсказуемости, тестирования и отладки. Если собеседник углубляется, можешь обсудить, что в некоторых случаях (например, в Redux Toolkit с createAsyncThunk) middleware берёт на себя управление lifecycle actions.

Что проверяет интервьюер

  • Понимание фундаментальных принципов Redux (чистые функции, детерминизм)
  • Знание архитектуры Redux (dispatch → middleware → reducer)
  • Умение различать синхронные и асинхронные операции в контексте state management
  • Практический опыт работы с middleware (Thunk, Saga, RTK Query)
  • Понимание trade-off: почему нельзя "просто добавить async" в редюсер

Типичные ошибки

  • Утверждение, что "можно, если использовать async/await" - это нарушает принцип чистоты
  • Предложение использовать редюсер для кеширования запросов - кеширование должно быть в middleware или selectors
  • Путаница между редюсером и action creator - асинхронность возможна в action creator (через middleware), но не в редюсере
  • Игнорирование проблемы воспроизводимости состояний - главная причина запрета
  • Предложение обрабатывать ошибки запроса внутри редюсера - ошибки должны обрабатываться в middleware и диспатчиться как отдельные actions

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью