redux-standard-actions
Flux Standard Action utilities for Redux.
This project was a fork of redux-actions. It's now developed and published as a separate NPM module for active maintenance and to optimize on development iteration speed.
Getting started
Install via NPM.
npm install --save redux-standard-actions
API
Flux Standard Action Creators
makeActionCreator(type, payloadCreator = identity, ?metaCreator)
Returns an Flux Standard Action creator.
payloadCreator
can only be a function or undefined
(in which case the identity is used). metaCreator
is an optional function that builds the meta
value of the action, receiving the same arguments as payloadCreator
.
Note that payload
will only be set on the action if payloadCreator
does not return undefined
, and meta
will be set only if metaCreator
is a function.
let decrement = makeActionCreator('DECREMENT', amount => -amount));
expect(decrement(42)).to.deep.equal({ type: 'DECREMENT', payload: -42 });
If the action creator is called with an Error , action.error
will be set to true
and the payload creator will not be called; instead, the payload is set to the error.
Note that the meta creator will still be called in this case.
const decrement = makeActionCreator('DECREMENT', amount => -amount);
const error = new TypeError('not a number');
expect(decrement(error)).to.deep.equal({ type: 'DECREMENT', payload: error, error: true });
makeActionCreators(?actionsMap, ?...actionTypes)
Returns an object mapping action types to Flux Standard Action creators.
The keys of this object are camel-cased from the keys in actionsMap
; the values are the action creators.
actionsMap
is an optional object with action types as keys, and payload creators as values. actionTypes
is an optional list of positional arguments that are action type strings; these action types will use the identity payload creator.
There's currently no support for specifying any metaCreator
with this syntax (though it would be easy to add).
const { actionOne, actionTwo, actionThree } = makeActionCreators({
ACTION_ONE(key, value) {
return { [key]: value };
},
ACTION_TWO(first, second) {
return [ first, second ];
},
}, 'ACTION_THREE');
expect(actionOne('key', 1)).to.deep.equal({
type: 'ACTION_ONE',
payload: { key: 1 }
});
expect(actionTwo('first', 'second')).to.deep.equal({
type: 'ACTION_TWO',
payload: ['first', 'second'],
});
expect(actionThree(3)).to.deep.equal({
type: 'ACTION_THREE',
payload: 3,
});
combineActions(...types)
Combine any number of FSA types or FSA creators for use by an FSA reducer.
types
is a variadic list of arguments which can be action type strings or action creators.
This method exists because while FSA type strings can be joined with a conventional delimiter, there is no obvious way for a library user to combine FSA creators.
The return value of this method is meant solely for use as action types in makeActionReducer
and makeActionReducers
.
const increment = makeActionCreator('INCREMENT', amount => ({ amount }))
const decrement = makeActionCreator('DECREMENT', amount => ({ amount: -amount }))
const reducer = makeActionReducer(
combineActions(increment, decrement),
{
next(state, { payload: { amount } }) {
return { ...state, counter: state.counter + amount }
},
throw(state) {
return { ...state, counter: 0 }
},
},
{ counter: 10 }
)
expect(reducer(undefined, increment(1)).to.deep.equal({ counter: 11 })
expect(reducer(undefined, decrement(1)).to.deep.equal({ counter: 9 })
expect(reducer(undefined, increment(new Error)).to.deep.equal({ counter: 0 })
expect(reducer(undefined, decrement(new Error)).to.deep.equal({ counter: 0 })
This also works in when declaring reducers with the next
/throw
object format in makeActionReducer
and makeActionReducers
.
Flux Standard Action Reducers
makeActionReducer(type, reducerFn | reducerMap, ?defaultState)
Returns a reducer that handles Flux Standard Actions of a certain type.
type
is a string action type, or an action creator from makeActionCreator
.
If a function reducerFn
is given, it is used to handle all Flux Standard Actions with type type
, i.e. those with error: true
and those without an error
key.
Otherwise, you can pass an object reducerMap
with separate reducers for next()
and throw()
, which will only handle non-error and error FSA's, respectively.
All reducers here must be undefined
or a function. undefined
reducers will default to the identity.
makeActionReducer('LOGIN', {
next(state, { payload: { userProfile } }) {
return { ...state, loggedInUser: userProfile }
},
throw(state, action) {
// reset the state in case anything goes wrong
return { ...state, loggedInUser: null }
}
}, { loggedInUser: null });
The optional third parameter specifies a default state which is used when an undefined
state is passed to the reducer.
makeActionReducers(reducerMap, ?defaultState)
Returns a reduced reducer from multiple action reducers.
reducerMap
is an object where the keys are action types or action creators, and the values are the corresponding reducer functions, or an object in the next/throw form.
Any undefined
reducer will be defaulted to the identity, as in makeActionReducer
.
The optional second parameter specifies a default or initial state, which is used when undefined
is passed to the reduced reducer.
const increment = makeActionCreator('INCREMENT');
const decrement = makeActionCreator('DECREMENT');
const reducer = makeActionReducers({
[increment]: (state, { payload: { amount } }) => ({ ...state, counter: state.counter + amount }),
[decrement]: {
next(state, { payload: { amount } }) {
return { ...state, counter: state.counter - amount }
},
throw(state) {
return { ...state, counter: 0 }
},
},
}, { counter: 0 });
// can handle actions dispatched from the action creators
expect(reducer(undefined, increment({ amount: 1 }))).to.deep.equal({ counter: 1 })
expect(reducer({ counter: 3 }, increment({ amount: 7 }))).to.deep.equal({ counter: 10 })
expect(reducer({ counter: 3 }, decrement({ amount: 1 }))).to.deep.equal({ counter: 2 })
// can handle actions not dispatched directly from
// the action creator, and error actions as well
expect(
reducer(
{ counter: 3 },
{ type: 'DECREMENT', payload: { amount: 7 } }
)
).to.deep.equal({ counter: -4 })
expect(
reducer(
{ counter: 3 },
{ type: 'DECREMENT', payload: new Error, error: true }
)
).to.deep.equal({ counter: 0 })
Usage with middleware
Integrate with redux-thunk, or redux-saga.