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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@musicq/store

v2.1.0

Published

A state manager built on top of RxJS.

Downloads

13

Readme

Store

Store is a state manager library which is built on top of RxJS. It's inspired by redux-observable, but doesn't depend on redux.

Philosophy

        dispatch                   monitor
view ---------------> action <----------------> side effects (e.g. async tasks)
 ∧                      |
 |                      |
 |                      |
 | subscribe            |
 |                      |
 |                      |
 |                      ∨
store <-------------- reducer
           update

Installation

Using npm

npm install @musicq/store

or using yarn

yarn add @musciq/store

Usage

First of all, we need to create a global store instance using createStore function.

Create a store.js file and append the below code.

import { createStore } from '@musicq/store'

export const store = createStore()

It will create a store instance. The store instance contains 2 objects, one is state$, which is the source of our state, the other is dispatch function, it is used to dispatch an action to update our state.

Next we need to create a reducer to reduce our state.

Create a file todo.js

export function todos(state = [], action) {
  switch (action.type) {
    case 'CREATE TODO':
      return [...state, action.payload]
    case 'DELETE TODO':
      return state.filter((t) => t.id !== action.payload)
    default:
      return state
  }
}

Then, import todo reducer into store.js file

import { todos } from './todo'

Pass todo reducer as a parameter of createStore

export const store = createStore(todos)

Everything is ready, let's play with it!

Create a app.js file.

import { store } from 'store'

store.state$.subscribe((state) => console.log(state))

store.dispatch({
  type: 'CREATE TODO',
  payload: { id: 1, content: 'Learn how to use @musicq/store' },
})

setTimeout(() => {
  store.dispatch({ type: 'DELETE TODO', payload: 1 })
}, 3000)

That looks great! However, how could we dispatch an async action? That's super easy in store.

Let's say we need to tell the server the new todo data via HTTP request, then update the store once the server responses.

To achieve that goal, let's create an effects.js file to manage our effect functions.

import { createEffect } from '@musicq/store'
import { switchMap } from 'rxjs/operators'
import { from } from 'rxjs'

export const effect = createEffect((action$, state$, dispatch) =>
  action$.pipe(
    filter((action) => action.type === 'SEND NEW TODO TO SERVER'),
    switchMap((action) =>
      // mock communication with server
      from(
        new Promise((resolve) => {
          setTimeout(() => resolve(action.payload), 1000)
        })
      )
    ),
    // Dispatch an action
    // This equals to `dispatch({type: 'CREATE TODO', payload: res})`
    map((res) => ({ type: 'CREATE TODO', payload: res }))
  )
)

Now, let's import our effects into store.js file and register it to the store to make it work.

import { effect } from './effects.js'

store.registerEffects([effect /* you can have multiple effects here */])

Then let change the dispatch action name in our app.js file

store.dispatch({
  type: 'SEND NEW TODO TO SERVER',
  payload: { id: 1, content: 'Learn how to use @musicq/store' },
})

Now when we run the app.js, it won't update the store immediately, instead, there will be a 1s delay, and then update the store.

So the flow will be

dispatch create action --- 1s later update the store --------- 3s later delete the todo from the store ---->

Types

Action

interface Action {
  type: string
  payload?: any
}

Reducer

type Reducer<T> = (state: T, action: Action) => T

EffectFn

export type EffectFn<T> = (
  action$: Subject<Action>,
  state$: Observable<T> & { value: T },
  dispatch: DispatchFn
) => Observable<Action | any>

API

createStore

To create a store instance

type createStore = <T>(reducers?: Reducer<T>) => {
  state$: Observable<T> & { value: T }
  dispatch: (action: Action) => void
  registerEffects: (effects: EffectFn<T>[]) => void
}

createEffect

type createEffect = <T>(
  fn: EffectFn<T>
) => (
  action$: Subject<Action>,
  state$: Observable<T> & { value: T },
  dispatch: DispatchFn
) => Observable<any>

combineReducers

Combine multiple reducers

type combineReducers = (reducers: {
  [key: string]: Reducer<any>
}) => Reducer<any>

For example, if you have a todo reducer and a user reducer. You can combine them up and pass it to the createStore.

function todos(state = [], action) {
  switch (action.type) {
    case 'CREATE TODO':
      return [...state, action.payload]
    default:
      return state
  }
}

function users(state = [], action) {
  switch (action.type) {
    case 'CREATE USER':
      return [...state, action.payload]
    default:
      return state
  }
}

const reducers = combineReducers({
  todos,
  users,
})

const store = createStore(reducers)