README
HEERLIK
A lightweight, turnkey alternative for managing your application state.
- Small with zero runtime dependencies
- Minimises abstractions and indirections typically associated with state updates
- Typesafe & immutable
- Removes the need to dispatch actions, reducers, or handle complex immutable state update logic
- Integrates with the Redux Devtools extension
- Designed to be framework-agnostic (already includes bindings for React & Angular)
INSTALLATION
npm install heerlik
INITIALIZATION
export const store = defineStore({
todos: [] as { id: number, text: string, done?: boolean }[],
user: { profile: { firstName: '', lastName: '' } }
});
integrateStoreWithReduxDevtools(store, { name: 'my app' });
WRITING STATE
store.update(s => s.user.profile).replace({ firstName: 'Terence', lastName: 'Mckenna' });
// dispatches { type: user.profile.replace(), payload: { firstName: 'Terence', lastName: 'Mckenna' } }
store.update(s => s.user.profile).patch({ firstName: 'Terence' });
// dispatches { type: user.profile.patch(), payload: { firstName: 'Terence' } }
store.update(s => s.todos).removeFirst();
// dispatches { type: todos.removeFirst() }
store.update(s => s.todos).removeLast();
// dispatches { type: todos.removeLast() }
store.update(s => s.todos).prepend({ id: 1, text: 'Bake cookies' }, { id: 2, text: 'Eat cookies' });
// dispatches { type: todos.prepend(), payload: { id: 1, text: 'Bake cookies' }, { id: 2, text: 'Eat cookies' } }
store.update(s => s.todos).append({ id: 4, text: 'Read book' }, { id: 5, text: 'Take a shower' }, { id: 6, text: 'Go to bed' });
// dispatches { type: todos.append(), payload: { id: 4, text: 'Read book' }, { id: 5, text: 'Take a shower' }, { id: 6, text: 'Go to bed' } }
store.update(s => s.todos).remove(t => t.done === true);
// dispatches { type: todos.remove() }
store.update(s => s.todos).removeAll();
// dispatches { type: todos.removeAll() }
store.update(s => s.todos).patch(t => t.done ? t : ({ done: true }));
// dispatches { type: todos.patch() }
store.update(s => s.todos[s.todos.findIndex(t => t.id === 1)]).replace({ id: 1, text: 'Buy cookies' });
// dispatches { type: todos.0.replace(), payload: { id: 1, text: 'Buy cookies' } }
store.updateWithName('removeAllCompletedTodos', s => s.todos).remove(t => t.done === true);
// dispatches { type: 'removeAllCompletedTodos' }
store.updateManyAtOnce('doManyThings', [
() => store.select(s => s.user.profile).assign({ firstName: 'Terence' }),
() => store.select(s => s.todos).push({ id: 1, text: 'Bake cookies' }, { id: 2, text: 'Eat cookies' }),
]);
READING STATE
// Get state
const myState = store.stateGet();
REACT
// WITH HOOKS
const todos = useSelector(store, s => s.todos);
// WITHOUT HOOKS
class MyComponent extends React.Component<{ userName: string, propFromParent: number }> {
}
export default mapStateToProps(store, (state, ownProps: { propFromParentComponent: string }) => ({
userName: state.user.profile.firstName,
propFromParent: ownProps.propFromParentComponent,
}))(MyComponent);
ANGULAR
export class AppModule {
constructor(appRef: ApplicationRef) {
listenToDevtoolsDispatch(() => appRef.tick());
}
}
export class MyComponent {
todos$ = select(store, s => s.todos);
}
SUBSCRIPTIONS
// Listen to any state change
const stateChanged = store.subscribeToRoot(s => console.log(s));
stateChanged.unsubscribe();
// Listen to specific state change
const profileChanged = store.subscribe(s => s.user.profile, profile => console.log(profile));
profileChanged.unsubscribe();