Skip to Content
InternalsReactive System in Depth

Reactive System in Depth

The previous page showed how to use signal, computed, and effect.
This page answers a different question:

why is Ansiq designed this way?

Start with one rule

Ansiq keeps a strict boundary:

  • the reactive system decides what became dirty
  • the runtime decides how the terminal should update

If that boundary collapses, several bad things happen quickly:

  • dependency propagation gets mixed with UI updates
  • it becomes hard to tell whether a bug is in the graph or the layout system
  • localized updates become much harder to optimize

What the reactive graph actually records

You can think of Ansiq’s reactive graph as three kinds of nodes:

  • signal
  • computed
  • effect

signal

signal is the base source of reactive state:

  • what is the current value?
  • who depends on it?

computed

computed is derived state:

  • it should not become duplicated business state
  • it is calculated from other reactive values

effect

effect is the side-effect boundary:

  • it is not where UI is produced
  • it is not a second state container
  • it reacts after tracked reads change

One important note here:

effect is not React’s useEffect.
It has no dependency array. Dependencies are tracked automatically from reactive reads during execution.

What an update actually looks like

When you call signal.set(...) or signal.set_if_changed(...), Ansiq does not jump straight to drawing the terminal. It first propagates dirty information:

From reactive graph to terminal update
1signal value changes
2reactive graph marks dependent scopes dirty
3runtime collects dirty scopes
4runtime rerenders a subtree or the root
5layout and patch stages update the terminal

The important part is where the reactive graph stops:

  • it marks dirty scopes
  • then it hands control to the runtime

It does not decide:

  • which ancestors must relayout
  • which regions should redraw
  • what exact terminal patches get emitted

Those are runtime decisions.

Why Ansiq does not center everything on whole-component rerender

If every state change automatically implied “rerender the whole component tree”, then:

  • small input changes would expand into large updates
  • subtree replacement would become much less useful
  • the damage model would stay coarse

Ansiq wants a tighter path:

signal -> dirty scope -> subtree replacement -> partial relayout -> patch

That is one of the main reasons it feels closer to Vue / Solid than to a React-style “state update means rerender everything nearby” model.

Where effect fits

effect has a narrow but important job:

  • react to tracked changes
  • trigger side effects
  • avoid becoming the place where UI computations live

Good uses:

  • logging
  • syncing non-UI side effects
  • task coordination
  • triggering follow-up messages

Bad uses:

  • replacing computed
  • replacing application state
  • hiding all business logic inside one reactive callback

A practical rule:

  • for displayed derived values, prefer computed
  • for source-of-truth state, prefer signal
  • for behavior and side effects, prefer effect

Why this helps debugging

Once the boundary is clear, debugging gets much easier. You can ask:

  • is dependency propagation wrong?
  • is dirty scope collection wrong?
  • or is rerender / relayout / patching wrong?

If the reactive graph and the UI tree are mixed together, those questions blur into each other very quickly.

Where to go next

If you want the full architectural picture, continue with:

Last updated on