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.

explanation

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

WakaPAC recursively wraps nested objects, arrays, and the objects inside those arrays in proxies, so changes at any depth trigger a DOM update:

<div id="app">
    <p>{{user.name}}</p>
    <p>Theme: {{user.preferences.theme}}</p>
    <button data-pac-bind="click: changeTheme">Toggle Theme</button>

    <ul data-pac-bind="foreach: todos" data-pac-item="todo">
        <li>
            <input type="checkbox" data-pac-bind="checked: todo.completed">
            <span>{{todo.text}}</span>
            <button data-pac-bind="click: toggleTodo">Toggle</button>
        </li>
    </ul>
    <button data-pac-bind="click: addTodo">Add Todo</button>
</div>

<script>
    wakaPAC('#app', {
        user: {
            name: 'John',
            preferences: {
                theme: 'dark'
            }
        },
        todos: [
            { text: 'Learn WakaPAC', completed: false },
            { text: 'Build an app', completed: false }
        ],

        changeTheme() {
            // Nested property changes are reactive at any depth
            this.user.preferences.theme =
                this.user.preferences.theme === 'dark' ? 'light' : 'dark';
        },

        addTodo() {
            // Array mutator methods (push, pop, splice, shift, unshift, sort, reverse)
            // are intercepted automatically - no need to reassign the array
            this.todos.push({ text: 'New todo', completed: false });
        },

        toggleTodo(event, todo) {
            // Objects inside arrays are proxies too, so their properties are reactive
            todo.completed = !todo.completed;
        }
    });
</script>

Reactivity Caveats

Reading After a Write

A property write updates the abstraction immediately, but the DOM update it triggers is deferred to a microtask, not applied in the same line. Code that reads the DOM right after a write will see the old state:

this.editing = true;
this.container.querySelector('input').focus(); // ⚠️ input may not be rendered yet

Call wakaPAC.flushReactiveChanges(pacId) to force the pending DOM update to apply immediately:

this.editing = true;
wakaPAC.flushReactiveChanges(this.pacId);
this.container.querySelector('input').focus(); // ✅ input is rendered

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.

Escaping the Proxy

Every reactive value exposes an unwrap() method that returns the underlying raw object, useful when you need to compare or pass the original reference to code that shouldn't interact with the proxy — for example, a plugin performing identity comparisons. Calling unwrap() does not remove reactivity from the property; it just gives you the raw object for that call.

wakaPAC('#app', {
    item: { id: 1, name: 'Example' },

    check() {
        // this.item is a reactive proxy
        // this.item.unwrap() is the original plain object
        const raw = this.item.unwrap();
        console.log(raw === this.item); // false — proxy !== raw object
    }
});
Changes made directly to a raw object bypass the reactivity system and will not update the DOM. Only use unwrap() for reading or identity comparisons, not for writing.