我有一个名为useAPICall
的API挂钩,该挂钩具有回调call
。此回调检查存储在名为auth
的reactn变量中的令牌是否已过期,并在必要时刷新它,然后调用fetch函数。
我在组件中这样称呼它:
const [api] = useAPICall();
useEffect(() => {
api.call('/api/settings/mine/').then(data => {
// do stuff here
});
}, []);
它确实起作用。它通过身份验证流程并调用API。但是,如果我有useAPICall
个组件,它们都试图同时调用API(例如冷页面加载),那么它的每个实例都会调用refresh令牌方法,因为它已过期。
身份验证信息(访问/刷新令牌)存储在如下的reactn全局变量auth
中,位于useAPICall.js
钩子内
import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react';
import {useDispatch, useGlobal} from 'reactn';
export function useAPICall() {
const [auth, setAuth] = useGlobal('auth');
const authRefreshSuccess = useDispatch('authRefreshSuccess');
async function refreshToken() {
console.log('Refreshing access token...');
const authResponse = await fetch('/api/auth/token/refresh/', {
method: 'POST',
credentials: 'same-origin',
body: JSON.stringify({refresh: auth.refresh.token}),
headers: {
'Content-Type': 'application/json',
},
});
if (authResponse.ok) {
const authToken = await authResponse.json();
await authRefreshSuccess(authToken);
return authToken.access;
}
}
function isTokenExpired() {
if (localAuth.access)
return auth.access.exp <= Math.floor(Date.now() / 1000);
else
return false;
}
const call = useCallback(async (endpoint, options={headers: {}}) => {
console.log('performing api call');
token = undefined;
if (isTokenExpired())
token = await refreshToken();
else
token = localAuth.access.token;
const res = await fetch(endpoint, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`,
}
});
if (!res.ok)
throw await res.json();
return res.json();
}, []);
const anonCall = useCallback(async (endpoint, options={}}) => {
const res = await fetch(endpoint, options);
if (!res.ok)
throw await res.json();
return res.json();
}, []);
const api = useMemo(
() => ({
call,
anonCall,
}),
[call, anonCall,]
);
return [api]
}
如何防止它们多次触发刷新方法?
如果有一种更好的方法(没有redux)具有通用的API流(任何API调用都将首先检查访问令牌并在必要时刷新),那么我愿意听。
答案 0 :(得分:2)
我设法通过将promise存储在全局变量中来做到这一点。
let refreshPromise = null;
export function useAuthentication() {
async function getBearer() {
if (isExpired(jwt)) {
if (refreshPromise == null) {
refreshPromise = refresh().then((jwt) => {
refreshPromise = null;
return jwt;
});
}
await refreshPromise;
}
let authData = getAuthData();
if (authData && authData.accessToken) {
return `Bearer ${authData.accessToken}`;
}
return null;
}
const AuthenticationService = {
getBearer,
...
};
return AuthenticationService;
}
希望这会有所帮助!