🛠️

カスタムフックを完全に理解する Part1

4 min read

はじめに

この記事ではカスタムフックを理解し、自分で作成・利用できるようになることがゴールです。
最近までカスタフックは"難しい"から勉強するのが面倒だし、別に使わなくていいじゃんと思っていたのですが、実際は "簡単" かつ "便利" でした。メリットが多く学習すればすぐにプロジェクトで利用したくなるでしょう。

ということで、この記事が私のようなカスタフック敬遠勢の助けになればと思っています。

【この記事で取り扱わない内容】
・hooks(useState等)の使い方
・カスタフックのテスト(近日ReactTestingLibraryを使ったテスト方法を投稿します。)

カスタムフックとは?

カスタムフックは文字通りフックをカスタムしたものになります。
「カスタム」と「フック」について理解すればカスタムフックの5割は理解できちゃいますので、
「カスタム」と「フック」という言葉について考えていきます。

フック

公式より引用です。

フック (hook) は React 16.8 で追加された新機能です。state などの React の機能を、クラスを書かずに使えるようになります。

https://ja.reactjs.org/docs/hooks-intro.html

つまり、hook とは Reactのクラスを書くことで利用できる state やライフサイクルなどの機能を、関数コンポーネント内で使用できるようにするための関数のことです。

まぁ、カスタムフックを理解するのにクラスを理解する必要はないです。
ここではフックとは以下の 関数 を指すのだと理解できればOKです。

  • useState
  • useEffect
  • useContext
  • useReducer
  • useCallback
  • useMemo
  • useRef
  • useImperativeHandle
  • useLayoutEffect
  • useDebugValue

https://ja.reactjs.org/docs/hooks-reference.html

カスタム

カスタムでググるといろいろ出てきますが、私は次にように説明します。

オリジナルの状態から何か変えているもの。

一言でいうと改造です。カスタムの理解はふんわりな感じでOKです。
今回"オリジナル"の対象となるのはReactから提供されているフックのことですね。

カスタムフック

改めて、フックとはなんなのか。

useStateやuseEfectなどのフックに、機能を追加したもの。

これだけです。
では実際に簡単なカスタムフックを作ってみましょう。

カスタムフックを作ってみる。

今回カスタムの対象とするフックはuseStateです。
カスタマイズする前にuseStateの機能を書き出してみました。

1. 引数でstateの初期値を決める
2. stateをコンポーネントに持たせる。
3. stateを更新する。

この3つかなと思います。
これ以外の機能さえ追加できればそれはカスタムフックということになります。

では、作ってみましょう。まずは簡単なUIコンポーネントを作ります。

export const App = () => {
  return (
    <div className="App">
      <h1>らーめん</h1>
    </div>
  );
};

次にどこでもいいので次の名前のファイルを作成します。

useDouble.ts

useDoubleの中は次のように書きます。

import { useState } from "react";

export const useDouble = (initialValue: number) => {
  const [value, setValue] = useState(initialValue);
  const setter = (num: number) => {
    setValue(num);
  };

  return [value, setter];
};

これをapp.tsxでimportして次によう書いてみます。

import { useDouble } from "../useDouble";
export const App = () => {
  const [value, setter] = useDouble(0);
  return (
    <div className="App">
      <button onClick={() => setter(1)}>関数実行</button>
      <h1>{value}</h1>
    </div>
  );
};

これでボタンを押下するとh1で表示されている0が1に変わります。

useStateはこんな感じで外からimportして利用できます。ただ、これだと名前が違うだけでuseDoubleはuseStateと同じです。先ほど挙げたuseStateの3つの機能以外を持っていないのでuseDoubelはカスタフックではないと言えそうです。
ここからカスタマイズしていきましょう。ここで再度useStateの機能を確認します。

1. 引数でstateの初期値を決める
2. stateをコンポーネントに持たせる。
3. stateを更新する。

まずは、1の機能を改造してみます。
useDoubleの引数はそのままstateにセットせず、どんな値を渡しても0にするようにします。

import { useState } from "react";

export const useDouble = (_initialValue: number) => {
  const [value, setValue] = useState(0);
  const setter = (num: number) => {
    setValue(num);
  };

  return [value, setter];
};

はい。なんて意味のない実装でしょうか。しかし、ボタンを押せばちゃんと描画が更新されました。このようにuseStateが提供するstateやstate更新機能は持ちつつ、新たな機能とを追加した "useStateではないもの" が作れました。これでも立派なカスタムフックですね!

では、次は少し実践的な処理を加えてみます。3の機能を改造します。

3. stateを更新する。

改造する前に3の機能について少し考えてみます。
useStateの更新は次のように書かれると思います

const [value, setValue] = useState(0);
setValue(100);
console.log(value) // => 100

setValueは受け取った値をそのままstateにセットし処理を完了してますね。
次はここをカスタマイズしてみます。
次のようにuseDoubleを書いてみてください。

export const useDouble = (_initialValue: number) => {
  const [value, setValue] = useState(0);
  const setter = (num: number) => {
    setValue(num);
  };

// stateを更新する関数doubleを定義
// ただ更新するだけではなく、引数で受け取った値を2倍にする。
  const double = (num: number) => {
    const result = num * 2;
    setValue(result);
  };

 // 型定義が面倒なのでオブジェクトで返すようにしました。
 // この返却方法は一般的です。(記事の後半で詳しく解説します。)
  return { value, setter, double };
};

appも書き換えましょう

export const App = () => {
  const { value, double } = useDouble(100);
  return (
    <div className="App">
      <button onClick={() => double(10)}>2倍関数実行</button>
      <h1>{value}</h1>
    </div>
  );
};

新たにdouble関数を実行するボタンを配置しました。実行してみるとvalueの描画が20に変わりました。
これでstateの更新処理にひと手間加えることができました。カスタムフックとはたったこれだけのことなのです。ここまでくれば好きなようにカスタムフックが作れそうです。あとは、好きなように関数を増やしたり追加でフックを定義したりすることでより複雑なカスタムフックを作っていけそうですね。

ということで、ちょっと複雑で実践的なカスタムフックを作ってみましょう。
useStateを1つだけ利用していましたが、2つ使ってさらにuseEffectも使っちゃいましょう。

カスタムフック応用

執筆中

Discussion

ログインするとコメントできます