The mental model: components run once
A React component is a function that runs again on every state change, and hooks rebuild their values from the previous render.
A React component is a function that runs again on every state change, and hooks rebuild their values from the
previous render. A ShadowJS component is a setup function. It runs once, builds real DOM nodes, and the
compiler wires every {expression} that reads a signal to the one text node or attribute it fills. Nothing
re-renders, so there are no dependency arrays, no stale closures and no rules of hooks.
| React | ShadowJS |
|---|---|
| The component function runs on every update | The component function runs once per instance |
| A virtual DOM is diffed | Signals update the exact node that reads them |
| State is read as a plain variable | State is read by calling an accessor: count() |
useEffect(fn, [a, b]) |
useEffect(fn) tracks whatever fn reads |
useMemo, useCallback, memo to avoid re-renders |
Not needed. Nothing re-renders |
| Hooks must run in the same order | Hooks are ordinary calls and may be conditional |
The first block counts how often the component body runs. It prints setup once, during the mount that the
page test performs.
src/counter.tsxtsx
import { useStore } from "@shadow-js/core";
export function Counter() {
console.log("setup");
const [count, setCount] = useStore(0);
return (
<button type="button" onClick={() => setCount((n) => n + 1)}>
{count()}
</button>
);
}
setupTwo clicks later the body has still run once, and the same text node holds the new value.