redux-knife-manager
v3.2.0
Published
The redux utility for managing redux entities.
Downloads
10
Readme
Redux Knife Manager
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
- 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()));
- 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>
);
}
}
- The
counter knife
can be also used in other places, assume you are usingredux-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.
- namespace (String, default: 'app'):
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 onactionMap
. - reducerMap (Function(actionType, allActionType)):
Redux Knife Manager will pass generated actions toreducerMap
. 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 withcategory
.
- actionMap (Array of String):
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 theget
method to retrieve the whole state, and selectors to retr. - actionType (Object):
The collection of action type, and the properties of actionType are based onactionMap
. - action (Object):
The collection of action generator, and the properties of action are based onactionMap
. 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:
- selector (Object):
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
.