-
Hello everyone, I'm using tanstack-query v4 ( I invalidate the messages by calling: MessagesQuery.invalidate() // this call will trigger queryClient.invalidateQueries(keys.all) However, there's a case in which the query function gets called, but the query data remains stale That happens when the user isn't on the chat screen (query is inactive). Below you can see how my MessagesQuery looks like: // This is the maximum number of pages to keep in the infinite query
const maxPagesToKeep = 3;
export namespace MessagesQuery {
export const keys = {
all: ["communication/useMessages"] as const,
channel: (channelId: string, messageId = 0) =>
[...keys.all, { channelId }, { messageId }] as const,
};
export const invalidate = (
channelId?: string,
filters?: InvalidateQueryFilters<readonly Message[]>,
) => {
if (!channelId) {
return queryClient.invalidateQueries(keys.all);
}
// Cut off redundant pages from the infinite query
// This way we won't have to wait for all the pages to refetch when we invalidate the query
// Thus the user will see all UI updates faster
InfiniteQueryService.cutOffRedundantPagesFromInfiniteQuery(
() => MessagesQuery.getData(channelId),
(data) => MessagesQuery.setData(keys.channel(channelId), data),
maxPagesToKeep,
);
return queryClient.invalidateQueries({
queryKey: keys.channel(channelId),
...filters,
});
};
type QueryData = InfiniteData<readonly Message[]>;
export const getData = (channelId: string, messageId?: number) => {
return queryClient.getQueryData<QueryData>(
keys.channel(channelId, messageId),
);
};
export const setData = (key: readonly unknown[], data: QueryData) => {
return queryClient.setQueryData<QueryData>(key, data);
};
export const pageLimit = 15;
export const useQuery = (
channelId: string,
messageId?: number,
options: UseInfiniteQueryOptions<readonly Message[]> = = {
enabled: true,
}
) => {
const isDisconnected = useIsDisconnected();
const queryInfo = useInfiniteQuery<readonly Message[]>({
...options,
queryKey: keys.channel(channelId, messageId),
enabled:
!isDisconnected && options?.enabled && Boolean(api.getToken()),
getNextPageParam: (lastPage): number | undefined => {
if (!lastPage) return undefined;
return last(lastPage)?.id;
},
queryFn: async ({ pageParam: lastMessageId }) => {
const messages = await api.communication.getMessages(
channelId,
pageLimit,
lastMessageId,
messageId,
);
// After messages have been fetched, check if channels need to be invalidated
// Use case: When a user receives a notification, channels need to refetch after the messages from a channel have been fetched,
// since the channel.unreadCount will be updated only after the messages have been fetched
// otherwise the unread count will be incorrect
const notificationFlags = useNotificationFlagsStore.getState();
if (notificationFlags.value.invalidateChannels) {
ChannelsQuery.invalidate();
notificationFlags.update({ invalidateChannels: false });
}
const orderedMessages = [];
for (let i = 0; i < messages.length; i++) {
const firstMessage = messages[i];
const secondMessage = messages[i + 1];
if (!secondMessage) {
orderedMessages.push(firstMessage);
break;
} else if (
// This is necessary since the FlatList is inverted and system messages are displayed after the regular messages
firstMessage.createdAt === secondMessage.createdAt &&
firstMessage.isSystemMessage &&
!secondMessage.isSystemMessage
) {
orderedMessages.push(secondMessage);
orderedMessages.push(firstMessage);
i++;
} else {
orderedMessages.push(firstMessage);
}
}
return orderedMessages;
},
});
return {
...queryInfo,
data: {
...queryInfo.data,
pages: queryInfo.data?.pages.flat() ?? [],
},
};
};
} When I add a log to display the newest message in the queryFn I can see that the new data is fetched, while the same log on queryClient configuration: export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: INTERVALS.ms1h * 5,
cacheTime: INTERVALS.ms1h * 5,
keepPreviousData: true,
},
},
});
export const persister = createAsyncStoragePersister({
storage: AsyncStorage,
}); From what I could understand, it looks like the |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
I was able to solve the issue. It was because next page was fetching while the whole query was refeteched. Here's the whole breakdown
I solved the issue by adding a check that would only attach onEndReached={hasTransitionEnded ? fetchMoreMessages : null} |
Beta Was this translation helpful? Give feedback.
I was able to solve the issue. It was because next page was fetching while the whole query was refeteched. Here's the whole breakdown
onEndReached
callback that callsmessagesQuery.fetchNextPage
onEndReachedCb
was called during transition, every time the screen was opened. This resulted in many redundant pages being fetched, even though the user didn't scroll nearly to the end