React Query封装useUser后onSuccess/onError多次触发的解决办法
解决React Query中useUser Hook的onSuccess/onError多次触发问题
问题原因
每个调用useUser的组件实例都会独立注册一套onSuccess和onError回调,当查询完成(成功/失败)时,所有注册的回调都会被执行,所以会出现多次触发的情况。
解决方案
方案1:全局统一处理回调(推荐)
如果回调逻辑是全局唯一的(比如全局提示、全局状态更新),不要把回调写在useUser内部,而是通过QueryClient的订阅功能实现全局监听,确保只触发一次:
// 在全局根组件(如App.tsx)中添加 import { useQueryClient } from '@tanstack/react-query'; import { useEffect } from 'react'; function App() { const queryClient = useQueryClient(); useEffect(() => { // 订阅userData查询的成功/失败事件 const unsubscribe = queryClient.subscribeQueries({ queryKey: ['userData'], onSuccess: () => { console.log('仅触发一次的成功回调'); }, onError: (error) => { console.log('仅触发一次的错误回调'); } }); // 组件卸载时取消订阅 return unsubscribe; }, [queryClient]); // ...其他代码 }
同时修改useUser,移除内部的回调:
export const useUser = () => { return useQuery<User>(['userData'], () => API.fetchUserData(), { staleTime: Infinity }) }
方案2:控制回调触发的时机
如果需要保留useUser内部的回调,但只希望在数据或错误状态变化时触发,可以通过notifyOnChangeProps限制回调的触发条件,避免组件重渲染导致的重复执行:
export const useUser = () => { return useQuery<User>(['userData'], () => API.fetchUserData(), { staleTime: Infinity, // 仅当data或error发生变化时,才触发回调 notifyOnChangeProps: ['data', 'error'], onSuccess: () => { console.log('仅在数据加载/更新时触发'); }, onError: (error) => { console.log('仅在请求出错时触发'); } }) }
方案3:标记回调是否已执行
如果只需要回调执行一次(比如首次加载成功/失败后不再触发),可以用useRef来标记回调的执行状态:
import { useRef } from 'react'; import { useQuery } from '@tanstack/react-query'; export const useUser = () => { const hasRunSuccess = useRef(false); const hasRunError = useRef(false); return useQuery<User>(['userData'], () => API.fetchUserData(), { staleTime: Infinity, onSuccess: () => { if (!hasRunSuccess.current) { console.log('只触发一次的成功回调'); hasRunSuccess.current = true; } }, onError: (error) => { if (!hasRunError.current) { console.log('只触发一次的错误回调'); hasRunError.current = true; } } }) }
内容的提问来源于stack exchange,提问作者React-beginner
相关产品推荐
相关产品推荐

