-
Notifications
You must be signed in to change notification settings - Fork 670
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #645 from aryaemami59/noop-check
- Loading branch information
Showing
23 changed files
with
1,168 additions
and
757 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import type { AnyFunction } from '../types' | ||
|
||
/** | ||
* Runs a check to determine if the given result function behaves as an | ||
* identity function. An identity function is one that returns its | ||
* input unchanged, for example, `x => x`. This check helps ensure | ||
* efficient memoization and prevent unnecessary re-renders by encouraging | ||
* proper use of transformation logic in result functions and | ||
* extraction logic in input selectors. | ||
* | ||
* @param resultFunc - The result function to be checked. | ||
*/ | ||
export const runIdentityFunctionCheck = (resultFunc: AnyFunction) => { | ||
let isInputSameAsOutput = false | ||
try { | ||
const emptyObject = {} | ||
if (resultFunc(emptyObject) === emptyObject) isInputSameAsOutput = true | ||
} catch { | ||
// Do nothing | ||
} | ||
if (isInputSameAsOutput) { | ||
console.warn( | ||
'The result function returned its own inputs without modification. e.g' + | ||
'\n`createSelector([state => state.todos], todos => todos)`' + | ||
'\nThis could lead to inefficient memoization and unnecessary re-renders.' + | ||
'\nEnsure transformation logic is in the result function, and extraction logic is in the input selectors.' | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import type { CreateSelectorOptions, UnknownMemoizer } from '../types' | ||
|
||
/** | ||
* Runs a stability check to ensure the input selector results remain stable | ||
* when provided with the same arguments. This function is designed to detect | ||
* changes in the output of input selectors, which can impact the performance of memoized selectors. | ||
* | ||
* @param inputSelectorResultsObject - An object containing two arrays: `inputSelectorResults` and `inputSelectorResultsCopy`, representing the results of input selectors. | ||
* @param options - Options object consisting of a `memoize` function and a `memoizeOptions` object. | ||
* @param inputSelectorArgs - List of arguments being passed to the input selectors. | ||
*/ | ||
export const runInputStabilityCheck = ( | ||
inputSelectorResultsObject: { | ||
inputSelectorResults: unknown[] | ||
inputSelectorResultsCopy: unknown[] | ||
}, | ||
options: Required< | ||
Pick< | ||
CreateSelectorOptions<UnknownMemoizer, UnknownMemoizer>, | ||
'memoize' | 'memoizeOptions' | ||
> | ||
>, | ||
inputSelectorArgs: unknown[] | IArguments | ||
) => { | ||
const { memoize, memoizeOptions } = options | ||
const { inputSelectorResults, inputSelectorResultsCopy } = | ||
inputSelectorResultsObject | ||
const createAnEmptyObject = memoize(() => ({}), ...memoizeOptions) | ||
// if the memoize method thinks the parameters are equal, these *should* be the same reference | ||
const areInputSelectorResultsEqual = | ||
createAnEmptyObject.apply(null, inputSelectorResults) === | ||
createAnEmptyObject.apply(null, inputSelectorResultsCopy) | ||
if (!areInputSelectorResultsEqual) { | ||
// do we want to log more information about the selector? | ||
console.warn( | ||
'An input selector returned a different result when passed same arguments.' + | ||
'\nThis means your output selector will likely run more frequently than intended.' + | ||
'\nAvoid returning a new reference inside your input selector, e.g.' + | ||
'\n`createSelector([state => state.todos.map(todo => todo.id)], todoIds => todoIds.length)`', | ||
{ | ||
arguments: inputSelectorArgs, | ||
firstInputs: inputSelectorResults, | ||
secondInputs: inputSelectorResultsCopy | ||
} | ||
) | ||
} | ||
} |
Oops, something went wrong.