Wondering what’s next for npm?Check out our public roadmap! »

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

    1.1.3 • Public • Published

    Build Status Codecov npm (scoped) GitHub license

    Veams RxStore (@veams/rx-store)

    Veams provides the missing RxJS wrapper for Redux to provide a simple but powerful state management module.

    It is framework agnostic and can be used in any web application frameworks/libraries like Angular, React, Vue.

    It is written in TypeScript.

    It is bundled and gzipped around 4KB. Take a look at our uncompressed stats file to find out more about our bundle.

    Why this package?

    Why not using Redux standalone?

    Redux has some great benefits and advantages: small, simple and it has a huge community and eco system. That's why we are using it!

    But: It follows the pattern of Observables, without having the power of RxJS Observables.

    Try to use it out of the React ecosystem. You will discover that you have to add a lot manual work to handle filtering of values and changes. You need to add your own comparison logic.

    @veams/rx-store to rescue!

    @veams/rx-store starts there, where Redux ends. It provides a simple interface to get slices/chunks/parts out your store.

    Next to that, you only subscribe to changes for this specific portion of the store, means @veams/rx-store is filtering the changes for you and only executes your provided subscription callback when the new state is different from the previous one.

    Let's see the benefits in code form. Say we have a store which has the following structure:

    {
        ui: {
            breakpoint: "small"
        },
        person: {
            status: "fetched",
            entities: {
                1: {
                    name: "John",
                    gender: "male"
                },
                2: {
                    name: "Max",
                    gender: "male"
                },
                3: {
                    name: "Adriana",
                    gender: "female"
                }
            }
        }
    }

    When we only want to listen to changes to our UI slice, we can do the following:

    const ui$ = store
                    .select(state => state.ui)
                    .subscribe((uiState) => {
                        console.log(uiState); // Prints {breakpoint: "small"} and upcoming changes to the object
                    });
    
    // Later on we want to unsubscribe
    ui$.unsubscribe();

    You can go even further down in your data chain:

    const personX$ = store
                        .select(state => state.person.entities.1)
                        .subscribe((dataFromPersonWithId1) => {
                            console.log(dataFromPersonWithId1); // Prints {name: "John", gender: "male"} and upcoming changes to the object
                        });
    
    // Later on we want to unsubscribe
    personX$.unsubscribe();

    Or what if you want to add more custom handling like returning only females:

    import { map } from 'rxjs/operators';
    
    const getFemales = (data) => Object.values(data).filter(person => person.gender === "female")
    
    // By using operators
    const femalesByOperator$ = store
                        .select(state => state.person.entities)
                        .pipe(
                            map(persons => getFemales(persons))
                        )
                        .subscribe((females) => {
                            console.log(females); // Prints [{name: "Adriana", gender: "female"}] and upcoming changes to the person slice
                        });
    
    // By using selector function
    const femalesBySelector$ = store
                        .select(state => getFemales(state.person.entities))
                        .subscribe((females) => {
                            console.log(females); // Prints [{name: "Adriana", gender: "female"}] and upcoming changes to the person slice
                        });
    
    // Later on we want to unsubscribe
    femalesByOperator$.unsubscribe();
    femalesBySelector$.unsubscribe();

    It is as easy as this!

    You can also take a look at the demo implementation! In the provided example we have simple counter and store in place. Via setTimeout() we add data to the store - nothing more and really basic.


    Installation

    NPM

    npm install @veams/rx-store --save

    Yarn

    yarn add @veams/rx-store

    Usage

    A simple store gist can look like this:

    import { createObservableFromRedux } from '@veams/rx-store';
    import { combineReducers, createStore } from 'redux';
    
    /** 
     * Redux stuff starts
     */
    // Typical reducer
    function uiReducer(state, action) {
        switch(action.type) {
            case 'ui:currentMedia': {
                return {...state, ui: {
                    ...state.ui,
                    currentMedia: action.payload
                }}
            }
            default: 
                return state;
        }
    }
    
    // And another one
    function anotherReducer(state, action) {
        switch(action.type) {
            case 'test:update': {
                return {...state, test: {
                    ...state.test,
                    activeIdx: action.payload
                }}
            }
            default: 
                return state;
        }
    }
    
    // Let's create 
    const rootReducer = combineReducers({
    	ui: uiReducer,
    	another: anotherReducer
    });
    
    const reduxStore = createStore(rootReducer, /* Place your middlewares here */)
    
    /** 
     * Redux stuff ends
     */
    
    /** 
     * RxStore
     */
    const store = createObservableFromRedux({
        useSingleton: false,
        store: reduxStore
    });
    
    export default store;

    By calling createObservableFromRedux() with the option useSingleton: true we create a singleton which you can use in your app by using the library import like this:

    import { store } from '@veams/rx-store';

    Select & Subscription

    Because @veams/rx-store is giving you back a RxStore interface, you are now able to select a slice out of it and subscribe to changes:

    import { store } from '@veams/rx-store';
    
    const testState$ = store.select((state) => state.test);
    const activeIdx = 0;
    
    testState$.subscribe(data => {
        activeIdx = data.activeIdx;
        
        console.log(activeIdx); // Print out new value 
    })

    Operators

    Because we have RxJS in place you can do all common operations like you wish:

    // Apply operators ... 
    testState$.pipe(
        filter(data => data.activeIdx < 4),
        delay(500),
    ).subscribe(data => { // ... and subscribe to new data values
        console.log(data.activeIdx);
    })

    Actions

    To update the store you need to dispatch actions:

    import { store } from '@veams/rx-store';
    
    store.dispatch({type: 'test:update', payload: 1})

    Bringing it all together it can look like this:

    import { store } from '@veams/rx-store';
    import { filter, delay } from 'rxjs/operators';
    
    // Select a state slice from store
    const testState$ = store.select((state) => state.test);
    const activeIdx = 0;
    
    // Just select an element.
    const app = document.getElementById('app');
    const btn = document.getElementById('btn');
    
    // Apply operators ... 
    testState$.pipe(
        filter(data => data.activeIdx < 4),
        delay(500),
    ).subscribe(data => { // ... and subscribe to new data values
        activeIdx = data.activeIdx
        
        // Call a function to render the app.
        renderApp(activeIdx);
    })
    
    // Simple rendering of html
    function renderApp(activeIdx) {
        app.innerHTML = activeIdx;
    }
    
    // Click handler to update the store by using actions.
    btn.addEventListener('click', () => store.dispatch({type: 'test:update', payload: activeIdx + 1}))

    Some technical stuff for you

    Interface

    export interface RxStore {
      redux: Store; 
      observable: Observable<unknown>;
      select: (selector: (data: any) => any) => Observable<any>;
      dispatch: (action: AnyAction) => void;
    }

    API

    createObservableFromRedux()

    Create store singleton by passing Redux.

    • @param {Object} options - Options object.
    • @return {RxStore} store

    The options object has some defaults, these are:

    DEFAULT_OPTIONS = {
    	useSingleton: true,
        store: createStore((state) => state)
    };
    

    store

    The returned store has the following API:

    redux

    Redux instance you passed.

    It should not be necessary to work with the instance.

    observable

    Observable instance which was created by createObservableFromRedux.

    It should not be necessary to work with the instance.

    select(cb)

    Select function can be used to get the whole state or a specific slice from state.

    • @param {Function} selector - Selector function which gets passed the store.
    • @return {Any} state slice

    dispatch(actionObject)

    Dispatch function to update store.

    • @param {Object} action - Action object containing type and payload.

    Install

    npm i @veams/rx-store

    DownloadsWeekly Downloads

    1

    Version

    1.1.3

    License

    none

    Unpacked Size

    596 kB

    Total Files

    37

    Last publish

    Collaborators

    • avatar
    • avatar