Derived values
There are three ways to derive a value from state, from most explicit to most forgiving.
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
constdeclarations 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.
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.