ನೀವು ಎಂದಾದರೂ ಟೂಲ್‌ಟಿಪ್ (tooltip) ಮೇಲಿನ ಎಡ ಮೂಲೆಯಿಂದ ಸರಿಯಾದ ಜಾಗಕ್ಕೆ ಹಾರುವಂತೆ (snaps) ನೋಡಿದ್ದೀರಾ? ಅಥವಾ ಮಾಡಲ್ (modal) ಸರಿಯಾದ ಗಾತ್ರಕ್ಕೆ ಬರುವ ಮೊದಲು ಒಂದು ಕ್ಷಣ ಮಿಂಚಿದಂತೆ (flashes) ಕಾಣುತ್ತಿದೆಯೇ? ಆ ಒಂದು ಕ್ಷಣದ ತೊಂದರೆಯೇ 'ಲೇಔಟ್ ಫ್ಲಿಕರ್' (layout flicker). React, DOM ಅನ್ನು ಓದಿ, ತಿದ್ದುಪಡಿ ಮಾಡಬೇಕೆಂದು ಲೆಕ್ಕಾಚಾರ ಮಾಡಿ, ಸ್ಟೇಟ್ (state) ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡುವಷ್ಟರಲ್ಲಿ ಬ್ರೌಸರ್ ಈಗಾಗಲೇ ಪಿಕ್ಸೆಲ್‌ಗಳನ್ನು ಸ್ಕ್ರೀನ್ ಮೇಲೆ ತೋರಿಸಲು ಪ್ರಾರಂಭಿಸಿರುತ್ತದೆ. ಇದಕ್ಕೆ ಸಾಮಾನ್ಯ ಪರಿಹಾರವೆಂದರೆ useEffect ಬದಲಿಗೆ useLayoutEffect ಬಳಸುವುದು. ಈ ಬದಲಾವಣೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ, ಆದರೆ ಬ್ರೌಸರ್ ಪೈಪ್‌ಲೈನ್‌ ಒಳಗೆ ಪ್ರತಿಯೊಂದು ಹೂಕ್ (hook) ಯಾವಾಗ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ ಎಂಬುದು ನಿಮಗೆ ನಿಖರವಾಗಿ ತಿಳಿದಿದ್ದರೆ ಮಾತ್ರ.

ಬ್ರೌಸರ್ ಪೈಪ್‌ಲೈನ್: Render, Commit, Paint

React ಒಂದು ಕಾಂಪೊನೆಂಟ್ ಅನ್ನು ಮೂರು ವಿಭಿನ್ನ ಹಂತಗಳಲ್ಲಿ ಅಪ್‌ಡೇಟ್ ಮಾಡುತ್ತದೆ. render ಹಂತದಲ್ಲಿ, React, Virtual DOM ಅನ್ನು ನಿರ್ಮಿಸುತ್ತದೆ—ಅಥವಾ ಮರುನಿರ್ಮಿಸುತ್ತದೆ—ಮತ್ತು diff ಅನ್ನು ಲೆಕ್ಕಹಾಕುತ್ತದೆ. ಇಲ್ಲಿ ಯಾವುದೇ ಪಿಕ್ಸೆಲ್ ಬದಲಾವಣೆಗಳು ಆಗುವುದಿಲ್ಲ; ಇದು ಕೇವಲ ಮೆಮೊರಿಯಲ್ಲಿ ನಡೆಯುವ ಲೆಕ್ಕಾಚಾರವಾಗಿದೆ. ನಂತರ commit ಹಂತ ಬರುತ್ತದೆ, ಅಲ್ಲಿ React ಆ ಬದಲಾವಣೆಗಳನ್ನು ನೈಜ DOM ನೋಡ್‌ಗಳಿಗೆ ಅನ್ವಯಿಸುತ್ತದೆ. ಸ್ಟೈಲ್‌ಗಳು ಅಪ್‌ಡೇಟ್ ಆಗುತ್ತವೆ, ನೋಡ್‌ಗಳನ್ನು ಸೇರಿಸಲಾಗುತ್ತದೆ ಅಥವಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ ಮತ್ತು ಪಠ್ಯವು ಬದಲಾಗುತ್ತದೆ.

ನಂತರ ಬ್ರೌಸರ್ ನಿಯಂತ್ರಣ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. paint ಹಂತದಲ್ಲಿ, ಬ್ರೌಸರ್‌ನ ರೆಂಡರಿಂಗ್ ಇಂಜಿನ್ ಲೇಔಟ್ ಜಿಯೋಮೆಟ್ರಿಯನ್ನು ಲೆಕ್ಕಹಾಕಿ ಸ್ಕ್ರೀನ್ ಮೇಲೆ ಪಿಕ್ಸೆಲ್‌ಗಳನ್ನು ಬಿಡಿಸುತ್ತದೆ. ಈ ಅನುಕ್ರಮವು ಕಟ್ಟುನಿಟ್ಟಾಗಿದೆ. ಬ್ರೌಸರ್ ಪೇಂಟ್ ಮಾಡುವ ಮೊದಲು ಲೇಔಟ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಬೇಕು ಮತ್ತು ಬಳಕೆದಾರರು ಏನನ್ನೂ ನೋಡುವ ಮೊದಲು ಪೇಂಟಿಂಗ್ ಅನ್ನು ಮುಗಿಸಬೇಕು. commit ಮತ್ತು paint ನಡುವಿನ ಅಂತರವು ಮಿಲಿಸೆಕೆಂಡುಗಳಲ್ಲಿರುತ್ತದೆ, ಆದರೆ ಅದು ನಿಜವಾದ ಅಂತರವಾಗಿದೆ ಮತ್ತು ಇದೇ useEffect ಮತ್ತು useLayoutEffect ನಡುವಿನ ವ್ಯತ್ಯಾಸ ಕಂಡುಬರುವ ಸಮಯವಾಗಿದೆ.

useEffect ಏಕೆ ಫ್ಲಿಕರ್ ಅನ್ನು ಉಂಟುಮಾಡುತ್ತದೆ

useEffect ಅಸಿಂಕ್ರೋನಸ್ (asynchronously) ಆಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ, ಅಂದರೆ ಬ್ರೌಸರ್ ಈಗಾಗಲೇ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಪೇಂಟ್ ಮಾಡಿದ ನಂತರ ಇದು ಕಾರ್ಯನಿರ್ವಹಿಸಲು ನಿಗದಿತವಾಗಿರುತ್ತದೆ. DOM ಅಪ್‌ಡೇಟ್ ಆಗಿರುತ್ತದೆ, ಪಿಕ್ಸೆಲ್‌ಗಳು ಬಿಡಿಸಲ್ಪಟ್ಟಿರುತ್ತವೆ ಮತ್ತು ನಂತರ React ನಿಮ್ಮ ಎಫೆಕ್ಟ್ ಅನ್ನು ರನ್ ಮಾಡಲು ಮರಳಿ ಬರುತ್ತದೆ.

ಒಂದು ಬಟನ್ ಅಡಿಯಲ್ಲಿ ಡ್ರಾಪ್‌ಡೌನ್ ಮೆನುವನ್ನು ನೀವು ರೆಂಡರ್ ಮಾಡುತ್ತಿದ್ದೀರಿ ಎಂದು ಭಾವಿಸಿ. useEffect ಒಳಗೆ, ನೀವು buttonRef.current.getBoundingClientRect() ಅನ್ನು ಕರೆ ಮಾಡಿ, ಸರಿಯಾದ top ಮತ್ತು left ಕೋಆರ್ಡಿನೇಟ್ಸ್‌ಗಳನ್ನು ಲೆಕ್ಕಹಾಕಿ, ಅವುಗಳನ್ನು ಸ್ಟೇಟ್‌ನಲ್ಲಿ ಸಂಗ್ರಹಿಸುತ್ತೀರಿ. useEffect ಪೇಂಟ್ ನಂತರ ರನ್ ಆಗುವುದರಿಂದ, ಬ್ರೌಸರ್ ಈಗಾಗಲೇ ಡ್ರಾಪ್‌ಡೌನ್ ಅನ್ನು ಅದರ ಡಿಫಾಲ್ಟ್ ಸ್ಥಾನದಲ್ಲಿ, ಬಹುಶಃ top: 0, left: 0 ನಲ್ಲಿ ಬಿಡಿಸಿರುತ್ತದೆ. ಆ ಪೇಂಟ್ ಆದ ನಂತರವಷ್ಟೇ ನಿಮ್ಮ ಎಫೆಕ್ಟ್ ಸ್ಟೇಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡುತ್ತದೆ. React ತಿದ್ದುಪಡಿ ಮಾಡಿದ ಕೋಆರ್ಡಿನೇಟ್ಸ್‌ಗಳನ್ನು ಕಮಿಟ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ಬ್ರೌಸರ್ ಮತ್ತೆ ಪೇಂಟ್ ಮಾಡುತ್ತದೆ. ಬಳಕೆದಾರರು ಎರಡು ಫ್ರೇಮ್‌ಗಳನ್ನು ನೋಡುತ್ತಾರೆ: ತಪ್ಪು ಸ್ಥಾನ ಮತ್ತು ನಂತರ ಸರಿಯಾದ ಸ್ಥಾನ. ಆ ದೃಶ್ಯ ಬದಲಾವಣೆಯೇ (visual snap) ಎಲ್ಲರೂ ತಪ್ಪಿಸಲು ಪ್ರಯತ್ನಿಸುವ ಫ್ಲಿಕರ್.

ಡೇಟಾ ಫೆಚಿಂಗ್ (data fetching), API ಕರೆಗಳು, ಅನಾಲಿಟಿಕ್ಸ್ ಟ್ರ್ಯಾಕಿಂಗ್ ಅಥವಾ ಇವೆಂಟ್ ಲಿಸನರ್‌ಗಳನ್ನು ಸೆಟ್ ಮಾಡಲು ಈ ವಿಳಂಬವು ಮುಖ್ಯವಲ್ಲ. ಪೇಂಟ್ ಆದ ಕೆಲವು ಮಿಲಿಸೆಕೆಂಡುಗಳ ನಂತರ ಅನಾಲಿಟಿಕ್ಸ್ ಬೀಕನ್ ಕಾರ್ಯನಿರ್ವಹಿಸಿದರೆ ಬಳಕೆದಾರರಿಗೆ ತೊಂದರೆಯಾಗುವುದಿಲ್ಲ. ವಾಸ್ತವವಾಗಿ, ದೃಶ್ಯೇತರ ಕೆಲಸಗಳನ್ನು (non-visual work) ಪೇಂಟ್ ನಂತರದವರೆಗೆ ಮುಂದೂಡುವುದು ಆರಂಭಿಕ ರೆಂಡರ್ ಅನ್ನು ಸ್ಪಂದನೀಯವಾಗಿ (responsive) ಇಡುತ್ತದೆ. ಆದರೆ ಲೇಔಟ್‌ಗೆ ಸಂಬಂಧಿಸಿದ ತಿದ್ದುಪಡಿಗಳಿಗಾಗಿ, useEffect ತುಂಬಾ ತಡವಾಗುತ್ತದೆ.

useLayoutEffect ಪೇಂಟನ್ನು ಹೇಗೆ ತಡೆಯುತ್ತದೆ

useLayoutEffect ಸಿಂಕ್ರೋನಸ್ (synchronously) ಆಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ, ಅಂದರೆ React, DOM ಅನ್ನು ಮ್ಯುಟೇಟ್ ಮಾಡಿದ ತಕ್ಷಣವೇ, ಆದರೆ ಬ್ರೌಸರ್ ಲೇಔಟ್ ಅನ್ನು ಲೆಕ್ಕಹಾಕುವ ಅಥವಾ ಪಿಕ್ಸೆಲ್‌ಗಳನ್ನು ಪೇಂಟ್ ಮಾಡುವ ಮೊದಲು ಇದು ರನ್ ಆಗುತ್ತದೆ. ಇದು ಪೇಂಟ್ ಪೈಪ್‌ಲೈನ್ ಅನ್ನು ಸಂಪೂರ್ಣವಾಗಿ ತಡೆಯುತ್ತದೆ.

ನೀವು ಅದೇ ಡ್ರಾಪ್‌ಡೌನ್ ಅಳತೆಯನ್ನು useLayoutEffect ಒಳಗೆ ಮಾಡಿದರೆ, ಅನುಕ್ರಮವು ಬದಲಾಗುತ್ತದೆ. React ಆರಂಭಿಕ DOM ಅಪ್‌ಡೇಟ್ ಅನ್ನು ಕಮಿಟ್ ಮಾಡುತ್ತದೆ, ನಿಮ್ಮ ಲೇಔಟ್ ಎಫೆಕ್ಟ್ ಅನ್ನು ರನ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ನಿಮ್ಮ ಸ್ಟೇಟ್ ಅಪ್‌ಡೇಟ್ ಒಂದು ಸಿಂಕ್ರೋನಸ್ ರೀ-ರೆಂಡರ್ ಅನ್ನು ಪ್ರಚೋದಿಸುತ್ತದೆ. React ತಿದ್ದುಪಡಿ ಮಾಡಿದ ಕೋಆರ್ಡಿನೇಟ್ಸ್‌ಗಳನ್ನು ಕಮಿಟ್ ಮಾಡುತ್ತದೆ ಮತ್ತು ನಂತರವೇ ಬ್ರೌಸರ್ ಪೇಂಟ್ ಮಾಡುತ್ತದೆ. ಬಳಕೆದಾರರು ಈಗಾಗಲೇ ಸರಿಯಾಗಿರುವ ಒಂದು ಫ್ರೇಮ್ ಅನ್ನು ನೋಡುತ್ತಾರೆ.

ಈ ಬ್ಲಾಕಿಂಗ್ ವರ್ತನೆಯು ಒಂದು ವೈಶಿಷ್ಟ್ಯವೂ ಹೌದು ಮತ್ತು ಅಪಾಯವೂ ಹೌದು. useLayoutEffect ತನ್ನ ಕೆಲಸ ಮುಗಿಸುವವರೆಗೆ ಬ್ರೌಸರ್ ಪೇಂಟ್ ಮಾಡದಂತೆ ತಡೆಯುವುದರಿಂದ, ಅದರೊಳಗೆ ಯಾವುದೇ ಭಾರೀ ಲೆಕ್ಕಾಚಾರಗಳು (heavy computation) ಇದ್ದರೆ UI ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ (freezes). ಕೆಲವು ಡಜನ್ ಮಿಲಿಸೆಕೆಂಡುಗಳ ಬ್ಲಾಕ್ ಮಾಡಿದ ಪೇಂಟ್ ಕೂಡ ಬಳಕೆದಾರರಿಗೆ 'ಜಾಂಕ್' (jank) ತರಹ ಅನಿಸುತ್ತದೆ. ಅದಕ್ಕಾಗಿಯೇ React ಡಾಕ್ಯುಮೆಂಟ್‌ಗಳು ನೀವು useEffect ಇಂದ ಪ್ರಾರಂಭಿಸಲು ಮತ್ತು ನೀವು ಸಹಿಸಿಕೊಳ್ಳಲಾಗದ ಫ್ಲಿಕರ್ ಅನ್ನು ಗಮನಿಸಿದಾಗ ಮಾತ್ರ useLayoutEffect ಗೆ ಬದಲಾಯಿಸಲು ಸ್ಪಷ್ಟವಾಗಿ ಹೇಳುತ್ತವೆ.

ಪ್ರತಿಯೊಂದು ಹೂಕ್ ಅನ್ನು ಯಾವಾಗ ಬಳಸಬೇಕು

ನಿಮ್ಮ ಹೆಚ್ಚಿನ ಲಾಜಿಕ್ useEffect ನಲ್ಲಿ ಇರಬೇಕು. ಇದನ್ನು ಈ ಕೆಳಗಿನವುಗಳಿಗಾಗಿ ಬಳಸಿ:

  • API ಇಂದ ಡೇಟಾವನ್ನು ಫೆಚ್ ಮಾಡುವುದು
  • ಸಬ್‌ಸ್ಕ್ರಿಪ್ಶನ್‌ಗಳು ಅಥವಾ ಇವೆಂಟ್ ಲಿಸನರ್‌ಗಳನ್ನು ಸೆಟ್ ಮಾಡುವುದು
  • ಅನಾಲಿಟಿಕ್ಸ್ ಇವೆಂಟ್‌ಗಳನ್ನು ಕಳುಹಿಸುವುದು
  • ಲೇಔಟ್ ಅನ್ನು ತಕ್ಷಣವೇ ಓದದ ಅಥವಾ ಮ್ಯುಟೇಟ್ ಮಾಡದ ಯಾವುದೇ ಸೈಡ್ ಎಫೆಕ್ಟ್ (side effect)

ಬಳಕೆದಾರರು ಫ್ರೇಮ್ ಅನ್ನು ನೋಡುವ ಮೊದಲೇ DOM ಅನ್ನು ಓದಿ ಮತ್ತು ಮರಳಿ ಬರೆಯಲೇಬೇಕಾದ ಕಾರ್ಯಾಚರಣೆಗಳಿಗಾಗಿ useLayoutEffect ಅನ್ನು ಮೀಸಲಿಡಿ:

  • ಎಲಿಮೆಂಟ್‌ನ ಡೈಮೆನ್ಶನ್‌ಗಳನ್ನು ಅಳೆಯುವುದು (ಉದಾಹರಣೆಗೆ: width, height ಅಥವಾ scroll position)
  • ಟೂಲ್‌ಟಿಪ್‌ಗಳು, ಪಾಪೋವರ್‌ಗಳು ಅಥವಾ ಕಾಂಟೆಕ್ಸ್ಟ್ ಮೆನುಗಳಿಗಾಗಿ ಕೋಆರ್ಡಿನೇಟ್ಸ್‌ಗಳನ್ನು ಲೆಕ್ಕಹಾಕುವುದು
  • ದೃಶ್ಯ ಸ್ಥಾನವು ರೆಂಡರ್ ಆದ ಜಿಯೋಮೆಟ್ರಿಯ ಮೇಲೆ ಅವಲಂಬಿತವಾಗಿದ್ದಾಗ ಲೇಔಟ್ ಶಿಫ್ಟ್‌ಗಳನ್ನು ತಡೆಯುವುದು

ಯಾವುದನ್ನು ಆರಿಸಬೇಕೆಂದು ನಿಮಗೆ ಖಚಿತವಿಲ್ಲದಿದ್ದರೆ, useEffect ಅನ್ನು ಡಿಫಾಲ್ಟ್ ಆಗಿ ಬಳಸಿ. ದೃಶ್ಯ ಅಸ್ಥಿರತೆಯನ್ನು (visual instability) ಗಮನಿಸಿದಾಗ ಮಾತ್ರ useLayoutEffect ಗೆ ಬದಲಾಯಿಸಿ. ಈ ನಿಯಮವು ಹೆಚ್ಚಿನ React ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಸುಗಮವಾಗಿ ಚಲಿಸುವಂತೆ ಮಾಡುತ್ತದೆ.

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.