What is Reactive Enhancements?
Quick Start (30 seconds)
// 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); // trueWhat 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:
- Enhanced Batching — Priority-based update queue with infinite loop detection
- Deep Reactivity for Collections — Reactive
MapandSetsupport - Enhanced Computed Properties — Smart caching with circular dependency detection
- Error Boundaries — Isolated error handling so one bad effect doesn't crash everything
- Async Support — Async effects with
AbortSignaland race-condition-safe async state - 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 whyWhat 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 localhostMental 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)
| Feature | What 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
| Function | Description |
|---|---|
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.ErrorBoundary | Error boundary class |
ReactiveUtils.DevTools | Development tools |
New global
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 constantsBig 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 HEREThe 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 localhostKey Takeaways
- Enhancements are production-hardening — they make the reactive system more robust, not more complex
- Transparent patches — existing code works without changes
- Six categories: batching, collections, computed, errors, async, devtools
- Priority queue ensures computed properties update before effects
- Error boundaries prevent one bad effect from crashing others
- Async support handles cancellation and race conditions automatically
- 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!