Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fkrasnowski/stateverse
Async state manager - painless manager for async state changes and side effects
https://github.com/fkrasnowski/stateverse
async side-effects state state-management typescript
Last synced: 25 days ago
JSON representation
Async state manager - painless manager for async state changes and side effects
- Host: GitHub
- URL: https://github.com/fkrasnowski/stateverse
- Owner: fkrasnowski
- License: mit
- Created: 2020-06-24T21:13:03.000Z (over 4 years ago)
- Default Branch: master
- Last Pushed: 2023-01-06T10:17:48.000Z (almost 2 years ago)
- Last Synced: 2024-11-09T11:51:33.402Z (about 2 months ago)
- Topics: async, side-effects, state, state-management, typescript
- Language: TypeScript
- Homepage:
- Size: 858 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# stateverse ๐ฝ
Async state manager - painless manager for async state changes and side effects
## Installation
```sh
# npm
npm i --save stateverse# yarn
yarn add stateverse
```## Features
- ๐ฃ Minimalistic API
- ๐จโ๐ฉโ๐งโ๐ฆ Framework agnostic
- ๐ฆพ TypeScript support## Example
```js
const counter = createStore(0)
.addReducers({
add: (state, v) => state + v,
sub: (state, v) => state - v,
reset: () => 0,
})
.addEffects({
countDownFx: async (reducers, cleanup) => {
const interval = setInterval(() => {
if (counter.state > 0) reducers.inc();
else clearInterval(interval);
}, 1000);
cleanup(() => {
clearInterval(interval);
});
},
});counter.watch((count) => console.log(`counter: ${count}`));
counter.actions.add.watch((v) => console.log(`add: ${v}`));
counter.actions.sub.watch((v) => console.log(`substract: ${v}`));
counter.actions.add(10);
//add: 10
//counter: 10
counter.actions.sub(5);
// substract: 5
// counter: 5
counter.actions.countDownFx();
// substract: 1
// counter: 4
// substract: 1
// counter: 3
//...
```## Core concepts
### Store
It's an object that holds state. You can define many stores. To define one use `createStore` factory:
```js
import { createStore } from 'stateverse';const store = createStore({ state });
```### Action
**Action** is either `reducer` or `effect`. Actions represent an intention to change `state`. `actions` can be attached to the store. Call action from actions property of store:
```js
store.actions.addData({ login: 'fkrasnowski', password: 'โ(๏พใฎ๏พโ)' });
```### Reducer
**Reducer** is a function that takes state (and additional values) and returns a new state that replaces the old one. Reducers must be `pure`. To define reducers use `.addReducers` method:
```js
store.addReducers({
addData: (state, data) => ({ ...state, data }),
});
```### Effect
**Effect** is a container for asynchronous state changes. `Effect` has access to all `reducers` of the `store`. Store runs one effect at the same time. If another `action` is triggered before effect finish, the effect is being canceled and the cleanup function is executed. To define effect use `.addEffects` method:
```js
store.addEffects({
loadDataFx: async (reducers, cleanup, id) => {
reducers.setLoading(true);
const data = await fetchData(id);
reducers.addData(data);
reducers.setLoading(false);
},
});
```### Cleanup
Some `effects` of course demand `cleanup`. To add so provide your `cleanup` function as an argument for `cleanup`. The `cleanup` function is attached asynchronously so implement it as early as possible:
```js
const countDownFx = async (reducers, cleanup) => {
const interval = setInterval(() => {
if (counter.state > 0) reducers.inc();
else clearInterval(interval);
}, 1000);
cleanup(() => {
clearInterval(interval);
});
},```
### Watchable
**Watchable** is a function that can be watched. It means you can attach a `callback` to be triggered while function is called. `Actions` are watchable by default. You can create a `watchable` from function like so:
```js
import { watchable } from 'stateverse';const watchableFn = watchable((v) => v + ๐);
watchableFn.watch((...args) => console.log('watchout!',...args)));
watchableFn('๐ฅ') //Logs: wachout! ๐ฅ
```### Inner state โ
It's really **important** to understand this idea. **Stateverse** assumes that `effects` break the integrity of the state in general. That's why effects are _`auto-canceled`_ while new `action` is being invoked. The new `action` does not operate on `state` from within canceled effect. It takes the `state` from **before** the effect has affected it! This is the so-called **inner state** - an unexposed state accessible **only** by reducers that are protected from inconstant changes
## API
#### `createStore(initialState: T): stateverse.Store`
Returns store that holds state of type `T`
#### `.state`
Returns current state
#### `.watch(callback: (state: T) => any)`
Attaches a `callback` function to observe state changes
#### `.unwatch(callback: (state: T) => any)`
Detaches a callback function from observation
```js
const callbackFn = fn.spy();
store.watch(callbackFn); //Attaches: callbackFn
//...
store.unwatch(callbackFn); //Detaches: callbackFn
```#### `.actions..watch(callback: (...values: any) => any)`
Triggers `callback` on action call with `values` (arguments) provided to the action
#### `.map(mapFn: (state: T) => S): stateverse.WatchStore`
A map function to map state and watch only for particular state changes
```js
arrayStore.map((state) => state[0]).watch(console.log);
```#### `Reducer = (state: T, ...values: any) => T`
A pure function that takes state as first argument and additional values provided on action call
#### `Effect = (reducers: Actions, cleanup: (cleanupFn: Cleanup) => void, ...values: any) => Promise`
A function returning `Promise` (`async function`)
#### `.addReducers(reducers: Reducers)`
Adds `reducers` to store
#### `.addEffects(effects: Effects)`
Adds `effects` to store
#### `watchable(fn: Function): watchableFn`
Creates function with additional methods `.watch` and `.unwatch`
## Complementary packages
- **React:** [stateverse-react](https://github.com/fkrasnowski/stateverse-react)