source: node_modules/reselect/src/autotrackMemoize/autotrackMemoize.ts@ d24f17c

main
Last change on this file since d24f17c was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 3.7 KB
Line 
1import { createNode, updateNode } from './proxy'
2import type { Node } from './tracking'
3
4import { createCacheKeyComparator, referenceEqualityCheck } from '../lruMemoize'
5import type { AnyFunction, DefaultMemoizeFields, Simplify } from '../types'
6import { createCache } from './autotracking'
7
8/**
9 * Uses an "auto-tracking" approach inspired by the work of the Ember Glimmer team.
10 * It uses a Proxy to wrap arguments and track accesses to nested fields
11 * in your selector on first read. Later, when the selector is called with
12 * new arguments, it identifies which accessed fields have changed and
13 * only recalculates the result if one or more of those accessed fields have changed.
14 * This allows it to be more precise than the shallow equality checks in `lruMemoize`.
15 *
16 * __Design Tradeoffs for `autotrackMemoize`:__
17 * - Pros:
18 * - It is likely to avoid excess calculations and recalculate fewer times than `lruMemoize` will,
19 * which may also result in fewer component re-renders.
20 * - Cons:
21 * - It only has a cache size of 1.
22 * - It is slower than `lruMemoize`, 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)
23 * - It can have some unexpected behavior. Because it tracks nested field accesses,
24 * cases where you don't access a field will not recalculate properly.
25 * For example, a badly-written selector like:
26 * ```ts
27 * createSelector([state => state.todos], todos => todos)
28 * ```
29 * that just immediately returns the extracted value will never update, because it doesn't see any field accesses to check.
30 *
31 * __Use Cases for `autotrackMemoize`:__
32 * - It is likely best used for cases where you need to access specific nested fields
33 * in data, and avoid recalculating if other fields in the same data objects are immutably updated.
34 *
35 * @param func - The function to be memoized.
36 * @returns A memoized function with a `.clearCache()` method attached.
37 *
38 * @example
39 * <caption>Using `createSelector`</caption>
40 * ```ts
41 * import { unstable_autotrackMemoize as autotrackMemoize, createSelector } from 'reselect'
42 *
43 * const selectTodoIds = createSelector(
44 * [(state: RootState) => state.todos],
45 * (todos) => todos.map(todo => todo.id),
46 * { memoize: autotrackMemoize }
47 * )
48 * ```
49 *
50 * @example
51 * <caption>Using `createSelectorCreator`</caption>
52 * ```ts
53 * import { unstable_autotrackMemoize as autotrackMemoize, createSelectorCreator } from 'reselect'
54 *
55 * const createSelectorAutotrack = createSelectorCreator({ memoize: autotrackMemoize })
56 *
57 * const selectTodoIds = createSelectorAutotrack(
58 * [(state: RootState) => state.todos],
59 * (todos) => todos.map(todo => todo.id)
60 * )
61 * ```
62 *
63 * @template Func - The type of the function that is memoized.
64 *
65 * @see {@link https://reselect.js.org/api/unstable_autotrackMemoize autotrackMemoize}
66 *
67 * @since 5.0.0
68 * @public
69 * @experimental
70 */
71export function autotrackMemoize<Func extends AnyFunction>(func: Func) {
72 // we reference arguments instead of spreading them for performance reasons
73
74 const node: Node<Record<string, unknown>> = createNode(
75 [] as unknown as Record<string, unknown>
76 )
77
78 let lastArgs: IArguments | null = null
79
80 const shallowEqual = createCacheKeyComparator(referenceEqualityCheck)
81
82 const cache = createCache(() => {
83 const res = func.apply(null, node.proxy as unknown as any[])
84 return res
85 })
86
87 function memoized() {
88 if (!shallowEqual(lastArgs, arguments)) {
89 updateNode(node, arguments as unknown as Record<string, unknown>)
90 lastArgs = arguments
91 }
92 return cache.value
93 }
94
95 memoized.clearCache = () => {
96 return cache.clear()
97 }
98
99 return memoized as Func & Simplify<DefaultMemoizeFields>
100}
Note: See TracBrowser for help on using the repository browser.