Мутація state в reducer
Коротка відповідь
Не можна мутувати (змінювати напряму)
stateв reducer, тому що Redux (і React) мають бачити, що стан змінився, а це можливо тільки при створенні нового об'єкта.
Детально
Reducer має бути чистою функцією, а це означає:
- Він не змінює вхідні дані (state).
- Він повертає новий об'єкт, що описує новий стан.
Приклад правильного редьюсера
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
return { ...state, count: state.count + 1 } // створюємо новий об'єкт
default:
return state
}
}Приклад з мутацією
function counterReducer(state = { count: 0 }, action) {
switch (action.type) {
case 'INCREMENT':
state.count++ // мутує об'єкт
return state // повертає той самий об'єкт
default:
return state
}
}Проблема: Redux (і React) не зрозуміє, що стан змінився,
оскільки посилання на об'єкт не змінилося - state залишився тим самим об'єктом у пам'яті.
Чому це важливо
1. Redux порівнює стани за посиланням
Redux (і React через useSelector) використовують поверхневе порівняння (===), щоб зрозуміти, чи змінився стан.
oldState === newState // якщо true -> не перерендерюємоЯкщо ти мутував об'єкт, посилання залишається тим самим -> Redux думає, що нічого не змінилося -> компонент не оновлюється.
2. Іммутабельність дозволяє "time travel"
Redux DevTools можуть "перемотувати" стан вперед-назад, тому що кожен стан зберігається як нова версія об'єкта.
Якби редьюсери мутували state, старі стани просто знищувалися б.
3. Просте тестування
Чисті функції легко тестувати: однакові вхідні дані -> завжди той самий результат. Якщо ти мутуєш state, результат може залежати від попереднього виклику.
4. Оптимізація React-компонентів
Багато оптимізацій (наприклад, React.memo, useSelector, PureComponent)
базуються на порівнянні посилань (===), а не на глибокому порівнянні.
Іммутабельність робить це можливим.
Як не мутувати state
Використовуй іммутабельні методи:
| Операція | Мутація | Іммутабельно |
|---|---|---|
| Додати елемент | array.push(x) | [...array, x] |
| Видалити елемент | array.splice(1,1) | array.filter((_, i) => i !== 1) |
| Змінити властивість | obj.key = value | { ...obj, key: value } |
| Змінити вкладений об'єкт | state.user.name = 'Tim' | { ...state, user: { ...state.user, name: 'Tim' } } |
Підсумок
| Причина | Чому не можна мутувати |
|---|---|
| Redux порівнює за посиланням | Не побачить змін |
| Time travel у DevTools | Втратяться старі стани |
| Тестування | Стане непередбачуваним |
| Оптимізації React | Не працюватимуть коректно |
| Принцип чистих функцій | Порушується детермінізм |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.