This is intended to become the only API for building Incr_dom apps, and S_simple and S_derived should be removed soon. This should provide essentially the full optimization power of S_derived, but should be simpler to use than S_simple

module Model : sig ... end

The Model represents essentially the complete state of the GUI, including the ordinary data that the application is displaying, as well as what you might call the "interaction state", things describing where you are in the lifecycle of the GUI, what view is currently up, where focus is, etc.

module Action : sig ... end
module State : sig ... end
val on_startup : schedule_action:(Action.t -> unit) -> Model.t -> State.t Async_kernel.Deferred.t

on_startup is called once, right after the initial DOM is set to the view that corresponds to the initial state. This is useful for doing things like starting up async processes. Note that this part of the computation does not support any incrementality, since it's only run once.

create is a function that incrementally constructs a Component. Note that a Component supports functions like apply_action, which return a new Model.t, without taking a model as an explicit input. The intent is for apply_action to have access to the current model via its construction

Here's an example of how this might look in practice.

module Model = struct
  type t = { counter : int } [@@deriving fields, compare]

  let cutoff t1 t2 = compare t1 t2 = 0

module State = struct
  type t = unit

module Action = struct
  type t = Increment [@@deriving sexp_of]

  let should_log _ = false

let initial_model = { Model.counter = 0 }

let on_startup ~schedule_actions _model =
  every (Time_ns.Span.of_sec 1.) (fun () ->
    schedule_actions [ Action.Increment ]);

let create model ~old_model:_ ~inject:_ =
  let open Incr.Let_syntax in
  let%map apply_action =
    let%map counter = model >>| Model.counter in
    fun (Increment : Action.t) _ ~schedule_actions:_ ->
      { Model.counter = counter + 1 }
  and view =
    let%map counter =
      let%map counter = model >>| Model.counter in
      Vdom.Node.div [] [ Vdom.Node.text (Int.to_string counter) ]
    Vdom.Node.body [] [ counter ]
  and model = model in
  (* Note that we don't include [on_display] or [update_visibility], since
     these are optional arguments *)
  Component.create ~apply_action model view

The full code for this example can be found in examples/counter.