18133 lines
885 KiB
JavaScript
18133 lines
885 KiB
JavaScript
import * as React from 'react';
|
||
import Scheduler from 'scheduler';
|
||
/**
|
||
* @license React
|
||
* react-reconciler.development.js
|
||
*
|
||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||
*
|
||
* This source code is licensed under the MIT license found in the
|
||
* LICENSE file in the root directory of this source tree.
|
||
*/
|
||
'use strict';
|
||
export default function $$$reconciler($$$hostConfig) {
|
||
var exports = {};
|
||
'use strict';
|
||
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||
var suppressWarning = false;
|
||
function setSuppressWarning(newSuppressWarning) {
|
||
{
|
||
suppressWarning = newSuppressWarning;
|
||
}
|
||
} // In DEV, calls to console.warn and console.error get replaced
|
||
// by calls to these methods by a Babel plugin.
|
||
//
|
||
// In PROD (or in packages without access to React internals),
|
||
// they are left as they are instead.
|
||
function warn(format) {
|
||
{
|
||
if (!suppressWarning) {
|
||
for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
||
args[_key - 1] = arguments[_key];
|
||
}
|
||
printWarning('warn', format, args);
|
||
}
|
||
}
|
||
}
|
||
function error(format) {
|
||
{
|
||
if (!suppressWarning) {
|
||
for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
||
args[_key2 - 1] = arguments[_key2];
|
||
}
|
||
printWarning('error', format, args);
|
||
}
|
||
}
|
||
}
|
||
function printWarning(level, format, args) {
|
||
// When changing this logic, you might want to also
|
||
// update consoleWithStackDev.www.js as well.
|
||
{
|
||
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||
var stack = ReactDebugCurrentFrame.getStackAddendum();
|
||
if (stack !== '') {
|
||
format += '%s';
|
||
args = args.concat([stack]);
|
||
} // eslint-disable-next-line react-internal/safe-string-coercion
|
||
var argsWithFormat = args.map(function (item) {
|
||
return String(item);
|
||
}); // Careful: RN currently depends on this prefix
|
||
argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
|
||
// breaks IE9: https://github.com/facebook/react/issues/13610
|
||
// eslint-disable-next-line react-internal/no-production-logging
|
||
Function.prototype.apply.call(console[level], console, argsWithFormat);
|
||
}
|
||
}
|
||
var assign = Object.assign;
|
||
/**
|
||
* `ReactInstanceMap` maintains a mapping from a public facing stateful
|
||
* instance (key) and the internal representation (value). This allows public
|
||
* methods to accept the user facing instance as an argument and map them back
|
||
* to internal methods.
|
||
*
|
||
* Note that this module is currently shared and assumed to be stateless.
|
||
* If this becomes an actual Map, that will break.
|
||
*/
|
||
function get(key) {
|
||
return key._reactInternals;
|
||
}
|
||
function set(key, value) {
|
||
key._reactInternals = value;
|
||
}
|
||
// -----------------------------------------------------------------------------
|
||
// the react-reconciler package.
|
||
var enableNewReconciler = false; // Support legacy Primer support on internal FB www
|
||
var enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics.
|
||
var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber
|
||
var enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz
|
||
var warnAboutStringRefs = false; // -----------------------------------------------------------------------------
|
||
// Debugging and DevTools
|
||
// -----------------------------------------------------------------------------
|
||
// Adds user timing marks for e.g. state updates, suspense, and work loop stuff,
|
||
// for an experimental timeline tool.
|
||
var enableSchedulingProfiler = true; // Helps identify side effects in render-phase lifecycle hooks and setState
|
||
var enableProfilerTimer = true; // Record durations for commit and passive effects phases.
|
||
var enableProfilerCommitHooks = true; // Phase param passed to onRender callback differentiates between an "update" and a "cascading-update".
|
||
var FunctionComponent = 0;
|
||
var ClassComponent = 1;
|
||
var IndeterminateComponent = 2; // Before we know whether it is function or class
|
||
var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
|
||
var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
|
||
var HostComponent = 5;
|
||
var HostText = 6;
|
||
var Fragment = 7;
|
||
var Mode = 8;
|
||
var ContextConsumer = 9;
|
||
var ContextProvider = 10;
|
||
var ForwardRef = 11;
|
||
var Profiler = 12;
|
||
var SuspenseComponent = 13;
|
||
var MemoComponent = 14;
|
||
var SimpleMemoComponent = 15;
|
||
var LazyComponent = 16;
|
||
var IncompleteClassComponent = 17;
|
||
var DehydratedFragment = 18;
|
||
var SuspenseListComponent = 19;
|
||
var ScopeComponent = 21;
|
||
var OffscreenComponent = 22;
|
||
var LegacyHiddenComponent = 23;
|
||
var CacheComponent = 24;
|
||
var TracingMarkerComponent = 25;
|
||
// ATTENTION
|
||
// When adding new symbols to this file,
|
||
// Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
|
||
// The Symbol used to tag the ReactElement-like types.
|
||
var REACT_ELEMENT_TYPE = Symbol.for('react.element');
|
||
var REACT_PORTAL_TYPE = Symbol.for('react.portal');
|
||
var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
|
||
var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
|
||
var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
|
||
var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
|
||
var REACT_CONTEXT_TYPE = Symbol.for('react.context');
|
||
var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
|
||
var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
|
||
var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
|
||
var REACT_MEMO_TYPE = Symbol.for('react.memo');
|
||
var REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
||
var REACT_SCOPE_TYPE = Symbol.for('react.scope');
|
||
var REACT_DEBUG_TRACING_MODE_TYPE = Symbol.for('react.debug_trace_mode');
|
||
var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');
|
||
var REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden');
|
||
var REACT_CACHE_TYPE = Symbol.for('react.cache');
|
||
var REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker');
|
||
var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
||
var FAUX_ITERATOR_SYMBOL = '@@iterator';
|
||
function getIteratorFn(maybeIterable) {
|
||
if (maybeIterable === null || typeof maybeIterable !== 'object') {
|
||
return null;
|
||
}
|
||
var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
|
||
if (typeof maybeIterator === 'function') {
|
||
return maybeIterator;
|
||
}
|
||
return null;
|
||
}
|
||
function getWrappedName(outerType, innerType, wrapperName) {
|
||
var displayName = outerType.displayName;
|
||
if (displayName) {
|
||
return displayName;
|
||
}
|
||
var functionName = innerType.displayName || innerType.name || '';
|
||
return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName;
|
||
} // Keep in sync with react-reconciler/getComponentNameFromFiber
|
||
function getContextName(type) {
|
||
return type.displayName || 'Context';
|
||
} // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
|
||
function getComponentNameFromType(type) {
|
||
if (type == null) {
|
||
// Host root, text node or just invalid type.
|
||
return null;
|
||
}
|
||
{
|
||
if (typeof type.tag === 'number') {
|
||
error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
if (typeof type === 'function') {
|
||
return type.displayName || type.name || null;
|
||
}
|
||
if (typeof type === 'string') {
|
||
return type;
|
||
}
|
||
switch (type) {
|
||
case REACT_FRAGMENT_TYPE:
|
||
return 'Fragment';
|
||
case REACT_PORTAL_TYPE:
|
||
return 'Portal';
|
||
case REACT_PROFILER_TYPE:
|
||
return 'Profiler';
|
||
case REACT_STRICT_MODE_TYPE:
|
||
return 'StrictMode';
|
||
case REACT_SUSPENSE_TYPE:
|
||
return 'Suspense';
|
||
case REACT_SUSPENSE_LIST_TYPE:
|
||
return 'SuspenseList';
|
||
}
|
||
if (typeof type === 'object') {
|
||
switch (type.$$typeof) {
|
||
case REACT_CONTEXT_TYPE:
|
||
var context = type;
|
||
return getContextName(context) + '.Consumer';
|
||
case REACT_PROVIDER_TYPE:
|
||
var provider = type;
|
||
return getContextName(provider._context) + '.Provider';
|
||
case REACT_FORWARD_REF_TYPE:
|
||
return getWrappedName(type, type.render, 'ForwardRef');
|
||
case REACT_MEMO_TYPE:
|
||
var outerName = type.displayName || null;
|
||
if (outerName !== null) {
|
||
return outerName;
|
||
}
|
||
return getComponentNameFromType(type.type) || 'Memo';
|
||
case REACT_LAZY_TYPE:
|
||
{
|
||
var lazyComponent = type;
|
||
var payload = lazyComponent._payload;
|
||
var init = lazyComponent._init;
|
||
try {
|
||
return getComponentNameFromType(init(payload));
|
||
}
|
||
catch (x) {
|
||
return null;
|
||
}
|
||
}
|
||
// eslint-disable-next-line no-fallthrough
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function getWrappedName$1(outerType, innerType, wrapperName) {
|
||
var functionName = innerType.displayName || innerType.name || '';
|
||
return outerType.displayName || (functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName);
|
||
} // Keep in sync with shared/getComponentNameFromType
|
||
function getContextName$1(type) {
|
||
return type.displayName || 'Context';
|
||
}
|
||
function getComponentNameFromFiber(fiber) {
|
||
var tag = fiber.tag, type = fiber.type;
|
||
switch (tag) {
|
||
case CacheComponent:
|
||
return 'Cache';
|
||
case ContextConsumer:
|
||
var context = type;
|
||
return getContextName$1(context) + '.Consumer';
|
||
case ContextProvider:
|
||
var provider = type;
|
||
return getContextName$1(provider._context) + '.Provider';
|
||
case DehydratedFragment:
|
||
return 'DehydratedFragment';
|
||
case ForwardRef:
|
||
return getWrappedName$1(type, type.render, 'ForwardRef');
|
||
case Fragment:
|
||
return 'Fragment';
|
||
case HostComponent:
|
||
// Host component type is the display name (e.g. "div", "View")
|
||
return type;
|
||
case HostPortal:
|
||
return 'Portal';
|
||
case HostRoot:
|
||
return 'Root';
|
||
case HostText:
|
||
return 'Text';
|
||
case LazyComponent:
|
||
// Name comes from the type in this case; we don't have a tag.
|
||
return getComponentNameFromType(type);
|
||
case Mode:
|
||
if (type === REACT_STRICT_MODE_TYPE) {
|
||
// Don't be less specific than shared/getComponentNameFromType
|
||
return 'StrictMode';
|
||
}
|
||
return 'Mode';
|
||
case OffscreenComponent:
|
||
return 'Offscreen';
|
||
case Profiler:
|
||
return 'Profiler';
|
||
case ScopeComponent:
|
||
return 'Scope';
|
||
case SuspenseComponent:
|
||
return 'Suspense';
|
||
case SuspenseListComponent:
|
||
return 'SuspenseList';
|
||
case TracingMarkerComponent:
|
||
return 'TracingMarker';
|
||
// The display name for this tags come from the user-provided type:
|
||
case ClassComponent:
|
||
case FunctionComponent:
|
||
case IncompleteClassComponent:
|
||
case IndeterminateComponent:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
if (typeof type === 'function') {
|
||
return type.displayName || type.name || null;
|
||
}
|
||
if (typeof type === 'string') {
|
||
return type;
|
||
}
|
||
break;
|
||
}
|
||
return null;
|
||
}
|
||
// Don't change these two values. They're used by React Dev Tools.
|
||
var NoFlags =
|
||
/* */
|
||
0;
|
||
var PerformedWork =
|
||
/* */
|
||
1; // You can change the rest (and add more).
|
||
var Placement =
|
||
/* */
|
||
2;
|
||
var Update =
|
||
/* */
|
||
4;
|
||
var ChildDeletion =
|
||
/* */
|
||
16;
|
||
var ContentReset =
|
||
/* */
|
||
32;
|
||
var Callback =
|
||
/* */
|
||
64;
|
||
var DidCapture =
|
||
/* */
|
||
128;
|
||
var ForceClientRender =
|
||
/* */
|
||
256;
|
||
var Ref =
|
||
/* */
|
||
512;
|
||
var Snapshot =
|
||
/* */
|
||
1024;
|
||
var Passive =
|
||
/* */
|
||
2048;
|
||
var Hydrating =
|
||
/* */
|
||
4096;
|
||
var Visibility =
|
||
/* */
|
||
8192;
|
||
var StoreConsistency =
|
||
/* */
|
||
16384;
|
||
var LifecycleEffectMask = Passive | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit)
|
||
var HostEffectMask =
|
||
/* */
|
||
32767; // These are not really side effects, but we still reuse this field.
|
||
var Incomplete =
|
||
/* */
|
||
32768;
|
||
var ShouldCapture =
|
||
/* */
|
||
65536;
|
||
var ForceUpdateForLegacySuspense =
|
||
/* */
|
||
131072;
|
||
var Forked =
|
||
/* */
|
||
1048576; // Static tags describe aspects of a fiber that are not specific to a render,
|
||
// e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
|
||
// This enables us to defer more work in the unmount case,
|
||
// since we can defer traversing the tree during layout to look for Passive effects,
|
||
// and instead rely on the static flag as a signal that there may be cleanup work.
|
||
var RefStatic =
|
||
/* */
|
||
2097152;
|
||
var LayoutStatic =
|
||
/* */
|
||
4194304;
|
||
var PassiveStatic =
|
||
/* */
|
||
8388608; // These flags allow us to traverse to fibers that have effects on mount
|
||
// without traversing the entire tree after every commit for
|
||
// double invoking
|
||
var MountLayoutDev =
|
||
/* */
|
||
16777216;
|
||
var MountPassiveDev =
|
||
/* */
|
||
33554432; // Groups of flags that are used in the commit phase to skip over trees that
|
||
// don't contain effects, by checking subtreeFlags.
|
||
var BeforeMutationMask = // TODO: Remove Update flag from before mutation phase by re-landing Visibility
|
||
|
||
// flag logic (see #20043)
|
||
Update | Snapshot | (0);
|
||
var MutationMask = Placement | Update | ChildDeletion | ContentReset | Ref | Hydrating | Visibility;
|
||
var LayoutMask = Update | Callback | Ref | Visibility; // TODO: Split into PassiveMountMask and PassiveUnmountMask
|
||
var PassiveMask = Passive | ChildDeletion; // Union of tags that don't get reset on clones.
|
||
// This allows certain concepts to persist without recalculating them,
|
||
// e.g. whether a subtree contains passive effects or portals.
|
||
var StaticMask = LayoutStatic | PassiveStatic | RefStatic;
|
||
var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||
function getNearestMountedFiber(fiber) {
|
||
var node = fiber;
|
||
var nearestMounted = fiber;
|
||
if (!fiber.alternate) {
|
||
// If there is no alternate, this might be a new tree that isn't inserted
|
||
// yet. If it is, then it will have a pending insertion effect on it.
|
||
var nextNode = node;
|
||
do {
|
||
node = nextNode;
|
||
if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
|
||
// This is an insertion or in-progress hydration. The nearest possible
|
||
// mounted fiber is the parent but we need to continue to figure out
|
||
// if that one is still mounted.
|
||
nearestMounted = node.return;
|
||
}
|
||
nextNode = node.return;
|
||
} while (nextNode);
|
||
}
|
||
else {
|
||
while (node.return) {
|
||
node = node.return;
|
||
}
|
||
}
|
||
if (node.tag === HostRoot) {
|
||
// TODO: Check if this was a nested HostRoot when used with
|
||
// renderContainerIntoSubtree.
|
||
return nearestMounted;
|
||
} // If we didn't hit the root, that means that we're in an disconnected tree
|
||
// that has been unmounted.
|
||
return null;
|
||
}
|
||
function isFiberMounted(fiber) {
|
||
return getNearestMountedFiber(fiber) === fiber;
|
||
}
|
||
function isMounted(component) {
|
||
{
|
||
var owner = ReactCurrentOwner.current;
|
||
if (owner !== null && owner.tag === ClassComponent) {
|
||
var ownerFiber = owner;
|
||
var instance = ownerFiber.stateNode;
|
||
if (!instance._warnedAboutRefsInRender) {
|
||
error('%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentNameFromFiber(ownerFiber) || 'A component');
|
||
}
|
||
instance._warnedAboutRefsInRender = true;
|
||
}
|
||
}
|
||
var fiber = get(component);
|
||
if (!fiber) {
|
||
return false;
|
||
}
|
||
return getNearestMountedFiber(fiber) === fiber;
|
||
}
|
||
function assertIsMounted(fiber) {
|
||
if (getNearestMountedFiber(fiber) !== fiber) {
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
}
|
||
function findCurrentFiberUsingSlowPath(fiber) {
|
||
var alternate = fiber.alternate;
|
||
if (!alternate) {
|
||
// If there is no alternate, then we only need to check if it is mounted.
|
||
var nearestMounted = getNearestMountedFiber(fiber);
|
||
if (nearestMounted === null) {
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
if (nearestMounted !== fiber) {
|
||
return null;
|
||
}
|
||
return fiber;
|
||
} // If we have two possible branches, we'll walk backwards up to the root
|
||
// to see what path the root points to. On the way we may hit one of the
|
||
// special cases and we'll deal with them.
|
||
var a = fiber;
|
||
var b = alternate;
|
||
while (true) {
|
||
var parentA = a.return;
|
||
if (parentA === null) {
|
||
// We're at the root.
|
||
break;
|
||
}
|
||
var parentB = parentA.alternate;
|
||
if (parentB === null) {
|
||
// There is no alternate. This is an unusual case. Currently, it only
|
||
// happens when a Suspense component is hidden. An extra fragment fiber
|
||
// is inserted in between the Suspense fiber and its children. Skip
|
||
// over this extra fragment fiber and proceed to the next parent.
|
||
var nextParent = parentA.return;
|
||
if (nextParent !== null) {
|
||
a = b = nextParent;
|
||
continue;
|
||
} // If there's no parent, we're at the root.
|
||
break;
|
||
} // If both copies of the parent fiber point to the same child, we can
|
||
// assume that the child is current. This happens when we bailout on low
|
||
// priority: the bailed out fiber's child reuses the current child.
|
||
if (parentA.child === parentB.child) {
|
||
var child = parentA.child;
|
||
while (child) {
|
||
if (child === a) {
|
||
// We've determined that A is the current branch.
|
||
assertIsMounted(parentA);
|
||
return fiber;
|
||
}
|
||
if (child === b) {
|
||
// We've determined that B is the current branch.
|
||
assertIsMounted(parentA);
|
||
return alternate;
|
||
}
|
||
child = child.sibling;
|
||
} // We should never have an alternate for any mounting node. So the only
|
||
// way this could possibly happen is if this was unmounted, if at all.
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
if (a.return !== b.return) {
|
||
// The return pointer of A and the return pointer of B point to different
|
||
// fibers. We assume that return pointers never criss-cross, so A must
|
||
// belong to the child set of A.return, and B must belong to the child
|
||
// set of B.return.
|
||
a = parentA;
|
||
b = parentB;
|
||
}
|
||
else {
|
||
// The return pointers point to the same fiber. We'll have to use the
|
||
// default, slow path: scan the child sets of each parent alternate to see
|
||
// which child belongs to which set.
|
||
//
|
||
// Search parent A's child set
|
||
var didFindChild = false;
|
||
var _child = parentA.child;
|
||
while (_child) {
|
||
if (_child === a) {
|
||
didFindChild = true;
|
||
a = parentA;
|
||
b = parentB;
|
||
break;
|
||
}
|
||
if (_child === b) {
|
||
didFindChild = true;
|
||
b = parentA;
|
||
a = parentB;
|
||
break;
|
||
}
|
||
_child = _child.sibling;
|
||
}
|
||
if (!didFindChild) {
|
||
// Search parent B's child set
|
||
_child = parentB.child;
|
||
while (_child) {
|
||
if (_child === a) {
|
||
didFindChild = true;
|
||
a = parentB;
|
||
b = parentA;
|
||
break;
|
||
}
|
||
if (_child === b) {
|
||
didFindChild = true;
|
||
b = parentB;
|
||
a = parentA;
|
||
break;
|
||
}
|
||
_child = _child.sibling;
|
||
}
|
||
if (!didFindChild) {
|
||
throw new Error('Child was not found in either parent set. This indicates a bug ' + 'in React related to the return pointer. Please file an issue.');
|
||
}
|
||
}
|
||
}
|
||
if (a.alternate !== b) {
|
||
throw new Error("Return fibers should always be each others' alternates. " + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
} // If the root is not a host container, we're in a disconnected tree. I.e.
|
||
// unmounted.
|
||
if (a.tag !== HostRoot) {
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
if (a.stateNode.current === a) {
|
||
// We've determined that A is the current branch.
|
||
return fiber;
|
||
} // Otherwise B has to be current branch.
|
||
return alternate;
|
||
}
|
||
function findCurrentHostFiber(parent) {
|
||
var currentParent = findCurrentFiberUsingSlowPath(parent);
|
||
return currentParent !== null ? findCurrentHostFiberImpl(currentParent) : null;
|
||
}
|
||
function findCurrentHostFiberImpl(node) {
|
||
// Next we'll drill down this component to find the first HostComponent/Text.
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
return node;
|
||
}
|
||
var child = node.child;
|
||
while (child !== null) {
|
||
var match = findCurrentHostFiberImpl(child);
|
||
if (match !== null) {
|
||
return match;
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
return null;
|
||
}
|
||
function findCurrentHostFiberWithNoPortals(parent) {
|
||
var currentParent = findCurrentFiberUsingSlowPath(parent);
|
||
return currentParent !== null ? findCurrentHostFiberWithNoPortalsImpl(currentParent) : null;
|
||
}
|
||
function findCurrentHostFiberWithNoPortalsImpl(node) {
|
||
// Next we'll drill down this component to find the first HostComponent/Text.
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
return node;
|
||
}
|
||
var child = node.child;
|
||
while (child !== null) {
|
||
if (child.tag !== HostPortal) {
|
||
var match = findCurrentHostFiberWithNoPortalsImpl(child);
|
||
if (match !== null) {
|
||
return match;
|
||
}
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
return null;
|
||
}
|
||
var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
|
||
function isArray(a) {
|
||
return isArrayImpl(a);
|
||
}
|
||
// This is a host config that's used for the `react-reconciler` package on npm.
|
||
// It is only used by third-party renderers.
|
||
//
|
||
// Its API lets you pass the host config as an argument.
|
||
// However, inside the `react-reconciler` we treat host config as a module.
|
||
// This file is a shim between two worlds.
|
||
//
|
||
// It works because the `react-reconciler` bundle is wrapped in something like:
|
||
//
|
||
// module.exports = function ($$$config) {
|
||
// /* reconciler code */
|
||
// }
|
||
//
|
||
// So `$$$config` looks like a global variable, but it's
|
||
// really an argument to a top-level wrapping function.
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
// eslint-disable-line no-undef
|
||
var getPublicInstance = $$$hostConfig.getPublicInstance;
|
||
var getRootHostContext = $$$hostConfig.getRootHostContext;
|
||
var getChildHostContext = $$$hostConfig.getChildHostContext;
|
||
var prepareForCommit = $$$hostConfig.prepareForCommit;
|
||
var resetAfterCommit = $$$hostConfig.resetAfterCommit;
|
||
var createInstance = $$$hostConfig.createInstance;
|
||
var appendInitialChild = $$$hostConfig.appendInitialChild;
|
||
var finalizeInitialChildren = $$$hostConfig.finalizeInitialChildren;
|
||
var prepareUpdate = $$$hostConfig.prepareUpdate;
|
||
var shouldSetTextContent = $$$hostConfig.shouldSetTextContent;
|
||
var createTextInstance = $$$hostConfig.createTextInstance;
|
||
var scheduleTimeout = $$$hostConfig.scheduleTimeout;
|
||
var cancelTimeout = $$$hostConfig.cancelTimeout;
|
||
var noTimeout = $$$hostConfig.noTimeout;
|
||
var isPrimaryRenderer = $$$hostConfig.isPrimaryRenderer;
|
||
var warnsIfNotActing = $$$hostConfig.warnsIfNotActing;
|
||
var supportsMutation = $$$hostConfig.supportsMutation;
|
||
var supportsPersistence = $$$hostConfig.supportsPersistence;
|
||
var supportsHydration = $$$hostConfig.supportsHydration;
|
||
var getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
|
||
var beforeActiveInstanceBlur = $$$hostConfig.beforeActiveInstanceBlur;
|
||
var afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur;
|
||
var preparePortalMount = $$$hostConfig.preparePortalMount;
|
||
var prepareScopeUpdate = $$$hostConfig.prepareScopeUpdate;
|
||
var getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
|
||
var getCurrentEventPriority = $$$hostConfig.getCurrentEventPriority;
|
||
var detachDeletedInstance = $$$hostConfig.detachDeletedInstance; // -------------------
|
||
// Microtasks
|
||
// (optional)
|
||
// -------------------
|
||
var supportsMicrotasks = $$$hostConfig.supportsMicrotasks;
|
||
var scheduleMicrotask = $$$hostConfig.scheduleMicrotask; // -------------------
|
||
// Test selectors
|
||
// (optional)
|
||
// -------------------
|
||
var supportsTestSelectors = $$$hostConfig.supportsTestSelectors;
|
||
var findFiberRoot = $$$hostConfig.findFiberRoot;
|
||
var getBoundingRect = $$$hostConfig.getBoundingRect;
|
||
var getTextContent = $$$hostConfig.getTextContent;
|
||
var isHiddenSubtree = $$$hostConfig.isHiddenSubtree;
|
||
var matchAccessibilityRole = $$$hostConfig.matchAccessibilityRole;
|
||
var setFocusIfFocusable = $$$hostConfig.setFocusIfFocusable;
|
||
var setupIntersectionObserver = $$$hostConfig.setupIntersectionObserver; // -------------------
|
||
// Mutation
|
||
// (optional)
|
||
// -------------------
|
||
var appendChild = $$$hostConfig.appendChild;
|
||
var appendChildToContainer = $$$hostConfig.appendChildToContainer;
|
||
var commitTextUpdate = $$$hostConfig.commitTextUpdate;
|
||
var commitMount = $$$hostConfig.commitMount;
|
||
var commitUpdate = $$$hostConfig.commitUpdate;
|
||
var insertBefore = $$$hostConfig.insertBefore;
|
||
var insertInContainerBefore = $$$hostConfig.insertInContainerBefore;
|
||
var removeChild = $$$hostConfig.removeChild;
|
||
var removeChildFromContainer = $$$hostConfig.removeChildFromContainer;
|
||
var resetTextContent = $$$hostConfig.resetTextContent;
|
||
var hideInstance = $$$hostConfig.hideInstance;
|
||
var hideTextInstance = $$$hostConfig.hideTextInstance;
|
||
var unhideInstance = $$$hostConfig.unhideInstance;
|
||
var unhideTextInstance = $$$hostConfig.unhideTextInstance;
|
||
var clearContainer = $$$hostConfig.clearContainer; // -------------------
|
||
// Persistence
|
||
// (optional)
|
||
// -------------------
|
||
var cloneInstance = $$$hostConfig.cloneInstance;
|
||
var createContainerChildSet = $$$hostConfig.createContainerChildSet;
|
||
var appendChildToContainerChildSet = $$$hostConfig.appendChildToContainerChildSet;
|
||
var finalizeContainerChildren = $$$hostConfig.finalizeContainerChildren;
|
||
var replaceContainerChildren = $$$hostConfig.replaceContainerChildren;
|
||
var cloneHiddenInstance = $$$hostConfig.cloneHiddenInstance;
|
||
var cloneHiddenTextInstance = $$$hostConfig.cloneHiddenTextInstance; // -------------------
|
||
// Hydration
|
||
// (optional)
|
||
// -------------------
|
||
var canHydrateInstance = $$$hostConfig.canHydrateInstance;
|
||
var canHydrateTextInstance = $$$hostConfig.canHydrateTextInstance;
|
||
var canHydrateSuspenseInstance = $$$hostConfig.canHydrateSuspenseInstance;
|
||
var isSuspenseInstancePending = $$$hostConfig.isSuspenseInstancePending;
|
||
var isSuspenseInstanceFallback = $$$hostConfig.isSuspenseInstanceFallback;
|
||
var getSuspenseInstanceFallbackErrorDetails = $$$hostConfig.getSuspenseInstanceFallbackErrorDetails;
|
||
var registerSuspenseInstanceRetry = $$$hostConfig.registerSuspenseInstanceRetry;
|
||
var getNextHydratableSibling = $$$hostConfig.getNextHydratableSibling;
|
||
var getFirstHydratableChild = $$$hostConfig.getFirstHydratableChild;
|
||
var getFirstHydratableChildWithinContainer = $$$hostConfig.getFirstHydratableChildWithinContainer;
|
||
var getFirstHydratableChildWithinSuspenseInstance = $$$hostConfig.getFirstHydratableChildWithinSuspenseInstance;
|
||
var hydrateInstance = $$$hostConfig.hydrateInstance;
|
||
var hydrateTextInstance = $$$hostConfig.hydrateTextInstance;
|
||
var hydrateSuspenseInstance = $$$hostConfig.hydrateSuspenseInstance;
|
||
var getNextHydratableInstanceAfterSuspenseInstance = $$$hostConfig.getNextHydratableInstanceAfterSuspenseInstance;
|
||
var commitHydratedContainer = $$$hostConfig.commitHydratedContainer;
|
||
var commitHydratedSuspenseInstance = $$$hostConfig.commitHydratedSuspenseInstance;
|
||
var clearSuspenseBoundary = $$$hostConfig.clearSuspenseBoundary;
|
||
var clearSuspenseBoundaryFromContainer = $$$hostConfig.clearSuspenseBoundaryFromContainer;
|
||
var shouldDeleteUnhydratedTailInstances = $$$hostConfig.shouldDeleteUnhydratedTailInstances;
|
||
var didNotMatchHydratedContainerTextInstance = $$$hostConfig.didNotMatchHydratedContainerTextInstance;
|
||
var didNotMatchHydratedTextInstance = $$$hostConfig.didNotMatchHydratedTextInstance;
|
||
var didNotHydrateInstanceWithinContainer = $$$hostConfig.didNotHydrateInstanceWithinContainer;
|
||
var didNotHydrateInstanceWithinSuspenseInstance = $$$hostConfig.didNotHydrateInstanceWithinSuspenseInstance;
|
||
var didNotHydrateInstance = $$$hostConfig.didNotHydrateInstance;
|
||
var didNotFindHydratableInstanceWithinContainer = $$$hostConfig.didNotFindHydratableInstanceWithinContainer;
|
||
var didNotFindHydratableTextInstanceWithinContainer = $$$hostConfig.didNotFindHydratableTextInstanceWithinContainer;
|
||
var didNotFindHydratableSuspenseInstanceWithinContainer = $$$hostConfig.didNotFindHydratableSuspenseInstanceWithinContainer;
|
||
var didNotFindHydratableInstanceWithinSuspenseInstance = $$$hostConfig.didNotFindHydratableInstanceWithinSuspenseInstance;
|
||
var didNotFindHydratableTextInstanceWithinSuspenseInstance = $$$hostConfig.didNotFindHydratableTextInstanceWithinSuspenseInstance;
|
||
var didNotFindHydratableSuspenseInstanceWithinSuspenseInstance = $$$hostConfig.didNotFindHydratableSuspenseInstanceWithinSuspenseInstance;
|
||
var didNotFindHydratableInstance = $$$hostConfig.didNotFindHydratableInstance;
|
||
var didNotFindHydratableTextInstance = $$$hostConfig.didNotFindHydratableTextInstance;
|
||
var didNotFindHydratableSuspenseInstance = $$$hostConfig.didNotFindHydratableSuspenseInstance;
|
||
var errorHydratingContainer = $$$hostConfig.errorHydratingContainer;
|
||
// Helpers to patch console.logs to avoid logging during side-effect free
|
||
// replaying on render function. This currently only patches the object
|
||
// lazily which won't cover if the log function was extracted eagerly.
|
||
// We could also eagerly patch the method.
|
||
var disabledDepth = 0;
|
||
var prevLog;
|
||
var prevInfo;
|
||
var prevWarn;
|
||
var prevError;
|
||
var prevGroup;
|
||
var prevGroupCollapsed;
|
||
var prevGroupEnd;
|
||
function disabledLog() { }
|
||
disabledLog.__reactDisabledLog = true;
|
||
function disableLogs() {
|
||
{
|
||
if (disabledDepth === 0) {
|
||
/* eslint-disable react-internal/no-production-logging */
|
||
prevLog = console.log;
|
||
prevInfo = console.info;
|
||
prevWarn = console.warn;
|
||
prevError = console.error;
|
||
prevGroup = console.group;
|
||
prevGroupCollapsed = console.groupCollapsed;
|
||
prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099
|
||
var props = {
|
||
configurable: true,
|
||
enumerable: true,
|
||
value: disabledLog,
|
||
writable: true
|
||
}; // $FlowFixMe Flow thinks console is immutable.
|
||
Object.defineProperties(console, {
|
||
info: props,
|
||
log: props,
|
||
warn: props,
|
||
error: props,
|
||
group: props,
|
||
groupCollapsed: props,
|
||
groupEnd: props
|
||
});
|
||
/* eslint-enable react-internal/no-production-logging */
|
||
}
|
||
disabledDepth++;
|
||
}
|
||
}
|
||
function reenableLogs() {
|
||
{
|
||
disabledDepth--;
|
||
if (disabledDepth === 0) {
|
||
/* eslint-disable react-internal/no-production-logging */
|
||
var props = {
|
||
configurable: true,
|
||
enumerable: true,
|
||
writable: true
|
||
}; // $FlowFixMe Flow thinks console is immutable.
|
||
Object.defineProperties(console, {
|
||
log: assign({}, props, {
|
||
value: prevLog
|
||
}),
|
||
info: assign({}, props, {
|
||
value: prevInfo
|
||
}),
|
||
warn: assign({}, props, {
|
||
value: prevWarn
|
||
}),
|
||
error: assign({}, props, {
|
||
value: prevError
|
||
}),
|
||
group: assign({}, props, {
|
||
value: prevGroup
|
||
}),
|
||
groupCollapsed: assign({}, props, {
|
||
value: prevGroupCollapsed
|
||
}),
|
||
groupEnd: assign({}, props, {
|
||
value: prevGroupEnd
|
||
})
|
||
});
|
||
/* eslint-enable react-internal/no-production-logging */
|
||
}
|
||
if (disabledDepth < 0) {
|
||
error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
}
|
||
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||
var prefix;
|
||
function describeBuiltInComponentFrame(name, source, ownerFn) {
|
||
{
|
||
if (prefix === undefined) {
|
||
// Extract the VM specific prefix used by each line.
|
||
try {
|
||
throw Error();
|
||
}
|
||
catch (x) {
|
||
var match = x.stack.trim().match(/\n( *(at )?)/);
|
||
prefix = match && match[1] || '';
|
||
}
|
||
} // We use the prefix to ensure our stacks line up with native stack frames.
|
||
return '\n' + prefix + name;
|
||
}
|
||
}
|
||
var reentry = false;
|
||
var componentFrameCache;
|
||
{
|
||
var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
||
componentFrameCache = new PossiblyWeakMap();
|
||
}
|
||
function describeNativeComponentFrame(fn, construct) {
|
||
// If something asked for a stack inside a fake render, it should get ignored.
|
||
if (!fn || reentry) {
|
||
return '';
|
||
}
|
||
{
|
||
var frame = componentFrameCache.get(fn);
|
||
if (frame !== undefined) {
|
||
return frame;
|
||
}
|
||
}
|
||
var control;
|
||
reentry = true;
|
||
var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined.
|
||
Error.prepareStackTrace = undefined;
|
||
var previousDispatcher;
|
||
{
|
||
previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function
|
||
// for warnings.
|
||
ReactCurrentDispatcher.current = null;
|
||
disableLogs();
|
||
}
|
||
try {
|
||
// This should throw.
|
||
if (construct) {
|
||
// Something should be setting the props in the constructor.
|
||
var Fake = function () {
|
||
throw Error();
|
||
}; // $FlowFixMe
|
||
Object.defineProperty(Fake.prototype, 'props', {
|
||
set: function () {
|
||
// We use a throwing setter instead of frozen or non-writable props
|
||
// because that won't throw in a non-strict mode function.
|
||
throw Error();
|
||
}
|
||
});
|
||
if (typeof Reflect === 'object' && Reflect.construct) {
|
||
// We construct a different control for this case to include any extra
|
||
// frames added by the construct call.
|
||
try {
|
||
Reflect.construct(Fake, []);
|
||
}
|
||
catch (x) {
|
||
control = x;
|
||
}
|
||
Reflect.construct(fn, [], Fake);
|
||
}
|
||
else {
|
||
try {
|
||
Fake.call();
|
||
}
|
||
catch (x) {
|
||
control = x;
|
||
}
|
||
fn.call(Fake.prototype);
|
||
}
|
||
}
|
||
else {
|
||
try {
|
||
throw Error();
|
||
}
|
||
catch (x) {
|
||
control = x;
|
||
}
|
||
fn();
|
||
}
|
||
}
|
||
catch (sample) {
|
||
// This is inlined manually because closure doesn't do it for us.
|
||
if (sample && control && typeof sample.stack === 'string') {
|
||
// This extracts the first frame from the sample that isn't also in the control.
|
||
// Skipping one frame that we assume is the frame that calls the two.
|
||
var sampleLines = sample.stack.split('\n');
|
||
var controlLines = control.stack.split('\n');
|
||
var s = sampleLines.length - 1;
|
||
var c = controlLines.length - 1;
|
||
while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {
|
||
// We expect at least one stack frame to be shared.
|
||
// Typically this will be the root most one. However, stack frames may be
|
||
// cut off due to maximum stack limits. In this case, one maybe cut off
|
||
// earlier than the other. We assume that the sample is longer or the same
|
||
// and there for cut off earlier. So we should find the root most frame in
|
||
// the sample somewhere in the control.
|
||
c--;
|
||
}
|
||
for (; s >= 1 && c >= 0; s--, c--) {
|
||
// Next we find the first one that isn't the same which should be the
|
||
// frame that called our sample function and the control.
|
||
if (sampleLines[s] !== controlLines[c]) {
|
||
// In V8, the first line is describing the message but other VMs don't.
|
||
// If we're about to return the first line, and the control is also on the same
|
||
// line, that's a pretty good indicator that our sample threw at same line as
|
||
// the control. I.e. before we entered the sample frame. So we ignore this result.
|
||
// This can happen if you passed a class to function component, or non-function.
|
||
if (s !== 1 || c !== 1) {
|
||
do {
|
||
s--;
|
||
c--; // We may still have similar intermediate frames from the construct call.
|
||
// The next one that isn't the same should be our match though.
|
||
if (c < 0 || sampleLines[s] !== controlLines[c]) {
|
||
// V8 adds a "new" prefix for native classes. Let's remove it to make it prettier.
|
||
var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "<anonymous>"
|
||
// but we have a user-provided "displayName"
|
||
// splice it in to make the stack more readable.
|
||
if (fn.displayName && _frame.includes('<anonymous>')) {
|
||
_frame = _frame.replace('<anonymous>', fn.displayName);
|
||
}
|
||
{
|
||
if (typeof fn === 'function') {
|
||
componentFrameCache.set(fn, _frame);
|
||
}
|
||
} // Return the line we found.
|
||
return _frame;
|
||
}
|
||
} while (s >= 1 && c >= 0);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
finally {
|
||
reentry = false;
|
||
{
|
||
ReactCurrentDispatcher.current = previousDispatcher;
|
||
reenableLogs();
|
||
}
|
||
Error.prepareStackTrace = previousPrepareStackTrace;
|
||
} // Fallback to just using the name if we couldn't make it throw.
|
||
var name = fn ? fn.displayName || fn.name : '';
|
||
var syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';
|
||
{
|
||
if (typeof fn === 'function') {
|
||
componentFrameCache.set(fn, syntheticFrame);
|
||
}
|
||
}
|
||
return syntheticFrame;
|
||
}
|
||
function describeClassComponentFrame(ctor, source, ownerFn) {
|
||
{
|
||
return describeNativeComponentFrame(ctor, true);
|
||
}
|
||
}
|
||
function describeFunctionComponentFrame(fn, source, ownerFn) {
|
||
{
|
||
return describeNativeComponentFrame(fn, false);
|
||
}
|
||
}
|
||
function shouldConstruct(Component) {
|
||
var prototype = Component.prototype;
|
||
return !!(prototype && prototype.isReactComponent);
|
||
}
|
||
function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) {
|
||
if (type == null) {
|
||
return '';
|
||
}
|
||
if (typeof type === 'function') {
|
||
{
|
||
return describeNativeComponentFrame(type, shouldConstruct(type));
|
||
}
|
||
}
|
||
if (typeof type === 'string') {
|
||
return describeBuiltInComponentFrame(type);
|
||
}
|
||
switch (type) {
|
||
case REACT_SUSPENSE_TYPE:
|
||
return describeBuiltInComponentFrame('Suspense');
|
||
case REACT_SUSPENSE_LIST_TYPE:
|
||
return describeBuiltInComponentFrame('SuspenseList');
|
||
}
|
||
if (typeof type === 'object') {
|
||
switch (type.$$typeof) {
|
||
case REACT_FORWARD_REF_TYPE:
|
||
return describeFunctionComponentFrame(type.render);
|
||
case REACT_MEMO_TYPE:
|
||
// Memo may contain any component type so we recursively resolve it.
|
||
return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
|
||
case REACT_LAZY_TYPE:
|
||
{
|
||
var lazyComponent = type;
|
||
var payload = lazyComponent._payload;
|
||
var init = lazyComponent._init;
|
||
try {
|
||
// Lazy may contain any component type so we recursively resolve it.
|
||
return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn);
|
||
}
|
||
catch (x) { }
|
||
}
|
||
}
|
||
}
|
||
return '';
|
||
}
|
||
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
||
var loggedTypeFailures = {};
|
||
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||
function setCurrentlyValidatingElement(element) {
|
||
{
|
||
if (element) {
|
||
var owner = element._owner;
|
||
var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);
|
||
ReactDebugCurrentFrame.setExtraStackFrame(stack);
|
||
}
|
||
else {
|
||
ReactDebugCurrentFrame.setExtraStackFrame(null);
|
||
}
|
||
}
|
||
}
|
||
function checkPropTypes(typeSpecs, values, location, componentName, element) {
|
||
{
|
||
// $FlowFixMe This is okay but Flow doesn't know it.
|
||
var has = Function.call.bind(hasOwnProperty);
|
||
for (var typeSpecName in typeSpecs) {
|
||
if (has(typeSpecs, typeSpecName)) {
|
||
var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
|
||
// fail the render phase where it didn't fail before. So we log it.
|
||
// After these have been cleaned up, we'll let them throw.
|
||
try {
|
||
// This is intentionally an invariant that gets caught. It's the same
|
||
// behavior as without this statement except with a better message.
|
||
if (typeof typeSpecs[typeSpecName] !== 'function') {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.');
|
||
err.name = 'Invariant Violation';
|
||
throw err;
|
||
}
|
||
error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED');
|
||
}
|
||
catch (ex) {
|
||
error$1 = ex;
|
||
}
|
||
if (error$1 && !(error$1 instanceof Error)) {
|
||
setCurrentlyValidatingElement(element);
|
||
error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1);
|
||
setCurrentlyValidatingElement(null);
|
||
}
|
||
if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {
|
||
// Only monitor this failure once because there tends to be a lot of the
|
||
// same error.
|
||
loggedTypeFailures[error$1.message] = true;
|
||
setCurrentlyValidatingElement(element);
|
||
error('Failed %s type: %s', location, error$1.message);
|
||
setCurrentlyValidatingElement(null);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var valueStack = [];
|
||
var fiberStack;
|
||
{
|
||
fiberStack = [];
|
||
}
|
||
var index = -1;
|
||
function createCursor(defaultValue) {
|
||
return {
|
||
current: defaultValue
|
||
};
|
||
}
|
||
function pop(cursor, fiber) {
|
||
if (index < 0) {
|
||
{
|
||
error('Unexpected pop.');
|
||
}
|
||
return;
|
||
}
|
||
{
|
||
if (fiber !== fiberStack[index]) {
|
||
error('Unexpected Fiber popped.');
|
||
}
|
||
}
|
||
cursor.current = valueStack[index];
|
||
valueStack[index] = null;
|
||
{
|
||
fiberStack[index] = null;
|
||
}
|
||
index--;
|
||
}
|
||
function push(cursor, value, fiber) {
|
||
index++;
|
||
valueStack[index] = cursor.current;
|
||
{
|
||
fiberStack[index] = fiber;
|
||
}
|
||
cursor.current = value;
|
||
}
|
||
var warnedAboutMissingGetChildContext;
|
||
{
|
||
warnedAboutMissingGetChildContext = {};
|
||
}
|
||
var emptyContextObject = {};
|
||
{
|
||
Object.freeze(emptyContextObject);
|
||
} // A cursor to the current merged context object on the stack.
|
||
var contextStackCursor = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed.
|
||
var didPerformWorkStackCursor = createCursor(false); // Keep track of the previous context object that was on the stack.
|
||
// We use this to get access to the parent context after we have already
|
||
// pushed the next context provider, and now need to merge their contexts.
|
||
var previousContext = emptyContextObject;
|
||
function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) {
|
||
{
|
||
if (didPushOwnContextIfProvider && isContextProvider(Component)) {
|
||
// If the fiber is a context provider itself, when we read its context
|
||
// we may have already pushed its own child context on the stack. A context
|
||
// provider should not "see" its own child context. Therefore we read the
|
||
// previous (parent) context instead for a context provider.
|
||
return previousContext;
|
||
}
|
||
return contextStackCursor.current;
|
||
}
|
||
}
|
||
function cacheContext(workInProgress, unmaskedContext, maskedContext) {
|
||
{
|
||
var instance = workInProgress.stateNode;
|
||
instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
|
||
instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
|
||
}
|
||
}
|
||
function getMaskedContext(workInProgress, unmaskedContext) {
|
||
{
|
||
var type = workInProgress.type;
|
||
var contextTypes = type.contextTypes;
|
||
if (!contextTypes) {
|
||
return emptyContextObject;
|
||
} // Avoid recreating masked context unless unmasked context has changed.
|
||
// Failing to do this will result in unnecessary calls to componentWillReceiveProps.
|
||
// This may trigger infinite loops if componentWillReceiveProps calls setState.
|
||
var instance = workInProgress.stateNode;
|
||
if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
|
||
return instance.__reactInternalMemoizedMaskedChildContext;
|
||
}
|
||
var context = {};
|
||
for (var key in contextTypes) {
|
||
context[key] = unmaskedContext[key];
|
||
}
|
||
{
|
||
var name = getComponentNameFromFiber(workInProgress) || 'Unknown';
|
||
checkPropTypes(contextTypes, context, 'context', name);
|
||
} // Cache unmasked context so we can avoid recreating masked context unless necessary.
|
||
// Context is created before the class component is instantiated so check for instance.
|
||
if (instance) {
|
||
cacheContext(workInProgress, unmaskedContext, context);
|
||
}
|
||
return context;
|
||
}
|
||
}
|
||
function hasContextChanged() {
|
||
{
|
||
return didPerformWorkStackCursor.current;
|
||
}
|
||
}
|
||
function isContextProvider(type) {
|
||
{
|
||
var childContextTypes = type.childContextTypes;
|
||
return childContextTypes !== null && childContextTypes !== undefined;
|
||
}
|
||
}
|
||
function popContext(fiber) {
|
||
{
|
||
pop(didPerformWorkStackCursor, fiber);
|
||
pop(contextStackCursor, fiber);
|
||
}
|
||
}
|
||
function popTopLevelContextObject(fiber) {
|
||
{
|
||
pop(didPerformWorkStackCursor, fiber);
|
||
pop(contextStackCursor, fiber);
|
||
}
|
||
}
|
||
function pushTopLevelContextObject(fiber, context, didChange) {
|
||
{
|
||
if (contextStackCursor.current !== emptyContextObject) {
|
||
throw new Error('Unexpected context found on stack. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
push(contextStackCursor, context, fiber);
|
||
push(didPerformWorkStackCursor, didChange, fiber);
|
||
}
|
||
}
|
||
function processChildContext(fiber, type, parentContext) {
|
||
{
|
||
var instance = fiber.stateNode;
|
||
var childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future.
|
||
// It has only been added in Fiber to match the (unintentional) behavior in Stack.
|
||
if (typeof instance.getChildContext !== 'function') {
|
||
{
|
||
var componentName = getComponentNameFromFiber(fiber) || 'Unknown';
|
||
if (!warnedAboutMissingGetChildContext[componentName]) {
|
||
warnedAboutMissingGetChildContext[componentName] = true;
|
||
error('%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName);
|
||
}
|
||
}
|
||
return parentContext;
|
||
}
|
||
var childContext = instance.getChildContext();
|
||
for (var contextKey in childContext) {
|
||
if (!(contextKey in childContextTypes)) {
|
||
throw new Error((getComponentNameFromFiber(fiber) || 'Unknown') + ".getChildContext(): key \"" + contextKey + "\" is not defined in childContextTypes.");
|
||
}
|
||
}
|
||
{
|
||
var name = getComponentNameFromFiber(fiber) || 'Unknown';
|
||
checkPropTypes(childContextTypes, childContext, 'child context', name);
|
||
}
|
||
return assign({}, parentContext, childContext);
|
||
}
|
||
}
|
||
function pushContextProvider(workInProgress) {
|
||
{
|
||
var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity.
|
||
// If the instance does not exist yet, we will push null at first,
|
||
// and replace it on the stack later when invalidating the context.
|
||
var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject; // Remember the parent context so we can merge with it later.
|
||
// Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
|
||
previousContext = contextStackCursor.current;
|
||
push(contextStackCursor, memoizedMergedChildContext, workInProgress);
|
||
push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress);
|
||
return true;
|
||
}
|
||
}
|
||
function invalidateContextProvider(workInProgress, type, didChange) {
|
||
{
|
||
var instance = workInProgress.stateNode;
|
||
if (!instance) {
|
||
throw new Error('Expected to have an instance by this point. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
if (didChange) {
|
||
// Merge parent and own context.
|
||
// Skip this if we're not updating due to sCU.
|
||
// This avoids unnecessarily recomputing memoized values.
|
||
var mergedContext = processChildContext(workInProgress, type, previousContext);
|
||
instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one.
|
||
// It is important to unwind the context in the reverse order.
|
||
pop(didPerformWorkStackCursor, workInProgress);
|
||
pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed.
|
||
push(contextStackCursor, mergedContext, workInProgress);
|
||
push(didPerformWorkStackCursor, didChange, workInProgress);
|
||
}
|
||
else {
|
||
pop(didPerformWorkStackCursor, workInProgress);
|
||
push(didPerformWorkStackCursor, didChange, workInProgress);
|
||
}
|
||
}
|
||
}
|
||
function findCurrentUnmaskedContext(fiber) {
|
||
{
|
||
// Currently this is only used with renderSubtreeIntoContainer; not sure if it
|
||
// makes sense elsewhere
|
||
if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
|
||
throw new Error('Expected subtree parent to be a mounted class component. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
var node = fiber;
|
||
do {
|
||
switch (node.tag) {
|
||
case HostRoot:
|
||
return node.stateNode.context;
|
||
case ClassComponent:
|
||
{
|
||
var Component = node.type;
|
||
if (isContextProvider(Component)) {
|
||
return node.stateNode.__reactInternalMemoizedMergedChildContext;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
node = node.return;
|
||
} while (node !== null);
|
||
throw new Error('Found unexpected detached subtree parent. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
var LegacyRoot = 0;
|
||
var ConcurrentRoot = 1;
|
||
var NoMode =
|
||
/* */
|
||
0; // TODO: Remove ConcurrentMode by reading from the root tag instead
|
||
var ConcurrentMode =
|
||
/* */
|
||
1;
|
||
var ProfileMode =
|
||
/* */
|
||
2;
|
||
var StrictLegacyMode =
|
||
/* */
|
||
8;
|
||
var StrictEffectsMode =
|
||
/* */
|
||
16;
|
||
// TODO: This is pretty well supported by browsers. Maybe we can drop it.
|
||
var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros.
|
||
// Based on:
|
||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
|
||
var log = Math.log;
|
||
var LN2 = Math.LN2;
|
||
function clz32Fallback(x) {
|
||
var asUint = x >>> 0;
|
||
if (asUint === 0) {
|
||
return 32;
|
||
}
|
||
return 31 - (log(asUint) / LN2 | 0) | 0;
|
||
}
|
||
// If those values are changed that package should be rebuilt and redeployed.
|
||
var TotalLanes = 31;
|
||
var NoLanes =
|
||
/* */
|
||
0;
|
||
var NoLane =
|
||
/* */
|
||
0;
|
||
var SyncLane =
|
||
/* */
|
||
1;
|
||
var InputContinuousHydrationLane =
|
||
/* */
|
||
2;
|
||
var InputContinuousLane =
|
||
/* */
|
||
4;
|
||
var DefaultHydrationLane =
|
||
/* */
|
||
8;
|
||
var DefaultLane =
|
||
/* */
|
||
16;
|
||
var TransitionHydrationLane =
|
||
/* */
|
||
32;
|
||
var TransitionLanes =
|
||
/* */
|
||
4194240;
|
||
var TransitionLane1 =
|
||
/* */
|
||
64;
|
||
var TransitionLane2 =
|
||
/* */
|
||
128;
|
||
var TransitionLane3 =
|
||
/* */
|
||
256;
|
||
var TransitionLane4 =
|
||
/* */
|
||
512;
|
||
var TransitionLane5 =
|
||
/* */
|
||
1024;
|
||
var TransitionLane6 =
|
||
/* */
|
||
2048;
|
||
var TransitionLane7 =
|
||
/* */
|
||
4096;
|
||
var TransitionLane8 =
|
||
/* */
|
||
8192;
|
||
var TransitionLane9 =
|
||
/* */
|
||
16384;
|
||
var TransitionLane10 =
|
||
/* */
|
||
32768;
|
||
var TransitionLane11 =
|
||
/* */
|
||
65536;
|
||
var TransitionLane12 =
|
||
/* */
|
||
131072;
|
||
var TransitionLane13 =
|
||
/* */
|
||
262144;
|
||
var TransitionLane14 =
|
||
/* */
|
||
524288;
|
||
var TransitionLane15 =
|
||
/* */
|
||
1048576;
|
||
var TransitionLane16 =
|
||
/* */
|
||
2097152;
|
||
var RetryLanes =
|
||
/* */
|
||
130023424;
|
||
var RetryLane1 =
|
||
/* */
|
||
4194304;
|
||
var RetryLane2 =
|
||
/* */
|
||
8388608;
|
||
var RetryLane3 =
|
||
/* */
|
||
16777216;
|
||
var RetryLane4 =
|
||
/* */
|
||
33554432;
|
||
var RetryLane5 =
|
||
/* */
|
||
67108864;
|
||
var SomeRetryLane = RetryLane1;
|
||
var SelectiveHydrationLane =
|
||
/* */
|
||
134217728;
|
||
var NonIdleLanes =
|
||
/* */
|
||
268435455;
|
||
var IdleHydrationLane =
|
||
/* */
|
||
268435456;
|
||
var IdleLane =
|
||
/* */
|
||
536870912;
|
||
var OffscreenLane =
|
||
/* */
|
||
1073741824; // This function is used for the experimental timeline (react-devtools-timeline)
|
||
// It should be kept in sync with the Lanes values above.
|
||
function getLabelForLane(lane) {
|
||
{
|
||
if (lane & SyncLane) {
|
||
return 'Sync';
|
||
}
|
||
if (lane & InputContinuousHydrationLane) {
|
||
return 'InputContinuousHydration';
|
||
}
|
||
if (lane & InputContinuousLane) {
|
||
return 'InputContinuous';
|
||
}
|
||
if (lane & DefaultHydrationLane) {
|
||
return 'DefaultHydration';
|
||
}
|
||
if (lane & DefaultLane) {
|
||
return 'Default';
|
||
}
|
||
if (lane & TransitionHydrationLane) {
|
||
return 'TransitionHydration';
|
||
}
|
||
if (lane & TransitionLanes) {
|
||
return 'Transition';
|
||
}
|
||
if (lane & RetryLanes) {
|
||
return 'Retry';
|
||
}
|
||
if (lane & SelectiveHydrationLane) {
|
||
return 'SelectiveHydration';
|
||
}
|
||
if (lane & IdleHydrationLane) {
|
||
return 'IdleHydration';
|
||
}
|
||
if (lane & IdleLane) {
|
||
return 'Idle';
|
||
}
|
||
if (lane & OffscreenLane) {
|
||
return 'Offscreen';
|
||
}
|
||
}
|
||
}
|
||
var NoTimestamp = -1;
|
||
var nextTransitionLane = TransitionLane1;
|
||
var nextRetryLane = RetryLane1;
|
||
function getHighestPriorityLanes(lanes) {
|
||
switch (getHighestPriorityLane(lanes)) {
|
||
case SyncLane:
|
||
return SyncLane;
|
||
case InputContinuousHydrationLane:
|
||
return InputContinuousHydrationLane;
|
||
case InputContinuousLane:
|
||
return InputContinuousLane;
|
||
case DefaultHydrationLane:
|
||
return DefaultHydrationLane;
|
||
case DefaultLane:
|
||
return DefaultLane;
|
||
case TransitionHydrationLane:
|
||
return TransitionHydrationLane;
|
||
case TransitionLane1:
|
||
case TransitionLane2:
|
||
case TransitionLane3:
|
||
case TransitionLane4:
|
||
case TransitionLane5:
|
||
case TransitionLane6:
|
||
case TransitionLane7:
|
||
case TransitionLane8:
|
||
case TransitionLane9:
|
||
case TransitionLane10:
|
||
case TransitionLane11:
|
||
case TransitionLane12:
|
||
case TransitionLane13:
|
||
case TransitionLane14:
|
||
case TransitionLane15:
|
||
case TransitionLane16:
|
||
return lanes & TransitionLanes;
|
||
case RetryLane1:
|
||
case RetryLane2:
|
||
case RetryLane3:
|
||
case RetryLane4:
|
||
case RetryLane5:
|
||
return lanes & RetryLanes;
|
||
case SelectiveHydrationLane:
|
||
return SelectiveHydrationLane;
|
||
case IdleHydrationLane:
|
||
return IdleHydrationLane;
|
||
case IdleLane:
|
||
return IdleLane;
|
||
case OffscreenLane:
|
||
return OffscreenLane;
|
||
default:
|
||
{
|
||
error('Should have found matching lanes. This is a bug in React.');
|
||
} // This shouldn't be reachable, but as a fallback, return the entire bitmask.
|
||
return lanes;
|
||
}
|
||
}
|
||
function getNextLanes(root, wipLanes) {
|
||
// Early bailout if there's no pending work left.
|
||
var pendingLanes = root.pendingLanes;
|
||
if (pendingLanes === NoLanes) {
|
||
return NoLanes;
|
||
}
|
||
var nextLanes = NoLanes;
|
||
var suspendedLanes = root.suspendedLanes;
|
||
var pingedLanes = root.pingedLanes; // Do not work on any idle work until all the non-idle work has finished,
|
||
// even if the work is suspended.
|
||
var nonIdlePendingLanes = pendingLanes & NonIdleLanes;
|
||
if (nonIdlePendingLanes !== NoLanes) {
|
||
var nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
|
||
if (nonIdleUnblockedLanes !== NoLanes) {
|
||
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
|
||
}
|
||
else {
|
||
var nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
|
||
if (nonIdlePingedLanes !== NoLanes) {
|
||
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// The only remaining work is Idle.
|
||
var unblockedLanes = pendingLanes & ~suspendedLanes;
|
||
if (unblockedLanes !== NoLanes) {
|
||
nextLanes = getHighestPriorityLanes(unblockedLanes);
|
||
}
|
||
else {
|
||
if (pingedLanes !== NoLanes) {
|
||
nextLanes = getHighestPriorityLanes(pingedLanes);
|
||
}
|
||
}
|
||
}
|
||
if (nextLanes === NoLanes) {
|
||
// This should only be reachable if we're suspended
|
||
// TODO: Consider warning in this path if a fallback timer is not scheduled.
|
||
return NoLanes;
|
||
} // If we're already in the middle of a render, switching lanes will interrupt
|
||
// it and we'll lose our progress. We should only do this if the new lanes are
|
||
// higher priority.
|
||
if (wipLanes !== NoLanes && wipLanes !== nextLanes && // If we already suspended with a delay, then interrupting is fine. Don't
|
||
// bother waiting until the root is complete.
|
||
(wipLanes & suspendedLanes) === NoLanes) {
|
||
var nextLane = getHighestPriorityLane(nextLanes);
|
||
var wipLane = getHighestPriorityLane(wipLanes);
|
||
if ( // Tests whether the next lane is equal or lower priority than the wip
|
||
// one. This works because the bits decrease in priority as you go left.
|
||
nextLane >= wipLane || // Default priority updates should not interrupt transition updates. The
|
||
// only difference between default updates and transition updates is that
|
||
// default updates do not support refresh transitions.
|
||
nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes) {
|
||
// Keep working on the existing in-progress tree. Do not interrupt.
|
||
return wipLanes;
|
||
}
|
||
}
|
||
if ((nextLanes & InputContinuousLane) !== NoLanes) {
|
||
// When updates are sync by default, we entangle continuous priority updates
|
||
// and default updates, so they render in the same batch. The only reason
|
||
// they use separate lanes is because continuous updates should interrupt
|
||
// transitions, but default updates should not.
|
||
nextLanes |= pendingLanes & DefaultLane;
|
||
} // Check for entangled lanes and add them to the batch.
|
||
//
|
||
// A lane is said to be entangled with another when it's not allowed to render
|
||
// in a batch that does not also include the other lane. Typically we do this
|
||
// when multiple updates have the same source, and we only want to respond to
|
||
// the most recent event from that source.
|
||
//
|
||
// Note that we apply entanglements *after* checking for partial work above.
|
||
// This means that if a lane is entangled during an interleaved event while
|
||
// it's already rendering, we won't interrupt it. This is intentional, since
|
||
// entanglement is usually "best effort": we'll try our best to render the
|
||
// lanes in the same batch, but it's not worth throwing out partially
|
||
// completed work in order to do it.
|
||
// TODO: Reconsider this. The counter-argument is that the partial work
|
||
// represents an intermediate state, which we don't want to show to the user.
|
||
// And by spending extra time finishing it, we're increasing the amount of
|
||
// time it takes to show the final state, which is what they are actually
|
||
// waiting for.
|
||
//
|
||
// For those exceptions where entanglement is semantically important, like
|
||
// useMutableSource, we should ensure that there is no partial work at the
|
||
// time we apply the entanglement.
|
||
var entangledLanes = root.entangledLanes;
|
||
if (entangledLanes !== NoLanes) {
|
||
var entanglements = root.entanglements;
|
||
var lanes = nextLanes & entangledLanes;
|
||
while (lanes > 0) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
nextLanes |= entanglements[index];
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
return nextLanes;
|
||
}
|
||
function getMostRecentEventTime(root, lanes) {
|
||
var eventTimes = root.eventTimes;
|
||
var mostRecentEventTime = NoTimestamp;
|
||
while (lanes > 0) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
var eventTime = eventTimes[index];
|
||
if (eventTime > mostRecentEventTime) {
|
||
mostRecentEventTime = eventTime;
|
||
}
|
||
lanes &= ~lane;
|
||
}
|
||
return mostRecentEventTime;
|
||
}
|
||
function computeExpirationTime(lane, currentTime) {
|
||
switch (lane) {
|
||
case SyncLane:
|
||
case InputContinuousHydrationLane:
|
||
case InputContinuousLane:
|
||
// User interactions should expire slightly more quickly.
|
||
//
|
||
// NOTE: This is set to the corresponding constant as in Scheduler.js.
|
||
// When we made it larger, a product metric in www regressed, suggesting
|
||
// there's a user interaction that's being starved by a series of
|
||
// synchronous updates. If that theory is correct, the proper solution is
|
||
// to fix the starvation. However, this scenario supports the idea that
|
||
// expiration times are an important safeguard when starvation
|
||
// does happen.
|
||
return currentTime + 250;
|
||
case DefaultHydrationLane:
|
||
case DefaultLane:
|
||
case TransitionHydrationLane:
|
||
case TransitionLane1:
|
||
case TransitionLane2:
|
||
case TransitionLane3:
|
||
case TransitionLane4:
|
||
case TransitionLane5:
|
||
case TransitionLane6:
|
||
case TransitionLane7:
|
||
case TransitionLane8:
|
||
case TransitionLane9:
|
||
case TransitionLane10:
|
||
case TransitionLane11:
|
||
case TransitionLane12:
|
||
case TransitionLane13:
|
||
case TransitionLane14:
|
||
case TransitionLane15:
|
||
case TransitionLane16:
|
||
return currentTime + 5000;
|
||
case RetryLane1:
|
||
case RetryLane2:
|
||
case RetryLane3:
|
||
case RetryLane4:
|
||
case RetryLane5:
|
||
// TODO: Retries should be allowed to expire if they are CPU bound for
|
||
// too long, but when I made this change it caused a spike in browser
|
||
// crashes. There must be some other underlying bug; not super urgent but
|
||
// ideally should figure out why and fix it. Unfortunately we don't have
|
||
// a repro for the crashes, only detected via production metrics.
|
||
return NoTimestamp;
|
||
case SelectiveHydrationLane:
|
||
case IdleHydrationLane:
|
||
case IdleLane:
|
||
case OffscreenLane:
|
||
// Anything idle priority or lower should never expire.
|
||
return NoTimestamp;
|
||
default:
|
||
{
|
||
error('Should have found matching lanes. This is a bug in React.');
|
||
}
|
||
return NoTimestamp;
|
||
}
|
||
}
|
||
function markStarvedLanesAsExpired(root, currentTime) {
|
||
// TODO: This gets called every time we yield. We can optimize by storing
|
||
// the earliest expiration time on the root. Then use that to quickly bail out
|
||
// of this function.
|
||
var pendingLanes = root.pendingLanes;
|
||
var suspendedLanes = root.suspendedLanes;
|
||
var pingedLanes = root.pingedLanes;
|
||
var expirationTimes = root.expirationTimes; // Iterate through the pending lanes and check if we've reached their
|
||
// expiration time. If so, we'll assume the update is being starved and mark
|
||
// it as expired to force it to finish.
|
||
var lanes = pendingLanes;
|
||
while (lanes > 0) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
var expirationTime = expirationTimes[index];
|
||
if (expirationTime === NoTimestamp) {
|
||
// Found a pending lane with no expiration time. If it's not suspended, or
|
||
// if it's pinged, assume it's CPU-bound. Compute a new expiration time
|
||
// using the current time.
|
||
if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) {
|
||
// Assumes timestamps are monotonically increasing.
|
||
expirationTimes[index] = computeExpirationTime(lane, currentTime);
|
||
}
|
||
}
|
||
else if (expirationTime <= currentTime) {
|
||
// This lane expired
|
||
root.expiredLanes |= lane;
|
||
}
|
||
lanes &= ~lane;
|
||
}
|
||
} // This returns the highest priority pending lanes regardless of whether they
|
||
// are suspended.
|
||
function getHighestPriorityPendingLanes(root) {
|
||
return getHighestPriorityLanes(root.pendingLanes);
|
||
}
|
||
function getLanesToRetrySynchronouslyOnError(root) {
|
||
var everythingButOffscreen = root.pendingLanes & ~OffscreenLane;
|
||
if (everythingButOffscreen !== NoLanes) {
|
||
return everythingButOffscreen;
|
||
}
|
||
if (everythingButOffscreen & OffscreenLane) {
|
||
return OffscreenLane;
|
||
}
|
||
return NoLanes;
|
||
}
|
||
function includesSyncLane(lanes) {
|
||
return (lanes & SyncLane) !== NoLanes;
|
||
}
|
||
function includesNonIdleWork(lanes) {
|
||
return (lanes & NonIdleLanes) !== NoLanes;
|
||
}
|
||
function includesOnlyRetries(lanes) {
|
||
return (lanes & RetryLanes) === lanes;
|
||
}
|
||
function includesOnlyNonUrgentLanes(lanes) {
|
||
var UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
|
||
return (lanes & UrgentLanes) === NoLanes;
|
||
}
|
||
function includesOnlyTransitions(lanes) {
|
||
return (lanes & TransitionLanes) === lanes;
|
||
}
|
||
function includesBlockingLane(root, lanes) {
|
||
var SyncDefaultLanes = InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | DefaultLane;
|
||
return (lanes & SyncDefaultLanes) !== NoLanes;
|
||
}
|
||
function includesExpiredLane(root, lanes) {
|
||
// This is a separate check from includesBlockingLane because a lane can
|
||
// expire after a render has already started.
|
||
return (lanes & root.expiredLanes) !== NoLanes;
|
||
}
|
||
function isTransitionLane(lane) {
|
||
return (lane & TransitionLanes) !== NoLanes;
|
||
}
|
||
function claimNextTransitionLane() {
|
||
// Cycle through the lanes, assigning each new transition to the next lane.
|
||
// In most cases, this means every transition gets its own lane, until we
|
||
// run out of lanes and cycle back to the beginning.
|
||
var lane = nextTransitionLane;
|
||
nextTransitionLane <<= 1;
|
||
if ((nextTransitionLane & TransitionLanes) === NoLanes) {
|
||
nextTransitionLane = TransitionLane1;
|
||
}
|
||
return lane;
|
||
}
|
||
function claimNextRetryLane() {
|
||
var lane = nextRetryLane;
|
||
nextRetryLane <<= 1;
|
||
if ((nextRetryLane & RetryLanes) === NoLanes) {
|
||
nextRetryLane = RetryLane1;
|
||
}
|
||
return lane;
|
||
}
|
||
function getHighestPriorityLane(lanes) {
|
||
return lanes & -lanes;
|
||
}
|
||
function pickArbitraryLane(lanes) {
|
||
// This wrapper function gets inlined. Only exists so to communicate that it
|
||
// doesn't matter which bit is selected; you can pick any bit without
|
||
// affecting the algorithms where its used. Here I'm using
|
||
// getHighestPriorityLane because it requires the fewest operations.
|
||
return getHighestPriorityLane(lanes);
|
||
}
|
||
function pickArbitraryLaneIndex(lanes) {
|
||
return 31 - clz32(lanes);
|
||
}
|
||
function laneToIndex(lane) {
|
||
return pickArbitraryLaneIndex(lane);
|
||
}
|
||
function includesSomeLane(a, b) {
|
||
return (a & b) !== NoLanes;
|
||
}
|
||
function isSubsetOfLanes(set, subset) {
|
||
return (set & subset) === subset;
|
||
}
|
||
function mergeLanes(a, b) {
|
||
return a | b;
|
||
}
|
||
function removeLanes(set, subset) {
|
||
return set & ~subset;
|
||
}
|
||
function intersectLanes(a, b) {
|
||
return a & b;
|
||
} // Seems redundant, but it changes the type from a single lane (used for
|
||
// updates) to a group of lanes (used for flushing work).
|
||
function laneToLanes(lane) {
|
||
return lane;
|
||
}
|
||
function higherPriorityLane(a, b) {
|
||
// This works because the bit ranges decrease in priority as you go left.
|
||
return a !== NoLane && a < b ? a : b;
|
||
}
|
||
function createLaneMap(initial) {
|
||
// Intentionally pushing one by one.
|
||
// https://v8.dev/blog/elements-kinds#avoid-creating-holes
|
||
var laneMap = [];
|
||
for (var i = 0; i < TotalLanes; i++) {
|
||
laneMap.push(initial);
|
||
}
|
||
return laneMap;
|
||
}
|
||
function markRootUpdated(root, updateLane, eventTime) {
|
||
root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update
|
||
// could unblock them. Clear the suspended lanes so that we can try rendering
|
||
// them again.
|
||
//
|
||
// TODO: We really only need to unsuspend only lanes that are in the
|
||
// `subtreeLanes` of the updated fiber, or the update lanes of the return
|
||
// path. This would exclude suspended updates in an unrelated sibling tree,
|
||
// since there's no way for this update to unblock it.
|
||
//
|
||
// We don't do this if the incoming update is idle, because we never process
|
||
// idle updates until after all the regular updates have finished; there's no
|
||
// way it could unblock a transition.
|
||
if (updateLane !== IdleLane) {
|
||
root.suspendedLanes = NoLanes;
|
||
root.pingedLanes = NoLanes;
|
||
}
|
||
var eventTimes = root.eventTimes;
|
||
var index = laneToIndex(updateLane); // We can always overwrite an existing timestamp because we prefer the most
|
||
// recent event, and we assume time is monotonically increasing.
|
||
eventTimes[index] = eventTime;
|
||
}
|
||
function markRootSuspended(root, suspendedLanes) {
|
||
root.suspendedLanes |= suspendedLanes;
|
||
root.pingedLanes &= ~suspendedLanes; // The suspended lanes are no longer CPU-bound. Clear their expiration times.
|
||
var expirationTimes = root.expirationTimes;
|
||
var lanes = suspendedLanes;
|
||
while (lanes > 0) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
expirationTimes[index] = NoTimestamp;
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
function markRootPinged(root, pingedLanes, eventTime) {
|
||
root.pingedLanes |= root.suspendedLanes & pingedLanes;
|
||
}
|
||
function markRootFinished(root, remainingLanes) {
|
||
var noLongerPendingLanes = root.pendingLanes & ~remainingLanes;
|
||
root.pendingLanes = remainingLanes; // Let's try everything again
|
||
root.suspendedLanes = NoLanes;
|
||
root.pingedLanes = NoLanes;
|
||
root.expiredLanes &= remainingLanes;
|
||
root.mutableReadLanes &= remainingLanes;
|
||
root.entangledLanes &= remainingLanes;
|
||
var entanglements = root.entanglements;
|
||
var eventTimes = root.eventTimes;
|
||
var expirationTimes = root.expirationTimes; // Clear the lanes that no longer have pending work
|
||
var lanes = noLongerPendingLanes;
|
||
while (lanes > 0) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
entanglements[index] = NoLanes;
|
||
eventTimes[index] = NoTimestamp;
|
||
expirationTimes[index] = NoTimestamp;
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
function markRootEntangled(root, entangledLanes) {
|
||
// In addition to entangling each of the given lanes with each other, we also
|
||
// have to consider _transitive_ entanglements. For each lane that is already
|
||
// entangled with *any* of the given lanes, that lane is now transitively
|
||
// entangled with *all* the given lanes.
|
||
//
|
||
// Translated: If C is entangled with A, then entangling A with B also
|
||
// entangles C with B.
|
||
//
|
||
// If this is hard to grasp, it might help to intentionally break this
|
||
// function and look at the tests that fail in ReactTransition-test.js. Try
|
||
// commenting out one of the conditions below.
|
||
var rootEntangledLanes = root.entangledLanes |= entangledLanes;
|
||
var entanglements = root.entanglements;
|
||
var lanes = rootEntangledLanes;
|
||
while (lanes) {
|
||
var index = pickArbitraryLaneIndex(lanes);
|
||
var lane = 1 << index;
|
||
if ( // Is this one of the newly entangled lanes?
|
||
lane & entangledLanes | // Is this lane transitively entangled with the newly entangled lanes?
|
||
entanglements[index] & entangledLanes) {
|
||
entanglements[index] |= entangledLanes;
|
||
}
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
function getBumpedLaneForHydration(root, renderLanes) {
|
||
var renderLane = getHighestPriorityLane(renderLanes);
|
||
var lane;
|
||
switch (renderLane) {
|
||
case InputContinuousLane:
|
||
lane = InputContinuousHydrationLane;
|
||
break;
|
||
case DefaultLane:
|
||
lane = DefaultHydrationLane;
|
||
break;
|
||
case TransitionLane1:
|
||
case TransitionLane2:
|
||
case TransitionLane3:
|
||
case TransitionLane4:
|
||
case TransitionLane5:
|
||
case TransitionLane6:
|
||
case TransitionLane7:
|
||
case TransitionLane8:
|
||
case TransitionLane9:
|
||
case TransitionLane10:
|
||
case TransitionLane11:
|
||
case TransitionLane12:
|
||
case TransitionLane13:
|
||
case TransitionLane14:
|
||
case TransitionLane15:
|
||
case TransitionLane16:
|
||
case RetryLane1:
|
||
case RetryLane2:
|
||
case RetryLane3:
|
||
case RetryLane4:
|
||
case RetryLane5:
|
||
lane = TransitionHydrationLane;
|
||
break;
|
||
case IdleLane:
|
||
lane = IdleHydrationLane;
|
||
break;
|
||
default:
|
||
// Everything else is already either a hydration lane, or shouldn't
|
||
// be retried at a hydration lane.
|
||
lane = NoLane;
|
||
break;
|
||
} // Check if the lane we chose is suspended. If so, that indicates that we
|
||
// already attempted and failed to hydrate at that level. Also check if we're
|
||
// already rendering that lane, which is rare but could happen.
|
||
if ((lane & (root.suspendedLanes | renderLanes)) !== NoLane) {
|
||
// Give up trying to hydrate and fall back to client render.
|
||
return NoLane;
|
||
}
|
||
return lane;
|
||
}
|
||
function addFiberToLanesMap(root, fiber, lanes) {
|
||
if (!isDevToolsPresent) {
|
||
return;
|
||
}
|
||
var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
|
||
while (lanes > 0) {
|
||
var index = laneToIndex(lanes);
|
||
var lane = 1 << index;
|
||
var updaters = pendingUpdatersLaneMap[index];
|
||
updaters.add(fiber);
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
function movePendingFibersToMemoized(root, lanes) {
|
||
if (!isDevToolsPresent) {
|
||
return;
|
||
}
|
||
var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
|
||
var memoizedUpdaters = root.memoizedUpdaters;
|
||
while (lanes > 0) {
|
||
var index = laneToIndex(lanes);
|
||
var lane = 1 << index;
|
||
var updaters = pendingUpdatersLaneMap[index];
|
||
if (updaters.size > 0) {
|
||
updaters.forEach(function (fiber) {
|
||
var alternate = fiber.alternate;
|
||
if (alternate === null || !memoizedUpdaters.has(alternate)) {
|
||
memoizedUpdaters.add(fiber);
|
||
}
|
||
});
|
||
updaters.clear();
|
||
}
|
||
lanes &= ~lane;
|
||
}
|
||
}
|
||
function getTransitionsForLanes(root, lanes) {
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
var DiscreteEventPriority = SyncLane;
|
||
var ContinuousEventPriority = InputContinuousLane;
|
||
var DefaultEventPriority = DefaultLane;
|
||
var IdleEventPriority = IdleLane;
|
||
var currentUpdatePriority = NoLane;
|
||
function getCurrentUpdatePriority() {
|
||
return currentUpdatePriority;
|
||
}
|
||
function setCurrentUpdatePriority(newPriority) {
|
||
currentUpdatePriority = newPriority;
|
||
}
|
||
function runWithPriority(priority, fn) {
|
||
var previousPriority = currentUpdatePriority;
|
||
try {
|
||
currentUpdatePriority = priority;
|
||
return fn();
|
||
}
|
||
finally {
|
||
currentUpdatePriority = previousPriority;
|
||
}
|
||
}
|
||
function higherEventPriority(a, b) {
|
||
return a !== 0 && a < b ? a : b;
|
||
}
|
||
function lowerEventPriority(a, b) {
|
||
return a === 0 || a > b ? a : b;
|
||
}
|
||
function isHigherEventPriority(a, b) {
|
||
return a !== 0 && a < b;
|
||
}
|
||
function lanesToEventPriority(lanes) {
|
||
var lane = getHighestPriorityLane(lanes);
|
||
if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
|
||
return DiscreteEventPriority;
|
||
}
|
||
if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
|
||
return ContinuousEventPriority;
|
||
}
|
||
if (includesNonIdleWork(lane)) {
|
||
return DefaultEventPriority;
|
||
}
|
||
return IdleEventPriority;
|
||
}
|
||
// This module only exists as an ESM wrapper around the external CommonJS
|
||
var scheduleCallback = Scheduler.unstable_scheduleCallback;
|
||
var cancelCallback = Scheduler.unstable_cancelCallback;
|
||
var shouldYield = Scheduler.unstable_shouldYield;
|
||
var requestPaint = Scheduler.unstable_requestPaint;
|
||
var now = Scheduler.unstable_now;
|
||
var ImmediatePriority = Scheduler.unstable_ImmediatePriority;
|
||
var UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
|
||
var NormalPriority = Scheduler.unstable_NormalPriority;
|
||
var IdlePriority = Scheduler.unstable_IdlePriority;
|
||
// this doesn't actually exist on the scheduler, but it *does*
|
||
// on scheduler/unstable_mock, which we'll need for internal testing
|
||
var unstable_yieldValue = Scheduler.unstable_yieldValue;
|
||
var unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue;
|
||
var rendererID = null;
|
||
var injectedHook = null;
|
||
var injectedProfilingHooks = null;
|
||
var hasLoggedError = false;
|
||
var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
|
||
function injectInternals(internals) {
|
||
if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
||
// No DevTools
|
||
return false;
|
||
}
|
||
var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||
if (hook.isDisabled) {
|
||
// This isn't a real property on the hook, but it can be set to opt out
|
||
// of DevTools integration and associated warnings and logs.
|
||
// https://github.com/facebook/react/issues/3877
|
||
return true;
|
||
}
|
||
if (!hook.supportsFiber) {
|
||
{
|
||
error('The installed version of React DevTools is too old and will not work ' + 'with the current version of React. Please update React DevTools. ' + 'https://reactjs.org/link/react-devtools');
|
||
} // DevTools exists, even though it doesn't support Fiber.
|
||
return true;
|
||
}
|
||
try {
|
||
if (enableSchedulingProfiler) {
|
||
// Conditionally inject these hooks only if Timeline profiler is supported by this build.
|
||
// This gives DevTools a way to feature detect that isn't tied to version number
|
||
// (since profiling and timeline are controlled by different feature flags).
|
||
internals = assign({}, internals, {
|
||
getLaneLabelMap: getLaneLabelMap,
|
||
injectProfilingHooks: injectProfilingHooks
|
||
});
|
||
}
|
||
rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks.
|
||
injectedHook = hook;
|
||
}
|
||
catch (err) {
|
||
// Catch all errors because it is unsafe to throw during initialization.
|
||
{
|
||
error('React instrumentation encountered an error: %s.', err);
|
||
}
|
||
}
|
||
if (hook.checkDCE) {
|
||
// This is the real DevTools.
|
||
return true;
|
||
}
|
||
else {
|
||
// This is likely a hook installed by Fast Refresh runtime.
|
||
return false;
|
||
}
|
||
}
|
||
function onScheduleRoot(root, children) {
|
||
{
|
||
if (injectedHook && typeof injectedHook.onScheduleFiberRoot === 'function') {
|
||
try {
|
||
injectedHook.onScheduleFiberRoot(rendererID, root, children);
|
||
}
|
||
catch (err) {
|
||
if (!hasLoggedError) {
|
||
hasLoggedError = true;
|
||
error('React instrumentation encountered an error: %s', err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function onCommitRoot(root, eventPriority) {
|
||
if (injectedHook && typeof injectedHook.onCommitFiberRoot === 'function') {
|
||
try {
|
||
var didError = (root.current.flags & DidCapture) === DidCapture;
|
||
if (enableProfilerTimer) {
|
||
var schedulerPriority;
|
||
switch (eventPriority) {
|
||
case DiscreteEventPriority:
|
||
schedulerPriority = ImmediatePriority;
|
||
break;
|
||
case ContinuousEventPriority:
|
||
schedulerPriority = UserBlockingPriority;
|
||
break;
|
||
case DefaultEventPriority:
|
||
schedulerPriority = NormalPriority;
|
||
break;
|
||
case IdleEventPriority:
|
||
schedulerPriority = IdlePriority;
|
||
break;
|
||
default:
|
||
schedulerPriority = NormalPriority;
|
||
break;
|
||
}
|
||
injectedHook.onCommitFiberRoot(rendererID, root, schedulerPriority, didError);
|
||
}
|
||
else {
|
||
injectedHook.onCommitFiberRoot(rendererID, root, undefined, didError);
|
||
}
|
||
}
|
||
catch (err) {
|
||
{
|
||
if (!hasLoggedError) {
|
||
hasLoggedError = true;
|
||
error('React instrumentation encountered an error: %s', err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function onPostCommitRoot(root) {
|
||
if (injectedHook && typeof injectedHook.onPostCommitFiberRoot === 'function') {
|
||
try {
|
||
injectedHook.onPostCommitFiberRoot(rendererID, root);
|
||
}
|
||
catch (err) {
|
||
{
|
||
if (!hasLoggedError) {
|
||
hasLoggedError = true;
|
||
error('React instrumentation encountered an error: %s', err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function onCommitUnmount(fiber) {
|
||
if (injectedHook && typeof injectedHook.onCommitFiberUnmount === 'function') {
|
||
try {
|
||
injectedHook.onCommitFiberUnmount(rendererID, fiber);
|
||
}
|
||
catch (err) {
|
||
{
|
||
if (!hasLoggedError) {
|
||
hasLoggedError = true;
|
||
error('React instrumentation encountered an error: %s', err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function setIsStrictModeForDevtools(newIsStrictMode) {
|
||
{
|
||
if (typeof unstable_yieldValue === 'function') {
|
||
// We're in a test because Scheduler.unstable_yieldValue only exists
|
||
// in SchedulerMock. To reduce the noise in strict mode tests,
|
||
// suppress warnings and disable scheduler yielding during the double render
|
||
unstable_setDisableYieldValue(newIsStrictMode);
|
||
setSuppressWarning(newIsStrictMode);
|
||
}
|
||
if (injectedHook && typeof injectedHook.setStrictMode === 'function') {
|
||
try {
|
||
injectedHook.setStrictMode(rendererID, newIsStrictMode);
|
||
}
|
||
catch (err) {
|
||
{
|
||
if (!hasLoggedError) {
|
||
hasLoggedError = true;
|
||
error('React instrumentation encountered an error: %s', err);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
} // Profiler API hooks
|
||
function injectProfilingHooks(profilingHooks) {
|
||
injectedProfilingHooks = profilingHooks;
|
||
}
|
||
function getLaneLabelMap() {
|
||
{
|
||
var map = new Map();
|
||
var lane = 1;
|
||
for (var index = 0; index < TotalLanes; index++) {
|
||
var label = getLabelForLane(lane);
|
||
map.set(lane, label);
|
||
lane *= 2;
|
||
}
|
||
return map;
|
||
}
|
||
}
|
||
function markCommitStarted(lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStarted === 'function') {
|
||
injectedProfilingHooks.markCommitStarted(lanes);
|
||
}
|
||
}
|
||
}
|
||
function markCommitStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStopped === 'function') {
|
||
injectedProfilingHooks.markCommitStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentRenderStarted(fiber) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStarted === 'function') {
|
||
injectedProfilingHooks.markComponentRenderStarted(fiber);
|
||
}
|
||
}
|
||
}
|
||
function markComponentRenderStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStopped === 'function') {
|
||
injectedProfilingHooks.markComponentRenderStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentPassiveEffectMountStarted(fiber) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted === 'function') {
|
||
injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber);
|
||
}
|
||
}
|
||
}
|
||
function markComponentPassiveEffectMountStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped === 'function') {
|
||
injectedProfilingHooks.markComponentPassiveEffectMountStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentPassiveEffectUnmountStarted(fiber) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted === 'function') {
|
||
injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber);
|
||
}
|
||
}
|
||
}
|
||
function markComponentPassiveEffectUnmountStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped === 'function') {
|
||
injectedProfilingHooks.markComponentPassiveEffectUnmountStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentLayoutEffectMountStarted(fiber) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted === 'function') {
|
||
injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber);
|
||
}
|
||
}
|
||
}
|
||
function markComponentLayoutEffectMountStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped === 'function') {
|
||
injectedProfilingHooks.markComponentLayoutEffectMountStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentLayoutEffectUnmountStarted(fiber) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted === 'function') {
|
||
injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber);
|
||
}
|
||
}
|
||
}
|
||
function markComponentLayoutEffectUnmountStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped === 'function') {
|
||
injectedProfilingHooks.markComponentLayoutEffectUnmountStopped();
|
||
}
|
||
}
|
||
}
|
||
function markComponentErrored(fiber, thrownValue, lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentErrored === 'function') {
|
||
injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes);
|
||
}
|
||
}
|
||
}
|
||
function markComponentSuspended(fiber, wakeable, lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentSuspended === 'function') {
|
||
injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes);
|
||
}
|
||
}
|
||
}
|
||
function markLayoutEffectsStarted(lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStarted === 'function') {
|
||
injectedProfilingHooks.markLayoutEffectsStarted(lanes);
|
||
}
|
||
}
|
||
}
|
||
function markLayoutEffectsStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStopped === 'function') {
|
||
injectedProfilingHooks.markLayoutEffectsStopped();
|
||
}
|
||
}
|
||
}
|
||
function markPassiveEffectsStarted(lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStarted === 'function') {
|
||
injectedProfilingHooks.markPassiveEffectsStarted(lanes);
|
||
}
|
||
}
|
||
}
|
||
function markPassiveEffectsStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStopped === 'function') {
|
||
injectedProfilingHooks.markPassiveEffectsStopped();
|
||
}
|
||
}
|
||
}
|
||
function markRenderStarted(lanes) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStarted === 'function') {
|
||
injectedProfilingHooks.markRenderStarted(lanes);
|
||
}
|
||
}
|
||
}
|
||
function markRenderYielded() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderYielded === 'function') {
|
||
injectedProfilingHooks.markRenderYielded();
|
||
}
|
||
}
|
||
}
|
||
function markRenderStopped() {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStopped === 'function') {
|
||
injectedProfilingHooks.markRenderStopped();
|
||
}
|
||
}
|
||
}
|
||
function markRenderScheduled(lane) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderScheduled === 'function') {
|
||
injectedProfilingHooks.markRenderScheduled(lane);
|
||
}
|
||
}
|
||
}
|
||
function markForceUpdateScheduled(fiber, lane) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markForceUpdateScheduled === 'function') {
|
||
injectedProfilingHooks.markForceUpdateScheduled(fiber, lane);
|
||
}
|
||
}
|
||
}
|
||
function markStateUpdateScheduled(fiber, lane) {
|
||
{
|
||
if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markStateUpdateScheduled === 'function') {
|
||
injectedProfilingHooks.markStateUpdateScheduled(fiber, lane);
|
||
}
|
||
}
|
||
}
|
||
/**
|
||
* inlined Object.is polyfill to avoid requiring consumers ship their own
|
||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
||
*/
|
||
function is(x, y) {
|
||
return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
|
||
;
|
||
}
|
||
var objectIs = typeof Object.is === 'function' ? Object.is : is;
|
||
var syncQueue = null;
|
||
var includesLegacySyncCallbacks = false;
|
||
var isFlushingSyncQueue = false;
|
||
function scheduleSyncCallback(callback) {
|
||
// Push this callback into an internal queue. We'll flush these either in
|
||
// the next tick, or earlier if something calls `flushSyncCallbackQueue`.
|
||
if (syncQueue === null) {
|
||
syncQueue = [callback];
|
||
}
|
||
else {
|
||
// Push onto existing queue. Don't need to schedule a callback because
|
||
// we already scheduled one when we created the queue.
|
||
syncQueue.push(callback);
|
||
}
|
||
}
|
||
function scheduleLegacySyncCallback(callback) {
|
||
includesLegacySyncCallbacks = true;
|
||
scheduleSyncCallback(callback);
|
||
}
|
||
function flushSyncCallbacksOnlyInLegacyMode() {
|
||
// Only flushes the queue if there's a legacy sync callback scheduled.
|
||
// TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
|
||
// it might make more sense for the queue to be a list of roots instead of a
|
||
// list of generic callbacks. Then we can have two: one for legacy roots, one
|
||
// for concurrent roots. And this method would only flush the legacy ones.
|
||
if (includesLegacySyncCallbacks) {
|
||
flushSyncCallbacks();
|
||
}
|
||
}
|
||
function flushSyncCallbacks() {
|
||
if (!isFlushingSyncQueue && syncQueue !== null) {
|
||
// Prevent re-entrance.
|
||
isFlushingSyncQueue = true;
|
||
var i = 0;
|
||
var previousUpdatePriority = getCurrentUpdatePriority();
|
||
try {
|
||
var isSync = true;
|
||
var queue = syncQueue; // TODO: Is this necessary anymore? The only user code that runs in this
|
||
// queue is in the render or commit phases.
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
for (; i < queue.length; i++) {
|
||
var callback = queue[i];
|
||
do {
|
||
callback = callback(isSync);
|
||
} while (callback !== null);
|
||
}
|
||
syncQueue = null;
|
||
includesLegacySyncCallbacks = false;
|
||
}
|
||
catch (error) {
|
||
// If something throws, leave the remaining callbacks on the queue.
|
||
if (syncQueue !== null) {
|
||
syncQueue = syncQueue.slice(i + 1);
|
||
} // Resume flushing in the next tick
|
||
scheduleCallback(ImmediatePriority, flushSyncCallbacks);
|
||
throw error;
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousUpdatePriority);
|
||
isFlushingSyncQueue = false;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
// This is imported by the event replaying implementation in React DOM. It's
|
||
// in a separate file to break a circular dependency between the renderer and
|
||
// the reconciler.
|
||
function isRootDehydrated(root) {
|
||
var currentState = root.current.memoizedState;
|
||
return currentState.isDehydrated;
|
||
}
|
||
// TODO: Use the unified fiber stack module instead of this local one?
|
||
// Intentionally not using it yet to derisk the initial implementation, because
|
||
// the way we push/pop these values is a bit unusual. If there's a mistake, I'd
|
||
// rather the ids be wrong than crash the whole reconciler.
|
||
var forkStack = [];
|
||
var forkStackIndex = 0;
|
||
var treeForkProvider = null;
|
||
var treeForkCount = 0;
|
||
var idStack = [];
|
||
var idStackIndex = 0;
|
||
var treeContextProvider = null;
|
||
var treeContextId = 1;
|
||
var treeContextOverflow = '';
|
||
function isForkedChild(workInProgress) {
|
||
warnIfNotHydrating();
|
||
return (workInProgress.flags & Forked) !== NoFlags;
|
||
}
|
||
function getForksAtLevel(workInProgress) {
|
||
warnIfNotHydrating();
|
||
return treeForkCount;
|
||
}
|
||
function getTreeId() {
|
||
var overflow = treeContextOverflow;
|
||
var idWithLeadingBit = treeContextId;
|
||
var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit);
|
||
return id.toString(32) + overflow;
|
||
}
|
||
function pushTreeFork(workInProgress, totalChildren) {
|
||
// This is called right after we reconcile an array (or iterator) of child
|
||
// fibers, because that's the only place where we know how many children in
|
||
// the whole set without doing extra work later, or storing addtional
|
||
// information on the fiber.
|
||
//
|
||
// That's why this function is separate from pushTreeId — it's called during
|
||
// the render phase of the fork parent, not the child, which is where we push
|
||
// the other context values.
|
||
//
|
||
// In the Fizz implementation this is much simpler because the child is
|
||
// rendered in the same callstack as the parent.
|
||
//
|
||
// It might be better to just add a `forks` field to the Fiber type. It would
|
||
// make this module simpler.
|
||
warnIfNotHydrating();
|
||
forkStack[forkStackIndex++] = treeForkCount;
|
||
forkStack[forkStackIndex++] = treeForkProvider;
|
||
treeForkProvider = workInProgress;
|
||
treeForkCount = totalChildren;
|
||
}
|
||
function pushTreeId(workInProgress, totalChildren, index) {
|
||
warnIfNotHydrating();
|
||
idStack[idStackIndex++] = treeContextId;
|
||
idStack[idStackIndex++] = treeContextOverflow;
|
||
idStack[idStackIndex++] = treeContextProvider;
|
||
treeContextProvider = workInProgress;
|
||
var baseIdWithLeadingBit = treeContextId;
|
||
var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
|
||
// of the id; we use it to account for leading 0s.
|
||
var baseLength = getBitLength(baseIdWithLeadingBit) - 1;
|
||
var baseId = baseIdWithLeadingBit & ~(1 << baseLength);
|
||
var slot = index + 1;
|
||
var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into
|
||
// consideration the leading 1 we use to mark the end of the sequence.
|
||
if (length > 30) {
|
||
// We overflowed the bitwise-safe range. Fall back to slower algorithm.
|
||
// This branch assumes the length of the base id is greater than 5; it won't
|
||
// work for smaller ids, because you need 5 bits per character.
|
||
//
|
||
// We encode the id in multiple steps: first the base id, then the
|
||
// remaining digits.
|
||
//
|
||
// Each 5 bit sequence corresponds to a single base 32 character. So for
|
||
// example, if the current id is 23 bits long, we can convert 20 of those
|
||
// bits into a string of 4 characters, with 3 bits left over.
|
||
//
|
||
// First calculate how many bits in the base id represent a complete
|
||
// sequence of characters.
|
||
var numberOfOverflowBits = baseLength - baseLength % 5; // Then create a bitmask that selects only those bits.
|
||
var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string.
|
||
var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id.
|
||
var restOfBaseId = baseId >> numberOfOverflowBits;
|
||
var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because
|
||
// we made more room, this time it won't overflow.
|
||
var restOfLength = getBitLength(totalChildren) + restOfBaseLength;
|
||
var restOfNewBits = slot << restOfBaseLength;
|
||
var id = restOfNewBits | restOfBaseId;
|
||
var overflow = newOverflow + baseOverflow;
|
||
treeContextId = 1 << restOfLength | id;
|
||
treeContextOverflow = overflow;
|
||
}
|
||
else {
|
||
// Normal path
|
||
var newBits = slot << baseLength;
|
||
var _id = newBits | baseId;
|
||
var _overflow = baseOverflow;
|
||
treeContextId = 1 << length | _id;
|
||
treeContextOverflow = _overflow;
|
||
}
|
||
}
|
||
function pushMaterializedTreeId(workInProgress) {
|
||
warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear
|
||
// in its children.
|
||
var returnFiber = workInProgress.return;
|
||
if (returnFiber !== null) {
|
||
var numberOfForks = 1;
|
||
var slotIndex = 0;
|
||
pushTreeFork(workInProgress, numberOfForks);
|
||
pushTreeId(workInProgress, numberOfForks, slotIndex);
|
||
}
|
||
}
|
||
function getBitLength(number) {
|
||
return 32 - clz32(number);
|
||
}
|
||
function getLeadingBit(id) {
|
||
return 1 << getBitLength(id) - 1;
|
||
}
|
||
function popTreeContext(workInProgress) {
|
||
// Restore the previous values.
|
||
// This is a bit more complicated than other context-like modules in Fiber
|
||
// because the same Fiber may appear on the stack multiple times and for
|
||
// different reasons. We have to keep popping until the work-in-progress is
|
||
// no longer at the top of the stack.
|
||
while (workInProgress === treeForkProvider) {
|
||
treeForkProvider = forkStack[--forkStackIndex];
|
||
forkStack[forkStackIndex] = null;
|
||
treeForkCount = forkStack[--forkStackIndex];
|
||
forkStack[forkStackIndex] = null;
|
||
}
|
||
while (workInProgress === treeContextProvider) {
|
||
treeContextProvider = idStack[--idStackIndex];
|
||
idStack[idStackIndex] = null;
|
||
treeContextOverflow = idStack[--idStackIndex];
|
||
idStack[idStackIndex] = null;
|
||
treeContextId = idStack[--idStackIndex];
|
||
idStack[idStackIndex] = null;
|
||
}
|
||
}
|
||
function getSuspendedTreeContext() {
|
||
warnIfNotHydrating();
|
||
if (treeContextProvider !== null) {
|
||
return {
|
||
id: treeContextId,
|
||
overflow: treeContextOverflow
|
||
};
|
||
}
|
||
else {
|
||
return null;
|
||
}
|
||
}
|
||
function restoreSuspendedTreeContext(workInProgress, suspendedContext) {
|
||
warnIfNotHydrating();
|
||
idStack[idStackIndex++] = treeContextId;
|
||
idStack[idStackIndex++] = treeContextOverflow;
|
||
idStack[idStackIndex++] = treeContextProvider;
|
||
treeContextId = suspendedContext.id;
|
||
treeContextOverflow = suspendedContext.overflow;
|
||
treeContextProvider = workInProgress;
|
||
}
|
||
function warnIfNotHydrating() {
|
||
{
|
||
if (!getIsHydrating()) {
|
||
error('Expected to be hydrating. This is a bug in React. Please file ' + 'an issue.');
|
||
}
|
||
}
|
||
}
|
||
// This may have been an insertion or a hydration.
|
||
var hydrationParentFiber = null;
|
||
var nextHydratableInstance = null;
|
||
var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
|
||
// due to earlier mismatches or a suspended fiber.
|
||
var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary
|
||
var hydrationErrors = null;
|
||
function warnIfHydrating() {
|
||
{
|
||
if (isHydrating) {
|
||
error('We should not be hydrating here. This is a bug in React. Please file a bug.');
|
||
}
|
||
}
|
||
}
|
||
function markDidThrowWhileHydratingDEV() {
|
||
{
|
||
didSuspendOrErrorDEV = true;
|
||
}
|
||
}
|
||
function didSuspendOrErrorWhileHydratingDEV() {
|
||
{
|
||
return didSuspendOrErrorDEV;
|
||
}
|
||
}
|
||
function enterHydrationState(fiber) {
|
||
if (!supportsHydration) {
|
||
return false;
|
||
}
|
||
var parentInstance = fiber.stateNode.containerInfo;
|
||
nextHydratableInstance = getFirstHydratableChildWithinContainer(parentInstance);
|
||
hydrationParentFiber = fiber;
|
||
isHydrating = true;
|
||
hydrationErrors = null;
|
||
didSuspendOrErrorDEV = false;
|
||
return true;
|
||
}
|
||
function reenterHydrationStateFromDehydratedSuspenseInstance(fiber, suspenseInstance, treeContext) {
|
||
if (!supportsHydration) {
|
||
return false;
|
||
}
|
||
nextHydratableInstance = getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
|
||
hydrationParentFiber = fiber;
|
||
isHydrating = true;
|
||
hydrationErrors = null;
|
||
didSuspendOrErrorDEV = false;
|
||
if (treeContext !== null) {
|
||
restoreSuspendedTreeContext(fiber, treeContext);
|
||
}
|
||
return true;
|
||
}
|
||
function warnUnhydratedInstance(returnFiber, instance) {
|
||
{
|
||
switch (returnFiber.tag) {
|
||
case HostRoot:
|
||
{
|
||
didNotHydrateInstanceWithinContainer(returnFiber.stateNode.containerInfo, instance);
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
||
didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, // TODO: Delete this argument when we remove the legacy root API.
|
||
isConcurrentMode);
|
||
break;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
var suspenseState = returnFiber.memoizedState;
|
||
if (suspenseState.dehydrated !== null)
|
||
didNotHydrateInstanceWithinSuspenseInstance(suspenseState.dehydrated, instance);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function deleteHydratableInstance(returnFiber, instance) {
|
||
warnUnhydratedInstance(returnFiber, instance);
|
||
var childToDelete = createFiberFromHostInstanceForDeletion();
|
||
childToDelete.stateNode = instance;
|
||
childToDelete.return = returnFiber;
|
||
var deletions = returnFiber.deletions;
|
||
if (deletions === null) {
|
||
returnFiber.deletions = [childToDelete];
|
||
returnFiber.flags |= ChildDeletion;
|
||
}
|
||
else {
|
||
deletions.push(childToDelete);
|
||
}
|
||
}
|
||
function warnNonhydratedInstance(returnFiber, fiber) {
|
||
{
|
||
if (didSuspendOrErrorDEV) {
|
||
// Inside a boundary that already suspended. We're currently rendering the
|
||
// siblings of a suspended node. The mismatch may be due to the missing
|
||
// data, so it's probably a false positive.
|
||
return;
|
||
}
|
||
switch (returnFiber.tag) {
|
||
case HostRoot:
|
||
{
|
||
var parentContainer = returnFiber.stateNode.containerInfo;
|
||
switch (fiber.tag) {
|
||
case HostComponent:
|
||
var type = fiber.type;
|
||
var props = fiber.pendingProps;
|
||
didNotFindHydratableInstanceWithinContainer(parentContainer, type, props);
|
||
break;
|
||
case HostText:
|
||
var text = fiber.pendingProps;
|
||
didNotFindHydratableTextInstanceWithinContainer(parentContainer, text);
|
||
break;
|
||
case SuspenseComponent:
|
||
didNotFindHydratableSuspenseInstanceWithinContainer(parentContainer);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
var parentType = returnFiber.type;
|
||
var parentProps = returnFiber.memoizedProps;
|
||
var parentInstance = returnFiber.stateNode;
|
||
switch (fiber.tag) {
|
||
case HostComponent:
|
||
{
|
||
var _type = fiber.type;
|
||
var _props = fiber.pendingProps;
|
||
var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
||
didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props, // TODO: Delete this argument when we remove the legacy root API.
|
||
isConcurrentMode);
|
||
break;
|
||
}
|
||
case HostText:
|
||
{
|
||
var _text = fiber.pendingProps;
|
||
var _isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
||
didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text, // TODO: Delete this argument when we remove the legacy root API.
|
||
_isConcurrentMode);
|
||
break;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
didNotFindHydratableSuspenseInstance(parentType, parentProps, parentInstance);
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
var suspenseState = returnFiber.memoizedState;
|
||
var _parentInstance = suspenseState.dehydrated;
|
||
if (_parentInstance !== null)
|
||
switch (fiber.tag) {
|
||
case HostComponent:
|
||
var _type2 = fiber.type;
|
||
var _props2 = fiber.pendingProps;
|
||
didNotFindHydratableInstanceWithinSuspenseInstance(_parentInstance, _type2, _props2);
|
||
break;
|
||
case HostText:
|
||
var _text2 = fiber.pendingProps;
|
||
didNotFindHydratableTextInstanceWithinSuspenseInstance(_parentInstance, _text2);
|
||
break;
|
||
case SuspenseComponent:
|
||
didNotFindHydratableSuspenseInstanceWithinSuspenseInstance(_parentInstance);
|
||
break;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
function insertNonHydratedInstance(returnFiber, fiber) {
|
||
fiber.flags = fiber.flags & ~Hydrating | Placement;
|
||
warnNonhydratedInstance(returnFiber, fiber);
|
||
}
|
||
function tryHydrate(fiber, nextInstance) {
|
||
switch (fiber.tag) {
|
||
case HostComponent:
|
||
{
|
||
var type = fiber.type;
|
||
var props = fiber.pendingProps;
|
||
var instance = canHydrateInstance(nextInstance, type, props);
|
||
if (instance !== null) {
|
||
fiber.stateNode = instance;
|
||
hydrationParentFiber = fiber;
|
||
nextHydratableInstance = getFirstHydratableChild(instance);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
case HostText:
|
||
{
|
||
var text = fiber.pendingProps;
|
||
var textInstance = canHydrateTextInstance(nextInstance, text);
|
||
if (textInstance !== null) {
|
||
fiber.stateNode = textInstance;
|
||
hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate.
|
||
nextHydratableInstance = null;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
|
||
if (suspenseInstance !== null) {
|
||
var suspenseState = {
|
||
dehydrated: suspenseInstance,
|
||
treeContext: getSuspendedTreeContext(),
|
||
retryLane: OffscreenLane
|
||
};
|
||
fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber.
|
||
// This simplifies the code for getHostSibling and deleting nodes,
|
||
// since it doesn't have to consider all Suspense boundaries and
|
||
// check if they're dehydrated ones or not.
|
||
var dehydratedFragment = createFiberFromDehydratedFragment(suspenseInstance);
|
||
dehydratedFragment.return = fiber;
|
||
fiber.child = dehydratedFragment;
|
||
hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into
|
||
// it during the first pass. Instead, we'll reenter it later.
|
||
nextHydratableInstance = null;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
function shouldClientRenderOnMismatch(fiber) {
|
||
return (fiber.mode & ConcurrentMode) !== NoMode && (fiber.flags & DidCapture) === NoFlags;
|
||
}
|
||
function throwOnHydrationMismatch(fiber) {
|
||
throw new Error('Hydration failed because the initial UI does not match what was ' + 'rendered on the server.');
|
||
}
|
||
function tryToClaimNextHydratableInstance(fiber) {
|
||
if (!isHydrating) {
|
||
return;
|
||
}
|
||
var nextInstance = nextHydratableInstance;
|
||
if (!nextInstance) {
|
||
if (shouldClientRenderOnMismatch(fiber)) {
|
||
warnNonhydratedInstance(hydrationParentFiber, fiber);
|
||
throwOnHydrationMismatch();
|
||
} // Nothing to hydrate. Make it an insertion.
|
||
insertNonHydratedInstance(hydrationParentFiber, fiber);
|
||
isHydrating = false;
|
||
hydrationParentFiber = fiber;
|
||
return;
|
||
}
|
||
var firstAttemptedInstance = nextInstance;
|
||
if (!tryHydrate(fiber, nextInstance)) {
|
||
if (shouldClientRenderOnMismatch(fiber)) {
|
||
warnNonhydratedInstance(hydrationParentFiber, fiber);
|
||
throwOnHydrationMismatch();
|
||
} // If we can't hydrate this instance let's try the next one.
|
||
// We use this as a heuristic. It's based on intuition and not data so it
|
||
// might be flawed or unnecessary.
|
||
nextInstance = getNextHydratableSibling(firstAttemptedInstance);
|
||
var prevHydrationParentFiber = hydrationParentFiber;
|
||
if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
|
||
// Nothing to hydrate. Make it an insertion.
|
||
insertNonHydratedInstance(hydrationParentFiber, fiber);
|
||
isHydrating = false;
|
||
hydrationParentFiber = fiber;
|
||
return;
|
||
} // We matched the next one, we'll now assume that the first one was
|
||
// superfluous and we'll delete it. Since we can't eagerly delete it
|
||
// we'll have to schedule a deletion. To do that, this node needs a dummy
|
||
// fiber associated with it.
|
||
deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
|
||
}
|
||
}
|
||
function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
|
||
if (!supportsHydration) {
|
||
throw new Error('Expected prepareToHydrateHostInstance() to never be called. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
var instance = fiber.stateNode;
|
||
var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
|
||
var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber, shouldWarnIfMismatchDev); // TODO: Type this specific to this type of component.
|
||
fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
|
||
// is a new ref we mark this as an update.
|
||
if (updatePayload !== null) {
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
function prepareToHydrateHostTextInstance(fiber) {
|
||
if (!supportsHydration) {
|
||
throw new Error('Expected prepareToHydrateHostTextInstance() to never be called. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
var textInstance = fiber.stateNode;
|
||
var textContent = fiber.memoizedProps;
|
||
var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
|
||
var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber, shouldWarnIfMismatchDev);
|
||
if (shouldUpdate) {
|
||
// We assume that prepareToHydrateHostTextInstance is called in a context where the
|
||
// hydration parent is the parent host component of this host text.
|
||
var returnFiber = hydrationParentFiber;
|
||
if (returnFiber !== null) {
|
||
switch (returnFiber.tag) {
|
||
case HostRoot:
|
||
{
|
||
var parentContainer = returnFiber.stateNode.containerInfo;
|
||
var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
||
didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API.
|
||
isConcurrentMode);
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
var parentType = returnFiber.type;
|
||
var parentProps = returnFiber.memoizedProps;
|
||
var parentInstance = returnFiber.stateNode;
|
||
var _isConcurrentMode2 = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
||
didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API.
|
||
_isConcurrentMode2);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return shouldUpdate;
|
||
}
|
||
function prepareToHydrateHostSuspenseInstance(fiber) {
|
||
if (!supportsHydration) {
|
||
throw new Error('Expected prepareToHydrateHostSuspenseInstance() to never be called. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
var suspenseState = fiber.memoizedState;
|
||
var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null;
|
||
if (!suspenseInstance) {
|
||
throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
hydrateSuspenseInstance(suspenseInstance, fiber);
|
||
}
|
||
function skipPastDehydratedSuspenseInstance(fiber) {
|
||
if (!supportsHydration) {
|
||
throw new Error('Expected skipPastDehydratedSuspenseInstance() to never be called. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
var suspenseState = fiber.memoizedState;
|
||
var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null;
|
||
if (!suspenseInstance) {
|
||
throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
|
||
}
|
||
function popToNextHostParent(fiber) {
|
||
var parent = fiber.return;
|
||
while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== SuspenseComponent) {
|
||
parent = parent.return;
|
||
}
|
||
hydrationParentFiber = parent;
|
||
}
|
||
function popHydrationState(fiber) {
|
||
if (!supportsHydration) {
|
||
return false;
|
||
}
|
||
if (fiber !== hydrationParentFiber) {
|
||
// We're deeper than the current hydration context, inside an inserted
|
||
// tree.
|
||
return false;
|
||
}
|
||
if (!isHydrating) {
|
||
// If we're not currently hydrating but we're in a hydration context, then
|
||
// we were an insertion and now need to pop up reenter hydration of our
|
||
// siblings.
|
||
popToNextHostParent(fiber);
|
||
isHydrating = true;
|
||
return false;
|
||
} // If we have any remaining hydratable nodes, we need to delete them now.
|
||
// We only do this deeper than head and body since they tend to have random
|
||
// other nodes in them. We also ignore components with pure text content in
|
||
// side of them. We also don't delete anything inside the root container.
|
||
if (fiber.tag !== HostRoot && (fiber.tag !== HostComponent || shouldDeleteUnhydratedTailInstances(fiber.type) && !shouldSetTextContent(fiber.type, fiber.memoizedProps))) {
|
||
var nextInstance = nextHydratableInstance;
|
||
if (nextInstance) {
|
||
if (shouldClientRenderOnMismatch(fiber)) {
|
||
warnIfUnhydratedTailNodes(fiber);
|
||
throwOnHydrationMismatch();
|
||
}
|
||
else {
|
||
while (nextInstance) {
|
||
deleteHydratableInstance(fiber, nextInstance);
|
||
nextInstance = getNextHydratableSibling(nextInstance);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
popToNextHostParent(fiber);
|
||
if (fiber.tag === SuspenseComponent) {
|
||
nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
|
||
}
|
||
else {
|
||
nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
|
||
}
|
||
return true;
|
||
}
|
||
function hasUnhydratedTailNodes() {
|
||
return isHydrating && nextHydratableInstance !== null;
|
||
}
|
||
function warnIfUnhydratedTailNodes(fiber) {
|
||
var nextInstance = nextHydratableInstance;
|
||
while (nextInstance) {
|
||
warnUnhydratedInstance(fiber, nextInstance);
|
||
nextInstance = getNextHydratableSibling(nextInstance);
|
||
}
|
||
}
|
||
function resetHydrationState() {
|
||
if (!supportsHydration) {
|
||
return;
|
||
}
|
||
hydrationParentFiber = null;
|
||
nextHydratableInstance = null;
|
||
isHydrating = false;
|
||
didSuspendOrErrorDEV = false;
|
||
}
|
||
function upgradeHydrationErrorsToRecoverable() {
|
||
if (hydrationErrors !== null) {
|
||
// Successfully completed a forced client render. The errors that occurred
|
||
// during the hydration attempt are now recovered. We will log them in
|
||
// commit phase, once the entire tree has finished.
|
||
queueRecoverableErrors(hydrationErrors);
|
||
hydrationErrors = null;
|
||
}
|
||
}
|
||
function getIsHydrating() {
|
||
return isHydrating;
|
||
}
|
||
function queueHydrationError(error) {
|
||
if (hydrationErrors === null) {
|
||
hydrationErrors = [error];
|
||
}
|
||
else {
|
||
hydrationErrors.push(error);
|
||
}
|
||
}
|
||
var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig;
|
||
var NoTransition = null;
|
||
function requestCurrentTransition() {
|
||
return ReactCurrentBatchConfig.transition;
|
||
}
|
||
/**
|
||
* Performs equality by iterating through keys on an object and returning false
|
||
* when any key has values which are not strictly equal between the arguments.
|
||
* Returns true when the values of all keys are strictly equal.
|
||
*/
|
||
function shallowEqual(objA, objB) {
|
||
if (objectIs(objA, objB)) {
|
||
return true;
|
||
}
|
||
if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
|
||
return false;
|
||
}
|
||
var keysA = Object.keys(objA);
|
||
var keysB = Object.keys(objB);
|
||
if (keysA.length !== keysB.length) {
|
||
return false;
|
||
} // Test for A's keys different from B.
|
||
for (var i = 0; i < keysA.length; i++) {
|
||
var currentKey = keysA[i];
|
||
if (!hasOwnProperty.call(objB, currentKey) || !objectIs(objA[currentKey], objB[currentKey])) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
function describeFiber(fiber) {
|
||
var owner = fiber._debugOwner ? fiber._debugOwner.type : null;
|
||
var source = fiber._debugSource;
|
||
switch (fiber.tag) {
|
||
case HostComponent:
|
||
return describeBuiltInComponentFrame(fiber.type);
|
||
case LazyComponent:
|
||
return describeBuiltInComponentFrame('Lazy');
|
||
case SuspenseComponent:
|
||
return describeBuiltInComponentFrame('Suspense');
|
||
case SuspenseListComponent:
|
||
return describeBuiltInComponentFrame('SuspenseList');
|
||
case FunctionComponent:
|
||
case IndeterminateComponent:
|
||
case SimpleMemoComponent:
|
||
return describeFunctionComponentFrame(fiber.type);
|
||
case ForwardRef:
|
||
return describeFunctionComponentFrame(fiber.type.render);
|
||
case ClassComponent:
|
||
return describeClassComponentFrame(fiber.type);
|
||
default:
|
||
return '';
|
||
}
|
||
}
|
||
function getStackByFiberInDevAndProd(workInProgress) {
|
||
try {
|
||
var info = '';
|
||
var node = workInProgress;
|
||
do {
|
||
info += describeFiber(node);
|
||
node = node.return;
|
||
} while (node);
|
||
return info;
|
||
}
|
||
catch (x) {
|
||
return '\nError generating stack: ' + x.message + '\n' + x.stack;
|
||
}
|
||
}
|
||
var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;
|
||
var current = null;
|
||
var isRendering = false;
|
||
function getCurrentFiberOwnerNameInDevOrNull() {
|
||
{
|
||
if (current === null) {
|
||
return null;
|
||
}
|
||
var owner = current._debugOwner;
|
||
if (owner !== null && typeof owner !== 'undefined') {
|
||
return getComponentNameFromFiber(owner);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function getCurrentFiberStackInDev() {
|
||
{
|
||
if (current === null) {
|
||
return '';
|
||
} // Safe because if current fiber exists, we are reconciling,
|
||
// and it is guaranteed to be the work-in-progress version.
|
||
return getStackByFiberInDevAndProd(current);
|
||
}
|
||
}
|
||
function resetCurrentFiber() {
|
||
{
|
||
ReactDebugCurrentFrame$1.getCurrentStack = null;
|
||
current = null;
|
||
isRendering = false;
|
||
}
|
||
}
|
||
function setCurrentFiber(fiber) {
|
||
{
|
||
ReactDebugCurrentFrame$1.getCurrentStack = fiber === null ? null : getCurrentFiberStackInDev;
|
||
current = fiber;
|
||
isRendering = false;
|
||
}
|
||
}
|
||
function getCurrentFiber() {
|
||
{
|
||
return current;
|
||
}
|
||
}
|
||
function setIsRendering(rendering) {
|
||
{
|
||
isRendering = rendering;
|
||
}
|
||
}
|
||
var ReactStrictModeWarnings = {
|
||
recordUnsafeLifecycleWarnings: function (fiber, instance) { },
|
||
flushPendingUnsafeLifecycleWarnings: function () { },
|
||
recordLegacyContextWarning: function (fiber, instance) { },
|
||
flushLegacyContextWarning: function () { },
|
||
discardPendingWarnings: function () { }
|
||
};
|
||
{
|
||
var findStrictRoot = function (fiber) {
|
||
var maybeStrictRoot = null;
|
||
var node = fiber;
|
||
while (node !== null) {
|
||
if (node.mode & StrictLegacyMode) {
|
||
maybeStrictRoot = node;
|
||
}
|
||
node = node.return;
|
||
}
|
||
return maybeStrictRoot;
|
||
};
|
||
var setToSortedString = function (set) {
|
||
var array = [];
|
||
set.forEach(function (value) {
|
||
array.push(value);
|
||
});
|
||
return array.sort().join(', ');
|
||
};
|
||
var pendingComponentWillMountWarnings = [];
|
||
var pendingUNSAFE_ComponentWillMountWarnings = [];
|
||
var pendingComponentWillReceivePropsWarnings = [];
|
||
var pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
||
var pendingComponentWillUpdateWarnings = [];
|
||
var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about.
|
||
var didWarnAboutUnsafeLifecycles = new Set();
|
||
ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (fiber, instance) {
|
||
// Dedupe strategy: Warn once per component.
|
||
if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
|
||
return;
|
||
}
|
||
if (typeof instance.componentWillMount === 'function' && // Don't warn about react-lifecycles-compat polyfilled components.
|
||
instance.componentWillMount.__suppressDeprecationWarning !== true) {
|
||
pendingComponentWillMountWarnings.push(fiber);
|
||
}
|
||
if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
|
||
}
|
||
if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
|
||
pendingComponentWillReceivePropsWarnings.push(fiber);
|
||
}
|
||
if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
||
pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
|
||
}
|
||
if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
|
||
pendingComponentWillUpdateWarnings.push(fiber);
|
||
}
|
||
if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
||
pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
|
||
}
|
||
};
|
||
ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
|
||
// We do an initial pass to gather component names
|
||
var componentWillMountUniqueNames = new Set();
|
||
if (pendingComponentWillMountWarnings.length > 0) {
|
||
pendingComponentWillMountWarnings.forEach(function (fiber) {
|
||
componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingComponentWillMountWarnings = [];
|
||
}
|
||
var UNSAFE_componentWillMountUniqueNames = new Set();
|
||
if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
|
||
pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) {
|
||
UNSAFE_componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingUNSAFE_ComponentWillMountWarnings = [];
|
||
}
|
||
var componentWillReceivePropsUniqueNames = new Set();
|
||
if (pendingComponentWillReceivePropsWarnings.length > 0) {
|
||
pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
|
||
componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingComponentWillReceivePropsWarnings = [];
|
||
}
|
||
var UNSAFE_componentWillReceivePropsUniqueNames = new Set();
|
||
if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
|
||
pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) {
|
||
UNSAFE_componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
||
}
|
||
var componentWillUpdateUniqueNames = new Set();
|
||
if (pendingComponentWillUpdateWarnings.length > 0) {
|
||
pendingComponentWillUpdateWarnings.forEach(function (fiber) {
|
||
componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingComponentWillUpdateWarnings = [];
|
||
}
|
||
var UNSAFE_componentWillUpdateUniqueNames = new Set();
|
||
if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
|
||
pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) {
|
||
UNSAFE_componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutUnsafeLifecycles.add(fiber.type);
|
||
});
|
||
pendingUNSAFE_ComponentWillUpdateWarnings = [];
|
||
} // Finally, we flush all the warnings
|
||
// UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
|
||
if (UNSAFE_componentWillMountUniqueNames.size > 0) {
|
||
var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames);
|
||
error('Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move code with side effects to componentDidMount, and set initial state in the constructor.\n' + '\nPlease update the following components: %s', sortedNames);
|
||
}
|
||
if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
|
||
var _sortedNames = setToSortedString(UNSAFE_componentWillReceivePropsUniqueNames);
|
||
error('Using UNSAFE_componentWillReceiveProps in strict mode is not recommended ' + 'and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + "* If you're updating state whenever props change, " + 'refactor your code to use memoization techniques or move it to ' + 'static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n' + '\nPlease update the following components: %s', _sortedNames);
|
||
}
|
||
if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
|
||
var _sortedNames2 = setToSortedString(UNSAFE_componentWillUpdateUniqueNames);
|
||
error('Using UNSAFE_componentWillUpdate in strict mode is not recommended ' + 'and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + '\nPlease update the following components: %s', _sortedNames2);
|
||
}
|
||
if (componentWillMountUniqueNames.size > 0) {
|
||
var _sortedNames3 = setToSortedString(componentWillMountUniqueNames);
|
||
warn('componentWillMount has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move code with side effects to componentDidMount, and set initial state in the constructor.\n' + '* Rename componentWillMount to UNSAFE_componentWillMount to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames3);
|
||
}
|
||
if (componentWillReceivePropsUniqueNames.size > 0) {
|
||
var _sortedNames4 = setToSortedString(componentWillReceivePropsUniqueNames);
|
||
warn('componentWillReceiveProps has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + "* If you're updating state whenever props change, refactor your " + 'code to use memoization techniques or move it to ' + 'static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n' + '* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames4);
|
||
}
|
||
if (componentWillUpdateUniqueNames.size > 0) {
|
||
var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames);
|
||
warn('componentWillUpdate has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + '* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames5);
|
||
}
|
||
};
|
||
var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about.
|
||
var didWarnAboutLegacyContext = new Set();
|
||
ReactStrictModeWarnings.recordLegacyContextWarning = function (fiber, instance) {
|
||
var strictRoot = findStrictRoot(fiber);
|
||
if (strictRoot === null) {
|
||
error('Expected to find a StrictMode component in a strict mode tree. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
return;
|
||
} // Dedup strategy: Warn once per component.
|
||
if (didWarnAboutLegacyContext.has(fiber.type)) {
|
||
return;
|
||
}
|
||
var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
|
||
if (fiber.type.contextTypes != null || fiber.type.childContextTypes != null || instance !== null && typeof instance.getChildContext === 'function') {
|
||
if (warningsForRoot === undefined) {
|
||
warningsForRoot = [];
|
||
pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
|
||
}
|
||
warningsForRoot.push(fiber);
|
||
}
|
||
};
|
||
ReactStrictModeWarnings.flushLegacyContextWarning = function () {
|
||
pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
|
||
if (fiberArray.length === 0) {
|
||
return;
|
||
}
|
||
var firstFiber = fiberArray[0];
|
||
var uniqueNames = new Set();
|
||
fiberArray.forEach(function (fiber) {
|
||
uniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
||
didWarnAboutLegacyContext.add(fiber.type);
|
||
});
|
||
var sortedNames = setToSortedString(uniqueNames);
|
||
try {
|
||
setCurrentFiber(firstFiber);
|
||
error('Legacy context API has been detected within a strict-mode tree.' + '\n\nThe old API will be supported in all 16.x releases, but applications ' + 'using it should migrate to the new version.' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here: https://reactjs.org/link/legacy-context', sortedNames);
|
||
}
|
||
finally {
|
||
resetCurrentFiber();
|
||
}
|
||
});
|
||
};
|
||
ReactStrictModeWarnings.discardPendingWarnings = function () {
|
||
pendingComponentWillMountWarnings = [];
|
||
pendingUNSAFE_ComponentWillMountWarnings = [];
|
||
pendingComponentWillReceivePropsWarnings = [];
|
||
pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
||
pendingComponentWillUpdateWarnings = [];
|
||
pendingUNSAFE_ComponentWillUpdateWarnings = [];
|
||
pendingLegacyContextWarning = new Map();
|
||
};
|
||
}
|
||
/*
|
||
* The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol
|
||
* and Temporal.* types. See https://github.com/facebook/react/pull/22064.
|
||
*
|
||
* The functions in this module will throw an easier-to-understand,
|
||
* easier-to-debug exception with a clear errors message message explaining the
|
||
* problem. (Instead of a confusing exception thrown inside the implementation
|
||
* of the `value` object).
|
||
*/
|
||
// $FlowFixMe only called in DEV, so void return is not possible.
|
||
function typeName(value) {
|
||
{
|
||
// toStringTag is needed for namespaced types like Temporal.Instant
|
||
var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;
|
||
var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object';
|
||
return type;
|
||
}
|
||
} // $FlowFixMe only called in DEV, so void return is not possible.
|
||
function willCoercionThrow(value) {
|
||
{
|
||
try {
|
||
testStringCoercion(value);
|
||
return false;
|
||
}
|
||
catch (e) {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
function testStringCoercion(value) {
|
||
// If you ended up here by following an exception call stack, here's what's
|
||
// happened: you supplied an object or symbol value to React (as a prop, key,
|
||
// DOM attribute, CSS property, string ref, etc.) and when React tried to
|
||
// coerce it to a string using `'' + value`, an exception was thrown.
|
||
//
|
||
// The most common types that will cause this exception are `Symbol` instances
|
||
// and Temporal objects like `Temporal.Instant`. But any object that has a
|
||
// `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this
|
||
// exception. (Library authors do this to prevent users from using built-in
|
||
// numeric operators like `+` or comparison operators like `>=` because custom
|
||
// methods are needed to perform accurate arithmetic or comparison.)
|
||
//
|
||
// To fix the problem, coerce this object or symbol value to a string before
|
||
// passing it to React. The most reliable way is usually `String(value)`.
|
||
//
|
||
// To find which value is throwing, check the browser or debugger console.
|
||
// Before this exception was thrown, there should be `console.error` output
|
||
// that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the
|
||
// problem and how that type was used: key, atrribute, input value prop, etc.
|
||
// In most cases, this console output also shows the component and its
|
||
// ancestor components where the exception happened.
|
||
//
|
||
// eslint-disable-next-line react-internal/safe-string-coercion
|
||
return '' + value;
|
||
}
|
||
function checkKeyStringCoercion(value) {
|
||
{
|
||
if (willCoercionThrow(value)) {
|
||
error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));
|
||
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
||
}
|
||
}
|
||
}
|
||
function checkPropStringCoercion(value, propName) {
|
||
{
|
||
if (willCoercionThrow(value)) {
|
||
error('The provided `%s` prop is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', propName, typeName(value));
|
||
return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
||
}
|
||
}
|
||
}
|
||
function resolveDefaultProps(Component, baseProps) {
|
||
if (Component && Component.defaultProps) {
|
||
// Resolve default props. Taken from ReactElement
|
||
var props = assign({}, baseProps);
|
||
var defaultProps = Component.defaultProps;
|
||
for (var propName in defaultProps) {
|
||
if (props[propName] === undefined) {
|
||
props[propName] = defaultProps[propName];
|
||
}
|
||
}
|
||
return props;
|
||
}
|
||
return baseProps;
|
||
}
|
||
var valueCursor = createCursor(null);
|
||
var rendererSigil;
|
||
{
|
||
// Use this to detect multiple renderers using the same context
|
||
rendererSigil = {};
|
||
}
|
||
var currentlyRenderingFiber = null;
|
||
var lastContextDependency = null;
|
||
var lastFullyObservedContext = null;
|
||
var isDisallowedContextReadInDEV = false;
|
||
function resetContextDependencies() {
|
||
// This is called right before React yields execution, to ensure `readContext`
|
||
// cannot be called outside the render phase.
|
||
currentlyRenderingFiber = null;
|
||
lastContextDependency = null;
|
||
lastFullyObservedContext = null;
|
||
{
|
||
isDisallowedContextReadInDEV = false;
|
||
}
|
||
}
|
||
function enterDisallowedContextReadInDEV() {
|
||
{
|
||
isDisallowedContextReadInDEV = true;
|
||
}
|
||
}
|
||
function exitDisallowedContextReadInDEV() {
|
||
{
|
||
isDisallowedContextReadInDEV = false;
|
||
}
|
||
}
|
||
function pushProvider(providerFiber, context, nextValue) {
|
||
if (isPrimaryRenderer) {
|
||
push(valueCursor, context._currentValue, providerFiber);
|
||
context._currentValue = nextValue;
|
||
{
|
||
if (context._currentRenderer !== undefined && context._currentRenderer !== null && context._currentRenderer !== rendererSigil) {
|
||
error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.');
|
||
}
|
||
context._currentRenderer = rendererSigil;
|
||
}
|
||
}
|
||
else {
|
||
push(valueCursor, context._currentValue2, providerFiber);
|
||
context._currentValue2 = nextValue;
|
||
{
|
||
if (context._currentRenderer2 !== undefined && context._currentRenderer2 !== null && context._currentRenderer2 !== rendererSigil) {
|
||
error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.');
|
||
}
|
||
context._currentRenderer2 = rendererSigil;
|
||
}
|
||
}
|
||
}
|
||
function popProvider(context, providerFiber) {
|
||
var currentValue = valueCursor.current;
|
||
pop(valueCursor, providerFiber);
|
||
if (isPrimaryRenderer) {
|
||
{
|
||
context._currentValue = currentValue;
|
||
}
|
||
}
|
||
else {
|
||
{
|
||
context._currentValue2 = currentValue;
|
||
}
|
||
}
|
||
}
|
||
function scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {
|
||
// Update the child lanes of all the ancestors, including the alternates.
|
||
var node = parent;
|
||
while (node !== null) {
|
||
var alternate = node.alternate;
|
||
if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
|
||
node.childLanes = mergeLanes(node.childLanes, renderLanes);
|
||
if (alternate !== null) {
|
||
alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
|
||
}
|
||
}
|
||
else if (alternate !== null && !isSubsetOfLanes(alternate.childLanes, renderLanes)) {
|
||
alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
|
||
}
|
||
if (node === propagationRoot) {
|
||
break;
|
||
}
|
||
node = node.return;
|
||
}
|
||
{
|
||
if (node !== propagationRoot) {
|
||
error('Expected to find the propagation root when scheduling context work. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
}
|
||
function propagateContextChange(workInProgress, context, renderLanes) {
|
||
{
|
||
propagateContextChange_eager(workInProgress, context, renderLanes);
|
||
}
|
||
}
|
||
function propagateContextChange_eager(workInProgress, context, renderLanes) {
|
||
var fiber = workInProgress.child;
|
||
if (fiber !== null) {
|
||
// Set the return pointer of the child to the work-in-progress fiber.
|
||
fiber.return = workInProgress;
|
||
}
|
||
while (fiber !== null) {
|
||
var nextFiber = void 0; // Visit this fiber.
|
||
var list = fiber.dependencies;
|
||
if (list !== null) {
|
||
nextFiber = fiber.child;
|
||
var dependency = list.firstContext;
|
||
while (dependency !== null) {
|
||
// Check if the context matches.
|
||
if (dependency.context === context) {
|
||
// Match! Schedule an update on this fiber.
|
||
if (fiber.tag === ClassComponent) {
|
||
// Schedule a force update on the work-in-progress.
|
||
var lane = pickArbitraryLane(renderLanes);
|
||
var update = createUpdate(NoTimestamp, lane);
|
||
update.tag = ForceUpdate; // TODO: Because we don't have a work-in-progress, this will add the
|
||
// update to the current fiber, too, which means it will persist even if
|
||
// this render is thrown away. Since it's a race condition, not sure it's
|
||
// worth fixing.
|
||
// Inlined `enqueueUpdate` to remove interleaved update check
|
||
var updateQueue = fiber.updateQueue;
|
||
if (updateQueue === null)
|
||
;
|
||
else {
|
||
var sharedQueue = updateQueue.shared;
|
||
var pending = sharedQueue.pending;
|
||
if (pending === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update;
|
||
}
|
||
else {
|
||
update.next = pending.next;
|
||
pending.next = update;
|
||
}
|
||
sharedQueue.pending = update;
|
||
}
|
||
}
|
||
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null) {
|
||
alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
|
||
}
|
||
scheduleContextWorkOnParentPath(fiber.return, renderLanes, workInProgress); // Mark the updated lanes on the list, too.
|
||
list.lanes = mergeLanes(list.lanes, renderLanes); // Since we already found a match, we can stop traversing the
|
||
// dependency list.
|
||
break;
|
||
}
|
||
dependency = dependency.next;
|
||
}
|
||
}
|
||
else if (fiber.tag === ContextProvider) {
|
||
// Don't scan deeper if this is a matching provider
|
||
nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
|
||
}
|
||
else if (fiber.tag === DehydratedFragment) {
|
||
// If a dehydrated suspense boundary is in this subtree, we don't know
|
||
// if it will have any context consumers in it. The best we can do is
|
||
// mark it as having updates.
|
||
var parentSuspense = fiber.return;
|
||
if (parentSuspense === null) {
|
||
throw new Error('We just came from a parent so we must have had a parent. This is a bug in React.');
|
||
}
|
||
parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
|
||
var _alternate = parentSuspense.alternate;
|
||
if (_alternate !== null) {
|
||
_alternate.lanes = mergeLanes(_alternate.lanes, renderLanes);
|
||
} // This is intentionally passing this fiber as the parent
|
||
// because we want to schedule this fiber as having work
|
||
// on its children. We'll use the childLanes on
|
||
// this fiber to indicate that a context has changed.
|
||
scheduleContextWorkOnParentPath(parentSuspense, renderLanes, workInProgress);
|
||
nextFiber = fiber.sibling;
|
||
}
|
||
else {
|
||
// Traverse down.
|
||
nextFiber = fiber.child;
|
||
}
|
||
if (nextFiber !== null) {
|
||
// Set the return pointer of the child to the work-in-progress fiber.
|
||
nextFiber.return = fiber;
|
||
}
|
||
else {
|
||
// No child. Traverse to next sibling.
|
||
nextFiber = fiber;
|
||
while (nextFiber !== null) {
|
||
if (nextFiber === workInProgress) {
|
||
// We're back to the root of this subtree. Exit.
|
||
nextFiber = null;
|
||
break;
|
||
}
|
||
var sibling = nextFiber.sibling;
|
||
if (sibling !== null) {
|
||
// Set the return pointer of the sibling to the work-in-progress fiber.
|
||
sibling.return = nextFiber.return;
|
||
nextFiber = sibling;
|
||
break;
|
||
} // No more siblings. Traverse up.
|
||
nextFiber = nextFiber.return;
|
||
}
|
||
}
|
||
fiber = nextFiber;
|
||
}
|
||
}
|
||
function prepareToReadContext(workInProgress, renderLanes) {
|
||
currentlyRenderingFiber = workInProgress;
|
||
lastContextDependency = null;
|
||
lastFullyObservedContext = null;
|
||
var dependencies = workInProgress.dependencies;
|
||
if (dependencies !== null) {
|
||
{
|
||
var firstContext = dependencies.firstContext;
|
||
if (firstContext !== null) {
|
||
if (includesSomeLane(dependencies.lanes, renderLanes)) {
|
||
// Context list has a pending update. Mark that this fiber performed work.
|
||
markWorkInProgressReceivedUpdate();
|
||
} // Reset the work-in-progress list
|
||
dependencies.firstContext = null;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function readContext(context) {
|
||
{
|
||
// This warning would fire if you read context inside a Hook like useMemo.
|
||
// Unlike the class check below, it's not enforced in production for perf.
|
||
if (isDisallowedContextReadInDEV) {
|
||
error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
||
}
|
||
}
|
||
var value = isPrimaryRenderer ? context._currentValue : context._currentValue2;
|
||
if (lastFullyObservedContext === context)
|
||
;
|
||
else {
|
||
var contextItem = {
|
||
context: context,
|
||
memoizedValue: value,
|
||
next: null
|
||
};
|
||
if (lastContextDependency === null) {
|
||
if (currentlyRenderingFiber === null) {
|
||
throw new Error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
||
} // This is the first dependency for this component. Create a new list.
|
||
lastContextDependency = contextItem;
|
||
currentlyRenderingFiber.dependencies = {
|
||
lanes: NoLanes,
|
||
firstContext: contextItem
|
||
};
|
||
}
|
||
else {
|
||
// Append a new context item.
|
||
lastContextDependency = lastContextDependency.next = contextItem;
|
||
}
|
||
}
|
||
return value;
|
||
}
|
||
// render. When this render exits, either because it finishes or because it is
|
||
// interrupted, the interleaved updates will be transferred onto the main part
|
||
// of the queue.
|
||
var concurrentQueues = null;
|
||
function pushConcurrentUpdateQueue(queue) {
|
||
if (concurrentQueues === null) {
|
||
concurrentQueues = [queue];
|
||
}
|
||
else {
|
||
concurrentQueues.push(queue);
|
||
}
|
||
}
|
||
function finishQueueingConcurrentUpdates() {
|
||
// Transfer the interleaved updates onto the main queue. Each queue has a
|
||
// `pending` field and an `interleaved` field. When they are not null, they
|
||
// point to the last node in a circular linked list. We need to append the
|
||
// interleaved list to the end of the pending list by joining them into a
|
||
// single, circular list.
|
||
if (concurrentQueues !== null) {
|
||
for (var i = 0; i < concurrentQueues.length; i++) {
|
||
var queue = concurrentQueues[i];
|
||
var lastInterleavedUpdate = queue.interleaved;
|
||
if (lastInterleavedUpdate !== null) {
|
||
queue.interleaved = null;
|
||
var firstInterleavedUpdate = lastInterleavedUpdate.next;
|
||
var lastPendingUpdate = queue.pending;
|
||
if (lastPendingUpdate !== null) {
|
||
var firstPendingUpdate = lastPendingUpdate.next;
|
||
lastPendingUpdate.next = firstInterleavedUpdate;
|
||
lastInterleavedUpdate.next = firstPendingUpdate;
|
||
}
|
||
queue.pending = lastInterleavedUpdate;
|
||
}
|
||
}
|
||
concurrentQueues = null;
|
||
}
|
||
}
|
||
function enqueueConcurrentHookUpdate(fiber, queue, update, lane) {
|
||
var interleaved = queue.interleaved;
|
||
if (interleaved === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update; // At the end of the current render, this queue's interleaved updates will
|
||
// be transferred to the pending queue.
|
||
pushConcurrentUpdateQueue(queue);
|
||
}
|
||
else {
|
||
update.next = interleaved.next;
|
||
interleaved.next = update;
|
||
}
|
||
queue.interleaved = update;
|
||
return markUpdateLaneFromFiberToRoot(fiber, lane);
|
||
}
|
||
function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane) {
|
||
var interleaved = queue.interleaved;
|
||
if (interleaved === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update; // At the end of the current render, this queue's interleaved updates will
|
||
// be transferred to the pending queue.
|
||
pushConcurrentUpdateQueue(queue);
|
||
}
|
||
else {
|
||
update.next = interleaved.next;
|
||
interleaved.next = update;
|
||
}
|
||
queue.interleaved = update;
|
||
}
|
||
function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
|
||
var interleaved = queue.interleaved;
|
||
if (interleaved === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update; // At the end of the current render, this queue's interleaved updates will
|
||
// be transferred to the pending queue.
|
||
pushConcurrentUpdateQueue(queue);
|
||
}
|
||
else {
|
||
update.next = interleaved.next;
|
||
interleaved.next = update;
|
||
}
|
||
queue.interleaved = update;
|
||
return markUpdateLaneFromFiberToRoot(fiber, lane);
|
||
}
|
||
function enqueueConcurrentRenderForLane(fiber, lane) {
|
||
return markUpdateLaneFromFiberToRoot(fiber, lane);
|
||
} // Calling this function outside this module should only be done for backwards
|
||
// compatibility and should always be accompanied by a warning.
|
||
var unsafe_markUpdateLaneFromFiberToRoot = markUpdateLaneFromFiberToRoot;
|
||
function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
|
||
// Update the source fiber's lanes
|
||
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
|
||
var alternate = sourceFiber.alternate;
|
||
if (alternate !== null) {
|
||
alternate.lanes = mergeLanes(alternate.lanes, lane);
|
||
}
|
||
{
|
||
if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) {
|
||
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
|
||
}
|
||
} // Walk the parent path to the root and update the child lanes.
|
||
var node = sourceFiber;
|
||
var parent = sourceFiber.return;
|
||
while (parent !== null) {
|
||
parent.childLanes = mergeLanes(parent.childLanes, lane);
|
||
alternate = parent.alternate;
|
||
if (alternate !== null) {
|
||
alternate.childLanes = mergeLanes(alternate.childLanes, lane);
|
||
}
|
||
else {
|
||
{
|
||
if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
|
||
warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
|
||
}
|
||
}
|
||
}
|
||
node = parent;
|
||
parent = parent.return;
|
||
}
|
||
if (node.tag === HostRoot) {
|
||
var root = node.stateNode;
|
||
return root;
|
||
}
|
||
else {
|
||
return null;
|
||
}
|
||
}
|
||
var UpdateState = 0;
|
||
var ReplaceState = 1;
|
||
var ForceUpdate = 2;
|
||
var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`.
|
||
// It should only be read right after calling `processUpdateQueue`, via
|
||
// `checkHasForceUpdateAfterProcessing`.
|
||
var hasForceUpdate = false;
|
||
var didWarnUpdateInsideUpdate;
|
||
var currentlyProcessingQueue;
|
||
{
|
||
didWarnUpdateInsideUpdate = false;
|
||
currentlyProcessingQueue = null;
|
||
}
|
||
function initializeUpdateQueue(fiber) {
|
||
var queue = {
|
||
baseState: fiber.memoizedState,
|
||
firstBaseUpdate: null,
|
||
lastBaseUpdate: null,
|
||
shared: {
|
||
pending: null,
|
||
interleaved: null,
|
||
lanes: NoLanes
|
||
},
|
||
effects: null
|
||
};
|
||
fiber.updateQueue = queue;
|
||
}
|
||
function cloneUpdateQueue(current, workInProgress) {
|
||
// Clone the update queue from current. Unless it's already a clone.
|
||
var queue = workInProgress.updateQueue;
|
||
var currentQueue = current.updateQueue;
|
||
if (queue === currentQueue) {
|
||
var clone = {
|
||
baseState: currentQueue.baseState,
|
||
firstBaseUpdate: currentQueue.firstBaseUpdate,
|
||
lastBaseUpdate: currentQueue.lastBaseUpdate,
|
||
shared: currentQueue.shared,
|
||
effects: currentQueue.effects
|
||
};
|
||
workInProgress.updateQueue = clone;
|
||
}
|
||
}
|
||
function createUpdate(eventTime, lane) {
|
||
var update = {
|
||
eventTime: eventTime,
|
||
lane: lane,
|
||
tag: UpdateState,
|
||
payload: null,
|
||
callback: null,
|
||
next: null
|
||
};
|
||
return update;
|
||
}
|
||
function enqueueUpdate(fiber, update, lane) {
|
||
var updateQueue = fiber.updateQueue;
|
||
if (updateQueue === null) {
|
||
// Only occurs if the fiber has been unmounted.
|
||
return null;
|
||
}
|
||
var sharedQueue = updateQueue.shared;
|
||
{
|
||
if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
|
||
error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');
|
||
didWarnUpdateInsideUpdate = true;
|
||
}
|
||
}
|
||
if (isUnsafeClassRenderPhaseUpdate()) {
|
||
// This is an unsafe render phase update. Add directly to the update
|
||
// queue so we can process it immediately during the current render.
|
||
var pending = sharedQueue.pending;
|
||
if (pending === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update;
|
||
}
|
||
else {
|
||
update.next = pending.next;
|
||
pending.next = update;
|
||
}
|
||
sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering
|
||
// this fiber. This is for backwards compatibility in the case where you
|
||
// update a different component during render phase than the one that is
|
||
// currently renderings (a pattern that is accompanied by a warning).
|
||
return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
|
||
}
|
||
else {
|
||
return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
|
||
}
|
||
}
|
||
function entangleTransitions(root, fiber, lane) {
|
||
var updateQueue = fiber.updateQueue;
|
||
if (updateQueue === null) {
|
||
// Only occurs if the fiber has been unmounted.
|
||
return;
|
||
}
|
||
var sharedQueue = updateQueue.shared;
|
||
if (isTransitionLane(lane)) {
|
||
var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must
|
||
// have finished. We can remove them from the shared queue, which represents
|
||
// a superset of the actually pending lanes. In some cases we may entangle
|
||
// more than we need to, but that's OK. In fact it's worse if we *don't*
|
||
// entangle when we should.
|
||
queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
|
||
var newQueueLanes = mergeLanes(queueLanes, lane);
|
||
sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
|
||
// the lane finished since the last time we entangled it. So we need to
|
||
// entangle it again, just to be sure.
|
||
markRootEntangled(root, newQueueLanes);
|
||
}
|
||
}
|
||
function enqueueCapturedUpdate(workInProgress, capturedUpdate) {
|
||
// Captured updates are updates that are thrown by a child during the render
|
||
// phase. They should be discarded if the render is aborted. Therefore,
|
||
// we should only put them on the work-in-progress queue, not the current one.
|
||
var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone.
|
||
var current = workInProgress.alternate;
|
||
if (current !== null) {
|
||
var currentQueue = current.updateQueue;
|
||
if (queue === currentQueue) {
|
||
// The work-in-progress queue is the same as current. This happens when
|
||
// we bail out on a parent fiber that then captures an error thrown by
|
||
// a child. Since we want to append the update only to the work-in
|
||
// -progress queue, we need to clone the updates. We usually clone during
|
||
// processUpdateQueue, but that didn't happen in this case because we
|
||
// skipped over the parent when we bailed out.
|
||
var newFirst = null;
|
||
var newLast = null;
|
||
var firstBaseUpdate = queue.firstBaseUpdate;
|
||
if (firstBaseUpdate !== null) {
|
||
// Loop through the updates and clone them.
|
||
var update = firstBaseUpdate;
|
||
do {
|
||
var clone = {
|
||
eventTime: update.eventTime,
|
||
lane: update.lane,
|
||
tag: update.tag,
|
||
payload: update.payload,
|
||
callback: update.callback,
|
||
next: null
|
||
};
|
||
if (newLast === null) {
|
||
newFirst = newLast = clone;
|
||
}
|
||
else {
|
||
newLast.next = clone;
|
||
newLast = clone;
|
||
}
|
||
update = update.next;
|
||
} while (update !== null); // Append the captured update the end of the cloned list.
|
||
if (newLast === null) {
|
||
newFirst = newLast = capturedUpdate;
|
||
}
|
||
else {
|
||
newLast.next = capturedUpdate;
|
||
newLast = capturedUpdate;
|
||
}
|
||
}
|
||
else {
|
||
// There are no base updates.
|
||
newFirst = newLast = capturedUpdate;
|
||
}
|
||
queue = {
|
||
baseState: currentQueue.baseState,
|
||
firstBaseUpdate: newFirst,
|
||
lastBaseUpdate: newLast,
|
||
shared: currentQueue.shared,
|
||
effects: currentQueue.effects
|
||
};
|
||
workInProgress.updateQueue = queue;
|
||
return;
|
||
}
|
||
} // Append the update to the end of the list.
|
||
var lastBaseUpdate = queue.lastBaseUpdate;
|
||
if (lastBaseUpdate === null) {
|
||
queue.firstBaseUpdate = capturedUpdate;
|
||
}
|
||
else {
|
||
lastBaseUpdate.next = capturedUpdate;
|
||
}
|
||
queue.lastBaseUpdate = capturedUpdate;
|
||
}
|
||
function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
|
||
switch (update.tag) {
|
||
case ReplaceState:
|
||
{
|
||
var payload = update.payload;
|
||
if (typeof payload === 'function') {
|
||
// Updater function
|
||
{
|
||
enterDisallowedContextReadInDEV();
|
||
}
|
||
var nextState = payload.call(instance, prevState, nextProps);
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
payload.call(instance, prevState, nextProps);
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
exitDisallowedContextReadInDEV();
|
||
}
|
||
return nextState;
|
||
} // State object
|
||
return payload;
|
||
}
|
||
case CaptureUpdate:
|
||
{
|
||
workInProgress.flags = workInProgress.flags & ~ShouldCapture | DidCapture;
|
||
}
|
||
// Intentional fallthrough
|
||
case UpdateState:
|
||
{
|
||
var _payload = update.payload;
|
||
var partialState;
|
||
if (typeof _payload === 'function') {
|
||
// Updater function
|
||
{
|
||
enterDisallowedContextReadInDEV();
|
||
}
|
||
partialState = _payload.call(instance, prevState, nextProps);
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
_payload.call(instance, prevState, nextProps);
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
exitDisallowedContextReadInDEV();
|
||
}
|
||
}
|
||
else {
|
||
// Partial state object
|
||
partialState = _payload;
|
||
}
|
||
if (partialState === null || partialState === undefined) {
|
||
// Null and undefined are treated as no-ops.
|
||
return prevState;
|
||
} // Merge the partial state and the previous state.
|
||
return assign({}, prevState, partialState);
|
||
}
|
||
case ForceUpdate:
|
||
{
|
||
hasForceUpdate = true;
|
||
return prevState;
|
||
}
|
||
}
|
||
return prevState;
|
||
}
|
||
function processUpdateQueue(workInProgress, props, instance, renderLanes) {
|
||
// This is always non-null on a ClassComponent or HostRoot
|
||
var queue = workInProgress.updateQueue;
|
||
hasForceUpdate = false;
|
||
{
|
||
currentlyProcessingQueue = queue.shared;
|
||
}
|
||
var firstBaseUpdate = queue.firstBaseUpdate;
|
||
var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue.
|
||
var pendingQueue = queue.shared.pending;
|
||
if (pendingQueue !== null) {
|
||
queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first
|
||
// and last so that it's non-circular.
|
||
var lastPendingUpdate = pendingQueue;
|
||
var firstPendingUpdate = lastPendingUpdate.next;
|
||
lastPendingUpdate.next = null; // Append pending updates to base queue
|
||
if (lastBaseUpdate === null) {
|
||
firstBaseUpdate = firstPendingUpdate;
|
||
}
|
||
else {
|
||
lastBaseUpdate.next = firstPendingUpdate;
|
||
}
|
||
lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then
|
||
// we need to transfer the updates to that queue, too. Because the base
|
||
// queue is a singly-linked list with no cycles, we can append to both
|
||
// lists and take advantage of structural sharing.
|
||
// TODO: Pass `current` as argument
|
||
var current = workInProgress.alternate;
|
||
if (current !== null) {
|
||
// This is always non-null on a ClassComponent or HostRoot
|
||
var currentQueue = current.updateQueue;
|
||
var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
|
||
if (currentLastBaseUpdate !== lastBaseUpdate) {
|
||
if (currentLastBaseUpdate === null) {
|
||
currentQueue.firstBaseUpdate = firstPendingUpdate;
|
||
}
|
||
else {
|
||
currentLastBaseUpdate.next = firstPendingUpdate;
|
||
}
|
||
currentQueue.lastBaseUpdate = lastPendingUpdate;
|
||
}
|
||
}
|
||
} // These values may change as we process the queue.
|
||
if (firstBaseUpdate !== null) {
|
||
// Iterate through the list of updates to compute the result.
|
||
var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
|
||
// from the original lanes.
|
||
var newLanes = NoLanes;
|
||
var newBaseState = null;
|
||
var newFirstBaseUpdate = null;
|
||
var newLastBaseUpdate = null;
|
||
var update = firstBaseUpdate;
|
||
do {
|
||
var updateLane = update.lane;
|
||
var updateEventTime = update.eventTime;
|
||
if (!isSubsetOfLanes(renderLanes, updateLane)) {
|
||
// Priority is insufficient. Skip this update. If this is the first
|
||
// skipped update, the previous update/state is the new base
|
||
// update/state.
|
||
var clone = {
|
||
eventTime: updateEventTime,
|
||
lane: updateLane,
|
||
tag: update.tag,
|
||
payload: update.payload,
|
||
callback: update.callback,
|
||
next: null
|
||
};
|
||
if (newLastBaseUpdate === null) {
|
||
newFirstBaseUpdate = newLastBaseUpdate = clone;
|
||
newBaseState = newState;
|
||
}
|
||
else {
|
||
newLastBaseUpdate = newLastBaseUpdate.next = clone;
|
||
} // Update the remaining priority in the queue.
|
||
newLanes = mergeLanes(newLanes, updateLane);
|
||
}
|
||
else {
|
||
// This update does have sufficient priority.
|
||
if (newLastBaseUpdate !== null) {
|
||
var _clone = {
|
||
eventTime: updateEventTime,
|
||
// This update is going to be committed so we never want uncommit
|
||
// it. Using NoLane works because 0 is a subset of all bitmasks, so
|
||
// this will never be skipped by the check above.
|
||
lane: NoLane,
|
||
tag: update.tag,
|
||
payload: update.payload,
|
||
callback: update.callback,
|
||
next: null
|
||
};
|
||
newLastBaseUpdate = newLastBaseUpdate.next = _clone;
|
||
} // Process this update.
|
||
newState = getStateFromUpdate(workInProgress, queue, update, newState, props, instance);
|
||
var callback = update.callback;
|
||
if (callback !== null && // If the update was already committed, we should not queue its
|
||
// callback again.
|
||
update.lane !== NoLane) {
|
||
workInProgress.flags |= Callback;
|
||
var effects = queue.effects;
|
||
if (effects === null) {
|
||
queue.effects = [update];
|
||
}
|
||
else {
|
||
effects.push(update);
|
||
}
|
||
}
|
||
}
|
||
update = update.next;
|
||
if (update === null) {
|
||
pendingQueue = queue.shared.pending;
|
||
if (pendingQueue === null) {
|
||
break;
|
||
}
|
||
else {
|
||
// An update was scheduled from inside a reducer. Add the new
|
||
// pending updates to the end of the list and keep processing.
|
||
var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we
|
||
// unravel them when transferring them to the base queue.
|
||
var _firstPendingUpdate = _lastPendingUpdate.next;
|
||
_lastPendingUpdate.next = null;
|
||
update = _firstPendingUpdate;
|
||
queue.lastBaseUpdate = _lastPendingUpdate;
|
||
queue.shared.pending = null;
|
||
}
|
||
}
|
||
} while (true);
|
||
if (newLastBaseUpdate === null) {
|
||
newBaseState = newState;
|
||
}
|
||
queue.baseState = newBaseState;
|
||
queue.firstBaseUpdate = newFirstBaseUpdate;
|
||
queue.lastBaseUpdate = newLastBaseUpdate; // Interleaved updates are stored on a separate queue. We aren't going to
|
||
// process them during this render, but we do need to track which lanes
|
||
// are remaining.
|
||
var lastInterleaved = queue.shared.interleaved;
|
||
if (lastInterleaved !== null) {
|
||
var interleaved = lastInterleaved;
|
||
do {
|
||
newLanes = mergeLanes(newLanes, interleaved.lane);
|
||
interleaved = interleaved.next;
|
||
} while (interleaved !== lastInterleaved);
|
||
}
|
||
else if (firstBaseUpdate === null) {
|
||
// `queue.lanes` is used for entangling transitions. We can set it back to
|
||
// zero once the queue is empty.
|
||
queue.shared.lanes = NoLanes;
|
||
} // Set the remaining expiration time to be whatever is remaining in the queue.
|
||
// This should be fine because the only two other things that contribute to
|
||
// expiration time are props and context. We're already in the middle of the
|
||
// begin phase by the time we start processing the queue, so we've already
|
||
// dealt with the props. Context in components that specify
|
||
// shouldComponentUpdate is tricky; but we'll have to account for
|
||
// that regardless.
|
||
markSkippedUpdateLanes(newLanes);
|
||
workInProgress.lanes = newLanes;
|
||
workInProgress.memoizedState = newState;
|
||
}
|
||
{
|
||
currentlyProcessingQueue = null;
|
||
}
|
||
}
|
||
function callCallback(callback, context) {
|
||
if (typeof callback !== 'function') {
|
||
throw new Error('Invalid argument passed as callback. Expected a function. Instead ' + ("received: " + callback));
|
||
}
|
||
callback.call(context);
|
||
}
|
||
function resetHasForceUpdateBeforeProcessing() {
|
||
hasForceUpdate = false;
|
||
}
|
||
function checkHasForceUpdateAfterProcessing() {
|
||
return hasForceUpdate;
|
||
}
|
||
function commitUpdateQueue(finishedWork, finishedQueue, instance) {
|
||
// Commit the effects
|
||
var effects = finishedQueue.effects;
|
||
finishedQueue.effects = null;
|
||
if (effects !== null) {
|
||
for (var i = 0; i < effects.length; i++) {
|
||
var effect = effects[i];
|
||
var callback = effect.callback;
|
||
if (callback !== null) {
|
||
effect.callback = null;
|
||
callCallback(callback, instance);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var fakeInternalInstance = {}; // React.Component uses a shared frozen object by default.
|
||
// We'll use it to determine whether we need to initialize legacy refs.
|
||
var emptyRefsObject = new React.Component().refs;
|
||
var didWarnAboutStateAssignmentForComponent;
|
||
var didWarnAboutUninitializedState;
|
||
var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
|
||
var didWarnAboutLegacyLifecyclesAndDerivedState;
|
||
var didWarnAboutUndefinedDerivedState;
|
||
var warnOnUndefinedDerivedState;
|
||
var warnOnInvalidCallback;
|
||
var didWarnAboutDirectlyAssigningPropsToState;
|
||
var didWarnAboutContextTypeAndContextTypes;
|
||
var didWarnAboutInvalidateContextType;
|
||
{
|
||
didWarnAboutStateAssignmentForComponent = new Set();
|
||
didWarnAboutUninitializedState = new Set();
|
||
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
|
||
didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
|
||
didWarnAboutDirectlyAssigningPropsToState = new Set();
|
||
didWarnAboutUndefinedDerivedState = new Set();
|
||
didWarnAboutContextTypeAndContextTypes = new Set();
|
||
didWarnAboutInvalidateContextType = new Set();
|
||
var didWarnOnInvalidCallback = new Set();
|
||
warnOnInvalidCallback = function (callback, callerName) {
|
||
if (callback === null || typeof callback === 'function') {
|
||
return;
|
||
}
|
||
var key = callerName + '_' + callback;
|
||
if (!didWarnOnInvalidCallback.has(key)) {
|
||
didWarnOnInvalidCallback.add(key);
|
||
error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
|
||
}
|
||
};
|
||
warnOnUndefinedDerivedState = function (type, partialState) {
|
||
if (partialState === undefined) {
|
||
var componentName = getComponentNameFromType(type) || 'Component';
|
||
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
|
||
didWarnAboutUndefinedDerivedState.add(componentName);
|
||
error('%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + 'You have returned undefined.', componentName);
|
||
}
|
||
}
|
||
}; // This is so gross but it's at least non-critical and can be removed if
|
||
// it causes problems. This is meant to give a nicer error message for
|
||
// ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
|
||
// ...)) which otherwise throws a "_processChildContext is not a function"
|
||
// exception.
|
||
Object.defineProperty(fakeInternalInstance, '_processChildContext', {
|
||
enumerable: false,
|
||
value: function () {
|
||
throw new Error('_processChildContext is not available in React 16+. This likely ' + 'means you have multiple copies of React and are attempting to nest ' + 'a React 15 tree inside a React 16 tree using ' + "unstable_renderSubtreeIntoContainer, which isn't supported. Try " + 'to make sure you have only one copy of React (and ideally, switch ' + 'to ReactDOM.createPortal).');
|
||
}
|
||
});
|
||
Object.freeze(fakeInternalInstance);
|
||
}
|
||
function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
|
||
var prevState = workInProgress.memoizedState;
|
||
var partialState = getDerivedStateFromProps(nextProps, prevState);
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
// Invoke the function an extra time to help detect side-effects.
|
||
partialState = getDerivedStateFromProps(nextProps, prevState);
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
warnOnUndefinedDerivedState(ctor, partialState);
|
||
} // Merge the partial state and the previous state.
|
||
var memoizedState = partialState === null || partialState === undefined ? prevState : assign({}, prevState, partialState);
|
||
workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the
|
||
// base state.
|
||
if (workInProgress.lanes === NoLanes) {
|
||
// Queue is always non-null for classes
|
||
var updateQueue = workInProgress.updateQueue;
|
||
updateQueue.baseState = memoizedState;
|
||
}
|
||
}
|
||
var classComponentUpdater = {
|
||
isMounted: isMounted,
|
||
enqueueSetState: function (inst, payload, callback) {
|
||
var fiber = get(inst);
|
||
var eventTime = requestEventTime();
|
||
var lane = requestUpdateLane(fiber);
|
||
var update = createUpdate(eventTime, lane);
|
||
update.payload = payload;
|
||
if (callback !== undefined && callback !== null) {
|
||
{
|
||
warnOnInvalidCallback(callback, 'setState');
|
||
}
|
||
update.callback = callback;
|
||
}
|
||
var root = enqueueUpdate(fiber, update, lane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
entangleTransitions(root, fiber, lane);
|
||
}
|
||
{
|
||
markStateUpdateScheduled(fiber, lane);
|
||
}
|
||
},
|
||
enqueueReplaceState: function (inst, payload, callback) {
|
||
var fiber = get(inst);
|
||
var eventTime = requestEventTime();
|
||
var lane = requestUpdateLane(fiber);
|
||
var update = createUpdate(eventTime, lane);
|
||
update.tag = ReplaceState;
|
||
update.payload = payload;
|
||
if (callback !== undefined && callback !== null) {
|
||
{
|
||
warnOnInvalidCallback(callback, 'replaceState');
|
||
}
|
||
update.callback = callback;
|
||
}
|
||
var root = enqueueUpdate(fiber, update, lane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
entangleTransitions(root, fiber, lane);
|
||
}
|
||
{
|
||
markStateUpdateScheduled(fiber, lane);
|
||
}
|
||
},
|
||
enqueueForceUpdate: function (inst, callback) {
|
||
var fiber = get(inst);
|
||
var eventTime = requestEventTime();
|
||
var lane = requestUpdateLane(fiber);
|
||
var update = createUpdate(eventTime, lane);
|
||
update.tag = ForceUpdate;
|
||
if (callback !== undefined && callback !== null) {
|
||
{
|
||
warnOnInvalidCallback(callback, 'forceUpdate');
|
||
}
|
||
update.callback = callback;
|
||
}
|
||
var root = enqueueUpdate(fiber, update, lane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
entangleTransitions(root, fiber, lane);
|
||
}
|
||
{
|
||
markForceUpdateScheduled(fiber, lane);
|
||
}
|
||
}
|
||
};
|
||
function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
|
||
var instance = workInProgress.stateNode;
|
||
if (typeof instance.shouldComponentUpdate === 'function') {
|
||
var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
// Invoke the function an extra time to help detect side-effects.
|
||
shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
if (shouldUpdate === undefined) {
|
||
error('%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentNameFromType(ctor) || 'Component');
|
||
}
|
||
}
|
||
return shouldUpdate;
|
||
}
|
||
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
|
||
return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
|
||
}
|
||
return true;
|
||
}
|
||
function checkClassInstance(workInProgress, ctor, newProps) {
|
||
var instance = workInProgress.stateNode;
|
||
{
|
||
var name = getComponentNameFromType(ctor) || 'Component';
|
||
var renderPresent = instance.render;
|
||
if (!renderPresent) {
|
||
if (ctor.prototype && typeof ctor.prototype.render === 'function') {
|
||
error('%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name);
|
||
}
|
||
else {
|
||
error('%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name);
|
||
}
|
||
}
|
||
if (instance.getInitialState && !instance.getInitialState.isReactClassApproved && !instance.state) {
|
||
error('getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name);
|
||
}
|
||
if (instance.getDefaultProps && !instance.getDefaultProps.isReactClassApproved) {
|
||
error('getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name);
|
||
}
|
||
if (instance.propTypes) {
|
||
error('propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name);
|
||
}
|
||
if (instance.contextType) {
|
||
error('contextType was defined as an instance property on %s. Use a static ' + 'property to define contextType instead.', name);
|
||
}
|
||
{
|
||
if (instance.contextTypes) {
|
||
error('contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name);
|
||
}
|
||
if (ctor.contextType && ctor.contextTypes && !didWarnAboutContextTypeAndContextTypes.has(ctor)) {
|
||
didWarnAboutContextTypeAndContextTypes.add(ctor);
|
||
error('%s declares both contextTypes and contextType static properties. ' + 'The legacy contextTypes property will be ignored.', name);
|
||
}
|
||
}
|
||
if (typeof instance.componentShouldUpdate === 'function') {
|
||
error('%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name);
|
||
}
|
||
if (ctor.prototype && ctor.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') {
|
||
error('%s has a method called shouldComponentUpdate(). ' + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentNameFromType(ctor) || 'A pure component');
|
||
}
|
||
if (typeof instance.componentDidUnmount === 'function') {
|
||
error('%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name);
|
||
}
|
||
if (typeof instance.componentDidReceiveProps === 'function') {
|
||
error('%s has a method called ' + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + 'If you meant to update the state in response to changing props, ' + 'use componentWillReceiveProps(). If you meant to fetch data or ' + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name);
|
||
}
|
||
if (typeof instance.componentWillRecieveProps === 'function') {
|
||
error('%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name);
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') {
|
||
error('%s has a method called ' + 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', name);
|
||
}
|
||
var hasMutatedProps = instance.props !== newProps;
|
||
if (instance.props !== undefined && hasMutatedProps) {
|
||
error('%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name);
|
||
}
|
||
if (instance.defaultProps) {
|
||
error('Setting defaultProps as an instance property on %s is not supported and will be ignored.' + ' Instead, define defaultProps as a static property on %s.', name, name);
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function' && typeof instance.componentDidUpdate !== 'function' && !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)) {
|
||
didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
|
||
error('%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' + 'This component defines getSnapshotBeforeUpdate() only.', getComponentNameFromType(ctor));
|
||
}
|
||
if (typeof instance.getDerivedStateFromProps === 'function') {
|
||
error('%s: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name);
|
||
}
|
||
if (typeof instance.getDerivedStateFromError === 'function') {
|
||
error('%s: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name);
|
||
}
|
||
if (typeof ctor.getSnapshotBeforeUpdate === 'function') {
|
||
error('%s: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.', name);
|
||
}
|
||
var _state = instance.state;
|
||
if (_state && (typeof _state !== 'object' || isArray(_state))) {
|
||
error('%s.state: must be set to an object or null', name);
|
||
}
|
||
if (typeof instance.getChildContext === 'function' && typeof ctor.childContextTypes !== 'object') {
|
||
error('%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', name);
|
||
}
|
||
}
|
||
}
|
||
function adoptClassInstance(workInProgress, instance) {
|
||
instance.updater = classComponentUpdater;
|
||
workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates
|
||
set(instance, workInProgress);
|
||
{
|
||
instance._reactInternalInstance = fakeInternalInstance;
|
||
}
|
||
}
|
||
function constructClassInstance(workInProgress, ctor, props) {
|
||
var isLegacyContextConsumer = false;
|
||
var unmaskedContext = emptyContextObject;
|
||
var context = emptyContextObject;
|
||
var contextType = ctor.contextType;
|
||
{
|
||
if ('contextType' in ctor) {
|
||
var isValid = // Allow null for conditional declaration
|
||
contextType === null || contextType !== undefined && contextType.$$typeof === REACT_CONTEXT_TYPE && contextType._context === undefined; // Not a <Context.Consumer>
|
||
if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
|
||
didWarnAboutInvalidateContextType.add(ctor);
|
||
var addendum = '';
|
||
if (contextType === undefined) {
|
||
addendum = ' However, it is set to undefined. ' + 'This can be caused by a typo or by mixing up named and default imports. ' + 'This can also happen due to a circular dependency, so ' + 'try moving the createContext() call to a separate file.';
|
||
}
|
||
else if (typeof contextType !== 'object') {
|
||
addendum = ' However, it is set to a ' + typeof contextType + '.';
|
||
}
|
||
else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
|
||
addendum = ' Did you accidentally pass the Context.Provider instead?';
|
||
}
|
||
else if (contextType._context !== undefined) {
|
||
// <Context.Consumer>
|
||
addendum = ' Did you accidentally pass the Context.Consumer instead?';
|
||
}
|
||
else {
|
||
addendum = ' However, it is set to an object with keys {' + Object.keys(contextType).join(', ') + '}.';
|
||
}
|
||
error('%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext().%s', getComponentNameFromType(ctor) || 'Component', addendum);
|
||
}
|
||
}
|
||
}
|
||
if (typeof contextType === 'object' && contextType !== null) {
|
||
context = readContext(contextType);
|
||
}
|
||
else {
|
||
unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
||
var contextTypes = ctor.contextTypes;
|
||
isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined;
|
||
context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject;
|
||
}
|
||
var instance = new ctor(props, context); // Instantiate twice to help detect side-effects.
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
instance = new ctor(props, context); // eslint-disable-line no-new
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
}
|
||
var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
|
||
adoptClassInstance(workInProgress, instance);
|
||
{
|
||
if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
|
||
var componentName = getComponentNameFromType(ctor) || 'Component';
|
||
if (!didWarnAboutUninitializedState.has(componentName)) {
|
||
didWarnAboutUninitializedState.add(componentName);
|
||
error('`%s` uses `getDerivedStateFromProps` but its initial state is ' + '%s. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `%s`. ' + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', componentName, instance.state === null ? 'null' : 'undefined', componentName);
|
||
}
|
||
} // If new component APIs are defined, "unsafe" lifecycles won't be called.
|
||
// Warn about these lifecycles if they are present.
|
||
// Don't warn about react-lifecycles-compat polyfilled methods though.
|
||
if (typeof ctor.getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
var foundWillMountName = null;
|
||
var foundWillReceivePropsName = null;
|
||
var foundWillUpdateName = null;
|
||
if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true) {
|
||
foundWillMountName = 'componentWillMount';
|
||
}
|
||
else if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
foundWillMountName = 'UNSAFE_componentWillMount';
|
||
}
|
||
if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
|
||
foundWillReceivePropsName = 'componentWillReceiveProps';
|
||
}
|
||
else if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
||
foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
|
||
}
|
||
if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
|
||
foundWillUpdateName = 'componentWillUpdate';
|
||
}
|
||
else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
||
foundWillUpdateName = 'UNSAFE_componentWillUpdate';
|
||
}
|
||
if (foundWillMountName !== null || foundWillReceivePropsName !== null || foundWillUpdateName !== null) {
|
||
var _componentName = getComponentNameFromType(ctor) || 'Component';
|
||
var newApiName = typeof ctor.getDerivedStateFromProps === 'function' ? 'getDerivedStateFromProps()' : 'getSnapshotBeforeUpdate()';
|
||
if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) {
|
||
didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName);
|
||
error('Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' + 'The above lifecycles should be removed. Learn more about this warning here:\n' + 'https://reactjs.org/link/unsafe-component-lifecycles', _componentName, newApiName, foundWillMountName !== null ? "\n " + foundWillMountName : '', foundWillReceivePropsName !== null ? "\n " + foundWillReceivePropsName : '', foundWillUpdateName !== null ? "\n " + foundWillUpdateName : '');
|
||
}
|
||
}
|
||
}
|
||
} // Cache unmasked context so we can avoid recreating masked context unless necessary.
|
||
// ReactFiberContext usually updates this cache but can't for newly-created instances.
|
||
if (isLegacyContextConsumer) {
|
||
cacheContext(workInProgress, unmaskedContext, context);
|
||
}
|
||
return instance;
|
||
}
|
||
function callComponentWillMount(workInProgress, instance) {
|
||
var oldState = instance.state;
|
||
if (typeof instance.componentWillMount === 'function') {
|
||
instance.componentWillMount();
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
instance.UNSAFE_componentWillMount();
|
||
}
|
||
if (oldState !== instance.state) {
|
||
{
|
||
error('%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentNameFromFiber(workInProgress) || 'Component');
|
||
}
|
||
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
||
}
|
||
}
|
||
function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
|
||
var oldState = instance.state;
|
||
if (typeof instance.componentWillReceiveProps === 'function') {
|
||
instance.componentWillReceiveProps(newProps, nextContext);
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
||
instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
|
||
}
|
||
if (instance.state !== oldState) {
|
||
{
|
||
var componentName = getComponentNameFromFiber(workInProgress) || 'Component';
|
||
if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
|
||
didWarnAboutStateAssignmentForComponent.add(componentName);
|
||
error('%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName);
|
||
}
|
||
}
|
||
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
||
}
|
||
} // Invokes the mount life-cycles on a previously never rendered instance.
|
||
function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
|
||
{
|
||
checkClassInstance(workInProgress, ctor, newProps);
|
||
}
|
||
var instance = workInProgress.stateNode;
|
||
instance.props = newProps;
|
||
instance.state = workInProgress.memoizedState;
|
||
instance.refs = emptyRefsObject;
|
||
initializeUpdateQueue(workInProgress);
|
||
var contextType = ctor.contextType;
|
||
if (typeof contextType === 'object' && contextType !== null) {
|
||
instance.context = readContext(contextType);
|
||
}
|
||
else {
|
||
var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
||
instance.context = getMaskedContext(workInProgress, unmaskedContext);
|
||
}
|
||
{
|
||
if (instance.state === newProps) {
|
||
var componentName = getComponentNameFromType(ctor) || 'Component';
|
||
if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
|
||
didWarnAboutDirectlyAssigningPropsToState.add(componentName);
|
||
error('%s: It is not recommended to assign props directly to state ' + "because updates to props won't be reflected in state. " + 'In most cases, it is better to use props directly.', componentName);
|
||
}
|
||
}
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, instance);
|
||
}
|
||
{
|
||
ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress, instance);
|
||
}
|
||
}
|
||
instance.state = workInProgress.memoizedState;
|
||
var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
||
if (typeof getDerivedStateFromProps === 'function') {
|
||
applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
||
instance.state = workInProgress.memoizedState;
|
||
} // In order to support react-lifecycles-compat polyfilled components,
|
||
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
||
if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
|
||
callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's
|
||
// process them now.
|
||
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
||
instance.state = workInProgress.memoizedState;
|
||
}
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
var fiberFlags = Update;
|
||
{
|
||
fiberFlags |= LayoutStatic;
|
||
}
|
||
if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
||
fiberFlags |= MountLayoutDev;
|
||
}
|
||
workInProgress.flags |= fiberFlags;
|
||
}
|
||
}
|
||
function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) {
|
||
var instance = workInProgress.stateNode;
|
||
var oldProps = workInProgress.memoizedProps;
|
||
instance.props = oldProps;
|
||
var oldContext = instance.context;
|
||
var contextType = ctor.contextType;
|
||
var nextContext = emptyContextObject;
|
||
if (typeof contextType === 'object' && contextType !== null) {
|
||
nextContext = readContext(contextType);
|
||
}
|
||
else {
|
||
var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
||
nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
|
||
}
|
||
var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
||
var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what
|
||
// ever the previously attempted to render - not the "current". However,
|
||
// during componentDidUpdate we pass the "current" props.
|
||
// In order to support react-lifecycles-compat polyfilled components,
|
||
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
||
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
|
||
if (oldProps !== newProps || oldContext !== nextContext) {
|
||
callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
|
||
}
|
||
}
|
||
resetHasForceUpdateBeforeProcessing();
|
||
var oldState = workInProgress.memoizedState;
|
||
var newState = instance.state = oldState;
|
||
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
||
newState = workInProgress.memoizedState;
|
||
if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
|
||
// If an update was already in progress, we should schedule an Update
|
||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
var fiberFlags = Update;
|
||
{
|
||
fiberFlags |= LayoutStatic;
|
||
}
|
||
if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
||
fiberFlags |= MountLayoutDev;
|
||
}
|
||
workInProgress.flags |= fiberFlags;
|
||
}
|
||
return false;
|
||
}
|
||
if (typeof getDerivedStateFromProps === 'function') {
|
||
applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
||
newState = workInProgress.memoizedState;
|
||
}
|
||
var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
|
||
if (shouldUpdate) {
|
||
// In order to support react-lifecycles-compat polyfilled components,
|
||
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
||
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
|
||
if (typeof instance.componentWillMount === 'function') {
|
||
instance.componentWillMount();
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
instance.UNSAFE_componentWillMount();
|
||
}
|
||
}
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
var _fiberFlags = Update;
|
||
{
|
||
_fiberFlags |= LayoutStatic;
|
||
}
|
||
if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
||
_fiberFlags |= MountLayoutDev;
|
||
}
|
||
workInProgress.flags |= _fiberFlags;
|
||
}
|
||
}
|
||
else {
|
||
// If an update was already in progress, we should schedule an Update
|
||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
var _fiberFlags2 = Update;
|
||
{
|
||
_fiberFlags2 |= LayoutStatic;
|
||
}
|
||
if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
||
_fiberFlags2 |= MountLayoutDev;
|
||
}
|
||
workInProgress.flags |= _fiberFlags2;
|
||
} // If shouldComponentUpdate returned false, we should still update the
|
||
// memoized state to indicate that this work can be reused.
|
||
workInProgress.memoizedProps = newProps;
|
||
workInProgress.memoizedState = newState;
|
||
} // Update the existing instance's state, props, and context pointers even
|
||
// if shouldComponentUpdate returns false.
|
||
instance.props = newProps;
|
||
instance.state = newState;
|
||
instance.context = nextContext;
|
||
return shouldUpdate;
|
||
} // Invokes the update life-cycles and returns false if it shouldn't rerender.
|
||
function updateClassInstance(current, workInProgress, ctor, newProps, renderLanes) {
|
||
var instance = workInProgress.stateNode;
|
||
cloneUpdateQueue(current, workInProgress);
|
||
var unresolvedOldProps = workInProgress.memoizedProps;
|
||
var oldProps = workInProgress.type === workInProgress.elementType ? unresolvedOldProps : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
|
||
instance.props = oldProps;
|
||
var unresolvedNewProps = workInProgress.pendingProps;
|
||
var oldContext = instance.context;
|
||
var contextType = ctor.contextType;
|
||
var nextContext = emptyContextObject;
|
||
if (typeof contextType === 'object' && contextType !== null) {
|
||
nextContext = readContext(contextType);
|
||
}
|
||
else {
|
||
var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
||
nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
|
||
}
|
||
var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
||
var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what
|
||
// ever the previously attempted to render - not the "current". However,
|
||
// during componentDidUpdate we pass the "current" props.
|
||
// In order to support react-lifecycles-compat polyfilled components,
|
||
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
||
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
|
||
if (unresolvedOldProps !== unresolvedNewProps || oldContext !== nextContext) {
|
||
callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
|
||
}
|
||
}
|
||
resetHasForceUpdateBeforeProcessing();
|
||
var oldState = workInProgress.memoizedState;
|
||
var newState = instance.state = oldState;
|
||
processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
||
newState = workInProgress.memoizedState;
|
||
if (unresolvedOldProps === unresolvedNewProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing() && !(enableLazyContextPropagation)) {
|
||
// If an update was already in progress, we should schedule an Update
|
||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||
if (typeof instance.componentDidUpdate === 'function') {
|
||
if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.flags |= Update;
|
||
}
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.flags |= Snapshot;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
if (typeof getDerivedStateFromProps === 'function') {
|
||
applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
||
newState = workInProgress.memoizedState;
|
||
}
|
||
var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) || // TODO: In some cases, we'll end up checking if context has changed twice,
|
||
// both before and after `shouldComponentUpdate` has been called. Not ideal,
|
||
// but I'm loath to refactor this function. This only happens for memoized
|
||
// components so it's not that common.
|
||
enableLazyContextPropagation;
|
||
if (shouldUpdate) {
|
||
// In order to support react-lifecycles-compat polyfilled components,
|
||
// Unsafe lifecycles should not be invoked for components using the new APIs.
|
||
if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
|
||
if (typeof instance.componentWillUpdate === 'function') {
|
||
instance.componentWillUpdate(newProps, newState, nextContext);
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
||
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
|
||
}
|
||
}
|
||
if (typeof instance.componentDidUpdate === 'function') {
|
||
workInProgress.flags |= Update;
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
workInProgress.flags |= Snapshot;
|
||
}
|
||
}
|
||
else {
|
||
// If an update was already in progress, we should schedule an Update
|
||
// effect even though we're bailing out, so that cWU/cDU are called.
|
||
if (typeof instance.componentDidUpdate === 'function') {
|
||
if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.flags |= Update;
|
||
}
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.flags |= Snapshot;
|
||
}
|
||
} // If shouldComponentUpdate returned false, we should still update the
|
||
// memoized props/state to indicate that this work can be reused.
|
||
workInProgress.memoizedProps = newProps;
|
||
workInProgress.memoizedState = newState;
|
||
} // Update the existing instance's state, props, and context pointers even
|
||
// if shouldComponentUpdate returns false.
|
||
instance.props = newProps;
|
||
instance.state = newState;
|
||
instance.context = nextContext;
|
||
return shouldUpdate;
|
||
}
|
||
var didWarnAboutMaps;
|
||
var didWarnAboutGenerators;
|
||
var didWarnAboutStringRefs;
|
||
var ownerHasKeyUseWarning;
|
||
var ownerHasFunctionTypeWarning;
|
||
var warnForMissingKey = function (child, returnFiber) { };
|
||
{
|
||
didWarnAboutMaps = false;
|
||
didWarnAboutGenerators = false;
|
||
didWarnAboutStringRefs = {};
|
||
/**
|
||
* Warn if there's no key explicitly set on dynamic arrays of children or
|
||
* object keys are not valid. This allows us to keep track of children between
|
||
* updates.
|
||
*/
|
||
ownerHasKeyUseWarning = {};
|
||
ownerHasFunctionTypeWarning = {};
|
||
warnForMissingKey = function (child, returnFiber) {
|
||
if (child === null || typeof child !== 'object') {
|
||
return;
|
||
}
|
||
if (!child._store || child._store.validated || child.key != null) {
|
||
return;
|
||
}
|
||
if (typeof child._store !== 'object') {
|
||
throw new Error('React Component in warnForMissingKey should have a _store. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
child._store.validated = true;
|
||
var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
||
if (ownerHasKeyUseWarning[componentName]) {
|
||
return;
|
||
}
|
||
ownerHasKeyUseWarning[componentName] = true;
|
||
error('Each child in a list should have a unique ' + '"key" prop. See https://reactjs.org/link/warning-keys for ' + 'more information.');
|
||
};
|
||
}
|
||
function coerceRef(returnFiber, current, element) {
|
||
var mixedRef = element.ref;
|
||
if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
|
||
{
|
||
// TODO: Clean this up once we turn on the string ref warning for
|
||
// everyone, because the strict mode case will no longer be relevant
|
||
if ((returnFiber.mode & StrictLegacyMode || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs
|
||
// because these cannot be automatically converted to an arrow function
|
||
// using a codemod. Therefore, we don't have to warn about string refs again.
|
||
!(element._owner && element._self && element._owner.stateNode !== element._self)) {
|
||
var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
||
if (!didWarnAboutStringRefs[componentName]) {
|
||
{
|
||
error('A string ref, "%s", has been found within a strict mode tree. ' + 'String refs are a source of potential bugs and should be avoided. ' + 'We recommend using useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref', mixedRef);
|
||
}
|
||
didWarnAboutStringRefs[componentName] = true;
|
||
}
|
||
}
|
||
}
|
||
if (element._owner) {
|
||
var owner = element._owner;
|
||
var inst;
|
||
if (owner) {
|
||
var ownerFiber = owner;
|
||
if (ownerFiber.tag !== ClassComponent) {
|
||
throw new Error('Function components cannot have string refs. ' + 'We recommend using useRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref');
|
||
}
|
||
inst = ownerFiber.stateNode;
|
||
}
|
||
if (!inst) {
|
||
throw new Error("Missing owner for string ref " + mixedRef + ". This error is likely caused by a " + 'bug in React. Please file an issue.');
|
||
} // Assigning this to a const so Flow knows it won't change in the closure
|
||
var resolvedInst = inst;
|
||
{
|
||
checkPropStringCoercion(mixedRef, 'ref');
|
||
}
|
||
var stringRef = '' + mixedRef; // Check if previous string ref matches new string ref
|
||
if (current !== null && current.ref !== null && typeof current.ref === 'function' && current.ref._stringRef === stringRef) {
|
||
return current.ref;
|
||
}
|
||
var ref = function (value) {
|
||
var refs = resolvedInst.refs;
|
||
if (refs === emptyRefsObject) {
|
||
// This is a lazy pooled frozen object, so we need to initialize.
|
||
refs = resolvedInst.refs = {};
|
||
}
|
||
if (value === null) {
|
||
delete refs[stringRef];
|
||
}
|
||
else {
|
||
refs[stringRef] = value;
|
||
}
|
||
};
|
||
ref._stringRef = stringRef;
|
||
return ref;
|
||
}
|
||
else {
|
||
if (typeof mixedRef !== 'string') {
|
||
throw new Error('Expected ref to be a function, a string, an object returned by React.createRef(), or null.');
|
||
}
|
||
if (!element._owner) {
|
||
throw new Error("Element ref was specified as a string (" + mixedRef + ") but no owner was set. This could happen for one of" + ' the following reasons:\n' + '1. You may be adding a ref to a function component\n' + "2. You may be adding a ref to a component that was not created inside a component's render method\n" + '3. You have multiple copies of React loaded\n' + 'See https://reactjs.org/link/refs-must-have-owner for more information.');
|
||
}
|
||
}
|
||
}
|
||
return mixedRef;
|
||
}
|
||
function throwOnInvalidObjectType(returnFiber, newChild) {
|
||
var childString = Object.prototype.toString.call(newChild);
|
||
throw new Error("Objects are not valid as a React child (found: " + (childString === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : childString) + "). " + 'If you meant to render a collection of children, use an array ' + 'instead.');
|
||
}
|
||
function warnOnFunctionType(returnFiber) {
|
||
{
|
||
var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
||
if (ownerHasFunctionTypeWarning[componentName]) {
|
||
return;
|
||
}
|
||
ownerHasFunctionTypeWarning[componentName] = true;
|
||
error('Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of <Component /> from render. ' + 'Or maybe you meant to call this function rather than return it.');
|
||
}
|
||
}
|
||
function resolveLazy(lazyType) {
|
||
var payload = lazyType._payload;
|
||
var init = lazyType._init;
|
||
return init(payload);
|
||
} // This wrapper function exists because I expect to clone the code in each path
|
||
// to be able to optimize each path individually by branching early. This needs
|
||
// a compiler or we can do it manually. Helpers that don't need this branching
|
||
// live outside of this function.
|
||
function ChildReconciler(shouldTrackSideEffects) {
|
||
function deleteChild(returnFiber, childToDelete) {
|
||
if (!shouldTrackSideEffects) {
|
||
// Noop.
|
||
return;
|
||
}
|
||
var deletions = returnFiber.deletions;
|
||
if (deletions === null) {
|
||
returnFiber.deletions = [childToDelete];
|
||
returnFiber.flags |= ChildDeletion;
|
||
}
|
||
else {
|
||
deletions.push(childToDelete);
|
||
}
|
||
}
|
||
function deleteRemainingChildren(returnFiber, currentFirstChild) {
|
||
if (!shouldTrackSideEffects) {
|
||
// Noop.
|
||
return null;
|
||
} // TODO: For the shouldClone case, this could be micro-optimized a bit by
|
||
// assuming that after the first child we've already added everything.
|
||
var childToDelete = currentFirstChild;
|
||
while (childToDelete !== null) {
|
||
deleteChild(returnFiber, childToDelete);
|
||
childToDelete = childToDelete.sibling;
|
||
}
|
||
return null;
|
||
}
|
||
function mapRemainingChildren(returnFiber, currentFirstChild) {
|
||
// Add the remaining children to a temporary map so that we can find them by
|
||
// keys quickly. Implicit (null) keys get added to this set with their index
|
||
// instead.
|
||
var existingChildren = new Map();
|
||
var existingChild = currentFirstChild;
|
||
while (existingChild !== null) {
|
||
if (existingChild.key !== null) {
|
||
existingChildren.set(existingChild.key, existingChild);
|
||
}
|
||
else {
|
||
existingChildren.set(existingChild.index, existingChild);
|
||
}
|
||
existingChild = existingChild.sibling;
|
||
}
|
||
return existingChildren;
|
||
}
|
||
function useFiber(fiber, pendingProps) {
|
||
// We currently set sibling to null and index to 0 here because it is easy
|
||
// to forget to do before returning it. E.g. for the single child case.
|
||
var clone = createWorkInProgress(fiber, pendingProps);
|
||
clone.index = 0;
|
||
clone.sibling = null;
|
||
return clone;
|
||
}
|
||
function placeChild(newFiber, lastPlacedIndex, newIndex) {
|
||
newFiber.index = newIndex;
|
||
if (!shouldTrackSideEffects) {
|
||
// During hydration, the useId algorithm needs to know which fibers are
|
||
// part of a list of children (arrays, iterators).
|
||
newFiber.flags |= Forked;
|
||
return lastPlacedIndex;
|
||
}
|
||
var current = newFiber.alternate;
|
||
if (current !== null) {
|
||
var oldIndex = current.index;
|
||
if (oldIndex < lastPlacedIndex) {
|
||
// This is a move.
|
||
newFiber.flags |= Placement;
|
||
return lastPlacedIndex;
|
||
}
|
||
else {
|
||
// This item can stay in place.
|
||
return oldIndex;
|
||
}
|
||
}
|
||
else {
|
||
// This is an insertion.
|
||
newFiber.flags |= Placement;
|
||
return lastPlacedIndex;
|
||
}
|
||
}
|
||
function placeSingleChild(newFiber) {
|
||
// This is simpler for the single child case. We only need to do a
|
||
// placement for inserting new children.
|
||
if (shouldTrackSideEffects && newFiber.alternate === null) {
|
||
newFiber.flags |= Placement;
|
||
}
|
||
return newFiber;
|
||
}
|
||
function updateTextNode(returnFiber, current, textContent, lanes) {
|
||
if (current === null || current.tag !== HostText) {
|
||
// Insert
|
||
var created = createFiberFromText(textContent, returnFiber.mode, lanes);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
else {
|
||
// Update
|
||
var existing = useFiber(current, textContent);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
function updateElement(returnFiber, current, element, lanes) {
|
||
var elementType = element.type;
|
||
if (elementType === REACT_FRAGMENT_TYPE) {
|
||
return updateFragment(returnFiber, current, element.props.children, lanes, element.key);
|
||
}
|
||
if (current !== null) {
|
||
if (current.elementType === elementType || ( // Keep this check inline so it only runs on the false path:
|
||
isCompatibleFamilyForHotReloading(current, element)) || // Lazy types should reconcile their resolved type.
|
||
// We need to do this after the Hot Reloading check above,
|
||
// because hot reloading has different semantics than prod because
|
||
// it doesn't resuspend. So we can't let the call below suspend.
|
||
typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === current.type) {
|
||
// Move based on index
|
||
var existing = useFiber(current, element.props);
|
||
existing.ref = coerceRef(returnFiber, current, element);
|
||
existing.return = returnFiber;
|
||
{
|
||
existing._debugSource = element._source;
|
||
existing._debugOwner = element._owner;
|
||
}
|
||
return existing;
|
||
}
|
||
} // Insert
|
||
var created = createFiberFromElement(element, returnFiber.mode, lanes);
|
||
created.ref = coerceRef(returnFiber, current, element);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
function updatePortal(returnFiber, current, portal, lanes) {
|
||
if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) {
|
||
// Insert
|
||
var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
else {
|
||
// Update
|
||
var existing = useFiber(current, portal.children || []);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
function updateFragment(returnFiber, current, fragment, lanes, key) {
|
||
if (current === null || current.tag !== Fragment) {
|
||
// Insert
|
||
var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
else {
|
||
// Update
|
||
var existing = useFiber(current, fragment);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
function createChild(returnFiber, newChild, lanes) {
|
||
if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
||
// Text nodes don't have keys. If the previous node is implicitly keyed
|
||
// we can continue to replace it without aborting even if it is not a text
|
||
// node.
|
||
var created = createFiberFromText('' + newChild, returnFiber.mode, lanes);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
var _created = createFiberFromElement(newChild, returnFiber.mode, lanes);
|
||
_created.ref = coerceRef(returnFiber, null, newChild);
|
||
_created.return = returnFiber;
|
||
return _created;
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
var _created2 = createFiberFromPortal(newChild, returnFiber.mode, lanes);
|
||
_created2.return = returnFiber;
|
||
return _created2;
|
||
}
|
||
case REACT_LAZY_TYPE:
|
||
{
|
||
var payload = newChild._payload;
|
||
var init = newChild._init;
|
||
return createChild(returnFiber, init(payload), lanes);
|
||
}
|
||
}
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
var _created3 = createFiberFromFragment(newChild, returnFiber.mode, lanes, null);
|
||
_created3.return = returnFiber;
|
||
return _created3;
|
||
}
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
{
|
||
if (typeof newChild === 'function') {
|
||
warnOnFunctionType(returnFiber);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function updateSlot(returnFiber, oldFiber, newChild, lanes) {
|
||
// Update the fiber if the keys match, otherwise return null.
|
||
var key = oldFiber !== null ? oldFiber.key : null;
|
||
if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
||
// Text nodes don't have keys. If the previous node is implicitly keyed
|
||
// we can continue to replace it without aborting even if it is not a text
|
||
// node.
|
||
if (key !== null) {
|
||
return null;
|
||
}
|
||
return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);
|
||
}
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
if (newChild.key === key) {
|
||
return updateElement(returnFiber, oldFiber, newChild, lanes);
|
||
}
|
||
else {
|
||
return null;
|
||
}
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
if (newChild.key === key) {
|
||
return updatePortal(returnFiber, oldFiber, newChild, lanes);
|
||
}
|
||
else {
|
||
return null;
|
||
}
|
||
}
|
||
case REACT_LAZY_TYPE:
|
||
{
|
||
var payload = newChild._payload;
|
||
var init = newChild._init;
|
||
return updateSlot(returnFiber, oldFiber, init(payload), lanes);
|
||
}
|
||
}
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
if (key !== null) {
|
||
return null;
|
||
}
|
||
return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
|
||
}
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
{
|
||
if (typeof newChild === 'function') {
|
||
warnOnFunctionType(returnFiber);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
function updateFromMap(existingChildren, returnFiber, newIdx, newChild, lanes) {
|
||
if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
||
// Text nodes don't have keys, so we neither have to check the old nor
|
||
// new node for the key. If both are text nodes, they match.
|
||
var matchedFiber = existingChildren.get(newIdx) || null;
|
||
return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes);
|
||
}
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
||
return updateElement(returnFiber, _matchedFiber, newChild, lanes);
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
||
return updatePortal(returnFiber, _matchedFiber2, newChild, lanes);
|
||
}
|
||
case REACT_LAZY_TYPE:
|
||
var payload = newChild._payload;
|
||
var init = newChild._init;
|
||
return updateFromMap(existingChildren, returnFiber, newIdx, init(payload), lanes);
|
||
}
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
var _matchedFiber3 = existingChildren.get(newIdx) || null;
|
||
return updateFragment(returnFiber, _matchedFiber3, newChild, lanes, null);
|
||
}
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
{
|
||
if (typeof newChild === 'function') {
|
||
warnOnFunctionType(returnFiber);
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
/**
|
||
* Warns if there is a duplicate or missing key
|
||
*/
|
||
function warnOnInvalidKey(child, knownKeys, returnFiber) {
|
||
{
|
||
if (typeof child !== 'object' || child === null) {
|
||
return knownKeys;
|
||
}
|
||
switch (child.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
case REACT_PORTAL_TYPE:
|
||
warnForMissingKey(child, returnFiber);
|
||
var key = child.key;
|
||
if (typeof key !== 'string') {
|
||
break;
|
||
}
|
||
if (knownKeys === null) {
|
||
knownKeys = new Set();
|
||
knownKeys.add(key);
|
||
break;
|
||
}
|
||
if (!knownKeys.has(key)) {
|
||
knownKeys.add(key);
|
||
break;
|
||
}
|
||
error('Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.', key);
|
||
break;
|
||
case REACT_LAZY_TYPE:
|
||
var payload = child._payload;
|
||
var init = child._init;
|
||
warnOnInvalidKey(init(payload), knownKeys, returnFiber);
|
||
break;
|
||
}
|
||
}
|
||
return knownKeys;
|
||
}
|
||
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {
|
||
// This algorithm can't optimize by searching from both ends since we
|
||
// don't have backpointers on fibers. I'm trying to see how far we can get
|
||
// with that model. If it ends up not being worth the tradeoffs, we can
|
||
// add it later.
|
||
// Even with a two ended optimization, we'd want to optimize for the case
|
||
// where there are few changes and brute force the comparison instead of
|
||
// going for the Map. It'd like to explore hitting that path first in
|
||
// forward-only mode and only go for the Map once we notice that we need
|
||
// lots of look ahead. This doesn't handle reversal as well as two ended
|
||
// search but that's unusual. Besides, for the two ended optimization to
|
||
// work on Iterables, we'd need to copy the whole set.
|
||
// In this first iteration, we'll just live with hitting the bad case
|
||
// (adding everything to a Map) in for every insert/move.
|
||
// If you change this code, also update reconcileChildrenIterator() which
|
||
// uses the same algorithm.
|
||
{
|
||
// First, validate keys.
|
||
var knownKeys = null;
|
||
for (var i = 0; i < newChildren.length; i++) {
|
||
var child = newChildren[i];
|
||
knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
|
||
}
|
||
}
|
||
var resultingFirstChild = null;
|
||
var previousNewFiber = null;
|
||
var oldFiber = currentFirstChild;
|
||
var lastPlacedIndex = 0;
|
||
var newIdx = 0;
|
||
var nextOldFiber = null;
|
||
for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
|
||
if (oldFiber.index > newIdx) {
|
||
nextOldFiber = oldFiber;
|
||
oldFiber = null;
|
||
}
|
||
else {
|
||
nextOldFiber = oldFiber.sibling;
|
||
}
|
||
var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);
|
||
if (newFiber === null) {
|
||
// TODO: This breaks on empty slots like null children. That's
|
||
// unfortunate because it triggers the slow path all the time. We need
|
||
// a better way to communicate whether this was a miss or null,
|
||
// boolean, undefined, etc.
|
||
if (oldFiber === null) {
|
||
oldFiber = nextOldFiber;
|
||
}
|
||
break;
|
||
}
|
||
if (shouldTrackSideEffects) {
|
||
if (oldFiber && newFiber.alternate === null) {
|
||
// We matched the slot, but we didn't reuse the existing fiber, so we
|
||
// need to delete the existing child.
|
||
deleteChild(returnFiber, oldFiber);
|
||
}
|
||
}
|
||
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
// TODO: Move out of the loop. This only happens for the first run.
|
||
resultingFirstChild = newFiber;
|
||
}
|
||
else {
|
||
// TODO: Defer siblings if we're not at the right index for this slot.
|
||
// I.e. if we had null values before, then we want to defer this
|
||
// for each null value. However, we also don't want to call updateSlot
|
||
// with the previous one.
|
||
previousNewFiber.sibling = newFiber;
|
||
}
|
||
previousNewFiber = newFiber;
|
||
oldFiber = nextOldFiber;
|
||
}
|
||
if (newIdx === newChildren.length) {
|
||
// We've reached the end of the new children. We can delete the rest.
|
||
deleteRemainingChildren(returnFiber, oldFiber);
|
||
if (getIsHydrating()) {
|
||
var numberOfForks = newIdx;
|
||
pushTreeFork(returnFiber, numberOfForks);
|
||
}
|
||
return resultingFirstChild;
|
||
}
|
||
if (oldFiber === null) {
|
||
// If we don't have any more existing children we can choose a fast path
|
||
// since the rest will all be insertions.
|
||
for (; newIdx < newChildren.length; newIdx++) {
|
||
var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
|
||
if (_newFiber === null) {
|
||
continue;
|
||
}
|
||
lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
// TODO: Move out of the loop. This only happens for the first run.
|
||
resultingFirstChild = _newFiber;
|
||
}
|
||
else {
|
||
previousNewFiber.sibling = _newFiber;
|
||
}
|
||
previousNewFiber = _newFiber;
|
||
}
|
||
if (getIsHydrating()) {
|
||
var _numberOfForks = newIdx;
|
||
pushTreeFork(returnFiber, _numberOfForks);
|
||
}
|
||
return resultingFirstChild;
|
||
} // Add all children to a key map for quick lookups.
|
||
var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
|
||
for (; newIdx < newChildren.length; newIdx++) {
|
||
var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], lanes);
|
||
if (_newFiber2 !== null) {
|
||
if (shouldTrackSideEffects) {
|
||
if (_newFiber2.alternate !== null) {
|
||
// The new fiber is a work in progress, but if there exists a
|
||
// current, that means that we reused the fiber. We need to delete
|
||
// it from the child list so that we don't add it to the deletion
|
||
// list.
|
||
existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
|
||
}
|
||
}
|
||
lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
resultingFirstChild = _newFiber2;
|
||
}
|
||
else {
|
||
previousNewFiber.sibling = _newFiber2;
|
||
}
|
||
previousNewFiber = _newFiber2;
|
||
}
|
||
}
|
||
if (shouldTrackSideEffects) {
|
||
// Any existing children that weren't consumed above were deleted. We need
|
||
// to add them to the deletion list.
|
||
existingChildren.forEach(function (child) {
|
||
return deleteChild(returnFiber, child);
|
||
});
|
||
}
|
||
if (getIsHydrating()) {
|
||
var _numberOfForks2 = newIdx;
|
||
pushTreeFork(returnFiber, _numberOfForks2);
|
||
}
|
||
return resultingFirstChild;
|
||
}
|
||
function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, lanes) {
|
||
// This is the same implementation as reconcileChildrenArray(),
|
||
// but using the iterator instead.
|
||
var iteratorFn = getIteratorFn(newChildrenIterable);
|
||
if (typeof iteratorFn !== 'function') {
|
||
throw new Error('An object is not an iterable. This error is likely caused by a bug in ' + 'React. Please file an issue.');
|
||
}
|
||
{
|
||
// We don't support rendering Generators because it's a mutation.
|
||
// See https://github.com/facebook/react/issues/12995
|
||
if (typeof Symbol === 'function' && // $FlowFixMe Flow doesn't know about toStringTag
|
||
newChildrenIterable[Symbol.toStringTag] === 'Generator') {
|
||
if (!didWarnAboutGenerators) {
|
||
error('Using Generators as children is unsupported and will likely yield ' + 'unexpected results because enumerating a generator mutates it. ' + 'You may convert it to an array with `Array.from()` or the ' + '`[...spread]` operator before rendering. Keep in mind ' + 'you might need to polyfill these features for older browsers.');
|
||
}
|
||
didWarnAboutGenerators = true;
|
||
} // Warn about using Maps as children
|
||
if (newChildrenIterable.entries === iteratorFn) {
|
||
if (!didWarnAboutMaps) {
|
||
error('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.');
|
||
}
|
||
didWarnAboutMaps = true;
|
||
} // First, validate keys.
|
||
// We'll get a different iterator later for the main pass.
|
||
var _newChildren = iteratorFn.call(newChildrenIterable);
|
||
if (_newChildren) {
|
||
var knownKeys = null;
|
||
var _step = _newChildren.next();
|
||
for (; !_step.done; _step = _newChildren.next()) {
|
||
var child = _step.value;
|
||
knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
|
||
}
|
||
}
|
||
}
|
||
var newChildren = iteratorFn.call(newChildrenIterable);
|
||
if (newChildren == null) {
|
||
throw new Error('An iterable object provided no iterator.');
|
||
}
|
||
var resultingFirstChild = null;
|
||
var previousNewFiber = null;
|
||
var oldFiber = currentFirstChild;
|
||
var lastPlacedIndex = 0;
|
||
var newIdx = 0;
|
||
var nextOldFiber = null;
|
||
var step = newChildren.next();
|
||
for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {
|
||
if (oldFiber.index > newIdx) {
|
||
nextOldFiber = oldFiber;
|
||
oldFiber = null;
|
||
}
|
||
else {
|
||
nextOldFiber = oldFiber.sibling;
|
||
}
|
||
var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
|
||
if (newFiber === null) {
|
||
// TODO: This breaks on empty slots like null children. That's
|
||
// unfortunate because it triggers the slow path all the time. We need
|
||
// a better way to communicate whether this was a miss or null,
|
||
// boolean, undefined, etc.
|
||
if (oldFiber === null) {
|
||
oldFiber = nextOldFiber;
|
||
}
|
||
break;
|
||
}
|
||
if (shouldTrackSideEffects) {
|
||
if (oldFiber && newFiber.alternate === null) {
|
||
// We matched the slot, but we didn't reuse the existing fiber, so we
|
||
// need to delete the existing child.
|
||
deleteChild(returnFiber, oldFiber);
|
||
}
|
||
}
|
||
lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
// TODO: Move out of the loop. This only happens for the first run.
|
||
resultingFirstChild = newFiber;
|
||
}
|
||
else {
|
||
// TODO: Defer siblings if we're not at the right index for this slot.
|
||
// I.e. if we had null values before, then we want to defer this
|
||
// for each null value. However, we also don't want to call updateSlot
|
||
// with the previous one.
|
||
previousNewFiber.sibling = newFiber;
|
||
}
|
||
previousNewFiber = newFiber;
|
||
oldFiber = nextOldFiber;
|
||
}
|
||
if (step.done) {
|
||
// We've reached the end of the new children. We can delete the rest.
|
||
deleteRemainingChildren(returnFiber, oldFiber);
|
||
if (getIsHydrating()) {
|
||
var numberOfForks = newIdx;
|
||
pushTreeFork(returnFiber, numberOfForks);
|
||
}
|
||
return resultingFirstChild;
|
||
}
|
||
if (oldFiber === null) {
|
||
// If we don't have any more existing children we can choose a fast path
|
||
// since the rest will all be insertions.
|
||
for (; !step.done; newIdx++, step = newChildren.next()) {
|
||
var _newFiber3 = createChild(returnFiber, step.value, lanes);
|
||
if (_newFiber3 === null) {
|
||
continue;
|
||
}
|
||
lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
// TODO: Move out of the loop. This only happens for the first run.
|
||
resultingFirstChild = _newFiber3;
|
||
}
|
||
else {
|
||
previousNewFiber.sibling = _newFiber3;
|
||
}
|
||
previousNewFiber = _newFiber3;
|
||
}
|
||
if (getIsHydrating()) {
|
||
var _numberOfForks3 = newIdx;
|
||
pushTreeFork(returnFiber, _numberOfForks3);
|
||
}
|
||
return resultingFirstChild;
|
||
} // Add all children to a key map for quick lookups.
|
||
var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
|
||
for (; !step.done; newIdx++, step = newChildren.next()) {
|
||
var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, lanes);
|
||
if (_newFiber4 !== null) {
|
||
if (shouldTrackSideEffects) {
|
||
if (_newFiber4.alternate !== null) {
|
||
// The new fiber is a work in progress, but if there exists a
|
||
// current, that means that we reused the fiber. We need to delete
|
||
// it from the child list so that we don't add it to the deletion
|
||
// list.
|
||
existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);
|
||
}
|
||
}
|
||
lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
|
||
if (previousNewFiber === null) {
|
||
resultingFirstChild = _newFiber4;
|
||
}
|
||
else {
|
||
previousNewFiber.sibling = _newFiber4;
|
||
}
|
||
previousNewFiber = _newFiber4;
|
||
}
|
||
}
|
||
if (shouldTrackSideEffects) {
|
||
// Any existing children that weren't consumed above were deleted. We need
|
||
// to add them to the deletion list.
|
||
existingChildren.forEach(function (child) {
|
||
return deleteChild(returnFiber, child);
|
||
});
|
||
}
|
||
if (getIsHydrating()) {
|
||
var _numberOfForks4 = newIdx;
|
||
pushTreeFork(returnFiber, _numberOfForks4);
|
||
}
|
||
return resultingFirstChild;
|
||
}
|
||
function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, lanes) {
|
||
// There's no need to check for keys on text nodes since we don't have a
|
||
// way to define them.
|
||
if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
|
||
// We already have an existing node so let's just update it and delete
|
||
// the rest.
|
||
deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
|
||
var existing = useFiber(currentFirstChild, textContent);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
} // The existing first child is not a text node so we need to create one
|
||
// and delete the existing ones.
|
||
deleteRemainingChildren(returnFiber, currentFirstChild);
|
||
var created = createFiberFromText(textContent, returnFiber.mode, lanes);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
|
||
var key = element.key;
|
||
var child = currentFirstChild;
|
||
while (child !== null) {
|
||
// TODO: If key === null and child.key === null, then this only applies to
|
||
// the first item in the list.
|
||
if (child.key === key) {
|
||
var elementType = element.type;
|
||
if (elementType === REACT_FRAGMENT_TYPE) {
|
||
if (child.tag === Fragment) {
|
||
deleteRemainingChildren(returnFiber, child.sibling);
|
||
var existing = useFiber(child, element.props.children);
|
||
existing.return = returnFiber;
|
||
{
|
||
existing._debugSource = element._source;
|
||
existing._debugOwner = element._owner;
|
||
}
|
||
return existing;
|
||
}
|
||
}
|
||
else {
|
||
if (child.elementType === elementType || ( // Keep this check inline so it only runs on the false path:
|
||
isCompatibleFamilyForHotReloading(child, element)) || // Lazy types should reconcile their resolved type.
|
||
// We need to do this after the Hot Reloading check above,
|
||
// because hot reloading has different semantics than prod because
|
||
// it doesn't resuspend. So we can't let the call below suspend.
|
||
typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === child.type) {
|
||
deleteRemainingChildren(returnFiber, child.sibling);
|
||
var _existing = useFiber(child, element.props);
|
||
_existing.ref = coerceRef(returnFiber, child, element);
|
||
_existing.return = returnFiber;
|
||
{
|
||
_existing._debugSource = element._source;
|
||
_existing._debugOwner = element._owner;
|
||
}
|
||
return _existing;
|
||
}
|
||
} // Didn't match.
|
||
deleteRemainingChildren(returnFiber, child);
|
||
break;
|
||
}
|
||
else {
|
||
deleteChild(returnFiber, child);
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
if (element.type === REACT_FRAGMENT_TYPE) {
|
||
var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
else {
|
||
var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
|
||
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
|
||
_created4.return = returnFiber;
|
||
return _created4;
|
||
}
|
||
}
|
||
function reconcileSinglePortal(returnFiber, currentFirstChild, portal, lanes) {
|
||
var key = portal.key;
|
||
var child = currentFirstChild;
|
||
while (child !== null) {
|
||
// TODO: If key === null and child.key === null, then this only applies to
|
||
// the first item in the list.
|
||
if (child.key === key) {
|
||
if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {
|
||
deleteRemainingChildren(returnFiber, child.sibling);
|
||
var existing = useFiber(child, portal.children || []);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
else {
|
||
deleteRemainingChildren(returnFiber, child);
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
deleteChild(returnFiber, child);
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
|
||
created.return = returnFiber;
|
||
return created;
|
||
} // This API will tag the children with the side-effect of the reconciliation
|
||
// itself. They will be added to the side-effect list as we pass through the
|
||
// children and the parent.
|
||
function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
|
||
// This function is not recursive.
|
||
// If the top level item is an array, we treat it as a set of children,
|
||
// not as a fragment. Nested arrays on the other hand will be treated as
|
||
// fragment nodes. Recursion happens at the normal flow.
|
||
// Handle top level unkeyed fragments as if they were arrays.
|
||
// This leads to an ambiguity between <>{[...]}</> and <>...</>.
|
||
// We treat the ambiguous cases above the same.
|
||
var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
|
||
if (isUnkeyedTopLevelFragment) {
|
||
newChild = newChild.props.children;
|
||
} // Handle object types
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
|
||
case REACT_PORTAL_TYPE:
|
||
return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes));
|
||
case REACT_LAZY_TYPE:
|
||
var payload = newChild._payload;
|
||
var init = newChild._init; // TODO: This function is supposed to be non-recursive.
|
||
return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes);
|
||
}
|
||
if (isArray(newChild)) {
|
||
return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes);
|
||
}
|
||
if (getIteratorFn(newChild)) {
|
||
return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes);
|
||
}
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
||
return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, lanes));
|
||
}
|
||
{
|
||
if (typeof newChild === 'function') {
|
||
warnOnFunctionType(returnFiber);
|
||
}
|
||
} // Remaining cases are all treated as empty.
|
||
return deleteRemainingChildren(returnFiber, currentFirstChild);
|
||
}
|
||
return reconcileChildFibers;
|
||
}
|
||
var reconcileChildFibers = ChildReconciler(true);
|
||
var mountChildFibers = ChildReconciler(false);
|
||
function cloneChildFibers(current, workInProgress) {
|
||
if (current !== null && workInProgress.child !== current.child) {
|
||
throw new Error('Resuming work not yet implemented.');
|
||
}
|
||
if (workInProgress.child === null) {
|
||
return;
|
||
}
|
||
var currentChild = workInProgress.child;
|
||
var newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
|
||
workInProgress.child = newChild;
|
||
newChild.return = workInProgress;
|
||
while (currentChild.sibling !== null) {
|
||
currentChild = currentChild.sibling;
|
||
newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps);
|
||
newChild.return = workInProgress;
|
||
}
|
||
newChild.sibling = null;
|
||
} // Reset a workInProgress child set to prepare it for a second pass.
|
||
function resetChildFibers(workInProgress, lanes) {
|
||
var child = workInProgress.child;
|
||
while (child !== null) {
|
||
resetWorkInProgress(child, lanes);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
var NO_CONTEXT = {};
|
||
var contextStackCursor$1 = createCursor(NO_CONTEXT);
|
||
var contextFiberStackCursor = createCursor(NO_CONTEXT);
|
||
var rootInstanceStackCursor = createCursor(NO_CONTEXT);
|
||
function requiredContext(c) {
|
||
if (c === NO_CONTEXT) {
|
||
throw new Error('Expected host context to exist. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
||
}
|
||
return c;
|
||
}
|
||
function getRootHostContainer() {
|
||
var rootInstance = requiredContext(rootInstanceStackCursor.current);
|
||
return rootInstance;
|
||
}
|
||
function pushHostContainer(fiber, nextRootInstance) {
|
||
// Push current root instance onto the stack;
|
||
// This allows us to reset root when portals are popped.
|
||
push(rootInstanceStackCursor, nextRootInstance, fiber); // Track the context and the Fiber that provided it.
|
||
// This enables us to pop only Fibers that provide unique contexts.
|
||
push(contextFiberStackCursor, fiber, fiber); // Finally, we need to push the host context to the stack.
|
||
// However, we can't just call getRootHostContext() and push it because
|
||
// we'd have a different number of entries on the stack depending on
|
||
// whether getRootHostContext() throws somewhere in renderer code or not.
|
||
// So we push an empty value first. This lets us safely unwind on errors.
|
||
push(contextStackCursor$1, NO_CONTEXT, fiber);
|
||
var nextRootContext = getRootHostContext(nextRootInstance); // Now that we know this function doesn't throw, replace it.
|
||
pop(contextStackCursor$1, fiber);
|
||
push(contextStackCursor$1, nextRootContext, fiber);
|
||
}
|
||
function popHostContainer(fiber) {
|
||
pop(contextStackCursor$1, fiber);
|
||
pop(contextFiberStackCursor, fiber);
|
||
pop(rootInstanceStackCursor, fiber);
|
||
}
|
||
function getHostContext() {
|
||
var context = requiredContext(contextStackCursor$1.current);
|
||
return context;
|
||
}
|
||
function pushHostContext(fiber) {
|
||
var rootInstance = requiredContext(rootInstanceStackCursor.current);
|
||
var context = requiredContext(contextStackCursor$1.current);
|
||
var nextContext = getChildHostContext(context, fiber.type, rootInstance); // Don't push this Fiber's context unless it's unique.
|
||
if (context === nextContext) {
|
||
return;
|
||
} // Track the context and the Fiber that provided it.
|
||
// This enables us to pop only Fibers that provide unique contexts.
|
||
push(contextFiberStackCursor, fiber, fiber);
|
||
push(contextStackCursor$1, nextContext, fiber);
|
||
}
|
||
function popHostContext(fiber) {
|
||
// Do not pop unless this Fiber provided the current context.
|
||
// pushHostContext() only pushes Fibers that provide unique contexts.
|
||
if (contextFiberStackCursor.current !== fiber) {
|
||
return;
|
||
}
|
||
pop(contextStackCursor$1, fiber);
|
||
pop(contextFiberStackCursor, fiber);
|
||
}
|
||
var DefaultSuspenseContext = 0; // The Suspense Context is split into two parts. The lower bits is
|
||
// inherited deeply down the subtree. The upper bits only affect
|
||
// this immediate suspense boundary and gets reset each new
|
||
// boundary or suspense list.
|
||
var SubtreeSuspenseContextMask = 1; // Subtree Flags:
|
||
// InvisibleParentSuspenseContext indicates that one of our parent Suspense
|
||
// boundaries is not currently showing visible main content.
|
||
// Either because it is already showing a fallback or is not mounted at all.
|
||
// We can use this to determine if it is desirable to trigger a fallback at
|
||
// the parent. If not, then we might need to trigger undesirable boundaries
|
||
// and/or suspend the commit to avoid hiding the parent content.
|
||
var InvisibleParentSuspenseContext = 1; // Shallow Flags:
|
||
// ForceSuspenseFallback can be used by SuspenseList to force newly added
|
||
// items into their fallback state during one of the render passes.
|
||
var ForceSuspenseFallback = 2;
|
||
var suspenseStackCursor = createCursor(DefaultSuspenseContext);
|
||
function hasSuspenseContext(parentContext, flag) {
|
||
return (parentContext & flag) !== 0;
|
||
}
|
||
function setDefaultShallowSuspenseContext(parentContext) {
|
||
return parentContext & SubtreeSuspenseContextMask;
|
||
}
|
||
function setShallowSuspenseContext(parentContext, shallowContext) {
|
||
return parentContext & SubtreeSuspenseContextMask | shallowContext;
|
||
}
|
||
function addSubtreeSuspenseContext(parentContext, subtreeContext) {
|
||
return parentContext | subtreeContext;
|
||
}
|
||
function pushSuspenseContext(fiber, newContext) {
|
||
push(suspenseStackCursor, newContext, fiber);
|
||
}
|
||
function popSuspenseContext(fiber) {
|
||
pop(suspenseStackCursor, fiber);
|
||
}
|
||
function shouldCaptureSuspense(workInProgress, hasInvisibleParent) {
|
||
// If it was the primary children that just suspended, capture and render the
|
||
// fallback. Otherwise, don't capture and bubble to the next boundary.
|
||
var nextState = workInProgress.memoizedState;
|
||
if (nextState !== null) {
|
||
if (nextState.dehydrated !== null) {
|
||
// A dehydrated boundary always captures.
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
var props = workInProgress.memoizedProps; // Regular boundaries always capture.
|
||
{
|
||
return true;
|
||
} // If it's a boundary we should avoid, then we prefer to bubble up to the
|
||
}
|
||
function findFirstSuspended(row) {
|
||
var node = row;
|
||
while (node !== null) {
|
||
if (node.tag === SuspenseComponent) {
|
||
var state = node.memoizedState;
|
||
if (state !== null) {
|
||
var dehydrated = state.dehydrated;
|
||
if (dehydrated === null || isSuspenseInstancePending(dehydrated) || isSuspenseInstanceFallback(dehydrated)) {
|
||
return node;
|
||
}
|
||
}
|
||
}
|
||
else if (node.tag === SuspenseListComponent && // revealOrder undefined can't be trusted because it don't
|
||
// keep track of whether it suspended or not.
|
||
node.memoizedProps.revealOrder !== undefined) {
|
||
var didSuspend = (node.flags & DidCapture) !== NoFlags;
|
||
if (didSuspend) {
|
||
return node;
|
||
}
|
||
}
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === row) {
|
||
return null;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === row) {
|
||
return null;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
return null;
|
||
}
|
||
var NoFlags$1 =
|
||
/* */
|
||
0; // Represents whether effect should fire.
|
||
var HasEffect =
|
||
/* */
|
||
1; // Represents the phase in which the effect (not the clean-up) fires.
|
||
var Insertion =
|
||
/* */
|
||
2;
|
||
var Layout =
|
||
/* */
|
||
4;
|
||
var Passive$1 =
|
||
/* */
|
||
8;
|
||
// and should be reset before starting a new render.
|
||
// This tracks which mutable sources need to be reset after a render.
|
||
var workInProgressSources = [];
|
||
function resetWorkInProgressVersions() {
|
||
for (var i = 0; i < workInProgressSources.length; i++) {
|
||
var mutableSource = workInProgressSources[i];
|
||
if (isPrimaryRenderer) {
|
||
mutableSource._workInProgressVersionPrimary = null;
|
||
}
|
||
else {
|
||
mutableSource._workInProgressVersionSecondary = null;
|
||
}
|
||
}
|
||
workInProgressSources.length = 0;
|
||
}
|
||
// This ensures that the version used for server rendering matches the one
|
||
// that is eventually read during hydration.
|
||
// If they don't match there's a potential tear and a full deopt render is required.
|
||
function registerMutableSourceForHydration(root, mutableSource) {
|
||
var getVersion = mutableSource._getVersion;
|
||
var version = getVersion(mutableSource._source); // TODO Clear this data once all pending hydration work is finished.
|
||
// Retaining it forever may interfere with GC.
|
||
if (root.mutableSourceEagerHydrationData == null) {
|
||
root.mutableSourceEagerHydrationData = [mutableSource, version];
|
||
}
|
||
else {
|
||
root.mutableSourceEagerHydrationData.push(mutableSource, version);
|
||
}
|
||
}
|
||
var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentBatchConfig$1 = ReactSharedInternals.ReactCurrentBatchConfig;
|
||
var didWarnAboutMismatchedHooksForComponent;
|
||
var didWarnUncachedGetSnapshot;
|
||
{
|
||
didWarnAboutMismatchedHooksForComponent = new Set();
|
||
}
|
||
// These are set right before calling the component.
|
||
var renderLanes = NoLanes; // The work-in-progress fiber. I've named it differently to distinguish it from
|
||
// the work-in-progress hook.
|
||
var currentlyRenderingFiber$1 = null; // Hooks are stored as a linked list on the fiber's memoizedState field. The
|
||
// current hook list is the list that belongs to the current fiber. The
|
||
// work-in-progress hook list is a new list that will be added to the
|
||
// work-in-progress fiber.
|
||
var currentHook = null;
|
||
var workInProgressHook = null; // Whether an update was scheduled at any point during the render phase. This
|
||
// does not get reset if we do another render pass; only when we're completely
|
||
// finished evaluating this component. This is an optimization so we know
|
||
// whether we need to clear render phase updates after a throw.
|
||
var didScheduleRenderPhaseUpdate = false; // Where an update was scheduled only during the current render pass. This
|
||
// gets reset after each attempt.
|
||
// TODO: Maybe there's some way to consolidate this with
|
||
// `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`.
|
||
var didScheduleRenderPhaseUpdateDuringThisPass = false; // Counts the number of useId hooks in this component.
|
||
var localIdCounter = 0; // Used for ids that are generated completely client-side (i.e. not during
|
||
// hydration). This counter is global, so client ids are not stable across
|
||
// render attempts.
|
||
var globalClientIdCounter = 0;
|
||
var RE_RENDER_LIMIT = 25; // In DEV, this is the name of the currently executing primitive hook
|
||
var currentHookNameInDev = null; // In DEV, this list ensures that hooks are called in the same order between renders.
|
||
// The list stores the order of hooks used during the initial render (mount).
|
||
// Subsequent renders (updates) reference this list.
|
||
var hookTypesDev = null;
|
||
var hookTypesUpdateIndexDev = -1; // In DEV, this tracks whether currently rendering component needs to ignore
|
||
// the dependencies for Hooks that need them (e.g. useEffect or useMemo).
|
||
// When true, such Hooks will always be "remounted". Only used during hot reload.
|
||
var ignorePreviousDependencies = false;
|
||
function mountHookTypesDev() {
|
||
{
|
||
var hookName = currentHookNameInDev;
|
||
if (hookTypesDev === null) {
|
||
hookTypesDev = [hookName];
|
||
}
|
||
else {
|
||
hookTypesDev.push(hookName);
|
||
}
|
||
}
|
||
}
|
||
function updateHookTypesDev() {
|
||
{
|
||
var hookName = currentHookNameInDev;
|
||
if (hookTypesDev !== null) {
|
||
hookTypesUpdateIndexDev++;
|
||
if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
|
||
warnOnHookMismatchInDev(hookName);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function checkDepsAreArrayDev(deps) {
|
||
{
|
||
if (deps !== undefined && deps !== null && !isArray(deps)) {
|
||
// Verify deps, but only on mount to avoid extra checks.
|
||
// It's unlikely their type would change as usually you define them inline.
|
||
error('%s received a final argument that is not an array (instead, received `%s`). When ' + 'specified, the final argument must be an array.', currentHookNameInDev, typeof deps);
|
||
}
|
||
}
|
||
}
|
||
function warnOnHookMismatchInDev(currentHookName) {
|
||
{
|
||
var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1);
|
||
if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
|
||
didWarnAboutMismatchedHooksForComponent.add(componentName);
|
||
if (hookTypesDev !== null) {
|
||
var table = '';
|
||
var secondColumnStart = 30;
|
||
for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
|
||
var oldHookName = hookTypesDev[i];
|
||
var newHookName = i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
|
||
var row = i + 1 + ". " + oldHookName; // Extra space so second column lines up
|
||
// lol @ IE not supporting String#repeat
|
||
while (row.length < secondColumnStart) {
|
||
row += ' ';
|
||
}
|
||
row += newHookName + '\n';
|
||
table += row;
|
||
}
|
||
error('React has detected a change in the order of Hooks called by %s. ' + 'This will lead to bugs and errors if not fixed. ' + 'For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' + ' Previous render Next render\n' + ' ------------------------------------------------------\n' + '%s' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function throwInvalidHookError() {
|
||
throw new Error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.');
|
||
}
|
||
function areHookInputsEqual(nextDeps, prevDeps) {
|
||
{
|
||
if (ignorePreviousDependencies) {
|
||
// Only true when this component is being hot reloaded.
|
||
return false;
|
||
}
|
||
}
|
||
if (prevDeps === null) {
|
||
{
|
||
error('%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
|
||
}
|
||
return false;
|
||
}
|
||
{
|
||
// Don't bother comparing lengths in prod because these arrays should be
|
||
// passed inline.
|
||
if (nextDeps.length !== prevDeps.length) {
|
||
error('The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, "[" + prevDeps.join(', ') + "]", "[" + nextDeps.join(', ') + "]");
|
||
}
|
||
}
|
||
for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
|
||
if (objectIs(nextDeps[i], prevDeps[i])) {
|
||
continue;
|
||
}
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
|
||
renderLanes = nextRenderLanes;
|
||
currentlyRenderingFiber$1 = workInProgress;
|
||
{
|
||
hookTypesDev = current !== null ? current._debugHookTypes : null;
|
||
hookTypesUpdateIndexDev = -1; // Used for hot reloading:
|
||
ignorePreviousDependencies = current !== null && current.type !== workInProgress.type;
|
||
}
|
||
workInProgress.memoizedState = null;
|
||
workInProgress.updateQueue = null;
|
||
workInProgress.lanes = NoLanes; // The following should have already been reset
|
||
// currentHook = null;
|
||
// workInProgressHook = null;
|
||
// didScheduleRenderPhaseUpdate = false;
|
||
// localIdCounter = 0;
|
||
// TODO Warn if no hooks are used at all during mount, then some are used during update.
|
||
// Currently we will identify the update render as a mount because memoizedState === null.
|
||
// This is tricky because it's valid for certain types of components (e.g. React.lazy)
|
||
// Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
|
||
// Non-stateful hooks (e.g. context) don't get added to memoizedState,
|
||
// so memoizedState would be null during updates and mounts.
|
||
{
|
||
if (current !== null && current.memoizedState !== null) {
|
||
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
|
||
}
|
||
else if (hookTypesDev !== null) {
|
||
// This dispatcher handles an edge case where a component is updating,
|
||
// but no stateful hooks have been used.
|
||
// We want to match the production code behavior (which will use HooksDispatcherOnMount),
|
||
// but with the extra DEV validation to ensure hooks ordering hasn't changed.
|
||
// This dispatcher does that.
|
||
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
|
||
}
|
||
else {
|
||
ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
|
||
}
|
||
}
|
||
var children = Component(props, secondArg); // Check if there was a render phase update
|
||
if (didScheduleRenderPhaseUpdateDuringThisPass) {
|
||
// Keep rendering in a loop for as long as render phase updates continue to
|
||
// be scheduled. Use a counter to prevent infinite loops.
|
||
var numberOfReRenders = 0;
|
||
do {
|
||
didScheduleRenderPhaseUpdateDuringThisPass = false;
|
||
localIdCounter = 0;
|
||
if (numberOfReRenders >= RE_RENDER_LIMIT) {
|
||
throw new Error('Too many re-renders. React limits the number of renders to prevent ' + 'an infinite loop.');
|
||
}
|
||
numberOfReRenders += 1;
|
||
{
|
||
// Even when hot reloading, allow dependencies to stabilize
|
||
// after first render to prevent infinite render phase updates.
|
||
ignorePreviousDependencies = false;
|
||
} // Start over from the beginning of the list
|
||
currentHook = null;
|
||
workInProgressHook = null;
|
||
workInProgress.updateQueue = null;
|
||
{
|
||
// Also validate hook order for cascading updates.
|
||
hookTypesUpdateIndexDev = -1;
|
||
}
|
||
ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV;
|
||
children = Component(props, secondArg);
|
||
} while (didScheduleRenderPhaseUpdateDuringThisPass);
|
||
} // We can assume the previous dispatcher is always this one, since we set it
|
||
// at the beginning of the render phase and there's no re-entrance.
|
||
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
||
{
|
||
workInProgress._debugHookTypes = hookTypesDev;
|
||
} // This check uses currentHook so that it works the same in DEV and prod bundles.
|
||
// hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
|
||
var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
|
||
renderLanes = NoLanes;
|
||
currentlyRenderingFiber$1 = null;
|
||
currentHook = null;
|
||
workInProgressHook = null;
|
||
{
|
||
currentHookNameInDev = null;
|
||
hookTypesDev = null;
|
||
hookTypesUpdateIndexDev = -1; // Confirm that a static flag was not added or removed since the last
|
||
// render. If this fires, it suggests that we incorrectly reset the static
|
||
// flags in some other part of the codebase. This has happened before, for
|
||
// example, in the SuspenseList implementation.
|
||
if (current !== null && (current.flags & StaticMask) !== (workInProgress.flags & StaticMask) && // Disable this warning in legacy mode, because legacy Suspense is weird
|
||
// and creates false positives. To make this work in legacy mode, we'd
|
||
// need to mark fibers that commit in an incomplete state, somehow. For
|
||
// now I'll disable the warning that most of the bugs that would trigger
|
||
// it are either exclusive to concurrent mode or exist in both.
|
||
(current.mode & ConcurrentMode) !== NoMode) {
|
||
error('Internal React error: Expected static flag was missing. Please ' + 'notify the React team.');
|
||
}
|
||
}
|
||
didScheduleRenderPhaseUpdate = false; // This is reset by checkDidRenderIdHook
|
||
// localIdCounter = 0;
|
||
if (didRenderTooFewHooks) {
|
||
throw new Error('Rendered fewer hooks than expected. This may be caused by an accidental ' + 'early return statement.');
|
||
}
|
||
return children;
|
||
}
|
||
function checkDidRenderIdHook() {
|
||
// This should be called immediately after every renderWithHooks call.
|
||
// Conceptually, it's part of the return value of renderWithHooks; it's only a
|
||
// separate function to avoid using an array tuple.
|
||
var didRenderIdHook = localIdCounter !== 0;
|
||
localIdCounter = 0;
|
||
return didRenderIdHook;
|
||
}
|
||
function bailoutHooks(current, workInProgress, lanes) {
|
||
workInProgress.updateQueue = current.updateQueue; // TODO: Don't need to reset the flags here, because they're reset in the
|
||
// complete phase (bubbleProperties).
|
||
if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
||
workInProgress.flags &= ~(MountPassiveDev | MountLayoutDev | Passive | Update);
|
||
}
|
||
else {
|
||
workInProgress.flags &= ~(Passive | Update);
|
||
}
|
||
current.lanes = removeLanes(current.lanes, lanes);
|
||
}
|
||
function resetHooksAfterThrow() {
|
||
// We can assume the previous dispatcher is always this one, since we set it
|
||
// at the beginning of the render phase and there's no re-entrance.
|
||
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
||
if (didScheduleRenderPhaseUpdate) {
|
||
// There were render phase updates. These are only valid for this render
|
||
// phase, which we are now aborting. Remove the updates from the queues so
|
||
// they do not persist to the next render. Do not remove updates from hooks
|
||
// that weren't processed.
|
||
//
|
||
// Only reset the updates from the queue if it has a clone. If it does
|
||
// not have a clone, that means it wasn't processed, and the updates were
|
||
// scheduled before we entered the render phase.
|
||
var hook = currentlyRenderingFiber$1.memoizedState;
|
||
while (hook !== null) {
|
||
var queue = hook.queue;
|
||
if (queue !== null) {
|
||
queue.pending = null;
|
||
}
|
||
hook = hook.next;
|
||
}
|
||
didScheduleRenderPhaseUpdate = false;
|
||
}
|
||
renderLanes = NoLanes;
|
||
currentlyRenderingFiber$1 = null;
|
||
currentHook = null;
|
||
workInProgressHook = null;
|
||
{
|
||
hookTypesDev = null;
|
||
hookTypesUpdateIndexDev = -1;
|
||
currentHookNameInDev = null;
|
||
isUpdatingOpaqueValueInRenderPhase = false;
|
||
}
|
||
didScheduleRenderPhaseUpdateDuringThisPass = false;
|
||
localIdCounter = 0;
|
||
}
|
||
function mountWorkInProgressHook() {
|
||
var hook = {
|
||
memoizedState: null,
|
||
baseState: null,
|
||
baseQueue: null,
|
||
queue: null,
|
||
next: null
|
||
};
|
||
if (workInProgressHook === null) {
|
||
// This is the first hook in the list
|
||
currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
|
||
}
|
||
else {
|
||
// Append to the end of the list
|
||
workInProgressHook = workInProgressHook.next = hook;
|
||
}
|
||
return workInProgressHook;
|
||
}
|
||
function updateWorkInProgressHook() {
|
||
// This function is used both for updates and for re-renders triggered by a
|
||
// render phase update. It assumes there is either a current hook we can
|
||
// clone, or a work-in-progress hook from a previous render pass that we can
|
||
// use as a base. When we reach the end of the base list, we must switch to
|
||
// the dispatcher used for mounts.
|
||
var nextCurrentHook;
|
||
if (currentHook === null) {
|
||
var current = currentlyRenderingFiber$1.alternate;
|
||
if (current !== null) {
|
||
nextCurrentHook = current.memoizedState;
|
||
}
|
||
else {
|
||
nextCurrentHook = null;
|
||
}
|
||
}
|
||
else {
|
||
nextCurrentHook = currentHook.next;
|
||
}
|
||
var nextWorkInProgressHook;
|
||
if (workInProgressHook === null) {
|
||
nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
|
||
}
|
||
else {
|
||
nextWorkInProgressHook = workInProgressHook.next;
|
||
}
|
||
if (nextWorkInProgressHook !== null) {
|
||
// There's already a work-in-progress. Reuse it.
|
||
workInProgressHook = nextWorkInProgressHook;
|
||
nextWorkInProgressHook = workInProgressHook.next;
|
||
currentHook = nextCurrentHook;
|
||
}
|
||
else {
|
||
// Clone from the current hook.
|
||
if (nextCurrentHook === null) {
|
||
throw new Error('Rendered more hooks than during the previous render.');
|
||
}
|
||
currentHook = nextCurrentHook;
|
||
var newHook = {
|
||
memoizedState: currentHook.memoizedState,
|
||
baseState: currentHook.baseState,
|
||
baseQueue: currentHook.baseQueue,
|
||
queue: currentHook.queue,
|
||
next: null
|
||
};
|
||
if (workInProgressHook === null) {
|
||
// This is the first hook in the list.
|
||
currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
|
||
}
|
||
else {
|
||
// Append to the end of the list.
|
||
workInProgressHook = workInProgressHook.next = newHook;
|
||
}
|
||
}
|
||
return workInProgressHook;
|
||
}
|
||
function createFunctionComponentUpdateQueue() {
|
||
return {
|
||
lastEffect: null,
|
||
stores: null
|
||
};
|
||
}
|
||
function basicStateReducer(state, action) {
|
||
// $FlowFixMe: Flow doesn't like mixed types
|
||
return typeof action === 'function' ? action(state) : action;
|
||
}
|
||
function mountReducer(reducer, initialArg, init) {
|
||
var hook = mountWorkInProgressHook();
|
||
var initialState;
|
||
if (init !== undefined) {
|
||
initialState = init(initialArg);
|
||
}
|
||
else {
|
||
initialState = initialArg;
|
||
}
|
||
hook.memoizedState = hook.baseState = initialState;
|
||
var queue = {
|
||
pending: null,
|
||
interleaved: null,
|
||
lanes: NoLanes,
|
||
dispatch: null,
|
||
lastRenderedReducer: reducer,
|
||
lastRenderedState: initialState
|
||
};
|
||
hook.queue = queue;
|
||
var dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber$1, queue);
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
function updateReducer(reducer, initialArg, init) {
|
||
var hook = updateWorkInProgressHook();
|
||
var queue = hook.queue;
|
||
if (queue === null) {
|
||
throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
|
||
}
|
||
queue.lastRenderedReducer = reducer;
|
||
var current = currentHook; // The last rebase update that is NOT part of the base state.
|
||
var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
|
||
var pendingQueue = queue.pending;
|
||
if (pendingQueue !== null) {
|
||
// We have new updates that haven't been processed yet.
|
||
// We'll add them to the base queue.
|
||
if (baseQueue !== null) {
|
||
// Merge the pending queue and the base queue.
|
||
var baseFirst = baseQueue.next;
|
||
var pendingFirst = pendingQueue.next;
|
||
baseQueue.next = pendingFirst;
|
||
pendingQueue.next = baseFirst;
|
||
}
|
||
{
|
||
if (current.baseQueue !== baseQueue) {
|
||
// Internal invariant that should never happen, but feasibly could in
|
||
// the future if we implement resuming, or some form of that.
|
||
error('Internal error: Expected work-in-progress queue to be a clone. ' + 'This is a bug in React.');
|
||
}
|
||
}
|
||
current.baseQueue = baseQueue = pendingQueue;
|
||
queue.pending = null;
|
||
}
|
||
if (baseQueue !== null) {
|
||
// We have a queue to process.
|
||
var first = baseQueue.next;
|
||
var newState = current.baseState;
|
||
var newBaseState = null;
|
||
var newBaseQueueFirst = null;
|
||
var newBaseQueueLast = null;
|
||
var update = first;
|
||
do {
|
||
var updateLane = update.lane;
|
||
if (!isSubsetOfLanes(renderLanes, updateLane)) {
|
||
// Priority is insufficient. Skip this update. If this is the first
|
||
// skipped update, the previous update/state is the new base
|
||
// update/state.
|
||
var clone = {
|
||
lane: updateLane,
|
||
action: update.action,
|
||
hasEagerState: update.hasEagerState,
|
||
eagerState: update.eagerState,
|
||
next: null
|
||
};
|
||
if (newBaseQueueLast === null) {
|
||
newBaseQueueFirst = newBaseQueueLast = clone;
|
||
newBaseState = newState;
|
||
}
|
||
else {
|
||
newBaseQueueLast = newBaseQueueLast.next = clone;
|
||
} // Update the remaining priority in the queue.
|
||
// TODO: Don't need to accumulate this. Instead, we can remove
|
||
// renderLanes from the original lanes.
|
||
currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane);
|
||
markSkippedUpdateLanes(updateLane);
|
||
}
|
||
else {
|
||
// This update does have sufficient priority.
|
||
if (newBaseQueueLast !== null) {
|
||
var _clone = {
|
||
// This update is going to be committed so we never want uncommit
|
||
// it. Using NoLane works because 0 is a subset of all bitmasks, so
|
||
// this will never be skipped by the check above.
|
||
lane: NoLane,
|
||
action: update.action,
|
||
hasEagerState: update.hasEagerState,
|
||
eagerState: update.eagerState,
|
||
next: null
|
||
};
|
||
newBaseQueueLast = newBaseQueueLast.next = _clone;
|
||
} // Process this update.
|
||
if (update.hasEagerState) {
|
||
// If this update is a state update (not a reducer) and was processed eagerly,
|
||
// we can use the eagerly computed state
|
||
newState = update.eagerState;
|
||
}
|
||
else {
|
||
var action = update.action;
|
||
newState = reducer(newState, action);
|
||
}
|
||
}
|
||
update = update.next;
|
||
} while (update !== null && update !== first);
|
||
if (newBaseQueueLast === null) {
|
||
newBaseState = newState;
|
||
}
|
||
else {
|
||
newBaseQueueLast.next = newBaseQueueFirst;
|
||
} // Mark that the fiber performed work, but only if the new state is
|
||
// different from the current state.
|
||
if (!objectIs(newState, hook.memoizedState)) {
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
hook.memoizedState = newState;
|
||
hook.baseState = newBaseState;
|
||
hook.baseQueue = newBaseQueueLast;
|
||
queue.lastRenderedState = newState;
|
||
} // Interleaved updates are stored on a separate queue. We aren't going to
|
||
// process them during this render, but we do need to track which lanes
|
||
// are remaining.
|
||
var lastInterleaved = queue.interleaved;
|
||
if (lastInterleaved !== null) {
|
||
var interleaved = lastInterleaved;
|
||
do {
|
||
var interleavedLane = interleaved.lane;
|
||
currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, interleavedLane);
|
||
markSkippedUpdateLanes(interleavedLane);
|
||
interleaved = interleaved.next;
|
||
} while (interleaved !== lastInterleaved);
|
||
}
|
||
else if (baseQueue === null) {
|
||
// `queue.lanes` is used for entangling transitions. We can set it back to
|
||
// zero once the queue is empty.
|
||
queue.lanes = NoLanes;
|
||
}
|
||
var dispatch = queue.dispatch;
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
function rerenderReducer(reducer, initialArg, init) {
|
||
var hook = updateWorkInProgressHook();
|
||
var queue = hook.queue;
|
||
if (queue === null) {
|
||
throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
|
||
}
|
||
queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous
|
||
// work-in-progress hook.
|
||
var dispatch = queue.dispatch;
|
||
var lastRenderPhaseUpdate = queue.pending;
|
||
var newState = hook.memoizedState;
|
||
if (lastRenderPhaseUpdate !== null) {
|
||
// The queue doesn't persist past this render pass.
|
||
queue.pending = null;
|
||
var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
|
||
var update = firstRenderPhaseUpdate;
|
||
do {
|
||
// Process this render phase update. We don't have to check the
|
||
// priority because it will always be the same as the current
|
||
// render's.
|
||
var action = update.action;
|
||
newState = reducer(newState, action);
|
||
update = update.next;
|
||
} while (update !== firstRenderPhaseUpdate); // Mark that the fiber performed work, but only if the new state is
|
||
// different from the current state.
|
||
if (!objectIs(newState, hook.memoizedState)) {
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
hook.memoizedState = newState; // Don't persist the state accumulated from the render phase updates to
|
||
// the base state unless the queue is empty.
|
||
// TODO: Not sure if this is the desired semantics, but it's what we
|
||
// do for gDSFP. I can't remember why.
|
||
if (hook.baseQueue === null) {
|
||
hook.baseState = newState;
|
||
}
|
||
queue.lastRenderedState = newState;
|
||
}
|
||
return [newState, dispatch];
|
||
}
|
||
function mountMutableSource(source, getSnapshot, subscribe) {
|
||
{
|
||
return undefined;
|
||
}
|
||
}
|
||
function updateMutableSource(source, getSnapshot, subscribe) {
|
||
{
|
||
return undefined;
|
||
}
|
||
}
|
||
function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
|
||
var fiber = currentlyRenderingFiber$1;
|
||
var hook = mountWorkInProgressHook();
|
||
var nextSnapshot;
|
||
var isHydrating = getIsHydrating();
|
||
if (isHydrating) {
|
||
if (getServerSnapshot === undefined) {
|
||
throw new Error('Missing getServerSnapshot, which is required for ' + 'server-rendered content. Will revert to client rendering.');
|
||
}
|
||
nextSnapshot = getServerSnapshot();
|
||
{
|
||
if (!didWarnUncachedGetSnapshot) {
|
||
if (nextSnapshot !== getServerSnapshot()) {
|
||
error('The result of getServerSnapshot should be cached to avoid an infinite loop');
|
||
didWarnUncachedGetSnapshot = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
nextSnapshot = getSnapshot();
|
||
{
|
||
if (!didWarnUncachedGetSnapshot) {
|
||
var cachedSnapshot = getSnapshot();
|
||
if (!objectIs(nextSnapshot, cachedSnapshot)) {
|
||
error('The result of getSnapshot should be cached to avoid an infinite loop');
|
||
didWarnUncachedGetSnapshot = true;
|
||
}
|
||
}
|
||
} // Unless we're rendering a blocking lane, schedule a consistency check.
|
||
// Right before committing, we will walk the tree and check if any of the
|
||
// stores were mutated.
|
||
//
|
||
// We won't do this if we're hydrating server-rendered content, because if
|
||
// the content is stale, it's already visible anyway. Instead we'll patch
|
||
// it up in a passive effect.
|
||
var root = getWorkInProgressRoot();
|
||
if (root === null) {
|
||
throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.');
|
||
}
|
||
if (!includesBlockingLane(root, renderLanes)) {
|
||
pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
|
||
}
|
||
} // Read the current snapshot from the store on every render. This breaks the
|
||
// normal rules of React, and only works because store updates are
|
||
// always synchronous.
|
||
hook.memoizedState = nextSnapshot;
|
||
var inst = {
|
||
value: nextSnapshot,
|
||
getSnapshot: getSnapshot
|
||
};
|
||
hook.queue = inst; // Schedule an effect to subscribe to the store.
|
||
mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Schedule an effect to update the mutable instance fields. We will update
|
||
// this whenever subscribe, getSnapshot, or value changes. Because there's no
|
||
// clean-up function, and we track the deps correctly, we can call pushEffect
|
||
// directly, without storing any additional state. For the same reason, we
|
||
// don't need to set a static flag, either.
|
||
// TODO: We can move this to the passive phase once we add a pre-commit
|
||
// consistency check. See the next comment.
|
||
fiber.flags |= Passive;
|
||
pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null);
|
||
return nextSnapshot;
|
||
}
|
||
function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
|
||
var fiber = currentlyRenderingFiber$1;
|
||
var hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the
|
||
// normal rules of React, and only works because store updates are
|
||
// always synchronous.
|
||
var nextSnapshot = getSnapshot();
|
||
{
|
||
if (!didWarnUncachedGetSnapshot) {
|
||
var cachedSnapshot = getSnapshot();
|
||
if (!objectIs(nextSnapshot, cachedSnapshot)) {
|
||
error('The result of getSnapshot should be cached to avoid an infinite loop');
|
||
didWarnUncachedGetSnapshot = true;
|
||
}
|
||
}
|
||
}
|
||
var prevSnapshot = hook.memoizedState;
|
||
var snapshotChanged = !objectIs(prevSnapshot, nextSnapshot);
|
||
if (snapshotChanged) {
|
||
hook.memoizedState = nextSnapshot;
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
var inst = hook.queue;
|
||
updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // 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 (inst.getSnapshot !== getSnapshot || snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
|
||
// checking whether we scheduled a subscription effect above.
|
||
workInProgressHook !== null && workInProgressHook.memoizedState.tag & HasEffect) {
|
||
fiber.flags |= Passive;
|
||
pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null); // Unless we're rendering a blocking lane, schedule a consistency check.
|
||
// Right before committing, we will walk the tree and check if any of the
|
||
// stores were mutated.
|
||
var root = getWorkInProgressRoot();
|
||
if (root === null) {
|
||
throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.');
|
||
}
|
||
if (!includesBlockingLane(root, renderLanes)) {
|
||
pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
|
||
}
|
||
}
|
||
return nextSnapshot;
|
||
}
|
||
function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
|
||
fiber.flags |= StoreConsistency;
|
||
var check = {
|
||
getSnapshot: getSnapshot,
|
||
value: renderedSnapshot
|
||
};
|
||
var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
|
||
if (componentUpdateQueue === null) {
|
||
componentUpdateQueue = createFunctionComponentUpdateQueue();
|
||
currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
|
||
componentUpdateQueue.stores = [check];
|
||
}
|
||
else {
|
||
var stores = componentUpdateQueue.stores;
|
||
if (stores === null) {
|
||
componentUpdateQueue.stores = [check];
|
||
}
|
||
else {
|
||
stores.push(check);
|
||
}
|
||
}
|
||
}
|
||
function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
|
||
// These are updated in the passive phase
|
||
inst.value = nextSnapshot;
|
||
inst.getSnapshot = getSnapshot; // Something may have been mutated in between render and commit. This could
|
||
// have been in an event that fired before the passive effects, or it could
|
||
// have been in a layout effect. In that case, we would have used the old
|
||
// snapsho and getSnapshot values to bail out. We need to check one more time.
|
||
if (checkIfSnapshotChanged(inst)) {
|
||
// Force a re-render.
|
||
forceStoreRerender(fiber);
|
||
}
|
||
}
|
||
function subscribeToStore(fiber, inst, subscribe) {
|
||
var handleStoreChange = function () {
|
||
// The store changed. Check if the snapshot changed since the last time we
|
||
// read from the store.
|
||
if (checkIfSnapshotChanged(inst)) {
|
||
// Force a re-render.
|
||
forceStoreRerender(fiber);
|
||
}
|
||
}; // Subscribe to the store and return a clean-up function.
|
||
return subscribe(handleStoreChange);
|
||
}
|
||
function checkIfSnapshotChanged(inst) {
|
||
var latestGetSnapshot = inst.getSnapshot;
|
||
var prevValue = inst.value;
|
||
try {
|
||
var nextValue = latestGetSnapshot();
|
||
return !objectIs(prevValue, nextValue);
|
||
}
|
||
catch (error) {
|
||
return true;
|
||
}
|
||
}
|
||
function forceStoreRerender(fiber) {
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
}
|
||
function mountState(initialState) {
|
||
var hook = mountWorkInProgressHook();
|
||
if (typeof initialState === 'function') {
|
||
// $FlowFixMe: Flow doesn't like mixed types
|
||
initialState = initialState();
|
||
}
|
||
hook.memoizedState = hook.baseState = initialState;
|
||
var queue = {
|
||
pending: null,
|
||
interleaved: null,
|
||
lanes: NoLanes,
|
||
dispatch: null,
|
||
lastRenderedReducer: basicStateReducer,
|
||
lastRenderedState: initialState
|
||
};
|
||
hook.queue = queue;
|
||
var dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue);
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
function updateState(initialState) {
|
||
return updateReducer(basicStateReducer);
|
||
}
|
||
function rerenderState(initialState) {
|
||
return rerenderReducer(basicStateReducer);
|
||
}
|
||
function pushEffect(tag, create, destroy, deps) {
|
||
var effect = {
|
||
tag: tag,
|
||
create: create,
|
||
destroy: destroy,
|
||
deps: deps,
|
||
// Circular
|
||
next: null
|
||
};
|
||
var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
|
||
if (componentUpdateQueue === null) {
|
||
componentUpdateQueue = createFunctionComponentUpdateQueue();
|
||
currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
|
||
componentUpdateQueue.lastEffect = effect.next = effect;
|
||
}
|
||
else {
|
||
var lastEffect = componentUpdateQueue.lastEffect;
|
||
if (lastEffect === null) {
|
||
componentUpdateQueue.lastEffect = effect.next = effect;
|
||
}
|
||
else {
|
||
var firstEffect = lastEffect.next;
|
||
lastEffect.next = effect;
|
||
effect.next = firstEffect;
|
||
componentUpdateQueue.lastEffect = effect;
|
||
}
|
||
}
|
||
return effect;
|
||
}
|
||
function mountRef(initialValue) {
|
||
var hook = mountWorkInProgressHook();
|
||
{
|
||
var _ref2 = {
|
||
current: initialValue
|
||
};
|
||
hook.memoizedState = _ref2;
|
||
return _ref2;
|
||
}
|
||
}
|
||
function updateRef(initialValue) {
|
||
var hook = updateWorkInProgressHook();
|
||
return hook.memoizedState;
|
||
}
|
||
function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
|
||
var hook = mountWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
currentlyRenderingFiber$1.flags |= fiberFlags;
|
||
hook.memoizedState = pushEffect(HasEffect | hookFlags, create, undefined, nextDeps);
|
||
}
|
||
function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
|
||
var hook = updateWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
var destroy = undefined;
|
||
if (currentHook !== null) {
|
||
var prevEffect = currentHook.memoizedState;
|
||
destroy = prevEffect.destroy;
|
||
if (nextDeps !== null) {
|
||
var prevDeps = prevEffect.deps;
|
||
if (areHookInputsEqual(nextDeps, prevDeps)) {
|
||
hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
currentlyRenderingFiber$1.flags |= fiberFlags;
|
||
hook.memoizedState = pushEffect(HasEffect | hookFlags, create, destroy, nextDeps);
|
||
}
|
||
function mountEffect(create, deps) {
|
||
if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
||
return mountEffectImpl(MountPassiveDev | Passive | PassiveStatic, Passive$1, create, deps);
|
||
}
|
||
else {
|
||
return mountEffectImpl(Passive | PassiveStatic, Passive$1, create, deps);
|
||
}
|
||
}
|
||
function updateEffect(create, deps) {
|
||
return updateEffectImpl(Passive, Passive$1, create, deps);
|
||
}
|
||
function mountInsertionEffect(create, deps) {
|
||
return mountEffectImpl(Update, Insertion, create, deps);
|
||
}
|
||
function updateInsertionEffect(create, deps) {
|
||
return updateEffectImpl(Update, Insertion, create, deps);
|
||
}
|
||
function mountLayoutEffect(create, deps) {
|
||
var fiberFlags = Update;
|
||
{
|
||
fiberFlags |= LayoutStatic;
|
||
}
|
||
if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
||
fiberFlags |= MountLayoutDev;
|
||
}
|
||
return mountEffectImpl(fiberFlags, Layout, create, deps);
|
||
}
|
||
function updateLayoutEffect(create, deps) {
|
||
return updateEffectImpl(Update, Layout, create, deps);
|
||
}
|
||
function imperativeHandleEffect(create, ref) {
|
||
if (typeof ref === 'function') {
|
||
var refCallback = ref;
|
||
var _inst = create();
|
||
refCallback(_inst);
|
||
return function () {
|
||
refCallback(null);
|
||
};
|
||
}
|
||
else if (ref !== null && ref !== undefined) {
|
||
var refObject = ref;
|
||
{
|
||
if (!refObject.hasOwnProperty('current')) {
|
||
error('Expected useImperativeHandle() first argument to either be a ' + 'ref callback or React.createRef() object. Instead received: %s.', 'an object with keys {' + Object.keys(refObject).join(', ') + '}');
|
||
}
|
||
}
|
||
var _inst2 = create();
|
||
refObject.current = _inst2;
|
||
return function () {
|
||
refObject.current = null;
|
||
};
|
||
}
|
||
}
|
||
function mountImperativeHandle(ref, create, deps) {
|
||
{
|
||
if (typeof create !== 'function') {
|
||
error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null');
|
||
}
|
||
} // TODO: If deps are provided, should we skip comparing the ref itself?
|
||
var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
||
var fiberFlags = Update;
|
||
{
|
||
fiberFlags |= LayoutStatic;
|
||
}
|
||
if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
||
fiberFlags |= MountLayoutDev;
|
||
}
|
||
return mountEffectImpl(fiberFlags, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
||
}
|
||
function updateImperativeHandle(ref, create, deps) {
|
||
{
|
||
if (typeof create !== 'function') {
|
||
error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null');
|
||
}
|
||
} // TODO: If deps are provided, should we skip comparing the ref itself?
|
||
var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
||
return updateEffectImpl(Update, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
||
}
|
||
function mountDebugValue(value, formatterFn) {
|
||
// The react-debug-hooks package injects its own implementation
|
||
// so that e.g. DevTools can display custom hook values.
|
||
}
|
||
var updateDebugValue = mountDebugValue;
|
||
function mountCallback(callback, deps) {
|
||
var hook = mountWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
hook.memoizedState = [callback, nextDeps];
|
||
return callback;
|
||
}
|
||
function updateCallback(callback, deps) {
|
||
var hook = updateWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
var prevState = hook.memoizedState;
|
||
if (prevState !== null) {
|
||
if (nextDeps !== null) {
|
||
var prevDeps = prevState[1];
|
||
if (areHookInputsEqual(nextDeps, prevDeps)) {
|
||
return prevState[0];
|
||
}
|
||
}
|
||
}
|
||
hook.memoizedState = [callback, nextDeps];
|
||
return callback;
|
||
}
|
||
function mountMemo(nextCreate, deps) {
|
||
var hook = mountWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
var nextValue = nextCreate();
|
||
hook.memoizedState = [nextValue, nextDeps];
|
||
return nextValue;
|
||
}
|
||
function updateMemo(nextCreate, deps) {
|
||
var hook = updateWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
var prevState = hook.memoizedState;
|
||
if (prevState !== null) {
|
||
// Assume these are defined. If they're not, areHookInputsEqual will warn.
|
||
if (nextDeps !== null) {
|
||
var prevDeps = prevState[1];
|
||
if (areHookInputsEqual(nextDeps, prevDeps)) {
|
||
return prevState[0];
|
||
}
|
||
}
|
||
}
|
||
var nextValue = nextCreate();
|
||
hook.memoizedState = [nextValue, nextDeps];
|
||
return nextValue;
|
||
}
|
||
function mountDeferredValue(value) {
|
||
var hook = mountWorkInProgressHook();
|
||
hook.memoizedState = value;
|
||
return value;
|
||
}
|
||
function updateDeferredValue(value) {
|
||
var hook = updateWorkInProgressHook();
|
||
var resolvedCurrentHook = currentHook;
|
||
var prevValue = resolvedCurrentHook.memoizedState;
|
||
return updateDeferredValueImpl(hook, prevValue, value);
|
||
}
|
||
function rerenderDeferredValue(value) {
|
||
var hook = updateWorkInProgressHook();
|
||
if (currentHook === null) {
|
||
// This is a rerender during a mount.
|
||
hook.memoizedState = value;
|
||
return value;
|
||
}
|
||
else {
|
||
// This is a rerender during an update.
|
||
var prevValue = currentHook.memoizedState;
|
||
return updateDeferredValueImpl(hook, prevValue, value);
|
||
}
|
||
}
|
||
function updateDeferredValueImpl(hook, prevValue, value) {
|
||
var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);
|
||
if (shouldDeferValue) {
|
||
// This is an urgent update. If the value has changed, keep using the
|
||
// previous value and spawn a deferred render to update it later.
|
||
if (!objectIs(value, prevValue)) {
|
||
// Schedule a deferred render
|
||
var deferredLane = claimNextTransitionLane();
|
||
currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, deferredLane);
|
||
markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent
|
||
// from the latest value. The name "baseState" doesn't really match how we
|
||
// use it because we're reusing a state hook field instead of creating a
|
||
// new one.
|
||
hook.baseState = true;
|
||
} // Reuse the previous value
|
||
return prevValue;
|
||
}
|
||
else {
|
||
// This is not an urgent update, so we can use the latest value regardless
|
||
// of what it is. No need to defer it.
|
||
// However, if we're currently inside a spawned render, then we need to mark
|
||
// this as an update to prevent the fiber from bailing out.
|
||
//
|
||
// `baseState` is true when the current value is different from the rendered
|
||
// value. The name doesn't really match how we use it because we're reusing
|
||
// a state hook field instead of creating a new one.
|
||
if (hook.baseState) {
|
||
// Flip this back to false.
|
||
hook.baseState = false;
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
hook.memoizedState = value;
|
||
return value;
|
||
}
|
||
}
|
||
function startTransition(setPending, callback, options) {
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority));
|
||
setPending(true);
|
||
var prevTransition = ReactCurrentBatchConfig$1.transition;
|
||
ReactCurrentBatchConfig$1.transition = {};
|
||
var currentTransition = ReactCurrentBatchConfig$1.transition;
|
||
{
|
||
ReactCurrentBatchConfig$1.transition._updatedFibers = new Set();
|
||
}
|
||
try {
|
||
setPending(false);
|
||
callback();
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$1.transition = prevTransition;
|
||
{
|
||
if (prevTransition === null && currentTransition._updatedFibers) {
|
||
var updatedFibersCount = currentTransition._updatedFibers.size;
|
||
if (updatedFibersCount > 10) {
|
||
warn('Detected a large number of updates inside startTransition. ' + 'If this is due to a subscription please re-write it to use React provided hooks. ' + 'Otherwise concurrent mode guarantees are off the table.');
|
||
}
|
||
currentTransition._updatedFibers.clear();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function mountTransition() {
|
||
var _mountState = mountState(false), isPending = _mountState[0], setPending = _mountState[1]; // The `start` method never changes.
|
||
var start = startTransition.bind(null, setPending);
|
||
var hook = mountWorkInProgressHook();
|
||
hook.memoizedState = start;
|
||
return [isPending, start];
|
||
}
|
||
function updateTransition() {
|
||
var _updateState = updateState(), isPending = _updateState[0];
|
||
var hook = updateWorkInProgressHook();
|
||
var start = hook.memoizedState;
|
||
return [isPending, start];
|
||
}
|
||
function rerenderTransition() {
|
||
var _rerenderState = rerenderState(), isPending = _rerenderState[0];
|
||
var hook = updateWorkInProgressHook();
|
||
var start = hook.memoizedState;
|
||
return [isPending, start];
|
||
}
|
||
var isUpdatingOpaqueValueInRenderPhase = false;
|
||
function getIsUpdatingOpaqueValueInRenderPhaseInDEV() {
|
||
{
|
||
return isUpdatingOpaqueValueInRenderPhase;
|
||
}
|
||
}
|
||
function mountId() {
|
||
var hook = mountWorkInProgressHook();
|
||
var root = getWorkInProgressRoot(); // TODO: In Fizz, id generation is specific to each server config. Maybe we
|
||
// should do this in Fiber, too? Deferring this decision for now because
|
||
// there's no other place to store the prefix except for an internal field on
|
||
// the public createRoot object, which the fiber tree does not currently have
|
||
// a reference to.
|
||
var identifierPrefix = root.identifierPrefix;
|
||
var id;
|
||
if (getIsHydrating()) {
|
||
var treeId = getTreeId(); // Use a captial R prefix for server-generated ids.
|
||
id = ':' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end
|
||
// that represents the position of this useId hook among all the useId
|
||
// hooks for this fiber.
|
||
var localId = localIdCounter++;
|
||
if (localId > 0) {
|
||
id += 'H' + localId.toString(32);
|
||
}
|
||
id += ':';
|
||
}
|
||
else {
|
||
// Use a lowercase r prefix for client-generated ids.
|
||
var globalClientId = globalClientIdCounter++;
|
||
id = ':' + identifierPrefix + 'r' + globalClientId.toString(32) + ':';
|
||
}
|
||
hook.memoizedState = id;
|
||
return id;
|
||
}
|
||
function updateId() {
|
||
var hook = updateWorkInProgressHook();
|
||
var id = hook.memoizedState;
|
||
return id;
|
||
}
|
||
function dispatchReducerAction(fiber, queue, action) {
|
||
{
|
||
if (typeof arguments[3] === 'function') {
|
||
error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
|
||
}
|
||
}
|
||
var lane = requestUpdateLane(fiber);
|
||
var update = {
|
||
lane: lane,
|
||
action: action,
|
||
hasEagerState: false,
|
||
eagerState: null,
|
||
next: null
|
||
};
|
||
if (isRenderPhaseUpdate(fiber)) {
|
||
enqueueRenderPhaseUpdate(queue, update);
|
||
}
|
||
else {
|
||
var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
entangleTransitionUpdate(root, queue, lane);
|
||
}
|
||
}
|
||
markUpdateInDevTools(fiber, lane);
|
||
}
|
||
function dispatchSetState(fiber, queue, action) {
|
||
{
|
||
if (typeof arguments[3] === 'function') {
|
||
error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
|
||
}
|
||
}
|
||
var lane = requestUpdateLane(fiber);
|
||
var update = {
|
||
lane: lane,
|
||
action: action,
|
||
hasEagerState: false,
|
||
eagerState: null,
|
||
next: null
|
||
};
|
||
if (isRenderPhaseUpdate(fiber)) {
|
||
enqueueRenderPhaseUpdate(queue, update);
|
||
}
|
||
else {
|
||
var alternate = fiber.alternate;
|
||
if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
|
||
// The queue is currently empty, which means we can eagerly compute the
|
||
// next state before entering the render phase. If the new state is the
|
||
// same as the current state, we may be able to bail out entirely.
|
||
var lastRenderedReducer = queue.lastRenderedReducer;
|
||
if (lastRenderedReducer !== null) {
|
||
var prevDispatcher;
|
||
{
|
||
prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
}
|
||
try {
|
||
var currentState = queue.lastRenderedState;
|
||
var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute
|
||
// it, on the update object. If the reducer hasn't changed by the
|
||
// time we enter the render phase, then the eager state can be used
|
||
// without calling the reducer again.
|
||
update.hasEagerState = true;
|
||
update.eagerState = eagerState;
|
||
if (objectIs(eagerState, currentState)) {
|
||
// Fast path. We can bail out without scheduling React to re-render.
|
||
// It's still possible that we'll need to rebase this update later,
|
||
// if the component re-renders for a different reason and by that
|
||
// time the reducer has changed.
|
||
// TODO: Do we still need to entangle transitions in this case?
|
||
enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane);
|
||
return;
|
||
}
|
||
}
|
||
catch (error) { // Suppress the error. It will throw again in the render phase.
|
||
}
|
||
finally {
|
||
{
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
entangleTransitionUpdate(root, queue, lane);
|
||
}
|
||
}
|
||
markUpdateInDevTools(fiber, lane);
|
||
}
|
||
function isRenderPhaseUpdate(fiber) {
|
||
var alternate = fiber.alternate;
|
||
return fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1;
|
||
}
|
||
function enqueueRenderPhaseUpdate(queue, update) {
|
||
// This is a render phase update. Stash it in a lazily-created map of
|
||
// queue -> linked list of updates. After this render pass, we'll restart
|
||
// and apply the stashed updates on top of the work-in-progress hook.
|
||
didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
|
||
var pending = queue.pending;
|
||
if (pending === null) {
|
||
// This is the first update. Create a circular list.
|
||
update.next = update;
|
||
}
|
||
else {
|
||
update.next = pending.next;
|
||
pending.next = update;
|
||
}
|
||
queue.pending = update;
|
||
} // TODO: Move to ReactFiberConcurrentUpdates?
|
||
function entangleTransitionUpdate(root, queue, lane) {
|
||
if (isTransitionLane(lane)) {
|
||
var queueLanes = queue.lanes; // If any entangled lanes are no longer pending on the root, then they
|
||
// must have finished. We can remove them from the shared queue, which
|
||
// represents a superset of the actually pending lanes. In some cases we
|
||
// may entangle more than we need to, but that's OK. In fact it's worse if
|
||
// we *don't* entangle when we should.
|
||
queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
|
||
var newQueueLanes = mergeLanes(queueLanes, lane);
|
||
queue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
|
||
// the lane finished since the last time we entangled it. So we need to
|
||
// entangle it again, just to be sure.
|
||
markRootEntangled(root, newQueueLanes);
|
||
}
|
||
}
|
||
function markUpdateInDevTools(fiber, lane, action) {
|
||
{
|
||
markStateUpdateScheduled(fiber, lane);
|
||
}
|
||
}
|
||
var ContextOnlyDispatcher = {
|
||
readContext: readContext,
|
||
useCallback: throwInvalidHookError,
|
||
useContext: throwInvalidHookError,
|
||
useEffect: throwInvalidHookError,
|
||
useImperativeHandle: throwInvalidHookError,
|
||
useInsertionEffect: throwInvalidHookError,
|
||
useLayoutEffect: throwInvalidHookError,
|
||
useMemo: throwInvalidHookError,
|
||
useReducer: throwInvalidHookError,
|
||
useRef: throwInvalidHookError,
|
||
useState: throwInvalidHookError,
|
||
useDebugValue: throwInvalidHookError,
|
||
useDeferredValue: throwInvalidHookError,
|
||
useTransition: throwInvalidHookError,
|
||
useMutableSource: throwInvalidHookError,
|
||
useSyncExternalStore: throwInvalidHookError,
|
||
useId: throwInvalidHookError,
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
var HooksDispatcherOnMountInDEV = null;
|
||
var HooksDispatcherOnMountWithHookTypesInDEV = null;
|
||
var HooksDispatcherOnUpdateInDEV = null;
|
||
var HooksDispatcherOnRerenderInDEV = null;
|
||
var InvalidNestedHooksDispatcherOnMountInDEV = null;
|
||
var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
|
||
var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
|
||
{
|
||
var warnInvalidContextAccess = function () {
|
||
error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
||
};
|
||
var warnInvalidHookAccess = function () {
|
||
error('Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see ' + 'https://reactjs.org/link/rules-of-hooks');
|
||
};
|
||
HooksDispatcherOnMountInDEV = {
|
||
readContext: function (context) {
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
return mountCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
mountHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
return mountEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
return mountImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
return mountInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
return mountLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
mountHookTypesDev();
|
||
checkDepsAreArrayDev(deps);
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
mountHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
mountHookTypesDev();
|
||
return mountRef(initialValue);
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
mountHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
mountHookTypesDev();
|
||
return mountDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
mountHookTypesDev();
|
||
return mountDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
mountHookTypesDev();
|
||
return mountTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
mountHookTypesDev();
|
||
return mountMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
mountHookTypesDev();
|
||
return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
mountHookTypesDev();
|
||
return mountId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
HooksDispatcherOnMountWithHookTypesInDEV = {
|
||
readContext: function (context) {
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
updateHookTypesDev();
|
||
return mountCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
updateHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
updateHookTypesDev();
|
||
return mountEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
updateHookTypesDev();
|
||
return mountImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
updateHookTypesDev();
|
||
return mountInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
updateHookTypesDev();
|
||
return mountLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
updateHookTypesDev();
|
||
return mountRef(initialValue);
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
updateHookTypesDev();
|
||
return mountDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
updateHookTypesDev();
|
||
return mountDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
updateHookTypesDev();
|
||
return mountTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
updateHookTypesDev();
|
||
return mountMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
updateHookTypesDev();
|
||
return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
updateHookTypesDev();
|
||
return mountId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
HooksDispatcherOnUpdateInDEV = {
|
||
readContext: function (context) {
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
updateHookTypesDev();
|
||
return updateCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
updateHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
updateHookTypesDev();
|
||
return updateEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
updateHookTypesDev();
|
||
return updateImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
updateHookTypesDev();
|
||
return updateInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
updateHookTypesDev();
|
||
return updateLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
updateHookTypesDev();
|
||
return updateRef();
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
updateHookTypesDev();
|
||
return updateDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
updateHookTypesDev();
|
||
return updateDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
updateHookTypesDev();
|
||
return updateTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
updateHookTypesDev();
|
||
return updateMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
updateHookTypesDev();
|
||
return updateSyncExternalStore(subscribe, getSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
updateHookTypesDev();
|
||
return updateId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
HooksDispatcherOnRerenderInDEV = {
|
||
readContext: function (context) {
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
updateHookTypesDev();
|
||
return updateCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
updateHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
updateHookTypesDev();
|
||
return updateEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
updateHookTypesDev();
|
||
return updateImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
updateHookTypesDev();
|
||
return updateInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
updateHookTypesDev();
|
||
return updateLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
||
try {
|
||
return updateMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
||
try {
|
||
return rerenderReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
updateHookTypesDev();
|
||
return updateRef();
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
||
try {
|
||
return rerenderState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
updateHookTypesDev();
|
||
return updateDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
updateHookTypesDev();
|
||
return rerenderDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
updateHookTypesDev();
|
||
return rerenderTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
updateHookTypesDev();
|
||
return updateMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
updateHookTypesDev();
|
||
return updateSyncExternalStore(subscribe, getSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
updateHookTypesDev();
|
||
return updateId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
InvalidNestedHooksDispatcherOnMountInDEV = {
|
||
readContext: function (context) {
|
||
warnInvalidContextAccess();
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountRef(initialValue);
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
||
try {
|
||
return mountState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
warnInvalidHookAccess();
|
||
mountHookTypesDev();
|
||
return mountId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
InvalidNestedHooksDispatcherOnUpdateInDEV = {
|
||
readContext: function (context) {
|
||
warnInvalidContextAccess();
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateRef();
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateSyncExternalStore(subscribe, getSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
InvalidNestedHooksDispatcherOnRerenderInDEV = {
|
||
readContext: function (context) {
|
||
warnInvalidContextAccess();
|
||
return readContext(context);
|
||
},
|
||
useCallback: function (callback, deps) {
|
||
currentHookNameInDev = 'useCallback';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateCallback(callback, deps);
|
||
},
|
||
useContext: function (context) {
|
||
currentHookNameInDev = 'useContext';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return readContext(context);
|
||
},
|
||
useEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateEffect(create, deps);
|
||
},
|
||
useImperativeHandle: function (ref, create, deps) {
|
||
currentHookNameInDev = 'useImperativeHandle';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateImperativeHandle(ref, create, deps);
|
||
},
|
||
useInsertionEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useInsertionEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateInsertionEffect(create, deps);
|
||
},
|
||
useLayoutEffect: function (create, deps) {
|
||
currentHookNameInDev = 'useLayoutEffect';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateLayoutEffect(create, deps);
|
||
},
|
||
useMemo: function (create, deps) {
|
||
currentHookNameInDev = 'useMemo';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return updateMemo(create, deps);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useReducer: function (reducer, initialArg, init) {
|
||
currentHookNameInDev = 'useReducer';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return rerenderReducer(reducer, initialArg, init);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useRef: function (initialValue) {
|
||
currentHookNameInDev = 'useRef';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateRef();
|
||
},
|
||
useState: function (initialState) {
|
||
currentHookNameInDev = 'useState';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
var prevDispatcher = ReactCurrentDispatcher$1.current;
|
||
ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
||
try {
|
||
return rerenderState(initialState);
|
||
}
|
||
finally {
|
||
ReactCurrentDispatcher$1.current = prevDispatcher;
|
||
}
|
||
},
|
||
useDebugValue: function (value, formatterFn) {
|
||
currentHookNameInDev = 'useDebugValue';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateDebugValue();
|
||
},
|
||
useDeferredValue: function (value) {
|
||
currentHookNameInDev = 'useDeferredValue';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return rerenderDeferredValue(value);
|
||
},
|
||
useTransition: function () {
|
||
currentHookNameInDev = 'useTransition';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return rerenderTransition();
|
||
},
|
||
useMutableSource: function (source, getSnapshot, subscribe) {
|
||
currentHookNameInDev = 'useMutableSource';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateMutableSource();
|
||
},
|
||
useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
||
currentHookNameInDev = 'useSyncExternalStore';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateSyncExternalStore(subscribe, getSnapshot);
|
||
},
|
||
useId: function () {
|
||
currentHookNameInDev = 'useId';
|
||
warnInvalidHookAccess();
|
||
updateHookTypesDev();
|
||
return updateId();
|
||
},
|
||
unstable_isNewReconciler: enableNewReconciler
|
||
};
|
||
}
|
||
var now$1 = Scheduler.unstable_now;
|
||
var commitTime = 0;
|
||
var layoutEffectStartTime = -1;
|
||
var profilerStartTime = -1;
|
||
var passiveEffectStartTime = -1;
|
||
/**
|
||
* Tracks whether the current update was a nested/cascading update (scheduled from a layout effect).
|
||
*
|
||
* The overall sequence is:
|
||
* 1. render
|
||
* 2. commit (and call `onRender`, `onCommit`)
|
||
* 3. check for nested updates
|
||
* 4. flush passive effects (and call `onPostCommit`)
|
||
*
|
||
* Nested updates are identified in step 3 above,
|
||
* but step 4 still applies to the work that was just committed.
|
||
* We use two flags to track nested updates then:
|
||
* one tracks whether the upcoming update is a nested update,
|
||
* and the other tracks whether the current update was a nested update.
|
||
* The first value gets synced to the second at the start of the render phase.
|
||
*/
|
||
var currentUpdateIsNested = false;
|
||
var nestedUpdateScheduled = false;
|
||
function isCurrentUpdateNested() {
|
||
return currentUpdateIsNested;
|
||
}
|
||
function markNestedUpdateScheduled() {
|
||
{
|
||
nestedUpdateScheduled = true;
|
||
}
|
||
}
|
||
function resetNestedUpdateFlag() {
|
||
{
|
||
currentUpdateIsNested = false;
|
||
nestedUpdateScheduled = false;
|
||
}
|
||
}
|
||
function syncNestedUpdateFlag() {
|
||
{
|
||
currentUpdateIsNested = nestedUpdateScheduled;
|
||
nestedUpdateScheduled = false;
|
||
}
|
||
}
|
||
function getCommitTime() {
|
||
return commitTime;
|
||
}
|
||
function recordCommitTime() {
|
||
commitTime = now$1();
|
||
}
|
||
function startProfilerTimer(fiber) {
|
||
profilerStartTime = now$1();
|
||
if (fiber.actualStartTime < 0) {
|
||
fiber.actualStartTime = now$1();
|
||
}
|
||
}
|
||
function stopProfilerTimerIfRunning(fiber) {
|
||
profilerStartTime = -1;
|
||
}
|
||
function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
|
||
if (profilerStartTime >= 0) {
|
||
var elapsedTime = now$1() - profilerStartTime;
|
||
fiber.actualDuration += elapsedTime;
|
||
if (overrideBaseTime) {
|
||
fiber.selfBaseDuration = elapsedTime;
|
||
}
|
||
profilerStartTime = -1;
|
||
}
|
||
}
|
||
function recordLayoutEffectDuration(fiber) {
|
||
if (layoutEffectStartTime >= 0) {
|
||
var elapsedTime = now$1() - layoutEffectStartTime;
|
||
layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
|
||
// Or the root (for the DevTools Profiler to read)
|
||
var parentFiber = fiber.return;
|
||
while (parentFiber !== null) {
|
||
switch (parentFiber.tag) {
|
||
case HostRoot:
|
||
var root = parentFiber.stateNode;
|
||
root.effectDuration += elapsedTime;
|
||
return;
|
||
case Profiler:
|
||
var parentStateNode = parentFiber.stateNode;
|
||
parentStateNode.effectDuration += elapsedTime;
|
||
return;
|
||
}
|
||
parentFiber = parentFiber.return;
|
||
}
|
||
}
|
||
}
|
||
function recordPassiveEffectDuration(fiber) {
|
||
if (passiveEffectStartTime >= 0) {
|
||
var elapsedTime = now$1() - passiveEffectStartTime;
|
||
passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
|
||
// Or the root (for the DevTools Profiler to read)
|
||
var parentFiber = fiber.return;
|
||
while (parentFiber !== null) {
|
||
switch (parentFiber.tag) {
|
||
case HostRoot:
|
||
var root = parentFiber.stateNode;
|
||
if (root !== null) {
|
||
root.passiveEffectDuration += elapsedTime;
|
||
}
|
||
return;
|
||
case Profiler:
|
||
var parentStateNode = parentFiber.stateNode;
|
||
if (parentStateNode !== null) {
|
||
// Detached fibers have their state node cleared out.
|
||
// In this case, the return pointer is also cleared out,
|
||
// so we won't be able to report the time spent in this Profiler's subtree.
|
||
parentStateNode.passiveEffectDuration += elapsedTime;
|
||
}
|
||
return;
|
||
}
|
||
parentFiber = parentFiber.return;
|
||
}
|
||
}
|
||
}
|
||
function startLayoutEffectTimer() {
|
||
layoutEffectStartTime = now$1();
|
||
}
|
||
function startPassiveEffectTimer() {
|
||
passiveEffectStartTime = now$1();
|
||
}
|
||
function transferActualDuration(fiber) {
|
||
// Transfer time spent rendering these children so we don't lose it
|
||
// after we rerender. This is used as a helper in special cases
|
||
// where we should count the work of multiple passes.
|
||
var child = fiber.child;
|
||
while (child) {
|
||
fiber.actualDuration += child.actualDuration;
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
function createCapturedValueAtFiber(value, source) {
|
||
// If the value is an error, call this function immediately after it is thrown
|
||
// so the stack is accurate.
|
||
return {
|
||
value: value,
|
||
source: source,
|
||
stack: getStackByFiberInDevAndProd(source),
|
||
digest: null
|
||
};
|
||
}
|
||
function createCapturedValue(value, digest, stack) {
|
||
return {
|
||
value: value,
|
||
source: null,
|
||
stack: stack != null ? stack : null,
|
||
digest: digest != null ? digest : null
|
||
};
|
||
}
|
||
// This module is forked in different environments.
|
||
// By default, return `true` to log errors to the console.
|
||
// Forks can return `false` if this isn't desirable.
|
||
function showErrorDialog(boundary, errorInfo) {
|
||
return true;
|
||
}
|
||
function logCapturedError(boundary, errorInfo) {
|
||
try {
|
||
var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging.
|
||
// This enables renderers like ReactNative to better manage redbox behavior.
|
||
if (logError === false) {
|
||
return;
|
||
}
|
||
var error = errorInfo.value;
|
||
if (true) {
|
||
var source = errorInfo.source;
|
||
var stack = errorInfo.stack;
|
||
var componentStack = stack !== null ? stack : ''; // Browsers support silencing uncaught errors by calling
|
||
// `preventDefault()` in window `error` handler.
|
||
// We record this information as an expando on the error.
|
||
if (error != null && error._suppressLogging) {
|
||
if (boundary.tag === ClassComponent) {
|
||
// The error is recoverable and was silenced.
|
||
// Ignore it and don't print the stack addendum.
|
||
// This is handy for testing error boundaries without noise.
|
||
return;
|
||
} // The error is fatal. Since the silencing might have
|
||
// been accidental, we'll surface it anyway.
|
||
// However, the browser would have silenced the original error
|
||
// so we'll print it first, and then print the stack addendum.
|
||
console['error'](error); // Don't transform to our wrapper
|
||
// For a more detailed description of this block, see:
|
||
// https://github.com/facebook/react/pull/13384
|
||
}
|
||
var componentName = source ? getComponentNameFromFiber(source) : null;
|
||
var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component:" : 'The above error occurred in one of your React components:';
|
||
var errorBoundaryMessage;
|
||
if (boundary.tag === HostRoot) {
|
||
errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.';
|
||
}
|
||
else {
|
||
var errorBoundaryName = getComponentNameFromFiber(boundary) || 'Anonymous';
|
||
errorBoundaryMessage = "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + errorBoundaryName + ".");
|
||
}
|
||
var combinedMessage = componentNameMessage + "\n" + componentStack + "\n\n" + ("" + errorBoundaryMessage); // In development, we provide our own message with just the component stack.
|
||
// We don't include the original error message and JS stack because the browser
|
||
// has already printed it. Even if the application swallows the error, it is still
|
||
// displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
|
||
console['error'](combinedMessage); // Don't transform to our wrapper
|
||
}
|
||
else {
|
||
// In production, we print the error directly.
|
||
// This will include the message, the JS stack, and anything the browser wants to show.
|
||
// We pass the error object instead of custom message so that the browser displays the error natively.
|
||
console['error'](error); // Don't transform to our wrapper
|
||
}
|
||
}
|
||
catch (e) {
|
||
// This method must not throw, or React internal state will get messed up.
|
||
// If console.error is overridden, or logCapturedError() shows a dialog that throws,
|
||
// we want to report this error outside of the normal stack as a last resort.
|
||
// https://github.com/facebook/react/issues/13188
|
||
setTimeout(function () {
|
||
throw e;
|
||
});
|
||
}
|
||
}
|
||
var PossiblyWeakMap$1 = typeof WeakMap === 'function' ? WeakMap : Map;
|
||
function createRootErrorUpdate(fiber, errorInfo, lane) {
|
||
var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null.
|
||
update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property
|
||
// being called "element".
|
||
update.payload = {
|
||
element: null
|
||
};
|
||
var error = errorInfo.value;
|
||
update.callback = function () {
|
||
onUncaughtError(error);
|
||
logCapturedError(fiber, errorInfo);
|
||
};
|
||
return update;
|
||
}
|
||
function createClassErrorUpdate(fiber, errorInfo, lane) {
|
||
var update = createUpdate(NoTimestamp, lane);
|
||
update.tag = CaptureUpdate;
|
||
var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
|
||
if (typeof getDerivedStateFromError === 'function') {
|
||
var error$1 = errorInfo.value;
|
||
update.payload = function () {
|
||
return getDerivedStateFromError(error$1);
|
||
};
|
||
update.callback = function () {
|
||
{
|
||
markFailedErrorBoundaryForHotReloading(fiber);
|
||
}
|
||
logCapturedError(fiber, errorInfo);
|
||
};
|
||
}
|
||
var inst = fiber.stateNode;
|
||
if (inst !== null && typeof inst.componentDidCatch === 'function') {
|
||
update.callback = function callback() {
|
||
{
|
||
markFailedErrorBoundaryForHotReloading(fiber);
|
||
}
|
||
logCapturedError(fiber, errorInfo);
|
||
if (typeof getDerivedStateFromError !== 'function') {
|
||
// To preserve the preexisting retry behavior of error boundaries,
|
||
// we keep track of which ones already failed during this batch.
|
||
// This gets reset before we yield back to the browser.
|
||
// TODO: Warn in strict mode if getDerivedStateFromError is
|
||
// not defined.
|
||
markLegacyErrorBoundaryAsFailed(this);
|
||
}
|
||
var error$1 = errorInfo.value;
|
||
var stack = errorInfo.stack;
|
||
this.componentDidCatch(error$1, {
|
||
componentStack: stack !== null ? stack : ''
|
||
});
|
||
{
|
||
if (typeof getDerivedStateFromError !== 'function') {
|
||
// If componentDidCatch is the only error boundary method defined,
|
||
// then it needs to call setState to recover from errors.
|
||
// If no state update is scheduled then the boundary will swallow the error.
|
||
if (!includesSomeLane(fiber.lanes, SyncLane)) {
|
||
error('%s: Error boundaries should implement getDerivedStateFromError(). ' + 'In that method, return a state update to display an error message or fallback UI.', getComponentNameFromFiber(fiber) || 'Unknown');
|
||
}
|
||
}
|
||
}
|
||
};
|
||
}
|
||
return update;
|
||
}
|
||
function attachPingListener(root, wakeable, lanes) {
|
||
// Attach a ping listener
|
||
//
|
||
// The data might resolve before we have a chance to commit the fallback. Or,
|
||
// in the case of a refresh, we'll never commit a fallback. So we need to
|
||
// attach a listener now. When it resolves ("pings"), we can decide whether to
|
||
// try rendering the tree again.
|
||
//
|
||
// Only attach a listener if one does not already exist for the lanes
|
||
// we're currently rendering (which acts like a "thread ID" here).
|
||
//
|
||
// We only need to do this in concurrent mode. Legacy Suspense always
|
||
// commits fallbacks synchronously, so there are no pings.
|
||
var pingCache = root.pingCache;
|
||
var threadIDs;
|
||
if (pingCache === null) {
|
||
pingCache = root.pingCache = new PossiblyWeakMap$1();
|
||
threadIDs = new Set();
|
||
pingCache.set(wakeable, threadIDs);
|
||
}
|
||
else {
|
||
threadIDs = pingCache.get(wakeable);
|
||
if (threadIDs === undefined) {
|
||
threadIDs = new Set();
|
||
pingCache.set(wakeable, threadIDs);
|
||
}
|
||
}
|
||
if (!threadIDs.has(lanes)) {
|
||
// Memoize using the thread ID to prevent redundant listeners.
|
||
threadIDs.add(lanes);
|
||
var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
|
||
{
|
||
if (isDevToolsPresent) {
|
||
// If we have pending work still, restore the original updaters
|
||
restorePendingUpdaters(root, lanes);
|
||
}
|
||
}
|
||
wakeable.then(ping, ping);
|
||
}
|
||
}
|
||
function attachRetryListener(suspenseBoundary, root, wakeable, lanes) {
|
||
// Retry listener
|
||
//
|
||
// If the fallback does commit, we need to attach a different type of
|
||
// listener. This one schedules an update on the Suspense boundary to turn
|
||
// the fallback state off.
|
||
//
|
||
// Stash the wakeable on the boundary fiber so we can access it in the
|
||
// commit phase.
|
||
//
|
||
// When the wakeable resolves, we'll attempt to render the boundary
|
||
// again ("retry").
|
||
var wakeables = suspenseBoundary.updateQueue;
|
||
if (wakeables === null) {
|
||
var updateQueue = new Set();
|
||
updateQueue.add(wakeable);
|
||
suspenseBoundary.updateQueue = updateQueue;
|
||
}
|
||
else {
|
||
wakeables.add(wakeable);
|
||
}
|
||
}
|
||
function resetSuspendedComponent(sourceFiber, rootRenderLanes) {
|
||
// A legacy mode Suspense quirk, only relevant to hook components.
|
||
var tag = sourceFiber.tag;
|
||
if ((sourceFiber.mode & ConcurrentMode) === NoMode && (tag === FunctionComponent || tag === ForwardRef || tag === SimpleMemoComponent)) {
|
||
var currentSource = sourceFiber.alternate;
|
||
if (currentSource) {
|
||
sourceFiber.updateQueue = currentSource.updateQueue;
|
||
sourceFiber.memoizedState = currentSource.memoizedState;
|
||
sourceFiber.lanes = currentSource.lanes;
|
||
}
|
||
else {
|
||
sourceFiber.updateQueue = null;
|
||
sourceFiber.memoizedState = null;
|
||
}
|
||
}
|
||
}
|
||
function getNearestSuspenseBoundaryToCapture(returnFiber) {
|
||
var node = returnFiber;
|
||
do {
|
||
if (node.tag === SuspenseComponent && shouldCaptureSuspense(node)) {
|
||
return node;
|
||
} // This boundary already captured during this render. Continue to the next
|
||
// boundary.
|
||
node = node.return;
|
||
} while (node !== null);
|
||
return null;
|
||
}
|
||
function markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes) {
|
||
// This marks a Suspense boundary so that when we're unwinding the stack,
|
||
// it captures the suspended "exception" and does a second (fallback) pass.
|
||
if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
|
||
// Legacy Mode Suspense
|
||
//
|
||
// If the boundary is in legacy mode, we should *not*
|
||
// suspend the commit. Pretend as if the suspended component rendered
|
||
// null and keep rendering. When the Suspense boundary completes,
|
||
// we'll do a second pass to render the fallback.
|
||
if (suspenseBoundary === returnFiber) {
|
||
// Special case where we suspended while reconciling the children of
|
||
// a Suspense boundary's inner Offscreen wrapper fiber. This happens
|
||
// when a React.lazy component is a direct child of a
|
||
// Suspense boundary.
|
||
//
|
||
// Suspense boundaries are implemented as multiple fibers, but they
|
||
// are a single conceptual unit. The legacy mode behavior where we
|
||
// pretend the suspended fiber committed as `null` won't work,
|
||
// because in this case the "suspended" fiber is the inner
|
||
// Offscreen wrapper.
|
||
//
|
||
// Because the contents of the boundary haven't started rendering
|
||
// yet (i.e. nothing in the tree has partially rendered) we can
|
||
// switch to the regular, concurrent mode behavior: mark the
|
||
// boundary with ShouldCapture and enter the unwind phase.
|
||
suspenseBoundary.flags |= ShouldCapture;
|
||
}
|
||
else {
|
||
suspenseBoundary.flags |= DidCapture;
|
||
sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete.
|
||
// But we shouldn't call any lifecycle methods or callbacks. Remove
|
||
// all lifecycle effect tags.
|
||
sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
|
||
if (sourceFiber.tag === ClassComponent) {
|
||
var currentSourceFiber = sourceFiber.alternate;
|
||
if (currentSourceFiber === null) {
|
||
// This is a new mount. Change the tag so it's not mistaken for a
|
||
// completed class component. For example, we should not call
|
||
// componentWillUnmount if it is deleted.
|
||
sourceFiber.tag = IncompleteClassComponent;
|
||
}
|
||
else {
|
||
// When we try rendering again, we should not reuse the current fiber,
|
||
// since it's known to be in an inconsistent state. Use a force update to
|
||
// prevent a bail out.
|
||
var update = createUpdate(NoTimestamp, SyncLane);
|
||
update.tag = ForceUpdate;
|
||
enqueueUpdate(sourceFiber, update, SyncLane);
|
||
}
|
||
} // The source fiber did not complete. Mark it with Sync priority to
|
||
// indicate that it still has pending work.
|
||
sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
|
||
}
|
||
return suspenseBoundary;
|
||
} // Confirmed that the boundary is in a concurrent mode tree. Continue
|
||
// with the normal suspend path.
|
||
//
|
||
// After this we'll use a set of heuristics to determine whether this
|
||
// render pass will run to completion or restart or "suspend" the commit.
|
||
// The actual logic for this is spread out in different places.
|
||
//
|
||
// This first principle is that if we're going to suspend when we complete
|
||
// a root, then we should also restart if we get an update or ping that
|
||
// might unsuspend it, and vice versa. The only reason to suspend is
|
||
// because you think you might want to restart before committing. However,
|
||
// it doesn't make sense to restart only while in the period we're suspended.
|
||
//
|
||
// Restarting too aggressively is also not good because it starves out any
|
||
// intermediate loading state. So we use heuristics to determine when.
|
||
// Suspense Heuristics
|
||
//
|
||
// If nothing threw a Promise or all the same fallbacks are already showing,
|
||
// then don't suspend/restart.
|
||
//
|
||
// If this is an initial render of a new tree of Suspense boundaries and
|
||
// those trigger a fallback, then don't suspend/restart. We want to ensure
|
||
// that we can show the initial loading state as quickly as possible.
|
||
//
|
||
// If we hit a "Delayed" case, such as when we'd switch from content back into
|
||
// a fallback, then we should always suspend/restart. Transitions apply
|
||
// to this case. If none is defined, JND is used instead.
|
||
//
|
||
// If we're already showing a fallback and it gets "retried", allowing us to show
|
||
// another level, but there's still an inner boundary that would show a fallback,
|
||
// then we suspend/restart for 500ms since the last time we showed a fallback
|
||
// anywhere in the tree. This effectively throttles progressive loading into a
|
||
// consistent train of commits. This also gives us an opportunity to restart to
|
||
// get to the completed state slightly earlier.
|
||
//
|
||
// If there's ambiguity due to batching it's resolved in preference of:
|
||
// 1) "delayed", 2) "initial render", 3) "retry".
|
||
//
|
||
// We want to ensure that a "busy" state doesn't get force committed. We want to
|
||
// ensure that new initial loading states can commit as soon as possible.
|
||
suspenseBoundary.flags |= ShouldCapture; // TODO: I think we can remove this, since we now use `DidCapture` in
|
||
// the begin phase to prevent an early bailout.
|
||
suspenseBoundary.lanes = rootRenderLanes;
|
||
return suspenseBoundary;
|
||
}
|
||
function throwException(root, returnFiber, sourceFiber, value, rootRenderLanes) {
|
||
// The source fiber did not complete.
|
||
sourceFiber.flags |= Incomplete;
|
||
{
|
||
if (isDevToolsPresent) {
|
||
// If we have pending work still, restore the original updaters
|
||
restorePendingUpdaters(root, rootRenderLanes);
|
||
}
|
||
}
|
||
if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
|
||
// This is a wakeable. The component suspended.
|
||
var wakeable = value;
|
||
resetSuspendedComponent(sourceFiber);
|
||
{
|
||
if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
|
||
markDidThrowWhileHydratingDEV();
|
||
}
|
||
}
|
||
var suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
|
||
if (suspenseBoundary !== null) {
|
||
suspenseBoundary.flags &= ~ForceClientRender;
|
||
markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // We only attach ping listeners in concurrent mode. Legacy Suspense always
|
||
// commits fallbacks synchronously, so there are no pings.
|
||
if (suspenseBoundary.mode & ConcurrentMode) {
|
||
attachPingListener(root, wakeable, rootRenderLanes);
|
||
}
|
||
attachRetryListener(suspenseBoundary, root, wakeable);
|
||
return;
|
||
}
|
||
else {
|
||
// No boundary was found. Unless this is a sync update, this is OK.
|
||
// We can suspend and wait for more data to arrive.
|
||
if (!includesSyncLane(rootRenderLanes)) {
|
||
// This is not a sync update. Suspend. Since we're not activating a
|
||
// Suspense boundary, this will unwind all the way to the root without
|
||
// performing a second pass to render a fallback. (This is arguably how
|
||
// refresh transitions should work, too, since we're not going to commit
|
||
// the fallbacks anyway.)
|
||
//
|
||
// This case also applies to initial hydration.
|
||
attachPingListener(root, wakeable, rootRenderLanes);
|
||
renderDidSuspendDelayIfPossible();
|
||
return;
|
||
} // This is a sync/discrete update. We treat this case like an error
|
||
// because discrete renders are expected to produce a complete tree
|
||
// synchronously to maintain consistency with external state.
|
||
var uncaughtSuspenseError = new Error('A component suspended while responding to synchronous input. This ' + 'will cause the UI to be replaced with a loading indicator. To ' + 'fix, updates that suspend should be wrapped ' + 'with startTransition.'); // If we're outside a transition, fall through to the regular error path.
|
||
// The error will be caught by the nearest suspense boundary.
|
||
value = uncaughtSuspenseError;
|
||
}
|
||
}
|
||
else {
|
||
// This is a regular error, not a Suspense wakeable.
|
||
if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
|
||
markDidThrowWhileHydratingDEV();
|
||
var _suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber); // If the error was thrown during hydration, we may be able to recover by
|
||
// discarding the dehydrated content and switching to a client render.
|
||
// Instead of surfacing the error, find the nearest Suspense boundary
|
||
// and render it again without hydration.
|
||
if (_suspenseBoundary !== null) {
|
||
if ((_suspenseBoundary.flags & ShouldCapture) === NoFlags) {
|
||
// Set a flag to indicate that we should try rendering the normal
|
||
// children again, not the fallback.
|
||
_suspenseBoundary.flags |= ForceClientRender;
|
||
}
|
||
markSuspenseBoundaryShouldCapture(_suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // Even though the user may not be affected by this error, we should
|
||
// still log it so it can be fixed.
|
||
queueHydrationError(createCapturedValueAtFiber(value, sourceFiber));
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
value = createCapturedValueAtFiber(value, sourceFiber);
|
||
renderDidError(value); // We didn't find a boundary that could handle this type of exception. Start
|
||
// over and traverse parent path again, this time treating the exception
|
||
// as an error.
|
||
var workInProgress = returnFiber;
|
||
do {
|
||
switch (workInProgress.tag) {
|
||
case HostRoot:
|
||
{
|
||
var _errorInfo = value;
|
||
workInProgress.flags |= ShouldCapture;
|
||
var lane = pickArbitraryLane(rootRenderLanes);
|
||
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
|
||
var update = createRootErrorUpdate(workInProgress, _errorInfo, lane);
|
||
enqueueCapturedUpdate(workInProgress, update);
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
// Capture and retry
|
||
var errorInfo = value;
|
||
var ctor = workInProgress.type;
|
||
var instance = workInProgress.stateNode;
|
||
if ((workInProgress.flags & DidCapture) === NoFlags && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
|
||
workInProgress.flags |= ShouldCapture;
|
||
var _lane = pickArbitraryLane(rootRenderLanes);
|
||
workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state
|
||
var _update = createClassErrorUpdate(workInProgress, errorInfo, _lane);
|
||
enqueueCapturedUpdate(workInProgress, _update);
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
workInProgress = workInProgress.return;
|
||
} while (workInProgress !== null);
|
||
}
|
||
function getSuspendedCache() {
|
||
{
|
||
return null;
|
||
} // This function is called when a Suspense boundary suspends. It returns the
|
||
}
|
||
var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
|
||
var didReceiveUpdate = false;
|
||
var didWarnAboutBadClass;
|
||
var didWarnAboutModulePatternComponent;
|
||
var didWarnAboutContextTypeOnFunctionComponent;
|
||
var didWarnAboutGetDerivedStateOnFunctionComponent;
|
||
var didWarnAboutFunctionRefs;
|
||
var didWarnAboutReassigningProps;
|
||
var didWarnAboutRevealOrder;
|
||
var didWarnAboutTailOptions;
|
||
{
|
||
didWarnAboutBadClass = {};
|
||
didWarnAboutModulePatternComponent = {};
|
||
didWarnAboutContextTypeOnFunctionComponent = {};
|
||
didWarnAboutGetDerivedStateOnFunctionComponent = {};
|
||
didWarnAboutFunctionRefs = {};
|
||
didWarnAboutReassigningProps = false;
|
||
didWarnAboutRevealOrder = {};
|
||
didWarnAboutTailOptions = {};
|
||
}
|
||
function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
|
||
if (current === null) {
|
||
// If this is a fresh new component that hasn't been rendered yet, we
|
||
// won't update its child set by applying minimal side-effects. Instead,
|
||
// we will add them all to the child before it gets rendered. That means
|
||
// we can optimize this reconciliation pass by not tracking side-effects.
|
||
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
|
||
}
|
||
else {
|
||
// If the current child is the same as the work in progress, it means that
|
||
// we haven't yet started any work on these children. Therefore, we use
|
||
// the clone algorithm to create a copy of all the current children.
|
||
// If we had any progressed work already, that is invalid at this point so
|
||
// let's throw it out.
|
||
workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
|
||
}
|
||
}
|
||
function forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes) {
|
||
// This function is fork of reconcileChildren. It's used in cases where we
|
||
// want to reconcile without matching against the existing set. This has the
|
||
// effect of all current children being unmounted; even if the type and key
|
||
// are the same, the old child is unmounted and a new child is created.
|
||
//
|
||
// To do this, we're going to go through the reconcile algorithm twice. In
|
||
// the first pass, we schedule a deletion for all the current children by
|
||
// passing null.
|
||
workInProgress.child = reconcileChildFibers(workInProgress, current.child, null, renderLanes); // In the second pass, we mount the new children. The trick here is that we
|
||
// pass null in place of where we usually pass the current child set. This has
|
||
// the effect of remounting all children regardless of whether their
|
||
// identities match.
|
||
workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes);
|
||
}
|
||
function updateForwardRef(current, workInProgress, Component, nextProps, renderLanes) {
|
||
// TODO: current can be non-null here even if the component
|
||
// hasn't yet mounted. This happens after the first render suspends.
|
||
// We'll need to figure out if this is fine or can cause issues.
|
||
{
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
// Lazy component props can't be validated in createElement
|
||
// because they're only guaranteed to be resolved here.
|
||
var innerPropTypes = Component.propTypes;
|
||
if (innerPropTypes) {
|
||
checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
||
'prop', getComponentNameFromType(Component));
|
||
}
|
||
}
|
||
}
|
||
var render = Component.render;
|
||
var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent
|
||
var nextChildren;
|
||
var hasId;
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
{
|
||
markComponentRenderStarted(workInProgress);
|
||
}
|
||
{
|
||
ReactCurrentOwner$1.current = workInProgress;
|
||
setIsRendering(true);
|
||
nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
setIsRendering(false);
|
||
}
|
||
{
|
||
markComponentRenderStopped();
|
||
}
|
||
if (current !== null && !didReceiveUpdate) {
|
||
bailoutHooks(current, workInProgress, renderLanes);
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
if (getIsHydrating() && hasId) {
|
||
pushMaterializedTreeId(workInProgress);
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateMemoComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
||
if (current === null) {
|
||
var type = Component.type;
|
||
if (isSimpleFunctionComponent(type) && Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either.
|
||
Component.defaultProps === undefined) {
|
||
var resolvedType = type;
|
||
{
|
||
resolvedType = resolveFunctionForHotReloading(type);
|
||
} // If this is a plain function component without default props,
|
||
// and with only the default shallow comparison, we upgrade it
|
||
// to a SimpleMemoComponent to allow fast path updates.
|
||
workInProgress.tag = SimpleMemoComponent;
|
||
workInProgress.type = resolvedType;
|
||
{
|
||
validateFunctionComponentInDev(workInProgress, type);
|
||
}
|
||
return updateSimpleMemoComponent(current, workInProgress, resolvedType, nextProps, renderLanes);
|
||
}
|
||
{
|
||
var innerPropTypes = type.propTypes;
|
||
if (innerPropTypes) {
|
||
// Inner memo component props aren't currently validated in createElement.
|
||
// We could move it there, but we'd still need this for lazy code path.
|
||
checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
||
'prop', getComponentNameFromType(type));
|
||
}
|
||
}
|
||
var child = createFiberFromTypeAndProps(Component.type, null, nextProps, workInProgress, workInProgress.mode, renderLanes);
|
||
child.ref = workInProgress.ref;
|
||
child.return = workInProgress;
|
||
workInProgress.child = child;
|
||
return child;
|
||
}
|
||
{
|
||
var _type = Component.type;
|
||
var _innerPropTypes = _type.propTypes;
|
||
if (_innerPropTypes) {
|
||
// Inner memo component props aren't currently validated in createElement.
|
||
// We could move it there, but we'd still need this for lazy code path.
|
||
checkPropTypes(_innerPropTypes, nextProps, // Resolved props
|
||
'prop', getComponentNameFromType(_type));
|
||
}
|
||
}
|
||
var currentChild = current.child; // This is always exactly one child
|
||
var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);
|
||
if (!hasScheduledUpdateOrContext) {
|
||
// This will be the props with resolved defaultProps,
|
||
// unlike current.memoizedProps which will be the unresolved ones.
|
||
var prevProps = currentChild.memoizedProps; // Default to shallow comparison
|
||
var compare = Component.compare;
|
||
compare = compare !== null ? compare : shallowEqual;
|
||
if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
var newChild = createWorkInProgress(currentChild, nextProps);
|
||
newChild.ref = workInProgress.ref;
|
||
newChild.return = workInProgress;
|
||
workInProgress.child = newChild;
|
||
return newChild;
|
||
}
|
||
function updateSimpleMemoComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
||
// TODO: current can be non-null here even if the component
|
||
// hasn't yet mounted. This happens when the inner render suspends.
|
||
// We'll need to figure out if this is fine or can cause issues.
|
||
{
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
// Lazy component props can't be validated in createElement
|
||
// because they're only guaranteed to be resolved here.
|
||
var outerMemoType = workInProgress.elementType;
|
||
if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
|
||
// We warn when you define propTypes on lazy()
|
||
// so let's just skip over it to find memo() outer wrapper.
|
||
// Inner props for memo are validated later.
|
||
var lazyComponent = outerMemoType;
|
||
var payload = lazyComponent._payload;
|
||
var init = lazyComponent._init;
|
||
try {
|
||
outerMemoType = init(payload);
|
||
}
|
||
catch (x) {
|
||
outerMemoType = null;
|
||
} // Inner propTypes will be validated in the function component path.
|
||
var outerPropTypes = outerMemoType && outerMemoType.propTypes;
|
||
if (outerPropTypes) {
|
||
checkPropTypes(outerPropTypes, nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
|
||
'prop', getComponentNameFromType(outerMemoType));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (current !== null) {
|
||
var prevProps = current.memoizedProps;
|
||
if (shallowEqual(prevProps, nextProps) && current.ref === workInProgress.ref && ( // Prevent bailout if the implementation changed due to hot reload.
|
||
workInProgress.type === current.type)) {
|
||
didReceiveUpdate = false; // The props are shallowly equal. Reuse the previous props object, like we
|
||
// would during a normal fiber bailout.
|
||
//
|
||
// We don't have strong guarantees that the props object is referentially
|
||
// equal during updates where we can't bail out anyway — like if the props
|
||
// are shallowly equal, but there's a local state or context update in the
|
||
// same batch.
|
||
//
|
||
// However, as a principle, we should aim to make the behavior consistent
|
||
// across different ways of memoizing a component. For example, React.memo
|
||
// has a different internal Fiber layout if you pass a normal function
|
||
// component (SimpleMemoComponent) versus if you pass a different type
|
||
// like forwardRef (MemoComponent). But this is an implementation detail.
|
||
// Wrapping a component in forwardRef (or React.lazy, etc) shouldn't
|
||
// affect whether the props object is reused during a bailout.
|
||
workInProgress.pendingProps = nextProps = prevProps;
|
||
if (!checkScheduledUpdateOrContext(current, renderLanes)) {
|
||
// The pending lanes were cleared at the beginning of beginWork. We're
|
||
// about to bail out, but there might be other lanes that weren't
|
||
// included in the current render. Usually, the priority level of the
|
||
// remaining updates is accumulated during the evaluation of the
|
||
// component (i.e. when processing the update queue). But since since
|
||
// we're bailing out early *without* evaluating the component, we need
|
||
// to account for it here, too. Reset to the value of the current fiber.
|
||
// NOTE: This only applies to SimpleMemoComponent, not MemoComponent,
|
||
// because a MemoComponent fiber does not have hooks or an update queue;
|
||
// rather, it wraps around an inner component, which may or may not
|
||
// contains hooks.
|
||
// TODO: Move the reset at in beginWork out of the common path so that
|
||
// this is no longer necessary.
|
||
workInProgress.lanes = current.lanes;
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
|
||
// This is a special case that only exists for legacy mode.
|
||
// See https://github.com/facebook/react/pull/19216.
|
||
didReceiveUpdate = true;
|
||
}
|
||
}
|
||
}
|
||
return updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes);
|
||
}
|
||
function updateOffscreenComponent(current, workInProgress, renderLanes) {
|
||
var nextProps = workInProgress.pendingProps;
|
||
var nextChildren = nextProps.children;
|
||
var prevState = current !== null ? current.memoizedState : null;
|
||
if (nextProps.mode === 'hidden' || enableLegacyHidden) {
|
||
// Rendering a hidden tree.
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
// In legacy sync mode, don't defer the subtree. Render it now.
|
||
// TODO: Consider how Offscreen should work with transitions in the future
|
||
var nextState = {
|
||
baseLanes: NoLanes,
|
||
cachePool: null,
|
||
transitions: null
|
||
};
|
||
workInProgress.memoizedState = nextState;
|
||
pushRenderLanes(workInProgress, renderLanes);
|
||
}
|
||
else if (!includesSomeLane(renderLanes, OffscreenLane)) {
|
||
var spawnedCachePool = null; // We're hidden, and we're not rendering at Offscreen. We will bail out
|
||
// and resume this tree later.
|
||
var nextBaseLanes;
|
||
if (prevState !== null) {
|
||
var prevBaseLanes = prevState.baseLanes;
|
||
nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes);
|
||
}
|
||
else {
|
||
nextBaseLanes = renderLanes;
|
||
} // Schedule this fiber to re-render at offscreen priority. Then bailout.
|
||
workInProgress.lanes = workInProgress.childLanes = laneToLanes(OffscreenLane);
|
||
var _nextState = {
|
||
baseLanes: nextBaseLanes,
|
||
cachePool: spawnedCachePool,
|
||
transitions: null
|
||
};
|
||
workInProgress.memoizedState = _nextState;
|
||
workInProgress.updateQueue = null;
|
||
// to avoid a push/pop misalignment.
|
||
pushRenderLanes(workInProgress, nextBaseLanes);
|
||
return null;
|
||
}
|
||
else {
|
||
// This is the second render. The surrounding visible content has already
|
||
// committed. Now we resume rendering the hidden tree.
|
||
// Rendering at offscreen, so we can clear the base lanes.
|
||
var _nextState2 = {
|
||
baseLanes: NoLanes,
|
||
cachePool: null,
|
||
transitions: null
|
||
};
|
||
workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out.
|
||
var subtreeRenderLanes = prevState !== null ? prevState.baseLanes : renderLanes;
|
||
pushRenderLanes(workInProgress, subtreeRenderLanes);
|
||
}
|
||
}
|
||
else {
|
||
// Rendering a visible tree.
|
||
var _subtreeRenderLanes;
|
||
if (prevState !== null) {
|
||
// We're going from hidden -> visible.
|
||
_subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes);
|
||
workInProgress.memoizedState = null;
|
||
}
|
||
else {
|
||
// We weren't previously hidden, and we still aren't, so there's nothing
|
||
// special to do. Need to push to the stack regardless, though, to avoid
|
||
// a push/pop misalignment.
|
||
_subtreeRenderLanes = renderLanes;
|
||
}
|
||
pushRenderLanes(workInProgress, _subtreeRenderLanes);
|
||
}
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
} // Note: These happen to have identical begin phases, for now. We shouldn't hold
|
||
function updateFragment(current, workInProgress, renderLanes) {
|
||
var nextChildren = workInProgress.pendingProps;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateMode(current, workInProgress, renderLanes) {
|
||
var nextChildren = workInProgress.pendingProps.children;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateProfiler(current, workInProgress, renderLanes) {
|
||
{
|
||
workInProgress.flags |= Update;
|
||
{
|
||
// Reset effect durations for the next eventual effect phase.
|
||
// These are reset during render to allow the DevTools commit hook a chance to read them,
|
||
var stateNode = workInProgress.stateNode;
|
||
stateNode.effectDuration = 0;
|
||
stateNode.passiveEffectDuration = 0;
|
||
}
|
||
}
|
||
var nextProps = workInProgress.pendingProps;
|
||
var nextChildren = nextProps.children;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function markRef(current, workInProgress) {
|
||
var ref = workInProgress.ref;
|
||
if (current === null && ref !== null || current !== null && current.ref !== ref) {
|
||
// Schedule a Ref effect
|
||
workInProgress.flags |= Ref;
|
||
{
|
||
workInProgress.flags |= RefStatic;
|
||
}
|
||
}
|
||
}
|
||
function updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
||
{
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
// Lazy component props can't be validated in createElement
|
||
// because they're only guaranteed to be resolved here.
|
||
var innerPropTypes = Component.propTypes;
|
||
if (innerPropTypes) {
|
||
checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
||
'prop', getComponentNameFromType(Component));
|
||
}
|
||
}
|
||
}
|
||
var context;
|
||
{
|
||
var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
|
||
context = getMaskedContext(workInProgress, unmaskedContext);
|
||
}
|
||
var nextChildren;
|
||
var hasId;
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
{
|
||
markComponentRenderStarted(workInProgress);
|
||
}
|
||
{
|
||
ReactCurrentOwner$1.current = workInProgress;
|
||
setIsRendering(true);
|
||
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
setIsRendering(false);
|
||
}
|
||
{
|
||
markComponentRenderStopped();
|
||
}
|
||
if (current !== null && !didReceiveUpdate) {
|
||
bailoutHooks(current, workInProgress, renderLanes);
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
if (getIsHydrating() && hasId) {
|
||
pushMaterializedTreeId(workInProgress);
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateClassComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
||
{
|
||
// This is used by DevTools to force a boundary to error.
|
||
switch (shouldError(workInProgress)) {
|
||
case false:
|
||
{
|
||
var _instance = workInProgress.stateNode;
|
||
var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack.
|
||
// Is there a better way to do this?
|
||
var tempInstance = new ctor(workInProgress.memoizedProps, _instance.context);
|
||
var state = tempInstance.state;
|
||
_instance.updater.enqueueSetState(_instance, state, null);
|
||
break;
|
||
}
|
||
case true:
|
||
{
|
||
workInProgress.flags |= DidCapture;
|
||
workInProgress.flags |= ShouldCapture; // eslint-disable-next-line react-internal/prod-error-codes
|
||
var error$1 = new Error('Simulated error coming from DevTools');
|
||
var lane = pickArbitraryLane(renderLanes);
|
||
workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state
|
||
var update = createClassErrorUpdate(workInProgress, createCapturedValueAtFiber(error$1, workInProgress), lane);
|
||
enqueueCapturedUpdate(workInProgress, update);
|
||
break;
|
||
}
|
||
}
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
// Lazy component props can't be validated in createElement
|
||
// because they're only guaranteed to be resolved here.
|
||
var innerPropTypes = Component.propTypes;
|
||
if (innerPropTypes) {
|
||
checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
||
'prop', getComponentNameFromType(Component));
|
||
}
|
||
}
|
||
} // Push context providers early to prevent context stack mismatches.
|
||
// During mounting we don't know the child context yet as the instance doesn't exist.
|
||
// We will invalidate the child context in finishClassComponent() right after rendering.
|
||
var hasContext;
|
||
if (isContextProvider(Component)) {
|
||
hasContext = true;
|
||
pushContextProvider(workInProgress);
|
||
}
|
||
else {
|
||
hasContext = false;
|
||
}
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
var instance = workInProgress.stateNode;
|
||
var shouldUpdate;
|
||
if (instance === null) {
|
||
resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress); // In the initial pass we might need to construct the instance.
|
||
constructClassInstance(workInProgress, Component, nextProps);
|
||
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
||
shouldUpdate = true;
|
||
}
|
||
else if (current === null) {
|
||
// In a resume, we'll already have an instance we can reuse.
|
||
shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
||
}
|
||
else {
|
||
shouldUpdate = updateClassInstance(current, workInProgress, Component, nextProps, renderLanes);
|
||
}
|
||
var nextUnitOfWork = finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes);
|
||
{
|
||
var inst = workInProgress.stateNode;
|
||
if (shouldUpdate && inst.props !== nextProps) {
|
||
if (!didWarnAboutReassigningProps) {
|
||
error('It looks like %s is reassigning its own `this.props` while rendering. ' + 'This is not supported and can lead to confusing bugs.', getComponentNameFromFiber(workInProgress) || 'a component');
|
||
}
|
||
didWarnAboutReassigningProps = true;
|
||
}
|
||
}
|
||
return nextUnitOfWork;
|
||
}
|
||
function finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes) {
|
||
// Refs should update even if shouldComponentUpdate returns false
|
||
markRef(current, workInProgress);
|
||
var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;
|
||
if (!shouldUpdate && !didCaptureError) {
|
||
// Context providers should defer to sCU for rendering
|
||
if (hasContext) {
|
||
invalidateContextProvider(workInProgress, Component, false);
|
||
}
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
var instance = workInProgress.stateNode; // Rerender
|
||
ReactCurrentOwner$1.current = workInProgress;
|
||
var nextChildren;
|
||
if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
|
||
// If we captured an error, but getDerivedStateFromError is not defined,
|
||
// unmount all the children. componentDidCatch will schedule an update to
|
||
// re-render a fallback. This is temporary until we migrate everyone to
|
||
// the new API.
|
||
// TODO: Warn in a future release.
|
||
nextChildren = null;
|
||
{
|
||
stopProfilerTimerIfRunning();
|
||
}
|
||
}
|
||
else {
|
||
{
|
||
markComponentRenderStarted(workInProgress);
|
||
}
|
||
{
|
||
setIsRendering(true);
|
||
nextChildren = instance.render();
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
instance.render();
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
setIsRendering(false);
|
||
}
|
||
{
|
||
markComponentRenderStopped();
|
||
}
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
if (current !== null && didCaptureError) {
|
||
// If we're recovering from an error, reconcile without reusing any of
|
||
// the existing children. Conceptually, the normal children and the children
|
||
// that are shown on error are two different sets, so we shouldn't reuse
|
||
// normal children even if their identities match.
|
||
forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes);
|
||
}
|
||
else {
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
} // Memoize state using the values we just used to render.
|
||
// TODO: Restructure so we never read values from the instance.
|
||
workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it.
|
||
if (hasContext) {
|
||
invalidateContextProvider(workInProgress, Component, true);
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
function pushHostRootContext(workInProgress) {
|
||
var root = workInProgress.stateNode;
|
||
if (root.pendingContext) {
|
||
pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context);
|
||
}
|
||
else if (root.context) {
|
||
// Should always be set
|
||
pushTopLevelContextObject(workInProgress, root.context, false);
|
||
}
|
||
pushHostContainer(workInProgress, root.containerInfo);
|
||
}
|
||
function updateHostRoot(current, workInProgress, renderLanes) {
|
||
pushHostRootContext(workInProgress);
|
||
if (current === null) {
|
||
throw new Error('Should have a current fiber. This is a bug in React.');
|
||
}
|
||
var nextProps = workInProgress.pendingProps;
|
||
var prevState = workInProgress.memoizedState;
|
||
var prevChildren = prevState.element;
|
||
cloneUpdateQueue(current, workInProgress);
|
||
processUpdateQueue(workInProgress, nextProps, null, renderLanes);
|
||
var nextState = workInProgress.memoizedState;
|
||
var root = workInProgress.stateNode;
|
||
// being called "element".
|
||
var nextChildren = nextState.element;
|
||
if (supportsHydration && prevState.isDehydrated) {
|
||
// This is a hydration root whose shell has not yet hydrated. We should
|
||
// attempt to hydrate.
|
||
// Flip isDehydrated to false to indicate that when this render
|
||
// finishes, the root will no longer be dehydrated.
|
||
var overrideState = {
|
||
element: nextChildren,
|
||
isDehydrated: false,
|
||
cache: nextState.cache,
|
||
pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries,
|
||
transitions: nextState.transitions
|
||
};
|
||
var updateQueue = workInProgress.updateQueue; // `baseState` can always be the last state because the root doesn't
|
||
// have reducer functions so it doesn't need rebasing.
|
||
updateQueue.baseState = overrideState;
|
||
workInProgress.memoizedState = overrideState;
|
||
if (workInProgress.flags & ForceClientRender) {
|
||
// Something errored during a previous attempt to hydrate the shell, so we
|
||
// forced a client render.
|
||
var recoverableError = createCapturedValueAtFiber(new Error('There was an error while hydrating. Because the error happened outside ' + 'of a Suspense boundary, the entire root will switch to ' + 'client rendering.'), workInProgress);
|
||
return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError);
|
||
}
|
||
else if (nextChildren !== prevChildren) {
|
||
var _recoverableError = createCapturedValueAtFiber(new Error('This root received an early update, before anything was able ' + 'hydrate. Switched the entire root to client rendering.'), workInProgress);
|
||
return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, _recoverableError);
|
||
}
|
||
else {
|
||
// The outermost shell has not hydrated yet. Start hydrating.
|
||
enterHydrationState(workInProgress);
|
||
var child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
|
||
workInProgress.child = child;
|
||
var node = child;
|
||
while (node) {
|
||
// Mark each child as hydrating. This is a fast path to know whether this
|
||
// tree is part of a hydrating tree. This is used to determine if a child
|
||
// node has fully mounted yet, and for scheduling event replaying.
|
||
// Conceptually this is similar to Placement in that a new subtree is
|
||
// inserted into the React tree here. It just happens to not need DOM
|
||
// mutations because it already exists.
|
||
node.flags = node.flags & ~Placement | Hydrating;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
// Root is not dehydrated. Either this is a client-only root, or it
|
||
// already hydrated.
|
||
resetHydrationState();
|
||
if (nextChildren === prevChildren) {
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
function mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError) {
|
||
// Revert to client rendering.
|
||
resetHydrationState();
|
||
queueHydrationError(recoverableError);
|
||
workInProgress.flags |= ForceClientRender;
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateHostComponent(current, workInProgress, renderLanes) {
|
||
pushHostContext(workInProgress);
|
||
if (current === null) {
|
||
tryToClaimNextHydratableInstance(workInProgress);
|
||
}
|
||
var type = workInProgress.type;
|
||
var nextProps = workInProgress.pendingProps;
|
||
var prevProps = current !== null ? current.memoizedProps : null;
|
||
var nextChildren = nextProps.children;
|
||
var isDirectTextChild = shouldSetTextContent(type, nextProps);
|
||
if (isDirectTextChild) {
|
||
// We special case a direct text child of a host node. This is a common
|
||
// case. We won't handle it as a reified child. We will instead handle
|
||
// this in the host environment that also has access to this prop. That
|
||
// avoids allocating another HostText fiber and traversing it.
|
||
nextChildren = null;
|
||
}
|
||
else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
|
||
// If we're switching from a direct text child to a normal child, or to
|
||
// empty, we need to schedule the text content to be reset.
|
||
workInProgress.flags |= ContentReset;
|
||
}
|
||
markRef(current, workInProgress);
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function updateHostText(current, workInProgress) {
|
||
if (current === null) {
|
||
tryToClaimNextHydratableInstance(workInProgress);
|
||
} // Nothing to do here. This is terminal. We'll do the completion step
|
||
// immediately after.
|
||
return null;
|
||
}
|
||
function mountLazyComponent(_current, workInProgress, elementType, renderLanes) {
|
||
resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
|
||
var props = workInProgress.pendingProps;
|
||
var lazyComponent = elementType;
|
||
var payload = lazyComponent._payload;
|
||
var init = lazyComponent._init;
|
||
var Component = init(payload); // Store the unwrapped component in the type.
|
||
workInProgress.type = Component;
|
||
var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
|
||
var resolvedProps = resolveDefaultProps(Component, props);
|
||
var child;
|
||
switch (resolvedTag) {
|
||
case FunctionComponent:
|
||
{
|
||
{
|
||
validateFunctionComponentInDev(workInProgress, Component);
|
||
workInProgress.type = Component = resolveFunctionForHotReloading(Component);
|
||
}
|
||
child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderLanes);
|
||
return child;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
{
|
||
workInProgress.type = Component = resolveClassForHotReloading(Component);
|
||
}
|
||
child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderLanes);
|
||
return child;
|
||
}
|
||
case ForwardRef:
|
||
{
|
||
{
|
||
workInProgress.type = Component = resolveForwardRefForHotReloading(Component);
|
||
}
|
||
child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderLanes);
|
||
return child;
|
||
}
|
||
case MemoComponent:
|
||
{
|
||
{
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
var outerPropTypes = Component.propTypes;
|
||
if (outerPropTypes) {
|
||
checkPropTypes(outerPropTypes, resolvedProps, // Resolved for outer only
|
||
'prop', getComponentNameFromType(Component));
|
||
}
|
||
}
|
||
}
|
||
child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
|
||
renderLanes);
|
||
return child;
|
||
}
|
||
}
|
||
var hint = '';
|
||
{
|
||
if (Component !== null && typeof Component === 'object' && Component.$$typeof === REACT_LAZY_TYPE) {
|
||
hint = ' Did you wrap a component in React.lazy() more than once?';
|
||
}
|
||
} // This message intentionally doesn't mention ForwardRef or MemoComponent
|
||
// because the fact that it's a separate type of work is an
|
||
// implementation detail.
|
||
throw new Error("Element type is invalid. Received a promise that resolves to: " + Component + ". " + ("Lazy element type must resolve to a class or function." + hint));
|
||
}
|
||
function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderLanes) {
|
||
resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); // Promote the fiber to a class and try rendering again.
|
||
workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent`
|
||
// Push context providers early to prevent context stack mismatches.
|
||
// During mounting we don't know the child context yet as the instance doesn't exist.
|
||
// We will invalidate the child context in finishClassComponent() right after rendering.
|
||
var hasContext;
|
||
if (isContextProvider(Component)) {
|
||
hasContext = true;
|
||
pushContextProvider(workInProgress);
|
||
}
|
||
else {
|
||
hasContext = false;
|
||
}
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
constructClassInstance(workInProgress, Component, nextProps);
|
||
mountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
||
return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes);
|
||
}
|
||
function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) {
|
||
resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
|
||
var props = workInProgress.pendingProps;
|
||
var context;
|
||
{
|
||
var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
|
||
context = getMaskedContext(workInProgress, unmaskedContext);
|
||
}
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
var value;
|
||
var hasId;
|
||
{
|
||
markComponentRenderStarted(workInProgress);
|
||
}
|
||
{
|
||
if (Component.prototype && typeof Component.prototype.render === 'function') {
|
||
var componentName = getComponentNameFromType(Component) || 'Unknown';
|
||
if (!didWarnAboutBadClass[componentName]) {
|
||
error("The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName);
|
||
didWarnAboutBadClass[componentName] = true;
|
||
}
|
||
}
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
|
||
}
|
||
setIsRendering(true);
|
||
ReactCurrentOwner$1.current = workInProgress;
|
||
value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
setIsRendering(false);
|
||
}
|
||
{
|
||
markComponentRenderStopped();
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
{
|
||
// Support for module components is deprecated and is removed behind a flag.
|
||
// Whether or not it would crash later, we want to show a good message in DEV first.
|
||
if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
|
||
var _componentName = getComponentNameFromType(Component) || 'Unknown';
|
||
if (!didWarnAboutModulePatternComponent[_componentName]) {
|
||
error('The <%s /> component appears to be a function component that returns a class instance. ' + 'Change %s to a class that extends React.Component instead. ' + "If you can't use a class try assigning the prototype on the function as a workaround. " + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + 'cannot be called with `new` by React.', _componentName, _componentName, _componentName);
|
||
didWarnAboutModulePatternComponent[_componentName] = true;
|
||
}
|
||
}
|
||
}
|
||
if ( // Run these checks in production only if the flag is off.
|
||
// Eventually we'll delete this branch altogether.
|
||
typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
|
||
{
|
||
var _componentName2 = getComponentNameFromType(Component) || 'Unknown';
|
||
if (!didWarnAboutModulePatternComponent[_componentName2]) {
|
||
error('The <%s /> component appears to be a function component that returns a class instance. ' + 'Change %s to a class that extends React.Component instead. ' + "If you can't use a class try assigning the prototype on the function as a workaround. " + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + 'cannot be called with `new` by React.', _componentName2, _componentName2, _componentName2);
|
||
didWarnAboutModulePatternComponent[_componentName2] = true;
|
||
}
|
||
} // Proceed under the assumption that this is a class instance
|
||
workInProgress.tag = ClassComponent; // Throw out any hooks that were used.
|
||
workInProgress.memoizedState = null;
|
||
workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches.
|
||
// During mounting we don't know the child context yet as the instance doesn't exist.
|
||
// We will invalidate the child context in finishClassComponent() right after rendering.
|
||
var hasContext = false;
|
||
if (isContextProvider(Component)) {
|
||
hasContext = true;
|
||
pushContextProvider(workInProgress);
|
||
}
|
||
else {
|
||
hasContext = false;
|
||
}
|
||
workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
|
||
initializeUpdateQueue(workInProgress);
|
||
adoptClassInstance(workInProgress, value);
|
||
mountClassInstance(workInProgress, Component, props, renderLanes);
|
||
return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes);
|
||
}
|
||
else {
|
||
// Proceed under the assumption that this is a function component
|
||
workInProgress.tag = FunctionComponent;
|
||
{
|
||
if (workInProgress.mode & StrictLegacyMode) {
|
||
setIsStrictModeForDevtools(true);
|
||
try {
|
||
value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes);
|
||
hasId = checkDidRenderIdHook();
|
||
}
|
||
finally {
|
||
setIsStrictModeForDevtools(false);
|
||
}
|
||
}
|
||
}
|
||
if (getIsHydrating() && hasId) {
|
||
pushMaterializedTreeId(workInProgress);
|
||
}
|
||
reconcileChildren(null, workInProgress, value, renderLanes);
|
||
{
|
||
validateFunctionComponentInDev(workInProgress, Component);
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
}
|
||
function validateFunctionComponentInDev(workInProgress, Component) {
|
||
{
|
||
if (Component) {
|
||
if (Component.childContextTypes) {
|
||
error('%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component');
|
||
}
|
||
}
|
||
if (workInProgress.ref !== null) {
|
||
var info = '';
|
||
var ownerName = getCurrentFiberOwnerNameInDevOrNull();
|
||
if (ownerName) {
|
||
info += '\n\nCheck the render method of `' + ownerName + '`.';
|
||
}
|
||
var warningKey = ownerName || '';
|
||
var debugSource = workInProgress._debugSource;
|
||
if (debugSource) {
|
||
warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
|
||
}
|
||
if (!didWarnAboutFunctionRefs[warningKey]) {
|
||
didWarnAboutFunctionRefs[warningKey] = true;
|
||
error('Function components cannot be given refs. ' + 'Attempts to access this ref will fail. ' + 'Did you mean to use React.forwardRef()?%s', info);
|
||
}
|
||
}
|
||
if (typeof Component.getDerivedStateFromProps === 'function') {
|
||
var _componentName3 = getComponentNameFromType(Component) || 'Unknown';
|
||
if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) {
|
||
error('%s: Function components do not support getDerivedStateFromProps.', _componentName3);
|
||
didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true;
|
||
}
|
||
}
|
||
if (typeof Component.contextType === 'object' && Component.contextType !== null) {
|
||
var _componentName4 = getComponentNameFromType(Component) || 'Unknown';
|
||
if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) {
|
||
error('%s: Function components do not support contextType.', _componentName4);
|
||
didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var SUSPENDED_MARKER = {
|
||
dehydrated: null,
|
||
treeContext: null,
|
||
retryLane: NoLane
|
||
};
|
||
function mountSuspenseOffscreenState(renderLanes) {
|
||
return {
|
||
baseLanes: renderLanes,
|
||
cachePool: getSuspendedCache(),
|
||
transitions: null
|
||
};
|
||
}
|
||
function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) {
|
||
var cachePool = null;
|
||
return {
|
||
baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
|
||
cachePool: cachePool,
|
||
transitions: prevOffscreenState.transitions
|
||
};
|
||
} // TODO: Probably should inline this back
|
||
function shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) {
|
||
// If we're already showing a fallback, there are cases where we need to
|
||
// remain on that fallback regardless of whether the content has resolved.
|
||
// For example, SuspenseList coordinates when nested content appears.
|
||
if (current !== null) {
|
||
var suspenseState = current.memoizedState;
|
||
if (suspenseState === null) {
|
||
// Currently showing content. Don't hide it, even if ForceSuspenseFallback
|
||
// is true. More precise name might be "ForceRemainSuspenseFallback".
|
||
// Note: This is a factoring smell. Can't remain on a fallback if there's
|
||
// no fallback to remain on.
|
||
return false;
|
||
}
|
||
} // Not currently showing content. Consult the Suspense context.
|
||
return hasSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
||
}
|
||
function getRemainingWorkInPrimaryTree(current, renderLanes) {
|
||
// TODO: Should not remove render lanes that were pinged during this render
|
||
return removeLanes(current.childLanes, renderLanes);
|
||
}
|
||
function updateSuspenseComponent(current, workInProgress, renderLanes) {
|
||
var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend.
|
||
{
|
||
if (shouldSuspend(workInProgress)) {
|
||
workInProgress.flags |= DidCapture;
|
||
}
|
||
}
|
||
var suspenseContext = suspenseStackCursor.current;
|
||
var showFallback = false;
|
||
var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;
|
||
if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) {
|
||
// Something in this boundary's subtree already suspended. Switch to
|
||
// rendering the fallback children.
|
||
showFallback = true;
|
||
workInProgress.flags &= ~DidCapture;
|
||
}
|
||
else {
|
||
// Attempting the main content
|
||
if (current === null || current.memoizedState !== null) {
|
||
// This is a new mount or this boundary is already showing a fallback state.
|
||
// Mark this subtree context as having at least one invisible parent that could
|
||
// handle the fallback state.
|
||
// Avoided boundaries are not considered since they cannot handle preferred fallback states.
|
||
{
|
||
suspenseContext = addSubtreeSuspenseContext(suspenseContext, InvisibleParentSuspenseContext);
|
||
}
|
||
}
|
||
}
|
||
suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
||
pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense
|
||
// boundary's children. This involves some custom reconciliation logic. Two
|
||
// main reasons this is so complicated.
|
||
//
|
||
// First, Legacy Mode has different semantics for backwards compatibility. The
|
||
// primary tree will commit in an inconsistent state, so when we do the
|
||
// second pass to render the fallback, we do some exceedingly, uh, clever
|
||
// hacks to make that not totally break. Like transferring effects and
|
||
// deletions from hidden tree. In Concurrent Mode, it's much simpler,
|
||
// because we bailout on the primary tree completely and leave it in its old
|
||
// state, no effects. Same as what we do for Offscreen (except that
|
||
// Offscreen doesn't have the first render pass).
|
||
//
|
||
// Second is hydration. During hydration, the Suspense fiber has a slightly
|
||
// different layout, where the child points to a dehydrated fragment, which
|
||
// contains the DOM rendered by the server.
|
||
//
|
||
// Third, even if you set all that aside, Suspense is like error boundaries in
|
||
// that we first we try to render one tree, and if that fails, we render again
|
||
// and switch to a different tree. Like a try/catch block. So we have to track
|
||
// which branch we're currently rendering. Ideally we would model this using
|
||
// a stack.
|
||
if (current === null) {
|
||
// Initial mount
|
||
// Special path for hydration
|
||
// If we're currently hydrating, try to hydrate this boundary.
|
||
tryToClaimNextHydratableInstance(workInProgress); // This could've been a dehydrated suspense component.
|
||
var suspenseState = workInProgress.memoizedState;
|
||
if (suspenseState !== null) {
|
||
var dehydrated = suspenseState.dehydrated;
|
||
if (dehydrated !== null) {
|
||
return mountDehydratedSuspenseComponent(workInProgress, dehydrated);
|
||
}
|
||
}
|
||
var nextPrimaryChildren = nextProps.children;
|
||
var nextFallbackChildren = nextProps.fallback;
|
||
if (showFallback) {
|
||
var fallbackFragment = mountSuspenseFallbackChildren(workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes);
|
||
var primaryChildFragment = workInProgress.child;
|
||
primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes);
|
||
workInProgress.memoizedState = SUSPENDED_MARKER;
|
||
return fallbackFragment;
|
||
}
|
||
else {
|
||
return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);
|
||
}
|
||
}
|
||
else {
|
||
// This is an update.
|
||
// Special path for hydration
|
||
var prevState = current.memoizedState;
|
||
if (prevState !== null) {
|
||
var _dehydrated = prevState.dehydrated;
|
||
if (_dehydrated !== null) {
|
||
return updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, _dehydrated, prevState, renderLanes);
|
||
}
|
||
}
|
||
if (showFallback) {
|
||
var _nextFallbackChildren = nextProps.fallback;
|
||
var _nextPrimaryChildren = nextProps.children;
|
||
var fallbackChildFragment = updateSuspenseFallbackChildren(current, workInProgress, _nextPrimaryChildren, _nextFallbackChildren, renderLanes);
|
||
var _primaryChildFragment2 = workInProgress.child;
|
||
var prevOffscreenState = current.child.memoizedState;
|
||
_primaryChildFragment2.memoizedState = prevOffscreenState === null ? mountSuspenseOffscreenState(renderLanes) : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
|
||
_primaryChildFragment2.childLanes = getRemainingWorkInPrimaryTree(current, renderLanes);
|
||
workInProgress.memoizedState = SUSPENDED_MARKER;
|
||
return fallbackChildFragment;
|
||
}
|
||
else {
|
||
var _nextPrimaryChildren2 = nextProps.children;
|
||
var _primaryChildFragment3 = updateSuspensePrimaryChildren(current, workInProgress, _nextPrimaryChildren2, renderLanes);
|
||
workInProgress.memoizedState = null;
|
||
return _primaryChildFragment3;
|
||
}
|
||
}
|
||
}
|
||
function mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) {
|
||
var mode = workInProgress.mode;
|
||
var primaryChildProps = {
|
||
mode: 'visible',
|
||
children: primaryChildren
|
||
};
|
||
var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode);
|
||
primaryChildFragment.return = workInProgress;
|
||
workInProgress.child = primaryChildFragment;
|
||
return primaryChildFragment;
|
||
}
|
||
function mountSuspenseFallbackChildren(workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
||
var mode = workInProgress.mode;
|
||
var progressedPrimaryFragment = workInProgress.child;
|
||
var primaryChildProps = {
|
||
mode: 'hidden',
|
||
children: primaryChildren
|
||
};
|
||
var primaryChildFragment;
|
||
var fallbackChildFragment;
|
||
if ((mode & ConcurrentMode) === NoMode && progressedPrimaryFragment !== null) {
|
||
// In legacy mode, we commit the primary tree as if it successfully
|
||
// completed, even though it's in an inconsistent state.
|
||
primaryChildFragment = progressedPrimaryFragment;
|
||
primaryChildFragment.childLanes = NoLanes;
|
||
primaryChildFragment.pendingProps = primaryChildProps;
|
||
if (workInProgress.mode & ProfileMode) {
|
||
// Reset the durations from the first pass so they aren't included in the
|
||
// final amounts. This seems counterintuitive, since we're intentionally
|
||
// not measuring part of the render phase, but this makes it match what we
|
||
// do in Concurrent Mode.
|
||
primaryChildFragment.actualDuration = 0;
|
||
primaryChildFragment.actualStartTime = -1;
|
||
primaryChildFragment.selfBaseDuration = 0;
|
||
primaryChildFragment.treeBaseDuration = 0;
|
||
}
|
||
fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null);
|
||
}
|
||
else {
|
||
primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode);
|
||
fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null);
|
||
}
|
||
primaryChildFragment.return = workInProgress;
|
||
fallbackChildFragment.return = workInProgress;
|
||
primaryChildFragment.sibling = fallbackChildFragment;
|
||
workInProgress.child = primaryChildFragment;
|
||
return fallbackChildFragment;
|
||
}
|
||
function mountWorkInProgressOffscreenFiber(offscreenProps, mode, renderLanes) {
|
||
// The props argument to `createFiberFromOffscreen` is `any` typed, so we use
|
||
// this wrapper function to constrain it.
|
||
return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null);
|
||
}
|
||
function updateWorkInProgressOffscreenFiber(current, offscreenProps) {
|
||
// The props argument to `createWorkInProgress` is `any` typed, so we use this
|
||
// wrapper function to constrain it.
|
||
return createWorkInProgress(current, offscreenProps);
|
||
}
|
||
function updateSuspensePrimaryChildren(current, workInProgress, primaryChildren, renderLanes) {
|
||
var currentPrimaryChildFragment = current.child;
|
||
var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
|
||
var primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, {
|
||
mode: 'visible',
|
||
children: primaryChildren
|
||
});
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
primaryChildFragment.lanes = renderLanes;
|
||
}
|
||
primaryChildFragment.return = workInProgress;
|
||
primaryChildFragment.sibling = null;
|
||
if (currentFallbackChildFragment !== null) {
|
||
// Delete the fallback child fragment
|
||
var deletions = workInProgress.deletions;
|
||
if (deletions === null) {
|
||
workInProgress.deletions = [currentFallbackChildFragment];
|
||
workInProgress.flags |= ChildDeletion;
|
||
}
|
||
else {
|
||
deletions.push(currentFallbackChildFragment);
|
||
}
|
||
}
|
||
workInProgress.child = primaryChildFragment;
|
||
return primaryChildFragment;
|
||
}
|
||
function updateSuspenseFallbackChildren(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
||
var mode = workInProgress.mode;
|
||
var currentPrimaryChildFragment = current.child;
|
||
var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
|
||
var primaryChildProps = {
|
||
mode: 'hidden',
|
||
children: primaryChildren
|
||
};
|
||
var primaryChildFragment;
|
||
if ( // In legacy mode, we commit the primary tree as if it successfully
|
||
// completed, even though it's in an inconsistent state.
|
||
(mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was
|
||
// already cloned. In legacy mode, the only case where this isn't true is
|
||
// when DevTools forces us to display a fallback; we skip the first render
|
||
// pass entirely and go straight to rendering the fallback. (In Concurrent
|
||
// Mode, SuspenseList can also trigger this scenario, but this is a legacy-
|
||
// only codepath.)
|
||
workInProgress.child !== currentPrimaryChildFragment) {
|
||
var progressedPrimaryFragment = workInProgress.child;
|
||
primaryChildFragment = progressedPrimaryFragment;
|
||
primaryChildFragment.childLanes = NoLanes;
|
||
primaryChildFragment.pendingProps = primaryChildProps;
|
||
if (workInProgress.mode & ProfileMode) {
|
||
// Reset the durations from the first pass so they aren't included in the
|
||
// final amounts. This seems counterintuitive, since we're intentionally
|
||
// not measuring part of the render phase, but this makes it match what we
|
||
// do in Concurrent Mode.
|
||
primaryChildFragment.actualDuration = 0;
|
||
primaryChildFragment.actualStartTime = -1;
|
||
primaryChildFragment.selfBaseDuration = currentPrimaryChildFragment.selfBaseDuration;
|
||
primaryChildFragment.treeBaseDuration = currentPrimaryChildFragment.treeBaseDuration;
|
||
} // The fallback fiber was added as a deletion during the first pass.
|
||
// However, since we're going to remain on the fallback, we no longer want
|
||
// to delete it.
|
||
workInProgress.deletions = null;
|
||
}
|
||
else {
|
||
primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, primaryChildProps); // Since we're reusing a current tree, we need to reuse the flags, too.
|
||
// (We don't do this in legacy mode, because in legacy mode we don't re-use
|
||
// the current tree; see previous branch.)
|
||
primaryChildFragment.subtreeFlags = currentPrimaryChildFragment.subtreeFlags & StaticMask;
|
||
}
|
||
var fallbackChildFragment;
|
||
if (currentFallbackChildFragment !== null) {
|
||
fallbackChildFragment = createWorkInProgress(currentFallbackChildFragment, fallbackChildren);
|
||
}
|
||
else {
|
||
fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null); // Needs a placement effect because the parent (the Suspense boundary) already
|
||
// mounted but this is a new fiber.
|
||
fallbackChildFragment.flags |= Placement;
|
||
}
|
||
fallbackChildFragment.return = workInProgress;
|
||
primaryChildFragment.return = workInProgress;
|
||
primaryChildFragment.sibling = fallbackChildFragment;
|
||
workInProgress.child = primaryChildFragment;
|
||
return fallbackChildFragment;
|
||
}
|
||
function retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, recoverableError) {
|
||
// Falling back to client rendering. Because this has performance
|
||
// implications, it's considered a recoverable error, even though the user
|
||
// likely won't observe anything wrong with the UI.
|
||
//
|
||
// The error is passed in as an argument to enforce that every caller provide
|
||
// a custom message, or explicitly opt out (currently the only path that opts
|
||
// out is legacy mode; every concurrent path provides an error).
|
||
if (recoverableError !== null) {
|
||
queueHydrationError(recoverableError);
|
||
} // This will add the old fiber to the deletion list
|
||
reconcileChildFibers(workInProgress, current.child, null, renderLanes); // We're now not suspended nor dehydrated.
|
||
var nextProps = workInProgress.pendingProps;
|
||
var primaryChildren = nextProps.children;
|
||
var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Needs a placement effect because the parent (the Suspense boundary) already
|
||
// mounted but this is a new fiber.
|
||
primaryChildFragment.flags |= Placement;
|
||
workInProgress.memoizedState = null;
|
||
return primaryChildFragment;
|
||
}
|
||
function mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
||
var fiberMode = workInProgress.mode;
|
||
var primaryChildProps = {
|
||
mode: 'visible',
|
||
children: primaryChildren
|
||
};
|
||
var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, fiberMode);
|
||
var fallbackChildFragment = createFiberFromFragment(fallbackChildren, fiberMode, renderLanes, null); // Needs a placement effect because the parent (the Suspense
|
||
// boundary) already mounted but this is a new fiber.
|
||
fallbackChildFragment.flags |= Placement;
|
||
primaryChildFragment.return = workInProgress;
|
||
fallbackChildFragment.return = workInProgress;
|
||
primaryChildFragment.sibling = fallbackChildFragment;
|
||
workInProgress.child = primaryChildFragment;
|
||
if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
|
||
// We will have dropped the effect list which contains the
|
||
// deletion. We need to reconcile to delete the current child.
|
||
reconcileChildFibers(workInProgress, current.child, null, renderLanes);
|
||
}
|
||
return fallbackChildFragment;
|
||
}
|
||
function mountDehydratedSuspenseComponent(workInProgress, suspenseInstance, renderLanes) {
|
||
// During the first pass, we'll bail out and not drill into the children.
|
||
// Instead, we'll leave the content in place and try to hydrate it later.
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
{
|
||
error('Cannot hydrate Suspense in legacy mode. Switch from ' + 'ReactDOM.hydrate(element, container) to ' + 'ReactDOMClient.hydrateRoot(container, <App />)' + '.render(element) or remove the Suspense components from ' + 'the server rendered components.');
|
||
}
|
||
workInProgress.lanes = laneToLanes(SyncLane);
|
||
}
|
||
else if (isSuspenseInstanceFallback(suspenseInstance)) {
|
||
// This is a client-only boundary. Since we won't get any content from the server
|
||
// for this, we need to schedule that at a higher priority based on when it would
|
||
// have timed out. In theory we could render it in this pass but it would have the
|
||
// wrong priority associated with it and will prevent hydration of parent path.
|
||
// Instead, we'll leave work left on it to render it in a separate commit.
|
||
// TODO This time should be the time at which the server rendered response that is
|
||
// a parent to this boundary was displayed. However, since we currently don't have
|
||
// a protocol to transfer that time, we'll just estimate it by using the current
|
||
// time. This will mean that Suspense timeouts are slightly shifted to later than
|
||
// they should be.
|
||
// Schedule a normal pri update to render this content.
|
||
workInProgress.lanes = laneToLanes(DefaultHydrationLane);
|
||
}
|
||
else {
|
||
// We'll continue hydrating the rest at offscreen priority since we'll already
|
||
// be showing the right content coming from the server, it is no rush.
|
||
workInProgress.lanes = laneToLanes(OffscreenLane);
|
||
}
|
||
return null;
|
||
}
|
||
function updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, suspenseInstance, suspenseState, renderLanes) {
|
||
if (!didSuspend) {
|
||
// This is the first render pass. Attempt to hydrate.
|
||
// We should never be hydrating at this point because it is the first pass,
|
||
// but after we've already committed once.
|
||
warnIfHydrating();
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, // TODO: When we delete legacy mode, we should make this error argument
|
||
// required — every concurrent mode path that causes hydration to
|
||
// de-opt to client rendering should have an error message.
|
||
null);
|
||
}
|
||
if (isSuspenseInstanceFallback(suspenseInstance)) {
|
||
// This boundary is in a permanent fallback state. In this case, we'll never
|
||
// get an update and we'll never be able to hydrate the final content. Let's just try the
|
||
// client side render instead.
|
||
var digest, message, stack;
|
||
{
|
||
var _getSuspenseInstanceF = getSuspenseInstanceFallbackErrorDetails(suspenseInstance);
|
||
digest = _getSuspenseInstanceF.digest;
|
||
message = _getSuspenseInstanceF.message;
|
||
stack = _getSuspenseInstanceF.stack;
|
||
}
|
||
var error;
|
||
if (message) {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
error = new Error(message);
|
||
}
|
||
else {
|
||
error = new Error('The server could not finish this Suspense boundary, likely ' + 'due to an error during server rendering. Switched to ' + 'client rendering.');
|
||
}
|
||
var capturedValue = createCapturedValue(error, digest, stack);
|
||
return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, capturedValue);
|
||
}
|
||
// any context has changed, we need to treat is as if the input might have changed.
|
||
var hasContextChanged = includesSomeLane(renderLanes, current.childLanes);
|
||
if (didReceiveUpdate || hasContextChanged) {
|
||
// This boundary has changed since the first render. This means that we are now unable to
|
||
// hydrate it. We might still be able to hydrate it using a higher priority lane.
|
||
var root = getWorkInProgressRoot();
|
||
if (root !== null) {
|
||
var attemptHydrationAtLane = getBumpedLaneForHydration(root, renderLanes);
|
||
if (attemptHydrationAtLane !== NoLane && attemptHydrationAtLane !== suspenseState.retryLane) {
|
||
// Intentionally mutating since this render will get interrupted. This
|
||
// is one of the very rare times where we mutate the current tree
|
||
// during the render phase.
|
||
suspenseState.retryLane = attemptHydrationAtLane; // TODO: Ideally this would inherit the event time of the current render
|
||
var eventTime = NoTimestamp;
|
||
enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
|
||
scheduleUpdateOnFiber(root, current, attemptHydrationAtLane, eventTime);
|
||
}
|
||
} // If we have scheduled higher pri work above, this will probably just abort the render
|
||
// since we now have higher priority work, but in case it doesn't, we need to prepare to
|
||
// render something, if we time out. Even if that requires us to delete everything and
|
||
// skip hydration.
|
||
// Delay having to do this as long as the suspense timeout allows us.
|
||
renderDidSuspendDelayIfPossible();
|
||
var _capturedValue = createCapturedValue(new Error('This Suspense boundary received an update before it finished ' + 'hydrating. This caused the boundary to switch to client rendering. ' + 'The usual way to fix this is to wrap the original update ' + 'in startTransition.'));
|
||
return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue);
|
||
}
|
||
else if (isSuspenseInstancePending(suspenseInstance)) {
|
||
// This component is still pending more data from the server, so we can't hydrate its
|
||
// content. We treat it as if this component suspended itself. It might seem as if
|
||
// we could just try to render it client-side instead. However, this will perform a
|
||
// lot of unnecessary work and is unlikely to complete since it often will suspend
|
||
// on missing data anyway. Additionally, the server might be able to render more
|
||
// than we can on the client yet. In that case we'd end up with more fallback states
|
||
// on the client than if we just leave it alone. If the server times out or errors
|
||
// these should update this boundary to the permanent Fallback state instead.
|
||
// Mark it as having captured (i.e. suspended).
|
||
workInProgress.flags |= DidCapture; // Leave the child in place. I.e. the dehydrated fragment.
|
||
workInProgress.child = current.child; // Register a callback to retry this boundary once the server has sent the result.
|
||
var retry = retryDehydratedSuspenseBoundary.bind(null, current);
|
||
registerSuspenseInstanceRetry(suspenseInstance, retry);
|
||
return null;
|
||
}
|
||
else {
|
||
// This is the first attempt.
|
||
reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress, suspenseInstance, suspenseState.treeContext);
|
||
var primaryChildren = nextProps.children;
|
||
var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Mark the children as hydrating. This is a fast path to know whether this
|
||
// tree is part of a hydrating tree. This is used to determine if a child
|
||
// node has fully mounted yet, and for scheduling event replaying.
|
||
// Conceptually this is similar to Placement in that a new subtree is
|
||
// inserted into the React tree here. It just happens to not need DOM
|
||
// mutations because it already exists.
|
||
primaryChildFragment.flags |= Hydrating;
|
||
return primaryChildFragment;
|
||
}
|
||
}
|
||
else {
|
||
// This is the second render pass. We already attempted to hydrated, but
|
||
// something either suspended or errored.
|
||
if (workInProgress.flags & ForceClientRender) {
|
||
// Something errored during hydration. Try again without hydrating.
|
||
workInProgress.flags &= ~ForceClientRender;
|
||
var _capturedValue2 = createCapturedValue(new Error('There was an error while hydrating this Suspense boundary. ' + 'Switched to client rendering.'));
|
||
return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue2);
|
||
}
|
||
else if (workInProgress.memoizedState !== null) {
|
||
// Something suspended and we should still be in dehydrated mode.
|
||
// Leave the existing child in place.
|
||
workInProgress.child = current.child; // The dehydrated completion pass expects this flag to be there
|
||
// but the normal suspense pass doesn't.
|
||
workInProgress.flags |= DidCapture;
|
||
return null;
|
||
}
|
||
else {
|
||
// Suspended but we should no longer be in dehydrated mode.
|
||
// Therefore we now have to render the fallback.
|
||
var nextPrimaryChildren = nextProps.children;
|
||
var nextFallbackChildren = nextProps.fallback;
|
||
var fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes);
|
||
var _primaryChildFragment4 = workInProgress.child;
|
||
_primaryChildFragment4.memoizedState = mountSuspenseOffscreenState(renderLanes);
|
||
workInProgress.memoizedState = SUSPENDED_MARKER;
|
||
return fallbackChildFragment;
|
||
}
|
||
}
|
||
}
|
||
function scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {
|
||
fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null) {
|
||
alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
|
||
}
|
||
scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);
|
||
}
|
||
function propagateSuspenseContextChange(workInProgress, firstChild, renderLanes) {
|
||
// Mark any Suspense boundaries with fallbacks as having work to do.
|
||
// If they were previously forced into fallbacks, they may now be able
|
||
// to unblock.
|
||
var node = firstChild;
|
||
while (node !== null) {
|
||
if (node.tag === SuspenseComponent) {
|
||
var state = node.memoizedState;
|
||
if (state !== null) {
|
||
scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
|
||
}
|
||
}
|
||
else if (node.tag === SuspenseListComponent) {
|
||
// If the tail is hidden there might not be an Suspense boundaries
|
||
// to schedule work on. In this case we have to schedule it on the
|
||
// list itself.
|
||
// We don't have to traverse to the children of the list since
|
||
// the list will propagate the change when it rerenders.
|
||
scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
|
||
}
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === workInProgress) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === workInProgress) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
function findLastContentRow(firstChild) {
|
||
// This is going to find the last row among these children that is already
|
||
// showing content on the screen, as opposed to being in fallback state or
|
||
// new. If a row has multiple Suspense boundaries, any of them being in the
|
||
// fallback state, counts as the whole row being in a fallback state.
|
||
// Note that the "rows" will be workInProgress, but any nested children
|
||
// will still be current since we haven't rendered them yet. The mounted
|
||
// order may not be the same as the new order. We use the new order.
|
||
var row = firstChild;
|
||
var lastContentRow = null;
|
||
while (row !== null) {
|
||
var currentRow = row.alternate; // New rows can't be content rows.
|
||
if (currentRow !== null && findFirstSuspended(currentRow) === null) {
|
||
lastContentRow = row;
|
||
}
|
||
row = row.sibling;
|
||
}
|
||
return lastContentRow;
|
||
}
|
||
function validateRevealOrder(revealOrder) {
|
||
{
|
||
if (revealOrder !== undefined && revealOrder !== 'forwards' && revealOrder !== 'backwards' && revealOrder !== 'together' && !didWarnAboutRevealOrder[revealOrder]) {
|
||
didWarnAboutRevealOrder[revealOrder] = true;
|
||
if (typeof revealOrder === 'string') {
|
||
switch (revealOrder.toLowerCase()) {
|
||
case 'together':
|
||
case 'forwards':
|
||
case 'backwards':
|
||
{
|
||
error('"%s" is not a valid value for revealOrder on <SuspenseList />. ' + 'Use lowercase "%s" instead.', revealOrder, revealOrder.toLowerCase());
|
||
break;
|
||
}
|
||
case 'forward':
|
||
case 'backward':
|
||
{
|
||
error('"%s" is not a valid value for revealOrder on <SuspenseList />. ' + 'React uses the -s suffix in the spelling. Use "%ss" instead.', revealOrder, revealOrder.toLowerCase());
|
||
break;
|
||
}
|
||
default:
|
||
error('"%s" is not a supported revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder);
|
||
break;
|
||
}
|
||
}
|
||
else {
|
||
error('%s is not a supported value for revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function validateTailOptions(tailMode, revealOrder) {
|
||
{
|
||
if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) {
|
||
if (tailMode !== 'collapsed' && tailMode !== 'hidden') {
|
||
didWarnAboutTailOptions[tailMode] = true;
|
||
error('"%s" is not a supported value for tail on <SuspenseList />. ' + 'Did you mean "collapsed" or "hidden"?', tailMode);
|
||
}
|
||
else if (revealOrder !== 'forwards' && revealOrder !== 'backwards') {
|
||
didWarnAboutTailOptions[tailMode] = true;
|
||
error('<SuspenseList tail="%s" /> is only valid if revealOrder is ' + '"forwards" or "backwards". ' + 'Did you mean to specify revealOrder="forwards"?', tailMode);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function validateSuspenseListNestedChild(childSlot, index) {
|
||
{
|
||
var isAnArray = isArray(childSlot);
|
||
var isIterable = !isAnArray && typeof getIteratorFn(childSlot) === 'function';
|
||
if (isAnArray || isIterable) {
|
||
var type = isAnArray ? 'array' : 'iterable';
|
||
error('A nested %s was passed to row #%s in <SuspenseList />. Wrap it in ' + 'an additional SuspenseList to configure its revealOrder: ' + '<SuspenseList revealOrder=...> ... ' + '<SuspenseList revealOrder=...>{%s}</SuspenseList> ... ' + '</SuspenseList>', type, index, type);
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
function validateSuspenseListChildren(children, revealOrder) {
|
||
{
|
||
if ((revealOrder === 'forwards' || revealOrder === 'backwards') && children !== undefined && children !== null && children !== false) {
|
||
if (isArray(children)) {
|
||
for (var i = 0; i < children.length; i++) {
|
||
if (!validateSuspenseListNestedChild(children[i], i)) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
var iteratorFn = getIteratorFn(children);
|
||
if (typeof iteratorFn === 'function') {
|
||
var childrenIterator = iteratorFn.call(children);
|
||
if (childrenIterator) {
|
||
var step = childrenIterator.next();
|
||
var _i = 0;
|
||
for (; !step.done; step = childrenIterator.next()) {
|
||
if (!validateSuspenseListNestedChild(step.value, _i)) {
|
||
return;
|
||
}
|
||
_i++;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
error('A single row was passed to a <SuspenseList revealOrder="%s" />. ' + 'This is not useful since it needs multiple rows. ' + 'Did you mean to pass multiple children or an array?', revealOrder);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function initSuspenseListRenderState(workInProgress, isBackwards, tail, lastContentRow, tailMode) {
|
||
var renderState = workInProgress.memoizedState;
|
||
if (renderState === null) {
|
||
workInProgress.memoizedState = {
|
||
isBackwards: isBackwards,
|
||
rendering: null,
|
||
renderingStartTime: 0,
|
||
last: lastContentRow,
|
||
tail: tail,
|
||
tailMode: tailMode
|
||
};
|
||
}
|
||
else {
|
||
// We can reuse the existing object from previous renders.
|
||
renderState.isBackwards = isBackwards;
|
||
renderState.rendering = null;
|
||
renderState.renderingStartTime = 0;
|
||
renderState.last = lastContentRow;
|
||
renderState.tail = tail;
|
||
renderState.tailMode = tailMode;
|
||
}
|
||
} // This can end up rendering this component multiple passes.
|
||
// The first pass splits the children fibers into two sets. A head and tail.
|
||
// We first render the head. If anything is in fallback state, we do another
|
||
// pass through beginWork to rerender all children (including the tail) with
|
||
// the force suspend context. If the first render didn't have anything in
|
||
// in fallback state. Then we render each row in the tail one-by-one.
|
||
// That happens in the completeWork phase without going back to beginWork.
|
||
function updateSuspenseListComponent(current, workInProgress, renderLanes) {
|
||
var nextProps = workInProgress.pendingProps;
|
||
var revealOrder = nextProps.revealOrder;
|
||
var tailMode = nextProps.tail;
|
||
var newChildren = nextProps.children;
|
||
validateRevealOrder(revealOrder);
|
||
validateTailOptions(tailMode, revealOrder);
|
||
validateSuspenseListChildren(newChildren, revealOrder);
|
||
reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
||
var suspenseContext = suspenseStackCursor.current;
|
||
var shouldForceFallback = hasSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
||
if (shouldForceFallback) {
|
||
suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
||
workInProgress.flags |= DidCapture;
|
||
}
|
||
else {
|
||
var didSuspendBefore = current !== null && (current.flags & DidCapture) !== NoFlags;
|
||
if (didSuspendBefore) {
|
||
// If we previously forced a fallback, we need to schedule work
|
||
// on any nested boundaries to let them know to try to render
|
||
// again. This is the same as context updating.
|
||
propagateSuspenseContextChange(workInProgress, workInProgress.child, renderLanes);
|
||
}
|
||
suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
||
}
|
||
pushSuspenseContext(workInProgress, suspenseContext);
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
// In legacy mode, SuspenseList doesn't work so we just
|
||
// use make it a noop by treating it as the default revealOrder.
|
||
workInProgress.memoizedState = null;
|
||
}
|
||
else {
|
||
switch (revealOrder) {
|
||
case 'forwards':
|
||
{
|
||
var lastContentRow = findLastContentRow(workInProgress.child);
|
||
var tail;
|
||
if (lastContentRow === null) {
|
||
// The whole list is part of the tail.
|
||
// TODO: We could fast path by just rendering the tail now.
|
||
tail = workInProgress.child;
|
||
workInProgress.child = null;
|
||
}
|
||
else {
|
||
// Disconnect the tail rows after the content row.
|
||
// We're going to render them separately later.
|
||
tail = lastContentRow.sibling;
|
||
lastContentRow.sibling = null;
|
||
}
|
||
initSuspenseListRenderState(workInProgress, false, // isBackwards
|
||
tail, lastContentRow, tailMode);
|
||
break;
|
||
}
|
||
case 'backwards':
|
||
{
|
||
// We're going to find the first row that has existing content.
|
||
// At the same time we're going to reverse the list of everything
|
||
// we pass in the meantime. That's going to be our tail in reverse
|
||
// order.
|
||
var _tail = null;
|
||
var row = workInProgress.child;
|
||
workInProgress.child = null;
|
||
while (row !== null) {
|
||
var currentRow = row.alternate; // New rows can't be content rows.
|
||
if (currentRow !== null && findFirstSuspended(currentRow) === null) {
|
||
// This is the beginning of the main content.
|
||
workInProgress.child = row;
|
||
break;
|
||
}
|
||
var nextRow = row.sibling;
|
||
row.sibling = _tail;
|
||
_tail = row;
|
||
row = nextRow;
|
||
} // TODO: If workInProgress.child is null, we can continue on the tail immediately.
|
||
initSuspenseListRenderState(workInProgress, true, // isBackwards
|
||
_tail, null, // last
|
||
tailMode);
|
||
break;
|
||
}
|
||
case 'together':
|
||
{
|
||
initSuspenseListRenderState(workInProgress, false, // isBackwards
|
||
null, // tail
|
||
null, // last
|
||
undefined);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
// The default reveal order is the same as not having
|
||
// a boundary.
|
||
workInProgress.memoizedState = null;
|
||
}
|
||
}
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
function updatePortalComponent(current, workInProgress, renderLanes) {
|
||
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
||
var nextChildren = workInProgress.pendingProps;
|
||
if (current === null) {
|
||
// Portals are special because we don't append the children during mount
|
||
// but at commit. Therefore we need to track insertions which the normal
|
||
// flow doesn't do during mount. This doesn't happen at the root because
|
||
// the root always starts with a "current" with a null child.
|
||
// TODO: Consider unifying this with how the root works.
|
||
workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes);
|
||
}
|
||
else {
|
||
reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
var hasWarnedAboutUsingNoValuePropOnContextProvider = false;
|
||
function updateContextProvider(current, workInProgress, renderLanes) {
|
||
var providerType = workInProgress.type;
|
||
var context = providerType._context;
|
||
var newProps = workInProgress.pendingProps;
|
||
var oldProps = workInProgress.memoizedProps;
|
||
var newValue = newProps.value;
|
||
{
|
||
if (!('value' in newProps)) {
|
||
if (!hasWarnedAboutUsingNoValuePropOnContextProvider) {
|
||
hasWarnedAboutUsingNoValuePropOnContextProvider = true;
|
||
error('The `value` prop is required for the `<Context.Provider>`. Did you misspell it or forget to pass it?');
|
||
}
|
||
}
|
||
var providerPropTypes = workInProgress.type.propTypes;
|
||
if (providerPropTypes) {
|
||
checkPropTypes(providerPropTypes, newProps, 'prop', 'Context.Provider');
|
||
}
|
||
}
|
||
pushProvider(workInProgress, context, newValue);
|
||
{
|
||
if (oldProps !== null) {
|
||
var oldValue = oldProps.value;
|
||
if (objectIs(oldValue, newValue)) {
|
||
// No change. Bailout early if children are the same.
|
||
if (oldProps.children === newProps.children && !hasContextChanged()) {
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
}
|
||
else {
|
||
// The context value changed. Search for matching consumers and schedule
|
||
// them to update.
|
||
propagateContextChange(workInProgress, context, renderLanes);
|
||
}
|
||
}
|
||
}
|
||
var newChildren = newProps.children;
|
||
reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
var hasWarnedAboutUsingContextAsConsumer = false;
|
||
function updateContextConsumer(current, workInProgress, renderLanes) {
|
||
var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In
|
||
// DEV mode, we create a separate object for Context.Consumer that acts
|
||
// like a proxy to Context. This proxy object adds unnecessary code in PROD
|
||
// so we use the old behaviour (Context.Consumer references Context) to
|
||
// reduce size and overhead. The separate object references context via
|
||
// a property called "_context", which also gives us the ability to check
|
||
// in DEV mode if this property exists or not and warn if it does not.
|
||
{
|
||
if (context._context === undefined) {
|
||
// This may be because it's a Context (rather than a Consumer).
|
||
// Or it may be because it's older React where they're the same thing.
|
||
// We only want to warn if we're sure it's a new React.
|
||
if (context !== context.Consumer) {
|
||
if (!hasWarnedAboutUsingContextAsConsumer) {
|
||
hasWarnedAboutUsingContextAsConsumer = true;
|
||
error('Rendering <Context> directly is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
context = context._context;
|
||
}
|
||
}
|
||
var newProps = workInProgress.pendingProps;
|
||
var render = newProps.children;
|
||
{
|
||
if (typeof render !== 'function') {
|
||
error('A context consumer was rendered with multiple children, or a child ' + "that isn't a function. A context consumer expects a single child " + 'that is a function. If you did pass a function, make sure there ' + 'is no trailing or leading whitespace around it.');
|
||
}
|
||
}
|
||
prepareToReadContext(workInProgress, renderLanes);
|
||
var newValue = readContext(context);
|
||
{
|
||
markComponentRenderStarted(workInProgress);
|
||
}
|
||
var newChildren;
|
||
{
|
||
ReactCurrentOwner$1.current = workInProgress;
|
||
setIsRendering(true);
|
||
newChildren = render(newValue);
|
||
setIsRendering(false);
|
||
}
|
||
{
|
||
markComponentRenderStopped();
|
||
} // React DevTools reads this flag.
|
||
workInProgress.flags |= PerformedWork;
|
||
reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
||
return workInProgress.child;
|
||
}
|
||
function markWorkInProgressReceivedUpdate() {
|
||
didReceiveUpdate = true;
|
||
}
|
||
function resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress) {
|
||
if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
if (current !== null) {
|
||
// A lazy component only mounts if it suspended inside a non-
|
||
// concurrent tree, in an inconsistent state. We want to treat it like
|
||
// a new mount, even though an empty version of it already committed.
|
||
// Disconnect the alternate pointers.
|
||
current.alternate = null;
|
||
workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect
|
||
workInProgress.flags |= Placement;
|
||
}
|
||
}
|
||
}
|
||
function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
|
||
if (current !== null) {
|
||
// Reuse previous dependencies
|
||
workInProgress.dependencies = current.dependencies;
|
||
}
|
||
{
|
||
// Don't update "base" render times for bailouts.
|
||
stopProfilerTimerIfRunning();
|
||
}
|
||
markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work.
|
||
if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
|
||
// The children don't have any work either. We can skip them.
|
||
// TODO: Once we add back resuming, we should check if the children are
|
||
// a work-in-progress set. If so, we need to transfer their effects.
|
||
{
|
||
return null;
|
||
}
|
||
} // This fiber doesn't have work, but its subtree does. Clone the child
|
||
// fibers and continue.
|
||
cloneChildFibers(current, workInProgress);
|
||
return workInProgress.child;
|
||
}
|
||
function remountFiber(current, oldWorkInProgress, newWorkInProgress) {
|
||
{
|
||
var returnFiber = oldWorkInProgress.return;
|
||
if (returnFiber === null) {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
throw new Error('Cannot swap the root fiber.');
|
||
} // Disconnect from the old current.
|
||
// It will get deleted.
|
||
current.alternate = null;
|
||
oldWorkInProgress.alternate = null; // Connect to the new tree.
|
||
newWorkInProgress.index = oldWorkInProgress.index;
|
||
newWorkInProgress.sibling = oldWorkInProgress.sibling;
|
||
newWorkInProgress.return = oldWorkInProgress.return;
|
||
newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it.
|
||
if (oldWorkInProgress === returnFiber.child) {
|
||
returnFiber.child = newWorkInProgress;
|
||
}
|
||
else {
|
||
var prevSibling = returnFiber.child;
|
||
if (prevSibling === null) {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
throw new Error('Expected parent to have a child.');
|
||
}
|
||
while (prevSibling.sibling !== oldWorkInProgress) {
|
||
prevSibling = prevSibling.sibling;
|
||
if (prevSibling === null) {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
throw new Error('Expected to find the previous sibling.');
|
||
}
|
||
}
|
||
prevSibling.sibling = newWorkInProgress;
|
||
} // Delete the old fiber and place the new one.
|
||
// Since the old fiber is disconnected, we have to schedule it manually.
|
||
var deletions = returnFiber.deletions;
|
||
if (deletions === null) {
|
||
returnFiber.deletions = [current];
|
||
returnFiber.flags |= ChildDeletion;
|
||
}
|
||
else {
|
||
deletions.push(current);
|
||
}
|
||
newWorkInProgress.flags |= Placement; // Restart work from the new fiber.
|
||
return newWorkInProgress;
|
||
}
|
||
}
|
||
function checkScheduledUpdateOrContext(current, renderLanes) {
|
||
// Before performing an early bailout, we must check if there are pending
|
||
// updates or context.
|
||
var updateLanes = current.lanes;
|
||
if (includesSomeLane(updateLanes, renderLanes)) {
|
||
return true;
|
||
} // No pending update, but because context is propagated lazily, we need
|
||
return false;
|
||
}
|
||
function attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes) {
|
||
// This fiber does not have any pending work. Bailout without entering
|
||
// the begin phase. There's still some bookkeeping we that needs to be done
|
||
// in this optimized path, mostly pushing stuff onto the stack.
|
||
switch (workInProgress.tag) {
|
||
case HostRoot:
|
||
pushHostRootContext(workInProgress);
|
||
var root = workInProgress.stateNode;
|
||
resetHydrationState();
|
||
break;
|
||
case HostComponent:
|
||
pushHostContext(workInProgress);
|
||
break;
|
||
case ClassComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
if (isContextProvider(Component)) {
|
||
pushContextProvider(workInProgress);
|
||
}
|
||
break;
|
||
}
|
||
case HostPortal:
|
||
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
||
break;
|
||
case ContextProvider:
|
||
{
|
||
var newValue = workInProgress.memoizedProps.value;
|
||
var context = workInProgress.type._context;
|
||
pushProvider(workInProgress, context, newValue);
|
||
break;
|
||
}
|
||
case Profiler:
|
||
{
|
||
// Profiler should only call onRender when one of its descendants actually rendered.
|
||
var hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
|
||
if (hasChildWork) {
|
||
workInProgress.flags |= Update;
|
||
}
|
||
{
|
||
// Reset effect durations for the next eventual effect phase.
|
||
// These are reset during render to allow the DevTools commit hook a chance to read them,
|
||
var stateNode = workInProgress.stateNode;
|
||
stateNode.effectDuration = 0;
|
||
stateNode.passiveEffectDuration = 0;
|
||
}
|
||
}
|
||
break;
|
||
case SuspenseComponent:
|
||
{
|
||
var state = workInProgress.memoizedState;
|
||
if (state !== null) {
|
||
if (state.dehydrated !== null) {
|
||
pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // We know that this component will suspend again because if it has
|
||
// been unsuspended it has committed as a resolved Suspense component.
|
||
// If it needs to be retried, it should have work scheduled on it.
|
||
workInProgress.flags |= DidCapture; // We should never render the children of a dehydrated boundary until we
|
||
// upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
|
||
return null;
|
||
} // If this boundary is currently timed out, we need to decide
|
||
// whether to retry the primary children, or to skip over it and
|
||
// go straight to the fallback. Check the priority of the primary
|
||
// child fragment.
|
||
var primaryChildFragment = workInProgress.child;
|
||
var primaryChildLanes = primaryChildFragment.childLanes;
|
||
if (includesSomeLane(renderLanes, primaryChildLanes)) {
|
||
// The primary children have pending work. Use the normal path
|
||
// to attempt to render the primary children again.
|
||
return updateSuspenseComponent(current, workInProgress, renderLanes);
|
||
}
|
||
else {
|
||
// The primary child fragment does not have pending work marked
|
||
// on it
|
||
pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // The primary children do not have pending work with sufficient
|
||
// priority. Bailout.
|
||
var child = bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
if (child !== null) {
|
||
// The fallback children have pending work. Skip over the
|
||
// primary children and work on the fallback.
|
||
return child.sibling;
|
||
}
|
||
else {
|
||
// Note: We can return `null` here because we already checked
|
||
// whether there were nested context consumers, via the call to
|
||
// `bailoutOnAlreadyFinishedWork` above.
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current));
|
||
}
|
||
break;
|
||
}
|
||
case SuspenseListComponent:
|
||
{
|
||
var didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
|
||
var _hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
|
||
if (didSuspendBefore) {
|
||
if (_hasChildWork) {
|
||
// If something was in fallback state last time, and we have all the
|
||
// same children then we're still in progressive loading state.
|
||
// Something might get unblocked by state updates or retries in the
|
||
// tree which will affect the tail. So we need to use the normal
|
||
// path to compute the correct tail.
|
||
return updateSuspenseListComponent(current, workInProgress, renderLanes);
|
||
} // If none of the children had any work, that means that none of
|
||
// them got retried so they'll still be blocked in the same way
|
||
// as before. We can fast bail out.
|
||
workInProgress.flags |= DidCapture;
|
||
} // If nothing suspended before and we're rendering the same children,
|
||
// then the tail doesn't matter. Anything new that suspends will work
|
||
// in the "together" mode, so we can continue from the state we had.
|
||
var renderState = workInProgress.memoizedState;
|
||
if (renderState !== null) {
|
||
// Reset to the "together" mode in case we've started a different
|
||
// update in the past but didn't complete it.
|
||
renderState.rendering = null;
|
||
renderState.tail = null;
|
||
renderState.lastEffect = null;
|
||
}
|
||
pushSuspenseContext(workInProgress, suspenseStackCursor.current);
|
||
if (_hasChildWork) {
|
||
break;
|
||
}
|
||
else {
|
||
// If none of the children had any work, that means that none of
|
||
// them got retried so they'll still be blocked in the same way
|
||
// as before. We can fast bail out.
|
||
return null;
|
||
}
|
||
}
|
||
case OffscreenComponent:
|
||
case LegacyHiddenComponent:
|
||
{
|
||
// Need to check if the tree still needs to be deferred. This is
|
||
// almost identical to the logic used in the normal update path,
|
||
// so we'll just enter that. The only difference is we'll bail out
|
||
// at the next level instead of this one, because the child props
|
||
// have not changed. Which is fine.
|
||
// TODO: Probably should refactor `beginWork` to split the bailout
|
||
// path from the normal path. I'm tempted to do a labeled break here
|
||
// but I won't :)
|
||
workInProgress.lanes = NoLanes;
|
||
return updateOffscreenComponent(current, workInProgress, renderLanes);
|
||
}
|
||
}
|
||
return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
||
}
|
||
function beginWork(current, workInProgress, renderLanes) {
|
||
{
|
||
if (workInProgress._debugNeedsRemount && current !== null) {
|
||
// This will restart the begin phase with a new fiber.
|
||
return remountFiber(current, workInProgress, createFiberFromTypeAndProps(workInProgress.type, workInProgress.key, workInProgress.pendingProps, workInProgress._debugOwner || null, workInProgress.mode, workInProgress.lanes));
|
||
}
|
||
}
|
||
if (current !== null) {
|
||
var oldProps = current.memoizedProps;
|
||
var newProps = workInProgress.pendingProps;
|
||
if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload:
|
||
workInProgress.type !== current.type)) {
|
||
// If props or context changed, mark the fiber as having performed work.
|
||
// This may be unset if the props are determined to be equal later (memo).
|
||
didReceiveUpdate = true;
|
||
}
|
||
else {
|
||
// Neither props nor legacy context changes. Check if there's a pending
|
||
// update or context change.
|
||
var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);
|
||
if (!hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there
|
||
// may not be work scheduled on `current`, so we check for this flag.
|
||
(workInProgress.flags & DidCapture) === NoFlags) {
|
||
// No pending updates or context. Bail out now.
|
||
didReceiveUpdate = false;
|
||
return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);
|
||
}
|
||
if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
|
||
// This is a special case that only exists for legacy mode.
|
||
// See https://github.com/facebook/react/pull/19216.
|
||
didReceiveUpdate = true;
|
||
}
|
||
else {
|
||
// An update was scheduled on this fiber, but there are no new props
|
||
// nor legacy context. Set this to false. If an update queue or context
|
||
// consumer produces a changed value, it will set this to true. Otherwise,
|
||
// the component will assume the children have not changed and bail out.
|
||
didReceiveUpdate = false;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
didReceiveUpdate = false;
|
||
if (getIsHydrating() && isForkedChild(workInProgress)) {
|
||
// Check if this child belongs to a list of muliple children in
|
||
// its parent.
|
||
//
|
||
// In a true multi-threaded implementation, we would render children on
|
||
// parallel threads. This would represent the beginning of a new render
|
||
// thread for this subtree.
|
||
//
|
||
// We only use this for id generation during hydration, which is why the
|
||
// logic is located in this special branch.
|
||
var slotIndex = workInProgress.index;
|
||
var numberOfForks = getForksAtLevel();
|
||
pushTreeId(workInProgress, numberOfForks, slotIndex);
|
||
}
|
||
} // Before entering the begin phase, clear pending update priority.
|
||
// TODO: This assumes that we're about to evaluate the component and process
|
||
// the update queue. However, there's an exception: SimpleMemoComponent
|
||
// sometimes bails out later in the begin phase. This indicates that we should
|
||
// move this assignment out of the common path and into each branch.
|
||
workInProgress.lanes = NoLanes;
|
||
switch (workInProgress.tag) {
|
||
case IndeterminateComponent:
|
||
{
|
||
return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes);
|
||
}
|
||
case LazyComponent:
|
||
{
|
||
var elementType = workInProgress.elementType;
|
||
return mountLazyComponent(current, workInProgress, elementType, renderLanes);
|
||
}
|
||
case FunctionComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
var unresolvedProps = workInProgress.pendingProps;
|
||
var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps);
|
||
return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes);
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var _Component = workInProgress.type;
|
||
var _unresolvedProps = workInProgress.pendingProps;
|
||
var _resolvedProps = workInProgress.elementType === _Component ? _unresolvedProps : resolveDefaultProps(_Component, _unresolvedProps);
|
||
return updateClassComponent(current, workInProgress, _Component, _resolvedProps, renderLanes);
|
||
}
|
||
case HostRoot:
|
||
return updateHostRoot(current, workInProgress, renderLanes);
|
||
case HostComponent:
|
||
return updateHostComponent(current, workInProgress, renderLanes);
|
||
case HostText:
|
||
return updateHostText(current, workInProgress);
|
||
case SuspenseComponent:
|
||
return updateSuspenseComponent(current, workInProgress, renderLanes);
|
||
case HostPortal:
|
||
return updatePortalComponent(current, workInProgress, renderLanes);
|
||
case ForwardRef:
|
||
{
|
||
var type = workInProgress.type;
|
||
var _unresolvedProps2 = workInProgress.pendingProps;
|
||
var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
|
||
return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes);
|
||
}
|
||
case Fragment:
|
||
return updateFragment(current, workInProgress, renderLanes);
|
||
case Mode:
|
||
return updateMode(current, workInProgress, renderLanes);
|
||
case Profiler:
|
||
return updateProfiler(current, workInProgress, renderLanes);
|
||
case ContextProvider:
|
||
return updateContextProvider(current, workInProgress, renderLanes);
|
||
case ContextConsumer:
|
||
return updateContextConsumer(current, workInProgress, renderLanes);
|
||
case MemoComponent:
|
||
{
|
||
var _type2 = workInProgress.type;
|
||
var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.
|
||
var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
|
||
{
|
||
if (workInProgress.type !== workInProgress.elementType) {
|
||
var outerPropTypes = _type2.propTypes;
|
||
if (outerPropTypes) {
|
||
checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only
|
||
'prop', getComponentNameFromType(_type2));
|
||
}
|
||
}
|
||
}
|
||
_resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
|
||
return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, renderLanes);
|
||
}
|
||
case SimpleMemoComponent:
|
||
{
|
||
return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, renderLanes);
|
||
}
|
||
case IncompleteClassComponent:
|
||
{
|
||
var _Component2 = workInProgress.type;
|
||
var _unresolvedProps4 = workInProgress.pendingProps;
|
||
var _resolvedProps4 = workInProgress.elementType === _Component2 ? _unresolvedProps4 : resolveDefaultProps(_Component2, _unresolvedProps4);
|
||
return mountIncompleteClassComponent(current, workInProgress, _Component2, _resolvedProps4, renderLanes);
|
||
}
|
||
case SuspenseListComponent:
|
||
{
|
||
return updateSuspenseListComponent(current, workInProgress, renderLanes);
|
||
}
|
||
case ScopeComponent:
|
||
{
|
||
break;
|
||
}
|
||
case OffscreenComponent:
|
||
{
|
||
return updateOffscreenComponent(current, workInProgress, renderLanes);
|
||
}
|
||
}
|
||
throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.');
|
||
}
|
||
function markUpdate(workInProgress) {
|
||
// Tag the fiber with an update effect. This turns a Placement into
|
||
// a PlacementAndUpdate.
|
||
workInProgress.flags |= Update;
|
||
}
|
||
function markRef$1(workInProgress) {
|
||
workInProgress.flags |= Ref;
|
||
{
|
||
workInProgress.flags |= RefStatic;
|
||
}
|
||
}
|
||
function hadNoMutationsEffects(current, completedWork) {
|
||
var didBailout = current !== null && current.child === completedWork.child;
|
||
if (didBailout) {
|
||
return true;
|
||
}
|
||
if ((completedWork.flags & ChildDeletion) !== NoFlags) {
|
||
return false;
|
||
} // TODO: If we move the `hadNoMutationsEffects` call after `bubbleProperties`
|
||
// then we only have to check the `completedWork.subtreeFlags`.
|
||
var child = completedWork.child;
|
||
while (child !== null) {
|
||
if ((child.flags & MutationMask) !== NoFlags || (child.subtreeFlags & MutationMask) !== NoFlags) {
|
||
return false;
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
return true;
|
||
}
|
||
var appendAllChildren;
|
||
var updateHostContainer;
|
||
var updateHostComponent$1;
|
||
var updateHostText$1;
|
||
if (supportsMutation) {
|
||
// Mutation mode
|
||
appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
|
||
// We only have the top Fiber that was created but we need recurse down its
|
||
// children to find all the terminal nodes.
|
||
var node = workInProgress.child;
|
||
while (node !== null) {
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
appendInitialChild(parent, node.stateNode);
|
||
}
|
||
else if (node.tag === HostPortal)
|
||
;
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === workInProgress) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === workInProgress) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
};
|
||
updateHostContainer = function (current, workInProgress) {
|
||
};
|
||
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
|
||
// If we have an alternate, that means this is an update and we need to
|
||
// schedule a side-effect to do the updates.
|
||
var oldProps = current.memoizedProps;
|
||
if (oldProps === newProps) {
|
||
// In mutation mode, this is sufficient for a bailout because
|
||
// we won't touch this node even if children changed.
|
||
return;
|
||
} // If we get updated because one of our children updated, we don't
|
||
// have newProps so we'll have to reuse them.
|
||
// TODO: Split the update API as separate for the props vs. children.
|
||
// Even better would be if children weren't special cased at all tho.
|
||
var instance = workInProgress.stateNode;
|
||
var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host
|
||
// component is hitting the resume path. Figure out why. Possibly
|
||
// related to `hidden`.
|
||
var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); // TODO: Type this specific to this type of component.
|
||
workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
|
||
// is a new ref we mark this as an update. All the work is done in commitWork.
|
||
if (updatePayload) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
};
|
||
updateHostText$1 = function (current, workInProgress, oldText, newText) {
|
||
// If the text differs, mark it as an update. All the work in done in commitWork.
|
||
if (oldText !== newText) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
};
|
||
}
|
||
else if (supportsPersistence) {
|
||
// Persistent host tree mode
|
||
appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
|
||
// We only have the top Fiber that was created but we need recurse down its
|
||
// children to find all the terminal nodes.
|
||
var node = workInProgress.child;
|
||
while (node !== null) {
|
||
// eslint-disable-next-line no-labels
|
||
if (node.tag === HostComponent) {
|
||
var instance = node.stateNode;
|
||
if (needsVisibilityToggle && isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
var props = node.memoizedProps;
|
||
var type = node.type;
|
||
instance = cloneHiddenInstance(instance, type, props, node);
|
||
}
|
||
appendInitialChild(parent, instance);
|
||
}
|
||
else if (node.tag === HostText) {
|
||
var _instance = node.stateNode;
|
||
if (needsVisibilityToggle && isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
var text = node.memoizedProps;
|
||
_instance = cloneHiddenTextInstance(_instance, text, node);
|
||
}
|
||
appendInitialChild(parent, _instance);
|
||
}
|
||
else if (node.tag === HostPortal)
|
||
;
|
||
else if (node.tag === OffscreenComponent && node.memoizedState !== null) {
|
||
// The children in this boundary are hidden. Toggle their visibility
|
||
// before appending.
|
||
var child = node.child;
|
||
if (child !== null) {
|
||
child.return = node;
|
||
}
|
||
appendAllChildren(parent, node, true, true);
|
||
}
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
} // $FlowFixMe This is correct but Flow is confused by the labeled break.
|
||
node = node;
|
||
if (node === workInProgress) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === workInProgress) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}; // An unfortunate fork of appendAllChildren because we have two different parent types.
|
||
var appendAllChildrenToContainer = function (containerChildSet, workInProgress, needsVisibilityToggle, isHidden) {
|
||
// We only have the top Fiber that was created but we need recurse down its
|
||
// children to find all the terminal nodes.
|
||
var node = workInProgress.child;
|
||
while (node !== null) {
|
||
// eslint-disable-next-line no-labels
|
||
if (node.tag === HostComponent) {
|
||
var instance = node.stateNode;
|
||
if (needsVisibilityToggle && isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
var props = node.memoizedProps;
|
||
var type = node.type;
|
||
instance = cloneHiddenInstance(instance, type, props, node);
|
||
}
|
||
appendChildToContainerChildSet(containerChildSet, instance);
|
||
}
|
||
else if (node.tag === HostText) {
|
||
var _instance2 = node.stateNode;
|
||
if (needsVisibilityToggle && isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
var text = node.memoizedProps;
|
||
_instance2 = cloneHiddenTextInstance(_instance2, text, node);
|
||
}
|
||
appendChildToContainerChildSet(containerChildSet, _instance2);
|
||
}
|
||
else if (node.tag === HostPortal)
|
||
;
|
||
else if (node.tag === OffscreenComponent && node.memoizedState !== null) {
|
||
// The children in this boundary are hidden. Toggle their visibility
|
||
// before appending.
|
||
var child = node.child;
|
||
if (child !== null) {
|
||
child.return = node;
|
||
}
|
||
appendAllChildrenToContainer(containerChildSet, node, true, true);
|
||
}
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
} // $FlowFixMe This is correct but Flow is confused by the labeled break.
|
||
node = node;
|
||
if (node === workInProgress) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === workInProgress) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
};
|
||
updateHostContainer = function (current, workInProgress) {
|
||
var portalOrRoot = workInProgress.stateNode;
|
||
var childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
|
||
if (childrenUnchanged)
|
||
;
|
||
else {
|
||
var container = portalOrRoot.containerInfo;
|
||
var newChildSet = createContainerChildSet(container); // If children might have changed, we have to add them all to the set.
|
||
appendAllChildrenToContainer(newChildSet, workInProgress, false, false);
|
||
portalOrRoot.pendingChildren = newChildSet; // Schedule an update on the container to swap out the container.
|
||
markUpdate(workInProgress);
|
||
finalizeContainerChildren(container, newChildSet);
|
||
}
|
||
};
|
||
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
|
||
var currentInstance = current.stateNode;
|
||
var oldProps = current.memoizedProps; // If there are no effects associated with this node, then none of our children had any updates.
|
||
// This guarantees that we can reuse all of them.
|
||
var childrenUnchanged = hadNoMutationsEffects(current, workInProgress);
|
||
if (childrenUnchanged && oldProps === newProps) {
|
||
// No changes, just reuse the existing instance.
|
||
// Note that this might release a previous clone.
|
||
workInProgress.stateNode = currentInstance;
|
||
return;
|
||
}
|
||
var recyclableInstance = workInProgress.stateNode;
|
||
var currentHostContext = getHostContext();
|
||
var updatePayload = null;
|
||
if (oldProps !== newProps) {
|
||
updatePayload = prepareUpdate(recyclableInstance, type, oldProps, newProps, rootContainerInstance, currentHostContext);
|
||
}
|
||
if (childrenUnchanged && updatePayload === null) {
|
||
// No changes, just reuse the existing instance.
|
||
// Note that this might release a previous clone.
|
||
workInProgress.stateNode = currentInstance;
|
||
return;
|
||
}
|
||
var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance);
|
||
if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance, currentHostContext)) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
workInProgress.stateNode = newInstance;
|
||
if (childrenUnchanged) {
|
||
// If there are no other effects in this tree, we need to flag this node as having one.
|
||
// Even though we're not going to use it for anything.
|
||
// Otherwise parents won't know that there are new children to propagate upwards.
|
||
markUpdate(workInProgress);
|
||
}
|
||
else {
|
||
// If children might have changed, we have to add them all to the set.
|
||
appendAllChildren(newInstance, workInProgress, false, false);
|
||
}
|
||
};
|
||
updateHostText$1 = function (current, workInProgress, oldText, newText) {
|
||
if (oldText !== newText) {
|
||
// If the text content differs, we'll create a new text instance for it.
|
||
var rootContainerInstance = getRootHostContainer();
|
||
var currentHostContext = getHostContext();
|
||
workInProgress.stateNode = createTextInstance(newText, rootContainerInstance, currentHostContext, workInProgress); // We'll have to mark it as having an effect, even though we won't use the effect for anything.
|
||
// This lets the parents know that at least one of their children has changed.
|
||
markUpdate(workInProgress);
|
||
}
|
||
else {
|
||
workInProgress.stateNode = current.stateNode;
|
||
}
|
||
};
|
||
}
|
||
else {
|
||
// No host operations
|
||
updateHostContainer = function (current, workInProgress) {
|
||
};
|
||
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
|
||
};
|
||
updateHostText$1 = function (current, workInProgress, oldText, newText) {
|
||
};
|
||
}
|
||
function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {
|
||
if (getIsHydrating()) {
|
||
// If we're hydrating, we should consume as many items as we can
|
||
// so we don't leave any behind.
|
||
return;
|
||
}
|
||
switch (renderState.tailMode) {
|
||
case 'hidden':
|
||
{
|
||
// Any insertions at the end of the tail list after this point
|
||
// should be invisible. If there are already mounted boundaries
|
||
// anything before them are not considered for collapsing.
|
||
// Therefore we need to go through the whole tail to find if
|
||
// there are any.
|
||
var tailNode = renderState.tail;
|
||
var lastTailNode = null;
|
||
while (tailNode !== null) {
|
||
if (tailNode.alternate !== null) {
|
||
lastTailNode = tailNode;
|
||
}
|
||
tailNode = tailNode.sibling;
|
||
} // Next we're simply going to delete all insertions after the
|
||
// last rendered item.
|
||
if (lastTailNode === null) {
|
||
// All remaining items in the tail are insertions.
|
||
renderState.tail = null;
|
||
}
|
||
else {
|
||
// Detach the insertion after the last node that was already
|
||
// inserted.
|
||
lastTailNode.sibling = null;
|
||
}
|
||
break;
|
||
}
|
||
case 'collapsed':
|
||
{
|
||
// Any insertions at the end of the tail list after this point
|
||
// should be invisible. If there are already mounted boundaries
|
||
// anything before them are not considered for collapsing.
|
||
// Therefore we need to go through the whole tail to find if
|
||
// there are any.
|
||
var _tailNode = renderState.tail;
|
||
var _lastTailNode = null;
|
||
while (_tailNode !== null) {
|
||
if (_tailNode.alternate !== null) {
|
||
_lastTailNode = _tailNode;
|
||
}
|
||
_tailNode = _tailNode.sibling;
|
||
} // Next we're simply going to delete all insertions after the
|
||
// last rendered item.
|
||
if (_lastTailNode === null) {
|
||
// All remaining items in the tail are insertions.
|
||
if (!hasRenderedATailFallback && renderState.tail !== null) {
|
||
// We suspended during the head. We want to show at least one
|
||
// row at the tail. So we'll keep on and cut off the rest.
|
||
renderState.tail.sibling = null;
|
||
}
|
||
else {
|
||
renderState.tail = null;
|
||
}
|
||
}
|
||
else {
|
||
// Detach the insertion after the last node that was already
|
||
// inserted.
|
||
_lastTailNode.sibling = null;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function bubbleProperties(completedWork) {
|
||
var didBailout = completedWork.alternate !== null && completedWork.alternate.child === completedWork.child;
|
||
var newChildLanes = NoLanes;
|
||
var subtreeFlags = NoFlags;
|
||
if (!didBailout) {
|
||
// Bubble up the earliest expiration time.
|
||
if ((completedWork.mode & ProfileMode) !== NoMode) {
|
||
// In profiling mode, resetChildExpirationTime is also used to reset
|
||
// profiler durations.
|
||
var actualDuration = completedWork.actualDuration;
|
||
var treeBaseDuration = completedWork.selfBaseDuration;
|
||
var child = completedWork.child;
|
||
while (child !== null) {
|
||
newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes));
|
||
subtreeFlags |= child.subtreeFlags;
|
||
subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will
|
||
// only be updated if work is done on the fiber (i.e. it doesn't bailout).
|
||
// When work is done, it should bubble to the parent's actualDuration. If
|
||
// the fiber has not been cloned though, (meaning no work was done), then
|
||
// this value will reflect the amount of time spent working on a previous
|
||
// render. In that case it should not bubble. We determine whether it was
|
||
// cloned by comparing the child pointer.
|
||
actualDuration += child.actualDuration;
|
||
treeBaseDuration += child.treeBaseDuration;
|
||
child = child.sibling;
|
||
}
|
||
completedWork.actualDuration = actualDuration;
|
||
completedWork.treeBaseDuration = treeBaseDuration;
|
||
}
|
||
else {
|
||
var _child = completedWork.child;
|
||
while (_child !== null) {
|
||
newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes));
|
||
subtreeFlags |= _child.subtreeFlags;
|
||
subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code
|
||
// smell because it assumes the commit phase is never concurrent with
|
||
// the render phase. Will address during refactor to alternate model.
|
||
_child.return = completedWork;
|
||
_child = _child.sibling;
|
||
}
|
||
}
|
||
completedWork.subtreeFlags |= subtreeFlags;
|
||
}
|
||
else {
|
||
// Bubble up the earliest expiration time.
|
||
if ((completedWork.mode & ProfileMode) !== NoMode) {
|
||
// In profiling mode, resetChildExpirationTime is also used to reset
|
||
// profiler durations.
|
||
var _treeBaseDuration = completedWork.selfBaseDuration;
|
||
var _child2 = completedWork.child;
|
||
while (_child2 !== null) {
|
||
newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child2.lanes, _child2.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to,
|
||
// so we should bubble those up even during a bailout. All the other
|
||
// flags have a lifetime only of a single render + commit, so we should
|
||
// ignore them.
|
||
subtreeFlags |= _child2.subtreeFlags & StaticMask;
|
||
subtreeFlags |= _child2.flags & StaticMask;
|
||
_treeBaseDuration += _child2.treeBaseDuration;
|
||
_child2 = _child2.sibling;
|
||
}
|
||
completedWork.treeBaseDuration = _treeBaseDuration;
|
||
}
|
||
else {
|
||
var _child3 = completedWork.child;
|
||
while (_child3 !== null) {
|
||
newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child3.lanes, _child3.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to,
|
||
// so we should bubble those up even during a bailout. All the other
|
||
// flags have a lifetime only of a single render + commit, so we should
|
||
// ignore them.
|
||
subtreeFlags |= _child3.subtreeFlags & StaticMask;
|
||
subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code
|
||
// smell because it assumes the commit phase is never concurrent with
|
||
// the render phase. Will address during refactor to alternate model.
|
||
_child3.return = completedWork;
|
||
_child3 = _child3.sibling;
|
||
}
|
||
}
|
||
completedWork.subtreeFlags |= subtreeFlags;
|
||
}
|
||
completedWork.childLanes = newChildLanes;
|
||
return didBailout;
|
||
}
|
||
function completeDehydratedSuspenseBoundary(current, workInProgress, nextState) {
|
||
if (hasUnhydratedTailNodes() && (workInProgress.mode & ConcurrentMode) !== NoMode && (workInProgress.flags & DidCapture) === NoFlags) {
|
||
warnIfUnhydratedTailNodes(workInProgress);
|
||
resetHydrationState();
|
||
workInProgress.flags |= ForceClientRender | Incomplete | ShouldCapture;
|
||
return false;
|
||
}
|
||
var wasHydrated = popHydrationState(workInProgress);
|
||
if (nextState !== null && nextState.dehydrated !== null) {
|
||
// We might be inside a hydration state the first time we're picking up this
|
||
// Suspense boundary, and also after we've reentered it for further hydration.
|
||
if (current === null) {
|
||
if (!wasHydrated) {
|
||
throw new Error('A dehydrated suspense component was completed without a hydrated node. ' + 'This is probably a bug in React.');
|
||
}
|
||
prepareToHydrateHostSuspenseInstance(workInProgress);
|
||
bubbleProperties(workInProgress);
|
||
{
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
var isTimedOutSuspense = nextState !== null;
|
||
if (isTimedOutSuspense) {
|
||
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
||
var primaryChildFragment = workInProgress.child;
|
||
if (primaryChildFragment !== null) {
|
||
// $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
||
workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
else {
|
||
// We might have reentered this boundary to hydrate it. If so, we need to reset the hydration
|
||
// state since we're now exiting out of it. popHydrationState doesn't do that for us.
|
||
resetHydrationState();
|
||
if ((workInProgress.flags & DidCapture) === NoFlags) {
|
||
// This boundary did not suspend so it's now hydrated and unsuspended.
|
||
workInProgress.memoizedState = null;
|
||
} // If nothing suspended, we need to schedule an effect to mark this boundary
|
||
// as having hydrated so events know that they're free to be invoked.
|
||
// It's also a signal to replay events and the suspense callback.
|
||
// If something suspended, schedule an effect to attach retry listeners.
|
||
// So we might as well always mark this.
|
||
workInProgress.flags |= Update;
|
||
bubbleProperties(workInProgress);
|
||
{
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
var _isTimedOutSuspense = nextState !== null;
|
||
if (_isTimedOutSuspense) {
|
||
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
||
var _primaryChildFragment = workInProgress.child;
|
||
if (_primaryChildFragment !== null) {
|
||
// $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
||
workInProgress.treeBaseDuration -= _primaryChildFragment.treeBaseDuration;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
else {
|
||
// Successfully completed this tree. If this was a forced client render,
|
||
// there may have been recoverable errors during first hydration
|
||
// attempt. If so, add them to a queue so we can log them in the
|
||
// commit phase.
|
||
upgradeHydrationErrorsToRecoverable(); // Fall through to normal Suspense path
|
||
return true;
|
||
}
|
||
}
|
||
function completeWork(current, workInProgress, renderLanes) {
|
||
var newProps = workInProgress.pendingProps; // Note: This intentionally doesn't check if we're hydrating because comparing
|
||
// to the current tree provider fiber is just as fast and less error-prone.
|
||
// Ideally we would have a special version of the work loop only
|
||
// for hydration.
|
||
popTreeContext(workInProgress);
|
||
switch (workInProgress.tag) {
|
||
case IndeterminateComponent:
|
||
case LazyComponent:
|
||
case SimpleMemoComponent:
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case Fragment:
|
||
case Mode:
|
||
case Profiler:
|
||
case ContextConsumer:
|
||
case MemoComponent:
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
case ClassComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
if (isContextProvider(Component)) {
|
||
popContext(workInProgress);
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
var fiberRoot = workInProgress.stateNode;
|
||
popHostContainer(workInProgress);
|
||
popTopLevelContextObject(workInProgress);
|
||
resetWorkInProgressVersions();
|
||
if (fiberRoot.pendingContext) {
|
||
fiberRoot.context = fiberRoot.pendingContext;
|
||
fiberRoot.pendingContext = null;
|
||
}
|
||
if (current === null || current.child === null) {
|
||
// If we hydrated, pop so that we can delete any remaining children
|
||
// that weren't hydrated.
|
||
var wasHydrated = popHydrationState(workInProgress);
|
||
if (wasHydrated) {
|
||
// If we hydrated, then we'll need to schedule an update for
|
||
// the commit side-effects on the root.
|
||
markUpdate(workInProgress);
|
||
}
|
||
else {
|
||
if (current !== null) {
|
||
var prevState = current.memoizedState;
|
||
if ( // Check if this is a client root
|
||
!prevState.isDehydrated || // Check if we reverted to client rendering (e.g. due to an error)
|
||
(workInProgress.flags & ForceClientRender) !== NoFlags) {
|
||
// Schedule an effect to clear this container at the start of the
|
||
// next commit. This handles the case of React rendering into a
|
||
// container with previous children. It's also safe to do for
|
||
// updates too, because current.child would only be null if the
|
||
// previous render was null (so the container would already
|
||
// be empty).
|
||
workInProgress.flags |= Snapshot; // If this was a forced client render, there may have been
|
||
// recoverable errors during first hydration attempt. If so, add
|
||
// them to a queue so we can log them in the commit phase.
|
||
upgradeHydrationErrorsToRecoverable();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
updateHostContainer(current, workInProgress);
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
popHostContext(workInProgress);
|
||
var rootContainerInstance = getRootHostContainer();
|
||
var type = workInProgress.type;
|
||
if (current !== null && workInProgress.stateNode != null) {
|
||
updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
|
||
if (current.ref !== workInProgress.ref) {
|
||
markRef$1(workInProgress);
|
||
}
|
||
}
|
||
else {
|
||
if (!newProps) {
|
||
if (workInProgress.stateNode === null) {
|
||
throw new Error('We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
||
} // This can happen when we abort work.
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context
|
||
// "stack" as the parent. Then append children as we go in beginWork
|
||
// or completeWork depending on whether we want to add them top->down or
|
||
// bottom->up. Top->down is faster in IE11.
|
||
var _wasHydrated = popHydrationState(workInProgress);
|
||
if (_wasHydrated) {
|
||
// TODO: Move this and createInstance step into the beginPhase
|
||
// to consolidate.
|
||
if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) {
|
||
// If changes to the hydrated node need to be applied at the
|
||
// commit-phase we mark this as such.
|
||
markUpdate(workInProgress);
|
||
}
|
||
}
|
||
else {
|
||
var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
|
||
appendAllChildren(instance, workInProgress, false, false);
|
||
workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
|
||
// (eg DOM renderer supports auto-focus for certain elements).
|
||
// Make sure such renderers get scheduled for later work.
|
||
if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance, currentHostContext)) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
}
|
||
if (workInProgress.ref !== null) {
|
||
// If there is a ref on a host node we need to schedule a callback
|
||
markRef$1(workInProgress);
|
||
}
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case HostText:
|
||
{
|
||
var newText = newProps;
|
||
if (current && workInProgress.stateNode != null) {
|
||
var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need
|
||
// to schedule a side-effect to do the updates.
|
||
updateHostText$1(current, workInProgress, oldText, newText);
|
||
}
|
||
else {
|
||
if (typeof newText !== 'string') {
|
||
if (workInProgress.stateNode === null) {
|
||
throw new Error('We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
||
} // This can happen when we abort work.
|
||
}
|
||
var _rootContainerInstance = getRootHostContainer();
|
||
var _currentHostContext = getHostContext();
|
||
var _wasHydrated2 = popHydrationState(workInProgress);
|
||
if (_wasHydrated2) {
|
||
if (prepareToHydrateHostTextInstance(workInProgress)) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
}
|
||
else {
|
||
workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
|
||
}
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
popSuspenseContext(workInProgress);
|
||
var nextState = workInProgress.memoizedState; // Special path for dehydrated boundaries. We may eventually move this
|
||
// to its own fiber type so that we can add other kinds of hydration
|
||
// boundaries that aren't associated with a Suspense tree. In anticipation
|
||
// of such a refactor, all the hydration logic is contained in
|
||
// this branch.
|
||
if (current === null || current.memoizedState !== null && current.memoizedState.dehydrated !== null) {
|
||
var fallthroughToNormalSuspensePath = completeDehydratedSuspenseBoundary(current, workInProgress, nextState);
|
||
if (!fallthroughToNormalSuspensePath) {
|
||
if (workInProgress.flags & ShouldCapture) {
|
||
// Special case. There were remaining unhydrated nodes. We treat
|
||
// this as a mismatch. Revert to client rendering.
|
||
return workInProgress;
|
||
}
|
||
else {
|
||
// Did not finish hydrating, either because this is the initial
|
||
// render or because something suspended.
|
||
return null;
|
||
}
|
||
} // Continue with the normal Suspense path.
|
||
}
|
||
if ((workInProgress.flags & DidCapture) !== NoFlags) {
|
||
// Something suspended. Re-render with the fallback children.
|
||
workInProgress.lanes = renderLanes; // Do not reset the effect list.
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
transferActualDuration(workInProgress);
|
||
} // Don't bubble properties in this case.
|
||
return workInProgress;
|
||
}
|
||
var nextDidTimeout = nextState !== null;
|
||
var prevDidTimeout = current !== null && current.memoizedState !== null;
|
||
// a passive effect, which is when we process the transitions
|
||
if (nextDidTimeout !== prevDidTimeout) {
|
||
// an effect to toggle the subtree's visibility. When we switch from
|
||
// fallback -> primary, the inner Offscreen fiber schedules this effect
|
||
// as part of its normal complete phase. But when we switch from
|
||
// primary -> fallback, the inner Offscreen fiber does not have a complete
|
||
// phase. So we need to schedule its effect here.
|
||
//
|
||
// We also use this flag to connect/disconnect the effects, but the same
|
||
// logic applies: when re-connecting, the Offscreen fiber's complete
|
||
// phase will handle scheduling the effect. It's only when the fallback
|
||
// is active that we have to do anything special.
|
||
if (nextDidTimeout) {
|
||
var _offscreenFiber2 = workInProgress.child;
|
||
_offscreenFiber2.flags |= Visibility; // TODO: This will still suspend a synchronous tree if anything
|
||
// in the concurrent tree already suspended during this render.
|
||
// This is a known bug.
|
||
if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
|
||
// TODO: Move this back to throwException because this is too late
|
||
// if this is a large tree which is common for initial loads. We
|
||
// don't know if we should restart a render or not until we get
|
||
// this marker, and this is too late.
|
||
// If this render already had a ping or lower pri updates,
|
||
// and this is the first time we know we're going to suspend we
|
||
// should be able to immediately restart from within throwException.
|
||
var hasInvisibleChildContext = current === null && (workInProgress.memoizedProps.unstable_avoidThisFallback !== true || !enableSuspenseAvoidThisFallback);
|
||
if (hasInvisibleChildContext || hasSuspenseContext(suspenseStackCursor.current, InvisibleParentSuspenseContext)) {
|
||
// If this was in an invisible tree or a new render, then showing
|
||
// this boundary is ok.
|
||
renderDidSuspend();
|
||
}
|
||
else {
|
||
// Otherwise, we're going to have to hide content so we should
|
||
// suspend for longer if possible.
|
||
renderDidSuspendDelayIfPossible();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var wakeables = workInProgress.updateQueue;
|
||
if (wakeables !== null) {
|
||
// Schedule an effect to attach a retry listener to the promise.
|
||
// TODO: Move to passive phase
|
||
workInProgress.flags |= Update;
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
{
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
if (nextDidTimeout) {
|
||
// Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
||
var primaryChildFragment = workInProgress.child;
|
||
if (primaryChildFragment !== null) {
|
||
// $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
||
workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
case HostPortal:
|
||
popHostContainer(workInProgress);
|
||
updateHostContainer(current, workInProgress);
|
||
if (current === null) {
|
||
preparePortalMount(workInProgress.stateNode.containerInfo);
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
case ContextProvider:
|
||
// Pop provider fiber
|
||
var context = workInProgress.type._context;
|
||
popProvider(context, workInProgress);
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
case IncompleteClassComponent:
|
||
{
|
||
// Same as class component case. I put it down here so that the tags are
|
||
// sequential to ensure this switch is compiled to a jump table.
|
||
var _Component = workInProgress.type;
|
||
if (isContextProvider(_Component)) {
|
||
popContext(workInProgress);
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case SuspenseListComponent:
|
||
{
|
||
popSuspenseContext(workInProgress);
|
||
var renderState = workInProgress.memoizedState;
|
||
if (renderState === null) {
|
||
// We're running in the default, "independent" mode.
|
||
// We don't do anything in this mode.
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;
|
||
var renderedTail = renderState.rendering;
|
||
if (renderedTail === null) {
|
||
// We just rendered the head.
|
||
if (!didSuspendAlready) {
|
||
// This is the first pass. We need to figure out if anything is still
|
||
// suspended in the rendered set.
|
||
// If new content unsuspended, but there's still some content that
|
||
// didn't. Then we need to do a second pass that forces everything
|
||
// to keep showing their fallbacks.
|
||
// We might be suspended if something in this render pass suspended, or
|
||
// something in the previous committed pass suspended. Otherwise,
|
||
// there's no chance so we can skip the expensive call to
|
||
// findFirstSuspended.
|
||
var cannotBeSuspended = renderHasNotSuspendedYet() && (current === null || (current.flags & DidCapture) === NoFlags);
|
||
if (!cannotBeSuspended) {
|
||
var row = workInProgress.child;
|
||
while (row !== null) {
|
||
var suspended = findFirstSuspended(row);
|
||
if (suspended !== null) {
|
||
didSuspendAlready = true;
|
||
workInProgress.flags |= DidCapture;
|
||
cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as
|
||
// part of the second pass. In that case nothing will subscribe to
|
||
// its thenables. Instead, we'll transfer its thenables to the
|
||
// SuspenseList so that it can retry if they resolve.
|
||
// There might be multiple of these in the list but since we're
|
||
// going to wait for all of them anyway, it doesn't really matter
|
||
// which ones gets to ping. In theory we could get clever and keep
|
||
// track of how many dependencies remain but it gets tricky because
|
||
// in the meantime, we can add/remove/change items and dependencies.
|
||
// We might bail out of the loop before finding any but that
|
||
// doesn't matter since that means that the other boundaries that
|
||
// we did find already has their listeners attached.
|
||
var newThenables = suspended.updateQueue;
|
||
if (newThenables !== null) {
|
||
workInProgress.updateQueue = newThenables;
|
||
workInProgress.flags |= Update;
|
||
} // Rerender the whole list, but this time, we'll force fallbacks
|
||
// to stay in place.
|
||
// Reset the effect flags before doing the second pass since that's now invalid.
|
||
// Reset the child fibers to their original state.
|
||
workInProgress.subtreeFlags = NoFlags;
|
||
resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately
|
||
// rerender the children.
|
||
pushSuspenseContext(workInProgress, setShallowSuspenseContext(suspenseStackCursor.current, ForceSuspenseFallback)); // Don't bubble properties in this case.
|
||
return workInProgress.child;
|
||
}
|
||
row = row.sibling;
|
||
}
|
||
}
|
||
if (renderState.tail !== null && now() > getRenderTargetTime()) {
|
||
// We have already passed our CPU deadline but we still have rows
|
||
// left in the tail. We'll just give up further attempts to render
|
||
// the main content and only render fallbacks.
|
||
workInProgress.flags |= DidCapture;
|
||
didSuspendAlready = true;
|
||
cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
|
||
// to get it started back up to attempt the next item. While in terms
|
||
// of priority this work has the same priority as this current render,
|
||
// it's not part of the same transition once the transition has
|
||
// committed. If it's sync, we still want to yield so that it can be
|
||
// painted. Conceptually, this is really the same as pinging.
|
||
// We can use any RetryLane even if it's the one currently rendering
|
||
// since we're leaving it behind on this node.
|
||
workInProgress.lanes = SomeRetryLane;
|
||
}
|
||
}
|
||
else {
|
||
cutOffTailIfNeeded(renderState, false);
|
||
} // Next we're going to render the tail.
|
||
}
|
||
else {
|
||
// Append the rendered row to the child list.
|
||
if (!didSuspendAlready) {
|
||
var _suspended = findFirstSuspended(renderedTail);
|
||
if (_suspended !== null) {
|
||
workInProgress.flags |= DidCapture;
|
||
didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't
|
||
// get lost if this row ends up dropped during a second pass.
|
||
var _newThenables = _suspended.updateQueue;
|
||
if (_newThenables !== null) {
|
||
workInProgress.updateQueue = _newThenables;
|
||
workInProgress.flags |= Update;
|
||
}
|
||
cutOffTailIfNeeded(renderState, true); // This might have been modified.
|
||
if (renderState.tail === null && renderState.tailMode === 'hidden' && !renderedTail.alternate && !getIsHydrating() // We don't cut it if we're hydrating.
|
||
) {
|
||
// We're done.
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
}
|
||
else if ( // The time it took to render last row is greater than the remaining
|
||
// time we have to render. So rendering one more row would likely
|
||
// exceed it.
|
||
now() * 2 - renderState.renderingStartTime > getRenderTargetTime() && renderLanes !== OffscreenLane) {
|
||
// We have now passed our CPU deadline and we'll just give up further
|
||
// attempts to render the main content and only render fallbacks.
|
||
// The assumption is that this is usually faster.
|
||
workInProgress.flags |= DidCapture;
|
||
didSuspendAlready = true;
|
||
cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
|
||
// to get it started back up to attempt the next item. While in terms
|
||
// of priority this work has the same priority as this current render,
|
||
// it's not part of the same transition once the transition has
|
||
// committed. If it's sync, we still want to yield so that it can be
|
||
// painted. Conceptually, this is really the same as pinging.
|
||
// We can use any RetryLane even if it's the one currently rendering
|
||
// since we're leaving it behind on this node.
|
||
workInProgress.lanes = SomeRetryLane;
|
||
}
|
||
}
|
||
if (renderState.isBackwards) {
|
||
// The effect list of the backwards tail will have been added
|
||
// to the end. This breaks the guarantee that life-cycles fire in
|
||
// sibling order but that isn't a strong guarantee promised by React.
|
||
// Especially since these might also just pop in during future commits.
|
||
// Append to the beginning of the list.
|
||
renderedTail.sibling = workInProgress.child;
|
||
workInProgress.child = renderedTail;
|
||
}
|
||
else {
|
||
var previousSibling = renderState.last;
|
||
if (previousSibling !== null) {
|
||
previousSibling.sibling = renderedTail;
|
||
}
|
||
else {
|
||
workInProgress.child = renderedTail;
|
||
}
|
||
renderState.last = renderedTail;
|
||
}
|
||
}
|
||
if (renderState.tail !== null) {
|
||
// We still have tail rows to render.
|
||
// Pop a row.
|
||
var next = renderState.tail;
|
||
renderState.rendering = next;
|
||
renderState.tail = next.sibling;
|
||
renderState.renderingStartTime = now();
|
||
next.sibling = null; // Restore the context.
|
||
// TODO: We can probably just avoid popping it instead and only
|
||
// setting it the first time we go from not suspended to suspended.
|
||
var suspenseContext = suspenseStackCursor.current;
|
||
if (didSuspendAlready) {
|
||
suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
||
}
|
||
else {
|
||
suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
||
}
|
||
pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row.
|
||
// Don't bubble properties in this case.
|
||
return next;
|
||
}
|
||
bubbleProperties(workInProgress);
|
||
return null;
|
||
}
|
||
case ScopeComponent:
|
||
{
|
||
break;
|
||
}
|
||
case OffscreenComponent:
|
||
case LegacyHiddenComponent:
|
||
{
|
||
popRenderLanes(workInProgress);
|
||
var _nextState = workInProgress.memoizedState;
|
||
var nextIsHidden = _nextState !== null;
|
||
if (current !== null) {
|
||
var _prevState = current.memoizedState;
|
||
var prevIsHidden = _prevState !== null;
|
||
if (prevIsHidden !== nextIsHidden && ( // LegacyHidden doesn't do any hiding — it only pre-renders.
|
||
!enableLegacyHidden)) {
|
||
workInProgress.flags |= Visibility;
|
||
}
|
||
}
|
||
if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
|
||
bubbleProperties(workInProgress);
|
||
}
|
||
else {
|
||
// Don't bubble properties for hidden children unless we're rendering
|
||
// at offscreen priority.
|
||
if (includesSomeLane(subtreeRenderLanes, OffscreenLane)) {
|
||
bubbleProperties(workInProgress);
|
||
if (supportsMutation) {
|
||
// Check if there was an insertion or update in the hidden subtree.
|
||
// If so, we need to hide those nodes in the commit phase, so
|
||
// schedule a visibility effect.
|
||
if (workInProgress.subtreeFlags & (Placement | Update)) {
|
||
workInProgress.flags |= Visibility;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
case CacheComponent:
|
||
{
|
||
return null;
|
||
}
|
||
case TracingMarkerComponent:
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.');
|
||
}
|
||
function unwindWork(current, workInProgress, renderLanes) {
|
||
// Note: This intentionally doesn't check if we're hydrating because comparing
|
||
// to the current tree provider fiber is just as fast and less error-prone.
|
||
// Ideally we would have a special version of the work loop only
|
||
// for hydration.
|
||
popTreeContext(workInProgress);
|
||
switch (workInProgress.tag) {
|
||
case ClassComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
if (isContextProvider(Component)) {
|
||
popContext(workInProgress);
|
||
}
|
||
var flags = workInProgress.flags;
|
||
if (flags & ShouldCapture) {
|
||
workInProgress.flags = flags & ~ShouldCapture | DidCapture;
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
transferActualDuration(workInProgress);
|
||
}
|
||
return workInProgress;
|
||
}
|
||
return null;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
var root = workInProgress.stateNode;
|
||
popHostContainer(workInProgress);
|
||
popTopLevelContextObject(workInProgress);
|
||
resetWorkInProgressVersions();
|
||
var _flags = workInProgress.flags;
|
||
if ((_flags & ShouldCapture) !== NoFlags && (_flags & DidCapture) === NoFlags) {
|
||
// There was an error during render that wasn't captured by a suspense
|
||
// boundary. Do a second pass on the root to unmount the children.
|
||
workInProgress.flags = _flags & ~ShouldCapture | DidCapture;
|
||
return workInProgress;
|
||
} // We unwound to the root without completing it. Exit.
|
||
return null;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
// TODO: popHydrationState
|
||
popHostContext(workInProgress);
|
||
return null;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
popSuspenseContext(workInProgress);
|
||
var suspenseState = workInProgress.memoizedState;
|
||
if (suspenseState !== null && suspenseState.dehydrated !== null) {
|
||
if (workInProgress.alternate === null) {
|
||
throw new Error('Threw in newly mounted dehydrated component. This is likely a bug in ' + 'React. Please file an issue.');
|
||
}
|
||
resetHydrationState();
|
||
}
|
||
var _flags2 = workInProgress.flags;
|
||
if (_flags2 & ShouldCapture) {
|
||
workInProgress.flags = _flags2 & ~ShouldCapture | DidCapture; // Captured a suspense effect. Re-render the boundary.
|
||
if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
||
transferActualDuration(workInProgress);
|
||
}
|
||
return workInProgress;
|
||
}
|
||
return null;
|
||
}
|
||
case SuspenseListComponent:
|
||
{
|
||
popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been
|
||
// caught by a nested boundary. If not, it should bubble through.
|
||
return null;
|
||
}
|
||
case HostPortal:
|
||
popHostContainer(workInProgress);
|
||
return null;
|
||
case ContextProvider:
|
||
var context = workInProgress.type._context;
|
||
popProvider(context, workInProgress);
|
||
return null;
|
||
case OffscreenComponent:
|
||
case LegacyHiddenComponent:
|
||
popRenderLanes(workInProgress);
|
||
return null;
|
||
case CacheComponent:
|
||
return null;
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
function unwindInterruptedWork(current, interruptedWork, renderLanes) {
|
||
// Note: This intentionally doesn't check if we're hydrating because comparing
|
||
// to the current tree provider fiber is just as fast and less error-prone.
|
||
// Ideally we would have a special version of the work loop only
|
||
// for hydration.
|
||
popTreeContext(interruptedWork);
|
||
switch (interruptedWork.tag) {
|
||
case ClassComponent:
|
||
{
|
||
var childContextTypes = interruptedWork.type.childContextTypes;
|
||
if (childContextTypes !== null && childContextTypes !== undefined) {
|
||
popContext(interruptedWork);
|
||
}
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
var root = interruptedWork.stateNode;
|
||
popHostContainer(interruptedWork);
|
||
popTopLevelContextObject(interruptedWork);
|
||
resetWorkInProgressVersions();
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
popHostContext(interruptedWork);
|
||
break;
|
||
}
|
||
case HostPortal:
|
||
popHostContainer(interruptedWork);
|
||
break;
|
||
case SuspenseComponent:
|
||
popSuspenseContext(interruptedWork);
|
||
break;
|
||
case SuspenseListComponent:
|
||
popSuspenseContext(interruptedWork);
|
||
break;
|
||
case ContextProvider:
|
||
var context = interruptedWork.type._context;
|
||
popProvider(context, interruptedWork);
|
||
break;
|
||
case OffscreenComponent:
|
||
case LegacyHiddenComponent:
|
||
popRenderLanes(interruptedWork);
|
||
break;
|
||
}
|
||
}
|
||
function invokeGuardedCallbackProd(name, func, context, a, b, c, d, e, f) {
|
||
var funcArgs = Array.prototype.slice.call(arguments, 3);
|
||
try {
|
||
func.apply(context, funcArgs);
|
||
}
|
||
catch (error) {
|
||
this.onError(error);
|
||
}
|
||
}
|
||
var invokeGuardedCallbackImpl = invokeGuardedCallbackProd;
|
||
{
|
||
// In DEV mode, we swap out invokeGuardedCallback for a special version
|
||
// that plays more nicely with the browser's DevTools. The idea is to preserve
|
||
// "Pause on exceptions" behavior. Because React wraps all user-provided
|
||
// functions in invokeGuardedCallback, and the production version of
|
||
// invokeGuardedCallback uses a try-catch, all user exceptions are treated
|
||
// like caught exceptions, and the DevTools won't pause unless the developer
|
||
// takes the extra step of enabling pause on caught exceptions. This is
|
||
// unintuitive, though, because even though React has caught the error, from
|
||
// the developer's perspective, the error is uncaught.
|
||
//
|
||
// To preserve the expected "Pause on exceptions" behavior, we don't use a
|
||
// try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake
|
||
// DOM node, and call the user-provided callback from inside an event handler
|
||
// for that fake event. If the callback throws, the error is "captured" using
|
||
// a global event handler. But because the error happens in a different
|
||
// event loop context, it does not interrupt the normal program flow.
|
||
// Effectively, this gives us try-catch behavior without actually using
|
||
// try-catch. Neat!
|
||
// Check that the browser supports the APIs we need to implement our special
|
||
// DEV version of invokeGuardedCallback
|
||
if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
|
||
var fakeNode = document.createElement('react');
|
||
invokeGuardedCallbackImpl = function invokeGuardedCallbackDev(name, func, context, a, b, c, d, e, f) {
|
||
// If document doesn't exist we know for sure we will crash in this method
|
||
// when we call document.createEvent(). However this can cause confusing
|
||
// errors: https://github.com/facebook/create-react-app/issues/3482
|
||
// So we preemptively throw with a better message instead.
|
||
if (typeof document === 'undefined' || document === null) {
|
||
throw new Error('The `document` global was defined when React was initialized, but is not ' + 'defined anymore. This can happen in a test environment if a component ' + 'schedules an update from an asynchronous callback, but the test has already ' + 'finished running. To solve this, you can either unmount the component at ' + 'the end of your test (and ensure that any asynchronous operations get ' + 'canceled in `componentWillUnmount`), or you can change the test itself ' + 'to be asynchronous.');
|
||
}
|
||
var evt = document.createEvent('Event');
|
||
var didCall = false; // Keeps track of whether the user-provided callback threw an error. We
|
||
// set this to true at the beginning, then set it to false right after
|
||
// calling the function. If the function errors, `didError` will never be
|
||
// set to false. This strategy works even if the browser is flaky and
|
||
// fails to call our global error handler, because it doesn't rely on
|
||
// the error event at all.
|
||
var didError = true; // Keeps track of the value of window.event so that we can reset it
|
||
// during the callback to let user code access window.event in the
|
||
// browsers that support it.
|
||
var windowEvent = window.event; // Keeps track of the descriptor of window.event to restore it after event
|
||
// dispatching: https://github.com/facebook/react/issues/13688
|
||
var windowEventDescriptor = Object.getOwnPropertyDescriptor(window, 'event');
|
||
function restoreAfterDispatch() {
|
||
// We immediately remove the callback from event listeners so that
|
||
// nested `invokeGuardedCallback` calls do not clash. Otherwise, a
|
||
// nested call would trigger the fake event handlers of any call higher
|
||
// in the stack.
|
||
fakeNode.removeEventListener(evtType, callCallback, false); // We check for window.hasOwnProperty('event') to prevent the
|
||
// window.event assignment in both IE <= 10 as they throw an error
|
||
// "Member not found" in strict mode, and in Firefox which does not
|
||
// support window.event.
|
||
if (typeof window.event !== 'undefined' && window.hasOwnProperty('event')) {
|
||
window.event = windowEvent;
|
||
}
|
||
} // Create an event handler for our fake event. We will synchronously
|
||
// dispatch our fake event using `dispatchEvent`. Inside the handler, we
|
||
// call the user-provided callback.
|
||
var funcArgs = Array.prototype.slice.call(arguments, 3);
|
||
function callCallback() {
|
||
didCall = true;
|
||
restoreAfterDispatch();
|
||
func.apply(context, funcArgs);
|
||
didError = false;
|
||
} // Create a global error event handler. We use this to capture the value
|
||
// that was thrown. It's possible that this error handler will fire more
|
||
// than once; for example, if non-React code also calls `dispatchEvent`
|
||
// and a handler for that event throws. We should be resilient to most of
|
||
// those cases. Even if our error event handler fires more than once, the
|
||
// last error event is always used. If the callback actually does error,
|
||
// we know that the last error event is the correct one, because it's not
|
||
// possible for anything else to have happened in between our callback
|
||
// erroring and the code that follows the `dispatchEvent` call below. If
|
||
// the callback doesn't error, but the error event was fired, we know to
|
||
// ignore it because `didError` will be false, as described above.
|
||
var error; // Use this to track whether the error event is ever called.
|
||
var didSetError = false;
|
||
var isCrossOriginError = false;
|
||
function handleWindowError(event) {
|
||
error = event.error;
|
||
didSetError = true;
|
||
if (error === null && event.colno === 0 && event.lineno === 0) {
|
||
isCrossOriginError = true;
|
||
}
|
||
if (event.defaultPrevented) {
|
||
// Some other error handler has prevented default.
|
||
// Browsers silence the error report if this happens.
|
||
// We'll remember this to later decide whether to log it or not.
|
||
if (error != null && typeof error === 'object') {
|
||
try {
|
||
error._suppressLogging = true;
|
||
}
|
||
catch (inner) { // Ignore.
|
||
}
|
||
}
|
||
}
|
||
} // Create a fake event type.
|
||
var evtType = "react-" + (name ? name : 'invokeguardedcallback'); // Attach our event handlers
|
||
window.addEventListener('error', handleWindowError);
|
||
fakeNode.addEventListener(evtType, callCallback, false); // Synchronously dispatch our fake event. If the user-provided function
|
||
// errors, it will trigger our global error handler.
|
||
evt.initEvent(evtType, false, false);
|
||
fakeNode.dispatchEvent(evt);
|
||
if (windowEventDescriptor) {
|
||
Object.defineProperty(window, 'event', windowEventDescriptor);
|
||
}
|
||
if (didCall && didError) {
|
||
if (!didSetError) {
|
||
// The callback errored, but the error event never fired.
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
error = new Error('An error was thrown inside one of your components, but React ' + "doesn't know what it was. This is likely due to browser " + 'flakiness. React does its best to preserve the "Pause on ' + 'exceptions" behavior of the DevTools, which requires some ' + "DEV-mode only tricks. It's possible that these don't work in " + 'your browser. Try triggering the error in production mode, ' + 'or switching to a modern browser. If you suspect that this is ' + 'actually an issue with React, please file an issue.');
|
||
}
|
||
else if (isCrossOriginError) {
|
||
// eslint-disable-next-line react-internal/prod-error-codes
|
||
error = new Error("A cross-origin error was thrown. React doesn't have access to " + 'the actual error object in development. ' + 'See https://reactjs.org/link/crossorigin-error for more information.');
|
||
}
|
||
this.onError(error);
|
||
} // Remove our event listeners
|
||
window.removeEventListener('error', handleWindowError);
|
||
if (!didCall) {
|
||
// Something went really wrong, and our event was not dispatched.
|
||
// https://github.com/facebook/react/issues/16734
|
||
// https://github.com/facebook/react/issues/16585
|
||
// Fall back to the production implementation.
|
||
restoreAfterDispatch();
|
||
return invokeGuardedCallbackProd.apply(this, arguments);
|
||
}
|
||
};
|
||
}
|
||
}
|
||
var invokeGuardedCallbackImpl$1 = invokeGuardedCallbackImpl;
|
||
var hasError = false;
|
||
var caughtError = null; // Used by event system to capture/rethrow the first error.
|
||
var reporter = {
|
||
onError: function (error) {
|
||
hasError = true;
|
||
caughtError = error;
|
||
}
|
||
};
|
||
/**
|
||
* Call a function while guarding against errors that happens within it.
|
||
* Returns an error if it throws, otherwise null.
|
||
*
|
||
* In production, this is implemented using a try-catch. The reason we don't
|
||
* use a try-catch directly is so that we can swap out a different
|
||
* implementation in DEV mode.
|
||
*
|
||
* @param {String} name of the guard to use for logging or debugging
|
||
* @param {Function} func The function to invoke
|
||
* @param {*} context The context to use when calling the function
|
||
* @param {...*} args Arguments for function
|
||
*/
|
||
function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
|
||
hasError = false;
|
||
caughtError = null;
|
||
invokeGuardedCallbackImpl$1.apply(reporter, arguments);
|
||
}
|
||
function hasCaughtError() {
|
||
return hasError;
|
||
}
|
||
function clearCaughtError() {
|
||
if (hasError) {
|
||
var error = caughtError;
|
||
hasError = false;
|
||
caughtError = null;
|
||
return error;
|
||
}
|
||
else {
|
||
throw new Error('clearCaughtError was called but no error was captured. This error ' + 'is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
var didWarnAboutUndefinedSnapshotBeforeUpdate = null;
|
||
{
|
||
didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
|
||
} // Used during the commit phase to track the state of the Offscreen component stack.
|
||
// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
|
||
// Only used when enableSuspenseLayoutEffectSemantics is enabled.
|
||
var offscreenSubtreeIsHidden = false;
|
||
var offscreenSubtreeWasHidden = false;
|
||
var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
||
var nextEffect = null; // Used for Profiling builds to track updaters.
|
||
var inProgressLanes = null;
|
||
var inProgressRoot = null;
|
||
function reportUncaughtErrorInDEV(error) {
|
||
// Wrapping each small part of the commit phase into a guarded
|
||
// callback is a bit too slow (https://github.com/facebook/react/pull/21666).
|
||
// But we rely on it to surface errors to DEV tools like overlays
|
||
// (https://github.com/facebook/react/issues/21712).
|
||
// As a compromise, rethrow only caught errors in a guard.
|
||
{
|
||
invokeGuardedCallback(null, function () {
|
||
throw error;
|
||
});
|
||
clearCaughtError();
|
||
}
|
||
}
|
||
var callComponentWillUnmountWithTimer = function (current, instance) {
|
||
instance.props = current.memoizedProps;
|
||
instance.state = current.memoizedState;
|
||
if (current.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
instance.componentWillUnmount();
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(current);
|
||
}
|
||
}
|
||
else {
|
||
instance.componentWillUnmount();
|
||
}
|
||
}; // Capture errors so they don't interrupt mounting.
|
||
function safelyCallCommitHookLayoutEffectListMount(current, nearestMountedAncestor) {
|
||
try {
|
||
commitHookEffectListMount(Layout, current);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
} // Capture errors so they don't interrupt unmounting.
|
||
function safelyCallComponentWillUnmount(current, nearestMountedAncestor, instance) {
|
||
try {
|
||
callComponentWillUnmountWithTimer(current, instance);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
} // Capture errors so they don't interrupt mounting.
|
||
function safelyCallComponentDidMount(current, nearestMountedAncestor, instance) {
|
||
try {
|
||
instance.componentDidMount();
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
} // Capture errors so they don't interrupt mounting.
|
||
function safelyAttachRef(current, nearestMountedAncestor) {
|
||
try {
|
||
commitAttachRef(current);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
}
|
||
function safelyDetachRef(current, nearestMountedAncestor) {
|
||
var ref = current.ref;
|
||
if (ref !== null) {
|
||
if (typeof ref === 'function') {
|
||
var retVal;
|
||
try {
|
||
if (enableProfilerTimer && enableProfilerCommitHooks && current.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
retVal = ref(null);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(current);
|
||
}
|
||
}
|
||
else {
|
||
retVal = ref(null);
|
||
}
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
{
|
||
if (typeof retVal === 'function') {
|
||
error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(current));
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
ref.current = null;
|
||
}
|
||
}
|
||
}
|
||
function safelyCallDestroy(current, nearestMountedAncestor, destroy) {
|
||
try {
|
||
destroy();
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(current, nearestMountedAncestor, error);
|
||
}
|
||
}
|
||
var focusedInstanceHandle = null;
|
||
var shouldFireAfterActiveInstanceBlur = false;
|
||
function commitBeforeMutationEffects(root, firstChild) {
|
||
focusedInstanceHandle = prepareForCommit(root.containerInfo);
|
||
nextEffect = firstChild;
|
||
commitBeforeMutationEffects_begin(); // We no longer need to track the active instance fiber
|
||
var shouldFire = shouldFireAfterActiveInstanceBlur;
|
||
shouldFireAfterActiveInstanceBlur = false;
|
||
focusedInstanceHandle = null;
|
||
return shouldFire;
|
||
}
|
||
function commitBeforeMutationEffects_begin() {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect; // This phase is only used for beforeActiveInstanceBlur.
|
||
var child = fiber.child;
|
||
if ((fiber.subtreeFlags & BeforeMutationMask) !== NoFlags && child !== null) {
|
||
child.return = fiber;
|
||
nextEffect = child;
|
||
}
|
||
else {
|
||
commitBeforeMutationEffects_complete();
|
||
}
|
||
}
|
||
}
|
||
function commitBeforeMutationEffects_complete() {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
setCurrentFiber(fiber);
|
||
try {
|
||
commitBeforeMutationEffectsOnFiber(fiber);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
resetCurrentFiber();
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function commitBeforeMutationEffectsOnFiber(finishedWork) {
|
||
var current = finishedWork.alternate;
|
||
var flags = finishedWork.flags;
|
||
if ((flags & Snapshot) !== NoFlags) {
|
||
setCurrentFiber(finishedWork);
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
if (current !== null) {
|
||
var prevProps = current.memoizedProps;
|
||
var prevState = current.memoizedState;
|
||
var instance = finishedWork.stateNode; // We could update instance props and state here,
|
||
// but instead we rely on them being set during last render.
|
||
// TODO: revisit this when we implement resuming.
|
||
{
|
||
if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
||
if (instance.props !== finishedWork.memoizedProps) {
|
||
error('Expected %s props to match memoized props before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
if (instance.state !== finishedWork.memoizedState) {
|
||
error('Expected %s state to match memoized state before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
}
|
||
}
|
||
var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
|
||
{
|
||
var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate;
|
||
if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
|
||
didWarnSet.add(finishedWork.type);
|
||
error('%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + 'must be returned. You have returned undefined.', getComponentNameFromFiber(finishedWork));
|
||
}
|
||
}
|
||
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
||
}
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
if (supportsMutation) {
|
||
var root = finishedWork.stateNode;
|
||
clearContainer(root.containerInfo);
|
||
}
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
case HostText:
|
||
case HostPortal:
|
||
case IncompleteClassComponent:
|
||
// Nothing to do for these component types
|
||
break;
|
||
default:
|
||
{
|
||
throw new Error('This unit of work tag should not have side-effects. This error is ' + 'likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
resetCurrentFiber();
|
||
}
|
||
}
|
||
function commitHookEffectListUnmount(flags, finishedWork, nearestMountedAncestor) {
|
||
var updateQueue = finishedWork.updateQueue;
|
||
var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
||
if (lastEffect !== null) {
|
||
var firstEffect = lastEffect.next;
|
||
var effect = firstEffect;
|
||
do {
|
||
if ((effect.tag & flags) === flags) {
|
||
// Unmount
|
||
var destroy = effect.destroy;
|
||
effect.destroy = undefined;
|
||
if (destroy !== undefined) {
|
||
{
|
||
if ((flags & Passive$1) !== NoFlags$1) {
|
||
markComponentPassiveEffectUnmountStarted(finishedWork);
|
||
}
|
||
else if ((flags & Layout) !== NoFlags$1) {
|
||
markComponentLayoutEffectUnmountStarted(finishedWork);
|
||
}
|
||
}
|
||
{
|
||
if ((flags & Insertion) !== NoFlags$1) {
|
||
setIsRunningInsertionEffect(true);
|
||
}
|
||
}
|
||
safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
|
||
{
|
||
if ((flags & Insertion) !== NoFlags$1) {
|
||
setIsRunningInsertionEffect(false);
|
||
}
|
||
}
|
||
{
|
||
if ((flags & Passive$1) !== NoFlags$1) {
|
||
markComponentPassiveEffectUnmountStopped();
|
||
}
|
||
else if ((flags & Layout) !== NoFlags$1) {
|
||
markComponentLayoutEffectUnmountStopped();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
effect = effect.next;
|
||
} while (effect !== firstEffect);
|
||
}
|
||
}
|
||
function commitHookEffectListMount(flags, finishedWork) {
|
||
var updateQueue = finishedWork.updateQueue;
|
||
var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
||
if (lastEffect !== null) {
|
||
var firstEffect = lastEffect.next;
|
||
var effect = firstEffect;
|
||
do {
|
||
if ((effect.tag & flags) === flags) {
|
||
{
|
||
if ((flags & Passive$1) !== NoFlags$1) {
|
||
markComponentPassiveEffectMountStarted(finishedWork);
|
||
}
|
||
else if ((flags & Layout) !== NoFlags$1) {
|
||
markComponentLayoutEffectMountStarted(finishedWork);
|
||
}
|
||
} // Mount
|
||
var create = effect.create;
|
||
{
|
||
if ((flags & Insertion) !== NoFlags$1) {
|
||
setIsRunningInsertionEffect(true);
|
||
}
|
||
}
|
||
effect.destroy = create();
|
||
{
|
||
if ((flags & Insertion) !== NoFlags$1) {
|
||
setIsRunningInsertionEffect(false);
|
||
}
|
||
}
|
||
{
|
||
if ((flags & Passive$1) !== NoFlags$1) {
|
||
markComponentPassiveEffectMountStopped();
|
||
}
|
||
else if ((flags & Layout) !== NoFlags$1) {
|
||
markComponentLayoutEffectMountStopped();
|
||
}
|
||
}
|
||
{
|
||
var destroy = effect.destroy;
|
||
if (destroy !== undefined && typeof destroy !== 'function') {
|
||
var hookName = void 0;
|
||
if ((effect.tag & Layout) !== NoFlags) {
|
||
hookName = 'useLayoutEffect';
|
||
}
|
||
else if ((effect.tag & Insertion) !== NoFlags) {
|
||
hookName = 'useInsertionEffect';
|
||
}
|
||
else {
|
||
hookName = 'useEffect';
|
||
}
|
||
var addendum = void 0;
|
||
if (destroy === null) {
|
||
addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).';
|
||
}
|
||
else if (typeof destroy.then === 'function') {
|
||
addendum = '\n\nIt looks like you wrote ' + hookName + '(async () => ...) or returned a Promise. ' + 'Instead, write the async function inside your effect ' + 'and call it immediately:\n\n' + hookName + '(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';
|
||
}
|
||
else {
|
||
addendum = ' You returned: ' + destroy;
|
||
}
|
||
error('%s must not return anything besides a function, ' + 'which is used for clean-up.%s', hookName, addendum);
|
||
}
|
||
}
|
||
}
|
||
effect = effect.next;
|
||
} while (effect !== firstEffect);
|
||
}
|
||
}
|
||
function commitPassiveEffectDurations(finishedRoot, finishedWork) {
|
||
{
|
||
// Only Profilers with work in their subtree will have an Update effect scheduled.
|
||
if ((finishedWork.flags & Update) !== NoFlags) {
|
||
switch (finishedWork.tag) {
|
||
case Profiler:
|
||
{
|
||
var passiveEffectDuration = finishedWork.stateNode.passiveEffectDuration;
|
||
var _finishedWork$memoize = finishedWork.memoizedProps, id = _finishedWork$memoize.id, onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase.
|
||
// It does not get reset until the start of the next commit phase.
|
||
var commitTime = getCommitTime();
|
||
var phase = finishedWork.alternate === null ? 'mount' : 'update';
|
||
{
|
||
if (isCurrentUpdateNested()) {
|
||
phase = 'nested-update';
|
||
}
|
||
}
|
||
if (typeof onPostCommit === 'function') {
|
||
onPostCommit(id, phase, passiveEffectDuration, commitTime);
|
||
} // Bubble times to the next nearest ancestor Profiler.
|
||
// After we process that Profiler, we'll bubble further up.
|
||
var parentFiber = finishedWork.return;
|
||
outer: while (parentFiber !== null) {
|
||
switch (parentFiber.tag) {
|
||
case HostRoot:
|
||
var root = parentFiber.stateNode;
|
||
root.passiveEffectDuration += passiveEffectDuration;
|
||
break outer;
|
||
case Profiler:
|
||
var parentStateNode = parentFiber.stateNode;
|
||
parentStateNode.passiveEffectDuration += passiveEffectDuration;
|
||
break outer;
|
||
}
|
||
parentFiber = parentFiber.return;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function commitLayoutEffectOnFiber(finishedRoot, current, finishedWork, committedLanes) {
|
||
if ((finishedWork.flags & LayoutMask) !== NoFlags) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (!offscreenSubtreeWasHidden) {
|
||
// At this point layout effects have already been destroyed (during mutation phase).
|
||
// This is done to prevent sibling component effects from interfering with each other,
|
||
// e.g. a destroy function in one component should never override a ref set
|
||
// by a create function in another component during the same commit.
|
||
if (finishedWork.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
commitHookEffectListMount(Layout | HasEffect, finishedWork);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(finishedWork);
|
||
}
|
||
}
|
||
else {
|
||
commitHookEffectListMount(Layout | HasEffect, finishedWork);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var instance = finishedWork.stateNode;
|
||
if (finishedWork.flags & Update) {
|
||
if (!offscreenSubtreeWasHidden) {
|
||
if (current === null) {
|
||
// We could update instance props and state here,
|
||
// but instead we rely on them being set during last render.
|
||
// TODO: revisit this when we implement resuming.
|
||
{
|
||
if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
||
if (instance.props !== finishedWork.memoizedProps) {
|
||
error('Expected %s props to match memoized props before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
if (instance.state !== finishedWork.memoizedState) {
|
||
error('Expected %s state to match memoized state before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
}
|
||
}
|
||
if (finishedWork.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
instance.componentDidMount();
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(finishedWork);
|
||
}
|
||
}
|
||
else {
|
||
instance.componentDidMount();
|
||
}
|
||
}
|
||
else {
|
||
var prevProps = finishedWork.elementType === finishedWork.type ? current.memoizedProps : resolveDefaultProps(finishedWork.type, current.memoizedProps);
|
||
var prevState = current.memoizedState; // We could update instance props and state here,
|
||
// but instead we rely on them being set during last render.
|
||
// TODO: revisit this when we implement resuming.
|
||
{
|
||
if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
||
if (instance.props !== finishedWork.memoizedProps) {
|
||
error('Expected %s props to match memoized props before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
if (instance.state !== finishedWork.memoizedState) {
|
||
error('Expected %s state to match memoized state before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
}
|
||
}
|
||
if (finishedWork.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(finishedWork);
|
||
}
|
||
}
|
||
else {
|
||
instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
|
||
}
|
||
}
|
||
}
|
||
} // TODO: I think this is now always non-null by the time it reaches the
|
||
// commit phase. Consider removing the type check.
|
||
var updateQueue = finishedWork.updateQueue;
|
||
if (updateQueue !== null) {
|
||
{
|
||
if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
||
if (instance.props !== finishedWork.memoizedProps) {
|
||
error('Expected %s props to match memoized props before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
if (instance.state !== finishedWork.memoizedState) {
|
||
error('Expected %s state to match memoized state before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
||
}
|
||
}
|
||
} // We could update instance props and state here,
|
||
// but instead we rely on them being set during last render.
|
||
// TODO: revisit this when we implement resuming.
|
||
commitUpdateQueue(finishedWork, updateQueue, instance);
|
||
}
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
// TODO: I think this is now always non-null by the time it reaches the
|
||
// commit phase. Consider removing the type check.
|
||
var _updateQueue = finishedWork.updateQueue;
|
||
if (_updateQueue !== null) {
|
||
var _instance = null;
|
||
if (finishedWork.child !== null) {
|
||
switch (finishedWork.child.tag) {
|
||
case HostComponent:
|
||
_instance = getPublicInstance(finishedWork.child.stateNode);
|
||
break;
|
||
case ClassComponent:
|
||
_instance = finishedWork.child.stateNode;
|
||
break;
|
||
}
|
||
}
|
||
commitUpdateQueue(finishedWork, _updateQueue, _instance);
|
||
}
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
var _instance2 = finishedWork.stateNode; // Renderers may schedule work to be done after host components are mounted
|
||
// (eg DOM renderer may schedule auto-focus for inputs and form controls).
|
||
// These effects should only be committed when components are first mounted,
|
||
// aka when there is no current/alternate.
|
||
if (current === null && finishedWork.flags & Update) {
|
||
var type = finishedWork.type;
|
||
var props = finishedWork.memoizedProps;
|
||
commitMount(_instance2, type, props, finishedWork);
|
||
}
|
||
break;
|
||
}
|
||
case HostText:
|
||
{
|
||
// We have no life-cycles associated with text.
|
||
break;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
// We have no life-cycles associated with portals.
|
||
break;
|
||
}
|
||
case Profiler:
|
||
{
|
||
{
|
||
var _finishedWork$memoize2 = finishedWork.memoizedProps, onCommit = _finishedWork$memoize2.onCommit, onRender = _finishedWork$memoize2.onRender;
|
||
var effectDuration = finishedWork.stateNode.effectDuration;
|
||
var commitTime = getCommitTime();
|
||
var phase = current === null ? 'mount' : 'update';
|
||
{
|
||
if (isCurrentUpdateNested()) {
|
||
phase = 'nested-update';
|
||
}
|
||
}
|
||
if (typeof onRender === 'function') {
|
||
onRender(finishedWork.memoizedProps.id, phase, finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, commitTime);
|
||
}
|
||
{
|
||
if (typeof onCommit === 'function') {
|
||
onCommit(finishedWork.memoizedProps.id, phase, effectDuration, commitTime);
|
||
} // Schedule a passive effect for this Profiler to call onPostCommit hooks.
|
||
// This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
|
||
// because the effect is also where times bubble to parent Profilers.
|
||
enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor.
|
||
// Do not reset these values until the next render so DevTools has a chance to read them first.
|
||
var parentFiber = finishedWork.return;
|
||
outer: while (parentFiber !== null) {
|
||
switch (parentFiber.tag) {
|
||
case HostRoot:
|
||
var root = parentFiber.stateNode;
|
||
root.effectDuration += effectDuration;
|
||
break outer;
|
||
case Profiler:
|
||
var parentStateNode = parentFiber.stateNode;
|
||
parentStateNode.effectDuration += effectDuration;
|
||
break outer;
|
||
}
|
||
parentFiber = parentFiber.return;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
|
||
break;
|
||
}
|
||
case SuspenseListComponent:
|
||
case IncompleteClassComponent:
|
||
case ScopeComponent:
|
||
case OffscreenComponent:
|
||
case LegacyHiddenComponent:
|
||
case TracingMarkerComponent:
|
||
{
|
||
break;
|
||
}
|
||
default:
|
||
throw new Error('This unit of work tag should not have side-effects. This error is ' + 'likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
}
|
||
if (!offscreenSubtreeWasHidden) {
|
||
{
|
||
if (finishedWork.flags & Ref) {
|
||
commitAttachRef(finishedWork);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function reappearLayoutEffectsOnFiber(node) {
|
||
// Turn on layout effects in a tree that previously disappeared.
|
||
// TODO (Offscreen) Check: flags & LayoutStatic
|
||
switch (node.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (node.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
safelyCallCommitHookLayoutEffectListMount(node, node.return);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(node);
|
||
}
|
||
}
|
||
else {
|
||
safelyCallCommitHookLayoutEffectListMount(node, node.return);
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var instance = node.stateNode;
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
safelyCallComponentDidMount(node, node.return, instance);
|
||
}
|
||
safelyAttachRef(node, node.return);
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
safelyAttachRef(node, node.return);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||
// Only hide or unhide the top-most host nodes.
|
||
var hostSubtreeRoot = null;
|
||
if (supportsMutation) {
|
||
// We only have the top Fiber that was inserted but we need to recurse down its
|
||
// children to find all the terminal nodes.
|
||
var node = finishedWork;
|
||
while (true) {
|
||
if (node.tag === HostComponent) {
|
||
if (hostSubtreeRoot === null) {
|
||
hostSubtreeRoot = node;
|
||
try {
|
||
var instance = node.stateNode;
|
||
if (isHidden) {
|
||
hideInstance(instance);
|
||
}
|
||
else {
|
||
unhideInstance(node.stateNode, node.memoizedProps);
|
||
}
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
else if (node.tag === HostText) {
|
||
if (hostSubtreeRoot === null) {
|
||
try {
|
||
var _instance3 = node.stateNode;
|
||
if (isHidden) {
|
||
hideTextInstance(_instance3);
|
||
}
|
||
else {
|
||
unhideTextInstance(_instance3, node.memoizedProps);
|
||
}
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
else if ((node.tag === OffscreenComponent || node.tag === LegacyHiddenComponent) && node.memoizedState !== null && node !== finishedWork)
|
||
;
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === finishedWork) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === finishedWork) {
|
||
return;
|
||
}
|
||
if (hostSubtreeRoot === node) {
|
||
hostSubtreeRoot = null;
|
||
}
|
||
node = node.return;
|
||
}
|
||
if (hostSubtreeRoot === node) {
|
||
hostSubtreeRoot = null;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
}
|
||
function commitAttachRef(finishedWork) {
|
||
var ref = finishedWork.ref;
|
||
if (ref !== null) {
|
||
var instance = finishedWork.stateNode;
|
||
var instanceToUse;
|
||
switch (finishedWork.tag) {
|
||
case HostComponent:
|
||
instanceToUse = getPublicInstance(instance);
|
||
break;
|
||
default:
|
||
instanceToUse = instance;
|
||
} // Moved outside to ensure DCE works with this flag
|
||
if (typeof ref === 'function') {
|
||
var retVal;
|
||
if (finishedWork.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
retVal = ref(instanceToUse);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(finishedWork);
|
||
}
|
||
}
|
||
else {
|
||
retVal = ref(instanceToUse);
|
||
}
|
||
{
|
||
if (typeof retVal === 'function') {
|
||
error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(finishedWork));
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
{
|
||
if (!ref.hasOwnProperty('current')) {
|
||
error('Unexpected ref object provided for %s. ' + 'Use either a ref-setter function or React.createRef().', getComponentNameFromFiber(finishedWork));
|
||
}
|
||
}
|
||
ref.current = instanceToUse;
|
||
}
|
||
}
|
||
}
|
||
function detachFiberMutation(fiber) {
|
||
// Cut off the return pointer to disconnect it from the tree.
|
||
// This enables us to detect and warn against state updates on an unmounted component.
|
||
// It also prevents events from bubbling from within disconnected components.
|
||
//
|
||
// Ideally, we should also clear the child pointer of the parent alternate to let this
|
||
// get GC:ed but we don't know which for sure which parent is the current
|
||
// one so we'll settle for GC:ing the subtree of this child.
|
||
// This child itself will be GC:ed when the parent updates the next time.
|
||
//
|
||
// Note that we can't clear child or sibling pointers yet.
|
||
// They're needed for passive effects and for findDOMNode.
|
||
// We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
|
||
//
|
||
// Don't reset the alternate yet, either. We need that so we can detach the
|
||
// alternate's fields in the passive phase. Clearing the return pointer is
|
||
// sufficient for findDOMNode semantics.
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null) {
|
||
alternate.return = null;
|
||
}
|
||
fiber.return = null;
|
||
}
|
||
function detachFiberAfterEffects(fiber) {
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null) {
|
||
fiber.alternate = null;
|
||
detachFiberAfterEffects(alternate);
|
||
} // Note: Defensively using negation instead of < in case
|
||
// `deletedTreeCleanUpLevel` is undefined.
|
||
{
|
||
// Clear cyclical Fiber fields. This level alone is designed to roughly
|
||
// approximate the planned Fiber refactor. In that world, `setState` will be
|
||
// bound to a special "instance" object instead of a Fiber. The Instance
|
||
// object will not have any of these fields. It will only be connected to
|
||
// the fiber tree via a single link at the root. So if this level alone is
|
||
// sufficient to fix memory issues, that bodes well for our plans.
|
||
fiber.child = null;
|
||
fiber.deletions = null;
|
||
fiber.sibling = null; // The `stateNode` is cyclical because on host nodes it points to the host
|
||
// tree, which has its own pointers to children, parents, and siblings.
|
||
// The other host nodes also point back to fibers, so we should detach that
|
||
// one, too.
|
||
if (fiber.tag === HostComponent) {
|
||
var hostInstance = fiber.stateNode;
|
||
if (hostInstance !== null) {
|
||
detachDeletedInstance(hostInstance);
|
||
}
|
||
}
|
||
fiber.stateNode = null; // I'm intentionally not clearing the `return` field in this level. We
|
||
// already disconnect the `return` pointer at the root of the deleted
|
||
// subtree (in `detachFiberMutation`). Besides, `return` by itself is not
|
||
// cyclical — it's only cyclical when combined with `child`, `sibling`, and
|
||
// `alternate`. But we'll clear it in the next level anyway, just in case.
|
||
{
|
||
fiber._debugOwner = null;
|
||
}
|
||
{
|
||
// Theoretically, nothing in here should be necessary, because we already
|
||
// disconnected the fiber from the tree. So even if something leaks this
|
||
// particular fiber, it won't leak anything else
|
||
//
|
||
// The purpose of this branch is to be super aggressive so we can measure
|
||
// if there's any difference in memory impact. If there is, that could
|
||
// indicate a React leak we don't know about.
|
||
fiber.return = null;
|
||
fiber.dependencies = null;
|
||
fiber.memoizedProps = null;
|
||
fiber.memoizedState = null;
|
||
fiber.pendingProps = null;
|
||
fiber.stateNode = null; // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
|
||
fiber.updateQueue = null;
|
||
}
|
||
}
|
||
}
|
||
function emptyPortalContainer(current) {
|
||
if (!supportsPersistence) {
|
||
return;
|
||
}
|
||
var portal = current.stateNode;
|
||
var containerInfo = portal.containerInfo;
|
||
var emptyChildSet = createContainerChildSet(containerInfo);
|
||
replaceContainerChildren(containerInfo, emptyChildSet);
|
||
}
|
||
function getHostParentFiber(fiber) {
|
||
var parent = fiber.return;
|
||
while (parent !== null) {
|
||
if (isHostParent(parent)) {
|
||
return parent;
|
||
}
|
||
parent = parent.return;
|
||
}
|
||
throw new Error('Expected to find a host parent. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
||
}
|
||
function isHostParent(fiber) {
|
||
return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal;
|
||
}
|
||
function getHostSibling(fiber) {
|
||
// We're going to search forward into the tree until we find a sibling host
|
||
// node. Unfortunately, if multiple insertions are done in a row we have to
|
||
// search past them. This leads to exponential search for the next sibling.
|
||
// TODO: Find a more efficient way to do this.
|
||
var node = fiber;
|
||
siblings: while (true) {
|
||
// If we didn't find anything, let's try the next sibling.
|
||
while (node.sibling === null) {
|
||
if (node.return === null || isHostParent(node.return)) {
|
||
// If we pop out of the root or hit the parent the fiber we are the
|
||
// last sibling.
|
||
return null;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedFragment) {
|
||
// If it is not host node and, we might have a host node inside it.
|
||
// Try to search down until we find one.
|
||
if (node.flags & Placement) {
|
||
// If we don't have a child, try the siblings instead.
|
||
continue siblings;
|
||
} // If we don't have a child, try the siblings instead.
|
||
// We also skip portals because they are not part of this host tree.
|
||
if (node.child === null || node.tag === HostPortal) {
|
||
continue siblings;
|
||
}
|
||
else {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
}
|
||
} // Check if this host node is stable or about to be placed.
|
||
if (!(node.flags & Placement)) {
|
||
// Found it!
|
||
return node.stateNode;
|
||
}
|
||
}
|
||
}
|
||
function commitPlacement(finishedWork) {
|
||
if (!supportsMutation) {
|
||
return;
|
||
} // Recursively insert all host nodes into the parent.
|
||
var parentFiber = getHostParentFiber(finishedWork); // Note: these two variables *must* always be updated together.
|
||
switch (parentFiber.tag) {
|
||
case HostComponent:
|
||
{
|
||
var parent = parentFiber.stateNode;
|
||
if (parentFiber.flags & ContentReset) {
|
||
// Reset the text content of the parent before doing any insertions
|
||
resetTextContent(parent); // Clear ContentReset from the effect tag
|
||
parentFiber.flags &= ~ContentReset;
|
||
}
|
||
var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
|
||
// children to find all the terminal nodes.
|
||
insertOrAppendPlacementNode(finishedWork, before, parent);
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
case HostPortal:
|
||
{
|
||
var _parent = parentFiber.stateNode.containerInfo;
|
||
var _before = getHostSibling(finishedWork);
|
||
insertOrAppendPlacementNodeIntoContainer(finishedWork, _before, _parent);
|
||
break;
|
||
}
|
||
// eslint-disable-next-line-no-fallthrough
|
||
default:
|
||
throw new Error('Invalid host parent fiber. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
||
}
|
||
}
|
||
function insertOrAppendPlacementNodeIntoContainer(node, before, parent) {
|
||
var tag = node.tag;
|
||
var isHost = tag === HostComponent || tag === HostText;
|
||
if (isHost) {
|
||
var stateNode = node.stateNode;
|
||
if (before) {
|
||
insertInContainerBefore(parent, stateNode, before);
|
||
}
|
||
else {
|
||
appendChildToContainer(parent, stateNode);
|
||
}
|
||
}
|
||
else if (tag === HostPortal)
|
||
;
|
||
else {
|
||
var child = node.child;
|
||
if (child !== null) {
|
||
insertOrAppendPlacementNodeIntoContainer(child, before, parent);
|
||
var sibling = child.sibling;
|
||
while (sibling !== null) {
|
||
insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
|
||
sibling = sibling.sibling;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function insertOrAppendPlacementNode(node, before, parent) {
|
||
var tag = node.tag;
|
||
var isHost = tag === HostComponent || tag === HostText;
|
||
if (isHost) {
|
||
var stateNode = node.stateNode;
|
||
if (before) {
|
||
insertBefore(parent, stateNode, before);
|
||
}
|
||
else {
|
||
appendChild(parent, stateNode);
|
||
}
|
||
}
|
||
else if (tag === HostPortal)
|
||
;
|
||
else {
|
||
var child = node.child;
|
||
if (child !== null) {
|
||
insertOrAppendPlacementNode(child, before, parent);
|
||
var sibling = child.sibling;
|
||
while (sibling !== null) {
|
||
insertOrAppendPlacementNode(sibling, before, parent);
|
||
sibling = sibling.sibling;
|
||
}
|
||
}
|
||
}
|
||
} // These are tracked on the stack as we recursively traverse a
|
||
// deleted subtree.
|
||
// TODO: Update these during the whole mutation phase, not just during
|
||
// a deletion.
|
||
var hostParent = null;
|
||
var hostParentIsContainer = false;
|
||
function commitDeletionEffects(root, returnFiber, deletedFiber) {
|
||
if (supportsMutation) {
|
||
// We only have the top Fiber that was deleted but we need to recurse down its
|
||
// children to find all the terminal nodes.
|
||
// Recursively delete all host nodes from the parent, detach refs, clean
|
||
// up mounted layout effects, and call componentWillUnmount.
|
||
// We only need to remove the topmost host child in each branch. But then we
|
||
// still need to keep traversing to unmount effects, refs, and cWU. TODO: We
|
||
// could split this into two separate traversals functions, where the second
|
||
// one doesn't include any removeChild logic. This is maybe the same
|
||
// function as "disappearLayoutEffects" (or whatever that turns into after
|
||
// the layout phase is refactored to use recursion).
|
||
// Before starting, find the nearest host parent on the stack so we know
|
||
// which instance/container to remove the children from.
|
||
// TODO: Instead of searching up the fiber return path on every deletion, we
|
||
// can track the nearest host component on the JS stack as we traverse the
|
||
// tree during the commit phase. This would make insertions faster, too.
|
||
var parent = returnFiber;
|
||
findParent: while (parent !== null) {
|
||
switch (parent.tag) {
|
||
case HostComponent:
|
||
{
|
||
hostParent = parent.stateNode;
|
||
hostParentIsContainer = false;
|
||
break findParent;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
hostParent = parent.stateNode.containerInfo;
|
||
hostParentIsContainer = true;
|
||
break findParent;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
hostParent = parent.stateNode.containerInfo;
|
||
hostParentIsContainer = true;
|
||
break findParent;
|
||
}
|
||
}
|
||
parent = parent.return;
|
||
}
|
||
if (hostParent === null) {
|
||
throw new Error('Expected to find a host parent. This error is likely caused by ' + 'a bug in React. Please file an issue.');
|
||
}
|
||
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
|
||
hostParent = null;
|
||
hostParentIsContainer = false;
|
||
}
|
||
else {
|
||
// Detach refs and call componentWillUnmount() on the whole subtree.
|
||
commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
|
||
}
|
||
detachFiberMutation(deletedFiber);
|
||
}
|
||
function recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, parent) {
|
||
// TODO: Use a static flag to skip trees that don't have unmount effects
|
||
var child = parent.child;
|
||
while (child !== null) {
|
||
commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
function commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, deletedFiber) {
|
||
onCommitUnmount(deletedFiber); // The cases in this outer switch modify the stack before they traverse
|
||
// into their subtree. There are simpler cases in the inner switch
|
||
// that don't modify the stack.
|
||
switch (deletedFiber.tag) {
|
||
case HostComponent:
|
||
{
|
||
if (!offscreenSubtreeWasHidden) {
|
||
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
||
} // Intentional fallthrough to next branch
|
||
}
|
||
// eslint-disable-next-line-no-fallthrough
|
||
case HostText:
|
||
{
|
||
// We only need to remove the nearest host child. Set the host parent
|
||
// to `null` on the stack to indicate that nested children don't
|
||
// need to be removed.
|
||
if (supportsMutation) {
|
||
var prevHostParent = hostParent;
|
||
var prevHostParentIsContainer = hostParentIsContainer;
|
||
hostParent = null;
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
hostParent = prevHostParent;
|
||
hostParentIsContainer = prevHostParentIsContainer;
|
||
if (hostParent !== null) {
|
||
// Now that all the child effects have unmounted, we can remove the
|
||
// node from the tree.
|
||
if (hostParentIsContainer) {
|
||
removeChildFromContainer(hostParent, deletedFiber.stateNode);
|
||
}
|
||
else {
|
||
removeChild(hostParent, deletedFiber.stateNode);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
}
|
||
return;
|
||
}
|
||
case DehydratedFragment:
|
||
{
|
||
// Delete the dehydrated suspense boundary and all of its content.
|
||
if (supportsMutation) {
|
||
if (hostParent !== null) {
|
||
if (hostParentIsContainer) {
|
||
clearSuspenseBoundaryFromContainer(hostParent, deletedFiber.stateNode);
|
||
}
|
||
else {
|
||
clearSuspenseBoundary(hostParent, deletedFiber.stateNode);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
if (supportsMutation) {
|
||
// When we go into a portal, it becomes the parent to remove from.
|
||
var _prevHostParent = hostParent;
|
||
var _prevHostParentIsContainer = hostParentIsContainer;
|
||
hostParent = deletedFiber.stateNode.containerInfo;
|
||
hostParentIsContainer = true;
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
hostParent = _prevHostParent;
|
||
hostParentIsContainer = _prevHostParentIsContainer;
|
||
}
|
||
else {
|
||
emptyPortalContainer(deletedFiber);
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
}
|
||
return;
|
||
}
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (!offscreenSubtreeWasHidden) {
|
||
var updateQueue = deletedFiber.updateQueue;
|
||
if (updateQueue !== null) {
|
||
var lastEffect = updateQueue.lastEffect;
|
||
if (lastEffect !== null) {
|
||
var firstEffect = lastEffect.next;
|
||
var effect = firstEffect;
|
||
do {
|
||
var _effect = effect, destroy = _effect.destroy, tag = _effect.tag;
|
||
if (destroy !== undefined) {
|
||
if ((tag & Insertion) !== NoFlags$1) {
|
||
safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
||
}
|
||
else if ((tag & Layout) !== NoFlags$1) {
|
||
{
|
||
markComponentLayoutEffectUnmountStarted(deletedFiber);
|
||
}
|
||
if (deletedFiber.mode & ProfileMode) {
|
||
startLayoutEffectTimer();
|
||
safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
||
recordLayoutEffectDuration(deletedFiber);
|
||
}
|
||
else {
|
||
safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
||
}
|
||
{
|
||
markComponentLayoutEffectUnmountStopped();
|
||
}
|
||
}
|
||
}
|
||
effect = effect.next;
|
||
} while (effect !== firstEffect);
|
||
}
|
||
}
|
||
}
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
if (!offscreenSubtreeWasHidden) {
|
||
safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
||
var instance = deletedFiber.stateNode;
|
||
if (typeof instance.componentWillUnmount === 'function') {
|
||
safelyCallComponentWillUnmount(deletedFiber, nearestMountedAncestor, instance);
|
||
}
|
||
}
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
return;
|
||
}
|
||
case ScopeComponent:
|
||
{
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
return;
|
||
}
|
||
case OffscreenComponent:
|
||
{
|
||
if ( // TODO: Remove this dead flag
|
||
deletedFiber.mode & ConcurrentMode) {
|
||
// If this offscreen component is hidden, we already unmounted it. Before
|
||
// deleting the children, track that it's already unmounted so that we
|
||
// don't attempt to unmount the effects again.
|
||
// TODO: If the tree is hidden, in most cases we should be able to skip
|
||
// over the nested children entirely. An exception is we haven't yet found
|
||
// the topmost host node to delete, which we already track on the stack.
|
||
// But the other case is portals, which need to be detached no matter how
|
||
// deeply they are nested. We should use a subtree flag to track whether a
|
||
// subtree includes a nested portal.
|
||
var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
||
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
||
}
|
||
else {
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
function commitSuspenseCallback(finishedWork) {
|
||
// TODO: Move this to passive phase
|
||
var newState = finishedWork.memoizedState;
|
||
}
|
||
function commitSuspenseHydrationCallbacks(finishedRoot, finishedWork) {
|
||
if (!supportsHydration) {
|
||
return;
|
||
}
|
||
var newState = finishedWork.memoizedState;
|
||
if (newState === null) {
|
||
var current = finishedWork.alternate;
|
||
if (current !== null) {
|
||
var prevState = current.memoizedState;
|
||
if (prevState !== null) {
|
||
var suspenseInstance = prevState.dehydrated;
|
||
if (suspenseInstance !== null) {
|
||
commitHydratedSuspenseInstance(suspenseInstance);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function attachSuspenseRetryListeners(finishedWork) {
|
||
// If this boundary just timed out, then it will have a set of wakeables.
|
||
// For each wakeable, attach a listener so that when it resolves, React
|
||
// attempts to re-render the boundary in the primary (pre-timeout) state.
|
||
var wakeables = finishedWork.updateQueue;
|
||
if (wakeables !== null) {
|
||
finishedWork.updateQueue = null;
|
||
var retryCache = finishedWork.stateNode;
|
||
if (retryCache === null) {
|
||
retryCache = finishedWork.stateNode = new PossiblyWeakSet();
|
||
}
|
||
wakeables.forEach(function (wakeable) {
|
||
// Memoize using the boundary fiber to prevent redundant listeners.
|
||
var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
|
||
if (!retryCache.has(wakeable)) {
|
||
retryCache.add(wakeable);
|
||
{
|
||
if (isDevToolsPresent) {
|
||
if (inProgressLanes !== null && inProgressRoot !== null) {
|
||
// If we have pending work still, associate the original updaters with it.
|
||
restorePendingUpdaters(inProgressRoot, inProgressLanes);
|
||
}
|
||
else {
|
||
throw Error('Expected finished root and lanes to be set. This is a bug in React.');
|
||
}
|
||
}
|
||
}
|
||
wakeable.then(retry, retry);
|
||
}
|
||
});
|
||
}
|
||
} // This function detects when a Suspense boundary goes from visible to hidden.
|
||
function commitMutationEffects(root, finishedWork, committedLanes) {
|
||
inProgressLanes = committedLanes;
|
||
inProgressRoot = root;
|
||
setCurrentFiber(finishedWork);
|
||
commitMutationEffectsOnFiber(finishedWork, root);
|
||
setCurrentFiber(finishedWork);
|
||
inProgressLanes = null;
|
||
inProgressRoot = null;
|
||
}
|
||
function recursivelyTraverseMutationEffects(root, parentFiber, lanes) {
|
||
// Deletions effects can be scheduled on any fiber type. They need to happen
|
||
// before the children effects hae fired.
|
||
var deletions = parentFiber.deletions;
|
||
if (deletions !== null) {
|
||
for (var i = 0; i < deletions.length; i++) {
|
||
var childToDelete = deletions[i];
|
||
try {
|
||
commitDeletionEffects(root, parentFiber, childToDelete);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(childToDelete, parentFiber, error);
|
||
}
|
||
}
|
||
}
|
||
var prevDebugFiber = getCurrentFiber();
|
||
if (parentFiber.subtreeFlags & MutationMask) {
|
||
var child = parentFiber.child;
|
||
while (child !== null) {
|
||
setCurrentFiber(child);
|
||
commitMutationEffectsOnFiber(child, root);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
setCurrentFiber(prevDebugFiber);
|
||
}
|
||
function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
|
||
var current = finishedWork.alternate;
|
||
var flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber,
|
||
// because the fiber tag is more specific. An exception is any flag related
|
||
// to reconcilation, because those can be set on all fiber types.
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Update) {
|
||
try {
|
||
commitHookEffectListUnmount(Insertion | HasEffect, finishedWork, finishedWork.return);
|
||
commitHookEffectListMount(Insertion | HasEffect, finishedWork);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
} // Layout effects are destroyed during the mutation phase so that all
|
||
// destroy functions for all fibers are called before any create functions.
|
||
// This prevents sibling component effects from interfering with each other,
|
||
// e.g. a destroy function in one component should never override a ref set
|
||
// by a create function in another component during the same commit.
|
||
if (finishedWork.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
recordLayoutEffectDuration(finishedWork);
|
||
}
|
||
else {
|
||
try {
|
||
commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Ref) {
|
||
if (current !== null) {
|
||
safelyDetachRef(current, current.return);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Ref) {
|
||
if (current !== null) {
|
||
safelyDetachRef(current, current.return);
|
||
}
|
||
}
|
||
if (supportsMutation) {
|
||
// TODO: ContentReset gets cleared by the children during the commit
|
||
// phase. This is a refactor hazard because it means we must read
|
||
// flags the flags after `commitReconciliationEffects` has already run;
|
||
// the order matters. We should refactor so that ContentReset does not
|
||
// rely on mutating the flag during commit. Like by setting a flag
|
||
// during the render phase instead.
|
||
if (finishedWork.flags & ContentReset) {
|
||
var instance = finishedWork.stateNode;
|
||
try {
|
||
resetTextContent(instance);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
if (flags & Update) {
|
||
var _instance4 = finishedWork.stateNode;
|
||
if (_instance4 != null) {
|
||
// Commit the work prepared earlier.
|
||
var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps
|
||
// as the newProps. The updatePayload will contain the real change in
|
||
// this case.
|
||
var oldProps = current !== null ? current.memoizedProps : newProps;
|
||
var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components.
|
||
var updatePayload = finishedWork.updateQueue;
|
||
finishedWork.updateQueue = null;
|
||
if (updatePayload !== null) {
|
||
try {
|
||
commitUpdate(_instance4, updatePayload, type, oldProps, newProps, finishedWork);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostText:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Update) {
|
||
if (supportsMutation) {
|
||
if (finishedWork.stateNode === null) {
|
||
throw new Error('This should have a text node initialized. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
||
}
|
||
var textInstance = finishedWork.stateNode;
|
||
var newText = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps
|
||
// as the newProps. The updatePayload will contain the real change in
|
||
// this case.
|
||
var oldText = current !== null ? current.memoizedProps : newText;
|
||
try {
|
||
commitTextUpdate(textInstance, oldText, newText);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Update) {
|
||
if (supportsMutation && supportsHydration) {
|
||
if (current !== null) {
|
||
var prevRootState = current.memoizedState;
|
||
if (prevRootState.isDehydrated) {
|
||
try {
|
||
commitHydratedContainer(root.containerInfo);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (supportsPersistence) {
|
||
var containerInfo = root.containerInfo;
|
||
var pendingChildren = root.pendingChildren;
|
||
try {
|
||
replaceContainerChildren(containerInfo, pendingChildren);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Update) {
|
||
if (supportsPersistence) {
|
||
var portal = finishedWork.stateNode;
|
||
var _containerInfo = portal.containerInfo;
|
||
var _pendingChildren = portal.pendingChildren;
|
||
try {
|
||
replaceContainerChildren(_containerInfo, _pendingChildren);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
var offscreenFiber = finishedWork.child;
|
||
if (offscreenFiber.flags & Visibility) {
|
||
var offscreenInstance = offscreenFiber.stateNode;
|
||
var newState = offscreenFiber.memoizedState;
|
||
var isHidden = newState !== null; // Track the current state on the Offscreen instance so we can
|
||
// read it during an event
|
||
offscreenInstance.isHidden = isHidden;
|
||
if (isHidden) {
|
||
var wasHidden = offscreenFiber.alternate !== null && offscreenFiber.alternate.memoizedState !== null;
|
||
if (!wasHidden) {
|
||
// TODO: Move to passive phase
|
||
markCommitTimeOfFallback();
|
||
}
|
||
}
|
||
}
|
||
if (flags & Update) {
|
||
try {
|
||
commitSuspenseCallback(finishedWork);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
}
|
||
attachSuspenseRetryListeners(finishedWork);
|
||
}
|
||
return;
|
||
}
|
||
case OffscreenComponent:
|
||
{
|
||
var _wasHidden = current !== null && current.memoizedState !== null;
|
||
if ( // TODO: Remove this dead flag
|
||
finishedWork.mode & ConcurrentMode) {
|
||
// Before committing the children, track on the stack whether this
|
||
// offscreen subtree was already hidden, so that we don't unmount the
|
||
// effects again.
|
||
var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
||
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || _wasHidden;
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
||
}
|
||
else {
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
}
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Visibility) {
|
||
var _offscreenInstance = finishedWork.stateNode;
|
||
var _newState = finishedWork.memoizedState;
|
||
var _isHidden = _newState !== null;
|
||
var offscreenBoundary = finishedWork; // Track the current state on the Offscreen instance so we can
|
||
// read it during an event
|
||
_offscreenInstance.isHidden = _isHidden;
|
||
{
|
||
if (_isHidden) {
|
||
if (!_wasHidden) {
|
||
if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
|
||
nextEffect = offscreenBoundary;
|
||
var offscreenChild = offscreenBoundary.child;
|
||
while (offscreenChild !== null) {
|
||
nextEffect = offscreenChild;
|
||
disappearLayoutEffects_begin(offscreenChild);
|
||
offscreenChild = offscreenChild.sibling;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (supportsMutation) {
|
||
// TODO: This needs to run whenever there's an insertion or update
|
||
// inside a hidden Offscreen tree.
|
||
hideOrUnhideAllChildren(offscreenBoundary, _isHidden);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case SuspenseListComponent:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
if (flags & Update) {
|
||
attachSuspenseRetryListeners(finishedWork);
|
||
}
|
||
return;
|
||
}
|
||
case ScopeComponent:
|
||
{
|
||
return;
|
||
}
|
||
default:
|
||
{
|
||
recursivelyTraverseMutationEffects(root, finishedWork);
|
||
commitReconciliationEffects(finishedWork);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
function commitReconciliationEffects(finishedWork) {
|
||
// Placement effects (insertions, reorders) can be scheduled on any fiber
|
||
// type. They needs to happen after the children effects have fired, but
|
||
// before the effects on this fiber have fired.
|
||
var flags = finishedWork.flags;
|
||
if (flags & Placement) {
|
||
try {
|
||
commitPlacement(finishedWork);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
||
} // Clear the "placement" from effect tag so that we know that this is
|
||
// inserted, before any life-cycles like componentDidMount gets called.
|
||
// TODO: findDOMNode doesn't rely on this any more but isMounted does
|
||
// and isMounted is deprecated anyway so we should be able to kill this.
|
||
finishedWork.flags &= ~Placement;
|
||
}
|
||
if (flags & Hydrating) {
|
||
finishedWork.flags &= ~Hydrating;
|
||
}
|
||
}
|
||
function commitLayoutEffects(finishedWork, root, committedLanes) {
|
||
inProgressLanes = committedLanes;
|
||
inProgressRoot = root;
|
||
nextEffect = finishedWork;
|
||
commitLayoutEffects_begin(finishedWork, root, committedLanes);
|
||
inProgressLanes = null;
|
||
inProgressRoot = null;
|
||
}
|
||
function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) {
|
||
// Suspense layout effects semantics don't change for legacy roots.
|
||
var isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var firstChild = fiber.child;
|
||
if (fiber.tag === OffscreenComponent && isModernRoot) {
|
||
// Keep track of the current Offscreen stack's state.
|
||
var isHidden = fiber.memoizedState !== null;
|
||
var newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
|
||
if (newOffscreenSubtreeIsHidden) {
|
||
// The Offscreen tree is hidden. Skip over its layout effects.
|
||
commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
||
continue;
|
||
}
|
||
else {
|
||
// TODO (Offscreen) Also check: subtreeFlags & LayoutMask
|
||
var current = fiber.alternate;
|
||
var wasHidden = current !== null && current.memoizedState !== null;
|
||
var newOffscreenSubtreeWasHidden = wasHidden || offscreenSubtreeWasHidden;
|
||
var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
|
||
var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden; // Traverse the Offscreen subtree with the current Offscreen as the root.
|
||
offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
|
||
offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
|
||
if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
|
||
// This is the root of a reappearing boundary. Turn its layout effects
|
||
// back on.
|
||
nextEffect = fiber;
|
||
reappearLayoutEffects_begin(fiber);
|
||
}
|
||
var child = firstChild;
|
||
while (child !== null) {
|
||
nextEffect = child;
|
||
commitLayoutEffects_begin(child, // New root; bubble back up to here and stop.
|
||
root, committedLanes);
|
||
child = child.sibling;
|
||
} // Restore Offscreen state and resume in our-progress traversal.
|
||
nextEffect = fiber;
|
||
offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
|
||
offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
||
commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
||
continue;
|
||
}
|
||
}
|
||
if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) {
|
||
firstChild.return = fiber;
|
||
nextEffect = firstChild;
|
||
}
|
||
else {
|
||
commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
||
}
|
||
}
|
||
}
|
||
function commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
if ((fiber.flags & LayoutMask) !== NoFlags) {
|
||
var current = fiber.alternate;
|
||
setCurrentFiber(fiber);
|
||
try {
|
||
commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
resetCurrentFiber();
|
||
}
|
||
if (fiber === subtreeRoot) {
|
||
nextEffect = null;
|
||
return;
|
||
}
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function disappearLayoutEffects_begin(subtreeRoot) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var firstChild = fiber.child; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (fiber.mode & ProfileMode) {
|
||
try {
|
||
startLayoutEffectTimer();
|
||
commitHookEffectListUnmount(Layout, fiber, fiber.return);
|
||
}
|
||
finally {
|
||
recordLayoutEffectDuration(fiber);
|
||
}
|
||
}
|
||
else {
|
||
commitHookEffectListUnmount(Layout, fiber, fiber.return);
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
// TODO (Offscreen) Check: flags & RefStatic
|
||
safelyDetachRef(fiber, fiber.return);
|
||
var instance = fiber.stateNode;
|
||
if (typeof instance.componentWillUnmount === 'function') {
|
||
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
||
}
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
safelyDetachRef(fiber, fiber.return);
|
||
break;
|
||
}
|
||
case OffscreenComponent:
|
||
{
|
||
// Check if this is a
|
||
var isHidden = fiber.memoizedState !== null;
|
||
if (isHidden) {
|
||
// Nested Offscreen tree is already hidden. Don't disappear
|
||
// its effects.
|
||
disappearLayoutEffects_complete(subtreeRoot);
|
||
continue;
|
||
}
|
||
break;
|
||
}
|
||
} // TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||
if (firstChild !== null) {
|
||
firstChild.return = fiber;
|
||
nextEffect = firstChild;
|
||
}
|
||
else {
|
||
disappearLayoutEffects_complete(subtreeRoot);
|
||
}
|
||
}
|
||
}
|
||
function disappearLayoutEffects_complete(subtreeRoot) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
if (fiber === subtreeRoot) {
|
||
nextEffect = null;
|
||
return;
|
||
}
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function reappearLayoutEffects_begin(subtreeRoot) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var firstChild = fiber.child;
|
||
if (fiber.tag === OffscreenComponent) {
|
||
var isHidden = fiber.memoizedState !== null;
|
||
if (isHidden) {
|
||
// Nested Offscreen tree is still hidden. Don't re-appear its effects.
|
||
reappearLayoutEffects_complete(subtreeRoot);
|
||
continue;
|
||
}
|
||
} // TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
||
if (firstChild !== null) {
|
||
// This node may have been reused from a previous render, so we can't
|
||
// assume its return pointer is correct.
|
||
firstChild.return = fiber;
|
||
nextEffect = firstChild;
|
||
}
|
||
else {
|
||
reappearLayoutEffects_complete(subtreeRoot);
|
||
}
|
||
}
|
||
}
|
||
function reappearLayoutEffects_complete(subtreeRoot) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect; // TODO (Offscreen) Check: flags & LayoutStatic
|
||
setCurrentFiber(fiber);
|
||
try {
|
||
reappearLayoutEffectsOnFiber(fiber);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
resetCurrentFiber();
|
||
if (fiber === subtreeRoot) {
|
||
nextEffect = null;
|
||
return;
|
||
}
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
// This node may have been reused from a previous render, so we can't
|
||
// assume its return pointer is correct.
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function commitPassiveMountEffects(root, finishedWork, committedLanes, committedTransitions) {
|
||
nextEffect = finishedWork;
|
||
commitPassiveMountEffects_begin(finishedWork, root, committedLanes, committedTransitions);
|
||
}
|
||
function commitPassiveMountEffects_begin(subtreeRoot, root, committedLanes, committedTransitions) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var firstChild = fiber.child;
|
||
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
|
||
firstChild.return = fiber;
|
||
nextEffect = firstChild;
|
||
}
|
||
else {
|
||
commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions);
|
||
}
|
||
}
|
||
}
|
||
function commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
if ((fiber.flags & Passive) !== NoFlags) {
|
||
setCurrentFiber(fiber);
|
||
try {
|
||
commitPassiveMountOnFiber(root, fiber, committedLanes, committedTransitions);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
resetCurrentFiber();
|
||
}
|
||
if (fiber === subtreeRoot) {
|
||
nextEffect = null;
|
||
return;
|
||
}
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function commitPassiveMountOnFiber(finishedRoot, finishedWork, committedLanes, committedTransitions) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (finishedWork.mode & ProfileMode) {
|
||
startPassiveEffectTimer();
|
||
try {
|
||
commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);
|
||
}
|
||
finally {
|
||
recordPassiveEffectDuration(finishedWork);
|
||
}
|
||
}
|
||
else {
|
||
commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function commitPassiveUnmountEffects(firstChild) {
|
||
nextEffect = firstChild;
|
||
commitPassiveUnmountEffects_begin();
|
||
}
|
||
function commitPassiveUnmountEffects_begin() {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var child = fiber.child;
|
||
if ((nextEffect.flags & ChildDeletion) !== NoFlags) {
|
||
var deletions = fiber.deletions;
|
||
if (deletions !== null) {
|
||
for (var i = 0; i < deletions.length; i++) {
|
||
var fiberToDelete = deletions[i];
|
||
nextEffect = fiberToDelete;
|
||
commitPassiveUnmountEffectsInsideOfDeletedTree_begin(fiberToDelete, fiber);
|
||
}
|
||
{
|
||
// A fiber was deleted from this parent fiber, but it's still part of
|
||
// the previous (alternate) parent fiber's list of children. Because
|
||
// children are a linked list, an earlier sibling that's still alive
|
||
// will be connected to the deleted fiber via its `alternate`:
|
||
//
|
||
// live fiber
|
||
// --alternate--> previous live fiber
|
||
// --sibling--> deleted fiber
|
||
//
|
||
// We can't disconnect `alternate` on nodes that haven't been deleted
|
||
// yet, but we can disconnect the `sibling` and `child` pointers.
|
||
var previousFiber = fiber.alternate;
|
||
if (previousFiber !== null) {
|
||
var detachedChild = previousFiber.child;
|
||
if (detachedChild !== null) {
|
||
previousFiber.child = null;
|
||
do {
|
||
var detachedSibling = detachedChild.sibling;
|
||
detachedChild.sibling = null;
|
||
detachedChild = detachedSibling;
|
||
} while (detachedChild !== null);
|
||
}
|
||
}
|
||
}
|
||
nextEffect = fiber;
|
||
}
|
||
}
|
||
if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
|
||
child.return = fiber;
|
||
nextEffect = child;
|
||
}
|
||
else {
|
||
commitPassiveUnmountEffects_complete();
|
||
}
|
||
}
|
||
}
|
||
function commitPassiveUnmountEffects_complete() {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
if ((fiber.flags & Passive) !== NoFlags) {
|
||
setCurrentFiber(fiber);
|
||
commitPassiveUnmountOnFiber(fiber);
|
||
resetCurrentFiber();
|
||
}
|
||
var sibling = fiber.sibling;
|
||
if (sibling !== null) {
|
||
sibling.return = fiber.return;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = fiber.return;
|
||
}
|
||
}
|
||
function commitPassiveUnmountOnFiber(finishedWork) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (finishedWork.mode & ProfileMode) {
|
||
startPassiveEffectTimer();
|
||
commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return);
|
||
recordPassiveEffectDuration(finishedWork);
|
||
}
|
||
else {
|
||
commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(deletedSubtreeRoot, nearestMountedAncestor) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect; // Deletion effects fire in parent -> child order
|
||
// TODO: Check if fiber has a PassiveStatic flag
|
||
setCurrentFiber(fiber);
|
||
commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
|
||
resetCurrentFiber();
|
||
var child = fiber.child; // TODO: Only traverse subtree if it has a PassiveStatic flag. (But, if we
|
||
// do this, still need to handle `deletedTreeCleanUpLevel` correctly.)
|
||
if (child !== null) {
|
||
child.return = fiber;
|
||
nextEffect = child;
|
||
}
|
||
else {
|
||
commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot);
|
||
}
|
||
}
|
||
}
|
||
function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot) {
|
||
while (nextEffect !== null) {
|
||
var fiber = nextEffect;
|
||
var sibling = fiber.sibling;
|
||
var returnFiber = fiber.return;
|
||
{
|
||
// Recursively traverse the entire deleted tree and clean up fiber fields.
|
||
// This is more aggressive than ideal, and the long term goal is to only
|
||
// have to detach the deleted tree at the root.
|
||
detachFiberAfterEffects(fiber);
|
||
if (fiber === deletedSubtreeRoot) {
|
||
nextEffect = null;
|
||
return;
|
||
}
|
||
}
|
||
if (sibling !== null) {
|
||
sibling.return = returnFiber;
|
||
nextEffect = sibling;
|
||
return;
|
||
}
|
||
nextEffect = returnFiber;
|
||
}
|
||
}
|
||
function commitPassiveUnmountInsideDeletedTreeOnFiber(current, nearestMountedAncestor) {
|
||
switch (current.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if (current.mode & ProfileMode) {
|
||
startPassiveEffectTimer();
|
||
commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor);
|
||
recordPassiveEffectDuration(current);
|
||
}
|
||
else {
|
||
commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
} // TODO: Reuse reappearLayoutEffects traversal here?
|
||
function invokeLayoutEffectMountInDEV(fiber) {
|
||
{
|
||
// We don't need to re-check StrictEffectsMode here.
|
||
// This function is only called if that check has already passed.
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
try {
|
||
commitHookEffectListMount(Layout | HasEffect, fiber);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var instance = fiber.stateNode;
|
||
try {
|
||
instance.componentDidMount();
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function invokePassiveEffectMountInDEV(fiber) {
|
||
{
|
||
// We don't need to re-check StrictEffectsMode here.
|
||
// This function is only called if that check has already passed.
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
try {
|
||
commitHookEffectListMount(Passive$1 | HasEffect, fiber);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function invokeLayoutEffectUnmountInDEV(fiber) {
|
||
{
|
||
// We don't need to re-check StrictEffectsMode here.
|
||
// This function is only called if that check has already passed.
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
try {
|
||
commitHookEffectListUnmount(Layout | HasEffect, fiber, fiber.return);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var instance = fiber.stateNode;
|
||
if (typeof instance.componentWillUnmount === 'function') {
|
||
safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function invokePassiveEffectUnmountInDEV(fiber) {
|
||
{
|
||
// We don't need to re-check StrictEffectsMode here.
|
||
// This function is only called if that check has already passed.
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
try {
|
||
commitHookEffectListUnmount(Passive$1 | HasEffect, fiber, fiber.return);
|
||
}
|
||
catch (error) {
|
||
captureCommitPhaseError(fiber, fiber.return, error);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var COMPONENT_TYPE = 0;
|
||
var HAS_PSEUDO_CLASS_TYPE = 1;
|
||
var ROLE_TYPE = 2;
|
||
var TEST_NAME_TYPE = 3;
|
||
var TEXT_TYPE = 4;
|
||
if (typeof Symbol === 'function' && Symbol.for) {
|
||
var symbolFor = Symbol.for;
|
||
COMPONENT_TYPE = symbolFor('selector.component');
|
||
HAS_PSEUDO_CLASS_TYPE = symbolFor('selector.has_pseudo_class');
|
||
ROLE_TYPE = symbolFor('selector.role');
|
||
TEST_NAME_TYPE = symbolFor('selector.test_id');
|
||
TEXT_TYPE = symbolFor('selector.text');
|
||
}
|
||
function createComponentSelector(component) {
|
||
return {
|
||
$$typeof: COMPONENT_TYPE,
|
||
value: component
|
||
};
|
||
}
|
||
function createHasPseudoClassSelector(selectors) {
|
||
return {
|
||
$$typeof: HAS_PSEUDO_CLASS_TYPE,
|
||
value: selectors
|
||
};
|
||
}
|
||
function createRoleSelector(role) {
|
||
return {
|
||
$$typeof: ROLE_TYPE,
|
||
value: role
|
||
};
|
||
}
|
||
function createTextSelector(text) {
|
||
return {
|
||
$$typeof: TEXT_TYPE,
|
||
value: text
|
||
};
|
||
}
|
||
function createTestNameSelector(id) {
|
||
return {
|
||
$$typeof: TEST_NAME_TYPE,
|
||
value: id
|
||
};
|
||
}
|
||
function findFiberRootForHostRoot(hostRoot) {
|
||
var maybeFiber = getInstanceFromNode(hostRoot);
|
||
if (maybeFiber != null) {
|
||
if (typeof maybeFiber.memoizedProps['data-testname'] !== 'string') {
|
||
throw new Error('Invalid host root specified. Should be either a React container or a node with a testname attribute.');
|
||
}
|
||
return maybeFiber;
|
||
}
|
||
else {
|
||
var fiberRoot = findFiberRoot(hostRoot);
|
||
if (fiberRoot === null) {
|
||
throw new Error('Could not find React container within specified host subtree.');
|
||
} // The Flow type for FiberRoot is a little funky.
|
||
// createFiberRoot() cheats this by treating the root as :any and adding stateNode lazily.
|
||
return fiberRoot.stateNode.current;
|
||
}
|
||
}
|
||
function matchSelector(fiber, selector) {
|
||
switch (selector.$$typeof) {
|
||
case COMPONENT_TYPE:
|
||
if (fiber.type === selector.value) {
|
||
return true;
|
||
}
|
||
break;
|
||
case HAS_PSEUDO_CLASS_TYPE:
|
||
return hasMatchingPaths(fiber, selector.value);
|
||
case ROLE_TYPE:
|
||
if (fiber.tag === HostComponent) {
|
||
var node = fiber.stateNode;
|
||
if (matchAccessibilityRole(node, selector.value)) {
|
||
return true;
|
||
}
|
||
}
|
||
break;
|
||
case TEXT_TYPE:
|
||
if (fiber.tag === HostComponent || fiber.tag === HostText) {
|
||
var textContent = getTextContent(fiber);
|
||
if (textContent !== null && textContent.indexOf(selector.value) >= 0) {
|
||
return true;
|
||
}
|
||
}
|
||
break;
|
||
case TEST_NAME_TYPE:
|
||
if (fiber.tag === HostComponent) {
|
||
var dataTestID = fiber.memoizedProps['data-testname'];
|
||
if (typeof dataTestID === 'string' && dataTestID.toLowerCase() === selector.value.toLowerCase()) {
|
||
return true;
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
throw new Error('Invalid selector type specified.');
|
||
}
|
||
return false;
|
||
}
|
||
function selectorToString(selector) {
|
||
switch (selector.$$typeof) {
|
||
case COMPONENT_TYPE:
|
||
var displayName = getComponentNameFromType(selector.value) || 'Unknown';
|
||
return "<" + displayName + ">";
|
||
case HAS_PSEUDO_CLASS_TYPE:
|
||
return ":has(" + (selectorToString(selector) || '') + ")";
|
||
case ROLE_TYPE:
|
||
return "[role=\"" + selector.value + "\"]";
|
||
case TEXT_TYPE:
|
||
return "\"" + selector.value + "\"";
|
||
case TEST_NAME_TYPE:
|
||
return "[data-testname=\"" + selector.value + "\"]";
|
||
default:
|
||
throw new Error('Invalid selector type specified.');
|
||
}
|
||
}
|
||
function findPaths(root, selectors) {
|
||
var matchingFibers = [];
|
||
var stack = [root, 0];
|
||
var index = 0;
|
||
while (index < stack.length) {
|
||
var fiber = stack[index++];
|
||
var selectorIndex = stack[index++];
|
||
var selector = selectors[selectorIndex];
|
||
if (fiber.tag === HostComponent && isHiddenSubtree(fiber)) {
|
||
continue;
|
||
}
|
||
else {
|
||
while (selector != null && matchSelector(fiber, selector)) {
|
||
selectorIndex++;
|
||
selector = selectors[selectorIndex];
|
||
}
|
||
}
|
||
if (selectorIndex === selectors.length) {
|
||
matchingFibers.push(fiber);
|
||
}
|
||
else {
|
||
var child = fiber.child;
|
||
while (child !== null) {
|
||
stack.push(child, selectorIndex);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
}
|
||
return matchingFibers;
|
||
} // Same as findPaths but with eager bailout on first match
|
||
function hasMatchingPaths(root, selectors) {
|
||
var stack = [root, 0];
|
||
var index = 0;
|
||
while (index < stack.length) {
|
||
var fiber = stack[index++];
|
||
var selectorIndex = stack[index++];
|
||
var selector = selectors[selectorIndex];
|
||
if (fiber.tag === HostComponent && isHiddenSubtree(fiber)) {
|
||
continue;
|
||
}
|
||
else {
|
||
while (selector != null && matchSelector(fiber, selector)) {
|
||
selectorIndex++;
|
||
selector = selectors[selectorIndex];
|
||
}
|
||
}
|
||
if (selectorIndex === selectors.length) {
|
||
return true;
|
||
}
|
||
else {
|
||
var child = fiber.child;
|
||
while (child !== null) {
|
||
stack.push(child, selectorIndex);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
function findAllNodes(hostRoot, selectors) {
|
||
if (!supportsTestSelectors) {
|
||
throw new Error('Test selector API is not supported by this renderer.');
|
||
}
|
||
var root = findFiberRootForHostRoot(hostRoot);
|
||
var matchingFibers = findPaths(root, selectors);
|
||
var instanceRoots = [];
|
||
var stack = Array.from(matchingFibers);
|
||
var index = 0;
|
||
while (index < stack.length) {
|
||
var node = stack[index++];
|
||
if (node.tag === HostComponent) {
|
||
if (isHiddenSubtree(node)) {
|
||
continue;
|
||
}
|
||
instanceRoots.push(node.stateNode);
|
||
}
|
||
else {
|
||
var child = node.child;
|
||
while (child !== null) {
|
||
stack.push(child);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
}
|
||
return instanceRoots;
|
||
}
|
||
function getFindAllNodesFailureDescription(hostRoot, selectors) {
|
||
if (!supportsTestSelectors) {
|
||
throw new Error('Test selector API is not supported by this renderer.');
|
||
}
|
||
var root = findFiberRootForHostRoot(hostRoot);
|
||
var maxSelectorIndex = 0;
|
||
var matchedNames = []; // The logic of this loop should be kept in sync with findPaths()
|
||
var stack = [root, 0];
|
||
var index = 0;
|
||
while (index < stack.length) {
|
||
var fiber = stack[index++];
|
||
var selectorIndex = stack[index++];
|
||
var selector = selectors[selectorIndex];
|
||
if (fiber.tag === HostComponent && isHiddenSubtree(fiber)) {
|
||
continue;
|
||
}
|
||
else if (matchSelector(fiber, selector)) {
|
||
matchedNames.push(selectorToString(selector));
|
||
selectorIndex++;
|
||
if (selectorIndex > maxSelectorIndex) {
|
||
maxSelectorIndex = selectorIndex;
|
||
}
|
||
}
|
||
if (selectorIndex < selectors.length) {
|
||
var child = fiber.child;
|
||
while (child !== null) {
|
||
stack.push(child, selectorIndex);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
}
|
||
if (maxSelectorIndex < selectors.length) {
|
||
var unmatchedNames = [];
|
||
for (var i = maxSelectorIndex; i < selectors.length; i++) {
|
||
unmatchedNames.push(selectorToString(selectors[i]));
|
||
}
|
||
return 'findAllNodes was able to match part of the selector:\n' + (" " + matchedNames.join(' > ') + "\n\n") + 'No matching component was found for:\n' + (" " + unmatchedNames.join(' > '));
|
||
}
|
||
return null;
|
||
}
|
||
function findBoundingRects(hostRoot, selectors) {
|
||
if (!supportsTestSelectors) {
|
||
throw new Error('Test selector API is not supported by this renderer.');
|
||
}
|
||
var instanceRoots = findAllNodes(hostRoot, selectors);
|
||
var boundingRects = [];
|
||
for (var i = 0; i < instanceRoots.length; i++) {
|
||
boundingRects.push(getBoundingRect(instanceRoots[i]));
|
||
}
|
||
for (var _i = boundingRects.length - 1; _i > 0; _i--) {
|
||
var targetRect = boundingRects[_i];
|
||
var targetLeft = targetRect.x;
|
||
var targetRight = targetLeft + targetRect.width;
|
||
var targetTop = targetRect.y;
|
||
var targetBottom = targetTop + targetRect.height;
|
||
for (var j = _i - 1; j >= 0; j--) {
|
||
if (_i !== j) {
|
||
var otherRect = boundingRects[j];
|
||
var otherLeft = otherRect.x;
|
||
var otherRight = otherLeft + otherRect.width;
|
||
var otherTop = otherRect.y;
|
||
var otherBottom = otherTop + otherRect.height; // Merging all rects to the minimums set would be complicated,
|
||
// but we can handle the most common cases:
|
||
// 1. completely overlapping rects
|
||
// 2. adjacent rects that are the same width or height (e.g. items in a list)
|
||
//
|
||
// Even given the above constraints,
|
||
// we still won't end up with the fewest possible rects without doing multiple passes,
|
||
// but it's good enough for this purpose.
|
||
if (targetLeft >= otherLeft && targetTop >= otherTop && targetRight <= otherRight && targetBottom <= otherBottom) {
|
||
// Complete overlapping rects; remove the inner one.
|
||
boundingRects.splice(_i, 1);
|
||
break;
|
||
}
|
||
else if (targetLeft === otherLeft && targetRect.width === otherRect.width && !(otherBottom < targetTop) && !(otherTop > targetBottom)) {
|
||
// Adjacent vertical rects; merge them.
|
||
if (otherTop > targetTop) {
|
||
otherRect.height += otherTop - targetTop;
|
||
otherRect.y = targetTop;
|
||
}
|
||
if (otherBottom < targetBottom) {
|
||
otherRect.height = targetBottom - otherTop;
|
||
}
|
||
boundingRects.splice(_i, 1);
|
||
break;
|
||
}
|
||
else if (targetTop === otherTop && targetRect.height === otherRect.height && !(otherRight < targetLeft) && !(otherLeft > targetRight)) {
|
||
// Adjacent horizontal rects; merge them.
|
||
if (otherLeft > targetLeft) {
|
||
otherRect.width += otherLeft - targetLeft;
|
||
otherRect.x = targetLeft;
|
||
}
|
||
if (otherRight < targetRight) {
|
||
otherRect.width = targetRight - otherLeft;
|
||
}
|
||
boundingRects.splice(_i, 1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return boundingRects;
|
||
}
|
||
function focusWithin(hostRoot, selectors) {
|
||
if (!supportsTestSelectors) {
|
||
throw new Error('Test selector API is not supported by this renderer.');
|
||
}
|
||
var root = findFiberRootForHostRoot(hostRoot);
|
||
var matchingFibers = findPaths(root, selectors);
|
||
var stack = Array.from(matchingFibers);
|
||
var index = 0;
|
||
while (index < stack.length) {
|
||
var fiber = stack[index++];
|
||
if (isHiddenSubtree(fiber)) {
|
||
continue;
|
||
}
|
||
if (fiber.tag === HostComponent) {
|
||
var node = fiber.stateNode;
|
||
if (setFocusIfFocusable(node)) {
|
||
return true;
|
||
}
|
||
}
|
||
var child = fiber.child;
|
||
while (child !== null) {
|
||
stack.push(child);
|
||
child = child.sibling;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
var commitHooks = [];
|
||
function onCommitRoot$1() {
|
||
if (supportsTestSelectors) {
|
||
commitHooks.forEach(function (commitHook) {
|
||
return commitHook();
|
||
});
|
||
}
|
||
}
|
||
function observeVisibleRects(hostRoot, selectors, callback, options) {
|
||
if (!supportsTestSelectors) {
|
||
throw new Error('Test selector API is not supported by this renderer.');
|
||
}
|
||
var instanceRoots = findAllNodes(hostRoot, selectors);
|
||
var _setupIntersectionObs = setupIntersectionObserver(instanceRoots, callback, options), disconnect = _setupIntersectionObs.disconnect, observe = _setupIntersectionObs.observe, unobserve = _setupIntersectionObs.unobserve; // When React mutates the host environment, we may need to change what we're listening to.
|
||
var commitHook = function () {
|
||
var nextInstanceRoots = findAllNodes(hostRoot, selectors);
|
||
instanceRoots.forEach(function (target) {
|
||
if (nextInstanceRoots.indexOf(target) < 0) {
|
||
unobserve(target);
|
||
}
|
||
});
|
||
nextInstanceRoots.forEach(function (target) {
|
||
if (instanceRoots.indexOf(target) < 0) {
|
||
observe(target);
|
||
}
|
||
});
|
||
};
|
||
commitHooks.push(commitHook);
|
||
return {
|
||
disconnect: function () {
|
||
// Stop listening for React mutations:
|
||
var index = commitHooks.indexOf(commitHook);
|
||
if (index >= 0) {
|
||
commitHooks.splice(index, 1);
|
||
} // Disconnect the host observer:
|
||
disconnect();
|
||
}
|
||
};
|
||
}
|
||
var ReactCurrentActQueue = ReactSharedInternals.ReactCurrentActQueue;
|
||
function isLegacyActEnvironment(fiber) {
|
||
{
|
||
// Legacy mode. We preserve the behavior of React 17's act. It assumes an
|
||
// act environment whenever `jest` is defined, but you can still turn off
|
||
// spurious warnings by setting IS_REACT_ACT_ENVIRONMENT explicitly
|
||
// to false.
|
||
var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
|
||
typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined; // $FlowExpectedError - Flow doesn't know about jest
|
||
var jestIsDefined = typeof jest !== 'undefined';
|
||
return warnsIfNotActing && jestIsDefined && isReactActEnvironmentGlobal !== false;
|
||
}
|
||
}
|
||
function isConcurrentActEnvironment() {
|
||
{
|
||
var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
|
||
typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined;
|
||
if (!isReactActEnvironmentGlobal && ReactCurrentActQueue.current !== null) {
|
||
// TODO: Include link to relevant documentation page.
|
||
error('The current testing environment is not configured to support ' + 'act(...)');
|
||
}
|
||
return isReactActEnvironmentGlobal;
|
||
}
|
||
}
|
||
var ceil = Math.ceil;
|
||
var ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner, ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig, ReactCurrentActQueue$1 = ReactSharedInternals.ReactCurrentActQueue;
|
||
var NoContext =
|
||
/* */
|
||
0;
|
||
var BatchedContext =
|
||
/* */
|
||
1;
|
||
var RenderContext =
|
||
/* */
|
||
2;
|
||
var CommitContext =
|
||
/* */
|
||
4;
|
||
var RootInProgress = 0;
|
||
var RootFatalErrored = 1;
|
||
var RootErrored = 2;
|
||
var RootSuspended = 3;
|
||
var RootSuspendedWithDelay = 4;
|
||
var RootCompleted = 5;
|
||
var RootDidNotComplete = 6; // Describes where we are in the React execution stack
|
||
var executionContext = NoContext; // The root we're working on
|
||
var workInProgressRoot = null; // The fiber we're working on
|
||
var workInProgress = null; // The lanes we're rendering
|
||
var workInProgressRootRenderLanes = NoLanes; // Stack that allows components to change the render lanes for its subtree
|
||
// This is a superset of the lanes we started working on at the root. The only
|
||
// case where it's different from `workInProgressRootRenderLanes` is when we
|
||
// enter a subtree that is hidden and needs to be unhidden: Suspense and
|
||
// Offscreen component.
|
||
//
|
||
// Most things in the work loop should deal with workInProgressRootRenderLanes.
|
||
// Most things in begin/complete phases should deal with subtreeRenderLanes.
|
||
var subtreeRenderLanes = NoLanes;
|
||
var subtreeRenderLanesCursor = createCursor(NoLanes); // Whether to root completed, errored, suspended, etc.
|
||
var workInProgressRootExitStatus = RootInProgress; // A fatal error, if one is thrown
|
||
var workInProgressRootFatalError = null; // "Included" lanes refer to lanes that were worked on during this render. It's
|
||
// slightly different than `renderLanes` because `renderLanes` can change as you
|
||
// enter and exit an Offscreen tree. This value is the combination of all render
|
||
// lanes for the entire render phase.
|
||
var workInProgressRootIncludedLanes = NoLanes; // The work left over by components that were visited during this render. Only
|
||
// includes unprocessed updates, not work in bailed out children.
|
||
var workInProgressRootSkippedLanes = NoLanes; // Lanes that were updated (in an interleaved event) during this render.
|
||
var workInProgressRootInterleavedUpdatedLanes = NoLanes; // Lanes that were updated during the render phase (*not* an interleaved event).
|
||
var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during the render phase.
|
||
var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
|
||
// We will log them once the tree commits.
|
||
var workInProgressRootRecoverableErrors = null; // The most recent time we committed a fallback. This lets us ensure a train
|
||
// model where we don't commit new loading states in too quick succession.
|
||
var globalMostRecentFallbackTime = 0;
|
||
var FALLBACK_THROTTLE_MS = 500; // The absolute time for when we should start giving up on rendering
|
||
// more and prefer CPU suspense heuristics instead.
|
||
var workInProgressRootRenderTargetTime = Infinity; // How long a render is supposed to take before we start following CPU
|
||
// suspense heuristics and opt out of rendering more content.
|
||
var RENDER_TIMEOUT_MS = 500;
|
||
var workInProgressTransitions = null;
|
||
function resetRenderTimer() {
|
||
workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;
|
||
}
|
||
function getRenderTargetTime() {
|
||
return workInProgressRootRenderTargetTime;
|
||
}
|
||
var hasUncaughtError = false;
|
||
var firstUncaughtError = null;
|
||
var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfilerNestedUpdateScheduledHook is true;
|
||
var rootDoesHavePassiveEffects = false;
|
||
var rootWithPendingPassiveEffects = null;
|
||
var pendingPassiveEffectsLanes = NoLanes;
|
||
var pendingPassiveProfilerEffects = [];
|
||
var pendingPassiveTransitions = null; // Use these to prevent an infinite loop of nested updates
|
||
var NESTED_UPDATE_LIMIT = 50;
|
||
var nestedUpdateCount = 0;
|
||
var rootWithNestedUpdates = null;
|
||
var isFlushingPassiveEffects = false;
|
||
var didScheduleUpdateDuringPassiveEffects = false;
|
||
var NESTED_PASSIVE_UPDATE_LIMIT = 50;
|
||
var nestedPassiveUpdateCount = 0;
|
||
var rootWithPassiveNestedUpdates = null; // If two updates are scheduled within the same event, we should treat their
|
||
// event times as simultaneous, even if the actual clock time has advanced
|
||
// between the first and second call.
|
||
var currentEventTime = NoTimestamp;
|
||
var currentEventTransitionLane = NoLanes;
|
||
var isRunningInsertionEffect = false;
|
||
function getWorkInProgressRoot() {
|
||
return workInProgressRoot;
|
||
}
|
||
function requestEventTime() {
|
||
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
||
// We're inside React, so it's fine to read the actual time.
|
||
return now();
|
||
} // We're not inside React, so we may be in the middle of a browser event.
|
||
if (currentEventTime !== NoTimestamp) {
|
||
// Use the same start time for all updates until we enter React again.
|
||
return currentEventTime;
|
||
} // This is the first update since React yielded. Compute a new start time.
|
||
currentEventTime = now();
|
||
return currentEventTime;
|
||
}
|
||
function requestUpdateLane(fiber) {
|
||
// Special cases
|
||
var mode = fiber.mode;
|
||
if ((mode & ConcurrentMode) === NoMode) {
|
||
return SyncLane;
|
||
}
|
||
else if ((executionContext & RenderContext) !== NoContext && workInProgressRootRenderLanes !== NoLanes) {
|
||
// This is a render phase update. These are not officially supported. The
|
||
// old behavior is to give this the same "thread" (lanes) as
|
||
// whatever is currently rendering. So if you call `setState` on a component
|
||
// that happens later in the same render, it will flush. Ideally, we want to
|
||
// remove the special case and treat them as if they came from an
|
||
// interleaved event. Regardless, this pattern is not officially supported.
|
||
// This behavior is only a fallback. The flag only exists until we can roll
|
||
// out the setState warning, since existing code might accidentally rely on
|
||
// the current behavior.
|
||
return pickArbitraryLane(workInProgressRootRenderLanes);
|
||
}
|
||
var isTransition = requestCurrentTransition() !== NoTransition;
|
||
if (isTransition) {
|
||
if (ReactCurrentBatchConfig$2.transition !== null) {
|
||
var transition = ReactCurrentBatchConfig$2.transition;
|
||
if (!transition._updatedFibers) {
|
||
transition._updatedFibers = new Set();
|
||
}
|
||
transition._updatedFibers.add(fiber);
|
||
} // The algorithm for assigning an update to a lane should be stable for all
|
||
// updates at the same priority within the same event. To do this, the
|
||
// inputs to the algorithm must be the same.
|
||
//
|
||
// The trick we use is to cache the first of each of these inputs within an
|
||
// event. Then reset the cached values once we can be sure the event is
|
||
// over. Our heuristic for that is whenever we enter a concurrent work loop.
|
||
if (currentEventTransitionLane === NoLane) {
|
||
// All transitions within the same event are assigned the same lane.
|
||
currentEventTransitionLane = claimNextTransitionLane();
|
||
}
|
||
return currentEventTransitionLane;
|
||
} // Updates originating inside certain React methods, like flushSync, have
|
||
// their priority set by tracking it with a context variable.
|
||
//
|
||
// The opaque type returned by the host config is internally a lane, so we can
|
||
// use that directly.
|
||
// TODO: Move this type conversion to the event priority module.
|
||
var updateLane = getCurrentUpdatePriority();
|
||
if (updateLane !== NoLane) {
|
||
return updateLane;
|
||
} // This update originated outside React. Ask the host environment for an
|
||
// appropriate priority, based on the type of event.
|
||
//
|
||
// The opaque type returned by the host config is internally a lane, so we can
|
||
// use that directly.
|
||
// TODO: Move this type conversion to the event priority module.
|
||
var eventLane = getCurrentEventPriority();
|
||
return eventLane;
|
||
}
|
||
function requestRetryLane(fiber) {
|
||
// This is a fork of `requestUpdateLane` designed specifically for Suspense
|
||
// "retries" — a special update that attempts to flip a Suspense boundary
|
||
// from its placeholder state to its primary/resolved state.
|
||
// Special cases
|
||
var mode = fiber.mode;
|
||
if ((mode & ConcurrentMode) === NoMode) {
|
||
return SyncLane;
|
||
}
|
||
return claimNextRetryLane();
|
||
}
|
||
function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {
|
||
checkForNestedUpdates();
|
||
{
|
||
if (isRunningInsertionEffect) {
|
||
error('useInsertionEffect must not schedule updates.');
|
||
}
|
||
}
|
||
{
|
||
if (isFlushingPassiveEffects) {
|
||
didScheduleUpdateDuringPassiveEffects = true;
|
||
}
|
||
} // Mark that the root has a pending update.
|
||
markRootUpdated(root, lane, eventTime);
|
||
if ((executionContext & RenderContext) !== NoLanes && root === workInProgressRoot) {
|
||
// This update was dispatched during the render phase. This is a mistake
|
||
// if the update originates from user space (with the exception of local
|
||
// hook updates, which are handled differently and don't reach this
|
||
// function), but there are some internal React features that use this as
|
||
// an implementation detail, like selective hydration.
|
||
warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase
|
||
}
|
||
else {
|
||
// This is a normal update, scheduled from outside the render phase. For
|
||
// example, during an input event.
|
||
{
|
||
if (isDevToolsPresent) {
|
||
addFiberToLanesMap(root, fiber, lane);
|
||
}
|
||
}
|
||
warnIfUpdatesNotWrappedWithActDEV(fiber);
|
||
if (root === workInProgressRoot) {
|
||
// Received an update to a tree that's in the middle of rendering. Mark
|
||
// that there was an interleaved update work on this root. Unless the
|
||
// `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
|
||
// phase update. In that case, we don't treat render phase updates as if
|
||
// they were interleaved, for backwards compat reasons.
|
||
if ((executionContext & RenderContext) === NoContext) {
|
||
workInProgressRootInterleavedUpdatedLanes = mergeLanes(workInProgressRootInterleavedUpdatedLanes, lane);
|
||
}
|
||
if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
|
||
// The root already suspended with a delay, which means this render
|
||
// definitely won't finish. Since we have a new update, let's mark it as
|
||
// suspended now, right before marking the incoming update. This has the
|
||
// effect of interrupting the current render and switching to the update.
|
||
// TODO: Make sure this doesn't override pings that happen while we've
|
||
// already started rendering.
|
||
markRootSuspended$1(root, workInProgressRootRenderLanes);
|
||
}
|
||
}
|
||
ensureRootIsScheduled(root, eventTime);
|
||
if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
|
||
!(ReactCurrentActQueue$1.isBatchingLegacy)) {
|
||
// Flush the synchronous work now, unless we're already working or inside
|
||
// a batch. This is intentionally inside scheduleUpdateOnFiber instead of
|
||
// scheduleCallbackForFiber to preserve the ability to schedule a callback
|
||
// without immediately flushing it. We only do this for user-initiated
|
||
// updates, to preserve historical behavior of legacy mode.
|
||
resetRenderTimer();
|
||
flushSyncCallbacksOnlyInLegacyMode();
|
||
}
|
||
}
|
||
}
|
||
function scheduleInitialHydrationOnRoot(root, lane, eventTime) {
|
||
// This is a special fork of scheduleUpdateOnFiber that is only used to
|
||
// schedule the initial hydration of a root that has just been created. Most
|
||
// of the stuff in scheduleUpdateOnFiber can be skipped.
|
||
//
|
||
// The main reason for this separate path, though, is to distinguish the
|
||
// initial children from subsequent updates. In fully client-rendered roots
|
||
// (createRoot instead of hydrateRoot), all top-level renders are modeled as
|
||
// updates, but hydration roots are special because the initial render must
|
||
// match what was rendered on the server.
|
||
var current = root.current;
|
||
current.lanes = lane;
|
||
markRootUpdated(root, lane, eventTime);
|
||
ensureRootIsScheduled(root, eventTime);
|
||
}
|
||
function isUnsafeClassRenderPhaseUpdate(fiber) {
|
||
// Check if this is a render phase update. Only called by class components,
|
||
// which special (deprecated) behavior for UNSAFE_componentWillReceive props.
|
||
return ( // TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
|
||
// decided not to enable it.
|
||
(executionContext & RenderContext) !== NoContext);
|
||
} // Use this function to schedule a task for a root. There's only one task per
|
||
// root; if a task was already scheduled, we'll check to make sure the priority
|
||
// of the existing task is the same as the priority of the next level that the
|
||
// root has work on. This function is called on every update, and right before
|
||
// exiting a task.
|
||
function ensureRootIsScheduled(root, currentTime) {
|
||
var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as
|
||
// expired so we know to work on those next.
|
||
markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.
|
||
var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);
|
||
if (nextLanes === NoLanes) {
|
||
// Special case: There's nothing to work on.
|
||
if (existingCallbackNode !== null) {
|
||
cancelCallback$1(existingCallbackNode);
|
||
}
|
||
root.callbackNode = null;
|
||
root.callbackPriority = NoLane;
|
||
return;
|
||
} // We use the highest priority lane to represent the priority of the callback.
|
||
var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it.
|
||
var existingCallbackPriority = root.callbackPriority;
|
||
if (existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a
|
||
// Scheduler task, rather than an `act` task, cancel it and re-scheduled
|
||
// on the `act` queue.
|
||
!(ReactCurrentActQueue$1.current !== null && existingCallbackNode !== fakeActCallbackNode)) {
|
||
{
|
||
// If we're going to re-use an existing task, it needs to exist.
|
||
// Assume that discrete update microtasks are non-cancellable and null.
|
||
// TODO: Temporary until we confirm this warning is not fired.
|
||
if (existingCallbackNode == null && existingCallbackPriority !== SyncLane) {
|
||
error('Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.');
|
||
}
|
||
} // The priority hasn't changed. We can reuse the existing task. Exit.
|
||
return;
|
||
}
|
||
if (existingCallbackNode != null) {
|
||
// Cancel the existing callback. We'll schedule a new one below.
|
||
cancelCallback$1(existingCallbackNode);
|
||
} // Schedule a new callback.
|
||
var newCallbackNode;
|
||
if (newCallbackPriority === SyncLane) {
|
||
// Special case: Sync React callbacks are scheduled on a special
|
||
// internal queue
|
||
if (root.tag === LegacyRoot) {
|
||
if (ReactCurrentActQueue$1.isBatchingLegacy !== null) {
|
||
ReactCurrentActQueue$1.didScheduleLegacyUpdate = true;
|
||
}
|
||
scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
|
||
}
|
||
else {
|
||
scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
|
||
}
|
||
if (supportsMicrotasks) {
|
||
// Flush the queue in a microtask.
|
||
if (ReactCurrentActQueue$1.current !== null) {
|
||
// Inside `act`, use our internal `act` queue so that these get flushed
|
||
// at the end of the current scope even when using the sync version
|
||
// of `act`.
|
||
ReactCurrentActQueue$1.current.push(flushSyncCallbacks);
|
||
}
|
||
else {
|
||
scheduleMicrotask(function () {
|
||
// In Safari, appending an iframe forces microtasks to run.
|
||
// https://github.com/facebook/react/issues/22459
|
||
// We don't support running callbacks in the middle of render
|
||
// or commit so we need to check against that.
|
||
if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
||
// Note that this would still prematurely flush the callbacks
|
||
// if this happens outside render or commit phase (e.g. in an event).
|
||
flushSyncCallbacks();
|
||
}
|
||
});
|
||
}
|
||
}
|
||
else {
|
||
// Flush the queue in an Immediate task.
|
||
scheduleCallback$1(ImmediatePriority, flushSyncCallbacks);
|
||
}
|
||
newCallbackNode = null;
|
||
}
|
||
else {
|
||
var schedulerPriorityLevel;
|
||
switch (lanesToEventPriority(nextLanes)) {
|
||
case DiscreteEventPriority:
|
||
schedulerPriorityLevel = ImmediatePriority;
|
||
break;
|
||
case ContinuousEventPriority:
|
||
schedulerPriorityLevel = UserBlockingPriority;
|
||
break;
|
||
case DefaultEventPriority:
|
||
schedulerPriorityLevel = NormalPriority;
|
||
break;
|
||
case IdleEventPriority:
|
||
schedulerPriorityLevel = IdlePriority;
|
||
break;
|
||
default:
|
||
schedulerPriorityLevel = NormalPriority;
|
||
break;
|
||
}
|
||
newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
|
||
}
|
||
root.callbackPriority = newCallbackPriority;
|
||
root.callbackNode = newCallbackNode;
|
||
} // This is the entry point for every concurrent task, i.e. anything that
|
||
// goes through Scheduler.
|
||
function performConcurrentWorkOnRoot(root, didTimeout) {
|
||
{
|
||
resetNestedUpdateFlag();
|
||
} // Since we know we're in a React event, we can clear the current
|
||
// event time. The next update will compute a new event time.
|
||
currentEventTime = NoTimestamp;
|
||
currentEventTransitionLane = NoLanes;
|
||
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
||
throw new Error('Should not already be working.');
|
||
} // Flush any pending passive effects before deciding which lanes to work on,
|
||
// in case they schedule additional work.
|
||
var originalCallbackNode = root.callbackNode;
|
||
var didFlushPassiveEffects = flushPassiveEffects();
|
||
if (didFlushPassiveEffects) {
|
||
// Something in the passive effect phase may have canceled the current task.
|
||
// Check if the task node for this root was changed.
|
||
if (root.callbackNode !== originalCallbackNode) {
|
||
// The current task was canceled. Exit. We don't need to call
|
||
// `ensureRootIsScheduled` because the check above implies either that
|
||
// there's a new task, or that there's no remaining work on this root.
|
||
return null;
|
||
}
|
||
} // Determine the next lanes to work on, using the fields stored
|
||
// on the root.
|
||
var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);
|
||
if (lanes === NoLanes) {
|
||
// Defensive coding. This is never expected to happen.
|
||
return null;
|
||
} // We disable time-slicing in some cases: if the work has been CPU-bound
|
||
// for too long ("expired" work, to prevent starvation), or we're in
|
||
// sync-updates-by-default mode.
|
||
// TODO: We only check `didTimeout` defensively, to account for a Scheduler
|
||
// bug we're still investigating. Once the bug in Scheduler is fixed,
|
||
// we can remove this, since we track expiration ourselves.
|
||
var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && (!didTimeout);
|
||
var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);
|
||
if (exitStatus !== RootInProgress) {
|
||
if (exitStatus === RootErrored) {
|
||
// If something threw an error, try rendering one more time. We'll
|
||
// render synchronously to block concurrent data mutations, and we'll
|
||
// includes all pending updates are included. If it still fails after
|
||
// the second attempt, we'll give up and commit the resulting tree.
|
||
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
||
if (errorRetryLanes !== NoLanes) {
|
||
lanes = errorRetryLanes;
|
||
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
|
||
}
|
||
}
|
||
if (exitStatus === RootFatalErrored) {
|
||
var fatalError = workInProgressRootFatalError;
|
||
prepareFreshStack(root, NoLanes);
|
||
markRootSuspended$1(root, lanes);
|
||
ensureRootIsScheduled(root, now());
|
||
throw fatalError;
|
||
}
|
||
if (exitStatus === RootDidNotComplete) {
|
||
// The render unwound without completing the tree. This happens in special
|
||
// cases where need to exit the current render without producing a
|
||
// consistent tree or committing.
|
||
//
|
||
// This should only happen during a concurrent render, not a discrete or
|
||
// synchronous update. We should have already checked for this when we
|
||
// unwound the stack.
|
||
markRootSuspended$1(root, lanes);
|
||
}
|
||
else {
|
||
// The render completed.
|
||
// Check if this render may have yielded to a concurrent event, and if so,
|
||
// confirm that any newly rendered stores are consistent.
|
||
// TODO: It's possible that even a concurrent render may never have yielded
|
||
// to the main thread, if it was fast enough, or if it expired. We could
|
||
// skip the consistency check in that case, too.
|
||
var renderWasConcurrent = !includesBlockingLane(root, lanes);
|
||
var finishedWork = root.current.alternate;
|
||
if (renderWasConcurrent && !isRenderConsistentWithExternalStores(finishedWork)) {
|
||
// A store was mutated in an interleaved event. Render again,
|
||
// synchronously, to block further mutations.
|
||
exitStatus = renderRootSync(root, lanes); // We need to check again if something threw
|
||
if (exitStatus === RootErrored) {
|
||
var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
||
if (_errorRetryLanes !== NoLanes) {
|
||
lanes = _errorRetryLanes;
|
||
exitStatus = recoverFromConcurrentError(root, _errorRetryLanes); // We assume the tree is now consistent because we didn't yield to any
|
||
// concurrent events.
|
||
}
|
||
}
|
||
if (exitStatus === RootFatalErrored) {
|
||
var _fatalError = workInProgressRootFatalError;
|
||
prepareFreshStack(root, NoLanes);
|
||
markRootSuspended$1(root, lanes);
|
||
ensureRootIsScheduled(root, now());
|
||
throw _fatalError;
|
||
}
|
||
} // We now have a consistent tree. The next step is either to commit it,
|
||
// or, if something suspended, wait to commit it after a timeout.
|
||
root.finishedWork = finishedWork;
|
||
root.finishedLanes = lanes;
|
||
finishConcurrentRender(root, exitStatus, lanes);
|
||
}
|
||
}
|
||
ensureRootIsScheduled(root, now());
|
||
if (root.callbackNode === originalCallbackNode) {
|
||
// The task node scheduled for this root is the same one that's
|
||
// currently executed. Need to return a continuation.
|
||
return performConcurrentWorkOnRoot.bind(null, root);
|
||
}
|
||
return null;
|
||
}
|
||
function recoverFromConcurrentError(root, errorRetryLanes) {
|
||
// If an error occurred during hydration, discard server response and fall
|
||
// back to client side render.
|
||
// Before rendering again, save the errors from the previous attempt.
|
||
var errorsFromFirstAttempt = workInProgressRootConcurrentErrors;
|
||
if (isRootDehydrated(root)) {
|
||
// The shell failed to hydrate. Set a flag to force a client rendering
|
||
// during the next attempt. To do this, we call prepareFreshStack now
|
||
// to create the root work-in-progress fiber. This is a bit weird in terms
|
||
// of factoring, because it relies on renderRootSync not calling
|
||
// prepareFreshStack again in the call below, which happens because the
|
||
// root and lanes haven't changed.
|
||
//
|
||
// TODO: I think what we should do is set ForceClientRender inside
|
||
// throwException, like we do for nested Suspense boundaries. The reason
|
||
// it's here instead is so we can switch to the synchronous work loop, too.
|
||
// Something to consider for a future refactor.
|
||
var rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);
|
||
rootWorkInProgress.flags |= ForceClientRender;
|
||
{
|
||
errorHydratingContainer(root.containerInfo);
|
||
}
|
||
}
|
||
var exitStatus = renderRootSync(root, errorRetryLanes);
|
||
if (exitStatus !== RootErrored) {
|
||
// Successfully finished rendering on retry
|
||
// The errors from the failed first attempt have been recovered. Add
|
||
// them to the collection of recoverable errors. We'll log them in the
|
||
// commit phase.
|
||
var errorsFromSecondAttempt = workInProgressRootRecoverableErrors;
|
||
workInProgressRootRecoverableErrors = errorsFromFirstAttempt; // The errors from the second attempt should be queued after the errors
|
||
// from the first attempt, to preserve the causal sequence.
|
||
if (errorsFromSecondAttempt !== null) {
|
||
queueRecoverableErrors(errorsFromSecondAttempt);
|
||
}
|
||
}
|
||
return exitStatus;
|
||
}
|
||
function queueRecoverableErrors(errors) {
|
||
if (workInProgressRootRecoverableErrors === null) {
|
||
workInProgressRootRecoverableErrors = errors;
|
||
}
|
||
else {
|
||
workInProgressRootRecoverableErrors.push.apply(workInProgressRootRecoverableErrors, errors);
|
||
}
|
||
}
|
||
function finishConcurrentRender(root, exitStatus, lanes) {
|
||
switch (exitStatus) {
|
||
case RootInProgress:
|
||
case RootFatalErrored:
|
||
{
|
||
throw new Error('Root did not complete. This is a bug in React.');
|
||
}
|
||
// Flow knows about invariant, so it complains if I add a break
|
||
// statement, but eslint doesn't know about invariant, so it complains
|
||
// if I do. eslint-disable-next-line no-fallthrough
|
||
case RootErrored:
|
||
{
|
||
// We should have already attempted to retry this tree. If we reached
|
||
// this point, it errored again. Commit it.
|
||
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
||
break;
|
||
}
|
||
case RootSuspended:
|
||
{
|
||
markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we
|
||
// should immediately commit it or wait a bit.
|
||
if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope
|
||
!shouldForceFlushFallbacksInDEV()) {
|
||
// This render only included retries, no updates. Throttle committing
|
||
// retries so that we don't show too many loading states too quickly.
|
||
var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.
|
||
if (msUntilTimeout > 10) {
|
||
var nextLanes = getNextLanes(root, NoLanes);
|
||
if (nextLanes !== NoLanes) {
|
||
// There's additional work on this root.
|
||
break;
|
||
}
|
||
var suspendedLanes = root.suspendedLanes;
|
||
if (!isSubsetOfLanes(suspendedLanes, lanes)) {
|
||
// We should prefer to render the fallback of at the last
|
||
// suspended level. Ping the last suspended level to try
|
||
// rendering it again.
|
||
// FIXME: What if the suspended lanes are Idle? Should not restart.
|
||
var eventTime = requestEventTime();
|
||
markRootPinged(root, suspendedLanes);
|
||
break;
|
||
} // The render is suspended, it hasn't timed out, and there's no
|
||
// lower priority work to do. Instead of committing the fallback
|
||
// immediately, wait for more data to arrive.
|
||
root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), msUntilTimeout);
|
||
break;
|
||
}
|
||
} // The work expired. Commit immediately.
|
||
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
||
break;
|
||
}
|
||
case RootSuspendedWithDelay:
|
||
{
|
||
markRootSuspended$1(root, lanes);
|
||
if (includesOnlyTransitions(lanes)) {
|
||
// This is a transition, so we should exit without committing a
|
||
// placeholder and without scheduling a timeout. Delay indefinitely
|
||
// until we receive more data.
|
||
break;
|
||
}
|
||
if (!shouldForceFlushFallbacksInDEV()) {
|
||
// This is not a transition, but we did trigger an avoided state.
|
||
// Schedule a placeholder to display after a short delay, using the Just
|
||
// Noticeable Difference.
|
||
// TODO: Is the JND optimization worth the added complexity? If this is
|
||
// the only reason we track the event time, then probably not.
|
||
// Consider removing.
|
||
var mostRecentEventTime = getMostRecentEventTime(root, lanes);
|
||
var eventTimeMs = mostRecentEventTime;
|
||
var timeElapsedMs = now() - eventTimeMs;
|
||
var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.
|
||
if (_msUntilTimeout > 10) {
|
||
// Instead of committing the fallback immediately, wait for more data
|
||
// to arrive.
|
||
root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), _msUntilTimeout);
|
||
break;
|
||
}
|
||
} // Commit the placeholder.
|
||
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
||
break;
|
||
}
|
||
case RootCompleted:
|
||
{
|
||
// The work completed. Ready to commit.
|
||
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
throw new Error('Unknown root exit status.');
|
||
}
|
||
}
|
||
}
|
||
function isRenderConsistentWithExternalStores(finishedWork) {
|
||
// Search the rendered tree for external store reads, and check whether the
|
||
// stores were mutated in a concurrent event. Intentionally using an iterative
|
||
// loop instead of recursion so we can exit early.
|
||
var node = finishedWork;
|
||
while (true) {
|
||
if (node.flags & StoreConsistency) {
|
||
var updateQueue = node.updateQueue;
|
||
if (updateQueue !== null) {
|
||
var checks = updateQueue.stores;
|
||
if (checks !== null) {
|
||
for (var i = 0; i < checks.length; i++) {
|
||
var check = checks[i];
|
||
var getSnapshot = check.getSnapshot;
|
||
var renderedValue = check.value;
|
||
try {
|
||
if (!objectIs(getSnapshot(), renderedValue)) {
|
||
// Found an inconsistent store.
|
||
return false;
|
||
}
|
||
}
|
||
catch (error) {
|
||
// If `getSnapshot` throws, return `false`. This will schedule
|
||
// a re-render, and the error will be rethrown during render.
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var child = node.child;
|
||
if (node.subtreeFlags & StoreConsistency && child !== null) {
|
||
child.return = node;
|
||
node = child;
|
||
continue;
|
||
}
|
||
if (node === finishedWork) {
|
||
return true;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === finishedWork) {
|
||
return true;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
} // Flow doesn't know this is unreachable, but eslint does
|
||
// eslint-disable-next-line no-unreachable
|
||
return true;
|
||
}
|
||
function markRootSuspended$1(root, suspendedLanes) {
|
||
// When suspending, we should always exclude lanes that were pinged or (more
|
||
// rarely, since we try to avoid it) updated during the render phase.
|
||
// TODO: Lol maybe there's a better way to factor this besides this
|
||
// obnoxiously named function :)
|
||
suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
|
||
suspendedLanes = removeLanes(suspendedLanes, workInProgressRootInterleavedUpdatedLanes);
|
||
markRootSuspended(root, suspendedLanes);
|
||
} // This is the entry point for synchronous tasks that don't go
|
||
// through Scheduler
|
||
function performSyncWorkOnRoot(root) {
|
||
{
|
||
syncNestedUpdateFlag();
|
||
}
|
||
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
||
throw new Error('Should not already be working.');
|
||
}
|
||
flushPassiveEffects();
|
||
var lanes = getNextLanes(root, NoLanes);
|
||
if (!includesSomeLane(lanes, SyncLane)) {
|
||
// There's no remaining sync work left.
|
||
ensureRootIsScheduled(root, now());
|
||
return null;
|
||
}
|
||
var exitStatus = renderRootSync(root, lanes);
|
||
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
|
||
// If something threw an error, try rendering one more time. We'll render
|
||
// synchronously to block concurrent data mutations, and we'll includes
|
||
// all pending updates are included. If it still fails after the second
|
||
// attempt, we'll give up and commit the resulting tree.
|
||
var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
||
if (errorRetryLanes !== NoLanes) {
|
||
lanes = errorRetryLanes;
|
||
exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
|
||
}
|
||
}
|
||
if (exitStatus === RootFatalErrored) {
|
||
var fatalError = workInProgressRootFatalError;
|
||
prepareFreshStack(root, NoLanes);
|
||
markRootSuspended$1(root, lanes);
|
||
ensureRootIsScheduled(root, now());
|
||
throw fatalError;
|
||
}
|
||
if (exitStatus === RootDidNotComplete) {
|
||
throw new Error('Root did not complete. This is a bug in React.');
|
||
} // We now have a consistent tree. Because this is a sync render, we
|
||
// will commit it even if something suspended.
|
||
var finishedWork = root.current.alternate;
|
||
root.finishedWork = finishedWork;
|
||
root.finishedLanes = lanes;
|
||
commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); // Before exiting, make sure there's a callback scheduled for the next
|
||
// pending level.
|
||
ensureRootIsScheduled(root, now());
|
||
return null;
|
||
}
|
||
function flushRoot(root, lanes) {
|
||
if (lanes !== NoLanes) {
|
||
markRootEntangled(root, mergeLanes(lanes, SyncLane));
|
||
ensureRootIsScheduled(root, now());
|
||
if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
||
resetRenderTimer();
|
||
flushSyncCallbacks();
|
||
}
|
||
}
|
||
}
|
||
function deferredUpdates(fn) {
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(DefaultEventPriority);
|
||
return fn();
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
}
|
||
}
|
||
function batchedUpdates(fn, a) {
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= BatchedContext;
|
||
try {
|
||
return fn(a);
|
||
}
|
||
finally {
|
||
executionContext = prevExecutionContext; // If there were legacy sync updates, flush them at the end of the outer
|
||
// most batchedUpdates-like method.
|
||
if (executionContext === NoContext && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
|
||
!(ReactCurrentActQueue$1.isBatchingLegacy)) {
|
||
resetRenderTimer();
|
||
flushSyncCallbacksOnlyInLegacyMode();
|
||
}
|
||
}
|
||
}
|
||
function discreteUpdates(fn, a, b, c, d) {
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
return fn(a, b, c, d);
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
if (executionContext === NoContext) {
|
||
resetRenderTimer();
|
||
}
|
||
}
|
||
} // Overload the definition to the two valid signatures.
|
||
// Warning, this opts-out of checking the function body.
|
||
// eslint-disable-next-line no-redeclare
|
||
function flushSync(fn) {
|
||
// In legacy mode, we flush pending passive effects at the beginning of the
|
||
// next event, not at the end of the previous one.
|
||
if (rootWithPendingPassiveEffects !== null && rootWithPendingPassiveEffects.tag === LegacyRoot && (executionContext & (RenderContext | CommitContext)) === NoContext) {
|
||
flushPassiveEffects();
|
||
}
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= BatchedContext;
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
if (fn) {
|
||
return fn();
|
||
}
|
||
else {
|
||
return undefined;
|
||
}
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.
|
||
// Note that this will happen even if batchedUpdates is higher up
|
||
// the stack.
|
||
if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
||
flushSyncCallbacks();
|
||
}
|
||
}
|
||
}
|
||
function isAlreadyRendering() {
|
||
// Used by the renderer to print a warning if certain APIs are called from
|
||
// the wrong context.
|
||
return (executionContext & (RenderContext | CommitContext)) !== NoContext;
|
||
}
|
||
function flushControlled(fn) {
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= BatchedContext;
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
fn();
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
executionContext = prevExecutionContext;
|
||
if (executionContext === NoContext) {
|
||
// Flush the immediate callbacks that were scheduled during this batch
|
||
resetRenderTimer();
|
||
flushSyncCallbacks();
|
||
}
|
||
}
|
||
}
|
||
function pushRenderLanes(fiber, lanes) {
|
||
push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);
|
||
subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);
|
||
workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);
|
||
}
|
||
function popRenderLanes(fiber) {
|
||
subtreeRenderLanes = subtreeRenderLanesCursor.current;
|
||
pop(subtreeRenderLanesCursor, fiber);
|
||
}
|
||
function prepareFreshStack(root, lanes) {
|
||
root.finishedWork = null;
|
||
root.finishedLanes = NoLanes;
|
||
var timeoutHandle = root.timeoutHandle;
|
||
if (timeoutHandle !== noTimeout) {
|
||
// The root previous suspended and scheduled a timeout to commit a fallback
|
||
// state. Now that we have additional work, cancel the timeout.
|
||
root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
|
||
cancelTimeout(timeoutHandle);
|
||
}
|
||
if (workInProgress !== null) {
|
||
var interruptedWork = workInProgress.return;
|
||
while (interruptedWork !== null) {
|
||
var current = interruptedWork.alternate;
|
||
unwindInterruptedWork(current, interruptedWork);
|
||
interruptedWork = interruptedWork.return;
|
||
}
|
||
}
|
||
workInProgressRoot = root;
|
||
var rootWorkInProgress = createWorkInProgress(root.current, null);
|
||
workInProgress = rootWorkInProgress;
|
||
workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
|
||
workInProgressRootExitStatus = RootInProgress;
|
||
workInProgressRootFatalError = null;
|
||
workInProgressRootSkippedLanes = NoLanes;
|
||
workInProgressRootInterleavedUpdatedLanes = NoLanes;
|
||
workInProgressRootPingedLanes = NoLanes;
|
||
workInProgressRootConcurrentErrors = null;
|
||
workInProgressRootRecoverableErrors = null;
|
||
finishQueueingConcurrentUpdates();
|
||
{
|
||
ReactStrictModeWarnings.discardPendingWarnings();
|
||
}
|
||
return rootWorkInProgress;
|
||
}
|
||
function handleError(root, thrownValue) {
|
||
do {
|
||
var erroredWork = workInProgress;
|
||
try {
|
||
// Reset module-level state that was set during the render phase.
|
||
resetContextDependencies();
|
||
resetHooksAfterThrow();
|
||
resetCurrentFiber(); // TODO: I found and added this missing line while investigating a
|
||
// separate issue. Write a regression test using string refs.
|
||
ReactCurrentOwner$2.current = null;
|
||
if (erroredWork === null || erroredWork.return === null) {
|
||
// Expected to be working on a non-root fiber. This is a fatal error
|
||
// because there's no ancestor that can handle it; the root is
|
||
// supposed to capture all errors that weren't caught by an error
|
||
// boundary.
|
||
workInProgressRootExitStatus = RootFatalErrored;
|
||
workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next
|
||
// sibling, or the parent if there are no siblings. But since the root
|
||
// has no siblings nor a parent, we set it to null. Usually this is
|
||
// handled by `completeUnitOfWork` or `unwindWork`, but since we're
|
||
// intentionally not calling those, we need set it here.
|
||
// TODO: Consider calling `unwindWork` to pop the contexts.
|
||
workInProgress = null;
|
||
return;
|
||
}
|
||
if (enableProfilerTimer && erroredWork.mode & ProfileMode) {
|
||
// Record the time spent rendering before an error was thrown. This
|
||
// avoids inaccurate Profiler durations in the case of a
|
||
// suspended render.
|
||
stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);
|
||
}
|
||
if (enableSchedulingProfiler) {
|
||
markComponentRenderStopped();
|
||
if (thrownValue !== null && typeof thrownValue === 'object' && typeof thrownValue.then === 'function') {
|
||
var wakeable = thrownValue;
|
||
markComponentSuspended(erroredWork, wakeable, workInProgressRootRenderLanes);
|
||
}
|
||
else {
|
||
markComponentErrored(erroredWork, thrownValue, workInProgressRootRenderLanes);
|
||
}
|
||
}
|
||
throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);
|
||
completeUnitOfWork(erroredWork);
|
||
}
|
||
catch (yetAnotherThrownValue) {
|
||
// Something in the return path also threw.
|
||
thrownValue = yetAnotherThrownValue;
|
||
if (workInProgress === erroredWork && erroredWork !== null) {
|
||
// If this boundary has already errored, then we had trouble processing
|
||
// the error. Bubble it to the next boundary.
|
||
erroredWork = erroredWork.return;
|
||
workInProgress = erroredWork;
|
||
}
|
||
else {
|
||
erroredWork = workInProgress;
|
||
}
|
||
continue;
|
||
} // Return to the normal work loop.
|
||
return;
|
||
} while (true);
|
||
}
|
||
function pushDispatcher() {
|
||
var prevDispatcher = ReactCurrentDispatcher$2.current;
|
||
ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;
|
||
if (prevDispatcher === null) {
|
||
// The React isomorphic package does not include a default dispatcher.
|
||
// Instead the first renderer will lazily attach one, in order to give
|
||
// nicer error messages.
|
||
return ContextOnlyDispatcher;
|
||
}
|
||
else {
|
||
return prevDispatcher;
|
||
}
|
||
}
|
||
function popDispatcher(prevDispatcher) {
|
||
ReactCurrentDispatcher$2.current = prevDispatcher;
|
||
}
|
||
function markCommitTimeOfFallback() {
|
||
globalMostRecentFallbackTime = now();
|
||
}
|
||
function markSkippedUpdateLanes(lane) {
|
||
workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);
|
||
}
|
||
function renderDidSuspend() {
|
||
if (workInProgressRootExitStatus === RootInProgress) {
|
||
workInProgressRootExitStatus = RootSuspended;
|
||
}
|
||
}
|
||
function renderDidSuspendDelayIfPossible() {
|
||
if (workInProgressRootExitStatus === RootInProgress || workInProgressRootExitStatus === RootSuspended || workInProgressRootExitStatus === RootErrored) {
|
||
workInProgressRootExitStatus = RootSuspendedWithDelay;
|
||
} // Check if there are updates that we skipped tree that might have unblocked
|
||
// this render.
|
||
if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))) {
|
||
// Mark the current render as suspended so that we switch to working on
|
||
// the updates that were skipped. Usually we only suspend at the end of
|
||
// the render phase.
|
||
// TODO: We should probably always mark the root as suspended immediately
|
||
// (inside this function), since by suspending at the end of the render
|
||
// phase introduces a potential mistake where we suspend lanes that were
|
||
// pinged or updated while we were rendering.
|
||
markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);
|
||
}
|
||
}
|
||
function renderDidError(error) {
|
||
if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {
|
||
workInProgressRootExitStatus = RootErrored;
|
||
}
|
||
if (workInProgressRootConcurrentErrors === null) {
|
||
workInProgressRootConcurrentErrors = [error];
|
||
}
|
||
else {
|
||
workInProgressRootConcurrentErrors.push(error);
|
||
}
|
||
} // Called during render to determine if anything has suspended.
|
||
// Returns false if we're not sure.
|
||
function renderHasNotSuspendedYet() {
|
||
// If something errored or completed, we can't really be sure,
|
||
// so those are false.
|
||
return workInProgressRootExitStatus === RootInProgress;
|
||
}
|
||
function renderRootSync(root, lanes) {
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= RenderContext;
|
||
var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack
|
||
// and prepare a fresh one. Otherwise we'll continue where we left off.
|
||
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
|
||
{
|
||
if (isDevToolsPresent) {
|
||
var memoizedUpdaters = root.memoizedUpdaters;
|
||
if (memoizedUpdaters.size > 0) {
|
||
restorePendingUpdaters(root, workInProgressRootRenderLanes);
|
||
memoizedUpdaters.clear();
|
||
} // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
|
||
// If we bailout on this work, we'll move them back (like above).
|
||
// It's important to move them now in case the work spawns more work at the same priority with different updaters.
|
||
// That way we can keep the current update and future updates separate.
|
||
movePendingFibersToMemoized(root, lanes);
|
||
}
|
||
}
|
||
workInProgressTransitions = getTransitionsForLanes();
|
||
prepareFreshStack(root, lanes);
|
||
}
|
||
{
|
||
markRenderStarted(lanes);
|
||
}
|
||
do {
|
||
try {
|
||
workLoopSync();
|
||
break;
|
||
}
|
||
catch (thrownValue) {
|
||
handleError(root, thrownValue);
|
||
}
|
||
} while (true);
|
||
resetContextDependencies();
|
||
executionContext = prevExecutionContext;
|
||
popDispatcher(prevDispatcher);
|
||
if (workInProgress !== null) {
|
||
// This is a sync render, so we should have finished the whole tree.
|
||
throw new Error('Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.');
|
||
}
|
||
{
|
||
markRenderStopped();
|
||
} // Set this to null to indicate there's no in-progress render.
|
||
workInProgressRoot = null;
|
||
workInProgressRootRenderLanes = NoLanes;
|
||
return workInProgressRootExitStatus;
|
||
} // The work loop is an extremely hot path. Tell Closure not to inline it.
|
||
/** @noinline */
|
||
function workLoopSync() {
|
||
// Already timed out, so perform work without checking if we need to yield.
|
||
while (workInProgress !== null) {
|
||
performUnitOfWork(workInProgress);
|
||
}
|
||
}
|
||
function renderRootConcurrent(root, lanes) {
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= RenderContext;
|
||
var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack
|
||
// and prepare a fresh one. Otherwise we'll continue where we left off.
|
||
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
|
||
{
|
||
if (isDevToolsPresent) {
|
||
var memoizedUpdaters = root.memoizedUpdaters;
|
||
if (memoizedUpdaters.size > 0) {
|
||
restorePendingUpdaters(root, workInProgressRootRenderLanes);
|
||
memoizedUpdaters.clear();
|
||
} // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
|
||
// If we bailout on this work, we'll move them back (like above).
|
||
// It's important to move them now in case the work spawns more work at the same priority with different updaters.
|
||
// That way we can keep the current update and future updates separate.
|
||
movePendingFibersToMemoized(root, lanes);
|
||
}
|
||
}
|
||
workInProgressTransitions = getTransitionsForLanes();
|
||
resetRenderTimer();
|
||
prepareFreshStack(root, lanes);
|
||
}
|
||
{
|
||
markRenderStarted(lanes);
|
||
}
|
||
do {
|
||
try {
|
||
workLoopConcurrent();
|
||
break;
|
||
}
|
||
catch (thrownValue) {
|
||
handleError(root, thrownValue);
|
||
}
|
||
} while (true);
|
||
resetContextDependencies();
|
||
popDispatcher(prevDispatcher);
|
||
executionContext = prevExecutionContext;
|
||
if (workInProgress !== null) {
|
||
// Still work remaining.
|
||
{
|
||
markRenderYielded();
|
||
}
|
||
return RootInProgress;
|
||
}
|
||
else {
|
||
// Completed the tree.
|
||
{
|
||
markRenderStopped();
|
||
} // Set this to null to indicate there's no in-progress render.
|
||
workInProgressRoot = null;
|
||
workInProgressRootRenderLanes = NoLanes; // Return the final exit status.
|
||
return workInProgressRootExitStatus;
|
||
}
|
||
}
|
||
/** @noinline */
|
||
function workLoopConcurrent() {
|
||
// Perform work until Scheduler asks us to yield
|
||
while (workInProgress !== null && !shouldYield()) {
|
||
performUnitOfWork(workInProgress);
|
||
}
|
||
}
|
||
function performUnitOfWork(unitOfWork) {
|
||
// The current, flushed, state of this fiber is the alternate. Ideally
|
||
// nothing should rely on this, but relying on it here means that we don't
|
||
// need an additional field on the work in progress.
|
||
var current = unitOfWork.alternate;
|
||
setCurrentFiber(unitOfWork);
|
||
var next;
|
||
if ((unitOfWork.mode & ProfileMode) !== NoMode) {
|
||
startProfilerTimer(unitOfWork);
|
||
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
|
||
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
|
||
}
|
||
else {
|
||
next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
|
||
}
|
||
resetCurrentFiber();
|
||
unitOfWork.memoizedProps = unitOfWork.pendingProps;
|
||
if (next === null) {
|
||
// If this doesn't spawn new work, complete the current work.
|
||
completeUnitOfWork(unitOfWork);
|
||
}
|
||
else {
|
||
workInProgress = next;
|
||
}
|
||
ReactCurrentOwner$2.current = null;
|
||
}
|
||
function completeUnitOfWork(unitOfWork) {
|
||
// Attempt to complete the current unit of work, then move to the next
|
||
// sibling. If there are no more siblings, return to the parent fiber.
|
||
var completedWork = unitOfWork;
|
||
do {
|
||
// The current, flushed, state of this fiber is the alternate. Ideally
|
||
// nothing should rely on this, but relying on it here means that we don't
|
||
// need an additional field on the work in progress.
|
||
var current = completedWork.alternate;
|
||
var returnFiber = completedWork.return; // Check if the work completed or if something threw.
|
||
if ((completedWork.flags & Incomplete) === NoFlags) {
|
||
setCurrentFiber(completedWork);
|
||
var next = void 0;
|
||
if ((completedWork.mode & ProfileMode) === NoMode) {
|
||
next = completeWork(current, completedWork, subtreeRenderLanes);
|
||
}
|
||
else {
|
||
startProfilerTimer(completedWork);
|
||
next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.
|
||
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
|
||
}
|
||
resetCurrentFiber();
|
||
if (next !== null) {
|
||
// Completing this fiber spawned new work. Work on that next.
|
||
workInProgress = next;
|
||
return;
|
||
}
|
||
}
|
||
else {
|
||
// This fiber did not complete because something threw. Pop values off
|
||
// the stack without entering the complete phase. If this is a boundary,
|
||
// capture values if possible.
|
||
var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes.
|
||
if (_next !== null) {
|
||
// If completing this work spawned new work, do that next. We'll come
|
||
// back here again.
|
||
// Since we're restarting, remove anything that is not a host effect
|
||
// from the effect tag.
|
||
_next.flags &= HostEffectMask;
|
||
workInProgress = _next;
|
||
return;
|
||
}
|
||
if ((completedWork.mode & ProfileMode) !== NoMode) {
|
||
// Record the render duration for the fiber that errored.
|
||
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.
|
||
var actualDuration = completedWork.actualDuration;
|
||
var child = completedWork.child;
|
||
while (child !== null) {
|
||
actualDuration += child.actualDuration;
|
||
child = child.sibling;
|
||
}
|
||
completedWork.actualDuration = actualDuration;
|
||
}
|
||
if (returnFiber !== null) {
|
||
// Mark the parent fiber as incomplete and clear its subtree flags.
|
||
returnFiber.flags |= Incomplete;
|
||
returnFiber.subtreeFlags = NoFlags;
|
||
returnFiber.deletions = null;
|
||
}
|
||
else {
|
||
// We've unwound all the way to the root.
|
||
workInProgressRootExitStatus = RootDidNotComplete;
|
||
workInProgress = null;
|
||
return;
|
||
}
|
||
}
|
||
var siblingFiber = completedWork.sibling;
|
||
if (siblingFiber !== null) {
|
||
// If there is more work to do in this returnFiber, do that next.
|
||
workInProgress = siblingFiber;
|
||
return;
|
||
} // Otherwise, return to the parent
|
||
completedWork = returnFiber; // Update the next thing we're working on in case something throws.
|
||
workInProgress = completedWork;
|
||
} while (completedWork !== null); // We've reached the root.
|
||
if (workInProgressRootExitStatus === RootInProgress) {
|
||
workInProgressRootExitStatus = RootCompleted;
|
||
}
|
||
}
|
||
function commitRoot(root, recoverableErrors, transitions) {
|
||
// TODO: This no longer makes any sense. We already wrap the mutation and
|
||
// layout phases. Should be able to remove.
|
||
var previousUpdateLanePriority = getCurrentUpdatePriority();
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
commitRootImpl(root, recoverableErrors, transitions, previousUpdateLanePriority);
|
||
}
|
||
finally {
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
setCurrentUpdatePriority(previousUpdateLanePriority);
|
||
}
|
||
return null;
|
||
}
|
||
function commitRootImpl(root, recoverableErrors, transitions, renderPriorityLevel) {
|
||
do {
|
||
// `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
|
||
// means `flushPassiveEffects` will sometimes result in additional
|
||
// passive effects. So we need to keep flushing in a loop until there are
|
||
// no more pending effects.
|
||
// TODO: Might be better if `flushPassiveEffects` did not automatically
|
||
// flush synchronous work at the end, to avoid factoring hazards like this.
|
||
flushPassiveEffects();
|
||
} while (rootWithPendingPassiveEffects !== null);
|
||
flushRenderPhaseStrictModeWarningsInDEV();
|
||
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
||
throw new Error('Should not already be working.');
|
||
}
|
||
var finishedWork = root.finishedWork;
|
||
var lanes = root.finishedLanes;
|
||
{
|
||
markCommitStarted(lanes);
|
||
}
|
||
if (finishedWork === null) {
|
||
{
|
||
markCommitStopped();
|
||
}
|
||
return null;
|
||
}
|
||
else {
|
||
{
|
||
if (lanes === NoLanes) {
|
||
error('root.finishedLanes should not be empty during a commit. This is a ' + 'bug in React.');
|
||
}
|
||
}
|
||
}
|
||
root.finishedWork = null;
|
||
root.finishedLanes = NoLanes;
|
||
if (finishedWork === root.current) {
|
||
throw new Error('Cannot commit the same tree as before. This error is likely caused by ' + 'a bug in React. Please file an issue.');
|
||
} // commitRoot never returns a continuation; it always finishes synchronously.
|
||
// So we can clear these now to allow a new callback to be scheduled.
|
||
root.callbackNode = null;
|
||
root.callbackPriority = NoLane; // Update the first and last pending times on this root. The new first
|
||
// pending time is whatever is left on the root fiber.
|
||
var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
|
||
markRootFinished(root, remainingLanes);
|
||
if (root === workInProgressRoot) {
|
||
// We can reset these now that they are finished.
|
||
workInProgressRoot = null;
|
||
workInProgress = null;
|
||
workInProgressRootRenderLanes = NoLanes;
|
||
} // If there are pending passive effects, schedule a callback to process them.
|
||
// Do this as early as possible, so it is queued before anything else that
|
||
// might get scheduled in the commit phase. (See #16714.)
|
||
// TODO: Delete all other places that schedule the passive effect callback
|
||
// They're redundant.
|
||
if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags) {
|
||
if (!rootDoesHavePassiveEffects) {
|
||
rootDoesHavePassiveEffects = true;
|
||
// to store it in pendingPassiveTransitions until they get processed
|
||
// We need to pass this through as an argument to commitRoot
|
||
// because workInProgressTransitions might have changed between
|
||
// the previous render and commit if we throttle the commit
|
||
// with setTimeout
|
||
pendingPassiveTransitions = transitions;
|
||
scheduleCallback$1(NormalPriority, function () {
|
||
flushPassiveEffects(); // This render triggered passive effects: release the root cache pool
|
||
// *after* passive effects fire to avoid freeing a cache pool that may
|
||
// be referenced by a node in the tree (HostRoot, Cache boundary etc)
|
||
return null;
|
||
});
|
||
}
|
||
} // Check if there are any effects in the whole tree.
|
||
// TODO: This is left over from the effect list implementation, where we had
|
||
// to check for the existence of `firstEffect` to satisfy Flow. I think the
|
||
// only other reason this optimization exists is because it affects profiling.
|
||
// Reconsider whether this is necessary.
|
||
var subtreeHasEffects = (finishedWork.subtreeFlags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;
|
||
var rootHasEffect = (finishedWork.flags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;
|
||
if (subtreeHasEffects || rootHasEffect) {
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
setCurrentUpdatePriority(DiscreteEventPriority);
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= CommitContext; // Reset this to null before calling lifecycles
|
||
ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass
|
||
// of the effect list for each phase: all mutation effects come before all
|
||
// layout effects, and so on.
|
||
// The first phase a "before mutation" phase. We use this phase to read the
|
||
// state of the host tree right before we mutate it. This is where
|
||
// getSnapshotBeforeUpdate is called.
|
||
var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork);
|
||
{
|
||
// Mark the current commit time to be shared by all Profilers in this
|
||
// batch. This enables them to be grouped later.
|
||
recordCommitTime();
|
||
}
|
||
commitMutationEffects(root, finishedWork, lanes);
|
||
resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after
|
||
// the mutation phase, so that the previous tree is still current during
|
||
// componentWillUnmount, but before the layout phase, so that the finished
|
||
// work is current during componentDidMount/Update.
|
||
root.current = finishedWork; // The next phase is the layout phase, where we call effects that read
|
||
{
|
||
markLayoutEffectsStarted(lanes);
|
||
}
|
||
commitLayoutEffects(finishedWork, root, lanes);
|
||
{
|
||
markLayoutEffectsStopped();
|
||
}
|
||
// opportunity to paint.
|
||
requestPaint();
|
||
executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value.
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition;
|
||
}
|
||
else {
|
||
// No effects.
|
||
root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were
|
||
// no effects.
|
||
// TODO: Maybe there's a better way to report this.
|
||
{
|
||
recordCommitTime();
|
||
}
|
||
}
|
||
var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
|
||
if (rootDoesHavePassiveEffects) {
|
||
// This commit has passive effects. Stash a reference to them. But don't
|
||
// schedule a callback until after flushing layout work.
|
||
rootDoesHavePassiveEffects = false;
|
||
rootWithPendingPassiveEffects = root;
|
||
pendingPassiveEffectsLanes = lanes;
|
||
}
|
||
else {
|
||
{
|
||
nestedPassiveUpdateCount = 0;
|
||
rootWithPassiveNestedUpdates = null;
|
||
}
|
||
} // Read this again, since an effect might have updated it
|
||
remainingLanes = root.pendingLanes; // Check if there's remaining work on this root
|
||
// TODO: This is part of the `componentDidCatch` implementation. Its purpose
|
||
// is to detect whether something might have called setState inside
|
||
// `componentDidCatch`. The mechanism is known to be flawed because `setState`
|
||
// inside `componentDidCatch` is itself flawed — that's why we recommend
|
||
// `getDerivedStateFromError` instead. However, it could be improved by
|
||
// checking if remainingLanes includes Sync work, instead of whether there's
|
||
// any work remaining at all (which would also include stuff like Suspense
|
||
// retries or transitions). It's been like this for a while, though, so fixing
|
||
// it probably isn't that urgent.
|
||
if (remainingLanes === NoLanes) {
|
||
// If there's no remaining work, we can clear the set of already failed
|
||
// error boundaries.
|
||
legacyErrorBoundariesThatAlreadyFailed = null;
|
||
}
|
||
{
|
||
if (!rootDidHavePassiveEffects) {
|
||
commitDoubleInvokeEffectsInDEV(root.current, false);
|
||
}
|
||
}
|
||
onCommitRoot(finishedWork.stateNode, renderPriorityLevel);
|
||
{
|
||
if (isDevToolsPresent) {
|
||
root.memoizedUpdaters.clear();
|
||
}
|
||
}
|
||
{
|
||
onCommitRoot$1();
|
||
} // Always call this before exiting `commitRoot`, to ensure that any
|
||
// additional work on this root is scheduled.
|
||
ensureRootIsScheduled(root, now());
|
||
if (recoverableErrors !== null) {
|
||
// There were errors during this render, but recovered from them without
|
||
// needing to surface it to the UI. We log them here.
|
||
var onRecoverableError = root.onRecoverableError;
|
||
for (var i = 0; i < recoverableErrors.length; i++) {
|
||
var recoverableError = recoverableErrors[i];
|
||
var componentStack = recoverableError.stack;
|
||
var digest = recoverableError.digest;
|
||
onRecoverableError(recoverableError.value, {
|
||
componentStack: componentStack,
|
||
digest: digest
|
||
});
|
||
}
|
||
}
|
||
if (hasUncaughtError) {
|
||
hasUncaughtError = false;
|
||
var error$1 = firstUncaughtError;
|
||
firstUncaughtError = null;
|
||
throw error$1;
|
||
} // If the passive effects are the result of a discrete render, flush them
|
||
// synchronously at the end of the current task so that the result is
|
||
// immediately observable. Otherwise, we assume that they are not
|
||
// order-dependent and do not need to be observed by external systems, so we
|
||
// can wait until after paint.
|
||
// TODO: We can optimize this by not scheduling the callback earlier. Since we
|
||
// currently schedule the callback in multiple places, will wait until those
|
||
// are consolidated.
|
||
if (includesSomeLane(pendingPassiveEffectsLanes, SyncLane) && root.tag !== LegacyRoot) {
|
||
flushPassiveEffects();
|
||
} // Read this again, since a passive effect might have updated it
|
||
remainingLanes = root.pendingLanes;
|
||
if (includesSomeLane(remainingLanes, SyncLane)) {
|
||
{
|
||
markNestedUpdateScheduled();
|
||
} // Count the number of times the root synchronously re-renders without
|
||
// finishing. If there are too many, it indicates an infinite update loop.
|
||
if (root === rootWithNestedUpdates) {
|
||
nestedUpdateCount++;
|
||
}
|
||
else {
|
||
nestedUpdateCount = 0;
|
||
rootWithNestedUpdates = root;
|
||
}
|
||
}
|
||
else {
|
||
nestedUpdateCount = 0;
|
||
} // If layout work was scheduled, flush it now.
|
||
flushSyncCallbacks();
|
||
{
|
||
markCommitStopped();
|
||
}
|
||
return null;
|
||
}
|
||
function flushPassiveEffects() {
|
||
// Returns whether passive effects were flushed.
|
||
// TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
|
||
// probably just combine the two functions. I believe they were only separate
|
||
// in the first place because we used to wrap it with
|
||
// `Scheduler.runWithPriority`, which accepts a function. But now we track the
|
||
// priority within React itself, so we can mutate the variable directly.
|
||
if (rootWithPendingPassiveEffects !== null) {
|
||
var renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
|
||
var priority = lowerEventPriority(DefaultEventPriority, renderPriority);
|
||
var prevTransition = ReactCurrentBatchConfig$2.transition;
|
||
var previousPriority = getCurrentUpdatePriority();
|
||
try {
|
||
ReactCurrentBatchConfig$2.transition = null;
|
||
setCurrentUpdatePriority(priority);
|
||
return flushPassiveEffectsImpl();
|
||
}
|
||
finally {
|
||
setCurrentUpdatePriority(previousPriority);
|
||
ReactCurrentBatchConfig$2.transition = prevTransition; // Once passive effects have run for the tree - giving components a
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
function enqueuePendingPassiveProfilerEffect(fiber) {
|
||
{
|
||
pendingPassiveProfilerEffects.push(fiber);
|
||
if (!rootDoesHavePassiveEffects) {
|
||
rootDoesHavePassiveEffects = true;
|
||
scheduleCallback$1(NormalPriority, function () {
|
||
flushPassiveEffects();
|
||
return null;
|
||
});
|
||
}
|
||
}
|
||
}
|
||
function flushPassiveEffectsImpl() {
|
||
if (rootWithPendingPassiveEffects === null) {
|
||
return false;
|
||
} // Cache and clear the transitions flag
|
||
var transitions = pendingPassiveTransitions;
|
||
pendingPassiveTransitions = null;
|
||
var root = rootWithPendingPassiveEffects;
|
||
var lanes = pendingPassiveEffectsLanes;
|
||
rootWithPendingPassiveEffects = null; // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.
|
||
// Figure out why and fix it. It's not causing any known issues (probably
|
||
// because it's only used for profiling), but it's a refactor hazard.
|
||
pendingPassiveEffectsLanes = NoLanes;
|
||
if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
||
throw new Error('Cannot flush passive effects while already rendering.');
|
||
}
|
||
{
|
||
isFlushingPassiveEffects = true;
|
||
didScheduleUpdateDuringPassiveEffects = false;
|
||
}
|
||
{
|
||
markPassiveEffectsStarted(lanes);
|
||
}
|
||
var prevExecutionContext = executionContext;
|
||
executionContext |= CommitContext;
|
||
commitPassiveUnmountEffects(root.current);
|
||
commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects
|
||
{
|
||
var profilerEffects = pendingPassiveProfilerEffects;
|
||
pendingPassiveProfilerEffects = [];
|
||
for (var i = 0; i < profilerEffects.length; i++) {
|
||
var _fiber = profilerEffects[i];
|
||
commitPassiveEffectDurations(root, _fiber);
|
||
}
|
||
}
|
||
{
|
||
markPassiveEffectsStopped();
|
||
}
|
||
{
|
||
commitDoubleInvokeEffectsInDEV(root.current, true);
|
||
}
|
||
executionContext = prevExecutionContext;
|
||
flushSyncCallbacks();
|
||
{
|
||
// If additional passive effects were scheduled, increment a counter. If this
|
||
// exceeds the limit, we'll fire a warning.
|
||
if (didScheduleUpdateDuringPassiveEffects) {
|
||
if (root === rootWithPassiveNestedUpdates) {
|
||
nestedPassiveUpdateCount++;
|
||
}
|
||
else {
|
||
nestedPassiveUpdateCount = 0;
|
||
rootWithPassiveNestedUpdates = root;
|
||
}
|
||
}
|
||
else {
|
||
nestedPassiveUpdateCount = 0;
|
||
}
|
||
isFlushingPassiveEffects = false;
|
||
didScheduleUpdateDuringPassiveEffects = false;
|
||
} // TODO: Move to commitPassiveMountEffects
|
||
onPostCommitRoot(root);
|
||
{
|
||
var stateNode = root.current.stateNode;
|
||
stateNode.effectDuration = 0;
|
||
stateNode.passiveEffectDuration = 0;
|
||
}
|
||
return true;
|
||
}
|
||
function isAlreadyFailedLegacyErrorBoundary(instance) {
|
||
return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
|
||
}
|
||
function markLegacyErrorBoundaryAsFailed(instance) {
|
||
if (legacyErrorBoundariesThatAlreadyFailed === null) {
|
||
legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
|
||
}
|
||
else {
|
||
legacyErrorBoundariesThatAlreadyFailed.add(instance);
|
||
}
|
||
}
|
||
function prepareToThrowUncaughtError(error) {
|
||
if (!hasUncaughtError) {
|
||
hasUncaughtError = true;
|
||
firstUncaughtError = error;
|
||
}
|
||
}
|
||
var onUncaughtError = prepareToThrowUncaughtError;
|
||
function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {
|
||
var errorInfo = createCapturedValueAtFiber(error, sourceFiber);
|
||
var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);
|
||
var root = enqueueUpdate(rootFiber, update, SyncLane);
|
||
var eventTime = requestEventTime();
|
||
if (root !== null) {
|
||
markRootUpdated(root, SyncLane, eventTime);
|
||
ensureRootIsScheduled(root, eventTime);
|
||
}
|
||
}
|
||
function captureCommitPhaseError(sourceFiber, nearestMountedAncestor, error$1) {
|
||
{
|
||
reportUncaughtErrorInDEV(error$1);
|
||
setIsRunningInsertionEffect(false);
|
||
}
|
||
if (sourceFiber.tag === HostRoot) {
|
||
// Error was thrown at the root. There is no parent, so the root
|
||
// itself should capture it.
|
||
captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error$1);
|
||
return;
|
||
}
|
||
var fiber = null;
|
||
{
|
||
fiber = nearestMountedAncestor;
|
||
}
|
||
while (fiber !== null) {
|
||
if (fiber.tag === HostRoot) {
|
||
captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error$1);
|
||
return;
|
||
}
|
||
else if (fiber.tag === ClassComponent) {
|
||
var ctor = fiber.type;
|
||
var instance = fiber.stateNode;
|
||
if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
|
||
var errorInfo = createCapturedValueAtFiber(error$1, sourceFiber);
|
||
var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);
|
||
var root = enqueueUpdate(fiber, update, SyncLane);
|
||
var eventTime = requestEventTime();
|
||
if (root !== null) {
|
||
markRootUpdated(root, SyncLane, eventTime);
|
||
ensureRootIsScheduled(root, eventTime);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
fiber = fiber.return;
|
||
}
|
||
{
|
||
// TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning
|
||
// will fire for errors that are thrown by destroy functions inside deleted
|
||
// trees. What it should instead do is propagate the error to the parent of
|
||
// the deleted tree. In the meantime, do not add this warning to the
|
||
// allowlist; this is only for our internal use.
|
||
error('Internal React error: Attempted to capture a commit phase error ' + 'inside a detached tree. This indicates a bug in React. Likely ' + 'causes include deleting the same fiber more than once, committing an ' + 'already-finished tree, or an inconsistent return pointer.\n\n' + 'Error message:\n\n%s', error$1);
|
||
}
|
||
}
|
||
function pingSuspendedRoot(root, wakeable, pingedLanes) {
|
||
var pingCache = root.pingCache;
|
||
if (pingCache !== null) {
|
||
// The wakeable resolved, so we no longer need to memoize, because it will
|
||
// never be thrown again.
|
||
pingCache.delete(wakeable);
|
||
}
|
||
var eventTime = requestEventTime();
|
||
markRootPinged(root, pingedLanes);
|
||
warnIfSuspenseResolutionNotWrappedWithActDEV(root);
|
||
if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {
|
||
// Received a ping at the same priority level at which we're currently
|
||
// rendering. We might want to restart this render. This should mirror
|
||
// the logic of whether or not a root suspends once it completes.
|
||
// TODO: If we're rendering sync either due to Sync, Batched or expired,
|
||
// we should probably never restart.
|
||
// If we're suspended with delay, or if it's a retry, we'll always suspend
|
||
// so we can always restart.
|
||
if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {
|
||
// Restart from the root.
|
||
prepareFreshStack(root, NoLanes);
|
||
}
|
||
else {
|
||
// Even though we can't restart right now, we might get an
|
||
// opportunity later. So we mark this render as having a ping.
|
||
workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);
|
||
}
|
||
}
|
||
ensureRootIsScheduled(root, eventTime);
|
||
}
|
||
function retryTimedOutBoundary(boundaryFiber, retryLane) {
|
||
// The boundary fiber (a Suspense component or SuspenseList component)
|
||
// previously was rendered in its fallback state. One of the promises that
|
||
// suspended it has resolved, which means at least part of the tree was
|
||
// likely unblocked. Try rendering again, at a new lanes.
|
||
if (retryLane === NoLane) {
|
||
// TODO: Assign this to `suspenseState.retryLane`? to avoid
|
||
// unnecessary entanglement?
|
||
retryLane = requestRetryLane(boundaryFiber);
|
||
} // TODO: Special case idle priority?
|
||
var eventTime = requestEventTime();
|
||
var root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
|
||
if (root !== null) {
|
||
markRootUpdated(root, retryLane, eventTime);
|
||
ensureRootIsScheduled(root, eventTime);
|
||
}
|
||
}
|
||
function retryDehydratedSuspenseBoundary(boundaryFiber) {
|
||
var suspenseState = boundaryFiber.memoizedState;
|
||
var retryLane = NoLane;
|
||
if (suspenseState !== null) {
|
||
retryLane = suspenseState.retryLane;
|
||
}
|
||
retryTimedOutBoundary(boundaryFiber, retryLane);
|
||
}
|
||
function resolveRetryWakeable(boundaryFiber, wakeable) {
|
||
var retryLane = NoLane; // Default
|
||
var retryCache;
|
||
switch (boundaryFiber.tag) {
|
||
case SuspenseComponent:
|
||
retryCache = boundaryFiber.stateNode;
|
||
var suspenseState = boundaryFiber.memoizedState;
|
||
if (suspenseState !== null) {
|
||
retryLane = suspenseState.retryLane;
|
||
}
|
||
break;
|
||
case SuspenseListComponent:
|
||
retryCache = boundaryFiber.stateNode;
|
||
break;
|
||
default:
|
||
throw new Error('Pinged unknown suspense boundary type. ' + 'This is probably a bug in React.');
|
||
}
|
||
if (retryCache !== null) {
|
||
// The wakeable resolved, so we no longer need to memoize, because it will
|
||
// never be thrown again.
|
||
retryCache.delete(wakeable);
|
||
}
|
||
retryTimedOutBoundary(boundaryFiber, retryLane);
|
||
} // Computes the next Just Noticeable Difference (JND) boundary.
|
||
// The theory is that a person can't tell the difference between small differences in time.
|
||
// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable
|
||
// difference in the experience. However, waiting for longer might mean that we can avoid
|
||
// showing an intermediate loading state. The longer we have already waited, the harder it
|
||
// is to tell small differences in time. Therefore, the longer we've already waited,
|
||
// the longer we can wait additionally. At some point we have to give up though.
|
||
// We pick a train model where the next boundary commits at a consistent schedule.
|
||
// These particular numbers are vague estimates. We expect to adjust them based on research.
|
||
function jnd(timeElapsed) {
|
||
return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;
|
||
}
|
||
function checkForNestedUpdates() {
|
||
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
|
||
nestedUpdateCount = 0;
|
||
rootWithNestedUpdates = null;
|
||
throw new Error('Maximum update depth exceeded. This can happen when a component ' + 'repeatedly calls setState inside componentWillUpdate or ' + 'componentDidUpdate. React limits the number of nested updates to ' + 'prevent infinite loops.');
|
||
}
|
||
{
|
||
if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
|
||
nestedPassiveUpdateCount = 0;
|
||
rootWithPassiveNestedUpdates = null;
|
||
error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');
|
||
}
|
||
}
|
||
}
|
||
function flushRenderPhaseStrictModeWarningsInDEV() {
|
||
{
|
||
ReactStrictModeWarnings.flushLegacyContextWarning();
|
||
{
|
||
ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
|
||
}
|
||
}
|
||
}
|
||
function commitDoubleInvokeEffectsInDEV(fiber, hasPassiveEffects) {
|
||
{
|
||
// TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
|
||
// so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
|
||
// Maybe not a big deal since this is DEV only behavior.
|
||
setCurrentFiber(fiber);
|
||
invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
|
||
if (hasPassiveEffects) {
|
||
invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
|
||
}
|
||
invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
|
||
if (hasPassiveEffects) {
|
||
invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
|
||
}
|
||
resetCurrentFiber();
|
||
}
|
||
}
|
||
function invokeEffectsInDev(firstChild, fiberFlags, invokeEffectFn) {
|
||
{
|
||
// We don't need to re-check StrictEffectsMode here.
|
||
// This function is only called if that check has already passed.
|
||
var current = firstChild;
|
||
var subtreeRoot = null;
|
||
while (current !== null) {
|
||
var primarySubtreeFlag = current.subtreeFlags & fiberFlags;
|
||
if (current !== subtreeRoot && current.child !== null && primarySubtreeFlag !== NoFlags) {
|
||
current = current.child;
|
||
}
|
||
else {
|
||
if ((current.flags & fiberFlags) !== NoFlags) {
|
||
invokeEffectFn(current);
|
||
}
|
||
if (current.sibling !== null) {
|
||
current = current.sibling;
|
||
}
|
||
else {
|
||
current = subtreeRoot = current.return;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var didWarnStateUpdateForNotYetMountedComponent = null;
|
||
function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
|
||
{
|
||
if ((executionContext & RenderContext) !== NoContext) {
|
||
// We let the other warning about render phase updates deal with this one.
|
||
return;
|
||
}
|
||
if (!(fiber.mode & ConcurrentMode)) {
|
||
return;
|
||
}
|
||
var tag = fiber.tag;
|
||
if (tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent) {
|
||
// Only warn for user-defined components, not internal ones like Suspense.
|
||
return;
|
||
} // We show the whole stack but dedupe on the top component's name because
|
||
// the problematic code almost always lies inside that component.
|
||
var componentName = getComponentNameFromFiber(fiber) || 'ReactComponent';
|
||
if (didWarnStateUpdateForNotYetMountedComponent !== null) {
|
||
if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {
|
||
return;
|
||
}
|
||
didWarnStateUpdateForNotYetMountedComponent.add(componentName);
|
||
}
|
||
else {
|
||
didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);
|
||
}
|
||
var previousFiber = current;
|
||
try {
|
||
setCurrentFiber(fiber);
|
||
error("Can't perform a React state update on a component that hasn't mounted yet. " + 'This indicates that you have a side-effect in your render function that ' + 'asynchronously later calls tries to update the component. Move this work to ' + 'useEffect instead.');
|
||
}
|
||
finally {
|
||
if (previousFiber) {
|
||
setCurrentFiber(fiber);
|
||
}
|
||
else {
|
||
resetCurrentFiber();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var beginWork$1;
|
||
{
|
||
var dummyFiber = null;
|
||
beginWork$1 = function (current, unitOfWork, lanes) {
|
||
// If a component throws an error, we replay it again in a synchronously
|
||
// dispatched event, so that the debugger will treat it as an uncaught
|
||
// error See ReactErrorUtils for more information.
|
||
// Before entering the begin phase, copy the work-in-progress onto a dummy
|
||
// fiber. If beginWork throws, we'll use this to reset the state.
|
||
var originalWorkInProgressCopy = assignFiberPropertiesInDEV(dummyFiber, unitOfWork);
|
||
try {
|
||
return beginWork(current, unitOfWork, lanes);
|
||
}
|
||
catch (originalError) {
|
||
if (didSuspendOrErrorWhileHydratingDEV() || originalError !== null && typeof originalError === 'object' && typeof originalError.then === 'function') {
|
||
// Don't replay promises.
|
||
// Don't replay errors if we are hydrating and have already suspended or handled an error
|
||
throw originalError;
|
||
} // Keep this code in sync with handleError; any changes here must have
|
||
// corresponding changes there.
|
||
resetContextDependencies();
|
||
resetHooksAfterThrow(); // Don't reset current debug fiber, since we're about to work on the
|
||
// same fiber again.
|
||
// Unwind the failed stack frame
|
||
unwindInterruptedWork(current, unitOfWork); // Restore the original properties of the fiber.
|
||
assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);
|
||
if (unitOfWork.mode & ProfileMode) {
|
||
// Reset the profiler timer.
|
||
startProfilerTimer(unitOfWork);
|
||
} // Run beginWork again.
|
||
invokeGuardedCallback(null, beginWork, null, current, unitOfWork, lanes);
|
||
if (hasCaughtError()) {
|
||
var replayError = clearCaughtError();
|
||
if (typeof replayError === 'object' && replayError !== null && replayError._suppressLogging && typeof originalError === 'object' && originalError !== null && !originalError._suppressLogging) {
|
||
// If suppressed, let the flag carry over to the original error which is the one we'll rethrow.
|
||
originalError._suppressLogging = true;
|
||
}
|
||
} // We always throw the original error in case the second render pass is not idempotent.
|
||
// This can happen if a memoized function or CommonJS module doesn't throw after first invocation.
|
||
throw originalError;
|
||
}
|
||
};
|
||
}
|
||
var didWarnAboutUpdateInRender = false;
|
||
var didWarnAboutUpdateInRenderForAnotherComponent;
|
||
{
|
||
didWarnAboutUpdateInRenderForAnotherComponent = new Set();
|
||
}
|
||
function warnAboutRenderPhaseUpdatesInDEV(fiber) {
|
||
{
|
||
if (isRendering && !getIsUpdatingOpaqueValueInRenderPhaseInDEV()) {
|
||
switch (fiber.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
var renderingComponentName = workInProgress && getComponentNameFromFiber(workInProgress) || 'Unknown'; // Dedupe by the rendering component because it's the one that needs to be fixed.
|
||
var dedupeKey = renderingComponentName;
|
||
if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
|
||
didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
|
||
var setStateComponentName = getComponentNameFromFiber(fiber) || 'Unknown';
|
||
error('Cannot update a component (`%s`) while rendering a ' + 'different component (`%s`). To locate the bad setState() call inside `%s`, ' + 'follow the stack trace as described in https://reactjs.org/link/setstate-in-render', setStateComponentName, renderingComponentName, renderingComponentName);
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
if (!didWarnAboutUpdateInRender) {
|
||
error('Cannot update during an existing state transition (such as ' + 'within `render`). Render methods should be a pure ' + 'function of props and state.');
|
||
didWarnAboutUpdateInRender = true;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function restorePendingUpdaters(root, lanes) {
|
||
{
|
||
if (isDevToolsPresent) {
|
||
var memoizedUpdaters = root.memoizedUpdaters;
|
||
memoizedUpdaters.forEach(function (schedulingFiber) {
|
||
addFiberToLanesMap(root, schedulingFiber, lanes);
|
||
}); // This function intentionally does not clear memoized updaters.
|
||
// Those may still be relevant to the current commit
|
||
// and a future one (e.g. Suspense).
|
||
}
|
||
}
|
||
}
|
||
var fakeActCallbackNode = {};
|
||
function scheduleCallback$1(priorityLevel, callback) {
|
||
{
|
||
// If we're currently inside an `act` scope, bypass Scheduler and push to
|
||
// the `act` queue instead.
|
||
var actQueue = ReactCurrentActQueue$1.current;
|
||
if (actQueue !== null) {
|
||
actQueue.push(callback);
|
||
return fakeActCallbackNode;
|
||
}
|
||
else {
|
||
return scheduleCallback(priorityLevel, callback);
|
||
}
|
||
}
|
||
}
|
||
function cancelCallback$1(callbackNode) {
|
||
if (callbackNode === fakeActCallbackNode) {
|
||
return;
|
||
} // In production, always call Scheduler. This function will be stripped out.
|
||
return cancelCallback(callbackNode);
|
||
}
|
||
function shouldForceFlushFallbacksInDEV() {
|
||
// Never force flush in production. This function should get stripped out.
|
||
return ReactCurrentActQueue$1.current !== null;
|
||
}
|
||
function warnIfUpdatesNotWrappedWithActDEV(fiber) {
|
||
{
|
||
if (fiber.mode & ConcurrentMode) {
|
||
if (!isConcurrentActEnvironment()) {
|
||
// Not in an act environment. No need to warn.
|
||
return;
|
||
}
|
||
}
|
||
else {
|
||
// Legacy mode has additional cases where we suppress a warning.
|
||
if (!isLegacyActEnvironment()) {
|
||
// Not in an act environment. No need to warn.
|
||
return;
|
||
}
|
||
if (executionContext !== NoContext) {
|
||
// Legacy mode doesn't warn if the update is batched, i.e.
|
||
// batchedUpdates or flushSync.
|
||
return;
|
||
}
|
||
if (fiber.tag !== FunctionComponent && fiber.tag !== ForwardRef && fiber.tag !== SimpleMemoComponent) {
|
||
// For backwards compatibility with pre-hooks code, legacy mode only
|
||
// warns for updates that originate from a hook.
|
||
return;
|
||
}
|
||
}
|
||
if (ReactCurrentActQueue$1.current === null) {
|
||
var previousFiber = current;
|
||
try {
|
||
setCurrentFiber(fiber);
|
||
error('An update to %s inside a test was not wrapped in act(...).\n\n' + 'When testing, code that causes React state updates should be ' + 'wrapped into act(...):\n\n' + 'act(() => {\n' + ' /* fire events that update state */\n' + '});\n' + '/* assert on the output */\n\n' + "This ensures that you're testing the behavior the user would see " + 'in the browser.' + ' Learn more at https://reactjs.org/link/wrap-tests-with-act', getComponentNameFromFiber(fiber));
|
||
}
|
||
finally {
|
||
if (previousFiber) {
|
||
setCurrentFiber(fiber);
|
||
}
|
||
else {
|
||
resetCurrentFiber();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
function warnIfSuspenseResolutionNotWrappedWithActDEV(root) {
|
||
{
|
||
if (root.tag !== LegacyRoot && isConcurrentActEnvironment() && ReactCurrentActQueue$1.current === null) {
|
||
error('A suspended resource finished loading inside a test, but the event ' + 'was not wrapped in act(...).\n\n' + 'When testing, code that resolves suspended data should be wrapped ' + 'into act(...):\n\n' + 'act(() => {\n' + ' /* finish loading suspended data */\n' + '});\n' + '/* assert on the output */\n\n' + "This ensures that you're testing the behavior the user would see " + 'in the browser.' + ' Learn more at https://reactjs.org/link/wrap-tests-with-act');
|
||
}
|
||
}
|
||
}
|
||
function setIsRunningInsertionEffect(isRunning) {
|
||
{
|
||
isRunningInsertionEffect = isRunning;
|
||
}
|
||
}
|
||
/* eslint-disable react-internal/prod-error-codes */
|
||
var resolveFamily = null; // $FlowFixMe Flow gets confused by a WeakSet feature check below.
|
||
var failedBoundaries = null;
|
||
var setRefreshHandler = function (handler) {
|
||
{
|
||
resolveFamily = handler;
|
||
}
|
||
};
|
||
function resolveFunctionForHotReloading(type) {
|
||
{
|
||
if (resolveFamily === null) {
|
||
// Hot reloading is disabled.
|
||
return type;
|
||
}
|
||
var family = resolveFamily(type);
|
||
if (family === undefined) {
|
||
return type;
|
||
} // Use the latest known implementation.
|
||
return family.current;
|
||
}
|
||
}
|
||
function resolveClassForHotReloading(type) {
|
||
// No implementation differences.
|
||
return resolveFunctionForHotReloading(type);
|
||
}
|
||
function resolveForwardRefForHotReloading(type) {
|
||
{
|
||
if (resolveFamily === null) {
|
||
// Hot reloading is disabled.
|
||
return type;
|
||
}
|
||
var family = resolveFamily(type);
|
||
if (family === undefined) {
|
||
// Check if we're dealing with a real forwardRef. Don't want to crash early.
|
||
if (type !== null && type !== undefined && typeof type.render === 'function') {
|
||
// ForwardRef is special because its resolved .type is an object,
|
||
// but it's possible that we only have its inner render function in the map.
|
||
// If that inner render function is different, we'll build a new forwardRef type.
|
||
var currentRender = resolveFunctionForHotReloading(type.render);
|
||
if (type.render !== currentRender) {
|
||
var syntheticType = {
|
||
$$typeof: REACT_FORWARD_REF_TYPE,
|
||
render: currentRender
|
||
};
|
||
if (type.displayName !== undefined) {
|
||
syntheticType.displayName = type.displayName;
|
||
}
|
||
return syntheticType;
|
||
}
|
||
}
|
||
return type;
|
||
} // Use the latest known implementation.
|
||
return family.current;
|
||
}
|
||
}
|
||
function isCompatibleFamilyForHotReloading(fiber, element) {
|
||
{
|
||
if (resolveFamily === null) {
|
||
// Hot reloading is disabled.
|
||
return false;
|
||
}
|
||
var prevType = fiber.elementType;
|
||
var nextType = element.type; // If we got here, we know types aren't === equal.
|
||
var needsCompareFamilies = false;
|
||
var $$typeofNextType = typeof nextType === 'object' && nextType !== null ? nextType.$$typeof : null;
|
||
switch (fiber.tag) {
|
||
case ClassComponent:
|
||
{
|
||
if (typeof nextType === 'function') {
|
||
needsCompareFamilies = true;
|
||
}
|
||
break;
|
||
}
|
||
case FunctionComponent:
|
||
{
|
||
if (typeof nextType === 'function') {
|
||
needsCompareFamilies = true;
|
||
}
|
||
else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
||
// We don't know the inner type yet.
|
||
// We're going to assume that the lazy inner type is stable,
|
||
// and so it is sufficient to avoid reconciling it away.
|
||
// We're not going to unwrap or actually use the new lazy type.
|
||
needsCompareFamilies = true;
|
||
}
|
||
break;
|
||
}
|
||
case ForwardRef:
|
||
{
|
||
if ($$typeofNextType === REACT_FORWARD_REF_TYPE) {
|
||
needsCompareFamilies = true;
|
||
}
|
||
else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
||
needsCompareFamilies = true;
|
||
}
|
||
break;
|
||
}
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
if ($$typeofNextType === REACT_MEMO_TYPE) {
|
||
// TODO: if it was but can no longer be simple,
|
||
// we shouldn't set this.
|
||
needsCompareFamilies = true;
|
||
}
|
||
else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
||
needsCompareFamilies = true;
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
return false;
|
||
} // Check if both types have a family and it's the same one.
|
||
if (needsCompareFamilies) {
|
||
// Note: memo() and forwardRef() we'll compare outer rather than inner type.
|
||
// This means both of them need to be registered to preserve state.
|
||
// If we unwrapped and compared the inner types for wrappers instead,
|
||
// then we would risk falsely saying two separate memo(Foo)
|
||
// calls are equivalent because they wrap the same Foo function.
|
||
var prevFamily = resolveFamily(prevType);
|
||
if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
function markFailedErrorBoundaryForHotReloading(fiber) {
|
||
{
|
||
if (resolveFamily === null) {
|
||
// Hot reloading is disabled.
|
||
return;
|
||
}
|
||
if (typeof WeakSet !== 'function') {
|
||
return;
|
||
}
|
||
if (failedBoundaries === null) {
|
||
failedBoundaries = new WeakSet();
|
||
}
|
||
failedBoundaries.add(fiber);
|
||
}
|
||
}
|
||
var scheduleRefresh = function (root, update) {
|
||
{
|
||
if (resolveFamily === null) {
|
||
// Hot reloading is disabled.
|
||
return;
|
||
}
|
||
var staleFamilies = update.staleFamilies, updatedFamilies = update.updatedFamilies;
|
||
flushPassiveEffects();
|
||
flushSync(function () {
|
||
scheduleFibersWithFamiliesRecursively(root.current, updatedFamilies, staleFamilies);
|
||
});
|
||
}
|
||
};
|
||
var scheduleRoot = function (root, element) {
|
||
{
|
||
if (root.context !== emptyContextObject) {
|
||
// Super edge case: root has a legacy _renderSubtree context
|
||
// but we don't know the parentComponent so we can't pass it.
|
||
// Just ignore. We'll delete this with _renderSubtree code path later.
|
||
return;
|
||
}
|
||
flushPassiveEffects();
|
||
flushSync(function () {
|
||
updateContainer(element, root, null, null);
|
||
});
|
||
}
|
||
};
|
||
function scheduleFibersWithFamiliesRecursively(fiber, updatedFamilies, staleFamilies) {
|
||
{
|
||
var alternate = fiber.alternate, child = fiber.child, sibling = fiber.sibling, tag = fiber.tag, type = fiber.type;
|
||
var candidateType = null;
|
||
switch (tag) {
|
||
case FunctionComponent:
|
||
case SimpleMemoComponent:
|
||
case ClassComponent:
|
||
candidateType = type;
|
||
break;
|
||
case ForwardRef:
|
||
candidateType = type.render;
|
||
break;
|
||
}
|
||
if (resolveFamily === null) {
|
||
throw new Error('Expected resolveFamily to be set during hot reload.');
|
||
}
|
||
var needsRender = false;
|
||
var needsRemount = false;
|
||
if (candidateType !== null) {
|
||
var family = resolveFamily(candidateType);
|
||
if (family !== undefined) {
|
||
if (staleFamilies.has(family)) {
|
||
needsRemount = true;
|
||
}
|
||
else if (updatedFamilies.has(family)) {
|
||
if (tag === ClassComponent) {
|
||
needsRemount = true;
|
||
}
|
||
else {
|
||
needsRender = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (failedBoundaries !== null) {
|
||
if (failedBoundaries.has(fiber) || alternate !== null && failedBoundaries.has(alternate)) {
|
||
needsRemount = true;
|
||
}
|
||
}
|
||
if (needsRemount) {
|
||
fiber._debugNeedsRemount = true;
|
||
}
|
||
if (needsRemount || needsRender) {
|
||
var _root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (_root !== null) {
|
||
scheduleUpdateOnFiber(_root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
}
|
||
if (child !== null && !needsRemount) {
|
||
scheduleFibersWithFamiliesRecursively(child, updatedFamilies, staleFamilies);
|
||
}
|
||
if (sibling !== null) {
|
||
scheduleFibersWithFamiliesRecursively(sibling, updatedFamilies, staleFamilies);
|
||
}
|
||
}
|
||
}
|
||
var findHostInstancesForRefresh = function (root, families) {
|
||
{
|
||
var hostInstances = new Set();
|
||
var types = new Set(families.map(function (family) {
|
||
return family.current;
|
||
}));
|
||
findHostInstancesForMatchingFibersRecursively(root.current, types, hostInstances);
|
||
return hostInstances;
|
||
}
|
||
};
|
||
function findHostInstancesForMatchingFibersRecursively(fiber, types, hostInstances) {
|
||
{
|
||
var child = fiber.child, sibling = fiber.sibling, tag = fiber.tag, type = fiber.type;
|
||
var candidateType = null;
|
||
switch (tag) {
|
||
case FunctionComponent:
|
||
case SimpleMemoComponent:
|
||
case ClassComponent:
|
||
candidateType = type;
|
||
break;
|
||
case ForwardRef:
|
||
candidateType = type.render;
|
||
break;
|
||
}
|
||
var didMatch = false;
|
||
if (candidateType !== null) {
|
||
if (types.has(candidateType)) {
|
||
didMatch = true;
|
||
}
|
||
}
|
||
if (didMatch) {
|
||
// We have a match. This only drills down to the closest host components.
|
||
// There's no need to search deeper because for the purpose of giving
|
||
// visual feedback, "flashing" outermost parent rectangles is sufficient.
|
||
findHostInstancesForFiberShallowly(fiber, hostInstances);
|
||
}
|
||
else {
|
||
// If there's no match, maybe there will be one further down in the child tree.
|
||
if (child !== null) {
|
||
findHostInstancesForMatchingFibersRecursively(child, types, hostInstances);
|
||
}
|
||
}
|
||
if (sibling !== null) {
|
||
findHostInstancesForMatchingFibersRecursively(sibling, types, hostInstances);
|
||
}
|
||
}
|
||
}
|
||
function findHostInstancesForFiberShallowly(fiber, hostInstances) {
|
||
{
|
||
var foundHostInstances = findChildHostInstancesForFiberShallowly(fiber, hostInstances);
|
||
if (foundHostInstances) {
|
||
return;
|
||
} // If we didn't find any host children, fallback to closest host parent.
|
||
var node = fiber;
|
||
while (true) {
|
||
switch (node.tag) {
|
||
case HostComponent:
|
||
hostInstances.add(node.stateNode);
|
||
return;
|
||
case HostPortal:
|
||
hostInstances.add(node.stateNode.containerInfo);
|
||
return;
|
||
case HostRoot:
|
||
hostInstances.add(node.stateNode.containerInfo);
|
||
return;
|
||
}
|
||
if (node.return === null) {
|
||
throw new Error('Expected to reach root first.');
|
||
}
|
||
node = node.return;
|
||
}
|
||
}
|
||
}
|
||
function findChildHostInstancesForFiberShallowly(fiber, hostInstances) {
|
||
{
|
||
var node = fiber;
|
||
var foundHostInstances = false;
|
||
while (true) {
|
||
if (node.tag === HostComponent) {
|
||
// We got a match.
|
||
foundHostInstances = true;
|
||
hostInstances.add(node.stateNode); // There may still be more, so keep searching.
|
||
}
|
||
else if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === fiber) {
|
||
return foundHostInstances;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === fiber) {
|
||
return foundHostInstances;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
var hasBadMapPolyfill;
|
||
{
|
||
hasBadMapPolyfill = false;
|
||
try {
|
||
var nonExtensibleObject = Object.preventExtensions({});
|
||
/* eslint-disable no-new */
|
||
new Map([[nonExtensibleObject, null]]);
|
||
new Set([nonExtensibleObject]);
|
||
/* eslint-enable no-new */
|
||
}
|
||
catch (e) {
|
||
// TODO: Consider warning about bad polyfills
|
||
hasBadMapPolyfill = true;
|
||
}
|
||
}
|
||
function FiberNode(tag, pendingProps, key, mode) {
|
||
// Instance
|
||
this.tag = tag;
|
||
this.key = key;
|
||
this.elementType = null;
|
||
this.type = null;
|
||
this.stateNode = null; // Fiber
|
||
this.return = null;
|
||
this.child = null;
|
||
this.sibling = null;
|
||
this.index = 0;
|
||
this.ref = null;
|
||
this.pendingProps = pendingProps;
|
||
this.memoizedProps = null;
|
||
this.updateQueue = null;
|
||
this.memoizedState = null;
|
||
this.dependencies = null;
|
||
this.mode = mode; // Effects
|
||
this.flags = NoFlags;
|
||
this.subtreeFlags = NoFlags;
|
||
this.deletions = null;
|
||
this.lanes = NoLanes;
|
||
this.childLanes = NoLanes;
|
||
this.alternate = null;
|
||
{
|
||
// Note: The following is done to avoid a v8 performance cliff.
|
||
//
|
||
// Initializing the fields below to smis and later updating them with
|
||
// double values will cause Fibers to end up having separate shapes.
|
||
// This behavior/bug has something to do with Object.preventExtension().
|
||
// Fortunately this only impacts DEV builds.
|
||
// Unfortunately it makes React unusably slow for some applications.
|
||
// To work around this, initialize the fields below with doubles.
|
||
//
|
||
// Learn more about this here:
|
||
// https://github.com/facebook/react/issues/14365
|
||
// https://bugs.chromium.org/p/v8/issues/detail?id=8538
|
||
this.actualDuration = Number.NaN;
|
||
this.actualStartTime = Number.NaN;
|
||
this.selfBaseDuration = Number.NaN;
|
||
this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
|
||
// This won't trigger the performance cliff mentioned above,
|
||
// and it simplifies other profiler code (including DevTools).
|
||
this.actualDuration = 0;
|
||
this.actualStartTime = -1;
|
||
this.selfBaseDuration = 0;
|
||
this.treeBaseDuration = 0;
|
||
}
|
||
{
|
||
// This isn't directly used but is handy for debugging internals:
|
||
this._debugSource = null;
|
||
this._debugOwner = null;
|
||
this._debugNeedsRemount = false;
|
||
this._debugHookTypes = null;
|
||
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
|
||
Object.preventExtensions(this);
|
||
}
|
||
}
|
||
} // This is a constructor function, rather than a POJO constructor, still
|
||
// please ensure we do the following:
|
||
// 1) Nobody should add any instance methods on this. Instance methods can be
|
||
// more difficult to predict when they get optimized and they are almost
|
||
// never inlined properly in static compilers.
|
||
// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
|
||
// always know when it is a fiber.
|
||
// 3) We might want to experiment with using numeric keys since they are easier
|
||
// to optimize in a non-JIT environment.
|
||
// 4) We can easily go from a constructor to a createFiber object literal if that
|
||
// is faster.
|
||
// 5) It should be easy to port this to a C struct and keep a C implementation
|
||
// compatible.
|
||
var createFiber = function (tag, pendingProps, key, mode) {
|
||
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
|
||
return new FiberNode(tag, pendingProps, key, mode);
|
||
};
|
||
function shouldConstruct$1(Component) {
|
||
var prototype = Component.prototype;
|
||
return !!(prototype && prototype.isReactComponent);
|
||
}
|
||
function isSimpleFunctionComponent(type) {
|
||
return typeof type === 'function' && !shouldConstruct$1(type) && type.defaultProps === undefined;
|
||
}
|
||
function resolveLazyComponentTag(Component) {
|
||
if (typeof Component === 'function') {
|
||
return shouldConstruct$1(Component) ? ClassComponent : FunctionComponent;
|
||
}
|
||
else if (Component !== undefined && Component !== null) {
|
||
var $$typeof = Component.$$typeof;
|
||
if ($$typeof === REACT_FORWARD_REF_TYPE) {
|
||
return ForwardRef;
|
||
}
|
||
if ($$typeof === REACT_MEMO_TYPE) {
|
||
return MemoComponent;
|
||
}
|
||
}
|
||
return IndeterminateComponent;
|
||
} // This is used to create an alternate fiber to do work on.
|
||
function createWorkInProgress(current, pendingProps) {
|
||
var workInProgress = current.alternate;
|
||
if (workInProgress === null) {
|
||
// We use a double buffering pooling technique because we know that we'll
|
||
// only ever need at most two versions of a tree. We pool the "other" unused
|
||
// node that we're free to reuse. This is lazily created to avoid allocating
|
||
// extra objects for things that are never updated. It also allow us to
|
||
// reclaim the extra memory if needed.
|
||
workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
|
||
workInProgress.elementType = current.elementType;
|
||
workInProgress.type = current.type;
|
||
workInProgress.stateNode = current.stateNode;
|
||
{
|
||
// DEV-only fields
|
||
workInProgress._debugSource = current._debugSource;
|
||
workInProgress._debugOwner = current._debugOwner;
|
||
workInProgress._debugHookTypes = current._debugHookTypes;
|
||
}
|
||
workInProgress.alternate = current;
|
||
current.alternate = workInProgress;
|
||
}
|
||
else {
|
||
workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type.
|
||
workInProgress.type = current.type; // We already have an alternate.
|
||
// Reset the effect tag.
|
||
workInProgress.flags = NoFlags; // The effects are no longer valid.
|
||
workInProgress.subtreeFlags = NoFlags;
|
||
workInProgress.deletions = null;
|
||
{
|
||
// We intentionally reset, rather than copy, actualDuration & actualStartTime.
|
||
// This prevents time from endlessly accumulating in new commits.
|
||
// This has the downside of resetting values for different priority renders,
|
||
// But works for yielding (the common case) and should support resuming.
|
||
workInProgress.actualDuration = 0;
|
||
workInProgress.actualStartTime = -1;
|
||
}
|
||
} // Reset all effects except static ones.
|
||
// Static effects are not specific to a render.
|
||
workInProgress.flags = current.flags & StaticMask;
|
||
workInProgress.childLanes = current.childLanes;
|
||
workInProgress.lanes = current.lanes;
|
||
workInProgress.child = current.child;
|
||
workInProgress.memoizedProps = current.memoizedProps;
|
||
workInProgress.memoizedState = current.memoizedState;
|
||
workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so
|
||
// it cannot be shared with the current fiber.
|
||
var currentDependencies = current.dependencies;
|
||
workInProgress.dependencies = currentDependencies === null ? null : {
|
||
lanes: currentDependencies.lanes,
|
||
firstContext: currentDependencies.firstContext
|
||
}; // These will be overridden during the parent's reconciliation
|
||
workInProgress.sibling = current.sibling;
|
||
workInProgress.index = current.index;
|
||
workInProgress.ref = current.ref;
|
||
{
|
||
workInProgress.selfBaseDuration = current.selfBaseDuration;
|
||
workInProgress.treeBaseDuration = current.treeBaseDuration;
|
||
}
|
||
{
|
||
workInProgress._debugNeedsRemount = current._debugNeedsRemount;
|
||
switch (workInProgress.tag) {
|
||
case IndeterminateComponent:
|
||
case FunctionComponent:
|
||
case SimpleMemoComponent:
|
||
workInProgress.type = resolveFunctionForHotReloading(current.type);
|
||
break;
|
||
case ClassComponent:
|
||
workInProgress.type = resolveClassForHotReloading(current.type);
|
||
break;
|
||
case ForwardRef:
|
||
workInProgress.type = resolveForwardRefForHotReloading(current.type);
|
||
break;
|
||
}
|
||
}
|
||
return workInProgress;
|
||
} // Used to reuse a Fiber for a second pass.
|
||
function resetWorkInProgress(workInProgress, renderLanes) {
|
||
// This resets the Fiber to what createFiber or createWorkInProgress would
|
||
// have set the values to before during the first pass. Ideally this wouldn't
|
||
// be necessary but unfortunately many code paths reads from the workInProgress
|
||
// when they should be reading from current and writing to workInProgress.
|
||
// We assume pendingProps, index, key, ref, return are still untouched to
|
||
// avoid doing another reconciliation.
|
||
// Reset the effect flags but keep any Placement tags, since that's something
|
||
// that child fiber is setting, not the reconciliation.
|
||
workInProgress.flags &= StaticMask | Placement; // The effects are no longer valid.
|
||
var current = workInProgress.alternate;
|
||
if (current === null) {
|
||
// Reset to createFiber's initial values.
|
||
workInProgress.childLanes = NoLanes;
|
||
workInProgress.lanes = renderLanes;
|
||
workInProgress.child = null;
|
||
workInProgress.subtreeFlags = NoFlags;
|
||
workInProgress.memoizedProps = null;
|
||
workInProgress.memoizedState = null;
|
||
workInProgress.updateQueue = null;
|
||
workInProgress.dependencies = null;
|
||
workInProgress.stateNode = null;
|
||
{
|
||
// Note: We don't reset the actualTime counts. It's useful to accumulate
|
||
// actual time across multiple render passes.
|
||
workInProgress.selfBaseDuration = 0;
|
||
workInProgress.treeBaseDuration = 0;
|
||
}
|
||
}
|
||
else {
|
||
// Reset to the cloned values that createWorkInProgress would've.
|
||
workInProgress.childLanes = current.childLanes;
|
||
workInProgress.lanes = current.lanes;
|
||
workInProgress.child = current.child;
|
||
workInProgress.subtreeFlags = NoFlags;
|
||
workInProgress.deletions = null;
|
||
workInProgress.memoizedProps = current.memoizedProps;
|
||
workInProgress.memoizedState = current.memoizedState;
|
||
workInProgress.updateQueue = current.updateQueue; // Needed because Blocks store data on type.
|
||
workInProgress.type = current.type; // Clone the dependencies object. This is mutated during the render phase, so
|
||
// it cannot be shared with the current fiber.
|
||
var currentDependencies = current.dependencies;
|
||
workInProgress.dependencies = currentDependencies === null ? null : {
|
||
lanes: currentDependencies.lanes,
|
||
firstContext: currentDependencies.firstContext
|
||
};
|
||
{
|
||
// Note: We don't reset the actualTime counts. It's useful to accumulate
|
||
// actual time across multiple render passes.
|
||
workInProgress.selfBaseDuration = current.selfBaseDuration;
|
||
workInProgress.treeBaseDuration = current.treeBaseDuration;
|
||
}
|
||
}
|
||
return workInProgress;
|
||
}
|
||
function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {
|
||
var mode;
|
||
if (tag === ConcurrentRoot) {
|
||
mode = ConcurrentMode;
|
||
if (isStrictMode === true) {
|
||
mode |= StrictLegacyMode;
|
||
{
|
||
mode |= StrictEffectsMode;
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
mode = NoMode;
|
||
}
|
||
if (isDevToolsPresent) {
|
||
// Always collect profile timings when DevTools are present.
|
||
// This enables DevTools to start capturing timing at any point–
|
||
// Without some nodes in the tree having empty base times.
|
||
mode |= ProfileMode;
|
||
}
|
||
return createFiber(HostRoot, null, null, mode);
|
||
}
|
||
function createFiberFromTypeAndProps(type, // React$ElementType
|
||
key, pendingProps, owner, mode, lanes) {
|
||
var fiberTag = IndeterminateComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
|
||
var resolvedType = type;
|
||
if (typeof type === 'function') {
|
||
if (shouldConstruct$1(type)) {
|
||
fiberTag = ClassComponent;
|
||
{
|
||
resolvedType = resolveClassForHotReloading(resolvedType);
|
||
}
|
||
}
|
||
else {
|
||
{
|
||
resolvedType = resolveFunctionForHotReloading(resolvedType);
|
||
}
|
||
}
|
||
}
|
||
else if (typeof type === 'string') {
|
||
fiberTag = HostComponent;
|
||
}
|
||
else {
|
||
getTag: switch (type) {
|
||
case REACT_FRAGMENT_TYPE:
|
||
return createFiberFromFragment(pendingProps.children, mode, lanes, key);
|
||
case REACT_STRICT_MODE_TYPE:
|
||
fiberTag = Mode;
|
||
mode |= StrictLegacyMode;
|
||
if ((mode & ConcurrentMode) !== NoMode) {
|
||
// Strict effects should never run on legacy roots
|
||
mode |= StrictEffectsMode;
|
||
}
|
||
break;
|
||
case REACT_PROFILER_TYPE:
|
||
return createFiberFromProfiler(pendingProps, mode, lanes, key);
|
||
case REACT_SUSPENSE_TYPE:
|
||
return createFiberFromSuspense(pendingProps, mode, lanes, key);
|
||
case REACT_SUSPENSE_LIST_TYPE:
|
||
return createFiberFromSuspenseList(pendingProps, mode, lanes, key);
|
||
case REACT_OFFSCREEN_TYPE:
|
||
return createFiberFromOffscreen(pendingProps, mode, lanes, key);
|
||
case REACT_LEGACY_HIDDEN_TYPE:
|
||
// eslint-disable-next-line no-fallthrough
|
||
case REACT_SCOPE_TYPE:
|
||
// eslint-disable-next-line no-fallthrough
|
||
case REACT_CACHE_TYPE:
|
||
// eslint-disable-next-line no-fallthrough
|
||
case REACT_TRACING_MARKER_TYPE:
|
||
// eslint-disable-next-line no-fallthrough
|
||
case REACT_DEBUG_TRACING_MODE_TYPE:
|
||
// eslint-disable-next-line no-fallthrough
|
||
default:
|
||
{
|
||
if (typeof type === 'object' && type !== null) {
|
||
switch (type.$$typeof) {
|
||
case REACT_PROVIDER_TYPE:
|
||
fiberTag = ContextProvider;
|
||
break getTag;
|
||
case REACT_CONTEXT_TYPE:
|
||
// This is a consumer
|
||
fiberTag = ContextConsumer;
|
||
break getTag;
|
||
case REACT_FORWARD_REF_TYPE:
|
||
fiberTag = ForwardRef;
|
||
{
|
||
resolvedType = resolveForwardRefForHotReloading(resolvedType);
|
||
}
|
||
break getTag;
|
||
case REACT_MEMO_TYPE:
|
||
fiberTag = MemoComponent;
|
||
break getTag;
|
||
case REACT_LAZY_TYPE:
|
||
fiberTag = LazyComponent;
|
||
resolvedType = null;
|
||
break getTag;
|
||
}
|
||
}
|
||
var info = '';
|
||
{
|
||
if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
|
||
info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and " + 'named imports.';
|
||
}
|
||
var ownerName = owner ? getComponentNameFromFiber(owner) : null;
|
||
if (ownerName) {
|
||
info += '\n\nCheck the render method of `' + ownerName + '`.';
|
||
}
|
||
}
|
||
throw new Error('Element type is invalid: expected a string (for built-in ' + 'components) or a class/function (for composite components) ' + ("but got: " + (type == null ? type : typeof type) + "." + info));
|
||
}
|
||
}
|
||
}
|
||
var fiber = createFiber(fiberTag, pendingProps, key, mode);
|
||
fiber.elementType = type;
|
||
fiber.type = resolvedType;
|
||
fiber.lanes = lanes;
|
||
{
|
||
fiber._debugOwner = owner;
|
||
}
|
||
return fiber;
|
||
}
|
||
function createFiberFromElement(element, mode, lanes) {
|
||
var owner = null;
|
||
{
|
||
owner = element._owner;
|
||
}
|
||
var type = element.type;
|
||
var key = element.key;
|
||
var pendingProps = element.props;
|
||
var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes);
|
||
{
|
||
fiber._debugSource = element._source;
|
||
fiber._debugOwner = element._owner;
|
||
}
|
||
return fiber;
|
||
}
|
||
function createFiberFromFragment(elements, mode, lanes, key) {
|
||
var fiber = createFiber(Fragment, elements, key, mode);
|
||
fiber.lanes = lanes;
|
||
return fiber;
|
||
}
|
||
function createFiberFromProfiler(pendingProps, mode, lanes, key) {
|
||
{
|
||
if (typeof pendingProps.id !== 'string') {
|
||
error('Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.', typeof pendingProps.id);
|
||
}
|
||
}
|
||
var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
|
||
fiber.elementType = REACT_PROFILER_TYPE;
|
||
fiber.lanes = lanes;
|
||
{
|
||
fiber.stateNode = {
|
||
effectDuration: 0,
|
||
passiveEffectDuration: 0
|
||
};
|
||
}
|
||
return fiber;
|
||
}
|
||
function createFiberFromSuspense(pendingProps, mode, lanes, key) {
|
||
var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
|
||
fiber.elementType = REACT_SUSPENSE_TYPE;
|
||
fiber.lanes = lanes;
|
||
return fiber;
|
||
}
|
||
function createFiberFromSuspenseList(pendingProps, mode, lanes, key) {
|
||
var fiber = createFiber(SuspenseListComponent, pendingProps, key, mode);
|
||
fiber.elementType = REACT_SUSPENSE_LIST_TYPE;
|
||
fiber.lanes = lanes;
|
||
return fiber;
|
||
}
|
||
function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
|
||
var fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
|
||
fiber.elementType = REACT_OFFSCREEN_TYPE;
|
||
fiber.lanes = lanes;
|
||
var primaryChildInstance = {
|
||
isHidden: false
|
||
};
|
||
fiber.stateNode = primaryChildInstance;
|
||
return fiber;
|
||
}
|
||
function createFiberFromText(content, mode, lanes) {
|
||
var fiber = createFiber(HostText, content, null, mode);
|
||
fiber.lanes = lanes;
|
||
return fiber;
|
||
}
|
||
function createFiberFromHostInstanceForDeletion() {
|
||
var fiber = createFiber(HostComponent, null, null, NoMode);
|
||
fiber.elementType = 'DELETED';
|
||
return fiber;
|
||
}
|
||
function createFiberFromDehydratedFragment(dehydratedNode) {
|
||
var fiber = createFiber(DehydratedFragment, null, null, NoMode);
|
||
fiber.stateNode = dehydratedNode;
|
||
return fiber;
|
||
}
|
||
function createFiberFromPortal(portal, mode, lanes) {
|
||
var pendingProps = portal.children !== null ? portal.children : [];
|
||
var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
|
||
fiber.lanes = lanes;
|
||
fiber.stateNode = {
|
||
containerInfo: portal.containerInfo,
|
||
pendingChildren: null,
|
||
// Used by persistent updates
|
||
implementation: portal.implementation
|
||
};
|
||
return fiber;
|
||
} // Used for stashing WIP properties to replay failed work in DEV.
|
||
function assignFiberPropertiesInDEV(target, source) {
|
||
if (target === null) {
|
||
// This Fiber's initial properties will always be overwritten.
|
||
// We only use a Fiber to ensure the same hidden class so DEV isn't slow.
|
||
target = createFiber(IndeterminateComponent, null, null, NoMode);
|
||
} // This is intentionally written as a list of all properties.
|
||
// We tried to use Object.assign() instead but this is called in
|
||
// the hottest path, and Object.assign() was too slow:
|
||
// https://github.com/facebook/react/issues/12502
|
||
// This code is DEV-only so size is not a concern.
|
||
target.tag = source.tag;
|
||
target.key = source.key;
|
||
target.elementType = source.elementType;
|
||
target.type = source.type;
|
||
target.stateNode = source.stateNode;
|
||
target.return = source.return;
|
||
target.child = source.child;
|
||
target.sibling = source.sibling;
|
||
target.index = source.index;
|
||
target.ref = source.ref;
|
||
target.pendingProps = source.pendingProps;
|
||
target.memoizedProps = source.memoizedProps;
|
||
target.updateQueue = source.updateQueue;
|
||
target.memoizedState = source.memoizedState;
|
||
target.dependencies = source.dependencies;
|
||
target.mode = source.mode;
|
||
target.flags = source.flags;
|
||
target.subtreeFlags = source.subtreeFlags;
|
||
target.deletions = source.deletions;
|
||
target.lanes = source.lanes;
|
||
target.childLanes = source.childLanes;
|
||
target.alternate = source.alternate;
|
||
{
|
||
target.actualDuration = source.actualDuration;
|
||
target.actualStartTime = source.actualStartTime;
|
||
target.selfBaseDuration = source.selfBaseDuration;
|
||
target.treeBaseDuration = source.treeBaseDuration;
|
||
}
|
||
target._debugSource = source._debugSource;
|
||
target._debugOwner = source._debugOwner;
|
||
target._debugNeedsRemount = source._debugNeedsRemount;
|
||
target._debugHookTypes = source._debugHookTypes;
|
||
return target;
|
||
}
|
||
function FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError) {
|
||
this.tag = tag;
|
||
this.containerInfo = containerInfo;
|
||
this.pendingChildren = null;
|
||
this.current = null;
|
||
this.pingCache = null;
|
||
this.finishedWork = null;
|
||
this.timeoutHandle = noTimeout;
|
||
this.context = null;
|
||
this.pendingContext = null;
|
||
this.callbackNode = null;
|
||
this.callbackPriority = NoLane;
|
||
this.eventTimes = createLaneMap(NoLanes);
|
||
this.expirationTimes = createLaneMap(NoTimestamp);
|
||
this.pendingLanes = NoLanes;
|
||
this.suspendedLanes = NoLanes;
|
||
this.pingedLanes = NoLanes;
|
||
this.expiredLanes = NoLanes;
|
||
this.mutableReadLanes = NoLanes;
|
||
this.finishedLanes = NoLanes;
|
||
this.entangledLanes = NoLanes;
|
||
this.entanglements = createLaneMap(NoLanes);
|
||
this.identifierPrefix = identifierPrefix;
|
||
this.onRecoverableError = onRecoverableError;
|
||
if (supportsHydration) {
|
||
this.mutableSourceEagerHydrationData = null;
|
||
}
|
||
{
|
||
this.effectDuration = 0;
|
||
this.passiveEffectDuration = 0;
|
||
}
|
||
{
|
||
this.memoizedUpdaters = new Set();
|
||
var pendingUpdatersLaneMap = this.pendingUpdatersLaneMap = [];
|
||
for (var _i = 0; _i < TotalLanes; _i++) {
|
||
pendingUpdatersLaneMap.push(new Set());
|
||
}
|
||
}
|
||
{
|
||
switch (tag) {
|
||
case ConcurrentRoot:
|
||
this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()';
|
||
break;
|
||
case LegacyRoot:
|
||
this._debugRootType = hydrate ? 'hydrate()' : 'render()';
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the
|
||
// host config, but because they are passed in at runtime, we have to thread
|
||
// them through the root constructor. Perhaps we should put them all into a
|
||
// single type, like a DynamicHostConfig that is defined by the renderer.
|
||
identifierPrefix, onRecoverableError, transitionCallbacks) {
|
||
var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);
|
||
// stateNode is any.
|
||
var uninitializedFiber = createHostRootFiber(tag, isStrictMode);
|
||
root.current = uninitializedFiber;
|
||
uninitializedFiber.stateNode = root;
|
||
{
|
||
var _initialState = {
|
||
element: initialChildren,
|
||
isDehydrated: hydrate,
|
||
cache: null,
|
||
// not enabled yet
|
||
transitions: null,
|
||
pendingSuspenseBoundaries: null
|
||
};
|
||
uninitializedFiber.memoizedState = _initialState;
|
||
}
|
||
initializeUpdateQueue(uninitializedFiber);
|
||
return root;
|
||
}
|
||
var ReactVersion = '18.2.0';
|
||
function createPortal(children, containerInfo, // TODO: figure out the API for cross-renderer implementation.
|
||
implementation) {
|
||
var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
||
{
|
||
checkKeyStringCoercion(key);
|
||
}
|
||
return {
|
||
// This tag allow us to uniquely identify this as a React Portal
|
||
$$typeof: REACT_PORTAL_TYPE,
|
||
key: key == null ? null : '' + key,
|
||
children: children,
|
||
containerInfo: containerInfo,
|
||
implementation: implementation
|
||
};
|
||
}
|
||
var didWarnAboutNestedUpdates;
|
||
var didWarnAboutFindNodeInStrictMode;
|
||
{
|
||
didWarnAboutNestedUpdates = false;
|
||
didWarnAboutFindNodeInStrictMode = {};
|
||
}
|
||
function getContextForSubtree(parentComponent) {
|
||
if (!parentComponent) {
|
||
return emptyContextObject;
|
||
}
|
||
var fiber = get(parentComponent);
|
||
var parentContext = findCurrentUnmaskedContext(fiber);
|
||
if (fiber.tag === ClassComponent) {
|
||
var Component = fiber.type;
|
||
if (isContextProvider(Component)) {
|
||
return processChildContext(fiber, Component, parentContext);
|
||
}
|
||
}
|
||
return parentContext;
|
||
}
|
||
function findHostInstance(component) {
|
||
var fiber = get(component);
|
||
if (fiber === undefined) {
|
||
if (typeof component.render === 'function') {
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
else {
|
||
var keys = Object.keys(component).join(',');
|
||
throw new Error("Argument appears to not be a ReactComponent. Keys: " + keys);
|
||
}
|
||
}
|
||
var hostFiber = findCurrentHostFiber(fiber);
|
||
if (hostFiber === null) {
|
||
return null;
|
||
}
|
||
return hostFiber.stateNode;
|
||
}
|
||
function findHostInstanceWithWarning(component, methodName) {
|
||
{
|
||
var fiber = get(component);
|
||
if (fiber === undefined) {
|
||
if (typeof component.render === 'function') {
|
||
throw new Error('Unable to find node on an unmounted component.');
|
||
}
|
||
else {
|
||
var keys = Object.keys(component).join(',');
|
||
throw new Error("Argument appears to not be a ReactComponent. Keys: " + keys);
|
||
}
|
||
}
|
||
var hostFiber = findCurrentHostFiber(fiber);
|
||
if (hostFiber === null) {
|
||
return null;
|
||
}
|
||
if (hostFiber.mode & StrictLegacyMode) {
|
||
var componentName = getComponentNameFromFiber(fiber) || 'Component';
|
||
if (!didWarnAboutFindNodeInStrictMode[componentName]) {
|
||
didWarnAboutFindNodeInStrictMode[componentName] = true;
|
||
var previousFiber = current;
|
||
try {
|
||
setCurrentFiber(hostFiber);
|
||
if (fiber.mode & StrictLegacyMode) {
|
||
error('%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + 'Instead, add a ref directly to the element you want to reference. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-find-node', methodName, methodName, componentName);
|
||
}
|
||
else {
|
||
error('%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which renders StrictMode children. ' + 'Instead, add a ref directly to the element you want to reference. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-find-node', methodName, methodName, componentName);
|
||
}
|
||
}
|
||
finally {
|
||
// Ideally this should reset to previous but this shouldn't be called in
|
||
// render and there's another warning for that anyway.
|
||
if (previousFiber) {
|
||
setCurrentFiber(previousFiber);
|
||
}
|
||
else {
|
||
resetCurrentFiber();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return hostFiber.stateNode;
|
||
}
|
||
}
|
||
function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
|
||
var hydrate = false;
|
||
var initialChildren = null;
|
||
return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
|
||
}
|
||
function createHydrationContainer(initialChildren, // TODO: Remove `callback` when we delete legacy mode.
|
||
callback, containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
|
||
var hydrate = true;
|
||
var root = createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); // TODO: Move this to FiberRoot constructor
|
||
root.context = getContextForSubtree(null); // Schedule the initial render. In a hydration root, this is different from
|
||
// a regular update because the initial render must match was was rendered
|
||
// on the server.
|
||
// NOTE: This update intentionally doesn't have a payload. We're only using
|
||
// the update to schedule work on the root fiber (and, for legacy roots, to
|
||
// enqueue the callback if one is provided).
|
||
var current = root.current;
|
||
var eventTime = requestEventTime();
|
||
var lane = requestUpdateLane(current);
|
||
var update = createUpdate(eventTime, lane);
|
||
update.callback = callback !== undefined && callback !== null ? callback : null;
|
||
enqueueUpdate(current, update, lane);
|
||
scheduleInitialHydrationOnRoot(root, lane, eventTime);
|
||
return root;
|
||
}
|
||
function updateContainer(element, container, parentComponent, callback) {
|
||
{
|
||
onScheduleRoot(container, element);
|
||
}
|
||
var current$1 = container.current;
|
||
var eventTime = requestEventTime();
|
||
var lane = requestUpdateLane(current$1);
|
||
{
|
||
markRenderScheduled(lane);
|
||
}
|
||
var context = getContextForSubtree(parentComponent);
|
||
if (container.context === null) {
|
||
container.context = context;
|
||
}
|
||
else {
|
||
container.pendingContext = context;
|
||
}
|
||
{
|
||
if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
|
||
didWarnAboutNestedUpdates = true;
|
||
error('Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentNameFromFiber(current) || 'Unknown');
|
||
}
|
||
}
|
||
var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
|
||
// being called "element".
|
||
update.payload = {
|
||
element: element
|
||
};
|
||
callback = callback === undefined ? null : callback;
|
||
if (callback !== null) {
|
||
{
|
||
if (typeof callback !== 'function') {
|
||
error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
|
||
}
|
||
}
|
||
update.callback = callback;
|
||
}
|
||
var root = enqueueUpdate(current$1, update, lane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, current$1, lane, eventTime);
|
||
entangleTransitions(root, current$1, lane);
|
||
}
|
||
return lane;
|
||
}
|
||
function getPublicRootInstance(container) {
|
||
var containerFiber = container.current;
|
||
if (!containerFiber.child) {
|
||
return null;
|
||
}
|
||
switch (containerFiber.child.tag) {
|
||
case HostComponent:
|
||
return getPublicInstance(containerFiber.child.stateNode);
|
||
default:
|
||
return containerFiber.child.stateNode;
|
||
}
|
||
}
|
||
function attemptSynchronousHydration(fiber) {
|
||
switch (fiber.tag) {
|
||
case HostRoot:
|
||
{
|
||
var root = fiber.stateNode;
|
||
if (isRootDehydrated(root)) {
|
||
// Flush the first scheduled "update".
|
||
var lanes = getHighestPriorityPendingLanes(root);
|
||
flushRoot(root, lanes);
|
||
}
|
||
break;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
flushSync(function () {
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
|
||
}
|
||
}); // If we're still blocked after this, we need to increase
|
||
// the priority of any promises resolving within this
|
||
// boundary so that they next attempt also has higher pri.
|
||
var retryLane = SyncLane;
|
||
markRetryLaneIfNotHydrated(fiber, retryLane);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
function markRetryLaneImpl(fiber, retryLane) {
|
||
var suspenseState = fiber.memoizedState;
|
||
if (suspenseState !== null && suspenseState.dehydrated !== null) {
|
||
suspenseState.retryLane = higherPriorityLane(suspenseState.retryLane, retryLane);
|
||
}
|
||
} // Increases the priority of thenables when they resolve within this boundary.
|
||
function markRetryLaneIfNotHydrated(fiber, retryLane) {
|
||
markRetryLaneImpl(fiber, retryLane);
|
||
var alternate = fiber.alternate;
|
||
if (alternate) {
|
||
markRetryLaneImpl(alternate, retryLane);
|
||
}
|
||
}
|
||
function attemptDiscreteHydration(fiber) {
|
||
if (fiber.tag !== SuspenseComponent) {
|
||
// We ignore HostRoots here because we can't increase
|
||
// their priority and they should not suspend on I/O,
|
||
// since you have to wrap anything that might suspend in
|
||
// Suspense.
|
||
return;
|
||
}
|
||
var lane = SyncLane;
|
||
var root = enqueueConcurrentRenderForLane(fiber, lane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
}
|
||
markRetryLaneIfNotHydrated(fiber, lane);
|
||
}
|
||
function attemptContinuousHydration(fiber) {
|
||
if (fiber.tag !== SuspenseComponent) {
|
||
// We ignore HostRoots here because we can't increase
|
||
// their priority and they should not suspend on I/O,
|
||
// since you have to wrap anything that might suspend in
|
||
// Suspense.
|
||
return;
|
||
}
|
||
var lane = SelectiveHydrationLane;
|
||
var root = enqueueConcurrentRenderForLane(fiber, lane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
}
|
||
markRetryLaneIfNotHydrated(fiber, lane);
|
||
}
|
||
function attemptHydrationAtCurrentPriority(fiber) {
|
||
if (fiber.tag !== SuspenseComponent) {
|
||
// We ignore HostRoots here because we can't increase
|
||
// their priority other than synchronously flush it.
|
||
return;
|
||
}
|
||
var lane = requestUpdateLane(fiber);
|
||
var root = enqueueConcurrentRenderForLane(fiber, lane);
|
||
if (root !== null) {
|
||
var eventTime = requestEventTime();
|
||
scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
||
}
|
||
markRetryLaneIfNotHydrated(fiber, lane);
|
||
}
|
||
function findHostInstanceWithNoPortals(fiber) {
|
||
var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
|
||
if (hostFiber === null) {
|
||
return null;
|
||
}
|
||
return hostFiber.stateNode;
|
||
}
|
||
var shouldErrorImpl = function (fiber) {
|
||
return null;
|
||
};
|
||
function shouldError(fiber) {
|
||
return shouldErrorImpl(fiber);
|
||
}
|
||
var shouldSuspendImpl = function (fiber) {
|
||
return false;
|
||
};
|
||
function shouldSuspend(fiber) {
|
||
return shouldSuspendImpl(fiber);
|
||
}
|
||
var overrideHookState = null;
|
||
var overrideHookStateDeletePath = null;
|
||
var overrideHookStateRenamePath = null;
|
||
var overrideProps = null;
|
||
var overridePropsDeletePath = null;
|
||
var overridePropsRenamePath = null;
|
||
var scheduleUpdate = null;
|
||
var setErrorHandler = null;
|
||
var setSuspenseHandler = null;
|
||
{
|
||
var copyWithDeleteImpl = function (obj, path, index) {
|
||
var key = path[index];
|
||
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
||
if (index + 1 === path.length) {
|
||
if (isArray(updated)) {
|
||
updated.splice(key, 1);
|
||
}
|
||
else {
|
||
delete updated[key];
|
||
}
|
||
return updated;
|
||
} // $FlowFixMe number or string is fine here
|
||
updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
|
||
return updated;
|
||
};
|
||
var copyWithDelete = function (obj, path) {
|
||
return copyWithDeleteImpl(obj, path, 0);
|
||
};
|
||
var copyWithRenameImpl = function (obj, oldPath, newPath, index) {
|
||
var oldKey = oldPath[index];
|
||
var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
||
if (index + 1 === oldPath.length) {
|
||
var newKey = newPath[index]; // $FlowFixMe number or string is fine here
|
||
updated[newKey] = updated[oldKey];
|
||
if (isArray(updated)) {
|
||
updated.splice(oldKey, 1);
|
||
}
|
||
else {
|
||
delete updated[oldKey];
|
||
}
|
||
}
|
||
else {
|
||
// $FlowFixMe number or string is fine here
|
||
updated[oldKey] = copyWithRenameImpl(// $FlowFixMe number or string is fine here
|
||
obj[oldKey], oldPath, newPath, index + 1);
|
||
}
|
||
return updated;
|
||
};
|
||
var copyWithRename = function (obj, oldPath, newPath) {
|
||
if (oldPath.length !== newPath.length) {
|
||
warn('copyWithRename() expects paths of the same length');
|
||
return;
|
||
}
|
||
else {
|
||
for (var i = 0; i < newPath.length - 1; i++) {
|
||
if (oldPath[i] !== newPath[i]) {
|
||
warn('copyWithRename() expects paths to be the same except for the deepest key');
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
return copyWithRenameImpl(obj, oldPath, newPath, 0);
|
||
};
|
||
var copyWithSetImpl = function (obj, path, index, value) {
|
||
if (index >= path.length) {
|
||
return value;
|
||
}
|
||
var key = path[index];
|
||
var updated = isArray(obj) ? obj.slice() : assign({}, obj); // $FlowFixMe number or string is fine here
|
||
updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
|
||
return updated;
|
||
};
|
||
var copyWithSet = function (obj, path, value) {
|
||
return copyWithSetImpl(obj, path, 0, value);
|
||
};
|
||
var findHook = function (fiber, id) {
|
||
// For now, the "id" of stateful hooks is just the stateful hook index.
|
||
// This may change in the future with e.g. nested hooks.
|
||
var currentHook = fiber.memoizedState;
|
||
while (currentHook !== null && id > 0) {
|
||
currentHook = currentHook.next;
|
||
id--;
|
||
}
|
||
return currentHook;
|
||
}; // Support DevTools editable values for useState and useReducer.
|
||
overrideHookState = function (fiber, id, path, value) {
|
||
var hook = findHook(fiber, id);
|
||
if (hook !== null) {
|
||
var newState = copyWithSet(hook.memoizedState, path, value);
|
||
hook.memoizedState = newState;
|
||
hook.baseState = newState; // We aren't actually adding an update to the queue,
|
||
// because there is no update we can add for useReducer hooks that won't trigger an error.
|
||
// (There's no appropriate action type for DevTools overrides.)
|
||
// As a result though, React will see the scheduled update as a noop and bailout.
|
||
// Shallow cloning props works as a workaround for now to bypass the bailout check.
|
||
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
}
|
||
};
|
||
overrideHookStateDeletePath = function (fiber, id, path) {
|
||
var hook = findHook(fiber, id);
|
||
if (hook !== null) {
|
||
var newState = copyWithDelete(hook.memoizedState, path);
|
||
hook.memoizedState = newState;
|
||
hook.baseState = newState; // We aren't actually adding an update to the queue,
|
||
// because there is no update we can add for useReducer hooks that won't trigger an error.
|
||
// (There's no appropriate action type for DevTools overrides.)
|
||
// As a result though, React will see the scheduled update as a noop and bailout.
|
||
// Shallow cloning props works as a workaround for now to bypass the bailout check.
|
||
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
}
|
||
};
|
||
overrideHookStateRenamePath = function (fiber, id, oldPath, newPath) {
|
||
var hook = findHook(fiber, id);
|
||
if (hook !== null) {
|
||
var newState = copyWithRename(hook.memoizedState, oldPath, newPath);
|
||
hook.memoizedState = newState;
|
||
hook.baseState = newState; // We aren't actually adding an update to the queue,
|
||
// because there is no update we can add for useReducer hooks that won't trigger an error.
|
||
// (There's no appropriate action type for DevTools overrides.)
|
||
// As a result though, React will see the scheduled update as a noop and bailout.
|
||
// Shallow cloning props works as a workaround for now to bypass the bailout check.
|
||
fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
}
|
||
}; // Support DevTools props for function components, forwardRef, memo, host components, etc.
|
||
overrideProps = function (fiber, path, value) {
|
||
fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
|
||
if (fiber.alternate) {
|
||
fiber.alternate.pendingProps = fiber.pendingProps;
|
||
}
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
};
|
||
overridePropsDeletePath = function (fiber, path) {
|
||
fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
|
||
if (fiber.alternate) {
|
||
fiber.alternate.pendingProps = fiber.pendingProps;
|
||
}
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
};
|
||
overridePropsRenamePath = function (fiber, oldPath, newPath) {
|
||
fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
|
||
if (fiber.alternate) {
|
||
fiber.alternate.pendingProps = fiber.pendingProps;
|
||
}
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
};
|
||
scheduleUpdate = function (fiber) {
|
||
var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
||
if (root !== null) {
|
||
scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
||
}
|
||
};
|
||
setErrorHandler = function (newShouldErrorImpl) {
|
||
shouldErrorImpl = newShouldErrorImpl;
|
||
};
|
||
setSuspenseHandler = function (newShouldSuspendImpl) {
|
||
shouldSuspendImpl = newShouldSuspendImpl;
|
||
};
|
||
}
|
||
function findHostInstanceByFiber(fiber) {
|
||
var hostFiber = findCurrentHostFiber(fiber);
|
||
if (hostFiber === null) {
|
||
return null;
|
||
}
|
||
return hostFiber.stateNode;
|
||
}
|
||
function emptyFindFiberByHostInstance(instance) {
|
||
return null;
|
||
}
|
||
function getCurrentFiberForDevTools() {
|
||
return current;
|
||
}
|
||
function injectIntoDevTools(devToolsConfig) {
|
||
var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
|
||
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||
return injectInternals({
|
||
bundleType: devToolsConfig.bundleType,
|
||
version: devToolsConfig.version,
|
||
rendererPackageName: devToolsConfig.rendererPackageName,
|
||
rendererConfig: devToolsConfig.rendererConfig,
|
||
overrideHookState: overrideHookState,
|
||
overrideHookStateDeletePath: overrideHookStateDeletePath,
|
||
overrideHookStateRenamePath: overrideHookStateRenamePath,
|
||
overrideProps: overrideProps,
|
||
overridePropsDeletePath: overridePropsDeletePath,
|
||
overridePropsRenamePath: overridePropsRenamePath,
|
||
setErrorHandler: setErrorHandler,
|
||
setSuspenseHandler: setSuspenseHandler,
|
||
scheduleUpdate: scheduleUpdate,
|
||
currentDispatcherRef: ReactCurrentDispatcher,
|
||
findHostInstanceByFiber: findHostInstanceByFiber,
|
||
findFiberByHostInstance: findFiberByHostInstance || emptyFindFiberByHostInstance,
|
||
// React Refresh
|
||
findHostInstancesForRefresh: findHostInstancesForRefresh,
|
||
scheduleRefresh: scheduleRefresh,
|
||
scheduleRoot: scheduleRoot,
|
||
setRefreshHandler: setRefreshHandler,
|
||
// Enables DevTools to append owner stacks to error messages in DEV mode.
|
||
getCurrentFiber: getCurrentFiberForDevTools,
|
||
// Enables DevTools to detect reconciler version rather than renderer version
|
||
// which may not match for third party renderers.
|
||
reconcilerVersion: ReactVersion
|
||
});
|
||
}
|
||
exports.attemptContinuousHydration = attemptContinuousHydration;
|
||
exports.attemptDiscreteHydration = attemptDiscreteHydration;
|
||
exports.attemptHydrationAtCurrentPriority = attemptHydrationAtCurrentPriority;
|
||
exports.attemptSynchronousHydration = attemptSynchronousHydration;
|
||
exports.batchedUpdates = batchedUpdates;
|
||
exports.createComponentSelector = createComponentSelector;
|
||
exports.createContainer = createContainer;
|
||
exports.createHasPseudoClassSelector = createHasPseudoClassSelector;
|
||
exports.createHydrationContainer = createHydrationContainer;
|
||
exports.createPortal = createPortal;
|
||
exports.createRoleSelector = createRoleSelector;
|
||
exports.createTestNameSelector = createTestNameSelector;
|
||
exports.createTextSelector = createTextSelector;
|
||
exports.deferredUpdates = deferredUpdates;
|
||
exports.discreteUpdates = discreteUpdates;
|
||
exports.findAllNodes = findAllNodes;
|
||
exports.findBoundingRects = findBoundingRects;
|
||
exports.findHostInstance = findHostInstance;
|
||
exports.findHostInstanceWithNoPortals = findHostInstanceWithNoPortals;
|
||
exports.findHostInstanceWithWarning = findHostInstanceWithWarning;
|
||
exports.flushControlled = flushControlled;
|
||
exports.flushPassiveEffects = flushPassiveEffects;
|
||
exports.flushSync = flushSync;
|
||
exports.focusWithin = focusWithin;
|
||
exports.getCurrentUpdatePriority = getCurrentUpdatePriority;
|
||
exports.getFindAllNodesFailureDescription = getFindAllNodesFailureDescription;
|
||
exports.getPublicRootInstance = getPublicRootInstance;
|
||
exports.injectIntoDevTools = injectIntoDevTools;
|
||
exports.isAlreadyRendering = isAlreadyRendering;
|
||
exports.observeVisibleRects = observeVisibleRects;
|
||
exports.registerMutableSourceForHydration = registerMutableSourceForHydration;
|
||
exports.runWithPriority = runWithPriority;
|
||
exports.shouldError = shouldError;
|
||
exports.shouldSuspend = shouldSuspend;
|
||
exports.updateContainer = updateContainer;
|
||
return exports;
|
||
}
|
||
;
|