10574 lines
366 KiB
JavaScript
10574 lines
366 KiB
JavaScript
/** @license React v16.8.6
|
||
* react-test-renderer.production.min.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.
|
||
*/
|
||
import React from 'resource://devtools/client/shared/vendor/react.mjs';
|
||
|
||
/**
|
||
* Use invariant() to assert state which your program assumes to be true.
|
||
*
|
||
* Provide sprintf-style format (only %s is supported) and arguments
|
||
* to provide information about what broke and what you were
|
||
* expecting.
|
||
*
|
||
* The invariant message will be stripped in production, but the invariant
|
||
* will remain to ensure logic does not differ in production.
|
||
*/
|
||
|
||
function invariant(condition, format, a, b, c, d, e, f) {
|
||
if (!condition) {
|
||
var error = void 0;
|
||
if (format === undefined) {
|
||
error = new Error('Minified exception occurred; use the non-minified dev environment ' + 'for the full error message and additional helpful warnings.');
|
||
} else {
|
||
var args = [a, b, c, d, e, f];
|
||
var argIndex = 0;
|
||
error = new Error(format.replace(/%s/g, function () {
|
||
return args[argIndex++];
|
||
}));
|
||
error.name = 'Invariant Violation';
|
||
}
|
||
|
||
error.framesToPop = 1; // we don't care about invariant's own frame
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// Relying on the `invariant()` implementation lets us
|
||
// preserve the format and params in the www builds.
|
||
/**
|
||
* WARNING: DO NOT manually require this module.
|
||
* This is a replacement for `invariant(...)` used by the error code system
|
||
* and will _only_ be required by the corresponding babel pass.
|
||
* It always throws.
|
||
*/
|
||
function reactProdInvariant(code) {
|
||
var argCount = arguments.length - 1;
|
||
var url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
|
||
for (var argIdx = 0; argIdx < argCount; argIdx++) {
|
||
url += '&args[]=' + encodeURIComponent(arguments[argIdx + 1]);
|
||
}
|
||
// Rename it so that our build transform doesn't attempt
|
||
// to replace this invariant() call with reactProdInvariant().
|
||
var i = invariant;
|
||
i(false,
|
||
// The error code is intentionally part of the message (and
|
||
// not the format argument) so that we could deduplicate
|
||
// different errors in logs based on the code.
|
||
'Minified React error #' + code + '; visit %s ' + 'for the full message or use the non-minified dev environment ' + 'for full errors and additional helpful warnings. ', url);
|
||
}
|
||
|
||
var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||
|
||
var _assign = ReactInternals.assign;
|
||
|
||
/**
|
||
* Similar to invariant but only logs a warning if the condition is not met.
|
||
* This can be used to log issues in development environments in critical
|
||
* paths. Removing the logging code for production environments will keep the
|
||
* same logic and follow the same code paths.
|
||
*/
|
||
|
||
/**
|
||
* `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.
|
||
*/
|
||
|
||
/**
|
||
* This API should be called `delete` but we'd have to make sure to always
|
||
* transform these to strings for IE support. When this transform is fully
|
||
* supported we can rename it.
|
||
*/
|
||
|
||
|
||
function get(key) {
|
||
return key._reactInternalFiber;
|
||
}
|
||
|
||
|
||
|
||
function set(key, value) {
|
||
key._reactInternalFiber = value;
|
||
}
|
||
|
||
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||
|
||
// Prevent newer renderers from RTE when used with older react package versions.
|
||
// Current owner and dispatcher used to share the same ref,
|
||
// but PR #14548 split them out to better support the react-debug-tools package.
|
||
if (!ReactSharedInternals.hasOwnProperty('ReactCurrentDispatcher')) {
|
||
ReactSharedInternals.ReactCurrentDispatcher = {
|
||
current: null
|
||
};
|
||
}
|
||
|
||
// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
|
||
// nor polyfill, then a plain number is used for performance.
|
||
var hasSymbol = typeof Symbol === 'function' && Symbol.for;
|
||
|
||
var REACT_ELEMENT_TYPE = hasSymbol ? Symbol.for('react.element') : 0xeac7;
|
||
var REACT_PORTAL_TYPE = hasSymbol ? Symbol.for('react.portal') : 0xeaca;
|
||
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol.for('react.fragment') : 0xeacb;
|
||
var REACT_STRICT_MODE_TYPE = hasSymbol ? Symbol.for('react.strict_mode') : 0xeacc;
|
||
var REACT_PROFILER_TYPE = hasSymbol ? Symbol.for('react.profiler') : 0xead2;
|
||
var REACT_PROVIDER_TYPE = hasSymbol ? Symbol.for('react.provider') : 0xeacd;
|
||
var REACT_CONTEXT_TYPE = hasSymbol ? Symbol.for('react.context') : 0xeace;
|
||
|
||
var REACT_CONCURRENT_MODE_TYPE = hasSymbol ? Symbol.for('react.concurrent_mode') : 0xeacf;
|
||
var REACT_FORWARD_REF_TYPE = hasSymbol ? Symbol.for('react.forward_ref') : 0xead0;
|
||
var REACT_SUSPENSE_TYPE = hasSymbol ? Symbol.for('react.suspense') : 0xead1;
|
||
var REACT_MEMO_TYPE = hasSymbol ? Symbol.for('react.memo') : 0xead3;
|
||
var REACT_LAZY_TYPE = hasSymbol ? Symbol.for('react.lazy') : 0xead4;
|
||
|
||
var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && 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;
|
||
}
|
||
|
||
var Pending = 0;
|
||
var Resolved = 1;
|
||
var Rejected = 2;
|
||
|
||
function refineResolvedLazyComponent(lazyComponent) {
|
||
return lazyComponent._status === Resolved ? lazyComponent._result : null;
|
||
}
|
||
|
||
function getWrappedName(outerType, innerType, wrapperName) {
|
||
var functionName = innerType.displayName || innerType.name || '';
|
||
return outerType.displayName || (functionName !== '' ? wrapperName + '(' + functionName + ')' : wrapperName);
|
||
}
|
||
|
||
function getComponentName(type) {
|
||
if (type == null) {
|
||
// Host root, text node or just invalid type.
|
||
return null;
|
||
}
|
||
if (typeof type === 'function') {
|
||
return type.displayName || type.name || null;
|
||
}
|
||
if (typeof type === 'string') {
|
||
return type;
|
||
}
|
||
switch (type) {
|
||
case REACT_CONCURRENT_MODE_TYPE:
|
||
return 'ConcurrentMode';
|
||
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';
|
||
}
|
||
if (typeof type === 'object') {
|
||
switch (type.$$typeof) {
|
||
case REACT_CONTEXT_TYPE:
|
||
return 'Context.Consumer';
|
||
case REACT_PROVIDER_TYPE:
|
||
return 'Context.Provider';
|
||
case REACT_FORWARD_REF_TYPE:
|
||
return getWrappedName(type, type.render, 'ForwardRef');
|
||
case REACT_MEMO_TYPE:
|
||
return getComponentName(type.type);
|
||
case REACT_LAZY_TYPE:
|
||
{
|
||
var thenable = type;
|
||
var resolvedThenable = refineResolvedLazyComponent(thenable);
|
||
if (resolvedThenable) {
|
||
return getComponentName(resolvedThenable);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
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 DehydratedSuspenseComponent = 18;
|
||
|
||
// Don't change these two values. They're used by React Dev Tools.
|
||
var NoEffect = /* */0;
|
||
var PerformedWork = /* */1;
|
||
|
||
// You can change the rest (and add more).
|
||
var Placement = /* */2;
|
||
var Update = /* */4;
|
||
var PlacementAndUpdate = /* */6;
|
||
var Deletion = /* */8;
|
||
var ContentReset = /* */16;
|
||
var Callback = /* */32;
|
||
var DidCapture = /* */64;
|
||
var Ref = /* */128;
|
||
var Snapshot = /* */256;
|
||
var Passive = /* */512;
|
||
|
||
// Passive & Update & Callback & Ref & Snapshot
|
||
var LifecycleEffectMask = /* */932;
|
||
|
||
// Union of all host effects
|
||
var HostEffectMask = /* */1023;
|
||
|
||
var Incomplete = /* */1024;
|
||
var ShouldCapture = /* */2048;
|
||
|
||
var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
||
|
||
var MOUNTING = 1;
|
||
var MOUNTED = 2;
|
||
var UNMOUNTED = 3;
|
||
|
||
function isFiberMountedImpl(fiber) {
|
||
var node = 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.
|
||
if ((node.effectTag & Placement) !== NoEffect) {
|
||
return MOUNTING;
|
||
}
|
||
while (node.return) {
|
||
node = node.return;
|
||
if ((node.effectTag & Placement) !== NoEffect) {
|
||
return MOUNTING;
|
||
}
|
||
}
|
||
} else {
|
||
while (node.return) {
|
||
node = node.return;
|
||
}
|
||
}
|
||
if (node.tag === HostRoot) {
|
||
// TODO: Check if this was a nested HostRoot when used with
|
||
// renderContainerIntoSubtree.
|
||
return MOUNTED;
|
||
}
|
||
// If we didn't hit the root, that means that we're in an disconnected tree
|
||
// that has been unmounted.
|
||
return UNMOUNTED;
|
||
}
|
||
|
||
function isFiberMounted(fiber) {
|
||
return isFiberMountedImpl(fiber) === MOUNTED;
|
||
}
|
||
|
||
function isMounted(component) {
|
||
var fiber = get(component);
|
||
if (!fiber) {
|
||
return false;
|
||
}
|
||
return isFiberMountedImpl(fiber) === MOUNTED;
|
||
}
|
||
|
||
function assertIsMounted(fiber) {
|
||
!(isFiberMountedImpl(fiber) === MOUNTED) ? reactProdInvariant('188') : void 0;
|
||
}
|
||
|
||
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 state = isFiberMountedImpl(fiber);
|
||
!(state !== UNMOUNTED) ? reactProdInvariant('188') : void 0;
|
||
if (state === MOUNTING) {
|
||
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;
|
||
var parentB = parentA ? parentA.alternate : null;
|
||
if (!parentA || !parentB) {
|
||
// 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.
|
||
reactProdInvariant('188');
|
||
}
|
||
|
||
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;
|
||
}
|
||
!didFindChild ? reactProdInvariant('189') : void 0;
|
||
}
|
||
}
|
||
|
||
!(a.alternate === b) ? reactProdInvariant('190') : void 0;
|
||
}
|
||
// If the root is not a host container, we're in a disconnected tree. I.e.
|
||
// unmounted.
|
||
!(a.tag === HostRoot) ? reactProdInvariant('188') : void 0;
|
||
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);
|
||
if (!currentParent) {
|
||
return null;
|
||
}
|
||
|
||
// Next we'll drill down this component to find the first HostComponent/Text.
|
||
var node = currentParent;
|
||
while (true) {
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
return node;
|
||
} else if (node.child) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === currentParent) {
|
||
return null;
|
||
}
|
||
while (!node.sibling) {
|
||
if (!node.return || node.return === currentParent) {
|
||
return null;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
// Flow needs the return null here, but ESLint complains about it.
|
||
// eslint-disable-next-line no-unreachable
|
||
return null;
|
||
}
|
||
|
||
// Current virtual time
|
||
var nowImplementation = function () {
|
||
return 0;
|
||
};
|
||
var scheduledCallback = null;
|
||
var yieldedValues = [];
|
||
|
||
var didStop = false;
|
||
var expectedNumberOfYields = -1;
|
||
|
||
function scheduleDeferredCallback$1(callback, options) {
|
||
scheduledCallback = callback;
|
||
var fakeCallbackId = 0;
|
||
return fakeCallbackId;
|
||
}
|
||
|
||
function cancelDeferredCallback$1(timeoutID) {
|
||
scheduledCallback = null;
|
||
}
|
||
|
||
function setNowImplementation(implementation) {
|
||
nowImplementation = implementation;
|
||
}
|
||
|
||
function shouldYield$1() {
|
||
if (expectedNumberOfYields !== -1 && yieldedValues.length >= expectedNumberOfYields) {
|
||
// We yielded at least as many values as expected. Stop rendering.
|
||
didStop = true;
|
||
return true;
|
||
}
|
||
// Keep rendering.
|
||
return false;
|
||
}
|
||
|
||
function flushAll() {
|
||
yieldedValues = [];
|
||
while (scheduledCallback !== null) {
|
||
var cb = scheduledCallback;
|
||
scheduledCallback = null;
|
||
cb();
|
||
}
|
||
var values = yieldedValues;
|
||
yieldedValues = [];
|
||
return values;
|
||
}
|
||
|
||
function flushNumberOfYields(count) {
|
||
expectedNumberOfYields = count;
|
||
didStop = false;
|
||
yieldedValues = [];
|
||
try {
|
||
while (scheduledCallback !== null && !didStop) {
|
||
var cb = scheduledCallback;
|
||
scheduledCallback = null;
|
||
cb();
|
||
}
|
||
return yieldedValues;
|
||
} finally {
|
||
expectedNumberOfYields = -1;
|
||
didStop = false;
|
||
yieldedValues = [];
|
||
}
|
||
}
|
||
|
||
function yieldValue(value) {
|
||
yieldedValues.push(value);
|
||
}
|
||
|
||
function clearYields() {
|
||
var values = yieldedValues;
|
||
yieldedValues = [];
|
||
return values;
|
||
}
|
||
|
||
// Renderers that don't support persistence
|
||
// can re-export everything from this module.
|
||
|
||
function shim() {
|
||
reactProdInvariant('270');
|
||
}
|
||
|
||
// Persistence (when unsupported)
|
||
var supportsPersistence = false;
|
||
var cloneInstance = shim;
|
||
var createContainerChildSet = shim;
|
||
var appendChildToContainerChildSet = shim;
|
||
var finalizeContainerChildren = shim;
|
||
var replaceContainerChildren = shim;
|
||
var cloneHiddenInstance = shim;
|
||
var cloneUnhiddenInstance = shim;
|
||
var createHiddenTextInstance = shim;
|
||
|
||
// Renderers that don't support hydration
|
||
// can re-export everything from this module.
|
||
|
||
function shim$1() {
|
||
reactProdInvariant('305');
|
||
}
|
||
|
||
// Hydration (when unsupported)
|
||
|
||
var supportsHydration = false;
|
||
var canHydrateInstance = shim$1;
|
||
var canHydrateTextInstance = shim$1;
|
||
var canHydrateSuspenseInstance = shim$1;
|
||
var getNextHydratableSibling = shim$1;
|
||
var getFirstHydratableChild = shim$1;
|
||
var hydrateInstance = shim$1;
|
||
var hydrateTextInstance = shim$1;
|
||
var getNextHydratableInstanceAfterSuspenseInstance = shim$1;
|
||
var clearSuspenseBoundary = shim$1;
|
||
var clearSuspenseBoundaryFromContainer = shim$1;
|
||
|
||
var NO_CONTEXT = {};
|
||
var UPDATE_SIGNAL = {};
|
||
function getPublicInstance(inst) {
|
||
switch (inst.tag) {
|
||
case 'INSTANCE':
|
||
var _createNodeMock = inst.rootContainerInstance.createNodeMock;
|
||
return _createNodeMock({
|
||
type: inst.type,
|
||
props: inst.props
|
||
});
|
||
default:
|
||
return inst;
|
||
}
|
||
}
|
||
|
||
function appendChild(parentInstance, child) {
|
||
var index = parentInstance.children.indexOf(child);
|
||
if (index !== -1) {
|
||
parentInstance.children.splice(index, 1);
|
||
}
|
||
parentInstance.children.push(child);
|
||
}
|
||
|
||
function insertBefore(parentInstance, child, beforeChild) {
|
||
var index = parentInstance.children.indexOf(child);
|
||
if (index !== -1) {
|
||
parentInstance.children.splice(index, 1);
|
||
}
|
||
var beforeIndex = parentInstance.children.indexOf(beforeChild);
|
||
parentInstance.children.splice(beforeIndex, 0, child);
|
||
}
|
||
|
||
function removeChild(parentInstance, child) {
|
||
var index = parentInstance.children.indexOf(child);
|
||
parentInstance.children.splice(index, 1);
|
||
}
|
||
|
||
function getRootHostContext(rootContainerInstance) {
|
||
return NO_CONTEXT;
|
||
}
|
||
|
||
function getChildHostContext(parentHostContext, type, rootContainerInstance) {
|
||
return NO_CONTEXT;
|
||
}
|
||
|
||
function prepareForCommit(containerInfo) {
|
||
// noop
|
||
}
|
||
|
||
function resetAfterCommit(containerInfo) {
|
||
// noop
|
||
}
|
||
|
||
function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
|
||
return {
|
||
type: type,
|
||
props: props,
|
||
isHidden: false,
|
||
children: [],
|
||
rootContainerInstance: rootContainerInstance,
|
||
tag: 'INSTANCE'
|
||
};
|
||
}
|
||
|
||
function appendInitialChild(parentInstance, child) {
|
||
var index = parentInstance.children.indexOf(child);
|
||
if (index !== -1) {
|
||
parentInstance.children.splice(index, 1);
|
||
}
|
||
parentInstance.children.push(child);
|
||
}
|
||
|
||
function finalizeInitialChildren(testElement, type, props, rootContainerInstance, hostContext) {
|
||
return false;
|
||
}
|
||
|
||
function prepareUpdate(testElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
|
||
return UPDATE_SIGNAL;
|
||
}
|
||
|
||
function shouldSetTextContent(type, props) {
|
||
return false;
|
||
}
|
||
|
||
function shouldDeprioritizeSubtree(type, props) {
|
||
return false;
|
||
}
|
||
|
||
function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
|
||
return {
|
||
text: text,
|
||
isHidden: false,
|
||
tag: 'TEXT'
|
||
};
|
||
}
|
||
|
||
var isPrimaryRenderer = false;
|
||
// This approach enables `now` to be mocked by tests,
|
||
// Even after the reconciler has initialized and read host config values.
|
||
var now = function () {
|
||
return nowImplementation();
|
||
};
|
||
var scheduleDeferredCallback$$1 = scheduleDeferredCallback$1;
|
||
var cancelDeferredCallback$$1 = cancelDeferredCallback$1;
|
||
var shouldYield$$1 = shouldYield$1;
|
||
|
||
var scheduleTimeout = setTimeout;
|
||
var cancelTimeout = clearTimeout;
|
||
var noTimeout = -1;
|
||
var schedulePassiveEffects = scheduleDeferredCallback$$1;
|
||
var cancelPassiveEffects = cancelDeferredCallback$$1;
|
||
|
||
// -------------------
|
||
// Mutation
|
||
// -------------------
|
||
|
||
var supportsMutation = true;
|
||
|
||
function commitUpdate(instance, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
|
||
instance.type = type;
|
||
instance.props = newProps;
|
||
}
|
||
|
||
|
||
|
||
function commitTextUpdate(textInstance, oldText, newText) {
|
||
textInstance.text = newText;
|
||
}
|
||
|
||
function resetTextContent(testElement) {
|
||
// noop
|
||
}
|
||
|
||
var appendChildToContainer = appendChild;
|
||
var insertInContainerBefore = insertBefore;
|
||
var removeChildFromContainer = removeChild;
|
||
|
||
function hideInstance(instance) {
|
||
instance.isHidden = true;
|
||
}
|
||
|
||
function hideTextInstance(textInstance) {
|
||
textInstance.isHidden = true;
|
||
}
|
||
|
||
function unhideInstance(instance, props) {
|
||
instance.isHidden = false;
|
||
}
|
||
|
||
function unhideTextInstance(textInstance, text) {
|
||
textInstance.isHidden = false;
|
||
}
|
||
|
||
/**
|
||
* Copyright (c) 2013-present, Facebook, Inc.
|
||
*
|
||
* This source code is licensed under the MIT license found in the
|
||
* LICENSE file in the root directory of this source tree.
|
||
*/
|
||
|
||
var BEFORE_SLASH_RE = /^(.*)[\\\/]/;
|
||
|
||
var describeComponentFrame = function (name, source, ownerName) {
|
||
var sourceInfo = '';
|
||
if (source) {
|
||
var path = source.fileName;
|
||
var fileName = path.replace(BEFORE_SLASH_RE, '');
|
||
sourceInfo = ' (at ' + fileName + ':' + source.lineNumber + ')';
|
||
} else if (ownerName) {
|
||
sourceInfo = ' (created by ' + ownerName + ')';
|
||
}
|
||
return '\n in ' + (name || 'Unknown') + sourceInfo;
|
||
};
|
||
|
||
var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
||
|
||
function describeFiber(fiber) {
|
||
switch (fiber.tag) {
|
||
case HostRoot:
|
||
case HostPortal:
|
||
case HostText:
|
||
case Fragment:
|
||
case ContextProvider:
|
||
case ContextConsumer:
|
||
return '';
|
||
default:
|
||
var owner = fiber._debugOwner;
|
||
var source = fiber._debugSource;
|
||
var name = getComponentName(fiber.type);
|
||
var ownerName = null;
|
||
if (owner) {
|
||
ownerName = getComponentName(owner.type);
|
||
}
|
||
return describeComponentFrame(name, source, ownerName);
|
||
}
|
||
}
|
||
|
||
function getStackByFiberInDevAndProd(workInProgress) {
|
||
var info = '';
|
||
var node = workInProgress;
|
||
do {
|
||
info += describeFiber(node);
|
||
node = node.return;
|
||
} while (node);
|
||
return info;
|
||
}
|
||
|
||
var enableUserTimingAPI = false;
|
||
|
||
|
||
var enableProfilerTimer = false;
|
||
var enableSchedulerTracing = false;
|
||
var enableSuspenseServerRenderer = false;
|
||
|
||
|
||
|
||
|
||
|
||
// Only used in www builds.
|
||
|
||
// Prefix measurements so that it's possible to filter them.
|
||
// Longer prefixes are hard to read in DevTools.
|
||
var reactEmoji = '\u269B';
|
||
var warningEmoji = '\u26D4';
|
||
var supportsUserTiming = typeof performance !== 'undefined' && typeof performance.mark === 'function' && typeof performance.clearMarks === 'function' && typeof performance.measure === 'function' && typeof performance.clearMeasures === 'function';
|
||
|
||
// Keep track of current fiber so that we know the path to unwind on pause.
|
||
// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
|
||
var currentFiber = null;
|
||
// If we're in the middle of user code, which fiber and method is it?
|
||
// Reusing `currentFiber` would be confusing for this because user code fiber
|
||
// can change during commit phase too, but we don't need to unwind it (since
|
||
// lifecycles in the commit phase don't resemble a tree).
|
||
var currentPhase = null;
|
||
var currentPhaseFiber = null;
|
||
// Did lifecycle hook schedule an update? This is often a performance problem,
|
||
// so we will keep track of it, and include it in the report.
|
||
// Track commits caused by cascading updates.
|
||
var isCommitting = false;
|
||
var hasScheduledUpdateInCurrentCommit = false;
|
||
var hasScheduledUpdateInCurrentPhase = false;
|
||
var commitCountInCurrentWorkLoop = 0;
|
||
var effectCountInCurrentCommit = 0;
|
||
var isWaitingForCallback = false;
|
||
// During commits, we only show a measurement once per method name
|
||
// to avoid stretch the commit phase with measurement overhead.
|
||
var labelsInCurrentCommit = new Set();
|
||
|
||
var formatMarkName = function (markName) {
|
||
return reactEmoji + ' ' + markName;
|
||
};
|
||
|
||
var formatLabel = function (label, warning) {
|
||
var prefix = warning ? warningEmoji + ' ' : reactEmoji + ' ';
|
||
var suffix = warning ? ' Warning: ' + warning : '';
|
||
return '' + prefix + label + suffix;
|
||
};
|
||
|
||
var beginMark = function (markName) {
|
||
performance.mark(formatMarkName(markName));
|
||
};
|
||
|
||
var clearMark = function (markName) {
|
||
performance.clearMarks(formatMarkName(markName));
|
||
};
|
||
|
||
var endMark = function (label, markName, warning) {
|
||
var formattedMarkName = formatMarkName(markName);
|
||
var formattedLabel = formatLabel(label, warning);
|
||
try {
|
||
performance.measure(formattedLabel, formattedMarkName);
|
||
} catch (err) {}
|
||
// If previous mark was missing for some reason, this will throw.
|
||
// This could only happen if React crashed in an unexpected place earlier.
|
||
// Don't pile on with more errors.
|
||
|
||
// Clear marks immediately to avoid growing buffer.
|
||
performance.clearMarks(formattedMarkName);
|
||
performance.clearMeasures(formattedLabel);
|
||
};
|
||
|
||
var getFiberMarkName = function (label, debugID) {
|
||
return label + ' (#' + debugID + ')';
|
||
};
|
||
|
||
var getFiberLabel = function (componentName, isMounted, phase) {
|
||
if (phase === null) {
|
||
// These are composite component total time measurements.
|
||
return componentName + ' [' + (isMounted ? 'update' : 'mount') + ']';
|
||
} else {
|
||
// Composite component methods.
|
||
return componentName + '.' + phase;
|
||
}
|
||
};
|
||
|
||
var beginFiberMark = function (fiber, phase) {
|
||
var componentName = getComponentName(fiber.type) || 'Unknown';
|
||
var debugID = fiber._debugID;
|
||
var isMounted = fiber.alternate !== null;
|
||
var label = getFiberLabel(componentName, isMounted, phase);
|
||
|
||
if (isCommitting && labelsInCurrentCommit.has(label)) {
|
||
// During the commit phase, we don't show duplicate labels because
|
||
// there is a fixed overhead for every measurement, and we don't
|
||
// want to stretch the commit phase beyond necessary.
|
||
return false;
|
||
}
|
||
labelsInCurrentCommit.add(label);
|
||
|
||
var markName = getFiberMarkName(label, debugID);
|
||
beginMark(markName);
|
||
return true;
|
||
};
|
||
|
||
var clearFiberMark = function (fiber, phase) {
|
||
var componentName = getComponentName(fiber.type) || 'Unknown';
|
||
var debugID = fiber._debugID;
|
||
var isMounted = fiber.alternate !== null;
|
||
var label = getFiberLabel(componentName, isMounted, phase);
|
||
var markName = getFiberMarkName(label, debugID);
|
||
clearMark(markName);
|
||
};
|
||
|
||
var endFiberMark = function (fiber, phase, warning) {
|
||
var componentName = getComponentName(fiber.type) || 'Unknown';
|
||
var debugID = fiber._debugID;
|
||
var isMounted = fiber.alternate !== null;
|
||
var label = getFiberLabel(componentName, isMounted, phase);
|
||
var markName = getFiberMarkName(label, debugID);
|
||
endMark(label, markName, warning);
|
||
};
|
||
|
||
var shouldIgnoreFiber = function (fiber) {
|
||
// Host components should be skipped in the timeline.
|
||
// We could check typeof fiber.type, but does this work with RN?
|
||
switch (fiber.tag) {
|
||
case HostRoot:
|
||
case HostComponent:
|
||
case HostText:
|
||
case HostPortal:
|
||
case Fragment:
|
||
case ContextProvider:
|
||
case ContextConsumer:
|
||
case Mode:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
};
|
||
|
||
var clearPendingPhaseMeasurement = function () {
|
||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||
clearFiberMark(currentPhaseFiber, currentPhase);
|
||
}
|
||
currentPhaseFiber = null;
|
||
currentPhase = null;
|
||
hasScheduledUpdateInCurrentPhase = false;
|
||
};
|
||
|
||
var pauseTimers = function () {
|
||
// Stops all currently active measurements so that they can be resumed
|
||
// if we continue in a later deferred loop from the same unit of work.
|
||
var fiber = currentFiber;
|
||
while (fiber) {
|
||
if (fiber._debugIsCurrentlyTiming) {
|
||
endFiberMark(fiber, null, null);
|
||
}
|
||
fiber = fiber.return;
|
||
}
|
||
};
|
||
|
||
var resumeTimersRecursively = function (fiber) {
|
||
if (fiber.return !== null) {
|
||
resumeTimersRecursively(fiber.return);
|
||
}
|
||
if (fiber._debugIsCurrentlyTiming) {
|
||
beginFiberMark(fiber, null);
|
||
}
|
||
};
|
||
|
||
var resumeTimers = function () {
|
||
// Resumes all measurements that were active during the last deferred loop.
|
||
if (currentFiber !== null) {
|
||
resumeTimersRecursively(currentFiber);
|
||
}
|
||
};
|
||
|
||
function recordEffect() {
|
||
if (enableUserTimingAPI) {
|
||
effectCountInCurrentCommit++;
|
||
}
|
||
}
|
||
|
||
function recordScheduleUpdate() {
|
||
if (enableUserTimingAPI) {
|
||
if (isCommitting) {
|
||
hasScheduledUpdateInCurrentCommit = true;
|
||
}
|
||
if (currentPhase !== null && currentPhase !== 'componentWillMount' && currentPhase !== 'componentWillReceiveProps') {
|
||
hasScheduledUpdateInCurrentPhase = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
function startRequestCallbackTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (supportsUserTiming && !isWaitingForCallback) {
|
||
isWaitingForCallback = true;
|
||
beginMark('(Waiting for async callback...)');
|
||
}
|
||
}
|
||
}
|
||
|
||
function stopRequestCallbackTimer(didExpire, expirationTime) {
|
||
if (enableUserTimingAPI) {
|
||
if (supportsUserTiming) {
|
||
isWaitingForCallback = false;
|
||
var warning = didExpire ? 'React was blocked by main thread' : null;
|
||
endMark('(Waiting for async callback... will force flush in ' + expirationTime + ' ms)', '(Waiting for async callback...)', warning);
|
||
}
|
||
}
|
||
}
|
||
|
||
function startWorkTimer(fiber) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||
return;
|
||
}
|
||
// If we pause, this is the fiber to unwind from.
|
||
currentFiber = fiber;
|
||
if (!beginFiberMark(fiber, null)) {
|
||
return;
|
||
}
|
||
fiber._debugIsCurrentlyTiming = true;
|
||
}
|
||
}
|
||
|
||
function cancelWorkTimer(fiber) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||
return;
|
||
}
|
||
// Remember we shouldn't complete measurement for this fiber.
|
||
// Otherwise flamechart will be deep even for small updates.
|
||
fiber._debugIsCurrentlyTiming = false;
|
||
clearFiberMark(fiber, null);
|
||
}
|
||
}
|
||
|
||
function stopWorkTimer(fiber) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||
return;
|
||
}
|
||
// If we pause, its parent is the fiber to unwind from.
|
||
currentFiber = fiber.return;
|
||
if (!fiber._debugIsCurrentlyTiming) {
|
||
return;
|
||
}
|
||
fiber._debugIsCurrentlyTiming = false;
|
||
endFiberMark(fiber, null, null);
|
||
}
|
||
}
|
||
|
||
function stopFailedWorkTimer(fiber) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
|
||
return;
|
||
}
|
||
// If we pause, its parent is the fiber to unwind from.
|
||
currentFiber = fiber.return;
|
||
if (!fiber._debugIsCurrentlyTiming) {
|
||
return;
|
||
}
|
||
fiber._debugIsCurrentlyTiming = false;
|
||
var warning = fiber.tag === SuspenseComponent || fiber.tag === DehydratedSuspenseComponent ? 'Rendering was suspended' : 'An error was thrown inside this error boundary';
|
||
endFiberMark(fiber, null, warning);
|
||
}
|
||
}
|
||
|
||
function startPhaseTimer(fiber, phase) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
clearPendingPhaseMeasurement();
|
||
if (!beginFiberMark(fiber, phase)) {
|
||
return;
|
||
}
|
||
currentPhaseFiber = fiber;
|
||
currentPhase = phase;
|
||
}
|
||
}
|
||
|
||
function stopPhaseTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
if (currentPhase !== null && currentPhaseFiber !== null) {
|
||
var warning = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null;
|
||
endFiberMark(currentPhaseFiber, currentPhase, warning);
|
||
}
|
||
currentPhase = null;
|
||
currentPhaseFiber = null;
|
||
}
|
||
}
|
||
|
||
function startWorkLoopTimer(nextUnitOfWork) {
|
||
if (enableUserTimingAPI) {
|
||
currentFiber = nextUnitOfWork;
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
commitCountInCurrentWorkLoop = 0;
|
||
// This is top level call.
|
||
// Any other measurements are performed within.
|
||
beginMark('(React Tree Reconciliation)');
|
||
// Resume any measurements that were in progress during the last loop.
|
||
resumeTimers();
|
||
}
|
||
}
|
||
|
||
function stopWorkLoopTimer(interruptedBy, didCompleteRoot) {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
var warning = null;
|
||
if (interruptedBy !== null) {
|
||
if (interruptedBy.tag === HostRoot) {
|
||
warning = 'A top-level update interrupted the previous render';
|
||
} else {
|
||
var componentName = getComponentName(interruptedBy.type) || 'Unknown';
|
||
warning = 'An update to ' + componentName + ' interrupted the previous render';
|
||
}
|
||
} else if (commitCountInCurrentWorkLoop > 1) {
|
||
warning = 'There were cascading updates';
|
||
}
|
||
commitCountInCurrentWorkLoop = 0;
|
||
var label = didCompleteRoot ? '(React Tree Reconciliation: Completed Root)' : '(React Tree Reconciliation: Yielded)';
|
||
// Pause any measurements until the next loop.
|
||
pauseTimers();
|
||
endMark(label, '(React Tree Reconciliation)', warning);
|
||
}
|
||
}
|
||
|
||
function startCommitTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
isCommitting = true;
|
||
hasScheduledUpdateInCurrentCommit = false;
|
||
labelsInCurrentCommit.clear();
|
||
beginMark('(Committing Changes)');
|
||
}
|
||
}
|
||
|
||
function stopCommitTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
|
||
var warning = null;
|
||
if (hasScheduledUpdateInCurrentCommit) {
|
||
warning = 'Lifecycle hook scheduled a cascading update';
|
||
} else if (commitCountInCurrentWorkLoop > 0) {
|
||
warning = 'Caused by a cascading update in earlier commit';
|
||
}
|
||
hasScheduledUpdateInCurrentCommit = false;
|
||
commitCountInCurrentWorkLoop++;
|
||
isCommitting = false;
|
||
labelsInCurrentCommit.clear();
|
||
|
||
endMark('(Committing Changes)', '(Committing Changes)', warning);
|
||
}
|
||
}
|
||
|
||
function startCommitSnapshotEffectsTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
effectCountInCurrentCommit = 0;
|
||
beginMark('(Committing Snapshot Effects)');
|
||
}
|
||
}
|
||
|
||
function stopCommitSnapshotEffectsTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
var count = effectCountInCurrentCommit;
|
||
effectCountInCurrentCommit = 0;
|
||
endMark('(Committing Snapshot Effects: ' + count + ' Total)', '(Committing Snapshot Effects)', null);
|
||
}
|
||
}
|
||
|
||
function startCommitHostEffectsTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
effectCountInCurrentCommit = 0;
|
||
beginMark('(Committing Host Effects)');
|
||
}
|
||
}
|
||
|
||
function stopCommitHostEffectsTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
var count = effectCountInCurrentCommit;
|
||
effectCountInCurrentCommit = 0;
|
||
endMark('(Committing Host Effects: ' + count + ' Total)', '(Committing Host Effects)', null);
|
||
}
|
||
}
|
||
|
||
function startCommitLifeCyclesTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
effectCountInCurrentCommit = 0;
|
||
beginMark('(Calling Lifecycle Methods)');
|
||
}
|
||
}
|
||
|
||
function stopCommitLifeCyclesTimer() {
|
||
if (enableUserTimingAPI) {
|
||
if (!supportsUserTiming) {
|
||
return;
|
||
}
|
||
var count = effectCountInCurrentCommit;
|
||
effectCountInCurrentCommit = 0;
|
||
endMark('(Calling Lifecycle Methods: ' + count + ' Total)', '(Calling Lifecycle Methods)', null);
|
||
}
|
||
}
|
||
|
||
var valueStack = [];
|
||
|
||
var index = -1;
|
||
|
||
function createCursor(defaultValue) {
|
||
return {
|
||
current: defaultValue
|
||
};
|
||
}
|
||
|
||
function pop(cursor, fiber) {
|
||
if (index < 0) {
|
||
return;
|
||
}
|
||
|
||
cursor.current = valueStack[index];
|
||
|
||
valueStack[index] = null;
|
||
|
||
index--;
|
||
}
|
||
|
||
function push(cursor, value, fiber) {
|
||
index++;
|
||
|
||
valueStack[index] = cursor.current;
|
||
|
||
cursor.current = value;
|
||
}
|
||
|
||
var 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];
|
||
}
|
||
|
||
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) {
|
||
!(contextStackCursor.current === emptyContextObject) ? reactProdInvariant('168') : void 0;
|
||
|
||
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') {
|
||
return parentContext;
|
||
}
|
||
|
||
var childContext = void 0;
|
||
startPhaseTimer(fiber, 'getChildContext');
|
||
childContext = instance.getChildContext();
|
||
stopPhaseTimer();
|
||
for (var contextKey in childContext) {
|
||
!(contextKey in childContextTypes) ? reactProdInvariant('108', getComponentName(type) || 'Unknown', contextKey) : void 0;
|
||
}
|
||
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;
|
||
!instance ? reactProdInvariant('169') : void 0;
|
||
|
||
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
|
||
!(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? reactProdInvariant('170') : void 0;
|
||
|
||
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);
|
||
reactProdInvariant('171');
|
||
}
|
||
|
||
var onCommitFiberRoot = null;
|
||
var onCommitFiberUnmount = null;
|
||
function catchErrors(fn) {
|
||
return function (arg) {
|
||
try {
|
||
return fn(arg);
|
||
} catch (err) {
|
||
|
||
}
|
||
};
|
||
}
|
||
|
||
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) {
|
||
return true;
|
||
}
|
||
try {
|
||
var rendererID = hook.inject(internals);
|
||
// We have successfully injected, so now it is safe to set up hooks.
|
||
onCommitFiberRoot = catchErrors(function (root) {
|
||
return hook.onCommitFiberRoot(rendererID, root);
|
||
});
|
||
onCommitFiberUnmount = catchErrors(function (fiber) {
|
||
return hook.onCommitFiberUnmount(rendererID, fiber);
|
||
});
|
||
} catch (err) {
|
||
// Catch all errors because it is unsafe to throw during initialization.
|
||
|
||
}
|
||
// DevTools exists
|
||
return true;
|
||
}
|
||
|
||
function onCommitRoot(root) {
|
||
if (typeof onCommitFiberRoot === 'function') {
|
||
onCommitFiberRoot(root);
|
||
}
|
||
}
|
||
|
||
function onCommitUnmount(fiber) {
|
||
if (typeof onCommitFiberUnmount === 'function') {
|
||
onCommitFiberUnmount(fiber);
|
||
}
|
||
}
|
||
|
||
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
|
||
// Math.pow(2, 30) - 1
|
||
// 0b111111111111111111111111111111
|
||
var maxSigned31BitInt = 1073741823;
|
||
|
||
var NoWork = 0;
|
||
var Never = 1;
|
||
var Sync = maxSigned31BitInt;
|
||
|
||
var UNIT_SIZE = 10;
|
||
var MAGIC_NUMBER_OFFSET = maxSigned31BitInt - 1;
|
||
|
||
// 1 unit of expiration time represents 10ms.
|
||
function msToExpirationTime(ms) {
|
||
// Always add an offset so that we don't clash with the magic number for NoWork.
|
||
return MAGIC_NUMBER_OFFSET - (ms / UNIT_SIZE | 0);
|
||
}
|
||
|
||
function expirationTimeToMs(expirationTime) {
|
||
return (MAGIC_NUMBER_OFFSET - expirationTime) * UNIT_SIZE;
|
||
}
|
||
|
||
function ceiling(num, precision) {
|
||
return ((num / precision | 0) + 1) * precision;
|
||
}
|
||
|
||
function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
|
||
return MAGIC_NUMBER_OFFSET - ceiling(MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE);
|
||
}
|
||
|
||
var LOW_PRIORITY_EXPIRATION = 5000;
|
||
var LOW_PRIORITY_BATCH_SIZE = 250;
|
||
|
||
function computeAsyncExpiration(currentTime) {
|
||
return computeExpirationBucket(currentTime, LOW_PRIORITY_EXPIRATION, LOW_PRIORITY_BATCH_SIZE);
|
||
}
|
||
|
||
// We intentionally set a higher expiration time for interactive updates in
|
||
// dev than in production.
|
||
//
|
||
// If the main thread is being blocked so long that you hit the expiration,
|
||
// it's a problem that could be solved with better scheduling.
|
||
//
|
||
// People will be more likely to notice this and fix it with the long
|
||
// expiration time in development.
|
||
//
|
||
// In production we opt for better UX at the risk of masking scheduling
|
||
// problems, by expiring fast.
|
||
var HIGH_PRIORITY_EXPIRATION = 150;
|
||
var HIGH_PRIORITY_BATCH_SIZE = 100;
|
||
|
||
function computeInteractiveExpiration(currentTime) {
|
||
return computeExpirationBucket(currentTime, HIGH_PRIORITY_EXPIRATION, HIGH_PRIORITY_BATCH_SIZE);
|
||
}
|
||
|
||
var NoContext = 0;
|
||
var ConcurrentMode = 1;
|
||
var StrictMode = 2;
|
||
var ProfileMode = 4;
|
||
|
||
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.contextDependencies = null;
|
||
|
||
this.mode = mode;
|
||
|
||
// Effects
|
||
this.effectTag = NoEffect;
|
||
this.nextEffect = null;
|
||
|
||
this.firstEffect = null;
|
||
this.lastEffect = null;
|
||
|
||
this.expirationTime = NoWork;
|
||
this.childExpirationTime = NoWork;
|
||
|
||
this.alternate = null;
|
||
|
||
if (enableProfilerTimer) {
|
||
// 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 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(Component) {
|
||
var prototype = Component.prototype;
|
||
return !!(prototype && prototype.isReactComponent);
|
||
}
|
||
|
||
function isSimpleFunctionComponent(type) {
|
||
return typeof type === 'function' && !shouldConstruct(type) && type.defaultProps === undefined;
|
||
}
|
||
|
||
function resolveLazyComponentTag(Component) {
|
||
if (typeof Component === 'function') {
|
||
return shouldConstruct(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, expirationTime) {
|
||
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;
|
||
|
||
workInProgress.alternate = current;
|
||
current.alternate = workInProgress;
|
||
} else {
|
||
workInProgress.pendingProps = pendingProps;
|
||
|
||
// We already have an alternate.
|
||
// Reset the effect tag.
|
||
workInProgress.effectTag = NoEffect;
|
||
|
||
// The effect list is no longer valid.
|
||
workInProgress.nextEffect = null;
|
||
workInProgress.firstEffect = null;
|
||
workInProgress.lastEffect = null;
|
||
|
||
if (enableProfilerTimer) {
|
||
// 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;
|
||
}
|
||
}
|
||
|
||
workInProgress.childExpirationTime = current.childExpirationTime;
|
||
workInProgress.expirationTime = current.expirationTime;
|
||
|
||
workInProgress.child = current.child;
|
||
workInProgress.memoizedProps = current.memoizedProps;
|
||
workInProgress.memoizedState = current.memoizedState;
|
||
workInProgress.updateQueue = current.updateQueue;
|
||
workInProgress.contextDependencies = current.contextDependencies;
|
||
|
||
// These will be overridden during the parent's reconciliation
|
||
workInProgress.sibling = current.sibling;
|
||
workInProgress.index = current.index;
|
||
workInProgress.ref = current.ref;
|
||
|
||
if (enableProfilerTimer) {
|
||
workInProgress.selfBaseDuration = current.selfBaseDuration;
|
||
workInProgress.treeBaseDuration = current.treeBaseDuration;
|
||
}
|
||
|
||
return workInProgress;
|
||
}
|
||
|
||
function createHostRootFiber(isConcurrent) {
|
||
var mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
|
||
|
||
if (enableProfilerTimer && 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, expirationTime) {
|
||
var fiber = void 0;
|
||
|
||
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(type)) {
|
||
fiberTag = ClassComponent;
|
||
}
|
||
} else if (typeof type === 'string') {
|
||
fiberTag = HostComponent;
|
||
} else {
|
||
getTag: switch (type) {
|
||
case REACT_FRAGMENT_TYPE:
|
||
return createFiberFromFragment(pendingProps.children, mode, expirationTime, key);
|
||
case REACT_CONCURRENT_MODE_TYPE:
|
||
return createFiberFromMode(pendingProps, mode | ConcurrentMode | StrictMode, expirationTime, key);
|
||
case REACT_STRICT_MODE_TYPE:
|
||
return createFiberFromMode(pendingProps, mode | StrictMode, expirationTime, key);
|
||
case REACT_PROFILER_TYPE:
|
||
return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
|
||
case REACT_SUSPENSE_TYPE:
|
||
return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
|
||
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;
|
||
break getTag;
|
||
case REACT_MEMO_TYPE:
|
||
fiberTag = MemoComponent;
|
||
break getTag;
|
||
case REACT_LAZY_TYPE:
|
||
fiberTag = LazyComponent;
|
||
resolvedType = null;
|
||
break getTag;
|
||
}
|
||
}
|
||
var info = '';
|
||
reactProdInvariant('130', type == null ? type : typeof type, info);
|
||
}
|
||
}
|
||
}
|
||
|
||
fiber = createFiber(fiberTag, pendingProps, key, mode);
|
||
fiber.elementType = type;
|
||
fiber.type = resolvedType;
|
||
fiber.expirationTime = expirationTime;
|
||
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromElement(element, mode, expirationTime) {
|
||
var owner = null;
|
||
var type = element.type;
|
||
var key = element.key;
|
||
var pendingProps = element.props;
|
||
var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, expirationTime);
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromFragment(elements, mode, expirationTime, key) {
|
||
var fiber = createFiber(Fragment, elements, key, mode);
|
||
fiber.expirationTime = expirationTime;
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromProfiler(pendingProps, mode, expirationTime, key) {
|
||
var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
|
||
// TODO: The Profiler fiber shouldn't have a type. It has a tag.
|
||
fiber.elementType = REACT_PROFILER_TYPE;
|
||
fiber.type = REACT_PROFILER_TYPE;
|
||
fiber.expirationTime = expirationTime;
|
||
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromMode(pendingProps, mode, expirationTime, key) {
|
||
var fiber = createFiber(Mode, pendingProps, key, mode);
|
||
|
||
// TODO: The Mode fiber shouldn't have a type. It has a tag.
|
||
var type = (mode & ConcurrentMode) === NoContext ? REACT_STRICT_MODE_TYPE : REACT_CONCURRENT_MODE_TYPE;
|
||
fiber.elementType = type;
|
||
fiber.type = type;
|
||
|
||
fiber.expirationTime = expirationTime;
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromSuspense(pendingProps, mode, expirationTime, key) {
|
||
var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
|
||
|
||
// TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
|
||
var type = REACT_SUSPENSE_TYPE;
|
||
fiber.elementType = type;
|
||
fiber.type = type;
|
||
|
||
fiber.expirationTime = expirationTime;
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromText(content, mode, expirationTime) {
|
||
var fiber = createFiber(HostText, content, null, mode);
|
||
fiber.expirationTime = expirationTime;
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromHostInstanceForDeletion() {
|
||
var fiber = createFiber(HostComponent, null, null, NoContext);
|
||
// TODO: These should not need a type.
|
||
fiber.elementType = 'DELETED';
|
||
fiber.type = 'DELETED';
|
||
return fiber;
|
||
}
|
||
|
||
function createFiberFromPortal(portal, mode, expirationTime) {
|
||
var pendingProps = portal.children !== null ? portal.children : [];
|
||
var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
|
||
fiber.expirationTime = expirationTime;
|
||
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.
|
||
|
||
var ReactInternals$1 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||
|
||
var _ReactInternals$Sched = ReactInternals$1.SchedulerTracing;
|
||
var __interactionsRef = _ReactInternals$Sched.__interactionsRef;
|
||
var __subscriberRef = _ReactInternals$Sched.__subscriberRef;
|
||
var unstable_clear = _ReactInternals$Sched.unstable_clear;
|
||
var unstable_getCurrent = _ReactInternals$Sched.unstable_getCurrent;
|
||
var unstable_getThreadID = _ReactInternals$Sched.unstable_getThreadID;
|
||
var unstable_subscribe = _ReactInternals$Sched.unstable_subscribe;
|
||
var unstable_trace = _ReactInternals$Sched.unstable_trace;
|
||
var unstable_unsubscribe = _ReactInternals$Sched.unstable_unsubscribe;
|
||
var unstable_wrap = _ReactInternals$Sched.unstable_wrap;
|
||
|
||
// TODO: This should be lifted into the renderer.
|
||
|
||
|
||
// The following attributes are only used by interaction tracing builds.
|
||
// They enable interactions to be associated with their async work,
|
||
// And expose interaction metadata to the React DevTools Profiler plugin.
|
||
// Note that these attributes are only defined when the enableSchedulerTracing flag is enabled.
|
||
|
||
|
||
// Exported FiberRoot type includes all properties,
|
||
// To avoid requiring potentially error-prone :any casts throughout the project.
|
||
// Profiling properties are only safe to access in profiling builds (when enableSchedulerTracing is true).
|
||
// The types are defined separately within this file to ensure they stay in sync.
|
||
// (We don't have to use an inline :any cast when enableSchedulerTracing is disabled.)
|
||
|
||
|
||
function createFiberRoot(containerInfo, isConcurrent, hydrate) {
|
||
// Cyclic construction. This cheats the type system right now because
|
||
// stateNode is any.
|
||
var uninitializedFiber = createHostRootFiber(isConcurrent);
|
||
|
||
var root = void 0;
|
||
if (enableSchedulerTracing) {
|
||
root = {
|
||
current: uninitializedFiber,
|
||
containerInfo: containerInfo,
|
||
pendingChildren: null,
|
||
|
||
earliestPendingTime: NoWork,
|
||
latestPendingTime: NoWork,
|
||
earliestSuspendedTime: NoWork,
|
||
latestSuspendedTime: NoWork,
|
||
latestPingedTime: NoWork,
|
||
|
||
pingCache: null,
|
||
|
||
didError: false,
|
||
|
||
pendingCommitExpirationTime: NoWork,
|
||
finishedWork: null,
|
||
timeoutHandle: noTimeout,
|
||
context: null,
|
||
pendingContext: null,
|
||
hydrate: hydrate,
|
||
nextExpirationTimeToWorkOn: NoWork,
|
||
expirationTime: NoWork,
|
||
firstBatch: null,
|
||
nextScheduledRoot: null,
|
||
|
||
interactionThreadID: unstable_getThreadID(),
|
||
memoizedInteractions: new Set(),
|
||
pendingInteractionMap: new Map()
|
||
};
|
||
} else {
|
||
root = {
|
||
current: uninitializedFiber,
|
||
containerInfo: containerInfo,
|
||
pendingChildren: null,
|
||
|
||
pingCache: null,
|
||
|
||
earliestPendingTime: NoWork,
|
||
latestPendingTime: NoWork,
|
||
earliestSuspendedTime: NoWork,
|
||
latestSuspendedTime: NoWork,
|
||
latestPingedTime: NoWork,
|
||
|
||
didError: false,
|
||
|
||
pendingCommitExpirationTime: NoWork,
|
||
finishedWork: null,
|
||
timeoutHandle: noTimeout,
|
||
context: null,
|
||
pendingContext: null,
|
||
hydrate: hydrate,
|
||
nextExpirationTimeToWorkOn: NoWork,
|
||
expirationTime: NoWork,
|
||
firstBatch: null,
|
||
nextScheduledRoot: null
|
||
};
|
||
}
|
||
|
||
uninitializedFiber.stateNode = root;
|
||
|
||
// The reason for the way the Flow types are structured in this file,
|
||
// Is to avoid needing :any casts everywhere interaction tracing fields are used.
|
||
// Unfortunately that requires an :any cast for non-interaction tracing capable builds.
|
||
// $FlowFixMe Remove this :any cast and replace it with something better.
|
||
return root;
|
||
}
|
||
|
||
var ReactInternals$2 = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
||
|
||
var _ReactInternals$Sched$1 = ReactInternals$2.Scheduler;
|
||
var unstable_cancelCallback = _ReactInternals$Sched$1.unstable_cancelCallback;
|
||
var unstable_now = _ReactInternals$Sched$1.unstable_now;
|
||
var unstable_scheduleCallback = _ReactInternals$Sched$1.unstable_scheduleCallback;
|
||
var unstable_shouldYield = _ReactInternals$Sched$1.unstable_shouldYield;
|
||
var unstable_getFirstCallbackNode = _ReactInternals$Sched$1.unstable_getFirstCallbackNode;
|
||
var unstable_runWithPriority = _ReactInternals$Sched$1.unstable_runWithPriority;
|
||
var unstable_next = _ReactInternals$Sched$1.unstable_next;
|
||
var unstable_continueExecution = _ReactInternals$Sched$1.unstable_continueExecution;
|
||
var unstable_pauseExecution = _ReactInternals$Sched$1.unstable_pauseExecution;
|
||
var unstable_getCurrentPriorityLevel = _ReactInternals$Sched$1.unstable_getCurrentPriorityLevel;
|
||
var unstable_ImmediatePriority = _ReactInternals$Sched$1.unstable_ImmediatePriority;
|
||
var unstable_UserBlockingPriority = _ReactInternals$Sched$1.unstable_UserBlockingPriority;
|
||
var unstable_NormalPriority = _ReactInternals$Sched$1.unstable_NormalPriority;
|
||
var unstable_LowPriority = _ReactInternals$Sched$1.unstable_LowPriority;
|
||
var unstable_IdlePriority = _ReactInternals$Sched$1.unstable_IdlePriority;
|
||
|
||
/**
|
||
* 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
|
||
*/
|
||
|
||
|
||
/**
|
||
* Same as invokeGuardedCallback, but instead of returning an error, it stores
|
||
* it in a global so it can be rethrown by `rethrowCaughtError` later.
|
||
* TODO: See if caughtError and rethrowError can be unified.
|
||
*
|
||
* @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
|
||
*/
|
||
|
||
|
||
/**
|
||
* During execution of guarded functions we will capture the first error which
|
||
* we will rethrow to be handled by the top level error handler.
|
||
*/
|
||
|
||
/**
|
||
* Forked from fbjs/warning:
|
||
* https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
|
||
*
|
||
* Only change is we use console.warn instead of console.error,
|
||
* and do nothing when 'console' is not supported.
|
||
* This really simplifies the code.
|
||
* ---
|
||
* Similar to invariant but only logs a warning if the condition is not met.
|
||
* This can be used to log issues in development environments in critical
|
||
* paths. Removing the logging code for production environments will keep the
|
||
* same logic and follow the same code paths.
|
||
*/
|
||
|
||
// This lets us hook into Fiber to debug what it's doing.
|
||
// See https://github.com/facebook/react/pull/8033.
|
||
// This is not part of the public API, not even for React DevTools.
|
||
// You may only inject a debugTool if you work on React Fiber itself.
|
||
|
||
// TODO: Offscreen updates should never suspend. However, a promise that
|
||
// suspended inside an offscreen subtree should be able to ping at the priority
|
||
// of the outer render.
|
||
|
||
function markPendingPriorityLevel(root, expirationTime) {
|
||
// If there's a gap between completing a failed root and retrying it,
|
||
// additional updates may be scheduled. Clear `didError`, in case the update
|
||
// is sufficient to fix the error.
|
||
root.didError = false;
|
||
|
||
// Update the latest and earliest pending times
|
||
var earliestPendingTime = root.earliestPendingTime;
|
||
if (earliestPendingTime === NoWork) {
|
||
// No other pending updates.
|
||
root.earliestPendingTime = root.latestPendingTime = expirationTime;
|
||
} else {
|
||
if (earliestPendingTime < expirationTime) {
|
||
// This is the earliest pending update.
|
||
root.earliestPendingTime = expirationTime;
|
||
} else {
|
||
var latestPendingTime = root.latestPendingTime;
|
||
if (latestPendingTime > expirationTime) {
|
||
// This is the latest pending update
|
||
root.latestPendingTime = expirationTime;
|
||
}
|
||
}
|
||
}
|
||
findNextExpirationTimeToWorkOn(expirationTime, root);
|
||
}
|
||
|
||
function markCommittedPriorityLevels(root, earliestRemainingTime) {
|
||
root.didError = false;
|
||
|
||
if (earliestRemainingTime === NoWork) {
|
||
// Fast path. There's no remaining work. Clear everything.
|
||
root.earliestPendingTime = NoWork;
|
||
root.latestPendingTime = NoWork;
|
||
root.earliestSuspendedTime = NoWork;
|
||
root.latestSuspendedTime = NoWork;
|
||
root.latestPingedTime = NoWork;
|
||
findNextExpirationTimeToWorkOn(NoWork, root);
|
||
return;
|
||
}
|
||
|
||
if (earliestRemainingTime < root.latestPingedTime) {
|
||
root.latestPingedTime = NoWork;
|
||
}
|
||
|
||
// Let's see if the previous latest known pending level was just flushed.
|
||
var latestPendingTime = root.latestPendingTime;
|
||
if (latestPendingTime !== NoWork) {
|
||
if (latestPendingTime > earliestRemainingTime) {
|
||
// We've flushed all the known pending levels.
|
||
root.earliestPendingTime = root.latestPendingTime = NoWork;
|
||
} else {
|
||
var earliestPendingTime = root.earliestPendingTime;
|
||
if (earliestPendingTime > earliestRemainingTime) {
|
||
// We've flushed the earliest known pending level. Set this to the
|
||
// latest pending time.
|
||
root.earliestPendingTime = root.latestPendingTime;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Now let's handle the earliest remaining level in the whole tree. We need to
|
||
// decide whether to treat it as a pending level or as suspended. Check
|
||
// it falls within the range of known suspended levels.
|
||
|
||
var earliestSuspendedTime = root.earliestSuspendedTime;
|
||
if (earliestSuspendedTime === NoWork) {
|
||
// There's no suspended work. Treat the earliest remaining level as a
|
||
// pending level.
|
||
markPendingPriorityLevel(root, earliestRemainingTime);
|
||
findNextExpirationTimeToWorkOn(NoWork, root);
|
||
return;
|
||
}
|
||
|
||
var latestSuspendedTime = root.latestSuspendedTime;
|
||
if (earliestRemainingTime < latestSuspendedTime) {
|
||
// The earliest remaining level is later than all the suspended work. That
|
||
// means we've flushed all the suspended work.
|
||
root.earliestSuspendedTime = NoWork;
|
||
root.latestSuspendedTime = NoWork;
|
||
root.latestPingedTime = NoWork;
|
||
|
||
// There's no suspended work. Treat the earliest remaining level as a
|
||
// pending level.
|
||
markPendingPriorityLevel(root, earliestRemainingTime);
|
||
findNextExpirationTimeToWorkOn(NoWork, root);
|
||
return;
|
||
}
|
||
|
||
if (earliestRemainingTime > earliestSuspendedTime) {
|
||
// The earliest remaining time is earlier than all the suspended work.
|
||
// Treat it as a pending update.
|
||
markPendingPriorityLevel(root, earliestRemainingTime);
|
||
findNextExpirationTimeToWorkOn(NoWork, root);
|
||
return;
|
||
}
|
||
|
||
// The earliest remaining time falls within the range of known suspended
|
||
// levels. We should treat this as suspended work.
|
||
findNextExpirationTimeToWorkOn(NoWork, root);
|
||
}
|
||
|
||
function hasLowerPriorityWork(root, erroredExpirationTime) {
|
||
var latestPendingTime = root.latestPendingTime;
|
||
var latestSuspendedTime = root.latestSuspendedTime;
|
||
var latestPingedTime = root.latestPingedTime;
|
||
return latestPendingTime !== NoWork && latestPendingTime < erroredExpirationTime || latestSuspendedTime !== NoWork && latestSuspendedTime < erroredExpirationTime || latestPingedTime !== NoWork && latestPingedTime < erroredExpirationTime;
|
||
}
|
||
|
||
function isPriorityLevelSuspended(root, expirationTime) {
|
||
var earliestSuspendedTime = root.earliestSuspendedTime;
|
||
var latestSuspendedTime = root.latestSuspendedTime;
|
||
return earliestSuspendedTime !== NoWork && expirationTime <= earliestSuspendedTime && expirationTime >= latestSuspendedTime;
|
||
}
|
||
|
||
function markSuspendedPriorityLevel(root, suspendedTime) {
|
||
root.didError = false;
|
||
clearPing(root, suspendedTime);
|
||
|
||
// First, check the known pending levels and update them if needed.
|
||
var earliestPendingTime = root.earliestPendingTime;
|
||
var latestPendingTime = root.latestPendingTime;
|
||
if (earliestPendingTime === suspendedTime) {
|
||
if (latestPendingTime === suspendedTime) {
|
||
// Both known pending levels were suspended. Clear them.
|
||
root.earliestPendingTime = root.latestPendingTime = NoWork;
|
||
} else {
|
||
// The earliest pending level was suspended. Clear by setting it to the
|
||
// latest pending level.
|
||
root.earliestPendingTime = latestPendingTime;
|
||
}
|
||
} else if (latestPendingTime === suspendedTime) {
|
||
// The latest pending level was suspended. Clear by setting it to the
|
||
// latest pending level.
|
||
root.latestPendingTime = earliestPendingTime;
|
||
}
|
||
|
||
// Finally, update the known suspended levels.
|
||
var earliestSuspendedTime = root.earliestSuspendedTime;
|
||
var latestSuspendedTime = root.latestSuspendedTime;
|
||
if (earliestSuspendedTime === NoWork) {
|
||
// No other suspended levels.
|
||
root.earliestSuspendedTime = root.latestSuspendedTime = suspendedTime;
|
||
} else {
|
||
if (earliestSuspendedTime < suspendedTime) {
|
||
// This is the earliest suspended level.
|
||
root.earliestSuspendedTime = suspendedTime;
|
||
} else if (latestSuspendedTime > suspendedTime) {
|
||
// This is the latest suspended level
|
||
root.latestSuspendedTime = suspendedTime;
|
||
}
|
||
}
|
||
|
||
findNextExpirationTimeToWorkOn(suspendedTime, root);
|
||
}
|
||
|
||
function markPingedPriorityLevel(root, pingedTime) {
|
||
root.didError = false;
|
||
|
||
// TODO: When we add back resuming, we need to ensure the progressed work
|
||
// is thrown out and not reused during the restarted render. One way to
|
||
// invalidate the progressed work is to restart at expirationTime + 1.
|
||
var latestPingedTime = root.latestPingedTime;
|
||
if (latestPingedTime === NoWork || latestPingedTime > pingedTime) {
|
||
root.latestPingedTime = pingedTime;
|
||
}
|
||
findNextExpirationTimeToWorkOn(pingedTime, root);
|
||
}
|
||
|
||
function clearPing(root, completedTime) {
|
||
var latestPingedTime = root.latestPingedTime;
|
||
if (latestPingedTime >= completedTime) {
|
||
root.latestPingedTime = NoWork;
|
||
}
|
||
}
|
||
|
||
function findEarliestOutstandingPriorityLevel(root, renderExpirationTime) {
|
||
var earliestExpirationTime = renderExpirationTime;
|
||
|
||
var earliestPendingTime = root.earliestPendingTime;
|
||
var earliestSuspendedTime = root.earliestSuspendedTime;
|
||
if (earliestPendingTime > earliestExpirationTime) {
|
||
earliestExpirationTime = earliestPendingTime;
|
||
}
|
||
if (earliestSuspendedTime > earliestExpirationTime) {
|
||
earliestExpirationTime = earliestSuspendedTime;
|
||
}
|
||
return earliestExpirationTime;
|
||
}
|
||
|
||
function didExpireAtExpirationTime(root, currentTime) {
|
||
var expirationTime = root.expirationTime;
|
||
if (expirationTime !== NoWork && currentTime <= expirationTime) {
|
||
// The root has expired. Flush all work up to the current time.
|
||
root.nextExpirationTimeToWorkOn = currentTime;
|
||
}
|
||
}
|
||
|
||
function findNextExpirationTimeToWorkOn(completedExpirationTime, root) {
|
||
var earliestSuspendedTime = root.earliestSuspendedTime;
|
||
var latestSuspendedTime = root.latestSuspendedTime;
|
||
var earliestPendingTime = root.earliestPendingTime;
|
||
var latestPingedTime = root.latestPingedTime;
|
||
|
||
// Work on the earliest pending time. Failing that, work on the latest
|
||
// pinged time.
|
||
var nextExpirationTimeToWorkOn = earliestPendingTime !== NoWork ? earliestPendingTime : latestPingedTime;
|
||
|
||
// If there is no pending or pinged work, check if there's suspended work
|
||
// that's lower priority than what we just completed.
|
||
if (nextExpirationTimeToWorkOn === NoWork && (completedExpirationTime === NoWork || latestSuspendedTime < completedExpirationTime)) {
|
||
// The lowest priority suspended work is the work most likely to be
|
||
// committed next. Let's start rendering it again, so that if it times out,
|
||
// it's ready to commit.
|
||
nextExpirationTimeToWorkOn = latestSuspendedTime;
|
||
}
|
||
|
||
var expirationTime = nextExpirationTimeToWorkOn;
|
||
if (expirationTime !== NoWork && earliestSuspendedTime > expirationTime) {
|
||
// Expire using the earliest known expiration time.
|
||
expirationTime = earliestSuspendedTime;
|
||
}
|
||
|
||
root.nextExpirationTimeToWorkOn = nextExpirationTimeToWorkOn;
|
||
root.expirationTime = expirationTime;
|
||
}
|
||
|
||
/**
|
||
* 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 hasOwnProperty = Object.prototype.hasOwnProperty;
|
||
|
||
/**
|
||
* 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 (is(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++) {
|
||
if (!hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysA[i]])) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
function readLazyComponentType(lazyComponent) {
|
||
var status = lazyComponent._status;
|
||
var result = lazyComponent._result;
|
||
switch (status) {
|
||
case Resolved:
|
||
{
|
||
var Component = result;
|
||
return Component;
|
||
}
|
||
case Rejected:
|
||
{
|
||
var error = result;
|
||
throw error;
|
||
}
|
||
case Pending:
|
||
{
|
||
var thenable = result;
|
||
throw thenable;
|
||
}
|
||
default:
|
||
{
|
||
lazyComponent._status = Pending;
|
||
var ctor = lazyComponent._ctor;
|
||
var _thenable = ctor();
|
||
_thenable.then(function (moduleObject) {
|
||
if (lazyComponent._status === Pending) {
|
||
var defaultExport = moduleObject.default;
|
||
lazyComponent._status = Resolved;
|
||
lazyComponent._result = defaultExport;
|
||
}
|
||
}, function (error) {
|
||
if (lazyComponent._status === Pending) {
|
||
lazyComponent._status = Rejected;
|
||
lazyComponent._result = error;
|
||
}
|
||
});
|
||
// Handle synchronous thenables.
|
||
switch (lazyComponent._status) {
|
||
case Resolved:
|
||
return lazyComponent._result;
|
||
case Rejected:
|
||
throw lazyComponent._result;
|
||
}
|
||
lazyComponent._result = _thenable;
|
||
throw _thenable;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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;
|
||
|
||
function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
|
||
var prevState = workInProgress.memoizedState;
|
||
|
||
var partialState = getDerivedStateFromProps(nextProps, prevState);
|
||
|
||
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.
|
||
var updateQueue = workInProgress.updateQueue;
|
||
if (updateQueue !== null && workInProgress.expirationTime === NoWork) {
|
||
updateQueue.baseState = memoizedState;
|
||
}
|
||
}
|
||
|
||
var classComponentUpdater = {
|
||
isMounted: isMounted,
|
||
enqueueSetState: function (inst, payload, callback) {
|
||
var fiber = get(inst);
|
||
var currentTime = requestCurrentTime();
|
||
var expirationTime = computeExpirationForFiber(currentTime, fiber);
|
||
|
||
var update = createUpdate(expirationTime);
|
||
update.payload = payload;
|
||
if (callback !== undefined && callback !== null) {
|
||
update.callback = callback;
|
||
}
|
||
|
||
flushPassiveEffects$1();
|
||
enqueueUpdate(fiber, update);
|
||
scheduleWork(fiber, expirationTime);
|
||
},
|
||
enqueueReplaceState: function (inst, payload, callback) {
|
||
var fiber = get(inst);
|
||
var currentTime = requestCurrentTime();
|
||
var expirationTime = computeExpirationForFiber(currentTime, fiber);
|
||
|
||
var update = createUpdate(expirationTime);
|
||
update.tag = ReplaceState;
|
||
update.payload = payload;
|
||
|
||
if (callback !== undefined && callback !== null) {
|
||
update.callback = callback;
|
||
}
|
||
|
||
flushPassiveEffects$1();
|
||
enqueueUpdate(fiber, update);
|
||
scheduleWork(fiber, expirationTime);
|
||
},
|
||
enqueueForceUpdate: function (inst, callback) {
|
||
var fiber = get(inst);
|
||
var currentTime = requestCurrentTime();
|
||
var expirationTime = computeExpirationForFiber(currentTime, fiber);
|
||
|
||
var update = createUpdate(expirationTime);
|
||
update.tag = ForceUpdate;
|
||
|
||
if (callback !== undefined && callback !== null) {
|
||
update.callback = callback;
|
||
}
|
||
|
||
flushPassiveEffects$1();
|
||
enqueueUpdate(fiber, update);
|
||
scheduleWork(fiber, expirationTime);
|
||
}
|
||
};
|
||
|
||
function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
|
||
var instance = workInProgress.stateNode;
|
||
if (typeof instance.shouldComponentUpdate === 'function') {
|
||
startPhaseTimer(workInProgress, 'shouldComponentUpdate');
|
||
var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
|
||
stopPhaseTimer();
|
||
|
||
return shouldUpdate;
|
||
}
|
||
|
||
if (ctor.prototype && ctor.prototype.isPureReactComponent) {
|
||
return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
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);
|
||
|
||
}
|
||
|
||
function constructClassInstance(workInProgress, ctor, props, renderExpirationTime) {
|
||
var isLegacyContextConsumer = false;
|
||
var unmaskedContext = emptyContextObject;
|
||
var context = null;
|
||
var contextType = ctor.contextType;
|
||
|
||
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;
|
||
}
|
||
|
||
// Instantiate twice to help detect side-effects.
|
||
var instance = new ctor(props, context);
|
||
var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
|
||
adoptClassInstance(workInProgress, instance);
|
||
|
||
if (isLegacyContextConsumer) {
|
||
cacheContext(workInProgress, unmaskedContext, context);
|
||
}
|
||
|
||
return instance;
|
||
}
|
||
|
||
function callComponentWillMount(workInProgress, instance) {
|
||
startPhaseTimer(workInProgress, 'componentWillMount');
|
||
var oldState = instance.state;
|
||
|
||
if (typeof instance.componentWillMount === 'function') {
|
||
instance.componentWillMount();
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
instance.UNSAFE_componentWillMount();
|
||
}
|
||
|
||
stopPhaseTimer();
|
||
|
||
if (oldState !== instance.state) {
|
||
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
||
}
|
||
}
|
||
|
||
function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
|
||
var oldState = instance.state;
|
||
startPhaseTimer(workInProgress, 'componentWillReceiveProps');
|
||
if (typeof instance.componentWillReceiveProps === 'function') {
|
||
instance.componentWillReceiveProps(newProps, nextContext);
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
||
instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
|
||
}
|
||
stopPhaseTimer();
|
||
|
||
if (instance.state !== oldState) {
|
||
classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
||
}
|
||
}
|
||
|
||
// Invokes the mount life-cycles on a previously never rendered instance.
|
||
function mountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
|
||
var instance = workInProgress.stateNode;
|
||
instance.props = newProps;
|
||
instance.state = workInProgress.memoizedState;
|
||
instance.refs = emptyRefsObject;
|
||
|
||
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);
|
||
}
|
||
|
||
var updateQueue = workInProgress.updateQueue;
|
||
if (updateQueue !== null) {
|
||
processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
|
||
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.
|
||
updateQueue = workInProgress.updateQueue;
|
||
if (updateQueue !== null) {
|
||
processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
|
||
instance.state = workInProgress.memoizedState;
|
||
}
|
||
}
|
||
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
}
|
||
|
||
function resumeMountClassInstance(workInProgress, ctor, newProps, renderExpirationTime) {
|
||
var instance = workInProgress.stateNode;
|
||
|
||
var oldProps = workInProgress.memoizedProps;
|
||
instance.props = oldProps;
|
||
|
||
var oldContext = instance.context;
|
||
var contextType = ctor.contextType;
|
||
var nextContext = void 0;
|
||
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;
|
||
var updateQueue = workInProgress.updateQueue;
|
||
if (updateQueue !== null) {
|
||
processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
|
||
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') {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
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')) {
|
||
startPhaseTimer(workInProgress, 'componentWillMount');
|
||
if (typeof instance.componentWillMount === 'function') {
|
||
instance.componentWillMount();
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
||
instance.UNSAFE_componentWillMount();
|
||
}
|
||
stopPhaseTimer();
|
||
}
|
||
if (typeof instance.componentDidMount === 'function') {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
} 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') {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
|
||
// 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, renderExpirationTime) {
|
||
var instance = workInProgress.stateNode;
|
||
|
||
var oldProps = workInProgress.memoizedProps;
|
||
instance.props = workInProgress.type === workInProgress.elementType ? oldProps : resolveDefaultProps(workInProgress.type, oldProps);
|
||
|
||
var oldContext = instance.context;
|
||
var contextType = ctor.contextType;
|
||
var nextContext = void 0;
|
||
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 (oldProps !== newProps || oldContext !== nextContext) {
|
||
callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
|
||
}
|
||
}
|
||
|
||
resetHasForceUpdateBeforeProcessing();
|
||
|
||
var oldState = workInProgress.memoizedState;
|
||
var newState = instance.state = oldState;
|
||
var updateQueue = workInProgress.updateQueue;
|
||
if (updateQueue !== null) {
|
||
processUpdateQueue(workInProgress, updateQueue, newProps, instance, renderExpirationTime);
|
||
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.componentDidUpdate === 'function') {
|
||
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.effectTag |= 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);
|
||
|
||
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')) {
|
||
startPhaseTimer(workInProgress, 'componentWillUpdate');
|
||
if (typeof instance.componentWillUpdate === 'function') {
|
||
instance.componentWillUpdate(newProps, newState, nextContext);
|
||
}
|
||
if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
||
instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
|
||
}
|
||
stopPhaseTimer();
|
||
}
|
||
if (typeof instance.componentDidUpdate === 'function') {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
workInProgress.effectTag |= 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 (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
}
|
||
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
||
if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
||
workInProgress.effectTag |= 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 isArray = Array.isArray;
|
||
|
||
function coerceRef(returnFiber, current$$1, element) {
|
||
var mixedRef = element.ref;
|
||
if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
|
||
if (element._owner) {
|
||
var owner = element._owner;
|
||
var inst = void 0;
|
||
if (owner) {
|
||
var ownerFiber = owner;
|
||
!(ownerFiber.tag === ClassComponent) ? reactProdInvariant('309') : void 0;
|
||
inst = ownerFiber.stateNode;
|
||
}
|
||
!inst ? reactProdInvariant('147', mixedRef) : void 0;
|
||
var stringRef = '' + mixedRef;
|
||
// Check if previous string ref matches new string ref
|
||
if (current$$1 !== null && current$$1.ref !== null && typeof current$$1.ref === 'function' && current$$1.ref._stringRef === stringRef) {
|
||
return current$$1.ref;
|
||
}
|
||
var ref = function (value) {
|
||
var refs = inst.refs;
|
||
if (refs === emptyRefsObject) {
|
||
// This is a lazy pooled frozen object, so we need to initialize.
|
||
refs = inst.refs = {};
|
||
}
|
||
if (value === null) {
|
||
delete refs[stringRef];
|
||
} else {
|
||
refs[stringRef] = value;
|
||
}
|
||
};
|
||
ref._stringRef = stringRef;
|
||
return ref;
|
||
} else {
|
||
!(typeof mixedRef === 'string') ? reactProdInvariant('284') : void 0;
|
||
!element._owner ? reactProdInvariant('290', mixedRef) : void 0;
|
||
}
|
||
}
|
||
return mixedRef;
|
||
}
|
||
|
||
function throwOnInvalidObjectType(returnFiber, newChild) {
|
||
if (returnFiber.type !== 'textarea') {
|
||
var addendum = '';
|
||
reactProdInvariant('31', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum);
|
||
}
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
// Deletions are added in reversed order so we add it to the front.
|
||
// At this point, the return fiber's effect list is empty except for
|
||
// deletions, so we can just append the deletion to the list. The remaining
|
||
// effects aren't added until the complete phase. Once we implement
|
||
// resuming, this may not be true.
|
||
var last = returnFiber.lastEffect;
|
||
if (last !== null) {
|
||
last.nextEffect = childToDelete;
|
||
returnFiber.lastEffect = childToDelete;
|
||
} else {
|
||
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
|
||
}
|
||
childToDelete.nextEffect = null;
|
||
childToDelete.effectTag = Deletion;
|
||
}
|
||
|
||
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
|
||
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, expirationTime) {
|
||
// 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, expirationTime);
|
||
clone.index = 0;
|
||
clone.sibling = null;
|
||
return clone;
|
||
}
|
||
|
||
function placeChild(newFiber, lastPlacedIndex, newIndex) {
|
||
newFiber.index = newIndex;
|
||
if (!shouldTrackSideEffects) {
|
||
// Noop.
|
||
return lastPlacedIndex;
|
||
}
|
||
var current$$1 = newFiber.alternate;
|
||
if (current$$1 !== null) {
|
||
var oldIndex = current$$1.index;
|
||
if (oldIndex < lastPlacedIndex) {
|
||
// This is a move.
|
||
newFiber.effectTag = Placement;
|
||
return lastPlacedIndex;
|
||
} else {
|
||
// This item can stay in place.
|
||
return oldIndex;
|
||
}
|
||
} else {
|
||
// This is an insertion.
|
||
newFiber.effectTag = 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.effectTag = Placement;
|
||
}
|
||
return newFiber;
|
||
}
|
||
|
||
function updateTextNode(returnFiber, current$$1, textContent, expirationTime) {
|
||
if (current$$1 === null || current$$1.tag !== HostText) {
|
||
// Insert
|
||
var created = createFiberFromText(textContent, returnFiber.mode, expirationTime);
|
||
created.return = returnFiber;
|
||
return created;
|
||
} else {
|
||
// Update
|
||
var existing = useFiber(current$$1, textContent, expirationTime);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
|
||
function updateElement(returnFiber, current$$1, element, expirationTime) {
|
||
if (current$$1 !== null && current$$1.elementType === element.type) {
|
||
// Move based on index
|
||
var existing = useFiber(current$$1, element.props, expirationTime);
|
||
existing.ref = coerceRef(returnFiber, current$$1, element);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
} else {
|
||
// Insert
|
||
var created = createFiberFromElement(element, returnFiber.mode, expirationTime);
|
||
created.ref = coerceRef(returnFiber, current$$1, element);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
}
|
||
|
||
function updatePortal(returnFiber, current$$1, portal, expirationTime) {
|
||
if (current$$1 === null || current$$1.tag !== HostPortal || current$$1.stateNode.containerInfo !== portal.containerInfo || current$$1.stateNode.implementation !== portal.implementation) {
|
||
// Insert
|
||
var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
|
||
created.return = returnFiber;
|
||
return created;
|
||
} else {
|
||
// Update
|
||
var existing = useFiber(current$$1, portal.children || [], expirationTime);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
|
||
function updateFragment(returnFiber, current$$1, fragment, expirationTime, key) {
|
||
if (current$$1 === null || current$$1.tag !== Fragment) {
|
||
// Insert
|
||
var created = createFiberFromFragment(fragment, returnFiber.mode, expirationTime, key);
|
||
created.return = returnFiber;
|
||
return created;
|
||
} else {
|
||
// Update
|
||
var existing = useFiber(current$$1, fragment, expirationTime);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
}
|
||
}
|
||
|
||
function createChild(returnFiber, newChild, expirationTime) {
|
||
if (typeof newChild === 'string' || 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, expirationTime);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
var _created = createFiberFromElement(newChild, returnFiber.mode, expirationTime);
|
||
_created.ref = coerceRef(returnFiber, null, newChild);
|
||
_created.return = returnFiber;
|
||
return _created;
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
var _created2 = createFiberFromPortal(newChild, returnFiber.mode, expirationTime);
|
||
_created2.return = returnFiber;
|
||
return _created2;
|
||
}
|
||
}
|
||
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
var _created3 = createFiberFromFragment(newChild, returnFiber.mode, expirationTime, null);
|
||
_created3.return = returnFiber;
|
||
return _created3;
|
||
}
|
||
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
|
||
// Update the fiber if the keys match, otherwise return null.
|
||
|
||
var key = oldFiber !== null ? oldFiber.key : null;
|
||
|
||
if (typeof newChild === 'string' || 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, expirationTime);
|
||
}
|
||
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
if (newChild.key === key) {
|
||
if (newChild.type === REACT_FRAGMENT_TYPE) {
|
||
return updateFragment(returnFiber, oldFiber, newChild.props.children, expirationTime, key);
|
||
}
|
||
return updateElement(returnFiber, oldFiber, newChild, expirationTime);
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
if (newChild.key === key) {
|
||
return updatePortal(returnFiber, oldFiber, newChild, expirationTime);
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
if (key !== null) {
|
||
return null;
|
||
}
|
||
|
||
return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null);
|
||
}
|
||
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function updateFromMap(existingChildren, returnFiber, newIdx, newChild, expirationTime) {
|
||
if (typeof newChild === 'string' || 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, expirationTime);
|
||
}
|
||
|
||
if (typeof newChild === 'object' && newChild !== null) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
{
|
||
var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
||
if (newChild.type === REACT_FRAGMENT_TYPE) {
|
||
return updateFragment(returnFiber, _matchedFiber, newChild.props.children, expirationTime, newChild.key);
|
||
}
|
||
return updateElement(returnFiber, _matchedFiber, newChild, expirationTime);
|
||
}
|
||
case REACT_PORTAL_TYPE:
|
||
{
|
||
var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
||
return updatePortal(returnFiber, _matchedFiber2, newChild, expirationTime);
|
||
}
|
||
}
|
||
|
||
if (isArray(newChild) || getIteratorFn(newChild)) {
|
||
var _matchedFiber3 = existingChildren.get(newIdx) || null;
|
||
return updateFragment(returnFiber, _matchedFiber3, newChild, expirationTime, null);
|
||
}
|
||
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* Warns if there is a duplicate or missing key
|
||
*/
|
||
function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) {
|
||
// 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.
|
||
|
||
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], expirationTime);
|
||
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);
|
||
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], expirationTime);
|
||
if (!_newFiber) {
|
||
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;
|
||
}
|
||
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], expirationTime);
|
||
if (_newFiber2) {
|
||
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);
|
||
});
|
||
}
|
||
|
||
return resultingFirstChild;
|
||
}
|
||
|
||
function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, expirationTime) {
|
||
// This is the same implementation as reconcileChildrenArray(),
|
||
// but using the iterator instead.
|
||
|
||
var iteratorFn = getIteratorFn(newChildrenIterable);
|
||
!(typeof iteratorFn === 'function') ? reactProdInvariant('150') : void 0;
|
||
|
||
var newChildren = iteratorFn.call(newChildrenIterable);
|
||
!(newChildren != null) ? reactProdInvariant('151') : void 0;
|
||
|
||
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, expirationTime);
|
||
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) {
|
||
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);
|
||
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, expirationTime);
|
||
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;
|
||
}
|
||
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, expirationTime);
|
||
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);
|
||
});
|
||
}
|
||
|
||
return resultingFirstChild;
|
||
}
|
||
|
||
function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, expirationTime) {
|
||
// 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, expirationTime);
|
||
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, expirationTime);
|
||
created.return = returnFiber;
|
||
return created;
|
||
}
|
||
|
||
function reconcileSingleElement(returnFiber, currentFirstChild, element, expirationTime) {
|
||
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) {
|
||
if (child.tag === Fragment ? element.type === REACT_FRAGMENT_TYPE : child.elementType === element.type) {
|
||
deleteRemainingChildren(returnFiber, child.sibling);
|
||
var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime);
|
||
existing.ref = coerceRef(returnFiber, child, element);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
} else {
|
||
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, expirationTime, element.key);
|
||
created.return = returnFiber;
|
||
return created;
|
||
} else {
|
||
var _created4 = createFiberFromElement(element, returnFiber.mode, expirationTime);
|
||
_created4.ref = coerceRef(returnFiber, currentFirstChild, element);
|
||
_created4.return = returnFiber;
|
||
return _created4;
|
||
}
|
||
}
|
||
|
||
function reconcileSinglePortal(returnFiber, currentFirstChild, portal, expirationTime) {
|
||
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 || [], expirationTime);
|
||
existing.return = returnFiber;
|
||
return existing;
|
||
} else {
|
||
deleteRemainingChildren(returnFiber, child);
|
||
break;
|
||
}
|
||
} else {
|
||
deleteChild(returnFiber, child);
|
||
}
|
||
child = child.sibling;
|
||
}
|
||
|
||
var created = createFiberFromPortal(portal, returnFiber.mode, expirationTime);
|
||
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, expirationTime) {
|
||
// 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
|
||
var isObject = typeof newChild === 'object' && newChild !== null;
|
||
|
||
if (isObject) {
|
||
switch (newChild.$$typeof) {
|
||
case REACT_ELEMENT_TYPE:
|
||
return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, expirationTime));
|
||
case REACT_PORTAL_TYPE:
|
||
return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, expirationTime));
|
||
}
|
||
}
|
||
|
||
if (typeof newChild === 'string' || typeof newChild === 'number') {
|
||
return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, expirationTime));
|
||
}
|
||
|
||
if (isArray(newChild)) {
|
||
return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime);
|
||
}
|
||
|
||
if (getIteratorFn(newChild)) {
|
||
return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime);
|
||
}
|
||
|
||
if (isObject) {
|
||
throwOnInvalidObjectType(returnFiber, newChild);
|
||
}
|
||
|
||
if (typeof newChild === 'undefined' && !isUnkeyedTopLevelFragment) {
|
||
// If the new child is undefined, and the return fiber is a composite
|
||
// component, throw an error. If Fiber return types are disabled,
|
||
// we already threw above.
|
||
switch (returnFiber.tag) {
|
||
case ClassComponent:
|
||
{
|
||
|
||
}
|
||
// Intentionally fall through to the next case, which handles both
|
||
// functions and classes
|
||
// eslint-disable-next-lined no-fallthrough
|
||
case FunctionComponent:
|
||
{
|
||
var Component = returnFiber.type;
|
||
reactProdInvariant('152', Component.displayName || Component.name || 'Component');
|
||
}
|
||
}
|
||
}
|
||
|
||
// Remaining cases are all treated as empty.
|
||
return deleteRemainingChildren(returnFiber, currentFirstChild);
|
||
}
|
||
|
||
return reconcileChildFibers;
|
||
}
|
||
|
||
var reconcileChildFibers = ChildReconciler(true);
|
||
var mountChildFibers = ChildReconciler(false);
|
||
|
||
function cloneChildFibers(current$$1, workInProgress) {
|
||
!(current$$1 === null || workInProgress.child === current$$1.child) ? reactProdInvariant('153') : void 0;
|
||
|
||
if (workInProgress.child === null) {
|
||
return;
|
||
}
|
||
|
||
var currentChild = workInProgress.child;
|
||
var newChild = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
|
||
workInProgress.child = newChild;
|
||
|
||
newChild.return = workInProgress;
|
||
while (currentChild.sibling !== null) {
|
||
currentChild = currentChild.sibling;
|
||
newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps, currentChild.expirationTime);
|
||
newChild.return = workInProgress;
|
||
}
|
||
newChild.sibling = null;
|
||
}
|
||
|
||
var NO_CONTEXT$1 = {};
|
||
|
||
var contextStackCursor$1 = createCursor(NO_CONTEXT$1);
|
||
var contextFiberStackCursor = createCursor(NO_CONTEXT$1);
|
||
var rootInstanceStackCursor = createCursor(NO_CONTEXT$1);
|
||
|
||
function requiredContext(c) {
|
||
!(c !== NO_CONTEXT$1) ? reactProdInvariant('174') : void 0;
|
||
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$1, 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 NoEffect$1 = /* */0;
|
||
var UnmountSnapshot = /* */2;
|
||
var UnmountMutation = /* */4;
|
||
var MountMutation = /* */8;
|
||
var UnmountLayout = /* */16;
|
||
var MountLayout = /* */32;
|
||
var MountPassive = /* */64;
|
||
var UnmountPassive = /* */128;
|
||
|
||
var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher;
|
||
|
||
|
||
// These are set right before calling the component.
|
||
var renderExpirationTime = NoWork;
|
||
// 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 nextCurrentHook = null;
|
||
var firstWorkInProgressHook = null;
|
||
var workInProgressHook = null;
|
||
var nextWorkInProgressHook = null;
|
||
|
||
var remainingExpirationTime = NoWork;
|
||
var componentUpdateQueue = null;
|
||
var sideEffectTag = 0;
|
||
|
||
// Updates scheduled during render will trigger an immediate re-render at the
|
||
// end of the current pass. We can't store these updates on the normal queue,
|
||
// because if the work is aborted, they should be discarded. Because this is
|
||
// a relatively rare case, we also don't want to add an additional field to
|
||
// either the hook or queue object types. So we store them in a lazily create
|
||
// map of queue -> render-phase updates, which are discarded once the component
|
||
// completes without re-rendering.
|
||
|
||
// Whether an update was scheduled during the currently executing render pass.
|
||
var didScheduleRenderPhaseUpdate = false;
|
||
// Lazily created map of render-phase updates
|
||
var renderPhaseUpdates = null;
|
||
// Counter to prevent infinite loops.
|
||
var numberOfReRenders = 0;
|
||
var RE_RENDER_LIMIT = 25;
|
||
|
||
function throwInvalidHookError() {
|
||
reactProdInvariant('321');
|
||
}
|
||
|
||
function areHookInputsEqual(nextDeps, prevDeps) {
|
||
if (prevDeps === null) {
|
||
return false;
|
||
}
|
||
|
||
for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
|
||
if (is(nextDeps[i], prevDeps[i])) {
|
||
continue;
|
||
}
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
function renderWithHooks(current, workInProgress, Component, props, refOrContext, nextRenderExpirationTime) {
|
||
renderExpirationTime = nextRenderExpirationTime;
|
||
currentlyRenderingFiber$1 = workInProgress;
|
||
nextCurrentHook = current !== null ? current.memoizedState : null;
|
||
|
||
{
|
||
ReactCurrentDispatcher$1.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate;
|
||
}
|
||
|
||
var children = Component(props, refOrContext);
|
||
|
||
if (didScheduleRenderPhaseUpdate) {
|
||
do {
|
||
didScheduleRenderPhaseUpdate = false;
|
||
numberOfReRenders += 1;
|
||
|
||
// Start over from the beginning of the list
|
||
nextCurrentHook = current !== null ? current.memoizedState : null;
|
||
nextWorkInProgressHook = firstWorkInProgressHook;
|
||
|
||
currentHook = null;
|
||
workInProgressHook = null;
|
||
componentUpdateQueue = null;
|
||
|
||
ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdate;
|
||
|
||
children = Component(props, refOrContext);
|
||
} while (didScheduleRenderPhaseUpdate);
|
||
|
||
renderPhaseUpdates = null;
|
||
numberOfReRenders = 0;
|
||
}
|
||
|
||
// 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-entrancy.
|
||
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
||
|
||
var renderedWork = currentlyRenderingFiber$1;
|
||
|
||
renderedWork.memoizedState = firstWorkInProgressHook;
|
||
renderedWork.expirationTime = remainingExpirationTime;
|
||
renderedWork.updateQueue = componentUpdateQueue;
|
||
renderedWork.effectTag |= sideEffectTag;
|
||
|
||
var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
|
||
|
||
renderExpirationTime = NoWork;
|
||
currentlyRenderingFiber$1 = null;
|
||
|
||
currentHook = null;
|
||
nextCurrentHook = null;
|
||
firstWorkInProgressHook = null;
|
||
workInProgressHook = null;
|
||
nextWorkInProgressHook = null;
|
||
|
||
remainingExpirationTime = NoWork;
|
||
componentUpdateQueue = null;
|
||
sideEffectTag = 0;
|
||
|
||
// These were reset above
|
||
// didScheduleRenderPhaseUpdate = false;
|
||
// renderPhaseUpdates = null;
|
||
// numberOfReRenders = 0;
|
||
|
||
!!didRenderTooFewHooks ? reactProdInvariant('300') : void 0;
|
||
|
||
return children;
|
||
}
|
||
|
||
function bailoutHooks(current, workInProgress, expirationTime) {
|
||
workInProgress.updateQueue = current.updateQueue;
|
||
workInProgress.effectTag &= ~(Passive | Update);
|
||
if (current.expirationTime <= expirationTime) {
|
||
current.expirationTime = NoWork;
|
||
}
|
||
}
|
||
|
||
function resetHooks() {
|
||
// 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-entrancy.
|
||
ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
||
|
||
// This is used to reset the state of this module when a component throws.
|
||
// It's also called inside mountIndeterminateComponent if we determine the
|
||
// component is a module-style component.
|
||
renderExpirationTime = NoWork;
|
||
currentlyRenderingFiber$1 = null;
|
||
|
||
currentHook = null;
|
||
nextCurrentHook = null;
|
||
firstWorkInProgressHook = null;
|
||
workInProgressHook = null;
|
||
nextWorkInProgressHook = null;
|
||
|
||
remainingExpirationTime = NoWork;
|
||
componentUpdateQueue = null;
|
||
sideEffectTag = 0;
|
||
|
||
didScheduleRenderPhaseUpdate = false;
|
||
renderPhaseUpdates = null;
|
||
numberOfReRenders = 0;
|
||
}
|
||
|
||
function mountWorkInProgressHook() {
|
||
var hook = {
|
||
memoizedState: null,
|
||
|
||
baseState: null,
|
||
queue: null,
|
||
baseUpdate: null,
|
||
|
||
next: null
|
||
};
|
||
|
||
if (workInProgressHook === null) {
|
||
// This is the first hook in the list
|
||
firstWorkInProgressHook = 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.
|
||
if (nextWorkInProgressHook !== null) {
|
||
// There's already a work-in-progress. Reuse it.
|
||
workInProgressHook = nextWorkInProgressHook;
|
||
nextWorkInProgressHook = workInProgressHook.next;
|
||
|
||
currentHook = nextCurrentHook;
|
||
nextCurrentHook = currentHook !== null ? currentHook.next : null;
|
||
} else {
|
||
// Clone from the current hook.
|
||
!(nextCurrentHook !== null) ? reactProdInvariant('310') : void 0;
|
||
currentHook = nextCurrentHook;
|
||
|
||
var newHook = {
|
||
memoizedState: currentHook.memoizedState,
|
||
|
||
baseState: currentHook.baseState,
|
||
queue: currentHook.queue,
|
||
baseUpdate: currentHook.baseUpdate,
|
||
|
||
next: null
|
||
};
|
||
|
||
if (workInProgressHook === null) {
|
||
// This is the first hook in the list.
|
||
workInProgressHook = firstWorkInProgressHook = newHook;
|
||
} else {
|
||
// Append to the end of the list.
|
||
workInProgressHook = workInProgressHook.next = newHook;
|
||
}
|
||
nextCurrentHook = currentHook.next;
|
||
}
|
||
return workInProgressHook;
|
||
}
|
||
|
||
function createFunctionComponentUpdateQueue() {
|
||
return {
|
||
lastEffect: null
|
||
};
|
||
}
|
||
|
||
function basicStateReducer(state, action) {
|
||
return typeof action === 'function' ? action(state) : action;
|
||
}
|
||
|
||
function mountReducer(reducer, initialArg, init) {
|
||
var hook = mountWorkInProgressHook();
|
||
var initialState = void 0;
|
||
if (init !== undefined) {
|
||
initialState = init(initialArg);
|
||
} else {
|
||
initialState = initialArg;
|
||
}
|
||
hook.memoizedState = hook.baseState = initialState;
|
||
var queue = hook.queue = {
|
||
last: null,
|
||
dispatch: null,
|
||
lastRenderedReducer: reducer,
|
||
lastRenderedState: initialState
|
||
};
|
||
var dispatch = queue.dispatch = dispatchAction.bind(null,
|
||
// Flow doesn't know this is non-null, but we do.
|
||
currentlyRenderingFiber$1, queue);
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
|
||
function updateReducer(reducer, initialArg, init) {
|
||
var hook = updateWorkInProgressHook();
|
||
var queue = hook.queue;
|
||
!(queue !== null) ? reactProdInvariant('311') : void 0;
|
||
|
||
queue.lastRenderedReducer = reducer;
|
||
|
||
if (numberOfReRenders > 0) {
|
||
// This is a re-render. Apply the new render phase updates to the previous
|
||
var _dispatch = queue.dispatch;
|
||
if (renderPhaseUpdates !== null) {
|
||
// Render phase updates are stored in a map of queue -> linked list
|
||
var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
|
||
if (firstRenderPhaseUpdate !== undefined) {
|
||
renderPhaseUpdates.delete(queue);
|
||
var newState = hook.memoizedState;
|
||
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 !== null);
|
||
|
||
// Mark that the fiber performed work, but only if the new state is
|
||
// different from the current state.
|
||
if (!is(newState, hook.memoizedState)) {
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
|
||
hook.memoizedState = newState;
|
||
// Don't persist the state accumlated 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.baseUpdate === queue.last) {
|
||
hook.baseState = newState;
|
||
}
|
||
|
||
queue.lastRenderedState = newState;
|
||
|
||
return [newState, _dispatch];
|
||
}
|
||
}
|
||
return [hook.memoizedState, _dispatch];
|
||
}
|
||
|
||
// The last update in the entire queue
|
||
var last = queue.last;
|
||
// The last update that is part of the base state.
|
||
var baseUpdate = hook.baseUpdate;
|
||
var baseState = hook.baseState;
|
||
|
||
// Find the first unprocessed update.
|
||
var first = void 0;
|
||
if (baseUpdate !== null) {
|
||
if (last !== null) {
|
||
// For the first update, the queue is a circular linked list where
|
||
// `queue.last.next = queue.first`. Once the first update commits, and
|
||
// the `baseUpdate` is no longer empty, we can unravel the list.
|
||
last.next = null;
|
||
}
|
||
first = baseUpdate.next;
|
||
} else {
|
||
first = last !== null ? last.next : null;
|
||
}
|
||
if (first !== null) {
|
||
var _newState = baseState;
|
||
var newBaseState = null;
|
||
var newBaseUpdate = null;
|
||
var prevUpdate = baseUpdate;
|
||
var _update = first;
|
||
var didSkip = false;
|
||
do {
|
||
var updateExpirationTime = _update.expirationTime;
|
||
if (updateExpirationTime < renderExpirationTime) {
|
||
// Priority is insufficient. Skip this update. If this is the first
|
||
// skipped update, the previous update/state is the new base
|
||
// update/state.
|
||
if (!didSkip) {
|
||
didSkip = true;
|
||
newBaseUpdate = prevUpdate;
|
||
newBaseState = _newState;
|
||
}
|
||
// Update the remaining priority in the queue.
|
||
if (updateExpirationTime > remainingExpirationTime) {
|
||
remainingExpirationTime = updateExpirationTime;
|
||
}
|
||
} else {
|
||
// Process this update.
|
||
if (_update.eagerReducer === reducer) {
|
||
// If this update was processed eagerly, and its reducer matches the
|
||
// current reducer, we can use the eagerly computed state.
|
||
_newState = _update.eagerState;
|
||
} else {
|
||
var _action2 = _update.action;
|
||
_newState = reducer(_newState, _action2);
|
||
}
|
||
}
|
||
prevUpdate = _update;
|
||
_update = _update.next;
|
||
} while (_update !== null && _update !== first);
|
||
|
||
if (!didSkip) {
|
||
newBaseUpdate = prevUpdate;
|
||
newBaseState = _newState;
|
||
}
|
||
|
||
// Mark that the fiber performed work, but only if the new state is
|
||
// different from the current state.
|
||
if (!is(_newState, hook.memoizedState)) {
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
|
||
hook.memoizedState = _newState;
|
||
hook.baseUpdate = newBaseUpdate;
|
||
hook.baseState = newBaseState;
|
||
|
||
queue.lastRenderedState = _newState;
|
||
}
|
||
|
||
var dispatch = queue.dispatch;
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
|
||
function mountState(initialState) {
|
||
var hook = mountWorkInProgressHook();
|
||
if (typeof initialState === 'function') {
|
||
initialState = initialState();
|
||
}
|
||
hook.memoizedState = hook.baseState = initialState;
|
||
var queue = hook.queue = {
|
||
last: null,
|
||
dispatch: null,
|
||
lastRenderedReducer: basicStateReducer,
|
||
lastRenderedState: initialState
|
||
};
|
||
var dispatch = queue.dispatch = dispatchAction.bind(null,
|
||
// Flow doesn't know this is non-null, but we do.
|
||
currentlyRenderingFiber$1, queue);
|
||
return [hook.memoizedState, dispatch];
|
||
}
|
||
|
||
function updateState(initialState) {
|
||
return updateReducer(basicStateReducer, initialState);
|
||
}
|
||
|
||
function pushEffect(tag, create, destroy, deps) {
|
||
var effect = {
|
||
tag: tag,
|
||
create: create,
|
||
destroy: destroy,
|
||
deps: deps,
|
||
// Circular
|
||
next: null
|
||
};
|
||
if (componentUpdateQueue === null) {
|
||
componentUpdateQueue = createFunctionComponentUpdateQueue();
|
||
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 ref = { current: initialValue };
|
||
hook.memoizedState = ref;
|
||
return ref;
|
||
}
|
||
|
||
function updateRef(initialValue) {
|
||
var hook = updateWorkInProgressHook();
|
||
return hook.memoizedState;
|
||
}
|
||
|
||
function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {
|
||
var hook = mountWorkInProgressHook();
|
||
var nextDeps = deps === undefined ? null : deps;
|
||
sideEffectTag |= fiberEffectTag;
|
||
hook.memoizedState = pushEffect(hookEffectTag, create, undefined, nextDeps);
|
||
}
|
||
|
||
function updateEffectImpl(fiberEffectTag, hookEffectTag, 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)) {
|
||
pushEffect(NoEffect$1, create, destroy, nextDeps);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
sideEffectTag |= fiberEffectTag;
|
||
hook.memoizedState = pushEffect(hookEffectTag, create, destroy, nextDeps);
|
||
}
|
||
|
||
function mountEffect(create, deps) {
|
||
return mountEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
|
||
}
|
||
|
||
function updateEffect(create, deps) {
|
||
return updateEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);
|
||
}
|
||
|
||
function mountLayoutEffect(create, deps) {
|
||
return mountEffectImpl(Update, UnmountMutation | MountLayout, create, deps);
|
||
}
|
||
|
||
function updateLayoutEffect(create, deps) {
|
||
return updateEffectImpl(Update, UnmountMutation | MountLayout, 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;
|
||
var _inst2 = create();
|
||
refObject.current = _inst2;
|
||
return function () {
|
||
refObject.current = null;
|
||
};
|
||
}
|
||
}
|
||
|
||
function mountImperativeHandle(ref, create, deps) {
|
||
var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
||
|
||
return mountEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
||
}
|
||
|
||
function updateImperativeHandle(ref, create, deps) {
|
||
var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
||
|
||
return updateEffectImpl(Update, UnmountMutation | MountLayout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
||
}
|
||
|
||
function mountDebugValue(value, formatterFn) {
|
||
// This hook is normally a no-op.
|
||
// 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 dispatchAction(fiber, queue, action) {
|
||
!(numberOfReRenders < RE_RENDER_LIMIT) ? reactProdInvariant('301') : void 0;
|
||
|
||
var alternate = fiber.alternate;
|
||
if (fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1) {
|
||
// 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.
|
||
didScheduleRenderPhaseUpdate = true;
|
||
var update = {
|
||
expirationTime: renderExpirationTime,
|
||
action: action,
|
||
eagerReducer: null,
|
||
eagerState: null,
|
||
next: null
|
||
};
|
||
if (renderPhaseUpdates === null) {
|
||
renderPhaseUpdates = new Map();
|
||
}
|
||
var firstRenderPhaseUpdate = renderPhaseUpdates.get(queue);
|
||
if (firstRenderPhaseUpdate === undefined) {
|
||
renderPhaseUpdates.set(queue, update);
|
||
} else {
|
||
// Append the update to the end of the list.
|
||
var lastRenderPhaseUpdate = firstRenderPhaseUpdate;
|
||
while (lastRenderPhaseUpdate.next !== null) {
|
||
lastRenderPhaseUpdate = lastRenderPhaseUpdate.next;
|
||
}
|
||
lastRenderPhaseUpdate.next = update;
|
||
}
|
||
} else {
|
||
flushPassiveEffects$1();
|
||
|
||
var currentTime = requestCurrentTime();
|
||
var _expirationTime = computeExpirationForFiber(currentTime, fiber);
|
||
|
||
var _update2 = {
|
||
expirationTime: _expirationTime,
|
||
action: action,
|
||
eagerReducer: null,
|
||
eagerState: null,
|
||
next: null
|
||
};
|
||
|
||
// Append the update to the end of the list.
|
||
var _last = queue.last;
|
||
if (_last === null) {
|
||
// This is the first update. Create a circular list.
|
||
_update2.next = _update2;
|
||
} else {
|
||
var first = _last.next;
|
||
if (first !== null) {
|
||
// Still circular.
|
||
_update2.next = first;
|
||
}
|
||
_last.next = _update2;
|
||
}
|
||
queue.last = _update2;
|
||
|
||
if (fiber.expirationTime === NoWork && (alternate === null || alternate.expirationTime === NoWork)) {
|
||
// 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) {
|
||
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.
|
||
_update2.eagerReducer = _lastRenderedReducer;
|
||
_update2.eagerState = _eagerState;
|
||
if (is(_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.
|
||
return;
|
||
}
|
||
} catch (error) {
|
||
// Suppress the error. It will throw again in the render phase.
|
||
} finally {
|
||
|
||
}
|
||
}
|
||
}
|
||
scheduleWork(fiber, _expirationTime);
|
||
}
|
||
}
|
||
|
||
var ContextOnlyDispatcher = {
|
||
readContext: readContext,
|
||
|
||
useCallback: throwInvalidHookError,
|
||
useContext: throwInvalidHookError,
|
||
useEffect: throwInvalidHookError,
|
||
useImperativeHandle: throwInvalidHookError,
|
||
useLayoutEffect: throwInvalidHookError,
|
||
useMemo: throwInvalidHookError,
|
||
useReducer: throwInvalidHookError,
|
||
useRef: throwInvalidHookError,
|
||
useState: throwInvalidHookError,
|
||
useDebugValue: throwInvalidHookError
|
||
};
|
||
|
||
var HooksDispatcherOnMount = {
|
||
readContext: readContext,
|
||
|
||
useCallback: mountCallback,
|
||
useContext: readContext,
|
||
useEffect: mountEffect,
|
||
useImperativeHandle: mountImperativeHandle,
|
||
useLayoutEffect: mountLayoutEffect,
|
||
useMemo: mountMemo,
|
||
useReducer: mountReducer,
|
||
useRef: mountRef,
|
||
useState: mountState,
|
||
useDebugValue: mountDebugValue
|
||
};
|
||
|
||
var HooksDispatcherOnUpdate = {
|
||
readContext: readContext,
|
||
|
||
useCallback: updateCallback,
|
||
useContext: readContext,
|
||
useEffect: updateEffect,
|
||
useImperativeHandle: updateImperativeHandle,
|
||
useLayoutEffect: updateLayoutEffect,
|
||
useMemo: updateMemo,
|
||
useReducer: updateReducer,
|
||
useRef: updateRef,
|
||
useState: updateState,
|
||
useDebugValue: updateDebugValue
|
||
};
|
||
|
||
var commitTime = 0;
|
||
var profilerStartTime = -1;
|
||
|
||
function getCommitTime() {
|
||
return commitTime;
|
||
}
|
||
|
||
function recordCommitTime() {
|
||
if (!enableProfilerTimer) {
|
||
return;
|
||
}
|
||
commitTime = now();
|
||
}
|
||
|
||
function startProfilerTimer(fiber) {
|
||
if (!enableProfilerTimer) {
|
||
return;
|
||
}
|
||
|
||
profilerStartTime = now();
|
||
|
||
if (fiber.actualStartTime < 0) {
|
||
fiber.actualStartTime = now();
|
||
}
|
||
}
|
||
|
||
function stopProfilerTimerIfRunning(fiber) {
|
||
if (!enableProfilerTimer) {
|
||
return;
|
||
}
|
||
profilerStartTime = -1;
|
||
}
|
||
|
||
function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
|
||
if (!enableProfilerTimer) {
|
||
return;
|
||
}
|
||
|
||
if (profilerStartTime >= 0) {
|
||
var elapsedTime = now() - profilerStartTime;
|
||
fiber.actualDuration += elapsedTime;
|
||
if (overrideBaseTime) {
|
||
fiber.selfBaseDuration = elapsedTime;
|
||
}
|
||
profilerStartTime = -1;
|
||
}
|
||
}
|
||
|
||
// The deepest Fiber on the stack involved in a hydration context.
|
||
// This may have been an insertion or a hydration.
|
||
var hydrationParentFiber = null;
|
||
var nextHydratableInstance = null;
|
||
var isHydrating = false;
|
||
|
||
function enterHydrationState(fiber) {
|
||
if (!supportsHydration) {
|
||
return false;
|
||
}
|
||
|
||
var parentInstance = fiber.stateNode.containerInfo;
|
||
nextHydratableInstance = getFirstHydratableChild(parentInstance);
|
||
hydrationParentFiber = fiber;
|
||
isHydrating = true;
|
||
return true;
|
||
}
|
||
|
||
function reenterHydrationStateFromDehydratedSuspenseInstance(fiber) {
|
||
if (!supportsHydration) {
|
||
return false;
|
||
}
|
||
|
||
var suspenseInstance = fiber.stateNode;
|
||
nextHydratableInstance = getNextHydratableSibling(suspenseInstance);
|
||
popToNextHostParent(fiber);
|
||
isHydrating = true;
|
||
return true;
|
||
}
|
||
|
||
function deleteHydratableInstance(returnFiber, instance) {
|
||
var childToDelete = createFiberFromHostInstanceForDeletion();
|
||
childToDelete.stateNode = instance;
|
||
childToDelete.return = returnFiber;
|
||
childToDelete.effectTag = Deletion;
|
||
|
||
// This might seem like it belongs on progressedFirstDeletion. However,
|
||
// these children are not part of the reconciliation list of children.
|
||
// Even if we abort and rereconcile the children, that will try to hydrate
|
||
// again and the nodes are still in the host tree so these will be
|
||
// recreated.
|
||
if (returnFiber.lastEffect !== null) {
|
||
returnFiber.lastEffect.nextEffect = childToDelete;
|
||
returnFiber.lastEffect = childToDelete;
|
||
} else {
|
||
returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
|
||
}
|
||
}
|
||
|
||
function insertNonHydratedInstance(returnFiber, fiber) {
|
||
fiber.effectTag |= Placement;
|
||
|
||
}
|
||
|
||
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;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
case HostText:
|
||
{
|
||
var text = fiber.pendingProps;
|
||
var textInstance = canHydrateTextInstance(nextInstance, text);
|
||
if (textInstance !== null) {
|
||
fiber.stateNode = textInstance;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
if (enableSuspenseServerRenderer) {
|
||
var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
|
||
if (suspenseInstance !== null) {
|
||
// Downgrade the tag to a dehydrated component until we've hydrated it.
|
||
fiber.tag = DehydratedSuspenseComponent;
|
||
fiber.stateNode = suspenseInstance;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
function tryToClaimNextHydratableInstance(fiber) {
|
||
if (!isHydrating) {
|
||
return;
|
||
}
|
||
var nextInstance = nextHydratableInstance;
|
||
if (!nextInstance) {
|
||
// Nothing to hydrate. Make it an insertion.
|
||
insertNonHydratedInstance(hydrationParentFiber, fiber);
|
||
isHydrating = false;
|
||
hydrationParentFiber = fiber;
|
||
return;
|
||
}
|
||
var firstAttemptedInstance = nextInstance;
|
||
if (!tryHydrate(fiber, nextInstance)) {
|
||
// 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);
|
||
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(hydrationParentFiber, firstAttemptedInstance);
|
||
}
|
||
hydrationParentFiber = fiber;
|
||
nextHydratableInstance = getFirstHydratableChild(nextInstance);
|
||
}
|
||
|
||
function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
|
||
if (!supportsHydration) {
|
||
reactProdInvariant('175');
|
||
}
|
||
|
||
var instance = fiber.stateNode;
|
||
var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber);
|
||
// 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) {
|
||
reactProdInvariant('176');
|
||
}
|
||
|
||
var textInstance = fiber.stateNode;
|
||
var textContent = fiber.memoizedProps;
|
||
var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
|
||
return shouldUpdate;
|
||
}
|
||
|
||
function skipPastDehydratedSuspenseInstance(fiber) {
|
||
if (!supportsHydration) {
|
||
reactProdInvariant('316');
|
||
}
|
||
var suspenseInstance = fiber.stateNode;
|
||
!suspenseInstance ? reactProdInvariant('317') : void 0;
|
||
nextHydratableInstance = getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
|
||
}
|
||
|
||
function popToNextHostParent(fiber) {
|
||
var parent = fiber.return;
|
||
while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== DehydratedSuspenseComponent) {
|
||
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;
|
||
}
|
||
|
||
var type = fiber.type;
|
||
|
||
// 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.
|
||
// TODO: Better heuristic.
|
||
if (fiber.tag !== HostComponent || type !== 'head' && type !== 'body' && !shouldSetTextContent(type, fiber.memoizedProps)) {
|
||
var nextInstance = nextHydratableInstance;
|
||
while (nextInstance) {
|
||
deleteHydratableInstance(fiber, nextInstance);
|
||
nextInstance = getNextHydratableSibling(nextInstance);
|
||
}
|
||
}
|
||
|
||
popToNextHostParent(fiber);
|
||
nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
|
||
return true;
|
||
}
|
||
|
||
function resetHydrationState() {
|
||
if (!supportsHydration) {
|
||
return;
|
||
}
|
||
|
||
hydrationParentFiber = null;
|
||
nextHydratableInstance = null;
|
||
isHydrating = false;
|
||
}
|
||
|
||
var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner;
|
||
|
||
var didReceiveUpdate = false;
|
||
|
||
|
||
|
||
function reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime) {
|
||
if (current$$1 === 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, renderExpirationTime);
|
||
} 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$$1.child, nextChildren, renderExpirationTime);
|
||
}
|
||
}
|
||
|
||
function forceUnmountCurrentAndReconcile(current$$1, workInProgress, nextChildren, renderExpirationTime) {
|
||
// 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$$1.child, null, renderExpirationTime);
|
||
// 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 their
|
||
// identity matches.
|
||
workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
|
||
}
|
||
|
||
function updateForwardRef(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
|
||
// 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.
|
||
|
||
var render = Component.render;
|
||
var ref = workInProgress.ref;
|
||
|
||
// The rest is a fork of updateFunctionComponent
|
||
var nextChildren = void 0;
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
{
|
||
nextChildren = renderWithHooks(current$$1, workInProgress, render, nextProps, ref, renderExpirationTime);
|
||
}
|
||
|
||
if (current$$1 !== null && !didReceiveUpdate) {
|
||
bailoutHooks(current$$1, workInProgress, renderExpirationTime);
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
|
||
if (current$$1 === null) {
|
||
var type = Component.type;
|
||
if (isSimpleFunctionComponent(type) && Component.compare === null &&
|
||
// SimpleMemoComponent codepath doesn't resolve outer props either.
|
||
Component.defaultProps === undefined) {
|
||
// 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 = type;
|
||
return updateSimpleMemoComponent(current$$1, workInProgress, type, nextProps, updateExpirationTime, renderExpirationTime);
|
||
}
|
||
var child = createFiberFromTypeAndProps(Component.type, null, nextProps, null, workInProgress.mode, renderExpirationTime);
|
||
child.ref = workInProgress.ref;
|
||
child.return = workInProgress;
|
||
workInProgress.child = child;
|
||
return child;
|
||
}
|
||
var currentChild = current$$1.child; // This is always exactly one child
|
||
if (updateExpirationTime < renderExpirationTime) {
|
||
// 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$$1.ref === workInProgress.ref) {
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
}
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
var newChild = createWorkInProgress(currentChild, nextProps, renderExpirationTime);
|
||
newChild.ref = workInProgress.ref;
|
||
newChild.return = workInProgress;
|
||
workInProgress.child = newChild;
|
||
return newChild;
|
||
}
|
||
|
||
function updateSimpleMemoComponent(current$$1, workInProgress, Component, nextProps, updateExpirationTime, renderExpirationTime) {
|
||
// 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 (current$$1 !== null) {
|
||
var prevProps = current$$1.memoizedProps;
|
||
if (shallowEqual(prevProps, nextProps) && current$$1.ref === workInProgress.ref) {
|
||
didReceiveUpdate = false;
|
||
if (updateExpirationTime < renderExpirationTime) {
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
}
|
||
}
|
||
return updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
|
||
}
|
||
|
||
function updateFragment(current$$1, workInProgress, renderExpirationTime) {
|
||
var nextChildren = workInProgress.pendingProps;
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateMode(current$$1, workInProgress, renderExpirationTime) {
|
||
var nextChildren = workInProgress.pendingProps.children;
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateProfiler(current$$1, workInProgress, renderExpirationTime) {
|
||
if (enableProfilerTimer) {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
var nextProps = workInProgress.pendingProps;
|
||
var nextChildren = nextProps.children;
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function markRef(current$$1, workInProgress) {
|
||
var ref = workInProgress.ref;
|
||
if (current$$1 === null && ref !== null || current$$1 !== null && current$$1.ref !== ref) {
|
||
// Schedule a Ref effect
|
||
workInProgress.effectTag |= Ref;
|
||
}
|
||
}
|
||
|
||
function updateFunctionComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
|
||
var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
|
||
var context = getMaskedContext(workInProgress, unmaskedContext);
|
||
|
||
var nextChildren = void 0;
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
{
|
||
nextChildren = renderWithHooks(current$$1, workInProgress, Component, nextProps, context, renderExpirationTime);
|
||
}
|
||
|
||
if (current$$1 !== null && !didReceiveUpdate) {
|
||
bailoutHooks(current$$1, workInProgress, renderExpirationTime);
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateClassComponent(current$$1, workInProgress, Component, nextProps, renderExpirationTime) {
|
||
var hasContext = void 0;
|
||
if (isContextProvider(Component)) {
|
||
hasContext = true;
|
||
pushContextProvider(workInProgress);
|
||
} else {
|
||
hasContext = false;
|
||
}
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
|
||
var instance = workInProgress.stateNode;
|
||
var shouldUpdate = void 0;
|
||
if (instance === null) {
|
||
if (current$$1 !== null) {
|
||
// An class component without an instance only mounts if it suspended
|
||
// inside a non- concurrent tree, in an inconsistent state. We want to
|
||
// tree it like a new mount, even though an empty version of it already
|
||
// committed. Disconnect the alternate pointers.
|
||
current$$1.alternate = null;
|
||
workInProgress.alternate = null;
|
||
// Since this is conceptually a new fiber, schedule a Placement effect
|
||
workInProgress.effectTag |= Placement;
|
||
}
|
||
// In the initial pass we might need to construct the instance.
|
||
constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
|
||
mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
|
||
shouldUpdate = true;
|
||
} else if (current$$1 === null) {
|
||
// In a resume, we'll already have an instance we can reuse.
|
||
shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
|
||
} else {
|
||
shouldUpdate = updateClassInstance(current$$1, workInProgress, Component, nextProps, renderExpirationTime);
|
||
}
|
||
var nextUnitOfWork = finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime);
|
||
return nextUnitOfWork;
|
||
}
|
||
|
||
function finishClassComponent(current$$1, workInProgress, Component, shouldUpdate, hasContext, renderExpirationTime) {
|
||
// Refs should update even if shouldComponentUpdate returns false
|
||
markRef(current$$1, workInProgress);
|
||
|
||
var didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect;
|
||
|
||
if (!shouldUpdate && !didCaptureError) {
|
||
// Context providers should defer to sCU for rendering
|
||
if (hasContext) {
|
||
invalidateContextProvider(workInProgress, Component, false);
|
||
}
|
||
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
|
||
var instance = workInProgress.stateNode;
|
||
|
||
// Rerender
|
||
ReactCurrentOwner$2.current = workInProgress;
|
||
var nextChildren = void 0;
|
||
if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
|
||
// If we captured an error, but getDerivedStateFrom catch 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;
|
||
|
||
if (enableProfilerTimer) {
|
||
stopProfilerTimerIfRunning(workInProgress);
|
||
}
|
||
} else {
|
||
{
|
||
nextChildren = instance.render();
|
||
}
|
||
}
|
||
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
if (current$$1 !== 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$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
} else {
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
}
|
||
|
||
// 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$$1, workInProgress, renderExpirationTime) {
|
||
pushHostRootContext(workInProgress);
|
||
var updateQueue = workInProgress.updateQueue;
|
||
!(updateQueue !== null) ? reactProdInvariant('282') : void 0;
|
||
var nextProps = workInProgress.pendingProps;
|
||
var prevState = workInProgress.memoizedState;
|
||
var prevChildren = prevState !== null ? prevState.element : null;
|
||
processUpdateQueue(workInProgress, updateQueue, nextProps, null, renderExpirationTime);
|
||
var nextState = workInProgress.memoizedState;
|
||
// Caution: React DevTools currently depends on this property
|
||
// being called "element".
|
||
var nextChildren = nextState.element;
|
||
if (nextChildren === prevChildren) {
|
||
// If the state is the same as before, that's a bailout because we had
|
||
// no work that expires at this time.
|
||
resetHydrationState();
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
var root = workInProgress.stateNode;
|
||
if ((current$$1 === null || current$$1.child === null) && root.hydrate && enterHydrationState(workInProgress)) {
|
||
// If we don't have any current children this might be the first pass.
|
||
// We always try to hydrate. If this isn't a hydration pass there won't
|
||
// be any children to hydrate which is effectively the same thing as
|
||
// not hydrating.
|
||
|
||
// This is a bit of a hack. We track the host root as a placement to
|
||
// know that we're currently in a mounting state. That way isMounted
|
||
// works as expected. We must reset this before committing.
|
||
// TODO: Delete this when we delete isMounted and findDOMNode.
|
||
workInProgress.effectTag |= Placement;
|
||
|
||
// Ensure that children mount into this root without tracking
|
||
// side-effects. This ensures that we don't store Placement effects on
|
||
// nodes that will be hydrated.
|
||
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
|
||
} else {
|
||
// Otherwise reset hydration state in case we aborted and resumed another
|
||
// root.
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
resetHydrationState();
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateHostComponent(current$$1, workInProgress, renderExpirationTime) {
|
||
pushHostContext(workInProgress);
|
||
|
||
if (current$$1 === null) {
|
||
tryToClaimNextHydratableInstance(workInProgress);
|
||
}
|
||
|
||
var type = workInProgress.type;
|
||
var nextProps = workInProgress.pendingProps;
|
||
var prevProps = current$$1 !== null ? current$$1.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 have 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.effectTag |= ContentReset;
|
||
}
|
||
|
||
markRef(current$$1, workInProgress);
|
||
|
||
// Check the host config to see if the children are offscreen/hidden.
|
||
if (renderExpirationTime !== Never && workInProgress.mode & ConcurrentMode && shouldDeprioritizeSubtree(type, nextProps)) {
|
||
// Schedule this fiber to re-render at offscreen priority. Then bailout.
|
||
workInProgress.expirationTime = workInProgress.childExpirationTime = Never;
|
||
return null;
|
||
}
|
||
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateHostText(current$$1, workInProgress) {
|
||
if (current$$1 === 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, updateExpirationTime, renderExpirationTime) {
|
||
if (_current !== null) {
|
||
// An 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.effectTag |= Placement;
|
||
}
|
||
|
||
var props = workInProgress.pendingProps;
|
||
// We can't start a User Timing measurement with correct label yet.
|
||
// Cancel and resume right after we know the tag.
|
||
cancelWorkTimer(workInProgress);
|
||
var Component = readLazyComponentType(elementType);
|
||
// Store the unwrapped component in the type.
|
||
workInProgress.type = Component;
|
||
var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
|
||
startWorkTimer(workInProgress);
|
||
var resolvedProps = resolveDefaultProps(Component, props);
|
||
var child = void 0;
|
||
switch (resolvedTag) {
|
||
case FunctionComponent:
|
||
{
|
||
child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderExpirationTime);
|
||
break;
|
||
}
|
||
case ForwardRef:
|
||
{
|
||
child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderExpirationTime);
|
||
break;
|
||
}
|
||
case MemoComponent:
|
||
{
|
||
child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
|
||
updateExpirationTime, renderExpirationTime);
|
||
break;
|
||
}
|
||
default:
|
||
{
|
||
var hint = '';
|
||
reactProdInvariant('306', Component, hint);
|
||
}
|
||
}
|
||
return child;
|
||
}
|
||
|
||
function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderExpirationTime) {
|
||
if (_current !== null) {
|
||
// An incomplete 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.effectTag |= Placement;
|
||
}
|
||
|
||
// 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 = void 0;
|
||
if (isContextProvider(Component)) {
|
||
hasContext = true;
|
||
pushContextProvider(workInProgress);
|
||
} else {
|
||
hasContext = false;
|
||
}
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
|
||
constructClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
|
||
mountClassInstance(workInProgress, Component, nextProps, renderExpirationTime);
|
||
|
||
return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
|
||
}
|
||
|
||
function mountIndeterminateComponent(_current, workInProgress, Component, renderExpirationTime) {
|
||
if (_current !== null) {
|
||
// An indeterminate 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.effectTag |= Placement;
|
||
}
|
||
|
||
var props = workInProgress.pendingProps;
|
||
var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
|
||
var context = getMaskedContext(workInProgress, unmaskedContext);
|
||
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
|
||
var value = void 0;
|
||
|
||
{
|
||
value = renderWithHooks(null, workInProgress, Component, props, context, renderExpirationTime);
|
||
}
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
|
||
if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
|
||
// Proceed under the assumption that this is a class instance
|
||
workInProgress.tag = ClassComponent;
|
||
|
||
// Throw out any hooks that were used.
|
||
resetHooks();
|
||
|
||
// 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;
|
||
|
||
var getDerivedStateFromProps = Component.getDerivedStateFromProps;
|
||
if (typeof getDerivedStateFromProps === 'function') {
|
||
applyDerivedStateFromProps(workInProgress, Component, getDerivedStateFromProps, props);
|
||
}
|
||
|
||
adoptClassInstance(workInProgress, value);
|
||
mountClassInstance(workInProgress, Component, props, renderExpirationTime);
|
||
return finishClassComponent(null, workInProgress, Component, true, hasContext, renderExpirationTime);
|
||
} else {
|
||
// Proceed under the assumption that this is a function component
|
||
workInProgress.tag = FunctionComponent;
|
||
reconcileChildren(null, workInProgress, value, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
}
|
||
|
||
function updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
|
||
var mode = workInProgress.mode;
|
||
var nextProps = workInProgress.pendingProps;
|
||
|
||
// We should attempt to render the primary children unless this boundary
|
||
// already suspended during this render (`alreadyCaptured` is true).
|
||
var nextState = workInProgress.memoizedState;
|
||
|
||
var nextDidTimeout = void 0;
|
||
if ((workInProgress.effectTag & DidCapture) === NoEffect) {
|
||
// This is the first attempt.
|
||
nextState = null;
|
||
nextDidTimeout = false;
|
||
} else {
|
||
// Something in this boundary's subtree already suspended. Switch to
|
||
// rendering the fallback children.
|
||
nextState = {
|
||
timedOutAt: nextState !== null ? nextState.timedOutAt : NoWork
|
||
};
|
||
nextDidTimeout = true;
|
||
workInProgress.effectTag &= ~DidCapture;
|
||
}
|
||
|
||
// This next part is a bit confusing. If the children timeout, we switch to
|
||
// showing the fallback children in place of the "primary" children.
|
||
// However, we don't want to delete the primary children because then their
|
||
// state will be lost (both the React state and the host state, e.g.
|
||
// uncontrolled form inputs). Instead we keep them mounted and hide them.
|
||
// Both the fallback children AND the primary children are rendered at the
|
||
// same time. Once the primary children are un-suspended, we can delete
|
||
// the fallback children — don't need to preserve their state.
|
||
//
|
||
// The two sets of children are siblings in the host environment, but
|
||
// semantically, for purposes of reconciliation, they are two separate sets.
|
||
// So we store them using two fragment fibers.
|
||
//
|
||
// However, we want to avoid allocating extra fibers for every placeholder.
|
||
// They're only necessary when the children time out, because that's the
|
||
// only time when both sets are mounted.
|
||
//
|
||
// So, the extra fragment fibers are only used if the children time out.
|
||
// Otherwise, we render the primary children directly. This requires some
|
||
// custom reconciliation logic to preserve the state of the primary
|
||
// children. It's essentially a very basic form of re-parenting.
|
||
|
||
// `child` points to the child fiber. In the normal case, this is the first
|
||
// fiber of the primary children set. In the timed-out case, it's a
|
||
// a fragment fiber containing the primary children.
|
||
var child = void 0;
|
||
// `next` points to the next fiber React should render. In the normal case,
|
||
// it's the same as `child`: the first fiber of the primary children set.
|
||
// In the timed-out case, it's a fragment fiber containing the *fallback*
|
||
// children -- we skip over the primary children entirely.
|
||
var next = void 0;
|
||
if (current$$1 === null) {
|
||
if (enableSuspenseServerRenderer) {
|
||
// If we're currently hydrating, try to hydrate this boundary.
|
||
// But only if this has a fallback.
|
||
if (nextProps.fallback !== undefined) {
|
||
tryToClaimNextHydratableInstance(workInProgress);
|
||
// This could've changed the tag if this was a dehydrated suspense component.
|
||
if (workInProgress.tag === DehydratedSuspenseComponent) {
|
||
return updateDehydratedSuspenseComponent(null, workInProgress, renderExpirationTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
// This is the initial mount. This branch is pretty simple because there's
|
||
// no previous state that needs to be preserved.
|
||
if (nextDidTimeout) {
|
||
// Mount separate fragments for primary and fallback children.
|
||
var nextFallbackChildren = nextProps.fallback;
|
||
var primaryChildFragment = createFiberFromFragment(null, mode, NoWork, null);
|
||
|
||
if ((workInProgress.mode & ConcurrentMode) === NoContext) {
|
||
// Outside of concurrent mode, we commit the effects from the
|
||
var progressedState = workInProgress.memoizedState;
|
||
var progressedPrimaryChild = progressedState !== null ? workInProgress.child.child : workInProgress.child;
|
||
primaryChildFragment.child = progressedPrimaryChild;
|
||
}
|
||
|
||
var fallbackChildFragment = createFiberFromFragment(nextFallbackChildren, mode, renderExpirationTime, null);
|
||
primaryChildFragment.sibling = fallbackChildFragment;
|
||
child = primaryChildFragment;
|
||
// Skip the primary children, and continue working on the
|
||
// fallback children.
|
||
next = fallbackChildFragment;
|
||
child.return = next.return = workInProgress;
|
||
} else {
|
||
// Mount the primary children without an intermediate fragment fiber.
|
||
var nextPrimaryChildren = nextProps.children;
|
||
child = next = mountChildFibers(workInProgress, null, nextPrimaryChildren, renderExpirationTime);
|
||
}
|
||
} else {
|
||
// This is an update. This branch is more complicated because we need to
|
||
// ensure the state of the primary children is preserved.
|
||
var prevState = current$$1.memoizedState;
|
||
var prevDidTimeout = prevState !== null;
|
||
if (prevDidTimeout) {
|
||
// The current tree already timed out. That means each child set is
|
||
var currentPrimaryChildFragment = current$$1.child;
|
||
var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
|
||
if (nextDidTimeout) {
|
||
// Still timed out. Reuse the current primary children by cloning
|
||
// its fragment. We're going to skip over these entirely.
|
||
var _nextFallbackChildren = nextProps.fallback;
|
||
var _primaryChildFragment = createWorkInProgress(currentPrimaryChildFragment, currentPrimaryChildFragment.pendingProps, NoWork);
|
||
|
||
if ((workInProgress.mode & ConcurrentMode) === NoContext) {
|
||
// Outside of concurrent mode, we commit the effects from the
|
||
var _progressedState = workInProgress.memoizedState;
|
||
var _progressedPrimaryChild = _progressedState !== null ? workInProgress.child.child : workInProgress.child;
|
||
if (_progressedPrimaryChild !== currentPrimaryChildFragment.child) {
|
||
_primaryChildFragment.child = _progressedPrimaryChild;
|
||
}
|
||
}
|
||
|
||
// Because primaryChildFragment is a new fiber that we're inserting as the
|
||
// parent of a new tree, we need to set its treeBaseDuration.
|
||
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
|
||
// treeBaseDuration is the sum of all the child tree base durations.
|
||
var treeBaseDuration = 0;
|
||
var hiddenChild = _primaryChildFragment.child;
|
||
while (hiddenChild !== null) {
|
||
treeBaseDuration += hiddenChild.treeBaseDuration;
|
||
hiddenChild = hiddenChild.sibling;
|
||
}
|
||
_primaryChildFragment.treeBaseDuration = treeBaseDuration;
|
||
}
|
||
|
||
// Clone the fallback child fragment, too. These we'll continue
|
||
// working on.
|
||
var _fallbackChildFragment = _primaryChildFragment.sibling = createWorkInProgress(currentFallbackChildFragment, _nextFallbackChildren, currentFallbackChildFragment.expirationTime);
|
||
child = _primaryChildFragment;
|
||
_primaryChildFragment.childExpirationTime = NoWork;
|
||
// Skip the primary children, and continue working on the
|
||
// fallback children.
|
||
next = _fallbackChildFragment;
|
||
child.return = next.return = workInProgress;
|
||
} else {
|
||
// No longer suspended. Switch back to showing the primary children,
|
||
// and remove the intermediate fragment fiber.
|
||
var _nextPrimaryChildren = nextProps.children;
|
||
var currentPrimaryChild = currentPrimaryChildFragment.child;
|
||
var primaryChild = reconcileChildFibers(workInProgress, currentPrimaryChild, _nextPrimaryChildren, renderExpirationTime);
|
||
|
||
// If this render doesn't suspend, we need to delete the fallback
|
||
// children. Wait until the complete phase, after we've confirmed the
|
||
// fallback is no longer needed.
|
||
// TODO: Would it be better to store the fallback fragment on
|
||
// the stateNode?
|
||
|
||
// Continue rendering the children, like we normally do.
|
||
child = next = primaryChild;
|
||
}
|
||
} else {
|
||
// The current tree has not already timed out. That means the primary
|
||
// children are not wrapped in a fragment fiber.
|
||
var _currentPrimaryChild = current$$1.child;
|
||
if (nextDidTimeout) {
|
||
// Timed out. Wrap the children in a fragment fiber to keep them
|
||
// separate from the fallback children.
|
||
var _nextFallbackChildren2 = nextProps.fallback;
|
||
var _primaryChildFragment2 = createFiberFromFragment(
|
||
// It shouldn't matter what the pending props are because we aren't
|
||
// going to render this fragment.
|
||
null, mode, NoWork, null);
|
||
_primaryChildFragment2.child = _currentPrimaryChild;
|
||
|
||
// Even though we're creating a new fiber, there are no new children,
|
||
// because we're reusing an already mounted tree. So we don't need to
|
||
// schedule a placement.
|
||
// primaryChildFragment.effectTag |= Placement;
|
||
|
||
if ((workInProgress.mode & ConcurrentMode) === NoContext) {
|
||
// Outside of concurrent mode, we commit the effects from the
|
||
var _progressedState2 = workInProgress.memoizedState;
|
||
var _progressedPrimaryChild2 = _progressedState2 !== null ? workInProgress.child.child : workInProgress.child;
|
||
_primaryChildFragment2.child = _progressedPrimaryChild2;
|
||
}
|
||
|
||
// Because primaryChildFragment is a new fiber that we're inserting as the
|
||
// parent of a new tree, we need to set its treeBaseDuration.
|
||
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
|
||
// treeBaseDuration is the sum of all the child tree base durations.
|
||
var _treeBaseDuration = 0;
|
||
var _hiddenChild = _primaryChildFragment2.child;
|
||
while (_hiddenChild !== null) {
|
||
_treeBaseDuration += _hiddenChild.treeBaseDuration;
|
||
_hiddenChild = _hiddenChild.sibling;
|
||
}
|
||
_primaryChildFragment2.treeBaseDuration = _treeBaseDuration;
|
||
}
|
||
|
||
// Create a fragment from the fallback children, too.
|
||
var _fallbackChildFragment2 = _primaryChildFragment2.sibling = createFiberFromFragment(_nextFallbackChildren2, mode, renderExpirationTime, null);
|
||
_fallbackChildFragment2.effectTag |= Placement;
|
||
child = _primaryChildFragment2;
|
||
_primaryChildFragment2.childExpirationTime = NoWork;
|
||
// Skip the primary children, and continue working on the
|
||
// fallback children.
|
||
next = _fallbackChildFragment2;
|
||
child.return = next.return = workInProgress;
|
||
} else {
|
||
// Still haven't timed out. Continue rendering the children, like we
|
||
// normally do.
|
||
var _nextPrimaryChildren2 = nextProps.children;
|
||
next = child = reconcileChildFibers(workInProgress, _currentPrimaryChild, _nextPrimaryChildren2, renderExpirationTime);
|
||
}
|
||
}
|
||
workInProgress.stateNode = current$$1.stateNode;
|
||
}
|
||
|
||
workInProgress.memoizedState = nextState;
|
||
workInProgress.child = child;
|
||
return next;
|
||
}
|
||
|
||
function updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime) {
|
||
if (current$$1 === null) {
|
||
// 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.
|
||
workInProgress.expirationTime = Never;
|
||
return null;
|
||
}
|
||
// We use childExpirationTime to indicate that a child might depend on context, so if
|
||
// any context has changed, we need to treat is as if the input might have changed.
|
||
var hasContextChanged$$1 = current$$1.childExpirationTime >= renderExpirationTime;
|
||
if (didReceiveUpdate || hasContextChanged$$1) {
|
||
// 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 an earlier expiration time but
|
||
// during this render we can't. Instead, we're going to delete the whole subtree and
|
||
// instead inject a new real Suspense boundary to take its place, which may render content
|
||
// or fallback. The real Suspense boundary will suspend for a while so we have some time
|
||
// to ensure it can produce real content, but all state and pending events will be lost.
|
||
|
||
// Detach from the current dehydrated boundary.
|
||
current$$1.alternate = null;
|
||
workInProgress.alternate = null;
|
||
|
||
// Insert a deletion in the effect list.
|
||
var returnFiber = workInProgress.return;
|
||
!(returnFiber !== null) ? reactProdInvariant('315') : void 0;
|
||
var last = returnFiber.lastEffect;
|
||
if (last !== null) {
|
||
last.nextEffect = current$$1;
|
||
returnFiber.lastEffect = current$$1;
|
||
} else {
|
||
returnFiber.firstEffect = returnFiber.lastEffect = current$$1;
|
||
}
|
||
current$$1.nextEffect = null;
|
||
current$$1.effectTag = Deletion;
|
||
|
||
// Upgrade this work in progress to a real Suspense component.
|
||
workInProgress.tag = SuspenseComponent;
|
||
workInProgress.stateNode = null;
|
||
workInProgress.memoizedState = null;
|
||
// This is now an insertion.
|
||
workInProgress.effectTag |= Placement;
|
||
// Retry as a real Suspense component.
|
||
return updateSuspenseComponent(null, workInProgress, renderExpirationTime);
|
||
}
|
||
if ((workInProgress.effectTag & DidCapture) === NoEffect) {
|
||
// This is the first attempt.
|
||
reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress);
|
||
var nextProps = workInProgress.pendingProps;
|
||
var nextChildren = nextProps.children;
|
||
workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
} else {
|
||
// Something suspended. Leave the existing children in place.
|
||
// TODO: In non-concurrent mode, should we commit the nodes we have hydrated so far?
|
||
workInProgress.child = null;
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function updatePortalComponent(current$$1, workInProgress, renderExpirationTime) {
|
||
pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
||
var nextChildren = workInProgress.pendingProps;
|
||
if (current$$1 === 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, renderExpirationTime);
|
||
} else {
|
||
reconcileChildren(current$$1, workInProgress, nextChildren, renderExpirationTime);
|
||
}
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateContextProvider(current$$1, workInProgress, renderExpirationTime) {
|
||
var providerType = workInProgress.type;
|
||
var context = providerType._context;
|
||
|
||
var newProps = workInProgress.pendingProps;
|
||
var oldProps = workInProgress.memoizedProps;
|
||
|
||
var newValue = newProps.value;
|
||
|
||
pushProvider(workInProgress, newValue);
|
||
|
||
if (oldProps !== null) {
|
||
var oldValue = oldProps.value;
|
||
var changedBits = calculateChangedBits(context, newValue, oldValue);
|
||
if (changedBits === 0) {
|
||
// No change. Bailout early if children are the same.
|
||
if (oldProps.children === newProps.children && !hasContextChanged()) {
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
} else {
|
||
// The context value changed. Search for matching consumers and schedule
|
||
// them to update.
|
||
propagateContextChange(workInProgress, context, changedBits, renderExpirationTime);
|
||
}
|
||
}
|
||
|
||
var newChildren = newProps.children;
|
||
reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function updateContextConsumer(current$$1, workInProgress, renderExpirationTime) {
|
||
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.
|
||
var newProps = workInProgress.pendingProps;
|
||
var render = newProps.children;
|
||
|
||
prepareToReadContext(workInProgress, renderExpirationTime);
|
||
var newValue = readContext(context, newProps.unstable_observedBits);
|
||
var newChildren = void 0;
|
||
{
|
||
newChildren = render(newValue);
|
||
}
|
||
|
||
// React DevTools reads this flag.
|
||
workInProgress.effectTag |= PerformedWork;
|
||
reconcileChildren(current$$1, workInProgress, newChildren, renderExpirationTime);
|
||
return workInProgress.child;
|
||
}
|
||
|
||
function markWorkInProgressReceivedUpdate() {
|
||
didReceiveUpdate = true;
|
||
}
|
||
|
||
function bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime) {
|
||
cancelWorkTimer(workInProgress);
|
||
|
||
if (current$$1 !== null) {
|
||
// Reuse previous context list
|
||
workInProgress.contextDependencies = current$$1.contextDependencies;
|
||
}
|
||
|
||
if (enableProfilerTimer) {
|
||
// Don't update "base" render times for bailouts.
|
||
stopProfilerTimerIfRunning(workInProgress);
|
||
}
|
||
|
||
// Check if the children have any pending work.
|
||
var childExpirationTime = workInProgress.childExpirationTime;
|
||
if (childExpirationTime < renderExpirationTime) {
|
||
// 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;
|
||
} else {
|
||
// This fiber doesn't have work, but its subtree does. Clone the child
|
||
// fibers and continue.
|
||
cloneChildFibers(current$$1, workInProgress);
|
||
return workInProgress.child;
|
||
}
|
||
}
|
||
|
||
function beginWork(current$$1, workInProgress, renderExpirationTime) {
|
||
var updateExpirationTime = workInProgress.expirationTime;
|
||
|
||
if (current$$1 !== null) {
|
||
var oldProps = current$$1.memoizedProps;
|
||
var newProps = workInProgress.pendingProps;
|
||
|
||
if (oldProps !== newProps || hasContextChanged()) {
|
||
// 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 if (updateExpirationTime < renderExpirationTime) {
|
||
didReceiveUpdate = false;
|
||
// 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);
|
||
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;
|
||
pushProvider(workInProgress, newValue);
|
||
break;
|
||
}
|
||
case Profiler:
|
||
if (enableProfilerTimer) {
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
break;
|
||
case SuspenseComponent:
|
||
{
|
||
var state = workInProgress.memoizedState;
|
||
var didTimeout = state !== null;
|
||
if (didTimeout) {
|
||
// 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
|
||
var primaryChildFragment = workInProgress.child;
|
||
var primaryChildExpirationTime = primaryChildFragment.childExpirationTime;
|
||
if (primaryChildExpirationTime !== NoWork && primaryChildExpirationTime >= renderExpirationTime) {
|
||
// The primary children have pending work. Use the normal path
|
||
// to attempt to render the primary children again.
|
||
return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
|
||
} else {
|
||
// The primary children do not have pending work with sufficient
|
||
// priority. Bailout.
|
||
var child = bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
if (child !== null) {
|
||
// The fallback children have pending work. Skip over the
|
||
// primary children and work on the fallback.
|
||
return child.sibling;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case DehydratedSuspenseComponent:
|
||
{
|
||
if (enableSuspenseServerRenderer) {
|
||
// We know that this component will suspend again because if it has
|
||
// been unsuspended it has committed as a regular Suspense component.
|
||
// If it needs to be retried, it should have work scheduled on it.
|
||
workInProgress.effectTag |= DidCapture;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return bailoutOnAlreadyFinishedWork(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
} else {
|
||
didReceiveUpdate = false;
|
||
}
|
||
|
||
// Before entering the begin phase, clear the expiration time.
|
||
workInProgress.expirationTime = NoWork;
|
||
|
||
switch (workInProgress.tag) {
|
||
case IndeterminateComponent:
|
||
{
|
||
var elementType = workInProgress.elementType;
|
||
return mountIndeterminateComponent(current$$1, workInProgress, elementType, renderExpirationTime);
|
||
}
|
||
case LazyComponent:
|
||
{
|
||
var _elementType = workInProgress.elementType;
|
||
return mountLazyComponent(current$$1, workInProgress, _elementType, updateExpirationTime, renderExpirationTime);
|
||
}
|
||
case FunctionComponent:
|
||
{
|
||
var _Component = workInProgress.type;
|
||
var unresolvedProps = workInProgress.pendingProps;
|
||
var resolvedProps = workInProgress.elementType === _Component ? unresolvedProps : resolveDefaultProps(_Component, unresolvedProps);
|
||
return updateFunctionComponent(current$$1, workInProgress, _Component, resolvedProps, renderExpirationTime);
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var _Component2 = workInProgress.type;
|
||
var _unresolvedProps = workInProgress.pendingProps;
|
||
var _resolvedProps = workInProgress.elementType === _Component2 ? _unresolvedProps : resolveDefaultProps(_Component2, _unresolvedProps);
|
||
return updateClassComponent(current$$1, workInProgress, _Component2, _resolvedProps, renderExpirationTime);
|
||
}
|
||
case HostRoot:
|
||
return updateHostRoot(current$$1, workInProgress, renderExpirationTime);
|
||
case HostComponent:
|
||
return updateHostComponent(current$$1, workInProgress, renderExpirationTime);
|
||
case HostText:
|
||
return updateHostText(current$$1, workInProgress);
|
||
case SuspenseComponent:
|
||
return updateSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
|
||
case HostPortal:
|
||
return updatePortalComponent(current$$1, workInProgress, renderExpirationTime);
|
||
case ForwardRef:
|
||
{
|
||
var type = workInProgress.type;
|
||
var _unresolvedProps2 = workInProgress.pendingProps;
|
||
var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
|
||
return updateForwardRef(current$$1, workInProgress, type, _resolvedProps2, renderExpirationTime);
|
||
}
|
||
case Fragment:
|
||
return updateFragment(current$$1, workInProgress, renderExpirationTime);
|
||
case Mode:
|
||
return updateMode(current$$1, workInProgress, renderExpirationTime);
|
||
case Profiler:
|
||
return updateProfiler(current$$1, workInProgress, renderExpirationTime);
|
||
case ContextProvider:
|
||
return updateContextProvider(current$$1, workInProgress, renderExpirationTime);
|
||
case ContextConsumer:
|
||
return updateContextConsumer(current$$1, workInProgress, renderExpirationTime);
|
||
case MemoComponent:
|
||
{
|
||
var _type2 = workInProgress.type;
|
||
var _unresolvedProps3 = workInProgress.pendingProps;
|
||
// Resolve outer props first, then resolve inner props.
|
||
var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
|
||
_resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
|
||
return updateMemoComponent(current$$1, workInProgress, _type2, _resolvedProps3, updateExpirationTime, renderExpirationTime);
|
||
}
|
||
case SimpleMemoComponent:
|
||
{
|
||
return updateSimpleMemoComponent(current$$1, workInProgress, workInProgress.type, workInProgress.pendingProps, updateExpirationTime, renderExpirationTime);
|
||
}
|
||
case IncompleteClassComponent:
|
||
{
|
||
var _Component3 = workInProgress.type;
|
||
var _unresolvedProps4 = workInProgress.pendingProps;
|
||
var _resolvedProps4 = workInProgress.elementType === _Component3 ? _unresolvedProps4 : resolveDefaultProps(_Component3, _unresolvedProps4);
|
||
return mountIncompleteClassComponent(current$$1, workInProgress, _Component3, _resolvedProps4, renderExpirationTime);
|
||
}
|
||
case DehydratedSuspenseComponent:
|
||
{
|
||
if (enableSuspenseServerRenderer) {
|
||
return updateDehydratedSuspenseComponent(current$$1, workInProgress, renderExpirationTime);
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
reactProdInvariant('156');
|
||
}
|
||
|
||
var valueCursor = createCursor(null);
|
||
|
||
var currentlyRenderingFiber = null;
|
||
var lastContextDependency = null;
|
||
var lastContextWithAllBitsObserved = null;
|
||
|
||
function resetContextDependences() {
|
||
// This is called right before React yields execution, to ensure `readContext`
|
||
// cannot be called outside the render phase.
|
||
currentlyRenderingFiber = null;
|
||
lastContextDependency = null;
|
||
lastContextWithAllBitsObserved = null;
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
function pushProvider(providerFiber, nextValue) {
|
||
var context = providerFiber.type._context;
|
||
|
||
if (isPrimaryRenderer) {
|
||
push(valueCursor, context._currentValue, providerFiber);
|
||
|
||
context._currentValue = nextValue;
|
||
|
||
} else {
|
||
push(valueCursor, context._currentValue2, providerFiber);
|
||
|
||
context._currentValue2 = nextValue;
|
||
|
||
}
|
||
}
|
||
|
||
function popProvider(providerFiber) {
|
||
var currentValue = valueCursor.current;
|
||
|
||
pop(valueCursor, providerFiber);
|
||
|
||
var context = providerFiber.type._context;
|
||
if (isPrimaryRenderer) {
|
||
context._currentValue = currentValue;
|
||
} else {
|
||
context._currentValue2 = currentValue;
|
||
}
|
||
}
|
||
|
||
function calculateChangedBits(context, newValue, oldValue) {
|
||
if (is(oldValue, newValue)) {
|
||
// No change
|
||
return 0;
|
||
} else {
|
||
var changedBits = typeof context._calculateChangedBits === 'function' ? context._calculateChangedBits(oldValue, newValue) : maxSigned31BitInt;
|
||
|
||
return changedBits | 0;
|
||
}
|
||
}
|
||
|
||
function scheduleWorkOnParentPath(parent, renderExpirationTime) {
|
||
// Update the child expiration time of all the ancestors, including
|
||
// the alternates.
|
||
var node = parent;
|
||
while (node !== null) {
|
||
var alternate = node.alternate;
|
||
if (node.childExpirationTime < renderExpirationTime) {
|
||
node.childExpirationTime = renderExpirationTime;
|
||
if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
|
||
alternate.childExpirationTime = renderExpirationTime;
|
||
}
|
||
} else if (alternate !== null && alternate.childExpirationTime < renderExpirationTime) {
|
||
alternate.childExpirationTime = renderExpirationTime;
|
||
} else {
|
||
// Neither alternate was updated, which means the rest of the
|
||
// ancestor path already has sufficient priority.
|
||
break;
|
||
}
|
||
node = node.return;
|
||
}
|
||
}
|
||
|
||
function propagateContextChange(workInProgress, context, changedBits, renderExpirationTime) {
|
||
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.contextDependencies;
|
||
if (list !== null) {
|
||
nextFiber = fiber.child;
|
||
|
||
var dependency = list.first;
|
||
while (dependency !== null) {
|
||
// Check if the context matches.
|
||
if (dependency.context === context && (dependency.observedBits & changedBits) !== 0) {
|
||
// Match! Schedule an update on this fiber.
|
||
|
||
if (fiber.tag === ClassComponent) {
|
||
// Schedule a force update on the work-in-progress.
|
||
var update = createUpdate(renderExpirationTime);
|
||
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.
|
||
enqueueUpdate(fiber, update);
|
||
}
|
||
|
||
if (fiber.expirationTime < renderExpirationTime) {
|
||
fiber.expirationTime = renderExpirationTime;
|
||
}
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null && alternate.expirationTime < renderExpirationTime) {
|
||
alternate.expirationTime = renderExpirationTime;
|
||
}
|
||
|
||
scheduleWorkOnParentPath(fiber.return, renderExpirationTime);
|
||
|
||
// Mark the expiration time on the list, too.
|
||
if (list.expirationTime < renderExpirationTime) {
|
||
list.expirationTime = renderExpirationTime;
|
||
}
|
||
|
||
// 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 (enableSuspenseServerRenderer && fiber.tag === DehydratedSuspenseComponent) {
|
||
// If a dehydrated suspense component 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 on its children.
|
||
if (fiber.expirationTime < renderExpirationTime) {
|
||
fiber.expirationTime = renderExpirationTime;
|
||
}
|
||
var _alternate = fiber.alternate;
|
||
if (_alternate !== null && _alternate.expirationTime < renderExpirationTime) {
|
||
_alternate.expirationTime = renderExpirationTime;
|
||
}
|
||
// 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 childExpirationTime on
|
||
// this fiber to indicate that a context has changed.
|
||
scheduleWorkOnParentPath(fiber, renderExpirationTime);
|
||
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, renderExpirationTime) {
|
||
currentlyRenderingFiber = workInProgress;
|
||
lastContextDependency = null;
|
||
lastContextWithAllBitsObserved = null;
|
||
|
||
var currentDependencies = workInProgress.contextDependencies;
|
||
if (currentDependencies !== null && currentDependencies.expirationTime >= renderExpirationTime) {
|
||
// Context list has a pending update. Mark that this fiber performed work.
|
||
markWorkInProgressReceivedUpdate();
|
||
}
|
||
|
||
// Reset the work-in-progress list
|
||
workInProgress.contextDependencies = null;
|
||
}
|
||
|
||
function readContext(context, observedBits) {
|
||
if (lastContextWithAllBitsObserved === context) {
|
||
// Nothing to do. We already observe everything in this context.
|
||
} else if (observedBits === false || observedBits === 0) {
|
||
// Do not observe any updates.
|
||
} else {
|
||
var resolvedObservedBits = void 0; // Avoid deopting on observable arguments or heterogeneous types.
|
||
if (typeof observedBits !== 'number' || observedBits === maxSigned31BitInt) {
|
||
// Observe all updates.
|
||
lastContextWithAllBitsObserved = context;
|
||
resolvedObservedBits = maxSigned31BitInt;
|
||
} else {
|
||
resolvedObservedBits = observedBits;
|
||
}
|
||
|
||
var contextItem = {
|
||
context: context,
|
||
observedBits: resolvedObservedBits,
|
||
next: null
|
||
};
|
||
|
||
if (lastContextDependency === null) {
|
||
!(currentlyRenderingFiber !== null) ? reactProdInvariant('308') : void 0;
|
||
|
||
// This is the first dependency for this component. Create a new list.
|
||
lastContextDependency = contextItem;
|
||
currentlyRenderingFiber.contextDependencies = {
|
||
first: contextItem,
|
||
expirationTime: NoWork
|
||
};
|
||
} else {
|
||
// Append a new context item.
|
||
lastContextDependency = lastContextDependency.next = contextItem;
|
||
}
|
||
}
|
||
return isPrimaryRenderer ? context._currentValue : context._currentValue2;
|
||
}
|
||
|
||
// UpdateQueue is a linked list of prioritized updates.
|
||
//
|
||
// Like fibers, update queues come in pairs: a current queue, which represents
|
||
// the visible state of the screen, and a work-in-progress queue, which can be
|
||
// mutated and processed asynchronously before it is committed — a form of
|
||
// double buffering. If a work-in-progress render is discarded before finishing,
|
||
// we create a new work-in-progress by cloning the current queue.
|
||
//
|
||
// Both queues share a persistent, singly-linked list structure. To schedule an
|
||
// update, we append it to the end of both queues. Each queue maintains a
|
||
// pointer to first update in the persistent list that hasn't been processed.
|
||
// The work-in-progress pointer always has a position equal to or greater than
|
||
// the current queue, since we always work on that one. The current queue's
|
||
// pointer is only updated during the commit phase, when we swap in the
|
||
// work-in-progress.
|
||
//
|
||
// For example:
|
||
//
|
||
// Current pointer: A - B - C - D - E - F
|
||
// Work-in-progress pointer: D - E - F
|
||
// ^
|
||
// The work-in-progress queue has
|
||
// processed more updates than current.
|
||
//
|
||
// The reason we append to both queues is because otherwise we might drop
|
||
// updates without ever processing them. For example, if we only add updates to
|
||
// the work-in-progress queue, some updates could be lost whenever a work-in
|
||
// -progress render restarts by cloning from current. Similarly, if we only add
|
||
// updates to the current queue, the updates will be lost whenever an already
|
||
// in-progress queue commits and swaps with the current queue. However, by
|
||
// adding to both queues, we guarantee that the update will be part of the next
|
||
// work-in-progress. (And because the work-in-progress queue becomes the
|
||
// current queue once it commits, there's no danger of applying the same
|
||
// update twice.)
|
||
//
|
||
// Prioritization
|
||
// --------------
|
||
//
|
||
// Updates are not sorted by priority, but by insertion; new updates are always
|
||
// appended to the end of the list.
|
||
//
|
||
// The priority is still important, though. When processing the update queue
|
||
// during the render phase, only the updates with sufficient priority are
|
||
// included in the result. If we skip an update because it has insufficient
|
||
// priority, it remains in the queue to be processed later, during a lower
|
||
// priority render. Crucially, all updates subsequent to a skipped update also
|
||
// remain in the queue *regardless of their priority*. That means high priority
|
||
// updates are sometimes processed twice, at two separate priorities. We also
|
||
// keep track of a base state, that represents the state before the first
|
||
// update in the queue is applied.
|
||
//
|
||
// For example:
|
||
//
|
||
// Given a base state of '', and the following queue of updates
|
||
//
|
||
// A1 - B2 - C1 - D2
|
||
//
|
||
// where the number indicates the priority, and the update is applied to the
|
||
// previous state by appending a letter, React will process these updates as
|
||
// two separate renders, one per distinct priority level:
|
||
//
|
||
// First render, at priority 1:
|
||
// Base state: ''
|
||
// Updates: [A1, C1]
|
||
// Result state: 'AC'
|
||
//
|
||
// Second render, at priority 2:
|
||
// Base state: 'A' <- The base state does not include C1,
|
||
// because B2 was skipped.
|
||
// Updates: [B2, C1, D2] <- C1 was rebased on top of B2
|
||
// Result state: 'ABCD'
|
||
//
|
||
// Because we process updates in insertion order, and rebase high priority
|
||
// updates when preceding updates are skipped, the final result is deterministic
|
||
// regardless of priority. Intermediate state may vary according to system
|
||
// resources, but the final state is always the same.
|
||
|
||
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;
|
||
|
||
|
||
function createUpdateQueue(baseState) {
|
||
var queue = {
|
||
baseState: baseState,
|
||
firstUpdate: null,
|
||
lastUpdate: null,
|
||
firstCapturedUpdate: null,
|
||
lastCapturedUpdate: null,
|
||
firstEffect: null,
|
||
lastEffect: null,
|
||
firstCapturedEffect: null,
|
||
lastCapturedEffect: null
|
||
};
|
||
return queue;
|
||
}
|
||
|
||
function cloneUpdateQueue(currentQueue) {
|
||
var queue = {
|
||
baseState: currentQueue.baseState,
|
||
firstUpdate: currentQueue.firstUpdate,
|
||
lastUpdate: currentQueue.lastUpdate,
|
||
|
||
// TODO: With resuming, if we bail out and resuse the child tree, we should
|
||
// keep these effects.
|
||
firstCapturedUpdate: null,
|
||
lastCapturedUpdate: null,
|
||
|
||
firstEffect: null,
|
||
lastEffect: null,
|
||
|
||
firstCapturedEffect: null,
|
||
lastCapturedEffect: null
|
||
};
|
||
return queue;
|
||
}
|
||
|
||
function createUpdate(expirationTime) {
|
||
return {
|
||
expirationTime: expirationTime,
|
||
|
||
tag: UpdateState,
|
||
payload: null,
|
||
callback: null,
|
||
|
||
next: null,
|
||
nextEffect: null
|
||
};
|
||
}
|
||
|
||
function appendUpdateToQueue(queue, update) {
|
||
// Append the update to the end of the list.
|
||
if (queue.lastUpdate === null) {
|
||
// Queue is empty
|
||
queue.firstUpdate = queue.lastUpdate = update;
|
||
} else {
|
||
queue.lastUpdate.next = update;
|
||
queue.lastUpdate = update;
|
||
}
|
||
}
|
||
|
||
function enqueueUpdate(fiber, update) {
|
||
// Update queues are created lazily.
|
||
var alternate = fiber.alternate;
|
||
var queue1 = void 0;
|
||
var queue2 = void 0;
|
||
if (alternate === null) {
|
||
// There's only one fiber.
|
||
queue1 = fiber.updateQueue;
|
||
queue2 = null;
|
||
if (queue1 === null) {
|
||
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
|
||
}
|
||
} else {
|
||
// There are two owners.
|
||
queue1 = fiber.updateQueue;
|
||
queue2 = alternate.updateQueue;
|
||
if (queue1 === null) {
|
||
if (queue2 === null) {
|
||
// Neither fiber has an update queue. Create new ones.
|
||
queue1 = fiber.updateQueue = createUpdateQueue(fiber.memoizedState);
|
||
queue2 = alternate.updateQueue = createUpdateQueue(alternate.memoizedState);
|
||
} else {
|
||
// Only one fiber has an update queue. Clone to create a new one.
|
||
queue1 = fiber.updateQueue = cloneUpdateQueue(queue2);
|
||
}
|
||
} else {
|
||
if (queue2 === null) {
|
||
// Only one fiber has an update queue. Clone to create a new one.
|
||
queue2 = alternate.updateQueue = cloneUpdateQueue(queue1);
|
||
} else {
|
||
// Both owners have an update queue.
|
||
}
|
||
}
|
||
}
|
||
if (queue2 === null || queue1 === queue2) {
|
||
// There's only a single queue.
|
||
appendUpdateToQueue(queue1, update);
|
||
} else {
|
||
// There are two queues. We need to append the update to both queues,
|
||
// while accounting for the persistent structure of the list — we don't
|
||
// want the same update to be added multiple times.
|
||
if (queue1.lastUpdate === null || queue2.lastUpdate === null) {
|
||
// One of the queues is not empty. We must add the update to both queues.
|
||
appendUpdateToQueue(queue1, update);
|
||
appendUpdateToQueue(queue2, update);
|
||
} else {
|
||
// Both queues are non-empty. The last update is the same in both lists,
|
||
// because of structural sharing. So, only append to one of the lists.
|
||
appendUpdateToQueue(queue1, update);
|
||
// But we still need to update the `lastUpdate` pointer of queue2.
|
||
queue2.lastUpdate = update;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
function enqueueCapturedUpdate(workInProgress, update) {
|
||
// Captured updates go into a separate list, and only on the work-in-
|
||
// progress queue.
|
||
var workInProgressQueue = workInProgress.updateQueue;
|
||
if (workInProgressQueue === null) {
|
||
workInProgressQueue = workInProgress.updateQueue = createUpdateQueue(workInProgress.memoizedState);
|
||
} else {
|
||
// TODO: I put this here rather than createWorkInProgress so that we don't
|
||
// clone the queue unnecessarily. There's probably a better way to
|
||
// structure this.
|
||
workInProgressQueue = ensureWorkInProgressQueueIsAClone(workInProgress, workInProgressQueue);
|
||
}
|
||
|
||
// Append the update to the end of the list.
|
||
if (workInProgressQueue.lastCapturedUpdate === null) {
|
||
// This is the first render phase update
|
||
workInProgressQueue.firstCapturedUpdate = workInProgressQueue.lastCapturedUpdate = update;
|
||
} else {
|
||
workInProgressQueue.lastCapturedUpdate.next = update;
|
||
workInProgressQueue.lastCapturedUpdate = update;
|
||
}
|
||
}
|
||
|
||
function ensureWorkInProgressQueueIsAClone(workInProgress, queue) {
|
||
var current = workInProgress.alternate;
|
||
if (current !== null) {
|
||
// If the work-in-progress queue is equal to the current queue,
|
||
// we need to clone it first.
|
||
if (queue === current.updateQueue) {
|
||
queue = workInProgress.updateQueue = cloneUpdateQueue(queue);
|
||
}
|
||
}
|
||
return queue;
|
||
}
|
||
|
||
function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
|
||
switch (update.tag) {
|
||
case ReplaceState:
|
||
{
|
||
var _payload = update.payload;
|
||
if (typeof _payload === 'function') {
|
||
// Updater function
|
||
var nextState = _payload.call(instance, prevState, nextProps);
|
||
return nextState;
|
||
}
|
||
// State object
|
||
return _payload;
|
||
}
|
||
case CaptureUpdate:
|
||
{
|
||
workInProgress.effectTag = workInProgress.effectTag & ~ShouldCapture | DidCapture;
|
||
}
|
||
// Intentional fallthrough
|
||
case UpdateState:
|
||
{
|
||
var _payload2 = update.payload;
|
||
var partialState = void 0;
|
||
if (typeof _payload2 === 'function') {
|
||
// Updater function
|
||
partialState = _payload2.call(instance, prevState, nextProps);
|
||
|
||
} else {
|
||
// Partial state object
|
||
partialState = _payload2;
|
||
}
|
||
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, queue, props, instance, renderExpirationTime) {
|
||
hasForceUpdate = false;
|
||
|
||
queue = ensureWorkInProgressQueueIsAClone(workInProgress, queue);
|
||
|
||
var newBaseState = queue.baseState;
|
||
var newFirstUpdate = null;
|
||
var newExpirationTime = NoWork;
|
||
|
||
// Iterate through the list of updates to compute the result.
|
||
var update = queue.firstUpdate;
|
||
var resultState = newBaseState;
|
||
while (update !== null) {
|
||
var updateExpirationTime = update.expirationTime;
|
||
if (updateExpirationTime < renderExpirationTime) {
|
||
// This update does not have sufficient priority. Skip it.
|
||
if (newFirstUpdate === null) {
|
||
// This is the first skipped update. It will be the first update in
|
||
// the new list.
|
||
newFirstUpdate = update;
|
||
// Since this is the first update that was skipped, the current result
|
||
// is the new base state.
|
||
newBaseState = resultState;
|
||
}
|
||
// Since this update will remain in the list, update the remaining
|
||
// expiration time.
|
||
if (newExpirationTime < updateExpirationTime) {
|
||
newExpirationTime = updateExpirationTime;
|
||
}
|
||
} else {
|
||
// This update does have sufficient priority. Process it and compute
|
||
// a new result.
|
||
resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
|
||
var _callback = update.callback;
|
||
if (_callback !== null) {
|
||
workInProgress.effectTag |= Callback;
|
||
// Set this to null, in case it was mutated during an aborted render.
|
||
update.nextEffect = null;
|
||
if (queue.lastEffect === null) {
|
||
queue.firstEffect = queue.lastEffect = update;
|
||
} else {
|
||
queue.lastEffect.nextEffect = update;
|
||
queue.lastEffect = update;
|
||
}
|
||
}
|
||
}
|
||
// Continue to the next update.
|
||
update = update.next;
|
||
}
|
||
|
||
// Separately, iterate though the list of captured updates.
|
||
var newFirstCapturedUpdate = null;
|
||
update = queue.firstCapturedUpdate;
|
||
while (update !== null) {
|
||
var _updateExpirationTime = update.expirationTime;
|
||
if (_updateExpirationTime < renderExpirationTime) {
|
||
// This update does not have sufficient priority. Skip it.
|
||
if (newFirstCapturedUpdate === null) {
|
||
// This is the first skipped captured update. It will be the first
|
||
// update in the new list.
|
||
newFirstCapturedUpdate = update;
|
||
// If this is the first update that was skipped, the current result is
|
||
// the new base state.
|
||
if (newFirstUpdate === null) {
|
||
newBaseState = resultState;
|
||
}
|
||
}
|
||
// Since this update will remain in the list, update the remaining
|
||
// expiration time.
|
||
if (newExpirationTime < _updateExpirationTime) {
|
||
newExpirationTime = _updateExpirationTime;
|
||
}
|
||
} else {
|
||
// This update does have sufficient priority. Process it and compute
|
||
// a new result.
|
||
resultState = getStateFromUpdate(workInProgress, queue, update, resultState, props, instance);
|
||
var _callback2 = update.callback;
|
||
if (_callback2 !== null) {
|
||
workInProgress.effectTag |= Callback;
|
||
// Set this to null, in case it was mutated during an aborted render.
|
||
update.nextEffect = null;
|
||
if (queue.lastCapturedEffect === null) {
|
||
queue.firstCapturedEffect = queue.lastCapturedEffect = update;
|
||
} else {
|
||
queue.lastCapturedEffect.nextEffect = update;
|
||
queue.lastCapturedEffect = update;
|
||
}
|
||
}
|
||
}
|
||
update = update.next;
|
||
}
|
||
|
||
if (newFirstUpdate === null) {
|
||
queue.lastUpdate = null;
|
||
}
|
||
if (newFirstCapturedUpdate === null) {
|
||
queue.lastCapturedUpdate = null;
|
||
} else {
|
||
workInProgress.effectTag |= Callback;
|
||
}
|
||
if (newFirstUpdate === null && newFirstCapturedUpdate === null) {
|
||
// We processed every update, without skipping. That means the new base
|
||
// state is the same as the result state.
|
||
newBaseState = resultState;
|
||
}
|
||
|
||
queue.baseState = newBaseState;
|
||
queue.firstUpdate = newFirstUpdate;
|
||
queue.firstCapturedUpdate = newFirstCapturedUpdate;
|
||
|
||
// 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.
|
||
workInProgress.expirationTime = newExpirationTime;
|
||
workInProgress.memoizedState = resultState;
|
||
|
||
|
||
}
|
||
|
||
function callCallback(callback, context) {
|
||
!(typeof callback === 'function') ? reactProdInvariant('191', callback) : void 0;
|
||
callback.call(context);
|
||
}
|
||
|
||
function resetHasForceUpdateBeforeProcessing() {
|
||
hasForceUpdate = false;
|
||
}
|
||
|
||
function checkHasForceUpdateAfterProcessing() {
|
||
return hasForceUpdate;
|
||
}
|
||
|
||
function commitUpdateQueue(finishedWork, finishedQueue, instance, renderExpirationTime) {
|
||
// If the finished render included captured updates, and there are still
|
||
// lower priority updates left over, we need to keep the captured updates
|
||
// in the queue so that they are rebased and not dropped once we process the
|
||
// queue again at the lower priority.
|
||
if (finishedQueue.firstCapturedUpdate !== null) {
|
||
// Join the captured update list to the end of the normal list.
|
||
if (finishedQueue.lastUpdate !== null) {
|
||
finishedQueue.lastUpdate.next = finishedQueue.firstCapturedUpdate;
|
||
finishedQueue.lastUpdate = finishedQueue.lastCapturedUpdate;
|
||
}
|
||
// Clear the list of captured updates.
|
||
finishedQueue.firstCapturedUpdate = finishedQueue.lastCapturedUpdate = null;
|
||
}
|
||
|
||
// Commit the effects
|
||
commitUpdateEffects(finishedQueue.firstEffect, instance);
|
||
finishedQueue.firstEffect = finishedQueue.lastEffect = null;
|
||
|
||
commitUpdateEffects(finishedQueue.firstCapturedEffect, instance);
|
||
finishedQueue.firstCapturedEffect = finishedQueue.lastCapturedEffect = null;
|
||
}
|
||
|
||
function commitUpdateEffects(effect, instance) {
|
||
while (effect !== null) {
|
||
var _callback3 = effect.callback;
|
||
if (_callback3 !== null) {
|
||
effect.callback = null;
|
||
callCallback(_callback3, instance);
|
||
}
|
||
effect = effect.nextEffect;
|
||
}
|
||
}
|
||
|
||
function createCapturedValue(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)
|
||
};
|
||
}
|
||
|
||
function markUpdate(workInProgress) {
|
||
// Tag the fiber with an update effect. This turns a Placement into
|
||
// a PlacementAndUpdate.
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
|
||
function markRef$1(workInProgress) {
|
||
workInProgress.effectTag |= Ref;
|
||
}
|
||
|
||
var appendAllChildren = void 0;
|
||
var updateHostContainer = void 0;
|
||
var updateHostComponent$1 = void 0;
|
||
var updateHostText$1 = void 0;
|
||
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) {
|
||
// If we have a portal child, then we don't want to traverse
|
||
// down its children. Instead, we'll get insertions from each child in
|
||
// the portal directly.
|
||
} 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 (workInProgress) {
|
||
// Noop
|
||
};
|
||
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
|
||
branches: if (node.tag === HostComponent) {
|
||
var instance = node.stateNode;
|
||
if (needsVisibilityToggle) {
|
||
var props = node.memoizedProps;
|
||
var type = node.type;
|
||
if (isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
instance = cloneHiddenInstance(instance, type, props, node);
|
||
} else {
|
||
// This child was previously inside a timed out tree. If it was not
|
||
// updated during this render, it may need to be unhidden. Clone
|
||
// again to be sure.
|
||
instance = cloneUnhiddenInstance(instance, type, props, node);
|
||
}
|
||
node.stateNode = instance;
|
||
}
|
||
appendInitialChild(parent, instance);
|
||
} else if (node.tag === HostText) {
|
||
var _instance = node.stateNode;
|
||
if (needsVisibilityToggle) {
|
||
var text = node.memoizedProps;
|
||
var rootContainerInstance = getRootHostContainer();
|
||
var currentHostContext = getHostContext();
|
||
if (isHidden) {
|
||
_instance = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
|
||
} else {
|
||
_instance = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
|
||
}
|
||
node.stateNode = _instance;
|
||
}
|
||
appendInitialChild(parent, _instance);
|
||
} else if (node.tag === HostPortal) {
|
||
// If we have a portal child, then we don't want to traverse
|
||
// down its children. Instead, we'll get insertions from each child in
|
||
// the portal directly.
|
||
} else if (node.tag === SuspenseComponent) {
|
||
var current = node.alternate;
|
||
if (current !== null) {
|
||
var oldState = current.memoizedState;
|
||
var newState = node.memoizedState;
|
||
var oldIsHidden = oldState !== null;
|
||
var newIsHidden = newState !== null;
|
||
if (oldIsHidden !== newIsHidden) {
|
||
// The placeholder either just timed out or switched back to the normal
|
||
// children after having previously timed out. Toggle the visibility of
|
||
// the direct host children.
|
||
var primaryChildParent = newIsHidden ? node.child : node;
|
||
if (primaryChildParent !== null) {
|
||
appendAllChildren(parent, primaryChildParent, true, newIsHidden);
|
||
}
|
||
// eslint-disable-next-line no-labels
|
||
break branches;
|
||
}
|
||
}
|
||
if (node.child !== null) {
|
||
// Continue traversing like normal
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
} 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
|
||
branches: if (node.tag === HostComponent) {
|
||
var instance = node.stateNode;
|
||
if (needsVisibilityToggle) {
|
||
var props = node.memoizedProps;
|
||
var type = node.type;
|
||
if (isHidden) {
|
||
// This child is inside a timed out tree. Hide it.
|
||
instance = cloneHiddenInstance(instance, type, props, node);
|
||
} else {
|
||
// This child was previously inside a timed out tree. If it was not
|
||
// updated during this render, it may need to be unhidden. Clone
|
||
// again to be sure.
|
||
instance = cloneUnhiddenInstance(instance, type, props, node);
|
||
}
|
||
node.stateNode = instance;
|
||
}
|
||
appendChildToContainerChildSet(containerChildSet, instance);
|
||
} else if (node.tag === HostText) {
|
||
var _instance2 = node.stateNode;
|
||
if (needsVisibilityToggle) {
|
||
var text = node.memoizedProps;
|
||
var rootContainerInstance = getRootHostContainer();
|
||
var currentHostContext = getHostContext();
|
||
if (isHidden) {
|
||
_instance2 = createHiddenTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
|
||
} else {
|
||
_instance2 = createTextInstance(text, rootContainerInstance, currentHostContext, workInProgress);
|
||
}
|
||
node.stateNode = _instance2;
|
||
}
|
||
appendChildToContainerChildSet(containerChildSet, _instance2);
|
||
} else if (node.tag === HostPortal) {
|
||
// If we have a portal child, then we don't want to traverse
|
||
// down its children. Instead, we'll get insertions from each child in
|
||
// the portal directly.
|
||
} else if (node.tag === SuspenseComponent) {
|
||
var current = node.alternate;
|
||
if (current !== null) {
|
||
var oldState = current.memoizedState;
|
||
var newState = node.memoizedState;
|
||
var oldIsHidden = oldState !== null;
|
||
var newIsHidden = newState !== null;
|
||
if (oldIsHidden !== newIsHidden) {
|
||
// The placeholder either just timed out or switched back to the normal
|
||
// children after having previously timed out. Toggle the visibility of
|
||
// the direct host children.
|
||
var primaryChildParent = newIsHidden ? node.child : node;
|
||
if (primaryChildParent !== null) {
|
||
appendAllChildrenToContainer(containerChildSet, primaryChildParent, true, newIsHidden);
|
||
}
|
||
// eslint-disable-next-line no-labels
|
||
break branches;
|
||
}
|
||
}
|
||
if (node.child !== null) {
|
||
// Continue traversing like normal
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
} 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 (workInProgress) {
|
||
var portalOrRoot = workInProgress.stateNode;
|
||
var childrenUnchanged = workInProgress.firstEffect === null;
|
||
if (childrenUnchanged) {
|
||
// No changes, just reuse the existing instance.
|
||
} 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 = workInProgress.firstEffect === null;
|
||
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 {
|
||
// No host operations
|
||
updateHostContainer = function (workInProgress) {
|
||
// Noop
|
||
};
|
||
updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
|
||
// Noop
|
||
};
|
||
updateHostText$1 = function (current, workInProgress, oldText, newText) {
|
||
// Noop
|
||
};
|
||
}
|
||
|
||
function completeWork(current, workInProgress, renderExpirationTime) {
|
||
var newProps = workInProgress.pendingProps;
|
||
|
||
switch (workInProgress.tag) {
|
||
case IndeterminateComponent:
|
||
break;
|
||
case LazyComponent:
|
||
break;
|
||
case SimpleMemoComponent:
|
||
case FunctionComponent:
|
||
break;
|
||
case ClassComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
if (isContextProvider(Component)) {
|
||
popContext(workInProgress);
|
||
}
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
popHostContainer(workInProgress);
|
||
popTopLevelContextObject(workInProgress);
|
||
var fiberRoot = workInProgress.stateNode;
|
||
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.
|
||
popHydrationState(workInProgress);
|
||
// This resets the hacky state to fix isMounted before committing.
|
||
// TODO: Delete this when we delete isMounted and findDOMNode.
|
||
workInProgress.effectTag &= ~Placement;
|
||
}
|
||
updateHostContainer(workInProgress);
|
||
break;
|
||
}
|
||
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) {
|
||
!(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
|
||
// This can happen when we abort work.
|
||
break;
|
||
}
|
||
|
||
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 we want to add then 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 needs 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);
|
||
|
||
// 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);
|
||
}
|
||
workInProgress.stateNode = instance;
|
||
}
|
||
|
||
if (workInProgress.ref !== null) {
|
||
// If there is a ref on a host node we need to schedule a callback
|
||
markRef$1(workInProgress);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
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') {
|
||
!(workInProgress.stateNode !== null) ? reactProdInvariant('166') : void 0;
|
||
// This can happen when we abort work.
|
||
}
|
||
var _rootContainerInstance = getRootHostContainer();
|
||
var _currentHostContext = getHostContext();
|
||
var _wasHydrated = popHydrationState(workInProgress);
|
||
if (_wasHydrated) {
|
||
if (prepareToHydrateHostTextInstance(workInProgress)) {
|
||
markUpdate(workInProgress);
|
||
}
|
||
} else {
|
||
workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case ForwardRef:
|
||
break;
|
||
case SuspenseComponent:
|
||
{
|
||
var nextState = workInProgress.memoizedState;
|
||
if ((workInProgress.effectTag & DidCapture) !== NoEffect) {
|
||
// Something suspended. Re-render with the fallback children.
|
||
workInProgress.expirationTime = renderExpirationTime;
|
||
// Do not reset the effect list.
|
||
return workInProgress;
|
||
}
|
||
|
||
var nextDidTimeout = nextState !== null;
|
||
var prevDidTimeout = current !== null && current.memoizedState !== null;
|
||
|
||
if (current !== null && !nextDidTimeout && prevDidTimeout) {
|
||
// We just switched from the fallback to the normal children. Delete
|
||
// the fallback.
|
||
// TODO: Would it be better to store the fallback fragment on
|
||
var currentFallbackChild = current.child.sibling;
|
||
if (currentFallbackChild !== null) {
|
||
// Deletions go at the beginning of the return fiber's effect list
|
||
var first = workInProgress.firstEffect;
|
||
if (first !== null) {
|
||
workInProgress.firstEffect = currentFallbackChild;
|
||
currentFallbackChild.nextEffect = first;
|
||
} else {
|
||
workInProgress.firstEffect = workInProgress.lastEffect = currentFallbackChild;
|
||
currentFallbackChild.nextEffect = null;
|
||
}
|
||
currentFallbackChild.effectTag = Deletion;
|
||
}
|
||
}
|
||
|
||
if (nextDidTimeout || prevDidTimeout) {
|
||
// If the children are hidden, or if they were previous hidden, schedule
|
||
// an effect to toggle their visibility. This is also used to attach a
|
||
// retry listener to the promise.
|
||
workInProgress.effectTag |= Update;
|
||
}
|
||
break;
|
||
}
|
||
case Fragment:
|
||
break;
|
||
case Mode:
|
||
break;
|
||
case Profiler:
|
||
break;
|
||
case HostPortal:
|
||
popHostContainer(workInProgress);
|
||
updateHostContainer(workInProgress);
|
||
break;
|
||
case ContextProvider:
|
||
// Pop provider fiber
|
||
popProvider(workInProgress);
|
||
break;
|
||
case ContextConsumer:
|
||
break;
|
||
case MemoComponent:
|
||
break;
|
||
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);
|
||
}
|
||
break;
|
||
}
|
||
case DehydratedSuspenseComponent:
|
||
{
|
||
if (enableSuspenseServerRenderer) {
|
||
if (current === null) {
|
||
var _wasHydrated2 = popHydrationState(workInProgress);
|
||
!_wasHydrated2 ? reactProdInvariant('318') : void 0;
|
||
skipPastDehydratedSuspenseInstance(workInProgress);
|
||
} else if ((workInProgress.effectTag & DidCapture) === NoEffect) {
|
||
// This boundary did not suspend so it's now hydrated.
|
||
// To handle any future suspense cases, we're going to now upgrade it
|
||
// to a Suspense component. We detach it from the existing current fiber.
|
||
current.alternate = null;
|
||
workInProgress.alternate = null;
|
||
workInProgress.tag = SuspenseComponent;
|
||
workInProgress.memoizedState = null;
|
||
workInProgress.stateNode = null;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
default:
|
||
reactProdInvariant('156');
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
function shouldCaptureSuspense(workInProgress) {
|
||
// In order to capture, the Suspense component must have a fallback prop.
|
||
if (workInProgress.memoizedProps.fallback === undefined) {
|
||
return false;
|
||
}
|
||
// 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;
|
||
return nextState === 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(capturedError) {
|
||
return true;
|
||
}
|
||
|
||
function logCapturedError(capturedError) {
|
||
var logError = showErrorDialog(capturedError);
|
||
|
||
// 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 = capturedError.error;
|
||
{
|
||
// 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);
|
||
}
|
||
}
|
||
|
||
var PossiblyWeakSet$1 = typeof WeakSet === 'function' ? WeakSet : Set;
|
||
|
||
function logError(boundary, errorInfo) {
|
||
var source = errorInfo.source;
|
||
var stack = errorInfo.stack;
|
||
if (stack === null && source !== null) {
|
||
stack = getStackByFiberInDevAndProd(source);
|
||
}
|
||
|
||
var capturedError = {
|
||
componentName: source !== null ? getComponentName(source.type) : null,
|
||
componentStack: stack !== null ? stack : '',
|
||
error: errorInfo.value,
|
||
errorBoundary: null,
|
||
errorBoundaryName: null,
|
||
errorBoundaryFound: false,
|
||
willRetry: false
|
||
};
|
||
|
||
if (boundary !== null && boundary.tag === ClassComponent) {
|
||
capturedError.errorBoundary = boundary.stateNode;
|
||
capturedError.errorBoundaryName = getComponentName(boundary.type);
|
||
capturedError.errorBoundaryFound = true;
|
||
capturedError.willRetry = true;
|
||
}
|
||
|
||
try {
|
||
logCapturedError(capturedError);
|
||
} 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 callComponentWillUnmountWithTimer = function (current$$1, instance) {
|
||
startPhaseTimer(current$$1, 'componentWillUnmount');
|
||
instance.props = current$$1.memoizedProps;
|
||
instance.state = current$$1.memoizedState;
|
||
instance.componentWillUnmount();
|
||
stopPhaseTimer();
|
||
};
|
||
|
||
// Capture errors so they don't interrupt unmounting.
|
||
function safelyCallComponentWillUnmount(current$$1, instance) {
|
||
{
|
||
try {
|
||
callComponentWillUnmountWithTimer(current$$1, instance);
|
||
} catch (unmountError) {
|
||
captureCommitPhaseError(current$$1, unmountError);
|
||
}
|
||
}
|
||
}
|
||
|
||
function safelyDetachRef(current$$1) {
|
||
var ref = current$$1.ref;
|
||
if (ref !== null) {
|
||
if (typeof ref === 'function') {
|
||
{
|
||
try {
|
||
ref(null);
|
||
} catch (refError) {
|
||
captureCommitPhaseError(current$$1, refError);
|
||
}
|
||
}
|
||
} else {
|
||
ref.current = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
function safelyCallDestroy(current$$1, destroy) {
|
||
{
|
||
try {
|
||
destroy();
|
||
} catch (error) {
|
||
captureCommitPhaseError(current$$1, error);
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitBeforeMutationLifeCycles(current$$1, finishedWork) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
commitHookEffectList(UnmountSnapshot, NoEffect$1, finishedWork);
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
if (finishedWork.effectTag & Snapshot) {
|
||
if (current$$1 !== null) {
|
||
var prevProps = current$$1.memoizedProps;
|
||
var prevState = current$$1.memoizedState;
|
||
startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
|
||
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.
|
||
var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
|
||
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
||
stopPhaseTimer();
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostRoot:
|
||
case HostComponent:
|
||
case HostText:
|
||
case HostPortal:
|
||
case IncompleteClassComponent:
|
||
// Nothing to do for these component types
|
||
return;
|
||
default:
|
||
{
|
||
reactProdInvariant('163');
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitHookEffectList(unmountTag, mountTag, 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 & unmountTag) !== NoEffect$1) {
|
||
// Unmount
|
||
var destroy = effect.destroy;
|
||
effect.destroy = undefined;
|
||
if (destroy !== undefined) {
|
||
destroy();
|
||
}
|
||
}
|
||
if ((effect.tag & mountTag) !== NoEffect$1) {
|
||
// Mount
|
||
var create = effect.create;
|
||
effect.destroy = create();
|
||
|
||
|
||
}
|
||
effect = effect.next;
|
||
} while (effect !== firstEffect);
|
||
}
|
||
}
|
||
|
||
function commitPassiveHookEffects(finishedWork) {
|
||
commitHookEffectList(UnmountPassive, NoEffect$1, finishedWork);
|
||
commitHookEffectList(NoEffect$1, MountPassive, finishedWork);
|
||
}
|
||
|
||
function commitLifeCycles(finishedRoot, current$$1, finishedWork, committedExpirationTime) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case SimpleMemoComponent:
|
||
{
|
||
commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
var instance = finishedWork.stateNode;
|
||
if (finishedWork.effectTag & Update) {
|
||
if (current$$1 === null) {
|
||
startPhaseTimer(finishedWork, 'componentDidMount');
|
||
// 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.
|
||
instance.componentDidMount();
|
||
stopPhaseTimer();
|
||
} else {
|
||
var prevProps = finishedWork.elementType === finishedWork.type ? current$$1.memoizedProps : resolveDefaultProps(finishedWork.type, current$$1.memoizedProps);
|
||
var prevState = current$$1.memoizedState;
|
||
startPhaseTimer(finishedWork, 'componentDidUpdate');
|
||
// 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.
|
||
instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
|
||
stopPhaseTimer();
|
||
}
|
||
}
|
||
var updateQueue = finishedWork.updateQueue;
|
||
if (updateQueue !== null) {
|
||
commitUpdateQueue(finishedWork, updateQueue, instance, committedExpirationTime);
|
||
}
|
||
return;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
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, committedExpirationTime);
|
||
}
|
||
return;
|
||
}
|
||
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$$1 === null && finishedWork.effectTag & Update) {
|
||
var type = finishedWork.type;
|
||
var props = finishedWork.memoizedProps;
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
case HostText:
|
||
{
|
||
// We have no life-cycles associated with text.
|
||
return;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
// We have no life-cycles associated with portals.
|
||
return;
|
||
}
|
||
case Profiler:
|
||
{
|
||
if (enableProfilerTimer) {
|
||
var onRender = finishedWork.memoizedProps.onRender;
|
||
|
||
if (enableSchedulerTracing) {
|
||
onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime(), finishedRoot.memoizedInteractions);
|
||
} else {
|
||
onRender(finishedWork.memoizedProps.id, current$$1 === null ? 'mount' : 'update', finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, getCommitTime());
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case SuspenseComponent:
|
||
break;
|
||
case IncompleteClassComponent:
|
||
break;
|
||
default:
|
||
{
|
||
reactProdInvariant('163');
|
||
}
|
||
}
|
||
}
|
||
|
||
function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
||
if (supportsMutation) {
|
||
// We only have the top Fiber that was inserted but we need to recurse down its
|
||
var node = finishedWork;
|
||
while (true) {
|
||
if (node.tag === HostComponent) {
|
||
var instance = node.stateNode;
|
||
if (isHidden) {
|
||
hideInstance(instance);
|
||
} else {
|
||
unhideInstance(node.stateNode, node.memoizedProps);
|
||
}
|
||
} else if (node.tag === HostText) {
|
||
var _instance3 = node.stateNode;
|
||
if (isHidden) {
|
||
hideTextInstance(_instance3);
|
||
} else {
|
||
unhideTextInstance(_instance3, node.memoizedProps);
|
||
}
|
||
} else if (node.tag === SuspenseComponent && node.memoizedState !== null) {
|
||
// Found a nested Suspense component that timed out. Skip over the
|
||
var fallbackChildFragment = node.child.sibling;
|
||
fallbackChildFragment.return = node;
|
||
node = fallbackChildFragment;
|
||
continue;
|
||
} 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;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitAttachRef(finishedWork) {
|
||
var ref = finishedWork.ref;
|
||
if (ref !== null) {
|
||
var instance = finishedWork.stateNode;
|
||
var instanceToUse = void 0;
|
||
switch (finishedWork.tag) {
|
||
case HostComponent:
|
||
instanceToUse = getPublicInstance(instance);
|
||
break;
|
||
default:
|
||
instanceToUse = instance;
|
||
}
|
||
if (typeof ref === 'function') {
|
||
ref(instanceToUse);
|
||
} else {
|
||
ref.current = instanceToUse;
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitDetachRef(current$$1) {
|
||
var currentRef = current$$1.ref;
|
||
if (currentRef !== null) {
|
||
if (typeof currentRef === 'function') {
|
||
currentRef(null);
|
||
} else {
|
||
currentRef.current = null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// User-originating errors (lifecycles and refs) should not interrupt
|
||
// deletion, so don't let them throw. Host-originating errors should
|
||
// interrupt deletion, so it's okay
|
||
function commitUnmount(current$$1) {
|
||
onCommitUnmount(current$$1);
|
||
|
||
switch (current$$1.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
var updateQueue = current$$1.updateQueue;
|
||
if (updateQueue !== null) {
|
||
var lastEffect = updateQueue.lastEffect;
|
||
if (lastEffect !== null) {
|
||
var firstEffect = lastEffect.next;
|
||
var effect = firstEffect;
|
||
do {
|
||
var destroy = effect.destroy;
|
||
if (destroy !== undefined) {
|
||
safelyCallDestroy(current$$1, destroy);
|
||
}
|
||
effect = effect.next;
|
||
} while (effect !== firstEffect);
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
safelyDetachRef(current$$1);
|
||
var instance = current$$1.stateNode;
|
||
if (typeof instance.componentWillUnmount === 'function') {
|
||
safelyCallComponentWillUnmount(current$$1, instance);
|
||
}
|
||
return;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
safelyDetachRef(current$$1);
|
||
return;
|
||
}
|
||
case HostPortal:
|
||
{
|
||
// TODO: this is recursive.
|
||
// We are also not using this parent because
|
||
// the portal will get pushed immediately.
|
||
if (supportsMutation) {
|
||
unmountHostComponents(current$$1);
|
||
} else if (supportsPersistence) {
|
||
emptyPortalContainer(current$$1);
|
||
}
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitNestedUnmounts(root) {
|
||
// While we're inside a removed host node we don't want to call
|
||
// removeChild on the inner nodes because they're removed by the top
|
||
// call anyway. We also want to call componentWillUnmount on all
|
||
// composites before this host node is removed from the tree. Therefore
|
||
var node = root;
|
||
while (true) {
|
||
commitUnmount(node);
|
||
// Visit children because they may contain more composite or host nodes.
|
||
// Skip portals because commitUnmount() currently visits them recursively.
|
||
if (node.child !== null && (
|
||
// If we use mutation we drill down into portals using commitUnmount above.
|
||
// If we don't use mutation we drill down into portals here instead.
|
||
!supportsMutation || node.tag !== HostPortal)) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
if (node === root) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === root) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
|
||
function detachFiber(current$$1) {
|
||
// Cut off the return pointers to disconnect it from the tree. Ideally, we
|
||
// should 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.
|
||
current$$1.return = null;
|
||
current$$1.child = null;
|
||
current$$1.memoizedState = null;
|
||
current$$1.updateQueue = null;
|
||
var alternate = current$$1.alternate;
|
||
if (alternate !== null) {
|
||
alternate.return = null;
|
||
alternate.child = null;
|
||
alternate.memoizedState = null;
|
||
alternate.updateQueue = null;
|
||
}
|
||
}
|
||
|
||
function emptyPortalContainer(current$$1) {
|
||
if (!supportsPersistence) {
|
||
return;
|
||
}
|
||
|
||
var portal = current$$1.stateNode;
|
||
var containerInfo = portal.containerInfo;
|
||
|
||
var emptyChildSet = createContainerChildSet(containerInfo);
|
||
replaceContainerChildren(containerInfo, emptyChildSet);
|
||
}
|
||
|
||
function commitContainer(finishedWork) {
|
||
if (!supportsPersistence) {
|
||
return;
|
||
}
|
||
|
||
switch (finishedWork.tag) {
|
||
case ClassComponent:
|
||
{
|
||
return;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
return;
|
||
}
|
||
case HostText:
|
||
{
|
||
return;
|
||
}
|
||
case HostRoot:
|
||
case HostPortal:
|
||
{
|
||
var portalOrRoot = finishedWork.stateNode;
|
||
var containerInfo = portalOrRoot.containerInfo,
|
||
_pendingChildren = portalOrRoot.pendingChildren;
|
||
|
||
replaceContainerChildren(containerInfo, _pendingChildren);
|
||
return;
|
||
}
|
||
default:
|
||
{
|
||
reactProdInvariant('163');
|
||
}
|
||
}
|
||
}
|
||
|
||
function getHostParentFiber(fiber) {
|
||
var parent = fiber.return;
|
||
while (parent !== null) {
|
||
if (isHostParent(parent)) {
|
||
return parent;
|
||
}
|
||
parent = parent.return;
|
||
}
|
||
reactProdInvariant('160');
|
||
}
|
||
|
||
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.
|
||
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 !== DehydratedSuspenseComponent) {
|
||
// 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.effectTag & 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.effectTag & 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.
|
||
var parent = void 0;
|
||
var isContainer = void 0;
|
||
|
||
switch (parentFiber.tag) {
|
||
case HostComponent:
|
||
parent = parentFiber.stateNode;
|
||
isContainer = false;
|
||
break;
|
||
case HostRoot:
|
||
parent = parentFiber.stateNode.containerInfo;
|
||
isContainer = true;
|
||
break;
|
||
case HostPortal:
|
||
parent = parentFiber.stateNode.containerInfo;
|
||
isContainer = true;
|
||
break;
|
||
default:
|
||
reactProdInvariant('161');
|
||
}
|
||
if (parentFiber.effectTag & ContentReset) {
|
||
// Reset the text content of the parent before doing any insertions
|
||
parentFiber.effectTag &= ~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.
|
||
var node = finishedWork;
|
||
while (true) {
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
if (before) {
|
||
if (isContainer) {
|
||
insertInContainerBefore(parent, node.stateNode, before);
|
||
} else {
|
||
insertBefore(parent, node.stateNode, before);
|
||
}
|
||
} else {
|
||
if (isContainer) {
|
||
appendChildToContainer(parent, node.stateNode);
|
||
} else {
|
||
appendChild(parent, node.stateNode);
|
||
}
|
||
}
|
||
} else if (node.tag === HostPortal) {
|
||
// If the insertion itself is a portal, then we don't want to traverse
|
||
// down its children. Instead, we'll get insertions from each child in
|
||
// the portal directly.
|
||
} 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;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
|
||
function unmountHostComponents(current$$1) {
|
||
// We only have the top Fiber that was deleted but we need to recurse down its
|
||
var node = current$$1;
|
||
|
||
// Each iteration, currentParent is populated with node's host parent if not
|
||
// currentParentIsValid.
|
||
var currentParentIsValid = false;
|
||
|
||
// Note: these two variables *must* always be updated together.
|
||
var currentParent = void 0;
|
||
var currentParentIsContainer = void 0;
|
||
|
||
while (true) {
|
||
if (!currentParentIsValid) {
|
||
var parent = node.return;
|
||
findParent: while (true) {
|
||
!(parent !== null) ? reactProdInvariant('160') : void 0;
|
||
switch (parent.tag) {
|
||
case HostComponent:
|
||
currentParent = parent.stateNode;
|
||
currentParentIsContainer = false;
|
||
break findParent;
|
||
case HostRoot:
|
||
currentParent = parent.stateNode.containerInfo;
|
||
currentParentIsContainer = true;
|
||
break findParent;
|
||
case HostPortal:
|
||
currentParent = parent.stateNode.containerInfo;
|
||
currentParentIsContainer = true;
|
||
break findParent;
|
||
}
|
||
parent = parent.return;
|
||
}
|
||
currentParentIsValid = true;
|
||
}
|
||
|
||
if (node.tag === HostComponent || node.tag === HostText) {
|
||
commitNestedUnmounts(node);
|
||
// After all the children have unmounted, it is now safe to remove the
|
||
// node from the tree.
|
||
if (currentParentIsContainer) {
|
||
removeChildFromContainer(currentParent, node.stateNode);
|
||
} else {
|
||
removeChild(currentParent, node.stateNode);
|
||
}
|
||
// Don't visit children because we already visited them.
|
||
} else if (enableSuspenseServerRenderer && node.tag === DehydratedSuspenseComponent) {
|
||
// Delete the dehydrated suspense boundary and all of its content.
|
||
if (currentParentIsContainer) {
|
||
clearSuspenseBoundaryFromContainer(currentParent, node.stateNode);
|
||
} else {
|
||
clearSuspenseBoundary(currentParent, node.stateNode);
|
||
}
|
||
} else if (node.tag === HostPortal) {
|
||
if (node.child !== null) {
|
||
// When we go into a portal, it becomes the parent to remove from.
|
||
// We will reassign it back when we pop the portal on the way up.
|
||
currentParent = node.stateNode.containerInfo;
|
||
currentParentIsContainer = true;
|
||
// Visit children because portals might contain host components.
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
} else {
|
||
commitUnmount(node);
|
||
// Visit children because we may find more host components below.
|
||
if (node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
}
|
||
if (node === current$$1) {
|
||
return;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === null || node.return === current$$1) {
|
||
return;
|
||
}
|
||
node = node.return;
|
||
if (node.tag === HostPortal) {
|
||
// When we go out of the portal, we need to restore the parent.
|
||
// Since we don't keep a stack of them, we will search for it.
|
||
currentParentIsValid = false;
|
||
}
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
}
|
||
|
||
function commitDeletion(current$$1) {
|
||
if (supportsMutation) {
|
||
// Recursively delete all host nodes from the parent.
|
||
// Detach refs and call componentWillUnmount() on the whole subtree.
|
||
unmountHostComponents(current$$1);
|
||
} else {
|
||
// Detach refs and call componentWillUnmount() on the whole subtree.
|
||
commitNestedUnmounts(current$$1);
|
||
}
|
||
detachFiber(current$$1);
|
||
}
|
||
|
||
function commitWork(current$$1, finishedWork) {
|
||
if (!supportsMutation) {
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
// Note: We currently never use MountMutation, but useLayout uses
|
||
// UnmountMutation.
|
||
commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
|
||
return;
|
||
}
|
||
}
|
||
|
||
commitContainer(finishedWork);
|
||
return;
|
||
}
|
||
|
||
switch (finishedWork.tag) {
|
||
case FunctionComponent:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case SimpleMemoComponent:
|
||
{
|
||
// Note: We currently never use MountMutation, but useLayout uses
|
||
// UnmountMutation.
|
||
commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
{
|
||
return;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
var instance = finishedWork.stateNode;
|
||
if (instance != 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$$1 !== null ? current$$1.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) {
|
||
commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork);
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
case HostText:
|
||
{
|
||
!(finishedWork.stateNode !== null) ? reactProdInvariant('162') : void 0;
|
||
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$$1 !== null ? current$$1.memoizedProps : newText;
|
||
commitTextUpdate(textInstance, oldText, newText);
|
||
return;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
return;
|
||
}
|
||
case Profiler:
|
||
{
|
||
return;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
var newState = finishedWork.memoizedState;
|
||
|
||
var newDidTimeout = void 0;
|
||
var primaryChildParent = finishedWork;
|
||
if (newState === null) {
|
||
newDidTimeout = false;
|
||
} else {
|
||
newDidTimeout = true;
|
||
primaryChildParent = finishedWork.child;
|
||
if (newState.timedOutAt === NoWork) {
|
||
// If the children had not already timed out, record the time.
|
||
// This is used to compute the elapsed time during subsequent
|
||
// attempts to render the children.
|
||
newState.timedOutAt = requestCurrentTime();
|
||
}
|
||
}
|
||
|
||
if (primaryChildParent !== null) {
|
||
hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
|
||
}
|
||
|
||
// If this boundary just timed out, then it will have a set of thenables.
|
||
// For each thenable, attach a listener so that when it resolves, React
|
||
// attempts to re-render the boundary in the primary (pre-timeout) state.
|
||
var thenables = finishedWork.updateQueue;
|
||
if (thenables !== null) {
|
||
finishedWork.updateQueue = null;
|
||
var retryCache = finishedWork.stateNode;
|
||
if (retryCache === null) {
|
||
retryCache = finishedWork.stateNode = new PossiblyWeakSet$1();
|
||
}
|
||
thenables.forEach(function (thenable) {
|
||
// Memoize using the boundary fiber to prevent redundant listeners.
|
||
var retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
|
||
if (enableSchedulerTracing) {
|
||
retry = unstable_wrap(retry);
|
||
}
|
||
if (!retryCache.has(thenable)) {
|
||
retryCache.add(thenable);
|
||
thenable.then(retry, retry);
|
||
}
|
||
});
|
||
}
|
||
|
||
return;
|
||
}
|
||
case IncompleteClassComponent:
|
||
{
|
||
return;
|
||
}
|
||
default:
|
||
{
|
||
reactProdInvariant('163');
|
||
}
|
||
}
|
||
}
|
||
|
||
function commitResetTextContent(current$$1) {
|
||
if (!supportsMutation) {
|
||
return;
|
||
}
|
||
resetTextContent(current$$1.stateNode);
|
||
}
|
||
|
||
var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
||
var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
||
|
||
function createRootErrorUpdate(fiber, errorInfo, expirationTime) {
|
||
var update = createUpdate(expirationTime);
|
||
// 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);
|
||
logError(fiber, errorInfo);
|
||
};
|
||
return update;
|
||
}
|
||
|
||
function createClassErrorUpdate(fiber, errorInfo, expirationTime) {
|
||
var update = createUpdate(expirationTime);
|
||
update.tag = CaptureUpdate;
|
||
var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
|
||
if (typeof getDerivedStateFromError === 'function') {
|
||
var error = errorInfo.value;
|
||
update.payload = function () {
|
||
return getDerivedStateFromError(error);
|
||
};
|
||
}
|
||
|
||
var inst = fiber.stateNode;
|
||
if (inst !== null && typeof inst.componentDidCatch === 'function') {
|
||
update.callback = function callback() {
|
||
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 = errorInfo.value;
|
||
var stack = errorInfo.stack;
|
||
logError(fiber, errorInfo);
|
||
this.componentDidCatch(error, {
|
||
componentStack: stack !== null ? stack : ''
|
||
});
|
||
|
||
};
|
||
}
|
||
return update;
|
||
}
|
||
|
||
function attachPingListener(root, renderExpirationTime, thenable) {
|
||
// Attach a listener to the promise to "ping" the root and retry. But
|
||
// only if one does not already exist for the current render expiration
|
||
// time (which acts like a "thread ID" here).
|
||
var pingCache = root.pingCache;
|
||
var threadIDs = void 0;
|
||
if (pingCache === null) {
|
||
pingCache = root.pingCache = new PossiblyWeakMap();
|
||
threadIDs = new Set();
|
||
pingCache.set(thenable, threadIDs);
|
||
} else {
|
||
threadIDs = pingCache.get(thenable);
|
||
if (threadIDs === undefined) {
|
||
threadIDs = new Set();
|
||
pingCache.set(thenable, threadIDs);
|
||
}
|
||
}
|
||
if (!threadIDs.has(renderExpirationTime)) {
|
||
// Memoize using the thread ID to prevent redundant listeners.
|
||
threadIDs.add(renderExpirationTime);
|
||
var ping = pingSuspendedRoot.bind(null, root, thenable, renderExpirationTime);
|
||
if (enableSchedulerTracing) {
|
||
ping = unstable_wrap(ping);
|
||
}
|
||
thenable.then(ping, ping);
|
||
}
|
||
}
|
||
|
||
function throwException(root, returnFiber, sourceFiber, value, renderExpirationTime) {
|
||
// The source fiber did not complete.
|
||
sourceFiber.effectTag |= Incomplete;
|
||
// Its effect list is no longer valid.
|
||
sourceFiber.firstEffect = sourceFiber.lastEffect = null;
|
||
|
||
if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
|
||
// This is a thenable.
|
||
var thenable = value;
|
||
|
||
// Find the earliest timeout threshold of all the placeholders in the
|
||
// ancestor path. We could avoid this traversal by storing the thresholds on
|
||
// the stack, but we choose not to because we only hit this path if we're
|
||
// IO-bound (i.e. if something suspends). Whereas the stack is used even in
|
||
// the non-IO- bound case.
|
||
var _workInProgress = returnFiber;
|
||
var earliestTimeoutMs = -1;
|
||
var startTimeMs = -1;
|
||
do {
|
||
if (_workInProgress.tag === SuspenseComponent) {
|
||
var current$$1 = _workInProgress.alternate;
|
||
if (current$$1 !== null) {
|
||
var currentState = current$$1.memoizedState;
|
||
if (currentState !== null) {
|
||
// Reached a boundary that already timed out. Do not search
|
||
// any further.
|
||
var timedOutAt = currentState.timedOutAt;
|
||
startTimeMs = expirationTimeToMs(timedOutAt);
|
||
// Do not search any further.
|
||
break;
|
||
}
|
||
}
|
||
var timeoutPropMs = _workInProgress.pendingProps.maxDuration;
|
||
if (typeof timeoutPropMs === 'number') {
|
||
if (timeoutPropMs <= 0) {
|
||
earliestTimeoutMs = 0;
|
||
} else if (earliestTimeoutMs === -1 || timeoutPropMs < earliestTimeoutMs) {
|
||
earliestTimeoutMs = timeoutPropMs;
|
||
}
|
||
}
|
||
}
|
||
// If there is a DehydratedSuspenseComponent we don't have to do anything because
|
||
// if something suspends inside it, we will simply leave that as dehydrated. It
|
||
// will never timeout.
|
||
_workInProgress = _workInProgress.return;
|
||
} while (_workInProgress !== null);
|
||
|
||
// Schedule the nearest Suspense to re-render the timed out view.
|
||
_workInProgress = returnFiber;
|
||
do {
|
||
if (_workInProgress.tag === SuspenseComponent && shouldCaptureSuspense(_workInProgress)) {
|
||
// Found the nearest boundary.
|
||
|
||
// Stash the promise on the boundary fiber. If the boundary times out, we'll
|
||
var thenables = _workInProgress.updateQueue;
|
||
if (thenables === null) {
|
||
var updateQueue = new Set();
|
||
updateQueue.add(thenable);
|
||
_workInProgress.updateQueue = updateQueue;
|
||
} else {
|
||
thenables.add(thenable);
|
||
}
|
||
|
||
// If the boundary is outside of concurrent mode, we should *not*
|
||
// suspend the commit. Pretend as if the suspended component rendered
|
||
// null and keep rendering. In the commit phase, we'll schedule a
|
||
// subsequent synchronous update to re-render the Suspense.
|
||
//
|
||
// Note: It doesn't matter whether the component that suspended was
|
||
// inside a concurrent mode tree. If the Suspense is outside of it, we
|
||
// should *not* suspend the commit.
|
||
if ((_workInProgress.mode & ConcurrentMode) === NoEffect) {
|
||
_workInProgress.effectTag |= DidCapture;
|
||
|
||
// 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.effectTag &= ~(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 updte to
|
||
// prevent a bail out.
|
||
var update = createUpdate(Sync);
|
||
update.tag = ForceUpdate;
|
||
enqueueUpdate(sourceFiber, update);
|
||
}
|
||
}
|
||
|
||
// The source fiber did not complete. Mark it with Sync priority to
|
||
// indicate that it still has pending work.
|
||
sourceFiber.expirationTime = Sync;
|
||
|
||
// Exit without suspending.
|
||
return;
|
||
}
|
||
|
||
// Confirmed that the boundary is in a concurrent mode tree. Continue
|
||
// with the normal suspend path.
|
||
|
||
attachPingListener(root, renderExpirationTime, thenable);
|
||
|
||
var absoluteTimeoutMs = void 0;
|
||
if (earliestTimeoutMs === -1) {
|
||
// If no explicit threshold is given, default to an arbitrarily large
|
||
// value. The actual size doesn't matter because the threshold for the
|
||
// whole tree will be clamped to the expiration time.
|
||
absoluteTimeoutMs = maxSigned31BitInt;
|
||
} else {
|
||
if (startTimeMs === -1) {
|
||
// This suspend happened outside of any already timed-out
|
||
// placeholders. We don't know exactly when the update was
|
||
// scheduled, but we can infer an approximate start time from the
|
||
// expiration time. First, find the earliest uncommitted expiration
|
||
// time in the tree, including work that is suspended. Then subtract
|
||
// the offset used to compute an async update's expiration time.
|
||
// This will cause high priority (interactive) work to expire
|
||
// earlier than necessary, but we can account for this by adjusting
|
||
// for the Just Noticeable Difference.
|
||
var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, renderExpirationTime);
|
||
var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
|
||
startTimeMs = earliestExpirationTimeMs - LOW_PRIORITY_EXPIRATION;
|
||
}
|
||
absoluteTimeoutMs = startTimeMs + earliestTimeoutMs;
|
||
}
|
||
|
||
// Mark the earliest timeout in the suspended fiber's ancestor path.
|
||
// After completing the root, we'll take the largest of all the
|
||
// suspended fiber's timeouts and use it to compute a timeout for the
|
||
// whole tree.
|
||
renderDidSuspend(root, absoluteTimeoutMs, renderExpirationTime);
|
||
|
||
_workInProgress.effectTag |= ShouldCapture;
|
||
_workInProgress.expirationTime = renderExpirationTime;
|
||
return;
|
||
} else if (enableSuspenseServerRenderer && _workInProgress.tag === DehydratedSuspenseComponent) {
|
||
attachPingListener(root, renderExpirationTime, thenable);
|
||
|
||
// Since we already have a current fiber, we can eagerly add a retry listener.
|
||
var retryCache = _workInProgress.memoizedState;
|
||
if (retryCache === null) {
|
||
retryCache = _workInProgress.memoizedState = new PossiblyWeakSet();
|
||
var _current = _workInProgress.alternate;
|
||
!_current ? reactProdInvariant('319') : void 0;
|
||
_current.memoizedState = retryCache;
|
||
}
|
||
// Memoize using the boundary fiber to prevent redundant listeners.
|
||
if (!retryCache.has(thenable)) {
|
||
retryCache.add(thenable);
|
||
var retry = retryTimedOutBoundary.bind(null, _workInProgress, thenable);
|
||
if (enableSchedulerTracing) {
|
||
retry = unstable_wrap(retry);
|
||
}
|
||
thenable.then(retry, retry);
|
||
}
|
||
_workInProgress.effectTag |= ShouldCapture;
|
||
_workInProgress.expirationTime = renderExpirationTime;
|
||
return;
|
||
}
|
||
// This boundary already captured during this render. Continue to the next
|
||
// boundary.
|
||
_workInProgress = _workInProgress.return;
|
||
} while (_workInProgress !== null);
|
||
// No boundary was found. Fallthrough to error mode.
|
||
// TODO: Use invariant so the message is stripped in prod?
|
||
value = new Error((getComponentName(sourceFiber.type) || 'A React component') + ' suspended while rendering, but no fallback UI was specified.\n' + '\n' + 'Add a <Suspense fallback=...> component higher in the tree to ' + 'provide a loading indicator or placeholder to display.' + getStackByFiberInDevAndProd(sourceFiber));
|
||
}
|
||
|
||
// 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.
|
||
renderDidError();
|
||
value = createCapturedValue(value, sourceFiber);
|
||
var workInProgress = returnFiber;
|
||
do {
|
||
switch (workInProgress.tag) {
|
||
case HostRoot:
|
||
{
|
||
var _errorInfo = value;
|
||
workInProgress.effectTag |= ShouldCapture;
|
||
workInProgress.expirationTime = renderExpirationTime;
|
||
var _update = createRootErrorUpdate(workInProgress, _errorInfo, renderExpirationTime);
|
||
enqueueCapturedUpdate(workInProgress, _update);
|
||
return;
|
||
}
|
||
case ClassComponent:
|
||
// Capture and retry
|
||
var errorInfo = value;
|
||
var ctor = workInProgress.type;
|
||
var instance = workInProgress.stateNode;
|
||
if ((workInProgress.effectTag & DidCapture) === NoEffect && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
|
||
workInProgress.effectTag |= ShouldCapture;
|
||
workInProgress.expirationTime = renderExpirationTime;
|
||
// Schedule the error boundary to re-render using updated state
|
||
var _update2 = createClassErrorUpdate(workInProgress, errorInfo, renderExpirationTime);
|
||
enqueueCapturedUpdate(workInProgress, _update2);
|
||
return;
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
workInProgress = workInProgress.return;
|
||
} while (workInProgress !== null);
|
||
}
|
||
|
||
function unwindWork(workInProgress, renderExpirationTime) {
|
||
switch (workInProgress.tag) {
|
||
case ClassComponent:
|
||
{
|
||
var Component = workInProgress.type;
|
||
if (isContextProvider(Component)) {
|
||
popContext(workInProgress);
|
||
}
|
||
var effectTag = workInProgress.effectTag;
|
||
if (effectTag & ShouldCapture) {
|
||
workInProgress.effectTag = effectTag & ~ShouldCapture | DidCapture;
|
||
return workInProgress;
|
||
}
|
||
return null;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
popHostContainer(workInProgress);
|
||
popTopLevelContextObject(workInProgress);
|
||
var _effectTag = workInProgress.effectTag;
|
||
!((_effectTag & DidCapture) === NoEffect) ? reactProdInvariant('285') : void 0;
|
||
workInProgress.effectTag = _effectTag & ~ShouldCapture | DidCapture;
|
||
return workInProgress;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
// TODO: popHydrationState
|
||
popHostContext(workInProgress);
|
||
return null;
|
||
}
|
||
case SuspenseComponent:
|
||
{
|
||
var _effectTag2 = workInProgress.effectTag;
|
||
if (_effectTag2 & ShouldCapture) {
|
||
workInProgress.effectTag = _effectTag2 & ~ShouldCapture | DidCapture;
|
||
// Captured a suspense effect. Re-render the boundary.
|
||
return workInProgress;
|
||
}
|
||
return null;
|
||
}
|
||
case DehydratedSuspenseComponent:
|
||
{
|
||
if (enableSuspenseServerRenderer) {
|
||
// TODO: popHydrationState
|
||
var _effectTag3 = workInProgress.effectTag;
|
||
if (_effectTag3 & ShouldCapture) {
|
||
workInProgress.effectTag = _effectTag3 & ~ShouldCapture | DidCapture;
|
||
// Captured a suspense effect. Re-render the boundary.
|
||
return workInProgress;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
case HostPortal:
|
||
popHostContainer(workInProgress);
|
||
return null;
|
||
case ContextProvider:
|
||
popProvider(workInProgress);
|
||
return null;
|
||
default:
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function unwindInterruptedWork(interruptedWork) {
|
||
switch (interruptedWork.tag) {
|
||
case ClassComponent:
|
||
{
|
||
var childContextTypes = interruptedWork.type.childContextTypes;
|
||
if (childContextTypes !== null && childContextTypes !== undefined) {
|
||
popContext(interruptedWork);
|
||
}
|
||
break;
|
||
}
|
||
case HostRoot:
|
||
{
|
||
popHostContainer(interruptedWork);
|
||
popTopLevelContextObject(interruptedWork);
|
||
break;
|
||
}
|
||
case HostComponent:
|
||
{
|
||
popHostContext(interruptedWork);
|
||
break;
|
||
}
|
||
case HostPortal:
|
||
popHostContainer(interruptedWork);
|
||
break;
|
||
case ContextProvider:
|
||
popProvider(interruptedWork);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||
var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
|
||
|
||
|
||
if (enableSchedulerTracing) {
|
||
// Provide explicit error message when production+profiling bundle of e.g. react-dom
|
||
// is used with production (non-profiling) bundle of scheduler/tracing
|
||
!(__interactionsRef != null && __interactionsRef.current != null) ? reactProdInvariant('302') : void 0;
|
||
}
|
||
|
||
var isWorking = false;
|
||
|
||
// The next work in progress fiber that we're currently working on.
|
||
var nextUnitOfWork = null;
|
||
var nextRoot = null;
|
||
// The time at which we're currently rendering work.
|
||
var nextRenderExpirationTime = NoWork;
|
||
var nextLatestAbsoluteTimeoutMs = -1;
|
||
var nextRenderDidError = false;
|
||
|
||
// The next fiber with an effect that we're currently committing.
|
||
var nextEffect = null;
|
||
|
||
var isCommitting$1 = false;
|
||
var rootWithPendingPassiveEffects = null;
|
||
var passiveEffectCallbackHandle = null;
|
||
var passiveEffectCallback = null;
|
||
|
||
var legacyErrorBoundariesThatAlreadyFailed = null;
|
||
|
||
// Used for performance tracking.
|
||
var interruptedBy = null;
|
||
|
||
function resetStack() {
|
||
if (nextUnitOfWork !== null) {
|
||
var interruptedWork = nextUnitOfWork.return;
|
||
while (interruptedWork !== null) {
|
||
unwindInterruptedWork(interruptedWork);
|
||
interruptedWork = interruptedWork.return;
|
||
}
|
||
}
|
||
|
||
nextRoot = null;
|
||
nextRenderExpirationTime = NoWork;
|
||
nextLatestAbsoluteTimeoutMs = -1;
|
||
nextRenderDidError = false;
|
||
nextUnitOfWork = null;
|
||
}
|
||
|
||
function commitAllHostEffects() {
|
||
while (nextEffect !== null) {
|
||
recordEffect();
|
||
|
||
var effectTag = nextEffect.effectTag;
|
||
|
||
if (effectTag & ContentReset) {
|
||
commitResetTextContent(nextEffect);
|
||
}
|
||
|
||
if (effectTag & Ref) {
|
||
var current$$1 = nextEffect.alternate;
|
||
if (current$$1 !== null) {
|
||
commitDetachRef(current$$1);
|
||
}
|
||
}
|
||
|
||
// The following switch statement is only concerned about placement,
|
||
// updates, and deletions. To avoid needing to add a case for every
|
||
// possible bitmap value, we remove the secondary effects from the
|
||
// effect tag and switch on that value.
|
||
var primaryEffectTag = effectTag & (Placement | Update | Deletion);
|
||
switch (primaryEffectTag) {
|
||
case Placement:
|
||
{
|
||
commitPlacement(nextEffect);
|
||
// 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.
|
||
nextEffect.effectTag &= ~Placement;
|
||
break;
|
||
}
|
||
case PlacementAndUpdate:
|
||
{
|
||
// Placement
|
||
commitPlacement(nextEffect);
|
||
// Clear the "placement" from effect tag so that we know that this is inserted, before
|
||
// any life-cycles like componentDidMount gets called.
|
||
nextEffect.effectTag &= ~Placement;
|
||
|
||
// Update
|
||
var _current = nextEffect.alternate;
|
||
commitWork(_current, nextEffect);
|
||
break;
|
||
}
|
||
case Update:
|
||
{
|
||
var _current2 = nextEffect.alternate;
|
||
commitWork(_current2, nextEffect);
|
||
break;
|
||
}
|
||
case Deletion:
|
||
{
|
||
commitDeletion(nextEffect);
|
||
break;
|
||
}
|
||
}
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
function commitBeforeMutationLifecycles() {
|
||
while (nextEffect !== null) {
|
||
var effectTag = nextEffect.effectTag;
|
||
if (effectTag & Snapshot) {
|
||
recordEffect();
|
||
var current$$1 = nextEffect.alternate;
|
||
commitBeforeMutationLifeCycles(current$$1, nextEffect);
|
||
}
|
||
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
function commitAllLifeCycles(finishedRoot, committedExpirationTime) {
|
||
while (nextEffect !== null) {
|
||
var effectTag = nextEffect.effectTag;
|
||
|
||
if (effectTag & (Update | Callback)) {
|
||
recordEffect();
|
||
var current$$1 = nextEffect.alternate;
|
||
commitLifeCycles(finishedRoot, current$$1, nextEffect, committedExpirationTime);
|
||
}
|
||
|
||
if (effectTag & Ref) {
|
||
recordEffect();
|
||
commitAttachRef(nextEffect);
|
||
}
|
||
|
||
if (effectTag & Passive) {
|
||
rootWithPendingPassiveEffects = finishedRoot;
|
||
}
|
||
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
|
||
}
|
||
|
||
function commitPassiveEffects(root, firstEffect) {
|
||
rootWithPendingPassiveEffects = null;
|
||
passiveEffectCallbackHandle = null;
|
||
passiveEffectCallback = null;
|
||
|
||
// Set this to true to prevent re-entrancy
|
||
var previousIsRendering = isRendering;
|
||
isRendering = true;
|
||
|
||
var effect = firstEffect;
|
||
do {
|
||
if (effect.effectTag & Passive) {
|
||
var didError = false;
|
||
var error = void 0;
|
||
{
|
||
try {
|
||
commitPassiveHookEffects(effect);
|
||
} catch (e) {
|
||
didError = true;
|
||
error = e;
|
||
}
|
||
}
|
||
if (didError) {
|
||
captureCommitPhaseError(effect, error);
|
||
}
|
||
}
|
||
effect = effect.nextEffect;
|
||
} while (effect !== null);
|
||
isRendering = previousIsRendering;
|
||
|
||
// Check if work was scheduled by one of the effects
|
||
var rootExpirationTime = root.expirationTime;
|
||
if (rootExpirationTime !== NoWork) {
|
||
requestWork(root, rootExpirationTime);
|
||
}
|
||
// Flush any sync work that was scheduled by effects
|
||
if (!isBatchingUpdates && !isRendering) {
|
||
performSyncWork();
|
||
}
|
||
}
|
||
|
||
function isAlreadyFailedLegacyErrorBoundary(instance) {
|
||
return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
|
||
}
|
||
|
||
function markLegacyErrorBoundaryAsFailed(instance) {
|
||
if (legacyErrorBoundariesThatAlreadyFailed === null) {
|
||
legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
|
||
} else {
|
||
legacyErrorBoundariesThatAlreadyFailed.add(instance);
|
||
}
|
||
}
|
||
|
||
function flushPassiveEffects$1() {
|
||
if (passiveEffectCallbackHandle !== null) {
|
||
cancelPassiveEffects(passiveEffectCallbackHandle);
|
||
}
|
||
if (passiveEffectCallback !== null) {
|
||
// We call the scheduled callback instead of commitPassiveEffects directly
|
||
// to ensure tracing works correctly.
|
||
passiveEffectCallback();
|
||
}
|
||
}
|
||
|
||
function commitRoot(root, finishedWork) {
|
||
isWorking = true;
|
||
isCommitting$1 = true;
|
||
startCommitTimer();
|
||
|
||
!(root.current !== finishedWork) ? reactProdInvariant('177') : void 0;
|
||
var committedExpirationTime = root.pendingCommitExpirationTime;
|
||
!(committedExpirationTime !== NoWork) ? reactProdInvariant('261') : void 0;
|
||
root.pendingCommitExpirationTime = NoWork;
|
||
|
||
// Update the pending priority levels to account for the work that we are
|
||
// about to commit. This needs to happen before calling the lifecycles, since
|
||
// they may schedule additional updates.
|
||
var updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
|
||
var childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
|
||
var earliestRemainingTimeBeforeCommit = childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit ? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
|
||
markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
|
||
|
||
var prevInteractions = null;
|
||
if (enableSchedulerTracing) {
|
||
// Restore any pending interactions at this point,
|
||
// So that cascading work triggered during the render phase will be accounted for.
|
||
prevInteractions = __interactionsRef.current;
|
||
__interactionsRef.current = root.memoizedInteractions;
|
||
}
|
||
|
||
// Reset this to null before calling lifecycles
|
||
ReactCurrentOwner$1.current = null;
|
||
|
||
var firstEffect = void 0;
|
||
if (finishedWork.effectTag > PerformedWork) {
|
||
// A fiber's effect list consists only of its children, not itself. So if
|
||
// the root has an effect, we need to add it to the end of the list. The
|
||
// resulting list is the set that would belong to the root's parent, if
|
||
// it had one; that is, all the effects in the tree including the root.
|
||
if (finishedWork.lastEffect !== null) {
|
||
finishedWork.lastEffect.nextEffect = finishedWork;
|
||
firstEffect = finishedWork.firstEffect;
|
||
} else {
|
||
firstEffect = finishedWork;
|
||
}
|
||
} else {
|
||
// There is no effect on the root.
|
||
firstEffect = finishedWork.firstEffect;
|
||
}
|
||
|
||
prepareForCommit(root.containerInfo);
|
||
|
||
// Invoke instances of getSnapshotBeforeUpdate before mutation.
|
||
nextEffect = firstEffect;
|
||
startCommitSnapshotEffectsTimer();
|
||
while (nextEffect !== null) {
|
||
var didError = false;
|
||
var error = void 0;
|
||
{
|
||
try {
|
||
commitBeforeMutationLifecycles();
|
||
} catch (e) {
|
||
didError = true;
|
||
error = e;
|
||
}
|
||
}
|
||
if (didError) {
|
||
!(nextEffect !== null) ? reactProdInvariant('178') : void 0;
|
||
captureCommitPhaseError(nextEffect, error);
|
||
// Clean-up
|
||
if (nextEffect !== null) {
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
}
|
||
}
|
||
stopCommitSnapshotEffectsTimer();
|
||
|
||
if (enableProfilerTimer) {
|
||
// Mark the current commit time to be shared by all Profilers in this batch.
|
||
// This enables them to be grouped later.
|
||
recordCommitTime();
|
||
}
|
||
|
||
// Commit all the side-effects within a tree. We'll do this in two passes.
|
||
// The first pass performs all the host insertions, updates, deletions and
|
||
// ref unmounts.
|
||
nextEffect = firstEffect;
|
||
startCommitHostEffectsTimer();
|
||
while (nextEffect !== null) {
|
||
var _didError = false;
|
||
var _error = void 0;
|
||
{
|
||
try {
|
||
commitAllHostEffects();
|
||
} catch (e) {
|
||
_didError = true;
|
||
_error = e;
|
||
}
|
||
}
|
||
if (_didError) {
|
||
!(nextEffect !== null) ? reactProdInvariant('178') : void 0;
|
||
captureCommitPhaseError(nextEffect, _error);
|
||
// Clean-up
|
||
if (nextEffect !== null) {
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
}
|
||
}
|
||
stopCommitHostEffectsTimer();
|
||
|
||
resetAfterCommit(root.containerInfo);
|
||
|
||
// The work-in-progress tree is now the current tree. This must come after
|
||
// the first pass of the commit phase, so that the previous tree is still
|
||
// current during componentWillUnmount, but before the second pass, so that
|
||
// the finished work is current during componentDidMount/Update.
|
||
root.current = finishedWork;
|
||
|
||
// In the second pass we'll perform all life-cycles and ref callbacks.
|
||
// Life-cycles happen as a separate pass so that all placements, updates,
|
||
// and deletions in the entire tree have already been invoked.
|
||
// This pass also triggers any renderer-specific initial effects.
|
||
nextEffect = firstEffect;
|
||
startCommitLifeCyclesTimer();
|
||
while (nextEffect !== null) {
|
||
var _didError2 = false;
|
||
var _error2 = void 0;
|
||
{
|
||
try {
|
||
commitAllLifeCycles(root, committedExpirationTime);
|
||
} catch (e) {
|
||
_didError2 = true;
|
||
_error2 = e;
|
||
}
|
||
}
|
||
if (_didError2) {
|
||
!(nextEffect !== null) ? reactProdInvariant('178') : void 0;
|
||
captureCommitPhaseError(nextEffect, _error2);
|
||
if (nextEffect !== null) {
|
||
nextEffect = nextEffect.nextEffect;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (firstEffect !== null && rootWithPendingPassiveEffects !== null) {
|
||
// This commit included a passive effect. These do not need to fire until
|
||
// after the next paint. Schedule an callback to fire them in an async
|
||
// event. To ensure serial execution, the callback will be flushed early if
|
||
// we enter rootWithPendingPassiveEffects commit phase before then.
|
||
var callback = commitPassiveEffects.bind(null, root, firstEffect);
|
||
if (enableSchedulerTracing) {
|
||
// TODO: Avoid this extra callback by mutating the tracing ref directly,
|
||
// like we do at the beginning of commitRoot. I've opted not to do that
|
||
// here because that code is still in flux.
|
||
callback = unstable_wrap(callback);
|
||
}
|
||
passiveEffectCallbackHandle = unstable_runWithPriority(unstable_NormalPriority, function () {
|
||
return schedulePassiveEffects(callback);
|
||
});
|
||
passiveEffectCallback = callback;
|
||
}
|
||
|
||
isCommitting$1 = false;
|
||
isWorking = false;
|
||
stopCommitLifeCyclesTimer();
|
||
stopCommitTimer();
|
||
onCommitRoot(finishedWork.stateNode);
|
||
var updateExpirationTimeAfterCommit = finishedWork.expirationTime;
|
||
var childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
|
||
var earliestRemainingTimeAfterCommit = childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit ? childExpirationTimeAfterCommit : updateExpirationTimeAfterCommit;
|
||
if (earliestRemainingTimeAfterCommit === NoWork) {
|
||
// If there's no remaining work, we can clear the set of already failed
|
||
// error boundaries.
|
||
legacyErrorBoundariesThatAlreadyFailed = null;
|
||
}
|
||
onCommit(root, earliestRemainingTimeAfterCommit);
|
||
|
||
if (enableSchedulerTracing) {
|
||
__interactionsRef.current = prevInteractions;
|
||
|
||
var subscriber = void 0;
|
||
|
||
try {
|
||
subscriber = __subscriberRef.current;
|
||
if (subscriber !== null && root.memoizedInteractions.size > 0) {
|
||
var threadID = computeThreadID(committedExpirationTime, root.interactionThreadID);
|
||
subscriber.onWorkStopped(root.memoizedInteractions, threadID);
|
||
}
|
||
} catch (error) {
|
||
// It's not safe for commitRoot() to throw.
|
||
// Store the error for now and we'll re-throw in finishRendering().
|
||
if (!hasUnhandledError) {
|
||
hasUnhandledError = true;
|
||
unhandledError = error;
|
||
}
|
||
} finally {
|
||
// Clear completed interactions from the pending Map.
|
||
// Unless the render was suspended or cascading work was scheduled,
|
||
// In which case– leave pending interactions until the subsequent render.
|
||
var pendingInteractionMap = root.pendingInteractionMap;
|
||
pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
|
||
// Only decrement the pending interaction count if we're done.
|
||
// If there's still work at the current priority,
|
||
// That indicates that we are waiting for suspense data.
|
||
if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) {
|
||
pendingInteractionMap.delete(scheduledExpirationTime);
|
||
|
||
scheduledInteractions.forEach(function (interaction) {
|
||
interaction.__count--;
|
||
|
||
if (subscriber !== null && interaction.__count === 0) {
|
||
try {
|
||
subscriber.onInteractionScheduledWorkCompleted(interaction);
|
||
} catch (error) {
|
||
// It's not safe for commitRoot() to throw.
|
||
// Store the error for now and we'll re-throw in finishRendering().
|
||
if (!hasUnhandledError) {
|
||
hasUnhandledError = true;
|
||
unhandledError = error;
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
|
||
function resetChildExpirationTime(workInProgress, renderTime) {
|
||
if (renderTime !== Never && workInProgress.childExpirationTime === Never) {
|
||
// The children of this component are hidden. Don't bubble their
|
||
// expiration times.
|
||
return;
|
||
}
|
||
|
||
var newChildExpirationTime = NoWork;
|
||
|
||
// Bubble up the earliest expiration time.
|
||
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
|
||
// We're in profiling mode.
|
||
// Let's use this same traversal to update the render durations.
|
||
var actualDuration = workInProgress.actualDuration;
|
||
var treeBaseDuration = workInProgress.selfBaseDuration;
|
||
|
||
// 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.
|
||
var shouldBubbleActualDurations = workInProgress.alternate === null || workInProgress.child !== workInProgress.alternate.child;
|
||
|
||
var child = workInProgress.child;
|
||
while (child !== null) {
|
||
var childUpdateExpirationTime = child.expirationTime;
|
||
var childChildExpirationTime = child.childExpirationTime;
|
||
if (childUpdateExpirationTime > newChildExpirationTime) {
|
||
newChildExpirationTime = childUpdateExpirationTime;
|
||
}
|
||
if (childChildExpirationTime > newChildExpirationTime) {
|
||
newChildExpirationTime = childChildExpirationTime;
|
||
}
|
||
if (shouldBubbleActualDurations) {
|
||
actualDuration += child.actualDuration;
|
||
}
|
||
treeBaseDuration += child.treeBaseDuration;
|
||
child = child.sibling;
|
||
}
|
||
workInProgress.actualDuration = actualDuration;
|
||
workInProgress.treeBaseDuration = treeBaseDuration;
|
||
} else {
|
||
var _child = workInProgress.child;
|
||
while (_child !== null) {
|
||
var _childUpdateExpirationTime = _child.expirationTime;
|
||
var _childChildExpirationTime = _child.childExpirationTime;
|
||
if (_childUpdateExpirationTime > newChildExpirationTime) {
|
||
newChildExpirationTime = _childUpdateExpirationTime;
|
||
}
|
||
if (_childChildExpirationTime > newChildExpirationTime) {
|
||
newChildExpirationTime = _childChildExpirationTime;
|
||
}
|
||
_child = _child.sibling;
|
||
}
|
||
}
|
||
|
||
workInProgress.childExpirationTime = newChildExpirationTime;
|
||
}
|
||
|
||
function completeUnitOfWork(workInProgress) {
|
||
// 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.
|
||
while (true) {
|
||
// 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$$1 = workInProgress.alternate;
|
||
var returnFiber = workInProgress.return;
|
||
var siblingFiber = workInProgress.sibling;
|
||
|
||
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
|
||
nextUnitOfWork = workInProgress;
|
||
if (enableProfilerTimer) {
|
||
if (workInProgress.mode & ProfileMode) {
|
||
startProfilerTimer(workInProgress);
|
||
}
|
||
nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
|
||
if (workInProgress.mode & ProfileMode) {
|
||
// Update render duration assuming we didn't error.
|
||
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
|
||
}
|
||
} else {
|
||
nextUnitOfWork = completeWork(current$$1, workInProgress, nextRenderExpirationTime);
|
||
}
|
||
stopWorkTimer(workInProgress);
|
||
resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
|
||
if (nextUnitOfWork !== null) {
|
||
// Completing this fiber spawned new work. Work on that next.
|
||
return nextUnitOfWork;
|
||
}
|
||
|
||
if (returnFiber !== null &&
|
||
// Do not append effects to parents if a sibling failed to complete
|
||
(returnFiber.effectTag & Incomplete) === NoEffect) {
|
||
// Append all the effects of the subtree and this fiber onto the effect
|
||
// list of the parent. The completion order of the children affects the
|
||
// side-effect order.
|
||
if (returnFiber.firstEffect === null) {
|
||
returnFiber.firstEffect = workInProgress.firstEffect;
|
||
}
|
||
if (workInProgress.lastEffect !== null) {
|
||
if (returnFiber.lastEffect !== null) {
|
||
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
|
||
}
|
||
returnFiber.lastEffect = workInProgress.lastEffect;
|
||
}
|
||
|
||
// If this fiber had side-effects, we append it AFTER the children's
|
||
// side-effects. We can perform certain side-effects earlier if
|
||
// needed, by doing multiple passes over the effect list. We don't want
|
||
// to schedule our own side-effect on our own list because if end up
|
||
// reusing children we'll schedule this effect onto itself since we're
|
||
// at the end.
|
||
var effectTag = workInProgress.effectTag;
|
||
// Skip both NoWork and PerformedWork tags when creating the effect list.
|
||
// PerformedWork effect is read by React DevTools but shouldn't be committed.
|
||
if (effectTag > PerformedWork) {
|
||
if (returnFiber.lastEffect !== null) {
|
||
returnFiber.lastEffect.nextEffect = workInProgress;
|
||
} else {
|
||
returnFiber.firstEffect = workInProgress;
|
||
}
|
||
returnFiber.lastEffect = workInProgress;
|
||
}
|
||
}
|
||
|
||
if (siblingFiber !== null) {
|
||
// If there is more work to do in this returnFiber, do that next.
|
||
return siblingFiber;
|
||
} else if (returnFiber !== null) {
|
||
// If there's no more work in this returnFiber. Complete the returnFiber.
|
||
workInProgress = returnFiber;
|
||
continue;
|
||
} else {
|
||
// We've reached the root.
|
||
return null;
|
||
}
|
||
} else {
|
||
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
|
||
// Record the render duration for the fiber that errored.
|
||
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
|
||
|
||
// Include the time spent working on failed children before continuing.
|
||
var actualDuration = workInProgress.actualDuration;
|
||
var child = workInProgress.child;
|
||
while (child !== null) {
|
||
actualDuration += child.actualDuration;
|
||
child = child.sibling;
|
||
}
|
||
workInProgress.actualDuration = actualDuration;
|
||
}
|
||
|
||
// 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(workInProgress, nextRenderExpirationTime);
|
||
// Because this fiber did not complete, don't reset its expiration time.
|
||
if (workInProgress.effectTag & DidCapture) {
|
||
// Restarting an error boundary
|
||
stopFailedWorkTimer(workInProgress);
|
||
} else {
|
||
stopWorkTimer(workInProgress);
|
||
}
|
||
|
||
if (next !== null) {
|
||
stopWorkTimer(workInProgress);
|
||
next.effectTag &= HostEffectMask;
|
||
return next;
|
||
}
|
||
|
||
if (returnFiber !== null) {
|
||
// Mark the parent fiber as incomplete and clear its effect list.
|
||
returnFiber.firstEffect = returnFiber.lastEffect = null;
|
||
returnFiber.effectTag |= Incomplete;
|
||
}
|
||
|
||
if (siblingFiber !== null) {
|
||
// If there is more work to do in this returnFiber, do that next.
|
||
return siblingFiber;
|
||
} else if (returnFiber !== null) {
|
||
// If there's no more work in this returnFiber. Complete the returnFiber.
|
||
workInProgress = returnFiber;
|
||
continue;
|
||
} else {
|
||
return null;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Without this explicit null return Flow complains of invalid return type
|
||
// TODO Remove the above while(true) loop
|
||
// eslint-disable-next-line no-unreachable
|
||
return null;
|
||
}
|
||
|
||
function performUnitOfWork(workInProgress) {
|
||
// 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$$1 = workInProgress.alternate;
|
||
|
||
// See if beginning this work spawns more work.
|
||
startWorkTimer(workInProgress);
|
||
var next = void 0;
|
||
if (enableProfilerTimer) {
|
||
if (workInProgress.mode & ProfileMode) {
|
||
startProfilerTimer(workInProgress);
|
||
}
|
||
|
||
next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
|
||
workInProgress.memoizedProps = workInProgress.pendingProps;
|
||
|
||
if (workInProgress.mode & ProfileMode) {
|
||
// Record the render duration assuming we didn't bailout (or error).
|
||
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, true);
|
||
}
|
||
} else {
|
||
next = beginWork(current$$1, workInProgress, nextRenderExpirationTime);
|
||
workInProgress.memoizedProps = workInProgress.pendingProps;
|
||
}
|
||
|
||
if (next === null) {
|
||
// If this doesn't spawn new work, complete the current work.
|
||
next = completeUnitOfWork(workInProgress);
|
||
}
|
||
|
||
ReactCurrentOwner$1.current = null;
|
||
|
||
return next;
|
||
}
|
||
|
||
function workLoop(isYieldy) {
|
||
if (!isYieldy) {
|
||
// Flush work without yielding
|
||
while (nextUnitOfWork !== null) {
|
||
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
|
||
}
|
||
} else {
|
||
// Flush asynchronous work until there's a higher priority event
|
||
while (nextUnitOfWork !== null && !shouldYieldToRenderer()) {
|
||
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
|
||
}
|
||
}
|
||
}
|
||
|
||
function renderRoot(root, isYieldy) {
|
||
!!isWorking ? reactProdInvariant('243') : void 0;
|
||
|
||
flushPassiveEffects$1();
|
||
|
||
isWorking = true;
|
||
var previousDispatcher = ReactCurrentDispatcher.current;
|
||
ReactCurrentDispatcher.current = ContextOnlyDispatcher;
|
||
|
||
var expirationTime = root.nextExpirationTimeToWorkOn;
|
||
|
||
// Check if we're starting from a fresh stack, or if we're resuming from
|
||
// previously yielded work.
|
||
if (expirationTime !== nextRenderExpirationTime || root !== nextRoot || nextUnitOfWork === null) {
|
||
// Reset the stack and start working from the root.
|
||
resetStack();
|
||
nextRoot = root;
|
||
nextRenderExpirationTime = expirationTime;
|
||
nextUnitOfWork = createWorkInProgress(nextRoot.current, null, nextRenderExpirationTime);
|
||
root.pendingCommitExpirationTime = NoWork;
|
||
|
||
if (enableSchedulerTracing) {
|
||
// Determine which interactions this batch of work currently includes,
|
||
// So that we can accurately attribute time spent working on it,
|
||
var interactions = new Set();
|
||
root.pendingInteractionMap.forEach(function (scheduledInteractions, scheduledExpirationTime) {
|
||
if (scheduledExpirationTime >= expirationTime) {
|
||
scheduledInteractions.forEach(function (interaction) {
|
||
return interactions.add(interaction);
|
||
});
|
||
}
|
||
});
|
||
|
||
// Store the current set of interactions on the FiberRoot for a few reasons:
|
||
// We can re-use it in hot functions like renderRoot() without having to recalculate it.
|
||
// We will also use it in commitWork() to pass to any Profiler onRender() hooks.
|
||
// This also provides DevTools with a way to access it when the onCommitRoot() hook is called.
|
||
root.memoizedInteractions = interactions;
|
||
|
||
if (interactions.size > 0) {
|
||
var subscriber = __subscriberRef.current;
|
||
if (subscriber !== null) {
|
||
var threadID = computeThreadID(expirationTime, root.interactionThreadID);
|
||
try {
|
||
subscriber.onWorkStarted(interactions, threadID);
|
||
} catch (error) {
|
||
// Work thrown by an interaction tracing subscriber should be rethrown,
|
||
// But only once it's safe (to avoid leaving the scheduler in an invalid state).
|
||
// Store the error for now and we'll re-throw in finishRendering().
|
||
if (!hasUnhandledError) {
|
||
hasUnhandledError = true;
|
||
unhandledError = error;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
var prevInteractions = null;
|
||
if (enableSchedulerTracing) {
|
||
// We're about to start new traced work.
|
||
// Restore pending interactions so cascading work triggered during the render phase will be accounted for.
|
||
prevInteractions = __interactionsRef.current;
|
||
__interactionsRef.current = root.memoizedInteractions;
|
||
}
|
||
|
||
var didFatal = false;
|
||
|
||
startWorkLoopTimer(nextUnitOfWork);
|
||
|
||
do {
|
||
try {
|
||
workLoop(isYieldy);
|
||
} catch (thrownValue) {
|
||
resetContextDependences();
|
||
resetHooks();
|
||
|
||
// Reset in case completion throws.
|
||
// This is only used in DEV and when replaying is on.
|
||
if (nextUnitOfWork === null) {
|
||
// This is a fatal error.
|
||
didFatal = true;
|
||
onUncaughtError(thrownValue);
|
||
} else {
|
||
if (enableProfilerTimer && nextUnitOfWork.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(nextUnitOfWork, true);
|
||
}
|
||
|
||
!(nextUnitOfWork !== null) ? reactProdInvariant('271') : void 0;
|
||
|
||
var sourceFiber = nextUnitOfWork;
|
||
var returnFiber = sourceFiber.return;
|
||
if (returnFiber === null) {
|
||
// This is the root. The root could capture its own errors. However,
|
||
// we don't know if it errors before or after we pushed the host
|
||
// context. This information is needed to avoid a stack mismatch.
|
||
// Because we're not sure, treat this as a fatal error. We could track
|
||
// which phase it fails in, but doesn't seem worth it. At least
|
||
// for now.
|
||
didFatal = true;
|
||
onUncaughtError(thrownValue);
|
||
} else {
|
||
throwException(root, returnFiber, sourceFiber, thrownValue, nextRenderExpirationTime);
|
||
nextUnitOfWork = completeUnitOfWork(sourceFiber);
|
||
continue;
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
} while (true);
|
||
|
||
if (enableSchedulerTracing) {
|
||
// Traced work is done for now; restore the previous interactions.
|
||
__interactionsRef.current = prevInteractions;
|
||
}
|
||
|
||
// We're done performing work. Time to clean up.
|
||
isWorking = false;
|
||
ReactCurrentDispatcher.current = previousDispatcher;
|
||
resetContextDependences();
|
||
resetHooks();
|
||
|
||
// Yield back to main thread.
|
||
if (didFatal) {
|
||
var _didCompleteRoot = false;
|
||
stopWorkLoopTimer(interruptedBy, _didCompleteRoot);
|
||
interruptedBy = null;
|
||
// There was a fatal error.
|
||
nextRoot = null;
|
||
onFatal(root);
|
||
return;
|
||
}
|
||
|
||
if (nextUnitOfWork !== null) {
|
||
// There's still remaining async work in this tree, but we ran out of time
|
||
// in the current frame. Yield back to the renderer. Unless we're
|
||
// interrupted by a higher priority update, we'll continue later from where
|
||
// we left off.
|
||
var _didCompleteRoot2 = false;
|
||
stopWorkLoopTimer(interruptedBy, _didCompleteRoot2);
|
||
interruptedBy = null;
|
||
onYield(root);
|
||
return;
|
||
}
|
||
|
||
// We completed the whole tree.
|
||
var didCompleteRoot = true;
|
||
stopWorkLoopTimer(interruptedBy, didCompleteRoot);
|
||
var rootWorkInProgress = root.current.alternate;
|
||
!(rootWorkInProgress !== null) ? reactProdInvariant('281') : void 0;
|
||
|
||
// `nextRoot` points to the in-progress root. A non-null value indicates
|
||
// that we're in the middle of an async render. Set it to null to indicate
|
||
// there's no more work to be done in the current batch.
|
||
nextRoot = null;
|
||
interruptedBy = null;
|
||
|
||
if (nextRenderDidError) {
|
||
// There was an error
|
||
if (hasLowerPriorityWork(root, expirationTime)) {
|
||
// There's lower priority work. If so, it may have the effect of fixing
|
||
// the exception that was just thrown. Exit without committing. This is
|
||
// similar to a suspend, but without a timeout because we're not waiting
|
||
// for a promise to resolve. React will restart at the lower
|
||
// priority level.
|
||
markSuspendedPriorityLevel(root, expirationTime);
|
||
var suspendedExpirationTime = expirationTime;
|
||
var rootExpirationTime = root.expirationTime;
|
||
onSuspend(root, rootWorkInProgress, suspendedExpirationTime, rootExpirationTime, -1 // Indicates no timeout
|
||
);
|
||
return;
|
||
} else if (
|
||
// There's no lower priority work, but we're rendering asynchronously.
|
||
// Synchronously attempt to render the same level one more time. This is
|
||
// similar to a suspend, but without a timeout because we're not waiting
|
||
// for a promise to resolve.
|
||
!root.didError && isYieldy) {
|
||
root.didError = true;
|
||
var _suspendedExpirationTime = root.nextExpirationTimeToWorkOn = expirationTime;
|
||
var _rootExpirationTime = root.expirationTime = Sync;
|
||
onSuspend(root, rootWorkInProgress, _suspendedExpirationTime, _rootExpirationTime, -1 // Indicates no timeout
|
||
);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (isYieldy && nextLatestAbsoluteTimeoutMs !== -1) {
|
||
// The tree was suspended.
|
||
var _suspendedExpirationTime2 = expirationTime;
|
||
markSuspendedPriorityLevel(root, _suspendedExpirationTime2);
|
||
|
||
// Find the earliest uncommitted expiration time in the tree, including
|
||
// work that is suspended. The timeout threshold cannot be longer than
|
||
// the overall expiration.
|
||
var earliestExpirationTime = findEarliestOutstandingPriorityLevel(root, expirationTime);
|
||
var earliestExpirationTimeMs = expirationTimeToMs(earliestExpirationTime);
|
||
if (earliestExpirationTimeMs < nextLatestAbsoluteTimeoutMs) {
|
||
nextLatestAbsoluteTimeoutMs = earliestExpirationTimeMs;
|
||
}
|
||
|
||
// Subtract the current time from the absolute timeout to get the number
|
||
// of milliseconds until the timeout. In other words, convert an absolute
|
||
// timestamp to a relative time. This is the value that is passed
|
||
// to `setTimeout`.
|
||
var currentTimeMs = expirationTimeToMs(requestCurrentTime());
|
||
var msUntilTimeout = nextLatestAbsoluteTimeoutMs - currentTimeMs;
|
||
msUntilTimeout = msUntilTimeout < 0 ? 0 : msUntilTimeout;
|
||
|
||
// TODO: Account for the Just Noticeable Difference
|
||
|
||
var _rootExpirationTime2 = root.expirationTime;
|
||
onSuspend(root, rootWorkInProgress, _suspendedExpirationTime2, _rootExpirationTime2, msUntilTimeout);
|
||
return;
|
||
}
|
||
|
||
// Ready to commit.
|
||
onComplete(root, rootWorkInProgress, expirationTime);
|
||
}
|
||
|
||
function captureCommitPhaseError(sourceFiber, value) {
|
||
var expirationTime = Sync;
|
||
var fiber = sourceFiber.return;
|
||
while (fiber !== null) {
|
||
switch (fiber.tag) {
|
||
case ClassComponent:
|
||
var ctor = fiber.type;
|
||
var instance = fiber.stateNode;
|
||
if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
|
||
var errorInfo = createCapturedValue(value, sourceFiber);
|
||
var update = createClassErrorUpdate(fiber, errorInfo, expirationTime);
|
||
enqueueUpdate(fiber, update);
|
||
scheduleWork(fiber, expirationTime);
|
||
return;
|
||
}
|
||
break;
|
||
case HostRoot:
|
||
{
|
||
var _errorInfo = createCapturedValue(value, sourceFiber);
|
||
var _update = createRootErrorUpdate(fiber, _errorInfo, expirationTime);
|
||
enqueueUpdate(fiber, _update);
|
||
scheduleWork(fiber, expirationTime);
|
||
return;
|
||
}
|
||
}
|
||
fiber = fiber.return;
|
||
}
|
||
|
||
if (sourceFiber.tag === HostRoot) {
|
||
// Error was thrown at the root. There is no parent, so the root
|
||
// itself should capture it.
|
||
var rootFiber = sourceFiber;
|
||
var _errorInfo2 = createCapturedValue(value, rootFiber);
|
||
var _update2 = createRootErrorUpdate(rootFiber, _errorInfo2, expirationTime);
|
||
enqueueUpdate(rootFiber, _update2);
|
||
scheduleWork(rootFiber, expirationTime);
|
||
}
|
||
}
|
||
|
||
function computeThreadID(expirationTime, interactionThreadID) {
|
||
// Interaction threads are unique per root and expiration time.
|
||
return expirationTime * 1000 + interactionThreadID;
|
||
}
|
||
|
||
function computeExpirationForFiber(currentTime, fiber) {
|
||
var priorityLevel = unstable_getCurrentPriorityLevel();
|
||
|
||
var expirationTime = void 0;
|
||
if ((fiber.mode & ConcurrentMode) === NoContext) {
|
||
// Outside of concurrent mode, updates are always synchronous.
|
||
expirationTime = Sync;
|
||
} else if (isWorking && !isCommitting$1) {
|
||
// During render phase, updates expire during as the current render.
|
||
expirationTime = nextRenderExpirationTime;
|
||
} else {
|
||
switch (priorityLevel) {
|
||
case unstable_ImmediatePriority:
|
||
expirationTime = Sync;
|
||
break;
|
||
case unstable_UserBlockingPriority:
|
||
expirationTime = computeInteractiveExpiration(currentTime);
|
||
break;
|
||
case unstable_NormalPriority:
|
||
// This is a normal, concurrent update
|
||
expirationTime = computeAsyncExpiration(currentTime);
|
||
break;
|
||
case unstable_LowPriority:
|
||
case unstable_IdlePriority:
|
||
expirationTime = Never;
|
||
break;
|
||
default:
|
||
reactProdInvariant('313');
|
||
}
|
||
|
||
// If we're in the middle of rendering a tree, do not update at the same
|
||
// expiration time that is already rendering.
|
||
if (nextRoot !== null && expirationTime === nextRenderExpirationTime) {
|
||
expirationTime -= 1;
|
||
}
|
||
}
|
||
|
||
// Keep track of the lowest pending interactive expiration time. This
|
||
// allows us to synchronously flush all interactive updates
|
||
// when needed.
|
||
// TODO: Move this to renderer?
|
||
return expirationTime;
|
||
}
|
||
|
||
function renderDidSuspend(root, absoluteTimeoutMs, suspendedTime) {
|
||
// Schedule the timeout.
|
||
if (absoluteTimeoutMs >= 0 && nextLatestAbsoluteTimeoutMs < absoluteTimeoutMs) {
|
||
nextLatestAbsoluteTimeoutMs = absoluteTimeoutMs;
|
||
}
|
||
}
|
||
|
||
function renderDidError() {
|
||
nextRenderDidError = true;
|
||
}
|
||
|
||
function pingSuspendedRoot(root, thenable, pingTime) {
|
||
// A promise that previously suspended React from committing has resolved.
|
||
// If React is still suspended, try again at the previous level (pingTime).
|
||
|
||
var pingCache = root.pingCache;
|
||
if (pingCache !== null) {
|
||
// The thenable resolved, so we no longer need to memoize, because it will
|
||
// never be thrown again.
|
||
pingCache.delete(thenable);
|
||
}
|
||
|
||
if (nextRoot !== null && nextRenderExpirationTime === pingTime) {
|
||
// Received a ping at the same priority level at which we're currently
|
||
// rendering. Restart from the root.
|
||
nextRoot = null;
|
||
} else {
|
||
// Confirm that the root is still suspended at this level. Otherwise exit.
|
||
if (isPriorityLevelSuspended(root, pingTime)) {
|
||
// Ping at the original level
|
||
markPingedPriorityLevel(root, pingTime);
|
||
var rootExpirationTime = root.expirationTime;
|
||
if (rootExpirationTime !== NoWork) {
|
||
requestWork(root, rootExpirationTime);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function retryTimedOutBoundary(boundaryFiber, thenable) {
|
||
// The boundary fiber (a Suspense component) previously timed out and 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
|
||
var retryCache = void 0;
|
||
if (enableSuspenseServerRenderer) {
|
||
switch (boundaryFiber.tag) {
|
||
case SuspenseComponent:
|
||
retryCache = boundaryFiber.stateNode;
|
||
break;
|
||
case DehydratedSuspenseComponent:
|
||
retryCache = boundaryFiber.memoizedState;
|
||
break;
|
||
default:
|
||
reactProdInvariant('314');
|
||
}
|
||
} else {
|
||
retryCache = boundaryFiber.stateNode;
|
||
}
|
||
if (retryCache !== null) {
|
||
// The thenable resolved, so we no longer need to memoize, because it will
|
||
// never be thrown again.
|
||
retryCache.delete(thenable);
|
||
}
|
||
|
||
var currentTime = requestCurrentTime();
|
||
var retryTime = computeExpirationForFiber(currentTime, boundaryFiber);
|
||
var root = scheduleWorkToRoot(boundaryFiber, retryTime);
|
||
if (root !== null) {
|
||
markPendingPriorityLevel(root, retryTime);
|
||
var rootExpirationTime = root.expirationTime;
|
||
if (rootExpirationTime !== NoWork) {
|
||
requestWork(root, rootExpirationTime);
|
||
}
|
||
}
|
||
}
|
||
|
||
function scheduleWorkToRoot(fiber, expirationTime) {
|
||
recordScheduleUpdate();
|
||
|
||
if (fiber.expirationTime < expirationTime) {
|
||
fiber.expirationTime = expirationTime;
|
||
}
|
||
var alternate = fiber.alternate;
|
||
if (alternate !== null && alternate.expirationTime < expirationTime) {
|
||
alternate.expirationTime = expirationTime;
|
||
}
|
||
// Walk the parent path to the root and update the child expiration time.
|
||
var node = fiber.return;
|
||
var root = null;
|
||
if (node === null && fiber.tag === HostRoot) {
|
||
root = fiber.stateNode;
|
||
} else {
|
||
while (node !== null) {
|
||
alternate = node.alternate;
|
||
if (node.childExpirationTime < expirationTime) {
|
||
node.childExpirationTime = expirationTime;
|
||
if (alternate !== null && alternate.childExpirationTime < expirationTime) {
|
||
alternate.childExpirationTime = expirationTime;
|
||
}
|
||
} else if (alternate !== null && alternate.childExpirationTime < expirationTime) {
|
||
alternate.childExpirationTime = expirationTime;
|
||
}
|
||
if (node.return === null && node.tag === HostRoot) {
|
||
root = node.stateNode;
|
||
break;
|
||
}
|
||
node = node.return;
|
||
}
|
||
}
|
||
|
||
if (enableSchedulerTracing) {
|
||
if (root !== null) {
|
||
var interactions = __interactionsRef.current;
|
||
if (interactions.size > 0) {
|
||
var pendingInteractionMap = root.pendingInteractionMap;
|
||
var pendingInteractions = pendingInteractionMap.get(expirationTime);
|
||
if (pendingInteractions != null) {
|
||
interactions.forEach(function (interaction) {
|
||
if (!pendingInteractions.has(interaction)) {
|
||
// Update the pending async work count for previously unscheduled interaction.
|
||
interaction.__count++;
|
||
}
|
||
|
||
pendingInteractions.add(interaction);
|
||
});
|
||
} else {
|
||
pendingInteractionMap.set(expirationTime, new Set(interactions));
|
||
|
||
// Update the pending async work count for the current interactions.
|
||
interactions.forEach(function (interaction) {
|
||
interaction.__count++;
|
||
});
|
||
}
|
||
|
||
var subscriber = __subscriberRef.current;
|
||
if (subscriber !== null) {
|
||
var threadID = computeThreadID(expirationTime, root.interactionThreadID);
|
||
subscriber.onWorkScheduled(interactions, threadID);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return root;
|
||
}
|
||
|
||
|
||
|
||
function scheduleWork(fiber, expirationTime) {
|
||
var root = scheduleWorkToRoot(fiber, expirationTime);
|
||
if (root === null) {
|
||
return;
|
||
}
|
||
|
||
if (!isWorking && nextRenderExpirationTime !== NoWork && expirationTime > nextRenderExpirationTime) {
|
||
// This is an interruption. (Used for performance tracking.)
|
||
interruptedBy = fiber;
|
||
resetStack();
|
||
}
|
||
markPendingPriorityLevel(root, expirationTime);
|
||
if (
|
||
// If we're in the render phase, we don't need to schedule this root
|
||
// for an update, because we'll do it before we exit...
|
||
!isWorking || isCommitting$1 ||
|
||
// ...unless this is a different root than the one we're rendering.
|
||
nextRoot !== root) {
|
||
var rootExpirationTime = root.expirationTime;
|
||
requestWork(root, rootExpirationTime);
|
||
}
|
||
if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
|
||
// Reset this back to zero so subsequent updates don't throw.
|
||
nestedUpdateCount = 0;
|
||
reactProdInvariant('185');
|
||
}
|
||
}
|
||
|
||
function syncUpdates(fn, a, b, c, d) {
|
||
return unstable_runWithPriority(unstable_ImmediatePriority, function () {
|
||
return fn(a, b, c, d);
|
||
});
|
||
}
|
||
|
||
// TODO: Everything below this is written as if it has been lifted to the
|
||
// renderers. I'll do this in a follow-up.
|
||
|
||
// Linked-list of roots
|
||
var firstScheduledRoot = null;
|
||
var lastScheduledRoot = null;
|
||
|
||
var callbackExpirationTime = NoWork;
|
||
var callbackID = void 0;
|
||
var isRendering = false;
|
||
var nextFlushedRoot = null;
|
||
var nextFlushedExpirationTime = NoWork;
|
||
var hasUnhandledError = false;
|
||
var unhandledError = null;
|
||
|
||
var isBatchingUpdates = false;
|
||
var isUnbatchingUpdates = false;
|
||
|
||
var completedBatches = null;
|
||
|
||
var originalStartTimeMs = now();
|
||
var currentRendererTime = msToExpirationTime(originalStartTimeMs);
|
||
var currentSchedulerTime = currentRendererTime;
|
||
|
||
// Use these to prevent an infinite loop of nested updates
|
||
var NESTED_UPDATE_LIMIT = 50;
|
||
var nestedUpdateCount = 0;
|
||
var lastCommittedRootDuringThisBatch = null;
|
||
|
||
function recomputeCurrentRendererTime() {
|
||
var currentTimeMs = now() - originalStartTimeMs;
|
||
currentRendererTime = msToExpirationTime(currentTimeMs);
|
||
}
|
||
|
||
function scheduleCallbackWithExpirationTime(root, expirationTime) {
|
||
if (callbackExpirationTime !== NoWork) {
|
||
// A callback is already scheduled. Check its expiration time (timeout).
|
||
if (expirationTime < callbackExpirationTime) {
|
||
// Existing callback has sufficient timeout. Exit.
|
||
return;
|
||
} else {
|
||
if (callbackID !== null) {
|
||
// Existing callback has insufficient timeout. Cancel and schedule a
|
||
// new one.
|
||
cancelDeferredCallback$$1(callbackID);
|
||
}
|
||
}
|
||
// The request callback timer is already running. Don't start a new one.
|
||
} else {
|
||
startRequestCallbackTimer();
|
||
}
|
||
|
||
callbackExpirationTime = expirationTime;
|
||
var currentMs = now() - originalStartTimeMs;
|
||
var expirationTimeMs = expirationTimeToMs(expirationTime);
|
||
var timeout = expirationTimeMs - currentMs;
|
||
callbackID = scheduleDeferredCallback$$1(performAsyncWork, { timeout: timeout });
|
||
}
|
||
|
||
// For every call to renderRoot, one of onFatal, onComplete, onSuspend, and
|
||
// onYield is called upon exiting. We use these in lieu of returning a tuple.
|
||
// I've also chosen not to inline them into renderRoot because these will
|
||
// eventually be lifted into the renderer.
|
||
function onFatal(root) {
|
||
root.finishedWork = null;
|
||
}
|
||
|
||
function onComplete(root, finishedWork, expirationTime) {
|
||
root.pendingCommitExpirationTime = expirationTime;
|
||
root.finishedWork = finishedWork;
|
||
}
|
||
|
||
function onSuspend(root, finishedWork, suspendedExpirationTime, rootExpirationTime, msUntilTimeout) {
|
||
root.expirationTime = rootExpirationTime;
|
||
if (msUntilTimeout === 0 && !shouldYieldToRenderer()) {
|
||
// Don't wait an additional tick. Commit the tree immediately.
|
||
root.pendingCommitExpirationTime = suspendedExpirationTime;
|
||
root.finishedWork = finishedWork;
|
||
} else if (msUntilTimeout > 0) {
|
||
// Wait `msUntilTimeout` milliseconds before committing.
|
||
root.timeoutHandle = scheduleTimeout(onTimeout.bind(null, root, finishedWork, suspendedExpirationTime), msUntilTimeout);
|
||
}
|
||
}
|
||
|
||
function onYield(root) {
|
||
root.finishedWork = null;
|
||
}
|
||
|
||
function onTimeout(root, finishedWork, suspendedExpirationTime) {
|
||
// The root timed out. Commit it.
|
||
root.pendingCommitExpirationTime = suspendedExpirationTime;
|
||
root.finishedWork = finishedWork;
|
||
// Read the current time before entering the commit phase. We can be
|
||
// certain this won't cause tearing related to batching of event updates
|
||
// because we're at the top of a timer event.
|
||
recomputeCurrentRendererTime();
|
||
currentSchedulerTime = currentRendererTime;
|
||
flushRoot(root, suspendedExpirationTime);
|
||
}
|
||
|
||
function onCommit(root, expirationTime) {
|
||
root.expirationTime = expirationTime;
|
||
root.finishedWork = null;
|
||
}
|
||
|
||
function requestCurrentTime() {
|
||
// requestCurrentTime is called by the scheduler to compute an expiration
|
||
// time.
|
||
//
|
||
// Expiration times are computed by adding to the current time (the start
|
||
// time). However, if two updates are scheduled within the same event, we
|
||
// should treat their start times as simultaneous, even if the actual clock
|
||
// time has advanced between the first and second call.
|
||
|
||
// In other words, because expiration times determine how updates are batched,
|
||
// we want all updates of like priority that occur within the same event to
|
||
// receive the same expiration time. Otherwise we get tearing.
|
||
//
|
||
// We keep track of two separate times: the current "renderer" time and the
|
||
// current "scheduler" time. The renderer time can be updated whenever; it
|
||
// only exists to minimize the calls performance.now.
|
||
//
|
||
// But the scheduler time can only be updated if there's no pending work, or
|
||
// if we know for certain that we're not in the middle of an event.
|
||
|
||
if (isRendering) {
|
||
// We're already rendering. Return the most recently read time.
|
||
return currentSchedulerTime;
|
||
}
|
||
// Check if there's pending work.
|
||
findHighestPriorityRoot();
|
||
if (nextFlushedExpirationTime === NoWork || nextFlushedExpirationTime === Never) {
|
||
// If there's no pending work, or if the pending work is offscreen, we can
|
||
// read the current time without risk of tearing.
|
||
recomputeCurrentRendererTime();
|
||
currentSchedulerTime = currentRendererTime;
|
||
return currentSchedulerTime;
|
||
}
|
||
// There's already pending work. We might be in the middle of a browser
|
||
// event. If we were to read the current time, it could cause multiple updates
|
||
// within the same event to receive different expiration times, leading to
|
||
// tearing. Return the last read time. During the next idle callback, the
|
||
// time will be updated.
|
||
return currentSchedulerTime;
|
||
}
|
||
|
||
// requestWork is called by the scheduler whenever a root receives an update.
|
||
// It's up to the renderer to call renderRoot at some point in the future.
|
||
function requestWork(root, expirationTime) {
|
||
addRootToSchedule(root, expirationTime);
|
||
if (isRendering) {
|
||
// Prevent reentrancy. Remaining work will be scheduled at the end of
|
||
// the currently rendering batch.
|
||
return;
|
||
}
|
||
|
||
if (isBatchingUpdates) {
|
||
// Flush work at the end of the batch.
|
||
if (isUnbatchingUpdates) {
|
||
// ...unless we're inside unbatchedUpdates, in which case we should
|
||
// flush it now.
|
||
nextFlushedRoot = root;
|
||
nextFlushedExpirationTime = Sync;
|
||
performWorkOnRoot(root, Sync, false);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// TODO: Get rid of Sync and use current time?
|
||
if (expirationTime === Sync) {
|
||
performSyncWork();
|
||
} else {
|
||
scheduleCallbackWithExpirationTime(root, expirationTime);
|
||
}
|
||
}
|
||
|
||
function addRootToSchedule(root, expirationTime) {
|
||
// Add the root to the schedule.
|
||
// Check if this root is already part of the schedule.
|
||
if (root.nextScheduledRoot === null) {
|
||
// This root is not already scheduled. Add it.
|
||
root.expirationTime = expirationTime;
|
||
if (lastScheduledRoot === null) {
|
||
firstScheduledRoot = lastScheduledRoot = root;
|
||
root.nextScheduledRoot = root;
|
||
} else {
|
||
lastScheduledRoot.nextScheduledRoot = root;
|
||
lastScheduledRoot = root;
|
||
lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
|
||
}
|
||
} else {
|
||
// This root is already scheduled, but its priority may have increased.
|
||
var remainingExpirationTime = root.expirationTime;
|
||
if (expirationTime > remainingExpirationTime) {
|
||
// Update the priority.
|
||
root.expirationTime = expirationTime;
|
||
}
|
||
}
|
||
}
|
||
|
||
function findHighestPriorityRoot() {
|
||
var highestPriorityWork = NoWork;
|
||
var highestPriorityRoot = null;
|
||
if (lastScheduledRoot !== null) {
|
||
var previousScheduledRoot = lastScheduledRoot;
|
||
var root = firstScheduledRoot;
|
||
while (root !== null) {
|
||
var remainingExpirationTime = root.expirationTime;
|
||
if (remainingExpirationTime === NoWork) {
|
||
// This root no longer has work. Remove it from the scheduler.
|
||
|
||
// TODO: This check is redudant, but Flow is confused by the branch
|
||
// below where we set lastScheduledRoot to null, even though we break
|
||
// from the loop right after.
|
||
!(previousScheduledRoot !== null && lastScheduledRoot !== null) ? reactProdInvariant('244') : void 0;
|
||
if (root === root.nextScheduledRoot) {
|
||
// This is the only root in the list.
|
||
root.nextScheduledRoot = null;
|
||
firstScheduledRoot = lastScheduledRoot = null;
|
||
break;
|
||
} else if (root === firstScheduledRoot) {
|
||
// This is the first root in the list.
|
||
var next = root.nextScheduledRoot;
|
||
firstScheduledRoot = next;
|
||
lastScheduledRoot.nextScheduledRoot = next;
|
||
root.nextScheduledRoot = null;
|
||
} else if (root === lastScheduledRoot) {
|
||
// This is the last root in the list.
|
||
lastScheduledRoot = previousScheduledRoot;
|
||
lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
|
||
root.nextScheduledRoot = null;
|
||
break;
|
||
} else {
|
||
previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
|
||
root.nextScheduledRoot = null;
|
||
}
|
||
root = previousScheduledRoot.nextScheduledRoot;
|
||
} else {
|
||
if (remainingExpirationTime > highestPriorityWork) {
|
||
// Update the priority, if it's higher
|
||
highestPriorityWork = remainingExpirationTime;
|
||
highestPriorityRoot = root;
|
||
}
|
||
if (root === lastScheduledRoot) {
|
||
break;
|
||
}
|
||
if (highestPriorityWork === Sync) {
|
||
// Sync is highest priority by definition so
|
||
// we can stop searching.
|
||
break;
|
||
}
|
||
previousScheduledRoot = root;
|
||
root = root.nextScheduledRoot;
|
||
}
|
||
}
|
||
}
|
||
|
||
nextFlushedRoot = highestPriorityRoot;
|
||
nextFlushedExpirationTime = highestPriorityWork;
|
||
}
|
||
|
||
// TODO: This wrapper exists because many of the older tests (the ones that use
|
||
// flushDeferredPri) rely on the number of times `shouldYield` is called. We
|
||
// should get rid of it.
|
||
var didYield = false;
|
||
function shouldYieldToRenderer() {
|
||
if (didYield) {
|
||
return true;
|
||
}
|
||
if (shouldYield$$1()) {
|
||
didYield = true;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
function performAsyncWork() {
|
||
try {
|
||
if (!shouldYieldToRenderer()) {
|
||
// The callback timed out. That means at least one update has expired.
|
||
// Iterate through the root schedule. If they contain expired work, set
|
||
// the next render expiration time to the current time. This has the effect
|
||
// of flushing all expired work in a single batch, instead of flushing each
|
||
// level one at a time.
|
||
if (firstScheduledRoot !== null) {
|
||
recomputeCurrentRendererTime();
|
||
var root = firstScheduledRoot;
|
||
do {
|
||
didExpireAtExpirationTime(root, currentRendererTime);
|
||
// The root schedule is circular, so this is never null.
|
||
root = root.nextScheduledRoot;
|
||
} while (root !== firstScheduledRoot);
|
||
}
|
||
}
|
||
performWork(NoWork, true);
|
||
} finally {
|
||
didYield = false;
|
||
}
|
||
}
|
||
|
||
function performSyncWork() {
|
||
performWork(Sync, false);
|
||
}
|
||
|
||
function performWork(minExpirationTime, isYieldy) {
|
||
// Keep working on roots until there's no more work, or until there's a higher
|
||
// priority event.
|
||
findHighestPriorityRoot();
|
||
|
||
if (isYieldy) {
|
||
recomputeCurrentRendererTime();
|
||
currentSchedulerTime = currentRendererTime;
|
||
|
||
if (enableUserTimingAPI) {
|
||
var didExpire = nextFlushedExpirationTime > currentRendererTime;
|
||
var timeout = expirationTimeToMs(nextFlushedExpirationTime);
|
||
stopRequestCallbackTimer(didExpire, timeout);
|
||
}
|
||
|
||
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime && !(didYield && currentRendererTime > nextFlushedExpirationTime)) {
|
||
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, currentRendererTime > nextFlushedExpirationTime);
|
||
findHighestPriorityRoot();
|
||
recomputeCurrentRendererTime();
|
||
currentSchedulerTime = currentRendererTime;
|
||
}
|
||
} else {
|
||
while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && minExpirationTime <= nextFlushedExpirationTime) {
|
||
performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false);
|
||
findHighestPriorityRoot();
|
||
}
|
||
}
|
||
|
||
// We're done flushing work. Either we ran out of time in this callback,
|
||
// or there's no more work left with sufficient priority.
|
||
|
||
// If we're inside a callback, set this to false since we just completed it.
|
||
if (isYieldy) {
|
||
callbackExpirationTime = NoWork;
|
||
callbackID = null;
|
||
}
|
||
// If there's work left over, schedule a new callback.
|
||
if (nextFlushedExpirationTime !== NoWork) {
|
||
scheduleCallbackWithExpirationTime(nextFlushedRoot, nextFlushedExpirationTime);
|
||
}
|
||
|
||
// Clean-up.
|
||
finishRendering();
|
||
}
|
||
|
||
function flushRoot(root, expirationTime) {
|
||
!!isRendering ? reactProdInvariant('253') : void 0;
|
||
// Perform work on root as if the given expiration time is the current time.
|
||
// This has the effect of synchronously flushing all work up to and
|
||
// including the given time.
|
||
nextFlushedRoot = root;
|
||
nextFlushedExpirationTime = expirationTime;
|
||
performWorkOnRoot(root, expirationTime, false);
|
||
// Flush any sync work that was scheduled by lifecycles
|
||
performSyncWork();
|
||
}
|
||
|
||
function finishRendering() {
|
||
nestedUpdateCount = 0;
|
||
lastCommittedRootDuringThisBatch = null;
|
||
|
||
if (completedBatches !== null) {
|
||
var batches = completedBatches;
|
||
completedBatches = null;
|
||
for (var i = 0; i < batches.length; i++) {
|
||
var batch = batches[i];
|
||
try {
|
||
batch._onComplete();
|
||
} catch (error) {
|
||
if (!hasUnhandledError) {
|
||
hasUnhandledError = true;
|
||
unhandledError = error;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
if (hasUnhandledError) {
|
||
var error = unhandledError;
|
||
unhandledError = null;
|
||
hasUnhandledError = false;
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
function performWorkOnRoot(root, expirationTime, isYieldy) {
|
||
!!isRendering ? reactProdInvariant('245') : void 0;
|
||
|
||
isRendering = true;
|
||
|
||
// Check if this is async work or sync/expired work.
|
||
if (!isYieldy) {
|
||
// Flush work without yielding.
|
||
// TODO: Non-yieldy work does not necessarily imply expired work. A renderer
|
||
// may want to perform some work without yielding, but also without
|
||
// requiring the root to complete (by triggering placeholders).
|
||
|
||
var finishedWork = root.finishedWork;
|
||
if (finishedWork !== null) {
|
||
// This root is already complete. We can commit it.
|
||
completeRoot(root, finishedWork, expirationTime);
|
||
} else {
|
||
root.finishedWork = null;
|
||
// If this root previously suspended, clear its existing timeout, since
|
||
// we're about to try rendering again.
|
||
var timeoutHandle = root.timeoutHandle;
|
||
if (timeoutHandle !== noTimeout) {
|
||
root.timeoutHandle = noTimeout;
|
||
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
|
||
cancelTimeout(timeoutHandle);
|
||
}
|
||
renderRoot(root, isYieldy);
|
||
finishedWork = root.finishedWork;
|
||
if (finishedWork !== null) {
|
||
// We've completed the root. Commit it.
|
||
completeRoot(root, finishedWork, expirationTime);
|
||
}
|
||
}
|
||
} else {
|
||
// Flush async work.
|
||
var _finishedWork = root.finishedWork;
|
||
if (_finishedWork !== null) {
|
||
// This root is already complete. We can commit it.
|
||
completeRoot(root, _finishedWork, expirationTime);
|
||
} else {
|
||
root.finishedWork = null;
|
||
// If this root previously suspended, clear its existing timeout, since
|
||
// we're about to try rendering again.
|
||
var _timeoutHandle = root.timeoutHandle;
|
||
if (_timeoutHandle !== noTimeout) {
|
||
root.timeoutHandle = noTimeout;
|
||
// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
|
||
cancelTimeout(_timeoutHandle);
|
||
}
|
||
renderRoot(root, isYieldy);
|
||
_finishedWork = root.finishedWork;
|
||
if (_finishedWork !== null) {
|
||
// We've completed the root. Check the if we should yield one more time
|
||
// before committing.
|
||
if (!shouldYieldToRenderer()) {
|
||
// Still time left. Commit the root.
|
||
completeRoot(root, _finishedWork, expirationTime);
|
||
} else {
|
||
// There's no time left. Mark this root as complete. We'll come
|
||
// back and commit it later.
|
||
root.finishedWork = _finishedWork;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
isRendering = false;
|
||
}
|
||
|
||
function completeRoot(root, finishedWork, expirationTime) {
|
||
// Check if there's a batch that matches this expiration time.
|
||
var firstBatch = root.firstBatch;
|
||
if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
|
||
if (completedBatches === null) {
|
||
completedBatches = [firstBatch];
|
||
} else {
|
||
completedBatches.push(firstBatch);
|
||
}
|
||
if (firstBatch._defer) {
|
||
// This root is blocked from committing by a batch. Unschedule it until
|
||
// we receive another update.
|
||
root.finishedWork = finishedWork;
|
||
root.expirationTime = NoWork;
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Commit the root.
|
||
root.finishedWork = null;
|
||
|
||
// Check if this is a nested update (a sync update scheduled during the
|
||
// commit phase).
|
||
if (root === lastCommittedRootDuringThisBatch) {
|
||
// If the next root is the same as the previous root, this is a nested
|
||
// update. To prevent an infinite loop, increment the nested update count.
|
||
nestedUpdateCount++;
|
||
} else {
|
||
// Reset whenever we switch roots.
|
||
lastCommittedRootDuringThisBatch = root;
|
||
nestedUpdateCount = 0;
|
||
}
|
||
unstable_runWithPriority(unstable_ImmediatePriority, function () {
|
||
commitRoot(root, finishedWork);
|
||
});
|
||
}
|
||
|
||
function onUncaughtError(error) {
|
||
!(nextFlushedRoot !== null) ? reactProdInvariant('246') : void 0;
|
||
// Unschedule this root so we don't work on it again until there's
|
||
// another update.
|
||
nextFlushedRoot.expirationTime = NoWork;
|
||
if (!hasUnhandledError) {
|
||
hasUnhandledError = true;
|
||
unhandledError = error;
|
||
}
|
||
}
|
||
|
||
// TODO: Batching should be implemented at the renderer level, not inside
|
||
// the reconciler.
|
||
function batchedUpdates(fn, a) {
|
||
var previousIsBatchingUpdates = isBatchingUpdates;
|
||
isBatchingUpdates = true;
|
||
try {
|
||
return fn(a);
|
||
} finally {
|
||
isBatchingUpdates = previousIsBatchingUpdates;
|
||
if (!isBatchingUpdates && !isRendering) {
|
||
performSyncWork();
|
||
}
|
||
}
|
||
}
|
||
|
||
// TODO: Batching should be implemented at the renderer level, not within
|
||
// the reconciler.
|
||
function flushSync(fn, a) {
|
||
!!isRendering ? reactProdInvariant('187') : void 0;
|
||
var previousIsBatchingUpdates = isBatchingUpdates;
|
||
isBatchingUpdates = true;
|
||
try {
|
||
return syncUpdates(fn, a);
|
||
} finally {
|
||
isBatchingUpdates = previousIsBatchingUpdates;
|
||
performSyncWork();
|
||
}
|
||
}
|
||
|
||
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 scheduleRootUpdate(current$$1, element, expirationTime, callback) {
|
||
var update = createUpdate(expirationTime);
|
||
// Caution: React DevTools currently depends on this property
|
||
// being called "element".
|
||
update.payload = { element: element };
|
||
|
||
callback = callback === undefined ? null : callback;
|
||
if (callback !== null) {
|
||
update.callback = callback;
|
||
}
|
||
|
||
flushPassiveEffects$1();
|
||
enqueueUpdate(current$$1, update);
|
||
scheduleWork(current$$1, expirationTime);
|
||
|
||
return expirationTime;
|
||
}
|
||
|
||
function updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback) {
|
||
// TODO: If this is a nested container, this won't be the root.
|
||
var current$$1 = container.current;
|
||
|
||
var context = getContextForSubtree(parentComponent);
|
||
if (container.context === null) {
|
||
container.context = context;
|
||
} else {
|
||
container.pendingContext = context;
|
||
}
|
||
|
||
return scheduleRootUpdate(current$$1, element, expirationTime, callback);
|
||
}
|
||
|
||
function createContainer(containerInfo, isConcurrent, hydrate) {
|
||
return createFiberRoot(containerInfo, isConcurrent, hydrate);
|
||
}
|
||
|
||
function updateContainer(element, container, parentComponent, callback) {
|
||
var current$$1 = container.current;
|
||
var currentTime = requestCurrentTime();
|
||
var expirationTime = computeExpirationForFiber(currentTime, current$$1);
|
||
return updateContainerAtExpirationTime(element, container, parentComponent, expirationTime, callback);
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
var overrideProps = null;
|
||
|
||
function injectIntoDevTools(devToolsConfig) {
|
||
var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
|
||
var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
||
|
||
|
||
return injectInternals(_assign({}, devToolsConfig, {
|
||
overrideProps: overrideProps,
|
||
currentDispatcherRef: ReactCurrentDispatcher,
|
||
findHostInstanceByFiber: function (fiber) {
|
||
var hostFiber = findCurrentHostFiber(fiber);
|
||
if (hostFiber === null) {
|
||
return null;
|
||
}
|
||
return hostFiber.stateNode;
|
||
},
|
||
findFiberByHostInstance: function (instance) {
|
||
if (!findFiberByHostInstance) {
|
||
// Might not be implemented by the renderer.
|
||
return null;
|
||
}
|
||
return findFiberByHostInstance(instance);
|
||
}
|
||
}));
|
||
}
|
||
|
||
// This file intentionally does *not* have the Flow annotation.
|
||
// Don't add it. See `./inline-typed.js` for an explanation.
|
||
|
||
// TODO: this is special because it gets imported during build.
|
||
|
||
var ReactVersion = '16.8.6';
|
||
|
||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||
|
||
function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }
|
||
|
||
// for .act's return value
|
||
|
||
|
||
var defaultTestOptions = {
|
||
createNodeMock: function () {
|
||
return null;
|
||
}
|
||
};
|
||
|
||
function toJSON(inst) {
|
||
if (inst.isHidden) {
|
||
// Omit timed out children from output entirely. This seems like the least
|
||
// surprising behavior. We could perhaps add a separate API that includes
|
||
// them, if it turns out people need it.
|
||
return null;
|
||
}
|
||
switch (inst.tag) {
|
||
case 'TEXT':
|
||
return inst.text;
|
||
case 'INSTANCE':
|
||
{
|
||
/* eslint-disable no-unused-vars */
|
||
// We don't include the `children` prop in JSON.
|
||
// Instead, we will include the actual rendered children.
|
||
var _inst$props = inst.props,
|
||
_children = _inst$props.children,
|
||
_props = _objectWithoutProperties(_inst$props, ['children']);
|
||
/* eslint-enable */
|
||
|
||
|
||
var renderedChildren = null;
|
||
if (inst.children && inst.children.length) {
|
||
for (var i = 0; i < inst.children.length; i++) {
|
||
var renderedChild = toJSON(inst.children[i]);
|
||
if (renderedChild !== null) {
|
||
if (renderedChildren === null) {
|
||
renderedChildren = [renderedChild];
|
||
} else {
|
||
renderedChildren.push(renderedChild);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
var json = {
|
||
type: inst.type,
|
||
props: _props,
|
||
children: renderedChildren
|
||
};
|
||
Object.defineProperty(json, '$$typeof', {
|
||
value: Symbol.for('react.test.json')
|
||
});
|
||
return json;
|
||
}
|
||
default:
|
||
throw new Error('Unexpected node type in toJSON: ' + inst.tag);
|
||
}
|
||
}
|
||
|
||
function childrenToTree(node) {
|
||
if (!node) {
|
||
return null;
|
||
}
|
||
var children = nodeAndSiblingsArray(node);
|
||
if (children.length === 0) {
|
||
return null;
|
||
} else if (children.length === 1) {
|
||
return toTree(children[0]);
|
||
}
|
||
return flatten(children.map(toTree));
|
||
}
|
||
|
||
function nodeAndSiblingsArray(nodeWithSibling) {
|
||
var array = [];
|
||
var node = nodeWithSibling;
|
||
while (node != null) {
|
||
array.push(node);
|
||
node = node.sibling;
|
||
}
|
||
return array;
|
||
}
|
||
|
||
function flatten(arr) {
|
||
var result = [];
|
||
var stack = [{ i: 0, array: arr }];
|
||
while (stack.length) {
|
||
var n = stack.pop();
|
||
while (n.i < n.array.length) {
|
||
var el = n.array[n.i];
|
||
n.i += 1;
|
||
if (Array.isArray(el)) {
|
||
stack.push(n);
|
||
stack.push({ i: 0, array: el });
|
||
break;
|
||
}
|
||
result.push(el);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
|
||
function toTree(node) {
|
||
if (node == null) {
|
||
return null;
|
||
}
|
||
switch (node.tag) {
|
||
case HostRoot:
|
||
return childrenToTree(node.child);
|
||
case HostPortal:
|
||
return childrenToTree(node.child);
|
||
case ClassComponent:
|
||
return {
|
||
nodeType: 'component',
|
||
type: node.type,
|
||
props: _assign({}, node.memoizedProps),
|
||
instance: node.stateNode,
|
||
rendered: childrenToTree(node.child)
|
||
};
|
||
case FunctionComponent:
|
||
case SimpleMemoComponent:
|
||
return {
|
||
nodeType: 'component',
|
||
type: node.type,
|
||
props: _assign({}, node.memoizedProps),
|
||
instance: null,
|
||
rendered: childrenToTree(node.child)
|
||
};
|
||
case HostComponent:
|
||
{
|
||
return {
|
||
nodeType: 'host',
|
||
type: node.type,
|
||
props: _assign({}, node.memoizedProps),
|
||
instance: null, // TODO: use createNodeMock here somehow?
|
||
rendered: flatten(nodeAndSiblingsArray(node.child).map(toTree))
|
||
};
|
||
}
|
||
case HostText:
|
||
return node.stateNode.text;
|
||
case Fragment:
|
||
case ContextProvider:
|
||
case ContextConsumer:
|
||
case Mode:
|
||
case Profiler:
|
||
case ForwardRef:
|
||
case MemoComponent:
|
||
case IncompleteClassComponent:
|
||
return childrenToTree(node.child);
|
||
default:
|
||
reactProdInvariant('214', node.tag);
|
||
}
|
||
}
|
||
|
||
var validWrapperTypes = new Set([FunctionComponent, ClassComponent, HostComponent, ForwardRef, MemoComponent, SimpleMemoComponent,
|
||
// Normally skipped, but used when there's more than one root child.
|
||
HostRoot]);
|
||
|
||
function getChildren(parent) {
|
||
var children = [];
|
||
var startingNode = parent;
|
||
var node = startingNode;
|
||
if (node.child === null) {
|
||
return children;
|
||
}
|
||
node.child.return = node;
|
||
node = node.child;
|
||
outer: while (true) {
|
||
var descend = false;
|
||
if (validWrapperTypes.has(node.tag)) {
|
||
children.push(wrapFiber(node));
|
||
} else if (node.tag === HostText) {
|
||
children.push('' + node.memoizedProps);
|
||
} else {
|
||
descend = true;
|
||
}
|
||
if (descend && node.child !== null) {
|
||
node.child.return = node;
|
||
node = node.child;
|
||
continue;
|
||
}
|
||
while (node.sibling === null) {
|
||
if (node.return === startingNode) {
|
||
break outer;
|
||
}
|
||
node = node.return;
|
||
}
|
||
node.sibling.return = node.return;
|
||
node = node.sibling;
|
||
}
|
||
return children;
|
||
}
|
||
|
||
var ReactTestInstance = function () {
|
||
ReactTestInstance.prototype._currentFiber = function _currentFiber() {
|
||
// Throws if this component has been unmounted.
|
||
var fiber = findCurrentFiberUsingSlowPath(this._fiber);
|
||
!(fiber !== null) ? reactProdInvariant('224') : void 0;
|
||
return fiber;
|
||
};
|
||
|
||
function ReactTestInstance(fiber) {
|
||
_classCallCheck(this, ReactTestInstance);
|
||
|
||
!validWrapperTypes.has(fiber.tag) ? reactProdInvariant('225', fiber.tag) : void 0;
|
||
this._fiber = fiber;
|
||
}
|
||
|
||
// Custom search functions
|
||
ReactTestInstance.prototype.find = function find(predicate) {
|
||
return expectOne(this.findAll(predicate, { deep: false }), 'matching custom predicate: ' + predicate.toString());
|
||
};
|
||
|
||
ReactTestInstance.prototype.findByType = function findByType(type) {
|
||
return expectOne(this.findAllByType(type, { deep: false }), 'with node type: "' + (type.displayName || type.name) + '"');
|
||
};
|
||
|
||
ReactTestInstance.prototype.findByProps = function findByProps(props) {
|
||
return expectOne(this.findAllByProps(props, { deep: false }), 'with props: ' + JSON.stringify(props));
|
||
};
|
||
|
||
ReactTestInstance.prototype.findAll = function findAll(predicate) {
|
||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||
|
||
return _findAll(this, predicate, options);
|
||
};
|
||
|
||
ReactTestInstance.prototype.findAllByType = function findAllByType(type) {
|
||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||
|
||
return _findAll(this, function (node) {
|
||
return node.type === type;
|
||
}, options);
|
||
};
|
||
|
||
ReactTestInstance.prototype.findAllByProps = function findAllByProps(props) {
|
||
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
||
|
||
return _findAll(this, function (node) {
|
||
return node.props && propsMatch(node.props, props);
|
||
}, options);
|
||
};
|
||
|
||
_createClass(ReactTestInstance, [{
|
||
key: 'instance',
|
||
get: function () {
|
||
if (this._fiber.tag === HostComponent) {
|
||
return getPublicInstance(this._fiber.stateNode);
|
||
} else {
|
||
return this._fiber.stateNode;
|
||
}
|
||
}
|
||
}, {
|
||
key: 'type',
|
||
get: function () {
|
||
return this._fiber.type;
|
||
}
|
||
}, {
|
||
key: 'props',
|
||
get: function () {
|
||
return this._currentFiber().memoizedProps;
|
||
}
|
||
}, {
|
||
key: 'parent',
|
||
get: function () {
|
||
var parent = this._fiber.return;
|
||
while (parent !== null) {
|
||
if (validWrapperTypes.has(parent.tag)) {
|
||
if (parent.tag === HostRoot) {
|
||
// Special case: we only "materialize" instances for roots
|
||
// if they have more than a single child. So we'll check that now.
|
||
if (getChildren(parent).length < 2) {
|
||
return null;
|
||
}
|
||
}
|
||
return wrapFiber(parent);
|
||
}
|
||
parent = parent.return;
|
||
}
|
||
return null;
|
||
}
|
||
}, {
|
||
key: 'children',
|
||
get: function () {
|
||
return getChildren(this._currentFiber());
|
||
}
|
||
}]);
|
||
|
||
return ReactTestInstance;
|
||
}();
|
||
|
||
function _findAll(root, predicate, options) {
|
||
var deep = options ? options.deep : true;
|
||
var results = [];
|
||
|
||
if (predicate(root)) {
|
||
results.push(root);
|
||
if (!deep) {
|
||
return results;
|
||
}
|
||
}
|
||
|
||
root.children.forEach(function (child) {
|
||
if (typeof child === 'string') {
|
||
return;
|
||
}
|
||
results.push.apply(results, _findAll(child, predicate, options));
|
||
});
|
||
|
||
return results;
|
||
}
|
||
|
||
function expectOne(all, message) {
|
||
if (all.length === 1) {
|
||
return all[0];
|
||
}
|
||
|
||
var prefix = all.length === 0 ? 'No instances found ' : 'Expected 1 but found ' + all.length + ' instances ';
|
||
|
||
throw new Error(prefix + message);
|
||
}
|
||
|
||
function propsMatch(props, filter) {
|
||
for (var key in filter) {
|
||
if (props[key] !== filter[key]) {
|
||
return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
var ReactTestRendererFiber = {
|
||
create: function (element, options) {
|
||
var createNodeMock = defaultTestOptions.createNodeMock;
|
||
var isConcurrent = false;
|
||
if (typeof options === 'object' && options !== null) {
|
||
if (typeof options.createNodeMock === 'function') {
|
||
createNodeMock = options.createNodeMock;
|
||
}
|
||
if (options.unstable_isConcurrent === true) {
|
||
isConcurrent = true;
|
||
}
|
||
}
|
||
var container = {
|
||
children: [],
|
||
createNodeMock: createNodeMock,
|
||
tag: 'CONTAINER'
|
||
};
|
||
var root = createContainer(container, isConcurrent, false);
|
||
!(root != null) ? reactProdInvariant('215') : void 0;
|
||
updateContainer(element, root, null, null);
|
||
|
||
var entry = {
|
||
root: undefined, // makes flow happy
|
||
// we define a 'getter' for 'root' below using 'Object.defineProperty'
|
||
toJSON: function () {
|
||
if (root == null || root.current == null || container == null) {
|
||
return null;
|
||
}
|
||
if (container.children.length === 0) {
|
||
return null;
|
||
}
|
||
if (container.children.length === 1) {
|
||
return toJSON(container.children[0]);
|
||
}
|
||
if (container.children.length === 2 && container.children[0].isHidden === true && container.children[1].isHidden === false) {
|
||
// Omit timed out children from output entirely, including the fact that we
|
||
// temporarily wrap fallback and timed out children in an array.
|
||
return toJSON(container.children[1]);
|
||
}
|
||
var renderedChildren = null;
|
||
if (container.children && container.children.length) {
|
||
for (var i = 0; i < container.children.length; i++) {
|
||
var renderedChild = toJSON(container.children[i]);
|
||
if (renderedChild !== null) {
|
||
if (renderedChildren === null) {
|
||
renderedChildren = [renderedChild];
|
||
} else {
|
||
renderedChildren.push(renderedChild);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return renderedChildren;
|
||
},
|
||
toTree: function () {
|
||
if (root == null || root.current == null) {
|
||
return null;
|
||
}
|
||
return toTree(root.current);
|
||
},
|
||
update: function (newElement) {
|
||
if (root == null || root.current == null) {
|
||
return;
|
||
}
|
||
updateContainer(newElement, root, null, null);
|
||
},
|
||
unmount: function () {
|
||
if (root == null || root.current == null) {
|
||
return;
|
||
}
|
||
updateContainer(null, root, null, null);
|
||
container = null;
|
||
root = null;
|
||
},
|
||
getInstance: function () {
|
||
if (root == null || root.current == null) {
|
||
return null;
|
||
}
|
||
return getPublicRootInstance(root);
|
||
},
|
||
|
||
|
||
unstable_flushAll: flushAll,
|
||
unstable_flushSync: function (fn) {
|
||
clearYields();
|
||
return flushSync(fn);
|
||
},
|
||
|
||
unstable_flushNumberOfYields: flushNumberOfYields,
|
||
unstable_clearYields: clearYields
|
||
};
|
||
|
||
Object.defineProperty(entry, 'root', {
|
||
configurable: true,
|
||
enumerable: true,
|
||
get: function () {
|
||
if (root === null) {
|
||
throw new Error("Can't access .root on unmounted test renderer");
|
||
}
|
||
var children = getChildren(root.current);
|
||
if (children.length === 0) {
|
||
throw new Error("Can't access .root on unmounted test renderer");
|
||
} else if (children.length === 1) {
|
||
// Normally, we skip the root and just give you the child.
|
||
return children[0];
|
||
} else {
|
||
// However, we give you the root if there's more than one root child.
|
||
// We could make this the behavior for all cases but it would be a breaking change.
|
||
return wrapFiber(root.current);
|
||
}
|
||
}
|
||
});
|
||
|
||
return entry;
|
||
},
|
||
|
||
|
||
unstable_yield: yieldValue,
|
||
unstable_clearYields: clearYields,
|
||
|
||
/* eslint-disable camelcase */
|
||
unstable_batchedUpdates: batchedUpdates,
|
||
/* eslint-enable camelcase */
|
||
|
||
unstable_setNowImplementation: setNowImplementation,
|
||
|
||
act: function (callback) {
|
||
// note: keep these warning messages in sync with
|
||
// createNoop.js and ReactTestUtils.js
|
||
var result = batchedUpdates(callback);
|
||
flushPassiveEffects();
|
||
// we want the user to not expect a return,
|
||
// but we want to warn if they use it like they can await on it.
|
||
return {
|
||
then: function () {
|
||
|
||
}
|
||
};
|
||
}
|
||
};
|
||
|
||
// root used to flush effects during .act() calls
|
||
var actRoot = createContainer({
|
||
children: [],
|
||
createNodeMock: defaultTestOptions.createNodeMock,
|
||
tag: 'CONTAINER'
|
||
}, true, false);
|
||
|
||
function flushPassiveEffects() {
|
||
// Trick to flush passive effects without exposing an internal API:
|
||
// Create a throwaway root and schedule a dummy update on it.
|
||
updateContainer(null, actRoot, null, null);
|
||
}
|
||
|
||
var fiberToWrapper = new WeakMap();
|
||
function wrapFiber(fiber) {
|
||
var wrapper = fiberToWrapper.get(fiber);
|
||
if (wrapper === undefined && fiber.alternate !== null) {
|
||
wrapper = fiberToWrapper.get(fiber.alternate);
|
||
}
|
||
if (wrapper === undefined) {
|
||
wrapper = new ReactTestInstance(fiber);
|
||
fiberToWrapper.set(fiber, wrapper);
|
||
}
|
||
return wrapper;
|
||
}
|
||
|
||
// Enable ReactTestRenderer to be used to test DevTools integration.
|
||
injectIntoDevTools({
|
||
findFiberByHostInstance: function () {
|
||
throw new Error('TestRenderer does not support findFiberByHostInstance()');
|
||
},
|
||
bundleType: 0,
|
||
version: ReactVersion,
|
||
rendererPackageName: 'react-test-renderer'
|
||
});
|
||
|
||
|
||
|
||
var ReactTestRenderer = ({
|
||
default: ReactTestRendererFiber
|
||
});
|
||
|
||
var ReactTestRenderer$1 = ( ReactTestRenderer && ReactTestRendererFiber ) || ReactTestRenderer;
|
||
|
||
// TODO: decide on the top-level export form.
|
||
// This is hacky but makes it work with both Rollup and Jest.
|
||
var reactTestRenderer = ReactTestRenderer$1.default || ReactTestRenderer$1;
|
||
|
||
export default reactTestRenderer;
|