React Fundamentals

0% completed

Previous
Next
Handling Errors, Cleanup, and Race Conditions with useEffect

Handling Errors

When fetching data, errors can happen, like network issues or invalid URLs. You can handle these errors with .catch() or a try-catch block.

Example:

useEffect(() => { const fetchData = async () => { try { const response = await fetch("https://api.example.com/fruits"); if (!response.ok) throw new Error("Network response was not ok"); const data = await response.json(); setFruits(data); setLoading(false); } catch (error) { console.error("Error fetching data:", error); } }; fetchData(); }, []);

In this useEffect hook example:

  1. An asynchronous function of fetchData fetches data from a URL ("https://api.example.com/fruits").
  2. Inside fetchData, it sends a request to the API and checks if the response is successful.
  3. If the response is valid, it parses the data as JSON and updates the component's state with the fetched fruits using setFruits(data). It also sets setLoading(false) to indicate the loading process is complete.
  4. If there's an error during the fetch or if the response is not okay, it logs the error to the console.
  5. The fetchData function is called immediately after being defined.
  6. The empty dependency array ([]) ensures this effect runs only once when the component mounts.

Fetching Data from APIs with Loading State and Error Handling

Here’s a simple example of fetching data from an API using useEffect:

Example:

import React, { useState, useEffect } from "react"; const FruitList = () => { const [fruits, setFruits] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch("https://api.example.com/fruits") .then((response) => response.json()) .then((data) => { setFruits(data); setLoading(false); }) .catch((error) => console.error("Error fetching fruits:", error)); }, []); if (loading) return <p>Loading...</p>; return ( <ul> {fruits.map((fruit) => ( <li key={fruit.id}>{fruit.name}</li> ))} </ul> ); }; export default FruitList;

Breakdown of the Code:

  1. State Management

    • fruits holds the fetched data.
    • loading tracks whether the data is still being fetched.
  2. Fetching Data

    • The fetch function retrieves data from the API.
    • The .then() methods handle the response.
  3. Conditional Rendering

    • If the data is still loading, we display a "Loading..." message.
  4. Error Handling

    • The .catch() method handles errors in a Promise chain by logging a custom error message and the error object (e.g., with console.error), ensuring efficient error management.

Cleaning Up Effects

Sometimes, effects like timers or subscriptions need to be cleaned up to prevent memory leaks. useEffect lets you return a cleanup function that runs when the component unmounts.

Example:

useEffect(() => { const timer = setInterval(() => { console.log("Timer running..."); }, 1000); return () => clearInterval(timer); // Cleanup }, []);

For data fetching, you might clean up ongoing requests using the AbortController.

Race Conditions

Race conditions happen when multiple fetch requests overlap, and the app uses outdated data. To prevent this, you can use the Abortcontroller to cancel the previous request when a new one starts.

Example:

useEffect(() => { const controller = new AbortController(); const signal = controller.signal; const fetchData = async () => { try { const response = await fetch("https://api.example.com/fruits", { signal }); const data = await response.json(); setFruits(data); } catch (error) { if (error.name === "AbortError") { console.log("Fetch aborted"); } else { console.error("Error fetching data:", error); } } }; fetchData(); return () => controller.abort(); // Cancel the request }, []);

.....

.....

.....

Like the course? Get enrolled and start learning!
Previous
Next