0

Similiar to Should we use axios inside both components and store?

I have a state called authorization which contains the Bearer token value that would be used in Axios calls. The state is available in a context and accessible using the useContext hook.

I create the AxiosInstance where I add a interceptors.request.use to add the Authorization header.

What I've done so far was useMemo with the authorization value a a dependency. But since Axios operation is asynchronous it seems that I may get the wrong axios instance.

I did a bit of refactoring using useRef and I still had a bit of an issue.

What I then did was implement the Observer pattern and send a notification to the component that provides the Axios client that the authorization header was changed and update the ref. However, again there's still some cases where the old client is being invoked.

What I am wondering is should I store it in useState or is there a fundamental problem with the approach of storing the Axios client and instead should I just bite the bullet and create a new axios client per request which takes the authorization header that's presently in the state?

Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265

2 Answers2

1

The way I typically do it is to save the authentication information in a React context or to redux, and create axios instances as needed to access the authorization token.

Maybe something like:

const getBearerToken() => { ... [your implementation to retrieve from context here] ... };

const webserviceRequest = (url) => () => axios.create({
    baseURL: url,
    ... [AxiosRequestConfig options here] ...
    headers: {
         Authorization: `Bearer ${getBearerToken()}`, 
         ...
    },
});

Then, you can define your webservice requests by invoking this function, e.g.:

const sampleGetRequest = () => webserviceRequest(SAMPLE_URL)().get('');
const samplePostRequest = (postData) => webserviceRequest(SAMPLE_URL)().post('', postData);

These return a Promise<AxiosResponse> which you can call as normal, e.g.

sampleGetRequest().then((response) => { ... }).catch((error) => { ... })

and so on.

The key point is that the webserviceRequest returns a function which creates an asynchronous webservice request with the current authorization token. You don't ever save the webserviceRequest function because that authorization token can become stale.

C. Helling
  • 1,394
  • 6
  • 20
  • 34
  • yes I did that one too... there's a cost to creating a new Axios instance but should be offloaded if the requests are not in rapid succession. – Archimedes Trajano Feb 07 '23 at 01:18
1

With the dependency on React context, I'd avoid using interceptors for this (I'd avoid using Axios all together but that's just my opinion).

Instead, try creating a custom hook

import axios from "axios";
import { useContext } from "react";

const api = axios.create({ /* baseURL, etc */ });

const useApi = () => {
  const authorization = useContext(AuthContext); // guessing

  if (authorization) {
    api.defaults.headers.common.Authorization = `Bearer ${authorization}`;
  } else {
    delete api.defaults.headers.common.Authorization;
  }

  return api;
};
Phil
  • 157,677
  • 23
  • 242
  • 245