Learning
Všechny články
Frontend10. srpna 2026 6 min

useEffect běží po paintu, useLayoutEffect před ním

Kde v timeline render–commit sedí tvůj effect, rozhoduje o všem

Effect nemá zpoždění náhodou, má ho podle fáze

Většina zmatků kolem useEffect a useLayoutEffect zmizí ve chvíli, kdy si uvědomíš jednu věc: React neaktualizuje UI jedním krokem. Rozdělí to na dvě striktně oddělené fáze a mezi ně a browser vloží přesně danou timeline. Tvůj effect do té timeline jen zapadá na jedno konkrétní místo. Když víš kam, přestaneš hádat.

React dělá při každém updatu tři kroky: trigger (něco zavolá setState), render a commit. Render a commit jsou to důležité rozdělení.


Render počítá, commit sahá do DOMu

Render je čisté volání tvých komponent. React zavolá funkci komponenty, dostane JSX, a rekurzivně sestaví, jak má strom vypadat. Nic se přitom nekreslí a nic nesahá do DOMu. Proto musí být render čistý — žádné side-efekty, stejný vstup vždy stejný výstup. React si render může klidně zahodit a spustit znovu.

Commit je ta část, kde React teprve sáhne na reálný DOM. Při prvním renderu přidá uzly přes appendChild. Při dalších udělá jen minimální rozdíl — když se <input> nezměnil, React na něj nesáhne, takže si udrží fokus i rozepsaný text. Tohle je důvod, proč se stav v poli neztratí, i když se komponenta rerenderuje.

Klíčové je pořadí: nejdřív se doběhne celý render, teprve pak přijde commit. A až po commitu dostane slovo browser a překreslí obrazovku — to je paint. Tři body na časové ose: render skončil, commit zapsal DOM, browser paintuje.


Effect je jen bod na téhle ose

Teď dává timing effektů smysl sám od sebe, protože oba effecty se jen zaháknou na různé místo té osy.

useLayoutEffect běží synchronně hned po commitu, ještě před paintem. React garantuje, že kód uvnitř — a jakýkoli setState, který v něm spustíš — proběhne dřív, než browser stihne malovat. Browser čeká.

useEffect běží až po paintu, asynchronně. Commit zapíše DOM, browser namaluje, uživatel vidí obrázek, a teprve pak se pustí tvůj effect.

// commit zapíše DOM
useLayoutEffect(() => { /* tady, browser ještě nemaloval */ })
// browser paintuje
useEffect(() => { /* tady, uživatel už vidí předchozí stav */ })

Z toho plyne celá praktická rada kolem těch dvou háčků. Není to o výkonu obecně, je to o tom, na které straně paintu potřebuješ být.


Kdy potřebuješ být před paintem

Vezmi tooltip, který se má vejít nad prvek — a když se nevejde, spadnout pod něj. Abys to rozhodl, musíš změřit jeho výšku přes getBoundingClientRect. Jenže výšku znáš až potom, co je tooltip v DOMu.

S useEffect je to takhle: React commitne tooltip s výškou 0, browser ho namaluje na špatné místo, pak doběhne effect, změří, nastaví setState a browser přemaluje. Uživatel vidí probliknutí — tooltip skočí z jedné pozice na druhou.

S useLayoutEffect proběhne měření i následný rerender celé mezi commitem a paintem. Browser namaluje až finální pozici. Žádné blikání, protože uživatel nikdy neviděl ten mezistav.

To je jediný pravý důvod sáhnout po useLayoutEffect: čteš layout a hned na základě něj překresluješ, a nechceš, aby byl mezistav vidět. Cena je, že blokuješ browser — dokud tvůj kód nedoběhne, nic se nenamaluje. Proto docs říkají jasně: preferuj useEffect, kdykoli to jde. Fetche dat, subscribe, logování, nastavení timeru — to všechno klidně smí být až po paintu.


Co si z toho odnést

Render a commit nejsou implementační detail, jsou to dvě fáze, mezi které se vejde přesná časová osa: render spočítá, commit zapíše DOM, browser paintuje. useLayoutEffect sedí před paintem a proto může měřit layout bez blikání — za cenu blokování browseru. useEffect sedí za paintem a je to tvoje výchozí volba pro cokoli, co uživatel nemusí vidět hotové před prvním překreslením.

Když příště uvidíš problikávat mezistav, neptej se, jak ho „schovat" — zeptej se, na které straně paintu ten kód běží. Skoro vždycky jsi omylem za paintem a stačí přesun na useLayoutEffect. A když naopak něco zdržuje první vykreslení, hledej těžký useLayoutEffect, který drží browser jako rukojmí.

Zdroje

Poslech místo čtení — 2 zdroje k načtení.