Что такое Redux ? Это менеджер состояний. Чаще всего его используют с React, но его возможности не ограничиваются одной этой библиотекой. Хотя в React есть собственный метод управления состояниями (почитать о нём можно в руководстве по React), он плохо масштабируется. Перемещение состояния вверх по дереву работает для простых приложений, но в более сложных архитектурах изменение состояния производится через свойства (props). Ещё лучше делать это через внешнее глобальное хранилище.
Библиотека Redux — это способ управления состоянием приложения. Она основана на нескольких концепциях, изучив которые, можно с лёгкостью решать проблемы с состоянием. Вы узнаете о них далее, в этом руководстве по Redux для начинающих.
Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.Содержание:
Когда нужно пользоваться Redux?
Redux идеально использовать в средних и крупных приложениях. Им стоит пользоваться только в случаях, когда невозможно управлять состоянием приложения с помощью стандартного менеджера состояний в React или любой другой библиотеке.
Простым приложениям Redux не нужен.
Использование Redux
Разберём основные концепции библиотеки Redux, которые нужно понимать начинающим.
Неизменяемое дерево состояний
В Redux общее состояние приложения представлено одним объектом JavaScript — state (состояние) или state tree (дерево состояний). Неизменяемое дерево состояний доступно только для чтения, изменить ничего напрямую нельзя. Изменения возможны только при отправке action (действия).
Действия
Действие (action) — это JavaScript-объект, который лаконично описывает суть изменения:
{
type: 'CLICKED_SIDEBAR'
}
// подробности об изменении
{
type: 'SELECTED_USER',
userId: 232
}
Единственное требование к объекту действия — это наличие свойства type
, значением которого обычно является строка.
Типы действий должны быть константами
В простом приложении тип действия задаётся строкой. По мере разрастания функциональности приложения лучше переходить на константы:
const ADD_ITEM = 'ADD_ITEM'
const action = { type: ADD_ITEM, title: 'Third item' }
и выносить действия в отдельные файлы. А затем их импортировать:
import { ADD_ITEM, REMOVE_ITEM } from './actions'
Генераторы действий
Генераторы действий (actions creators) — это функции, создающие действия.
function addItem(t) {
return {
type: ADD_ITEM,
title: t
}
}
Обычно инициируются вместе с функцией отправки действия:
dispatch(addItem('Milk'))
Или при определении этой функции:
const dispatchAddItem = i => dispatch(addItem(i))
dispatchAddItem('Milk')
Редукторы
При запуске действия обязательно что-то происходит и состояние приложения изменяется. Это работа редукторов.
Что такое редуктор
Редуктор (reducer) — это чистая функция, которая вычисляет следующее состояние дерева на основании его предыдущего состояния и применяемого действия.
(currentState, action) => newState
Чистая функция работает независимо от состояния программы и выдаёт выходное значение, принимая входное и не меняя ничего в нём и в остальной программе. Получается, что редуктор возвращает совершенно новый объект дерева состояний, которым заменяется предыдущий.
Чего не должен делать редуктор
Редуктор — это всегда чистая функция, поэтому он не должен:
- мутировать аргументы;
- мутировать состояние. Вместо этого создаётся новое состояние с помощью
Object.assign({}, ...)
; - иметь побочные эффекты (никаких API-вызовов с какими-либо изменениями);
- вызывать нечистые функции. Это функции, результат которых зависит от чего-то кроме их аргументов (например,
Date.now()
илиMath.random()
).
Поскольку состояние в сложных приложениях может сильно разрастаться, к каждому действию применяется не один, а сразу несколько редукторов.
Симулятор редуктора
Упрощённо базовую структуру Redux можно представить так:
Состояние
{
list: [
{ title: "First item" },
{ title: "Second item" },
],
title: 'Grocieries list'
}
Список действий
{ type: 'ADD_ITEM', title: 'Third item' }
{ type: 'REMOVE_ITEM', index: 1 }
{ type: 'CHANGE_LIST_TITLE', title: 'Road trip list' }
Редуктор для каждой части состояния
const title = (state = '', action) => {
if (action.type === 'CHANGE_LIST_TITLE') {
return action.title
} else {
return state
}
}
const list = (state = [], action) => {
switch (action.type) {
case 'ADD_ITEM':
return state.concat([{ title: action.title }])
case 'REMOVE_ITEM':
return state.map((item, index) =>
action.index === index
? { title: item.title }
: item
default:
return state
}
}
Редуктор для общего состояния
const listManager = (state = {}, action) => {
return {
title: title(state.title, action),
list: list(state.list, action),
}
}
Хранилище
Хранилище (store) — это объект, который:
- содержит состояние приложения;
- отображает состояние через
getState()
; - может обновлять состояние через
dispatch()
; - позволяет регистрироваться (или удаляться) в качестве слушателя изменения состояния через
subscribe()
.
Хранилище в приложении всегда уникально. Так создаётся хранилище для приложения listManager:
import { createStore } from 'redux'
import listManager from './reducers'
let store = createStore(listManager)
Хранилище можно инициировать через серверные данные:
let store = createStore(listManager, preexistingState)
Функции хранилища
Получение состояния:
store.getState()
Обновление состояния:
store.dispatch(addItem('Something'))
Прослушивание изменений состояния:
const unsubscribe = store.subscribe(() =>
const newState = store.getState()
)
unsubscribe()
Поток данных
Поток данных в Redux всегда однонаправлен.
Передача действий с потоками данных происходит через вызов метода dispatch()
в хранилище. Само хранилище передаёт действия редуктору и генерирует следующее состояние, а затем обновляет состояние и уведомляет об этом всех слушателей.
***
Советуем начинающим в Redux прочитать нашу статью о других способах передачи данных.
Перевод статьи «A quick guide to Redux for beginners»
Ольга Сайфудинова