-
-
Notifications
You must be signed in to change notification settings - Fork 238
/
useMutableSource.ts
116 lines (110 loc) 路 2.87 KB
/
useMutableSource.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/*
export {
unstable_createMutableSource as createMutableSource,
unstable_useMutableSource as useMutableSource,
} from 'react'
*/
// useMutableSource emulation almost equivalent to useSubscription
import { useEffect, useRef, useState } from 'react'
const TARGET = '_uMS_T'
const GET_VERSION = '_uMS_V'
type MutableSource<T, V> = {
[TARGET]: T
[GET_VERSION]: (target: T) => V
}
export const createMutableSource = <T, V>(
target: T,
getVersion: (target: T) => V
): MutableSource<T, V> => ({
[TARGET]: target,
[GET_VERSION]: getVersion,
})
export const useMutableSource = <T, V, S>(
source: MutableSource<T, V>,
getSnapshot: (target: T) => S,
subscribe: (target: T, callback: () => void) => () => void
) => {
const lastVersion = useRef<V>()
const currentVersion = source[GET_VERSION](source[TARGET])
const [state, setState] = useState(
() =>
[
/* [0] */ source,
/* [1] */ getSnapshot,
/* [2] */ subscribe,
/* [3] */ currentVersion,
/* [4] */ getSnapshot(source[TARGET]),
] as const
)
let currentSnapshot = state[4]
if (
state[0] !== source ||
state[1] !== getSnapshot ||
state[2] !== subscribe
) {
currentSnapshot = getSnapshot(source[TARGET])
setState([
/* [0] */ source,
/* [1] */ getSnapshot,
/* [2] */ subscribe,
/* [3] */ currentVersion,
/* [4] */ currentSnapshot,
])
} else if (
currentVersion !== state[3] &&
currentVersion !== lastVersion.current
) {
currentSnapshot = getSnapshot(source[TARGET])
if (!Object.is(currentSnapshot, state[4])) {
setState([
/* [0] */ source,
/* [1] */ getSnapshot,
/* [2] */ subscribe,
/* [3] */ currentVersion,
/* [4] */ currentSnapshot,
])
}
}
useEffect(() => {
let didUnsubscribe = false
const checkForUpdates = () => {
if (didUnsubscribe) {
return
}
try {
const nextSnapshot = getSnapshot(source[TARGET])
const nextVersion = source[GET_VERSION](source[TARGET])
lastVersion.current = nextVersion
setState((prev) => {
if (
prev[0] !== source ||
prev[1] !== getSnapshot ||
prev[2] !== subscribe
) {
return prev
}
if (Object.is(prev[4], nextSnapshot)) {
return prev
}
return [
/* [0] */ prev[0],
/* [1] */ prev[1],
/* [2] */ prev[2],
/* [3] */ nextVersion,
/* [4] */ nextSnapshot,
]
})
} catch (e) {
// schedule update
setState((prev) => [...prev])
}
}
const unsubscribe = subscribe(source[TARGET], checkForUpdates)
checkForUpdates()
return () => {
didUnsubscribe = true
unsubscribe()
}
}, [source, getSnapshot, subscribe])
return currentSnapshot
}