Як RTK реалізує іммутабельність?
Коротка відповідь
Redux Toolkit реалізує іммутабельність через бібліотеку Immer, яка дозволяє писати «мутуючий» код, але насправді створює іммутабельну копію стану.
Нагадаю: що таке іммутабельність
Іммутабельність - це принцип, за яким не можна змінювати наявний стан напряму, а потрібно створювати новий:
Неправильно:
state.count++ // mutation (changing an existing object)Правильно (у чистому Redux):
return { ...state, count: state.count + 1 } // new objectЯк працює Immer усередині RTK
Immer робить ось що:
- За кожного виклику редʼюсера RTK передає в нього «чернетку» (draft) стану.
- Цей draft - це проксі-об'єкт, який відстежує всі зміни.
- Ти можеш «мутувати» його звичайним чином:
state.value++
state.todos.push(todo)
state.user.name = 'Tim'- Після виходу з функції Immer автоматично створює іммутабельну копію стану, застосовуючи всі зафіксовані зміни.
- Підсумок: старий стан залишається незміненим, новий створюється з урахуванням змін.
Приклад (магія Immer)
import { createSlice } from '@reduxjs/toolkit'
const counterSlice = createSlice({
name: 'counter',
initialState: { value: 0 },
reducers: {
increment: (state) => {
// Write as if mutating
state.value++
}
}
})Що робить RTK під капотом:
import { produce } from 'immer'
function incrementReducer(state, action) {
return produce(state, draft => {
draft.value++
})
}produce() з Immer створює новий іммутабельний об'єкт, де value збільшено, а все інше - ті самі посилання.
Як працює produce()
import { produce } from 'immer'
const base = { count: 1, user: { name: 'Tim' } }
const next = produce(base, draft => {
draft.count++
draft.user.name = 'John'
})
console.log(next)
// { count: 2, user: { name: 'John' } }
console.log(base)
// { count: 1, user: { name: 'Tim' } } did not changeImmer використовує Proxy та структурне порівняння, щоб ефективно копіювати лише змінені гілки дерева стану (а не весь об'єкт).
Чому це зручно
| Проблема без Immer | Рішення в RTK |
|---|---|
| Потрібно писати return { ...state, ... } щоразу | Можна писати state.value++ |
| Складно оновлювати вкладені об'єкти | Просто звертаєшся до state.user.profile.name |
| Ризик випадкової мутації | Immer захищає від цього |
| Важко читати код | Логіка редʼюсера стає «природною» та чистою |
Приклад порівняння
Звичайний Redux:
function reducer(state, action) {
return {
...state,
user: {
...state.user,
name: 'Alex'
}
}
}Redux Toolkit (Immer усередині):
function reducer(state, action) {
state.user.name = 'Alex'
}Код став утричі коротшим, а іммутабельність при цьому збереглася.
Що ще робить Immer корисним
- Копіює лише змінені частини state, тому працює ефективно.
- Підтримує складні структури: масиви, об'єкти, вкладеність.
- Легко відловлює помилки мутацій у Dev-режимі.
- Працює з TypeScript без проблем.
Візуально
[current state]
↓ (Immer creates a draft via Proxy)
[draft: temporary copy]
↓ (you "mutate" it)
Immer records the changes
↓
[new immutable state]Підсумок
| Що | Як працює |
|---|---|
| Immer | Бібліотека, що створює іммутабельні копії через Proxy |
| RTK | Використовує Immer усередині createSlice() |
| Розробник | Пише «мутуючий» код, але отримує іммутабельність безкоштовно |
| Результат | Менше коду, менше помилок, чиста логіка |
Підсумкове формулювання:
Redux Toolkit забезпечує іммутабельність стану за допомогою Immer, який створює «чорновий» Proxy-об'єкт, відстежує зміни й повертає новий, безпечно скопійований стан, не змінюючи оригінал.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.