[]previous and next page

Switch and Match

<Switch fallback={...}> takes <Match when={...}> children and renders the first one whose when is truthy, or the fallback if none is.

For agentsUpdated

<Switch fallback={...}> takes <Match when={...}> children and renders the first one whose when is truthy, or the fallback if none is. Each Match accepts the same child forms as Show: plain children, an accessor callback, and keyed.

  • Re-selecting the same Match with the same truthiness does not rebuild it. Selecting a different one disposes the previous branch before the new one is created.
  • Conditions are evaluated in order each time any of them changes; an earlier Match that becomes truthy takes over from a later one.
  • Every child of Switch must be a Match (null, undefined and booleans are ignored). Anything else throws a TypeError (ShadowJS Switch: children must be <Match>) when the Switch is created.
src/status.tsxtsx
import { useStore } from "@shadow-js/core";
import { Match, Switch } from "@shadow-js/web";

type State = { kind: "loading" } | { kind: "error"; message: string } | { kind: "ready"; rows: number };

export function Status() {
  const [state] = useStore<State>({ kind: "ready", rows: 3 });

  return (
    <Switch fallback={<p>Idle</p>}>
      <Match when={state.kind === "loading"}>
        <p>Loading</p>
      </Match>
      <Match when={state.kind === "error"}>
        <p>Failed</p>
      </Match>
      <Match when={state.kind === "ready"}>
        <p>Loaded {(state as { rows: number }).rows} rows</p>
      </Match>
    </Switch>
  );
}

Because the when expressions are plain booleans, narrowing is lost in the children. Pass the narrowed value as when and use the callback form instead. The callback receives an accessor of the truthy value; annotate its parameter, because Match children are not contextually typed:

src/status-narrowed.tsxtsx
import { useStore } from "@shadow-js/core";
import { Match, Switch } from "@shadow-js/web";

interface Ready {
  kind: "ready";
  rows: number;
}

type State = { kind: "loading" } | Ready;

function asReady(state: State): Ready | undefined {
  return state.kind === "ready" ? state : undefined;
}

export function Narrowed() {
  const [state] = useStore<State>({ kind: "ready", rows: 2 });

  return (
    <Switch fallback={<p>Loading</p>}>
      <Match when={asReady(state)}>{(ready: () => Ready) => <p>{ready().rows} rows</p>}</Match>
    </Switch>
  );
}