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

Dec 05 2022
В этом посте мы углубимся в Redux Saga — фреймворк JavaScript, используемый для обработки побочных эффектов в нашем веб-приложении. В предыдущем посте я познакомил нас с основными концепциями 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. Дайте мне знать, если вы хотели бы прочитать об этом!