Асинхронные actions
ОБНОВЛЕНИЕ 2018: Вышло второе издание (современный код и версии пакетов, данное издание УСТАРЕЛО)
На канале так же проводятся бесплатные вебинары, публикуются переводы и авторские материалы, присоединяйтесь!
Асинхронные actions
Давайте представим синхронное действие: 1. Пользователь кликнул на кнопку 2. dispatch action {type: ТИП_ДЕЙСТВИЯ, payload: доп.данные}
3. интерфейс обновился
Давайте представим асинхронное действие: 1. Пользователь кликнул на кнопку 2. dispatch action {type: ТИП_ДЕЙСТВИЯ_ЗАПРОС}
3. запрос выполнился успешно 4. dispatch action {type: ТИП_ДЕЙСТВИЯ_УСПЕШНО, payload: доп.данные}
4. запрос выполнился неудачно 5. dispatch action {type: ТИП_ДЕЙСТВИЯ_НЕУДАЧНО, error: true, payload: доп.данные ошибки}
Благодаря такой схеме, в reducer'e мы сможем реализовать подобное:
Как нам известно, действие - это простой объект, который возвращается функцией его создающей (action creator).
Убедимся в этом:
src/actions/PageActions.js
Было бы неплохо иметь возможность возвращать не простой объект, а функцию, внутри которой иметь доступ к методу dispatch
, и вызывать его с необходимым типом действия. Псевдокод, мог бы выглядеть так:
Но вот незадача, actions - это простой объект, и если action creator возвращает не простой объект, а функцию, то это как-то... Подождите! Ведь это именно то, что нам нужно: Если action creator возвращает не простой объект, а функцию - выполни ее, иначе если это простой объект ... тадам, передай дальше. Более того, благодаря applyMiddleware
у нас как раз есть доступный метод dispatch! И еще бонусом getState.
Отлично, мы только что поняли, что нам нужен еще один усилитель. Такой усилитель уже написан, причем код его невероятно прост, я даже приведу его здесь:
усилитель: redux-thunk
Нам остается лишь добавить зависимость в наш проект, и убедиться, что у нас redux версии, не ниже 3.1.0
Для практики, предлагаю написать следующее:
по клику на кнопку с номером года
меняется год в заголовке
ниже (где должны быть фото), появляется текст "Загрузка..."
после удачной загрузки*
убрать текст "Загрузка..."
отобразить строку "У тебя ХХ фото" (зависит, от длины массива, переданного в action.payload)
* вместо реального метода загрузки, использовать setTimeout, который является удобным для тренировок исполнения асинхронных запросов.
Вы можете попробовать выполнить это задание сами, а потом сравнить его с решением ниже.
Для отображения / скрытия фразы "Загрузка...", используйте в reducer'е еще одно свойство у состояния. Например, fetching:
Решение ниже.
Для начала изменим набор констант:
src/constants/Page.js
Далее добавим новый усилитель: src/store/configureStore.js
Изменим action creator: src/actions/PageActions.js
Изменим reducer: src/reducers/page.js
У нас готова логика для обновления состояния (и интерфейса, разумеется). Осталось поправить отображение.
Так как мы переписали и переименовали функцию (setYear -> getPhotos):
src/containers/App.js
Причем, в mapDispatchToProps
- нам ничего менять не нужно, так как мы по прежнему присоединяем все pageActions в props контейнера <App />
Обновим соответствующий компонент: src/components/Page.js
Когда будете проверять работу в браузере, обратите внимание на логгер. Он все так же работает и информативен.
Пока мы писали код для асинхронного запроса, мы НЕ нарушили главные принципы redux-приложения: 1. Мы всегда возвращали новое состояние (новый объект, смотрите src/reducers/page.js) 2. Мы строго следовали однонаправленному потоку данных в приложении: юзер кликнул - возникло действие - редьюсер изменил - компонент отобразил.
Итого: вы можете сами дописать наше приложение, чтобы оно взаимодействовало с VK, так как все что нужно, это добавить реальный асинхронный запрос (точнее парочку - для логина, и для получения фото). Ложку дегтя добавляет тот факт, что для этого потребуется создать в интерфейсе VK приложение, и выполнять наши запросы с реального сервера, так как VK.API не работает с localhost.
Об этом мы и поговорим в следующей главе.
Исходный код на данный момент.
Last updated