Поток данных (Data Flow)
Архитектура Redux вращается вокруг строго однонаправленного потока данных.
Это значит, что все данные в приложении следуют одному паттерну жизненного цикла, делая логику вашего приложения более предсказуемой и легкой для понимания. Также это способствует большей упорядоченности данных (data normalization), так что в конечном итоге у вас не будет нескольких изолированных копий одних и тех же данных, которые ничего не знают друг о друге.
Если вы до сих пор не убеждены, прочтите Мотивацию и The Case for Flux для ознакомления с убедительными аргументами в пользу однонаправленного потока данных. Хотя Redux — это не совсем Flux, он дает такие же основные преимущества.
Жизненный цикл данных в любом Redux-приложении включает в себя 4 шага:
Вы вызываете
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-колбеки или даже с запланированными интервалами.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 или маршрутизация по приложению. Все это должно происходить только после того, как экшен будет совершен.
Главный редюсер может комбинировать результат работы нескольких редюсеров в единственное дерево состояния приложения.
Каким образом вы будете структурировать главный редюсер, зависит только от Вас. 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()
— это просто удобная утилита, вы совершено не обязаны ее использовать. Вы можете написать главный редюсер самостоятельно!
Redux-стор сохраняет полное дерево состояния, которое возвращает главный редюсер.
Это новое дерево является следующим состоянием Вашего приложения! Каждый слушатель, зарегистрированный с помощью
store.subscribe(listener)
, будет вызван. Слушатели могут вызыватьstore.getState()
для получения текущего состояния приложения.Теперь UI может быть обновлен для отражения нового состояния приложения. Если вы используете такие биндинги (bindings), как React Redux, то это та точка, в которой стоит вызвать
component.setState(newState)
Следующие шаги
Теперь, когда вы знаете, как работает Redux, давайте свяжем его с React приложением.
Заметка для опытных пользователей
Если Вы уже знакомы с основными концепциями и уже освоили это обучающее руководство, то не забудьте посетить асинхронный поток (async flow) в руководстве для опытных для изучения того, как именно мидлвары изменяют асинхронные экшены прежде чем они достигнут редюсера.