Це, мабуть, технічно найважливіший розділ для розуміння того, як писати Redux код правильно. Більшість багів у початківців виникають саме через порушення цих правил.
Концепція прийшла з функціонального програмування. Функція вважається "чистою", якщо вона відповідає двом критеріям:
// Чиста функція
// Завжди повертає a + b.
// Не змінює нічого зовні.
function add(a, b) {
return a + b;
}
// Нечиста (Недетермінована)
// Повертає різні значення при кожному виклику.
function randomId() {
return Math.random().toString(36);
}
// Нечиста (Побічний ефект)
// Змінює глобальну змінну count.
let count = 0;
function increment() {
count++; // Side effect!
return count;
}
// Нечиста (Мережевий запит)
// Залежить від зовнішнього світу.
async function getUser() {
return await fetch('/api/user');
}
Це означає, що в редюсері ЗАБОРОНЕНО:
Math.random(), Date.now().fetch, axios).state або action.Іммутабельність означає, що дані не можна змінювати після створення. Якщо ви хочете змінити дані, ви повинні створити копію з новими значеннями.
У JavaScript об'єкти та масиви є мутабельними (змінними) за посиланням. Це корінь зла в Redux, якщо не бути обережним.
const user = { name: 'Ivan', age: 25 };
// ❌ МУТАЦІЯ
// Ми змінили існуючий об'єкт.
// Посилання на об'єкт залишилося тим самим!
user.age = 26;
// ✅ ІММУТАБЕЛЬНЕ ОНОВЛЕННЯ
// Ми створили НОВИЙ об'єкт.
// Посилання змінилося!
const updatedUser = { ...user, age: 26 };
prevProps === nextProps).obj.prop = 1), посилання на obj не зміниться. React/Redux подумає, що нічого не змінилося, і не оновить компонент.Вам доведеться часто писати такий код у класичному Redux (або використовувати Redux Toolkit, який робить це за вас).
Використовуйте Spread Operator (...).
const state = {
loading: false,
data: { id: 1, text: 'Hello' }
};
// ❌ Погано
state.loading = true;
// ✅ Добре
return {
...state, // Копіюємо всі поля (data)
loading: true // Перезаписуємо loading
};
Це найскладніше. Потрібно копіювати кожен рівень вкладеності (Shallow Copy).
// Хочемо змінити state.data.text
return {
...state,
data: {
...state.data, // Копіюємо поля всередині data
text: 'Bye' // Оновлюємо text
}
};
const todos = [1, 2, 3];
// ❌ Погано (push мутує масив)
todos.push(4);
// ✅ Добре
const newTodos = [...todos, 4];
Використовуйте filter, бо він повертає новий масив.
// Видалити todo з id=2
const newTodos = state.todos.filter(todo => todo.id !== 2);
Використовуйте map.
// Перемкнути статус todo з id=2
const newTodos = state.todos.map(todo => {
if (todo.id === 2) {
// Знайшли потрібний? Повертаємо копію зі змінами
return { ...todo, completed: !todo.completed };
}
// Не той? Повертаємо без змін
return todo;
});
Тепер, коли ми озброєні теорією, давайте напишемо наш перший реальний Redux код.
Філософія Redux та Три Принципи
Redux часто вважають складною бібліотекою, але насправді він базується на кількох дуже простих ідеях. Складність виникає не з самої бібліотеки, а з нових термінів та обмежень, які вона накладає.
Створення Store (Classic Redux)
Ми починаємо практичну частину з "класичного" Redux. Це саме той підхід, який використовувався до 2019 року. Хоча зараз рекомендовано використовувати Redux Toolkit, розуміння того, як працює createStore, є критичним для розуміння самої суті бібліотеки.