rematch-default-reducers
v1.2.0
Published
Generate reducers for @rematch/core based on your models' intial state
Downloads
21
Maintainers
Readme
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 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
tometa?: {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.
The rootState
Model and Reducers
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.
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])
payload
(type depends on property's intial state) - the value with which to update the the propertymeta?: {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} = 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
/* {
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
Extra Reducers for Array
Type Properties
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:
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:
store.getState().myModel.things // => ['world']
dispatch.myModel.concatThings(['hello'])
store.getState().myModel.things // => ['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:
store.getState().myModel.things // => ['world']
dispatch.myModel.concatThingsTo(['henlo'])
store.getState().myModel.things // => ['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:
store.getState().myModel.things // ['blah', 4, 'blah]
dispatch.myModel.filterThings({where: (el, i) => typeof el === 'string'})
store.getState().myModel.things // ['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:
getState().myModel.things // => [{name: 'George'}, {name: 'Abe'}]
dispatch.myModel.insertThing({where: (el, i) => i === 1, {name: 'Ben'}})
getState().myModel.things // => [{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:
getState().myModel.things // => [1, 2, 3]
dispatch.myModel.insertThings({where: (el, i) => i === 1, [4, 5, 6]})
getState().myModel.things // => [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:
store.getState().myModel.things // => ['henlo', 'world']
dispatch.myModel.mapThings((el, idx) => el.toUpperCase())
store.getState().myModel.things // => ['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:
store.getState().myModel.things // => [1, 2, 3, 4, 5]
dispatch.myModel.popThings()
store.getState().myModel.things // => [1, 2, 3, 4]
dispatch.myModel.popThings(2)
store.getState().myModel.things // => [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:
store.getState().myModel.things // => [1, 2, 3]
dispatch.myModel.pushThing(4)
store.getState().myModel.things // => [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] = getState().myModel.things // => [{name: 'George'}, {name: 'Abe'}]
dispatch.myModel.removeThing(george)
getState().myModel.things // => [{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:
getState().myModel.things // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.removeThing({where: (el, idx) => el.length < 3})
getState().myModel.things // => ["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:
getState().myModel.things // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.removeThings({where: (el, idx) => el.length < 3})
getState().myModel.things // => ["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:
getState().myModel.things // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.replaceThing({where: (el, idx) => el.length < 3, 'find'})
getState().myModel.things // => ["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:
getState().myModel.things // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.shiftThings()
getState().myModel.things // => ['you', 'be', 'my', 'neighbor?']
dispatch.myModel.shiftThings(2)
getState().myModel.things // => ['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:
getState().myModel.things // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.unshiftThing('Howdy!')
getState().myModel.things // => ['Howdy!', "won't", 'you', 'be', 'my', 'neighbor?']
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[])
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
store.getState().myModel // => ['world']
dispatch.myModel.concat(['hello'])
store.getState().myModel // => ['world', 'hello']
dispatch.myModel.concatTo(payload: any[])
payload: any[]
Sets myModel.state
to the result of concatenating myModel.state
to the end
of payload
store.getState().myModel // => ['world']
dispatch.myModel.concatTo(['henlo'])
store.getState().myModel // => ['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:
store.getState().myModel // ['blah', 4, 'blah]
dispatch.myModel.filter({where: (el, i) => typeof el === 'string'})
store.getState().myModel // ['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.
getState().myModel // => [{name: 'George'}, {name: 'Abe'}]
dispatch.myModel.insert({where: (el, i) => i === 1, {name: 'Ben'}})
getState().myModel // => [{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
.
getState().myModel // => [1, 2, 3]
dispatch.myModel.insertAll({where: (el, i) => i === 1, [4, 5, 6]})
getState().myModel // => [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.
store.getState().myModel // => ['henlo', 'world']
dispatch.myModel.map((el, idx) => el.toUpperCase())
store.getState().myModel // => ['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.
store.getState().myModel // => [1, 2, 3, 4, 5]
dispatch.myModel.pop()
store.getState().myModel // => [1, 2, 3, 4]
dispatch.myModel.pop(2)
store.getState().myModel // => [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.
store.getState().myModel // => [1, 2, 3]
dispatch.myModel.pushThing(4)
store.getState().myModel // => [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] = getState().myModel // => [{name: 'George'}, {name: 'Abe'}]
dispatch.myModel.remove(george)
getState().myModel // => [{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:
getState().myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.remove({where: (el, idx) => el.length < 3})
getState().myModel // => ["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:
getState()
.myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']
.dispatch.myModel.remove({where: (el, idx) => el.length < 3})
getState().myModel // => ["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:
getState().myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.replace({where: (el, idx) => el.length < 3, 'find'})
getState().myModel // => ["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:
getState().myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.shift()
getState().myModel // => ['you', 'be', 'my', 'neighbor?']
dispatch.myModel.shift(2)
getState().myModel // => ['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:
getState().myModel // => ["won't", 'you', 'be', 'my', 'neighbor?']
dispatch.myModel.unshift('Howdy!')
getState().myModel // => ['Howdy!', "won't", 'you', 'be', 'my', 'neighbor?']