react-context-stores

light-weight context stores that can be used by react apps for global state management with redux-like features but implemented using purely react components.

Usage no npm install needed!

<script type="module">
  import reactContextStores from 'https://cdn.skypack.dev/react-context-stores';
</script>

README

react-context-stores · License: MIT npm version JavaScript Style Guide

React Context Stores provides light-weight context stores that can be used by react apps for global state management with redux-like features but using purely react components.

Motivation

This React Context Stores was created to provide light-weight alternatives for setting up react Context API or using React-Redux library for medium to light weight apps (or create-react-app) need for global state management also with redux-like features and does not use any external libraries. The React app demonstrates a working example on how to use the react-context-stores. The react store uses the useContext and useReducer react hooks to implement an app state management system. Therefore your react app version should support this hooks as well.

Install

npm install --save react-context-stores

Table of content

Usage

react-context-stores provides 3 types of store classes.

  1. ContextStore
  2. ContextReduxStore
  3. ContextCombineStore

Context Store

Import the ContextStore class and initialize the store outside the react component. Wrap the components you wish to provide store to via .Provider component of the store instance then pass the store data to the .Provider via the value prop. Finally, export the ContextStore instance to be accessible from other component.

// App.jsx
import React from 'react';
import { ContextStore } from 'react-context-stores';
import Counter from './components/Counter/Counter';

// Initialize the store with a name and export the store instance
export const contextStore = new ContextStore('Context Store');

// This could be any component
const App = () => {
  const [counter, setCounter] = useState(0);
    // Wrap the children with the provider property of the store
    // Pass the value to be provided globally to the children components to the *value* prop
  return (
    <contextStore.Provider value={{ counter, setCounter }}>
      <h3>Context Store Example</h3>
      <Counter />
    </contextStore.Provider>
  );
}

export default App;

Access the store data in the Counter component. .useStore method of the store instance returns an object of storeStateObject

// ./components/Counter/Counter.jsx
import React from 'react'
// import the store instance
import { contextStore } from '../../App'

const Counter = () => {
  // Destruct the store data
  const { counter, setCounter } = contextStore.useStore()
  // Use the store data
  return (
    <div>
      <div>{counter}</div>
      <div>
        <button onClick={() => setCounter(counter1 - 1)}>-</button>
        <button onClick={() => setCounter(0)}>reset</button>
        <button onClick={() => setCounter(counter1 + 1)}>+</button>
      </div>
    </div>
  )
}

export default Counter

ContextStore can be instantiated as many as needed and in any part of the application.

Context Redux Store

Context redux store provides a redux like interface.

Firstly, we need to setup the store.

// ./store/counterSlice/index.js

// Store data instance
export const initialState = {
  counter: 0
}

// Reducer to update the store
export const reducer = (state, action) => {
  switch (action.type) {
    case 'CHANGE':
      return { ...state, counter: state.counter + action.payload }
    case 'RESET':
      return { ...state, counter: 0 }
    default:
      return state
  }
}

// Actions object
export const actions = {
  change: (payload) => ({ type: 'CHANGE', payload }),
  reset: { type: 'RESET' }
}

Import the ContextReduxStore class and initialize the store outside the react component. Wrap the components you wish to provide store to via .Provider component of the store instance then pass the store data to the .Provider via the value prop. The value must be an array of [stateObject, reducerFuction]. Finally, export the ContextReduxStore instance to be accessible from other component.

// App.jsx
import React from 'react';
import { ContextReduxStore } from 'react-context-stores';
import { initialState, reducer } from './store/counterSlice'
import Counter from './components/Counter/Counter';

// Initialize the store with a name and export the store instance
export const contextReduxStore = new ContextReduxStore('Context Redux Store');

// This could be any component
const App = () => {
  // Wrap the children with the provider property of the store
  // Pass the value to be provided globally to the children components to the *value* prop
  return (
    <contextReduxStore.Provider value={[initialState, reducer]}>
      <h3>Context Redux Store Example</h3>
      <Counter />
    </contextReduxStore.Provider>
  );
}

export default App;

Access the store data in the Counter component. .useStore method of the store instance returns an array of [stateObject, dispatchFunction].

// ./components/Counter/Counter.jsx
import React from 'react';
import { actions } from '../../store/counterSlice';
// import the store instance
import { contextReduxStore } from '../../App';

const Counter = () => {
  // Destruct the store data
  const [ state, dispatch ] = contextReduxStore.useStore();
  // Use the store data
  return (
    <div>
      <div>{ state.counter }</div>
      <div>
        <button onClick={() => dispatch(actions.change(-1))}>-</button>
        <button onClick={() => dispatch(actions.reset}>reset</button>
        <button onClick={() => dispatch(actions.change(+1)}>+</button>
      </div>
    </div>
  );
}

export default Counter;

ContextReduxStore can be instantiated as many as needed and in any part of the application.

Context Combine Store

Context combine store provides a redux like interface with the combine store feature.

Having setup the store(s), we need to combine the store.

// ./store/index.js
import { counterInitialState, counterReducer } from './counterSlice'
import { otherInitialState, otherReducer } from './otherSlice'

/**
 * Object that combines different store slices
 */
const combineStoreSetup = {
  // The format for combining store is as follows
  // sliceName: [sliceInitialState, sliceReducer ]
  counterStore: [counterInitialState, counterReducer],
  otherStore: [otherInitialState, otherReducer]
}

export default combineStoreSetup

Import the ContextCombineStore class and initialize the store outside the react component. Then, Wrap the components you wish to provide store to via .Provider component of the store instance then pass the store data to the .Provider via the value prop. The value must be an object of {...sliceNames}, where each slice is an array of [sliceStateObject, sliceReducerFunction]. Finally, export the ContextCombineStore instance to be accessible from other component.

// App.js
import React from 'react';
import { ContextCombineStore } from 'react-context-stores';
import combineStoreSetup from './store'
import Counter from './components/Counter/Counter';

// Initialize the store with a name and export the store instance
export const contextCombineStore = new ContextCombineStore('Context Combine Store');
.
// This could be any component
const App = () => {
  // Wrap the children with the provider property of the store
  // Pass the value to be provided globally to the children components to the *value* prop
  return (
    <contextCombineStore.Provider value={combineStoreSetup}>
      <h3>Context Combine Store Example</h3>
      <Counter />
    </contextCombineStore.Provider>
  );
}

export default App;

Access the store data in the Counter component. .useStore method of the store instance returns an object of { ...sliceNames } where each slice is an array of [sliceStateObject, sliceDispatchFunction].

// ./components/Counter/Counter
import React from 'react';
import { actions } from '../../store/counterSlice';
// import the store instance
import { contextCombineStore } from '../../App';

const Counter = () => {
  // Destruct the stores data
  const { counterStore, /* otherStore */ } = contextCombineStore.useStore();
  // Access any of the stores
  const [ state, dispatch ] = counterStore;
  // Use the store data
  return (
    <div>
      <div>{ state.counter }</div>
      <div>
        <button onClick={() => dispatch(actions.change(-1))}>-</button>
        <button onClick={() => dispatch(actions.reset}>reset</button>
        <button onClick={() => dispatch(actions.change(+1)}>+</button>
      </div>
    </div>
  );
}

export default Counter;

ContextCombineStore can be instantiated as many as needed and in any part of the application.

License

MIT ©

Example

An example use of the store can be found in the example folder.