redux-knife-manager

    3.2.0 • Public • Published

    Redux Knife Manager

    Build Status Coverage Status Maintainability node version npm version npm monthly download license

    Redux Knife Manager is the lightweight library for easily managing, encapsulating and generating the redux entities such as action, reducer, selector and so on.

    Redux Knife Manager has following features:

    • It is very suitable for redux, redux-saga and related stacks.
    • Use naming convention to generate the redux action, redux action type and selector automatically.
    • Keep the codebase more cleaner even if cross-container interactions are very often.
    • Prevent the collision of action type constants.
    • Reuse the selector concept in redux containers and redux saga flows.
    • Support universal application.
    • You can focus on redux reducer implementation and testing.

    In short, it can be used to reduce the codebase complexity and gain the better convention while developing.

    Installation

    Please use the following command to install Redux Knife Manager, assume you use the package management system with yarn

    yarn add redux-knife-manager redux

    or npm.

    npm install --save redux-knife-manager redux

    Quick start

    1. Consider the counter application, we need to configure the counter knife first.
    import { createStore, combineReducers } from 'redux';
    import reduxKnifeManager from 'redux-knife-manager';
     
    // 1. Initialize Redux Knife Manager
    reduxKnifeManager.initialize();
     
    // 2. Add a knife to Redux Knife Manager
    reduxKnifeManager.addKnife('counter', {
      actionMap: ['increase', 'decrease'],
      reducerMap: ({ increase, decrease }) => ({
        [increase]: (state, action) => ({
          num: state.num + action.value,
        }),
     
        [decrease]: (state, action) => ({
          num: state.num - action.value,
        }),
      }),
      defaultState: {
        num: 0,
      },
    });
     
    // 3. reducer can also listen cross-category actions
    reduxKnifeManager.addKnife('inverse', {
      actionMap: ['reset'],
      reducerMap: (
        { reset },
        { counter: { increase, decrease } },
      ) => ({
        [counter]: (state, action) => ({
          num: state.num - action.value,
        }),
     
        [counter]: (state, action) => ({
          num: state.num + action.value,
        }),
     
        [reset]: () => ({
          num: 0,
        }),
      }),
      defaultState: {
        num: 0,
      },
    });
     
    // 4. Configure the redux store
    const store = createStore(combineReducers(reduxKnifeManager.getRootReducer()));
    1. After configuring the counter knife, we can now get the counter value and dispatch the increase/decrease action.
    import React from 'react';
    import PropTypes from 'prop-types';
    import { connect } from 'react-redux';
    import reduxKnifeManager from 'redux-knife-manager';
     
    // 1. Get the counter knife
    const counterKnife = reduxKnifeManager.getKnife('counter');
     
    // 2. Configure the mapStateToProps
    function mapStateToProps(state) {
      return {
        num: counterKnife.selector.get(state, 'num'),
      };
    }
     
    // 3. Connect to redux
    @connect(mapStateToProps)
    export default class App extends React.Comopnent {
      static propTypes = {
        dispatch: PropTypes.func,
        num: PropTypes.number,
      };
     
      onIncrease() {
        // dispatch the increase action
        const { dispatch } = this.props;
        dispatch(counterKnife.action.increase({ value: Math.random() }));
      }
     
      onDecrease() {
        // dispatch the decrease action
        const { dispatch } = this.props;
        dispatch(counterKnife.action.decrease({ value: 1 }));
      }
     
      render() {
        const { num } = this.props;
     
        return (
          <div>
            <button onClick={this.onIncrease}>Increase</button>
            <button onClick={this.onDecrease}>Decrease</button>
            <div>{num}</div>
          </div>
        );
      }
    }
    1. The counter knife can be also used in other places, assume you are using redux-saga in asynchronous flow management.
    import { takeEvery } from 'redux-saga/effects';
    import reduxKnifeManager from 'redux-knife-manager';
     
    const counterKnife = reduxKnifeManager.getKnife('counter');
     
    export default function* counterSaga() {
      yield takeEvery(counterKnife.actionType.increase, function* handleIncrese(action) {
        // do something like print the action value
        console.log(action);
      });
    }

    Detailed examples

    The project takes todoMVC as detailed examples, please refers the examples folder.

    API reference

    initialize(options)

    The function initialize is used to initialize Redux Knife Manager. Since Redux Knife Manager is the single instance, the knives and related entries will be released when initialize has been called.

    Arguments

    • options (Object):
      • namespace (String, default: 'app'):
        The namespace is the prefix of top level of redux store to restore the state of knives.

    Example

    reduxKnifeManager.initialize({
      namespace: 'example',
    });

    addKnife(category, config)

    The function addKnife is used to add a knife to Redux Knife Manager. It will generate the redux entities such as action, reducer, selector automatically by the given config.

    Arguments

    • category (String):
      It is the the identifier to associate with the knife.
    • config (Object):
      • actionMap (Array of String):
        Redux Knife Manager will generate collections of action generator and action type which are based on actionMap.
      • reducerMap (Function(actionType, allActionType)):
        Redux Knife Manager will pass generated actions to reducerMap. And it must return the object of definition of reducers which are associated with spicfied actions.
      • defaultState (Object):
        The default state of knife which is associated with category.

    Returns

    • Return Knife Object if the knife is configured successfully.
    • Otherwise, undefined.

    Example

    // 1. Initialize Reudx Knife Manager
    reduxKnifeManager.initialize({
      namespace: 'example',
    });
     
     
    // 2. Add a knife to Reudx Knife Manager
    reduxKnifeManager.addKnife('counter', {
      actionMap: ['increase', 'decrease'],
      reducerMap: ({ increase, decrease }) => ({
        [increase]: (state, action) => ({
          num: state.num + action.value,
        }),
     
        [decrease]: (state, action) => ({
          num: state.num - action.value,
        }),
      }),
      defaultState: {
        num: 0,
      },
    });
     
    // 3. reducer can also listen cross-category actions
    reduxKnifeManager.addKnife('inverse', {
      actionMap: ['reset'],
      reducerMap: (
        { reset },
        { counter: { increase, decrease } },
      ) => ({
        [increase]: (state, action) => ({
          num: state.num - action.value,
        }),
     
        [decrease]: (state, action) => ({
          num: state.num + action.value,
        }),
     
        [reset]: () => ({
          num: 0,
        }),
      }),
      defaultState: {
        num: 0,
      },
    });
     
    // 4. Configure the redux store
    const store = createStore(combineReducers(reduxKnifeManager.getRootReducer()));
     
    /* 6. The redux store will be as follow:
     * {
     *   example: {
     *     counter: {
     *       num: 0,
     *     },
     *     inverse: {
     *       num: 0,
     *     },
     *  }
     */

    getKnife(category)

    The function addKnife is used to retrieve a knife from Redux Knife Manager.

    Arguments

    • category (String):
      It is the the identifier to retrieve the knife.

    Returns

    • Return the Knife Object which is associated with the given category.
    • Otherwise, it will return undefined if the knife is not exist.
    • Knife Object:
      • selector (Object):
        The collection of selector. It will generate the get method to retrieve the whole state, and selectors to retr.
      • actionType (Object):
        The collection of action type, and the properties of actionType are based on actionMap.
      • action (Object):
        The collection of action generator, and the properties of action are based on actionMap. In order to simplify the interface, the action generator do only accept the payload with the plain object, and it will construct the simple action generator. The definition of action generator is as follow:
          action[name] = (payload = {}) => ({
            type: autoGeneratedConstant,
            ...payload,
          });

    Example

    // 1. Initialize Redux Knife Manager
    reduxKnifeManager.initialize({
      namespace: 'example',
    });
     
    // 2. Add a knife to Redux Knife Manager
    reduxKnifeManager.addKnife('counter', {
      actionMap: ['increase', 'decrease'],
      reducerMap: ({ increase, decrease }) => ({
        [increase]: (state, action) => ({
          num: state.num + action.value,
        }),
     
        [decrease]: (state, action) => ({
          num: state.num - action.value,
        }),
      }),
      defaultState: {
        num: 0,
      },
    });
     
    // 3. Configure the redux store
    const store = createStore(combineReducers(reduxKnifeManager.getRootReducer()));
     
    const counterKnife = reduxKnifeManager.getKnife('counter');
    // 4. The collection of action type
    // You can get the action type of increase via the following statement
    console.log(counterKnife.actionType.increase);
     
    // You can get the action type of decrease via the following statement
    console.log(counterKnife.actionType.decrease);
     
     
    // 5. The collection of action
    // You can get the increase action via the following statement
    console.log(counterKnife.action.increase({ value: 1 }));
     
    // You can get the decrease action via the following statement
    console.log(counterKnife.action.decrease({ value: 1 }));
     
     
    // 6. The collection of selector
    // You can get the whould state of counterKnife via the following statement
    // and the value should be { num: 0 }
    console.log(counterKnife.selector.get(store.getState()));
     
    // You can get the num value of counterKnife via the following statement
    // and the value should be 0
    console.log(counterKnife.selector.get(store.getState(), 'num'));
     
     
    // 7. Reducer should also work well
    store.dispatch(counterKnife.action.increase({ value: 10 }));
     
    // You can get the num value of counterKnife via the following statement
    // and the value should be 10
    console.log(counterKnife.selector.get(store.getState(), 'num'));

    getKnives()

    The function getKnives will return all knives in Redux Knife Manager.

    Returns

    • Return the Object which is consist of knives which are associated their category.

    Example

    // 1. Initialize Redux Knife Manager
    reduxKnifeManager.initialize();
     
    // 2. Add knives to Redux Knife Manager
    reduxKnifeManager.addKnife('k1', { ... });
    reduxKnifeManager.addKnife('k2', { ... });
    reduxKnifeManager.addKnife('k3', { ... });
     
    const knives = reduxKnifeManager.getKnives();
     
    /*
     * The value of knives is as follow:
     * {
     *    k1: { ... },
     *    k2: { ... },
     *    k3: { ... }
     *  }
     */

    getRootReducer()

    The function getRootReducer is used to get combined reducers of knives. It should be used to configure with redux store.

    Returns

    • Return the Object of combined reducer of knives which are associated with namespace.

    Example

    // 1. Initialize Redux Knife Manager
    reduxKnifeManager.initialize();
     
    // 2. Add knives to Redux Knife Manager
    reduxKnifeManager.addKnife('k1', { ... });
    reduxKnifeManager.addKnife('k2', { ... });
    reduxKnifeManager.addKnife('k3', { ... });
     
    // 3. Configure the redux store
    const store = createStore(combineReducers(reduxKnifeManager.getRootReducer()));

    Todo

    • Adding the example for integrating with redux-saga
    • Adding the example for integrating with re-select

    Inspired by

    License

    This project is licensed under the MIT license, Copyright (c) 2018 madetheforcebewithyou. For more information, please see LICENSE.

    Install

    npm i redux-knife-manager

    DownloadsWeekly Downloads

    38

    Version

    3.2.0

    License

    MIT

    Unpacked Size

    189 kB

    Total Files

    9

    Last publish

    Collaborators

    • avatar
    • avatar