https://github.com/reactgular/stateful
Stateful is a tiny state manager for TypeScript and RxJS projects.
https://github.com/reactgular/stateful
javascript library redux rxjs state-management tiny-library typescript
Last synced: 11 months ago
JSON representation
Stateful is a tiny state manager for TypeScript and RxJS projects.
- Host: GitHub
- URL: https://github.com/reactgular/stateful
- Owner: reactgular
- License: mit
- Created: 2019-09-12T00:24:37.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2020-07-18T06:29:18.000Z (over 5 years ago)
- Last Synced: 2024-04-25T11:43:02.091Z (almost 2 years ago)
- Topics: javascript, library, redux, rxjs, state-management, tiny-library, typescript
- Language: TypeScript
- Homepage:
- Size: 209 KB
- Stars: 1
- Watchers: 1
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
[](https://travis-ci.org/reactgular/stateful)
[](https://coveralls.io/github/reactgular/stateful?branch=master)
[](https://badge.fury.io/js/%40reactgular%2Fstateful)
## What is Stateful?
Stateful is a tiny state manager for TypeScript and [RxJS](https://github.com/ReactiveX/RxJS) projects. It's a class that encapsulates a [BehaviorSubject](https://www.learnrxjs.io/subjects/behaviorsubject.html) and
provides basic store manipulation methods. It's indented for use in small classes that don't need the complexity of a larger state manager.
Stateful is about **1kb** in size after *minification*.
## Installation
To get started, install the package from npm.
```bash
npm install --save @reactgular/stateful
```
## Usage
Stateful is small, simple and easy to use.
- You construct a Stateful object with a *default state* and use an interface to define the *state object* type.
- You can then `patch()`, `set()` and `reset()` the internal state.
- You use `select()` to create observables of state property changes, and `selector()` to create custom selectors.
```typescript
import {Stateful} from '@reactgular/stateful';
interface ExampleState {name: string; count: number; }
const state = new Stateful({name: "Example", count: 4});
state.patch({name: 'Something'});
state.select('name').subscribe(value => console.log(value)); // prints "Something"
```
# Stateful Class
Usage documentation for the `Stateful` class.
#### Properties
- `state$`: An observable that emits all changes to the state.
#### Methods
- `complete()`: Stops emitting changes made to the state.
- `default()`: Returns the default state used with the constructor or reset.
- `patch(state: Partial)`: Patches the current state with partial values.
- `patch(name: TKey, value: TState[TKey])`: Patches a single property on the state with a value.
- `reset(defaultState?: TState)`: Resets the state to the original state used by the constructor, or updates the original state with the passed argument.
- `select(name: TKey): Observable`: Creates an observable that emits values from a property on the state object.
- `selector(selector: (s: TState) => TValue): Observable`: Creates an observable that emits values produced by the selector function.
- `set(state: TState)`: Sets the current state.
- `snapshot(): TState`: Peeks at the current internal state.
## Examples
You can create *selectors* from the state by using property names. TypeScript will *infer* the correct `Observable` from the property key.
```typescript
import {Stateful} from '@reactgular/stateful';
interface ExampleState {name: string; count: number; }
const state = new Stateful({name: "Example", count: 4});
const name$ = state.select('name'); // Observable
name$.subscribe(value => console.log(value)); // prints "Example", "Hello World"
state.patch({name: "Hello World"});
```
You can write your own custom *selectors*.
```typescript
import {Stateful} from '@reactgular/stateful';
interface ExampleState {name: string; count: number; }
const state = new Stateful({name: "Example", count: 4});
const nameAndCount$ = state.selector(state => `${state.name} AND ${state.count}`);
nameAndCount$.subscribe(value => console.log(value)); // prints "Example AND 4"
```
## Example Angular Service
You can use Stateful as a base class for an Angular service.
```typescript
import {Observable} from 'rxjs';
import {Stateful} from '@reactgular/stateful';
interface ExampleState { counter: number }
@Injectable()
export class ExampleService extends Stateful {
public constructor() {
super({counter: 0})
}
public counter(): Observable {
return this.select('counter');
}
public increment() {
const counter = this.snapshot().counter + 1;
this.patch({counter});
}
public decrement() {
const counter = this.snapshot().counter - 1;
this.patch({counter});
}
}
```
## Example Angular Component
You can use Stateful as an internal state manager for an Angular component. By patching incoming `@Input()` properties into the Stateful
object you can more easily work with observables.
```typescript
interface ProductState {
productId?: number;
price?: number;
}
@Component({
selector: 'project',
template: `Price: {{price$ | async}}
Taxes: {{taxes$ | async}}`
})
export class ProductComponent implements OnInit {
private state: Stateful = new Stateful({});
public price$ = this.state.select('price');
public taxes$ = this.state.select('price').pipe(
map(price => price * 0.07)
);
@Input()
public set productId(productId: string) {
this.state.patch({productId});
}
@Input()
public set price(price: string) {
this.state.patch({price});
}
}
```
# StorageStateful Class
StorageStateful extends Stateful and offers persistence of state to a storage service like `localStorage` or `sessionStorage`.
```typescript
import {StorageStateful} from '@reactgular/stateful';
interface ExampleState {name: string; count: number; }
const state = new StorageStateful('app', {name: "Example", count: 4});
```
Pass the storage `key` as the first parameter to the constructor, and the default state as the second parameter. The state will
be persisted to `localStorage` by default under that `key`. Any changes patched to the state are serialized to storage.
You can configure custom serializers and storage objects using the `StorageStatefulConfig` interface as the third parameter.
```typescript
/**
* Configuration options for the StorageStateful class.
*/
export interface StorageStatefulConfig {
/**
* A deserialize function that converts a string into a state object.
*/
deserializer?: (state: string) => TState;
/**
* A serialize function that converts a state object into a string.
*/
serializer?: (state: TState) => string;
/**
* The storage object to use (can be localStorage or sessionStorage).
*/
storage?: Storage;
}
```