Skip to content

Instantly share code, notes, and snippets.

@akullpp
Created February 8, 2024 23: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 akullpp/8ea817f67bcf485ff1690c28633c4da8 to your computer and use it in GitHub Desktop.
Save akullpp/8ea817f67bcf485ff1690c28633c4da8 to your computer and use it in GitHub Desktop.
Deep compare version of useEffect for React
import { useEffect, useMemo, useRef } from 'react'
import { isEqual } from 'lodash-es'
type UseEffectParams = Parameters<typeof useEffect>
type EffectCallback = UseEffectParams[0]
type DependencyList = UseEffectParams[1]
type UseEffectReturn = ReturnType<typeof useEffect>
const isPrimitive = (val: unknown) => {
return val == null || /^[sbn]/.test(typeof val)
}
const checkDeps = (deps: DependencyList) => {
if (!deps || !deps.length) {
throw new Error('Use useEffect instead of useDeepEffect.')
}
if (deps.every(isPrimitive)) {
throw new Error('Use useEffect instead of useDeepEffect.')
}
}
/**
* @param value the value to be memoized (usually a dependency list)
* @returns a memoized version of the value as long as it remains deeply equal
*/
const useDeepCompareMemoize = <T>(value: T) => {
const ref = useRef<T>(value)
const signalRef = useRef<number>(0)
if (!isEqual(value, ref.current)) {
ref.current = value
signalRef.current += 1
}
return useMemo(() => {
return ref.current
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [signalRef.current])
}
const useDeepEffect = (callback: EffectCallback, dependencies: DependencyList): UseEffectReturn => {
if (import.meta.env.DEV) {
checkDeps(dependencies)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
return useEffect(callback, useDeepCompareMemoize(dependencies))
}
export { useDeepEffect }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment