javascript - React 路由器/React 查询不会取消/在导航上发出请求 - React/TypeScript

标签 javascript reactjs typescript react-router react-query

在我的应用程序中,我使用react-router v5和react/typescript,我有一个使用react-query并获取一些数据的组件。目前,它仅在第一次渲染组件时获取数据,当导航请求不会被取消并且导航回来时,它不会发出新请求。该组件接受一个 id 参数,该参数根据 id 获取数据,因此它需要刷新组件,或者可能需要将方法添加到 useEffect Hook 中?

路由组件

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 })}> 

无论是通过编程方式还是正常方式,它仍然是一样的。

最佳答案

[...] and navigating back it does not make a new request.

首先,根据您的代码,根据设置为 useQuery 本身选项的 staleTime 选项,缓存应每五秒失效一次。因此,每次安装 useQuery Hook 时(例如在路由更改时),如果过去了五秒,则应发出新请求。尽管您引用的 id 似乎未定义,但您的代码似乎不完整。

无论如何,由于您正在请求带有 ID 的资源的详细信息,因此您应该考虑使用如下查询键:[planeInfo, id] 而不是 planeInfo独自的。来自 documentation :

Since query keys uniquely describe the data they are fetching, they should include any variables you use in your query function that change. For example:

function Todos({ todoId }) {
    const result = useQuery(['todos', todoId], () => 
    fetchTodoById(todoId))
}

处理取消导航请求:

您无法将 React Query 中的 useQuery Hook 包装在 useEffect Hook 中,但您可以使用 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 实例code> 进入 Component2 并从 useEffect 调用缓存键上的 cancelQueries:

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 设置为 true 将确保 React Query 不使用模式匹配并取消更广泛的查询集。您可以决定这对于您的实现需求是否是必要的。将 fetching 设置为 true 将确保 React Query 包含并取消当前正在获取数据的查询。

请注意,通过依赖 useEffect,在某些情况下,由于用户离开页面(例如模态)以外的因素,其父组件可能会被卸载。在这种情况下,您应该将 useQuery 在组件树中向上移动到一个仅在用户导航时卸载的组件中,然后将 useQuery 的结果传递给子组件组件作为 props,以避免过早取消。

或者,您可以使用 Axios 而不是 fetch。使用 Axios,您可以 cancel a request using a global cancel token ,并将执行该取消与 React Router 的 useLocation ( example here ) 结合起来。当然,您也可以将监听路由更改的 useLocationQueryClient.cancelQueries 结合起来。事实上,有很多可能的方法来解决你的问题。

关于javascript - React 路由器/React 查询不会取消/在导航上发出请求 - React/TypeScript,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69651014/

相关文章:

python - webpack bundle export 中的 React 组件库,用于运行时渲染

javascript - 如何创建一个适用于我的用户模型的不同属性的排序函数

visual-studio - Visual Studio 2015 : Cannot edit TypeScript files, 获取异常 0x80020101

javascript - 使用 getElementsByClassName 具有多个元素的 HTML Javascript setTimeout

javascript - 如何获得 jQuery 中某个类的所有表行的总和?

ruby-on-rails - 为什么我的 axios get 调用一遍又一遍地重复使用 React.useEffect 从 Rails 后端获取?

javascript - 使用注入(inject)服务扩展 Angular Component 方法

typescript - 理解类型断言

javascript - 如何使用 jquery 在现有 li 列表之间添加带有文本的 li 元素?

javascript - jQuery parents ("tr") 淡出不工作