Kanzo UI
Hooks

useAsyncCollection

Items fetched as the user types — a debounced query, a request that aborts when a newer one starts, and the loading, empty and error states of the last one.

0 offered

Usage

import { useAsyncCollection } from "@kanzo-tech/ui";
const { collection, setQuery, loading, empty } = useAsyncCollection({
  load: (query, signal) => fetch(`/tags?q=${query}`, { signal }).then((r) => r.json()),
});

<Combobox collection={collection} onInputValueChange={(d) => setQuery(d.inputValue)}>

Filtering is the server's. The Combobox machine never mutates the collection it is given, so a list that is fetched needs nothing from it but the input text — which is what setQuery takes. load is called with an empty query on mount, so a list can be offered before anything is typed, and a request the user has already typed past is aborted rather than merely ignored: pass signal to fetch.

The machine underneath is Ark's useAsyncList. What this adds is the pause before it runs, the ListCollection a Combobox, Select or Listbox takes, and labelOf.

Loading, empty and error

loading is a request in flight; the previous items stay in collection until it lands, so the list does not blank between keystrokes. empty is the last request landing with nothing — it is false while loading, so a ComboboxEmpty gated on it never says "no results" over a request that has not answered. error is the last failure, and the previous items stay too. Where those are drawn is yours: the parts do not draw a spinner.

Multiple, and custom values

multiple is a prop of the Combobox, not of the hook. What it needs from the hook is labelOf: a selected value's label exists only in the batch that offered it, and the next keystroke replaces the batch, so a chip drawn from collection turns back into its raw value the moment the user types again.

A value nobody offered — a reference someone types in full — is allowCustomValue, and it reaches you through onInputValueChange. If writing it is expensive, pair it with useDebouncedCommit:

Committed: nothing yet

API Reference

useAsyncCollection

function useAsyncCollection<T extends CollectionItem>(
  options: AsyncCollectionOptions<T>,
): AsyncCollection<T>;

Options

OptionTypeDefault
load(query: string, signal: AbortSignal | undefined) => Promise<T[]>required
itemToValue(item: T) => stringitem.value
itemToString(item: T) => stringitem.label
debouncenumber — milliseconds typing must pause before load runs250
initialItemsT[]none

Returns

NameTypeWhat it is
collectionListCollection<T>What to hand the control. Rebuilt when a load lands.
setQuery(query: string) => voidWire to onInputValueChange.
loadingbooleanA request is in flight.
emptybooleanThe last request landed and offered nothing.
errorunknownThe last failure.
reload() => voidAsk again for the current query.
labelOf(value: string) => string | undefinedThe label of any value this hook has offered.

On this page