-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathhooks.js
More file actions
99 lines (83 loc) · 2.52 KB
/
hooks.js
File metadata and controls
99 lines (83 loc) · 2.52 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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import { useState, useLayoutEffect, useEffect } from 'preact/hooks';
import { options as _options } from 'preact';
const MODE_HYDRATE = 1 << 5;
/** @type {boolean} */
let hydrating;
// Cast to use internal Options type
const options = /** @type {import('../../src/internal').Options} */ (_options);
let oldBeforeRender = options._render;
/** @type {(vnode: import('./internal').VNode) => void} */
options._render = _vnode => {
hydrating = !!(_vnode._flags & MODE_HYDRATE);
if (oldBeforeRender) oldBeforeRender(_vnode);
};
/**
* This is taken from https://github.com/facebook/react/blob/main/packages/use-sync-external-store/src/useSyncExternalStoreShimClient.js#L84
* on a high level this cuts out the warnings, ... and attempts a smaller implementation
* @typedef {{ _value: any; _getSnapshot: () => any }} Store
*/
export function useSyncExternalStore(
subscribe,
getSnapshot,
getServerSnapshot
) {
const value =
typeof window === 'undefined' || hydrating
? getServerSnapshot
? getServerSnapshot()
: missingGetServerSnapshot()
: getSnapshot();
/**
* @typedef {{ _instance: Store }} StoreRef
* @type {[StoreRef, (store: StoreRef) => void]}
*/
const [{ _instance }, forceUpdate] = useState({
_instance: { _value: value, _getSnapshot: getSnapshot }
});
useLayoutEffect(() => {
_instance._value = value;
_instance._getSnapshot = getSnapshot;
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
}, [subscribe, value, getSnapshot]);
useEffect(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
return subscribe(() => {
if (didSnapshotChange(_instance)) {
forceUpdate({ _instance });
}
});
}, [subscribe]);
return value;
}
/** @type {(inst: Store) => boolean} */
function didSnapshotChange(inst) {
const latestGetSnapshot = inst._getSnapshot;
const prevValue = inst._value;
try {
const nextValue = latestGetSnapshot();
return !Object.is(prevValue, nextValue);
} catch (error) {
return true;
}
}
function missingGetServerSnapshot() {
throw new Error(
'Missing "getServerSnapshot" parameter for "useSyncExternalStore", this is required for server rendering & hydration of server-rendered content'
);
}
export function startTransition(cb) {
cb();
}
export function useDeferredValue(val) {
return val;
}
export function useTransition() {
return [false, startTransition];
}
// TODO: in theory this should be done after a VNode is diffed as we want to insert
// styles/... before it attaches
export const useInsertionEffect = useLayoutEffect;