React Query - 超越基础

2025-06-07

React Query - 超越基础

互联网上已经有不少文章探讨过使用 React-Query 的优势。它的易用性,useQuery例如useMutation只需一行代码即可访问加载、获取或错误状态以及响应数据,已经被反复提及。但更高级、更基础的功能却鲜有提及。因此,我决定深入探讨 React-Query 的一些特性。

我将举一个简单的待办事项列表应用的例子,其中会显示待办事项列表。当用户想要创建新的待办事项时,会打开一个模态表单。待办事项创建成功后,系统会重新获取待办事项列表。在待办事项列表中,如果用户点击某个待办事项,则会跳转到一个新页面,其中会显示该待办事项的相应详细信息。

1. 成功和错误

useQueryuseMutation支持各种配置选项。其中两个是onSucess和 ,onError它们接受一个函数作为参数。如果我们想要执行一些非数据渲染逻辑,这些函数会特别有用。在待办事项列表示例中,如果我们想抛出成功或错误消息,则可以使用不一定是组件的芯片。(如果需要渲染组件,最好使用isSuccessisError)。基本上,它们可以像我们在 API 获取中使用的 .then 回调函数一样工作。更好的用例是分派一些 Redux 状态。这里也可以这样做,无需费力useEffect

import { message } from "antd"

const { isLoading, isError, mutateAsync } = useMutation(
  "todo/create",
  () => {
    return axios.post("http://localhost:4000/todo/create", task)
  },
  {
    onSuccess: res => {
      dispatch(setTaskList(res.data.task_list))
      message.success("New todo created")
    },
    onError: () => {
      message.error("Some error occured")
    },
  }
)
Enter fullscreen mode Exit fullscreen mode

2. 查询无效

在我们的任务应用示例中,我们讨论过在成功创建任务后重新获取所有任务列表。因此,查询失效的魔力就体现在这里。我们必须使用之前讨论过的onSucces函数。在该函数中,我们可以使用查询失效来使查询失效,即要求 React-query 重新获取一个或多个查询。

在我们的待办事项应用程序中,当我们的待办事项创建成功时,我们将使获取所有待办事项列表的查询无效。

// 📁 AllTodos.jsx
// in todo/all query we are fetching the list of all todos
const { isLoading, isError, data } = useQuery("todo/all", () => {
  return axios.get(`http://localhost:4000/todo/all`)
})

// 📁 CreateTodo.jsx
// now when todo/create is successful we are invalidating todo/all
// So that todo list is being fetching in create new todo creation
const { isLoading, isError, mutateAsync } = useMutation(
  "todo/create",
  () => {
    return axios.post("http://localhost:4000/todo/create", todo)
  },
  {
    onSuccess: () => {
      queryClient.invalidateQueries("todo/all")
    },
  }
)
Enter fullscreen mode Exit fullscreen mode

3. 查询重试

这可能很小。但在情况需要时会很方便。React 查询带有一些与每个配置选项相对应的预配置默认值。例如,缓存时间为 5 分钟,陈旧时间为 0。因此,其中之一是重试选项。它的默认值是 3。也就是说,如果查询在第一次尝试中没有成功获取查询,它将继续尝试 3 次才声明isError为真。在某些情况下,您可能不想要这种行为。您可以随时将其更改为其他数字,表示您希望发生的重试次数。另一种方式是,重试也接受 true 和 false 作为值。这是什么意思?如果重试为真,则 React 查询将获取查询直到成功,如果为假,则在任何不成功的尝试后都不会重试。

const { isLoading, isError, data } = useQuery(
  "todo/all",
  () => {
    return axios.get(`http://localhost:4000/todo/all`);
  },
  {
    retry: 1, // or `true` or `false` or e.g. 6
  }
);
Enter fullscreen mode Exit fullscreen mode

所有这些选项都可以针对每个查询进行更改。但您可能希望为所有查询声明自己的配置选项(除非您在某个特定查询中另有说明)。那么您应该考虑在查询客户端中执行此操作。

4. 有条件地启用查询

在某些情况下,您可能希望查询仅在满足某些条件时运行。React useQuery-query 的所有其他优点,作为钩子,我们不能直接在某些 if else 语句中使用它们,因为这会破坏 React 钩子的基本规则。对于这类场景,React-query 附带一个名为 enabled 的选项。我们可以将其硬编码为 true 或 false,但它真正出色的地方在于传递变量。现在,根据该变量值的变化,查询将被启用或禁用。这太酷了!

例如,在我们的待办事项应用中,当用户转到单个待办事项时,todo_id 会作为参数传递到 URL 中(使用 react-router 或其他路由库)。然后根据 todo_id 获取详细信息。现在我们希望仅当参数不为空时才获取查询。我们可以这样做:

const { id } = useParams(); // from react-router

const { isLoading, isError, data } = useQuery(
  ["todo/detail", id],
  () => {
    return axios.get(`http://localhost:4000/todo/detail/:${id}`);
  },
  {
    enabled: !!id,
  }
);
Enter fullscreen mode Exit fullscreen mode

5. 自定义查询钩子

这更多是个人观点,而非 React-Query 独有的功能。因此,如果您需要自定义预配置选项之外的行为,或者需要访问onSuccessonError选项,很快您就可能会遇到类似这样的情况。有些人可能更喜欢能够立即轻松查看查询中发生的情况。但如果您需要跨多个组件访问同一个查询,您可能需要创建自己的自定义钩子,并将其包裹在整个 React-Query 逻辑中。我向您保证,这并非什么高深的技巧。如果我们考虑前面的例子,它会是这样的:

const useGetTodoById = (id) => {
  const { isLoading, isError, data } = useQuery(
    ["todo/detail", id],
    () => {
      return axios.get(`http://localhost:4000/todo/detail/:${id}`);
    },
    {
      enabled: !!id,
      retry: 1,
      onSuccess: () => {
        // invalidate some query
      },
      onError: () => {
        // do something else
      },
    }
  );
  export { isLoading, isError, data };
};
// in the component use it just like this
const { isLoading, isError, data } = useGetTodoById(id)
Enter fullscreen mode Exit fullscreen mode

一些专业提示

  1. 如果您考虑编写自定义钩子,您可能还会考虑声明一个变量来存储这些数据,或者如果您出于某种原因需要状态代码,那么您也可以在这里将其抽象出来,并将其作为单个值传递,使其成为我们需要映射或执行其他操作的数据。定义明确的变量比通用数据更有意义。
   const { isLoading, isError, data } = useQuery(
       ["todo/detail", id],
       () => {
         return axios.get(`http://localhost:4000/todo/detail/:${id}`);
       },
       {
         enabled: !!id,
         retry: 1,
         onSuccess: () => {
           // invalidate some query
         },
         onError: () => {
           // do something else
         },
       }
     );
     const fetchedTodo = data.data.todo
     const fetchTodoStatus = data.status
     export { isLoading, isError, fetchedTodo, fetchTodoStatus }
   }
Enter fullscreen mode Exit fullscreen mode
  1. 如果要将数据重命名为其他名称,您也可以直接在 react-query 中执行此操作。不仅如此,您还可以将isLoading或重命名isError为其他名称。如果您需要在一个组件中访问两个或多个查询,则尤其需要这样做。
   const {
     isLoading: isAllTodoLoading,
     isError: isAllTodoError,
     data: allTodo,
   } = useQuery("todo/all", () => {
     return axios.post("http://localhost:4000/todo/all", todo)
   })
Enter fullscreen mode Exit fullscreen mode
  1. 您可以使用 API 路由作为查询名称。如果您将查询函数抽象到其他地方,这将非常有意义。如果您发现需要访问您认为已经在某个组件中使用过的特定查询,并且现在想在其他组件中使用它,这也可能有所帮助。以这种方式命名,您将很容易找到该特定查询的名称。毕竟,查询名称对于充分利用 React-Query 的优势至关重要。我在整篇文章中都遵循了这一点。

  2. 如果使用自定义钩子,您可以根据它们的主路由将它们保存在单独的文件中。并将它们全部保存在 services 文件夹下,您可能已经在使用 axios 这样做了。

   src
      - components
      - pages
      - services
          - todo.js
          - user.js
Enter fullscreen mode Exit fullscreen mode

这并不是详尽无遗的,只是我每天都会用到的几个。

最后一部分内容纯属个人拙见,您可能不认同,或者我的做法可能确实有误。请随时告诉我。

文章来源:https://dev.to/nabanita_sarkar/react-query-beyond-basics-17pb
PREV
安装 AUR 软件包 概要 如何使用 侧边栏主题
NEXT
使用现代 API 映射 JavaScript 对象