Security News
JSR Working Group Kicks Off with Ambitious Roadmap and Plans for Open Governance
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
re-reselect
Advanced tools
The re-reselect npm package is an extension of the reselect library, which is used for creating memoized selectors in Redux. re-reselect adds the ability to cache selectors based on dynamic keys, which can help optimize performance in applications with complex state trees.
Dynamic Key-Based Caching
This feature allows you to create selectors that cache their results based on dynamic keys. In this example, the selector caches results based on the item ID, which can improve performance by avoiding unnecessary recalculations.
const createCachedSelector = require('re-reselect').default;
const inputSelector = state => state.items;
const idSelector = (state, id) => id;
const cachedSelector = createCachedSelector(
inputSelector,
idSelector,
(items, id) => items.find(item => item.id === id)
)(idSelector);
const state = { items: [{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }] };
console.log(cachedSelector(state, 1)); // { id: 1, name: 'Item 1' }
Custom Cache Management
This feature allows you to use custom cache management strategies. In this example, an LRU (Least Recently Used) cache is used to manage the cached selectors, which can help in scenarios where memory usage needs to be controlled.
const createCachedSelector = require('re-reselect').default;
const LRU = require('lru-cache');
const cache = new LRU({ max: 100 });
const inputSelector = state => state.items;
const idSelector = (state, id) => id;
const cachedSelector = createCachedSelector(
inputSelector,
idSelector,
(items, id) => items.find(item => item.id === id)
)({ cacheObject: cache });
const state = { items: [{ id: 1, name: 'Item 1' }, { id: 2, name: 'Item 2' }] };
console.log(cachedSelector(state, 1)); // { id: 1, name: 'Item 1' }
Composite Key Selectors
This feature allows you to create selectors that use composite keys for caching. In this example, the selector caches results based on a combination of item ID and type, which can be useful for more complex state structures.
const createCachedSelector = require('re-reselect').default;
const inputSelector = state => state.items;
const compositeKeySelector = (state, id, type) => `${id}-${type}`;
const cachedSelector = createCachedSelector(
inputSelector,
compositeKeySelector,
(items, key) => items.find(item => `${item.id}-${item.type}` === key)
)(compositeKeySelector);
const state = { items: [{ id: 1, type: 'A', name: 'Item 1' }, { id: 2, type: 'B', name: 'Item 2' }] };
console.log(cachedSelector(state, 1, 'A')); // { id: 1, type: 'A', name: 'Item 1' }
Reselect is a simple library for creating memoized selectors in Redux. It does not support dynamic key-based caching like re-reselect, but it is a foundational library that re-reselect builds upon. Reselect is useful for creating selectors that are memoized based on their input arguments.
Memoize-one is a library that memoizes the most recent result of a function. It is simpler than re-reselect and does not support dynamic key-based caching. It is useful for scenarios where you only need to cache the last result of a function call.
Re-reselect is an extension of reselect that adds dynamic key-based caching. It is more powerful than reselect and memoize-one for scenarios where you need to cache results based on dynamic keys.
From v5, reselect
provides the ability to natively implement custom memoization/caching solutions via createSelector
options. Most of the features re-reselect
used to enable should be now natively available in reselect
. re-reselect
will try to support reselect
v5+ for backward compatibility reasons.
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:
reselect
with custom caching strategiesimport {createCachedSelector} from 're-reselect';
// Normal reselect routine: declare "inputSelectors" and "resultFunc"
const getUsers = state => state.users;
const getLibraryId = (state, libraryName) => state.libraries[libraryName].id;
const getUsersByLibrary = createCachedSelector(
// inputSelectors
getUsers,
getLibraryId,
// resultFunc
(users, libraryId) => expensiveComputation(users, libraryId),
)(
// re-reselect keySelector (receives selectors' arguments)
// Use "libraryName" as cacheKey
(_state_, libraryName) => libraryName
);
// Cached selectors behave like normal selectors:
// 2 reselect selectors are created, called and cached
const reactUsers = getUsersByLibrary(state, 'react');
const vueUsers = getUsersByLibrary(state, 'vue');
// This 3rd call hits the cache
const reactUsersAgain = getUsersByLibrary(state, 'react');
// reactUsers === reactUsersAgain
// "expensiveComputation" called twice in total
npm install reselect
npm install re-reselect
Let's say getData
is a reselect
selector.
getData(state, itemId, 'dataA');
getData(state, itemId, 'dataB');
getData(state, itemId, 'dataA');
The 3rd argument invalidates reselect
cache on each call, forcing getData
to re-evaluate and return a new value.
re-reselect
selectors keep a cache of reselect
selectors stored by cacheKey
.
cacheKey
is the return value of the keySelector
function. It's by default a string
or number
but it can be anything depending on the chosen cache strategy (see cache objects docs).
keySelector
is a custom function which:
state
, itemId
, dataType
)cacheKey
A unique persisting reselect
selector instance stored in cache is used to compute data for a given cacheKey
(1:1).
Back to the example, we might setup re-reselect
to retrieve 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 getData = 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
);
Replacing a selector with a cached selector is invisible to the consuming application since the API is the same.
When a cached selector is called, the following happens behind the scenes:
cacheKey
for the current call by executing keySelector
reselect
selector stored under the given cacheKey
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. 😀
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');
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.
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();
import {createCachedSelector} from 're-reselect';
createCachedSelector(
// ...reselect's `createSelector` arguments
)(
keySelector | { options }
)
Takes the same arguments as reselect's createSelector
and returns a new function which accepts a keySelector
or an options
object.
Returns a selector instance.
import {createStructuredCachedSelector} from 're-reselect';
createStructuredCachedSelector(
// ...reselect's `createStructuredSelector` arguments
)(
keySelector | { options }
)
Takes the same arguments as reselect's createStructuredSelector
and returns a new function which accepts a keySelector
or an options
object.
Returns a selector instance.
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 resolver.
Type: function
Default: undefined
The keySelector
used by the cached selector.
Type: object
Default: FlatObjectCache
An optional custom cache strategy object to handle the caching behaviour. Read more about re-reselect's custom cache here.
Type: function
Default: undefined
An optional function with the following signature returning the keySelector
used by the cached selector.
type keySelectorCreator = (selectorInputs: {
inputSelectors: InputSelector[];
resultFunc: ResultFunc;
keySelector: KeySelector;
}) => KeySelector;
This allows the ability to dynamically generate keySelectors
on runtime based on provided inputSelectors
/resultFunc
supporting key selectors composition. It overrides any provided keySelector
.
See programmatic keySelector composition example.
Type: function
Default: reselect
's createSelector
An optional function describing a custom version of createSelector.
createCachedSelector
and createStructuredCachedSelector
return a selector instance which extends the API of a standard reselect selector.
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 the cacheObject instance being used by the selector (for advanced caching operations like this).
.clearCache()
Clear whole selector
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.
.keySelector
Get keySelector
for utility compositions or testing.
re-reselect
should be deprecated in favour of reselect
memoization/cache optionsThanks to you all (emoji key):
FAQs
Enhance Reselect selectors with deeper memoization and cache management
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.
Security News
At its inaugural meeting, the JSR Working Group outlined plans for an open governance model and a roadmap to enhance JavaScript package management.
Security News
Research
An advanced npm supply chain attack is leveraging Ethereum smart contracts for decentralized, persistent malware control, evading traditional defenses.
Security News
Research
Attackers are impersonating Sindre Sorhus on npm with a fake 'chalk-node' package containing a malicious backdoor to compromise developers' projects.