我正在使用 React Query 通过 Firebase 管理我的 React Native 应用程序中的用户个人资料。对用户进行身份验证后,我想从我的 API 获取他们的个人资料数据并更新缓存,以便立即更新此挂钩中使用
currentProfile
的其他组件。
这是我的设置的简化版本:
useCurrentProfile
Hook:使用 useQuery
获取并缓存当前用户的个人资料。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
不起作用,因为它只更新正确的缓存,而不是未定义的缓存。调用 fetchCurrentProfile 后,我希望 useQuery 使用缓存中的新配置文件数据更新其数据字段,而无需额外的 API 调用。
{
"@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",
}
我认为您没有正确订阅 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
,具体取决于你如何使用这个钩子下游的东西。