The Fetch API is a modern JavaScript interface for fetching resources like data or files asynchronously from the web. The Fetch API is known for its asynchronous nature which means, the fetch requests does not in any way block the execution of other code while awaiting response from the Fetch API. It simplifies the process of fetching data, handling responses and plays an important role in allowing developers to build efficient and interactive web experiences.
Prerequisites
- Good knowledge of JavaScript and Fetch APIs.
- Intermediate experience in React.
Consuming the Fetch API
In React applications, any activity that involves external sourcing of data is handled using the useEffect hook. The useEffect hook houses the asynchronous function of the FetchAPI. In this article, we will make use of the Open Library Search API , a simple app to retrieve book data on Open Library. We would like to narrow our search to the authors and just like most Fetch APIs, we can attach a query parameter to the endpoint to fetch specific data.
function App() {
useEffect(
function () {
async function FetchBooks() {
const res = await fetch(
`https://openlibrary.org/search/authors.json?`
);
const data = await res.json();
}
FetchBooks();
},[dependency array]
)
return (
<>
Enter Author's Name:{" "}
<input
type="text"/>
<h2>Search Results</h2>
<ol>
</ol>
</>
);
}
This is the basic structure of our React App component:
- useEffect Hook: Responsible for handling side effects of data fetching and will cause the App component to render when the application mounts. It also houses a dependency array which is the state and it causes the component to re-render anytime there is a change.
- FetchBooks: Is our asynchronous function that awaits a response in json format from the Open Library Search API and takes in a query parameter, which will be determined by an input state.
- An input type that will take in the searchTerm state and dynamically add it to the fetch request.
- An ordered list that will be rendered to display results of our search query.
Handling States and Events
States are referred to as any data that will likely change in a React application. When declaring states, two parameters are specified - the state value and the state updater function, that sets the state. In our Book Search application’s case, we need two states which are:
- State that will take in the search term from the input, called the searchTerm.
- State that will store the search results, called the searchResults.
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
Next, is to attach an onChange event handler to the input to take in the event and pass it as a search query to the fetch API’s query parameter. The setSearchTerm handles the updating of every key pressed as a state value.
Adding the onChange event handler to the input
<input
type="text"
onChange={(e) => setSearchTerm(e.target.value)}
value={searchTerm}
/>
Appending the searchTerm state to the fetch API endpoint query parameter
const res = await fetch(
`https://openlibrary.org/search/authors.json?q=${searchTerm}`
Rendering the List
After a successful response has been gotten from the API and the data gotten, we want to display it in the ordered list returned by our JSX. We achieve this by mapping through the data present in the searchResults array state.
<ol>
{searchResults.map((books) => (
<li key={books.key}>
{books.name}
</li>
))}
</ol>
We now have all our essential pieces in place to be able make a Fetch request and display the response(data).
A look at the Browser Network Tab
When performing fetch requests, it is good practice to keep an eye on the network tab of your browser. A quick look at that shows a request for every query parameter entered. This means that for every searchTerm entered or individual key pressed, there is a request sent to the Fetch API - some may come with data and some without any data.
Food for thought
As a good front-end developer,
- Do you think that it is efficient and good work to allow so many requests to get sent to the API?
- Imagine a situation whereby the search term is lengthy and the user’s internet connection is very slow. In fact, you can mimic such a situation by choosing slow 3G from the Throttling dropdown options above - still in the Network tab section. You will notice how terrible of an experience that can be. However, there is a fascinating solution to this problem which is the Abort Controller!
The AbortController JavaScript Web API
This Web API represents a controller object that has the capability to abort one or more web requests. It cancels many web requests leading up to a ‘final’ web request that is allowed to fetch data from the API.
Firstly, we create a new AbortController object using the AbortController() constructor.
const controller = new AbortController();
Secondly, we make use of the signal property of the AbortController object which we call controller.
const { signal } = controller;
Thirdly, the fetch API allows us to attach options. So, we attach the signal as an option object to the fetch request.
const res = await fetch(
`https://openlibrary.org/search/authors.json?q=${searchTerm}`,
{ signal }
);
Finally, we return a CleanUp function which calls the abort() method of the controller.
return function () {
controller.abort();
};
The clean up function should be returned immediately after calling the asynchronous function.
AbortController Error Handling
The AbortController generates an AbortError message on the Console anytime an operation is being aborted. This allows you to know when an operation is aborted and is different from other forms of errors you might encounter while performing asynchronous operations.
Final full code piece
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [searchTerm, setSearchTerm] = useState("");
const [searchResults, setSearchResults] = useState([]);
useEffect(
function () {
const controller = new AbortController();
const { signal } = controller;
async function FetchBooks() {
const res = await fetch(
`https://openlibrary.org/search/authors.json?q=${searchTerm}`,
{ signal }
);
const data = await res.json();
setSearchResults(data.docs);
}
FetchBooks();
return function () {
controller.abort();
};
},
[searchTerm]
);
return (
<>
Enter Author's Name:{" "}
<input
type="text"
onChange={(e) => setSearchTerm(e.target.value)}
value={searchTerm}
/>
<h2>Search Results</h2>
<ol className="olStyle">
{searchResults.map((books) => (
<li style={{ padding: "3px" }} key={books.key}>
{books.name}
</li>
))}
</ol>
</>
);
}
Final Results
Now, the abortController cancels all fetch requests leading up to the last. The act of canceling these requests has prevented the network from executing numerous fetch operations, thereby enhancing the overall efficiency of the fetch process.
NOTE: A successful fetch request largely depends on the speed of keystroke as the abortController will permit the sending of query parameter requests if the keys are being pressed a bit slower.
The idea behind the abortController is pretty cool and is highly recommended to build efficient applications.
Connect with me on LinkedIn for more useful front-end related posts.
Top comments (0)