「モダンJavaScriptの基本から始めるReact実践の教科書」の感想・備忘録7

スポンサーリンク
「モダンJavaScriptの基本から始めるReact実践の教科書」の感想・備忘録6の続き

カスタムフック

カスタムフックとは

カスタムフックとは任意の処理をまとめた自作のフックのことで、ロジックをコンポーネントから分離したり、複数のコンポーネントからロジックを再利用したりすることが可能となる。

  • useStateやuseEffectのようにuseXxxと命名する。
  • src/hooksディレクトリに格納するのが一般的。
  • カスタムフックとして定義した関数は、Stateや関数をオブジェクトでまとめてreturnする。
    (戻り値は自由に決められるが、オブジェクトで返すのが一般的)

カスタムフックの必要性

例えば、以下のコンポーネントはhandleFetchUsersButtonClick関数でフラグの設定・データの取得・データの変換を行っているためコード量が増えてしまっている。
コンポーネントは与えられたpropsに基づいて画面の見た目を構築することを責務とし、複雑なロジック部分は分離すべきである。
また、他のコンポーネントでもユーザー取得APIを使う場合、ロジック部分が分離してあれば再利用することができる。

import React, { FC, useState } from 'react';

type User = {
  id: number,
  name: string,
  age: number
}

const Sample2: FC = () => {
  const [users, setUsers] = useState<User[]>([])
  const [isLoading, setIsLoading] = useState<boolean>(false)
  const [isError, setIsError] = useState<boolean>(false)
  
  const handleFetchUsersButtonClick = () => {
    setIsLoading(true)
    setIsError(false)
    fetch("/users.json")
      .then((res: Response) => res.json())
      .then((json: User[]) => {
        json.map((user: User)=> user.name = `${user.name}さん`)
        setUsers(json)
      })
      .catch(() => setIsError(true))
      .finally(() => setIsLoading(false))
  }
  return (
    <div>
      <h1>sample2</h1>
      <button onClick={handleFetchUsersButtonClick}>users.json取得</button>
      <p>{ isLoading && '読み込み中'}</p>
      <p>{ isError && 'エラー'}</p>
      { users.map((user: User)=> <p key={user.id}>{user.name}</p>)}
    </div>
  );
}

export default Sample2;

カスタムフックの実装

mkdir src/hooks;
touch src/hooks/useFetchUsers.tsx

import React, { useState } from 'react';
import type { User } from '../types/user';

export const useFetchUsers = () => {
  const [users, setUsers] = useState<User[]>([])
  const [isLoading, setIsLoading] = useState<boolean>(false)
  const [isError, setIsError] = useState<boolean>(false)
  
  const handleFetchUsersButtonClick = (): void => {
    setIsLoading(true)
    setIsError(false)
    fetch("/users.json")
      .then((res: Response) => res.json())
      .then((json: User[]) => {
        json.map((user: User)=> user.name = `${user.name}さん`)
        setUsers(json)
      })
      .catch(() => setIsError(true))
      .finally(() => setIsLoading(false))
  }
  return {users, isLoading, isError, handleFetchUsersButtonClick}
}

利用時

import React, { FC, useState } from 'react';
import type { User } from './types/user';
import { useFetchUsers } from './hooks/useFetchUsers';

const Sample2: FC = () => {
  const {users, isLoading, isError, handleFetchUsersButtonClick } = useFetchUsers()
  
  return (
    <div>
      <h1>sample2</h1>
      <button onClick={handleFetchUsersButtonClick}>users.json取得</button>
      <p>{ isLoading && '読み込み中'}</p>
      <p>{ isError && 'エラー'}</p>
      { users.map((user: User)=> <p key={user.id}>{user.name}</p>)}
    </div>
  );
}

export default Sample2

コメント