Основы Redux
1. Введение
Требования к функционалу приложений постоянно растут, в результате растет количество состояний интерфейса: асинхронная загрузка данных, лоадеры, фильтры и т. п.
За всем этим необходимо следить и обрабатывать, и это не просто. В один момент можно просто перестать улавливать связь между изменениями, так как контроль над тем, когда, почему и как изменилось состояние потерян из-за сложности самого состояния.
Идеальный вариант, это когда интерфейс вообще не знает о бизнес-логике. В этом нам помогают библиотеки управления состоянием. Redux
и Mobx
самые популярные, а для бекендов основаных на GraphQL
есть Apollo
.
2. Основные принципы Redux
Предсказуемость результата - существует всегда один источник правды, хранилище (
store
), хранящее в себе состояние приложения и методы для работы с ним.Поддреживаемость - есть набор правил и лучших практик о том, как должен быть структурирован код, что делает его более единообразным и понятным.
Инструменты разработчика - все происходящее можно отслеживать в режиме реального времени.
Простота тестирования - первое правило написания тестируемого кода - писать небольшие функции, которые выполняют только одну вещь и независимы. Redux - это в основном функции: маленькие, чистые и изолированные.
2.1. Поток данных
Без использования библиотеки управления состоянием процесс обновления данных выглядит следующим образом: компонент инициализирует изменение состояния вызвав метод полученный пропом, после чего измененное состояние пробрасывается пропами вниз по дереву.
Поток данных в Redux всегда однонаправленный, и очень простой. За данные отвечает хранилище, компоненты только вызывают методы обновления данных и потом получают обновленные данные.
Пользователь, работая с интерфейсом, инициализирует отправку действий (
actions
)Хранилище (
store
) вызывает все объявленные редьюсеры (reducers
), передавая им текущее состояние (state
) и действие (action
)Хранилище (
store
) сохраняет обновленное дерево состояния (state
) возвращенное из редьюсеров (reducers
)При обновлении состояния (
state
) вызываются все подписчики для обновления интерфейса
3. Store
Хранилище (store
) - js-объект, который содержит состояние приложения и методы доступа к нему, отправки действий и регистрации слушателей.
Хранит состояние (
state
) приложения как один объектПозволяет получить доступ к состоянию через метод
getState()
Напрямую состояние доступно только для чтения
Единственный способ изменить состояние - отправить действие (
action
), объект, описывающий, что произошлоДля отправки дейсвтий есть метод
dispatch(action)
Изменения производятся с использованием чистых функций - редюсеров (
reducers
), которые реагируют на действияРегистрирация слушателей делается методом
subscribe(listener)
Так как все состояние приложения хранится как один объект, стоит подумать о форме состояния прежде чем писать какой-либо код. Продумайте минимальное представление состояния приложения в виде объекта.
3.1. Функция createStore
Для того чтобы создать хранилище, используется функция createStore
, которая принимает набор параметров и возвращает созданное хранилище.
reducer
- функция, которая возвращает следующее дерево состояния, учитывая текущее дерево состояния и действие для обработки.preloadedState
- начальное состояние, к примеру сериализаванное состояние последнего пользовательского сеанса. Это должен быть объект той же формы, что и, как минимум, часть состояния.enhancer
- расширяет возможности хранилища при помощи прослоек (middleware).
4. Actions
Действия (actions
) - это объекты, которые помогают доставить данные из компонентов в хранилище.
Хранят минимально необходимый набор информации.
Должны иметь свойство
type
, которое указывает тип выполняемого действия.Помимо поля
type
, структура действия может быть произвольной.
Действия создаются функциями (action creators
), которые могут быть асинхронными и иметь побочные эффекты. В базовом варианте они просто возвращают объект-дейтсвие.
5. Reducers
Редюсеры (reducers) - это чистые функции, которые принимают предыдущее состояние приложения и действие, а затем возвращают новое следующее состояние.
Они определяют, как изменяется состояние приложения в ответ на действия, отправленные в хранилище. Помните, что действия описывают только то, что произошло, а не как изменяется состояние приложения.
Вещи, которые нельзя делать внутри редюсера:
Мутировать аргументы
Выполнять побочные эффекты, такие как API-запросы и т. п.
Вызывать нечистые функции, например
Date.now()
Как выполнять побочные эффекты мы рассмотрим далее, пока что просто помните - редюсер должен быть чистым. Получая те же аргументы, он должен вычислить следующее состояние и вернуть его. Без сюрпризов. Никаких побочных эффектов. Никаких мутаций. Просто рассчет.
Вот редьюсер, который принимает текущее состояние и действие как аргументы, а затем возвращает следующее состояние.
Обратите внимание:
Мы создаем копию
state
, а не мутируем его.Мы возвращаем предыдущее состояние по умолчанию. Важно вернуть предыдущее состояние для любого неизвестного действия.
6. React и Redux
Для того чтобы использовать React и Redux вместе, необходимо добавить пакет react-redux
. Это набор компонентов связывающих React-компоненты и Redux-хранилище через контекст Context.
6.1. Provider
Компонент <Provider>
, оборачивает все дерево компонентов приложения и, используя контекст, предоставляет store
и его методы.
6.2. connect()
Если какой-либо компонент хочет получить доступ к store
, он должен быть обернут в функцию connect()
, которая свяжет компонент и store
. Предоставляет доступ к state
и disaptch()
.
connect
это HOC, он не изменяет переданный ему компонент, а возвращает новый компонент связанный с хранилищем.
mapStateToProps(state, [ownProps])
- функция соединяющая часть состояния с пропами компонента. Таким образом, связанный компонент будет иметь доступ к необходимой ему части состояния.
Получает
state
как параметр и позволяет выбрать из всегоstate
только необходимые компоненту слайсы (части).Возвращает объект, свойства которого дополнят
props
компонента.Вызывается каждый раз когда хранилище обновляется.
Если нет необходимости подписываться на обновления, передаем
null
.Если функция объявлена как принимающая два параметра, первым будет передана ссылка на
state
, а вторым ссылка на пропы самого компонента.
mapDispatchToProps(dispatch, [ownProps])
- функция соединяющая отправку действий с пропами компонента. Таким образом, связанный компонент сможет отправлять действия посредством вызова методов указанных в возвращаемом объекте.
Получает ссылку на метод
dispatch
как параметр и позволяет объявить методы для отправки действий.Возвращает объект, свойства которого дополнят
props
компонента.Если функция объявлена как принимающая два параметра, первым будет передана ссылка на
dispatch
, а вторым ссылка на пропы самого компонента.
Если аргументы действия совпадают с параметрами объявляемого метода, можно вместо функции передать объект. В таком случае connect
пройдет по ключам объекта и обернет их в dispatch
.
7. Redux DevTools
Чтобы упростить работу с Redux существуют Redux DevTools. Они помогают отслеживать изменение состояния с течением времени, наблюдать за отправкой дейтсвий и т. п. Естеcтвенно, это предназначено только для разработки.
8. Организация хранилища
Необходимо хранить все состояние приложения в Redux? Можно ли использовать setState()
? Как разработчик, ваша задача - определить, из какого набора данных состоит приложение, и где их лучше хранить.
Некоторые общие правила для определения того, какие данные должны быть помещены в Redux. Если ответ да, то есть смысл использовать хранилище, а не локальное состояние.
Необходимы ли эти данные другим частям приложения?
Необходимо ли на основе этих данных создавать дополнительные производные?
Используются ли одни и те же данные для управления несколькими компонентами?
Есть ли необходимость в кешировании?
8.1. Дополнительные материалы
9. Композиция редюсеров
Еще одна полезная функция Redux - возможность делать композицию редьюсеров, то есть совмещать много в один. Это позволяет удобно поддерживать гораздо более сложные состояния в больших приложениях. Для этого используется функция combineReducers()
.
10. Дополнительные материалы
Last updated
Was this helpful?