Skip to content
Hooks

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...
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

@paraminitialValue The initial value of the observable or a function that returns the initial value

@seehttps://www.legendapp.com/dev/state/react/#useObservable

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 QueryClientProvidergetQueryClient() injects the client from context.

@paramoptions - Plain object, per-field Observable, or outer Observable of options.

@returnsObservable reflecting query state (incl. fetchNextPage/fetchPreviousPage/refetch).

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

@paraminitialValue The initial value of the observable or a function that returns the initial value

@seehttps://www.legendapp.com/dev/state/react/#useObservable

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 { 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>;
}

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>;
}
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;

View on GitHub