iT邦幫忙

2023 iThome 鐵人賽

DAY 6
0

對前端工程師來說,串接api應該是超級常見的事了吧,我們今天就用axios來寫useFetch來控制data、loading與錯誤吧

基礎版

import axios, { Method, AxiosRequestConfig } from "axios";
import { useEffect, useState } from "react";

type UseFetchProps = {
  method?: Method;
  options?: AxiosRequestConfig;
};

export function useFetch<T>(
  url: string,
  { method, options }: UseFetchProps = { method: "GET" }
) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<string>("");

  async function fetchApi() {
    try {
      setLoading(true);
      const res = await axios({ method, url, ...options });
      setData(res.data);
    } catch (err) {
      console.error(err);
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }

  useEffect(() => {
    fetchApi();
  }, [url]);

  return { data, loading, error };
}

因為基礎版的應該大家都算熟悉了,我就不多做介紹了,我們有兩個參數,url 是必填的,method與option都是選填,method預設是用GET ,需要用POST再傳入method就好,大部分的前幾天都有提過,應該不會太困難,MethodAxiosRequestConfig 的這個Type是axios給的,我們不需要自行定義或者用泛型

但基礎版有時候在我們實際上可能不夠用,所以我又寫了一個進階版

進階版

更多的是我添加了以下功能

  1. 常用的header也放進來了,雖然也是能放在options裡傳入,但是如果常用的話,區分開來也比較清楚,例如你要call的api需要authorization token
  2. 加上了緩存
  3. 在error上更清楚給予錯誤訊息
  4. 加上abort controller ,避免內存泄露
import axios, { AxiosRequestConfig, Method } from "axios";
import { useEffect, useState, useRef } from "react";

// 用來儲存緩存的數據
const cache: Record<string, any> = {};

type UseFetchProps = {
  url: string;
  method?: Method;
  headers?: Record<string, string>;
  options?: AxiosRequestConfig;
};

type UseFetchState<T> = {
  data: T | null;
  loading: boolean;
  error: {
    message: string;
    code?: number;
  } | null;
};

export function useFetchAPI<T>(props: UseFetchProps): UseFetchState<T> {
  const { url, method = "GET", headers, options } = props;
  const cacheKey = useRef(`${method}-${url}-${JSON.stringify(options)}`);
  const [state, setState] = useState<UseFetchState<T>>({
    data: null,
    loading: true,
    error: null,
  });

  useEffect(() => {
    const controller = new AbortController();

    // 檢查緩存
    if (cache[cacheKey.current]) {
      setState({
        data: cache[cacheKey.current],
        loading: false,
        error: null,
      });
      return;
    }

    // 發出新請求
    const fetchData = async () => {
      try {
        const res = await axios({
          method,
          url,
          headers,
          signal: controller.signal,
          ...options,
        });

        // 更新緩存
        cache[cacheKey.current] = res.data;

        setState({
          data: res.data,
          loading: false,
          error: null,
        });
      } catch (error) {
        setState({
          data: null,
          loading: false,
          error: {
            message: error.message,
            code: error.response?.status,
          },
        });
      }
    };

    fetchData();

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

  return state;
}

當然還有很多可以加的

例如:

  1. 重新抓取資料:類似react-queryrefetch
  2. Debounce/Throttle: 需要搜索或其他高頻操作,添加防抖或節流
  3. Response 預處理: 預處理返回的數據,例如自動解析 JSON,轉換日期格式等
  4. 重試機制: 在請求失敗時,自動重試的機制。

這些都可以自己嘗試,並且自己試試看寫出測試,明天就來教學如何寫api的測試摟~


上一篇
[Day 5] 用第二種測試方式寫useSessionStorage測試
下一篇
[Day 7] useFetch測試摟
系列文
React進階班,用typescript與jest製作自己的custom hooks庫30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言