useSequence()function
Subscribe to an async iterable for the lifetime of the component.
useSequence(sequence?: AsyncIterable<T>): T | undefined
React hooks and context helpers for integrating Shelving shelving/store, async sequences, and API/DB providers into React components. The module is built on useSyncExternalStore and standard React patterns — no magic, no global state.
Store.value implements the React Suspense contract directly: it throws a Promise while the store is loading (which Suspense catches and shows the fallback) and throws the error reason if the store has failed (which an error boundary catches). useStore() wires a store into useSyncExternalStore so the component re-renders whenever the store emits a new value.
createAPIContext() and createDBContext() are factory functions that return a React context component plus typed hooks. Each mounted context instance gets its own in-memory cache, so multiple subtrees can use independent providers. This mirrors the createDataContext() / createCacheContext() pattern used throughout the library.
A recurring React problem is stale closures and unnecessary re-renders caused by objects being recreated on every render. useInstance(), useLazy(), useReduce(), and useMap() each solve a specific variant of this: useInstance() memoises a class constructor call; useLazy() memoises an arbitrary factory call; useReduce() lets you fold render state with custom equality logic; useMap() gives you a single mutable Map that lives for the lifetime of the component.
The per-symbol pages below carry the detailed usage for each hook and context factory. This section shows how the pieces fit together for real tasks — start here, then follow the links for specifics.
Create a context once at module scope, wrap the tree in it, and let child components suspend while data loads. The same shape works for createAPIContext() — swap DBContext.useItem() / DBContext.useQuery() for APIContext.useAPI().
import { Suspense } from "react";
import { createDBContext } from "shelving/react";
const { DBContext, useItem, useQuery } = createDBContext(dbProvider);
function UserCard({ id }: { id: string }) {
const user = useItem(Users, id).value; // Suspends while loading.
return <li>{user.name}</li>;
}
function UserList() {
const users = useQuery(Users, { $order: "name" }).value;
return <ul>{users.map(u => <UserCard key={u.id} id={u.id} />)}</ul>;
}
function App() {
return (
<DBContext>
<ErrorBoundary fallback={<p>Something went wrong.</p>}>
<Suspense fallback={<p>Loading…</p>}>
<UserList />
</Suspense>
</ErrorBoundary>
</DBContext>
);
}A store that depends on props can't live at module scope. useInstance() constructs it once and keeps it stable across renders, while useStore() subscribes the component to its updates.
import { useInstance, useStore } from "shelving/react";
import { BooleanStore } from "shelving/store";
function Toggle() {
const open = useInstance(BooleanStore); // Created once, stable across renders.
useStore(open); // Re-render whenever the store changes.
return <button onClick={() => open.toggle()}>{open.value ? "Open" : "Closed"}</button>;
}Subscribe to an async iterable for the lifetime of the component.
useSequence(sequence?: AsyncIterable<T>): T | undefined
Create a mutable Map that persists for the lifetime of the component.
useMap(): Map<K, V>
Use memoised value with reduction logic.
useReduce(reduce: (previous: T | undefined, ...a: A) => T, ...args: A): T
useReduce(reduce: (previous: T | undefined, ...a: A) => T | undefined, ...args: A): T | undefined
Subscribe to a Shelving Store instance to refresh this component when its value changes.
useStore(store: T): T
useStore(store?: T | undefined): T | undefined
Create a data context
createDBContext(provider: DBProvider<I, T>): DBContext<I, T>
Use a memoised class instance.
useInstance(Constructor: new (...a: A) => T, ...args: A): T
Create an API context.
createAPIContext(provider: APIProvider<P, R>): APIContext<P, R>
Use a memoised value with lazy initialisation.
useLazy(value: (...args: A) => T, ...args: A): T
useLazy(value: T, ...args: Arguments): T
useLazy(value: Lazy<T, A>, ...args: A): T
Bundle of hooks and a provider component returned by createDBContext().
{
useItem<II extends I, TT extends T>(
collection: Nullish<Collection<string, II, TT>>, //
id: Nullish<II>,
): ItemStore<II, TT> | undefined;
useItem<II extends I, TT extends T>(
collection: Collection<string, II, TT>, //
id: II,
): ItemStore<II, TT>;
useQuery<II extends I, TT extends T>(
collection: Nullish<Collection<string, II, TT>>, //
query: Nullish<Query<Item<II, TT>>>,
): QueryStore<II, TT> | undefined;
useQuery<II extends I, TT extends T>(
collection: Collection<string, II, TT>, //
query: Query<Item<II, TT>>,
): QueryStore<II, TT>;
readonly DBContext: ({ children }: { children: ReactNode }) => ReactElement;
requireDBCache(): DBCache<I, T>;
}Bundle of hooks and a provider component returned by createAPIContext()
{
useAPI<PP extends P, RR extends R>(this: void, endpoint: Endpoint<PP, RR>, payload: PP): EndpointStore<PP, RR>;
useAPI<PP extends P, RR extends R>(this: void, endpoint: Nullish<Endpoint<PP, RR>>, payload: PP): EndpointStore<PP, RR> | undefined;
readonly APIContext: (props: ChildProps) => ReactElement;
requireAPICache(): APICache<P, R>;
}