@fluixi/reactive
v1.0.0-alpha.57
Published
Fine-grained reactive signals, memos, effects, stores and an RxJS-style observable layer.
Readme
@fluixi/reactive
Fine-grained reactive signals, memos, effects, stores and an RxJS-style observable layer.
A powerful, fine-grained reactive system with SolidJS-inspired features for building high-performance applications.
Features
🎯 Core Reactivity
- Signals - Fine-grained reactive primitives
- Effects - Automatic dependency tracking
- Memos - Cached computed values
- Batch Updates - SolidJS-style batching with
runUpdates()
⚡ Advanced Features
- Priority-Based Scheduling - Optimal update ordering (RENDER → COMPUTE → EFFECT → TRANSITION)
- Transitions - Non-blocking updates for better UX
- Suspense Boundaries - Declarative async data loading
- Error Boundaries - Graceful error handling
- Context API - State sharing without prop drilling
- Render Effects - Synchronous DOM updates
- Reactions - Manual dependency tracking
- Owner Management - Advanced lifecycle control
🔍 Developer Experience
- Enhanced Debugging - Detailed warnings with dependency chains and performance metrics
- Memory Leak Detection - Find potential issues early
- Node Statistics - Monitor reactive graph performance
- TypeScript - Full type safety and inference
- RxJS Integration - Seamless Observable interop
Installation
npm install @fluixi/reactiveQuick Start
Basic Usage
import { createSignal, createEffect, createMemo, batch } from '@fluixi/reactive';
// Create reactive state
const [count, setCount] = createSignal(0);
// Derived state (cached)
const doubled = createMemo(() => count() * 2);
// Side effects (auto-tracking)
createEffect(() => {
console.log(`Count: ${count()}, Doubled: ${doubled()}`);
});
// Update state
setCount(1); // Effect runs: "Count: 1, Doubled: 2"
// Batch multiple updates
batch(() => {
setCount(2);
setCount(3);
}); // Effect runs once: "Count: 3, Doubled: 6"Advanced Usage
import {
startTransition,
useTransition,
createSuspense,
trackPromise,
catchError,
createContext,
useContext
} from '@fluixi/reactive';
// Non-blocking updates
const [isPending, start] = useTransition();
start(() => {
setLargeDataSet(expensiveComputation());
});
// Async data with suspense
const UserProfile = createSuspense(
() => {
const data = trackPromise(fetchUser(userId()));
return <Profile data={data} />;
},
<ProfileSkeleton />
);
// Error handling
const safeData = catchError(
() => parseData(input()),
(error) => {
console.error(error);
return fallbackValue;
}
);
// Context for state sharing
const ThemeContext = createContext({ mode: 'light' });
ThemeContext.Provider({
value: { mode: 'dark' },
children: () => <App />
});
// Consume context
const theme = useContext(ThemeContext);Priority-Based Scheduling
Updates are processed in optimal order:
enum UpdatePriority {
IMMEDIATE = 0, // Immediate synchronous updates
RENDER = 1, // DOM updates (highest priority)
COMPUTE = 2, // Memos and computed values
EFFECT = 3, // Side effects
TRANSITION = 4, // Low-priority updates (lowest)
}Batch & Update Queue
SolidJS-inspired batching with runUpdates():
batch(() => {
setA(1);
setB(2);
setC(3);
}); // All effects run once in priority order
// Internal mechanism:
// 1. Updates queued in updateQueue
// 2. runUpdates() processes queue
// 3. Flushes in priority order: renders → computations → effectsEnhanced Debugging
Automatic warnings for performance issues:
⚠️ [Reactivity Warning] Coarse Dependency Detected
Node Information:
┌─────────────┬──────────────────┐
│ Node Name │ userData │
│ Observers │ 18 │
└─────────────┴──────────────────┘
Reason:
Reading large object (23 keys) - consider using
derived signals for specific properties
Performance Metrics:
┌───────────────────┬──────────┐
│ Updates │ 45 │
│ Avg Frequency │ 120ms │
└───────────────────┴──────────┘
Suggestions:
• Use derived signals for specific properties
• Consider breaking into smaller signalsAPI Reference
Core API
createSignal(initialValue, options?)- Create reactive statecreateEffect(fn, options?)- Run side effectscreateMemo(fn, initialValue?, options?)- Create derived statebatch(fn)- Batch multiple updatesuntrack(fn)- Read signals without trackingcreateRoot(fn)- Create disposal root
Transitions
startTransition(fn)- Mark updates as low priorityuseTransition()- Track transition state
Suspense
createSuspense(fn, fallback)- Create async boundarytrackPromise(promise)- Register promise with suspense
Error Handling
catchError(fn, handler)- Handle errors gracefully
Context
createContext(defaultValue)- Create contextuseContext(context)- Consume context
Enhanced Effects
createRenderEffect(fn)- Synchronous DOM updatescreateReaction(onInvalidate)- Manual dependency trackingon(deps, fn, options?)- Explicit dependencies
Owner Management
getOwner()- Get current reactive ownerrunWithOwner(owner, fn)- Run with specific owner
Debugging
warnCoarseRead(node, reason)- Enhanced warningsgetNodeStats()- Get reactive graph statisticsfindPotentialLeaks()- Find memory leaksenableDevMode()- Enable dev mode features
Utilities
createEnhancedSignal(initialValue)- Signal with extrascreateAsyncSignal(asyncFn)- Async signalcreateLazySignal(fn)- Lazy-evaluated signalcreateSignalWithHistory(initialValue)- Signal with undo/redocreateThrottledSignal(signal, delay)- Throttled updatescreateDebouncedSignal(signal, delay)- Debounced updatescreateTrigger()- Manual triggercreateSelector(source, fn)- Keyed selectorcreateResource(fetcher, source?)- Async resourcecreateStore(initialValue)- Nested reactive object
Documentation
Complete documentation available at:
- Overview - Introduction and concepts
- Core API - Complete API reference
- SolidJS Features - Advanced features guide
- Advanced Patterns - Complex patterns
- Quick Reference - Quick lookup
- What's New - Latest enhancements
What's New (v3.2.0+)
Major Enhancements
✨ Priority-Based Scheduling - Updates execute in optimal order
✨ SolidJS-Style Batching - New runUpdates() mechanism
✨ Transitions API - Non-blocking updates with startTransition()
✨ Suspense Boundaries - Declarative async with createSuspense()
✨ Error Boundaries - Graceful error handling with catchError()
✨ Context API - State sharing with createContext()
✨ Enhanced Debugging - Detailed warnings and performance metrics
✨ Transition State Tracking - Automatic priority in computations
Breaking Changes
None! All enhancements are 100% backward compatible.
Performance Tips
Use transitions for expensive updates
startTransition(() => setLargeList(compute()));Batch related updates
batch(() => { setA(1); setB(2); });Use memos for expensive computations
const result = createMemo(() => expensive(signal()));Leverage suspense for async
createSuspense(() => trackPromise(fetch()), <Loading />);Monitor with debugging tools
const stats = getNodeStats(); const leaks = findPotentialLeaks();
Best Practices
✅ Do
- Use
batch()for multiple related updates - Use
startTransition()for non-urgent updates - Add error boundaries with
catchError() - Use context instead of prop drilling
- Monitor performance with debugging tools
❌ Don't
- Update signals without batching when multiple updates needed
- Block UI with expensive synchronous computations
- Ignore errors in reactive code
- Prop drill when context would work better
Building
nx build reactiveRunning Tests
nx test reactiveLicense
MIT
Credits
Inspired by:
- SolidJS - Reactivity primitives and scheduling
- React - Transitions and Suspense concepts
- Vue 3 - Reactivity system design
Version: 3.2.0+
Status: ✅ Production Ready
