カスタムフックを作ってみよう
投稿日: 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というのが関数名前(=カスタムフックの名前)です。
この中に、
useStateuseStateuseEffectの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を使用できません。
※ カスタムフックの中で、別のカスタムフックを呼び出して使用することも可能です。
データ取得以外にも、いろんなところで処理を共通化できるので、
ぜひ活用してみてください!
転職段階でカスタムフックを使いこなせていないといけないかというと、全然いらないですが、
実務ではめちゃくちゃ使うので、慣れておくとより良いくらいのイメージです。