/** @license React v16.2.0 * react-test-renderer.development.js * * 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. */ 'use strict'; if (process.env.NODE_ENV !== "production") { (function() { 'use strict'; var _assign = require('object-assign'); var invariant = require('fbjs/lib/invariant'); var warning = require('fbjs/lib/warning'); var React = require('react'); var emptyObject = require('fbjs/lib/emptyObject'); var checkPropTypes = require('prop-types/checkPropTypes'); var shallowEqual = require('fbjs/lib/shallowEqual'); /** * 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. */ var enableAsyncSubtreeAPI = true; // Exports ReactDOM.createRoot var enableUserTimingAPI = true; // Mutating mode (React DOM, React ART, React Native): var enableMutatingReconciler = true; // Experimental noop mode (currently unused): var enableNoopReconciler = false; // Experimental persistent mode (CS): var enablePersistentReconciler = false; // Helps identify side effects in begin-phase lifecycle hooks and setState reducers: var debugRenderPhaseSideEffects = false; // Only used in www builds. /** * `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 ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; var ReactCurrentOwner = ReactInternals.ReactCurrentOwner; var ReactDebugCurrentFrame = ReactInternals.ReactDebugCurrentFrame; function getComponentName(fiber) { var type = fiber.type; if (typeof type === 'string') { return type; } if (typeof type === 'function') { return type.displayName || type.name; } return null; } var IndeterminateComponent = 0; // Before we know whether it is functional or class var FunctionalComponent = 1; var ClassComponent = 2; 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 CallComponent = 7; var CallHandlerPhase = 8; var ReturnComponent = 9; var Fragment = 10; // Don't change these two values: var NoEffect = 0; // 0b00000000 var PerformedWork = 1; // 0b00000001 // You can change the rest (and add more). var Placement = 2; // 0b00000010 var Update = 4; // 0b00000100 var PlacementAndUpdate = 6; // 0b00000110 var Deletion = 8; // 0b00001000 var ContentReset = 16; // 0b00010000 var Callback = 32; // 0b00100000 var Err = 64; // 0b01000000 var Ref = 128; // 0b10000000 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 owner = ReactCurrentOwner.current; if (owner !== null && owner.tag === ClassComponent) { var ownerFiber = owner; var instance = ownerFiber.stateNode; warning(instance._warnedAboutRefsInRender, '%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentName(ownerFiber) || 'A component'); instance._warnedAboutRefsInRender = true; } } var fiber = get(component); if (!fiber) { return false; } return isFiberMountedImpl(fiber) === MOUNTED; } function assertIsMounted(fiber) { !(isFiberMountedImpl(fiber) === MOUNTED) ? invariant(false, 'Unable to find node on an unmounted component.') : 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) ? invariant(false, 'Unable to find node on an unmounted component.') : 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. invariant(false, 'Unable to find node on an unmounted component.'); } if (a['return'] !== b['return']) { // The return pointer of A and the return pointer of B point to different // fibers. We assume that return pointers never criss-cross, so A must // belong to the child set of A.return, and B must belong to the child // set of B.return. a = parentA; b = parentB; } else { // The return pointers point to the same fiber. We'll have to use the // default, slow path: scan the child sets of each parent alternate to see // which child belongs to which set. // // Search parent A's child set var didFindChild = false; var _child = parentA.child; while (_child) { if (_child === a) { didFindChild = true; a = parentA; b = parentB; break; } if (_child === b) { didFindChild = true; b = parentA; a = parentB; break; } _child = _child.sibling; } if (!didFindChild) { // Search parent B's child set _child = parentB.child; while (_child) { if (_child === a) { didFindChild = true; a = parentB; b = parentA; break; } if (_child === b) { didFindChild = true; b = parentB; a = parentA; break; } _child = _child.sibling; } !didFindChild ? invariant(false, 'Child was not found in either parent set. This indicates a bug in React related to the return pointer. Please file an issue.') : void 0; } } !(a.alternate === b) ? invariant(false, 'Return fibers should always be each others\' alternates. This error is likely caused by a bug in React. Please file an issue.') : void 0; } // If the root is not a host container, we're in a disconnected tree. I.e. // unmounted. !(a.tag === HostRoot) ? invariant(false, 'Unable to find node on an unmounted component.') : 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; } function findCurrentHostFiberWithNoPortals(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.tag !== HostPortal) { 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; } var valueStack = []; { var fiberStack = []; } var index = -1; function createCursor(defaultValue) { return { current: defaultValue }; } function pop(cursor, fiber) { if (index < 0) { { warning(false, 'Unexpected pop.'); } return; } { if (fiber !== fiberStack[index]) { warning(false, 'Unexpected Fiber popped.'); } } cursor.current = valueStack[index]; valueStack[index] = null; { fiberStack[index] = null; } index--; } function push(cursor, value, fiber) { index++; valueStack[index] = cursor.current; { fiberStack[index] = fiber; } cursor.current = value; } function reset() { while (index > -1) { valueStack[index] = null; { fiberStack[index] = null; } index--; } } var describeComponentFrame = function (name, source, ownerName) { return '\n in ' + (name || 'Unknown') + (source ? ' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' + source.lineNumber + ')' : ownerName ? ' (created by ' + ownerName + ')' : ''); }; function describeFiber(fiber) { switch (fiber.tag) { case IndeterminateComponent: case FunctionalComponent: case ClassComponent: case HostComponent: var owner = fiber._debugOwner; var source = fiber._debugSource; var name = getComponentName(fiber); var ownerName = null; if (owner) { ownerName = getComponentName(owner); } return describeComponentFrame(name, source, ownerName); default: return ''; } } // This function can only be called with a work-in-progress fiber and // only during begin or complete phase. Do not call it under any other // circumstances. function getStackAddendumByWorkInProgressFiber(workInProgress) { var info = ''; var node = workInProgress; do { info += describeFiber(node); // Otherwise this return pointer might point to the wrong tree: node = node['return']; } while (node); return info; } function getCurrentFiberOwnerName() { { var fiber = ReactDebugCurrentFiber.current; if (fiber === null) { return null; } var owner = fiber._debugOwner; if (owner !== null && typeof owner !== 'undefined') { return getComponentName(owner); } } return null; } function getCurrentFiberStackAddendum() { { var fiber = ReactDebugCurrentFiber.current; if (fiber === null) { return null; } // Safe because if current fiber exists, we are reconciling, // and it is guaranteed to be the work-in-progress version. return getStackAddendumByWorkInProgressFiber(fiber); } return null; } function resetCurrentFiber() { ReactDebugCurrentFrame.getCurrentStack = null; ReactDebugCurrentFiber.current = null; ReactDebugCurrentFiber.phase = null; } function setCurrentFiber(fiber) { ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum; ReactDebugCurrentFiber.current = fiber; ReactDebugCurrentFiber.phase = null; } function setCurrentPhase(phase) { ReactDebugCurrentFiber.phase = phase; } var ReactDebugCurrentFiber = { current: null, phase: null, resetCurrentFiber: resetCurrentFiber, setCurrentFiber: setCurrentFiber, setCurrentPhase: setCurrentPhase, getCurrentFiberOwnerName: getCurrentFiberOwnerName, getCurrentFiberStackAddendum: getCurrentFiberStackAddendum }; // 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$$1) { var prefix = warning$$1 ? warningEmoji + ' ' : reactEmoji + ' '; var suffix = warning$$1 ? ' Warning: ' + warning$$1 : ''; 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$$1) { var formattedMarkName = formatMarkName(markName); var formattedLabel = formatLabel(label, warning$$1); 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) || '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) || '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$$1) { var componentName = getComponentName(fiber) || '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$$1); }; 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 ReturnComponent: case Fragment: 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) { if (enableUserTimingAPI) { if (supportsUserTiming) { isWaitingForCallback = false; var warning$$1 = didExpire ? 'React was blocked by main thread' : null; endMark('(Waiting for async callback...)', '(Waiting for async callback...)', warning$$1); } } } 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$$1 = 'An error was thrown inside this error boundary'; endFiberMark(fiber, null, warning$$1); } } 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$$1 = hasScheduledUpdateInCurrentPhase ? 'Scheduled a cascading update' : null; endFiberMark(currentPhaseFiber, currentPhase, warning$$1); } 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) { if (enableUserTimingAPI) { if (!supportsUserTiming) { return; } var warning$$1 = null; if (interruptedBy !== null) { if (interruptedBy.tag === HostRoot) { warning$$1 = 'A top-level update interrupted the previous render'; } else { var componentName = getComponentName(interruptedBy) || 'Unknown'; warning$$1 = 'An update to ' + componentName + ' interrupted the previous render'; } } else if (commitCountInCurrentWorkLoop > 1) { warning$$1 = 'There were cascading updates'; } commitCountInCurrentWorkLoop = 0; // Pause any measurements until the next loop. pauseTimers(); endMark('(React Tree Reconciliation)', '(React Tree Reconciliation)', warning$$1); } } 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$$1 = null; if (hasScheduledUpdateInCurrentCommit) { warning$$1 = 'Lifecycle hook scheduled a cascading update'; } else if (commitCountInCurrentWorkLoop > 0) { warning$$1 = 'Caused by a cascading update in earlier commit'; } hasScheduledUpdateInCurrentCommit = false; commitCountInCurrentWorkLoop++; isCommitting = false; labelsInCurrentCommit.clear(); endMark('(Committing Changes)', '(Committing Changes)', warning$$1); } } 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 warnedAboutMissingGetChildContext = {}; } // A cursor to the current merged context object on the stack. var contextStackCursor = createCursor(emptyObject); // 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 = emptyObject; function getUnmaskedContext(workInProgress) { var hasOwnContext = isContextProvider(workInProgress); if (hasOwnContext) { // If the fiber is a context provider itself, when we read its context // we 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 emptyObject; } // Avoid recreating masked context unless unmasked context has changed. // Failing to do this will result in unnecessary calls to componentWillReceiveProps. // This may trigger infinite loops if componentWillReceiveProps calls setState. var instance = workInProgress.stateNode; if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) { return instance.__reactInternalMemoizedMaskedChildContext; } var context = {}; for (var key in contextTypes) { context[key] = unmaskedContext[key]; } { var name = getComponentName(workInProgress) || 'Unknown'; checkPropTypes(contextTypes, context, 'context', name, ReactDebugCurrentFiber.getCurrentFiberStackAddendum); } // Cache unmasked context so we can avoid recreating masked context unless necessary. // Context is created before the class component is instantiated so check for instance. if (instance) { cacheContext(workInProgress, unmaskedContext, context); } return context; } function hasContextChanged() { return didPerformWorkStackCursor.current; } function isContextConsumer(fiber) { return fiber.tag === ClassComponent && fiber.type.contextTypes != null; } function isContextProvider(fiber) { return fiber.tag === ClassComponent && fiber.type.childContextTypes != null; } function popContextProvider(fiber) { if (!isContextProvider(fiber)) { return; } pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } function popTopLevelContextObject(fiber) { pop(didPerformWorkStackCursor, fiber); pop(contextStackCursor, fiber); } function pushTopLevelContextObject(fiber, context, didChange) { !(contextStackCursor.cursor == null) ? invariant(false, 'Unexpected context found on stack. This error is likely caused by a bug in React. Please file an issue.') : void 0; push(contextStackCursor, context, fiber); push(didPerformWorkStackCursor, didChange, fiber); } function processChildContext(fiber, parentContext) { var instance = fiber.stateNode; var childContextTypes = fiber.type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future. // It has only been added in Fiber to match the (unintentional) behavior in Stack. if (typeof instance.getChildContext !== 'function') { { var componentName = getComponentName(fiber) || 'Unknown'; if (!warnedAboutMissingGetChildContext[componentName]) { warnedAboutMissingGetChildContext[componentName] = true; warning(false, '%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName); } } return parentContext; } var childContext = void 0; { ReactDebugCurrentFiber.setCurrentPhase('getChildContext'); } startPhaseTimer(fiber, 'getChildContext'); childContext = instance.getChildContext(); stopPhaseTimer(); { ReactDebugCurrentFiber.setCurrentPhase(null); } for (var contextKey in childContext) { !(contextKey in childContextTypes) ? invariant(false, '%s.getChildContext(): key "%s" is not defined in childContextTypes.', getComponentName(fiber) || 'Unknown', contextKey) : void 0; } { var name = getComponentName(fiber) || 'Unknown'; checkPropTypes(childContextTypes, childContext, 'child context', name, // In practice, there is one case in which we won't get a stack. It's when // somebody calls unstable_renderSubtreeIntoContainer() and we process // context from the parent component instance. The stack will be missing // because it's outside of the reconciliation, and so the pointer has not // been set. This is rare and doesn't matter. We'll also remove that API. ReactDebugCurrentFiber.getCurrentFiberStackAddendum); } return _assign({}, parentContext, childContext); } function pushContextProvider(workInProgress) { if (!isContextProvider(workInProgress)) { return false; } 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 || emptyObject; // 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, didChange) { var instance = workInProgress.stateNode; !instance ? invariant(false, 'Expected to have an instance by this point. This error is likely caused by a bug in React. Please file an issue.') : 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, 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 resetContext() { previousContext = emptyObject; contextStackCursor.current = emptyObject; didPerformWorkStackCursor.current = false; } function findCurrentUnmaskedContext(fiber) { // Currently this is only used with renderSubtreeIntoContainer; not sure if it // makes sense elsewhere !(isFiberMounted(fiber) && fiber.tag === ClassComponent) ? invariant(false, 'Expected subtree parent to be a mounted class component. This error is likely caused by a bug in React. Please file an issue.') : void 0; var node = fiber; while (node.tag !== HostRoot) { if (isContextProvider(node)) { return node.stateNode.__reactInternalMemoizedMergedChildContext; } var parent = node['return']; !parent ? invariant(false, 'Found unexpected detached subtree parent. This error is likely caused by a bug in React. Please file an issue.') : void 0; node = parent; } return node.stateNode.context; } var NoWork = 0; // TODO: Use an opaque type once ESLint et al support the syntax var Sync = 1; var Never = 2147483647; // Max int32: Math.pow(2, 31) - 1 var UNIT_SIZE = 10; var MAGIC_NUMBER_OFFSET = 2; // 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 (ms / UNIT_SIZE | 0) + MAGIC_NUMBER_OFFSET; } function expirationTimeToMs(expirationTime) { return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE; } function ceiling(num, precision) { return ((num / precision | 0) + 1) * precision; } function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) { return ceiling(currentTime + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE); } var NoContext = 0; var AsyncUpdates = 1; { var hasBadMapPolyfill = false; try { var nonExtensibleObject = Object.preventExtensions({}); /* eslint-disable no-new */ /* eslint-enable no-new */ } catch (e) { // TODO: Consider warning about bad polyfills hasBadMapPolyfill = true; } } // A Fiber is work on a Component that needs to be done or was done. There can // be more than one per component. { var debugCounter = 1; } function FiberNode(tag, key, internalContextTag) { // Instance this.tag = tag; this.key = key; this.type = null; this.stateNode = null; // Fiber this['return'] = null; this.child = null; this.sibling = null; this.index = 0; this.ref = null; this.pendingProps = null; this.memoizedProps = null; this.updateQueue = null; this.memoizedState = null; this.internalContextTag = internalContextTag; // Effects this.effectTag = NoEffect; this.nextEffect = null; this.firstEffect = null; this.lastEffect = null; this.expirationTime = NoWork; this.alternate = null; { this._debugID = debugCounter++; this._debugSource = null; this._debugOwner = null; this._debugIsCurrentlyTiming = false; if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { Object.preventExtensions(this); } } } // This is a constructor function, rather than a POJO constructor, still // please ensure we do the following: // 1) Nobody should add any instance methods on this. Instance methods can be // more difficult to predict when they get optimized and they are almost // never inlined properly in static compilers. // 2) Nobody should rely on `instanceof Fiber` for type testing. We should // always know when it is a fiber. // 3) We might want to experiment with using numeric keys since they are easier // to optimize in a non-JIT environment. // 4) We can easily go from a constructor to a createFiber object literal if that // is faster. // 5) It should be easy to port this to a C struct and keep a C implementation // compatible. var createFiber = function (tag, key, internalContextTag) { // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors return new FiberNode(tag, key, internalContextTag); }; function shouldConstruct(Component) { return !!(Component.prototype && Component.prototype.isReactComponent); } // 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, current.key, current.internalContextTag); workInProgress.type = current.type; workInProgress.stateNode = current.stateNode; { // DEV-only fields workInProgress._debugID = current._debugID; workInProgress._debugSource = current._debugSource; workInProgress._debugOwner = current._debugOwner; } workInProgress.alternate = current; current.alternate = workInProgress; } else { // 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; } workInProgress.expirationTime = expirationTime; workInProgress.pendingProps = pendingProps; workInProgress.child = current.child; workInProgress.memoizedProps = current.memoizedProps; workInProgress.memoizedState = current.memoizedState; workInProgress.updateQueue = current.updateQueue; // These will be overridden during the parent's reconciliation workInProgress.sibling = current.sibling; workInProgress.index = current.index; workInProgress.ref = current.ref; return workInProgress; } function createHostRootFiber() { var fiber = createFiber(HostRoot, null, NoContext); return fiber; } function createFiberFromElement(element, internalContextTag, expirationTime) { var owner = null; { owner = element._owner; } var fiber = void 0; var type = element.type, key = element.key; if (typeof type === 'function') { fiber = shouldConstruct(type) ? createFiber(ClassComponent, key, internalContextTag) : createFiber(IndeterminateComponent, key, internalContextTag); fiber.type = type; fiber.pendingProps = element.props; } else if (typeof type === 'string') { fiber = createFiber(HostComponent, key, internalContextTag); fiber.type = type; fiber.pendingProps = element.props; } else if (typeof type === 'object' && type !== null && typeof type.tag === 'number') { // Currently assumed to be a continuation and therefore is a fiber already. // TODO: The yield system is currently broken for updates in some cases. // The reified yield stores a fiber, but we don't know which fiber that is; // the current or a workInProgress? When the continuation gets rendered here // we don't know if we can reuse that fiber or if we need to clone it. // There is probably a clever way to restructure this. fiber = type; fiber.pendingProps = element.props; } else { var info = ''; { if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { info += ' You likely forgot to export your component from the file ' + "it's defined in, or you might have mixed up default and named imports."; } var ownerName = owner ? getComponentName(owner) : null; if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } } invariant(false, 'Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s', type == null ? type : typeof type, info); } { fiber._debugSource = element._source; fiber._debugOwner = element._owner; } fiber.expirationTime = expirationTime; return fiber; } function createFiberFromFragment(elements, internalContextTag, expirationTime, key) { var fiber = createFiber(Fragment, key, internalContextTag); fiber.pendingProps = elements; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromText(content, internalContextTag, expirationTime) { var fiber = createFiber(HostText, null, internalContextTag); fiber.pendingProps = content; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromHostInstanceForDeletion() { var fiber = createFiber(HostComponent, null, NoContext); fiber.type = 'DELETED'; return fiber; } function createFiberFromCall(call, internalContextTag, expirationTime) { var fiber = createFiber(CallComponent, call.key, internalContextTag); fiber.type = call.handler; fiber.pendingProps = call; fiber.expirationTime = expirationTime; return fiber; } function createFiberFromReturn(returnNode, internalContextTag, expirationTime) { var fiber = createFiber(ReturnComponent, null, internalContextTag); fiber.expirationTime = expirationTime; return fiber; } function createFiberFromPortal(portal, internalContextTag, expirationTime) { var fiber = createFiber(HostPortal, portal.key, internalContextTag); fiber.pendingProps = portal.children || []; fiber.expirationTime = expirationTime; fiber.stateNode = { containerInfo: portal.containerInfo, pendingChildren: null, // Used by persistent updates implementation: portal.implementation }; return fiber; } function createFiberRoot(containerInfo, hydrate) { // Cyclic construction. This cheats the type system right now because // stateNode is any. var uninitializedFiber = createHostRootFiber(); var root = { current: uninitializedFiber, containerInfo: containerInfo, pendingChildren: null, remainingExpirationTime: NoWork, isReadyForCommit: false, finishedWork: null, context: null, pendingContext: null, hydrate: hydrate, nextScheduledRoot: null }; uninitializedFiber.stateNode = root; return root; } var onCommitFiberRoot = null; var onCommitFiberUnmount = null; var hasLoggedError = false; function catchErrors(fn) { return function (arg) { try { return fn(arg); } catch (err) { if (true && !hasLoggedError) { hasLoggedError = true; warning(false, 'React DevTools encountered an error: %s', err); } } }; } 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) { { warning(false, 'The installed version of React DevTools is too old and will not work ' + 'with the current version of React. Please update React DevTools. ' + 'https://fb.me/react-devtools'); } // DevTools exists, even though it doesn't support Fiber. 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. { warning(false, 'React DevTools encountered an error: %s.', err); } } // DevTools exists return true; } function onCommitRoot(root) { if (typeof onCommitFiberRoot === 'function') { onCommitFiberRoot(root); } } function onCommitUnmount(fiber) { if (typeof onCommitFiberUnmount === 'function') { onCommitFiberUnmount(fiber); } } var ReactErrorUtils = { // Used by Fiber to simulate a try-catch. _caughtError: null, _hasCaughtError: false, // Used by event system to capture/rethrow the first error. _rethrowError: null, _hasRethrowError: false, injection: { injectErrorUtils: function (injectedErrorUtils) { !(typeof injectedErrorUtils.invokeGuardedCallback === 'function') ? invariant(false, 'Injected invokeGuardedCallback() must be a function.') : void 0; invokeGuardedCallback$1 = injectedErrorUtils.invokeGuardedCallback; } }, /** * 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 */ invokeGuardedCallback: function (name, func, context, a, b, c, d, e, f) { invokeGuardedCallback$1.apply(ReactErrorUtils, arguments); }, /** * 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 */ invokeGuardedCallbackAndCatchFirstError: function (name, func, context, a, b, c, d, e, f) { ReactErrorUtils.invokeGuardedCallback.apply(this, arguments); if (ReactErrorUtils.hasCaughtError()) { var error = ReactErrorUtils.clearCaughtError(); if (!ReactErrorUtils._hasRethrowError) { ReactErrorUtils._hasRethrowError = true; ReactErrorUtils._rethrowError = error; } } }, /** * During execution of guarded functions we will capture the first error which * we will rethrow to be handled by the top level error handler. */ rethrowCaughtError: function () { return rethrowCaughtError.apply(ReactErrorUtils, arguments); }, hasCaughtError: function () { return ReactErrorUtils._hasCaughtError; }, clearCaughtError: function () { if (ReactErrorUtils._hasCaughtError) { var error = ReactErrorUtils._caughtError; ReactErrorUtils._caughtError = null; ReactErrorUtils._hasCaughtError = false; return error; } else { invariant(false, 'clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue.'); } } }; var invokeGuardedCallback$1 = function (name, func, context, a, b, c, d, e, f) { ReactErrorUtils._hasCaughtError = false; ReactErrorUtils._caughtError = null; var funcArgs = Array.prototype.slice.call(arguments, 3); try { func.apply(context, funcArgs); } catch (error) { ReactErrorUtils._caughtError = error; ReactErrorUtils._hasCaughtError = true; } }; { // In DEV mode, we swap out invokeGuardedCallback for a special version // that plays more nicely with the browser's DevTools. The idea is to preserve // "Pause on exceptions" behavior. Because React wraps all user-provided // functions in invokeGuardedCallback, and the production version of // invokeGuardedCallback uses a try-catch, all user exceptions are treated // like caught exceptions, and the DevTools won't pause unless the developer // takes the extra step of enabling pause on caught exceptions. This is // untintuitive, though, because even though React has caught the error, from // the developer's perspective, the error is uncaught. // // To preserve the expected "Pause on exceptions" behavior, we don't use a // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake // DOM node, and call the user-provided callback from inside an event handler // for that fake event. If the callback throws, the error is "captured" using // a global event handler. But because the error happens in a different // event loop context, it does not interrupt the normal program flow. // Effectively, this gives us try-catch behavior without actually using // try-catch. Neat! // Check that the browser supports the APIs we need to implement our special // DEV version of invokeGuardedCallback if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') { var fakeNode = document.createElement('react'); var invokeGuardedCallbackDev = function (name, func, context, a, b, c, d, e, f) { // Keeps track of whether the user-provided callback threw an error. We // set this to true at the beginning, then set it to false right after // calling the function. If the function errors, `didError` will never be // set to false. This strategy works even if the browser is flaky and // fails to call our global error handler, because it doesn't rely on // the error event at all. var didError = true; // Create an event handler for our fake event. We will synchronously // dispatch our fake event using `dispatchEvent`. Inside the handler, we // call the user-provided callback. var funcArgs = Array.prototype.slice.call(arguments, 3); function callCallback() { // We immediately remove the callback from event listeners so that // nested `invokeGuardedCallback` calls do not clash. Otherwise, a // nested call would trigger the fake event handlers of any call higher // in the stack. fakeNode.removeEventListener(evtType, callCallback, false); func.apply(context, funcArgs); didError = false; } // Create a global error event handler. We use this to capture the value // that was thrown. It's possible that this error handler will fire more // than once; for example, if non-React code also calls `dispatchEvent` // and a handler for that event throws. We should be resilient to most of // those cases. Even if our error event handler fires more than once, the // last error event is always used. If the callback actually does error, // we know that the last error event is the correct one, because it's not // possible for anything else to have happened in between our callback // erroring and the code that follows the `dispatchEvent` call below. If // the callback doesn't error, but the error event was fired, we know to // ignore it because `didError` will be false, as described above. var error = void 0; // Use this to track whether the error event is ever called. var didSetError = false; var isCrossOriginError = false; function onError(event) { error = event.error; didSetError = true; if (error === null && event.colno === 0 && event.lineno === 0) { isCrossOriginError = true; } } // Create a fake event type. var evtType = 'react-' + (name ? name : 'invokeguardedcallback'); // Attach our event handlers window.addEventListener('error', onError); fakeNode.addEventListener(evtType, callCallback, false); // Synchronously dispatch our fake event. If the user-provided function // errors, it will trigger our global error handler. var evt = document.createEvent('Event'); evt.initEvent(evtType, false, false); fakeNode.dispatchEvent(evt); if (didError) { if (!didSetError) { // The callback errored, but the error event never fired. error = new Error('An error was thrown inside one of your components, but React ' + "doesn't know what it was. This is likely due to browser " + 'flakiness. React does its best to preserve the "Pause on ' + 'exceptions" behavior of the DevTools, which requires some ' + "DEV-mode only tricks. It's possible that these don't work in " + 'your browser. Try triggering the error in production mode, ' + 'or switching to a modern browser. If you suspect that this is ' + 'actually an issue with React, please file an issue.'); } else if (isCrossOriginError) { error = new Error("A cross-origin error was thrown. React doesn't have access to " + 'the actual error object in development. ' + 'See https://fb.me/react-crossorigin-error for more information.'); } ReactErrorUtils._hasCaughtError = true; ReactErrorUtils._caughtError = error; } else { ReactErrorUtils._hasCaughtError = false; ReactErrorUtils._caughtError = null; } // Remove our event listeners window.removeEventListener('error', onError); }; invokeGuardedCallback$1 = invokeGuardedCallbackDev; } } var rethrowCaughtError = function () { if (ReactErrorUtils._hasRethrowError) { var error = ReactErrorUtils._rethrowError; ReactErrorUtils._rethrowError = null; ReactErrorUtils._hasRethrowError = false; throw error; } }; { var didWarnUpdateInsideUpdate = false; } // Callbacks are not validated until invocation // Singly linked-list of updates. When an update is scheduled, it is added to // the queue of the current fiber and the work-in-progress fiber. The two queues // are separate but they share a persistent structure. // // During reconciliation, updates are removed from the work-in-progress fiber, // but they remain on the current fiber. That ensures that if a work-in-progress // is aborted, the aborted updates are recovered by cloning from current. // // The work-in-progress queue is always a subset of the current queue. // // When the tree is committed, the work-in-progress becomes the current. function createUpdateQueue(baseState) { var queue = { baseState: baseState, expirationTime: NoWork, first: null, last: null, callbackList: null, hasForceUpdate: false, isInitialized: false }; { queue.isProcessing = false; } return queue; } function insertUpdateIntoQueue(queue, update) { // Append the update to the end of the list. if (queue.last === null) { // Queue is empty queue.first = queue.last = update; } else { queue.last.next = update; queue.last = update; } if (queue.expirationTime === NoWork || queue.expirationTime > update.expirationTime) { queue.expirationTime = update.expirationTime; } } function insertUpdateIntoFiber(fiber, update) { // We'll have at least one and at most two distinct update queues. var alternateFiber = fiber.alternate; var queue1 = fiber.updateQueue; if (queue1 === null) { // TODO: We don't know what the base state will be until we begin work. // It depends on which fiber is the next current. Initialize with an empty // base state, then set to the memoizedState when rendering. Not super // happy with this approach. queue1 = fiber.updateQueue = createUpdateQueue(null); } var queue2 = void 0; if (alternateFiber !== null) { queue2 = alternateFiber.updateQueue; if (queue2 === null) { queue2 = alternateFiber.updateQueue = createUpdateQueue(null); } } else { queue2 = null; } queue2 = queue2 !== queue1 ? queue2 : null; // Warn if an update is scheduled from inside an updater function. { if ((queue1.isProcessing || queue2 !== null && queue2.isProcessing) && !didWarnUpdateInsideUpdate) { warning(false, 'An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.'); didWarnUpdateInsideUpdate = true; } } // If there's only one queue, add the update to that queue and exit. if (queue2 === null) { insertUpdateIntoQueue(queue1, update); return; } // If either queue is empty, we need to add to both queues. if (queue1.last === null || queue2.last === null) { insertUpdateIntoQueue(queue1, update); insertUpdateIntoQueue(queue2, update); return; } // If both lists are not empty, the last update is the same for both lists // because of structural sharing. So, we should only append to one of // the lists. insertUpdateIntoQueue(queue1, update); // But we still need to update the `last` pointer of queue2. queue2.last = update; } function getUpdateExpirationTime(fiber) { if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) { return NoWork; } var updateQueue = fiber.updateQueue; if (updateQueue === null) { return NoWork; } return updateQueue.expirationTime; } function getStateFromUpdate(update, instance, prevState, props) { var partialState = update.partialState; if (typeof partialState === 'function') { var updateFn = partialState; // Invoke setState callback an extra time to help detect side-effects. if (debugRenderPhaseSideEffects) { updateFn.call(instance, prevState, props); } return updateFn.call(instance, prevState, props); } else { return partialState; } } function processUpdateQueue(current, workInProgress, queue, instance, props, renderExpirationTime) { if (current !== null && current.updateQueue === queue) { // We need to create a work-in-progress queue, by cloning the current queue. var currentQueue = queue; queue = workInProgress.updateQueue = { baseState: currentQueue.baseState, expirationTime: currentQueue.expirationTime, first: currentQueue.first, last: currentQueue.last, isInitialized: currentQueue.isInitialized, // These fields are no longer valid because they were already committed. // Reset them. callbackList: null, hasForceUpdate: false }; } { // Set this flag so we can warn if setState is called inside the update // function of another setState. queue.isProcessing = true; } // Reset the remaining expiration time. If we skip over any updates, we'll // increase this accordingly. queue.expirationTime = NoWork; // TODO: We don't know what the base state will be until we begin work. // It depends on which fiber is the next current. Initialize with an empty // base state, then set to the memoizedState when rendering. Not super // happy with this approach. var state = void 0; if (queue.isInitialized) { state = queue.baseState; } else { state = queue.baseState = workInProgress.memoizedState; queue.isInitialized = true; } var dontMutatePrevState = true; var update = queue.first; var didSkip = false; while (update !== null) { var updateExpirationTime = update.expirationTime; if (updateExpirationTime > renderExpirationTime) { // This update does not have sufficient priority. Skip it. var remainingExpirationTime = queue.expirationTime; if (remainingExpirationTime === NoWork || remainingExpirationTime > updateExpirationTime) { // Update the remaining expiration time. queue.expirationTime = updateExpirationTime; } if (!didSkip) { didSkip = true; queue.baseState = state; } // Continue to the next update. update = update.next; continue; } // This update does have sufficient priority. // If no previous updates were skipped, drop this update from the queue by // advancing the head of the list. if (!didSkip) { queue.first = update.next; if (queue.first === null) { queue.last = null; } } // Process the update var _partialState = void 0; if (update.isReplace) { state = getStateFromUpdate(update, instance, state, props); dontMutatePrevState = true; } else { _partialState = getStateFromUpdate(update, instance, state, props); if (_partialState) { if (dontMutatePrevState) { // $FlowFixMe: Idk how to type this properly. state = _assign({}, state, _partialState); } else { state = _assign(state, _partialState); } dontMutatePrevState = false; } } if (update.isForced) { queue.hasForceUpdate = true; } if (update.callback !== null) { // Append to list of callbacks. var _callbackList = queue.callbackList; if (_callbackList === null) { _callbackList = queue.callbackList = []; } _callbackList.push(update); } update = update.next; } if (queue.callbackList !== null) { workInProgress.effectTag |= Callback; } else if (queue.first === null && !queue.hasForceUpdate) { // The queue is empty. We can reset it. workInProgress.updateQueue = null; } if (!didSkip) { didSkip = true; queue.baseState = state; } { // No longer processing. queue.isProcessing = false; } return state; } function commitCallbacks(queue, context) { var callbackList = queue.callbackList; if (callbackList === null) { return; } // Set the list to null to make sure they don't get called more than once. queue.callbackList = null; for (var i = 0; i < callbackList.length; i++) { var update = callbackList[i]; var _callback = update.callback; // This update might be processed again. Clear the callback so it's only // called once. update.callback = null; !(typeof _callback === 'function') ? invariant(false, 'Invalid argument passed as callback. Expected a function. Instead received: %s', _callback) : void 0; _callback.call(context); } } var fakeInternalInstance = {}; var isArray = Array.isArray; { var didWarnAboutStateAssignmentForComponent = {}; var warnOnInvalidCallback = function (callback, callerName) { warning(callback === null || typeof callback === 'function', '%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback); }; // This is so gross but it's at least non-critical and can be removed if // it causes problems. This is meant to give a nicer error message for // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component, // ...)) which otherwise throws a "_processChildContext is not a function" // exception. Object.defineProperty(fakeInternalInstance, '_processChildContext', { enumerable: false, value: function () { invariant(false, '_processChildContext is not available in React 16+. This likely means you have multiple copies of React and are attempting to nest a React 15 tree inside a React 16 tree using unstable_renderSubtreeIntoContainer, which isn\'t supported. Try to make sure you have only one copy of React (and ideally, switch to ReactDOM.createPortal).'); } }); Object.freeze(fakeInternalInstance); } var ReactFiberClassComponent = function (scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState) { // Class component state updater var updater = { isMounted: isMounted, enqueueSetState: function (instance, partialState, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'setState'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: partialState, callback: callback, isReplace: false, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); }, enqueueReplaceState: function (instance, state, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'replaceState'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: state, callback: callback, isReplace: true, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); }, enqueueForceUpdate: function (instance, callback) { var fiber = get(instance); callback = callback === undefined ? null : callback; { warnOnInvalidCallback(callback, 'forceUpdate'); } var expirationTime = computeExpirationForFiber(fiber); var update = { expirationTime: expirationTime, partialState: null, callback: callback, isReplace: false, isForced: true, nextCallback: null, next: null }; insertUpdateIntoFiber(fiber, update); scheduleWork(fiber, expirationTime); } }; function checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext) { if (oldProps === null || workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate) { // If the workInProgress already has an Update effect, return true return true; } var instance = workInProgress.stateNode; var type = workInProgress.type; if (typeof instance.shouldComponentUpdate === 'function') { startPhaseTimer(workInProgress, 'shouldComponentUpdate'); var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.shouldComponentUpdate(newProps, newState, newContext); } { warning(shouldUpdate !== undefined, '%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentName(workInProgress) || 'Unknown'); } return shouldUpdate; } if (type.prototype && type.prototype.isPureReactComponent) { return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState); } return true; } function checkClassInstance(workInProgress) { var instance = workInProgress.stateNode; var type = workInProgress.type; { var name = getComponentName(workInProgress); var renderPresent = instance.render; if (!renderPresent) { if (type.prototype && typeof type.prototype.render === 'function') { warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name); } else { warning(false, '%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name); } } var noGetInitialStateOnES6 = !instance.getInitialState || instance.getInitialState.isReactClassApproved || instance.state; warning(noGetInitialStateOnES6, 'getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name); var noGetDefaultPropsOnES6 = !instance.getDefaultProps || instance.getDefaultProps.isReactClassApproved; warning(noGetDefaultPropsOnES6, 'getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name); var noInstancePropTypes = !instance.propTypes; warning(noInstancePropTypes, 'propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name); var noInstanceContextTypes = !instance.contextTypes; warning(noInstanceContextTypes, 'contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name); var noComponentShouldUpdate = typeof instance.componentShouldUpdate !== 'function'; warning(noComponentShouldUpdate, '%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name); if (type.prototype && type.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') { warning(false, '%s has a method called shouldComponentUpdate(). ' + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentName(workInProgress) || 'A pure component'); } var noComponentDidUnmount = typeof instance.componentDidUnmount !== 'function'; warning(noComponentDidUnmount, '%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name); var noComponentDidReceiveProps = typeof instance.componentDidReceiveProps !== 'function'; warning(noComponentDidReceiveProps, '%s has a method called ' + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + 'If you meant to update the state in response to changing props, ' + 'use componentWillReceiveProps(). If you meant to fetch data or ' + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name); var noComponentWillRecieveProps = typeof instance.componentWillRecieveProps !== 'function'; warning(noComponentWillRecieveProps, '%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name); var hasMutatedProps = instance.props !== workInProgress.pendingProps; warning(instance.props === undefined || !hasMutatedProps, '%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name); var noInstanceDefaultProps = !instance.defaultProps; warning(noInstanceDefaultProps, 'Setting defaultProps as an instance property on %s is not supported and will be ignored.' + ' Instead, define defaultProps as a static property on %s.', name, name); } var state = instance.state; if (state && (typeof state !== 'object' || isArray(state))) { warning(false, '%s.state: must be set to an object or null', getComponentName(workInProgress)); } if (typeof instance.getChildContext === 'function') { warning(typeof workInProgress.type.childContextTypes === 'object', '%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', getComponentName(workInProgress)); } } function resetInputPointers(workInProgress, instance) { instance.props = workInProgress.memoizedProps; instance.state = workInProgress.memoizedState; } function adoptClassInstance(workInProgress, instance) { instance.updater = updater; workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates set(instance, workInProgress); { instance._reactInternalInstance = fakeInternalInstance; } } function constructClassInstance(workInProgress, props) { var ctor = workInProgress.type; var unmaskedContext = getUnmaskedContext(workInProgress); var needsContext = isContextConsumer(workInProgress); var context = needsContext ? getMaskedContext(workInProgress, unmaskedContext) : emptyObject; var instance = new ctor(props, context); adoptClassInstance(workInProgress, instance); // Cache unmasked context so we can avoid recreating masked context unless necessary. // ReactFiberContext usually updates this cache but can't for newly-created instances. if (needsContext) { cacheContext(workInProgress, unmaskedContext, context); } return instance; } function callComponentWillMount(workInProgress, instance) { startPhaseTimer(workInProgress, 'componentWillMount'); var oldState = instance.state; instance.componentWillMount(); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillMount(); } if (oldState !== instance.state) { { warning(false, '%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentName(workInProgress)); } updater.enqueueReplaceState(instance, instance.state, null); } } function callComponentWillReceiveProps(workInProgress, instance, newProps, newContext) { startPhaseTimer(workInProgress, 'componentWillReceiveProps'); var oldState = instance.state; instance.componentWillReceiveProps(newProps, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillReceiveProps(newProps, newContext); } if (instance.state !== oldState) { { var componentName = getComponentName(workInProgress) || 'Component'; if (!didWarnAboutStateAssignmentForComponent[componentName]) { warning(false, '%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName); didWarnAboutStateAssignmentForComponent[componentName] = true; } } updater.enqueueReplaceState(instance, instance.state, null); } } // Invokes the mount life-cycles on a previously never rendered instance. function mountClassInstance(workInProgress, renderExpirationTime) { var current = workInProgress.alternate; { checkClassInstance(workInProgress); } var instance = workInProgress.stateNode; var state = instance.state || null; var props = workInProgress.pendingProps; !props ? invariant(false, 'There must be pending props for an initial mount. This error is likely caused by a bug in React. Please file an issue.') : void 0; var unmaskedContext = getUnmaskedContext(workInProgress); instance.props = props; instance.state = workInProgress.memoizedState = state; instance.refs = emptyObject; instance.context = getMaskedContext(workInProgress, unmaskedContext); if (enableAsyncSubtreeAPI && workInProgress.type != null && workInProgress.type.prototype != null && workInProgress.type.prototype.unstable_isAsyncReactComponent === true) { workInProgress.internalContextTag |= AsyncUpdates; } if (typeof instance.componentWillMount === 'function') { callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's // process them now. var updateQueue = workInProgress.updateQueue; if (updateQueue !== null) { instance.state = processUpdateQueue(current, workInProgress, updateQueue, instance, props, renderExpirationTime); } } if (typeof instance.componentDidMount === 'function') { workInProgress.effectTag |= Update; } } // Called on a preexisting class instance. Returns false if a resumed render // could be reused. // function resumeMountClassInstance( // workInProgress: Fiber, // priorityLevel: PriorityLevel, // ): boolean { // const instance = workInProgress.stateNode; // resetInputPointers(workInProgress, instance); // let newState = workInProgress.memoizedState; // let newProps = workInProgress.pendingProps; // if (!newProps) { // // If there isn't any new props, then we'll reuse the memoized props. // // This could be from already completed work. // newProps = workInProgress.memoizedProps; // invariant( // newProps != null, // 'There should always be pending or memoized props. This error is ' + // 'likely caused by a bug in React. Please file an issue.', // ); // } // const newUnmaskedContext = getUnmaskedContext(workInProgress); // const newContext = getMaskedContext(workInProgress, newUnmaskedContext); // const oldContext = instance.context; // const oldProps = workInProgress.memoizedProps; // if ( // typeof instance.componentWillReceiveProps === 'function' && // (oldProps !== newProps || oldContext !== newContext) // ) { // callComponentWillReceiveProps( // workInProgress, // instance, // newProps, // newContext, // ); // } // // Process the update queue before calling shouldComponentUpdate // const updateQueue = workInProgress.updateQueue; // if (updateQueue !== null) { // newState = processUpdateQueue( // workInProgress, // updateQueue, // instance, // newState, // newProps, // priorityLevel, // ); // } // // TODO: Should we deal with a setState that happened after the last // // componentWillMount and before this componentWillMount? Probably // // unsupported anyway. // if ( // !checkShouldComponentUpdate( // workInProgress, // workInProgress.memoizedProps, // newProps, // workInProgress.memoizedState, // newState, // newContext, // ) // ) { // // Update the existing instance's state, props, and context pointers even // // though we're bailing out. // instance.props = newProps; // instance.state = newState; // instance.context = newContext; // return false; // } // // Update the input pointers now so that they are correct when we call // // componentWillMount // instance.props = newProps; // instance.state = newState; // instance.context = newContext; // if (typeof instance.componentWillMount === 'function') { // callComponentWillMount(workInProgress, instance); // // componentWillMount may have called setState. Process the update queue. // const newUpdateQueue = workInProgress.updateQueue; // if (newUpdateQueue !== null) { // newState = processUpdateQueue( // workInProgress, // newUpdateQueue, // instance, // newState, // newProps, // priorityLevel, // ); // } // } // if (typeof instance.componentDidMount === 'function') { // workInProgress.effectTag |= Update; // } // instance.state = newState; // return true; // } // Invokes the update life-cycles and returns false if it shouldn't rerender. function updateClassInstance(current, workInProgress, renderExpirationTime) { var instance = workInProgress.stateNode; resetInputPointers(workInProgress, instance); var oldProps = workInProgress.memoizedProps; var newProps = workInProgress.pendingProps; if (!newProps) { // If there aren't any new props, then we'll reuse the memoized props. // This could be from already completed work. newProps = oldProps; !(newProps != null) ? invariant(false, 'There should always be pending or memoized props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } var oldContext = instance.context; var newUnmaskedContext = getUnmaskedContext(workInProgress); var newContext = getMaskedContext(workInProgress, newUnmaskedContext); // 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. if (typeof instance.componentWillReceiveProps === 'function' && (oldProps !== newProps || oldContext !== newContext)) { callComponentWillReceiveProps(workInProgress, instance, newProps, newContext); } // Compute the next state using the memoized state and the update queue. var oldState = workInProgress.memoizedState; // TODO: Previous state can be null. var newState = void 0; if (workInProgress.updateQueue !== null) { newState = processUpdateQueue(current, workInProgress, workInProgress.updateQueue, instance, newProps, renderExpirationTime); } else { newState = oldState; } if (oldProps === newProps && oldState === newState && !hasContextChanged() && !(workInProgress.updateQueue !== null && workInProgress.updateQueue.hasForceUpdate)) { // 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; } } return false; } var shouldUpdate = checkShouldComponentUpdate(workInProgress, oldProps, newProps, oldState, newState, newContext); if (shouldUpdate) { if (typeof instance.componentWillUpdate === 'function') { startPhaseTimer(workInProgress, 'componentWillUpdate'); instance.componentWillUpdate(newProps, newState, newContext); stopPhaseTimer(); // Simulate an async bailout/interruption by invoking lifecycle twice. if (debugRenderPhaseSideEffects) { instance.componentWillUpdate(newProps, newState, newContext); } } if (typeof instance.componentDidUpdate === '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.componentDidUpdate === 'function') { if (oldProps !== current.memoizedProps || oldState !== current.memoizedState) { workInProgress.effectTag |= Update; } } // If shouldComponentUpdate returned false, we should still update the // memoized props/state to indicate that this work can be reused. memoizeProps(workInProgress, newProps); memoizeState(workInProgress, newState); } // Update the existing instance's state, props, and context pointers even // if shouldComponentUpdate returns false. instance.props = newProps; instance.state = newState; instance.context = newContext; return shouldUpdate; } return { adoptClassInstance: adoptClassInstance, constructClassInstance: constructClassInstance, mountClassInstance: mountClassInstance, // resumeMountClassInstance, updateClassInstance: updateClassInstance }; }; // 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_CALL_TYPE = hasSymbol ? Symbol['for']('react.call') : 0xeac8; var REACT_RETURN_TYPE = hasSymbol ? Symbol['for']('react.return') : 0xeac9; var REACT_PORTAL_TYPE = hasSymbol ? Symbol['for']('react.portal') : 0xeaca; var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol['for']('react.fragment') : 0xeacb; var MAYBE_ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; var FAUX_ITERATOR_SYMBOL = '@@iterator'; function getIteratorFn(maybeIterable) { if (maybeIterable === null || typeof maybeIterable === 'undefined') { return null; } var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; if (typeof maybeIterator === 'function') { return maybeIterator; } return null; } var getCurrentFiberStackAddendum$1 = ReactDebugCurrentFiber.getCurrentFiberStackAddendum; { var didWarnAboutMaps = false; /** * Warn if there's no key explicitly set on dynamic arrays of children or * object keys are not valid. This allows us to keep track of children between * updates. */ var ownerHasKeyUseWarning = {}; var ownerHasFunctionTypeWarning = {}; var warnForMissingKey = function (child) { if (child === null || typeof child !== 'object') { return; } if (!child._store || child._store.validated || child.key != null) { return; } !(typeof child._store === 'object') ? invariant(false, 'React Component in warnForMissingKey should have a _store. This error is likely caused by a bug in React. Please file an issue.') : void 0; child._store.validated = true; var currentComponentErrorInfo = 'Each child in an array or iterator should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.' + (getCurrentFiberStackAddendum$1() || ''); if (ownerHasKeyUseWarning[currentComponentErrorInfo]) { return; } ownerHasKeyUseWarning[currentComponentErrorInfo] = true; warning(false, 'Each child in an array or iterator should have a unique ' + '"key" prop. See https://fb.me/react-warning-keys for ' + 'more information.%s', getCurrentFiberStackAddendum$1()); }; } var isArray$1 = Array.isArray; function coerceRef(current, element) { var mixedRef = element.ref; if (mixedRef !== null && typeof mixedRef !== 'function') { if (element._owner) { var owner = element._owner; var inst = void 0; if (owner) { var ownerFiber = owner; !(ownerFiber.tag === ClassComponent) ? invariant(false, 'Stateless function components cannot have refs.') : void 0; inst = ownerFiber.stateNode; } !inst ? invariant(false, 'Missing owner for string ref %s. This error is likely caused by a bug in React. Please file an issue.', mixedRef) : void 0; var stringRef = '' + mixedRef; // Check if previous string ref matches new string ref if (current !== null && current.ref !== null && current.ref._stringRef === stringRef) { return current.ref; } var ref = function (value) { var refs = inst.refs === emptyObject ? inst.refs = {} : inst.refs; if (value === null) { delete refs[stringRef]; } else { refs[stringRef] = value; } }; ref._stringRef = stringRef; return ref; } else { !(typeof mixedRef === 'string') ? invariant(false, 'Expected ref to be a function or a string.') : void 0; !element._owner ? invariant(false, 'Element ref was specified as a string (%s) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).', mixedRef) : void 0; } } return mixedRef; } function throwOnInvalidObjectType(returnFiber, newChild) { if (returnFiber.type !== 'textarea') { var addendum = ''; { addendum = ' If you meant to render a collection of children, use an array ' + 'instead.' + (getCurrentFiberStackAddendum$1() || ''); } invariant(false, 'Objects are not valid as a React child (found: %s).%s', Object.prototype.toString.call(newChild) === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : newChild, addendum); } } function warnOnFunctionType() { var currentComponentErrorInfo = 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of from render. ' + 'Or maybe you meant to call this function rather than return it.' + (getCurrentFiberStackAddendum$1() || ''); if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) { return; } ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true; warning(false, 'Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of from render. ' + 'Or maybe you meant to call this function rather than return it.%s', getCurrentFiberStackAddendum$1() || ''); } // 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 = newFiber.alternate; if (current !== null) { var oldIndex = current.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, textContent, expirationTime) { if (current === null || current.tag !== HostText) { // Insert var created = createFiberFromText(textContent, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, textContent, expirationTime); existing['return'] = returnFiber; return existing; } } function updateElement(returnFiber, current, element, expirationTime) { if (current !== null && current.type === element.type) { // Move based on index var existing = useFiber(current, element.props, expirationTime); existing.ref = coerceRef(current, element); existing['return'] = returnFiber; { existing._debugSource = element._source; existing._debugOwner = element._owner; } return existing; } else { // Insert var created = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime); created.ref = coerceRef(current, element); created['return'] = returnFiber; return created; } } function updateCall(returnFiber, current, call, expirationTime) { // TODO: Should this also compare handler to determine whether to reuse? if (current === null || current.tag !== CallComponent) { // Insert var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Move based on index var existing = useFiber(current, call, expirationTime); existing['return'] = returnFiber; return existing; } } function updateReturn(returnFiber, current, returnNode, expirationTime) { if (current === null || current.tag !== ReturnComponent) { // Insert var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime); created.type = returnNode.value; created['return'] = returnFiber; return created; } else { // Move based on index var existing = useFiber(current, null, expirationTime); existing.type = returnNode.value; existing['return'] = returnFiber; return existing; } } function updatePortal(returnFiber, current, portal, expirationTime) { if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) { // Insert var created = createFiberFromPortal(portal, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, portal.children || [], expirationTime); existing['return'] = returnFiber; return existing; } } function updateFragment(returnFiber, current, fragment, expirationTime, key) { if (current === null || current.tag !== Fragment) { // Insert var created = createFiberFromFragment(fragment, returnFiber.internalContextTag, expirationTime, key); created['return'] = returnFiber; return created; } else { // Update var existing = useFiber(current, 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.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } if (typeof newChild === 'object' && newChild !== null) { switch (newChild.$$typeof) { case REACT_ELEMENT_TYPE: { if (newChild.type === REACT_FRAGMENT_TYPE) { var _created = createFiberFromFragment(newChild.props.children, returnFiber.internalContextTag, expirationTime, newChild.key); _created['return'] = returnFiber; return _created; } else { var _created2 = createFiberFromElement(newChild, returnFiber.internalContextTag, expirationTime); _created2.ref = coerceRef(null, newChild); _created2['return'] = returnFiber; return _created2; } } case REACT_CALL_TYPE: { var _created3 = createFiberFromCall(newChild, returnFiber.internalContextTag, expirationTime); _created3['return'] = returnFiber; return _created3; } case REACT_RETURN_TYPE: { var _created4 = createFiberFromReturn(newChild, returnFiber.internalContextTag, expirationTime); _created4.type = newChild.value; _created4['return'] = returnFiber; return _created4; } case REACT_PORTAL_TYPE: { var _created5 = createFiberFromPortal(newChild, returnFiber.internalContextTag, expirationTime); _created5['return'] = returnFiber; return _created5; } } if (isArray$1(newChild) || getIteratorFn(newChild)) { var _created6 = createFiberFromFragment(newChild, returnFiber.internalContextTag, expirationTime, null); _created6['return'] = returnFiber; return _created6; } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } 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_CALL_TYPE: { if (newChild.key === key) { return updateCall(returnFiber, oldFiber, newChild, expirationTime); } else { return null; } } case REACT_RETURN_TYPE: { // Returns 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 // yield. if (key === null) { return updateReturn(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$1(newChild) || getIteratorFn(newChild)) { if (key !== null) { return null; } return updateFragment(returnFiber, oldFiber, newChild, expirationTime, null); } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } 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_CALL_TYPE: { var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; return updateCall(returnFiber, _matchedFiber2, newChild, expirationTime); } case REACT_RETURN_TYPE: { // Returns don't have keys, so we neither have to check the old nor // new node for the key. If both are returns, they match. var _matchedFiber3 = existingChildren.get(newIdx) || null; return updateReturn(returnFiber, _matchedFiber3, newChild, expirationTime); } case REACT_PORTAL_TYPE: { var _matchedFiber4 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; return updatePortal(returnFiber, _matchedFiber4, newChild, expirationTime); } } if (isArray$1(newChild) || getIteratorFn(newChild)) { var _matchedFiber5 = existingChildren.get(newIdx) || null; return updateFragment(returnFiber, _matchedFiber5, newChild, expirationTime, null); } throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } return null; } /** * Warns if there is a duplicate or missing key */ function warnOnInvalidKey(child, knownKeys) { { if (typeof child !== 'object' || child === null) { return knownKeys; } switch (child.$$typeof) { case REACT_ELEMENT_TYPE: case REACT_CALL_TYPE: case REACT_PORTAL_TYPE: warnForMissingKey(child); var key = child.key; if (typeof key !== 'string') { break; } if (knownKeys === null) { knownKeys = new Set(); knownKeys.add(key); break; } if (!knownKeys.has(key)) { knownKeys.add(key); break; } warning(false, 'Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.%s', key, getCurrentFiberStackAddendum$1()); break; default: break; } } return knownKeys; } function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, expirationTime) { // This algorithm can't optimize by searching from boths ends since we // don't have backpointers on fibers. I'm trying to see how far we can get // with that model. If it ends up not being worth the tradeoffs, we can // add it later. // Even with a two ended optimization, we'd want to optimize for the case // where there are few changes and brute force the comparison instead of // going for the Map. It'd like to explore hitting that path first in // forward-only mode and only go for the Map once we notice that we need // lots of look ahead. This doesn't handle reversal as well as two ended // search but that's unusual. Besides, for the two ended optimization to // work on Iterables, we'd need to copy the whole set. // In this first iteration, we'll just live with hitting the bad case // (adding everything to a Map) in for every insert/move. // If you change this code, also update reconcileChildrenIterator() which // uses the same algorithm. { // First, validate keys. var knownKeys = null; for (var i = 0; i < newChildren.length; i++) { var child = newChildren[i]; knownKeys = warnOnInvalidKey(child, knownKeys); } } 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') ? invariant(false, 'An object is not an iterable. This error is likely caused by a bug in React. Please file an issue.') : void 0; { // Warn about using Maps as children if (typeof newChildrenIterable.entries === 'function') { var possibleMap = newChildrenIterable; if (possibleMap.entries === iteratorFn) { warning(didWarnAboutMaps, 'Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.%s', getCurrentFiberStackAddendum$1()); didWarnAboutMaps = true; } } // First, validate keys. // We'll get a different iterator later for the main pass. var _newChildren = iteratorFn.call(newChildrenIterable); if (_newChildren) { var knownKeys = null; var _step = _newChildren.next(); for (; !_step.done; _step = _newChildren.next()) { var child = _step.value; knownKeys = warnOnInvalidKey(child, knownKeys); } } } var newChildren = iteratorFn.call(newChildrenIterable); !(newChildren != null) ? invariant(false, 'An iterable object provided no iterator.') : 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.internalContextTag, 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.type === element.type) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, element.type === REACT_FRAGMENT_TYPE ? element.props.children : element.props, expirationTime); existing.ref = coerceRef(child, element); existing['return'] = returnFiber; { existing._debugSource = element._source; existing._debugOwner = element._owner; } 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.internalContextTag, expirationTime, element.key); created['return'] = returnFiber; return created; } else { var _created7 = createFiberFromElement(element, returnFiber.internalContextTag, expirationTime); _created7.ref = coerceRef(currentFirstChild, element); _created7['return'] = returnFiber; return _created7; } } function reconcileSingleCall(returnFiber, currentFirstChild, call, expirationTime) { var key = call.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 === CallComponent) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, call, expirationTime); existing['return'] = returnFiber; return existing; } else { deleteRemainingChildren(returnFiber, child); break; } } else { deleteChild(returnFiber, child); } child = child.sibling; } var created = createFiberFromCall(call, returnFiber.internalContextTag, expirationTime); created['return'] = returnFiber; return created; } function reconcileSingleReturn(returnFiber, currentFirstChild, returnNode, expirationTime) { // There's no need to check for keys on yields since they're stateless. var child = currentFirstChild; if (child !== null) { if (child.tag === ReturnComponent) { deleteRemainingChildren(returnFiber, child.sibling); var existing = useFiber(child, null, expirationTime); existing.type = returnNode.value; existing['return'] = returnFiber; return existing; } else { deleteRemainingChildren(returnFiber, child); } } var created = createFiberFromReturn(returnNode, returnFiber.internalContextTag, expirationTime); created.type = returnNode.value; created['return'] = returnFiber; return created; } 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.internalContextTag, 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. if (typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null) { 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_CALL_TYPE: return placeSingleChild(reconcileSingleCall(returnFiber, currentFirstChild, newChild, expirationTime)); case REACT_RETURN_TYPE: return placeSingleChild(reconcileSingleReturn(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$1(newChild)) { return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, expirationTime); } if (getIteratorFn(newChild)) { return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, expirationTime); } if (isObject) { throwOnInvalidObjectType(returnFiber, newChild); } { if (typeof newChild === 'function') { warnOnFunctionType(); } } if (typeof newChild === 'undefined') { // 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: { { var instance = returnFiber.stateNode; if (instance.render._isMockFunction) { // We allow auto-mocks to proceed as if they're returning null. break; } } } // Intentionally fall through to the next case, which handles both // functions and classes // eslint-disable-next-lined no-fallthrough case FunctionalComponent: { var Component = returnFiber.type; invariant(false, '%s(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.', 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, workInProgress) { !(current === null || workInProgress.child === current.child) ? invariant(false, 'Resuming work not yet implemented.') : 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 warnedAboutStatelessRefs = {}; } var ReactFiberBeginWork = function (config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber) { var shouldSetTextContent = config.shouldSetTextContent, useSyncScheduling = config.useSyncScheduling, shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree; var pushHostContext = hostContext.pushHostContext, pushHostContainer = hostContext.pushHostContainer; var enterHydrationState = hydrationContext.enterHydrationState, resetHydrationState = hydrationContext.resetHydrationState, tryToClaimNextHydratableInstance = hydrationContext.tryToClaimNextHydratableInstance; var _ReactFiberClassCompo = ReactFiberClassComponent(scheduleWork, computeExpirationForFiber, memoizeProps, memoizeState), adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance, constructClassInstance = _ReactFiberClassCompo.constructClassInstance, mountClassInstance = _ReactFiberClassCompo.mountClassInstance, updateClassInstance = _ReactFiberClassCompo.updateClassInstance; // TODO: Remove this and use reconcileChildrenAtExpirationTime directly. function reconcileChildren(current, workInProgress, nextChildren) { reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, workInProgress.expirationTime); } function reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime) { if (current === null) { // If this is a fresh new component that hasn't been rendered yet, we // won't update its child set by applying minimal side-effects. Instead, // we will add them all to the child before it gets rendered. That means // we can optimize this reconciliation pass by not tracking side-effects. workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, 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.child, nextChildren, renderExpirationTime); } } function updateFragment(current, workInProgress) { var nextChildren = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextChildren === null) { nextChildren = workInProgress.memoizedProps; } } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextChildren); return workInProgress.child; } function markRef(current, workInProgress) { var ref = workInProgress.ref; if (ref !== null && (!current || current.ref !== ref)) { // Schedule a Ref effect workInProgress.effectTag |= Ref; } } function updateFunctionalComponent(current, workInProgress) { var fn = workInProgress.type; var nextProps = workInProgress.pendingProps; var memoizedProps = workInProgress.memoizedProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextProps === null) { nextProps = memoizedProps; } } else { if (nextProps === null || memoizedProps === nextProps) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } // TODO: consider bringing fn.shouldComponentUpdate() back. // It used to be here. } var unmaskedContext = getUnmaskedContext(workInProgress); var context = getMaskedContext(workInProgress, unmaskedContext); var nextChildren; { ReactCurrentOwner.current = workInProgress; ReactDebugCurrentFiber.setCurrentPhase('render'); nextChildren = fn(nextProps, context); ReactDebugCurrentFiber.setCurrentPhase(null); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextProps); return workInProgress.child; } function updateClassComponent(current, workInProgress, renderExpirationTime) { // 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 = pushContextProvider(workInProgress); var shouldUpdate = void 0; if (current === null) { if (!workInProgress.stateNode) { // In the initial pass we might need to construct the instance. constructClassInstance(workInProgress, workInProgress.pendingProps); mountClassInstance(workInProgress, renderExpirationTime); shouldUpdate = true; } else { invariant(false, 'Resuming work not yet implemented.'); // In a resume, we'll already have an instance we can reuse. // shouldUpdate = resumeMountClassInstance(workInProgress, renderExpirationTime); } } else { shouldUpdate = updateClassInstance(current, workInProgress, renderExpirationTime); } return finishClassComponent(current, workInProgress, shouldUpdate, hasContext); } function finishClassComponent(current, workInProgress, shouldUpdate, hasContext) { // Refs should update even if shouldComponentUpdate returns false markRef(current, workInProgress); if (!shouldUpdate) { // Context providers should defer to sCU for rendering if (hasContext) { invalidateContextProvider(workInProgress, false); } return bailoutOnAlreadyFinishedWork(current, workInProgress); } var instance = workInProgress.stateNode; // Rerender ReactCurrentOwner.current = workInProgress; var nextChildren = void 0; { ReactDebugCurrentFiber.setCurrentPhase('render'); nextChildren = instance.render(); if (debugRenderPhaseSideEffects) { instance.render(); } ReactDebugCurrentFiber.setCurrentPhase(null); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; reconcileChildren(current, workInProgress, nextChildren); // Memoize props and state using the values we just used to render. // TODO: Restructure so we never read values from the instance. memoizeState(workInProgress, instance.state); memoizeProps(workInProgress, instance.props); // The context might have changed so we need to recalculate it. if (hasContext) { invalidateContextProvider(workInProgress, true); } return workInProgress.child; } function pushHostRootContext(workInProgress) { var root = workInProgress.stateNode; if (root.pendingContext) { pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context); } else if (root.context) { // Should always be set pushTopLevelContextObject(workInProgress, root.context, false); } pushHostContainer(workInProgress, root.containerInfo); } function updateHostRoot(current, workInProgress, renderExpirationTime) { pushHostRootContext(workInProgress); var updateQueue = workInProgress.updateQueue; if (updateQueue !== null) { var prevState = workInProgress.memoizedState; var state = processUpdateQueue(current, workInProgress, updateQueue, null, null, renderExpirationTime); if (prevState === state) { // 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, workInProgress); } var element = state.element; var root = workInProgress.stateNode; if ((current === null || current.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, element, renderExpirationTime); } else { // Otherwise reset hydration state in case we aborted and resumed another // root. resetHydrationState(); reconcileChildren(current, workInProgress, element); } memoizeState(workInProgress, state); return workInProgress.child; } resetHydrationState(); // If there is no update queue, that's a bailout because the root has no props. return bailoutOnAlreadyFinishedWork(current, workInProgress); } function updateHostComponent(current, workInProgress, renderExpirationTime) { pushHostContext(workInProgress); if (current === null) { tryToClaimNextHydratableInstance(workInProgress); } var type = workInProgress.type; var memoizedProps = workInProgress.memoizedProps; var nextProps = workInProgress.pendingProps; if (nextProps === null) { nextProps = memoizedProps; !(nextProps !== null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } var prevProps = current !== null ? current.memoizedProps : null; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. } else if (nextProps === null || memoizedProps === nextProps) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } 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 && 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, workInProgress); // Check the host config to see if the children are offscreen/hidden. if (renderExpirationTime !== Never && !useSyncScheduling && shouldDeprioritizeSubtree(type, nextProps)) { // Down-prioritize the children. workInProgress.expirationTime = Never; // Bailout and come back to this fiber later. return null; } reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextProps); return workInProgress.child; } function updateHostText(current, workInProgress) { if (current === null) { tryToClaimNextHydratableInstance(workInProgress); } var nextProps = workInProgress.pendingProps; if (nextProps === null) { nextProps = workInProgress.memoizedProps; } memoizeProps(workInProgress, nextProps); // Nothing to do here. This is terminal. We'll do the completion step // immediately after. return null; } function mountIndeterminateComponent(current, workInProgress, renderExpirationTime) { !(current === null) ? invariant(false, 'An indeterminate component should never have mounted. This error is likely caused by a bug in React. Please file an issue.') : void 0; var fn = workInProgress.type; var props = workInProgress.pendingProps; var unmaskedContext = getUnmaskedContext(workInProgress); var context = getMaskedContext(workInProgress, unmaskedContext); var value; { if (fn.prototype && typeof fn.prototype.render === 'function') { var componentName = getComponentName(workInProgress); warning(false, "The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName); } ReactCurrentOwner.current = workInProgress; value = fn(props, context); } // React DevTools reads this flag. workInProgress.effectTag |= PerformedWork; if (typeof value === 'object' && value !== null && typeof value.render === 'function') { // Proceed under the assumption that this is a class instance workInProgress.tag = ClassComponent; // 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 = pushContextProvider(workInProgress); adoptClassInstance(workInProgress, value); mountClassInstance(workInProgress, renderExpirationTime); return finishClassComponent(current, workInProgress, true, hasContext); } else { // Proceed under the assumption that this is a functional component workInProgress.tag = FunctionalComponent; { var Component = workInProgress.type; if (Component) { warning(!Component.childContextTypes, '%s(...): childContextTypes cannot be defined on a functional component.', Component.displayName || Component.name || 'Component'); } if (workInProgress.ref !== null) { var info = ''; var ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName(); if (ownerName) { info += '\n\nCheck the render method of `' + ownerName + '`.'; } var warningKey = ownerName || workInProgress._debugID || ''; var debugSource = workInProgress._debugSource; if (debugSource) { warningKey = debugSource.fileName + ':' + debugSource.lineNumber; } if (!warnedAboutStatelessRefs[warningKey]) { warnedAboutStatelessRefs[warningKey] = true; warning(false, 'Stateless function components cannot be given refs. ' + 'Attempts to access this ref will fail.%s%s', info, ReactDebugCurrentFiber.getCurrentFiberStackAddendum()); } } } reconcileChildren(current, workInProgress, value); memoizeProps(workInProgress, props); return workInProgress.child; } } function updateCallComponent(current, workInProgress, renderExpirationTime) { var nextCall = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextCall === null) { nextCall = current && current.memoizedProps; !(nextCall !== null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } } else if (nextCall === null || workInProgress.memoizedProps === nextCall) { nextCall = workInProgress.memoizedProps; // TODO: When bailing out, we might need to return the stateNode instead // of the child. To check it for work. // return bailoutOnAlreadyFinishedWork(current, workInProgress); } var nextChildren = nextCall.children; // The following is a fork of reconcileChildrenAtExpirationTime but using // stateNode to store the child. if (current === null) { workInProgress.stateNode = mountChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime); } else { workInProgress.stateNode = reconcileChildFibers(workInProgress, workInProgress.stateNode, nextChildren, renderExpirationTime); } memoizeProps(workInProgress, nextCall); // This doesn't take arbitrary time so we could synchronously just begin // eagerly do the work of workInProgress.child as an optimization. return workInProgress.stateNode; } function updatePortalComponent(current, workInProgress, renderExpirationTime) { pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); var nextChildren = workInProgress.pendingProps; if (hasContextChanged()) { // Normally we can bail out on props equality but if context has changed // we don't do the bailout and we have to reuse existing props instead. if (nextChildren === null) { nextChildren = current && current.memoizedProps; !(nextChildren != null) ? invariant(false, 'We should always have pending or current props. This error is likely caused by a bug in React. Please file an issue.') : void 0; } } else if (nextChildren === null || workInProgress.memoizedProps === nextChildren) { return bailoutOnAlreadyFinishedWork(current, workInProgress); } if (current === null) { // Portals are special because we don't append the children during mount // but at commit. Therefore we need to track insertions which the normal // flow doesn't do during mount. This doesn't happen at the root because // the root always starts with a "current" with a null child. // TODO: Consider unifying this with how the root works. workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderExpirationTime); memoizeProps(workInProgress, nextChildren); } else { reconcileChildren(current, workInProgress, nextChildren); memoizeProps(workInProgress, nextChildren); } return workInProgress.child; } /* function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) { let child = firstChild; do { // Ensure that the first and last effect of the parent corresponds // to the children's first and last effect. if (!returnFiber.firstEffect) { returnFiber.firstEffect = child.firstEffect; } if (child.lastEffect) { if (returnFiber.lastEffect) { returnFiber.lastEffect.nextEffect = child.firstEffect; } returnFiber.lastEffect = child.lastEffect; } } while (child = child.sibling); } */ function bailoutOnAlreadyFinishedWork(current, workInProgress) { cancelWorkTimer(workInProgress); // TODO: We should ideally be able to bail out early if the children have no // more work to do. However, since we don't have a separation of this // Fiber's priority and its children yet - we don't know without doing lots // of the same work we do anyway. Once we have that separation we can just // bail out here if the children has no more work at this priority level. // if (workInProgress.priorityOfChildren <= priorityLevel) { // // If there are side-effects in these children that have not yet been // // committed we need to ensure that they get properly transferred up. // if (current && current.child !== workInProgress.child) { // reuseChildrenEffects(workInProgress, child); // } // return null; // } cloneChildFibers(current, workInProgress); return workInProgress.child; } function bailoutOnLowPriority(current, workInProgress) { cancelWorkTimer(workInProgress); // TODO: Handle HostComponent tags here as well and call pushHostContext()? // See PR 8590 discussion for context switch (workInProgress.tag) { case HostRoot: pushHostRootContext(workInProgress); break; case ClassComponent: pushContextProvider(workInProgress); break; case HostPortal: pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); break; } // TODO: What if this is currently in progress? // How can that happen? How is this not being cloned? return null; } // TODO: Delete memoizeProps/State and move to reconcile/bailout instead function memoizeProps(workInProgress, nextProps) { workInProgress.memoizedProps = nextProps; } function memoizeState(workInProgress, nextState) { workInProgress.memoizedState = nextState; // Don't reset the updateQueue, in case there are pending updates. Resetting // is handled by processUpdateQueue. } function beginWork(current, workInProgress, renderExpirationTime) { if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } switch (workInProgress.tag) { case IndeterminateComponent: return mountIndeterminateComponent(current, workInProgress, renderExpirationTime); case FunctionalComponent: return updateFunctionalComponent(current, workInProgress); case ClassComponent: return updateClassComponent(current, workInProgress, renderExpirationTime); case HostRoot: return updateHostRoot(current, workInProgress, renderExpirationTime); case HostComponent: return updateHostComponent(current, workInProgress, renderExpirationTime); case HostText: return updateHostText(current, workInProgress); case CallHandlerPhase: // This is a restart. Reset the tag to the initial phase. workInProgress.tag = CallComponent; // Intentionally fall through since this is now the same. case CallComponent: return updateCallComponent(current, workInProgress, renderExpirationTime); case ReturnComponent: // A return component is just a placeholder, we can just run through the // next one immediately. return null; case HostPortal: return updatePortalComponent(current, workInProgress, renderExpirationTime); case Fragment: return updateFragment(current, workInProgress); default: invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); } } function beginFailedWork(current, workInProgress, renderExpirationTime) { // Push context providers here to avoid a push/pop context mismatch. switch (workInProgress.tag) { case ClassComponent: pushContextProvider(workInProgress); break; case HostRoot: pushHostRootContext(workInProgress); break; default: invariant(false, 'Invalid type of work. This error is likely caused by a bug in React. Please file an issue.'); } // Add an error effect so we can handle the error during the commit phase workInProgress.effectTag |= Err; // This is a weird case where we do "resume" work — work that failed on // our first attempt. Because we no longer have a notion of "progressed // deletions," reset the child to the current child to make sure we delete // it again. TODO: Find a better way to handle this, perhaps during a more // general overhaul of error handling. if (current === null) { workInProgress.child = null; } else if (workInProgress.child !== current.child) { workInProgress.child = current.child; } if (workInProgress.expirationTime === NoWork || workInProgress.expirationTime > renderExpirationTime) { return bailoutOnLowPriority(current, workInProgress); } // If we don't bail out, we're going be recomputing our children so we need // to drop our effect list. workInProgress.firstEffect = null; workInProgress.lastEffect = null; // Unmount the current children as if the component rendered null var nextChildren = null; reconcileChildrenAtExpirationTime(current, workInProgress, nextChildren, renderExpirationTime); if (workInProgress.tag === ClassComponent) { var instance = workInProgress.stateNode; workInProgress.memoizedProps = instance.props; workInProgress.memoizedState = instance.state; } return workInProgress.child; } return { beginWork: beginWork, beginFailedWork: beginFailedWork }; }; var ReactFiberCompleteWork = function (config, hostContext, hydrationContext) { var createInstance = config.createInstance, createTextInstance = config.createTextInstance, appendInitialChild = config.appendInitialChild, finalizeInitialChildren = config.finalizeInitialChildren, prepareUpdate = config.prepareUpdate, mutation = config.mutation, persistence = config.persistence; var getRootHostContainer = hostContext.getRootHostContainer, popHostContext = hostContext.popHostContext, getHostContext = hostContext.getHostContext, popHostContainer = hostContext.popHostContainer; var prepareToHydrateHostInstance = hydrationContext.prepareToHydrateHostInstance, prepareToHydrateHostTextInstance = hydrationContext.prepareToHydrateHostTextInstance, popHydrationState = hydrationContext.popHydrationState; function markUpdate(workInProgress) { // Tag the fiber with an update effect. This turns a Placement into // an UpdateAndPlacement. workInProgress.effectTag |= Update; } function markRef(workInProgress) { workInProgress.effectTag |= Ref; } function appendAllReturns(returns, workInProgress) { var node = workInProgress.stateNode; if (node) { node['return'] = workInProgress; } while (node !== null) { if (node.tag === HostComponent || node.tag === HostText || node.tag === HostPortal) { invariant(false, 'A call cannot have host component children.'); } else if (node.tag === ReturnComponent) { returns.push(node.type); } else if (node.child !== null) { node.child['return'] = node; node = node.child; continue; } while (node.sibling === null) { if (node['return'] === null || node['return'] === workInProgress) { return; } node = node['return']; } node.sibling['return'] = node['return']; node = node.sibling; } } function moveCallToHandlerPhase(current, workInProgress, renderExpirationTime) { var call = workInProgress.memoizedProps; !call ? invariant(false, 'Should be resolved by now. This error is likely caused by a bug in React. Please file an issue.') : void 0; // First step of the call has completed. Now we need to do the second. // TODO: It would be nice to have a multi stage call represented by a // single component, or at least tail call optimize nested ones. Currently // that requires additional fields that we don't want to add to the fiber. // So this requires nested handlers. // Note: This doesn't mutate the alternate node. I don't think it needs to // since this stage is reset for every pass. workInProgress.tag = CallHandlerPhase; // Build up the returns. // TODO: Compare this to a generator or opaque helpers like Children. var returns = []; appendAllReturns(returns, workInProgress); var fn = call.handler; var props = call.props; var nextChildren = fn(props, returns); var currentFirstChild = current !== null ? current.child : null; workInProgress.child = reconcileChildFibers(workInProgress, currentFirstChild, nextChildren, renderExpirationTime); return workInProgress.child; } function appendAllChildren(parent, workInProgress) { // 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; } } var updateHostContainer = void 0; var updateHostComponent = void 0; var updateHostText = void 0; if (mutation) { if (enableMutatingReconciler) { // Mutation mode updateHostContainer = function (workInProgress) { // Noop }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // 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 = 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 { invariant(false, 'Mutating reconciler is disabled.'); } } else if (persistence) { if (enablePersistentReconciler) { // Persistent host tree mode var cloneInstance = persistence.cloneInstance, createContainerChildSet = persistence.createContainerChildSet, appendChildToContainerChildSet = persistence.appendChildToContainerChildSet, finalizeContainerChildren = persistence.finalizeContainerChildren; // An unfortunate fork of appendAllChildren because we have two different parent types. var appendAllChildrenToContainer = function (containerChildSet, workInProgress) { // 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) { appendChildToContainerChildSet(containerChildSet, 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) { 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 (finalizeContainerChildren(container, newChildSet)) { markUpdate(workInProgress); } portalOrRoot.pendingChildren = newChildSet; // If children might have changed, we have to add them all to the set. appendAllChildrenToContainer(newChildSet, workInProgress); // Schedule an update on the container to swap out the container. markUpdate(workInProgress); } }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // 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; var currentInstance = current.stateNode; if (childrenUnchanged && updatePayload === null) { // No changes, just reuse the existing instance. // Note that this might release a previous clone. workInProgress.stateNode = currentInstance; } else { var recyclableInstance = workInProgress.stateNode; var newInstance = cloneInstance(currentInstance, updatePayload, type, oldProps, newProps, workInProgress, childrenUnchanged, recyclableInstance); if (finalizeInitialChildren(newInstance, type, newProps, rootContainerInstance)) { 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); } } }; updateHostText = 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 { invariant(false, 'Persistent reconciler is disabled.'); } } else { if (enableNoopReconciler) { // No host operations updateHostContainer = function (workInProgress) { // Noop }; updateHostComponent = function (current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance) { // Noop }; updateHostText = function (current, workInProgress, oldText, newText) { // Noop }; } else { invariant(false, 'Noop reconciler is disabled.'); } } function completeWork(current, workInProgress, renderExpirationTime) { // Get the latest props. var newProps = workInProgress.pendingProps; if (newProps === null) { newProps = workInProgress.memoizedProps; } else if (workInProgress.expirationTime !== Never || renderExpirationTime === Never) { // Reset the pending props, unless this was a down-prioritization. workInProgress.pendingProps = null; } switch (workInProgress.tag) { case FunctionalComponent: return null; case ClassComponent: { // We are leaving this subtree, so pop context if any. popContextProvider(workInProgress); return null; } 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); return null; } case HostComponent: { popHostContext(workInProgress); var rootContainerInstance = getRootHostContainer(); var type = workInProgress.type; if (current !== null && workInProgress.stateNode != null) { // 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 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(); var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); updateHostComponent(current, workInProgress, updatePayload, type, oldProps, newProps, rootContainerInstance); if (current.ref !== workInProgress.ref) { markRef(workInProgress); } } else { if (!newProps) { !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0; // This can happen when we abort work. return null; } var _currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context // "stack" as the parent. Then append children as we go in beginWork // or completeWork depending on 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); // 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)) { 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(workInProgress); } } return null; } case HostText: { var newText = newProps; if (current && workInProgress.stateNode != null) { var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need // to schedule a side-effect to do the updates. updateHostText(current, workInProgress, oldText, newText); } else { if (typeof newText !== 'string') { !(workInProgress.stateNode !== null) ? invariant(false, 'We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue.') : void 0; // This can happen when we abort work. return null; } var _rootContainerInstance = getRootHostContainer(); var _currentHostContext2 = getHostContext(); var _wasHydrated = popHydrationState(workInProgress); if (_wasHydrated) { if (prepareToHydrateHostTextInstance(workInProgress)) { markUpdate(workInProgress); } } else { workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext2, workInProgress); } } return null; } case CallComponent: return moveCallToHandlerPhase(current, workInProgress, renderExpirationTime); case CallHandlerPhase: // Reset the tag to now be a first phase call. workInProgress.tag = CallComponent; return null; case ReturnComponent: // Does nothing. return null; case Fragment: return null; case HostPortal: popHostContainer(workInProgress); updateHostContainer(workInProgress); return null; // Error cases case IndeterminateComponent: invariant(false, 'An indeterminate component should have become determinate before completing. This error is likely caused by a bug in React. Please file an issue.'); // eslint-disable-next-line no-fallthrough default: invariant(false, 'Unknown unit of work tag. This error is likely caused by a bug in React. Please file an issue.'); } } return { completeWork: completeWork }; }; var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError$1 = ReactErrorUtils.hasCaughtError; var clearCaughtError$1 = ReactErrorUtils.clearCaughtError; var ReactFiberCommitWork = function (config, captureError) { var getPublicInstance = config.getPublicInstance, mutation = config.mutation, persistence = config.persistence; var callComponentWillUnmountWithTimer = function (current, instance) { startPhaseTimer(current, 'componentWillUnmount'); instance.props = current.memoizedProps; instance.state = current.memoizedState; instance.componentWillUnmount(); stopPhaseTimer(); }; // Capture errors so they don't interrupt unmounting. function safelyCallComponentWillUnmount(current, instance) { { invokeGuardedCallback$2(null, callComponentWillUnmountWithTimer, null, current, instance); if (hasCaughtError$1()) { var unmountError = clearCaughtError$1(); captureError(current, unmountError); } } } function safelyDetachRef(current) { var ref = current.ref; if (ref !== null) { { invokeGuardedCallback$2(null, ref, null, null); if (hasCaughtError$1()) { var refError = clearCaughtError$1(); captureError(current, refError); } } } } function commitLifeCycles(current, finishedWork) { switch (finishedWork.tag) { case ClassComponent: { var instance = finishedWork.stateNode; if (finishedWork.effectTag & Update) { if (current === null) { startPhaseTimer(finishedWork, 'componentDidMount'); instance.props = finishedWork.memoizedProps; instance.state = finishedWork.memoizedState; instance.componentDidMount(); stopPhaseTimer(); } else { var prevProps = current.memoizedProps; var prevState = current.memoizedState; startPhaseTimer(finishedWork, 'componentDidUpdate'); instance.props = finishedWork.memoizedProps; instance.state = finishedWork.memoizedState; instance.componentDidUpdate(prevProps, prevState); stopPhaseTimer(); } } var updateQueue = finishedWork.updateQueue; if (updateQueue !== null) { commitCallbacks(updateQueue, instance); } return; } case HostRoot: { var _updateQueue = finishedWork.updateQueue; if (_updateQueue !== null) { var _instance = finishedWork.child !== null ? finishedWork.child.stateNode : null; commitCallbacks(_updateQueue, _instance); } 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 === null && finishedWork.effectTag & Update) { var type = finishedWork.type; var props = finishedWork.memoizedProps; commitMount(_instance2, type, props, finishedWork); } return; } case HostText: { // We have no life-cycles associated with text. return; } case HostPortal: { // We have no life-cycles associated with portals. return; } default: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } } function commitAttachRef(finishedWork) { var ref = finishedWork.ref; if (ref !== null) { var instance = finishedWork.stateNode; switch (finishedWork.tag) { case HostComponent: ref(getPublicInstance(instance)); break; default: ref(instance); } } } function commitDetachRef(current) { var currentRef = current.ref; if (currentRef !== null) { currentRef(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) { if (typeof onCommitUnmount === 'function') { onCommitUnmount(current); } switch (current.tag) { case ClassComponent: { safelyDetachRef(current); var instance = current.stateNode; if (typeof instance.componentWillUnmount === 'function') { safelyCallComponentWillUnmount(current, instance); } return; } case HostComponent: { safelyDetachRef(current); return; } case CallComponent: { commitNestedUnmounts(current.stateNode); return; } case HostPortal: { // TODO: this is recursive. // We are also not using this parent because // the portal will get pushed immediately. if (enableMutatingReconciler && mutation) { unmountHostComponents(current); } else if (enablePersistentReconciler && persistence) { emptyPortalContainer(current); } 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. !mutation || 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) { // 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['return'] = null; current.child = null; if (current.alternate) { current.alternate.child = null; current.alternate['return'] = null; } } if (!mutation) { var commitContainer = void 0; if (persistence) { var replaceContainerChildren = persistence.replaceContainerChildren, createContainerChildSet = persistence.createContainerChildSet; var emptyPortalContainer = function (current) { var portal = current.stateNode; var containerInfo = portal.containerInfo; var emptyChildSet = createContainerChildSet(containerInfo); replaceContainerChildren(containerInfo, emptyChildSet); }; commitContainer = function (finishedWork) { 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: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } }; } else { commitContainer = function (finishedWork) { // Noop }; } if (enablePersistentReconciler || enableNoopReconciler) { return { commitResetTextContent: function (finishedWork) {}, commitPlacement: function (finishedWork) {}, commitDeletion: function (current) { // Detach refs and call componentWillUnmount() on the whole subtree. commitNestedUnmounts(current); detachFiber(current); }, commitWork: function (current, finishedWork) { commitContainer(finishedWork); }, commitLifeCycles: commitLifeCycles, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; } else if (persistence) { invariant(false, 'Persistent reconciler is disabled.'); } else { invariant(false, 'Noop reconciler is disabled.'); } } var commitMount = mutation.commitMount, commitUpdate = mutation.commitUpdate, resetTextContent = mutation.resetTextContent, commitTextUpdate = mutation.commitTextUpdate, appendChild = mutation.appendChild, appendChildToContainer = mutation.appendChildToContainer, insertBefore = mutation.insertBefore, insertInContainerBefore = mutation.insertInContainerBefore, removeChild = mutation.removeChild, removeChildFromContainer = mutation.removeChildFromContainer; function getHostParentFiber(fiber) { var parent = fiber['return']; while (parent !== null) { if (isHostParent(parent)) { return parent; } parent = parent['return']; } invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.'); } function isHostParent(fiber) { return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal; } function getHostSibling(fiber) { // We're going to search forward into the tree until we find a sibling host // node. Unfortunately, if multiple insertions are done in a row we have to // search past them. This leads to exponential search for the next sibling. 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) { // 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) { // Recursively insert all host nodes into the parent. var parentFiber = getHostParentFiber(finishedWork); 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: invariant(false, 'Invalid host parent fiber. This error is likely caused by a bug in React. Please file an issue.'); } if (parentFiber.effectTag & ContentReset) { // Reset the text content of the parent before doing any insertions resetTextContent(parent); // Clear ContentReset from the effect tag parentFiber.effectTag &= ~ContentReset; } var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need 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) { // We only have the top Fiber that was inserted but we need recurse down its var node = current; // Each iteration, currentParent is populated with node's host parent if not // currentParentIsValid. var currentParentIsValid = false; var currentParent = void 0; var currentParentIsContainer = void 0; while (true) { if (!currentParentIsValid) { var parent = node['return']; findParent: while (true) { !(parent !== null) ? invariant(false, 'Expected to find a host parent. This error is likely caused by a bug in React. Please file an issue.') : 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 (node.tag === HostPortal) { // 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; // Visit children because portals might contain host components. if (node.child !== null) { 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) { return; } while (node.sibling === null) { if (node['return'] === null || node['return'] === current) { 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) { // Recursively delete all host nodes from the parent. // Detach refs and call componentWillUnmount() on the whole subtree. unmountHostComponents(current); detachFiber(current); } function commitWork(current, finishedWork) { switch (finishedWork.tag) { 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 !== null ? current.memoizedProps : newProps; var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components. var updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; if (updatePayload !== null) { commitUpdate(instance, updatePayload, type, oldProps, newProps, finishedWork); } } return; } case HostText: { !(finishedWork.stateNode !== null) ? invariant(false, 'This should have a text node initialized. This error is likely caused by a bug in React. Please file an issue.') : 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 !== null ? current.memoizedProps : newText; commitTextUpdate(textInstance, oldText, newText); return; } case HostRoot: { return; } default: { invariant(false, 'This unit of work tag should not have side-effects. This error is likely caused by a bug in React. Please file an issue.'); } } } function commitResetTextContent(current) { resetTextContent(current.stateNode); } if (enableMutatingReconciler) { return { commitResetTextContent: commitResetTextContent, commitPlacement: commitPlacement, commitDeletion: commitDeletion, commitWork: commitWork, commitLifeCycles: commitLifeCycles, commitAttachRef: commitAttachRef, commitDetachRef: commitDetachRef }; } else { invariant(false, 'Mutating reconciler is disabled.'); } }; var NO_CONTEXT = {}; var ReactFiberHostContext = function (config) { var getChildHostContext = config.getChildHostContext, getRootHostContext = config.getRootHostContext; var contextStackCursor = createCursor(NO_CONTEXT); var contextFiberStackCursor = createCursor(NO_CONTEXT); var rootInstanceStackCursor = createCursor(NO_CONTEXT); function requiredContext(c) { !(c !== NO_CONTEXT) ? invariant(false, 'Expected host context to exist. This error is likely caused by a bug in React. Please file an issue.') : 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); var nextRootContext = getRootHostContext(nextRootInstance); // 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, nextRootContext, fiber); } function popHostContainer(fiber) { pop(contextStackCursor, fiber); pop(contextFiberStackCursor, fiber); pop(rootInstanceStackCursor, fiber); } function getHostContext() { var context = requiredContext(contextStackCursor.current); return context; } function pushHostContext(fiber) { var rootInstance = requiredContext(rootInstanceStackCursor.current); var context = requiredContext(contextStackCursor.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, 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, fiber); pop(contextFiberStackCursor, fiber); } function resetHostContainer() { contextStackCursor.current = NO_CONTEXT; rootInstanceStackCursor.current = NO_CONTEXT; } return { getHostContext: getHostContext, getRootHostContainer: getRootHostContainer, popHostContainer: popHostContainer, popHostContext: popHostContext, pushHostContainer: pushHostContainer, pushHostContext: pushHostContext, resetHostContainer: resetHostContainer }; }; var ReactFiberHydrationContext = function (config) { var shouldSetTextContent = config.shouldSetTextContent, hydration = config.hydration; // If this doesn't have hydration mode. if (!hydration) { return { enterHydrationState: function () { return false; }, resetHydrationState: function () {}, tryToClaimNextHydratableInstance: function () {}, prepareToHydrateHostInstance: function () { invariant(false, 'Expected prepareToHydrateHostInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.'); }, prepareToHydrateHostTextInstance: function () { invariant(false, 'Expected prepareToHydrateHostTextInstance() to never be called. This error is likely caused by a bug in React. Please file an issue.'); }, popHydrationState: function (fiber) { return false; } }; } var canHydrateInstance = hydration.canHydrateInstance, canHydrateTextInstance = hydration.canHydrateTextInstance, getNextHydratableSibling = hydration.getNextHydratableSibling, getFirstHydratableChild = hydration.getFirstHydratableChild, hydrateInstance = hydration.hydrateInstance, hydrateTextInstance = hydration.hydrateTextInstance, didNotMatchHydratedContainerTextInstance = hydration.didNotMatchHydratedContainerTextInstance, didNotMatchHydratedTextInstance = hydration.didNotMatchHydratedTextInstance, didNotHydrateContainerInstance = hydration.didNotHydrateContainerInstance, didNotHydrateInstance = hydration.didNotHydrateInstance, didNotFindHydratableContainerInstance = hydration.didNotFindHydratableContainerInstance, didNotFindHydratableContainerTextInstance = hydration.didNotFindHydratableContainerTextInstance, didNotFindHydratableInstance = hydration.didNotFindHydratableInstance, didNotFindHydratableTextInstance = hydration.didNotFindHydratableTextInstance; // 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) { var parentInstance = fiber.stateNode.containerInfo; nextHydratableInstance = getFirstHydratableChild(parentInstance); hydrationParentFiber = fiber; isHydrating = true; return true; } function deleteHydratableInstance(returnFiber, instance) { { switch (returnFiber.tag) { case HostRoot: didNotHydrateContainerInstance(returnFiber.stateNode.containerInfo, instance); break; case HostComponent: didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance); break; } } 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; { switch (returnFiber.tag) { case HostRoot: { var parentContainer = returnFiber.stateNode.containerInfo; switch (fiber.tag) { case HostComponent: var type = fiber.type; var props = fiber.pendingProps; didNotFindHydratableContainerInstance(parentContainer, type, props); break; case HostText: var text = fiber.pendingProps; didNotFindHydratableContainerTextInstance(parentContainer, text); break; } break; } case HostComponent: { var parentType = returnFiber.type; var parentProps = returnFiber.memoizedProps; var parentInstance = returnFiber.stateNode; switch (fiber.tag) { case HostComponent: var _type = fiber.type; var _props = fiber.pendingProps; didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props); break; case HostText: var _text = fiber.pendingProps; didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text); break; } break; } default: return; } } } 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; } 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; } 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(nextInstance); 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, nextHydratableInstance); } hydrationParentFiber = fiber; nextHydratableInstance = getFirstHydratableChild(nextInstance); } function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) { 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) { var textInstance = fiber.stateNode; var textContent = fiber.memoizedProps; var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber); { if (shouldUpdate) { // We assume that prepareToHydrateHostTextInstance is called in a context where the // hydration parent is the parent host component of this host text. var returnFiber = hydrationParentFiber; if (returnFiber !== null) { switch (returnFiber.tag) { case HostRoot: { var parentContainer = returnFiber.stateNode.containerInfo; didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent); break; } case HostComponent: { var parentType = returnFiber.type; var parentProps = returnFiber.memoizedProps; var parentInstance = returnFiber.stateNode; didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent); break; } } } } } return shouldUpdate; } function popToNextHostParent(fiber) { var parent = fiber['return']; while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot) { parent = parent['return']; } hydrationParentFiber = parent; } function popHydrationState(fiber) { 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() { hydrationParentFiber = null; nextHydratableInstance = null; isHydrating = false; } return { enterHydrationState: enterHydrationState, resetHydrationState: resetHydrationState, tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance, prepareToHydrateHostInstance: prepareToHydrateHostInstance, prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance, popHydrationState: popHydrationState }; }; // 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. var ReactFiberInstrumentation = { debugTool: null }; var ReactFiberInstrumentation_1 = ReactFiberInstrumentation; var defaultShowDialog = function (capturedError) { return true; }; var showDialog = defaultShowDialog; function logCapturedError(capturedError) { var logError = showDialog(capturedError); // Allow injected showDialog() to prevent default console.error logging. // This enables renderers like ReactNative to better manage redbox behavior. if (logError === false) { return; } var error = capturedError.error; var suppressLogging = error && error.suppressReactErrorLogging; if (suppressLogging) { return; } { var componentName = capturedError.componentName, componentStack = capturedError.componentStack, errorBoundaryName = capturedError.errorBoundaryName, errorBoundaryFound = capturedError.errorBoundaryFound, willRetry = capturedError.willRetry; var componentNameMessage = componentName ? 'The above error occurred in the <' + componentName + '> component:' : 'The above error occurred in one of your React components:'; var errorBoundaryMessage = void 0; // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow. if (errorBoundaryFound && errorBoundaryName) { if (willRetry) { errorBoundaryMessage = 'React will try to recreate this component tree from scratch ' + ('using the error boundary you provided, ' + errorBoundaryName + '.'); } else { errorBoundaryMessage = 'This error was initially handled by the error boundary ' + errorBoundaryName + '.\n' + 'Recreating the tree from scratch failed so React will unmount the tree.'; } } else { errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://fb.me/react-error-boundaries to learn more about error boundaries.'; } var combinedMessage = '' + componentNameMessage + componentStack + '\n\n' + ('' + errorBoundaryMessage); // In development, we provide our own message with just the component stack. // We don't include the original error message and JS stack because the browser // has already printed it. Even if the application swallows the error, it is still // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. console.error(combinedMessage); } } var invokeGuardedCallback = ReactErrorUtils.invokeGuardedCallback; var hasCaughtError = ReactErrorUtils.hasCaughtError; var clearCaughtError = ReactErrorUtils.clearCaughtError; { var didWarnAboutStateTransition = false; var didWarnSetStateChildContext = false; var didWarnStateUpdateForUnmountedComponent = {}; var warnAboutUpdateOnUnmounted = function (fiber) { var componentName = getComponentName(fiber) || 'ReactClass'; if (didWarnStateUpdateForUnmountedComponent[componentName]) { return; } warning(false, 'Can only update a mounted or mounting ' + 'component. This usually means you called setState, replaceState, ' + 'or forceUpdate on an unmounted component. This is a no-op.\n\nPlease ' + 'check the code for the %s component.', componentName); didWarnStateUpdateForUnmountedComponent[componentName] = true; }; var warnAboutInvalidUpdates = function (instance) { switch (ReactDebugCurrentFiber.phase) { case 'getChildContext': if (didWarnSetStateChildContext) { return; } warning(false, 'setState(...): Cannot call setState() inside getChildContext()'); didWarnSetStateChildContext = true; break; case 'render': if (didWarnAboutStateTransition) { return; } warning(false, 'Cannot update during an existing state transition (such as within ' + "`render` or another component's constructor). Render methods should " + 'be a pure function of props and state; constructor side-effects are ' + 'an anti-pattern, but can be moved to `componentWillMount`.'); didWarnAboutStateTransition = true; break; } }; } var ReactFiberScheduler = function (config) { var hostContext = ReactFiberHostContext(config); var hydrationContext = ReactFiberHydrationContext(config); var popHostContainer = hostContext.popHostContainer, popHostContext = hostContext.popHostContext, resetHostContainer = hostContext.resetHostContainer; var _ReactFiberBeginWork = ReactFiberBeginWork(config, hostContext, hydrationContext, scheduleWork, computeExpirationForFiber), beginWork = _ReactFiberBeginWork.beginWork, beginFailedWork = _ReactFiberBeginWork.beginFailedWork; var _ReactFiberCompleteWo = ReactFiberCompleteWork(config, hostContext, hydrationContext), completeWork = _ReactFiberCompleteWo.completeWork; var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError), commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent, commitPlacement = _ReactFiberCommitWork.commitPlacement, commitDeletion = _ReactFiberCommitWork.commitDeletion, commitWork = _ReactFiberCommitWork.commitWork, commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles, commitAttachRef = _ReactFiberCommitWork.commitAttachRef, commitDetachRef = _ReactFiberCommitWork.commitDetachRef; var now = config.now, scheduleDeferredCallback = config.scheduleDeferredCallback, cancelDeferredCallback = config.cancelDeferredCallback, useSyncScheduling = config.useSyncScheduling, prepareForCommit = config.prepareForCommit, resetAfterCommit = config.resetAfterCommit; // Represents the current time in ms. var startTime = now(); var mostRecentCurrentTime = msToExpirationTime(0); // Represents the expiration time that incoming updates should use. (If this // is NoWork, use the default strategy: async updates in async mode, sync // updates in sync mode.) var expirationContext = NoWork; 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; // The next fiber with an effect that we're currently committing. var nextEffect = null; // Keep track of which fibers have captured an error that need to be handled. // Work is removed from this collection after componentDidCatch is called. var capturedErrors = null; // Keep track of which fibers have failed during the current batch of work. // This is a different set than capturedErrors, because it is not reset until // the end of the batch. This is needed to propagate errors correctly if a // subtree fails more than once. var failedBoundaries = null; // Error boundaries that captured an error during the current commit. var commitPhaseBoundaries = null; var firstUncaughtError = null; var didFatal = false; var isCommitting = false; var isUnmounting = false; // Used for performance tracking. var interruptedBy = null; function resetContextStack() { // Reset the stack reset(); // Reset the cursors resetContext(); resetHostContainer(); } function commitAllHostEffects() { while (nextEffect !== null) { { ReactDebugCurrentFiber.setCurrentFiber(nextEffect); } recordEffect(); var effectTag = nextEffect.effectTag; if (effectTag & ContentReset) { commitResetTextContent(nextEffect); } if (effectTag & Ref) { var current = nextEffect.alternate; if (current !== null) { commitDetachRef(current); } } // 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 & ~(Callback | Err | ContentReset | Ref | PerformedWork); 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: { isUnmounting = true; commitDeletion(nextEffect); isUnmounting = false; break; } } nextEffect = nextEffect.nextEffect; } { ReactDebugCurrentFiber.resetCurrentFiber(); } } function commitAllLifeCycles() { while (nextEffect !== null) { var effectTag = nextEffect.effectTag; if (effectTag & (Update | Callback)) { recordEffect(); var current = nextEffect.alternate; commitLifeCycles(current, nextEffect); } if (effectTag & Ref) { recordEffect(); commitAttachRef(nextEffect); } if (effectTag & Err) { recordEffect(); commitErrorHandling(nextEffect); } var next = nextEffect.nextEffect; // Ensure that we clean these up so that we don't accidentally keep them. // I'm not actually sure this matters because we can't reset firstEffect // and lastEffect since they're on every node, not just the effectful // ones. So we have to clean everything as we reuse nodes anyway. nextEffect.nextEffect = null; // Ensure that we reset the effectTag here so that we can rely on effect // tags to reason about the current life-cycle. nextEffect = next; } } function commitRoot(finishedWork) { // We keep track of this so that captureError can collect any boundaries // that capture an error during the commit phase. The reason these aren't // local to this function is because errors that occur during cWU are // captured elsewhere, to prevent the unmount from being interrupted. isWorking = true; isCommitting = true; startCommitTimer(); var root = finishedWork.stateNode; !(root.current !== finishedWork) ? invariant(false, 'Cannot commit the same tree as before. This is probably a bug related to the return field. This error is likely caused by a bug in React. Please file an issue.') : void 0; root.isReadyForCommit = false; // Reset this to null before calling lifecycles ReactCurrentOwner.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(); // 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; { invokeGuardedCallback(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; _error = clearCaughtError(); } } if (didError) { !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0; captureError(nextEffect, _error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } stopCommitHostEffectsTimer(); resetAfterCommit(); // 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 _didError = false; var _error2 = void 0; { invokeGuardedCallback(null, commitAllLifeCycles, null); if (hasCaughtError()) { _didError = true; _error2 = clearCaughtError(); } } if (_didError) { !(nextEffect !== null) ? invariant(false, 'Should have next effect. This error is likely caused by a bug in React. Please file an issue.') : void 0; captureError(nextEffect, _error2); if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } isCommitting = false; isWorking = false; stopCommitLifeCyclesTimer(); stopCommitTimer(); if (typeof onCommitRoot === 'function') { onCommitRoot(finishedWork.stateNode); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork); } // If we caught any errors during this commit, schedule their boundaries // to update. if (commitPhaseBoundaries) { commitPhaseBoundaries.forEach(scheduleErrorRecovery); commitPhaseBoundaries = null; } if (firstUncaughtError !== null) { var _error3 = firstUncaughtError; firstUncaughtError = null; onUncaughtError(_error3); } var remainingTime = root.current.expirationTime; if (remainingTime === NoWork) { capturedErrors = null; failedBoundaries = null; } return remainingTime; } function resetExpirationTime(workInProgress, renderTime) { if (renderTime !== Never && workInProgress.expirationTime === Never) { // The children of this component are hidden. Don't bubble their // expiration times. return; } // Check for pending updates. var newExpirationTime = getUpdateExpirationTime(workInProgress); // TODO: Calls need to visit stateNode // Bubble up the earliest expiration time. var child = workInProgress.child; while (child !== null) { if (child.expirationTime !== NoWork && (newExpirationTime === NoWork || newExpirationTime > child.expirationTime)) { newExpirationTime = child.expirationTime; } child = child.sibling; } workInProgress.expirationTime = newExpirationTime; } function completeUnitOfWork(workInProgress) { 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 = workInProgress.alternate; { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = completeWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } var returnFiber = workInProgress['return']; var siblingFiber = workInProgress.sibling; resetExpirationTime(workInProgress, nextRenderExpirationTime); if (next !== null) { stopWorkTimer(workInProgress); if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress); } // If completing this work spawned new work, do that next. We'll come // back here again. return next; } if (returnFiber !== null) { // 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; } } stopWorkTimer(workInProgress); if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onCompleteWork(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. var root = workInProgress.stateNode; root.isReadyForCommit = true; 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 = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = beginWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; } function performFailedUnitOfWork(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 = workInProgress.alternate; // See if beginning this work spawns more work. startWorkTimer(workInProgress); { ReactDebugCurrentFiber.setCurrentFiber(workInProgress); } var next = beginFailedWork(current, workInProgress, nextRenderExpirationTime); { ReactDebugCurrentFiber.resetCurrentFiber(); } if (true && ReactFiberInstrumentation_1.debugTool) { ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress); } if (next === null) { // If this doesn't spawn new work, complete the current work. next = completeUnitOfWork(workInProgress); } ReactCurrentOwner.current = null; return next; } function workLoop(expirationTime) { if (capturedErrors !== null) { // If there are unhandled errors, switch to the slow work loop. // TODO: How to avoid this check in the fast path? Maybe the renderer // could keep track of which roots have unhandled errors and call a // forked version of renderRoot. slowWorkLoopThatChecksForFailedWork(expirationTime); return; } if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) { return; } if (nextRenderExpirationTime <= mostRecentCurrentTime) { // Flush all expired work. while (nextUnitOfWork !== null) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } function slowWorkLoopThatChecksForFailedWork(expirationTime) { if (nextRenderExpirationTime === NoWork || nextRenderExpirationTime > expirationTime) { return; } if (nextRenderExpirationTime <= mostRecentCurrentTime) { // Flush all expired work. while (nextUnitOfWork !== null) { if (hasCapturedError(nextUnitOfWork)) { // Use a forked version of performUnitOfWork nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); } else { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } else { // Flush asynchronous work until the deadline runs out of time. while (nextUnitOfWork !== null && !shouldYield()) { if (hasCapturedError(nextUnitOfWork)) { // Use a forked version of performUnitOfWork nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork); } else { nextUnitOfWork = performUnitOfWork(nextUnitOfWork); } } } } function renderRootCatchBlock(root, failedWork, boundary, expirationTime) { // We're going to restart the error boundary that captured the error. // Conceptually, we're unwinding the stack. We need to unwind the // context stack, too. unwindContexts(failedWork, boundary); // Restart the error boundary using a forked version of // performUnitOfWork that deletes the boundary's children. The entire // failed subree will be unmounted. During the commit phase, a special // lifecycle method is called on the error boundary, which triggers // a re-render. nextUnitOfWork = performFailedUnitOfWork(boundary); // Continue working. workLoop(expirationTime); } function renderRoot(root, expirationTime) { !!isWorking ? invariant(false, 'renderRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0; isWorking = true; // We're about to mutate the work-in-progress tree. If the root was pending // commit, it no longer is: we'll need to complete it again. root.isReadyForCommit = false; // Check if we're starting from a fresh stack, or if we're resuming from // previously yielded work. if (root !== nextRoot || expirationTime !== nextRenderExpirationTime || nextUnitOfWork === null) { // Reset the stack and start working from the root. resetContextStack(); nextRoot = root; nextRenderExpirationTime = expirationTime; nextUnitOfWork = createWorkInProgress(nextRoot.current, null, expirationTime); } startWorkLoopTimer(nextUnitOfWork); var didError = false; var error = null; { invokeGuardedCallback(null, workLoop, null, expirationTime); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } // An error was thrown during the render phase. while (didError) { if (didFatal) { // This was a fatal error. Don't attempt to recover from it. firstUncaughtError = error; break; } var failedWork = nextUnitOfWork; if (failedWork === null) { // An error was thrown but there's no current unit of work. This can // happen during the commit phase if there's a bug in the renderer. didFatal = true; continue; } // "Capture" the error by finding the nearest boundary. If there is no // error boundary, we use the root. var boundary = captureError(failedWork, error); !(boundary !== null) ? invariant(false, 'Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue.') : void 0; if (didFatal) { // The error we just captured was a fatal error. This happens // when the error propagates to the root more than once. continue; } didError = false; error = null; { invokeGuardedCallback(null, renderRootCatchBlock, null, root, failedWork, boundary, expirationTime); if (hasCaughtError()) { didError = true; error = clearCaughtError(); continue; } } // We're finished working. Exit the error loop. break; } var uncaughtError = firstUncaughtError; // We're done performing work. Time to clean up. stopWorkLoopTimer(interruptedBy); interruptedBy = null; isWorking = false; didFatal = false; firstUncaughtError = null; if (uncaughtError !== null) { onUncaughtError(uncaughtError); } return root.isReadyForCommit ? root.current.alternate : null; } // Returns the boundary that captured the error, or null if the error is ignored function captureError(failedWork, error) { // It is no longer valid because we exited the user code. ReactCurrentOwner.current = null; { ReactDebugCurrentFiber.resetCurrentFiber(); } // Search for the nearest error boundary. var boundary = null; // Passed to logCapturedError() var errorBoundaryFound = false; var willRetry = false; var errorBoundaryName = null; // Host containers are a special case. If the failed work itself is a host // container, then it acts as its own boundary. In all other cases, we // ignore the work itself and only search through the parents. if (failedWork.tag === HostRoot) { boundary = failedWork; if (isFailedBoundary(failedWork)) { // If this root already failed, there must have been an error when // attempting to unmount it. This is a worst-case scenario and // should only be possible if there's a bug in the renderer. didFatal = true; } } else { var node = failedWork['return']; while (node !== null && boundary === null) { if (node.tag === ClassComponent) { var instance = node.stateNode; if (typeof instance.componentDidCatch === 'function') { errorBoundaryFound = true; errorBoundaryName = getComponentName(node); // Found an error boundary! boundary = node; willRetry = true; } } else if (node.tag === HostRoot) { // Treat the root like a no-op error boundary boundary = node; } if (isFailedBoundary(node)) { // This boundary is already in a failed state. // If we're currently unmounting, that means this error was // thrown while unmounting a failed subtree. We should ignore // the error. if (isUnmounting) { return null; } // If we're in the commit phase, we should check to see if // this boundary already captured an error during this commit. // This case exists because multiple errors can be thrown during // a single commit without interruption. if (commitPhaseBoundaries !== null && (commitPhaseBoundaries.has(node) || node.alternate !== null && commitPhaseBoundaries.has(node.alternate))) { // If so, we should ignore this error. return null; } // The error should propagate to the next boundary -— we keep looking. boundary = null; willRetry = false; } node = node['return']; } } if (boundary !== null) { // Add to the collection of failed boundaries. This lets us know that // subsequent errors in this subtree should propagate to the next boundary. if (failedBoundaries === null) { failedBoundaries = new Set(); } failedBoundaries.add(boundary); // This method is unsafe outside of the begin and complete phases. // We might be in the commit phase when an error is captured. // The risk is that the return path from this Fiber may not be accurate. // That risk is acceptable given the benefit of providing users more context. var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork); var _componentName = getComponentName(failedWork); // Add to the collection of captured errors. This is stored as a global // map of errors and their component stack location keyed by the boundaries // that capture them. We mostly use this Map as a Set; it's a Map only to // avoid adding a field to Fiber to store the error. if (capturedErrors === null) { capturedErrors = new Map(); } var capturedError = { componentName: _componentName, componentStack: _componentStack, error: error, errorBoundary: errorBoundaryFound ? boundary.stateNode : null, errorBoundaryFound: errorBoundaryFound, errorBoundaryName: errorBoundaryName, willRetry: willRetry }; capturedErrors.set(boundary, capturedError); try { logCapturedError(capturedError); } catch (e) { // Prevent cycle if logCapturedError() throws. // A cycle may still occur if logCapturedError renders a component that throws. var suppressLogging = e && e.suppressReactErrorLogging; if (!suppressLogging) { console.error(e); } } // If we're in the commit phase, defer scheduling an update on the // boundary until after the commit is complete if (isCommitting) { if (commitPhaseBoundaries === null) { commitPhaseBoundaries = new Set(); } commitPhaseBoundaries.add(boundary); } else { // Otherwise, schedule an update now. // TODO: Is this actually necessary during the render phase? Is it // possible to unwind and continue rendering at the same priority, // without corrupting internal state? scheduleErrorRecovery(boundary); } return boundary; } else if (firstUncaughtError === null) { // If no boundary is found, we'll need to throw the error firstUncaughtError = error; } return null; } function hasCapturedError(fiber) { // TODO: capturedErrors should store the boundary instance, to avoid needing // to check the alternate. return capturedErrors !== null && (capturedErrors.has(fiber) || fiber.alternate !== null && capturedErrors.has(fiber.alternate)); } function isFailedBoundary(fiber) { // TODO: failedBoundaries should store the boundary instance, to avoid // needing to check the alternate. return failedBoundaries !== null && (failedBoundaries.has(fiber) || fiber.alternate !== null && failedBoundaries.has(fiber.alternate)); } function commitErrorHandling(effectfulFiber) { var capturedError = void 0; if (capturedErrors !== null) { capturedError = capturedErrors.get(effectfulFiber); capturedErrors['delete'](effectfulFiber); if (capturedError == null) { if (effectfulFiber.alternate !== null) { effectfulFiber = effectfulFiber.alternate; capturedError = capturedErrors.get(effectfulFiber); capturedErrors['delete'](effectfulFiber); } } } !(capturedError != null) ? invariant(false, 'No error for given unit of work. This error is likely caused by a bug in React. Please file an issue.') : void 0; switch (effectfulFiber.tag) { case ClassComponent: var instance = effectfulFiber.stateNode; var info = { componentStack: capturedError.componentStack }; // Allow the boundary to handle the error, usually by scheduling // an update to itself instance.componentDidCatch(capturedError.error, info); return; case HostRoot: if (firstUncaughtError === null) { firstUncaughtError = capturedError.error; } return; default: invariant(false, 'Invalid type of work. This error is likely caused by a bug in React. Please file an issue.'); } } function unwindContexts(from, to) { var node = from; while (node !== null) { switch (node.tag) { case ClassComponent: popContextProvider(node); break; case HostComponent: popHostContext(node); break; case HostRoot: popHostContainer(node); break; case HostPortal: popHostContainer(node); break; } if (node === to || node.alternate === to) { stopFailedWorkTimer(node); break; } else { stopWorkTimer(node); } node = node['return']; } } function computeAsyncExpiration() { // Given the current clock time, returns an expiration time. We use rounding // to batch like updates together. // Should complete within ~1000ms. 1200ms max. var currentTime = recalculateCurrentTime(); var expirationMs = 1000; var bucketSizeMs = 200; return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs); } function computeExpirationForFiber(fiber) { var expirationTime = void 0; if (expirationContext !== NoWork) { // An explicit expiration context was set; expirationTime = expirationContext; } else if (isWorking) { if (isCommitting) { // Updates that occur during the commit phase should have sync priority // by default. expirationTime = Sync; } else { // Updates during the render phase should expire at the same time as // the work that is being rendered. expirationTime = nextRenderExpirationTime; } } else { // No explicit expiration context was set, and we're not currently // performing work. Calculate a new expiration time. if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) { // This is a sync update expirationTime = Sync; } else { // This is an async update expirationTime = computeAsyncExpiration(); } } return expirationTime; } function scheduleWork(fiber, expirationTime) { return scheduleWorkImpl(fiber, expirationTime, false); } function checkRootNeedsClearing(root, fiber, expirationTime) { if (!isWorking && root === nextRoot && expirationTime < nextRenderExpirationTime) { // Restart the root from the top. if (nextUnitOfWork !== null) { // This is an interruption. (Used for performance tracking.) interruptedBy = fiber; } nextRoot = null; nextUnitOfWork = null; nextRenderExpirationTime = NoWork; } } function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) { recordScheduleUpdate(); { if (!isErrorRecovery && fiber.tag === ClassComponent) { var instance = fiber.stateNode; warnAboutInvalidUpdates(instance); } } var node = fiber; while (node !== null) { // Walk the parent path to the root and update each node's // expiration time. if (node.expirationTime === NoWork || node.expirationTime > expirationTime) { node.expirationTime = expirationTime; } if (node.alternate !== null) { if (node.alternate.expirationTime === NoWork || node.alternate.expirationTime > expirationTime) { node.alternate.expirationTime = expirationTime; } } if (node['return'] === null) { if (node.tag === HostRoot) { var root = node.stateNode; checkRootNeedsClearing(root, fiber, expirationTime); requestWork(root, expirationTime); checkRootNeedsClearing(root, fiber, expirationTime); } else { { if (!isErrorRecovery && fiber.tag === ClassComponent) { warnAboutUpdateOnUnmounted(fiber); } } return; } } node = node['return']; } } function scheduleErrorRecovery(fiber) { scheduleWorkImpl(fiber, Sync, true); } function recalculateCurrentTime() { // Subtract initial time so it fits inside 32bits var ms = now() - startTime; mostRecentCurrentTime = msToExpirationTime(ms); return mostRecentCurrentTime; } function deferredUpdates(fn) { var previousExpirationContext = expirationContext; expirationContext = computeAsyncExpiration(); try { return fn(); } finally { expirationContext = previousExpirationContext; } } function syncUpdates(fn) { var previousExpirationContext = expirationContext; expirationContext = Sync; try { return fn(); } finally { expirationContext = previousExpirationContext; } } // 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 = -1; var isRendering = false; var nextFlushedRoot = null; var nextFlushedExpirationTime = NoWork; var deadlineDidExpire = false; var hasUnhandledError = false; var unhandledError = null; var deadline = null; var isBatchingUpdates = false; var isUnbatchingUpdates = false; // Use these to prevent an infinite loop of nested updates var NESTED_UPDATE_LIMIT = 1000; var nestedUpdateCount = 0; var timeHeuristicForUnitOfWork = 1; function scheduleCallbackWithExpiration(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 { // Existing callback has insufficient timeout. Cancel and schedule a // new one. cancelDeferredCallback(callbackID); } // The request callback timer is already running. Don't start a new one. } else { startRequestCallbackTimer(); } // Compute a timeout for the given expiration time. var currentMs = now() - startTime; var expirationMs = expirationTimeToMs(expirationTime); var timeout = expirationMs - currentMs; callbackExpirationTime = expirationTime; callbackID = scheduleDeferredCallback(performAsyncWork, { timeout: timeout }); } // 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) { if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { invariant(false, 'Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.'); } // 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.remainingExpirationTime = 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.remainingExpirationTime; if (remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime) { // Update the priority. root.remainingExpirationTime = 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(nextFlushedRoot, nextFlushedExpirationTime); } return; } // TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { performWork(Sync, null); } else { scheduleCallbackWithExpiration(expirationTime); } } function findHighestPriorityRoot() { var highestPriorityWork = NoWork; var highestPriorityRoot = null; if (lastScheduledRoot !== null) { var previousScheduledRoot = lastScheduledRoot; var root = firstScheduledRoot; while (root !== null) { var remainingExpirationTime = root.remainingExpirationTime; 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) ? invariant(false, 'Should have a previous and last root. This error is likely caused by a bug in React. Please file an issue.') : 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 (highestPriorityWork === NoWork || remainingExpirationTime < highestPriorityWork) { // Update the priority, if it's higher highestPriorityWork = remainingExpirationTime; highestPriorityRoot = root; } if (root === lastScheduledRoot) { break; } previousScheduledRoot = root; root = root.nextScheduledRoot; } } } // 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. var previousFlushedRoot = nextFlushedRoot; if (previousFlushedRoot !== null && previousFlushedRoot === highestPriorityRoot) { nestedUpdateCount++; } else { // Reset whenever we switch roots. nestedUpdateCount = 0; } nextFlushedRoot = highestPriorityRoot; nextFlushedExpirationTime = highestPriorityWork; } function performAsyncWork(dl) { performWork(NoWork, dl); } function performWork(minExpirationTime, dl) { deadline = dl; // Keep working on roots until there's no more work, or until the we reach // the deadline. findHighestPriorityRoot(); if (enableUserTimingAPI && deadline !== null) { var didExpire = nextFlushedExpirationTime < recalculateCurrentTime(); stopRequestCallbackTimer(didExpire); } while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork && (minExpirationTime === NoWork || nextFlushedExpirationTime <= minExpirationTime) && !deadlineDidExpire) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime); // Find the next highest priority work. 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 (deadline !== null) { callbackExpirationTime = NoWork; callbackID = -1; } // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { scheduleCallbackWithExpiration(nextFlushedExpirationTime); } // Clean-up. deadline = null; deadlineDidExpire = false; nestedUpdateCount = 0; if (hasUnhandledError) { var _error4 = unhandledError; unhandledError = null; hasUnhandledError = false; throw _error4; } } function performWorkOnRoot(root, expirationTime) { !!isRendering ? invariant(false, 'performWorkOnRoot was called recursively. This error is likely caused by a bug in React. Please file an issue.') : void 0; isRendering = true; // Check if this is async work or sync/expired work. // TODO: Pass current time as argument to renderRoot, commitRoot if (expirationTime <= recalculateCurrentTime()) { // Flush sync work. var finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. root.finishedWork = null; root.remainingExpirationTime = commitRoot(finishedWork); } else { root.finishedWork = null; finishedWork = renderRoot(root, expirationTime); if (finishedWork !== null) { // We've completed the root. Commit it. root.remainingExpirationTime = commitRoot(finishedWork); } } } else { // Flush async work. var _finishedWork = root.finishedWork; if (_finishedWork !== null) { // This root is already complete. We can commit it. root.finishedWork = null; root.remainingExpirationTime = commitRoot(_finishedWork); } else { root.finishedWork = null; _finishedWork = renderRoot(root, expirationTime); if (_finishedWork !== null) { // We've completed the root. Check the deadline one more time // before committing. if (!shouldYield()) { // Still time left. Commit the root. root.remainingExpirationTime = commitRoot(_finishedWork); } else { // There's no time left. Mark this root as complete. We'll come // back and commit it later. root.finishedWork = _finishedWork; } } } } isRendering = false; } // When working on async work, the reconciler asks the renderer if it should // yield execution. For DOM, we implement this with requestIdleCallback. function shouldYield() { if (deadline === null) { return false; } if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) { // Disregard deadline.didTimeout. Only expired work should be flushed // during a timeout. This path is only hit for non-expired work. return false; } deadlineDidExpire = true; return true; } // TODO: Not happy about this hook. Conceptually, renderRoot should return a // tuple of (isReadyForCommit, didError, error) function onUncaughtError(error) { !(nextFlushedRoot !== null) ? invariant(false, 'Should be working on a root. This error is likely caused by a bug in React. Please file an issue.') : void 0; // Unschedule this root so we don't work on it again until there's // another update. nextFlushedRoot.remainingExpirationTime = 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) { performWork(Sync, null); } } } // TODO: Batching should be implemented at the renderer level, not inside // the reconciler. function unbatchedUpdates(fn) { if (isBatchingUpdates && !isUnbatchingUpdates) { isUnbatchingUpdates = true; try { return fn(); } finally { isUnbatchingUpdates = false; } } return fn(); } // TODO: Batching should be implemented at the renderer level, not within // the reconciler. function flushSync(fn) { var previousIsBatchingUpdates = isBatchingUpdates; isBatchingUpdates = true; try { return syncUpdates(fn); } finally { isBatchingUpdates = previousIsBatchingUpdates; !!isRendering ? invariant(false, 'flushSync was called from inside a lifecycle method. It cannot be called when React is already rendering.') : void 0; performWork(Sync, null); } } return { computeAsyncExpiration: computeAsyncExpiration, computeExpirationForFiber: computeExpirationForFiber, scheduleWork: scheduleWork, batchedUpdates: batchedUpdates, unbatchedUpdates: unbatchedUpdates, flushSync: flushSync, deferredUpdates: deferredUpdates }; }; { var didWarnAboutNestedUpdates = false; } // 0 is PROD, 1 is DEV. // Might add PROFILE later. function getContextForSubtree(parentComponent) { if (!parentComponent) { return emptyObject; } var fiber = get(parentComponent); var parentContext = findCurrentUnmaskedContext(fiber); return isContextProvider(fiber) ? processChildContext(fiber, parentContext) : parentContext; } var ReactFiberReconciler$1 = function (config) { var getPublicInstance = config.getPublicInstance; var _ReactFiberScheduler = ReactFiberScheduler(config), computeAsyncExpiration = _ReactFiberScheduler.computeAsyncExpiration, computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber, scheduleWork = _ReactFiberScheduler.scheduleWork, batchedUpdates = _ReactFiberScheduler.batchedUpdates, unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates, flushSync = _ReactFiberScheduler.flushSync, deferredUpdates = _ReactFiberScheduler.deferredUpdates; function scheduleTopLevelUpdate(current, element, callback) { { if (ReactDebugCurrentFiber.phase === 'render' && ReactDebugCurrentFiber.current !== null && !didWarnAboutNestedUpdates) { didWarnAboutNestedUpdates = true; warning(false, 'Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentName(ReactDebugCurrentFiber.current) || 'Unknown'); } } callback = callback === undefined ? null : callback; { warning(callback === null || typeof callback === 'function', 'render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback); } var expirationTime = void 0; // Check if the top-level element is an async wrapper component. If so, // treat updates to the root as async. This is a bit weird but lets us // avoid a separate `renderAsync` API. if (enableAsyncSubtreeAPI && element != null && element.type != null && element.type.prototype != null && element.type.prototype.unstable_isAsyncReactComponent === true) { expirationTime = computeAsyncExpiration(); } else { expirationTime = computeExpirationForFiber(current); } var update = { expirationTime: expirationTime, partialState: { element: element }, callback: callback, isReplace: false, isForced: false, nextCallback: null, next: null }; insertUpdateIntoFiber(current, update); scheduleWork(current, expirationTime); } function findHostInstance(fiber) { var hostFiber = findCurrentHostFiber(fiber); if (hostFiber === null) { return null; } return hostFiber.stateNode; } return { createContainer: function (containerInfo, hydrate) { return createFiberRoot(containerInfo, hydrate); }, updateContainer: function (element, container, parentComponent, callback) { // TODO: If this is a nested container, this won't be the root. var current = container.current; { if (ReactFiberInstrumentation_1.debugTool) { if (current.alternate === null) { ReactFiberInstrumentation_1.debugTool.onMountContainer(container); } else if (element === null) { ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container); } else { ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container); } } } var context = getContextForSubtree(parentComponent); if (container.context === null) { container.context = context; } else { container.pendingContext = context; } scheduleTopLevelUpdate(current, element, callback); }, batchedUpdates: batchedUpdates, unbatchedUpdates: unbatchedUpdates, deferredUpdates: deferredUpdates, flushSync: flushSync, getPublicRootInstance: function (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; } }, findHostInstance: findHostInstance, findHostInstanceWithNoPortals: function (fiber) { var hostFiber = findCurrentHostFiberWithNoPortals(fiber); if (hostFiber === null) { return null; } return hostFiber.stateNode; }, injectIntoDevTools: function (devToolsConfig) { var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance; return injectInternals(_assign({}, devToolsConfig, { findHostInstanceByFiber: function (fiber) { return findHostInstance(fiber); }, findFiberByHostInstance: function (instance) { if (!findFiberByHostInstance) { // Might not be implemented by the renderer. return null; } return findFiberByHostInstance(instance); } })); } }; }; var ReactFiberReconciler$2 = Object.freeze({ default: ReactFiberReconciler$1 }); var ReactFiberReconciler$3 = ( ReactFiberReconciler$2 && ReactFiberReconciler$1 ) || ReactFiberReconciler$2; // TODO: bundle Flow types with the package. // TODO: decide on the top-level export form. // This is hacky but makes it work with both Rollup and Jest. var reactReconciler = ReactFiberReconciler$3['default'] ? ReactFiberReconciler$3['default'] : ReactFiberReconciler$3; var getFiberCurrentPropsFromNode = null; var getInstanceFromNode = null; { } /** * Standard/simple iteration through an event's collected dispatches. */ /** * @see executeDispatchesInOrderStopAtTrueImpl */ /** * Execution of a "direct" dispatch - there must be at most one dispatch * accumulated on the event or it is considered an error. It doesn't really make * sense for an event with multiple dispatches (bubbled) to keep track of the * return values at each dispatch execution, but it does tend to make sense when * dealing with "direct" dispatches. * * @return {*} The return value of executing the single dispatch. */ /** * @param {SyntheticEvent} event * @return {boolean} True iff number of dispatches accumulated is greater than 0. */ // Use to restore controlled state after a change event has fired. var fiberHostComponent = null; var restoreTarget = null; var restoreQueue = null; function restoreStateOfTarget(target) { // We perform this translation at the end of the event loop so that we // always receive the correct fiber here var internalInstance = getInstanceFromNode(target); if (!internalInstance) { // Unmounted return; } !(fiberHostComponent && typeof fiberHostComponent.restoreControlledState === 'function') ? invariant(false, 'Fiber needs to be injected to handle a fiber target for controlled events. This error is likely caused by a bug in React. Please file an issue.') : void 0; var props = getFiberCurrentPropsFromNode(internalInstance.stateNode); fiberHostComponent.restoreControlledState(internalInstance.stateNode, internalInstance.type, props); } function restoreStateIfNeeded() { if (!restoreTarget) { return; } var target = restoreTarget; var queuedTargets = restoreQueue; restoreTarget = null; restoreQueue = null; restoreStateOfTarget(target); if (queuedTargets) { for (var i = 0; i < queuedTargets.length; i++) { restoreStateOfTarget(queuedTargets[i]); } } } // Used as a way to call batchedUpdates when we don't have a reference to // the renderer. Such as when we're dispatching events or if third party // libraries need to call batchedUpdates. Eventually, this API will go away when // everything is batched by default. We'll then have a similar API to opt-out of // scheduled work and instead do synchronous work. // Defaults var fiberBatchedUpdates = function (fn, bookkeeping) { return fn(bookkeeping); }; var isNestingBatched = false; function batchedUpdates(fn, bookkeeping) { if (isNestingBatched) { // If we are currently inside another batch, we need to wait until it // fully completes before restoring state. Therefore, we add the target to // a queue of work. return fiberBatchedUpdates(fn, bookkeeping); } isNestingBatched = true; try { return fiberBatchedUpdates(fn, bookkeeping); } finally { // Here we wait until all updates have propagated, which is important // when using controlled components within layers: // https://github.com/facebook/react/issues/1698 // Then we restore state of any controlled component. isNestingBatched = false; restoreStateIfNeeded(); } } 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; } 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); } var TestRenderer = reactReconciler({ getRootHostContext: function () { return emptyObject; }, getChildHostContext: function () { return emptyObject; }, prepareForCommit: function () { // noop }, resetAfterCommit: function () { // noop }, createInstance: function (type, props, rootContainerInstance, hostContext, internalInstanceHandle) { return { type: type, props: props, children: [], rootContainerInstance: rootContainerInstance, tag: 'INSTANCE' }; }, appendInitialChild: function (parentInstance, child) { var index = parentInstance.children.indexOf(child); if (index !== -1) { parentInstance.children.splice(index, 1); } parentInstance.children.push(child); }, finalizeInitialChildren: function (testElement, type, props, rootContainerInstance) { return false; }, prepareUpdate: function (testElement, type, oldProps, newProps, rootContainerInstance, hostContext) { return UPDATE_SIGNAL; }, shouldSetTextContent: function (type, props) { return false; }, shouldDeprioritizeSubtree: function (type, props) { return false; }, createTextInstance: function (text, rootContainerInstance, hostContext, internalInstanceHandle) { return { text: text, tag: 'TEXT' }; }, scheduleDeferredCallback: function (fn) { return setTimeout(fn, 0, { timeRemaining: Infinity }); }, cancelDeferredCallback: function (timeoutID) { clearTimeout(timeoutID); }, useSyncScheduling: true, getPublicInstance: getPublicInstance, now: function () { // Test renderer does not use expiration return 0; }, mutation: { commitUpdate: function (instance, updatePayload, type, oldProps, newProps, internalInstanceHandle) { instance.type = type; instance.props = newProps; }, commitMount: function (instance, type, newProps, internalInstanceHandle) { // noop }, commitTextUpdate: function (textInstance, oldText, newText) { textInstance.text = newText; }, resetTextContent: function (testElement) { // noop }, appendChild: appendChild, appendChildToContainer: appendChild, insertBefore: insertBefore, insertInContainerBefore: insertBefore, removeChild: removeChild, removeChildFromContainer: removeChild } }); var defaultTestOptions = { createNodeMock: function () { return null; } }; function toJSON(inst) { 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) { renderedChildren = inst.children.map(toJSON); } 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 nodeAndSiblingsTrees(nodeWithSibling) { var array = []; var node = nodeWithSibling; while (node != null) { array.push(node); node = node.sibling; } var trees = array.map(toTree); return trees.length ? trees : null; } function hasSiblings(node) { return node && node.sibling; } function toTree(node) { if (node == null) { return null; } switch (node.tag) { case HostRoot: // 3 return toTree(node.child); case ClassComponent: return { nodeType: 'component', type: node.type, props: _assign({}, node.memoizedProps), instance: node.stateNode, rendered: hasSiblings(node.child) ? nodeAndSiblingsTrees(node.child) : toTree(node.child) }; case FunctionalComponent: // 1 return { nodeType: 'component', type: node.type, props: _assign({}, node.memoizedProps), instance: null, rendered: hasSiblings(node.child) ? nodeAndSiblingsTrees(node.child) : toTree(node.child) }; case HostComponent: // 5 return { nodeType: 'host', type: node.type, props: _assign({}, node.memoizedProps), instance: null, // TODO: use createNodeMock here somehow? rendered: nodeAndSiblingsTrees(node.child) }; case HostText: // 6 return node.stateNode.text; default: invariant(false, 'toTree() does not yet know how to handle nodes with tag=%s', node.tag); } } 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; } var validWrapperTypes = new Set([FunctionalComponent, ClassComponent, HostComponent]); var ReactTestInstance = function () { ReactTestInstance.prototype._currentFiber = function _currentFiber() { // Throws if this component has been unmounted. var fiber = findCurrentFiberUsingSlowPath(this._fiber); !(fiber !== null) ? invariant(false, 'Can\'t read from currently-mounting component. This error is likely caused by a bug in React. Please file an issue.') : void 0; return fiber; }; function ReactTestInstance(fiber) { _classCallCheck(this, ReactTestInstance); !validWrapperTypes.has(fiber.tag) ? invariant(false, 'Unexpected object passed to ReactTestInstance constructor (tag: %s). This is probably a bug in React.', 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']; return parent === null || parent.tag === HostRoot ? null : wrapFiber(parent); } }, { key: 'children', get: function () { var children = []; var startingNode = this._currentFiber(); var node = startingNode; if (node.child === null) { return children; } node.child['return'] = node; node = node.child; outer: while (true) { var descend = false; switch (node.tag) { case FunctionalComponent: case ClassComponent: case HostComponent: children.push(wrapFiber(node)); break; case HostText: children.push('' + node.memoizedProps); break; case Fragment: descend = true; break; default: invariant(false, 'Unsupported component type %s in test renderer. This is probably a bug in React.', node.tag); } 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; } }]); 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; } } var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = root.children[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var child = _step.value; if (typeof child === 'string') { continue; } results.push.apply(results, _findAll(child, predicate, options)); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } } finally { if (_didIteratorError) { throw _iteratorError; } } } 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; if (options && typeof options.createNodeMock === 'function') { createNodeMock = options.createNodeMock; } var container = { children: [], createNodeMock: createNodeMock, tag: 'CONTAINER' }; var root = TestRenderer.createContainer(container, false); !(root != null) ? invariant(false, 'something went wrong') : void 0; TestRenderer.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]); } return container.children.map(toJSON); }, toTree: function () { if (root == null || root.current == null) { return null; } return toTree(root.current); }, update: function (newElement) { if (root == null || root.current == null) { return; } TestRenderer.updateContainer(newElement, root, null, null); }, unmount: function () { if (root == null || root.current == null) { return; } TestRenderer.updateContainer(null, root, null); container = null; root = null; }, getInstance: function () { if (root == null || root.current == null) { return null; } return TestRenderer.getPublicRootInstance(root); } }; Object.defineProperty(entry, 'root', { configurable: true, enumerable: true, get: function () { if (root === null || root.current.child === null) { throw new Error("Can't access .root on unmounted test renderer"); } return wrapFiber(root.current.child); } }); return entry; }, /* eslint-disable camelcase */ unstable_batchedUpdates: batchedUpdates }; var ReactTestRenderer = Object.freeze({ 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['default'] : ReactTestRenderer$1; module.exports = reactTestRenderer; })(); }