React axios インターセプターでフックを使えるようにする
Vite + React + TypeScript 環境で axios を使用しています。
axiosのインタセプターでフックを使おうとしたのですが、その方法がわからずちょっと迷ったので、解決した方法を記事にまとめました。
axiosのバージョン: v0.25.0
axiosで、デフォルトConfig と インタセプター基本的な設定方法
axios公式サイト:デフォルトConfig
axios公式サイト:インタセプター
まずは公式サイトに従って、基本的な方法でデフォルトConfig と インタセプターを作成してみます。
axios 拡張用のファイルを作成します。
ファイル名はなんでもいいですが、axiosClient.ts というファイル名で作成しました。
▶ すべての HTTP リクエストに共通するデフォルト config を指定する
すべての HTTP リクエストに共通するデフォルトの config を指定するには、axios.create メソッドの引数に config 値を指定します。
export const axiosClient = axios.create({
baseURL: 'https://xxxxx.xxx.com',
timeout: 3000,
headers: {
'Content-Type': 'application/json'
}
})
▶ すべてのリクエストに割り込み処理を入れる
先ほど axios.create メソッドで作成した axios インスタンスに、リクエストが送信される前に割り込み処理(インターセプター)を追加します。
リクエストインターセプターではリクエストヘッダーにアクセストークンを設定したりといった用途に使われます。
axiosClient.interceptors.request.use((config: AxiosRequestConfig) => {
if (config.headers !== undefined) {
// ヘッダにアクセストークンを埋める
// const accessToken = getAccessToken()
// if (accessToken) {
// config.headers.Authorization = `Bearer ${accessToken}`
// }
}
return config
})
▶ すべてのレスポンスに割り込み処理を入れる
リクエストのインターセプターと同様、axios.create メソッドで作成した axios インスタンスに、レスポンスへの割り込み処理(インターセプター)を追加します。
ログを残したり、エラーを調整したりといった用途に使われます。
axiosClient.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
switch (error.response?.status) {
case 401:
// なにかする
break
default:
break
}
return Promise.reject(error)
}
)
▶ 全部まとめると
import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
const BaseUrl = 'https://xxxxx.xxx.com'
/**
* デフォルト config の設定
*/
export const axiosClient = axios.create({
baseURL: BaseUrl,
timeout: 3000,
headers: {
'Content-Type': 'application/json'
}
})
/**
* リクエスト インターセプター
*/
axiosClient.interceptors.request.use((config: AxiosRequestConfig) => {
if (config.headers !== undefined) {
// --ヘッダにアクセストークンを埋める
// const accessToken = getAccessToken()
// if (accessToken) {
// config.headers.Authorization = `Bearer ${accessToken}`
// }
}
return config
})
/**
* レスポンス インターセプター
*/
axiosClient.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
(error: AxiosError) => {
switch (error.response?.status) {
case 401:
// なにかする
break
default:
break
}
return Promise.reject(error)
}
)
使用するときは、axios.create メソッドで作成した axios インスタンスからリクエストを送信します。
+ import { axiosClient } from '@/_lib/axiosConfig'
export type Sample = {
id: number
name: string
}
export function useSampleApi() {
const GetAll = async (): Promise<Sample[]> => {
try {
+ const response = await axiosClient.get(`/api/Sample/GetAll`)
return response.data
} catch (error: unknown) {
throw error
}
}
}
フックをつかえるようにする
先ほど作成した axiosClient.ts は関数コンポーネントではないので、このままではフックを使えません。
関数コンポーネントに変更して、フックを使えるようにします。
useEffect で インターセプターを設定し、コンポーネントのアンマウント時にインターセプターを解除するクリーンアップ関数を作成します。
2022/05/12 prop を書き忘れていたので追記しました。
import React from 'react'
import axios, { AxiosRequestConfig } from 'axios'
import { useNavigate } from 'react-router-dom'
const BaseUrl = 'https://xxxxx.xxx.com'
// デフォルト config の設定
export const axiosClient = axios.create({
baseURL: BaseUrl,
timeout: 5000,
headers: {
'Content-Type': 'application/json'
}
})
export function AxiosClientProvider({children}: {children: React.ReactElement}) {
// 関数コンポーネントなのでフックが使える
+ const navigate = useNavigate()
React.useEffect(() => {
// リクエスト インターセプター
const requestInterceptors = axiosClient.interceptors.request.use((config: AxiosRequestConfig) => {
if (config.headers !== undefined) {
// const accessToken = getAccessToken()
// if (accessToken) {
// config.headers.Authorization = `Bearer ${accessToken}`
// }
}
return config
})
// レスポンス インターセプター
const responseInterceptor = axiosClient.interceptors.response.use(
(response) => {
return response
},
(error) => {
switch (error.response?.status) {
case 401:
// なにかする
break
default:
break
}
return Promise.reject(error)
}
)
// クリーンアップ
return () => {
axiosClient.interceptors.request.eject(requestInterceptors)
axiosClient.interceptors.response.eject(responseInterceptor)
}
}, [])
return (<>{children}</>)
}
使い方はApp.tsxなどで、作成した AxiosClientProvider でコンポーネントをラップします。
import React from 'react'
import { ErrorBoundary } from 'react-error-boundary'
import { BrowserRouter } from 'react-router-dom'
import { ThemeProvider, createTheme } from '@mui/material/styles'
import { appTheme } from '@/_styles/appTheme'
import { Global } from '@emotion/react'
import sanitize from '@/assets/sanitize.css'
import { ErrorFallback } from '@/pages'
import { AxiosClientProvider } from '@/_lib/AxiosClientProvider'
import { SampleComponent } from '@/pages'
const theme = createTheme(appTheme)
export function App() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<ErrorBoundary FallbackComponent={ErrorFallback}>
<BrowserRouter>
+ <AxiosClientProvider>
<ThemeProvider theme={theme}>
<Global styles={sanitize} />
<SampleComponent />
</ThemeProvider>
+ </AxiosClientProvider>
</BrowserRouter>
</ErrorBoundary>
</React.Suspense>
)
}
リクエストを送信する際は、AxiosClientProvider.tsx で作成した axios インスタンスを使用します。
import { axiosClient } from '@/_lib/AxiosClientProvider'
export type Sample = {
id: number
name: string
}
export function useSampleApi() {
const GetAll = async (): Promise<Sample[]> => {
try {
const response = await axiosClient.get<Sample[]>(`/api/Sample/GetAll`)
return response.data
} catch (error: unknown) {
throw error
}
}
return { GetAll }
}
以上、axiosインターセプターでフックを使えるようになりました。
Discussion