Gathering detailed insights and metrics for rememo
Gathering detailed insights and metrics for rememo
Gathering detailed insights and metrics for rememo
Gathering detailed insights and metrics for rememo
Memoized selectors for Redux and other immutable object derivation
npm install rememo
Typescript
Module System
Node Version
NPM Version
JavaScript (100%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
17 Stars
106 Commits
4 Forks
3 Watchers
3 Branches
1 Contributors
Updated on Oct 05, 2024
Latest Version
4.0.2
Package Id
rememo@4.0.2
Unpacked Size
36.37 kB
Size
8.67 kB
File Count
8
NPM Version
8.15.1
Node Version
17.2.0
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
Memoized selectors for Redux and other immutable object derivation.
Rememo's default export is a function which accepts two arguments: the selector function whose return value is to be cached, and a second function which returns the reference or array of references upon which the selector's derivation depends. The return value is a new function which accepts the same arguments as the selector.
1import createSelector from 'rememo'; 2 3const getTasksByCompletion = createSelector( 4 // The expensive computation: 5 (state, isComplete) => 6 state.todo.filter((task) => task.complete === isComplete), 7 8 // The reference(s) upon which the computation depends: 9 (state) => [state.todo] 10); 11 12// The selector will only calculate the return value once so long as the state 13// `todo` reference remains the same 14let completedTasks; 15completedTasks = getTasksByCompletion(state, true); // Computed 16completedTasks = getTasksByCompletion(state, true); // Returned from cache
Rememo is published as an npm package:
npm install rememo
Browser-ready versions are available from unpkg. The browser-ready version assigns itself on the global scope as window.rememo
.
1<script src="https://unpkg.com/rememo/dist/rememo.min.js"></script> 2<script> 3 var createSelector = window.rememo; 4 5 // ... 6</script>
Rememo's default export is a function:
1createSelector( 2 selector: (...args: any[]) => any, 3 getDependants?: (...args: any[]) => any[], 4): (...args: any[]) => any
The returned function is a memoized selector with the following signature:
1memoizedSelector(source: object, ...args: any[]): any
It's expected that the first argument to the memoized function is the source from which the selector operates. It is ignored when considering whether the argument result has already been cached.
The memoized selector function includes two additional properties:
clear()
: When invoked, resets memoization cache.getDependants(source: Object, ...args: any[])
: The dependants getter for the selector.The getDependants
property can be useful when creating selectors which compose other memoized selectors, in which case the dependants are the union of the two selectors' dependants:
1const getTasksByCompletion = createSelector( 2 (state, isComplete) => 3 state.todo.filter((task) => task.complete === isComplete), 4 (state) => [state.todo] 5); 6 7const getTasksByCompletionForCurrentDate = createSelector( 8 (state, isComplete) => 9 getTasksByCompletion(state, isComplete).filter( 10 (task) => task.date === state.currentDate 11 ), 12 (state, isComplete) => [ 13 ...getTasksByCompletion.getDependants(state, isComplete), 14 state.currentDate, 15 ] 16);
While designed specifically for use with Redux, Rememo is a simple pattern for efficiently deriving values from any immutable data object. Rememo takes advantage of Redux's core principles of data normalization and immutability. While tracking normalized data in a Redux store is beneficial for eliminating redudancy and reducing overall memory storage, in doing so it sacrifices conveniences that would otherwise make for a pleasant developer experience. It's for this reason that a selector pattern can be desirable. A selector is nothing more than a function which receives the current state and optionally a set of arguments to be used in determining the calculated value.
For example, consider the following state structure to describe a to-do list application:
1const state = { 2 todo: [ 3 { text: 'Go to the gym', complete: true }, 4 { text: 'Try to spend time in the sunlight', complete: false }, 5 { text: 'Laundry must be done', complete: true }, 6 ], 7};
If we wanted to filter tasks by completion, we could write a simple function:
1function getTasksByCompletion(state, isComplete) { 2 return state.todo.filter((task) => task.complete === isComplete); 3}
This works well enough and requires no additional tools, but you'll observe that the filtering we perform on the set of to-do tasks could become costly if we were to have thousands of tasks. And this is just a simple example; real-world use cases could involve far more expensive computation. Add to this the very real likelihood that our application might call this function many times even when our to-do set has not changed.
Furthermore, when used in combination with React.PureComponent
or react-redux
's connect
— which creates pure components by default — it is advisable to pass unchanging object and array references as props on subsequent renders. A selector which returns a new reference on each invocation (as occurs with Array#map
or Array#filter
), your component will needlessly render even if the underlying data does not change.
This is where Rememo comes in: a Rememo selector will cache the resulting value so long as the references upon which it depends have not changed. This works particularly well for immutable data structures, where we can perform a trivial strict equality comparison (===
) to determine whether state has changed. Without guaranteed immutability, equality can only be known by deeply traversing the object structure, an operation which in many cases is far more costly than the original computation.
In our above example, we know the value of the function will only change if the set of to-do's has changed. It's in Rememo's second argument that we describe this dependency:
1const getTasksByCompletion = createSelector( 2 (state, isComplete) => 3 state.todo.filter((task) => task.complete === isComplete), 4 (state) => [state.todo] 5);
Now we can call getTasksByCompletion
as many times as we want without needlessly wasting time filtering tasks when the todo
set has not changed.
To simplify testing of memoized selectors, the function returned by createSelector
includes a clear
function:
1const getTasksByCompletion = require('../selector'); 2 3// Test licecycle management varies by runner. This example uses Mocha. 4beforeEach(() => { 5 getTasksByCompletion.clear(); 6});
Alternatively, you can create separate references (exports) for your memoized and unmemoized selectors, then test only the unmemoized selector.
Refer to Rememo's own tests as an example.
How does this differ from Reselect, another selector memoization library?
Reselect and Rememo largely share the same goals, but have slightly different implementation semantics. Reselect optimizes for function composition, requiring that you pass as arguments functions returning derived data of increasing specificity. Constrasting it to our to-do example above, with Reselect we would pass two arguments: a function which retrieves todo
from the state object, and a second function which receives that set as an argument and performs the completeness filter. The distinction is not as obvious with a simple example like this one, and can be seen more clearly with examples in Reselect's README.
Rememo instead encourages you to consider the derivation first-and-foremost without requiring you to build up the individual dependencies ahead of time. This is especially convenient if your computation depends on many disparate state paths, or if you choose not to memoize all selectors and would rather opt-in to caching at your own judgment. Composing selectors is still straight-forward in Rememo if you subscribe to the convention of passing state
always as the first argument, since this enables your selectors to call upon other each other passing the complete state object.
Copyright 2018-2022 Andrew Duthie
Released under the MIT License.
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
no dangerous workflow patterns detected
Reason
license file detected
Details
Reason
6 existing vulnerabilities detected
Details
Reason
dependency not pinned by hash detected -- score normalized to 3
Details
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 0/26 approved changesets -- score normalized to 0
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-07-07
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More