@babbling/signals
v0.0.1
Published
A reactive state management tool
Readme
Project Status
:scientist: Experimental
I'm using this in personal projects. There are rough edges and the API is constantly evolving. I might abandon it.
Philosophy
- State changes should only happen in response to IO events or errors.
- Changes should be batched and atomic.
- Gather, Plan, Execute: Effects run by gathering inputs, planning a change, and executing the plan.
Installing
npm install --save @babbling/signalsTo use the bleeding edge version from the main branch, use the @rc tag (release candidate):
npm install --save @babbling/signals@rcUsage
Everything is built around atoms. Atoms are reactive values that can be read and updated.
import { atom, swap, get } from '@babbling/signals';
const $count = atom(0);
console.log(get($count)); // 0
const increment = action((amount) => {
swap($count, get($count) + amount);
});Updates are executed in "actions", a batch of changes that apply all at once or not at all.
The Signal API is exported for advanced cases, such as custom bindings or watchers. See the React binding for an example.
External Sources
For data sources that change over time, you can bind with the external(...) API:
const $visibility = external(
() => document.visibilityState,
(onChange) => {
document.addEventListener('visibilitychange', onChange);
return () => document.removeEventListener('visibilitychange', onChange);
}
);Behaviors
Behaviors are the effect system. They are writable values inside actions and run when the action commits.
const useDarkTheme = action(() => {
swap(theme, 'dark');
});
const theme = behavior((value: string) => {
localStorage.setItem('theme', value);
});Bindings
A few bindings are exposed for popular packages. They're optional and assume you've already installed the package.
React
// React
import { atom } from '@babbling/signals';
import { useValue } from '@babbling/signals/react';
const $count = atom(0);
export function Counter() {
const count = useValue($count)
return <p>Count: {count}</p>
}Immer
import { atom, action } from '@babbling/signals';
import { update } from '@babbling/signals/immer';
const $state = atom({ count: 0 });
export const increment = action(() => {
update($state, (draft) => {
draft.count += 1;
});
});