Redux Saga — это middleware-библиотека для Redux, которая использует генераторы JavaScript для управления side-эффектами (побочными эффектами) в приложении. Она особенно полезна для сложных асинхронных workflows, которые трудно выразить с помощью thunks.
Использует генераторы (ES6 Generators):
Эффекты (Effects):
Отмена задач:
Сложные асинхронные workflows:
Долгоживущие операции:
Требующие отмены операции:
Race conditions:
npm install redux-saga
import createSagaMiddleware from 'redux-saga';
import { takeEvery } from 'redux-saga/effects';
// Создание middleware
const sagaMiddleware = createSagaMiddleware();
// Подключение к Redux store
const store = createStore(
rootReducer,
applyMiddleware(sagaMiddleware)
);
// Запуск корневой saga
sagaMiddleware.run(rootSaga);
import { call, put, takeEvery } from 'redux-saga/effects';
function* fetchUser(action) {
try {
const user = yield call(fetch, `/api/users/${action.payload}`);
yield put({ type: 'USER_FETCH_SUCCEEDED', payload: user });
} catch (e) {
yield put({ type: 'USER_FETCH_FAILED', error: e.message });
}
}
function* watchFetchUser() {
yield takeEvery('USER_FETCH_REQUESTED', fetchUser);
}
Эффект | Описание |
---|---|
call(fn, ...args) | Вызывает функцию (асинхронную тоже) |
put(action) | Диспатчит action в store |
take(pattern) | Ждет определенный action |
fork(fn, ...args) | Неблокирующий вызов функции |
cancel(task) | Отменяет fork-задачу |
all([...effects]) | Параллельное выполнение |
import { call, put, takeLatest, all } from 'redux-saga/effects';
function* fetchUserAndPosts(userId) {
try {
// Параллельное выполнение запросов
const [user, posts] = yield all([
call(fetch, `/api/users/${userId}`),
call(fetch, `/api/users/${userId}/posts`)
]);
yield put({
type: 'FETCH_SUCCESS',
payload: { user, posts }
});
} catch (error) {
yield put({
type: 'FETCH_FAILURE',
error: error.message
});
}
}
function* watchFetchRequests() {
yield takeLatest('FETCH_DATA_REQUEST', fetchUserAndPosts);
}
Тестируемость:
Контроль потока:
Отмена операций:
Декларативный стиль:
Кривая обучения:
Избыточность:
Пример тестирования saga:
import { call } from 'redux-saga/effects';
import { expectSaga } from 'redux-saga-test-plan';
it('should fetch user', () => {
const mockUser = { id: 1, name: 'John' };
return expectSaga(fetchUser, { payload: 1 })
.provide([
[call(fetch, '/api/users/1'), mockUser]
])
.put({ type: 'USER_FETCH_SUCCEEDED', payload: mockUser })
.run();
});