શું તમે ક્યારેય એવું ટૂલટિપ બનાવ્યું છે જે ટોપ-લેફ્ટ ખૂણેથી તેના સાચા સ્થાને ઝટકા સાથે જાય છે? અથવા એવું મોડલ જે તેની સાચી સાઈઝમાં સ્થિર થતા પહેલા ખોટી સાઈઝમાં ફ્લેશ થાય છે? આ ક્ષણિક ખામીને 'લેઆઉટ ફ્લિકર' (layout flicker) કહેવામાં આવે છે. આ ત્યારે થાય છે જ્યારે React DOM વાંચે છે, સુધારો કરવા માટે ગણતરી કરે છે અને સ્ટેટ અપડેટ કરે છે, પરંતુ બ્રાઉઝર પહેલેથી જ સ્ક્રીન પર પિક્સેલ્સ બતાવવાનું શરૂ કરી ચૂક્યું હોય છે. સામાન્ય ઉપાય useEffect ને બદલે useLayoutEffect નો ઉપયોગ કરવાનો છે. આ બદલાવ કામ તો કરે છે, પરંતુ તે ત્યારે જ કામ કરે છે જ્યારે તમે બ્રાઉઝર પાઇપલાઇનમાં દરેક હૂક (hook) ક્યારે ફાયર થાય છે તે બરાબર સમજો.
બ્રાઉઝર પાઇપલાઇન: રેન્ડર, કમિટ, પેઇન્ટ
React ત્રણ અલગ-અલગ તબક્કામાં ઘટક (component) ને અપડેટ કરે છે. render ફેઝમાં, React વર્ચ્યુઅલ DOM બનાવે છે—અથવા ફરીથી બનાવે છે—અને તફાવત (diff) ની ગણતરી કરે છે. હજુ સુધી કોઈ વાસ્તવિક પિક્સેલ ફેરફારો થતા નથી; આ માત્ર મેમરીમાં થતી ગણતરી છે. ત્યારબાદ commit ફેઝ આવે છે, જ્યાં React તે ફેરફારોને વાસ્તવિક DOM નોડ્સ પર લાગુ કરે છે. સ્ટાઇલ્સ અપડેટ થાય છે, નોડ્સ ઉમેરવામાં અથવા દૂર કરવામાં આવે છે, અને ટેક્સ્ટ બદલાય છે.
ત્યારબાદ બ્રાઉઝર નિયંત્રણ લે છે. paint ફેઝમાં, બ્રાઉઝરનું રેન્ડરિંગ એન્જિન લેઆઉટ ભૂમિતિ (geometry) ની ગણતરી કરે છે અને સ્ક્રીન પર પિક્સેલ્સ દોરે છે. આ ક્રમ કડક છે. બ્રાઉઝરે પેઇન્ટ કરતા પહેલા લેઆઉટ પૂર્ણ કરવું આવશ્યક છે, અને વપરાશકર્તા કંઈપણ નવું જુએ તે પહેલાં તેણે પેઇન્ટિંગ પૂરું કરવું આવશ્યક છે. કમિટ અને પેઇન્ટ વચ્ચેનો તફાવત મિલિસેકન્ડમાં માપવામાં આવે છે, પરંતુ તે વાસ્તવિક છે, અને આ એ વિન્ડો છે જ્યાં useEffect અને useLayoutEffect અલગ પડે છે.
useEffect શા માટે ફ્લિકર પેદા કરે છે
useEffect અસિંક્રોનસલી (asynchronously) ચાલે છે, જે બ્રાઉઝર દ્વારા સ્ક્રીન પેઇન્ટ કર્યા પછી ફાયર કરવા માટે શેડ્યૂલ કરવામાં આવે છે. DOM અપડેટ થઈ જાય છે, પિક્સેલ્સ દોરાઈ જાય છે, અને પછી React તમારો ઇફેક્ટ ચલાવવા માટે પાછું આવે છે.
ધારો કે તમે બટન નીચે ડ્રોપડાઉન મેનૂ રેન્ડર કરો છો. useEffect ની અંદર, તમે buttonRef.current.getBoundingClientRect() કોલ કરો છો, સાચા top અને left કોઓર્ડિનેટ્સની ગણતરી કરો છો, અને તેને સ્ટેટમાં સ્ટોર કરો છો. કારણ કે useEffect પેઇન્ટ પછી ચાલે છે, બ્રાઉઝર પહેલેથી જ ડ્રોપડાઉનને તેની ડિફોલ્ટ પોઝિશન પર, કદાચ top: 0, left: 0 પર દોરી ચૂક્યું હોય છે. તે પેઇન્ટ થયા પછી જ તમારો ઇફેક્ટ સ્ટેટ અપડેટ કરે છે. React સુધારેલા કોઓર્ડિનેટ્સ કમિટ કરે છે, અને બ્રાઉઝર ફરીથી પેઇન્ટ કરે છે. વપરાશકર્તા બે ફ્રેમ્સ જુએ છે: પહેલા ખોટી પોઝિશન, પછી સાચી પોઝિશન. આ વિઝ્યુઅલ ઝટકો (snap) એ ફ્લિકર છે જેને દરેક ટાળવાનો પ્રયાસ કરે છે.
ડેટા ફેચિંગ, API કોલ્સ, એનાલિટિક્સ ટ્રેકિંગ અથવા ઇવેન્ટ લિસનર્સ સેટઅપ કરવા માટે, આ વિલંબ મહત્વનો નથી. વપરાશકર્તાને તેનાથી કોઈ ફરક પડતો નથી કે એનાલિટિક્સ બીકન પેઇન્ટ થયાના થોડા મિલિસેકન્ડ પછી ફાયર થાય છે. હકીકતમાં, નોન-વિઝ્યુઅલ કામને પેઇન્ટ પછી સુધી રાખવાથી પ્રારંભિક રેન્ડર પ્રતિભાવશીલ (responsive) રહે છે. પરંતુ લેઆઉટ-આધારિત સુધારાઓ માટે, useEffect ઘણું મોડું છે.
useLayoutEffect પેઇન્ટને કેવી રીતે બ્લોક કરે છે
useLayoutEffect સિંક્રોનસલી (synchronously) ચાલે છે, React દ્વારા DOM માં ફેરફાર કર્યા પછી તરત જ, પરંતુ બ્રાઉઝર પાસે લેઆઉટની ગણતરી કરવાની અથવા પિક્સેલ્સ પેઇન્ટ કરવાની તક મળે તે પહેલાં. તે પેઇન્ટ પાઇપલાઇનને સંપૂર્ણપણે બ્લોક કરે છે.
જો તમે useLayoutEffect ની અંદર સમાન ડ્રોપડાઉન મેઝરમેન્ટ કરો છો, તો ક્રમ બદલાઈ જાય છે. React પ્રારંભિક DOM અપડેટ કમિટ કરે છે, તમારો લેઆઉટ ઇફેક્ટ ચલાવે છે, અને તમારું સ્ટેટ અપડેટ એક સિંક્રોનસ રી-રેન્ડર ટ્રિગર કરે છે. React સુધારેલા કોઓર્ડિનેટ્સ કમિટ કરે છે, અને ત્યાર પછી જ બ્રાઉઝર પેઇન્ટ કરે છે. વપરાશકર્તા એક જ ફ્રેમ જુએ છે, જે પહેલેથી જ સાચી હોય છે.
આ બ્લોકિંગ બિહેવિયર એ સુવિધા અને જોખમ બંને છે. કારણ કે useLayoutEffect બ્રાઉઝરને તે પૂરું ન થાય ત્યાં સુધી પેઇન્ટ કરતા અટકાવે છે, તેથી તેની અંદર કોઈપણ ભારે ગણતરી UI ને ફ્રીઝ કરી દે છે. બ્લોક થયેલ પેઇન્ટના થોડા ડઝન મિલિસેકન્ડ પણ વપરાશકર્તાને 'જૅન્ક' (jank) જેવું લાગે છે. આથી જ React ડોક્યુમેન્ટેશન સ્પષ્ટપણે કહે છે કે useEffect થી શરૂઆત કરો અને useLayoutEffect નો ઉપયોગ ત્યારે જ કરો જ્યારે તમે ખરેખર એવો ફ્લિકર જુઓ જેને તમે સહન કરી શકતા નથી.
કયા હૂકનો ઉપયોગ ક્યારે કરવો
તમારું મોટાભાગનું લોજિક useEffect માં હોવું જોઈએ. તેનો ઉપયોગ આ માટે કરો:
- API માંથી ડેટા ફેચ કરવા માટે
- સબ્સ્ક્રિપ્શન્સ અથવા ઇવેન્ટ લિસનર્સ સેટઅપ કરવા માટે
- એનાલિટિક્સ ઇવેન્ટ્સ મોકલવા માટે
- કોઈપણ સાઇડ ઇફેક્ટ જે તરત જ લેઆઉટ વાંચતી નથી અથવા તેમાં ફેરફાર કરતી નથી
useLayoutEffect ને એવા ઓપરેશન્સ માટે અનામત રાખો જે વપરાશકર્તા ફ્રેમ જુએ તે પહેલાં DOM વાંચવા અને પાછું લખવા માટે અનિવાર્ય હોય:
- એલિમેન્ટના પરિમાણો (dimensions) માપવા, જેમ કે પહોળાઈ (width), ઊંચાઈ (height), અથવા સ્ક્રોલ પોઝિશન
- ટૂલટિપ્સ, પોપઓવર્સ અથવા કોન્ટેક્સ્ટ મેનૂ માટે કોઓર્ડિનેટ્સની ગણતરી કરવી
- જ્યારે વિઝ્યુઅલ પોઝિશન રેન્ડર થયેલ ભૂમિતિ પર આધારિત હોય ત્યારે દેખીતા લેઆઉટ શિફ્ટને અટકાવવા
જો તમે અનિશ્ચિત હોવ કે કયું પસંદ કરવું, તો useEffect ને ડિફોલ્ટ તરીકે પસંદ કરો. જ્યારે તમે વિઝ્યુઅલ અસ્થિરતા નોંધો ત્યારે જ useLayoutEffect પર જાઓ. આ નિયમ જ મોટાભાગની React એપ્લિકેશનોને સરળતાથી ચલાવવામાં મદદ કરશે.
સર્વર-સાઇડ રેન્ડરિંગની મુશ્કેલી (The Server-Side Rendering Gotcha)
If you use Next.js, Remix, or any framework that renders React on the server, you will hit a warning with useLayoutEffect. Because the server has no DOM, the hook has nothing to measure. React warns you that it expected a browser environment and did not find one. During hydration, this mismatch can also cause subtle bugs because the server-rendered markup and the client’s first intended render may differ.
The standard fix is an isomorphic hook that selects the right effect based on the environment:
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
Use this wrapper in any component that must measure DOM nodes but might execute during server rendering. It silences the warning and keeps your server output consistent.
Performance and Best Practices
Since useLayoutEffect blocks painting, keep the body of the hook as light as possible. Read the layout value, compute the correction, and write it back. Do not fetch data, parse large objects, or run expensive algorithms inside it. Heavy code here will stall the main thread and make your interface feel frozen.
When you measure elements, use React refs rather than document.getElementById. Refs are tied to your component instance, survive re-renders without query tricks, and work reliably with portals or conditional rendering. Global ID lookups break component encapsulation and can return null at exactly the moment you need them.
useEffect is the right default for nearly every side effect. It lets the browser paint without interruption and handles data, events, and external synchronization cleanly. useLayoutEffect is a specialized tool for a specific problem: reading layout and writing back before the paint. Master the timing difference between them, and you will stop chasing flickers and start preventing them.
The real takeaway: Start with useEffect for everything. The moment you see a tooltip or modal blink into the wrong place before correcting itself, that is your signal. Switch to useLayoutEffect, measure the DOM, adjust your layout, and let the browser paint once—correctly.
