A lightweight, type-safe state management solution built on functional programming principles with immutable state updates and time-travel debugging.
- Immutable state using Immer
- Type-safe actions and state (TypeScript first)
- Time-travel debugging with undo/redo
- Functional API with pure reducers
- Reactive subscriptions to state changes
- Minimal boilerplate
- Framework agnostic (works with React, Vue, Svelte, or vanilla JS)
pnpm installimport { createStore } from '@your-repo/fp-state';
const store = createStore({
initialState: { count: 0 },
handlers: {
increment: (draft) => {
draft.count += 1;
},
add: (draft, amount: number) => {
draft.count += amount;
}
}
});// Dispatch actions
store.dispatch('increment');
store.dispatch('add', 5);
// Get current state
const state = store.getState();
// Subscribe to changes
store.subscribe('*', (state, context) => {
console.log('State changed:', state);
console.log('Action context:', context);
});
// Undo/redo
store.undo();
store.redo();Creates a new store instance.
Parameters:
config.initialState: The initial state objectconfig.handlers: Action handlers object (shape:{ [action: string]: (draft, payload) => void })
Returns: Store instance
Returns a frozen copy of the current state.
Dispatches an action to update state.
Subscribes to state changes. Returns unsubscribe function.
actionType: Specific action or"*"for all actionslistener: Callback(state, context) => void
Reverts to previous state. Returns true if undo was successful.
Reapplies next state. Returns true if redo was successful.
past(): Returns array of past statesfuture(): Returns array of future states
All state updates are handled immutably using Immer. Your handlers write "mutable" code that gets converted to immutable updates.
Action handlers are pure functions that take current state (draft) and payload, returning nothing (they modify the draft).
Full TypeScript support with inferred types for:
- State shape
- Action payloads
- Subscription contexts
Built-in history tracking enables:
- Undo/redo functionality
- State snapshots
- Action replay
Dispatch → Apply Handler → Update State → Notify Subscribers
↑
(Immer draft)
const store = createStore({
initialState: { user: null, todos: [] },
handlers: {
login: (draft, user: User) => {
draft.user = user;
},
addTodo: (draft, todo: Todo) => {
draft.todos.push(todo);
}
}
});Subscribe to get rich action context:
store.subscribe('*', (state, context) => {
console.log('Action:', context.action); // "DISPATCH", "UNDO", "REDO"
console.log('Method:', context.method); // action type
console.log('Payload:', context.payload);
console.log('Timestamp:', context.timestamp);
});function withLogger(store) {
const originalDispatch = store.dispatch;
return {
...store,
dispatch: (actionType, payload) => {
console.log('Dispatching:', actionType, payload);
originalDispatch(actionType, payload);
}
};
}
const loggedStore = withLogger(store);- Time-Travel Debugging:
console.log('Past states:', store.history.past());
console.log('Future states:', store.history.future());- Action Logging:
store.subscribe('*', (_, context) => {
console.groupCollapsed(`Action: ${context.method}`);
console.log('Payload:', context.payload);
console.log('State:', store.getState());
console.groupEnd();
});- Predictable state - Single source of truth
- Maintainable - Clear state transitions
- Testable - Pure handlers are easy to test
- Scalable - Works for small to large apps
- Debuggable - Full action history
See the full todo app implementation in the examples folder demonstrating:
- Adding todos
- Toggling completion
- Removing todos
- Clearing all todos
- Undo/redo functionality
- Action logging
© 2025 built by Haileiyesus Mesafint as part of iCog-Labs AGI internship training program.