Open1

カスタムフック

どるあがどるあが

カスタムフックでロジックを分ける

やりたいこと

  • ユーザー情報を取得して整形して返す
  • ローディング中「loading...」と表示
  • データ取得エラーの時に「データの取得に失敗しました」と表示
  • ビジネスロジックをカスタムフックで分ける

完成画像

App.tsx
export const App = () => {
  // ロジックはカスタムフックに分ける
  const { getUsers, userProfiles, loading, error } = useAllUsers();
  const onClickFetchUser = () => getUsers();

  return (
    <div className="App">
      <button onClick={onClickFetchUser}>データ取得</button>
      <br />
      {error ? (
        <p style={{ color: "red" }}>データの取得に失敗しました</p>
      ) : loading ? (
        <p>Loading...</p>
      ) : (
        <>
          {userProfiles.map((user) => (
            <UserCard key={user.id} user={user} />
          ))}
        </>
      )}
    </div>
  );
};

ちょっとずつ見ていく

App.tsx
{error ? (
        <p style={{ color: "red" }}>データの取得に失敗しました</p>
      ) : loading ? (
        <p>Loading...</p>
      ) :

error,loadingの値を見て、trueならばメッセージを出す。
これらはHooksの中で状態(state)として管理している。

App.tsx
  // ロジックはカスタムフックに分ける
  const { getUsers, userProfiles, loading, error } = useAllUsers();

ロジックをカスタムフックで開発

useAllUsers.ts
// 全ユーザーの一覧を取得するHooks

import axios from "axios";
import { useState } from "react";
import { UserProfile } from "../types/userProfile";
import { User } from "../types/api/user";

export const useAllUsers = () => {
  const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);

  const getUsers = () => {
    setLoading(true);
    setError(false);
    axios
      .get<Array<User>>("https://jsonplaceholder.typicode.com/users")
      .then((res) => {
        const data = res.data.map((user) => ({
          id: user.id,
          name: `${user.name}(${user.username})`,
          email: user.email,
          address: `${user.address.city}${user.address.suite}${user.address.street}`
        }));
        setUserProfiles(data);
      })
      .catch(() => {
        setError(true);
      })
      .finally(() => {
        setLoading(false);
      });
  };

  return { getUsers, userProfiles, loading, error };
};

ユーザー情報、ローディング情報、エラー情報をstateとして持つ

  const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(false);

axiosでデータ取得。型はUserであり配列で取得する

    axios
      .get<Array<User>>("https://jsonplaceholder.typicode.com/users")

ここがよくわからん。。。
なぜこれでdataに配列を取得できるんだ。mapでの処理はどうなっているのかさっぱりわからない。
最終的にsetUserProfiles(data)でdataに修正したオブジェクトを渡す

.then((res) => {
        const data = res.data.map((user) => ({
          id: user.id,
          name: `${user.name}(${user.username})`,
          email: user.email,
          address: `${user.address.city}${user.address.suite}${user.address.street}`
        }));
        setUserProfiles(data);
      })

.catchはエラーが起きた場合に実行される。errorにtrueを設定し、App.tsxでの表示が変わる

      .catch(() => {
        setError(true);
      })

.finallyでエラーの有り無しに関わらず実行される。ここではloadingをfalseにする。つまりボタンを押してから表示が終わるまでtrueになっていることになる。

      .finally(() => {
        setLoading(false);
      });

return でstateや関数を返す。

return { getUsers, userProfiles, loading, error };

使用するときは普通のHooksのように使える。

  // ロジックはカスタムフックに分ける
  const { getUsers, userProfiles, loading, error } = useAllUsers();
  const onClickFetchUser = () => getUsers();