1 | import type { CreateSelectorOptions, UnknownMemoizer } from '../types'
|
---|
2 |
|
---|
3 | /**
|
---|
4 | * Runs a stability check to ensure the input selector results remain stable
|
---|
5 | * when provided with the same arguments. This function is designed to detect
|
---|
6 | * changes in the output of input selectors, which can impact the performance of memoized selectors.
|
---|
7 | *
|
---|
8 | * @param inputSelectorResultsObject - An object containing two arrays: `inputSelectorResults` and `inputSelectorResultsCopy`, representing the results of input selectors.
|
---|
9 | * @param options - Options object consisting of a `memoize` function and a `memoizeOptions` object.
|
---|
10 | * @param inputSelectorArgs - List of arguments being passed to the input selectors.
|
---|
11 | *
|
---|
12 | * @see {@link https://reselect.js.org/api/development-only-stability-checks/#inputstabilitycheck `inputStabilityCheck`}
|
---|
13 | *
|
---|
14 | * @since 5.0.0
|
---|
15 | * @internal
|
---|
16 | */
|
---|
17 | export const runInputStabilityCheck = (
|
---|
18 | inputSelectorResultsObject: {
|
---|
19 | inputSelectorResults: unknown[]
|
---|
20 | inputSelectorResultsCopy: unknown[]
|
---|
21 | },
|
---|
22 | options: Required<
|
---|
23 | Pick<
|
---|
24 | CreateSelectorOptions<UnknownMemoizer, UnknownMemoizer>,
|
---|
25 | 'memoize' | 'memoizeOptions'
|
---|
26 | >
|
---|
27 | >,
|
---|
28 | inputSelectorArgs: unknown[] | IArguments
|
---|
29 | ) => {
|
---|
30 | const { memoize, memoizeOptions } = options
|
---|
31 | const { inputSelectorResults, inputSelectorResultsCopy } =
|
---|
32 | inputSelectorResultsObject
|
---|
33 | const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions)
|
---|
34 | // if the memoize method thinks the parameters are equal, these *should* be the same reference
|
---|
35 | const areInputSelectorResultsEqual =
|
---|
36 | createAnEmptyObject.apply(null, inputSelectorResults) ===
|
---|
37 | createAnEmptyObject.apply(null, inputSelectorResultsCopy)
|
---|
38 | if (!areInputSelectorResultsEqual) {
|
---|
39 | let stack: string | undefined = undefined
|
---|
40 | try {
|
---|
41 | throw new Error()
|
---|
42 | } catch (e) {
|
---|
43 | // eslint-disable-next-line @typescript-eslint/no-extra-semi, no-extra-semi
|
---|
44 | ;({ stack } = e as Error)
|
---|
45 | }
|
---|
46 | console.warn(
|
---|
47 | 'An input selector returned a different result when passed same arguments.' +
|
---|
48 | '\nThis means your output selector will likely run more frequently than intended.' +
|
---|
49 | '\nAvoid returning a new reference inside your input selector, e.g.' +
|
---|
50 | '\n`createSelector([state => state.todos.map(todo => todo.id)], todoIds => todoIds.length)`',
|
---|
51 | {
|
---|
52 | arguments: inputSelectorArgs,
|
---|
53 | firstInputs: inputSelectorResults,
|
---|
54 | secondInputs: inputSelectorResultsCopy,
|
---|
55 | stack
|
---|
56 | }
|
---|
57 | )
|
---|
58 | }
|
---|
59 | }
|
---|