概要
axiosでエラーが発生した時にエラー画面に遷移させるとか、axiosのインターセプターでuseNavigateを使いたい時ってありますよね。色々と試行錯誤して、比較的簡単に実現できる方法が分かりましたので、同じように困っている方に向けてマイベストプラクティスとして共有させていただきたいと思います。
やり方
axiosのレスポンスインターセプターをコンポーネント化し、これをReact Routerで有効にします。
axios.ts
axiosのインスタンスを作成し、必要に応じて共通の設定を入れてexportします。
※ここではextendsフォルダ配下に作ったとします。
const axiosClient = axios.create({
baseURL: process.env.REACT_APP_API_BASE_URL,
timeout: 30000,
})
export default axiosClient
AxiosResponseHandler.tsx
axiosのレスポンスインターセプター用にコンポーネントを作成します。(※コンポーネントなのでファイルの拡張子は.tsxにする必要があります)
useEffect()を使ってaxiosのレスポンスインターセプターを設定します。コンポーネントになっているのでuseNavigateなどのReact Hooksを自由に使うことができます。
useEffectの処理は非同期で行われるのでInterceptorがうまく設定できないことがあるためisSetというState変数を作って確実に設定されるように制御します。
returnは引数のchildrenをそのまま返すようにします。
import axios from 'extends/axios'
type Props = {
children: React.ReactNode
}
export const AxiosResponseHandler = ({ children }: Props) => {
const navigate = useNavigate()
const [isSet, setIsSet] = useState(false)
useEffect(() => {
// リクエストインターセプター
const requestInterceptors = axios.interceptors.request.use((config) => {
// 例:共通リクエストヘッダーを付ける
config.headers['XXXXXXX'] = XXXXX
return config
})
// レスポンスインターセプター
const responseInterceptors = axios.interceptors.response.use(
(response) => response,
async (error) => {
switch (error.response?.status) {
(省略)
case 500:
navigate('error/500')
break
default:
return await Promise.reject(error)
}
},
)
}, [])
setIsSet(true)
// クリーンアップ
return () => {
axios.interceptors.request.eject(requestInterceptors)
axios.interceptors.response.eject(responseInterceptors)
}
return <>{isSet && children}</>
}
Router.tsx
BrowserRouterとRoutesの間にAxiosResponseHandlerを入れます。これによりRoutes配下の画面でextends/axiosを使用すれば、AxiosResponseHandlerのaxiosレスポンスインターセプターが有効になります。
const Router = () => (
<BrowserRouter>
<AxiosResponseHandler>
<Routes>
<Route path="signIn" element={<SingIn />} />
(省略)
</Routes>
</AxiosResponseHandler>
</BrowserRouter>
)
export default Router
(参考) 直接axiosを使うのを禁止する
拡張したaxiosではなく、直接axiosを使ってしまうとインターセプターの処理を通らないので困ったことになります。これを防ぐためESLintを使って直接axiosをインポートするのを禁止します。
ESLintの導入方法についてはここでは触れませんので各自でググってください。
.eslintrc.json
{
"rules": {
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "axios",
"message": "Please import axios from 'extends/axios'."
}
]
}
]
}
}