Створення actions · redux-course-ru
ОБНОВЛЕННЯ 2018: Вийшло друге видання (сучасний код та версії пакетів, дане видання ЗАСТАРІЛО)
Створення actions
Нарешті ми підходимо до питання взаємодії з користувачем програми. Практично будь-яка дія користувача в інтерфейсі =направлення дії (dispatch actions)
Натисніть кнопку року, наш додаток:
- встановлює заголовок
- завантажує фото цього року
Зараз пропоную розглянути встановлення заголовка. Завантаження фотографій вимагає виконання асинхронного запиту, а щоб дістатися до цього, ми повинні розглянути кілька цікавих речей. До того ж, встановлення заголовка відмінно показує на простому прикладі, як обертаються дані всередині redux-додатки, а саме:
- Додаток отримав початковий стан (initial state)
- Користувач натиснувши кнопку відправив дію (dispatch action)
- Відповідний редьюсер оновив частину додатку, згідно з тим, що дізнався від дії.
- Програма змінилася і тепер відображає новий стан.
- . (Все повторюється по колу, з пункту 2)
Це і є однонаправлений потік даних.
Створимо page action:
Нагадую, що поля type і payload - лише "негласна" угода. Трохи про це можна почитати англійською тут.
Виправимо редьюсер page:
Зверніть увагу, у аргументах у функції page вказано другий аргумент - action. Це стандартні аргументи redux reducer'а. Завдяки цьому ми можемо легко обробляти різні дії за їх типом, потрапляючи в потрібну секцію case оператора switch.
Також зверніть увагу, що ми незмінили об'єкт state, а повернулиновий з полем year рівним action.payload (а значитьроком, обраним користувачем).
Додаємо виклик actions з компонентів
У нас є action, і є reducer готовий змінити state програми (так, я навмисне пишу іноді ці слова англійською). Але наш компонент не знає, як звернутися до необхідної дії.
Відповідно до таблиці з минулого розділу: для зміни даних, наш компонент Page.js повинен викликати callback з this.props, а наш контейнер* App.js - відправляти дію (dispatch action).
* я говорю, контейнер, хоча правильніше називати контейнером, але так як він генерується функцією connect на основі App.js, вважаю це допустимим.
connect першим аргументом приймає "мапінг" (відповідність) state до props, а другим мапінг dispatch до props. Як би дико це не звучало, на практиці це означає, що нам достатньо передати другий аргумент.
Почнемо з аналізу mapDispatchToProps . Всередині функції ми використовували допоміжну функцію з redux - bindActionCreators (оф. документація, яка дозволила викликати setYear, якщо висловитися просто з деякими припущеннями як:
Тим самим, необхідна зміна прослуховується в redux store, і в нашому редьюсері Page відповідно.
Отже, після виконання connect(mapStateToProps, mapDispatchToProps)(App) ми отримали в App.js нові властивості (props), що наочно демонструє вкладка "React" у chrome dev tools.
Додавши setYear у властивості Page.js , не важко використовувати необхідний action з компонента, який як і раніше знати нічого не знає про redux.
UPDATE [18.03.16]: властивість innerText наведена в коді нижче - нестандартна, тому з ним можуть виникнути проблеми в деяких браузерах. Замість нього можна використовувати - textContent.
Власне код компонентаPage як і раніше дуже простий. Рядок ::this.onYearBtnClick === this.onYearBtnClick.bind(this) , і потрібна так як React з версії 0.14.x не прив'язує цей компонент.
Використання подвійної двокрапки - це можливість ES7 (experimental), яка доступна в babel з налаштуванням stage=0 (для тих хто писав код, починаючи з розділу "Підготовка" - все вже налаштовано, дивись файл .babelrc)
Глава видалася досить довгою, а найгірше, що ми написали "кіпу" коду, лише для оновлення цифри в заголовку. Де прибуток, як кажуть?
Профіт виявиться далі, коли ваша програма розростеться. Коли його потрібно буде підтримувати і додавати нові фічі. За рахунок однонаправленого потоку даних (користувач клікнув - дія викликалася - ред'юсер змінив стан - компонент відмалював зміни) навіть у додатку, написаному давно, у вас вийде дуже швидко розібратися і внести необхідні оновлення, які вимагає бізнес. До того ж такий підхід відмінно працює і для командної роботи.