-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathuseCustomEffect.ts
92 lines (79 loc) · 2.49 KB
/
useCustomEffect.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
import {
DependencyList,
EffectCallback,
useRef,
useEffect,
useDebugValue,
} from 'react'
import useMounted from './useMounted'
export type EffectHook = (effect: EffectCallback, deps?: DependencyList) => void
export type IsEqual<TDeps extends DependencyList> = (
nextDeps: TDeps,
prevDeps: TDeps,
) => boolean
export type CustomEffectOptions<TDeps extends DependencyList> = {
isEqual: IsEqual<TDeps>
effectHook?: EffectHook
}
type CleanUp = {
(): void
cleanup?: ReturnType<EffectCallback>
}
/**
* a useEffect() hook with customized depedency comparision
*
* @param effect The effect callback
* @param dependencies A list of dependencies
* @param isEqual A function comparing the next and previous dependencyLists
*/
function useCustomEffect<TDeps extends DependencyList = DependencyList>(
effect: EffectCallback,
dependencies: TDeps,
isEqual: IsEqual<TDeps>,
): void
/**
* a useEffect() hook with customized depedency comparision
*
* @param effect The effect callback
* @param dependencies A list of dependencies
* @param options
* @param options.isEqual A function comparing the next and previous dependencyLists
* @param options.effectHook the underlying effect hook used, defaults to useEffect
*/
function useCustomEffect<TDeps extends DependencyList = DependencyList>(
effect: EffectCallback,
dependencies: TDeps,
options: CustomEffectOptions<TDeps>,
): void
function useCustomEffect<TDeps extends DependencyList = DependencyList>(
effect: EffectCallback,
dependencies: TDeps,
isEqualOrOptions: IsEqual<TDeps> | CustomEffectOptions<TDeps>,
) {
const isMounted = useMounted()
const { isEqual, effectHook = useEffect } =
typeof isEqualOrOptions === 'function'
? { isEqual: isEqualOrOptions }
: isEqualOrOptions
const dependenciesRef = useRef<TDeps>()
dependenciesRef.current = dependencies
const cleanupRef = useRef<CleanUp | null>(null)
effectHook(() => {
// If the ref the is `null` it's either the first effect or the last effect
// ran and was cleared, meaning _this_ update should run, b/c the equality
// check failed on in the cleanup of the last effect.
if (cleanupRef.current === null) {
const cleanup = effect()
cleanupRef.current = () => {
if (isMounted() && isEqual(dependenciesRef.current!, dependencies)) {
return
}
cleanupRef.current = null
if (cleanup) cleanup()
}
}
return cleanupRef.current
})
useDebugValue(effect)
}
export default useCustomEffect