- Animation
- Bootstrap
- Button
- Components
- Conditional rendering
- Context API
- Debounce
- Error boundary
- Events
- Form
- Fragment
- Hooks
- Inline styling
- Key
- Lazy loading
- Lifecycle methods
- Portal
- Prop types
- Props
- Redux library
- Ref
- Router
- State
- State management
- Strict mode
- Suspense
- useCallback Hook
- useEffect hook
- useMemo hook
- useReducer hook
- useRef hook
- Virtual DOM
REACT
React useEffect Hook: Syntax, Usage, and Examples
The React useEffect hook lets you synchronize a component with external systems. It replaces lifecycle methods like componentDidMount
, componentDidUpdate
, and componentWillUnmount
from class-based components. In function components, you use useEffect
to perform side effects such as data fetching, subscriptions, DOM updates, and timers.
By integrating the React useEffect hook into your component logic, you can make your app dynamic, reactive to changes, and properly structured for cleanup.
How React useEffect Works
React calls the useEffect function after every render by default. You pass it a function that performs the effect and, optionally, a dependency array that determines when the effect runs again.
Basic syntax:
useEffect(() => {
// code to run after render
});
This runs after every render. To control when it runs, you can add a dependency array as a second argument.
Controlled execution:
useEffect(() => {
console.log("Runs only once");
}, []);
The empty array means it runs only once after the first render.
You Can Handle Side Effects
Side effects include operations that reach outside React’s rendering process, such as:
- API requests
- Setting timers
- Event listeners
- Manual DOM manipulation
- Logging
Example: Fetching data when the component mounts
useEffect(() => {
fetch("/api/user")
.then(res => res.json())
.then(data => setUser(data));
}, []);
This runs once when the component is added to the DOM.
You Should Clean Up with useEffect
To prevent memory leaks, you can return a cleanup function. This cleanup runs before the component unmounts or before re-running the effect.
useEffect(() => {
const intervalId = setInterval(() => {
console.log("Tick");
}, 1000);
return () => clearInterval(intervalId);
}, []);
This is especially important for subscriptions and timers.
Dependency Array Explained
The dependency array controls when the effect runs. React compares values in the array after every render. If any value changes, React re-runs the effect.
useEffect(() => {
console.log("Runs when count changes");
}, [count]);
An empty array runs the effect once. An array with dependencies runs the effect when those dependencies change. Omitting the array entirely causes the effect to run after every render.
Be precise with dependencies to avoid issues like the React useEffect unexpectedly called error, which often occurs due to state updates inside the effect.
Avoiding Infinite Loops in useEffect
An infinite loop can happen if you update state inside the effect without controlling the dependencies. For example:
useEffect(() => {
setCount(count + 1); // Dangerous
});
This updates the state, which triggers another render, which re-runs the effect, leading to a loop.
Always use a dependency array to limit how often the effect runs.
useEffect vs useState in React
React useEffect and useState serve different purposes but often work together. useState
holds and updates component state, while useEffect
reacts to those changes.
Example:
const [searchTerm, setSearchTerm] = useState("");
useEffect(() => {
fetchData(searchTerm);
}, [searchTerm]);
Here, useState
stores the input value, and useEffect
triggers an API call each time the term changes.
Understanding the difference between useEffect vs useState React logic improves control over component behavior.
You Can Use Async Functions in useEffect
The effect function itself cannot be marked async
, but you can define and call an inner async function.
useEffect(() => {
const fetchData = async () => {
const res = await fetch("/api/data");
const result = await res.json();
setData(result);
};
fetchData();
}, []);
This pattern enables API calls and other asynchronous logic without breaking the rules of hooks.
Common useEffect Issues and Fixes
useEffect unexpectedly called
This usually happens when dependencies are missing or incorrect. Always declare every variable or function used inside the effect in the dependency array.
useEffect(() => {
someCallback(); // Declare someCallback in dependencies
}, [someCallback]);
React strict mode can also call the effect twice on mount in development, which is expected and harmless.
useEffect called twice
In development, React runs useEffect twice to detect unsafe side effects. This doesn't happen in production, but you should avoid writing logic that causes problems if the effect runs more than once.
useEffect with an empty array
This is the equivalent of componentDidMount
, and it’s safe for running setup logic like initial API calls.
Handling useEffect in Component Unmount
Cleanup logic helps avoid memory leaks when a component is removed from the DOM.
Example:
useEffect(() => {
const handler = () => console.log("Window resized");
window.addEventListener("resize", handler);
return () => {
window.removeEventListener("resize", handler);
};
}, []);
This is essential for maintaining performance and avoiding bugs.
You Should Use Multiple useEffect Hooks
Each effect should do one thing. Using multiple useEffect hooks improves clarity and separation of concerns.
useEffect(() => {
fetchUser();
}, [userId]);
useEffect(() => {
logPageView();
}, []);
This keeps logic modular and easier to debug.
Using useEffect for Event Listeners
You can attach and detach custom events inside useEffect
.
useEffect(() => {
const handleScroll = () => console.log("Scrolled");
window.addEventListener("scroll", handleScroll);
return () => {
window.removeEventListener("scroll", handleScroll);
};
}, []);
This avoids duplicating listeners and keeps the UI efficient.
useEffect with React Context or Props
React useEffect also tracks changes in context or props. When a prop value changes, the effect re-runs.
useEffect(() => {
console.log("User role changed:", role);
}, [role]);
This keeps components responsive to external changes.
Best Practices for useEffect in React
- Keep effects small and focused
- Declare all dependencies
- Separate unrelated concerns into separate effects
- Avoid modifying state conditionally inside useEffect
- Use cleanup functions for subscriptions and timers
These practices lead to more maintainable and bug-free components.
The useEffect React hook provides a powerful way to synchronize function components with external systems. You can perform side effects like fetching data, subscribing to services, and cleaning up timers or event listeners.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.