17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Reactでカスタムフックを作ってAPIを共通化する方法

Last updated at Posted at 2020-08-25

最初はEndPointごとにAPIアクセス処理を作成しようとしていた

サーバ側のAPIでデータを取得しようとするときに、API毎にAPIアクセス処理を作成しようとしていました。下記のような感じです。

下記の例は、News情報をhttps://example.com/api/newsに取得し一覧に表示します。もしエラーが出たらerrorページに遷移させるというものです。

news.tsx
import { useEffect, useState } from "react";
import Router from "next/router";
import NewsApi from "newsApi";

const NewsPage = () => {
  const [list, setList] = useState<News[]>([]);
  useEffect(() => {
    const func = async () => {
      const res = await NewsApi.list();
      if (res.status !== 200) {
        Router.push("/error");
      } else {
        setList(res.data);
      }
    };
    func();
  }, []);
  return (
    <ul>
      {list &&
        list.map((n) => (
          <li key={n.newsID}>
            <p>{n.startTime}</p>
            <dl>
              <dt>{n.title}</dt>
              <dd>{n.description}</dd>
            </dl>
          </li>
        ))}
    </ul>
  );
};
newsApi.ts
import axios from "axios";
import News from "news";

class NewsApi {
  list = async () => {
    const res = await axios.get<News[]>("https://example.com/api/news", {
      headers: getAuthHeader(),
    }).catch((err) => {
        return err.response;
      });
    return res;
  };
}

export default new NewsApi();
news.ts
type News = {
  newsID: number;
  title: string;
  description: string;
  linkURL: string;
  startTime: Date;
  endTime: Date;
};

export default News;

カスタムフックで共通化する

私の作っているAPIの共通仕様として、ヘッダーに認証用のgetAuthHeader()を付与するというものがありました。
また、エラーページに遷移させるというのも各API共通のものとなります。
そうすると、毎回書くのは手間だったり、間違いが起こったりする可能性がありますので、API毎にAPIアクセス処理を用意するのではなく、共通化したものを用意した方がいいのではないかと。

カスタムフックと用いて実装したいと思います。

カスタムフックとは、名前が ”use” で始まり、ほかのフックを呼び出せる JavaScript の関数のことです。
https://ja.reactjs.org/docs/hooks-custom.html

ということで、useApi.tsを新たに作成しました。
axiosのインスタンス(httpClient)を使ってもらって、呼び出し元で定義するように修正しています。

useApi.ts
import axios, { AxiosResponse } from "axios";
import Router from "next/router";
import { useState, useEffect } from "react";

export let httpClient = axios.create({
  headers: getAuthHeader(),
});

const useApi = <T>(
  path: string,
  axiosFunc: () => Promise<AxiosResponse<T>>,
  initialState: T,
  handleError: ((res) => void) | null = null
): T => {
  const [data, setData] = useState<T>(initialState);
  useEffect(() => {
    const func = async () => {
      const res = await axiosFunc().catch((err) => {
        return err.response;
      });
      if (res.status !== 200) {
        handleError ? handleError(res) : Router.push("/error");
      } else {
        setData(res.data);
      }
    };
    func();
  }, []);
  return data;
};

export default useApi;

news.tsxはこう変わりました。

news.tsx
import News from "news";
import useApi, { httpClient } from "useApi";

const NewsPage = () => {
  const path = "https://example.com/api/news";
  const req = () => {
    return httpClient.get(path);
  };
  const list = useApi<News[]>(path, req, []);
  return (
    <ul>
      {list &&
        list.map((n) => (
          <li key={n.newsID}>
            <p>{n.startTime}</p>
            <dl>
              <dt>{n.title}</dt>
              <dd>{n.description}</dd>
            </dl>
          </li>
        ))}
    </ul>
  );
};

node.js-12.8.0 Typescript-3.9.7 next.js-9.4.4

17
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?