4. Components run once
This is the single most important idea in Pyreon. A component function runs exactly once — on mount. It is not re-invoked when state changes. So what makes the UI update?
Reactivity depends on where you read a signal. A signal read inside a JSX expression (or an
effect/computed) is wired to that spot; a write updates only that spot. The component body itself never re-runs.
function Counter() {
const count = signal(0)
console.log('this logs ONCE, ever') // body runs once
return (
<button onClick={() => count.update((c) => c + 1)}>
{() => count()} {/* this thunk re-runs on every change */}
</button>
)
}The example below proves it — the body logs once, but the reactive thunk re-runs on each click:
The key idea: because the body runs once, two things follow that trip up newcomers:
Read
props.xin the reactive scope, not at the top.const { x } = props(orconst v = props.x) captures the value once and never updates. Readprops.xinside the JSX/effect, or usesplitProps.Pass props as expressions.
<Child value={count()} />is reactive (the compiler wraps it); destructuring on the way in freezes it.
This is the opposite of React's model (where the function re-runs every render). Once it clicks, everything else in Pyreon follows from it.