React Query 手动触发数据获取:按钮点击时使用 useQuery
问题描述
在 React Query 中,通常使用 useQuery
钩子来自动获取数据,但有时我们需要在用户执行特定操作(如点击按钮)时才触发数据获取。许多开发者面临的问题是:如何在按钮点击时手动触发 useQuery
,同时正确处理返回的数据和状态。
解决方案
方法一:禁用自动查询并使用 refetch(推荐)
这是 React Query 官方推荐的方法,通过设置 enabled: false
禁用自动查询,然后使用 refetch
方法手动触发。
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
方法,它提供了更多控制选项。
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
时,如果缓存未过期,可能会直接返回缓存数据而非重新请求。可以通过设置 staleTime
和 cacheTime
控制缓存行为。
重复请求处理
默认情况下,如果已有请求正在进行,再次调用 refetch
会取消当前请求并发起新请求。可以通过 cancelRefetch: false
选项禁用此行为:
refetch({ cancelRefetch: false })
错误处理
默认情况下,refetch
不会抛出错误,只会在控制台记录。如需抛出错误,请使用:
refetch({ throwOnError: true })
进阶用法
依赖查询
可以使用 enabled
选项创建依赖查询,仅在满足特定条件时执行:
// 先获取用户信息
const { data: user } = useQuery({
queryKey: ['user', userId],
queryFn: fetchUser,
})
// 然后获取用户的项目(依赖用户信息)
const { data: projects } = useQuery({
queryKey: ['projects', user?.id],
queryFn: fetchProjects,
enabled: !!user, // 仅在用户信息存在时执行
})
自定义 Hook 封装
为重用逻辑,可以封装自定义 Hook:
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 中手动触发数据获取主要有两种方式:
- 使用
refetch
方法:通过设置enabled: false
禁用自动查询,然后使用返回的refetch
函数手动触发 - 使用
queryClient.fetchQuery
:直接通过 QueryClient 实例调用,提供更细粒度的控制
选择哪种方法取决于具体需求:
- 简单场景使用
refetch
更便捷 - 需要更多控制或复杂状态管理时使用
queryClient.fetchQuery
无论哪种方法,都要妥善处理加载状态、错误处理和缓存策略,以提供最佳用户体验。