You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
103 lines
5.2 KiB
103 lines
5.2 KiB
4 months ago
|
import { invariant } from "../../utilities/globals/index.js";
|
||
|
import * as React from "rehackt";
|
||
|
import { canUseLayoutEffect } from "../../utilities/index.js";
|
||
|
var didWarnUncachedGetSnapshot = false;
|
||
|
// Prevent webpack from complaining about our feature detection of the
|
||
|
// useSyncExternalStore property of the React namespace, which is expected not
|
||
|
// to exist when using React 17 and earlier, and that's fine.
|
||
|
var uSESKey = "useSyncExternalStore";
|
||
|
var realHook = React[uSESKey];
|
||
|
// Adapted from https://www.npmjs.com/package/use-sync-external-store, with
|
||
|
// Apollo Client deviations called out by "// DEVIATION ..." comments.
|
||
|
// When/if React.useSyncExternalStore is defined, delegate fully to it.
|
||
|
export var useSyncExternalStore = realHook ||
|
||
|
(function (subscribe, getSnapshot, getServerSnapshot) {
|
||
|
// Read the current snapshot from the store on every render. Again, this
|
||
|
// breaks the rules of React, and only works here because of specific
|
||
|
// implementation details, most importantly that updates are
|
||
|
// always synchronous.
|
||
|
var value = getSnapshot();
|
||
|
if (
|
||
|
// DEVIATION: Using __DEV__
|
||
|
globalThis.__DEV__ !== false &&
|
||
|
!didWarnUncachedGetSnapshot &&
|
||
|
// DEVIATION: Not using Object.is because we know our snapshots will never
|
||
|
// be exotic primitive values like NaN, which is !== itself.
|
||
|
value !== getSnapshot()) {
|
||
|
didWarnUncachedGetSnapshot = true;
|
||
|
// DEVIATION: Using invariant.error instead of console.error directly.
|
||
|
globalThis.__DEV__ !== false && invariant.error(58);
|
||
|
}
|
||
|
// Because updates are synchronous, we don't queue them. Instead we force a
|
||
|
// re-render whenever the subscribed state changes by updating an some
|
||
|
// arbitrary useState hook. Then, during render, we call getSnapshot to read
|
||
|
// the current value.
|
||
|
//
|
||
|
// Because we don't actually use the state returned by the useState hook, we
|
||
|
// can save a bit of memory by storing other stuff in that slot.
|
||
|
//
|
||
|
// To implement the early bailout, we need to track some things on a mutable
|
||
|
// object. Usually, we would put that in a useRef hook, but we can stash it in
|
||
|
// our useState hook instead.
|
||
|
//
|
||
|
// To force a re-render, we call forceUpdate({inst}). That works because the
|
||
|
// new object always fails an equality check.
|
||
|
var _a = React.useState({
|
||
|
inst: { value: value, getSnapshot: getSnapshot },
|
||
|
}), inst = _a[0].inst, forceUpdate = _a[1];
|
||
|
// Track the latest getSnapshot function with a ref. This needs to be updated
|
||
|
// in the layout phase so we can access it during the tearing check that
|
||
|
// happens on subscribe.
|
||
|
if (canUseLayoutEffect) {
|
||
|
// DEVIATION: We avoid calling useLayoutEffect when !canUseLayoutEffect,
|
||
|
// which may seem like a conditional hook, but this code ends up behaving
|
||
|
// unconditionally (one way or the other) because canUseLayoutEffect is
|
||
|
// constant.
|
||
|
React.useLayoutEffect(function () {
|
||
|
Object.assign(inst, { value: value, getSnapshot: getSnapshot });
|
||
|
// Whenever getSnapshot or subscribe changes, we need to check in the
|
||
|
// commit phase if there was an interleaved mutation. In concurrent mode
|
||
|
// this can happen all the time, but even in synchronous mode, an earlier
|
||
|
// effect may have mutated the store.
|
||
|
if (checkIfSnapshotChanged(inst)) {
|
||
|
// Force a re-render.
|
||
|
forceUpdate({ inst: inst });
|
||
|
}
|
||
|
}, [subscribe, value, getSnapshot]);
|
||
|
}
|
||
|
else {
|
||
|
Object.assign(inst, { value: value, getSnapshot: getSnapshot });
|
||
|
}
|
||
|
React.useEffect(function () {
|
||
|
// Check for changes right before subscribing. Subsequent changes will be
|
||
|
// detected in the subscription handler.
|
||
|
if (checkIfSnapshotChanged(inst)) {
|
||
|
// Force a re-render.
|
||
|
forceUpdate({ inst: inst });
|
||
|
}
|
||
|
// Subscribe to the store and return a clean-up function.
|
||
|
return subscribe(function handleStoreChange() {
|
||
|
// TODO: Because there is no cross-renderer API for batching updates, it's
|
||
|
// up to the consumer of this library to wrap their subscription event
|
||
|
// with unstable_batchedUpdates. Should we try to detect when this isn't
|
||
|
// the case and print a warning in development?
|
||
|
// The store changed. Check if the snapshot changed since the last time we
|
||
|
// read from the store.
|
||
|
if (checkIfSnapshotChanged(inst)) {
|
||
|
// Force a re-render.
|
||
|
forceUpdate({ inst: inst });
|
||
|
}
|
||
|
});
|
||
|
}, [subscribe]);
|
||
|
return value;
|
||
|
});
|
||
|
function checkIfSnapshotChanged(_a) {
|
||
|
var value = _a.value, getSnapshot = _a.getSnapshot;
|
||
|
try {
|
||
|
return value !== getSnapshot();
|
||
|
}
|
||
|
catch (_b) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
//# sourceMappingURL=useSyncExternalStore.js.map
|