框架
版本

渲染优化

React Query 会自动应用一些优化,以确保您的组件只在实际需要时才重新渲染。这通过以下方式实现:

结构共享

React Query 使用一种称为“结构共享”的技术,以确保在重新渲染之间尽可能多地保持引用完整。如果数据通过网络获取,通常情况下,通过 JSON 解析响应,您会得到一个全新的引用。然而,如果数据中没有任何变化,React Query 将保留原始引用。如果子集发生变化,React Query 将保留未变化的部分,只替换变化的部分。

注意:此优化仅在 queryFn 返回 JSON 兼容数据时有效。您可以通过全局设置 structuralSharing: false 或按查询设置来关闭它,也可以通过向其传递一个函数来实现自己的结构共享。

引用同一性

useQueryuseInfiniteQueryuseMutation 返回的顶级对象以及从 useQueries 返回的数组不具有引用稳定性。它将在每次渲染时成为一个新的引用。然而,从这些钩子返回的 data 属性将尽可能稳定。

跟踪属性

React Query 只有在 useQuery 返回的属性之一实际“使用”时才会触发重新渲染。这是通过使用 Proxy 对象实现的。这避免了许多不必要的重新渲染,例如,因为 isFetchingisStale 等属性可能经常变化,但在组件中并未使用。

您可以通过全局或按查询手动设置 notifyOnChangeProps 来定制此功能。如果您想关闭此功能,可以设置 notifyOnChangeProps: 'all'

注意:通过解构或直接访问属性来访问属性时,会调用 Proxy 的 get 陷阱。如果您使用对象剩余解构,将禁用此优化。我们有一个lint 规则来防范此陷阱。

选择器

您可以使用 select 选项来选择组件应订阅的数据子集。这对于高度优化的数据转换或避免不必要的重新渲染非常有用。

js
export const useTodos = (select) => {
  return useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    select,
  })
}

export const useTodoCount = () => {
  return useTodos((data) => data.length)
}
export const useTodos = (select) => {
  return useQuery({
    queryKey: ['todos'],
    queryFn: fetchTodos,
    select,
  })
}

export const useTodoCount = () => {
  return useTodos((data) => data.length)
}

使用 useTodoCount 自定义钩子的组件只会在待办事项长度变化时重新渲染。如果待办事项的名称发生变化,它将不会重新渲染。

注意:select 在成功缓存的数据上操作,不是抛出错误的合适位置。错误的真相来源是 queryFn,返回错误的 select 函数会导致 dataundefinedisSuccesstrue。如果您希望查询在数据不正确时失败,我们建议在 queryFn 中处理错误,或者如果您的错误情况与缓存无关,则在查询钩子之外处理。

记忆化

select 函数仅在以下情况才会重新运行:

  • select 函数本身引用发生变化
  • data 发生变化

这意味着如上所示的内联 select 函数将在每次渲染时运行。为了避免这种情况,您可以将 select 函数包装在 useCallback 中,或者如果它没有任何依赖,则将其提取到稳定的函数引用中。

js
// wrapped in useCallback
export const useTodoCount = () => {
  return useTodos(useCallback((data) => data.length, []))
}
// wrapped in useCallback
export const useTodoCount = () => {
  return useTodos(useCallback((data) => data.length, []))
}
js
// extracted to a stable function reference
const selectTodoCount = (data) => data.length

export const useTodoCount = () => {
  return useTodos(selectTodoCount)
}
// extracted to a stable function reference
const selectTodoCount = (data) => data.length

export const useTodoCount = () => {
  return useTodos(selectTodoCount)
}

延伸阅读

有关这些主题的深入指南,请阅读社区资源中的 React Query 渲染优化。要了解如何最好地优化 select 选项,请阅读 React Query 选择器,超强