use Graft Reload

A flag that goes up and comes back down after a delay, to remount a subtree.

API

Options

proptypedefault
delay?number0milliseconds the flag stays up

Returns

proptypedefault
isReloading?booleanwhether a reload is in progress
onReload?() => voidraises the flag
id?stringstable id

Usage

Up, then down

tsx
// -constants/graft_panel.ts
export const GraftPanel = createGraft({ name: "GraftPanel", graft: useGraftReload });

// -components/reload_button.tsx
export const ReloadButton = () => {
  const { isReloading, onReload } = GraftPanel.use();

  return (
    <Button disabled={isReloading} open={isReloading} onClick={onReload}>
      <Button.IconLabel reverse icon={<Button.Icon icon={<Spinner />} />} label={<Button.Label label="reload" />} />
    </Button>
  );
};
previewclick a few times, then reload: the count and the timestamp are both new
mounted at 4:35:46 PM

Details

Remounting on purpose

Sometimes the cheapest way to reset a subtree is to stop rendering it for a moment. Swap it out while isReloading is up and everything below goes with it — state, refs, effects, whatever an uncontrolled input was holding — and comes back the way it started.

tsx
// -components/panel_controls.tsx
export const PanelControls = () => {
  const { isReloading, onReload } = GraftPanel.use();

  return (
    <>
      <Button disabled={isReloading} open={isReloading} onClick={onReload}>
        <Button.IconLabel reverse icon={<Button.Icon icon={<Spinner />} />} label={<Button.Label label="reload" />} />
      </Button>

      <Guard guardIf={isReloading} shouldHide>
        <PanelBody />
      </Guard>
    </>
  );
};

// -components/panel_body.tsx — everything in here is new after a reload
export const PanelBody = () => {
  const [mountedAt] = useState(() => new Date().toLocaleTimeString());
  const [clicks, setClicks] = useState(0);

  return (
    <>
      <Button onClick={() => setClicks((previous) => previous + 1)}>{`clicks: ${clicks}`}</Button>
      <Chip>{`mounted at ${mountedAt}`}</Chip>
    </>
  );
};

A key does the same thing when you have something to key on. This is for the times you do not: nothing changed, you simply want it new again.

The delay is the point

With delay at zero the flag is up for a single frame — enough to remount, invisible to the eye. Give it a few hundred milliseconds instead and the same flag doubles as the condition for a skeleton, so the reset does not read as a flicker.

It measures nothing and waits for nothing: it is a timer, not a request. If what you are reloading is asynchronous, its own pending state is the one to render — this one just decides when the subtree is replaced.