カスタムフックを作ってみよう

カスタムフックを作ってみよう

投稿日: 2024年10月04日

Tips
要約
  • Reactのカスタムフックを使って、データ取得処理を共通化し、複数のコンポーネントで再利用できる。
  • useDataFetchというカスタムフックでは、APIからデータを取得し、ローディング状態を管理する機能を提供する。
  • カスタムフックの利用により、処理の重複を避け、記述ミスのリスクを減らすことができる。
音声で記事を再生

Reactには、useStateuseEffectなどの基本的なフックが組み込まれていますが、
独自の機能を内包したフック(カスタムフック)を作ることができます。

カスタムフックを使うことで、複数のコンポーネントでロジックを使い回しできるようになります。

例えば

ShiftBのカリキュラムでブログサイトを作る際、
いろんなページで、データ取得の処理を書くと思います。

  const [posts, setPosts] = useState<Post[]>([])
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const fetcher = async () => {
      const res = await fetch('api/posts')
      const { posts } = await res.json()
      setPosts(posts)
      setLoading(false)
    }

    fetcher()
  }, [])
  • データを格納するためにuseStateの定義
  • 読み込みステータスを管理するためにuseStateの定義
  • ページマウント時に処理させるためにuseEffect処理を追加

など、ページ毎にこれらを毎回書くのは結構面倒ですよね。

そんな時に、これらの処理群を1つのまとまりにして、共通化できる機能が「カスタムフック」です。

早速、上記の処理をまとめたカスタムフックを以下に書いてみます。

import { useEffect, useState } from 'react'

export const useDataFetch = <Data,>(path: string) => {
  const [data, setData] = useState<Data>()
  const [loading, setLoading] = useState(true)

  useEffect(() => {
    const fetcher = async () => {
      const res = await fetch(`api/${path}`)
      const { data } = await res.json()
      setData(data)
      setLoading(false)
    }

    fetcher()
  }, [path])

  return { data, loading }
}

useDataFetchというのが関数名前(=カスタムフックの名前)です。

この中に、

  • データを格納するためのuseState
  • 読み込みステータスを管理するためのuseState
  • ページマウント時に処理させるためのuseEffect

の3つの処理をまとめます。

  1. 引数のpath: stringで、エンドポイントを受け取ります。記事一覧以外のあらゆるエンドポイントにも対応できるようにするため、引数で受け取れるようにします。
  2. useEffectの中で、受け取ったpathに対してfetchでAPIリクエストを行いデータを取得し、setDataでstateに格納します。
  3. 最後に、return { data, loading }の部分で、データを格納しているdataと、ローデイング管理のstateのloadingをreturnします。

上記のカスタムフック(useDataFetch)は、ページコンポーネントから呼び出して使うことができます。

記事一覧ページの場合

import { useDataFetch } from './useHoge'

export default function Page() {
  const { data, loading } = useDataFetch<Post[]>('posts');

  if (!data || loading) return <div>loading...</div>

  return (
    <div className="">
      <div className={classes.container}>
        // ...

const { data, loading } = useDataFetch<Post[]>('posts');

の行が、作成したカスタムフックの呼び出しです。

const { data, loading }で、useDataFetch.ts側でreturnした値を受け取ることができます。

useDataFetch<Post[]>('posts');の引数の'post'がエンドポイントです。

この1行だけで、api/postに対してAPIリクエストをして受け取ったデータがdataに格納され、コンポーネント内で使用できます。

このuseDataFetchは、他のページでも使いまわすことができます。

記事詳細ページの場合

import { useDataFetch } from '@/app/useHoge'

export default function Page() {
  const { id } = useParams()
  const { data, loading } = useDataFetch<Post>(`posts/${id}`)

  if (!data || loading) return <div>loading...</div>

  return (
    <div className={classes.container}>
      // ...

管理者ページのカテゴリー一覧ページの場合

import { useDataFetch } from "@/app/useHoge";

export default function Page() {
  const { data, loading } = useDataFetch<Category[]>('admin/categories')

  if (!data || loading) return <div>loading...</div>

  return (
    <div className="">
      // ...

このように、コンポーネントに書く処理の量がグッと減らせますし、また、1箇所に処理がまとまっているので、同じ処理を繰り返し書くことによる記述ミスのリスクも減らすことができます。

カスタムフックを作る際のルール

カスタムフックの命名は必ず、Reactの組み込みフックと同じように「use」で始める必要があります。

これはReactのルールで、useで始まらない関数はフックとして認識されません。

通常のTS(JS)関数と何が違うの?

よくこの疑問を持たれる方がいますが、

カスタムフックの特徴は、「useState, useEffect などのReactフックを使用できる」という点です。

通常のTS(JS)関数では、内部でuseStateuseEffectを使用できません。

※ カスタムフックの中で、別のカスタムフックを呼び出して使用することも可能です。

公式ドキュメントの説明

データ取得以外にも、いろんなところで処理を共通化できるので、
ぜひ活用してみてください!

転職段階でカスタムフックを使いこなせていないといけないかというと、全然いらないですが、
実務ではめちゃくちゃ使うので、慣れておくとより良いくらいのイメージです。

シェア!

Threads
icon
ぶべ
Webの修行中 / 個人開発奮闘中 / ベンチプレス110kg / Reactの先生
Loading...
記事一覧に戻る
Threads
0