iT邦幫忙

2021 iThome 鐵人賽

DAY 10
2
Modern Web

用30天更加認識 React.js 這個好朋友系列 第 10

Day10-React Hook 篇-打造自己的 Hook:Custom Hook

經過前面幾天的介紹,我們認識了許多常使用的 hooks,不過除了那些 hooks 之外,我們也可以將一些常用的共同程式邏輯抽取出來寫一個函式,這就是 Custom Hook。

使用 Custom Hook 要知道的事

  1. 使用 Custom hook 必須以 use 作為函式名稱的開頭,讓 eslint-plugin-react-hooks 去檢查這個 hook 是否有違反 react hook 的規則。
  2. 可以在 Custom hook 裡面使用其他的 React hook。

useAxios

在了解 Custom Hook 後,我們來實做一個 Custom Hook,這個 hook 是一個用來呼叫 Api 並進行一些處理的 Custom Hook,名字就叫 useAxios。

2022/1/26 更新 註: 這裡只是示範如果做一個 Custom hook,在呼叫 api 還有其他的做法,例如 redux dispatch async action + middleware,讀者可以自行依照狀況判斷使用~

第一步,建立雛形

在 codesandbox 新增一個檔案 useAxios,引入 axios 套件後,我們要使用 JSONplaceholder 的 api 作為 Base URL,等這個 hook 完成後會使用它來呼叫 JSONplaceholder 的 api。

在 useAxios 檔案我們宣告一個函式 useAxios,在做呼叫 api 時我們也會需要對一些像是呼叫 api 的載入時間和錯誤訊息做處理,所以建立兩個 state: isLoading 和 error,並且建立一個發出請求的函式 sendRequest,在這個函式內會對呼叫的 api 做處理,最後回傳一個包含 isLoading、error 和 sendRequest 的物件。

import { useState, useCallback } from "react";
import axios from "axios";

axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";

const useAxios = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const sendRequest = useCallback(() => {}, []);

  return {
    isLoading,
    error,
    sendRequest,
  };
};

export default useAxios;

第二步,完成 sendRequest 函式

在 sendRequest 中,我們會傳入兩個參數 requestConfig 和 applyData,因為在呼叫每隻 api 的路徑、http 方法和其他參數都不太一樣,所以透過 requestConfig 可以將這些資訊帶入到 sendRequest 裡面,處理完後透過 callback function 也就是第二個參數 applyData 將呼叫 api 的結果回傳。

完成後的程式碼如下:

import { useState, useCallback } from 'react';
import axios from 'axios';

axios.defaults.baseURL = "https://jsonplaceholder.typicode.com";

const useAxios = () => {
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);

  const sendRequest = useCallback(async (requestConfig, applyData) => {
    setIsLoading(true);
    setError(null);

    let res;
    try {
      res = await axios({
        url: requestConfig.url,
        method: requestConfig.method ? requestConfig.method : 'GET',
        headers: requestConfig.headers ? requestConfig.headers : {},
        data: requestConfig.data ? requestConfig.data : null,
      });
    } catch (error) {
      setError(error.message || 'Something went wrong!');
    } finally {
      if (res) {
        applyData(res.data);
      }
      setIsLoading(false);
    }
  }, []);

  return {
    isLoading,
    error,
    sendRequest,
  };
};

export default useAxios;

完成 useAxios hook,我們就可以實際使用囉!

在 App 元件中呼叫幾支 JSONplaceholder 的 api。

import { useEffect, useState } from "react";

import useAxios from "./useAxios";

export default function App() {
  const [data, setData] = useState([]);
  const { isLoading, error, sendRequest: fetchData } = useAxios();
  const { sendRequest: createData } = useAxios();

  useEffect(() => {
    fetchData({ url: "/posts" }, (res) => {
      console.log(res);
      setData(res);
    });
  }, []);

  const clickNewData = () => {
    createData(
      {
        url: "/posts",
        method: "POST",
        data: {
          // id 沒加是因為 JSONPlaceholder 只做假新增不會更新到資料庫,所以 JSONPlaceholder 會自動產生一個假的 id
          userId: 10,
          title: "新增的物件",
          body: "example"
        }
      },
      (res) => console.log(res)
    );
  };

  if (isLoading) return <h1>Loading...</h1>;
  if (error) return <h1>{error}</h1>;

  return (
    <>
      <ul>
        {data.map((item) => (
          <li key={item.id}>
            <p>{item.title}</p>
          </li>
        ))}
      </ul>
      <button onClick={clickNewData}>新增</button>
    </>
  );
}

這次的實作程式碼在以下連結,另外附上 JSONplaceholder 的官網連結:

範例程式碼

JSONplaceholder

2023/03/02 補充-Optimizing a custom Hook

這是我在閱讀 React Docs 看到的,useCallback 的文件有提到推薦為 custom Hook 內的函式加上 useCallback。


上一篇
Day9-React Hook 篇-認識 useCallback
下一篇
Day11-React 表單驗證篇-不使用 hook 或第三方函式庫
系列文
用30天更加認識 React.js 這個好朋友33
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言