I’m using @tanstack/react-query with the useInfiniteQuery hook to fetch paginated data for a list of items. The infinite query works fine for normal pagination and scrolling. However, when I change the searchQuery, the data is cleared and a loading spinner is shown until the new data arrives.
Goal: I want to keep showing the previous data on the screen while the new data is being fetched in the background after changing the searchQuery. I know useQuery supports keepPreviousData, but useInfiniteQuery does not have this option.
const fetchPaginatedIngredients = async ({
pageParam = 1,
queryKey,
}: QueryFunctionContext<QueryKey>) => {
const [, searchQuery] = queryKey;
const response = await axiosInstance.get("/api/ingredients", {
params: { query: searchQuery, page: pageParam, limit: 20 },
});
return response.data;
};
const SupplierIngredients = () => {
const [searchQuery, setSearchQuery] = useState("");
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
} = useInfiniteQuery({
queryKey: ["ingredients", searchQuery],
queryFn: fetchPaginatedIngredients,
getNextPageParam: (lastPage) => {
if (lastPage.currentPage < lastPage.totalPages) {
return lastPage.currentPage + 1;
}
return undefined;
},
staleTime: 1000 * 60 * 5,
retry: 3,
});
const ingredients = useMemo(() => {
return data?.pages.flatMap((page) => page.data) || [];
}, [data]);
return (
<div>
<input
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search ingredients"
/>
{isLoading && ingredients.length === 0 && <div>Loading...</div>}
<ul>
{ingredients.map((ingredient) => (
<li key={ingredient.id}>{ingredient.name}</li>
))}
</ul>
{hasNextPage && (
<button onClick={() => fetchNextPage()}>Load More</button>
)}
</div>
);
};