state-manager-utility
v3.0.2
Published
Global state management library
Maintainers
Readme
State Manager Utility
A tiny yet powerful state management library for front-end projects. Follows pub-sub architecture to maintain and broadcast state. Can be used as a lightweight alternative to Redux.
Features
✨ Lightweight - Minimal footprint, maximum power
🔄 Pub-Sub Pattern - Simple event-based state management
💾 Persistence Support - Optional state persistence across sessions
📘 TypeScript Support - Full type definitions included
🎯 No Dependencies - Zero runtime dependencies (peer dependency optional for persistence)
Installation
npm install state-manager-utility --saveQuick Start
JavaScript
import { EmitEvent, SubscribeToEvent, UnsubscribeEvent } from 'state-manager-utility';
// Emit an event with data
EmitEvent({
event: 'user:login',
data: { userId: 123, name: 'John' }
});
// ⚠️ IMPORTANT: Always define callback as a named function (not inline)
// This ensures you can properly unsubscribe using the same reference
function handleUserLogin(data) {
console.log('User logged in:', data);
}
// Subscribe to events
SubscribeToEvent({
event: 'user:login',
callback: handleUserLogin // Named function reference
});
// Cleanup: Unsubscribe when no longer needed
UnsubscribeEvent({
event: 'user:login',
callback: handleUserLogin // Same reference used for unsubscribing
});TypeScript
import { EmitEvent, SubscribeToEvent, UnsubscribeEvent } from 'state-manager-utility';
interface User {
userId: number;
name: string;
}
// Emit typed events
EmitEvent({
event: 'user:login',
data: { userId: 123, name: 'John' } as User
});
// Define callback as a named function for proper unsubscription
function handleUserLogin(data: User, meta?: { event: string, extraParams: any }) {
console.log('User logged in:', data.name);
console.log('Event name:', meta?.event);
}
// Subscribe with typed callback
SubscribeToEvent({
event: 'user:login',
callback: handleUserLogin // Named function reference
});
// Cleanup: Unsubscribe when no longer needed
UnsubscribeEvent({
event: 'user:login',
callback: handleUserLogin // Same reference
});API Reference
EmitEvent
Broadcasts an event with data. The event can be stored for future subscribers or transmitted temporarily.
EmitEvent({
event: string,
data: any,
isMemoryStore?: boolean,
isTemp?: boolean,
dontTransmit?: boolean
}): voidParameters:
event(string) - Unique identifier for the eventdata(any) - Data to be broadcastedisMemoryStore(boolean, optional) - Iftrue, persists event across sessions (requires storage utility initialization)isTemp(boolean, optional) - Iftrue, event is broadcasted only once without storingdontTransmit(boolean, optional) - Iftrue, stores the event but doesn't broadcast immediately
Example:
EmitEvent({
event: 'cart:updated',
data: { items: 3, total: 99.99 },
isMemoryStore: true // Persist across sessions
});SubscribeToEvent
Subscribes to an event and receives callbacks when the event is emitted.
SubscribeToEvent({
event: string,
callback: (payload?: any, meta?: { event: string, extraParams: any }) => void,
extraParams?: any,
isMemoryStore?: boolean,
isTemp?: boolean,
skipOldEvent?: boolean
}): voidParameters:
event(string) - Event identifier to subscribe tocallback(function) - Function called when event is emitted. Receives(data, { event, extraParams })extraParams(any, optional) - Additional parameters passed back in callbackisMemoryStore(boolean, optional) - Subscribe to persisted eventsisTemp(boolean, optional) - Unsubscribe automatically after first callbackskipOldEvent(boolean, optional) - Iftrue, doesn't trigger callback for existing event data
Example:
// Define callback as named function
function handleCartUpdate(data, meta) {
console.log(`Cart has ${data.items} items`);
console.log('Extra params:', meta.extraParams);
}
SubscribeToEvent({
event: 'cart:updated',
callback: handleCartUpdate,
extraParams: { source: 'header-component' }
});
// Cleanup when done
UnsubscribeEvent({
event: 'cart:updated',
callback: handleCartUpdate
});UnsubscribeEvent
Removes an event listener.
UnsubscribeEvent({
event: string,
callback: (payload?: any) => void,
isMemoryStore?: boolean
}): voidParameters:
event(string) - Event identifiercallback(function) - Same callback function reference used inSubscribeToEventisMemoryStore(boolean, optional) - Target persisted event store
Example:
// ⚠️ CRITICAL: Use the same function reference for both subscribe and unsubscribe
function handleCartUpdate(data) {
console.log('Cart updated:', data);
}
// Subscribe
SubscribeToEvent({
event: 'cart:updated',
callback: handleCartUpdate // Store this reference
});
// Later, unsubscribe using the SAME reference
UnsubscribeEvent({
event: 'cart:updated',
callback: handleCartUpdate // Must be the same function
});
// ❌ WRONG: This will NOT work (inline functions create new references)
// SubscribeToEvent({
// event: 'cart:updated',
// callback: (data) => console.log(data) // New reference
// });
// UnsubscribeEvent({
// event: 'cart:updated',
// callback: (data) => console.log(data) // Different reference - won't unsubscribe!
// });GetBroadcasterData
Retrieves stored event data without subscribing.
GetBroadcasterData({
event: string,
isMemoryStore?: boolean
}): { success: true, response: any } | { success: false }Parameters:
event(string) - Event identifierisMemoryStore(boolean, optional) - Retrieve from persisted store
Returns:
- Object with
success: trueandresponse(event data) if found - Object with
success: falseif event not found
Example:
const result = GetBroadcasterData({ event: 'user:login' });
if (result.success) {
console.log('User data:', result.response);
}DeleteEvent
Removes an event from the store. All listeners will stop receiving updates.
DeleteEvent({
event: string,
isMemoryStore?: boolean
}): voidParameters:
event(string) - Event identifier to removeisMemoryStore(boolean, optional) - Delete from persisted store
Example:
DeleteEvent({ event: 'temp:session', isMemoryStore: false });HasEventSubscribed
Checks if anyone is currently subscribed to an event.
HasEventSubscribed({
event: string,
isMemoryStore?: boolean
}): booleanParameters:
event(string) - Event identifierisMemoryStore(boolean, optional) - Check persisted store
Returns:
trueif there are active subscribersfalseif no subscribers
Example:
if (HasEventSubscribed({ event: 'notifications:new' })) {
console.log('Someone is listening to notifications');
}InitialiseStateManager
Initialize the library with storage utilities for persistence support. Only required if you want events to persist across sessions.
InitialiseStateManager({
StorageUtils: {
SetItem: (key: string, value: any) => void,
GetItem: (key: string) => any
}
}): voidParameters:
StorageUtils(object) - Object withSetItemandGetItemfunctions from storage-utility
Example:
import { InitialiseStateManager } from 'state-manager-utility';
import { SetItem, GetItem } from 'storage-utility';
// Initialize with storage utilities
InitialiseStateManager({
StorageUtils: { SetItem, GetItem }
});
// Now you can use isMemoryStore option
EmitEvent({
event: 'user:preferences',
data: { theme: 'dark' },
isMemoryStore: true // Will persist across sessions
});Best Practices
⚠️ Always Use Named Functions for Callbacks
CRITICAL: When subscribing to events, always define callbacks as named functions (not inline arrow functions) if you plan to unsubscribe later. This is because UnsubscribeEvent requires the exact same function reference to properly remove the listener.
// ✅ CORRECT: Named function allows proper unsubscription
function handleEvent(data) {
console.log(data);
}
SubscribeToEvent({ event: 'myEvent', callback: handleEvent });
UnsubscribeEvent({ event: 'myEvent', callback: handleEvent }); // Works!
// ❌ WRONG: Inline functions create different references
SubscribeToEvent({
event: 'myEvent',
callback: (data) => console.log(data) // Reference #1
});
UnsubscribeEvent({
event: 'myEvent',
callback: (data) => console.log(data) // Reference #2 - Different! Won't work!
});Why this matters:
- JavaScript compares function references, not function content
- Each inline function creates a new reference, even if the code is identical
- Without matching references,
UnsubscribeEventcannot find and remove the listener - This leads to memory leaks and unexpected behavior
Exception: You can use inline functions for temporary subscriptions (isTemp: true) since they auto-cleanup.
Usage Patterns
React Component Example
import React, { useEffect, useState } from 'react';
import { EmitEvent, SubscribeToEvent, UnsubscribeEvent } from 'state-manager-utility';
function CartComponent() {
const [cartCount, setCartCount] = useState(0);
useEffect(() => {
const handleCartUpdate = (data) => {
setCartCount(data.items);
};
// Subscribe to cart updates
SubscribeToEvent({
event: 'cart:updated',
callback: handleCartUpdate
});
// Cleanup on unmount
return () => {
UnsubscribeEvent({
event: 'cart:updated',
callback: handleCartUpdate
});
};
}, []);
const addToCart = () => {
EmitEvent({
event: 'cart:updated',
data: { items: cartCount + 1 }
});
};
return (
<div>
<p>Cart Items: {cartCount}</p>
<button onClick={addToCart}>Add to Cart</button>
</div>
);
}Temporary Events (One-time Subscription)
// Note: For temp subscriptions, inline functions are acceptable since no manual cleanup needed
// The subscription auto-removes after first callback
SubscribeToEvent({
event: 'modal:opened',
callback: (data) => {
console.log('Modal opened once:', data);
},
isTemp: true // Auto-unsubscribes after first callback
});
// Or with named function if you need to cancel before it fires
function handleModalOpen(data) {
console.log('Modal opened:', data);
}
SubscribeToEvent({
event: 'modal:opened',
callback: handleModalOpen,
isTemp: true
});
// You can still manually unsubscribe before it fires if needed
UnsubscribeEvent({
event: 'modal:opened',
callback: handleModalOpen
});Skip Old Events
// Define callback as named function
function handleLiveUpdate(data) {
console.log('New update:', data);
}
// Don't trigger callback with existing data, only new emissions
SubscribeToEvent({
event: 'live:updates',
callback: handleLiveUpdate,
skipOldEvent: true // Only react to new events
});
// Cleanup when component unmounts
UnsubscribeEvent({
event: 'live:updates',
callback: handleLiveUpdate
});When to Use
✅ Good for:
- Small to medium React/Vue/Angular applications
- Component communication without prop drilling
- Simple state sharing across unrelated components
- Event-driven architectures
- Lightweight state management needs
⚠️ Consider alternatives for:
- Large applications with complex state logic (use Redux/MobX)
- Applications requiring time-travel debugging
- Heavy state transformations and middleware requirements
License
MIT © Shubham Kesarwani
Repository
Author
Shubham Kesarwani
📧 [email protected]
🔗 GitHub
