Fetching Resources in Parallel
Chapter 3 tackles the challenge of optimizing network requests in React applications. It focuses on implementing parallel data fetching strategies to minimize the impact of the network waterfall effect, enhancing application performance and user experience.
In this chapter, you will learn
The first issue we encounter is with the rendering order. Initially, in the Profile
component, useEffect
triggers a network request. However, since data takes 1.5 seconds to return, we display a loading indicator in the meantime.
Once the data arrives, we render the About
section, and a similar process occurs in the Friends
component. Here, useEffect
initiates another network request, waiting for the data to return.
Visualizing the request timeline, it looks like this:
The process involves three renderings. After the first render, the page displays a loading...
message while initiating the /users/u1
request. When the server responds, the About
section is displayed. As Friends
renders, lacking available data, it shows a loading...
message in its section and sends out the /users/u1/friends
request. Upon receiving this data, the third rendering occurs.
This sequence might be obvious in our current setup, but consider more complex scenarios. Imagine the Friends
component nested deeper in the component tree or used in different pages or sections. In such cases, identifying the problem by statically reading the code becomes challenging.
The situation worsens with more nested components following the same useEffect
+ loading
+ error
pattern, potentially leading to cumulative performance issues:
Over time, as the component tree grows, the page becomes increasingly slower.
However, one might wonder if initiating data fetching simultaneously could mitigate this wait time.
Sending Requests in Parallel
We can address this issue by sending parallel requests. In the Profile
component, we can start both requests simultaneously using Promise.all
, passing the fetched friends
list to the Friends
component:
The Promise.all()
static method accepts an iterable of promises, returning a single Promise. This promise resolves when all input promises fulfill (including for an empty iterable), resulting in an array of fulfillment values. If any input promises reject, the returned promise rejects with the first rejection reason.
Consequently, we modify Friends
into a presentational component, responding only to the passed users
list, rather than making its own requests:
Now, the total wait time is reduced to max(1.5, 1.5) = 1.5
seconds, a significant improvement:
The only remaining issue is the potential wait for the slower request in extreme cases. We'll accept this limitation for now and explore solutions in subsequent chapters.
Request Dependency
Parallel requests expedite the loading of independent data. However, some requests depend on others. For example, we might need to fetch user information first and use the interests
array from the response to retrieve recommended feeds for the user. This sequential dependency necessitates a return to the initial approach.
In the Feeds
component, we define loading, error, and data states, and useEffect
initiates network fetching after the initial render:
In the Profile
component, we include Feeds
as follows:
Initially, About
and Friends
load, and as soon as the user
data is available, we use interests[0]
to fetch feeds, potentially taking another second. The overall wait time amounts to max(1.5, 1.5) + 1 = 2.5
seconds.
This approach combines parallel and sequential requests, yielding better performance than the initial method.
The feeds
request must wait for the completion of the previous two requests, displaying a large spinner
in the interim. While this solution is functional, we must consider the runtime data requirements for each specific user.
You have Completed Chapter 3
By mastering parallel requests and managing dependencies in network calls, this chapter sets the foundation for building faster and more responsive React applications. Join us as we continue to navigate the intricate world of advanced network patterns in React.
In the next chapter, we explore further optimization strategies and delve into more complex scenarios of network fetching in React.