淺談 React Hooks 簡介與優缺點分析

Lin Yen-Cheng on 2019-04-12 6 min. read

Module Pattern

在剛開始學習這個新的寫法時,直覺的會讓人想到利用 js closure 衍生出來的 Module Pattern,回想當初學這個 Pattern 是為了製作函式庫,所以有一些需要存的狀態還有需要讓外部叫用的功能,運用 function 會產生 closure 的特性,我們就可以區分出 private, public 的變數以及函式。

底下示範了一個運用 IIFE (Immediately Invoked Function Expression) 來實作的計數器模組,使用 IIFE 的目的是希望一定義就執行,這就是最基本的 Module pattern 了,可以發現 count 就是我們 private 的變數,指能夠透過回傳的 public function 來進行操作。

const moduleCounter = (function(){
    let count = 0;
    return {
        getValue(): function() {
            return count;
        },
        increment: function()  {
            return ++count;
        },
        reset: function() {
            console.log('reset:' + count);
            count = 0;
        }
    };
}());

hooks 簡介

那為什麼會覺得很像 Module pattern,寫下來會發現,hooks 並沒有提出太多新的概念,僅僅是利用會 return jsx 的 function 搭配特製的函式來取得保存的狀態做為元件的寫法。

雖然沒有真的去看原始碼,但感覺差異在 IIFE 是立即就定義,另外一個是 JSX 由 react 編譯時才進行相關操作及定義,其中 useState 感覺就是要把 private 變數也就是狀態放進 closure 的寫法,狀態則是 react 用來操作 Dom 變化一個重要的依據。最後當 JSX 編譯完後,我們就可以透過類似叫用 public function 的方法去改變 private 變數。

function Counter() {
  const [count, setCount] = useState(0);

  function increment() {
    setCount(count + 1);
  }

  function reset() {
    setCount(0);
  }

  function getValue() {
    return count;
  }

  return (
    <div>
      <button type="button" onClick=&#123;increment&#125;>
        add
      </button>
      <button type="button" onClick=&#123;reset&#125;>
        reset
      </button>
      <span>&#123;getValue()&#125;</span>
    </div>
  );
&#125;

由於 React 是 component-based 的一個函式庫,所以元件本身的定義和規範就蠻重要的,其中比較特殊的是元件在實際運用上會有一些生命週期,大致上我們平常會使用到的就是 componentDidMountcomponentDidUpdate,剩下可能會用到但比較少的是 componentWillUnmount,由於是寫在 function 中,所以可以想像整個 function 的內容都是原來寫法中 render() 裡的內容,差別在把 constructor 中的狀態用其他的方法寫在這個 function 裡面,元件原本由狀態改變來驅動的特性一樣沒有改變。

在 hooks 中,是用 useEffect(didUpdateCallback) 來運用元件週期的,最重要的是加上條件這個部分,底下是官方的範例,中括號陣列裡面的就是觸發條件,如果使用空陣列,就相當於 componentDidMount 的效果:

useEffect(() => &#123;
  const subscription = props.source.subscribe();
  return () => &#123;
    subscription.unsubscribe();
  &#125;;
&#125;, [props.source]);

更好的寫法則是再抽出來,就會變下面這樣,更清楚也更好測試,也可以重複的去使用相關邏輯。


function useCounter(initial) &#123;
  const [count, setCount] = useState(initial);
  const increment = () => setCount(count + 1);
  const reset = () => setCount(0);
  return &#123; reset, count, increment &#125;;
&#125;

function Counter() &#123;
    const [reset, count, increment] = useCounter(0);

    return (
    <div>
        <button type="button" onClick=&#123;increment&#125;>add</button>
        <button type="button" onClick=&#123;reset&#125;>reset</button>
        <span>&#123;count)&#125;</span>
    </div>
    );
&#125;

hooks 優缺點比較

優點

  • 較接近原生的 js 寫法,對於剛開始接觸的人有好處,且不需要懂 ES6 也可以寫
  • 減少了解太過多餘的元件週期,只要控制好 useEffect 即可
  • 用相對簡單的寫法解決複雜的問題,這點尤其重要

缺點

  • useEffect 由於把三個元件狀態合在一起,導致寫法太過簡單,也因為合在一起的關係所以使用時要注意,如果沒有加上限制就容易造成不停的觸發
  • 盡量避免在 function 中寫到 new 或是可能沒有防呆事件的 listener,因為在每次更新畫面的時候都會重做一次

喜歡這篇文章,請幫忙拍拍手喔 🤣

share