redux-processor
v2.6.1
Published
It's a tool to organize your reducers and action creators, built on top of Redux.
Downloads
7
Readme
Redux-Processor
It's a tool to organize your reducers and action creators, built on top of Redux.
This module ships ES6 code, so be sure that you're using tools like Babel or your target browsers supports ES6 natively.
Why yet another tool
We used to organize our action creators and reducers in separate files, but on large scale apps this approach doesn't work very well. Tons of separate files and constants with action names brings bunch of pain for developes. Approach of Processor is to merge all of these files.
Reducers in Processor are separated by chunks of state and it allows you to store initial state, action creators and reducers all together.
Installation
Simple as hell
npm install redux-processor -s
or, if you're familiar with Yarn
yarn add redux-processor
How to
Methods
Processor itself provides two functions to create state chunks:
processor(initialState, actions)
– processor functionality itself
processorMulti(multiActions[, initialState])
– for those, who's familiar with combineReducers
Importing
Both ES6 modules and CommonJS styles are supported, so you're free to choose you the way to import Processor:
ES6 style
import { processor } from 'redux-processor'
import { multi } from 'redux-processor'
/* OR */
import { multi as processor } from 'redux-processor'
/* OR */
import processor from 'redux-processor/lib/processor'
import multi from 'redux-processor/lib/multi'
CommonJS style
const processor = require('redux-processor').processor
const multi = require('redux-processor').multi
/* OR */
const { processor } = require('redux-processor')
/* OR */
const processor = require('redux-processor/lib/processor');
const multi = require('redux-processor/lib/multi');
Basics
Simple example of how to use processor within your application:
import { createStore } from 'redux';
import { processor } from 'redux-processor';
const initialState = {
count: 1
};
const preducers = processor(initialState, {
increase: {
type: "INCREASE",
process(state){
return Object.assign({}, state, {
count: state.count + 1
})
}
},
decrease: {
type: "DECREASE",
process(state){
return Object.assign({}, state, {
count: state.count - 1
})
}
},
})
const store = createStore(preducers.reducer);
store.subscribe(function(){
console.log(`Count is ${store.getState().count}`)
})
store.dispatch({ type: "INCREASE" }) //=> Count is 1
store.dispatch({ type: "INCREASE" }) //=> Count is 2
store.dispatch({ type: "DECREASE" }) //=> Count is 1
Using Named Actions
Named Actions is a handy tool designed to avoid action types sharing over your app and keep them only as a system-level names. The only place where you can see action types is the reducer declaration file.
To setup Named Actions Processor provides a wrap(store)
function
const store = createStore(preducers.reducer);
const actions = preducers.wrap(store);
store.subscribe(function(){
console.log(`Count is ${store.getState().count}`)
})
actions.increase() //=> Count is 1
actions.increase() //=> Count is 2
actions.decrease() //=> Count is 1
Action creators
When looking at these examples you maybe wonder how to provide data to reducer when you need to perform more complex mutations to the state. It's also very simple:
const initialState = {
name: "John",
surname: "Sena"
};
const preducers = processor(initialState, {
setName: {
type: "SET_NAME",
action(name){
return { name: name }
},
process(state, payload){
return Object.assign({}, state, {
name: payload.name
})
}
},
setSurname: {
type: "SET_SURNAME",
action(surname){
return surname
},
process(state, surname){
return {...state, surname}
}
},
})
const store = createStore(preducers.reducer);
store.subscribe(function(){
const {name, surname} = store.getState();
console.log(`Hello ${name} ${surname}`)
})
store.setName("Mike") //=> Hello Mike Sena
store.setSurname("Wasowski") //=> Hello Mike Wasowski
Describing named actions and action creators
As you can see, action creators works just like you used to. The only difference is that reducer function takes only a payload part of an action, everything else Processor does under the hood, so you don't need to deal with action types at all.
If your actions are so simple, so they don't need any additional data then you can get rid of action creator at all, Processor will create Named Action for you automatically.
Also if you need to pass some data to an action "as-is", then you can pass any number of arguments to Named Actions. The only crucial difference here is that if you're passing more than one argument to a Named Action, then you will get an array inside of reducer. To clarify this part look at the example below:
const initialState = {
name: "John",
surname: "Sena"
};
const preducers = processor(initialState, {
setFullName: {
type: "SET_NAME",
process(state, payload){
const [name, surname] = payload
return Object.assign({}, state, { name, surname })
}
}
})
const store = createStore(preducers.reducer);
store.subscribe(function(){
const {name, surname} = store.getState();
console.log(`Hello ${name} ${surname}`)
})
store.setFullName("Mike", "Shinoda") //=> Hello Mike Shinoda
You can notice that we use array destructuring syntax to create variables with name
and surname
. That's because we didn't create any action creator, so Processor has created it for us. As Processor doesn't know anything about data that we're passing and how to deal with it, it just transferring all arguments that we're passing to Named Action down to the reducer function. It causes the payload to be an array.
Otherwise, is we're passing only one argument to a Named Action, then we'll get a single value:
const initialState = {
filter: "ALL",
};
const preducers = processor(initialState, {
setFilter: {
type: "SET_FILTER",
process(state, filter){
return Object.assign({}, state, { filter })
}
}
})
const store = createStore(preducers.reducer);
store.subscribe(function(){
console.log(`Filter: ${store.getState().filter}`)
})
store.setFilter("COMPLETED") //=> Filter: COMPLETED
Multiple processors
Basic usage
You're might be familiar with redux's combineReducers
function. Processor has its own alternative with similar functionality. Let's say you have two scopes in your state: user
and todos
. To separate reducers for these scopes you can use multi(setup[, initialState])
function.
import { createStore } from 'redux';
import { multi as processor } from 'redux-processor';
const initialState = {
user: {
name: "Mike",
surname: "Shinoda"
},
todos: []
};
const mpreducers = processor({
user: {
setName: {
type: "SET_NAME",
process (state, name) => { ...state, name }
}
setSurname: {
type: "SET_SURNAME",
process (state, surname) => { ...state, surname }
}
},
todos: {
add: {
type: "ADD_TODO",
process(state, payload){
const todo = Object.assign({
id: generateTodoID()
}, payload)
return [...state, todo]
}
}
}
}, initialState)
const store = createStore(mpreducers.reducer);
const actions = mpreducers.wrap(store);
actions.user.setName("Nicholas");
actions.user.setSurname("Cage");
actions.todos.add("First todo");
actions.todos.add("Second todo");
Defining state for each processor
If you want to define initial state and structure for each reducer individually, then take a look at next example which works just like the previous one, but each reducer takes care of its part of a state. Resulting state is a combination of states returned by individual state chunks:
import { createStore } from 'redux';
import { multi as processor } from 'redux-processor';
const mpreducers = processor({
user: {
state: {
name: "Mike",
surname: "Shinoda"
},
setName: {
type: "SET_NAME",
process (state, name) => { ...state, name }
}
setSurname: {
type: "SET_SURNAME",
process (state, surname) => { ...state, surname }
}
},
todos: {
state: [],
add: {
type: "ADD_TODO",
process(state, payload){
const todo = Object.assign({
id: generateTodoID()
}, payload)
return [...state, todo]
}
}
}
})
const store = createStore(mpreducers.reducer);
const actions = mpreducers.wrap(store);
actions.user.setName("Nicholas");
actions.user.setSurname("Cage");
actions.todos.add("First todo");
actions.todos.add("Second todo");
In this case you don't need to provide initial state for processor
, instead you providing state for each state chunk itself.
Resetting store
Sometimes it might be useful to be able to reset whole redux state completely. For trat purpose reduxp-processor
offers resetStore()
method that does exactly what it says. Notice that unlike regular reducers, resetStore()
lives at the root of actions
provided by wrap()
method.
const store = createStore(mpreducers.reducer);
const actions = mpreducers.wrap(store);
actions.user.setName("Nicholas");
actions.user.setSurname("Cage");
actions.resetStore()
Organizing reducers
With processor it's very simple to organize your code using separate files for each of your state chunks:
user_reducer.js
module.exports.state = {
name: "Mike",
surname: "Shinoda"
}
module.exports.setName = {
type: "SET_NAME",
process (state, name) => { ...state, name }
}
module.exports.setSurname = {
type: "SET_SURNAME",
process (state, surname) => { ...state, surname }
}
todos_reducer.js
module.exports.state = []
module.exports.add = {
type: "ADD_TODO",
process(state, payload){
const todo = Object.assign({
id: generateTodoID()
}, payload)
return [...state, todo]
}
}
app.js
import { createStore } from 'redux';
import { multi as processor } from 'redux-processor';
const mpreducers = processor({
user: require('reducers/user_reducer.js'),
todos: require('reducers/todos_reducer.js')
})
const store = createStore(mpreducers.reducer);
const actions = mpreducers.wrap(store);
actions.user.setName("Nicholas");
actions.user.setSurname("Cage");
actions.todos.add("First todo");
actions.todos.add("Second todo");
Contribution
Feel free to fork and create PRs.
License
MIT