On This Page
Creating
Value
Faces
Refreshing
Teardown
Resource
A resource is a Signal whose value an async fetcher produces. It tracks the signals the fetcher reads, refetches when they change, and exposes fetch status through three independent faces (loading, error, and settled) while the value itself holds last-good through a refresh and through a rejection.
Creating
resource
resource(fetcher, options);Creates a resource and runs the fetcher immediately. The fetcher runs as an async reaction: signals read before its first await are tracked, and changing one re-fires the fetch. It receives the backing Reaction as its argument, so comp.abortSignal cancels superseded IO and comp.track() re-enters tracking after an await. A synchronous (non-promise) return settles at once, without a fetch ever going in flight.
Parameters
| Name | Type | Description |
|---|---|---|
| fetcher | function | Produces the value, receiving the backing computation |
| options | object | Optional configuration |
Options
| Name | Type | Default | Description |
|---|---|---|---|
| initialValue | any | undefined |
The value get() returns before the first settle |
| onError | function | undefined |
Called with the rejection error after the faces settle. Superseded runs never report |
| concurrency | 'latest' | 'overlap' |
'latest' |
'latest' serializes runs (abort and coalesce), 'overlap' starts every fetch at once with newest-settle-wins for fetchers that can’t cooperate with cancellation |
| equality | function | deep equality | Becomes the refresh dedup, so a refetch producing an equal value wakes no value readers |
| safety | 'clone' | 'reference' | 'none' |
'reference' |
Value-protection preset. See Signal Options |
| id | function | id ?? _id ?? hash ?? key |
Resolves an array item’s identity for getItem and the collection helpers |
| context | object | undefined |
Debugging context surfaced in tracing output |
Usage
import { resource, signal } from '@semantic-ui/reactivity';
const query = signal('');
const results = resource(async (comp) => { const term = query.get(); // tracked, refetches when query changes if (term.length < 2) { return []; // sync return settles now, no fetch in flight } const res = await fetch(`/search?q=${term}`, { signal: comp.abortSignal }); return res.json();}, { initialValue: [] });By default (concurrency: 'latest'), runs never overlap. When a tracked read changes or refresh() fires while a fetch is in flight, that run’s abortSignal aborts and a superseded run drops its settle entirely, with no value, no error, and no face flip from a stale run. Supersession is signaled once the fetch is in flight, so a write from inside the fetcher’s own synchronous head re-fires the fetch and converges on the latest value rather than dropping. The latest run wins.
By default (concurrency: 'latest'), runs never overlap. When a tracked read changes or refresh() fires while a fetch is in flight, that run’s abortSignal aborts and a superseded run drops its settle entirely, with no value, no error, and no face flip from a stale run. Supersession is signaled once the fetch is in flight, so a write from inside the fetcher’s own synchronous head re-fires the fetch and converges on the latest value rather than dropping. The latest run wins.
In the default 'latest' mode the abort is cooperative. A fetcher that ignores comp.abortSignal and returns a promise that never settles blocks the next refetch, since runs never overlap. Pass the abort signal to your fetch or timers so a superseded run can actually cancel, or set concurrency: 'overlap' so an uncancellable fetch never blocks the next one.
In the default 'latest' mode the abort is cooperative. A fetcher that ignores comp.abortSignal and returns a promise that never settles blocks the next refetch, since runs never overlap. Pass the abort signal to your fetch or timers so a superseded run can actually cancel, or set concurrency: 'overlap' so an uncancellable fetch never blocks the next one.
Concurrency
concurrency sets how a refetch behaves when one fires while a fetch is still in flight.
'latest' (the default) serializes runs as described above. The in-flight run aborts, the refetch coalesces into one re-run after it settles, at most one fetch is ever in flight, and settled() tracks it.
'overlap' starts every fetch immediately. Concurrent fetches are allowed and the newest run’s settle wins, so an out-of-order arrival never clobbers the latest value and the loading face stays true until the newest run settles. A superseded run still aborts its abortSignal, but progress never waits on that cooperation. Reach for it when a fetcher cannot thread cancellation and a hung request must not block the next refetch. Two costs come with it: concurrent fetches under rapid churn, and overlap runs are invisible to settled() because the backing reaction stays synchronous.
Example
Value
get
resource.get();Returns the produced value and subscribes the running reaction, the same tracked read as any Signal. It returns initialValue before the first settle, then the last fulfilled payload, held across a refresh in flight and across a rejection. A resource is a Signal, so peek(), getItem() (with the id option), and equality dedup all apply.
Returns
The current value.
Faces
Each face is an independently reactive read that re-runs only at its own transitions, so a reader of one face is untouched by changes to the others.
loading
resource.loading;true while a fetch is in flight, false otherwise. A synchronous fetcher return never flips it. A skeleton state is loading && !settled, a refresh shimmer is loading && settled.
error
resource.error;The error from the most recent rejected fetch, or undefined when the last settle fulfilled. Cleared on the next fulfilled settle. Rejections land here and never reach console.error or surface as unhandled rejections.
settled
resource.settled;Latches true after the first completed fetch (fulfilled or rejected) and stays true for the resource’s life. Its reader re-runs once, at that first settle.
Refreshing
refresh
resource.refresh();Re-fires the fetcher by invalidating the backing reaction. The stored value holds until the refresh settles, so readers keep the last-good value through the reload.
Usage
const feed = resource(async (comp) => { const res = await fetch('/feed', { signal: comp.abortSignal }); return res.json();});
feed.refresh(); // reload, get() stays on the last payload until it landsTeardown
stop
resource.stop();Permanently stops the resource. Re-fires end, the loading face clears (a stopped resource can never be fetching), and the last value stays readable. A resource created inside a reaction stops with its parent, and an unreferenced handle self-stops once nothing holds it. Idempotent.
settled() from the package waits for in-flight fetches, resolving once every resource and reaction is quiet.
settled() from the package waits for in-flight fetches, resolving once every resource and reaction is quiet.
Usage
const feed = resource(async () => (await fetch('/feed')).json());
feed.stop();feed.get(); // still the last value, but no longer refetches