Skip to content

State Management

Christopher Robison edited this page Dec 12, 2025 · 1 revision

Advanced State Management in LARC

LARC provides comprehensive state management capabilities: persistent, cross-tab synchronized, offline-first state management while maintaining the zero-build, framework-agnostic philosophy.

Overview

LARC's state management suite provides:

  • Cross-tab synchronization - State stays in sync across browser tabs
  • Offline-first support - Queue mutations when offline, sync when reconnected
  • Flexible persistence - Route state to memory, localStorage, sessionStorage, or IndexedDB
  • Computed state - Derived values with automatic dependency tracking
  • Runtime validation - JSON Schema validation without build tools
  • Time-travel debugging - Undo/redo with history management
  • Enhanced debugging - State tree visualization, metrics, and performance profiling

Core Principles:

  • Zero build required - works directly in browsers
  • Framework agnostic - works with vanilla JS, React, Vue, etc.
  • Web Components based - declarative HTML configuration
  • Progressive enhancement - adopt features incrementally

State Components

1. pan-state-sync - Cross-Tab Synchronization

Synchronizes state across multiple browser tabs using BroadcastChannel API.

Features:

  • Leader election to prevent sync loops
  • Conflict resolution strategies (last-write-wins, merge, custom)
  • Automatic state hydration when tabs become visible
  • Tab heartbeat monitoring

Usage:

<pan-state-sync
  channel="myapp-sync"
  topics="users.*,todos.*"
  strategy="last-write-wins"
  leader="auto"
  debug>
</pan-state-sync>

API:

const sync = document.querySelector('pan-state-sync');

console.log(sync.isLeader);     // true/false
console.log(sync.tabId);        // Unique tab ID
await sync.requestFullSync();   // Force sync from leader
sync.clearCache();              // Clear local cache

2. pan-computed-state - Derived State

Creates computed values that automatically update when dependencies change.

Features:

  • Subscribe to multiple PAN topics
  • Async computation support
  • Debouncing for performance
  • Memoization strategies (none, shallow, deep)

Usage:

<pan-computed-state
  sources="cart.items,user.discount"
  output="cart.total"
  debounce="100"
  memo="shallow"
  retain>
  <script>
    (items, discount) => {
      const subtotal = items.reduce((sum, item) => sum + item.price, 0);
      return subtotal - (discount || 0);
    }
  </script>
</pan-computed-state>

Async Example:

<pan-computed-state
  sources="user.location"
  output="weather.current"
  async
  debounce="500">
  <script>
    async (location) => {
      const res = await fetch(`/api/weather?lat=${location.lat}&lon=${location.lon}`);
      return await res.json();
    }
  </script>
</pan-computed-state>

3. pan-offline-sync - Offline-First Support

Queues mutations when offline and automatically syncs when reconnected.

Features:

  • IndexedDB-based mutation queue
  • Automatic retry with exponential backoff
  • Conflict resolution (server-wins, client-wins, merge)
  • Network status monitoring

Usage:

<pan-offline-sync
  storage="offline-queue"
  retry-max="3"
  retry-delay="1000"
  topics="todos.*,notes.*"
  strategy="server-wins"
  endpoints='{"todos.*": "/api/todos", "notes.*": "/api/notes"}'
  debug>
</pan-offline-sync>

API:

const offlineSync = document.querySelector('pan-offline-sync');

console.log(offlineSync.online);         // true/false
console.log(offlineSync.queueLength);    // Number of pending mutations

await offlineSync.syncNow();             // Force sync if online
await offlineSync.clearQueue();          // Clear all pending

4. pan-persistence-strategy - Declarative Persistence

Routes state to different storage backends based on topic patterns.

Features:

  • Multiple storage backends (memory, localStorage, sessionStorage, IndexedDB)
  • TTL (time-to-live) support for cache expiration
  • Size limits per topic
  • Automatic hydration on page load

Usage:

<pan-persistence-strategy auto-hydrate debug>
  <!-- Session data in memory, expires in 30 min -->
  <strategy topics="session.*" storage="memory" ttl="1800000"></strategy>

  <!-- User preferences in localStorage -->
  <strategy topics="user.preferences.*" storage="localStorage"></strategy>

  <!-- Form drafts in sessionStorage -->
  <strategy topics="form.draft.*" storage="sessionStorage" ttl="3600000"></strategy>

  <!-- Large data in IndexedDB, max 5MB per item -->
  <strategy topics="*.list.*" storage="indexedDB" database="app-data" max-size="5242880"></strategy>
</pan-persistence-strategy>

5. pan-schema-validator - Runtime Validation

Validates PAN messages against JSON Schema without build tools.

Features:

  • Subset of JSON Schema Draft-07
  • Strict mode (reject invalid) or warning mode (log only)
  • Built-in format validators (email, uri, date-time, uuid, ipv4, ipv6)
  • Detailed error messages with paths

Usage:

<pan-schema-validator topic="users.item.*" strict>
  <script type="application/json">
  {
    "type": "object",
    "properties": {
      "id": { "type": "string" },
      "email": { "type": "string", "format": "email" },
      "age": { "type": "number", "minimum": 0, "maximum": 150 },
      "role": { "type": "string", "enum": ["admin", "user", "guest"] }
    },
    "required": ["id", "email"]
  }
  </script>
</pan-schema-validator>

6. pan-undo-redo - Time-Travel Debugging

Provides undo/redo functionality with history management.

Features:

  • Configurable history stack size
  • Automatic change batching (100ms window)
  • Auto-snapshot on interval
  • Jump to specific timestamp

Usage:

<pan-undo-redo
  topics="editor.*,canvas.*"
  max-history="50"
  channel="history"
  auto-snapshot
  snapshot-interval="5000"
  debug>
</pan-undo-redo>

Control:

// Via PAN topics
panClient.publish('history.undo', null);
panClient.publish('history.redo', null);
panClient.publish('history.clear', null);

// Direct API
const undoRedo = document.querySelector('pan-undo-redo');
undoRedo.undo();
undoRedo.redo();
console.log(undoRedo.canUndo);  // true/false
console.log(undoRedo.canRedo);  // true/false

7. Enhanced pan-store

The local reactive store includes composition helpers:

const store = createStore({ count: 0, name: 'Ada' });

// Select nested values
console.log(store.select('user.profile.name'));

// Derive computed values
store.derive('doubled', ['count'], (count) => count * 2);

// Batch updates (single event)
store.batch(({ set }) => {
  set('count', 10);
  set('name', 'Bob');
});

// Middleware
const unsub = store.use(({ key, value, oldValue }) => {
  console.log(`${key}: ${oldValue}${value}`);
});

// Reset to initial state
store.reset();

Quick Start

Basic Setup

<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
</head>
<body>
  <!-- Load LARC -->
  <script type="module">
    import '@larcjs/core';
  </script>

  <!-- PAN Bus -->
  <pan-bus debug></pan-bus>

  <!-- State Management Components -->
  <pan-persistence-strategy auto-hydrate>
    <strategy topics="*" storage="localStorage"></strategy>
  </pan-persistence-strategy>

  <pan-state-sync channel="myapp" topics="*"></pan-state-sync>

  <!-- Your app code -->
  <script type="module">
    import { PanClient } from '@larcjs/core';
    const panClient = new PanClient();

    // Publish state
    panClient.publish('user.name', 'Ada', { retain: true });

    // Subscribe to changes
    panClient.subscribe('user.name', ({ data }) => {
      console.log('Name changed:', data);
    });
  </script>
</body>
</html>

Common Patterns

Shopping Cart

<pan-persistence-strategy auto-hydrate>
  <strategy topics="cart.*" storage="localStorage"></strategy>
</pan-persistence-strategy>

<pan-computed-state sources="cart.items" output="cart.total" retain>
  <script>
    (items) => items.reduce((sum, item) => sum + item.price * item.quantity, 0)
  </script>
</pan-computed-state>

<pan-state-sync channel="cart" topics="cart.*"></pan-state-sync>

Form with Validation

<pan-schema-validator topic="forms.registration" strict>
  <script type="application/json">
  {
    "type": "object",
    "properties": {
      "email": { "type": "string", "format": "email" },
      "age": { "type": "integer", "minimum": 13 }
    },
    "required": ["email"]
  }
  </script>
</pan-schema-validator>

<pan-persistence-strategy auto-hydrate>
  <strategy topics="forms.draft.*" storage="sessionStorage" ttl="3600000"></strategy>
</pan-persistence-strategy>

Best Practices

  1. Use retained messages for important state - Session data, user prefs, UI state
  2. Debounce frequently changing values - Input fields, scroll positions
  3. Validate at boundaries - User input, API responses
  4. Choose appropriate storage - Memory for temporary, IndexedDB for large data
  5. Monitor performance - Use pan-inspector metrics view
  6. Test offline scenarios - Use Chrome DevTools network throttling
  7. Handle conflicts gracefully - Choose appropriate resolution strategy
  8. Clean up subscriptions - Unsubscribe in disconnectedCallback

Troubleshooting

Cross-tab sync not working:

  • Check BroadcastChannel browser support
  • Verify same channel name across tabs
  • Check browser console for errors

Offline sync not queuing:

  • Verify IndexedDB is available
  • Check topic patterns match mutations
  • Look for validation errors

State not persisting:

  • Check storage quotas (localStorage ~5-10MB, IndexedDB ~50MB+)
  • Verify topic patterns in persistence strategy
  • Check browser privacy settings

Undo/redo not working:

  • Ensure topics match tracked patterns
  • Check max-history limit
  • Verify changes aren't from undo-redo itself

See Also

Clone this wiki locally