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();