A tiny, framework-agnostic state store for the web. Simple, reactive, and immutable. Works with everything.
- Immutable State: State is always frozen to prevent accidental mutations.
- Deep Freeze: Recursively freezes all nested objects for deep immutability.
- Deep Clone: Uses
structuredClone
if available, falls back toJSON.parse(JSON.stringify(...))
to ensure state updates are truly immutable. - Simple API: Only three methods:
get
,set
, andsubscribe
. - Functional Updates: Supports both direct value updates and updater functions.
- Subscription Model: Listen to state changes and react accordingly.
- Instant Notification: Optionally call listeners immediately with the current state.
- Unsubscribe Support: Easily remove listeners when no longer needed.
- Clear Error Handling: Provides actionable error messages for debugging.
- No External Dependencies: Self-contained, minimalistic, and easy to integrate.
- Developer Mode: Optional developer mode toggle for maximum performance in production scenarios.
Method | Description |
---|---|
get() |
Returns the current state. |
set((state) => {}) or set(value) |
Replaces the state. Accepts either a new state or an updater function that returns new state. |
subscribe((state => {})), false) |
Subscribes a listener function to state changes. Returns an unsubscribe function. Setting false to true will run the listener instantly. |
eis(null, {"dev": false}) |
Default exported function the first argument takes your inital state (uses set ) and the second is an optional configuration object that can be used to set developer mode to false disabling freezing and cloning for better performance. Returns an array of functions (get, set, subscribe). |
- Invalid Initial State: Logs an
InitError
ifinitialState
is undefined, defaulting to null. - Invalid Object Freeze: Throws a
TypeError
if an invalid object (e.g., string, number, null) is passed to_freeze
, expecting a plain object or array. - Non-serializable Object: Throws a
CloneError
if an object or array cannot be cloned due to non-serializable data (e.g., functions, Date objects, circular references). - Undefined Set Value: Logs a
SetError
if set is called with undefined. - Updater Returns Undefined: Logs a
NoOp
warning if an updater function returns undefined. - Invalid Listener: Logs a
SubscribeError
if a non-function is passed to subscribe, ignoring the subscription.
npm install @taujor/eis
or
yarn add @taujor/eis
Via CDN:
import eis from 'https://unpkg.com/@taujor/eis/eis.js'
Or include directly in your project:
import eis from '@taujor/eis'
import eis from '@taujor/eis';
const [get, set, subscribe] = eis({ count: 0 });
// Subscribe to state changes
const unsubscribe = subscribe((state) => {
console.log('State changed:', state);
}, true);
// Update state
set({ count: 1 });
// Logs: State changed: { count: 1 }
set((state) => ({ count: state.count + 1 }));
// Logs: State changed: { count: 2 }
// Get current state
console.log(get());
// { count: 2 }
// Unsubscribe
unsubscribe();
import { useEffect, useState } from 'react';
import eis from '@taujor/eis';
// Initialize the state store with a number
const [get, set, subscribe] = eis(0);
function Counter() {
// Use React state to trigger re-renders
const [count, setCount] = useState(get());
// Subscribe to eis state changes
useEffect(() => {
const unsubscribe = subscribe((newState) => {
console.log('Count:', newState);
setCount(newState); // Update React state
}, true); // Immediate invocation to sync initial state
return unsubscribe; // Cleanup on unmount
}, []);
return (
<div style={{ padding: '20px' }}>
<h1>Simple Counter</h1>
<p>Count: {count}</p>
<button
onClick={() => set((state) => state + 1)}
style={{ marginRight: '10px' }}
>
Increment
</button>
<button
onClick={() => set((state) => state - 1)}
>
Decrement
</button>
</div>
);
}
export default Counter;
- Simple State Management: Ideal for small to medium sized web apps.
- Immutable Data Flow: Ensures data integrity by preventing direct state mutations.
- Reactive UI Updates: Works well with UI libraries for reactive updates.
- Backend Task Scheduling & Caching: Useful for caching API requests or batching background jobs.
- Debugging: Clear error messages help identify issues during development.
- Testing: Predictable state updates make testing straightforward.
- JSON Serializable Objects:
JSON.parse(JSON.stringify(...))
does not handle non-serializable objects so for consistency non-serializable objects are also rejected when usingstructuredClone
. - Performance Overhead: Deep cloning and freezing can be slow for very large state objects. Try to keep them small or even better flat. Use multiple local stores instead of a single global store to achieve better performance. While still good practice this is now mitigated by setting developer mode to false to disable cloning/freezing in production like so
eis(null, {dev: false})
- Documentation: A documentation website for eis with tutorials, framework integration examples, and extensive technical documentation so everyone can use eis to its full potential.
Contributions are welcome! Please open an issue or submit a pull request.
If you're using eis.js why not show off about it? Let everyone know in the dedicated discussion