Reactを学習しているとuseState, useEffectなどのReact Hookの他にカスタムフック(Custom Hook)という言葉を聞く機会があると思います。カスタムフックがどのようなものかわからないという人向けにシンプルな例を使って動作確認を行いながら説明を行っています。

カスタムフックについてはReactのドキュメントには”A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.”(カスタムフックは名前がuseで始まり他のHookをcallするJavaScript関数です)と記載されているのでその点にも注目して説明を行います。

カスタムフックとは

カスタムフックは複数のコンポーネントの中に存在する共通の処理を取り出して作成した関数です。汎用的なカスタムフックを作成することができれば1つのアプリケーション内で再利用するだけではなくReact, Next.js, Remixを問わず別のアプリケーションでも再利用することが可能になります。

カスタムフックには汎用的なカスタムフックを集めたRooksといったサイトもあります。自作しなくても他の人が作成したカスタムフックを利用することもが可能です。本書でもシンプルな例を利用してカスタムフックについての説明を行いますがRooksにあるカスタムフックもソースコードを読めばすぐに理解できるものもたくさんあるのでカスタムフックの作り方に迷った時にはぜひ参考にしてみてください。

User, Postコンポーネントの作成

自作のカスタムフックを作成する前に最初はカスタムフックを作成するための元となるコンポーネントが必要となります。JSONPlaceHolderからデータを取得し、取得したデータを表示するコンポーネントUser.js, Post.jsを作成します。

JSONPlaceHolderは提供されているURLにアクセスするとダミーのJSONデータを戻す無料のサービスです。

User.jsファイルではJSONPlaceholderからユーザ情報を取得してユーザ一覧を表示しています。


import { useState, useEffect } from 'react';

const User = () => {
  const [users, setUsers] = useState([]);
  useEffect(() => {
    const fetchUser = async () => {
      const response = await fetch(
        'https://jsonplaceholder.typicode.com/users'
      );
      const users = await response.json();
      setUsers(users);
    };
    fetchUser();
  }, []);
  return (
    <div>
      <h1>ユーザ一覧</h1>
      <ul>
        {users.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default User;

Post.jsファイルではJSONPlaceholderから記事情報を取得して記事一覧を表示しています。


import { useState, useEffect } from 'react';

const Post = () => {
  const [posts, setPosts] = useState([]);
  useEffect(() => {
    const fetchPost = async () => {
      const response = await fetch(
        'https://jsonplaceholder.typicode.com/posts'
      );
      const posts = await response.json();
      setPosts(posts);
    };
    fetchPost();
  }, []);
  return (
    <div>
      <h1>記事一覧</h1>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default Post;

作成したコンポーネントをimportして表示します。


import User from './components/User';
import Post from './components/Post';
function App() {
  return (
    <div style={{ margin: '1em' }}>
      <User />
      <Post />
    </div>
  );
}

export default App;

ブラウザで確認すると以下の画面が表示されます。

ユーザ一覧と記事一覧
ユーザ一覧と記事一覧

User.jsとPost.jsを比較するとコードの内容はほとんど同じであることがわかるかと思います。データを取得する処理についてはURLと変数の名前が異なる程度の違いです。この2つのコンポーネントを見て共通する部分はどのあたりか答えられない人はいないと思います。皆さんが今思い浮かべている共通する処理を使ってカスタムフックを作成します。

カスタムフックの作成

srcフォルダの下にhooksフォルダを作成して、useFetchData.jsファイルを作成します。Reactのドキュメント通り名前の先頭にはuseを使います。User.jsとPost.jsで共通化できる部分をuseFetchData関数として取り出すと下記のように記述することができます。User.jsとPost.jsではアクセスするURLが異なるのでURLは引数として外側から渡します。


import { useState, useEffect } from 'react';

const useFetchData = (url) => {
  const [data, setData] = useState([]);
  useEffect(() => {
    const fetchPost = async () => {
      const response = await fetch(url);
      const data = await response.json();
      setData(data);
    };
    fetchPost();
  }, [url]);
  return { data };
};

export default useFetchData;

useFetchData.jsでは冒頭に説明した”A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.”の説明通り、名前はuseFetchDataでuseState, useEffectをcallしています。

カスタムフックの利用

カスタムフックのuseFetchDataが作成できたらUser.js, Post.jsでimportを行いuseFetchDataを利用します。

データを取得する部分についての処理はすべてuseFetchDataに記述されているのでどちらのコードも最初に比べてすっきりします。


import useFetchData from '../hooks/useFetchData';

const User = () => {
  const { data } = useFetchData('https://jsonplaceholder.typicode.com/users');

  return (
    <div>
      <h1>ユーザ一覧</h1>
      <ul>
        {data.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default User;

import useFetchData from '../hooks/useFetchData';

const Post = () => {
  const { data } = useFetchData('https://jsonplaceholder.typicode.com/posts');
  return (
    <div>
      <h1>記事一覧</h1>
      <ul>
        {data.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
};

export default Post;

改めて冒頭に説明した”カスタムフックは複数のコンポーネントの中に存在する共通の処理を取り出して作成する関数です。”が最初はわかりにくかった人も動作確認を行った後は理解できたのではないでしょうか。もう一つカスタムフックの例があればもっと理解できるという人であればRooksのhttps://github.com/imbhargav5/rooks/blob/main/src/hooks/useCounter.tsがおすすめです。プログラミングを学びはじめには必ずといっていいほど出てくるカウンターをカスタムフックとして作成しています。TypeScriptで記述されていますが、TypeScriptの知識がなくても理解できると思います。

今後はカスタムフックに出会ったとしてもカスタムフックがどのようなものか理解できているので対応できるはずです。