Effects and scopes
Effects
Signal.effect runs its function now, and again whenever a signal it read changes. It returns an IDisposable; dispose it to stop:
open Fable.Ripple
let count = Var.create 0
// Runs immediately: count = 0
let stop = Signal.effect (fun () -> printfn "count = %d" count.Value)
count.Value <- 1 // count = 1
stop.Dispose()
count.Value <- 2 // disposed - nothing runs
The write to 2 prints nothing - the effect is gone.
Effects run synchronously: the write returns after every affected effect has run. To coalesce several writes into one run, see Batching.
Autorun
Signal.autorun is the same, without a handle. The effect stops when the enclosing scope is disposed:
open Fable.Ripple
let count = Var.create 0
// Runs immediately: count = 0
Signal.autorun (fun () -> printfn "count = %d" count.Value)
count.Value <- 1 // count = 1
Subscribe
Signal.subscribe is the same, with the current value passed to the handler:
open Fable.Ripple
let count = Var.create 0
// Runs immediately: count = 0
count |> Signal.subscribe (fun n -> printfn "count = %d" n) |> ignore
count.Value <- 5 // count = 5
Scopes
Signal.root runs a function in a fresh scope. Every effect and computed created inside belongs to the scope; disposing the returned handle tears them all down at once:
open Fable.Ripple
let count = Var.create 0
let _, dispose =
Signal.root (fun () ->
Signal.autorun (fun () -> printfn "count = %d" count.Value)
Signal.onCleanup (fun () -> printfn "torn down")
) // the effect ran on creation: count = 0
count.Value <- 1 // count = 1
dispose.Dispose() // torn down
count.Value <- 2 // the scope is gone - nothing runs
After Dispose, the source lives on but the effect is detached - the write to 2 prints nothing, and no reference is left behind.
Signal.onCleanup registers a callback with the current scope, for teardown work of your own.
The DOM layer does this for you: every row of an Html.each and every branch of an Html.switch runs in its own root, disposed when the row or branch leaves.