Data Fetching
Fetch API
Master React data fetching with the native Fetch API, covering status states, request cleanups, race conditions, and AbortController.
1. Learning Objectives
In this lesson, you will learn how to sync components with external APIs using the native Fetch API. By the end of this topic, you will be able to:
- Perform GET and POST requests inside a component's
useEffectHook. - Manage loading, success, and error states to build robust UIs.
- Identify and resolve "race conditions" in data fetching.
- Use
AbortControllerto cancel ongoing HTTP requests. - Prevent memory leaks caused by updating state on unmounted components.
2. Overview
The native **Fetch API** is the browser standard for making network requests. In React, data fetching is usually initiated inside a useEffect Hook. To build a robust user experience, components must track loading and error states while cleaning up pending requests when parameters change or the component unmounts.
3. Why This Topic Matters
Handling network request lifecycles correctly prevents critical UI bugs:
- The Race Condition Bug: If a user clicks quickly between options (like User A, then User B), two network requests are sent. If the request for User A takes longer and resolves *after* User B, the screen will display User A's data, even though User B is selected. Cleanups prevent this.
- Unmounted State Updates: If a network request resolves after a component has unmounted, trying to update the component's state triggers a memory leak and console warnings.
4. Real-World Analogy
Think of data fetching cleanups like **canceling a restaurant order**:
- Without Cleanup (Race Condition): You order a pizza (Request 1), then quickly change your mind and order a burger (Request 2). Because there is no cleanup, the kitchen prepares both. The pizza arrives 30 minutes after your burger, overwriting your dinner.
- With Cleanup (AbortController): You order the pizza, then change your mind. You immediately tell the waiter to cancel the pizza order (abort Request 1) and prepare the burger instead. You only receive the burger, avoiding waste.
5. Core Concepts
Below is a comparison of cleanups using Boolean Flags and AbortControllers:
| Property | Boolean Flag Cleanup | AbortController API Cleanup (Recommended) |
|---|---|---|
| Mechanism | Uses a local variable (e.g. active = false) in the effect cleanup. |
Uses the browser's native AbortController to cancel the request. |
| Network behavior | The request runs to completion; the component simply ignores the resolved data. | The browser cancels the network request immediately, saving bandwidth. |
| Implementation Complexity | Low. Requires a single boolean check. | Medium. Requires instantiating a controller and passing a signal. |
6. Syntax & API Reference
This example shows how to configure a fetch call with an AbortController:
7. Visual Diagram
This diagram displays how a race condition occurs and how an AbortController resolves it:
8. Live Example — Full Working Code
Below is a complete HTML page demonstrating safe data fetching with loading indicators and abort cleanups:
What just happened? Clicking "Next" quickly aborts the previous network request and starts a new one immediately. Check the browser console to see `Fetch for user #X aborted.` logs. The AbortController prevents race conditions by canceling stale requests, ensuring that the component only renders data matching the active user ID.
9. Interactive Playground
Try It Yourself Challenges:
- Comment out the
controller.abort()statement in the cleanup function, change the network speed to "Slow 3G" in devtools, click next quickly, and verify that intermediate user names flash on the screen. - Modify the fetch call to perform a POST request that sends a new mock user object to the API.
10. Common Mistakes
| Mistake | Why it happens | Wrong | Correct |
|---|---|---|---|
| Omitting a cleanup mechanism for network requests | If you do not abort or ignore requests in the cleanup function, responses can resolve in arbitrary order. This can cause race conditions and trigger state updates on unmounted components, leading to memory leaks. | useEffect(() => { (no cleanup) |
Instantiate an AbortController, pass its signal to fetch, and call controller.abort() in the cleanup function. |
| Assuming fetch checks throw on HTTP error status codes | The native fetch() function only rejects the promise on network failures (e.g. no internet connection). It does not reject on HTTP error statuses (like 404 Not Found or 500 Server Error). You must check the response status manually. |
fetch(url).catch(err => setError(err)) (misses 404/500 errors) |
fetch(url).then(res => { |
11. Best Practices
- Always check res.ok: Check the
okproperty on response objects to handle HTTP errors (like 404 or 500 status codes). - Always clean up requests: Use `AbortController` (or a boolean flag) to cancel pending requests on parameter changes or unmounts, preventing race conditions and memory leaks.
- Track request states explicitly: Maintain separate states for `loading`, `error`, and `data` to handle all possible request lifecycles.
- Extract API logic: Move raw fetch code out of components and into separate helper modules or custom hooks (like `useFetch`) to keep components clean.
12. Browser Compatibility/Requirements
The Fetch API and `AbortController` are supported in all modern browsers.
13. Interview Questions
Q1: How do race conditions occur in React data fetching, and how does AbortController resolve them?
Answer: A race condition occurs when multiple async requests are sent in quick succession, and their responses resolve in a different order than they were sent. If a previous request resolves after a later request, the screen will display stale data. Passing an `AbortController` signal to the `fetch` call allows us to cancel the previous request immediately when the component re-renders or unmounts, ensuring that only the latest request's response updates the UI.
Q2: Does native `fetch` throw an exception on 404 or 500 error status codes? How should you handle these statuses?
Answer: No, native `fetch` does not reject the promise on HTTP error status codes (like 404 or 500); it only rejects on physical network failures (like loss of connection). To handle HTTP errors, you must check the response status manually using the `res.ok` boolean property, throwing a manual error if `res.ok` is false: `if (!res.ok) throw new Error('HTTP Error');`
14. Debugging Exercise
Identify why this component throws "Can't perform a React state update on an unmounted component" errors in testing console logs:
Diagnosis: The effect does not include a cleanup mechanism. If the user navigates away and the component unmounts before the fetch request resolves, the promise's callback executes setImg on the unmounted component, triggering a memory leak warning.
Fix: Use a boolean flag or an AbortController to prevent state updates after unmounting:
15. Practice Exercises
Exercise 1: Create a search input fetcher
Build a component named SearchFetcher. As the user types in an input field, fetch matching results from an API. Use an AbortController to cancel previous search requests as the user continues typing.
16. Scenario-Based Challenge
The Multi-Step Checkout Payment Sync Controller:
You are designing a checkout wizard. Clicking "Submit Payment" calls a payment processing API. If the user navigates away or closes the tab during processing, you must handle the network lifecycle carefully to avoid duplicate transactions. Describe how you would configure the native fetch calls and cleanups to handle this scenario.
17. Quick Quiz
Q1: What name property is set on errors caught when an HTTP request is aborted?
A) CancelError
B) AbortError
C) NetworkError
Answer: B — When an HTTP request is canceled using AbortController, the promise rejects with an error whose name property is 'AbortError'.
18. Summary & Key Takeaways
- In React, native Fetch API calls are initiated inside
useEffectHooks. - Always track loading, success, and error states explicitly to build responsive UIs.
- Use
AbortControllerto cancel pending network requests, preventing race conditions and memory leaks. - Validate responses using `res.ok`, as native `fetch` does not throw errors on 404 or 500 status codes.
19. Cheat Sheet
| Fetch API Element | Purpose |
|---|---|
const controller = new AbortController(); |
Creates an AbortController instance to cancel requests. |
fetch(url, { signal: controller.signal }) |
Associates the abort signal with the fetch request. |
controller.abort(); |
Aborts the network request, rejecting the promise with an `AbortError`. |