npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rematch-default-reducers

v1.2.0

Published

Generate reducers for @rematch/core based on your models' intial state

Downloads

21

Readme

Rematch Default Reducers

npm codecov Build Status Commitizen friendly semantic-release

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 payloads 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 action

    • typeCheck?: boolean (optional) - enables/disables type-checking that prevents set from altering the type/interface of the model. Default: true.

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])

  • 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 action

    • typeCheck?: boolean (optional) - enables/disables type-checking that prevents set${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 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
/* {
      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:

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[])

  • 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?']