heerlik-react

*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 ac

Usage no npm install needed!

<script type="module">
  import heerlikReact from 'https://cdn.skypack.dev/heerlik-react';
</script>

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();