Разбираемся со связкой React и Redux на простейшем примере

Разбираемся со связкой React и Redux на простейшем примере


14.06.2018
/
oberset

Простой пример, который поможет быстро разобраться, как управлять данными с помощью библиотеки Redux в ваших приложениях на React.

Настройка проекта

Первое, что нам необходимо сделать перед запуском приложения на React – это настроить сборщик Webpack и трансформатор javascript-кода Babel. Вначале установим нужные npm-пакеты:

Скачиваем Webpack:
npm install -g webpack webpack-cli

Скачиваем Babel и необходимые плагины:
npm install --save-dev babel-core babel-loader babel-polyfill babel-preset-es2015 babel-preset-react babel-plugin-transform-decorators-legacy babel-plugin-transform-class-properties

Скачиваем React и Redux:
npm install --save react react-dom prop-types redux react-redux

После установки npm-пакетов, в корневой папке проекта нужно создать конфигурационные файлы webpack.config.js и .babelrc.

Содержимое файла webpack.config.js:
'use strict';

var path = require("path");

module.exports = {
mode: 'development',
entry: {
"index": "./index.js"
},
output: {
path: path.join(__dirname, "build"),
filename: "[name].bundle.js"
},
module: {
rules: [
{
test: /\.js$/,
use: "babel-loader"
}
]
},
devtool: "source-map",
watch: true,
watchOptions: {
aggregateTimeout: 300
}
};

Содержимое файла .babelrc:
{
"presets": ["react", "es2015"],
"plugins": [
"transform-decorators-legacy",
"transform-class-properties"
]
}

Теперь создадим в корневой папке проекта файлы index.js и index.html.

Файл index.js:
console.log('Start React Application!!!')

Файл index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Test react-redux</title>
</head>
<body>
<div ></div>
<script src="build/index.bundle.js"></script>
</body>
</html>

Запускаем webpack из корневой папки:
webpack-cli

После запуска webpack должен создать js-файл index.bundle.js и положить его в папку build (как мы указали в конфиге). Если все прошло успешно, то в консоли отобразится примерно такое содержимое:

  • Результат запуска webpack-cli в консоли

    Запуск webpack-cli в консоли

Попробуем открыть файл index.html в браузере:

  • Все готово для старта

    Все готово для старта


После настройки проекта можно переходить непосредственно к разработке нашего приложения с использованием React и Redux.

Взаимодействие React и Redux на примере отображения значения счетчика

Для демонстрации примера работы библиотеки Redux в связке с React, создадим небольшое приложение, которое будет отображать в браузере значение счетчика. Так как кода в нашем проекте будет совсем немного, для наглядности примера мы не будем разбивать приложение на множество файлов (хотя в реальном проекте это будет обязательно), а объединим весь код в файле index.js.

Откроем файл index.js, созданный нами ранее и вместо вывода сообщения в консоль, подключим следующие пакеты:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers} from 'redux';
import {Provider, connect} from 'react-redux';
import PropTypes from 'prop-types';

Тут мы импортировали сам фреймворк «реакт», пакет react-dom для отображения реакт-компонентов в структуре DOM браузера, функции библиотеки Redux и пакет «react-redux», который и осуществляет взаимодействие «реакта» и «редукса».

Добавим код для установки значения счетчика:
let counter = 1;

const counterReducer = (state = null, action) => {
switch (action.type) {
case 'TEST':
return action.counter;
default:
return state;
}
};

const store = createStore(
combineReducers({counter: counterReducer}),
{counter}
);

Redux хранит все данные приложения в едином хранилище (store), который создается с помощью функции createStore. В нашем примере store хранит только одно поле counter. Для обновления значения в store нужно создать функцию, которая принимает в качестве параметров текущее значение state и действие action. Действие – это обычный javascript-объект, состоящий из типа действия (обязательного поля type) и произвольных полей, из которых потом можно вернуть новое значение для store. В нашем случае функция counterReducer возвращает новое значение счетчика из поля action.counter. В функции также производится проверка action.type, чтобы обрабатывать только указанный тип действий.

В качестве второго параметра в функцию createStore можно передать исходное состояние хранилища, мы таким образом можем установить начальное значение счетчика из переменной counter.

Подпишемся на обновление хранилища, чтобы выводить в консоль текущее состояние:
store.subscribe(() => console.log(store.getState()));

Далее создадим React-компонент для отображения счетчика:
@connect(
state => ({
counter: state.counter
}),
dispatch => ({})
)
class App extends React.Component {

static contextTypes = {
store: PropTypes.object
};

render() {
return (
<h1>Counter value: {this.props.counter}</h1>
);
}
}

Декоратор @connect, добавленный к компоненту, производит привязку значения из хранилища store к свойству компонента. Т.е значение поля counter из store теперь можно прочитать из this.props.

Далее нам остается отобразить компонент в браузере используя метод render из пакета «react-dom» и добавить тестовую функцию для обновления значения счетчика:
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('test-app')
);

setInterval(() => {
store.dispatch({
type: 'TEST',
counter: ++counter
});
}, 1000);

Компонент Provider из пакета «react-redux» добавляет store ко всем вложенным компонентам. Метод store.dispatch запускает механизм обновления хранилища, в качестве параметра он принимает объект действия, состоящий из типа действия и нового значения счетчика.

После запуска примера в браузере, можно увидеть, как значение счетчика каждую секунду увеличивается на 1. Каждое обновление состояния хранилища выводится в консоль.

  • Вывод счетчика в браузере

Полный код приложения:
import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers} from 'redux';
import {Provider, connect} from 'react-redux';
import PropTypes from 'prop-types';

let counter = 1;

const counterReducer = (state = null, action) => {
switch (action.type) {
case 'TEST':
return action.counter;
default:
return state;
}
};

const store = createStore(
combineReducers({counter: counterReducer}),
{counter}
);

store.subscribe(() => console.log(store.getState()));

@connect(
state => ({
counter: state.counter
}),
dispatch => ({})
)
class App extends React.Component {

static contextTypes = {
store: PropTypes.object
};

render() {
return (
<h1>Counter value: {this.props.counter}</h1>
);
}
}

ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('test-app')
);

setInterval(() => {
store.dispatch({
type: 'TEST',
counter: ++counter
});
}, 1000);

Надеюсь, этот простейший пример поможет вам разобраться с использованием связки React и Redux.



Как в React повесить DOM-событие на window или document?

Иногда внутри React-компонента требуется повесить DOM-событие на window или document, например, если нужно добавить обработчик события resize или scroll окна браузера.

Композиция с помощью HOC (Higher Order Component) в React

Компонент высшего порядка в React (Higher Order Component или сокращенно HOC) – это простая javascript-функция, которая принимает на вход один или несколько компонентов и возвращает другой компонент.

Сохранение состояния в React Component: особенности работы метода setState

Прежде всего давайте разберемся, что такое внутреннее состояние компонента в React и для чего оно может понадобиться. После этого поговорим об особенностях работы метода setState.