() translation by (you can also view the original English article)
Когда вы изучаете React, вы почти всегда будете слышать, как люди говорят, насколько велик Redux, и что вы должны его попробовать. Экосистема React растет быстрыми темпами, и существует так много библиотек, которые вы можете подключить с помощью React, таких как flow, redux, middlewares, mobx и т. д.
В React легко обучаться, но привыкание к всей экосистеме React требует времени. Этот учебник представляет собой введение в один из неотъемлемых компонентов экосистемы React-Redux.
Основная терминология Non-Redux
Вот некоторые из наиболее часто используемых терминов, с которыми вы, возможно, не знакомы, но они не являются специфическими для Redux как таковой. Вы можете пропустить этот раздел и вернуться сюда, когда/если это не имеет смысла.
Чистая функция
Чистая функция - это просто нормальная функция с двумя дополнительными ограничениями, которые она должна удовлетворять:
- Учитывая набор входов, функция всегда должна возвращать тот же результат.
- Он не вызывает побочных эффектов.
Например, вот чистая функция, которая возвращает сумму двух чисел.
1 |
/* Pure add function */
|
2 |
const add = (x,y) => { |
3 |
return x+y; |
4 |
}
|
5 |
|
6 |
console.log(add(2,3)) //5 |
7 |
Чистые функции дают прогнозируемый результат и детерминированы. Функция становится смешанной, когда она выполняет что-либо иное, кроме вычисления возвращаемого значения.
Например, функция добавления ниже использует глобальное состояние для вычисления его вывода. Кроме того, функция также записывает значение в консоль, что считается побочным эффектом.
1 |
const y = 10; |
2 |
|
3 |
const impureAdd = (x) => { |
4 |
console.log(`The inputs are ${x} and ${y}`); |
5 |
return x+y; |
6 |
}
|
Наблюдаемые побочные эффекты
«Наблюдаемые побочные эффекты» - это причудливый термин для взаимодействия, выполняемого функцией с внешним миром. Если функция пытается записать значение в переменную, которая существует вне функции или пытается вызвать внешний метод, вы можете смело называть это побочными эффектами.
Однако, если чистая функция вызывает другую чистую функцию, тогда эту функцию можно рассматривать как чистую. Вот некоторые из общих побочных эффектов:
- вызов API
- запись на консоль или печать данных
- мутирующие данные
- DOM-манипуляция
- получение текущего времени
Контейнерные и презентационные компоненты
Разделение архитектуры компонента на два полезно при работе с приложениями React. Вы можете классифицировать их по двум категориям: компоненты контейнера и презентационные компоненты. Они также широко известны как умные и немые компоненты.
Контейнерный компонент связан с тем, как все работает, в то время как компоненты представления касаются того, как выглядят вещи. Чтобы лучше понять концепции, я рассмотрел это в другом учебнике: «Контейнер против презентационных компонентов в реактиве».
Отменяемые и неизменяемые объекты
Изменчивый объект можно определить следующим образом:
Измененный объект - это объект, состояние которого может быть изменено после его создания.
Неизменность - это полная противоположность - неизменный объект - объект, состояние которого не может быть изменено после его создания. В JavaScript строки и номера неизменяемы, но объектов и массивов нет. Этот пример демонстрирует разницу лучше.
1 |
/*Strings and numbers are immutable */
|
2 |
|
3 |
let a = 10; |
4 |
|
5 |
let b = a; |
6 |
|
7 |
b = 3; |
8 |
|
9 |
console.log(`a = ${a} and b = ${b} `); //a = 10 and b = 3 |
10 |
|
11 |
/* But objects and arrays are not */
|
12 |
|
13 |
/*Let's start with objects */
|
14 |
|
15 |
let user = { |
16 |
name: "Bob", |
17 |
age: 22, |
18 |
job: "None" |
19 |
}
|
20 |
|
21 |
active_user = user; |
22 |
|
23 |
active_user.name = "Tim"; |
24 |
|
25 |
//Both the objects have the same value
|
26 |
console.log(active_user); // {"name":"Tim","age":22,"job":"None"} |
27 |
|
28 |
console.log(user); // {"name":"Tim","age":22,"job":"None"} |
29 |
|
30 |
/* Now for arrays */
|
31 |
|
32 |
let usersId = [1,2,3,4,5] |
33 |
|
34 |
let usersIdDup = usersId; |
35 |
|
36 |
usersIdDup.pop(); |
37 |
|
38 |
console.log(usersIdDup); //[1,2,3,4] |
39 |
console.log(usersId); //[1,2,3,4] |
Чтобы сделать объекты неизменяемыми, используйте метод Object.assign
для создания нового метода или нового распространенного оператора .
1 |
let user = { |
2 |
name: "Bob", |
3 |
age: 22, |
4 |
job: "None" |
5 |
}
|
6 |
|
7 |
active_user = Object.assign({}, user, {name:"Tim"}) |
8 |
|
9 |
console.log(user); //{"name":"Bob","age":22,"job":"None"} |
10 |
console.log(active_user); //{"name":"Tim","age":22,"job":"None"} |
Почему Redux?
Официальная страница определяет Redux следующим образом:
Redux является предсказуемым контейнером состояний для приложений JavaScript.
Хотя это точно описывает Redux, все ровно легко потеряться, когда вы видите большую картину Redux в первый раз. У этого есть так много движущихся частей, которые вам нужно совместить. Но как только вы это сделаете, я обещаю вам, вы полюбите Redux.
Redux - это библиотека управления состоянием, которую вы можете подключить к любой библиотеке JavaScript, а не только для реактивов. Тем не менее, он отлично работает с React из-за функционального характера React. Чтобы понять это лучше, давайте посмотрим на состояние.



Как вы можете видеть, состояние компонента определяет, что получает рендеринг и как оно ведет себя. Приложение имеет начальное состояние, и любое пользовательское взаимодействие запускает действие, которое обновляет состояние. Когда состояние обновляется, страница перезагружается.
С помощью React каждый компонент имеет локальное состояние, доступное изнутри компонента, или вы можете передать их как реквизиты дочерним компонентам. Обычно мы используем состояние для хранения:
- Состояние пользовательского интерфейса и переходные данные. Это включает в себя список элементов пользовательского интерфейса для меню навигации или ввода формы в управляемом компоненте.
- Состояние приложения, такое как данные, полученные с сервера, состояние входа пользователя и т. д.
Сохранение данных приложения в состоянии компонента в порядке, если у вас есть базовое приложение React с несколькими компонентами.



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



Почему вы должны использовать Redux?
Вот с чем вы можете встретиться работая с React.
- Вы создаете приложение среднего размера, и ваши компоненты аккуратно разделяются на интеллектуальные и немые компоненты.
- Интеллектуальные компоненты обрабатывают состояние, а затем передают их на немые компоненты. Они заботятся о вызове API, извлечении данных из источника данных, обработке данных и настройке состояния. Немые компоненты получают реквизит и возвращают представление пользовательского интерфейса.
- Когда вы собираетесь написать новый компонент, не всегда ясно, где разместить состояние. Вы можете позволить государству быть частью контейнера, который является непосредственным родителем этого компонента. Еще лучше, вы можете переместить состояние выше в иерархии, чтобы состояние было доступно для нескольких презентационных компонентов.
- Когда приложение растет, вы видите, что состояние разбросано по всему месту. Когда компонент должен получить доступ к состоянию, к которому у него сразу нет доступа, вы попытаетесь поднять состояние до ближайшего предка компонента.
- После постоянного рефакторинга и очистки вы оказываетесь в большинстве штатных мест в верхней части иерархии компонентов.
- Наконец, вы решаете, что неплохо позволить компоненту в верхнем дескрипторе состояние глобально, а затем передать все. Каждый другой компонент может подписаться на реквизит, который им нужен, и игнорировать остальные.
Это то, что я лично испытал с React, и многие другие разработчики согласятся. React- это библиотека представлений, и дело не в React чтобы управлять состоянием. Мы ищем принцип разделения идей.
Redux помогает вам отделить состояние приложения от React. Redux создает глобальный магазин, который находится на верхнем уровне вашего приложения и передает состояние всем остальным компонентам. В отличие от Flux, Redux не имеет нескольких объектов хранилища. Все состояние приложения находится внутри этого объекта-хранилища, и вы могли бы поменять слой вида на другую библиотеку с неповрежденным хранилищем.
Компоненты повторно отображаются каждый раз, когда хранилище обновляется, что очень мало влияет на производительность. Это хорошая новость, и это приносит массу преимуществ вместе с этим. Вы можете относиться ко всем своим компонентам React как к немым, и React может просто сосредоточиться на стороне вещей.
Теперь, когда мы знаем, почему Redux полезен, давайте погрузимся в архитектуру Redux.
Redux Архитектура
Когда вы изучаете Redux, есть несколько основных понятий, к которым вам нужно привыкнуть. На приведенном ниже рисунке описывается архитектура Redux и все составные вместе.



Если вы привыкли к Flux, некоторые элементы могут выглядеть знакомыми. Если нет, это тоже хорошо, потому что мы собираемся покрыть все с базы. Во-первых, убедитесь, что у вас установлено сокращение:
1 |
npm install redux
|
Для настройки сервера разработки используйте приложение create-react-app или вашу любимую конфигурацию webpack. Поскольку Redux является независимым государственным управлением, мы еще не собираемся подключаться к React. Поэтому удалите содержимое index.js, и мы будем играть с Redux для остальной части этого урока.
Хранилище
В хранилище находится один большой объект JavaScript, который содержит несколько пар ключ-значение, которые представляют текущее состояние приложения. В отличие от объекта State в React, который разбрызгивается через разные компоненты, у нас есть только один магазин. Хранилище обеспечивает состояние приложения, и каждый раз, когда состояние обновляется, просмотр rerenders.
Однако вы никогда не сможете изменить или изменить хранилище. Вместо этого вы создаете новые версии хранилища.
1 |
(previousState, action) => newState |
Из-за этого вы можете путешествовать во времени во всех штатах с момента загрузки приложения в свой браузер.
В хранилище есть три способа связи с остальной архитектурой. Вот они:
- Store.getState() - для доступа к текущему состоянию вашего приложения.
- Store.dispatch(действие) - запуск изменения состояния на основе действия. Подробнее о действиях ниже.
- Store.subscribe(слушатель). Чтобы прослушать любое изменение состояния. Он будет вызываться каждый раз, когда действие отправляется.
Давайте создадим хранилище. Redux имеет метод createStore
для создания нового хранилища. Вам нужно передать ему редуктор, хотя мы не знаем, что это такое. Поэтому я просто создаю функцию, называемую редуктором. Вы можете указать второй аргумент, задающий начальное состояние хранилища.
src/index.js
1 |
import { createStore } from "redux"; |
2 |
// This is the reducer
|
3 |
const reducer = () => { |
4 |
/*Something goes here */
|
5 |
}
|
6 |
|
7 |
//initialState is optional.
|
8 |
//For this demo, I am using a counter, but usually state is an object
|
9 |
const initialState = 0 |
10 |
const store = createStore(reducer, initialState); |
Теперь мы будем слышать любые изменения в хранилище, а затем console.log()
текущее состояние хранилища.
1 |
store.subscribe( () => { |
2 |
console.log("State has changed" + store.getState()); |
3 |
})
|
Итак, как мы обновляем хранилище? В Redux есть что-то, называемое действиями, которые делают это возможным.
Создатели Action / Action
Действия также представляют собой обычные объекты JavaScript, которые отправляют информацию из вашего приложения в хранилище. Если у вас очень простой счетчик с кнопкой инкремента, нажатие на него приведет к срабатыванию действия, которое выглядит следующим образом:
1 |
{
|
2 |
type: "INCREMENT", |
3 |
payload: 1 |
4 |
}
|
Они являются единственным источником информации в магазине. Состояние хранилища изменяется только в ответ на действие. Каждое действие должно иметь свойство типа, которое описывает, что намеревается сделать объект действия. Помимо этого, структура действия полностью зависит от вас. Однако сохраните свое действие маленьким, потому что действие представляет собой минимальный объем информации, необходимой для преобразования состояния приложения.
Например, в приведенном выше примере свойство type установлено в «INCREMENT», а также добавлено дополнительное свойство полезной нагрузки. Вы можете переименовать свойство полезной нагрузки в нечто более значимое или, в нашем случае, опустить его полностью. Вы можете отправить действие в хранилище, как это.
1 |
store.dispatch({type: "INCREMENT", payload: 1}); |
При кодировании Redux вы обычно не будете использовать действия напрямую. Вместо этого вы будете вызывать функции, возвращающие действия, и эти функции широко известны как создатели действий. Вот создатель действия для действия приращения, о котором мы говорили ранее.
1 |
const incrementCount = (count) => { |
2 |
return { |
3 |
type: "INCREMENT", |
4 |
payload: count |
5 |
}
|
6 |
}
|
Итак, чтобы обновить состояние счетчика, вам нужно будет отправить действие incrementCount
следующим образом:
1 |
store.dispatch(incrementCount(1)); |
2 |
store.dispatch(incrementCount(1)); |
3 |
store.dispatch(incrementCount(1)); |
Если вы направляетесь в консоль браузера, вы увидите, что он работает, частично. Мы получаем неопределенность, потому что мы еще не определили редуктор.



Итак, теперь мы рассмотрели действия и магазин. Однако нам нужен механизм для преобразования информации, предоставляемой действием, и преобразования состояния хранилища. Редукторы служат для этой цели.
Переходники
Действие описывает проблему, и редуктор отвечает за решение проблемы. В предыдущем примере метод incrementCount
возвратил действие, которое предоставило информацию о типе изменения, который мы хотели бы сделать в строении. Редуктор использует эту информацию для фактического обновления состояния. В документах есть большая точка, которую вы всегда должны помнить при использовании Redux:
Учитывая те же аргументы, редуктор должен вычислить следующее состояние и вернуть его. Без сюрпризов. Никаких побочных эффектов. Нет вызовов API. Никаких мутаций. Просто расчет.
Это означает, что редуктор должен быть чистой функцией. Учитывая набор входов, он всегда должен возвращать тот же результат. Помимо этого, он не должен делать ничего больше. Кроме того, редуктор не является местом для побочных эффектов, таких как вызов AJAX или выборка данных из API.
Давайте заполним редуктор для нашего счетчика.
1 |
// This is the reducer
|
2 |
|
3 |
const reducer = (state = initialState, action) => { |
4 |
switch (action.type) { |
5 |
case "INCREMENT": |
6 |
return state + action.payload |
7 |
default: |
8 |
return state |
9 |
}
|
10 |
}
|
Редуктор принимает два аргумента - состояние и действие - и возвращает новое состояние.
1 |
(previousState, action) => newState |
Состояние принимает значение по умолчанию, initialState
, которое будет использоваться, только если значение состояния не определено. В противном случае фактическое значение состояния будет сохранено. Мы используем оператор switch для выбора правильного действия. Обновите браузер, и все будет работать так, как ожидалось.
Давайте добавим дело для DECREMENT
, без которого счетчик будет неполным.
1 |
// This is the reducer
|
2 |
|
3 |
const reducer = (state = initialState, action) => { |
4 |
switch (action.type) { |
5 |
case "INCREMENT": |
6 |
return state + action.payload |
7 |
case "DECREMENT": |
8 |
return state - action.payload |
9 |
default: |
10 |
return state |
11 |
}
|
12 |
}
|
Вот создатель действия.
1 |
const decrementCount = (count) => { |
2 |
return { |
3 |
type: "DECREMENT", |
4 |
payload: count |
5 |
}
|
6 |
}
|
Наконец, отправьте его в хранилище.
1 |
store.dispatch(incrementCount(4)); //4 |
2 |
store.dispatch(decrementCount(2)); //2 |
Все правильно!
Подведем итоги
Этот учебник должен был стать отправной точкой для управления состоянием с помощью Redux. Мы рассмотрели все необходимое, чтобы понять основные концепции Redux, такие как хранилище, действия и редукторы. К концу обучения мы также создали рабочий демонстрационный demo счетчик. Хотя это было не так уж много информации, но мы узнали, как все части головоломки подходят друг другу.
За последние пару лет React стал популярным. Фактически, у нас есть ряд предметов на рынке, доступных для покупки, просмотра, реализации и т. д. Если вы ищете дополнительные ресурсы вокруг React, не стесняйтесь их проверять.
В следующем уроке мы будем использовать то, что мы узнали здесь, для создания приложения React с использованием Redux. Оставайтесь с нами. Поделитесь своими мыслями в комментариях.