JS: Redux (React)

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

В отличие от синхронных запросов, которые выполняются здесь и сейчас, асинхронные растянуты во времени. Каждый асинхронный запрос можно представить конечным автоматом с тремя состояниями "something requested", "answer received" или "request failed". Почему это важно? Как минимум нам важно знать, когда запрос был выполнен, чтобы оповестить пользователя и произвести необходимые изменения. Но также важно знать, когда запрос начался.

Предположим, что пользователь заполнил форму создания новой задачи и нажал "создать", а потом быстро нажал "создать" ещё раз до того, как запрос успел выполниться. Такая ситуация нередко встречается и с обычными формами без JS. Она приводит к тому, что на сервере создаются две одинаковые сущности либо выскакивают ошибки, связанные с валидацией. Правильное решение в подобной ситуации связано с необходимостью менять интерфейс так, чтобы повторная отправка стала невозможной. Как правило, всё сводится к блокированию кнопки отправки с крутящимся спинером. Соответственно, после окончания запроса кнопку необходимо разблокировать или, если того требует UI, вообще убрать форму. То же самое нужно делать не только в случае успеха, но и в случае провала, иначе может получиться ситуация, что пользовательский запрос провалился, а кнопка осталась заблокирована навсегда (до перезагрузки страницы).

С точки зрения нашего React-Redux приложения автомат будет состоять из состояния в контейнере и трёх событий:

  • TASK_UPDATE_REQUEST.
  • TASK_UPDATE_SUCCESS.
  • TASK_UPDATE_FAILURE.

Именование в стиле request, success и failure — рекомендация самого Redux. Желательно всегда придерживаться именно её в случаях, когда состояния три. Большинство запросов укладываются именно в эту схему.

export const updateTaskRequest = createAction('TASK_UPDATE_REQUEST');
export const updateTaskSuccess = createAction('TASK_UPDATE_SUCCESS');
export const updateTaskFailure = createAction('TASK_UPDATE_FAILURE');

Действия, описанные выше, по сути, синхронны. А где тогда выполняется сам запрос?

Для этого вводится понятие асинхронные действия (async actions). И если в React для работы с асинхронными вызовами ничего дополнительно делать не нужно, то Redux из коробки это не умеет. Наиболее простой способ начать выполнять запросы на сервер — подключить библиотеку redux-thunk. Она представляет из себя мидлвар, который нужно не забыть подключить:

import thunk from 'redux-thunk';

const store = createStore(
  reducers,
  /* ... */,
  applyMiddleware(thunk),
);

На этом интеграция заканчивается. Следующим шагом создаются сами действия:

export const updateTask = (id, values) => async (dispatch) => {
  dispatch(updateTaskRequest());
  try {
    const response = await axios.patch(routes.taskUrl(id), { task: values });
    dispatch(updateTaskSuccess({ task: response.data }));
  } catch (e) {
    // Обязательно выводите ошибку, иначе вы не узнаете что пошло не так при отладке
    console.log(e);
    dispatch(updateTaskFailure());
  }
};

В отличие от синхронных действий, асинхронное — функция, даже две функции. Внешняя функция принимает те параметры, которые нам нужны, а вот внутренняя, асинхронная, принимает на вход функцию dispatch. Кстати, эту внутреннюю функцию вызывать не придётся, вся грязная работа делается автоматически за счёт проброса действий через контейнер. Работа этого действия для нашей ситуации описывается так:

  1. Уведомляем Redux о начале внешнего запроса.
  2. Выполняем внешний запрос.
  3. Если запрос выполнился удачно, уведомляем Redux и передаём туда полученные данные (если есть).
  4. Если запрос выполнился неудачно, уведомляем Redux.

Этот паттерн встречается в реальных приложениях крайне часто.

Посмотрим, как в коде React вызывается обработчик, выполняющий наше действие:

class EditTaskForm extends React.Component {
  handleUpdateTask = (values) => {
    const { updateTask, task } = this.props;
    updateTask(task.id, values);
  }

  render() {
    const { taskCreatingState } = this.props;
    const disabled = taskCreatingState === 'requested';

    return (
      <form action="" className="form-inline" onSubmit={this.props.handleSubmit(this.handleUpdateTask)}>
        <div className="form-group mx-3">
          <Field name="text" required component="input" type="text" />
        </div>
        <button type="submit" disabled={disabled} className="btn btn-primary btn-sm">Update</button>
      </form>
    );
  }
}

export default reduxForm({
  form: 'editTask',
})(EditTaskForm);

Из кода выше видно, что действие вызывается как обычно.

Библиотека redux-thunk всего лишь один из многих способов работы с асинхронными действиями в Redux. Существуют и другие пакеты, дающие больший контроль и больший уровень автоматизации. Но, как правило, они сложнее в понимании.


<span class="translation_missing" title="translation missing: ru.web.courses.lessons.mentors.mentor_avatars">Mentor Avatars</span>

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.
Об обучении на Хекслете

Для полного доступа к курсу нужна профессиональная подписка

Профессиональная подписка откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
115
курсов
892
упражнения
2241
час теории
3196
тестов

Открыть доступ

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

  • 115 курсов, 2000+ часов теории
  • 800 практических заданий в браузере
  • 250 000 студентов

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Rambler
Логотип компании Bookmate
Логотип компании Botmother

Есть вопрос или хотите участвовать в обсуждении?

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы соглашаетесь c «Политикой конфиденциальности» и «Условиями оказания услуг».