Optimizing UseEffect Hook for API Calls: Reduce Server Load and Rerenders with AbortController
Table of Contents
Introduction
Problem with Example Code
Solution with Example Code
Benefits
Conclusion
Introduction
The useEffect
hook is one of the most powerful hooks in React. It allows us to perform side effects in functional components, such as data fetching, DOM manipulation, and subscriptions. However, improper use of useEffect
can lead to performance issues, such as unnecessary re-renders and redundant API calls.
In this article, we will discuss how to optimize the useEffect
hook while making API calls in it. We will also provide a full code example to demonstrate the solution.
Problem with Example Code
Consider the following simple React component:
import React, { useState, useEffect } from "react";
const ExampleComponent = () => {
const [id, setId] = useState(0);
const [text, setText] = useState("");
const handleClick = () => {
setId(Math.floor(Math.random() * 100));
};
useEffect(() => {
async function fetchData() {
const response = await fetch(`/api/posts/${id}`);
const data = await response.json();
setText(data.text);
}
fetchData();
}, [id]);
return (
<div>
<button onClick={handleClick}>Show New Post</button>
<p>{text}</p>
</div>
);
};
This component has two state variables: id
and text
. The handleClick
function updates the id
state to a random value between 0 and 100. The useEffect
hook is used to fetch a new post from the API whenever the id
state changes.
The problem with this code is that it can trigger unnecessary API calls. For example, if the user clicks the "Show New Post" button five times in quick succession, the useEffect
hook will be triggered five times, resulting in five API calls. This can be inefficient, especially if the API calls are expensive.and also a bad User experience, as the text visibly updates 5 times before it reaches its final State.
Solution with Example Code:
To optimize the useEffect
hook for API calls, we can use a technique called request cancellation. Request cancellation allows us to abort a fetch request before it completes.
To implement request cancellation, we can use the AbortController
interface. The AbortController
interface allows us to create a controller object that can be used to abort one or more fetch requests.
Here is an example of how to use the AbortController
interface to optimize the useEffect
hook for API calls:
import React, { useState, useEffect } from "react";
const ExampleComponent = () => {
const [id, setId] = useState(0);
const [text, setText] = useState("");
const controller = new AbortController();
const handleClick = () => {
setId(Math.floor(Math.random() * 100));
};
useEffect(() => {
async function fetchData() {
const response = await fetch(`/api/posts/${id}`, {
signal: controller.signal,
});
const data = await response.json();
setText(data.text);
}
fetchData();
return () => {
controller.abort();
};
}, [id]);
return (
<div>
<button onClick={handleClick}>Show New Post</button>
<p>{text}</p>
</div>
);
};
The key difference between this code and the previous example is that we are passing the controller.signal
object to the fetch
call. This tells fetch
to abort the request if the controller
is aborted.
Also, we have added a cleanup function to the useEffect
hook. this function runs every time the component is unmounted, but in this case we aren't unmounting the component on state change, however this cleanup function also runs before you go for another round of that useEffect
. So, every time the dependency changes and a new useEffect
hook is run, this cleanup function is first run for the previous useEffect
hook.
In this cleanup function, we can simply return controller.abort()
. The abort()
method allows us to cancel the fetch call when needed. So, if the user clicks the button multiple times in quick succession, the abort()
method will cancel the fetch calls of all the previous useEffect
hooks, one by one, except for the useEffect
hook triggered by the last button click. This ensures that all pending fetch requests are cancelled.
Benefits:
This example code has several benefits:
It prevents unnecessary API calls. If the user clicks the "Show New Post" button multiple times in quick succession, only the last API call will be made.
It improves performance by reducing the number of re-renders.
It reduces the load on the server by preventing redundant API calls.
Conclusion:
By following the best practices outlined in this blog post, you can optimize the useEffect hook for API calls to improve the performance and scalability of your React applications.
Let's connect: