Files
simply_cpq/website/node_modules/@mui/x-internals/useDisposable/useDisposable.js
T
2026-08-07 21:39:30 +02:00

147 lines
6.9 KiB
JavaScript

"use strict";
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useDisposable = void 0;
var _index = _interopRequireDefault(require("../formatErrorMessage/index.js"));
var React = _interopRequireWildcard(require("react"));
var _useOnMount = require("@base-ui/utils/useOnMount");
var _safeReact = require("@base-ui/utils/safeReact");
var _disposable = require("../disposable");
// React 19 exposes shared internals under `__CLIENT_INTERNALS_…` with the
// fiber under `A.getOwner()`; React 18 exposes them under `__SECRET_INTERNALS_…`
// with the fiber under `ReactCurrentOwner.current`. `mode` is a bitfield on
// the fiber. We key on StrictEffectsMode (16) — the bit a concurrent-root
// `<StrictMode>` sets on React 18/19, and the bit that actually drives the
// mount→unmount→mount effect replay this hook guards against. Setups that
// don't replay effects (legacy-root StrictMode, React 17, Preact) don't set
// it, so they fall through to a single, normal dispose. Bit values are
// React-version-specific; if a future React renumbers them and detection
// misfires, the mount counter throws rather than silently leaking.
const STRICT_MODE_BITS = 0b10000;
// Read the internals off Base UI's `SafeReact` — a plain clone of the React
// namespace — rather than `React` directly. Bundlers rewrite a static
// `React.__SECRET_INTERNALS_…` access into a named import of `react`, which
// errors at build time when the key isn't exported (React 19 drops
// `__SECRET_INTERNALS_…`, React 18 drops `__CLIENT_INTERNALS_…`). Reading off
// the clone keeps it a plain property access the bundler leaves alone.
const SafeReactInternals = _safeReact.SafeReact;
const ReactInternals =
// eslint-disable-next-line no-underscore-dangle
SafeReactInternals.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE ??
// eslint-disable-next-line no-underscore-dangle
SafeReactInternals.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
function isInStrictMode() {
try {
const owner = ReactInternals?.A?.getOwner?.() ?? ReactInternals?.ReactCurrentOwner?.current ?? null;
if (owner == null || typeof owner.mode !== 'number') {
return false;
}
// eslint-disable-next-line no-bitwise
return (owner.mode & STRICT_MODE_BITS) !== 0;
} catch {
return false;
}
}
// Module-private symbol used to stash the `useOnMount` callback on the
// instance during the first render so subsequent renders can pass the same
// reference into `useOnMount` without allocating a fresh closure.
const MOUNT = Symbol('useDisposable.mount');
// Module-private symbol holding the dev-only StrictMode bookkeeping that lets
// the mount callback distinguish StrictMode's simulated unmount from a real
// one and assert our render-time detection was correct.
const DEV_STATE = Symbol('useDisposable.devState');
const UNINITIALIZED = Symbol();
/**
* Production variant: lazily creates the instance on first render and stashes
* a single mount/cleanup pair on it under a module-private symbol so render-
* pass allocations stay at zero after the first render. No StrictMode
* detection is needed because StrictMode's double-mount only happens in dev.
* @returns {T} the lazily-created instance.
*/
function useDisposableProduction(factory) {
const ref = React.useRef(UNINITIALIZED);
if (ref.current === UNINITIALIZED) {
const inst = factory();
const cleanup = () => {
inst[_disposable.disposeSymbol]();
// Reset so a fiber-preserving remount (e.g. `<Activity>` reveal, which
// re-renders before re-running effects) rebuilds a fresh instance instead
// of reusing the disposed one.
ref.current = UNINITIALIZED;
};
inst[MOUNT] = () => cleanup;
ref.current = inst;
}
(0, _useOnMount.useOnMount)(ref.current[MOUNT]);
return ref.current;
}
/**
* Development variant: detects StrictMode by reading the currently-rendering
* fiber's `mode` bits from React shared internals so the same instance
* survives StrictMode's mount→unmount→mount cycle.
*
* The mount callback counts mounts to keep the detection honest:
* - it skips dispose on StrictMode's *first* (simulated) unmount only, and
* disposes on every real unmount — including the final one inside
* `<StrictMode>`, so no instance is leaked in dev;
* - if the instance is mounted a second time but was already disposed, our
* render-time detection produced a false negative. Rather than silently
* hand back a dead instance (which is the failure that motivated this hook),
* it throws so the bug surfaces immediately.
* @returns {T} the lazily-created instance.
*/
function useDisposableDevelopment(factory) {
const ref = React.useRef(UNINITIALIZED);
if (ref.current === UNINITIALIZED) {
const inst = factory();
// Captured during render because the owner fiber is only set while React
// is rendering — by the time the effect runs it's already null.
const state = {
detectedStrict: isInStrictMode(),
mountCount: 0,
disposed: false
};
inst[DEV_STATE] = state;
inst[MOUNT] = () => {
state.mountCount += 1;
if (state.mountCount > 1 && state.disposed) {
throw new Error(process.env.NODE_ENV !== "production" ? 'MUI X: useDisposable failed to detect React StrictMode.\n' + "The instance was disposed on StrictMode's simulated unmount and is about to be reused while torn down.\n" + 'This is an internal invariant violation — please report it at https://github.com/mui/mui-x/issues.' : (0, _index.default)(288));
}
return () => {
// Skip StrictMode's simulated unmount (the first unmount, when strict
// mode was detected); dispose on every real unmount.
if (state.detectedStrict && state.mountCount < 2) {
return;
}
state.disposed = true;
inst[_disposable.disposeSymbol]();
// Reset so a fiber-preserving remount (e.g. `<Activity>` reveal, which
// re-renders before re-running effects) rebuilds a fresh instance
// instead of reusing the disposed one.
ref.current = UNINITIALIZED;
};
};
ref.current = inst;
}
(0, _useOnMount.useOnMount)(ref.current[MOUNT]);
return ref.current;
}
/**
* Lazily creates an instance on first render and runs its `[disposeSymbol]`
* once on unmount. The cleanup runs synchronously; in development StrictMode's
* simulated unmount is detected and skipped so the same instance survives the
* double mount. The development variant degrades to production behaviour on
* runtimes that don't replay the double mount (Preact, React 17) because no
* readable StrictEffectsMode bit is found, so no runtime branching is needed.
*/
const useDisposable = exports.useDisposable = process.env.NODE_ENV === 'production' ? useDisposableProduction : useDisposableDevelopment;