Overview

Actions go in, a handler returns the next model, and a listener runs only when FastEq says its slice changed.

The loop

Enqueue is cheap and asynchronous. Nothing is applied until run(). Follow-ups returned from a handler are dispatched immediately (nested), then the loop continues. Click a node to highlight it.

MermoidAscent.diagram(loop, config = loopConfig, viewport = Some(Viewport(420)))
changedsame
MermoidAscent.diagramInteractive(loop, config = loopConfig, initialWidth = 420)
viewport 420px
changedsame

A running counter

The number below is a real Conduit, the same widget as Getting started. + enqueues Inc. The loop applies the handler, and the listener patches the text only because the count changed.

conduit.Conduit.make
def make[M: FastEq, E](init: M)(h: => ActionHandler[M, ?, E]): Conduit[M, E] =
    get(apply(init)(h))

View source

GettingStarted.liveCounter
0

Install

libraryDependencies += "rocks.earlyeffect" %% "conduit" % "0.0.7"

Use %%% on Scala.js or Native. These live widgets use ascent-conduit. That bridge is optional and is not a dependency of the published artifact.

PieceJob
ModelImmutable case class, usually derives Optics
Actionenum X extends Action (alias for AppAction[Any, Nothing])
HandlerPartial function from action to ActionResult on a lensed slice
ConduitQueue + Ref + dispatch loop + listeners

Read Getting started for the handler, then Mental model for run vs run(false).