React — useEffect explained

NISHANK KUMAR
4 min readMay 19, 2023

useEffect is a built-in hook in React that allows you to perform side effects in functional components. Side effects typically include things like fetching data from an API, subscribing to events, manipulating the DOM, or setting up timers.

The useEffect hook is called after the component has rendered and provides a way to run code that needs to be executed in response to changes in dependencies. It accepts two arguments: a callback function and an optional dependency array.

Here’s the basic syntax of useEffect:

useEffect(() => {
// Side effect code goes here
}, [dependency1, dependency2, ...]);

The callback function passed to useEffect is executed after the component renders and re-renders. It can contain any code that performs side effects. This function can return a cleanup function to handle any necessary cleanup tasks when the component is unmounted or before it re-renders.

The dependency array is an optional second argument to useEffect. It is used to specify dependencies that the effect depends on. React will compare the current values of the dependencies with the previous render, and if any of the dependencies have changed, the effect function will be re-executed. If the dependency array is omitted, the effect function will be executed after every render.

Here’s an example that demonstrates the usage of useEffect:

import React, { useEffect, useState } from 'react';

function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
// Fetch data from an API
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
// Update the state with the fetched data
setData(data);
});
// Cleanup function (optional)
return () => {
// Perform cleanup tasks if needed
};
}, []);
return (
<div>
{data ? (
<p>Data: {data}</p>
) : (
<p>Loading...</p>
)}
</div>
);
}
export default MyComponent;

In the example above, we have a MyComponent functional component that uses the useEffect hook. It initializes a data state variable using the useState hook.

Inside the useEffect callback, we fetch data from an API using the fetch function. When the data is fetched successfully, we update the state by calling the setData function.

The optional cleanup function is returned from the useEffect callback. It can be used to perform any necessary cleanup tasks, such as canceling subscriptions or clearing timers, before the component is unmounted or re-rendered.

The dependency array in this example is an empty array [], indicating that the effect should only run once, after the initial render. If you want the effect to be dependent on a specific value or prop, you can include it in the dependency array. Whenever that value changes, the effect will be re-executed.

Note that when using the dependency array, make sure to include all the variables and props that are used inside the effect. Omitting dependencies can lead to bugs or stale data.

Overall, useEffect is a powerful hook that allows you to handle side effects and manage the lifecycle of functional components in a declarative and concise manner.

Example of Case of bugs/issues when we have missed using dependency array variables/props

When using the useEffect hook, it's important to include all variables and props that are used inside the effect in the dependency array. Omitting dependencies can indeed lead to bugs or stale data.

Let’s consider an example to understand this better:

import React, { useEffect, useState } from 'react';

function MyComponent({ id }: { id: number }) {
const [data, setData] = useState(null);
useEffect(() => {
// Fetch data based on the provided ID
fetch(`https://api.example.com/data/${id}`)
.then(response => response.json())
.then(data => {
// Update the state with the fetched data
setData(data);
});
}, []);
return (
<div>
{data ? (
<p>Data: {data}</p>
) : (
<p>Loading...</p>
)}
</div>
);
}
export default MyComponent;

In the above example, the MyComponent functional component takes an id prop. Inside the useEffect hook, we use this id prop to fetch data from an API.

However, we have mistakenly omitted the id prop from the dependency array of the useEffect hook ([]). This means that the effect is not re-executed when the id prop changes.

This omission can lead to bugs or stale data because the effect is not being updated when the id prop changes. The effect will only run once, during the initial render, with the initial value of the id prop. If the id prop changes later on, the effect won't run again, and the component will keep displaying the data corresponding to the initial id.

To fix this issue and ensure that the effect is triggered whenever the id prop changes, we need to include it in the dependency array:

useEffect(() => {
// Fetch data based on the provided ID
fetch(`https://api.example.com/data/${id}`)
.then(response => response.json())
.then(data => {
// Update the state with the fetched data
setData(data);
});
}, [id]);

By including id in the dependency array, the effect will be re-executed whenever the id prop changes. This ensures that the component stays up to date with the latest data based on the new id value.

So, by omitting dependencies in the dependency array, you may encounter bugs or end up with stale data because the effect doesn’t have the correct information to respond to changes in those variables or props. Including all the necessary dependencies in the dependency array helps maintain the consistency and accuracy of your component’s behavior.

Thanks.

--

--