Проблема с race condition при обновлении токена axios React

Использую interceptors в axios, столкнулся с проблемой, у меня на странице проекта есть несколько запросов, interceptors отрабатывает на все из них, от чего токен может обновляться по нескольку раз.

Долго гуглил и искал, но внятного решения без костылей и без Redux найти не смог, Redux то я знаю, но проект не маленький и завозить его уже поздновато, есть ли возможность на чистом React первый interceptors отправлять на обновление токена, а остальных заставить его дожидаться?

import axios from "axios";

const API_URL = `${process.env.REACT_APP_URL_API}`;

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

api.interceptors.request.use((config) => {
    if (localStorage.getItem("access")) {
        config.headers.Authorization = `Bearer ${localStorage.getItem(
            "access"
        )}`;
    }
    return config;
});

api.interceptors.response.use(
    (config) => {
        return config;
    },
    async (error) => {
        const originalRequest = error.config;
        if (
            error.response.status == 401 &&
            error.response.data.detail !== "Неверный логин или пароль" &&
            error.config &&
            !error.config._isRetry
        ) {
            originalRequest._isRetry = true;
            try {
                const response = await axios.post(`${API_URL}/token-refresh/`, {
                    refresh: localStorage.getItem("refresh"),
                });
                localStorage.setItem("access", response.data.access);
                localStorage.setItem("refresh", response.data.refresh);
                return api.request(originalRequest);
            } catch (error) {}
        } else {
            throw error;
        }
    }
);

Ответы (1 шт):

Автор решения: Mike

Запоминайте в какой нибудь переменной (не локальной, что бы была доступна при всех вызовах) промис который возвращает axios.post и если переменная не пуста то делайте await на нее, вместо вызова axios.post. Т.е. что то в этом роде

....
{
  originalRequest._isRetry = true;
  if(! api.interceptors._refresh_promise) {
      api.interceptors._refresh_promise = axios.post(`${API_URL}/token-refresh/`, {
                refresh: localStorage.getItem("refresh"),
      });      
  }
  try {
      const response = await api.interceptors._refresh_promise;
      ....
  } catch (error) {}
  finally {
      api.interceptors._refresh_promise = false;
  }
}

api.interceptors._refresh_promise Использована для примера, используйте какое нибудь более уместное для React место хранения. Хотя возможно более правильным будет создать собственный промис, который при работе сделает await axios.post и последующие строки кода работающие с response.

Так же, возможно стоит проверять и ждать завершения этого промиса перед выполнением любого запроса (если его можно перехватить в axios, лично у меня для вызова api используется обертка поверх axios, которая делает подобные вещи), а не в обработчике ошибок axios.

→ Ссылка