Как легко создать свое первое промежуточное программное обеспечение Redux

kak legko sozdat svoe pervoe promezhutochnoe programmnoe obespechenie

Габриэле Чимато

YFKTwWxOMLQqYOVMFLRUOrQJeUkWkxn2o0av
Фото JESHOOTS.COM на Unsplash

Практически каждая реальная программа React широко использует асинхронные запросы. Если вы управляете состоянием программы с помощью Redux, есть несколько способов обработки асинхронных действий.

Возможно, вы слышали о redux-thunkили redux-saga, самые популярные решения для обработки асинхронных действий в Redux Такие подходы пригодятся, когда вам нужно отследить статус запроса в вашем штате.

Шаблон, который я видел довольно часто, использующий thunks является следующим:

import {
  FETCH_DATA_ERROR,
  FETCH_DATA_PENDING,
  FETCH_DATA_SUCCESS,
} from 'constants/actionTypes';

function fetchMyDataError(error) {
  return {
    type: FETCH_DATA_ERROR,
    payload: error,
  };
}

function fetchDataPending() {
  return { type: FETCH_DATA_PENDING };
}

function fetchMyDataSuccess(response) {
  return {
    type: FETCH_DATA_SUCCESS.
    payload: response,
  };
}

function fetchData() {
  return (dispatch) => {
    dispatch(fetchDataPending());
      
    fetch('
      .then(res => res.json())
      .then(data => dispatch(fetchMyDataSuccess(data)))
      .catch(err => dispatch(fetchMyDataError(err)));
  };
}

Как видите, мы написали достаточное количество кода. Этот пример можно упростить и обрабатывать только одной функцией. В любом случае, в скором времени это будет казаться очень повторяющимся и утомительным, особенно если вам нужно отслеживать продолжительность жизни каждого асинхронного запроса в вашей программе. Такое многословие не помогает с шаблоном, необходимым для программы, использующей Redux.

Когда шаблон или блок кода используются снова и снова, хорошо извлечь его в функции. Это отвлекает логику этого, и для «функционирования» требуется лишь наименьшее количество данных. В это время я начал играть с идеей написать собственное промежуточное программное обеспечение. redux-slim-async помогает мне пропустить шаблонный код и обеспечить отличный контроль с помощью крошечного API. Давайте рассмотрим предыдущий пример с новым промежуточным программным обеспечением:

import {
  FETCH_DATA_PENDING,
  FETCH_DATA_SUCCESS,
  FETCH_DATA_ERROR,
} from 'constants/actionTypes';

function fetchData() {
  return {
    types: [
      FETCH_DATA_PENDING,
      FETCH_DATA_SUCCESS,
      FETCH_DATA_ERROR,
    ],
    callAPI: fetch(‘
      .then(res => res.json()),
  }
}

Все эти неудобные функции исчезли, и мы fetchData теперь минимальный – достаточно аккуратно! ?

Теперь давайте создадим меньшую версию такого промежуточного ПО. Это поможет нам понять внутреннюю работу этого, и вы сможете создать свой собственный следующий!

Создание промежуточного ПО

Позвольте мне немедленно показать вам код этого небольшого промежуточного ПО. Вы увидите, что это не так ошеломляюще, как вам может показаться.

function createSlimAsyncMiddleware({ dispatch, getState }) {
  return next => action => {
    const {
      types,
      callAPI,
      shouldCallAPI = () => true,
    } = action;
      
    if (!actionIsValid(action)) next(action);
    if (!shouldCallAPI(getState())) {
      return Promise.resolve(getState());
    }
      
    const [pendingType, successType, errorType] = types;
      
    dispatch({ type: pendingType });
      
    return callAPI()
      .then(response => {
        dispatch({
          type: successType,
          payload: response,
        });
        
        return Promise.resolve(getState());
      })
      .catch(error => {
        dispatch({
          type: errorType,
          payload: error,
        });
        
        return Promise.reject(error);
     });
  };
}

Подождите секунду… это все? Совершенно!

Давайте по одной строчке. Это промежуточное программное обеспечение — это функция, которая возвращает функцию, которая возвращает возвращающую функцию a Promise. Как бы странно это ни звучало, вы увидите, что это гораздо проще, чем кажется.

Наша функция промежуточного программного обеспечения получает объект с двумя полями: dispatch и getState. Это именуемые параметры, предоставленные Redux.

  • dispatch: как следует из названия, это то, что мы используем для отправки действия. Это даст нам возможность управлять действиями внутри промежуточного ПО.
  • getState: это функция, возвращающая текущее состояние в заданный момент времени. Это может быть полезно, если мы хотим вернуть обновленное состояние после отправки действия

на первая линия мы имеем функцию с одним аргументом объекта с полями dispatch и getState.

на вторая строка мы возвращаем функцию, принимающую вызов вызванного аргумента next. Такая функция возвращает принимающую функцию action и что-то делает. Об этом позже. Но что есть next для ? Почему мы должны вернуть функцию, которая возвращает функцию, которая что-то делает?

То, что Redux делает под капотом, это компонует промежуточное программное обеспечение таким образом, чтобы каждое из них имело ссылку на… next один! Имя очень помогает сделать его интуитивно понятным. Мы заканчиваем официальный Redux dispatch работать с нашим промежуточным программным обеспечением. Это создает конвейер, через который должно пройти действие.

Помните, что вам НЕ ОБЯЗАТЕЛЬНО звонить по телефону next(action)но вам нужно это сделать, если вы не хотите блокировать процесс отправки (мы увидим конкретный случай в нашем промежуточном программном обеспечении).

n9N9siS5VEsMDiFEBrOSMK-ko91Zl8Z4SoFM
Блок-схема, исследующая конвейер промежуточного ПО упрощенным способом

В нашем случае это полезно, поскольку мы не хотим перехватывать каждое действие, а только те, которые действительны для нашего промежуточного ПО. Для простоты я добавил проверку под названием actionIsValid. Эта функция занимает action как аргумент и возвращает логическое значение. Возвращенное логическое значение представляет действительность этого действия для нашего промежуточного ПО.

actionisValid это хорошее место для проверки на наличие ошибок и throw их, если это нужно. Если он недействителен, я использую нашу ссылку на next промежуточное программное обеспечение и передать ему действие. Иначе мы можем наконец-то использовать действие и что-то сделать (схема выше представляет упрощенную версию этой логики).

Остальные промежуточные ПО достаточно интуитивно понятны. Мы проверяем действительность действия, чтобы определить, следует ли выполнять наш асинхронный запрос или нет.

shouldCallAPI Является параметром нашего API промежуточного программного обеспечения. Учитывая состояние, он возвращает логическое значение, определяющее, должен ли выполняться наш запрос или нет. Промежуточное программное обеспечение придает ему значение по умолчанию (возвращающая функция true ). Если нам не нужно делать вызов API, мы возвращаемся Promise.resolve. Таким образом мы можем использовать .then или async/await на любое асинхронное действие, проходящее через наше промежуточное программное обеспечение.

const [pendingType, successType, errorType] = types;

Следующий шаг – определение действия type поле, переданное в качестве параметра. Мы используем деструктуризацию массива, чтобы разобрать наш types параметр массива.

dispatch({ type: pendingType });

Теперь мы наконец-то можем воспользоваться dispatch метод. Это посылает действие Redux, как вы это делаете обычно. Такое действие отражает состояние «ожидания» нашего асинхронного запроса.

return callAPI()
  .then(response => {
    dispatch({
      type: successType,
      payload: response,
    });
    
    return Promise.resolve(getState());
  })
  .catch(error => {
    dispatch({
      type: errorType,
      payload: error,
    });
    
    return Promise.reject(error);
  });

Наконец-то мы имеем последнее return заявление. Здесь мы выполняем вызов API и исходя из того, как Promise решает, мы присылаем и возвращаем разные значения.

  • Успех: учитывая ответ от API, мы отправляем успешное действие. Полезная нагрузка – ответ на запрос. Сразу после этого возвращаемся а Promise что решается с обновленным состоянием нашей программы. Это позволяет нам использовать .then(updatedState => …do somethiнг)
  • Ошибка: если Promise отклоняем, тогда мы присылаем действие об ошибке. В этом случае полезной нагрузкой является сама ошибка.

Это! Как было показано ранее, мы можем создавать действия и использовать их следующим образом:

// Our Action

function fetchData() {
  return {
    types: [
      FETCH_DATA_PENDING,
      FETCH_DATA_SUCCESS,
      FETCH_DATA_ERROR,
    ],
    shouldCallAPI: state => state.dataArr.length === 0,
    callAPI: () =>
      fetch('.then(res => res.json()),
  }
}

// Inside the component

class MyComponent extends Component {
  componentDidMoun() {
    this.props.fetchData()
      .then(state => {
        console.log('updated state after async action:', state);
      })
      .catch(err => {
        console.log('an error occured');
      });
  }
  
// Rest of the component omitted...

}

В этом простом случае мы получаем данные только если наш массив данных пуст. Затем мы регистрируем обновленное состояние после запроса или сообщения об ошибке, если Promise отвергает..

Вывод

Создание промежуточного ПО Redux интуитивно понятно. Вы имеете доступ к диспетчеру магазина и getState функция. Используйте их, чтобы получить доступ к последнему состоянию программы или отправить действия.

Вы также должны помнить об использовании next при необходимости и убедитесь, что не перекрываете выходной трубопровод. В нашем случае, если бы мы не позвонили next(action) любые действия, которые не были действительны для нашего промежуточного ПО, были бы просто отклонены ⚠️!!

Некоторые детали реализации были опущены для простоты. Если вы хотите копнуть глубже, смело исследуйте redux-slim-async промежуточное программное обеспечение здесь.

Поставьте ⭐️, если вам это нравится! Я создал это промежуточное ПО и сейчас использую его в производстве во избежание многих шаблонов. Не стесняйтесь попробовать и оставить отзыв в любое время. Вот еще один ценный ресурс, чтобы еще больше выучить промежуточное программное обеспечение, документы Redux!

Вы также можете следить за мной в Twitter @SuperGabry

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *