Signal
Reactive state containers that form the foundation of the reactivity model.
The core reactive primitive for holding mutable state.
Creating Signals
#Signal.make(value)
#Creates a new signal with an initial value.
let count = Signal.make(0)
let name = Signal.make("Alice")
let items = Signal.make(["a", "b", "c"])Signal.make(~name, value)
#Creates a named signal for debugging purposes.
let count = Signal.make(~name="counter", 0)Reading Signals
#Signal.get(signal)
#Reads the current value and creates a dependency. When called inside a Computed or Effect, the computed/effect will re-run when this signal changes.
let count = Signal.make(5)
let value = Signal.get(count) // 5
// Inside a computed — creates a dependency
let doubled = Computed.make(() => Signal.get(count) * 2)Signal.peek(signal)
#Reads the current value without creating a dependency. Useful when you need to read a value but don't want to trigger re-computation.
let count = Signal.make(5)
// Won't create a dependency
let value = Signal.peek(count)Updating Signals
#Signal.set(signal, value)
#Sets a new value for the signal.
Signal.set(count, 10)Signal.update(signal, fn)
#Updates the signal value based on the previous value.
Signal.update(count, n => n + 1)
Signal.update(items, arr => Array.concat(arr, ["d"]))Batching Updates
#Signal.batch(fn)
#Batches multiple signal updates into a single notification cycle. Improves performance when updating many signals at once.
let firstName = Signal.make("John")
let lastName = Signal.make("Doe")
// Both updates trigger a single re-computation
Signal.batch(() => {
Signal.set(firstName, "Jane")
Signal.set(lastName, "Smith")
})Untracked Reads
#Signal.untrack(fn)
#Reads signals without creating dependencies. Similar to peek but works for a block of code.
let a = Signal.make(1)
let b = Signal.make(2)
// Only depends on 'a', not 'b'
let computed = Computed.make(() => {
let aVal = Signal.get(a)
let bVal = Signal.untrack(() => Signal.get(b))
aVal + bVal
})