npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@veams/rx-store

v1.1.3

Published

The missing RxJS wrapper for Redux to provide a simple but powerful state management module.

Downloads

20

Readme

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.