Всем привет! Камол здесь. Эта статья — мой взгляд на библиотеку, которую я использую каждый день: Redux. Последние несколько лет я работаю в экосистеме ReactJS. Я недавно (в прошлом году) взял Redux. До Redux я пробовал несколько реализаций потока, но они не совсем то, что я хочу от слоя данных. Либо потому, что он не делает то, что мне нужно, либо требует слишком много изменений в текущей кодовой базе. Redux — это отдельная история. Он отлично работает на нескольких крупных проектах в моей нынешней компании. В Redux много крутых идей: однонаправленный поток данных, один источник правды и неизменяемость среди прочего. Они значительно упрощают мой рабочий процесс. Теперь так легко отлаживать код. И у него есть универсальная система промежуточного программного обеспечения. Выбор Redux был правильным шагом.

Тем не менее, Redux не является универсальным решением. Это государственная библиотека управления. Очень самоуверенный, я бы сказал. При разработке магазина Redux следует помнить о нескольких вещах:

  1. Redux не является частью React
  2. Несколько магазинов против одного магазина с несколькими редьюсерами
  3. Действия являются чистыми функциями и должны возвращать объект
  4. Отправить
  5. Использование реакции-редукции

Редукс! == Реагировать

Я думаю, само собой разумеется, что React и Redux — это две разные вещи. Redux не зависит от библиотеки React, и наоборот. React позаботится о логике представления, а Redux позаботится о логике данных. Redux можно интегрировать в любое внешнее приложение с помощью React, Angular или Backbone. Вы даже можете использовать Redux в чистом проекте JavaScript без фреймворка.

Редукторы

То, что большинству людей нужно осознать, когда они впервые начинают использовать Redux, — это концепция редюсера. Redux хранит ВСЕ данные о состоянии в одном большом объекте. Некоторые могут называть его «деревом состояний». Но в большинстве случаев вы не меняете все в дереве. Вот почему вам нужно «свести» его к подмножеству дерева, с которым вы хотите работать. Это способ структурировать состояние в приложении.

На практике редуктор — это функция с двумя аргументами, которая всегда возвращает объект:

const defaultState = {};
function myReducer(state = defaultState, action) {
  switch(action.type) {
    default:
      return state;
  }
}

Эта функция используется в первый раз для инициализации этой части дерева состояний. В настоящее время этот редьюсер ничего не делает, кроме передачи текущего состояния. Теперь предположим, что у нас есть значение типа string.

const CHANGE_MY_VALUE = 'CHANGE_MY_VALUE';
const defaultState = {
  myValue: ''
};
function myReducer(state = defaultState, action) {
  switch(action.type) {
    case CHANGE_MY_VALUE:
      return { myValue: action.newValue };
    default:
      return state;
  }
}

Теперь наш редьюсер готов принять новое действие типа CHANGE_MY_VALUE.

Действия

Существует только один единственный экземпляр, который содержит все данные о состоянии. И вы меняете любую часть информации, отправляя действие в магазин. Действие — это функция, которая возвращает объект. Возвращаемый объект всегда должен содержать тип. Redux использует «тип», чтобы выяснить, что делать с состоянием.

function changeMyValue(newValue) {
  return {
    type: CHANGE_MY_VALUE,
    newValue: newValue
  };
}

Дело в том, что если вы попытаетесь выполнить эту функцию сейчас, она ничего не сделает с магазином Redux. Потому что для того, чтобы уведомить Redux о нашем «намерении», нам нужно использовать один из API Redux. Вам нужна еще одна функция под названием Dispatch.

Отправлять

Давайте сложим все кусочки вместе. Во-первых, нам нужен магазин

import { createStore } from 'redux';
import { myReducer } from './myReducer';
const TheStore = createStore(myReducer);

TheStore — это новый экземпляр магазина Redux. Он имеет одно поле «myValue». Он также имеет две функции, которые вы можете использовать:

  • getState() — возвращает все текущее состояние магазина
  • dispatch() — способ связи с магазином

Чтобы изменить данные внутри хранилища, вы «отправляете» действие, вызывая

import { changeMyValue } from './changeMyValue';
console.log(TheStore.getState().myValue); // empty string
TheStore.dispatch(changeMyValue('alkazar!'));
console.log(TheStore.getState().myValue); // alkazar!

Использование реакции-редукции

Мы прошли базовую механику магазина Redux. Теперь вы можете с удовольствием использовать Redux с ванильным JavaScript. Однако использование Redux с существующим фреймворком довольно многословно. Итак, хорошие люди в сообществе Redux пишут «адаптеры» для вас. В большинстве популярных фреймворков этот адаптер уже есть. Просто выполните поиск через регистратор npm. Теперь, поскольку я большой поклонник React и работаю с ним недавно, пример с React кажется логичным выбором ;)

import React from 'react';
const MyReactView = React.createClass({
  propTypes: {
    myValue: React.PropTypes.string.isRequired
  },
  onClick() {
    this.props.changeMyValue('!razakla');
  },
  render() {
    return (<div>
      <span>myValue:{this.props.myValue}</span>
      <button onClick={this.onClick}>Change</button>
    </div>);
  }
});

Теперь, чтобы подключить Redux

import { connect } from 'react-redux';
import MyReactView from './MyReactView.jsx';
import { changeMyValue } from './changeMyValue';
function mapStateToProps(getState) {
  const { myValue } = getState();
  return { myValue };
}
function mapDispatchToProps(dispatch) {
  return {
    changeMyValue: function(newValue) {
      dispatch(changeMyValue(newValue));
    }
  };
}
export default connect(mapStateToProps, mapDispatchToProps)(MyReactView);

Connect() — это функция более высокого порядка. Функция высшего порядка — это функция, которая возвращает новую функцию. Думайте об этом как о способе реализации фабричного шаблона. Connect() принимает два аргумента: mapStateToProps и MapDispatchToProps.

  • MapStateToProps — это способ чтения данных из хранилища Redux. Он предоставляет одну функцию getState() для получения текущего состояния из хранилища. все, что возвращается из этой функции, будет объединено с реквизитами компонента React, с которым вы вызываете connect(). В этом примере вы получите this.props.myValue
  • MapDispatchToProps — это способ сопоставить действие хранилища с реквизитами компонента React, с которым вы вызываете connect(). В этом примере вы получите this.props.changeMyValue()

Резюме

Этого простого примера должно быть более чем достаточно, чтобы начать работу с Redux. В мире Redux есть еще кое-что. Вы можете добавить промежуточное ПО, чтобы выполнить действие более высокого порядка с помощью redux-thunk. Вы можете выходить из дерева состояний для консоли каждый раз, когда отправляется действие, и многое другое в соответствии с вашими потребностями.

В настоящее время в большом, большом мире JavaScript существует слишком много библиотек. Пройти их все невозможно. Но уверяю вас, приобретение Redux было одним из самых стоящих решений, которые я принял в этом году. Помимо кристально чистого API, Redux имеет очень активное сообщество разработчиков, поддерживающее его. Многие живые проекты сегодня с большим успехом используют Redux. Вы просто не ошибетесь с Redux.

Если вам нужна помощь в интеграции Redux или React в ваш проект, я буду рад пообщаться. Напишите мне электронную почту, и я свяжусь с вами.