Skip to main content

Мутація state в reducer

Коротка відповідь

Не можна мутувати (змінювати напряму) state в reducer, тому що Redux (і React) мають бачити, що стан змінився, а це можливо тільки при створенні нового об'єкта.


Детально

Reducer має бути чистою функцією, а це означає:

  1. Він не змінює вхідні дані (state).
  2. Він повертає новий об'єкт, що описує новий стан.

Приклад правильного редьюсера

javascript
function counterReducer(state = { count: 0 }, action) { switch (action.type) { case 'INCREMENT': return { ...state, count: state.count + 1 } // створюємо новий об'єкт default: return state } }

Приклад з мутацією

javascript
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) використовують поверхневе порівняння (===), щоб зрозуміти, чи змінився стан.

javascript
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Не працюватимуть коректно
Принцип чистих функційПорушується детермінізм

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.