質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

1回答

1665閲覧

【React v18】Recoilでグローバルステートに保存した値を呼び出してaxiosのinterceptors.requestを使ってHTTPリクエストのヘッダーに埋め込みたい

Utsubo

総合スコア15

MacOS(OSX)

MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

TypeScript

TypeScriptは、マイクロソフトによって開発された フリーでオープンソースのプログラミング言語です。 TypeScriptは、JavaScriptの構文の拡張であるので、既存の JavaScriptのコードにわずかな修正を加えれば動作します。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

1グッド

0クリップ

投稿2023/02/05 13:26

実現したいこと

access tokenがグローバルステートに保存されている場合に、axiosのinterceptors.requestを使ってHTTPリクエストのヘッダーにaccess tokenを埋め込みたい。
現状では、useRecoilValueを使って保存されたaccess tokenの値を呼び出しそれをinterceptors.requestを使ってヘッダーに埋め込もうとしているが、関数コンポーネント、カスタムフック外でフックを使ってしまっているためエラーが発生してしまっている。

前提

バックエンド側をRails、フロント側をReactを使ってSPA構成でアプリを作成しています。
ログインフォーム送信後にaccess tokenがレスポンスが返ってくるので、その値をRecoilを使ってグローバルステートに保存できているところまでは確認できました。
Reactはbullet-proofというOSSを参考にしているためフォルダ構成はbullet-proofとほぼ同じです。
https://github.com/alan2207/bulletproof-react

発生している問題・エラーメッセージ

Uncaught (in promise) Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.

関数コンポーネント、カスタムフック外でuseRecoilValueを使ってしまっているのでエラー発生しているため、想定のエラーです。

該当のソースコード

HTTPリクエストヘッダーにaccess token情報を埋め込むために9-12行目を追加したところでエラー発生しています。

client/src/lib/apiClient.ts

js

1import applyCaseMiddleware from 'axios-case-converter'; 2import Axios, { AxiosRequestConfig } from 'axios'; 3 4import { API_URL } from '@/config'; 5import { useRecoilValue } from 'recoil'; 6import { authState } from '@/global_states/auth/authState'; 7 8function authRequestInterceptor(config: AxiosRequestConfig) { 9 const token = useRecoilValue(authState); 10 if (token) { 11 config.headers.authorization = `${token}`; 12 } 13 config.headers.Accept = 'application/json'; 14 config.headers = { 'X-Requested-With': 'XMLHttpRequest' }; 15 return config; 16} 17 18const options = { 19 ignoreHeaders: true 20} 21 22export const ApiClient = applyCaseMiddleware(Axios.create({ 23 withCredentials: true, 24 baseURL: API_URL, 25}), options); 26 27ApiClient.interceptors.request.use(authRequestInterceptor); 28ApiClient.interceptors.response.use( 29 (response) => response.data, 30);

試したこと

  • 初めは関数コンポーネント内、カスタムフック内以外でフックが使えないということを知らず、関数内でフックを使って保存したaccess tokenの値を呼び出し、ヘッダーに埋め込もうとしましたがエラーが発生しました
  • 「recoil グローバルステート axiosのヘッダー」やこれに似た検索をかけてみましたが問題解決に関連しそうな情報は出てきませんでした

フロントエンドの経験がかなり浅く、他に何を試せばいいか分からない状態が続いております。
お力添えいただければ幸いです。よろしくお願いいたします。

補足情報(FW/ツールのバージョンなど)

OS: macOS Ventura 13.2 (intel processor)
Docker: Docker version 20.10.21
docker-compose: Docker Compose version v2.13.0
React: 18.2.0
TypeScript: 4.9.4
axios: 1.2.1
recoil: 0.7.6
Ruby: 3.1.2
Rails: 7.0.4

y-temp4👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

カスタムフックを作成するのはどうでしょうか?

client/src/hooks/useAuthInterceptor.ts

ts

1import { AxiosRequestConfig } from 'axios'; 2import { useRecoilValue } from 'recoil'; 3import { authState } from '@/global_states/auth/authState'; 4 5export const useAuthInterceptor = () => { 6 const token = useRecoilValue(authState); 7 8 const authRequestInterceptor = (config: AxiosRequestConfig) => { 9 if (token) { 10 config.headers.authorization = `${token}`; 11 } 12 config.headers.Accept = 'application/json'; 13 config.headers['X-Requested-With'] = 'XMLHttpRequest'; 14 return config; 15 }; 16 17 return authRequestInterceptor; 18};

client/src/components/ApiInterceptorProvider.tsx

tsx

1import { FC, useEffect } from 'react'; 2import { ApiClient } from '@/lib/apiClient'; 3import { useAuthInterceptor } from '@/hooks/useAuthInterceptor'; 4 5export const ApiInterceptorProvider: FC = ({ children }) => { 6 const authInterceptor = useAuthInterceptor(); 7 8 useEffect(() => { 9 const interceptorId = ApiClient.interceptors.request.use(authInterceptor); 10 11 return () => { 12 ApiClient.interceptors.request.eject(interceptorId); 13 }; 14 }, [authInterceptor]); 15 16 return <>{children}</>; 17};

tsx

1import React from 'react'; 2import { ApiInterceptorProvider } from '@/components/ApiInterceptorProvider'; 3import { YourOtherComponents } from '@/components/YourOtherComponents'; 4 5function App() { 6 return ( 7 <ApiInterceptorProvider> 8 <YourOtherComponents /> 9 </ApiInterceptorProvider> 10 ); 11} 12 13export default App;

投稿2023/03/31 07:51

y-temp4

総合スコア67

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Utsubo

2023/05/08 01:19

y-temp4さん、返信が遅くなってしまい申し訳ございません。 ご回答いただきありがとうございます、実装方法を参考にさせていただきました。 現在、別のアプリを作成しており認証認可方式が変わってしまったため、手元のアプリには実装できないのが残念ですが、教えていただいた実装方法は今後のための勉強になりました。 改めて、ご回答いただきありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問