UNPKG

5.68 kBJavaScriptView Raw
1import { invariant } from "../../utilities/globals/index.js";
2import * as React from "rehackt";
3import { canUseLayoutEffect } from "../../utilities/index.js";
4var didWarnUncachedGetSnapshot = false;
5// Prevent webpack from complaining about our feature detection of the
6// useSyncExternalStore property of the React namespace, which is expected not
7// to exist when using React 17 and earlier, and that's fine.
8var uSESKey = "useSyncExternalStore";
9var realHook = React[uSESKey];
10// Adapted from https://www.npmjs.com/package/use-sync-external-store, with
11// Apollo Client deviations called out by "// DEVIATION ..." comments.
12// When/if React.useSyncExternalStore is defined, delegate fully to it.
13export var useSyncExternalStore = realHook ||
14 (function (subscribe, getSnapshot, getServerSnapshot) {
15 // Read the current snapshot from the store on every render. Again, this
16 // breaks the rules of React, and only works here because of specific
17 // implementation details, most importantly that updates are
18 // always synchronous.
19 var value = getSnapshot();
20 if (
21 // DEVIATION: Using __DEV__
22 globalThis.__DEV__ !== false &&
23 !didWarnUncachedGetSnapshot &&
24 // DEVIATION: Not using Object.is because we know our snapshots will never
25 // be exotic primitive values like NaN, which is !== itself.
26 value !== getSnapshot()) {
27 didWarnUncachedGetSnapshot = true;
28 // DEVIATION: Using invariant.error instead of console.error directly.
29 globalThis.__DEV__ !== false && invariant.error(60);
30 }
31 // Because updates are synchronous, we don't queue them. Instead we force a
32 // re-render whenever the subscribed state changes by updating an some
33 // arbitrary useState hook. Then, during render, we call getSnapshot to read
34 // the current value.
35 //
36 // Because we don't actually use the state returned by the useState hook, we
37 // can save a bit of memory by storing other stuff in that slot.
38 //
39 // To implement the early bailout, we need to track some things on a mutable
40 // object. Usually, we would put that in a useRef hook, but we can stash it in
41 // our useState hook instead.
42 //
43 // To force a re-render, we call forceUpdate({inst}). That works because the
44 // new object always fails an equality check.
45 var _a = React.useState({
46 inst: { value: value, getSnapshot: getSnapshot },
47 }), inst = _a[0].inst, forceUpdate = _a[1];
48 // Track the latest getSnapshot function with a ref. This needs to be updated
49 // in the layout phase so we can access it during the tearing check that
50 // happens on subscribe.
51 if (canUseLayoutEffect) {
52 // DEVIATION: We avoid calling useLayoutEffect when !canUseLayoutEffect,
53 // which may seem like a conditional hook, but this code ends up behaving
54 // unconditionally (one way or the other) because canUseLayoutEffect is
55 // constant.
56 React.useLayoutEffect(function () {
57 Object.assign(inst, { value: value, getSnapshot: getSnapshot });
58 // Whenever getSnapshot or subscribe changes, we need to check in the
59 // commit phase if there was an interleaved mutation. In concurrent mode
60 // this can happen all the time, but even in synchronous mode, an earlier
61 // effect may have mutated the store.
62 if (checkIfSnapshotChanged(inst)) {
63 // Force a re-render.
64 forceUpdate({ inst: inst });
65 }
66 // React Hook React.useLayoutEffect has a missing dependency: 'inst'. Either include it or remove the dependency array.
67 // eslint-disable-next-line react-hooks/exhaustive-deps
68 }, [subscribe, value, getSnapshot]);
69 }
70 else {
71 Object.assign(inst, { value: value, getSnapshot: getSnapshot });
72 }
73 React.useEffect(function () {
74 // Check for changes right before subscribing. Subsequent changes will be
75 // detected in the subscription handler.
76 if (checkIfSnapshotChanged(inst)) {
77 // Force a re-render.
78 forceUpdate({ inst: inst });
79 }
80 // Subscribe to the store and return a clean-up function.
81 return subscribe(function handleStoreChange() {
82 // TODO: Because there is no cross-renderer API for batching updates, it's
83 // up to the consumer of this library to wrap their subscription event
84 // with unstable_batchedUpdates. Should we try to detect when this isn't
85 // the case and print a warning in development?
86 // The store changed. Check if the snapshot changed since the last time we
87 // read from the store.
88 if (checkIfSnapshotChanged(inst)) {
89 // Force a re-render.
90 forceUpdate({ inst: inst });
91 }
92 });
93 // React Hook React.useEffect has a missing dependency: 'inst'. Either include it or remove the dependency array.
94 // eslint-disable-next-line react-hooks/exhaustive-deps
95 }, [subscribe]);
96 return value;
97 });
98function checkIfSnapshotChanged(_a) {
99 var value = _a.value, getSnapshot = _a.getSnapshot;
100 try {
101 return value !== getSnapshot();
102 }
103 catch (_b) {
104 return true;
105 }
106}
107//# sourceMappingURL=useSyncExternalStore.js.map
\No newline at end of file