# Component-owned state and reactions
Click to update a signal, derive a value, and publish an imperative effect after commit.

## APIs demonstrated

- [useSignal](/reference/hooks/use-signal/) from `@pibbl/core`
- [useComputed](/reference/hooks/use-computed/) from `@pibbl/core`
- [useConst](/reference/hooks/use-const/) from `@pibbl/core`
- [useRef](/reference/hooks/use-ref/) from `@pibbl/core`
- [useReaction](/reference/hooks/use-reaction/) from `@pibbl/core`
- [PibblRef](/reference/types/canvas-runtime/) from `@pibbl/core`
- [PibblReactionOptions](/reference/types/canvas-runtime/) from `@pibbl/core`

## Complete example

[Plain source](/minimal/lifecycle/hooks.tsx). Run as a browser module after the document body exists. TSX uses `jsx: "react-jsx"` and `jsxImportSource: "@pibbl/core"`.

```tsx
/** @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 });

```

## Usage notes

- Hooks run inside a mounted synchronous component. A ref mutation does not request rendering. Reaction setup and cleanup must not write signals.

## Documentation version

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