Skip to main content

Новий об'єкт під час оновлення стану

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

Тому що React не відстежує внутрішні зміни об'єктів, а визначає, чи змінився стан, за посиланням на об'єкт.

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


1. Що відбувається, коли ти робиш так:

javascript
const [user, setUser] = useState({ name: "Alex", age: 25 }); function updateName() { user.name = "Tim"; // mutating the object directly setUser(user); }

Що бачить React:

  • Старе посилання: 0xA12F
  • Нове посилання: 0xA12F (те саме, адже об'єкт не створювався заново)
  • React порівнює посилання і вирішує: "Об'єкт той самий, отже, змін немає."

Результат: компонент не перерендериться, UI залишиться старим, попри зміну даних.


2. Правильний спосіб - створити новий об'єкт

javascript
setUser({ ...user, name: "Tim" });

Що тепер відбувається:

  • Старий об'єкт -> 0xA12F
  • Новий об'єкт -> 0xB83C (у нього та сама структура, але інше посилання)
  • React порівнює посилання і розуміє: "Це новий об'єкт, потрібно оновити компонент."

Компонент ререндериться, UI оновлюється.


3. Чому React порівнює посилання, а не значення

React використовує поверхневе (shallow) порівняння, щоб визначити, чи потрібно оновлювати компонент:

javascript
oldState === newState

Він не робить глибокого порівняння (deep equal), інакше це було б надто повільно для великих об'єктів.

Тому React визначає зміну стану за зміною посилання (вказівника). Якщо посилання на об'єкт змінилося, стан новий, оновлюємо UI.


4. Приклад із масивом

Той самий принцип для масивів:

Погано:

javascript
items.push("new"); // mutating the array setItems(items);

Добре:

javascript
setItems([...items, "new"]); // creating a new array

React бачить нове посилання на масив і оновлює компонент.


5. Чому мутація - це ще й логічна помилка

Якщо ти мутуєш старий стан, то:

  • старі посилання (у замиканнях, хуках, props, ефектах) тепер вказують на змінені дані - це ламає передбачуваність,
  • React може «думати», що стан старий, і не викликати потрібні ефекти,
  • devtools і time-travel debugging стають неможливими (не можна «відкотити» стан назад, якщо він мутований).

6. Приклад наочно

javascript
function UserCard() { const [user, setUser] = useState({ name: "Alex", age: 25 }); const handleClick = () => { // Direct mutation user.age++; setUser(user); // won't work }; const handleClickCorrect = () => { // Immutable update setUser(prev => ({ ...prev, age: prev.age + 1 })); }; console.log("Re-render:", user); return ( <> <p>{user.name} - {user.age}</p> <button onClick={handleClick}>Bad</button> <button onClick={handleClickCorrect}>Good</button> </> ); }

При натисканні на «Bad» React не перерендериться, а при «Good» - оновить UI як потрібно.


Підсумок

ПоведінкаПряма мутаціяКопіювання ({ ...obj })
Змінюється посиланняНіТак
React бачить змінуНіТак
Відбувається ререндерНіТак
ПередбачуваністьЛамаєтьсяЗберігається
Підтримка DevTools / time travelНеможливаМожлива

Підсумкова думка:

У React стан має бути іммутабельним (незмінюваним). Завжди створюй новий об'єкт або масив замість зміни старого - тоді React зможе коректно визначити, що стан змінився, і оновити інтерфейс.

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

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

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