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
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
|