Що робить "flushSync()"?
Визначення
flushSync(callback)- це функція з пакетуreact-dom, яка змушує React негайно "скинути" (flush) всі очікувані оновлення стану всередині переданого колбека і синхронно оновити DOM.
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
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
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:
- Одразу виконує
setCount(); - Синхронно викликає рендер і commit;
- Оновлює DOM;
- Тільки потім виходить із
flushSync; - Лог покаже актуальний
count.
4. Як це працює під капотом
flushSync:
- тимчасово вимикає concurrent batching;
- змушує React виконати всі очікувані оновлення (включно з тими, що були заплановані раніше);
- негайно викликає commit phase, оновлюючи DOM і layout-ефекти;
- потім повертає React назад у звичайний асинхронний режим.
Без flushSync:
setState() → чекаємо → React вирішує коли оновити
З flushSync:
setState() → одразу render → commit → готовий DOM5. Де це потрібно застосовувати
| Сценарій | Чому потрібен flushSync |
|---|---|
Потрібно виміряти DOM після setState | Щоб отримати актуальні розміри |
| Запуск анімації одразу після зміни | DOM повинен бути оновлений миттєво |
| Фокусування або скрол після оновлення | Щоб елемент точно існував у DOM |
| Робота із зовнішніми бібліотеками (GSAP, D3, Chart.js) | Ці бібліотеки очікують актуальний стан DOM |
| Іноді - при інтеграції з React Transition Group, Portal або Drag'n'Drop | Коли commit повинен відбутися синхронно |
6. Але використовувати обережно!
React спеціально робить рендер асинхронним, щоб:
- не блокувати інтерфейс,
- оптимізувати батчинг оновлень,
- підвищувати чутливість UI.
flushSync() вимикає ці переваги для конкретної ділянки коду.
Не роби так:
flushSync(() => {
setA(1);
});
flushSync(() => {
setB(2);
});Кожен виклик змусить React рендерити і комітити заново, що б'є по продуктивності.
Краще згрупувати декілька оновлень в один flushSync().
7. Взаємодія з життєвим циклом
| Етап | Що відбувається при flushSync() |
|---|---|
| Render Phase | Запускається негайно для всіх змінених компонентів |
| Commit Phase | Відбувається синхронно |
Ефекти (useLayoutEffect) | Виконуються одразу після commit |
Ефекти (useEffect) | Все ще виконуються асинхронно (на наступному tick) |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.