@pietro-marino/reactjs-store

A React.js store made with useContext and useReducer like redux

Usage no npm install needed!

<script type="module">
  import pietroMarinoReactjsStore from 'https://cdn.skypack.dev/@pietro-marino/reactjs-store';
</script>

README

reactjs-store

A modern, simple, useful and powerful React.js store made with useContext and useReducer like reduxjs with redux-actions plugin support.

It is made to create a ready-to-go store in a moment by writing less code by calling just a createStore function.

Like redux the initial state is created by the reducers registered by you, the library call them with the @@PM_REACT_STORE_INIT action.

Installation

Inside of your project install the library with the following command:

npm install --save @pietro-marino/reactjs-store

or

yarn add @pietro-marino/reactjs-store

How to use

Create the actions with the createAction function, like this:

import createAction from "@pietro-marino/reactjs-store/createAction";

export const INCREMENT = "INCREMENT";
export const DECREMENT = "DECREMENT";

export const increment = createAction(INCREMENT);
export const increment = createAction(DECREMENT);

Now, you have three method to create all reducer you need:

  1. write it, for example:
  const INITIAL_STATE = { value: 0 };

  export default function myReducer(state = INITIAL_STATE, action) {
    let newState = null;
    const payload = action.payload;
    switch(action.type) {
      case 'increment':
        newState = { value: state.value++ };
        break;
      case 'decrement':
        newState = { value: state.value-- };
        break;
      default:
        newState = state;
    }
    return newState;
  }
  1. if the reducer only handles an action you can create it by calling the built-in function called handleAction:
import handleAction from "@pietro-marino/reactjs-store/handleAction";

const INITIAL_STATE = { value: 0 };

const myReducer = handleAction('increment', (state, action) => {
  return { value: state.value++ };
}, INITIAL_STATE);

export default myReducer;
  1. to avoid using the switch you can use the handleActions function:
import handleActions from "@pietro-marino/reactjs-store/handleActions";

const INITIAL_STATE = { value: 0 };

function increment(state, action) {
  return { value: state.value++ };
}

function decrement(state, action) {
  return { value: state.value++ };
}

const myReducer = handleActions({
  'increment': increment,
  'decrement': decrement
}, INITIAL_STATE);

export default myReducer;

Finally, you can create the store and some utilities you need with createStore function. This function create for you:

  • the Provider component

  • the Consumer component

  • useStore hook to use state and the dispatcher within your functional component

    const [state, dispatch] = useStore();
    
  • useSelector hook to extract a portion of the state

    const [state, dispatch] = useSelector(state => state['key']);
    
  • useDispatch hook to extract only the dispatcher from useStore, if help you

    const dispatch = useDispatch();
    

So, with only one line of code you can create them all, like this:

  import createStore from "@pietro-marino/reactjs-store";

  export const {
    Provider,
    Consumer,
    useStore,
    useSelector,
    useDispatch
  } = createStore(myReducer);

Last but not least, for example we use the store created above, in your application:

App.js

import React from "react";
import { Provider } from "wherever you have created your store";

export default function App () {
  return (
    <Provider>
      <Counter />
    </Provider>
  );
}

Counter.js

import React from "react";
import { useStore } from "wherever you have created your store";
import { increment, decrement } from "wherever you have created your actions";

export default function Counter () {
  const [state, dispatch] = useStore();
  const incrementCount = dispatch(increment());
  const decrementCount = dispatch(decrement());

  return (
    <div className="counter">
      <div>
        Count: <span>${state.value}</span>
      </div>
      <div className="counter-actions">
        <button onClick={incrementCount}>+</button>
        <button onClick={decrementCount}>-</button>
      </div>
    </div>
  );
}

Advanced uses

You can combine actions to simplify the reducers:

import handleActions from "@pietro-marino/reactjs-store/handleActions";
import combineActions from "@pietro-marino/reactjs-store/combineActions";

const INITIAL_STATE = { value: 0 };

function increment(state, action) {
  return { value: state.value++ };
}

function decrement(state, action) {
  return { value: state.value++ };
}

function reset(state, action) {
  return { value: 0 };
}

const myReducer = handleActions({
  'increment': increment,
  'decrement': decrement,
  [combineActions("reset", "setZero")]: reset
}, INITIAL_STATE);

export default myReducer;

Combine reducers to a root reducer, like this:

import combineReducers from "@pietro-marino/reactjs-store/combineReducers";

const reducerOne = (state, action) => {};
const reducerTwo = (state, action) => {};
const reducerThree = (state, action) => {};

const rootReducer = combineReducers({
  'one': reducerOne,
  'two': reducertwo,
  'three': reducerThree
});

import createStore from "@pietro-marino/reactjs-store";

export const {
  Provider,
  Consumer,
  useStore,
  useSelector,
  useDispatch
} = createStore(myReducer);

// useful selectors
export const useOneSelector = () => useSelector(state => state['one']);
export const useTwoSelector = () => useSelector(state => state['two']);
export const useThreeSelector = () => useSelector(state => state['three']);

Examples

Inside the examples folder you can find a todo-app example that use this library.

License

@pietro-marino/reactjs-store is MIT licensed.