-
-
Notifications
You must be signed in to change notification settings - Fork 567
/
useAtomValue.ts
87 lines (79 loc) 路 2.36 KB
/
useAtomValue.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
import {
useCallback,
useContext,
useDebugValue,
useEffect,
useReducer,
} from 'react'
import type { Reducer } from 'react'
import type { Atom, Scope } from './atom'
import { getScopeContext } from './contexts'
import { COMMIT_ATOM, READ_ATOM, SUBSCRIBE_ATOM } from './store'
import type { VersionObject } from './store'
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T
export function useAtomValue<Value>(
atom: Atom<Value>,
scope?: Scope
): Awaited<Value> {
const ScopeContext = getScopeContext(scope)
const { s: store } = useContext(ScopeContext)
const getAtomValue = useCallback(
(version?: VersionObject) => {
// This call to READ_ATOM is the place where derived atoms will actually be
// recomputed if needed.
const atomState = store[READ_ATOM](atom, version)
if ('e' in atomState) {
throw atomState.e // read error
}
if ('p' in atomState) {
throw atomState.p // read promise
}
if ('v' in atomState) {
return atomState.v as Awaited<Value>
}
throw new Error('no atom value')
},
[store, atom]
)
// Pull the atoms's state from the store into React state.
const [[version, value, atomFromUseReducer], rerenderIfChanged] = useReducer<
Reducer<
readonly [VersionObject | undefined, Awaited<Value>, Atom<Value>],
VersionObject | undefined
>,
undefined
>(
useCallback(
(prev, nextVersion) => {
const nextValue = getAtomValue(nextVersion)
if (Object.is(prev[1], nextValue) && prev[2] === atom) {
return prev // bail out
}
return [nextVersion, nextValue, atom]
},
[getAtomValue, atom]
),
undefined,
() => {
// NOTE should/could branch on mount?
const initialVersion = undefined
const initialValue = getAtomValue(initialVersion)
return [initialVersion, initialValue, atom]
}
)
if (atomFromUseReducer !== atom) {
rerenderIfChanged(undefined)
}
useEffect(() => {
// Call `rerenderIfChanged` whenever this atom is invalidated. Note
// that derived atoms may not be recomputed yet.
const unsubscribe = store[SUBSCRIBE_ATOM](atom, rerenderIfChanged)
rerenderIfChanged(undefined)
return unsubscribe
}, [store, atom])
useEffect(() => {
store[COMMIT_ATOM](atom, version)
})
useDebugValue(value)
return value
}