StunkStunk
Core Concepts

Computed

Derive reactive state from multiple chunks using computed().

computed() creates a derived chunk that depends on multiple source chunks. It automatically recomputes whenever any of its dependencies change, and only notifies subscribers when the result actually differs from the previous value.

import { chunk, computed } from "stunk";

const price = chunk(100);
const quantity = chunk(3);

const total = computed([price, quantity], (p, q) => p * q);

total.get(); // 300

quantity.set(5);
total.get(); // 500 — recomputed automatically

API

computed(dependencies, computeFn): Computed<TResult>
ParameterTypeDescription
dependenciesChunk<any>[]Array of source chunks to depend on
computeFn(...values) => TResultFunction receiving current values of each dependency

The argument order in computeFn matches the order of dependencies.


The Computed interface

A computed chunk extends Chunk<T> with two additional methods:

isDirty()

Returns true if any dependency has changed since the last computation. Stunk uses this internally to decide whether to recompute — you can also use it yourself for debugging or optimization.

const total = computed([price, quantity], (p, q) => p * q);

total.isDirty(); // false

price.set(200);
total.isDirty(); // false — already recomputed synchronously

recompute()

Manually forces recomputation from current dependency values. Normally you don't need this — Stunk recomputes automatically on every dependency change.

total.recompute();

Read-only enforcement

Like select(), computed chunks are read-only. Calling set() throws an error:

const total = computed([price, quantity], (p, q) => p * q);

total.set(999); // ❌ throws: "Cannot set values directly on computed"

To change the result, update the source chunks:

price.set(50);
total.get(); // 150 (50 * 3)

reset() on a computed chunk resets all its dependencies back to their initial values:

price.set(200);
quantity.set(10);

total.reset(); // resets price and quantity to their initial values
total.get(); // 300 (100 * 3)

Performance

Stunk uses a fast path for primitives and shallow equality for objects to avoid unnecessary updates:

  • For primitives (numbers, strings, booleans) — strict !== comparison
  • For objectsshallowEqual check to avoid re-notifying when the shape and values are the same

This means computed chunks only propagate updates when the result genuinely changes.


TypeScript

Types flow through automatically from dependencies:

const price = chunk(100); // Chunk<number>
const quantity = chunk(3); // Chunk<number>
const label = chunk("Total"); // Chunk<string>

const summary = computed(
  [price, quantity, label],
  (p, q, l) => `${l}: ₦${p * q}`, // (number, number, string) => string
);
// Computed<string>

In React

Derive outside the component, then pass to useChunkValue() since computed is read-only:

import { chunk, computed } from "stunk";
import { useChunkValue } from "stunk/react";

const price = chunk(100);
const quantity = chunk(2);
const total = computed([price, quantity], (p, q) => p * q); // outside component

function OrderSummary() {
  const totalValue = useChunkValue(total); // read-only

  return <p>Total: ${totalValue}</p>;
}

useComputed exists in v2 but will be removed in v3. Compute outside the component and pass the result directly to useChunkValue() instead.


Coming in v3

The computed() API is being redesigned in v3 to automatically detect dependencies — no more arrays:

// v3 — auto dependency tracking
const total = computed(() => price.get() * quantity.get());

The v2 API will remain supported during the transition.


What's next?

On this page