Data Fetch and Display
Make a component that fetches data from a public API and displays it in a list or table format.
Solution
Data
The following data is sourced from Random User Generator.
Refresh the page to see the loading animation.
Loading...
Explanation
Before we start
Before starting, it's worth mentioning that this is not a barebones approach to creating a data fetching component. In this React component you will see loading messages, error handling, TypeScript
types, abstraction and custom hooks
.
The Solution Component
Focusing at the root level, we can see the custom hook, useFetchUsers()
and the structure of the HTML
. We pull in users
, isLoading
and error
to output the fetched content from the API.
Notice the pattern isLoading && <p>Loading...</p>
. This is called short-cicuit evaluation
and is a quick way to check that the value exists, and if it does, show the component. We did the same with the error
component.
Displaying the data
It's important to use semantic HTML
when structuring your code. This is the proper way to display a list.
Then we use the map
function to produce a list of the users. Notice that we've abstracted the li
element into a User
component. We supply a key and the user data.
The User Component
This is a simple li
component that uses optional chaining
(the ?) to only execute if the value exists.
fetchUserData function
This follows a standard pattern using the fetch
API. Note that we are using an async
function because it is asynchronous.
We check to see if there is an OK response header. Before converting the JSON
into readable JavaScript
and returning the data array.
useFetchUsers custom hook
To abstract the logic we've used a custom hook. We keep the state
of users
, isLoading
and error
.
useEffect
then runs when the component is loaded, and only once. This is set by leaving the dependency array
empty.
Since the fetch is asynchronous, we use an async
function and a trycatch
block.
Note the use of finally
to reset loading
to false regardless of outcome.
To finish our custom hook we return the useful variables.
Code
import { useState, useEffect } from "react";
// types
type userProps = {
gender: string;
name: {
title: string;
first: string;
last: string;
};
email: string;
};
type allUserProps = userProps[];
type APIResponse = {
results: userProps[];
};
// function
const Solution = () => {
const { users, isLoading, error } = useFetchUsers();
return (
<div>
<h2>Data</h2>
<p>
The following data is sourced from{" "}
<a href="https://randomuser.me">Random User Generator</a>.
</p>
<p>Refresh the page to see the loading animation.</p>
{isLoading && <p>Loading...</p>}
{error && <p>Error: {error}</p>}
<ul>
{users && users.map((user) => <User key={user.email} {...user} />)}
</ul>
</div>
);
};
export default Solution;
// display user
const User = (user: userProps) => {
return (
<li>
{user?.name?.title} {user?.name?.first} {user?.name?.last}
</li>
);
};
// fetch data
const fetchUserData = async (): Promise<allUserProps> => {
const response = await fetch("https://randomuser.me/api?results=5&nat=gb");
if (!response.ok) throw Error("Error fetching data");
const data: APIResponse = await response.json();
return data.results;
};
// custom hook
function useFetchUsers() {
const [users, setUsers] = useState<allUserProps>([]);
const [isLoading, setIsLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const fetchData = async () => {
try {
const results = await fetchUserData();
if (results.length > 0) {
setUsers(results);
setError(null);
} else {
throw Error("No results found.");
}
} catch (error) {
setError(
error instanceof Error ? error.message : "An unknown error occured"
);
} finally {
setIsLoading(false);
}
};
fetchData();
}, []);
return { users, isLoading, error };
}
Styling
// None