✍🏻

React + typescript カスタムフックの使い方

2022/11/14に公開

はじめに

今回は、カスタムフックについて紹介しようと思います。
(なかなか、typescript の情報がなくて困りますね。。。)
今回は、以下を参考にさせて頂きました。

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

では、早速本題へ移りましょう。

カスタムフックとは

カスタムフックとは、他のhooksをまとめるためのものです。useStateuseEffectといった様々なhooksを一つのファイル内で使用して、コードが長くなってしまったことはないでしょうか?コードが長いと読む気も失せますし、チーム開発だとコードレビューが厳しいです。(現在チーム開発をしているので、できるだけ気にしています。)hooksをまとめてしまってコードをスッキリさせたい!そんな時に、活躍するのがカスタムフックです。

実際に、ハンズオン形式でやってみましょう。

カスタムフックの使い方

今回は、ボタンを押すと背景色が変化するコードを準備しました。
まずは、プロジェクトを準備しましょう。

npx create-next-app --ts

プロジェクトができたら、以下のソースコードをsrc/pages/index.tsxにコピペしましょう!

src/pages/index.tsx
import type { NextPage } from "next";
import { useState } from "react";

const Home: NextPage = () => {
  const [color, setColor] = useState<"red" | "blue">("red");
  const [bgColor, setBgColor] = useState<"yellow" | "black">("yellow");

  return (
    <div
      style={{
        color: color,
        background: bgColor,
      }}
    >
      <h1>Hello</h1>
      <div>
        <h2>テキストボタン</h2>
        <button
          onClick={() => {
            setColor("red");
          }}
        ></button>
        <button
          onClick={() => {
            setColor("blue");
          }}
        ></button>
      </div>
      <div>
        <h2>背景ボタン</h2>
        <button
          onClick={() => {
            setBgColor("yellow");
          }}
        ></button>
        <button
          onClick={() => {
            setBgColor("black");
          }}
        ></button>
      </div>
    </div>
  );
};

export default Home;

これで、以下のような画面が表示されると思います。各ボタンを押すと、テキストや背景の色が変化することを確認してください。
(配色のセンスがないのは、申し訳ありません。。。)

では、本題に入ります。今回は、現在index.tsx内で使用している 2 つのuseStateを外に出して、このファイルを読み取り専用にします。では、src配下に新しいフォルダを作成しましょう。私は、hooksというフォルダを作成します。作成後、hooksの配下にtextSetColor.tsbgSetColor.tsを作成します。これで、現在のフォルダ構成は以下のようになっているかと思います。(フォルダの構成は参考程度で大丈夫です。)

src
  ├─pages
  |      index.tsx
  └─hooks
         textSetColor.ts
         bgSetColor.ts

まずテキストの色の状態管理している、以下のコード

const [color, setColor] = useState<"red" | "blue">("red");

このコードをカスタムフックにしてみましょう。
textSetColor.tsに移って以下の関数名で関数を定義しましょう。(関数名が重要になります。)

export default function useTextColor() {}

その後、先ほど紹介したindex.tsxに定義しているテキストの色の状態を管理しているuseStateを、先ほど定義した関数の中に移してください。次に、useStateがないため、エラーが出ているためuseStateimportしましょう。これで、以下のようになっているかと思います。

src/hooks/textSetColor.ts
import { useState } from "react";

export default function useTextColor() {
    const [color, setColor] = useState<"red" | "blue">("red");

}

ここまで来たら、後は簡単です。このファイルを作成する前、index.tsxではuseStatecolorsetColorの二つの変数と更新用関数を使用していました。しかし現在、これら 2 つがなくてindex.tsx内ではエラーがでています。エラーを解消する為には、colorsetColorを渡せばエラーが治りますね。ということで、戻り値にcolorsetColorを返してあげましょう。以下のようにします。

src/hooks/textSetColor.ts
import { useState } from "react";

export default function useTextColor() {
    const [color, setColor] = useState<"red" | "blue">("red");

    return {color , setColor}
}

ここで、戻り値は{}これで返してあげましょう。配列で返すことも可能ですが、配列で返してしまうと、この後、使用する際に型の宣言を無駄にしてあげる必要が出てしまい面倒になります。「{}」で、返しておくと型の推測をしてくれるため、楽できます。基本は、この返し方をすれば問題ありません。
最後に、index.tsxに移り、先ほど作成したカスタムフックを呼び出してあげましょう。呼出し方は、以下のようになります。

const { color, setColor } = useTextColor();

index.tsxに記述すると、以下のようになります。

src/pages/index.tsx
import type { NextPage } from "next";
import { useState } from "react";
import useTextColor from "../hooks/textSetColor";

const Home: NextPage = () => {
  const { color, setColor } = useTextColor();
  const [bgColor, setBgColor] = useState<"yellow" | "black">("yellow");

  return (
    <div
      style={{
        color: color,
        background: bgColor,
      }}
    >
      <h1>Hello</h1>
      <div>
        <h2>テキストボタン</h2>
        <button
          onClick={() => {
            setColor("red");
          }}
        ></button>
        <button
          onClick={() => {
            setColor("blue");
          }}
        ></button>
      </div>
      <div>
        <h2>背景ボタン</h2>
        <button
          onClick={() => {
            setBgColor("yellow");
          }}
        ></button>
        <button
          onClick={() => {
            setBgColor("black");
          }}
        ></button>
      </div>
    </div>
  );
};

export default Home;

これで、先ほどまで出ていたエラーが解消されていると思います。実際に、動きを確認してみましょう。問題なく動いてますね。
ここで、カスタムフックのポイントです。カスタムフックの関数名が重要だと途中記載しましたが、カスタムフックの関数名は必ずuse~と先頭にuseをつける必要があります。今回は、カスタムフックの名前をuseTextColorとしましたね。もし、このようにしないとカスタムフックと認識されずエラーが出てしまいます。また、useStateと違い左辺が以下のようになっていることにも注意です。(今回、戻り値を{}で返しているため。)

const { color, setColor } = useSetColor();

配列で返した際は[]で囲みますが、カスタムフックなのか分かりづらく、型の定義を再度する必要があり非常に使い勝手が悪いです。カスタムを使用する際は{}で変数や関数を返しましょう。同様に、背景色の色を管理しているuseStateもカスタムフックにします。ここで、できる方は、ご自身でやってみましょう。

ちなみに、コードは以下のようになります。

src/hooks/bgSetColor.ts
import { useState } from "react";

export default function useBgColor() {
  const [bgColor, setBgColor] = useState<"yellow" | "black">("yellow");

  return { bgColor, setBgColor };
}
src/pages/index.tsx
import type { NextPage } from "next";
import useBgColor from "../hooks/bgSetColor";
import useTextColor from "../hooks/textSetColor";

const Home: NextPage = () => {
  const { color, setColor } = useTextColor();
  const { bgColor, setBgColor } = useBgColor();

  return (
    <div
      style={{
        color: color,
        background: bgColor,
      }}
    >
      <h1>Hello</h1>
      <div>
        <h2>テキストボタン</h2>
        <button
          onClick={() => {
            setColor("red");
          }}
        ></button>
        <button
          onClick={() => {
            setColor("blue");
          }}
        ></button>
      </div>
      <div>
        <h2>背景ボタン</h2>
        <button
          onClick={() => {
            setBgColor("yellow");
          }}
        ></button>
        <button
          onClick={() => {
            setBgColor("black");
          }}
        ></button>
      </div>
    </div>
  );
};

export default Home;

以上で終了です。実際に動かしてみましょう。問題なく動作しますね。今回は、二つに分割しましたが、一つにまとめることも問題なく可能です。
そちらは、是非チャレンジしてみましょう!きっと力になるはずです!

最後に

今回は、カスタムフックを紹介させて頂きました。まだまだ学ぶことが多く大変ですが、なんとか頑張っています。まだまだ、学ぶことが多くあるためこれからも記事は自分の知識として残し、共有できるように頑張ろうと思います。何か質問や指摘等があれば遠慮なく、よろしくお願いします。では、また次回の記事で!

Discussion