Redux tutorial: Введение в Redux & React-redux / Хабр
Учебник: введение в React
Данный учебник не предполагает каких-либо знаний React.
Перед тем как начнём
В данном разделе мы создадим небольшую игру. У вас может возникнуть
соблазн пропустить его, так как вы не пишете игры — не делайте такое поспешное решение.
Методы, которые вы здесь изучите, имеют основополагающее
значение для создания любых React приложений, а их освоение даст вам
глубокое понимание React.
Подсказка
Этот учебник предназначен для людей, которые желают учиться на практике.
Если же вы предпочитаете изучать теоретические концепции с нуля, ознакомьтесь
с нашим пошаговым руководством.
Также вы можете найти пошаговое руководство и данный учебник дополняющими друг друга.
Учебник состоит из нескольких разделов:
Установка. Даст вам отправную точку, чтобы следовать учебнику.
Обзор. Познакомит вас с основами React: компонентами, свойствами и состоянием.
Завершение игры. Научит вас наиболее распространенным методам разработки в React.
Добавление Time Travel. Даст вам более глубокое понимание уникальных преимуществ React.
Вам не обязательно изучать полностью все разделы, чтобы извлечь пользу из этого учебника.
Постарайтесь добраться как можно дальше — даже если это одна или две главы.
Во время изучения можно копировать и вставлять код, но
мы рекомендуем набирать его вручную. Это поможет вам развить мышечную
память и получить более глубокое понимание происходящего.
Что мы разрабатываем?
В этом учебнике мы покажем, как создать интерактивную
игру в крестики-нолики используя React.
Вы можете увидеть, что именно мы будем разрабатывать здесь: Окончательный
рез
8 бесплатных обучалок фреймворку React.js | GeekBrains
И Redux для увеличения кругозора.
https://d2xzmw6cctk25h.cloudfront.net/post/1076/og_cover_image/8e7c3877b8f543f785d3b3d7dd6da6f9
React.js практически с самого своего появления в 2013 году стал привлекать внимание массовой аудитории разработчиков. В 2017 году это официально один из двух наиболее популярных JavaScript-фреймворков (библиотек), а по некоторым данным и вовсе абсолютный лидер. В общем, если вы веб-разработчик, то вам строго рекомендуется хотя бы ознакомиться с React и его возможностями. И вам в помощь 8 абсолютно бесплатных туториалов.
GitBook по React на русском
Начнём с русскоязычного учебника, ведь многие начинающие гики в нашей стране просто игнорируют потребность в изучении английского языка на профессиональном уровне. Впрочем, с таким учебником он не особо-то и нужен.
Здесь вся необходимая для новичка теория, изложенная максимально доступно, с огромным количеством примеров, картинок, демонстрирующих работу с формами, и самое главное — минимум голого текста.
Курсы по React от Tyler McGinnis
От русского языка переходим к хардкорно-английскому, а именно видеокурсам, направленным на базовое знакомство с библиотекой React. 48 занятий (не переживайте, в сумме это 287 минут), посвящённых базовым принципам работы с библиотекой, рассмотрению шаблонных примеров и решению типовых задач. Можете использовать вместе с русским учебником для подтягивания знаний английского.
Hacking with React
Электронная бесплатная книга по React, которая поможет вам практически с нулевыми знаниями погрузиться в мир веб-разработки. Начинается она с настройки библиотеки, знакомит с JSX, а далее с помощью Hacking with React вы создадите свой первый проект. Но не надейтесь найти здесь всю интересующую вас информацию по React и уж тем более по разработке веба. Данная книга только для поверхностного ознакомления. Кстати, если вы любитель почитать книги вне дома и работы, можете за нескромные 10$ скачать автономную версию учебника.
Официальный туториал по React
Если вам нужна исчерпывающая информация, единственный способ найти её — обратиться к первоисточнику, то есть к официальной странице библиотеки. Почему данный туториал не на первом месте в данном списке? Всё просто. Дело в том, что, на мой личный взгляд, он несколько избыточен для новичка. Если у вас есть опыт работы с каким-либо ещё JS-фреймворком, то официальное представление удобно и понятно. Если вы только вчера постигли основы программирования, лучше загляните сюда попозже.
Фундаментальные курсы от Egghead
Платформа Egghead предлагает видеоуроки, среди которых есть как общий вводный курс по JavaScript, так и занятия по отдельным фреймворкам, будь то Angular или React. Последний курс является третьим по объему материала, и он включает в себя 5 бесплатных разделов.
Два из них посвящено общим вопросам создания мобильных веб-приложений, два — знакомству с Redux, и ещё один раздел поведает о вопросах роутинга в React. Так как платформа выпускает бесплатные материалы исключительно для «затравки», то на полноценные знания можете рассчитывать только при оплате подписки категории Pro. Однако для получения представления о React и Redux хватит и этих коротких занятий.
GtiBook по Redux на русском
Раз уж речь зашла о Redux, который хоть и не является частью React, но определённо обитает где-то в этой же экосистеме, то вашему вниманию представлен русскоязычный учебник по этой дисциплине. Автор тот же, но материал здесь рассчитан на более искушённую публику. Здесь вы прочитаете и про webpack, и про hot module replacement, чего не было в первой части. А в результате вы вместе с автором создадите приложение для сборки фотографий из сети Вконтакте по заданным параметрам.
React и Redux с Cabin
Это минимальный туториал по созданию приложения с нуля. Начинаете с установки React, его настройки, подключаете Redux, работаете с фильтрами и получаете готовый продукт. Информация очень схожа с русскоязычной версией, но здесь больший акцент делается на гибкость и функциональность. А ещё работать здесь будете с фотографиями из Instagram.
The React Convention
Перед вами онлайн-журнал по разработке приложений с React. В силу специфики новичкам сюда лучше не соваться. Зато опытные, а точнее, «промежуточные» разработчики найдут здесь много полезного как по шаблонным проектным решениям, так и интересным вариантам применения библиотеки. В общем, всем тем, кто уже не новичок в React и Redux, но всё ещё много сомневается, обязательно периодически заходить, читать и не забывать периодически проверять обновления.
Почти 500 React проектов и ресурсов
Почти 500 React проектов и ресурсов
Почти 500 React проектов и ресурсов
Коллекция удивительных вещей, касающихся экосистемы React. Все, что вы найдете по списку ниже, абсолютно бесплатно.
React
JavaScript Library for building User Interfaces
React General Resources
React Community
React Online Playgrounds
React Tutorials
React General Tutorials
React Hooks
React and TypeScript
React Performance
React Internals
React Interview Questions
React Tools
React Development Tools
React Frameworks
- next.js — The React Framework
- gatsby.js — Free and open source framework based on React
- react-admin — Frontend Framework for building B2B applications on top of REST/GraphQL APIs
React Styling
- styled-components — Visual primitives for the component age
- emotion — Library designed for writing CSS styles with JavaScript
- radium — A toolchain for React component styling
- jss — Authoring tool for CSS
React Routing
- react-router — Declarative routing for React
- navi — Declarative, asynchronous routing for React
- curi — JavaScript router for single-page applications
React Component Libraries
- material-ui — React components for faster and easier web development
- ant-design — A design system with values of Nature and Determinacy
- blueprint — A React-based UI toolkit for the webs
- office-ui-fabric-react — React components for building Microsoft web experiences
- react-bootstrap — Bootstrap components built with React
- reactstrap — Simple React Bootstrap 4 components
- semantic-ui-react — The official Semantic-UI-React integration
- react-fontawesome — Font Awesome 5 component library for React
- reakit — Accessible, Composable and Customizable components for React
- rsuite — A suite of React components
- atlaskit — Atlassian’s official UI library, built according to the Atlassian Design Guidelines.
- baseweb — Base Web is a foundation for initiating, evolving, and unifying web products.
- primereact — A complete UI Framework for React with 50+ components featuring material, bootstrap and custom themes.
React Awesome Components
React for Command Line
- ink — React for interactive command-line apps
- react-blessed — A React renderer for blessed terminal interface library
React Testing
React Libraries
React Integration
React State Management
React AR and VR
- React 360 — Create exciting 360 and VR experiences using React
- Viro React — Platform for rapidly building AR/VR applications using React Native
Forms
Autocomplete
Graphics
Data Managing
Maps
Charts
React Native
Framework for building native apps using React
React Native General Resources
React Native Tutorials
React Native Development Tools
React Native Sample Apps
React Native Boilerplates
React Native Awesome Components
Redux
Predictable State Container for JavaScript Apps
Redux General Resources
Redux Tools
Redux Tutorials
MobX
Simple, scalable state management for JavaScript Apps
MobX General Resources
MobX Tools
MobX Tutorials
GraphQL
Query Language
GraphQL Spec
GraphQL Tools
GraphQL Tutorials
GraphQL Implementations
Server Integration
Database Integration
Relay
Data-Driven React Applications
Relay General Resources
Relay Tutorials
Relay Tools
Apollo
Data stack based on GraphQL
Apollo General Resources
Apollo Tools
Videos
reactjsvideos.com
Important Talks
React.js Conf 2015 Playlist
ReactEurope Conf 2015 Day 1 Playlist
ReactEurope Conf 2015 Day 2 Playlist
ReactRally Conf 2015 Playlist
React.js Conf 2016 Playlist
React Amsterdam 2016 Playlist
ReactEurope Conf 2016 Day 1 Playlist
ReactEurope Conf 2016 Day 2 Playlist
ReactRally Conf 2016 Playlist
React.js Amsterdam 2018 Playlist
Video Tutorials
Demo React Apps
Real React Apps
- kibana — Your window into the Elastic Stack
- firefox debugger — The Firefox debugger that works anywhere
- spectrum – Simple, powerful online communities
- mattermost — Open source Slack alternative
- overreacted — Personal blog by Dan Abramov
- winamp2-js — Winamp 2 reimplemented for the browser
Базовое руководство | React Redux
Чтобы увидеть, как использовать React Redux на практике, мы покажем пошаговый пример, создав приложение со списком задач.
Пример списка Todo #
Перейти к
Компоненты пользовательского интерфейса React
Мы реализовали наши компоненты пользовательского интерфейса React следующим образом:
-
TodoApp
— это компонент входа для нашего приложения. Он отображает заголовок, компонентыAddTodo
,TodoList
иVisibilityFilters
. -
AddTodo
— это компонент, который позволяет пользователю вводить элемент задачи и добавлять его в список после нажатия кнопки «Добавить задачу»:- Он использует управляемый ввод, который устанавливает состояние на
onChange
. - Когда пользователь нажимает кнопку «Добавить задачу», он отправляет действие (которое мы предоставим с помощью React Redux) для добавления задачи в магазин.
- Он использует управляемый ввод, который устанавливает состояние на
-
TodoList
— это компонент, который отображает список задач:- Он отображает отфильтрованный список задач, когда выбран один из
VisibilityFilters
.
- Он отображает отфильтрованный список задач, когда выбран один из
-
Todo
— это компонент, который отображает один элемент задачи:- Он отображает содержимое задачи и показывает, что задача завершена, вычеркивая его.
- Он отправляет действие для переключения полного статуса задачи при
onClick
.
-
VisibilityFilters
отображает простой набор фильтров: все , завершены, и не завершены. Щелчок по каждому из них фильтрует задачи:- Он принимает свойство
activeFilter
от родителя, которое указывает, какой фильтр в настоящее время выбран пользователем.Активный фильтр отображается с подчеркиванием. - Отправляет действие
setFilter
для обновления выбранного фильтра.
- Он принимает свойство
-
константы
содержат данные констант для нашего приложения. - И, наконец, индекс
The Redux Store
Часть Redux приложения была настроена с использованием шаблонов, рекомендованных в документации Redux:
- Store
-
todos
: нормализованный редуктор задач.Он содержит картуbyIds
всех задач иallIds
, которая содержит список всех идентификаторов. -
visibilityFilters
: простая строкавсе
,завершенные
илинеполные
.
-
- Создатели действий
-
addTodo
создает действие для добавления задач. Он принимает одну строковую переменнуюcontent
и возвращает действиеADD_TODO
с полезной нагрузкой , содержащее самоинкрементирующийся идентификаторid
и содержимое -
toggleTodo
создает действие для переключения задач.Он принимает одну числовую переменнуюid
и возвращает действиеTOGGLE_TODO
с полезной нагрузкойid
только -
setFilter
создает действие для установки активного фильтра приложения. Он принимает одностроковую переменнуюfilter
и возвращает действиеSET_FILTER
с полезной нагрузкой
-
- Редукторы
- Редуктор
todos
- Добавляет
id
к своимallIds
и устанавливает задачу в своем полеbyIds
после получения действияADD_TODO
- Переключает поле
завершено
для задачи после получения действияTOGGLE_TODO
- Добавляет
- Редуктор
visibilityFilters
устанавливает свою часть магазина в новый фильтр, который он получает от полезной нагрузки действияSET_FILTER
- Редуктор
- Типы действий
- Мы используем файл
actionTypes.js
для хранения констант типов действий для повторного использования
- Мы используем файл
- Selectors
-
getTodoList
возвращает списокallIds
изtodos
store -
getTodoById
находит задачу в магазине, заданном идентификаторомid
-
getTodos
немного сложнее. Он берет всеid
s изallIds
, находит каждую задачу вbyIds
и возвращает окончательный массив задач -
getTodosByVisibilityFilter
фильтрует задачи в соответствии с фильтром видимости
-
Вы можете проверить этот CodeSandbox для исходного кода компонентов пользовательского интерфейса и неподключенного хранилища Redux, описанного выше.
Теперь мы покажем, как подключить этот магазин к нашему приложению с помощью React Redux.
Предоставление магазина #
Сначала нам нужно сделать магазин
доступным для нашего приложения. Для этого мы обертываем наше приложение с помощью API
, предоставляемого React Redux.
Копировать
import React from ‘react’
import ReactDOM from ‘react-dom’
import TodoApp from ‘./TodoApp’
import {Provider} from ‘react-redux’
import store from ‘./ redux / store ‘
const rootElement = document.getElementById (‘ root ‘)
ReactDOM.render (
,
rootElement
)
Обратите внимание, как наш
теперь обернут
с store
, переданным как опора.
Подключение компонентов #
React Redux предоставляет функцию connect
для чтения значений из хранилища Redux (и повторного чтения значений при обновлении хранилища).
Функция connect
принимает два аргумента, оба необязательные:
mapStateToProps
: вызывается каждый раз при изменении состояния хранилища. Он получает все состояние хранилища и должен возвращать объект данных, который нужен этому компоненту.mapDispatchToProps
: этот параметр может быть функцией или объектом.- Если это функция, она будет вызываться один раз при создании компонента. Он получит
dispatch
в качестве аргумента и должен вернуть объект, полный функций, которые используютdispatch
для отправки действий. - Если это объект, полный создателей действий, каждый создатель действий будет преобразован в функцию prop, которая автоматически отправляет свое действие при вызове. Примечание : Мы рекомендуем использовать эту «сокращенную» форму.
- Если это функция, она будет вызываться один раз при создании компонента. Он получит
Обычно вы вызываете connect
следующим образом:
Copy
const mapStateToProps = (state, ownProps) => ({
})
const mapDispatchToProps = {
}
const connectToStoSto = connect (
mapStateToProps,
mapDispatchToProps
)
const ConnectedComponent = connectToStore (Component)
connect (
mapStateToProps,
mapDispatchToProps,
MapDispatch) первый.Ему необходимо инициировать изменения в store
, чтобы добавить новые задачи. Следовательно, он должен иметь возможность отправлять
действий в магазин. Вот как мы это делаем.
Наш создатель действия addTodo
выглядит так:
Скопируйте
import {ADD_TODO} из ‘./actionTypes’
let nextTodoId = 0
export const addTodo = content => ({
type: ADD_TODO2, payload: {
id: ++ nextTodoId,
content
}
})
Передавая его в connect
, наш компонент получает его как опору, и он автоматически отправляет действие при его вызове.
Копировать
import {connect} из ‘react-redux’
import {addTodo} from ‘../redux/actions’
class AddTodo extends React.Component {
}
export default connect (
null,
{addTodo}
) (AddTodo)
Обратите внимание, что
заключен в оболочку с родительским компонентом с именем
. Между тем,
теперь получает одно свойство: действие addTodo
.
Нам также необходимо реализовать функцию handleAddTodo
, чтобы она могла отправлять действие addTodo
и сбрасывать ввод
Копировать
import React from ‘react’
import {connect} from ‘react-redux’
import {addTodo} from ‘../redux/actions’
class AddTodo extends React.Component {
handleAddTodo = () => {
this.props.addTodo (this.state.input)
this.setState ({input: »})
}
render () {
return (
onChange = {e => this.updateInput (e.target.value)}
value = {this.state.input}
/>
Добавить Todo
< / button>
)
}
}
экспорт по умолчанию connect (
null,
{addTodo}
) (AddTodo)
Теперь наш
подключен к магазину.Когда мы добавляем задачу, она отправляет действие по изменению магазина. Мы не видим этого в приложении, потому что остальные компоненты еще не подключены. Если у вас подключено расширение Redux DevTools, вы должны увидеть отправляемое действие:
Вы также должны увидеть, что магазин изменился соответствующим образом:
Компонент
отвечает за рендеринг список задач. Следовательно, ему необходимо читать данные из магазина.Мы включаем его, вызывая connect
с параметром mapStateToProps
, функцией, описывающей, какая часть данных нам нужна из магазина.
Наш компонент
принимает задачу в качестве свойств. У нас есть эта информация из поля byIds
из todos
. Однако нам также нужна информация из поля allIds
магазина, указывающая, какие задачи и в каком порядке они должны отображаться. Наша функция mapStateToProps
может выглядеть так:
Копировать
import {connect} из «react-redux»;
const TodoList =
const mapStateToProps = state => {
const {byIds, allIds} = состояние.todos || {};
const todos =
allIds && allIds.length
? allIds.map (id => (byIds? {… byIds [id], id}: null))
: null;
возврат {todos};
};
экспорт подключения по умолчанию (mapStateToProps) (TodoList);
К счастью, у нас есть селектор, который делает именно это. Мы можем просто импортировать селектор и использовать его здесь.
Копировать
export const getTodosState = store => store.todos
export const getTodoList = store =>
getTodosState (store)? getTodosState (магазин).allIds: []
экспорт const getTodoById = (store, id) =>
getTodosState (store)? {… getTodosState (store) .byIds [id], id}: {}
export const getTodos = store =>
getTodoList (store) .map (id => getTodoById (store, id))
Копировать
импортировать из «react-redux»;
импорт {getTodos} из «../redux/selectors»;
const TodoList =
экспорт по умолчанию connect (state => ({todos: getTodos (state)})) (TodoList);
Мы рекомендуем инкапсулировать любые сложные поисковые запросы или вычисления данных в функциях выбора.Кроме того, вы можете дополнительно оптимизировать производительность, используя Reselect для написания «мемоизированных» селекторов, которые могут пропускать ненужную работу. (См. Страницу документации Redux по вычислению производных данных и сообщение в блоге Idiomatic Redux: Использование повторного выбора селекторов для инкапсуляции и производительности для получения дополнительной информации о том, почему и как использовать функции селектора.)
Теперь, когда наш
подключен в магазин. Он должен получить список задач, сопоставить их и передать каждую задачу компоненту
.
, в свою очередь, отобразит их на экране. Теперь попробуйте добавить задачу. Он должен появиться в нашем списке дел!
Подключим еще компоненты. Прежде чем мы это сделаем, давайте сделаем паузу и сначала узнаем немного больше о connect
.
Общие способы вызова connect
#
В зависимости от того, с какими компонентами вы работаете, существуют разные способы вызова connect
, наиболее распространенные из которых приведены ниже:
Не Подписка на Магазин | Подписка на Магазин | |
---|---|---|
Не вводить Создатели действий | connect () (Компонент) | connect (mapStateToProps) (Компонент) |
connect (null, mapDispatchToProps) (Component) | connect (mapStateToProps, mapDispatchToProps) (Component) |
Не подписываться на магазин и не вводить создателей действий #
без предоставления каких-либо аргументов, ваш компонент будет:
- не повторно визуализировать при изменении хранилища es
- получит
реквизита.отправка
, которую вы можете использовать для отправки действия вручную
Копировать
export default connect () (Компонент)
Подпишитесь на магазин и не вводите создателей действий #
Если вы вызываете connect
только с mapStateToProps
, ваш компонент будет:
- подписаться на значения, которые
mapStateToProps
извлекает из хранилища, и повторно визуализировать только тогда, когда эти значения изменились. - получит
props.dispatch
, который вы можете использовать для отправки действия вручную
Копировать
const mapStateToProps = состояние => состояние.partOfState
export default connect (mapStateToProps) (Component)
Не подписывайтесь на хранилище и вводите создателей действий #
Если вы вызываете connect
только с mapDispatchToProps
, ваш компонент будет:
- not re -render при изменении хранилища
- получает каждого из создателей действий, которые вы вводите с помощью
mapDispatchToProps
в качестве свойств, и автоматически отправляет действия при вызове
Copy
import {addTodo} from ‘./ actionCreators ‘
export default connect (
null,
{addTodo}
) (Компонент)
Подпишитесь на магазин и создайте действия для инъекций #
Если вы вызываете , подключитесь к
с помощью mapStateToProps
и mapDispatchToProps
, ваш компонент будет:
- подписаться на значения, которые
mapStateToProps
извлекает из хранилища, и повторно визуализировать только после того, как эти значения изменились. - получит всех создателей действий, которые вы вводите с
mapDispatchToProps
в качестве свойств и автоматически отправляет действия при вызове.
Скопируйте
import * как actionCreators из ‘./actionCreators’
const mapStateToProps = state => state.partOfState
export default connect (
mapStateToProps,
actionCreators
) (
) Эти четыре случая охватывают
самые простые способы использования подключают
. Чтобы узнать больше о connect
, продолжайте читать наш раздел API, который объясняет это более подробно.
Теперь подключим остальную часть нашего
.
Как мы должны реализовать взаимодействие переключения задач? У увлеченного читателя уже есть ответ. Если ваша среда настроена и вы выполнили все до этого момента, сейчас хорошее время оставить это в стороне и реализовать функцию самостоятельно. Неудивительно, что мы подключаем наш
к отправке toggleTodo
аналогичным образом:
Скопируйте
import {connect} из «react-redux»;
импорт {toggleTodo} из «../redux/actions «;
const Todo =
экспорт по умолчанию connect (
null,
{toggleTodo}
) (Todo);
Теперь наши задачи можно полностью переключить. Мы почти готовы!
Наконец, давайте реализуем нашу функцию VisibilityFilters
.
Компонент
должен иметь возможность читать из хранилища, какой фильтр в настоящее время активен, и отправлять действия в хранилище.Следовательно, нам нужно передать как mapStateToProps
, так и mapDispatchToProps
. mapStateToProps
здесь может быть простым средством доступа к состоянию visibilityFilter
. А mapDispatchToProps
будет содержать создатель действия setFilter
.
Скопируйте
import {connect} из «react-redux»;
импорт {setFilter} из «../redux/actions»;
const VisibilityFilters =
const mapStateToProps = state => {
return {activeFilter: state.visibilityFilter};
};
экспорт подключения по умолчанию (
mapStateToProps,
{setFilter}
) (VisibilityFilters);
Между тем, нам также необходимо обновить наш компонент
для фильтрации задач в соответствии с активным фильтром. Ранее mapStateToProps
, которые мы передавали в вызов функции
connect
, был просто селектором, который выбирает весь список задач.Давайте напишем еще один селектор, который поможет фильтровать задачи по их статусу.
Копировать
export const getTodosByVisibilityFilter = (store, visibilityFilter) => {
const allTodos = getTodos (store)
switch (visibilityFilter) {
case VISIBILITY_FILTERS.COMPLETED = 9000Todos to to allTodos to. завершено)
case VISIBILITY_FILTERS.INCOMPLETE:
return allTodos.filter (todo =>! todo.completed)
case VISIBILITY_FILTERS.ALL:
по умолчанию:
вернуть allTodos
}
}
И подключиться к магазину с помощью селектора:
Копировать
const mapStateToProps = state => {
const {visibilityFilter} = state
const todos = getTodosByVisibilityFilter (state, visibilityFilter)
return {todos}
}
export default connect (mapStateToProps) (TodoList)
Итак, мы закончили очень простой пример приложения todo с React Redux.Все наши компоненты связаны! Разве это не хорошо? 🎉🎊
Ссылки #
Дополнительная помощь #
Основы Redux, Часть 1: Обзор Redux
- Что такое Redux и почему вы можете его использовать
- Основные части, из которых состоит приложение Redux
Введение #
Добро пожаловать в учебник по основам Redux! Это руководство познакомит вас с основными концепциями, принципами и шаблонами использования Redux . К тому времени, как вы закончите, вы должны понимать, какие части составляют приложение Redux, как потоки данных при использовании Redux и наши стандартные рекомендуемые шаблоны для создания приложений Redux.
В части 1 этого руководства мы кратко рассмотрим минимальный пример работающего приложения Redux, чтобы увидеть, что это за части, а в части 2: Концепции Redux и поток данных мы рассмотрим эти части более подробно. и как данные передаются в приложении Redux.
Начиная с части 3: Состояние, действия и редукторы, мы будем использовать эти знания для создания небольшого примера приложения, которое демонстрирует, как эти части сочетаются друг с другом, и поговорим о том, как Redux работает на практике. После того, как мы завершим создание рабочего примера приложения «вручную», чтобы вы могли точно видеть, что происходит, мы поговорим о некоторых стандартных шаблонах и абстракциях, обычно используемых с Redux.Наконец, мы увидим, как эти примеры нижнего уровня преобразуются в шаблоны более высокого уровня, которые мы рекомендуем для фактического использования в реальных приложениях.
Как читать этот учебник #
В этом руководстве вы узнаете, «как работает Redux» , а также , почему существуют эти шаблоны . Однако справедливое предупреждение — изучение концепций отличается от их применения на практике в реальных приложениях.
Исходный код будет менее лаконичным, чем то, как мы предлагаем написать код реального приложения , но записать его от руки — лучший способ научиться.Как только вы поймете, как все сочетается друг с другом, мы рассмотрим использование Redux Toolkit, чтобы упростить работу. Redux Toolkit — это рекомендуемый способ создания производственных приложений с Redux , он основан на всех концепциях, которые мы рассмотрим в этом руководстве. Как только вы поймете основные концепции, описанные здесь, вы поймете, как более эффективно использовать Redux Toolkit.
Если вы хотите узнать больше о том, как Redux используется для написания реальных приложений, см .:
Мы постарались сделать эти объяснения удобными для новичков, но нам нужно сделать некоторые предположения о том, что вы мы уже знаем, чтобы мы могли сосредоточиться на объяснении самого Redux. В этом руководстве предполагается, что вы знакомы с :
. Если вы еще не знакомы с этими темами, мы рекомендуем вам сначала освоить их, а затем вернуться, чтобы узнать о Redux . Мы будем здесь, когда вы будете готовы!
Наконец, вы должны убедиться, что в вашем браузере установлены расширения React и Redux DevTools:
- React DevTools Extension:
- Redux DevTools Extension:
What is Redux? #
Это помогает понять, что эта вещь «Redux» стоит на первом месте.Что оно делает? Какие проблемы это помогает мне решать? Зачем мне это использовать?
Redux — это шаблон и библиотека для управления и обновления состояния приложения с использованием событий, называемых «действиями». Он служит централизованным хранилищем состояния, которое необходимо использовать во всем приложении, с правилами, гарантирующими, что состояние может обновляться только предсказуемым образом.
Почему я должен использовать Redux? #
Redux помогает вам управлять «глобальным» состоянием — состоянием, которое необходимо во многих частях вашего приложения.
Шаблоны и инструменты, предоставляемые Redux, упрощают понимание того, когда, где, почему и как обновляется состояние в вашем приложении, и как логика вашего приложения будет вести себя, когда эти изменения произойдут . Redux направляет вас к написанию предсказуемого и тестируемого кода, что дает вам уверенность в том, что ваше приложение будет работать должным образом.
Когда мне следует использовать Redux? #
Redux помогает справиться с управлением общим состоянием, но, как и любой другой инструмент, у него есть компромиссы.Есть больше концепций, которые нужно изучить, и больше кода, который нужно написать. Он также добавляет некоторую косвенность в ваш код и просит вас соблюдать определенные ограничения. Это компромисс между краткосрочной и долгосрочной производительностью.
Redux более полезен, когда:
- У вас есть большое количество состояний приложения, которое необходимо во многих местах приложения
- Состояние приложения часто обновляется с течением времени
- Логика обновления этого состояния может быть сложной
- Приложение имеет кодовую базу среднего или большого размера, и над ним могут работать многие люди
Не всем приложениям нужен Redux.Найдите время, чтобы подумать о том, какое приложение вы создаете, и решить, какие инструменты лучше всего помогут решить проблемы, над которыми вы работаете.
Если вы не уверены, подходит ли Redux для вашего приложения, эти ресурсы содержат дополнительные рекомендации:
Библиотеки и инструменты Redux #
Redux — это небольшая автономная библиотека JS. Однако он обычно используется с несколькими другими пакетами:
React-Redux #
Redux может интегрироваться с любой структурой пользовательского интерфейса и наиболее часто используется с React. React-Redux — наш официальный пакет, который позволяет вашим компонентам React взаимодействовать с хранилищем Redux, считывая части состояния и отправляя действия для обновления хранилища.
Redux Toolkit #
Redux Toolkit — это наш рекомендуемый подход для написания логики Redux. Он содержит пакеты и функции, которые, по нашему мнению, необходимы для создания приложения Redux. Redux Toolkit строится на основе предлагаемых нами лучших практик, упрощает большинство задач Redux, предотвращает распространенные ошибки и упрощает написание приложений Redux.
Redux DevTools Extension #
Redux DevTools Extension показывает историю изменений состояния в вашем хранилище Redux с течением времени. Это позволяет эффективно отлаживать ваши приложения, в том числе с использованием мощных методов, таких как «отладка во времени».
Основы Redux #
Теперь, когда вы знаете, что такое Redux, давайте кратко рассмотрим, из каких частей состоит приложение Redux и как оно работает.
Остальная часть описания на этой странице посвящена исключительно основной библиотеке Redux (пакет redux
).Мы поговорим о других пакетах, связанных с Redux, в оставшейся части учебника.
Магазин Redux #
Центром каждого приложения Redux является магазин . «Хранилище» — это контейнер, в котором хранится глобальное состояние вашего приложения .
Хранилище — это объект JavaScript с несколькими специальными функциями и возможностями, которые отличают его от простого глобального объекта:
- Вы никогда не должны напрямую изменять или изменять состояние, которое хранится внутри хранилища Redux.
- Вместо этого, единственный способ вызвать обновление состояния — создать простой объект action , который описывает «что-то, что произошло в приложении», а затем отправить действие в магазин, чтобы сообщить ему, что произошло.
- Когда действие отправлено, хранилище запускает функцию root reducer и позволяет ей вычислять новое состояние на основе старого состояния и действия
- Наконец, хранилище уведомляет подписчиков , что состояние было обновлено, поэтому пользовательский интерфейс может быть обновлен новыми данными.
Redux Core Example App #
Давайте посмотрим на минимальный рабочий пример Redux-приложения — небольшое приложение-счетчик:
Поскольку Redux является автономной библиотекой JS без зависимостей, этот пример написан путем загрузки только одного скрипта для библиотеки Redux и использует базовый JS и HTML для пользовательского интерфейса.На практике Redux обычно используется путем установки пакетов Redux из NPM, а пользовательский интерфейс создается с использованием такой библиотеки, как React.
Давайте разберем этот пример на отдельные части, чтобы увидеть, что происходит.
Состояние, действия и редукторы #
Начнем с определения начального значения состояния для описания приложения:
Копировать
const initialState = {
Значение
: 0
}
Для этого приложения мы собираемся отслеживать одно число с текущим значением нашего счетчика.
Приложения Redux обычно имеют объект JS в качестве корневой части состояния с другими значениями внутри этого объекта.
Затем мы определяем функцию редуктора . Редуктор получает два аргумента: текущее состояние
и
действие
объект, описывающий произошедшее. Когда приложение Redux запускается, у нас еще нет состояния,
поэтому мы предоставляем initialState
в качестве значения по умолчанию для этого редуктора:
Копировать
function counterReducer (state = initialState, action) {
switch (action.type) {
case ‘counter / incremented’:
return {… состояние, значение: state.value + 1}
case ‘counter / incremented’:
return {… состояние, значение: состояние .value — 1}
по умолчанию:
состояние возврата
}
}
Объекты действия всегда имеют поле типа
, которое представляет собой строку, которую вы предоставляете
действует как уникальное имя для действия. Тип
должно быть читабельным именем, чтобы
любой, кто смотрит на этот код, понимает, что он означает.В этом случае мы используем
слово «counter» как первая половина нашего типа действия, а вторая половина — это
описание «случившегося». В этом случае наш «счетчик» был «увеличен», поэтому
мы записываем тип действия как «счетчик / увеличиваемый»
.
В зависимости от типа действия нам необходимо либо вернуть новый объект в
быть результатом нового состояния
или вернуть существующий объект состояния
, если ничего не должно измениться.
Обратите внимание, что мы обновляем состояние неизменяемо , копируя существующее состояние и обновляя
копировать, а не изменять исходный объект напрямую.
Store #
Теперь, когда у нас есть функция редуктора, мы можем создать экземпляр store ,
вызов библиотеки Redux createStore
API.
Копировать
const store = Redux.createStore (counterReducer)
Мы передаем функцию reducer в createStore
, который использует функцию reducer
для генерации начального состояния и для расчета любых будущих обновлений.
UI #
В любом приложении пользовательский интерфейс отображает текущее состояние на экране.Когда пользователь
что-то делает, приложение обновит свои данные, а затем перерисует пользовательский интерфейс с этими значениями.
Копировать
const valueEl = document.getElementById (‘value’)
function render () {
const state = store.getState ()
valueEl.innerHTML = state.value.toString ()
}
render ()
store.subscribe (render)
В этом небольшом примере мы используем только некоторые базовые элементы HTML в качестве нашего пользовательского интерфейса,
с одним
Итак, мы пишем функцию, которая знает, как получить последнее состояние из Redux
store с помощью метода store.getState ()
, затем принимает это значение и обновляет пользовательский интерфейс, чтобы отобразить его.
Магазин Redux позволяет нам вызвать store.subscribe ()
и передать функцию обратного вызова подписчика, которая будет вызываться
каждый раз магазин обновляется. Итак, мы можем передать нашу функцию render
в качестве подписчика и знать, что
каждый раз, когда магазин обновляется, мы можем обновить пользовательский интерфейс до последнего значения.
Redux — это автономная библиотека, которую можно использовать где угодно. Это также означает, что его можно использовать с любым слоем пользовательского интерфейса.
Dispatching Actions #
Наконец, нам нужно реагировать на ввод пользователя, создавая объекты action , которые
Опишите, что произошло, и отправили их в магазин. Когда мы вызываем store.dispatch (действие)
,
магазин запускает редуктор, вычисляет обновленное состояние и запускает подписчиков
для обновления пользовательского интерфейса.
Копия
документа.getElementById (‘приращение’). addEventListener (‘щелчок’, функция () {
store.dispatch ({type: ‘counter / incremented’})
})
document.getElementById (‘декремент’). addEventListener ( ‘щелчок’, функция () {
store.dispatch ({тип: ‘счетчик / уменьшение’})
})
документ
.getElementById (‘incrementIfOdd’)
.addEventListener (‘щелчок’, функция () {
if (store.getState (). Value% 2! == 0) {
store.dispatch ({type: ‘counter / incremented’})
Redux Essentials, Часть 1: Обзор и концепции Redux
- Что такое Redux и почему вы можете его использовать
- Ключевые термины и концепции Redux
- Как потоки данных через приложение Redux
Введение #
Добро пожаловать в учебное пособие по Redux Essentials! Это руководство познакомит вас с Redux и научит, как правильно его использовать, используя наши последние рекомендуемые инструменты и лучшие практики .К тому времени, когда вы закончите, вы сможете начать создавать свои собственные приложения Redux, используя инструменты и шаблоны, которые вы узнали здесь.
В Части 1 этого руководства мы рассмотрим ключевые концепции и термины, которые вам необходимо знать для использования Redux, а в Части 2: Структура приложения Redux мы рассмотрим базовое приложение React + Redux, чтобы увидеть, как сочетаются части все вместе.
Начиная с части 3: «Базовый поток данных Redux», мы будем использовать эти знания для создания небольшого приложения для каналов социальных сетей с некоторыми реальными функциями, посмотрим, как эти части фактически работают на практике, и поговорим о некоторых важных шаблонах и рекомендациях. для использования Redux.
Как читать этот учебник #
Эта страница будет посвящена тому, чтобы показать вам, , как правильно использовать Redux, и объяснить достаточно концепций, чтобы вы могли понять, как правильно создавать приложения Redux.
Мы постарались сделать эти объяснения удобными для новичков, но нам нужно сделать некоторые предположения о том, что вы уже знаете:
Если вы еще не знакомы с этими темами, мы рекомендуем вам потратить некоторое время на то, чтобы сначала освоитесь с ними, а затем вернитесь, чтобы узнать о Redux .Мы будем здесь, когда вы будете готовы!
Убедитесь, что в вашем браузере установлены расширения React и Redux DevTools:
- React DevTools Extension:
- Redux DevTools Extension:
What is Redux? #
Это помогает понять, что это » Redux «стоит на первом месте. Что оно делает? Какие проблемы это помогает мне решать? Зачем мне это использовать?
Redux — это шаблон и библиотека для управления и обновления состояния приложения с использованием событий, называемых «действиями». Он служит централизованным хранилищем состояния, которое необходимо использовать во всем приложении, с правилами, гарантирующими, что состояние может обновляться только предсказуемым образом.
Почему я должен использовать Redux? #
Redux помогает вам управлять «глобальным» состоянием — состоянием, которое необходимо во многих частях вашего приложения.
Шаблоны и инструменты, предоставляемые Redux, упрощают понимание того, когда, где, почему и как обновляется состояние в вашем приложении, и как логика вашего приложения будет вести себя, когда эти изменения произойдут .Redux направляет вас к написанию предсказуемого и тестируемого кода, что дает вам уверенность в том, что ваше приложение будет работать должным образом.
Когда мне следует использовать Redux? #
Redux помогает справиться с управлением общим состоянием, но, как и любой другой инструмент, у него есть компромиссы. Нужно изучить больше концепций и написать больше кода. Он также добавляет некоторую косвенность в ваш код и просит вас соблюдать определенные ограничения. Это компромисс между краткосрочной и долгосрочной производительностью.
Redux более полезен, когда:
- У вас есть большое количество состояний приложения, которое необходимо во многих местах приложения
- Состояние приложения часто обновляется с течением времени
- Логика обновления этого состояния может быть сложной
- Приложение имеет кодовую базу среднего или большого размера, и над ним могут работать многие люди
Не всем приложениям нужен Redux.Найдите время, чтобы подумать о том, какое приложение вы создаете, и решить, какие инструменты лучше всего помогут решить проблемы, над которыми вы работаете.
Если вы не уверены, подходит ли Redux для вашего приложения, эти ресурсы содержат дополнительные рекомендации:
Библиотеки и инструменты Redux #
Redux — это небольшая автономная библиотека JS. Однако он обычно используется с несколькими другими пакетами:
React-Redux #
Redux может интегрироваться с любой структурой пользовательского интерфейса и наиболее часто используется с React. React-Redux — наш официальный пакет, который позволяет вашим компонентам React взаимодействовать с хранилищем Redux, считывая части состояния и отправляя действия для обновления хранилища.
Redux Toolkit #
Redux Toolkit — это наш рекомендуемый подход для написания логики Redux. Он содержит пакеты и функции, которые, по нашему мнению, необходимы для создания приложения Redux. Redux Toolkit строится на основе предлагаемых нами лучших практик, упрощает большинство задач Redux, предотвращает распространенные ошибки и упрощает написание приложений Redux.
Redux DevTools Extension #
Redux DevTools Extension показывает историю изменений состояния в вашем хранилище Redux с течением времени. Это позволяет эффективно отлаживать ваши приложения, в том числе с использованием мощных методов, таких как «отладка во времени».
Термины и концепции Redux #
Прежде чем мы погрузимся в реальный код, давайте поговорим о некоторых терминах и концепциях, которые вам необходимо знать для использования Redux.
State Management #
Давайте начнем с небольшого компонента счетчика React.Он отслеживает число в состоянии компонента и увеличивает число при нажатии кнопки:
Копировать
function Counter () {
const [counter, setCounter] = useState (0)
const increment = () => {
setCounter (prevCounter => prevCounter + 1)
}
return (
Value: {counter}
)
}
Это автономное приложение со следующими частями:
- Состояние , источник истины, который движет нашим приложением;
- Представление , декларативное описание пользовательского интерфейса на основе текущего состояния
- Действия , события, которые происходят в приложении на основе пользовательского ввода, и запускающие обновления в состоянии
Это небольшой пример «одностороннего потока данных» :
- Состояние описывает состояние приложения в определенный момент времени
- Пользовательский интерфейс отображается на основе этого состояния
- Когда что-то происходит (например, пользователь нажимает кнопка), состояние обновляется в зависимости от того, что произошло.
- Пользовательский интерфейс повторно визуализируется на основе нового состояния
Однако простота может нарушиться, когда у нас есть нескольких компонентов, которые должны совместно использовать и использовать одно и то же состояние , особенно если эти компоненты расположены в разных частях приложения.Иногда это можно решить, «подняв состояние» до родительских компонентов, но это не всегда помогает.
Один из способов решить эту проблему — извлечь общее состояние из компонентов и поместить его в централизованное место за пределами дерева компонентов. Благодаря этому наше дерево компонентов становится большим «представлением», и любой компонент может получить доступ к состоянию или запускать действия, независимо от того, где они находятся в дереве!
Определяя и разделяя концепции, участвующие в управлении состоянием и применяя правила, которые поддерживают независимость между представлениями и состояниями, мы придаем нашему коду больше структуры и удобства сопровождения.
Это основная идея Redux: единое централизованное место для хранения глобального состояния в вашем приложении и определенных шаблонов, которым необходимо следовать при обновлении этого состояния, чтобы сделать код предсказуемым.
Неизменяемость #
«Изменяемый» означает «изменчивый». Если что-то «неизменное», это никогда не может быть изменено.
Все объекты и массивы JavaScript по умолчанию изменяемы. Если я создаю объект, я могу изменить содержимое его полей. Если я создам массив, я также могу изменить его содержимое:
Копировать
const obj = {a: 1, b: 2}
obj.b = 3
const arr = [‘a’, ‘b’]
arr.push (‘c’)
arr [1] = ‘d’
Это называется изменением объекта или массива. Это тот же объект или ссылка на массив в памяти, но теперь содержимое внутри объекта изменилось.
Для неизменного обновления значений ваш код должен сделать копий существующих объектов / массивов, а затем изменить копии .
Мы можем сделать это вручную, используя операторы распределения массивов / объектов в JavaScript, а также методы массива, которые возвращают новые копии массива вместо изменения исходного массива:
Копировать
const obj = {
a: {
c : 3
},
b: 2
}
const obj2 = {
…obj,
a: {
… obj.a,
c: 42
}
}
const arr = [‘a’, ‘b’]
const arr2 = arr. concat (‘c’)
const arr3 = arr.slice ()
arr3.push (‘c’)
Redux ожидает, что все обновления состояния будут выполняться постоянно . Мы рассмотрим, где и как это важно, немного позже, а также несколько более простых способов написать неизменную логику обновления.
Для получения дополнительной информации о том, как неизменяемость работает в JavaScript, см .:
Терминология #
Есть некоторые важные термины Redux, с которыми вам необходимо ознакомиться, прежде чем мы продолжим:
Действия #
Действие — это простой объект JavaScript, имеющий поле типа
. Вы можете думать о действии как о событии, которое описывает что-то, что произошло в приложении .
Поле типа
должно быть строкой, дающей этому действию описательное имя, например «todos / todoAdded»
. Обычно мы пишем эту строку типа, например «domain / eventName»
, где первая часть — это функция или категория, к которой принадлежит это действие, а вторая часть — это конкретное событие, которое произошло.
Объект действия может иметь другие поля с дополнительной информацией о том, что произошло.По соглашению мы помещаем эту информацию в поле с именем payload
.
Типичный объект действия может выглядеть так:
Copy
const addTodoAction = {
type: ‘todos / todoAdded’,
payload: ‘Buy milk’
}
Action Creators #
Создатель действий — это функция, которая создает и возвращает объект действия. Обычно мы используем их, поэтому нам не нужно каждый раз писать объект действия вручную:
Копировать
const addTodo = text => {
return {
type: ‘todos / todoAdded’,
payload: text
}
}
Редукторы #
Редуктор — это функция, которая принимает текущее состояние
и объект действия
, решает, как обновить состояние, если необходимо, и возвращает новое состояние: (состояние , действие) => newState
. Редуктор можно рассматривать как прослушиватель событий, который обрабатывает события в зависимости от типа полученного действия (события).
Функции «Reducer» получили свое название, потому что они похожи на функцию обратного вызова, которую вы передаете методу Array.reduce ()
.
Редукторы должны всегда следовать некоторым определенным правилам:
- Они должны только вычислять новое значение состояния на основе состояния
аргументы
- Им не разрешено изменять существующее состояние
- Они не должны выполнять асинхронную логику, вычислять случайные значения или вызывать другие «побочные эффекты».
Мы поговорим подробнее о правилах редукторов позже, в том числе о том, почему они важны и как им правильно следовать.
Логика внутри функций редуктора обычно следует той же серии шагов:
- Проверить, заботится ли редуктор об этом действии
- Если да, сделайте копию состояния, обновите копию новыми значениями и верните ее
- В противном случае вернуть существующее состояние без изменений
Вот небольшой пример редуктора, показывающий шаги, которые должен выполнять каждый редуктор:
Копировать
const initialState = {value: 0}
function counterReducer (state = initialState , действие) {
если (действие.type === ‘counter / increment’) {
return {
… state,
value: state.value + 1
}
}
return state
}
Редукторы могут использовать любые своего рода логика внутри, чтобы решить, каким должно быть новое состояние: if / else
, switch
, циклы и так далее.
Подробное объяснение: почему они называются «редукторами»?
Метод Array.reduce ()
позволяет вам брать массив значений, обрабатывать каждый элемент в массиве по одному и возвращать единственный окончательный результат.Вы можете думать об этом как о «уменьшении массива до одного значения».
Array.reduce ()
принимает в качестве аргумента функцию обратного вызова, которая будет вызываться один раз для каждого элемента в массиве. Он принимает два аргумента:
-
previousResult
, значение, которое ваш обратный вызов возвратил в последний раз -
currentItem
, текущий элемент в массиве
При первом запуске обратного вызова не будет previousResult
доступно, поэтому нам нужно также передать начальное значение, которое будет использоваться в качестве первого previousResult
.
Если бы мы хотели сложить массив чисел, чтобы узнать, какова общая сумма, мы могли бы написать обратный вызов уменьшения, который выглядит следующим образом:
Копировать
const numbers = [2, 5, 8]
const addNumbers = ( previousResult, currentItem) => {
console.log ({previousResult, currentItem})
return previousResult + currentItem
}
const initialValue = 0
const total = numbers.reduce (addNumbers, initialValue)
.log (total)
Обратите внимание, что этой функции «уменьшения обратного вызова» addNumber
не нужно ничего отслеживать. Он принимает аргументы previousResult
и currentItem
, что-то делает с ними и возвращает новое значение результата.
Функция редуктора Redux — это точно такая же идея, что и функция «обратного вызова уменьшения»! Он принимает «предыдущий результат» (состояние ,
) и «текущий элемент» (объект действия ), определяет новое значение состояния на основе этих аргументов и возвращает это новое состояние.
Если бы мы должны были создать массив действий Redux, вызвать reduce ()
и передать функцию редуктора, мы бы получили конечный результат таким же образом:
Копировать
const actions = [
{type: ‘counter / increment’},
{type: ‘counter / increment’},
{type: ‘counter / increment’}
]
const initialState = {value: 0}
const finalResult = действия. reduce (counterReducer, initialState)
console.log (finalResult)
Можно сказать, что редукторы Redux сокращают набор действий (с течением времени) до единого состояния .Разница в том, что с Array.reduce ()
это происходит сразу, а с Redux — в течение всего срока службы вашего запущенного приложения.
Store #
Текущее состояние приложения Redux находится в объекте, называемом store .
Хранилище создается путем передачи редуктора и имеет метод getState
, который возвращает значение текущего состояния:
Копировать
import {configureStore} from ‘@ reduxjs / toolkit’
const store = configureStore ({reducer : counterReducer})
консоль.log (store.getState ())
Dispatch #
В хранилище Redux есть метод под названием dispatch
. Единственный способ обновить состояние — вызвать store.dispatch ()
и передать объект действия . Магазин запустит свою функцию редуктора и сохранит внутри новое значение состояния, и мы можем вызвать getState ()
для получения обновленного значения:
Копировать
store.dispatch ({type: ‘counter / increment’})
console .log (store.getState ())
Диспетчерские действия можно рассматривать как «запуск события» в приложении.Что-то случилось, и мы хотим, чтобы об этом знал магазин. Редукторы действуют как прослушиватели событий, и когда они слышат интересующее их действие, они обновляют состояние в ответ.
Обычно мы вызываем создателей действий для выполнения правильного действия:
Копировать
const increment = () => {
return {
type: ‘counter / increment’
}
}
store.dispatch (increment ())
console.log (store.getState ())
Селекторы #
Селекторы — это функции, которые знают, как извлекать определенные фрагменты информации из значения состояния хранилища.По мере роста приложения это может помочь избежать повторения логики, поскольку разные части приложения должны читать одни и те же данные:
Копировать
const selectCounterValue = state => state.value
const currentValue = selectCounterValue (store.getState ())
console.log (currentValue)
Redux Application Data Flow #
Ранее мы говорили об «одностороннем потоке данных», который описывает следующую последовательность шагов для обновления приложения:
- State описывает состояние приложение в определенный момент времени
- Пользовательский интерфейс отображается на основе этого состояния
- Когда что-то происходит (например, пользователь нажимает кнопку), состояние обновляется на основе того, что произошло
- Пользовательский интерфейс повторно отображается на основе новое состояние
В частности, для Redux мы можем разбить эти шаги более подробно:
- Начальная настройка:
- Хранилище Redux создается с использованием функции корневого редуктора
- Хранилище вызывает корневой r educer один раз и сохраняет возвращаемое значение в качестве исходного состояния
- Когда пользовательский интерфейс сначала визуализируется, компоненты пользовательского интерфейса получают доступ к текущему состоянию хранилища Redux и используют эти данные, чтобы решить, что отображать.Они также подписываются на любые будущие обновления магазина, чтобы знать, изменилось ли состояние.
- Обновления:
- Что-то происходит в приложении, например, пользователь нажимает кнопку
- Код приложения отправляет действие в хранилище Redux, например отправку
({type: 'counter / increment'})
- Магазин снова запускает функцию редуктора с предыдущим состоянием
- Магазин уведомляет все части пользовательского интерфейса, на которые подписана подписка, о том, что магазин был обновлено
- Каждый компонент пользовательского интерфейса, которому нужны данные из хранилища, проверяет, изменились ли части состояния, которые им необходимы.
- Каждый компонент, который видит, что его данные изменились, вызывает повторную визуализацию с новыми данными, чтобы он мог обновить то, что отображается на экране
Вот как этот поток данных выглядит визуально:
What You ‘ ve Learned #
В Redux есть ряд новых терминов и концепций, которые нужно запомнить. Напоминаем, что мы только что рассмотрели:
- Redux — это библиотека для управления глобальным состоянием приложения
- Redux обычно используется с библиотекой React-Redux для интеграции Redux и React вместе
- Redux Toolkit — рекомендуемый способ для написания логики Redux
- Redux использует структуру приложения «односторонний поток данных»
- Состояние описывает состояние приложения в определенный момент времени, и пользовательский интерфейс отображается на основе этого состояния
- Когда что-то происходит в приложение:
- Пользовательский интерфейс отправляет действие
- В магазине запускаются редукторы, и состояние обновляется на основе того, что произошло.
- Магазин уведомляет пользовательский интерфейс об изменении состояния
- Пользовательский интерфейс повторно отображает на основе новое состояние
- Redux использует несколько типов кода
- Действия представляют собой простые объекты с полем
типа
и описывают «что t произошло «в приложении - Редукторы — это функции, которые вычисляют новое значение состояния на основе предыдущего состояния + действие
- Хранилище Redux запускает корневой редуктор всякий раз, когда действие отправлено
- Действия представляют собой простые объекты с полем
Что Далее? #
Мы рассмотрели каждую отдельную часть приложения Redux.Затем перейдите к части 2: Структура приложения Redux, где мы рассмотрим полный рабочий пример, чтобы увидеть, как части сочетаются друг с другом.
Полное руководство по React Redux для начинающих (2019)
Пытаясь понять Redux, действительно сбивает с толку, как все это работает. Тем более как новичок.
Столько терминологии! Действия, редукторы, создатели действий, промежуточное ПО, чистые функции, неизменяемость, преобразователи…
Как все это сочетается с React для создания рабочего приложения?
Вы можете часами читать блоги и пробовать разбираться в сложных приложениях «реального мира», пытаясь собрать их воедино.
В этом руководстве по Redux я собираюсь объяснить, как использовать Redux с React постепенно — начиная с простого React — и на очень простом примере React + Redux. Я объясню , почему каждая функция полезна (и когда вы можете пропустить некоторые из них).
Затем мы рассмотрим более сложные темы одна за другой, пока вы не разберетесь в полностью . Ну вот 🙂
Небольшое предупреждение: этот учебник — completeeete . Под этим я подразумеваю и .Я превратил это в полноценный бесплатный курс, и . Я сделал красивый PDF-файл, который вы можете читать на своем iPad или [на любом устройстве Android, которое сейчас круто]. Оставьте свой адрес электронной почты в коробке, чтобы получить и то, и другое прямо сейчас.
Успех! Теперь проверьте свою электронную почту.
(Я знаю, как вкладки браузера выходят из-под контроля. Так много интересного для чтения, так мало времени!)
Основы Redux в одном видео
Если вы предпочитаете просмотр чтению, в этом видео рассказывается, как добавить Redux в приложение React, шаг за шагом:
Это аналогично первой части этого руководства, где мы берем простое приложение React и добавляем в него Redux по частям.
Или продолжайте читать! В приведенном ниже руководстве рассматривается все, что есть в видео, а также некоторые другие.
Стоит ли использовать Redux?
Особенно актуально в 2020 году задаться вопросом … стоит ли вам по-прежнему использовать Redux? Есть ли что-нибудь получше с хуками, контекстом или какой-нибудь другой библиотекой?
Краткий ответ: даже с большим количеством альтернатив Redux еще не умер. Но имеет ли это смысл для вашего приложения… ну, это зависит от обстоятельств.
Супер просто? Только несколько бит состояния в одном или двух местах? Состояние локального компонента, вероятно, будет отличным.Вы можете сделать это с помощью классов, хуков или того и другого.
Немного сложнее, с несколькими «глобальными» вещами, которыми нужно поделиться в приложении? Контекстный API может быть идеальным для вас.
Много глобального состояния с взаимодействием между отключенными частями приложения? Или большое приложение, которое со временем станет только больше? Попробуйте Redux.
Вы всегда можете добавить Redux и позже . Вам не нужно принимать решение в первый день. Начните с простого и добавляйте сложности, когда и где вам это нужно.
Знаете ли вы, как реагировать?
React можно использовать самостоятельно без Redux. Redux — это надстройка к React.
Даже если вы собираетесь использовать их оба, я очень рекомендую сначала изучить только React без Redux. Разберитесь в свойствах, состоянии и одностороннем потоке данных и научитесь «думать в React», прежде чем пытаться изучить Redux. Изучение их обоих одновременно — верный путь к путанице. У меня есть подробное руководство по React, которое охватывает все важные вещи, которые вам нужно знать.
Я тоже собрал это в 5-дневный бесплатный курс! (это довольно длинный учебник, поэтому имеет смысл разложить его)
Успех! Теперь проверьте свою электронную почту.
Преимущества Redux
Если вы использовали React более нескольких минут, вы, вероятно, знаете о свойствах и одностороннем потоке данных. Данные передаются вниз дерева компонентов через props. Учитывая такой компонент:
Счетчик
, хранящийся в состоянии приложения
, будет передан как опора:
Чтобы данные возвращались вверх по дереву , они должны пройти через функцию обратного вызова, поэтому эта функция обратного вызова должна сначала быть передана вниз любым компонентам, которые хотят вызвать ее для передачи данных вверх.
Вы можете представить себе данные как электричество , подключенные цветными проводами к компонентам, которые заботятся о нем. Данные передаются по этим проводам вниз и вверх, но их нельзя провести сквозь воздух — их нужно соединить цепью от одного компонента к другому.
Передача данных на нескольких уровнях — это боль
Рано или поздно вы столкнетесь с ситуацией, когда в контейнере верхнего уровня есть некоторые данные, а эти данные нужны дочернему элементу на 4+ уровня ниже. Вот пример из Twitter со всеми выделенными аватарами:
Давайте представим, что компонент приложения верхнего уровня
удерживает в состоянии объект пользователя
. пользователь
содержит аватар, дескриптор и другую информацию профиля текущего пользователя.
Чтобы доставить данные пользователя
ко всем компонентам 3 Avatar
, пользователь должен быть прописан через кучу промежуточных компонентов, которым данные не нужны.
Получение данных там похоже на продвижение иглы в горной экспедиции. Подождите, в этом нет никакого смысла. Так или иначе, — это боль . Также известен как «буровая установка».
Что еще более важно, это не очень хороший дизайн программного обеспечения. Промежуточные компоненты вынуждены принимать и передавать реквизиты, которые им не нужны. Это означает, что рефакторинг и повторное использование этих компонентов будет сложнее, чем нужно.
Было бы неплохо, если бы компоненты, которым не нужны данные, вообще не видели их?
Redux — один из способов решения этой проблемы.
Передача данных между соседними компонентами
Если у вас есть компоненты-братья и сестры, которым необходимо обмениваться данными, способ сделать это в React — перетащить эти данные вверх в родительский компонент и передать их с помощью props.
Это может быть громоздко. Redux может помочь, предоставив вам одного глобального «родителя», где вы можете хранить данные, а затем вы можете подключить
дочерние компоненты к данным с помощью React-Redux.
Используйте React-Redux для подключения данных к любому компоненту
Используя функцию connect
, которая поставляется с react-redux
, вы можете подключить любой компонент к хранилищу Redux и получить необходимые данные.
Redux также делает некоторые другие интересные вещи, такие как упрощение отладки (Redux DevTools позволяет вам проверять каждое изменение состояния), отладка во времени (вы можете откатить изменения состояния и посмотреть, как ваше приложение выглядело в прошлом) и это может сделать ваш код более удобным в обслуживании в долгосрочной перспективе.Это также научит вас больше узнать о функциональном программировании.
Встроенные альтернативы Redux
Если Redux кажется излишним для вашей ситуации, обратите внимание на эти альтернативы. Они встроены прямо в React.
Альтернатива Redux: API контекста React
Под капотом React-Redux использует встроенный контекстный API React для передачи данных. Если хотите, можете исключить посредника и напрямую использовать Context. Вы упустите прекрасные возможности Redux, упомянутые выше, но если ваше приложение простое и вам нужен простой способ передачи данных, Context может быть идеальным.
Поскольку вы здесь, я предполагаю, что вы хотите изучить Redux, и я не буду прямо здесь сравнивать Redux с Context API или хуками useContext и useReducer. Вы можете узнать больше по этим ссылкам.
Если вы хотите глубоко погрузиться в контекст API, посмотрите мой курс React Context for State Management на сайте egghead.
Другая альтернатива: используйте children
Prop
В зависимости от того, как вы структурируете свое приложение, вы можете более напрямую передавать данные дочерним компонентам, используя комбинацию дочерних объектов
и других свойств в качестве «слотов».Вы можете эффективно пропустить несколько уровней в иерархии, если вы все устроите правильно.
У меня есть статья об этом шаблоне «слотов» и о том, как организовать дерево компонентов для более эффективной передачи данных.
Изучите Redux, начиная с Plain React
Мы собираемся применить инкрементный подход, начав с простого приложения React с состоянием компонентов, добавляя части Redux по частям и по ходу исправляя ошибки. Назовем это «Разработка, управляемая ошибками» 🙂
Вот счетчик:
В этом примере компонент Counter хранит состояние, а приложение, окружающее его, является простой оболочкой.
Counter.js
импортировать React из react;
class Counter extends React.Component {
state = {count: 0}
инкремент = () => {
this.setState ({
count: this.state.count + 1
});
}
декремент = () => {
this.setState ({
count: this.state.count - 1
});
}
render () {
возвращение (
Счетчик
{это.state.count}
)
}
}
экспорт счетчика по умолчанию;
В качестве краткого обзора, вот как это работает:
- Счетчик
Состояние
хранится в компонентесчетчика
- Когда пользователь нажимает «+», вызывается обработчик кнопки
onClick
, который вызывает функциюприращения
. - Функция приращения
- Поскольку состояние было изменено, React повторно визуализирует компонент
Counter
(и его дочерние элементы), и отображается новое значение счетчика.
Если вам нужно больше подробностей о том, как работают изменения состояния, прочтите «Наглядное руководство по состоянию в React», а затем вернитесь сюда.
А если серьезно: если выше был обзор , а не , вам нужно узнать, как работает состояние React , прежде чем вы изучите Redux, иначе это будет очень запутанно. Пройдите мой бесплатный 5-дневный курс React, обретите уверенность в простом React и возвращайтесь сюда.
Follow Along!
Лучший способ научиться этому — попробовать! Итак, вот CodeSandbox, где вы можете следить:
-> Открыть этот CodeSandbox в отдельной вкладке
Я настоятельно рекомендую вам синхронизировать CodeSandbox с учебником и вводить примеры по мере продвижения.
Добавить Redux в приложение React
В CodeSandbox разверните раздел «Зависимости» на левой панели и нажмите «Добавить зависимость».
Найдите redux
, добавьте его, затем снова нажмите «Добавить зависимость», найдите response-redux
и добавьте его.
В локальном проекте их можно установить с помощью Yarn или NPM: npm install --save redux react-redux
.
redux против React-redux
redux
дает вам хранилище и позволяет вам сохранять в нем состояние, выводить состояние и реагировать при изменении состояния. Но это все, что он делает.
На самом деле это react-redux
, которое позволяет вам соединять части состояния с компонентами React.
Верно: redux
вообще ничего не знает о React .
Но эти библиотеки похожи на две капли воды в контейнере. В 99,999% случаев, когда кто-либо упоминает «Redux» в контексте React, они имеют в виду обе эти библиотеки в тандеме. Так что имейте это в виду, когда вы увидите упоминание Redux в StackOverflow, Reddit или где-то еще.
Библиотека redux
также может использоваться вне приложения React. Он будет работать с приложениями Vue, Angular и даже серверными приложениями Node / Express.
Redux имеет одно глобальное хранилище
Мы начнем с рассмотрения только самого Redux и только одной его части: магазина .
Мы говорили о том, как Redux поддерживает состояние вашего приложения в одном хранилище . И как вы можете извлечь части этого состояния и подключить их к своим компонентам в качестве свойств.
Вы часто будете видеть слова «состояние» и «магазин» как синонимы. Технически состояние — это данные, а хранилище — это место, где они хранятся.
Итак: на первом этапе рефакторинга с простого React на Redux нам нужно создать хранилище для хранения состояния.
Создание Redux Store
Redux поставляется с удобной функцией, которая создает магазины, и называется она createStore
. Достаточно логично, а?
Давайте создадим магазин в index.js
. Импортируйте createStore
и назовите его так:
index.js
импорт {createStore} из "redux";
const store = createStore ();
const App = () => (
<Счетчик />
);
Это должно завершиться ошибкой «Ожидалось, что редуктор является функцией.”
Магазину нужен редуктор
Итак, вот что особенность Redux: он не очень умный.
Можно было ожидать, что, создав магазин, он придаст вашему состоянию хорошее значение по умолчанию. Может быть, пустой объект?
Но нет. Здесь нет соглашения о конфигурации.
Redux делает ноль предположений о форме вашего состояния. Это может быть объект, число, строка или что угодно. Тебе решать!
Мы должны предоставить функцию, которая будет возвращать состояние.Эта функция называется редуктором (мы поймем, почему через минуту). Итак, давайте сделаем действительно простой, передадим его в createStore
и посмотрим, что произойдет:
index.js
редуктор функции (состояние, действие) {
console.log ('редуктор', состояние, действие);
вернуть состояние;
}
const store = createStore (редуктор);
После внесения этого изменения откройте консоль (в CodeSandbox нажмите кнопку «Консоль» внизу).
Вы должны увидеть записанное там сообщение, примерно такое:
(буквы и цифры после INIT рандомизируются Redux)
Обратите внимание, как Redux вызывал ваш редуктор во время создания магазина.(Чтобы доказать это: поместите console.log
сразу после вызова createStore
и обратите внимание, как он распечатывается после редуктора)
Также обратите внимание, как Redux передал состояние
из undefined
, и действие было объектом со свойством типа
.
Подробнее о действиях поговорим через минуту. А пока давайте рассмотрим редуктор .
Что такое редуктор Redux?
Термин «редуктор» может показаться немного пугающим и непривычным, но после этого раздела, я думаю, вы согласитесь, что это, как говорится, «просто функция.”
Вы когда-нибудь использовали функцию reduce
для массива?
Вот как это работает: вы передаете ему функцию, и она вызывает вашу функцию один раз для каждого элемента массива, аналогично тому, как работает map
— вы, вероятно, знакомы с map
из списков рендеринга вещей в React.
Ваша функция вызывается с двумя аргументами: результатом последней итерации и текущим элементом массива. Он объединяет текущий элемент с предыдущим «итоговым» результатом и возвращает новый итог.
Это станет более понятным на примере:
var letter = ['r', 'e', 'd', 'u', 'x'];
// `reduce` принимает 2 аргумента:
// - функция для уменьшения (можно сказать "редуктор")
// - начальное значение для accumulatedResult
var word = letter.reduce (
function (AccumulatedResult, arrayItem) {
возврат накопленного результата + arrayItem;
},
''); // <- обратите внимание на этот пустой строковый аргумент: это начальное значение
console.log (word) // => "redux"
Функцию, которую вы передаете в reduce
, можно по праву назвать «reducer»… потому что она сокращает весь массив элементов до единственного результата.
Redux — это , по сути, — модная версия Array reduce
. Ранее вы видели, что редукторы Redux имеют эту сигнатуру:
(состояние, действие) => newState
Значение: он берет текущее состояние
и действие
и возвращает newState
. Очень похоже на подпись редуктора Array.reduce
!
(AccumulatedValue, nextItem) => nextAccumulatedValue
Редукторы
Redux работают так же, как функция, которую вы передаете в Array.уменьшить! 🙂 То, что они уменьшают, — это действия. Они сводят набор действий (с течением времени) к единому состоянию . Разница в том, что с Array reduce это происходит сразу, а с Redux — в течение всего срока службы вашего запущенного приложения.
Если вы все еще не уверены, ознакомьтесь с моим руководством по работе редукторов Redux. В противном случае, давайте двигаться вперед.
Привести редуктору начальное состояние
Помните, что задача редуктора — взять текущее состояние
и действие
и вернуть новое состояние.
У него есть еще одна задача: он должен возвращать начальное состояние при первом вызове. Это что-то вроде «начальной загрузки» вашего приложения. Где-то нужно начинать, верно?
Идиоматический способ сделать это — определить переменную initialState
и использовать синтаксис аргумента по умолчанию ES6, чтобы присвоить ей состояние
.
Поскольку мы собираемся переместить наше состояние Counter
в Redux, давайте прямо сейчас настроим его начальное состояние.Внутри компонента Counter
наше состояние представлено как объект со счетчиком
, поэтому здесь мы отразим ту же форму.
index.js
const initialState = {
количество: 0
};
редуктор функции (состояние = начальное состояние, действие) {
console.log ('редуктор', состояние, действие);
вернуть состояние;
}
Если вы посмотрите на консоль еще раз, вы увидите, что она напечатана {count: 0}
как значение для состояния
. Вот чего мы хотим.
Итак, это подводит нас к важному правилу о редукторах.
Важное правило редукторов №1 : Никогда не возвращайте undefined из редуктора.
Вы всегда хотите, чтобы ваше состояние было определено. Определенное состояние — это счастливое состояние. Определенное состояние un — это un happy (и, вероятно, сломает ваше приложение).
Диспетчерские действия по изменению состояния
Ага, сразу два новых условия: мы собираемся «разослать» какие-то «действия».
Что такое действие Redux?
Действие — это Redux-язык для простого объекта со свойством type
.Вот и все. Следуя этим 2 правилам, это действие:
{
тип: "добавить товар",
товар: «Яблоко»
}
Это тоже действие:
Вот еще один:
Действия — вещи очень свободной формы. Пока это объект с типа
, это честная игра.
Для того, чтобы все было разумно и поддерживаемым, w
React Redux Tutorial for Beginners: The Complete Guide (2020)
Самый простой учебник по React Redux, который я хотел бы иметь, когда начал учиться.Включает Redux Toolkit!
Когда я только начал изучать Redux, я хотел бы иметь «Redux для чайников». Я чувствовал себя тупым, потому что не мог осмыслить действия, создатели действий, редукторы . Как насчет промежуточного программного обеспечения? Ты меня разыгрываешь!
Итак, в те дни я начал преподавать Redux, чтобы узнать больше, одновременно с написанием собственного учебника по React Redux. С тех пор я многому научился, и это руководство по React Redux — попытка помочь вам изучить Redux самым простым способом .
Этот учебник также включает Redux Toolkit .
Psst .. Cerchi un tutorial Redux in italiano?
Прочтите это: Redux, где мы сейчас?
Redux сильно изменил . Инструментарий Redux стал рекомендуемым способом использования Redux , новые кодовые базы, основанные на наборе инструментов Redux, будут сильно отличаться от того, что мы использовали для написания в прошлом.
Однако фундаментальными строительными блоками Redux по-прежнему являются действий, редукторов, промежуточного программного обеспечения и хранилища , и вам нужно хорошее знание этих блоков lego, чтобы владеть Redux и Redux toolkit .
В этом руководстве я сначала расскажу о «классическом» коде Redux, а также о React / Redux с и подключусь к
. Позже вы увидите, как выполнить рефакторинг до Redux Toolkit . Если вы предпочитаете смотреть видео, попробуйте , этот мой бесплатный курс Redux .
А теперь наслаждайтесь чтением!
СОДЕРЖАНИЕ
Руководство по
React Redux: для кого это руководство для
Следующее руководство по React Redux предназначено для разработчиков JavaScript, хорошо разбирающихся в ES6 и React .В Интернете есть бесчисленное количество руководств по React, если вы хотите освежить свое понимание, но если вы только начинаете, я предлагаю прочитать «Начало работы с React» Тани Расция.
Учебник
React Redux: что вы узнаете
Руководство охватывает в основном Redux с React из-за широкого распространения этой комбинации, но Redux также может использоваться как автономная библиотека без какой-либо внешней оболочки / библиотеки.
Итак, из следующего руководства вы узнаете:
- что такое Redux
- Строительные блоки Redux
- как использовать Redux автономно
- как использовать Redux с React
Учебное пособие по React Redux: минимальная среда разработки React
Перед тем, как начать, убедитесь, что у вас установлена среда разработки React . .
Чтобы сделать его, вы можете следовать инструкциям по настройке React, webpack и Babel или, что еще лучше, использовать create-response-app:
npx create-response-app реагировать-redux-tutorial
После того, как вы закончите, все готово.
Учебник
React Redux: в каком состоянии?
Что такое Redux ? Чтобы ответить на этот вопрос, мы должны сначала поговорить о состоянии в веб-приложениях JavaScript . Рассмотрим простой пользовательский поток:
«как пользователь я могу щелкнуть кнопку с именем« Нажми меня, и модальное окно должно появиться вскоре после этого ».
Угадайте, что, даже в этом тривиальном взаимодействии есть состояние , с которым мы должны иметь дело. Например, мы можем описать начальное состояние приложения как простой объект JavaScript:
const state = {
buttonClicked: 'нет',
modalOpen: 'нет'
}
Когда пользователь нажимает, состояние меняется, и мы имеем:
const state = {
buttonClicked: 'да',
modalOpen: 'да'
}
Как вы надежно отслеживаете эти изменения состояния ? Что, если состояние изменено случайно по какой-то несвязанной логике? Есть ли библиотека, которая может нам помочь?
Также, если вы работали с React до того, как термин «состояние» вас не удивит.Полагаю, вы уже написали некий компонент React с отслеживанием состояния, например,
импортировать React, {компонент} из "реагировать";
class ExampleComponent расширяет Component {
constructor (props) {
супер (реквизит);
this.state = {
статьи: [
{title: "Учебное пособие по React Redux для начинающих", id: 1},
{title: "Учебник по TypeScript для начинающих", id: 2}
]
};
}
render () {
const {статьи} = this.state;
return {article.map (el => - {el.title}
)}
;
}
}
Компонент React с отслеживанием состояния — это класс JavaScript (с хуками React, которые больше не используются).
В компоненте React состояние содержит данные, которые могут быть отображены для пользователя . Состояние в React также может измениться в ответ на действия и события : на самом деле вы можете обновить состояние локального компонента с помощью this.setState ()
.
Итак, в целом типичное приложение JavaScript имеет состояние .Например, состояние:
- что видит пользователь (данные)
- данные, которые мы получаем из API
- URL
- элементы, выбранные на странице
- возможных ошибок для показа пользователю
Ознакомившись с основами, давайте теперь поговорим о , какую проблему пытается решить Redux .
Какую проблему решает Redux?
Итак, состояние присутствует везде в веб-приложении . С этого момента я буду говорить о состоянии и логике в контексте типичного приложения React, но считает, что эти концепции применимы к любой архитектуре внешнего интерфейса , независимо от конкретной библиотеки.Дело в том, можете ли вы представить, какое состояние имеет приложение веб-приложения?
Даже простое одностраничное приложение может выйти из-под контроля без четких границ между всеми слоями приложения . Это особенно верно в отношении React.
Да, вы можете обойтись, сохраняя состояние в родительском компоненте React (или в контексте), пока приложение остается небольшим. Тогда все станет сложнее, особенно когда вы добавите больше поведений в приложение .В какой-то момент в вам может понадобиться согласованный способ отслеживания изменений состояния . Не только, я бы сказал, что компоненты внешнего интерфейса не должны знать о бизнес-логике . Когда-либо.
К сожалению, сегодня в компоненты внешнего интерфейса вбивается тонна логики . Есть ли альтернатива этой агонии?
Redux может решить именно эти проблемы. Вначале может быть не ясно, но Redux помогает дать каждому компоненту внешнего интерфейса точную часть состояния, в которой он нуждается. .
Еще лучше, Redux может хранить бизнес-логику внутри своего собственного уровня (промежуточного программного обеспечения) вместе с кодом для выборки данных. У этого подхода много преимуществ.
В следующих разделах мы увидим, когда и как использовать Redux в ваших приложениях . Сначала пару слов о типичном вопросе: «Следует ли мне использовать Redux»?
Учебник
React Redux: следует ли использовать Redux?
Должен признать Я здесь немного предвзят , потому что мне нравится Redux, и я предлагаю его, когда присоединяюсь к новому проекту.Но , возможно, вам не нужен ни Redux, ни альтернативы, такие как Mobx и его друзья . Еще больше, если вы работаете над простым проектом .
Например, в React есть способов избежать использования Redux: дочерние реквизиты, контекстный API .
Но для средних и крупных проектов я всегда считал Redux (или его эквивалент) почти обязательным: библиотек управления состоянием прекрасно удерживают логику и поведение, абстрагированные от UI . Тестируемость пользовательского интерфейса стремительно растет, а значит, продуктивность разработчиков — .Если вы посмотрите на это с другой точки зрения, вы можете возразить, что Redux имеет свою цену. Он добавляет вашему приложению еще один уровень абстракции.
Какую стоимость вы готовы заплатить? Сложные компоненты внешнего интерфейса или несколько уровней абстракций ?
Хватит говорить. В следующем разделе мы начнем создавать доказательство концепции, чтобы представить:
- Основные принципы Redux
- Redux вместе с React
Опять же, убедитесь, что у вас есть среда разработки React, готовая к использованию .
Учебник
React Redux: знакомство с магазином Redux
Когда я впервые обратился к Redux, я был сбит с толку. Слишком много терминологии: действие , редуктор, промежуточное ПО . Но помимо этого мне было непонятно, как были склеены все движущиеся части ? Были какие-то миньоны что ли? В Redux нет миньонов (к сожалению) .
То, что управляет всеми винтиками, — это магазин . Повторите со мной: в магазине .Хранилище в Redux — это своего рода волшебство, и хранит все состояние приложения.
Итак, давайте создадим магазин , чтобы начать играть с Redux. Перейдите в среду разработки React и установите Redux:
компакт-диск React-redux-tutorial
npm я сокращение --save-dev
Создайте каталог для магазина:
Затем создайте новый файл src / js / store / index.js и инициализируйте хранилище:
import {createStore} из "redux";
импортировать rootReducer из "../reducers/index ";
const store = createStore (rootReducer);
экспорт магазина по умолчанию;
Как видите, хранилище является результатом вызова createStore , функции из библиотеки Redux. createStore принимает в качестве первого аргумента редуктор, и в нашем случае мы передали rootReducer (еще не представленный).
Вы также можете передать начальное состояние в createStore , что полезно для рендеринга на стороне сервера и предварительной загрузки состояния , но пока нас это не интересует.Самая важная концепция, которую необходимо понять, заключается в том, что состояние в Redux исходит от редукторов . Повторимся: редукторов формируют состояние вашего приложения .
Вооружившись этими знаниями, давайте перейдем к , нашему первому редуктору Redux .
Смотрите ветку на Github
React Redux tutorial: знакомство с редукторами Redux
Что такое редуктор? Редуктор Redux — это просто функция JavaScript . Он принимает два параметра : текущее состояние и действие (подробнее о действиях скоро).
В типичном компоненте React локальное состояние может быть изменено на месте. В Redux этого делать нельзя. Третий принцип Redux (изложенный его создателем) предписывает, что состояние является неизменным и не может измениться на месте .
Другими словами редуктор должен быть чистым. Чистая функция возвращает точно такой же вывод для данного ввода. Несмотря на эту терминологию , рассуждать о редукторе не так уж сложно .
В нашем примере мы создадим простой редуктор , который принимает начальное состояние и действие в качестве параметров.Создайте каталог для корневого редуктора:
Затем создайте новый файл, src / js / reducers / index.js :
const initialState = {
статьи: []
};
function rootReducer (state = initialState, action) {
вернуть состояние;
};
экспортировать rootReducer по умолчанию;
Обратите внимание, как начальное состояние передается как параметр по умолчанию. Но с теперь наш редуктор ничего не делает, кроме возврата исходного состояния .
В следующем разделе мы добавим в микс экшен , и все станет интересно.
Смотрите ветку на Github
React Redux tutorial: знакомство с действиями Redux и именованными константами
Редукторы
Redux, без сомнения, самая важная концепция Redux. Редукторы создают состояние приложения. Но как редуктор узнает, когда нужно сгенерировать следующее состояние?
Второй принцип Redux гласит, что единственный способ изменить состояние — это отправить сигнал в хранилище. Этот сигнал — действие .Итак, « отправляет действие » означает, что отправляет сигнал в магазин.
Запутались? Обнадеживает то, что действия Redux — это не что иное, как объекты JavaScript . Так выглядит действие:
{
тип: 'ADD_ARTICLE',
полезная нагрузка: {title: 'React Redux Tutorial', id: 1}
}
Как видите, это объект JavaScript с двумя свойствами: тип и полезная нагрузка .
Свойство type определяет, как должно изменяться состояние, и оно всегда требуется Redux.Вместо этого свойство payload описывает, что следует изменить, и может быть опущено , если у вас нет новых данных для сохранения в магазине.
В качестве передовой практики в Redux мы заключаем каждое действие в функцию , так что создание объекта абстрагируется. Такая функция получила название создателя действий : давайте соберем все вместе, создав простой создатель действий.
Создать каталог для действий:
Затем создайте новый файл src / js / actions / index.js :
функция экспорта addArticle (payload) {
вернуть {тип: "ADD_ARTICLE", полезная нагрузка}
};
Вы можете заметить, что свойство type является строкой. Строки подвержены опечаткам и дублируют , поэтому лучше объявлять действия как константы . Создайте для них новую папку:
mkdir -p src / js / константы
Затем создайте новый файл, src / js / constants / action-types.js :
экспортная константа ADD_ARTICLE = "ADD_ARTICLE";
Теперь откройте снова src / js / actions / index.js и обновите действие, чтобы использовать типы действий:
импортировать {ADD_ARTICLE} из "../constants/action-types";
функция экспорта addArticle (payload) {
вернуть {тип: ADD_ARTICLE, полезная нагрузка};
}
Как видите, мы начинаем работать с несколькими файлами. Это главная неприятность Redux для большинства людей. Вы можете следовать соглашению Redux duck, если хотите, оно хранит все связанные с Redux элементы в одном файле.
На один шаг ближе к рабочему приложению Redux, но для сначала нам нужно настроить редуктор для улавливания нового действия .
Смотрите ветку на Github
Рефакторинг редуктора
Мы оставили последний раздел со следующим вопросом: как редуктор узнает, когда генерировать следующее состояние ? Ключевым моментом здесь является магазин Redux. Когда действие отправлено, хранилище пересылает сообщение (объект действия) редуктору .
В этот момент редуктор говорит: «О, давайте посмотрим на свойство type этого действия». Затем , в зависимости от типа действия, редуктор создает следующее состояние , в конечном итоге объединяет полезные данные действия в новое состояние.
Ранее мы создали редуктор, который ничего не делает. Давайте исправим! Откройте src / js / reducers / index.js и обновите reducer с помощью оператора if для , проверяя тип действия :
импортировать {ADD_ARTICLE} из "../constants/action-types";
const initialState = {
статьи: []
};
function rootReducer (state = initialState, action) {
if (action.type === ADD_ARTICLE) {
state.articles.push (действие.payload);
}
вернуть состояние;
}
экспортировать rootReducer по умолчанию;
Также есть строка кода, которая переводит полезные данные действия в начальное состояние .Кажется, поступить правильно. Но это же неправильно ! Наш редуктор нарушает основной принцип Redux: неизменяемость .
Array.prototype.push
— это нечистая функция : она изменяет исходный массив. Но это еще не все. Мы также меняем исходное состояние на месте.
Нам нужно исправить. Сначала мы можем вернуть новый объект JavaScript с Object.assign
. Таким образом мы сохраняем исходное состояние без изменений. Затем мы можем использовать массив .prototype.concat
вместо Array.prototype.push
для сохранения исходного массива:
импортировать {ADD_ARTICLE} из "../constants/action-types";
const initialState = {
статьи: []
};
function rootReducer (state = initialState, action) {
if (action.type === ADD_ARTICLE) {
return Object.assign ({}, состояние, {
статьи: state.articles.concat (action.payload)
});
}
вернуть состояние;
}
экспортировать rootReducer по умолчанию;
Теперь начальное состояние осталось нетронутым, и результирующее состояние является просто копией начального состояния .Запомните два ключевых момента для , избегая мутаций в Redux :
- используйте оператор concat, slice или spread для массивов
- используйте
Object.assign
или объект распространения объектов
Если этот материал о неизменности кажется вам шаблонным, стартовый комплект redux решил проблему. Взгляните на него, когда закончите это руководство.
В следующем разделе мы поиграем с Redux из консоли браузера . Держись крепче!
Смотрите ветку на Github
Учебное пособие по React Redux: методы хранилища Redux
Это будет очень быстро, обещаю.
Вы можете быть удивлены, узнав, что сам Redux представляет собой небольшую библиотеку (2 КБ), а наиболее важные методы — это всего три :
- getState для чтения текущего состояния приложения
- отправка для отправки акции
- подписаться на для прослушивания изменений состояния
Мы будем играть с ними в консоли браузера. Для этого нам нужно экспортировать хранилище и созданное нами действие как глобальные переменные.Создайте новый файл с именем src / js / index.js и поместите туда следующий код:
импортный магазин из "../js/store/index";
импортировать {addArticle} из "../js/actions/index";
window.store = магазин;
window.addArticle = addArticle;
Теперь откройте src / index.js , очистите его содержимое и обновите его следующим образом:
индекс импорта из "./js/index";
Теперь запустите сервер разработки с:
перейдите по адресу http: // localhost: 3000 / и откройте консоль с помощью F12.Начните с , считывая текущее состояние :
Ноль статей. На самом деле мы еще не обновили начальное состояние. Чтобы сделать вещи интересными, мы можем прослушивать обновления состояния с помощью подписки .
Метод подписки принимает обратный вызов, который срабатывает всякий раз, когда отправляется действие. Отправка действия означает уведомление магазина о том, что мы намерены изменить состояние.
Зарегистрируйте обратный звонок с помощью:
store.subscribe (() => console.log ('Смотри, мама, Redux !!'));
Чтобы изменить состояние в Redux, нам нужно отправить действие . Чтобы отправить действие, мы вызовем метод отправки. На данный момент у нас есть одно действие: addArticle для добавления нового элемента в состояние. Отправим действие с:
store.dispatch (addArticle ({title: 'Учебное пособие по React Redux для начинающих', id: 1}));
Сразу после выполнения приведенного выше кода вы должны увидеть «Смотри ма, Redux !!». Чтобы убедиться, что состояние изменилось, запустите снова:
Вот и все. Это Redux в самом простом виде. Это было сложно?
Как только вы почувствуете себя уверенно, переходите к следующим разделам. Мы сразу перейдем к , подключив React к Redux !
Учебник
React Redux: подключение React с Redux
Изучив Redux, я понял, что это не так уж и сложно. Я знал, как получить доступ к текущему состоянию с помощью getState. Я знал, как отправить действие с помощью отправки и как отслеживать изменения состояния с помощью подписки.
Но я не знал, как соединить вместе React и Redux.Я спрашивал себя: следует ли вызывать getState из React? Как отправить действие из компонента React? И так далее.
Redux не зависит от платформы . Вы можете использовать его с ванильным Javascript. Или с Angular. Или с React. Есть привязки для объединения Redux с вашим любимым фреймворком / библиотекой.
Для React существует react-redux , библиотека, для которой вам нужно выучить всего один метод: подключить . Что оно делает? Неудивительно, что связывает компонент React с хранилищем Redux .
Вы будете использовать connect с двумя или тремя аргументами в зависимости от варианта использования:
- a функция mapStateToProps (вы можете назвать ее также «select»)
- a mapDispatchToProps функция
mapStateToProps делает именно то, что предполагает его название: он соединяет часть состояния Redux с реквизитами компонента React. Таким образом, подключенный компонент React получит доступ к точной части магазина, которая ему нужна .
mapDispatchToProps делает нечто подобное, но для действий. mapDispatchToProps связывает действия Redux с реквизитами React . Таким образом, подключенный компонент React сможет отправлять сообщения в магазин .
В следующем разделе мы, наконец, запачкаем руки. Мы создадим сверхпростое приложение из трех компонентов:
- компонент приложения
- Компонент «Список» для отображения статей
- Компонент формы для добавления новых статей
Учебное пособие по React Redux: компонент приложения и хранилище Redux
Перед запуском установите react-redux с:
npm я реагирую-redux --save-dev
Я сказал, что mapStateToProps связывает часть состояния Redux с реквизитами компонента React.Вы можете задаться вопросом: достаточно ли этого для соединения Redux с React ? Нет, это не так. Нам также нужен провайдер , высокоуровневый компонент из react-redux .
Откройте src / index.js , удалите все и обновите файл следующим кодом:
импортировать React из "react";
import {render} из "react-dom";
импортировать {Provider} из "react-redux";
импортировать магазин из "./js/store/index";
импортировать приложение из "./js/components/App";
визуализировать (
<Приложение />
,
документ.getElementById ("корень")
);
Как видите, Provider завершает ваше приложение React и сообщает ему обо всем хранилище Redux.
Теперь создадим компонент App. Ничего особенного: приложение должно импортировать компонент List и отрисовывать себя. Создайте каталог для хранения компонентов:
mkdir -p SRC / JS / компоненты
и новый файл с именем src / js / components / App.js :
импортировать React из "react";
Список импорта из "./Список";
const App = () => (
Статьи
<Список />
);
экспортировать приложение по умолчанию;
Сохраните и закройте файл, затем перейдите к созданию списка.
Учебное пособие по React Redux: список компонентов и состояние Redux
Пока ничего особенного не сделали. Но наш новый компонент List будет взаимодействовать с хранилищем Redux .
Краткое резюме: ключ для , соединяющий компонент React с Redux, — это connect .Connect принимает хотя бы один аргумент.
Поскольку мы хотим, чтобы List получал список статей, нужно соединить state.articles с компонентом. Как? С mapStateToProps . Обратите внимание, что «mapStateToProps» — это просто соглашение, большинство разработчиков, например, используют «select».
Создайте новый файл с именем src / js / components / List.js . Должно получиться так:
импортировать React из "react";
импорт из "react-redux";
const mapStateToProps = state => {
вернуть {статьи: состояние.статьи};
};
const ConnectedList = ({статьи}) => (
{article.map (el => (
- {el.title}
))}
);
const List = connect (mapStateToProps) (ConnectedList);
экспорт списка по умолчанию;
Компонент List получает опору article , которая является копией массива статей, который мы видели в состоянии Redux. Происходит от редуктора:
const initialState = {
статьи: []
};
function rootReducer (state = initialState, action) {
если (действие.type === ADD_ARTICLE) {
return Object.assign ({}, состояние, {
статьи: state.articles.concat (action.payload)
});
}
вернуть состояние;
}
Всегда помните: состояние в redux исходит от редукторов .
Наконец, компонент экспортируется как список. Список является результатом соединения компонента ConnectedList без сохранения состояния с хранилищем Redux.
Все еще не уверены? Чтобы понять, как работает Connect, потребуется время. . Но не бойтесь, путь к изучению Redux вымощен «ах-ха» моментами .
Я предлагаю сделать перерыв для изучения как connect, так и mapStateToProps.
Перейдите к следующему разделу, когда будете готовы!
Учебное пособие по React Redux: компонент формы и действия Redux
Компонент Form, который мы собираемся создать, немного сложнее, чем List. Это форма для добавления новых элементов в наше приложение, и для этого мы будем использовать класс JavaScript.
Заявление об ограничении ответственности : Я написал это руководство, когда в React еще не было хуков. Я мог бы использовать здесь функциональный компонент, но , чтобы не искажать учебник, я буду придерживаться классов .
Компонент класса поможет сохранить некоторое локальное состояние, например входные данные формы. Он также получает действие Redux . Таким образом, может обновить глобальное состояние, отправив действие addArticle .
Создайте новый файл в src / js / components / Form.js
со следующим кодом:
импортировать React, {Component} из "react";
импорт из "react-redux";
импортировать {addArticle} из "../actions/index";
function mapDispatchToProps (dispatch) {
возвращение {
addArticle: article => отправка (addArticle (статья))
};
}
class ConnectedForm расширяет Component {
constructor (props) {
супер (реквизит);
этот.state = {
заглавие: ""
};
this.handleChange = this.handleChange.bind (это);
this.handleSubmit = this.handleSubmit.bind (это);
}
handleChange (event) {
this.setState ({[event.target.id]: event.target.value});
}
handleSubmit (event) {
event.preventDefault ();
const {название} = this.state;
this.props.addArticle ({название});
this.setState ({название: ""});
}
render () {
const {название} = this.state;
возвращение (
);
}
}
const Form = connect (
значение NULL,
mapDispatchToProps
) (ConnectedForm);
экспорт формы по умолчанию;
Краткое описание компонента:
- стандартный материал Реагировать рядом с mapDispatchToProps и подключаться
- mapDispatchToProps связывает действия Redux с реквизитами React
- метод handleSubmit отправляет действие
Для подключения создателей действий к компоненту вы можете использовать форму функции:
function mapDispatchToProps (dispatch) {
возвращение {
addArticle: article => отправка (addArticle (статья))
};
}
В качестве альтернативы вы также можете передать mapDispatchToProps как объект.Взгляните сюда.
Наконец, компонент экспортируется как форма. Форма — это результат подключения ConnectedForm к хранилищу Redux.
Примечание: первый аргумент для подключения должен быть нулевым, когда mapStateToProps отсутствует, как в нашем примере . Или вы получите TypeError: отправка — это не функция.
Наши комплектующие готовы! Теперь обновите приложение, чтобы включить компонент формы:
импортировать React из "react";
импортировать список из "./List";
Импортировать форму из "./ Форма »;
const App = () => (
<>
Статьи
<Список />
Добавить новую статью
<Форма />
>
);
экспортировать приложение по умолчанию;
Запустите сервер разработки с:
Вы должны увидеть наше рабочее подтверждение концепции (в первой версии этого руководства я использовал Bootstrap, а затем удалил его).
Ничего особенного, но все же полезно для демонстрации работы React и Redux!
Компонент List слева обращается к хранилищу Redux.Он будет повторно отображаться всякий раз, когда вы добавляете новый элемент.
Вы сделали это! Но мы еще не закончили! В следующем разделе мы рассмотрим промежуточное ПО Redux . Держись крепче!
Смотрите ветку на Github
Учебник
React Redux: что такое промежуточное ПО Redux?
Пока что мы видели строительные блоки Redux: магазин , ГАИ. Reducers , который делает состояние в Redux.
Тогда есть действий, простых объектов JavaScript, действующих как мессенджеры в вашем приложении .Наконец, у нас есть создателей действий, функций для создания этих сообщений .
Теперь представьте себе следующий сценарий: вам нужно запретить пользователям создавать статьи с определенными словами внутри заголовка. Давайте посмотрим на handleSubmit
в Form.js
:
handleSubmit (событие) {
event.preventDefault ();
const {название} = this.state;
this.props.addArticle ({название});
this.setState ({название: ""});
}
Мы можем просто добавить чек перед this.props.addArticle
правильно? Это может быть что-то вроде этого:
handleSubmit (событие) {
event.preventDefault ();
const {название} = this.state;
const disabledWords = ['спам', 'деньги'];
const foundWord = disabledWords.filter (word => title.includes (word))
if (foundWord) {
вернуть this.props.titleForbidden ();
}
this.props.addArticle ({название});
this.setState ({название: ""});
}
Разве не весь смысл Redux в перемещении логики из наших компонентов React ? И что? Можем ли мы проверить свойство title внутри редуктора? Может быть! Пока там отправим другое действие в ответ на запрещенное слово… но как мне получить доступ к диспетчеризации внутри редуктора?
Понятно, что мы хотим чего-то другого. Похоже, мы хотим, чтобы проверил полезную нагрузку действия (и свойство title), прежде чем действие перейдет к редуктору . Должен быть способ подключения к потоку приложения . Угадайте, что это — именно то, что делает промежуточное ПО Redux .
Промежуточное ПО Redux — это функция, которая способна перехватывать наши действия и действовать соответствующим образом, прежде чем они достигнут редуктора .Хотя теория довольно проста, промежуточное ПО Redux может показаться немного запутанным.
В своей базовой форме промежуточное ПО Redux — это функция, возвращающая функцию, которая принимает следующий параметр в качестве параметра. Затем внутренняя функция возвращает другую функцию, которая выполняет действие в качестве параметра и, наконец, возвращает next (действие)
. Вот как это выглядит:
functionhibitedWordsMiddleware () {
return function (next) {
return function (action) {
вернуться далее (действие);
}
}
}
Последний пункт действительно важен: вы всегда должны вызывать next (action)
в промежуточном программном обеспечении.Если вы его забудете, Redux остановится, и никакие другие действия не достигнут редуктора. next (действие)
перемещает приложение вперед на , вызывая следующее промежуточное ПО в цепочке .
В промежуточном программном обеспечении вы также можете получить доступ к getState
и dispatch
:
functionhibitedWordsMiddleware ({getState, dispatch}) {
return function (next) {
return function (action) {
вернуться далее (действие);
}
}
}
Интересно также то, что промежуточное ПО не закрывается после next (action)
.Если вас интересует , считывающий следующее состояние приложения после того, как цепочка промежуточного программного обеспечения запускает , вы можете зафиксировать его с помощью getState
после next (действие)
:
functionhibitedWordsMiddleware ({getState, dispatch}) {
return function (next) {
return function (action) {
const nextAction = next (действие);
состояние константы = getState ();
return nextAction;
}
}
}
Тем не менее, не забудьте вернуть следующее действие в конце.
Я знаю, ты хочешь плакать и поменять карьеру, но терпи меня. Промежуточное ПО в Redux очень важно, потому что оно будет содержать основную часть логики вашего приложения. Если задуматься, то для абстрагирования бизнес-логики нет лучшего места, чем промежуточное ПО.
Вооружившись этими знаниями, мы можем создать наше первое промежуточное ПО Redux : оно должно проверять, есть ли в полезной нагрузке действия плохие слова. Мы увидим реальную реализацию в следующем разделе.
React Redux tutorial: ваше первое промежуточное ПО Redux
Промежуточное ПО, которое мы собираемся создать, должно проверять полезную нагрузку действия. Есть много преимуществ от использования промежуточного программного обеспечения Redux :
- большая часть логики может находиться вне библиотеки пользовательского интерфейса
- становится повторно используемым элементом логики, о
- можно тестировать изолированно
Промежуточное ПО
легко рассуждать
Промежуточное ПО
Итак, давайте запачкаем руки. Создайте новую папку для промежуточного программного обеспечения:
mkdir -p src / js / промежуточное ПО
Теперь создайте новый файл с именем src / js / middleware / index.js
. Структура нашего первого промежуточного программного обеспечения должна быть такой:
functionhibitedWordsMiddleware ({dispatch}) {
return function (next) {
return function (action) {
вернуться далее (действие);
}
}
}
На данный момент нам не нужен getState
, мы просто получаем dispatch
в качестве первого параметра. Ницца. Теперь нам нужно проверить полезную нагрузку действия, а именно свойство title. Если заголовок совпадает с одним или несколькими плохими словами, мы не позволяем пользователю добавлять статью.
Кроме того, проверка должна запускаться, только если действие имеет тип ADD_ARTICLE
. Это имеет смысл. Как насчет этого:
импортировать {ADD_ARTICLE} из "../constants/action-types";
const disabledWords = ["спам", "деньги"];
функция экспорта запрещеноWordsMiddleware ({dispatch}) {
return function (next) {
return function (action) {
if (action.type === ADD_ARTICLE) {
const foundWord = disabledWords.filter (word =>
действие.payload.title.includes (слово)
);
if (foundWord.length) {
возвратная отправка ({type: "FOUND_BAD_WORD"});
}
}
вернуться далее (действие);
};
};
}
Вот что делает промежуточное ПО: при типе действия ADD_ARTICLE
проверяет, не содержит ли action.payload.title
плохое слово. Если это так, то отправьте действие типа FOUND_BAD_WORD
, в противном случае позволит следующему промежуточному программному обеспечению запустить .
А теперь пришло время подключить hibiddenWordsMiddleware
к магазину Redux.Для этого нам нужно импортировать наше промежуточное ПО, еще одну утилиту из Redux (applyMiddleware), а затем приготовить все вместе.
Откройте src / js / store / index.js
и измените файл следующим образом:
import {createStore, applyMiddleware} из "redux";
импортировать rootReducer из "../reducers/index";
импортировать {disabledWordsMiddleware} из "../middleware";
const store = createStore (
rootReducer,
applyMiddleware (запрещенные словаMiddleware)
);
экспорт магазина по умолчанию;
Примечание : если вы хотите включить Redux Dev Tools, используйте этот код.
Сохраните и закройте файл, запустите npm start и проверьте, работает ли промежуточное ПО. Попробуйте добавить статью с «деньгами» в ее названии, и вы не увидите, что новая статья появится в списке .
Промежуточное ПО работает! Отличная работа! В следующих разделах мы рассмотрим асинхронные действия в Redux с Redux Thunk и Redux Saga .
Посмотреть урок 5 курса Redux, работа с промежуточным программным обеспечением
Смотрите ветку на Github
React Redux tutorial: асинхронные действия в Redux, наивный способ
До сих пор мы имели дело с синхронными данными .То есть отправляет действие синхронно . Нет AJAX, никаких обещаний. Мы возвращаем простой объект от наших создателей действий. И когда действие достигает редуктора, мы возвращаем следующее состояние.
Теперь предположим, что мы хотим получить данные из API . В React вы бы позвонили в componentDidMount и завершили бы его. Но как насчет Redux? Какое место подходит для вызова асинхронных функций ? Давайте подумаем об этом.
Редукторы? Ни за что. Редукторы должны оставаться сухими и чистыми.Редуктор — не лучшее место для асинхронной логики.
Действия? Как я должен это делать? Действия в Redux — это простые объекты. А как насчет создателей действий? Создатель действий — это функция, и это выглядит как хорошее место для вызова API! Давайте попробуем.
Мы создадим новое действие с именем getData . Это действие вызывает API с Fetch и возвращает действие Redux.
Откройте src / js / actions / index.js
и создайте новое действие с именем getData
:
функция экспорта getData () {
return fetch ("https: // jsonplaceholder.typicode.com/posts ")
.then (ответ => response.json ())
.then (json => {
вернуть {тип: "DATA_LOADED", полезная нагрузка: json};
});
}
В этом есть смысл. Будет ли работать ? Давайте подключим компонент React для отправки getData
из componentDidMount
. mapDispatchToProps
(на этот раз с сокращенной формой объекта) сопоставит создателей действий Redux с реквизитами нашего компонента. Создайте новый компонент React в src / js / components / Posts.js
:
импортировать React, {компонент} из "реагировать";
импорт из "react-redux";
импортировать {getData} из "../actions/index";
экспортный класс Post расширяет компонент {
constructor (props) {
супер (реквизит);
}
componentDidMount () {
this.props.getData ();
}
render () {
return null;
}
}
экспортировать соединение по умолчанию (
значение NULL,
{ получить данные }
)(Почта);
Redux Tutorial: Обзор и пошаговое руководство
Есть ли у вас опыт использования React? Вы слышали о Redux, но отложили изучение его, потому что он выглядит очень сложным, а все руководства кажутся ошеломляющими? Если это так, эта статья для вас! Сдерживайте свой страх сдерживания состояния и отправляйтесь со мной в это относительно безболезненное путешествие.
Предварительные требования
Вы должны уже знать, как использовать React для этого руководства, так как я не буду объяснять какие-либо аспекты самого React.
Также загрузите Redux DevTools для Chrome или FireFox.
Голы
В этом руководстве мы создадим небольшое приложение для блога. Он будет получать сообщения и комментарии из API. Я создал одно и то же приложение как с простым Redux, так и с Redux Toolkit (RTK), официально санкционированным набором инструментов для Redux. Вот ссылки на источник и демонстрации как простой, так и RTK-версии.
Приложение React + Redux (Обычный Redux)
Приложение React + Redux Toolkit
Примечание. Приложения извлекаются из реального API через JSON Placeholder API. Из-за ограничения скорости на CodeSandbox API может показаться медленным, но это не имеет ничего общего с самим приложением Redux. Вы также можете клонировать репозиторий локально.
Узнаем:
- Что такое Redux и , почему вам может понадобиться
- Терминология Redux: действия, редукторы, хранение, отправка, подключение и контейнер
- Выполнение асинхронных вызовов API с помощью Redux Thunk
- Как создать небольшое реальное приложение с React и Redux
- Как использовать Redux Toolkit для упрощения разработки приложений Redux
Что такое Redux?
Redux — это контейнер состояний для приложений JavaScript.Обычно с React вы управляете состоянием на уровне компонента и передаете состояние через свойства. С Redux все состояние вашего приложения управляется в одном неизменяемом объекте. Каждое обновление состояния Redux приводит к созданию копии разделов состояния плюс новое изменение.
Redux изначально был создан Дэном Абрамовым и Эндрю Кларком.
Почему я должен использовать Redux?
- Простое управление глобальным состоянием — доступ или обновление любой части состояния из любого компонента, подключенного к Redux
- Легко отслеживайте изменения с помощью Redux DevTools — любое действие или изменение состояния отслеживается и легко отслеживается с помощью Redux.Тот факт, что все состояние приложения отслеживается при каждом изменении, означает, что вы можете легко выполнять отладку во времени, чтобы перемещаться между изменениями.
Обратной стороной Redux является то, что нужно настраивать и поддерживать множество начальных шаблонов (особенно если вы используете простой Redux без Redux Toolkit). Приложение меньшего размера может не нуждаться в Redux и вместо этого может выиграть от простого использования Context API для нужд глобального состояния.
По моему личному опыту, я создал приложение только с Context, и позже мне потребовалось все преобразовать в Redux, чтобы сделать его более удобным в обслуживании и организованным.
Терминология
Обычно я не люблю просто составлять список терминов и определений, но в Redux есть несколько, которые, вероятно, незнакомы, поэтому я просто собираюсь определить их все заранее, чтобы было легче ссылаться на них. Хотя вы можете перейти к началу раздела руководства, я думаю, что было бы хорошо прочитать все определения, чтобы сначала получить представление о них и представить их в своей голове.
В качестве примеров я буду использовать типичное приложение задачи и действие удаления задачи.
Действия
Действие отправляет данные из вашего приложения в хранилище Redux. Действие обычно представляет собой объект с двумя свойствами: типа
и (необязательно) полезной нагрузки
. Тип обычно представляет собой строку в верхнем регистре (присвоенную константе), описывающую действие. Полезная нагрузка — это дополнительные данные, которые могут быть переданы.
Тип действия
const DELETE_TODO = 'DELETE_TODO'
Действие
{
тип: DELETE_TODO,
полезная нагрузка: идентификатор,
}
Создатели действий
Создатель действия — это функция, возвращающая действие.
Создатель действий
const deleteTodo = (id) => ({тип: DELETE_TODO, полезная нагрузка: id})
Редукторы
Редуктор — это функция, которая принимает два параметра: состояние
и действие
. Редуктор неизменяем и всегда возвращает копию всего состояния. Редуктор обычно состоит из оператора switch
, который перебирает все возможные типы действий.
Редуктор
const initialState = {
задачи: [
{id: 1, text: 'Ешь'},
{id: 2, text: 'Sleep'},
],
загрузка: ложь,
hasErrors: ложь,
}
function todoReducer (state = initialState, action) {
переключатель (действие.тип) {
case DELETE_TODO:
возвращение {
...штат,
задачи: state.todos.filter ((todo) => todo.id! == action.payload),
}
по умолчанию:
состояние возврата
}
}
Магазин
Состояние приложения Redux находится в хранилище , которое инициализируется редуктором. При использовании с React существует
, чтобы обернуть приложение, и все внутри Provider может иметь доступ к Redux.
Магазин
импорт {createStore} из "redux"
импортировать {Provider} из 'response-redux'
импортировать редуктор из './ редукторы
const store = createStore (редуктор)
визуализировать (
<Приложение />
,
document.getElementById ('корень'),
)
Отправка
отправка
— это метод, доступный для объекта хранилища, который принимает объект, который используется для обновления состояния Redux. Обычно этот объект является результатом вызова создателя действия.
const Component = ({dispatch}) => {
useEffect (() => {
отправка (deleteTodo ())
}, [отправка])
}
Подключить
Функция connect ()
— это один из типичных способов подключения React к Redux.Связанный компонент иногда называют контейнером .
Хорошо, это касается основных условий Redux. Читать терминологию без какого-либо контекста может быть затруднительно, поэтому приступим.
Начало работы
Для облегчения быстрого начала работы в моем примере для настройки среды используется приложение Create React.
npx create-react-app redux-tutorial
cd redux-tutorial
Redux требует нескольких зависимостей.
Вы можете yarn добавить
или npm i
их, и я также буду использовать react-router-dom
, но это все для дополнительных зависимостей.
npm i \
редукция \
реакция-редукция \
redux-thunk \
redux-devtools-extension \
реагировать-маршрутизатор-дом
И удалите все шаблоны. Вместо этого мы добавим все необходимое с нуля.
Мы создадим каталоги для Redux reducer
и Redux действий
, а также страниц
и компонентов
, с которыми вы уже должны быть знакомы по React.
mkdir действия компоненты компоненты страницы редукторы
И мы вернем index.js
, App.js
и index.css
.
сенсорный index.js index.css App.js
Итак, на данный момент дерево каталогов вашего проекта выглядит следующим образом.
└── src /
├── действия /
├── компоненты /
├── страницы /
├── редукторы /
├── App.js
├── index.css
└── index.js
Для индекса .css
, просто возьмите его содержимое и вставьте. Я собираюсь остановиться только на функциональности, а не на стиле, поэтому я просто написал несколько очень простых стилей, чтобы сайт выглядел достаточно прилично.
Теперь у нас достаточно шаблона, чтобы начать работу, поэтому мы приступим к работе над точкой входа.
Настройка Redux Store
Когда я только начал изучать Redux, это показалось мне таким ошеломляющим, потому что каждое приложение, которое я смотрел, было настроено немного по-другому.Посмотрев на множество более современных приложений и взяв аспекты, которые были общими для всех из них, я получил хорошее представление о том, что действительно должно быть в приложении Redux, и что люди просто перемещают вещи в быть уникальный.
Существует множество руководств, которые покажут вам, как получить очень простой магазин Redux с настроенными задачами, но я не считаю это очень полезным для понимания того, как настроить уровень производства, поэтому я собираюсь установить все необходимое с самого начала.Даже в этом случае будут некоторые упрямые аспекты, потому что Redux очень гибкий.
В index.js
мы добавим несколько вещей.
-
createStore
, чтобы создать хранилище, которое будет поддерживать состояние Redux -
applyMiddleware
, чтобы иметь возможность использовать промежуточное ПО, в данном случаеthunk
-
Провайдер
, чтобы обернуть все приложение в Redux -
thunk
, промежуточное ПО, которое позволяет нам выполнять асинхронные действия в Redux -
composeWithDevTools
, код, который подключает ваше приложение к Redux DevTools
индекс.js
импортировать React из 'React'
import {render} из 'react-dom'
импортировать {createStore, applyMiddleware} из redux
импортировать {Provider} из 'response-redux'
импортировать преобразователь из 'redux-thunk'
импортировать {composeWithDevTools} из 'redux-devtools-extension'
импортировать приложение из './App'
импортировать rootReducer из './reducers'
import './index.css'
const store = createStore (rootReducer, composeWithDevTools (applyMiddleware (thunk)))
визуализировать (
<Приложение />
,
документ.getElementById ('корень'),
)
Поместите компонент в App.js
. Мы изменим это позже, но пока мы просто хотим, чтобы приложение работало.
App.js
импортировать React из React
const App = () => {
return Привет, Redux
}
экспорт приложения по умолчанию
Ввод редукторов
Последнее, что нужно сделать, это ввести редуктор. Редуктор — это функция, которая определяет изменения в состоянии Redux.Это чистая функция, которая возвращает копию состояния с новым изменением.
Изящная особенность Redux заключается в том, что мы можем иметь много редукторов и объединять их все в один корневой редуктор, который использует хранилище, используя combReducers
. Это приводит к тому, что мы можем легко организовать наш код, сохраняя при этом все в одном корневом дереве состояний.
Поскольку это приложение будет похоже на блог, в нем будет список сообщений, и мы сейчас поместим его в сообщенийReducer
.Наличие этого метода combReducers
позволяет нам добавлять все, что мы хотим — commentsReducer
, authReducer и т. Д.
Создайте файл, который объединит все редукторы в redurs / index.js
.
редукторов / index.js
импорт {combReducers} из "redux"
импортировать postsReducer из './postsReducer'
const rootReducer = combReducers ({
posts: postsReducer,
})
экспорт по умолчанию rootReducer
Наконец, мы сделаем сообщений Reducer
.Мы можем настроить его с начальным состоянием. Как и следовало ожидать от обычного компонента React, у нас будет состояние , загружающее
и hasErrors
, а также массив сообщений
, в котором будут жить все сообщения. Сначала мы установим его без каких-либо действий в переключателе, просто случай по умолчанию, который возвращает все состояние.
Редукторы / посты
Reducer.js
экспорт const initialState = {
сообщений: [],
загрузка: ложь,
hasErrors: ложь,
}
экспортировать функцию по умолчанию postsReducer (state = initialState, action) {
переключатель (действие.тип) {
по умолчанию:
состояние возврата
}
}
Теперь у нас, по крайней мере, достаточно настроек, чтобы приложение загружалось без сбоев.
Redux Инструменты разработчика
После загрузки приложения и настройки Redux
мы можем взглянуть на Redux DevTools. После загрузки это будет вкладка в ваших Инструментах разработчика. Если вы нажмете на State , вы увидите все текущее состояние приложения.
Здесь еще немного, но Redux DevTools просто великолепен, когда у вас есть много редукторов и действий.Он отслеживает все изменения в вашем приложении и упрощает отладку по сравнению с обычным React.
Настройка действий Redux
Итак, теперь у нас есть редуктор для сообщений, но у нас нет никаких действий, то есть редуктор будет только возвращать состояние, не изменяя его каким-либо образом. Действия — это то, как мы общаемся с магазином Redux. Для этого приложения для блога мы захотим получить сообщения из API и поместить их в состояние Redux.
Поскольку получение сообщений является асинхронным действием, потребуется использование преобразователя Redux.К счастью, нам не нужно делать ничего особенного, чтобы использовать thunk, кроме настройки его в магазине, что мы уже сделали.
Создайте actions / postsActions.js
. Сначала мы определим типов действий как константы. Это не обязательно, но это общепринятое соглашение, которое упрощает экспорт действий и предотвращает опечатки. Мы хотим сделать три вещи:
-
getPosts
— начните сообщать Redux, что мы собираемся получать сообщения из API -
getPostsSuccess
— передать сообщения в Redux при успешном вызове API -
getPostsFailure
— сообщить Redux, что во время Redux произошла ошибка при неудачном вызове API
действий / сообщенийActions.js
экспорт const GET_POSTS = 'GET_POSTS'
экспорт const GET_POSTS_SUCCESS = 'GET_POSTS_SUCCESS'
константа экспорта GET_POSTS_FAILURE = 'GET_POSTS_FAILURE'
Затем создайте создателей действий , функций, которые возвращают действие , которое состоит из типа и необязательной полезной нагрузки, содержащей данные.
действий / сообщенийActions.js
export const getPosts = () => ({
тип: GET_POSTS,
})
export const getPostsSuccess = (posts) => ({
тип: GET_POSTS_SUCCESS,
полезная нагрузка: сообщения,
})
export const getPostsFailure = () => ({
тип: GET_POSTS_FAILURE,
})
Наконец, выполните асинхронное действие преобразователя, объединяющее все три вышеуказанных действия.При вызове он отправляет начальное действие getPosts ()
, чтобы сообщить Redux о подготовке к вызову API, затем в try / catch
делает все необходимое для получения данных и отправляет действие успеха или отказа, если необходимо. .
действий / сообщенийActions.js
функция экспорта fetchPosts () {
return async (отправка) => {
отправка (getPosts ())
пытаться {
const response = ожидание выборки ('https://jsonplaceholder.typicode.com/posts')
const data = ждать ответа.json ()
отправка (getPostsSuccess (данные))
} catch (ошибка) {
отправка (getPostsFailure ())
}
}
}
Отлично, мы закончили создание действий! Все, что осталось сделать, это указать редуктору, что делать с состоянием каждого действия.
Реагирование на действия
Вернувшись к нашему посту редуктору, у нас есть переключатель, который еще ничего не делает.
Редукторы / посты
Reducer.js
экспорт функции по умолчанию postsReducer (state = initialState, action) {
переключатель (действие.тип) {
по умолчанию:
состояние возврата
}
}
Теперь, когда у нас есть действия, мы можем перенести их со страницы postsActions
.
import * как действия из '../actions/postsActions'
Для каждого действия мы создадим случай
, который возвращает все состояние плюс любые изменения, которые мы в него вносим. Для GET_POSTS
, например, все, что мы хотим сделать, это указать приложению установить loading
на true
, поскольку мы будем делать вызов API.
кейсы. GET_POSTS:
return {... state, loading: true}
-
GET_POSTS
— начать загрузку -
GET_POSTS_SUCCESS
— в приложении есть сообщения, нет ошибок, и оно должно перестать загружаться -
GET_POSTS_FAILURE
— приложение имеет ошибки и должно перестать загружаться
Вот и весь редуктор.
Редукторы / посты
Reducer.js
импортировать * как действия из "../actions/postsActions"
export const initialState = {
сообщений: [],
загрузка: ложь,
hasErrors: ложь,
}
экспортировать функцию по умолчанию postsReducer (state = initialState, action) {
переключатель (действие.тип) {
case actions.GET_POSTS:
return {... state, loading: true}
case actions.GET_POSTS_SUCCESS:
return {posts: action.payload, loading: false, hasErrors: false}
case actions.GET_POSTS_FAILURE:
return {... состояние, загрузка: false, hasErrors: true}
по умолчанию:
состояние возврата
}
}
Теперь наши действия и редукторы готовы, так что все, что осталось сделать, это подключить все к приложению React.
Подключение Redux к компонентам React
Поскольку созданное мной демонстрационное приложение использует React Router, чтобы иметь несколько маршрутов — панель управления, список всех сообщений и страницу отдельных сообщений, я сейчас добавлю React Router.Я просто добавлю панель управления и список всех сообщений для этой демонстрации.
App.js
импортировать React из React
импортировать {BrowserRouter as Router, Switch, Route, Redirect} из 'response-router-dom'
импортировать DashboardPage из './pages/DashboardPage'
импортировать PostsPage из './pages/PostsPage'
const App = () => {
возвращение (
<Маршрутизатор>
<Переключатель>
<Точный путь маршрута = "/" component = {DashboardPage} />
<Точный путь маршрута = "/ posts" component = {PostsPage} />
<Перенаправить на = "/" />
)
}
экспорт приложения по умолчанию
Мы можем создать страницу панели инструментов, которая является обычным компонентом React.
страниц / DashboardPage.js
импортировать React из React
импортировать {Link} из 'response-router-dom'
const DashboardPage = () => (
<раздел>
Панель управления
Это панель управления.
<Ссылка на = "/ posts" className = "button">
Просмотр сообщений
)
экспорт по умолчанию DashboardPage
Для каждого поста создадим компонент Post
, который будет отображать заголовок и отрывок из текста статьи.Сделайте Post.js
в подкаталоге components
.
компонентов / Post.js
импортировать React из React
экспорт const Post = ({post}) => (
{post.title}
{post.body.substring (0, 100)}
)
Компоненты, которые не подключаются к Redux, по-прежнему важны и полезны для небольших многократно используемых областей, таких как этот компонент Post.
Теперь самое интересное для страницы сообщений — перенос Redux в React. Для этого мы будем использовать , соединить
с react-redux
. Во-первых, мы просто сделаем обычный компонент для страницы.
страниц / PostsPage.js
импортировать React из React
const PostsPage = () => {
возвращение (
<раздел>
Сообщения
)
}
экспорт по умолчанию PostsPage
Затем введем и подключим
.Функция connect — это функция более высокого порядка, которая соединяет хранилище Redux с компонентом React. Мы передадим параметр mapStateToProps
в connect
. Эта функция с удачным названием будет принимать любое состояние из хранилища Redux и передавать его в свойства компонента React. Мы добавим , загружающие
, сообщений
и hasErrors
из сообщений Redux Reducer
.
страниц / PostsPage.js
импортировать React из React
импортировать {подключиться} из 'response-redux'
const PostsPage = ({загрузка, сообщения, hasErrors}) => {
возвращение (
<раздел>
Сообщения
)
}
const mapStateToProps = (состояние) => ({загрузка: состояние.posts.loading, posts: state.posts.posts, hasErrors: state.posts.hasErrors,}) экспортировать соединение по умолчанию (mapStateToProps) (PostsPage)
Поскольку этот компонент использует состояние того же редуктора, мы могли бы также написать
state => state.posts
. Однако изучение того, как писать это долгим путем, полезно знать в случае, если вам нужно объединить несколько редукторов в один и тот же компонент.
Наконец, мы добавим асинхронный fetchPosts
из действий, который представляет собой действие, объединяющее весь жизненный цикл получения всех сообщений в одно.Используя useEffect
от React, мы отправим
fetchPosts
при монтировании компонента. Отправка
будет автоматически доступна на подключенном компоненте.
страниц / PostsPage.js
импортировать React, {useEffect} из 'react'import {connect} из' react-redux '
импортировать {fetchPosts} из "../actions/postsActions"
const PostsPage = ({отправка, загрузка, сообщения, hasErrors}) => {useEffect (() => {dispatch (fetchPosts ())}, [отправка])
возвращение (
<раздел>
Сообщения
)
}
const mapStateToProps = (состояние) => ({
загрузка: состояние.posts.loading,
posts: state.posts.posts,
hasErrors: state.posts.hasErrors,
})
экспорт подключения по умолчанию (mapStateToProps) (PostsPage)
Все, что осталось сделать на этом этапе, — это отобразить все три возможных состояния страницы — загружается ли она, есть ли ошибка или успешно получены сообщения из API.
страниц / PostsPage.js
импортировать React, {useEffect} из "реагировать"
импортировать {подключиться} из 'response-redux'
импортировать {fetchPosts} из "../actions/postsActions"
импортировать {Post} из '../components/Post '
const PostsPage = ({отправка, загрузка, сообщения, hasErrors}) => {
useEffect (() => {
отправка (fetchPosts ())
}, [отправка])
const renderPosts = () => {if (loading) return Загрузка сообщений ...
if (hasErrors) return Невозможно отобразить сообщения.
return posts.map ((post) => <Ключ публикации = {post.id} post = {post} />)}
возвращение (
<раздел>
Сообщения
{renderPosts ()}
)
}
const mapStateToProps = (состояние) => ({
загрузка: состояние.posts.loading,
posts: state.posts.posts,
hasErrors: state.posts.hasErrors,
})
экспорт подключения по умолчанию (mapStateToProps) (PostsPage)
И все — теперь у нас есть подключенный компонент, и мы переносим данные из API в наше хранилище Redux. Используя Redux DevTools, мы можем видеть каждое действие, как оно происходит, и изменения (различие) после каждого изменения состояния.
Конец
На этом руководство по созданию приложения с простым Redux заканчивается.Если вы посмотрите исходный код демонстрационного приложения, вы увидите, что многое было добавлено — редуктор и действия для одного сообщения и для комментариев.
Я бы порекомендовал завершить ваш проект, чтобы он соответствовал демонстрационному приложению. Нет никаких новых концепций, которые нужно изучать, но вы создадите еще два редуктора и действия и увидите, как объединить два состояния в один компонент для страницы с одним сообщением, что приведет к одному сообщению, а также комментариям к этому сообщению.
Есть еще одна вещь, о которой я хочу рассказать — Redux Toolkit.Redux Toolkit или RTK — это более новый и простой официальный способ использования Redux. Вы можете заметить, что Redux имеет лот шаблонов для установки и требует гораздо больше папок и файлов, чем обычный React. Появились некоторые шаблоны, чтобы попытаться смягчить все это, например, шаблон Redux ducks, но мы можем упростить его еще больше.
Просмотрите исходный код демонстрационного приложения Redux Toolkit, которое является тем же приложением, которое мы только что создали с помощью Redux, но с использованием RTK. Все намного проще, с дра
.