我们使用react-query
从API获取数据。对于每个api调用,都有一个具有不同查询键的单独钩子,但它们都使用相同的函数callApi()
,该函数以api url作为参数来获取数据。
在callAPI()
钩子中,我们有逻辑从authState中获得要使用的认证令牌。(下面的代码)
const useAxios = (baseUrl = config.API_URL) => {
const history = useHistory()
const [{ accessToken }] = useAuthState()
const callApi = useCallback(
async ({ headers, ...rest }) => {
try {
const { data } = await axios({
baseURL: baseUrl,
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
...headers,
},
...rest,
validateStatus: (status) => status >= 200 && status <= 299,
})
return data
} catch (err) {
if (err && err.response && err.response.status === 401) {
history.push(`${appConfig.APP_SUBPATH}/sessionExpired`)
} else if (err && err.response && err.response.status === 503) {
history.push(`${appConfig.APP_SUBPATH}/underMaintenance`)
}
throw err
}
},
[accessToken],
)
return callApi
}
export default useAxios
export const useGetItems = () => {
const callApi = useAxios()
return useQuery(ALL_ITEMS_KEY, () =>
callApi({
method: 'GET',
url: 'api/v1/items',
}).then((data) => data.data),
)
}
问题:在令牌刷新后,react-query仍然使用旧令牌重新获取无效的查询,导致401 UnAuthorized,尽管状态中的令牌也被更新了。我尝试使用react-query开发工具
为什么即使刷新了,react-query仍然使用旧令牌?如何防止react-query使用旧令牌?
编辑:我明白,如果令牌被更新,react-query不会自动重新获取,但如果查询被标记为无效,则会自动重新获取,如下所示。在这种情况下,它使用第一次调用时使用的旧的过期令牌
queryCache.invalidateQueries((x) => x.queryKey.includes(ALL_ITEMS_KEY))
在queryKey:
中没有访问令牌useQuery(ALL_ITEMS_KEY)
所以ReactQuery没有办法理解它应该重新获取数据时,它的变化。将其添加到查询键:
useQuery(
[...ALL_ITEMS_KEY, accessToken],
() => callApi(...),
{ enabled: !!accessToken }, // optional?
)
可选,如果没有令牌,您也可以禁用查询。
你在React中遇到了一个过时的关闭问题,这是因为你的反应依赖(令牌)不是queryKey
的一部分。
当重取发生时,queryFunction从上次键更改的时间开始使用,因此它仍然看到闭包中的旧令牌。
这就是为什么在queryKey中包含所有依赖项是如此重要。
我有一个关于闭包的两部分博文系列,也许这有助于更好地理解这个问题:
- https://tkdodo.eu/blog/hooks-dependencies-and-stale-closures
现在令牌是一个"特殊"情况下,我不想在QueryKey中包含它们。问题是:为什么令牌来自useAuthState
?我猜这是react上下文,但是为什么令牌需要首先存储在react上下文中?从react查询的角度来看,不需要用新的令牌通知/重新呈现组件,除非您还在queryKey中包含该令牌。
处理令牌的更好的地方是,因为你已经在使用axios,一个axios拦截器。