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 🙏

© 2026 – Pkg Stats / Ryan Hefner

xstore

v2.0.8

Published

Xstore is a state container for React

Readme

xstore npm

Xstore is a state container for React

Installation

NPM

npm install --save xstore

Usage

At first you should add so called handlers to store

import React from 'react'
import ReactDOM from 'react-dom'
import Store from 'xstore'
import App from './components/App'

import user from './store_handlers/user'
import dictionary from './store_handlers/dictionary'

// you can set default params here
// "flat" and "withPrefix" are only available options
Store.setDefaultParams({
  flat: true,
  withPrefix: true
});

// handler name should not contain "_" sign
Store.addHandlers({
  user,
  dictionary
});

ReactDOM.render(
  <App/>,
  document.getElementById('root')
);

An example of handler './store_handlers/user.js'. Add "init" reducer to set default state

import axios from 'axios'

const DEFAULT_STATE = {
  name: 'user',
  status: 'alive'
}

/**
 ===============
 Callback on change state
 ===============
*/
const onStateChange = (state) => {
  // ...some actions
}

/**
 ===============
 Reducers
 ===============
*/

// will be automatically called to initiate state
// the state will be an empty object if this reducer doesnt exist 
// dispatch it to reset state
// dispatch('USER_INIT')
const init = () => {
  return DEFAULT_STATE;
}

// will be automatically executed when Store.reset() called
// use it when you need some additional functionality to reset state
// second argument is state that saved in the local storage
// dispatch('USER_RESET')
const reset = (state, savedInLocalStorage) => {
  if (savedInLocalStorage) {
    return savedInLocalStorage;
  }
  delete state.name;
  delete state.status;
  return state;
}

// dispatch('USER_CHANGED')
const changed = (state, data) => {
  // you need to return only state params that should be changed
  // store will automatically create new merged state object
  return data;
}

// dispatch('USER_FETCHING')
const fetching = (state) => {
  return {fetching: true};
}

/**
 ===============
 Actions
 ===============
*/

// doAction('USER_CHANGE')
const change = ({dispatch, then, doAction, and}, data) => {
  // {setState, dispatch, dispatchAsync, doAction, then, and, getState, state, reset, getSavedState}
  // dispatch returns new state
  let newState = dispatch('USER_CHANGED', data);
  // or the same but shorter
  let newState = then('CHANGED', data);
  // "then" calls dispatch with own handler "user"

  doAction('USER_DO_SOME_ON_CHANGE', data);
  // or the same but shorter
  and('DO_SOME_ON_CHANGE', data);
  // "and" calls doAction with own handler "user"

  // be carefull, "reset" calls Store.reset() which resets all states
  
  // you can use "return" statemant here
  return "whatever";
}

// doAction('USER_LOAD')
const load = ({setState, then, dispatchAsync}, data) => {
  // {setState, dispatch, dispatchAsync, doAction, then, and, getState, state, reset, getSavedState}
  
  // dispatchAsync is the same dispatch but with tiny timeout
  // use it if an action is called from "componentDidMount" method
  // so parental connect component still not mounted and cant update your component
  // this won't return new state
  dispatchAsync('USER_FETCHING');  

  const promise = axios.get('/api/load.php', data)
    .then(({data}) => {
      then('CHANGED', data);
      // or you can use setState
      setState(data);
    });

    // or shorter way
    const promise = axios.get('/api/load.php', data).then(setState);

  return promise;
}

// doAction('USER_SOME_ACTION')
const some_action = ({getSavedState}, data) => {
  // {setState, dispatch, dispatchAsync, doAction, then, and, getState, state, reset, getSavedState}
  const savedState = getSavedState('user');

  if (savedState) {
    return 'state is saved in the local storage';
  }
}

 // saving state in the local storage, so it will be restored after page reloaded
 const localStore = {
    // unique key (the only required field)
    key: 'user_state',
    // list of state fields that need to save
    // they will be merged with initial state
    // whole state will be saved if names not defined
    names: [
      'currentStatus',
      'currentPage',
      'someParam'
    ],
    // or use function to define state that should be saved
    // "names" have bigger priority than "getData"
    getData: (state) => {
      delete state.some;
      state.any = true;
      return state;
    },
    // you can do initial merging with this function
    getInitialData: (defaultState, stateFromLocalStorage) => {
      return {
        ...defaultState,
        ...stateFromLocalStorage
      };
    },
    // return false if you dont want to get state from local storage
    shouldLoad: () => {
      return true;
    },
    // return false if state should not be saved
    shouldSave: (state) => {
      return state.someValue === true;
    },
    // return true if state should be removed from the local storage
    shouldRemove: (state) => {
      return !state.someItem;
    },
    // if state is changing too frequently use timeout
    timeout: 100,
    // lifetime of stored data
    // Nmin, Nhour, Nday, Nmonth
    lifetime: '2hour'
  },

export default {
  localStore,
  onStateChange,
  actions: {
    load,
    change,
    some_action
  },
  reducers: {
    init,
    reset,
    changed,
    fetching
  }
} 

This is example how to connect a component with the store

import React from 'react'
import Store from 'xstore'

class ComponentToConnect extends React.Component {
  render() {
    let {user, dictionary} = this.props;
    return (
      <div className="some-component">
        ....
      </div>
    )
  }

  // connected component has prop doAction
  handleSomeAction(value) {
    this.props.doAction('USER_DO_SOME_ACTION', value);
  }

  // connected component has prop dispatch
  handleSomeChanges(data) {
    this.props.dispatch('USER_SOMETHING_CHANGED', data);
  }
}

const params = {
  has: 'user, dictionary'
}
export default Store.connect(ComponentToConnect, params);
// or shorter way
export default Store.connect(ComponentToConnect, 'user, dictionary');

Possible connect params

{
  // store states which will be passed to component as props "user" and "dictionary"
  has: 'user, dictionary',
  // or
  has: ['user', 'dictionary'],

  // if you need just few certain fields from state
  has: 'user:name|status, dictionary:userStatuses',
  // or
  has: ['user:name|status', 'dictionary:userStatuses'],

  // you can also pass "*" that means it should have all stores
  has: '*',

  // component won't be rendered till it doesnt receive these props from the store
  shouldHave: 'user,dictionary',
  // or
  shouldHave: ['user', 'dictionary'],

  // all needed states will be merged and flattened
  // so component will have props "name", "status", "userStatuses" instead of "user" and "dictionary"
  flat: true,

  // needed to add a prefix for flattening
  // so component will have props "user_name", "user_status", "dictionary_userStatuses"
  withPrefix: true,

  // you can add handlers this way
  // if you pass all handlers needed for a component right here
  // then you dont need to pass "has" prop
  handlers: {
    user,
    dictionary
  },

  // makes connect component not pure
  // by default connect components are all pure
  pure: false
}

A list of xstore's available methods

import Store from 'xstore'

// returns whole cloned state
let state = Store.getState();

// returns cloned state with name "user"
let userState = Store.getState('user');

// returns field "name" from state with name "user"
let userName = Store.getState('user.name');

// set params by default
Store.setDefaultParams({
  flat: true,
  withPrefix: true
});

// call to add handlers
Store.addHandlers({
  user: userHandler,
  dictionary: dictionaryHandler
})

// call to do store's action
// USER_LOAD means that store will call action "load" of handler "user"
// the first argument can be "User_Load" or "user_load" or even "uSeR_lOaD"
Store.doAction('USER_LOAD', {id: userId});

// call to dispatch store's changes and redraw components
Store.dispatch('USER_LOADED', data);

// so if you have handler with name "catalog" you have to call actions which look like
Store.dispatch('CATALOG_FETCH_SUCCESS');
// so reducer should have name "fetch_success"

Store.doAction('CATALOG_ADD_ITEM', {item});
// so action should have name "add_item"

Store.reset();
// calls "reset" reducer (if such exists) of every single handler to reset all states
// if "reset" reducer doesnt exist then "init" reducer is to be called instead

Calling store's action from any place

import Store from 'xstore'

Store.doAction('USER_CHANGE', {name: 'NewName'});

Dispatching store's changes from any place

import Store from 'xstore'

Store.dispatch('USER_CHANGED', data);

Creating handlers (initial state, reducers and actions in one file)

npm install -g xstore
xstore create-handler filename

or add to your scripts in package.json this command

{
  scripts: {
    "create-handler": "node node_modules/xstore/bin/exec.js"
  }
}
npm run create-handler filename

It will create file filename.js in the process directory if it doesn't exist. The file will contain template code of a handler

License

MIT