I'm using React Query to manage user profiles in my React Native app with Firebase. After authenticating a user, I want to fetch their profile data from my API and update the cache so that other components using currentProfile from this hook are immediately updated.
Here’s a simplified version of my setup:
useCurrentProfileHook: Fetches and caches the current user’s profile usinguseQuery.fetchCurrentProfileFunction: Fetches the profile data and updates the cache withqueryClient.fetchQuery.
The problem
After calling fetchQuery, the data field in useQuery does not update as expected. It only updates if I call refetch right after fetchQuery, which I want to avoid because it would trigger an additional API call.
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,
};
};
What I've Tried
- I confirmed that
fetchQuerysuccessfully retrieves the profile data. - After calling
fetchQuery, I checked the cache withqueryClient.getQueryData, which shows the data is there, forqueryKey=['currentProfile', { uid: [USER_ID]}]. - After calling
fetchQuery, I checked thequeryClient's cache withqueryClient.getQueryCache().getAll()and discovered that there were 2 caches: one withqueryKey=['currentProfile', { uid: [USER_ID]}]and one withqueryKey=['currentProfile', { uid: undefined }]. My guess is that when instantiatinguseQuery, even though theenabledflag is false, it still creates a cache withqueryKey=['currentProfile', { uid: undefined }]and that's why callingfetchQuerywith the correctuiddoes not work, because it only updates the correct cache, not the undefined one. - Calling refetch updates useQuery’s data field, but this adds an unwanted extra API call.
Expected Behavior:
After calling fetchCurrentProfile, I expect useQuery to update its data field with the new profile data from the cache without needing an additional API call.
Questions:
- Why doesn’t useQuery update data after fetchQuery and setQueryData?
- Is there a way to manually trigger a re-render for useQuery to recognize the updated cache, without triggering a new queryFn call?
Additional Info:
{
"@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",
}