LoginSignup
Utsubo
@Utsubo

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Recoilでグローバルステートに保存したアクセストークンをaxiosのinterceptors.requestを使ってHTTPリクエストのヘッダーに埋め込みたい

解決したいこと

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

この実装方法では実現が難しいのか、実装方法が間違っているかの判断もできない状態です。
解決方法またはこの実装方法は難しいなどのアドバイス・ご指摘をいただきたいです。よろしくお願いいたします。

前提

バックエンド側をRails、フロント側をReactを使ってSPA構成でアプリを作成しています。
ログインフォーム送信後にアクセストークンがレスポンスが返ってくるので、その値をRecoilを使ってグローバルステートに保存できているところまでは確認できました。
React側の構成はbulletproof-reactというOSSを参考にしているためディレクトリ構成はほぼ同じです。
https://github.com/alan2207/bulletproof-react
bulletproof-reactの場合はsrc/utils/storage.ts内で以下のように、local storageに保存されたアクセストークンをHTTPリクエストのヘッダにセットしていますが、私はRecoilを使ってグローバルステートに保存されたアクセストークンをHTTPリクエストのヘッダにセットしたいと考えております。
bulletproof-react/src/lib/axios.ts

import Axios, { AxiosRequestConfig } from 'axios';

import { API_URL } from '@/config';
import { useNotificationStore } from '@/stores/notifications';
import storage from '@/utils/storage';

function authRequestInterceptor(config: AxiosRequestConfig) {
  const token = storage.getToken();
  if (token) {
    config.headers.authorization = `${token}`;
  }
  config.headers.Accept = 'application/json';
  return config;
}

export const axios = Axios.create({
  baseURL: API_URL,
});

axios.interceptors.request.use(authRequestInterceptor);
axios.interceptors.response.use(
  (response) => {
    return response.data;
  },
  (error) => {
    const message = error.response?.data?.message || error.message;
    useNotificationStore.getState().addNotification({
      type: 'error',
      title: 'Error',
      message,
    });

    return Promise.reject(error);
  }
);

発生している問題・エラー

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.

該当するソースコード

HTTPリクエストヘッダーにアクセストークン情報を埋め込むために9-12行目を追加したところでエラー発生しています。
client/src/lib/apiClient.ts

import applyCaseMiddleware from 'axios-case-converter';
import Axios, { AxiosRequestConfig } from 'axios';

import { API_URL } from '@/config';
import { useRecoilValue } from 'recoil';
import { authState } from '@/global_states/auth/authState';

function authRequestInterceptor(config: AxiosRequestConfig) {
  const token = useRecoilValue(authState);
  if (token) {
    config.headers.authorization = `${token}`;
  }
  config.headers.Accept = 'application/json';
  config.headers = { 'X-Requested-With': 'XMLHttpRequest' };
  return config;
}

const options = {
  ignoreHeaders: true
}

export const ApiClient = applyCaseMiddleware(Axios.create({
  withCredentials: true,
  baseURL: API_URL,
}), options);

ApiClient.interceptors.request.use(authRequestInterceptor);
ApiClient.interceptors.response.use(
  (response) => response.data,
);

自分で試したこと

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

環境

OS: macOS Ventura 13.2 (intel processor)
React: 18.2.0
TypeScript: 4.9.4
axios: 1.2.1
recoil: 0.7.6

0

Your answer might help someone💌