Перейти к основному содержимому

Основы Redux, Часть 1: Обзор Redux

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Что вы изучите
  • Что такое Redux и зачем его использовать
  • Основные компоненты Redux-приложения

Введение

Добро пожаловать в учебник по основам Redux! Этот учебник познакомит вас с основными концепциями, принципами и шаблонами использования Redux. К моменту завершения вы будете понимать компоненты Redux-приложения, поток данных при работе с Redux и наши стандартные рекомендации по построению Redux-приложений.

В Части 1 этого учебника мы кратко рассмотрим минимальный пример работающего Redux-приложения, чтобы увидеть его компоненты, а в Части 2: Концепции Redux и поток данных подробно разберём эти компоненты и то, как данные передаются в Redux-приложении.

Начиная с Части 3: Состояние, действия и редюсеры, мы используем эти знания для создания небольшого примера приложения, демонстрирующего взаимодействие компонентов, и обсудим практическую работу Redux. После создания работающего примера "вручную" (чтобы вы видели происходящее), мы обсудим стандартные шаблоны и абстракции, обычно используемые с Redux. Наконец, мы увидим, как эти низкоуровневые примеры преобразуются в высокоуровневые шаблоны, рекомендуемые для реальных приложений.

Как работать с этим учебником

Этот учебник научит вас "как работает Redux", а также объяснит, почему существуют эти шаблоны.

Неофициальный Бета-перевод

Эта страница переведена PageTurner AI (бета). Не одобрена официально проектом. Нашли ошибку? Сообщить о проблеме →

Внимание!

Обратите внимание, что в этом руководстве намеренно используются устаревшие шаблоны логики Redux, требующие больше кода, чем "современные Redux" шаблоны с Redux Toolkit, которые мы рекомендуем как правильный подход для создания приложений на Redux сегодня. Это сделано для объяснения принципов и концепций Redux. Данный материал не предназначен для использования в production-проектах.

Чтобы изучить "современный Redux" с Redux Toolkit, перейдите по ссылкам:

После понимания взаимодействия компонентов мы рассмотрим использование Redux Toolkit для упрощения работы. Redux Toolkit — рекомендуемый способ создания production-приложений на Redux, он построен на всех концепциях, которые мы изучим в этом учебнике. Усвоив основные концепции, вы сможете эффективнее использовать Redux Toolkit.

Мы старались сделать объяснения доступными для новичков, но нам нужно предположить вашу базовую подготовку, чтобы сосредоточиться на Redux. Этот учебник предполагает, что вы знакомы с:

Если вы ещё не уверенно владеете этими темами, мы рекомендуем сначала изучить их, а затем вернуться к изучению Redux. Мы будем ждать вашего возвращения!

Наконец, убедитесь, что установили расширения React и Redux DevTools для своего браузера:

Что такое Redux?

Для начала стоит понять, что вообще представляет собой "Redux". Что он делает? Какие проблемы помогает решить? Зачем его использовать?

Redux — это шаблон и библиотека для управления и обновления глобального состояния приложения, где UI инициирует события под названием "actions" для описания происходящего, а отдельная логика обновления под названием "reducers" изменяет состояние в ответ. Он служит централизованным хранилищем для состояния, используемого во всём приложении, с правилами, гарантирующими предсказуемое обновление состояния.

Почему стоит использовать Redux?

Redux помогает управлять "глобальным" состоянием — состоянием, которое требуется во многих частях приложения.

Шаблоны и инструменты Redux упрощают понимание того, когда, где, почему и как обновляется состояние в вашем приложении, и как будет вести себя логика приложения при этих изменениях. Redux направляет вас к написанию предсказуемого и тестируемого кода, что даёт уверенность в работе приложения.

Когда следует использовать Redux?

Redux помогает управлять общим состоянием приложения, но как любой инструмент имеет свои компромиссы. Он требует изучения дополнительных концепций и написания большего кода. Также он добавляет уровень абстракции и требует соблюдения определённых ограничений. Это компромисс между краткосрочной и долгосрочной продуктивностью.

Redux особенно полезен когда:

  • У вас есть большой объём состояния приложения, который требуется во многих частях приложения

  • Состояние приложения часто обновляется со временем

  • Логика обновления этого состояния может быть сложной

  • Приложение имеет средний или крупный кодобазу, над которым могут работать многие разработчики

Не всем приложениям нужен Redux. Проанализируйте тип приложения, которое вы создаёте, и выберите инструменты, которые наилучшим образом решат ваши задачи.

Хотите узнать больше?

Библиотеки и инструменты Redux

Redux — это небольшая самостоятельная JS-библиотека. Однако её обычно используют с несколькими другими пакетами:

Redux Toolkit

Redux Toolkit — наш рекомендуемый подход для написания логики Redux. Он содержит пакеты и функции, необходимые для создания Redux-приложений. Redux Toolkit включает лучшие практики, упрощает большинство задач Redux, предотвращает распространённые ошибки и облегчает разработку приложений на Redux.

React-Redux

Redux можно интегрировать с любым UI-фреймворком, но чаще всего его используют с React. React-Redux — наш официальный пакет, позволяющий React-компонентам взаимодействовать с Redux-хранилищем: читать состояние и отправлять действия для его обновления.

Расширение Redux DevTools

Расширение Redux DevTools показывает историю изменений состояния в вашем Redux-хранилище. Это позволяет эффективно отлаживать приложения, используя такие мощные техники как "отладка путешествием во времени".

Основы Redux

Теперь, когда вы знаете, что такое Redux, кратко рассмотрим компоненты Redux-приложения и принцип его работы.

Информация

Оставшаяся часть описания посвящена исключительно основной библиотеке Redux (пакет redux). Сопутствующие пакеты мы обсудим далее в учебнике.

Хранилище Redux

Центром каждого Redux-приложения является хранилище (store). "Хранилище" — это контейнер, который хранит состояние (state) вашего приложения.

Хранилище — это JavaScript-объект с несколькими особыми функциями и возможностями, отличающими его от простого глобального объекта:

  • Никогда не изменяйте состояние, хранящееся в Redux-хранилище, напрямую.

  • Вместо этого единственный способ обновить состояние — создать простой объект action, описывающий "что-то произошедшее в приложении", и затем отправить (dispatch) этот action в хранилище, чтобы сообщить о событии.

  • Когда action отправлен, хранилище запускает корневую функцию reducer, которая вычисляет новое состояние на основе предыдущего состояния и полученного action.

  • Наконец, хранилище уведомляет подписчиков (subscribers) об обновлении состояния, чтобы UI мог обновиться с новыми данными.

Пример приложения на базе Redux Core

Рассмотрим минимальный рабочий пример приложения на Redux — простое приложение-счётчик:

Поскольку Redux — это самостоятельная JS-библиотека без зависимостей, в этом примере мы просто загружаем один тег script для библиотеки Redux и используем базовые JS и HTML для UI. На практике Redux обычно используется через установку пакетов из NPM, а UI создаётся с помощью библиотек вроде React.

Информация

Часть 5: UI и React показывает, как использовать Redux и React вместе.

Разберём этот пример на составные части, чтобы понять, что происходит.

Состояние (State), Действия (Actions) и Редьюсеры (Reducers)

Сначала определяем начальное значение state для описания приложения:

// Define an initial state value for the app
const initialState = {
value: 0
}

В этом приложении мы будем отслеживать одно число — текущее значение счётчика.

Обычно в Redux-приложениях корневым элементом состояния является JS-объект, содержащий другие значения.

Затем определяем функцию reducer. Редьюсер принимает два аргумента: текущее state и объект action, описывающий произошедшее событие. При первоначальном запуске Redux-приложения у нас ещё нет состояния, поэтому мы задаём initialState как значение по умолчанию для этого редьюсера:

// Create a "reducer" function that determines what the new state
// should be when something happens in the app
function counterReducer(state = initialState, action) {
// Reducers usually look at the type of action that happened
// to decide how to update the state
switch (action.type) {
case 'counter/incremented':
return { ...state, value: state.value + 1 }
case 'counter/decremented':
return { ...state, value: state.value - 1 }
default:
// If the reducer doesn't care about this action type,
// return the existing state unchanged
return state
}
}

Объекты action всегда содержат поле type — строку, которую вы задаёте как уникальный идентификатор действия. type должен быть читаемым, чтобы любой разработчик понимал его назначение. В нашем случае мы используем 'counter' как основу типа действия, а вторая часть описывает "что произошло". Здесь счётчик был увеличен, поэтому тип действия — 'counter/incremented'.

В зависимости от типа действия мы либо возвращаем совершенно новый объект в качестве нового state, либо возвращаем существующий объект state, если изменений не требуется. Важно: состояние обновляется иммутабельно — мы копируем существующее состояние и изменяем копию, а не модифицируем исходный объект напрямую.

Хранилище (Store)

Теперь создаём экземпляр хранилища, используя API Redux createStore.

// Create a new Redux store with the `createStore` function,
// and use the `counterReducer` for the update logic
const store = Redux.createStore(counterReducer)

Мы передаём функцию-редьюсер в createStore, которая использует её для генерации начального состояния и вычисления будущих обновлений.

Пользовательский интерфейс (UI)

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

// Our "user interface" is some text in a single HTML element
const valueEl = document.getElementById('value')

// Whenever the store state changes, update the UI by
// reading the latest store state and showing new data
function render() {
const state = store.getState()
valueEl.innerHTML = state.value.toString()
}

// Update the UI with the initial data
render()
// And subscribe to redraw whenever the data changes in the future
store.subscribe(render)

В этом минимальном примере мы используем только базовые HTML-элементы для UI, где текущее значение отображается в одном <div>.

Поэтому мы пишем функцию, которая умеет получать актуальное состояние из Redux-хранилища через метод store.getState(), а затем использует это значение для обновления UI.

Redux-хранилище позволяет вызвать store.subscribe() и передать функцию обратного вызова, которая будет вызываться при каждом обновлении хранилища. Таким образом, мы можем передать нашу функцию render в качестве подписчика и быть уверенными, что при каждом обновлении хранилища мы обновим UI актуальными данными.

Redux — это самостоятельная библиотека, которую можно использовать где угодно. Это также означает, что её можно интегрировать с любым UI-слоем.

Отправка действий (Dispatching Actions)

Наконец, нам нужно реагировать на действия пользователя, создавая объекты действий (actions), которые описывают произошедшее, и отправляя (dispatching) их в хранилище. Когда мы вызываем store.dispatch(action), хранилище запускает редюсер, вычисляет новое состояние и запускает подписчиков для обновления UI.

// Handle user inputs by "dispatching" action objects,
// which should describe "what happened" in the app
document.getElementById('increment').addEventListener('click', function () {
store.dispatch({ type: 'counter/incremented' })
})

document.getElementById('decrement').addEventListener('click', function () {
store.dispatch({ type: 'counter/decremented' })
})

document
.getElementById('incrementIfOdd')
.addEventListener('click', function () {
// We can write logic to decide what to do based on the state
if (store.getState().value % 2 !== 0) {
store.dispatch({ type: 'counter/incremented' })
}
})

document
.getElementById('incrementAsync')
.addEventListener('click', function () {
// We can also write async logic that interacts with the store
setTimeout(function () {
store.dispatch({ type: 'counter/incremented' })
}, 1000)
})

Здесь мы отправим действия, которые заставят редюсер добавить 1 или вычесть 1 из текущего значения счётчика.

Мы также можем написать код, который отправляет действие только при выполнении определённого условия, или асинхронный код, который отправляет действие с задержкой.

Поток данных

Мы можем представить поток данных в Redux-приложении с помощью следующей схемы. Она показывает, как:

  • действия отправляются в ответ на взаимодействие пользователя, например, клик

  • хранилище запускает функцию редюсера для вычисления нового состояния

  • UI считывает новое состояние для отображения новых значений

(Не волнуйтесь, если пока не всё понятно! Держите эту схему в уме, пока изучаете остальные части учебника, и вы увидите, как всё взаимосвязано.)

Диаграмма потока данных в Redux

Итоги изученного

Хотя пример со счётчиком небольшой, он демонстрирует все основные части реального Redux-приложения. Всё, о чём мы будем говорить в следующих разделах, расширяет эти базовые элементы.

Итак, давайте повторим изученное:

Резюме
  • Redux — это библиотека для управления глобальным состоянием приложения
    • Обычно используется вместе с React-Redux для интеграции с React
    • Redux Toolkit — стандартный способ написания Redux-логики
  • Паттерн обновлений в Redux разделяет «что произошло» и «как изменяется состояние»
    • Действия (Actions) — простые объекты с полем type, описывающие «что произошло»
    • Редюсеры (Reducers) — функции, вычисляющие новое состояние на основе предыдущего состояния + действия
    • Хранилище (Store) запускает корневой редюсер при диспатче действия
  • Redux использует архитектуру «однонаправленного потока данных»
    • Состояние описывает состояние приложения в конкретный момент, UI отображается на его основе
    • При возникновении события:
      • UI диспатчит действие
      • Хранилище запускает редюсеры, обновляя состояние
      • Хранилище уведомляет UI об изменении состояния
    • UI перерисовывается с учётом нового состояния

Что дальше?

Теперь, когда вы знаете основные части Redux-приложения, переходите к Части 2: Концепции Redux и поток данных, где мы подробнее рассмотрим поток данных в Redux-приложении.