Nephrite Power Mace

    @lacolaco/reactive-store
    TypeScript icon, indicating that this package has built-in type declarations

    5.0.0 • Public • Published

    @lacolaco/reactive-store

    Very simple store implementation for state management with RxJS.

    https://yarn.pm/@lacolaco/reactive-store

    @lacolaco/reactive-store Dev Token

    CI

    npm version

    Install

    $ yarn add rxjs @lacolaco/reactive-store
    

    Concept

    • RxJS: Use the ecosystem
    • TypeScript: Type-safe state management
    • Simple: Easy to understand what the library does and doesn't

    How to Use

    Create a store: new Store({ initialValue })

    import { Store } from '@lacolaco/reactive-store';
    
    export interface CounterState {
      count: number;
    }
    
    export const initialValue: CounterState = {
      count: 0,
    };
    
    export const counterStore = new Store<CounterState>({ initialValue });

    Use the store

    Get the current value: .value: T

    export const counterStore = new Store<CounterState>({ initialValue: 1 });
    
    console.log(counterStore.value); // => 1

    Observe value changes: .valueChanges: Observable<T>

    .valueChange returns a raw observable of the store.

    export const counterStore = new Store<CounterState>({ initialValue: 1 });
    
    counterStore.valueChanges.subscribe(value => {
      console.log(value); // => 1
    });
    
    // You can use `pipe` and operators of RxJS.
    const doubled$: Observable<number> = counterStore.valueChanges.pipe(map(value => value * 2));

    Update the store: .update((value: T) => T): void

    update takes a function which takes the current value and returns a new value.

    export const counterStore = new Store<CounterState>({ initialValue: 1 });
    
    counterStore.update(value => value + 1);
    
    console.log(counterStore.value); // => 2

    Observe scoped value: .select((value: T) => U): Observable<U>

    select method is for mapping and memoize the scoped value. This is using internally it uses RxJS's map and distinctUntilChanged operators.

    export const counterStore = new Store<CounterState>({
      initialValue: { count: 1 },
    });
    
    counterStore.valueChanges.subscribe(value => {
      console.log(value); // => { count: 1 }
    });
    
    const selected$: Observable<number> = counterStore.select(value => value.count);
    
    selected$.subscribe(value => {
      console.log(value); // => 1
    });

    Listen update events: .storeUpdateChanges: Observable<StoreUpdateChange<T>>

    A store dispatchs a change event every time updating the store. This is for debugging or integrating with other tools.

    const counterStore = new Store<CounterState>({
      initialValue,
    });
    
    counterStore.storeUpdateChanges.subscribe(change => {
      console.log(`Previous Value`, change.previousValue);
      console.log(`Current Value`, change.currentValue);
      console.log(`Label`, change.label);
    });

    label is a string value you can pass to update as an option.

    export const counterStore = new Store<CounterState>({ initialValue: 1 });
    
    counterStore.update(value => value + 1, { label: 'increment' });

    Reset the store: .reset(): void

    reset the store with the initial value.

    export const counterStore = new Store<CounterState>({ initialValue: 1 });
    
    counterStore.reset();

    Integration Example

    Use with immer

    immer is a library to work with immutable state in a more convenient way. You can use immer intuitively with Store#update.

    import { Store } from '@lacolaco/reactive-store';
    import produce from 'immer';
    
    const store = new Store({
      initialValue: { count: 0 },
    });
    
    store.update(
      produce(draft => {
        draft.count = 5; // mutate draft directly
      }),
    );
    
    console.log(store.value); // => 5

    Use in Angular

    In Angular application, I recommend to creating new store with extending Store and provide it for Dependency Injection.

    // state/counter.store.ts
    import { Injectable } from '@angular/core';
    import { Store } from '@lacolaco/reactive-store';
    
    export interface CounterState {
      count: number;
    }
    
    export const initialValue: CounterState = {
      count: 0,
    };
    
    // Or you can use your NgModule's `providers` array to provide this service.
    @Injectable({ providedIn: 'root' })
    export class CounterStore extends Store<CounterState> {
      constructor() {
        super({ initialValue });
      }
    }
    // app.component.ts
    @Component({
      selector: 'app-root',
      template: `
        <p>Counter: {{ count$ | async }}</p>
      `,
    })
    export class AppComponent implements OnInit {
      count$: Observable<number>;
      constructor(private counterStore: CounterStore) {
        this.count$ = this.counterStore.select(value => value.count);
      }
    
      incrementCount() {
        this.counterStore.update(
          value => ({
            ...value,
            count: value.count + 1,
          }),
          { label: 'increment' },
        );
      }
    }

    License

    MIT

    Author

    Suguru Inatomi a.k.a. @lacolaco

    Keywords

    none

    Install

    npm i @lacolaco/reactive-store

    DownloadsWeekly Downloads

    10

    Version

    5.0.0

    License

    MIT

    Unpacked Size

    21.6 kB

    Total Files

    29

    Last publish

    Collaborators

    • lacolaco