反应路由器/反应查询不会取消/发出导航请求 - 反应/打字稿

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

使用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。您可以看到为什么对缓存对象进行更精确的引用是有益的。我还使用了两个query filters:exactfetching。将 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) 结合起来。你当然也可以将useLocationQueryClient.cancelQueries 结合使用来监听路由变化。事实上,有很多可能的方法来解决您的问题。

【讨论】:

以上是关于反应路由器/反应查询不会取消/发出导航请求 - 反应/打字稿的主要内容,如果未能解决你的问题,请参考以下文章

如何在反应中使用 axios 发出 POST 请求?

自己尝试写web,网页上点击链接没反应,没有发出http请求。

嵌套反应路由器组件不会在页面重新加载时加载/渲染?

反应路由器更改链接网址但不显示组件

带有反应的 Kerberos 身份验证

以多步形式反应路由器导航