BRUCE_FEBRUCE_FE

EN/CH Mode

BRUCE_FE JS Interview Notes - Implement Custom useFetch Hook

Learn how to implement custom useFetch Hook in React, master data fetching, loading state management, error handling, and caching mechanisms to enhance frontend application user experience.

影片縮圖

Lazy to read articles? Then watch videos!

Custom useFetch Hook Overview

In React applications, data fetching is a common requirement. Custom useFetch Hook can encapsulate data fetching logic, including loading states, error handling, and even implement advanced features like caching and request deduplication, thereby simplifying component code and improving reusability.

Why Do We Need Custom useFetch Hook?

  • 1. Separation of concerns: Separate data fetching logic from view logic
  • 2. Code reuse: Avoid writing similar data fetching logic repeatedly in multiple components
  • 3. Consistent handling: Uniformly handle loading states, errors, and responses
  • 4. Feature extension: Easily add features like caching, debouncing, throttling, etc.
  • 5. Simplified testing: Make components and data fetching logic easier to test independently

Basic useFetch Hook Implementation

Below is a basic useFetch Hook implementation containing data fetching, loading state, and error handling:

import { useState, useEffect } from 'react';

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // 重置狀態
    setLoading(true);
    setData(null);
    setError(null);
    
    // 獲取數據
    fetch(url)
      .then(response => {
        // 檢查響應狀態
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json();
      })
      .then(json => {
        setData(json);
        setLoading(false);
      })
      .catch(err => {
        setError(err.message);
        setLoading(false);
      });
  }, [url]); // 當 URL 變化時重新獲取數據

  return { data, loading, error };
}

// 使用示例
function UserProfile({ userId }) {
  const { data, loading, error } = useFetch(`/api/users/${userId}`);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (!data) return null;

  return (
    <div>
      <h1>{data.name}</h1>
      <p>Email: {data.email}</p>
    </div>
  );
}

This basic implementation provides three key states: data, loading state, and error information, allowing components to render different UIs based on these states.

useFetch Implementation with Request Cancellation

In practical applications, when components unmount or dependencies change, we need to cancel ongoing requests to avoid memory leaks and state update errors. Below is a useFetch Hook implementation using AbortController for request cancellation:

function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Create AbortController instance
    const controller = new AbortController();
    const signal = controller.signal;
    
    // Reset states
    setLoading(true);
    setData(null);
    setError(null);
    
    // Fetch data
    fetch(url, { signal })
      .then(response => {
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        return response.json();
      })
      .then(json => {
        setData(json);
        setLoading(false);
      })
      .catch(err => {
        // Ignore errors caused by request cancellation
        if (err.name === 'AbortError') {
          console.log('Fetch aborted');
        } else {
          setError(err.message);
          setLoading(false);
        }
      });
    
    // Cleanup function: Cancel request when component unmounts or dependencies change
    return () => {
      controller.abort();
    };
  }, [url]);

  return { data, loading, error };
}

This improved version uses the AbortController API to cancel ongoing requests. When the component unmounts or the URL changes, the cleanup function automatically executes, canceling incomplete requests, effectively preventing memory leaks and issues with setting state on unmounted components.