Skip to content

Instantly share code, notes, and snippets.

@alexandrusavin
Created December 23, 2020 17:27
Show Gist options
  • Save alexandrusavin/eb70c2d020def6b51306d95cf344f94b to your computer and use it in GitHub Desktop.
Save alexandrusavin/eb70c2d020def6b51306d95cf344f94b to your computer and use it in GitHub Desktop.
ReactFnCompPropsChecker
import React from 'react';
// TYPES
interface PropsCheckerProps<T> {
children: (props: T) => React.ReactElement | null;
childrenProps: T;
compType?: ComparaisonTypes;
verbose?: boolean;
}
interface Props {
[key: string]: unknown;
}
type ComparaisonTypes = 'SIMPLE' | 'SHALLOW' | 'DEEP';
// COMPARISON FUNCTIONS
function simpleCheck(a: unknown, b: unknown) {
return a !== b;
}
const log = (message: string, verbose?: boolean) => {
if (verbose) {
console.log(message);
}
};
function deepCheck(a: unknown, b: unknown, verbose?: boolean) {
try {
return JSON.stringify(a) !== JSON.stringify(b);
} catch (e) {
log(e, verbose);
return false;
}
}
function shallowCheck(a: unknown, b: unknown, verbose?: boolean) {
if (typeof a !== typeof b) {
log('Not the same type', verbose);
return false;
}
if (typeof a !== 'object') {
return simpleCheck(a, b);
}
const A = a as Props;
const B = b as Props;
const keys = Object.keys(A);
if (!deepCheck(A, B, verbose)) {
log('Objects keys changed', verbose);
return false;
}
return keys.reduce((prev, k) => {
if (simpleCheck(A[k], B[k])) {
log(`Object differ at key : ${k}`, verbose);
return false;
}
return prev;
}, true);
}
function compFNSelection(compType: ComparaisonTypes) {
switch (compType) {
case 'SIMPLE':
return (a: unknown, b: unknown, _verbose?: boolean) => simpleCheck(a, b);
case 'SHALLOW':
return (a: unknown, b: unknown, verbose?: boolean) => shallowCheck(a, b, verbose);
case 'DEEP':
return (a: unknown, b: unknown, verbose?: boolean) => deepCheck(a, b, verbose);
default:
console.log('Comparaison type unvailable. Test will always return false');
return () => false;
}
}
/**
* This component wraps another one and logs which props changed
* Warning, this component is not meant to be used in production mode as it's slowing the rendering and using extra memory
* @param WrappedComponent The component to analyse
* @param compType The possible comparaison type (Be carefull with "DEEP". It may get errors in case of circular references)
*/
export default function ReactFnCompPropsChecker<T extends Props>(props: PropsCheckerProps<T>) {
const { children, childrenProps, compType = 'SIMPLE', verbose } = props;
const oldPropsRef = React.useRef<T>();
React.useEffect(() => {
const oldProps = oldPropsRef.current;
if (oldProps === undefined) {
console.log('First render : ');
Object.keys(childrenProps).forEach((k) => console.log(`${k} : ${childrenProps[k]}`));
} else {
const changedProps = Object.keys(childrenProps)
.filter((k) => compFNSelection(compType)(oldProps[k], childrenProps[k], verbose))
.map((k) => `${k} : [OLD] ${oldProps[k]}, [NEW] ${childrenProps[k]}`);
if (changedProps.length > 0) {
console.log('Changed props : ');
changedProps.forEach(console.log);
} else {
console.log('No props changed');
}
}
oldPropsRef.current = childrenProps;
}, [childrenProps, compType, verbose]);
return children(childrenProps);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment