Releases: reduxjs/reselect
v5.1.1
This patch release fixes behavior of resultEqualityCheck
in weakMapMemoize
, fixes the case of lruMemoize
being given a maxSize
less than 1, and tweaks the internal implementation of lruMemoize
. (We've also updated our general build tooling.)
Changelog
Bug fixes
Previously, providing the resultEqualityCheck
option to weakMapMemoize
resulted in it being called with empty objects as part of the initialization / dev check process. That could be an issue if your comparison function expected different values. We've updated the logic to avoid that, as well as improving a couple other perf aspects.
Previously, passing a maxSize
< 1 to lruMemoize
would result in it creating a larger cache. That's now fixed.
lruMemoize
now uses a symbol for its NOT_FOUND
value instead of a string.
What's Changed
- Ensure
lruMemoize
correctly memoizes whenmaxSize
is set to a number less than 1 by @aryaemami59 in #698 - Fix
resultEqualityCheck
behavior inweakMapMemoize
by @aryaemami59 in #699 - Update TypeScript to 5.4 by @aryaemami59 in #708
- Upgrade to Yarn 4 by @aryaemami59 in #705
- Fix: use unique value for NOT_FOUND by @romgrk in #709
Full Changelog: v5.1.0...v5.1.1
v5.1.0
This minor release:
- Adds a new
createSelector.withTypes<RootState>()
andcreateStructuredSelector.withTypes<RootState>()
API - Deprecates the
TypedStructuredSelectorCreator
type introduced in 5.0 - Aims to reduce false positives in
identityFunctionCheck
by only running if the output selector is passed one argument - Fixes a bug with
weakMapMemoize
'sresultEqualityCheck
when used with a primitive result.
withTypes
Most commonly, selectors will accept the root state of a Redux store as their first argument. withTypes
allows you to specify what that first argument will be ahead of creating the selector, meaning it doesn't have to be specified.
// previously
export const selectPostById = createSelector(
[
(state: RootState) => state.posts.entities,
(state: RootState, id: number) => id,
],
(entities, id) => entities[id],
);
// now
export const createAppSelector = createSelector.withTypes<RootState>();
export const selectPostById = createAppSelector(
[(state) => state.posts.entities, (state, id: number) => id],
(entities, id) => entities[id],
);
Known limitations
Due to a Typescript issue, inference of the output selector's parameters only works with withTypes
when using an array of input selectors.
If using the variadic version, you can either wrap your input selectors in an array instance (as above), or annotate the parameters manually.
export const createAppSelector = createSelector.withTypes<RootState>();
export const selectPostById = createAppSelector(
(state) => state.posts.entities,
(state, id: number) => id,
// parameters cannot be inferred, so need annotating
(entities: Record<number, Post>, id: number) => entities[id],
);
What's Changed
- Reduce
identityFunctionCheck
false positives by @Methuselah96 in #660 - Fix cut content inside TOC of docs by @aryaemami59 in #664
- Remove redundant Svg requires from components in docs by @aryaemami59 in #665
- Fix
_lastResult.deref
is not a function (it is undefined) in React Native and Expo applications by @aryaemami59 in #671 - Update getting-started.mdx by @cchaonie in #672
- Update createSelectorCreator.mdx with correct defaults by @lukeapage in #674
- Introduce pre-typed
createSelector
viacreateSelector.withTypes<RootState>()
method by @aryaemami59 in #673 - Bump RTK and React-Redux to latest versions by @aryaemami59 in #676
- add publish job by @EskiMojo14 in #677
- Wrap up implementation of
TypedStructuredSelectorCreator
by @aryaemami59 in #667 - Introduce pre-typed
createStructuredSelector
viacreateStructuredSelector.ts.withTypes<RootState>()
method by @aryaemami59 in #678 - Bump
vitest
to v1 by @aryaemami59 in #668
New Contributors
- @Methuselah96 made their first contribution in #660
- @cchaonie made their first contribution in #672
Full Changelog: v5.0.1...v5.1.0
v5.0.0
This major release:
- Switches
createSelector
to use a newweakMapMemoize
method as the default memoizer - Renames the existing
defaultMemoize
method tolruMemoize
- Adds new configuration options to
createSelector
- Adds new development mode checks
- Updates the packaging for better ESM/CJS compatibility and modernizes the build output
- Makes significant updates to the TS types.
This release has breaking changes. (note: this points to v5.0.1, which contains a hotfix that was released prior to the announcement.)
This release is part of a wave of major versions of all the Redux packages: Redux Toolkit 2.0, Redux core 5.0, React-Redux 9.0, Reselect 5.0, and Redux Thunk 3.0.
For full details on all of the breaking changes and other significant changes to all of those packages, see the "Migrating to RTK 2.0 and Redux 5.0" migration guide in the Redux docs.
We have a new docs site! The Reselect docs are now at https://reselect.js.org.
Note
The Redux core, Reselect, and Redux Thunk packages are included as part of Redux Toolkit, and RTK users do not need to manually upgrade them - you'll get them as part of the upgrade to RTK 2.0. (If you're not using Redux Toolkit yet, please start migrating your existing legacy Redux code to use Redux Toolkit today!)
# RTK
npm install @reduxjs/toolkit
yarn add @reduxjs/toolkit
# Standalone
npm install reselect
yarn add reselect
Changelog
createSelector
Uses weakMapMemoize
By Default
Reselect's createSelector
originally only had one memoization function, which has originally called defaultMemoize
(and per below, is now renamed to lruMemoize
). It's always used a customizable comparison method to compare each argument. Over time, we added more functionality, particularly in v4.1.0 where lruMemoize
gained options for {memoize, maxSize, resultEqualityCheck}
.
However, lruMemoize
has limitations. The biggest one is that the default cache size is 1. This makes selector instances hard to reuse in scenarios like list items, which might call selectSomeValue(state, props.id)
, and thus never actually memoize due to changing arguments. There are workarounds, but they're cumbersome - using createSelectorCreator
to create a customized createSelector
function with a different memoization implementation, creating unique selector instances per component, or setting a fixed maxSize
.
For 5.0, we added a new weakMapMemoize
memoization function, which takes a different approach (as originally implemented in the React codebase). It uses an internal tree of cache nodes rather than a single value or a list of values. This gives weakMapMemoize
an effectively infinite cache size!
We've done a fair amount of testing, and weakMapMemoize
both performs faster and has more frequent cache hits than lruMemoize
.
Given that, we've made the switch so that createSelector
uses weakMapMemoize
by default! This should result in better performance for Redux and React apps that use Reselect.
This is hopefully a mostly non-breaking change at the code level, and an overall improvement at the behavior level.
This is a breaking change. weakMapMemoize
does not have an equalityCheck
option or allow customizing the comparison behavior - it's entirely based on reference comparisons, since it uses WeakMap/Map
internally. It also does not have a maxSize
option, but does have resultEqualityCheck
.
If you need to customize the overall equality comparison behavior, import and pass lruMemoize
as the memoize
and argsMemoize
option!
Also, note that an "infinite cache size" from one point of view can be considered a "memory leak" for another point of view. The use of WeakMap
s should mean that in most cases values do get garbage collected when the rest of the app no longer needs those, but there may be some scenarios with use of primitive keys that could lead to potential leaks. If this looks like it's happening for you, please compare behavior with lruMemoize
instead, and file an issue report so we can investigate.
New / Improved createSelector
Memoization Options
Originally, the only way to customize createSelector
's behavior (such as using an alternate memoization function) was to first create a customized version via createSelectorCreator(memoizerFunction, memoizerOptions)
. This was typically used for creating use cases like deep equality comparisons with _.equal
instead of shallow equality, as well as alternate memoizers that had a notion of cache size.
With Reselect 4.1.0, we added the ability to pass memoizer options directly to createSelector
, and also updated defaultMemoize
to accept several options such as a max cache size. This meant that you could call createSelector(...inputFns, outputFn, {memoizeOptions: {maxSize: 100}}), but you couldn't change the memoizer _function_ being used directly - that still required use of
createSelectorCreator`.
Additionally, Reselect internally uses the provided memoizer function twice internally: once on the overall arguments passed to selectSomeValue(a, b, c)
, and a second time on the values extracted by the input functions such as state => state.a
. There have been multiple issues over the years where users wanted to provide separate memoization functions for the arguments vs the extracted values, such as a reference equality check for the arguments and a shallow check for the extracted values.
With this release, you can now pass alternate memoizer functions directly to createSelector
, and both createSelector
and createSelectorCreator
accept separate options for memoize
and argsMemoize
(along with any options for those):
const selectTodoIds = createSelector(
(state: TodoState) => state.todos,
todos => todos.map(({ id }) => id),
{
memoize: defaultMemoize,
memoizeOptions: {
resultEqualityCheck: (a, b) => a === b
}
argsMemoize: microMemoize,
argsMemoizeOptions: {
isEqual: (a, b) => a === b
},
}
)
This should mostly eliminate the need to use createSelectorCreator
for customization. (You can still use it for encapsulation / reuse if you want to create many selectors with the same customization options.)
ESM/CJS Package Compatibility
The biggest theme of the Redux v5 and RTK 2.0 releases is trying to get "true" ESM package publishing compatibility in place, while still supporting CJS in the published package.
The primary build artifact is now an ESM file, dist/reselect.mjs
. Most build tools should pick this up. There's also a CJS artifact, and a second copy of the ESM file named reselect.legacy-esm.js
to support Webpack 4 (which does not recognize the exports
field in package.json
). Additionally, all of the build artifacts now live under ./dist/
in the published package.
Modernized Build Output
We now publish modern JS syntax targeting ES2020, including optional chaining, object spread, and other modern syntax. If you need to
Build Tooling
We're now building the package using https://github.com/egoist/tsup. We also now include sourcemaps for the ESM and CJS artifacts.
Dropping UMD Builds
Redux has always shipped with UMD build artifacts. These are primarily meant for direct import as script tags, such as in a CodePen or a no-bundler build environment.
We've dropped those build artifacts from the published package, on the grounds that the use cases seem pretty rare today.
There's now a reselect.browser.mjs
file in the package that can be loaded from a CDN like Unpkg.
If you have strong use cases for us continuing to include UMD build artifacts, please let us know!
Dev Mode Checks
createSelector
now does checks in development mode for common mistakes, like input selectors that always return new references, or result functions that immediately return their argument. These checks can be customized at selector creation or globally.
This is important, as an input selector returning a materially different result with the same parameters means that the output selector will never memoize correctly and be run unnecessarily, thus (potentially) creating a new result and causing rerenders.
const addNumbers = createSelector(
// this input selector will always return a new reference when run
// so cache will never be used
(a, b) => ({ a, b }),
({ a, b }) => ({ total: a + b })
)
// instead, you should have an input selector for each stable piece of data
const addNumbersStable = createSelector(
(a, b) => a,
(a, b) => b,
(a, b) => ({
total: a + b,
})
)
This is done the first time the selector is called, unless configured otherwise. See the Reselect docs on dev-mode checks for more details.
TypeScript Changes
We've dropped support for TS 4.6 and earlier, and our support matrix is now TS 4.7+.
The ParametricSelector
and OutputParametricSelector
types have been removed. Use Selector
and OutputSelector
instead.
The TS types have been updated to provide a better visual hover preview representation of a selector. It should now actually be previewed as "a function with attached fields", like:
const selectTodos: ((state: {
todos: {
id: number;
title: string;
description: string;
completed: boolean;
}[];
}) => number[]) & {
clearCa...
v5.0.0-rc.1
This release candidate renames the original defaultMemoize
function to lruMemoize
, and improves dev check warnings to include a stack trace to help track down details. This release has breaking changes.
See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.
npm install reselect@next
yarn add reselect@next
Changelog
Renaming defaultMemoize
to lruMemoize
Reselect's createSelector
originally only had one memoization function, which has always called defaultMemoize
. In the previous v5.0.0-rc.0
release, we switched createSelector
to use the new weakMapMemoize
function as the default.
That meant that defaultMemoize
was now badly named, because it isn't the default any more.
Given that, we've renamed defaultMemoize
to lruMemoize
, to better describe what it does.
This should not affect most Reselect users, since few apps actually customize selector setup. For those that do have references to defaultMemoize
in the codebase, replace those with lruMemoize
.
What's Changed
- Add stack to dev check warnings by @EskiMojo14 in #653
- Rename
defaultMemoize
tolruMemoize
by @aryaemami59 in #654
Full Changelog: v5.0.0-rc.0...v5.0.0-rc.1
v5.0.0-rc.0
This release candidate switches createSelector
to use weakMapMemoize
as the default memoization method, adds a resultEqualityCheck
option to weakMapMemoize
, reworks the dev mode check setup and adds a dev-mode check for result functions that look like x => x
, and makes some final types tweaks. This has breaking changes.
See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.
npm install reselect@next
yarn add reselect@next
Changelog
createSelector
Uses weakMapMemoize
By Default
Reselect's createSelector
originally only had one memoization function, which has always called defaultMemoize
. It's always used a customizable comparison method to compare each argument. Over time, we added more functionality, particularly in v4.1.0 where defaultMemoize
gained options for {memoize, maxSize, resultEqualityCheck}
.
However, defaultMemoize
has limitations. The biggest one is that the default cache size is 1. This makes selector instances hard to reuse in scenarios like list items, which might call selectSomeValue(state, props.id)
, and thus never actually memoize due to changing arguments. There are workarounds, but they're cumbersome - using createSelectorCreator
to create a customized createSelector
function with a different memoization implementation, creating unique selector instances per component, or setting a fixed maxSize
.
For 5.0, we added a new weakMapMemoize
memoization function, which takes a different approach. It uses an internal tree of cache nodes rather than a single value or a list of values. This gives weakMapMemoize
an effectively infinite cache size!
We've done a fair amount of testing, and weakMapMemoize
both performs faster and has more frequent cache hits than defaultMemoize
.
Given that, we've made the switch so that createSelector
uses weakMapMemoize
by default! This should result in better performance for Redux and React apps that use Reselect.
This is hopefully a mostly non-breaking change at the code level, and an overall improvement at the behavior level.
This is a breaking change. weakMapMemoize
does not have an equalityCheck
option or allow customizing the comparison behavior - it's entirely based on reference comparisons, since it uses WeakMap/Map
internally. It also does not have a maxSize
option, but does have resultEqualityCheck
.
If you need to customize the overall equality comparison behavior, import and pass defaultMemoize
as the memoize
and argsMemoize
option!
Also, since defaultMemoize
is no longer the actual "default" memoization function, we are considering a potential rename of defaultMemoize
to something like lruMemoize
to clarify the naming.
Dev-Mode Checks
Earlier, we added an inputStabilityCheck
that checked for input selectors that accidentally return new references, with a globally exported override method.
In this release, we've added an additional dev mode check that looks for result functions that look like x => x
- in other words, passing the input function result straight through. This is almost always a logic error. Either the input selectors are doing too much work, or the selector is just performing a straight lookup with no derived values and it should be a plain function instead of memoized.
Both checks are customizable on a per-selector-instance basis, and also controllable at a global level:
setGlobalDevModeChecks({
inputStabilityCheck: 'always',
identityFunctionCheck: 'never'
})
createSelector(
[input1, input2],
resultFn,
{devModeChecks: {identityFunctionCheck: 'always'}}
)
What's Changed
- Update Docs for v5 changes. by @aryaemami59 in #636
- V5 final type changes by @aryaemami59 in #639
- Match
resetRecomputations
andresetDependencyRecomputations
behavior to their types by @aryaemami59 in #646 - Try adding
resultEqualityCheck
toweakMapMemoize
by @markerikson in #647 - Make
EqualityFn
slightly more type-safe. by @aryaemami59 in #651 - Switch createSelector to use weakMapMemoize by default by @markerikson in #649
- Add warning in development when a result function is
x => x
. by @aryaemami59 in #645
Full Changelog: v5.0.0-beta.1...v5.0.0-rc.0
v5.0.0-beta.1
This beta release improves the TS types so that hover previews of generated selectors have a much more readable format.
Note that we hope to release Redux Toolkit 2.0, Redux core 5.0, and React-Redux 9.0 by the start of December! (If we don't hit that, we'll aim for January, after the holidays.)
See the preview Redux Toolkit 2.0 + Redux core 5.0 Migration Guide for an overview of breaking changes in RTK 2.0 and Redux core.
npm install reselect@beta
yarn add reselect@beta
What's Changed
- Fix hover preview of generated selectors. by @aryaemami59 in #633
Full Changelog: v5.0.0-beta.0...v5.0.0-beta.1
v5.0.0-beta.0
This beta release updates createSelector
to accept additional memoization functions and memoizer options directly (without needing to use createSelectorCreator
first), renames an experimental memoizer to unstable_autotrackMemoizer
for clarity, and drops support for TS 4.6 and earlier.
npm i reselect@beta
yarn add reselect@beta
This is part of the in-progress Redux Toolkit 2.0 beta work, and Reselect 5.0 will be included with Redux Toolkit 2.0 when it is released. Please try out RTK 2.0 beta and give us feedback!
Changelog
New / Improved createSelector
Memoization Options
Originally, the only way to customize createSelector
's behavior (such as using an alternate memoization function) was to first create a customized version via createSelectorCreator(memoizerFunction, memoizerOptions)
. This was typically used for creating use cases like deep equality comparisons with _.equal
instead of shallow equality, as well as alternate memoizers that had a notion of cache size.
With Reselect 4.1.0, we added the ability to pass memoizer options directly to createSelector
, and also updated defaultMemoize
to accept several options such as a max cache size. This meant that you could call createSelector(...inputFns, outputFn, {memoizeOptions: {maxSize: 100}}), but you couldn't change the memoizer _function_ being used directly - that still required use of
createSelectorCreator`.
Additionally, Reselect internally uses the provided memoizer function twice internally: once on the overall arguments passed to selectSomeValue(a, b, c)
, and a second time on the values extracted by the input functions such as state => state.a
. There have been multiple issues over the years where users wanted to provide separate memoization functions for the arguments vs the extracted values, such as a reference equality check for the arguments and a shallow check for the extracted values.
With this release, you can now pass alternate memoizer functions directly to createSelector
, and both createSelector
and createSelectorCreator
accept separate options for memoize
and argsMemoize
(along with any options for those):
const selectTodoIds = createSelector(
(state: TodoState) => state.todos,
todos => todos.map(({ id }) => id),
{
memoize: defaultMemoize,
memoizeOptions: {
resultEqualityCheck: (a, b) => a === b
}
argsMemoize: microMemoize,
argsMemoizeOptions: {
isEqual: (a, b) => a === b
},
}
)
This should mostly eliminate the need to use createSelectorCreator
for customization. (You can still use it for encapsulation / reuse if you want to create many selectors with the same customization options.)
Thanks to @aryaemami59 for some incredibly comprehensive efforts reworking the internals of createSelector
, our TS types, and the codebase structure in order to make this possible!
Other Changes
We've dropped support for TS 4.6 and earlier. Our current TS support matrix is TS 4.7+.
The experimental autotrackMemoize
function has been renamed to unstable_autotrackeMemoize
. It will still be exported, but it needs significant further testing before we consider it ready.
What's Changed
- Drop support for TS 4.6 and earlier, and update CI to use latest RTK betas by @markerikson in #630
- Allow passing in memoize functions directly to
createSelector
. by @aryaemami59 in #626 - Rename to
unstable_autotrackMemoize
and bump Vitest version by @markerikson in #631
Full Changelog: v5.0.0-alpha.2...v5.0.0-beta.0
v5.0.0-alpha.2
This alpha release updates createSelector
to run an extra one-time check for input selector stability in development builds, and updates the build artifacts to include sourcemaps and a browser-ready ESM production bundle.
Changelog
Development Input Stability Checks
Reselect uses two levels of memoization. The first level checks if any of the actual arguments have changed, and the second sees if the values extracted by the input functions have changed.
If an input function always returns a new reference, like (state) => ({a: state.a, b: state.b})
or (state) => state.items.map()
, that will cause the selector to never memoize properly. This is a bug, similar conceptually to always returning a new reference in useSelector()
, or always including a new reference in useEffect
's dependencies array.
Since this is a common mistake, we've added a development mode check to catch this. By default, createSelector
will now run try executing the memoization function twice during the first call to the selector. If the result appears to be different, it will log a warning with the arguments and the two different sets of extracted input values.
You can configure this behavior in two ways: by passing an inputStabilityCheck
option directly to createSelector
, or by importing the global setInputStabilityCheckEnabled()
function:
type StabilityCheck = 'always' | 'once' | 'never'
const unstableInput = (a: number, b: number) => ({ a, b })
// Create a selector that double-checks the inputs every time it runs
const selector = createSelector(
unstableInput,
({ a, b }) => a + b,
{inputStabilityCheck: 'always'}
)
// Set all selectors to never double-check by default (unless given a specific option)
import { setInputStabilityCheckEnabled } from 'reselect'
setInputStabilityCheckEnabled('never')
Build Artifact Changes
We now include a reselect.browser.mjs
ESM artifact has been compiled for production settings and is ready for use as an ES module in browsers.
We also have updated the reselect.legacy-esm.js
artifact to transpile syntax for compat with Webpack 4.
What's Changed
- Run input selectors twice, to check stability. by @EskiMojo14 in #612
- Build browser ESM and Webpack 4 compat artifacts, and add sourcemaps by @markerikson in #613
Full Changelog: v5.0.0-alpha.1...v5.0.0-alpha.2
v5.0.0-alpha.1
This alpha release adds two experimental new memoizers with different capabilities and tradeoffs.
npm i reselect@alpha
yarn add reselect@alpha
See the release notes for v5.0.0-alpha.0 for details on previous ESM/CJS build compat changes.
Changelog
New Experimental autotrack
and weakmap
Memoizers
Reselect has always allowed swapping out the function memoizer used inside of createSelector
. Reselect's existing defaultMemoize
memoizer is based on shallow equality checks for arguments. This is simple and fast, but also has limitations.
The most common limitation with defaultMemoize
and shallow equality checks is that it can produce "false positive" recalculations. A classic example of this would be a selector that extracts an array of todo IDs:
const selectTodoIds = createSelector(
(state: RootState) => state.todos,
(todos) => todos.map(t => t.id)
)
If you dispatch a todoToggled()
action that flips state.todos[3].completed
, that will produce a new todo
object at index 3 and a new todos
array, because it's an immutable update. However, selectTodoIds
will see that todos
is a new reference and recalculate the result, even though none of the todo.id
fields have changed. This creates a new IDs array that is shallow-equal to the last one. This is both a waste of computation time, and a new result reference that could cause a component to re-render even though the array hasn't conceptually changed.
createSelector
has also always defaulted to a cache size of 1. With Reselect 4.1, we added a maxSize
option to defaultMemoize
, but this requires a known fixed cache size value at creation time. It's hard to estimate how many cache entries you might need in the future (will my list have 10 items? 100? 1000?).
This release includes two new experimental memoizers that have differing tradeoffs, with the goal of addressing these issues in different ways.
For now, both of these can be used by calling createSelectorCreator
and generating a customized version of createSelector
:
import { createSelectorCreator, autotrackMemoize, weakmapMemoize } from 'reselect'
const createSelectorAutotrack = createSelectorCreator(autotrackMemoize)
const createSelectorWeakmap = createSelectorCreator(weakmapMemoize)
In future 5.0-alpha releases, we'd like to investigate passing these directly to createSelector()
calls.
autotrackMemoize
autotrackMemoize
uses an "auto-tracking" approach inspired by the work of the Ember Glimmer team. It uses a Proxy
to wrap arguments and track accesses to nested fields in your selector on first read. Later, when the selector is called with new arguments, it identifies which accessed fields have changed and only recalculates the result if one or more of those accessed fields have changed.
This allows it to be more precise than the shallow equality checks in defaultMemoize
. In fact, with that exact same selectTodoIds
code above, a selector that uses autotrackMemoize
will not recalculate if you flip a todo.completed
field, because it can see that you only accessed the todo.id
fields.
This memoizer is directly based on the code and concepts from these articles and examples:
- https://github.com/simonihmig/tracked-redux
- https://www.pzuraq.com/blog/how-autotracking-works
- https://gist.github.com/pzuraq/79bf862e0f8cd9521b79c4b6eccdc4f9
- https://github.com/lifeart/glimmerx-workshop
- https://v5.chriskrycho.com/journal/autotracking-elegant-dx-via-cutting-edge-cs/
- https://github.com/glimmerjs/glimmer-vm/blob/master/packages/%2540glimmer/validator/
Design Tradeoffs for autotrackMemoize
- It only has a cache size of 1
- It is slower than
defaultMemoize
, because it has to do more work. (How much slower is dependent on the number of accessed fields in a selector, number of calls, frequency of input changes, etc) - It can have some unexpected behavior. Because it tracks nested field accesses, cases where you don't access a field will not recalculate properly. For example, a badly-written selector like
createSelector(state => state.todos, todos => todos)
that just immediately returns the extracted value will never update, because it doesn't see any field accesses to check. (You shouldn't write selectors like that to begin with :) But we've seen them in the wild.) - It is likely to avoid excess calculations and recalculate fewer times than
defaultMemoize
will, which may also result in fewer component re-renders
Use Cases for autotrackMemoize
autotrackMemoize
is likely best used for cases where you need to access specific nested fields in data, and avoid recalculating if other fields in the same data objects are immutably updated.
weakmapMemoize
defaultMemoize
has to be explicitly configured to have a cache size larger than 1, and uses an LRU cache internally.
weakmapMemoize
creates a tree of WeakMap
-based cache nodes based on the identity of the arguments it's been called with (in this case, the extracted values from your input functions). This allows weakmapMemoize
to have an effectively infinite cache size. Cache results will be kept in memory as long as references to the arguments still exist, and then cleared out as the arguments are garbage-collected.
This memoizer is directly based on code from the React codebase:
Design Tradeoffs for weakmapMemoize
- There's currently no way to alter the argument comparisons - they're based on strict reference equality
- It's roughly the same speed as
defaultMemoize
, although likely a fraction slower - It has an effectively infinite cache size, but you have no control over how long values are kept in cache as it's based on garbage collection and
WeakMap
s
Use Cases for weakmapMemoize
This memoizer is likely best used for cases where you need to call the same selector instance with many different arguments, such as a single selector instance that is used in a list item component and called with item IDs like useSelector(state => selectSomeData(state, props.category))
.
Argument Memoization Uses defaultMemoize
Back in PR #297, the outer argument memoization was changed to use the same provided memoization function as the inner extracted values memoization. For performance reasons, we've flipped this back to use defaultMemoize
and shallow equality checks for the outer argument memoization. In most cases this should have no change at all for end users, because the memoizer is rarely overridden anyway.
We'd like to investigate allowing customization of both arguments and extracted values memoizers in a later 5.0-alpha release
What's Changed
- Add experimental new memoizers: autotrack and weakmap by @markerikson in #605
Full Changelog: v5.0.0-alpha.0...v5.0.0-alpha.1
v5.0.0-alpha.0
This alpha release updates Reselect's build tooling, and updates the packaging for proper ESM compability.
This accompanies the Redux Toolkit 2.0 alphas, currently in progress.
npm i reselect@alpha
yarn add reselect@alpha
Changelog
ESM/CJS Package Compatibility
The biggest theme of the Redux v5 and RTK 2.0 releases is trying to get "true" ESM package publishing compatibility in place, while still supporting CJS in the published package.
Earlier RTK alphas made changes to the package.json
contents and published build artifacts in an attempt to get ESM+CJS compat working correctly, but those alphas had several varying compat issues.
We've set up a battery of example applications in the RTK repo that use a variety of build tools (currently CRA4, CRA5, Next 13, and Vite, Node CJS mode, and Node ESM mode), to verify that Redux and Redux Toolkit compile, import, and run correctly with both TS and various bundlers. We've also set up a check using a custom CLI wrapper around https://arethetypeswrong.github.io to check for potential packaging incompatibilities.
This release changes the names and contents of the published build artifacts, and the various exports/module/main
fields in package.json
to point to those.
The primary build artifact is now an ESM file, dist/reselect.mjs
. Most build tools should pick this up. There's also a CJS artifact as well.
As of this release, we think we have ESM+CJS compat working correctly, but we ask that the community try out the alphas in your apps and let us know of any compat problems!
Note: The one known potential issue is that TypeScript's new
moduleResolution: "node16"
mode may see a mismatch between the ESM artifacts and the TS typedefs when imported in a Node CJS environment, and [that may allow hypothetically-incorrect import usage. (See ongoing discussion in https://github.com/arethetypeswrong/arethetypeswrong.github.io/issues/21 .) In practice, we think that probably won't be a concern, and we'll do further investigation before a final release.
What's Changed
- Upgrade all build tooling for v5 alpha by @markerikson in #603
- Tweak CI setup to add CodeSandbox CI and fix Yarn deps by @markerikson in #607
Full Changelog: v4.1.8...v5.0.0-alpha.0