Пришло время разобраться с Redux и написать простое приложение с его использованием.
Посмотреть готовое приложение можно на хосте.
Я не буду долго рассказывать зачем он нужен и как он работает. В сети есть масса информации.
Остановлюсь только на том, что Redux используется для работы с данными. В библиотеке React нет простых методов для работы с данными и в больших приложениях это часто вызывает трудности.
Для простоты доступа к данным и работы с ними, мы будем использовать Redux.
Стоит напомнить, что Redux использует Flux архитектуру (не путайте с Flux-библиотекой, которая тоже использует такую архитектуру). Flux предполагает однонаправленный поток данных - One-way binding
Для простоты понимания этого, стоит посмотреть на картинку <<Схема>>:
Справа, квадратик у нас имеет подписью View. Это и есть те компоненты, которые вы непосредственно создаете в React и которые в перспективе будут отражать дрянные на экране монитора. В браузере эти компоненты получают данные из Store.
Store - это хранилище и именно так его нужно понимать в данном контексте. По своей сути Store - это обыкновенный объект, который и определяет, как у нас будет выглядеть наши компоненты.
Важный момент заключается в том, что Store является единственным и общим хранилищем для всех наших компонентов и в будущем когда у вас будет увеличиваться и разрастаться, это будет очень удобно, потому что вы сможете легко контролировать процесс распределения свойств.
Далее, у нас идёт Dispatcher. Dispatcher это центральный узел, который обрабатывает все наши потоки данных. То есть - всё проходит через него! А конкретно, через него проходит те данные, которые определяют как раз-таки action.
Значит есть один action (слева на картинке). Этот Action у нас стоит самого начала и считаете это теми данными которые идут по умолчанию. То есть, по умолчанию поступает какие-то данные в Action говорит диспетчеру - "Покажи мне Username такой-то и Lastname такой-то!". И диспетчер заносит изменения в Store, откуда они подтягиваются View (React -components) и мы видим картинку на экране.
В тоже время, если мы хотим поменять нашу визуальную картинку со стороны пользователя, мы так же создаем новый Action. Новый Action, который опять обращается к диспетчеру и делает тоже самое. Говорит диспетчеру "Обнови username Коля на username Вася" и он его обновляет. Это определяет уже наш View.
Вот в этом собственное заключается вся суть архитектуры Flux и самого принципа One Way binding, то есть однонаправленного потока данных. Если вы заметили, то у нас поток данных идёт всегда в одну сторону!
View не может непосредственно повлиять на что-то, что есть в нашим хранилище! Это главное отличие его от так называемого Two-way binding, например, который использует ангуляр.
После установки, мы можем открыть наш проект в редакторе.
Выбираем файл index.js в папке src - это точка входа в наше приложение.
Импортируем компонент Provider из модуля react-redux
Данный компонент является оберткой для нашего приложения и позволяет получать компонентам свойства из Store, поэтому следующим шагом нам нужно обернуть компонент App в Provider.
Следующее, что нам нужно сделать, это импортировать функцию, которая и создает Store.
Для создания хранилища - Store напишем:
В качестве параметра эта функция должна принимать reducer. Сам по себе редусер - это просто функция, поэтому мы для начала, можем записать вместо него , пустую функцию, например стрелочную -
Для соединения провайдера с хранилищем Store, мы добавим Provider атрибут store, со значением store.
Таким образом, мы создали Store, обернули его в провайдер и сделали данные доступные для наших компонентов.
Reducer по своей сути это функция. Эта функция запускается каждый раз, когда мы отправляем какое-то действие. Пользовательского действия у нас пока не будет, а будет действие- инициализации ( т.е. начало работы нашего приложения).
Сам термин пришел к нам из JS Array.prototype.reduce().
То есть, мы берем предыдущее состояние State, то что нам нужно поменять, соединяем это и получаем что-то новое, что в последствии у нас отрендерится на экране.
Создадим первый редусер в файле info.js
Функция принимает в качестве стандартного значения -
Проще говоря редусер принимает один
Файл index.js в папке reducers, нам потребуется для того, чтобы все редусеры держать в одном месте, но в разных файлах. В качестве "родительского" редусера мы будем использовать index.js. В этом нам поможет, то что уже реализовано в библиотеке Redux -
Теперь, самое время вернуться в файл index.js в корне проекта, и заменить анонимную, пустую функцию на
Таким образом наш Store, будет знать, что когда мы к нему обращаемся, то он будет вызывать все редусеры, которые у нас есть.
Store по своей сути, это просто обычный объект, который содержит State для всего нашего приложения. То есть всё, что у нас может изменяться, обо всём этом должен знать State и он это регулирует.
Фактически, мы сейчас с помощью нашей функции в редусере (
App.js
Чтобы подключить Redux к компоненту есть специальная функция -
Подключаем ее:
Она работает очень просто. В том месте, где мы экспортируем наш компонент, просто дописываем эту функцию и в скобках прикрепляем к ней наш компонент:
Если сейчас запустит и посмотреть приложение:
Здесь можно видеть, что функция
App.js
Создаем дополнительную функцию
Чтобы обратится к этим данным, нам нужно обратиться к нашему
И чтобы эта функция заработала, мы должны ее передать, в качестве аргумента, в функцию
Таким образом мы с помощью функции передали в компонент
Мы смогли передать те значения, которые мы изначально несли в нашу "Базу данных" -
Вывести свойства на экран нам легко -
Практически, мы получили тоже самое, как если бы передали свойство
Псомотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Simple data transfer with Redux"
Действия пользователя будут представлены в Action, который на картинке <<Схема>> вверху.
Для лучшего понимания работы редусеров, следует вспомнить, как различаются компоненты в React:
Согласно этой схеме, нам стоит разделить наше приложение на контейнеры и компоненты. Это поможет правильно разделить логику нашего приложения и в будущем, когда приложение разрастется, это поможет легко его поддерживать.
В папке src создадим папки: containers и components.
В папке components создадим: User.js и Year.js
В файле User.js создадим простой, глупый компонент - Dumb Component:
Его render() мы взяли из файла App.js.
В файле Year.js мы создадим несколько кнопок, для возможности выбора пользователем, соответствующего года.
Если коротко, то создали компонент, который будет выводит кнопки. На кнопки навесили событие
Таким образом мы написали два dumb-компонента.
Теперь нам нужно поменять файл App.js
Поскольку он у нас будет выполнять функцию smart - компонента, мы переместим его в папку src/containers
Так как мы поменяли директорию файла App.js, то в файле нам нужно изменить путь подключения его в файле index.js в корне сайта.
На этом этапе нам нужно запустить приложение, чтобы убедиться, что оно по прежнему работает.
Посмотреть весь код приложения на этом этапе, можно в репо - redux-simple-abc ci -m"divided the application into components"
Теперь нам необходимо в наш smart - компонент App.js добавить наши dump-компоненты - User.js и Year.js
Если сейчас посмотреть в браузер, то мы увидим:
Вывели на экран все статические данные, кроме необходимых переменных.
Переменная
Эту переменную мы не меняем в нашем приложении и вывод ее на экран это просто свидетельство того, что Redux работает.
Все действия будут происходить в компоненте Year.js и для того, чтобы их увидеть, нам нужно создать экшен.
В папке src создаем папку actions а в ней файл actionYear.js
Экшен по своей сути это обычный объект. Для удобства их создают через специальные функции, которые называются Action Creators. Это упрощает чтение кода и очень удобно для разработки!
Название функции setYearAction - произвольное, но лучше, если оно будет понятным.
Функция обязательно возвращает объект и в нем должно быть обязательно поле - type.
Поле - type, чем то похоже на идентификатора экшена, и значение этого поля всегда строка с заглавными буквами - UPPERCASE.
Вторая переменная
Более подробно о формате написания экшенов можно почитать в стандартах - Flux Standard Action
Как мы говорили ранее - экшен - ЧТО МЕНЯЕМ... А мы меняем -
Теперь нам нужно понять КАК нам его изменить.
За это у нас отвечает редусер.
Переходим в редусеры src/reducers/info.js и будем его усовершенствовать.
Добавим некий изначальный стейт - например:
Таким образом редусер будет принимать начальное состояние:
Обычно их указывают в формате
В нем обратимся к типу экшена и обработаем различные варианты.
Если у нас тип = "SET_YEAR", то мы возвращаем весь предыдущий стейт (
Другими совами: Мы берем наш изначальный стейт :
reducers/info.js
Вторым аргументом берем стейт из экшена: actions/actionYear.js
Соединяем их вместе и получаем что-то новое! А конкретно - изменяем поле
Теперь нашей задачей является - доделать наш "умный компонент", который должен отправлять данные в Store.
Переходим в src/containers/App.js
Первое - импортировать функцию из actionYear.js
Выведем год статически:
Посмотрим в браузере:
Таким образом мы вывели "стартовые значения" стейта.
Теперь нам нужно оживить наши кнопки!
Для этого нам нужно передать новые данные диспетчеру, а он их добавит в Store.
Для этого есть специальная функция -
setYearFunction - произвольное название. Мы выбрали его, чтобы просто отличать функцию от других. В качестве ключа в нее мы передаем функцию, которую мы записали в стрелочном формате. Эта функция принимает аргумент
В нижней строке мы просто подключили ее, указав в качеств аргумента через запятую.
Если посмотреть в браузер, то в панели разработчика мы увидим:
Поскольку мы подключили в наш компоненту новую функцию -
Теперь для того, чтобы нам ее использовать, нам не нужно делать ничего сложного, а просто получить доступ к этому свойству.
В файле App.js передадим компоненту Year напишем свойство, которому передадим функцию
Теперь становится ясным, что в функции onBtnClick мы как раз и возвращаем это свойство со значением текста кнопки:
Файл: components/Year.js
Теперь самое время посмтреть наше приложение и проверить, что все работает правильно:
Эта функция возвращает нам
Далее эти данные попадают в функцию
В Store запускается редусер.Он принимает начальный стейт и экшен, который ему прислал
Далее возвращается в приложение функцией
Посмотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Finish"
Посмотреть готовое приложение можно на хосте.
Я не буду долго рассказывать зачем он нужен и как он работает. В сети есть масса информации.
Остановлюсь только на том, что Redux используется для работы с данными. В библиотеке React нет простых методов для работы с данными и в больших приложениях это часто вызывает трудности.
Для простоты доступа к данным и работы с ними, мы будем использовать Redux.
Стоит напомнить, что Redux использует Flux архитектуру (не путайте с Flux-библиотекой, которая тоже использует такую архитектуру). Flux предполагает однонаправленный поток данных - One-way binding
Для простоты понимания этого, стоит посмотреть на картинку <<Схема>>:
Справа, квадратик у нас имеет подписью View. Это и есть те компоненты, которые вы непосредственно создаете в React и которые в перспективе будут отражать дрянные на экране монитора. В браузере эти компоненты получают данные из Store.
Store - это хранилище и именно так его нужно понимать в данном контексте. По своей сути Store - это обыкновенный объект, который и определяет, как у нас будет выглядеть наши компоненты.
Важный момент заключается в том, что Store является единственным и общим хранилищем для всех наших компонентов и в будущем когда у вас будет увеличиваться и разрастаться, это будет очень удобно, потому что вы сможете легко контролировать процесс распределения свойств.
Далее, у нас идёт Dispatcher. Dispatcher это центральный узел, который обрабатывает все наши потоки данных. То есть - всё проходит через него! А конкретно, через него проходит те данные, которые определяют как раз-таки action.
Значит есть один action (слева на картинке). Этот Action у нас стоит самого начала и считаете это теми данными которые идут по умолчанию. То есть, по умолчанию поступает какие-то данные в Action говорит диспетчеру - "Покажи мне Username такой-то и Lastname такой-то!". И диспетчер заносит изменения в Store, откуда они подтягиваются View (React -components) и мы видим картинку на экране.
В тоже время, если мы хотим поменять нашу визуальную картинку со стороны пользователя, мы так же создаем новый Action. Новый Action, который опять обращается к диспетчеру и делает тоже самое. Говорит диспетчеру "Обнови username Коля на username Вася" и он его обновляет. Это определяет уже наш View.
Вот в этом собственное заключается вся суть архитектуры Flux и самого принципа One Way binding, то есть однонаправленного потока данных. Если вы заметили, то у нас поток данных идёт всегда в одну сторону!
View не может непосредственно повлиять на что-то, что есть в нашим хранилище! Это главное отличие его от так называемого Two-way binding, например, который использует ангуляр.
Действия загрузки данных.
Передача данных.
Для простоты, мы будем использовать готовое решение create-react-app. Как его установить я рассказывал в своих постах о React Переходим в папку нашего приложения и устанавливаем два дополнительных модуля:- redux
- react-redux
npm install redux react-redux
--save-dev
команду можно не писать.
После установки, мы можем открыть наш проект в редакторе.
Выбираем файл index.js в папке src - это точка входа в наше приложение.
Импортируем компонент Provider из модуля react-redux
import { Provider } from "react-redux";
Данный компонент является оберткой для нашего приложения и позволяет получать компонентам свойства из Store, поэтому следующим шагом нам нужно обернуть компонент App в Provider.
ReactDOM.render(<Provider> <App /> </Provider>, document.getElementById('root'));
Следующее, что нам нужно сделать, это импортировать функцию, которая и создает Store.
import { createStore } from 'redux';
Для создания хранилища - Store напишем:
const store = createStore(()=>{});
В качестве параметра эта функция должна принимать reducer. Сам по себе редусер - это просто функция, поэтому мы для начала, можем записать вместо него , пустую функцию, например стрелочную -
()=>{}
.
Для соединения провайдера с хранилищем Store, мы добавим Provider атрибут store, со значением store.
ReactDOM.render(<Provider store={store}>
<App />
</Provider>, document.getElementById('root'));
Таким образом, мы создали Store, обернули его в провайдер и сделали данные доступные для наших компонентов.
Создание Reducers.
В папке src создадим папку reducers и в ней два файла index.js и info.js.Reducer по своей сути это функция. Эта функция запускается каждый раз, когда мы отправляем какое-то действие. Пользовательского действия у нас пока не будет, а будет действие- инициализации ( т.е. начало работы нашего приложения).
Сам термин пришел к нам из JS Array.prototype.reduce().
Вся суть редусера - это взять предыдущее, значение, плюс текущее значение и в конце - вернуть новое значение.
То есть, мы берем предыдущее состояние State, то что нам нужно поменять, соединяем это и получаем что-то новое, что в последствии у нас отрендерится на экране.
Создадим первый редусер в файле info.js
// имитация базы данных const initialState = { user: "unknown user" } // сам редусер export default function userInfo (state = initialState) { return state }
Функция принимает в качестве стандартного значения -
initialState
и возвращает новый state
на основании старого.
Проще говоря редусер принимает один
state
, а возвращает новый state
.
Файл index.js в папке reducers, нам потребуется для того, чтобы все редусеры держать в одном месте, но в разных файлах. В качестве "родительского" редусера мы будем использовать index.js. В этом нам поможет, то что уже реализовано в библиотеке Redux -
combineReducers
import { combineReducers } from "redux" import userInfo from "./info" const rootReducer = combineReducers({ userInfo }) export default rootReducer;
Теперь, самое время вернуться в файл index.js в корне проекта, и заменить анонимную, пустую функцию на
rootReducer
. И добавим импорт редусеров в наш файл.
import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; import { Provider } from "react-redux"; import { createStore } from "redux"; import rootReducer from "./reducers/index"; const store = createStore(rootReducer); ReactDOM.render(<Provider store={store}> <App /> </Provider>, document.getElementById('root')); serviceWorker.unregister();
Таким образом наш Store, будет знать, что когда мы к нему обращаемся, то он будет вызывать все редусеры, которые у нас есть.
Store по своей сути, это просто обычный объект, который содержит State для всего нашего приложения. То есть всё, что у нас может изменяться, обо всём этом должен знать State и он это регулирует.
Фактически, мы сейчас с помощью нашей функции в редусере (
function userInfo
), занесли в наш State данных переменной user
со значением "unknown user"
. Теперь осталась нам подключить наш компонент к этому Store и взять с него то что принадлежит нам по праву, а именно наши свойства.
App.js
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import { connect } from "react-redux" class App extends Component { render() { return ( <h2>Welcome to React</h2> ); } } export default connect()(App);
Чтобы подключить Redux к компоненту есть специальная функция -
connect
Подключаем ее:
import { connect } from "react-redux";
Она работает очень просто. В том месте, где мы экспортируем наш компонент, просто дописываем эту функцию и в скобках прикрепляем к ней наш компонент:
export default connect()(App);
Если сейчас запустит и посмотреть приложение:
Здесь можно видеть, что функция
connect
создает дополнительный компонент над компонентом App
.
Передача свойств приложению.
Для передачи свойств приложению:App.js
import React, { Component } from 'react'; import './App.css'; import { connect } from "react-redux" class App extends Component { render() { return ( <h2>Welcome to React {this.props.user}</h2> ); } } function mapStateToProps(state) { return { user: state.userInfo.user } } export default connect(mapStateToProps)(App);
Создаем дополнительную функцию
mapStateToProps
. Название произвольное, но оно рекомендуется, чтобы другие разработчики понимали то, что делает эта функция. Эта функция будет передавать наши state
в свойства. Функция принимает наш state
, а возвращать будет некую переменную ( для логичности - user
), а в качестве значения возьмем те данные, которые указали на данный момент в редюсере.
Чтобы обратится к этим данным, нам нужно обратиться к нашему
state
, с которого мы подтягиваем данные. Дaлее к userinfo
из rootReducer
и уже используя userinfo
мы попадаем в наш изначальный редусер и получаем доступ к значению переменной user
в файле info.js.
user: state.userInfo.user
И чтобы эта функция заработала, мы должны ее передать, в качестве аргумента, в функцию
connect
.
Таким образом мы с помощью функции передали в компонент
App
свойство user
со значением unknown user
Мы смогли передать те значения, которые мы изначально несли в нашу "Базу данных" -
const initialState
.
Вывести свойства на экран нам легко -
{this.props.user}
Практически, мы получили тоже самое, как если бы передали свойство
user={"unknown user"}
непосредственно в компонент реакт.
Псомотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Simple data transfer with Redux"
Действия пользователя.
Действия - Actions
Все что мы делали ранее не относилось к действиям пользователя. Если следовать второй картинке этого поста, то мы разбирали работу Action, который слева на картинке.Действия пользователя будут представлены в Action, который на картинке <<Схема>> вверху.
Важно запомнить, что Action определяют что именно нужно поменять, а Reducer - как именно.
Для лучшего понимания работы редусеров, следует вспомнить, как различаются компоненты в React:
Согласно этой схеме, нам стоит разделить наше приложение на контейнеры и компоненты. Это поможет правильно разделить логику нашего приложения и в будущем, когда приложение разрастется, это поможет легко его поддерживать.
В папке src создадим папки: containers и components.
В папке components создадим: User.js и Year.js
В файле User.js создадим простой, глупый компонент - Dumb Component:
import React from "react" export default class User extends React.Component { render() { return ( <h2>Welcome to React {this.props.user}</h2> ); } }
Его render() мы взяли из файла App.js.
В файле Year.js мы создадим несколько кнопок, для возможности выбора пользователем, соответствующего года.
import React from "react" export default class Year extends React.Component { constructor(props) { super(props) this.onBtnClick = this.onBtnClick.bind(this) } onBtnClick(event) { return this.props.setYear(event.target.textContent) } render() { return <div> <button onClick={this.onBtnClick}>1975</button> <button onClick={this.onBtnClick}>1991</button> <button onClick={this.onBtnClick}>2015</button> <p>This year has been chosen - {this.props.year}</p> </div> } }
Если коротко, то создали компонент, который будет выводит кнопки. На кнопки навесили событие
onClick
, которое вызывает функцию onBtnClick
. В конструкторе прибиндили эту функцию. Эта функция возвращает функцию setYear()
, которая в качестве аргумента принимает значение кнопки (текст кнопки - в данном случае год) - event.target.textContent
.
Таким образом мы написали два dumb-компонента.
Теперь нам нужно поменять файл App.js
Поскольку он у нас будет выполнять функцию smart - компонента, мы переместим его в папку src/containers
Так как мы поменяли директорию файла App.js, то в файле нам нужно изменить путь подключения его в файле index.js в корне сайта.
import App from './containers/App';
На этом этапе нам нужно запустить приложение, чтобы убедиться, что оно по прежнему работает.
Посмотреть весь код приложения на этом этапе, можно в репо - redux-simple-abc ci -m"divided the application into components"
Теперь нам необходимо в наш smart - компонент App.js добавить наши dump-компоненты - User.js и Year.js
import React, { Component } from 'react'; import './App.css'; import User from '../components/User'; import Year from '../components/Year'; import { connect } from "react-redux" class App extends Component { render() { return ( <div> <User /> <Year /> </div> ); } } function mapStateToProps(state) { return { user: state.userInfo.user } } export default connect(mapStateToProps)(App);
Если сейчас посмотреть в браузер, то мы увидим:
Вывели на экран все статические данные, кроме необходимых переменных.
Переменная
user
не задействована в экшенах и мы ее можем легко отобразить прямо сейчас:
<User user={this.props.user} />
Эту переменную мы не меняем в нашем приложении и вывод ее на экран это просто свидетельство того, что Redux работает.
Все действия будут происходить в компоненте Year.js и для того, чтобы их увидеть, нам нужно создать экшен.
В папке src создаем папку actions а в ней файл actionYear.js
export default function setYearAction(year) { return { type: "SET_YEAR", payload: year } }
Экшен по своей сути это обычный объект. Для удобства их создают через специальные функции, которые называются Action Creators. Это упрощает чтение кода и очень удобно для разработки!
Название функции setYearAction - произвольное, но лучше, если оно будет понятным.
Функция обязательно возвращает объект и в нем должно быть обязательно поле - type.
Поле - type, чем то похоже на идентификатора экшена, и значение этого поля всегда строка с заглавными буквами - UPPERCASE.
Вторая переменная
payload
, будет принимать значение year
то, которое мы передаем в эту функцию изначально.
payload
- тоже вне гласное соглашение. Обозначает что-то типа "полезной нагрузки".
Более подробно о формате написания экшенов можно почитать в стандартах - Flux Standard Action
Как мы говорили ранее - экшен - ЧТО МЕНЯЕМ... А мы меняем -
year
, который в аргументе и меняем его экшеном - "SET_YEAR"
Теперь нам нужно понять КАК нам его изменить.
За это у нас отвечает редусер.
Переходим в редусеры src/reducers/info.js и будем его усовершенствовать.
Добавим некий изначальный стейт - например:
year: 2015
, а в самом редусере добавим еще один аргумент - action
Таким образом редусер будет принимать начальное состояние:
state = initialState
и то, что должно измениться - action
, все это соединяет и возвращает нам новый стейт на основании проделанной работы.
Обычно их указывают в формате
switch/case
.
В нем обратимся к типу экшена и обработаем различные варианты.
Если у нас тип = "SET_YEAR", то мы возвращаем весь предыдущий стейт (
...state
) и ключ year
с новым значением action.payload
.
Другими совами: Мы берем наш изначальный стейт :
reducers/info.js
const initialState = {
user: "unknown user",
year: 2015
}
Вторым аргументом берем стейт из экшена: actions/actionYear.js
export default function setYearAction(year) {
return {
type: "SET_YEAR",
payload: year
}
}
Соединяем их вместе и получаем что-то новое! А конкретно - изменяем поле
year
на то, которое указано в экшене.
Теперь нашей задачей является - доделать наш "умный компонент", который должен отправлять данные в Store.
Переходим в src/containers/App.js
Первое - импортировать функцию из actionYear.js
import setYearAction from '../actions/actionYear';
Выведем год статически:
import React, { Component } from 'react'; import './App.css'; import User from '../components/User'; import Year from '../components/Year'; import setYearAction from '../actions/actionYear'; import { connect } from "react-redux"; class App extends Component { render() { return ( <div> <User user={this.props.user} /> <Year year={this.props.year}/> </div> ); } } function mapStateToProps(state) { return { user: state.userInfo.user, year: state.userInfo.year } } export default connect(mapStateToProps)(App);
Посмотрим в браузере:
Таким образом мы вывели "стартовые значения" стейта.
Теперь нам нужно оживить наши кнопки!
Для этого нам нужно передать новые данные диспетчеру, а он их добавит в Store.
Для этого есть специальная функция -
mapDispatchToProps(dispatch)
.Она принимает аргумент - dispatch
и возвращает некий объект.
function mapDispatchToProps(dispatch) { return { setYearFunction: year => { dispatch(setYearAction(year)) } } } export default connect(mapStateToProps, mapDispatchToProps)(App);
setYearFunction - произвольное название. Мы выбрали его, чтобы просто отличать функцию от других. В качестве ключа в нее мы передаем функцию, которую мы записали в стрелочном формате. Эта функция принимает аргумент
year
, который передаст наш экшен actionYear
и с ним мы в функции dispatch() вызываем экшен креатор. - setYearAction(year)
и в качестве аргумента ей передадим тот же year
В нижней строке мы просто подключили ее, указав в качеств аргумента через запятую.
Если посмотреть в браузер, то в панели разработчика мы увидим:
Поскольку мы подключили в наш компоненту новую функцию -
mapDispatchToProps
она передала в наш компоненте новое свойство - setYearFunction
(как мы его и назвали в этой функции)
Теперь для того, чтобы нам ее использовать, нам не нужно делать ничего сложного, а просто получить доступ к этому свойству.
В файле App.js передадим компоненту Year напишем свойство, которому передадим функцию
setYearFunction
из ретурна mapDispatchToProps
<Year year={this.props.year} setYear={this.props.setYearFunction} />
this.props
- пишем, потому что функция передается в качестве свойства.
Теперь становится ясным, что в функции onBtnClick мы как раз и возвращаем это свойство со значением текста кнопки:
Файл: components/Year.js
onBtnClick(event) {
return this.props.setYear(event.target.textContent)
}
Теперь самое время посмтреть наше приложение и проверить, что все работает правильно:
Коротко:
Все начианется в нашем компоненте -Year
.Здесь есть кнопки, при нажатии на которые срабатывает функция onBtnClic
Эта функция возвращает нам
event.target.textContent
- то есть в данном случае это текст кнопки.
Далее эти данные попадают в функцию
mapDispatchToProps(dispatch)
и в ней уже year
будет равен тому, что мы указали в компоненте и функция dispatch(setYearAction(year))
отправляет наш экшен в хранилище -Store с помощью метода connect(mapStateToProps, mapDispatchToProps)(App)
В Store запускается редусер.Он принимает начальный стейт и экшен, который ему прислал
mapDispatchToProps(dispatch)
, срабатывает условие - "SET_YEAR"
и year
меняет значение на новое - action.payload
.
Далее возвращается в приложение функцией
mapStateToProps(state)
и мы получаем доступ к этим данным.
Посмотреть весь код приложения, можно в репо - redux-simple-abc ci -m"Finish"