最初はEndPointごとにAPIアクセス処理を作成しようとしていた
サーバ側のAPIでデータを取得しようとするときに、API毎にAPIアクセス処理を作成しようとしていました。下記のような感じです。
下記の例は、News情報をhttps://example.com/api/news
に取得し一覧に表示します。もしエラーが出たらerror
ページに遷移させるというものです。
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>
);
};
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();
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
)を使ってもらって、呼び出し元で定義するように修正しています。
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はこう変わりました。
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>
);
};