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

Dynamic Property Addition

Properties added to an object after proxy creation are not automatically reactive. To add reactive properties dynamically, reassign the entire object:

// ❌ This won't be reactive
this.user.newProperty = 'value';

// ✅ This will be reactive
this.user = { ...this.user, newProperty: 'value' };

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.