How Reactivity Works
WakaPAC's reactivity system automatically synchronizes your data with the DOM. Change a property, and the UI updates - no manual DOM manipulation required.
The Basics
When you create a WakaPAC component, all properties in your abstraction object become reactive:
wakaPAC('#app', {
count: 0,
increment() {
this.count++; // Change is detected, DOM updates scheduled
}
});
WakaPAC's reactivity engine:
- Creates proxies for all properties in your abstraction object
- Tracks dependencies - which DOM elements reference which properties
- Queues updates when properties change, avoiding redundant operations
- Batches DOM changes in microtasks for optimal performance
- Updates selectively - only affected DOM elements are modified
The Reactivity Pipeline
Deep Reactivity
Nested Objects
WakaPAC recursively wraps nested objects in proxies, making changes at any depth reactive:
<div id="app">
<p>{{user.name}}</p>
<p>Theme: {{user.preferences.theme}}</p>
<button data-pac-bind="click: changeTheme">Toggle Theme</button>
</div>
<script>
wakaPAC('#app', {
user: {
name: 'John',
preferences: {
theme: 'dark'
}
},
changeTheme() {
// Deep property changes trigger reactivity
this.user.preferences.theme =
this.user.preferences.theme === 'dark' ? 'light' : 'dark';
}
});
</script>
Array Mutations
WakaPAC intercepts array mutator methods (push, pop, splice, shift, unshift, sort, reverse) to trigger updates automatically:
<div id="app">
<ul data-pac-bind="foreach: todos" data-pac-item="todo">
<li>{{todo.text}} (Item {{$index}})</li>
</ul>
<button data-pac-bind="click: addTodo">Add Todo</button>
</div>
<script>
wakaPAC('#app', {
todos: [
{ text: 'Learn WakaPAC', completed: false },
{ text: 'Build an app', completed: false }
],
addTodo() {
// Array mutations automatically trigger re-render of the list
this.todos.push({
text: 'New todo',
completed: false
});
}
});
</script>
Objects Within Arrays
Objects stored in arrays are also wrapped in proxies, making their property changes reactive:
<div id="app">
<div data-pac-bind="foreach: todos" data-pac-item="todo" >
<input type="checkbox" data-pac-bind="checked: todo.completed">
<span>{{todo.text}} (Item {{$index}})</span>
<button data-pac-bind="click: toggleTodo">Toggle</button>
</div>
</div>
<script>
wakaPAC('#app', {
todos: [
{ text: 'Task 1', completed: false },
{ text: 'Task 2', completed: false }
],
toggleTodo(todo, index) {
// Modifying object properties inside arrays triggers reactivity
todo.completed = !todo.completed;
}
});
</script>
Foreach Index Variable
Within a foreach loop, WakaPAC provides the special variable $index containing the current iteration index (0-based). The $ prefix distinguishes it from user-defined properties, preventing naming conflicts.
You can customize the index variable name using the data-pac-index attribute:
<div data-pac-bind="foreach: items" data-pac-index="i">
<span>Item {{i}}: {{item}}</span>
</div>
Performance Optimizations
Update Batching
WakaPAC batches multiple changes into a single DOM update cycle using microtasks. If you change the same property multiple times synchronously, only the final value triggers a DOM update:
this.count = 1;
this.count = 2;
this.count = 3;
// DOM updates once with value 3, not three times
Dependency Tracking
Only elements that actually reference a changed property are updated. If you have 100 elements but only one uses {{count}}, changing count updates only that element.
Smart Deduplication
If an element is queued for update multiple times before the microtask executes (e.g., from changes to different properties it depends on), it only updates once with all new values.
Reactivity Caveats
Array Index Assignment
Direct array index assignment is reactive, but sparse array assignments may not behave as expected:
// ✅ Reactive
this.todos[0] = newTodo;
// ⚠️ Creates sparse array, may not update correctly
this.todos[100] = newTodo;
Use push(), splice(), or reassign the array for complex operations.
Primitive Value Reassignment
When passing primitive values to child components or functions, changes to the original won't affect the copy:
let count = this.count; // count is now a copy
count++; // this.count is unchanged
Always modify properties directly on this to maintain reactivity.