useInfiniteQuery is an auto-generated hook for managing paginated data with infinite scroll or “load more” functionality.
Overview
For an endpoint defined with build.infiniteQuery, the generated hook will be useGetXxxInfiniteQuery.
export const pokemonApi = createApi({
baseQuery: fetchBaseQuery({ baseUrl: 'https://example.com' }),
endpoints: (build) => ({
getPokemon: build.infiniteQuery<Pokemon[], string, number>({
infiniteQueryOptions: {
initialPageParam: 1,
getNextPageParam: (lastPage, allPages, lastPageParam) =>
lastPageParam + 1,
getPreviousPageParam: (firstPage, allPages, firstPageParam) =>
firstPageParam > 1 ? firstPageParam - 1 : undefined,
},
query: ({ queryArg, pageParam }) => `/type/${queryArg}?page=${pageParam}`,
}),
}),
});
export const { useGetPokemonInfiniteQuery } = pokemonApi;
Signature
const result = useXxxInfiniteQuery(arg, options?);
arg
QueryArg | Signal<QueryArg> | (() => QueryArg)
required
The query argument (like type name, search term, etc.)
Optional configuration objectShow Infinite query options
Skip this query execution if true
refetchOnMountOrArgChange
Refetch on mount or when arg changes
Select a subset of the result
Return Value
data
Signal<InfiniteData<ResultType> | undefined>
Object containing:
pages: ResultType[] - Array of page results
pageParams: PageParam[] - Array of page parameters used
true if loading the first page
true if fetching any page
true if fetching the next page
true if fetching the previous page
true if there is a next page available
true if there is a previous page available
Function to fetch the next page
Function to fetch the previous page
Function to refetch all pages
Usage Examples
import { useGetPokemonInfiniteQuery } from './api';
@Component({
selector: 'app-pokemon-list',
template: `
@if (pokemonQuery.isLoading()) {
<p>Loading...</p>
}
@for (pokemon of allResults(); track pokemon.id) {
<div>{{ pokemon.name }}</div>
}
@if (pokemonQuery.hasNextPage()) {
<button
[disabled]="pokemonQuery.isFetchingNextPage()"
(click)="loadMore()">
{{ pokemonQuery.isFetchingNextPage() ? 'Loading...' : 'Load More' }}
</button>
}
`,
})
export class PokemonListComponent {
pokemonQuery = useGetPokemonInfiniteQuery('fire');
allResults = computed(() =>
this.pokemonQuery.data()?.pages.flat() ?? []
);
loadMore() {
this.pokemonQuery.fetchNextPage();
}
}
import { viewChild, afterNextRender } from '@angular/core';
@Component({
selector: 'app-pokemon-list',
template: `
@for (pokemon of allResults(); track pokemon.id) {
<div>{{ pokemon.name }}</div>
}
<div #sentinel class="scroll-sentinel">
@if (pokemonQuery.isFetchingNextPage()) {
<p>Loading more...</p>
}
</div>
`,
})
export class PokemonListComponent {
pokemonQuery = useGetPokemonInfiniteQuery('fire');
sentinel = viewChild<ElementRef>('sentinel');
allResults = computed(() =>
this.pokemonQuery.data()?.pages.flat() ?? []
);
constructor() {
afterNextRender(() => {
const observer = new IntersectionObserver((entries) => {
if (entries[0].isIntersecting && this.pokemonQuery.hasNextPage()) {
this.pokemonQuery.fetchNextPage();
}
});
if (this.sentinel()?.nativeElement) {
observer.observe(this.sentinel()!.nativeElement);
}
});
}
}
@Component({
template: `
@if (pokemonQuery.hasPreviousPage()) {
<button (click)="pokemonQuery.fetchPreviousPage()">
Load Previous
</button>
}
@for (pokemon of allResults(); track pokemon.id) {
<div>{{ pokemon.name }}</div>
}
@if (pokemonQuery.hasNextPage()) {
<button (click)="pokemonQuery.fetchNextPage()">
Load Next
</button>
}
`,
})
export class PokemonListComponent {
pokemonQuery = useGetPokemonInfiniteQuery('fire');
allResults = computed(() =>
this.pokemonQuery.data()?.pages.flat() ?? []
);
}
Accessing Page Parameters
export class PokemonListComponent {
pokemonQuery = useGetPokemonInfiniteQuery('fire');
pageInfo = computed(() => {
const data = this.pokemonQuery.data();
if (!data) return null;
return {
totalPages: data.pages.length,
pageParams: data.pageParams,
lastPage: data.pageParams[data.pageParams.length - 1],
};
});
}
Data Structure
{
pages: [
[{ id: 1, name: 'Charmander' }, ...], // Page 1
[{ id: 11, name: 'Charmeleon' }, ...], // Page 2
[{ id: 21, name: 'Charizard' }, ...], // Page 3
],
pageParams: [1, 2, 3]
}
Use computed() to flatten the pages array for easier template usage:allResults = computed(() => this.query.data()?.pages.flat() ?? []);
Refetching All Pages
export class PokemonListComponent {
pokemonQuery = useGetPokemonInfiniteQuery('fire');
refreshAll() {
// Refetches all loaded pages
this.pokemonQuery.refetch();
}
}
refetch() will re-fetch ALL loaded pages, not just the first page. For large lists, this can be expensive.
See Also