Майстерність Redux та архітектура стану
Архітектура та Store
Серце вашого застосунку: Redux Store
В основі кожного Redux-додатку лежить store (сховище). Уявіть його як центральний хаб, що містить весь стан вашого застосунку. Цей стан — це один великий об'єкт JavaScript, який є єдиним джерелом правди. Замість того, щоб розкидати дані по різних компонентах, ви зберігаєте все в одному місці.
Такий підхід значно спрощує керування даними, особливо у великих і складних застосунках. Коли будь-який компонент потребує доступу до даних або хоче їх змінити, він звертається до цього центрального сховища. Це робить потік даних передбачуваним і легким для відстеження.
Односпрямований потік даних
Redux працює за принципом односпрямованого потоку даних. Це означає, що дані у вашому застосунку рухаються в одному, передбачуваному напрямку. Цей цикл гарантує, що зміни стану не відбуваються хаотично, а слідують чіткій логіці.
Цикл виглядає так:
- Дія (Action): Усе починається з дії. Це звичайний об'єкт, який описує, що сталося. Наприклад, користувач натиснув кнопку «Додати товар до кошика».
- Редюсер (Reducer): Дія відправляється до редюсера. Редюсер — це чиста функція, яка приймає поточний стан та дію, і повертає новий стан. Вона не змінює старий стан, а створює його копію зі змінами.
- Сховище (Store): Сховище оновлюється новим станом, який повернув редюсер.
- Інтерфейс (UI): Компоненти React, які підписані на зміни в сховищі, отримують оновлений стан і перемальовуються, щоб відобразити ці зміни.
Цей цикл забезпечує чітке розділення відповідальності та полегшує налагодження, оскільки ви завжди можете відстежити, яка дія призвела до певної зміни стану.
Налаштування Store з Redux Toolkit
Раніше для створення сховища використовували функцію createStore з основної бібліотеки Redux. Однак сучасний підхід — це використання configureStore з Redux Toolkit (RTK). Ця функція значно спрощує налаштування та включає корисні інструменти «з коробки».
configureStore автоматично:
- Об'єднує ваші редюсери.
- Додає
redux-thunkmiddleware для роботи з асинхронними запитами. - Налаштовує інтеграцію з Redux DevTools Extension для зручного дебагінгу.
Це робить код чистішим і менш схильним до помилок, дозволяючи вам зосередитись на логіці програми, а не на конфігурації.
// app/store.js
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import userReducer from '../features/user/userSlice';
export const store = configureStore({
reducer: {
// Тут ми об'єднуємо всі наші редюсери
counter: counterReducer,
user: userReducer,
},
});
Middleware
noun
Проміжне програмне забезпечення, що знаходиться між відправкою дії (dispatch) та моментом, коли вона досягає редюсера. Middleware дозволяє перехоплювати дії та додавати додаткову логіку, наприклад, для асинхронних запитів, логування або маршрутизації.
Завдяки redux-thunk, який входить до складу configureStore за замовчуванням, ви можете створювати "thunk"-функції. Це спеціальні функції, які можуть виконувати асинхронну логіку (наприклад, запити до API) і відправляти дії в залежності від результату. Це ключовий інструмент для керування побічними ефектами у Redux-додатках.
Незмінність (Immutability)
Один із найважливіших принципів Redux — це незмінність стану. Це означає, що ви ніколи не повинні напряму змінювати об'єкт стану або його властивості. Замість цього, ви завжди створюєте нову копію стану з необхідними змінами.
Чому це так важливо?
- Передбачуваність: Коли стан незмінний, легко відстежити, як він змінювався з часом. Це робить дебаггінг значно простішим.
- Оптимізація продуктивності: React та інші бібліотеки можуть ефективно визначати, чи потрібно перемальовувати компонент, просто порівнюючи посилання на об'єкти стану. Якщо посилання не змінилося, значить, і стан не змінився, і перемальовування не потрібне.
Пряма мутація стану може призвести до непередбачуваної поведінки та багів, які важко знайти.
// ❌ Неправильно: пряма мутація стану
const initialState = { value: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'counter/incremented':
state.value++; // Мутація!
return state;
default:
return state;
}
}
// ✅ Правильно: повернення нового об'єкта стану
const initialState = { value: 0 };
function counterReducer(state = initialState, action) {
switch (action.type) {
case 'counter/incremented':
return {
...state, // Копіюємо старі властивості
value: state.value + 1, // Оновлюємо потрібну
};
default:
return state;
}
}
Оскільки configureStore автоматично налаштовує все необхідне, ви можете одразу почати використовувати Redux DevTools.
Це розширення для браузера, яке дозволяє візуально відстежувати кожну дію, що відбувається у вашому додатку, та бачити, як змінюється стан. Ви можете навіть "подорожувати в часі", скасовуючи дії та повертаючись до попередніх станів, що є неймовірно потужним інструментом для відлагодження.
Що є єдиним джерелом правди для стану всього додатку в Redux?
Який з наведених варіантів правильно описує односпрямований потік даних у Redux?
Розуміння цих основних концепцій є ключем до ефективного використання Redux. Далі ми розглянемо, як створювати редюсери та дії за допомогою Redux Toolkit.