API Reference

Every export from @assistant-ui/tap.

resource

Turns a hook into a resource factory. Wrap a use-prefixed hook.

import { resource } from "@assistant-ui/tap";

const useCounter = (props: { initialValue: number }) => {
  const [count, setCount] = useState(props.initialValue);
  return { count, increment: () => setCount((c) => c + 1) };
};

const Counter = resource(useCounter);

Calling the factory produces a ResourceElement.

withKey

Attaches a key to a ResourceElement for identity preservation in lists.

import { withKey } from "@assistant-ui/tap";

withKey("my-key", Counter({ initialValue: 0 }))

Hooks

Inside a resource body you use React's own hooks, imported from "react": useState, useReducer, useEffect, useMemo, useCallback, useRef, useEffectEvent, and use. They behave as they do in a component and follow the rules of hooks, with a few differences.

The hooks below are tap's own additions.

useResource

Hosts a single resource element. Isomorphic: works both inside a React component and inside another resource. Returns the resource's value.

const value = useResource(Counter({ initialValue: 0 }));

useResources

Hosts a dynamic, keyed list of resources. Takes an array of elements; every element must have a key via withKey. Isomorphic.

const values = useResources(
  items.map((item) => withKey(item.id, Item({ text: item.text }))),
);

useTapRoot

Takes a render callback and hosts it as a subscribable boundary, returning a stable { getValue, subscribe } handle instead of the value directly. Isomorphic. See Subscribable boundary.

const handle = useTapRoot(function CounterRoot() {
  return useResource(Counter({ initialValue: 0 }));
});
handle.getValue();
handle.subscribe(() => {});

useTapHost

Hosts a resource tree inside a React component. The tree renders with the component and commits in a passive effect, so it never blocks paint. Returns { value, effects }.

effects is a per-render callback (not a hook) that performs the commit. The host mounts it itself, but parent effects run after children's, so a child effect that subscribes to the tree would run before the commit. To commit ahead of a subtree, render a component before it that passes effects to a deps-less useEffect; the first instance to run wins. Consumers of effects must be descendants of the host component.

function CounterProvider({ children }: { children: ReactNode }) {
  const { value, effects } = useTapHost(function CounterHost() {
    return useResource(Counter({ initialValue: 0 }));
  });
  return (
    <CounterContext.Provider value={value}>
      <TapEffects effects={effects} />
      {children}
    </CounterContext.Provider>
  );
}

function TapEffects({ effects }: { effects: () => void }) {
  useEffect(effects);
  return null;
}

createTapRoot

Hosts a resource tree outside React. Takes a render callback and returns { getValue, subscribe, unmount } directly.

const root = createTapRoot(function CounterRoot() {
  return useResource(Counter({ initialValue: 0 }));
});
root.getValue();
root.subscribe(() => {});
root.unmount();

flushTapSync

Flushes pending tap-scheduled updates synchronously. Applies to tap-scheduled trees (createTapRoot / useTapRoot); for useResource trees use flushSync from react-dom.

flushTapSync(() => handle.getValue().increment());

Context

Tap context uses regular React contexts and is supported as of @assistant-ui/tap 0.9.

Create a context with createContext from "react". Read it with use / useContext from "react".

const ThemeContext = createContext("light");

useContextProvider

Provides a context value to every resource rendered inside the callback.

useContextProvider(ThemeContext, "dark", () => useResource(Button()));

Types

Resource

type Resource<R, A extends readonly unknown[]> = (
  ...args: A
) => ResourceElement<R, A>;

The factory returned by resource(). A is the tuple of arguments the hook takes (a single-object hook is just A = [Props]).

ResourceElement

type ResourceElement<R, A extends readonly unknown[]> = {
  hook: (...args: A) => R;
  args: Readonly<A>;
  key?: string | number;
};

An inert { hook, args } description of a resource to host: the hook plus the arguments to call it with. Hosting it is just hook(...args).

ContravariantResource

type ContravariantResource<R, A extends readonly unknown[]> = (
  ...args: A
) => ResourceElement<R>;

A contravariant Resource for accepting resources as parameters with broader assignability (the returned element omits the A parameter).