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:
models:
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 byinit()
from@rematch/core
state
may not containnull
orundefined
values, or elseTypeError
will be thrown.null
/undefined
may be allowed by passing{allowNil: true}
asopts
- the
opts?: {allowNil, typeCheck}
allowNil?: boolean
(default:true
) - iffalse
, models'state
may containnull
/undefined
, but reducers will not be generated for those slices of theredux
storetypeCheck?: boolean
(default:true
) - iffalse
, default reducer actions will not perform type checking to ensurepayload
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])
-
payload
:any
(required) - the value to set${modelName}.state
to -
meta?: {typeCheck?: boolean}
(optional) - options for the currently dispatched actiontypeCheck?: boolean
(optional) - enables/disables type-checking that preventsset
from altering the type/interface of the model. Default:true
.
When ${modelName}.state
is a {}
-Object
set
performs a deep merge betweenstate
andpayload
.
When ${modelName}.state
is NOT a {}
-Object
set
overwritesmodel.state
with the value ofpayload
Note: If the
payload
ofset
would alter the type/interface with which${modelName}.state
was initialized, then aTypeError
will be thrown, unless{typeCheck: false}
has been passed as an option towithDefaultReducers
or as ameta
option todispatch.${modelName}.set
.
dispatch.${modelName}.reset()
Sets ${modelName}.state
to the value it was initialized with.
rootState
Model and Reducers
The
withDefaultReducers
adds a pseudo-model calledrootState
. It has nostate
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 theredux
store in order to produce the next state in a single action.meta?: {typeCheck?: boolean}
(optional) - options for the currently dispatched actiontypeCheck?: boolean
(optional) - enables/disables type-checking that preventsset
from altering the type/interface of the model. Default:true
.
dispatch.rootState.reset()
Resets all models back to their initial state.
${modelName}.state
is a {}
-Object
When 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])
-
payload
(type depends on property's intial state) - the value with which to update the the property -
meta?: {typeCheck?: boolean}
(optional) - options for the currently dispatched actiontypeCheck?: boolean
(optional) - enables/disables type-checking that preventsset${PropName}
from altering the type/interface of the model. Default:true
.
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
ofset${PropName}
would alter the type/interface with which${modelName}.state
was initialized, then aTypeError
will be thrown, unless{typeCheck: false}
has been passed as an option towithDefaultReducers
or as ameta
option todispatch.${modelName}.set${PropName}
.
Example:
const dispatch getState = dispatchuser dispatchuser dispatchuser user/* { name: 'Anderson', things: ['thing1', 'thing2'], address: { street: { primary: '123 ABC Lane', secondary: '', }, city: '' state: '', }} */
dispatch.${modelName}.reset${PropName}
Resets the property to its initial state
Array
Type Properties
Extra Reducers for 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 =
The following reducers would be generated:
dispatch.myModel.concatThings(payload: any[])
dispatch.myModel.concatThingsTo(payload: any[])
dispatch.myModel.filterThings(payload: { where })
dispatch.myModel.insertThing(payload: { where, payload })
dispatch.myModel.insertThings(payload: { where, payload })
dispatch.myModel.mapThings(mapFn)
dispatch.myModel.popThings(n?: number)
dispatch.myModel.pushThing(payload: any)
dispatch.myModel.removeThing(payload: any)
dispatch.myModel.removeThing(payload: { where })
dispatch.myModel.removeThings(payload: { where })
dispatch.myModel.replaceThing(payload: { where, payload })
dispatch.myModel.shiftThings(n?: number)
dispatch.myModel.unshiftThing(payload: any)
Take note that some reducers refer to the property name in the singular and some in the plural.
dispatch.myModel.concatThings(payload: any[])
payload: any[]
Sets myModel.state.things
to the result of concatentating payload
to the end
of myModel.state.things
Example:
storemyModelthings // => ['world']dispatchmyModelstoremyModelthings // => ['world', 'hello']
dispatch.myModel.concatThingsTo(payload: any[])
payload: any[]
Sets myModel.state.things
to the result of concatenating
myModel.state.things
to the end of payload
Example:
storemyModelthings // => ['world']dispatchmyModelstoremyModelthings // => ['henlo', 'world']
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:
storemyModelthings // ['blah', 4, 'blah]dispatchmyModelstoremyModelthings // ['blah', 'blah]
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:
myModelthings // => [{name: 'George'}, {name: 'Abe'}]dispatchmyModelmyModelthings // => [{name: 'George'}, {name: 'Ben'}}, {name: 'Abe'}]
dispatch.myModel.insertThings(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any[]
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:
myModelthings // => [1, 2, 3]dispatchmyModelmyModelthings // => [1, 4, 5, 6, 2, 3]
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:
storemyModelthings // => ['henlo', 'world']dispatchmyModelstoremyModelthings // => ['HENLO', 'WORLD']
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:
storemyModelthings // => [1, 2, 3, 4, 5]dispatchmyModelstoremyModelthings // => [1, 2, 3, 4]dispatchmyModelstoremyModelthings // => [1, 2]
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:
storemyModelthings // => [1, 2, 3]dispatchmyModelstoremyModelthings // => [1, 2, 3, 4]
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 = myModelthings // => [{name: 'George'}, {name: 'Abe'}]dispatchmyModelmyModelthings // => [{name: 'Abe'}]
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:
myModelthings // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ["won't", 'you', 'my', 'neighbor?']
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:
myModelthings // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ["won't", 'you', 'neighbor?']
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:
myModelthings // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ["won't", 'you', 'find', 'my', 'neighbor?']
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:
myModelthings // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ['you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ['my', 'neighbor?']
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:
myModelthings // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModelthings // => ['Howdy!', "won't", 'you', 'be', 'my', 'neighbor?']
${modelName}.state
is []
(Array
)
When 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 =
The following reducers that would be generated:
dispatch.myModel.concat(payload: any[])
dispatch.myModel.concatTo(payload: any[])
dispatch.myModel.filter(payload: { where })
dispatch.myModel.insert(payload: { where, payload })
dispatch.myModel.insertAll(payload: { where, payload })
dispatch.myModel.map(mapFn)
dispatch.myModel.pop(n?: number)
dispatch.myModel.push(payload: any)
dispatch.myModel.remove(payload: any)
dispatch.myModel.remove(payload: { where })
dispatch.myModel.removeAll(payload: { where })
dispatch.myModel.replace(payload: { where, payload })
dispatch.myModel.shift(n?: number)
dispatch.myModel.unshift(payload: any)
dispatch.myModel.concat(payload: any[])
payload: any[]
Sets myModel.state
to the result of concatentating payload
to the end of
myModel.state
storemyModel // => ['world']dispatchmyModelstoremyModel // => ['world', 'hello']
dispatch.myModel.concatTo(payload: any[])
payload: any[]
Sets myModel.state
to the result of concatenating myModel.state
to the end
of payload
storemyModel // => ['world']dispatchmyModelstoremyModel // => ['henlo', 'world']
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:
storemyModel // ['blah', 4, 'blah]dispatchmyModelstoremyModel // ['blah', 'blah]
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.
myModel // => [{name: 'George'}, {name: 'Abe'}]dispatchmyModelmyModel // => [{name: 'George'}, {name: 'Ben'}}, {name: 'Abe'}]
dispatch.myModel.insertAll(payload: { where, payload })
payload.where: function(elmt: any, index: number): boolean
payload.payload: any[]
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
.
myModel // => [1, 2, 3]dispatchmyModelmyModel // => [1, 4, 5, 6, 2, 3]
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.
storemyModel // => ['henlo', 'world']dispatchmyModelstoremyModel // => ['HENLO', 'WORLD']
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.
storemyModel // => [1, 2, 3, 4, 5]dispatchmyModelstoremyModel // => [1, 2, 3, 4]dispatchmyModelstoremyModel // => [1, 2]
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.
storemyModel // => [1, 2, 3]dispatchmyModelstoremyModel // => [1, 2, 3, 4]
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 = myModel // => [{name: 'George'}, {name: 'Abe'}]dispatchmyModelmyModel // => [{name: 'Abe'}]
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:
myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModel // => ["won't", 'you', 'my', 'neighbor?']
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:
myModel // => ["won't", 'you', 'be', 'my', 'neighbor?'] dispatchmyModelmyModel // => ["won't", 'you', 'neighbor?']
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:
myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModel // => ["won't", 'you', 'find', 'my', 'neighbor?']
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:
myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModel // => ['you', 'be', 'my', 'neighbor?']dispatchmyModelmyModel // => ['my', 'neighbor?']
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:
myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']dispatchmyModelmyModel // => ['Howdy!', "won't", 'you', 'be', 'my', 'neighbor?']