Effects run after render when you need to synchronize with something outside React: timers, subscriptions, manual DOM APIs, or simulated fetches. useEffect is the primary hook for this.
Basic shape
const { useEffect, useState } = React;
useEffect(() => {
// side effect
return () => {
// optional cleanup
};
}, [dependencies]);
When effects run
- After paint, asynchronously—does not block the browser from showing UI
- Re-runs when dependencies in the array change
- Empty array
[]— run once after mount (plus cleanup on unmount) - Omitting the array — runs after every render (rare; easy to cause loops)
Important interview questions and answers
- Q: useEffect vs event handler?
A: Handlers respond to user actions; effects respond to render/state/prop changes and external sync. - Q: Can you set state in useEffect?
A: Yes, but guard against infinite loops—only set state when something actually changed.
Self-check
- What does an empty dependency array mean?
- When should you return a cleanup function?
Challenge
Document title
- Type in the input and watch
document.titleupdate (check the browser tab in the iframe). - Confirm cleanup resets the title on unmount by clearing the preview.
Done when: document.title matches the input while the component is mounted.
Pitfall: An effect with no dependency array runs after every render—almost always a bug for fetch/subscribe logic.
Interview prep
- When should you use useEffect?
To synchronize with external systems—fetch, subscriptions, timers—not to derive display data from props. The dependency array controls re-runs; cleanup runs before re-run and unmount.