Reactivity
This page describes Regor reactivity based on actual runtime behavior.
Core model
Section titled “Core model”- A ref is a callable value container (
r(),r(newValue)) with.valuealias. ref()andsref()are both refs (isRef(...) === true).ref()is deep-conversion oriented.sref()is shallow-conversion oriented.
ref vs sref (important)
Section titled “ref vs sref (important)”ref(value)
Section titled “ref(value)”ref recursively converts nested object/array properties to refs.
const user = ref({ name: 'Ada', meta: { active: true } })user().name('Grace')user().meta().active(false)Key behavior:
- Object/array content is converted recursively.
- Source objects are mutated in place during conversion.
- Existing refs are reused.
Node,Date,RegExp,Promise,Errorare not recursively converted.
sref(value)
Section titled “sref(value)”sref keeps nested properties as plain values (no recursive wrapping).
const user = sref({ name: 'Ada', age: 30 })user().name = 'Grace'user().age = 31Key behavior:
- Nested values stay plain unless they were already refs.
- Arrays/Map/Set are made reactive via prototype adaptation.
sref(sourceRef)returns the same ref instance.
Access and update forms
Section titled “Access and update forms”For both ref and sref:
const r = ref(1)r() // getr(2) // setr.value // getr.value = 3 // setDerived refs: computed, computeRef, computeMany
Section titled “Derived refs: computed, computeRef, computeMany”Regor computed refs are:
- Read-only.
- Lazy (first evaluation happens on first read).
- Cached until dependencies change.
- Invalidated on source change, then recomputed on next read.
const a = ref(1)const b = ref(2)const sum = computeMany([a, b], (x, y) => x + y)
sum() // 3 (computes now)sum() // 3 (cached)a(5)sum() // 7 (recomputed after invalidation)watchEffect
Section titled “watchEffect”watchEffect immediately runs and tracks refs accessed during execution.
When any tracked ref changes:
- Cleanup callbacks registered through
onCleanupare called. - Effect is re-subscribed using newly accessed refs.
const count = ref(0)const stop = watchEffect((onCleanup) => { const snapshot = count() onCleanup?.(() => console.log('cleanup', snapshot)) console.log('value', snapshot)})Use silence(() => ...) to read refs without tracking.
observe and observeMany
Section titled “observe and observeMany”observe(source, cb, init?) listens to one ref.
observeMany([a, b], cb, init?) listens to multiple refs and returns tuple values.
Both return a stop function.
Batch and manual trigger
Section titled “Batch and manual trigger”batch/startBatch/endBatch defer observer notification until batch end.
batch(() => { a(1) a(2) b(3)})Observers are triggered once per changed ref after batch completion.
Use trigger(ref) to manually notify observers.
trigger(ref, undefined, true) recursively triggers nested refs.
Pause and resume
Section titled “Pause and resume”pause(ref) disables auto observer firing for that ref.
resume(ref) re-enables it.
Manual trigger(ref) still works while paused.
Other important helpers
Section titled “Other important helpers”unref(x)unwraps one ref level.isRef(x)checks any ref (reforsref).isDeepRef(x)checks whether value was created as deepref.flatten(x)recursively unwraps refs in objects/arrays/Map/Set into plain data.entangle(a, b)creates two-way sync and initializesbwitha.persist(ref, key)syncs ref data withlocalStorage.
flatten sample
Section titled “flatten sample”Use flatten when you need a plain snapshot for logging, serialization, or transport.
const state = ref({ user: ref({ name: 'Ada' }), tags: new Set([ref('core')]), meta: new Map([['count', ref(2)]]),})
const plain = flatten(state)// {// user: { name: 'Ada' },// tags: Set { 'core' },// meta: Map { 'count' => 2 }// }Collections and caveats
Section titled “Collections and caveats”MapandSetare reactive through Regor’s proxy prototypes.WeakMapandWeakSetare not auto-reactive; usetrigger(...)manually when needed.- For very large nested structures, pick
refvssrefintentionally based on update style and cost.