Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/sanchedev/tiny-engine/llms.txt

Use this file to discover all available pages before exploring further.

useComputed derives a read-only signal from one or more existing signals. The supplied function is executed immediately to compute the initial value, and then re-executed automatically whenever any signal that was read during the previous computation changes. The result is exposed as a getter just like the one returned by useSignal.

Signature

useComputed<T>(fn: () => T, refreshOnNodeStart?: boolean): SignalGetter<T>

Parameters

fn
() => T
required
The computation function. Any SignalGetter called inside this function is tracked as a dependency. When a dependency changes the function is re-run and the derived value is updated.
refreshOnNodeStart
boolean
When true, the computed value is also re-evaluated when the component’s root node fires its started event. Defaults to false. Use this when the computation depends on a value — such as a script reference — that is only available after the node finishes initialising.

Return value

SignalGetter<T> — a zero-argument function () => T. Reading it inside JSX props or other reactive contexts automatically tracks it as a dependency.

Examples

Cooldown progress bar

CooldownBar.tsx
import { useSignal, useComputed } from 'tiny-engine/hooks'

const COOLDOWN_SECONDS = 3

function CooldownBar() {
  const [elapsed, setElapsed] = useSignal(0)

  // progress goes from 0 → 1 over the cooldown period
  const progress = useComputed(() => Math.min(elapsed() / COOLDOWN_SECONDS, 1))

  return (
    <transform>
      <sprite
        textureId={BAR_TEXTURE}
        // Reactive: re-evaluated every time elapsed changes
        scaleX={() => progress()}
      />
    </transform>
  )
}

Reactive animation name

PlayerSprite.tsx
import { useSignal, useComputed, useRefNode } from 'tiny-engine/hooks'
import { PrimaryNode } from 'tiny-engine'

function Player() {
  const [isRunning, setRunning] = useSignal(false)
  const [isJumping, setJumping] = useSignal(false)

  const animName = useComputed(() => {
    if (isJumping()) return 'jump'
    if (isRunning()) return 'run'
    return 'idle'
  })

  const player = useRefNode(PrimaryNode.AnimationPlayer)

  return (
    <transform>
      <animation-player
        ref={player}
        // Reactive prop: updates animation whenever animName changes
        currentAnimation={() => animName()}
      />
    </transform>
  )
}

Depending on a script reference (refreshOnNodeStart)

When the computation relies on a value that only exists after the node starts — such as a TinyScript property — pass true as the second argument:
BossHealthBar.tsx
import { useRefNode, useScript, useComputed } from 'tiny-engine/hooks'
import { PrimaryNode } from 'tiny-engine'

function BossHealthBar() {
  const sprite = useRefNode(PrimaryNode.Sprite)
  const script = useScript<BossScript>(sprite)

  // script.current is undefined until the node starts, so refresh on start
  const healthRatio = useComputed(
    () => (script.current?.health ?? 0) / 4000,
    true, // re-evaluates when the node's started event fires
  )

  return (
    <sprite ref={sprite} textureId={BOSS_BAR} scaleX={() => healthRatio()} />
  )
}
useComputed returns a read-only getter. There is no setter — attempting to update the underlying signal directly is not possible. If you need writable derived state, use useSignal and update it inside a useEffect.
Because useComputed tracks its dependencies dynamically, branching logic works as expected: if a branch is not reached during a particular evaluation, signals inside that branch are not tracked until the branch is reached again.

Build docs developers (and LLMs) love