A proxy based signals library that allows for small but powerful state management. It is build on top of three core principles:
- Reactive: like any signals library, effects can be created that are executed once state is changed.
- Immutable: data can be made immutable and cannot be mutated directly. Changes can be made via commands.
- Access-layer: commands can be registered to the store. These are functions that can be invoked to make changes.
Simple definition of a store
import { signal, effect } from "chifferobe";
// define a command
const increment =
(state) =>
(amount = 1) =>
(state.count += amount);
// define the signal
const myStore = signal({ count: 0 }, { increment });
// interact with the signal
console.log(myStore.count); // 0
myStore.increment(2);
console.log(myStore.count); // 2
Basic effects
// Define an effect
let double = 0;
const dispose = effect(() => (double = myStore.count * 2));
console.log(double); // 4
myStore.increment(2);
console.log(double); // 8
// Remove the effect
dispose();
Effects based in multiple stores
const myStore1 = signal({ count: 0 }, { increment });
const myStore2 = signal({ count: 4 }, { increment });
// Define an effect
let sum = 0;
const dispose = effect(() => (sum = myStore1.count + myStore2.count));
console.log(sum); // 4
myStore.increment(2);
console.log(double); // 6
// Remove the effect
dispose();
type CountStore = { count: number };
type CountCommands = {
increment: (number?: number) => CountStore;
};
const initStore = { count: 0 };
const commands = {
increment: (state) => (n) => {
return { ...s, count: s.count + n };
},
};
const store = signal<CountStore, CountCommands>(initStore, commands);
A generic React Hook implementation that automatically rerenders if the store value changes.
import { effect } from "chifferobe";
import { useLayoutEffect, useReducer } from "react";
export function useStore(store, key) {
const [state, setState] = useState({});
useLayoutEffect(() => {
const dispose = effect(() => setState(store[key]));
return () => dispose();
}, []); //eslint-disable-line
return state;
}
// Apply in a component
function MyButton() {
// here a view on the data is being used in the hook
const count = useReadStore(myStore, "count");
return <button onClick={store.increment}>{`value ${count}`}</button>;
}