【React】使うとよりスッキリ!”カスタムフック”について

React

カスタムフックについて、こちらのUdemy講座での学習内容をまとめました!

https://www.udemy.com/course/react_stepup/learn/lecture/24823658#content

カスタムフックとは

名前の通り、use◯◯というネーミングをして、自分でhooksを作ることができる。

コンポーネントからロジックの部分を切り出すというのが主な目的であり、それにより使い回しができるようになる。

カスタムフックの使用例の実装のため、jsonplaceholderのデモデータを使用し、取得データを表示する機能を作成。

取得したデータを変換して表示する

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

function App() {

  const [userProfiles, setUserProfiles] = useState<Array<UserProfile>>([])

  const onClickFetchUser = () => {
    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)
    })

  }
  return (
    <div className="App">
      <button onClick={onClickFetchUser}>データ取得</button>
      {userProfiles.map((user) => (
        <UserCard user={user}/>
      ))}
    </div>
  );
}

export default App;

import { VFC } from "react"
import { UserProfile } from "../types/userProfile"

type Props = {
  user: UserProfile
}

export const UserCard: VFC<Props> = (props) => {
  const { user } = props;

  return (
    <div style={style}>
      <dl>
        <dt>名前</dt>
        <dd>{user.name}</dd>
        <dt>メール</dt>
        <dd>{user.email}</dd>
        <dt>住所</dt>
        <dd>{user.address}</dd>
      </dl>
    </div>
  )
}

ボタンクリックで関数onClickFetchUserが実行される。

jsonplaceholderのデモデータを、使用する形のデータid, name, email, addressに変換し、オブジェクトを作成し、それらを入れた配列をdataとして作成。

userProfilesをループしてUserCardをレンダリング

これにより、UserCardに設定したuserに値が入ることで、名前、メール、住所を取得できる。

ここで使用されているVFCとは

Function Componentの型定義のことでpropsの型を定義している。

データ取得中にローディングを表示する

ローディング中にローディング中であることを表現する機能を実装する。

方法としては、ローディングの情報を持ったstateを用意し、クリック時にローディングをtrueにし、処理が終わったらfalseにするといった処理にする。

errorがあった場合と無かった場合を三項演算子を使って設定する。

三項演算子の基本形
{error ? (A) : (B)}

errorならAを、そうでなければBを。

更に三項演算子を重ねる場合
{error ? (A) : Loading ? (B) : (C)}

errorならAを、LoadingならBを、そうでなければCを。

コードの全貌

import axios from 'axios';
import './App.css';
import { UserCard } from './components/UserCard';
import { User } from './types/api/user';
import { useState } from 'react'
import { UserProfile } from './types/userProfile';

function App() {

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

  const onClickFetchUser = () => {
    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 (
    <div className="App">
      <button onClick={onClickFetchUser}>データ取得</button>
        <br />
        {error ? (
          <p>データの取得に失敗しました</p>
        ) : loading ? (
          <p>Loading...</p>
        ) : (
          <>
            {userProfiles.map((user) => (
              <UserCard key={user.id} user={user}/>
            ))}
          </>
        )}
    </div>
  );
}

export default App;

どんな状態でも最終的にはfalseにしたいので、finallyを使ってstateを設定

.catchにはerrorの場合のstateを設定。

カスタムフックを作成する

いよいろカスタムフックを作成していきます。

hook名はuseAllUsersとし、名前を全件取得するhooksを作成する。

上記のonClickFetchUser関数の中身を丸々移換して、returnで関数を返す。

import { useState } from "react"
import { UserProfile } from "../types/userProfile"
import axios from "axios"
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 }
}

作成したカスタムフックをApp側で呼び出す

上記でreturnされた値を、useAllUsersで受け取る。

onClickFetchUser実行時にgetUsersが実行されるようにする。

import './App.css';
import { UserCard } from './components/UserCard';
import { useAllUsers } from './hooks/useAllUsers';

function App() {

  const { getUsers, userProfiles, loading, error } = useAllUsers();

  const onClickFetchUser = () => getUsers();

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

export default App;

完了!

コメント

タイトルとURLをコピーしました