Troubleshooting Guide
Common issues and solutions for the Context-Action framework.
🔧 Common Framework Issues
Memory Leak Issues
Event Object Storage Prevention
Issue: DOM event objects being stored in stores causing memory leaks.
Solution: The framework automatically detects and prevents storing event objects:
// ❌ This will trigger an error and prevent storage
store.setValue({ event: domEvent });
// ✅ Extract only needed data
store.setValue({
eventData: {
clientX: domEvent.clientX,
clientY: domEvent.clientY,
timestamp: Date.now()
}
});Error Message:
[Context-Action] Event object detected in Store.setValue - this may cause memory leaksEventBus Memory Optimization
Issue: Large objects (DOM elements, React components) stored in event history causing memory leaks.
Solution: EventBus automatically stores only essential metadata for memory-heavy objects:
// EventBus automatically handles this:
eventBus.emit('domUpdate', domElement);
// Stores: { __eventBusDataType: 'DOMElement', tagName: 'DIV', id: 'myId', className: 'container' }Type Compatibility Issues
Cross-Platform Timeout Types
Issue: setTimeout returns different types in browser vs Node.js environments.
Solution: Use proper timeout types for cross-platform compatibility:
// ✅ Framework uses this approach internally
private batchTimeoutId: ReturnType<typeof requestAnimationFrame> | null = null;Comparison & Circular Reference Issues
Circular Reference Detection
Issue: Incorrect circular reference detection in deep comparison.
Solution: The framework uses improved circular reference checking:
// Framework handles this automatically:
// Checks each value individually rather than both together
if (visited.has(a)) {
return Object.is(a, b);
}
if (visited.has(b)) {
return Object.is(a, b);
}Error Handling Standardization
Centralized Error System
Issue: Inconsistent error handling across modules.
Solution: All modules use standardized ErrorHandlers:
// Framework uses centralized error handling
ErrorHandlers.store('Error in event handler', {
event: eventName,
handlerCount: handlers.size
}, error);🚨 Critical Performance Issues
For detailed troubleshooting guides, see:
- Infinite Loop Issues - Comprehensive guide for all infinite loop patterns (NEW)
- Performance Issues - Memory management, re-renders, and optimization
Quick Reference: Infinite Loop Prevention
Toast System Infinite Loops
Issue: Application freezes when toast limit is reached, causing infinite HMR updates.
Root Cause: When maxToasts limit is reached, removing old toasts triggers tracked actions that create new toasts:
// ❌ PROBLEM: Creates infinite loop
if (currentToasts.length >= config.maxToasts) {
// This dispatch is tracked and creates a new toast!
toastActionRegister.dispatch('removeToast', { toastId: oldestToast.id });
}Symptoms:
- Browser DevTools shows continuous HMR updates
- Application becomes unresponsive after 4-5 consecutive actions
- Console logs show:
Current toast state: {currentToastsCount: 4, maxToasts: 4, stackIndex: 14}
Solution: Use direct store updates instead of dispatching actions:
// ✅ SOLUTION: Direct store update prevents loop
if (currentToasts.length >= config.maxToasts) {
clearToastTimers(oldestToast.id);
const filteredToasts = currentToasts.filter(toast => toast.id !== oldestToast.id);
toastsStore.setValue(filteredToasts); // Direct update - no action dispatch
}Action Handler Re-registration Loops
Issue: Handlers constantly re-registering causing performance degradation.
Root Cause: useCallback dependencies changing on every render:
// ❌ PROBLEM: Dependencies cause re-registration
useEffect(() => {
const unsubscribe = register('action', handler);
return unsubscribe;
}, [handler, store]); // These change every render!Solution: Use stable references with refs:
// ✅ SOLUTION: Stable handler registration
const handlersRef = useRef({ /* handlers */ });
useEffect(() => {
handlersRef.current = { /* updated handlers */ };
}, [store]);
const stableHandler = useCallback((payload) =>
handlersRef.current.handler(payload), []);
useEffect(() => {
return register('action', stableHandler);
}, []); // Empty deps - register only onceTimer Cascade Problems
Issue: Multiple timers creating cascading effects in rapid succession.
Root Cause: Each action creates multiple timers without cleanup:
// ❌ PROBLEM: Timer accumulation
function sendMessage() {
setTimeout(() => simulateTyping(), 100); // Timer 1
setTimeout(() => autoScroll(), 150); // Timer 2
if (shouldAutoRespond) {
setTimeout(() => respond(), 1500); // Timer 3
}
} // No cleanup mechanism!Solution: Centralized timer management:
// ✅ SOLUTION: Managed timers
const timersRef = useRef<NodeJS.Timeout[]>([]);
const addTimer = (timer: NodeJS.Timeout) => {
timersRef.current.push(timer);
};
const clearAllTimers = () => {
timersRef.current.forEach(clearTimeout);
timersRef.current = [];
};
useEffect(() => () => clearAllTimers(), []); // Cleanup on unmountRate Limiting and Toast Management
Toast Spam Prevention
Issue: Rapid consecutive actions creating too many toasts.
Configuration: Rate limiting is disabled by default to avoid blocking legitimate user interactions:
// Default: No rate limiting - all toasts shown
setupSelectiveActionToast(actionRegister, trackedActions);
// Optional: Enable rate limiting for high-traffic scenarios
setupSelectiveActionToast(actionRegister, trackedActions, {
enableRateLimit: true,
maxToasts: 5, // Max 5 toasts per second
resetInterval: 1000 // Reset counter every 1 second
});Prevention Strategy: Instead of aggressive rate limiting, use action filtering:
// ✅ SOLUTION: Exclude problematic action types
const trackedActions = [
'sendMessage', // Track important actions
'addToCart',
// Exclude removal actions that can cause loops:
// 'deleteMessage', 'removeFromCart', 'clearChat'
];Memory Leak Prevention
Best Practices:
- Timer Cleanup: Always clean up timers
- Action Filtering: Exclude removal actions from toast tracking
- Direct Store Updates: Use store operations instead of action dispatches for internal operations
- Duplicate Prevention: Prevent identical toasts within short timeframes
🚨 Common Runtime Issues
Handler State Access Problems
Stale State in Handlers
Issue: Using component scope values in action handlers leads to stale state.
// ❌ PROBLEM: Stale closure value
function MyComponent() {
const user = useStoreValue(userStore); // Trapped in closure!
useActionHandler('updateUser', async () => {
if (user.isActive) { // This could be stale!
// Update logic
}
});
}Solution: Always get fresh state from stores:
// ✅ SOLUTION: Fresh state access
function MyComponent() {
useActionHandler('updateUser', useCallback(async () => {
const currentUser = userStore.getValue(); // Always fresh!
if (currentUser.isActive) {
// Update logic
}
}, [userStore]));
}Performance Issues
Excessive Re-renders
Issue: Components re-rendering too frequently.
Diagnostics:
// Add logging to track re-renders
function MyComponent() {
console.log('Component rendered:', Date.now());
const value = useStoreValue(myStore);
return <div>{value}</div>;
}Solutions:
- Check store comparison strategy:
// Use appropriate comparison for your data
store.setComparisonOptions({ strategy: 'shallow' }); // For objects
store.setComparisonOptions({ strategy: 'reference' }); // For primitives- Optimize selectors:
// ❌ Creates new object each time
const data = useStoreValue(store, (value) => ({ computed: value * 2 }));
// ✅ Stable reference with proper comparison
const data = useStoreValue(store, useCallback(
(value) => ({ computed: value * 2 }),
[]
));Memory Issues
Unresolved Refs
Issue: RefContext promises never resolving.
Diagnostics:
const { waitForMount } = useRefHandler('myRef');
// Add timeout for debugging
Promise.race([
waitForMount(),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Ref mount timeout')), 5000)
)
]).catch(console.error);Solutions:
- Ensure ref is properly set:
const { setRef } = useRefHandler('myRef');
// Make sure this is called
<div ref={setRef}>Content</div>- Check component mounting order:
// Ref handlers should be registered before components mount
function RefLogicComponent({ children }) {
// Register handlers here
useRefHandler('myRef', ...);
return children;
}🔍 Debugging Tools
Error Statistics
Access error statistics for debugging:
import { getErrorStatistics } from '@context-action/react';
// Get comprehensive error information
const stats = getErrorStatistics();
console.log('Total errors:', stats.totalErrors);
console.log('Error types:', stats.errorsByType);
console.log('Recent errors:', stats.recentErrors);Store Debugging
Monitor store changes:
// Enable debug mode
store.setNotificationMode('immediate'); // For real-time updates
// Monitor listener count
console.log('Active listeners:', store.getListenerCount());
// Check comparison settings
console.log('Comparison options:', store.getComparisonOptions());EventBus Debugging
Monitor event flow:
// Check active events
console.log('Event names:', eventBus.getEventNames());
console.log('Handler counts:', eventBus.getTotalHandlerCount());
// View event history
console.log('Recent events:', eventBus.getHistory());🛡️ Prevention Strategies
Development Guidelines
- Always use
useCallbackfor action handlers - Never store event objects in stores
- Access fresh state with
store.getValue()in handlers - Handle async errors with try-catch blocks
- Clean up resources in useEffect cleanup functions
- Avoid circular logging dependencies - Don't use
actionLoggerin data change handlers that could trigger more actions - Use direct LogMonitor integration instead of generic
onDataChangedpatterns
Testing Recommendations
- Test unmounted scenarios to catch memory leaks
- Verify handler cleanup when components unmount
- Mock timers for consistent testing
- Test error boundaries for graceful error handling
Performance Monitoring
- Monitor memory usage in development
- Check re-render frequency with React DevTools
- Profile store operations in performance-critical paths
- Test with realistic data sizes to catch scaling issues
📞 Getting Help
If you encounter issues not covered here:
- Check the Error System: Review
getErrorStatistics()for detailed error information - Enable Debug Mode: Set stores to
immediatenotification mode for real-time debugging - Review Recent Changes: Check if issues started after recent updates
- Test in Isolation: Create minimal reproductions to isolate the problem
For persistent issues, the centralized error handling system provides detailed context and stack traces to help identify the root cause.