Асинхронные actions

Давайте представим синхронное действие:

  • Пользователь кликнул на кнопку

  • dispatch action {type: ТИП_ДЕЙСТВИЯ, payload: доп.данные}

  • интерфейс обновился

Давайте представим асинхронное действие:

  • Пользователь кликнул на кнопку

  • dispatch action {type: ТИП_ДЕЙСТВИЯ_ЗАПРОС}

  • запрос выполнился успешно

    • dispatch action {type: ТИП_ДЕЙСТВИЯ_УСПЕШНО, payload: доп.данные}

  • запрос выполнился неудачно

    • dispatch action {type: ТИП_ДЕЙСТВИЯ_НЕУДАЧНО, error: true, payload: доп.данные ошибки}

Благодаря такой схеме, в reducer'e мы сможем реализовать подобное:

switch(тип_действия)
  case ТИП_ДЕЙСТВИЯ_ЗАПРОС:
    покажи preloader
  case ТИП_ДЕЙСТВИЯ_УСПЕШНО:
    скрой preloader, покажи данные
  case ТИП_ДЕЙСТВИЯ_НЕУДАЧНО:
    скрой preloader, покажи ошибку

Как нам известно, действие - это простой объект, который возвращается функцией его создающей (action creator).

Убедимся в этом:

src/actions/PageActions.js

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

Но вот незадача, actions - это простой объект, и если action creator возвращает не простой объект, а функцию, то это как-то... Подождите! Ведь это именно то, что нам нужно: Если action creator возвращает не простой объект, а функцию - выполни ее, иначе если это простой объект ... тадам, передай дальше. Более того, мы знаем, что в цепочке middleware у нас как раз есть доступный метод dispatch! И еще бонусом getState.

Отлично, мы только что поняли, что нам нужен еще один усилитель. Такой усилитель уже написан, причем код его невероятно прост, я даже приведу его здесь:

усилитель: redux-thunk

Нам остается лишь добавить зависимость в наш проект.

И добавить redux-thunk в цепочку усилителей перед логгером, так как логгер должен быть последним усилителем в цепочке.

Для практики, предлагаю написать следующее:

  • по клику на кнопку с номером года

    • меняется год в заголовке

    • ниже (где должны быть фото), появляется текст "Загрузка..."

  • после удачной загрузки*

    • убрать текст "Загрузка..."

    • отобразить строку "У тебя ХХ фото" (зависит, от длины массива, переданного в action.payload)

* вместо реального метода загрузки, будем использовать setTimeout, который является удобным для тренировок исполнения асинхронных запросов.

Вы можете попробовать выполнить это задание сами, а потом сравнить его с решением ниже.

Для отображения / скрытия фразы "Загрузка...", используйте в reducer'е еще одно свойство у состояния. Например, isFetching:

Решение ниже.

Изменим action creator: src/actions/PageActions.js

Изменим reducer: src/reducers/page.js

У нас готова логика для обновления состояния (и интерфейса, разумеется). Осталось поправить отображение.

Так как мы переписали и переименовали функцию (setYear -> getPhotos):

src/containers/App.js

Обновим соответствующий компонент:

src/components/Page.js

Когда будете проверять работу в браузере, обратите внимание на логгер. Он все так же работает и информативен.

async-request-setTimeout

Пока мы писали код для асинхронного запроса, мы НЕ нарушили главные принципы redux-приложения:

  1. Мы всегда возвращали новое состояние (новый объект, смотрите src/reducers/page.js)

  2. Мы строго следовали однонаправленному потоку данных в приложении: юзер кликнул - возникло действие - редьюсер изменил - компонент отобразил.

Итого: вы можете сами дописать наше приложение, чтобы оно взаимодействовало с VK, так как все что нужно, это добавить реальный асинхронный запрос (точнее парочку - для логина, и для получения фото). Для этого придется почитать документацию по работе с VK API.

Для тех, кто хочет добить пример поскорее - следующая глава, в которой мы загрузим таки реальные фото из вашего профиля VK.

Исходный код на данный момент.

Last updated

Was this helpful?