Skip to content

React useEffect Interview Guide

useEffect is a React Hook used to perform side effects in functional components.

Common use cases:

  • API calls
  • Event listeners
  • Timers
  • DOM manipulation
  • Subscriptions
useEffect(() => {
console.log("Component rendered");
});

It runs after the component renders.


useEffect(() => {
// Side Effect
return () => {
// Cleanup
};
}, [dependencies]);
  • First argument -> effect function
  • Return function -> cleanup
  • Second argument -> dependency array

useEffect(() => {
console.log("Executed");
});

Runs:

  • After the first render
  • After every re-render

useEffect(() => {
getUsers();
}, []);

An empty dependency array means the effect runs only after the initial render, similar to componentDidMount().


useEffect(() => {
console.log("Count changed");
}, [count]);

Runs:

  • On initial render
  • Whenever count changes
useEffect(() => {
fetchData();
}, []);
useEffect(() => {
console.log("Updated");
}, [count]);
useEffect(() => {
return () => {
console.log("Cleanup");
};
}, []);

Can one useEffect replace all lifecycle methods?

Section titled “Can one useEffect replace all lifecycle methods?”

Yes. Multiple effects are recommended.

useEffect(() => {
fetchUsers();
}, []);
useEffect(() => {
updateTitle();
}, [count]);
useEffect(() => {
return () => {
cleanup();
};
}, []);
useEffect(() => {
console.log("Effect");
});

Runs after every render.

useEffect(() => {
console.log("Once");
}, []);

Runs only once after mounting.

useEffect(() => {
console.log("Changed");
}, [name, age]);

Runs whenever name or age changes.

Incorrect:

useEffect(() => {
console.log(count);
}, []);

Correct:

useEffect(() => {
console.log(count);
}, [count]);

Including dependencies avoids stale values.

useEffect(() => {
return () => {
console.log("Cleanup");
};
}, []);

Cleanup prevents:

  • Memory leaks
  • Duplicate subscriptions
  • Unnecessary resource usage
useEffect(() => {
const handleResize = () => {};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
useEffect(() => {
const id = setInterval(() => {
console.log("Running");
}, 1000);
return () => clearInterval(id);
}, []);
useEffect(() => {
fetch("/users")
.then(res => res.json())
.then(data => setUsers(data));
}, []);

API calls are side effects and belong inside useEffect.

Incorrect:

useEffect(async () => {
}, []);

Correct:

useEffect(() => {
const loadData = async () => {
const res = await fetch("/users");
};
loadData();
}, []);
useEffect(() => {
setCount(count + 1);
}, [count]);
flowchart TD
A[Count changes] --> B[useEffect executes]
B --> C["setCount()"]
C --> D[Component re-renders]
D --> A

Avoid this by using an appropriate dependency array and preventing unnecessary state updates.

Problem:

useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
}, []);

Solution:

useEffect(() => {
const id = setInterval(() => {
setCount(prev => prev + 1);
}, 1000);
return () => clearInterval(id);
}, []);
useEffect useLayoutEffect
Runs after paint Runs before paint
Non-blocking Blocking
Best for API calls Best for DOM measurements
Better performance Use sparingly

Example:

useLayoutEffect(() => {
console.log(element.offsetWidth);
}, []);

Avoid unrelated logic in one effect.

Less maintainable:

useEffect(() => {
fetchUsers();
updateTitle();
setupListener();
}, []);

Better:

useEffect(fetchUsers, []);
useEffect(updateTitle, [count]);
useEffect(setupListener, []);

React compares dependency values using Object.is().

Primitives:

count === count

Objects and arrays:

{} !== {}
[] !== []

New object references cause effects to run.

const user = useMemo(() => ({
name: "John"
}), []);
useEffect(() => {
}, [user]);
function App() {
console.log("Render");
useEffect(() => {
console.log("Effect");
}, []);
return <div>Hello</div>;
}

Output:

Render
Effect
sequenceDiagram
participant R as React
participant C as Component
R->>C: Render component
C-->>R: Return JSX
R->>R: Paint UI
R->>C: Execute useEffect

useEffect is a React Hook used to perform side effects after rendering. It can replicate componentDidMount, componentDidUpdate, and componentWillUnmount using dependency arrays and cleanup functions.

  • useEffect handles side effects after rendering.
  • Dependency arrays determine when an effect executes.
  • Cleanup functions prevent memory leaks and remove subscriptions.
  • Avoid infinite loops by managing state updates and dependencies carefully.
  • Prefer multiple focused effects over one large effect.