Зачем иммутабельное изменение состояние в React?
Состояние
Иммутабельность нужна, потому что React обнаруживает изменения сравнением ссылок. Мутация объекта оставляет ссылку прежней — для React «ничего не изменилось».
Что ломает мутация
const [user, setUser] = useState({ name: 'Аня', age: 25 });
// ❌ мутация: ссылка та же
user.age = 26;
setUser(user); // Object.is(старое, новое) === true → рендер ПРОПУЩЕН
// ✅ иммутабельно: новая ссылка
setUser({ ...user, age: 26 }); // рендер произойдётКонкретные жертвы мутаций:
- useState/useReducer: сеттер с той же ссылкой пропускает перерендер.
- React.memo / PureComponent: поверхностное сравнение props не увидит изменений внутри мутированного объекта — компонент не обновится.
- Зависимости useEffect/useMemo: сравниваются по Object.is — мутация не перезапустит эффект.
- Redux-селекторы: подписки не сработают.
Побочные выгоды
- Предсказуемость: значение из прошлого рендера гарантированно не «поменяется под ногами» — можно хранить историю (undo), сравнивать prev/next.
- Дешёвые проверки: сравнить две ссылки — O(1), вместо глубокого обхода объектов.
- Отладка: снапшоты состояния в девтулзах честные.
Практика
setItems([...items, newItem]); // добавить
setItems(items.filter((i) => i.id !== id)); // удалить
setItems(items.map((i) => i.id === id ? { ...i, done: true } : i)); // обновитьДля глубоких структур — Immer (внутри Redux Toolkit): пишешь «мутации», получаешь иммутабельные копии.
Коротко для собеседования
React сравнивает состояние и props по ссылкам (Object.is): мутация не меняет ссылку, поэтому пропадают перерендеры, ломаются React.memo и зависимости эффектов. Иммутабельные обновления (spread, map/filter, Immer) дают новую ссылку — изменения видны, сравнения дёшевы, поведение предсказуемо.