@rematch/select
Advanced tools
Comparing version 1.0.2 to 2.0.0
@@ -5,44 +5,116 @@ 'use strict'; | ||
var select = {}; | ||
function getSelect() { | ||
var reselect = require('reselect'); | ||
var makeSelect = function () { | ||
/** | ||
* Maps models to structured selector | ||
* @param mapSelectToStructure function that gets passed `selectors` and returns an object | ||
* @param structuredSelectorCreator=createStructuredSelector if you need to provide your own implementation | ||
* | ||
* @return the result of calling `structuredSelectorCreator` with the new selectors | ||
*/ | ||
function select(mapSelectToStructure, structuredSelectorCreator) { | ||
if (structuredSelectorCreator === void 0) { structuredSelectorCreator = reselect.createStructuredSelector; } | ||
var func = function (state, props) { | ||
func = structuredSelectorCreator(mapSelectToStructure(select)); | ||
return func(state, props); | ||
}; | ||
return function (state, props) { return func(state, props); }; | ||
} | ||
return select; | ||
} | ||
var selectPlugin = function (_a) { | ||
var _b = (_a === void 0 ? {} : _a).sliceState, sliceState = _b === void 0 ? function (rootState, model) { return rootState[model.name]; } : _b; | ||
return ({ | ||
exposed: { select: select }, | ||
onInit: function () { | ||
this.validate([ | ||
[ | ||
typeof sliceState !== 'function', | ||
"The selectPlugin's getState config must be a function. Instead got type " + typeof sliceState + ".", | ||
], | ||
]); | ||
}; | ||
var makeFactoryGroup = function () { | ||
var ready = false; | ||
var factories = new Set(); | ||
return { | ||
add: function (added) { | ||
if (!ready) { | ||
added.forEach(function (factory) { return factories.add(factory); }); | ||
} | ||
else { | ||
added.forEach(function (factory) { return factory(); }); | ||
} | ||
}, | ||
finish: function (factory) { | ||
factories.delete(factory); | ||
}, | ||
startBuilding: function () { | ||
ready = true; | ||
factories.forEach(function (factory) { return factory(); }); | ||
}, | ||
}; | ||
}; | ||
var validateConfig = function (config) { | ||
if (config.sliceState && typeof config.sliceState !== 'function') { | ||
throw new Error('select plugin config sliceState must be a function'); | ||
} | ||
if (config.selectorCreator && typeof config.selectorCreator !== 'function') { | ||
throw new Error('select plugin config selectorCreator must be a function'); | ||
} | ||
}; | ||
var createSelectPlugin = function (config) { | ||
if (config === void 0) { config = {}; } | ||
validateConfig(config); | ||
var sliceState = config.sliceState || (function (state, model) { return state[model.name]; }); | ||
var selectorCreator = config.selectorCreator || reselect.createSelector; | ||
var slice = function (model) { return function (stateOrNext) { | ||
if (typeof stateOrNext === 'function') { | ||
return selectorCreator(function (state) { return sliceState(state, model); }, stateOrNext); | ||
} | ||
return sliceState(stateOrNext, model); | ||
}; }; | ||
var hasProps = function (inner) { | ||
return function (models) { | ||
var _this = this; | ||
return selectorCreator(function (props) { return props; }, function (props) { return inner.call(_this, models, props); }); | ||
}; | ||
}; | ||
var factoryGroup = makeFactoryGroup(); | ||
var select = makeSelect(); | ||
return { | ||
exposed: { | ||
select: select, | ||
sliceState: sliceState, | ||
selectorCreator: selectorCreator, | ||
}, | ||
onModel: function (model) { | ||
var _this = this; | ||
select[model.name] = {}; | ||
Object.keys(model.selectors || {}).forEach(function (selectorName) { | ||
var selectorFactories = typeof model.selectors === 'function' | ||
? model.selectors(slice(model), selectorCreator, hasProps) | ||
: model.selectors; | ||
factoryGroup.add(Object.keys(selectorFactories || {}).map(function (selectorName) { | ||
_this.validate([ | ||
[ | ||
typeof model.selectors[selectorName] !== 'function', | ||
typeof selectorFactories[selectorName] !== 'function', | ||
"Selector (" + model.name + "/" + selectorName + ") must be a function", | ||
], | ||
]); | ||
select[model.name][selectorName] = function (state) { | ||
var _a; | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
return (_a = model.selectors)[selectorName].apply(_a, [sliceState(state, model)].concat(args)); | ||
var factory = function () { | ||
factoryGroup.finish(factory); | ||
delete select[model.name][selectorName]; | ||
return (select[model.name][selectorName] = selectorFactories[selectorName].call(select[model.name], select)); | ||
}; | ||
}); | ||
// Define a getter for early constructing | ||
Object.defineProperty(select[model.name], selectorName, { | ||
configurable: true, | ||
get: function () { | ||
return factory(); | ||
}, | ||
}); | ||
return factory; | ||
})); | ||
}, | ||
}); | ||
onStoreCreated: function (store) { | ||
factoryGroup.startBuilding(); | ||
return { | ||
select: select, | ||
}; | ||
}, | ||
}; | ||
}; | ||
exports.select = select; | ||
exports.getSelect = getSelect; | ||
exports.default = selectPlugin; | ||
exports.createSelector = reselect.createSelector; | ||
exports.createStructuredSelector = reselect.createStructuredSelector; | ||
exports.default = createSelectPlugin; | ||
//# sourceMappingURL=rematch-select.cjs.js.map |
@@ -1,42 +0,114 @@ | ||
var select = {}; | ||
function getSelect() { | ||
import { createSelector, createStructuredSelector } from 'reselect'; | ||
export { createSelector, createStructuredSelector } from 'reselect'; | ||
var makeSelect = function () { | ||
/** | ||
* Maps models to structured selector | ||
* @param mapSelectToStructure function that gets passed `selectors` and returns an object | ||
* @param structuredSelectorCreator=createStructuredSelector if you need to provide your own implementation | ||
* | ||
* @return the result of calling `structuredSelectorCreator` with the new selectors | ||
*/ | ||
function select(mapSelectToStructure, structuredSelectorCreator) { | ||
if (structuredSelectorCreator === void 0) { structuredSelectorCreator = createStructuredSelector; } | ||
var func = function (state, props) { | ||
func = structuredSelectorCreator(mapSelectToStructure(select)); | ||
return func(state, props); | ||
}; | ||
return function (state, props) { return func(state, props); }; | ||
} | ||
return select; | ||
} | ||
var selectPlugin = function (_a) { | ||
var _b = (_a === void 0 ? {} : _a).sliceState, sliceState = _b === void 0 ? function (rootState, model) { return rootState[model.name]; } : _b; | ||
return ({ | ||
exposed: { select: select }, | ||
onInit: function () { | ||
this.validate([ | ||
[ | ||
typeof sliceState !== 'function', | ||
"The selectPlugin's getState config must be a function. Instead got type " + typeof sliceState + ".", | ||
], | ||
]); | ||
}; | ||
var makeFactoryGroup = function () { | ||
var ready = false; | ||
var factories = new Set(); | ||
return { | ||
add: function (added) { | ||
if (!ready) { | ||
added.forEach(function (factory) { return factories.add(factory); }); | ||
} | ||
else { | ||
added.forEach(function (factory) { return factory(); }); | ||
} | ||
}, | ||
finish: function (factory) { | ||
factories.delete(factory); | ||
}, | ||
startBuilding: function () { | ||
ready = true; | ||
factories.forEach(function (factory) { return factory(); }); | ||
}, | ||
}; | ||
}; | ||
var validateConfig = function (config) { | ||
if (config.sliceState && typeof config.sliceState !== 'function') { | ||
throw new Error('select plugin config sliceState must be a function'); | ||
} | ||
if (config.selectorCreator && typeof config.selectorCreator !== 'function') { | ||
throw new Error('select plugin config selectorCreator must be a function'); | ||
} | ||
}; | ||
var createSelectPlugin = function (config) { | ||
if (config === void 0) { config = {}; } | ||
validateConfig(config); | ||
var sliceState = config.sliceState || (function (state, model) { return state[model.name]; }); | ||
var selectorCreator = config.selectorCreator || createSelector; | ||
var slice = function (model) { return function (stateOrNext) { | ||
if (typeof stateOrNext === 'function') { | ||
return selectorCreator(function (state) { return sliceState(state, model); }, stateOrNext); | ||
} | ||
return sliceState(stateOrNext, model); | ||
}; }; | ||
var hasProps = function (inner) { | ||
return function (models) { | ||
var _this = this; | ||
return selectorCreator(function (props) { return props; }, function (props) { return inner.call(_this, models, props); }); | ||
}; | ||
}; | ||
var factoryGroup = makeFactoryGroup(); | ||
var select = makeSelect(); | ||
return { | ||
exposed: { | ||
select: select, | ||
sliceState: sliceState, | ||
selectorCreator: selectorCreator, | ||
}, | ||
onModel: function (model) { | ||
var _this = this; | ||
select[model.name] = {}; | ||
Object.keys(model.selectors || {}).forEach(function (selectorName) { | ||
var selectorFactories = typeof model.selectors === 'function' | ||
? model.selectors(slice(model), selectorCreator, hasProps) | ||
: model.selectors; | ||
factoryGroup.add(Object.keys(selectorFactories || {}).map(function (selectorName) { | ||
_this.validate([ | ||
[ | ||
typeof model.selectors[selectorName] !== 'function', | ||
typeof selectorFactories[selectorName] !== 'function', | ||
"Selector (" + model.name + "/" + selectorName + ") must be a function", | ||
], | ||
]); | ||
select[model.name][selectorName] = function (state) { | ||
var _a; | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
return (_a = model.selectors)[selectorName].apply(_a, [sliceState(state, model)].concat(args)); | ||
var factory = function () { | ||
factoryGroup.finish(factory); | ||
delete select[model.name][selectorName]; | ||
return (select[model.name][selectorName] = selectorFactories[selectorName].call(select[model.name], select)); | ||
}; | ||
}); | ||
// Define a getter for early constructing | ||
Object.defineProperty(select[model.name], selectorName, { | ||
configurable: true, | ||
get: function () { | ||
return factory(); | ||
}, | ||
}); | ||
return factory; | ||
})); | ||
}, | ||
}); | ||
onStoreCreated: function (store) { | ||
factoryGroup.startBuilding(); | ||
return { | ||
select: select, | ||
}; | ||
}, | ||
}; | ||
}; | ||
export default selectPlugin; | ||
export { select, getSelect }; | ||
export default createSelectPlugin; | ||
//# sourceMappingURL=rematch-select.esm.js.map |
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(factory((global.RematchSelect = {}))); | ||
}(this, (function (exports) { 'use strict'; | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('reselect')) : | ||
typeof define === 'function' && define.amd ? define(['exports', 'reselect'], factory) : | ||
(factory((global.RematchSelect = {}),global.reselect)); | ||
}(this, (function (exports,reselect) { 'use strict'; | ||
var select = {}; | ||
function getSelect() { | ||
var makeSelect = function () { | ||
/** | ||
* Maps models to structured selector | ||
* @param mapSelectToStructure function that gets passed `selectors` and returns an object | ||
* @param structuredSelectorCreator=createStructuredSelector if you need to provide your own implementation | ||
* | ||
* @return the result of calling `structuredSelectorCreator` with the new selectors | ||
*/ | ||
function select(mapSelectToStructure, structuredSelectorCreator) { | ||
if (structuredSelectorCreator === void 0) { structuredSelectorCreator = reselect.createStructuredSelector; } | ||
var func = function (state, props) { | ||
func = structuredSelectorCreator(mapSelectToStructure(select)); | ||
return func(state, props); | ||
}; | ||
return function (state, props) { return func(state, props); }; | ||
} | ||
return select; | ||
} | ||
var selectPlugin = function (_a) { | ||
var _b = (_a === void 0 ? {} : _a).sliceState, sliceState = _b === void 0 ? function (rootState, model) { return rootState[model.name]; } : _b; | ||
return ({ | ||
exposed: { select: select }, | ||
onInit: function () { | ||
this.validate([ | ||
[ | ||
typeof sliceState !== 'function', | ||
"The selectPlugin's getState config must be a function. Instead got type " + typeof sliceState + ".", | ||
], | ||
]); | ||
}; | ||
var makeFactoryGroup = function () { | ||
var ready = false; | ||
var factories = new Set(); | ||
return { | ||
add: function (added) { | ||
if (!ready) { | ||
added.forEach(function (factory) { return factories.add(factory); }); | ||
} | ||
else { | ||
added.forEach(function (factory) { return factory(); }); | ||
} | ||
}, | ||
finish: function (factory) { | ||
factories.delete(factory); | ||
}, | ||
startBuilding: function () { | ||
ready = true; | ||
factories.forEach(function (factory) { return factory(); }); | ||
}, | ||
}; | ||
}; | ||
var validateConfig = function (config) { | ||
if (config.sliceState && typeof config.sliceState !== 'function') { | ||
throw new Error('select plugin config sliceState must be a function'); | ||
} | ||
if (config.selectorCreator && typeof config.selectorCreator !== 'function') { | ||
throw new Error('select plugin config selectorCreator must be a function'); | ||
} | ||
}; | ||
var createSelectPlugin = function (config) { | ||
if (config === void 0) { config = {}; } | ||
validateConfig(config); | ||
var sliceState = config.sliceState || (function (state, model) { return state[model.name]; }); | ||
var selectorCreator = config.selectorCreator || reselect.createSelector; | ||
var slice = function (model) { return function (stateOrNext) { | ||
if (typeof stateOrNext === 'function') { | ||
return selectorCreator(function (state) { return sliceState(state, model); }, stateOrNext); | ||
} | ||
return sliceState(stateOrNext, model); | ||
}; }; | ||
var hasProps = function (inner) { | ||
return function (models) { | ||
var _this = this; | ||
return selectorCreator(function (props) { return props; }, function (props) { return inner.call(_this, models, props); }); | ||
}; | ||
}; | ||
var factoryGroup = makeFactoryGroup(); | ||
var select = makeSelect(); | ||
return { | ||
exposed: { | ||
select: select, | ||
sliceState: sliceState, | ||
selectorCreator: selectorCreator, | ||
}, | ||
onModel: function (model) { | ||
var _this = this; | ||
select[model.name] = {}; | ||
Object.keys(model.selectors || {}).forEach(function (selectorName) { | ||
var selectorFactories = typeof model.selectors === 'function' | ||
? model.selectors(slice(model), selectorCreator, hasProps) | ||
: model.selectors; | ||
factoryGroup.add(Object.keys(selectorFactories || {}).map(function (selectorName) { | ||
_this.validate([ | ||
[ | ||
typeof model.selectors[selectorName] !== 'function', | ||
typeof selectorFactories[selectorName] !== 'function', | ||
"Selector (" + model.name + "/" + selectorName + ") must be a function", | ||
], | ||
]); | ||
select[model.name][selectorName] = function (state) { | ||
var _a; | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
return (_a = model.selectors)[selectorName].apply(_a, [sliceState(state, model)].concat(args)); | ||
var factory = function () { | ||
factoryGroup.finish(factory); | ||
delete select[model.name][selectorName]; | ||
return (select[model.name][selectorName] = selectorFactories[selectorName].call(select[model.name], select)); | ||
}; | ||
}); | ||
// Define a getter for early constructing | ||
Object.defineProperty(select[model.name], selectorName, { | ||
configurable: true, | ||
get: function () { | ||
return factory(); | ||
}, | ||
}); | ||
return factory; | ||
})); | ||
}, | ||
}); | ||
onStoreCreated: function (store) { | ||
factoryGroup.startBuilding(); | ||
return { | ||
select: select, | ||
}; | ||
}, | ||
}; | ||
}; | ||
exports.select = select; | ||
exports.getSelect = getSelect; | ||
exports.default = selectPlugin; | ||
exports.createSelector = reselect.createSelector; | ||
exports.createStructuredSelector = reselect.createStructuredSelector; | ||
exports.default = createSelectPlugin; | ||
@@ -50,0 +120,0 @@ Object.defineProperty(exports, '__esModule', { value: true }); |
{ | ||
"name": "@rematch/select", | ||
"version": "1.0.2", | ||
"version": "2.0.0", | ||
"description": "Selectors plugin for Rematch", | ||
@@ -18,7 +18,7 @@ "keywords": [ | ||
"dist", | ||
"index.d.ts" | ||
"src/typings.d.ts" | ||
], | ||
"main": "dist/rematch-select.cjs.js", | ||
"module": "dist/rematch-select.esm.js", | ||
"types": "index.d.ts", | ||
"types": "src/typings.d.ts", | ||
"repository": { | ||
@@ -36,4 +36,4 @@ "type": "git", | ||
"devDependencies": { | ||
"@rematch/core": "^1.0.0-alpha.8", | ||
"rollup": "^0.60.1", | ||
"@rematch/core": "^1.0.0-beta.5", | ||
"rollup": "^0.62.0", | ||
"rollup-plugin-commonjs": "^9.1.3", | ||
@@ -47,5 +47,9 @@ "rollup-plugin-replace": "^2.0.0", | ||
"peerDependencies": { | ||
"@rematch/core": ">=1.0.0-alpha.8" | ||
"@rematch/core": "1.0.0-beta.5" | ||
}, | ||
"dependencies": { | ||
"reselect": "^3.0.1" | ||
}, | ||
"authors": [ | ||
"Sam Richard <sam.richard@gmail.com> (https://github.com/d3dc)", | ||
"Blair Bodnar <blairbodnar@gmail.com> (https://github.com/blairbodnar)", | ||
@@ -52,0 +56,0 @@ "Tom Aranda <tga@arandacybersolutions.com> (https://github.com/taranda)" |
304
README.md
# Rematch Select | ||
Selectors plugin for Rematch. | ||
Selectors plugin for Rematch. Wires your store models with dependencies and collects their selectors. Uses [reselect](https://github.com/reduxjs/reselect) by default. | ||
> This is the documentation for @rematch/select 2.0. For older versions see [the legacy docs](https://github.com/rematch/rematch/blob/v1/plugins/select/README.md) | ||
- [Getting Started](#getting-started) | ||
- [Building Selectors](#building-selectors) | ||
- [API Docs](#api-docs) | ||
- [Recipes](#recipes) | ||
## Getting Started | ||
### Install | ||
@@ -11,12 +22,10 @@ | ||
> For @rematch/core@0.x use @rematch/select@0.3.0 | ||
### Setup | ||
```js | ||
import selectPlugin, { getSelect } from '@rematch/select' | ||
import selectPlugin from '@rematch/select' | ||
import { init } from '@rematch/core' | ||
export const select = getSelect(); | ||
init({ | ||
@@ -27,8 +36,16 @@ plugins: [selectPlugin()] | ||
### selectors | ||
`selectors: { [string]: (state, ...params) => any }` | ||
Selectors are read-only snippets of state. | ||
## Building Selectors | ||
### Basics | ||
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`: | ||
```js | ||
@@ -42,4 +59,5 @@ { | ||
selectors: { | ||
total(state) { | ||
return state.reduce((a, b) => a + (b.price * b.amount), 0) | ||
total() { | ||
return (rootState, props) => | ||
rootState.cart.reduce((a, b) => a + (b.price * b.amount), 0) | ||
} | ||
@@ -50,26 +68,87 @@ } | ||
> note: By default, the selector state does not refer to the complete state, only the state within the model. | ||
To change this behavior, use the sliceState configuration option described below. | ||
Selectors can be called anywhere within your app. | ||
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: | ||
```js | ||
import { select } from '@rematch/select' | ||
{ | ||
name: 'cart', | ||
selectors: (slice, createSelector, hasProps) => ({ | ||
... | ||
}) | ||
} | ||
``` | ||
const store = init({ ... }) | ||
select.cart.total(store.getState()) | ||
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 | ||
```js | ||
total () { | ||
return slice(cart => | ||
cart.reduce((a, b) => a + (b.price * b.amount), 0) | ||
) | ||
} | ||
``` | ||
Selectors can also be used with memoization libraries like [reselect](https://github.com/reactjs/reselect). | ||
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. | ||
```js | ||
import { createSelector } from 'reselect' | ||
total () { | ||
return createSelector( | ||
slice, | ||
(state, props) => props.shipping, | ||
(cart, shipping) => cart.reduce((a, b) => a + (b.price * b.amount), shipping) | ||
) | ||
} | ||
``` | ||
{ | ||
selectors: { | ||
total: createSelector( | ||
state => state, | ||
state => state.reduce((a, b) => a + (b.price * b.amount), 0) | ||
) | ||
### Combining selectors | ||
`@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 name `models` internally. | ||
> If your factory is a `function`, you can use `this` as a shortcut to the current model's selectors. | ||
```js | ||
poorSortByHot (models) { | ||
return createSelector( | ||
this.cart, | ||
models.popularity.pastDay, | ||
(cart, hot) => cart.sort((a, b) => hot[a.product] > hot[b.product]) | ||
) | ||
} | ||
``` | ||
#### Deriving state | ||
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: | ||
```js | ||
reducers: { | ||
'selectedGroup/change' (state, id) { | ||
return { | ||
...state, | ||
list: id | ||
? state.unfilteredList.filter.(p => p.group === id) | ||
: state.unfilteredList | ||
} | ||
} | ||
@@ -79,30 +158,176 @@ } | ||
### Configuration Options | ||
The `selectorPlugin()` method will accept a configuration object with the following property. | ||
#### sliceState: | ||
### Selector arguments | ||
As you may have noticed, a selector's dependencies can receive `props`: | ||
```js | ||
(state, props) => props.shipping | ||
``` | ||
[Be careful when passing `props` to a selector - passing different props could reset the cache! ](https://github.com/reduxjs/reselect/blob/master/README.md#sharing-selectors-with-props-across-multiple-component-instances) | ||
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](#re-reselect) | ||
> `hasProps` is a "higher-order selector factory" - it creates factories that can be used in other factories | ||
```js | ||
expensiveFilter: hasProps(function (models, lowerLimit) { | ||
return slice(items => items.filter(item => item.price > lowerLimit)) | ||
}), | ||
wouldGetFreeShipping () { | ||
return this.expensiveFilter(20.00) | ||
}, | ||
``` | ||
## Using Selectors in your app | ||
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](https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector). | ||
```js | ||
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. | ||
```js | ||
const store = init({ ... }) | ||
store.select.cart.expensiveFilter(50.00)(store.getState()) | ||
``` | ||
## External Selectors | ||
`@rematch/select` supports using your own `selectorCreator` directly in the models. | ||
> Configuring this store-wide in `options` makes testing easier. | ||
```js | ||
isHypeBeast (models) { | ||
return customCreateSelector( | ||
slice, | ||
state => this.sortByHot(state)[0], | ||
(state, hottest) => hottest.price > 100.00 | ||
) | ||
} | ||
``` | ||
## API Docs | ||
```js | ||
import selectPlugin from '@rematch/select' | ||
``` | ||
- [selectPlugin](#selectplugin) | ||
- [selectorCreator](#configselectorcreator) | ||
- [sliceState](#configslicestate) | ||
- [store.select](#storeselect) | ||
### selectPlugin | ||
`selectPlugin(config?: any)` | ||
Create the plugin. | ||
```js | ||
init({ | ||
plugins: [ selectPlugin(config) ] | ||
}) | ||
``` | ||
#### config.selectorCreator: | ||
`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](#Recipes) for other uses. | ||
#### config.sliceState: | ||
`sliceState: (rootState, model) => any` | ||
An option that allows the user to specify how the state will be sliced before being passed to the selectors. | ||
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. | ||
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. | ||
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. | ||
##### Example 1 - Use the root state in selectors as opposed to a slice: | ||
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.](#immutablejs) | ||
This can easily be accomplished by returning the `rootState` in the `getState` config: | ||
### store.select: | ||
`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](https://github.com/reduxjs/reselect#createstructuredselectorinputselectors-selectorcreator--createselector) 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. | ||
## Recipes | ||
### Re-reselect | ||
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](https://github.com/toomuchdesign/re-reselect) | ||
```js | ||
const select = selectorsPlugin({ sliceState: rootState => rootState }); | ||
import { createCachedSelector } from 're-reselect' | ||
selectorPlugin({ | ||
selectorCreator: createCachedSelector | ||
}) | ||
``` | ||
Now the `state` parameter that is passed to all of the selectors will be the root state. | ||
```js | ||
total () { | ||
const mapProps = (state, props) => props.id | ||
return createSelector( | ||
slice, | ||
mapProps, | ||
(cart, id) => cart.reduce((a, b) => a + (b.price * b.amount), 0) | ||
)(mapProps) | ||
} | ||
``` | ||
##### Example 2 - Use an Immutable JS object as the store | ||
### Immutable.js | ||
**Use an Immutable JS object as the store** | ||
If you are using an [Immutable.js](https://facebook.github.io/immutable-js/) Map as your store, you will need to slice | ||
@@ -112,3 +337,6 @@ the state using [Map.get()](http://facebook.github.io/immutable-js/docs/#/Map/get): | ||
```js | ||
const select = selectorsPlugin({ sliceState: (rootState, model) => rootState.get(model.name) }) | ||
selectorsPlugin({ | ||
sliceState: (rootState, model) => | ||
rootState.get(model.name) | ||
}) | ||
``` | ||
@@ -115,0 +343,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
49102
405
340
2
9
+ Addedreselect@^3.0.1
+ Added@rematch/core@1.0.0-beta.5(transitive)
+ Addedjs-tokens@4.0.0(transitive)
+ Addedloose-envify@1.4.0(transitive)
+ Addedredux@4.0.0(transitive)
+ Addedreselect@3.0.1(transitive)
+ Addedsymbol-observable@1.2.0(transitive)
- Removed@rematch/core@2.2.0(transitive)
- Removedredux@5.0.1(transitive)