use Graft Store

A value that is held, with a way back to the one it started with.

API

Options

proptypedefault
defaultValueTthe starting value, and the one reset goes back to

Returns

proptypedefault
value?Tthe current value
setValue?Dispatch<SetStateAction<T>>as useState, updater included
resetValue?() => voidback to the value it started with
id?stringstable id

Usage

A value and a way back

tsx
// -constants/graft_draft.ts
export const GraftDraft = createGraft({ name: "GraftDraft", graft: useGraftStore<string | null> });

// -components/draft_controls.tsx
export const DraftControls = () => {
  const { resetValue, setValue, value } = GraftDraft.use();

  return (
    <>
      <Chip>{value ?? "null"}</Chip>
      <Button onClick={() => setValue("edited")}>set</Button>
      <Button onClick={() => setValue(null)}>empty</Button>
      <Button onClick={resetValue}>reset</Button>
    </>
  );
};
previewempty is a value like any other, reset goes back to where it started
draft

Details

useState, distributed

The value is useState and nothing else — same semantics, setValue takes an updater, batching and transitions behave as you expect. The hook adds the one exit worth having: resetValue, back to the value it started with.

Whether the store can be empty is your decision, not the hook's: a useGraftStore<Draft> always holds a draft, a useGraftStore<Draft | null> can be emptied with setValue(null). Nothing is nullable behind your back, so value is the type you asked for and needs no ?? at every read.

The default is read once

defaultValue is required and read on mount, then kept: resetting twice gives the same thing twice, even if what you passed has since changed. It takes a value, not a factory — if the default has to be computed, compute it before and hand over the result.

It also means a store can be seeded from something above it: a provider mounted when a dialog opens starts from the payload that dialog carries, and unmounting is how it is cleared. No effect to synchronise, and no stale value to reset by hand.

tsx
function useGraft() {
  const dialog = GraftDialogInvoice.use();

  return useGraftStore<Invoice | null>({ defaultValue: dialog.storage });
}