React useEffect Interview Questions
One of the most frequently asked React interview questions is:
What is useEffect and why do we need it?
Most developers answer:
useEffect is used for API calls.While API calls are a common use case, interviewers often go deeper:
- What is a side effect?
- When does useEffect run?
- How does the dependency array work?
- What are cleanup functions?
- Can useEffect cause infinite loops?
- What is the difference between useEffect and useLayoutEffect?
Let's understand useEffect from first principles.
1. Why Do We Need useEffect?
React components should primarily focus on:
Rendering UIExample:
function UserCard() {
return <h1>John</h1>;
}However, applications often need to perform operations such as:
- API requests
- Event listeners
- Timers
- Analytics tracking
- WebSocket connections
- DOM interactions
These operations are called:
Side Effects2. What is a Side Effect?
A side effect is any operation that interacts with something outside React's rendering process.
Examples:
fetch("/users");setInterval(...);window.addEventListener(...);localStorage.setItem(...);All of these are side effects.
3. Understanding the useEffect Hook
useEffect is a React Hook that allows components to perform side effects after rendering.
Example:
useEffect(() => {
console.log("Effect");
});React executes the effect after rendering is complete.
Visual Representation
Render
↓
Paint
↓
useEffect RunsThis sequence is important for interviews.
Basic Example
function App() {
useEffect(() => {
console.log("Mounted");
});
return <h1>Hello</h1>;
}Output:
Mountedafter rendering occurs.
How useEffect Works
Syntax:
useEffect(() => {
// Side Effect
});Structure:
useEffect(effectFunction, dependencies);4. Controlling Execution: The Dependency Array
The dependency array controls when the effect runs.
Example:
useEffect(() => {
console.log("Effect");
}, []);Understanding dependency arrays is one of the most important React interview topics.
Case 1: No Dependency Array
Example:
useEffect(() => {
console.log("Effect");
});Runs:
Every RenderExample
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Effect");
});
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}Every click:
Render
↓
Effect RunsCase 2: Empty Dependency Array
Example:
useEffect(() => {
console.log("Mounted");
}, []);Runs:
Only Once
After Initial MountCommon Use Cases
Perfect for:
- Initial API requests
- Analytics initialization
- Event listener setup
- WebSocket connections
API Call Example
useEffect(() => {
fetchUsers();
}, []);Runs only once when the component mounts.
Case 3: Specific Dependencies
Example:
useEffect(() => {
console.log("Count Changed");
}, [count]);Runs whenever:
count;changes.
Visual Representation
count Changes
↓
Re-render
↓
Effect RunsMultiple Dependencies
Example:
useEffect(() => {
console.log("Updated");
}, [count, user]);Runs when:
count;or
user;changes.
5. Resource Management: Cleanup Functions
One of the most important useEffect concepts.
Example:
useEffect(() => {
console.log("Effect");
return () => {
console.log("Cleanup");
};
}, []);What is a Cleanup Function?
A cleanup function allows React to remove resources created by an effect.
Common examples:
- Timers
- Event listeners
- Subscriptions
- WebSocket connections
Timer Example
Bad:
useEffect(() => {
setInterval(() => {
console.log("Running");
}, 1000);
}, []);Problem:
Interval Never StopsCorrect
useEffect(() => {
const id = setInterval(() => {
console.log("Running");
}, 1000);
return () => {
clearInterval(id);
};
}, []);Event Listener Example
Bad:
useEffect(() => {
window.addEventListener("resize", handleResize);
}, []);Problem
Listener Remains
After Component RemovalCorrect
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);When Does Cleanup Run?
Interviewers frequently ask this.
Cleanup runs:
Before Unmount
Component RemovedBefore The Next Effect
Example:
useEffect(() => {
console.log("Effect");
return () => {
console.log("Cleanup");
};
}, [count]);When:
count;changes:
Output:
Cleanup
Effect6. Common Pitfall: Infinite Loops
A common interview question.
Bad:
const [count, setCount] = useState(0);
useEffect(() => {
setCount(count + 1);
});Result:
Render
↓
Effect
↓
State Update
↓
Render
↓
EffectInfinite loop.
Why Does This Happen?
Because:
State Update
↓
Re-render
↓
Effect Runs Againrepeats forever.
Fix
Example:
useEffect(() => {
setCount(1);
}, []);Runs only once.
7. Common Pitfall: Stale Closures & Missing Dependencies
Example:
useEffect(() => {
console.log(user);
}, []);Problem:
user;is used but not included.
Potential issue:
Stale DataCorrect
useEffect(() => {
console.log(user);
}, [user]);Stale Closure Example
A common senior-level topic.
Example:
useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(id);
}, []);Problem:
count Never Updatesinside the interval.
Why?
The effect captures:
count;from the initial render.
This is called:
Stale Closure8. Production Pattern: Fetch Requests & AbortController
useEffect(() => {
fetch("/users")
.then((res) => res.json())
.then(setUsers);
}, []);Very common production usage.
AbortController Pattern
Better:
useEffect(() => {
const controller = new AbortController();
fetch("/users", {
signal: controller.signal,
});
return () => {
controller.abort();
};
}, []);Prevents unnecessary requests after unmounting.
9. useEffect vs. useLayoutEffect
A common senior interview topic.
useEffect
Runs:
After PaintuseLayoutEffect
Runs:
Before Paintand blocks rendering.
Visual Representation
useEffect
Render
↓
Paint
↓
EffectuseLayoutEffect
Render
↓
Layout Effect
↓
Paint10. React 18 Strict Mode Effect Behavior
In development:
<React.StrictMode>may execute effects twice.
Example:
useEffect(() => {
console.log("Mounted");
}, []);Development:
Mounted
MountedProduction:
Mounted11. Common Interview Q&A
What is useEffect?
A hook for performing side effects after rendering.
What Are Side Effects?
Operations that interact with systems outside React rendering.
Examples:
- API calls
- Timers
- Event listeners
When Does useEffect Run?
After React completes rendering and painting.
What Does an Empty Dependency Array Mean?
Run Once After MountWhat Happens Without a Dependency Array?
Runs After Every RenderWhen Does Cleanup Run?
- Before unmount
- Before the next effect executes
What Causes Infinite Loops in useEffect?
State updates that continuously trigger re-renders.
What Is a Stale Closure?
An effect capturing outdated values from previous renders.
Dependency Array Summary
| Dependency Array | Behavior |
|---|---|
| No Array | Every Render |
| [] | Initial Mount Only |
| [count] | When count Changes |
| [count, user] | When Any Dependency Changes |
Senior-Level Interview Answer
useEffect is a React Hook used for performing side effects after rendering. Common side effects include API requests, timers, subscriptions, event listeners, and browser storage interactions. The dependency array determines when the effect executes. An empty dependency array causes the effect to run only after the initial mount, while specified dependencies cause re-execution when those values change. Effects can also return cleanup functions that run before unmounting or before subsequent effect executions, helping prevent memory leaks and stale subscriptions. Understanding dependency management, cleanup behavior, and stale closures is essential for building reliable React applications.
Common Interview Mistakes
❌ useEffect Is Only For API Calls
Correct:
useEffect handles all kinds
of side effects.❌ Cleanup Runs Only On Unmount
Correct:
Cleanup also runs before
the next effect executes.❌ Empty Dependency Arrays Are Always Correct
Correct:
Dependencies should reflect
values used inside the effect.❌ useEffect Runs Before Render
Correct:
Render Happens First.
Effect Runs After Paint.Key Takeaways
- Side Effects: Operations that interact with the outside world (APIs, timers, storage) outside the render pipeline.
- Rendering Sequence: Effects run asynchronously after the render is committed and painted to the screen.
- Dependency Array: Controls execution; empty
[]runs once, populated[deps]runs on changes, omitted runs on every render. - Cleanup Return: Executes before the component unmounts and before the next effect run, preventing memory leaks.
- Stale Closures: Occur when dependencies are missing, capturing variables from older render scopes.