நீங்கள் ஒரு டூல்டிப் (tooltip) உருவாக்கியிருக்கிறீர்களா? அது இடது மேல் மூலையிலிருந்து அதன் சரியான இடத்திற்குத் திடீரெனத் தாவுகிறதா? அல்லது ஒரு மோடல் (modal) சரியான அளவிற்குத் தெரியாமல், முதலில் தவறான அளவில் தோன்றிவிட்டு பிறகு சரியான நிலைக்குத் திரும்புகிறதா? அந்த ஒரு நொடி ஏற்படும் பிழைதான் 'layout flicker'. React DOM-ஐப் படித்து, ஒரு திருத்தத்தைக் கணக்கிட்டு, ஸ்டேட்டை (state) புதுப்பிக்கும்போது, பிரவுசர் ஏற்கனவே பிக்சல்களைத் திரையில் காட்டத் தொடங்கிவிடுவதால் இது நிகழ்கிறது. இதற்குப் பொதுவாகப் பரிந்துரைக்கப்படும் தீர்வு useEffect-க்கு பதிலாக useLayoutEffect-ஐப் பயன்படுத்துவதாகும். இந்த மாற்றம் வேலை செய்யும், ஆனால் பிரவுசர் பைப்லைனில் ஒவ்வொரு ஹுக்கும் (hook) சரியாக எப்போது இயங்குகிறது என்பதை நீங்கள் புரிந்துகொண்டால் மட்டுமே இது சாத்தியம்.

பிரவுசர் பைப்லைன்: Render, Commit, Paint

React ஒரு கூறினை (component) மூன்று வெவ்வேறு நிலைகளில் புதுப்பிக்கிறது. render நிலையில், React விர்ச்சுவல் DOM-ஐ (Virtual DOM) உருவாக்குகிறது அல்லது மீண்டும் உருவாக்குகிறது மற்றும் மாற்றங்களைக் (diff) கணக்கிடுகிறது. இதில் உண்மையான பிக்சல் மாற்றங்கள் எதுவும் நிகழாது; இது நினைவகத்தில் (memory) நடக்கும் தூய கணக்கீடு மட்டுமே. அடுத்து commit நிலை வருகிறது, இங்கே React அந்த மாற்றங்களை உண்மையான DOM நோட்களுக்கு (nodes) பயன்படுத்துகிறது. ஸ்டைல்கள் புதுப்பிக்கப்படுகின்றன, நோட்கள் சேர்க்கப்படுகின்றன அல்லது நீக்கப்படுகின்றன, மற்றும் உரை (text) மாற்றங்கள் செய்யப்படுகின்றன.

அதன்பின் பிரவுசர் பொறுப்பேற்கிறது. paint நிலையில், பிரவுசரின் ரெண்டரிங் இன்ஜின் (rendering engine) லேஅவுட் வடிவியலைக் (layout geometry) கணக்கிட்டு, திரையில் பிக்சல்களை வரைகிறது. இந்த வரிசைமுறை மிகவும் இறுக்கமானது. பிரவுசர் பெயிண்ட் செய்வதற்கு முன் லேஅவுட்டை முடிக்க வேண்டும், மேலும் பயனர் எதையும் புதிதாகப் பார்ப்பதற்கு முன் பெயிண்டிங்கை முடிக்க வேண்டும். commit மற்றும் paint ஆகியவற்றுக்கு இடையிலான இடைவெளி மில்லிசெகண்டுகளில் அளவிடப்படுகிறது, ஆனால் அது உண்மையானது, மேலும் useEffect மற்றும் useLayoutEffect ஆகியவற்றுக்கு இடையிலான வேறுபாட்டைத் தீர்மானிக்கும் விண்டோ இதுவேயாகும்.

ஏன் useEffect பிளிக்கரைனை (flicker) ஏற்படுத்துகிறது?

useEffect அசின்க்ரோனஸாக (asynchronously) இயங்குகிறது, அதாவது பிரவுசர் ஏற்கனவே திரையை பெயிண்ட் செய்த பிறகு இது இயங்குவதற்குத் திட்டமிடப்பட்டுள்ளது. DOM புதுப்பிக்கப்படுகிறது, பிக்சல்கள் வரையப்படுகின்றன, அதன் பிறகு React உங்கள் எஃபெக்ட்டை (effect) இயக்கத் திரும்புகிறது.

ஒரு பட்டனுக்குக் கீழே ஒரு டிராப்டவுன் மெனுவை (dropdown menu) நீங்கள் ரெண்டர் செய்கிறீர்கள் என்று கற்பனை செய்து பாருங்கள். useEffect-க்குள், நீங்கள் buttonRef.current.getBoundingClientRect() என்பதை அழைத்து, சரியான top மற்றும் left ஆயத்தொலைவுகளைக் (coordinates) கணக்கிட்டு, அவற்றை ஸ்டேட்டில் சேமிக்கிறீர்கள். useEffect பெயிண்டிற்குப் பிறகு இயங்குவதால், பிரவுசர் ஏற்கனவே டிராப்டவுனை அதன் இயல்பு நிலைப் பொசிஷனில் (ஒருவேளை top: 0, left: 0 இல்) வரைந்துவிட்டது. அந்த பெயிண்டிற்குப் பிறகுதான் உங்கள் எஃபெக்ட் ஸ்டேட்டைப் புதுப்பிக்கிறது. React திருத்தப்பட்ட ஆயத்தொலைவுகளை commit செய்கிறது, பின்னர் பிரவுசர் மீண்டும் பெயிண்ட் செய்கிறது. பயனர் இரண்டு பிரேம்களைப் பார்க்கிறார்: முதலில் தவறான நிலை, பிறகு சரியான நிலை. அந்தத் திடீர் நகர்வுதான் (visual snap) அனைவரும் தவிர்க்க முயலும் அந்த பிளிக்கரேன் (flicker).

டேட்டா ஃபெட்சிங் (data fetching), API அழைப்புகள், அனலிட்டிக்ஸ் ட்ராக்கிங் (analytics tracking) அல்லது ஈவென்ட் லிசனர்ஸை (event listeners) அமைப்பது போன்றவற்றுக்கு இந்தத் தாமதம் முக்கியமல்ல. பெயிண்டிற்கு சில மில்லிசெகண்டுகள் கழித்து ஒரு அனலிட்டிக்ஸ் பீக்கன் (analytics beacon) இயங்கினால் பயனர் கவலைப்பட மாட்டார். உண்மையில், விஷுவல் அல்லாத வேலைகளை பெயிண்டிற்குப் பிறகு தள்ளிப்போடுவது ஆரம்ப ரெண்டரிங்கை (initial render) விரைவாக வைத்திருக்க உதவும். ஆனால் லேஅவுட் சார்ந்த திருத்தங்களுக்கு, useEffect மிகவும் தாமதமாகிவிடுகிறது.

useLayoutEffect எவ்வாறு பெயிண்டைத் (paint) தடுக்கிறது?

useLayoutEffect சின்க்ரோனஸாக (synchronously) இயங்குகிறது, அதாவது React DOM-ஐ மாற்றியமைத்த உடனேயே, ஆனால் பிரவுசர் லேஅவுட்டைக் கணக்கிட அல்லது பிக்சல்களை வரைய வாய்ப்பு கிடைப்பதற்கு முன்பே இது இயங்கும். இது பெயிண்ட் பைப்லைனை முழுமையாகத் தடுக்கிறது.

அதே டிராப்டவுன் அளவீட்டை நீங்கள் useLayoutEffect-க்குள் செய்தால், வரிசைமுறை மாறும். React ஆரம்ப DOM புதுப்பிப்பை commit செய்கிறது, உங்கள் லேஅவுட் எஃபெக்ட்டை இயக்குகிறது, மேலும் உங்கள் ஸ்டேட் அப்டேட் ஒரு சின்க்ரோனஸ் ரீ-ரெண்டரைத் (re-render) தூண்டுகிறது. React திருத்தப்பட்ட ஆயத்தொலைவுகளை commit செய்கிறது, அதன் பின்னரே பிரவுசர் பெயிண்ட் செய்கிறது. பயனர் ஏற்கனவே சரியான நிலையில் உள்ள ஒரே ஒரு பிரேமை மட்டுமே பார்க்கிறார்.

இந்தத் தடுக்கும் நடத்தை (blocking behavior) ஒரு வசதி மற்றும் ஒரு ஆபத்து ஆகிய இரண்டையுமே கொண்டுள்ளது. useLayoutEffect அது முடிக்கும் வரை பிரவுசர் பெயிண்ட் செய்வதைத் தடுப்பதால், அதற்குள் செய்யப்படும் எந்தவொரு கனமான கணக்கீடும் UI-ஐ உறைய வைக்கும் (freeze). சில டஜன் மில்லிசெகண்டுகள் பெயிண்ட் தடுக்கப்பட்டாலும், அது பயனருக்குத் தட்டுப்பாடாக (jank) உணரப்படும். இதனால்தான் React ஆவணங்கள் முதலில் useEffect-ஐப் பயன்படுத்தவும், நீங்கள் தாங்க முடியாத பிளிக்கரேனை (flicker) உண்மையில் காணும்போது மட்டும் useLayoutEffect-க்கு மாறவும் தெளிவாகக் கூறுகின்றன.

ஒவ்வொரு ஹுக்கையும் எப்போது பயன்படுத்த வேண்டும்?

உங்கள் பெரும்பாலான லாஜிக் useEffect-ல் இருக்க வேண்டும். இதைப் பயன்படுத்துங்கள்:

  • API-லிருந்து டேட்டாவைப் பெற (Fetching data)
  • சப்ஸ்கிரிப்ஷன்கள் (subscriptions) அல்லது ஈவென்ட் லிசனர்ஸை அமைக்க
  • அனலிட்டிக்ஸ் ஈவென்ட்களை அனுப்ப
  • லேஅவுட்டை உடனடியாகப் படிக்காத அல்லது மாற்றாத எந்தவொரு சைடு எஃபெக்ட் (side effect) செயல்பாட்டிற்கும்

பயனர் பிரேமைப் பார்ப்பதற்கு முன்பே DOM-ஐப் படித்து மீண்டும் எழுத வேண்டிய செயல்பாடுகளுக்கு மட்டும் useLayoutEffect-ஐ ஒதுக்குங்கள்:

  • அகலம் (width), உயரம் (height) அல்லது ஸ்க்ரோல் பொசிஷன் போன்ற எலிமென்ட் அளவுகளை அளவிடுதல்
  • டூல்டிப்கள் (tooltips), பாப்ஓவர்கள் (popovers) அல்லது கான்டெக்ஸ்ட் மெனுக்களுக்கான (context menus) ஆயத்தொலைவுகளைக் கணக்கிடுதல்
  • விஷுவல் பொசிஷன் ரெண்டர் செய்யப்பட்ட வடிவியலைச் சார்ந்து இருக்கும்போது, லேஅவுட் நகர்வைத் (layout shifts) தடுத்தல்

எதைத் தேர்ந்தெடுப்பது என்று உங்களுக்குத் தெரியவில்லை என்றால், useEffect-ஐத் தேர்ந்தெடுப்பதே சிறந்தது. விஷுவல் நிலையற்ற தன்மையை (visual instability) நீங்கள் கவனிக்கும்போது மட்டும் useLayoutEffect-க்கு மாறவும். இந்த ஒரு விதி மட்டுமே பெரும்பாலான React அப்ளிகேஷன்கள் சீராக இயங்குவதை உறுதி செய்யும்.

சர்வர்-சைடு ரெண்டரிங் (Server-Side Rendering) சிக்கல்

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.