除useRef外,如何让react-query的onSuccess仅在首次成功时执行?
问题与解决方案:useQuery首次onSuccess仅执行一次的替代方案
问题描述
需要保留useQuery的标准行为,但要让一段代码仅在首次onSuccess回调中执行。已知可通过useRef实现,想了解其他替代方案。
无法1:1复现实际场景,但提供的示例里,点击按钮递增计数器后,计数器会被重置为100,原因是onSuccess执行了两次。
示例代码:
const [counter, setCounter] = useState(0); const { data, isLoading } = useQuery( ["key"], () => { return axios.get("https://picsum.photos/200"); }, { onSuccess() { // 此处会执行两次 setCounter(100); console.log("fetch"); } } ); return ( <div className="App"> <button onClick={() => { setCounter(counter + 1); }} > counter {counter} </button> {data?.data && "数据已加载"} </div> );
替代方案
1. 结合useQuery的isSuccess状态与useEffect监听
通过useEffect监听isSuccess状态变化,仅在状态从false变为true时执行目标代码:
const [counter, setCounter] = useState(0); const { data, isSuccess } = useQuery( ["key"], () => axios.get("https://picsum.photos/200"), ); useEffect(() => { if (isSuccess) { setCounter(100); console.log("fetch"); } }, [isSuccess]);
注:若后续查询因失效、重新获取再次成功,只要isSuccess未从false切换回true,就不会重复执行。此方案已能避免示例中的重复重置问题。
2. 依赖查询data的初始赋值状态
判断data是否从undefined变为有值,结合useEffect监听data变化,仅在首次获取到数据时执行代码:
const [counter, setCounter] = useState(0); const { data } = useQuery( ["key"], () => axios.get("https://picsum.photos/200"), ); useEffect(() => { if (data) { setCounter(100); console.log("fetch"); } }, [data]);
核心逻辑:首次请求成功后data才会被赋值,后续重新获取时data会更新,但只要业务允许后续成功不触发代码,此方案即可满足需求。
3. 利用react-query的enabled控制(限无需后续重新获取场景)
通过enabled选项控制查询是否执行,首次成功后禁用查询,避免后续触发onSuccess:
const [counter, setCounter] = useState(0); const [queryEnabled, setQueryEnabled] = useState(true); const { data } = useQuery( ["key"], () => axios.get("https://picsum.photos/200"), { enabled: queryEnabled, onSuccess() { setCounter(100); console.log("fetch"); setQueryEnabled(false); } } );
注意:此方案会在首次成功后停止查询的后续执行,若需保留useQuery的标准行为(如后台刷新、失效重试),则不适用。
示例中onSuccess重复执行的原因
点击按钮更新counter会触发组件重新渲染,而react-query的设计是:组件重新渲染时,若查询已处于成功状态,会再次执行onSuccess回调,以保证组件状态与查询结果同步。这就是每次点击按钮后,计数器被重置为100的原因。
内容的提问来源于stack exchange,提问作者Alex Ironside
相关产品推荐
相关产品推荐

