Reactivity in CellUI

At the heart of CellUI is the Signal. Unlike React's useState, which triggers a complete re-render of your component function, CellUI components execute exactly once. A signal is a reactive container that strictly updates only the DOM nodes that care about it.

Basic Signals

You create a signal by calling signal(initialValue).

import { signal, view } from '@cmj/cellui';

function Counter() {
  // 1. Create the signal
  const count = signal(0);
  
  // 2. Mutate it via the .value property
  const increment = () => {
    count.value++;
  };
  
  // 3. Bind it directly into the view
  return view`
    <button onclick="${increment}">
      Clicked ${count} times
    </button>
  `;
}

Because count is a reference to the Signal class instance, CellUI binds it natively to the TextNode. When count.value++ happens, the button's text updates instantly in O(1) time without rebuilding the button.

Deep Object Signals (Proxies)

The true power of CellUI is its out-of-the-box support for Deep Signals via ES6 Proxies.

If you pass a JSON Object into a signal, CellUI recursively wraps it. You can mutate deep properties, and CellUI will efficiently update only the DOM nodes listening to that specific nested property!

const user = signal({
  profile: {
    name: 'Alice',
    preferences: { theme: 'dark' }
  }
});

function ProfileView() {
  const toggleTheme = () => {
    // This granular mutation works instantly!
    user.value.profile.preferences.theme.value = 'light';
  };

  return view`
    <!-- Binds only to the specific nested property -->
    <div>Theme: ${user.value.profile.preferences.theme}</div>
    <button onclick="${toggleTheme}">Toggle</button>
  `;
}

Side Effects (effect)

If you need to run arbitrary JavaScript code whenever a signal changes, use effect().

You do not need to declare a dependency array! CellUI tracks dependencies automatically based on which signals you read inside the effect block.

import { signal, effect } from '@cmj/cellui';

const searchQuery = signal("CellUI framework");

// This will log immediately, AND whenever searchQuery changes
effect(() => {
  console.log("Searching for:", searchQuery.value);
});

Because CellUI utilizes WeakRef internally for DOM bindings, signals do not cause detached DOM memory leaks when elements are removed from the page.