-
Notifications
You must be signed in to change notification settings - Fork 23
Expand file tree
/
Copy pathuseStateObservable.ts
More file actions
76 lines (68 loc) · 1.85 KB
/
Copy pathuseStateObservable.ts
File metadata and controls
76 lines (68 loc) · 1.85 KB
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
import {
DefaultedStateObservable,
liftSuspense,
NoSubscribersError,
StateObservable,
StatePromise,
SUSPENSE,
} from "@rx-state/core"
import { useRef, useState } from "react"
import useSyncExternalStore from "./internal/useSyncExternalStore"
import { useSubscription } from "./Subscribe"
type VoidCb = () => void
interface Ref<T> {
source$: StateObservable<T>
args: [
(cb: VoidCb) => VoidCb,
() => Exclude<T, typeof SUSPENSE>,
() => Exclude<T, typeof SUSPENSE>,
]
}
export const useStateObservable = <O>(
source$: StateObservable<O>,
): Exclude<O, typeof SUSPENSE> => {
const subscription = useSubscription()
const [, setError] = useState()
const callbackRef = useRef<Ref<O>>()
if (!callbackRef.current) {
const getValue = (src: StateObservable<O>) => {
const result = src.getValue()
if (result instanceof StatePromise)
throw result.catch((e) => {
if (e instanceof NoSubscribersError) return e
throw e
})
return result as any
}
const gv: <T>() => Exclude<T, typeof SUSPENSE> = () => {
const src = callbackRef.current!.source$ as DefaultedStateObservable<O>
if (!src.getRefCount() && !src.getDefaultValue) {
if (!subscription) throw new Error("Missing Subscribe!")
subscription(src)
}
return getValue(src)
}
callbackRef.current = {
source$: null as any,
args: [, gv, gv] as any,
}
}
const ref = callbackRef.current
if (ref.source$ !== source$) {
ref.source$ = source$
ref.args[0] = (next: () => void) => {
const subscription = liftSuspense()(source$).subscribe({
next,
error: (e) => {
setError(() => {
throw e
})
},
})
return () => {
subscription.unsubscribe()
}
}
}
return useSyncExternalStore(...ref!.args)
}