Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що робить "flushSync()"?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`flushSync(callback)`** - це функція з пакету `react-dom`, яка змушує React негайно "скинути" (flush) всі очікувані оновлення стану всередині переданого колбека і синхронно оновити DOM. **Ключове:** після виходу з `flushSync()` React гарантує, що компонент уже перерендерений, DOM оновлений, і всі layout-ефекти (`useLayoutEffect`) для цього оновлення вже відпрацювали.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Визначення > `flushSync(callback)` - це функція з пакету `react-dom`, > яка **змушує React негайно "скинути" (flush) всі очікувані оновлення стану** всередині переданого колбека і **синхронно оновити DOM**. ```javascript import { flushSync } from 'react-dom'; flushSync(() => { setState(newValue); }); ``` Після виходу з `flushSync()` React гарантує, що: - компонент уже **перерендерений**; - **DOM оновлений**; - усі **layout-ефекти (**`useLayoutEffect`**)** для цього оновлення вже відпрацювали. --- ## 1. Навіщо він взагалі потрібен Типово React працює **асинхронно** - особливо в React 18 з **Concurrent Rendering**. Він може: - відкладати оновлення; - об'єднувати декілька `setState` в один рендер (batching); - не одразу оновлювати DOM. Іноді це створює ситуації, коли тобі **потрібно негайно побачити результат у DOM**, наприклад: - щоб виміряти елемент (`offsetHeight`); - щоб плавно запустити анімацію; - щоб зробити фокус; - щоб синхронно відреагувати на дію користувача. --- ## 2. Приклад: без `flushSync` ```javascript function Example() { const [count, setCount] = useState(0); const divRef = useRef(); const handleClick = () => { setCount(c => c + 1); console.log(divRef.current.textContent); // старе значення }; return <div ref={divRef}>{count}</div>; } ``` Що відбувається: - `setCount` ставить оновлення в чергу; - React *планує* рендер, але не виконує його миттєво; - лог виконується **до оновлення DOM** -> ти бачиш старе значення. --- ## 3. Приклад з `flushSync` ```javascript import { flushSync } from 'react-dom'; function Example() { const [count, setCount] = useState(0); const divRef = useRef(); const handleClick = () => { flushSync(() => { setCount(c => c + 1); // оновлення стану }); console.log(divRef.current.textContent); // вже нове значення }; return <div ref={divRef}>{count}</div>; } ``` Тепер React: 1. Одразу виконує `setCount()`; 2. **Синхронно викликає рендер і commit**; 3. Оновлює DOM; 4. Тільки потім виходить із `flushSync`; 5. Лог покаже актуальний `count`. --- ## 4. Як це працює під капотом `flushSync`: - **тимчасово вимикає concurrent batching**; - змушує React **виконати всі очікувані оновлення** (включно з тими, що були заплановані раніше); - **негайно викликає commit phase**, оновлюючи DOM і layout-ефекти; - потім повертає React назад у звичайний асинхронний режим. ```javascript Без flushSync: setState() → чекаємо → React вирішує коли оновити З flushSync: setState() → одразу render → commit → готовий DOM ``` --- ## 5. Де це потрібно застосовувати | Сценарій | Чому потрібен `flushSync` | |---|---| | Потрібно виміряти DOM після `setState` | Щоб отримати актуальні розміри | | Запуск анімації одразу після зміни | DOM повинен бути оновлений миттєво | | Фокусування або скрол після оновлення | Щоб елемент точно існував у DOM | | Робота із зовнішніми бібліотеками (GSAP, D3, Chart.js) | Ці бібліотеки очікують актуальний стан DOM | | Іноді - при інтеграції з React Transition Group, Portal або Drag'n'Drop | Коли commit повинен відбутися синхронно | --- ## 6. Але використовувати обережно! React спеціально **робить рендер асинхронним**, щоб: - не блокувати інтерфейс, - оптимізувати батчинг оновлень, - підвищувати чутливість UI. `flushSync()` **вимикає ці переваги** для конкретної ділянки коду. ### Не роби так: ```javascript flushSync(() => { setA(1); }); flushSync(() => { setB(2); }); ``` Кожен виклик змусить React **рендерити і комітити заново**, що б'є по продуктивності. Краще згрупувати декілька оновлень в один `flushSync()`. --- ## 7. Взаємодія з життєвим циклом | Етап | Що відбувається при `flushSync()` | |---|---| | Render Phase | Запускається негайно для всіх змінених компонентів | | Commit Phase | Відбувається синхронно | | Ефекти (`useLayoutEffect`) | Виконуються одразу після commit | | Ефекти (`useEffect`) | Все ще виконуються асинхронно (на наступному tick) |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.