海豹人的第一個家

Sealman

Frontend Engineer / Taiwanese / Passion Comes From Mastery

Code Reuse: React Custom Hooks

本文介紹為何要使用 Custom Hooks 以及其撰寫的方式。

What is Custom Hook

  • Custom Hooks:可以使用 React Hooks 與 State 的函式
  • 使用時機:當不同元件裡有著一定程度共通的邏輯時,我們會想要複用它,而在元件中只撰寫不同的部分
  • 特性:Custom Hook 每次使用時,各自內部的 state 與 effect 都是完全獨立的

Creating a Custom Hook Function

Custom Hooks 並不是 Functional Components,它是一個函式,只是在做法上有點類似。

首先,我們通常都會在 src 底下建立一個 hooks 資料夾,用來存放之後建立的 Custom Hooks。而 Custom Hooks 的檔案名稱也是個人喜好,我自己喜歡用 use-xxx.js 作為命名規則。

範例:新增 use-counter.js 檔案,建立一個函式 useCounter

注意!此時的 useXXX 則是必須遵守的名稱規範,這是為了能讓 React 辨別這是 Custom Hooks。

import { useEffect, useState } from 'react';

const useCounter = () => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter((prevCounter) => prevCounter + 1);
    }, 1000);

    return () => clearInterval(interval);
  }, []);

  return counter;
};

export default useCounter;

如同內建的 React Hooks,這個 Custom Hook 也會 return 東西,不過可以是「任何」型別。

共用的是邏輯,不會共用狀態

在 Custom Hooks 中,使用 useStateuseEffect 會與使用它的元件做連結。

如果在「多個組件」使用同一個 Custom Hook,每個組件都會產生一套自己的 Custom Hook,也就是裡面使用的 State 或 Effect 等資料都是「不會共用」的。

Using Custom Hooks

現在我們在其他元件中使用 useCounter 來取得回傳值,到這裡,我們已經成功做到邏輯拆分囉。

const ForwardCounter = () => {
  const counter = useCounter(); // 這個 counter 是 Custom Hook 回傳的

  return <Card>{counter}</Card>;
};

export default ForwardCounter;

接下來,我們再針對不同的邏輯去做改變,像是透過「參數」來指定不同的邏輯。

例如:透過 forwards 參數,給予 Custom Hook false 表示遞減,預設的 true 則為遞增。

const useCounter = (forwards = true) => {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      // 根據參數的值判斷要執行的動作
      if (forwards) {
        setCounter((prevCounter) => prevCounter + 1);
      } else {
        setCounter((prevCounter) => prevCounter - 1);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, [forwards]); // 記得把參數放入 Dependencies array

  return counter;
};

現在我們來使用參數吧,選擇傳入 false 表示要執行遞減。

const BackwardCounter = () => {
  const counter = useCounter(false);

  return <Card>{counter}</Card>;
};

使用 Custom Hooks 的注意事項

1. 注意那些傳參考的類別

如果 Custom Hook 回傳的內容有函式(或物件),在使用 useEffect 時「不能」將它加入 dependencies array 以偵測 Function 的改變。

為什麼?照理來說不是要加入嗎?

是沒錯,可是加入後會造成 Infinite Loop!因為每次新生成的函式物件雖然內容看似一樣,但其實傳的參考都不同,這就會造成 useEffect 一直重跑。

  • 解決方法:給予可能變動的函式(或物件)都套上 useCallback(或 useMemo),這樣就能確保是同一個物件了

2. 避免在使用 Custom Hook 時傳入參數,將外部依賴改為函式參數

除了上述說的使用 useCallback 或 useMemo 以外,其實我們還有另一個辦法,那就是將放在 Custom Hook 身上的 Dependencies 改放到內容裡面。

內容就是指 Custom Hook 中用到這些變數的地方,我們直接讓 Custom Hook 裡面的 Function 使用參數就好,這時候就不必對 Custom Hook 傳參數,Custom Hook 也就不用新增 Dependencies 了。

回顧

看完這篇文章,我們到底有什麼收穫呢?藉由本文可以理解到…

  • Custom Hooks

References