Skip to content

Sponsor

Sponsor

What is Reactive Enhancements?

Quick Start (30 seconds)

javascript
// Safe effect that won't crash your app
const dispose = safeEffect(() => {
  Elements.output.update({ textContent: state.count });
});

// Async effect with automatic cancellation
const stop = asyncEffect(async (signal) => {
  const response = await fetch(`/api/data?q=${state.query}`, { signal });
  const data = await response.json();
  Elements.results.update({ textContent: JSON.stringify(data) });
});

// Enhanced async state with race condition prevention
const users = asyncState(null);
await users.execute(async (signal) => {
  const res = await fetch('/api/users', { signal });
  return res.json();
});
console.log(users.data);     // [...users]
console.log(users.isSuccess); // true

What is Reactive Enhancements?

Reactive Enhancements is a production-hardening module for the DOMHelpers reactive system. It adds six categories of improvements on top of the core reactive library:

  1. Enhanced Batching — Priority-based update queue with infinite loop detection
  2. Deep Reactivity for Collections — Reactive Map and Set support
  3. Enhanced Computed Properties — Smart caching with circular dependency detection
  4. Error Boundaries — Isolated error handling so one bad effect doesn't crash everything
  5. Async Support — Async effects with AbortSignal and race-condition-safe async state
  6. DevTools — State inspection, change history, and debugging tools

Simply put, these are the features that take the reactive system from "works in development" to "reliable in production."


Why Does This Exist?

The Challenges in Production Reactive Systems

As reactive apps grow, you encounter problems that the core reactive library wasn't designed to handle:

Production Challenges:

├── Batching issues
│   └── Multiple effects for the same state run in unpredictable order

├── Collection limitations
│   └── Map and Set mutations don't trigger reactive effects

├── Computed inefficiency
│   └── Computed properties recalculate even when nothing changed
│   └── Circular computed properties cause infinite loops

├── Error fragility
│   └── One effect throwing an error can stop other effects

├── Async complexity
│   └── Fetching data creates race conditions
│   └── Previous requests aren't cancelled when new ones start

└── Debugging difficulty
    └── Hard to see what state changed and why

What Enhancements Add

After enhancements load:

├── Priority queue
│   └── Computed → Watchers → Effects (guaranteed order)
│   └── Infinite loop detection (max 100 flushes)

├── Reactive Map and Set
│   └── map.set(), set.add() trigger reactive effects

├── Smart computed
│   └── Caches results, only recalculates when dirty
│   └── Throws clear error on circular dependencies

├── Error boundaries
│   └── Effects wrapped in try/catch with retry support
│   └── One effect failing doesn't affect others

├── Async effects
│   └── AbortController integration for automatic cancellation
│   └── Race condition prevention via request ID tracking

└── DevTools
    └── Track states, log changes, inspect history
    └── Auto-enabled on localhost

Mental Model

Think of the enhancements module as a building inspection and upgrade for your reactive system.

The core reactive library is the house — it has walls, a roof, and plumbing (state, effects, watchers). The enhancements module is the inspector who comes in and:

  • Adds a priority mail system so important updates arrive first (batching)
  • Installs smart meters on the water and electricity (Map/Set reactivity)
  • Puts circuit breakers in the electrical panel so one short doesn't take out the whole house (error boundaries)
  • Adds automatic shutoff valves for when things go wrong (async cancellation)
  • Installs security cameras so you can see what's happening (DevTools)

The house still works the same way — but now it's production-ready.


What Changes When This Module Loads?

Transparent patches (existing code works, no API changes)

FeatureWhat changes
state()Now wraps Map/Set properties in reactive proxies
computed(state, { key: fn })Now caches results and detects circular dependencies

New functions added to ReactiveUtils

FunctionDescription
safeEffect(fn, opts)Effect with error boundary and retry
safeWatch(state, key, fn, opts)Watcher with error boundary
asyncEffect(fn, opts)Async effect with AbortSignal
asyncState(initial, opts)Async state with race prevention
ReactiveUtils.ErrorBoundaryError boundary class
ReactiveUtils.DevToolsDevelopment tools

New global

javascript
ReactiveEnhancements          // The full API object
ReactiveEnhancements.batch    // Enhanced batch
ReactiveEnhancements.queueUpdate  // Priority queue function
ReactiveEnhancements.safeEffect   // Safe effect
ReactiveEnhancements.safeWatch    // Safe watcher
ReactiveEnhancements.asyncEffect  // Async effect
ReactiveEnhancements.asyncState   // Async state
ReactiveEnhancements.ErrorBoundary // Error boundary class
ReactiveEnhancements.DevTools     // DevTools
ReactiveEnhancements.PRIORITY     // Priority constants

Big Picture: How Enhancements Fit In

DOMHelpers Reactive System (load order)

├── reactive module               → Core state, effects, computed, watchers
├── 02_dh-reactive-array-patch    → Array mutation reactivity
├── 03_dh-reactive-collections    → Collection CRUD
├── 04_dh-reactive-form           → Form state management
├── 05_dh-reactive-cleanup        → Lifecycle & disposal
├── 06_dh-reactive-enhancements   → Production hardening ← YOU ARE HERE

The enhancements module loads last and patches everything above. It depends on the reactive core (01) and benefits from the cleanup system (05).


The Six Parts at a Glance

06_dh-reactive-enhancements.js

├── PART 1: Enhanced Batching
│   ├── Priority queue: COMPUTED (1) → WATCH (2) → EFFECT (3)
│   ├── queueMicrotask-based flush
│   └── Infinite loop detection (100 flush max)

├── PART 2: Deep Reactivity for Collections
│   ├── createReactiveMap() — Proxy wrapper for Map
│   ├── createReactiveSet() — Proxy wrapper for Set
│   └── Auto-applied when state() detects Map/Set properties

├── PART 3: Enhanced Computed Properties
│   ├── Caching via WeakMap
│   ├── Dirty tracking per tick
│   └── Circular dependency detection with stack trace

├── PART 4: Error Boundaries
│   ├── ErrorBoundary class with retry and fallback
│   ├── safeEffect() — effect with error boundary
│   └── safeWatch() — watcher with error boundary

├── PART 5: Async Support
│   ├── asyncEffect() — effect with AbortSignal
│   └── asyncState() — state with execute, abort, reset, refetch

└── PART 6: DevTools
    ├── trackState(), trackEffect(), logChange()
    ├── getStates(), getHistory(), clearHistory()
    └── Auto-enabled on localhost

Key Takeaways

  1. Enhancements are production-hardening — they make the reactive system more robust, not more complex
  2. Transparent patches — existing code works without changes
  3. Six categories: batching, collections, computed, errors, async, devtools
  4. Priority queue ensures computed properties update before effects
  5. Error boundaries prevent one bad effect from crashing others
  6. Async support handles cancellation and race conditions automatically
  7. DevTools auto-enable on localhost for easy debugging

What's next?

Let's explore the enhanced batching system and how the priority queue ensures consistent updates.

Let's continue!