Skip to content

React Query 手动触发数据获取:按钮点击时使用 useQuery

问题描述

在 React Query 中,通常使用 useQuery 钩子来自动获取数据,但有时我们需要在用户执行特定操作(如点击按钮)时才触发数据获取。许多开发者面临的问题是:如何在按钮点击时手动触发 useQuery,同时正确处理返回的数据和状态

解决方案

方法一:禁用自动查询并使用 refetch(推荐)

这是 React Query 官方推荐的方法,通过设置 enabled: false 禁用自动查询,然后使用 refetch 方法手动触发。

jsx
import { useQuery } from '@tanstack/react-query'

const fetchData = async () => {
  // 你的数据获取逻辑
  const response = await fetch('/api/data')
  return response.json()
}

function MyComponent() {
  const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['myData'],
    queryFn: fetchData,
    enabled: false, // 禁用自动获取
  })

  const handleClick = () => {
    refetch() // 手动触发数据获取
  }

  return (
    <div>
      <button onClick={handleClick} disabled={isLoading}>
        {isLoading ? '加载中...' : '获取数据'}
      </button>
      {error && <div>错误: {error.message}</div>}
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  )
}

方法二:使用 queryClient.fetchQuery

对于更复杂的场景,可以使用 queryClient.fetchQuery 方法,它提供了更多控制选项。

jsx
import { useQueryClient } from '@tanstack/react-query'

function MyComponent() {
  const queryClient = useQueryClient()
  const [isLoading, setIsLoading] = useState(false)
  const [data, setData] = useState(null)

  const handleClick = async () => {
    try {
      setIsLoading(true)
      const result = await queryClient.fetchQuery({
        queryKey: ['myData'],
        queryFn: fetchData,
      })
      setData(result)
    } catch (error) {
      console.error('获取数据失败:', error)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div>
      <button onClick={handleClick} disabled={isLoading}>
        {isLoading ? '加载中...' : '获取数据'}
      </button>
      {data && <pre>{JSON.stringify(data, null, 2)}</pre>}
    </div>
  )
}

最佳实践与注意事项

缓存策略

React Query 默认会缓存数据。使用 refetch 时,如果缓存未过期,可能会直接返回缓存数据而非重新请求。可以通过设置 staleTimecacheTime 控制缓存行为。

重复请求处理

默认情况下,如果已有请求正在进行,再次调用 refetch 会取消当前请求并发起新请求。可以通过 cancelRefetch: false 选项禁用此行为:

jsx
refetch({ cancelRefetch: false })

错误处理

默认情况下,refetch 不会抛出错误,只会在控制台记录。如需抛出错误,请使用:

jsx
refetch({ throwOnError: true })

进阶用法

依赖查询

可以使用 enabled 选项创建依赖查询,仅在满足特定条件时执行:

jsx
// 先获取用户信息
const { data: user } = useQuery({
  queryKey: ['user', userId],
  queryFn: fetchUser,
})

// 然后获取用户的项目(依赖用户信息)
const { data: projects } = useQuery({
  queryKey: ['projects', user?.id],
  queryFn: fetchProjects,
  enabled: !!user, // 仅在用户信息存在时执行
})

自定义 Hook 封装

为重用逻辑,可以封装自定义 Hook:

jsx
const useManualQuery = (queryKey, queryFn) => {
  const query = useQuery({
    queryKey,
    queryFn,
    enabled: false,
  })

  const fetch = () => {
    if (query.isFetching) return Promise.resolve(query)
    if (query.isFetched && !query.isStale && !query.isError) {
      return Promise.resolve(query)
    }
    return query.refetch()
  }

  return { ...query, fetch }
}

// 使用
const { data, fetch: getData } = useManualQuery(['myData'], fetchData)

总结

在 React Query 中手动触发数据获取主要有两种方式:

  1. 使用 refetch 方法:通过设置 enabled: false 禁用自动查询,然后使用返回的 refetch 函数手动触发
  2. 使用 queryClient.fetchQuery:直接通过 QueryClient 实例调用,提供更细粒度的控制

选择哪种方法取决于具体需求:

  • 简单场景使用 refetch 更便捷
  • 需要更多控制或复杂状态管理时使用 queryClient.fetchQuery

无论哪种方法,都要妥善处理加载状态、错误处理和缓存策略,以提供最佳用户体验。