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

React Query调用refetch时携带旧state参数发起请求的问题

问题原因
  • 本质是React闭包陷阱 + Hook执行顺序问题:
    1. React组件重渲染时,所有Hook会严格按照代码书写顺序从上到下执行。当body状态更新触发重渲染时,写在前面的useEffect会先执行,此时它拿到的refetch是上一次渲染周期中useQuery返回的实例,对应的查询函数()=>fetchCommissionsData(body)捕获的是上一轮渲染的旧body值。
    2. useEffect同步逻辑执行完成后,代码才会走到后面的useQuery调用,生成捕获了最新body值的新查询函数和新refetch方法,但此时旧refetch已经带着旧参数发起了请求。
  • 该写法本身不符合React Query的设计逻辑:查询依赖的参数应当放入queryKey中,由React Query自动监听key变化触发重查,额外维护本地状态+useEffect手动调用refetch的写法绕开了框架的依赖追踪机制,极易触发闭包问题。
修复方案

推荐写法(符合React Query设计规范)

去掉多余的本地body状态和手动refetch逻辑,将请求参数纳入queryKey管理,依靠React Query原生的依赖追踪自动触发查询,从根源避免闭包问题:

import React from 'react'
import { getCommissionsData } from "../utils/apis";
import { queryKeys } from "../utils/constants";
import { useQueryClient, useQuery } from "react-query";

const fetchCommissionsData = async (request) => {
    const response = await getCommissionsData(request)
    return response
}

export function useCommissionsData() {
  const queryClient = useQueryClient()
  const searchValues = queryClient.getQueryData(queryKeys.searchValues)

  // 对外暴露的查询方法,拼接完参数后更新缓存中的请求体
  const getCommissionsData = (data) => {
    const requestBody = {
      ...data,
      movementType: searchValues.movementType,
      viewType: searchValues.viewType,
      summaryType: searchValues.summaryType
    }
    queryClient.setQueryData([queryKeys.commissionsData, 'requestBody'], requestBody)
  }

  // 从缓存读取当前最新的请求参数
  const currentBody = queryClient.getQueryData([queryKeys.commissionsData, 'requestBody']) ?? null
  const fallback = [];

  // 将请求参数放入queryKey,参数变化时自动触发查询
  const { data = fallback } = useQuery(
    [queryKeys.commissionsData, currentBody],
    () => fetchCommissionsData(currentBody),
    {
      refetchOnWindowFocus: false,
      enabled: !!currentBody // 初始无参数时不发起请求
    }
  );

  return { data, getCommissionsData }
}

兼容原有逻辑的最小改动写法

如果要保留本地维护body状态的逻辑,只需要调整Hook书写顺序:把useQuery移到useEffect之前,保证useEffect执行时能拿到最新的refetch实例,同时补全依赖数组即可:

export function useCommissionsData() {
const [body, setBody] = useState(null)
const queryClient = useQueryClient()
const searchValues = queryClient.getQueryData(queryKeys.searchValues)
    
const getCommissionsData = (data) => {
    const newBody = {
        ...data,
        movementType: searchValues.movementType,
        viewType: searchValues.viewType,
        summaryType: searchValues.summaryType
    }
    setBody(newBody)
}

const fallback = [];
// 先执行useQuery,生成绑定了最新body值的查询函数和refetch
const { data = fallback, refetch } = useQuery([queryKeys.commissionsData], () => fetchCommissionsData(body), {
    refetchOnWindowFocus: false,
    enabled: false
});

// useEffect写在useQuery之后,拿到的是最新的refetch
useEffect(() => {
    if(body) refetch()
}, [body, refetch])

return { data, getCommissionsData }
}

内容的提问来源于stack exchange,提问作者Leon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:24:18