Skip to content

Effect

Side effects that run when their dependencies change, with automatic cleanup.

Side effects that automatically re-run when their dependencies change.

Creating Effects

#

Effect.run(fn)

#

Creates and immediately runs an effect. The effect re-runs whenever any signal or computed it reads changes.

let count = Signal.make(0)

let disposer = Effect.run(() => {
  Console.log(`Count is: ${Signal.get(count)->Int.toString}`)
})

// Logs: "Count is: 0"
Signal.set(count, 1)
// Logs: "Count is: 1"

Effect.run(~name, fn)

#

Creates a named effect for debugging.

Effect.run(~name="logger", () => {
  Console.log(Signal.get(count))
})

Cleanup Functions

#

Effects can return a cleanup function that runs before the effect re-runs and when the effect is disposed.

let count = Signal.make(0)

Effect.run(() => {
  let value = Signal.get(count)
  Console.log(`Setting up for: ${value->Int.toString}`)

  // Cleanup function — runs before next execution
  Some(() => {
    Console.log(`Cleaning up for: ${value->Int.toString}`)
  })
})

Signal.set(count, 1)
// Logs: "Cleaning up for: 0"
// Logs: "Setting up for: 1"

Disposal

#

disposer.dispose()

#

Effect.run returns a disposer object. Call dispose() to stop the effect and run any cleanup function.

let disposer = Effect.run(() => {
  Console.log(Signal.get(count))
  Some(() => Console.log("Cleanup!"))
})

// Stop tracking and run cleanup
disposer.dispose()
// Logs: "Cleanup!"

// Future changes won't trigger the effect
Signal.set(count, 100) // Nothing logged

Common Use Cases

#

DOM Updates

#
let title = Signal.make("Hello")

Effect.run(() => {
  let el = Document.getElementById("title")
  el->Element.setTextContent(Signal.get(title))
  None
})

Event Listeners

#
let isActive = Signal.make(false)

Effect.run(() => {
  if Signal.get(isActive) {
    let handler = _ => Console.log("Clicked!")
    Window.addEventListener("click", handler)
    Some(() => Window.removeEventListener("click", handler))
  } else {
    None
  }
})

Timers

#
let interval = Signal.make(1000)

Effect.run(() => {
  let ms = Signal.get(interval)
  let id = setInterval(() => Console.log("Tick!"), ms)
  Some(() => clearInterval(id))
})

Local Storage Sync

#
let theme = Signal.make("light")

Effect.run(() => {
  let current = Signal.get(theme)
  LocalStorage.setItem("theme", current)
  None
})
Was this page helpful?