Fetching on input: the source form
useResource(source, fetcher) runs fetcher(value, info) for the current value of source() and again whenever it changes.
useResource(source, fetcher) runs fetcher(value, info) for the current value of source() and again
whenever it changes. While the source is false, null or undefined the resource does not fetch (it is
"unresolved", or "ready" if it already had a value). A plain, non-function source fetches once for that
value.
info is { signal, refetching, value }:
signalis anAbortSignalthat aborts when a newer fetch starts, when the source becomes empty, or when the owner is disposed. Pass it tofetchso the network request is canceled, not just ignored.refetchingistrueforrefetch()and for source changes after a value existed.valueis the last value, for fetchers that merge (pagination, patches).
A response that arrives after a newer fetch started is dropped, whether or not the fetcher honors the signal, so an out-of-order response can never overwrite a newer one.
src/search.tsxtsx
import { useResource, useStore } from "@shadow-js/core";
import { Show } from "@shadow-js/web";
export interface Backend {
find(term: string, signal: AbortSignal): Promise<string[]>;
}
export function Search(props: { backend: Backend }) {
const [term, setTerm] = useStore("");
// An empty term is falsy, so nothing is fetched for it.
const results = useResource(
() => term().trim() || false,
(value, { signal }) => props.backend.find(value, signal),
);
return (
<div>
<input aria-label="term" value={term()} onInput={(event) => setTerm(event.currentTarget.value)} />
<Show when={results.loading}>
<p>searching</p>
</Show>
<ul>{(results.latest ?? []).map((row) => <li>{row}</li>)}</ul>
</div>
);
}
(flush comes from @shadow-js/testing, which @shadow-js/testing/dom re-exports.)