反应路由器/反应查询不会取消/发出导航请求 - 反应/打字稿
Posted
技术标签:
【中文标题】反应路由器/反应查询不会取消/发出导航请求 - 反应/打字稿【英文标题】:React router / react query does not cancel/make request on navigation - React/ typescript 【发布时间】:2021-12-07 13:42:59 【问题描述】:在我的应用程序中,我使用 react-router v5 和 react/typescript 我有一个使用 react-query 并获取一些数据的组件。目前它只在第一次渲染组件时获取数据,当导航请求不会被取消并且导航回来时它不会发出新请求。这个组件接受一个 id 参数,它根据 id 获取数据,所以它需要刷新组件或者我需要将方法添加到 useEffect
钩子中?
路由组件
import React from 'react';
import BrowserRouter, Route, Switch from 'react-router-dom';
import QueryClient, QueryClientProvider from 'react-query';
import RouteComponentProps from "react-router-dom";
import Component1 from '../Component1';
import Component2 from '../Component2';
const queryClient = new QueryClient()
const Routing: React.FunctionComponent = () =>
return (
<QueryClientProvider client=queryClient>
<BrowserRouter>
<Switch>
<Route exact path="/" component=Component1 />
<Route path="/details/:id" render=(props: RouteComponentProps<any>) => <Component2 ...props/> />
<Route component=NotFound />
</Switch>
</BrowserRouter>
</QueryClientProvider>
)
export default Routing;
组件 2 (id)
import React from 'react';
import useQuery from 'react-query';
import RouteComponentProps, useLocation from "react-router-dom";
interface stateType
model: pathname: string ,
start: pathname: string | Date
const Component2: React.FunctionComponent<RouteComponentProps<any>> = (props) =>
const state = useLocation<stateType>();
let alertInnerId = props.match.params.id;
const fetchChart = async () =>
const res = await fetch(`/detail/$id`);
return res.json();
;
const data, status = useQuery('planeInfo', fetchPlane,
staleTime: 5000,
);
return (
<>
status === 'error' && (
<div className="mt-5">Error fetching data!</div>
)
status === 'loading' && (
<div className="mt-5">Loading data ...
</div>
)
status === 'success' && (
data.map(inner =>
return (
<p>inner.id</p>
)
)
)
</div>
</>
)
export default Component2;
在组件 1 中,我以编程方式导航:
onClick=() => history.push(`/detail/$id`, model: plane.model, start: formattedStartDateTime )>
无论是通过编程方式还是正常方式,它仍然是一样的。
【问题讨论】:
React 路由器不会自行取消网络请求。如果您想处理 API 请求的取消,see this。您可以在useEffect
的清理功能中取消您的请求
但是将 react-query 包装在 uesEffect() 中会更好吗
【参考方案1】:
[...] 并返回它不会发出新请求。
首先,根据您的代码,根据设置为useQuery
本身的选项的staleTime
选项,缓存应每五秒失效一次。所以每次挂载useQuery
钩子时(例如在路由更改时),如果五秒钟过去了,应该发出一个新请求。尽管您引用的 id
似乎未定义,但您的代码似乎确实不完整。
在任何情况下,由于您要使用 ID 请求资源的详细信息,因此您应该考虑使用如下查询键:[planeInfo, id]
而不是单独使用 planeInfo
。来自documentation:
由于查询键唯一地描述了它们正在获取的数据,因此它们 应该包括您在查询函数中使用的任何变量 改变。例如:
function Todos( todoId )
const result = useQuery(['todos', todoId], () =>
fetchTodoById(todoId))
处理取消导航请求:
您不能将 React Query 中的 useQuery
钩子包装在 useEffect
钩子中,而是可以使用 useEffect
的返回函数来清理您的 useQuery
请求,当组件卸载。使用useQuery
有两种方法(可能更多)来取消请求:
useQuery
的返回对象上暴露的remove
方法
使用QueryClient
方法:cancelQueries
(see: useQuery
reference here)
see: QueryClient
reference here,特别是cancelQueries
使用remove
和useEffect
(我只保留了您代码的相关部分)
const Component2: React.FunctionComponent <RouteComponentProps<any>> = (props) =>
const fetchChart = async() =>
const res = await fetch(`/detail/$id`);
return res.json();
;
const
data,
status,
/** access the remove method **/
remove
= useQuery('planeInfo', fetchPlane,
staleTime: 5000,
);
useEffect(() =>
/** when this component unmounts, call it **/
return () => remove()
/** make sure to set an empty deps array **/
, [])
/** the rest of your component **/
像这样调用remove
将取消任何正在进行的请求,但顾名思义,它也会从缓存中删除查询。根据您是否需要将数据保存在缓存中,这可能是也可能不是可行的策略。如果需要保留数据,可以改用canceQueries
方法。
使用 cancelQueries
和 useEffect
与之前非常相似,除了这里您需要从路由组件文件中导出您的 queryClient
实例(正如您在此处定义的那样),然后您将 QueryClient
的该实例导入 Component2 并调用 cancelQueries
来自useEffect
的缓存键:
import queryClient from "./routing-component"
const Component2: React.FunctionComponent <RouteComponentProps<any>> = (props) =>
const fetchChart = async() =>
const res = await fetch(`/detail/$id`);
return res.json();
;
const
data,
status,
= useQuery(['planeInfo', id], fetchPlane,
staleTime: 5000,
);
useEffect(() =>
/** when this component unmounts, call it **/
return () => queryClient.cancelQueries(['planeInfo', id], exact: true, fetching: true)
, [])
/** the rest of your component **/
您可以看到,我已经按照我之前的建议实现了查询键,并且还使用了 id。您可以看到为什么对缓存对象进行更精确的引用是有益的。我还使用了两个query filters:exact
和fetching
。将 exact
设置为 true 将确保 React Query 不使用模式匹配并取消更广泛的查询集。您可以决定这对于您的实施需求是否是必要的。将 fetching
设置为 true 将确保 React Query 包含和取消当前正在获取数据的查询。
请注意,通过依赖useEffect
,在某些情况下,由于用户离开页面以外的其他因素(例如模式),它的父组件可能会被卸载。在这种情况下,您应该在组件树中将 useQuery
向上移动到仅在用户导航时卸载的组件中,然后将 useQuery
的结果作为道具传递给子组件,以避免过早取消。
或者,您可以使用 Axios 代替 fetch。使用 Axios,您可以 cancel a request using a global cancel token,并将执行该取消操作与 React Router 的 useLocation
(example here) 结合起来。你当然也可以将useLocation
与QueryClient.cancelQueries
结合使用来监听路由变化。事实上,有很多可能的方法来解决您的问题。
【讨论】:
以上是关于反应路由器/反应查询不会取消/发出导航请求 - 反应/打字稿的主要内容,如果未能解决你的问题,请参考以下文章