-
Notifications
You must be signed in to change notification settings - Fork 1
State Management
LARC provides comprehensive state management capabilities: persistent, cross-tab synchronized, offline-first state management while maintaining the zero-build, framework-agnostic philosophy.
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
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 cacheCreates 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>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 pendingRoutes 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>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>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/falseThe 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();<!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><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><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>- Use retained messages for important state - Session data, user prefs, UI state
- Debounce frequently changing values - Input fields, scroll positions
- Validate at boundaries - User input, API responses
- Choose appropriate storage - Memory for temporary, IndexedDB for large data
- Monitor performance - Use pan-inspector metrics view
- Test offline scenarios - Use Chrome DevTools network throttling
- Handle conflicts gracefully - Choose appropriate resolution strategy
- Clean up subscriptions - Unsubscribe in disconnectedCallback
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
- API-Reference - Full API documentation
- Quick-Start-Guide - Getting started
- Troubleshooting - Common issues