Redux-Saga: зачем нужен, если уже есть Redux
Пошаговое руководство по использованию Redux-Saga вместо redux-thunk: когда нужен, как добавить в проект и какие преимущества дает управление асинхронными side-effects.
Если ваш store стал “зависать” при загрузке данных, а простые redux-thunk
-экшены уже не справляются, имеет смысл рассмотреть более надежный способ управления side effects. Я расскажу, как Redux-Saga
решает типичные проблемы и в каких сценариях его стоит использовать вместо привычного Redux.
Почему обычный Redux не покрывает всю асинхронку
Redux чистый контейнер для состояния. Он не знает, откуда приходят данные, и не умеет “потягиваться” к серверу. Поэтому в проектах добавляют middleware (thunk, saga, observable) именно для этой “дырки”. redux-thunk
позволяет писать функции-диспатчеры, но при росте количества запросов, цепочек и отмен читаемость резко падает. Сложные сценарии параллельные запросы, автоматические ретраи, отмена при unmount требуют более выразительного инструмента.
Что такое Redux-Saga?
Redux-Saga
middleware, построенное на генераторах ES6. Саги это “прослушиватели”, реагирующие на определенные действия и выполняющие любые side effects: запросы, таймеры, навигацию. Главное преимущество полный контроль над потоком. Можно писать последовательные, параллельные и отменяемые операции, не жертвуя читаемостью.
Пример простой саги
import { call, put, takeEvery } from "redux-saga/effects";
import api from "../api";
function* fetchUser(action) {
try {
const data = yield call(api.getUser, action.payload.id);
yield put({ type: "USER_FETCH_SUCCESS", payload: data });
} catch (e) {
yield put({ type: "USER_FETCH_FAILURE", error: e.message });
}
}
export function* watchUserRequests() {
yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
Сага выглядит как обычная функция, но yield
явно указывает, где происходит запрос и где обрабатывается результат. Это упрощает тестирование: каждый yield
легко мокать.
Сравнение с Redux-Thunk
Фича | Redux-Thunk | Redux-Saga |
---|---|---|
Параллельные запросы | ❌ (ручная гонка) | ✅ (effect all ) |
Отмена запросов при unmount | ❌ (нужен abort) | ✅ (effect cancel ) |
Retry / backoff | ❌ (самописный) | ✅ (effect delay ) |
Тестируемость | ✅ (мок функций) | ✅ (мок генераторов) |
Сложные цепочки действий | ❌ (глубокие вложения) | ✅ (yield ) |
Из таблицы видно, что Redux-Saga
не заменяет Redux, а дополняет его там, где обычных thunk-ов уже не хватает.
Как внедрить Saga в существующий проект
- Установите зависимость:
npm i redux-saga
. - Создайте файл
sagas/index.js
и соберите все саги:
import { all } from "redux-saga/effects";
import { watchUserRequests } from "./userSaga";
import { watchPosts } from "./postsSaga";
export default function* rootSaga() {
yield all([watchUserRequests(), watchPosts()]);
}
- Подключите middleware при создании стора:
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "./reducers";
import rootSaga from "./sagas";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
После этого любые экшены, объявленные в саге, будут обрабатываться автоматически. Если вы знакомы с мемоизацией компонентов, то yield
напоминает useMemo
: результат сохраняется, пока зависимости не изменятся.
Когда стоит отказываться от Saga?
- Проект небольшого масштаба (запросов < 5, простая бизнес-логика).
redux-thunk
будет легче поддерживать. - Команда не знакома с генераторами и не готова вкладываться в обучение сложность
Saga
может стать барьером. - Требуется максимальная минималистичность bundle-size: saga добавляет ~10 KB gzipped.
Если же вы уже сталкивались с проблемами оптимизации рендера и ищете способ избавиться от “лесенки” коллбеков, Redux-Saga
станет гибким слоем, который держит бизнес-логику в одном месте, а UI-компоненты чистыми.
FAQ / чеклист
- takeLatest автоматически отменит предыдущий запрос, если пришел новый. Это избавляет от гонок.
- Тестировать генераторы:
redux-saga-test-plan
позволяет проверять каждыйyield
без реальных запросов. - Не делайте саги слишком “толстыми” вынесите чистую бизнес-логику в отдельные функции, а саги оставьте только для оркестрации.
- Следите за подписками:
takeEvery
без ограничения может привести к утечкам, если количество действий резко возрастет. - Не смешивайте redux-thunk и redux-saga в одном модуле выбирайте один подход к side effects, иначе появится путаница.
Если ваш проект уже использует Redux, а вы сталкиваетесь с ростом количества асинхронных цепочек, Redux-Saga
предоставляет инструмент, который упрощает управление, делает код предсказуемым и легко тестируемым. В местах, где обычный Redux “залипает”, saga спасет ваш рабочий процесс.