在我的应用程序中,我使用的是react-router v5和react/typescript,我有一个组件使用react-query并获取一些数据。目前,它只在组件第一次呈现时获取数据,当导航请求没有被取消和导航返回时,它不会发出新的请求。这个组件接受一个id参数,该参数根据id获取数据,所以它需要刷新组件,或者我可能需要将方法添加到useEffect
钩子中?
<路由组件/strong>
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;
于Component2 (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 })}>
无论通过编程方式还是普通方式,都是一样的。
[…]]并返回,它不会发出新的请求。
首先,根据您的代码,根据staleTime
选项被设置为useQuery
本身的选项,缓存应该每五秒失效一次。因此,每次挂载useQuery
钩子时(例如在路由更改时),如果五秒钟过去了,就应该发出一个新的请求。你的代码似乎是不完整的,虽然你引用的id
似乎是未定义的。
在任何情况下,由于您正在请求具有ID的资源的详细信息,您应该考虑使用像:[planeInfo, id]
这样的查询键,而不是单独使用planeInfo
。来自文档:
由于查询键唯一地描述了它们正在获取的数据,因此它们应该包括您在查询函数中使用的任何变量改变。例如:
function Todos({ todoId }) {
const result = useQuery(['todos', todoId], () =>
fetchTodoById(todoId))
}
处理取消导航请求:
你不能在useEffect
钩子中包装来自React Query的useQuery
钩子,但是你可以使用useEffect
的返回函数来清理你的useQuery
请求,当组件卸载时有效地取消请求。使用useQuery
,有两种(可能更多)方法来取消请求:
- 使用
useQuery
返回对象上暴露的
remove
方法使用QueryClient
方法:cancelQueries
(见useQuery
参考)
参见此处的QueryClient
reference,特别是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。您可以看到为什么对缓存对象有一个更精确的引用是有益的。我还使用两个查询过滤器:exact
和fetching
。将exact
设置为true将确保React Query不使用模式匹配并取消更广泛的查询集。您可以决定这对于您的实现需求是否必要。将fetching
设置为true将确保React Query包含和取消当前正在获取数据的查询。
请注意,依赖于useEffect
,在某些情况下,它的父组件可能由于用户从页面导航(如模态)以外的因素而卸载。在这种情况下,你应该把你的useQuery
在组件树中向上移动到一个只会在用户导航时卸载的组件中,然后把useQuery
的结果作为道具传递给子组件,以避免过早取消。
或者你可以使用Axios代替fetch。在Axios中,你可以使用全局取消令牌来取消请求,并将该取消与React Router的useLocation
结合执行(示例如下)。当然,您也可以将useLocation
侦听路由变化与QueryClient.cancelQueries
结合起来。事实上,有许多可能的方法来解决你的问题。