Fetching 101: Basics of Data Fetching in React
This chapter dives into the essentials of data fetching in React applications, starting from a simple user profile page. It highlights the initial steps of making API calls, dealing with network delays, and managing state with React's useEffect hook.
In this chapter, you will learn
Starting Simple in React Data Fetching
Imagine a simple React application: a profile page where a logged-in user can view their profile. To achieve this, we need to fetch the user's information from an API using their ID.
The API endpoint we'll use returns basic user information. It's designed to include a delay, allowing us to later examine how slow API responses can impact frontend performance.
Consider this API call:
And the expected response:
In a typical React setup, we would handle this data fetching within a useEffect
call. React triggers this effect after completing the initial render.
Here's how you might implement this in src/profile.tsx
:
The get
function is a straightforward wrapper around the native fetch
. You can replace it with axios.get
or any other preferred method.
Here's the utility function in utils.ts
:
To render the Profile
component, update App.tsx
as follows:
Visualizing the rendering sequence over time, it would look something like this diagram:
When a user accesses a React application, a typical flow begins with the browser downloading the initial HTML. As it parses the HTML, it encounters links to resources like JS and CSS. This process involves downloading, parsing, and executing JS bundles, building the CSSOM, and so on.
Note: HTML parsing is usually done in a streaming manner, meaning it starts as soon as some bytes are received rather than waiting for the entire HTML to download. For simplicity, our illustration assumes the entire HTML is downloaded before DOM construction. More details can be found in Rendering on the Web, Client-side rendering of HTML and interactivity, and Populating the page: how browsers work.
As JavaScript executes, React begins rendering and manipulating the DOM, then triggers a network request through useEffect
. It waits until data returns from the server before re-rendering with the new data.
To enhance user experience, we can introduce a Spinner
component during data loading and an Error
component for handling issues like unresponsive backends or nonexistent users.
With these additions, the Profile
component now includes additional states:
This structure should be familiar if you've worked with React before.
Next, let's add more than just the username. We'll create an About
component to display user information, adding simple styles for visual appeal.
For brevity, I'm omitting Tailwind CSS from the code snippets. You can view the full styled components in the corresponding code repository.
When data is correctly fetched, it renders as shown:
We now have a basic Profile page, retrieving data from a backend API intentionally delayed by 1.5 seconds.
Implementing the User's Friends List
Consider the user's friends list, typically stored in a separate table and accessed via a different API endpoint. For example, /users/<id>/friends
. We'll fetch this data in a new component, Friends
.
The structure of the Friends
component mirrors that of Profile
: managing state, fetching data in useEffect
, and rendering based on loading, error, and successful data retrieval states.
We can incorporate Friends
into the Profile
component like any regular React component:
At first glance, this implementation seems fine. However, if you consider the time taken for each API call, you might spot a potential issue. What if the /friends
API also takes 1.5 seconds to respond? The total time to display the full page would be 3 seconds.
You have Completed Chapter 2
This chapter lays the groundwork for mastering network requests in React. It provides a practical example of fetching and rendering data, setting the stage for more advanced patterns and performance considerations in subsequent chapters.
The next chapter will further explore complex data fetching scenarios, addressing performance challenges in frontend applications.