React router / React query不会在导航- React/ typescript上取消/发出请求.&



在我的应用程序中,我使用的是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参考)

参见此处的QueryClientreference,特别是cancelQueries

使用removeuseEffect

(我只保留了你代码中相关的部分)

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方法。

使用cancelQueriesuseEffect

非常像以前,除了这里你需要导出你的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。您可以看到为什么对缓存对象有一个更精确的引用是有益的。我还使用两个查询过滤器:exactfetching。将exact设置为true将确保React Query不使用模式匹配并取消更广泛的查询集。您可以决定这对于您的实现需求是否必要。将fetching设置为true将确保React Query包含和取消当前正在获取数据的查询。

请注意,依赖于useEffect,在某些情况下,它的父组件可能由于用户从页面导航(如模态)以外的因素而卸载。在这种情况下,你应该把你的useQuery在组件树中向上移动到一个只会在用户导航时卸载的组件中,然后把useQuery的结果作为道具传递给子组件,以避免过早取消。

或者你可以使用Axios代替fetch。在Axios中,你可以使用全局取消令牌来取消请求,并将该取消与React Router的useLocation结合执行(示例如下)。当然,您也可以将useLocation侦听路由变化与QueryClient.cancelQueries结合起来。事实上,有许多可能的方法来解决你的问题。

最新更新