Skip to content

Instantly share code, notes, and snippets.

@rburgst
Created October 19, 2022 12:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rburgst/1d41b247244f6dac33e02b7e7314a300 to your computer and use it in GitHub Desktop.
Save rburgst/1d41b247244f6dac33e02b7e7314a300 to your computer and use it in GitHub Desktop.
use-what-changed preact version
/* eslint-disable no-console */
import type { RefObject } from 'preact';
import React from 'preact/compat';
/**
* Stolen from https://github.com/simbathesailor/use-what-changed/blob/master/src/useWhatChanged.tsx
*/
type TypeDependency = any[];
type TypeDependencyNames = string;
let whatDebugChanged = 0;
let configuration = { active: true };
function setUseWhatChange({ active = true }: any = {}): void {
configuration = { ...configuration, active };
}
/**
* Taken random color logic from some stackoverflow answer
*/
function getRandomColor(): string {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
/**
*
* Check whether the dependency item is an object. then
*/
const isObject = (t: any): boolean => {
return Object.prototype.toString.call(t) === '[object Object]';
};
function getPrintableInfo(dependencyItem: any): string {
/**
* Printing the info into viewable format
*/
if (isObject(dependencyItem) || Array.isArray(dependencyItem)) {
let ans;
try {
ans = JSON.stringify(dependencyItem, null, 2);
} catch (e) {
ans = 'CIRCULAR JSON';
}
return ans;
}
return dependencyItem;
}
// const isDevelopment = process.env['NODE_ENV'] === 'development';
function useHotRefs(value: any): RefObject<any> {
const fnRef = React.useRef(value);
React.useEffect(() => {
fnRef.current = value;
});
return fnRef;
}
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
function useWhatChanged(
dependency?: TypeDependency,
dependencyNames?: TypeDependencyNames,
suffix?: string,
hookName?: string
) {
// It's a fair assumption the hooks type will not change for a component during
// its life time
const hookNameFinal = React.useMemo(() => {
if (hookName === 'useLayoutEffect') {
return 'useLayoutEffect';
}
// if(hookName === "useEffect" || !hookName) {
return 'useEffect';
// }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// This ref is responsible for book keeping of the old value
const dependencyRef = React.useRef(dependency);
// For count bookkeeping , for easy debugging
const whatChangedHookCountRef = React.useRef(1);
// For assigning color for easy debugging
const backgroundColorRef = React.useRef('');
const isDependencyArr = Array.isArray(dependencyRef.current);
React[hookNameFinal](() => {
if (
dependencyRef.current &&
isDependencyArr
// dependencyRef.current.length > 0
) {
whatDebugChanged++;
whatChangedHookCountRef.current = whatDebugChanged;
backgroundColorRef.current = getRandomColor();
}
// const MyWindow: IWindow = window;
}, [dependencyRef, isDependencyArr]);
function postConsole(): void {
console.log('\n');
console.log(
`%c///// END SECTION/////`,
`background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
'\n'
);
console.log('\n');
console.log('\n');
}
function logBanners({
isFirstMount,
suffixText,
isBlankArrayAsDependency,
}: {
isFirstMount?: boolean;
suffixText?: string;
isBlankArrayAsDependency?: boolean;
}): void {
if (configuration.active) {
console.log(
`%c///// START SECTION /////`,
`background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
'\n'
);
console.log('\n');
console.log(
`%c ${whatChangedHookCountRef.current} ${suffix ?? ''}`,
`background: ${backgroundColorRef.current}; color: white; font-size: 10px`,
'👇🏾',
`${isFirstMount ? 'FIRST RUN' : 'UPDATES'}`,
`${suffixText}`
);
if (isBlankArrayAsDependency) {
postConsole();
}
}
}
const longBannersRef = useHotRefs(logBanners);
React[hookNameFinal](
// eslint-disable-next-line sonarjs/cognitive-complexity,complexity
() => {
if (!(dependencyRef.current && isDependencyArr)) {
return;
}
// if (dependencyRef.current.length === 0) {
// return;
// }
// More info, if needed by user
const stringSplitted = dependencyNames ? dependencyNames.split(',') : null;
let changed = false;
const whatChanged = dependency
? dependency.reduce((acc, dep, index) => {
if (dependencyRef.current && dep !== dependencyRef.current[index]) {
const oldValue = dependencyRef.current[index];
dependencyRef.current[index] = dep;
if (dependencyNames && stringSplitted) {
changed = true;
acc[`"✅" ${stringSplitted[index]}`] = {
'Old Value': getPrintableInfo(oldValue),
'New Value': getPrintableInfo(dep),
};
} else {
acc[`"✅" ${index}`] = {
'Old Value': getPrintableInfo(oldValue),
'New Value': getPrintableInfo(dep),
};
}
return acc;
}
if (dependencyNames && stringSplitted) {
acc[`"⏺" ${stringSplitted[index]}`] = {
'Old Value': getPrintableInfo(dep),
'New Value': getPrintableInfo(dep),
};
} else {
acc[`"⏺" ${index}`] = {
'Old Value': getPrintableInfo(dep),
'New Value': getPrintableInfo(dep),
};
}
return acc;
}, {})
: {};
if (configuration.active) {
const isBlankArrayAsDependency =
whatChanged && Object.keys(whatChanged).length === 0 && isDependencyArr;
longBannersRef.current({
isFirstMount: !changed,
suffixText: isBlankArrayAsDependency ? ` 👉🏽 This will run only once on mount.` : ``,
isBlankArrayAsDependency,
});
if (!isBlankArrayAsDependency) {
console.table(whatChanged);
postConsole();
}
}
},
[
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
...(() => {
if (dependency && isDependencyArr) {
return dependency;
}
return [];
})(),
dependencyRef,
longBannersRef,
hookName,
]
);
}
export { useWhatChanged, setUseWhatChange };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment