Перейти к содержанию

Поток данных

Архитектура Redux вращается вокруг строго однонаправленного потока данных (Data Flow).

Это значит, что все данные в приложении следуют одному паттерну жизненного цикла, делая логику вашего приложения более предсказуемой и легкой для понимания. Также это способствует большей упорядоченности данных (data normalization), так что в конечном итоге у вас не будет нескольких изолированных копий одних и тех же данных, которые ничего не знают друг о друге.

Если вы до сих пор не убеждены, прочтите The Case for Flux для ознакомления с убедительными аргументами в пользу однонаправленного потока данных. Хотя Redux — это не совсем Flux, он дает такие же основные преимущества.

Жизненный цикл данных в любом Redux-приложении включает в себя 4 шага:

1. Вы вызываете store.dispatch(action)

Экшен — это простой javascript-объект, который описывает что случилось. Например:

{ type: 'LIKE_ARTICLE', articleId: 42 }
{ type: 'FETCH_USER_SUCCESS', response: { id: 3, name: 'Mary' } }
{ type: 'ADD_TODO', text: 'Read the Redux docs.' }

Думайте о экшене, как об очень коротком фрагменте новостей. "Мэри залайкала статью 42" или "«Прочитать документацию Redux» было добавлено в todo-список".

Вы можете вызвать store.dispatch(action) из любого места Вашего приложения, включая компоненты и XHR-колбеки или даже с запланированными интервалами.

2. Redux-стор вызывает функцию-редьюсер, который вы ему передали

Стор передаст два аргумента при вызове редьюсера: текущее дерево состояния (current state tree) и экшен (action). Например, в todo-приложении главный редьюсер может принимать что-то такое:

// Текущее состояние приложения (список дел и выбранный фильтр)
let previousState = {
  visibleTodoFilter: 'SHOW_ALL',
  todos: [
    {
      text: 'Read the docs.',
      complete: false,
    },
  ],
};

// Выполнение экшена (добавление дела)
let action = {
  type: 'ADD_TODO',
  text: 'Understand the flow.',
};

// Ваш редьюсер возвращает следующее состояние приложения
let nextState = todoApp(previousState, action);

Обратите внимание на то, что редьюсер — это чистая функция. Он только вычисляет следующее состояние. Он должен быть совершенно предсказуемым: тип возвращаемых данных не должен меняться, если на вход подаются данные одного типа. Он не должен совершать никаких сайд-эффектов, таких как обращение к API или маршрутизация по приложению. Все это должно происходить только после того, как экшен будет совершен.

3. Главный редьюсер может комбинировать результат работы нескольких редьюсеров в единственное дерево состояния приложения

Каким образом вы будете структурировать главный редьюсер, зависит только от Вас. Redux поставляется с хелпером combineReducers(), полезным для "разделения" главного редьюсера на отдельные функции, которые управляют отдельными ветвями дерева состояния.

combineReducers() работает следующим образом. Допустим, у вас есть два редьюсера: один для списка todo-дел, второй — для выбранного сейчас режима отображения этого списка:

function todos(state = [], action) {
  // как-то вычисляет nextState...
  return nextState;
}

function visibleTodoFilter(state = 'SHOW_ALL', action) {
  // как-то вычисляет nextState...
  return nextState;
}

let todoApp = combineReducers({
  todos,
  visibleTodoFilter,
});

Когда вы инициируете экшен, todoApp, которое вернул combineReducers, вызовет оба редьюсера:

let nextTodos = todos(state.todos, action);
let nextVisibleTodoFilter = visibleTodoFilter(
  state.visibleTodoFilter,
  action
);

Затем оба набора состояний будут снова собраны в единое состояние:

return {
  todos: nextTodos,
  visibleTodoFilter: nextVisibleTodoFilter,
};

Так как combineReducers() — это просто удобная утилита, вы совершено не обязаны ее использовать. Вы можете написать главный редьюсер самостоятельно!

4. Redux-стор сохраняет полное дерево состояния, которое возвращает главный редьюсер

Это новое дерево является следующим состоянием Вашего приложения! Каждый слушатель, зарегистрированный с помощью store.subscribe(listener), будет вызван. Слушатели могут вызывать store.getState() для получения текущего состояния приложения.

Теперь UI может быть обновлен для отражения нового состояния приложения. Если вы используете такие биндинги (bindings), как React Redux, то это та точка, в которой стоит вызвать component.setState(newState)

Следующие шаги

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

Заметка для опытных пользователей

Если Вы уже знакомы с основными концепциями и уже освоили это обучающее руководство, то не забудьте посетить асинхронный поток (async flow) в руководстве для опытных для изучения того, как именно мидлвары изменяют асинхронные экшены прежде чем они достигнут редьюсера.