React Hooks: useEffect

Tay Bencardino
4 min readSep 19, 2023

--

Photo by Lautaro Andreani on Unsplash

useEffect is a hook in the React JavaScript library that allows you to perform side effects in functional components. React components are typically used to render UI elements, but sometimes, you need to interact with the browser's DOM, make network requests, or perform other tasks that are not directly related to rendering.

How it works

You can use useEffect within a functional component to specify code that should run after the component has been rendered. It takes two arguments:

  • A function that contains the code you want to run as its first parameter.
  • An optional array (dependency array) as its second parameter, which allows you to control when the effect runs. If you omit the second parameter, the effect runs after every render. If you provide an empty array ([]) as the second parameter, the effect only runs once. If you provide values in the array, the effect will run whenever any of those values change.

1. If you omit the second parameter:

When you omit the second parameter (dependency array) in useEffect, the effect will run after every render of the component. Here's an example:

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

const Counter = () => {
const [count, setCount] = useState(0);

// useEffect without a dependency array
useEffect(() => {
// This code will run after every render of the component
console.log(`Count: ${count}`);
});

const increment = () => {
setCount(count + 1);
};

return (
<div>
<h1>Counter</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}

export default Counter;

When you click the “Increment” button, it updates the count state, and the useEffect code logs the current count to the console after every render. This is because there are no dependencies specified, so the effect runs whenever the component renders, including the initial render and subsequent renders caused by state changes.

👎 Thumbs down: it can lead to unintended consequences and performance issues in your React application, decreasing the application performance and responsiveness, triggering infinite loops, redundant network requests (wasting bandwidth and resources) and a less clear code, leading to potential bugs, confusion and hard to mantain.

2. If you provide an empty array:

When you provide an empty array ([]) as the second parameter of useEffect, it indicates that the effect should run only once. Here’s an example:

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

const DisplayFetchData = () => {
const [data, setData] = useState([]);

// useEffect with an empty dependency array
useEffect(() => {
// This code will run once when the component renders
// Simulate fetching data after a delay
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
};

fetchData();
}, []); // An empty dependency array means this effect runs only once

return (
<ul>
{data.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
}

export default DisplayFetchData;

When we render the DisplayFetchData component, the useEffect code runs and simulates fetching data from an API. The code inside the useEffect runs only once when the component is initially mounted. It doesn't run again when the component re-renders, making it suitable for tasks like fetching initial data, setting up subscriptions, or initializing resources that should not be repeated with each re-render of the component.

👍 Thumbs up: Using an empty dependency array ([]) with useEffect to run code only once when the component mounts is generally a recommended and safe practice! It helps improve performance and ensures that setup code isn’t repeated unnecessarily.

3. If you provide values in the array:

When you provide values in the dependency array of useEffect, it means that the effect will run whenever any of those values change. This allows you to react to specific changes in state or props. Here's an example:

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

const Counter = () => {
const [count, setCount] = useState(0);

// useEffect with a dependency array containing 'count'
useEffect(() => {
// This code will run whenever 'count' changes
console.log(`Count has changed: ${count}`);
}, [count]); // 'count' is specified as a dependency


const increment = () => {
setCount(count + 1);
};

return (
<div>
<h1>Counter</h1>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}

export default Counter;

When the Counter component initially renders, the effect runs once because count has changed from its initial value (0). Then we click the “Increment” button, and it will update the count state, triggering a re-render of the component. After the re-render, the useEffect code runs again because the count state has changed, and it logs the updated count to the console.

By specifying count as a dependency, we ensure that the effect reacts to changes in that specific piece of state. This is useful when you want to perform actions or side effects based on changes to certain variables within your component.

👍👍 Thumbs up-up: By specifying dependencies, you have precise control over when the effect should run. It ensures that the code within the effect runs only when the specified dependencies change. This helps prevent unnecessary re-renders and keeps your component efficient. Also, it optimises your app's performance, makes your code more readable, avoids infinite loops, maintains the app's stability and makes it easier to debug.

In the end useEffect is a powerful tool in React for handling various side effects and is essential for managing the lifecycle of functional components. Overall, specifying dependencies in the dependency array of useEffect is a best practice for React development. It allows you to manage side effects controlled and efficiently, leading to better performance and maintainability in your applications. It helps keep your components clean and declarative while allowing you to work with imperative code when necessary.

Happy coding! 🚀

Resources:

  • Book React 18 Design Patterns and Best Practices — Fourth Edition
  • React Official Documentation — react.dev

--

--