- Notifications
You must be signed in to change notification settings - Fork46
Enhance Reselect selectors with deeper memoization and cache management.
License
toomuchdesign/re-reselect
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Fromv5,reselect provides the ability to natively implement custom memoization/caching solutions viacreateSelector options. Most of the featuresre-reselect used to enable should be now natively available inreselect.re-reselect will try to supportreselect v5+ for backward compatibility reasons.
re-reselect is a lightweight wrapper aroundReselect meant to enhance selectors withdeeper memoization andcache management.
Switching between different arguments using standardreselect selectors causescache invalidation since defaultreselect cache has alimit of one.
re-reselectforwards different calls to differentreselectselectors stored in cache, so that computed/memoized values are retained.
re-reselectselectors work as normalreselectselectors but they are able to determine whencreating a new selector or querying a cached one on the fly, depending on the supplied arguments.
Useful to:
- Retain selector's cache when sequentiallycalled with one/few different arguments (example)
- Join similar selectors into one
- Share selectors with props across multiple component instances (seereselect example andre-reselect solution)
- Instantiate selectorson runtime
- Enhance
reselectwithcustom caching strategies
import{createCachedSelector}from're-reselect';// Normal reselect routine: declare "inputSelectors" and "resultFunc"constgetUsers=state=>state.users;constgetLibraryId=(state,libraryName)=>state.libraries[libraryName].id;constgetUsersByLibrary=createCachedSelector(// inputSelectorsgetUsers,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 cachedconstreactUsers=getUsersByLibrary(state,'react');constvueUsers=getUsersByLibrary(state,'vue');// This 3rd call hits the cacheconstreactUsersAgain=getUsersByLibrary(state,'react');// reactUsers === reactUsersAgain// "expensiveComputation" called twice in total
npm install reselectnpm install re-reselect
Let's saygetData is areselect selector.
getData(state,itemId,'dataA');getData(state,itemId,'dataB');getData(state,itemId,'dataA');
The3rd argument invalidatesreselect cache on each call, forcinggetData to re-evaluate and return a new value.
re-reselect selectors keep acache ofreselect selectors stored bycacheKey.
cacheKey is the return value of thekeySelector function. It's by default astring ornumber but it can be anything depending on the chosen cache strategy (seecache objects docs).
keySelector is a custom function which:
- takes the same arguments as the selector itself (in the example:
state,itemId,dataType) - returns a
cacheKey
Aunique persistingreselect selector instance stored in cache is used to compute data for a givencacheKey (1:1).
Back to the example, we might setupre-reselect to retrieve data byquerying one of the cached selectors using the 3rd argument ascacheKey, allowing cache invalidation only whenstate oritemId change (but notdataType):
constgetData=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:
- Evaluate the
cacheKeyfor the current call by executingkeySelector - Retrieve from cache the
reselectselector stored under the givencacheKey - Return found selector or create a new one if no selector was found
- Call returned selector with provided arguments
Easy, but doesn't scale. See"join similar selectors" example.
The solution suggested inReselect docs is fine, but it has a few downsides:
- Bloats your code by exposing both
getselectors andmakeGetselector factories - Needs to import/call the selector factory instead of directly using the selector
- Two different instances, given the same arguments, will individually store and recompute the same result (readthis)
3- Wrap yourmakeGetPieceOfData selector factory into a memoizer function and call the returning memoized selector
This is whatre-reselect actually does. 😀
- Join similar selectors
- Avoid selector factories
- Cache API calls
- Programmatic keySelector composition
- Usage with Selectorator
How do I wrap my existing selector with re-reselect?
Given yourreselect selectors:
import{createSelector}from'reselect';exportconstgetMyData=createSelector(selectorA,selectorB,selectorC,(A,B,C)=>doSomethingWith(A,B,C),);
...addkeySelector in the second function call:
import{createCachedSelector}from're-reselect';exportconstgetMyData=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!
constmyData=getMyData(state,'foo','bar');
How do I use multiple inputs to set the cacheKey?
A few good examples anda bonus:
// Basic usage: use a single argument as cacheKeycreateCachedSelector(// ...)((state,arg1,arg2,arg3)=>arg3)// Use multiple arguments and chain them into a stringcreateCachedSelector(// ...)((state,arg1,arg2,arg3)=>`${arg1}:${arg3}`)// Extract properties from an objectcreateCachedSelector(// ...)((state,props)=>`${props.a}:${props.b}`)
How do I limit the cache size?
Use acacheObject which provides that feature by supplying acacheObject option.
You can also writeyour own cache strategy!
How to share a selector across multiple components while passing in props and retaining memoization?
This example shows howre-reselect would solve the scenario described inreselect docs.
How do I test a re-reselect selector?
Like a normal reselect selector!
re-reselect selectors expose the samereselect testing methods:
dependenciesresultFuncrecomputationsresetRecomputations
Read more about testing selectors onreselect docs.
Eachre-reselect selector exposes agetMatchingSelector method which returns theunderlying matching selector instance for the given arguments,instead of the result.
getMatchingSelector expects the same arguments as a normal selector callBUT returns the instance of the cached selector itself.
Once you get a selector instance you can callits public methods.
import{createCachedSelector}from're-reselect';exportconstgetMyData=createCachedSelector(selectorA,selectorB,(A,B)=>doSomethingWith(A,B))((state,arg1)=>arg1// cacheKey);// Call your selectorconstmyFooData=getMyData(state,'foo');constmyBarData=getMyData(state,'bar');// Call getMatchingSelector method to retrieve underlying reselect selectors// which generated "myFooData" and "myBarData" resultsconstmyFooDataSelector=getMyData.getMatchingSelector(state,'foo');constmyBarDataSelector=getMyData.getMatchingSelector(state,'bar');// Call reselect's selectors methodsmyFooDataSelector.recomputations();myFooDataSelector.resetRecomputations();
import{createCachedSelector}from're-reselect';createCachedSelector(// ...reselect's `createSelector` arguments)(keySelector|{ options})
Takes the same arguments as reselect'screateSelector and returns a new function which accepts akeySelector or anoptions object.
Returns aselector instance.
import{createStructuredCachedSelector}from're-reselect';createStructuredCachedSelector(// ...reselect's `createStructuredSelector` arguments)(keySelector|{ options})
Takes the same arguments as reselect'screateStructuredSelector and returns a new function which accepts akeySelector or anoptions object.
Returns aselector instance.
A custom function receiving the same arguments as your selectors (andinputSelectors) andreturning acacheKey.
cacheKey isby default astring ornumber but can be anything depending on the chosen cache strategy (seecacheObject option).
ThekeySelector idea comes fromLodash's .memoize resolver.
Type:function
Default:undefined
ThekeySelector used by the cached selector.
Type:object
Default:FlatObjectCache
An optional customcache strategy object to handle the caching behaviour. Read more aboutre-reselect's custom cache here.
Type:function
Default:undefined
An optional function with the following signature returning thekeySelector used by the cached selector.
typekeySelectorCreator=(selectorInputs:{inputSelectors:InputSelector[];resultFunc:ResultFunc;keySelector:KeySelector;})=>KeySelector;
This allows the ability to dynamicallygeneratekeySelectors on runtime based on providedinputSelectors/resultFunc supportingkey selectors composition. It overrides any providedkeySelector.
Seeprogrammatic keySelector composition example.
Type:function
Default:reselect'screateSelector
An optional function describing acustom version of createSelector.
createCachedSelector andcreateStructuredCachedSelector return aselector instance which extends the API of astandard reselect selector.
The followings are advanced methods and you won't need them for basic usage!
Retrieve the selector responding to the given arguments.
Remove from the cache the selector responding to the given arguments.
Get the cacheObject instance being used by the selector (for advanced caching operations likethis).
Clear wholeselector cache.
Get an array containing the providedinputSelectors. Refer to relevant discussion onReselect repo.
GetresultFunc for easilytesting composed selectors.
Return the number of times the selector's result function has been recomputed.
Resetrecomputations count.
GetkeySelector for utility compositions or testing.
- re-reselect your whole redux state
- Understanding reselect and re-reselect
- React re-reselect: Better memoization and cache management
- Advanced Redux patterns: selectors
- Be selective with your state
- A swift developer’s React Native experience
- 5 key Redux libraries to improve code reuse
- Rematch's docs
- Redux re-reselect playground
- Improve tests readability
- Port to native TS based on reselect v5 approach
- Find out whether
re-reselectshould be deprecated in favour ofreselectmemoization/cache options
Thanks to you all (emoji key):
About
Enhance Reselect selectors with deeper memoization and cache management.
Topics
Resources
License
Contributing
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Uh oh!
There was an error while loading.Please reload this page.
