Automate the Redux boilerplate.
This library is still in the idea phase. Please check it out and file any issues you encounter.
npm install --save autodux
And then in your file:
import autodux from 'autodux';Or using CommonJS syntax:
const autodux = require('autodux');Redux is great, but you have to make a lot of boilerplate:
- Action constants
- Action creators
- Reducers
- Selectors
It's great that Redux is such a low-level tool. It's allowed a lot of flexibility and enabled the community to experiment with best practices and patterns.
It's terrible that Redux is such a low-level tool. It turns out that:
- Most reducers spend most of their logic switching over action types.
- Most of the actual state updates could be replaced with generic tools like "concat payload to an array", "remove object from array by some prop", or "increment a number". Better to reuse utilities than to implement these things from scratch every time.
- Lots of action creators don't need arguments or payloads -- just action types.
- Action types can be automatically generated by combining the name of the state slice with the name of the action, e.g.,
counter/increment. - Lots of selectors just need to grab the state slice.
Lots of Redux beginners separate all these things into separate files, meaning you have to open and import a whole bunch of files just to get some simple state management in your app.
What if you could write some simple, declarative code that would automatically create your:
- Action type constants
- Reducer switching logic
- State slice selectors
- Action object shape - automatically inserting the correct action type
- Entire action creators if no payload is required
Turns out, when you add this simple logic on top of Redux, you can do a lot more with a lot less code.
import autodux from 'autodux';
// This can be used for action creators that pass
// a single argument through as the payload,
// and also for selectors that just select the
// whole reducer state.
const id = x => x;
const counter = autodux({
// the slice of state your reducer controls
slice: 'counter',
// The initial value of your reducer state
initial: 0,
// No need to implement switching logic -- it's
// done for you.
actions: {
increment: {
reducer: state => state + 1
},
decrement: {
reducer: state => state - 1
},
multiply: {
create: id,
reducer: (state, payload) => state * payload
}
},
// No need to select the state slice -- it's done for you.
selectors: {
getValue: id
}
});What you get from that is an object that looks like this:
{
initial: 0,
actions: {
increment: { [Function]
type: 'counter/increment'
},
decrement: { [Function]
type: 'counter/decrement'
},
multiply: { [Function]
type: 'counter/multiply'
}
},
selectors: {
getValue: [Function: wrapper]
},
reducer: [Function: reducer]
}Let's explore that object a bit:
const {
selectors: { getValue },
increment,
decrement
} = counter;
const actions = [
increment(),
increment(),
increment(),
decrement()
];
actions.reduce(reducer, initial); // 2
console.log(increment.type); // 'counter/increment'
console.log(getValue({counter: 3})); // 3