Product
Introducing Enhanced Alert Actions and Triage Functionality
Socket now supports four distinct alert actions instead of the previous two, and alert triaging allows users to override the actions taken for all individual alerts.
re-reselect
Advanced tools
Changelog
3.0.0
dependencies
propertyReadme
re-reselect
is a lightweight wrapper around Reselect meant to enhance selectors with deeper memoization and cache management.
Switching between different arguments using standard reselect
selectors causes cache invalidation since default reselect
cache has a limit of one.
re-reselect
forwards different calls to different reselect
selectors stored in cache, so that computed/memoized values are retained.
re-reselect
selectors work as normal reselect
selectors but they are able to determine when creating a new selector or querying a cached one on the fly, depending on the supplied arguments.
Useful to:
import createCachedSelector from 're-reselect';
// Normal reselect routine: declare "inputSelectors" and "resultFunc"
const selectorA = state => state.a;
const selectorB = (state, itemName) => state.items[itemName];
const cachedSelector = createCachedSelector(
// inputSelectors
selectorA,
selectorB,
// resultFunc
(A, B) => expensiveComputation(A, B)
)(
// keySelector
// Instruct re-reselect to use "itemName" as cacheKey
(state, itemName) => itemName
);
// Use the cached selector like a normal selector:
const fooResult = cachedSelector(state, 'foo');
const barResult = cachedSelector(state, 'bar');
// 2 reselect selectors were created, called and cached behind the scenes
const fooResultAgain = cachedSelector(state, 'foo');
// fooResult === fooResultAgain
// Cache was not invalidated by calling "cachedSelector(state, 'bar')"
// "expensiveComputation" totally called twice
.getMatchingSelector(selectorArguments)
.removeMatchingSelector(selectorArguments)
.cache
.clearCache()
.dependencies
.resultFunc
.recomputations()
.resetRecomputations()
npm install reselect
npm install re-reselect
I found myself wrapping a library of data elaboration (quite heavy stuff) with reselect selectors (getPieceOfData
in the example).
On each store update, I had to repeatedly call the selector in order to retrieve all the pieces of data needed by my UI. Like this:
getPieceOfData(state, itemId, 'dataA');
getPieceOfData(state, itemId, 'dataB');
getPieceOfData(state, itemId, 'dataC');
What happens, here? getPieceOfData
selector cache is invalidated on each call because of the different 3rd 'dataX'
argument.
re-reselect
selectors keep a cache of reselect
selectors and store/retrieve them by cacheKey
.
cacheKey
is by default a string
or number
but can be anything depending on the chosen cache strategy (see cacheObject
option).
cacheKey
is the output of keySelector
, declared at selector initialization.
keySelector
is a custom function which:
state
, itemId
, 'dataX'
)cacheKey
.Note that the same reselect
selector instance stored in cache will be used for computing data for the same cacheKey
(1:1).
Back to the example, re-reselect
retrieves data by querying one of the cached selectors using the 3rd argument as cacheKey
, allowing cache invalidation only when state
or itemId
change (but not dataType
):
const getPieceOfData = createCachedSelector(
state => state,
(state, itemId) => itemId,
(state, itemId, dataType) => dataType,
(state, itemId, dataType) => expensiveComputation(state, itemId, dataType)
)(
(state, itemId, dataType) => dataType // Use dataType as cacheKey
);
createCachedSelector
returns a selector with the same signature as a normal reselect
selector.
But now, each time the selector is called, the following happens behind the scenes:
cacheKey
for current call by executing keySelector
reselect
selector stored under the given cacheKey
Re-reselect stays completely optional and consumes your installed reselect module (reselect
is declared as peer dependency).
Easy, but doesn't scale. See "join similar selectors" example.
makeGetPieceOfData
selector factory as explained in Reselect docsThe solution suggested in Reselect docs is fine, but it has a few downsides:
get
selectors and makeGet
selector factoriesmakeGetPieceOfData
selector factory into a memoizer function and call the returning memoized selectorThis is what re-reselect
actually does! :-) It's quite verbose (since has to be repeated for each selector), that's why re-reselect is here.
Given your reselect
selectors:
import {createSelector} from 'reselect';
export const getMyData = createSelector(
selectorA,
selectorB,
selectorC,
(A, B, C) => doSomethingWith(A, B, C)
);
...add keySelector
in the second function call:
import createCachedSelector from 're-reselect';
export const getMyData = createCachedSelector(
selectorA,
selectorB,
selectorC,
(A, B, C) => doSomethingWith(A, B, C)
)(
(state, arg1, arg2) => arg2 // Use arg2 as cacheKey
);
Voilà, getMyData
is ready for use!
const myData = getMyData(state, 'foo', 'bar');
cacheKey
is the return value of keySelector
.
keySelector
receives the same arguments of your inputSelectors
and (by default) must return a string
or number
.
A few good examples and a bonus:
// Basic usage: use a single argument as cacheKey
createCachedSelector(
// ...
)(
(state, arg1, arg2, arg3) => arg3
)
// Use multiple arguments and chain them into a string
createCachedSelector(
// ...
)(
(state, arg1, arg2, arg3) => `${arg1}:${arg3}`
)
// Extract properties from an object
createCachedSelector(
// ...
)(
(state, props) => `${props.a}:${props.b}`
)
Use a cacheObject
which provides that feature by supplying a cacheObject
option.
You can also write your own cache strategy!
This example shows how re-reselect
would solve the scenario described in reselect docs.
Just like a normal reselect selector!
re-reselect
selectors expose the same reselect
testing methods:
dependencies
resultFunc
recomputations
resetRecomputations
Read more about testing selectors on reselect
docs.
reselect
selectors stored in the cacheEach re-reselect selector exposes a getMatchingSelector
method which returns the underlying matching selector instance for the given arguments, instead of the result.
getMatchingSelector
expects the same arguments as a normal selector call BUT returns the instance of the cached selector itself.
Once you get a selector instance you can call its public methods.
import createCachedSelector from 're-reselect';
export const getMyData = createCachedSelector(
selectorA,
selectorB,
(A, B) => doSomethingWith(A, B)
)(
(state, arg1) => arg1 // cacheKey
);
// Call your selector
const myFooData = getMyData(state, 'foo');
const myBarData = getMyData(state, 'bar');
// Call getMatchingSelector method to retrieve underlying reselect selectors
// which generated "myFooData" and "myBarData" results
const myFooDataSelector = getMyData.getMatchingSelector(state, 'foo');
const myBarDataSelector = getMyData.getMatchingSelector(state, 'bar');
// Call reselect's selectors methods
myFooDataSelector.recomputations();
myFooDataSelector.resetRecomputations();
Re-reselect
exposes its cached selector creator as default export.
import reReselect from 're-reselect';
// or:
import createCachedSelector from 're-reselect';
Re-reselect accepts reselect's original createSelector
arguments and returns a new function which accepts 2 arguments:
keySelector
options { cacheObject, selectorCreator }
(optional)keySelector
is a custom function receiving the same arguments as your selectors (and inputSelectors
) and returning a cacheKey
.
cacheKey
is by default a string
or number
but can be anything depending on the chosen cache strategy (see cacheObject
option).
The keySelector
idea comes from Lodash's .memoize.
An optional custom strategy object to handle the caching behaviour.
Default cache: FlatObjectCache
.
re-reselect
provides 6 ready to use cache object constructors:
name | accepted cacheKey | type | storage |
---|---|---|---|
FlatObjectCache | number string | flat unlimited | JS object |
FifoObjectCache | number string | first in first out | JS object |
LruObjectCache | number string | least recently used | JS object |
FlatMapCache | any | flat unlimited | Map object |
FifoMapCache | any | first in first out | Map object |
LruMapCache | any | least recently used | Map object |
import createCachedSelector, {LruObjectCache, LruMapCache} from 're-reselect';
createCachedSelector(
// ...
)(
keySelector,
{
cacheObject: new LruObjectCache({cacheSize: 5}),
// or:
// cacheObject: new LruMapCache({cacheSize: 5}),
}
);
[*]ObjectCache strategy objects treat cacheKey
of type number
like strings, since they are used as arguments of JS objects.
[*]MapCache strategy objects needs a Map objects polyfill in order to use them on non-supporting browsers.
You can provide any kind of cache strategy. Declare a JS object adhering to the following interface:
interface ICacheObject {
set(key: any, selectorFn: any): void;
get(key: any): any;
remove(key: any): void;
clear(): void;
isValidCacheKey?(key: any): boolean; // optional
}
An optional function describing a custom selectors. By default it uses reselect
's createSelector
.
(Function): a reReselectInstance
selector ready to be used like a normal reselect selector.
Retrieve data for given arguments.
The followings are advanced methods and you won't need them for basic usage!
.getMatchingSelector(selectorArguments)
Retrieve the selector responding to the given arguments.
.removeMatchingSelector(selectorArguments)
Remove from the cache the selector responding to the given arguments.
.cache
Get cacheObject instance being used by the selector (for advanced caching operations like this).
.clearCache()
Clear whole reReselectInstance
cache.
.dependencies
Get an array containing the provided inputSelectors
. Refer to relevant discussion on Reselect repo.
.resultFunc
Get resultFunc
for easily testing composed selectors.
.recomputations()
Return the number of times the selector's result function has been recomputed.
.resetRecomputations()
Reset recomputations
count.
Thanks to you all (emoji key):
FAQs
Enhance Reselect selectors with deeper memoization and cache management
The npm package re-reselect receives a total of 136,066 weekly downloads. As such, re-reselect popularity was classified as popular.
We found that re-reselect demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer 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.
Product
Socket now supports four distinct alert actions instead of the previous two, and alert triaging allows users to override the actions taken for all individual alerts.
Security News
Polyfill.io has been serving malware for months via its CDN, after the project's open source maintainer sold the service to a company based in China.
Security News
OpenSSF is warning open source maintainers to stay vigilant against reputation farming on GitHub, where users artificially inflate their status by manipulating interactions on closed issues and PRs.