PassFront
  1. PassFront
  2. База знаний
  3. Состояние

Зачем иммутабельное изменение состояние в 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) дают новую ссылку — изменения видны, сравнения дёшевы, поведение предсказуемо.

Другие вопросы раздела