Redux-Saga - Русский перевод
Оригинал (На английском языке): https://redux-saga.js.org/
Перевод для этой страницы взят отсюда: https://github.com/redux-saga/redux-saga/blob/master/README_ru.md
redux-saga
- это библиотека, которая призвана упростить и улучшить выполнение сайд-эффектов (т.е. таких действий, как асинхронные операции, типа загрузки данных и "грязных" действий, типа доступа к браузерному кэшу) в React/Redux приложениях.
Можно представить это так, что сага - это как отдельный поток в вашем приложении, который отвечает за сайд-эффекты. redux-saga
- это redux мидлвар, что означает, что этот поток может запускаться, останавливаться и отменяться из основного приложения с помощью обычных redux экшенов, оно имеет доступ к полному состоянию redux приложения и также может диспатчить redux экшены.
Библиотека использует концепцию ES6, под названием генераторы, для того, чтобы сделать эти асинхронные потоки легкими для чтения, написания и тестирования. (если вы не знакомы с этим, здесь есть некоторые ссылки для ознакомления) Тем самым, эти асинхронные потоки выглядят, как ваш стандартный синхронный JavaScript код. (наподобие async
/await
, но генераторы имеют несколько отличных возможностей, необходимых нам)
Возможно, вы уже использовали redux-thunk
, перед тем как обрабатывать ваши выборки данных. В отличие от redux thunk, вы не оказываетесь в callback аду, вы можете легко тестировать ваши асинхронные потоки и ваши экшены остаются чистыми.
Приступая к работе
$ npm install --save redux-saga
или
$ yarn add redux-saga
Альтернативно, вы можете использовать предоставленные UMD сборки напрямую в <script>
на HTML странице.
Пример использования
Предположим, что у нас есть интерфейс для извлечения некоторых пользовательских данных с удаленного сервера при нажатии кнопки. (Для краткости, мы просто покажем код запуска экшена.)
class UserComponent extends React.Component {
...
onSomeButtonClicked() {
const { userId, dispatch } = this.props
dispatch({type: 'USER_FETCH_REQUESTED', payload: {userId}})
}
...
}
Компонент диспатчит action в виде простого объекта в Store. Мы создадим сагу, которая слушает все USER_FETCH_REQUESTED
экшены и триггерит вызовы API для извлечения пользовательских данных.
sagas.js
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import Api from '...'
// worker Saga: будет запускаться на экшены типа `USER_FETCH_REQUESTED`
function* fetchUser(action) {
try {
const user = yield call(Api.fetchUser, action.payload.userId);
yield put({type: "USER_FETCH_SUCCEEDED", user: user});
} catch (e) {
yield put({type: "USER_FETCH_FAILED", message: e.message});
}
}
/*
Запускаем `fetchUser` на каждый задиспатченый экшен `USER_FETCH_REQUESTED`.
Позволяет одновременно получать данные пользователей.
*/
function* mySaga() {
yield takeEvery("USER_FETCH_REQUESTED", fetchUser);
}
/*
В качестве альтернативы вы можете использовать `takeLatest`.
Не допускает одновременное получение данных пользователей. Если `USER_FETCH_REQUESTED`
диспатчится в то время когда предыдущий запрос все еще находится в ожидании ответа,
то этот ожидающий ответа запрос отменяется и срабатывает только последний.
*/
function* mySaga() {
yield takeLatest("USER_FETCH_REQUESTED", fetchUser);
}
export default mySaga;
Для запуска нашей саги, мы подключим ее к Redux Store, используя redux-saga
мидлвар.
main.js
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import reducer from './reducers'
import mySaga from './sagas'
// создаем saga мидлвар
const sagaMiddleware = createSagaMiddleware()
// монтируем его в Store
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
// затем запускаем сагу
sagaMiddleware.run(mySaga)
// рендерим приложение