В этой части (которая наиболее сложная из пройденных), мы добавим в наше приложение Redux (документация на русском) и подключим его к React. Redux управляет состоянием приложения. Он включает в себя такие понятия, как:
- хранилище (store) - простой JavaScript объект, представляющий состояние вашего приложения;
- действия (actions), которые обычно запускаются пользователем;
- редюсеры (reducers), которые можно рассматривать как обработчики действий.
Редюсеры воздействуют на состояние приложения (хранилище), и когда состояние приложения изменяется, что-то происходит в вашем приложении. Хорошая визуальная демонстрация Redux находится здесь.
Для того чтобы продемонстрировать использование Redux наиболее доступным способом, наше приложение будет состоять из сообщения и кнопки. Сообщение будет показывать лает собака или нет (изначально - нет), а кнопка будет заставлять ее лаять, что должно будет отражаться в сообщении.
В этой части нам потребуется два пакета: redux
и react-redux
.
- Запустите
yarn add redux react-redux
.
Давайте начнем с создания двух папок: src/client/actions
и src/client/reducers
.
- В
actions
, создайтеdog-actions.js
:
export const MAKE_BARK = 'MAKE_BARK';
export const makeBark = () => ({
type: MAKE_BARK,
payload: true,
});
Тут мы определяем тип действия - MAKE_BARK
, и функцию makeBark
(именуемую генератор действий), которая запускает действие MAKE_BARK
. Мы их экспортируем, т.к. они нам понадобятся в других файлах. Это действие построено на основе модели Flux Standard Action, вот почему оно имеет атрибуты type
и payload
.
- В
reducers
создайтеdog-reducer.js
:
import { MAKE_BARK } from '../actions/dog-actions';
const initialState = {
hasBarked: false,
};
const dogReducer = (state = initialState, action) => {
switch (action.type) {
case MAKE_BARK:
return { hasBarked: action.payload };
default:
return state;
}
};
export default dogReducer;
Здесь мы определили исходное состояние приложения, являющееся объектом, содержащим свойство hasBarked
, установленное в false
, и dogReducer
- функцию, ответственную за перемену состояния, в зависимости от того, какое действие произошло. Состояние не может быть изменено в этой функции, но должен быть возвращен совершенно новый объект состояния.
- Изменим
app.jsx
, чтобы создать хранилище. Можете заменить весь файл следующим содержимым:
import React from 'react';
import ReactDOM from 'react-dom';
import { createStore, combineReducers } from 'redux';
import { Provider } from 'react-redux';
import dogReducer from './reducers/dog-reducer';
import BarkMessage from './containers/bark-message';
import BarkButton from './containers/bark-button';
const store = createStore(combineReducers({
dog: dogReducer,
}));
ReactDOM.render(
<Provider store={store}>
<div>
<BarkMessage />
<BarkButton />
</div>
</Provider>
, document.querySelector('.app')
);
Наше хранилище создано функцией Redux createStore
, вполне наглядно. Объект хранилища собирается путем комбинирования всех редюсеров (в нашем случае одного) с помощью функции Redux combineReducers
. Каждый редюсер в ней имеет имя, наш назовем dog
.
Мы сделали достаточно в части, относящейся к чистому Redux.
Теперь мы подключим Redux к React, используя пакет react-redux
. Для того чтобы react-redux
мог передать хранилище в наше приложение на React, нам нужно обернуть все приложение в компонент <Provider>
. Этот компонент должен содержать единственный дочерний элемент, так что мы добавили <div>
, и этот <div>
содержит два основных элемента нашего приложения: BarkMessage
и BarkButton
.
Как вы могли заметить, в разделе import
мы импортируем BarkMessage
и BarkButton
из директории containers
. Сейчас самое время представить концепцию Компонентов и Контейнеров.
Компоненты - это "глупые" компоненты React, в том смысле, что они ничего не знают о состоянии Redux. Контейнеры - "умные", знают о состоянии и о том, что мы собираемся подключиться (connect) к "глупым" компонентам.
-
Создайте 2 папки,
src/client/components
иsrc/client/containers
. -
В
components
создайте следующие файлы:
button.jsx
import React, { PropTypes } from 'react';
const Button = ({ action, actionLabel }) => <button onClick={action}>{actionLabel}</button>;
Button.propTypes = {
action: PropTypes.func.isRequired,
actionLabel: PropTypes.string.isRequired,
};
export default Button;
и message.jsx:
import React, { PropTypes } from 'react';
const Message = ({ message }) => <div>{message}</div>;
Message.propTypes = {
message: PropTypes.string.isRequired,
};
export default Message;
Это примеры "глупых" компонентов. Они практически лишены логики и просто отображают то что потребуется, путем передачи им свойств (props) React. Основное отличие button.jsx
от message.jsx
в том, что первый содержит действие в качестве одного из параметров. Это действие срабатывает по событию onClick
. В контексте нашего приложения надпись Button
никогда не будет изменяться, однако компонент Message
должен отражать состояние нашего приложения и будет изменяться на основе этого.
Опять же, компоненты ничего не знают о Redux действиях или о состоянии нашего приложения. Вот почему мы собираемся создать "умные" контейнеры, которые "подведут" нужные действия и данные к этим двум "глупым" компонентам.
- В
containers
создайте следующие файлы:
bark-button.js
import { connect } from 'react-redux';
import Button from '../components/button';
import { makeBark } from '../actions/dog-actions';
const mapDispatchToProps = dispatch => ({
action: () => { dispatch(makeBark()); },
actionLabel: 'Bark',
});
export default connect(null, mapDispatchToProps)(Button);
и bark-message.js:
import { connect } from 'react-redux';
import Message from '../components/message';
const mapStateToProps = state => ({
message: state.dog.hasBarked ? 'The dog barked' : 'The dog did not bark',
});
export default connect(mapStateToProps)(Message);
BarkButton
подключает действие makeBark
и метод Redux dispatch
к Button
. А BarkMessage
подключает Message
к состоянию приложения. Когда состояние изменится, Message
автоматически перегенерируется с нужным значением свойства message
. Эти подключения выполнены через функцию connect
пакета react-redux
.
- Теперь можете запустить
yarn start
и открытьindex.html
. Вы должны увидеть надпись "The dog did not bark" и кнопку. Когда вы нажмете на кнопку, сообщение должно измениться на "The dog barked".
Следующий раздел: 10 - Immutable JS и улучшения Redux
Назад в предыдущий раздел или Содержание.