Inspired by React's Zustand.
npm i ngx-stashrDefine your state and actions in a state file (e.g., counter.state.ts).
import { createStash } from 'ngx-stashr';
interface CounterState {
count: number;
increment: () => void;
decrement: () => void;
reset: () => void;
}
export const counterStash = createStash<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));Use the stash directly in your components. It's just a signal really.
import { Component } from '@angular/core';
import { counterStash } from './counter.state';
@Component({
selector: 'app-counter',
standalone: true,
template: `
<h1>Count: {{ stash().count }}</h1>
<button (click)="stash().increment()">+</button>
<button (click)="stash().decrement()">-</button>
<button (click)="stash().reset()">Reset</button>
`
})
export class CounterComponent {
readonly stash = counterStash;
}You can create computed signals for specific slices of state. This can further optimize performance if needed.
@Component({ ... })
export class CounterDisplayComponent {
readonly stash = counterStash;
// only updates when count changes
readonly count = this.stash.select(state => state.count);
// derived state
readonly doubleCount = this.stash.select(state => state.count * 2);
}You can persist state to localStorage (or any other storage) using the persist middleware.
import { createStash, persist } from 'ngx-stashr';
export const settingsStash = createStash(
persist(
(set) => ({
theme: 'light',
toggleTheme: () => set((state) => ({
theme: state.theme === 'light' ? 'dark' : 'light'
})),
}),
{
name: 'app-settings', // unique name
// storage: sessionStorage // optional, just defaults as localStorage
}
)
);You can debug the state mutations with the logger middleware. It will report the previous state, action, and next state to the console.
import { createStash, logger } from 'ngx-stashr';
export const stash = createStash(
logger(
(set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 }), false, 'increment')
}),
{
name: 'CounterStash',
enabled: true // defaults to true
}
)
);You can compose middleware by swallowing. Just make sure persist sits as the outer wrapper if chaining it.
export const stash = createStash(
persist(
logger(
(set) => ({ count: 0 }),
{ name: 'MyStash' }
),
{ name: 'storage-key' }
)
);Creates a stash. Returns a Signal that also contains API methods.
stash(): Get the current state (signal).stash.get(): Get the current state (non-reactive readonly snapshot).stash.set(partial, replace?, ...args): Update state.partialcan be an object or a function(state) => partial. Optionalargsare passed to listeners (just useful for logging actions).stash.select(selector): Create a computed signal from the state.stash.subscribe(listener): Subscribe to state changes manually.