1. Введение в Redux: Архитектура Flux, Store, Actions и чистые функции Reducers
Введение в Redux: Архитектура Flux, Store, Actions и чистые функции Reducers
Добро пожаловать в курс «Освоение Redux». Если вы когда-либо создавали сложные приложения на JavaScript (особенно на React), вы, вероятно, сталкивались с проблемой управления состоянием. Данные разбросаны по разным компонентам, передача пропсов на десять уровней вниз превращается в кошмар, а отслеживание того, кто и когда изменил данные, становится невозможным. Именно здесь на сцену выходит Redux.
В этой первой статье мы разберем фундаментальные концепции Redux, не привязываясь к конкретному UI-фреймворку. Мы поймем, откуда растут корни этой библиотеки, изучив архитектуру Flux, и детально разберем три кита Redux: Store, Actions и Reducers.
Проблема управления состоянием
Представьте, что ваше приложение — это сложная система водопровода. Вода (данные) должна поступать в разные части дома (компоненты). В простых приложениях вы можете просто носить воду ведрами (props). Но когда дом превращается в небоскреб, бегать с ведрами становится неэффективно и опасно — вода проливается, ведра теряются, и никто не знает, сколько воды осталось в главном резервуаре.
Redux предлагает установить централизованную насосную станцию и четкую систему труб, где вода течет только в одном направлении. Это делает поведение системы предсказуемым.
Архитектура Flux: Прародитель Redux
Чтобы понять Redux, нужно взглянуть на его предшественника — архитектуру Flux, разработанную Facebook. Flux — это не библиотека, а паттерн проектирования, созданный для решения проблем двунаправленного потока данных (как в MVC), который часто приводил к каскадным обновлениям и непредсказуемым багам.
Ключевая идея Flux — однонаправленный поток данных (Unidirectional Data Flow).
!Схема однонаправленного потока данных в архитектуре Flux
Основные компоненты Flux:
Redux развил эту идею, упростив её. В отличие от Flux, где может быть много Stores и сложный Dispatcher, Redux использует один Store и полагается на чистые функции для изменения состояния.
Три принципа Redux
Redux строится на трех фундаментальных принципах:
Давайте разберем каждый элемент подробнее.
Store: Сердце приложения
Store (Хранилище) — это объект, который держит состояние приложения. В Redux есть только один Store. Это как банковское хранилище: все деньги (данные) лежат там, и вы не можете просто зайти и взять их, не пройдя через кассира.
Store выполняет следующие задачи:
* Хранит состояние приложения.
* Предоставляет доступ к состоянию через метод getState().
* Позволяет обновлять состояние через метод dispatch(action).
* Позволяет подписываться на изменения через метод subscribe(listener).
Actions: Намерение изменить данные
Action (Действие) — это обычный JavaScript-объект, который описывает, что произошло. Это единственный источник информации для Store. Actions — это как квитанция, которую вы передаете кассиру в банке: «Я хочу положить 100 рублей».
Обязательное требование к Action — наличие поля type. Оно обычно задается строковой константой.
Пример Action:
Здесь type говорит о типе события, а payload (полезная нагрузка) содержит дополнительные данные, необходимые для выполнения действия. Поле payload не является обязательным стандартом Redux, но является общепринятым соглашением (FSA — Flux Standard Action).
Reducers: Логика изменений
Если Action описывает, что произошло, то Reducer (Редьюсер) описывает, как меняется состояние приложения в ответ на это действие.
Технически, Reducer — это чистая функция, которая принимает текущее состояние и действие, а возвращает новое состояние.
Математически это можно выразить следующей формулой:
Где — новое состояние приложения, — функция-редьюсер, — текущее состояние, а — действие (Action).
Что такое «Чистая функция»?
Понимание чистых функций критически важно для работы с Redux. Функция считается чистой, если она соблюдает два правила:
!Сравнение чистой функции и функции с побочными эффектами
Иммутабельность (Неизменяемость)
В Redux запрещено изменять состояние напрямую. Вы никогда не должны писать что-то вроде state.value = 5. Вместо этого редьюсер должен вернуть новый объект состояния, который содержит изменения.
Пример простейшего редьюсера:
Обратите внимание на использование оператора расширения (spread operator) ...state. Это распространенный способ создания копии объекта в JavaScript для соблюдения принципа иммутабельности.
Поток данных в Redux
Теперь, когда мы знаем все компоненты, давайте соберем их вместе и посмотрим на жизненный цикл данных в Redux приложении. Этот процесс строго однонаправленный:
store.dispatch(action).!Жизненный цикл данных в Redux: от действия пользователя до обновления интерфейса
Почему это работает?
Такая жесткая структура может показаться избыточной для простых задач. Зачем писать Actions и Reducers, если можно просто изменить переменную?
Ответ кроется в предсказуемости и отладке. Поскольку редьюсеры — это чистые функции, вы всегда можете воспроизвести любую ошибку, просто зная начальное состояние и последовательность действий. Это открывает возможности для таких инструментов, как «путешествие во времени» (Time Travel Debugging), когда вы можете отматывать состояние приложения назад и вперед, чтобы понять, что пошло не так.
Заключение
Мы разобрали фундамент Redux. Теперь вы знаете, что Store — это единственное хранилище, Actions — это вестники событий, а Reducers — это чистые функции, которые решают, как изменится мир вашего приложения. В следующей статье мы перейдем от теории к практике и настроим наше первое Redux-окружение.