EN/CH Mode
BRUCE_FE React Interview Notes - Choosing between useEffect and useLayoutEffect
Deep comparison of useEffect and useLayoutEffect execution timing, use cases, and performance impact. Understand their differences in React rendering process, when to use useLayoutEffect to avoid flickering, and how to handle them properly in SSR environments.
EN/CH Mode

Lazy to read articles? Then watch videos!
Fundamental Differences in Execution Timing
The APIs of useEffect and useLayoutEffect are identical, but they differ in their execution timing within React's rendering process:
| Hook | Execution Timing | Sync/Async | Screen Update Timing |
|---|---|---|---|
useEffect | After render, after browser paint | Asynchronous | User sees updates first, then effect executes |
useLayoutEffect | After render, before browser paint | Synchronous | User sees updates after effect execution |
Rendering Process Comparison:
useEffect Flow:
- 1. React updates Virtual DOM and calculates differences
- 2. React updates actual DOM
- 3. Browser paints screen (user sees updates)
- 4. Then executes useEffect callback
useLayoutEffect Flow:
- 1. React updates Virtual DOM and calculates differences
- 2. React updates actual DOM
- 3. Executes useLayoutEffect callback
- 4. Browser paints screen (user sees updates)
// Same basic syntax, but different execution timing
import { useEffect, useLayoutEffect } from 'react';
function ExampleComponent() {
const [state, setState] = useState(initialState);
// Executes after screen paint (non-blocking)
useEffect(() => {
// Code here runs after screen update
// User sees UI even if operation takes time
}, [dependencies]);
// Executes before screen paint (blocking)
useLayoutEffect(() => {
// Code here runs after DOM update but before screen paint
// Operations here block screen update until completion
}, [dependencies]);
return <div>...</div>;
}Comparison of Use Cases
Simply put, the choice between useEffect and useLayoutEffect depends on when you need the side effects to execute:
| Hook | Use Cases | Practical Examples |
|---|---|---|
useEffectRecommended First Choice |
|
|
useLayoutEffectFor Specific Cases |
|
|
Remember: Unless you need to perform DOM operations before screen painting (to avoid flicker or need precise measurements), you should use useEffect. useLayoutEffect blocks rendering and may affect performance.
Performance Impact
Choosing the appropriate hook can significantly impact application performance:
useEffect Performance Characteristics
- 1. Non-blocking rendering - screen updates don't wait for effect completion
- 2. Allows browser to paint UI before executing time-consuming operations
- 3. More efficient for most side effects
- 4. May cause visual flicker (if effect changes visible elements)
useLayoutEffect Performance Characteristics
- 1. Blocking rendering - delays screen updates until effect completes
- 2. Time-consuming operations in effect cause noticeable UI delays
- 3. Prevents flickering issues
- 4. More suitable for operations requiring synchronous DOM updates
// Performance impact case: flickering issue
// ❌ Using useEffect may cause flickering
function FlickeringExample() {
const [width, setWidth] = useState(0);
const divRef = useRef();
useEffect(() => {
// Issue: Initial state is rendered first, user sees this frame
// Then measurement and update happen, causing content to jump
setWidth(divRef.current?.getBoundingClientRect().width || 0);
}, []);
return <div ref={divRef} style={{ width: `${width}px` }}>Content may flicker</div>;
}
// ✅ Using useLayoutEffect prevents flickering
function SmoothExample() {
const [width, setWidth] = useState(0);
const divRef = useRef();
useLayoutEffect(() => {
// Advantage: Measurement and update complete before screen paint
// User only sees final result, no intermediate states
setWidth(divRef.current?.getBoundingClientRect().width || 0);
}, []);
return <div ref={divRef} style={{ width: `${width}px` }}>Content won't flicker</div>;
}Practical Examples: When to Choose Each
Typical Examples of Using useLayoutEffect
// Example 1: Tooltip Positioning - Calculate position before screen paint
function Tooltip({ text, targetRef }) {
const [position, setPosition] = useState({ top: 0, left: 0 });
const tooltipRef = useRef(null);
useLayoutEffect(() => {
// Get dimensions of target element and tooltip
// Calculate tooltip position (centered above target)
// Update position state immediately, ensure completion before render
if (targetRef.current && tooltipRef.current) {
const targetRect = targetRef.current.getBoundingClientRect();
const tooltipRect = tooltipRef.current.getBoundingClientRect();
setPosition({
top: targetRect.top - tooltipRect.height - 10,
left: targetRect.left + targetRect.width / 2 - tooltipRect.width / 2
});
}
}, [targetRef]);
return <div ref={tooltipRef} style={{position: 'fixed', top: `${position.top}px`, left: `${position.left}px`}}>{text}</div>;
}
// Example 2: Scroll to Element - Prevent flickering
function ScrollToElement({ elementId }) {
useLayoutEffect(() => {
// Find target element and scroll immediately
// Execute before screen paint, user won't see intermediate states
const element = document.getElementById(elementId);
if (element) element.scrollIntoView({ behavior: 'smooth' });
}, [elementId]);
return null;
}Typical Examples of Using useEffect
// Example 1: Data Fetching
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Non-blocking operation, user can see loading state first
setLoading(true);
fetchUser(userId)
.then(data => {
setUser(data);
setLoading(false);
})
.catch(error => {
console.error(error);
setLoading(false);
});
}, [userId]);
if (loading) return <div>Loading...</div>;
if (!user) return <div>User not found</div>;
return <div>{user.name}</div>;
}SSR (Server-Side Rendering) Special Considerations
In server-side rendering (SSR) environments, useLayoutEffect will show a warning and won't execute because there is no "layout phase" on the server. This is because:
- 1. Server has no browser environment, cannot perform DOM measurements or updates
- 2. The concept of 'browser painting' doesn't exist during SSR
- 3. React on the server only renders HTML strings, no actual DOM manipulation
Handling useLayoutEffect in SSR environments:
// Method 1: Conditionally use different hooks
import { useEffect, useLayoutEffect } from 'react';
// Choose appropriate hook based on environment
const useIsomorphicLayoutEffect =
typeof window !== 'undefined' ? useLayoutEffect : useEffect;
function MyComponent() {
useIsomorphicLayoutEffect(() => {
// In browser, this behaves like useLayoutEffect
// On server, this behaves like useEffect (actually doesn't execute)
// ...
}, []);
return <div>...</div>;
}
// Method 2: Delay DOM operations until client-side
function SSRSafeComponent() {
const [isMounted, setIsMounted] = useState(false);
// Set as mounted on first client-side render
useEffect(() => {
setIsMounted(true);
}, []);
// Only execute DOM operations when client-side rendering
useLayoutEffect(() => {
// This effect only runs on client
if (isMounted) {
// Safely perform DOM measurements and operations...
}
}, [isMounted]);
return <div>...</div>;
}🔥 Common Interview Questions
(1) What are the main differences between useEffect and useLayoutEffect?
Answer: The main differences lie in their execution timing:
useEffect
• Asynchronous execution
• Executes after screen update
• Doesn't block rendering
useLayoutEffect
• Synchronous execution
• Executes before screen update
• Blocks rendering
Both have identical APIs but differ in execution timing, suitable for different scenarios.
(2) Why should useEffect be preferred in most cases?
Answer: Reasons to prefer useEffect:
- 1. Better performance: doesn't block screen updates
- 2. Suitable for most scenarios: data fetching, subscription setup, etc. don't need to block UI
- 3. SSR friendly: more consistent behavior in server-side rendering environments
- 4. Prevents lag: time-consuming operations don't delay UI display
(3) When should useLayoutEffect be used?
Answer: Scenarios suitable for useLayoutEffect:
Prevent Flickering
Element position or style needs adjustment before user sees it
DOM Measurement & Positioning
Tooltips, popups need precise positioning
Animation Initialization
Set correct initial state to avoid jumping
Scroll Position Control
Adjust scroll position before page display
// Simple example to prevent flickering
function Tooltip({ text, targetRef }) {
const tooltipRef = useRef(null);
useLayoutEffect(() => {
// Measure target element position
const targetRect = targetRef.current.getBoundingClientRect();
// Position tooltip before screen update
tooltipRef.current.style.top = targetRect.bottom + 'px';
tooltipRef.current.style.left = targetRect.left + 'px';
}, []);
return <div ref={tooltipRef}>{text}</div>;
}(4) How to safely use useLayoutEffect in SSR environments?
Answer: Server has no DOM, cannot execute layout effects. Solutions:
- 1. Create isomorphic hook, choose different hook based on environment
- 2. Use client-side detection, ensure execution only in browser
// Isomorphic hook solution
const useIsomorphicLayoutEffect =
typeof window !== 'undefined'
? useLayoutEffect
: useEffect;
// Client-side detection solution
function SafeComponent() {
const [isBrowser, setIsBrowser] = useState(false);
useEffect(() => {
setIsBrowser(true);
}, []);
useLayoutEffect(() => {
if (isBrowser) {
// Safely perform DOM operations
}
}, [isBrowser]);
return <div>...</div>;
}Best practice: Separate code requiring layout measurements into pure client-side components.