/ Gists / Fetch userlist with handling via AbortController
On gists

Fetch userlist with handling via AbortController

React

App.jsx Raw #

import { useEffect, useState } from "react"
import { User } from "./User"

export default function App() {
  const [users, setUsers] = useState([])
  const [error, setError] = useState(null)
  const [status, setStatus] = useState("idle")

  useEffect(() => {
    setStatus("loading")
    setUsers([])
    setError(null)

    const controller = new AbortController()
    fetch("https://jsonplaceholder.typicode.com/users", {
      signal: controller.signal,
    })
      .then(res => {
        if (res.ok) return res.json()
        throw new Error(`Status code: ${res.status}`)
      })
      .then(data => {
        setUsers(data)
        setStatus("fetched")
        setError(null)
      })
      .catch(err => {
        if (err.name === "AbortError") return
        setError(err)
        setUsers([])
        setStatus("error")
      })

    return () => {
      controller.abort()
    }
  }, [])

  return (
    <>
      <h1>User List</h1>
      {status === "loading" && <h2>Loading...</h2>}
      {status === "error" && (
        <>
          <h2>Error fetching users</h2>
          <p>{error.message}</p>
        </>
      )}
      {status === "fetched" && (
        <ul>
          {users.map(user => (
            <User key={user.id} name={user.name} />
          ))}
        </ul>
      )}
    </>
  )
}

App2.jsx Raw #

// 2 variant try-catch, await

import { useEffect, useState } from "react";
import User from "./User";

export default function App() {
  const [users, setUsers] = useState([]);
  const [status, setStatus] = useState("idle");

  useEffect(() => {
    const controller = new AbortController();
    (async () => {
      try {
        setStatus("loading");
        setUsers([]);
        const res = await fetch("https://jsonplaceholder.typicode.com/users", {
          signal: controller.signal,
        });
        if (res.ok) {
          setUsers(await res.json());
          setStatus("fetched");
          return;
        }
        throw new Error("Error fetching users");
      } catch (e) {
        if (e.name === "AbortError") return;
        setStatus("error");
        setUsers([]);
        console.log(e.message);
      }
    })();

    return () => {
      controller.abort();
    };
  }, []);

  return (
    <>
      <h1>Users List</h1>
      {status === "loading" && <h2>Loading...</h2>}
      {status === "error" && <h2>Error!</h2>}
      {status === "fetched" && users.length === 0 && <p>No users found.</p>}
      {status === "fetched" && <User users={users} />}
    </>
  );
}

useFetch.js Raw #

import { useState, useEffect } from "react"

export function useFetch(url, options = {}) {
  const [data, setData] = useState(null)
  const [error, setError] = useState(null)
  const [status, setStatus] = useState("idle")

  useEffect(() => {
    setData(null)
    setError(null)
    setStatus("loading")

    const controller = new AbortController()
    fetch(url, { signal: controller.signal, ...options })
      .then(res => {
        if (res.ok) return res.json()
        throw new Error(`Status code: ${res.status}`)
      })
      .then(data => {
        setData(data)
        setError(null)
        setStatus("fetched")
      })
      .catch(err => {
        if (err.name === "AbortError") return
        setData(null)
        setError(err)
        setStatus("error")
      })

    return () => {
      controller.abort()
    }
  }, [url, options])

  return { data, status, error }
}