Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@rematch/select
Advanced tools
Selectors plugin for Rematch. Wires your store models with dependencies and collects their selectors. Uses reselect by default.
This is the documentation for @rematch/select 2.0. For older versions see the legacy docs
npm install @rematch/select
import selectPlugin from '@rematch/select'
import { init } from '@rematch/core'
init({
plugins: [selectPlugin()]
})
Selectors are read-only snippets of state. They can be combined across models for powerful data retrieval.
Since selectors can depend on each other, they need to be created by factory functions. When the store is fully ready, each factory will be evaluated once.
{ selectors: { (models) => selector } }
The most basic selector is a function that receives rootState
:
{
name: 'cart',
state: [{
price: 42.00,
amount: 3,
}],
selectors: {
total() {
return (rootState, props) =>
rootState.cart.reduce((a, b) => a + (b.price * b.amount), 0)
}
}
}
If we hook total up to something like react-redux
's connect
, it will be recomputed any time our rootState
changes. To avoid this, @rematch/select
includes reselect
.
Models gain access to the reselect
api through dependency injection:
{
name: 'cart',
selectors: (slice, createSelector, hasProps) => ({
...
})
}
Our selector, total
, depends only on the cart model's state and doesn't need to update when the rest of the store updates. slice
creates a basic selector
memoized by the model's slice of state.
slice
is private to the current model.
To make a part of your model's state public, add a selector
total () {
return slice(cart =>
cart.reduce((a, b) => a + (b.price * b.amount), 0)
)
}
If you want more control over what the dependencies of your selector are,
you can directly call the passed in createSelector
.
This will memoize the last function by the results of all the previous functions.
slice
can also be used as a selector directly, simply returning the model's slice of state.
total () {
return createSelector(
slice,
(state, props) => props.shipping,
(cart, shipping) => cart.reduce((a, b) => a + (b.price * b.amount), shipping)
)
}
@rematch/select
injects select
into each selector factory to allow it to depend on other models state.
It might be less redundant to give
select
the descriptive namemodels
internally.
If your factory is a
function
, you can usethis
as a shortcut to the current model's selectors.
poorSortByHot (models) {
return createSelector(
this.cart,
models.popularity.pastDay,
(cart, hot) => cart.sort((a, b) => hot[a.product] > hot[b.product])
)
}
Selectors are great for deriving state lazily. But, you should only depend on the selectors a model makes public - access to another model's slice
is not allowed.
Using listeners to eagerly keep track of the changes to another model might fit some applications better:
reducers: {
'selectedGroup/change' (state, id) {
return {
...state,
list: id
? state.unfilteredList.filter.(p => p.group === id)
: state.unfilteredList
}
}
}
As you may have noticed, a selector's dependencies can receive props
:
(state, props) => props.shipping
Be careful when passing props
to a selector - passing different props could reset the cache!
In some situations, configurable selectors may be better off with isolated caches. To opt-in to this behavior and create a new cache for every configuration, @rematch/select
injects hasProps
.
hasProps
wraps a factory so that it receives models
and props
. For complex calculations or dashboards a recipe may be better
hasProps
is a "higher-order selector factory" - it creates factories that can be used in other factories
expensiveFilter: hasProps(function (models, lowerLimit) {
return slice(items => items.filter(item => item.price > lowerLimit))
}),
wouldGetFreeShipping () {
return this.expensiveFilter(20.00)
},
Most apps will consume selectors through connect
. For this use case, the store's select
can be called as a function to create a selector you can pass directly to connect, or call yourself. As a function, select
ensures your component re-renders only when its data actually changes.
Under the hood,
select
creates a structuredSelector.
import { connect } from 'react-redux'
import { select } = './store'
connect(select(models => {
total: models.cart.total,
eligibleItems: models.cart.wouldGetFreeShipping
}))(...)
Selectors can also be called directly anywhere within your app.
const store = init({ ... })
store.select.cart.expensiveFilter(50.00)(store.getState())
@rematch/select
supports using your own selectorCreator
directly in the models.
Configuring this store-wide in
options
makes testing easier.
isHypeBeast (models) {
return customCreateSelector(
slice,
state => this.sortByHot(state)[0],
(state, hottest) => hottest.price > 100.00
)
}
import selectPlugin from '@rematch/select'
selectPlugin(config?: any)
Create the plugin.
init({
plugins: [ selectPlugin(config) ]
})
selectorCreator: (...deps, resultFunc) => any
An option that allows the user to specify a different function to be used when creating selectors.
The default is createSelector
from reselect
. See recipes for other uses.
sliceState: (rootState, model) => any
An option that allows the user to specify how the state will be sliced in the slice
function.
The function takes the rootState
as the first parameter and the model
corresponding to the selector as the
second parameter. It should return the desired state slice required by the selector.
The default is to return the slice of the state that corresponds to the owning model's name, but this assumes the store is a Javascript object.
Most of the time the default should be used, however, there are some cases where one may want to specify the sliceState
function. See the immutable.js recipe for an example.
select( mapSelectToStructure: (select) => object)
Using select
as a function lets you bind your view as a selector itself - preventing un-needed re-renders.
Creates a structuredSelector using the selectors you return in mapSelectToStructure
.
select: { [modelName]: { [selectorName]: (state) => any } }
select
also contains all of the selectors from your store models. Selectors can be called anywhere and do not have to be called inside another selector.
When working on a dashboard or doing calculations with a lot of external values, you may find your selectors always re-run. This happens when your selector has props (such as a hasProps
factory) and then share your selectors between multiple components.
Selectors have a cache size of 1. Passing a different set of props will invalidate the cache. re-reselect exists to solve this by caching your selectors by props as well
import { createCachedSelector } from 're-reselect'
selectorPlugin({
selectorCreator: createCachedSelector
})
total () {
const mapProps = (state, props) => props.id
return createSelector(
slice,
mapProps,
(cart, id) => cart.reduce((a, b) => a + (b.price * b.amount), 0)
)(mapProps)
}
Use an Immutable JS object as the store If you are using an Immutable.js Map as your store, you will need to slice the state using Map.get():
selectorsPlugin({
sliceState: (rootState, model) =>
rootState.get(model.name)
})
Now you can use an Immutable.js Map as your store and access the appropriate slice of the state in each of your selectors.
FAQs
Selectors plugin for Rematch
The npm package @rematch/select receives a total of 7,235 weekly downloads. As such, @rematch/select popularity was classified as popular.
We found that @rematch/select demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.