Алгоритм работы с React & Redux – с чего начать?
Тип Поста

Алгоритм работы с React & Redux – с чего начать?

Алгоритм работы с React & Redux – с чего начать?

Redux представляет собой контейнер предсказуемого состояния данных для JavaScript, что позволяет создавать надежные и легко расширяемые приложения, которые будут работать в различных окружениях исполнения.

В данной статье мы рассмотрим простой алгоритм подключения Redux к React-приложению, и последующей работы с ним.

Мы разберем каким образом подключить Redux к приложению, а также – где, когда и в какой последовательности использовать Actions, Reducers и Dispatchers.

Для простоты понимания, сначала мы произведем все необходимые действия в одном JavaScript файле, затем разобьём получившийся код на несколько файлов, т.е. создадим начальную структуру приложения. И в конце всего подключим полезный пакет react-redux, который позволит значительно упростить работу с Redux.

Итак, начнём!

Часть 1

1. Подготовка

В большинстве туториалах по использованию React & Redux предварительно создается довольно-таки большая структура приложения, с применением сборки приложения и различных дополнительных инструментов. Все это может создать (и создаст, поверьте) трудности для понимания работы Redux, принцип которой весьма прост. Чтобы избежать ненужных трудностей, мы будем использовать минимально необходимый набор инструментов для работы нашего приложения.

Стартовый шаблон приложения можно скачать по ссылки из репозитория проекта.

Разберем более подробно необходимый минимум для работы нашего приложения.

В файле package.json содержится несколько пакетов, которые обеспечат корректное функционирование приложения:

  • babel-preset-react – преобразует код JSX в обычный JS
  • babel-preset-es2015 – преобразует синтаксис ES6 в ES5
  • babelify & watchify – вспомогательные тулзы для наблюдения за файлами и работы babel
  • live-server – простой сервер для нашего приложения с автообновлением
  • concurrently – одновременный запуск нескольких npm команд

Ну и самое главное, в файле src/index.html мы просто подключим библиотеки React & Redux по ссылке – всё как в старые добрые времена 🙂

После того, как стартовый шаблон скачан из репозитория, необходимо выполнить установку пакетов командой npm install.

Добавим в файл src/app.js начальный код для проверки работоспособности нашего приложения:

const App = (props) => {
  return (
    <div className='app'>
      <h1>Привет, React & Redux!</h1>
    </div>
  )
};

ReactDOM.render(
  <App></App>,
  document.getElementById('root')
);

Запустим приложение командой npm start, и если все было установлено правильно, то в браузере появится приветствие, созданное с помощью React.

2. Создаем Store

Первым делом создадим хранилище, которое будет общим для всего приложения:

const store = Redux.createStore(Redux.combineReducers({
  posts
}));

Специально для целей объединения нескольких редукторов, мы используем метод Redux .combineReducers(), который произведет все необходимые действия.

3. Создаем Reducer

Затем создадим редуктор posts:

const posts = (state, action) => {
  switch(action.type) {
    case 'ADD_POST':
      let id = +new Date; // bad practice :)

      // Формируем новый массив объектов, в котором будут объединены все необходимые нам данные
      // Возвращаем новое состояние данных приложения в нужном нам формате
      return [...state, Object.assign({}, action.text, {id})];

    default:
      return state || [];
  }
};

Каждый редуктор принимает следующие параметры:

  1. state – состояние данных (posts) приложения в момент обращения к редуктору
  2. action – действие, при котором происходит обращение к редуктору

И возвращает или новое состояние данных, или состояние данных приложения по умолчанию.

4. Создаем Action Creators

Создадим Action Creators с именем addPost и действием ADD_POST:

const addPost = text => ({
  type: 'ADD_POST',
  text
});

Action Creators вернет объект с типом действия и любыми дополнительными данными, которые нам нужны.

5. Создаем дочерний компонент

Дочерний компонент нам понадобится для наглядной демонстрации Dispatchers и работы с действиями. Для начала изменим основной компонент, чтобы он смог вывести дочерний:

const App = (props) => {
  return (
    <div className='app'>
      {/* Вывод дочерних компонентов */}
      {props.children}
    </div>
  )
};

Затем создадим дочерний компонент, который, к примеру, будет отвечать за вывод постов:

class Content extends React.Component {
  render() {
    let {posts} = this.props; // destruction нужных нам свойств из объекта props

    return (
      <div className='content'>
        <h2>Все посты</h2>

        <ul>
          {posts.map((post, i) =>
            <li key={i}>{post.text}</li>
          )}
        </ul>

        <input ref='add' onKeyPress={this.createPost.bind(this)} type='text' />
      </div>
    )
  }

  createPost(event) {
    // Если это не клавиша enter, то прерываем работу
    if(event.which !== 13) return;

    let text = ReactDOM.findDOMNode(this.refs.add).value;
    this.props.addPost({text});
  }
}

6. Запускаем приложение

Данное решение является временным, в третьей части, когда мы подключим пакет react-redux, мы избавимся от функции run():

function run() {
  // Получаем данные, которые передадим в компонент Content
  let state = store.getState();

  ReactDOM.render(
    <App>
      <Content
        posts={state.posts}
        addPost={text => store.dispatch(addPost(text))}
      />
    </App>,
    document.getElementById('root')
  );
}

run();

// Подписываемся на изменения store, чтобы запускать run()
store.subscribe(run);

В данном случае, при каждом изменении store, будет запускаться run() функция и в компонент Content передадутся актуальные данные.

Сейчас наш дочерний компонент Content не имеет прямого доступа к store. Поэтому мы передаем в него свойство addPost, которое, по сути, является функций Dispatcher для инициализации Action Creator addPost. В самом же дочернем компоненте, мы вызываем этот Dispatcher в методе createPost():

this.props.addPost({text});

И передаем в него объект со свойством text, значение которого берется из текстового поля.

Последовательность такова:

Текстовое поле -> onKeyPress -> createPost -> this.props.addPost({text}) -> text => store.dispatch(addPost(text))

Код проекта на текущий момент можно посмотреть по ссылке: https://github.com/sbogdanov108/react-redux-algorithm/tree/01-Part-one.

Часть 2

7. Разбиваем приложение на файлы

Для начала уберем из index.html подключение React и Redux:

<!-- React -->
<script src="https://unpkg.com/react@15/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15/dist/react-dom.js"></script>

<!-- Redux -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.6.0/redux.js"></script>

Затем установим с помощью npm пакеты react, react-dom и redux:

npm i --save react react-dom redux

Создадим файл src/actions.js и поместим в него код Action Creators из файла app.js. Сейчас это только одна функция addPost():

export const addPost = text => ({
  ...
});

Теперь переместим редуктор в файл src/reducers.js:

export const posts = (state, action) => {
  switch(action.type) {
    ...
  }
};

Далее нам нужно поместить компоненты в разные файлы. Начнем с компонента App. Создадим файл src/components/App.js и поместим в него код нашего компонента:

import React from 'react';

const App = (props) => {
  ...
};

export default App;

И дочерний компонент переместим в файл src/components/Content.js:

import React from 'react';
import ReactDOM from 'react-dom';

class Content extends React.Component {
  ...
}

export default Content;

И конечно же не забудем подключить созданные файлы и нужные модули в файле src/app.js:

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers} from 'redux';

/*
 * Action creators
 * */
import {addPost} from './actions';

/*
* Reducers
* */
import * as reducers from './reducers';

/*
* Store
* */
const store = createStore(combineReducers(reducers));

/*
* Главный компонент
* */
import App from './components/App';

/*
* Дочерний компонент
* */
import Content from './components/Content';

// Далее без изменений...

Текущее состояние проекта можно посмотреть по ссылке: https://github.com/sbogdanov108/react-redux-algorithm/tree/02-Part-two.

Часть 3

8. Подключаем пакет react-redux

По своей сути, react-redux предоставляет удобную реализацию доступа компонентам к store и работы с actions.

Для начала установим этот пакет:

npm i --save react-redux

Теперь в файле src/app.js подключим этот пакет и обернем компонент нашего приложения специальным компонентом Provider, который предоставляется react-redux:

import React from 'react';
import ReactDOM from 'react-dom';
import {createStore, combineReducers} from 'redux';
import {Provider} from 'react-redux';

import * as reducers from './reducers';
import App from './components/App';
import Content from './components/Content';

const store = createStore(combineReducers(reducers));

ReactDOM.render(
  <Provider store={store}>
    <App>
      <Content />
    </App>
  </Provider>,
  document.getElementById('root')
);

В качестве параметра, компонент Provider принимает наше store.

Теперь дочерний компонент Content может получить прямой доступ к store.

9. Импортируем нужные модули

В файле src/component/Content.js внесем изменения:

import {connect} from 'react-redux';
import {addPost} from '../actions';

Метод connect() поможет определить, какие свойства и действия будут у нашего компонента, а также связать компонент и store.

Также мы импортировали те действия, которые относятся к нашему компоненту.

10. Определяем свойства и действия для компонента

// state destruction
const mapStateToProps = ({posts}) => ({
  posts
});

const mapDispatchToProps = (dispatch) => ({
  addPost: text => dispatch(addPost(text))
});

Функция mapStateToProps() определяет, какие свойства будут связаны между store и компонентом.

Функция mapDispatchToProps() определяет, какие действия будут доступны компоненту.

11. Связываем всё вместе

С помощью метода connect() связываем свойства и действия с нашим компонентом:

export default connect(mapStateToProps, mapDispatchToProps)(Content);

Метод connect() определяет, какие свойства и действия будут у нашего компонента.

Конечное состояние проекта можно посмотреть по ссылке: https://github.com/sbogdanov108/react-redux-algorithm.

12. Завершение

Итак, настало время подвести итог.

Краткий алгоритм работы с React & Redux будет следующим:

  • Cоздаем:
    1. Store
    2. Reducers
    3. Action Creators
  • Подключаем:
    1. Пакет react-redux
    2. Оборачиваем приложение компонентом Provider из этого пакета
    3. Передаем в Provider наше store
  • В компоненте, где нужен доступ к store подключаем:
    1. Метод connect() из пакета react-redux
    2. Созданные нами Action Creators
  • Создаем функции:
    1. mapStateToProps() – для свойств
    2. mapDispatchToProps() – для действий
  • Связываем:
    1. С помощью метода connect() наш компонент и созданные выше две функции

Готово!

При создании статьи были использованы следующие источники:

  1. React Docs
  2. Redux Docs
  3. react-redux Docs
Поделиться

6 комментариев

Оставить комментарий

Вы можете использовать следующие HTML-теги:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
Обязательно к заполнению