useInfiniteQuery
Bridges TanStack Query infinite queries with Legend-State. Returns query state as an Observable, and accepts Observable values in options and queryKey — just like useQuery. Refer to TanStack Query docs for full option details. Options like enabled, staleTime, gcTime, etc. accept MaybeObservable<T> for reactive control.
Both variants require a QueryClientProvider ancestor — the client is injected from context.
useInfiniteQuery
Loading
Paginated list — load more pages with a button.
Pages
0
Items
0
Has More
No
Loading first page...
Basic infinite query
Section titled “Basic infinite query”import { function For<T, TProps>({ each, optimized: isOptimized, item, itemProps, sortValues, children, }: { each?: ObservableParam<T[] | Record<any, T> | Map<any, T>>; optimized?: boolean; item?: FC<ForItemProps<T, TProps>>; itemProps?: TProps; sortValues?: (A: T, B: T, AKey: string, BKey: string) => number; children?: (value: Observable<T>, id: string | undefined) => ReactElement;}): ReactElement | null (+1 overload)
For, function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show, function useObservable<T>(): Observable<T | undefined> (+3 overloads)
A React hook that creates a new observable
useObservable } from "@usels/core";import { const useInfiniteQuery: <TQueryFnData = unknown, TQueryKey extends QueryKey = ReadonlyArray<unknown>, TPageParam = unknown>(options?: DeepMaybeObservable<CreateInfiniteQueryOptions<TQueryFnData, TQueryKey, TPageParam>>) => Observable<InfiniteQueryState<InfiniteData<TQueryFnData>>>
useInfiniteQuery } from "@usels/tanstack-query";
interface interface Page
Page { Page.items: {}
items: { id: number
id: number; name: string
name: string }[]; Page.nextCursor: number | null
nextCursor: number | null;}
function function InfiniteList(): JSX.Element
InfiniteList() { const const query: any
query = useInfiniteQuery<unknown, ReadonlyArray<unknown>, unknown>(options?: DeepMaybeObservable<CreateInfiniteQueryOptions<unknown, ReadonlyArray<unknown>, unknown>>): Observable<InfiniteQueryState<InfiniteData<unknown, unknown>>>
Core observable function for bridging TanStack Query infinite queries with Legend-State.
Must be called inside a scope wrapped by a QueryClientProvider —
getQueryClient() injects the client from context.
useInfiniteQuery({ queryKey: {}
queryKey: ["items"], queryFn: ({ pageParam }: { pageParam: any;}) => any
queryFn: ({ pageParam: any
pageParam }) => any
fetch(`/api/items?cursor=${pageParam: any
pageParam}`).any
then((r: any
r) => r: any
r.any
json() as type Promise = /*unresolved*/ any
Promise<interface Page
Page>), initialPageParam: number
initialPageParam: 0, getNextPageParam: (lastPage: any) => any
getNextPageParam: (lastPage: any
lastPage) => lastPage: any
lastPage.any
nextCursor, });
const const allItems$: any
allItems$ = useObservable<unknown>(value: Promise<unknown> | (() => unknown) | unknown, deps?: DependencyList): any (+3 overloads)
A React hook that creates a new observable
useObservable(() => (const query: any
query.any
data.any
get()?.any
pages ?? []).any
flatMap((page: any
page) => page: any
page.any
items) );
return ( <JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const query: any
query.any
isLoading}> <JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p>Loading...</JSX.IntrinsicElements.p: DetailedHTMLProps<HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p> </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> <function For<T, TProps>({ each, optimized: isOptimized, item, itemProps, sortValues, children, }: { each?: ObservableParam<T[] | Record<any, T> | Map<any, T>>; optimized?: boolean; item?: FC<ForItemProps<T, TProps>>; itemProps?: TProps; sortValues?: (A: T, B: T, AKey: string, BKey: string) => number; children?: (value: Observable<T>, id: string | undefined) => ReactElement;}): ReactElement | null (+1 overload)
For each?: any
each={const allItems$: any
allItems$}>{(item$: any
item$) => <JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>{item$: any
item$.any
name.any
get()}</JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>}</function For<T, TProps>({ each, optimized: isOptimized, item, itemProps, sortValues, children, }: { each?: ObservableParam<T[] | Record<any, T> | Map<any, T>>; optimized?: boolean; item?: FC<ForItemProps<T, TProps>>; itemProps?: TProps; sortValues?: (A: T, B: T, AKey: string, BKey: string) => number; children?: (value: Observable<T>, id: string | undefined) => ReactElement;}): ReactElement | null (+1 overload)
For> <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const query: any
query.any
hasNextPage}> <JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button DOMAttributes<HTMLButtonElement>.onClick?: MouseEventHandler<HTMLButtonElement> | undefined
onClick={() => const query: any
query.any
fetchNextPage()} ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled={const query: any
query.any
isFetchingNextPage.any
get()} > <function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show if: any
if={const query: any
query.any
isFetchingNextPage} else?: any
else="Load More"> Loading more... </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> </JSX.IntrinsicElements.button: DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button> </function Show<T>(props: Props<T>): ReactElement (+2 overloads)
Show> </JSX.IntrinsicElements.div: DetailedHTMLProps<HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div> );}import { For, observable, Show } from "@usels/core";import { createInfiniteQuery } from "@usels/tanstack-query";
interface Page { items: { id: number; name: string }[]; nextCursor: number | null;}
function InfiniteList() { "use scope"; const state$ = createInfiniteQuery<Page, ["items"], number>({ queryKey: ["items"], queryFn: ({ pageParam }) => fetch(`/api/items?cursor=${pageParam}`).then((r) => r.json()), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextCursor, });
const allItems$ = observable(() => (state$.data.get()?.pages ?? []).flatMap((page) => page.items) );
return ( <div> <Show if={state$.isLoading}> <p>Loading...</p> </Show> <For each={allItems$}>{(item$) => <div>{item$.name.get()}</div>}</For> <Show if={state$.hasNextPage}> <button onClick={() => state$.fetchNextPage.get()()} disabled={state$.isFetchingNextPage.get()} > <Show if={state$.isFetchingNextPage} else="Load More"> Loading more... </Show> </button> </Show> </div> );}Observable queryKey
Section titled “Observable queryKey”import { useInfiniteQuery } from "@usels/tanstack-query";import { observable } from "@usels/core";
const category$ = observable("all");
function FilteredList() { const query = useInfiniteQuery({ queryKey: ["items", category$], queryFn: ({ pageParam }) => fetch(`/api/items?category=${category$.peek()}&cursor=${pageParam}`).then((r) => r.json() ), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextCursor, }); return <div>{query.data.get()?.pages.length} pages</div>;}import { createInfiniteQuery } from "@usels/tanstack-query";import { observable } from "@usels/core";
const category$ = observable("all");
function FilteredList() { "use scope"; const state$ = createInfiniteQuery({ queryKey: ["items", category$], queryFn: ({ pageParam }) => fetch(`/api/items?category=${category$.peek()}&cursor=${pageParam}`).then((r) => r.json() ), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.nextCursor, }); return <div>{state$.data.get()?.pages.length} pages</div>;}Conditional fetching with Observable enabled
Section titled “Conditional fetching with Observable enabled”import { useInfiniteQuery } from "@usels/tanstack-query";import { observable } from "@usels/core";
const isReady$ = observable(false);
function Feed() { const query = useInfiniteQuery({ queryKey: ["feed"], queryFn: ({ pageParam }) => fetchFeed(pageParam), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.next, enabled: isReady$, }); return <div>{query.status.get()}</div>;}import { createInfiniteQuery } from "@usels/tanstack-query";import { observable } from "@usels/core";
const isReady$ = observable(false);
function Feed() { "use scope"; const state$ = createInfiniteQuery({ queryKey: ["feed"], queryFn: ({ pageParam }) => fetchFeed(pageParam), initialPageParam: 0, getNextPageParam: (lastPage) => lastPage.next, enabled: isReady$, }); return <div>{state$.status.get()}</div>;}export type { CreateInfiniteQueryOptions, InfiniteQueryState } from "./core";export { createInfiniteQuery } from "./core";/** * UseInfiniteQueryOptions is an alias for CreateInfiniteQueryOptions for backward compatibility. */export type UseInfiniteQueryOptions<TQueryFnData = unknown, TQueryKey extends QueryKey = QueryKey, TPageParam = unknown> = CreateInfiniteQueryOptions<TQueryFnData, TQueryKey, TPageParam>;/** * Connects TanStack Query Infinite Query with Legend-State observables. * Uses InfiniteQueryObserver to manage query state as observables. */export type UseInfiniteQuery = typeof createInfiniteQuery;export declare const useInfiniteQuery: UseInfiniteQuery;