React Query中useQuery查询函数随数据更新失效问题求助
问题:React Query v5中useQuery查询函数无法获取最新的deck值
在使用@tanstack/react-query v5.10.0时,当OfflineContext中的deck值从初始对象变为null后,调用queryClient.invalidateQueries触发查询重取,useQuery的查询函数仍使用旧的deck值,而非最新的null。已尝试用useCallback包裹查询函数,但问题依旧。需求是保持查询键不变的前提下,让查询函数能获取最新的deck值。
原代码片段
import { request } from "../utils/fetch-utils"; import { useQuery } from "@tanstack/react-query"; import { CustomError } from "../utils/customError"; import { useCallback, useContext } from "react"; import { OfflineContext } from "../context/OfflineContext"; import { DeckTypeForSearch } from "../types/types"; type SuccessResponseType = { title: string; cards: { title: string; note: string; cardId: string; _id: string }[]; _id: string; }; const getCards = async ( _id: string, deck: DeckTypeForSearch | null ): Promise<SuccessResponseType | null> => { if (!deck) { try { const data = await request.get(`/cards/${_id}`); return data?.data; } catch (error) { if (error instanceof CustomError) { throw new CustomError(error?.message, error?.statusCode); } else { throw new CustomError("Something went wrong!", 503); } } } else if (deck) { return { title: deck.title, cards: deck.cards, _id: deck.deck_id }; } return null; }; const useGetCards = (deck_id: string) => { const { deck } = useContext(OfflineContext); const queryKey = ["cards", deck_id]; const getDataCallbackFunction = useCallback(() => { return getCards(deck_id, deck); }, [deck, deck_id]); return useQuery({ queryKey: queryKey, queryFn: () => { return getDataCallbackFunction(); }, refetchOnWindowFocus: false, refetchOnReconnect: true, retry: 0, gcTime: 10800000, }); }; export default useGetCards;
触发重取的代码
queryClient.invalidateQueries({ queryKey: ["cards", mutationData.deck_id], });
原因分析
问题出在闭包捕获:useCallback生成的函数会捕获创建时的deck值,即使后续deck更新,已经缓存的查询函数闭包仍持有旧值。当调用invalidateQueries时,React Query会复用之前的查询函数闭包,导致无法获取最新的deck状态。
解决方案
使用useRef跟踪最新的deck值,让查询函数每次执行时都能访问到当前最新的状态,无需修改查询键:
import { request } from "../utils/fetch-utils"; import { useQuery } from "@tanstack/react-query"; import { CustomError } from "../utils/customError"; import { useContext, useRef, useEffect } from "react"; import { OfflineContext } from "../context/OfflineContext"; import { DeckTypeForSearch } from "../types/types"; type SuccessResponseType = { title: string; cards: { title: string; note: string; cardId: string; _id: string }[]; _id: string; }; const getCards = async ( _id: string, deck: DeckTypeForSearch | null ): Promise<SuccessResponseType | null> => { if (!deck) { try { const data = await request.get(`/cards/${_id}`); return data?.data; } catch (error) { if (error instanceof CustomError) { throw new CustomError(error?.message, error?.statusCode); } else { throw new CustomError("Something went wrong!", 503); } } } else if (deck) { return { title: deck.title, cards: deck.cards, _id: deck.deck_id }; } return null; }; const useGetCards = (deck_id: string) => { const { deck } = useContext(OfflineContext); // 用ref跟踪最新的deck值 const deckRef = useRef<DeckTypeForSearch | null>(deck); // 当deck更新时,同步更新ref的current值 useEffect(() => { deckRef.current = deck; }, [deck]); const queryKey = ["cards", deck_id]; return useQuery({ queryKey: queryKey, // 每次执行queryFn时,取ref.current的最新deck值 queryFn: () => { return getCards(deck_id, deckRef.current); }, refetchOnWindowFocus: false, refetchOnReconnect: true, retry: 0, gcTime: 10800000, }); }; export default useGetCards;
方案说明
useRef跟踪状态:deckRef会始终保存最新的deck值,不受闭包捕获的影响。useEffect同步更新:当deck状态变化时,自动更新deckRef.current,确保查询函数总能拿到最新值。- 无需修改查询键:保持原有的
["cards", deck_id]查询键,满足需求。
当再次调用queryClient.invalidateQueries时,查询函数会使用deckRef.current的最新值(即null),从而触发正确的网络请求。
内容的提问来源于stack exchange,提问作者Max Vhanamane
相关产品推荐
相关产品推荐

