Redux Saga: шаблоны, которые мы используем

В этом посте мы углубимся в Redux Saga — фреймворк JavaScript, используемый для обработки побочных эффектов в нашем веб-приложении. В предыдущем посте я познакомил нас с основными концепциями Redux Saga . Если вы только начинаете работать с Redux Saga, я рекомендую начать с него.
Конкретные темы, которые мы обсудим:
- Один раз запустить сагу с помощью
take
, вместоtakeLatest
илиtakeEvery
- Ожидание первого завершенного действия с
race
- Совместное использование сервисов в вашем приложении с помощью
getContext
Один раз запустить сагу с помощью take
, вместо takeLatest
илиtakeEvery
Внутри наших саг о слайсах мы обычно используем takeLatest
или takeEvery
, о чем я рассказывал в своем предыдущем посте . В большинстве случаев использования мы хотим учитывать каждое обнаруженное действие.
Но что, если нас интересует только первое появление действия, например первое взаимодействие пользователя с формой? Как бы выглядела эта реализация в нашей саге о слайсах? API Redux Saga не предлагает специального метода для этого, но, как мы сейчас увидим, есть простой способ сделать это.
Во-первых, некоторый шаблонный код:
function* changePasswordSubmittedHandler() {
// ...
}
function* changePasswordSaga() {
yield all([
takeLatest(CHANGE_PASSWORD_SUBMITTED, changePasswordSubmittedHandler)
])
}
Теперь давайте добавим обработчик, который мы хотим запускать только один раз.
function* changePasswordSubmittedHandler() {
// ...
}
function* changePasswordFormFocusedHandler() {
yield take(CHANGE_PASSWORD_FORM_FOCUSED)
// ...
}
function* changePasswordSaga() {
yield all([
takeLatest(CHANGE_PASSWORD_SUBMITTED, changePasswordSubmittedHandler),
changePasswordFormFocusedHandler()
])
}
Что тут происходит? Запуск changePasswordFormFocusedHandler
сам по себе, а не с takeLatest
или takeEvery
позволяет нам запустить обработчик один и только один раз. Оператор take
в начале заставляет функцию приостанавливаться . Только после того, как наше приложение обнаружит, CHANGE_PASSWORD_FORM_FOCUSED
оно прекратит паузу и запустит остальную часть функции.
Альтернативным подходом было бы включение флага в глобальное состояние вашего приложения, которое вы можете перевернуть, как только произойдет первое вхождение. Но если вы хотите избежать добавления свойств в состояние, этот подход прекрасно работает.
Ожидание первого завершенного действия сrace
Иногда мы хотим прослушивать несколько действий и предпринимать определенные шаги в зависимости от того, какое из этих действий завершилось первым. Например, когда пользователь отправляет платеж за заказ, транзакция может завершиться успешно или завершиться неудачей по ряду причин. Для этого сценария мы обратимся к race
комбинатору эффектов. Redux Saga ссылается на race
комбинаторы all
эффектов, потому что они оба принимают 1 или более эффектов и обрабатывают их одновременно.
Давайте посмотрим, как race
работает:
function* paymentSubmittedHandler() {
const { failed, finished, cancelled } = yield race({
failed: take(PAYMENT_SUBMISSION_FAILED),
finished: take(PAYMENT_SUBMISSION_FINISHED),
cancelled: take(PAYMENT_SUBMISSION_CANCELLED)
})
if (finished) {
// do something
}
if (cancelled || failed) {
// do something
}
}
Это полезный подход к обработке асинхронных запросов. Мы используем гонки для следующих сценариев:
- загрузка и инициализация SDK и библиотек
- получение сегментов информации о пользователе
- обработка редиректов
- проверка поиска
- управление токенами
- События пользовательского интерфейса, подобные приведенному ниже:
const { closed } = yield race({
confirmed: take(AGE_VERIFICATION_MODAL_CONFIRMED),
closed: take(AGE_VERIFICATION_MODAL_CLOSED)
})
Если мы хотим иметь доступ к определенным службам (или контекстам), которые можно использовать в наших сагах, мы можем использовать файлы getContext
. После некоторой быстрой настройки мы можем легко получить доступ к методам, которые могут вызывать API, LocalStorage, механизмы ведения журналов и промежуточное ПО.
import createSagaMiddleware from 'redux-saga'
const cookieStorage = {
save: value => {
// save the cookie
}
}
const logger = {
logInfo: info => {
// log the info
}
}
const sagaMiddleware = createSagaMiddleware({
context: {
cookieStorage,
logger
}
})
sagaMiddleware.run(rootSaga)
После этого мы можем получить доступ к этим контекстам в наших сагах слайсов с помощью getContext
.
function* cookiePreferenceStorageSaga() {
try {
const { cookieStorage } = yield getContext('cookieStorage')
const key = 'foobar'
yield call(cookieStorage.save, key)
yield put(cookiePreferenceStorageSucceeded())
} catch (error) {
const logger = yield getContext('logger')
logger.logInfo('Cookie preference storage error', { error })
}
}
Вывод
Эти изящные шаблоны служат надежным подходом к обработке динамической среды, такой как глобальное состояние массивного веб-приложения. Возможно, в следующем посте я задокументирую еще несколько шаблонов, которые мы используем в Just Eat Takeaway. Дайте мне знать, если вы хотели бы прочитать об этом!