Проблема с 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 шт):
Запоминайте в какой нибудь переменной (не локальной, что бы была доступна при всех вызовах) промис который возвращает 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.