1 added
1 removed
Original
2026-01-01
Modified
2026-02-21
1
<p><a>#статьи</a></p>
1
<p><a>#статьи</a></p>
2
<ul><li>26 дек 2025</li>
2
<ul><li>26 дек 2025</li>
3
<li>0</li>
3
<li>0</li>
4
</ul><p>Управляем состоянием приложения одной библиотекой.</p>
4
</ul><p>Управляем состоянием приложения одной библиотекой.</p>
5
<p>Иллюстрация: Оля Ежак для Skillbox Media</p>
5
<p>Иллюстрация: Оля Ежак для Skillbox Media</p>
6
<p>Пишет о сетях, инструментах для разработчиков и языках программирования. Любит готовить, играть в инди‑игры и программировать на Python.</p>
6
<p>Пишет о сетях, инструментах для разработчиков и языках программирования. Любит готовить, играть в инди‑игры и программировать на Python.</p>
7
<p>Redux - это библиотека для управления состоянием данных и пользовательским интерфейсом в JavaScript-приложениях. Её используют, когда данных становится много и они меняются в разных компонентах ПО: это потенциально может привести к проблемам, например к использованию в функциях устаревших значений переменных.</p>
7
<p>Redux - это библиотека для управления состоянием данных и пользовательским интерфейсом в JavaScript-приложениях. Её используют, когда данных становится много и они меняются в разных компонентах ПО: это потенциально может привести к проблемам, например к использованию в функциях устаревших значений переменных.</p>
8
<p>Из этой статьи вы узнаете, как работать с Redux и из каких компонентов состоит библиотека. Также мы напишем простое приложение, отработав теорию на практике. Статья будет полезна в первую очередь начинающим фронтендерам, работающим с React.</p>
8
<p>Из этой статьи вы узнаете, как работать с Redux и из каких компонентов состоит библиотека. Также мы напишем простое приложение, отработав теорию на практике. Статья будет полезна в первую очередь начинающим фронтендерам, работающим с React.</p>
9
<p><strong>Содержание</strong></p>
9
<p><strong>Содержание</strong></p>
10
<ul><li><a>Для чего нужна Redux</a></li>
10
<ul><li><a>Для чего нужна Redux</a></li>
11
<li><a>Из чего состоит библиотека</a></li>
11
<li><a>Из чего состоит библиотека</a></li>
12
<li><a>Как установить Redux и написать с ней приложение</a></li>
12
<li><a>Как установить Redux и написать с ней приложение</a></li>
13
<li><a>Что дальше</a></li>
13
<li><a>Что дальше</a></li>
14
</ul><p><a>Redux</a>используют в приложениях с большим объёмом данных, которые часто обновляются и используются в разных частях интерфейса. В такой ситуации сложно поддерживать согласованность состояния программы вручную: отдельные функции могут обращаться к устаревшим значениям, а интерфейс - вести себя непредсказуемо.</p>
14
</ul><p><a>Redux</a>используют в приложениях с большим объёмом данных, которые часто обновляются и используются в разных частях интерфейса. В такой ситуации сложно поддерживать согласованность состояния программы вручную: отдельные функции могут обращаться к устаревшим значениям, а интерфейс - вести себя непредсказуемо.</p>
15
<p>Ключевой термин в абзаце выше, который важно знать, чтобы оценить преимущества Redux, - это<strong>состояние</strong>. Под ним понимают единый объект данных, который описывает текущее состояние всего приложения в конкретный момент времени. Можно сказать, что это "снимок" того, что сейчас знает о себе программа: значения переменных, активные элементы интерфейса, пользовательский ввод и так далее.</p>
15
<p>Ключевой термин в абзаце выше, который важно знать, чтобы оценить преимущества Redux, - это<strong>состояние</strong>. Под ним понимают единый объект данных, который описывает текущее состояние всего приложения в конкретный момент времени. Можно сказать, что это "снимок" того, что сейчас знает о себе программа: значения переменных, активные элементы интерфейса, пользовательский ввод и так далее.</p>
16
<p>Redux решает проблему поддержания согласованности состояния приложения за счёт строгого набора правил. Библиотека однозначно указывает, кто может инициировать изменение данных, когда это допустимо и по каким правилам они обновляются. Ключевой принцип в том, что<strong>любой компонент в приложении не имеет права сам менять данные</strong>. Он может только запросить изменение, отправив об этом сообщение.</p>
16
<p>Redux решает проблему поддержания согласованности состояния приложения за счёт строгого набора правил. Библиотека однозначно указывает, кто может инициировать изменение данных, когда это допустимо и по каким правилам они обновляются. Ключевой принцип в том, что<strong>любой компонент в приложении не имеет права сам менять данные</strong>. Он может только запросить изменение, отправив об этом сообщение.</p>
17
<p>Такой подход основан на Flux-архитектуре. Чтобы разобраться в ней, сначала посмотрим на то, как происходит обмен данными в классической MVC-архитектуре.</p>
17
<p>Такой подход основан на Flux-архитектуре. Чтобы разобраться в ней, сначала посмотрим на то, как происходит обмен данными в классической MVC-архитектуре.</p>
18
<p>В традиционном подходе компоненты могут сами менять данные, а связи между ними часто двусторонние. Это создаёт проблему, так как число взаимодействий с ростом сложности приложения растёт и в нём появляются неочевидные зависимости. В итоге изменение в одном компоненте меняет интерфейс, интерфейс влияет на другой компонент, тот - на третий и так далее. Всё это делает работу приложения с MVC-архитектурой непредсказуемой, усложняет отладку и масштабирование.</p>
18
<p>В традиционном подходе компоненты могут сами менять данные, а связи между ними часто двусторонние. Это создаёт проблему, так как число взаимодействий с ростом сложности приложения растёт и в нём появляются неочевидные зависимости. В итоге изменение в одном компоненте меняет интерфейс, интерфейс влияет на другой компонент, тот - на третий и так далее. Всё это делает работу приложения с MVC-архитектурой непредсказуемой, усложняет отладку и масштабирование.</p>
19
MVC-архитектура: классический подход в разработке<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Для решения этой проблемы появилась Flux-архитектура с предсказуемым однонаправленным потоком данных. В ней изменения всегда идут по цепочке - от компонента (Action) через посредника (Dispatcher), который обновляет хранилище состояния приложения (Store) и, как результат, интерфейс (View). В отличие от MVC-архитектуры, интерфейс не может изменить состояние напрямую.</p>
19
MVC-архитектура: классический подход в разработке<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Для решения этой проблемы появилась Flux-архитектура с предсказуемым однонаправленным потоком данных. В ней изменения всегда идут по цепочке - от компонента (Action) через посредника (Dispatcher), который обновляет хранилище состояния приложения (Store) и, как результат, интерфейс (View). В отличие от MVC-архитектуры, интерфейс не может изменить состояние напрямую.</p>
20
<p>Каждое хранилище отвечает за свою часть данных, а состояние приложения распределено между ними и обновляется по единым правилам.</p>
20
<p>Каждое хранилище отвечает за свою часть данных, а состояние приложения распределено между ними и обновляется по единым правилам.</p>
21
Flux-архитектура<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Redux - одна из практических реализаций Flux-архитектуры с важным отличием: на смену нескольким хранилищам (Store) приходит одно.</p>
21
Flux-архитектура<em>Инфографика: Майя Мальгина для Skillbox Media</em><p>Redux - одна из практических реализаций Flux-архитектуры с важным отличием: на смену нескольким хранилищам (Store) приходит одно.</p>
22
<p>Но ради надёжности приходится жертвовать удобством - весь многоступенчатый процесс изменения состояния требуется прописывать для любых действий. Если для важных процедур вроде изменения баланса это оправдано, то для какой-нибудь мелочи (выпадающего окна или изменения цвета кнопки) - уже излишне.</p>
22
<p>Но ради надёжности приходится жертвовать удобством - весь многоступенчатый процесс изменения состояния требуется прописывать для любых действий. Если для важных процедур вроде изменения баланса это оправдано, то для какой-нибудь мелочи (выпадающего окна или изменения цвета кнопки) - уже излишне.</p>
23
<p>Проблему такой "бюрократии" частично решает<a>Redux Toolkit</a>(RTK) - набор инструментов от создателей библиотеки. RTK автоматизирует рутинные части процесса и позволяет разработчикам обойтись меньшим объёмом кода. О нём мы подробно поговорим в практической части статьи.</p>
23
<p>Проблему такой "бюрократии" частично решает<a>Redux Toolkit</a>(RTK) - набор инструментов от создателей библиотеки. RTK автоматизирует рутинные части процесса и позволяет разработчикам обойтись меньшим объёмом кода. О нём мы подробно поговорим в практической части статьи.</p>
24
<p>Перейдём от концепции к реализации. Redux не вводит новые абстракции, а только задаёт строгие правила взаимодействия обычных сущностей JavaScript. В библиотеке есть шесть ключевых элементов: store, state, action, reducer, dispatch и subscribe.</p>
24
<p>Перейдём от концепции к реализации. Redux не вводит новые абстракции, а только задаёт строгие правила взаимодействия обычных сущностей JavaScript. В библиотеке есть шесть ключевых элементов: store, state, action, reducer, dispatch и subscribe.</p>
25
<p>В примерах кода в этом разделе мы будем использовать приложение интернет-провайдера с одним абонентом. Это поможет лучше понять компоненты библиотеки.</p>
25
<p>В примерах кода в этом разделе мы будем использовать приложение интернет-провайдера с одним абонентом. Это поможет лучше понять компоненты библиотеки.</p>
26
<p>Store - главный объект, который связывает все части Redux воедино. Он хранит состояние, предоставляет к нему доступ, позволяет его изменять и подписываться на изменения тем компонентам, которых эти изменения затрагивают.</p>
26
<p>Store - главный объект, который связывает все части Redux воедино. Он хранит состояние, предоставляет к нему доступ, позволяет его изменять и подписываться на изменения тем компонентам, которых эти изменения затрагивают.</p>
27
<p>В коде это JavaScript-объект, который создаётся функцией createStore() при запуске приложения и остаётся в единственном экземпляре во время его работы.</p>
27
<p>В коде это JavaScript-объект, который создаётся функцией createStore() при запуске приложения и остаётся в единственном экземпляре во время его работы.</p>
28
const store = createStore(reducer);<p>createStore() принимает в качестве аргумента reducer - функцию, которая содержит конкретные операции. Необязательным вторым параметром является изначальное состояние хранилища - массив значений для компонентов. Например, такой:</p>
28
const store = createStore(reducer);<p>createStore() принимает в качестве аргумента reducer - функцию, которая содержит конкретные операции. Необязательным вторым параметром является изначальное состояние хранилища - массив значений для компонентов. Например, такой:</p>
29
let initialState = { user: null, isLoading: false, posts: [] };<p>У store есть два важных метода - dispatch и subscribe. dispatch() - это метод хранилища, который принимает в качестве аргумента действие и передаёт его редьюсеру.</p>
29
let initialState = { user: null, isLoading: false, posts: [] };<p>У store есть два важных метода - dispatch и subscribe. dispatch() - это метод хранилища, который принимает в качестве аргумента действие и передаёт его редьюсеру.</p>
30
store.dispatch(action)<p>subscribe() - это метод, который передаёт компоненту новое состояние приложения. Без него фактические изменения не отображались бы в интерфейсе.</p>
30
store.dispatch(action)<p>subscribe() - это метод, который передаёт компоненту новое состояние приложения. Без него фактические изменения не отображались бы в интерфейсе.</p>
31
<p>Метод принимает функцию-слушателя listener, которая будет автоматически запускаться каждый раз, когда кто-то вызывает dispatch:</p>
31
<p>Метод принимает функцию-слушателя listener, которая будет автоматически запускаться каждый раз, когда кто-то вызывает dispatch:</p>
32
store.subscribe(listener)<p>State - это снимок состояния приложения в конкретный момент времени. Он хранит данные, необходимые для отслеживания текущего статуса, - число пользователей, суммарную стоимость товаров в корзине, баланс счёта и другие изменяющиеся со временем данные.</p>
32
store.subscribe(listener)<p>State - это снимок состояния приложения в конкретный момент времени. Он хранит данные, необходимые для отслеживания текущего статуса, - число пользователей, суммарную стоимость товаров в корзине, баланс счёта и другие изменяющиеся со временем данные.</p>
33
<p>В коде это объект, который хранит массив значений для компонентов. В примере банковского приложения это выглядит так:</p>
33
<p>В коде это объект, который хранит массив значений для компонентов. В примере банковского приложения это выглядит так:</p>
34
const state = { user: { name: "Alex", id: 1 }, balance = 0, isInternetActive: false };<p>State в Redux доступен только для чтения. Вы не сможете написать state.balance = 500. Единственный способ изменить его - создать новый стейт через цепочку действий.</p>
34
const state = { user: { name: "Alex", id: 1 }, balance = 0, isInternetActive: false };<p>State в Redux доступен только для чтения. Вы не сможете написать state.balance = 500. Единственный способ изменить его - создать новый стейт через цепочку действий.</p>
35
<p>Action описывает событие и то, как требуется изменить состояние в связи с ним. В коде это простой объект с двумя свойствами - обязательным type и необязательным payload:</p>
35
<p>Action описывает событие и то, как требуется изменить состояние в связи с ним. В коде это простой объект с двумя свойствами - обязательным type и необязательным payload:</p>
36
const action = { // Действие с двумя свойствами type: "PAY_INTERNET", // Что случилось? Оплата интернета payload: 500 // Данные: сумма списания }; const action = { // Действие с одним свойством type: "RECONNECTION" // Что случилось? Переподключение };<p>Редьюсер определяет, как действие должно изменить состояние, и меняет его. В коде он представлен функцией, которая в качестве аргументов принимает текущее состояние и действие, а возвращает новое состояние. Так как редьюсер работает с разными действиями, то тело функции состоит из switch-case-конструкции.</p>
36
const action = { // Действие с двумя свойствами type: "PAY_INTERNET", // Что случилось? Оплата интернета payload: 500 // Данные: сумма списания }; const action = { // Действие с одним свойством type: "RECONNECTION" // Что случилось? Переподключение };<p>Редьюсер определяет, как действие должно изменить состояние, и меняет его. В коде он представлен функцией, которая в качестве аргументов принимает текущее состояние и действие, а возвращает новое состояние. Так как редьюсер работает с разными действиями, то тело функции состоит из switch-case-конструкции.</p>
37
<p>Если редьюсер не знает, как обработать поступившее действие, то обязан вернуть текущее состояние как есть. Иначе приложение перестанет работать.</p>
37
<p>Если редьюсер не знает, как обработать поступившее действие, то обязан вернуть текущее состояние как есть. Иначе приложение перестанет работать.</p>
38
// Начальное состояние (баланс на момент открытия счёта) const initialState = { balance: 1000, isInternetActive: false }; const bankReducer = (state = initialState, action) => { switch (action.type) { case "PAY_INTERNET": return { ...state, // Копируем все поля старого стейта balance: state.balance - action.payload, // Обновляем баланс isInternetActive: true // Включаем интернет }; case "DEPOSIT_MONEY": return { ...state, balance: state.balance + action.payload }; // Если тип экшена нам незнаком, возвращаем текущий стейт без изменений default: return state; } };<p>Соберём всё вместе. Как будет происходить оплата интернета в приложении, которое мы описали:</p>
38
// Начальное состояние (баланс на момент открытия счёта) const initialState = { balance: 1000, isInternetActive: false }; const bankReducer = (state = initialState, action) => { switch (action.type) { case "PAY_INTERNET": return { ...state, // Копируем все поля старого стейта balance: state.balance - action.payload, // Обновляем баланс isInternetActive: true // Включаем интернет }; case "DEPOSIT_MONEY": return { ...state, balance: state.balance + action.payload }; // Если тип экшена нам незнаком, возвращаем текущий стейт без изменений default: return state; } };<p>Соберём всё вместе. Как будет происходить оплата интернета в приложении, которое мы описали:</p>
39
<ul><li>Пользователь нажимает кнопку "Оплатить".</li>
39
<ul><li>Пользователь нажимает кнопку "Оплатить".</li>
40
<li>Генератор действий создаёт объект: { type: "PAY_INTERNET", payload: 500 }.</li>
40
<li>Генератор действий создаёт объект: { type: "PAY_INTERNET", payload: 500 }.</li>
41
<li>Компонент отправляет этот объект в хранилище: store.dispatch (action).</li>
41
<li>Компонент отправляет этот объект в хранилище: store.dispatch (action).</li>
42
<li>Хранилище "просыпается" и передаёт текущий стейт и экшен редьюсеру.</li>
42
<li>Хранилище "просыпается" и передаёт текущий стейт и экшен редьюсеру.</li>
43
<li>Функция-редьюсер считает: 1000 - 500 = 500. Она возвращает новый объект стейта, где баланс равен 500, а интернет включён.</li>
43
<li>Функция-редьюсер считает: 1000 - 500 = 500. Она возвращает новый объект стейта, где баланс равен 500, а интернет включён.</li>
44
<li>Хранилище сохраняет этот новый стейт вместо старого.</li>
44
<li>Хранилище сохраняет этот новый стейт вместо старого.</li>
45
<li>Интерфейс, подписанный на хранилище, видит обновление и перерисовывает баланс на экране.</li>
45
<li>Интерфейс, подписанный на хранилище, видит обновление и перерисовывает баланс на экране.</li>
46
</ul><p>Как видите, Redux позволяет создать цепочку действий со строгими правилами, что позволяет отображать правильный баланс в приложении.</p>
46
</ul><p>Как видите, Redux позволяет создать цепочку действий со строгими правилами, что позволяет отображать правильный баланс в приложении.</p>
47
<p>Теория - это хорошо, но Redux проще понять на практике. В этом разделе мы напишем часть простого банковского приложения на React. В ней реализуем весь цикл: от создания хранилища до отрисовки кнопок "Пополнить" и "Снять" в интерфейсе.</p>
47
<p>Теория - это хорошо, но Redux проще понять на практике. В этом разделе мы напишем часть простого банковского приложения на React. В ней реализуем весь цикл: от создания хранилища до отрисовки кнопок "Пополнить" и "Снять" в интерфейсе.</p>
48
<p>Мы предполагаем, что React.js и Node.js у вас уже установлены. Если нет, то сначала установите<a>Node.js</a>, а уже с её помощью установите<a>React.js</a>.</p>
48
<p>Мы предполагаем, что React.js и Node.js у вас уже установлены. Если нет, то сначала установите<a>Node.js</a>, а уже с её помощью установите<a>React.js</a>.</p>
49
<p>В прошлых разделах мы подробно разобрали чистую Redux. Её методы - это фундаментальные концепции, которые важно понимать. Однако в современной разработке почти всегда используется Redux Toolkit (RTK).</p>
49
<p>В прошлых разделах мы подробно разобрали чистую Redux. Её методы - это фундаментальные концепции, которые важно понимать. Однако в современной разработке почти всегда используется Redux Toolkit (RTK).</p>
50
<p><a>Redux Toolkit</a> - это официальный рекомендуемый набор инструментов для эффективной работы с Redux. Он был создан, чтобы упростить работу с библиотекой.</p>
50
<p><a>Redux Toolkit</a> - это официальный рекомендуемый набор инструментов для эффективной работы с Redux. Он был создан, чтобы упростить работу с библиотекой.</p>
51
-
<p>Для старта работы с ним установите пакет @reduxjs/toolkit - он уже включает в себя ядро Redux, react-redux - библиотеку-связку, и Redux Toolkit. Для этого введите в терминале:</p>
51
+
<p>Для старта работы с ним установите пакет @reduxjs/toolkit - он уже включает в себя ядро Redux, react-redux - библиотеку-св��зку, и Redux Toolkit. Для этого введите в терминале:</p>
52
npm install @reduxjs/toolkit react-redux<p>Разделим процесс на четыре шага, чтобы не запутаться: пропишем логику, подключим Redux, нарисуем интерфейс и протестируем результат. Разберём каждый из этапов подробно.</p>
52
npm install @reduxjs/toolkit react-redux<p>Разделим процесс на четыре шага, чтобы не запутаться: пропишем логику, подключим Redux, нарисуем интерфейс и протестируем результат. Разберём каждый из этапов подробно.</p>
53
<p><strong>Шаг 1. Прописываем логику.</strong>Для лучшей организации кода в проекте принято выносить всю логику Redux в отдельную папку.</p>
53
<p><strong>Шаг 1. Прописываем логику.</strong>Для лучшей организации кода в проекте принято выносить всю логику Redux в отдельную папку.</p>
54
<p>Создайте внутри папки src/ папку store/ с файлом index.js. Откройте его и вставьте код из блока ниже. Здесь мы используем функции Redux Toolkit configureStore() для создания хранилища и createSlice() для редьюсера.</p>
54
<p>Создайте внутри папки src/ папку store/ с файлом index.js. Откройте его и вставьте код из блока ниже. Здесь мы используем функции Redux Toolkit configureStore() для создания хранилища и createSlice() для редьюсера.</p>
55
// src/store/index.js // Здесь мы создаём Redux Slice и Redux Store import { configureStore, createSlice } from '@reduxjs/toolkit'; // 1. Создаём срез состояния (Slice) с помощью createSlice // Эта функция позволяет определить начальное состояние и редьюсеры и автоматически генерирует action creators в одном месте const bankSlice = createSlice({ name: 'bank', // Уникальное имя этого среза. Используется для Action Type, например: bank/deposit initialState: { balance: 0, // Начальное значение баланса }, reducers: { // В Redux Toolkit можно писать мутабельный код внутри редьюсеров // Благодаря встроенной библиотеке Immer, RTK сам превратит его в иммутабельное обновление состояния под капотом, обеспечивая безопасность deposit: (state, action) => { state.balance += action.payload; // Выглядит как прямое изменение, но безопасно! }, withdraw: (state, action) => { state.balance -= action.payload; // Выглядит как прямое изменение, но безопасно! }, // Здесь могли бы быть и другие редьюсеры, например toggleInternet: (state) => { state.isInternetActive = !state.isInternetActive; }, }, }); // Экспортируем наши функции-генераторы действий (action creators), которые были автоматически созданы createSlice export const { deposit, withdraw } = bankSlice.actions; // 2. Создаём Redux Store с помощью configureStore // configureStore - это обёртка над createStore, которая автоматически настраивает Redux DevTools, добавляет middleware (например, thunk) и упрощает объединение редьюсеров export const store = configureStore({ reducer: { // Здесь мы определяем, какие редьюсеры отвечают за какие части общего состояния // Ключ bank будет использоваться для доступа к состоянию этого среза: state.bank bank: bankSlice.reducer, }, });<p>Сравните с чистой Redux из предыдущего раздела: кое-что изменилось, а именно - появились две новые сущности:</p>
55
// src/store/index.js // Здесь мы создаём Redux Slice и Redux Store import { configureStore, createSlice } from '@reduxjs/toolkit'; // 1. Создаём срез состояния (Slice) с помощью createSlice // Эта функция позволяет определить начальное состояние и редьюсеры и автоматически генерирует action creators в одном месте const bankSlice = createSlice({ name: 'bank', // Уникальное имя этого среза. Используется для Action Type, например: bank/deposit initialState: { balance: 0, // Начальное значение баланса }, reducers: { // В Redux Toolkit можно писать мутабельный код внутри редьюсеров // Благодаря встроенной библиотеке Immer, RTK сам превратит его в иммутабельное обновление состояния под капотом, обеспечивая безопасность deposit: (state, action) => { state.balance += action.payload; // Выглядит как прямое изменение, но безопасно! }, withdraw: (state, action) => { state.balance -= action.payload; // Выглядит как прямое изменение, но безопасно! }, // Здесь могли бы быть и другие редьюсеры, например toggleInternet: (state) => { state.isInternetActive = !state.isInternetActive; }, }, }); // Экспортируем наши функции-генераторы действий (action creators), которые были автоматически созданы createSlice export const { deposit, withdraw } = bankSlice.actions; // 2. Создаём Redux Store с помощью configureStore // configureStore - это обёртка над createStore, которая автоматически настраивает Redux DevTools, добавляет middleware (например, thunk) и упрощает объединение редьюсеров export const store = configureStore({ reducer: { // Здесь мы определяем, какие редьюсеры отвечают за какие части общего состояния // Ключ bank будет использоваться для доступа к состоянию этого среза: state.bank bank: bankSlice.reducer, }, });<p>Сравните с чистой Redux из предыдущего раздела: кое-что изменилось, а именно - появились две новые сущности:</p>
56
<ul><li>createSlice(). Вместо ручного создания initialState, switch/case редьюсера и отдельных объектов action мы определяем всё это в одном месте. RTK сам создаст необходимые типы действий и функции-генераторы для них.</li>
56
<ul><li>createSlice(). Вместо ручного создания initialState, switch/case редьюсера и отдельных объектов action мы определяем всё это в одном месте. RTK сам создаст необходимые типы действий и функции-генераторы для них.</li>
57
<li>configureStore(). Это упрощённая версия createStore, которая позволяет легко комбинировать несколько редьюсеров для упрощения работы.</li>
57
<li>configureStore(). Это упрощённая версия createStore, которая позволяет легко комбинировать несколько редьюсеров для упрощения работы.</li>
58
</ul><p><strong>Шаг 2. Подключаем Redux к React.</strong>Redux Store готов. Следующий шаг - сделать его доступным для всех React-компонентов нашего приложения.</p>
58
</ul><p><strong>Шаг 2. Подключаем Redux к React.</strong>Redux Store готов. Следующий шаг - сделать его доступным для всех React-компонентов нашего приложения.</p>
59
<p>Откройте src/index.js. Не перепутайте его с src/store/index.js, с которым мы работали в предыдущем шаге! Вставьте в src/index.js код:</p>
59
<p>Откройте src/index.js. Не перепутайте его с src/store/index.js, с которым мы работали в предыдущем шаге! Вставьте в src/index.js код:</p>
60
// index.js (или main.jsx) import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; // Импортируем провайдер import { store } from './store'; // Импортируем наш Store import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );<p>Здесь используется <Provider> - специальный компонент Redux, который передаёт Redux Store во всё дерево вложенных компонентов. Благодаря этому любой компонент может получить доступ к состоянию и экшенам напрямую, без явной передачи state через пропсы от родителя к потомкам. Такой подход избавляет от prop drilling и упрощает работу с общим состоянием приложения по мере его роста.</p>
60
// index.js (или main.jsx) import React from 'react'; import ReactDOM from 'react-dom/client'; import { Provider } from 'react-redux'; // Импортируем провайдер import { store } from './store'; // Импортируем наш Store import App from './App'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store={store}> <App /> </Provider> );<p>Здесь используется <Provider> - специальный компонент Redux, который передаёт Redux Store во всё дерево вложенных компонентов. Благодаря этому любой компонент может получить доступ к состоянию и экшенам напрямую, без явной передачи state через пропсы от родителя к потомкам. Такой подход избавляет от prop drilling и упрощает работу с общим состоянием приложения по мере его роста.</p>
61
В Props компонент может получить данные только от родительского компонента. <Provider> позволяет избежать этого, так как состояние доступно всем компонентам напрямую<em>Инфографика: Майя Мальгина для Skillbox Media</em><p><strong>Шаг 3. Создаём компоненты интерфейса.</strong>Теперь создадим компонент, который будет отображать баланс и кнопки для взаимодействия с пользователем. Мы будем использовать хуки useSelector() для чтения состояния из Store и useDispatch() для отправки действий.</p>
61
В Props компонент может получить данные только от родительского компонента. <Provider> позволяет избежать этого, так как состояние доступно всем компонентам напрямую<em>Инфографика: Майя Мальгина для Skillbox Media</em><p><strong>Шаг 3. Создаём компоненты интерфейса.</strong>Теперь создадим компонент, который будет отображать баланс и кнопки для взаимодействия с пользователем. Мы будем использовать хуки useSelector() для чтения состояния из Store и useDispatch() для отправки действий.</p>
62
<p>Откройте src/App.js и вставьте в него следующий код:</p>
62
<p>Откройте src/App.js и вставьте в него следующий код:</p>
63
// src/App.jsx (или src/App.js) import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; // Импортируем action creators (deposit, withdraw) из нашего Redux Store import { deposit, withdraw } from './store'; function App() { // 1. Получаем текущий баланс из Redux Store с помощью хука useSelector // state здесь - это весь объект состояния Store // Мы обращаемся к 'state.bank.balance', потому что наш редьюсер был передан в configureStore под ключом bank const balance = useSelector((state) => state.bank.balance); // 2. Получаем функцию dispatch с помощью хука useDispatch // Эта функция нужна для отправки (диспетчеризации) наших действий в Store const dispatch = useDispatch(); return ( <div className="App" style={appContainerStyle}> <h1>Банк Redux Toolkit</h1> <h2>Ваш баланс: {balance}$</h2> <div className="buttons"> <button onClick={() => dispatch(deposit(100))} // Отправляем действие "deposit" с суммой 100 style={depositButtonStyle} > Пополнить на 100$ </button> <button onClick={() => dispatch(withdraw(50))} // Отправляем действие "withdraw" с суммой 50 style={withdrawButtonStyle} > Снять 50$ </button> </div> </div> ); } export default App; const appContainerStyle = { textAlign: 'center', fontFamily: 'Arial, sans-serif', padding: '20px', maxWidth: '500px', margin: '50px auto', border: '1px solid #eee', borderRadius: '8px', boxShadow: '0 4px 8px rgba(0,0,0,0.1)', backgroundColor: '#fff', }; const baseButtonStyle = { margin: '10px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer', border: 'none', borderRadius: '5px', color: 'white', fontWeight: 'bold', }; const depositButtonStyle = { ...baseButtonStyle, backgroundColor: '#4CAF50', // Зелёный для пополнения }; const withdrawButtonStyle = { ...baseButtonStyle, backgroundColor: '#f44336', // Красный для снятия };<p>Здесь мы тоже кое-что улучшили по сравнению с чистой Redux из прошлого раздела. Добавилось несколько сущностей:</p>
63
// src/App.jsx (или src/App.js) import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; // Импортируем action creators (deposit, withdraw) из нашего Redux Store import { deposit, withdraw } from './store'; function App() { // 1. Получаем текущий баланс из Redux Store с помощью хука useSelector // state здесь - это весь объект состояния Store // Мы обращаемся к 'state.bank.balance', потому что наш редьюсер был передан в configureStore под ключом bank const balance = useSelector((state) => state.bank.balance); // 2. Получаем функцию dispatch с помощью хука useDispatch // Эта функция нужна для отправки (диспетчеризации) наших действий в Store const dispatch = useDispatch(); return ( <div className="App" style={appContainerStyle}> <h1>Банк Redux Toolkit</h1> <h2>Ваш баланс: {balance}$</h2> <div className="buttons"> <button onClick={() => dispatch(deposit(100))} // Отправляем действие "deposit" с суммой 100 style={depositButtonStyle} > Пополнить на 100$ </button> <button onClick={() => dispatch(withdraw(50))} // Отправляем действие "withdraw" с суммой 50 style={withdrawButtonStyle} > Снять 50$ </button> </div> </div> ); } export default App; const appContainerStyle = { textAlign: 'center', fontFamily: 'Arial, sans-serif', padding: '20px', maxWidth: '500px', margin: '50px auto', border: '1px solid #eee', borderRadius: '8px', boxShadow: '0 4px 8px rgba(0,0,0,0.1)', backgroundColor: '#fff', }; const baseButtonStyle = { margin: '10px', padding: '10px 20px', fontSize: '16px', cursor: 'pointer', border: 'none', borderRadius: '5px', color: 'white', fontWeight: 'bold', }; const depositButtonStyle = { ...baseButtonStyle, backgroundColor: '#4CAF50', // Зелёный для пополнения }; const withdrawButtonStyle = { ...baseButtonStyle, backgroundColor: '#f44336', // Красный для снятия };<p>Здесь мы тоже кое-что улучшили по сравнению с чистой Redux из прошлого раздела. Добавилось несколько сущностей:</p>
64
<ul><li>useSelector(). Теперь мы обращаемся к state.bank.balance, потому что наш редьюсер был передан в configureStore() под ключом bank. Если бы у нас было несколько срезов (например, bankSlice и userSlice), мы бы обращались к state.bank или state.user.</li>
64
<ul><li>useSelector(). Теперь мы обращаемся к state.bank.balance, потому что наш редьюсер был передан в configureStore() под ключом bank. Если бы у нас было несколько срезов (например, bankSlice и userSlice), мы бы обращались к state.bank или state.user.</li>
65
<li>Диспетчер с action creators. Вместо ручного создания объекта { type: "DEPOSIT", payload: 100 } мы просто вызываем функцию deposit(100), а RTK сам генерирует за нас правильный объект действия.</li>
65
<li>Диспетчер с action creators. Вместо ручного создания объекта { type: "DEPOSIT", payload: 100 } мы просто вызываем функцию deposit(100), а RTK сам генерирует за нас правильный объект действия.</li>
66
</ul><p><strong>Шаг 4. Запускаем и проверяем приложение.</strong>Запустите проект, находясь в корневой папке терминала:</p>
66
</ul><p><strong>Шаг 4. Запускаем и проверяем приложение.</strong>Запустите проект, находясь в корневой папке терминала:</p>
67
npm start<p>Теперь откройте приложение по адресу<a>http://localhost:3000/</a>. Всё работает: кнопки позволяют менять баланс, и он отображается на экране.</p>
67
npm start<p>Теперь откройте приложение по адресу<a>http://localhost:3000/</a>. Всё работает: кнопки позволяют менять баланс, и он отображается на экране.</p>
68
Интерфейс веб-приложения "банка"<em>Скриншот: Google Chrome / Skillbox Media</em><p>Приложение получилось небольшим, но демонстрирующим преимущества Redux:</p>
68
Интерфейс веб-приложения "банка"<em>Скриншот: Google Chrome / Skillbox Media</em><p>Приложение получилось небольшим, но демонстрирующим преимущества Redux:</p>
69
<ul><li>Баланс хранится в одном-единственном объекте. В программе нет локальных состояний с балансом в компонентах, которые могли бы рассинхронизироваться и привести к его неверному отображению.</li>
69
<ul><li>Баланс хранится в одном-единственном объекте. В программе нет локальных состояний с балансом в компонентах, которые могли бы рассинхронизироваться и привести к его неверному отображению.</li>
70
<li>Ничто не может менять баланс напрямую - вместо этого вы отправляете действия, запускающие его обновление.</li>
70
<li>Ничто не может менять баланс напрямую - вместо этого вы отправляете действия, запускающие его обновление.</li>
71
<li>Приложение строго следует циклу "Событие → Действие → Обработка → Обновление → Отображение", что делает приложение предсказуемым и простым для отладки.</li>
71
<li>Приложение строго следует циклу "Событие → Действие → Обработка → Обновление → Отображение", что делает приложение предсказуемым и простым для отладки.</li>
72
</ul><p>Теперь вы освоили основы Redux. Чтобы их закрепить и узнать о других возможностях библиотеки, попробуйте написать несколько дополнительных модулей для приложения. Например, можно добавить функциональность регистрации и авторизации или отображение истории транзакций. Перед этим загляните<a>на официальный сайт Redux Toolkit</a> - там есть разделы с примерами и гайдами, которые мы рекомендуем изучить для углублённого понимания.</p>
72
</ul><p>Теперь вы освоили основы Redux. Чтобы их закрепить и узнать о других возможностях библиотеки, попробуйте написать несколько дополнительных модулей для приложения. Например, можно добавить функциональность регистрации и авторизации или отображение истории транзакций. Перед этим загляните<a>на официальный сайт Redux Toolkit</a> - там есть разделы с примерами и гайдами, которые мы рекомендуем изучить для углублённого понимания.</p>
73
<p>А ещё RTK с React могут быть полезны в бэкенд-разработке: в Redux Toolkit есть инструмент для управления состоянием загрузки, ошибками и кешированием -<a>RTK Query</a>.</p>
73
<p>А ещё RTK с React могут быть полезны в бэкенд-разработке: в Redux Toolkit есть инструмент для управления состоянием загрузки, ошибками и кешированием -<a>RTK Query</a>.</p>
74
<a>Курс с трудоустройством: "Профессия Разработчик + ИИ" Узнать о курсе</a>
74
<a>Курс с трудоустройством: "Профессия Разработчик + ИИ" Узнать о курсе</a>