Skip to content

Component-owned state and reactions

Read as Markdown

Click to update a signal, derive a value, and publish an imperative effect after commit.

Plain source. Run as a browser module after the document body exists. TSX uses jsx: "react-jsx" and jsxImportSource: "@pibbl/core".

/** @jsxImportSource @pibbl/core */
import {
pibbl,
Rectangle,
Text,
useSignal,
useComputed,
useConst,
useRef,
useReaction,
type PibblRef,
type PibblReactionOptions,
} from "@pibbl/core";
const canvas = document.createElement("canvas");
canvas.width = 320;
canvas.height = 160;
const output = document.createElement("output");
document.body.append(canvas, output);
function Counter() {
const count = useSignal(0);
const doubled = useComputed(() => count.get() * 2);
const label = useConst(() => "Click the rectangle");
const renders: PibblRef<number> = useRef(0);
renders.current = (renders.current ?? 0) + 1;
const options: PibblReactionOptions = { debugName: "counter-output" };
useReaction(
() => {
output.textContent = `Committed count: ${count.get()}`;
return () => {
output.textContent = "";
};
},
[],
options,
);
return [
<Rectangle
style={{ width: 320, height: 160, fill: "#267d7c" }}
onClick={() => count.update((n) => n + 1)}
/>,
<Text
pointerEvents="none"
style={{ left: 15, top: 50, fill: "white", font: "18px sans-serif" }}
>
{label}: {count.get()} / doubled {doubled.get()}
</Text>,
];
}
const controller = pibbl(canvas, <Counter />);
window.addEventListener("pagehide", () => controller.dispose(), { once: true });
  • Hooks run inside a mounted synchronous component. A ref mutation does not request rendering. Reaction setup and cleanup must not write signals.

Documentation built with @pibbl/core 0.0.2, revision 272a94a. ALPHA — NOT FOR PRODUCTION USE.