
When you're working with React and functional components, one of the most useful hooks you'll come across is useEffect
. It allows you to perform side effects in your components — like fetching data, setting up listeners, or interacting with the DOM.
One specific usage pattern of useEffect
that confuses many developers early on is this:
useEffect(() => {
// some code
}, []);
This is useEffect
with an empty dependency array. In this article, we’ll look at what it means, why it’s used, and when to apply it in real-world situations.
What Is useEffect
?
React's useEffect
runs after the component renders. It’s similar to lifecycle methods like componentDidMount
, componentDidUpdate
, and componentWillUnmount
in class components, but for function components.
The basic syntax looks like this:
useEffect(() => {
// your side effect logic here
}, [/* dependencies */]);
The second argument — the dependency array — tells React when to run the effect.
Why Use an Empty Dependency Array?
When you pass an empty array ([]
) as the second argument to useEffect
, you're telling React:
“Execute this code just one time — after the component mounts and is placed into the DOM.”
This is useful for actions that should only happen once, such as:
- Fetching initial data from an API
- Setting up a subscription or event listener
- Starting a timer or interval
Here’s an example:
useEffect(() => {
fetchData();
}, []);
In this case, fetchData()
will run only once — right after the component is mounted — and won’t run again on re-renders.
What Happens If You Skip the Dependency Array?
If you write useEffect
like this:
useEffect(() => {
fetchData();
});
Then fetchData()
will run after every render.
This might lead to problems such as:
- Repeated API calls
- Infinite loops (especially if you update state inside
useEffect
) - Performance issues
So, always use the dependency array based on your needs. If you want the code to run only once, include the empty array.
Real-World Example
Let’s say you're building a to-do app and want to load the initial list from the server when the component mounts:
import { useEffect, useState } from 'react';
function TodoList() {
const [todos, setTodos] = useState([]);
useEffect(() => {
async function fetchTodos() {
const res = await fetch('/api/todos');
const data = await res.json();
setTodos(data);
}
fetchTodos();
}, []);
return (
<ul>
{todos.map(todo => <li key={todo.id}>{todo.title}</li>)}
</ul>
);
}
Here, the API is called only once — when the component loads — and the data is saved to state.
Cleaning Up Inside useEffect([])
Sometimes, you need to clean up something when the component is removed — for example, removing an event listener. You can return a function inside useEffect
for this purpose.
useEffect(() => {
const handleScroll = () => {
console.log('User scrolled');
};
window.addEventListener('scroll', handleScroll);
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, []);
This cleanup function will run when the component unmounts.
Summary
Here’s what to remember about useEffect([])
:
- Use it when you want your side effect to run only once, just after the first render.
- It’s similar to
componentDidMount()
in class components. - Helpful for API calls, event listeners, or timers that only need to run once.
- Don’t forget the array — otherwise, your effect will run on every render.
- Be cautious when updating state inside
useEffect
, especially if the dependencies are not handled properly.
Conclusion
Understanding how useEffect([])
works is a key part of working with React. It’s a simple concept, but very powerful in real-world applications. Once you get comfortable with this pattern, you'll be able to handle initial data loading, event setup, and many other side effects in a clean and predictable way.
If you're just starting with React hooks, mastering this one pattern will give you a strong foundation.
Comments
Post a Comment