Skip to main content

Як RTK реалізує іммутабельність?

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

Redux Toolkit реалізує іммутабельність через бібліотеку Immer, яка дозволяє писати «мутуючий» код, але насправді створює іммутабельну копію стану.


Нагадаю: що таке іммутабельність

Іммутабельність - це принцип, за яким не можна змінювати наявний стан напряму, а потрібно створювати новий:

Неправильно:

javascript
state.count++ // mutation (changing an existing object)

Правильно (у чистому Redux):

javascript
return { ...state, count: state.count + 1 } // new object

Як працює Immer усередині RTK

Immer робить ось що:

  1. За кожного виклику редʼюсера RTK передає в нього «чернетку» (draft) стану.
  2. Цей draft - це проксі-об'єкт, який відстежує всі зміни.
  3. Ти можеш «мутувати» його звичайним чином:
javascript
state.value++ state.todos.push(todo) state.user.name = 'Tim'
  1. Після виходу з функції Immer автоматично створює іммутабельну копію стану, застосовуючи всі зафіксовані зміни.
  2. Підсумок: старий стан залишається незміненим, новий створюється з урахуванням змін.

Приклад (магія Immer)

javascript
import { createSlice } from '@reduxjs/toolkit' const counterSlice = createSlice({ name: 'counter', initialState: { value: 0 }, reducers: { increment: (state) => { // Write as if mutating state.value++ } } })

Що робить RTK під капотом:

javascript
import { produce } from 'immer' function incrementReducer(state, action) { return produce(state, draft => { draft.value++ }) }

produce() з Immer створює новий іммутабельний об'єкт, де value збільшено, а все інше - ті самі посилання.


Як працює produce()

javascript
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 change

Immer використовує Proxy та структурне порівняння, щоб ефективно копіювати лише змінені гілки дерева стану (а не весь об'єкт).


Чому це зручно

Проблема без ImmerРішення в RTK
Потрібно писати return { ...state, ... } щоразуМожна писати state.value++
Складно оновлювати вкладені об'єктиПросто звертаєшся до state.user.profile.name
Ризик випадкової мутаціїImmer захищає від цього
Важко читати кодЛогіка редʼюсера стає «природною» та чистою

Приклад порівняння

Звичайний Redux:

javascript
function reducer(state, action) { return { ...state, user: { ...state.user, name: 'Alex' } } }

Redux Toolkit (Immer усередині):

javascript
function reducer(state, action) { state.user.name = 'Alex' }

Код став утричі коротшим, а іммутабельність при цьому збереглася.


Що ще робить Immer корисним

  • Копіює лише змінені частини state, тому працює ефективно.
  • Підтримує складні структури: масиви, об'єкти, вкладеність.
  • Легко відловлює помилки мутацій у Dev-режимі.
  • Працює з TypeScript без проблем.

Візуально

javascript
[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-об'єкт, відстежує зміни й повертає новий, безпечно скопійований стан, не змінюючи оригінал.

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

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

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