Created
December 23, 2020 17:27
-
-
Save alexandrusavin/eb70c2d020def6b51306d95cf344f94b to your computer and use it in GitHub Desktop.
ReactFnCompPropsChecker
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
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