A resource is more than a value plus loading: first load, refresh, failure, and an optimistic
write make different promises to a view. AsyncData models those states; Effect and Fx still own
request execution, cancellation, and ordering.
Distinguish first load from refresh
Use one state for one resource, such as the currently selected profile. NoData means no result or
request yet. Loading means a first request is pending. Success and Failure describe completed
outcomes; each can also carry progress while a refresh or retry runs.
import * as AsyncData from "@typed/async-data"
type Profile = { readonly name: string }
const initial = AsyncData.NoData
const loading = AsyncData.loading()
const loaded = AsyncData.success<Profile>({ name: "Ada" })
const refreshing = AsyncData.startLoading(loaded)
refreshing is still a Success containing Ada, with progress marking pending work. If the request
then produces a Failure, that Failure does not retain Ada. Keeping stale data after a failed
refresh requires a separate policy. Do not infer these states from an empty value or a second
loading flag.
Render the state, including pending work
import * as AsyncData from "@typed/async-data"
type Profile = { readonly name: string }
const describeProfile = (data: AsyncData.AsyncData<Profile, string>) =>
AsyncData.match(data, {
NoData: () => "Choose a profile",
Loading: () => "Loading profile…",
Success: (profile, state) => `${profile.name}${state.progress ? " (refreshing)" : ""}`,
Failure: (_, state) => state.progress ? "Retrying profile…" : "Profile unavailable. Try again.",
Optimistic: (profile) => `${profile.name} (saving)`,
})
Matching the union keeps first load separate from refresh: the latter can display Ada while the
new request runs. getSuccess extracts an available value, including an optimistic value;
isSuccess identifies confirmed Success only.
Connect state to a request
AsyncData does not start requests. A producer decides when an input changes, which request to cancel, and which completion may publish. Build an asynchronous issue search shows those decisions together with loading, refresh, and retry in a working form.
For edits that display a proposed value before the server accepts it, continue with optimistic edits. The Optimistic state retains the state it replaced; that history is separate from a Success being refreshed.