[]previous and next page

Derived values

There are three ways to derive a value from state, from most explicit to most forgiving.

For agentsUpdated

There are three ways to derive a value from state, from most explicit to most forgiving.

useMemo(fn, options?) returns an accessor. It computes eagerly once, caches, and recomputes only when a dependency changed and someone reads it. Dependencies are re-discovered on every run. If the new value equals the old one (Object.is, or options.equals), downstream readers are not notified. { lazy: true } skips the first computation until the first read.

Derived constants are the forgiving form. Inside a component, a const computed from state becomes a memo without any ceremony (this is how double in the first example stays live). The rules, from the forgiving-reactivity contract:

You write Meaning
const double = count() * 2; {double} or {double()} a derived value; both forms are reactive
const { title, size = 3, label: text } = props; each name follows props (defaults only when undefined)
const b = a * 10; where a is derived chains are derived too
const f = mode() === "up" ? up : down; {f("x")} a derived function stays callable
  • Only const declarations at the top level of a component body (a PascalCase function with at most one parameter that contains JSX).
  • Left alone: hooks and primitives (use*, create*, batch, untrack, onMount, ...), functions, classes, JSX, literals, await, and anything not read in JSX or in a closure.
  • A derived value is a memo: it re-runs only when the signals it read change, and caches.
  • A name that is re-declared below the component's top level is not rewritten.
  • Calling a derived const (double()) returns its value; if the value is a function it is called.
src/totals.tsxtsx
import { useStore } from "@shadow-js/core";

export function Totals() {
  const [lines] = useStore([
    { price: 5, qty: 1 },
    { price: 3, qty: 2 },
  ]);
  const total = lines.reduce((sum, line) => sum + line.price * line.qty, 0);
  const count = lines.length;

  return (
    <p>
      Total: {total} ({count} lines)
    </p>
  );
}

Outside components (a plain function, a module, a hook), nothing is rewritten: write useMemo or an arrow function.

An arrow function is always live where it is called in JSX: {() => count() * 2} or {double()} with const double = () => count() * 2.