From d9a885a06590ef20b7fa24deacc375b315cfcb0f Mon Sep 17 00:00:00 2001 From: Alex K Date: Sun, 19 Jul 2026 18:54:48 +0300 Subject: [PATCH 1/2] docs: clarify how to fully clear a selector's cache (#569) --- website/docs/FAQ.mdx | 20 ++++++++++++++++++++ website/docs/api/createSelector.mdx | 14 ++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/website/docs/FAQ.mdx b/website/docs/FAQ.mdx index ab98c197..f969a1c8 100644 --- a/website/docs/FAQ.mdx +++ b/website/docs/FAQ.mdx @@ -330,6 +330,26 @@ test('selector unit test', () => { {/* END: FAQ/howToTest.test.ts */} +:::note Resetting a selector's cache between tests + +Because Reselect uses a two-stage "cascading" memoization, a memoized selector holds _two_ separate caches: + +- the `argsMemoize` cache for the arguments passed to the output selector, attached to the selector itself. +- the `memoize` cache for the results of the , attached to `memoizedResultFunc`. + +Calling `clearCache` on the selector only clears the first one. To fully reset a selector between tests, clear both: + +```ts +selector.clearCache() // clears the `argsMemoize` cache +selector.memoizedResultFunc.clearCache() // clears the `memoize` cache +selector.resetRecomputations() +selector.resetDependencyRecomputations() +``` + +Alternatively, create the selector inside each test (or in a `beforeEach` block) so that every test starts with a fresh cache. + +::: + ## Can I share a selector across multiple component instances? Yes, although if they pass in different arguments, you will need to handle that in order for memoization to work consistently: diff --git a/website/docs/api/createSelector.mdx b/website/docs/api/createSelector.mdx index ced43047..a9efab53 100644 --- a/website/docs/api/createSelector.mdx +++ b/website/docs/api/createSelector.mdx @@ -10,6 +10,7 @@ import Tabs from '@theme/Tabs' import TabItem from '@theme/TabItem' import { InternalLinks } from '@site/src/components/InternalLinks' +import Link from '@docusaurus/Link' # `createSelector` @@ -62,6 +63,19 @@ The output selectors created by `createSelector` have several additional propert | `memoize` | Function used to memoize the `resultFunc`. | | `argsMemoize` | Function used to memoize the arguments passed into the . | +:::note Clearing the cache + +Both the output selector and its `memoizedResultFunc` expose the fields attached by their memoize function — for the built-in memoizers that means `clearCache`, `resultsCount` and `resetResultsCount`. + +Because a selector keeps a separate cache for its arguments (`argsMemoize`) and for its result (`memoize`), fully clearing it requires calling `clearCache` on **both**: + +```ts +mySelector.clearCache() // clears the `argsMemoize` cache +mySelector.memoizedResultFunc.clearCache() // clears the `memoize` cache +``` + +::: + ## Type Parameters | Name | Description | From 4a7ff20174fdace8b7769623c2d42dd19a60bacd Mon Sep 17 00:00:00 2001 From: Alex K Date: Sun, 19 Jul 2026 18:57:40 +0300 Subject: [PATCH 2/2] types: document dual cache on selector fields and add clearCache type test (#569) --- src/types.ts | 9 +++++++++ typescript_test/argsMemoize.typetest.ts | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/types.ts b/src/types.ts index 742257a8..997ae91c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -188,6 +188,10 @@ export type OutputSelectorFields< /** * The memoized version of {@linkcode OutputSelectorFields.resultFunc resultFunc}. + * + * This holds its own cache, separate from the one attached to the output + * selector itself. To fully reset a selector, call `clearCache` on both + * (e.g. `selector.clearCache()` and `selector.memoizedResultFunc.clearCache()`). */ memoizedResultFunc: Combiner & ExtractMemoizerFields @@ -454,6 +458,11 @@ export type DefaultMemoizeFields = { * This method is typically used to reset the state of the cache, allowing * for the garbage collection of previously memoized results and ensuring * that future calls to the function recompute the results. + * + * **Note:** An output selector keeps a separate cache for its arguments + * (`argsMemoize`) and for its result (`memoize`). Calling `clearCache` on the + * selector only clears the former; to fully reset it, also call + * `selector.memoizedResultFunc.clearCache()`. */ clearCache: () => void resultsCount: () => number diff --git a/typescript_test/argsMemoize.typetest.ts b/typescript_test/argsMemoize.typetest.ts index 9729062c..91b0f824 100644 --- a/typescript_test/argsMemoize.typetest.ts +++ b/typescript_test/argsMemoize.typetest.ts @@ -1158,3 +1158,23 @@ function parameterLimit() { } ) } + +function clearCacheExistsOnDefaultSelectorAndMemoizedResultFunc() { + // A selector created with default memoizers keeps two separate caches: + // `argsMemoize` on the selector and `memoize` on `memoizedResultFunc`. + // Both must expose the memoizer fields (e.g. `clearCache`). See #569. + const selectorDefault = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id) + ) + + // Fields attached by `argsMemoize` (defaults to `weakMapMemoize`). + selectorDefault.clearCache() + expectExactType(selectorDefault.resultsCount()) + selectorDefault.resetResultsCount() + + // Fields attached by `memoize` (defaults to `weakMapMemoize`). + selectorDefault.memoizedResultFunc.clearCache() + expectExactType(selectorDefault.memoizedResultFunc.resultsCount()) + selectorDefault.memoizedResultFunc.resetResultsCount() +}