为什么 Tanstack React Query useQuery 在调用 queryClient.fetchQuery 后不更新数据

问题描述 投票:0回答:1

我正在使用 React Query 通过 Firebase 管理我的 React Native 应用程序中的用户个人资料。对用户进行身份验证后,我想从我的 API 获取他们的个人资料数据并更新缓存,以便立即更新此挂钩中使用

currentProfile
的其他组件。

这是我的设置的简化版本:

  1. useCurrentProfile
    Hook:使用
    useQuery
    获取并缓存当前用户的个人资料。
  2. fetchCurrentProfile
    功能:获取配置文件数据并使用
    queryClient.fetchQuery
    更新缓存。

问题

调用

fetchQuery
后,useQuery中的
data
字段没有按预期更新。仅当我在
refetch
之后调用
fetchQuery
时,它才会更新,我想避免这种情况,因为它会触发额外的 API 调用。

import { useQuery, useQueryClient } from '@tanstack/react-query';
import auth from '@react-native-firebase/auth';
import { getProfile as _getProfile } from '@/src/api/auth';

const useCurrentProfile = () => {
  const queryClient = useQueryClient();

  const { data: currentProfile, error, isLoading } = useQuery({
    enabled: !!auth().currentUser?.uid,
    queryKey: ['currentProfile', { uid:  auth().currentUser?.uid}],
    queryFn: () => _getProfile({ uid: auth().currentUser?.uid, context: 'useQuery' }),
  });

  const fetchCurrentProfile = async () => {
    if (!auth().currentUser?.uid) return null;

  const data = await queryClient.fetchQuery({
    queryKey: ['currentProfile', { uid:  auth().currentUser?.uid}],
    queryFn: () => _getProfile({ uid: auth().currentUser?.uid, context: 'fetchQuery' }),
  });

  console.log(
    '[useCurrentProfile] getQueryData',
    queryClient.getQueryData(['currentProfile', { uid:  auth().currentUser?.uid}]),
  );

  // console.log('[useCurrentProfile] getQueryCache', queryClient.getQueryCache().getAll()); // this shows that there are 2 queryKeys: one with ['currentProfile', { uid: [USER_ID]}] and one with ['currentProfile', { uid:  undefined }]. My guess is that when instantiating useQuery, even though the enabled flag is false, it still creates a cache with queryKey = ['currentProfile', { uid:  undefined }].

  // refetchCurrentProfile().then().catch(); // currentProfile updates only after refetch, but this triggers another, unnecessary, API fetch (because `fetchQuery` already made one).


    return data;
  };

  return {
    currentProfile,
    fetchCurrentProfile,
    error,
    isLoading,
  };
};

我尝试过的事情

  • 我确认
    fetchQuery
    已成功检索个人资料数据。
  • 调用
    fetchQuery
    后,我用
    queryClient.getQueryData
    检查了缓存,这表明数据在那里,对于
    queryKey=['currentProfile', { uid: [USER_ID]}]
  • 调用
    fetchQuery
    后,我用
    queryClient
    检查了
    queryClient.getQueryCache().getAll()
    的缓存,发现有2个缓存:一个带有
    queryKey=['currentProfile', { uid: [USER_ID]}]
    ,一个带有
    queryKey=['currentProfile', { uid:  undefined }]
    。我的猜测是,在实例化
    useQuery
    时,即使
    enabled
    标志为 false,它仍然会使用
    queryKey=['currentProfile', { uid:  undefined }]
    创建缓存,这就是为什么使用正确的
    fetchQuery
    调用
    uid
    不起作用,因为它只更新正确的缓存,而不是未定义的缓存。
  • 调用 refetch 会更新 useQuery 的数据字段,但这会增加不需要的额外 API 调用。

预期行为:

调用 fetchCurrentProfile 后,我希望 useQuery 使用缓存中的新配置文件数据更新其数据字段,而无需额外的 API 调用。

问题:

  1. 为什么在 fetchQuery 和 setQueryData 之后 useQuery 不更新数据?
  2. 有没有办法手动触发 useQuery 的重新渲染来识别更新的缓存,而不触发新的 queryFn 调用?

附加信息:

{
  "@react-native-firebase/app": "21.3.0",
  "@react-native-firebase/auth": "21.3.0",
  "@tanstack/react-query": "5.59.20",
  "expo": "51.0.39",
  "react": "18.2.0",
  "react-native": "0.74.5",
}

react-native firebase-authentication expo react-query tanstackreact-query
1个回答
0
投票

我认为您没有正确订阅 firebase 身份验证状态。从技术上讲,您还对一个

queryFn
使用了两个不同的
queryKey

我认为你可以尝试这个建议:

import { getProfile as _getProfile } from '@/src/api/auth';
import auth from '@react-native-firebase/auth';
import { queryOptions, skipToken, useQuery, useQueryClient } from '@tanstack/react-query';

const profileQueryOptions = (uid: string | null, meta: { queryContext: string }) =>
  queryOptions({
    queryKey: ['currentProfile', { uid }],
    queryFn: uid ? (ctx) => _getProfile({ uid, context: ctx.meta?.queryContext }) : skipToken,
    meta,
  });

const useCurrentProfile = () => {
  const [user, setUser] = React.useState();
  const queryClient = useQueryClient();

  const { data: currentProfile, error, isLoading } = useQuery(profileQueryOptions(user?.uid ?? null, { queryContext: 'useQuery' }));

  const fetchCurrentProfile = async () => {
    const _auth = auth();

    if (_auth.currentUser?.uid) return null;

    const data = await queryClient.fetchQuery(profileQueryOptions(_auth.currentUser?.uid ?? null, { queryContext: 'fetchQuery' }));

    console.log('[useCurrentProfile] getQueryData', queryClient.getQueryData(['currentProfile', { uid: _auth.currentUser?.uid }]));

    return data;
  };

  function onAuthStateChanged(user) {
    setUser(user);
  }

  React.useEffect(() => {
    const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
    return subscriber; // unsubscribe on unmount
  }, []);

  return {
    currentProfile,
    fetchCurrentProfile,
    error,
    isLoading,
  };
};

我认为即使有了订阅,你也可以摆脱

fetchCurrentProfile
,具体取决于你如何使用这个钩子下游的东西。

© www.soinside.com 2019 - 2024. All rights reserved.