Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Кілька викликів setState». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Якщо викликати `setState` (або `setCount`, `setUser` тощо) кілька разів поспіль **в одній події або синхронній ділянці коду**, React **об'єднує (batch)** ці оновлення і виконує **лише один ререндер**. **Ключове:** якщо передавати в `setState` просто значення, а не функцію, усі виклики використовують одне й те саме старе значення стану, тож можуть "загубитися".Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Коротка відповідь Якщо ти викликаєш `setState` (або `setCount`, `setUser` тощо) кілька разів поспіль **в одній події або синхронній ділянці коду**, React **об'єднує (batch)** ці оновлення і виконує **лише один ререндер**. Але! Якщо ти передаєш у `setState` просто значення (а не функцію), усі виклики будуть використовувати **те саме старе значення стану**, і тому можуть "загубитися". --- ## 1. Приклад проблеми ```javascript function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount(count + 1); setCount(count + 1); setCount(count + 1); }; return <button onClick={handleClick}>{count}</button>; } ``` Здається, що `count` має стати `3`, але насправді він стане `1`. Чому? --- ## 2. Що робить React React **не оновлює** `count` **миттєво** - він ставить усі апдейти в чергу: ```javascript setCount(count + 1) → "онови count з 0 до 1" setCount(count + 1) → "онови count з 0 до 1" setCount(count + 1) → "онови count з 0 до 1" ``` Усі три апдейти використовують **старе значення** `count = 0`, тому результат після застосування - `1`. --- ## 3. Правильний спосіб - використовувати функціональне оновлення Щоб кожен виклик спирався **на актуальне значення**, потрібно передавати в `setState` **функцію**, яка отримує попередній стан: ```javascript function Counter() { const [count, setCount] = useState(0); const handleClick = () => { setCount(prev => prev + 1); setCount(prev => prev + 1); setCount(prev => prev + 1); }; return <button onClick={handleClick}>{count}</button>; } ``` Тепер кожен виклик: ```javascript 1) prev = 0 → 1 2) prev = 1 → 2 3) prev = 2 → 3 ``` Результат - `3`, як і очікувалося. --- ## 4. Чому React так робить ### Асинхронність оновлень React використовує **batching (пакетування)** - він чекає завершення події (`onClick`, `onChange` тощо) і застосовує всі накопичені зміни **одним рендером**, щоб не перемальовувати компонент після кожного виклику. ### Економія продуктивності Замість 3 ререндерів React робить **1**. Це в десятки разів швидше при великих деревах компонентів. --- ## 5. Після виходу з події (наприклад, `setTimeout`) Іноді batching **не застосовується** (у старих версіях React <18) всередині асинхронних колбеків, і оновлення виконуються окремо: ```javascript setTimeout(() => { setCount(count + 1); setCount(count + 1); }, 0); ``` У React 18 і вище batching увімкнений **навіть для асинхронного коду**, тому тепер обидва оновлення теж об'єднаються. --- ## 6. Що зберігається всередині React React веде **чергу оновлень стану** (`updateQueue`): ```javascript [ (prev) => prev + 1, (prev) => prev + 1, (prev) => prev + 1 ] ``` Під час рендеру він проходить по ній: ```javascript let newState = oldState; for (const update of queue) { newState = update(newState); } ``` Після застосування всіх апдейтів компонент ререндериться **один раз**. --- ## Підсумок | Ситуація | Що станеться | | --- | --- | | Кілька `setState(value)` поспіль | Використовують старе значення → результат тільки останнього | | Кілька `setState(prev => …)` поспіль | Працюють послідовно → результат правильний | | Усі апдейти в одній події | Об'єднуються (batching) → 1 ререндер | | React <18 (старий) у `setTimeout` | batching не діє | | React 18+ | batching працює навіть в async коді | --- ## Висновок > React не оновлює стан одразу - він накопичує зміни. > Якщо потрібно оновити стан **на основі попереднього**, > завжди використовуй **функціональну форму**: > > ```javascript > setState(prev => newValueBasedOn(prev)); > ``` > > Це гарантує коректність, навіть при кількох викликах поспіль.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.