You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;

原因分析

  1. 闭包陷阱与Hook顺序问题:原代码中useEffect定义在useQuery之前,此时useEffect里的refetch是初始渲染时从useQuery获取的函数,它绑定了初始状态下的count(0)闭包。当count更新为1触发useEffect时,调用的依然是这个旧的refetch,其内部的queryFn自然只能拿到旧的count值。
  2. 依赖数组不完整:原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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:35:54