LoginSignup
45
51

More than 1 year has passed since last update.

【React】知っておくと便利なカスタムフック作成のコツ

Posted at

Reactでプロジェクトをつくる際、コンポーネント間で共通部分を一つにまとめることを意識しています。
表示部分に関してはできている(つもり)のですが、処理部分についてはレビューでも何回か指摘されるなど、あまりできていませんでした。
カスタムフックを利用すればいいことはわかっていたのですが、そもそもの作り方や使い方を理解できていなかったことが原因です。

ただ、カスタムフックをいくつかつくってみると作成にはコツがあることがわかりましたので、記事にまとめることにしました。
処理の共通化について悩んでいる方の参考になればと思います。

カスタムフックとは

カスタムフック(Custom Hooks)は、コンポーネント間で共通な処理(ロジック)を1つにまとめた再利用可能な関数です。

例えば以下のように、AppとAnalyticsという2つコンポーネントで"動画の取得"や"動画の選択"といった処理が共通である場合、各処理のカスタムフックを作成することでどちらのコンポーネントでも同じ処理を使えるようになります。
スクリーンショット 2021-05-16 9.57.39.png

カスタムフック作成のコツ

作成のコツとして”作成時に意識すること”と”作成のプロセス”についてまとめました。

作成時に意識すること

  • カスタムフックのコードはフックを利用するコンポーネントとは分離する
  • カスタムフックの中ではReact Hook(use~)を少なくとも1つ以上使う
  • 1つのカスタムフックは1つの目的だけをもつ

特に最後の"1つのカスタムフックは1つの目的だけをもつ"については、SOLIDの単一責任の原則に近い考え方になります。
一人のアクターが使うようにカスタムフックが設計されていないと、気づかないうちに別のアクターによって中身が修正されてしまったり、同じフックを2人以上のアクターが修正してしまうことでコンフリクトが起こってしまう可能性がでてきてしまいます。

作成のプロセス

  1. 処理部分を一行ずつ確認し目的ごとに分別する
  2. Inputを抽出する
  3. Outputを抽出する
  4. Inputを引数、Outputを戻り値としてコードをカスタムフックに抽出する

カスタムフックの作成例

YouTubeのAPIから動画を5件取得して、選択した動画をメインに表示するような画面を考えます。
ちなみにデフォルト表示は"YAMADA KATSUMI"の検索結果です。
スクリーンショット 2021-05-16 10.57.29.png

動画の取得処理に関するカスタムフックuseVideosを作成していきます。
もともとのコードは以下のようになっています。

App.js
const App = () => {
  const [videos, setVideos] = useState([]);
  const [selectedVideo, setSelectedVideo] = useState(null);

  useEffect(() => {
    onTermSubmit('YAMADA KATSUMI');
  }, []);

  const onTermSubmit = async (term) => {
    const response = await youtube.get('/search', {
      params: {
        q: term,
      },
    });

    setVideos(response.data.items);
    setSelectedVideo(response.data.items[0]);
  };

  return (
    <div className="ui container">
      <SearchBar onFormSubmit={onTermSubmit} />
      <div className="ui grid">
        <div className="ui row">
          <div className="eleven wide column">
            <VideoDetail video={selectedVideo} />
          </div>
          <div className="five wide column">
            <VideoList onVideoSelect={setSelectedVideo} videos={videos} />
          </div>
        </div>
      </div>
    </div>
  );
};

処理部分を一行ずつ確認し目的ごとに分別する

動画の取得処理を"Video"、動画の選択処理を"Selection"として該当コードを分別します。
スクリーンショット 2021-05-16 11.16.29.png

Inputを抽出する

カスタムフックを複数のコンポーネントで使えるように、"Video"の処理でInputになる箇所を抽出します。
今回のケースだと"YAMADA KATSUMI"の部分がInputにあたります。
これでカスタムフックを使用するコンポーネントごとにデフォルトの検索条件を変えることができるようになります。
スクリーンショット 2021-05-16 11.24.13.png

Outputを抽出する

コンポーネントで使用する箇所をOutputとして抽出します。
今回のケースでは、videosonTermSubmitが該当します。
スクリーンショット 2021-05-16 11.30.19.png

Inputを引数、Outputを戻り値としてコードをカスタムフックに抽出する

抽出したInputとOutputをもとに、カスタムフックuseVideosを作成します。
Inputにあたる部分をdefaultSearchTermとし、Outputにあたる部分をreturn [videos, search]としています。

useVideos.js
import { useState, useEffect } from 'react';
import youtube from '../apis/youtube';

const useVideos = (defaultSearchTerm) => {
  const [videos, setVideos] = useState([]);

  useEffect(() => {
    search(defaultSearchTerm);
  }, [defaultSearchTerm]);

  const search = async (term) => {
    const response = await youtube.get('/search', {
      params: {
        q: term,
      },
    });

    setVideos(response.data.items);
  };
  return [videos, search];
};

export default useVideos;

作成したカスタムフックuseVideosApp.jsconst [videos, search] = useVideos('YAMADA KATSUMI')のように読み込みます。
処理が一つにまとまったことでコードも読みやすくなりました。

App.js
const App = () => {
  const [selectedVideo, setSelectedVideo] = useState(null);
  const [videos, search] = useVideos('YAMADA KATSUMI');

  useEffect(() => {
    setSelectedVideo(videos[0]);
  }, [videos]);

  return (
    <div className="ui container">
      <SearchBar onFormSubmit={search} />
      <div className="ui grid">
        <div className="ui row">
          <div className="eleven wide column">
            <VideoDetail video={selectedVideo} />
          </div>
          <div className="five wide column">
            <VideoList onVideoSelect={setSelectedVideo} videos={videos} />
          </div>
        </div>
      </div>
    </div>
  );
};

参考資料

45
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
51