Reactivity System
The Lyt.js reactivity system is built on ES6 Proxy, providing complete reactive data tracking and update mechanisms.
reactive()
Creates a deep reactive object:
import { reactive } from 'lyt'
const state = reactive({
count: 0,
message: 'Hello',
nested: {
value: 42
}
})
// Direct access and modification
state.count++ // Triggers update
state.nested.value++ // Deep reactivityfunction reactive<T extends object>(target: T, options?: ReactiveOptions): T
interface ReactiveOptions {
deep?: boolean // Whether to use deep reactivity (default true)
readonly?: boolean // Whether to make it read-only (default false)
}readonly()
Creates a read-only reactive proxy:
import { reactive, readonly } from 'lyt'
const state = reactive({ count: 0 })
const readonlyState = readonly(state)
readonlyState.count = 1 // Warning: Cannot modify read-only objectshallowReactive()
Creates a shallow reactive object (only the first level is reactive):
import { shallowReactive } from 'lyt'
const state = shallowReactive({
count: 0,
nested: { value: 42 }
})
state.count++ // Triggers update
state.nested.value = 1 // Does NOT trigger update (shallow)ref()
Creates a Ref reference for wrapping primitive values:
import { ref } from 'lyt'
const count = ref(0)
console.log(count.value) // 0
count.value++
console.log(count.value) // 1Ref can also wrap objects (internally uses reactive for deep proxying):
const user = ref({ name: 'John', age: 25 })
user.value.name = 'Jane' // Triggers updateshallowRef()
Creates a shallow Ref (no automatic deep proxying):
const state = shallowRef({ count: 0 })
state.value.count++ // Does NOT trigger update
state.value = { count: 1 } // Replacing the entire value triggers updateUtility Functions
import { isRef, unref, toRef, toRefs, triggerRef } from 'lyt'
const count = ref(0)
isRef(count) // true
unref(count) // 0 (returns .value if Ref, otherwise returns the original value)
const state = reactive({ name: 'John', age: 25 })
const nameRef = toRef(state, 'name') // Creates a Ref for state.name
const { name, age } = toRefs(state) // Converts all properties to Refs
triggerRef(count) // Manually triggers Ref updatecomputed()
Creates a computed property (automatically cached based on dependencies):
import { ref, computed } from 'lyt'
const firstName = ref('John')
const lastName = ref('Doe')
// Read-only computed property
const fullName = computed(() => firstName.value + lastName.value)
console.log(fullName.value) // 'JohnDoe'
// Writable computed property
const name = computed({
get: () => firstName.value + lastName.value,
set: (val) => {
firstName.value = val[0]
lastName.value = val.slice(1)
}
})How it works
Computed properties use a dirty flag for lazy evaluation and caching. They only recompute when dependencies change; otherwise, the cached value is returned.
watch()
Watches reactive data changes:
import { ref, reactive, watch } from 'lyt'
// Watch a Ref
const count = ref(0)
watch(count, (newVal, oldVal) => {
console.log(`Changed from ${oldVal} to ${newVal}`)
})
// Watch a getter function
const state = reactive({ count: 0 })
watch(
() => state.count,
(newVal, oldVal) => {
console.log(`count changed to ${newVal}`)
},
{ immediate: true, deep: true }
)
// Watch multiple sources
watch([count, () => state.count], ([c1, c2]) => {
console.log(c1, c2)
})watchEffect()
Automatically tracks dependencies in a side effect function:
import { ref, watchEffect } from 'lyt'
const count = ref(0)
const stop = watchEffect(() => {
console.log(`Current count: ${count.value}`)
}) // Executes immediately
// Stop watching
stop()nextTick()
Executes a callback in the next microtask, after DOM updates are complete:
import { ref, nextTick } from 'lyt'
const count = ref(0)
async function increment() {
count.value++
await nextTick()
// DOM has been updated
console.log('DOM update complete')
}