Rematch Default Reducers

rematch makes working
with redux a breeze, but there's still a bit of
boilerlate that needs to be automated: reducers (or "actions" as I call them).
If you're tired of writing reducers like setThing
, addThing
, removeThing
,
or resetThing
for every single piece of state, then this library is for you.
Installation
npm install --save rematch-default-reducers
Usage:
import {init} from '@rematch/core'
import {withDefaultReducers} from 'rematch-default-reducers'
import {tonsOfModelsWithTonsOfState} from '../models'
export default init({
models: withDefaultReducers(tonsOfModelsWithTonsOfState),
})
Documentation
Below is the API for withDefaultReducers
and the reducers it generates.
withDefaultReducers(models, [opts])
the named and only export of rematch-default-reducer
models: {[modelName]: {state: any, reducers?: {[reducerName]: function}}}
- the
models
config expected by init()
from @rematch/core
state
may not contain null
or undefined
values, or else TypeError
will be thrown. null
/undefined
may be allowed by passing
{allowNil: true}
as opts
opts?: {allowNil, typeCheck}
allowNil?: boolean
(default: true
) - if false
, models' state
may
contain null
/undefined
, but reducers will not be generated for those
slices of the redux
store
typeCheck?: boolean
(default: true
) - if false
, default reducer
actions will not perform type checking to ensure payload
s preserve the
type/interface the model was initialized with.
Common Default Reducers
these default reducers are provided for all models
dispatch.${modelName}.set(payload, [meta])
When ${modelName}.state
is a {}
-Object
set
performs a deep merge between state
and payload
.
When ${modelName}.state
is NOT a {}
-Object
set
overwrites model.state
with the value of payload
Note: If the payload
of set
would alter the type/interface with which
${modelName}.state
was initialized, then a TypeError
will be thrown,
unless {typeCheck: false}
has been passed as an option to
withDefaultReducers
or as a meta
option to dispatch.${modelName}.set
.
dispatch.${modelName}.reset()
Sets ${modelName}.state
to the value it was initialized with.
The rootState
Model and Reducers
withDefaultReducers
adds a pseudo-model called rootState
. It has no
state
of its own, and only exists to provide a couple default reducers for
performing updates across multiple models in a single action.
dispatch.rootState.set(payload, [meta])
payload: {[modelName: string]: any}
- an updater object which will
effectively be deep-merged with the redux
store in order to produce the next
state in a single action.
meta?: {typeCheck?: boolean}
(optional) - options for the currently
dispatched action
typeCheck?: boolean
(optional) - enables/disables type-checking that
prevents set
from altering the type/interface of the model. Default:
true
.
dispatch.rootState.reset()
Resets all models back to their initial state.
When ${modelName}.state
is a {}
-Object
When a model's state
is a {}
-Object, default reducers are generated for each
property of a model's state
, in addition to dispatch.${modelName}.set
and dispatch.${modelName}.reset
. The reducer names are auto-generated
based on the property's name and follow a camel-case naming convention.
dispatch.${modelName}.set${PropName}(payload, [meta])
If the property being set is a {}
-Object, then it set the property to the
result of performing a deep merge between the property's current state and
the payload
.
Otherwise, it simply overwrites the value of the property with payload
.
Note: If the payload
of set${PropName}
would alter the type/interface
with which ${modelName}.state
was initialized, then a TypeError
will be
thrown, unless {typeCheck: false}
has been passed as an option to
withDefaultReducers
or as a meta
option to
dispatch.${modelName}.set${PropName}
.
Example:
const {dispatch, getState} = init({
models: withDefaultReducers({
user: {
state: {
name: '',
things: [],
address: {
street: {
primary: '',
secondary: '',
},
city: ''
state: '',
},
},
},
}),
})
dispatch.user.setAddress({
street: { primary: '123 ABC Lane'}
})
dispatch.user.setName('Anderson')
dispatch.user.setThings(['thing1', 'thing2'])
getState().user
dispatch.${modelName}.reset${PropName}
Resets the property to its initial state
When a property of ${modelName}.state
is an Array
, that property gets
several other reducers in addition to
set${PropName}
and
reset${PropName}
.
For the sake of example and readability, let's assume that we have store
initialized like so:
const {dispatch, getState} = init({
models: withDefaultReducers({
myModel: {
state: {things: []},
},
}),
})
The following reducers would be generated:
Take note that some reducers refer to the property name in the singular and
some in the plural.
dispatch.myModel.concatThings(payload: any[])
Sets myModel.state.things
to the result of concatentating payload
to the end
of myModel.state.things
Example:
store.getState().myModel.things
dispatch.myModel.concatThings(['hello'])
store.getState().myModel.things
dispatch.myModel.concatThingsTo(payload: any[])
Sets myModel.state.things
to the result of concatenating
myModel.state.things
to the end of payload
Example:
store.getState().myModel.things
dispatch.myModel.concatThingsTo(['henlo'])
store.getState().myModel.things
dispatch.myModel.filterThings(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Filters myModel.state.things
down to the elements that return true
when
passed to the predicate function on payload.where
along with the element's
index
.
Example:
store.getState().myModel.things
dispatch.myModel.filterThings({where: (el, i) => typeof el === 'string'})
store.getState().myModel.things
dispatch.myModel.insertThing(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any
Note singular | Inserts payload.payload
at the first index where
payload.where
returns true
. Pre-existing elements from that index onwards
have their indexes incremented by one.
Example:
getState().myModel.things
dispatch.myModel.insertThing({where: (el, i) => i === 1, {name: 'Ben'}})
getState().myModel.things
dispatch.myModel.insertThings(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
Inserts the contents of payload.payload
starting at the first index where
payload.where
returns true
. Pre-existing elements from that index onwards
have their indexes incremented by the length of payload.payload
.
Example:
getState().myModel.things
dispatch.myModel.insertThings({where: (el, i) => i === 1, [4, 5, 6]})
getState().myModel.things
dispatch.myModel.mapThings(mapFn)
mapFn: function(elmt: any, index: number): any
Sets myModel.things
to the array returned from mapFn
. Behaves mostly like
Array.prototype.map
, except it only has arity 1 and mapFn
has only arity 2.
Example:
store.getState().myModel.things
dispatch.myModel.mapThings((el, idx) => el.toUpperCase())
store.getState().myModel.things
dispatch.myModel.popThings(n?: number)
n?: number
(optional) - number of elements to "pop" from list. Default:
1
Sets myModel.things
to a copy of itself with the last n
elements removed.
Example:
store.getState().myModel.things
dispatch.myModel.popThings()
store.getState().myModel.things
dispatch.myModel.popThings(2)
store.getState().myModel.things
dispatch.myModel.pushThing(payload: any)
payload: any
- the value to append to the end of the list
Sets myModel.things
to a copy of itself with payload
appended as the last
element.
To append multiple elements see
dispatch.myModel.concatThings.
Example:
store.getState().myModel.things
dispatch.myModel.pushThing(4)
store.getState().myModel.things
dispatch.myModel.removeThing(payload: any)
payload: any
- the value to remove
Sets myModel.things
to a copy of itself omitting the first element found to be
strictly equal (===
) to payload
.
Example:
const [george] = getState().myModel.things
dispatch.myModel.removeThing(george)
getState().myModel.things
dispatch.myModel.removeThing(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Sets myModel.things
to a copy of itself omitting the first element for which
payload.where
returns true
.
Example:
getState().myModel.things
dispatch.myModel.removeThing({where: (el, idx) => el.length < 3})
getState().myModel.things
dispatch.myModel.removeThings(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Sets myModel.things
to a copy of itself omitting all elements for which
payload.where
returns true
.
Example:
getState().myModel.things
dispatch.myModel.removeThings({where: (el, idx) => el.length < 3})
getState().myModel.things
dispatch.myModel.replaceThing(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any
Sets myModel.things
to a copy of itself with the first element for which
payload.where
returns true
replaced with the value of payload.payload
.
Example:
getState().myModel.things
dispatch.myModel.replaceThing({where: (el, idx) => el.length < 3, 'find'})
getState().myModel.things
dispatch.myModel.shiftThings(n?: number)
n?: number
(optional) - the number of elements to remove from the front of
the list. Default: 1
Sets myModel.things
to a copy of itself with the first n
elements removed.
Example:
getState().myModel.things
dispatch.myModel.shiftThings()
getState().myModel.things
dispatch.myModel.shiftThings(2)
getState().myModel.things
dispatch.myModel.unshiftThing(payload: any)
payload: any
- the value to prepend to the list
Sets myModel.things
to a copy of itself with payload
prepended to the list.
To prepend multiple elements, see
dispatch.myModel.concatThingsTo
Example:
getState().myModel.things
dispatch.myModel.unshiftThing('Howdy!')
getState().myModel.things
When ${modelName}.state
is []
(Array
)
When ${modelName}.state
is an Array
, that model gets several other reducers
in addition to set
and
reset
.
For the sake of example and readability, let's assume that we have store
initialized like so:
const {dispatch, getState} = init({
models: withDefaultReducers({
myModel: {
state: [],
},
}),
})
The following reducers that would be generated:
dispatch.myModel.concat(payload: any[])
Sets myModel.state
to the result of concatentating payload
to the end of
myModel.state
store.getState().myModel
dispatch.myModel.concat(['hello'])
store.getState().myModel
dispatch.myModel.concatTo(payload: any[])
Sets myModel.state
to the result of concatenating myModel.state
to the end
of payload
store.getState().myModel
dispatch.myModel.concatTo(['henlo'])
store.getState().myModel
dispatch.myModel.filter(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Filters myModel.state
down to the elements that return true
when passed to
the predicate function on payload.where
along with the element's index
.
Example:
store.getState().myModel
dispatch.myModel.filter({where: (el, i) => typeof el === 'string'})
store.getState().myModel
dispatch.myModel.insert(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any
Note singular | Inserts payload.payload
at the first index where
payload.where
returns true
. Pre-existing elements from that index onwards
have their indexes incremented by one.
getState().myModel
dispatch.myModel.insert({where: (el, i) => i === 1, {name: 'Ben'}})
getState().myModel
dispatch.myModel.insertAll(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
Inserts the contents of payload.payload
starting at the first index where
payload.where
returns true
. Pre-existing elements from that index onwards
have their indexes incremented by the length of payload.payload
.
getState().myModel
dispatch.myModel.insertAll({where: (el, i) => i === 1, [4, 5, 6]})
getState().myModel
dispatch.myModel.map(mapFn)
mapFn: function(elmt: any, index: number): any
Sets myModel
to the array returned from mapFn
. Behaves mostly like
Array.prototype.map
, except it only has arity 1 and mapFn
has only arity 2.
store.getState().myModel
dispatch.myModel.map((el, idx) => el.toUpperCase())
store.getState().myModel
dispatch.myModel.pop(n?: number)
n?: number
(optional) - number of elements to "pop" from list. Default:
1
Sets myModel
to a copy of itself with the last n
elements removed.
store.getState().myModel
dispatch.myModel.pop()
store.getState().myModel
dispatch.myModel.pop(2)
store.getState().myModel
dispatch.myModel.push(payload: any)
payload: any
- the value to append to the end of the list
Sets myModel
to a copy of itself with payload
appended as the last element.
To append multiple elements see
dispatch.myModel.concat.
store.getState().myModel
dispatch.myModel.pushThing(4)
store.getState().myModel
dispatch.myModel.remove(payload: any)
payload: any
- the value to remove
Sets myModel
to a copy of itself omitting the first element found to be
strictly equal (===
) to payload
.
Example:
const [george] = getState().myModel
dispatch.myModel.remove(george)
getState().myModel
dispatch.myModel.remove(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Sets myModel
to a copy of itself omitting the first element for which
payload.where
returns true
.
Example:
getState().myModel
dispatch.myModel.remove({where: (el, idx) => el.length < 3})
getState().myModel
dispatch.myModel.removeAll(payload: { where })
payload.where: function(elmt: any, index: number): boolean
Sets myModel
to a copy of itself omitting all elements for which
payload.where
returns true
.
Example:
getState()
.myModel
.dispatch.myModel.remove({where: (el, idx) => el.length < 3})
getState().myModel
dispatch.myModel.replace(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any
Sets myModel
to a copy of itself with the first element for which
payload.where
returns true
replaced with the value of payload.payload
.
Example:
getState().myModel
dispatch.myModel.replace({where: (el, idx) => el.length < 3, 'find'})
getState().myModel
dispatch.myModel.shift(n?: number)
n?: number
(optional) - the number of elements to remove from the front of
the list. Default: 1
Sets myModel
to a copy of itself with the first n
elements removed.
Example:
getState().myModel
dispatch.myModel.shift()
getState().myModel
dispatch.myModel.shift(2)
getState().myModel
dispatch.myModel.unshift(payload: any)
payload: any
- the value to prepend to the list
Sets myModel
to a copy of itself with payload
prepended to the list.
To prepend multiple elements, see
dispatch.myModel.concatTo
Example:
getState().myModel
dispatch.myModel.unshift('Howdy!')
getState().myModel