Tanstack Query与useEffect配合异常问题及解决方案咨询
问题:useQuery首次触发无法获取最新count值
点击按钮将count从0更新为1后,自定义的fetcher函数能正确拿到最新的count值(1),但useQuery的queryFn中输出的count仍为0。经测试,将useEffect移至useQuery代码下方,并把refetch加入useEffect的依赖数组后,问题即可解决。
示例代码:
import { useEffect, useState } from "react"; import { useQuery } from "@tanstack/react-query"; function Comp() { const [count, setCount] = useState(0); useEffect(() => { if (count > 0) { fetcher(); refetch(); } }, [count]); const { refetch } = useQuery({ queryKey: ["count", count], queryFn: async () => { console.log(`useQuery called -> count=${count}`); if (count === 0) { return "ignore"; } const result = await fetch("...", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ count }), }); if (!result.ok) { //throw new Error("Something went wrong"); } return "ok"; }, enabled: false, }); const fetcher = async () => { console.log(`Fetch called -> count=${count}`); if (count === 0) { return; } try { const result = await fetch("...", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ count }), }); if (!result.ok) { } // do something if the request didn't fail } catch (e) {} }; return ( <> <button onClick={() => { setCount((prev) => prev + 1); }} > Click </button> </> ); } export default Comp;
原因分析
- 闭包陷阱与Hook顺序问题:原代码中useEffect定义在useQuery之前,此时useEffect里的
refetch是初始渲染时从useQuery获取的函数,它绑定了初始状态下的count(0)闭包。当count更新为1触发useEffect时,调用的依然是这个旧的refetch,其内部的queryFn自然只能拿到旧的count值。 - 依赖数组不完整:原useEffect的依赖数组仅包含
count,未加入refetch。当count变化导致useQuery生成新的refetch函数时,useEffect不会更新,仍使用旧的refetch。
无Redux情况下的解决方案
方案1:调整Hook顺序并完善依赖数组
将useEffect移至useQuery之后,并把refetch加入useEffect的依赖数组,确保每次count变化时,useEffect能拿到绑定最新count的refetch函数。
修改后的核心代码:
// 先定义useQuery const { refetch } = useQuery({ /* ...配置不变... */ }); // 再定义useEffect,并添加refetch到依赖 useEffect(() => { if (count > 0) { fetcher(); refetch(); } }, [count, refetch]);
方案2:通过refetch参数传递最新值
调用refetch时传递最新的count,并让queryFn从参数或queryKey中获取当前值,彻底避开闭包问题。
修改后的核心代码:
const { refetch } = useQuery({ queryKey: ["count", count], // 从queryKey中获取最新count queryFn: async ({ queryKey }) => { const [_, currentCount] = queryKey; console.log(`useQuery called -> count=${currentCount}`); // 后续逻辑使用currentCount替代count if (currentCount === 0) { return "ignore"; } const result = await fetch("...", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ count: currentCount }), }); // ...剩余逻辑不变 }, enabled: false, }); useEffect(() => { if (count > 0) { fetcher(); refetch(); // 无需额外传参,queryKey已包含最新count } }, [count, refetch]);
方案3:让useQuery自动触发
移除enabled: false,改为通过enabled条件控制useQuery的自动触发,无需手动调用refetch,从根源避免闭包问题。
修改后的核心代码:
const { data } = useQuery({ queryKey: ["count", count], queryFn: async () => { console.log(`useQuery called -> count=${count}`); if (count === 0) { return "ignore"; } const result = await fetch("...", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ count }), }); // ...剩余逻辑不变 }, // 仅当count>0时触发请求 enabled: count > 0, }); // 移除手动调用refetch的useEffect
内容的提问来源于stack exchange,提问作者George S.
相关产品推荐
相关产品推荐

