Асинхронный Redux

2. Асинхронные действия

Redux не предоставляет функционала для отправки асинхронных действий, эту задачу решают прослойки. Есть много готовых решений: для простых асинхронных операций подойдет redux-promise, для средней сложности redux-thunk, а для очень сложных и запутанных redux-saga или redux-observable.

Для начала необходимо научиться писать асинхронные action creators. Это функции которые вместо объекта-действия возвращают функцию. В компьютерных науках это называется thunk.

const asyncActionCreator = args => dispatch => {};

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

const asyncActionCreator = args => dispatch => {
  fetch('some url')
    .then(r => r.json())
    .then(data => {
      dispatch({
        type: 'FETCH_SUCCESS',
        payload: data,
      });
    });
};

2.1. thunk middleware

Напишем прослойку thunk, которая умеет обрабатывать асинхронные действия. Если действие это функция, она будет вызвана и аргументами ей будут переданы dispatch и getState, тем самым позволяя использовать dispatch в теле действия. В противном случае, если это обычный объект, действие будет отправлено дальше по цепочке прослоек.

const thunk = ({ dispatch, getState }) => next => action =>
  typeof action === 'function' ? action(dispatch, getState) : next(action);

Репозиторий redux-thunk

2.2. HTTP-запросы

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

Начальное состояние может выглядеть так, с полями флага загрузки и хранения ошибки.

{
  notes: {
    items: [],
    loading: false,
    error: null
  }
}
Copy

Тогда асинхронное действие может выглядеть так.

  • При notes/FETCH_START в поле loading записывается true, а при notes/FETCH_SUCCESS или notes/FETCH_FAILURE наоборот false

  • При notes/FETCH_FAILURE в поле error записывается объект ошибки

  • При notes/FETCH_SUCCESS в поле items записываются данные

const fetchStart = () => ({
  type: 'notes/FETCH_START',
});

const fetchSuccess = data => ({
  type: 'notes/FETCH_SUCCESS',
  payload: data,
});

const fetchFailure = error => ({
  type: 'notes/FETCH_FAILURE',
  payload: error,
});

const asyncActionCreator = args => dispatch => {
  dispatch(fetchStart());

  fetch('some url')
    .then(r => r.json())
    .then(data => dispatch(fetchSuccess(data)))
    .catch(err => dispatch(fetchFailure(err)));
};
Copy

2.3. Дополнительные материалы

Last updated

Was this helpful?