Skip to content

Instantly share code, notes, and snippets.

@MatthewCallis
Last active August 31, 2023 20:43
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 MatthewCallis/dc959d1e06f14a0ec931967e0fa83e63 to your computer and use it in GitHub Desktop.
Save MatthewCallis/dc959d1e06f14a0ec931967e0fa83e63 to your computer and use it in GitHub Desktop.
useEffectDebugger See what changed in useEffect
// https://stackoverflow.com/questions/55187563/determine-which-dependency-array-variable-caused-useeffect-hook-to-fire
// https://github.com/simbathesailor/use-what-changed/tree/master
// https://dev.to/vsramalwan/ways-to-handle-deep-object-comparison-in-useeffect-hook-1elm
import { toJS } from 'mobx'
import { useEffect, useRef } from 'react'
import type { EffectCallback } from 'react'
export interface Update {
oldValue: any
newValue: any
}
export interface ObjectDiff {
added: {} | ObjectDiff
updated: {
[propName: string]: Update | ObjectDiff
}
removed: {} | ObjectDiff
unchanged: {} | ObjectDiff
}
export const isObject = (obj: any) => {
return obj !== null && typeof obj === 'object'
}
/**
* @param oldObj The previous Object or Array.
* @param newObj The new Object or Array.
* @param deep If the comparison must be performed deeper than 1st-level properties.
* @return A difference summary between the two objects.
*/
export const diff = (oldObj: {} = {}, newObj: {} = {}, deep = true): ObjectDiff => {
const added = {}
const updated = {}
const removed = {}
const unchanged = {}
for (const oldProp in oldObj) {
if (oldObj.hasOwnProperty(oldProp)) {
const newPropValue = newObj[oldProp]
const oldPropValue = oldObj[oldProp]
if (newObj.hasOwnProperty(oldProp)) {
if (Object.is(newPropValue, oldPropValue)) {
unchanged[oldProp] = oldPropValue
} else {
updated[oldProp] =
deep && isObject(oldPropValue) && isObject(newPropValue)
? diff(oldPropValue, newPropValue, deep)
: { newValue: newPropValue }
}
} else {
removed[oldProp] = oldPropValue
}
}
}
for (const newProp in newObj) {
if (newObj.hasOwnProperty(newProp)) {
const oldPropValue = oldObj[newProp]
const newPropValue = newObj[newProp]
if (oldObj.hasOwnProperty(newProp)) {
if (!Object.is(oldPropValue, newPropValue)) {
if (!deep || !isObject(oldPropValue)) {
updated[newProp].oldValue = oldPropValue
}
}
} else {
added[newProp] = newPropValue
}
}
}
const output = {} as ObjectDiff
if (Object.keys(added).length) {
output['added'] = added
}
if (Object.keys(updated).length) {
output['updated'] = updated
}
if (Object.keys(removed).length) {
output['removed'] = removed
}
if (Object.keys(unchanged).length) {
output['unchanged'] = unchanged
}
return output as ObjectDiff
// return { added, updated, removed, unchanged }
}
export const usePrevious = (value: any, initialValue: any) => {
const ref = useRef(initialValue)
useEffect(() => {
ref.current = value
})
return ref.current
}
/**
* Hook to debug useEffect to see what has changed in the dependencies.
* @param {EffectCallback} effectHook The useEffect hook to debug
* @param {any[]} dependencies The useEffect dependencies
* @param {string[]} dependencyNames Names for the useEffect dependencies
*/
export const useEffectDebugger = (effectHook: EffectCallback, dependencies: any[], dependencyNames: string[] = []) => {
const previousDeps = usePrevious(dependencies, [])
const changedDeps = dependencies.reduce((accum: any, dependency: any, index: number) => {
if (dependency !== previousDeps[index]) {
const keyName = dependencyNames[index] || index
if (isObject(dependency) || isObject(previousDeps[index])) {
console.table(diff(toJS(previousDeps[index]), toJS(dependency)))
return {
...accum,
[keyName]: {
Before: diff(toJS(previousDeps[index]), toJS(dependency)),
After: diff(toJS(dependency), toJS(previousDeps[index])),
// // Diff: diff(toJS(previousDeps[index]), toJS(dependency)),
// ...diff(toJS(previousDeps[index]), toJS(dependency)),
},
}
}
return {
...accum,
[keyName]: {
Before: toJS(previousDeps[index]),
After: toJS(dependency),
},
}
}
return accum
}, {})
if (Object.keys(changedDeps).length) {
console.table(changedDeps)
}
useEffect(effectHook, dependencies)
}
export default useEffectDebugger
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment