redux version что это
Краткое руководство по Redux для начинающих
Библиотека Redux — это способ управления состоянием приложения. Она основана на нескольких концепциях, изучив которые, можно с лёгкостью решать проблемы с состоянием. Вы узнаете о них далее, в этом руководстве по Redux для начинающих.
Примечание Вы читаете улучшенную версию некогда выпущенной нами статьи.
Содержание:
Когда нужно пользоваться Redux?
Redux идеально использовать в средних и крупных приложениях. Им стоит пользоваться только в случаях, когда невозможно управлять состоянием приложения с помощью стандартного менеджера состояний в React или любой другой библиотеке.
Простым приложениям Redux не нужен.
Использование Redux
Разберём основные концепции библиотеки Redux, которые нужно понимать начинающим.
Неизменяемое дерево состояний
В Redux общее состояние приложения представлено одним объектом JavaScript — state (состояние) или state tree (дерево состояний). Неизменяемое дерево состояний доступно только для чтения, изменить ничего напрямую нельзя. Изменения возможны только при отправке action (действия).
Действия
Действие (action) — это JavaScript-объект, который лаконично описывает суть изменения:
Типы действий должны быть константами
В простом приложении тип действия задаётся строкой. По мере разрастания функциональности приложения лучше переходить на константы:
и выносить действия в отдельные файлы. А затем их импортировать:
Генераторы действий
Генераторы действий (actions creators) — это функции, создающие действия.
Обычно инициируются вместе с функцией отправки действия:
Или при определении этой функции:
Редукторы
При запуске действия обязательно что-то происходит и состояние приложения изменяется. Это работа редукторов.
Что такое редуктор
Редуктор (reducer) — это чистая функция, которая вычисляет следующее состояние дерева на основании его предыдущего состояния и применяемого действия.
Чистая функция работает независимо от состояния программы и выдаёт выходное значение, принимая входное и не меняя ничего в нём и в остальной программе. Получается, что редуктор возвращает совершенно новый объект дерева состояний, которым заменяется предыдущий.
Чего не должен делать редуктор
Редуктор — это всегда чистая функция, поэтому он не должен:
Поскольку состояние в сложных приложениях может сильно разрастаться, к каждому действию применяется не один, а сразу несколько редукторов.
Симулятор редуктора
Упрощённо базовую структуру Redux можно представить так:
Состояние
Список действий
Редуктор для каждой части состояния
Редуктор для общего состояния
Хранилище
Хранилище (store) — это объект, который:
Хранилище в приложении всегда уникально. Так создаётся хранилище для приложения listManager:
Хранилище можно инициировать через серверные данные:
Функции хранилища
Прослушивание изменений состояния:
Поток данных
Поток данных в Redux всегда однонаправлен.
Передача действий с потоками данных происходит через вызов метода dispatch() в хранилище. Само хранилище передаёт действия редуктору и генерирует следующее состояние, а затем обновляет состояние и уведомляет об этом всех слушателей.
Советуем начинающим в Redux прочитать нашу статью о других способах передачи данных.
Введение в Redux & React-redux
Оглавление
Введение
Вот вы прочитали мою статью про React (если нет, то настоятельно рекомендую вам сделать это) и начали разрабатывать приложения на нём. Но что это? Вы замечаете, как с расширением вашего приложения становится всё сложнее следить за текущим состоянием, сложно следить за тем, когда и какие компоненты рендарятся, когда они не рендарятся и почему они не рендарятся, сложно следить за потоком изменяющихся данных. Для этого и есть библиотека Redux. Сам React хоть и лёгкий, но для комфортной разработки на нем нужно много чего изучить.
И сегодня мы разберём 2 библиотеки: Redux и React-redux. Для использования Redux’а вам не нужно скачивать дополнительных библиотек, но, если использовать его в связке с библиотекой React-redux разработка становится ещё удобнее и проще.
Все примеры из этой статьи вы можете найти в этом репозитории на Github. Там находится полностью настроенное приложение React с использованием Redux и React-redux. Вы можете использовать его как начальную точку для вашего проекта. Изменяйте названия файлов и добавляйте новые в этот репозитории для создания собственного приложения. Смотрите во вкладку релизы для того что бы найти разные версии приложения. Первая содержит приложение только с использованием Redux, второе с использованием Redux и React-redux.
Мотивация использования Redux
Механизм локального хранилища компонента, который поставляется вместе с базовой библиотекой (React) неудобен тем, что такое хранилище изолировано. К примеру, если вы хотите, чтобы разные независимые компоненты реагировали на какое-либо событие, вам придётся либо передавать локальное состояние в виде пропсов дочерним компонентам, либо поднимать его вверх до ближайшего родительского компонента. В обоих случаях делать это не удобно. Код становится более грязным, трудночитаемым, а компоненты зависимыми от их вложенности. Redux снимает эту проблему так как всё состояние доступно всем компонентом без особых трудностей.
Redux является универсальным средством разработки и может быть использован в связке с различными библиотеками и фреймворками. В этой же статье будет рассматривается использование Redux в React приложениях.
1. Установка Redux и начало работы
Используете ли вы Yarn или Npm, выполните одну из этих команд для установки Redux:
Скорее всего вы используете папку src в которой хранится ваша кодовая база. Файлы, связанные с redux принято хранить в отдельной папке. Для этого я использую папку /src/store в которой хранится всё то, что связано с Redux и хранилищем приложения. Вы можете назвать ее по другому или поместить в другое место.
Создайте базовую структуру для хранилища. Она должна выглядит примерно следующим образом:
.store
├── actionCreators
│ ├── action_1.js
│ └── action_2.js
├── actions
│ ├── action_1.js
│ └── action_2.js
├── reducers
│ ├── reducer_1.js
│ ├── reducer_2.js
│ └── rootReducer.js
├── initialState.js
└── store.js
Конечно здесь я использовал примитивные названия для файлов, это сделано для наглядности. В настоящем проекте так называть файлы не стоит.
2. Redux
2.1 createStore
Когда вы создали базовую структуру для работы с хранилищем Redux пришло время понять то как вы можете взаимодействовать с ним.
Глобальное хранилище приложения создаётся в отдельном файле, который как правило называется store.js:
2.2 reducer()
reducer — чистая функция которая будет отвечать за обновление состояния. Здесь реализовывается логика в соответствие с которой будет происходить обновление полей store.
Так выглядит базовая функция reducer:
Функция принимает значение текущего состояния и обьект события (action). Обьект события содержит два свойства — это тип события (action.type) и значение события (action.value).
К примеру если нужно обработать событие onChange для поля ввода то объект события может выглядеть так:
Некоторые события могут не нуждаться в передаче каких-либо значении. К примеру, обрабатывая событие onClick мы можем сигнализировать о том, что событие произошло, более никаких данных не требуется, а как на него реагировать будет описывать логика, заложенная непосредственно в сам компонент которой должен на него реагировать и частично в reducer. Но во всех случаях необходимо определять тип события. Редьюсер как бы спрашивает: что произошло? actio.type равен «ACTION_1» ага значит произошло событие номер 1. Дальше его нужно как то обработать и обновить состояние. То, что вернёт редьюсер и будет новым состоянием.
ACTION_1 и ACTION_2 это константы событий. По-другому Actions. Про них мы поговорим далее 2.5 Actions.
Как вы уже догадались store может хранить сложную структуру данных состоящих из набора независимых свойств. Обновление одного свойства оставит нетронутым другие свойства. Так из примера выше, когда происходит событие номер один (ACTION_1) обновляется поле номер один (value_1) в store при этом поле номер два (value_2) остаётся нетронутым. В общем механизм схож с методом this.setState().
2.3 dispatch()
Что бы обновить store необходимо вызвать метод dispatch(). Он вызывается у объекта store который вы создаёте в store.js. Этот объект принято называть store поэтому обновление состояния в моём случае выглядит так:
ACTION_1 это константа события о которой речь пойдет дальше (см. Actions).
Эта функция вызовет функцию reducer который обработает событие и обновит соответствующие поля хранилища.
2.4 actionCreator()
На самом деле передавать объект события напрямую в dispatch() является признаком плохого тона. Для этого нужно использовать функцию под названием actionCreator. Она делает ровно то что и ожидается. Создаёт событие! Вызов этой функции нужно передавать как аргумент в dispatch а в actionCreator передавать необходимое значение (value). Базовый actionCreator выглядит следующим образом:
Таким образом вызов dispatch должен выглядеть так:
С использованием actionCreator код становится более чистым.
2.5 Actions
actions это константы, описывающие событие. Обычно это просто строка с названием описывающее событие. К примеру константа описывающее событие номер один будет выглядеть следующем образом:
Опять же в проекте вам стоит называть константы в соответствии с событием, которое она описывает: onClick, createUserSesion, deleteItem, addItem и т.д. Главное, чтобы было понятно. Замете что я нигде не писал import поэтому не забудьте импортировать ваши константы перед их использованием. Потому что константы тоже принято разбивать на отдельные файлы храня их в специальной папке. Хотя некоторые хранят их в одном файле под названием actionTypes.js. Такое решение нельзя назвать не правильным, но и не идеальным.
2.6 getState()
С помощью dispatch() обновили, а как теперь посмотреть новое значение store? Ничего изобретать не нужно, есть метод getState(). Он также, как и метод dispatch вызывается на экземпляре объекта store. Поэтому для моего примера вызов
вернёт значение полей хранилища. К примеру что бы посмотреть значение поля value_1 необходимо будет вызвать
2.7 subscribe()
А как же узнать, когда состояние обновилось? Для этого есть метод subscribe(). Он также вызывается на экземпляре store. Данный метод принимает функцию, которая будет вызывается каждый раз после обновления store. Он как бы «подписывает» функцию, переданную ему на обновление. К примеру следующий код при каждом обновлении (при каждом вызове dispatch()) будет выводить новое значение store в консоль.
Этот метод возвращает функцию unsubscribe(). Которая позволяет «отписаться от обновления». К примеру если компонент удаляется из DOM стоит отписать его методы от обновления в componentWillUnmount(). Этот метод жизненного цикла вызывается при размонтировании компонента и это именно то место где стоит отписываться от обновления. Проще говоря в деструкторе.
2.8 combineReducers()
combineReducers() позволяет объединить несколько редьюсеров в один.
Если логика обновления компонентов довольно сложна и\или необходимо обрабатывать большое количество различных типов событий, то корневой reducer может стать слишком громоздким. Лучшим решением будет разбить его на несколько отдельных редьюсеров каждый из которых отвечает за обработку только одного типа событий и обновления определённого поля.
Когда вы разбиваете базовый редьюсер на несколько, то название каждого из них должно соответствовать полю которое он обновляет в store.
К примеру если редьюсер обновляет поле номер один, то он может выглядеть так:
Название редьюсера (value_1) показывает какое свойство он будет обновлять в store. Если переименуете его в value_2 то он станет обновлять value_2. Поэтому учтите это!
Когда используется единый редьюсер мы показываем какое поле хотим обновить:
Но когда вы разделили ваши редьюсеры вам нужно просто вернуть новое значение:
Поскольку здесь не требуется указывать которое из полей обновляет редьюсер ибо его название и есть поле которое он обновляет.
2.9 initialState
initialState — объект, представляющий начальное состояние хранилища. Он является вторым не обязательным аргументом метода createStore(). С созданием хранилища можно сразу объявить начальное состояние для его полей. Этот объект желательно создавать, даже в тех случаях, когда объявления начального состояния не требуется. Потому что этот объект помогает посмотреть на структуру хранилища и название его полей. Обычный объект initialState выглядит следующим образом:
В некоторых случаях (когда компонент сразу использует значение из store), его объявление может стать обязательным иначе вы получите ошибку: TypeError: Cannot read property ‘value_1’ of undefined.
Также редьюсеры всегда должны возвращать по дефолту текущее состояние. К примеру, если используется единый reducer то последнее значение в switch должно выглядеть так:
Если же вы разделяете редьюсеры на независимые функции, то он должен возвращать значение того свойства за которое он отвечает:
Также если вы не передаёте объект initialState в createStore вы можете вернуть его из редьюсера. В обоих случаях будет инициализировано начальное состояние для store.
3. React-redux
Казалось бы, у нас есть всё что бы использовать Redux. Но на деле использование его без пакета React-redux в React приложениях выглядит не очень красиво.
3.1 Provider
Для использование store в компоненте вам необходимо передавать его в пропсы:
И после использовать в компоненте: this.props.state. Для этого react-redux предостовляет метод Provider:
Также можно передать store напрямую в компонент, не оборачивая его в Provider и это будет работать. Но лучше всё-таки используйте Provider.
3.2 mapStateToProps()
Этот метод вызывается всякий раз, когда происходит обновление store и именно он передаёт необходимые свойства из store в компонент. К примеру компонент, должен реагировать и обновлять UI каждый раз, когда поле номер один (value_1) обновилось. На обновление других полей ему реагировать не нужно. Если вы не используете React-redux вам бы пришлось использовать метод subscribe() что бы узнавать об обновлении и далее каким то образом проверять обновилось ли поле номер один или нет. В общем несложно понять, что такой код будет выглядеть слишком грязным и избыточным. С помощью mapStateToProps() можно чётко определить какие поля интересуют компонент. И на какие поля он должен реагировать.
Возвращаясь к примеру выше, если компоненту один нужно получать поле номер один (value_1) то mapStateToProps для него будет выглядеть следующим образом:
После внутри компонента мы можем обращается к полю value_1 через this.props.value_1. И каждый раз когда это поле будет обновляется компонент будет рендерится заново.
Вы можете создать отдельную папку в /src/store для хранения файлов каждый из которых будет содержать функцию mapStateToProps для всех ваших компонентов. Либо (как сделал это я) использовать единую функцию возвращающую функцию mapStateToProps для каждого компонента. Лично мне нравится такой подход. Такая функция выглядит следующим образом:
Эта функция в качестве аргумента принимает строку с названием компонента и возвращает функцию mapStateToProps которая возвращает объект со свойством из store необходимом для данного компонента. Эту функцию можно назвать mapStateToPropsGenerator().
3.3 mapDispatchToProps()
Эта функция передаёт в компонент методы для обновления необходимого поля store. Что бы не вызывать dispatch напрямую из компонента вы будете использовать данный метод для того что бы передавать в props метод вызов которого приведёт к вызову dispatch и обновлению соответствующего поля. Просто теперь это будет выглядеть более элегантно, а код более понятным и чистым.
К примеру компонент, номер один должен иметь возможность обновлять поле номер один из store. Тогда mapDispatchToProps для него будет выглядеть следующим образом:
Теперь для обновления свойства value_1 вы будете вызывать changeValue_1() через this.props.changeValue_1(value). Не вызывая dispatch напрямую через this.props.store.dispatch(action_1(value)).
bindActionCreators следует импортировать из redux. Он позволяет оборачивать функцию dispatch и actionCreator в единый объект. Вы можете не использовать bindActionCreators но тогда код будет выглядеть избыточным. Вы должны старятся реализовать какую-либо функциональность так, чтобы код выгладил просто и миниатюрно. Поэтому ничего лишнего писать не следует.
Только чистый и понятный код. Метод bindActionCreators(actionCreator, dispatch) принимает два обязательных параметра это функцию actionCreator о которой мы уже говорили и dispatch. Возвращая метод для изменения полей store.
Также как и для mapStateToProps я использую функцию генератор возвращающую функцию mapDispatchToProps для каждого компонента:
3.4 connect()
Ну и теперь кульминация! То без чего всё это не будет работать. Это функция connect.
Именно она связывает mapStateToProps и mapDispatchToProps с компонентом и передает необходимые поля и методы в него. Возвращает она новый компонент-обёртку для вашего компонента. Как правильно именовать такой компонент я не знаю, ибо в самой документации React-redux это не описывается. Лично я добавляю окончание _w для компонентов оберток. Как бы _w = wrap Component. Подключение компонента в этм случае выглядит так:
И теперь в ReactDOM.render() вы передаёте не ваш компонент, а тот что возвращает функция connect.
Если же у компонента нет необходимости в передаче ему mapStateToProps или mapDispatchToProps передавайте undefined или null в него.
Redux-in-russian
Оригинальная документация по Redux с переводом на русский
Redux является предсказуемым контейнером состояния для JavaScript приложений. (Не путайте с WordPress фреймворком – Redux Framework.)
Это позволяет вам создавать приложения, которые ведут себя одинаково в различных окружениях (клиент, сервер и нативные приложения), а также просто тестируются. Кроме того, это обеспечивает большой опыт отладки, например редактирование кода в реальном времени в сочетании с time traveling.
Вы можете использовать Redux вместе с React или с любой другой view-библиотекой. Это крошечная библиотека (2kB, включая зависимости).
Примечание: В настоящее время мы планируем переписать документацию Redux. Пожалуйста, уделите некоторое время заполните этот опрос, чтобы узнать, какой контент наиболее важен на сайте документации. Спасибо!
Изучите Redux
У нас есть множество доступных ресурсов, которые помогут вам изучить Redux, независимо от того, каков ваш уровень знаний или стиль обучения.
Только основы
Если вы новичок в Redux и хотите понять основные понятия, см:
Средний уровень
Когда вы освоите основы работы с экшенами, редюсерами и стором, у вас могут возникнуть вопросы по таким темам, как работа с асинхронной логикой и AJAX-запросами, подключение UI фреймворка, например React, к вашему стору Redux и настройка приложения для использования Redux:
Использование в реальном мире
Переход от приложения TodoMVC, к реальному production-приложению, может стать большим скачком, но у нас есть много ресурсов, которые помогут:
Наконец, Марк Эриксон проводит серию воркшопов по Redux при помощи Workshop.me. Проверьте расписание воркшопов для предстоящих дат и местоположений.
Помощь и обсуждения
Прежде чем продолжить
Вот несколько советов о том, когда имеет смысл использовать Redux:
Да, эти рекомендации являются субъективными и расплывчатыми, но это справедливо. Точка, в которой вы должны интегрировать Redux в ваше приложение, отличается для каждого пользователя и каждого приложения.
Дополнительные сведения о том, как использовать Redux, см:
Опыт разработки
Дэн Абрамов, автор Redux, написал Redux пока работал над своим докладом на React Europe, который назывался “Hot Reloading with Time Travel”. Его целью было создание библиотеки управления состоянием с минимальным API, но вполне предсказуемым поведением, так чтобы можно было реализовать протоколирование, горячую перезагрузку, путешествия во времени, универсальные приложения, запись и воспроизведение, без каких-либо вложений от разработчика.
Влияния
Redux развивает идеи Flux, но избегает его сложности, воспользовавшись примерами из Elm. Вне зависимости от того, использовали вы их или нет, Redux занимает всего несколько минут, чтобы начать с ним работу.
Установка
Для установки стабильной версии:
Предполагается, что вы используете npm в качестве менеджера пакетов.
Если нет, то вы можете получить доступ к этим файлам на unpkg, загрузить их или настроить ваш пакетный менеджер.
Исходные коды Redux написаны на ES2015, но мы предкомпилировали и CommonJS и UMD сборки в ES5 поэтому они работают в любом современном браузере. Вам нет необходимости использовать Babel или сборщик модулей чтобы начать пользоваться Redux.
Дополнительные пакеты
Обратите внимание, что в отличие от самого Redux, многие пакеты в экосистеме Redux не предоставляют сборки UMD, поэтому мы рекомендуем использовать сборщики модулей CommonJS, такие как [Webpack] (https://webpack.js.org/) и [Browserify] (http://browserify.org/) для наиболее комфортной разработки.
Основные принципы
Вместо того, что бы изменять состояние напрямую, вы определяете изменения, которые вы хотите произвести, с помощью простых объектов называемых экшенами. Затем вы пишете специальную функцию, называемую редюсер, чтобы решить, каким образом каждый экшен преобразует все состояние приложения.
Если вы пришли из Flux, есть одно важное различие, которое вы должны понимать. Redux не имеет Диспетчера (Dispatcher) или поддержки множества сторов. Вместо этого есть только один стор с одной корневой функцией-редюсером. Когда ваше приложение разрастется, вместо добавления сторов, вы разделяете корневой редюсер на более мелкие редюсеры, которые независимо друг от друга обслуживают разные части дерева состояния. Это аналогично тому, что в React приложении есть только один корневой компонент, состоящий из множества мелких компонентов.
Эта архитектура может показаться излишней для счетчика приложения, но красота этого шаблона в том, насколько хорошо она масштабируется для больших и сложных приложений. Она также предоставляет очень мощные инструменты для разработчиков, позволяющих проследить каждую мутацию к экшену, вызвавшему его. Вы можете записывать пользовательские сессии и воспроизводить их просто переигрывая каждый экшена.
Изучайте Redux вместе с его авторами
Видео уроки Redux от Dan Abramov
Getting Started with Redux
И так, чего же вы ждете?
Посмотрите 30 бесплатных уроков «Getting Started with Redux»!
Создание React приложений с помощью Redux
Смотрите бесплатный видео курс «Idiomatic Redux»
Практический курс Redux
Курс основывается на оригинальной бесплатной учебной серии «Практический Redux», но с обновленным и улучшенным контентом.
Воркшопы Redux
Документация
Для экпорта в PDF, ePub MOBI или чтения в оффлайн и инструкциям, как это можно осуществить, обратите внимание на: paulkogel/redux-offline-docs.
Для документации в Offline, пожалуйста посмотрите: devdocs
Примеры
Почти все примеры имеют соответствующую песочницу CodeSandbox. Это интерактивная версия кода, с которой вы можете играть онлайн.
Если вы новичок в экосистеме NPM и имеете проблемы с получением и запуском проекта или не уверены, куда вставить шаблон, попробуйте simplest-redux-example который использует Redux вместе с React и Browserify.
Отзывы
Благодарности
Особенная благодарность Jamie Paton за передачу прав на redux NPM пакет.
Вы можете найти официальное лого на GitHub.
История изменений
Это проект придерживается принципов семантического версионирования. Каждый релиз, вместе с инструкциями миграции, документированы на странице релизов Github.
Меценаты
Разработка Redux была профинансирована сообществом. Познакомьтесь с некоторыми из выдающихся компаний, которые сделали это возможным: