@ngstato/core
v0.4.2
Published
Stato — Zero-dependency state management engine
Maintainers
Readme
@ngstato/core
Tired of 14 lines of RxJS for a simple API call?
Write async/await. Get the same result. Ship ~3 KB instead of ~50 KB.
Documentation · API Reference · Helpers
Before / After
NgRx — rxMethod + pipe + tap + switchMap + from + tapResponse + patchState:
load: rxMethod<void>(pipe(
tap(() => patchState(store, { loading: true })),
switchMap(() => from(service.getAll()).pipe(
tapResponse({
next: (users) => patchState(store, { users, loading: false }),
error: (e) => patchState(store, { error: e.message })
})
))
))ngStato — async/await:
async load(state) {
state.loading = true
state.users = await http.get('/users')
state.loading = false
}Install
npm install @ngstato/core30-second example
import { createStore } from '@ngstato/core'
const store = createStore({
count: 0,
selectors: { doubled: (s) => s.count * 2 },
actions: {
inc(state) { state.count++ },
add(state, n: number) { state.count += n },
async load(state) { state.count = await fetchCount() }
}
})
await store.inc()
store.count // 1
store.doubled // 2 (memoized)Real-world store
import { createStore, http, retryable, optimistic } from '@ngstato/core'
const store = createStore({
users: [] as User[],
loading: false,
error: null as string | null,
selectors: {
total: (s) => s.users.length,
admins: (s) => s.users.filter(u => u.role === 'admin')
},
actions: {
loadUsers: retryable(async (state) => {
state.loading = true
state.users = await http.get('/users')
state.loading = false
}, { attempts: 3, backoff: 'exponential' }),
deleteUser: optimistic(
(state, id: string) => { state.users = state.users.filter(u => u.id !== id) },
async (_, id) => { await http.delete(`/users/${id}`) }
)
},
hooks: {
onInit: (store) => store.loadUsers(),
onError: (err, name) => console.error(`[${name}]`, err.message)
}
})Concurrency — without RxJS
import { exclusive, abortable, queued, retryable, optimistic } from '@ngstato/core'
actions: {
submit: exclusive(async (s) => { ... }), // → exhaustMap
search: abortable(async (s, q, { signal }) => { }), // → switchMap
send: queued(async (s, msg) => { ... }), // → concatMap
load: retryable(async (s) => { ... }, opts), // → retryWhen
delete: optimistic(apply, confirm), // → manual in NgRx
}Plus debounced · throttled · distinctUntilChanged · forkJoin · race · combineLatest · fromStream · pipeStream + 12 stream operators · createEntityAdapter · withEntities · withPersist · mergeFeatures · on() → Full API →
Inter-store reactions
import { on } from '@ngstato/core'
on([userStore.create, userStore.delete], (_, event) => {
console.log(`${event.name} ${event.status} in ${event.duration}ms`)
})Feature composition
const store = createStore({
items: [] as Item[],
...mergeFeatures(withLoading(), withPagination()),
actions: { async load(state) { ... } }
})
// store.loading, store.page, store.hasError — all availableThe numbers
| | NgRx v21 | ngStato | |:--|:--|:--| | Bundle | ~50 KB | ~3 KB | | CRUD store | ~90 lines | ~45 lines | | Concepts for async | 9 | 1 | | RxJS required | Yes | No |
📖 Documentation
becher.github.io/ngStato — Quick start · Core concepts · API · Helpers · NgRx migration
License
MIT
