Skip to content

Instantly share code, notes, and snippets.

@tvler
Last active May 20, 2021 12:43
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tvler/eeff76e58d2f1d3ce8320ba96cfd2efe to your computer and use it in GitHub Desktop.
Save tvler/eeff76e58d2f1d3ce8320ba96cfd2efe to your computer and use it in GitHub Desktop.
A custom React.memo memoization function that throws an error (in dev) if props change between renders.
/**
* A strict React.memo memoization function, built off
* the one that comes built-in, that throws an error
* (in dev) if props change between renders. Props you
* want to allow can be passed in as arguments.
*
* This "don't allow by default" model makes it so
* further changes to a component don't silently
* undo any memoization optimizations done before.
*
* The underlying shallow equality function is the
* exact same function as the one react normally runs
* for React.PureComponent or React.memo components.
* (https://github.com/facebook/react/blob/fc33f12bdee1d0ffbcc83d25199cdf4d47252736/packages/shared/shallowEqual.js)
*
* Ex:
* const Component = (props) => {
* return <>Hello {props.name}</>;
* };
*
* const MemoizedComponent = memo(
* Component,
* shallowEqualWarnInDev(
* 'name', // Allow `name` to change
* ),
* );
*/
export function shallowEqualWarnInDev<T>(...propsAllowedToChangeArr: Array<keyof T>) {
const propsAllowedToChange = new Set(propsAllowedToChangeArr);
return (prevProps: T, nextProps: T) => {
if (Object.is(prevProps, nextProps)) {
return true;
}
if (
typeof prevProps !== 'object' ||
prevProps === null ||
typeof nextProps !== 'object' ||
nextProps === null
) {
return false;
}
const keysA = Object.keys(prevProps) as Array<keyof T>;
const keysB = Object.keys(nextProps) as Array<keyof T>;
if (keysA.length !== keysB.length) {
return false;
}
// Test for A's keys different from B.
for (let i = 0; i < keysA.length; i++) {
if (
!Object.prototype.hasOwnProperty.call(nextProps, keysA[i]) ||
!Object.is(prevProps[keysA[i]], nextProps[keysA[i]])
) {
if (__DEV__ && !propsAllowedToChange.has(keysA[i])) {
throw new Error(`The prop \`${keysA[i]}\` is not expected to change between renders.`);
}
return false;
}
}
return true;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment