State (Store)

State provides deep reactivity for objects and arrays. Unlike signals which only track reassignment, state tracks nested property changes automatically.

The Problem with Signals + Objects

// Signals only react to reassignment
const user = signal({ name: 'Alice', age: 25 });

user.value.age = 26;           // ❌ Won't trigger updates!
user.value = { ...user.value, age: 26 };  // ✅ Works, but verbose

// Arrays have the same issue
const items = signal([1, 2, 3]);
items.value.push(4);           // ❌ Won't trigger updates!
items.value = [...items.value, 4];  // ✅ Works, but tedious

State to the Rescue

const { state } = LightviewX;

// Deep reactivity - mutations work!
const user = state({ name: 'Alice', age: 25 });

user.age = 26;                 // ✅ Triggers updates!
user.name = 'Bob';             // ✅ Triggers updates!

// Arrays just work
const items = state([1, 2, 3]);
items.push(4);                 // ✅ Triggers updates!
items[0] = 10;                 // ✅ Triggers updates!
items.sort();                  // ✅ Triggers updates!

Nested Objects

State tracks changes at any depth:

const app = state({
    user: {
        profile: {
            name: 'Alice',
            settings: {
                theme: 'dark',
                notifications: true
            }
        }
    },
    items: []
});

// All of these trigger updates:
app.user.profile.name = 'Bob';
app.user.profile.settings.theme = 'light';
app.items.push({ id: 1, text: 'Hello' });

In the UI

const todos = state([
    { text: 'Learn Lightview', done: true },
    { text: 'Build app', done: false }
]);

div(
    ul(() => todos.map((todo, i) => 
        li(
            input({ 
                type: 'checkbox', 
                checked: todo.done,
                onchange: () => todos[i].done = !todos[i].done
            }),
            span(todo.text)
        )
    )),
    button({ onclick: () => todos.push({ text: 'New', done: false }) }, 'Add')
)

Array Methods

All mutating array methods are reactive:

const items = state([1, 2, 3]);

items.push(4);         // Add to end
items.pop();           // Remove from end
items.shift();         // Remove from start
items.unshift(0);      // Add to start
items.splice(1, 1);    // Remove at index
items.sort();          // Sort in place
items.reverse();       // Reverse in place
items.fill(0);         // Fill with value

Named State

Like signals, you can name state objects for global access. This is especially useful for shared application state:

// Create named state
const appState = state({ 
    user: 'Guest', 
    theme: 'dark' 
}, 'app');

// Retrieve it anywhere
const globalState = state.get('app');

// Get or create
const settings = state.get('settings', { notifications: true });

Stored State

You can store named state objects in Storage objects (e.g. sessionStorage or localStorage) for persistence. It will be saved any time there is a change. Objects are automatically serialized to JSON and deserialized back to objects.

const user = state({name:'Guest', theme:'dark'}, {name:'user', storage:sessionStorage});

// Retrieve it elsewhere (even in another file)
const sameUser = state.get('user');

// Get or create with default value
// If 'user' exists, returns it. If not, creates it with default value.
const score = state.get('user', {storage:sessionStorage, defaultValue:{name:'Guest', theme:'dark'}});

Note: Manually updating the object in storage will not trigger updates.

Signal vs State

Use Signal Use State
Primitives (numbers, strings, bools) Objects with nested properties
Simple objects (replace whole thing) Objects you'll mutate in place
Arrays you'll replace Arrays you'll push/pop/splice
Slightly better performance More convenient API

Pro Tip

You can mix both! Use signals for simple values and state for complex structures:

const isLoading = signal(false);      // Simple boolean → signal
const error = signal(null);           // Simple value → signal
const items = state([]);              // Array to mutate → state (from LightviewX)
const formData = state({              // Object to mutate → state (from LightviewX)
    name: '',
    email: '',
    message: ''
});