useEffect: some issues with data fetching in Effects?
Tags: react, webdev
Published: 2022-10-24 15:48:54

In this tutorial we will discuss some issues with data fetching in `useEffect`:
**1.** [Race Conditions](#race condition)
**2.** [No Caching](#no-caching)
**3.** [No content in initial HTML](#no-content)
**4.** [Slow network waterfalls.](#slow-network)

---
## 1️⃣ Race Condition <a name="race condition"></a>
You have a component that accepts an `id` as a prop, uses that `id` to fetch corresponding data and display it.

![code snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m286eyas6zix3e5z31fs.png)

I made a [Sandbox ](https://codesandbox.io/s/race-condition-rekfni?file=/src/FetchData.js). to make it more clear for you, And i also use `setTimeOut()` to add random wait period.

<u>**Now, Where is the Problem</u> ⁉**
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](https://codesandbox.io/s/race-condition-rekfni?file=/src/FetchData.js).

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uxl1nq7wwui3ckd4luhw.png)

**<u>How can I fix Race Conditions</u> ⁉**
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgstpwgblcyeg1qc92u6.png)
According to React's official [Docs](https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect):
> If a component renders multiple times **The previous effect is cleaned up before executing the next effect.**

<u>**So, In the above code:**</u>
• 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](https://codesandbox.io/s/clean-up-function-with-boolean-flag-7bg3sy)

### ⏩ Clean-up Function with AbortController
![code snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vc067io8igkotoo1xjx.png)
<u>**In the above code:**</u>
• 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](https://codesandbox.io/s/clean-up-function-with-abrotcontroller-tqs5lc?file=/src/FetchData.js)

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

---

## 2️⃣ No Caching <a name="no-caching"></a>
🧐 <u>**Problem:**</u>
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.

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

✅ <u>**Solution:**</u>
We can use third-party solution like [React Query](https://tanstack.com/query/v4/?from=reactQueryV3&original=https://react-query-v3.tanstack.com/) or [useSwr](https://swr.vercel.app/)

---

## 3️⃣ No content in initial HTML <a name="no-content"></a>
🧐 <u>**Problem:**</u>
Fetching in `useEffect` means React components render, then fetch.
![Code Snippet](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0cqka9ck2wldky5511nh.png)
<u>**In the above code Snippet:**</u>
• 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..

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

✅ <u>**Solution:**</u>
We can use [Next.js](https://nextjs.org), Next.js has Built-in server-side rendering(SSR).

---

## 4️⃣ Slow network waterfalls <a name="slow-network"></a>
🧐 <u>**Problem:**</u>
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.

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

✅ <u>**Solution:**</u>
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jtsn9cilbjm4rfr886hd.png)

---

## 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