カスタムフックを作ってみよう
投稿日: 2024年10月04日
Reactには、useState
やuseEffect
などの基本的なフックが組み込まれていますが、
独自の機能を内包したフック(カスタムフック)を作ることができます。
カスタムフックを使うことで、複数のコンポーネントでロジックを使い回しできるようになります。
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つの処理をまとめます。
path: string
で、エンドポイントを受け取ります。記事一覧以外のあらゆるエンドポイントにも対応できるようにするため、引数で受け取れるようにします。useEffect
の中で、受け取ったpath
に対してfetch
でAPIリクエストを行いデータを取得し、setData
でstateに格納します。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
で始まらない関数はフックとして認識されません。
よくこの疑問を持たれる方がいますが、
カスタムフックの特徴は、「useState
, useEffect
などのReactフックを使用できる」という点です。
通常のTS(JS)関数では、内部でuseState
やuseEffect
を使用できません。
※ カスタムフックの中で、別のカスタムフックを呼び出して使用することも可能です。
データ取得以外にも、いろんなところで処理を共通化できるので、
ぜひ活用してみてください!
転職段階でカスタムフックを使いこなせていないといけないかというと、全然いらないですが、
実務ではめちゃくちゃ使うので、慣れておくとより良いくらいのイメージです。