custom-react-store

Custom Redux Store is a light-weight store provider that can be used by react apps for global state managemnent with redux-like features but using purely react components.

Usage no npm install needed!

<script type="module">
  import customReactStore from 'https://cdn.skypack.dev/custom-react-store';
</script>

README

custom-react-store · License: MIT npm version JavaScript Style Guide

Custom React Store is a light-weight store provider that can be used by react apps for global state managemnent with redux-like features but using purely react components.

Motivation

This Custom React Store was created to be a light-weight alternative to using React-Redux library for medium to light weight app (or create-react-app) need for global state managemnent also with redux-like features and does not use any external libraries. The React app demonstrates a working example on how to use the custom react store. 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 custom-react-store

Table of content

Usage

custom-react-store provides 3 types of store classes.

  1. CustomStore
  2. CustomReduxStore
  3. CustomCombineStore

Custom store

Import the CustomStore 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 CustomStore instance to be accessible from other component.

// App.jsx
import React from 'react';
import { CustomStore } from 'custom-react-store';
import Counter from './components/Counter/Counter';

// Initialize the store with a name and export the store instance
export const customStore = new CustomStore('Custom 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 (
    <customStore.Provider value={{ counter, setCounter }}>
      <h3>Custom Store Example</h3>
      <Counter />
    </customStore.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 { customStore } from '../../App';

const Counter = () => {
  // Destruct the store data
  const { counter, setCounter } = customStore.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;

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

Custom redux store

Custom 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 CustomReduxStore 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 CustomReduxStore instance to be accessible from other component.

// App.jsx
import React from 'react';
import { CustomReduxStore } from 'custom-react-store';
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 customReduxStore = new CustomReduxStore('Custom 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 (
    <customReduxStore.Provider value={[initialState, reducer]}>
      <h3>Custom Redux Store Example</h3>
      <Counter />
    </customReduxStore.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 { customReduxStore } from '../../App';

const Counter = () => {
  // Destruct the store data 
  const [ state, dispatch ] = customReduxStore.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;

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

Custom combine store

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

Firstly, we need to setup the store(s).

// ./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' }
}

Next, we need to combine the store.

// ./store/index.js
import { initialState, reducer } from "./counterSlice";

/**
 * Object that combines different store slices
 */
const combineStoreSetup = {
    // The format for combining store is as follows
    // sliceName: [sliceInitialState, sliceReducer ]
    counterStore1: [initialState, reducer],
    counterStore2: [initialState, reducer],
};

export default combineStoreSetup;

Import the CustomCombineStore 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 CustomCombineStore instance to be accessible from other component.

// App.js
import React from 'react';
import { CustomCombineStore } from 'custom-react-store';
import { initialState, reducer } from './store'
import Counter from './components/Counter/Counter';

// Initialize the store with a name and export the store instance
export const customCombineStore = new CustomCombineStore('Custom 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 (
    <customCombineStore.Provider value={[initialState, reducer]}>
      <h3>Custom Combine Store Example</h3>
      <Counter />
    </customCombineStore.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 { customCombineStore } from '../../App';

const Counter = () => {
  // Destruct the stores data 
  const { counterStore1, /* counterStore2 */ } = customCombineStore.useStore();
  // Access any of the stores
  const [ state, dispatch ] = counterStore1;
  // 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;

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

License

MIT © [ucer without any external dependencies.](https://github.com/ucer without any external dependencies.)

Example

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