Что такое иммутабельность и зачем она нужна
Иммутабельность — это свойство объекта, при котором его нельзя изменить после создания. В JavaScript, где большинство структур данных изменяемы по умолчанию, это может звучать непривычно. Но в контексте управления состоянием, особенно в React или Redux, иммутабельность помогает избежать неожиданных сайд-эффектов и упрощает отладку. Например, если вы работаете с массивом задач и хотите изменить одну, проще и безопаснее создать новый массив с обновлённой задачей, чем мутировать исходный. Это позволяет точно отслеживать изменения и использовать такие фичи, как time-travel debugging.
Как работает immer под капотом
Immer — это библиотека, которая делает работу с иммутабельными структурами данных проще и естественнее. Вместо того чтобы вручную копировать объекты и массивы, вы просто пишете код, как будто изменяете их напрямую. На самом деле, под капотом Immer использует концепцию черновиков (drafts). Когда вы вызываете функцию `produce`, библиотека создаёт прокси-объект на основе исходного состояния. Все изменения записываются именно в этот прокси. После завершения работы над черновиком, Immer сравнивает изменения и создаёт новый иммутабельный объект, который содержит только то, что было изменено.
Пример использования: обновление списка задач
Представим, у нас есть список задач, и мы хотим отметить одну как выполненную. Без Immer это выглядело бы громоздко: нужно скопировать массив, найти нужный элемент, скопировать объект задачи, изменить поле и собрать всё обратно. С Immer всё проще:
```js
import produce from 'immer';
const nextState = produce(state, draft => {
const task = draft.tasks.find(t => t.id === 3);
if (task) {
task.completed = true;
}
});
```
Здесь `state` остаётся неизменным, а `nextState` — это новый объект с обновлённой задачей. При этом код остаётся чистым и читаемым, как если бы мы просто изменяли объект напрямую. Это делает поддержку кода проще и снижает вероятность ошибок.
Диаграмма: как работает produce

Представьте цепочку:
Исходное состояние → Черновик (Proxy) → Мутации → Новый иммутабельный объект
Когда вы вызываете `produce`, Immer создаёт прокси-объект, который выглядит и ведёт себя как обычный объект. Все изменения записываются в него. После завершения функции, Immer сравнивает черновик с оригиналом и генерирует новый объект, копируя только изменённые части. Остальные части остаются ссылками на старые объекты для оптимизации. Это позволяет избежать полного копирования всего дерева состояния, что особенно важно при работе с большими структурами.
Сравнение с ручным клонированием

Без Immer разработчики часто используют спред-операторы или методы вроде `Object.assign` и `Array.map`, чтобы создавать новые объекты. Но такие подходы быстро становятся громоздкими при работе с вложенными структурами. Например, чтобы обновить свойство внутри глубоко вложенного объекта, нужно вручную копировать каждый уровень. Это не только неудобно, но и легко ошибиться. Immer же позволяет писать код наивно — как будто вы мутируете объект — и при этом сохранять иммутабельность. Это особенно полезно в Redux, где каждый редьюсер должен возвращать новый объект состояния.
Плюсы и минусы использования Immer
Immer значительно упрощает работу с иммутабельными данными, особенно при сложных вложенных структурах. Он снижает количество шаблонного кода, делает редьюсеры компактнее и повышает читаемость. Однако есть и нюансы. Например, Immer использует Proxy, что может вызвать проблемы в старых браузерах (до IE11). Кроме того, из-за магии под капотом может быть сложнее понять, где именно произошли изменения, особенно при сложных мутациях. Но в большинстве современных проектов плюсы перевешивают, и Immer становится стандартом де-факто.
Когда стоит использовать Immer, а когда — нет

Если вы работаете с React, Redux Toolkit или просто часто обновляете состояние, особенно вложенное, Immer — отличное решение. Он экономит время и делает код чище. Однако в случаях, когда структура данных простая или вы работаете в окружении с ограничениями (например, старые браузеры или специфические требования к производительности), возможно, стоит придерживаться ручного подхода. Также, если вы хотите полного контроля над каждым обновлением и предпочитаете явный код, Immer может показаться слишком «волшебным». В итоге всё зависит от задач и предпочтений команды.



