useEffect: some issues with data fetching in Effects?

amrguaily

Amr Guaily

Posted on October 24, 2022

useEffect: some issues with data fetching in Effects?

In this tutorial we will discuss some issues with data fetching in useEffect:
1. Race Conditions
2. No Caching
3. No content in initial HTML
4. Slow network waterfalls.


1️⃣ Race Condition

You have a component that accepts an id as a prop, uses that id to fetch corresponding data and display it.

code snippet

I made a Sandbox . to make it more clear for you, And i also use setTimeOut() to add random wait period.

Now, Where is the Problem
If you rapidly click the Fetch data! button several times. The app will make several requests with different ids which will finish randomly out of order. And as a user, I expect to see the result of the last request. but this may not be the case, our app displays a different result depending on which request completes first. Try it yourself.

For Example: Here I expect to see the result of last request which id == 58, but the request with id == 10 finishes after the last request, So our app display its result.

code snippet

How can I fix Race Conditions
To fix the race condition, we need to add a clean-up function to ignore stale responses - There are 2 main approaches:
1. Use Boolean flag.
2. Use AbortController.

⏩ Clean-up Function with Boolean Flag

code snippet
According to React's official Docs:

If a component renders multiple times The previous effect is cleaned up before executing the next effect.

So, In the above code:
• Changing id will cause a re-render.

• On every re-render the clean-up function gets run first to clean-up effects from the previous render before executing the next effect.

ignore is set to true in the clean-up function, so the previous requests won't be able to update our state.

• Now, We still send a request with each click on fetch data! button, But only the results from the last request will be used. check out the code in snadbox

⏩ Clean-up Function with AbortController

code snippet
In the above code:
• We initialize a new AbortController instance.

• Then pass the signal from the controller to our fetch as a signal property.

• As we did before - call the abort function inside the clean-up function. check out the code in snadbox

✅ Using AbortController is the preferred way to cancel API requests, because it avoids wasting user bandwidth.


2️⃣ No Caching

🧐 Problem:
There is no-caching, so if the user navigate to another page and then clicks back, He will see the loading spinner again until the data is re-fetched. And this would be a bad user experience.

🤔 What we want?
When the user navigate to another page and then clicks back, He Should see the previous screen instantly instead of a spinner.

Solution:
We can use third-party solution like React Query or useSwr


3️⃣ No content in initial HTML

🧐 Problem:
Fetching in useEffect means React components render, then fetch.
Code Snippet
In the above code Snippet:
• The function passed to useEffect will be executed after the component renders. So this means: In the initial render the data is null.

• Then, the effect function will be executed and update state with actual data.

Since React is a Clint-Side JS library, all these renders happens on The browser. As a result the initial HTML is pretty empty. And if you try to use server-side rendering, you'll notice that useEffect doesn't run on the server. So we will end with a big problem with SEO..

🤔 What we want?
We want to pre-render the content of a page on the server instead of on the Browser.

Solution:
We can use Next.js, Next.js has Built-in server-side rendering(SSR).


4️⃣ Slow network waterfalls

🧐 Problem:
If you have parent and child components both doing fetching in useEffect, then the child component can't even start fetching until the parent component finishes fetching. And This can lead to slow network waterfalls.

🤔 What we want?
The child component doesn’t have to wait for its parent to finish fetching their data before it can start.

Solution:
Stop fetching in child component. Instead, "lift" fetch calls to a parent so they can be handled in parallel via promise.all.

Then, pass the data down via props.
Render as you fetch


Main Resources

https://beta.reactjs.org/learn/you-might-not-need-an-effect#fetching-data

https://www.reddit.com/r/reactjs/comments/vi6q6f/comment/iddrjue

https://www.youtube.com/watch?v=QQYeipc_cik

https://maxrozen.com/race-conditions-fetching-data-react-with-useeffect

💖 💪 🙅 🚩
amrguaily
Amr Guaily

Posted on October 24, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related