AsyncList
View as MarkdownLoads, filters and pages a list of items asynchronously, without any DOM. A thin wrapper around the machine of @zag-js/async-list.
AsyncList has no rendering of its own: you create it with a load function, subscribe to it and render the items yourself. The Combobox Async Search example uses it to fill the list from an Extbase action while the user types.
import { debounce } from '@zag-js/utils';
import { AsyncList, extbase } from 'fluid-primitives';
interface City {
value: string;
title: string;
}
const list = new AsyncList<City>({
load: async ({ filter, signal }) => {
const response = await extbase.post(searchUrl, { q: filter }, { signal });
return { items: (await response.json()) as City[] };
},
});
list.subscribe(api => {
renderItems(api.items);
spinnerEl.hidden = !api.isLoading;
});
const setFilter = debounce((filter: string) => list.setFilter(filter), 300);
inputEl.addEventListener('input', () => setFilter(inputEl.value));
list.init();
setFilter() reloads the list straight away. Debouncing it is up to you, so you decide the delay and can skip it where it isn't needed, e.g. when the list only reloads from dependencies. Call list.destroy() when you are done with it.
The options of an AsyncList: the props of the @zag-js/async-list machine.
| Name | Description | Required | Default |
|---|---|---|---|
load | (args: LoadDetails<Item, Filter, Sorting, Cursor>) => Promise<LoadResult<Item, Cursor>>The function to call when the list is loaded | Yes | - |
sort | ((args: SortDetails<Item, Filter, Sorting>) => Promise<{ items: Item[]; }> | { items: Item[]; } | undefined) | undefinedThe function to call when the list is sorted client-side. When provided, sorting is performed locally instead of triggering a new load. | No | - |
initialItems | Item[] | undefinedThe initial items to display | No | - |
initialSorting | Sorting | undefinedThe initial sorting state | No | - |
initialFilter | Filter | undefinedThe initial filter state | No | - |
keepPreviousItems | boolean | undefinedWhether to keep the previous items visible while new data is loading. Prevents content flash when filter or sort changes. | No | false |
dependencies | LoadDependency[] | undefinedThe dependencies to watch for changes | No | - |
autoReload | boolean | undefinedWhether to automatically reload the list when the dependencies change | No | - |
onSuccess | ((details: { items: Item[]; }) => void | undefined) | undefinedThe function to call when the list is loaded successfully | No | - |
onError | ((details: { error: Error; }) => void | undefined) | undefinedThe function to call when the list fails to load | No | - |
Wraps the machine of @zag-js/async-list for use without any DOM or hydration: construct it, call init(), subscribe() to state changes and destroy() when done. Rendering is yours, do it from inside subscribe().
Debouncing or throttling setFilter() is left to the caller, wrap the call site with the debounce or throttle of @zag-js/utils, which this library already depends on.
class AsyncList<T, Filter = string, Sorting = asyncList.SortDescriptor<T>, Cursor = string>
Constructor
Permalink to heading "Constructor"| Name | Description | Required | Default |
|---|---|---|---|
options | AsyncListOptions<T, Filter, Sorting, Cursor> | Yes | - |
Properties
Permalink to heading "Properties"| Name | Description |
|---|---|
api | asyncList.Api<T, Filter, Sorting, Cursor>The api of the current state of the list. It is read fresh on every access, don't hold on to it. |
subscribe()
Calls fn with the api on every future state change. It does not fire for the current state, read api first if you need it.
subscribe(fn: (api: asyncList.Api<T, Filter, Sorting, Cursor>) => void): () => void
| Name | Description | Required | Default |
|---|---|---|---|
fn | (api: asyncList.Api<T, Filter, Sorting, Cursor>) => voidThe listener. | Yes | - |
Returns () => void. A function that removes the listener.
setFilter()
Sets the filter and reloads the list with it.
setFilter(filter: Filter): void
| Name | Description | Required | Default |
|---|---|---|---|
filter | Filter | Yes | - |
updateProps()
Changes options at runtime, e.g. dependencies to trigger an autoReload. load is fixed when the list is constructed and can't be swapped here.
updateProps(newProps: Partial<Omit<asyncList.Props<T, Filter, Sorting, Cursor>, 'load'>>): void
| Name | Description | Required | Default |
|---|---|---|---|
newProps | Partial<Omit<asyncList.Props<T, Filter, Sorting, Cursor>, 'load'>> | Yes | - |
init()
Starts the machine, which loads the list for the first time. Call it once, after constructing.
init(): void
destroy()
Stops the machine and cancels a load that is in flight.
destroy(): void
The api object
Permalink to heading "The api object"list.api and the argument of a subscribe() listener are the api of the machine:
Machine JavaScript API
Permalink to heading "Machine JavaScript API"| Name | Type | Description |
|---|---|---|
items | Item[] | The items in the list. |
filter | Filter | The current filter state. |
cursor | Cursor | undefined | The cursor for pagination. |
sorting | Sorting | undefined | The current sorting state. |
isLoading | boolean | Whether the list is loading (includes both fetching and sorting). |
isLoadingMore | boolean | Whether the list is loading more items via pagination. |
isSorting | boolean | Whether a client-side sort is in progress. |
isEmpty | boolean | Whether the list is empty. |
hasMore | boolean | Whether there are more items to load. |
error | Error | undefined | The error instance returned by the last fetch. |
abort | VoidFunction | Function to abort the current fetch. |
reload | VoidFunction | Function to reload the list |
loadMore | VoidFunction | Function to load more items |
setSorting | (sorting: Updater<Sorting | undefined>) => void | Function to update the sorting state and trigger a sort. Accepts a value or an updater function. |
setFilter | (filter: Updater<Filter>) => void | Function to update the filter state and trigger a reload. Accepts a value or an updater function. |
clearFilter | VoidFunction | Function to reset the filter to `initialFilter`. No-op if `initialFilter` was not provided. |