diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION
index 219cda440ac83..cb5c649f6e87e 100644
--- a/compiled/facebook-www/REVISION
+++ b/compiled/facebook-www/REVISION
@@ -1 +1 @@
-d310d654a7c7aab6c8213da84ef36dfba82711b0
+cfc1274e3be5a93a4c93f8fb87f2109993afe1dd
diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js
index 6b5154eef5b4b..7446ca9d7e3a2 100644
--- a/compiled/facebook-www/React-dev.modern.js
+++ b/compiled/facebook-www/React-dev.modern.js
@@ -27,7 +27,7 @@ if (
}
"use strict";
-var ReactVersion = "18.3.0-www-modern-9bafe2f6";
+var ReactVersion = "18.3.0-www-modern-addde25e";
// ATTENTION
// When adding new symbols to this file,
diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js
index f5da1f8b17577..6ac85abd1cdc7 100644
--- a/compiled/facebook-www/ReactART-dev.classic.js
+++ b/compiled/facebook-www/ReactART-dev.classic.js
@@ -69,7 +69,7 @@ function _assertThisInitialized(self) {
return self;
}
-var ReactVersion = "18.3.0-www-classic-d10ee72f";
+var ReactVersion = "18.3.0-www-classic-773bb029";
var LegacyRoot = 0;
var ConcurrentRoot = 1;
diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js
index 73465e7309f95..dbeb8785b1d29 100644
--- a/compiled/facebook-www/ReactART-prod.modern.js
+++ b/compiled/facebook-www/ReactART-prod.modern.js
@@ -9555,7 +9555,7 @@ var slice = Array.prototype.slice,
return null;
},
bundleType: 0,
- version: "18.3.0-www-modern-ce2970c7",
+ version: "18.3.0-www-modern-990522b1",
rendererPackageName: "react-art"
};
var internals$jscomp$inline_1283 = {
@@ -9586,7 +9586,7 @@ var internals$jscomp$inline_1283 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
- reconcilerVersion: "18.3.0-www-modern-ce2970c7"
+ reconcilerVersion: "18.3.0-www-modern-990522b1"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1284 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js
index 9f6d6acd56338..139223ff5502c 100644
--- a/compiled/facebook-www/ReactDOM-dev.classic.js
+++ b/compiled/facebook-www/ReactDOM-dev.classic.js
@@ -37,6 +37,7 @@ var dynamicFeatureFlags = require("ReactFeatureFlags");
var disableInputAttributeSyncing =
dynamicFeatureFlags.disableInputAttributeSyncing,
+ disableIEWorkarounds = dynamicFeatureFlags.disableIEWorkarounds,
enableTrustedTypesIntegration =
dynamicFeatureFlags.enableTrustedTypesIntegration,
replayFailedUnitOfWorkWithInvokeGuardedCallback =
@@ -5412,7 +5413,8 @@ var canDiffStyleForHydrationWarning;
// in that browser completely in favor of doing all that work.
// See https://github.com/facebook/react/issues/11807
- canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode;
+ canDiffStyleForHydrationWarning =
+ disableIEWorkarounds || (canUseDOM && !document.documentMode);
}
function validatePropertiesInDevelopment(type, props) {
@@ -5605,7 +5607,11 @@ function setInitialDOMProperties(
var nextHtml = nextProp ? nextProp[HTML] : undefined;
if (nextHtml != null) {
- setInnerHTML$1(domElement, nextHtml);
+ if (disableIEWorkarounds) {
+ domElement.innerHTML = nextHtml;
+ } else {
+ setInnerHTML$1(domElement, nextHtml);
+ }
}
} else if (propKey === CHILDREN) {
if (typeof nextProp === "string") {
@@ -5661,7 +5667,11 @@ function updateDOMProperties(
if (propKey === STYLE$1) {
setValueForStyles(domElement, propValue);
} else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
- setInnerHTML$1(domElement, propValue);
+ if (disableIEWorkarounds) {
+ domElement.innerHTML = propValue;
+ } else {
+ setInnerHTML$1(domElement, propValue);
+ }
} else if (propKey === CHILDREN) {
setTextContent(domElement, propValue);
} else {
@@ -9028,7 +9038,7 @@ var ContinuousEventPriority = InputContinuousLane;
var DefaultEventPriority = DefaultLane;
var IdleEventPriority = IdleLane;
var currentUpdatePriority = NoLane;
-function getCurrentUpdatePriority$1() {
+function getCurrentUpdatePriority() {
return currentUpdatePriority;
}
function setCurrentUpdatePriority(newPriority) {
@@ -12057,7 +12067,6 @@ function isOwnedInstance(node) {
return !!(node[internalHoistableMarker] || node[internalInstanceKey]);
}
-var restoreImpl = null;
var restoreTarget = null;
var restoreQueue = null;
@@ -12071,24 +12080,18 @@ function restoreStateOfTarget(target) {
return;
}
- if (typeof restoreImpl !== "function") {
- throw new Error(
- "setRestoreImplementation() needs to be called to handle a target for controlled " +
- "events. This error is likely caused by a bug in React. Please file an issue."
- );
- }
-
var stateNode = internalInstance.stateNode; // Guard against Fiber being unmounted.
if (stateNode) {
var props = getFiberCurrentPropsFromNode(stateNode);
- restoreImpl(internalInstance.stateNode, internalInstance.type, props);
+ restoreControlledState(
+ internalInstance.stateNode,
+ internalInstance.type,
+ props
+ );
}
}
-function setRestoreImplementation(impl) {
- restoreImpl = impl;
-}
function enqueueStateRestore(target) {
if (restoreTarget) {
if (restoreQueue) {
@@ -12121,6681 +12124,5740 @@ function restoreStateIfNeeded() {
}
}
-// 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 loggedTypeFailures = {};
+var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
-var batchedUpdatesImpl = function (fn, bookkeeping) {
- return fn(bookkeeping);
-};
+function setCurrentlyValidatingElement(element) {
+ {
+ if (element) {
+ var owner = element._owner;
+ var stack = describeUnknownElementTypeFrameInDEV(
+ element.type,
+ element._source,
+ owner ? owner.type : null
+ );
+ ReactDebugCurrentFrame.setExtraStackFrame(stack);
+ } else {
+ ReactDebugCurrentFrame.setExtraStackFrame(null);
+ }
+ }
+}
-var flushSyncImpl = function () {};
+function checkPropTypes(typeSpecs, values, location, componentName, element) {
+ {
+ // $FlowFixMe This is okay but Flow doesn't know it.
+ var has = Function.call.bind(hasOwnProperty);
-var isInsideEventHandler = false;
+ for (var typeSpecName in typeSpecs) {
+ if (has(typeSpecs, typeSpecName)) {
+ var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
-function finishEventHandler() {
- // 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.
- var controlledComponentsHavePendingUpdates = needsStateRestore();
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== "function") {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ var err = Error(
+ (componentName || "React class") +
+ ": " +
+ location +
+ " type `" +
+ typeSpecName +
+ "` is invalid; " +
+ "it must be a function, usually from the `prop-types` package, but received `" +
+ typeof typeSpecs[typeSpecName] +
+ "`." +
+ "This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`."
+ );
+ err.name = "Invariant Violation";
+ throw err;
+ }
- if (controlledComponentsHavePendingUpdates) {
- // If a controlled event was fired, we may need to restore the state of
- // the DOM node back to the controlled value. This is necessary when React
- // bails out of the update without touching the DOM.
- // TODO: Restore state in the microtask, after the discrete updates flush,
- // instead of early flushing them here.
- flushSyncImpl();
- restoreStateIfNeeded();
- }
-}
+ error$1 = typeSpecs[typeSpecName](
+ values,
+ typeSpecName,
+ componentName,
+ location,
+ null,
+ "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"
+ );
+ } catch (ex) {
+ error$1 = ex;
+ }
-function batchedUpdates$1(fn, a, b) {
- if (isInsideEventHandler) {
- // If we are currently inside another batch, we need to wait until it
- // fully completes before restoring state.
- return fn(a, b);
- }
+ if (error$1 && !(error$1 instanceof Error)) {
+ setCurrentlyValidatingElement(element);
- isInsideEventHandler = true;
+ error(
+ "%s: type specification of %s" +
+ " `%s` is invalid; the type checker " +
+ "function must return `null` or an `Error` but returned a %s. " +
+ "You may have forgotten to pass an argument to the type checker " +
+ "creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and " +
+ "shape all require an argument).",
+ componentName || "React class",
+ location,
+ typeSpecName,
+ typeof error$1
+ );
- try {
- return batchedUpdatesImpl(fn, a, b);
- } finally {
- isInsideEventHandler = false;
- finishEventHandler();
- }
-} // TODO: Replace with flushSync
-function setBatchingImplementation(
- _batchedUpdatesImpl,
- _discreteUpdatesImpl,
- _flushSyncImpl
-) {
- batchedUpdatesImpl = _batchedUpdatesImpl;
- flushSyncImpl = _flushSyncImpl;
-}
+ setCurrentlyValidatingElement(null);
+ }
-function isInteractive(tag) {
- return (
- tag === "button" ||
- tag === "input" ||
- tag === "select" ||
- tag === "textarea"
- );
-}
+ if (
+ error$1 instanceof Error &&
+ !(error$1.message in loggedTypeFailures)
+ ) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error$1.message] = true;
+ setCurrentlyValidatingElement(element);
-function shouldPreventMouseEvent(name, type, props) {
- switch (name) {
- case "onClick":
- case "onClickCapture":
- case "onDoubleClick":
- case "onDoubleClickCapture":
- case "onMouseDown":
- case "onMouseDownCapture":
- case "onMouseMove":
- case "onMouseMoveCapture":
- case "onMouseUp":
- case "onMouseUpCapture":
- case "onMouseEnter":
- return !!(props.disabled && isInteractive(type));
+ error("Failed %s type: %s", location, error$1.message);
- default:
- return false;
+ setCurrentlyValidatingElement(null);
+ }
+ }
+ }
}
}
-/**
- * @param {object} inst The instance, which is the source of events.
- * @param {string} registrationName Name of listener (e.g. `onClick`).
- * @return {?function} The stored callback.
- */
-function getListener(inst, registrationName) {
- var stateNode = inst.stateNode;
-
- if (stateNode === null) {
- // Work in progress (ex: onload events in incremental mode).
- return null;
- }
+var warnedAboutMissingGetChildContext;
- var props = getFiberCurrentPropsFromNode(stateNode);
+{
+ warnedAboutMissingGetChildContext = {};
+}
- if (props === null) {
- // Work in progress.
- return null;
- }
+var emptyContextObject = {};
- var listener = props[registrationName];
+{
+ Object.freeze(emptyContextObject);
+} // A cursor to the current merged context object on the stack.
- if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
- return null;
- }
+var contextStackCursor = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed.
- if (listener && typeof listener !== "function") {
- throw new Error(
- "Expected `" +
- registrationName +
- "` listener to be a function, instead got a value of `" +
- typeof listener +
- "` type."
- );
- }
+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.
- return listener;
-}
+var previousContext = emptyContextObject;
-var passiveBrowserEventsSupported = false; // Check if browser support events with passive listeners
-// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
+function getUnmaskedContext(
+ workInProgress,
+ Component,
+ didPushOwnContextIfProvider
+) {
+ {
+ if (didPushOwnContextIfProvider && isContextProvider(Component)) {
+ // If the fiber is a context provider itself, when we read its context
+ // we may have already pushed its own child context on the stack. A context
+ // provider should not "see" its own child context. Therefore we read the
+ // previous (parent) context instead for a context provider.
+ return previousContext;
+ }
-if (canUseDOM) {
- try {
- var options = {};
- Object.defineProperty(options, "passive", {
- get: function () {
- passiveBrowserEventsSupported = true;
- }
- });
- window.addEventListener("test", options, options);
- window.removeEventListener("test", options, options);
- } catch (e) {
- passiveBrowserEventsSupported = false;
+ return contextStackCursor.current;
}
}
-// Provided by www
-var ReactFbErrorUtils = require("ReactFbErrorUtils");
-
-if (typeof ReactFbErrorUtils.invokeGuardedCallback !== "function") {
- throw new Error(
- "Expected ReactFbErrorUtils.invokeGuardedCallback to be a function."
- );
+function cacheContext(workInProgress, unmaskedContext, maskedContext) {
+ {
+ var instance = workInProgress.stateNode;
+ instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
+ instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
+ }
}
-function invokeGuardedCallbackImpl(name, func, context, a, b, c, d, e, f) {
- // This will call `this.onError(err)` if an error was caught.
- ReactFbErrorUtils.invokeGuardedCallback.apply(this, arguments);
-}
+function getMaskedContext(workInProgress, unmaskedContext) {
+ {
+ var type = workInProgress.type;
+ var contextTypes = type.contextTypes;
-var hasError = false;
-var caughtError = null; // Used by event system to capture/rethrow the first error.
+ if (!contextTypes) {
+ return emptyContextObject;
+ } // Avoid recreating masked context unless unmasked context has changed.
+ // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
+ // This may trigger infinite loops if componentWillReceiveProps calls setState.
-var hasRethrowError = false;
-var rethrowError = null;
-var reporter = {
- onError: function (error) {
- hasError = true;
- caughtError = error;
- }
-};
-/**
- * Call a function while guarding against errors that happens within it.
- * Returns an error if it throws, otherwise null.
- *
- * In production, this is implemented using a try-catch. The reason we don't
- * use a try-catch directly is so that we can swap out a different
- * implementation in DEV mode.
- *
- * @param {String} name of the guard to use for logging or debugging
- * @param {Function} func The function to invoke
- * @param {*} context The context to use when calling the function
- * @param {...*} args Arguments for function
- */
+ var instance = workInProgress.stateNode;
-function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
- hasError = false;
- caughtError = null;
- invokeGuardedCallbackImpl.apply(reporter, 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
- */
+ if (
+ instance &&
+ instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
+ ) {
+ return instance.__reactInternalMemoizedMaskedChildContext;
+ }
-function invokeGuardedCallbackAndCatchFirstError(
- name,
- func,
- context,
- a,
- b,
- c,
- d,
- e,
- f
-) {
- invokeGuardedCallback.apply(this, arguments);
+ var context = {};
- if (hasError) {
- var error = clearCaughtError();
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
+ }
- if (!hasRethrowError) {
- hasRethrowError = true;
- rethrowError = error;
+ {
+ var name = getComponentNameFromFiber(workInProgress) || "Unknown";
+ checkPropTypes(contextTypes, context, "context", name);
+ } // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // Context is created before the class component is instantiated so check for instance.
+
+ if (instance) {
+ cacheContext(workInProgress, unmaskedContext, context);
}
+
+ return context;
}
}
-/**
- * During execution of guarded functions we will capture the first error which
- * we will rethrow to be handled by the top level error handler.
- */
-function rethrowCaughtError() {
- if (hasRethrowError) {
- var error = rethrowError;
- hasRethrowError = false;
- rethrowError = null;
- throw error;
+function hasContextChanged() {
+ {
+ return didPerformWorkStackCursor.current;
}
}
-function hasCaughtError() {
- return hasError;
-}
-function clearCaughtError() {
- if (hasError) {
- var error = caughtError;
- hasError = false;
- caughtError = null;
- return error;
- } else {
- throw new Error(
- "clearCaughtError was called but no error was captured. This error " +
- "is likely caused by a bug in React. Please file an issue."
- );
+
+function isContextProvider(type) {
+ {
+ var childContextTypes = type.childContextTypes;
+ return childContextTypes !== null && childContextTypes !== undefined;
}
}
-var EventListenerWWW = require("EventListener");
-
-function addEventBubbleListener(target, eventType, listener) {
- return EventListenerWWW.listen(target, eventType, listener);
-}
-function addEventCaptureListener(target, eventType, listener) {
- return EventListenerWWW.capture(target, eventType, listener);
-}
-function addEventCaptureListenerWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
-) {
- return EventListenerWWW.captureWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
- );
-}
-function addEventBubbleListenerWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
-) {
- return EventListenerWWW.bubbleWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
- );
+function popContext(fiber) {
+ {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+ }
}
-function removeEventListener(target, eventType, listener, capture) {
- listener.remove();
-} // Flow magic to verify the exports of this file match the original version.
-/**
- * These variables store information about text content of a target node,
- * allowing comparison of content before and after a given event.
- *
- * Identify the node where selection currently begins, then observe
- * both its text content and its current position in the DOM. Since the
- * browser may natively replace the target node during composition, we can
- * use its position to find its replacement.
- *
- *
- */
-var root = null;
-var startText = null;
-var fallbackText = null;
-function initialize(nativeEventTarget) {
- root = nativeEventTarget;
- startText = getText();
- return true;
-}
-function reset() {
- root = null;
- startText = null;
- fallbackText = null;
+function popTopLevelContextObject(fiber) {
+ {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+ }
}
-function getData() {
- if (fallbackText) {
- return fallbackText;
+
+function pushTopLevelContextObject(fiber, context, didChange) {
+ {
+ if (contextStackCursor.current !== emptyContextObject) {
+ throw new Error(
+ "Unexpected context found on stack. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
+
+ push(contextStackCursor, context, fiber);
+ push(didPerformWorkStackCursor, didChange, fiber);
}
+}
- var start;
- var startValue = startText;
- var startLength = startValue.length;
- var end;
- var endValue = getText();
- var endLength = endValue.length;
+function processChildContext(fiber, type, parentContext) {
+ {
+ var instance = fiber.stateNode;
+ var childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future.
+ // It has only been added in Fiber to match the (unintentional) behavior in Stack.
- for (start = 0; start < startLength; start++) {
- if (startValue[start] !== endValue[start]) {
- break;
+ if (typeof instance.getChildContext !== "function") {
+ {
+ var componentName = getComponentNameFromFiber(fiber) || "Unknown";
+
+ if (!warnedAboutMissingGetChildContext[componentName]) {
+ warnedAboutMissingGetChildContext[componentName] = true;
+
+ error(
+ "%s.childContextTypes is specified but there is no getChildContext() method " +
+ "on the instance. You can either define getChildContext() on %s or remove " +
+ "childContextTypes from it.",
+ componentName,
+ componentName
+ );
+ }
+ }
+
+ return parentContext;
}
- }
- var minEnd = startLength - start;
+ var childContext = instance.getChildContext();
- for (end = 1; end <= minEnd; end++) {
- if (startValue[startLength - end] !== endValue[endLength - end]) {
- break;
+ for (var contextKey in childContext) {
+ if (!(contextKey in childContextTypes)) {
+ throw new Error(
+ (getComponentNameFromFiber(fiber) || "Unknown") +
+ '.getChildContext(): key "' +
+ contextKey +
+ '" is not defined in childContextTypes.'
+ );
+ }
}
- }
- var sliceTail = end > 1 ? 1 - end : undefined;
- fallbackText = endValue.slice(start, sliceTail);
- return fallbackText;
-}
-function getText() {
- if ("value" in root) {
- return root.value;
- }
+ {
+ var name = getComponentNameFromFiber(fiber) || "Unknown";
+ checkPropTypes(childContextTypes, childContext, "child context", name);
+ }
- return root.textContent;
+ return assign({}, parentContext, childContext);
+ }
}
-/**
- * `charCode` represents the actual "character code" and is safe to use with
- * `String.fromCharCode`. As such, only keys that correspond to printable
- * characters produce a valid `charCode`, the only exception to this is Enter.
- * The Tab-key is considered non-printable and does not have a `charCode`,
- * presumably because it does not produce a tab-character in browsers.
- *
- * @param {object} nativeEvent Native browser event.
- * @return {number} Normalized `charCode` property.
- */
-function getEventCharCode(nativeEvent) {
- var charCode;
- var keyCode = nativeEvent.keyCode;
-
- if ("charCode" in nativeEvent) {
- charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`.
-
- if (charCode === 0 && keyCode === 13) {
- charCode = 13;
- }
- } else {
- // IE8 does not implement `charCode`, but `keyCode` has the correct value.
- charCode = keyCode;
- } // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
- // report Enter as charCode 10 when ctrl is pressed.
+function pushContextProvider(workInProgress) {
+ {
+ var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity.
+ // If the instance does not exist yet, we will push null at first,
+ // and replace it on the stack later when invalidating the context.
- if (charCode === 10) {
- charCode = 13;
- } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
- // Must not discard the (non-)printable Enter-key.
+ var memoizedMergedChildContext =
+ (instance && instance.__reactInternalMemoizedMergedChildContext) ||
+ emptyContextObject; // Remember the parent context so we can merge with it later.
+ // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
- if (charCode >= 32 || charCode === 13) {
- return charCode;
+ previousContext = contextStackCursor.current;
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
+ push(
+ didPerformWorkStackCursor,
+ didPerformWorkStackCursor.current,
+ workInProgress
+ );
+ return true;
}
-
- return 0;
}
-function functionThatReturnsTrue() {
- return true;
-}
-
-function functionThatReturnsFalse() {
- return false;
-} // This is intentionally a factory so that we have different returned constructors.
-// If we had a single constructor, it would be megamorphic and engines would deopt.
-
-function createSyntheticEvent(Interface) {
- /**
- * Synthetic events are dispatched by event plugins, typically in response to a
- * top-level event delegation handler.
- *
- * These systems should generally use pooling to reduce the frequency of garbage
- * collection. The system should check `isPersistent` to determine whether the
- * event should be released into the pool after being dispatched. Users that
- * need a persisted event should invoke `persist`.
- *
- * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
- * normalizing browser quirks. Subclasses do not necessarily have to implement a
- * DOM interface; custom application-specific events can also subclass this.
- */
- // $FlowFixMe[missing-this-annot]
- function SyntheticBaseEvent(
- reactName,
- reactEventType,
- targetInst,
- nativeEvent,
- nativeEventTarget
- ) {
- this._reactName = reactName;
- this._targetInst = targetInst;
- this.type = reactEventType;
- this.nativeEvent = nativeEvent;
- this.target = nativeEventTarget;
- this.currentTarget = null;
-
- for (var propName in Interface) {
- if (!Interface.hasOwnProperty(propName)) {
- continue;
- }
-
- var normalize = Interface[propName];
+function invalidateContextProvider(workInProgress, type, didChange) {
+ {
+ var instance = workInProgress.stateNode;
- if (normalize) {
- this[propName] = normalize(nativeEvent);
- } else {
- this[propName] = nativeEvent[propName];
- }
+ if (!instance) {
+ throw new Error(
+ "Expected to have an instance by this point. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
}
- var defaultPrevented =
- nativeEvent.defaultPrevented != null
- ? nativeEvent.defaultPrevented
- : nativeEvent.returnValue === false;
+ if (didChange) {
+ // Merge parent and own context.
+ // Skip this if we're not updating due to sCU.
+ // This avoids unnecessarily recomputing memoized values.
+ var mergedContext = processChildContext(
+ workInProgress,
+ type,
+ previousContext
+ );
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one.
+ // It is important to unwind the context in the reverse order.
- if (defaultPrevented) {
- this.isDefaultPrevented = functionThatReturnsTrue;
+ 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 {
- this.isDefaultPrevented = functionThatReturnsFalse;
+ pop(didPerformWorkStackCursor, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
}
+ }
+}
- this.isPropagationStopped = functionThatReturnsFalse;
- return this;
- } // $FlowFixMe[prop-missing] found when upgrading Flow
+function findCurrentUnmaskedContext(fiber) {
+ {
+ // Currently this is only used with renderSubtreeIntoContainer; not sure if it
+ // makes sense elsewhere
+ if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
+ throw new Error(
+ "Expected subtree parent to be a mounted class component. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
- assign(SyntheticBaseEvent.prototype, {
- // $FlowFixMe[missing-this-annot]
- preventDefault: function () {
- this.defaultPrevented = true;
- var event = this.nativeEvent;
+ var node = fiber;
- if (!event) {
- return;
- }
+ do {
+ switch (node.tag) {
+ case HostRoot:
+ return node.stateNode.context;
- if (event.preventDefault) {
- event.preventDefault(); // $FlowFixMe - flow is not aware of `unknown` in IE
- } else if (typeof event.returnValue !== "unknown") {
- event.returnValue = false;
- }
+ case ClassComponent: {
+ var Component = node.type;
- this.isDefaultPrevented = functionThatReturnsTrue;
- },
- // $FlowFixMe[missing-this-annot]
- stopPropagation: function () {
- var event = this.nativeEvent;
+ if (isContextProvider(Component)) {
+ return node.stateNode.__reactInternalMemoizedMergedChildContext;
+ }
- if (!event) {
- return;
- }
+ break;
+ }
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
- if (event.stopPropagation) {
- event.stopPropagation(); // $FlowFixMe - flow is not aware of `unknown` in IE
- } else if (typeof event.cancelBubble !== "unknown") {
- // The ChangeEventPlugin registers a "propertychange" event for
- // IE. This event does not support bubbling or cancelling, and
- // any references to cancelBubble throw "Member not found". A
- // typeof check of "unknown" circumvents this issue (and is also
- // IE specific).
- event.cancelBubble = true;
- }
+ node = node.return;
+ } while (node !== null);
- this.isPropagationStopped = functionThatReturnsTrue;
- },
+ throw new Error(
+ "Found unexpected detached subtree parent. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
+}
- /**
- * We release all dispatched `SyntheticEvent`s after each event loop, adding
- * them back into the pool. This allows a way to hold onto a reference that
- * won't be added back into the pool.
- */
- persist: function () {
- // Modern event system doesn't use pooling.
- },
+var LegacyRoot = 0;
+var ConcurrentRoot = 1;
- /**
- * Checks if this event should be released back into the pool.
- *
- * @return {boolean} True if this should not be released, false otherwise.
- */
- isPersistent: functionThatReturnsTrue
- });
- return SyntheticBaseEvent;
+// We use the existence of the state object as an indicator that the component
+// is hidden.
+var OffscreenVisible =
+ /* */
+ 1;
+var OffscreenDetached =
+ /* */
+ 2;
+var OffscreenPassiveEffectsConnected =
+ /* */
+ 4;
+function isOffscreenManual(offscreenFiber) {
+ return (
+ offscreenFiber.memoizedProps !== null &&
+ offscreenFiber.memoizedProps.mode === "manual"
+ );
}
+
/**
- * @interface Event
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
*/
+function is(x, y) {
+ return (
+ (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
+ );
+}
-var EventInterface = {
- eventPhase: 0,
- bubbles: 0,
- cancelable: 0,
- timeStamp: function (event) {
- return event.timeStamp || Date.now();
- },
- defaultPrevented: 0,
- isTrusted: 0
-};
-var SyntheticEvent = createSyntheticEvent(EventInterface);
-
-var UIEventInterface = assign({}, EventInterface, {
- view: 0,
- detail: 0
-});
+var objectIs = typeof Object.is === "function" ? Object.is : is; // $FlowFixMe[method-unbinding]
-var SyntheticUIEvent = createSyntheticEvent(UIEventInterface);
-var lastMovementX;
-var lastMovementY;
-var lastMouseEvent;
+var syncQueue = null;
+var includesLegacySyncCallbacks = false;
+var isFlushingSyncQueue = false;
+function scheduleSyncCallback(callback) {
+ // Push this callback into an internal queue. We'll flush these either in
+ // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
+ if (syncQueue === null) {
+ syncQueue = [callback];
+ } else {
+ // Push onto existing queue. Don't need to schedule a callback because
+ // we already scheduled one when we created the queue.
+ syncQueue.push(callback);
+ }
+}
+function scheduleLegacySyncCallback(callback) {
+ includesLegacySyncCallbacks = true;
+ scheduleSyncCallback(callback);
+}
+function flushSyncCallbacksOnlyInLegacyMode() {
+ // Only flushes the queue if there's a legacy sync callback scheduled.
+ // TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
+ // it might make more sense for the queue to be a list of roots instead of a
+ // list of generic callbacks. Then we can have two: one for legacy roots, one
+ // for concurrent roots. And this method would only flush the legacy ones.
+ if (includesLegacySyncCallbacks) {
+ flushSyncCallbacks();
+ }
+}
+function flushSyncCallbacks() {
+ if (!isFlushingSyncQueue && syncQueue !== null) {
+ // Prevent re-entrance.
+ isFlushingSyncQueue = true; // Set the event priority to discrete
+ // TODO: Is this necessary anymore? The only user code that runs in this
+ // queue is in the render or commit phases, which already set the
+ // event priority. Should be able to remove.
-function updateMouseMovementPolyfillState(event) {
- if (event !== lastMouseEvent) {
- if (lastMouseEvent && event.type === "mousemove") {
- // $FlowFixMe assuming this is a number
- lastMovementX = event.screenX - lastMouseEvent.screenX; // $FlowFixMe assuming this is a number
+ var previousUpdatePriority = getCurrentUpdatePriority();
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ var errors = null;
+ var queue = syncQueue; // $FlowFixMe[incompatible-use] found when upgrading Flow
- lastMovementY = event.screenY - lastMouseEvent.screenY;
- } else {
- lastMovementX = 0;
- lastMovementY = 0;
- }
+ for (var i = 0; i < queue.length; i++) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ var callback = queue[i];
- lastMouseEvent = event;
- }
-}
-/**
- * @interface MouseEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+ try {
+ do {
+ var isSync = true; // $FlowFixMe[incompatible-type] we bail out when we get a null
-var MouseEventInterface = assign({}, UIEventInterface, {
- screenX: 0,
- screenY: 0,
- clientX: 0,
- clientY: 0,
- pageX: 0,
- pageY: 0,
- ctrlKey: 0,
- shiftKey: 0,
- altKey: 0,
- metaKey: 0,
- getModifierState: getEventModifierState,
- button: 0,
- buttons: 0,
- relatedTarget: function (event) {
- if (event.relatedTarget === undefined)
- return event.fromElement === event.srcElement
- ? event.toElement
- : event.fromElement;
- return event.relatedTarget;
- },
- movementX: function (event) {
- if ("movementX" in event) {
- return event.movementX;
+ callback = callback(isSync);
+ } while (callback !== null);
+ } catch (error) {
+ // Collect errors so we can rethrow them at the end
+ if (errors === null) {
+ errors = [error];
+ } else {
+ errors.push(error);
+ }
+ }
}
- updateMouseMovementPolyfillState(event);
- return lastMovementX;
- },
- movementY: function (event) {
- if ("movementY" in event) {
- return event.movementY;
- } // Don't need to call updateMouseMovementPolyfillState() here
- // because it's guaranteed to have already run when movementX
- // was copied.
+ syncQueue = null;
+ includesLegacySyncCallbacks = false;
+ setCurrentUpdatePriority(previousUpdatePriority);
+ isFlushingSyncQueue = false;
- return lastMovementY;
+ if (errors !== null) {
+ if (errors.length > 1) {
+ if (typeof AggregateError === "function") {
+ // eslint-disable-next-line no-undef
+ throw new AggregateError(errors);
+ } else {
+ for (var _i = 1; _i < errors.length; _i++) {
+ scheduleCallback$2(
+ ImmediatePriority,
+ throwError.bind(null, errors[_i])
+ );
+ }
+
+ var firstError = errors[0];
+ throw firstError;
+ }
+ } else {
+ var error = errors[0];
+ throw error;
+ }
+ }
}
-});
-var SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
-/**
- * @interface DragEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+ return null;
+}
-var DragEventInterface = assign({}, MouseEventInterface, {
- dataTransfer: 0
-});
+function throwError(error) {
+ throw error;
+}
-var SyntheticDragEvent = createSyntheticEvent(DragEventInterface);
-/**
- * @interface FocusEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+var nativeConsole = console;
+var nativeConsoleLog = null;
+var pendingGroupArgs = [];
+var printedGroupIndex = -1;
-var FocusEventInterface = assign({}, UIEventInterface, {
- relatedTarget: 0
-});
+function formatLanes(laneOrLanes) {
+ return "0b" + laneOrLanes.toString(2).padStart(31, "0");
+}
-var SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
- * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
- */
+function group() {
+ for (
+ var _len = arguments.length, groupArgs = new Array(_len), _key = 0;
+ _key < _len;
+ _key++
+ ) {
+ groupArgs[_key] = arguments[_key];
+ }
-var AnimationEventInterface = assign({}, EventInterface, {
- animationName: 0,
- elapsedTime: 0,
- pseudoElement: 0
-});
+ pendingGroupArgs.push(groupArgs);
-var SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/clipboard-apis/
- */
+ if (nativeConsoleLog === null) {
+ nativeConsoleLog = nativeConsole.log;
+ nativeConsole.log = log;
+ }
+}
-var ClipboardEventInterface = assign({}, EventInterface, {
- clipboardData: function (event) {
- return "clipboardData" in event
- ? event.clipboardData
- : window.clipboardData;
+function groupEnd() {
+ pendingGroupArgs.pop();
+
+ while (printedGroupIndex >= pendingGroupArgs.length) {
+ nativeConsole.groupEnd();
+ printedGroupIndex--;
}
-});
-var SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
- */
+ if (pendingGroupArgs.length === 0) {
+ nativeConsole.log = nativeConsoleLog;
+ nativeConsoleLog = null;
+ }
+}
-var CompositionEventInterface = assign({}, EventInterface, {
- data: 0
-});
+function log() {
+ if (printedGroupIndex < pendingGroupArgs.length - 1) {
+ for (var i = printedGroupIndex + 1; i < pendingGroupArgs.length; i++) {
+ var groupArgs = pendingGroupArgs[i];
+ nativeConsole.group.apply(nativeConsole, groupArgs);
+ }
-var SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
- * /#events-inputevents
- */
-// Happens to share the same list for now.
+ printedGroupIndex = pendingGroupArgs.length - 1;
+ }
-var SyntheticInputEvent = SyntheticCompositionEvent;
-/**
- * Normalization of deprecated HTML5 `key` values
- * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
- */
+ if (typeof nativeConsoleLog === "function") {
+ nativeConsoleLog.apply(void 0, arguments);
+ } else {
+ nativeConsole.log.apply(nativeConsole, arguments);
+ }
+}
-var normalizeKey = {
- Esc: "Escape",
- Spacebar: " ",
- Left: "ArrowLeft",
- Up: "ArrowUp",
- Right: "ArrowRight",
- Down: "ArrowDown",
- Del: "Delete",
- Win: "OS",
- Menu: "ContextMenu",
- Apps: "ContextMenu",
- Scroll: "ScrollLock",
- MozPrintableKey: "Unidentified"
-};
-/**
- * Translation from legacy `keyCode` to HTML5 `key`
- * Only special keys supported, all others depend on keyboard layout or browser
- * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
- */
-
-var translateToKey = {
- "8": "Backspace",
- "9": "Tab",
- "12": "Clear",
- "13": "Enter",
- "16": "Shift",
- "17": "Control",
- "18": "Alt",
- "19": "Pause",
- "20": "CapsLock",
- "27": "Escape",
- "32": " ",
- "33": "PageUp",
- "34": "PageDown",
- "35": "End",
- "36": "Home",
- "37": "ArrowLeft",
- "38": "ArrowUp",
- "39": "ArrowRight",
- "40": "ArrowDown",
- "45": "Insert",
- "46": "Delete",
- "112": "F1",
- "113": "F2",
- "114": "F3",
- "115": "F4",
- "116": "F5",
- "117": "F6",
- "118": "F7",
- "119": "F8",
- "120": "F9",
- "121": "F10",
- "122": "F11",
- "123": "F12",
- "144": "NumLock",
- "145": "ScrollLock",
- "224": "Meta"
-};
-/**
- * @param {object} nativeEvent Native browser event.
- * @return {string} Normalized `key` property.
- */
-
-function getEventKey(nativeEvent) {
- if (nativeEvent.key) {
- // Normalize inconsistent values reported by browsers due to
- // implementations of a working draft specification.
- // FireFox implements `key` but returns `MozPrintableKey` for all
- // printable characters (normalized to `Unidentified`), ignore it.
- var key = normalizeKey[nativeEvent.key] || nativeEvent.key; // $FlowFixMe unable to index with a `mixed` value
-
- if (key !== "Unidentified") {
- return key;
+var REACT_LOGO_STYLE =
+ "background-color: #20232a; color: #61dafb; padding: 0 2px;";
+function logCommitStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c commit%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
}
- } // Browser does not implement `key`, polyfill as much of it as we can.
-
- if (nativeEvent.type === "keypress") {
- var charCode = getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- nativeEvent
- ); // The enter-key is technically both printable and non-printable and can
- // thus be captured by `keypress`, no other non-printable key should.
-
- return charCode === 13 ? "Enter" : String.fromCharCode(charCode);
}
-
- if (nativeEvent.type === "keydown" || nativeEvent.type === "keyup") {
- // While user keyboard layout determines the actual meaning of each
- // `keyCode` value, almost all function keys have a universal value.
- // $FlowFixMe unable to index with a `mixed` value
- return translateToKey[nativeEvent.keyCode] || "Unidentified";
+}
+function logCommitStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
}
-
- return "";
}
-/**
- * Translation from modifier key to the associated property in the event.
- * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
- */
-
-var modifierKeyToProp = {
- Alt: "altKey",
- Control: "ctrlKey",
- Meta: "metaKey",
- Shift: "shiftKey"
-}; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
-// getModifierState. If getModifierState is not supported, we map it to a set of
-// modifier keys exposed by the event. In this case, Lock-keys are not supported.
-// $FlowFixMe[missing-local-annot]
-// $FlowFixMe[missing-this-annot]
+var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; // $FlowFixMe: Flow cannot handle polymorphic WeakMaps
-function modifierStateGetter(keyArg) {
- var syntheticEvent = this;
- var nativeEvent = syntheticEvent.nativeEvent;
+var wakeableIDs = new PossiblyWeakMap$1();
+var wakeableID = 0;
- if (nativeEvent.getModifierState) {
- return nativeEvent.getModifierState(keyArg);
+function getWakeableID(wakeable) {
+ if (!wakeableIDs.has(wakeable)) {
+ wakeableIDs.set(wakeable, wakeableID++);
}
- var keyProp = modifierKeyToProp[keyArg];
- return keyProp ? !!nativeEvent[keyProp] : false;
+ return wakeableIDs.get(wakeable);
}
-function getEventModifierState(nativeEvent) {
- return modifierStateGetter;
+function logComponentSuspended(componentName, wakeable) {
+ {
+ if (enableDebugTracing) {
+ var id = getWakeableID(wakeable);
+ var display = wakeable.displayName || wakeable;
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " suspended",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ wakeable.then(
+ function () {
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " resolved",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ },
+ function () {
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " rejected",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ }
+ );
+ }
+ }
}
-/**
- * @interface KeyboardEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
-
-var KeyboardEventInterface = assign({}, UIEventInterface, {
- key: getEventKey,
- code: 0,
- location: 0,
- ctrlKey: 0,
- shiftKey: 0,
- altKey: 0,
- metaKey: 0,
- repeat: 0,
- locale: 0,
- getModifierState: getEventModifierState,
- // Legacy Interface
- charCode: function (event) {
- // `charCode` is the result of a KeyPress event and represents the value of
- // the actual printable character.
- // KeyPress is deprecated, but its replacement is not yet final and not
- // implemented in any major browser. Only KeyPress has charCode.
- if (event.type === "keypress") {
- return getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- event
+function logLayoutEffectsStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c layout effects%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
);
}
-
- return 0;
- },
- keyCode: function (event) {
- // `keyCode` is the result of a KeyDown/Up event and represents the value of
- // physical keyboard key.
- // The actual meaning of the value depends on the users' keyboard layout
- // which cannot be detected. Assuming that it is a US keyboard layout
- // provides a surprisingly accurate mapping for US and European users.
- // Due to this, it is left to the user to implement at this time.
- if (event.type === "keydown" || event.type === "keyup") {
- return event.keyCode;
+ }
+}
+function logLayoutEffectsStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
}
-
- return 0;
- },
- which: function (event) {
- // `which` is an alias for either `keyCode` or `charCode` depending on the
- // type of the event.
- if (event.type === "keypress") {
- return getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- event
+ }
+}
+function logPassiveEffectsStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c passive effects%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
);
}
-
- if (event.type === "keydown" || event.type === "keyup") {
- return event.keyCode;
+ }
+}
+function logPassiveEffectsStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
}
-
- return 0;
}
-});
+}
+function logRenderStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c render%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
+ }
+ }
+}
+function logRenderStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
+ }
+}
+function logForceUpdateScheduled(componentName, lane) {
+ {
+ if (enableDebugTracing) {
+ log(
+ "%c\u269B\uFE0F%c " +
+ componentName +
+ " forced update %c(" +
+ formatLanes(lane) +
+ ")",
+ REACT_LOGO_STYLE,
+ "color: #db2e1f; font-weight: bold;",
+ ""
+ );
+ }
+ }
+}
+function logStateUpdateScheduled(componentName, lane, payloadOrAction) {
+ {
+ if (enableDebugTracing) {
+ log(
+ "%c\u269B\uFE0F%c " +
+ componentName +
+ " updated state %c(" +
+ formatLanes(lane) +
+ ")",
+ REACT_LOGO_STYLE,
+ "color: #01a252; font-weight: bold;",
+ "",
+ payloadOrAction
+ );
+ }
+ }
+}
-var SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface);
-/**
- * @interface PointerEvent
- * @see http://www.w3.org/TR/pointerevents/
- */
+// This is imported by the event replaying implementation in React DOM. It's
+// in a separate file to break a circular dependency between the renderer and
+// the reconciler.
+function isRootDehydrated(root) {
+ var currentState = root.current.memoizedState;
+ return currentState.isDehydrated;
+}
-var PointerEventInterface = assign({}, MouseEventInterface, {
- pointerId: 0,
- width: 0,
- height: 0,
- pressure: 0,
- tangentialPressure: 0,
- tiltX: 0,
- tiltY: 0,
- twist: 0,
- pointerType: 0,
- isPrimary: 0
-});
+// Intentionally not using it yet to derisk the initial implementation, because
+// the way we push/pop these values is a bit unusual. If there's a mistake, I'd
+// rather the ids be wrong than crash the whole reconciler.
-var SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface);
-/**
- * @interface TouchEvent
- * @see http://www.w3.org/TR/touch-events/
- */
+var forkStack = [];
+var forkStackIndex = 0;
+var treeForkProvider = null;
+var treeForkCount = 0;
+var idStack = [];
+var idStackIndex = 0;
+var treeContextProvider = null;
+var treeContextId = 1;
+var treeContextOverflow = "";
+function isForkedChild(workInProgress) {
+ warnIfNotHydrating();
+ return (workInProgress.flags & Forked) !== NoFlags$1;
+}
+function getForksAtLevel(workInProgress) {
+ warnIfNotHydrating();
+ return treeForkCount;
+}
+function getTreeId() {
+ var overflow = treeContextOverflow;
+ var idWithLeadingBit = treeContextId;
+ var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit);
+ return id.toString(32) + overflow;
+}
+function pushTreeFork(workInProgress, totalChildren) {
+ // This is called right after we reconcile an array (or iterator) of child
+ // fibers, because that's the only place where we know how many children in
+ // the whole set without doing extra work later, or storing addtional
+ // information on the fiber.
+ //
+ // That's why this function is separate from pushTreeId — it's called during
+ // the render phase of the fork parent, not the child, which is where we push
+ // the other context values.
+ //
+ // In the Fizz implementation this is much simpler because the child is
+ // rendered in the same callstack as the parent.
+ //
+ // It might be better to just add a `forks` field to the Fiber type. It would
+ // make this module simpler.
+ warnIfNotHydrating();
+ forkStack[forkStackIndex++] = treeForkCount;
+ forkStack[forkStackIndex++] = treeForkProvider;
+ treeForkProvider = workInProgress;
+ treeForkCount = totalChildren;
+}
+function pushTreeId(workInProgress, totalChildren, index) {
+ warnIfNotHydrating();
+ idStack[idStackIndex++] = treeContextId;
+ idStack[idStackIndex++] = treeContextOverflow;
+ idStack[idStackIndex++] = treeContextProvider;
+ treeContextProvider = workInProgress;
+ var baseIdWithLeadingBit = treeContextId;
+ var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
+ // of the id; we use it to account for leading 0s.
-var TouchEventInterface = assign({}, UIEventInterface, {
- touches: 0,
- targetTouches: 0,
- changedTouches: 0,
- altKey: 0,
- metaKey: 0,
- ctrlKey: 0,
- shiftKey: 0,
- getModifierState: getEventModifierState
-});
+ var baseLength = getBitLength(baseIdWithLeadingBit) - 1;
+ var baseId = baseIdWithLeadingBit & ~(1 << baseLength);
+ var slot = index + 1;
+ var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into
+ // consideration the leading 1 we use to mark the end of the sequence.
-var SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
- * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
- */
+ if (length > 30) {
+ // We overflowed the bitwise-safe range. Fall back to slower algorithm.
+ // This branch assumes the length of the base id is greater than 5; it won't
+ // work for smaller ids, because you need 5 bits per character.
+ //
+ // We encode the id in multiple steps: first the base id, then the
+ // remaining digits.
+ //
+ // Each 5 bit sequence corresponds to a single base 32 character. So for
+ // example, if the current id is 23 bits long, we can convert 20 of those
+ // bits into a string of 4 characters, with 3 bits left over.
+ //
+ // First calculate how many bits in the base id represent a complete
+ // sequence of characters.
+ var numberOfOverflowBits = baseLength - (baseLength % 5); // Then create a bitmask that selects only those bits.
-var TransitionEventInterface = assign({}, EventInterface, {
- propertyName: 0,
- elapsedTime: 0,
- pseudoElement: 0
-});
+ var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string.
-var SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface);
-/**
- * @interface WheelEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+ var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id.
-var WheelEventInterface = assign({}, MouseEventInterface, {
- deltaX: function (event) {
- return "deltaX" in event
- ? event.deltaX // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
- : "wheelDeltaX" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDeltaX
- : 0;
- },
- deltaY: function (event) {
- return "deltaY" in event
- ? event.deltaY // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
- : "wheelDeltaY" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDeltaY // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
- : "wheelDelta" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDelta
- : 0;
- },
- deltaZ: 0,
- // Browsers without "deltaMode" is reporting in raw wheel delta where one
- // notch on the scroll is always +/- 120, roughly equivalent to pixels.
- // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
- // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
- deltaMode: 0
-});
+ var restOfBaseId = baseId >> numberOfOverflowBits;
+ var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because
+ // we made more room, this time it won't overflow.
-var SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface);
+ var restOfLength = getBitLength(totalChildren) + restOfBaseLength;
+ var restOfNewBits = slot << restOfBaseLength;
+ var id = restOfNewBits | restOfBaseId;
+ var overflow = newOverflow + baseOverflow;
+ treeContextId = (1 << restOfLength) | id;
+ treeContextOverflow = overflow;
+ } else {
+ // Normal path
+ var newBits = slot << baseLength;
-var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
+ var _id = newBits | baseId;
-var START_KEYCODE = 229;
-var canUseCompositionEvent = canUseDOM && "CompositionEvent" in window;
-var documentMode = null;
+ var _overflow = baseOverflow;
+ treeContextId = (1 << length) | _id;
+ treeContextOverflow = _overflow;
+ }
+}
+function pushMaterializedTreeId(workInProgress) {
+ warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear
+ // in its children.
-if (canUseDOM && "documentMode" in document) {
- documentMode = document.documentMode;
-} // Webkit offers a very useful `textInput` event that can be used to
-// directly represent `beforeInput`. The IE `textinput` event is not as
-// useful, so we don't use it.
+ var returnFiber = workInProgress.return;
-var canUseTextInputEvent = canUseDOM && "TextEvent" in window && !documentMode; // In IE9+, we have access to composition events, but the data supplied
-// by the native compositionend event may be incorrect. Japanese ideographic
-// spaces, for instance (\u3000) are not recorded correctly.
+ if (returnFiber !== null) {
+ var numberOfForks = 1;
+ var slotIndex = 0;
+ pushTreeFork(workInProgress, numberOfForks);
+ pushTreeId(workInProgress, numberOfForks, slotIndex);
+ }
+}
-var useFallbackCompositionData =
- canUseDOM &&
- (!canUseCompositionEvent ||
- (documentMode && documentMode > 8 && documentMode <= 11));
-var SPACEBAR_CODE = 32;
-var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
+function getBitLength(number) {
+ return 32 - clz32(number);
+}
-function registerEvents$3() {
- registerTwoPhaseEvent("onBeforeInput", [
- "compositionend",
- "keypress",
- "textInput",
- "paste"
- ]);
- registerTwoPhaseEvent("onCompositionEnd", [
- "compositionend",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
- registerTwoPhaseEvent("onCompositionStart", [
- "compositionstart",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
- registerTwoPhaseEvent("onCompositionUpdate", [
- "compositionupdate",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
-} // Track whether we've ever handled a keypress on the space key.
-
-var hasSpaceKeypress = false;
-/**
- * Return whether a native keypress event is assumed to be a command.
- * This is required because Firefox fires `keypress` events for key commands
- * (cut, copy, select-all, etc.) even though no character is inserted.
- */
-
-function isKeypressCommand(nativeEvent) {
- return (
- (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command.
- !(nativeEvent.ctrlKey && nativeEvent.altKey)
- );
+function getLeadingBit(id) {
+ return 1 << (getBitLength(id) - 1);
}
-/**
- * Translate native top level events into event types.
- */
-function getCompositionEventType(domEventName) {
- switch (domEventName) {
- case "compositionstart":
- return "onCompositionStart";
+function popTreeContext(workInProgress) {
+ // Restore the previous values.
+ // This is a bit more complicated than other context-like modules in Fiber
+ // because the same Fiber may appear on the stack multiple times and for
+ // different reasons. We have to keep popping until the work-in-progress is
+ // no longer at the top of the stack.
+ while (workInProgress === treeForkProvider) {
+ treeForkProvider = forkStack[--forkStackIndex];
+ forkStack[forkStackIndex] = null;
+ treeForkCount = forkStack[--forkStackIndex];
+ forkStack[forkStackIndex] = null;
+ }
- case "compositionend":
- return "onCompositionEnd";
+ while (workInProgress === treeContextProvider) {
+ treeContextProvider = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ treeContextOverflow = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ treeContextId = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ }
+}
+function getSuspendedTreeContext() {
+ warnIfNotHydrating();
- case "compositionupdate":
- return "onCompositionUpdate";
+ if (treeContextProvider !== null) {
+ return {
+ id: treeContextId,
+ overflow: treeContextOverflow
+ };
+ } else {
+ return null;
}
}
-/**
- * Does our fallback best-guess model think this event signifies that
- * composition has begun?
- */
+function restoreSuspendedTreeContext(workInProgress, suspendedContext) {
+ warnIfNotHydrating();
+ idStack[idStackIndex++] = treeContextId;
+ idStack[idStackIndex++] = treeContextOverflow;
+ idStack[idStackIndex++] = treeContextProvider;
+ treeContextId = suspendedContext.id;
+ treeContextOverflow = suspendedContext.overflow;
+ treeContextProvider = workInProgress;
+}
-function isFallbackCompositionStart(domEventName, nativeEvent) {
- return domEventName === "keydown" && nativeEvent.keyCode === START_KEYCODE;
+function warnIfNotHydrating() {
+ {
+ if (!getIsHydrating()) {
+ error(
+ "Expected to be hydrating. This is a bug in React. Please file " +
+ "an issue."
+ );
+ }
+ }
}
-/**
- * Does our fallback mode think that this event is the end of composition?
- */
-function isFallbackCompositionEnd(domEventName, nativeEvent) {
- switch (domEventName) {
- case "keyup":
- // Command keys insert or clear IME input.
- return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
+// This may have been an insertion or a hydration.
- case "keydown":
- // Expect IME keyCode on each keydown. If we get any other
- // code we must have exited earlier.
- return nativeEvent.keyCode !== START_KEYCODE;
+var hydrationParentFiber = null;
+var nextHydratableInstance = null;
+var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
+// due to earlier mismatches or a suspended fiber.
- case "keypress":
- case "mousedown":
- case "focusout":
- // Events are not possible without cancelling IME.
- return true;
+var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary
- default:
- return false;
+var hydrationErrors = null;
+var rootOrSingletonContext = false;
+
+function warnIfHydrating() {
+ {
+ if (isHydrating) {
+ error(
+ "We should not be hydrating here. This is a bug in React. Please file a bug."
+ );
+ }
}
}
-/**
- * Google Input Tools provides composition data via a CustomEvent,
- * with the `data` property populated in the `detail` object. If this
- * is available on the event object, use it. If not, this is a plain
- * composition event and we have nothing special to extract.
- *
- * @param {object} nativeEvent
- * @return {?string}
- */
-
-function getDataFromCustomEvent(nativeEvent) {
- var detail = nativeEvent.detail;
- if (typeof detail === "object" && "data" in detail) {
- return detail.data;
+function markDidThrowWhileHydratingDEV() {
+ {
+ didSuspendOrErrorDEV = true;
+ }
+}
+function didSuspendOrErrorWhileHydratingDEV() {
+ {
+ return didSuspendOrErrorDEV;
}
-
- return null;
}
-/**
- * Check if a composition event was triggered by Korean IME.
- * Our fallback mode does not work well with IE's Korean IME,
- * so just use native composition events when Korean IME is used.
- * Although CompositionEvent.locale property is deprecated,
- * it is available in IE, where our fallback mode is enabled.
- *
- * @param {object} nativeEvent
- * @return {boolean}
- */
-
-function isUsingKoreanIME(nativeEvent) {
- return nativeEvent.locale === "ko";
-} // Track the current IME composition status, if any.
-var isComposing = false;
-/**
- * @return {?object} A SyntheticCompositionEvent.
- */
+function enterHydrationState(fiber) {
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance =
+ getFirstHydratableChildWithinContainer(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ hydrationErrors = null;
+ didSuspendOrErrorDEV = false;
+ rootOrSingletonContext = true;
+ return true;
+}
-function extractCompositionEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
+function reenterHydrationStateFromDehydratedSuspenseInstance(
+ fiber,
+ suspenseInstance,
+ treeContext
) {
- var eventType;
- var fallbackData;
+ nextHydratableInstance =
+ getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ hydrationErrors = null;
+ didSuspendOrErrorDEV = false;
+ rootOrSingletonContext = false;
- if (canUseCompositionEvent) {
- eventType = getCompositionEventType(domEventName);
- } else if (!isComposing) {
- if (isFallbackCompositionStart(domEventName, nativeEvent)) {
- eventType = "onCompositionStart";
- }
- } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) {
- eventType = "onCompositionEnd";
+ if (treeContext !== null) {
+ restoreSuspendedTreeContext(fiber, treeContext);
}
- if (!eventType) {
- return null;
- }
+ return true;
+}
- if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
- // The current composition is stored statically and must not be
- // overwritten while composition continues.
- if (!isComposing && eventType === "onCompositionStart") {
- isComposing = initialize(nativeEventTarget);
- } else if (eventType === "onCompositionEnd") {
- if (isComposing) {
- fallbackData = getData();
+function warnUnhydratedInstance(returnFiber, instance) {
+ {
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ didNotHydrateInstanceWithinContainer(
+ returnFiber.stateNode.containerInfo,
+ instance
+ );
+ break;
}
- }
- }
-
- var listeners = accumulateTwoPhaseListeners(targetInst, eventType);
-
- if (listeners.length > 0) {
- var event = new SyntheticCompositionEvent(
- eventType,
- domEventName,
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- if (fallbackData) {
- // Inject data generated from fallback path into the synthetic event.
- // This matches the property of native CompositionEventInterface.
- // $FlowFixMe[incompatible-use]
- event.data = fallbackData;
- } else {
- var customData = getDataFromCustomEvent(nativeEvent);
+ case HostSingleton:
+ case HostComponent: {
+ var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotHydrateInstance(
+ returnFiber.type,
+ returnFiber.memoizedProps,
+ returnFiber.stateNode,
+ instance, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode
+ );
+ break;
+ }
- if (customData !== null) {
- // $FlowFixMe[incompatible-use]
- event.data = customData;
+ case SuspenseComponent: {
+ var suspenseState = returnFiber.memoizedState;
+ if (suspenseState.dehydrated !== null)
+ didNotHydrateInstanceWithinSuspenseInstance(
+ suspenseState.dehydrated,
+ instance
+ );
+ break;
}
}
}
}
-function getNativeBeforeInputChars(domEventName, nativeEvent) {
- switch (domEventName) {
- case "compositionend":
- return getDataFromCustomEvent(nativeEvent);
+function deleteHydratableInstance(returnFiber, instance) {
+ warnUnhydratedInstance(returnFiber, instance);
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete.return = returnFiber;
+ var deletions = returnFiber.deletions;
- case "keypress":
- /**
- * If native `textInput` events are available, our goal is to make
- * use of them. However, there is a special case: the spacebar key.
- * In Webkit, preventing default on a spacebar `textInput` event
- * cancels character insertion, but it *also* causes the browser
- * to fall back to its default spacebar behavior of scrolling the
- * page.
- *
- * Tracking at:
- * https://code.google.com/p/chromium/issues/detail?id=355103
- *
- * To avoid this issue, use the keypress event as if no `textInput`
- * event is available.
- */
- var which = nativeEvent.which;
+ if (deletions === null) {
+ returnFiber.deletions = [childToDelete];
+ returnFiber.flags |= ChildDeletion;
+ } else {
+ deletions.push(childToDelete);
+ }
+}
- if (which !== SPACEBAR_CODE) {
- return null;
- }
+function warnNonhydratedInstance(returnFiber, fiber) {
+ {
+ if (didSuspendOrErrorDEV) {
+ // Inside a boundary that already suspended. We're currently rendering the
+ // siblings of a suspended node. The mismatch may be due to the missing
+ // data, so it's probably a false positive.
+ return;
+ }
- hasSpaceKeypress = true;
- return SPACEBAR_CHAR;
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ var parentContainer = returnFiber.stateNode.containerInfo;
- case "textInput":
- // Record the characters to be added to the DOM.
- var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled
- // it at the keypress level and bail immediately. Android Chrome
- // doesn't give us keycodes, so we need to ignore it.
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent:
+ var type = fiber.type;
+ didNotFindHydratableInstanceWithinContainer(parentContainer, type);
+ break;
- if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
- return null;
- }
+ case HostText:
+ var text = fiber.pendingProps;
+ didNotFindHydratableTextInstanceWithinContainer(
+ parentContainer,
+ text
+ );
+ break;
+ }
- return chars;
+ break;
+ }
- default:
- // For other native event types, do nothing.
- return null;
- }
-}
-/**
- * For browsers that do not provide the `textInput` event, extract the
- * appropriate string to use for SyntheticInputEvent.
- */
+ case HostSingleton:
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
-function getFallbackBeforeInputChars(domEventName, nativeEvent) {
- // If we are currently composing (IME) and using a fallback to do so,
- // try to extract the composed characters from the fallback object.
- // If composition event is available, we extract a string only at
- // compositionevent, otherwise extract it at fallback events.
- if (isComposing) {
- if (
- domEventName === "compositionend" ||
- (!canUseCompositionEvent &&
- isFallbackCompositionEnd(domEventName, nativeEvent))
- ) {
- var chars = getData();
- reset();
- isComposing = false;
- return chars;
- }
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent: {
+ var _type = fiber.type;
+ var _props = fiber.pendingProps;
+ var isConcurrentMode =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotFindHydratableInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _type,
+ _props, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode
+ );
+ break;
+ }
- return null;
- }
+ case HostText: {
+ var _text = fiber.pendingProps;
- switch (domEventName) {
- case "paste":
- // If a paste event occurs after a keypress, throw out the input
- // chars. Paste events should not lead to BeforeInput events.
- return null;
+ var _isConcurrentMode =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
- case "keypress":
- /**
- * As of v27, Firefox may fire keypress events even when no character
- * will be inserted. A few possibilities:
- *
- * - `which` is `0`. Arrow keys, Esc key, etc.
- *
- * - `which` is the pressed key code, but no char is available.
- * Ex: 'AltGr + d` in Polish. There is no modified character for
- * this key combination and no character is inserted into the
- * document, but FF fires the keypress for char code `100` anyway.
- * No `input` event will occur.
- *
- * - `which` is the pressed key code, but a command combination is
- * being used. Ex: `Cmd+C`. No character is inserted, and no
- * `input` event will occur.
- */
- if (!isKeypressCommand(nativeEvent)) {
- // IE fires the `keypress` event when a user types an emoji via
- // Touch keyboard of Windows. In such a case, the `char` property
- // holds an emoji character like `\uD83D\uDE0A`. Because its length
- // is 2, the property `which` does not represent an emoji correctly.
- // In such a case, we directly return the `char` property instead of
- // using `which`.
- if (nativeEvent.char && nativeEvent.char.length > 1) {
- return nativeEvent.char;
- } else if (nativeEvent.which) {
- return String.fromCharCode(nativeEvent.which);
+ didNotFindHydratableTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _text, // TODO: Delete this argument when we remove the legacy root API.
+ _isConcurrentMode
+ );
+ break;
+ }
}
+
+ break;
}
- return null;
+ case SuspenseComponent: {
+ var suspenseState = returnFiber.memoizedState;
+ var _parentInstance = suspenseState.dehydrated;
+ if (_parentInstance !== null)
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent:
+ var _type2 = fiber.type;
+ didNotFindHydratableInstanceWithinSuspenseInstance(
+ _parentInstance,
+ _type2
+ );
+ break;
- case "compositionend":
- return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)
- ? null
- : nativeEvent.data;
+ case HostText:
+ var _text2 = fiber.pendingProps;
+ didNotFindHydratableTextInstanceWithinSuspenseInstance(
+ _parentInstance,
+ _text2
+ );
+ break;
+ }
+ break;
+ }
- default:
- return null;
+ default:
+ return;
+ }
}
}
-/**
- * Extract a SyntheticInputEvent for `beforeInput`, based on either native
- * `textInput` or fallback behavior.
- *
- * @return {?object} A SyntheticInputEvent.
- */
-function extractBeforeInputEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
-) {
- var chars;
-
- if (canUseTextInputEvent) {
- chars = getNativeBeforeInputChars(domEventName, nativeEvent);
- } else {
- chars = getFallbackBeforeInputChars(domEventName, nativeEvent);
- } // If no characters are being inserted, no BeforeInput event should
- // be fired.
-
- if (!chars) {
- return null;
- }
-
- var listeners = accumulateTwoPhaseListeners(targetInst, "onBeforeInput");
+function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.flags = (fiber.flags & ~Hydrating) | Placement;
+ warnNonhydratedInstance(returnFiber, fiber);
+}
- if (listeners.length > 0) {
- var event = new SyntheticInputEvent(
- "onBeforeInput",
- "beforeinput",
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- }); // $FlowFixMe[incompatible-use]
+function tryHydrateInstance(fiber, nextInstance) {
+ // fiber is a HostComponent Fiber
+ var instance = canHydrateInstance(nextInstance, fiber.type);
- event.data = chars;
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(instance);
+ rootOrSingletonContext = false;
+ return true;
}
-}
-/**
- * Create an `onBeforeInput` event to match
- * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
- *
- * This event plugin is based on the native `textInput` event
- * available in Chrome, Safari, Opera, and IE. This event fires after
- * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
- *
- * `beforeInput` is spec'd but not implemented in any browsers, and
- * the `input` event does not provide any useful information about what has
- * actually been added, contrary to the spec. Thus, `textInput` is the best
- * available event to identify the characters that have actually been inserted
- * into the target node.
- *
- * This plugin is also responsible for emitting `composition` events, thus
- * allowing us to share composition fallback code for both `beforeInput` and
- * `composition` event types.
- */
-function extractEvents$5(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- extractCompositionEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractBeforeInputEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
+ return false;
}
-/**
- * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
- */
-var supportedInputTypes = {
- color: true,
- date: true,
- datetime: true,
- "datetime-local": true,
- email: true,
- month: true,
- number: true,
- password: true,
- range: true,
- search: true,
- tel: true,
- text: true,
- time: true,
- url: true,
- week: true
-};
-
-function isTextInputElement(elem) {
- var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+function tryHydrateText(fiber, nextInstance) {
+ // fiber is a HostText Fiber
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
- if (nodeName === "input") {
- return !!supportedInputTypes[elem.type];
- }
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate.
- if (nodeName === "textarea") {
+ nextHydratableInstance = null;
return true;
}
return false;
}
-/**
- * Checks if an event is supported in the current execution environment.
- *
- * NOTE: This will not work correctly for non-generic events such as `change`,
- * `reset`, `load`, `error`, and `select`.
- *
- * Borrows from Modernizr.
- *
- * @param {string} eventNameSuffix Event name, e.g. "click".
- * @return {boolean} True if the event is supported.
- * @internal
- * @license Modernizr 3.0.0pre (Custom Build) | MIT
- */
+function tryHydrateSuspense(fiber, nextInstance) {
+ // fiber is a SuspenseComponent Fiber
+ var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
-function isEventSupported(eventNameSuffix) {
- if (!canUseDOM) {
- return false;
- }
+ if (suspenseInstance !== null) {
+ var suspenseState = {
+ dehydrated: suspenseInstance,
+ treeContext: getSuspendedTreeContext(),
+ retryLane: OffscreenLane
+ };
+ fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber.
+ // This simplifies the code for getHostSibling and deleting nodes,
+ // since it doesn't have to consider all Suspense boundaries and
+ // check if they're dehydrated ones or not.
- var eventName = "on" + eventNameSuffix;
- var isSupported = eventName in document;
+ var dehydratedFragment =
+ createFiberFromDehydratedFragment(suspenseInstance);
+ dehydratedFragment.return = fiber;
+ fiber.child = dehydratedFragment;
+ hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into
+ // it during the first pass. Instead, we'll reenter it later.
- if (!isSupported) {
- var element = document.createElement("div");
- element.setAttribute(eventName, "return;");
- isSupported = typeof element[eventName] === "function";
+ nextHydratableInstance = null;
+ return true;
}
- return isSupported;
-}
-
-function registerEvents$2() {
- registerTwoPhaseEvent("onChange", [
- "change",
- "click",
- "focusin",
- "focusout",
- "input",
- "keydown",
- "keyup",
- "selectionchange"
- ]);
+ return false;
}
-function createAndAccumulateChangeEvent(
- dispatchQueue,
- inst,
- nativeEvent,
- target
-) {
- // Flag this event loop as needing state restore.
- enqueueStateRestore(target);
- var listeners = accumulateTwoPhaseListeners(inst, "onChange");
-
- if (listeners.length > 0) {
- var event = new SyntheticEvent(
- "onChange",
- "change",
- null,
- nativeEvent,
- target
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- }
+function shouldClientRenderOnMismatch(fiber) {
+ return (
+ (fiber.mode & ConcurrentMode) !== NoMode &&
+ (fiber.flags & DidCapture) === NoFlags$1
+ );
}
-/**
- * For IE shims
- */
-
-var activeElement$1 = null;
-var activeElementInst$1 = null;
-/**
- * SECTION: handle `change` event
- */
-function shouldUseChangeEvent(elem) {
- var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
- return (
- nodeName === "select" || (nodeName === "input" && elem.type === "file")
+function throwOnHydrationMismatch(fiber) {
+ throw new Error(
+ "Hydration failed because the initial UI does not match what was " +
+ "rendered on the server."
);
}
-function manualDispatchChangeEvent(nativeEvent) {
- var dispatchQueue = [];
- createAndAccumulateChangeEvent(
- dispatchQueue,
- activeElementInst$1,
- nativeEvent,
- getEventTarget(nativeEvent)
- ); // If change and propertychange bubbled, we'd just bind to it like all the
- // other events and have it go through ReactBrowserEventEmitter. Since it
- // doesn't, we manually listen for the events and so we have to enqueue and
- // process the abstract event manually.
- //
- // Batching is necessary here in order to ensure that all event handlers run
- // before the next rerender (including event handlers attached to ancestor
- // elements instead of directly on the input). Without this, controlled
- // components don't work properly in conjunction with event bubbling because
- // the component is rerendered and the value reverted before all the event
- // handlers can run. See https://github.com/facebook/react/issues/708.
+function claimHydratableSingleton(fiber) {
+ {
+ if (!isHydrating) {
+ return;
+ }
- batchedUpdates$1(runEventInBatch, dispatchQueue);
+ var currentRootContainer = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ var instance = (fiber.stateNode = resolveSingletonInstance(
+ fiber.type,
+ fiber.pendingProps,
+ currentRootContainer,
+ currentHostContext,
+ false
+ ));
+ hydrationParentFiber = fiber;
+ rootOrSingletonContext = true;
+ nextHydratableInstance = getFirstHydratableChild(instance);
+ }
}
-function runEventInBatch(dispatchQueue) {
- processDispatchQueue(dispatchQueue, 0);
+function advanceToFirstAttemptableInstance(fiber) {
+ // fiber is HostComponent Fiber
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForInstance(
+ nextHydratableInstance,
+ fiber.type,
+ fiber.pendingProps
+ )
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
+ }
}
-function getInstIfValueChanged(targetInst) {
- var targetNode = getNodeFromInstance(targetInst);
-
- if (updateValueIfChanged(targetNode)) {
- return targetInst;
+function advanceToFirstAttemptableTextInstance() {
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForTextInstance(nextHydratableInstance)
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
}
}
-function getTargetInstForChangeEvent(domEventName, targetInst) {
- if (domEventName === "change") {
- return targetInst;
+function advanceToFirstAttemptableSuspenseInstance() {
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForSuspenseInstance(nextHydratableInstance)
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
}
}
-/**
- * SECTION: handle `input` event
- */
-var isInputEventSupported = false;
+function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
-if (canUseDOM) {
- // IE9 claims to support the input event but fails to trigger it when
- // deleting text, so we ignore its input events.
- isInputEventSupported =
- isEventSupported("input") &&
- (!document.documentMode || document.documentMode > 9);
-}
-/**
- * (For IE <=9) Starts tracking propertychange events on the passed-in element
- * and override the value property so that we can distinguish user events from
- * value changes in JS.
- */
+ {
+ if (!isHydratableType(fiber.type, fiber.pendingProps)) {
+ // This fiber never hydrates from the DOM and always does an insert
+ fiber.flags = (fiber.flags & ~Hydrating) | Placement;
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ }
-function startWatchingForValueChange(target, targetInst) {
- activeElement$1 = target;
- activeElementInst$1 = targetInst;
- activeElement$1.attachEvent("onpropertychange", handlePropertyChange);
-}
-/**
- * (For IE <=9) Removes the event listeners from the currently-tracked element,
- * if any exists.
- */
+ var initialInstance = nextHydratableInstance;
-function stopWatchingForValueChange() {
- if (!activeElement$1) {
- return;
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableInstance(fiber);
}
- activeElement$1.detachEvent("onpropertychange", handlePropertyChange);
- activeElement$1 = null;
- activeElementInst$1 = null;
-}
-/**
- * (For IE <=9) Handles a propertychange event, sending a `change` event if
- * the value of the active element has changed.
- */
-// $FlowFixMe[missing-local-annot]
+ var nextInstance = nextHydratableInstance;
-function handlePropertyChange(nativeEvent) {
- if (nativeEvent.propertyName !== "value") {
+ if (!nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
+
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
return;
}
- if (getInstIfValueChanged(activeElementInst$1)) {
- manualDispatchChangeEvent(nativeEvent);
- }
-}
+ var firstAttemptedInstance = nextInstance;
-function handleEventsForInputEventPolyfill(domEventName, target, targetInst) {
- if (domEventName === "focusin") {
- // In IE9, propertychange fires for most input events but is buggy and
- // doesn't fire when text is deleted, but conveniently, selectionchange
- // appears to fire in all of the remaining cases so we catch those and
- // forward the event if the value has changed
- // In either case, we don't want to call the event handler if the value
- // is changed from JS so we redefine a setter for `.value` that updates
- // our activeElementValue variable, allowing us to ignore those changes
- //
- // stopWatching() should be a noop here but we call it just in case we
- // missed a blur event somehow.
- stopWatchingForValueChange();
- startWatchingForValueChange(target, targetInst);
- } else if (domEventName === "focusout") {
- stopWatchingForValueChange();
- }
-} // For IE8 and IE9.
+ if (!tryHydrateInstance(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
-function getTargetInstForInputEventPolyfill(domEventName, targetInst) {
- if (
- domEventName === "selectionchange" ||
- domEventName === "keyup" ||
- domEventName === "keydown"
- ) {
- // On the selectionchange event, the target is just document which isn't
- // helpful for us so just check activeElement instead.
- //
- // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
- // propertychange on the first input event after setting `value` from a
- // script and fires only keydown, keypress, keyup. Catching keyup usually
- // gets it and catching keydown lets us fire an event for the first
- // keystroke if user does a key repeat (it'll be a little delayed: right
- // before the second keystroke). Other input methods (e.g., paste) seem to
- // fire selectionchange normally.
- return getInstIfValueChanged(activeElementInst$1);
- }
-}
-/**
- * SECTION: handle `click` event
- */
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
-function shouldUseClickEvent(elem) {
- // Use the `click` event to detect changes to checkbox and radio inputs.
- // This approach works across all browsers, whereas `change` does not fire
- // until `blur` in IE8.
- var nodeName = elem.nodeName;
- return (
- nodeName &&
- nodeName.toLowerCase() === "input" &&
- (elem.type === "checkbox" || elem.type === "radio")
- );
-}
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableInstance(fiber);
+ }
-function getTargetInstForClickEvent(domEventName, targetInst) {
- if (domEventName === "click") {
- return getInstIfValueChanged(targetInst);
- }
-}
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateInstance(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ 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.
-function getTargetInstForInputOrChangeEvent(domEventName, targetInst) {
- if (domEventName === "input" || domEventName === "change") {
- return getInstIfValueChanged(targetInst);
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
}
-function handleControlledInputBlur(node) {
- var state = node._wrapperState;
-
- if (!state || !state.controlled || node.type !== "number") {
+function tryToClaimNextHydratableTextInstance(fiber) {
+ if (!isHydrating) {
return;
}
- if (!disableInputAttributeSyncing) {
- // If controlled, assign the value attribute to the current value on blur
- setDefaultValue(node, "number", node.value);
+ var text = fiber.pendingProps;
+ var isHydratable = isHydratableText(text);
+ var initialInstance = nextHydratableInstance;
+
+ if (rootOrSingletonContext && isHydratable) {
+ // We may need to skip past certain nodes in these contexts.
+ // We don't skip if the text is not hydratable because we know no hydratables
+ // exist which could match this Fiber
+ advanceToFirstAttemptableTextInstance();
}
-}
-/**
- * This plugin creates an `onChange` event that normalizes change events
- * across form elements. This event fires at a time when it's possible to
- * change the element's value without seeing a flicker.
- *
- * Supported elements are:
- * - input (see `isTextInputElement`)
- * - textarea
- * - select
- */
-function extractEvents$4(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
- var getTargetInstFunc, handleEventFunc;
+ var nextInstance = nextHydratableInstance;
- if (shouldUseChangeEvent(targetNode)) {
- getTargetInstFunc = getTargetInstForChangeEvent;
- } else if (isTextInputElement(targetNode)) {
- if (isInputEventSupported) {
- getTargetInstFunc = getTargetInstForInputOrChangeEvent;
- } else {
- getTargetInstFunc = getTargetInstForInputEventPolyfill;
- handleEventFunc = handleEventsForInputEventPolyfill;
- }
- } else if (shouldUseClickEvent(targetNode)) {
- getTargetInstFunc = getTargetInstForClickEvent;
- } else if (
- enableCustomElementPropertySupport &&
- targetInst &&
- isCustomComponent(targetInst.elementType, targetInst.memoizedProps)
- ) {
- getTargetInstFunc = getTargetInstForChangeEvent;
+ if (!nextInstance || !isHydratable) {
+ // We exclude non hydrabable text because we know there are no matching hydratables.
+ // We either throw or insert depending on the render mode.
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
+
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ return;
}
- if (getTargetInstFunc) {
- var inst = getTargetInstFunc(domEventName, targetInst);
+ var firstAttemptedInstance = nextInstance;
- if (inst) {
- createAndAccumulateChangeEvent(
- dispatchQueue,
- inst,
- nativeEvent,
- nativeEventTarget
- );
- return;
+ if (!tryHydrateText(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
+
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
+
+ if (rootOrSingletonContext && isHydratable) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableTextInstance();
}
- }
- if (handleEventFunc) {
- handleEventFunc(domEventName, targetNode, targetInst);
- } // When blurring, set the value attribute for number inputs
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateText(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ 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.
- if (domEventName === "focusout") {
- handleControlledInputBlur(targetNode);
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
}
-function registerEvents$1() {
- registerDirectEvent("onMouseEnter", ["mouseout", "mouseover"]);
- registerDirectEvent("onMouseLeave", ["mouseout", "mouseover"]);
- registerDirectEvent("onPointerEnter", ["pointerout", "pointerover"]);
- registerDirectEvent("onPointerLeave", ["pointerout", "pointerover"]);
-}
-/**
- * For almost every interaction we care about, there will be both a top-level
- * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
- * we do not extract duplicate events. However, moving the mouse into the
- * browser from outside will not fire a `mouseout` event. In this case, we use
- * the `mouseover` top-level event.
- */
-
-function extractEvents$3(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var isOverEvent =
- domEventName === "mouseover" || domEventName === "pointerover";
- var isOutEvent = domEventName === "mouseout" || domEventName === "pointerout";
+function tryToClaimNextHydratableSuspenseInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
- if (isOverEvent && !isReplayingEvent(nativeEvent)) {
- // If this is an over event with a target, we might have already dispatched
- // the event in the out event of the other target. If this is replayed,
- // then it's because we couldn't dispatch against this target previously
- // so we have to do it now instead.
- var related = nativeEvent.relatedTarget || nativeEvent.fromElement;
+ var initialInstance = nextHydratableInstance;
- if (related) {
- // If the related node is managed by React, we can assume that we have
- // already dispatched the corresponding events during its mouseout.
- if (
- getClosestInstanceFromNode(related) ||
- isContainerMarkedAsRoot(related)
- ) {
- return;
- }
- }
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableSuspenseInstance();
}
- if (!isOutEvent && !isOverEvent) {
- // Must not be a mouse or pointer in or out - ignoring.
+ var nextInstance = nextHydratableInstance;
+
+ if (!nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
+
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
return;
}
- var win; // TODO: why is this nullable in the types but we read from it?
+ var firstAttemptedInstance = nextInstance;
- if (nativeEventTarget.window === nativeEventTarget) {
- // `nativeEventTarget` is probably a window object.
- win = nativeEventTarget;
- } else {
- // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
- var doc = nativeEventTarget.ownerDocument;
+ if (!tryHydrateSuspense(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
- if (doc) {
- win = doc.defaultView || doc.parentWindow;
- } else {
- win = window;
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
+
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableSuspenseInstance();
}
+
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateSuspense(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ return;
+ } // We matched the next one, we'll now assume that the first one was
+ // superfluous and we'll delete it. Since we can't eagerly delete it
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
+ // fiber associated with it.
+
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
+}
- var from;
- var to;
+function prepareToHydrateHostInstance(fiber, hostContext) {
+ var instance = fiber.stateNode;
+ var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
+ var updatePayload = hydrateInstance(
+ instance,
+ fiber.type,
+ fiber.memoizedProps,
+ hostContext,
+ fiber,
+ shouldWarnIfMismatchDev
+ ); // TODO: Type this specific to this type of component.
- if (isOutEvent) {
- var _related = nativeEvent.relatedTarget || nativeEvent.toElement;
+ 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.
- from = targetInst;
- to = _related ? getClosestInstanceFromNode(_related) : null;
+ if (updatePayload !== null) {
+ return true;
+ }
- if (to !== null) {
- var nearestMounted = getNearestMountedFiber(to);
- var tag = to.tag;
+ return false;
+}
- if (
- to !== nearestMounted ||
- (tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
- ) {
- to = null;
+function prepareToHydrateHostTextInstance(fiber) {
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
+ 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;
+ var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotMatchHydratedContainerTextInstance(
+ parentContainer,
+ textInstance,
+ textContent, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode,
+ shouldWarnIfMismatchDev
+ );
+ break;
+ }
+
+ case HostSingleton:
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
+
+ var _isConcurrentMode2 =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
+
+ didNotMatchHydratedTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ textInstance,
+ textContent, // TODO: Delete this argument when we remove the legacy root API.
+ _isConcurrentMode2,
+ shouldWarnIfMismatchDev
+ );
+ break;
+ }
}
}
- } else {
- // Moving to a node from outside the window.
- from = null;
- to = targetInst;
}
- if (from === to) {
- // Nothing pertains to our managed components.
- return;
- }
+ return shouldUpdate;
+}
- var SyntheticEventCtor = SyntheticMouseEvent;
- var leaveEventType = "onMouseLeave";
- var enterEventType = "onMouseEnter";
- var eventTypePrefix = "mouse";
+function prepareToHydrateHostSuspenseInstance(fiber) {
+ var suspenseState = fiber.memoizedState;
+ var suspenseInstance =
+ suspenseState !== null ? suspenseState.dehydrated : null;
- if (domEventName === "pointerout" || domEventName === "pointerover") {
- SyntheticEventCtor = SyntheticPointerEvent;
- leaveEventType = "onPointerLeave";
- enterEventType = "onPointerEnter";
- eventTypePrefix = "pointer";
+ if (!suspenseInstance) {
+ throw new Error(
+ "Expected to have a hydrated suspense instance. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
}
- var fromNode = from == null ? win : getNodeFromInstance(from);
- var toNode = to == null ? win : getNodeFromInstance(to);
- var leave = new SyntheticEventCtor(
- leaveEventType,
- eventTypePrefix + "leave",
- from,
- nativeEvent,
- nativeEventTarget
- );
- leave.target = fromNode;
- leave.relatedTarget = toNode;
- var enter = null; // We should only process this nativeEvent if we are processing
- // the first ancestor. Next time, we will ignore the event.
+ hydrateSuspenseInstance(suspenseInstance, fiber);
+}
- var nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget);
+function skipPastDehydratedSuspenseInstance(fiber) {
+ var suspenseState = fiber.memoizedState;
+ var suspenseInstance =
+ suspenseState !== null ? suspenseState.dehydrated : null;
- if (nativeTargetInst === targetInst) {
- var enterEvent = new SyntheticEventCtor(
- enterEventType,
- eventTypePrefix + "enter",
- to,
- nativeEvent,
- nativeEventTarget
+ if (!suspenseInstance) {
+ throw new Error(
+ "Expected to have a hydrated suspense instance. " +
+ "This error is likely caused by a bug in React. Please file an issue."
);
- enterEvent.target = toNode;
- enterEvent.relatedTarget = fromNode;
- enter = enterEvent;
}
- accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
+ return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
}
-/**
- * inlined Object.is polyfill to avoid requiring consumers ship their own
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- */
-function is(x, y) {
- return (
- (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
- );
-}
+function popToNextHostParent(fiber) {
+ hydrationParentFiber = fiber.return;
-var objectIs = typeof Object.is === "function" ? Object.is : is; // $FlowFixMe[method-unbinding]
+ while (hydrationParentFiber) {
+ switch (hydrationParentFiber.tag) {
+ case HostRoot:
+ case HostSingleton:
+ rootOrSingletonContext = true;
+ return;
-/**
- * Performs equality by iterating through keys on an object and returning false
- * when any key has values which are not strictly equal between the arguments.
- * Returns true when the values of all keys are strictly equal.
- */
+ case HostComponent:
+ case SuspenseComponent:
+ rootOrSingletonContext = false;
+ return;
-function shallowEqual(objA, objB) {
- if (objectIs(objA, objB)) {
- return true;
+ default:
+ hydrationParentFiber = hydrationParentFiber.return;
+ }
}
+}
- if (
- typeof objA !== "object" ||
- objA === null ||
- typeof objB !== "object" ||
- objB === null
- ) {
+function popHydrationState(fiber) {
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
return false;
}
- var keysA = Object.keys(objA);
- var keysB = Object.keys(objB);
-
- if (keysA.length !== keysB.length) {
+ 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;
- } // Test for A's keys different from B.
+ }
- for (var i = 0; i < keysA.length; i++) {
- var currentKey = keysA[i];
+ var shouldClear = false;
+ {
+ // With float we never clear the Root, or Singleton instances. We also do not clear Instances
+ // that have singleton text content
if (
- !hasOwnProperty.call(objB, currentKey) ||
- !objectIs(objA[currentKey], objB[currentKey])
+ fiber.tag !== HostRoot &&
+ fiber.tag !== HostSingleton &&
+ !(
+ fiber.tag === HostComponent &&
+ shouldSetTextContent(fiber.type, fiber.memoizedProps)
+ )
) {
- return false;
+ shouldClear = true;
}
}
+ if (shouldClear) {
+ var nextInstance = nextHydratableInstance;
+
+ if (nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnIfUnhydratedTailNodes(fiber);
+ throwOnHydrationMismatch();
+ } else {
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
+ }
+ }
+ }
+ }
+
+ popToNextHostParent(fiber);
+
+ if (fiber.tag === SuspenseComponent) {
+ nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
+ } else {
+ nextHydratableInstance = hydrationParentFiber
+ ? getNextHydratableSibling(fiber.stateNode)
+ : null;
+ }
+
return true;
}
-var skipSelectionChangeEvent =
- canUseDOM && "documentMode" in document && document.documentMode <= 11;
-
-function registerEvents() {
- registerTwoPhaseEvent("onSelect", [
- "focusout",
- "contextmenu",
- "dragend",
- "focusin",
- "keydown",
- "keyup",
- "mousedown",
- "mouseup",
- "selectionchange"
- ]);
+function hasUnhydratedTailNodes() {
+ return isHydrating && nextHydratableInstance !== null;
}
-var activeElement = null;
-var activeElementInst = null;
-var lastSelection = null;
-var mouseDown = false;
-/**
- * Get an object which is a unique representation of the current selection.
- *
- * The return value will not be consistent across nodes or browsers, but
- * two identical selections on the same node will return identical objects.
- */
+function warnIfUnhydratedTailNodes(fiber) {
+ var nextInstance = nextHydratableInstance;
-function getSelection(node) {
- if ("selectionStart" in node && hasSelectionCapabilities(node)) {
- return {
- start: node.selectionStart,
- end: node.selectionEnd
- };
- } else {
- var win = (node.ownerDocument && node.ownerDocument.defaultView) || window;
- var selection = win.getSelection();
- return {
- anchorNode: selection.anchorNode,
- anchorOffset: selection.anchorOffset,
- focusNode: selection.focusNode,
- focusOffset: selection.focusOffset
- };
+ while (nextInstance) {
+ warnUnhydratedInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
}
}
-/**
- * Get document associated with the event target.
- */
-function getEventTargetDocument(eventTarget) {
- return eventTarget.window === eventTarget
- ? eventTarget.document
- : eventTarget.nodeType === DOCUMENT_NODE
- ? eventTarget
- : eventTarget.ownerDocument;
+function resetHydrationState() {
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+ didSuspendOrErrorDEV = false;
}
-/**
- * Poll selection to see whether it's changed.
- *
- * @param {object} nativeEvent
- * @param {object} nativeEventTarget
- * @return {?SyntheticEvent}
- */
-function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
- // Ensure we have the right element, and that the user is not dragging a
- // selection (this matches native `select` event behavior). In HTML5, select
- // fires only on input and textarea thus if there's no focused element we
- // won't dispatch.
- var doc = getEventTargetDocument(nativeEventTarget);
+function upgradeHydrationErrorsToRecoverable() {
+ if (hydrationErrors !== null) {
+ // Successfully completed a forced client render. The errors that occurred
+ // during the hydration attempt are now recovered. We will log them in
+ // commit phase, once the entire tree has finished.
+ queueRecoverableErrors(hydrationErrors);
+ hydrationErrors = null;
+ }
+}
- if (
- mouseDown ||
- activeElement == null ||
- activeElement !== getActiveElement(doc)
- ) {
- return;
- } // Only fire when selection has actually changed.
+function getIsHydrating() {
+ return isHydrating;
+}
- var currentSelection = getSelection(activeElement);
+function queueHydrationError(error) {
+ if (hydrationErrors === null) {
+ hydrationErrors = [error];
+ } else {
+ hydrationErrors.push(error);
+ }
+}
- if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
- lastSelection = currentSelection;
- var listeners = accumulateTwoPhaseListeners(activeElementInst, "onSelect");
+// we wait until the current render is over (either finished or interrupted)
+// before adding it to the fiber/hook queue. Push to this array so we can
+// access the queue, fiber, update, et al later.
- if (listeners.length > 0) {
- var event = new SyntheticEvent(
- "onSelect",
- "select",
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- event.target = activeElement;
- }
- }
-}
-/**
- * This plugin creates an `onSelect` event that normalizes select events
- * across form elements.
- *
- * Supported elements are:
- * - input (see `isTextInputElement`)
- * - textarea
- * - contentEditable
- *
- * This differs from native browser implementations in the following ways:
- * - Fires on contentEditable fields as well as inputs.
- * - Fires for collapsed selection.
- * - Fires after user input.
- */
+var concurrentQueues = [];
+var concurrentQueuesIndex = 0;
+var concurrentlyUpdatedLanes = NoLanes;
+function finishQueueingConcurrentUpdates() {
+ var endIndex = concurrentQueuesIndex;
+ concurrentQueuesIndex = 0;
+ concurrentlyUpdatedLanes = NoLanes;
+ var i = 0;
-function extractEvents$2(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
+ while (i < endIndex) {
+ var fiber = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var queue = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var update = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var lane = concurrentQueues[i];
+ concurrentQueues[i++] = null;
- switch (domEventName) {
- // Track the input node that has focus.
- case "focusin":
- if (
- isTextInputElement(targetNode) ||
- targetNode.contentEditable === "true"
- ) {
- activeElement = targetNode;
- activeElementInst = targetInst;
- lastSelection = null;
+ if (queue !== null && update !== null) {
+ var pending = queue.pending;
+
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
}
- break;
+ queue.pending = update;
+ }
- case "focusout":
- activeElement = null;
- activeElementInst = null;
- lastSelection = null;
- break;
- // Don't fire the event while the user is dragging. This matches the
- // semantics of the native select event.
+ if (lane !== NoLane) {
+ markUpdateLaneFromFiberToRoot(fiber, update, lane);
+ }
+ }
+}
+function getConcurrentlyUpdatedLanes() {
+ return concurrentlyUpdatedLanes;
+}
- case "mousedown":
- mouseDown = true;
- break;
+function enqueueUpdate$1(fiber, queue, update, lane) {
+ // Don't update the `childLanes` on the return path yet. If we already in
+ // the middle of rendering, wait until after it has completed.
+ concurrentQueues[concurrentQueuesIndex++] = fiber;
+ concurrentQueues[concurrentQueuesIndex++] = queue;
+ concurrentQueues[concurrentQueuesIndex++] = update;
+ concurrentQueues[concurrentQueuesIndex++] = lane;
+ concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane); // The fiber's `lane` field is used in some places to check if any work is
+ // scheduled, to perform an eager bailout, so we need to update it immediately.
+ // TODO: We should probably move this to the "shared" queue instead.
- case "contextmenu":
- case "mouseup":
- case "dragend":
- mouseDown = false;
- constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
- break;
- // Chrome and IE fire non-standard event when selection is changed (and
- // sometimes when it hasn't). IE's event fires out of order with respect
- // to key and input events on deletion, so we discard it.
- //
- // Firefox doesn't support selectionchange, so check selection status
- // after each key entry. The selection changes after keydown and before
- // keyup, but we check on keydown as well in the case of holding down a
- // key, when multiple keydown events are fired but only one keyup is.
- // This is also our approach for IE handling, for the reason above.
+ fiber.lanes = mergeLanes(fiber.lanes, lane);
+ var alternate = fiber.alternate;
- case "selectionchange":
- if (skipSelectionChangeEvent) {
- break;
- }
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, lane);
+ }
+}
- // falls through
+function enqueueConcurrentHookUpdate(fiber, queue, update, lane) {
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
+ return getRootForUpdatedFiber(fiber);
+}
+function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update) {
+ // This function is used to queue an update that doesn't need a rerender. The
+ // only reason we queue it is in case there's a subsequent higher priority
+ // update that causes it to be rebased.
+ var lane = NoLane;
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane); // Usually we can rely on the upcoming render phase to process the concurrent
+ // queue. However, since this is a bail out, we're not scheduling any work
+ // here. So the update we just queued will leak until something else happens
+ // to schedule work (if ever).
+ //
+ // Check if we're currently in the middle of rendering a tree, and if not,
+ // process the queue immediately to prevent a leak.
- case "keydown":
- case "keyup":
- constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
+ var isConcurrentlyRendering = getWorkInProgressRoot() !== null;
+
+ if (!isConcurrentlyRendering) {
+ finishQueueingConcurrentUpdates();
}
}
+function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
+ return getRootForUpdatedFiber(fiber);
+}
+function enqueueConcurrentRenderForLane(fiber, lane) {
+ enqueueUpdate$1(fiber, null, null, lane);
+ return getRootForUpdatedFiber(fiber);
+} // Calling this function outside this module should only be done for backwards
+// compatibility and should always be accompanied by a warning.
-/**
- * Generate a mapping of standard vendor prefixes using the defined style property and event name.
- *
- * @param {string} styleProp
- * @param {string} eventName
- * @returns {object}
- */
-
-function makePrefixMap(styleProp, eventName) {
- var prefixes = {};
- prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
- prefixes["Webkit" + styleProp] = "webkit" + eventName;
- prefixes["Moz" + styleProp] = "moz" + eventName;
- return prefixes;
+function unsafe_markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
+ // NOTE: For Hyrum's Law reasons, if an infinite update loop is detected, it
+ // should throw before `markUpdateLaneFromFiberToRoot` is called. But this is
+ // undefined behavior and we can change it if we need to; it just so happens
+ // that, at the time of this writing, there's an internal product test that
+ // happens to rely on this.
+ var root = getRootForUpdatedFiber(sourceFiber);
+ markUpdateLaneFromFiberToRoot(sourceFiber, null, lane);
+ return root;
}
-/**
- * A list of event names to a configurable list of vendor prefixes.
- */
-var vendorPrefixes = {
- animationend: makePrefixMap("Animation", "AnimationEnd"),
- animationiteration: makePrefixMap("Animation", "AnimationIteration"),
- animationstart: makePrefixMap("Animation", "AnimationStart"),
- transitionend: makePrefixMap("Transition", "TransitionEnd")
-};
-/**
- * Event names that have already been detected and prefixed (if applicable).
- */
+function markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {
+ // Update the source fiber's lanes
+ sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
+ var alternate = sourceFiber.alternate;
-var prefixedEventNames = {};
-/**
- * Element to check for prefixes on.
- */
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, lane);
+ } // Walk the parent path to the root and update the child lanes.
-var style = {};
-/**
- * Bootstrap if a DOM exists.
- */
+ var isHidden = false;
+ var parent = sourceFiber.return;
+ var node = sourceFiber;
-if (canUseDOM) {
- style = document.createElement("div").style; // On some platforms, in particular some releases of Android 4.x,
- // the un-prefixed "animation" and "transition" properties are defined on the
- // style object but the events that fire will still be prefixed, so we need
- // to check if the un-prefixed events are usable, and if not remove them from the map.
+ while (parent !== null) {
+ parent.childLanes = mergeLanes(parent.childLanes, lane);
+ alternate = parent.alternate;
- if (!("AnimationEvent" in window)) {
- delete vendorPrefixes.animationend.animation;
- delete vendorPrefixes.animationiteration.animation;
- delete vendorPrefixes.animationstart.animation;
- } // Same as above
+ if (alternate !== null) {
+ alternate.childLanes = mergeLanes(alternate.childLanes, lane);
+ }
- if (!("TransitionEvent" in window)) {
- delete vendorPrefixes.transitionend.transition;
+ if (parent.tag === OffscreenComponent) {
+ // Check if this offscreen boundary is currently hidden.
+ //
+ // The instance may be null if the Offscreen parent was unmounted. Usually
+ // the parent wouldn't be reachable in that case because we disconnect
+ // fibers from the tree when they are deleted. However, there's a weird
+ // edge case where setState is called on a fiber that was interrupted
+ // before it ever mounted. Because it never mounts, it also never gets
+ // deleted. Because it never gets deleted, its return pointer never gets
+ // disconnected. Which means it may be attached to a deleted Offscreen
+ // parent node. (This discovery suggests it may be better for memory usage
+ // if we don't attach the `return` pointer until the commit phase, though
+ // in order to do that we'd need some other way to track the return
+ // pointer during the initial render, like on the stack.)
+ //
+ // This case is always accompanied by a warning, but we still need to
+ // account for it. (There may be other cases that we haven't discovered,
+ // too.)
+ var offscreenInstance = parent.stateNode;
+
+ if (
+ offscreenInstance !== null &&
+ !(offscreenInstance._visibility & OffscreenVisible)
+ ) {
+ isHidden = true;
+ }
+ }
+
+ node = parent;
+ parent = parent.return;
}
-}
-/**
- * Attempts to determine the correct vendor prefixed event name.
- *
- * @param {string} eventName
- * @returns {string}
- */
-function getVendorPrefixedEventName(eventName) {
- if (prefixedEventNames[eventName]) {
- return prefixedEventNames[eventName];
- } else if (!vendorPrefixes[eventName]) {
- return eventName;
+ if (isHidden && update !== null && node.tag === HostRoot) {
+ var root = node.stateNode;
+ markHiddenUpdate(root, update, lane);
}
+}
- var prefixMap = vendorPrefixes[eventName];
+function getRootForUpdatedFiber(sourceFiber) {
+ // TODO: We will detect and infinite update loop and throw even if this fiber
+ // has already unmounted. This isn't really necessary but it happens to be the
+ // current behavior we've used for several release cycles. Consider not
+ // performing this check if the updated fiber already unmounted, since it's
+ // not possible for that to cause an infinite update loop.
+ throwIfInfiniteUpdateLoopDetected(); // When a setState happens, we must ensure the root is scheduled. Because
+ // update queues do not have a backpointer to the root, the only way to do
+ // this currently is to walk up the return path. This used to not be a big
+ // deal because we would have to walk up the return path to set
+ // the `childLanes`, anyway, but now those two traversals happen at
+ // different times.
+ // TODO: Consider adding a `root` backpointer on the update queue.
- for (var styleProp in prefixMap) {
- if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
- return (prefixedEventNames[eventName] = prefixMap[styleProp]);
- }
+ detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
+ var node = sourceFiber;
+ var parent = node.return;
+
+ while (parent !== null) {
+ detectUpdateOnUnmountedFiber(sourceFiber, node);
+ node = parent;
+ parent = node.return;
}
- return eventName;
+ return node.tag === HostRoot ? node.stateNode : null;
}
-var ANIMATION_END = getVendorPrefixedEventName("animationend");
-var ANIMATION_ITERATION = getVendorPrefixedEventName("animationiteration");
-var ANIMATION_START = getVendorPrefixedEventName("animationstart");
-var TRANSITION_END = getVendorPrefixedEventName("transitionend");
+function detectUpdateOnUnmountedFiber(sourceFiber, parent) {
+ {
+ var alternate = parent.alternate;
-var topLevelEventsToReactNames = new Map(); // NOTE: Capitalization is important in this list!
-//
-// E.g. it needs "pointerDown", not "pointerdown".
-// This is because we derive both React name ("onPointerDown")
-// and DOM name ("pointerdown") from the same list.
-//
-// Exceptions that don't match this convention are listed separately.
-//
-// prettier-ignore
+ if (
+ alternate === null &&
+ (parent.flags & (Placement | Hydrating)) !== NoFlags$1
+ ) {
+ warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
+ }
+ }
+}
-var simpleEventPluginEvents = ['abort', 'auxClick', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'gotPointerCapture', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'lostPointerCapture', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'pointerCancel', 'pointerDown', 'pointerMove', 'pointerOut', 'pointerOver', 'pointerUp', 'progress', 'rateChange', 'reset', 'resize', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'touchCancel', 'touchEnd', 'touchStart', 'volumeChange', 'scroll', 'toggle', 'touchMove', 'waiting', 'wheel'];
+var UpdateState = 0;
+var ReplaceState = 1;
+var ForceUpdate = 2;
+var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`.
+// It should only be read right after calling `processUpdateQueue`, via
+// `checkHasForceUpdateAfterProcessing`.
+
+var hasForceUpdate = false;
+var didWarnUpdateInsideUpdate;
+var currentlyProcessingQueue;
{
- // Special case: these two events don't have on* React handler
- // and are only accessible via the createEventHandle API.
- topLevelEventsToReactNames.set("beforeblur", null);
- topLevelEventsToReactNames.set("afterblur", null);
+ didWarnUpdateInsideUpdate = false;
+ currentlyProcessingQueue = null;
}
-function registerSimpleEvent(domEventName, reactName) {
- topLevelEventsToReactNames.set(domEventName, reactName);
- registerTwoPhaseEvent(reactName, [domEventName]);
+function initializeUpdateQueue(fiber) {
+ var queue = {
+ baseState: fiber.memoizedState,
+ firstBaseUpdate: null,
+ lastBaseUpdate: null,
+ shared: {
+ pending: null,
+ lanes: NoLanes,
+ hiddenCallbacks: null
+ },
+ callbacks: null
+ };
+ fiber.updateQueue = queue;
}
+function cloneUpdateQueue(current, workInProgress) {
+ // Clone the update queue from current. Unless it's already a clone.
+ var queue = workInProgress.updateQueue;
+ var currentQueue = current.updateQueue;
-function registerSimpleEvents() {
- for (var i = 0; i < simpleEventPluginEvents.length; i++) {
- var eventName = simpleEventPluginEvents[i];
- var domEventName = eventName.toLowerCase();
- var capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
- registerSimpleEvent(domEventName, "on" + capitalizedEvent);
- } // Special cases where event names don't match.
-
- registerSimpleEvent(ANIMATION_END, "onAnimationEnd");
- registerSimpleEvent(ANIMATION_ITERATION, "onAnimationIteration");
- registerSimpleEvent(ANIMATION_START, "onAnimationStart");
- registerSimpleEvent("dblclick", "onDoubleClick");
- registerSimpleEvent("focusin", "onFocus");
- registerSimpleEvent("focusout", "onBlur");
- registerSimpleEvent(TRANSITION_END, "onTransitionEnd");
+ if (queue === currentQueue) {
+ var clone = {
+ baseState: currentQueue.baseState,
+ firstBaseUpdate: currentQueue.firstBaseUpdate,
+ lastBaseUpdate: currentQueue.lastBaseUpdate,
+ shared: currentQueue.shared,
+ callbacks: null
+ };
+ workInProgress.updateQueue = clone;
+ }
}
+function createUpdate(lane) {
+ var update = {
+ lane: lane,
+ tag: UpdateState,
+ payload: null,
+ callback: null,
+ next: null
+ };
+ return update;
+}
+function enqueueUpdate(fiber, update, lane) {
+ var updateQueue = fiber.updateQueue;
-function extractEvents$1(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var reactName = topLevelEventsToReactNames.get(domEventName);
-
- if (reactName === undefined) {
- return;
+ if (updateQueue === null) {
+ // Only occurs if the fiber has been unmounted.
+ return null;
}
- var SyntheticEventCtor = SyntheticEvent;
- var reactEventType = domEventName;
+ var sharedQueue = updateQueue.shared;
- switch (domEventName) {
- case "keypress":
- // Firefox creates a keypress event for function keys too. This removes
- // the unwanted keypress events. Enter is however both printable and
- // non-printable. One would expect Tab to be as well (but it isn't).
- if (getEventCharCode(nativeEvent) === 0) {
- return;
- }
+ {
+ if (
+ currentlyProcessingQueue === sharedQueue &&
+ !didWarnUpdateInsideUpdate
+ ) {
+ var componentName = getComponentNameFromFiber(fiber);
- /* falls through */
+ error(
+ "An update (setState, replaceState, or forceUpdate) was scheduled " +
+ "from inside an update function. Update functions should be pure, " +
+ "with zero side-effects. Consider using componentDidUpdate or a " +
+ "callback.\n\nPlease update the following component: %s",
+ componentName
+ );
- case "keydown":
- case "keyup":
- SyntheticEventCtor = SyntheticKeyboardEvent;
- break;
+ didWarnUpdateInsideUpdate = true;
+ }
+ }
- case "focusin":
- reactEventType = "focus";
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ if (isUnsafeClassRenderPhaseUpdate(fiber)) {
+ // This is an unsafe render phase update. Add directly to the update
+ // queue so we can process it immediately during the current render.
+ var pending = sharedQueue.pending;
- case "focusout":
- reactEventType = "blur";
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
+ }
- case "beforeblur":
- case "afterblur":
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering
+ // this fiber. This is for backwards compatibility in the case where you
+ // update a different component during render phase than the one that is
+ // currently renderings (a pattern that is accompanied by a warning).
- case "click":
- // Firefox creates a click event on right mouse clicks. This removes the
- // unwanted click events.
- if (nativeEvent.button === 2) {
- return;
- }
+ return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
+ } else {
+ return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
+ }
+}
+function entangleTransitions(root, fiber, lane) {
+ var updateQueue = fiber.updateQueue;
- /* falls through */
+ if (updateQueue === null) {
+ // Only occurs if the fiber has been unmounted.
+ return;
+ }
- case "auxclick":
- case "dblclick":
- case "mousedown":
- case "mousemove":
- case "mouseup": // TODO: Disabled elements should not respond to mouse events
+ var sharedQueue = updateQueue.shared;
- /* falls through */
+ if (isTransitionLane(lane)) {
+ var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must
+ // have finished. We can remove them from the shared queue, which represents
+ // a superset of the actually pending lanes. In some cases we may entangle
+ // more than we need to, but that's OK. In fact it's worse if we *don't*
+ // entangle when we should.
- case "mouseout":
- case "mouseover":
- case "contextmenu":
- SyntheticEventCtor = SyntheticMouseEvent;
- break;
+ queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
- case "drag":
- case "dragend":
- case "dragenter":
- case "dragexit":
- case "dragleave":
- case "dragover":
- case "dragstart":
- case "drop":
- SyntheticEventCtor = SyntheticDragEvent;
- break;
+ var newQueueLanes = mergeLanes(queueLanes, lane);
+ sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
+ // the lane finished since the last time we entangled it. So we need to
+ // entangle it again, just to be sure.
- case "touchcancel":
- case "touchend":
- case "touchmove":
- case "touchstart":
- SyntheticEventCtor = SyntheticTouchEvent;
- break;
+ markRootEntangled(root, newQueueLanes);
+ }
+}
+function enqueueCapturedUpdate(workInProgress, capturedUpdate) {
+ // Captured updates are updates that are thrown by a child during the render
+ // phase. They should be discarded if the render is aborted. Therefore,
+ // we should only put them on the work-in-progress queue, not the current one.
+ var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone.
- case ANIMATION_END:
- case ANIMATION_ITERATION:
- case ANIMATION_START:
- SyntheticEventCtor = SyntheticAnimationEvent;
- break;
+ var current = workInProgress.alternate;
- case TRANSITION_END:
- SyntheticEventCtor = SyntheticTransitionEvent;
- break;
+ if (current !== null) {
+ var currentQueue = current.updateQueue;
- case "scroll":
- SyntheticEventCtor = SyntheticUIEvent;
- break;
+ if (queue === currentQueue) {
+ // The work-in-progress queue is the same as current. This happens when
+ // we bail out on a parent fiber that then captures an error thrown by
+ // a child. Since we want to append the update only to the work-in
+ // -progress queue, we need to clone the updates. We usually clone during
+ // processUpdateQueue, but that didn't happen in this case because we
+ // skipped over the parent when we bailed out.
+ var newFirst = null;
+ var newLast = null;
+ var firstBaseUpdate = queue.firstBaseUpdate;
- case "wheel":
- SyntheticEventCtor = SyntheticWheelEvent;
- break;
+ if (firstBaseUpdate !== null) {
+ // Loop through the updates and clone them.
+ var update = firstBaseUpdate;
- case "copy":
- case "cut":
- case "paste":
- SyntheticEventCtor = SyntheticClipboardEvent;
- break;
+ do {
+ var clone = {
+ lane: update.lane,
+ tag: update.tag,
+ payload: update.payload,
+ // When this update is rebased, we should not fire its
+ // callback again.
+ callback: null,
+ next: null
+ };
- case "gotpointercapture":
- case "lostpointercapture":
- case "pointercancel":
- case "pointerdown":
- case "pointermove":
- case "pointerout":
- case "pointerover":
- case "pointerup":
- SyntheticEventCtor = SyntheticPointerEvent;
- break;
- }
+ if (newLast === null) {
+ newFirst = newLast = clone;
+ } else {
+ newLast.next = clone;
+ newLast = clone;
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
- var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+ update = update.next;
+ } while (update !== null); // Append the captured update the end of the cloned list.
- if (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) {
- var listeners = accumulateEventHandleNonManagedNodeListeners(
- // TODO: this cast may not make sense for events like
- // "focus" where React listens to e.g. "focusin".
- reactEventType,
- targetContainer,
- inCapturePhase
- );
+ if (newLast === null) {
+ newFirst = newLast = capturedUpdate;
+ } else {
+ newLast.next = capturedUpdate;
+ newLast = capturedUpdate;
+ }
+ } else {
+ // There are no base updates.
+ newFirst = newLast = capturedUpdate;
+ }
- if (listeners.length > 0) {
- // Intentionally create event lazily.
- var event = new SyntheticEventCtor(
- reactName,
- reactEventType,
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
+ queue = {
+ baseState: currentQueue.baseState,
+ firstBaseUpdate: newFirst,
+ lastBaseUpdate: newLast,
+ shared: currentQueue.shared,
+ callbacks: currentQueue.callbacks
+ };
+ workInProgress.updateQueue = queue;
+ return;
}
- } else {
- // Some events don't bubble in the browser.
- // In the past, React has always bubbled them, but this can be surprising.
- // We're going to try aligning closer to the browser behavior by not bubbling
- // them in React either. We'll start by not bubbling onScroll, and then expand.
- var accumulateTargetOnly =
- !inCapturePhase && // TODO: ideally, we'd eventually add all events from
- // nonDelegatedEvents list in DOMPluginEventSystem.
- // Then we can remove this special list.
- // This is a breaking change that can wait until React 18.
- domEventName === "scroll";
-
- var _listeners = accumulateSinglePhaseListeners(
- targetInst,
- reactName,
- nativeEvent.type,
- inCapturePhase,
- accumulateTargetOnly,
- nativeEvent
- );
+ } // Append the update to the end of the list.
- if (_listeners.length > 0) {
- // Intentionally create event lazily.
- var _event = new SyntheticEventCtor(
- reactName,
- reactEventType,
- null,
- nativeEvent,
- nativeEventTarget
- );
+ var lastBaseUpdate = queue.lastBaseUpdate;
- dispatchQueue.push({
- event: _event,
- listeners: _listeners
- });
- }
+ if (lastBaseUpdate === null) {
+ queue.firstBaseUpdate = capturedUpdate;
+ } else {
+ lastBaseUpdate.next = capturedUpdate;
}
-}
-registerSimpleEvents();
-registerEvents$1();
-registerEvents$2();
-registerEvents();
-registerEvents$3();
+ queue.lastBaseUpdate = capturedUpdate;
+}
-function extractEvents(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
+function getStateFromUpdate(
+ workInProgress,
+ queue,
+ update,
+ prevState,
+ nextProps,
+ instance
) {
- // TODO: we should remove the concept of a "SimpleEventPlugin".
- // This is the basic functionality of the event system. All
- // the other plugins are essentially polyfills. So the plugin
- // should probably be inlined somewhere and have its logic
- // be core the to event system. This would potentially allow
- // us to ship builds of React without the polyfilled plugins below.
- extractEvents$1(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
- );
- var shouldProcessPolyfillPlugins =
- (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the
- // event's native "bubble" phase, which means that we're
- // not in the capture phase. That's because we emulate
- // the capture phase here still. This is a trade-off,
- // because in an ideal world we would not emulate and use
- // the phases properly, like we do with the SimpleEvent
- // plugin. However, the plugins below either expect
- // emulation (EnterLeave) or use state localized to that
- // plugin (BeforeInput, Change, Select). The state in
- // these modules complicates things, as you'll essentially
- // get the case where the capture phase event might change
- // state, only for the following bubble event to come in
- // later and not trigger anything as the state now
- // invalidates the heuristics of the event plugin. We
- // could alter all these plugins to work in such ways, but
- // that might cause other unknown side-effects that we
- // can't foresee right now.
+ switch (update.tag) {
+ case ReplaceState: {
+ var payload = update.payload;
- if (shouldProcessPolyfillPlugins) {
- extractEvents$3(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$4(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$2(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$5(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- }
-} // List of events that need to be individually attached to media elements.
+ if (typeof payload === "function") {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ }
-var mediaEventTypes = [
- "abort",
- "canplay",
- "canplaythrough",
- "durationchange",
- "emptied",
- "encrypted",
- "ended",
- "error",
- "loadeddata",
- "loadedmetadata",
- "loadstart",
- "pause",
- "play",
- "playing",
- "progress",
- "ratechange",
- "resize",
- "seeked",
- "seeking",
- "stalled",
- "suspend",
- "timeupdate",
- "volumechange",
- "waiting"
-]; // We should not delegate these events to the container, but rather
-// set them on the actual target element itself. This is primarily
-// because these events do not consistently bubble in the DOM.
+ var nextState = payload.call(instance, prevState, nextProps);
-var nonDelegatedEvents = new Set(
- ["cancel", "close", "invalid", "load", "scroll", "toggle"].concat(
- mediaEventTypes
- )
-);
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
-function executeDispatch(event, listener, currentTarget) {
- var type = event.type || "unknown-event";
- event.currentTarget = currentTarget;
- invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
- event.currentTarget = null;
-}
+ try {
+ payload.call(instance, prevState, nextProps);
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
+ }
-function processDispatchQueueItemsInOrder(
- event,
- dispatchListeners,
- inCapturePhase
-) {
- var previousInstance;
+ exitDisallowedContextReadInDEV();
+ }
- if (inCapturePhase) {
- for (var i = dispatchListeners.length - 1; i >= 0; i--) {
- var _dispatchListeners$i = dispatchListeners[i],
- instance = _dispatchListeners$i.instance,
- currentTarget = _dispatchListeners$i.currentTarget,
- listener = _dispatchListeners$i.listener;
+ return nextState;
+ } // State object
- if (instance !== previousInstance && event.isPropagationStopped()) {
- return;
- }
+ return payload;
+ }
- executeDispatch(event, listener, currentTarget);
- previousInstance = instance;
+ case CaptureUpdate: {
+ workInProgress.flags =
+ (workInProgress.flags & ~ShouldCapture) | DidCapture;
}
- } else {
- for (var _i = 0; _i < dispatchListeners.length; _i++) {
- var _dispatchListeners$_i = dispatchListeners[_i],
- _instance = _dispatchListeners$_i.instance,
- _currentTarget = _dispatchListeners$_i.currentTarget,
- _listener = _dispatchListeners$_i.listener;
+ // Intentional fallthrough
- if (_instance !== previousInstance && event.isPropagationStopped()) {
- return;
- }
+ case UpdateState: {
+ var _payload = update.payload;
+ var partialState;
- executeDispatch(event, _listener, _currentTarget);
- previousInstance = _instance;
- }
- }
-}
+ if (typeof _payload === "function") {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ }
-function processDispatchQueue(dispatchQueue, eventSystemFlags) {
- var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+ partialState = _payload.call(instance, prevState, nextProps);
- for (var i = 0; i < dispatchQueue.length; i++) {
- var _dispatchQueue$i = dispatchQueue[i],
- event = _dispatchQueue$i.event,
- listeners = _dispatchQueue$i.listeners;
- processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); // event system doesn't use pooling.
- } // This would be a good time to rethrow if any of the event handlers threw.
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
- rethrowCaughtError();
-}
+ try {
+ _payload.call(instance, prevState, nextProps);
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
+ }
-function dispatchEventsForPlugins(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- targetInst,
- targetContainer
-) {
- var nativeEventTarget = getEventTarget(nativeEvent);
- var dispatchQueue = [];
- extractEvents(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
- );
- processDispatchQueue(dispatchQueue, eventSystemFlags);
-}
+ exitDisallowedContextReadInDEV();
+ }
+ } else {
+ // Partial state object
+ partialState = _payload;
+ }
-function listenToNonDelegatedEvent(domEventName, targetElement) {
- {
- if (!nonDelegatedEvents.has(domEventName)) {
- error(
- 'Did not expect a listenToNonDelegatedEvent() call for "%s". ' +
- "This is a bug in React. Please file an issue.",
- domEventName
- );
- }
- }
+ if (partialState === null || partialState === undefined) {
+ // Null and undefined are treated as no-ops.
+ return prevState;
+ } // Merge the partial state and the previous state.
- var isCapturePhaseListener = false;
- var listenerSet = getEventListenerSet(targetElement);
- var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
+ return assign({}, prevState, partialState);
+ }
- if (!listenerSet.has(listenerSetKey)) {
- addTrappedEventListener(
- targetElement,
- domEventName,
- IS_NON_DELEGATED,
- isCapturePhaseListener
- );
- listenerSet.add(listenerSetKey);
- }
-}
-function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
- {
- if (nonDelegatedEvents.has(domEventName) && !isCapturePhaseListener) {
- error(
- 'Did not expect a listenToNativeEvent() call for "%s" in the bubble phase. ' +
- "This is a bug in React. Please file an issue.",
- domEventName
- );
+ case ForceUpdate: {
+ hasForceUpdate = true;
+ return prevState;
}
}
- var eventSystemFlags = 0;
+ return prevState;
+}
- if (isCapturePhaseListener) {
- eventSystemFlags |= IS_CAPTURE_PHASE;
+function processUpdateQueue(workInProgress, props, instance, renderLanes) {
+ // This is always non-null on a ClassComponent or HostRoot
+ var queue = workInProgress.updateQueue;
+ hasForceUpdate = false;
+
+ {
+ currentlyProcessingQueue = queue.shared;
}
- addTrappedEventListener(
- target,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener
- );
-} // This is only used by createEventHandle when the
-// target is not a DOM element. E.g. window.
+ var firstBaseUpdate = queue.firstBaseUpdate;
+ var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue.
-function listenToNativeEventForNonManagedEventTarget(
- domEventName,
- isCapturePhaseListener,
- target
-) {
- var eventSystemFlags = IS_EVENT_HANDLE_NON_MANAGED_NODE;
- var listenerSet = getEventListenerSet(target);
- var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
+ var pendingQueue = queue.shared.pending;
- if (!listenerSet.has(listenerSetKey)) {
- if (isCapturePhaseListener) {
- eventSystemFlags |= IS_CAPTURE_PHASE;
- }
+ if (pendingQueue !== null) {
+ queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first
+ // and last so that it's non-circular.
- addTrappedEventListener(
- target,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener
- );
- listenerSet.add(listenerSetKey);
- }
-}
-var listeningMarker = "_reactListening" + Math.random().toString(36).slice(2);
-function listenToAllSupportedEvents(rootContainerElement) {
- if (!rootContainerElement[listeningMarker]) {
- rootContainerElement[listeningMarker] = true;
- allNativeEvents.forEach(function (domEventName) {
- // We handle selectionchange separately because it
- // doesn't bubble and needs to be on the document.
- if (domEventName !== "selectionchange") {
- if (!nonDelegatedEvents.has(domEventName)) {
- listenToNativeEvent(domEventName, false, rootContainerElement);
- }
-
- listenToNativeEvent(domEventName, true, rootContainerElement);
- }
- });
- var ownerDocument =
- rootContainerElement.nodeType === DOCUMENT_NODE
- ? rootContainerElement
- : rootContainerElement.ownerDocument;
+ var lastPendingUpdate = pendingQueue;
+ var firstPendingUpdate = lastPendingUpdate.next;
+ lastPendingUpdate.next = null; // Append pending updates to base queue
- if (ownerDocument !== null) {
- // The selectionchange event also needs deduplication
- // but it is attached to the document.
- if (!ownerDocument[listeningMarker]) {
- ownerDocument[listeningMarker] = true;
- listenToNativeEvent("selectionchange", false, ownerDocument);
- }
+ if (lastBaseUpdate === null) {
+ firstBaseUpdate = firstPendingUpdate;
+ } else {
+ lastBaseUpdate.next = firstPendingUpdate;
}
- }
-}
-function addTrappedEventListener(
- targetContainer,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener,
- isDeferredListenerForLegacyFBSupport
-) {
- var listener = createEventListenerWrapperWithPriority(
- targetContainer,
- domEventName,
- eventSystemFlags
- ); // If passive option is not supported, then the event will be
- // active and not passive.
+ lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then
+ // we need to transfer the updates to that queue, too. Because the base
+ // queue is a singly-linked list with no cycles, we can append to both
+ // lists and take advantage of structural sharing.
+ // TODO: Pass `current` as argument
- var isPassiveListener = undefined;
+ var current = workInProgress.alternate;
- if (passiveBrowserEventsSupported) {
- // Browsers introduced an intervention, making these events
- // passive by default on document. React doesn't bind them
- // to document anymore, but changing this now would undo
- // the performance wins from the change. So we emulate
- // the existing behavior manually on the roots now.
- // https://github.com/facebook/react/issues/19651
- if (
- domEventName === "touchstart" ||
- domEventName === "touchmove" ||
- domEventName === "wheel"
- ) {
- isPassiveListener = true;
- }
- }
+ if (current !== null) {
+ // This is always non-null on a ClassComponent or HostRoot
+ var currentQueue = current.updateQueue;
+ var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
- targetContainer =
- enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
- ? targetContainer.ownerDocument
- : targetContainer;
- var unsubscribeListener; // When legacyFBSupport is enabled, it's for when we
- // want to add a one time event listener to a container.
- // This should only be used with enableLegacyFBSupport
- // due to requirement to provide compatibility with
- // internal FB www event tooling. This works by removing
- // the event listener as soon as it is invoked. We could
- // also attempt to use the {once: true} param on
- // addEventListener, but that requires support and some
- // browsers do not support this today, and given this is
- // to support legacy code patterns, it's likely they'll
- // need support for such browsers.
+ if (currentLastBaseUpdate !== lastBaseUpdate) {
+ if (currentLastBaseUpdate === null) {
+ currentQueue.firstBaseUpdate = firstPendingUpdate;
+ } else {
+ currentLastBaseUpdate.next = firstPendingUpdate;
+ }
- if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
- var originalListener = listener; // $FlowFixMe[missing-this-annot]
- // $FlowFixMe[definition-cycle]
+ currentQueue.lastBaseUpdate = lastPendingUpdate;
+ }
+ }
+ } // These values may change as we process the queue.
- listener = function () {
- removeEventListener(targetContainer, domEventName, unsubscribeListener);
+ if (firstBaseUpdate !== null) {
+ // Iterate through the list of updates to compute the result.
+ var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
+ // from the original lanes.
- for (
- var _len = arguments.length, p = new Array(_len), _key = 0;
- _key < _len;
- _key++
- ) {
- p[_key] = arguments[_key];
- }
+ var newLanes = NoLanes;
+ var newBaseState = null;
+ var newFirstBaseUpdate = null;
+ var newLastBaseUpdate = null;
+ var update = firstBaseUpdate;
- return originalListener.apply(this, p);
- };
- } // TODO: There are too many combinations here. Consolidate them.
+ do {
+ // An extra OffscreenLane bit is added to updates that were made to
+ // a hidden tree, so that we can distinguish them from updates that were
+ // already there when the tree was hidden.
+ var updateLane = removeLanes(update.lane, OffscreenLane);
+ var isHiddenUpdate = updateLane !== update.lane; // Check if this update was made while the tree was hidden. If so, then
+ // it's not a "base" update and we should disregard the extra base lanes
+ // that were added to renderLanes when we entered the Offscreen tree.
- if (isCapturePhaseListener) {
- if (isPassiveListener !== undefined) {
- unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
- targetContainer,
- domEventName,
- listener,
- isPassiveListener
- );
- } else {
- unsubscribeListener = addEventCaptureListener(
- targetContainer,
- domEventName,
- listener
- );
- }
- } else {
- if (isPassiveListener !== undefined) {
- unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
- targetContainer,
- domEventName,
- listener,
- isPassiveListener
- );
- } else {
- unsubscribeListener = addEventBubbleListener(
- targetContainer,
- domEventName,
- listener
- );
- }
- }
-}
+ var shouldSkipUpdate = isHiddenUpdate
+ ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
+ : !isSubsetOfLanes(renderLanes, updateLane);
-function deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer) {
- // We defer all click events with legacy FB support mode on.
- // This means we add a one time event listener to trigger
- // after the FB delegated listeners fire.
- var isDeferredListenerForLegacyFBSupport = true;
- addTrappedEventListener(
- targetContainer,
- domEventName,
- IS_LEGACY_FB_SUPPORT_MODE,
- false,
- isDeferredListenerForLegacyFBSupport
- );
-}
+ if (shouldSkipUpdate) {
+ // Priority is insufficient. Skip this update. If this is the first
+ // skipped update, the previous update/state is the new base
+ // update/state.
+ var clone = {
+ lane: updateLane,
+ tag: update.tag,
+ payload: update.payload,
+ callback: update.callback,
+ next: null
+ };
-function isMatchingRootContainer(grandContainer, targetContainer) {
- return (
- grandContainer === targetContainer ||
- (grandContainer.nodeType === COMMENT_NODE &&
- grandContainer.parentNode === targetContainer)
- );
-}
+ if (newLastBaseUpdate === null) {
+ newFirstBaseUpdate = newLastBaseUpdate = clone;
+ newBaseState = newState;
+ } else {
+ newLastBaseUpdate = newLastBaseUpdate.next = clone;
+ } // Update the remaining priority in the queue.
-function dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- targetInst,
- targetContainer
-) {
- var ancestorInst = targetInst;
+ newLanes = mergeLanes(newLanes, updateLane);
+ } else {
+ // This update does have sufficient priority.
+ if (newLastBaseUpdate !== null) {
+ var _clone = {
+ // This update is going to be committed so we never want uncommit
+ // it. Using NoLane works because 0 is a subset of all bitmasks, so
+ // this will never be skipped by the check above.
+ lane: NoLane,
+ tag: update.tag,
+ payload: update.payload,
+ // When this update is rebased, we should not fire its
+ // callback again.
+ callback: null,
+ next: null
+ };
+ newLastBaseUpdate = newLastBaseUpdate.next = _clone;
+ } // Process this update.
- if (
- (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 &&
- (eventSystemFlags & IS_NON_DELEGATED) === 0
- ) {
- var targetContainerNode = targetContainer; // If we are using the legacy FB support flag, we
- // defer the event to the null with a one
- // time event listener so we can defer the event.
+ newState = getStateFromUpdate(
+ workInProgress,
+ queue,
+ update,
+ newState,
+ props,
+ instance
+ );
+ var callback = update.callback;
- if (
- enableLegacyFBSupport && // If our event flags match the required flags for entering
- // FB legacy mode and we are processing the "click" event,
- // then we can defer the event to the "document", to allow
- // for legacy FB support, where the expected behavior was to
- // match React < 16 behavior of delegated clicks to the doc.
- domEventName === "click" &&
- (eventSystemFlags & SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE) === 0 &&
- !isReplayingEvent(nativeEvent)
- ) {
- deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer);
- return;
- }
+ if (callback !== null) {
+ workInProgress.flags |= Callback;
- if (targetInst !== null) {
- // The below logic attempts to work out if we need to change
- // the target fiber to a different ancestor. We had similar logic
- // in the legacy event system, except the big difference between
- // systems is that the modern event system now has an event listener
- // attached to each React Root and React Portal Root. Together,
- // the DOM nodes representing these roots are the "rootContainer".
- // To figure out which ancestor instance we should use, we traverse
- // up the fiber tree from the target instance and attempt to find
- // root boundaries that match that of our current "rootContainer".
- // If we find that "rootContainer", we find the parent fiber
- // sub-tree for that root and make that our ancestor instance.
- var node = targetInst;
+ if (isHiddenUpdate) {
+ workInProgress.flags |= Visibility;
+ }
- mainLoop: while (true) {
- if (node === null) {
- return;
+ var callbacks = queue.callbacks;
+
+ if (callbacks === null) {
+ queue.callbacks = [callback];
+ } else {
+ callbacks.push(callback);
+ }
}
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
- var nodeTag = node.tag;
+ update = update.next;
- if (nodeTag === HostRoot || nodeTag === HostPortal) {
- var container = node.stateNode.containerInfo;
+ if (update === null) {
+ pendingQueue = queue.shared.pending;
- if (isMatchingRootContainer(container, targetContainerNode)) {
- break;
- }
+ if (pendingQueue === null) {
+ break;
+ } else {
+ // An update was scheduled from inside a reducer. Add the new
+ // pending updates to the end of the list and keep processing.
+ var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we
+ // unravel them when transferring them to the base queue.
- if (nodeTag === HostPortal) {
- // The target is a portal, but it's not the rootContainer we're looking for.
- // Normally portals handle their own events all the way down to the root.
- // So we should be able to stop now. However, we don't know if this portal
- // was part of *our* root.
- var grandNode = node.return;
+ var _firstPendingUpdate = _lastPendingUpdate.next;
+ _lastPendingUpdate.next = null;
+ update = _firstPendingUpdate;
+ queue.lastBaseUpdate = _lastPendingUpdate;
+ queue.shared.pending = null;
+ }
+ }
+ } while (true);
- while (grandNode !== null) {
- var grandTag = grandNode.tag;
+ if (newLastBaseUpdate === null) {
+ newBaseState = newState;
+ }
- if (grandTag === HostRoot || grandTag === HostPortal) {
- var grandContainer = grandNode.stateNode.containerInfo;
+ queue.baseState = newBaseState;
+ queue.firstBaseUpdate = newFirstBaseUpdate;
+ queue.lastBaseUpdate = newLastBaseUpdate;
- if (
- isMatchingRootContainer(grandContainer, targetContainerNode)
- ) {
- // This is the rootContainer we're looking for and we found it as
- // a parent of the Portal. That means we can ignore it because the
- // Portal will bubble through to us.
- return;
- }
- }
+ if (firstBaseUpdate === null) {
+ // `queue.lanes` is used for entangling transitions. We can set it back to
+ // zero once the queue is empty.
+ queue.shared.lanes = NoLanes;
+ } // Set the remaining expiration time to be whatever is remaining in the queue.
+ // This should be fine because the only two other things that contribute to
+ // expiration time are props and context. We're already in the middle of the
+ // begin phase by the time we start processing the queue, so we've already
+ // dealt with the props. Context in components that specify
+ // shouldComponentUpdate is tricky; but we'll have to account for
+ // that regardless.
- grandNode = grandNode.return;
- }
- } // Now we need to find it's corresponding host fiber in the other
- // tree. To do this we can use getClosestInstanceFromNode, but we
- // need to validate that the fiber is a host instance, otherwise
- // we need to traverse up through the DOM till we find the correct
- // node that is from the other tree.
+ markSkippedUpdateLanes(newLanes);
+ workInProgress.lanes = newLanes;
+ workInProgress.memoizedState = newState;
+ }
- while (container !== null) {
- var parentNode = getClosestInstanceFromNode(container);
+ {
+ currentlyProcessingQueue = null;
+ }
+}
- if (parentNode === null) {
- return;
- }
+function callCallback(callback, context) {
+ if (typeof callback !== "function") {
+ throw new Error(
+ "Invalid argument passed as callback. Expected a function. Instead " +
+ ("received: " + callback)
+ );
+ }
- var parentTag = parentNode.tag;
+ callback.call(context);
+}
- if (
- parentTag === HostComponent ||
- parentTag === HostText ||
- parentTag === HostHoistable ||
- parentTag === HostSingleton
- ) {
- node = ancestorInst = parentNode;
- continue mainLoop;
- }
+function resetHasForceUpdateBeforeProcessing() {
+ hasForceUpdate = false;
+}
+function checkHasForceUpdateAfterProcessing() {
+ return hasForceUpdate;
+}
+function deferHiddenCallbacks(updateQueue) {
+ // When an update finishes on a hidden component, its callback should not
+ // be fired until/unless the component is made visible again. Stash the
+ // callback on the shared queue object so it can be fired later.
+ var newHiddenCallbacks = updateQueue.callbacks;
- container = container.parentNode;
- }
- }
+ if (newHiddenCallbacks !== null) {
+ var existingHiddenCallbacks = updateQueue.shared.hiddenCallbacks;
- node = node.return;
- }
+ if (existingHiddenCallbacks === null) {
+ updateQueue.shared.hiddenCallbacks = newHiddenCallbacks;
+ } else {
+ updateQueue.shared.hiddenCallbacks =
+ existingHiddenCallbacks.concat(newHiddenCallbacks);
}
}
+}
+function commitHiddenCallbacks(updateQueue, context) {
+ // This component is switching from hidden -> visible. Commit any callbacks
+ // that were previously deferred.
+ var hiddenCallbacks = updateQueue.shared.hiddenCallbacks;
- batchedUpdates$1(function () {
- return dispatchEventsForPlugins(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- ancestorInst,
- targetContainer
- );
- });
+ if (hiddenCallbacks !== null) {
+ updateQueue.shared.hiddenCallbacks = null;
+
+ for (var i = 0; i < hiddenCallbacks.length; i++) {
+ var callback = hiddenCallbacks[i];
+ callCallback(callback, context);
+ }
+ }
}
+function commitCallbacks(updateQueue, context) {
+ var callbacks = updateQueue.callbacks;
-function createDispatchListener(instance, listener, currentTarget) {
- return {
- instance: instance,
- listener: listener,
- currentTarget: currentTarget
- };
+ if (callbacks !== null) {
+ updateQueue.callbacks = null;
+
+ for (var i = 0; i < callbacks.length; i++) {
+ var callback = callbacks[i];
+ callCallback(callback, context);
+ }
+ }
}
-function accumulateSinglePhaseListeners(
- targetFiber,
- reactName,
- nativeEventType,
- inCapturePhase,
- accumulateTargetOnly,
- nativeEvent
-) {
- var captureName = reactName !== null ? reactName + "Capture" : null;
- var reactEventName = inCapturePhase ? captureName : reactName;
- var listeners = [];
- var instance = targetFiber;
- var lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path.
+/**
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
+ */
- while (instance !== null) {
- var _instance2 = instance,
- stateNode = _instance2.stateNode,
- tag = _instance2.tag; // Handle listeners that are on HostComponents (i.e.
)
+function shallowEqual(objA, objB) {
+ if (objectIs(objA, objB)) {
+ return true;
+ }
+
+ if (
+ typeof objA !== "object" ||
+ objA === null ||
+ typeof objB !== "object" ||
+ objB === null
+ ) {
+ return false;
+ }
+
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
+
+ if (keysA.length !== keysB.length) {
+ return false;
+ } // Test for A's keys different from B.
+
+ for (var i = 0; i < keysA.length; i++) {
+ var currentKey = keysA[i];
if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
+ !hasOwnProperty.call(objB, currentKey) ||
+ !objectIs(objA[currentKey], objB[currentKey])
) {
- lastHostComponent = stateNode; // createEventHandle listeners
+ return false;
+ }
+ }
- {
- var eventHandlerListeners = getEventHandlerListeners(lastHostComponent);
+ return true;
+}
- if (eventHandlerListeners !== null) {
- eventHandlerListeners.forEach(function (entry) {
- if (
- entry.type === nativeEventType &&
- entry.capture === inCapturePhase
- ) {
- listeners.push(
- createDispatchListener(
- instance,
- entry.callback,
- lastHostComponent
- )
- );
- }
- });
- }
- } // Standard React on* listeners, i.e. onClick or onClickCapture
+var ReactStrictModeWarnings = {
+ recordUnsafeLifecycleWarnings: function (fiber, instance) {},
+ flushPendingUnsafeLifecycleWarnings: function () {},
+ recordLegacyContextWarning: function (fiber, instance) {},
+ flushLegacyContextWarning: function () {},
+ discardPendingWarnings: function () {}
+};
- if (reactEventName !== null) {
- var listener = getListener(instance, reactEventName);
+{
+ var findStrictRoot = function (fiber) {
+ var maybeStrictRoot = null;
+ var node = fiber;
- if (listener != null) {
- listeners.push(
- createDispatchListener(instance, listener, lastHostComponent)
- );
- }
+ while (node !== null) {
+ if (node.mode & StrictLegacyMode) {
+ maybeStrictRoot = node;
}
- } else if (
- tag === ScopeComponent &&
- lastHostComponent !== null &&
- stateNode !== null
- ) {
- // Scopes
- var reactScopeInstance = stateNode;
- var _eventHandlerListeners = getEventHandlerListeners(reactScopeInstance);
+ node = node.return;
+ }
- if (_eventHandlerListeners !== null) {
- _eventHandlerListeners.forEach(function (entry) {
- if (
- entry.type === nativeEventType &&
- entry.capture === inCapturePhase
- ) {
- listeners.push(
- createDispatchListener(
- instance,
- entry.callback,
- lastHostComponent
- )
- );
- }
- });
- }
- } // If we are only accumulating events for the target, then we don't
- // continue to propagate through the React fiber tree to find other
- // listeners.
+ return maybeStrictRoot;
+ };
- if (accumulateTargetOnly) {
- break;
- } // If we are processing the onBeforeBlur event, then we need to take
- // into consideration that part of the React tree might have been hidden
- // or deleted (as we're invoking this event during commit). We can find
- // this out by checking if intercept fiber set on the event matches the
- // current instance fiber. In which case, we should clear all existing
- // listeners.
+ var setToSortedString = function (set) {
+ var array = [];
+ set.forEach(function (value) {
+ array.push(value);
+ });
+ return array.sort().join(", ");
+ };
- if (nativeEvent.type === "beforeblur") {
- // $FlowFixMe[prop-missing] internal field
- var detachedInterceptFiber = nativeEvent._detachedInterceptFiber;
+ var pendingComponentWillMountWarnings = [];
+ var pendingUNSAFE_ComponentWillMountWarnings = [];
+ var pendingComponentWillReceivePropsWarnings = [];
+ var pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ var pendingComponentWillUpdateWarnings = [];
+ var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about.
- if (
- detachedInterceptFiber !== null &&
- (detachedInterceptFiber === instance ||
- detachedInterceptFiber === instance.alternate)
- ) {
- listeners = [];
- }
+ var didWarnAboutUnsafeLifecycles = new Set();
+
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (
+ fiber,
+ instance
+ ) {
+ // Dedupe strategy: Warn once per component.
+ if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
+ return;
}
- instance = instance.return;
- }
+ if (
+ typeof instance.componentWillMount === "function" && // Don't warn about react-lifecycles-compat polyfilled components.
+ instance.componentWillMount.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillMountWarnings.push(fiber);
+ }
- return listeners;
-} // We should only use this function for:
-// - BeforeInputEventPlugin
-// - ChangeEventPlugin
-// - SelectEventPlugin
-// This is because we only process these plugins
-// in the bubble phase, so we need to accumulate two
-// phase event listeners (via emulation).
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillMount === "function"
+ ) {
+ pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
+ }
-function accumulateTwoPhaseListeners(targetFiber, reactName) {
- var captureName = reactName + "Capture";
- var listeners = [];
- var instance = targetFiber; // Accumulate all instances and listeners via the target -> root path.
+ if (
+ typeof instance.componentWillReceiveProps === "function" &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
+ }
- while (instance !== null) {
- var _instance3 = instance,
- stateNode = _instance3.stateNode,
- tag = _instance3.tag; // Handle listeners that are on HostComponents (i.e.
)
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillReceiveProps === "function"
+ ) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
+ }
if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
+ typeof instance.componentWillUpdate === "function" &&
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
) {
- var currentTarget = stateNode;
- var captureListener = getListener(instance, captureName);
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
- if (captureListener != null) {
- listeners.unshift(
- createDispatchListener(instance, captureListener, currentTarget)
- );
- }
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillUpdate === "function"
+ ) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
+ }
+ };
- var bubbleListener = getListener(instance, reactName);
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
+ // We do an initial pass to gather component names
+ var componentWillMountUniqueNames = new Set();
- if (bubbleListener != null) {
- listeners.push(
- createDispatchListener(instance, bubbleListener, currentTarget)
+ if (pendingComponentWillMountWarnings.length > 0) {
+ pendingComponentWillMountWarnings.forEach(function (fiber) {
+ componentWillMountUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
);
- }
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillMountWarnings = [];
}
- instance = instance.return;
- }
+ var UNSAFE_componentWillMountUniqueNames = new Set();
- return listeners;
-}
+ if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillMountUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ }
-function getParent(inst) {
- if (inst === null) {
- return null;
- }
+ var componentWillReceivePropsUniqueNames = new Set();
- do {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- inst = inst.return; // TODO: If this is a HostRoot we might want to bail out.
- // That is depending on if we want nested subtrees (layers) to bubble
- // events to their parent. We could also go through parentNode on the
- // host node but that wouldn't work for React Native and doesn't let us
- // do the portal feature.
- } while (inst && inst.tag !== HostComponent && inst.tag !== HostSingleton);
+ if (pendingComponentWillReceivePropsWarnings.length > 0) {
+ pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
+ componentWillReceivePropsUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillReceivePropsWarnings = [];
+ }
- if (inst) {
- return inst;
- }
+ var UNSAFE_componentWillReceivePropsUniqueNames = new Set();
- return null;
-}
-/**
- * Return the lowest common ancestor of A and B, or null if they are in
- * different trees.
- */
+ if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillReceivePropsUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ }
-function getLowestCommonAncestor(instA, instB) {
- var nodeA = instA;
- var nodeB = instB;
- var depthA = 0;
+ var componentWillUpdateUniqueNames = new Set();
- for (var tempA = nodeA; tempA; tempA = getParent(tempA)) {
- depthA++;
- }
+ if (pendingComponentWillUpdateWarnings.length > 0) {
+ pendingComponentWillUpdateWarnings.forEach(function (fiber) {
+ componentWillUpdateUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillUpdateWarnings = [];
+ }
- var depthB = 0;
+ var UNSAFE_componentWillUpdateUniqueNames = new Set();
- for (var tempB = nodeB; tempB; tempB = getParent(tempB)) {
- depthB++;
- } // If A is deeper, crawl up.
+ if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillUpdateUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
+ } // Finally, we flush all the warnings
+ // UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
- while (depthA - depthB > 0) {
- nodeA = getParent(nodeA);
- depthA--;
- } // If B is deeper, crawl up.
+ if (UNSAFE_componentWillMountUniqueNames.size > 0) {
+ var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames);
- while (depthB - depthA > 0) {
- nodeB = getParent(nodeB);
- depthB--;
- } // Walk in lockstep until we find a match.
+ error(
+ "Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
+ "\nPlease update the following components: %s",
+ sortedNames
+ );
+ }
- var depth = depthA;
+ if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
+ var _sortedNames = setToSortedString(
+ UNSAFE_componentWillReceivePropsUniqueNames
+ );
- while (depth--) {
- if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
- return nodeA;
+ error(
+ "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended " +
+ "and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* If you're updating state whenever props change, " +
+ "refactor your code to use memoization techniques or move it to " +
+ "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames
+ );
}
- nodeA = getParent(nodeA);
- nodeB = getParent(nodeB);
- }
+ if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
+ var _sortedNames2 = setToSortedString(
+ UNSAFE_componentWillUpdateUniqueNames
+ );
- return null;
-}
+ error(
+ "Using UNSAFE_componentWillUpdate in strict mode is not recommended " +
+ "and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames2
+ );
+ }
-function accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- event,
- target,
- common,
- inCapturePhase
-) {
- var registrationName = event._reactName;
- var listeners = [];
- var instance = target;
+ if (componentWillMountUniqueNames.size > 0) {
+ var _sortedNames3 = setToSortedString(componentWillMountUniqueNames);
- while (instance !== null) {
- if (instance === common) {
- break;
+ warn(
+ "componentWillMount has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
+ "* Rename componentWillMount to UNSAFE_componentWillMount to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames3
+ );
}
- var _instance4 = instance,
- alternate = _instance4.alternate,
- stateNode = _instance4.stateNode,
- tag = _instance4.tag;
+ if (componentWillReceivePropsUniqueNames.size > 0) {
+ var _sortedNames4 = setToSortedString(
+ componentWillReceivePropsUniqueNames
+ );
- if (alternate !== null && alternate === common) {
- break;
+ warn(
+ "componentWillReceiveProps has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* If you're updating state whenever props change, refactor your " +
+ "code to use memoization techniques or move it to " +
+ "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
+ "* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames4
+ );
}
- if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
- ) {
- var currentTarget = stateNode;
+ if (componentWillUpdateUniqueNames.size > 0) {
+ var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames);
- if (inCapturePhase) {
- var captureListener = getListener(instance, registrationName);
+ warn(
+ "componentWillUpdate has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames5
+ );
+ }
+ };
- if (captureListener != null) {
- listeners.unshift(
- createDispatchListener(instance, captureListener, currentTarget)
- );
- }
- } else if (!inCapturePhase) {
- var bubbleListener = getListener(instance, registrationName);
+ var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about.
- if (bubbleListener != null) {
- listeners.push(
- createDispatchListener(instance, bubbleListener, currentTarget)
- );
- }
- }
- }
+ var didWarnAboutLegacyContext = new Set();
- instance = instance.return;
- }
+ ReactStrictModeWarnings.recordLegacyContextWarning = function (
+ fiber,
+ instance
+ ) {
+ var strictRoot = findStrictRoot(fiber);
- if (listeners.length !== 0) {
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- }
-} // We should only use this function for:
-// - EnterLeaveEventPlugin
-// This is because we only process this plugin
-// in the bubble phase, so we need to accumulate two
-// phase event listeners.
+ if (strictRoot === null) {
+ error(
+ "Expected to find a StrictMode component in a strict mode tree. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
-function accumulateEnterLeaveTwoPhaseListeners(
- dispatchQueue,
- leaveEvent,
- enterEvent,
- from,
- to
-) {
- var common = from && to ? getLowestCommonAncestor(from, to) : null;
+ return;
+ } // Dedup strategy: Warn once per component.
- if (from !== null) {
- accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- leaveEvent,
- from,
- common,
- false
- );
- }
+ if (didWarnAboutLegacyContext.has(fiber.type)) {
+ return;
+ }
- if (to !== null && enterEvent !== null) {
- accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- enterEvent,
- to,
- common,
- true
- );
- }
-}
-function accumulateEventHandleNonManagedNodeListeners(
- reactEventType,
- currentTarget,
- inCapturePhase
-) {
- var listeners = [];
- var eventListeners = getEventHandlerListeners(currentTarget);
+ var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
- if (eventListeners !== null) {
- eventListeners.forEach(function (entry) {
- if (entry.type === reactEventType && entry.capture === inCapturePhase) {
- listeners.push(
- createDispatchListener(null, entry.callback, currentTarget)
- );
+ if (
+ fiber.type.contextTypes != null ||
+ fiber.type.childContextTypes != null ||
+ (instance !== null && typeof instance.getChildContext === "function")
+ ) {
+ if (warningsForRoot === undefined) {
+ warningsForRoot = [];
+ pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
}
- });
- }
- return listeners;
-}
-function getListenerSetKey(domEventName, capture) {
- return domEventName + "__" + (capture ? "capture" : "bubble");
-}
+ warningsForRoot.push(fiber);
+ }
+ };
-// This is imported by the event replaying implementation in React DOM. It's
-// in a separate file to break a circular dependency between the renderer and
-// the reconciler.
-function isRootDehydrated(root) {
- var currentState = root.current.memoizedState;
- return currentState.isDehydrated;
-}
-
-var _attemptSynchronousHydration;
+ ReactStrictModeWarnings.flushLegacyContextWarning = function () {
+ pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
+ if (fiberArray.length === 0) {
+ return;
+ }
-function setAttemptSynchronousHydration(fn) {
- _attemptSynchronousHydration = fn;
-}
-function attemptSynchronousHydration$1(fiber) {
- _attemptSynchronousHydration(fiber);
-}
-var attemptDiscreteHydration$1;
-function setAttemptDiscreteHydration(fn) {
- attemptDiscreteHydration$1 = fn;
-}
-var attemptContinuousHydration$1;
-function setAttemptContinuousHydration(fn) {
- attemptContinuousHydration$1 = fn;
-}
-var attemptHydrationAtCurrentPriority$1;
-function setAttemptHydrationAtCurrentPriority(fn) {
- attemptHydrationAtCurrentPriority$1 = fn;
-}
-var getCurrentUpdatePriority;
-function setGetCurrentUpdatePriority(fn) {
- getCurrentUpdatePriority = fn;
-}
-var attemptHydrationAtPriority;
-function setAttemptHydrationAtPriority(fn) {
- attemptHydrationAtPriority = fn;
-} // TODO: Upgrade this definition once we're on a newer version of Flow that
-// has this definition built-in.
+ var firstFiber = fiberArray[0];
+ var uniqueNames = new Set();
+ fiberArray.forEach(function (fiber) {
+ uniqueNames.add(getComponentNameFromFiber(fiber) || "Component");
+ didWarnAboutLegacyContext.add(fiber.type);
+ });
+ var sortedNames = setToSortedString(uniqueNames);
-var hasScheduledReplayAttempt = false; // The queue of discrete events to be replayed.
+ try {
+ setCurrentFiber(firstFiber);
-var queuedDiscreteEvents = []; // Indicates if any continuous event targets are non-null for early bailout.
-// if the last target was dehydrated.
+ error(
+ "Legacy context API has been detected within a strict-mode tree." +
+ "\n\nThe old API will be supported in all 16.x releases, but applications " +
+ "using it should migrate to the new version." +
+ "\n\nPlease update the following components: %s" +
+ "\n\nLearn more about this warning here: https://reactjs.org/link/legacy-context",
+ sortedNames
+ );
+ } finally {
+ resetCurrentFiber();
+ }
+ });
+ };
-var queuedFocus = null;
-var queuedDrag = null;
-var queuedMouse = null; // For pointer events there can be one latest event per pointerId.
+ ReactStrictModeWarnings.discardPendingWarnings = function () {
+ pendingComponentWillMountWarnings = [];
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
+ pendingLegacyContextWarning = new Map();
+ };
+}
-var queuedPointers = new Map();
-var queuedPointerCaptures = new Map(); // We could consider replaying selectionchange and touchmoves too.
+var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we
+// detect this is caught by userspace, we'll log a warning in development.
-var queuedExplicitHydrationTargets = [];
-function hasQueuedDiscreteEvents() {
- return queuedDiscreteEvents.length > 0;
+var SuspenseException = new Error(
+ "Suspense Exception: This is not a real error! It's an implementation " +
+ "detail of `use` to interrupt the current render. You must either " +
+ "rethrow it immediately, or move the `use` call outside of the " +
+ "`try/catch` block. Capturing without rethrowing will lead to " +
+ "unexpected behavior.\n\n" +
+ "To handle async errors, wrap your component in an error boundary, or " +
+ "call the promise's `.catch` method and pass the result to `use`"
+);
+function createThenableState() {
+ // The ThenableState is created the first time a component suspends. If it
+ // suspends again, we'll reuse the same state.
+ return [];
}
-var discreteReplayableEvents = [
- "mousedown",
- "mouseup",
- "touchcancel",
- "touchend",
- "touchstart",
- "auxclick",
- "dblclick",
- "pointercancel",
- "pointerdown",
- "pointerup",
- "dragend",
- "dragstart",
- "drop",
- "compositionend",
- "compositionstart",
- "keydown",
- "keypress",
- "keyup",
- "input",
- "textInput", // Intentionally camelCase
- "copy",
- "cut",
- "paste",
- "click",
- "change",
- "contextmenu",
- "reset",
- "submit"
-];
-function isDiscreteEventThatRequiresHydration(eventType) {
- return discreteReplayableEvents.indexOf(eventType) > -1;
+function isThenableResolved(thenable) {
+ var status = thenable.status;
+ return status === "fulfilled" || status === "rejected";
}
-function createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- return {
- blockedOn: blockedOn,
- domEventName: domEventName,
- eventSystemFlags: eventSystemFlags,
- nativeEvent: nativeEvent,
- targetContainers: [targetContainer]
- };
-}
+function noop() {}
-function queueDiscreteEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- return;
+function trackUsedThenable(thenableState, thenable, index) {
+ if (ReactCurrentActQueue$2.current !== null) {
+ ReactCurrentActQueue$2.didUsePromise = true;
}
- var queuedEvent = createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- queuedDiscreteEvents.push(queuedEvent);
-
- if (queuedDiscreteEvents.length === 1) {
- // If this was the first discrete event, we might be able to
- // synchronously unblock it so that preventDefault still works.
- while (queuedEvent.blockedOn !== null) {
- var fiber = getInstanceFromNode(queuedEvent.blockedOn);
+ var previous = thenableState[index];
- if (fiber === null) {
- break;
- }
+ if (previous === undefined) {
+ thenableState.push(thenable);
+ } else {
+ if (previous !== thenable) {
+ // Reuse the previous thenable, and drop the new one. We can assume
+ // they represent the same value, because components are idempotent.
+ // Avoid an unhandled rejection errors for the Promises that we'll
+ // intentionally ignore.
+ thenable.then(noop, noop);
+ thenable = previous;
+ }
+ } // We use an expando to track the status and result of a thenable so that we
+ // can synchronously unwrap the value. Think of this as an extension of the
+ // Promise API, or a custom interface that is a superset of Thenable.
+ //
+ // If the thenable doesn't have a status, set it to "pending" and attach
+ // a listener that will update its status and result when it resolves.
- attemptSynchronousHydration$1(fiber);
+ switch (thenable.status) {
+ case "fulfilled": {
+ var fulfilledValue = thenable.value;
+ return fulfilledValue;
+ }
- if (queuedEvent.blockedOn === null) {
- // We got unblocked by hydration. Let's try again.
- replayUnblockedEvents(); // If we're reblocked, on an inner boundary, we might need
- // to attempt hydrating that one.
+ case "rejected": {
+ var rejectedError = thenable.reason;
+ throw rejectedError;
+ }
- continue;
+ default: {
+ if (typeof thenable.status === "string") {
+ // Only instrument the thenable if the status if not defined. If
+ // it's defined, but an unknown value, assume it's been instrumented by
+ // some custom userspace implementation. We treat it as "pending".
+ // Attach a dummy listener, to ensure that any lazy initialization can
+ // happen. Flight lazily parses JSON when the value is actually awaited.
+ thenable.then(noop, noop);
} else {
- // We're still blocked from hydration, we have to give up
- // and replay later.
- break;
- }
- }
- }
-} // Resets the replaying for this type of continuous event to no event.
+ var pendingThenable = thenable;
+ pendingThenable.status = "pending";
+ pendingThenable.then(
+ function (fulfilledValue) {
+ if (thenable.status === "pending") {
+ var fulfilledThenable = thenable;
+ fulfilledThenable.status = "fulfilled";
+ fulfilledThenable.value = fulfilledValue;
+ }
+ },
+ function (error) {
+ if (thenable.status === "pending") {
+ var rejectedThenable = thenable;
+ rejectedThenable.status = "rejected";
+ rejectedThenable.reason = error;
+ }
+ }
+ );
+ } // Check one more time in case the thenable resolved synchronously.
-function clearIfContinuousEvent(domEventName, nativeEvent) {
- switch (domEventName) {
- case "focusin":
- case "focusout":
- queuedFocus = null;
- break;
+ switch (thenable.status) {
+ case "fulfilled": {
+ var fulfilledThenable = thenable;
+ return fulfilledThenable.value;
+ }
- case "dragenter":
- case "dragleave":
- queuedDrag = null;
- break;
+ case "rejected": {
+ var rejectedThenable = thenable;
+ throw rejectedThenable.reason;
+ }
+ } // Suspend.
+ //
+ // Throwing here is an implementation detail that allows us to unwind the
+ // call stack. But we shouldn't allow it to leak into userspace. Throw an
+ // opaque placeholder value instead of the actual thenable. If it doesn't
+ // get captured by the work loop, log a warning, because that means
+ // something in userspace must have caught it.
- case "mouseover":
- case "mouseout":
- queuedMouse = null;
- break;
+ suspendedThenable = thenable;
- case "pointerover":
- case "pointerout": {
- var pointerId = nativeEvent.pointerId;
- queuedPointers.delete(pointerId);
- break;
- }
+ {
+ needsToResetSuspendedThenableDEV = true;
+ }
- case "gotpointercapture":
- case "lostpointercapture": {
- var _pointerId = nativeEvent.pointerId;
- queuedPointerCaptures.delete(_pointerId);
- break;
+ throw SuspenseException;
}
}
-}
+} // This is used to track the actual thenable that suspended so it can be
+// passed to the rest of the Suspense implementation — which, for historical
+// reasons, expects to receive a thenable.
-function accumulateOrCreateContinuousQueuedReplayableEvent(
- existingQueuedEvent,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- if (
- existingQueuedEvent === null ||
- existingQueuedEvent.nativeEvent !== nativeEvent
- ) {
- var queuedEvent = createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
+var suspendedThenable = null;
+var needsToResetSuspendedThenableDEV = false;
+function getSuspendedThenable() {
+ // This is called right after `use` suspends by throwing an exception. `use`
+ // throws an opaque value instead of the thenable itself so that it can't be
+ // caught in userspace. Then the work loop accesses the actual thenable using
+ // this function.
+ if (suspendedThenable === null) {
+ throw new Error(
+ "Expected a suspended thenable. This is a bug in React. Please file " +
+ "an issue."
);
+ }
- if (blockedOn !== null) {
- var fiber = getInstanceFromNode(blockedOn);
-
- if (fiber !== null) {
- // Attempt to increase the priority of this target.
- attemptContinuousHydration$1(fiber);
- }
- }
-
- return queuedEvent;
- } // If we have already queued this exact event, then it's because
- // the different event systems have different DOM event listeners.
- // We can accumulate the flags, and the targetContainers, and
- // store a single event to be replayed.
-
- existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
- var targetContainers = existingQueuedEvent.targetContainers;
+ var thenable = suspendedThenable;
+ suspendedThenable = null;
- if (
- targetContainer !== null &&
- targetContainers.indexOf(targetContainer) === -1
- ) {
- targetContainers.push(targetContainer);
+ {
+ needsToResetSuspendedThenableDEV = false;
}
- return existingQueuedEvent;
+ return thenable;
}
-
-function queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // These set relatedTarget to null because the replayed event will be treated as if we
- // moved from outside the window (no target) onto the target once it hydrates.
- // Instead of mutating we could clone the event.
- switch (domEventName) {
- case "focusin": {
- var focusEvent = nativeEvent;
- queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedFocus,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- focusEvent
- );
+function checkIfUseWrappedInTryCatch() {
+ {
+ // This was set right before SuspenseException was thrown, and it should
+ // have been cleared when the exception was handled. If it wasn't,
+ // it must have been caught by userspace.
+ if (needsToResetSuspendedThenableDEV) {
+ needsToResetSuspendedThenableDEV = false;
return true;
}
+ }
- case "dragenter": {
- var dragEvent = nativeEvent;
- queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedDrag,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- dragEvent
- );
- return true;
- }
+ return false;
+}
- case "mouseover": {
- var mouseEvent = nativeEvent;
- queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedMouse,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- mouseEvent
- );
- return true;
- }
+var thenableState$1 = null;
+var thenableIndexCounter$1 = 0;
+var didWarnAboutMaps;
+var didWarnAboutGenerators;
+var didWarnAboutStringRefs;
+var ownerHasKeyUseWarning;
+var ownerHasFunctionTypeWarning;
- case "pointerover": {
- var pointerEvent = nativeEvent;
- var pointerId = pointerEvent.pointerId;
- queuedPointers.set(
- pointerId,
- accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedPointers.get(pointerId) || null,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- pointerEvent
- )
- );
- return true;
+var warnForMissingKey = function (child, returnFiber) {};
+
+{
+ didWarnAboutMaps = false;
+ didWarnAboutGenerators = false;
+ didWarnAboutStringRefs = {};
+ /**
+ * Warn if there's no key explicitly set on dynamic arrays of children or
+ * object keys are not valid. This allows us to keep track of children between
+ * updates.
+ */
+
+ ownerHasKeyUseWarning = {};
+ ownerHasFunctionTypeWarning = {};
+
+ warnForMissingKey = function (child, returnFiber) {
+ if (child === null || typeof child !== "object") {
+ return;
}
- case "gotpointercapture": {
- var _pointerEvent = nativeEvent;
- var _pointerId2 = _pointerEvent.pointerId;
- queuedPointerCaptures.set(
- _pointerId2,
- accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedPointerCaptures.get(_pointerId2) || null,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- _pointerEvent
- )
- );
- return true;
+ if (!child._store || child._store.validated || child.key != null) {
+ return;
}
- }
- return false;
-} // Check if this target is unblocked. Returns true if it's unblocked.
+ if (typeof child._store !== "object") {
+ throw new Error(
+ "React Component in warnForMissingKey should have a _store. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ } // $FlowFixMe unable to narrow type from mixed to writable object
-function attemptExplicitHydrationTarget(queuedTarget) {
- // TODO: This function shares a lot of logic with findInstanceBlockingEvent.
- // Try to unify them. It's a bit tricky since it would require two return
- // values.
- var targetInst = getClosestInstanceFromNode(queuedTarget.target);
+ child._store.validated = true;
+ var componentName = getComponentNameFromFiber(returnFiber) || "Component";
- if (targetInst !== null) {
- var nearestMounted = getNearestMountedFiber(targetInst);
+ if (ownerHasKeyUseWarning[componentName]) {
+ return;
+ }
- if (nearestMounted !== null) {
- var tag = nearestMounted.tag;
+ ownerHasKeyUseWarning[componentName] = true;
- if (tag === SuspenseComponent) {
- var instance = getSuspenseInstanceFromFiber(nearestMounted);
+ error(
+ "Each child in a list should have a unique " +
+ '"key" prop. See https://reactjs.org/link/warning-keys for ' +
+ "more information."
+ );
+ };
+}
- if (instance !== null) {
- // We're blocked on hydrating this boundary.
- // Increase its priority.
- queuedTarget.blockedOn = instance;
- attemptHydrationAtPriority(queuedTarget.priority, function () {
- attemptHydrationAtCurrentPriority$1(nearestMounted);
- });
- return;
- }
- } else if (tag === HostRoot) {
- var root = nearestMounted.stateNode;
+function isReactClass(type) {
+ return type.prototype && type.prototype.isReactComponent;
+}
- if (isRootDehydrated(root)) {
- queuedTarget.blockedOn = getContainerFromFiber(nearestMounted); // We don't currently have a way to increase the priority of
- // a root other than sync.
+function unwrapThenable(thenable) {
+ var index = thenableIndexCounter$1;
+ thenableIndexCounter$1 += 1;
- return;
- }
- }
- }
+ if (thenableState$1 === null) {
+ thenableState$1 = createThenableState();
}
- queuedTarget.blockedOn = null;
+ return trackUsedThenable(thenableState$1, thenable, index);
}
-function queueExplicitHydrationTarget(target) {
- // TODO: This will read the priority if it's dispatched by the React
- // event system but not native events. Should read window.event.type, like
- // we do for updates (getCurrentEventPriority).
- var updatePriority = getCurrentUpdatePriority();
- var queuedTarget = {
- blockedOn: null,
- target: target,
- priority: updatePriority
- };
- var i = 0;
+function coerceRef(returnFiber, current, element) {
+ var mixedRef = element.ref;
- for (; i < queuedExplicitHydrationTargets.length; i++) {
- // Stop once we hit the first target with lower priority than
- if (
- !isHigherEventPriority(
- updatePriority,
- queuedExplicitHydrationTargets[i].priority
- )
- ) {
- break;
- }
- }
-
- queuedExplicitHydrationTargets.splice(i, 0, queuedTarget);
-
- if (i === 0) {
- attemptExplicitHydrationTarget(queuedTarget);
- }
-}
-
-function attemptReplayContinuousQueuedEvent(queuedEvent) {
- if (queuedEvent.blockedOn !== null) {
- return false;
- }
-
- var targetContainers = queuedEvent.targetContainers;
-
- while (targetContainers.length > 0) {
- var targetContainer = targetContainers[0];
- var nextBlockedOn = findInstanceBlockingEvent(
- queuedEvent.domEventName,
- queuedEvent.eventSystemFlags,
- targetContainer,
- queuedEvent.nativeEvent
- );
+ if (
+ mixedRef !== null &&
+ typeof mixedRef !== "function" &&
+ typeof mixedRef !== "object"
+ ) {
+ {
+ if (
+ // We warn in ReactElement.js if owner and self are equal for string refs
+ // because these cannot be automatically converted to an arrow function
+ // using a codemod. Therefore, we don't have to warn about string refs again.
+ !(
+ element._owner &&
+ element._self &&
+ element._owner.stateNode !== element._self
+ ) && // Will already throw with "Function components cannot have string refs"
+ !(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs"
+ !(typeof element.type === "function" && !isReactClass(element.type)) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
+ element._owner
+ ) {
+ var componentName =
+ getComponentNameFromFiber(returnFiber) || "Component";
- if (nextBlockedOn === null) {
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- var nativeEvent = queuedEvent.nativeEvent;
- var nativeEventClone = new nativeEvent.constructor(
- nativeEvent.type,
- nativeEvent
- );
- setReplayingEvent(nativeEventClone);
- nativeEvent.target.dispatchEvent(nativeEventClone);
- resetReplayingEvent();
- } else {
- setReplayingEvent(queuedEvent.nativeEvent);
- dispatchEventForPluginEventSystem(
- queuedEvent.domEventName,
- queuedEvent.eventSystemFlags,
- queuedEvent.nativeEvent,
- return_targetInst,
- targetContainer
- );
- resetReplayingEvent();
- }
- } else {
- // We're still blocked. Try again later.
- var fiber = getInstanceFromNode(nextBlockedOn);
+ if (!didWarnAboutStringRefs[componentName]) {
+ error(
+ 'Component "%s" contains the string ref "%s". Support for string refs ' +
+ "will be removed in a future major release. We recommend using " +
+ "useRef() or createRef() instead. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-string-ref",
+ componentName,
+ mixedRef
+ );
- if (fiber !== null) {
- attemptContinuousHydration$1(fiber);
+ didWarnAboutStringRefs[componentName] = true;
+ }
}
+ }
- queuedEvent.blockedOn = nextBlockedOn;
- return false;
- } // This target container was successfully dispatched. Try the next.
+ if (element._owner) {
+ var owner = element._owner;
+ var inst;
- targetContainers.shift();
- }
+ if (owner) {
+ var ownerFiber = owner;
- return true;
-}
+ if (ownerFiber.tag !== ClassComponent) {
+ throw new Error(
+ "Function components cannot have string refs. " +
+ "We recommend using useRef() instead. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-string-ref"
+ );
+ }
-function attemptReplayContinuousQueuedEventInMap(queuedEvent, key, map) {
- if (attemptReplayContinuousQueuedEvent(queuedEvent)) {
- map.delete(key);
- }
-}
+ inst = ownerFiber.stateNode;
+ }
-function replayUnblockedEvents() {
- hasScheduledReplayAttempt = false;
+ if (!inst) {
+ throw new Error(
+ "Missing owner for string ref " +
+ mixedRef +
+ ". This error is likely caused by a " +
+ "bug in React. Please file an issue."
+ );
+ } // Assigning this to a const so Flow knows it won't change in the closure
- if (!enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- // First replay discrete events.
- while (queuedDiscreteEvents.length > 0) {
- var nextDiscreteEvent = queuedDiscreteEvents[0];
+ var resolvedInst = inst;
- if (nextDiscreteEvent.blockedOn !== null) {
- // We're still blocked.
- // Increase the priority of this boundary to unblock
- // the next discrete event.
- var fiber = getInstanceFromNode(nextDiscreteEvent.blockedOn);
+ {
+ checkPropStringCoercion(mixedRef, "ref");
+ }
- if (fiber !== null) {
- attemptDiscreteHydration$1(fiber);
- }
+ var stringRef = "" + mixedRef; // Check if previous string ref matches new string ref
- break;
+ if (
+ current !== null &&
+ current.ref !== null &&
+ typeof current.ref === "function" &&
+ current.ref._stringRef === stringRef
+ ) {
+ return current.ref;
}
- var targetContainers = nextDiscreteEvent.targetContainers;
-
- while (targetContainers.length > 0) {
- var targetContainer = targetContainers[0];
- var nextBlockedOn = findInstanceBlockingEvent(
- nextDiscreteEvent.domEventName,
- nextDiscreteEvent.eventSystemFlags,
- targetContainer,
- nextDiscreteEvent.nativeEvent
- );
+ var ref = function (value) {
+ var refs = resolvedInst.refs;
- if (nextBlockedOn === null) {
- // This whole function is in !enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
- // so we don't need the new replay behavior code branch.
- setReplayingEvent(nextDiscreteEvent.nativeEvent);
- dispatchEventForPluginEventSystem(
- nextDiscreteEvent.domEventName,
- nextDiscreteEvent.eventSystemFlags,
- nextDiscreteEvent.nativeEvent,
- return_targetInst,
- targetContainer
- );
- resetReplayingEvent();
+ if (value === null) {
+ delete refs[stringRef];
} else {
- // We're still blocked. Try again later.
- nextDiscreteEvent.blockedOn = nextBlockedOn;
- break;
- } // This target container was successfully dispatched. Try the next.
+ refs[stringRef] = value;
+ }
+ };
- targetContainers.shift();
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ if (typeof mixedRef !== "string") {
+ throw new Error(
+ "Expected ref to be a function, a string, an object returned by React.createRef(), or null."
+ );
}
- if (nextDiscreteEvent.blockedOn === null) {
- // We've successfully replayed the first event. Let's try the next one.
- queuedDiscreteEvents.shift();
+ if (!element._owner) {
+ throw new Error(
+ "Element ref was specified as a string (" +
+ mixedRef +
+ ") but no owner was set. This could happen for one of" +
+ " the following reasons:\n" +
+ "1. You may be adding a ref to a function component\n" +
+ "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
+ "3. You have multiple copies of React loaded\n" +
+ "See https://reactjs.org/link/refs-must-have-owner for more information."
+ );
}
}
- } // Next replay any continuous events.
-
- if (queuedFocus !== null && attemptReplayContinuousQueuedEvent(queuedFocus)) {
- queuedFocus = null;
- }
-
- if (queuedDrag !== null && attemptReplayContinuousQueuedEvent(queuedDrag)) {
- queuedDrag = null;
- }
-
- if (queuedMouse !== null && attemptReplayContinuousQueuedEvent(queuedMouse)) {
- queuedMouse = null;
}
- queuedPointers.forEach(attemptReplayContinuousQueuedEventInMap);
- queuedPointerCaptures.forEach(attemptReplayContinuousQueuedEventInMap);
+ return mixedRef;
}
-function scheduleCallbackIfUnblocked(queuedEvent, unblocked) {
- if (queuedEvent.blockedOn === unblocked) {
- queuedEvent.blockedOn = null;
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ // $FlowFixMe[method-unbinding]
+ var childString = Object.prototype.toString.call(newChild);
+ throw new Error(
+ "Objects are not valid as a React child (found: " +
+ (childString === "[object Object]"
+ ? "object with keys {" + Object.keys(newChild).join(", ") + "}"
+ : childString) +
+ "). " +
+ "If you meant to render a collection of children, use an array " +
+ "instead."
+ );
+}
- if (!hasScheduledReplayAttempt) {
- hasScheduledReplayAttempt = true; // Schedule a callback to attempt replaying as many events as are
- // now unblocked. This first might not actually be unblocked yet.
- // We could check it early to avoid scheduling an unnecessary callback.
+function warnOnFunctionType(returnFiber) {
+ {
+ var componentName = getComponentNameFromFiber(returnFiber) || "Component";
- Scheduler.unstable_scheduleCallback(
- Scheduler.unstable_NormalPriority,
- replayUnblockedEvents
- );
+ if (ownerHasFunctionTypeWarning[componentName]) {
+ return;
}
+
+ ownerHasFunctionTypeWarning[componentName] = true;
+
+ error(
+ "Functions are not valid as a React child. This may happen if " +
+ "you return a Component instead of from render. " +
+ "Or maybe you meant to call this function rather than return it."
+ );
}
}
-function retryIfBlockedOn(unblocked) {
- // Mark anything that was blocked on this as no longer blocked
- // and eligible for a replay.
- if (queuedDiscreteEvents.length > 0) {
- scheduleCallbackIfUnblocked(queuedDiscreteEvents[0], unblocked); // This is a exponential search for each boundary that commits. I think it's
- // worth it because we expect very few discrete events to queue up and once
- // we are actually fully unblocked it will be fast to replay them.
+function resolveLazy(lazyType) {
+ var payload = lazyType._payload;
+ var init = lazyType._init;
+ return init(payload);
+} // This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
- for (var i = 1; i < queuedDiscreteEvents.length; i++) {
- var queuedEvent = queuedDiscreteEvents[i];
+function createChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
+ }
- if (queuedEvent.blockedOn === unblocked) {
- queuedEvent.blockedOn = null;
- }
+ var deletions = returnFiber.deletions;
+
+ if (deletions === null) {
+ returnFiber.deletions = [childToDelete];
+ returnFiber.flags |= ChildDeletion;
+ } else {
+ deletions.push(childToDelete);
}
}
- if (queuedFocus !== null) {
- scheduleCallbackIfUnblocked(queuedFocus, unblocked);
- }
+ 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.
- if (queuedDrag !== null) {
- scheduleCallbackIfUnblocked(queuedDrag, unblocked);
- }
+ var childToDelete = currentFirstChild;
- if (queuedMouse !== null) {
- scheduleCallbackIfUnblocked(queuedMouse, unblocked);
- }
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
+ }
- var unblock = function (queuedEvent) {
- return scheduleCallbackIfUnblocked(queuedEvent, unblocked);
- };
+ return null;
+ }
- queuedPointers.forEach(unblock);
- queuedPointerCaptures.forEach(unblock);
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ // instead.
+ var existingChildren = new Map();
+ var existingChild = currentFirstChild;
- for (var _i = 0; _i < queuedExplicitHydrationTargets.length; _i++) {
- var queuedTarget = queuedExplicitHydrationTargets[_i];
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
+ }
- if (queuedTarget.blockedOn === unblocked) {
- queuedTarget.blockedOn = null;
+ existingChild = existingChild.sibling;
}
+
+ return existingChildren;
}
- while (queuedExplicitHydrationTargets.length > 0) {
- var nextExplicitTarget = queuedExplicitHydrationTargets[0];
+ function useFiber(fiber, pendingProps) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
+ }
- if (nextExplicitTarget.blockedOn !== null) {
- // We're still blocked.
- break;
- } else {
- attemptExplicitHydrationTarget(nextExplicitTarget);
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
- if (nextExplicitTarget.blockedOn === null) {
- // We're unblocked.
- queuedExplicitHydrationTargets.shift();
- }
+ if (!shouldTrackSideEffects) {
+ // During hydration, the useId algorithm needs to know which fibers are
+ // part of a list of children (arrays, iterators).
+ newFiber.flags |= Forked;
+ return lastPlacedIndex;
}
- }
-}
-var ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig; // TODO: can we stop exporting these?
+ var current = newFiber.alternate;
-var _enabled = true; // This is exported in FB builds for use by legacy FB layer infra.
-// We'd like to remove this but it's not clear if this is safe.
+ if (current !== null) {
+ var oldIndex = current.index;
-function setEnabled(enabled) {
- _enabled = !!enabled;
-}
-function isEnabled() {
- return _enabled;
-}
-function createEventListenerWrapperWithPriority(
- targetContainer,
- domEventName,
- eventSystemFlags
-) {
- var eventPriority = getEventPriority(domEventName);
- var listenerWrapper;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.flags |= Placement | PlacementDEV;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
+ }
+ } else {
+ // This is an insertion.
+ newFiber.flags |= Placement | PlacementDEV;
+ return lastPlacedIndex;
+ }
+ }
- switch (eventPriority) {
- case DiscreteEventPriority:
- listenerWrapper = dispatchDiscreteEvent;
- break;
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.flags |= Placement | PlacementDEV;
+ }
- case ContinuousEventPriority:
- listenerWrapper = dispatchContinuousEvent;
- break;
+ return newFiber;
+ }
- case DefaultEventPriority:
- default:
- listenerWrapper = dispatchEvent;
- break;
+ function updateTextNode(returnFiber, current, textContent, lanes) {
+ if (current === null || current.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(textContent, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, textContent);
+ existing.return = returnFiber;
+ return existing;
+ }
}
- return listenerWrapper.bind(
- null,
- domEventName,
- eventSystemFlags,
- targetContainer
- );
-}
+ function updateElement(returnFiber, current, element, lanes) {
+ var elementType = element.type;
-function dispatchDiscreteEvent(
- domEventName,
- eventSystemFlags,
- container,
- nativeEvent
-) {
- var previousPriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig$3.transition;
- ReactCurrentBatchConfig$3.transition = null;
+ if (elementType === REACT_FRAGMENT_TYPE) {
+ return updateFragment(
+ returnFiber,
+ current,
+ element.props.children,
+ lanes,
+ element.key
+ );
+ }
- try {
- setCurrentUpdatePriority(DiscreteEventPriority);
- dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig$3.transition = prevTransition;
- }
-}
+ if (current !== null) {
+ if (
+ current.elementType === elementType || // Keep this check inline so it only runs on the false path:
+ isCompatibleFamilyForHotReloading(current, element) || // Lazy types should reconcile their resolved type.
+ // We need to do this after the Hot Reloading check above,
+ // because hot reloading has different semantics than prod because
+ // it doesn't resuspend. So we can't let the call below suspend.
+ (typeof elementType === "object" &&
+ elementType !== null &&
+ elementType.$$typeof === REACT_LAZY_TYPE &&
+ resolveLazy(elementType) === current.type)
+ ) {
+ // Move based on index
+ var existing = useFiber(current, element.props);
+ existing.ref = coerceRef(returnFiber, current, element);
+ existing.return = returnFiber;
-function dispatchContinuousEvent(
- domEventName,
- eventSystemFlags,
- container,
- nativeEvent
-) {
- var previousPriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig$3.transition;
- ReactCurrentBatchConfig$3.transition = null;
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
- try {
- setCurrentUpdatePriority(ContinuousEventPriority);
- dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig$3.transition = prevTransition;
- }
-}
+ return existing;
+ }
+ } // Insert
-function dispatchEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- if (!_enabled) {
- return;
+ var created = createFiberFromElement(element, returnFiber.mode, lanes);
+ created.ref = coerceRef(returnFiber, current, element);
+ created.return = returnFiber;
+ return created;
}
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- } else {
- dispatchEventOriginal(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- }
-}
-
-function dispatchEventOriginal(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // TODO: replaying capture phase events is currently broken
- // because we used to do it during top-level native bubble handlers
- // but now we use different bubble and capture handlers.
- // In eager mode, we attach capture listeners early, so we need
- // to filter them out until we fix the logic to handle them correctly.
- var allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0;
-
- if (
- allowReplay &&
- hasQueuedDiscreteEvents() &&
- isDiscreteEventThatRequiresHydration(domEventName)
- ) {
- // If we already have a queue of discrete events, and this is another discrete
- // event, then we can't dispatch it regardless of its target, since they
- // need to dispatch in order.
- queueDiscreteEvent(
- null, // Flags that we're not actually blocked on anything as far as we know.
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- return;
- }
-
- var blockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
-
- if (blockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
- );
-
- if (allowReplay) {
- clearIfContinuousEvent(domEventName, nativeEvent);
+ function updatePortal(returnFiber, current, portal, lanes) {
+ if (
+ current === null ||
+ current.tag !== HostPortal ||
+ current.stateNode.containerInfo !== portal.containerInfo ||
+ current.stateNode.implementation !== portal.implementation
+ ) {
+ // Insert
+ var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, portal.children || []);
+ existing.return = returnFiber;
+ return existing;
}
-
- return;
}
- if (allowReplay) {
- if (isDiscreteEventThatRequiresHydration(domEventName)) {
- // This to be replayed later once the target is available.
- queueDiscreteEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
+ function updateFragment(returnFiber, current, fragment, lanes, key) {
+ if (current === null || current.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(
+ fragment,
+ returnFiber.mode,
+ lanes,
+ key
);
- return;
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, fragment);
+ existing.return = returnFiber;
+ return existing;
}
+ }
+ function createChild(returnFiber, newChild, lanes) {
if (
- queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- )
+ (typeof newChild === "string" && newChild !== "") ||
+ typeof newChild === "number"
) {
- return;
- } // We need to clear only if we didn't queue because
- // queueing is accumulative.
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText("" + newChild, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ }
- clearIfContinuousEvent(domEventName, nativeEvent);
- } // This is not replayable so we'll invoke it but without a target,
- // in case the event system needs to trace it.
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ var _created = createFiberFromElement(
+ newChild,
+ returnFiber.mode,
+ lanes
+ );
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- null,
- targetContainer
- );
-}
+ _created.ref = coerceRef(returnFiber, null, newChild);
+ _created.return = returnFiber;
+ return _created;
+ }
-function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- var blockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
+ case REACT_PORTAL_TYPE: {
+ var _created2 = createFiberFromPortal(
+ newChild,
+ returnFiber.mode,
+ lanes
+ );
- if (blockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
- );
- clearIfContinuousEvent(domEventName, nativeEvent);
- return;
- }
+ _created2.return = returnFiber;
+ return _created2;
+ }
- if (
- queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- )
- ) {
- nativeEvent.stopPropagation();
- return;
- } // We need to clear only if we didn't queue because
- // queueing is accumulative.
+ case REACT_LAZY_TYPE: {
+ var payload = newChild._payload;
+ var init = newChild._init;
+ return createChild(returnFiber, init(payload), lanes);
+ }
+ }
- clearIfContinuousEvent(domEventName, nativeEvent);
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(
+ newChild,
+ returnFiber.mode,
+ lanes,
+ null
+ );
- if (
- eventSystemFlags & IS_CAPTURE_PHASE &&
- isDiscreteEventThatRequiresHydration(domEventName)
- ) {
- while (blockedOn !== null) {
- var fiber = getInstanceFromNode(blockedOn);
+ _created3.return = returnFiber;
+ return _created3;
+ } // Usable node types
+ //
+ // Unwrap the inner value and recursively call this function again.
- if (fiber !== null) {
- attemptSynchronousHydration$1(fiber);
+ if (typeof newChild.then === "function") {
+ var thenable = newChild;
+ return createChild(returnFiber, unwrapThenable(thenable), lanes);
}
- var nextBlockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
-
- if (nextBlockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
+ if (
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = newChild;
+ return createChild(
+ returnFiber,
+ readContextDuringReconcilation(returnFiber, context, lanes),
+ lanes
);
}
- if (nextBlockedOn === blockedOn) {
- break;
- }
-
- blockedOn = nextBlockedOn;
+ throwOnInvalidObjectType(returnFiber, newChild);
}
- if (blockedOn !== null) {
- nativeEvent.stopPropagation();
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType(returnFiber);
+ }
}
- return;
- } // This is not replayable so we'll invoke it but without a target,
- // in case the event system needs to trace it.
+ return null;
+ }
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- null,
- targetContainer
- );
-}
+ function updateSlot(returnFiber, oldFiber, newChild, lanes) {
+ // Update the fiber if the keys match, otherwise return null.
+ var key = oldFiber !== null ? oldFiber.key : null;
-var return_targetInst = null; // Returns a SuspenseInstance or Container if it's blocked.
-// The return_targetInst field above is conceptually part of the return value.
+ if (
+ (typeof newChild === "string" && newChild !== "") ||
+ typeof newChild === "number"
+ ) {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ if (key !== null) {
+ return null;
+ }
-function findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // TODO: Warn if _enabled is false.
- return_targetInst = null;
- var nativeEventTarget = getEventTarget(nativeEvent);
- var targetInst = getClosestInstanceFromNode(nativeEventTarget);
+ return updateTextNode(returnFiber, oldFiber, "" + newChild, lanes);
+ }
- if (targetInst !== null) {
- var nearestMounted = getNearestMountedFiber(targetInst);
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ if (newChild.key === key) {
+ return updateElement(returnFiber, oldFiber, newChild, lanes);
+ } else {
+ return null;
+ }
+ }
- if (nearestMounted === null) {
- // This tree has been unmounted already. Dispatch without a target.
- targetInst = null;
- } else {
- var tag = nearestMounted.tag;
+ case REACT_PORTAL_TYPE: {
+ if (newChild.key === key) {
+ return updatePortal(returnFiber, oldFiber, newChild, lanes);
+ } else {
+ return null;
+ }
+ }
- if (tag === SuspenseComponent) {
- var instance = getSuspenseInstanceFromFiber(nearestMounted);
+ case REACT_LAZY_TYPE: {
+ var payload = newChild._payload;
+ var init = newChild._init;
+ return updateSlot(returnFiber, oldFiber, init(payload), lanes);
+ }
+ }
- if (instance !== null) {
- // Queue the event to be replayed later. Abort dispatching since we
- // don't want this event dispatched twice through the event system.
- // TODO: If this is the first discrete event in the queue. Schedule an increased
- // priority for this boundary.
- return instance;
- } // This shouldn't happen, something went wrong but to avoid blocking
- // the whole system, dispatch the event without a target.
- // TODO: Warn.
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ if (key !== null) {
+ return null;
+ }
- targetInst = null;
- } else if (tag === HostRoot) {
- var root = nearestMounted.stateNode;
+ return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
+ } // Usable node types
+ //
+ // Unwrap the inner value and recursively call this function again.
- if (isRootDehydrated(root)) {
- // If this happens during a replay something went wrong and it might block
- // the whole system.
- return getContainerFromFiber(nearestMounted);
- }
+ if (typeof newChild.then === "function") {
+ var thenable = newChild;
+ return updateSlot(
+ returnFiber,
+ oldFiber,
+ unwrapThenable(thenable),
+ lanes
+ );
+ }
- targetInst = null;
- } else if (nearestMounted !== targetInst) {
- // If we get an event (ex: img onload) before committing that
- // component's mount, ignore it for now (that is, treat it as if it was an
- // event on a non-React tree). We might also consider queueing events and
- // dispatching them after the mount.
- targetInst = null;
+ if (
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = newChild;
+ return updateSlot(
+ returnFiber,
+ oldFiber,
+ readContextDuringReconcilation(returnFiber, context, lanes),
+ lanes
+ );
}
+
+ throwOnInvalidObjectType(returnFiber, newChild);
}
- }
- return_targetInst = targetInst; // We're not blocked on anything.
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType(returnFiber);
+ }
+ }
- return null;
-}
-function getEventPriority(domEventName) {
- switch (domEventName) {
- // Used by SimpleEventPlugin:
- case "cancel":
- case "click":
- case "close":
- case "contextmenu":
- case "copy":
- case "cut":
- case "auxclick":
- case "dblclick":
- case "dragend":
- case "dragstart":
- case "drop":
- case "focusin":
- case "focusout":
- case "input":
- case "invalid":
- case "keydown":
- case "keypress":
- case "keyup":
- case "mousedown":
- case "mouseup":
- case "paste":
- case "pause":
- case "play":
- case "pointercancel":
- case "pointerdown":
- case "pointerup":
- case "ratechange":
- case "reset":
- case "resize":
- case "seeked":
- case "submit":
- case "touchcancel":
- case "touchend":
- case "touchstart":
- case "volumechange": // Used by polyfills:
- // eslint-disable-next-line no-fallthrough
+ return null;
+ }
- case "change":
- case "selectionchange":
- case "textInput":
- case "compositionstart":
- case "compositionend":
- case "compositionupdate": // Only enableCreateEventHandleAPI:
- // eslint-disable-next-line no-fallthrough
+ function updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ newChild,
+ lanes
+ ) {
+ if (
+ (typeof newChild === "string" && newChild !== "") ||
+ typeof newChild === "number"
+ ) {
+ // Text nodes don't have keys, so we neither have to check the old nor
+ // new node for the key. If both are text nodes, they match.
+ var matchedFiber = existingChildren.get(newIdx) || null;
+ return updateTextNode(returnFiber, matchedFiber, "" + newChild, lanes);
+ }
- case "beforeblur":
- case "afterblur": // Not used by React but could be by user code:
- // eslint-disable-next-line no-fallthrough
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ var _matchedFiber =
+ existingChildren.get(
+ newChild.key === null ? newIdx : newChild.key
+ ) || null;
- case "beforeinput":
- case "blur":
- case "fullscreenchange":
- case "focus":
- case "hashchange":
- case "popstate":
- case "select":
- case "selectstart":
- return DiscreteEventPriority;
+ return updateElement(returnFiber, _matchedFiber, newChild, lanes);
+ }
- case "drag":
- case "dragenter":
- case "dragexit":
- case "dragleave":
- case "dragover":
- case "mousemove":
- case "mouseout":
- case "mouseover":
- case "pointermove":
- case "pointerout":
- case "pointerover":
- case "scroll":
- case "toggle":
- case "touchmove":
- case "wheel": // Not used by React but could be by user code:
- // eslint-disable-next-line no-fallthrough
+ case REACT_PORTAL_TYPE: {
+ var _matchedFiber2 =
+ existingChildren.get(
+ newChild.key === null ? newIdx : newChild.key
+ ) || null;
- case "mouseenter":
- case "mouseleave":
- case "pointerenter":
- case "pointerleave":
- return ContinuousEventPriority;
+ return updatePortal(returnFiber, _matchedFiber2, newChild, lanes);
+ }
- case "message": {
- // We might be in the Scheduler callback.
- // Eventually this mechanism will be replaced by a check
- // of the current priority on the native scheduler.
- var schedulerPriority = getCurrentPriorityLevel();
-
- switch (schedulerPriority) {
- case ImmediatePriority:
- return DiscreteEventPriority;
-
- case UserBlockingPriority:
- return ContinuousEventPriority;
+ case REACT_LAZY_TYPE:
+ var payload = newChild._payload;
+ var init = newChild._init;
+ return updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ init(payload),
+ lanes
+ );
+ }
- case NormalPriority$1:
- case LowPriority:
- // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
- return DefaultEventPriority;
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _matchedFiber3 = existingChildren.get(newIdx) || null;
- case IdlePriority:
- return IdleEventPriority;
+ return updateFragment(
+ returnFiber,
+ _matchedFiber3,
+ newChild,
+ lanes,
+ null
+ );
+ } // Usable node types
+ //
+ // Unwrap the inner value and recursively call this function again.
- default:
- return DefaultEventPriority;
+ if (typeof newChild.then === "function") {
+ var thenable = newChild;
+ return updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ unwrapThenable(thenable),
+ lanes
+ );
}
- }
- default:
- return DefaultEventPriority;
- }
-}
+ if (
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = newChild;
+ return updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ readContextDuringReconcilation(returnFiber, context, lanes),
+ lanes
+ );
+ }
-var loggedTypeFailures = {};
-var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
-function setCurrentlyValidatingElement(element) {
- {
- if (element) {
- var owner = element._owner;
- var stack = describeUnknownElementTypeFrameInDEV(
- element.type,
- element._source,
- owner ? owner.type : null
- );
- ReactDebugCurrentFrame.setExtraStackFrame(stack);
- } else {
- ReactDebugCurrentFrame.setExtraStackFrame(null);
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType(returnFiber);
+ }
}
+
+ return null;
}
-}
+ /**
+ * Warns if there is a duplicate or missing key
+ */
-function checkPropTypes(typeSpecs, values, location, componentName, element) {
- {
- // $FlowFixMe This is okay but Flow doesn't know it.
- var has = Function.call.bind(hasOwnProperty);
+ function warnOnInvalidKey(child, knownKeys, returnFiber) {
+ {
+ if (typeof child !== "object" || child === null) {
+ return knownKeys;
+ }
- for (var typeSpecName in typeSpecs) {
- if (has(typeSpecs, typeSpecName)) {
- var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
- // fail the render phase where it didn't fail before. So we log it.
- // After these have been cleaned up, we'll let them throw.
+ switch (child.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ warnForMissingKey(child, returnFiber);
+ var key = child.key;
- try {
- // This is intentionally an invariant that gets caught. It's the same
- // behavior as without this statement except with a better message.
- if (typeof typeSpecs[typeSpecName] !== "function") {
- // eslint-disable-next-line react-internal/prod-error-codes
- var err = Error(
- (componentName || "React class") +
- ": " +
- location +
- " type `" +
- typeSpecName +
- "` is invalid; " +
- "it must be a function, usually from the `prop-types` package, but received `" +
- typeof typeSpecs[typeSpecName] +
- "`." +
- "This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`."
- );
- err.name = "Invariant Violation";
- throw err;
+ if (typeof key !== "string") {
+ break;
}
- error$1 = typeSpecs[typeSpecName](
- values,
- typeSpecName,
- componentName,
- location,
- null,
- "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"
- );
- } catch (ex) {
- error$1 = ex;
- }
+ if (knownKeys === null) {
+ knownKeys = new Set();
+ knownKeys.add(key);
+ break;
+ }
- if (error$1 && !(error$1 instanceof Error)) {
- setCurrentlyValidatingElement(element);
+ if (!knownKeys.has(key)) {
+ knownKeys.add(key);
+ break;
+ }
error(
- "%s: type specification of %s" +
- " `%s` is invalid; the type checker " +
- "function must return `null` or an `Error` but returned a %s. " +
- "You may have forgotten to pass an argument to the type checker " +
- "creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and " +
- "shape all require an argument).",
- componentName || "React class",
- location,
- typeSpecName,
- typeof error$1
+ "Encountered two children with the same key, `%s`. " +
+ "Keys should be unique so that components maintain their identity " +
+ "across updates. Non-unique keys may cause children to be " +
+ "duplicated and/or omitted — the behavior is unsupported and " +
+ "could change in a future version.",
+ key
);
- setCurrentlyValidatingElement(null);
- }
-
- if (
- error$1 instanceof Error &&
- !(error$1.message in loggedTypeFailures)
- ) {
- // Only monitor this failure once because there tends to be a lot of the
- // same error.
- loggedTypeFailures[error$1.message] = true;
- setCurrentlyValidatingElement(element);
-
- error("Failed %s type: %s", location, error$1.message);
+ break;
- setCurrentlyValidatingElement(null);
- }
+ case REACT_LAZY_TYPE:
+ var payload = child._payload;
+ var init = child._init;
+ warnOnInvalidKey(init(payload), knownKeys, returnFiber);
+ break;
}
}
+
+ return knownKeys;
}
-}
-var warnedAboutMissingGetChildContext;
+ function reconcileChildrenArray(
+ returnFiber,
+ currentFirstChild,
+ newChildren,
+ lanes
+ ) {
+ // This algorithm can't optimize by searching from both ends since we
+ // don't have backpointers on fibers. I'm trying to see how far we can get
+ // with that model. If it ends up not being worth the tradeoffs, we can
+ // add it later.
+ // Even with a two ended optimization, we'd want to optimize for the case
+ // where there are few changes and brute force the comparison instead of
+ // going for the Map. It'd like to explore hitting that path first in
+ // forward-only mode and only go for the Map once we notice that we need
+ // lots of look ahead. This doesn't handle reversal as well as two ended
+ // search but that's unusual. Besides, for the two ended optimization to
+ // work on Iterables, we'd need to copy the whole set.
+ // In this first iteration, we'll just live with hitting the bad case
+ // (adding everything to a Map) in for every insert/move.
+ // If you change this code, also update reconcileChildrenIterator() which
+ // uses the same algorithm.
+ {
+ // First, validate keys.
+ var knownKeys = null;
-{
- warnedAboutMissingGetChildContext = {};
-}
+ for (var i = 0; i < newChildren.length; i++) {
+ var child = newChildren[i];
+ knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
+ }
+ }
-var emptyContextObject = {};
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
-{
- Object.freeze(emptyContextObject);
-} // A cursor to the current merged context object on the stack.
+ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
-var contextStackCursor = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed.
+ var newFiber = updateSlot(
+ returnFiber,
+ oldFiber,
+ newChildren[newIdx],
+ lanes
+ );
-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.
+ 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;
+ }
-var previousContext = emptyContextObject;
+ break;
+ }
-function getUnmaskedContext(
- workInProgress,
- Component,
- didPushOwnContextIfProvider
-) {
- {
- if (didPushOwnContextIfProvider && isContextProvider(Component)) {
- // If the fiber is a context provider itself, when we read its context
- // we may have already pushed its own child context on the stack. A context
- // provider should not "see" its own child context. Therefore we read the
- // previous (parent) context instead for a context provider.
- return previousContext;
- }
+ 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);
+ }
+ }
- return contextStackCursor.current;
- }
-}
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
-function cacheContext(workInProgress, unmaskedContext, maskedContext) {
- {
- var instance = workInProgress.stateNode;
- instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
- instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
- }
-}
+ 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;
+ }
-function getMaskedContext(workInProgress, unmaskedContext) {
- {
- var type = workInProgress.type;
- var contextTypes = type.contextTypes;
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
- if (!contextTypes) {
- return emptyContextObject;
- } // Avoid recreating masked context unless unmasked context has changed.
- // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
- // This may trigger infinite loops if componentWillReceiveProps calls setState.
+ if (newIdx === newChildren.length) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
- var instance = workInProgress.stateNode;
+ if (getIsHydrating()) {
+ var numberOfForks = newIdx;
+ pushTreeFork(returnFiber, numberOfForks);
+ }
- if (
- instance &&
- instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
- ) {
- return instance.__reactInternalMemoizedMaskedChildContext;
+ return resultingFirstChild;
}
- var context = {};
-
- for (var key in contextTypes) {
- context[key] = unmaskedContext[key];
- }
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
- {
- var name = getComponentNameFromFiber(workInProgress) || "Unknown";
- checkPropTypes(contextTypes, context, "context", name);
- } // Cache unmasked context so we can avoid recreating masked context unless necessary.
- // Context is created before the class component is instantiated so check for instance.
+ if (_newFiber === null) {
+ continue;
+ }
- if (instance) {
- cacheContext(workInProgress, unmaskedContext, context);
- }
+ lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
- return context;
- }
-}
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber;
+ } else {
+ previousNewFiber.sibling = _newFiber;
+ }
-function hasContextChanged() {
- {
- return didPerformWorkStackCursor.current;
- }
-}
+ previousNewFiber = _newFiber;
+ }
-function isContextProvider(type) {
- {
- var childContextTypes = type.childContextTypes;
- return childContextTypes !== null && childContextTypes !== undefined;
- }
-}
+ if (getIsHydrating()) {
+ var _numberOfForks = newIdx;
+ pushTreeFork(returnFiber, _numberOfForks);
+ }
-function popContext(fiber) {
- {
- pop(didPerformWorkStackCursor, fiber);
- pop(contextStackCursor, fiber);
- }
-}
+ return resultingFirstChild;
+ } // Add all children to a key map for quick lookups.
-function popTopLevelContextObject(fiber) {
- {
- pop(didPerformWorkStackCursor, fiber);
- pop(contextStackCursor, fiber);
- }
-}
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
-function pushTopLevelContextObject(fiber, context, didChange) {
- {
- if (contextStackCursor.current !== emptyContextObject) {
- throw new Error(
- "Unexpected context found on stack. " +
- "This error is likely caused by a bug in React. Please file an issue."
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber2 = updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ newChildren[newIdx],
+ lanes
);
- }
-
- push(contextStackCursor, context, fiber);
- push(didPerformWorkStackCursor, didChange, fiber);
- }
-}
-
-function processChildContext(fiber, type, parentContext) {
- {
- var instance = fiber.stateNode;
- var childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future.
- // It has only been added in Fiber to match the (unintentional) behavior in Stack.
- if (typeof instance.getChildContext !== "function") {
- {
- var componentName = getComponentNameFromFiber(fiber) || "Unknown";
+ if (_newFiber2 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber2.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(
+ _newFiber2.key === null ? newIdx : _newFiber2.key
+ );
+ }
+ }
- if (!warnedAboutMissingGetChildContext[componentName]) {
- warnedAboutMissingGetChildContext[componentName] = true;
+ lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
- error(
- "%s.childContextTypes is specified but there is no getChildContext() method " +
- "on the instance. You can either define getChildContext() on %s or remove " +
- "childContextTypes from it.",
- componentName,
- componentName
- );
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber2;
+ } else {
+ previousNewFiber.sibling = _newFiber2;
}
- }
- return parentContext;
+ previousNewFiber = _newFiber2;
+ }
}
- var childContext = instance.getChildContext();
-
- for (var contextKey in childContext) {
- if (!(contextKey in childContextTypes)) {
- throw new Error(
- (getComponentNameFromFiber(fiber) || "Unknown") +
- '.getChildContext(): key "' +
- contextKey +
- '" is not defined in childContextTypes.'
- );
- }
+ 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);
+ });
}
- {
- var name = getComponentNameFromFiber(fiber) || "Unknown";
- checkPropTypes(childContextTypes, childContext, "child context", name);
+ if (getIsHydrating()) {
+ var _numberOfForks2 = newIdx;
+ pushTreeFork(returnFiber, _numberOfForks2);
}
- return assign({}, parentContext, childContext);
+ return resultingFirstChild;
}
-}
-function pushContextProvider(workInProgress) {
- {
- var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity.
- // If the instance does not exist yet, we will push null at first,
- // and replace it on the stack later when invalidating the context.
+ function reconcileChildrenIterator(
+ returnFiber,
+ currentFirstChild,
+ newChildrenIterable,
+ lanes
+ ) {
+ // This is the same implementation as reconcileChildrenArray(),
+ // but using the iterator instead.
+ var iteratorFn = getIteratorFn(newChildrenIterable);
- var memoizedMergedChildContext =
- (instance && instance.__reactInternalMemoizedMergedChildContext) ||
- emptyContextObject; // Remember the parent context so we can merge with it later.
- // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
+ if (typeof iteratorFn !== "function") {
+ throw new Error(
+ "An object is not an iterable. This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
+ }
- previousContext = contextStackCursor.current;
- push(contextStackCursor, memoizedMergedChildContext, workInProgress);
- push(
- didPerformWorkStackCursor,
- didPerformWorkStackCursor.current,
- workInProgress
- );
- return true;
- }
-}
+ {
+ // We don't support rendering Generators because it's a mutation.
+ // See https://github.com/facebook/react/issues/12995
+ if (
+ typeof Symbol === "function" && // $FlowFixMe Flow doesn't know about toStringTag
+ newChildrenIterable[Symbol.toStringTag] === "Generator"
+ ) {
+ if (!didWarnAboutGenerators) {
+ error(
+ "Using Generators as children is unsupported and will likely yield " +
+ "unexpected results because enumerating a generator mutates it. " +
+ "You may convert it to an array with `Array.from()` or the " +
+ "`[...spread]` operator before rendering. Keep in mind " +
+ "you might need to polyfill these features for older browsers."
+ );
+ }
-function invalidateContextProvider(workInProgress, type, didChange) {
- {
- var instance = workInProgress.stateNode;
+ didWarnAboutGenerators = true;
+ } // Warn about using Maps as children
- if (!instance) {
- throw new Error(
- "Expected to have an instance by this point. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- }
+ if (newChildrenIterable.entries === iteratorFn) {
+ if (!didWarnAboutMaps) {
+ error(
+ "Using Maps as children is not supported. " +
+ "Use an array of keyed ReactElements instead."
+ );
+ }
- if (didChange) {
- // Merge parent and own context.
- // Skip this if we're not updating due to sCU.
- // This avoids unnecessarily recomputing memoized values.
- var mergedContext = processChildContext(
- workInProgress,
- type,
- previousContext
- );
- instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one.
- // It is important to unwind the context in the reverse order.
+ didWarnAboutMaps = true;
+ } // First, validate keys.
+ // We'll get a different iterator later for the main pass.
- pop(didPerformWorkStackCursor, workInProgress);
- pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed.
+ var _newChildren = iteratorFn.call(newChildrenIterable);
- push(contextStackCursor, mergedContext, workInProgress);
- push(didPerformWorkStackCursor, didChange, workInProgress);
- } else {
- pop(didPerformWorkStackCursor, workInProgress);
- push(didPerformWorkStackCursor, didChange, workInProgress);
+ if (_newChildren) {
+ var knownKeys = null;
+
+ var _step = _newChildren.next();
+
+ for (; !_step.done; _step = _newChildren.next()) {
+ var child = _step.value;
+ knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
+ }
+ }
}
- }
-}
-function findCurrentUnmaskedContext(fiber) {
- {
- // Currently this is only used with renderSubtreeIntoContainer; not sure if it
- // makes sense elsewhere
- if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
- throw new Error(
- "Expected subtree parent to be a mounted class component. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
+ var newChildren = iteratorFn.call(newChildrenIterable);
+
+ if (newChildren == null) {
+ throw new Error("An iterable object provided no iterator.");
}
- var node = fiber;
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+ var step = newChildren.next();
- do {
- switch (node.tag) {
- case HostRoot:
- return node.stateNode.context;
+ for (
+ ;
+ oldFiber !== null && !step.done;
+ newIdx++, step = newChildren.next()
+ ) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
- case ClassComponent: {
- var Component = node.type;
+ var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
- if (isContextProvider(Component)) {
- return node.stateNode.__reactInternalMemoizedMergedChildContext;
- }
+ 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;
+ 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);
}
- } // $FlowFixMe[incompatible-type] we bail out when we get a null
+ }
- node = node.return;
- } while (node !== null);
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
- throw new Error(
- "Found unexpected detached subtree parent. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- }
-}
+ 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;
+ }
-var LegacyRoot = 0;
-var ConcurrentRoot = 1;
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
-// We use the existence of the state object as an indicator that the component
-// is hidden.
-var OffscreenVisible =
- /* */
- 1;
-var OffscreenDetached =
- /* */
- 2;
-var OffscreenPassiveEffectsConnected =
- /* */
- 4;
-function isOffscreenManual(offscreenFiber) {
- return (
- offscreenFiber.memoizedProps !== null &&
- offscreenFiber.memoizedProps.mode === "manual"
- );
-}
+ if (step.done) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
-var syncQueue = null;
-var includesLegacySyncCallbacks = false;
-var isFlushingSyncQueue = false;
-function scheduleSyncCallback(callback) {
- // Push this callback into an internal queue. We'll flush these either in
- // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
- if (syncQueue === null) {
- syncQueue = [callback];
- } else {
- // Push onto existing queue. Don't need to schedule a callback because
- // we already scheduled one when we created the queue.
- syncQueue.push(callback);
- }
-}
-function scheduleLegacySyncCallback(callback) {
- includesLegacySyncCallbacks = true;
- scheduleSyncCallback(callback);
-}
-function flushSyncCallbacksOnlyInLegacyMode() {
- // Only flushes the queue if there's a legacy sync callback scheduled.
- // TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
- // it might make more sense for the queue to be a list of roots instead of a
- // list of generic callbacks. Then we can have two: one for legacy roots, one
- // for concurrent roots. And this method would only flush the legacy ones.
- if (includesLegacySyncCallbacks) {
- flushSyncCallbacks();
- }
-}
-function flushSyncCallbacks() {
- if (!isFlushingSyncQueue && syncQueue !== null) {
- // Prevent re-entrance.
- isFlushingSyncQueue = true; // Set the event priority to discrete
- // TODO: Is this necessary anymore? The only user code that runs in this
- // queue is in the render or commit phases, which already set the
- // event priority. Should be able to remove.
+ if (getIsHydrating()) {
+ var numberOfForks = newIdx;
+ pushTreeFork(returnFiber, numberOfForks);
+ }
- var previousUpdatePriority = getCurrentUpdatePriority$1();
- setCurrentUpdatePriority(DiscreteEventPriority);
- var errors = null;
- var queue = syncQueue; // $FlowFixMe[incompatible-use] found when upgrading Flow
+ return resultingFirstChild;
+ }
- for (var i = 0; i < queue.length; i++) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- var callback = queue[i];
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber3 = createChild(returnFiber, step.value, lanes);
- try {
- do {
- var isSync = true; // $FlowFixMe[incompatible-type] we bail out when we get a null
+ if (_newFiber3 === null) {
+ continue;
+ }
- callback = callback(isSync);
- } while (callback !== null);
- } catch (error) {
- // Collect errors so we can rethrow them at the end
- if (errors === null) {
- errors = [error];
+ lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
+
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber3;
} else {
- errors.push(error);
+ previousNewFiber.sibling = _newFiber3;
}
+
+ previousNewFiber = _newFiber3;
}
- }
- syncQueue = null;
- includesLegacySyncCallbacks = false;
- setCurrentUpdatePriority(previousUpdatePriority);
- isFlushingSyncQueue = false;
+ if (getIsHydrating()) {
+ var _numberOfForks3 = newIdx;
+ pushTreeFork(returnFiber, _numberOfForks3);
+ }
- if (errors !== null) {
- if (errors.length > 1) {
- if (typeof AggregateError === "function") {
- // eslint-disable-next-line no-undef
- throw new AggregateError(errors);
- } else {
- for (var _i = 1; _i < errors.length; _i++) {
- scheduleCallback$2(
- ImmediatePriority,
- throwError.bind(null, errors[_i])
+ return resultingFirstChild;
+ } // Add all children to a key map for quick lookups.
+
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
+
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber4 = updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ step.value,
+ lanes
+ );
+
+ if (_newFiber4 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber4.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren.delete(
+ _newFiber4.key === null ? newIdx : _newFiber4.key
);
}
+ }
- var firstError = errors[0];
- throw firstError;
+ lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber4;
+ } else {
+ previousNewFiber.sibling = _newFiber4;
}
- } else {
- var error = errors[0];
- throw error;
+
+ previousNewFiber = _newFiber4;
}
}
- }
-
- return null;
-}
-function throwError(error) {
- throw error;
-}
+ 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);
+ });
+ }
-var nativeConsole = console;
-var nativeConsoleLog = null;
-var pendingGroupArgs = [];
-var printedGroupIndex = -1;
+ if (getIsHydrating()) {
+ var _numberOfForks4 = newIdx;
+ pushTreeFork(returnFiber, _numberOfForks4);
+ }
-function formatLanes(laneOrLanes) {
- return "0b" + laneOrLanes.toString(2).padStart(31, "0");
-}
+ return resultingFirstChild;
+ }
-function group() {
- for (
- var _len = arguments.length, groupArgs = new Array(_len), _key = 0;
- _key < _len;
- _key++
+ function reconcileSingleTextNode(
+ returnFiber,
+ currentFirstChild,
+ textContent,
+ lanes
) {
- groupArgs[_key] = arguments[_key];
+ // There's no need to check for keys on text nodes since we don't have a
+ // way to define them.
+ if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
+ // We already have an existing node so let's just update it and delete
+ // the rest.
+ deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
+ var existing = useFiber(currentFirstChild, textContent);
+ existing.return = returnFiber;
+ return existing;
+ } // The existing first child is not a text node so we need to create one
+ // and delete the existing ones.
+
+ deleteRemainingChildren(returnFiber, currentFirstChild);
+ var created = createFiberFromText(textContent, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
}
- pendingGroupArgs.push(groupArgs);
+ function reconcileSingleElement(
+ returnFiber,
+ currentFirstChild,
+ element,
+ lanes
+ ) {
+ var key = element.key;
+ var child = currentFirstChild;
- if (nativeConsoleLog === null) {
- nativeConsoleLog = nativeConsole.log;
- nativeConsole.log = log;
- }
-}
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ var elementType = element.type;
-function groupEnd() {
- pendingGroupArgs.pop();
+ if (elementType === REACT_FRAGMENT_TYPE) {
+ if (child.tag === Fragment) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, element.props.children);
+ existing.return = returnFiber;
- while (printedGroupIndex >= pendingGroupArgs.length) {
- nativeConsole.groupEnd();
- printedGroupIndex--;
- }
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
- if (pendingGroupArgs.length === 0) {
- nativeConsole.log = nativeConsoleLog;
- nativeConsoleLog = null;
- }
-}
+ return existing;
+ }
+ } else {
+ if (
+ child.elementType === elementType || // Keep this check inline so it only runs on the false path:
+ isCompatibleFamilyForHotReloading(child, element) || // Lazy types should reconcile their resolved type.
+ // We need to do this after the Hot Reloading check above,
+ // because hot reloading has different semantics than prod because
+ // it doesn't resuspend. So we can't let the call below suspend.
+ (typeof elementType === "object" &&
+ elementType !== null &&
+ elementType.$$typeof === REACT_LAZY_TYPE &&
+ resolveLazy(elementType) === child.type)
+ ) {
+ deleteRemainingChildren(returnFiber, child.sibling);
-function log() {
- if (printedGroupIndex < pendingGroupArgs.length - 1) {
- for (var i = printedGroupIndex + 1; i < pendingGroupArgs.length; i++) {
- var groupArgs = pendingGroupArgs[i];
- nativeConsole.group.apply(nativeConsole, groupArgs);
- }
+ var _existing = useFiber(child, element.props);
- printedGroupIndex = pendingGroupArgs.length - 1;
- }
+ _existing.ref = coerceRef(returnFiber, child, element);
+ _existing.return = returnFiber;
- if (typeof nativeConsoleLog === "function") {
- nativeConsoleLog.apply(void 0, arguments);
- } else {
- nativeConsole.log.apply(nativeConsole, arguments);
- }
-}
+ {
+ _existing._debugSource = element._source;
+ _existing._debugOwner = element._owner;
+ }
-var REACT_LOGO_STYLE =
- "background-color: #20232a; color: #61dafb; padding: 0 2px;";
-function logCommitStarted(lanes) {
- {
- if (enableDebugTracing) {
- group(
- "%c\u269B\uFE0F%c commit%c (" + formatLanes(lanes) + ")",
- REACT_LOGO_STYLE,
- "",
- "font-weight: normal;"
- );
- }
- }
-}
-function logCommitStopped() {
- {
- if (enableDebugTracing) {
- groupEnd();
+ return _existing;
+ }
+ } // Didn't match.
+
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ } else {
+ deleteChild(returnFiber, child);
+ }
+
+ child = child.sibling;
}
- }
-}
-var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; // $FlowFixMe: Flow cannot handle polymorphic WeakMaps
-var wakeableIDs = new PossiblyWeakMap$1();
-var wakeableID = 0;
+ if (element.type === REACT_FRAGMENT_TYPE) {
+ var created = createFiberFromFragment(
+ element.props.children,
+ returnFiber.mode,
+ lanes,
+ element.key
+ );
+ created.return = returnFiber;
+ return created;
+ } else {
+ var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
-function getWakeableID(wakeable) {
- if (!wakeableIDs.has(wakeable)) {
- wakeableIDs.set(wakeable, wakeableID++);
+ _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
+ _created4.return = returnFiber;
+ return _created4;
+ }
}
- return wakeableIDs.get(wakeable);
-}
+ function reconcileSinglePortal(
+ returnFiber,
+ currentFirstChild,
+ portal,
+ lanes
+ ) {
+ var key = portal.key;
+ var child = currentFirstChild;
-function logComponentSuspended(componentName, wakeable) {
- {
- if (enableDebugTracing) {
- var id = getWakeableID(wakeable);
- var display = wakeable.displayName || wakeable;
- log(
- "%c\u269B\uFE0F%c " + componentName + " suspended",
- REACT_LOGO_STYLE,
- "color: #80366d; font-weight: bold;",
- id,
- display
- );
- wakeable.then(
- function () {
- log(
- "%c\u269B\uFE0F%c " + componentName + " resolved",
- REACT_LOGO_STYLE,
- "color: #80366d; font-weight: bold;",
- id,
- display
- );
- },
- function () {
- log(
- "%c\u269B\uFE0F%c " + componentName + " rejected",
- REACT_LOGO_STYLE,
- "color: #80366d; font-weight: bold;",
- id,
- display
- );
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (
+ child.tag === HostPortal &&
+ child.stateNode.containerInfo === portal.containerInfo &&
+ child.stateNode.implementation === portal.implementation
+ ) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, portal.children || []);
+ existing.return = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
}
- );
- }
- }
-}
-function logLayoutEffectsStarted(lanes) {
- {
- if (enableDebugTracing) {
- group(
- "%c\u269B\uFE0F%c layout effects%c (" + formatLanes(lanes) + ")",
- REACT_LOGO_STYLE,
- "",
- "font-weight: normal;"
- );
- }
- }
-}
-function logLayoutEffectsStopped() {
- {
- if (enableDebugTracing) {
- groupEnd();
- }
- }
-}
-function logPassiveEffectsStarted(lanes) {
- {
- if (enableDebugTracing) {
- group(
- "%c\u269B\uFE0F%c passive effects%c (" + formatLanes(lanes) + ")",
- REACT_LOGO_STYLE,
- "",
- "font-weight: normal;"
- );
- }
- }
-}
-function logPassiveEffectsStopped() {
- {
- if (enableDebugTracing) {
- groupEnd();
- }
- }
-}
-function logRenderStarted(lanes) {
- {
- if (enableDebugTracing) {
- group(
- "%c\u269B\uFE0F%c render%c (" + formatLanes(lanes) + ")",
- REACT_LOGO_STYLE,
- "",
- "font-weight: normal;"
- );
- }
- }
-}
-function logRenderStopped() {
- {
- if (enableDebugTracing) {
- groupEnd();
- }
- }
-}
-function logForceUpdateScheduled(componentName, lane) {
- {
- if (enableDebugTracing) {
- log(
- "%c\u269B\uFE0F%c " +
- componentName +
- " forced update %c(" +
- formatLanes(lane) +
- ")",
- REACT_LOGO_STYLE,
- "color: #db2e1f; font-weight: bold;",
- ""
- );
- }
- }
-}
-function logStateUpdateScheduled(componentName, lane, payloadOrAction) {
- {
- if (enableDebugTracing) {
- log(
- "%c\u269B\uFE0F%c " +
- componentName +
- " updated state %c(" +
- formatLanes(lane) +
- ")",
- REACT_LOGO_STYLE,
- "color: #01a252; font-weight: bold;",
- "",
- payloadOrAction
- );
+ } else {
+ deleteChild(returnFiber, child);
+ }
+
+ child = child.sibling;
}
- }
-}
-// Intentionally not using it yet to derisk the initial implementation, because
-// the way we push/pop these values is a bit unusual. If there's a mistake, I'd
-// rather the ids be wrong than crash the whole reconciler.
+ var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ } // This API will tag the children with the side-effect of the reconciliation
+ // itself. They will be added to the side-effect list as we pass through the
+ // children and the parent.
-var forkStack = [];
-var forkStackIndex = 0;
-var treeForkProvider = null;
-var treeForkCount = 0;
-var idStack = [];
-var idStackIndex = 0;
-var treeContextProvider = null;
-var treeContextId = 1;
-var treeContextOverflow = "";
-function isForkedChild(workInProgress) {
- warnIfNotHydrating();
- return (workInProgress.flags & Forked) !== NoFlags$1;
-}
-function getForksAtLevel(workInProgress) {
- warnIfNotHydrating();
- return treeForkCount;
-}
-function getTreeId() {
- var overflow = treeContextOverflow;
- var idWithLeadingBit = treeContextId;
- var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit);
- return id.toString(32) + overflow;
-}
-function pushTreeFork(workInProgress, totalChildren) {
- // This is called right after we reconcile an array (or iterator) of child
- // fibers, because that's the only place where we know how many children in
- // the whole set without doing extra work later, or storing addtional
- // information on the fiber.
- //
- // That's why this function is separate from pushTreeId — it's called during
- // the render phase of the fork parent, not the child, which is where we push
- // the other context values.
- //
- // In the Fizz implementation this is much simpler because the child is
- // rendered in the same callstack as the parent.
- //
- // It might be better to just add a `forks` field to the Fiber type. It would
- // make this module simpler.
- warnIfNotHydrating();
- forkStack[forkStackIndex++] = treeForkCount;
- forkStack[forkStackIndex++] = treeForkProvider;
- treeForkProvider = workInProgress;
- treeForkCount = totalChildren;
-}
-function pushTreeId(workInProgress, totalChildren, index) {
- warnIfNotHydrating();
- idStack[idStackIndex++] = treeContextId;
- idStack[idStackIndex++] = treeContextOverflow;
- idStack[idStackIndex++] = treeContextProvider;
- treeContextProvider = workInProgress;
- var baseIdWithLeadingBit = treeContextId;
- var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
- // of the id; we use it to account for leading 0s.
+ function reconcileChildFibersImpl(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ ) {
+ // This function is not recursive.
+ // If the top level item is an array, we treat it as a set of children,
+ // not as a fragment. Nested arrays on the other hand will be treated as
+ // fragment nodes. Recursion happens at the normal flow.
+ // Handle top level unkeyed fragments as if they were arrays.
+ // This leads to an ambiguity between <>{[...]}> and <>...>.
+ // We treat the ambiguous cases above the same.
+ // TODO: Let's use recursion like we do for Usable nodes?
+ var isUnkeyedTopLevelFragment =
+ typeof newChild === "object" &&
+ newChild !== null &&
+ newChild.type === REACT_FRAGMENT_TYPE &&
+ newChild.key === null;
- var baseLength = getBitLength(baseIdWithLeadingBit) - 1;
- var baseId = baseIdWithLeadingBit & ~(1 << baseLength);
- var slot = index + 1;
- var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into
- // consideration the leading 1 we use to mark the end of the sequence.
+ if (isUnkeyedTopLevelFragment) {
+ newChild = newChild.props.children;
+ } // Handle object types
- if (length > 30) {
- // We overflowed the bitwise-safe range. Fall back to slower algorithm.
- // This branch assumes the length of the base id is greater than 5; it won't
- // work for smaller ids, because you need 5 bits per character.
- //
- // We encode the id in multiple steps: first the base id, then the
- // remaining digits.
- //
- // Each 5 bit sequence corresponds to a single base 32 character. So for
- // example, if the current id is 23 bits long, we can convert 20 of those
- // bits into a string of 4 characters, with 3 bits left over.
- //
- // First calculate how many bits in the base id represent a complete
- // sequence of characters.
- var numberOfOverflowBits = baseLength - (baseLength % 5); // Then create a bitmask that selects only those bits.
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ return placeSingleChild(
+ reconcileSingleElement(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ )
+ );
- var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string.
+ case REACT_PORTAL_TYPE:
+ return placeSingleChild(
+ reconcileSinglePortal(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ )
+ );
- var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id.
+ case REACT_LAZY_TYPE:
+ var payload = newChild._payload;
+ var init = newChild._init; // TODO: This function is supposed to be non-recursive.
- var restOfBaseId = baseId >> numberOfOverflowBits;
- var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because
- // we made more room, this time it won't overflow.
+ return reconcileChildFibers(
+ returnFiber,
+ currentFirstChild,
+ init(payload),
+ lanes
+ );
+ }
- var restOfLength = getBitLength(totalChildren) + restOfBaseLength;
- var restOfNewBits = slot << restOfBaseLength;
- var id = restOfNewBits | restOfBaseId;
- var overflow = newOverflow + baseOverflow;
- treeContextId = (1 << restOfLength) | id;
- treeContextOverflow = overflow;
- } else {
- // Normal path
- var newBits = slot << baseLength;
+ if (isArray(newChild)) {
+ return reconcileChildrenArray(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ );
+ }
- var _id = newBits | baseId;
+ if (getIteratorFn(newChild)) {
+ return reconcileChildrenIterator(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ );
+ } // Usables are a valid React node type. When React encounters a Usable in
+ // a child position, it unwraps it using the same algorithm as `use`. For
+ // example, for promises, React will throw an exception to unwind the
+ // stack, then replay the component once the promise resolves.
+ //
+ // A difference from `use` is that React will keep unwrapping the value
+ // until it reaches a non-Usable type.
+ //
+ // e.g. Usable>> should resolve to T
+ //
+ // The structure is a bit unfortunate. Ideally, we shouldn't need to
+ // replay the entire begin phase of the parent fiber in order to reconcile
+ // the children again. This would require a somewhat significant refactor,
+ // because reconcilation happens deep within the begin phase, and
+ // depending on the type of work, not always at the end. We should
+ // consider as an future improvement.
- var _overflow = baseOverflow;
- treeContextId = (1 << length) | _id;
- treeContextOverflow = _overflow;
- }
-}
-function pushMaterializedTreeId(workInProgress) {
- warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear
- // in its children.
+ if (typeof newChild.then === "function") {
+ var thenable = newChild;
+ return reconcileChildFibersImpl(
+ returnFiber,
+ currentFirstChild,
+ unwrapThenable(thenable),
+ lanes
+ );
+ }
- var returnFiber = workInProgress.return;
+ if (
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = newChild;
+ return reconcileChildFibersImpl(
+ returnFiber,
+ currentFirstChild,
+ readContextDuringReconcilation(returnFiber, context, lanes),
+ lanes
+ );
+ }
- if (returnFiber !== null) {
- var numberOfForks = 1;
- var slotIndex = 0;
- pushTreeFork(workInProgress, numberOfForks);
- pushTreeId(workInProgress, numberOfForks, slotIndex);
- }
-}
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
-function getBitLength(number) {
- return 32 - clz32(number);
-}
+ if (
+ (typeof newChild === "string" && newChild !== "") ||
+ typeof newChild === "number"
+ ) {
+ return placeSingleChild(
+ reconcileSingleTextNode(
+ returnFiber,
+ currentFirstChild,
+ "" + newChild,
+ lanes
+ )
+ );
+ }
-function getLeadingBit(id) {
- return 1 << (getBitLength(id) - 1);
-}
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType(returnFiber);
+ }
+ } // Remaining cases are all treated as empty.
-function popTreeContext(workInProgress) {
- // Restore the previous values.
- // This is a bit more complicated than other context-like modules in Fiber
- // because the same Fiber may appear on the stack multiple times and for
- // different reasons. We have to keep popping until the work-in-progress is
- // no longer at the top of the stack.
- while (workInProgress === treeForkProvider) {
- treeForkProvider = forkStack[--forkStackIndex];
- forkStack[forkStackIndex] = null;
- treeForkCount = forkStack[--forkStackIndex];
- forkStack[forkStackIndex] = null;
+ return deleteRemainingChildren(returnFiber, currentFirstChild);
}
- while (workInProgress === treeContextProvider) {
- treeContextProvider = idStack[--idStackIndex];
- idStack[idStackIndex] = null;
- treeContextOverflow = idStack[--idStackIndex];
- idStack[idStackIndex] = null;
- treeContextId = idStack[--idStackIndex];
- idStack[idStackIndex] = null;
- }
-}
-function getSuspendedTreeContext() {
- warnIfNotHydrating();
+ function reconcileChildFibers(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ ) {
+ // This indirection only exists so we can reset `thenableState` at the end.
+ // It should get inlined by Closure.
+ thenableIndexCounter$1 = 0;
+ var firstChildFiber = reconcileChildFibersImpl(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ lanes
+ );
+ thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets
+ // set at the beginning.
- if (treeContextProvider !== null) {
- return {
- id: treeContextId,
- overflow: treeContextOverflow
- };
- } else {
- return null;
+ return firstChildFiber;
}
-}
-function restoreSuspendedTreeContext(workInProgress, suspendedContext) {
- warnIfNotHydrating();
- idStack[idStackIndex++] = treeContextId;
- idStack[idStackIndex++] = treeContextOverflow;
- idStack[idStackIndex++] = treeContextProvider;
- treeContextId = suspendedContext.id;
- treeContextOverflow = suspendedContext.overflow;
- treeContextProvider = workInProgress;
+
+ return reconcileChildFibers;
}
-function warnIfNotHydrating() {
- {
- if (!getIsHydrating()) {
- error(
- "Expected to be hydrating. This is a bug in React. Please file " +
- "an issue."
- );
- }
- }
+var reconcileChildFibers = createChildReconciler(true);
+var mountChildFibers = createChildReconciler(false);
+function resetChildReconcilerOnUnwind() {
+ // On unwind, clear any pending thenables that were used.
+ thenableState$1 = null;
+ thenableIndexCounter$1 = 0;
}
+function cloneChildFibers(current, workInProgress) {
+ if (current !== null && workInProgress.child !== current.child) {
+ throw new Error("Resuming work not yet implemented.");
+ }
-// This may have been an insertion or a hydration.
+ if (workInProgress.child === null) {
+ return;
+ }
-var hydrationParentFiber = null;
-var nextHydratableInstance = null;
-var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
-// due to earlier mismatches or a suspended fiber.
+ var currentChild = workInProgress.child;
+ var newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
+ workInProgress.child = newChild;
+ newChild.return = workInProgress;
-var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary
+ while (currentChild.sibling !== null) {
+ currentChild = currentChild.sibling;
+ newChild = newChild.sibling = createWorkInProgress(
+ currentChild,
+ currentChild.pendingProps
+ );
+ newChild.return = workInProgress;
+ }
-var hydrationErrors = null;
-var rootOrSingletonContext = false;
+ newChild.sibling = null;
+} // Reset a workInProgress child set to prepare it for a second pass.
-function warnIfHydrating() {
- {
- if (isHydrating) {
- error(
- "We should not be hydrating here. This is a bug in React. Please file a bug."
- );
- }
- }
-}
+function resetChildFibers(workInProgress, lanes) {
+ var child = workInProgress.child;
-function markDidThrowWhileHydratingDEV() {
- {
- didSuspendOrErrorDEV = true;
- }
-}
-function didSuspendOrErrorWhileHydratingDEV() {
- {
- return didSuspendOrErrorDEV;
+ while (child !== null) {
+ resetWorkInProgress(child, lanes);
+ child = child.sibling;
}
}
-function enterHydrationState(fiber) {
- var parentInstance = fiber.stateNode.containerInfo;
- nextHydratableInstance =
- getFirstHydratableChildWithinContainer(parentInstance);
- hydrationParentFiber = fiber;
- isHydrating = true;
- hydrationErrors = null;
- didSuspendOrErrorDEV = false;
- rootOrSingletonContext = true;
- return true;
-}
+// TODO: This isn't being used yet, but it's intended to replace the
+// InvisibleParentContext that is currently managed by SuspenseContext.
-function reenterHydrationStateFromDehydratedSuspenseInstance(
- fiber,
- suspenseInstance,
- treeContext
-) {
- nextHydratableInstance =
- getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
- hydrationParentFiber = fiber;
- isHydrating = true;
- hydrationErrors = null;
- didSuspendOrErrorDEV = false;
- rootOrSingletonContext = false;
+var currentTreeHiddenStackCursor = createCursor(null);
+var prevRenderLanesStackCursor = createCursor(NoLanes);
+function pushHiddenContext(fiber, context) {
+ var prevRenderLanes = getRenderLanes();
+ push(prevRenderLanesStackCursor, prevRenderLanes, fiber);
+ push(currentTreeHiddenStackCursor, context, fiber); // When rendering a subtree that's currently hidden, we must include all
+ // lanes that would have rendered if the hidden subtree hadn't been deferred.
+ // That is, in order to reveal content from hidden -> visible, we must commit
+ // all the updates that we skipped when we originally hid the tree.
- if (treeContext !== null) {
- restoreSuspendedTreeContext(fiber, treeContext);
- }
+ setRenderLanes(mergeLanes(prevRenderLanes, context.baseLanes));
+}
+function reuseHiddenContextOnStack(fiber) {
+ // This subtree is not currently hidden, so we don't need to add any lanes
+ // to the render lanes. But we still need to push something to avoid a
+ // context mismatch. Reuse the existing context on the stack.
+ push(prevRenderLanesStackCursor, getRenderLanes(), fiber);
+ push(
+ currentTreeHiddenStackCursor,
+ currentTreeHiddenStackCursor.current,
+ fiber
+ );
+}
+function popHiddenContext(fiber) {
+ // Restore the previous render lanes from the stack
+ setRenderLanes(prevRenderLanesStackCursor.current);
+ pop(currentTreeHiddenStackCursor, fiber);
+ pop(prevRenderLanesStackCursor, fiber);
+}
+function isCurrentTreeHidden() {
+ return currentTreeHiddenStackCursor.current !== null;
+}
- return true;
+// suspends, i.e. it's the nearest `catch` block on the stack.
+
+var suspenseHandlerStackCursor = createCursor(null); // Represents the outermost boundary that is not visible in the current tree.
+// Everything above this is the "shell". When this is null, it means we're
+// rendering in the shell of the app. If it's non-null, it means we're rendering
+// deeper than the shell, inside a new tree that wasn't already visible.
+//
+// The main way we use this concept is to determine whether showing a fallback
+// would result in a desirable or undesirable loading state. Activing a fallback
+// in the shell is considered an undersirable loading state, because it would
+// mean hiding visible (albeit stale) content in the current tree — we prefer to
+// show the stale content, rather than switch to a fallback. But showing a
+// fallback in a new tree is fine, because there's no stale content to
+// prefer instead.
+
+var shellBoundary = null;
+function getShellBoundary() {
+ return shellBoundary;
}
+function pushPrimaryTreeSuspenseHandler(handler) {
+ // TODO: Pass as argument
+ var current = handler.alternate;
+ var props = handler.pendingProps; // Experimental feature: Some Suspense boundaries are marked as having an
+ // undesirable fallback state. These have special behavior where we only
+ // activate the fallback if there's no other boundary on the stack that we can
+ // use instead.
-function warnUnhydratedInstance(returnFiber, instance) {
- {
- switch (returnFiber.tag) {
- case HostRoot: {
- didNotHydrateInstanceWithinContainer(
- returnFiber.stateNode.containerInfo,
- instance
- );
- break;
- }
+ if (
+ props.unstable_avoidThisFallback === true && // If an avoided boundary is already visible, it behaves identically to
+ // a regular Suspense boundary.
+ (current === null || isCurrentTreeHidden())
+ ) {
+ if (shellBoundary === null) {
+ // We're rendering in the shell. There's no parent Suspense boundary that
+ // can provide a desirable fallback state. We'll use this boundary.
+ push(suspenseHandlerStackCursor, handler, handler); // However, because this is not a desirable fallback, the children are
+ // still considered part of the shell. So we intentionally don't assign
+ // to `shellBoundary`.
+ } else {
+ // There's already a parent Suspense boundary that can provide a desirable
+ // fallback state. Prefer that one.
+ var handlerOnStack = suspenseHandlerStackCursor.current;
+ push(suspenseHandlerStackCursor, handlerOnStack, handler);
+ }
- case HostSingleton:
- case HostComponent: {
- var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
- didNotHydrateInstance(
- returnFiber.type,
- returnFiber.memoizedProps,
- returnFiber.stateNode,
- instance, // TODO: Delete this argument when we remove the legacy root API.
- isConcurrentMode
- );
- break;
- }
+ return;
+ } // TODO: If the parent Suspense handler already suspended, there's no reason
+ // to push a nested Suspense handler, because it will get replaced by the
+ // outer fallback, anyway. Consider this as a future optimization.
- case SuspenseComponent: {
- var suspenseState = returnFiber.memoizedState;
- if (suspenseState.dehydrated !== null)
- didNotHydrateInstanceWithinSuspenseInstance(
- suspenseState.dehydrated,
- instance
- );
- break;
+ push(suspenseHandlerStackCursor, handler, handler);
+
+ if (shellBoundary === null) {
+ if (current === null || isCurrentTreeHidden()) {
+ // This boundary is not visible in the current UI.
+ shellBoundary = handler;
+ } else {
+ var prevState = current.memoizedState;
+
+ if (prevState !== null) {
+ // This boundary is showing a fallback in the current UI.
+ shellBoundary = handler;
}
}
}
}
+function pushFallbackTreeSuspenseHandler(fiber) {
+ // We're about to render the fallback. If something in the fallback suspends,
+ // it's akin to throwing inside of a `catch` block. This boundary should not
+ // capture. Reuse the existing handler on the stack.
+ reuseSuspenseHandlerOnStack(fiber);
+}
+function pushOffscreenSuspenseHandler(fiber) {
+ if (fiber.tag === OffscreenComponent) {
+ push(suspenseHandlerStackCursor, fiber, fiber);
-function deleteHydratableInstance(returnFiber, instance) {
- warnUnhydratedInstance(returnFiber, instance);
- var childToDelete = createFiberFromHostInstanceForDeletion();
- childToDelete.stateNode = instance;
- childToDelete.return = returnFiber;
- var deletions = returnFiber.deletions;
+ if (shellBoundary !== null);
+ else {
+ var current = fiber.alternate;
- if (deletions === null) {
- returnFiber.deletions = [childToDelete];
- returnFiber.flags |= ChildDeletion;
+ if (current !== null) {
+ var prevState = current.memoizedState;
+
+ if (prevState !== null) {
+ // This is the first boundary in the stack that's already showing
+ // a fallback. So everything outside is considered the shell.
+ shellBoundary = fiber;
+ }
+ }
+ }
} else {
- deletions.push(childToDelete);
+ // This is a LegacyHidden component.
+ reuseSuspenseHandlerOnStack(fiber);
}
}
+function reuseSuspenseHandlerOnStack(fiber) {
+ push(suspenseHandlerStackCursor, getSuspenseHandler(), fiber);
+}
+function getSuspenseHandler() {
+ return suspenseHandlerStackCursor.current;
+}
+function popSuspenseHandler(fiber) {
+ pop(suspenseHandlerStackCursor, fiber);
-function warnNonhydratedInstance(returnFiber, fiber) {
- {
- if (didSuspendOrErrorDEV) {
- // Inside a boundary that already suspended. We're currently rendering the
- // siblings of a suspended node. The mismatch may be due to the missing
- // data, so it's probably a false positive.
- return;
- }
-
- switch (returnFiber.tag) {
- case HostRoot: {
- var parentContainer = returnFiber.stateNode.containerInfo;
-
- switch (fiber.tag) {
- case HostSingleton:
- case HostComponent:
- var type = fiber.type;
- didNotFindHydratableInstanceWithinContainer(parentContainer, type);
- break;
+ if (shellBoundary === fiber) {
+ // Popping back into the shell.
+ shellBoundary = null;
+ }
+} // SuspenseList context
+// TODO: Move to a separate module? We may change the SuspenseList
+// implementation to hide/show in the commit phase, anyway.
- case HostText:
- var text = fiber.pendingProps;
- didNotFindHydratableTextInstanceWithinContainer(
- parentContainer,
- text
- );
- break;
- }
+var DefaultSuspenseContext = 0;
+var SubtreeSuspenseContextMask = 1; // ForceSuspenseFallback can be used by SuspenseList to force newly added
+// items into their fallback state during one of the render passes.
- break;
- }
+var ForceSuspenseFallback = 2;
+var suspenseStackCursor = createCursor(DefaultSuspenseContext);
+function hasSuspenseListContext(parentContext, flag) {
+ return (parentContext & flag) !== 0;
+}
+function setDefaultShallowSuspenseListContext(parentContext) {
+ return parentContext & SubtreeSuspenseContextMask;
+}
+function setShallowSuspenseListContext(parentContext, shallowContext) {
+ return (parentContext & SubtreeSuspenseContextMask) | shallowContext;
+}
+function pushSuspenseListContext(fiber, newContext) {
+ push(suspenseStackCursor, newContext, fiber);
+}
+function popSuspenseListContext(fiber) {
+ pop(suspenseStackCursor, fiber);
+}
- case HostSingleton:
- case HostComponent: {
- var parentType = returnFiber.type;
- var parentProps = returnFiber.memoizedProps;
- var parentInstance = returnFiber.stateNode;
+// A non-null SuspenseState means that it is blocked for one reason or another.
+// - A non-null dehydrated field means it's blocked pending hydration.
+// - A non-null dehydrated field can use isSuspenseInstancePending or
+// isSuspenseInstanceFallback to query the reason for being dehydrated.
+// - A null dehydrated field means it's blocked by something suspending and
+// we're currently showing a fallback instead.
- switch (fiber.tag) {
- case HostSingleton:
- case HostComponent: {
- var _type = fiber.type;
- var _props = fiber.pendingProps;
- var isConcurrentMode =
- (returnFiber.mode & ConcurrentMode) !== NoMode;
- didNotFindHydratableInstance(
- parentType,
- parentProps,
- parentInstance,
- _type,
- _props, // TODO: Delete this argument when we remove the legacy root API.
- isConcurrentMode
- );
- break;
- }
+function findFirstSuspended(row) {
+ var node = row;
- case HostText: {
- var _text = fiber.pendingProps;
+ while (node !== null) {
+ if (node.tag === SuspenseComponent) {
+ var state = node.memoizedState;
- var _isConcurrentMode =
- (returnFiber.mode & ConcurrentMode) !== NoMode;
+ if (state !== null) {
+ var dehydrated = state.dehydrated;
- didNotFindHydratableTextInstance(
- parentType,
- parentProps,
- parentInstance,
- _text, // TODO: Delete this argument when we remove the legacy root API.
- _isConcurrentMode
- );
- break;
- }
+ if (
+ dehydrated === null ||
+ isSuspenseInstancePending(dehydrated) ||
+ isSuspenseInstanceFallback(dehydrated)
+ ) {
+ return node;
}
-
- break;
}
+ } else if (
+ node.tag === SuspenseListComponent && // revealOrder undefined can't be trusted because it don't
+ // keep track of whether it suspended or not.
+ node.memoizedProps.revealOrder !== undefined
+ ) {
+ var didSuspend = (node.flags & DidCapture) !== NoFlags$1;
- case SuspenseComponent: {
- var suspenseState = returnFiber.memoizedState;
- var _parentInstance = suspenseState.dehydrated;
- if (_parentInstance !== null)
- switch (fiber.tag) {
- case HostSingleton:
- case HostComponent:
- var _type2 = fiber.type;
- didNotFindHydratableInstanceWithinSuspenseInstance(
- _parentInstance,
- _type2
- );
- break;
-
- case HostText:
- var _text2 = fiber.pendingProps;
- didNotFindHydratableTextInstanceWithinSuspenseInstance(
- _parentInstance,
- _text2
- );
- break;
- }
- break;
+ if (didSuspend) {
+ return node;
}
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
- default:
- return;
+ if (node === row) {
+ return null;
}
- }
-}
-function insertNonHydratedInstance(returnFiber, fiber) {
- fiber.flags = (fiber.flags & ~Hydrating) | Placement;
- warnNonhydratedInstance(returnFiber, fiber);
-}
+ while (node.sibling === null) {
+ if (node.return === null || node.return === row) {
+ return null;
+ }
-function tryHydrateInstance(fiber, nextInstance) {
- // fiber is a HostComponent Fiber
- var instance = canHydrateInstance(nextInstance, fiber.type);
+ node = node.return;
+ }
- if (instance !== null) {
- fiber.stateNode = instance;
- hydrationParentFiber = fiber;
- nextHydratableInstance = getFirstHydratableChild(instance);
- rootOrSingletonContext = false;
- return true;
+ node.sibling.return = node.return;
+ node = node.sibling;
}
- return false;
+ return null;
}
-function tryHydrateText(fiber, nextInstance) {
- // fiber is a HostText Fiber
- var text = fiber.pendingProps;
- var textInstance = canHydrateTextInstance(nextInstance, text);
+var NoFlags =
+ /* */
+ 0; // Represents whether effect should fire.
- if (textInstance !== null) {
- fiber.stateNode = textInstance;
- hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate.
+var HasEffect =
+ /* */
+ 1; // Represents the phase in which the effect (not the clean-up) fires.
- nextHydratableInstance = null;
- return true;
- }
+var Insertion =
+ /* */
+ 2;
+var Layout =
+ /* */
+ 4;
+var Passive =
+ /* */
+ 8;
- return false;
-}
+// and should be reset before starting a new render.
+// This tracks which mutable sources need to be reset after a render.
-function tryHydrateSuspense(fiber, nextInstance) {
- // fiber is a SuspenseComponent Fiber
- var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
+var workInProgressSources = [];
+var rendererSigil$1;
- if (suspenseInstance !== null) {
- var suspenseState = {
- dehydrated: suspenseInstance,
- treeContext: getSuspendedTreeContext(),
- retryLane: OffscreenLane
- };
- fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber.
- // This simplifies the code for getHostSibling and deleting nodes,
- // since it doesn't have to consider all Suspense boundaries and
- // check if they're dehydrated ones or not.
+{
+ // Used to detect multiple renderers using the same mutable source.
+ rendererSigil$1 = {};
+}
- var dehydratedFragment =
- createFiberFromDehydratedFragment(suspenseInstance);
- dehydratedFragment.return = fiber;
- fiber.child = dehydratedFragment;
- hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into
- // it during the first pass. Instead, we'll reenter it later.
+function markSourceAsDirty(mutableSource) {
+ workInProgressSources.push(mutableSource);
+}
+function resetWorkInProgressVersions() {
+ for (var i = 0; i < workInProgressSources.length; i++) {
+ var mutableSource = workInProgressSources[i];
- nextHydratableInstance = null;
- return true;
+ {
+ mutableSource._workInProgressVersionPrimary = null;
+ }
}
- return false;
+ workInProgressSources.length = 0;
}
-
-function shouldClientRenderOnMismatch(fiber) {
- return (
- (fiber.mode & ConcurrentMode) !== NoMode &&
- (fiber.flags & DidCapture) === NoFlags$1
- );
+function getWorkInProgressVersion(mutableSource) {
+ {
+ return mutableSource._workInProgressVersionPrimary;
+ }
}
+function setWorkInProgressVersion(mutableSource, version) {
+ {
+ mutableSource._workInProgressVersionPrimary = version;
+ }
-function throwOnHydrationMismatch(fiber) {
- throw new Error(
- "Hydration failed because the initial UI does not match what was " +
- "rendered on the server."
- );
+ workInProgressSources.push(mutableSource);
}
-
-function claimHydratableSingleton(fiber) {
+function warnAboutMultipleRenderersDEV(mutableSource) {
{
- if (!isHydrating) {
- return;
+ {
+ if (mutableSource._currentPrimaryRenderer == null) {
+ mutableSource._currentPrimaryRenderer = rendererSigil$1;
+ } else if (mutableSource._currentPrimaryRenderer !== rendererSigil$1) {
+ error(
+ "Detected multiple renderers concurrently rendering the " +
+ "same mutable source. This is currently unsupported."
+ );
+ }
}
-
- var currentRootContainer = getRootHostContainer();
- var currentHostContext = getHostContext();
- var instance = (fiber.stateNode = resolveSingletonInstance(
- fiber.type,
- fiber.pendingProps,
- currentRootContainer,
- currentHostContext,
- false
- ));
- hydrationParentFiber = fiber;
- rootOrSingletonContext = true;
- nextHydratableInstance = getFirstHydratableChild(instance);
- }
-}
-
-function advanceToFirstAttemptableInstance(fiber) {
- // fiber is HostComponent Fiber
- while (
- nextHydratableInstance &&
- shouldSkipHydratableForInstance(
- nextHydratableInstance,
- fiber.type,
- fiber.pendingProps
- )
- ) {
- // Flow doesn't understand that inside this block nextHydratableInstance is not null
- var instance = nextHydratableInstance;
- nextHydratableInstance = getNextHydratableSibling(instance);
}
-}
+} // Eager reads the version of a mutable source and stores it on the root.
+// This ensures that the version used for server rendering matches the one
+// that is eventually read during hydration.
+// If they don't match there's a potential tear and a full deopt render is required.
-function advanceToFirstAttemptableTextInstance() {
- while (
- nextHydratableInstance &&
- shouldSkipHydratableForTextInstance(nextHydratableInstance)
- ) {
- // Flow doesn't understand that inside this block nextHydratableInstance is not null
- var instance = nextHydratableInstance;
- nextHydratableInstance = getNextHydratableSibling(instance);
- }
-}
+function registerMutableSourceForHydration(root, mutableSource) {
+ var getVersion = mutableSource._getVersion;
+ var version = getVersion(mutableSource._source); // TODO Clear this data once all pending hydration work is finished.
+ // Retaining it forever may interfere with GC.
-function advanceToFirstAttemptableSuspenseInstance() {
- while (
- nextHydratableInstance &&
- shouldSkipHydratableForSuspenseInstance(nextHydratableInstance)
- ) {
- // Flow doesn't understand that inside this block nextHydratableInstance is not null
- var instance = nextHydratableInstance;
- nextHydratableInstance = getNextHydratableSibling(instance);
+ if (root.mutableSourceEagerHydrationData == null) {
+ root.mutableSourceEagerHydrationData = [mutableSource, version];
+ } else {
+ root.mutableSourceEagerHydrationData.push(mutableSource, version);
}
}
-function tryToClaimNextHydratableInstance(fiber) {
- if (!isHydrating) {
- return;
- }
-
- {
- if (!isHydratableType(fiber.type, fiber.pendingProps)) {
- // This fiber never hydrates from the DOM and always does an insert
- fiber.flags = (fiber.flags & ~Hydrating) | Placement;
- isHydrating = false;
- hydrationParentFiber = fiber;
- return;
- }
- }
-
- var initialInstance = nextHydratableInstance;
-
- if (rootOrSingletonContext) {
- // We may need to skip past certain nodes in these contexts
- advanceToFirstAttemptableInstance(fiber);
- }
-
- var nextInstance = nextHydratableInstance;
-
- if (!nextInstance) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // Nothing to hydrate. Make it an insertion.
-
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- return;
- }
-
- var firstAttemptedInstance = nextInstance;
-
- if (!tryHydrateInstance(fiber, nextInstance)) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // If we can't hydrate this instance let's try the next one.
- // We use this as a heuristic. It's based on intuition and not data so it
- // might be flawed or unnecessary.
+var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher,
+ ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig;
+var didWarnAboutMismatchedHooksForComponent;
+var didWarnUncachedGetSnapshot;
+var didWarnAboutUseWrappedInTryCatch;
- nextHydratableInstance = getNextHydratableSibling(nextInstance);
- var prevHydrationParentFiber = hydrationParentFiber;
+{
+ didWarnAboutMismatchedHooksForComponent = new Set();
+ didWarnAboutUseWrappedInTryCatch = new Set();
+} // These are set right before calling the component.
- if (rootOrSingletonContext) {
- // We may need to skip past certain nodes in these contexts
- advanceToFirstAttemptableInstance(fiber);
- }
+var renderLanes$1 = NoLanes; // The work-in-progress fiber. I've named it differently to distinguish it from
+// the work-in-progress hook.
- if (
- !nextHydratableInstance ||
- !tryHydrateInstance(fiber, nextHydratableInstance)
- ) {
- // Nothing to hydrate. Make it an insertion.
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- 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.
+var currentlyRenderingFiber$1 = null; // Hooks are stored as a linked list on the fiber's memoizedState field. The
+// current hook list is the list that belongs to the current fiber. The
+// work-in-progress hook list is a new list that will be added to the
+// work-in-progress fiber.
- deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
- }
-}
+var currentHook = null;
+var workInProgressHook = null; // Whether an update was scheduled at any point during the render phase. This
+// does not get reset if we do another render pass; only when we're completely
+// finished evaluating this component. This is an optimization so we know
+// whether we need to clear render phase updates after a throw.
-function tryToClaimNextHydratableTextInstance(fiber) {
- if (!isHydrating) {
- return;
- }
+var didScheduleRenderPhaseUpdate = false; // Where an update was scheduled only during the current render pass. This
+// gets reset after each attempt.
+// TODO: Maybe there's some way to consolidate this with
+// `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`.
- var text = fiber.pendingProps;
- var isHydratable = isHydratableText(text);
- var initialInstance = nextHydratableInstance;
+var didScheduleRenderPhaseUpdateDuringThisPass = false;
+var shouldDoubleInvokeUserFnsInHooksDEV = false; // Counts the number of useId hooks in this component.
- if (rootOrSingletonContext && isHydratable) {
- // We may need to skip past certain nodes in these contexts.
- // We don't skip if the text is not hydratable because we know no hydratables
- // exist which could match this Fiber
- advanceToFirstAttemptableTextInstance();
- }
+var localIdCounter = 0; // Counts number of `use`-d thenables
- var nextInstance = nextHydratableInstance;
+var thenableIndexCounter = 0;
+var thenableState = null; // Used for ids that are generated completely client-side (i.e. not during
+// hydration). This counter is global, so client ids are not stable across
+// render attempts.
- if (!nextInstance || !isHydratable) {
- // We exclude non hydrabable text because we know there are no matching hydratables.
- // We either throw or insert depending on the render mode.
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // Nothing to hydrate. Make it an insertion.
+var globalClientIdCounter = 0;
+var RE_RENDER_LIMIT = 25; // In DEV, this is the name of the currently executing primitive hook
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- return;
- }
+var currentHookNameInDev = null; // In DEV, this list ensures that hooks are called in the same order between renders.
+// The list stores the order of hooks used during the initial render (mount).
+// Subsequent renders (updates) reference this list.
- var firstAttemptedInstance = nextInstance;
+var hookTypesDev = null;
+var hookTypesUpdateIndexDev = -1; // In DEV, this tracks whether currently rendering component needs to ignore
+// the dependencies for Hooks that need them (e.g. useEffect or useMemo).
+// When true, such Hooks will always be "remounted". Only used during hot reload.
- if (!tryHydrateText(fiber, nextInstance)) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // If we can't hydrate this instance let's try the next one.
- // We use this as a heuristic. It's based on intuition and not data so it
- // might be flawed or unnecessary.
+var ignorePreviousDependencies = false;
- nextHydratableInstance = getNextHydratableSibling(nextInstance);
- var prevHydrationParentFiber = hydrationParentFiber;
+function mountHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
- if (rootOrSingletonContext && isHydratable) {
- // We may need to skip past certain nodes in these contexts
- advanceToFirstAttemptableTextInstance();
+ if (hookTypesDev === null) {
+ hookTypesDev = [hookName];
+ } else {
+ hookTypesDev.push(hookName);
}
-
- if (
- !nextHydratableInstance ||
- !tryHydrateText(fiber, nextHydratableInstance)
- ) {
- // Nothing to hydrate. Make it an insertion.
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- return;
- } // We matched the next one, we'll now assume that the first one was
- // superfluous and we'll delete it. Since we can't eagerly delete it
- // we'll have to schedule a deletion. To do that, this node needs a dummy
- // fiber associated with it.
-
- deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
}
-function tryToClaimNextHydratableSuspenseInstance(fiber) {
- if (!isHydrating) {
- return;
- }
-
- var initialInstance = nextHydratableInstance;
-
- if (rootOrSingletonContext) {
- // We may need to skip past certain nodes in these contexts
- advanceToFirstAttemptableSuspenseInstance();
- }
-
- var nextInstance = nextHydratableInstance;
-
- if (!nextInstance) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // Nothing to hydrate. Make it an insertion.
-
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- return;
- }
-
- var firstAttemptedInstance = nextInstance;
-
- if (!tryHydrateSuspense(fiber, nextInstance)) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnNonhydratedInstance(hydrationParentFiber, fiber);
- throwOnHydrationMismatch();
- } // If we can't hydrate this instance let's try the next one.
- // We use this as a heuristic. It's based on intuition and not data so it
- // might be flawed or unnecessary.
+function updateHookTypesDev() {
+ {
+ var hookName = currentHookNameInDev;
- nextHydratableInstance = getNextHydratableSibling(nextInstance);
- var prevHydrationParentFiber = hydrationParentFiber;
+ if (hookTypesDev !== null) {
+ hookTypesUpdateIndexDev++;
- if (rootOrSingletonContext) {
- // We may need to skip past certain nodes in these contexts
- advanceToFirstAttemptableSuspenseInstance();
+ if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
+ warnOnHookMismatchInDev(hookName);
+ }
}
-
- if (
- !nextHydratableInstance ||
- !tryHydrateSuspense(fiber, nextHydratableInstance)
- ) {
- // Nothing to hydrate. Make it an insertion.
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- nextHydratableInstance = initialInstance;
- return;
- } // We matched the next one, we'll now assume that the first one was
- // superfluous and we'll delete it. Since we can't eagerly delete it
- // we'll have to schedule a deletion. To do that, this node needs a dummy
- // fiber associated with it.
-
- deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
}
-function prepareToHydrateHostInstance(fiber, hostContext) {
- var instance = fiber.stateNode;
- var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
- var updatePayload = hydrateInstance(
- instance,
- fiber.type,
- fiber.memoizedProps,
- hostContext,
- fiber,
- shouldWarnIfMismatchDev
- ); // TODO: Type this specific to this type of component.
-
- fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
- // is a new ref we mark this as an update.
-
- if (updatePayload !== null) {
- return true;
+function checkDepsAreArrayDev(deps) {
+ {
+ if (deps !== undefined && deps !== null && !isArray(deps)) {
+ // Verify deps, but only on mount to avoid extra checks.
+ // It's unlikely their type would change as usually you define them inline.
+ error(
+ "%s received a final argument that is not an array (instead, received `%s`). When " +
+ "specified, the final argument must be an array.",
+ currentHookNameInDev,
+ typeof deps
+ );
+ }
}
-
- return false;
}
-function prepareToHydrateHostTextInstance(fiber) {
- var textInstance = fiber.stateNode;
- var textContent = fiber.memoizedProps;
- var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
- var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
+function warnOnHookMismatchInDev(currentHookName) {
+ {
+ var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1);
- 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 (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
+ didWarnAboutMismatchedHooksForComponent.add(componentName);
- if (returnFiber !== null) {
- switch (returnFiber.tag) {
- case HostRoot: {
- var parentContainer = returnFiber.stateNode.containerInfo;
- var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
- didNotMatchHydratedContainerTextInstance(
- parentContainer,
- textInstance,
- textContent, // TODO: Delete this argument when we remove the legacy root API.
- isConcurrentMode,
- shouldWarnIfMismatchDev
- );
- break;
- }
+ if (hookTypesDev !== null) {
+ var table = "";
+ var secondColumnStart = 30;
- case HostSingleton:
- case HostComponent: {
- var parentType = returnFiber.type;
- var parentProps = returnFiber.memoizedProps;
- var parentInstance = returnFiber.stateNode;
+ for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
+ var oldHookName = hookTypesDev[i];
+ var newHookName =
+ i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
+ var row = i + 1 + ". " + oldHookName; // Extra space so second column lines up
+ // lol @ IE not supporting String#repeat
- var _isConcurrentMode2 =
- (returnFiber.mode & ConcurrentMode) !== NoMode;
+ while (row.length < secondColumnStart) {
+ row += " ";
+ }
- didNotMatchHydratedTextInstance(
- parentType,
- parentProps,
- parentInstance,
- textInstance,
- textContent, // TODO: Delete this argument when we remove the legacy root API.
- _isConcurrentMode2,
- shouldWarnIfMismatchDev
- );
- break;
+ row += newHookName + "\n";
+ table += row;
}
+
+ error(
+ "React has detected a change in the order of Hooks called by %s. " +
+ "This will lead to bugs and errors if not fixed. " +
+ "For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n" +
+ " Previous render Next render\n" +
+ " ------------------------------------------------------\n" +
+ "%s" +
+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
+ componentName,
+ table
+ );
}
}
}
-
- return shouldUpdate;
-}
-
-function prepareToHydrateHostSuspenseInstance(fiber) {
- var suspenseState = fiber.memoizedState;
- var suspenseInstance =
- suspenseState !== null ? suspenseState.dehydrated : null;
-
- if (!suspenseInstance) {
- throw new Error(
- "Expected to have a hydrated suspense instance. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- }
-
- hydrateSuspenseInstance(suspenseInstance, fiber);
}
-function skipPastDehydratedSuspenseInstance(fiber) {
- var suspenseState = fiber.memoizedState;
- var suspenseInstance =
- suspenseState !== null ? suspenseState.dehydrated : null;
-
- if (!suspenseInstance) {
- throw new Error(
- "Expected to have a hydrated suspense instance. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- }
-
- return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
+function throwInvalidHookError() {
+ throw new Error(
+ "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" +
+ " one of the following reasons:\n" +
+ "1. You might have mismatching versions of React and the renderer (such as React DOM)\n" +
+ "2. You might be breaking the Rules of Hooks\n" +
+ "3. You might have more than one copy of React in the same app\n" +
+ "See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."
+ );
}
-function popToNextHostParent(fiber) {
- hydrationParentFiber = fiber.return;
-
- while (hydrationParentFiber) {
- switch (hydrationParentFiber.tag) {
- case HostRoot:
- case HostSingleton:
- rootOrSingletonContext = true;
- return;
-
- case HostComponent:
- case SuspenseComponent:
- rootOrSingletonContext = false;
- return;
-
- default:
- hydrationParentFiber = hydrationParentFiber.return;
+function areHookInputsEqual(nextDeps, prevDeps) {
+ {
+ if (ignorePreviousDependencies) {
+ // Only true when this component is being hot reloaded.
+ return false;
}
}
-}
-function popHydrationState(fiber) {
- if (fiber !== hydrationParentFiber) {
- // We're deeper than the current hydration context, inside an inserted
- // tree.
- return false;
- }
+ if (prevDeps === null) {
+ {
+ error(
+ "%s received a final argument during this render, but not during " +
+ "the previous render. Even though the final argument is optional, " +
+ "its type cannot change between renders.",
+ currentHookNameInDev
+ );
+ }
- 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 shouldClear = false;
-
{
- // With float we never clear the Root, or Singleton instances. We also do not clear Instances
- // that have singleton text content
- if (
- fiber.tag !== HostRoot &&
- fiber.tag !== HostSingleton &&
- !(
- fiber.tag === HostComponent &&
- shouldSetTextContent(fiber.type, fiber.memoizedProps)
- )
- ) {
- shouldClear = true;
+ // Don't bother comparing lengths in prod because these arrays should be
+ // passed inline.
+ if (nextDeps.length !== prevDeps.length) {
+ error(
+ "The final argument passed to %s changed size between renders. The " +
+ "order and size of this array must remain constant.\n\n" +
+ "Previous: %s\n" +
+ "Incoming: %s",
+ currentHookNameInDev,
+ "[" + prevDeps.join(", ") + "]",
+ "[" + nextDeps.join(", ") + "]"
+ );
}
- }
-
- if (shouldClear) {
- var nextInstance = nextHydratableInstance;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (nextInstance) {
- if (shouldClientRenderOnMismatch(fiber)) {
- warnIfUnhydratedTailNodes(fiber);
- throwOnHydrationMismatch();
- } else {
- while (nextInstance) {
- deleteHydratableInstance(fiber, nextInstance);
- nextInstance = getNextHydratableSibling(nextInstance);
- }
- }
+ for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (objectIs(nextDeps[i], prevDeps[i])) {
+ continue;
}
- }
- popToNextHostParent(fiber);
-
- if (fiber.tag === SuspenseComponent) {
- nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
- } else {
- nextHydratableInstance = hydrationParentFiber
- ? getNextHydratableSibling(fiber.stateNode)
- : null;
+ return false;
}
return true;
}
-function hasUnhydratedTailNodes() {
- return isHydrating && nextHydratableInstance !== null;
-}
+function renderWithHooks(
+ current,
+ workInProgress,
+ Component,
+ props,
+ secondArg,
+ nextRenderLanes
+) {
+ renderLanes$1 = nextRenderLanes;
+ currentlyRenderingFiber$1 = workInProgress;
-function warnIfUnhydratedTailNodes(fiber) {
- var nextInstance = nextHydratableInstance;
+ {
+ hookTypesDev = current !== null ? current._debugHookTypes : null;
+ hookTypesUpdateIndexDev = -1; // Used for hot reloading:
- while (nextInstance) {
- warnUnhydratedInstance(fiber, nextInstance);
- nextInstance = getNextHydratableSibling(nextInstance);
+ ignorePreviousDependencies =
+ current !== null && current.type !== workInProgress.type;
}
-}
-
-function resetHydrationState() {
- hydrationParentFiber = null;
- nextHydratableInstance = null;
- isHydrating = false;
- didSuspendOrErrorDEV = false;
-}
-function upgradeHydrationErrorsToRecoverable() {
- if (hydrationErrors !== null) {
- // Successfully completed a forced client render. The errors that occurred
- // during the hydration attempt are now recovered. We will log them in
- // commit phase, once the entire tree has finished.
- queueRecoverableErrors(hydrationErrors);
- hydrationErrors = null;
+ workInProgress.memoizedState = null;
+ workInProgress.updateQueue = null;
+ workInProgress.lanes = NoLanes; // The following should have already been reset
+ // currentHook = null;
+ // workInProgressHook = null;
+ // didScheduleRenderPhaseUpdate = false;
+ // localIdCounter = 0;
+ // thenableIndexCounter = 0;
+ // thenableState = null;
+ // TODO Warn if no hooks are used at all during mount, then some are used during update.
+ // Currently we will identify the update render as a mount because memoizedState === null.
+ // This is tricky because it's valid for certain types of components (e.g. React.lazy)
+ // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
+ // Non-stateful hooks (e.g. context) don't get added to memoizedState,
+ // so memoizedState would be null during updates and mounts.
+
+ {
+ if (current !== null && current.memoizedState !== null) {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
+ } else if (hookTypesDev !== null) {
+ // This dispatcher handles an edge case where a component is updating,
+ // but no stateful hooks have been used.
+ // We want to match the production code behavior (which will use HooksDispatcherOnMount),
+ // but with the extra DEV validation to ensure hooks ordering hasn't changed.
+ // This dispatcher does that.
+ ReactCurrentDispatcher$1.current =
+ HooksDispatcherOnMountWithHookTypesInDEV;
+ } else {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
+ }
+ } // In Strict Mode, during development, user functions are double invoked to
+ // help detect side effects. The logic for how this is implemented for in
+ // hook components is a bit complex so let's break it down.
+ //
+ // We will invoke the entire component function twice. However, during the
+ // second invocation of the component, the hook state from the first
+ // invocation will be reused. That means things like `useMemo` functions won't
+ // run again, because the deps will match and the memoized result will
+ // be reused.
+ //
+ // We want memoized functions to run twice, too, so account for this, user
+ // functions are double invoked during the *first* invocation of the component
+ // function, and are *not* double invoked during the second incovation:
+ //
+ // - First execution of component function: user functions are double invoked
+ // - Second execution of component function (in Strict Mode, during
+ // development): user functions are not double invoked.
+ //
+ // This is intentional for a few reasons; most importantly, it's because of
+ // how `use` works when something suspends: it reuses the promise that was
+ // passed during the first attempt. This is itself a form of memoization.
+ // We need to be able to memoize the reactive inputs to the `use` call using
+ // a hook (i.e. `useMemo`), which means, the reactive inputs to `use` must
+ // come from the same component invocation as the output.
+ //
+ // There are plenty of tests to ensure this behavior is correct.
+
+ var shouldDoubleRenderDEV =
+ (workInProgress.mode & StrictLegacyMode) !== NoMode;
+ shouldDoubleInvokeUserFnsInHooksDEV = shouldDoubleRenderDEV;
+ var children = Component(props, secondArg);
+ shouldDoubleInvokeUserFnsInHooksDEV = false; // Check if there was a render phase update
+
+ if (didScheduleRenderPhaseUpdateDuringThisPass) {
+ // Keep rendering until the component stabilizes (there are no more render
+ // phase updates).
+ children = renderWithHooksAgain(
+ workInProgress,
+ Component,
+ props,
+ secondArg
+ );
}
-}
-function getIsHydrating() {
- return isHydrating;
-}
+ if (shouldDoubleRenderDEV) {
+ // In development, components are invoked twice to help detect side effects.
+ setIsStrictModeForDevtools(true);
-function queueHydrationError(error) {
- if (hydrationErrors === null) {
- hydrationErrors = [error];
- } else {
- hydrationErrors.push(error);
+ try {
+ children = renderWithHooksAgain(
+ workInProgress,
+ Component,
+ props,
+ secondArg
+ );
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
}
-}
-// we wait until the current render is over (either finished or interrupted)
-// before adding it to the fiber/hook queue. Push to this array so we can
-// access the queue, fiber, update, et al later.
+ finishRenderingHooks(current, workInProgress);
+ return children;
+}
-var concurrentQueues = [];
-var concurrentQueuesIndex = 0;
-var concurrentlyUpdatedLanes = NoLanes;
-function finishQueueingConcurrentUpdates() {
- var endIndex = concurrentQueuesIndex;
- concurrentQueuesIndex = 0;
- concurrentlyUpdatedLanes = NoLanes;
- var i = 0;
+function finishRenderingHooks(current, workInProgress) {
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrance.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
- while (i < endIndex) {
- var fiber = concurrentQueues[i];
- concurrentQueues[i++] = null;
- var queue = concurrentQueues[i];
- concurrentQueues[i++] = null;
- var update = concurrentQueues[i];
- concurrentQueues[i++] = null;
- var lane = concurrentQueues[i];
- concurrentQueues[i++] = null;
+ {
+ workInProgress._debugHookTypes = hookTypesDev;
+ } // This check uses currentHook so that it works the same in DEV and prod bundles.
+ // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
- if (queue !== null && update !== null) {
- var pending = queue.pending;
+ var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
+ renderLanes$1 = NoLanes;
+ currentlyRenderingFiber$1 = null;
+ currentHook = null;
+ workInProgressHook = null;
- if (pending === null) {
- // This is the first update. Create a circular list.
- update.next = update;
- } else {
- update.next = pending.next;
- pending.next = update;
- }
+ {
+ currentHookNameInDev = null;
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1; // Confirm that a static flag was not added or removed since the last
+ // render. If this fires, it suggests that we incorrectly reset the static
+ // flags in some other part of the codebase. This has happened before, for
+ // example, in the SuspenseList implementation.
- queue.pending = update;
+ if (
+ current !== null &&
+ (current.flags & StaticMask) !== (workInProgress.flags & StaticMask) && // Disable this warning in legacy mode, because legacy Suspense is weird
+ // and creates false positives. To make this work in legacy mode, we'd
+ // need to mark fibers that commit in an incomplete state, somehow. For
+ // now I'll disable the warning that most of the bugs that would trigger
+ // it are either exclusive to concurrent mode or exist in both.
+ (current.mode & ConcurrentMode) !== NoMode
+ ) {
+ error(
+ "Internal React error: Expected static flag was missing. Please " +
+ "notify the React team."
+ );
}
+ }
- if (lane !== NoLane) {
- markUpdateLaneFromFiberToRoot(fiber, update, lane);
+ didScheduleRenderPhaseUpdate = false; // This is reset by checkDidRenderIdHook
+ // localIdCounter = 0;
+
+ thenableIndexCounter = 0;
+ thenableState = null;
+
+ if (didRenderTooFewHooks) {
+ throw new Error(
+ "Rendered fewer hooks than expected. This may be caused by an accidental " +
+ "early return statement."
+ );
+ }
+
+ if (enableLazyContextPropagation) {
+ if (current !== null) {
+ if (!checkIfWorkInProgressReceivedUpdate()) {
+ // If there were no changes to props or state, we need to check if there
+ // was a context change. We didn't already do this because there's no
+ // 1:1 correspondence between dependencies and hooks. Although, because
+ // there almost always is in the common case (`readContext` is an
+ // internal API), we could compare in there. OTOH, we only hit this case
+ // if everything else bails out, so on the whole it might be better to
+ // keep the comparison out of the common path.
+ var currentDependencies = current.dependencies;
+
+ if (
+ currentDependencies !== null &&
+ checkIfContextChanged(currentDependencies)
+ ) {
+ markWorkInProgressReceivedUpdate();
+ }
+ }
}
}
-}
-function getConcurrentlyUpdatedLanes() {
- return concurrentlyUpdatedLanes;
-}
-function enqueueUpdate$1(fiber, queue, update, lane) {
- // Don't update the `childLanes` on the return path yet. If we already in
- // the middle of rendering, wait until after it has completed.
- concurrentQueues[concurrentQueuesIndex++] = fiber;
- concurrentQueues[concurrentQueuesIndex++] = queue;
- concurrentQueues[concurrentQueuesIndex++] = update;
- concurrentQueues[concurrentQueuesIndex++] = lane;
- concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane); // The fiber's `lane` field is used in some places to check if any work is
- // scheduled, to perform an eager bailout, so we need to update it immediately.
- // TODO: We should probably move this to the "shared" queue instead.
+ {
+ if (checkIfUseWrappedInTryCatch()) {
+ var componentName =
+ getComponentNameFromFiber(workInProgress) || "Unknown";
- fiber.lanes = mergeLanes(fiber.lanes, lane);
- var alternate = fiber.alternate;
+ if (!didWarnAboutUseWrappedInTryCatch.has(componentName)) {
+ didWarnAboutUseWrappedInTryCatch.add(componentName);
- if (alternate !== null) {
- alternate.lanes = mergeLanes(alternate.lanes, lane);
+ error(
+ "`use` was called from inside a try/catch block. This is not allowed " +
+ "and can lead to unexpected behavior. To handle errors triggered " +
+ "by `use`, wrap your component in a error boundary."
+ );
+ }
+ }
}
}
-function enqueueConcurrentHookUpdate(fiber, queue, update, lane) {
- var concurrentQueue = queue;
- var concurrentUpdate = update;
- enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
- return getRootForUpdatedFiber(fiber);
-}
-function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update) {
- // This function is used to queue an update that doesn't need a rerender. The
- // only reason we queue it is in case there's a subsequent higher priority
- // update that causes it to be rebased.
- var lane = NoLane;
- var concurrentQueue = queue;
- var concurrentUpdate = update;
- enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane); // Usually we can rely on the upcoming render phase to process the concurrent
- // queue. However, since this is a bail out, we're not scheduling any work
- // here. So the update we just queued will leak until something else happens
- // to schedule work (if ever).
+function replaySuspendedComponentWithHooks(
+ current,
+ workInProgress,
+ Component,
+ props,
+ secondArg
+) {
+ // This function is used to replay a component that previously suspended,
+ // after its data resolves.
//
- // Check if we're currently in the middle of rendering a tree, and if not,
- // process the queue immediately to prevent a leak.
-
- var isConcurrentlyRendering = getWorkInProgressRoot() !== null;
+ // It's a simplified version of renderWithHooks, but it doesn't need to do
+ // most of the set up work because they weren't reset when we suspended; they
+ // only get reset when the component either completes (finishRenderingHooks)
+ // or unwinds (resetHooksOnUnwind).
+ {
+ hookTypesUpdateIndexDev = -1; // Used for hot reloading:
- if (!isConcurrentlyRendering) {
- finishQueueingConcurrentUpdates();
+ ignorePreviousDependencies =
+ current !== null && current.type !== workInProgress.type;
}
-}
-function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
- var concurrentQueue = queue;
- var concurrentUpdate = update;
- enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
- return getRootForUpdatedFiber(fiber);
-}
-function enqueueConcurrentRenderForLane(fiber, lane) {
- enqueueUpdate$1(fiber, null, null, lane);
- return getRootForUpdatedFiber(fiber);
-} // Calling this function outside this module should only be done for backwards
-// compatibility and should always be accompanied by a warning.
-function unsafe_markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
- // NOTE: For Hyrum's Law reasons, if an infinite update loop is detected, it
- // should throw before `markUpdateLaneFromFiberToRoot` is called. But this is
- // undefined behavior and we can change it if we need to; it just so happens
- // that, at the time of this writing, there's an internal product test that
- // happens to rely on this.
- var root = getRootForUpdatedFiber(sourceFiber);
- markUpdateLaneFromFiberToRoot(sourceFiber, null, lane);
- return root;
+ var children = renderWithHooksAgain(
+ workInProgress,
+ Component,
+ props,
+ secondArg
+ );
+ finishRenderingHooks(current, workInProgress);
+ return children;
}
-function markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {
- // Update the source fiber's lanes
- sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
- var alternate = sourceFiber.alternate;
-
- if (alternate !== null) {
- alternate.lanes = mergeLanes(alternate.lanes, lane);
- } // Walk the parent path to the root and update the child lanes.
+function renderWithHooksAgain(workInProgress, Component, props, secondArg) {
+ // This is used to perform another render pass. It's used when setState is
+ // called during render, and for double invoking components in Strict Mode
+ // during development.
+ //
+ // The state from the previous pass is reused whenever possible. So, state
+ // updates that were already processed are not processed again, and memoized
+ // functions (`useMemo`) are not invoked again.
+ //
+ // Keep rendering in a loop for as long as render phase updates continue to
+ // be scheduled. Use a counter to prevent infinite loops.
+ var numberOfReRenders = 0;
+ var children;
- var isHidden = false;
- var parent = sourceFiber.return;
- var node = sourceFiber;
+ do {
+ if (didScheduleRenderPhaseUpdateDuringThisPass) {
+ // It's possible that a use() value depended on a state that was updated in
+ // this rerender, so we need to watch for different thenables this time.
+ thenableState = null;
+ }
- while (parent !== null) {
- parent.childLanes = mergeLanes(parent.childLanes, lane);
- alternate = parent.alternate;
+ thenableIndexCounter = 0;
+ didScheduleRenderPhaseUpdateDuringThisPass = false;
- if (alternate !== null) {
- alternate.childLanes = mergeLanes(alternate.childLanes, lane);
+ if (numberOfReRenders >= RE_RENDER_LIMIT) {
+ throw new Error(
+ "Too many re-renders. React limits the number of renders to prevent " +
+ "an infinite loop."
+ );
}
- if (parent.tag === OffscreenComponent) {
- // Check if this offscreen boundary is currently hidden.
- //
- // The instance may be null if the Offscreen parent was unmounted. Usually
- // the parent wouldn't be reachable in that case because we disconnect
- // fibers from the tree when they are deleted. However, there's a weird
- // edge case where setState is called on a fiber that was interrupted
- // before it ever mounted. Because it never mounts, it also never gets
- // deleted. Because it never gets deleted, its return pointer never gets
- // disconnected. Which means it may be attached to a deleted Offscreen
- // parent node. (This discovery suggests it may be better for memory usage
- // if we don't attach the `return` pointer until the commit phase, though
- // in order to do that we'd need some other way to track the return
- // pointer during the initial render, like on the stack.)
- //
- // This case is always accompanied by a warning, but we still need to
- // account for it. (There may be other cases that we haven't discovered,
- // too.)
- var offscreenInstance = parent.stateNode;
+ numberOfReRenders += 1;
- if (
- offscreenInstance !== null &&
- !(offscreenInstance._visibility & OffscreenVisible)
- ) {
- isHidden = true;
- }
+ {
+ // Even when hot reloading, allow dependencies to stabilize
+ // after first render to prevent infinite render phase updates.
+ ignorePreviousDependencies = false;
+ } // Start over from the beginning of the list
+
+ currentHook = null;
+ workInProgressHook = null;
+ workInProgress.updateQueue = null;
+
+ {
+ // Also validate hook order for cascading updates.
+ hookTypesUpdateIndexDev = -1;
}
- node = parent;
- parent = parent.return;
- }
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV;
+ children = Component(props, secondArg);
+ } while (didScheduleRenderPhaseUpdateDuringThisPass);
- if (isHidden && update !== null && node.tag === HostRoot) {
- var root = node.stateNode;
- markHiddenUpdate(root, update, lane);
- }
+ return children;
}
-function getRootForUpdatedFiber(sourceFiber) {
- // TODO: We will detect and infinite update loop and throw even if this fiber
- // has already unmounted. This isn't really necessary but it happens to be the
- // current behavior we've used for several release cycles. Consider not
- // performing this check if the updated fiber already unmounted, since it's
- // not possible for that to cause an infinite update loop.
- throwIfInfiniteUpdateLoopDetected(); // When a setState happens, we must ensure the root is scheduled. Because
- // update queues do not have a backpointer to the root, the only way to do
- // this currently is to walk up the return path. This used to not be a big
- // deal because we would have to walk up the return path to set
- // the `childLanes`, anyway, but now those two traversals happen at
- // different times.
- // TODO: Consider adding a `root` backpointer on the update queue.
-
- detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
- var node = sourceFiber;
- var parent = node.return;
+function checkDidRenderIdHook() {
+ // This should be called immediately after every renderWithHooks call.
+ // Conceptually, it's part of the return value of renderWithHooks; it's only a
+ // separate function to avoid using an array tuple.
+ var didRenderIdHook = localIdCounter !== 0;
+ localIdCounter = 0;
+ return didRenderIdHook;
+}
+function bailoutHooks(current, workInProgress, lanes) {
+ workInProgress.updateQueue = current.updateQueue; // TODO: Don't need to reset the flags here, because they're reset in the
+ // complete phase (bubbleProperties).
- while (parent !== null) {
- detectUpdateOnUnmountedFiber(sourceFiber, node);
- node = parent;
- parent = node.return;
+ if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
+ workInProgress.flags &= ~(
+ MountPassiveDev |
+ MountLayoutDev |
+ Passive$1 |
+ Update
+ );
+ } else {
+ workInProgress.flags &= ~(Passive$1 | Update);
}
- return node.tag === HostRoot ? node.stateNode : null;
+ current.lanes = removeLanes(current.lanes, lanes);
+}
+function resetHooksAfterThrow() {
+ // This is called immediaetly after a throw. It shouldn't reset the entire
+ // module state, because the work loop might decide to replay the component
+ // again without rewinding.
+ //
+ // It should only reset things like the current dispatcher, to prevent hooks
+ // from being called outside of a component.
+ // We can assume the previous dispatcher is always this one, since we set it
+ // at the beginning of the render phase and there's no re-entrance.
+ ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
}
+function resetHooksOnUnwind() {
+ if (didScheduleRenderPhaseUpdate) {
+ // There were render phase updates. These are only valid for this render
+ // phase, which we are now aborting. Remove the updates from the queues so
+ // they do not persist to the next render. Do not remove updates from hooks
+ // that weren't processed.
+ //
+ // Only reset the updates from the queue if it has a clone. If it does
+ // not have a clone, that means it wasn't processed, and the updates were
+ // scheduled before we entered the render phase.
+ var hook = currentlyRenderingFiber$1.memoizedState;
-function detectUpdateOnUnmountedFiber(sourceFiber, parent) {
- {
- var alternate = parent.alternate;
+ while (hook !== null) {
+ var queue = hook.queue;
- if (
- alternate === null &&
- (parent.flags & (Placement | Hydrating)) !== NoFlags$1
- ) {
- warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
+ if (queue !== null) {
+ queue.pending = null;
+ }
+
+ hook = hook.next;
}
+
+ didScheduleRenderPhaseUpdate = false;
}
-}
-var UpdateState = 0;
-var ReplaceState = 1;
-var ForceUpdate = 2;
-var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`.
-// It should only be read right after calling `processUpdateQueue`, via
-// `checkHasForceUpdateAfterProcessing`.
+ renderLanes$1 = NoLanes;
+ currentlyRenderingFiber$1 = null;
+ currentHook = null;
+ workInProgressHook = null;
-var hasForceUpdate = false;
-var didWarnUpdateInsideUpdate;
-var currentlyProcessingQueue;
+ {
+ hookTypesDev = null;
+ hookTypesUpdateIndexDev = -1;
+ currentHookNameInDev = null;
+ }
-{
- didWarnUpdateInsideUpdate = false;
- currentlyProcessingQueue = null;
+ didScheduleRenderPhaseUpdateDuringThisPass = false;
+ localIdCounter = 0;
+ thenableIndexCounter = 0;
+ thenableState = null;
}
-function initializeUpdateQueue(fiber) {
- var queue = {
- baseState: fiber.memoizedState,
- firstBaseUpdate: null,
- lastBaseUpdate: null,
- shared: {
- pending: null,
- lanes: NoLanes,
- hiddenCallbacks: null
- },
- callbacks: null
- };
- fiber.updateQueue = queue;
-}
-function cloneUpdateQueue(current, workInProgress) {
- // Clone the update queue from current. Unless it's already a clone.
- var queue = workInProgress.updateQueue;
- var currentQueue = current.updateQueue;
-
- if (queue === currentQueue) {
- var clone = {
- baseState: currentQueue.baseState,
- firstBaseUpdate: currentQueue.firstBaseUpdate,
- lastBaseUpdate: currentQueue.lastBaseUpdate,
- shared: currentQueue.shared,
- callbacks: null
- };
- workInProgress.updateQueue = clone;
- }
-}
-function createUpdate(lane) {
- var update = {
- lane: lane,
- tag: UpdateState,
- payload: null,
- callback: null,
+function mountWorkInProgressHook() {
+ var hook = {
+ memoizedState: null,
+ baseState: null,
+ baseQueue: null,
+ queue: null,
next: null
};
- return update;
-}
-function enqueueUpdate(fiber, update, lane) {
- var updateQueue = fiber.updateQueue;
- if (updateQueue === null) {
- // Only occurs if the fiber has been unmounted.
- return null;
+ if (workInProgressHook === null) {
+ // This is the first hook in the list
+ currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
+ } else {
+ // Append to the end of the list
+ workInProgressHook = workInProgressHook.next = hook;
}
- var sharedQueue = updateQueue.shared;
-
- {
- if (
- currentlyProcessingQueue === sharedQueue &&
- !didWarnUpdateInsideUpdate
- ) {
- var componentName = getComponentNameFromFiber(fiber);
-
- error(
- "An update (setState, replaceState, or forceUpdate) was scheduled " +
- "from inside an update function. Update functions should be pure, " +
- "with zero side-effects. Consider using componentDidUpdate or a " +
- "callback.\n\nPlease update the following component: %s",
- componentName
- );
+ return workInProgressHook;
+}
- didWarnUpdateInsideUpdate = true;
- }
- }
+function updateWorkInProgressHook() {
+ // This function is used both for updates and for re-renders triggered by a
+ // render phase update. It assumes there is either a current hook we can
+ // clone, or a work-in-progress hook from a previous render pass that we can
+ // use as a base.
+ var nextCurrentHook;
- if (isUnsafeClassRenderPhaseUpdate(fiber)) {
- // This is an unsafe render phase update. Add directly to the update
- // queue so we can process it immediately during the current render.
- var pending = sharedQueue.pending;
+ if (currentHook === null) {
+ var current = currentlyRenderingFiber$1.alternate;
- if (pending === null) {
- // This is the first update. Create a circular list.
- update.next = update;
+ if (current !== null) {
+ nextCurrentHook = current.memoizedState;
} else {
- update.next = pending.next;
- pending.next = update;
+ nextCurrentHook = null;
}
-
- sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering
- // this fiber. This is for backwards compatibility in the case where you
- // update a different component during render phase than the one that is
- // currently renderings (a pattern that is accompanied by a warning).
-
- return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
- return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
+ nextCurrentHook = currentHook.next;
}
-}
-function entangleTransitions(root, fiber, lane) {
- var updateQueue = fiber.updateQueue;
- if (updateQueue === null) {
- // Only occurs if the fiber has been unmounted.
- return;
- }
+ var nextWorkInProgressHook;
- var sharedQueue = updateQueue.shared;
+ if (workInProgressHook === null) {
+ nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
+ } else {
+ nextWorkInProgressHook = workInProgressHook.next;
+ }
- if (isTransitionLane(lane)) {
- var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must
- // have finished. We can remove them from the shared queue, which represents
- // a superset of the actually pending lanes. In some cases we may entangle
- // more than we need to, but that's OK. In fact it's worse if we *don't*
- // entangle when we should.
+ if (nextWorkInProgressHook !== null) {
+ // There's already a work-in-progress. Reuse it.
+ workInProgressHook = nextWorkInProgressHook;
+ nextWorkInProgressHook = workInProgressHook.next;
+ currentHook = nextCurrentHook;
+ } else {
+ // Clone from the current hook.
+ if (nextCurrentHook === null) {
+ var currentFiber = currentlyRenderingFiber$1.alternate;
- queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
+ if (currentFiber === null) {
+ // This is the initial render. This branch is reached when the component
+ // suspends, resumes, then renders an additional hook.
+ // Should never be reached because we should switch to the mount dispatcher first.
+ throw new Error(
+ "Update hook called on initial render. This is likely a bug in React. Please file an issue."
+ );
+ } else {
+ // This is an update. We should always have a current hook.
+ throw new Error("Rendered more hooks than during the previous render.");
+ }
+ }
- var newQueueLanes = mergeLanes(queueLanes, lane);
- sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
- // the lane finished since the last time we entangled it. So we need to
- // entangle it again, just to be sure.
+ currentHook = nextCurrentHook;
+ var newHook = {
+ memoizedState: currentHook.memoizedState,
+ baseState: currentHook.baseState,
+ baseQueue: currentHook.baseQueue,
+ queue: currentHook.queue,
+ next: null
+ };
- markRootEntangled(root, newQueueLanes);
+ if (workInProgressHook === null) {
+ // This is the first hook in the list.
+ currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
+ } else {
+ // Append to the end of the list.
+ workInProgressHook = workInProgressHook.next = newHook;
+ }
}
-}
-function enqueueCapturedUpdate(workInProgress, capturedUpdate) {
- // Captured updates are updates that are thrown by a child during the render
- // phase. They should be discarded if the render is aborted. Therefore,
- // we should only put them on the work-in-progress queue, not the current one.
- var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone.
- var current = workInProgress.alternate;
+ return workInProgressHook;
+} // NOTE: defining two versions of this function to avoid size impact when this feature is disabled.
+// Previously this function was inlined, the additional `memoCache` property makes it not inlined.
- if (current !== null) {
- var currentQueue = current.updateQueue;
+var createFunctionComponentUpdateQueue;
- if (queue === currentQueue) {
- // The work-in-progress queue is the same as current. This happens when
- // we bail out on a parent fiber that then captures an error thrown by
- // a child. Since we want to append the update only to the work-in
- // -progress queue, we need to clone the updates. We usually clone during
- // processUpdateQueue, but that didn't happen in this case because we
- // skipped over the parent when we bailed out.
- var newFirst = null;
- var newLast = null;
- var firstBaseUpdate = queue.firstBaseUpdate;
+{
+ createFunctionComponentUpdateQueue = function () {
+ return {
+ lastEffect: null,
+ events: null,
+ stores: null,
+ memoCache: null
+ };
+ };
+}
- if (firstBaseUpdate !== null) {
- // Loop through the updates and clone them.
- var update = firstBaseUpdate;
+function use(usable) {
+ if (usable !== null && typeof usable === "object") {
+ // $FlowFixMe[method-unbinding]
+ if (typeof usable.then === "function") {
+ // This is a thenable.
+ var thenable = usable; // Track the position of the thenable within this fiber.
- do {
- var clone = {
- lane: update.lane,
- tag: update.tag,
- payload: update.payload,
- // When this update is rebased, we should not fire its
- // callback again.
- callback: null,
- next: null
- };
+ var index = thenableIndexCounter;
+ thenableIndexCounter += 1;
- if (newLast === null) {
- newFirst = newLast = clone;
- } else {
- newLast.next = clone;
- newLast = clone;
- } // $FlowFixMe[incompatible-type] we bail out when we get a null
+ if (thenableState === null) {
+ thenableState = createThenableState();
+ }
- update = update.next;
- } while (update !== null); // Append the captured update the end of the cloned list.
+ var result = trackUsedThenable(thenableState, thenable, index);
- if (newLast === null) {
- newFirst = newLast = capturedUpdate;
- } else {
- newLast.next = capturedUpdate;
- newLast = capturedUpdate;
+ if (
+ currentlyRenderingFiber$1.alternate === null &&
+ (workInProgressHook === null
+ ? currentlyRenderingFiber$1.memoizedState === null
+ : workInProgressHook.next === null)
+ ) {
+ // Initial render, and either this is the first time the component is
+ // called, or there were no Hooks called after this use() the previous
+ // time (perhaps because it threw). Subsequent Hook calls should use the
+ // mount dispatcher.
+ {
+ ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
}
- } else {
- // There are no base updates.
- newFirst = newLast = capturedUpdate;
}
- queue = {
- baseState: currentQueue.baseState,
- firstBaseUpdate: newFirst,
- lastBaseUpdate: newLast,
- shared: currentQueue.shared,
- callbacks: currentQueue.callbacks
- };
- workInProgress.updateQueue = queue;
- return;
+ return result;
+ } else if (
+ usable.$$typeof === REACT_CONTEXT_TYPE ||
+ usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = usable;
+ return readContext(context);
}
- } // Append the update to the end of the list.
-
- var lastBaseUpdate = queue.lastBaseUpdate;
-
- if (lastBaseUpdate === null) {
- queue.firstBaseUpdate = capturedUpdate;
- } else {
- lastBaseUpdate.next = capturedUpdate;
- }
+ } // eslint-disable-next-line react-internal/safe-string-coercion
- queue.lastBaseUpdate = capturedUpdate;
+ throw new Error("An unsupported type was passed to use(): " + String(usable));
}
-function getStateFromUpdate(
- workInProgress,
- queue,
- update,
- prevState,
- nextProps,
- instance
-) {
- switch (update.tag) {
- case ReplaceState: {
- var payload = update.payload;
-
- if (typeof payload === "function") {
- // Updater function
- {
- enterDisallowedContextReadInDEV();
- }
-
- var nextState = payload.call(instance, prevState, nextProps);
-
- {
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
-
- try {
- payload.call(instance, prevState, nextProps);
- } finally {
- setIsStrictModeForDevtools(false);
- }
- }
+function useMemoCache(size) {
+ var memoCache = null; // Fast-path, load memo cache from wip fiber if already prepared
- exitDisallowedContextReadInDEV();
- }
+ var updateQueue = currentlyRenderingFiber$1.updateQueue;
- return nextState;
- } // State object
+ if (updateQueue !== null) {
+ memoCache = updateQueue.memoCache;
+ } // Otherwise clone from the current fiber
- return payload;
- }
+ if (memoCache == null) {
+ var current = currentlyRenderingFiber$1.alternate;
- case CaptureUpdate: {
- workInProgress.flags =
- (workInProgress.flags & ~ShouldCapture) | DidCapture;
- }
- // Intentional fallthrough
+ if (current !== null) {
+ var currentUpdateQueue = current.updateQueue;
- case UpdateState: {
- var _payload = update.payload;
- var partialState;
+ if (currentUpdateQueue !== null) {
+ var currentMemoCache = currentUpdateQueue.memoCache;
- if (typeof _payload === "function") {
- // Updater function
- {
- enterDisallowedContextReadInDEV();
+ if (currentMemoCache != null) {
+ memoCache = {
+ data: currentMemoCache.data.map(function (array) {
+ return array.slice();
+ }),
+ index: 0
+ };
}
+ }
+ }
+ } // Finally fall back to allocating a fresh instance of the cache
- partialState = _payload.call(instance, prevState, nextProps);
-
- {
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
+ if (memoCache == null) {
+ memoCache = {
+ data: [],
+ index: 0
+ };
+ }
- try {
- _payload.call(instance, prevState, nextProps);
- } finally {
- setIsStrictModeForDevtools(false);
- }
- }
+ if (updateQueue === null) {
+ updateQueue = createFunctionComponentUpdateQueue();
+ currentlyRenderingFiber$1.updateQueue = updateQueue;
+ }
- exitDisallowedContextReadInDEV();
- }
- } else {
- // Partial state object
- partialState = _payload;
- }
+ updateQueue.memoCache = memoCache;
+ var data = memoCache.data[memoCache.index];
- if (partialState === null || partialState === undefined) {
- // Null and undefined are treated as no-ops.
- return prevState;
- } // Merge the partial state and the previous state.
+ if (data === undefined) {
+ data = memoCache.data[memoCache.index] = new Array(size);
- return assign({}, prevState, partialState);
+ for (var i = 0; i < size; i++) {
+ data[i] = REACT_MEMO_CACHE_SENTINEL;
}
-
- case ForceUpdate: {
- hasForceUpdate = true;
- return prevState;
+ } else if (data.length !== size) {
+ // TODO: consider warning or throwing here
+ {
+ error(
+ "Expected a constant size argument for each invocation of useMemoCache. " +
+ "The previous cache was allocated with size %s but size %s was requested.",
+ data.length,
+ size
+ );
}
}
- return prevState;
+ memoCache.index++;
+ return data;
}
-function processUpdateQueue(workInProgress, props, instance, renderLanes) {
- // This is always non-null on a ClassComponent or HostRoot
- var queue = workInProgress.updateQueue;
- hasForceUpdate = false;
-
- {
- currentlyProcessingQueue = queue.shared;
- }
+function basicStateReducer(state, action) {
+ // $FlowFixMe: Flow doesn't like mixed types
+ return typeof action === "function" ? action(state) : action;
+}
- var firstBaseUpdate = queue.firstBaseUpdate;
- var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue.
+function mountReducer(reducer, initialArg, init) {
+ var hook = mountWorkInProgressHook();
+ var initialState;
- var pendingQueue = queue.shared.pending;
+ if (init !== undefined) {
+ initialState = init(initialArg);
+ } else {
+ initialState = initialArg;
+ }
- if (pendingQueue !== null) {
- queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first
- // and last so that it's non-circular.
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = {
+ pending: null,
+ lanes: NoLanes,
+ dispatch: null,
+ lastRenderedReducer: reducer,
+ lastRenderedState: initialState
+ };
+ hook.queue = queue;
+ var dispatch = (queue.dispatch = dispatchReducerAction.bind(
+ null,
+ currentlyRenderingFiber$1,
+ queue
+ ));
+ return [hook.memoizedState, dispatch];
+}
- var lastPendingUpdate = pendingQueue;
- var firstPendingUpdate = lastPendingUpdate.next;
- lastPendingUpdate.next = null; // Append pending updates to base queue
+function updateReducer(reducer, initialArg, init) {
+ var hook = updateWorkInProgressHook();
+ var queue = hook.queue;
- if (lastBaseUpdate === null) {
- firstBaseUpdate = firstPendingUpdate;
- } else {
- lastBaseUpdate.next = firstPendingUpdate;
- }
+ if (queue === null) {
+ throw new Error(
+ "Should have a queue. This is likely a bug in React. Please file an issue."
+ );
+ }
- lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then
- // we need to transfer the updates to that queue, too. Because the base
- // queue is a singly-linked list with no cycles, we can append to both
- // lists and take advantage of structural sharing.
- // TODO: Pass `current` as argument
+ queue.lastRenderedReducer = reducer;
+ var current = currentHook; // The last rebase update that is NOT part of the base state.
- var current = workInProgress.alternate;
+ var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
- if (current !== null) {
- // This is always non-null on a ClassComponent or HostRoot
- var currentQueue = current.updateQueue;
- var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
+ var pendingQueue = queue.pending;
- if (currentLastBaseUpdate !== lastBaseUpdate) {
- if (currentLastBaseUpdate === null) {
- currentQueue.firstBaseUpdate = firstPendingUpdate;
- } else {
- currentLastBaseUpdate.next = firstPendingUpdate;
- }
+ if (pendingQueue !== null) {
+ // We have new updates that haven't been processed yet.
+ // We'll add them to the base queue.
+ if (baseQueue !== null) {
+ // Merge the pending queue and the base queue.
+ var baseFirst = baseQueue.next;
+ var pendingFirst = pendingQueue.next;
+ baseQueue.next = pendingFirst;
+ pendingQueue.next = baseFirst;
+ }
- currentQueue.lastBaseUpdate = lastPendingUpdate;
+ {
+ if (current.baseQueue !== baseQueue) {
+ // Internal invariant that should never happen, but feasibly could in
+ // the future if we implement resuming, or some form of that.
+ error(
+ "Internal error: Expected work-in-progress queue to be a clone. " +
+ "This is a bug in React."
+ );
}
}
- } // These values may change as we process the queue.
- if (firstBaseUpdate !== null) {
- // Iterate through the list of updates to compute the result.
- var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
- // from the original lanes.
+ current.baseQueue = baseQueue = pendingQueue;
+ queue.pending = null;
+ }
- var newLanes = NoLanes;
+ if (baseQueue !== null) {
+ // We have a queue to process.
+ var first = baseQueue.next;
+ var newState = current.baseState;
var newBaseState = null;
- var newFirstBaseUpdate = null;
- var newLastBaseUpdate = null;
- var update = firstBaseUpdate;
+ var newBaseQueueFirst = null;
+ var newBaseQueueLast = null;
+ var update = first;
do {
// An extra OffscreenLane bit is added to updates that were made to
@@ -18808,7 +17870,7 @@ function processUpdateQueue(workInProgress, props, instance, renderLanes) {
var shouldSkipUpdate = isHiddenUpdate
? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
- : !isSubsetOfLanes(renderLanes, updateLane);
+ : !isSubsetOfLanes(renderLanes$1, updateLane);
if (shouldSkipUpdate) {
// Priority is insufficient. Skip this update. If this is the first
@@ -18816,24473 +17878,25354 @@ function processUpdateQueue(workInProgress, props, instance, renderLanes) {
// update/state.
var clone = {
lane: updateLane,
- tag: update.tag,
- payload: update.payload,
- callback: update.callback,
+ action: update.action,
+ hasEagerState: update.hasEagerState,
+ eagerState: update.eagerState,
next: null
};
- if (newLastBaseUpdate === null) {
- newFirstBaseUpdate = newLastBaseUpdate = clone;
+ if (newBaseQueueLast === null) {
+ newBaseQueueFirst = newBaseQueueLast = clone;
newBaseState = newState;
} else {
- newLastBaseUpdate = newLastBaseUpdate.next = clone;
+ newBaseQueueLast = newBaseQueueLast.next = clone;
} // Update the remaining priority in the queue.
+ // TODO: Don't need to accumulate this. Instead, we can remove
+ // renderLanes from the original lanes.
- newLanes = mergeLanes(newLanes, updateLane);
+ currentlyRenderingFiber$1.lanes = mergeLanes(
+ currentlyRenderingFiber$1.lanes,
+ updateLane
+ );
+ markSkippedUpdateLanes(updateLane);
} else {
// This update does have sufficient priority.
- if (newLastBaseUpdate !== null) {
+ if (newBaseQueueLast !== null) {
var _clone = {
// This update is going to be committed so we never want uncommit
// it. Using NoLane works because 0 is a subset of all bitmasks, so
// this will never be skipped by the check above.
lane: NoLane,
- tag: update.tag,
- payload: update.payload,
- // When this update is rebased, we should not fire its
- // callback again.
- callback: null,
+ action: update.action,
+ hasEagerState: update.hasEagerState,
+ eagerState: update.eagerState,
next: null
};
- newLastBaseUpdate = newLastBaseUpdate.next = _clone;
+ newBaseQueueLast = newBaseQueueLast.next = _clone;
} // Process this update.
- newState = getStateFromUpdate(
- workInProgress,
- queue,
- update,
- newState,
- props,
- instance
- );
- var callback = update.callback;
-
- if (callback !== null) {
- workInProgress.flags |= Callback;
-
- if (isHiddenUpdate) {
- workInProgress.flags |= Visibility;
- }
-
- var callbacks = queue.callbacks;
+ var action = update.action;
- if (callbacks === null) {
- queue.callbacks = [callback];
- } else {
- callbacks.push(callback);
- }
+ if (shouldDoubleInvokeUserFnsInHooksDEV) {
+ reducer(newState, action);
}
- } // $FlowFixMe[incompatible-type] we bail out when we get a null
-
- update = update.next;
-
- if (update === null) {
- pendingQueue = queue.shared.pending;
- if (pendingQueue === null) {
- break;
+ if (update.hasEagerState) {
+ // If this update is a state update (not a reducer) and was processed eagerly,
+ // we can use the eagerly computed state
+ newState = update.eagerState;
} else {
- // An update was scheduled from inside a reducer. Add the new
- // pending updates to the end of the list and keep processing.
- var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we
- // unravel them when transferring them to the base queue.
-
- var _firstPendingUpdate = _lastPendingUpdate.next;
- _lastPendingUpdate.next = null;
- update = _firstPendingUpdate;
- queue.lastBaseUpdate = _lastPendingUpdate;
- queue.shared.pending = null;
+ newState = reducer(newState, action);
}
}
- } while (true);
- if (newLastBaseUpdate === null) {
- newBaseState = newState;
- }
+ update = update.next;
+ } while (update !== null && update !== first);
- queue.baseState = newBaseState;
- queue.firstBaseUpdate = newFirstBaseUpdate;
- queue.lastBaseUpdate = newLastBaseUpdate;
+ if (newBaseQueueLast === null) {
+ newBaseState = newState;
+ } else {
+ newBaseQueueLast.next = newBaseQueueFirst;
+ } // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
- if (firstBaseUpdate === null) {
- // `queue.lanes` is used for entangling transitions. We can set it back to
- // zero once the queue is empty.
- queue.shared.lanes = NoLanes;
- } // Set the remaining expiration time to be whatever is remaining in the queue.
- // This should be fine because the only two other things that contribute to
- // expiration time are props and context. We're already in the middle of the
- // begin phase by the time we start processing the queue, so we've already
- // dealt with the props. Context in components that specify
- // shouldComponentUpdate is tricky; but we'll have to account for
- // that regardless.
+ if (!objectIs(newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
+ }
- markSkippedUpdateLanes(newLanes);
- workInProgress.lanes = newLanes;
- workInProgress.memoizedState = newState;
+ hook.memoizedState = newState;
+ hook.baseState = newBaseState;
+ hook.baseQueue = newBaseQueueLast;
+ queue.lastRenderedState = newState;
}
- {
- currentlyProcessingQueue = null;
+ if (baseQueue === null) {
+ // `queue.lanes` is used for entangling transitions. We can set it back to
+ // zero once the queue is empty.
+ queue.lanes = NoLanes;
}
+
+ var dispatch = queue.dispatch;
+ return [hook.memoizedState, dispatch];
}
-function callCallback(callback, context) {
- if (typeof callback !== "function") {
+function rerenderReducer(reducer, initialArg, init) {
+ var hook = updateWorkInProgressHook();
+ var queue = hook.queue;
+
+ if (queue === null) {
throw new Error(
- "Invalid argument passed as callback. Expected a function. Instead " +
- ("received: " + callback)
+ "Should have a queue. This is likely a bug in React. Please file an issue."
);
}
- callback.call(context);
-}
-
-function resetHasForceUpdateBeforeProcessing() {
- hasForceUpdate = false;
-}
-function checkHasForceUpdateAfterProcessing() {
- return hasForceUpdate;
-}
-function deferHiddenCallbacks(updateQueue) {
- // When an update finishes on a hidden component, its callback should not
- // be fired until/unless the component is made visible again. Stash the
- // callback on the shared queue object so it can be fired later.
- var newHiddenCallbacks = updateQueue.callbacks;
+ queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous
+ // work-in-progress hook.
- if (newHiddenCallbacks !== null) {
- var existingHiddenCallbacks = updateQueue.shared.hiddenCallbacks;
+ var dispatch = queue.dispatch;
+ var lastRenderPhaseUpdate = queue.pending;
+ var newState = hook.memoizedState;
- if (existingHiddenCallbacks === null) {
- updateQueue.shared.hiddenCallbacks = newHiddenCallbacks;
- } else {
- updateQueue.shared.hiddenCallbacks =
- existingHiddenCallbacks.concat(newHiddenCallbacks);
- }
- }
-}
-function commitHiddenCallbacks(updateQueue, context) {
- // This component is switching from hidden -> visible. Commit any callbacks
- // that were previously deferred.
- var hiddenCallbacks = updateQueue.shared.hiddenCallbacks;
+ if (lastRenderPhaseUpdate !== null) {
+ // The queue doesn't persist past this render pass.
+ queue.pending = null;
+ var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
+ var update = firstRenderPhaseUpdate;
- if (hiddenCallbacks !== null) {
- updateQueue.shared.hiddenCallbacks = null;
+ do {
+ // Process this render phase update. We don't have to check the
+ // priority because it will always be the same as the current
+ // render's.
+ var action = update.action;
+ newState = reducer(newState, action);
+ update = update.next;
+ } while (update !== firstRenderPhaseUpdate); // Mark that the fiber performed work, but only if the new state is
+ // different from the current state.
- for (var i = 0; i < hiddenCallbacks.length; i++) {
- var callback = hiddenCallbacks[i];
- callCallback(callback, context);
+ if (!objectIs(newState, hook.memoizedState)) {
+ markWorkInProgressReceivedUpdate();
}
- }
-}
-function commitCallbacks(updateQueue, context) {
- var callbacks = updateQueue.callbacks;
- if (callbacks !== null) {
- updateQueue.callbacks = null;
+ hook.memoizedState = newState; // Don't persist the state accumulated from the render phase updates to
+ // the base state unless the queue is empty.
+ // TODO: Not sure if this is the desired semantics, but it's what we
+ // do for gDSFP. I can't remember why.
- for (var i = 0; i < callbacks.length; i++) {
- var callback = callbacks[i];
- callCallback(callback, context);
+ if (hook.baseQueue === null) {
+ hook.baseState = newState;
}
- }
-}
-
-var ReactStrictModeWarnings = {
- recordUnsafeLifecycleWarnings: function (fiber, instance) {},
- flushPendingUnsafeLifecycleWarnings: function () {},
- recordLegacyContextWarning: function (fiber, instance) {},
- flushLegacyContextWarning: function () {},
- discardPendingWarnings: function () {}
-};
-{
- var findStrictRoot = function (fiber) {
- var maybeStrictRoot = null;
- var node = fiber;
+ queue.lastRenderedState = newState;
+ }
- while (node !== null) {
- if (node.mode & StrictLegacyMode) {
- maybeStrictRoot = node;
- }
+ return [newState, dispatch];
+}
- node = node.return;
- }
+function readFromUnsubscribedMutableSource(root, source, getSnapshot) {
+ {
+ warnAboutMultipleRenderersDEV(source);
+ }
- return maybeStrictRoot;
- };
+ var getVersion = source._getVersion;
+ var version = getVersion(source._source); // Is it safe for this component to read from this source during the current render?
- var setToSortedString = function (set) {
- var array = [];
- set.forEach(function (value) {
- array.push(value);
- });
- return array.sort().join(", ");
- };
+ var isSafeToReadFromSource = false; // Check the version first.
+ // If this render has already been started with a specific version,
+ // we can use it alone to determine if we can safely read from the source.
- var pendingComponentWillMountWarnings = [];
- var pendingUNSAFE_ComponentWillMountWarnings = [];
- var pendingComponentWillReceivePropsWarnings = [];
- var pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
- var pendingComponentWillUpdateWarnings = [];
- var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about.
+ var currentRenderVersion = getWorkInProgressVersion(source);
- var didWarnAboutUnsafeLifecycles = new Set();
+ if (currentRenderVersion !== null) {
+ // It's safe to read if the store hasn't been mutated since the last time
+ // we read something.
+ isSafeToReadFromSource = currentRenderVersion === version;
+ } else {
+ // If there's no version, then this is the first time we've read from the
+ // source during the current render pass, so we need to do a bit more work.
+ // What we need to determine is if there are any hooks that already
+ // subscribed to the source, and if so, whether there are any pending
+ // mutations that haven't been synchronized yet.
+ //
+ // If there are no pending mutations, then `root.mutableReadLanes` will be
+ // empty, and we know we can safely read.
+ //
+ // If there *are* pending mutations, we may still be able to safely read
+ // if the currently rendering lanes are inclusive of the pending mutation
+ // lanes, since that guarantees that the value we're about to read from
+ // the source is consistent with the values that we read during the most
+ // recent mutation.
+ isSafeToReadFromSource = isSubsetOfLanes(
+ renderLanes$1,
+ root.mutableReadLanes
+ );
- ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (
- fiber,
- instance
- ) {
- // Dedupe strategy: Warn once per component.
- if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
- return;
+ if (isSafeToReadFromSource) {
+ // If it's safe to read from this source during the current render,
+ // store the version in case other components read from it.
+ // A changed version number will let those components know to throw and restart the render.
+ setWorkInProgressVersion(source, version);
}
+ }
- if (
- typeof instance.componentWillMount === "function" && // Don't warn about react-lifecycles-compat polyfilled components.
- instance.componentWillMount.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillMountWarnings.push(fiber);
- }
+ if (isSafeToReadFromSource) {
+ var snapshot = getSnapshot(source._source);
- if (
- fiber.mode & StrictLegacyMode &&
- typeof instance.UNSAFE_componentWillMount === "function"
- ) {
- pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
+ {
+ if (typeof snapshot === "function") {
+ error(
+ "Mutable source should not return a function as the snapshot value. " +
+ "Functions may close over mutable values and cause tearing."
+ );
+ }
}
- if (
- typeof instance.componentWillReceiveProps === "function" &&
- instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillReceivePropsWarnings.push(fiber);
- }
+ return snapshot;
+ } else {
+ // This handles the special case of a mutable source being shared between renderers.
+ // In that case, if the source is mutated between the first and second renderer,
+ // The second renderer don't know that it needs to reset the WIP version during unwind,
+ // (because the hook only marks sources as dirty if it's written to their WIP version).
+ // That would cause this tear check to throw again and eventually be visible to the user.
+ // We can avoid this infinite loop by explicitly marking the source as dirty.
+ //
+ // This can lead to tearing in the first renderer when it resumes,
+ // but there's nothing we can do about that (short of throwing here and refusing to continue the render).
+ markSourceAsDirty(source); // Intentioally throw an error to force React to retry synchronously. During
+ // the synchronous retry, it will block interleaved mutations, so we should
+ // get a consistent read. Therefore, the following error should never be
+ // visible to the user.
+ // We expect this error not to be thrown during the synchronous retry,
+ // because we blocked interleaved mutations.
- if (
- fiber.mode & StrictLegacyMode &&
- typeof instance.UNSAFE_componentWillReceiveProps === "function"
- ) {
- pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
- }
+ throw new Error(
+ "Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue."
+ );
+ }
+}
- if (
- typeof instance.componentWillUpdate === "function" &&
- instance.componentWillUpdate.__suppressDeprecationWarning !== true
- ) {
- pendingComponentWillUpdateWarnings.push(fiber);
- }
+function useMutableSource(hook, source, getSnapshot, subscribe) {
+ var root = getWorkInProgressRoot();
- if (
- fiber.mode & StrictLegacyMode &&
- typeof instance.UNSAFE_componentWillUpdate === "function"
- ) {
- pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
- }
- };
+ if (root === null) {
+ throw new Error(
+ "Expected a work-in-progress root. This is a bug in React. Please file an issue."
+ );
+ }
- ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
- // We do an initial pass to gather component names
- var componentWillMountUniqueNames = new Set();
+ var getVersion = source._getVersion;
+ var version = getVersion(source._source);
+ var dispatcher = ReactCurrentDispatcher$1.current; // eslint-disable-next-line prefer-const
- if (pendingComponentWillMountWarnings.length > 0) {
- pendingComponentWillMountWarnings.forEach(function (fiber) {
- componentWillMountUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingComponentWillMountWarnings = [];
- }
+ var _dispatcher$useState = dispatcher.useState(function () {
+ return readFromUnsubscribedMutableSource(root, source, getSnapshot);
+ }),
+ currentSnapshot = _dispatcher$useState[0],
+ setSnapshot = _dispatcher$useState[1];
- var UNSAFE_componentWillMountUniqueNames = new Set();
+ var snapshot = currentSnapshot; // Grab a handle to the state hook as well.
+ // We use it to clear the pending update queue if we have a new source.
- if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
- pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) {
- UNSAFE_componentWillMountUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingUNSAFE_ComponentWillMountWarnings = [];
- }
+ var stateHook = workInProgressHook;
+ var memoizedState = hook.memoizedState;
+ var refs = memoizedState.refs;
+ var prevGetSnapshot = refs.getSnapshot;
+ var prevSource = memoizedState.source;
+ var prevSubscribe = memoizedState.subscribe;
+ var fiber = currentlyRenderingFiber$1;
+ hook.memoizedState = {
+ refs: refs,
+ source: source,
+ subscribe: subscribe
+ }; // Sync the values needed by our subscription handler after each commit.
- var componentWillReceivePropsUniqueNames = new Set();
+ dispatcher.useEffect(
+ function () {
+ refs.getSnapshot = getSnapshot; // Normally the dispatch function for a state hook never changes,
+ // but this hook recreates the queue in certain cases to avoid updates from stale sources.
+ // handleChange() below needs to reference the dispatch function without re-subscribing,
+ // so we use a ref to ensure that it always has the latest version.
- if (pendingComponentWillReceivePropsWarnings.length > 0) {
- pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
- componentWillReceivePropsUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingComponentWillReceivePropsWarnings = [];
- }
+ refs.setSnapshot = setSnapshot; // Check for a possible change between when we last rendered now.
- var UNSAFE_componentWillReceivePropsUniqueNames = new Set();
+ var maybeNewVersion = getVersion(source._source);
- if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
- pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) {
- UNSAFE_componentWillReceivePropsUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
- }
+ if (!objectIs(version, maybeNewVersion)) {
+ var maybeNewSnapshot = getSnapshot(source._source);
- var componentWillUpdateUniqueNames = new Set();
+ {
+ if (typeof maybeNewSnapshot === "function") {
+ error(
+ "Mutable source should not return a function as the snapshot value. " +
+ "Functions may close over mutable values and cause tearing."
+ );
+ }
+ }
- if (pendingComponentWillUpdateWarnings.length > 0) {
- pendingComponentWillUpdateWarnings.forEach(function (fiber) {
- componentWillUpdateUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingComponentWillUpdateWarnings = [];
- }
+ if (!objectIs(snapshot, maybeNewSnapshot)) {
+ setSnapshot(maybeNewSnapshot);
+ var lane = requestUpdateLane(fiber);
+ markRootMutableRead(root, lane);
+ } // If the source mutated between render and now,
+ // there may be state updates already scheduled from the old source.
+ // Entangle the updates so that they render in the same batch.
- var UNSAFE_componentWillUpdateUniqueNames = new Set();
+ markRootEntangled(root, root.mutableReadLanes);
+ }
+ },
+ [getSnapshot, source, subscribe]
+ ); // If we got a new source or subscribe function, re-subscribe in a passive effect.
- if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
- pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) {
- UNSAFE_componentWillUpdateUniqueNames.add(
- getComponentNameFromFiber(fiber) || "Component"
- );
- didWarnAboutUnsafeLifecycles.add(fiber.type);
- });
- pendingUNSAFE_ComponentWillUpdateWarnings = [];
- } // Finally, we flush all the warnings
- // UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
+ dispatcher.useEffect(
+ function () {
+ var handleChange = function () {
+ var latestGetSnapshot = refs.getSnapshot;
+ var latestSetSnapshot = refs.setSnapshot;
- if (UNSAFE_componentWillMountUniqueNames.size > 0) {
- var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames);
+ try {
+ latestSetSnapshot(latestGetSnapshot(source._source)); // Record a pending mutable source update with the same expiration time.
- error(
- "Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
- "\nPlease update the following components: %s",
- sortedNames
- );
- }
+ var lane = requestUpdateLane(fiber);
+ markRootMutableRead(root, lane);
+ } catch (error) {
+ // A selector might throw after a source mutation.
+ // e.g. it might try to read from a part of the store that no longer exists.
+ // In this case we should still schedule an update with React.
+ // Worst case the selector will throw again and then an error boundary will handle it.
+ latestSetSnapshot(function () {
+ throw error;
+ });
+ }
+ };
- if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
- var _sortedNames = setToSortedString(
- UNSAFE_componentWillReceivePropsUniqueNames
- );
+ var unsubscribe = subscribe(source._source, handleChange);
- error(
- "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended " +
- "and may indicate bugs in your code. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move data fetching code or side effects to componentDidUpdate.\n" +
- "* If you're updating state whenever props change, " +
- "refactor your code to use memoization techniques or move it to " +
- "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
- "\nPlease update the following components: %s",
- _sortedNames
- );
- }
+ {
+ if (typeof unsubscribe !== "function") {
+ error(
+ "Mutable source subscribe function must return an unsubscribe function."
+ );
+ }
+ }
- if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
- var _sortedNames2 = setToSortedString(
- UNSAFE_componentWillUpdateUniqueNames
- );
+ return unsubscribe;
+ },
+ [source, subscribe]
+ ); // If any of the inputs to useMutableSource change, reading is potentially unsafe.
+ //
+ // If either the source or the subscription have changed we can't can't trust the update queue.
+ // Maybe the source changed in a way that the old subscription ignored but the new one depends on.
+ //
+ // If the getSnapshot function changed, we also shouldn't rely on the update queue.
+ // It's possible that the underlying source was mutated between the when the last "change" event fired,
+ // and when the current render (with the new getSnapshot function) is processed.
+ //
+ // In both cases, we need to throw away pending updates (since they are no longer relevant)
+ // and treat reading from the source as we do in the mount case.
- error(
- "Using UNSAFE_componentWillUpdate in strict mode is not recommended " +
- "and may indicate bugs in your code. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move data fetching code or side effects to componentDidUpdate.\n" +
- "\nPlease update the following components: %s",
- _sortedNames2
- );
- }
+ if (
+ !objectIs(prevGetSnapshot, getSnapshot) ||
+ !objectIs(prevSource, source) ||
+ !objectIs(prevSubscribe, subscribe)
+ ) {
+ // Create a new queue and setState method,
+ // So if there are interleaved updates, they get pushed to the older queue.
+ // When this becomes current, the previous queue and dispatch method will be discarded,
+ // including any interleaving updates that occur.
+ var newQueue = {
+ pending: null,
+ lanes: NoLanes,
+ dispatch: null,
+ lastRenderedReducer: basicStateReducer,
+ lastRenderedState: snapshot
+ };
+ newQueue.dispatch = setSnapshot = dispatchSetState.bind(
+ null,
+ currentlyRenderingFiber$1,
+ newQueue
+ );
+ stateHook.queue = newQueue;
+ stateHook.baseQueue = null;
+ snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot);
+ stateHook.memoizedState = stateHook.baseState = snapshot;
+ }
- if (componentWillMountUniqueNames.size > 0) {
- var _sortedNames3 = setToSortedString(componentWillMountUniqueNames);
+ return snapshot;
+}
- warn(
- "componentWillMount has been renamed, and is not recommended for use. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
- "* Rename componentWillMount to UNSAFE_componentWillMount to suppress " +
- "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
- "To rename all deprecated lifecycles to their new names, you can run " +
- "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
- "\nPlease update the following components: %s",
- _sortedNames3
- );
- }
+function mountMutableSource(source, getSnapshot, subscribe) {
+ var hook = mountWorkInProgressHook();
+ hook.memoizedState = {
+ refs: {
+ getSnapshot: getSnapshot,
+ setSnapshot: null
+ },
+ source: source,
+ subscribe: subscribe
+ };
+ return useMutableSource(hook, source, getSnapshot, subscribe);
+}
- if (componentWillReceivePropsUniqueNames.size > 0) {
- var _sortedNames4 = setToSortedString(
- componentWillReceivePropsUniqueNames
- );
+function updateMutableSource(source, getSnapshot, subscribe) {
+ var hook = updateWorkInProgressHook();
+ return useMutableSource(hook, source, getSnapshot, subscribe);
+}
- warn(
- "componentWillReceiveProps has been renamed, and is not recommended for use. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move data fetching code or side effects to componentDidUpdate.\n" +
- "* If you're updating state whenever props change, refactor your " +
- "code to use memoization techniques or move it to " +
- "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
- "* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress " +
- "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
- "To rename all deprecated lifecycles to their new names, you can run " +
- "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
- "\nPlease update the following components: %s",
- _sortedNames4
+function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
+ var fiber = currentlyRenderingFiber$1;
+ var hook = mountWorkInProgressHook();
+ var nextSnapshot;
+ var isHydrating = getIsHydrating();
+
+ if (isHydrating) {
+ if (getServerSnapshot === undefined) {
+ throw new Error(
+ "Missing getServerSnapshot, which is required for " +
+ "server-rendered content. Will revert to client rendering."
);
}
- if (componentWillUpdateUniqueNames.size > 0) {
- var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames);
+ nextSnapshot = getServerSnapshot();
- warn(
- "componentWillUpdate has been renamed, and is not recommended for use. " +
- "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
- "* Move data fetching code or side effects to componentDidUpdate.\n" +
- "* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress " +
- "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
- "To rename all deprecated lifecycles to their new names, you can run " +
- "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
- "\nPlease update the following components: %s",
- _sortedNames5
- );
+ {
+ if (!didWarnUncachedGetSnapshot) {
+ if (nextSnapshot !== getServerSnapshot()) {
+ error(
+ "The result of getServerSnapshot should be cached to avoid an infinite loop"
+ );
+
+ didWarnUncachedGetSnapshot = true;
+ }
+ }
}
- };
+ } else {
+ nextSnapshot = getSnapshot();
- var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about.
+ {
+ if (!didWarnUncachedGetSnapshot) {
+ var cachedSnapshot = getSnapshot();
- var didWarnAboutLegacyContext = new Set();
+ if (!objectIs(nextSnapshot, cachedSnapshot)) {
+ error(
+ "The result of getSnapshot should be cached to avoid an infinite loop"
+ );
- ReactStrictModeWarnings.recordLegacyContextWarning = function (
- fiber,
- instance
- ) {
- var strictRoot = findStrictRoot(fiber);
+ didWarnUncachedGetSnapshot = true;
+ }
+ }
+ } // Unless we're rendering a blocking lane, schedule a consistency check.
+ // Right before committing, we will walk the tree and check if any of the
+ // stores were mutated.
+ //
+ // We won't do this if we're hydrating server-rendered content, because if
+ // the content is stale, it's already visible anyway. Instead we'll patch
+ // it up in a passive effect.
- if (strictRoot === null) {
- error(
- "Expected to find a StrictMode component in a strict mode tree. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
+ var root = getWorkInProgressRoot();
- return;
- } // Dedup strategy: Warn once per component.
+ if (root === null) {
+ throw new Error(
+ "Expected a work-in-progress root. This is a bug in React. Please file an issue."
+ );
+ }
- if (didWarnAboutLegacyContext.has(fiber.type)) {
- return;
+ if (!includesBlockingLane(root, renderLanes$1)) {
+ pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
}
+ } // Read the current snapshot from the store on every render. This breaks the
+ // normal rules of React, and only works because store updates are
+ // always synchronous.
- var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
+ hook.memoizedState = nextSnapshot;
+ var inst = {
+ value: nextSnapshot,
+ getSnapshot: getSnapshot
+ };
+ hook.queue = inst; // Schedule an effect to subscribe to the store.
- if (
- fiber.type.contextTypes != null ||
- fiber.type.childContextTypes != null ||
- (instance !== null && typeof instance.getChildContext === "function")
- ) {
- if (warningsForRoot === undefined) {
- warningsForRoot = [];
- pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
- }
+ mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Schedule an effect to update the mutable instance fields. We will update
+ // this whenever subscribe, getSnapshot, or value changes. Because there's no
+ // clean-up function, and we track the deps correctly, we can call pushEffect
+ // directly, without storing any additional state. For the same reason, we
+ // don't need to set a static flag, either.
+ // TODO: We can move this to the passive phase once we add a pre-commit
+ // consistency check. See the next comment.
- warningsForRoot.push(fiber);
- }
- };
+ fiber.flags |= Passive$1;
+ pushEffect(
+ HasEffect | Passive,
+ updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
+ undefined,
+ null
+ );
+ return nextSnapshot;
+}
- ReactStrictModeWarnings.flushLegacyContextWarning = function () {
- pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
- if (fiberArray.length === 0) {
- return;
- }
+function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
+ var fiber = currentlyRenderingFiber$1;
+ var hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the
+ // normal rules of React, and only works because store updates are
+ // always synchronous.
- var firstFiber = fiberArray[0];
- var uniqueNames = new Set();
- fiberArray.forEach(function (fiber) {
- uniqueNames.add(getComponentNameFromFiber(fiber) || "Component");
- didWarnAboutLegacyContext.add(fiber.type);
- });
- var sortedNames = setToSortedString(uniqueNames);
+ var nextSnapshot = getSnapshot();
- try {
- setCurrentFiber(firstFiber);
+ {
+ if (!didWarnUncachedGetSnapshot) {
+ var cachedSnapshot = getSnapshot();
+ if (!objectIs(nextSnapshot, cachedSnapshot)) {
error(
- "Legacy context API has been detected within a strict-mode tree." +
- "\n\nThe old API will be supported in all 16.x releases, but applications " +
- "using it should migrate to the new version." +
- "\n\nPlease update the following components: %s" +
- "\n\nLearn more about this warning here: https://reactjs.org/link/legacy-context",
- sortedNames
+ "The result of getSnapshot should be cached to avoid an infinite loop"
);
- } finally {
- resetCurrentFiber();
- }
- });
- };
-
- ReactStrictModeWarnings.discardPendingWarnings = function () {
- pendingComponentWillMountWarnings = [];
- pendingUNSAFE_ComponentWillMountWarnings = [];
- pendingComponentWillReceivePropsWarnings = [];
- pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
- pendingComponentWillUpdateWarnings = [];
- pendingUNSAFE_ComponentWillUpdateWarnings = [];
- pendingLegacyContextWarning = new Map();
- };
-}
-
-var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we
-// detect this is caught by userspace, we'll log a warning in development.
-var SuspenseException = new Error(
- "Suspense Exception: This is not a real error! It's an implementation " +
- "detail of `use` to interrupt the current render. You must either " +
- "rethrow it immediately, or move the `use` call outside of the " +
- "`try/catch` block. Capturing without rethrowing will lead to " +
- "unexpected behavior.\n\n" +
- "To handle async errors, wrap your component in an error boundary, or " +
- "call the promise's `.catch` method and pass the result to `use`"
-);
-function createThenableState() {
- // The ThenableState is created the first time a component suspends. If it
- // suspends again, we'll reuse the same state.
- return [];
-}
-function isThenableResolved(thenable) {
- var status = thenable.status;
- return status === "fulfilled" || status === "rejected";
-}
+ didWarnUncachedGetSnapshot = true;
+ }
+ }
+ }
-function noop() {}
+ var prevSnapshot = (currentHook || hook).memoizedState;
+ var snapshotChanged = !objectIs(prevSnapshot, nextSnapshot);
-function trackUsedThenable(thenableState, thenable, index) {
- if (ReactCurrentActQueue$2.current !== null) {
- ReactCurrentActQueue$2.didUsePromise = true;
+ if (snapshotChanged) {
+ hook.memoizedState = nextSnapshot;
+ markWorkInProgressReceivedUpdate();
}
- var previous = thenableState[index];
+ var inst = hook.queue;
+ updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [
+ subscribe
+ ]); // Whenever getSnapshot or subscribe changes, we need to check in the
+ // commit phase if there was an interleaved mutation. In concurrent mode
+ // this can happen all the time, but even in synchronous mode, an earlier
+ // effect may have mutated the store.
- if (previous === undefined) {
- thenableState.push(thenable);
- } else {
- if (previous !== thenable) {
- // Reuse the previous thenable, and drop the new one. We can assume
- // they represent the same value, because components are idempotent.
- // Avoid an unhandled rejection errors for the Promises that we'll
- // intentionally ignore.
- thenable.then(noop, noop);
- thenable = previous;
- }
- } // We use an expando to track the status and result of a thenable so that we
- // can synchronously unwrap the value. Think of this as an extension of the
- // Promise API, or a custom interface that is a superset of Thenable.
- //
- // If the thenable doesn't have a status, set it to "pending" and attach
- // a listener that will update its status and result when it resolves.
+ if (
+ inst.getSnapshot !== getSnapshot ||
+ snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
+ // checking whether we scheduled a subscription effect above.
+ (workInProgressHook !== null &&
+ workInProgressHook.memoizedState.tag & HasEffect)
+ ) {
+ fiber.flags |= Passive$1;
+ pushEffect(
+ HasEffect | Passive,
+ updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
+ undefined,
+ null
+ ); // Unless we're rendering a blocking lane, schedule a consistency check.
+ // Right before committing, we will walk the tree and check if any of the
+ // stores were mutated.
- switch (thenable.status) {
- case "fulfilled": {
- var fulfilledValue = thenable.value;
- return fulfilledValue;
- }
+ var root = getWorkInProgressRoot();
- case "rejected": {
- var rejectedError = thenable.reason;
- throw rejectedError;
+ if (root === null) {
+ throw new Error(
+ "Expected a work-in-progress root. This is a bug in React. Please file an issue."
+ );
}
- default: {
- if (typeof thenable.status === "string") {
- // Only instrument the thenable if the status if not defined. If
- // it's defined, but an unknown value, assume it's been instrumented by
- // some custom userspace implementation. We treat it as "pending".
- // Attach a dummy listener, to ensure that any lazy initialization can
- // happen. Flight lazily parses JSON when the value is actually awaited.
- thenable.then(noop, noop);
- } else {
- var pendingThenable = thenable;
- pendingThenable.status = "pending";
- pendingThenable.then(
- function (fulfilledValue) {
- if (thenable.status === "pending") {
- var fulfilledThenable = thenable;
- fulfilledThenable.status = "fulfilled";
- fulfilledThenable.value = fulfilledValue;
- }
- },
- function (error) {
- if (thenable.status === "pending") {
- var rejectedThenable = thenable;
- rejectedThenable.status = "rejected";
- rejectedThenable.reason = error;
- }
- }
- );
- } // Check one more time in case the thenable resolved synchronously.
-
- switch (thenable.status) {
- case "fulfilled": {
- var fulfilledThenable = thenable;
- return fulfilledThenable.value;
- }
+ if (!includesBlockingLane(root, renderLanes$1)) {
+ pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
+ }
+ }
- case "rejected": {
- var rejectedThenable = thenable;
- throw rejectedThenable.reason;
- }
- } // Suspend.
- //
- // Throwing here is an implementation detail that allows us to unwind the
- // call stack. But we shouldn't allow it to leak into userspace. Throw an
- // opaque placeholder value instead of the actual thenable. If it doesn't
- // get captured by the work loop, log a warning, because that means
- // something in userspace must have caught it.
+ return nextSnapshot;
+}
- suspendedThenable = thenable;
+function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
+ fiber.flags |= StoreConsistency;
+ var check = {
+ getSnapshot: getSnapshot,
+ value: renderedSnapshot
+ };
+ var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
- {
- needsToResetSuspendedThenableDEV = true;
- }
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
+ componentUpdateQueue.stores = [check];
+ } else {
+ var stores = componentUpdateQueue.stores;
- throw SuspenseException;
+ if (stores === null) {
+ componentUpdateQueue.stores = [check];
+ } else {
+ stores.push(check);
}
}
-} // This is used to track the actual thenable that suspended so it can be
-// passed to the rest of the Suspense implementation — which, for historical
-// reasons, expects to receive a thenable.
-
-var suspendedThenable = null;
-var needsToResetSuspendedThenableDEV = false;
-function getSuspendedThenable() {
- // This is called right after `use` suspends by throwing an exception. `use`
- // throws an opaque value instead of the thenable itself so that it can't be
- // caught in userspace. Then the work loop accesses the actual thenable using
- // this function.
- if (suspendedThenable === null) {
- throw new Error(
- "Expected a suspended thenable. This is a bug in React. Please file " +
- "an issue."
- );
- }
+}
- var thenable = suspendedThenable;
- suspendedThenable = null;
+function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
+ // These are updated in the passive phase
+ inst.value = nextSnapshot;
+ inst.getSnapshot = getSnapshot; // Something may have been mutated in between render and commit. This could
+ // have been in an event that fired before the passive effects, or it could
+ // have been in a layout effect. In that case, we would have used the old
+ // snapsho and getSnapshot values to bail out. We need to check one more time.
- {
- needsToResetSuspendedThenableDEV = false;
+ if (checkIfSnapshotChanged(inst)) {
+ // Force a re-render.
+ forceStoreRerender(fiber);
}
-
- return thenable;
}
-function checkIfUseWrappedInTryCatch() {
- {
- // This was set right before SuspenseException was thrown, and it should
- // have been cleared when the exception was handled. If it wasn't,
- // it must have been caught by userspace.
- if (needsToResetSuspendedThenableDEV) {
- needsToResetSuspendedThenableDEV = false;
- return true;
+
+function subscribeToStore(fiber, inst, subscribe) {
+ var handleStoreChange = function () {
+ // The store changed. Check if the snapshot changed since the last time we
+ // read from the store.
+ if (checkIfSnapshotChanged(inst)) {
+ // Force a re-render.
+ forceStoreRerender(fiber);
}
- }
+ }; // Subscribe to the store and return a clean-up function.
- return false;
+ return subscribe(handleStoreChange);
}
-var thenableState$1 = null;
-var thenableIndexCounter$1 = 0;
-var didWarnAboutMaps;
-var didWarnAboutGenerators;
-var didWarnAboutStringRefs;
-var ownerHasKeyUseWarning;
-var ownerHasFunctionTypeWarning;
-
-var warnForMissingKey = function (child, returnFiber) {};
+function checkIfSnapshotChanged(inst) {
+ var latestGetSnapshot = inst.getSnapshot;
+ var prevValue = inst.value;
-{
- didWarnAboutMaps = false;
- didWarnAboutGenerators = false;
- didWarnAboutStringRefs = {};
- /**
- * Warn if there's no key explicitly set on dynamic arrays of children or
- * object keys are not valid. This allows us to keep track of children between
- * updates.
- */
-
- ownerHasKeyUseWarning = {};
- ownerHasFunctionTypeWarning = {};
-
- warnForMissingKey = function (child, returnFiber) {
- if (child === null || typeof child !== "object") {
- return;
- }
-
- if (!child._store || child._store.validated || child.key != null) {
- return;
- }
+ try {
+ var nextValue = latestGetSnapshot();
+ return !objectIs(prevValue, nextValue);
+ } catch (error) {
+ return true;
+ }
+}
- if (typeof child._store !== "object") {
- throw new Error(
- "React Component in warnForMissingKey should have a _store. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- } // $FlowFixMe unable to narrow type from mixed to writable object
+function forceStoreRerender(fiber) {
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- child._store.validated = true;
- var componentName = getComponentNameFromFiber(returnFiber) || "Component";
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+}
- if (ownerHasKeyUseWarning[componentName]) {
- return;
- }
+function mountState(initialState) {
+ var hook = mountWorkInProgressHook();
- ownerHasKeyUseWarning[componentName] = true;
+ if (typeof initialState === "function") {
+ // $FlowFixMe: Flow doesn't like mixed types
+ initialState = initialState();
+ }
- error(
- "Each child in a list should have a unique " +
- '"key" prop. See https://reactjs.org/link/warning-keys for ' +
- "more information."
- );
+ hook.memoizedState = hook.baseState = initialState;
+ var queue = {
+ pending: null,
+ lanes: NoLanes,
+ dispatch: null,
+ lastRenderedReducer: basicStateReducer,
+ lastRenderedState: initialState
};
+ hook.queue = queue;
+ var dispatch = (queue.dispatch = dispatchSetState.bind(
+ null,
+ currentlyRenderingFiber$1,
+ queue
+ ));
+ return [hook.memoizedState, dispatch];
}
-function isReactClass(type) {
- return type.prototype && type.prototype.isReactComponent;
+function updateState(initialState) {
+ return updateReducer(basicStateReducer);
}
-function unwrapThenable(thenable) {
- var index = thenableIndexCounter$1;
- thenableIndexCounter$1 += 1;
-
- if (thenableState$1 === null) {
- thenableState$1 = createThenableState();
- }
-
- return trackUsedThenable(thenableState$1, thenable, index);
+function rerenderState(initialState) {
+ return rerenderReducer(basicStateReducer);
}
-function coerceRef(returnFiber, current, element) {
- var mixedRef = element.ref;
-
- if (
- mixedRef !== null &&
- typeof mixedRef !== "function" &&
- typeof mixedRef !== "object"
- ) {
- {
- if (
- // We warn in ReactElement.js if owner and self are equal for string refs
- // because these cannot be automatically converted to an arrow function
- // using a codemod. Therefore, we don't have to warn about string refs again.
- !(
- element._owner &&
- element._self &&
- element._owner.stateNode !== element._self
- ) && // Will already throw with "Function components cannot have string refs"
- !(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs"
- !(typeof element.type === "function" && !isReactClass(element.type)) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
- element._owner
- ) {
- var componentName =
- getComponentNameFromFiber(returnFiber) || "Component";
+function pushEffect(tag, create, destroy, deps) {
+ var effect = {
+ tag: tag,
+ create: create,
+ destroy: destroy,
+ deps: deps,
+ // Circular
+ next: null
+ };
+ var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
- if (!didWarnAboutStringRefs[componentName]) {
- error(
- 'Component "%s" contains the string ref "%s". Support for string refs ' +
- "will be removed in a future major release. We recommend using " +
- "useRef() or createRef() instead. " +
- "Learn more about using refs safely here: " +
- "https://reactjs.org/link/strict-mode-string-ref",
- componentName,
- mixedRef
- );
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var lastEffect = componentUpdateQueue.lastEffect;
- didWarnAboutStringRefs[componentName] = true;
- }
- }
+ if (lastEffect === null) {
+ componentUpdateQueue.lastEffect = effect.next = effect;
+ } else {
+ var firstEffect = lastEffect.next;
+ lastEffect.next = effect;
+ effect.next = firstEffect;
+ componentUpdateQueue.lastEffect = effect;
}
+ }
- if (element._owner) {
- var owner = element._owner;
- var inst;
+ return effect;
+}
- if (owner) {
- var ownerFiber = owner;
+var stackContainsErrorMessage = null;
- if (ownerFiber.tag !== ClassComponent) {
- throw new Error(
- "Function components cannot have string refs. " +
- "We recommend using useRef() instead. " +
- "Learn more about using refs safely here: " +
- "https://reactjs.org/link/strict-mode-string-ref"
- );
- }
+function getCallerStackFrame() {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ var stackFrames = new Error("Error message").stack.split("\n"); // Some browsers (e.g. Chrome) include the error message in the stack
+ // but others (e.g. Firefox) do not.
- inst = ownerFiber.stateNode;
- }
+ if (stackContainsErrorMessage === null) {
+ stackContainsErrorMessage = stackFrames[0].includes("Error message");
+ }
- if (!inst) {
- throw new Error(
- "Missing owner for string ref " +
- mixedRef +
- ". This error is likely caused by a " +
- "bug in React. Please file an issue."
- );
- } // Assigning this to a const so Flow knows it won't change in the closure
+ return stackContainsErrorMessage
+ ? stackFrames.slice(3, 4).join("\n")
+ : stackFrames.slice(2, 3).join("\n");
+}
- var resolvedInst = inst;
+function mountRef(initialValue) {
+ var hook = mountWorkInProgressHook();
- {
- checkPropStringCoercion(mixedRef, "ref");
- }
+ if (enableUseRefAccessWarning) {
+ {
+ // Support lazy initialization pattern shown in docs.
+ // We need to store the caller stack frame so that we don't warn on subsequent renders.
+ var hasBeenInitialized = initialValue != null;
+ var lazyInitGetterStack = null;
+ var didCheckForLazyInit = false; // Only warn once per component+hook.
- var stringRef = "" + mixedRef; // Check if previous string ref matches new string ref
+ var didWarnAboutRead = false;
+ var didWarnAboutWrite = false;
+ var current = initialValue;
+ var ref = {
+ get current() {
+ if (!hasBeenInitialized) {
+ didCheckForLazyInit = true;
+ lazyInitGetterStack = getCallerStackFrame();
+ } else if (currentlyRenderingFiber$1 !== null && !didWarnAboutRead) {
+ if (
+ lazyInitGetterStack === null ||
+ lazyInitGetterStack !== getCallerStackFrame()
+ ) {
+ didWarnAboutRead = true;
- if (
- current !== null &&
- current.ref !== null &&
- typeof current.ref === "function" &&
- current.ref._stringRef === stringRef
- ) {
- return current.ref;
- }
+ warn(
+ "%s: Unsafe read of a mutable value during render.\n\n" +
+ "Reading from a ref during render is only safe if:\n" +
+ "1. The ref value has not been updated, or\n" +
+ "2. The ref holds a lazily-initialized value that is only set once.\n",
+ getComponentNameFromFiber(currentlyRenderingFiber$1) ||
+ "Unknown"
+ );
+ }
+ }
- var ref = function (value) {
- var refs = resolvedInst.refs;
+ return current;
+ },
- if (value === null) {
- delete refs[stringRef];
- } else {
- refs[stringRef] = value;
+ set current(value) {
+ if (currentlyRenderingFiber$1 !== null && !didWarnAboutWrite) {
+ if (hasBeenInitialized || !didCheckForLazyInit) {
+ didWarnAboutWrite = true;
+
+ warn(
+ "%s: Unsafe write of a mutable value during render.\n\n" +
+ "Writing to a ref during render is only safe if the ref holds " +
+ "a lazily-initialized value that is only set once.\n",
+ getComponentNameFromFiber(currentlyRenderingFiber$1) ||
+ "Unknown"
+ );
+ }
+ }
+
+ hasBeenInitialized = true;
+ current = value;
}
};
-
- ref._stringRef = stringRef;
+ Object.seal(ref);
+ hook.memoizedState = ref;
return ref;
- } else {
- if (typeof mixedRef !== "string") {
- throw new Error(
- "Expected ref to be a function, a string, an object returned by React.createRef(), or null."
- );
- }
-
- if (!element._owner) {
- throw new Error(
- "Element ref was specified as a string (" +
- mixedRef +
- ") but no owner was set. This could happen for one of" +
- " the following reasons:\n" +
- "1. You may be adding a ref to a function component\n" +
- "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
- "3. You have multiple copies of React loaded\n" +
- "See https://reactjs.org/link/refs-must-have-owner for more information."
- );
- }
}
+ } else {
+ var _ref2 = {
+ current: initialValue
+ };
+ hook.memoizedState = _ref2;
+ return _ref2;
}
+}
- return mixedRef;
+function updateRef(initialValue) {
+ var hook = updateWorkInProgressHook();
+ return hook.memoizedState;
}
-function throwOnInvalidObjectType(returnFiber, newChild) {
- // $FlowFixMe[method-unbinding]
- var childString = Object.prototype.toString.call(newChild);
- throw new Error(
- "Objects are not valid as a React child (found: " +
- (childString === "[object Object]"
- ? "object with keys {" + Object.keys(newChild).join(", ") + "}"
- : childString) +
- "). " +
- "If you meant to render a collection of children, use an array " +
- "instead."
+function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ currentlyRenderingFiber$1.flags |= fiberFlags;
+ hook.memoizedState = pushEffect(
+ HasEffect | hookFlags,
+ create,
+ undefined,
+ nextDeps
);
}
-function warnOnFunctionType(returnFiber) {
- {
- var componentName = getComponentNameFromFiber(returnFiber) || "Component";
+function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var destroy = undefined; // currentHook is null when rerendering after a render phase state update.
- if (ownerHasFunctionTypeWarning[componentName]) {
- return;
+ if (currentHook !== null) {
+ var prevEffect = currentHook.memoizedState;
+ destroy = prevEffect.destroy;
+
+ if (nextDeps !== null) {
+ var prevDeps = prevEffect.deps;
+
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
+ return;
+ }
}
+ }
- ownerHasFunctionTypeWarning[componentName] = true;
+ currentlyRenderingFiber$1.flags |= fiberFlags;
+ hook.memoizedState = pushEffect(
+ HasEffect | hookFlags,
+ create,
+ destroy,
+ nextDeps
+ );
+}
- error(
- "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."
+function mountEffect(create, deps) {
+ if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
+ mountEffectImpl(
+ MountPassiveDev | Passive$1 | PassiveStatic,
+ Passive,
+ create,
+ deps
);
+ } else {
+ mountEffectImpl(Passive$1 | PassiveStatic, Passive, create, deps);
}
}
-function resolveLazy(lazyType) {
- var payload = lazyType._payload;
- var init = lazyType._init;
- return init(payload);
-} // This wrapper function exists because I expect to clone the code in each path
-// to be able to optimize each path individually by branching early. This needs
-// a compiler or we can do it manually. Helpers that don't need this branching
-// live outside of this function.
+function updateEffect(create, deps) {
+ updateEffectImpl(Passive$1, Passive, create, deps);
+}
-function createChildReconciler(shouldTrackSideEffects) {
- function deleteChild(returnFiber, childToDelete) {
- if (!shouldTrackSideEffects) {
- // Noop.
- return;
- }
+function useEffectEventImpl(payload) {
+ currentlyRenderingFiber$1.flags |= Update;
+ var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
- var deletions = returnFiber.deletions;
+ if (componentUpdateQueue === null) {
+ componentUpdateQueue = createFunctionComponentUpdateQueue();
+ currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
+ componentUpdateQueue.events = [payload];
+ } else {
+ var events = componentUpdateQueue.events;
- if (deletions === null) {
- returnFiber.deletions = [childToDelete];
- returnFiber.flags |= ChildDeletion;
+ if (events === null) {
+ componentUpdateQueue.events = [payload];
} else {
- deletions.push(childToDelete);
+ events.push(payload);
}
}
+}
- 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;
+function mountEvent(callback) {
+ var hook = mountWorkInProgressHook();
+ var ref = {
+ impl: callback
+ };
+ hook.memoizedState = ref; // $FlowIgnore[incompatible-return]
- while (childToDelete !== null) {
- deleteChild(returnFiber, childToDelete);
- childToDelete = childToDelete.sibling;
+ return function eventFn() {
+ if (isInvalidExecutionContextForEventFunction()) {
+ throw new Error(
+ "A function wrapped in useEffectEvent can't be called during rendering."
+ );
}
- return null;
- }
-
- function mapRemainingChildren(returnFiber, currentFirstChild) {
- // Add the remaining children to a temporary map so that we can find them by
- // keys quickly. Implicit (null) keys get added to this set with their index
- // instead.
- var existingChildren = new Map();
- var existingChild = currentFirstChild;
+ return ref.impl.apply(undefined, arguments);
+ };
+}
- while (existingChild !== null) {
- if (existingChild.key !== null) {
- existingChildren.set(existingChild.key, existingChild);
- } else {
- existingChildren.set(existingChild.index, existingChild);
- }
+function updateEvent(callback) {
+ var hook = updateWorkInProgressHook();
+ var ref = hook.memoizedState;
+ useEffectEventImpl({
+ ref: ref,
+ nextImpl: callback
+ }); // $FlowIgnore[incompatible-return]
- existingChild = existingChild.sibling;
+ return function eventFn() {
+ if (isInvalidExecutionContextForEventFunction()) {
+ throw new Error(
+ "A function wrapped in useEffectEvent can't be called during rendering."
+ );
}
- return existingChildren;
- }
+ return ref.impl.apply(undefined, arguments);
+ };
+}
- function useFiber(fiber, pendingProps) {
- // We currently set sibling to null and index to 0 here because it is easy
- // to forget to do before returning it. E.g. for the single child case.
- var clone = createWorkInProgress(fiber, pendingProps);
- clone.index = 0;
- clone.sibling = null;
- return clone;
- }
+function mountInsertionEffect(create, deps) {
+ mountEffectImpl(Update, Insertion, create, deps);
+}
- function placeChild(newFiber, lastPlacedIndex, newIndex) {
- newFiber.index = newIndex;
+function updateInsertionEffect(create, deps) {
+ return updateEffectImpl(Update, Insertion, create, deps);
+}
- if (!shouldTrackSideEffects) {
- // During hydration, the useId algorithm needs to know which fibers are
- // part of a list of children (arrays, iterators).
- newFiber.flags |= Forked;
- return lastPlacedIndex;
- }
+function mountLayoutEffect(create, deps) {
+ var fiberFlags = Update | LayoutStatic;
- var current = newFiber.alternate;
+ if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
+ fiberFlags |= MountLayoutDev;
+ }
- if (current !== null) {
- var oldIndex = current.index;
+ return mountEffectImpl(fiberFlags, Layout, create, deps);
+}
- if (oldIndex < lastPlacedIndex) {
- // This is a move.
- newFiber.flags |= Placement | PlacementDEV;
- return lastPlacedIndex;
- } else {
- // This item can stay in place.
- return oldIndex;
+function updateLayoutEffect(create, deps) {
+ return updateEffectImpl(Update, Layout, create, deps);
+}
+
+function imperativeHandleEffect(create, ref) {
+ if (typeof ref === "function") {
+ var refCallback = ref;
+ var inst = create();
+ refCallback(inst);
+ return function () {
+ refCallback(null);
+ };
+ } else if (ref !== null && ref !== undefined) {
+ var refObject = ref;
+
+ {
+ if (!refObject.hasOwnProperty("current")) {
+ error(
+ "Expected useImperativeHandle() first argument to either be a " +
+ "ref callback or React.createRef() object. Instead received: %s.",
+ "an object with keys {" + Object.keys(refObject).join(", ") + "}"
+ );
}
- } else {
- // This is an insertion.
- newFiber.flags |= Placement | PlacementDEV;
- return lastPlacedIndex;
}
- }
- function placeSingleChild(newFiber) {
- // This is simpler for the single child case. We only need to do a
- // placement for inserting new children.
- if (shouldTrackSideEffects && newFiber.alternate === null) {
- newFiber.flags |= Placement | PlacementDEV;
- }
+ var _inst = create();
- return newFiber;
+ refObject.current = _inst;
+ return function () {
+ refObject.current = null;
+ };
}
+}
- function updateTextNode(returnFiber, current, textContent, lanes) {
- if (current === null || current.tag !== HostText) {
- // Insert
- var created = createFiberFromText(textContent, returnFiber.mode, lanes);
- created.return = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, textContent);
- existing.return = returnFiber;
- return existing;
+function mountImperativeHandle(ref, create, deps) {
+ {
+ if (typeof create !== "function") {
+ error(
+ "Expected useImperativeHandle() second argument to be a function " +
+ "that creates a handle. Instead received: %s.",
+ create !== null ? typeof create : "null"
+ );
}
+ } // TODO: If deps are provided, should we skip comparing the ref itself?
+
+ var effectDeps =
+ deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+ var fiberFlags = Update | LayoutStatic;
+
+ if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
+ fiberFlags |= MountLayoutDev;
}
- function updateElement(returnFiber, current, element, lanes) {
- var elementType = element.type;
+ mountEffectImpl(
+ fiberFlags,
+ Layout,
+ imperativeHandleEffect.bind(null, create, ref),
+ effectDeps
+ );
+}
- if (elementType === REACT_FRAGMENT_TYPE) {
- return updateFragment(
- returnFiber,
- current,
- element.props.children,
- lanes,
- element.key
+function updateImperativeHandle(ref, create, deps) {
+ {
+ if (typeof create !== "function") {
+ error(
+ "Expected useImperativeHandle() second argument to be a function " +
+ "that creates a handle. Instead received: %s.",
+ create !== null ? typeof create : "null"
);
}
+ } // TODO: If deps are provided, should we skip comparing the ref itself?
- if (current !== null) {
- if (
- current.elementType === elementType || // Keep this check inline so it only runs on the false path:
- isCompatibleFamilyForHotReloading(current, element) || // Lazy types should reconcile their resolved type.
- // We need to do this after the Hot Reloading check above,
- // because hot reloading has different semantics than prod because
- // it doesn't resuspend. So we can't let the call below suspend.
- (typeof elementType === "object" &&
- elementType !== null &&
- elementType.$$typeof === REACT_LAZY_TYPE &&
- resolveLazy(elementType) === current.type)
- ) {
- // Move based on index
- var existing = useFiber(current, element.props);
- existing.ref = coerceRef(returnFiber, current, element);
- existing.return = returnFiber;
+ var effectDeps =
+ deps !== null && deps !== undefined ? deps.concat([ref]) : null;
+ updateEffectImpl(
+ Update,
+ Layout,
+ imperativeHandleEffect.bind(null, create, ref),
+ effectDeps
+ );
+}
- {
- existing._debugSource = element._source;
- existing._debugOwner = element._owner;
- }
+function mountDebugValue(value, formatterFn) {
+ // This hook is normally a no-op.
+ // The react-debug-hooks package injects its own implementation
+ // so that e.g. DevTools can display custom hook values.
+}
- return existing;
- }
- } // Insert
+var updateDebugValue = mountDebugValue;
- var created = createFiberFromElement(element, returnFiber.mode, lanes);
- created.ref = coerceRef(returnFiber, current, element);
- created.return = returnFiber;
- return created;
- }
+function mountCallback(callback, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
- function updatePortal(returnFiber, current, portal, lanes) {
- if (
- current === null ||
- current.tag !== HostPortal ||
- current.stateNode.containerInfo !== portal.containerInfo ||
- current.stateNode.implementation !== portal.implementation
- ) {
- // Insert
- var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
- created.return = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, portal.children || []);
- existing.return = returnFiber;
- return existing;
- }
- }
+function updateCallback(callback, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState;
- function updateFragment(returnFiber, current, fragment, lanes, key) {
- if (current === null || current.tag !== Fragment) {
- // Insert
- var created = createFiberFromFragment(
- fragment,
- returnFiber.mode,
- lanes,
- key
- );
- created.return = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, fragment);
- existing.return = returnFiber;
- return existing;
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
+
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
}
}
- function createChild(returnFiber, newChild, lanes) {
- if (
- (typeof newChild === "string" && newChild !== "") ||
- typeof newChild === "number"
- ) {
- // Text nodes don't have keys. If the previous node is implicitly keyed
- // we can continue to replace it without aborting even if it is not a text
- // node.
- var created = createFiberFromText("" + newChild, returnFiber.mode, lanes);
- created.return = returnFiber;
- return created;
- }
+ hook.memoizedState = [callback, nextDeps];
+ return callback;
+}
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- var _created = createFiberFromElement(
- newChild,
- returnFiber.mode,
- lanes
- );
+function mountMemo(nextCreate, deps) {
+ var hook = mountWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
- _created.ref = coerceRef(returnFiber, null, newChild);
- _created.return = returnFiber;
- return _created;
- }
+ if (shouldDoubleInvokeUserFnsInHooksDEV) {
+ nextCreate();
+ }
- case REACT_PORTAL_TYPE: {
- var _created2 = createFiberFromPortal(
- newChild,
- returnFiber.mode,
- lanes
- );
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
- _created2.return = returnFiber;
- return _created2;
- }
+function updateMemo(nextCreate, deps) {
+ var hook = updateWorkInProgressHook();
+ var nextDeps = deps === undefined ? null : deps;
+ var prevState = hook.memoizedState; // Assume these are defined. If they're not, areHookInputsEqual will warn.
- case REACT_LAZY_TYPE: {
- var payload = newChild._payload;
- var init = newChild._init;
- return createChild(returnFiber, init(payload), lanes);
- }
- }
+ if (nextDeps !== null) {
+ var prevDeps = prevState[1];
- if (isArray(newChild) || getIteratorFn(newChild)) {
- var _created3 = createFiberFromFragment(
- newChild,
- returnFiber.mode,
- lanes,
- null
- );
+ if (areHookInputsEqual(nextDeps, prevDeps)) {
+ return prevState[0];
+ }
+ }
- _created3.return = returnFiber;
- return _created3;
- } // Usable node types
- //
- // Unwrap the inner value and recursively call this function again.
+ if (shouldDoubleInvokeUserFnsInHooksDEV) {
+ nextCreate();
+ }
- if (typeof newChild.then === "function") {
- var thenable = newChild;
- return createChild(returnFiber, unwrapThenable(thenable), lanes);
- }
+ var nextValue = nextCreate();
+ hook.memoizedState = [nextValue, nextDeps];
+ return nextValue;
+}
- if (
- newChild.$$typeof === REACT_CONTEXT_TYPE ||
- newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
- ) {
- var context = newChild;
- return createChild(
- returnFiber,
- readContextDuringReconcilation(returnFiber, context, lanes),
- lanes
- );
- }
+function mountDeferredValue(value) {
+ var hook = mountWorkInProgressHook();
+ hook.memoizedState = value;
+ return value;
+}
- throwOnInvalidObjectType(returnFiber, newChild);
- }
+function updateDeferredValue(value) {
+ var hook = updateWorkInProgressHook();
+ var resolvedCurrentHook = currentHook;
+ var prevValue = resolvedCurrentHook.memoizedState;
+ return updateDeferredValueImpl(hook, prevValue, value);
+}
- {
- if (typeof newChild === "function") {
- warnOnFunctionType(returnFiber);
- }
- }
+function rerenderDeferredValue(value) {
+ var hook = updateWorkInProgressHook();
- return null;
+ if (currentHook === null) {
+ // This is a rerender during a mount.
+ hook.memoizedState = value;
+ return value;
+ } else {
+ // This is a rerender during an update.
+ var prevValue = currentHook.memoizedState;
+ return updateDeferredValueImpl(hook, prevValue, value);
}
+}
- function updateSlot(returnFiber, oldFiber, newChild, lanes) {
- // Update the fiber if the keys match, otherwise return null.
- var key = oldFiber !== null ? oldFiber.key : null;
+function updateDeferredValueImpl(hook, prevValue, value) {
+ var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes$1);
- if (
- (typeof newChild === "string" && newChild !== "") ||
- typeof newChild === "number"
- ) {
- // Text nodes don't have keys. If the previous node is implicitly keyed
- // we can continue to replace it without aborting even if it is not a text
- // node.
- if (key !== null) {
- return null;
- }
+ if (shouldDeferValue) {
+ // This is an urgent update. If the value has changed, keep using the
+ // previous value and spawn a deferred render to update it later.
+ if (!objectIs(value, prevValue)) {
+ // Schedule a deferred render
+ var deferredLane = claimNextTransitionLane();
+ currentlyRenderingFiber$1.lanes = mergeLanes(
+ currentlyRenderingFiber$1.lanes,
+ deferredLane
+ );
+ markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent
+ // from the latest value. The name "baseState" doesn't really match how we
+ // use it because we're reusing a state hook field instead of creating a
+ // new one.
- return updateTextNode(returnFiber, oldFiber, "" + newChild, lanes);
- }
+ hook.baseState = true;
+ } // Reuse the previous value
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- if (newChild.key === key) {
- return updateElement(returnFiber, oldFiber, newChild, lanes);
- } else {
- return null;
- }
- }
+ return prevValue;
+ } else {
+ // This is not an urgent update, so we can use the latest value regardless
+ // of what it is. No need to defer it.
+ // However, if we're currently inside a spawned render, then we need to mark
+ // this as an update to prevent the fiber from bailing out.
+ //
+ // `baseState` is true when the current value is different from the rendered
+ // value. The name doesn't really match how we use it because we're reusing
+ // a state hook field instead of creating a new one.
+ if (hook.baseState) {
+ // Flip this back to false.
+ hook.baseState = false;
+ markWorkInProgressReceivedUpdate();
+ }
- case REACT_PORTAL_TYPE: {
- if (newChild.key === key) {
- return updatePortal(returnFiber, oldFiber, newChild, lanes);
- } else {
- return null;
- }
- }
+ hook.memoizedState = value;
+ return value;
+ }
+}
- case REACT_LAZY_TYPE: {
- var payload = newChild._payload;
- var init = newChild._init;
- return updateSlot(returnFiber, oldFiber, init(payload), lanes);
- }
- }
+function startTransition(setPending, callback, options) {
+ var previousPriority = getCurrentUpdatePriority();
+ setCurrentUpdatePriority(
+ higherEventPriority(previousPriority, ContinuousEventPriority)
+ );
+ setPending(true);
+ var prevTransition = ReactCurrentBatchConfig$3.transition;
+ ReactCurrentBatchConfig$3.transition = {};
+ var currentTransition = ReactCurrentBatchConfig$3.transition;
- if (isArray(newChild) || getIteratorFn(newChild)) {
- if (key !== null) {
- return null;
- }
+ if (enableTransitionTracing) {
+ if (options !== undefined && options.name !== undefined) {
+ ReactCurrentBatchConfig$3.transition.name = options.name;
+ ReactCurrentBatchConfig$3.transition.startTime = now$1();
+ }
+ }
- return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
- } // Usable node types
- //
- // Unwrap the inner value and recursively call this function again.
+ {
+ ReactCurrentBatchConfig$3.transition._updatedFibers = new Set();
+ }
- if (typeof newChild.then === "function") {
- var thenable = newChild;
- return updateSlot(
- returnFiber,
- oldFiber,
- unwrapThenable(thenable),
- lanes
- );
- }
+ try {
+ setPending(false);
+ callback();
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig$3.transition = prevTransition;
- if (
- newChild.$$typeof === REACT_CONTEXT_TYPE ||
- newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
- ) {
- var context = newChild;
- return updateSlot(
- returnFiber,
- oldFiber,
- readContextDuringReconcilation(returnFiber, context, lanes),
- lanes
- );
- }
+ {
+ if (prevTransition === null && currentTransition._updatedFibers) {
+ var updatedFibersCount = currentTransition._updatedFibers.size;
- throwOnInvalidObjectType(returnFiber, newChild);
- }
+ currentTransition._updatedFibers.clear();
- {
- if (typeof newChild === "function") {
- warnOnFunctionType(returnFiber);
+ if (updatedFibersCount > 10) {
+ warn(
+ "Detected a large number of updates inside startTransition. " +
+ "If this is due to a subscription please re-write it to use React provided hooks. " +
+ "Otherwise concurrent mode guarantees are off the table."
+ );
+ }
}
}
-
- return null;
}
+}
- function updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- newChild,
- lanes
- ) {
- if (
- (typeof newChild === "string" && newChild !== "") ||
- typeof newChild === "number"
- ) {
- // Text nodes don't have keys, so we neither have to check the old nor
- // new node for the key. If both are text nodes, they match.
- var matchedFiber = existingChildren.get(newIdx) || null;
- return updateTextNode(returnFiber, matchedFiber, "" + newChild, lanes);
- }
+function mountTransition() {
+ var _mountState = mountState(false),
+ isPending = _mountState[0],
+ setPending = _mountState[1]; // The `start` method never changes.
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- var _matchedFiber =
- existingChildren.get(
- newChild.key === null ? newIdx : newChild.key
- ) || null;
+ var start = startTransition.bind(null, setPending);
+ var hook = mountWorkInProgressHook();
+ hook.memoizedState = start;
+ return [isPending, start];
+}
- return updateElement(returnFiber, _matchedFiber, newChild, lanes);
- }
+function updateTransition() {
+ var _updateState = updateState(),
+ isPending = _updateState[0];
- case REACT_PORTAL_TYPE: {
- var _matchedFiber2 =
- existingChildren.get(
- newChild.key === null ? newIdx : newChild.key
- ) || null;
+ var hook = updateWorkInProgressHook();
+ var start = hook.memoizedState;
+ return [isPending, start];
+}
- return updatePortal(returnFiber, _matchedFiber2, newChild, lanes);
- }
+function rerenderTransition() {
+ var _rerenderState = rerenderState(),
+ isPending = _rerenderState[0];
- case REACT_LAZY_TYPE:
- var payload = newChild._payload;
- var init = newChild._init;
- return updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- init(payload),
- lanes
- );
- }
+ var hook = updateWorkInProgressHook();
+ var start = hook.memoizedState;
+ return [isPending, start];
+}
- if (isArray(newChild) || getIteratorFn(newChild)) {
- var _matchedFiber3 = existingChildren.get(newIdx) || null;
+function mountId() {
+ var hook = mountWorkInProgressHook();
+ var root = getWorkInProgressRoot(); // TODO: In Fizz, id generation is specific to each server config. Maybe we
+ // should do this in Fiber, too? Deferring this decision for now because
+ // there's no other place to store the prefix except for an internal field on
+ // the public createRoot object, which the fiber tree does not currently have
+ // a reference to.
- return updateFragment(
- returnFiber,
- _matchedFiber3,
- newChild,
- lanes,
- null
- );
- } // Usable node types
- //
- // Unwrap the inner value and recursively call this function again.
+ var identifierPrefix = root.identifierPrefix;
+ var id;
- if (typeof newChild.then === "function") {
- var thenable = newChild;
- return updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- unwrapThenable(thenable),
- lanes
- );
- }
+ if (getIsHydrating()) {
+ var treeId = getTreeId(); // Use a captial R prefix for server-generated ids.
- if (
- newChild.$$typeof === REACT_CONTEXT_TYPE ||
- newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
- ) {
- var context = newChild;
- return updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- readContextDuringReconcilation(returnFiber, context, lanes),
- lanes
- );
- }
+ id = ":" + identifierPrefix + "R" + treeId; // Unless this is the first id at this level, append a number at the end
+ // that represents the position of this useId hook among all the useId
+ // hooks for this fiber.
- throwOnInvalidObjectType(returnFiber, newChild);
- }
+ var localId = localIdCounter++;
- {
- if (typeof newChild === "function") {
- warnOnFunctionType(returnFiber);
- }
+ if (localId > 0) {
+ id += "H" + localId.toString(32);
}
- return null;
+ id += ":";
+ } else {
+ // Use a lowercase r prefix for client-generated ids.
+ var globalClientId = globalClientIdCounter++;
+ id = ":" + identifierPrefix + "r" + globalClientId.toString(32) + ":";
}
- /**
- * Warns if there is a duplicate or missing key
- */
- function warnOnInvalidKey(child, knownKeys, returnFiber) {
- {
- if (typeof child !== "object" || child === null) {
- return knownKeys;
- }
+ hook.memoizedState = id;
+ return id;
+}
- switch (child.$$typeof) {
- case REACT_ELEMENT_TYPE:
- case REACT_PORTAL_TYPE:
- warnForMissingKey(child, returnFiber);
- var key = child.key;
+function updateId() {
+ var hook = updateWorkInProgressHook();
+ var id = hook.memoizedState;
+ return id;
+}
- if (typeof key !== "string") {
- break;
- }
+function mountRefresh() {
+ var hook = mountWorkInProgressHook();
+ var refresh = (hook.memoizedState = refreshCache.bind(
+ null,
+ currentlyRenderingFiber$1
+ ));
+ return refresh;
+}
- if (knownKeys === null) {
- knownKeys = new Set();
- knownKeys.add(key);
- break;
- }
+function updateRefresh() {
+ var hook = updateWorkInProgressHook();
+ return hook.memoizedState;
+}
- if (!knownKeys.has(key)) {
- knownKeys.add(key);
- break;
- }
+function refreshCache(fiber, seedKey, seedValue) {
+ // TODO: Consider warning if the refresh is at discrete priority, or if we
+ // otherwise suspect that it wasn't batched properly.
- error(
- "Encountered two children with the same key, `%s`. " +
- "Keys should be unique so that components maintain their identity " +
- "across updates. Non-unique keys may cause children to be " +
- "duplicated and/or omitted — the behavior is unsupported and " +
- "could change in a future version.",
- key
- );
+ var provider = fiber.return;
- break;
+ while (provider !== null) {
+ switch (provider.tag) {
+ case CacheComponent:
+ case HostRoot: {
+ // Schedule an update on the cache boundary to trigger a refresh.
+ var lane = requestUpdateLane(provider);
+ var refreshUpdate = createUpdate(lane);
+ var root = enqueueUpdate(provider, refreshUpdate, lane);
- case REACT_LAZY_TYPE:
- var payload = child._payload;
- var init = child._init;
- warnOnInvalidKey(init(payload), knownKeys, returnFiber);
- break;
- }
- }
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, provider, lane, eventTime);
+ entangleTransitions(root, provider, lane);
+ } // TODO: If a refresh never commits, the new cache created here must be
+ // released. A simple case is start refreshing a cache boundary, but then
+ // unmount that boundary before the refresh completes.
- return knownKeys;
- }
+ var seededCache = createCache();
- function reconcileChildrenArray(
- returnFiber,
- currentFirstChild,
- newChildren,
- lanes
- ) {
- // This algorithm can't optimize by searching from both ends since we
- // don't have backpointers on fibers. I'm trying to see how far we can get
- // with that model. If it ends up not being worth the tradeoffs, we can
- // add it later.
- // Even with a two ended optimization, we'd want to optimize for the case
- // where there are few changes and brute force the comparison instead of
- // going for the Map. It'd like to explore hitting that path first in
- // forward-only mode and only go for the Map once we notice that we need
- // lots of look ahead. This doesn't handle reversal as well as two ended
- // search but that's unusual. Besides, for the two ended optimization to
- // work on Iterables, we'd need to copy the whole set.
- // In this first iteration, we'll just live with hitting the bad case
- // (adding everything to a Map) in for every insert/move.
- // If you change this code, also update reconcileChildrenIterator() which
- // uses the same algorithm.
- {
- // First, validate keys.
- var knownKeys = null;
+ if (seedKey !== null && seedKey !== undefined && root !== null) {
+ {
+ // Seed the cache with the value passed by the caller. This could be
+ // from a server mutation, or it could be a streaming response.
+ seededCache.data.set(seedKey, seedValue);
+ }
+ }
- for (var i = 0; i < newChildren.length; i++) {
- var child = newChildren[i];
- knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
+ var payload = {
+ cache: seededCache
+ };
+ refreshUpdate.payload = payload;
+ return;
}
}
- 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;
- }
+ provider = provider.return;
+ } // TODO: Warn if unmounted?
+}
- var newFiber = updateSlot(
- returnFiber,
- oldFiber,
- newChildren[newIdx],
- lanes
+function dispatchReducerAction(fiber, queue, action) {
+ {
+ if (typeof arguments[3] === "function") {
+ error(
+ "State updates from the useState() and useReducer() Hooks don't support the " +
+ "second callback argument. To execute a side effect after " +
+ "rendering, declare it in the component body with useEffect()."
);
-
- 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);
+ var lane = requestUpdateLane(fiber);
+ var update = {
+ lane: lane,
+ action: action,
+ hasEagerState: false,
+ eagerState: null,
+ next: null
+ };
- if (getIsHydrating()) {
- var numberOfForks = newIdx;
- pushTreeFork(returnFiber, numberOfForks);
- }
+ if (isRenderPhaseUpdate(fiber)) {
+ enqueueRenderPhaseUpdate(queue, update);
+ } else {
+ var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
- return resultingFirstChild;
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ entangleTransitionUpdate(root, queue, lane);
}
+ }
- if (oldFiber === null) {
- // If we don't have any more existing children we can choose a fast path
- // since the rest will all be insertions.
- for (; newIdx < newChildren.length; newIdx++) {
- var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
+ markUpdateInDevTools(fiber, lane, action);
+}
- if (_newFiber === null) {
- continue;
- }
+function dispatchSetState(fiber, queue, action) {
+ {
+ if (typeof arguments[3] === "function") {
+ error(
+ "State updates from the useState() and useReducer() Hooks don't support the " +
+ "second callback argument. To execute a side effect after " +
+ "rendering, declare it in the component body with useEffect()."
+ );
+ }
+ }
- lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
+ var lane = requestUpdateLane(fiber);
+ var update = {
+ lane: lane,
+ action: action,
+ hasEagerState: false,
+ eagerState: null,
+ next: null
+ };
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = _newFiber;
- } else {
- previousNewFiber.sibling = _newFiber;
- }
+ if (isRenderPhaseUpdate(fiber)) {
+ enqueueRenderPhaseUpdate(queue, update);
+ } else {
+ var alternate = fiber.alternate;
- previousNewFiber = _newFiber;
- }
+ if (
+ fiber.lanes === NoLanes &&
+ (alternate === null || alternate.lanes === NoLanes)
+ ) {
+ // The queue is currently empty, which means we can eagerly compute the
+ // next state before entering the render phase. If the new state is the
+ // same as the current state, we may be able to bail out entirely.
+ var lastRenderedReducer = queue.lastRenderedReducer;
- if (getIsHydrating()) {
- var _numberOfForks = newIdx;
- pushTreeFork(returnFiber, _numberOfForks);
- }
+ if (lastRenderedReducer !== null) {
+ var prevDispatcher;
- return resultingFirstChild;
- } // Add all children to a key map for quick lookups.
+ {
+ prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
+ }
- var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
+ try {
+ var currentState = queue.lastRenderedState;
+ var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute
+ // it, on the update object. If the reducer hasn't changed by the
+ // time we enter the render phase, then the eager state can be used
+ // without calling the reducer again.
- for (; newIdx < newChildren.length; newIdx++) {
- var _newFiber2 = updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- newChildren[newIdx],
- lanes
- );
+ update.hasEagerState = true;
+ update.eagerState = eagerState;
- if (_newFiber2 !== null) {
- if (shouldTrackSideEffects) {
- if (_newFiber2.alternate !== null) {
- // The new fiber is a work in progress, but if there exists a
- // current, that means that we reused the fiber. We need to delete
- // it from the child list so that we don't add it to the deletion
- // list.
- existingChildren.delete(
- _newFiber2.key === null ? newIdx : _newFiber2.key
- );
+ if (objectIs(eagerState, currentState)) {
+ // Fast path. We can bail out without scheduling React to re-render.
+ // It's still possible that we'll need to rebase this update later,
+ // if the component re-renders for a different reason and by that
+ // time the reducer has changed.
+ // TODO: Do we still need to entangle transitions in this case?
+ enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
+ return;
+ }
+ } catch (error) {
+ // Suppress the error. It will throw again in the render phase.
+ } finally {
+ {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
}
-
- 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);
- });
- }
+ var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
- if (getIsHydrating()) {
- var _numberOfForks2 = newIdx;
- pushTreeFork(returnFiber, _numberOfForks2);
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ entangleTransitionUpdate(root, queue, lane);
}
-
- return resultingFirstChild;
}
- function reconcileChildrenIterator(
- returnFiber,
- currentFirstChild,
- newChildrenIterable,
- lanes
- ) {
- // This is the same implementation as reconcileChildrenArray(),
- // but using the iterator instead.
- var iteratorFn = getIteratorFn(newChildrenIterable);
+ markUpdateInDevTools(fiber, lane, action);
+}
- if (typeof iteratorFn !== "function") {
- throw new Error(
- "An object is not an iterable. This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
- }
+function isRenderPhaseUpdate(fiber) {
+ var alternate = fiber.alternate;
+ return (
+ fiber === currentlyRenderingFiber$1 ||
+ (alternate !== null && alternate === currentlyRenderingFiber$1)
+ );
+}
- {
- // We don't support rendering Generators because it's a mutation.
- // See https://github.com/facebook/react/issues/12995
- if (
- typeof Symbol === "function" && // $FlowFixMe Flow doesn't know about toStringTag
- newChildrenIterable[Symbol.toStringTag] === "Generator"
- ) {
- if (!didWarnAboutGenerators) {
- error(
- "Using Generators as children is unsupported and will likely yield " +
- "unexpected results because enumerating a generator mutates it. " +
- "You may convert it to an array with `Array.from()` or the " +
- "`[...spread]` operator before rendering. Keep in mind " +
- "you might need to polyfill these features for older browsers."
- );
- }
+function enqueueRenderPhaseUpdate(queue, update) {
+ // This is a render phase update. Stash it in a lazily-created map of
+ // queue -> linked list of updates. After this render pass, we'll restart
+ // and apply the stashed updates on top of the work-in-progress hook.
+ didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =
+ true;
+ var pending = queue.pending;
- didWarnAboutGenerators = true;
- } // Warn about using Maps as children
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
+ }
- if (newChildrenIterable.entries === iteratorFn) {
- if (!didWarnAboutMaps) {
- error(
- "Using Maps as children is not supported. " +
- "Use an array of keyed ReactElements instead."
- );
- }
+ queue.pending = update;
+} // TODO: Move to ReactFiberConcurrentUpdates?
- didWarnAboutMaps = true;
- } // First, validate keys.
- // We'll get a different iterator later for the main pass.
+function entangleTransitionUpdate(root, queue, lane) {
+ if (isTransitionLane(lane)) {
+ var queueLanes = queue.lanes; // If any entangled lanes are no longer pending on the root, then they
+ // must have finished. We can remove them from the shared queue, which
+ // represents a superset of the actually pending lanes. In some cases we
+ // may entangle more than we need to, but that's OK. In fact it's worse if
+ // we *don't* entangle when we should.
- var _newChildren = iteratorFn.call(newChildrenIterable);
+ queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
- if (_newChildren) {
- var knownKeys = null;
+ var newQueueLanes = mergeLanes(queueLanes, lane);
+ queue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
+ // the lane finished since the last time we entangled it. So we need to
+ // entangle it again, just to be sure.
- var _step = _newChildren.next();
+ markRootEntangled(root, newQueueLanes);
+ }
+}
- for (; !_step.done; _step = _newChildren.next()) {
- var child = _step.value;
- knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
- }
+function markUpdateInDevTools(fiber, lane, action) {
+ {
+ if (enableDebugTracing) {
+ if (fiber.mode & DebugTracingMode) {
+ var name = getComponentNameFromFiber(fiber) || "Unknown";
+ logStateUpdateScheduled(name, lane, action);
}
}
+ }
- var newChildren = iteratorFn.call(newChildrenIterable);
+ if (enableSchedulingProfiler) {
+ markStateUpdateScheduled(fiber, lane);
+ }
+}
- if (newChildren == null) {
- throw new Error("An iterable object provided no iterator.");
- }
+var ContextOnlyDispatcher = {
+ readContext: readContext,
+ useCallback: throwInvalidHookError,
+ useContext: throwInvalidHookError,
+ useEffect: throwInvalidHookError,
+ useImperativeHandle: throwInvalidHookError,
+ useInsertionEffect: throwInvalidHookError,
+ useLayoutEffect: throwInvalidHookError,
+ useMemo: throwInvalidHookError,
+ useReducer: throwInvalidHookError,
+ useRef: throwInvalidHookError,
+ useState: throwInvalidHookError,
+ useDebugValue: throwInvalidHookError,
+ useDeferredValue: throwInvalidHookError,
+ useTransition: throwInvalidHookError,
+ useMutableSource: throwInvalidHookError,
+ useSyncExternalStore: throwInvalidHookError,
+ useId: throwInvalidHookError
+};
- var resultingFirstChild = null;
- var previousNewFiber = null;
- var oldFiber = currentFirstChild;
- var lastPlacedIndex = 0;
- var newIdx = 0;
- var nextOldFiber = null;
- var step = newChildren.next();
-
- for (
- ;
- oldFiber !== null && !step.done;
- newIdx++, step = newChildren.next()
- ) {
- if (oldFiber.index > newIdx) {
- nextOldFiber = oldFiber;
- oldFiber = null;
- } else {
- nextOldFiber = oldFiber.sibling;
- }
-
- var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
+{
+ ContextOnlyDispatcher.useCacheRefresh = throwInvalidHookError;
+}
- 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;
- }
+{
+ ContextOnlyDispatcher.use = throwInvalidHookError;
+}
- break;
- }
+{
+ ContextOnlyDispatcher.useMemoCache = throwInvalidHookError;
+}
- 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);
- }
- }
+{
+ ContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;
+}
- lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+var HooksDispatcherOnMountInDEV = null;
+var HooksDispatcherOnMountWithHookTypesInDEV = null;
+var HooksDispatcherOnUpdateInDEV = null;
+var HooksDispatcherOnRerenderInDEV = null;
+var InvalidNestedHooksDispatcherOnMountInDEV = null;
+var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
+var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
- 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;
- }
+{
+ var warnInvalidContextAccess = function () {
+ error(
+ "Context can only be read while React is rendering. " +
+ "In classes, you can read it in the render method or getDerivedStateFromProps. " +
+ "In function components, you can read it directly in the function body, but not " +
+ "inside Hooks like useReducer() or useMemo()."
+ );
+ };
- previousNewFiber = newFiber;
- oldFiber = nextOldFiber;
- }
+ var warnInvalidHookAccess = function () {
+ error(
+ "Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. " +
+ "You can only call Hooks at the top level of your React function. " +
+ "For more information, see " +
+ "https://reactjs.org/link/rules-of-hooks"
+ );
+ };
- if (step.done) {
- // We've reached the end of the new children. We can delete the rest.
- deleteRemainingChildren(returnFiber, oldFiber);
+ HooksDispatcherOnMountInDEV = {
+ readContext: function (context) {
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ mountHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ return mountInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ mountHookTypesDev();
+ checkDepsAreArrayDev(deps);
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- if (getIsHydrating()) {
- var numberOfForks = newIdx;
- pushTreeFork(returnFiber, numberOfForks);
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- return resultingFirstChild;
- }
-
- if (oldFiber === null) {
- // If we don't have any more existing children we can choose a fast path
- // since the rest will all be insertions.
- for (; !step.done; newIdx++, step = newChildren.next()) {
- var _newFiber3 = createChild(returnFiber, step.value, lanes);
-
- if (_newFiber3 === null) {
- continue;
- }
-
- lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
-
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = _newFiber3;
- } else {
- previousNewFiber.sibling = _newFiber3;
- }
-
- previousNewFiber = _newFiber3;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ mountHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- if (getIsHydrating()) {
- var _numberOfForks3 = newIdx;
- pushTreeFork(returnFiber, _numberOfForks3);
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ mountHookTypesDev();
+ return mountDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ mountHookTypesDev();
+ return mountDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ mountHookTypesDev();
+ return mountTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ mountHookTypesDev();
+ return mountMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ mountHookTypesDev();
+ return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ mountHookTypesDev();
+ return mountId();
+ }
+ };
- 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.
+ {
+ HooksDispatcherOnMountInDEV.useCacheRefresh = function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ mountHookTypesDev();
+ return mountRefresh();
+ };
+ }
- for (; !step.done; newIdx++, step = newChildren.next()) {
- var _newFiber4 = updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- step.value,
- lanes
- );
+ {
+ HooksDispatcherOnMountInDEV.use = use;
+ }
- 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
- );
- }
- }
+ {
+ HooksDispatcherOnMountInDEV.useMemoCache = useMemoCache;
+ }
- lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+ {
+ HooksDispatcherOnMountInDEV.useEffectEvent = function useEffectEvent(
+ callback
+ ) {
+ currentHookNameInDev = "useEffectEvent";
+ mountHookTypesDev();
+ return mountEvent(callback);
+ };
+ }
- if (previousNewFiber === null) {
- resultingFirstChild = _newFiber4;
- } else {
- previousNewFiber.sibling = _newFiber4;
- }
+ HooksDispatcherOnMountWithHookTypesInDEV = {
+ readContext: function (context) {
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ updateHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ updateHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ updateHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ updateHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ updateHookTypesDev();
+ return mountInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ updateHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- previousNewFiber = _newFiber4;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
- }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- 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);
- });
- }
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ updateHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- if (getIsHydrating()) {
- var _numberOfForks4 = newIdx;
- pushTreeFork(returnFiber, _numberOfForks4);
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ updateHookTypesDev();
+ return mountDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ updateHookTypesDev();
+ return mountDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ updateHookTypesDev();
+ return mountTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ updateHookTypesDev();
+ return mountMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ updateHookTypesDev();
+ return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ updateHookTypesDev();
+ return mountId();
}
+ };
- return resultingFirstChild;
+ {
+ HooksDispatcherOnMountWithHookTypesInDEV.useCacheRefresh =
+ function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ updateHookTypesDev();
+ return mountRefresh();
+ };
}
- function reconcileSingleTextNode(
- returnFiber,
- currentFirstChild,
- textContent,
- lanes
- ) {
- // There's no need to check for keys on text nodes since we don't have a
- // way to define them.
- if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
- // We already have an existing node so let's just update it and delete
- // the rest.
- deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
- var existing = useFiber(currentFirstChild, textContent);
- existing.return = returnFiber;
- return existing;
- } // The existing first child is not a text node so we need to create one
- // and delete the existing ones.
-
- deleteRemainingChildren(returnFiber, currentFirstChild);
- var created = createFiberFromText(textContent, returnFiber.mode, lanes);
- created.return = returnFiber;
- return created;
+ {
+ HooksDispatcherOnMountWithHookTypesInDEV.use = use;
}
- function reconcileSingleElement(
- returnFiber,
- currentFirstChild,
- element,
- lanes
- ) {
- var key = element.key;
- var child = currentFirstChild;
+ {
+ HooksDispatcherOnMountWithHookTypesInDEV.useMemoCache = useMemoCache;
+ }
- while (child !== null) {
- // TODO: If key === null and child.key === null, then this only applies to
- // the first item in the list.
- if (child.key === key) {
- var elementType = element.type;
+ {
+ HooksDispatcherOnMountWithHookTypesInDEV.useEffectEvent =
+ function useEffectEvent(callback) {
+ currentHookNameInDev = "useEffectEvent";
+ updateHookTypesDev();
+ return mountEvent(callback);
+ };
+ }
- if (elementType === REACT_FRAGMENT_TYPE) {
- if (child.tag === Fragment) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(child, element.props.children);
- existing.return = returnFiber;
+ HooksDispatcherOnUpdateInDEV = {
+ readContext: function (context) {
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ updateHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ updateHookTypesDev();
+ return updateInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- {
- existing._debugSource = element._source;
- existing._debugOwner = element._owner;
- }
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- return existing;
- }
- } else {
- if (
- child.elementType === elementType || // Keep this check inline so it only runs on the false path:
- isCompatibleFamilyForHotReloading(child, element) || // Lazy types should reconcile their resolved type.
- // We need to do this after the Hot Reloading check above,
- // because hot reloading has different semantics than prod because
- // it doesn't resuspend. So we can't let the call below suspend.
- (typeof elementType === "object" &&
- elementType !== null &&
- elementType.$$typeof === REACT_LAZY_TYPE &&
- resolveLazy(elementType) === child.type)
- ) {
- deleteRemainingChildren(returnFiber, child.sibling);
-
- var _existing = useFiber(child, element.props);
-
- _existing.ref = coerceRef(returnFiber, child, element);
- _existing.return = returnFiber;
-
- {
- _existing._debugSource = element._source;
- _existing._debugOwner = element._owner;
- }
-
- return _existing;
- }
- } // Didn't match.
-
- deleteRemainingChildren(returnFiber, child);
- break;
- } else {
- deleteChild(returnFiber, child);
+ try {
+ return updateReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ updateHookTypesDev();
+ return updateRef();
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- child = child.sibling;
- }
-
- if (element.type === REACT_FRAGMENT_TYPE) {
- var created = createFiberFromFragment(
- element.props.children,
- returnFiber.mode,
- lanes,
- element.key
- );
- created.return = returnFiber;
- return created;
- } else {
- var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
-
- _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
- _created4.return = returnFiber;
- return _created4;
- }
- }
-
- function reconcileSinglePortal(
- returnFiber,
- currentFirstChild,
- portal,
- lanes
- ) {
- var key = portal.key;
- var child = currentFirstChild;
-
- while (child !== null) {
- // TODO: If key === null and child.key === null, then this only applies to
- // the first item in the list.
- if (child.key === key) {
- if (
- child.tag === HostPortal &&
- child.stateNode.containerInfo === portal.containerInfo &&
- child.stateNode.implementation === portal.implementation
- ) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(child, portal.children || []);
- existing.return = returnFiber;
- return existing;
- } else {
- deleteRemainingChildren(returnFiber, child);
- break;
- }
- } else {
- deleteChild(returnFiber, child);
+ try {
+ return updateState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
-
- child = child.sibling;
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ updateHookTypesDev();
+ return updateDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ updateHookTypesDev();
+ return updateDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ updateHookTypesDev();
+ return updateTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ updateHookTypesDev();
+ return updateMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ updateHookTypesDev();
+ return updateSyncExternalStore(subscribe, getSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ updateHookTypesDev();
+ return updateId();
}
+ };
- var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
- created.return = returnFiber;
- return created;
- } // This API will tag the children with the side-effect of the reconciliation
- // itself. They will be added to the side-effect list as we pass through the
- // children and the parent.
-
- function reconcileChildFibersImpl(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- ) {
- // This function is not recursive.
- // If the top level item is an array, we treat it as a set of children,
- // not as a fragment. Nested arrays on the other hand will be treated as
- // fragment nodes. Recursion happens at the normal flow.
- // Handle top level unkeyed fragments as if they were arrays.
- // This leads to an ambiguity between <>{[...]}> and <>...>.
- // We treat the ambiguous cases above the same.
- // TODO: Let's use recursion like we do for Usable nodes?
- var isUnkeyedTopLevelFragment =
- typeof newChild === "object" &&
- newChild !== null &&
- newChild.type === REACT_FRAGMENT_TYPE &&
- newChild.key === null;
-
- if (isUnkeyedTopLevelFragment) {
- newChild = newChild.props.children;
- } // Handle object types
+ {
+ HooksDispatcherOnUpdateInDEV.useCacheRefresh = function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ updateHookTypesDev();
+ return updateRefresh();
+ };
+ }
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE:
- return placeSingleChild(
- reconcileSingleElement(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- )
- );
+ {
+ HooksDispatcherOnUpdateInDEV.use = use;
+ }
- case REACT_PORTAL_TYPE:
- return placeSingleChild(
- reconcileSinglePortal(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- )
- );
+ {
+ HooksDispatcherOnUpdateInDEV.useMemoCache = useMemoCache;
+ }
- case REACT_LAZY_TYPE:
- var payload = newChild._payload;
- var init = newChild._init; // TODO: This function is supposed to be non-recursive.
+ {
+ HooksDispatcherOnUpdateInDEV.useEffectEvent = function useEffectEvent(
+ callback
+ ) {
+ currentHookNameInDev = "useEffectEvent";
+ updateHookTypesDev();
+ return updateEvent(callback);
+ };
+ }
- return reconcileChildFibers(
- returnFiber,
- currentFirstChild,
- init(payload),
- lanes
- );
- }
+ HooksDispatcherOnRerenderInDEV = {
+ readContext: function (context) {
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ updateHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ updateHookTypesDev();
+ return updateInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnRerenderInDEV;
- if (isArray(newChild)) {
- return reconcileChildrenArray(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- );
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnRerenderInDEV;
- if (getIteratorFn(newChild)) {
- return reconcileChildrenIterator(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- );
- } // Usables are a valid React node type. When React encounters a Usable in
- // a child position, it unwraps it using the same algorithm as `use`. For
- // example, for promises, React will throw an exception to unwind the
- // stack, then replay the component once the promise resolves.
- //
- // A difference from `use` is that React will keep unwrapping the value
- // until it reaches a non-Usable type.
- //
- // e.g. Usable>> should resolve to T
- //
- // The structure is a bit unfortunate. Ideally, we shouldn't need to
- // replay the entire begin phase of the parent fiber in order to reconcile
- // the children again. This would require a somewhat significant refactor,
- // because reconcilation happens deep within the begin phase, and
- // depending on the type of work, not always at the end. We should
- // consider as an future improvement.
-
- if (typeof newChild.then === "function") {
- var thenable = newChild;
- return reconcileChildFibersImpl(
- returnFiber,
- currentFirstChild,
- unwrapThenable(thenable),
- lanes
- );
+ try {
+ return rerenderReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ updateHookTypesDev();
+ return updateRef();
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnRerenderInDEV;
- if (
- newChild.$$typeof === REACT_CONTEXT_TYPE ||
- newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
- ) {
- var context = newChild;
- return reconcileChildFibersImpl(
- returnFiber,
- currentFirstChild,
- readContextDuringReconcilation(returnFiber, context, lanes),
- lanes
- );
+ try {
+ return rerenderState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
-
- throwOnInvalidObjectType(returnFiber, newChild);
- }
-
- if (
- (typeof newChild === "string" && newChild !== "") ||
- typeof newChild === "number"
- ) {
- return placeSingleChild(
- reconcileSingleTextNode(
- returnFiber,
- currentFirstChild,
- "" + newChild,
- lanes
- )
- );
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ updateHookTypesDev();
+ return updateDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ updateHookTypesDev();
+ return rerenderDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ updateHookTypesDev();
+ return rerenderTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ updateHookTypesDev();
+ return updateMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ updateHookTypesDev();
+ return updateSyncExternalStore(subscribe, getSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ updateHookTypesDev();
+ return updateId();
}
+ };
- {
- if (typeof newChild === "function") {
- warnOnFunctionType(returnFiber);
- }
- } // Remaining cases are all treated as empty.
-
- return deleteRemainingChildren(returnFiber, currentFirstChild);
- }
-
- function reconcileChildFibers(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- ) {
- // This indirection only exists so we can reset `thenableState` at the end.
- // It should get inlined by Closure.
- thenableIndexCounter$1 = 0;
- var firstChildFiber = reconcileChildFibersImpl(
- returnFiber,
- currentFirstChild,
- newChild,
- lanes
- );
- thenableState$1 = null; // Don't bother to reset `thenableIndexCounter` to 0 because it always gets
- // set at the beginning.
-
- return firstChildFiber;
+ {
+ HooksDispatcherOnRerenderInDEV.useCacheRefresh =
+ function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ updateHookTypesDev();
+ return updateRefresh();
+ };
}
- return reconcileChildFibers;
-}
-
-var reconcileChildFibers = createChildReconciler(true);
-var mountChildFibers = createChildReconciler(false);
-function resetChildReconcilerOnUnwind() {
- // On unwind, clear any pending thenables that were used.
- thenableState$1 = null;
- thenableIndexCounter$1 = 0;
-}
-function cloneChildFibers(current, workInProgress) {
- if (current !== null && workInProgress.child !== current.child) {
- throw new Error("Resuming work not yet implemented.");
+ {
+ HooksDispatcherOnRerenderInDEV.use = use;
}
- if (workInProgress.child === null) {
- return;
+ {
+ HooksDispatcherOnRerenderInDEV.useMemoCache = useMemoCache;
}
- var currentChild = workInProgress.child;
- var newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
- workInProgress.child = newChild;
- newChild.return = workInProgress;
-
- while (currentChild.sibling !== null) {
- currentChild = currentChild.sibling;
- newChild = newChild.sibling = createWorkInProgress(
- currentChild,
- currentChild.pendingProps
- );
- newChild.return = workInProgress;
+ {
+ HooksDispatcherOnRerenderInDEV.useEffectEvent = function useEffectEvent(
+ callback
+ ) {
+ currentHookNameInDev = "useEffectEvent";
+ updateHookTypesDev();
+ return updateEvent(callback);
+ };
}
- newChild.sibling = null;
-} // Reset a workInProgress child set to prepare it for a second pass.
-
-function resetChildFibers(workInProgress, lanes) {
- var child = workInProgress.child;
+ InvalidNestedHooksDispatcherOnMountInDEV = {
+ readContext: function (context) {
+ warnInvalidContextAccess();
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- while (child !== null) {
- resetWorkInProgress(child, lanes);
- child = child.sibling;
- }
-}
-
-// TODO: This isn't being used yet, but it's intended to replace the
-// InvisibleParentContext that is currently managed by SuspenseContext.
-
-var currentTreeHiddenStackCursor = createCursor(null);
-var prevRenderLanesStackCursor = createCursor(NoLanes);
-function pushHiddenContext(fiber, context) {
- var prevRenderLanes = getRenderLanes();
- push(prevRenderLanesStackCursor, prevRenderLanes, fiber);
- push(currentTreeHiddenStackCursor, context, fiber); // When rendering a subtree that's currently hidden, we must include all
- // lanes that would have rendered if the hidden subtree hadn't been deferred.
- // That is, in order to reveal content from hidden -> visible, we must commit
- // all the updates that we skipped when we originally hid the tree.
-
- setRenderLanes(mergeLanes(prevRenderLanes, context.baseLanes));
-}
-function reuseHiddenContextOnStack(fiber) {
- // This subtree is not currently hidden, so we don't need to add any lanes
- // to the render lanes. But we still need to push something to avoid a
- // context mismatch. Reuse the existing context on the stack.
- push(prevRenderLanesStackCursor, getRenderLanes(), fiber);
- push(
- currentTreeHiddenStackCursor,
- currentTreeHiddenStackCursor.current,
- fiber
- );
-}
-function popHiddenContext(fiber) {
- // Restore the previous render lanes from the stack
- setRenderLanes(prevRenderLanesStackCursor.current);
- pop(currentTreeHiddenStackCursor, fiber);
- pop(prevRenderLanesStackCursor, fiber);
-}
-function isCurrentTreeHidden() {
- return currentTreeHiddenStackCursor.current !== null;
-}
-
-// suspends, i.e. it's the nearest `catch` block on the stack.
-
-var suspenseHandlerStackCursor = createCursor(null); // Represents the outermost boundary that is not visible in the current tree.
-// Everything above this is the "shell". When this is null, it means we're
-// rendering in the shell of the app. If it's non-null, it means we're rendering
-// deeper than the shell, inside a new tree that wasn't already visible.
-//
-// The main way we use this concept is to determine whether showing a fallback
-// would result in a desirable or undesirable loading state. Activing a fallback
-// in the shell is considered an undersirable loading state, because it would
-// mean hiding visible (albeit stale) content in the current tree — we prefer to
-// show the stale content, rather than switch to a fallback. But showing a
-// fallback in a new tree is fine, because there's no stale content to
-// prefer instead.
-
-var shellBoundary = null;
-function getShellBoundary() {
- return shellBoundary;
-}
-function pushPrimaryTreeSuspenseHandler(handler) {
- // TODO: Pass as argument
- var current = handler.alternate;
- var props = handler.pendingProps; // Experimental feature: Some Suspense boundaries are marked as having an
- // undesirable fallback state. These have special behavior where we only
- // activate the fallback if there's no other boundary on the stack that we can
- // use instead.
-
- if (
- props.unstable_avoidThisFallback === true && // If an avoided boundary is already visible, it behaves identically to
- // a regular Suspense boundary.
- (current === null || isCurrentTreeHidden())
- ) {
- if (shellBoundary === null) {
- // We're rendering in the shell. There's no parent Suspense boundary that
- // can provide a desirable fallback state. We'll use this boundary.
- push(suspenseHandlerStackCursor, handler, handler); // However, because this is not a desirable fallback, the children are
- // still considered part of the shell. So we intentionally don't assign
- // to `shellBoundary`.
- } else {
- // There's already a parent Suspense boundary that can provide a desirable
- // fallback state. Prefer that one.
- var handlerOnStack = suspenseHandlerStackCursor.current;
- push(suspenseHandlerStackCursor, handlerOnStack, handler);
- }
-
- return;
- } // TODO: If the parent Suspense handler already suspended, there's no reason
- // to push a nested Suspense handler, because it will get replaced by the
- // outer fallback, anyway. Consider this as a future optimization.
-
- push(suspenseHandlerStackCursor, handler, handler);
-
- if (shellBoundary === null) {
- if (current === null || isCurrentTreeHidden()) {
- // This boundary is not visible in the current UI.
- shellBoundary = handler;
- } else {
- var prevState = current.memoizedState;
-
- if (prevState !== null) {
- // This boundary is showing a fallback in the current UI.
- shellBoundary = handler;
+ try {
+ return mountMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
- }
- }
-}
-function pushFallbackTreeSuspenseHandler(fiber) {
- // We're about to render the fallback. If something in the fallback suspends,
- // it's akin to throwing inside of a `catch` block. This boundary should not
- // capture. Reuse the existing handler on the stack.
- reuseSuspenseHandlerOnStack(fiber);
-}
-function pushOffscreenSuspenseHandler(fiber) {
- if (fiber.tag === OffscreenComponent) {
- push(suspenseHandlerStackCursor, fiber, fiber);
-
- if (shellBoundary !== null);
- else {
- var current = fiber.alternate;
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- if (current !== null) {
- var prevState = current.memoizedState;
+ try {
+ return mountReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountRef(initialValue);
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnMountInDEV;
- if (prevState !== null) {
- // This is the first boundary in the stack that's already showing
- // a fallback. So everything outside is considered the shell.
- shellBoundary = fiber;
- }
+ try {
+ return mountState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountId();
}
- } else {
- // This is a LegacyHidden component.
- reuseSuspenseHandlerOnStack(fiber);
- }
-}
-function reuseSuspenseHandlerOnStack(fiber) {
- push(suspenseHandlerStackCursor, getSuspenseHandler(), fiber);
-}
-function getSuspenseHandler() {
- return suspenseHandlerStackCursor.current;
-}
-function popSuspenseHandler(fiber) {
- pop(suspenseHandlerStackCursor, fiber);
+ };
- if (shellBoundary === fiber) {
- // Popping back into the shell.
- shellBoundary = null;
+ {
+ InvalidNestedHooksDispatcherOnMountInDEV.useCacheRefresh =
+ function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ mountHookTypesDev();
+ return mountRefresh();
+ };
}
-} // SuspenseList context
-// TODO: Move to a separate module? We may change the SuspenseList
-// implementation to hide/show in the commit phase, anyway.
-
-var DefaultSuspenseContext = 0;
-var SubtreeSuspenseContextMask = 1; // ForceSuspenseFallback can be used by SuspenseList to force newly added
-// items into their fallback state during one of the render passes.
-
-var ForceSuspenseFallback = 2;
-var suspenseStackCursor = createCursor(DefaultSuspenseContext);
-function hasSuspenseListContext(parentContext, flag) {
- return (parentContext & flag) !== 0;
-}
-function setDefaultShallowSuspenseListContext(parentContext) {
- return parentContext & SubtreeSuspenseContextMask;
-}
-function setShallowSuspenseListContext(parentContext, shallowContext) {
- return (parentContext & SubtreeSuspenseContextMask) | shallowContext;
-}
-function pushSuspenseListContext(fiber, newContext) {
- push(suspenseStackCursor, newContext, fiber);
-}
-function popSuspenseListContext(fiber) {
- pop(suspenseStackCursor, fiber);
-}
-// A non-null SuspenseState means that it is blocked for one reason or another.
-// - A non-null dehydrated field means it's blocked pending hydration.
-// - A non-null dehydrated field can use isSuspenseInstancePending or
-// isSuspenseInstanceFallback to query the reason for being dehydrated.
-// - A null dehydrated field means it's blocked by something suspending and
-// we're currently showing a fallback instead.
+ {
+ InvalidNestedHooksDispatcherOnMountInDEV.use = function (usable) {
+ warnInvalidHookAccess();
+ return use(usable);
+ };
+ }
-function findFirstSuspended(row) {
- var node = row;
+ {
+ InvalidNestedHooksDispatcherOnMountInDEV.useMemoCache = function (size) {
+ warnInvalidHookAccess();
+ return useMemoCache(size);
+ };
+ }
- while (node !== null) {
- if (node.tag === SuspenseComponent) {
- var state = node.memoizedState;
+ {
+ InvalidNestedHooksDispatcherOnMountInDEV.useEffectEvent =
+ function useEffectEvent(callback) {
+ currentHookNameInDev = "useEffectEvent";
+ warnInvalidHookAccess();
+ mountHookTypesDev();
+ return mountEvent(callback);
+ };
+ }
- if (state !== null) {
- var dehydrated = state.dehydrated;
+ InvalidNestedHooksDispatcherOnUpdateInDEV = {
+ readContext: function (context) {
+ warnInvalidContextAccess();
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- if (
- dehydrated === null ||
- isSuspenseInstancePending(dehydrated) ||
- isSuspenseInstanceFallback(dehydrated)
- ) {
- return node;
- }
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
- } else if (
- node.tag === SuspenseListComponent && // revealOrder undefined can't be trusted because it don't
- // keep track of whether it suspended or not.
- node.memoizedProps.revealOrder !== undefined
- ) {
- var didSuspend = (node.flags & DidCapture) !== NoFlags$1;
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- if (didSuspend) {
- return node;
+ try {
+ return updateReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
- } else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
-
- if (node === row) {
- return null;
- }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateRef();
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
- while (node.sibling === null) {
- if (node.return === null || node.return === row) {
- return null;
+ try {
+ return updateState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
-
- node = node.return;
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateSyncExternalStore(subscribe, getSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateId();
}
+ };
- node.sibling.return = node.return;
- node = node.sibling;
+ {
+ InvalidNestedHooksDispatcherOnUpdateInDEV.useCacheRefresh =
+ function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ updateHookTypesDev();
+ return updateRefresh();
+ };
}
- return null;
-}
-
-var NoFlags =
- /* */
- 0; // Represents whether effect should fire.
-
-var HasEffect =
- /* */
- 1; // Represents the phase in which the effect (not the clean-up) fires.
-
-var Insertion =
- /* */
- 2;
-var Layout =
- /* */
- 4;
-var Passive =
- /* */
- 8;
-
-// and should be reset before starting a new render.
-// This tracks which mutable sources need to be reset after a render.
-
-var workInProgressSources = [];
-var rendererSigil$1;
-
-{
- // Used to detect multiple renderers using the same mutable source.
- rendererSigil$1 = {};
-}
-
-function markSourceAsDirty(mutableSource) {
- workInProgressSources.push(mutableSource);
-}
-function resetWorkInProgressVersions() {
- for (var i = 0; i < workInProgressSources.length; i++) {
- var mutableSource = workInProgressSources[i];
-
- {
- mutableSource._workInProgressVersionPrimary = null;
- }
+ {
+ InvalidNestedHooksDispatcherOnUpdateInDEV.use = function (usable) {
+ warnInvalidHookAccess();
+ return use(usable);
+ };
}
- workInProgressSources.length = 0;
-}
-function getWorkInProgressVersion(mutableSource) {
{
- return mutableSource._workInProgressVersionPrimary;
+ InvalidNestedHooksDispatcherOnUpdateInDEV.useMemoCache = function (size) {
+ warnInvalidHookAccess();
+ return useMemoCache(size);
+ };
}
-}
-function setWorkInProgressVersion(mutableSource, version) {
+
{
- mutableSource._workInProgressVersionPrimary = version;
+ InvalidNestedHooksDispatcherOnUpdateInDEV.useEffectEvent =
+ function useEffectEvent(callback) {
+ currentHookNameInDev = "useEffectEvent";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateEvent(callback);
+ };
}
- workInProgressSources.push(mutableSource);
-}
-function warnAboutMultipleRenderersDEV(mutableSource) {
- {
- {
- if (mutableSource._currentPrimaryRenderer == null) {
- mutableSource._currentPrimaryRenderer = rendererSigil$1;
- } else if (mutableSource._currentPrimaryRenderer !== rendererSigil$1) {
- error(
- "Detected multiple renderers concurrently rendering the " +
- "same mutable source. This is currently unsupported."
- );
+ InvalidNestedHooksDispatcherOnRerenderInDEV = {
+ readContext: function (context) {
+ warnInvalidContextAccess();
+ return readContext(context);
+ },
+ useCallback: function (callback, deps) {
+ currentHookNameInDev = "useCallback";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateCallback(callback, deps);
+ },
+ useContext: function (context) {
+ currentHookNameInDev = "useContext";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return readContext(context);
+ },
+ useEffect: function (create, deps) {
+ currentHookNameInDev = "useEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateEffect(create, deps);
+ },
+ useImperativeHandle: function (ref, create, deps) {
+ currentHookNameInDev = "useImperativeHandle";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateImperativeHandle(ref, create, deps);
+ },
+ useInsertionEffect: function (create, deps) {
+ currentHookNameInDev = "useInsertionEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateInsertionEffect(create, deps);
+ },
+ useLayoutEffect: function (create, deps) {
+ currentHookNameInDev = "useLayoutEffect";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateLayoutEffect(create, deps);
+ },
+ useMemo: function (create, deps) {
+ currentHookNameInDev = "useMemo";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
+
+ try {
+ return updateMemo(create, deps);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useReducer: function (reducer, initialArg, init) {
+ currentHookNameInDev = "useReducer";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
+
+ try {
+ return rerenderReducer(reducer, initialArg, init);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
+ }
+ },
+ useRef: function (initialValue) {
+ currentHookNameInDev = "useRef";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateRef();
+ },
+ useState: function (initialState) {
+ currentHookNameInDev = "useState";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ var prevDispatcher = ReactCurrentDispatcher$1.current;
+ ReactCurrentDispatcher$1.current =
+ InvalidNestedHooksDispatcherOnUpdateInDEV;
+
+ try {
+ return rerenderState(initialState);
+ } finally {
+ ReactCurrentDispatcher$1.current = prevDispatcher;
}
+ },
+ useDebugValue: function (value, formatterFn) {
+ currentHookNameInDev = "useDebugValue";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateDebugValue();
+ },
+ useDeferredValue: function (value) {
+ currentHookNameInDev = "useDeferredValue";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return rerenderDeferredValue(value);
+ },
+ useTransition: function () {
+ currentHookNameInDev = "useTransition";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return rerenderTransition();
+ },
+ useMutableSource: function (source, getSnapshot, subscribe) {
+ currentHookNameInDev = "useMutableSource";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateMutableSource(source, getSnapshot, subscribe);
+ },
+ useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
+ currentHookNameInDev = "useSyncExternalStore";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateSyncExternalStore(subscribe, getSnapshot);
+ },
+ useId: function () {
+ currentHookNameInDev = "useId";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateId();
}
- }
-} // Eager reads the version of a mutable source and stores it on the root.
-// This ensures that the version used for server rendering matches the one
-// that is eventually read during hydration.
-// If they don't match there's a potential tear and a full deopt render is required.
+ };
-function registerMutableSourceForHydration(root, mutableSource) {
- var getVersion = mutableSource._getVersion;
- var version = getVersion(mutableSource._source); // TODO Clear this data once all pending hydration work is finished.
- // Retaining it forever may interfere with GC.
+ {
+ InvalidNestedHooksDispatcherOnRerenderInDEV.useCacheRefresh =
+ function useCacheRefresh() {
+ currentHookNameInDev = "useCacheRefresh";
+ updateHookTypesDev();
+ return updateRefresh();
+ };
+ }
- if (root.mutableSourceEagerHydrationData == null) {
- root.mutableSourceEagerHydrationData = [mutableSource, version];
- } else {
- root.mutableSourceEagerHydrationData.push(mutableSource, version);
+ {
+ InvalidNestedHooksDispatcherOnRerenderInDEV.use = function (usable) {
+ warnInvalidHookAccess();
+ return use(usable);
+ };
}
-}
-var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher,
- ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig;
-var didWarnAboutMismatchedHooksForComponent;
-var didWarnUncachedGetSnapshot;
-var didWarnAboutUseWrappedInTryCatch;
+ {
+ InvalidNestedHooksDispatcherOnRerenderInDEV.useMemoCache = function (size) {
+ warnInvalidHookAccess();
+ return useMemoCache(size);
+ };
+ }
-{
- didWarnAboutMismatchedHooksForComponent = new Set();
- didWarnAboutUseWrappedInTryCatch = new Set();
-} // These are set right before calling the component.
+ {
+ InvalidNestedHooksDispatcherOnRerenderInDEV.useEffectEvent =
+ function useEffectEvent(callback) {
+ currentHookNameInDev = "useEffectEvent";
+ warnInvalidHookAccess();
+ updateHookTypesDev();
+ return updateEvent(callback);
+ };
+ }
+}
-var renderLanes$1 = NoLanes; // The work-in-progress fiber. I've named it differently to distinguish it from
-// the work-in-progress hook.
+var now = Scheduler.unstable_now;
+var commitTime = 0;
+var layoutEffectStartTime = -1;
+var profilerStartTime = -1;
+var passiveEffectStartTime = -1;
+/**
+ * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect).
+ *
+ * The overall sequence is:
+ * 1. render
+ * 2. commit (and call `onRender`, `onCommit`)
+ * 3. check for nested updates
+ * 4. flush passive effects (and call `onPostCommit`)
+ *
+ * Nested updates are identified in step 3 above,
+ * but step 4 still applies to the work that was just committed.
+ * We use two flags to track nested updates then:
+ * one tracks whether the upcoming update is a nested update,
+ * and the other tracks whether the current update was a nested update.
+ * The first value gets synced to the second at the start of the render phase.
+ */
-var currentlyRenderingFiber$1 = null; // Hooks are stored as a linked list on the fiber's memoizedState field. The
-// current hook list is the list that belongs to the current fiber. The
-// work-in-progress hook list is a new list that will be added to the
-// work-in-progress fiber.
+var currentUpdateIsNested = false;
+var nestedUpdateScheduled = false;
-var currentHook = null;
-var workInProgressHook = null; // Whether an update was scheduled at any point during the render phase. This
-// does not get reset if we do another render pass; only when we're completely
-// finished evaluating this component. This is an optimization so we know
-// whether we need to clear render phase updates after a throw.
+function isCurrentUpdateNested() {
+ return currentUpdateIsNested;
+}
-var didScheduleRenderPhaseUpdate = false; // Where an update was scheduled only during the current render pass. This
-// gets reset after each attempt.
-// TODO: Maybe there's some way to consolidate this with
-// `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`.
+function markNestedUpdateScheduled() {
+ {
+ nestedUpdateScheduled = true;
+ }
+}
-var didScheduleRenderPhaseUpdateDuringThisPass = false;
-var shouldDoubleInvokeUserFnsInHooksDEV = false; // Counts the number of useId hooks in this component.
+function resetNestedUpdateFlag() {
+ {
+ currentUpdateIsNested = false;
+ nestedUpdateScheduled = false;
+ }
+}
-var localIdCounter = 0; // Counts number of `use`-d thenables
+function syncNestedUpdateFlag() {
+ {
+ currentUpdateIsNested = nestedUpdateScheduled;
+ nestedUpdateScheduled = false;
+ }
+}
-var thenableIndexCounter = 0;
-var thenableState = null; // Used for ids that are generated completely client-side (i.e. not during
-// hydration). This counter is global, so client ids are not stable across
-// render attempts.
+function getCommitTime() {
+ return commitTime;
+}
-var globalClientIdCounter = 0;
-var RE_RENDER_LIMIT = 25; // In DEV, this is the name of the currently executing primitive hook
+function recordCommitTime() {
+ commitTime = now();
+}
-var currentHookNameInDev = null; // In DEV, this list ensures that hooks are called in the same order between renders.
-// The list stores the order of hooks used during the initial render (mount).
-// Subsequent renders (updates) reference this list.
+function startProfilerTimer(fiber) {
+ profilerStartTime = now();
-var hookTypesDev = null;
-var hookTypesUpdateIndexDev = -1; // In DEV, this tracks whether currently rendering component needs to ignore
-// the dependencies for Hooks that need them (e.g. useEffect or useMemo).
-// When true, such Hooks will always be "remounted". Only used during hot reload.
+ if (fiber.actualStartTime < 0) {
+ fiber.actualStartTime = now();
+ }
+}
-var ignorePreviousDependencies = false;
+function stopProfilerTimerIfRunning(fiber) {
+ profilerStartTime = -1;
+}
-function mountHookTypesDev() {
- {
- var hookName = currentHookNameInDev;
+function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
+ if (profilerStartTime >= 0) {
+ var elapsedTime = now() - profilerStartTime;
+ fiber.actualDuration += elapsedTime;
- if (hookTypesDev === null) {
- hookTypesDev = [hookName];
- } else {
- hookTypesDev.push(hookName);
+ if (overrideBaseTime) {
+ fiber.selfBaseDuration = elapsedTime;
}
+
+ profilerStartTime = -1;
}
}
-function updateHookTypesDev() {
- {
- var hookName = currentHookNameInDev;
+function recordLayoutEffectDuration(fiber) {
+ if (layoutEffectStartTime >= 0) {
+ var elapsedTime = now() - layoutEffectStartTime;
+ layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
+ // Or the root (for the DevTools Profiler to read)
- if (hookTypesDev !== null) {
- hookTypesUpdateIndexDev++;
+ var parentFiber = fiber.return;
- if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
- warnOnHookMismatchInDev(hookName);
+ while (parentFiber !== null) {
+ switch (parentFiber.tag) {
+ case HostRoot:
+ var root = parentFiber.stateNode;
+ root.effectDuration += elapsedTime;
+ return;
+
+ case Profiler:
+ var parentStateNode = parentFiber.stateNode;
+ parentStateNode.effectDuration += elapsedTime;
+ return;
}
- }
- }
-}
-function checkDepsAreArrayDev(deps) {
- {
- if (deps !== undefined && deps !== null && !isArray(deps)) {
- // Verify deps, but only on mount to avoid extra checks.
- // It's unlikely their type would change as usually you define them inline.
- error(
- "%s received a final argument that is not an array (instead, received `%s`). When " +
- "specified, the final argument must be an array.",
- currentHookNameInDev,
- typeof deps
- );
+ parentFiber = parentFiber.return;
}
}
}
-function warnOnHookMismatchInDev(currentHookName) {
- {
- var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1);
-
- if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
- didWarnAboutMismatchedHooksForComponent.add(componentName);
+function recordPassiveEffectDuration(fiber) {
+ if (passiveEffectStartTime >= 0) {
+ var elapsedTime = now() - passiveEffectStartTime;
+ passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
+ // Or the root (for the DevTools Profiler to read)
- if (hookTypesDev !== null) {
- var table = "";
- var secondColumnStart = 30;
+ var parentFiber = fiber.return;
- for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
- var oldHookName = hookTypesDev[i];
- var newHookName =
- i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
- var row = i + 1 + ". " + oldHookName; // Extra space so second column lines up
- // lol @ IE not supporting String#repeat
+ while (parentFiber !== null) {
+ switch (parentFiber.tag) {
+ case HostRoot:
+ var root = parentFiber.stateNode;
- while (row.length < secondColumnStart) {
- row += " ";
+ if (root !== null) {
+ root.passiveEffectDuration += elapsedTime;
}
- row += newHookName + "\n";
- table += row;
- }
+ return;
- error(
- "React has detected a change in the order of Hooks called by %s. " +
- "This will lead to bugs and errors if not fixed. " +
- "For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n" +
- " Previous render Next render\n" +
- " ------------------------------------------------------\n" +
- "%s" +
- " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
- componentName,
- table
- );
+ case Profiler:
+ var parentStateNode = parentFiber.stateNode;
+
+ if (parentStateNode !== null) {
+ // Detached fibers have their state node cleared out.
+ // In this case, the return pointer is also cleared out,
+ // so we won't be able to report the time spent in this Profiler's subtree.
+ parentStateNode.passiveEffectDuration += elapsedTime;
+ }
+
+ return;
}
+
+ parentFiber = parentFiber.return;
}
}
}
-function throwInvalidHookError() {
- throw new Error(
- "Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for" +
- " one of the following reasons:\n" +
- "1. You might have mismatching versions of React and the renderer (such as React DOM)\n" +
- "2. You might be breaking the Rules of Hooks\n" +
- "3. You might have more than one copy of React in the same app\n" +
- "See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem."
- );
+function startLayoutEffectTimer() {
+ layoutEffectStartTime = now();
}
-function areHookInputsEqual(nextDeps, prevDeps) {
- {
- if (ignorePreviousDependencies) {
- // Only true when this component is being hot reloaded.
- return false;
- }
- }
+function startPassiveEffectTimer() {
+ passiveEffectStartTime = now();
+}
- if (prevDeps === null) {
- {
- error(
- "%s received a final argument during this render, but not during " +
- "the previous render. Even though the final argument is optional, " +
- "its type cannot change between renders.",
- currentHookNameInDev
- );
- }
+function transferActualDuration(fiber) {
+ // Transfer time spent rendering these children so we don't lose it
+ // after we rerender. This is used as a helper in special cases
+ // where we should count the work of multiple passes.
+ var child = fiber.child;
- return false;
+ while (child) {
+ // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+ fiber.actualDuration += child.actualDuration;
+ child = child.sibling;
}
+}
- {
- // Don't bother comparing lengths in prod because these arrays should be
- // passed inline.
- if (nextDeps.length !== prevDeps.length) {
- error(
- "The final argument passed to %s changed size between renders. The " +
- "order and size of this array must remain constant.\n\n" +
- "Previous: %s\n" +
- "Incoming: %s",
- currentHookNameInDev,
- "[" + prevDeps.join(", ") + "]",
- "[" + nextDeps.join(", ") + "]"
- );
- }
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+function resolveDefaultProps(Component, baseProps) {
+ if (Component && Component.defaultProps) {
+ // Resolve default props. Taken from ReactElement
+ var props = assign({}, baseProps);
+ var defaultProps = Component.defaultProps;
- for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (objectIs(nextDeps[i], prevDeps[i])) {
- continue;
+ for (var propName in defaultProps) {
+ if (props[propName] === undefined) {
+ props[propName] = defaultProps[propName];
+ }
}
- return false;
+ return props;
}
- return true;
+ return baseProps;
}
-function renderWithHooks(
- current,
- workInProgress,
- Component,
- props,
- secondArg,
- nextRenderLanes
-) {
- renderLanes$1 = nextRenderLanes;
- currentlyRenderingFiber$1 = workInProgress;
+var fakeInternalInstance = {};
+var didWarnAboutStateAssignmentForComponent;
+var didWarnAboutUninitializedState;
+var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
+var didWarnAboutLegacyLifecyclesAndDerivedState;
+var didWarnAboutUndefinedDerivedState;
+var didWarnAboutDirectlyAssigningPropsToState;
+var didWarnAboutContextTypeAndContextTypes;
+var didWarnAboutInvalidateContextType;
+var didWarnOnInvalidCallback;
+
+{
+ didWarnAboutStateAssignmentForComponent = new Set();
+ didWarnAboutUninitializedState = new Set();
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
+ didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
+ didWarnAboutDirectlyAssigningPropsToState = new Set();
+ didWarnAboutUndefinedDerivedState = new Set();
+ didWarnAboutContextTypeAndContextTypes = new Set();
+ didWarnAboutInvalidateContextType = new Set();
+ didWarnOnInvalidCallback = new Set(); // This is so gross but it's at least non-critical and can be removed if
+ // it causes problems. This is meant to give a nicer error message for
+ // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
+ // ...)) which otherwise throws a "_processChildContext is not a function"
+ // exception.
+
+ Object.defineProperty(fakeInternalInstance, "_processChildContext", {
+ enumerable: false,
+ value: function () {
+ throw new Error(
+ "_processChildContext is not available in React 16+. This likely " +
+ "means you have multiple copies of React and are attempting to nest " +
+ "a React 15 tree inside a React 16 tree using " +
+ "unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
+ "to make sure you have only one copy of React (and ideally, switch " +
+ "to ReactDOM.createPortal)."
+ );
+ }
+ });
+ Object.freeze(fakeInternalInstance);
+}
+function warnOnInvalidCallback$1(callback, callerName) {
{
- hookTypesDev = current !== null ? current._debugHookTypes : null;
- hookTypesUpdateIndexDev = -1; // Used for hot reloading:
+ if (callback === null || typeof callback === "function") {
+ return;
+ }
- ignorePreviousDependencies =
- current !== null && current.type !== workInProgress.type;
- }
+ var key = callerName + "_" + callback;
- workInProgress.memoizedState = null;
- workInProgress.updateQueue = null;
- workInProgress.lanes = NoLanes; // The following should have already been reset
- // currentHook = null;
- // workInProgressHook = null;
- // didScheduleRenderPhaseUpdate = false;
- // localIdCounter = 0;
- // thenableIndexCounter = 0;
- // thenableState = null;
- // TODO Warn if no hooks are used at all during mount, then some are used during update.
- // Currently we will identify the update render as a mount because memoizedState === null.
- // This is tricky because it's valid for certain types of components (e.g. React.lazy)
- // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
- // Non-stateful hooks (e.g. context) don't get added to memoizedState,
- // so memoizedState would be null during updates and mounts.
+ if (!didWarnOnInvalidCallback.has(key)) {
+ didWarnOnInvalidCallback.add(key);
- {
- if (current !== null && current.memoizedState !== null) {
- ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
- } else if (hookTypesDev !== null) {
- // This dispatcher handles an edge case where a component is updating,
- // but no stateful hooks have been used.
- // We want to match the production code behavior (which will use HooksDispatcherOnMount),
- // but with the extra DEV validation to ensure hooks ordering hasn't changed.
- // This dispatcher does that.
- ReactCurrentDispatcher$1.current =
- HooksDispatcherOnMountWithHookTypesInDEV;
- } else {
- ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
+ error(
+ "%s(...): Expected the last optional `callback` argument to be a " +
+ "function. Instead received: %s.",
+ callerName,
+ callback
+ );
}
- } // In Strict Mode, during development, user functions are double invoked to
- // help detect side effects. The logic for how this is implemented for in
- // hook components is a bit complex so let's break it down.
- //
- // We will invoke the entire component function twice. However, during the
- // second invocation of the component, the hook state from the first
- // invocation will be reused. That means things like `useMemo` functions won't
- // run again, because the deps will match and the memoized result will
- // be reused.
- //
- // We want memoized functions to run twice, too, so account for this, user
- // functions are double invoked during the *first* invocation of the component
- // function, and are *not* double invoked during the second incovation:
- //
- // - First execution of component function: user functions are double invoked
- // - Second execution of component function (in Strict Mode, during
- // development): user functions are not double invoked.
- //
- // This is intentional for a few reasons; most importantly, it's because of
- // how `use` works when something suspends: it reuses the promise that was
- // passed during the first attempt. This is itself a form of memoization.
- // We need to be able to memoize the reactive inputs to the `use` call using
- // a hook (i.e. `useMemo`), which means, the reactive inputs to `use` must
- // come from the same component invocation as the output.
- //
- // There are plenty of tests to ensure this behavior is correct.
-
- var shouldDoubleRenderDEV =
- (workInProgress.mode & StrictLegacyMode) !== NoMode;
- shouldDoubleInvokeUserFnsInHooksDEV = shouldDoubleRenderDEV;
- var children = Component(props, secondArg);
- shouldDoubleInvokeUserFnsInHooksDEV = false; // Check if there was a render phase update
-
- if (didScheduleRenderPhaseUpdateDuringThisPass) {
- // Keep rendering until the component stabilizes (there are no more render
- // phase updates).
- children = renderWithHooksAgain(
- workInProgress,
- Component,
- props,
- secondArg
- );
}
+}
- if (shouldDoubleRenderDEV) {
- // In development, components are invoked twice to help detect side effects.
- setIsStrictModeForDevtools(true);
+function warnOnUndefinedDerivedState(type, partialState) {
+ {
+ if (partialState === undefined) {
+ var componentName = getComponentNameFromType(type) || "Component";
- try {
- children = renderWithHooksAgain(
- workInProgress,
- Component,
- props,
- secondArg
- );
- } finally {
- setIsStrictModeForDevtools(false);
+ if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
+ didWarnAboutUndefinedDerivedState.add(componentName);
+
+ error(
+ "%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. " +
+ "You have returned undefined.",
+ componentName
+ );
+ }
}
}
-
- finishRenderingHooks(current, workInProgress);
- return children;
}
-function finishRenderingHooks(current, workInProgress) {
- // We can assume the previous dispatcher is always this one, since we set it
- // at the beginning of the render phase and there's no re-entrance.
- ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
-
- {
- workInProgress._debugHookTypes = hookTypesDev;
- } // This check uses currentHook so that it works the same in DEV and prod bundles.
- // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
-
- var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
- renderLanes$1 = NoLanes;
- currentlyRenderingFiber$1 = null;
- currentHook = null;
- workInProgressHook = null;
+function applyDerivedStateFromProps(
+ workInProgress,
+ ctor,
+ getDerivedStateFromProps,
+ nextProps
+) {
+ var prevState = workInProgress.memoizedState;
+ var partialState = getDerivedStateFromProps(nextProps, prevState);
{
- currentHookNameInDev = null;
- hookTypesDev = null;
- hookTypesUpdateIndexDev = -1; // Confirm that a static flag was not added or removed since the last
- // render. If this fires, it suggests that we incorrectly reset the static
- // flags in some other part of the codebase. This has happened before, for
- // example, in the SuspenseList implementation.
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
- if (
- current !== null &&
- (current.flags & StaticMask) !== (workInProgress.flags & StaticMask) && // Disable this warning in legacy mode, because legacy Suspense is weird
- // and creates false positives. To make this work in legacy mode, we'd
- // need to mark fibers that commit in an incomplete state, somehow. For
- // now I'll disable the warning that most of the bugs that would trigger
- // it are either exclusive to concurrent mode or exist in both.
- (current.mode & ConcurrentMode) !== NoMode
- ) {
- error(
- "Internal React error: Expected static flag was missing. Please " +
- "notify the React team."
- );
+ try {
+ // Invoke the function an extra time to help detect side-effects.
+ partialState = getDerivedStateFromProps(nextProps, prevState);
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
}
- }
- didScheduleRenderPhaseUpdate = false; // This is reset by checkDidRenderIdHook
- // localIdCounter = 0;
+ warnOnUndefinedDerivedState(ctor, partialState);
+ } // Merge the partial state and the previous state.
- thenableIndexCounter = 0;
- thenableState = null;
+ var memoizedState =
+ partialState === null || partialState === undefined
+ ? prevState
+ : assign({}, prevState, partialState);
+ workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the
+ // base state.
- if (didRenderTooFewHooks) {
- throw new Error(
- "Rendered fewer hooks than expected. This may be caused by an accidental " +
- "early return statement."
- );
+ if (workInProgress.lanes === NoLanes) {
+ // Queue is always non-null for classes
+ var updateQueue = workInProgress.updateQueue;
+ updateQueue.baseState = memoizedState;
}
+}
- if (enableLazyContextPropagation) {
- if (current !== null) {
- if (!checkIfWorkInProgressReceivedUpdate()) {
- // If there were no changes to props or state, we need to check if there
- // was a context change. We didn't already do this because there's no
- // 1:1 correspondence between dependencies and hooks. Although, because
- // there almost always is in the common case (`readContext` is an
- // internal API), we could compare in there. OTOH, we only hit this case
- // if everything else bails out, so on the whole it might be better to
- // keep the comparison out of the common path.
- var currentDependencies = current.dependencies;
+var classComponentUpdater = {
+ isMounted: isMounted,
+ // $FlowFixMe[missing-local-annot]
+ enqueueSetState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var lane = requestUpdateLane(fiber);
+ var update = createUpdate(lane);
+ update.payload = payload;
- if (
- currentDependencies !== null &&
- checkIfContextChanged(currentDependencies)
- ) {
- markWorkInProgressReceivedUpdate();
- }
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, "setState");
}
+
+ update.callback = callback;
}
- }
- {
- if (checkIfUseWrappedInTryCatch()) {
- var componentName =
- getComponentNameFromFiber(workInProgress) || "Unknown";
+ var root = enqueueUpdate(fiber, update, lane);
- if (!didWarnAboutUseWrappedInTryCatch.has(componentName)) {
- didWarnAboutUseWrappedInTryCatch.add(componentName);
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ entangleTransitions(root, fiber, lane);
+ }
- error(
- "`use` was called from inside a try/catch block. This is not allowed " +
- "and can lead to unexpected behavior. To handle errors triggered " +
- "by `use`, wrap your component in a error boundary."
- );
+ {
+ if (enableDebugTracing) {
+ if (fiber.mode & DebugTracingMode) {
+ var name = getComponentNameFromFiber(fiber) || "Unknown";
+ logStateUpdateScheduled(name, lane, payload);
+ }
}
}
- }
-}
-function replaySuspendedComponentWithHooks(
- current,
- workInProgress,
- Component,
- props,
- secondArg
-) {
- // This function is used to replay a component that previously suspended,
- // after its data resolves.
- //
- // It's a simplified version of renderWithHooks, but it doesn't need to do
- // most of the set up work because they weren't reset when we suspended; they
- // only get reset when the component either completes (finishRenderingHooks)
- // or unwinds (resetHooksOnUnwind).
- {
- hookTypesUpdateIndexDev = -1; // Used for hot reloading:
+ if (enableSchedulingProfiler) {
+ markStateUpdateScheduled(fiber, lane);
+ }
+ },
+ enqueueReplaceState: function (inst, payload, callback) {
+ var fiber = get(inst);
+ var lane = requestUpdateLane(fiber);
+ var update = createUpdate(lane);
+ update.tag = ReplaceState;
+ update.payload = payload;
- ignorePreviousDependencies =
- current !== null && current.type !== workInProgress.type;
- }
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, "replaceState");
+ }
- var children = renderWithHooksAgain(
- workInProgress,
- Component,
- props,
- secondArg
- );
- finishRenderingHooks(current, workInProgress);
- return children;
-}
+ update.callback = callback;
+ }
-function renderWithHooksAgain(workInProgress, Component, props, secondArg) {
- // This is used to perform another render pass. It's used when setState is
- // called during render, and for double invoking components in Strict Mode
- // during development.
- //
- // The state from the previous pass is reused whenever possible. So, state
- // updates that were already processed are not processed again, and memoized
- // functions (`useMemo`) are not invoked again.
- //
- // Keep rendering in a loop for as long as render phase updates continue to
- // be scheduled. Use a counter to prevent infinite loops.
- var numberOfReRenders = 0;
- var children;
+ var root = enqueueUpdate(fiber, update, lane);
- do {
- if (didScheduleRenderPhaseUpdateDuringThisPass) {
- // It's possible that a use() value depended on a state that was updated in
- // this rerender, so we need to watch for different thenables this time.
- thenableState = null;
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ entangleTransitions(root, fiber, lane);
}
- thenableIndexCounter = 0;
- didScheduleRenderPhaseUpdateDuringThisPass = false;
+ {
+ if (enableDebugTracing) {
+ if (fiber.mode & DebugTracingMode) {
+ var name = getComponentNameFromFiber(fiber) || "Unknown";
+ logStateUpdateScheduled(name, lane, payload);
+ }
+ }
+ }
- if (numberOfReRenders >= RE_RENDER_LIMIT) {
- throw new Error(
- "Too many re-renders. React limits the number of renders to prevent " +
- "an infinite loop."
- );
+ if (enableSchedulingProfiler) {
+ markStateUpdateScheduled(fiber, lane);
}
+ },
+ // $FlowFixMe[missing-local-annot]
+ enqueueForceUpdate: function (inst, callback) {
+ var fiber = get(inst);
+ var lane = requestUpdateLane(fiber);
+ var update = createUpdate(lane);
+ update.tag = ForceUpdate;
- numberOfReRenders += 1;
+ if (callback !== undefined && callback !== null) {
+ {
+ warnOnInvalidCallback$1(callback, "forceUpdate");
+ }
- {
- // Even when hot reloading, allow dependencies to stabilize
- // after first render to prevent infinite render phase updates.
- ignorePreviousDependencies = false;
- } // Start over from the beginning of the list
+ update.callback = callback;
+ }
- currentHook = null;
- workInProgressHook = null;
- workInProgress.updateQueue = null;
+ var root = enqueueUpdate(fiber, update, lane);
- {
- // Also validate hook order for cascading updates.
- hookTypesUpdateIndexDev = -1;
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ entangleTransitions(root, fiber, lane);
}
- ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV;
- children = Component(props, secondArg);
- } while (didScheduleRenderPhaseUpdateDuringThisPass);
+ {
+ if (enableDebugTracing) {
+ if (fiber.mode & DebugTracingMode) {
+ var name = getComponentNameFromFiber(fiber) || "Unknown";
+ logForceUpdateScheduled(name, lane);
+ }
+ }
+ }
- return children;
-}
+ if (enableSchedulingProfiler) {
+ markForceUpdateScheduled(fiber, lane);
+ }
+ }
+};
-function checkDidRenderIdHook() {
- // This should be called immediately after every renderWithHooks call.
- // Conceptually, it's part of the return value of renderWithHooks; it's only a
- // separate function to avoid using an array tuple.
- var didRenderIdHook = localIdCounter !== 0;
- localIdCounter = 0;
- return didRenderIdHook;
-}
-function bailoutHooks(current, workInProgress, lanes) {
- workInProgress.updateQueue = current.updateQueue; // TODO: Don't need to reset the flags here, because they're reset in the
- // complete phase (bubbleProperties).
+function checkShouldComponentUpdate(
+ workInProgress,
+ ctor,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ nextContext
+) {
+ var instance = workInProgress.stateNode;
- if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
- workInProgress.flags &= ~(
- MountPassiveDev |
- MountLayoutDev |
- Passive$1 |
- Update
+ if (typeof instance.shouldComponentUpdate === "function") {
+ var shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext
);
- } else {
- workInProgress.flags &= ~(Passive$1 | Update);
- }
-
- current.lanes = removeLanes(current.lanes, lanes);
-}
-function resetHooksAfterThrow() {
- // This is called immediaetly after a throw. It shouldn't reset the entire
- // module state, because the work loop might decide to replay the component
- // again without rewinding.
- //
- // It should only reset things like the current dispatcher, to prevent hooks
- // from being called outside of a component.
- // We can assume the previous dispatcher is always this one, since we set it
- // at the beginning of the render phase and there's no re-entrance.
- ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
-}
-function resetHooksOnUnwind() {
- if (didScheduleRenderPhaseUpdate) {
- // There were render phase updates. These are only valid for this render
- // phase, which we are now aborting. Remove the updates from the queues so
- // they do not persist to the next render. Do not remove updates from hooks
- // that weren't processed.
- //
- // Only reset the updates from the queue if it has a clone. If it does
- // not have a clone, that means it wasn't processed, and the updates were
- // scheduled before we entered the render phase.
- var hook = currentlyRenderingFiber$1.memoizedState;
- while (hook !== null) {
- var queue = hook.queue;
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
- if (queue !== null) {
- queue.pending = null;
+ try {
+ // Invoke the function an extra time to help detect side-effects.
+ shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ nextContext
+ );
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
}
- hook = hook.next;
+ if (shouldUpdate === undefined) {
+ error(
+ "%s.shouldComponentUpdate(): Returned undefined instead of a " +
+ "boolean value. Make sure to return true or false.",
+ getComponentNameFromType(ctor) || "Component"
+ );
+ }
}
- didScheduleRenderPhaseUpdate = false;
+ return shouldUpdate;
}
- renderLanes$1 = NoLanes;
- currentlyRenderingFiber$1 = null;
- currentHook = null;
- workInProgressHook = null;
-
- {
- hookTypesDev = null;
- hookTypesUpdateIndexDev = -1;
- currentHookNameInDev = null;
+ if (ctor.prototype && ctor.prototype.isPureReactComponent) {
+ return (
+ !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
+ );
}
- didScheduleRenderPhaseUpdateDuringThisPass = false;
- localIdCounter = 0;
- thenableIndexCounter = 0;
- thenableState = null;
+ return true;
}
-function mountWorkInProgressHook() {
- var hook = {
- memoizedState: null,
- baseState: null,
- baseQueue: null,
- queue: null,
- next: null
- };
+function checkClassInstance(workInProgress, ctor, newProps) {
+ var instance = workInProgress.stateNode;
- if (workInProgressHook === null) {
- // This is the first hook in the list
- currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
- } else {
- // Append to the end of the list
- workInProgressHook = workInProgressHook.next = hook;
- }
+ {
+ var name = getComponentNameFromType(ctor) || "Component";
+ var renderPresent = instance.render;
- return workInProgressHook;
-}
+ if (!renderPresent) {
+ if (ctor.prototype && typeof ctor.prototype.render === "function") {
+ error(
+ "%s(...): No `render` method found on the returned component " +
+ "instance: did you accidentally return an object from the constructor?",
+ name
+ );
+ } else {
+ error(
+ "%s(...): No `render` method found on the returned component " +
+ "instance: you may have forgotten to define `render`.",
+ name
+ );
+ }
+ }
-function updateWorkInProgressHook() {
- // This function is used both for updates and for re-renders triggered by a
- // render phase update. It assumes there is either a current hook we can
- // clone, or a work-in-progress hook from a previous render pass that we can
- // use as a base.
- var nextCurrentHook;
-
- if (currentHook === null) {
- var current = currentlyRenderingFiber$1.alternate;
-
- if (current !== null) {
- nextCurrentHook = current.memoizedState;
- } else {
- nextCurrentHook = null;
+ if (
+ instance.getInitialState &&
+ !instance.getInitialState.isReactClassApproved &&
+ !instance.state
+ ) {
+ error(
+ "getInitialState was defined on %s, a plain JavaScript class. " +
+ "This is only supported for classes created using React.createClass. " +
+ "Did you mean to define a state property instead?",
+ name
+ );
}
- } else {
- nextCurrentHook = currentHook.next;
- }
-
- var nextWorkInProgressHook;
-
- if (workInProgressHook === null) {
- nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
- } else {
- nextWorkInProgressHook = workInProgressHook.next;
- }
-
- if (nextWorkInProgressHook !== null) {
- // There's already a work-in-progress. Reuse it.
- workInProgressHook = nextWorkInProgressHook;
- nextWorkInProgressHook = workInProgressHook.next;
- currentHook = nextCurrentHook;
- } else {
- // Clone from the current hook.
- if (nextCurrentHook === null) {
- var currentFiber = currentlyRenderingFiber$1.alternate;
- if (currentFiber === null) {
- // This is the initial render. This branch is reached when the component
- // suspends, resumes, then renders an additional hook.
- // Should never be reached because we should switch to the mount dispatcher first.
- throw new Error(
- "Update hook called on initial render. This is likely a bug in React. Please file an issue."
- );
- } else {
- // This is an update. We should always have a current hook.
- throw new Error("Rendered more hooks than during the previous render.");
- }
+ if (
+ instance.getDefaultProps &&
+ !instance.getDefaultProps.isReactClassApproved
+ ) {
+ error(
+ "getDefaultProps was defined on %s, a plain JavaScript class. " +
+ "This is only supported for classes created using React.createClass. " +
+ "Use a static property to define defaultProps instead.",
+ name
+ );
}
- currentHook = nextCurrentHook;
- var newHook = {
- memoizedState: currentHook.memoizedState,
- baseState: currentHook.baseState,
- baseQueue: currentHook.baseQueue,
- queue: currentHook.queue,
- next: null
- };
-
- if (workInProgressHook === null) {
- // This is the first hook in the list.
- currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
- } else {
- // Append to the end of the list.
- workInProgressHook = workInProgressHook.next = newHook;
+ if (instance.propTypes) {
+ error(
+ "propTypes was defined as an instance property on %s. Use a static " +
+ "property to define propTypes instead.",
+ name
+ );
}
- }
-
- return workInProgressHook;
-} // NOTE: defining two versions of this function to avoid size impact when this feature is disabled.
-// Previously this function was inlined, the additional `memoCache` property makes it not inlined.
-
-var createFunctionComponentUpdateQueue;
-
-{
- createFunctionComponentUpdateQueue = function () {
- return {
- lastEffect: null,
- events: null,
- stores: null,
- memoCache: null
- };
- };
-}
-function use(usable) {
- if (usable !== null && typeof usable === "object") {
- // $FlowFixMe[method-unbinding]
- if (typeof usable.then === "function") {
- // This is a thenable.
- var thenable = usable; // Track the position of the thenable within this fiber.
-
- var index = thenableIndexCounter;
- thenableIndexCounter += 1;
+ if (instance.contextType) {
+ error(
+ "contextType was defined as an instance property on %s. Use a static " +
+ "property to define contextType instead.",
+ name
+ );
+ }
- if (thenableState === null) {
- thenableState = createThenableState();
+ {
+ if (instance.contextTypes) {
+ error(
+ "contextTypes was defined as an instance property on %s. Use a static " +
+ "property to define contextTypes instead.",
+ name
+ );
}
- var result = trackUsedThenable(thenableState, thenable, index);
-
if (
- currentlyRenderingFiber$1.alternate === null &&
- (workInProgressHook === null
- ? currentlyRenderingFiber$1.memoizedState === null
- : workInProgressHook.next === null)
+ ctor.contextType &&
+ ctor.contextTypes &&
+ !didWarnAboutContextTypeAndContextTypes.has(ctor)
) {
- // Initial render, and either this is the first time the component is
- // called, or there were no Hooks called after this use() the previous
- // time (perhaps because it threw). Subsequent Hook calls should use the
- // mount dispatcher.
- {
- ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
- }
+ didWarnAboutContextTypeAndContextTypes.add(ctor);
+
+ error(
+ "%s declares both contextTypes and contextType static properties. " +
+ "The legacy contextTypes property will be ignored.",
+ name
+ );
}
+ }
- return result;
- } else if (
- usable.$$typeof === REACT_CONTEXT_TYPE ||
- usable.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ if (typeof instance.componentShouldUpdate === "function") {
+ error(
+ "%s has a method called " +
+ "componentShouldUpdate(). Did you mean shouldComponentUpdate()? " +
+ "The name is phrased as a question because the function is " +
+ "expected to return a value.",
+ name
+ );
+ }
+
+ if (
+ ctor.prototype &&
+ ctor.prototype.isPureReactComponent &&
+ typeof instance.shouldComponentUpdate !== "undefined"
) {
- var context = usable;
- return readContext(context);
+ error(
+ "%s has a method called shouldComponentUpdate(). " +
+ "shouldComponentUpdate should not be used when extending React.PureComponent. " +
+ "Please extend React.Component if shouldComponentUpdate is used.",
+ getComponentNameFromType(ctor) || "A pure component"
+ );
}
- } // eslint-disable-next-line react-internal/safe-string-coercion
- throw new Error("An unsupported type was passed to use(): " + String(usable));
-}
+ if (typeof instance.componentDidUnmount === "function") {
+ error(
+ "%s has a method called " +
+ "componentDidUnmount(). But there is no such lifecycle method. " +
+ "Did you mean componentWillUnmount()?",
+ name
+ );
+ }
-function useMemoCache(size) {
- var memoCache = null; // Fast-path, load memo cache from wip fiber if already prepared
+ if (typeof instance.componentDidReceiveProps === "function") {
+ error(
+ "%s has a method called " +
+ "componentDidReceiveProps(). But there is no such lifecycle method. " +
+ "If you meant to update the state in response to changing props, " +
+ "use componentWillReceiveProps(). If you meant to fetch data or " +
+ "run side-effects or mutations after React has updated the UI, use componentDidUpdate().",
+ name
+ );
+ }
- var updateQueue = currentlyRenderingFiber$1.updateQueue;
+ if (typeof instance.componentWillRecieveProps === "function") {
+ error(
+ "%s has a method called " +
+ "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",
+ name
+ );
+ }
- if (updateQueue !== null) {
- memoCache = updateQueue.memoCache;
- } // Otherwise clone from the current fiber
+ if (typeof instance.UNSAFE_componentWillRecieveProps === "function") {
+ error(
+ "%s has a method called " +
+ "UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?",
+ name
+ );
+ }
- if (memoCache == null) {
- var current = currentlyRenderingFiber$1.alternate;
+ var hasMutatedProps = instance.props !== newProps;
- if (current !== null) {
- var currentUpdateQueue = current.updateQueue;
+ if (instance.props !== undefined && hasMutatedProps) {
+ error(
+ "%s(...): When calling super() in `%s`, make sure to pass " +
+ "up the same props that your component's constructor was passed.",
+ name,
+ name
+ );
+ }
- if (currentUpdateQueue !== null) {
- var currentMemoCache = currentUpdateQueue.memoCache;
+ if (instance.defaultProps) {
+ error(
+ "Setting defaultProps as an instance property on %s is not supported and will be ignored." +
+ " Instead, define defaultProps as a static property on %s.",
+ name,
+ name
+ );
+ }
- if (currentMemoCache != null) {
- memoCache = {
- data: currentMemoCache.data.map(function (array) {
- return array.slice();
- }),
- index: 0
- };
- }
- }
+ if (
+ typeof instance.getSnapshotBeforeUpdate === "function" &&
+ typeof instance.componentDidUpdate !== "function" &&
+ !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)
+ ) {
+ didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
+
+ error(
+ "%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). " +
+ "This component defines getSnapshotBeforeUpdate() only.",
+ getComponentNameFromType(ctor)
+ );
}
- } // Finally fall back to allocating a fresh instance of the cache
- if (memoCache == null) {
- memoCache = {
- data: [],
- index: 0
- };
- }
+ if (typeof instance.getDerivedStateFromProps === "function") {
+ error(
+ "%s: getDerivedStateFromProps() is defined as an instance method " +
+ "and will be ignored. Instead, declare it as a static method.",
+ name
+ );
+ }
- if (updateQueue === null) {
- updateQueue = createFunctionComponentUpdateQueue();
- currentlyRenderingFiber$1.updateQueue = updateQueue;
- }
+ if (typeof instance.getDerivedStateFromError === "function") {
+ error(
+ "%s: getDerivedStateFromError() is defined as an instance method " +
+ "and will be ignored. Instead, declare it as a static method.",
+ name
+ );
+ }
- updateQueue.memoCache = memoCache;
- var data = memoCache.data[memoCache.index];
+ if (typeof ctor.getSnapshotBeforeUpdate === "function") {
+ error(
+ "%s: getSnapshotBeforeUpdate() is defined as a static method " +
+ "and will be ignored. Instead, declare it as an instance method.",
+ name
+ );
+ }
- if (data === undefined) {
- data = memoCache.data[memoCache.index] = new Array(size);
+ var state = instance.state;
- for (var i = 0; i < size; i++) {
- data[i] = REACT_MEMO_CACHE_SENTINEL;
+ if (state && (typeof state !== "object" || isArray(state))) {
+ error("%s.state: must be set to an object or null", name);
}
- } else if (data.length !== size) {
- // TODO: consider warning or throwing here
- {
+
+ if (
+ typeof instance.getChildContext === "function" &&
+ typeof ctor.childContextTypes !== "object"
+ ) {
error(
- "Expected a constant size argument for each invocation of useMemoCache. " +
- "The previous cache was allocated with size %s but size %s was requested.",
- data.length,
- size
+ "%s.getChildContext(): childContextTypes must be defined in order to " +
+ "use getChildContext().",
+ name
);
}
}
-
- memoCache.index++;
- return data;
}
-function basicStateReducer(state, action) {
- // $FlowFixMe: Flow doesn't like mixed types
- return typeof action === "function" ? action(state) : action;
-}
+function adoptClassInstance(workInProgress, instance) {
+ instance.updater = classComponentUpdater;
+ workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates
-function mountReducer(reducer, initialArg, init) {
- var hook = mountWorkInProgressHook();
- var initialState;
+ set(instance, workInProgress);
- if (init !== undefined) {
- initialState = init(initialArg);
- } else {
- initialState = initialArg;
+ {
+ instance._reactInternalInstance = fakeInternalInstance;
}
-
- hook.memoizedState = hook.baseState = initialState;
- var queue = {
- pending: null,
- lanes: NoLanes,
- dispatch: null,
- lastRenderedReducer: reducer,
- lastRenderedState: initialState
- };
- hook.queue = queue;
- var dispatch = (queue.dispatch = dispatchReducerAction.bind(
- null,
- currentlyRenderingFiber$1,
- queue
- ));
- return [hook.memoizedState, dispatch];
}
-function updateReducer(reducer, initialArg, init) {
- var hook = updateWorkInProgressHook();
- var queue = hook.queue;
-
- if (queue === null) {
- throw new Error(
- "Should have a queue. This is likely a bug in React. Please file an issue."
- );
- }
-
- queue.lastRenderedReducer = reducer;
- var current = currentHook; // The last rebase update that is NOT part of the base state.
+function constructClassInstance(workInProgress, ctor, props) {
+ var isLegacyContextConsumer = false;
+ var unmaskedContext = emptyContextObject;
+ var context = emptyContextObject;
+ var contextType = ctor.contextType;
- var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
+ {
+ if ("contextType" in ctor) {
+ var isValid = // Allow null for conditional declaration
+ contextType === null ||
+ (contextType !== undefined &&
+ contextType.$$typeof === REACT_CONTEXT_TYPE &&
+ contextType._context === undefined); // Not a
- var pendingQueue = queue.pending;
+ if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
+ didWarnAboutInvalidateContextType.add(ctor);
+ var addendum = "";
- if (pendingQueue !== null) {
- // We have new updates that haven't been processed yet.
- // We'll add them to the base queue.
- if (baseQueue !== null) {
- // Merge the pending queue and the base queue.
- var baseFirst = baseQueue.next;
- var pendingFirst = pendingQueue.next;
- baseQueue.next = pendingFirst;
- pendingQueue.next = baseFirst;
- }
+ if (contextType === undefined) {
+ addendum =
+ " However, it is set to undefined. " +
+ "This can be caused by a typo or by mixing up named and default imports. " +
+ "This can also happen due to a circular dependency, so " +
+ "try moving the createContext() call to a separate file.";
+ } else if (typeof contextType !== "object") {
+ addendum = " However, it is set to a " + typeof contextType + ".";
+ } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
+ addendum = " Did you accidentally pass the Context.Provider instead?";
+ } else if (contextType._context !== undefined) {
+ //
+ addendum = " Did you accidentally pass the Context.Consumer instead?";
+ } else {
+ addendum =
+ " However, it is set to an object with keys {" +
+ Object.keys(contextType).join(", ") +
+ "}.";
+ }
- {
- if (current.baseQueue !== baseQueue) {
- // Internal invariant that should never happen, but feasibly could in
- // the future if we implement resuming, or some form of that.
error(
- "Internal error: Expected work-in-progress queue to be a clone. " +
- "This is a bug in React."
+ "%s defines an invalid contextType. " +
+ "contextType should point to the Context object returned by React.createContext().%s",
+ getComponentNameFromType(ctor) || "Component",
+ addendum
);
}
}
+ }
- current.baseQueue = baseQueue = pendingQueue;
- queue.pending = null;
+ if (typeof contextType === "object" && contextType !== null) {
+ context = readContext(contextType);
+ } else {
+ unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ var contextTypes = ctor.contextTypes;
+ isLegacyContextConsumer =
+ contextTypes !== null && contextTypes !== undefined;
+ context = isLegacyContextConsumer
+ ? getMaskedContext(workInProgress, unmaskedContext)
+ : emptyContextObject;
}
- if (baseQueue !== null) {
- // We have a queue to process.
- var first = baseQueue.next;
- var newState = current.baseState;
- var newBaseState = null;
- var newBaseQueueFirst = null;
- var newBaseQueueLast = null;
- var update = first;
+ var instance = new ctor(props, context); // Instantiate twice to help detect side-effects.
- do {
- // An extra OffscreenLane bit is added to updates that were made to
- // a hidden tree, so that we can distinguish them from updates that were
- // already there when the tree was hidden.
- var updateLane = removeLanes(update.lane, OffscreenLane);
- var isHiddenUpdate = updateLane !== update.lane; // Check if this update was made while the tree was hidden. If so, then
- // it's not a "base" update and we should disregard the extra base lanes
- // that were added to renderLanes when we entered the Offscreen tree.
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
- var shouldSkipUpdate = isHiddenUpdate
- ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
- : !isSubsetOfLanes(renderLanes$1, updateLane);
+ try {
+ instance = new ctor(props, context); // eslint-disable-line no-new
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
+ }
+ }
- if (shouldSkipUpdate) {
- // Priority is insufficient. Skip this update. If this is the first
- // skipped update, the previous update/state is the new base
- // update/state.
- var clone = {
- lane: updateLane,
- action: update.action,
- hasEagerState: update.hasEagerState,
- eagerState: update.eagerState,
- next: null
- };
+ var state = (workInProgress.memoizedState =
+ instance.state !== null && instance.state !== undefined
+ ? instance.state
+ : null);
+ adoptClassInstance(workInProgress, instance);
- if (newBaseQueueLast === null) {
- newBaseQueueFirst = newBaseQueueLast = clone;
- newBaseState = newState;
- } else {
- newBaseQueueLast = newBaseQueueLast.next = clone;
- } // Update the remaining priority in the queue.
- // TODO: Don't need to accumulate this. Instead, we can remove
- // renderLanes from the original lanes.
+ {
+ if (typeof ctor.getDerivedStateFromProps === "function" && state === null) {
+ var componentName = getComponentNameFromType(ctor) || "Component";
- currentlyRenderingFiber$1.lanes = mergeLanes(
- currentlyRenderingFiber$1.lanes,
- updateLane
+ if (!didWarnAboutUninitializedState.has(componentName)) {
+ didWarnAboutUninitializedState.add(componentName);
+
+ error(
+ "`%s` uses `getDerivedStateFromProps` but its initial state is " +
+ "%s. This is not recommended. Instead, define the initial state by " +
+ "assigning an object to `this.state` in the constructor of `%s`. " +
+ "This ensures that `getDerivedStateFromProps` arguments have a consistent shape.",
+ componentName,
+ instance.state === null ? "null" : "undefined",
+ componentName
);
- markSkippedUpdateLanes(updateLane);
- } else {
- // This update does have sufficient priority.
- if (newBaseQueueLast !== null) {
- var _clone = {
- // This update is going to be committed so we never want uncommit
- // it. Using NoLane works because 0 is a subset of all bitmasks, so
- // this will never be skipped by the check above.
- lane: NoLane,
- action: update.action,
- hasEagerState: update.hasEagerState,
- eagerState: update.eagerState,
- next: null
- };
- newBaseQueueLast = newBaseQueueLast.next = _clone;
- } // Process this update.
+ }
+ } // If new component APIs are defined, "unsafe" lifecycles won't be called.
+ // Warn about these lifecycles if they are present.
+ // Don't warn about react-lifecycles-compat polyfilled methods though.
- var action = update.action;
+ if (
+ typeof ctor.getDerivedStateFromProps === "function" ||
+ typeof instance.getSnapshotBeforeUpdate === "function"
+ ) {
+ var foundWillMountName = null;
+ var foundWillReceivePropsName = null;
+ var foundWillUpdateName = null;
- if (shouldDoubleInvokeUserFnsInHooksDEV) {
- reducer(newState, action);
- }
+ if (
+ typeof instance.componentWillMount === "function" &&
+ instance.componentWillMount.__suppressDeprecationWarning !== true
+ ) {
+ foundWillMountName = "componentWillMount";
+ } else if (typeof instance.UNSAFE_componentWillMount === "function") {
+ foundWillMountName = "UNSAFE_componentWillMount";
+ }
- if (update.hasEagerState) {
- // If this update is a state update (not a reducer) and was processed eagerly,
- // we can use the eagerly computed state
- newState = update.eagerState;
- } else {
- newState = reducer(newState, action);
- }
+ if (
+ typeof instance.componentWillReceiveProps === "function" &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
+ ) {
+ foundWillReceivePropsName = "componentWillReceiveProps";
+ } else if (
+ typeof instance.UNSAFE_componentWillReceiveProps === "function"
+ ) {
+ foundWillReceivePropsName = "UNSAFE_componentWillReceiveProps";
}
- update = update.next;
- } while (update !== null && update !== first);
+ if (
+ typeof instance.componentWillUpdate === "function" &&
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
+ ) {
+ foundWillUpdateName = "componentWillUpdate";
+ } else if (typeof instance.UNSAFE_componentWillUpdate === "function") {
+ foundWillUpdateName = "UNSAFE_componentWillUpdate";
+ }
- if (newBaseQueueLast === null) {
- newBaseState = newState;
- } else {
- newBaseQueueLast.next = newBaseQueueFirst;
- } // Mark that the fiber performed work, but only if the new state is
- // different from the current state.
+ if (
+ foundWillMountName !== null ||
+ foundWillReceivePropsName !== null ||
+ foundWillUpdateName !== null
+ ) {
+ var _componentName = getComponentNameFromType(ctor) || "Component";
- if (!objectIs(newState, hook.memoizedState)) {
- markWorkInProgressReceivedUpdate();
- }
+ var newApiName =
+ typeof ctor.getDerivedStateFromProps === "function"
+ ? "getDerivedStateFromProps()"
+ : "getSnapshotBeforeUpdate()";
- hook.memoizedState = newState;
- hook.baseState = newBaseState;
- hook.baseQueue = newBaseQueueLast;
- queue.lastRenderedState = newState;
- }
+ if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) {
+ didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName);
- if (baseQueue === null) {
- // `queue.lanes` is used for entangling transitions. We can set it back to
- // zero once the queue is empty.
- queue.lanes = NoLanes;
+ error(
+ "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" +
+ "%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n" +
+ "The above lifecycles should be removed. Learn more about this warning here:\n" +
+ "https://reactjs.org/link/unsafe-component-lifecycles",
+ _componentName,
+ newApiName,
+ foundWillMountName !== null ? "\n " + foundWillMountName : "",
+ foundWillReceivePropsName !== null
+ ? "\n " + foundWillReceivePropsName
+ : "",
+ foundWillUpdateName !== null ? "\n " + foundWillUpdateName : ""
+ );
+ }
+ }
+ }
+ } // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // ReactFiberContext usually updates this cache but can't for newly-created instances.
+
+ if (isLegacyContextConsumer) {
+ cacheContext(workInProgress, unmaskedContext, context);
}
- var dispatch = queue.dispatch;
- return [hook.memoizedState, dispatch];
+ return instance;
}
-function rerenderReducer(reducer, initialArg, init) {
- var hook = updateWorkInProgressHook();
- var queue = hook.queue;
+function callComponentWillMount(workInProgress, instance) {
+ var oldState = instance.state;
- if (queue === null) {
- throw new Error(
- "Should have a queue. This is likely a bug in React. Please file an issue."
- );
+ if (typeof instance.componentWillMount === "function") {
+ instance.componentWillMount();
}
- queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous
- // work-in-progress hook.
+ if (typeof instance.UNSAFE_componentWillMount === "function") {
+ instance.UNSAFE_componentWillMount();
+ }
- var dispatch = queue.dispatch;
- var lastRenderPhaseUpdate = queue.pending;
- var newState = hook.memoizedState;
+ if (oldState !== instance.state) {
+ {
+ error(
+ "%s.componentWillMount(): Assigning directly to this.state is " +
+ "deprecated (except inside a component's " +
+ "constructor). Use setState instead.",
+ getComponentNameFromFiber(workInProgress) || "Component"
+ );
+ }
- if (lastRenderPhaseUpdate !== null) {
- // The queue doesn't persist past this render pass.
- queue.pending = null;
- var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
- var update = firstRenderPhaseUpdate;
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ }
+}
- do {
- // Process this render phase update. We don't have to check the
- // priority because it will always be the same as the current
- // render's.
- var action = update.action;
- newState = reducer(newState, action);
- update = update.next;
- } while (update !== firstRenderPhaseUpdate); // Mark that the fiber performed work, but only if the new state is
- // different from the current state.
+function callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ nextContext
+) {
+ var oldState = instance.state;
- if (!objectIs(newState, hook.memoizedState)) {
- markWorkInProgressReceivedUpdate();
- }
+ if (typeof instance.componentWillReceiveProps === "function") {
+ instance.componentWillReceiveProps(newProps, nextContext);
+ }
- hook.memoizedState = newState; // Don't persist the state accumulated from the render phase updates to
- // the base state unless the queue is empty.
- // TODO: Not sure if this is the desired semantics, but it's what we
- // do for gDSFP. I can't remember why.
+ if (typeof instance.UNSAFE_componentWillReceiveProps === "function") {
+ instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
+ }
- if (hook.baseQueue === null) {
- hook.baseState = newState;
+ if (instance.state !== oldState) {
+ {
+ var componentName =
+ getComponentNameFromFiber(workInProgress) || "Component";
+
+ if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
+ didWarnAboutStateAssignmentForComponent.add(componentName);
+
+ error(
+ "%s.componentWillReceiveProps(): Assigning directly to " +
+ "this.state is deprecated (except inside a component's " +
+ "constructor). Use setState instead.",
+ componentName
+ );
+ }
}
- queue.lastRenderedState = newState;
+ classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
}
+} // Invokes the mount life-cycles on a previously never rendered instance.
- return [newState, dispatch];
-}
-
-function readFromUnsubscribedMutableSource(root, source, getSnapshot) {
+function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
{
- warnAboutMultipleRenderersDEV(source);
+ checkClassInstance(workInProgress, ctor, newProps);
}
- var getVersion = source._getVersion;
- var version = getVersion(source._source); // Is it safe for this component to read from this source during the current render?
-
- var isSafeToReadFromSource = false; // Check the version first.
- // If this render has already been started with a specific version,
- // we can use it alone to determine if we can safely read from the source.
-
- var currentRenderVersion = getWorkInProgressVersion(source);
+ var instance = workInProgress.stateNode;
+ instance.props = newProps;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = {};
+ initializeUpdateQueue(workInProgress);
+ var contextType = ctor.contextType;
- if (currentRenderVersion !== null) {
- // It's safe to read if the store hasn't been mutated since the last time
- // we read something.
- isSafeToReadFromSource = currentRenderVersion === version;
+ if (typeof contextType === "object" && contextType !== null) {
+ instance.context = readContext(contextType);
} else {
- // If there's no version, then this is the first time we've read from the
- // source during the current render pass, so we need to do a bit more work.
- // What we need to determine is if there are any hooks that already
- // subscribed to the source, and if so, whether there are any pending
- // mutations that haven't been synchronized yet.
- //
- // If there are no pending mutations, then `root.mutableReadLanes` will be
- // empty, and we know we can safely read.
- //
- // If there *are* pending mutations, we may still be able to safely read
- // if the currently rendering lanes are inclusive of the pending mutation
- // lanes, since that guarantees that the value we're about to read from
- // the source is consistent with the values that we read during the most
- // recent mutation.
- isSafeToReadFromSource = isSubsetOfLanes(
- renderLanes$1,
- root.mutableReadLanes
- );
-
- if (isSafeToReadFromSource) {
- // If it's safe to read from this source during the current render,
- // store the version in case other components read from it.
- // A changed version number will let those components know to throw and restart the render.
- setWorkInProgressVersion(source, version);
- }
+ var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
- if (isSafeToReadFromSource) {
- var snapshot = getSnapshot(source._source);
+ {
+ if (instance.state === newProps) {
+ var componentName = getComponentNameFromType(ctor) || "Component";
+
+ if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
+ didWarnAboutDirectlyAssigningPropsToState.add(componentName);
- {
- if (typeof snapshot === "function") {
error(
- "Mutable source should not return a function as the snapshot value. " +
- "Functions may close over mutable values and cause tearing."
+ "%s: It is not recommended to assign props directly to state " +
+ "because updates to props won't be reflected in state. " +
+ "In most cases, it is better to use props directly.",
+ componentName
);
}
}
- return snapshot;
- } else {
- // This handles the special case of a mutable source being shared between renderers.
- // In that case, if the source is mutated between the first and second renderer,
- // The second renderer don't know that it needs to reset the WIP version during unwind,
- // (because the hook only marks sources as dirty if it's written to their WIP version).
- // That would cause this tear check to throw again and eventually be visible to the user.
- // We can avoid this infinite loop by explicitly marking the source as dirty.
- //
- // This can lead to tearing in the first renderer when it resumes,
- // but there's nothing we can do about that (short of throwing here and refusing to continue the render).
- markSourceAsDirty(source); // Intentioally throw an error to force React to retry synchronously. During
- // the synchronous retry, it will block interleaved mutations, so we should
- // get a consistent read. Therefore, the following error should never be
- // visible to the user.
- // We expect this error not to be thrown during the synchronous retry,
- // because we blocked interleaved mutations.
+ if (workInProgress.mode & StrictLegacyMode) {
+ ReactStrictModeWarnings.recordLegacyContextWarning(
+ workInProgress,
+ instance
+ );
+ }
- throw new Error(
- "Cannot read from mutable source during the current render without tearing. This may be a bug in React. Please file an issue."
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
+ workInProgress,
+ instance
);
}
-}
-function useMutableSource(hook, source, getSnapshot, subscribe) {
- var root = getWorkInProgressRoot();
+ instance.state = workInProgress.memoizedState;
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
- if (root === null) {
- throw new Error(
- "Expected a work-in-progress root. This is a bug in React. Please file an issue."
+ if (typeof getDerivedStateFromProps === "function") {
+ applyDerivedStateFromProps(
+ workInProgress,
+ ctor,
+ getDerivedStateFromProps,
+ newProps
);
- }
+ instance.state = workInProgress.memoizedState;
+ } // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
- var getVersion = source._getVersion;
- var version = getVersion(source._source);
- var dispatcher = ReactCurrentDispatcher$1.current; // eslint-disable-next-line prefer-const
+ if (
+ typeof ctor.getDerivedStateFromProps !== "function" &&
+ typeof instance.getSnapshotBeforeUpdate !== "function" &&
+ (typeof instance.UNSAFE_componentWillMount === "function" ||
+ typeof instance.componentWillMount === "function")
+ ) {
+ callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's
+ // process them now.
- var _dispatcher$useState = dispatcher.useState(function () {
- return readFromUnsubscribedMutableSource(root, source, getSnapshot);
- }),
- currentSnapshot = _dispatcher$useState[0],
- setSnapshot = _dispatcher$useState[1];
+ processUpdateQueue(workInProgress, newProps, instance, renderLanes);
+ instance.state = workInProgress.memoizedState;
+ }
- var snapshot = currentSnapshot; // Grab a handle to the state hook as well.
- // We use it to clear the pending update queue if we have a new source.
+ if (typeof instance.componentDidMount === "function") {
+ var fiberFlags = Update | LayoutStatic;
- var stateHook = workInProgressHook;
- var memoizedState = hook.memoizedState;
- var refs = memoizedState.refs;
- var prevGetSnapshot = refs.getSnapshot;
- var prevSource = memoizedState.source;
- var prevSubscribe = memoizedState.subscribe;
- var fiber = currentlyRenderingFiber$1;
- hook.memoizedState = {
- refs: refs,
- source: source,
- subscribe: subscribe
- }; // Sync the values needed by our subscription handler after each commit.
+ if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
+ fiberFlags |= MountLayoutDev;
+ }
- dispatcher.useEffect(
- function () {
- refs.getSnapshot = getSnapshot; // Normally the dispatch function for a state hook never changes,
- // but this hook recreates the queue in certain cases to avoid updates from stale sources.
- // handleChange() below needs to reference the dispatch function without re-subscribing,
- // so we use a ref to ensure that it always has the latest version.
+ workInProgress.flags |= fiberFlags;
+ }
+}
- refs.setSnapshot = setSnapshot; // Check for a possible change between when we last rendered now.
+function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) {
+ var instance = workInProgress.stateNode;
+ var oldProps = workInProgress.memoizedProps;
+ instance.props = oldProps;
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = emptyContextObject;
- var maybeNewVersion = getVersion(source._source);
+ if (typeof contextType === "object" && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextLegacyUnmaskedContext = getUnmaskedContext(
+ workInProgress,
+ ctor,
+ true
+ );
+ nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
+ }
- if (!objectIs(version, maybeNewVersion)) {
- var maybeNewSnapshot = getSnapshot(source._source);
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles =
+ typeof getDerivedStateFromProps === "function" ||
+ typeof instance.getSnapshotBeforeUpdate === "function"; // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
- {
- if (typeof maybeNewSnapshot === "function") {
- error(
- "Mutable source should not return a function as the snapshot value. " +
- "Functions may close over mutable values and cause tearing."
- );
- }
- }
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillReceiveProps === "function" ||
+ typeof instance.componentWillReceiveProps === "function")
+ ) {
+ if (oldProps !== newProps || oldContext !== nextContext) {
+ callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ nextContext
+ );
+ }
+ }
- if (!objectIs(snapshot, maybeNewSnapshot)) {
- setSnapshot(maybeNewSnapshot);
- var lane = requestUpdateLane(fiber);
- markRootMutableRead(root, lane);
- } // If the source mutated between render and now,
- // there may be state updates already scheduled from the old source.
- // Entangle the updates so that they render in the same batch.
+ resetHasForceUpdateBeforeProcessing();
+ var oldState = workInProgress.memoizedState;
+ var newState = (instance.state = oldState);
+ processUpdateQueue(workInProgress, newProps, instance, renderLanes);
+ newState = workInProgress.memoizedState;
- markRootEntangled(root, root.mutableReadLanes);
+ if (
+ oldProps === newProps &&
+ oldState === newState &&
+ !hasContextChanged() &&
+ !checkHasForceUpdateAfterProcessing()
+ ) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === "function") {
+ var fiberFlags = Update | LayoutStatic;
+
+ if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
+ fiberFlags |= MountLayoutDev;
}
- },
- [getSnapshot, source, subscribe]
- ); // If we got a new source or subscribe function, re-subscribe in a passive effect.
- dispatcher.useEffect(
- function () {
- var handleChange = function () {
- var latestGetSnapshot = refs.getSnapshot;
- var latestSetSnapshot = refs.setSnapshot;
+ workInProgress.flags |= fiberFlags;
+ }
- try {
- latestSetSnapshot(latestGetSnapshot(source._source)); // Record a pending mutable source update with the same expiration time.
+ return false;
+ }
- var lane = requestUpdateLane(fiber);
- markRootMutableRead(root, lane);
- } catch (error) {
- // A selector might throw after a source mutation.
- // e.g. it might try to read from a part of the store that no longer exists.
- // In this case we should still schedule an update with React.
- // Worst case the selector will throw again and then an error boundary will handle it.
- latestSetSnapshot(function () {
- throw error;
- });
- }
- };
+ if (typeof getDerivedStateFromProps === "function") {
+ applyDerivedStateFromProps(
+ workInProgress,
+ ctor,
+ getDerivedStateFromProps,
+ newProps
+ );
+ newState = workInProgress.memoizedState;
+ }
- var unsubscribe = subscribe(source._source, handleChange);
+ var shouldUpdate =
+ checkHasForceUpdateAfterProcessing() ||
+ checkShouldComponentUpdate(
+ workInProgress,
+ ctor,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ nextContext
+ );
- {
- if (typeof unsubscribe !== "function") {
- error(
- "Mutable source subscribe function must return an unsubscribe function."
- );
- }
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillMount === "function" ||
+ typeof instance.componentWillMount === "function")
+ ) {
+ if (typeof instance.componentWillMount === "function") {
+ instance.componentWillMount();
}
- return unsubscribe;
- },
- [source, subscribe]
- ); // If any of the inputs to useMutableSource change, reading is potentially unsafe.
- //
- // If either the source or the subscription have changed we can't can't trust the update queue.
- // Maybe the source changed in a way that the old subscription ignored but the new one depends on.
- //
- // If the getSnapshot function changed, we also shouldn't rely on the update queue.
- // It's possible that the underlying source was mutated between the when the last "change" event fired,
- // and when the current render (with the new getSnapshot function) is processed.
- //
- // In both cases, we need to throw away pending updates (since they are no longer relevant)
- // and treat reading from the source as we do in the mount case.
+ if (typeof instance.UNSAFE_componentWillMount === "function") {
+ instance.UNSAFE_componentWillMount();
+ }
+ }
- if (
- !objectIs(prevGetSnapshot, getSnapshot) ||
- !objectIs(prevSource, source) ||
- !objectIs(prevSubscribe, subscribe)
- ) {
- // Create a new queue and setState method,
- // So if there are interleaved updates, they get pushed to the older queue.
- // When this becomes current, the previous queue and dispatch method will be discarded,
- // including any interleaving updates that occur.
- var newQueue = {
- pending: null,
- lanes: NoLanes,
- dispatch: null,
- lastRenderedReducer: basicStateReducer,
- lastRenderedState: snapshot
- };
- newQueue.dispatch = setSnapshot = dispatchSetState.bind(
- null,
- currentlyRenderingFiber$1,
- newQueue
- );
- stateHook.queue = newQueue;
- stateHook.baseQueue = null;
- snapshot = readFromUnsubscribedMutableSource(root, source, getSnapshot);
- stateHook.memoizedState = stateHook.baseState = snapshot;
- }
+ if (typeof instance.componentDidMount === "function") {
+ var _fiberFlags = Update | LayoutStatic;
- return snapshot;
-}
+ if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
+ _fiberFlags |= MountLayoutDev;
+ }
-function mountMutableSource(source, getSnapshot, subscribe) {
- var hook = mountWorkInProgressHook();
- hook.memoizedState = {
- refs: {
- getSnapshot: getSnapshot,
- setSnapshot: null
- },
- source: source,
- subscribe: subscribe
- };
- return useMutableSource(hook, source, getSnapshot, subscribe);
-}
+ workInProgress.flags |= _fiberFlags;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidMount === "function") {
+ var _fiberFlags2 = Update | LayoutStatic;
-function updateMutableSource(source, getSnapshot, subscribe) {
- var hook = updateWorkInProgressHook();
- return useMutableSource(hook, source, getSnapshot, subscribe);
-}
+ if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
+ _fiberFlags2 |= MountLayoutDev;
+ }
-function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
- var fiber = currentlyRenderingFiber$1;
- var hook = mountWorkInProgressHook();
- var nextSnapshot;
- var isHydrating = getIsHydrating();
+ workInProgress.flags |= _fiberFlags2;
+ } // If shouldComponentUpdate returned false, we should still update the
+ // memoized state to indicate that this work can be reused.
- if (isHydrating) {
- if (getServerSnapshot === undefined) {
- throw new Error(
- "Missing getServerSnapshot, which is required for " +
- "server-rendered content. Will revert to client rendering."
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ } // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+ return shouldUpdate;
+} // Invokes the update life-cycles and returns false if it shouldn't rerender.
+
+function updateClassInstance(
+ current,
+ workInProgress,
+ ctor,
+ newProps,
+ renderLanes
+) {
+ var instance = workInProgress.stateNode;
+ cloneUpdateQueue(current, workInProgress);
+ var unresolvedOldProps = workInProgress.memoizedProps;
+ var oldProps =
+ workInProgress.type === workInProgress.elementType
+ ? unresolvedOldProps
+ : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
+ instance.props = oldProps;
+ var unresolvedNewProps = workInProgress.pendingProps;
+ var oldContext = instance.context;
+ var contextType = ctor.contextType;
+ var nextContext = emptyContextObject;
+
+ if (typeof contextType === "object" && contextType !== null) {
+ nextContext = readContext(contextType);
+ } else {
+ var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
+ nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
+ }
+
+ var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
+ var hasNewLifecycles =
+ typeof getDerivedStateFromProps === "function" ||
+ typeof instance.getSnapshotBeforeUpdate === "function"; // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillReceiveProps === "function" ||
+ typeof instance.componentWillReceiveProps === "function")
+ ) {
+ if (
+ unresolvedOldProps !== unresolvedNewProps ||
+ oldContext !== nextContext
+ ) {
+ callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ nextContext
);
}
+ }
- nextSnapshot = getServerSnapshot();
+ resetHasForceUpdateBeforeProcessing();
+ var oldState = workInProgress.memoizedState;
+ var newState = (instance.state = oldState);
+ processUpdateQueue(workInProgress, newProps, instance, renderLanes);
+ newState = workInProgress.memoizedState;
- {
- if (!didWarnUncachedGetSnapshot) {
- if (nextSnapshot !== getServerSnapshot()) {
- error(
- "The result of getServerSnapshot should be cached to avoid an infinite loop"
- );
+ if (
+ unresolvedOldProps === unresolvedNewProps &&
+ oldState === newState &&
+ !hasContextChanged() &&
+ !checkHasForceUpdateAfterProcessing() &&
+ !(
+ enableLazyContextPropagation &&
+ current !== null &&
+ current.dependencies !== null &&
+ checkIfContextChanged(current.dependencies)
+ )
+ ) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === "function") {
+ if (
+ unresolvedOldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.flags |= Update;
+ }
+ }
- didWarnUncachedGetSnapshot = true;
- }
+ if (typeof instance.getSnapshotBeforeUpdate === "function") {
+ if (
+ unresolvedOldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.flags |= Snapshot;
}
}
- } else {
- nextSnapshot = getSnapshot();
- {
- if (!didWarnUncachedGetSnapshot) {
- var cachedSnapshot = getSnapshot();
+ return false;
+ }
- if (!objectIs(nextSnapshot, cachedSnapshot)) {
- error(
- "The result of getSnapshot should be cached to avoid an infinite loop"
- );
+ if (typeof getDerivedStateFromProps === "function") {
+ applyDerivedStateFromProps(
+ workInProgress,
+ ctor,
+ getDerivedStateFromProps,
+ newProps
+ );
+ newState = workInProgress.memoizedState;
+ }
- didWarnUncachedGetSnapshot = true;
- }
+ var shouldUpdate =
+ checkHasForceUpdateAfterProcessing() ||
+ checkShouldComponentUpdate(
+ workInProgress,
+ ctor,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ nextContext
+ ) || // TODO: In some cases, we'll end up checking if context has changed twice,
+ // both before and after `shouldComponentUpdate` has been called. Not ideal,
+ // but I'm loath to refactor this function. This only happens for memoized
+ // components so it's not that common.
+ (enableLazyContextPropagation &&
+ current !== null &&
+ current.dependencies !== null &&
+ checkIfContextChanged(current.dependencies));
+
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (
+ !hasNewLifecycles &&
+ (typeof instance.UNSAFE_componentWillUpdate === "function" ||
+ typeof instance.componentWillUpdate === "function")
+ ) {
+ if (typeof instance.componentWillUpdate === "function") {
+ instance.componentWillUpdate(newProps, newState, nextContext);
}
- } // Unless we're rendering a blocking lane, schedule a consistency check.
- // Right before committing, we will walk the tree and check if any of the
- // stores were mutated.
- //
- // We won't do this if we're hydrating server-rendered content, because if
- // the content is stale, it's already visible anyway. Instead we'll patch
- // it up in a passive effect.
- var root = getWorkInProgressRoot();
+ if (typeof instance.UNSAFE_componentWillUpdate === "function") {
+ instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
+ }
+ }
- if (root === null) {
- throw new Error(
- "Expected a work-in-progress root. This is a bug in React. Please file an issue."
- );
+ if (typeof instance.componentDidUpdate === "function") {
+ workInProgress.flags |= Update;
}
- if (!includesBlockingLane(root, renderLanes$1)) {
- pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
+ if (typeof instance.getSnapshotBeforeUpdate === "function") {
+ workInProgress.flags |= Snapshot;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === "function") {
+ if (
+ unresolvedOldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.flags |= Update;
+ }
}
- } // Read the current snapshot from the store on every render. This breaks the
- // normal rules of React, and only works because store updates are
- // always synchronous.
- hook.memoizedState = nextSnapshot;
- var inst = {
- value: nextSnapshot,
- getSnapshot: getSnapshot
- };
- hook.queue = inst; // Schedule an effect to subscribe to the store.
+ if (typeof instance.getSnapshotBeforeUpdate === "function") {
+ if (
+ unresolvedOldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.flags |= Snapshot;
+ }
+ } // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
- mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Schedule an effect to update the mutable instance fields. We will update
- // this whenever subscribe, getSnapshot, or value changes. Because there's no
- // clean-up function, and we track the deps correctly, we can call pushEffect
- // directly, without storing any additional state. For the same reason, we
- // don't need to set a static flag, either.
- // TODO: We can move this to the passive phase once we add a pre-commit
- // consistency check. See the next comment.
+ workInProgress.memoizedProps = newProps;
+ workInProgress.memoizedState = newState;
+ } // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
- fiber.flags |= Passive$1;
- pushEffect(
- HasEffect | Passive,
- updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
- undefined,
- null
- );
- return nextSnapshot;
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = nextContext;
+ return shouldUpdate;
}
-function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
- var fiber = currentlyRenderingFiber$1;
- var hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the
- // normal rules of React, and only works because store updates are
- // always synchronous.
+function createCapturedValueAtFiber(value, source) {
+ // If the value is an error, call this function immediately after it is thrown
+ // so the stack is accurate.
+ return {
+ value: value,
+ source: source,
+ stack: getStackByFiberInDevAndProd(source),
+ digest: null
+ };
+}
+function createCapturedValue(value, digest, stack) {
+ return {
+ value: value,
+ source: null,
+ stack: stack != null ? stack : null,
+ digest: digest != null ? digest : null
+ };
+}
- var nextSnapshot = getSnapshot();
+var ReactFiberErrorDialogWWW = require("ReactFiberErrorDialog");
- {
- if (!didWarnUncachedGetSnapshot) {
- var cachedSnapshot = getSnapshot();
+if (typeof ReactFiberErrorDialogWWW.showErrorDialog !== "function") {
+ throw new Error(
+ "Expected ReactFiberErrorDialog.showErrorDialog to be a function."
+ );
+}
- if (!objectIs(nextSnapshot, cachedSnapshot)) {
- error(
- "The result of getSnapshot should be cached to avoid an infinite loop"
- );
+function showErrorDialog(boundary, errorInfo) {
+ var capturedError = {
+ componentStack: errorInfo.stack !== null ? errorInfo.stack : "",
+ error: errorInfo.value,
+ errorBoundary:
+ boundary !== null && boundary.tag === ClassComponent
+ ? boundary.stateNode
+ : null
+ };
+ return ReactFiberErrorDialogWWW.showErrorDialog(capturedError);
+}
- didWarnUncachedGetSnapshot = true;
- }
+function logCapturedError(boundary, errorInfo) {
+ try {
+ var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging.
+ // This enables renderers like ReactNative to better manage redbox behavior.
+
+ if (logError === false) {
+ return;
}
- }
- var prevSnapshot = (currentHook || hook).memoizedState;
- var snapshotChanged = !objectIs(prevSnapshot, nextSnapshot);
+ var error = errorInfo.value;
- if (snapshotChanged) {
- hook.memoizedState = nextSnapshot;
- markWorkInProgressReceivedUpdate();
- }
+ if (true) {
+ var source = errorInfo.source;
+ var stack = errorInfo.stack;
+ var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling
+ // `preventDefault()` in window `error` handler.
+ // We record this information as an expando on the error.
- var inst = hook.queue;
- updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [
- subscribe
- ]); // Whenever getSnapshot or subscribe changes, we need to check in the
- // commit phase if there was an interleaved mutation. In concurrent mode
- // this can happen all the time, but even in synchronous mode, an earlier
- // effect may have mutated the store.
+ if (error != null && error._suppressLogging) {
+ if (boundary.tag === ClassComponent) {
+ // The error is recoverable and was silenced.
+ // Ignore it and don't print the stack addendum.
+ // This is handy for testing error boundaries without noise.
+ return;
+ } // The error is fatal. Since the silencing might have
+ // been accidental, we'll surface it anyway.
+ // However, the browser would have silenced the original error
+ // so we'll print it first, and then print the stack addendum.
- if (
- inst.getSnapshot !== getSnapshot ||
- snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
- // checking whether we scheduled a subscription effect above.
- (workInProgressHook !== null &&
- workInProgressHook.memoizedState.tag & HasEffect)
- ) {
- fiber.flags |= Passive$1;
- pushEffect(
- HasEffect | Passive,
- updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot),
- undefined,
- null
- ); // Unless we're rendering a blocking lane, schedule a consistency check.
- // Right before committing, we will walk the tree and check if any of the
- // stores were mutated.
+ console["error"](error); // Don't transform to our wrapper
+ // For a more detailed description of this block, see:
+ // https://github.com/facebook/react/pull/13384
+ }
- var root = getWorkInProgressRoot();
+ var componentName = source ? getComponentNameFromFiber(source) : null;
+ var componentNameMessage = componentName
+ ? "The above error occurred in the <" + componentName + "> component:"
+ : "The above error occurred in one of your React components:";
+ var errorBoundaryMessage;
- if (root === null) {
- throw new Error(
- "Expected a work-in-progress root. This is a bug in React. Please file an issue."
- );
- }
+ if (boundary.tag === HostRoot) {
+ errorBoundaryMessage =
+ "Consider adding an error boundary to your tree to customize error handling behavior.\n" +
+ "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.";
+ } else {
+ var errorBoundaryName =
+ getComponentNameFromFiber(boundary) || "Anonymous";
+ errorBoundaryMessage =
+ "React will try to recreate this component tree from scratch " +
+ ("using the error boundary you provided, " + errorBoundaryName + ".");
+ }
- if (!includesBlockingLane(root, renderLanes$1)) {
- pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
+ var combinedMessage =
+ componentNameMessage +
+ "\n" +
+ componentStack +
+ "\n\n" +
+ ("" + errorBoundaryMessage); // In development, we provide our own message with just the component stack.
+ // We don't include the original error message and JS stack because the browser
+ // has already printed it. Even if the application swallows the error, it is still
+ // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
+
+ console["error"](combinedMessage); // Don't transform to our wrapper
}
+ } catch (e) {
+ // This method must not throw, or React internal state will get messed up.
+ // If console.error is overridden, or logCapturedError() shows a dialog that throws,
+ // we want to report this error outside of the normal stack as a last resort.
+ // https://github.com/facebook/react/issues/13188
+ setTimeout(function () {
+ throw e;
+ });
}
-
- return nextSnapshot;
}
-function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
- fiber.flags |= StoreConsistency;
- var check = {
- getSnapshot: getSnapshot,
- value: renderedSnapshot
- };
- var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
+function createRootErrorUpdate(fiber, errorInfo, lane) {
+ var update = createUpdate(lane); // Unmount the root by rendering null.
- if (componentUpdateQueue === null) {
- componentUpdateQueue = createFunctionComponentUpdateQueue();
- currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
- componentUpdateQueue.stores = [check];
- } else {
- var stores = componentUpdateQueue.stores;
+ update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property
+ // being called "element".
- if (stores === null) {
- componentUpdateQueue.stores = [check];
- } else {
- stores.push(check);
- }
- }
-}
+ update.payload = {
+ element: null
+ };
+ var error = errorInfo.value;
-function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
- // These are updated in the passive phase
- inst.value = nextSnapshot;
- inst.getSnapshot = getSnapshot; // Something may have been mutated in between render and commit. This could
- // have been in an event that fired before the passive effects, or it could
- // have been in a layout effect. In that case, we would have used the old
- // snapsho and getSnapshot values to bail out. We need to check one more time.
+ update.callback = function () {
+ onUncaughtError(error);
+ logCapturedError(fiber, errorInfo);
+ };
- if (checkIfSnapshotChanged(inst)) {
- // Force a re-render.
- forceStoreRerender(fiber);
- }
+ return update;
}
-function subscribeToStore(fiber, inst, subscribe) {
- var handleStoreChange = function () {
- // The store changed. Check if the snapshot changed since the last time we
- // read from the store.
- if (checkIfSnapshotChanged(inst)) {
- // Force a re-render.
- forceStoreRerender(fiber);
- }
- }; // Subscribe to the store and return a clean-up function.
-
- return subscribe(handleStoreChange);
-}
-
-function checkIfSnapshotChanged(inst) {
- var latestGetSnapshot = inst.getSnapshot;
- var prevValue = inst.value;
-
- try {
- var nextValue = latestGetSnapshot();
- return !objectIs(prevValue, nextValue);
- } catch (error) {
- return true;
- }
-}
-
-function forceStoreRerender(fiber) {
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
-
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
- }
-}
-
-function mountState(initialState) {
- var hook = mountWorkInProgressHook();
-
- if (typeof initialState === "function") {
- // $FlowFixMe: Flow doesn't like mixed types
- initialState = initialState();
- }
-
- hook.memoizedState = hook.baseState = initialState;
- var queue = {
- pending: null,
- lanes: NoLanes,
- dispatch: null,
- lastRenderedReducer: basicStateReducer,
- lastRenderedState: initialState
- };
- hook.queue = queue;
- var dispatch = (queue.dispatch = dispatchSetState.bind(
- null,
- currentlyRenderingFiber$1,
- queue
- ));
- return [hook.memoizedState, dispatch];
-}
-
-function updateState(initialState) {
- return updateReducer(basicStateReducer);
-}
-
-function rerenderState(initialState) {
- return rerenderReducer(basicStateReducer);
-}
-
-function pushEffect(tag, create, destroy, deps) {
- var effect = {
- tag: tag,
- create: create,
- destroy: destroy,
- deps: deps,
- // Circular
- next: null
- };
- var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
-
- if (componentUpdateQueue === null) {
- componentUpdateQueue = createFunctionComponentUpdateQueue();
- currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
- componentUpdateQueue.lastEffect = effect.next = effect;
- } else {
- var lastEffect = componentUpdateQueue.lastEffect;
-
- if (lastEffect === null) {
- componentUpdateQueue.lastEffect = effect.next = effect;
- } else {
- var firstEffect = lastEffect.next;
- lastEffect.next = effect;
- effect.next = firstEffect;
- componentUpdateQueue.lastEffect = effect;
- }
- }
+function createClassErrorUpdate(fiber, errorInfo, lane) {
+ var update = createUpdate(lane);
+ update.tag = CaptureUpdate;
+ var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
- return effect;
-}
+ if (typeof getDerivedStateFromError === "function") {
+ var error$1 = errorInfo.value;
-var stackContainsErrorMessage = null;
+ update.payload = function () {
+ return getDerivedStateFromError(error$1);
+ };
-function getCallerStackFrame() {
- // eslint-disable-next-line react-internal/prod-error-codes
- var stackFrames = new Error("Error message").stack.split("\n"); // Some browsers (e.g. Chrome) include the error message in the stack
- // but others (e.g. Firefox) do not.
+ update.callback = function () {
+ {
+ markFailedErrorBoundaryForHotReloading(fiber);
+ }
- if (stackContainsErrorMessage === null) {
- stackContainsErrorMessage = stackFrames[0].includes("Error message");
+ logCapturedError(fiber, errorInfo);
+ };
}
- return stackContainsErrorMessage
- ? stackFrames.slice(3, 4).join("\n")
- : stackFrames.slice(2, 3).join("\n");
-}
-
-function mountRef(initialValue) {
- var hook = mountWorkInProgressHook();
-
- if (enableUseRefAccessWarning) {
- {
- // Support lazy initialization pattern shown in docs.
- // We need to store the caller stack frame so that we don't warn on subsequent renders.
- var hasBeenInitialized = initialValue != null;
- var lazyInitGetterStack = null;
- var didCheckForLazyInit = false; // Only warn once per component+hook.
+ var inst = fiber.stateNode;
- var didWarnAboutRead = false;
- var didWarnAboutWrite = false;
- var current = initialValue;
- var ref = {
- get current() {
- if (!hasBeenInitialized) {
- didCheckForLazyInit = true;
- lazyInitGetterStack = getCallerStackFrame();
- } else if (currentlyRenderingFiber$1 !== null && !didWarnAboutRead) {
- if (
- lazyInitGetterStack === null ||
- lazyInitGetterStack !== getCallerStackFrame()
- ) {
- didWarnAboutRead = true;
+ if (inst !== null && typeof inst.componentDidCatch === "function") {
+ // $FlowFixMe[missing-this-annot]
+ update.callback = function callback() {
+ {
+ markFailedErrorBoundaryForHotReloading(fiber);
+ }
- warn(
- "%s: Unsafe read of a mutable value during render.\n\n" +
- "Reading from a ref during render is only safe if:\n" +
- "1. The ref value has not been updated, or\n" +
- "2. The ref holds a lazily-initialized value that is only set once.\n",
- getComponentNameFromFiber(currentlyRenderingFiber$1) ||
- "Unknown"
- );
- }
- }
+ logCapturedError(fiber, errorInfo);
- return current;
- },
+ if (typeof getDerivedStateFromError !== "function") {
+ // To preserve the preexisting retry behavior of error boundaries,
+ // we keep track of which ones already failed during this batch.
+ // This gets reset before we yield back to the browser.
+ // TODO: Warn in strict mode if getDerivedStateFromError is
+ // not defined.
+ markLegacyErrorBoundaryAsFailed(this);
+ }
- set current(value) {
- if (currentlyRenderingFiber$1 !== null && !didWarnAboutWrite) {
- if (hasBeenInitialized || !didCheckForLazyInit) {
- didWarnAboutWrite = true;
+ var error$1 = errorInfo.value;
+ var stack = errorInfo.stack;
+ this.componentDidCatch(error$1, {
+ componentStack: stack !== null ? stack : ""
+ });
- warn(
- "%s: Unsafe write of a mutable value during render.\n\n" +
- "Writing to a ref during render is only safe if the ref holds " +
- "a lazily-initialized value that is only set once.\n",
- getComponentNameFromFiber(currentlyRenderingFiber$1) ||
- "Unknown"
- );
- }
+ {
+ if (typeof getDerivedStateFromError !== "function") {
+ // If componentDidCatch is the only error boundary method defined,
+ // then it needs to call setState to recover from errors.
+ // If no state update is scheduled then the boundary will swallow the error.
+ if (!includesSomeLane(fiber.lanes, SyncLane)) {
+ error(
+ "%s: Error boundaries should implement getDerivedStateFromError(). " +
+ "In that method, return a state update to display an error message or fallback UI.",
+ getComponentNameFromFiber(fiber) || "Unknown"
+ );
}
-
- hasBeenInitialized = true;
- current = value;
}
- };
- Object.seal(ref);
- hook.memoizedState = ref;
- return ref;
- }
- } else {
- var _ref2 = {
- current: initialValue
+ }
};
- hook.memoizedState = _ref2;
- return _ref2;
}
-}
-
-function updateRef(initialValue) {
- var hook = updateWorkInProgressHook();
- return hook.memoizedState;
-}
-function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
- var hook = mountWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
- currentlyRenderingFiber$1.flags |= fiberFlags;
- hook.memoizedState = pushEffect(
- HasEffect | hookFlags,
- create,
- undefined,
- nextDeps
- );
+ return update;
}
-function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
- var hook = updateWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
- var destroy = undefined; // currentHook is null when rerendering after a render phase state update.
-
- if (currentHook !== null) {
- var prevEffect = currentHook.memoizedState;
- destroy = prevEffect.destroy;
-
- if (nextDeps !== null) {
- var prevDeps = prevEffect.deps;
+function resetSuspendedComponent(sourceFiber, rootRenderLanes) {
+ if (enableLazyContextPropagation) {
+ var currentSourceFiber = sourceFiber.alternate;
- if (areHookInputsEqual(nextDeps, prevDeps)) {
- hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
- return;
- }
+ if (currentSourceFiber !== null) {
+ // Since we never visited the children of the suspended component, we
+ // need to propagate the context change now, to ensure that we visit
+ // them during the retry.
+ //
+ // We don't have to do this for errors because we retry errors without
+ // committing in between. So this is specific to Suspense.
+ propagateParentContextChangesToDeferredTree(
+ currentSourceFiber,
+ sourceFiber,
+ rootRenderLanes
+ );
}
- }
-
- currentlyRenderingFiber$1.flags |= fiberFlags;
- hook.memoizedState = pushEffect(
- HasEffect | hookFlags,
- create,
- destroy,
- nextDeps
- );
-}
-
-function mountEffect(create, deps) {
- if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
- mountEffectImpl(
- MountPassiveDev | Passive$1 | PassiveStatic,
- Passive,
- create,
- deps
- );
- } else {
- mountEffectImpl(Passive$1 | PassiveStatic, Passive, create, deps);
- }
-}
-
-function updateEffect(create, deps) {
- updateEffectImpl(Passive$1, Passive, create, deps);
-}
+ } // Reset the memoizedState to what it was before we attempted to render it.
+ // A legacy mode Suspense quirk, only relevant to hook components.
-function useEffectEventImpl(payload) {
- currentlyRenderingFiber$1.flags |= Update;
- var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
+ var tag = sourceFiber.tag;
- if (componentUpdateQueue === null) {
- componentUpdateQueue = createFunctionComponentUpdateQueue();
- currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
- componentUpdateQueue.events = [payload];
- } else {
- var events = componentUpdateQueue.events;
+ if (
+ (sourceFiber.mode & ConcurrentMode) === NoMode &&
+ (tag === FunctionComponent ||
+ tag === ForwardRef ||
+ tag === SimpleMemoComponent)
+ ) {
+ var currentSource = sourceFiber.alternate;
- if (events === null) {
- componentUpdateQueue.events = [payload];
+ if (currentSource) {
+ sourceFiber.updateQueue = currentSource.updateQueue;
+ sourceFiber.memoizedState = currentSource.memoizedState;
+ sourceFiber.lanes = currentSource.lanes;
} else {
- events.push(payload);
+ sourceFiber.updateQueue = null;
+ sourceFiber.memoizedState = null;
}
}
}
-function mountEvent(callback) {
- var hook = mountWorkInProgressHook();
- var ref = {
- impl: callback
- };
- hook.memoizedState = ref; // $FlowIgnore[incompatible-return]
+function markSuspenseBoundaryShouldCapture(
+ suspenseBoundary,
+ returnFiber,
+ sourceFiber,
+ root,
+ rootRenderLanes
+) {
+ // This marks a Suspense boundary so that when we're unwinding the stack,
+ // it captures the suspended "exception" and does a second (fallback) pass.
+ if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
+ // Legacy Mode Suspense
+ //
+ // If the boundary is in legacy mode, we should *not*
+ // suspend the commit. Pretend as if the suspended component rendered
+ // null and keep rendering. When the Suspense boundary completes,
+ // we'll do a second pass to render the fallback.
+ if (suspenseBoundary === returnFiber) {
+ // Special case where we suspended while reconciling the children of
+ // a Suspense boundary's inner Offscreen wrapper fiber. This happens
+ // when a React.lazy component is a direct child of a
+ // Suspense boundary.
+ //
+ // Suspense boundaries are implemented as multiple fibers, but they
+ // are a single conceptual unit. The legacy mode behavior where we
+ // pretend the suspended fiber committed as `null` won't work,
+ // because in this case the "suspended" fiber is the inner
+ // Offscreen wrapper.
+ //
+ // Because the contents of the boundary haven't started rendering
+ // yet (i.e. nothing in the tree has partially rendered) we can
+ // switch to the regular, concurrent mode behavior: mark the
+ // boundary with ShouldCapture and enter the unwind phase.
+ suspenseBoundary.flags |= ShouldCapture;
+ } else {
+ suspenseBoundary.flags |= DidCapture;
+ sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete.
+ // But we shouldn't call any lifecycle methods or callbacks. Remove
+ // all lifecycle effect tags.
- return function eventFn() {
- if (isInvalidExecutionContextForEventFunction()) {
- throw new Error(
- "A function wrapped in useEffectEvent can't be called during rendering."
- );
- }
+ sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
- return ref.impl.apply(undefined, arguments);
- };
-}
+ if (sourceFiber.tag === ClassComponent) {
+ var currentSourceFiber = sourceFiber.alternate;
-function updateEvent(callback) {
- var hook = updateWorkInProgressHook();
- var ref = hook.memoizedState;
- useEffectEventImpl({
- ref: ref,
- nextImpl: callback
- }); // $FlowIgnore[incompatible-return]
+ if (currentSourceFiber === null) {
+ // This is a new mount. Change the tag so it's not mistaken for a
+ // completed class component. For example, we should not call
+ // componentWillUnmount if it is deleted.
+ sourceFiber.tag = IncompleteClassComponent;
+ } else {
+ // When we try rendering again, we should not reuse the current fiber,
+ // since it's known to be in an inconsistent state. Use a force update to
+ // prevent a bail out.
+ var update = createUpdate(SyncLane);
+ update.tag = ForceUpdate;
+ enqueueUpdate(sourceFiber, update, SyncLane);
+ }
+ } // The source fiber did not complete. Mark it with Sync priority to
+ // indicate that it still has pending work.
- return function eventFn() {
- if (isInvalidExecutionContextForEventFunction()) {
- throw new Error(
- "A function wrapped in useEffectEvent can't be called during rendering."
- );
+ sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
}
- return ref.impl.apply(undefined, arguments);
- };
-}
+ return suspenseBoundary;
+ } // Confirmed that the boundary is in a concurrent mode tree. Continue
+ // with the normal suspend path.
+ //
+ // After this we'll use a set of heuristics to determine whether this
+ // render pass will run to completion or restart or "suspend" the commit.
+ // The actual logic for this is spread out in different places.
+ //
+ // This first principle is that if we're going to suspend when we complete
+ // a root, then we should also restart if we get an update or ping that
+ // might unsuspend it, and vice versa. The only reason to suspend is
+ // because you think you might want to restart before committing. However,
+ // it doesn't make sense to restart only while in the period we're suspended.
+ //
+ // Restarting too aggressively is also not good because it starves out any
+ // intermediate loading state. So we use heuristics to determine when.
+ // Suspense Heuristics
+ //
+ // If nothing threw a Promise or all the same fallbacks are already showing,
+ // then don't suspend/restart.
+ //
+ // If this is an initial render of a new tree of Suspense boundaries and
+ // those trigger a fallback, then don't suspend/restart. We want to ensure
+ // that we can show the initial loading state as quickly as possible.
+ //
+ // If we hit a "Delayed" case, such as when we'd switch from content back into
+ // a fallback, then we should always suspend/restart. Transitions apply
+ // to this case. If none is defined, JND is used instead.
+ //
+ // If we're already showing a fallback and it gets "retried", allowing us to show
+ // another level, but there's still an inner boundary that would show a fallback,
+ // then we suspend/restart for 500ms since the last time we showed a fallback
+ // anywhere in the tree. This effectively throttles progressive loading into a
+ // consistent train of commits. This also gives us an opportunity to restart to
+ // get to the completed state slightly earlier.
+ //
+ // If there's ambiguity due to batching it's resolved in preference of:
+ // 1) "delayed", 2) "initial render", 3) "retry".
+ //
+ // We want to ensure that a "busy" state doesn't get force committed. We want to
+ // ensure that new initial loading states can commit as soon as possible.
-function mountInsertionEffect(create, deps) {
- mountEffectImpl(Update, Insertion, create, deps);
-}
+ suspenseBoundary.flags |= ShouldCapture; // TODO: I think we can remove this, since we now use `DidCapture` in
+ // the begin phase to prevent an early bailout.
-function updateInsertionEffect(create, deps) {
- return updateEffectImpl(Update, Insertion, create, deps);
+ suspenseBoundary.lanes = rootRenderLanes;
+ return suspenseBoundary;
}
-function mountLayoutEffect(create, deps) {
- var fiberFlags = Update | LayoutStatic;
+function throwException(
+ root,
+ returnFiber,
+ sourceFiber,
+ value,
+ rootRenderLanes
+) {
+ // The source fiber did not complete.
+ sourceFiber.flags |= Incomplete;
- if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
- fiberFlags |= MountLayoutDev;
+ {
+ if (isDevToolsPresent) {
+ // If we have pending work still, restore the original updaters
+ restorePendingUpdaters(root, rootRenderLanes);
+ }
}
- return mountEffectImpl(fiberFlags, Layout, create, deps);
-}
-
-function updateLayoutEffect(create, deps) {
- return updateEffectImpl(Update, Layout, create, deps);
-}
-
-function imperativeHandleEffect(create, ref) {
- if (typeof ref === "function") {
- var refCallback = ref;
- var inst = create();
- refCallback(inst);
- return function () {
- refCallback(null);
- };
- } else if (ref !== null && ref !== undefined) {
- var refObject = ref;
+ if (
+ value !== null &&
+ typeof value === "object" &&
+ typeof value.then === "function"
+ ) {
+ // This is a wakeable. The component suspended.
+ var wakeable = value;
+ resetSuspendedComponent(sourceFiber, rootRenderLanes);
{
- if (!refObject.hasOwnProperty("current")) {
- error(
- "Expected useImperativeHandle() first argument to either be a " +
- "ref callback or React.createRef() object. Instead received: %s.",
- "an object with keys {" + Object.keys(refObject).join(", ") + "}"
- );
+ if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
+ markDidThrowWhileHydratingDEV();
}
}
- var _inst = create();
+ {
+ if (enableDebugTracing) {
+ if (sourceFiber.mode & DebugTracingMode) {
+ var name = getComponentNameFromFiber(sourceFiber) || "Unknown";
+ logComponentSuspended(name, wakeable);
+ }
+ }
+ } // Mark the nearest Suspense boundary to switch to rendering a fallback.
- refObject.current = _inst;
- return function () {
- refObject.current = null;
- };
- }
-}
-
-function mountImperativeHandle(ref, create, deps) {
- {
- if (typeof create !== "function") {
- error(
- "Expected useImperativeHandle() second argument to be a function " +
- "that creates a handle. Instead received: %s.",
- create !== null ? typeof create : "null"
- );
- }
- } // TODO: If deps are provided, should we skip comparing the ref itself?
-
- var effectDeps =
- deps !== null && deps !== undefined ? deps.concat([ref]) : null;
- var fiberFlags = Update | LayoutStatic;
+ var suspenseBoundary = getSuspenseHandler();
- if ((currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
- fiberFlags |= MountLayoutDev;
- }
+ if (suspenseBoundary !== null) {
+ switch (suspenseBoundary.tag) {
+ case SuspenseComponent: {
+ // If this suspense boundary is not already showing a fallback, mark
+ // the in-progress render as suspended. We try to perform this logic
+ // as soon as soon as possible during the render phase, so the work
+ // loop can know things like whether it's OK to switch to other tasks,
+ // or whether it can wait for data to resolve before continuing.
+ // TODO: Most of these checks are already performed when entering a
+ // Suspense boundary. We should track the information on the stack so
+ // we don't have to recompute it on demand. This would also allow us
+ // to unify with `use` which needs to perform this logic even sooner,
+ // before `throwException` is called.
+ if (sourceFiber.mode & ConcurrentMode) {
+ if (getShellBoundary() === null) {
+ // Suspended in the "shell" of the app. This is an undesirable
+ // loading state. We should avoid committing this tree.
+ renderDidSuspendDelayIfPossible();
+ } else {
+ // If we suspended deeper than the shell, we don't need to delay
+ // the commmit. However, we still call renderDidSuspend if this is
+ // a new boundary, to tell the work loop that a new fallback has
+ // appeared during this render.
+ // TODO: Theoretically we should be able to delete this branch.
+ // It's currently used for two things: 1) to throttle the
+ // appearance of successive loading states, and 2) in
+ // SuspenseList, to determine whether the children include any
+ // pending fallbacks. For 1, we should apply throttling to all
+ // retries, not just ones that render an additional fallback. For
+ // 2, we should check subtreeFlags instead. Then we can delete
+ // this branch.
+ var current = suspenseBoundary.alternate;
- mountEffectImpl(
- fiberFlags,
- Layout,
- imperativeHandleEffect.bind(null, create, ref),
- effectDeps
- );
-}
+ if (current === null) {
+ renderDidSuspend();
+ }
+ }
+ }
-function updateImperativeHandle(ref, create, deps) {
- {
- if (typeof create !== "function") {
- error(
- "Expected useImperativeHandle() second argument to be a function " +
- "that creates a handle. Instead received: %s.",
- create !== null ? typeof create : "null"
- );
- }
- } // TODO: If deps are provided, should we skip comparing the ref itself?
+ suspenseBoundary.flags &= ~ForceClientRender;
+ markSuspenseBoundaryShouldCapture(
+ suspenseBoundary,
+ returnFiber,
+ sourceFiber,
+ root,
+ rootRenderLanes
+ ); // Retry listener
+ //
+ // If the fallback does commit, we need to attach a different type of
+ // listener. This one schedules an update on the Suspense boundary to
+ // turn the fallback state off.
+ //
+ // Stash the wakeable on the boundary fiber so we can access it in the
+ // commit phase.
+ //
+ // When the wakeable resolves, we'll attempt to render the boundary
+ // again ("retry").
- var effectDeps =
- deps !== null && deps !== undefined ? deps.concat([ref]) : null;
- updateEffectImpl(
- Update,
- Layout,
- imperativeHandleEffect.bind(null, create, ref),
- effectDeps
- );
-}
+ var wakeables = suspenseBoundary.updateQueue;
-function mountDebugValue(value, formatterFn) {
- // This hook is normally a no-op.
- // The react-debug-hooks package injects its own implementation
- // so that e.g. DevTools can display custom hook values.
-}
+ if (wakeables === null) {
+ suspenseBoundary.updateQueue = new Set([wakeable]);
+ } else {
+ wakeables.add(wakeable);
+ }
-var updateDebugValue = mountDebugValue;
+ break;
+ }
-function mountCallback(callback, deps) {
- var hook = mountWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
- hook.memoizedState = [callback, nextDeps];
- return callback;
-}
+ case OffscreenComponent: {
+ if (suspenseBoundary.mode & ConcurrentMode) {
+ suspenseBoundary.flags |= ShouldCapture;
+ var offscreenQueue = suspenseBoundary.updateQueue;
-function updateCallback(callback, deps) {
- var hook = updateWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
- var prevState = hook.memoizedState;
+ if (offscreenQueue === null) {
+ var newOffscreenQueue = {
+ transitions: null,
+ markerInstances: null,
+ wakeables: new Set([wakeable])
+ };
+ suspenseBoundary.updateQueue = newOffscreenQueue;
+ } else {
+ var _wakeables = offscreenQueue.wakeables;
- if (nextDeps !== null) {
- var prevDeps = prevState[1];
+ if (_wakeables === null) {
+ offscreenQueue.wakeables = new Set([wakeable]);
+ } else {
+ _wakeables.add(wakeable);
+ }
+ }
- if (areHookInputsEqual(nextDeps, prevDeps)) {
- return prevState[0];
- }
- }
+ break;
+ }
+ }
+ // eslint-disable-next-line no-fallthrough
- hook.memoizedState = [callback, nextDeps];
- return callback;
-}
+ default: {
+ throw new Error(
+ "Unexpected Suspense handler tag (" +
+ suspenseBoundary.tag +
+ "). This " +
+ "is a bug in React."
+ );
+ }
+ } // We only attach ping listeners in concurrent mode. Legacy Suspense always
+ // commits fallbacks synchronously, so there are no pings.
-function mountMemo(nextCreate, deps) {
- var hook = mountWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
+ if (suspenseBoundary.mode & ConcurrentMode) {
+ attachPingListener(root, wakeable, rootRenderLanes);
+ }
- if (shouldDoubleInvokeUserFnsInHooksDEV) {
- nextCreate();
- }
+ return;
+ } else {
+ // No boundary was found. Unless this is a sync update, this is OK.
+ // We can suspend and wait for more data to arrive.
+ if (root.tag === ConcurrentRoot) {
+ // In a concurrent root, suspending without a Suspense boundary is
+ // allowed. It will suspend indefinitely without committing.
+ //
+ // TODO: Should we have different behavior for discrete updates? What
+ // about flushSync? Maybe it should put the tree into an inert state,
+ // and potentially log a warning. Revisit this for a future release.
+ attachPingListener(root, wakeable, rootRenderLanes);
+ renderDidSuspendDelayIfPossible();
+ return;
+ } else {
+ // In a legacy root, suspending without a boundary is always an error.
+ var uncaughtSuspenseError = new Error(
+ "A component suspended while responding to synchronous input. This " +
+ "will cause the UI to be replaced with a loading indicator. To " +
+ "fix, updates that suspend should be wrapped " +
+ "with startTransition."
+ );
+ value = uncaughtSuspenseError;
+ }
+ }
+ } else {
+ // This is a regular error, not a Suspense wakeable.
+ if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
+ markDidThrowWhileHydratingDEV();
- var nextValue = nextCreate();
- hook.memoizedState = [nextValue, nextDeps];
- return nextValue;
-}
+ var _suspenseBoundary = getSuspenseHandler(); // If the error was thrown during hydration, we may be able to recover by
+ // discarding the dehydrated content and switching to a client render.
+ // Instead of surfacing the error, find the nearest Suspense boundary
+ // and render it again without hydration.
-function updateMemo(nextCreate, deps) {
- var hook = updateWorkInProgressHook();
- var nextDeps = deps === undefined ? null : deps;
- var prevState = hook.memoizedState; // Assume these are defined. If they're not, areHookInputsEqual will warn.
+ if (_suspenseBoundary !== null) {
+ if ((_suspenseBoundary.flags & ShouldCapture) === NoFlags$1) {
+ // Set a flag to indicate that we should try rendering the normal
+ // children again, not the fallback.
+ _suspenseBoundary.flags |= ForceClientRender;
+ }
- if (nextDeps !== null) {
- var prevDeps = prevState[1];
+ markSuspenseBoundaryShouldCapture(
+ _suspenseBoundary,
+ returnFiber,
+ sourceFiber,
+ root,
+ rootRenderLanes
+ ); // Even though the user may not be affected by this error, we should
+ // still log it so it can be fixed.
- if (areHookInputsEqual(nextDeps, prevDeps)) {
- return prevState[0];
+ queueHydrationError(createCapturedValueAtFiber(value, sourceFiber));
+ return;
+ }
}
}
- if (shouldDoubleInvokeUserFnsInHooksDEV) {
- nextCreate();
- }
+ value = createCapturedValueAtFiber(value, sourceFiber);
+ renderDidError(value); // We didn't find a boundary that could handle this type of exception. Start
+ // over and traverse parent path again, this time treating the exception
+ // as an error.
- var nextValue = nextCreate();
- hook.memoizedState = [nextValue, nextDeps];
- return nextValue;
-}
+ var workInProgress = returnFiber;
-function mountDeferredValue(value) {
- var hook = mountWorkInProgressHook();
- hook.memoizedState = value;
- return value;
-}
+ do {
+ switch (workInProgress.tag) {
+ case HostRoot: {
+ var _errorInfo = value;
+ workInProgress.flags |= ShouldCapture;
+ var lane = pickArbitraryLane(rootRenderLanes);
+ workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
+ var update = createRootErrorUpdate(workInProgress, _errorInfo, lane);
+ enqueueCapturedUpdate(workInProgress, update);
+ return;
+ }
-function updateDeferredValue(value) {
- var hook = updateWorkInProgressHook();
- var resolvedCurrentHook = currentHook;
- var prevValue = resolvedCurrentHook.memoizedState;
- return updateDeferredValueImpl(hook, prevValue, value);
-}
+ case ClassComponent:
+ // Capture and retry
+ var errorInfo = value;
+ var ctor = workInProgress.type;
+ var instance = workInProgress.stateNode;
-function rerenderDeferredValue(value) {
- var hook = updateWorkInProgressHook();
+ if (
+ (workInProgress.flags & DidCapture) === NoFlags$1 &&
+ (typeof ctor.getDerivedStateFromError === "function" ||
+ (instance !== null &&
+ typeof instance.componentDidCatch === "function" &&
+ !isAlreadyFailedLegacyErrorBoundary(instance)))
+ ) {
+ workInProgress.flags |= ShouldCapture;
- if (currentHook === null) {
- // This is a rerender during a mount.
- hook.memoizedState = value;
- return value;
- } else {
- // This is a rerender during an update.
- var prevValue = currentHook.memoizedState;
- return updateDeferredValueImpl(hook, prevValue, value);
- }
-}
+ var _lane = pickArbitraryLane(rootRenderLanes);
-function updateDeferredValueImpl(hook, prevValue, value) {
- var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes$1);
+ workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state
- if (shouldDeferValue) {
- // This is an urgent update. If the value has changed, keep using the
- // previous value and spawn a deferred render to update it later.
- if (!objectIs(value, prevValue)) {
- // Schedule a deferred render
- var deferredLane = claimNextTransitionLane();
- currentlyRenderingFiber$1.lanes = mergeLanes(
- currentlyRenderingFiber$1.lanes,
- deferredLane
- );
- markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent
- // from the latest value. The name "baseState" doesn't really match how we
- // use it because we're reusing a state hook field instead of creating a
- // new one.
+ var _update = createClassErrorUpdate(
+ workInProgress,
+ errorInfo,
+ _lane
+ );
- hook.baseState = true;
- } // Reuse the previous value
+ enqueueCapturedUpdate(workInProgress, _update);
+ return;
+ }
- return prevValue;
- } else {
- // This is not an urgent update, so we can use the latest value regardless
- // of what it is. No need to defer it.
- // However, if we're currently inside a spawned render, then we need to mark
- // this as an update to prevent the fiber from bailing out.
- //
- // `baseState` is true when the current value is different from the rendered
- // value. The name doesn't really match how we use it because we're reusing
- // a state hook field instead of creating a new one.
- if (hook.baseState) {
- // Flip this back to false.
- hook.baseState = false;
- markWorkInProgressReceivedUpdate();
- }
+ break;
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
- hook.memoizedState = value;
- return value;
- }
+ workInProgress = workInProgress.return;
+ } while (workInProgress !== null);
}
-function startTransition(setPending, callback, options) {
- var previousPriority = getCurrentUpdatePriority$1();
- setCurrentUpdatePriority(
- higherEventPriority(previousPriority, ContinuousEventPriority)
- );
- setPending(true);
- var prevTransition = ReactCurrentBatchConfig$2.transition;
- ReactCurrentBatchConfig$2.transition = {};
- var currentTransition = ReactCurrentBatchConfig$2.transition;
-
+var TransitionRoot = 0;
+var TransitionTracingMarker = 1;
+function processTransitionCallbacks(pendingTransitions, endTime, callbacks) {
if (enableTransitionTracing) {
- if (options !== undefined && options.name !== undefined) {
- ReactCurrentBatchConfig$2.transition.name = options.name;
- ReactCurrentBatchConfig$2.transition.startTime = now$1();
- }
- }
+ if (pendingTransitions !== null) {
+ var transitionStart = pendingTransitions.transitionStart;
+ var onTransitionStart = callbacks.onTransitionStart;
- {
- ReactCurrentBatchConfig$2.transition._updatedFibers = new Set();
- }
+ if (transitionStart !== null && onTransitionStart != null) {
+ transitionStart.forEach(function (transition) {
+ return onTransitionStart(transition.name, transition.startTime);
+ });
+ }
- try {
- setPending(false);
- callback();
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig$2.transition = prevTransition;
+ var markerProgress = pendingTransitions.markerProgress;
+ var onMarkerProgress = callbacks.onMarkerProgress;
- {
- if (prevTransition === null && currentTransition._updatedFibers) {
- var updatedFibersCount = currentTransition._updatedFibers.size;
+ if (onMarkerProgress != null && markerProgress !== null) {
+ markerProgress.forEach(function (markerInstance, markerName) {
+ if (markerInstance.transitions !== null) {
+ // TODO: Clone the suspense object so users can't modify it
+ var pending =
+ markerInstance.pendingBoundaries !== null
+ ? Array.from(markerInstance.pendingBoundaries.values())
+ : [];
+ markerInstance.transitions.forEach(function (transition) {
+ onMarkerProgress(
+ transition.name,
+ markerName,
+ transition.startTime,
+ endTime,
+ pending
+ );
+ });
+ }
+ });
+ }
- currentTransition._updatedFibers.clear();
+ var markerComplete = pendingTransitions.markerComplete;
+ var onMarkerComplete = callbacks.onMarkerComplete;
- if (updatedFibersCount > 10) {
- warn(
- "Detected a large number of updates inside startTransition. " +
- "If this is due to a subscription please re-write it to use React provided hooks. " +
- "Otherwise concurrent mode guarantees are off the table."
- );
- }
+ if (markerComplete !== null && onMarkerComplete != null) {
+ markerComplete.forEach(function (transitions, markerName) {
+ transitions.forEach(function (transition) {
+ onMarkerComplete(
+ transition.name,
+ markerName,
+ transition.startTime,
+ endTime
+ );
+ });
+ });
}
- }
- }
-}
-
-function mountTransition() {
- var _mountState = mountState(false),
- isPending = _mountState[0],
- setPending = _mountState[1]; // The `start` method never changes.
- var start = startTransition.bind(null, setPending);
- var hook = mountWorkInProgressHook();
- hook.memoizedState = start;
- return [isPending, start];
-}
+ var markerIncomplete = pendingTransitions.markerIncomplete;
+ var onMarkerIncomplete = callbacks.onMarkerIncomplete;
-function updateTransition() {
- var _updateState = updateState(),
- isPending = _updateState[0];
+ if (onMarkerIncomplete != null && markerIncomplete !== null) {
+ markerIncomplete.forEach(function (_ref, markerName) {
+ var transitions = _ref.transitions,
+ aborts = _ref.aborts;
+ transitions.forEach(function (transition) {
+ var filteredAborts = [];
+ aborts.forEach(function (abort) {
+ switch (abort.reason) {
+ case "marker": {
+ filteredAborts.push({
+ type: "marker",
+ name: abort.name,
+ endTime: endTime
+ });
+ break;
+ }
- var hook = updateWorkInProgressHook();
- var start = hook.memoizedState;
- return [isPending, start];
-}
+ case "suspense": {
+ filteredAborts.push({
+ type: "suspense",
+ name: abort.name,
+ endTime: endTime
+ });
+ break;
+ }
+ }
+ });
-function rerenderTransition() {
- var _rerenderState = rerenderState(),
- isPending = _rerenderState[0];
+ if (filteredAborts.length > 0) {
+ onMarkerIncomplete(
+ transition.name,
+ markerName,
+ transition.startTime,
+ filteredAborts
+ );
+ }
+ });
+ });
+ }
- var hook = updateWorkInProgressHook();
- var start = hook.memoizedState;
- return [isPending, start];
-}
+ var transitionProgress = pendingTransitions.transitionProgress;
+ var onTransitionProgress = callbacks.onTransitionProgress;
-function mountId() {
- var hook = mountWorkInProgressHook();
- var root = getWorkInProgressRoot(); // TODO: In Fizz, id generation is specific to each server config. Maybe we
- // should do this in Fiber, too? Deferring this decision for now because
- // there's no other place to store the prefix except for an internal field on
- // the public createRoot object, which the fiber tree does not currently have
- // a reference to.
+ if (onTransitionProgress != null && transitionProgress !== null) {
+ transitionProgress.forEach(function (pending, transition) {
+ onTransitionProgress(
+ transition.name,
+ transition.startTime,
+ endTime,
+ Array.from(pending.values())
+ );
+ });
+ }
- var identifierPrefix = root.identifierPrefix;
- var id;
+ var transitionComplete = pendingTransitions.transitionComplete;
+ var onTransitionComplete = callbacks.onTransitionComplete;
- if (getIsHydrating()) {
- var treeId = getTreeId(); // Use a captial R prefix for server-generated ids.
-
- id = ":" + identifierPrefix + "R" + treeId; // Unless this is the first id at this level, append a number at the end
- // that represents the position of this useId hook among all the useId
- // hooks for this fiber.
+ if (transitionComplete !== null && onTransitionComplete != null) {
+ transitionComplete.forEach(function (transition) {
+ return onTransitionComplete(
+ transition.name,
+ transition.startTime,
+ endTime
+ );
+ });
+ }
+ }
+ }
+} // For every tracing marker, store a pointer to it. We will later access it
+// to get the set of suspense boundaries that need to resolve before the
+// tracing marker can be logged as complete
+// This code lives separate from the ReactFiberTransition code because
+// we push and pop on the tracing marker, not the suspense boundary
- var localId = localIdCounter++;
+var markerInstanceStack = createCursor(null);
+function pushRootMarkerInstance(workInProgress) {
+ if (enableTransitionTracing) {
+ // On the root, every transition gets mapped to it's own map of
+ // suspense boundaries. The transition is marked as complete when
+ // the suspense boundaries map is empty. We do this because every
+ // transition completes at different times and depends on different
+ // suspense boundaries to complete. We store all the transitions
+ // along with its map of suspense boundaries in the root incomplete
+ // transitions map. Each entry in this map functions like a tracing
+ // marker does, so we can push it onto the marker instance stack
+ var transitions = getWorkInProgressTransitions();
+ var root = workInProgress.stateNode;
- if (localId > 0) {
- id += "H" + localId.toString(32);
+ if (transitions !== null) {
+ transitions.forEach(function (transition) {
+ if (!root.incompleteTransitions.has(transition)) {
+ var markerInstance = {
+ tag: TransitionRoot,
+ transitions: new Set([transition]),
+ pendingBoundaries: null,
+ aborts: null,
+ name: null
+ };
+ root.incompleteTransitions.set(transition, markerInstance);
+ }
+ });
}
- id += ":";
- } else {
- // Use a lowercase r prefix for client-generated ids.
- var globalClientId = globalClientIdCounter++;
- id = ":" + identifierPrefix + "r" + globalClientId.toString(32) + ":";
- }
+ var markerInstances = []; // For ever transition on the suspense boundary, we push the transition
+ // along with its map of pending suspense boundaries onto the marker
+ // instance stack.
- hook.memoizedState = id;
- return id;
+ root.incompleteTransitions.forEach(function (markerInstance) {
+ markerInstances.push(markerInstance);
+ });
+ push(markerInstanceStack, markerInstances, workInProgress);
+ }
}
-
-function updateId() {
- var hook = updateWorkInProgressHook();
- var id = hook.memoizedState;
- return id;
+function popRootMarkerInstance(workInProgress) {
+ if (enableTransitionTracing) {
+ pop(markerInstanceStack, workInProgress);
+ }
}
-
-function mountRefresh() {
- var hook = mountWorkInProgressHook();
- var refresh = (hook.memoizedState = refreshCache.bind(
- null,
- currentlyRenderingFiber$1
- ));
- return refresh;
+function pushMarkerInstance(workInProgress, markerInstance) {
+ if (enableTransitionTracing) {
+ if (markerInstanceStack.current === null) {
+ push(markerInstanceStack, [markerInstance], workInProgress);
+ } else {
+ push(
+ markerInstanceStack,
+ markerInstanceStack.current.concat(markerInstance),
+ workInProgress
+ );
+ }
+ }
}
-
-function updateRefresh() {
- var hook = updateWorkInProgressHook();
- return hook.memoizedState;
+function popMarkerInstance(workInProgress) {
+ if (enableTransitionTracing) {
+ pop(markerInstanceStack, workInProgress);
+ }
}
+function getMarkerInstances() {
+ if (enableTransitionTracing) {
+ return markerInstanceStack.current;
+ }
-function refreshCache(fiber, seedKey, seedValue) {
- // TODO: Consider warning if the refresh is at discrete priority, or if we
- // otherwise suspect that it wasn't batched properly.
-
- var provider = fiber.return;
+ return null;
+}
- while (provider !== null) {
- switch (provider.tag) {
- case CacheComponent:
- case HostRoot: {
- // Schedule an update on the cache boundary to trigger a refresh.
- var lane = requestUpdateLane(provider);
- var refreshUpdate = createUpdate(lane);
- var root = enqueueUpdate(provider, refreshUpdate, lane);
+var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows
+// into a dehydrated boundary.
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, provider, lane, eventTime);
- entangleTransitions(root, provider, lane);
- } // TODO: If a refresh never commits, the new cache created here must be
- // released. A simple case is start refreshing a cache boundary, but then
- // unmount that boundary before the refresh completes.
+var SelectiveHydrationException = new Error(
+ "This is not a real error. It's an implementation detail of React's " +
+ "selective hydration feature. If this leaks into userspace, it's a bug in " +
+ "React. Please file an issue."
+);
+var didReceiveUpdate = false;
+var didWarnAboutBadClass;
+var didWarnAboutModulePatternComponent;
+var didWarnAboutContextTypeOnFunctionComponent;
+var didWarnAboutGetDerivedStateOnFunctionComponent;
+var didWarnAboutFunctionRefs;
+var didWarnAboutReassigningProps;
+var didWarnAboutRevealOrder;
+var didWarnAboutTailOptions;
+var didWarnAboutDefaultPropsOnFunctionComponent;
- var seededCache = createCache();
+{
+ didWarnAboutBadClass = {};
+ didWarnAboutModulePatternComponent = {};
+ didWarnAboutContextTypeOnFunctionComponent = {};
+ didWarnAboutGetDerivedStateOnFunctionComponent = {};
+ didWarnAboutFunctionRefs = {};
+ didWarnAboutReassigningProps = false;
+ didWarnAboutRevealOrder = {};
+ didWarnAboutTailOptions = {};
+ didWarnAboutDefaultPropsOnFunctionComponent = {};
+}
- if (seedKey !== null && seedKey !== undefined && root !== null) {
- {
- // Seed the cache with the value passed by the caller. This could be
- // from a server mutation, or it could be a streaming response.
- seededCache.data.set(seedKey, seedValue);
- }
- }
+function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
+ if (current === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderLanes
+ );
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ current.child,
+ nextChildren,
+ renderLanes
+ );
+ }
+}
- var payload = {
- cache: seededCache
- };
- refreshUpdate.payload = payload;
- return;
- }
- }
+function forceUnmountCurrentAndReconcile(
+ current,
+ workInProgress,
+ nextChildren,
+ renderLanes
+) {
+ // This function is fork of reconcileChildren. It's used in cases where we
+ // want to reconcile without matching against the existing set. This has the
+ // effect of all current children being unmounted; even if the type and key
+ // are the same, the old child is unmounted and a new child is created.
+ //
+ // To do this, we're going to go through the reconcile algorithm twice. In
+ // the first pass, we schedule a deletion for all the current children by
+ // passing null.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ current.child,
+ null,
+ renderLanes
+ ); // In the second pass, we mount the new children. The trick here is that we
+ // pass null in place of where we usually pass the current child set. This has
+ // the effect of remounting all children regardless of whether their
+ // identities match.
- provider = provider.return;
- } // TODO: Warn if unmounted?
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderLanes
+ );
}
-function dispatchReducerAction(fiber, queue, action) {
+function updateForwardRef(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens after the first render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
{
- if (typeof arguments[3] === "function") {
- error(
- "State updates from the useState() and useReducer() Hooks don't support the " +
- "second callback argument. To execute a side effect after " +
- "rendering, declare it in the component body with useEffect()."
- );
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
+
+ if (innerPropTypes) {
+ checkPropTypes(
+ innerPropTypes,
+ nextProps, // Resolved props
+ "prop",
+ getComponentNameFromType(Component)
+ );
+ }
}
}
- var lane = requestUpdateLane(fiber);
- var update = {
- lane: lane,
- action: action,
- hasEagerState: false,
- eagerState: null,
- next: null
- };
+ var render = Component.render;
+ var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent
- if (isRenderPhaseUpdate(fiber)) {
- enqueueRenderPhaseUpdate(queue, update);
- } else {
- var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
+ var nextChildren;
+ var hasId;
+ prepareToReadContext(workInProgress, renderLanes);
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- entangleTransitionUpdate(root, queue, lane);
- }
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
}
- markUpdateInDevTools(fiber, lane, action);
-}
-
-function dispatchSetState(fiber, queue, action) {
{
- if (typeof arguments[3] === "function") {
- error(
- "State updates from the useState() and useReducer() Hooks don't support the " +
- "second callback argument. To execute a side effect after " +
- "rendering, declare it in the component body with useEffect()."
- );
- }
+ ReactCurrentOwner$2.current = workInProgress;
+ setIsRendering(true);
+ nextChildren = renderWithHooks(
+ current,
+ workInProgress,
+ render,
+ nextProps,
+ ref,
+ renderLanes
+ );
+ hasId = checkDidRenderIdHook();
+ setIsRendering(false);
}
- var lane = requestUpdateLane(fiber);
- var update = {
- lane: lane,
- action: action,
- hasEagerState: false,
- eagerState: null,
- next: null
- };
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
+ }
- if (isRenderPhaseUpdate(fiber)) {
- enqueueRenderPhaseUpdate(queue, update);
- } else {
- var alternate = fiber.alternate;
+ if (current !== null && !didReceiveUpdate) {
+ bailoutHooks(current, workInProgress, renderLanes);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
+
+ if (getIsHydrating() && hasId) {
+ pushMaterializedTreeId(workInProgress);
+ } // React DevTools reads this flag.
+
+ workInProgress.flags |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+
+function updateMemoComponent(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ if (current === null) {
+ var type = Component.type;
if (
- fiber.lanes === NoLanes &&
- (alternate === null || alternate.lanes === NoLanes)
+ isSimpleFunctionComponent(type) &&
+ Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either.
+ Component.defaultProps === undefined
) {
- // The queue is currently empty, which means we can eagerly compute the
- // next state before entering the render phase. If the new state is the
- // same as the current state, we may be able to bail out entirely.
- var lastRenderedReducer = queue.lastRenderedReducer;
+ var resolvedType = type;
- if (lastRenderedReducer !== null) {
- var prevDispatcher;
+ {
+ resolvedType = resolveFunctionForHotReloading(type);
+ } // If this is a plain function component without default props,
+ // and with only the default shallow comparison, we upgrade it
+ // to a SimpleMemoComponent to allow fast path updates.
- {
- prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
- }
+ workInProgress.tag = SimpleMemoComponent;
+ workInProgress.type = resolvedType;
- try {
- var currentState = queue.lastRenderedState;
- var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute
- // it, on the update object. If the reducer hasn't changed by the
- // time we enter the render phase, then the eager state can be used
- // without calling the reducer again.
+ {
+ validateFunctionComponentInDev(workInProgress, type);
+ }
- update.hasEagerState = true;
- update.eagerState = eagerState;
+ return updateSimpleMemoComponent(
+ current,
+ workInProgress,
+ resolvedType,
+ nextProps,
+ renderLanes
+ );
+ }
- if (objectIs(eagerState, currentState)) {
- // Fast path. We can bail out without scheduling React to re-render.
- // It's still possible that we'll need to rebase this update later,
- // if the component re-renders for a different reason and by that
- // time the reducer has changed.
- // TODO: Do we still need to entangle transitions in this case?
- enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update);
- return;
- }
- } catch (error) {
- // Suppress the error. It will throw again in the render phase.
- } finally {
- {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
+ {
+ var innerPropTypes = type.propTypes;
+
+ if (innerPropTypes) {
+ // Inner memo component props aren't currently validated in createElement.
+ // We could move it there, but we'd still need this for lazy code path.
+ checkPropTypes(
+ innerPropTypes,
+ nextProps, // Resolved props
+ "prop",
+ getComponentNameFromType(type)
+ );
+ }
+
+ if (Component.defaultProps !== undefined) {
+ var componentName = getComponentNameFromType(type) || "Unknown";
+
+ if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
+ error(
+ "%s: Support for defaultProps will be removed from memo components " +
+ "in a future major release. Use JavaScript default parameters instead.",
+ componentName
+ );
+
+ didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
}
}
}
- var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
+ var child = createFiberFromTypeAndProps(
+ Component.type,
+ null,
+ nextProps,
+ workInProgress,
+ workInProgress.mode,
+ renderLanes
+ );
+ child.ref = workInProgress.ref;
+ child.return = workInProgress;
+ workInProgress.child = child;
+ return child;
+ }
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- entangleTransitionUpdate(root, queue, lane);
+ {
+ var _type = Component.type;
+ var _innerPropTypes = _type.propTypes;
+
+ if (_innerPropTypes) {
+ // Inner memo component props aren't currently validated in createElement.
+ // We could move it there, but we'd still need this for lazy code path.
+ checkPropTypes(
+ _innerPropTypes,
+ nextProps, // Resolved props
+ "prop",
+ getComponentNameFromType(_type)
+ );
}
}
- markUpdateInDevTools(fiber, lane, action);
-}
+ var currentChild = current.child; // This is always exactly one child
-function isRenderPhaseUpdate(fiber) {
- var alternate = fiber.alternate;
- return (
- fiber === currentlyRenderingFiber$1 ||
- (alternate !== null && alternate === currentlyRenderingFiber$1)
+ var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
+ current,
+ renderLanes
);
-}
-function enqueueRenderPhaseUpdate(queue, update) {
- // This is a render phase update. Stash it in a lazily-created map of
- // queue -> linked list of updates. After this render pass, we'll restart
- // and apply the stashed updates on top of the work-in-progress hook.
- didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate =
- true;
- var pending = queue.pending;
+ if (!hasScheduledUpdateOrContext) {
+ // This will be the props with resolved defaultProps,
+ // unlike current.memoizedProps which will be the unresolved ones.
+ var prevProps = currentChild.memoizedProps; // Default to shallow comparison
- if (pending === null) {
- // This is the first update. Create a circular list.
- update.next = update;
- } else {
- update.next = pending.next;
- pending.next = update;
- }
+ var compare = Component.compare;
+ compare = compare !== null ? compare : shallowEqual;
- queue.pending = update;
-} // TODO: Move to ReactFiberConcurrentUpdates?
+ if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
+ } // React DevTools reads this flag.
-function entangleTransitionUpdate(root, queue, lane) {
- if (isTransitionLane(lane)) {
- var queueLanes = queue.lanes; // If any entangled lanes are no longer pending on the root, then they
- // must have finished. We can remove them from the shared queue, which
- // represents a superset of the actually pending lanes. In some cases we
- // may entangle more than we need to, but that's OK. In fact it's worse if
- // we *don't* entangle when we should.
+ workInProgress.flags |= PerformedWork;
+ var newChild = createWorkInProgress(currentChild, nextProps);
+ newChild.ref = workInProgress.ref;
+ newChild.return = workInProgress;
+ workInProgress.child = newChild;
+ return newChild;
+}
- queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
+function updateSimpleMemoComponent(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ // TODO: current can be non-null here even if the component
+ // hasn't yet mounted. This happens when the inner render suspends.
+ // We'll need to figure out if this is fine or can cause issues.
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var outerMemoType = workInProgress.elementType;
- var newQueueLanes = mergeLanes(queueLanes, lane);
- queue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
- // the lane finished since the last time we entangled it. So we need to
- // entangle it again, just to be sure.
+ if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
+ // We warn when you define propTypes on lazy()
+ // so let's just skip over it to find memo() outer wrapper.
+ // Inner props for memo are validated later.
+ var lazyComponent = outerMemoType;
+ var payload = lazyComponent._payload;
+ var init = lazyComponent._init;
- markRootEntangled(root, newQueueLanes);
- }
-}
+ try {
+ outerMemoType = init(payload);
+ } catch (x) {
+ outerMemoType = null;
+ } // Inner propTypes will be validated in the function component path.
-function markUpdateInDevTools(fiber, lane, action) {
- {
- if (enableDebugTracing) {
- if (fiber.mode & DebugTracingMode) {
- var name = getComponentNameFromFiber(fiber) || "Unknown";
- logStateUpdateScheduled(name, lane, action);
+ var outerPropTypes = outerMemoType && outerMemoType.propTypes;
+
+ if (outerPropTypes) {
+ checkPropTypes(
+ outerPropTypes,
+ nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
+ "prop",
+ getComponentNameFromType(outerMemoType)
+ );
+ }
}
}
}
- if (enableSchedulingProfiler) {
- markStateUpdateScheduled(fiber, lane);
+ if (current !== null) {
+ var prevProps = current.memoizedProps;
+
+ if (
+ shallowEqual(prevProps, nextProps) &&
+ current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload.
+ workInProgress.type === current.type
+ ) {
+ didReceiveUpdate = false; // The props are shallowly equal. Reuse the previous props object, like we
+ // would during a normal fiber bailout.
+ //
+ // We don't have strong guarantees that the props object is referentially
+ // equal during updates where we can't bail out anyway — like if the props
+ // are shallowly equal, but there's a local state or context update in the
+ // same batch.
+ //
+ // However, as a principle, we should aim to make the behavior consistent
+ // across different ways of memoizing a component. For example, React.memo
+ // has a different internal Fiber layout if you pass a normal function
+ // component (SimpleMemoComponent) versus if you pass a different type
+ // like forwardRef (MemoComponent). But this is an implementation detail.
+ // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't
+ // affect whether the props object is reused during a bailout.
+
+ workInProgress.pendingProps = nextProps = prevProps;
+
+ if (!checkScheduledUpdateOrContext(current, renderLanes)) {
+ // The pending lanes were cleared at the beginning of beginWork. We're
+ // about to bail out, but there might be other lanes that weren't
+ // included in the current render. Usually, the priority level of the
+ // remaining updates is accumulated during the evaluation of the
+ // component (i.e. when processing the update queue). But since since
+ // we're bailing out early *without* evaluating the component, we need
+ // to account for it here, too. Reset to the value of the current fiber.
+ // NOTE: This only applies to SimpleMemoComponent, not MemoComponent,
+ // because a MemoComponent fiber does not have hooks or an update queue;
+ // rather, it wraps around an inner component, which may or may not
+ // contains hooks.
+ // TODO: Move the reset at in beginWork out of the common path so that
+ // this is no longer necessary.
+ workInProgress.lanes = current.lanes;
+ return bailoutOnAlreadyFinishedWork(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags$1) {
+ // This is a special case that only exists for legacy mode.
+ // See https://github.com/facebook/react/pull/19216.
+ didReceiveUpdate = true;
+ }
+ }
}
+
+ return updateFunctionComponent(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+ );
}
-var ContextOnlyDispatcher = {
- readContext: readContext,
- useCallback: throwInvalidHookError,
- useContext: throwInvalidHookError,
- useEffect: throwInvalidHookError,
- useImperativeHandle: throwInvalidHookError,
- useInsertionEffect: throwInvalidHookError,
- useLayoutEffect: throwInvalidHookError,
- useMemo: throwInvalidHookError,
- useReducer: throwInvalidHookError,
- useRef: throwInvalidHookError,
- useState: throwInvalidHookError,
- useDebugValue: throwInvalidHookError,
- useDeferredValue: throwInvalidHookError,
- useTransition: throwInvalidHookError,
- useMutableSource: throwInvalidHookError,
- useSyncExternalStore: throwInvalidHookError,
- useId: throwInvalidHookError
-};
+function updateOffscreenComponent(current, workInProgress, renderLanes) {
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ var nextIsDetached =
+ (workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0;
+ var prevState = current !== null ? current.memoizedState : null;
+ markRef$1(current, workInProgress);
-{
- ContextOnlyDispatcher.useCacheRefresh = throwInvalidHookError;
-}
+ if (
+ nextProps.mode === "hidden" ||
+ nextProps.mode === "unstable-defer-without-hiding" ||
+ nextIsDetached
+ ) {
+ // Rendering a hidden tree.
+ var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags$1;
-{
- ContextOnlyDispatcher.use = throwInvalidHookError;
-}
+ if (didSuspend) {
+ // Something suspended inside a hidden tree
+ // Include the base lanes from the last render
+ var nextBaseLanes =
+ prevState !== null
+ ? mergeLanes(prevState.baseLanes, renderLanes)
+ : renderLanes;
-{
- ContextOnlyDispatcher.useMemoCache = throwInvalidHookError;
-}
+ if (current !== null) {
+ // Reset to the current children
+ var currentChild = (workInProgress.child = current.child); // The current render suspended, but there may be other lanes with
+ // pending work. We can't read `childLanes` from the current Offscreen
+ // fiber because we reset it when it was deferred; however, we can read
+ // the pending lanes from the child fibers.
-{
- ContextOnlyDispatcher.useEffectEvent = throwInvalidHookError;
-}
+ var currentChildLanes = NoLanes;
-var HooksDispatcherOnMountInDEV = null;
-var HooksDispatcherOnMountWithHookTypesInDEV = null;
-var HooksDispatcherOnUpdateInDEV = null;
-var HooksDispatcherOnRerenderInDEV = null;
-var InvalidNestedHooksDispatcherOnMountInDEV = null;
-var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
-var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
+ while (currentChild !== null) {
+ currentChildLanes = mergeLanes(
+ mergeLanes(currentChildLanes, currentChild.lanes),
+ currentChild.childLanes
+ );
+ currentChild = currentChild.sibling;
+ }
-{
- var warnInvalidContextAccess = function () {
- error(
- "Context can only be read while React is rendering. " +
- "In classes, you can read it in the render method or getDerivedStateFromProps. " +
- "In function components, you can read it directly in the function body, but not " +
- "inside Hooks like useReducer() or useMemo()."
- );
- };
+ var lanesWeJustAttempted = nextBaseLanes;
+ var remainingChildLanes = removeLanes(
+ currentChildLanes,
+ lanesWeJustAttempted
+ );
+ workInProgress.childLanes = remainingChildLanes;
+ } else {
+ workInProgress.childLanes = NoLanes;
+ workInProgress.child = null;
+ }
- var warnInvalidHookAccess = function () {
- error(
- "Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. " +
- "You can only call Hooks at the top level of your React function. " +
- "For more information, see " +
- "https://reactjs.org/link/rules-of-hooks"
- );
- };
+ return deferHiddenOffscreenComponent(
+ current,
+ workInProgress,
+ nextBaseLanes,
+ renderLanes
+ );
+ }
- HooksDispatcherOnMountInDEV = {
- readContext: function (context) {
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- return mountCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- mountHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- return mountEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- return mountImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- return mountInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- return mountLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- mountHookTypesDev();
- checkDepsAreArrayDev(deps);
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ // In legacy sync mode, don't defer the subtree. Render it now.
+ // TODO: Consider how Offscreen should work with transitions in the future
+ var nextState = {
+ baseLanes: NoLanes,
+ cachePool: null
+ };
+ workInProgress.memoizedState = nextState;
- try {
- return mountMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ {
+ // push the cache pool even though we're going to bail out
+ // because otherwise there'd be a context mismatch
+ if (current !== null) {
+ pushTransition(workInProgress, null, null);
+ }
}
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- mountHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
- try {
- return mountReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ reuseHiddenContextOnStack(workInProgress);
+ pushOffscreenSuspenseHandler(workInProgress);
+ } else if (!includesSomeLane(renderLanes, OffscreenLane)) {
+ // We're hidden, and we're not rendering at Offscreen. We will bail out
+ // and resume this tree later.
+ // Schedule this fiber to re-render at Offscreen priority
+ workInProgress.lanes = workInProgress.childLanes =
+ laneToLanes(OffscreenLane); // Include the base lanes from the last render
+
+ var _nextBaseLanes =
+ prevState !== null
+ ? mergeLanes(prevState.baseLanes, renderLanes)
+ : renderLanes;
+
+ return deferHiddenOffscreenComponent(
+ current,
+ workInProgress,
+ _nextBaseLanes,
+ renderLanes
+ );
+ } else {
+ // This is the second render. The surrounding visible content has already
+ // committed. Now we resume rendering the hidden tree.
+ // Rendering at offscreen, so we can clear the base lanes.
+ var _nextState = {
+ baseLanes: NoLanes,
+ cachePool: null
+ };
+ workInProgress.memoizedState = _nextState;
+
+ if (current !== null) {
+ // If the render that spawned this one accessed the cache pool, resume
+ // using the same cache. Unless the parent changed, since that means
+ // there was a refresh.
+ var prevCachePool = prevState !== null ? prevState.cachePool : null; // TODO: Consider if and how Offscreen pre-rendering should
+ // be attributed to the transition that spawned it
+
+ pushTransition(workInProgress, prevCachePool, null);
+ } // Push the lanes that were skipped when we bailed out.
+
+ if (prevState !== null) {
+ pushHiddenContext(workInProgress, prevState);
+ } else {
+ reuseHiddenContextOnStack(workInProgress);
}
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- mountHookTypesDev();
- return mountRef(initialValue);
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- mountHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
- try {
- return mountState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ pushOffscreenSuspenseHandler(workInProgress);
+ }
+ } else {
+ // Rendering a visible tree.
+ if (prevState !== null) {
+ // We're going from hidden -> visible.
+ var _prevCachePool = null;
+
+ {
+ // If the render that spawned this one accessed the cache pool, resume
+ // using the same cache. Unless the parent changed, since that means
+ // there was a refresh.
+ _prevCachePool = prevState.cachePool;
}
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- mountHookTypesDev();
- return mountDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- mountHookTypesDev();
- return mountDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- mountHookTypesDev();
- return mountTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- mountHookTypesDev();
- return mountMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- mountHookTypesDev();
- return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- mountHookTypesDev();
- return mountId();
+
+ var transitions = null;
+
+ if (enableTransitionTracing) {
+ // We have now gone from hidden to visible, so any transitions should
+ // be added to the stack to get added to any Offscreen/suspense children
+ var instance = workInProgress.stateNode;
+
+ if (instance !== null && instance._transitions != null) {
+ transitions = Array.from(instance._transitions);
+ }
+ }
+
+ pushTransition(workInProgress, _prevCachePool, transitions); // Push the lanes that were skipped when we bailed out.
+
+ pushHiddenContext(workInProgress, prevState);
+ reuseSuspenseHandlerOnStack(workInProgress); // Since we're not hidden anymore, reset the state
+
+ workInProgress.memoizedState = null;
+ } else {
+ // We weren't previously hidden, and we still aren't, so there's nothing
+ // special to do. Need to push to the stack regardless, though, to avoid
+ // a push/pop misalignment.
+ {
+ // If the render that spawned this one accessed the cache pool, resume
+ // using the same cache. Unless the parent changed, since that means
+ // there was a refresh.
+ if (current !== null) {
+ pushTransition(workInProgress, null, null);
+ }
+ } // We're about to bail out, but we need to push this to the stack anyway
+ // to avoid a push/pop misalignment.
+
+ reuseHiddenContextOnStack(workInProgress);
+ reuseSuspenseHandlerOnStack(workInProgress);
}
+ }
+
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+
+function deferHiddenOffscreenComponent(
+ current,
+ workInProgress,
+ nextBaseLanes,
+ renderLanes
+) {
+ var nextState = {
+ baseLanes: nextBaseLanes,
+ // Save the cache pool so we can resume later.
+ cachePool: getOffscreenDeferredCache()
};
+ workInProgress.memoizedState = nextState;
{
- HooksDispatcherOnMountInDEV.useCacheRefresh = function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- mountHookTypesDev();
- return mountRefresh();
- };
- }
+ // push the cache pool even though we're going to bail out
+ // because otherwise there'd be a context mismatch
+ if (current !== null) {
+ pushTransition(workInProgress, null, null);
+ }
+ } // We're about to bail out, but we need to push this to the stack anyway
+ // to avoid a push/pop misalignment.
- {
- HooksDispatcherOnMountInDEV.use = use;
- }
+ reuseHiddenContextOnStack(workInProgress);
+ pushOffscreenSuspenseHandler(workInProgress);
- {
- HooksDispatcherOnMountInDEV.useMemoCache = useMemoCache;
+ if (enableLazyContextPropagation && current !== null) {
+ // Since this tree will resume rendering in a separate render, we need
+ // to propagate parent contexts now so we don't lose track of which
+ // ones changed.
+ propagateParentContextChangesToDeferredTree(
+ current,
+ workInProgress,
+ renderLanes
+ );
}
- {
- HooksDispatcherOnMountInDEV.useEffectEvent = function useEffectEvent(
- callback
- ) {
- currentHookNameInDev = "useEffectEvent";
- mountHookTypesDev();
- return mountEvent(callback);
+ return null;
+} // Note: These happen to have identical begin phases, for now. We shouldn't hold
+// ourselves to this constraint, though. If the behavior diverges, we should
+// fork the function.
+
+var updateLegacyHiddenComponent = updateOffscreenComponent;
+
+function updateCacheComponent(current, workInProgress, renderLanes) {
+ prepareToReadContext(workInProgress, renderLanes);
+ var parentCache = readContext(CacheContext);
+
+ if (current === null) {
+ // Initial mount. Request a fresh cache from the pool.
+ var freshCache = requestCacheFromPool(renderLanes);
+ var initialState = {
+ parent: parentCache,
+ cache: freshCache
};
- }
+ workInProgress.memoizedState = initialState;
+ initializeUpdateQueue(workInProgress);
+ pushCacheProvider(workInProgress, freshCache);
+ } else {
+ // Check for updates
+ if (includesSomeLane(current.lanes, renderLanes)) {
+ cloneUpdateQueue(current, workInProgress);
+ processUpdateQueue(workInProgress, null, null, renderLanes);
+ }
- HooksDispatcherOnMountWithHookTypesInDEV = {
- readContext: function (context) {
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- updateHookTypesDev();
- return mountCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- updateHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- updateHookTypesDev();
- return mountEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- updateHookTypesDev();
- return mountImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- updateHookTypesDev();
- return mountInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- updateHookTypesDev();
- return mountLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ var prevState = current.memoizedState;
+ var nextState = workInProgress.memoizedState; // Compare the new parent cache to the previous to see detect there was
+ // a refresh.
- try {
- return mountMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ if (prevState.parent !== parentCache) {
+ // Refresh in parent. Update the parent.
+ var derivedState = {
+ parent: parentCache,
+ cache: parentCache
+ }; // Copied from getDerivedStateFromProps implementation. Once the update
+ // queue is empty, persist the derived state onto the base state.
- try {
- return mountReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ workInProgress.memoizedState = derivedState;
+
+ if (workInProgress.lanes === NoLanes) {
+ var updateQueue = workInProgress.updateQueue;
+ workInProgress.memoizedState = updateQueue.baseState = derivedState;
}
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- updateHookTypesDev();
- return mountRef(initialValue);
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
- try {
- return mountState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ pushCacheProvider(workInProgress, parentCache); // No need to propagate a context change because the refreshed parent
+ // already did.
+ } else {
+ // The parent didn't refresh. Now check if this cache did.
+ var nextCache = nextState.cache;
+ pushCacheProvider(workInProgress, nextCache);
+
+ if (nextCache !== prevState.cache) {
+ // This cache refreshed. Propagate a context change.
+ propagateContextChange(workInProgress, CacheContext, renderLanes);
}
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- updateHookTypesDev();
- return mountDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- updateHookTypesDev();
- return mountDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- updateHookTypesDev();
- return mountTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- updateHookTypesDev();
- return mountMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- updateHookTypesDev();
- return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- updateHookTypesDev();
- return mountId();
}
- };
-
- {
- HooksDispatcherOnMountWithHookTypesInDEV.useCacheRefresh =
- function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- updateHookTypesDev();
- return mountRefresh();
- };
}
- {
- HooksDispatcherOnMountWithHookTypesInDEV.use = use;
- }
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+} // This should only be called if the name changes
- {
- HooksDispatcherOnMountWithHookTypesInDEV.useMemoCache = useMemoCache;
- }
+function updateTracingMarkerComponent(current, workInProgress, renderLanes) {
+ if (!enableTransitionTracing) {
+ return null;
+ } // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.
+ // A tracing marker is only associated with the transitions that rendered
+ // or updated it, so we can create a new set of transitions each time
- {
- HooksDispatcherOnMountWithHookTypesInDEV.useEffectEvent =
- function useEffectEvent(callback) {
- currentHookNameInDev = "useEffectEvent";
- updateHookTypesDev();
- return mountEvent(callback);
+ if (current === null) {
+ var currentTransitions = getPendingTransitions();
+
+ if (currentTransitions !== null) {
+ var markerInstance = {
+ tag: TransitionTracingMarker,
+ transitions: new Set(currentTransitions),
+ pendingBoundaries: null,
+ name: workInProgress.pendingProps.name,
+ aborts: null
};
+ workInProgress.stateNode = markerInstance; // We call the marker complete callback when all child suspense boundaries resolve.
+ // We do this in the commit phase on Offscreen. If the marker has no child suspense
+ // boundaries, we need to schedule a passive effect to make sure we call the marker
+ // complete callback.
+
+ workInProgress.flags |= Passive$1;
+ }
+ } else {
+ {
+ if (current.memoizedProps.name !== workInProgress.pendingProps.name) {
+ error(
+ "Changing the name of a tracing marker after mount is not supported. " +
+ "To remount the tracing marker, pass it a new key."
+ );
+ }
+ }
}
- HooksDispatcherOnUpdateInDEV = {
- readContext: function (context) {
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- updateHookTypesDev();
- return updateCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- updateHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- updateHookTypesDev();
- return updateEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- updateHookTypesDev();
- return updateImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- updateHookTypesDev();
- return updateInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- updateHookTypesDev();
- return updateLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ var instance = workInProgress.stateNode;
- try {
- return updateMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ if (instance !== null) {
+ pushMarkerInstance(workInProgress, instance);
+ }
- try {
- return updateReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- updateHookTypesDev();
- return updateRef();
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
- try {
- return updateState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- updateHookTypesDev();
- return updateDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- updateHookTypesDev();
- return updateDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- updateHookTypesDev();
- return updateTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- updateHookTypesDev();
- return updateMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- updateHookTypesDev();
- return updateSyncExternalStore(subscribe, getSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- updateHookTypesDev();
- return updateId();
- }
- };
+function updateFragment(current, workInProgress, renderLanes) {
+ var nextChildren = workInProgress.pendingProps;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
- {
- HooksDispatcherOnUpdateInDEV.useCacheRefresh = function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- updateHookTypesDev();
- return updateRefresh();
- };
- }
+function updateMode(current, workInProgress, renderLanes) {
+ var nextChildren = workInProgress.pendingProps.children;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+function updateProfiler(current, workInProgress, renderLanes) {
{
- HooksDispatcherOnUpdateInDEV.use = use;
- }
+ workInProgress.flags |= Update;
- {
- HooksDispatcherOnUpdateInDEV.useMemoCache = useMemoCache;
+ {
+ // Reset effect durations for the next eventual effect phase.
+ // These are reset during render to allow the DevTools commit hook a chance to read them,
+ var stateNode = workInProgress.stateNode;
+ stateNode.effectDuration = 0;
+ stateNode.passiveEffectDuration = 0;
+ }
}
- {
- HooksDispatcherOnUpdateInDEV.useEffectEvent = function useEffectEvent(
- callback
- ) {
- currentHookNameInDev = "useEffectEvent";
- updateHookTypesDev();
- return updateEvent(callback);
- };
- }
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
- HooksDispatcherOnRerenderInDEV = {
- readContext: function (context) {
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- updateHookTypesDev();
- return updateCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- updateHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- updateHookTypesDev();
- return updateEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- updateHookTypesDev();
- return updateImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- updateHookTypesDev();
- return updateInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- updateHookTypesDev();
- return updateLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnRerenderInDEV;
+function markRef$1(current, workInProgress) {
+ var ref = workInProgress.ref;
- try {
- return updateMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnRerenderInDEV;
+ if (
+ (current === null && ref !== null) ||
+ (current !== null && current.ref !== ref)
+ ) {
+ // Schedule a Ref effect
+ workInProgress.flags |= Ref;
+ workInProgress.flags |= RefStatic;
+ }
+}
- try {
- return rerenderReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- updateHookTypesDev();
- return updateRef();
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnRerenderInDEV;
+function updateFunctionComponent(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
- try {
- return rerenderState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ if (innerPropTypes) {
+ checkPropTypes(
+ innerPropTypes,
+ nextProps, // Resolved props
+ "prop",
+ getComponentNameFromType(Component)
+ );
}
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- updateHookTypesDev();
- return updateDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- updateHookTypesDev();
- return rerenderDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- updateHookTypesDev();
- return rerenderTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- updateHookTypesDev();
- return updateMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- updateHookTypesDev();
- return updateSyncExternalStore(subscribe, getSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- updateHookTypesDev();
- return updateId();
}
- };
+ }
+
+ var context;
{
- HooksDispatcherOnRerenderInDEV.useCacheRefresh =
- function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- updateHookTypesDev();
- return updateRefresh();
- };
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
+ context = getMaskedContext(workInProgress, unmaskedContext);
}
- {
- HooksDispatcherOnRerenderInDEV.use = use;
+ var nextChildren;
+ var hasId;
+ prepareToReadContext(workInProgress, renderLanes);
+
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
}
{
- HooksDispatcherOnRerenderInDEV.useMemoCache = useMemoCache;
+ ReactCurrentOwner$2.current = workInProgress;
+ setIsRendering(true);
+ nextChildren = renderWithHooks(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ context,
+ renderLanes
+ );
+ hasId = checkDidRenderIdHook();
+ setIsRendering(false);
}
- {
- HooksDispatcherOnRerenderInDEV.useEffectEvent = function useEffectEvent(
- callback
- ) {
- currentHookNameInDev = "useEffectEvent";
- updateHookTypesDev();
- return updateEvent(callback);
- };
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
}
- InvalidNestedHooksDispatcherOnMountInDEV = {
- readContext: function (context) {
- warnInvalidContextAccess();
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- warnInvalidHookAccess();
- mountHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ if (current !== null && !didReceiveUpdate) {
+ bailoutHooks(current, workInProgress, renderLanes);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
- try {
- return mountMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- warnInvalidHookAccess();
- mountHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ if (getIsHydrating() && hasId) {
+ pushMaterializedTreeId(workInProgress);
+ } // React DevTools reads this flag.
- try {
- return mountReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountRef(initialValue);
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- warnInvalidHookAccess();
- mountHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnMountInDEV;
+ workInProgress.flags |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
- try {
- return mountState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountId();
- }
- };
+function replayFunctionComponent(
+ current,
+ workInProgress,
+ nextProps,
+ Component,
+ renderLanes
+) {
+ // This function is used to replay a component that previously suspended,
+ // after its data resolves. It's a simplified version of
+ // updateFunctionComponent that reuses the hooks from the previous attempt.
+ var context;
{
- InvalidNestedHooksDispatcherOnMountInDEV.useCacheRefresh =
- function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- mountHookTypesDev();
- return mountRefresh();
- };
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
+ context = getMaskedContext(workInProgress, unmaskedContext);
}
- {
- InvalidNestedHooksDispatcherOnMountInDEV.use = function (usable) {
- warnInvalidHookAccess();
- return use(usable);
- };
+ prepareToReadContext(workInProgress, renderLanes);
+
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
}
- {
- InvalidNestedHooksDispatcherOnMountInDEV.useMemoCache = function (size) {
- warnInvalidHookAccess();
- return useMemoCache(size);
- };
+ var nextChildren = replaySuspendedComponentWithHooks(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ context
+ );
+ var hasId = checkDidRenderIdHook();
+
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
}
- {
- InvalidNestedHooksDispatcherOnMountInDEV.useEffectEvent =
- function useEffectEvent(callback) {
- currentHookNameInDev = "useEffectEvent";
- warnInvalidHookAccess();
- mountHookTypesDev();
- return mountEvent(callback);
- };
+ if (current !== null && !didReceiveUpdate) {
+ bailoutHooks(current, workInProgress, renderLanes);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
- InvalidNestedHooksDispatcherOnUpdateInDEV = {
- readContext: function (context) {
- warnInvalidContextAccess();
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ if (getIsHydrating() && hasId) {
+ pushMaterializedTreeId(workInProgress);
+ } // React DevTools reads this flag.
- try {
- return updateMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ workInProgress.flags |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+
+function updateClassComponent(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ {
+ // This is used by DevTools to force a boundary to error.
+ switch (shouldError(workInProgress)) {
+ case false: {
+ var _instance = workInProgress.stateNode;
+ var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack.
+ // Is there a better way to do this?
+
+ var tempInstance = new ctor(
+ workInProgress.memoizedProps,
+ _instance.context
+ );
+ var state = tempInstance.state;
+
+ _instance.updater.enqueueSetState(_instance, state, null);
+
+ break;
}
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
- try {
- return updateReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ case true: {
+ workInProgress.flags |= DidCapture;
+ workInProgress.flags |= ShouldCapture; // eslint-disable-next-line react-internal/prod-error-codes
+
+ var error$1 = new Error("Simulated error coming from DevTools");
+ var lane = pickArbitraryLane(renderLanes);
+ workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state
+
+ var update = createClassErrorUpdate(
+ workInProgress,
+ createCapturedValueAtFiber(error$1, workInProgress),
+ lane
+ );
+ enqueueCapturedUpdate(workInProgress, update);
+ break;
}
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateRef();
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ }
- try {
- return updateState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ if (workInProgress.type !== workInProgress.elementType) {
+ // Lazy component props can't be validated in createElement
+ // because they're only guaranteed to be resolved here.
+ var innerPropTypes = Component.propTypes;
+
+ if (innerPropTypes) {
+ checkPropTypes(
+ innerPropTypes,
+ nextProps, // Resolved props
+ "prop",
+ getComponentNameFromType(Component)
+ );
}
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateSyncExternalStore(subscribe, getSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateId();
}
- };
+ } // 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.
- {
- InvalidNestedHooksDispatcherOnUpdateInDEV.useCacheRefresh =
- function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- updateHookTypesDev();
- return updateRefresh();
- };
- }
+ var hasContext;
- {
- InvalidNestedHooksDispatcherOnUpdateInDEV.use = function (usable) {
- warnInvalidHookAccess();
- return use(usable);
- };
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
}
- {
- InvalidNestedHooksDispatcherOnUpdateInDEV.useMemoCache = function (size) {
- warnInvalidHookAccess();
- return useMemoCache(size);
- };
+ prepareToReadContext(workInProgress, renderLanes);
+ var instance = workInProgress.stateNode;
+ var shouldUpdate;
+
+ if (instance === null) {
+ resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress); // In the initial pass we might need to construct the instance.
+
+ constructClassInstance(workInProgress, Component, nextProps);
+ mountClassInstance(workInProgress, Component, nextProps, renderLanes);
+ shouldUpdate = true;
+ } else if (current === null) {
+ // In a resume, we'll already have an instance we can reuse.
+ shouldUpdate = resumeMountClassInstance(
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+ );
+ } else {
+ shouldUpdate = updateClassInstance(
+ current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+ );
}
+ var nextUnitOfWork = finishClassComponent(
+ current,
+ workInProgress,
+ Component,
+ shouldUpdate,
+ hasContext,
+ renderLanes
+ );
+
{
- InvalidNestedHooksDispatcherOnUpdateInDEV.useEffectEvent =
- function useEffectEvent(callback) {
- currentHookNameInDev = "useEffectEvent";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateEvent(callback);
- };
+ var inst = workInProgress.stateNode;
+
+ if (shouldUpdate && inst.props !== nextProps) {
+ if (!didWarnAboutReassigningProps) {
+ error(
+ "It looks like %s is reassigning its own `this.props` while rendering. " +
+ "This is not supported and can lead to confusing bugs.",
+ getComponentNameFromFiber(workInProgress) || "a component"
+ );
+ }
+
+ didWarnAboutReassigningProps = true;
+ }
}
- InvalidNestedHooksDispatcherOnRerenderInDEV = {
- readContext: function (context) {
- warnInvalidContextAccess();
- return readContext(context);
- },
- useCallback: function (callback, deps) {
- currentHookNameInDev = "useCallback";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateCallback(callback, deps);
- },
- useContext: function (context) {
- currentHookNameInDev = "useContext";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return readContext(context);
- },
- useEffect: function (create, deps) {
- currentHookNameInDev = "useEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateEffect(create, deps);
- },
- useImperativeHandle: function (ref, create, deps) {
- currentHookNameInDev = "useImperativeHandle";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateImperativeHandle(ref, create, deps);
- },
- useInsertionEffect: function (create, deps) {
- currentHookNameInDev = "useInsertionEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateInsertionEffect(create, deps);
- },
- useLayoutEffect: function (create, deps) {
- currentHookNameInDev = "useLayoutEffect";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateLayoutEffect(create, deps);
- },
- useMemo: function (create, deps) {
- currentHookNameInDev = "useMemo";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+ return nextUnitOfWork;
+}
- try {
- return updateMemo(create, deps);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
- }
- },
- useReducer: function (reducer, initialArg, init) {
- currentHookNameInDev = "useReducer";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
+function finishClassComponent(
+ current,
+ workInProgress,
+ Component,
+ shouldUpdate,
+ hasContext,
+ renderLanes
+) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef$1(current, workInProgress);
+ var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags$1;
- try {
- return rerenderReducer(reducer, initialArg, init);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ if (!shouldUpdate && !didCaptureError) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, false);
+ }
+
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
+
+ var instance = workInProgress.stateNode; // Rerender
+
+ ReactCurrentOwner$2.current = workInProgress;
+ var nextChildren;
+
+ if (
+ didCaptureError &&
+ typeof Component.getDerivedStateFromError !== "function"
+ ) {
+ // If we captured an error, but getDerivedStateFromError is not defined,
+ // unmount all the children. componentDidCatch will schedule an update to
+ // re-render a fallback. This is temporary until we migrate everyone to
+ // the new API.
+ // TODO: Warn in a future release.
+ nextChildren = null;
+
+ {
+ stopProfilerTimerIfRunning();
+ }
+ } else {
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
+ }
+
+ {
+ setIsRendering(true);
+ nextChildren = instance.render();
+
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
+
+ try {
+ instance.render();
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
}
- },
- useRef: function (initialValue) {
- currentHookNameInDev = "useRef";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateRef();
- },
- useState: function (initialState) {
- currentHookNameInDev = "useState";
- warnInvalidHookAccess();
- updateHookTypesDev();
- var prevDispatcher = ReactCurrentDispatcher$1.current;
- ReactCurrentDispatcher$1.current =
- InvalidNestedHooksDispatcherOnUpdateInDEV;
- try {
- return rerenderState(initialState);
- } finally {
- ReactCurrentDispatcher$1.current = prevDispatcher;
+ setIsRendering(false);
+ }
+
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
+ }
+ } // React DevTools reads this flag.
+
+ workInProgress.flags |= PerformedWork;
+
+ if (current !== null && didCaptureError) {
+ // If we're recovering from an error, reconcile without reusing any of
+ // the existing children. Conceptually, the normal children and the children
+ // that are shown on error are two different sets, so we shouldn't reuse
+ // normal children even if their identities match.
+ forceUnmountCurrentAndReconcile(
+ current,
+ workInProgress,
+ nextChildren,
+ renderLanes
+ );
+ } else {
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ } // Memoize state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+
+ workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it.
+
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, Component, true);
+ }
+
+ return workInProgress.child;
+}
+
+function pushHostRootContext(workInProgress) {
+ var root = workInProgress.stateNode;
+
+ if (root.pendingContext) {
+ pushTopLevelContextObject(
+ workInProgress,
+ root.pendingContext,
+ root.pendingContext !== root.context
+ );
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
+ }
+
+ pushHostContainer(workInProgress, root.containerInfo);
+}
+
+function updateHostRoot(current, workInProgress, renderLanes) {
+ pushHostRootContext(workInProgress);
+
+ if (current === null) {
+ throw new Error("Should have a current fiber. This is a bug in React.");
+ }
+
+ var nextProps = workInProgress.pendingProps;
+ var prevState = workInProgress.memoizedState;
+ var prevChildren = prevState.element;
+ cloneUpdateQueue(current, workInProgress);
+ processUpdateQueue(workInProgress, nextProps, null, renderLanes);
+ var nextState = workInProgress.memoizedState;
+ var root = workInProgress.stateNode;
+ pushRootTransition(workInProgress);
+
+ if (enableTransitionTracing) {
+ pushRootMarkerInstance(workInProgress);
+ }
+
+ {
+ var nextCache = nextState.cache;
+ pushCacheProvider(workInProgress, nextCache);
+
+ if (nextCache !== prevState.cache) {
+ // The root cache refreshed.
+ propagateContextChange(workInProgress, CacheContext, renderLanes);
+ }
+ } // Caution: React DevTools currently depends on this property
+ // being called "element".
+
+ var nextChildren = nextState.element;
+
+ if (prevState.isDehydrated) {
+ // This is a hydration root whose shell has not yet hydrated. We should
+ // attempt to hydrate.
+ // Flip isDehydrated to false to indicate that when this render
+ // finishes, the root will no longer be dehydrated.
+ var overrideState = {
+ element: nextChildren,
+ isDehydrated: false,
+ cache: nextState.cache
+ };
+ var updateQueue = workInProgress.updateQueue; // `baseState` can always be the last state because the root doesn't
+ // have reducer functions so it doesn't need rebasing.
+
+ updateQueue.baseState = overrideState;
+ workInProgress.memoizedState = overrideState;
+
+ if (workInProgress.flags & ForceClientRender) {
+ // Something errored during a previous attempt to hydrate the shell, so we
+ // forced a client render.
+ var recoverableError = createCapturedValueAtFiber(
+ new Error(
+ "There was an error while hydrating. Because the error happened outside " +
+ "of a Suspense boundary, the entire root will switch to " +
+ "client rendering."
+ ),
+ workInProgress
+ );
+ return mountHostRootWithoutHydrating(
+ current,
+ workInProgress,
+ nextChildren,
+ renderLanes,
+ recoverableError
+ );
+ } else if (nextChildren !== prevChildren) {
+ var _recoverableError = createCapturedValueAtFiber(
+ new Error(
+ "This root received an early update, before anything was able " +
+ "hydrate. Switched the entire root to client rendering."
+ ),
+ workInProgress
+ );
+
+ return mountHostRootWithoutHydrating(
+ current,
+ workInProgress,
+ nextChildren,
+ renderLanes,
+ _recoverableError
+ );
+ } else {
+ // The outermost shell has not hydrated yet. Start hydrating.
+ enterHydrationState(workInProgress);
+
+ {
+ var mutableSourceEagerHydrationData =
+ root.mutableSourceEagerHydrationData;
+
+ if (mutableSourceEagerHydrationData != null) {
+ for (var i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
+ var mutableSource = mutableSourceEagerHydrationData[i];
+ var version = mutableSourceEagerHydrationData[i + 1];
+ setWorkInProgressVersion(mutableSource, version);
+ }
+ }
+ }
+
+ var child = mountChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderLanes
+ );
+ workInProgress.child = child;
+ var node = child;
+
+ while (node) {
+ // Mark each child as hydrating. This is a fast path to know whether this
+ // tree is part of a hydrating tree. This is used to determine if a child
+ // node has fully mounted yet, and for scheduling event replaying.
+ // Conceptually this is similar to Placement in that a new subtree is
+ // inserted into the React tree here. It just happens to not need DOM
+ // mutations because it already exists.
+ node.flags = (node.flags & ~Placement) | Hydrating;
+ node = node.sibling;
}
- },
- useDebugValue: function (value, formatterFn) {
- currentHookNameInDev = "useDebugValue";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateDebugValue();
- },
- useDeferredValue: function (value) {
- currentHookNameInDev = "useDeferredValue";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return rerenderDeferredValue(value);
- },
- useTransition: function () {
- currentHookNameInDev = "useTransition";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return rerenderTransition();
- },
- useMutableSource: function (source, getSnapshot, subscribe) {
- currentHookNameInDev = "useMutableSource";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateMutableSource(source, getSnapshot, subscribe);
- },
- useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
- currentHookNameInDev = "useSyncExternalStore";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateSyncExternalStore(subscribe, getSnapshot);
- },
- useId: function () {
- currentHookNameInDev = "useId";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateId();
}
- };
+ } else {
+ // Root is not dehydrated. Either this is a client-only root, or it
+ // already hydrated.
+ resetHydrationState();
+
+ if (nextChildren === prevChildren) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
+
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ }
+
+ return workInProgress.child;
+}
+
+function mountHostRootWithoutHydrating(
+ current,
+ workInProgress,
+ nextChildren,
+ renderLanes,
+ recoverableError
+) {
+ // Revert to client rendering.
+ resetHydrationState();
+ queueHydrationError(recoverableError);
+ workInProgress.flags |= ForceClientRender;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+
+function updateHostComponent$1(current, workInProgress, renderLanes) {
+ pushHostContext(workInProgress);
+
+ if (current === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+
+ var type = workInProgress.type;
+ var nextProps = workInProgress.pendingProps;
+ var prevProps = current !== null ? current.memoizedProps : null;
+ var nextChildren = nextProps.children;
+ var isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also has access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.flags |= ContentReset;
+ }
+
+ markRef$1(current, workInProgress);
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
+
+function updateHostHoistable(current, workInProgress, renderLanes) {
+ markRef$1(current, workInProgress);
+ var currentProps = current === null ? null : current.memoizedProps;
+ var resource = (workInProgress.memoizedState = getResource(
+ workInProgress.type,
+ currentProps,
+ workInProgress.pendingProps
+ ));
+
+ if (current === null) {
+ if (!getIsHydrating() && resource === null) {
+ // This is not a Resource Hoistable and we aren't hydrating so we construct the instance.
+ workInProgress.stateNode = createHoistableInstance(
+ workInProgress.type,
+ workInProgress.pendingProps,
+ getRootHostContainer(),
+ workInProgress
+ );
+ }
+ } // Resources never have reconciler managed children. It is possible for
+ // the host implementation of getResource to consider children in the
+ // resource construction but they will otherwise be discarded. In practice
+ // this precludes all but the simplest children and Host specific warnings
+ // should be implemented to warn when children are passsed when otherwise not
+ // expected
+
+ return null;
+}
+
+function updateHostSingleton(current, workInProgress, renderLanes) {
+ pushHostContext(workInProgress);
+
+ if (current === null) {
+ claimHydratableSingleton(workInProgress);
+ }
+
+ var nextChildren = workInProgress.pendingProps.children;
+
+ if (current === null && !getIsHydrating()) {
+ // Similar to Portals we append Singleton children in the commit phase. So we
+ // Track insertions even on mount.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderLanes
+ );
+ } else {
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ }
+
+ markRef$1(current, workInProgress);
+ return workInProgress.child;
+}
+
+function updateHostText$1(current, workInProgress) {
+ if (current === null) {
+ tryToClaimNextHydratableTextInstance(workInProgress);
+ } // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+
+ return null;
+}
+
+function mountLazyComponent(
+ _current,
+ workInProgress,
+ elementType,
+ renderLanes
+) {
+ resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
+ var props = workInProgress.pendingProps;
+ var lazyComponent = elementType;
+ var payload = lazyComponent._payload;
+ var init = lazyComponent._init;
+ var Component = init(payload); // Store the unwrapped component in the type.
+
+ workInProgress.type = Component;
+ var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
+ var resolvedProps = resolveDefaultProps(Component, props);
+ var child;
+
+ switch (resolvedTag) {
+ case FunctionComponent: {
+ {
+ validateFunctionComponentInDev(workInProgress, Component);
+ workInProgress.type = Component =
+ resolveFunctionForHotReloading(Component);
+ }
+
+ child = updateFunctionComponent(
+ null,
+ workInProgress,
+ Component,
+ resolvedProps,
+ renderLanes
+ );
+ return child;
+ }
+
+ case ClassComponent: {
+ {
+ workInProgress.type = Component =
+ resolveClassForHotReloading(Component);
+ }
+
+ child = updateClassComponent(
+ null,
+ workInProgress,
+ Component,
+ resolvedProps,
+ renderLanes
+ );
+ return child;
+ }
+
+ case ForwardRef: {
+ {
+ workInProgress.type = Component =
+ resolveForwardRefForHotReloading(Component);
+ }
+
+ child = updateForwardRef(
+ null,
+ workInProgress,
+ Component,
+ resolvedProps,
+ renderLanes
+ );
+ return child;
+ }
+
+ case MemoComponent: {
+ {
+ if (workInProgress.type !== workInProgress.elementType) {
+ var outerPropTypes = Component.propTypes;
+
+ if (outerPropTypes) {
+ checkPropTypes(
+ outerPropTypes,
+ resolvedProps, // Resolved for outer only
+ "prop",
+ getComponentNameFromType(Component)
+ );
+ }
+ }
+ }
+
+ child = updateMemoComponent(
+ null,
+ workInProgress,
+ Component,
+ resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
+ renderLanes
+ );
+ return child;
+ }
+ }
+
+ var hint = "";
+
+ {
+ if (
+ Component !== null &&
+ typeof Component === "object" &&
+ Component.$$typeof === REACT_LAZY_TYPE
+ ) {
+ hint = " Did you wrap a component in React.lazy() more than once?";
+ }
+ } // This message intentionally doesn't mention ForwardRef or MemoComponent
+ // because the fact that it's a separate type of work is an
+ // implementation detail.
+
+ throw new Error(
+ "Element type is invalid. Received a promise that resolves to: " +
+ Component +
+ ". " +
+ ("Lazy element type must resolve to a class or function." + hint)
+ );
+}
+
+function mountIncompleteClassComponent(
+ _current,
+ workInProgress,
+ Component,
+ nextProps,
+ renderLanes
+) {
+ resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); // Promote the fiber to a class and try rendering again.
+
+ workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent`
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+
+ var hasContext;
+
+ if (isContextProvider(Component)) {
+ hasContext = true;
+ pushContextProvider(workInProgress);
+ } else {
+ hasContext = false;
+ }
+
+ prepareToReadContext(workInProgress, renderLanes);
+ constructClassInstance(workInProgress, Component, nextProps);
+ mountClassInstance(workInProgress, Component, nextProps, renderLanes);
+ return finishClassComponent(
+ null,
+ workInProgress,
+ Component,
+ true,
+ hasContext,
+ renderLanes
+ );
+}
+
+function mountIndeterminateComponent(
+ _current,
+ workInProgress,
+ Component,
+ renderLanes
+) {
+ resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
+ var props = workInProgress.pendingProps;
+ var context;
{
- InvalidNestedHooksDispatcherOnRerenderInDEV.useCacheRefresh =
- function useCacheRefresh() {
- currentHookNameInDev = "useCacheRefresh";
- updateHookTypesDev();
- return updateRefresh();
- };
+ var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
+ context = getMaskedContext(workInProgress, unmaskedContext);
}
- {
- InvalidNestedHooksDispatcherOnRerenderInDEV.use = function (usable) {
- warnInvalidHookAccess();
- return use(usable);
- };
- }
+ prepareToReadContext(workInProgress, renderLanes);
+ var value;
+ var hasId;
- {
- InvalidNestedHooksDispatcherOnRerenderInDEV.useMemoCache = function (size) {
- warnInvalidHookAccess();
- return useMemoCache(size);
- };
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
}
{
- InvalidNestedHooksDispatcherOnRerenderInDEV.useEffectEvent =
- function useEffectEvent(callback) {
- currentHookNameInDev = "useEffectEvent";
- warnInvalidHookAccess();
- updateHookTypesDev();
- return updateEvent(callback);
- };
- }
-}
+ if (
+ Component.prototype &&
+ typeof Component.prototype.render === "function"
+ ) {
+ var componentName = getComponentNameFromType(Component) || "Unknown";
-var now = Scheduler.unstable_now;
-var commitTime = 0;
-var layoutEffectStartTime = -1;
-var profilerStartTime = -1;
-var passiveEffectStartTime = -1;
-/**
- * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect).
- *
- * The overall sequence is:
- * 1. render
- * 2. commit (and call `onRender`, `onCommit`)
- * 3. check for nested updates
- * 4. flush passive effects (and call `onPostCommit`)
- *
- * Nested updates are identified in step 3 above,
- * but step 4 still applies to the work that was just committed.
- * We use two flags to track nested updates then:
- * one tracks whether the upcoming update is a nested update,
- * and the other tracks whether the current update was a nested update.
- * The first value gets synced to the second at the start of the render phase.
- */
+ if (!didWarnAboutBadClass[componentName]) {
+ error(
+ "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
+ "This is likely to cause errors. Change %s to extend React.Component instead.",
+ componentName,
+ componentName
+ );
-var currentUpdateIsNested = false;
-var nestedUpdateScheduled = false;
+ didWarnAboutBadClass[componentName] = true;
+ }
+ }
-function isCurrentUpdateNested() {
- return currentUpdateIsNested;
-}
+ if (workInProgress.mode & StrictLegacyMode) {
+ ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
+ }
-function markNestedUpdateScheduled() {
- {
- nestedUpdateScheduled = true;
+ setIsRendering(true);
+ ReactCurrentOwner$2.current = workInProgress;
+ value = renderWithHooks(
+ null,
+ workInProgress,
+ Component,
+ props,
+ context,
+ renderLanes
+ );
+ hasId = checkDidRenderIdHook();
+ setIsRendering(false);
}
-}
-function resetNestedUpdateFlag() {
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
+ } // React DevTools reads this flag.
+
+ workInProgress.flags |= PerformedWork;
+
{
- currentUpdateIsNested = false;
- nestedUpdateScheduled = false;
+ // Support for module components is deprecated and is removed behind a flag.
+ // Whether or not it would crash later, we want to show a good message in DEV first.
+ if (
+ typeof value === "object" &&
+ value !== null &&
+ typeof value.render === "function" &&
+ value.$$typeof === undefined
+ ) {
+ var _componentName = getComponentNameFromType(Component) || "Unknown";
+
+ if (!didWarnAboutModulePatternComponent[_componentName]) {
+ error(
+ "The <%s /> component appears to be a function component that returns a class instance. " +
+ "Change %s to a class that extends React.Component instead. " +
+ "If you can't use a class try assigning the prototype on the function as a workaround. " +
+ "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
+ "cannot be called with `new` by React.",
+ _componentName,
+ _componentName,
+ _componentName
+ );
+
+ didWarnAboutModulePatternComponent[_componentName] = true;
+ }
+ }
}
-}
-function syncNestedUpdateFlag() {
{
- currentUpdateIsNested = nestedUpdateScheduled;
- nestedUpdateScheduled = false;
- }
-}
+ // Proceed under the assumption that this is a function component
+ workInProgress.tag = FunctionComponent;
-function getCommitTime() {
- return commitTime;
-}
+ if (getIsHydrating() && hasId) {
+ pushMaterializedTreeId(workInProgress);
+ }
-function recordCommitTime() {
- commitTime = now();
-}
+ reconcileChildren(null, workInProgress, value, renderLanes);
-function startProfilerTimer(fiber) {
- profilerStartTime = now();
+ {
+ validateFunctionComponentInDev(workInProgress, Component);
+ }
- if (fiber.actualStartTime < 0) {
- fiber.actualStartTime = now();
+ return workInProgress.child;
}
}
-function stopProfilerTimerIfRunning(fiber) {
- profilerStartTime = -1;
-}
+function validateFunctionComponentInDev(workInProgress, Component) {
+ {
+ if (Component) {
+ if (Component.childContextTypes) {
+ error(
+ "%s(...): childContextTypes cannot be defined on a function component.",
+ Component.displayName || Component.name || "Component"
+ );
+ }
+ }
-function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
- if (profilerStartTime >= 0) {
- var elapsedTime = now() - profilerStartTime;
- fiber.actualDuration += elapsedTime;
+ if (workInProgress.ref !== null) {
+ var info = "";
+ var ownerName = getCurrentFiberOwnerNameInDevOrNull();
- if (overrideBaseTime) {
- fiber.selfBaseDuration = elapsedTime;
+ if (ownerName) {
+ info += "\n\nCheck the render method of `" + ownerName + "`.";
+ }
+
+ var warningKey = ownerName || "";
+ var debugSource = workInProgress._debugSource;
+
+ if (debugSource) {
+ warningKey = debugSource.fileName + ":" + debugSource.lineNumber;
+ }
+
+ if (!didWarnAboutFunctionRefs[warningKey]) {
+ didWarnAboutFunctionRefs[warningKey] = true;
+
+ error(
+ "Function components cannot be given refs. " +
+ "Attempts to access this ref will fail. " +
+ "Did you mean to use React.forwardRef()?%s",
+ info
+ );
+ }
}
- profilerStartTime = -1;
- }
-}
+ if (Component.defaultProps !== undefined) {
+ var componentName = getComponentNameFromType(Component) || "Unknown";
-function recordLayoutEffectDuration(fiber) {
- if (layoutEffectStartTime >= 0) {
- var elapsedTime = now() - layoutEffectStartTime;
- layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
- // Or the root (for the DevTools Profiler to read)
+ if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
+ error(
+ "%s: Support for defaultProps will be removed from function components " +
+ "in a future major release. Use JavaScript default parameters instead.",
+ componentName
+ );
- var parentFiber = fiber.return;
+ didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
+ }
+ }
- while (parentFiber !== null) {
- switch (parentFiber.tag) {
- case HostRoot:
- var root = parentFiber.stateNode;
- root.effectDuration += elapsedTime;
- return;
+ if (typeof Component.getDerivedStateFromProps === "function") {
+ var _componentName3 = getComponentNameFromType(Component) || "Unknown";
- case Profiler:
- var parentStateNode = parentFiber.stateNode;
- parentStateNode.effectDuration += elapsedTime;
- return;
+ if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) {
+ error(
+ "%s: Function components do not support getDerivedStateFromProps.",
+ _componentName3
+ );
+
+ didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true;
}
+ }
- parentFiber = parentFiber.return;
+ if (
+ typeof Component.contextType === "object" &&
+ Component.contextType !== null
+ ) {
+ var _componentName4 = getComponentNameFromType(Component) || "Unknown";
+
+ if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) {
+ error(
+ "%s: Function components do not support contextType.",
+ _componentName4
+ );
+
+ didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true;
+ }
}
}
}
-function recordPassiveEffectDuration(fiber) {
- if (passiveEffectStartTime >= 0) {
- var elapsedTime = now() - passiveEffectStartTime;
- passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
- // Or the root (for the DevTools Profiler to read)
+var SUSPENDED_MARKER = {
+ dehydrated: null,
+ treeContext: null,
+ retryLane: NoLane
+};
- var parentFiber = fiber.return;
+function mountSuspenseOffscreenState(renderLanes) {
+ return {
+ baseLanes: renderLanes,
+ cachePool: getSuspendedCache()
+ };
+}
- while (parentFiber !== null) {
- switch (parentFiber.tag) {
- case HostRoot:
- var root = parentFiber.stateNode;
+function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) {
+ var cachePool = null;
- if (root !== null) {
- root.passiveEffectDuration += elapsedTime;
- }
+ {
+ var prevCachePool = prevOffscreenState.cachePool;
- return;
+ if (prevCachePool !== null) {
+ var parentCache = CacheContext._currentValue;
- case Profiler:
- var parentStateNode = parentFiber.stateNode;
+ if (prevCachePool.parent !== parentCache) {
+ // Detected a refresh in the parent. This overrides any previously
+ // suspended cache.
+ cachePool = {
+ parent: parentCache,
+ pool: parentCache
+ };
+ } else {
+ // We can reuse the cache from last time. The only thing that would have
+ // overridden it is a parent refresh, which we checked for above.
+ cachePool = prevCachePool;
+ }
+ } else {
+ // If there's no previous cache pool, grab the current one.
+ cachePool = getSuspendedCache();
+ }
+ }
- if (parentStateNode !== null) {
- // Detached fibers have their state node cleared out.
- // In this case, the return pointer is also cleared out,
- // so we won't be able to report the time spent in this Profiler's subtree.
- parentStateNode.passiveEffectDuration += elapsedTime;
- }
+ return {
+ baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
+ cachePool: cachePool
+ };
+} // TODO: Probably should inline this back
- return;
- }
+function shouldRemainOnFallback(current, workInProgress, renderLanes) {
+ // If we're already showing a fallback, there are cases where we need to
+ // remain on that fallback regardless of whether the content has resolved.
+ // For example, SuspenseList coordinates when nested content appears.
+ if (current !== null) {
+ var suspenseState = current.memoizedState;
- parentFiber = parentFiber.return;
+ if (suspenseState === null) {
+ // Currently showing content. Don't hide it, even if ForceSuspenseFallback
+ // is true. More precise name might be "ForceRemainSuspenseFallback".
+ // Note: This is a factoring smell. Can't remain on a fallback if there's
+ // no fallback to remain on.
+ return false;
}
- }
-}
+ } // Not currently showing content. Consult the Suspense context.
-function startLayoutEffectTimer() {
- layoutEffectStartTime = now();
+ var suspenseContext = suspenseStackCursor.current;
+ return hasSuspenseListContext(suspenseContext, ForceSuspenseFallback);
}
-function startPassiveEffectTimer() {
- passiveEffectStartTime = now();
+function getRemainingWorkInPrimaryTree(current, renderLanes) {
+ // TODO: Should not remove render lanes that were pinged during this render
+ return removeLanes(current.childLanes, renderLanes);
}
-function transferActualDuration(fiber) {
- // Transfer time spent rendering these children so we don't lose it
- // after we rerender. This is used as a helper in special cases
- // where we should count the work of multiple passes.
- var child = fiber.child;
+function updateSuspenseComponent(current, workInProgress, renderLanes) {
+ var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend.
- while (child) {
- // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
- fiber.actualDuration += child.actualDuration;
- child = child.sibling;
+ {
+ if (shouldSuspend(workInProgress)) {
+ workInProgress.flags |= DidCapture;
+ }
}
-}
-function resolveDefaultProps(Component, baseProps) {
- if (Component && Component.defaultProps) {
- // Resolve default props. Taken from ReactElement
- var props = assign({}, baseProps);
- var defaultProps = Component.defaultProps;
+ var showFallback = false;
+ var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags$1;
- for (var propName in defaultProps) {
- if (props[propName] === undefined) {
- props[propName] = defaultProps[propName];
+ if (didSuspend || shouldRemainOnFallback(current)) {
+ // Something in this boundary's subtree already suspended. Switch to
+ // rendering the fallback children.
+ showFallback = true;
+ workInProgress.flags &= ~DidCapture;
+ } // OK, the next part is confusing. We're about to reconcile the Suspense
+ // boundary's children. This involves some custom reconciliation logic. Two
+ // main reasons this is so complicated.
+ //
+ // First, Legacy Mode has different semantics for backwards compatibility. The
+ // primary tree will commit in an inconsistent state, so when we do the
+ // second pass to render the fallback, we do some exceedingly, uh, clever
+ // hacks to make that not totally break. Like transferring effects and
+ // deletions from hidden tree. In Concurrent Mode, it's much simpler,
+ // because we bailout on the primary tree completely and leave it in its old
+ // state, no effects. Same as what we do for Offscreen (except that
+ // Offscreen doesn't have the first render pass).
+ //
+ // Second is hydration. During hydration, the Suspense fiber has a slightly
+ // different layout, where the child points to a dehydrated fragment, which
+ // contains the DOM rendered by the server.
+ //
+ // Third, even if you set all that aside, Suspense is like error boundaries in
+ // that we first we try to render one tree, and if that fails, we render again
+ // and switch to a different tree. Like a try/catch block. So we have to track
+ // which branch we're currently rendering. Ideally we would model this using
+ // a stack.
+
+ if (current === null) {
+ // Initial mount
+ // Special path for hydration
+ // If we're currently hydrating, try to hydrate this boundary.
+ if (getIsHydrating()) {
+ // We must push the suspense handler context *before* attempting to
+ // hydrate, to avoid a mismatch in case it errors.
+ if (showFallback) {
+ pushPrimaryTreeSuspenseHandler(workInProgress);
+ } else {
+ pushFallbackTreeSuspenseHandler(workInProgress);
}
+
+ tryToClaimNextHydratableSuspenseInstance(workInProgress); // This could've been a dehydrated suspense component.
+
+ var suspenseState = workInProgress.memoizedState;
+
+ if (suspenseState !== null) {
+ var dehydrated = suspenseState.dehydrated;
+
+ if (dehydrated !== null) {
+ return mountDehydratedSuspenseComponent(workInProgress, dehydrated);
+ }
+ } // If hydration didn't succeed, fall through to the normal Suspense path.
+ // To avoid a stack mismatch we need to pop the Suspense handler that we
+ // pushed above. This will become less awkward when move the hydration
+ // logic to its own fiber.
+
+ popSuspenseHandler(workInProgress);
}
- return props;
- }
+ var nextPrimaryChildren = nextProps.children;
+ var nextFallbackChildren = nextProps.fallback;
- return baseProps;
-}
+ if (showFallback) {
+ pushFallbackTreeSuspenseHandler(workInProgress);
+ var fallbackFragment = mountSuspenseFallbackChildren(
+ workInProgress,
+ nextPrimaryChildren,
+ nextFallbackChildren,
+ renderLanes
+ );
+ var primaryChildFragment = workInProgress.child;
+ primaryChildFragment.memoizedState =
+ mountSuspenseOffscreenState(renderLanes);
+ workInProgress.memoizedState = SUSPENDED_MARKER;
-var fakeInternalInstance = {};
-var didWarnAboutStateAssignmentForComponent;
-var didWarnAboutUninitializedState;
-var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
-var didWarnAboutLegacyLifecyclesAndDerivedState;
-var didWarnAboutUndefinedDerivedState;
-var didWarnAboutDirectlyAssigningPropsToState;
-var didWarnAboutContextTypeAndContextTypes;
-var didWarnAboutInvalidateContextType;
-var didWarnOnInvalidCallback;
+ if (enableTransitionTracing) {
+ var currentTransitions = getPendingTransitions();
-{
- didWarnAboutStateAssignmentForComponent = new Set();
- didWarnAboutUninitializedState = new Set();
- didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
- didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
- didWarnAboutDirectlyAssigningPropsToState = new Set();
- didWarnAboutUndefinedDerivedState = new Set();
- didWarnAboutContextTypeAndContextTypes = new Set();
- didWarnAboutInvalidateContextType = new Set();
- didWarnOnInvalidCallback = new Set(); // 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.
+ if (currentTransitions !== null) {
+ var parentMarkerInstances = getMarkerInstances();
+ var offscreenQueue = primaryChildFragment.updateQueue;
- Object.defineProperty(fakeInternalInstance, "_processChildContext", {
- enumerable: false,
- value: function () {
- throw new Error(
- "_processChildContext is not available in React 16+. This likely " +
- "means you have multiple copies of React and are attempting to nest " +
- "a React 15 tree inside a React 16 tree using " +
- "unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
- "to make sure you have only one copy of React (and ideally, switch " +
- "to ReactDOM.createPortal)."
+ if (offscreenQueue === null) {
+ var newOffscreenQueue = {
+ transitions: currentTransitions,
+ markerInstances: parentMarkerInstances,
+ wakeables: null
+ };
+ primaryChildFragment.updateQueue = newOffscreenQueue;
+ } else {
+ offscreenQueue.transitions = currentTransitions;
+ offscreenQueue.markerInstances = parentMarkerInstances;
+ }
+ }
+ }
+
+ return fallbackFragment;
+ } else if (typeof nextProps.unstable_expectedLoadTime === "number") {
+ // This is a CPU-bound tree. Skip this tree and show a placeholder to
+ // unblock the surrounding content. Then immediately retry after the
+ // initial commit.
+ pushFallbackTreeSuspenseHandler(workInProgress);
+
+ var _fallbackFragment = mountSuspenseFallbackChildren(
+ workInProgress,
+ nextPrimaryChildren,
+ nextFallbackChildren,
+ renderLanes
);
- }
- });
- Object.freeze(fakeInternalInstance);
-}
-function warnOnInvalidCallback$1(callback, callerName) {
- {
- if (callback === null || typeof callback === "function") {
- return;
+ var _primaryChildFragment = workInProgress.child;
+ _primaryChildFragment.memoizedState =
+ mountSuspenseOffscreenState(renderLanes);
+ workInProgress.memoizedState = SUSPENDED_MARKER; // TODO: Transition Tracing is not yet implemented for CPU Suspense.
+ // Since nothing actually suspended, there will nothing to ping this to
+ // get it started back up to attempt the next item. While in terms of
+ // priority this work has the same priority as this current render, it's
+ // not part of the same transition once the transition has committed. If
+ // it's sync, we still want to yield so that it can be painted.
+ // Conceptually, this is really the same as pinging. We can use any
+ // RetryLane even if it's the one currently rendering since we're leaving
+ // it behind on this node.
+
+ workInProgress.lanes = SomeRetryLane;
+ return _fallbackFragment;
+ } else {
+ pushPrimaryTreeSuspenseHandler(workInProgress);
+ return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);
}
+ } else {
+ // This is an update.
+ // Special path for hydration
+ var prevState = current.memoizedState;
- var key = callerName + "_" + callback;
+ if (prevState !== null) {
+ var _dehydrated = prevState.dehydrated;
- if (!didWarnOnInvalidCallback.has(key)) {
- didWarnOnInvalidCallback.add(key);
+ if (_dehydrated !== null) {
+ return updateDehydratedSuspenseComponent(
+ current,
+ workInProgress,
+ didSuspend,
+ nextProps,
+ _dehydrated,
+ prevState,
+ renderLanes
+ );
+ }
+ }
- error(
- "%s(...): Expected the last optional `callback` argument to be a " +
- "function. Instead received: %s.",
- callerName,
- callback
+ if (showFallback) {
+ pushFallbackTreeSuspenseHandler(workInProgress);
+ var _nextFallbackChildren = nextProps.fallback;
+ var _nextPrimaryChildren = nextProps.children;
+ var fallbackChildFragment = updateSuspenseFallbackChildren(
+ current,
+ workInProgress,
+ _nextPrimaryChildren,
+ _nextFallbackChildren,
+ renderLanes
);
- }
- }
-}
+ var _primaryChildFragment2 = workInProgress.child;
+ var prevOffscreenState = current.child.memoizedState;
+ _primaryChildFragment2.memoizedState =
+ prevOffscreenState === null
+ ? mountSuspenseOffscreenState(renderLanes)
+ : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
-function warnOnUndefinedDerivedState(type, partialState) {
- {
- if (partialState === undefined) {
- var componentName = getComponentNameFromType(type) || "Component";
+ if (enableTransitionTracing) {
+ var _currentTransitions = getPendingTransitions();
- if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
- didWarnAboutUndefinedDerivedState.add(componentName);
+ if (_currentTransitions !== null) {
+ var _parentMarkerInstances = getMarkerInstances();
- error(
- "%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. " +
- "You have returned undefined.",
- componentName
- );
+ var _offscreenQueue = _primaryChildFragment2.updateQueue;
+ var currentOffscreenQueue = current.updateQueue;
+
+ if (_offscreenQueue === null) {
+ var _newOffscreenQueue = {
+ transitions: _currentTransitions,
+ markerInstances: _parentMarkerInstances,
+ wakeables: null
+ };
+ _primaryChildFragment2.updateQueue = _newOffscreenQueue;
+ } else if (_offscreenQueue === currentOffscreenQueue) {
+ // If the work-in-progress queue is the same object as current, we
+ // can't modify it without cloning it first.
+ var _newOffscreenQueue2 = {
+ transitions: _currentTransitions,
+ markerInstances: _parentMarkerInstances,
+ wakeables:
+ currentOffscreenQueue !== null
+ ? currentOffscreenQueue.wakeables
+ : null
+ };
+ _primaryChildFragment2.updateQueue = _newOffscreenQueue2;
+ } else {
+ _offscreenQueue.transitions = _currentTransitions;
+ _offscreenQueue.markerInstances = _parentMarkerInstances;
+ }
+ }
}
+
+ _primaryChildFragment2.childLanes = getRemainingWorkInPrimaryTree(
+ current,
+ renderLanes
+ );
+ workInProgress.memoizedState = SUSPENDED_MARKER;
+ return fallbackChildFragment;
+ } else {
+ pushPrimaryTreeSuspenseHandler(workInProgress);
+ var _nextPrimaryChildren2 = nextProps.children;
+
+ var _primaryChildFragment3 = updateSuspensePrimaryChildren(
+ current,
+ workInProgress,
+ _nextPrimaryChildren2,
+ renderLanes
+ );
+
+ workInProgress.memoizedState = null;
+ return _primaryChildFragment3;
}
}
}
-function applyDerivedStateFromProps(
+function mountSuspensePrimaryChildren(
workInProgress,
- ctor,
- getDerivedStateFromProps,
- nextProps
+ primaryChildren,
+ renderLanes
) {
- var prevState = workInProgress.memoizedState;
- var partialState = getDerivedStateFromProps(nextProps, prevState);
-
- {
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
+ var mode = workInProgress.mode;
+ var primaryChildProps = {
+ mode: "visible",
+ children: primaryChildren
+ };
+ var primaryChildFragment = mountWorkInProgressOffscreenFiber(
+ primaryChildProps,
+ mode
+ );
+ primaryChildFragment.return = workInProgress;
+ workInProgress.child = primaryChildFragment;
+ return primaryChildFragment;
+}
- try {
- // Invoke the function an extra time to help detect side-effects.
- partialState = getDerivedStateFromProps(nextProps, prevState);
- } finally {
- setIsStrictModeForDevtools(false);
- }
- }
+function mountSuspenseFallbackChildren(
+ workInProgress,
+ primaryChildren,
+ fallbackChildren,
+ renderLanes
+) {
+ var mode = workInProgress.mode;
+ var progressedPrimaryFragment = workInProgress.child;
+ var primaryChildProps = {
+ mode: "hidden",
+ children: primaryChildren
+ };
+ var primaryChildFragment;
+ var fallbackChildFragment;
- warnOnUndefinedDerivedState(ctor, partialState);
- } // Merge the partial state and the previous state.
+ if (
+ (mode & ConcurrentMode) === NoMode &&
+ progressedPrimaryFragment !== null
+ ) {
+ // In legacy mode, we commit the primary tree as if it successfully
+ // completed, even though it's in an inconsistent state.
+ primaryChildFragment = progressedPrimaryFragment;
+ primaryChildFragment.childLanes = NoLanes;
+ primaryChildFragment.pendingProps = primaryChildProps;
- var memoizedState =
- partialState === null || partialState === undefined
- ? prevState
- : assign({}, prevState, partialState);
- workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the
- // base state.
+ if (workInProgress.mode & ProfileMode) {
+ // Reset the durations from the first pass so they aren't included in the
+ // final amounts. This seems counterintuitive, since we're intentionally
+ // not measuring part of the render phase, but this makes it match what we
+ // do in Concurrent Mode.
+ primaryChildFragment.actualDuration = 0;
+ primaryChildFragment.actualStartTime = -1;
+ primaryChildFragment.selfBaseDuration = 0;
+ primaryChildFragment.treeBaseDuration = 0;
+ }
- if (workInProgress.lanes === NoLanes) {
- // Queue is always non-null for classes
- var updateQueue = workInProgress.updateQueue;
- updateQueue.baseState = memoizedState;
+ fallbackChildFragment = createFiberFromFragment(
+ fallbackChildren,
+ mode,
+ renderLanes,
+ null
+ );
+ } else {
+ primaryChildFragment = mountWorkInProgressOffscreenFiber(
+ primaryChildProps,
+ mode
+ );
+ fallbackChildFragment = createFiberFromFragment(
+ fallbackChildren,
+ mode,
+ renderLanes,
+ null
+ );
}
-}
-
-var classComponentUpdater = {
- isMounted: isMounted,
- // $FlowFixMe[missing-local-annot]
- enqueueSetState: function (inst, payload, callback) {
- var fiber = get(inst);
- var lane = requestUpdateLane(fiber);
- var update = createUpdate(lane);
- update.payload = payload;
-
- if (callback !== undefined && callback !== null) {
- {
- warnOnInvalidCallback$1(callback, "setState");
- }
- update.callback = callback;
- }
+ primaryChildFragment.return = workInProgress;
+ fallbackChildFragment.return = workInProgress;
+ primaryChildFragment.sibling = fallbackChildFragment;
+ workInProgress.child = primaryChildFragment;
+ return fallbackChildFragment;
+}
- var root = enqueueUpdate(fiber, update, lane);
+function mountWorkInProgressOffscreenFiber(offscreenProps, mode, renderLanes) {
+ // The props argument to `createFiberFromOffscreen` is `any` typed, so we use
+ // this wrapper function to constrain it.
+ return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null);
+}
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- entangleTransitions(root, fiber, lane);
- }
+function updateWorkInProgressOffscreenFiber(current, offscreenProps) {
+ // The props argument to `createWorkInProgress` is `any` typed, so we use this
+ // wrapper function to constrain it.
+ return createWorkInProgress(current, offscreenProps);
+}
+function updateSuspensePrimaryChildren(
+ current,
+ workInProgress,
+ primaryChildren,
+ renderLanes
+) {
+ var currentPrimaryChildFragment = current.child;
+ var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
+ var primaryChildFragment = updateWorkInProgressOffscreenFiber(
+ currentPrimaryChildFragment,
{
- if (enableDebugTracing) {
- if (fiber.mode & DebugTracingMode) {
- var name = getComponentNameFromFiber(fiber) || "Unknown";
- logStateUpdateScheduled(name, lane, payload);
- }
- }
- }
-
- if (enableSchedulingProfiler) {
- markStateUpdateScheduled(fiber, lane);
+ mode: "visible",
+ children: primaryChildren
}
- },
- enqueueReplaceState: function (inst, payload, callback) {
- var fiber = get(inst);
- var lane = requestUpdateLane(fiber);
- var update = createUpdate(lane);
- update.tag = ReplaceState;
- update.payload = payload;
+ );
- if (callback !== undefined && callback !== null) {
- {
- warnOnInvalidCallback$1(callback, "replaceState");
- }
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ primaryChildFragment.lanes = renderLanes;
+ }
- update.callback = callback;
- }
+ primaryChildFragment.return = workInProgress;
+ primaryChildFragment.sibling = null;
- var root = enqueueUpdate(fiber, update, lane);
+ if (currentFallbackChildFragment !== null) {
+ // Delete the fallback child fragment
+ var deletions = workInProgress.deletions;
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- entangleTransitions(root, fiber, lane);
+ if (deletions === null) {
+ workInProgress.deletions = [currentFallbackChildFragment];
+ workInProgress.flags |= ChildDeletion;
+ } else {
+ deletions.push(currentFallbackChildFragment);
}
+ }
- {
- if (enableDebugTracing) {
- if (fiber.mode & DebugTracingMode) {
- var name = getComponentNameFromFiber(fiber) || "Unknown";
- logStateUpdateScheduled(name, lane, payload);
- }
- }
- }
+ workInProgress.child = primaryChildFragment;
+ return primaryChildFragment;
+}
- if (enableSchedulingProfiler) {
- markStateUpdateScheduled(fiber, lane);
- }
- },
- // $FlowFixMe[missing-local-annot]
- enqueueForceUpdate: function (inst, callback) {
- var fiber = get(inst);
- var lane = requestUpdateLane(fiber);
- var update = createUpdate(lane);
- update.tag = ForceUpdate;
+function updateSuspenseFallbackChildren(
+ current,
+ workInProgress,
+ primaryChildren,
+ fallbackChildren,
+ renderLanes
+) {
+ var mode = workInProgress.mode;
+ var currentPrimaryChildFragment = current.child;
+ var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
+ var primaryChildProps = {
+ mode: "hidden",
+ children: primaryChildren
+ };
+ var primaryChildFragment;
- if (callback !== undefined && callback !== null) {
- {
- warnOnInvalidCallback$1(callback, "forceUpdate");
- }
+ if (
+ // In legacy mode, we commit the primary tree as if it successfully
+ // completed, even though it's in an inconsistent state.
+ (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was
+ // already cloned. In legacy mode, the only case where this isn't true is
+ // when DevTools forces us to display a fallback; we skip the first render
+ // pass entirely and go straight to rendering the fallback. (In Concurrent
+ // Mode, SuspenseList can also trigger this scenario, but this is a legacy-
+ // only codepath.)
+ workInProgress.child !== currentPrimaryChildFragment
+ ) {
+ var progressedPrimaryFragment = workInProgress.child;
+ primaryChildFragment = progressedPrimaryFragment;
+ primaryChildFragment.childLanes = NoLanes;
+ primaryChildFragment.pendingProps = primaryChildProps;
- update.callback = callback;
- }
+ if (workInProgress.mode & ProfileMode) {
+ // Reset the durations from the first pass so they aren't included in the
+ // final amounts. This seems counterintuitive, since we're intentionally
+ // not measuring part of the render phase, but this makes it match what we
+ // do in Concurrent Mode.
+ primaryChildFragment.actualDuration = 0;
+ primaryChildFragment.actualStartTime = -1;
+ primaryChildFragment.selfBaseDuration =
+ currentPrimaryChildFragment.selfBaseDuration;
+ primaryChildFragment.treeBaseDuration =
+ currentPrimaryChildFragment.treeBaseDuration;
+ } // The fallback fiber was added as a deletion during the first pass.
+ // However, since we're going to remain on the fallback, we no longer want
+ // to delete it.
- var root = enqueueUpdate(fiber, update, lane);
+ workInProgress.deletions = null;
+ } else {
+ primaryChildFragment = updateWorkInProgressOffscreenFiber(
+ currentPrimaryChildFragment,
+ primaryChildProps
+ ); // Since we're reusing a current tree, we need to reuse the flags, too.
+ // (We don't do this in legacy mode, because in legacy mode we don't re-use
+ // the current tree; see previous branch.)
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- entangleTransitions(root, fiber, lane);
- }
+ primaryChildFragment.subtreeFlags =
+ currentPrimaryChildFragment.subtreeFlags & StaticMask;
+ }
- {
- if (enableDebugTracing) {
- if (fiber.mode & DebugTracingMode) {
- var name = getComponentNameFromFiber(fiber) || "Unknown";
- logForceUpdateScheduled(name, lane);
- }
- }
- }
+ var fallbackChildFragment;
- if (enableSchedulingProfiler) {
- markForceUpdateScheduled(fiber, lane);
- }
+ if (currentFallbackChildFragment !== null) {
+ fallbackChildFragment = createWorkInProgress(
+ currentFallbackChildFragment,
+ fallbackChildren
+ );
+ } else {
+ fallbackChildFragment = createFiberFromFragment(
+ fallbackChildren,
+ mode,
+ renderLanes,
+ null
+ ); // Needs a placement effect because the parent (the Suspense boundary) already
+ // mounted but this is a new fiber.
+
+ fallbackChildFragment.flags |= Placement;
}
-};
-function checkShouldComponentUpdate(
+ fallbackChildFragment.return = workInProgress;
+ primaryChildFragment.return = workInProgress;
+ primaryChildFragment.sibling = fallbackChildFragment;
+ workInProgress.child = primaryChildFragment;
+ return fallbackChildFragment;
+}
+
+function retrySuspenseComponentWithoutHydrating(
+ current,
workInProgress,
- ctor,
- oldProps,
- newProps,
- oldState,
- newState,
- nextContext
+ renderLanes,
+ recoverableError
) {
- var instance = workInProgress.stateNode;
+ // Falling back to client rendering. Because this has performance
+ // implications, it's considered a recoverable error, even though the user
+ // likely won't observe anything wrong with the UI.
+ //
+ // The error is passed in as an argument to enforce that every caller provide
+ // a custom message, or explicitly opt out (currently the only path that opts
+ // out is legacy mode; every concurrent path provides an error).
+ if (recoverableError !== null) {
+ queueHydrationError(recoverableError);
+ } // This will add the old fiber to the deletion list
- if (typeof instance.shouldComponentUpdate === "function") {
- var shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- nextContext
- );
+ reconcileChildFibers(workInProgress, current.child, null, renderLanes); // We're now not suspended nor dehydrated.
- {
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
+ var nextProps = workInProgress.pendingProps;
+ var primaryChildren = nextProps.children;
+ var primaryChildFragment = mountSuspensePrimaryChildren(
+ workInProgress,
+ primaryChildren
+ ); // Needs a placement effect because the parent (the Suspense boundary) already
+ // mounted but this is a new fiber.
- try {
- // Invoke the function an extra time to help detect side-effects.
- shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- nextContext
- );
- } finally {
- setIsStrictModeForDevtools(false);
- }
- }
+ primaryChildFragment.flags |= Placement;
+ workInProgress.memoizedState = null;
+ return primaryChildFragment;
+}
- if (shouldUpdate === undefined) {
- error(
- "%s.shouldComponentUpdate(): Returned undefined instead of a " +
- "boolean value. Make sure to return true or false.",
- getComponentNameFromType(ctor) || "Component"
- );
- }
- }
+function mountSuspenseFallbackAfterRetryWithoutHydrating(
+ current,
+ workInProgress,
+ primaryChildren,
+ fallbackChildren,
+ renderLanes
+) {
+ var fiberMode = workInProgress.mode;
+ var primaryChildProps = {
+ mode: "visible",
+ children: primaryChildren
+ };
+ var primaryChildFragment = mountWorkInProgressOffscreenFiber(
+ primaryChildProps,
+ fiberMode
+ );
+ var fallbackChildFragment = createFiberFromFragment(
+ fallbackChildren,
+ fiberMode,
+ renderLanes,
+ null
+ ); // Needs a placement effect because the parent (the Suspense
+ // boundary) already mounted but this is a new fiber.
- return shouldUpdate;
- }
+ fallbackChildFragment.flags |= Placement;
+ primaryChildFragment.return = workInProgress;
+ fallbackChildFragment.return = workInProgress;
+ primaryChildFragment.sibling = fallbackChildFragment;
+ workInProgress.child = primaryChildFragment;
- if (ctor.prototype && ctor.prototype.isPureReactComponent) {
- return (
- !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
- );
+ if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
+ // We will have dropped the effect list which contains the
+ // deletion. We need to reconcile to delete the current child.
+ reconcileChildFibers(workInProgress, current.child, null, renderLanes);
}
- return true;
+ return fallbackChildFragment;
}
-function checkClassInstance(workInProgress, ctor, newProps) {
- var instance = workInProgress.stateNode;
-
- {
- var name = getComponentNameFromType(ctor) || "Component";
- var renderPresent = instance.render;
-
- if (!renderPresent) {
- if (ctor.prototype && typeof ctor.prototype.render === "function") {
- error(
- "%s(...): No `render` method found on the returned component " +
- "instance: did you accidentally return an object from the constructor?",
- name
- );
- } else {
- error(
- "%s(...): No `render` method found on the returned component " +
- "instance: you may have forgotten to define `render`.",
- name
- );
- }
- }
-
- if (
- instance.getInitialState &&
- !instance.getInitialState.isReactClassApproved &&
- !instance.state
- ) {
+function mountDehydratedSuspenseComponent(
+ workInProgress,
+ suspenseInstance,
+ renderLanes
+) {
+ // During the first pass, we'll bail out and not drill into the children.
+ // Instead, we'll leave the content in place and try to hydrate it later.
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ {
error(
- "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
+ "Cannot hydrate Suspense in legacy mode. Switch from " +
+ "ReactDOM.hydrate(element, container) to " +
+ "ReactDOMClient.hydrateRoot(container, )" +
+ ".render(element) or remove the Suspense components from " +
+ "the server rendered components."
);
}
- if (
- instance.getDefaultProps &&
- !instance.getDefaultProps.isReactClassApproved
- ) {
- error(
- "getDefaultProps was defined on %s, a plain JavaScript class. " +
- "This is only supported for classes created using React.createClass. " +
- "Use a static property to define defaultProps instead.",
- name
- );
- }
+ workInProgress.lanes = laneToLanes(SyncLane);
+ } else if (isSuspenseInstanceFallback(suspenseInstance)) {
+ // This is a client-only boundary. Since we won't get any content from the server
+ // for this, we need to schedule that at a higher priority based on when it would
+ // have timed out. In theory we could render it in this pass but it would have the
+ // wrong priority associated with it and will prevent hydration of parent path.
+ // Instead, we'll leave work left on it to render it in a separate commit.
+ // TODO This time should be the time at which the server rendered response that is
+ // a parent to this boundary was displayed. However, since we currently don't have
+ // a protocol to transfer that time, we'll just estimate it by using the current
+ // time. This will mean that Suspense timeouts are slightly shifted to later than
+ // they should be.
+ // Schedule a normal pri update to render this content.
+ workInProgress.lanes = laneToLanes(DefaultHydrationLane);
+ } else {
+ // We'll continue hydrating the rest at offscreen priority since we'll already
+ // be showing the right content coming from the server, it is no rush.
+ workInProgress.lanes = laneToLanes(OffscreenLane);
+ }
- if (instance.propTypes) {
- error(
- "propTypes was defined as an instance property on %s. Use a static " +
- "property to define propTypes instead.",
- name
- );
- }
+ return null;
+}
- if (instance.contextType) {
- error(
- "contextType was defined as an instance property on %s. Use a static " +
- "property to define contextType instead.",
- name
+function updateDehydratedSuspenseComponent(
+ current,
+ workInProgress,
+ didSuspend,
+ nextProps,
+ suspenseInstance,
+ suspenseState,
+ renderLanes
+) {
+ if (!didSuspend) {
+ // This is the first render pass. Attempt to hydrate.
+ pushPrimaryTreeSuspenseHandler(workInProgress); // We should never be hydrating at this point because it is the first pass,
+ // but after we've already committed once.
+
+ warnIfHydrating();
+
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ return retrySuspenseComponentWithoutHydrating(
+ current,
+ workInProgress,
+ renderLanes,
+ null
);
}
- {
- if (instance.contextTypes) {
- error(
- "contextTypes was defined as an instance property on %s. Use a static " +
- "property to define contextTypes instead.",
- name
- );
+ if (isSuspenseInstanceFallback(suspenseInstance)) {
+ // This boundary is in a permanent fallback state. In this case, we'll never
+ // get an update and we'll never be able to hydrate the final content. Let's just try the
+ // client side render instead.
+ var digest, message, stack;
+
+ {
+ var _getSuspenseInstanceF =
+ getSuspenseInstanceFallbackErrorDetails(suspenseInstance);
+
+ digest = _getSuspenseInstanceF.digest;
+ message = _getSuspenseInstanceF.message;
+ stack = _getSuspenseInstanceF.stack;
}
- if (
- ctor.contextType &&
- ctor.contextTypes &&
- !didWarnAboutContextTypeAndContextTypes.has(ctor)
- ) {
- didWarnAboutContextTypeAndContextTypes.add(ctor);
+ var error;
- error(
- "%s declares both contextTypes and contextType static properties. " +
- "The legacy contextTypes property will be ignored.",
- name
+ if (message) {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ error = new Error(message);
+ } else {
+ error = new Error(
+ "The server could not finish this Suspense boundary, likely " +
+ "due to an error during server rendering. Switched to " +
+ "client rendering."
);
}
- }
- if (typeof instance.componentShouldUpdate === "function") {
- error(
- "%s has a method called " +
- "componentShouldUpdate(). Did you mean shouldComponentUpdate()? " +
- "The name is phrased as a question because the function is " +
- "expected to return a value.",
- name
+ error.digest = digest;
+ var capturedValue = createCapturedValue(error, digest, stack);
+ return retrySuspenseComponentWithoutHydrating(
+ current,
+ workInProgress,
+ renderLanes,
+ capturedValue
);
}
if (
- ctor.prototype &&
- ctor.prototype.isPureReactComponent &&
- typeof instance.shouldComponentUpdate !== "undefined"
+ enableLazyContextPropagation && // TODO: Factoring is a little weird, since we check this right below, too.
+ // But don't want to re-arrange the if-else chain until/unless this
+ // feature lands.
+ !didReceiveUpdate
) {
- error(
- "%s has a method called shouldComponentUpdate(). " +
- "shouldComponentUpdate should not be used when extending React.PureComponent. " +
- "Please extend React.Component if shouldComponentUpdate is used.",
- getComponentNameFromType(ctor) || "A pure component"
- );
- }
+ // We need to check if any children have context before we decide to bail
+ // out, so propagate the changes now.
+ lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);
+ } // We use lanes to indicate that a child might depend on context, so if
+ // any context has changed, we need to treat is as if the input might have changed.
- if (typeof instance.componentDidUnmount === "function") {
- error(
- "%s has a method called " +
- "componentDidUnmount(). But there is no such lifecycle method. " +
- "Did you mean componentWillUnmount()?",
- name
- );
- }
+ var hasContextChanged = includesSomeLane(renderLanes, current.childLanes);
- if (typeof instance.componentDidReceiveProps === "function") {
- error(
- "%s has a method called " +
- "componentDidReceiveProps(). But there is no such lifecycle method. " +
- "If you meant to update the state in response to changing props, " +
- "use componentWillReceiveProps(). If you meant to fetch data or " +
- "run side-effects or mutations after React has updated the UI, use componentDidUpdate().",
- name
- );
- }
+ if (didReceiveUpdate || hasContextChanged) {
+ // This boundary has changed since the first render. This means that we are now unable to
+ // hydrate it. We might still be able to hydrate it using a higher priority lane.
+ var root = getWorkInProgressRoot();
- if (typeof instance.componentWillRecieveProps === "function") {
- error(
- "%s has a method called " +
- "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",
- name
- );
- }
+ if (root !== null) {
+ var attemptHydrationAtLane = getBumpedLaneForHydration(
+ root,
+ renderLanes
+ );
- if (typeof instance.UNSAFE_componentWillRecieveProps === "function") {
- error(
- "%s has a method called " +
- "UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?",
- name
- );
- }
+ if (
+ attemptHydrationAtLane !== NoLane &&
+ attemptHydrationAtLane !== suspenseState.retryLane
+ ) {
+ // Intentionally mutating since this render will get interrupted. This
+ // is one of the very rare times where we mutate the current tree
+ // during the render phase.
+ suspenseState.retryLane = attemptHydrationAtLane; // TODO: Ideally this would inherit the event time of the current render
- var hasMutatedProps = instance.props !== newProps;
+ var eventTime = NoTimestamp;
+ enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
+ scheduleUpdateOnFiber(
+ root,
+ current,
+ attemptHydrationAtLane,
+ eventTime
+ ); // Throw a special object that signals to the work loop that it should
+ // interrupt the current render.
+ //
+ // Because we're inside a React-only execution stack, we don't
+ // strictly need to throw here — we could instead modify some internal
+ // work loop state. But using an exception means we don't need to
+ // check for this case on every iteration of the work loop. So doing
+ // it this way moves the check out of the fast path.
- if (instance.props !== undefined && hasMutatedProps) {
- error(
- "%s(...): When calling super() in `%s`, make sure to pass " +
- "up the same props that your component's constructor was passed.",
- name,
- name
- );
- }
+ throw SelectiveHydrationException;
+ }
+ } // If we did not selectively hydrate, we'll continue rendering without
+ // hydrating. Mark this tree as suspended to prevent it from committing
+ // outside a transition.
+ //
+ // This path should only happen if the hydration lane already suspended.
+ // Currently, it also happens during sync updates because there is no
+ // hydration lane for sync updates.
+ // TODO: We should ideally have a sync hydration lane that we can apply to do
+ // a pass where we hydrate this subtree in place using the previous Context and then
+ // reapply the update afterwards.
- if (instance.defaultProps) {
- error(
- "Setting defaultProps as an instance property on %s is not supported and will be ignored." +
- " Instead, define defaultProps as a static property on %s.",
- name,
- name
+ renderDidSuspendDelayIfPossible();
+ return retrySuspenseComponentWithoutHydrating(
+ current,
+ workInProgress,
+ renderLanes,
+ null
);
- }
-
- if (
- typeof instance.getSnapshotBeforeUpdate === "function" &&
- typeof instance.componentDidUpdate !== "function" &&
- !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)
- ) {
- didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
+ } else if (isSuspenseInstancePending(suspenseInstance)) {
+ // This component is still pending more data from the server, so we can't hydrate its
+ // content. We treat it as if this component suspended itself. It might seem as if
+ // we could just try to render it client-side instead. However, this will perform a
+ // lot of unnecessary work and is unlikely to complete since it often will suspend
+ // on missing data anyway. Additionally, the server might be able to render more
+ // than we can on the client yet. In that case we'd end up with more fallback states
+ // on the client than if we just leave it alone. If the server times out or errors
+ // these should update this boundary to the permanent Fallback state instead.
+ // Mark it as having captured (i.e. suspended).
+ workInProgress.flags |= DidCapture; // Leave the child in place. I.e. the dehydrated fragment.
- error(
- "%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). " +
- "This component defines getSnapshotBeforeUpdate() only.",
- getComponentNameFromType(ctor)
- );
- }
+ workInProgress.child = current.child; // Register a callback to retry this boundary once the server has sent the result.
- if (typeof instance.getDerivedStateFromProps === "function") {
- error(
- "%s: getDerivedStateFromProps() is defined as an instance method " +
- "and will be ignored. Instead, declare it as a static method.",
- name
+ var retry = retryDehydratedSuspenseBoundary.bind(null, current);
+ registerSuspenseInstanceRetry(suspenseInstance, retry);
+ return null;
+ } else {
+ // This is the first attempt.
+ reenterHydrationStateFromDehydratedSuspenseInstance(
+ workInProgress,
+ suspenseInstance,
+ suspenseState.treeContext
);
- }
+ var primaryChildren = nextProps.children;
+ var primaryChildFragment = mountSuspensePrimaryChildren(
+ workInProgress,
+ primaryChildren
+ ); // Mark the children as hydrating. This is a fast path to know whether this
+ // tree is part of a hydrating tree. This is used to determine if a child
+ // node has fully mounted yet, and for scheduling event replaying.
+ // Conceptually this is similar to Placement in that a new subtree is
+ // inserted into the React tree here. It just happens to not need DOM
+ // mutations because it already exists.
- if (typeof instance.getDerivedStateFromError === "function") {
- error(
- "%s: getDerivedStateFromError() is defined as an instance method " +
- "and will be ignored. Instead, declare it as a static method.",
- name
- );
+ primaryChildFragment.flags |= Hydrating;
+ return primaryChildFragment;
}
+ } else {
+ // This is the second render pass. We already attempted to hydrated, but
+ // something either suspended or errored.
+ if (workInProgress.flags & ForceClientRender) {
+ // Something errored during hydration. Try again without hydrating.
+ pushPrimaryTreeSuspenseHandler(workInProgress);
+ workInProgress.flags &= ~ForceClientRender;
- if (typeof ctor.getSnapshotBeforeUpdate === "function") {
- error(
- "%s: getSnapshotBeforeUpdate() is defined as a static method " +
- "and will be ignored. Instead, declare it as an instance method.",
- name
+ var _capturedValue = createCapturedValue(
+ new Error(
+ "There was an error while hydrating this Suspense boundary. " +
+ "Switched to client rendering."
+ )
);
- }
-
- var state = instance.state;
-
- if (state && (typeof state !== "object" || isArray(state))) {
- error("%s.state: must be set to an object or null", name);
- }
- if (
- typeof instance.getChildContext === "function" &&
- typeof ctor.childContextTypes !== "object"
- ) {
- error(
- "%s.getChildContext(): childContextTypes must be defined in order to " +
- "use getChildContext().",
- name
+ return retrySuspenseComponentWithoutHydrating(
+ current,
+ workInProgress,
+ renderLanes,
+ _capturedValue
);
- }
- }
-}
-
-function adoptClassInstance(workInProgress, instance) {
- instance.updater = classComponentUpdater;
- workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates
-
- set(instance, workInProgress);
-
- {
- instance._reactInternalInstance = fakeInternalInstance;
- }
-}
-
-function constructClassInstance(workInProgress, ctor, props) {
- var isLegacyContextConsumer = false;
- var unmaskedContext = emptyContextObject;
- var context = emptyContextObject;
- var contextType = ctor.contextType;
-
- {
- if ("contextType" in ctor) {
- var isValid = // Allow null for conditional declaration
- contextType === null ||
- (contextType !== undefined &&
- contextType.$$typeof === REACT_CONTEXT_TYPE &&
- contextType._context === undefined); // Not a
-
- if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
- didWarnAboutInvalidateContextType.add(ctor);
- var addendum = "";
-
- if (contextType === undefined) {
- addendum =
- " However, it is set to undefined. " +
- "This can be caused by a typo or by mixing up named and default imports. " +
- "This can also happen due to a circular dependency, so " +
- "try moving the createContext() call to a separate file.";
- } else if (typeof contextType !== "object") {
- addendum = " However, it is set to a " + typeof contextType + ".";
- } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
- addendum = " Did you accidentally pass the Context.Provider instead?";
- } else if (contextType._context !== undefined) {
- //
- addendum = " Did you accidentally pass the Context.Consumer instead?";
- } else {
- addendum =
- " However, it is set to an object with keys {" +
- Object.keys(contextType).join(", ") +
- "}.";
- }
+ } else if (workInProgress.memoizedState !== null) {
+ // Something suspended and we should still be in dehydrated mode.
+ // Leave the existing child in place.
+ // Push to avoid a mismatch
+ pushFallbackTreeSuspenseHandler(workInProgress);
+ workInProgress.child = current.child; // The dehydrated completion pass expects this flag to be there
+ // but the normal suspense pass doesn't.
- error(
- "%s defines an invalid contextType. " +
- "contextType should point to the Context object returned by React.createContext().%s",
- getComponentNameFromType(ctor) || "Component",
- addendum
+ workInProgress.flags |= DidCapture;
+ return null;
+ } else {
+ // Suspended but we should no longer be in dehydrated mode.
+ // Therefore we now have to render the fallback.
+ pushFallbackTreeSuspenseHandler(workInProgress);
+ var nextPrimaryChildren = nextProps.children;
+ var nextFallbackChildren = nextProps.fallback;
+ var fallbackChildFragment =
+ mountSuspenseFallbackAfterRetryWithoutHydrating(
+ current,
+ workInProgress,
+ nextPrimaryChildren,
+ nextFallbackChildren,
+ renderLanes
);
- }
- }
- }
-
- if (typeof contextType === "object" && contextType !== null) {
- context = readContext(contextType);
- } else {
- unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
- var contextTypes = ctor.contextTypes;
- isLegacyContextConsumer =
- contextTypes !== null && contextTypes !== undefined;
- context = isLegacyContextConsumer
- ? getMaskedContext(workInProgress, unmaskedContext)
- : emptyContextObject;
- }
-
- var instance = new ctor(props, context); // Instantiate twice to help detect side-effects.
-
- {
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
-
- try {
- instance = new ctor(props, context); // eslint-disable-line no-new
- } finally {
- setIsStrictModeForDevtools(false);
- }
+ var _primaryChildFragment4 = workInProgress.child;
+ _primaryChildFragment4.memoizedState =
+ mountSuspenseOffscreenState(renderLanes);
+ workInProgress.memoizedState = SUSPENDED_MARKER;
+ return fallbackChildFragment;
}
}
+}
- var state = (workInProgress.memoizedState =
- instance.state !== null && instance.state !== undefined
- ? instance.state
- : null);
- adoptClassInstance(workInProgress, instance);
+function scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {
+ fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
+ var alternate = fiber.alternate;
- {
- if (typeof ctor.getDerivedStateFromProps === "function" && state === null) {
- var componentName = getComponentNameFromType(ctor) || "Component";
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
+ }
- if (!didWarnAboutUninitializedState.has(componentName)) {
- didWarnAboutUninitializedState.add(componentName);
+ scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);
+}
- error(
- "`%s` uses `getDerivedStateFromProps` but its initial state is " +
- "%s. This is not recommended. Instead, define the initial state by " +
- "assigning an object to `this.state` in the constructor of `%s`. " +
- "This ensures that `getDerivedStateFromProps` arguments have a consistent shape.",
- componentName,
- instance.state === null ? "null" : "undefined",
- componentName
- );
- }
- } // If new component APIs are defined, "unsafe" lifecycles won't be called.
- // Warn about these lifecycles if they are present.
- // Don't warn about react-lifecycles-compat polyfilled methods though.
+function propagateSuspenseContextChange(
+ workInProgress,
+ firstChild,
+ renderLanes
+) {
+ // Mark any Suspense boundaries with fallbacks as having work to do.
+ // If they were previously forced into fallbacks, they may now be able
+ // to unblock.
+ var node = firstChild;
- if (
- typeof ctor.getDerivedStateFromProps === "function" ||
- typeof instance.getSnapshotBeforeUpdate === "function"
- ) {
- var foundWillMountName = null;
- var foundWillReceivePropsName = null;
- var foundWillUpdateName = null;
+ while (node !== null) {
+ if (node.tag === SuspenseComponent) {
+ var state = node.memoizedState;
- if (
- typeof instance.componentWillMount === "function" &&
- instance.componentWillMount.__suppressDeprecationWarning !== true
- ) {
- foundWillMountName = "componentWillMount";
- } else if (typeof instance.UNSAFE_componentWillMount === "function") {
- foundWillMountName = "UNSAFE_componentWillMount";
+ if (state !== null) {
+ scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
}
+ } else if (node.tag === SuspenseListComponent) {
+ // If the tail is hidden there might not be an Suspense boundaries
+ // to schedule work on. In this case we have to schedule it on the
+ // list itself.
+ // We don't have to traverse to the children of the list since
+ // the list will propagate the change when it rerenders.
+ scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
- if (
- typeof instance.componentWillReceiveProps === "function" &&
- instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
- ) {
- foundWillReceivePropsName = "componentWillReceiveProps";
- } else if (
- typeof instance.UNSAFE_componentWillReceiveProps === "function"
- ) {
- foundWillReceivePropsName = "UNSAFE_componentWillReceiveProps";
- }
+ if (node === workInProgress) {
+ return;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (
- typeof instance.componentWillUpdate === "function" &&
- instance.componentWillUpdate.__suppressDeprecationWarning !== true
- ) {
- foundWillUpdateName = "componentWillUpdate";
- } else if (typeof instance.UNSAFE_componentWillUpdate === "function") {
- foundWillUpdateName = "UNSAFE_componentWillUpdate";
+ while (node.sibling === null) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (node.return === null || node.return === workInProgress) {
+ return;
}
- if (
- foundWillMountName !== null ||
- foundWillReceivePropsName !== null ||
- foundWillUpdateName !== null
- ) {
- var _componentName = getComponentNameFromType(ctor) || "Component";
-
- var newApiName =
- typeof ctor.getDerivedStateFromProps === "function"
- ? "getDerivedStateFromProps()"
- : "getSnapshotBeforeUpdate()";
-
- if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) {
- didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName);
-
- error(
- "Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n" +
- "%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n" +
- "The above lifecycles should be removed. Learn more about this warning here:\n" +
- "https://reactjs.org/link/unsafe-component-lifecycles",
- _componentName,
- newApiName,
- foundWillMountName !== null ? "\n " + foundWillMountName : "",
- foundWillReceivePropsName !== null
- ? "\n " + foundWillReceivePropsName
- : "",
- foundWillUpdateName !== null ? "\n " + foundWillUpdateName : ""
- );
- }
- }
- }
- } // Cache unmasked context so we can avoid recreating masked context unless necessary.
- // ReactFiberContext usually updates this cache but can't for newly-created instances.
+ node = node.return;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (isLegacyContextConsumer) {
- cacheContext(workInProgress, unmaskedContext, context);
+ node.sibling.return = node.return;
+ node = node.sibling;
}
-
- return instance;
}
-function callComponentWillMount(workInProgress, instance) {
- var oldState = instance.state;
-
- if (typeof instance.componentWillMount === "function") {
- instance.componentWillMount();
- }
+function findLastContentRow(firstChild) {
+ // This is going to find the last row among these children that is already
+ // showing content on the screen, as opposed to being in fallback state or
+ // new. If a row has multiple Suspense boundaries, any of them being in the
+ // fallback state, counts as the whole row being in a fallback state.
+ // Note that the "rows" will be workInProgress, but any nested children
+ // will still be current since we haven't rendered them yet. The mounted
+ // order may not be the same as the new order. We use the new order.
+ var row = firstChild;
+ var lastContentRow = null;
- if (typeof instance.UNSAFE_componentWillMount === "function") {
- instance.UNSAFE_componentWillMount();
- }
+ while (row !== null) {
+ var currentRow = row.alternate; // New rows can't be content rows.
- if (oldState !== instance.state) {
- {
- error(
- "%s.componentWillMount(): Assigning directly to this.state is " +
- "deprecated (except inside a component's " +
- "constructor). Use setState instead.",
- getComponentNameFromFiber(workInProgress) || "Component"
- );
+ if (currentRow !== null && findFirstSuspended(currentRow) === null) {
+ lastContentRow = row;
}
- classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
+ row = row.sibling;
}
+
+ return lastContentRow;
}
-function callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- nextContext
-) {
- var oldState = instance.state;
+function validateRevealOrder(revealOrder) {
+ {
+ if (
+ revealOrder !== undefined &&
+ revealOrder !== "forwards" &&
+ revealOrder !== "backwards" &&
+ revealOrder !== "together" &&
+ !didWarnAboutRevealOrder[revealOrder]
+ ) {
+ didWarnAboutRevealOrder[revealOrder] = true;
- if (typeof instance.componentWillReceiveProps === "function") {
- instance.componentWillReceiveProps(newProps, nextContext);
- }
+ if (typeof revealOrder === "string") {
+ switch (revealOrder.toLowerCase()) {
+ case "together":
+ case "forwards":
+ case "backwards": {
+ error(
+ '"%s" is not a valid value for revealOrder on . ' +
+ 'Use lowercase "%s" instead.',
+ revealOrder,
+ revealOrder.toLowerCase()
+ );
- if (typeof instance.UNSAFE_componentWillReceiveProps === "function") {
- instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
- }
+ break;
+ }
- if (instance.state !== oldState) {
- {
- var componentName =
- getComponentNameFromFiber(workInProgress) || "Component";
+ case "forward":
+ case "backward": {
+ error(
+ '"%s" is not a valid value for revealOrder on . ' +
+ 'React uses the -s suffix in the spelling. Use "%ss" instead.',
+ revealOrder,
+ revealOrder.toLowerCase()
+ );
- if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
- didWarnAboutStateAssignmentForComponent.add(componentName);
+ break;
+ }
+
+ default:
+ error(
+ '"%s" is not a supported revealOrder on . ' +
+ 'Did you mean "together", "forwards" or "backwards"?',
+ revealOrder
+ );
+ break;
+ }
+ } else {
error(
- "%s.componentWillReceiveProps(): Assigning directly to " +
- "this.state is deprecated (except inside a component's " +
- "constructor). Use setState instead.",
- componentName
+ "%s is not a supported value for revealOrder on . " +
+ 'Did you mean "together", "forwards" or "backwards"?',
+ revealOrder
);
}
}
-
- classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
- }
-} // Invokes the mount life-cycles on a previously never rendered instance.
-
-function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
- {
- checkClassInstance(workInProgress, ctor, newProps);
- }
-
- var instance = workInProgress.stateNode;
- instance.props = newProps;
- instance.state = workInProgress.memoizedState;
- instance.refs = {};
- initializeUpdateQueue(workInProgress);
- var contextType = ctor.contextType;
-
- if (typeof contextType === "object" && contextType !== null) {
- instance.context = readContext(contextType);
- } else {
- var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
- instance.context = getMaskedContext(workInProgress, unmaskedContext);
}
+}
+function validateTailOptions(tailMode, revealOrder) {
{
- if (instance.state === newProps) {
- var componentName = getComponentNameFromType(ctor) || "Component";
+ if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) {
+ if (tailMode !== "collapsed" && tailMode !== "hidden") {
+ didWarnAboutTailOptions[tailMode] = true;
- if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
- didWarnAboutDirectlyAssigningPropsToState.add(componentName);
+ error(
+ '"%s" is not a supported value for tail on . ' +
+ 'Did you mean "collapsed" or "hidden"?',
+ tailMode
+ );
+ } else if (revealOrder !== "forwards" && revealOrder !== "backwards") {
+ didWarnAboutTailOptions[tailMode] = true;
error(
- "%s: It is not recommended to assign props directly to state " +
- "because updates to props won't be reflected in state. " +
- "In most cases, it is better to use props directly.",
- componentName
+ ' is only valid if revealOrder is ' +
+ '"forwards" or "backwards". ' +
+ 'Did you mean to specify revealOrder="forwards"?',
+ tailMode
);
}
}
-
- if (workInProgress.mode & StrictLegacyMode) {
- ReactStrictModeWarnings.recordLegacyContextWarning(
- workInProgress,
- instance
- );
- }
-
- ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
- workInProgress,
- instance
- );
- }
-
- instance.state = workInProgress.memoizedState;
- var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
-
- if (typeof getDerivedStateFromProps === "function") {
- applyDerivedStateFromProps(
- workInProgress,
- ctor,
- getDerivedStateFromProps,
- newProps
- );
- instance.state = workInProgress.memoizedState;
- } // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
-
- if (
- typeof ctor.getDerivedStateFromProps !== "function" &&
- typeof instance.getSnapshotBeforeUpdate !== "function" &&
- (typeof instance.UNSAFE_componentWillMount === "function" ||
- typeof instance.componentWillMount === "function")
- ) {
- callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's
- // process them now.
-
- processUpdateQueue(workInProgress, newProps, instance, renderLanes);
- instance.state = workInProgress.memoizedState;
- }
-
- if (typeof instance.componentDidMount === "function") {
- var fiberFlags = Update | LayoutStatic;
-
- if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
- fiberFlags |= MountLayoutDev;
- }
-
- workInProgress.flags |= fiberFlags;
}
}
-function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) {
- var instance = workInProgress.stateNode;
- var oldProps = workInProgress.memoizedProps;
- instance.props = oldProps;
- var oldContext = instance.context;
- var contextType = ctor.contextType;
- var nextContext = emptyContextObject;
-
- if (typeof contextType === "object" && contextType !== null) {
- nextContext = readContext(contextType);
- } else {
- var nextLegacyUnmaskedContext = getUnmaskedContext(
- workInProgress,
- ctor,
- true
- );
- nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
- }
+function validateSuspenseListNestedChild(childSlot, index) {
+ {
+ var isAnArray = isArray(childSlot);
+ var isIterable =
+ !isAnArray && typeof getIteratorFn(childSlot) === "function";
- var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
- var hasNewLifecycles =
- typeof getDerivedStateFromProps === "function" ||
- typeof instance.getSnapshotBeforeUpdate === "function"; // Note: During these life-cycles, instance.props/instance.state are what
- // ever the previously attempted to render - not the "current". However,
- // during componentDidUpdate we pass the "current" props.
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
+ if (isAnArray || isIterable) {
+ var type = isAnArray ? "array" : "iterable";
- if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillReceiveProps === "function" ||
- typeof instance.componentWillReceiveProps === "function")
- ) {
- if (oldProps !== newProps || oldContext !== nextContext) {
- callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- nextContext
+ error(
+ "A nested %s was passed to row #%s in . Wrap it in " +
+ "an additional SuspenseList to configure its revealOrder: " +
+ " ... " +
+ "{%s} ... " +
+ "",
+ type,
+ index,
+ type
);
- }
- }
-
- resetHasForceUpdateBeforeProcessing();
- var oldState = workInProgress.memoizedState;
- var newState = (instance.state = oldState);
- processUpdateQueue(workInProgress, newProps, instance, renderLanes);
- newState = workInProgress.memoizedState;
-
- if (
- oldProps === newProps &&
- oldState === newState &&
- !hasContextChanged() &&
- !checkHasForceUpdateAfterProcessing()
- ) {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidMount === "function") {
- var fiberFlags = Update | LayoutStatic;
-
- if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
- fiberFlags |= MountLayoutDev;
- }
- workInProgress.flags |= fiberFlags;
+ return false;
}
-
- return false;
- }
-
- if (typeof getDerivedStateFromProps === "function") {
- applyDerivedStateFromProps(
- workInProgress,
- ctor,
- getDerivedStateFromProps,
- newProps
- );
- newState = workInProgress.memoizedState;
}
- var shouldUpdate =
- checkHasForceUpdateAfterProcessing() ||
- checkShouldComponentUpdate(
- workInProgress,
- ctor,
- oldProps,
- newProps,
- oldState,
- newState,
- nextContext
- );
+ return true;
+}
- if (shouldUpdate) {
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
+function validateSuspenseListChildren(children, revealOrder) {
+ {
if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillMount === "function" ||
- typeof instance.componentWillMount === "function")
+ (revealOrder === "forwards" || revealOrder === "backwards") &&
+ children !== undefined &&
+ children !== null &&
+ children !== false
) {
- if (typeof instance.componentWillMount === "function") {
- instance.componentWillMount();
- }
-
- if (typeof instance.UNSAFE_componentWillMount === "function") {
- instance.UNSAFE_componentWillMount();
- }
- }
-
- if (typeof instance.componentDidMount === "function") {
- var _fiberFlags = Update | LayoutStatic;
-
- if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
- _fiberFlags |= MountLayoutDev;
- }
-
- workInProgress.flags |= _fiberFlags;
- }
- } else {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidMount === "function") {
- var _fiberFlags2 = Update | LayoutStatic;
-
- if ((workInProgress.mode & StrictEffectsMode) !== NoMode) {
- _fiberFlags2 |= MountLayoutDev;
- }
+ if (isArray(children)) {
+ for (var i = 0; i < children.length; i++) {
+ if (!validateSuspenseListNestedChild(children[i], i)) {
+ return;
+ }
+ }
+ } else {
+ var iteratorFn = getIteratorFn(children);
- workInProgress.flags |= _fiberFlags2;
- } // If shouldComponentUpdate returned false, we should still update the
- // memoized state to indicate that this work can be reused.
+ if (typeof iteratorFn === "function") {
+ var childrenIterator = iteratorFn.call(children);
- workInProgress.memoizedProps = newProps;
- workInProgress.memoizedState = newState;
- } // Update the existing instance's state, props, and context pointers even
- // if shouldComponentUpdate returns false.
+ if (childrenIterator) {
+ var step = childrenIterator.next();
+ var _i = 0;
- instance.props = newProps;
- instance.state = newState;
- instance.context = nextContext;
- return shouldUpdate;
-} // Invokes the update life-cycles and returns false if it shouldn't rerender.
+ for (; !step.done; step = childrenIterator.next()) {
+ if (!validateSuspenseListNestedChild(step.value, _i)) {
+ return;
+ }
-function updateClassInstance(
- current,
+ _i++;
+ }
+ }
+ } else {
+ error(
+ 'A single row was passed to a . ' +
+ "This is not useful since it needs multiple rows. " +
+ "Did you mean to pass multiple children or an array?",
+ revealOrder
+ );
+ }
+ }
+ }
+ }
+}
+
+function initSuspenseListRenderState(
workInProgress,
- ctor,
- newProps,
- renderLanes
+ isBackwards,
+ tail,
+ lastContentRow,
+ tailMode
) {
- var instance = workInProgress.stateNode;
- cloneUpdateQueue(current, workInProgress);
- var unresolvedOldProps = workInProgress.memoizedProps;
- var oldProps =
- workInProgress.type === workInProgress.elementType
- ? unresolvedOldProps
- : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
- instance.props = oldProps;
- var unresolvedNewProps = workInProgress.pendingProps;
- var oldContext = instance.context;
- var contextType = ctor.contextType;
- var nextContext = emptyContextObject;
+ var renderState = workInProgress.memoizedState;
- if (typeof contextType === "object" && contextType !== null) {
- nextContext = readContext(contextType);
+ if (renderState === null) {
+ workInProgress.memoizedState = {
+ isBackwards: isBackwards,
+ rendering: null,
+ renderingStartTime: 0,
+ last: lastContentRow,
+ tail: tail,
+ tailMode: tailMode
+ };
} else {
- var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
- nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
+ // We can reuse the existing object from previous renders.
+ renderState.isBackwards = isBackwards;
+ renderState.rendering = null;
+ renderState.renderingStartTime = 0;
+ renderState.last = lastContentRow;
+ renderState.tail = tail;
+ renderState.tailMode = tailMode;
}
+} // This can end up rendering this component multiple passes.
+// The first pass splits the children fibers into two sets. A head and tail.
+// We first render the head. If anything is in fallback state, we do another
+// pass through beginWork to rerender all children (including the tail) with
+// the force suspend context. If the first render didn't have anything in
+// in fallback state. Then we render each row in the tail one-by-one.
+// That happens in the completeWork phase without going back to beginWork.
- var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
- var hasNewLifecycles =
- typeof getDerivedStateFromProps === "function" ||
- typeof instance.getSnapshotBeforeUpdate === "function"; // Note: During these life-cycles, instance.props/instance.state are what
- // ever the previously attempted to render - not the "current". However,
- // during componentDidUpdate we pass the "current" props.
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
+function updateSuspenseListComponent(current, workInProgress, renderLanes) {
+ var nextProps = workInProgress.pendingProps;
+ var revealOrder = nextProps.revealOrder;
+ var tailMode = nextProps.tail;
+ var newChildren = nextProps.children;
+ validateRevealOrder(revealOrder);
+ validateTailOptions(tailMode, revealOrder);
+ validateSuspenseListChildren(newChildren, revealOrder);
+ reconcileChildren(current, workInProgress, newChildren, renderLanes);
+ var suspenseContext = suspenseStackCursor.current;
+ var shouldForceFallback = hasSuspenseListContext(
+ suspenseContext,
+ ForceSuspenseFallback
+ );
- if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillReceiveProps === "function" ||
- typeof instance.componentWillReceiveProps === "function")
- ) {
- if (
- unresolvedOldProps !== unresolvedNewProps ||
- oldContext !== nextContext
- ) {
- callComponentWillReceiveProps(
+ if (shouldForceFallback) {
+ suspenseContext = setShallowSuspenseListContext(
+ suspenseContext,
+ ForceSuspenseFallback
+ );
+ workInProgress.flags |= DidCapture;
+ } else {
+ var didSuspendBefore =
+ current !== null && (current.flags & DidCapture) !== NoFlags$1;
+
+ if (didSuspendBefore) {
+ // If we previously forced a fallback, we need to schedule work
+ // on any nested boundaries to let them know to try to render
+ // again. This is the same as context updating.
+ propagateSuspenseContextChange(
workInProgress,
- instance,
- newProps,
- nextContext
+ workInProgress.child,
+ renderLanes
);
}
+
+ suspenseContext = setDefaultShallowSuspenseListContext(suspenseContext);
}
- resetHasForceUpdateBeforeProcessing();
- var oldState = workInProgress.memoizedState;
- var newState = (instance.state = oldState);
- processUpdateQueue(workInProgress, newProps, instance, renderLanes);
- newState = workInProgress.memoizedState;
+ pushSuspenseListContext(workInProgress, suspenseContext);
- if (
- unresolvedOldProps === unresolvedNewProps &&
- oldState === newState &&
- !hasContextChanged() &&
- !checkHasForceUpdateAfterProcessing() &&
- !(
- enableLazyContextPropagation &&
- current !== null &&
- current.dependencies !== null &&
- checkIfContextChanged(current.dependencies)
- )
- ) {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === "function") {
- if (
- unresolvedOldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.flags |= Update;
- }
- }
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ // In legacy mode, SuspenseList doesn't work so we just
+ // use make it a noop by treating it as the default revealOrder.
+ workInProgress.memoizedState = null;
+ } else {
+ switch (revealOrder) {
+ case "forwards": {
+ var lastContentRow = findLastContentRow(workInProgress.child);
+ var tail;
- if (typeof instance.getSnapshotBeforeUpdate === "function") {
- if (
- unresolvedOldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.flags |= Snapshot;
+ if (lastContentRow === null) {
+ // The whole list is part of the tail.
+ // TODO: We could fast path by just rendering the tail now.
+ tail = workInProgress.child;
+ workInProgress.child = null;
+ } else {
+ // Disconnect the tail rows after the content row.
+ // We're going to render them separately later.
+ tail = lastContentRow.sibling;
+ lastContentRow.sibling = null;
+ }
+
+ initSuspenseListRenderState(
+ workInProgress,
+ false, // isBackwards
+ tail,
+ lastContentRow,
+ tailMode
+ );
+ break;
}
- }
- return false;
- }
+ case "backwards": {
+ // We're going to find the first row that has existing content.
+ // At the same time we're going to reverse the list of everything
+ // we pass in the meantime. That's going to be our tail in reverse
+ // order.
+ var _tail = null;
+ var row = workInProgress.child;
+ workInProgress.child = null;
- if (typeof getDerivedStateFromProps === "function") {
- applyDerivedStateFromProps(
- workInProgress,
- ctor,
- getDerivedStateFromProps,
- newProps
- );
- newState = workInProgress.memoizedState;
- }
+ while (row !== null) {
+ var currentRow = row.alternate; // New rows can't be content rows.
- var shouldUpdate =
- checkHasForceUpdateAfterProcessing() ||
- checkShouldComponentUpdate(
- workInProgress,
- ctor,
- oldProps,
- newProps,
- oldState,
- newState,
- nextContext
- ) || // TODO: In some cases, we'll end up checking if context has changed twice,
- // both before and after `shouldComponentUpdate` has been called. Not ideal,
- // but I'm loath to refactor this function. This only happens for memoized
- // components so it's not that common.
- (enableLazyContextPropagation &&
- current !== null &&
- current.dependencies !== null &&
- checkIfContextChanged(current.dependencies));
+ if (currentRow !== null && findFirstSuspended(currentRow) === null) {
+ // This is the beginning of the main content.
+ workInProgress.child = row;
+ break;
+ }
- if (shouldUpdate) {
- // In order to support react-lifecycles-compat polyfilled components,
- // Unsafe lifecycles should not be invoked for components using the new APIs.
- if (
- !hasNewLifecycles &&
- (typeof instance.UNSAFE_componentWillUpdate === "function" ||
- typeof instance.componentWillUpdate === "function")
- ) {
- if (typeof instance.componentWillUpdate === "function") {
- instance.componentWillUpdate(newProps, newState, nextContext);
- }
+ var nextRow = row.sibling;
+ row.sibling = _tail;
+ _tail = row;
+ row = nextRow;
+ } // TODO: If workInProgress.child is null, we can continue on the tail immediately.
- if (typeof instance.UNSAFE_componentWillUpdate === "function") {
- instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
+ initSuspenseListRenderState(
+ workInProgress,
+ true, // isBackwards
+ _tail,
+ null, // last
+ tailMode
+ );
+ break;
}
- }
-
- if (typeof instance.componentDidUpdate === "function") {
- workInProgress.flags |= Update;
- }
- if (typeof instance.getSnapshotBeforeUpdate === "function") {
- workInProgress.flags |= Snapshot;
- }
- } else {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === "function") {
- if (
- unresolvedOldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.flags |= Update;
+ case "together": {
+ initSuspenseListRenderState(
+ workInProgress,
+ false, // isBackwards
+ null, // tail
+ null, // last
+ undefined
+ );
+ break;
}
- }
- if (typeof instance.getSnapshotBeforeUpdate === "function") {
- if (
- unresolvedOldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.flags |= Snapshot;
+ default: {
+ // The default reveal order is the same as not having
+ // a boundary.
+ workInProgress.memoizedState = null;
}
- } // If shouldComponentUpdate returned false, we should still update the
- // memoized props/state to indicate that this work can be reused.
-
- workInProgress.memoizedProps = newProps;
- workInProgress.memoizedState = newState;
- } // Update the existing instance's state, props, and context pointers even
- // if shouldComponentUpdate returns false.
+ }
+ }
- instance.props = newProps;
- instance.state = newState;
- instance.context = nextContext;
- return shouldUpdate;
+ return workInProgress.child;
}
-function createCapturedValueAtFiber(value, source) {
- // If the value is an error, call this function immediately after it is thrown
- // so the stack is accurate.
- return {
- value: value,
- source: source,
- stack: getStackByFiberInDevAndProd(source),
- digest: null
- };
-}
-function createCapturedValue(value, digest, stack) {
- return {
- value: value,
- source: null,
- stack: stack != null ? stack : null,
- digest: digest != null ? digest : null
- };
-}
+function updatePortalComponent(current, workInProgress, renderLanes) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ var nextChildren = workInProgress.pendingProps;
-var ReactFiberErrorDialogWWW = require("ReactFiberErrorDialog");
+ if (current === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderLanes
+ );
+ } else {
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ }
-if (typeof ReactFiberErrorDialogWWW.showErrorDialog !== "function") {
- throw new Error(
- "Expected ReactFiberErrorDialog.showErrorDialog to be a function."
- );
+ return workInProgress.child;
}
-function showErrorDialog(boundary, errorInfo) {
- var capturedError = {
- componentStack: errorInfo.stack !== null ? errorInfo.stack : "",
- error: errorInfo.value,
- errorBoundary:
- boundary !== null && boundary.tag === ClassComponent
- ? boundary.stateNode
- : null
- };
- return ReactFiberErrorDialogWWW.showErrorDialog(capturedError);
-}
+var hasWarnedAboutUsingNoValuePropOnContextProvider = false;
-function logCapturedError(boundary, errorInfo) {
- try {
- var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging.
- // This enables renderers like ReactNative to better manage redbox behavior.
+function updateContextProvider(current, workInProgress, renderLanes) {
+ var providerType = workInProgress.type;
+ var context = providerType._context;
+ var newProps = workInProgress.pendingProps;
+ var oldProps = workInProgress.memoizedProps;
+ var newValue = newProps.value;
- if (logError === false) {
- return;
- }
+ {
+ if (!("value" in newProps)) {
+ if (!hasWarnedAboutUsingNoValuePropOnContextProvider) {
+ hasWarnedAboutUsingNoValuePropOnContextProvider = true;
- var error = errorInfo.value;
+ error(
+ "The `value` prop is required for the ``. Did you misspell it or forget to pass it?"
+ );
+ }
+ }
- if (true) {
- var source = errorInfo.source;
- var stack = errorInfo.stack;
- var componentStack = stack !== null ? stack : ""; // Browsers support silencing uncaught errors by calling
- // `preventDefault()` in window `error` handler.
- // We record this information as an expando on the error.
+ var providerPropTypes = workInProgress.type.propTypes;
- if (error != null && error._suppressLogging) {
- if (boundary.tag === ClassComponent) {
- // The error is recoverable and was silenced.
- // Ignore it and don't print the stack addendum.
- // This is handy for testing error boundaries without noise.
- return;
- } // The error is fatal. Since the silencing might have
- // been accidental, we'll surface it anyway.
- // However, the browser would have silenced the original error
- // so we'll print it first, and then print the stack addendum.
+ if (providerPropTypes) {
+ checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider");
+ }
+ }
- console["error"](error); // Don't transform to our wrapper
- // For a more detailed description of this block, see:
- // https://github.com/facebook/react/pull/13384
- }
+ pushProvider(workInProgress, context, newValue);
- var componentName = source ? getComponentNameFromFiber(source) : null;
- var componentNameMessage = componentName
- ? "The above error occurred in the <" + componentName + "> component:"
- : "The above error occurred in one of your React components:";
- var errorBoundaryMessage;
+ if (enableLazyContextPropagation);
+ else {
+ if (oldProps !== null) {
+ var oldValue = oldProps.value;
- if (boundary.tag === HostRoot) {
- errorBoundaryMessage =
- "Consider adding an error boundary to your tree to customize error handling behavior.\n" +
- "Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.";
+ if (objectIs(oldValue, newValue)) {
+ // No change. Bailout early if children are the same.
+ if (oldProps.children === newProps.children && !hasContextChanged()) {
+ return bailoutOnAlreadyFinishedWork(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ }
} else {
- var errorBoundaryName =
- getComponentNameFromFiber(boundary) || "Anonymous";
- errorBoundaryMessage =
- "React will try to recreate this component tree from scratch " +
- ("using the error boundary you provided, " + errorBoundaryName + ".");
+ // The context value changed. Search for matching consumers and schedule
+ // them to update.
+ propagateContextChange(workInProgress, context, renderLanes);
}
-
- var combinedMessage =
- componentNameMessage +
- "\n" +
- componentStack +
- "\n\n" +
- ("" + errorBoundaryMessage); // In development, we provide our own message with just the component stack.
- // We don't include the original error message and JS stack because the browser
- // has already printed it. Even if the application swallows the error, it is still
- // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
-
- console["error"](combinedMessage); // Don't transform to our wrapper
}
- } catch (e) {
- // This method must not throw, or React internal state will get messed up.
- // If console.error is overridden, or logCapturedError() shows a dialog that throws,
- // we want to report this error outside of the normal stack as a last resort.
- // https://github.com/facebook/react/issues/13188
- setTimeout(function () {
- throw e;
- });
}
-}
-
-function createRootErrorUpdate(fiber, errorInfo, lane) {
- var update = createUpdate(lane); // Unmount the root by rendering null.
-
- update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property
- // being called "element".
-
- update.payload = {
- element: null
- };
- var error = errorInfo.value;
-
- update.callback = function () {
- onUncaughtError(error);
- logCapturedError(fiber, errorInfo);
- };
- return update;
+ var newChildren = newProps.children;
+ reconcileChildren(current, workInProgress, newChildren, renderLanes);
+ return workInProgress.child;
}
-function createClassErrorUpdate(fiber, errorInfo, lane) {
- var update = createUpdate(lane);
- update.tag = CaptureUpdate;
- var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
+var hasWarnedAboutUsingContextAsConsumer = false;
- if (typeof getDerivedStateFromError === "function") {
- var error$1 = errorInfo.value;
+function updateContextConsumer(current, workInProgress, renderLanes) {
+ var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In
+ // DEV mode, we create a separate object for Context.Consumer that acts
+ // like a proxy to Context. This proxy object adds unnecessary code in PROD
+ // so we use the old behaviour (Context.Consumer references Context) to
+ // reduce size and overhead. The separate object references context via
+ // a property called "_context", which also gives us the ability to check
+ // in DEV mode if this property exists or not and warn if it does not.
- update.payload = function () {
- return getDerivedStateFromError(error$1);
- };
+ {
+ if (context._context === undefined) {
+ // This may be because it's a Context (rather than a Consumer).
+ // Or it may be because it's older React where they're the same thing.
+ // We only want to warn if we're sure it's a new React.
+ if (context !== context.Consumer) {
+ if (!hasWarnedAboutUsingContextAsConsumer) {
+ hasWarnedAboutUsingContextAsConsumer = true;
- update.callback = function () {
- {
- markFailedErrorBoundaryForHotReloading(fiber);
+ error(
+ "Rendering directly is not supported and will be removed in " +
+ "a future major release. Did you mean to render instead?"
+ );
+ }
}
-
- logCapturedError(fiber, errorInfo);
- };
+ } else {
+ context = context._context;
+ }
}
- var inst = fiber.stateNode;
+ var newProps = workInProgress.pendingProps;
+ var render = newProps.children;
- if (inst !== null && typeof inst.componentDidCatch === "function") {
- // $FlowFixMe[missing-this-annot]
- update.callback = function callback() {
- {
- markFailedErrorBoundaryForHotReloading(fiber);
- }
+ {
+ if (typeof render !== "function") {
+ error(
+ "A context consumer was rendered with multiple children, or a child " +
+ "that isn't a function. A context consumer expects a single child " +
+ "that is a function. If you did pass a function, make sure there " +
+ "is no trailing or leading whitespace around it."
+ );
+ }
+ }
- logCapturedError(fiber, errorInfo);
+ prepareToReadContext(workInProgress, renderLanes);
+ var newValue = readContext(context);
- if (typeof getDerivedStateFromError !== "function") {
- // To preserve the preexisting retry behavior of error boundaries,
- // we keep track of which ones already failed during this batch.
- // This gets reset before we yield back to the browser.
- // TODO: Warn in strict mode if getDerivedStateFromError is
- // not defined.
- markLegacyErrorBoundaryAsFailed(this);
- }
+ if (enableSchedulingProfiler) {
+ markComponentRenderStarted(workInProgress);
+ }
- var error$1 = errorInfo.value;
- var stack = errorInfo.stack;
- this.componentDidCatch(error$1, {
- componentStack: stack !== null ? stack : ""
- });
+ var newChildren;
- {
- if (typeof getDerivedStateFromError !== "function") {
- // If componentDidCatch is the only error boundary method defined,
- // then it needs to call setState to recover from errors.
- // If no state update is scheduled then the boundary will swallow the error.
- if (!includesSomeLane(fiber.lanes, SyncLane)) {
- error(
- "%s: Error boundaries should implement getDerivedStateFromError(). " +
- "In that method, return a state update to display an error message or fallback UI.",
- getComponentNameFromFiber(fiber) || "Unknown"
- );
- }
- }
- }
- };
+ {
+ ReactCurrentOwner$2.current = workInProgress;
+ setIsRendering(true);
+ newChildren = render(newValue);
+ setIsRendering(false);
}
- return update;
-}
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
+ } // React DevTools reads this flag.
-function resetSuspendedComponent(sourceFiber, rootRenderLanes) {
- if (enableLazyContextPropagation) {
- var currentSourceFiber = sourceFiber.alternate;
+ workInProgress.flags |= PerformedWork;
+ reconcileChildren(current, workInProgress, newChildren, renderLanes);
+ return workInProgress.child;
+}
- if (currentSourceFiber !== null) {
- // Since we never visited the children of the suspended component, we
- // need to propagate the context change now, to ensure that we visit
- // them during the retry.
- //
- // We don't have to do this for errors because we retry errors without
- // committing in between. So this is specific to Suspense.
- propagateParentContextChangesToDeferredTree(
- currentSourceFiber,
- sourceFiber,
- rootRenderLanes
- );
- }
- } // Reset the memoizedState to what it was before we attempted to render it.
- // A legacy mode Suspense quirk, only relevant to hook components.
+function updateScopeComponent(current, workInProgress, renderLanes) {
+ var nextProps = workInProgress.pendingProps;
+ var nextChildren = nextProps.children;
+ reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ return workInProgress.child;
+}
- var tag = sourceFiber.tag;
+function markWorkInProgressReceivedUpdate() {
+ didReceiveUpdate = true;
+}
+function checkIfWorkInProgressReceivedUpdate() {
+ return didReceiveUpdate;
+}
- if (
- (sourceFiber.mode & ConcurrentMode) === NoMode &&
- (tag === FunctionComponent ||
- tag === ForwardRef ||
- tag === SimpleMemoComponent)
- ) {
- var currentSource = sourceFiber.alternate;
+function resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress) {
+ if ((workInProgress.mode & ConcurrentMode) === NoMode) {
+ if (current !== null) {
+ // A lazy component only mounts if it suspended inside a non-
+ // concurrent tree, in an inconsistent state. We want to treat it like
+ // a new mount, even though an empty version of it already committed.
+ // Disconnect the alternate pointers.
+ current.alternate = null;
+ workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect
- if (currentSource) {
- sourceFiber.updateQueue = currentSource.updateQueue;
- sourceFiber.memoizedState = currentSource.memoizedState;
- sourceFiber.lanes = currentSource.lanes;
- } else {
- sourceFiber.updateQueue = null;
- sourceFiber.memoizedState = null;
+ workInProgress.flags |= Placement;
}
}
}
-function markSuspenseBoundaryShouldCapture(
- suspenseBoundary,
- returnFiber,
- sourceFiber,
- root,
- rootRenderLanes
-) {
- // This marks a Suspense boundary so that when we're unwinding the stack,
- // it captures the suspended "exception" and does a second (fallback) pass.
- if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
- // Legacy Mode Suspense
- //
- // If the boundary is in legacy mode, we should *not*
- // suspend the commit. Pretend as if the suspended component rendered
- // null and keep rendering. When the Suspense boundary completes,
- // we'll do a second pass to render the fallback.
- if (suspenseBoundary === returnFiber) {
- // Special case where we suspended while reconciling the children of
- // a Suspense boundary's inner Offscreen wrapper fiber. This happens
- // when a React.lazy component is a direct child of a
- // Suspense boundary.
- //
- // Suspense boundaries are implemented as multiple fibers, but they
- // are a single conceptual unit. The legacy mode behavior where we
- // pretend the suspended fiber committed as `null` won't work,
- // because in this case the "suspended" fiber is the inner
- // Offscreen wrapper.
- //
- // Because the contents of the boundary haven't started rendering
- // yet (i.e. nothing in the tree has partially rendered) we can
- // switch to the regular, concurrent mode behavior: mark the
- // boundary with ShouldCapture and enter the unwind phase.
- suspenseBoundary.flags |= ShouldCapture;
- } else {
- suspenseBoundary.flags |= DidCapture;
- sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete.
- // But we shouldn't call any lifecycle methods or callbacks. Remove
- // all lifecycle effect tags.
+function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
+ if (current !== null) {
+ // Reuse previous dependencies
+ workInProgress.dependencies = current.dependencies;
+ }
- sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
+ {
+ // Don't update "base" render times for bailouts.
+ stopProfilerTimerIfRunning();
+ }
- if (sourceFiber.tag === ClassComponent) {
- var currentSourceFiber = sourceFiber.alternate;
+ markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work.
- if (currentSourceFiber === null) {
- // This is a new mount. Change the tag so it's not mistaken for a
- // completed class component. For example, we should not call
- // componentWillUnmount if it is deleted.
- sourceFiber.tag = IncompleteClassComponent;
- } else {
- // When we try rendering again, we should not reuse the current fiber,
- // since it's known to be in an inconsistent state. Use a force update to
- // prevent a bail out.
- var update = createUpdate(SyncLane);
- update.tag = ForceUpdate;
- enqueueUpdate(sourceFiber, update, SyncLane);
- }
- } // The source fiber did not complete. Mark it with Sync priority to
- // indicate that it still has pending work.
+ if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
+ // The children don't have any work either. We can skip them.
+ // TODO: Once we add back resuming, we should check if the children are
+ // a work-in-progress set. If so, we need to transfer their effects.
+ if (enableLazyContextPropagation && current !== null) {
+ // Before bailing out, check if there are any context changes in
+ // the children.
+ lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);
- sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
+ if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
+ return null;
+ }
+ } else {
+ return null;
}
+ } // This fiber doesn't have work, but its subtree does. Clone the child
+ // fibers and continue.
- return suspenseBoundary;
- } // Confirmed that the boundary is in a concurrent mode tree. Continue
- // with the normal suspend path.
- //
- // After this we'll use a set of heuristics to determine whether this
- // render pass will run to completion or restart or "suspend" the commit.
- // The actual logic for this is spread out in different places.
- //
- // This first principle is that if we're going to suspend when we complete
- // a root, then we should also restart if we get an update or ping that
- // might unsuspend it, and vice versa. The only reason to suspend is
- // because you think you might want to restart before committing. However,
- // it doesn't make sense to restart only while in the period we're suspended.
- //
- // Restarting too aggressively is also not good because it starves out any
- // intermediate loading state. So we use heuristics to determine when.
- // Suspense Heuristics
- //
- // If nothing threw a Promise or all the same fallbacks are already showing,
- // then don't suspend/restart.
- //
- // If this is an initial render of a new tree of Suspense boundaries and
- // those trigger a fallback, then don't suspend/restart. We want to ensure
- // that we can show the initial loading state as quickly as possible.
- //
- // If we hit a "Delayed" case, such as when we'd switch from content back into
- // a fallback, then we should always suspend/restart. Transitions apply
- // to this case. If none is defined, JND is used instead.
- //
- // If we're already showing a fallback and it gets "retried", allowing us to show
- // another level, but there's still an inner boundary that would show a fallback,
- // then we suspend/restart for 500ms since the last time we showed a fallback
- // anywhere in the tree. This effectively throttles progressive loading into a
- // consistent train of commits. This also gives us an opportunity to restart to
- // get to the completed state slightly earlier.
- //
- // If there's ambiguity due to batching it's resolved in preference of:
- // 1) "delayed", 2) "initial render", 3) "retry".
- //
- // We want to ensure that a "busy" state doesn't get force committed. We want to
- // ensure that new initial loading states can commit as soon as possible.
+ cloneChildFibers(current, workInProgress);
+ return workInProgress.child;
+}
- suspenseBoundary.flags |= ShouldCapture; // TODO: I think we can remove this, since we now use `DidCapture` in
- // the begin phase to prevent an early bailout.
+function remountFiber(current, oldWorkInProgress, newWorkInProgress) {
+ {
+ var returnFiber = oldWorkInProgress.return;
- suspenseBoundary.lanes = rootRenderLanes;
- return suspenseBoundary;
-}
+ if (returnFiber === null) {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ throw new Error("Cannot swap the root fiber.");
+ } // Disconnect from the old current.
+ // It will get deleted.
-function throwException(
- root,
- returnFiber,
- sourceFiber,
- value,
- rootRenderLanes
-) {
- // The source fiber did not complete.
- sourceFiber.flags |= Incomplete;
+ current.alternate = null;
+ oldWorkInProgress.alternate = null; // Connect to the new tree.
- {
- if (isDevToolsPresent) {
- // If we have pending work still, restore the original updaters
- restorePendingUpdaters(root, rootRenderLanes);
- }
- }
+ newWorkInProgress.index = oldWorkInProgress.index;
+ newWorkInProgress.sibling = oldWorkInProgress.sibling;
+ newWorkInProgress.return = oldWorkInProgress.return;
+ newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it.
- if (
- value !== null &&
- typeof value === "object" &&
- typeof value.then === "function"
- ) {
- // This is a wakeable. The component suspended.
- var wakeable = value;
- resetSuspendedComponent(sourceFiber, rootRenderLanes);
+ if (oldWorkInProgress === returnFiber.child) {
+ returnFiber.child = newWorkInProgress;
+ } else {
+ var prevSibling = returnFiber.child;
- {
- if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
- markDidThrowWhileHydratingDEV();
- }
- }
+ if (prevSibling === null) {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ throw new Error("Expected parent to have a child.");
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
- {
- if (enableDebugTracing) {
- if (sourceFiber.mode & DebugTracingMode) {
- var name = getComponentNameFromFiber(sourceFiber) || "Unknown";
- logComponentSuspended(name, wakeable);
+ while (prevSibling.sibling !== oldWorkInProgress) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ prevSibling = prevSibling.sibling;
+
+ if (prevSibling === null) {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ throw new Error("Expected to find the previous sibling.");
}
- }
- } // Mark the nearest Suspense boundary to switch to rendering a fallback.
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
- var suspenseBoundary = getSuspenseHandler();
+ prevSibling.sibling = newWorkInProgress;
+ } // Delete the old fiber and place the new one.
+ // Since the old fiber is disconnected, we have to schedule it manually.
- if (suspenseBoundary !== null) {
- switch (suspenseBoundary.tag) {
- case SuspenseComponent: {
- // If this suspense boundary is not already showing a fallback, mark
- // the in-progress render as suspended. We try to perform this logic
- // as soon as soon as possible during the render phase, so the work
- // loop can know things like whether it's OK to switch to other tasks,
- // or whether it can wait for data to resolve before continuing.
- // TODO: Most of these checks are already performed when entering a
- // Suspense boundary. We should track the information on the stack so
- // we don't have to recompute it on demand. This would also allow us
- // to unify with `use` which needs to perform this logic even sooner,
- // before `throwException` is called.
- if (sourceFiber.mode & ConcurrentMode) {
- if (getShellBoundary() === null) {
- // Suspended in the "shell" of the app. This is an undesirable
- // loading state. We should avoid committing this tree.
- renderDidSuspendDelayIfPossible();
- } else {
- // If we suspended deeper than the shell, we don't need to delay
- // the commmit. However, we still call renderDidSuspend if this is
- // a new boundary, to tell the work loop that a new fallback has
- // appeared during this render.
- // TODO: Theoretically we should be able to delete this branch.
- // It's currently used for two things: 1) to throttle the
- // appearance of successive loading states, and 2) in
- // SuspenseList, to determine whether the children include any
- // pending fallbacks. For 1, we should apply throttling to all
- // retries, not just ones that render an additional fallback. For
- // 2, we should check subtreeFlags instead. Then we can delete
- // this branch.
- var current = suspenseBoundary.alternate;
+ var deletions = returnFiber.deletions;
- if (current === null) {
- renderDidSuspend();
- }
- }
- }
+ if (deletions === null) {
+ returnFiber.deletions = [current];
+ returnFiber.flags |= ChildDeletion;
+ } else {
+ deletions.push(current);
+ }
- suspenseBoundary.flags &= ~ForceClientRender;
- markSuspenseBoundaryShouldCapture(
- suspenseBoundary,
- returnFiber,
- sourceFiber,
- root,
- rootRenderLanes
- ); // Retry listener
- //
- // If the fallback does commit, we need to attach a different type of
- // listener. This one schedules an update on the Suspense boundary to
- // turn the fallback state off.
- //
- // Stash the wakeable on the boundary fiber so we can access it in the
- // commit phase.
- //
- // When the wakeable resolves, we'll attempt to render the boundary
- // again ("retry").
+ newWorkInProgress.flags |= Placement; // Restart work from the new fiber.
- var wakeables = suspenseBoundary.updateQueue;
+ return newWorkInProgress;
+ }
+}
- if (wakeables === null) {
- suspenseBoundary.updateQueue = new Set([wakeable]);
- } else {
- wakeables.add(wakeable);
- }
+function checkScheduledUpdateOrContext(current, renderLanes) {
+ // Before performing an early bailout, we must check if there are pending
+ // updates or context.
+ var updateLanes = current.lanes;
- break;
- }
+ if (includesSomeLane(updateLanes, renderLanes)) {
+ return true;
+ } // No pending update, but because context is propagated lazily, we need
+ // to check for a context change before we bail out.
- case OffscreenComponent: {
- if (suspenseBoundary.mode & ConcurrentMode) {
- suspenseBoundary.flags |= ShouldCapture;
- var offscreenQueue = suspenseBoundary.updateQueue;
+ if (enableLazyContextPropagation) {
+ var dependencies = current.dependencies;
- if (offscreenQueue === null) {
- var newOffscreenQueue = {
- transitions: null,
- markerInstances: null,
- wakeables: new Set([wakeable])
- };
- suspenseBoundary.updateQueue = newOffscreenQueue;
- } else {
- var _wakeables = offscreenQueue.wakeables;
+ if (dependencies !== null && checkIfContextChanged(dependencies)) {
+ return true;
+ }
+ }
- if (_wakeables === null) {
- offscreenQueue.wakeables = new Set([wakeable]);
- } else {
- _wakeables.add(wakeable);
- }
- }
+ return false;
+}
- break;
- }
- }
- // eslint-disable-next-line no-fallthrough
+function attemptEarlyBailoutIfNoScheduledUpdate(
+ current,
+ workInProgress,
+ renderLanes
+) {
+ // This fiber does not have any pending work. Bailout without entering
+ // the begin phase. There's still some bookkeeping we that needs to be done
+ // in this optimized path, mostly pushing stuff onto the stack.
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ pushRootTransition(workInProgress);
- default: {
- throw new Error(
- "Unexpected Suspense handler tag (" +
- suspenseBoundary.tag +
- "). This " +
- "is a bug in React."
- );
- }
- } // We only attach ping listeners in concurrent mode. Legacy Suspense always
- // commits fallbacks synchronously, so there are no pings.
+ if (enableTransitionTracing) {
+ pushRootMarkerInstance(workInProgress);
+ }
- if (suspenseBoundary.mode & ConcurrentMode) {
- attachPingListener(root, wakeable, rootRenderLanes);
+ {
+ var cache = current.memoizedState.cache;
+ pushCacheProvider(workInProgress, cache);
}
- return;
- } else {
- // No boundary was found. Unless this is a sync update, this is OK.
- // We can suspend and wait for more data to arrive.
- if (root.tag === ConcurrentRoot) {
- // In a concurrent root, suspending without a Suspense boundary is
- // allowed. It will suspend indefinitely without committing.
- //
- // TODO: Should we have different behavior for discrete updates? What
- // about flushSync? Maybe it should put the tree into an inert state,
- // and potentially log a warning. Revisit this for a future release.
- attachPingListener(root, wakeable, rootRenderLanes);
- renderDidSuspendDelayIfPossible();
- return;
- } else {
- // In a legacy root, suspending without a boundary is always an error.
- var uncaughtSuspenseError = new Error(
- "A component suspended while responding to synchronous input. This " +
- "will cause the UI to be replaced with a loading indicator. To " +
- "fix, updates that suspend should be wrapped " +
- "with startTransition."
- );
- value = uncaughtSuspenseError;
+ resetHydrationState();
+ break;
+
+ case HostSingleton:
+ case HostComponent:
+ pushHostContext(workInProgress);
+ break;
+
+ case ClassComponent: {
+ var Component = workInProgress.type;
+
+ if (isContextProvider(Component)) {
+ pushContextProvider(workInProgress);
}
+
+ break;
+ }
+
+ case HostPortal:
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ break;
+
+ case ContextProvider: {
+ var newValue = workInProgress.memoizedProps.value;
+ var context = workInProgress.type._context;
+ pushProvider(workInProgress, context, newValue);
+ break;
}
- } else {
- // This is a regular error, not a Suspense wakeable.
- if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
- markDidThrowWhileHydratingDEV();
- var _suspenseBoundary = getSuspenseHandler(); // If the error was thrown during hydration, we may be able to recover by
- // discarding the dehydrated content and switching to a client render.
- // Instead of surfacing the error, find the nearest Suspense boundary
- // and render it again without hydration.
+ case Profiler:
+ {
+ // Profiler should only call onRender when one of its descendants actually rendered.
+ var hasChildWork = includesSomeLane(
+ renderLanes,
+ workInProgress.childLanes
+ );
- if (_suspenseBoundary !== null) {
- if ((_suspenseBoundary.flags & ShouldCapture) === NoFlags$1) {
- // Set a flag to indicate that we should try rendering the normal
- // children again, not the fallback.
- _suspenseBoundary.flags |= ForceClientRender;
+ if (hasChildWork) {
+ workInProgress.flags |= Update;
}
- markSuspenseBoundaryShouldCapture(
- _suspenseBoundary,
- returnFiber,
- sourceFiber,
- root,
- rootRenderLanes
- ); // Even though the user may not be affected by this error, we should
- // still log it so it can be fixed.
-
- queueHydrationError(createCapturedValueAtFiber(value, sourceFiber));
- return;
+ {
+ // Reset effect durations for the next eventual effect phase.
+ // These are reset during render to allow the DevTools commit hook a chance to read them,
+ var stateNode = workInProgress.stateNode;
+ stateNode.effectDuration = 0;
+ stateNode.passiveEffectDuration = 0;
+ }
}
- }
- }
- value = createCapturedValueAtFiber(value, sourceFiber);
- renderDidError(value); // We didn't find a boundary that could handle this type of exception. Start
- // over and traverse parent path again, this time treating the exception
- // as an error.
+ break;
- var workInProgress = returnFiber;
+ case SuspenseComponent: {
+ var state = workInProgress.memoizedState;
- do {
- switch (workInProgress.tag) {
- case HostRoot: {
- var _errorInfo = value;
- workInProgress.flags |= ShouldCapture;
- var lane = pickArbitraryLane(rootRenderLanes);
- workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
- var update = createRootErrorUpdate(workInProgress, _errorInfo, lane);
- enqueueCapturedUpdate(workInProgress, update);
- return;
- }
+ if (state !== null) {
+ if (state.dehydrated !== null) {
+ // We're not going to render the children, so this is just to maintain
+ // push/pop symmetry
+ pushPrimaryTreeSuspenseHandler(workInProgress); // We know that this component will suspend again because if it has
+ // been unsuspended it has committed as a resolved Suspense component.
+ // If it needs to be retried, it should have work scheduled on it.
- case ClassComponent:
- // Capture and retry
- var errorInfo = value;
- var ctor = workInProgress.type;
- var instance = workInProgress.stateNode;
+ workInProgress.flags |= DidCapture; // We should never render the children of a dehydrated boundary until we
+ // upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
- if (
- (workInProgress.flags & DidCapture) === NoFlags$1 &&
- (typeof ctor.getDerivedStateFromError === "function" ||
- (instance !== null &&
- typeof instance.componentDidCatch === "function" &&
- !isAlreadyFailedLegacyErrorBoundary(instance)))
- ) {
- workInProgress.flags |= ShouldCapture;
+ return null;
+ } // If this boundary is currently timed out, we need to decide
+ // whether to retry the primary children, or to skip over it and
+ // go straight to the fallback. Check the priority of the primary
+ // child fragment.
- var _lane = pickArbitraryLane(rootRenderLanes);
+ var primaryChildFragment = workInProgress.child;
+ var primaryChildLanes = primaryChildFragment.childLanes;
- workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state
+ if (includesSomeLane(renderLanes, primaryChildLanes)) {
+ // The primary children have pending work. Use the normal path
+ // to attempt to render the primary children again.
+ return updateSuspenseComponent(current, workInProgress, renderLanes);
+ } else {
+ // The primary child fragment does not have pending work marked
+ // on it
+ pushPrimaryTreeSuspenseHandler(workInProgress); // The primary children do not have pending work with sufficient
+ // priority. Bailout.
- var _update = createClassErrorUpdate(
+ var child = bailoutOnAlreadyFinishedWork(
+ current,
workInProgress,
- errorInfo,
- _lane
+ renderLanes
);
- enqueueCapturedUpdate(workInProgress, _update);
- return;
+ if (child !== null) {
+ // The fallback children have pending work. Skip over the
+ // primary children and work on the fallback.
+ return child.sibling;
+ } else {
+ // Note: We can return `null` here because we already checked
+ // whether there were nested context consumers, via the call to
+ // `bailoutOnAlreadyFinishedWork` above.
+ return null;
+ }
}
+ } else {
+ pushPrimaryTreeSuspenseHandler(workInProgress);
+ }
- break;
- } // $FlowFixMe[incompatible-type] we bail out when we get a null
+ break;
+ }
- workInProgress = workInProgress.return;
- } while (workInProgress !== null);
-}
+ case SuspenseListComponent: {
+ var didSuspendBefore = (current.flags & DidCapture) !== NoFlags$1;
-var TransitionRoot = 0;
-var TransitionTracingMarker = 1;
-function processTransitionCallbacks(pendingTransitions, endTime, callbacks) {
- if (enableTransitionTracing) {
- if (pendingTransitions !== null) {
- var transitionStart = pendingTransitions.transitionStart;
- var onTransitionStart = callbacks.onTransitionStart;
+ var _hasChildWork = includesSomeLane(
+ renderLanes,
+ workInProgress.childLanes
+ );
- if (transitionStart !== null && onTransitionStart != null) {
- transitionStart.forEach(function (transition) {
- return onTransitionStart(transition.name, transition.startTime);
- });
+ if (enableLazyContextPropagation && !_hasChildWork) {
+ // Context changes may not have been propagated yet. We need to do
+ // that now, before we can decide whether to bail out.
+ // TODO: We use `childLanes` as a heuristic for whether there is
+ // remaining work in a few places, including
+ // `bailoutOnAlreadyFinishedWork` and
+ // `updateDehydratedSuspenseComponent`. We should maybe extract this
+ // into a dedicated function.
+ lazilyPropagateParentContextChanges(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ _hasChildWork = includesSomeLane(
+ renderLanes,
+ workInProgress.childLanes
+ );
}
- var markerProgress = pendingTransitions.markerProgress;
- var onMarkerProgress = callbacks.onMarkerProgress;
+ if (didSuspendBefore) {
+ if (_hasChildWork) {
+ // If something was in fallback state last time, and we have all the
+ // same children then we're still in progressive loading state.
+ // Something might get unblocked by state updates or retries in the
+ // tree which will affect the tail. So we need to use the normal
+ // path to compute the correct tail.
+ return updateSuspenseListComponent(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ } // If none of the children had any work, that means that none of
+ // them got retried so they'll still be blocked in the same way
+ // as before. We can fast bail out.
- if (onMarkerProgress != null && markerProgress !== null) {
- markerProgress.forEach(function (markerInstance, markerName) {
- if (markerInstance.transitions !== null) {
- // TODO: Clone the suspense object so users can't modify it
- var pending =
- markerInstance.pendingBoundaries !== null
- ? Array.from(markerInstance.pendingBoundaries.values())
- : [];
- markerInstance.transitions.forEach(function (transition) {
- onMarkerProgress(
- transition.name,
- markerName,
- transition.startTime,
- endTime,
- pending
- );
- });
- }
- });
- }
+ workInProgress.flags |= DidCapture;
+ } // If nothing suspended before and we're rendering the same children,
+ // then the tail doesn't matter. Anything new that suspends will work
+ // in the "together" mode, so we can continue from the state we had.
- var markerComplete = pendingTransitions.markerComplete;
- var onMarkerComplete = callbacks.onMarkerComplete;
+ var renderState = workInProgress.memoizedState;
- if (markerComplete !== null && onMarkerComplete != null) {
- markerComplete.forEach(function (transitions, markerName) {
- transitions.forEach(function (transition) {
- onMarkerComplete(
- transition.name,
- markerName,
- transition.startTime,
- endTime
- );
- });
- });
+ if (renderState !== null) {
+ // Reset to the "together" mode in case we've started a different
+ // update in the past but didn't complete it.
+ renderState.rendering = null;
+ renderState.tail = null;
+ renderState.lastEffect = null;
}
- var markerIncomplete = pendingTransitions.markerIncomplete;
- var onMarkerIncomplete = callbacks.onMarkerIncomplete;
-
- if (onMarkerIncomplete != null && markerIncomplete !== null) {
- markerIncomplete.forEach(function (_ref, markerName) {
- var transitions = _ref.transitions,
- aborts = _ref.aborts;
- transitions.forEach(function (transition) {
- var filteredAborts = [];
- aborts.forEach(function (abort) {
- switch (abort.reason) {
- case "marker": {
- filteredAborts.push({
- type: "marker",
- name: abort.name,
- endTime: endTime
- });
- break;
- }
-
- case "suspense": {
- filteredAborts.push({
- type: "suspense",
- name: abort.name,
- endTime: endTime
- });
- break;
- }
- }
- });
+ pushSuspenseListContext(workInProgress, suspenseStackCursor.current);
- if (filteredAborts.length > 0) {
- onMarkerIncomplete(
- transition.name,
- markerName,
- transition.startTime,
- filteredAborts
- );
- }
- });
- });
+ if (_hasChildWork) {
+ break;
+ } else {
+ // If none of the children had any work, that means that none of
+ // them got retried so they'll still be blocked in the same way
+ // as before. We can fast bail out.
+ return null;
}
+ }
- var transitionProgress = pendingTransitions.transitionProgress;
- var onTransitionProgress = callbacks.onTransitionProgress;
+ case OffscreenComponent:
+ case LegacyHiddenComponent: {
+ // Need to check if the tree still needs to be deferred. This is
+ // almost identical to the logic used in the normal update path,
+ // so we'll just enter that. The only difference is we'll bail out
+ // at the next level instead of this one, because the child props
+ // have not changed. Which is fine.
+ // TODO: Probably should refactor `beginWork` to split the bailout
+ // path from the normal path. I'm tempted to do a labeled break here
+ // but I won't :)
+ workInProgress.lanes = NoLanes;
+ return updateOffscreenComponent(current, workInProgress, renderLanes);
+ }
- if (onTransitionProgress != null && transitionProgress !== null) {
- transitionProgress.forEach(function (pending, transition) {
- onTransitionProgress(
- transition.name,
- transition.startTime,
- endTime,
- Array.from(pending.values())
- );
- });
+ case CacheComponent: {
+ {
+ var _cache = current.memoizedState.cache;
+ pushCacheProvider(workInProgress, _cache);
}
- var transitionComplete = pendingTransitions.transitionComplete;
- var onTransitionComplete = callbacks.onTransitionComplete;
+ break;
+ }
- if (transitionComplete !== null && onTransitionComplete != null) {
- transitionComplete.forEach(function (transition) {
- return onTransitionComplete(
- transition.name,
- transition.startTime,
- endTime
- );
- });
+ case TracingMarkerComponent: {
+ if (enableTransitionTracing) {
+ var instance = workInProgress.stateNode;
+
+ if (instance !== null) {
+ pushMarkerInstance(workInProgress, instance);
+ }
}
}
}
-} // For every tracing marker, store a pointer to it. We will later access it
-// to get the set of suspense boundaries that need to resolve before the
-// tracing marker can be logged as complete
-// This code lives separate from the ReactFiberTransition code because
-// we push and pop on the tracing marker, not the suspense boundary
-var markerInstanceStack = createCursor(null);
-function pushRootMarkerInstance(workInProgress) {
- if (enableTransitionTracing) {
- // On the root, every transition gets mapped to it's own map of
- // suspense boundaries. The transition is marked as complete when
- // the suspense boundaries map is empty. We do this because every
- // transition completes at different times and depends on different
- // suspense boundaries to complete. We store all the transitions
- // along with its map of suspense boundaries in the root incomplete
- // transitions map. Each entry in this map functions like a tracing
- // marker does, so we can push it onto the marker instance stack
- var transitions = getWorkInProgressTransitions();
- var root = workInProgress.stateNode;
+ return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+}
- if (transitions !== null) {
- transitions.forEach(function (transition) {
- if (!root.incompleteTransitions.has(transition)) {
- var markerInstance = {
- tag: TransitionRoot,
- transitions: new Set([transition]),
- pendingBoundaries: null,
- aborts: null,
- name: null
- };
- root.incompleteTransitions.set(transition, markerInstance);
- }
- });
+function beginWork$1(current, workInProgress, renderLanes) {
+ {
+ if (workInProgress._debugNeedsRemount && current !== null) {
+ // This will restart the begin phase with a new fiber.
+ return remountFiber(
+ current,
+ workInProgress,
+ createFiberFromTypeAndProps(
+ workInProgress.type,
+ workInProgress.key,
+ workInProgress.pendingProps,
+ workInProgress._debugOwner || null,
+ workInProgress.mode,
+ workInProgress.lanes
+ )
+ );
}
+ }
- var markerInstances = []; // For ever transition on the suspense boundary, we push the transition
- // along with its map of pending suspense boundaries onto the marker
- // instance stack.
+ if (current !== null) {
+ var oldProps = current.memoizedProps;
+ var newProps = workInProgress.pendingProps;
- root.incompleteTransitions.forEach(function (markerInstance) {
- markerInstances.push(markerInstance);
- });
- push(markerInstanceStack, markerInstances, workInProgress);
- }
-}
-function popRootMarkerInstance(workInProgress) {
- if (enableTransitionTracing) {
- pop(markerInstanceStack, workInProgress);
- }
-}
-function pushMarkerInstance(workInProgress, markerInstance) {
- if (enableTransitionTracing) {
- if (markerInstanceStack.current === null) {
- push(markerInstanceStack, [markerInstance], workInProgress);
+ if (
+ oldProps !== newProps ||
+ hasContextChanged() || // Force a re-render if the implementation changed due to hot reload:
+ workInProgress.type !== current.type
+ ) {
+ // If props or context changed, mark the fiber as having performed work.
+ // This may be unset if the props are determined to be equal later (memo).
+ didReceiveUpdate = true;
} else {
- push(
- markerInstanceStack,
- markerInstanceStack.current.concat(markerInstance),
- workInProgress
+ // Neither props nor legacy context changes. Check if there's a pending
+ // update or context change.
+ var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
+ current,
+ renderLanes
+ );
+
+ if (
+ !hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there
+ // may not be work scheduled on `current`, so we check for this flag.
+ (workInProgress.flags & DidCapture) === NoFlags$1
+ ) {
+ // No pending updates or context. Bail out now.
+ didReceiveUpdate = false;
+ return attemptEarlyBailoutIfNoScheduledUpdate(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ }
+
+ if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags$1) {
+ // This is a special case that only exists for legacy mode.
+ // See https://github.com/facebook/react/pull/19216.
+ didReceiveUpdate = true;
+ } else {
+ // An update was scheduled on this fiber, but there are no new props
+ // nor legacy context. Set this to false. If an update queue or context
+ // consumer produces a changed value, it will set this to true. Otherwise,
+ // the component will assume the children have not changed and bail out.
+ didReceiveUpdate = false;
+ }
+ }
+ } else {
+ didReceiveUpdate = false;
+
+ if (getIsHydrating() && isForkedChild(workInProgress)) {
+ // Check if this child belongs to a list of muliple children in
+ // its parent.
+ //
+ // In a true multi-threaded implementation, we would render children on
+ // parallel threads. This would represent the beginning of a new render
+ // thread for this subtree.
+ //
+ // We only use this for id generation during hydration, which is why the
+ // logic is located in this special branch.
+ var slotIndex = workInProgress.index;
+ var numberOfForks = getForksAtLevel();
+ pushTreeId(workInProgress, numberOfForks, slotIndex);
+ }
+ } // Before entering the begin phase, clear pending update priority.
+ // TODO: This assumes that we're about to evaluate the component and process
+ // the update queue. However, there's an exception: SimpleMemoComponent
+ // sometimes bails out later in the begin phase. This indicates that we should
+ // move this assignment out of the common path and into each branch.
+
+ workInProgress.lanes = NoLanes;
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent: {
+ return mountIndeterminateComponent(
+ current,
+ workInProgress,
+ workInProgress.type,
+ renderLanes
+ );
+ }
+
+ case LazyComponent: {
+ var elementType = workInProgress.elementType;
+ return mountLazyComponent(
+ current,
+ workInProgress,
+ elementType,
+ renderLanes
+ );
+ }
+
+ case FunctionComponent: {
+ var Component = workInProgress.type;
+ var unresolvedProps = workInProgress.pendingProps;
+ var resolvedProps =
+ workInProgress.elementType === Component
+ ? unresolvedProps
+ : resolveDefaultProps(Component, unresolvedProps);
+ return updateFunctionComponent(
+ current,
+ workInProgress,
+ Component,
+ resolvedProps,
+ renderLanes
+ );
+ }
+
+ case ClassComponent: {
+ var _Component = workInProgress.type;
+ var _unresolvedProps = workInProgress.pendingProps;
+
+ var _resolvedProps =
+ workInProgress.elementType === _Component
+ ? _unresolvedProps
+ : resolveDefaultProps(_Component, _unresolvedProps);
+
+ return updateClassComponent(
+ current,
+ workInProgress,
+ _Component,
+ _resolvedProps,
+ renderLanes
);
}
- }
-}
-function popMarkerInstance(workInProgress) {
- if (enableTransitionTracing) {
- pop(markerInstanceStack, workInProgress);
- }
-}
-function getMarkerInstances() {
- if (enableTransitionTracing) {
- return markerInstanceStack.current;
- }
- return null;
-}
+ case HostRoot:
+ return updateHostRoot(current, workInProgress, renderLanes);
-var ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner; // A special exception that's used to unwind the stack when an update flows
-// into a dehydrated boundary.
+ case HostHoistable: {
+ return updateHostHoistable(current, workInProgress);
+ }
-var SelectiveHydrationException = new Error(
- "This is not a real error. It's an implementation detail of React's " +
- "selective hydration feature. If this leaks into userspace, it's a bug in " +
- "React. Please file an issue."
-);
-var didReceiveUpdate = false;
-var didWarnAboutBadClass;
-var didWarnAboutModulePatternComponent;
-var didWarnAboutContextTypeOnFunctionComponent;
-var didWarnAboutGetDerivedStateOnFunctionComponent;
-var didWarnAboutFunctionRefs;
-var didWarnAboutReassigningProps;
-var didWarnAboutRevealOrder;
-var didWarnAboutTailOptions;
-var didWarnAboutDefaultPropsOnFunctionComponent;
+ // eslint-disable-next-line no-fallthrough
-{
- didWarnAboutBadClass = {};
- didWarnAboutModulePatternComponent = {};
- didWarnAboutContextTypeOnFunctionComponent = {};
- didWarnAboutGetDerivedStateOnFunctionComponent = {};
- didWarnAboutFunctionRefs = {};
- didWarnAboutReassigningProps = false;
- didWarnAboutRevealOrder = {};
- didWarnAboutTailOptions = {};
- didWarnAboutDefaultPropsOnFunctionComponent = {};
-}
+ case HostSingleton: {
+ return updateHostSingleton(current, workInProgress, renderLanes);
+ }
-function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
- if (current === null) {
- // If this is a fresh new component that hasn't been rendered yet, we
- // won't update its child set by applying minimal side-effects. Instead,
- // we will add them all to the child before it gets rendered. That means
- // we can optimize this reconciliation pass by not tracking side-effects.
- workInProgress.child = mountChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderLanes
- );
- } else {
- // If the current child is the same as the work in progress, it means that
- // we haven't yet started any work on these children. Therefore, we use
- // the clone algorithm to create a copy of all the current children.
- // If we had any progressed work already, that is invalid at this point so
- // let's throw it out.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- current.child,
- nextChildren,
- renderLanes
- );
- }
-}
+ // eslint-disable-next-line no-fallthrough
-function forceUnmountCurrentAndReconcile(
- current,
- workInProgress,
- nextChildren,
- renderLanes
-) {
- // This function is fork of reconcileChildren. It's used in cases where we
- // want to reconcile without matching against the existing set. This has the
- // effect of all current children being unmounted; even if the type and key
- // are the same, the old child is unmounted and a new child is created.
- //
- // To do this, we're going to go through the reconcile algorithm twice. In
- // the first pass, we schedule a deletion for all the current children by
- // passing null.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- current.child,
- null,
- renderLanes
- ); // In the second pass, we mount the new children. The trick here is that we
- // pass null in place of where we usually pass the current child set. This has
- // the effect of remounting all children regardless of whether their
- // identities match.
+ case HostComponent:
+ return updateHostComponent$1(current, workInProgress, renderLanes);
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderLanes
- );
-}
+ case HostText:
+ return updateHostText$1(current, workInProgress);
-function updateForwardRef(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
-) {
- // TODO: current can be non-null here even if the component
- // hasn't yet mounted. This happens after the first render suspends.
- // We'll need to figure out if this is fine or can cause issues.
- {
- if (workInProgress.type !== workInProgress.elementType) {
- // Lazy component props can't be validated in createElement
- // because they're only guaranteed to be resolved here.
- var innerPropTypes = Component.propTypes;
+ case SuspenseComponent:
+ return updateSuspenseComponent(current, workInProgress, renderLanes);
- if (innerPropTypes) {
- checkPropTypes(
- innerPropTypes,
- nextProps, // Resolved props
- "prop",
- getComponentNameFromType(Component)
- );
- }
- }
- }
+ case HostPortal:
+ return updatePortalComponent(current, workInProgress, renderLanes);
- var render = Component.render;
- var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent
+ case ForwardRef: {
+ var type = workInProgress.type;
+ var _unresolvedProps2 = workInProgress.pendingProps;
- var nextChildren;
- var hasId;
- prepareToReadContext(workInProgress, renderLanes);
+ var _resolvedProps2 =
+ workInProgress.elementType === type
+ ? _unresolvedProps2
+ : resolveDefaultProps(type, _unresolvedProps2);
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
- }
+ return updateForwardRef(
+ current,
+ workInProgress,
+ type,
+ _resolvedProps2,
+ renderLanes
+ );
+ }
- {
- ReactCurrentOwner$2.current = workInProgress;
- setIsRendering(true);
- nextChildren = renderWithHooks(
- current,
- workInProgress,
- render,
- nextProps,
- ref,
- renderLanes
- );
- hasId = checkDidRenderIdHook();
- setIsRendering(false);
- }
+ case Fragment:
+ return updateFragment(current, workInProgress, renderLanes);
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- }
+ case Mode:
+ return updateMode(current, workInProgress, renderLanes);
- if (current !== null && !didReceiveUpdate) {
- bailoutHooks(current, workInProgress, renderLanes);
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
- }
+ case Profiler:
+ return updateProfiler(current, workInProgress, renderLanes);
- if (getIsHydrating() && hasId) {
- pushMaterializedTreeId(workInProgress);
- } // React DevTools reads this flag.
+ case ContextProvider:
+ return updateContextProvider(current, workInProgress, renderLanes);
- workInProgress.flags |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ case ContextConsumer:
+ return updateContextConsumer(current, workInProgress, renderLanes);
-function updateMemoComponent(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
-) {
- if (current === null) {
- var type = Component.type;
+ case MemoComponent: {
+ var _type2 = workInProgress.type;
+ var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.
- if (
- isSimpleFunctionComponent(type) &&
- Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either.
- Component.defaultProps === undefined
- ) {
- var resolvedType = type;
+ var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
{
- resolvedType = resolveFunctionForHotReloading(type);
- } // If this is a plain function component without default props,
- // and with only the default shallow comparison, we upgrade it
- // to a SimpleMemoComponent to allow fast path updates.
-
- workInProgress.tag = SimpleMemoComponent;
- workInProgress.type = resolvedType;
+ if (workInProgress.type !== workInProgress.elementType) {
+ var outerPropTypes = _type2.propTypes;
- {
- validateFunctionComponentInDev(workInProgress, type);
+ if (outerPropTypes) {
+ checkPropTypes(
+ outerPropTypes,
+ _resolvedProps3, // Resolved for outer only
+ "prop",
+ getComponentNameFromType(_type2)
+ );
+ }
+ }
}
+ _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
+ return updateMemoComponent(
+ current,
+ workInProgress,
+ _type2,
+ _resolvedProps3,
+ renderLanes
+ );
+ }
+
+ case SimpleMemoComponent: {
return updateSimpleMemoComponent(
current,
workInProgress,
- resolvedType,
- nextProps,
+ workInProgress.type,
+ workInProgress.pendingProps,
renderLanes
);
}
- {
- var innerPropTypes = type.propTypes;
+ case IncompleteClassComponent: {
+ var _Component2 = workInProgress.type;
+ var _unresolvedProps4 = workInProgress.pendingProps;
- if (innerPropTypes) {
- // Inner memo component props aren't currently validated in createElement.
- // We could move it there, but we'd still need this for lazy code path.
- checkPropTypes(
- innerPropTypes,
- nextProps, // Resolved props
- "prop",
- getComponentNameFromType(type)
- );
- }
+ var _resolvedProps4 =
+ workInProgress.elementType === _Component2
+ ? _unresolvedProps4
+ : resolveDefaultProps(_Component2, _unresolvedProps4);
- if (Component.defaultProps !== undefined) {
- var componentName = getComponentNameFromType(type) || "Unknown";
+ return mountIncompleteClassComponent(
+ current,
+ workInProgress,
+ _Component2,
+ _resolvedProps4,
+ renderLanes
+ );
+ }
- if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
- error(
- "%s: Support for defaultProps will be removed from memo components " +
- "in a future major release. Use JavaScript default parameters instead.",
- componentName
- );
+ case SuspenseListComponent: {
+ return updateSuspenseListComponent(current, workInProgress, renderLanes);
+ }
- didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
- }
+ case ScopeComponent: {
+ {
+ return updateScopeComponent(current, workInProgress, renderLanes);
}
}
- var child = createFiberFromTypeAndProps(
- Component.type,
- null,
- nextProps,
- workInProgress,
- workInProgress.mode,
- renderLanes
- );
- child.ref = workInProgress.ref;
- child.return = workInProgress;
- workInProgress.child = child;
- return child;
- }
-
- {
- var _type = Component.type;
- var _innerPropTypes = _type.propTypes;
-
- if (_innerPropTypes) {
- // Inner memo component props aren't currently validated in createElement.
- // We could move it there, but we'd still need this for lazy code path.
- checkPropTypes(
- _innerPropTypes,
- nextProps, // Resolved props
- "prop",
- getComponentNameFromType(_type)
- );
+ case OffscreenComponent: {
+ return updateOffscreenComponent(current, workInProgress, renderLanes);
}
- }
-
- var currentChild = current.child; // This is always exactly one child
- var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
- current,
- renderLanes
- );
+ case LegacyHiddenComponent: {
+ {
+ return updateLegacyHiddenComponent(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ }
+ }
- if (!hasScheduledUpdateOrContext) {
- // This will be the props with resolved defaultProps,
- // unlike current.memoizedProps which will be the unresolved ones.
- var prevProps = currentChild.memoizedProps; // Default to shallow comparison
+ case CacheComponent: {
+ {
+ return updateCacheComponent(current, workInProgress, renderLanes);
+ }
+ }
- var compare = Component.compare;
- compare = compare !== null ? compare : shallowEqual;
+ case TracingMarkerComponent: {
+ if (enableTransitionTracing) {
+ return updateTracingMarkerComponent(
+ current,
+ workInProgress,
+ renderLanes
+ );
+ }
- if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ break;
}
- } // React DevTools reads this flag.
+ }
- workInProgress.flags |= PerformedWork;
- var newChild = createWorkInProgress(currentChild, nextProps);
- newChild.ref = workInProgress.ref;
- newChild.return = workInProgress;
- workInProgress.child = newChild;
- return newChild;
+ throw new Error(
+ "Unknown unit of work tag (" +
+ workInProgress.tag +
+ "). This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
}
-function updateSimpleMemoComponent(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
-) {
- // TODO: current can be non-null here even if the component
- // hasn't yet mounted. This happens when the inner render suspends.
- // We'll need to figure out if this is fine or can cause issues.
- {
- if (workInProgress.type !== workInProgress.elementType) {
- // Lazy component props can't be validated in createElement
- // because they're only guaranteed to be resolved here.
- var outerMemoType = workInProgress.elementType;
-
- if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
- // We warn when you define propTypes on lazy()
- // so let's just skip over it to find memo() outer wrapper.
- // Inner props for memo are validated later.
- var lazyComponent = outerMemoType;
- var payload = lazyComponent._payload;
- var init = lazyComponent._init;
-
- try {
- outerMemoType = init(payload);
- } catch (x) {
- outerMemoType = null;
- } // Inner propTypes will be validated in the function component path.
+var valueCursor = createCursor(null);
+var rendererCursorDEV;
- var outerPropTypes = outerMemoType && outerMemoType.propTypes;
+{
+ rendererCursorDEV = createCursor(null);
+}
- if (outerPropTypes) {
- checkPropTypes(
- outerPropTypes,
- nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
- "prop",
- getComponentNameFromType(outerMemoType)
- );
- }
- }
- }
- }
+var rendererSigil;
- if (current !== null) {
- var prevProps = current.memoizedProps;
+{
+ // Use this to detect multiple renderers using the same context
+ rendererSigil = {};
+}
- if (
- shallowEqual(prevProps, nextProps) &&
- current.ref === workInProgress.ref && // Prevent bailout if the implementation changed due to hot reload.
- workInProgress.type === current.type
- ) {
- didReceiveUpdate = false; // The props are shallowly equal. Reuse the previous props object, like we
- // would during a normal fiber bailout.
- //
- // We don't have strong guarantees that the props object is referentially
- // equal during updates where we can't bail out anyway — like if the props
- // are shallowly equal, but there's a local state or context update in the
- // same batch.
- //
- // However, as a principle, we should aim to make the behavior consistent
- // across different ways of memoizing a component. For example, React.memo
- // has a different internal Fiber layout if you pass a normal function
- // component (SimpleMemoComponent) versus if you pass a different type
- // like forwardRef (MemoComponent). But this is an implementation detail.
- // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't
- // affect whether the props object is reused during a bailout.
+var currentlyRenderingFiber = null;
+var lastContextDependency = null;
+var lastFullyObservedContext = null;
+var isDisallowedContextReadInDEV = false;
+function resetContextDependencies() {
+ // This is called right before React yields execution, to ensure `readContext`
+ // cannot be called outside the render phase.
+ currentlyRenderingFiber = null;
+ lastContextDependency = null;
+ lastFullyObservedContext = null;
- workInProgress.pendingProps = nextProps = prevProps;
+ {
+ isDisallowedContextReadInDEV = false;
+ }
+}
+function enterDisallowedContextReadInDEV() {
+ {
+ isDisallowedContextReadInDEV = true;
+ }
+}
+function exitDisallowedContextReadInDEV() {
+ {
+ isDisallowedContextReadInDEV = false;
+ }
+}
+function pushProvider(providerFiber, context, nextValue) {
+ {
+ push(valueCursor, context._currentValue, providerFiber);
+ context._currentValue = nextValue;
- if (!checkScheduledUpdateOrContext(current, renderLanes)) {
- // The pending lanes were cleared at the beginning of beginWork. We're
- // about to bail out, but there might be other lanes that weren't
- // included in the current render. Usually, the priority level of the
- // remaining updates is accumulated during the evaluation of the
- // component (i.e. when processing the update queue). But since since
- // we're bailing out early *without* evaluating the component, we need
- // to account for it here, too. Reset to the value of the current fiber.
- // NOTE: This only applies to SimpleMemoComponent, not MemoComponent,
- // because a MemoComponent fiber does not have hooks or an update queue;
- // rather, it wraps around an inner component, which may or may not
- // contains hooks.
- // TODO: Move the reset at in beginWork out of the common path so that
- // this is no longer necessary.
- workInProgress.lanes = current.lanes;
- return bailoutOnAlreadyFinishedWork(
- current,
- workInProgress,
- renderLanes
+ {
+ push(rendererCursorDEV, context._currentRenderer, providerFiber);
+
+ if (
+ context._currentRenderer !== undefined &&
+ context._currentRenderer !== null &&
+ context._currentRenderer !== rendererSigil
+ ) {
+ error(
+ "Detected multiple renderers concurrently rendering the " +
+ "same context provider. This is currently unsupported."
);
- } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags$1) {
- // This is a special case that only exists for legacy mode.
- // See https://github.com/facebook/react/pull/19216.
- didReceiveUpdate = true;
}
+
+ context._currentRenderer = rendererSigil;
}
}
-
- return updateFunctionComponent(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
- );
}
+function popProvider(context, providerFiber) {
+ var currentValue = valueCursor.current;
-function updateOffscreenComponent(current, workInProgress, renderLanes) {
- var nextProps = workInProgress.pendingProps;
- var nextChildren = nextProps.children;
- var nextIsDetached =
- (workInProgress.stateNode._pendingVisibility & OffscreenDetached) !== 0;
- var prevState = current !== null ? current.memoizedState : null;
- markRef$1(current, workInProgress);
-
- if (
- nextProps.mode === "hidden" ||
- nextProps.mode === "unstable-defer-without-hiding" ||
- nextIsDetached
- ) {
- // Rendering a hidden tree.
- var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags$1;
+ {
+ if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
+ context._currentValue = context._defaultValue;
+ } else {
+ context._currentValue = currentValue;
+ }
- if (didSuspend) {
- // Something suspended inside a hidden tree
- // Include the base lanes from the last render
- var nextBaseLanes =
- prevState !== null
- ? mergeLanes(prevState.baseLanes, renderLanes)
- : renderLanes;
+ {
+ var currentRenderer = rendererCursorDEV.current;
+ pop(rendererCursorDEV, providerFiber);
+ context._currentRenderer = currentRenderer;
+ }
+ }
- if (current !== null) {
- // Reset to the current children
- var currentChild = (workInProgress.child = current.child); // The current render suspended, but there may be other lanes with
- // pending work. We can't read `childLanes` from the current Offscreen
- // fiber because we reset it when it was deferred; however, we can read
- // the pending lanes from the child fibers.
+ pop(valueCursor, providerFiber);
+}
+function scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {
+ // Update the child lanes of all the ancestors, including the alternates.
+ var node = parent;
- var currentChildLanes = NoLanes;
+ while (node !== null) {
+ var alternate = node.alternate;
- while (currentChild !== null) {
- currentChildLanes = mergeLanes(
- mergeLanes(currentChildLanes, currentChild.lanes),
- currentChild.childLanes
- );
- currentChild = currentChild.sibling;
- }
+ if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
+ node.childLanes = mergeLanes(node.childLanes, renderLanes);
- var lanesWeJustAttempted = nextBaseLanes;
- var remainingChildLanes = removeLanes(
- currentChildLanes,
- lanesWeJustAttempted
- );
- workInProgress.childLanes = remainingChildLanes;
- } else {
- workInProgress.childLanes = NoLanes;
- workInProgress.child = null;
+ if (alternate !== null) {
+ alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
}
+ } else if (
+ alternate !== null &&
+ !isSubsetOfLanes(alternate.childLanes, renderLanes)
+ ) {
+ alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
+ } else;
- return deferHiddenOffscreenComponent(
- current,
- workInProgress,
- nextBaseLanes,
- renderLanes
+ if (node === propagationRoot) {
+ break;
+ }
+
+ node = node.return;
+ }
+
+ {
+ if (node !== propagationRoot) {
+ error(
+ "Expected to find the propagation root when scheduling context work. " +
+ "This error is likely caused by a bug in React. Please file an issue."
);
}
+ }
+}
+function propagateContextChange(workInProgress, context, renderLanes) {
+ if (enableLazyContextPropagation) {
+ // TODO: This path is only used by Cache components. Update
+ // lazilyPropagateParentContextChanges to look for Cache components so they
+ // can take advantage of lazy propagation.
+ var forcePropagateEntireTree = true;
+ propagateContextChanges(
+ workInProgress,
+ [context],
+ renderLanes,
+ forcePropagateEntireTree
+ );
+ } else {
+ propagateContextChange_eager(workInProgress, context, renderLanes);
+ }
+}
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- // In legacy sync mode, don't defer the subtree. Render it now.
- // TODO: Consider how Offscreen should work with transitions in the future
- var nextState = {
- baseLanes: NoLanes,
- cachePool: null
- };
- workInProgress.memoizedState = nextState;
+function propagateContextChange_eager(workInProgress, context, renderLanes) {
+ // Only used by eager implementation
+ if (enableLazyContextPropagation) {
+ return;
+ }
- {
- // push the cache pool even though we're going to bail out
- // because otherwise there'd be a context mismatch
- if (current !== null) {
- pushTransition(workInProgress, null, null);
- }
- }
+ var fiber = workInProgress.child;
- reuseHiddenContextOnStack(workInProgress);
- pushOffscreenSuspenseHandler(workInProgress);
- } else if (!includesSomeLane(renderLanes, OffscreenLane)) {
- // We're hidden, and we're not rendering at Offscreen. We will bail out
- // and resume this tree later.
- // Schedule this fiber to re-render at Offscreen priority
- workInProgress.lanes = workInProgress.childLanes =
- laneToLanes(OffscreenLane); // Include the base lanes from the last render
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
- var _nextBaseLanes =
- prevState !== null
- ? mergeLanes(prevState.baseLanes, renderLanes)
- : renderLanes;
+ while (fiber !== null) {
+ var nextFiber = void 0; // Visit this fiber.
- return deferHiddenOffscreenComponent(
- current,
- workInProgress,
- _nextBaseLanes,
- renderLanes
- );
- } else {
- // This is the second render. The surrounding visible content has already
- // committed. Now we resume rendering the hidden tree.
- // Rendering at offscreen, so we can clear the base lanes.
- var _nextState = {
- baseLanes: NoLanes,
- cachePool: null
- };
- workInProgress.memoizedState = _nextState;
+ var list = fiber.dependencies;
- if (current !== null) {
- // If the render that spawned this one accessed the cache pool, resume
- // using the same cache. Unless the parent changed, since that means
- // there was a refresh.
- var prevCachePool = prevState !== null ? prevState.cachePool : null; // TODO: Consider if and how Offscreen pre-rendering should
- // be attributed to the transition that spawned it
+ if (list !== null) {
+ nextFiber = fiber.child;
+ var dependency = list.firstContext;
- pushTransition(workInProgress, prevCachePool, null);
- } // Push the lanes that were skipped when we bailed out.
+ while (dependency !== null) {
+ // Check if the context matches.
+ if (dependency.context === context) {
+ // Match! Schedule an update on this fiber.
+ if (fiber.tag === ClassComponent) {
+ // Schedule a force update on the work-in-progress.
+ var lane = pickArbitraryLane(renderLanes);
+ var update = createUpdate(lane);
+ update.tag = ForceUpdate; // TODO: Because we don't have a work-in-progress, this will add the
+ // update to the current fiber, too, which means it will persist even if
+ // this render is thrown away. Since it's a race condition, not sure it's
+ // worth fixing.
+ // Inlined `enqueueUpdate` to remove interleaved update check
- if (prevState !== null) {
- pushHiddenContext(workInProgress, prevState);
- } else {
- reuseHiddenContextOnStack(workInProgress);
- }
+ var updateQueue = fiber.updateQueue;
- pushOffscreenSuspenseHandler(workInProgress);
- }
- } else {
- // Rendering a visible tree.
- if (prevState !== null) {
- // We're going from hidden -> visible.
- var _prevCachePool = null;
+ if (updateQueue === null);
+ else {
+ var sharedQueue = updateQueue.shared;
+ var pending = sharedQueue.pending;
- {
- // If the render that spawned this one accessed the cache pool, resume
- // using the same cache. Unless the parent changed, since that means
- // there was a refresh.
- _prevCachePool = prevState.cachePool;
- }
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
+ }
- var transitions = null;
+ sharedQueue.pending = update;
+ }
+ }
- if (enableTransitionTracing) {
- // We have now gone from hidden to visible, so any transitions should
- // be added to the stack to get added to any Offscreen/suspense children
- var instance = workInProgress.stateNode;
+ fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
+ var alternate = fiber.alternate;
- if (instance !== null && instance._transitions != null) {
- transitions = Array.from(instance._transitions);
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
+ }
+
+ scheduleContextWorkOnParentPath(
+ fiber.return,
+ renderLanes,
+ workInProgress
+ ); // Mark the updated lanes on the list, too.
+
+ list.lanes = mergeLanes(list.lanes, renderLanes); // Since we already found a match, we can stop traversing the
+ // dependency list.
+
+ break;
}
+
+ dependency = dependency.next;
}
+ } else if (fiber.tag === ContextProvider) {
+ // Don't scan deeper if this is a matching provider
+ nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
+ } else if (fiber.tag === DehydratedFragment) {
+ // If a dehydrated suspense boundary is in this subtree, we don't know
+ // if it will have any context consumers in it. The best we can do is
+ // mark it as having updates.
+ var parentSuspense = fiber.return;
- pushTransition(workInProgress, _prevCachePool, transitions); // Push the lanes that were skipped when we bailed out.
+ if (parentSuspense === null) {
+ throw new Error(
+ "We just came from a parent so we must have had a parent. This is a bug in React."
+ );
+ }
- pushHiddenContext(workInProgress, prevState);
- reuseSuspenseHandlerOnStack(workInProgress); // Since we're not hidden anymore, reset the state
+ parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
+ var _alternate = parentSuspense.alternate;
- workInProgress.memoizedState = null;
+ if (_alternate !== null) {
+ _alternate.lanes = mergeLanes(_alternate.lanes, renderLanes);
+ } // This is intentionally passing this fiber as the parent
+ // because we want to schedule this fiber as having work
+ // on its children. We'll use the childLanes on
+ // this fiber to indicate that a context has changed.
+
+ scheduleContextWorkOnParentPath(
+ parentSuspense,
+ renderLanes,
+ workInProgress
+ );
+ nextFiber = fiber.sibling;
} else {
- // We weren't previously hidden, and we still aren't, so there's nothing
- // special to do. Need to push to the stack regardless, though, to avoid
- // a push/pop misalignment.
- {
- // If the render that spawned this one accessed the cache pool, resume
- // using the same cache. Unless the parent changed, since that means
- // there was a refresh.
- if (current !== null) {
- pushTransition(workInProgress, null, null);
+ // Traverse down.
+ nextFiber = fiber.child;
+ }
+
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
+
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
}
- } // We're about to bail out, but we need to push this to the stack anyway
- // to avoid a push/pop misalignment.
- reuseHiddenContextOnStack(workInProgress);
- reuseSuspenseHandlerOnStack(workInProgress);
+ var sibling = nextFiber.sibling;
+
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
+ break;
+ } // No more siblings. Traverse up.
+
+ nextFiber = nextFiber.return;
+ }
}
- }
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
+ fiber = nextFiber;
+ }
}
-function deferHiddenOffscreenComponent(
- current,
+function propagateContextChanges(
workInProgress,
- nextBaseLanes,
- renderLanes
+ contexts,
+ renderLanes,
+ forcePropagateEntireTree
) {
- var nextState = {
- baseLanes: nextBaseLanes,
- // Save the cache pool so we can resume later.
- cachePool: getOffscreenDeferredCache()
- };
- workInProgress.memoizedState = nextState;
-
- {
- // push the cache pool even though we're going to bail out
- // because otherwise there'd be a context mismatch
- if (current !== null) {
- pushTransition(workInProgress, null, null);
- }
- } // We're about to bail out, but we need to push this to the stack anyway
- // to avoid a push/pop misalignment.
-
- reuseHiddenContextOnStack(workInProgress);
- pushOffscreenSuspenseHandler(workInProgress);
-
- if (enableLazyContextPropagation && current !== null) {
- // Since this tree will resume rendering in a separate render, we need
- // to propagate parent contexts now so we don't lose track of which
- // ones changed.
- propagateParentContextChangesToDeferredTree(
- current,
- workInProgress,
- renderLanes
- );
+ // Only used by lazy implementation
+ if (!enableLazyContextPropagation) {
+ return;
}
- return null;
-} // Note: These happen to have identical begin phases, for now. We shouldn't hold
-// ourselves to this constraint, though. If the behavior diverges, we should
-// fork the function.
-
-var updateLegacyHiddenComponent = updateOffscreenComponent;
+ var fiber = workInProgress.child;
-function updateCacheComponent(current, workInProgress, renderLanes) {
- prepareToReadContext(workInProgress, renderLanes);
- var parentCache = readContext(CacheContext);
+ if (fiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ fiber.return = workInProgress;
+ }
- if (current === null) {
- // Initial mount. Request a fresh cache from the pool.
- var freshCache = requestCacheFromPool(renderLanes);
- var initialState = {
- parent: parentCache,
- cache: freshCache
- };
- workInProgress.memoizedState = initialState;
- initializeUpdateQueue(workInProgress);
- pushCacheProvider(workInProgress, freshCache);
- } else {
- // Check for updates
- if (includesSomeLane(current.lanes, renderLanes)) {
- cloneUpdateQueue(current, workInProgress);
- processUpdateQueue(workInProgress, null, null, renderLanes);
- }
+ while (fiber !== null) {
+ var nextFiber = void 0; // Visit this fiber.
- var prevState = current.memoizedState;
- var nextState = workInProgress.memoizedState; // Compare the new parent cache to the previous to see detect there was
- // a refresh.
+ var list = fiber.dependencies;
- if (prevState.parent !== parentCache) {
- // Refresh in parent. Update the parent.
- var derivedState = {
- parent: parentCache,
- cache: parentCache
- }; // Copied from getDerivedStateFromProps implementation. Once the update
- // queue is empty, persist the derived state onto the base state.
+ if (list !== null) {
+ nextFiber = fiber.child;
+ var dep = list.firstContext;
- workInProgress.memoizedState = derivedState;
+ findChangedDep: while (dep !== null) {
+ // Assigning these to constants to help Flow
+ var dependency = dep;
+ var consumer = fiber;
- if (workInProgress.lanes === NoLanes) {
- var updateQueue = workInProgress.updateQueue;
- workInProgress.memoizedState = updateQueue.baseState = derivedState;
- }
+ for (var i = 0; i < contexts.length; i++) {
+ var context = contexts[i]; // Check if the context matches.
+ // TODO: Compare selected values to bail out early.
- pushCacheProvider(workInProgress, parentCache); // No need to propagate a context change because the refreshed parent
- // already did.
- } else {
- // The parent didn't refresh. Now check if this cache did.
- var nextCache = nextState.cache;
- pushCacheProvider(workInProgress, nextCache);
+ if (dependency.context === context) {
+ // Match! Schedule an update on this fiber.
+ // In the lazy implementation, don't mark a dirty flag on the
+ // dependency itself. Not all changes are propagated, so we can't
+ // rely on the propagation function alone to determine whether
+ // something has changed; the consumer will check. In the future, we
+ // could add back a dirty flag as an optimization to avoid double
+ // checking, but until we have selectors it's not really worth
+ // the trouble.
+ consumer.lanes = mergeLanes(consumer.lanes, renderLanes);
+ var alternate = consumer.alternate;
- if (nextCache !== prevState.cache) {
- // This cache refreshed. Propagate a context change.
- propagateContextChange(workInProgress, CacheContext, renderLanes);
- }
- }
- }
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
+ }
- var nextChildren = workInProgress.pendingProps.children;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-} // This should only be called if the name changes
+ scheduleContextWorkOnParentPath(
+ consumer.return,
+ renderLanes,
+ workInProgress
+ );
-function updateTracingMarkerComponent(current, workInProgress, renderLanes) {
- if (!enableTransitionTracing) {
- return null;
- } // TODO: (luna) Only update the tracing marker if it's newly rendered or it's name changed.
- // A tracing marker is only associated with the transitions that rendered
- // or updated it, so we can create a new set of transitions each time
+ if (!forcePropagateEntireTree) {
+ // During lazy propagation, when we find a match, we can defer
+ // propagating changes to the children, because we're going to
+ // visit them during render. We should continue propagating the
+ // siblings, though
+ nextFiber = null;
+ } // Since we already found a match, we can stop traversing the
+ // dependency list.
- if (current === null) {
- var currentTransitions = getPendingTransitions();
+ break findChangedDep;
+ }
+ }
- if (currentTransitions !== null) {
- var markerInstance = {
- tag: TransitionTracingMarker,
- transitions: new Set(currentTransitions),
- pendingBoundaries: null,
- name: workInProgress.pendingProps.name,
- aborts: null
- };
- workInProgress.stateNode = markerInstance; // We call the marker complete callback when all child suspense boundaries resolve.
- // We do this in the commit phase on Offscreen. If the marker has no child suspense
- // boundaries, we need to schedule a passive effect to make sure we call the marker
- // complete callback.
+ dep = dependency.next;
+ }
+ } else if (fiber.tag === DehydratedFragment) {
+ // If a dehydrated suspense boundary is in this subtree, we don't know
+ // if it will have any context consumers in it. The best we can do is
+ // mark it as having updates.
+ var parentSuspense = fiber.return;
- workInProgress.flags |= Passive$1;
- }
- } else {
- {
- if (current.memoizedProps.name !== workInProgress.pendingProps.name) {
- error(
- "Changing the name of a tracing marker after mount is not supported. " +
- "To remount the tracing marker, pass it a new key."
+ if (parentSuspense === null) {
+ throw new Error(
+ "We just came from a parent so we must have had a parent. This is a bug in React."
);
}
- }
- }
- var instance = workInProgress.stateNode;
+ parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
+ var _alternate2 = parentSuspense.alternate;
- if (instance !== null) {
- pushMarkerInstance(workInProgress, instance);
- }
+ if (_alternate2 !== null) {
+ _alternate2.lanes = mergeLanes(_alternate2.lanes, renderLanes);
+ } // This is intentionally passing this fiber as the parent
+ // because we want to schedule this fiber as having work
+ // on its children. We'll use the childLanes on
+ // this fiber to indicate that a context has changed.
- var nextChildren = workInProgress.pendingProps.children;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ scheduleContextWorkOnParentPath(
+ parentSuspense,
+ renderLanes,
+ workInProgress
+ );
+ nextFiber = null;
+ } else {
+ // Traverse down.
+ nextFiber = fiber.child;
+ }
-function updateFragment(current, workInProgress, renderLanes) {
- var nextChildren = workInProgress.pendingProps;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ if (nextFiber !== null) {
+ // Set the return pointer of the child to the work-in-progress fiber.
+ nextFiber.return = fiber;
+ } else {
+ // No child. Traverse to next sibling.
+ nextFiber = fiber;
-function updateMode(current, workInProgress, renderLanes) {
- var nextChildren = workInProgress.pendingProps.children;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
-function updateProfiler(current, workInProgress, renderLanes) {
- {
- workInProgress.flags |= Update;
+ var sibling = nextFiber.sibling;
- {
- // Reset effect durations for the next eventual effect phase.
- // These are reset during render to allow the DevTools commit hook a chance to read them,
- var stateNode = workInProgress.stateNode;
- stateNode.effectDuration = 0;
- stateNode.passiveEffectDuration = 0;
+ if (sibling !== null) {
+ // Set the return pointer of the sibling to the work-in-progress fiber.
+ sibling.return = nextFiber.return;
+ nextFiber = sibling;
+ break;
+ } // No more siblings. Traverse up.
+
+ nextFiber = nextFiber.return;
+ }
}
- }
- var nextProps = workInProgress.pendingProps;
- var nextChildren = nextProps.children;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
+ fiber = nextFiber;
+ }
}
-function markRef$1(current, workInProgress) {
- var ref = workInProgress.ref;
+function lazilyPropagateParentContextChanges(
+ current,
+ workInProgress,
+ renderLanes
+) {
+ var forcePropagateEntireTree = false;
+ propagateParentContextChanges(
+ current,
+ workInProgress,
+ renderLanes,
+ forcePropagateEntireTree
+ );
+} // Used for propagating a deferred tree (Suspense, Offscreen). We must propagate
+// to the entire subtree, because we won't revisit it until after the current
+// render has completed, at which point we'll have lost track of which providers
+// have changed.
- if (
- (current === null && ref !== null) ||
- (current !== null && current.ref !== ref)
- ) {
- // Schedule a Ref effect
- workInProgress.flags |= Ref;
- workInProgress.flags |= RefStatic;
- }
+function propagateParentContextChangesToDeferredTree(
+ current,
+ workInProgress,
+ renderLanes
+) {
+ var forcePropagateEntireTree = true;
+ propagateParentContextChanges(
+ current,
+ workInProgress,
+ renderLanes,
+ forcePropagateEntireTree
+ );
}
-function updateFunctionComponent(
+function propagateParentContextChanges(
current,
workInProgress,
- Component,
- nextProps,
- renderLanes
+ renderLanes,
+ forcePropagateEntireTree
) {
- {
- if (workInProgress.type !== workInProgress.elementType) {
- // Lazy component props can't be validated in createElement
- // because they're only guaranteed to be resolved here.
- var innerPropTypes = Component.propTypes;
+ if (!enableLazyContextPropagation) {
+ return;
+ } // Collect all the parent providers that changed. Since this is usually small
+ // number, we use an Array instead of Set.
- if (innerPropTypes) {
- checkPropTypes(
- innerPropTypes,
- nextProps, // Resolved props
- "prop",
- getComponentNameFromType(Component)
- );
+ var contexts = null;
+ var parent = workInProgress;
+ var isInsidePropagationBailout = false;
+
+ while (parent !== null) {
+ if (!isInsidePropagationBailout) {
+ if ((parent.flags & NeedsPropagation) !== NoFlags$1) {
+ isInsidePropagationBailout = true;
+ } else if ((parent.flags & DidPropagateContext) !== NoFlags$1) {
+ break;
}
}
- }
- var context;
+ if (parent.tag === ContextProvider) {
+ var currentParent = parent.alternate;
- {
- var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
- context = getMaskedContext(workInProgress, unmaskedContext);
- }
+ if (currentParent === null) {
+ throw new Error("Should have a current fiber. This is a bug in React.");
+ }
- var nextChildren;
- var hasId;
- prepareToReadContext(workInProgress, renderLanes);
+ var oldProps = currentParent.memoizedProps;
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
+ if (oldProps !== null) {
+ var providerType = parent.type;
+ var context = providerType._context;
+ var newProps = parent.pendingProps;
+ var newValue = newProps.value;
+ var oldValue = oldProps.value;
+
+ if (!objectIs(newValue, oldValue)) {
+ if (contexts !== null) {
+ contexts.push(context);
+ } else {
+ contexts = [context];
+ }
+ }
+ }
+ }
+
+ parent = parent.return;
}
- {
- ReactCurrentOwner$2.current = workInProgress;
- setIsRendering(true);
- nextChildren = renderWithHooks(
- current,
+ if (contexts !== null) {
+ // If there were any changed providers, search through the children and
+ // propagate their changes.
+ propagateContextChanges(
workInProgress,
- Component,
- nextProps,
- context,
- renderLanes
+ contexts,
+ renderLanes,
+ forcePropagateEntireTree
);
- hasId = checkDidRenderIdHook();
- setIsRendering(false);
- }
+ } // This is an optimization so that we only propagate once per subtree. If a
+ // deeply nested child bails out, and it calls this propagation function, it
+ // uses this flag to know that the remaining ancestor providers have already
+ // been propagated.
+ //
+ // NOTE: This optimization is only necessary because we sometimes enter the
+ // begin phase of nodes that don't have any work scheduled on them —
+ // specifically, the siblings of a node that _does_ have scheduled work. The
+ // siblings will bail out and call this function again, even though we already
+ // propagated content changes to it and its subtree. So we use this flag to
+ // mark that the parent providers already propagated.
+ //
+ // Unfortunately, though, we need to ignore this flag when we're inside a
+ // tree whose context propagation was deferred — that's what the
+ // `NeedsPropagation` flag is for.
+ //
+ // If we could instead bail out before entering the siblings' begin phase,
+ // then we could remove both `DidPropagateContext` and `NeedsPropagation`.
+ // Consider this as part of the next refactor to the fiber tree structure.
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- }
+ workInProgress.flags |= DidPropagateContext;
+}
- if (current !== null && !didReceiveUpdate) {
- bailoutHooks(current, workInProgress, renderLanes);
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
- }
+function checkIfContextChanged(currentDependencies) {
+ if (!enableLazyContextPropagation) {
+ return false;
+ } // Iterate over the current dependencies to see if something changed. This
+ // only gets called if props and state has already bailed out, so it's a
+ // relatively uncommon path, except at the root of a changed subtree.
+ // Alternatively, we could move these comparisons into `readContext`, but
+ // that's a much hotter path, so I think this is an appropriate trade off.
- if (getIsHydrating() && hasId) {
- pushMaterializedTreeId(workInProgress);
- } // React DevTools reads this flag.
+ var dependency = currentDependencies.firstContext;
- workInProgress.flags |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ while (dependency !== null) {
+ var context = dependency.context;
+ var newValue = context._currentValue;
+ var oldValue = dependency.memoizedValue;
-function replayFunctionComponent(
- current,
- workInProgress,
- nextProps,
- Component,
- renderLanes
-) {
- // This function is used to replay a component that previously suspended,
- // after its data resolves. It's a simplified version of
- // updateFunctionComponent that reuses the hooks from the previous attempt.
- var context;
+ if (!objectIs(newValue, oldValue)) {
+ return true;
+ }
- {
- var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
- context = getMaskedContext(workInProgress, unmaskedContext);
+ dependency = dependency.next;
}
- prepareToReadContext(workInProgress, renderLanes);
-
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
- }
+ return false;
+}
+function prepareToReadContext(workInProgress, renderLanes) {
+ currentlyRenderingFiber = workInProgress;
+ lastContextDependency = null;
+ lastFullyObservedContext = null;
+ var dependencies = workInProgress.dependencies;
- var nextChildren = replaySuspendedComponentWithHooks(
- current,
- workInProgress,
- Component,
- nextProps,
- context
- );
- var hasId = checkDidRenderIdHook();
+ if (dependencies !== null) {
+ if (enableLazyContextPropagation) {
+ // Reset the work-in-progress list
+ dependencies.firstContext = null;
+ } else {
+ var firstContext = dependencies.firstContext;
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- }
+ if (firstContext !== null) {
+ if (includesSomeLane(dependencies.lanes, renderLanes)) {
+ // Context list has a pending update. Mark that this fiber performed work.
+ markWorkInProgressReceivedUpdate();
+ } // Reset the work-in-progress list
- if (current !== null && !didReceiveUpdate) {
- bailoutHooks(current, workInProgress, renderLanes);
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ dependencies.firstContext = null;
+ }
+ }
}
-
- if (getIsHydrating() && hasId) {
- pushMaterializedTreeId(workInProgress);
- } // React DevTools reads this flag.
-
- workInProgress.flags |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
}
-
-function updateClassComponent(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
-) {
+function readContext(context) {
{
- // This is used by DevTools to force a boundary to error.
- switch (shouldError(workInProgress)) {
- case false: {
- var _instance = workInProgress.stateNode;
- var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack.
- // Is there a better way to do this?
-
- var tempInstance = new ctor(
- workInProgress.memoizedProps,
- _instance.context
- );
- var state = tempInstance.state;
+ // This warning would fire if you read context inside a Hook like useMemo.
+ // Unlike the class check below, it's not enforced in production for perf.
+ if (isDisallowedContextReadInDEV) {
+ error(
+ "Context can only be read while React is rendering. " +
+ "In classes, you can read it in the render method or getDerivedStateFromProps. " +
+ "In function components, you can read it directly in the function body, but not " +
+ "inside Hooks like useReducer() or useMemo()."
+ );
+ }
+ }
- _instance.updater.enqueueSetState(_instance, state, null);
+ return readContextForConsumer(currentlyRenderingFiber, context);
+}
+function readContextDuringReconcilation(consumer, context, renderLanes) {
+ if (currentlyRenderingFiber === null) {
+ prepareToReadContext(consumer, renderLanes);
+ }
- break;
- }
+ return readContextForConsumer(consumer, context);
+}
- case true: {
- workInProgress.flags |= DidCapture;
- workInProgress.flags |= ShouldCapture; // eslint-disable-next-line react-internal/prod-error-codes
+function readContextForConsumer(consumer, context) {
+ var value = context._currentValue;
- var error$1 = new Error("Simulated error coming from DevTools");
- var lane = pickArbitraryLane(renderLanes);
- workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state
+ if (lastFullyObservedContext === context);
+ else {
+ var contextItem = {
+ context: context,
+ memoizedValue: value,
+ next: null
+ };
- var update = createClassErrorUpdate(
- workInProgress,
- createCapturedValueAtFiber(error$1, workInProgress),
- lane
+ if (lastContextDependency === null) {
+ if (consumer === null) {
+ throw new Error(
+ "Context can only be read while React is rendering. " +
+ "In classes, you can read it in the render method or getDerivedStateFromProps. " +
+ "In function components, you can read it directly in the function body, but not " +
+ "inside Hooks like useReducer() or useMemo()."
);
- enqueueCapturedUpdate(workInProgress, update);
- break;
- }
- }
+ } // This is the first dependency for this component. Create a new list.
- if (workInProgress.type !== workInProgress.elementType) {
- // Lazy component props can't be validated in createElement
- // because they're only guaranteed to be resolved here.
- var innerPropTypes = Component.propTypes;
+ lastContextDependency = contextItem;
+ consumer.dependencies = {
+ lanes: NoLanes,
+ firstContext: contextItem
+ };
- if (innerPropTypes) {
- checkPropTypes(
- innerPropTypes,
- nextProps, // Resolved props
- "prop",
- getComponentNameFromType(Component)
- );
+ if (enableLazyContextPropagation) {
+ consumer.flags |= NeedsPropagation;
}
+ } else {
+ // Append a new context item.
+ lastContextDependency = lastContextDependency.next = contextItem;
}
- } // 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;
+ return value;
+}
- if (isContextProvider(Component)) {
- hasContext = true;
- pushContextProvider(workInProgress);
- } else {
- hasContext = false;
- }
+// replace it with a lightweight shim that only has the features we use.
- prepareToReadContext(workInProgress, renderLanes);
- var instance = workInProgress.stateNode;
- var shouldUpdate;
+var AbortControllerLocal =
+ typeof AbortController !== "undefined"
+ ? AbortController // $FlowFixMe[missing-this-annot]
+ : function AbortControllerShim() {
+ var listeners = [];
+ var signal = (this.signal = {
+ aborted: false,
+ addEventListener: function (type, listener) {
+ listeners.push(listener);
+ }
+ });
- if (instance === null) {
- resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress); // In the initial pass we might need to construct the instance.
+ this.abort = function () {
+ signal.aborted = true;
+ listeners.forEach(function (listener) {
+ return listener();
+ });
+ };
+ }; // Intentionally not named imports because Rollup would
+// use dynamic dispatch for CommonJS interop named imports.
- constructClassInstance(workInProgress, Component, nextProps);
- mountClassInstance(workInProgress, Component, nextProps, renderLanes);
- shouldUpdate = true;
- } else if (current === null) {
- // In a resume, we'll already have an instance we can reuse.
- shouldUpdate = resumeMountClassInstance(
- workInProgress,
- Component,
- nextProps,
- renderLanes
- );
- } else {
- shouldUpdate = updateClassInstance(
- current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
- );
- }
+var scheduleCallback$1 = Scheduler.unstable_scheduleCallback,
+ NormalPriority = Scheduler.unstable_NormalPriority;
+var CacheContext = {
+ $$typeof: REACT_CONTEXT_TYPE,
+ // We don't use Consumer/Provider for Cache components. So we'll cheat.
+ Consumer: null,
+ Provider: null,
+ // We'll initialize these at the root.
+ _currentValue: null,
+ _currentValue2: null,
+ _threadCount: 0,
+ _defaultValue: null,
+ _globalName: null
+};
- var nextUnitOfWork = finishClassComponent(
- current,
- workInProgress,
- Component,
- shouldUpdate,
- hasContext,
- renderLanes
- );
+{
+ CacheContext._currentRenderer = null;
+ CacheContext._currentRenderer2 = null;
+} // Creates a new empty Cache instance with a ref-count of 0. The caller is responsible
+// for retaining the cache once it is in use (retainCache), and releasing the cache
+// once it is no longer needed (releaseCache).
+function createCache() {
+ var cache = {
+ controller: new AbortControllerLocal(),
+ data: new Map(),
+ refCount: 0
+ };
+ return cache;
+}
+function retainCache(cache) {
{
- var inst = workInProgress.stateNode;
-
- if (shouldUpdate && inst.props !== nextProps) {
- if (!didWarnAboutReassigningProps) {
- error(
- "It looks like %s is reassigning its own `this.props` while rendering. " +
- "This is not supported and can lead to confusing bugs.",
- getComponentNameFromFiber(workInProgress) || "a component"
- );
- }
-
- didWarnAboutReassigningProps = true;
+ if (cache.controller.signal.aborted) {
+ warn(
+ "A cache instance was retained after it was already freed. " +
+ "This likely indicates a bug in React."
+ );
}
}
- return nextUnitOfWork;
-}
+ cache.refCount++;
+} // Cleanup a cache instance, potentially freeing it if there are no more references
-function finishClassComponent(
- current,
- workInProgress,
- Component,
- shouldUpdate,
- hasContext,
- renderLanes
-) {
- // Refs should update even if shouldComponentUpdate returns false
- markRef$1(current, workInProgress);
- var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags$1;
+function releaseCache(cache) {
+ cache.refCount--;
- if (!shouldUpdate && !didCaptureError) {
- // Context providers should defer to sCU for rendering
- if (hasContext) {
- invalidateContextProvider(workInProgress, Component, false);
+ {
+ if (cache.refCount < 0) {
+ warn(
+ "A cache instance was released after it was already freed. " +
+ "This likely indicates a bug in React."
+ );
}
-
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
}
- var instance = workInProgress.stateNode; // Rerender
-
- ReactCurrentOwner$2.current = workInProgress;
- var nextChildren;
+ if (cache.refCount === 0) {
+ scheduleCallback$1(NormalPriority, function () {
+ cache.controller.abort();
+ });
+ }
+}
+function pushCacheProvider(workInProgress, cache) {
+ pushProvider(workInProgress, CacheContext, cache);
+}
+function popCacheProvider(workInProgress, cache) {
+ popProvider(CacheContext, workInProgress);
+}
- if (
- didCaptureError &&
- typeof Component.getDerivedStateFromError !== "function"
- ) {
- // If we captured an error, but getDerivedStateFromError is not defined,
- // unmount all the children. componentDidCatch will schedule an update to
- // re-render a fallback. This is temporary until we migrate everyone to
- // the new API.
- // TODO: Warn in a future release.
- nextChildren = null;
+var ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig;
+var NoTransition = null;
+function requestCurrentTransition() {
+ return ReactCurrentBatchConfig$2.transition;
+} // When retrying a Suspense/Offscreen boundary, we restore the cache that was
+// used during the previous render by placing it here, on the stack.
- {
- stopProfilerTimerIfRunning();
- }
- } else {
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
- }
+var resumedCache = createCursor(null); // During the render/synchronous commit phase, we don't actually process the
+// transitions. Therefore, we want to lazily combine transitions. Instead of
+// comparing the arrays of transitions when we combine them and storing them
+// and filtering out the duplicates, we will instead store the unprocessed transitions
+// in an array and actually filter them in the passive phase.
- {
- setIsRendering(true);
- nextChildren = instance.render();
+var transitionStack = createCursor(null);
- if (workInProgress.mode & StrictLegacyMode) {
- setIsStrictModeForDevtools(true);
+function peekCacheFromPool() {
+ // If we're rendering inside a Suspense boundary that is currently hidden,
+ // we should use the same cache that we used during the previous render, if
+ // one exists.
- try {
- instance.render();
- } finally {
- setIsStrictModeForDevtools(false);
- }
- }
+ var cacheResumedFromPreviousRender = resumedCache.current;
- setIsRendering(false);
- }
+ if (cacheResumedFromPreviousRender !== null) {
+ return cacheResumedFromPreviousRender;
+ } // Otherwise, check the root's cache pool.
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- }
- } // React DevTools reads this flag.
+ var root = getWorkInProgressRoot();
+ var cacheFromRootCachePool = root.pooledCache;
+ return cacheFromRootCachePool;
+}
- workInProgress.flags |= PerformedWork;
+function requestCacheFromPool(renderLanes) {
+ // Similar to previous function, except if there's not already a cache in the
+ // pool, we allocate a new one.
+ var cacheFromPool = peekCacheFromPool();
- if (current !== null && didCaptureError) {
- // If we're recovering from an error, reconcile without reusing any of
- // the existing children. Conceptually, the normal children and the children
- // that are shown on error are two different sets, so we shouldn't reuse
- // normal children even if their identities match.
- forceUnmountCurrentAndReconcile(
- current,
- workInProgress,
- nextChildren,
- renderLanes
- );
- } else {
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- } // Memoize state using the values we just used to render.
- // TODO: Restructure so we never read values from the instance.
+ if (cacheFromPool !== null) {
+ return cacheFromPool;
+ } // Create a fresh cache and add it to the root cache pool. A cache can have
+ // multiple owners:
+ // - A cache pool that lives on the FiberRoot. This is where all fresh caches
+ // are originally created (TODO: except during refreshes, until we implement
+ // this correctly). The root takes ownership immediately when the cache is
+ // created. Conceptually, root.pooledCache is an Option> (owned),
+ // and the return value of this function is a &Arc (borrowed).
+ // - One of several fiber types: host root, cache boundary, suspense
+ // component. These retain and release in the commit phase.
- workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it.
+ var root = getWorkInProgressRoot();
+ var freshCache = createCache();
+ root.pooledCache = freshCache;
+ retainCache(freshCache);
- if (hasContext) {
- invalidateContextProvider(workInProgress, Component, true);
+ if (freshCache !== null) {
+ root.pooledCacheLanes |= renderLanes;
}
- return workInProgress.child;
+ return freshCache;
}
-
-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);
+function pushRootTransition(workInProgress, root, renderLanes) {
+ if (enableTransitionTracing) {
+ var rootTransitions = getWorkInProgressTransitions();
+ push(transitionStack, rootTransitions, workInProgress);
}
-
- pushHostContainer(workInProgress, root.containerInfo);
}
-
-function updateHostRoot(current, workInProgress, renderLanes) {
- pushHostRootContext(workInProgress);
-
- if (current === null) {
- throw new Error("Should have a current fiber. This is a bug in React.");
- }
-
- var nextProps = workInProgress.pendingProps;
- var prevState = workInProgress.memoizedState;
- var prevChildren = prevState.element;
- cloneUpdateQueue(current, workInProgress);
- processUpdateQueue(workInProgress, nextProps, null, renderLanes);
- var nextState = workInProgress.memoizedState;
- var root = workInProgress.stateNode;
- pushRootTransition(workInProgress);
-
+function popRootTransition(workInProgress, root, renderLanes) {
if (enableTransitionTracing) {
- pushRootMarkerInstance(workInProgress);
+ pop(transitionStack, workInProgress);
}
-
+}
+function pushTransition(
+ offscreenWorkInProgress,
+ prevCachePool,
+ newTransitions
+) {
{
- var nextCache = nextState.cache;
- pushCacheProvider(workInProgress, nextCache);
-
- if (nextCache !== prevState.cache) {
- // The root cache refreshed.
- propagateContextChange(workInProgress, CacheContext, renderLanes);
+ if (prevCachePool === null) {
+ push(resumedCache, resumedCache.current, offscreenWorkInProgress);
+ } else {
+ push(resumedCache, prevCachePool.pool, offscreenWorkInProgress);
}
- } // Caution: React DevTools currently depends on this property
- // being called "element".
-
- var nextChildren = nextState.element;
-
- if (prevState.isDehydrated) {
- // This is a hydration root whose shell has not yet hydrated. We should
- // attempt to hydrate.
- // Flip isDehydrated to false to indicate that when this render
- // finishes, the root will no longer be dehydrated.
- var overrideState = {
- element: nextChildren,
- isDehydrated: false,
- cache: nextState.cache
- };
- var updateQueue = workInProgress.updateQueue; // `baseState` can always be the last state because the root doesn't
- // have reducer functions so it doesn't need rebasing.
-
- updateQueue.baseState = overrideState;
- workInProgress.memoizedState = overrideState;
-
- if (workInProgress.flags & ForceClientRender) {
- // Something errored during a previous attempt to hydrate the shell, so we
- // forced a client render.
- var recoverableError = createCapturedValueAtFiber(
- new Error(
- "There was an error while hydrating. Because the error happened outside " +
- "of a Suspense boundary, the entire root will switch to " +
- "client rendering."
- ),
- workInProgress
- );
- return mountHostRootWithoutHydrating(
- current,
- workInProgress,
- nextChildren,
- renderLanes,
- recoverableError
- );
- } else if (nextChildren !== prevChildren) {
- var _recoverableError = createCapturedValueAtFiber(
- new Error(
- "This root received an early update, before anything was able " +
- "hydrate. Switched the entire root to client rendering."
- ),
- workInProgress
- );
+ }
- return mountHostRootWithoutHydrating(
- current,
- workInProgress,
- nextChildren,
- renderLanes,
- _recoverableError
- );
+ if (enableTransitionTracing) {
+ if (transitionStack.current === null) {
+ push(transitionStack, newTransitions, offscreenWorkInProgress);
+ } else if (newTransitions === null) {
+ push(transitionStack, transitionStack.current, offscreenWorkInProgress);
} else {
- // The outermost shell has not hydrated yet. Start hydrating.
- enterHydrationState(workInProgress);
-
- {
- var mutableSourceEagerHydrationData =
- root.mutableSourceEagerHydrationData;
-
- if (mutableSourceEagerHydrationData != null) {
- for (var i = 0; i < mutableSourceEagerHydrationData.length; i += 2) {
- var mutableSource = mutableSourceEagerHydrationData[i];
- var version = mutableSourceEagerHydrationData[i + 1];
- setWorkInProgressVersion(mutableSource, version);
- }
- }
- }
-
- var child = mountChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderLanes
+ push(
+ transitionStack,
+ transitionStack.current.concat(newTransitions),
+ offscreenWorkInProgress
);
- workInProgress.child = child;
- var node = child;
-
- while (node) {
- // Mark each child as hydrating. This is a fast path to know whether this
- // tree is part of a hydrating tree. This is used to determine if a child
- // node has fully mounted yet, and for scheduling event replaying.
- // Conceptually this is similar to Placement in that a new subtree is
- // inserted into the React tree here. It just happens to not need DOM
- // mutations because it already exists.
- node.flags = (node.flags & ~Placement) | Hydrating;
- node = node.sibling;
- }
}
- } else {
- // Root is not dehydrated. Either this is a client-only root, or it
- // already hydrated.
- resetHydrationState();
-
- if (nextChildren === prevChildren) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ }
+}
+function popTransition(workInProgress, current) {
+ if (current !== null) {
+ if (enableTransitionTracing) {
+ pop(transitionStack, workInProgress);
}
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
+ {
+ pop(resumedCache, workInProgress);
+ }
}
-
- return workInProgress.child;
}
+function getPendingTransitions() {
+ if (!enableTransitionTracing) {
+ return null;
+ }
-function mountHostRootWithoutHydrating(
- current,
- workInProgress,
- nextChildren,
- renderLanes,
- recoverableError
-) {
- // Revert to client rendering.
- resetHydrationState();
- queueHydrationError(recoverableError);
- workInProgress.flags |= ForceClientRender;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
+ return transitionStack.current;
}
+function getSuspendedCache() {
+ // cache that would have been used to render fresh data during this render,
+ // if there was any, so that we can resume rendering with the same cache when
+ // we receive more data.
-function updateHostComponent$1(current, workInProgress, renderLanes) {
- pushHostContext(workInProgress);
+ var cacheFromPool = peekCacheFromPool();
- if (current === null) {
- tryToClaimNextHydratableInstance(workInProgress);
+ if (cacheFromPool === null) {
+ return null;
}
- var type = workInProgress.type;
- var nextProps = workInProgress.pendingProps;
- var prevProps = current !== null ? current.memoizedProps : null;
- var nextChildren = nextProps.children;
- var isDirectTextChild = shouldSetTextContent(type, nextProps);
+ return {
+ // We must also save the parent, so that when we resume we can detect
+ // a refresh.
+ parent: CacheContext._currentValue,
+ pool: cacheFromPool
+ };
+}
+function getOffscreenDeferredCache() {
+ var cacheFromPool = peekCacheFromPool();
- if (isDirectTextChild) {
- // We special case a direct text child of a host node. This is a common
- // case. We won't handle it as a reified child. We will instead handle
- // this in the host environment that also has access to this prop. That
- // avoids allocating another HostText fiber and traversing it.
- nextChildren = null;
- } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
- // If we're switching from a direct text child to a normal child, or to
- // empty, we need to schedule the text content to be reset.
- workInProgress.flags |= ContentReset;
+ if (cacheFromPool === null) {
+ return null;
}
- markRef$1(current, workInProgress);
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
+ return {
+ // We must also store the parent, so that when we resume we can detect
+ // a refresh.
+ parent: CacheContext._currentValue,
+ pool: cacheFromPool
+ };
}
-function updateHostHoistable(current, workInProgress, renderLanes) {
- markRef$1(current, workInProgress);
- var currentProps = current === null ? null : current.memoizedProps;
- var resource = (workInProgress.memoizedState = getResource(
- workInProgress.type,
- currentProps,
- workInProgress.pendingProps
- ));
-
- if (current === null) {
- if (!getIsHydrating() && resource === null) {
- // This is not a Resource Hoistable and we aren't hydrating so we construct the instance.
- workInProgress.stateNode = createHoistableInstance(
- workInProgress.type,
- workInProgress.pendingProps,
- getRootHostContainer(),
- workInProgress
- );
- }
- } // Resources never have reconciler managed children. It is possible for
- // the host implementation of getResource to consider children in the
- // resource construction but they will otherwise be discarded. In practice
- // this precludes all but the simplest children and Host specific warnings
- // should be implemented to warn when children are passsed when otherwise not
- // expected
-
- return null;
+function getSuspenseFallbackChild(fiber) {
+ return fiber.child.sibling.child;
}
-function updateHostSingleton(current, workInProgress, renderLanes) {
- pushHostContext(workInProgress);
-
- if (current === null) {
- claimHydratableSingleton(workInProgress);
- }
+var emptyObject = {};
- var nextChildren = workInProgress.pendingProps.children;
+function collectScopedNodes(node, fn, scopedNodes) {
+ {
+ if (node.tag === HostComponent) {
+ var type = node.type,
+ memoizedProps = node.memoizedProps,
+ stateNode = node.stateNode;
+ var instance = getPublicInstance(stateNode);
- if (current === null && !getIsHydrating()) {
- // Similar to Portals we append Singleton children in the commit phase. So we
- // Track insertions even on mount.
- // TODO: Consider unifying this with how the root works.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderLanes
- );
- } else {
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- }
+ if (
+ instance !== null &&
+ fn(type, memoizedProps || emptyObject, instance) === true
+ ) {
+ scopedNodes.push(instance);
+ }
+ }
- markRef$1(current, workInProgress);
- return workInProgress.child;
-}
+ var child = node.child;
-function updateHostText$1(current, workInProgress) {
- if (current === null) {
- tryToClaimNextHydratableTextInstance(workInProgress);
- } // Nothing to do here. This is terminal. We'll do the completion step
- // immediately after.
+ if (isFiberSuspenseAndTimedOut(node)) {
+ child = getSuspenseFallbackChild(node);
+ }
- return null;
+ if (child !== null) {
+ collectScopedNodesFromChildren(child, fn, scopedNodes);
+ }
+ }
}
-function mountLazyComponent(
- _current,
- workInProgress,
- elementType,
- renderLanes
-) {
- resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
- var props = workInProgress.pendingProps;
- var lazyComponent = elementType;
- var payload = lazyComponent._payload;
- var init = lazyComponent._init;
- var Component = init(payload); // Store the unwrapped component in the type.
-
- workInProgress.type = Component;
- var resolvedTag = (workInProgress.tag = resolveLazyComponentTag(Component));
- var resolvedProps = resolveDefaultProps(Component, props);
- var child;
+function collectFirstScopedNode(node, fn) {
+ {
+ if (node.tag === HostComponent) {
+ var type = node.type,
+ memoizedProps = node.memoizedProps,
+ stateNode = node.stateNode;
+ var instance = getPublicInstance(stateNode);
- switch (resolvedTag) {
- case FunctionComponent: {
- {
- validateFunctionComponentInDev(workInProgress, Component);
- workInProgress.type = Component =
- resolveFunctionForHotReloading(Component);
+ if (instance !== null && fn(type, memoizedProps, instance) === true) {
+ return instance;
}
-
- child = updateFunctionComponent(
- null,
- workInProgress,
- Component,
- resolvedProps,
- renderLanes
- );
- return child;
}
- case ClassComponent: {
- {
- workInProgress.type = Component =
- resolveClassForHotReloading(Component);
- }
+ var child = node.child;
- child = updateClassComponent(
- null,
- workInProgress,
- Component,
- resolvedProps,
- renderLanes
- );
- return child;
+ if (isFiberSuspenseAndTimedOut(node)) {
+ child = getSuspenseFallbackChild(node);
}
- case ForwardRef: {
- {
- workInProgress.type = Component =
- resolveForwardRefForHotReloading(Component);
- }
-
- child = updateForwardRef(
- null,
- workInProgress,
- Component,
- resolvedProps,
- renderLanes
- );
- return child;
+ if (child !== null) {
+ return collectFirstScopedNodeFromChildren(child, fn);
}
+ }
- case MemoComponent: {
- {
- if (workInProgress.type !== workInProgress.elementType) {
- var outerPropTypes = Component.propTypes;
+ return null;
+}
- if (outerPropTypes) {
- checkPropTypes(
- outerPropTypes,
- resolvedProps, // Resolved for outer only
- "prop",
- getComponentNameFromType(Component)
- );
- }
- }
- }
+function collectScopedNodesFromChildren(startingChild, fn, scopedNodes) {
+ var child = startingChild;
- child = updateMemoComponent(
- null,
- workInProgress,
- Component,
- resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
- renderLanes
- );
- return child;
+ while (child !== null) {
+ collectScopedNodes(child, fn, scopedNodes);
+ child = child.sibling;
+ }
+}
+
+function collectFirstScopedNodeFromChildren(startingChild, fn) {
+ var child = startingChild;
+
+ while (child !== null) {
+ var scopedNode = collectFirstScopedNode(child, fn);
+
+ if (scopedNode !== null) {
+ return scopedNode;
}
+
+ child = child.sibling;
}
- var hint = "";
+ return null;
+}
- {
- if (
- Component !== null &&
- typeof Component === "object" &&
- Component.$$typeof === REACT_LAZY_TYPE
- ) {
- hint = " Did you wrap a component in React.lazy() more than once?";
+function collectNearestContextValues(node, context, childContextValues) {
+ if (node.tag === ContextProvider && node.type._context === context) {
+ var contextValue = node.memoizedProps.value;
+ childContextValues.push(contextValue);
+ } else {
+ var child = node.child;
+
+ if (isFiberSuspenseAndTimedOut(node)) {
+ child = getSuspenseFallbackChild(node);
}
- } // This message intentionally doesn't mention ForwardRef or MemoComponent
- // because the fact that it's a separate type of work is an
- // implementation detail.
- throw new Error(
- "Element type is invalid. Received a promise that resolves to: " +
- Component +
- ". " +
- ("Lazy element type must resolve to a class or function." + hint)
- );
+ if (child !== null) {
+ collectNearestChildContextValues(child, context, childContextValues);
+ }
+ }
}
-function mountIncompleteClassComponent(
- _current,
- workInProgress,
- Component,
- nextProps,
- renderLanes
+function collectNearestChildContextValues(
+ startingChild,
+ context,
+ childContextValues
) {
- resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); // Promote the fiber to a class and try rendering again.
+ var child = startingChild;
- workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent`
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
+ while (child !== null) {
+ collectNearestContextValues(child, context, childContextValues);
+ child = child.sibling;
+ }
+}
- var hasContext;
+function DO_NOT_USE_queryAllNodes(fn) {
+ var currentFiber = getInstanceFromScope(this);
- if (isContextProvider(Component)) {
- hasContext = true;
- pushContextProvider(workInProgress);
- } else {
- hasContext = false;
+ if (currentFiber === null) {
+ return null;
}
- prepareToReadContext(workInProgress, renderLanes);
- constructClassInstance(workInProgress, Component, nextProps);
- mountClassInstance(workInProgress, Component, nextProps, renderLanes);
- return finishClassComponent(
- null,
- workInProgress,
- Component,
- true,
- hasContext,
- renderLanes
- );
+ var child = currentFiber.child;
+ var scopedNodes = [];
+
+ if (child !== null) {
+ collectScopedNodesFromChildren(child, fn, scopedNodes);
+ }
+
+ return scopedNodes.length === 0 ? null : scopedNodes;
}
-function mountIndeterminateComponent(
- _current,
- workInProgress,
- Component,
- renderLanes
-) {
- resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
- var props = workInProgress.pendingProps;
- var context;
+function DO_NOT_USE_queryFirstNode(fn) {
+ var currentFiber = getInstanceFromScope(this);
- {
- var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
- context = getMaskedContext(workInProgress, unmaskedContext);
+ if (currentFiber === null) {
+ return null;
}
- prepareToReadContext(workInProgress, renderLanes);
- var value;
- var hasId;
+ var child = currentFiber.child;
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
+ if (child !== null) {
+ return collectFirstScopedNodeFromChildren(child, fn);
}
- {
- if (
- Component.prototype &&
- typeof Component.prototype.render === "function"
- ) {
- var componentName = getComponentNameFromType(Component) || "Unknown";
+ return null;
+}
- if (!didWarnAboutBadClass[componentName]) {
- error(
- "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
- "This is likely to cause errors. Change %s to extend React.Component instead.",
- componentName,
- componentName
- );
+function containsNode(node) {
+ var fiber = getInstanceFromNode$1(node);
- didWarnAboutBadClass[componentName] = true;
- }
+ while (fiber !== null) {
+ if (fiber.tag === ScopeComponent && fiber.stateNode === this) {
+ return true;
}
- if (workInProgress.mode & StrictLegacyMode) {
- ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
- }
+ fiber = fiber.return;
+ }
- setIsRendering(true);
- ReactCurrentOwner$2.current = workInProgress;
- value = renderWithHooks(
- null,
- workInProgress,
- Component,
- props,
- context,
- renderLanes
- );
- hasId = checkDidRenderIdHook();
- setIsRendering(false);
+ return false;
+}
+
+function getChildContextValues(context) {
+ var currentFiber = getInstanceFromScope(this);
+
+ if (currentFiber === null) {
+ return [];
}
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- } // React DevTools reads this flag.
+ var child = currentFiber.child;
+ var childContextValues = [];
- workInProgress.flags |= PerformedWork;
+ if (child !== null) {
+ collectNearestChildContextValues(child, context, childContextValues);
+ }
- {
- // Support for module components is deprecated and is removed behind a flag.
- // Whether or not it would crash later, we want to show a good message in DEV first.
- if (
- typeof value === "object" &&
- value !== null &&
- typeof value.render === "function" &&
- value.$$typeof === undefined
- ) {
- var _componentName = getComponentNameFromType(Component) || "Unknown";
+ return childContextValues;
+}
- if (!didWarnAboutModulePatternComponent[_componentName]) {
- error(
- "The <%s /> component appears to be a function component that returns a class instance. " +
- "Change %s to a class that extends React.Component instead. " +
- "If you can't use a class try assigning the prototype on the function as a workaround. " +
- "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " +
- "cannot be called with `new` by React.",
- _componentName,
- _componentName,
- _componentName
- );
+function createScopeInstance() {
+ return {
+ DO_NOT_USE_queryAllNodes: DO_NOT_USE_queryAllNodes,
+ DO_NOT_USE_queryFirstNode: DO_NOT_USE_queryFirstNode,
+ containsNode: containsNode,
+ getChildContextValues: getChildContextValues
+ };
+}
- didWarnAboutModulePatternComponent[_componentName] = true;
+function markUpdate(workInProgress) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // a PlacementAndUpdate.
+ workInProgress.flags |= Update;
+}
+
+function markRef(workInProgress) {
+ workInProgress.flags |= Ref | RefStatic;
+}
+
+function appendAllChildren(
+ parent,
+ workInProgress,
+ needsVisibilityToggle,
+ isHidden
+) {
+ {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal || node.tag === HostSingleton);
+ else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
}
+
+ if (node === workInProgress) {
+ return;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
+
+ while (node.sibling === null) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (node.return === null || node.return === workInProgress) {
+ return;
+ }
+
+ node = node.return;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
+
+ node.sibling.return = node.return;
+ node = node.sibling;
}
}
+} // An unfortunate fork of appendAllChildren because we have two different parent types.
+function updateHostComponent(current, workInProgress, type, newProps) {
{
- // Proceed under the assumption that this is a function component
- workInProgress.tag = FunctionComponent;
+ // 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 (getIsHydrating() && hasId) {
- pushMaterializedTreeId(workInProgress);
- }
+ if (oldProps === newProps) {
+ // In mutation mode, this is sufficient for a bailout because
+ // we won't touch this node even if children changed.
+ return;
+ } // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
- reconcileChildren(null, workInProgress, value, renderLanes);
+ var instance = workInProgress.stateNode;
+ var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host
+ // component is hitting the resume path. Figure out why. Possibly
+ // related to `hidden`.
- {
- validateFunctionComponentInDev(workInProgress, Component);
- }
+ var updatePayload = prepareUpdate(
+ instance,
+ type,
+ oldProps,
+ newProps,
+ currentHostContext
+ ); // TODO: Type this specific to this type of component.
- return workInProgress.child;
+ 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);
+ }
}
}
-function validateFunctionComponentInDev(workInProgress, Component) {
+function updateHostText(current, workInProgress, oldText, newText) {
{
- if (Component) {
- if (Component.childContextTypes) {
- error(
- "%s(...): childContextTypes cannot be defined on a function component.",
- Component.displayName || Component.name || "Component"
- );
- }
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
}
+ }
+}
- if (workInProgress.ref !== null) {
- var info = "";
- var ownerName = getCurrentFiberOwnerNameInDevOrNull();
-
- if (ownerName) {
- info += "\n\nCheck the render method of `" + ownerName + "`.";
- }
+function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {
+ if (getIsHydrating()) {
+ // If we're hydrating, we should consume as many items as we can
+ // so we don't leave any behind.
+ return;
+ }
- var warningKey = ownerName || "";
- var debugSource = workInProgress._debugSource;
+ switch (renderState.tailMode) {
+ case "hidden": {
+ // Any insertions at the end of the tail list after this point
+ // should be invisible. If there are already mounted boundaries
+ // anything before them are not considered for collapsing.
+ // Therefore we need to go through the whole tail to find if
+ // there are any.
+ var tailNode = renderState.tail;
+ var lastTailNode = null;
- if (debugSource) {
- warningKey = debugSource.fileName + ":" + debugSource.lineNumber;
- }
+ while (tailNode !== null) {
+ if (tailNode.alternate !== null) {
+ lastTailNode = tailNode;
+ }
- if (!didWarnAboutFunctionRefs[warningKey]) {
- didWarnAboutFunctionRefs[warningKey] = true;
+ tailNode = tailNode.sibling;
+ } // Next we're simply going to delete all insertions after the
+ // last rendered item.
- error(
- "Function components cannot be given refs. " +
- "Attempts to access this ref will fail. " +
- "Did you mean to use React.forwardRef()?%s",
- info
- );
+ if (lastTailNode === null) {
+ // All remaining items in the tail are insertions.
+ renderState.tail = null;
+ } else {
+ // Detach the insertion after the last node that was already
+ // inserted.
+ lastTailNode.sibling = null;
}
+
+ break;
}
- if (Component.defaultProps !== undefined) {
- var componentName = getComponentNameFromType(Component) || "Unknown";
+ case "collapsed": {
+ // Any insertions at the end of the tail list after this point
+ // should be invisible. If there are already mounted boundaries
+ // anything before them are not considered for collapsing.
+ // Therefore we need to go through the whole tail to find if
+ // there are any.
+ var _tailNode = renderState.tail;
+ var _lastTailNode = null;
- if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
- error(
- "%s: Support for defaultProps will be removed from function components " +
- "in a future major release. Use JavaScript default parameters instead.",
- componentName
- );
+ while (_tailNode !== null) {
+ if (_tailNode.alternate !== null) {
+ _lastTailNode = _tailNode;
+ }
- didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
+ _tailNode = _tailNode.sibling;
+ } // Next we're simply going to delete all insertions after the
+ // last rendered item.
+
+ if (_lastTailNode === null) {
+ // All remaining items in the tail are insertions.
+ if (!hasRenderedATailFallback && renderState.tail !== null) {
+ // We suspended during the head. We want to show at least one
+ // row at the tail. So we'll keep on and cut off the rest.
+ renderState.tail.sibling = null;
+ } else {
+ renderState.tail = null;
+ }
+ } else {
+ // Detach the insertion after the last node that was already
+ // inserted.
+ _lastTailNode.sibling = null;
}
+
+ break;
}
+ }
+}
- if (typeof Component.getDerivedStateFromProps === "function") {
- var _componentName3 = getComponentNameFromType(Component) || "Unknown";
+function bubbleProperties(completedWork) {
+ var didBailout =
+ completedWork.alternate !== null &&
+ completedWork.alternate.child === completedWork.child;
+ var newChildLanes = NoLanes;
+ var subtreeFlags = NoFlags$1;
- if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) {
- error(
- "%s: Function components do not support getDerivedStateFromProps.",
- _componentName3
+ if (!didBailout) {
+ // Bubble up the earliest expiration time.
+ if ((completedWork.mode & ProfileMode) !== NoMode) {
+ // In profiling mode, resetChildExpirationTime is also used to reset
+ // profiler durations.
+ var actualDuration = completedWork.actualDuration;
+ var treeBaseDuration = completedWork.selfBaseDuration;
+ var child = completedWork.child;
+
+ while (child !== null) {
+ newChildLanes = mergeLanes(
+ newChildLanes,
+ mergeLanes(child.lanes, child.childLanes)
);
+ subtreeFlags |= child.subtreeFlags;
+ subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will
+ // only be updated if work is done on the fiber (i.e. it doesn't bailout).
+ // When work is done, it should bubble to the parent's actualDuration. If
+ // the fiber has not been cloned though, (meaning no work was done), then
+ // this value will reflect the amount of time spent working on a previous
+ // render. In that case it should not bubble. We determine whether it was
+ // cloned by comparing the child pointer.
+ // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
- didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true;
+ actualDuration += child.actualDuration; // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+
+ treeBaseDuration += child.treeBaseDuration;
+ child = child.sibling;
}
- }
- if (
- typeof Component.contextType === "object" &&
- Component.contextType !== null
- ) {
- var _componentName4 = getComponentNameFromType(Component) || "Unknown";
+ completedWork.actualDuration = actualDuration;
+ completedWork.treeBaseDuration = treeBaseDuration;
+ } else {
+ var _child = completedWork.child;
- if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) {
- error(
- "%s: Function components do not support contextType.",
- _componentName4
+ while (_child !== null) {
+ newChildLanes = mergeLanes(
+ newChildLanes,
+ mergeLanes(_child.lanes, _child.childLanes)
);
+ subtreeFlags |= _child.subtreeFlags;
+ subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code
+ // smell because it assumes the commit phase is never concurrent with
+ // the render phase. Will address during refactor to alternate model.
- didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true;
+ _child.return = completedWork;
+ _child = _child.sibling;
}
}
- }
-}
-var SUSPENDED_MARKER = {
- dehydrated: null,
- treeContext: null,
- retryLane: NoLane
-};
-
-function mountSuspenseOffscreenState(renderLanes) {
- return {
- baseLanes: renderLanes,
- cachePool: getSuspendedCache()
- };
-}
-
-function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) {
- var cachePool = null;
+ completedWork.subtreeFlags |= subtreeFlags;
+ } else {
+ // Bubble up the earliest expiration time.
+ if ((completedWork.mode & ProfileMode) !== NoMode) {
+ // In profiling mode, resetChildExpirationTime is also used to reset
+ // profiler durations.
+ var _treeBaseDuration = completedWork.selfBaseDuration;
+ var _child2 = completedWork.child;
- {
- var prevCachePool = prevOffscreenState.cachePool;
+ while (_child2 !== null) {
+ newChildLanes = mergeLanes(
+ newChildLanes,
+ mergeLanes(_child2.lanes, _child2.childLanes)
+ ); // "Static" flags share the lifetime of the fiber/hook they belong to,
+ // so we should bubble those up even during a bailout. All the other
+ // flags have a lifetime only of a single render + commit, so we should
+ // ignore them.
- if (prevCachePool !== null) {
- var parentCache = CacheContext._currentValue;
+ subtreeFlags |= _child2.subtreeFlags & StaticMask;
+ subtreeFlags |= _child2.flags & StaticMask; // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
- if (prevCachePool.parent !== parentCache) {
- // Detected a refresh in the parent. This overrides any previously
- // suspended cache.
- cachePool = {
- parent: parentCache,
- pool: parentCache
- };
- } else {
- // We can reuse the cache from last time. The only thing that would have
- // overridden it is a parent refresh, which we checked for above.
- cachePool = prevCachePool;
+ _treeBaseDuration += _child2.treeBaseDuration;
+ _child2 = _child2.sibling;
}
+
+ completedWork.treeBaseDuration = _treeBaseDuration;
} else {
- // If there's no previous cache pool, grab the current one.
- cachePool = getSuspendedCache();
- }
- }
+ var _child3 = completedWork.child;
- return {
- baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
- cachePool: cachePool
- };
-} // TODO: Probably should inline this back
+ while (_child3 !== null) {
+ newChildLanes = mergeLanes(
+ newChildLanes,
+ mergeLanes(_child3.lanes, _child3.childLanes)
+ ); // "Static" flags share the lifetime of the fiber/hook they belong to,
+ // so we should bubble those up even during a bailout. All the other
+ // flags have a lifetime only of a single render + commit, so we should
+ // ignore them.
-function shouldRemainOnFallback(current, workInProgress, renderLanes) {
- // If we're already showing a fallback, there are cases where we need to
- // remain on that fallback regardless of whether the content has resolved.
- // For example, SuspenseList coordinates when nested content appears.
- if (current !== null) {
- var suspenseState = current.memoizedState;
+ subtreeFlags |= _child3.subtreeFlags & StaticMask;
+ subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code
+ // smell because it assumes the commit phase is never concurrent with
+ // the render phase. Will address during refactor to alternate model.
- if (suspenseState === null) {
- // Currently showing content. Don't hide it, even if ForceSuspenseFallback
- // is true. More precise name might be "ForceRemainSuspenseFallback".
- // Note: This is a factoring smell. Can't remain on a fallback if there's
- // no fallback to remain on.
- return false;
+ _child3.return = completedWork;
+ _child3 = _child3.sibling;
+ }
}
- } // Not currently showing content. Consult the Suspense context.
- var suspenseContext = suspenseStackCursor.current;
- return hasSuspenseListContext(suspenseContext, ForceSuspenseFallback);
-}
+ completedWork.subtreeFlags |= subtreeFlags;
+ }
-function getRemainingWorkInPrimaryTree(current, renderLanes) {
- // TODO: Should not remove render lanes that were pinged during this render
- return removeLanes(current.childLanes, renderLanes);
+ completedWork.childLanes = newChildLanes;
+ return didBailout;
}
-function updateSuspenseComponent(current, workInProgress, renderLanes) {
- var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend.
-
- {
- if (shouldSuspend(workInProgress)) {
- workInProgress.flags |= DidCapture;
- }
+function completeDehydratedSuspenseBoundary(
+ current,
+ workInProgress,
+ nextState
+) {
+ if (
+ hasUnhydratedTailNodes() &&
+ (workInProgress.mode & ConcurrentMode) !== NoMode &&
+ (workInProgress.flags & DidCapture) === NoFlags$1
+ ) {
+ warnIfUnhydratedTailNodes(workInProgress);
+ resetHydrationState();
+ workInProgress.flags |= ForceClientRender | Incomplete | ShouldCapture;
+ return false;
}
- var showFallback = false;
- var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags$1;
-
- if (didSuspend || shouldRemainOnFallback(current)) {
- // Something in this boundary's subtree already suspended. Switch to
- // rendering the fallback children.
- showFallback = true;
- workInProgress.flags &= ~DidCapture;
- } // OK, the next part is confusing. We're about to reconcile the Suspense
- // boundary's children. This involves some custom reconciliation logic. Two
- // main reasons this is so complicated.
- //
- // First, Legacy Mode has different semantics for backwards compatibility. The
- // primary tree will commit in an inconsistent state, so when we do the
- // second pass to render the fallback, we do some exceedingly, uh, clever
- // hacks to make that not totally break. Like transferring effects and
- // deletions from hidden tree. In Concurrent Mode, it's much simpler,
- // because we bailout on the primary tree completely and leave it in its old
- // state, no effects. Same as what we do for Offscreen (except that
- // Offscreen doesn't have the first render pass).
- //
- // Second is hydration. During hydration, the Suspense fiber has a slightly
- // different layout, where the child points to a dehydrated fragment, which
- // contains the DOM rendered by the server.
- //
- // Third, even if you set all that aside, Suspense is like error boundaries in
- // that we first we try to render one tree, and if that fails, we render again
- // and switch to a different tree. Like a try/catch block. So we have to track
- // which branch we're currently rendering. Ideally we would model this using
- // a stack.
+ var wasHydrated = popHydrationState(workInProgress);
- if (current === null) {
- // Initial mount
- // Special path for hydration
- // If we're currently hydrating, try to hydrate this boundary.
- if (getIsHydrating()) {
- // We must push the suspense handler context *before* attempting to
- // hydrate, to avoid a mismatch in case it errors.
- if (showFallback) {
- pushPrimaryTreeSuspenseHandler(workInProgress);
- } else {
- pushFallbackTreeSuspenseHandler(workInProgress);
+ if (nextState !== null && nextState.dehydrated !== null) {
+ // We might be inside a hydration state the first time we're picking up this
+ // Suspense boundary, and also after we've reentered it for further hydration.
+ if (current === null) {
+ if (!wasHydrated) {
+ throw new Error(
+ "A dehydrated suspense component was completed without a hydrated node. " +
+ "This is probably a bug in React."
+ );
}
- tryToClaimNextHydratableSuspenseInstance(workInProgress); // This could've been a dehydrated suspense component.
+ prepareToHydrateHostSuspenseInstance(workInProgress);
+ bubbleProperties(workInProgress);
- var suspenseState = workInProgress.memoizedState;
+ {
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ var isTimedOutSuspense = nextState !== null;
- if (suspenseState !== null) {
- var dehydrated = suspenseState.dehydrated;
+ if (isTimedOutSuspense) {
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
+ var primaryChildFragment = workInProgress.child;
- if (dehydrated !== null) {
- return mountDehydratedSuspenseComponent(workInProgress, dehydrated);
+ if (primaryChildFragment !== null) {
+ // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
+ workInProgress.treeBaseDuration -=
+ primaryChildFragment.treeBaseDuration;
+ }
+ }
}
- } // If hydration didn't succeed, fall through to the normal Suspense path.
- // To avoid a stack mismatch we need to pop the Suspense handler that we
- // pushed above. This will become less awkward when move the hydration
- // logic to its own fiber.
+ }
- popSuspenseHandler(workInProgress);
- }
+ return false;
+ } else {
+ // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration
+ // state since we're now exiting out of it. popHydrationState doesn't do that for us.
+ resetHydrationState();
- var nextPrimaryChildren = nextProps.children;
- var nextFallbackChildren = nextProps.fallback;
+ if ((workInProgress.flags & DidCapture) === NoFlags$1) {
+ // This boundary did not suspend so it's now hydrated and unsuspended.
+ workInProgress.memoizedState = null;
+ } // If nothing suspended, we need to schedule an effect to mark this boundary
+ // as having hydrated so events know that they're free to be invoked.
+ // It's also a signal to replay events and the suspense callback.
+ // If something suspended, schedule an effect to attach retry listeners.
+ // So we might as well always mark this.
- if (showFallback) {
- pushFallbackTreeSuspenseHandler(workInProgress);
- var fallbackFragment = mountSuspenseFallbackChildren(
- workInProgress,
- nextPrimaryChildren,
- nextFallbackChildren,
- renderLanes
- );
- var primaryChildFragment = workInProgress.child;
- primaryChildFragment.memoizedState =
- mountSuspenseOffscreenState(renderLanes);
- workInProgress.memoizedState = SUSPENDED_MARKER;
+ workInProgress.flags |= Update;
+ bubbleProperties(workInProgress);
- if (enableTransitionTracing) {
- var currentTransitions = getPendingTransitions();
+ {
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ var _isTimedOutSuspense = nextState !== null;
- if (currentTransitions !== null) {
- var parentMarkerInstances = getMarkerInstances();
- var offscreenQueue = primaryChildFragment.updateQueue;
+ if (_isTimedOutSuspense) {
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
+ var _primaryChildFragment = workInProgress.child;
- if (offscreenQueue === null) {
- var newOffscreenQueue = {
- transitions: currentTransitions,
- markerInstances: parentMarkerInstances,
- wakeables: null
- };
- primaryChildFragment.updateQueue = newOffscreenQueue;
- } else {
- offscreenQueue.transitions = currentTransitions;
- offscreenQueue.markerInstances = parentMarkerInstances;
+ if (_primaryChildFragment !== null) {
+ // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
+ workInProgress.treeBaseDuration -=
+ _primaryChildFragment.treeBaseDuration;
+ }
}
}
}
- return fallbackFragment;
- } else if (typeof nextProps.unstable_expectedLoadTime === "number") {
- // This is a CPU-bound tree. Skip this tree and show a placeholder to
- // unblock the surrounding content. Then immediately retry after the
- // initial commit.
- pushFallbackTreeSuspenseHandler(workInProgress);
-
- var _fallbackFragment = mountSuspenseFallbackChildren(
- workInProgress,
- nextPrimaryChildren,
- nextFallbackChildren,
- renderLanes
- );
-
- var _primaryChildFragment = workInProgress.child;
- _primaryChildFragment.memoizedState =
- mountSuspenseOffscreenState(renderLanes);
- workInProgress.memoizedState = SUSPENDED_MARKER; // TODO: Transition Tracing is not yet implemented for CPU Suspense.
- // Since nothing actually suspended, there will nothing to ping this to
- // get it started back up to attempt the next item. While in terms of
- // priority this work has the same priority as this current render, it's
- // not part of the same transition once the transition has committed. If
- // it's sync, we still want to yield so that it can be painted.
- // Conceptually, this is really the same as pinging. We can use any
- // RetryLane even if it's the one currently rendering since we're leaving
- // it behind on this node.
-
- workInProgress.lanes = SomeRetryLane;
- return _fallbackFragment;
- } else {
- pushPrimaryTreeSuspenseHandler(workInProgress);
- return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);
+ return false;
}
} else {
- // This is an update.
- // Special path for hydration
- var prevState = current.memoizedState;
-
- if (prevState !== null) {
- var _dehydrated = prevState.dehydrated;
+ // Successfully completed this tree. If this was a forced client render,
+ // there may have been recoverable errors during first hydration
+ // attempt. If so, add them to a queue so we can log them in the
+ // commit phase.
+ upgradeHydrationErrorsToRecoverable(); // Fall through to normal Suspense path
- if (_dehydrated !== null) {
- return updateDehydratedSuspenseComponent(
- current,
- workInProgress,
- didSuspend,
- nextProps,
- _dehydrated,
- prevState,
- renderLanes
- );
- }
- }
+ return true;
+ }
+}
- if (showFallback) {
- pushFallbackTreeSuspenseHandler(workInProgress);
- var _nextFallbackChildren = nextProps.fallback;
- var _nextPrimaryChildren = nextProps.children;
- var fallbackChildFragment = updateSuspenseFallbackChildren(
- current,
- workInProgress,
- _nextPrimaryChildren,
- _nextFallbackChildren,
- renderLanes
- );
- var _primaryChildFragment2 = workInProgress.child;
- var prevOffscreenState = current.child.memoizedState;
- _primaryChildFragment2.memoizedState =
- prevOffscreenState === null
- ? mountSuspenseOffscreenState(renderLanes)
- : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
+function completeWork(current, workInProgress, renderLanes) {
+ var newProps = workInProgress.pendingProps; // Note: This intentionally doesn't check if we're hydrating because comparing
+ // to the current tree provider fiber is just as fast and less error-prone.
+ // Ideally we would have a special version of the work loop only
+ // for hydration.
- if (enableTransitionTracing) {
- var _currentTransitions = getPendingTransitions();
+ popTreeContext(workInProgress);
- if (_currentTransitions !== null) {
- var _parentMarkerInstances = getMarkerInstances();
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ case LazyComponent:
+ case SimpleMemoComponent:
+ case FunctionComponent:
+ case ForwardRef:
+ case Fragment:
+ case Mode:
+ case Profiler:
+ case ContextConsumer:
+ case MemoComponent:
+ bubbleProperties(workInProgress);
+ return null;
- var _offscreenQueue = _primaryChildFragment2.updateQueue;
- var currentOffscreenQueue = current.updateQueue;
+ case ClassComponent: {
+ var Component = workInProgress.type;
- if (_offscreenQueue === null) {
- var _newOffscreenQueue = {
- transitions: _currentTransitions,
- markerInstances: _parentMarkerInstances,
- wakeables: null
- };
- _primaryChildFragment2.updateQueue = _newOffscreenQueue;
- } else if (_offscreenQueue === currentOffscreenQueue) {
- // If the work-in-progress queue is the same object as current, we
- // can't modify it without cloning it first.
- var _newOffscreenQueue2 = {
- transitions: _currentTransitions,
- markerInstances: _parentMarkerInstances,
- wakeables:
- currentOffscreenQueue !== null
- ? currentOffscreenQueue.wakeables
- : null
- };
- _primaryChildFragment2.updateQueue = _newOffscreenQueue2;
- } else {
- _offscreenQueue.transitions = _currentTransitions;
- _offscreenQueue.markerInstances = _parentMarkerInstances;
- }
- }
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
}
- _primaryChildFragment2.childLanes = getRemainingWorkInPrimaryTree(
- current,
- renderLanes
- );
- workInProgress.memoizedState = SUSPENDED_MARKER;
- return fallbackChildFragment;
- } else {
- pushPrimaryTreeSuspenseHandler(workInProgress);
- var _nextPrimaryChildren2 = nextProps.children;
-
- var _primaryChildFragment3 = updateSuspensePrimaryChildren(
- current,
- workInProgress,
- _nextPrimaryChildren2,
- renderLanes
- );
-
- workInProgress.memoizedState = null;
- return _primaryChildFragment3;
+ bubbleProperties(workInProgress);
+ return null;
}
- }
-}
-
-function mountSuspensePrimaryChildren(
- workInProgress,
- primaryChildren,
- renderLanes
-) {
- var mode = workInProgress.mode;
- var primaryChildProps = {
- mode: "visible",
- children: primaryChildren
- };
- var primaryChildFragment = mountWorkInProgressOffscreenFiber(
- primaryChildProps,
- mode
- );
- primaryChildFragment.return = workInProgress;
- workInProgress.child = primaryChildFragment;
- return primaryChildFragment;
-}
-
-function mountSuspenseFallbackChildren(
- workInProgress,
- primaryChildren,
- fallbackChildren,
- renderLanes
-) {
- var mode = workInProgress.mode;
- var progressedPrimaryFragment = workInProgress.child;
- var primaryChildProps = {
- mode: "hidden",
- children: primaryChildren
- };
- var primaryChildFragment;
- var fallbackChildFragment;
-
- if (
- (mode & ConcurrentMode) === NoMode &&
- progressedPrimaryFragment !== null
- ) {
- // In legacy mode, we commit the primary tree as if it successfully
- // completed, even though it's in an inconsistent state.
- primaryChildFragment = progressedPrimaryFragment;
- primaryChildFragment.childLanes = NoLanes;
- primaryChildFragment.pendingProps = primaryChildProps;
- if (workInProgress.mode & ProfileMode) {
- // Reset the durations from the first pass so they aren't included in the
- // final amounts. This seems counterintuitive, since we're intentionally
- // not measuring part of the render phase, but this makes it match what we
- // do in Concurrent Mode.
- primaryChildFragment.actualDuration = 0;
- primaryChildFragment.actualStartTime = -1;
- primaryChildFragment.selfBaseDuration = 0;
- primaryChildFragment.treeBaseDuration = 0;
- }
+ case HostRoot: {
+ var fiberRoot = workInProgress.stateNode;
- fallbackChildFragment = createFiberFromFragment(
- fallbackChildren,
- mode,
- renderLanes,
- null
- );
- } else {
- primaryChildFragment = mountWorkInProgressOffscreenFiber(
- primaryChildProps,
- mode
- );
- fallbackChildFragment = createFiberFromFragment(
- fallbackChildren,
- mode,
- renderLanes,
- null
- );
- }
+ if (enableTransitionTracing) {
+ var transitions = getWorkInProgressTransitions(); // We set the Passive flag here because if there are new transitions,
+ // we will need to schedule callbacks and process the transitions,
+ // which we do in the passive phase
- primaryChildFragment.return = workInProgress;
- fallbackChildFragment.return = workInProgress;
- primaryChildFragment.sibling = fallbackChildFragment;
- workInProgress.child = primaryChildFragment;
- return fallbackChildFragment;
-}
+ if (transitions !== null) {
+ workInProgress.flags |= Passive$1;
+ }
+ }
-function mountWorkInProgressOffscreenFiber(offscreenProps, mode, renderLanes) {
- // The props argument to `createFiberFromOffscreen` is `any` typed, so we use
- // this wrapper function to constrain it.
- return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null);
-}
+ {
+ var previousCache = null;
-function updateWorkInProgressOffscreenFiber(current, offscreenProps) {
- // The props argument to `createWorkInProgress` is `any` typed, so we use this
- // wrapper function to constrain it.
- return createWorkInProgress(current, offscreenProps);
-}
+ if (current !== null) {
+ previousCache = current.memoizedState.cache;
+ }
-function updateSuspensePrimaryChildren(
- current,
- workInProgress,
- primaryChildren,
- renderLanes
-) {
- var currentPrimaryChildFragment = current.child;
- var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
- var primaryChildFragment = updateWorkInProgressOffscreenFiber(
- currentPrimaryChildFragment,
- {
- mode: "visible",
- children: primaryChildren
- }
- );
+ var cache = workInProgress.memoizedState.cache;
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- primaryChildFragment.lanes = renderLanes;
- }
+ if (cache !== previousCache) {
+ // Run passive effects to retain/release the cache.
+ workInProgress.flags |= Passive$1;
+ }
- primaryChildFragment.return = workInProgress;
- primaryChildFragment.sibling = null;
+ popCacheProvider(workInProgress);
+ }
- if (currentFallbackChildFragment !== null) {
- // Delete the fallback child fragment
- var deletions = workInProgress.deletions;
+ if (enableTransitionTracing) {
+ popRootMarkerInstance(workInProgress);
+ }
- if (deletions === null) {
- workInProgress.deletions = [currentFallbackChildFragment];
- workInProgress.flags |= ChildDeletion;
- } else {
- deletions.push(currentFallbackChildFragment);
- }
- }
+ popRootTransition(workInProgress);
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ resetWorkInProgressVersions();
- workInProgress.child = primaryChildFragment;
- return primaryChildFragment;
-}
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
-function updateSuspenseFallbackChildren(
- current,
- workInProgress,
- primaryChildren,
- fallbackChildren,
- renderLanes
-) {
- var mode = workInProgress.mode;
- var currentPrimaryChildFragment = current.child;
- var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
- var primaryChildProps = {
- mode: "hidden",
- children: primaryChildren
- };
- var primaryChildFragment;
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ var wasHydrated = popHydrationState(workInProgress);
- if (
- // In legacy mode, we commit the primary tree as if it successfully
- // completed, even though it's in an inconsistent state.
- (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was
- // already cloned. In legacy mode, the only case where this isn't true is
- // when DevTools forces us to display a fallback; we skip the first render
- // pass entirely and go straight to rendering the fallback. (In Concurrent
- // Mode, SuspenseList can also trigger this scenario, but this is a legacy-
- // only codepath.)
- workInProgress.child !== currentPrimaryChildFragment
- ) {
- var progressedPrimaryFragment = workInProgress.child;
- primaryChildFragment = progressedPrimaryFragment;
- primaryChildFragment.childLanes = NoLanes;
- primaryChildFragment.pendingProps = primaryChildProps;
+ if (wasHydrated) {
+ // If we hydrated, then we'll need to schedule an update for
+ // the commit side-effects on the root.
+ markUpdate(workInProgress);
+ } else {
+ if (current !== null) {
+ var prevState = current.memoizedState;
- if (workInProgress.mode & ProfileMode) {
- // Reset the durations from the first pass so they aren't included in the
- // final amounts. This seems counterintuitive, since we're intentionally
- // not measuring part of the render phase, but this makes it match what we
- // do in Concurrent Mode.
- primaryChildFragment.actualDuration = 0;
- primaryChildFragment.actualStartTime = -1;
- primaryChildFragment.selfBaseDuration =
- currentPrimaryChildFragment.selfBaseDuration;
- primaryChildFragment.treeBaseDuration =
- currentPrimaryChildFragment.treeBaseDuration;
- } // The fallback fiber was added as a deletion during the first pass.
- // However, since we're going to remain on the fallback, we no longer want
- // to delete it.
+ if (
+ // Check if this is a client root
+ !prevState.isDehydrated || // Check if we reverted to client rendering (e.g. due to an error)
+ (workInProgress.flags & ForceClientRender) !== NoFlags$1
+ ) {
+ // Schedule an effect to clear this container at the start of the
+ // next commit. This handles the case of React rendering into a
+ // container with previous children. It's also safe to do for
+ // updates too, because current.child would only be null if the
+ // previous render was null (so the container would already
+ // be empty).
+ workInProgress.flags |= Snapshot; // If this was a forced client render, there may have been
+ // recoverable errors during first hydration attempt. If so, add
+ // them to a queue so we can log them in the commit phase.
- workInProgress.deletions = null;
- } else {
- primaryChildFragment = updateWorkInProgressOffscreenFiber(
- currentPrimaryChildFragment,
- primaryChildProps
- ); // Since we're reusing a current tree, we need to reuse the flags, too.
- // (We don't do this in legacy mode, because in legacy mode we don't re-use
- // the current tree; see previous branch.)
+ upgradeHydrationErrorsToRecoverable();
+ }
+ }
+ }
+ }
+ bubbleProperties(workInProgress);
- primaryChildFragment.subtreeFlags =
- currentPrimaryChildFragment.subtreeFlags & StaticMask;
- }
+ if (enableTransitionTracing) {
+ if ((workInProgress.subtreeFlags & Visibility) !== NoFlags$1) {
+ // If any of our suspense children toggle visibility, this means that
+ // the pending boundaries array needs to be updated, which we only
+ // do in the passive phase.
+ workInProgress.flags |= Passive$1;
+ }
+ }
- var fallbackChildFragment;
+ return null;
+ }
- if (currentFallbackChildFragment !== null) {
- fallbackChildFragment = createWorkInProgress(
- currentFallbackChildFragment,
- fallbackChildren
- );
- } else {
- fallbackChildFragment = createFiberFromFragment(
- fallbackChildren,
- mode,
- renderLanes,
- null
- ); // Needs a placement effect because the parent (the Suspense boundary) already
- // mounted but this is a new fiber.
+ case HostHoistable: {
+ {
+ var currentRef = current ? current.ref : null;
- fallbackChildFragment.flags |= Placement;
- }
+ if (currentRef !== workInProgress.ref) {
+ markRef(workInProgress);
+ }
- fallbackChildFragment.return = workInProgress;
- primaryChildFragment.return = workInProgress;
- primaryChildFragment.sibling = fallbackChildFragment;
- workInProgress.child = primaryChildFragment;
- return fallbackChildFragment;
-}
+ if (
+ // We are mounting and must Update this Hoistable in this commit
+ current === null || // We are transitioning to, from, or between Hoistable Resources
+ // and require an update
+ current.memoizedState !== workInProgress.memoizedState
+ ) {
+ markUpdate(workInProgress);
+ } else if (workInProgress.memoizedState === null) {
+ // We may have props to update on the Hoistable instance. We use the
+ // updateHostComponent path becuase it produces the update queue
+ // we need for Hoistables
+ updateHostComponent(
+ current,
+ workInProgress,
+ workInProgress.type,
+ workInProgress.pendingProps
+ );
+ }
-function retrySuspenseComponentWithoutHydrating(
- current,
- workInProgress,
- renderLanes,
- recoverableError
-) {
- // Falling back to client rendering. Because this has performance
- // implications, it's considered a recoverable error, even though the user
- // likely won't observe anything wrong with the UI.
- //
- // The error is passed in as an argument to enforce that every caller provide
- // a custom message, or explicitly opt out (currently the only path that opts
- // out is legacy mode; every concurrent path provides an error).
- if (recoverableError !== null) {
- queueHydrationError(recoverableError);
- } // This will add the old fiber to the deletion list
+ bubbleProperties(workInProgress);
+ return null;
+ }
+ }
+ // eslint-disable-next-line-no-fallthrough
- reconcileChildFibers(workInProgress, current.child, null, renderLanes); // We're now not suspended nor dehydrated.
+ case HostSingleton: {
+ {
+ popHostContext(workInProgress);
+ var rootContainerInstance = getRootHostContainer();
+ var type = workInProgress.type;
- var nextProps = workInProgress.pendingProps;
- var primaryChildren = nextProps.children;
- var primaryChildFragment = mountSuspensePrimaryChildren(
- workInProgress,
- primaryChildren
- ); // Needs a placement effect because the parent (the Suspense boundary) already
- // mounted but this is a new fiber.
+ if (current !== null && workInProgress.stateNode != null) {
+ updateHostComponent(current, workInProgress, type, newProps);
- primaryChildFragment.flags |= Placement;
- workInProgress.memoizedState = null;
- return primaryChildFragment;
-}
+ if (current.ref !== workInProgress.ref) {
+ markRef(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ if (workInProgress.stateNode === null) {
+ throw new Error(
+ "We must have new props for new mounts. This error is likely " +
+ "caused by a bug in React. Please file an issue."
+ );
+ } // This can happen when we abort work.
-function mountSuspenseFallbackAfterRetryWithoutHydrating(
- current,
- workInProgress,
- primaryChildren,
- fallbackChildren,
- renderLanes
-) {
- var fiberMode = workInProgress.mode;
- var primaryChildProps = {
- mode: "visible",
- children: primaryChildren
- };
- var primaryChildFragment = mountWorkInProgressOffscreenFiber(
- primaryChildProps,
- fiberMode
- );
- var fallbackChildFragment = createFiberFromFragment(
- fallbackChildren,
- fiberMode,
- renderLanes,
- null
- ); // Needs a placement effect because the parent (the Suspense
- // boundary) already mounted but this is a new fiber.
+ bubbleProperties(workInProgress);
+ return null;
+ }
- fallbackChildFragment.flags |= Placement;
- primaryChildFragment.return = workInProgress;
- fallbackChildFragment.return = workInProgress;
- primaryChildFragment.sibling = fallbackChildFragment;
- workInProgress.child = primaryChildFragment;
+ var currentHostContext = getHostContext();
- if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
- // We will have dropped the effect list which contains the
- // deletion. We need to reconcile to delete the current child.
- reconcileChildFibers(workInProgress, current.child, null, renderLanes);
- }
+ var _wasHydrated = popHydrationState(workInProgress);
- return fallbackChildFragment;
-}
+ if (_wasHydrated) {
+ // We ignore the boolean indicating there is an updateQueue because
+ // it is used only to set text children and HostSingletons do not
+ // use them.
+ prepareToHydrateHostInstance(workInProgress, currentHostContext);
+ } else {
+ workInProgress.stateNode = resolveSingletonInstance(
+ type,
+ newProps,
+ rootContainerInstance,
+ currentHostContext,
+ true
+ );
+ markUpdate(workInProgress);
+ }
-function mountDehydratedSuspenseComponent(
- workInProgress,
- suspenseInstance,
- renderLanes
-) {
- // During the first pass, we'll bail out and not drill into the children.
- // Instead, we'll leave the content in place and try to hydrate it later.
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- {
- error(
- "Cannot hydrate Suspense in legacy mode. Switch from " +
- "ReactDOM.hydrate(element, container) to " +
- "ReactDOMClient.hydrateRoot(container, )" +
- ".render(element) or remove the Suspense components from " +
- "the server rendered components."
- );
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef(workInProgress);
+ }
+ }
+
+ bubbleProperties(workInProgress);
+ return null;
+ }
}
+ // eslint-disable-next-line-no-fallthrough
- workInProgress.lanes = laneToLanes(SyncLane);
- } else if (isSuspenseInstanceFallback(suspenseInstance)) {
- // This is a client-only boundary. Since we won't get any content from the server
- // for this, we need to schedule that at a higher priority based on when it would
- // have timed out. In theory we could render it in this pass but it would have the
- // wrong priority associated with it and will prevent hydration of parent path.
- // Instead, we'll leave work left on it to render it in a separate commit.
- // TODO This time should be the time at which the server rendered response that is
- // a parent to this boundary was displayed. However, since we currently don't have
- // a protocol to transfer that time, we'll just estimate it by using the current
- // time. This will mean that Suspense timeouts are slightly shifted to later than
- // they should be.
- // Schedule a normal pri update to render this content.
- workInProgress.lanes = laneToLanes(DefaultHydrationLane);
- } else {
- // We'll continue hydrating the rest at offscreen priority since we'll already
- // be showing the right content coming from the server, it is no rush.
- workInProgress.lanes = laneToLanes(OffscreenLane);
- }
+ case HostComponent: {
+ popHostContext(workInProgress);
+ var _type = workInProgress.type;
- return null;
-}
+ if (current !== null && workInProgress.stateNode != null) {
+ updateHostComponent(current, workInProgress, _type, newProps);
-function updateDehydratedSuspenseComponent(
- current,
- workInProgress,
- didSuspend,
- nextProps,
- suspenseInstance,
- suspenseState,
- renderLanes
-) {
- if (!didSuspend) {
- // This is the first render pass. Attempt to hydrate.
- pushPrimaryTreeSuspenseHandler(workInProgress); // We should never be hydrating at this point because it is the first pass,
- // but after we've already committed once.
+ if (current.ref !== workInProgress.ref) {
+ markRef(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ if (workInProgress.stateNode === null) {
+ throw new Error(
+ "We must have new props for new mounts. This error is likely " +
+ "caused by a bug in React. Please file an issue."
+ );
+ } // This can happen when we abort work.
- warnIfHydrating();
+ bubbleProperties(workInProgress);
+ return null;
+ }
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- return retrySuspenseComponentWithoutHydrating(
- current,
- workInProgress,
- renderLanes,
- null
- );
- }
+ var _currentHostContext2 = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on whether we want to add them top->down or
+ // bottom->up. Top->down is faster in IE11.
- if (isSuspenseInstanceFallback(suspenseInstance)) {
- // This boundary is in a permanent fallback state. In this case, we'll never
- // get an update and we'll never be able to hydrate the final content. Let's just try the
- // client side render instead.
- var digest, message, stack;
+ var _wasHydrated2 = popHydrationState(workInProgress);
- {
- var _getSuspenseInstanceF =
- getSuspenseInstanceFallbackErrorDetails(suspenseInstance);
+ if (_wasHydrated2) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
+ if (
+ prepareToHydrateHostInstance(workInProgress, _currentHostContext2)
+ ) {
+ // If changes to the hydrated node need to be applied at the
+ // commit-phase we mark this as such.
+ markUpdate(workInProgress);
+ }
+ } else {
+ var _rootContainerInstance = getRootHostContainer();
- digest = _getSuspenseInstanceF.digest;
- message = _getSuspenseInstanceF.message;
- stack = _getSuspenseInstanceF.stack;
- }
+ var instance = createInstance(
+ _type,
+ newProps,
+ _rootContainerInstance,
+ _currentHostContext2,
+ workInProgress
+ );
+ appendAllChildren(instance, workInProgress);
+ workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
- var error;
+ if (finalizeInitialChildren(instance, _type, newProps)) {
+ markUpdate(workInProgress);
+ }
+ }
- if (message) {
- // eslint-disable-next-line react-internal/prod-error-codes
- error = new Error(message);
- } else {
- error = new Error(
- "The server could not finish this Suspense boundary, likely " +
- "due to an error during server rendering. Switched to " +
- "client rendering."
- );
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef(workInProgress);
+ }
}
- error.digest = digest;
- var capturedValue = createCapturedValue(error, digest, stack);
- return retrySuspenseComponentWithoutHydrating(
- current,
- workInProgress,
- renderLanes,
- capturedValue
- );
+ bubbleProperties(workInProgress);
+ return null;
}
- if (
- enableLazyContextPropagation && // TODO: Factoring is a little weird, since we check this right below, too.
- // But don't want to re-arrange the if-else chain until/unless this
- // feature lands.
- !didReceiveUpdate
- ) {
- // We need to check if any children have context before we decide to bail
- // out, so propagate the changes now.
- lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);
- } // We use lanes to indicate that a child might depend on context, so if
- // any context has changed, we need to treat is as if the input might have changed.
+ 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") {
+ if (workInProgress.stateNode === null) {
+ throw new Error(
+ "We must have new props for new mounts. This error is likely " +
+ "caused by a bug in React. Please file an issue."
+ );
+ } // This can happen when we abort work.
+ }
- var hasContextChanged = includesSomeLane(renderLanes, current.childLanes);
+ var _rootContainerInstance2 = getRootHostContainer();
- if (didReceiveUpdate || hasContextChanged) {
- // This boundary has changed since the first render. This means that we are now unable to
- // hydrate it. We might still be able to hydrate it using a higher priority lane.
- var root = getWorkInProgressRoot();
+ var _currentHostContext3 = getHostContext();
- if (root !== null) {
- var attemptHydrationAtLane = getBumpedLaneForHydration(
- root,
- renderLanes
- );
+ var _wasHydrated3 = popHydrationState(workInProgress);
- if (
- attemptHydrationAtLane !== NoLane &&
- attemptHydrationAtLane !== suspenseState.retryLane
- ) {
- // Intentionally mutating since this render will get interrupted. This
- // is one of the very rare times where we mutate the current tree
- // during the render phase.
- suspenseState.retryLane = attemptHydrationAtLane; // TODO: Ideally this would inherit the event time of the current render
+ if (_wasHydrated3) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
+ }
+ } else {
+ workInProgress.stateNode = createTextInstance(
+ newText,
+ _rootContainerInstance2,
+ _currentHostContext3,
+ workInProgress
+ );
+ }
+ }
- var eventTime = NoTimestamp;
- enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
- scheduleUpdateOnFiber(
- root,
- current,
- attemptHydrationAtLane,
- eventTime
- ); // Throw a special object that signals to the work loop that it should
- // interrupt the current render.
- //
- // Because we're inside a React-only execution stack, we don't
- // strictly need to throw here — we could instead modify some internal
- // work loop state. But using an exception means we don't need to
- // check for this case on every iteration of the work loop. So doing
- // it this way moves the check out of the fast path.
+ bubbleProperties(workInProgress);
+ return null;
+ }
- throw SelectiveHydrationException;
- }
- } // If we did not selectively hydrate, we'll continue rendering without
- // hydrating. Mark this tree as suspended to prevent it from committing
- // outside a transition.
- //
- // This path should only happen if the hydration lane already suspended.
- // Currently, it also happens during sync updates because there is no
- // hydration lane for sync updates.
- // TODO: We should ideally have a sync hydration lane that we can apply to do
- // a pass where we hydrate this subtree in place using the previous Context and then
- // reapply the update afterwards.
+ case SuspenseComponent: {
+ popSuspenseHandler(workInProgress);
+ var nextState = workInProgress.memoizedState; // Special path for dehydrated boundaries. We may eventually move this
+ // to its own fiber type so that we can add other kinds of hydration
+ // boundaries that aren't associated with a Suspense tree. In anticipation
+ // of such a refactor, all the hydration logic is contained in
+ // this branch.
- renderDidSuspendDelayIfPossible();
- return retrySuspenseComponentWithoutHydrating(
- current,
- workInProgress,
- renderLanes,
- null
- );
- } else if (isSuspenseInstancePending(suspenseInstance)) {
- // This component is still pending more data from the server, so we can't hydrate its
- // content. We treat it as if this component suspended itself. It might seem as if
- // we could just try to render it client-side instead. However, this will perform a
- // lot of unnecessary work and is unlikely to complete since it often will suspend
- // on missing data anyway. Additionally, the server might be able to render more
- // than we can on the client yet. In that case we'd end up with more fallback states
- // on the client than if we just leave it alone. If the server times out or errors
- // these should update this boundary to the permanent Fallback state instead.
- // Mark it as having captured (i.e. suspended).
- workInProgress.flags |= DidCapture; // Leave the child in place. I.e. the dehydrated fragment.
+ if (
+ current === null ||
+ (current.memoizedState !== null &&
+ current.memoizedState.dehydrated !== null)
+ ) {
+ var fallthroughToNormalSuspensePath =
+ completeDehydratedSuspenseBoundary(
+ current,
+ workInProgress,
+ nextState
+ );
- workInProgress.child = current.child; // Register a callback to retry this boundary once the server has sent the result.
+ if (!fallthroughToNormalSuspensePath) {
+ if (workInProgress.flags & ShouldCapture) {
+ // Special case. There were remaining unhydrated nodes. We treat
+ // this as a mismatch. Revert to client rendering.
+ return workInProgress;
+ } else {
+ // Did not finish hydrating, either because this is the initial
+ // render or because something suspended.
+ return null;
+ }
+ } // Continue with the normal Suspense path.
+ }
- var retry = retryDehydratedSuspenseBoundary.bind(null, current);
- registerSuspenseInstanceRetry(suspenseInstance, retry);
- return null;
- } else {
- // This is the first attempt.
- reenterHydrationStateFromDehydratedSuspenseInstance(
- workInProgress,
- suspenseInstance,
- suspenseState.treeContext
- );
- var primaryChildren = nextProps.children;
- var primaryChildFragment = mountSuspensePrimaryChildren(
- workInProgress,
- primaryChildren
- ); // Mark the children as hydrating. This is a fast path to know whether this
- // tree is part of a hydrating tree. This is used to determine if a child
- // node has fully mounted yet, and for scheduling event replaying.
- // Conceptually this is similar to Placement in that a new subtree is
- // inserted into the React tree here. It just happens to not need DOM
- // mutations because it already exists.
+ if ((workInProgress.flags & DidCapture) !== NoFlags$1) {
+ // Something suspended. Re-render with the fallback children.
+ workInProgress.lanes = renderLanes; // Do not reset the effect list.
- primaryChildFragment.flags |= Hydrating;
- return primaryChildFragment;
- }
- } else {
- // This is the second render pass. We already attempted to hydrated, but
- // something either suspended or errored.
- if (workInProgress.flags & ForceClientRender) {
- // Something errored during hydration. Try again without hydrating.
- pushPrimaryTreeSuspenseHandler(workInProgress);
- workInProgress.flags &= ~ForceClientRender;
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ transferActualDuration(workInProgress);
+ } // Don't bubble properties in this case.
- var _capturedValue = createCapturedValue(
- new Error(
- "There was an error while hydrating this Suspense boundary. " +
- "Switched to client rendering."
- )
- );
+ return workInProgress;
+ }
- return retrySuspenseComponentWithoutHydrating(
- current,
- workInProgress,
- renderLanes,
- _capturedValue
- );
- } else if (workInProgress.memoizedState !== null) {
- // Something suspended and we should still be in dehydrated mode.
- // Leave the existing child in place.
- // Push to avoid a mismatch
- pushFallbackTreeSuspenseHandler(workInProgress);
- workInProgress.child = current.child; // The dehydrated completion pass expects this flag to be there
- // but the normal suspense pass doesn't.
+ var nextDidTimeout = nextState !== null;
+ var prevDidTimeout = current !== null && current.memoizedState !== null;
- workInProgress.flags |= DidCapture;
- return null;
- } else {
- // Suspended but we should no longer be in dehydrated mode.
- // Therefore we now have to render the fallback.
- pushFallbackTreeSuspenseHandler(workInProgress);
- var nextPrimaryChildren = nextProps.children;
- var nextFallbackChildren = nextProps.fallback;
- var fallbackChildFragment =
- mountSuspenseFallbackAfterRetryWithoutHydrating(
- current,
- workInProgress,
- nextPrimaryChildren,
- nextFallbackChildren,
- renderLanes
- );
- var _primaryChildFragment4 = workInProgress.child;
- _primaryChildFragment4.memoizedState =
- mountSuspenseOffscreenState(renderLanes);
- workInProgress.memoizedState = SUSPENDED_MARKER;
- return fallbackChildFragment;
- }
- }
-}
+ if (nextDidTimeout) {
+ var offscreenFiber = workInProgress.child;
+ var _previousCache = null;
-function scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {
- fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
- var alternate = fiber.alternate;
+ if (
+ offscreenFiber.alternate !== null &&
+ offscreenFiber.alternate.memoizedState !== null &&
+ offscreenFiber.alternate.memoizedState.cachePool !== null
+ ) {
+ _previousCache =
+ offscreenFiber.alternate.memoizedState.cachePool.pool;
+ }
- if (alternate !== null) {
- alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
- }
+ var _cache = null;
- scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);
-}
+ if (
+ offscreenFiber.memoizedState !== null &&
+ offscreenFiber.memoizedState.cachePool !== null
+ ) {
+ _cache = offscreenFiber.memoizedState.cachePool.pool;
+ }
-function propagateSuspenseContextChange(
- workInProgress,
- firstChild,
- renderLanes
-) {
- // Mark any Suspense boundaries with fallbacks as having work to do.
- // If they were previously forced into fallbacks, they may now be able
- // to unblock.
- var node = firstChild;
+ if (_cache !== _previousCache) {
+ // Run passive effects to retain/release the cache.
+ offscreenFiber.flags |= Passive$1;
+ }
+ } // If the suspended state of the boundary changes, we need to schedule
+ // a passive effect, which is when we process the transitions
- while (node !== null) {
- if (node.tag === SuspenseComponent) {
- var state = node.memoizedState;
+ if (nextDidTimeout !== prevDidTimeout) {
+ if (enableTransitionTracing) {
+ var _offscreenFiber = workInProgress.child;
+ _offscreenFiber.flags |= Passive$1;
+ } // If the suspended state of the boundary changes, we need to schedule
+ // an effect to toggle the subtree's visibility. When we switch from
+ // fallback -> primary, the inner Offscreen fiber schedules this effect
+ // as part of its normal complete phase. But when we switch from
+ // primary -> fallback, the inner Offscreen fiber does not have a complete
+ // phase. So we need to schedule its effect here.
+ //
+ // We also use this flag to connect/disconnect the effects, but the same
+ // logic applies: when re-connecting, the Offscreen fiber's complete
+ // phase will handle scheduling the effect. It's only when the fallback
+ // is active that we have to do anything special.
- if (state !== null) {
- scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
+ if (nextDidTimeout) {
+ var _offscreenFiber2 = workInProgress.child;
+ _offscreenFiber2.flags |= Visibility;
+ }
}
- } else if (node.tag === SuspenseListComponent) {
- // If the tail is hidden there might not be an Suspense boundaries
- // to schedule work on. In this case we have to schedule it on the
- // list itself.
- // We don't have to traverse to the children of the list since
- // the list will propagate the change when it rerenders.
- scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
- } else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
- if (node === workInProgress) {
- return;
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ var wakeables = workInProgress.updateQueue;
- while (node.sibling === null) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (node.return === null || node.return === workInProgress) {
- return;
+ if (wakeables !== null) {
+ // Schedule an effect to attach a retry listener to the promise.
+ // TODO: Move to passive phase
+ workInProgress.flags |= Update;
}
- node = node.return;
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (
+ workInProgress.updateQueue !== null &&
+ workInProgress.memoizedProps.suspenseCallback != null
+ ) {
+ // Always notify the callback
+ // TODO: Move to passive phase
+ workInProgress.flags |= Update;
+ }
- node.sibling.return = node.return;
- node = node.sibling;
- }
-}
+ bubbleProperties(workInProgress);
-function findLastContentRow(firstChild) {
- // This is going to find the last row among these children that is already
- // showing content on the screen, as opposed to being in fallback state or
- // new. If a row has multiple Suspense boundaries, any of them being in the
- // fallback state, counts as the whole row being in a fallback state.
- // Note that the "rows" will be workInProgress, but any nested children
- // will still be current since we haven't rendered them yet. The mounted
- // order may not be the same as the new order. We use the new order.
- var row = firstChild;
- var lastContentRow = null;
+ {
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ if (nextDidTimeout) {
+ // Don't count time spent in a timed out Suspense subtree as part of the base duration.
+ var primaryChildFragment = workInProgress.child;
- while (row !== null) {
- var currentRow = row.alternate; // New rows can't be content rows.
+ if (primaryChildFragment !== null) {
+ // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
+ workInProgress.treeBaseDuration -=
+ primaryChildFragment.treeBaseDuration;
+ }
+ }
+ }
+ }
- if (currentRow !== null && findFirstSuspended(currentRow) === null) {
- lastContentRow = row;
+ return null;
}
- row = row.sibling;
- }
+ case HostPortal:
+ popHostContainer(workInProgress);
- return lastContentRow;
-}
+ if (current === null) {
+ preparePortalMount(workInProgress.stateNode.containerInfo);
+ }
-function validateRevealOrder(revealOrder) {
- {
- if (
- revealOrder !== undefined &&
- revealOrder !== "forwards" &&
- revealOrder !== "backwards" &&
- revealOrder !== "together" &&
- !didWarnAboutRevealOrder[revealOrder]
- ) {
- didWarnAboutRevealOrder[revealOrder] = true;
+ bubbleProperties(workInProgress);
+ return null;
- if (typeof revealOrder === "string") {
- switch (revealOrder.toLowerCase()) {
- case "together":
- case "forwards":
- case "backwards": {
- error(
- '"%s" is not a valid value for revealOrder on . ' +
- 'Use lowercase "%s" instead.',
- revealOrder,
- revealOrder.toLowerCase()
- );
+ case ContextProvider:
+ // Pop provider fiber
+ var context = workInProgress.type._context;
+ popProvider(context, workInProgress);
+ bubbleProperties(workInProgress);
+ return null;
- break;
- }
+ case IncompleteClassComponent: {
+ // Same as class component case. I put it down here so that the tags are
+ // sequential to ensure this switch is compiled to a jump table.
+ var _Component = workInProgress.type;
- case "forward":
- case "backward": {
- error(
- '"%s" is not a valid value for revealOrder on . ' +
- 'React uses the -s suffix in the spelling. Use "%ss" instead.',
- revealOrder,
- revealOrder.toLowerCase()
- );
+ if (isContextProvider(_Component)) {
+ popContext(workInProgress);
+ }
- break;
- }
+ bubbleProperties(workInProgress);
+ return null;
+ }
- default:
- error(
- '"%s" is not a supported revealOrder on . ' +
- 'Did you mean "together", "forwards" or "backwards"?',
- revealOrder
- );
+ case SuspenseListComponent: {
+ popSuspenseListContext(workInProgress);
+ var renderState = workInProgress.memoizedState;
- break;
- }
- } else {
- error(
- "%s is not a supported value for revealOrder on . " +
- 'Did you mean "together", "forwards" or "backwards"?',
- revealOrder
- );
+ if (renderState === null) {
+ // We're running in the default, "independent" mode.
+ // We don't do anything in this mode.
+ bubbleProperties(workInProgress);
+ return null;
}
- }
- }
-}
-function validateTailOptions(tailMode, revealOrder) {
- {
- if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) {
- if (tailMode !== "collapsed" && tailMode !== "hidden") {
- didWarnAboutTailOptions[tailMode] = true;
+ var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags$1;
+ var renderedTail = renderState.rendering;
- error(
- '"%s" is not a supported value for tail on . ' +
- 'Did you mean "collapsed" or "hidden"?',
- tailMode
- );
- } else if (revealOrder !== "forwards" && revealOrder !== "backwards") {
- didWarnAboutTailOptions[tailMode] = true;
+ if (renderedTail === null) {
+ // We just rendered the head.
+ if (!didSuspendAlready) {
+ // This is the first pass. We need to figure out if anything is still
+ // suspended in the rendered set.
+ // If new content unsuspended, but there's still some content that
+ // didn't. Then we need to do a second pass that forces everything
+ // to keep showing their fallbacks.
+ // We might be suspended if something in this render pass suspended, or
+ // something in the previous committed pass suspended. Otherwise,
+ // there's no chance so we can skip the expensive call to
+ // findFirstSuspended.
+ var cannotBeSuspended =
+ renderHasNotSuspendedYet() &&
+ (current === null || (current.flags & DidCapture) === NoFlags$1);
- error(
- ' is only valid if revealOrder is ' +
- '"forwards" or "backwards". ' +
- 'Did you mean to specify revealOrder="forwards"?',
- tailMode
- );
- }
- }
- }
-}
+ if (!cannotBeSuspended) {
+ var row = workInProgress.child;
-function validateSuspenseListNestedChild(childSlot, index) {
- {
- var isAnArray = isArray(childSlot);
- var isIterable =
- !isAnArray && typeof getIteratorFn(childSlot) === "function";
+ while (row !== null) {
+ var suspended = findFirstSuspended(row);
- if (isAnArray || isIterable) {
- var type = isAnArray ? "array" : "iterable";
+ if (suspended !== null) {
+ didSuspendAlready = true;
+ workInProgress.flags |= DidCapture;
+ cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as
+ // part of the second pass. In that case nothing will subscribe to
+ // its thenables. Instead, we'll transfer its thenables to the
+ // SuspenseList so that it can retry if they resolve.
+ // There might be multiple of these in the list but since we're
+ // going to wait for all of them anyway, it doesn't really matter
+ // which ones gets to ping. In theory we could get clever and keep
+ // track of how many dependencies remain but it gets tricky because
+ // in the meantime, we can add/remove/change items and dependencies.
+ // We might bail out of the loop before finding any but that
+ // doesn't matter since that means that the other boundaries that
+ // we did find already has their listeners attached.
- error(
- "A nested %s was passed to row #%s in . Wrap it in " +
- "an additional SuspenseList to configure its revealOrder: " +
- " ... " +
- "{%s} ... " +
- "",
- type,
- index,
- type
- );
+ var newThenables = suspended.updateQueue;
+
+ if (newThenables !== null) {
+ workInProgress.updateQueue = newThenables;
+ workInProgress.flags |= Update;
+ } // Rerender the whole list, but this time, we'll force fallbacks
+ // to stay in place.
+ // Reset the effect flags before doing the second pass since that's now invalid.
+ // Reset the child fibers to their original state.
+
+ workInProgress.subtreeFlags = NoFlags$1;
+ resetChildFibers(workInProgress, renderLanes); // Set up the Suspense List Context to force suspense and
+ // immediately rerender the children.
+
+ pushSuspenseListContext(
+ workInProgress,
+ setShallowSuspenseListContext(
+ suspenseStackCursor.current,
+ ForceSuspenseFallback
+ )
+ ); // Don't bubble properties in this case.
+
+ return workInProgress.child;
+ }
- return false;
- }
- }
+ row = row.sibling;
+ }
+ }
- return true;
-}
+ if (renderState.tail !== null && now$1() > getRenderTargetTime()) {
+ // We have already passed our CPU deadline but we still have rows
+ // left in the tail. We'll just give up further attempts to render
+ // the main content and only render fallbacks.
+ workInProgress.flags |= DidCapture;
+ didSuspendAlready = true;
+ cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
+ // to get it started back up to attempt the next item. While in terms
+ // of priority this work has the same priority as this current render,
+ // it's not part of the same transition once the transition has
+ // committed. If it's sync, we still want to yield so that it can be
+ // painted. Conceptually, this is really the same as pinging.
+ // We can use any RetryLane even if it's the one currently rendering
+ // since we're leaving it behind on this node.
-function validateSuspenseListChildren(children, revealOrder) {
- {
- if (
- (revealOrder === "forwards" || revealOrder === "backwards") &&
- children !== undefined &&
- children !== null &&
- children !== false
- ) {
- if (isArray(children)) {
- for (var i = 0; i < children.length; i++) {
- if (!validateSuspenseListNestedChild(children[i], i)) {
- return;
+ workInProgress.lanes = SomeRetryLane;
}
- }
+ } else {
+ cutOffTailIfNeeded(renderState, false);
+ } // Next we're going to render the tail.
} else {
- var iteratorFn = getIteratorFn(children);
+ // Append the rendered row to the child list.
+ if (!didSuspendAlready) {
+ var _suspended = findFirstSuspended(renderedTail);
- if (typeof iteratorFn === "function") {
- var childrenIterator = iteratorFn.call(children);
+ if (_suspended !== null) {
+ workInProgress.flags |= DidCapture;
+ didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't
+ // get lost if this row ends up dropped during a second pass.
- if (childrenIterator) {
- var step = childrenIterator.next();
- var _i = 0;
+ var _newThenables = _suspended.updateQueue;
- for (; !step.done; step = childrenIterator.next()) {
- if (!validateSuspenseListNestedChild(step.value, _i)) {
- return;
- }
+ if (_newThenables !== null) {
+ workInProgress.updateQueue = _newThenables;
+ workInProgress.flags |= Update;
+ }
- _i++;
+ cutOffTailIfNeeded(renderState, true); // This might have been modified.
+
+ if (
+ renderState.tail === null &&
+ renderState.tailMode === "hidden" &&
+ !renderedTail.alternate &&
+ !getIsHydrating() // We don't cut it if we're hydrating.
+ ) {
+ // We're done.
+ bubbleProperties(workInProgress);
+ return null;
}
+ } else if (
+ // The time it took to render last row is greater than the remaining
+ // time we have to render. So rendering one more row would likely
+ // exceed it.
+ now$1() * 2 - renderState.renderingStartTime >
+ getRenderTargetTime() &&
+ renderLanes !== OffscreenLane
+ ) {
+ // We have now passed our CPU deadline and we'll just give up further
+ // attempts to render the main content and only render fallbacks.
+ // The assumption is that this is usually faster.
+ workInProgress.flags |= DidCapture;
+ didSuspendAlready = true;
+ cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
+ // to get it started back up to attempt the next item. While in terms
+ // of priority this work has the same priority as this current render,
+ // it's not part of the same transition once the transition has
+ // committed. If it's sync, we still want to yield so that it can be
+ // painted. Conceptually, this is really the same as pinging.
+ // We can use any RetryLane even if it's the one currently rendering
+ // since we're leaving it behind on this node.
+
+ workInProgress.lanes = SomeRetryLane;
}
+ }
+
+ if (renderState.isBackwards) {
+ // The effect list of the backwards tail will have been added
+ // to the end. This breaks the guarantee that life-cycles fire in
+ // sibling order but that isn't a strong guarantee promised by React.
+ // Especially since these might also just pop in during future commits.
+ // Append to the beginning of the list.
+ renderedTail.sibling = workInProgress.child;
+ workInProgress.child = renderedTail;
} else {
- error(
- 'A single row was passed to a . ' +
- "This is not useful since it needs multiple rows. " +
- "Did you mean to pass multiple children or an array?",
- revealOrder
- );
+ var previousSibling = renderState.last;
+
+ if (previousSibling !== null) {
+ previousSibling.sibling = renderedTail;
+ } else {
+ workInProgress.child = renderedTail;
+ }
+
+ renderState.last = renderedTail;
}
}
- }
- }
-}
-
-function initSuspenseListRenderState(
- workInProgress,
- isBackwards,
- tail,
- lastContentRow,
- tailMode
-) {
- var renderState = workInProgress.memoizedState;
- if (renderState === null) {
- workInProgress.memoizedState = {
- isBackwards: isBackwards,
- rendering: null,
- renderingStartTime: 0,
- last: lastContentRow,
- tail: tail,
- tailMode: tailMode
- };
- } else {
- // We can reuse the existing object from previous renders.
- renderState.isBackwards = isBackwards;
- renderState.rendering = null;
- renderState.renderingStartTime = 0;
- renderState.last = lastContentRow;
- renderState.tail = tail;
- renderState.tailMode = tailMode;
- }
-} // This can end up rendering this component multiple passes.
-// The first pass splits the children fibers into two sets. A head and tail.
-// We first render the head. If anything is in fallback state, we do another
-// pass through beginWork to rerender all children (including the tail) with
-// the force suspend context. If the first render didn't have anything in
-// in fallback state. Then we render each row in the tail one-by-one.
-// That happens in the completeWork phase without going back to beginWork.
+ if (renderState.tail !== null) {
+ // We still have tail rows to render.
+ // Pop a row.
+ var next = renderState.tail;
+ renderState.rendering = next;
+ renderState.tail = next.sibling;
+ renderState.renderingStartTime = now$1();
+ next.sibling = null; // Restore the context.
+ // TODO: We can probably just avoid popping it instead and only
+ // setting it the first time we go from not suspended to suspended.
-function updateSuspenseListComponent(current, workInProgress, renderLanes) {
- var nextProps = workInProgress.pendingProps;
- var revealOrder = nextProps.revealOrder;
- var tailMode = nextProps.tail;
- var newChildren = nextProps.children;
- validateRevealOrder(revealOrder);
- validateTailOptions(tailMode, revealOrder);
- validateSuspenseListChildren(newChildren, revealOrder);
- reconcileChildren(current, workInProgress, newChildren, renderLanes);
- var suspenseContext = suspenseStackCursor.current;
- var shouldForceFallback = hasSuspenseListContext(
- suspenseContext,
- ForceSuspenseFallback
- );
+ var suspenseContext = suspenseStackCursor.current;
- if (shouldForceFallback) {
- suspenseContext = setShallowSuspenseListContext(
- suspenseContext,
- ForceSuspenseFallback
- );
- workInProgress.flags |= DidCapture;
- } else {
- var didSuspendBefore =
- current !== null && (current.flags & DidCapture) !== NoFlags$1;
+ if (didSuspendAlready) {
+ suspenseContext = setShallowSuspenseListContext(
+ suspenseContext,
+ ForceSuspenseFallback
+ );
+ } else {
+ suspenseContext =
+ setDefaultShallowSuspenseListContext(suspenseContext);
+ }
- if (didSuspendBefore) {
- // If we previously forced a fallback, we need to schedule work
- // on any nested boundaries to let them know to try to render
- // again. This is the same as context updating.
- propagateSuspenseContextChange(
- workInProgress,
- workInProgress.child,
- renderLanes
- );
- }
+ pushSuspenseListContext(workInProgress, suspenseContext); // Do a pass over the next row.
+ // Don't bubble properties in this case.
- suspenseContext = setDefaultShallowSuspenseListContext(suspenseContext);
- }
+ return next;
+ }
- pushSuspenseListContext(workInProgress, suspenseContext);
+ bubbleProperties(workInProgress);
+ return null;
+ }
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- // In legacy mode, SuspenseList doesn't work so we just
- // use make it a noop by treating it as the default revealOrder.
- workInProgress.memoizedState = null;
- } else {
- switch (revealOrder) {
- case "forwards": {
- var lastContentRow = findLastContentRow(workInProgress.child);
- var tail;
+ case ScopeComponent: {
+ {
+ if (current === null) {
+ var scopeInstance = createScopeInstance();
+ workInProgress.stateNode = scopeInstance;
+ prepareScopeUpdate(scopeInstance, workInProgress);
- if (lastContentRow === null) {
- // The whole list is part of the tail.
- // TODO: We could fast path by just rendering the tail now.
- tail = workInProgress.child;
- workInProgress.child = null;
+ if (workInProgress.ref !== null) {
+ markRef(workInProgress);
+ markUpdate(workInProgress);
+ }
} else {
- // Disconnect the tail rows after the content row.
- // We're going to render them separately later.
- tail = lastContentRow.sibling;
- lastContentRow.sibling = null;
+ if (workInProgress.ref !== null) {
+ markUpdate(workInProgress);
+ }
+
+ if (current.ref !== workInProgress.ref) {
+ markRef(workInProgress);
+ }
}
- initSuspenseListRenderState(
- workInProgress,
- false, // isBackwards
- tail,
- lastContentRow,
- tailMode
- );
- break;
+ bubbleProperties(workInProgress);
+ return null;
}
+ }
- case "backwards": {
- // We're going to find the first row that has existing content.
- // At the same time we're going to reverse the list of everything
- // we pass in the meantime. That's going to be our tail in reverse
- // order.
- var _tail = null;
- var row = workInProgress.child;
- workInProgress.child = null;
+ case OffscreenComponent:
+ case LegacyHiddenComponent: {
+ popSuspenseHandler(workInProgress);
+ popHiddenContext(workInProgress);
+ var _nextState = workInProgress.memoizedState;
+ var nextIsHidden = _nextState !== null; // Schedule a Visibility effect if the visibility has changed
- while (row !== null) {
- var currentRow = row.alternate; // New rows can't be content rows.
+ if (workInProgress.tag === LegacyHiddenComponent);
+ else {
+ if (current !== null) {
+ var _prevState = current.memoizedState;
+ var prevIsHidden = _prevState !== null;
- if (currentRow !== null && findFirstSuspended(currentRow) === null) {
- // This is the beginning of the main content.
- workInProgress.child = row;
- break;
+ if (prevIsHidden !== nextIsHidden) {
+ workInProgress.flags |= Visibility;
+ }
+ } else {
+ // On initial mount, we only need a Visibility effect if the tree
+ // is hidden.
+ if (nextIsHidden) {
+ workInProgress.flags |= Visibility;
}
+ }
+ }
- var nextRow = row.sibling;
- row.sibling = _tail;
- _tail = row;
- row = nextRow;
- } // TODO: If workInProgress.child is null, we can continue on the tail immediately.
+ if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
+ bubbleProperties(workInProgress);
+ } else {
+ // Don't bubble properties for hidden children unless we're rendering
+ // at offscreen priority.
+ if (
+ includesSomeLane(renderLanes, OffscreenLane) && // Also don't bubble if the tree suspended
+ (workInProgress.flags & DidCapture) === NoLanes
+ ) {
+ bubbleProperties(workInProgress); // Check if there was an insertion or update in the hidden subtree.
+ // If so, we need to hide those nodes in the commit phase, so
+ // schedule a visibility effect.
- initSuspenseListRenderState(
- workInProgress,
- true, // isBackwards
- _tail,
- null, // last
- tailMode
- );
- break;
+ if (
+ workInProgress.tag !== LegacyHiddenComponent &&
+ workInProgress.subtreeFlags & (Placement | Update)
+ ) {
+ workInProgress.flags |= Visibility;
+ }
+ }
}
- case "together": {
- initSuspenseListRenderState(
- workInProgress,
- false, // isBackwards
- null, // tail
- null, // last
- undefined
- );
- break;
+ if (workInProgress.updateQueue !== null) {
+ // Schedule an effect to attach Suspense retry listeners
+ // TODO: Move to passive phase
+ workInProgress.flags |= Update;
}
- default: {
- // The default reveal order is the same as not having
- // a boundary.
- workInProgress.memoizedState = null;
- }
- }
- }
+ {
+ var _previousCache2 = null;
- return workInProgress.child;
-}
+ if (
+ current !== null &&
+ current.memoizedState !== null &&
+ current.memoizedState.cachePool !== null
+ ) {
+ _previousCache2 = current.memoizedState.cachePool.pool;
+ }
-function updatePortalComponent(current, workInProgress, renderLanes) {
- pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
- var nextChildren = workInProgress.pendingProps;
+ var _cache2 = null;
- if (current === null) {
- // Portals are special because we don't append the children during mount
- // but at commit. Therefore we need to track insertions which the normal
- // flow doesn't do during mount. This doesn't happen at the root because
- // the root always starts with a "current" with a null child.
- // TODO: Consider unifying this with how the root works.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderLanes
- );
- } else {
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- }
+ if (
+ workInProgress.memoizedState !== null &&
+ workInProgress.memoizedState.cachePool !== null
+ ) {
+ _cache2 = workInProgress.memoizedState.cachePool.pool;
+ }
- return workInProgress.child;
-}
+ if (_cache2 !== _previousCache2) {
+ // Run passive effects to retain/release the cache.
+ workInProgress.flags |= Passive$1;
+ }
+ }
-var hasWarnedAboutUsingNoValuePropOnContextProvider = false;
+ popTransition(workInProgress, current);
+ return null;
+ }
-function updateContextProvider(current, workInProgress, renderLanes) {
- var providerType = workInProgress.type;
- var context = providerType._context;
- var newProps = workInProgress.pendingProps;
- var oldProps = workInProgress.memoizedProps;
- var newValue = newProps.value;
+ case CacheComponent: {
+ {
+ var _previousCache3 = null;
- {
- if (!("value" in newProps)) {
- if (!hasWarnedAboutUsingNoValuePropOnContextProvider) {
- hasWarnedAboutUsingNoValuePropOnContextProvider = true;
+ if (current !== null) {
+ _previousCache3 = current.memoizedState.cache;
+ }
- error(
- "The `value` prop is required for the ``. Did you misspell it or forget to pass it?"
- );
- }
- }
+ var _cache3 = workInProgress.memoizedState.cache;
- var providerPropTypes = workInProgress.type.propTypes;
+ if (_cache3 !== _previousCache3) {
+ // Run passive effects to retain/release the cache.
+ workInProgress.flags |= Passive$1;
+ }
- if (providerPropTypes) {
- checkPropTypes(providerPropTypes, newProps, "prop", "Context.Provider");
- }
- }
+ popCacheProvider(workInProgress);
+ bubbleProperties(workInProgress);
+ }
- pushProvider(workInProgress, context, newValue);
+ return null;
+ }
- if (enableLazyContextPropagation);
- else {
- if (oldProps !== null) {
- var oldValue = oldProps.value;
+ case TracingMarkerComponent: {
+ if (enableTransitionTracing) {
+ var _instance3 = workInProgress.stateNode;
- if (objectIs(oldValue, newValue)) {
- // No change. Bailout early if children are the same.
- if (oldProps.children === newProps.children && !hasContextChanged()) {
- return bailoutOnAlreadyFinishedWork(
- current,
- workInProgress,
- renderLanes
- );
+ if (_instance3 !== null) {
+ popMarkerInstance(workInProgress);
}
- } else {
- // The context value changed. Search for matching consumers and schedule
- // them to update.
- propagateContextChange(workInProgress, context, renderLanes);
+
+ bubbleProperties(workInProgress);
}
+
+ return null;
}
}
- var newChildren = newProps.children;
- reconcileChildren(current, workInProgress, newChildren, renderLanes);
- return workInProgress.child;
+ throw new Error(
+ "Unknown unit of work tag (" +
+ workInProgress.tag +
+ "). This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
}
-var hasWarnedAboutUsingContextAsConsumer = false;
-
-function updateContextConsumer(current, workInProgress, renderLanes) {
- var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In
- // DEV mode, we create a separate object for Context.Consumer that acts
- // like a proxy to Context. This proxy object adds unnecessary code in PROD
- // so we use the old behaviour (Context.Consumer references Context) to
- // reduce size and overhead. The separate object references context via
- // a property called "_context", which also gives us the ability to check
- // in DEV mode if this property exists or not and warn if it does not.
-
- {
- if (context._context === undefined) {
- // This may be because it's a Context (rather than a Consumer).
- // Or it may be because it's older React where they're the same thing.
- // We only want to warn if we're sure it's a new React.
- if (context !== context.Consumer) {
- if (!hasWarnedAboutUsingContextAsConsumer) {
- hasWarnedAboutUsingContextAsConsumer = true;
+function unwindWork(current, workInProgress, renderLanes) {
+ // Note: This intentionally doesn't check if we're hydrating because comparing
+ // to the current tree provider fiber is just as fast and less error-prone.
+ // Ideally we would have a special version of the work loop only
+ // for hydration.
+ popTreeContext(workInProgress);
- error(
- "Rendering directly is not supported and will be removed in " +
- "a future major release. Did you mean to render instead?"
- );
- }
- }
- } else {
- context = context._context;
- }
- }
+ switch (workInProgress.tag) {
+ case ClassComponent: {
+ var Component = workInProgress.type;
- var newProps = workInProgress.pendingProps;
- var render = newProps.children;
+ if (isContextProvider(Component)) {
+ popContext(workInProgress);
+ }
- {
- if (typeof render !== "function") {
- error(
- "A context consumer was rendered with multiple children, or a child " +
- "that isn't a function. A context consumer expects a single child " +
- "that is a function. If you did pass a function, make sure there " +
- "is no trailing or leading whitespace around it."
- );
- }
- }
+ var flags = workInProgress.flags;
- prepareToReadContext(workInProgress, renderLanes);
- var newValue = readContext(context);
+ if (flags & ShouldCapture) {
+ workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
- if (enableSchedulingProfiler) {
- markComponentRenderStarted(workInProgress);
- }
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ transferActualDuration(workInProgress);
+ }
- var newChildren;
+ return workInProgress;
+ }
- {
- ReactCurrentOwner$2.current = workInProgress;
- setIsRendering(true);
- newChildren = render(newValue);
- setIsRendering(false);
- }
+ return null;
+ }
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
- } // React DevTools reads this flag.
+ case HostRoot: {
+ {
+ popCacheProvider(workInProgress);
+ }
- workInProgress.flags |= PerformedWork;
- reconcileChildren(current, workInProgress, newChildren, renderLanes);
- return workInProgress.child;
-}
+ if (enableTransitionTracing) {
+ popRootMarkerInstance(workInProgress);
+ }
-function updateScopeComponent(current, workInProgress, renderLanes) {
- var nextProps = workInProgress.pendingProps;
- var nextChildren = nextProps.children;
- reconcileChildren(current, workInProgress, nextChildren, renderLanes);
- return workInProgress.child;
-}
+ popRootTransition(workInProgress);
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ resetWorkInProgressVersions();
+ var _flags = workInProgress.flags;
-function markWorkInProgressReceivedUpdate() {
- didReceiveUpdate = true;
-}
-function checkIfWorkInProgressReceivedUpdate() {
- return didReceiveUpdate;
-}
+ if (
+ (_flags & ShouldCapture) !== NoFlags$1 &&
+ (_flags & DidCapture) === NoFlags$1
+ ) {
+ // There was an error during render that wasn't captured by a suspense
+ // boundary. Do a second pass on the root to unmount the children.
+ workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture;
+ return workInProgress;
+ } // We unwound to the root without completing it. Exit.
-function resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress) {
- if ((workInProgress.mode & ConcurrentMode) === NoMode) {
- if (current !== null) {
- // A lazy component only mounts if it suspended inside a non-
- // concurrent tree, in an inconsistent state. We want to treat it like
- // a new mount, even though an empty version of it already committed.
- // Disconnect the alternate pointers.
- current.alternate = null;
- workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect
+ return null;
+ }
- workInProgress.flags |= Placement;
+ case HostHoistable:
+ case HostSingleton:
+ case HostComponent: {
+ // TODO: popHydrationState
+ popHostContext(workInProgress);
+ return null;
}
- }
-}
-function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
- if (current !== null) {
- // Reuse previous dependencies
- workInProgress.dependencies = current.dependencies;
- }
+ case SuspenseComponent: {
+ popSuspenseHandler(workInProgress);
+ var suspenseState = workInProgress.memoizedState;
- {
- // Don't update "base" render times for bailouts.
- stopProfilerTimerIfRunning();
- }
+ if (suspenseState !== null && suspenseState.dehydrated !== null) {
+ if (workInProgress.alternate === null) {
+ throw new Error(
+ "Threw in newly mounted dehydrated component. This is likely a bug in " +
+ "React. Please file an issue."
+ );
+ }
- markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work.
+ resetHydrationState();
+ }
- if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
- // The children don't have any work either. We can skip them.
- // TODO: Once we add back resuming, we should check if the children are
- // a work-in-progress set. If so, we need to transfer their effects.
- if (enableLazyContextPropagation && current !== null) {
- // Before bailing out, check if there are any context changes in
- // the children.
- lazilyPropagateParentContextChanges(current, workInProgress, renderLanes);
+ var _flags2 = workInProgress.flags;
- if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
- return null;
+ if (_flags2 & ShouldCapture) {
+ workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary.
+
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ transferActualDuration(workInProgress);
+ }
+
+ return workInProgress;
}
- } else {
+
return null;
}
- } // This fiber doesn't have work, but its subtree does. Clone the child
- // fibers and continue.
- cloneChildFibers(current, workInProgress);
- return workInProgress.child;
-}
+ case SuspenseListComponent: {
+ popSuspenseListContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been
+ // caught by a nested boundary. If not, it should bubble through.
-function remountFiber(current, oldWorkInProgress, newWorkInProgress) {
- {
- var returnFiber = oldWorkInProgress.return;
+ return null;
+ }
- if (returnFiber === null) {
- // eslint-disable-next-line react-internal/prod-error-codes
- throw new Error("Cannot swap the root fiber.");
- } // Disconnect from the old current.
- // It will get deleted.
+ case HostPortal:
+ popHostContainer(workInProgress);
+ return null;
- current.alternate = null;
- oldWorkInProgress.alternate = null; // Connect to the new tree.
+ case ContextProvider:
+ var context = workInProgress.type._context;
+ popProvider(context, workInProgress);
+ return null;
- newWorkInProgress.index = oldWorkInProgress.index;
- newWorkInProgress.sibling = oldWorkInProgress.sibling;
- newWorkInProgress.return = oldWorkInProgress.return;
- newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it.
+ case OffscreenComponent:
+ case LegacyHiddenComponent: {
+ popSuspenseHandler(workInProgress);
+ popHiddenContext(workInProgress);
+ popTransition(workInProgress, current);
+ var _flags3 = workInProgress.flags;
- if (oldWorkInProgress === returnFiber.child) {
- returnFiber.child = newWorkInProgress;
- } else {
- var prevSibling = returnFiber.child;
+ if (_flags3 & ShouldCapture) {
+ workInProgress.flags = (_flags3 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary.
- if (prevSibling === null) {
- // eslint-disable-next-line react-internal/prod-error-codes
- throw new Error("Expected parent to have a child.");
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if ((workInProgress.mode & ProfileMode) !== NoMode) {
+ transferActualDuration(workInProgress);
+ }
- while (prevSibling.sibling !== oldWorkInProgress) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- prevSibling = prevSibling.sibling;
+ return workInProgress;
+ }
- if (prevSibling === null) {
- // eslint-disable-next-line react-internal/prod-error-codes
- throw new Error("Expected to find the previous sibling.");
- }
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ return null;
+ }
- prevSibling.sibling = newWorkInProgress;
- } // Delete the old fiber and place the new one.
- // Since the old fiber is disconnected, we have to schedule it manually.
+ case CacheComponent:
+ {
+ popCacheProvider(workInProgress);
+ }
- var deletions = returnFiber.deletions;
+ return null;
- if (deletions === null) {
- returnFiber.deletions = [current];
- returnFiber.flags |= ChildDeletion;
- } else {
- deletions.push(current);
- }
+ case TracingMarkerComponent:
+ if (enableTransitionTracing) {
+ if (workInProgress.stateNode !== null) {
+ popMarkerInstance(workInProgress);
+ }
+ }
- newWorkInProgress.flags |= Placement; // Restart work from the new fiber.
+ return null;
- return newWorkInProgress;
+ default:
+ return null;
}
}
-function checkScheduledUpdateOrContext(current, renderLanes) {
- // Before performing an early bailout, we must check if there are pending
- // updates or context.
- var updateLanes = current.lanes;
+function unwindInterruptedWork(current, interruptedWork, renderLanes) {
+ // Note: This intentionally doesn't check if we're hydrating because comparing
+ // to the current tree provider fiber is just as fast and less error-prone.
+ // Ideally we would have a special version of the work loop only
+ // for hydration.
+ popTreeContext(interruptedWork);
- if (includesSomeLane(updateLanes, renderLanes)) {
- return true;
- } // No pending update, but because context is propagated lazily, we need
- // to check for a context change before we bail out.
+ switch (interruptedWork.tag) {
+ case ClassComponent: {
+ var childContextTypes = interruptedWork.type.childContextTypes;
- if (enableLazyContextPropagation) {
- var dependencies = current.dependencies;
+ if (childContextTypes !== null && childContextTypes !== undefined) {
+ popContext(interruptedWork);
+ }
- if (dependencies !== null && checkIfContextChanged(dependencies)) {
- return true;
+ break;
}
- }
-
- return false;
-}
-
-function attemptEarlyBailoutIfNoScheduledUpdate(
- current,
- workInProgress,
- renderLanes
-) {
- // This fiber does not have any pending work. Bailout without entering
- // the begin phase. There's still some bookkeeping we that needs to be done
- // in this optimized path, mostly pushing stuff onto the stack.
- switch (workInProgress.tag) {
- case HostRoot:
- pushHostRootContext(workInProgress);
- pushRootTransition(workInProgress);
- if (enableTransitionTracing) {
- pushRootMarkerInstance(workInProgress);
+ case HostRoot: {
+ {
+ popCacheProvider(interruptedWork);
}
- {
- var cache = current.memoizedState.cache;
- pushCacheProvider(workInProgress, cache);
+ if (enableTransitionTracing) {
+ popRootMarkerInstance(interruptedWork);
}
- resetHydrationState();
+ popRootTransition(interruptedWork);
+ popHostContainer(interruptedWork);
+ popTopLevelContextObject(interruptedWork);
+ resetWorkInProgressVersions();
break;
+ }
+ case HostHoistable:
case HostSingleton:
- case HostComponent:
- pushHostContext(workInProgress);
+ case HostComponent: {
+ popHostContext(interruptedWork);
break;
+ }
- case ClassComponent: {
- var Component = workInProgress.type;
+ case HostPortal:
+ popHostContainer(interruptedWork);
+ break;
- if (isContextProvider(Component)) {
- pushContextProvider(workInProgress);
- }
+ case SuspenseComponent:
+ popSuspenseHandler(interruptedWork);
+ break;
+ case SuspenseListComponent:
+ popSuspenseListContext(interruptedWork);
break;
- }
- case HostPortal:
- pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ case ContextProvider:
+ var context = interruptedWork.type._context;
+ popProvider(context, interruptedWork);
break;
- case ContextProvider: {
- var newValue = workInProgress.memoizedProps.value;
- var context = workInProgress.type._context;
- pushProvider(workInProgress, context, newValue);
+ case OffscreenComponent:
+ case LegacyHiddenComponent:
+ popSuspenseHandler(interruptedWork);
+ popHiddenContext(interruptedWork);
+ popTransition(interruptedWork, current);
break;
- }
- case Profiler:
+ case CacheComponent:
{
- // Profiler should only call onRender when one of its descendants actually rendered.
- var hasChildWork = includesSomeLane(
- renderLanes,
- workInProgress.childLanes
- );
+ popCacheProvider(interruptedWork);
+ }
- if (hasChildWork) {
- workInProgress.flags |= Update;
- }
+ break;
- {
- // Reset effect durations for the next eventual effect phase.
- // These are reset during render to allow the DevTools commit hook a chance to read them,
- var stateNode = workInProgress.stateNode;
- stateNode.effectDuration = 0;
- stateNode.passiveEffectDuration = 0;
+ case TracingMarkerComponent:
+ if (enableTransitionTracing) {
+ var instance = interruptedWork.stateNode;
+
+ if (instance !== null) {
+ popMarkerInstance(interruptedWork);
}
}
break;
+ }
+}
- case SuspenseComponent: {
- var state = workInProgress.memoizedState;
+// Provided by www
+var ReactFbErrorUtils = require("ReactFbErrorUtils");
- if (state !== null) {
- if (state.dehydrated !== null) {
- // We're not going to render the children, so this is just to maintain
- // push/pop symmetry
- pushPrimaryTreeSuspenseHandler(workInProgress); // We know that this component will suspend again because if it has
- // been unsuspended it has committed as a resolved Suspense component.
- // If it needs to be retried, it should have work scheduled on it.
+if (typeof ReactFbErrorUtils.invokeGuardedCallback !== "function") {
+ throw new Error(
+ "Expected ReactFbErrorUtils.invokeGuardedCallback to be a function."
+ );
+}
+
+function invokeGuardedCallbackImpl(name, func, context, a, b, c, d, e, f) {
+ // This will call `this.onError(err)` if an error was caught.
+ ReactFbErrorUtils.invokeGuardedCallback.apply(this, arguments);
+}
+
+var hasError = false;
+var caughtError = null; // Used by event system to capture/rethrow the first error.
+
+var hasRethrowError = false;
+var rethrowError = null;
+var reporter = {
+ onError: function (error) {
+ hasError = true;
+ caughtError = error;
+ }
+};
+/**
+ * Call a function while guarding against errors that happens within it.
+ * Returns an error if it throws, otherwise null.
+ *
+ * In production, this is implemented using a try-catch. The reason we don't
+ * use a try-catch directly is so that we can swap out a different
+ * implementation in DEV mode.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+
+function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
+ hasError = false;
+ caughtError = null;
+ invokeGuardedCallbackImpl.apply(reporter, 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
+ */
+
+function invokeGuardedCallbackAndCatchFirstError(
+ name,
+ func,
+ context,
+ a,
+ b,
+ c,
+ d,
+ e,
+ f
+) {
+ invokeGuardedCallback.apply(this, arguments);
+
+ if (hasError) {
+ var error = clearCaughtError();
+
+ if (!hasRethrowError) {
+ hasRethrowError = true;
+ 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.
+ */
+
+function rethrowCaughtError() {
+ if (hasRethrowError) {
+ var error = rethrowError;
+ hasRethrowError = false;
+ rethrowError = null;
+ throw error;
+ }
+}
+function hasCaughtError() {
+ return hasError;
+}
+function clearCaughtError() {
+ if (hasError) {
+ var error = caughtError;
+ hasError = false;
+ caughtError = null;
+ return error;
+ } else {
+ throw new Error(
+ "clearCaughtError was called but no error was captured. This error " +
+ "is likely caused by a bug in React. Please file an issue."
+ );
+ }
+}
+
+var didWarnAboutUndefinedSnapshotBeforeUpdate = null;
+
+{
+ didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
+} // Used during the commit phase to track the state of the Offscreen component stack.
+// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
+
+var offscreenSubtreeIsHidden = false;
+var offscreenSubtreeWasHidden = false;
+var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set;
+var nextEffect = null; // Used for Profiling builds to track updaters.
+
+var inProgressLanes = null;
+var inProgressRoot = null;
+
+function shouldProfile(current) {
+ return (
+ (current.mode & ProfileMode) !== NoMode &&
+ (getExecutionContext() & CommitContext) !== NoContext
+ );
+}
+
+function reportUncaughtErrorInDEV(error) {
+ // Wrapping each small part of the commit phase into a guarded
+ // callback is a bit too slow (https://github.com/facebook/react/pull/21666).
+ // But we rely on it to surface errors to DEV tools like overlays
+ // (https://github.com/facebook/react/issues/21712).
+ // As a compromise, rethrow only caught errors in a guard.
+ {
+ invokeGuardedCallback(null, function () {
+ throw error;
+ });
+ clearCaughtError();
+ }
+}
+
+function callComponentWillUnmountWithTimer(current, instance) {
+ instance.props = current.memoizedProps;
+ instance.state = current.memoizedState;
+
+ if (shouldProfile(current)) {
+ try {
+ startLayoutEffectTimer();
+ instance.componentWillUnmount();
+ } finally {
+ recordLayoutEffectDuration(current);
+ }
+ } else {
+ instance.componentWillUnmount();
+ }
+} // Capture errors so they don't interrupt unmounting.
- workInProgress.flags |= DidCapture; // We should never render the children of a dehydrated boundary until we
- // upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
+function safelyCallComponentWillUnmount(
+ current,
+ nearestMountedAncestor,
+ instance
+) {
+ try {
+ callComponentWillUnmountWithTimer(current, instance);
+ } catch (error) {
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
+ }
+} // Capture errors so they don't interrupt mounting.
- return null;
- } // If this boundary is currently timed out, we need to decide
- // whether to retry the primary children, or to skip over it and
- // go straight to the fallback. Check the priority of the primary
- // child fragment.
+function safelyAttachRef(current, nearestMountedAncestor) {
+ try {
+ commitAttachRef(current);
+ } catch (error) {
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
+ }
+}
- var primaryChildFragment = workInProgress.child;
- var primaryChildLanes = primaryChildFragment.childLanes;
+function safelyDetachRef(current, nearestMountedAncestor) {
+ var ref = current.ref;
+ var refCleanup = current.refCleanup;
- if (includesSomeLane(renderLanes, primaryChildLanes)) {
- // The primary children have pending work. Use the normal path
- // to attempt to render the primary children again.
- return updateSuspenseComponent(current, workInProgress, renderLanes);
+ if (ref !== null) {
+ if (typeof refCleanup === "function") {
+ try {
+ if (shouldProfile(current)) {
+ try {
+ startLayoutEffectTimer();
+ refCleanup();
+ } finally {
+ recordLayoutEffectDuration(current);
+ }
} else {
- // The primary child fragment does not have pending work marked
- // on it
- pushPrimaryTreeSuspenseHandler(workInProgress); // The primary children do not have pending work with sufficient
- // priority. Bailout.
+ refCleanup();
+ }
+ } catch (error) {
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
+ } finally {
+ // `refCleanup` has been called. Nullify all references to it to prevent double invocation.
+ current.refCleanup = null;
+ var finishedWork = current.alternate;
- var child = bailoutOnAlreadyFinishedWork(
- current,
- workInProgress,
- renderLanes
- );
+ if (finishedWork != null) {
+ finishedWork.refCleanup = null;
+ }
+ }
+ } else if (typeof ref === "function") {
+ var retVal;
- if (child !== null) {
- // The fallback children have pending work. Skip over the
- // primary children and work on the fallback.
- return child.sibling;
- } else {
- // Note: We can return `null` here because we already checked
- // whether there were nested context consumers, via the call to
- // `bailoutOnAlreadyFinishedWork` above.
- return null;
+ try {
+ if (shouldProfile(current)) {
+ try {
+ startLayoutEffectTimer();
+ retVal = ref(null);
+ } finally {
+ recordLayoutEffectDuration(current);
}
+ } else {
+ retVal = ref(null);
}
- } else {
- pushPrimaryTreeSuspenseHandler(workInProgress);
+ } catch (error) {
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
}
- break;
- }
-
- case SuspenseListComponent: {
- var didSuspendBefore = (current.flags & DidCapture) !== NoFlags$1;
-
- var _hasChildWork = includesSomeLane(
- renderLanes,
- workInProgress.childLanes
- );
-
- if (enableLazyContextPropagation && !_hasChildWork) {
- // Context changes may not have been propagated yet. We need to do
- // that now, before we can decide whether to bail out.
- // TODO: We use `childLanes` as a heuristic for whether there is
- // remaining work in a few places, including
- // `bailoutOnAlreadyFinishedWork` and
- // `updateDehydratedSuspenseComponent`. We should maybe extract this
- // into a dedicated function.
- lazilyPropagateParentContextChanges(
- current,
- workInProgress,
- renderLanes
- );
- _hasChildWork = includesSomeLane(
- renderLanes,
- workInProgress.childLanes
- );
+ {
+ if (typeof retVal === "function") {
+ error(
+ "Unexpected return value from a callback ref in %s. " +
+ "A callback ref should not return a function.",
+ getComponentNameFromFiber(current)
+ );
+ }
}
+ } else {
+ // $FlowFixMe unable to narrow type to RefObject
+ ref.current = null;
+ }
+ }
+}
- if (didSuspendBefore) {
- if (_hasChildWork) {
- // If something was in fallback state last time, and we have all the
- // same children then we're still in progressive loading state.
- // Something might get unblocked by state updates or retries in the
- // tree which will affect the tail. So we need to use the normal
- // path to compute the correct tail.
- return updateSuspenseListComponent(
- current,
- workInProgress,
- renderLanes
- );
- } // If none of the children had any work, that means that none of
- // them got retried so they'll still be blocked in the same way
- // as before. We can fast bail out.
+function safelyCallDestroy(current, nearestMountedAncestor, destroy) {
+ try {
+ destroy();
+ } catch (error) {
+ captureCommitPhaseError(current, nearestMountedAncestor, error);
+ }
+}
- workInProgress.flags |= DidCapture;
- } // If nothing suspended before and we're rendering the same children,
- // then the tail doesn't matter. Anything new that suspends will work
- // in the "together" mode, so we can continue from the state we had.
+var focusedInstanceHandle = null;
+var shouldFireAfterActiveInstanceBlur = false;
+function commitBeforeMutationEffects(root, firstChild) {
+ focusedInstanceHandle = prepareForCommit();
+ nextEffect = firstChild;
+ commitBeforeMutationEffects_begin(); // We no longer need to track the active instance fiber
- var renderState = workInProgress.memoizedState;
+ var shouldFire = shouldFireAfterActiveInstanceBlur;
+ shouldFireAfterActiveInstanceBlur = false;
+ focusedInstanceHandle = null;
+ return shouldFire;
+}
- if (renderState !== null) {
- // Reset to the "together" mode in case we've started a different
- // update in the past but didn't complete it.
- renderState.rendering = null;
- renderState.tail = null;
- renderState.lastEffect = null;
- }
+function commitBeforeMutationEffects_begin() {
+ while (nextEffect !== null) {
+ var fiber = nextEffect; // This phase is only used for beforeActiveInstanceBlur.
+ // Let's skip the whole loop if it's off.
- pushSuspenseListContext(workInProgress, suspenseStackCursor.current);
+ {
+ // TODO: Should wrap this in flags check, too, as optimization
+ var deletions = fiber.deletions;
- if (_hasChildWork) {
- break;
- } else {
- // If none of the children had any work, that means that none of
- // them got retried so they'll still be blocked in the same way
- // as before. We can fast bail out.
- return null;
+ if (deletions !== null) {
+ for (var i = 0; i < deletions.length; i++) {
+ var deletion = deletions[i];
+ commitBeforeMutationEffectsDeletion(deletion);
+ }
}
}
- case OffscreenComponent:
- case LegacyHiddenComponent: {
- // Need to check if the tree still needs to be deferred. This is
- // almost identical to the logic used in the normal update path,
- // so we'll just enter that. The only difference is we'll bail out
- // at the next level instead of this one, because the child props
- // have not changed. Which is fine.
- // TODO: Probably should refactor `beginWork` to split the bailout
- // path from the normal path. I'm tempted to do a labeled break here
- // but I won't :)
- workInProgress.lanes = NoLanes;
- return updateOffscreenComponent(current, workInProgress, renderLanes);
+ var child = fiber.child;
+
+ if (
+ (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags$1 &&
+ child !== null
+ ) {
+ child.return = fiber;
+ nextEffect = child;
+ } else {
+ commitBeforeMutationEffects_complete();
}
+ }
+}
- case CacheComponent: {
- {
- var _cache = current.memoizedState.cache;
- pushCacheProvider(workInProgress, _cache);
- }
+function commitBeforeMutationEffects_complete() {
+ while (nextEffect !== null) {
+ var fiber = nextEffect;
+ setCurrentFiber(fiber);
- break;
+ try {
+ commitBeforeMutationEffectsOnFiber(fiber);
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
}
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- var instance = workInProgress.stateNode;
+ resetCurrentFiber();
+ var sibling = fiber.sibling;
- if (instance !== null) {
- pushMarkerInstance(workInProgress, instance);
- }
- }
+ if (sibling !== null) {
+ sibling.return = fiber.return;
+ nextEffect = sibling;
+ return;
}
- }
- return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
+ nextEffect = fiber.return;
+ }
}
-function beginWork$1(current, workInProgress, renderLanes) {
+function commitBeforeMutationEffectsOnFiber(finishedWork) {
+ var current = finishedWork.alternate;
+ var flags = finishedWork.flags;
+
{
- if (workInProgress._debugNeedsRemount && current !== null) {
- // This will restart the begin phase with a new fiber.
- return remountFiber(
- current,
- workInProgress,
- createFiberFromTypeAndProps(
- workInProgress.type,
- workInProgress.key,
- workInProgress.pendingProps,
- workInProgress._debugOwner || null,
- workInProgress.mode,
- workInProgress.lanes
- )
- );
+ if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
+ // Check to see if the focused element was inside of a hidden (Suspense) subtree.
+ // TODO: Move this out of the hot path using a dedicated effect tag.
+ if (
+ finishedWork.tag === SuspenseComponent &&
+ isSuspenseBoundaryBeingHidden(current, finishedWork) && // $FlowFixMe[incompatible-call] found when upgrading Flow
+ doesFiberContain(finishedWork, focusedInstanceHandle)
+ ) {
+ shouldFireAfterActiveInstanceBlur = true;
+ beforeActiveInstanceBlur(finishedWork);
+ }
}
}
- if (current !== null) {
- var oldProps = current.memoizedProps;
- var newProps = workInProgress.pendingProps;
-
- if (
- oldProps !== newProps ||
- hasContextChanged() || // Force a re-render if the implementation changed due to hot reload:
- workInProgress.type !== current.type
- ) {
- // If props or context changed, mark the fiber as having performed work.
- // This may be unset if the props are determined to be equal later (memo).
- didReceiveUpdate = true;
- } else {
- // Neither props nor legacy context changes. Check if there's a pending
- // update or context change.
- var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(
- current,
- renderLanes
- );
+ if ((flags & Snapshot) !== NoFlags$1) {
+ setCurrentFiber(finishedWork);
+ }
- if (
- !hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there
- // may not be work scheduled on `current`, so we check for this flag.
- (workInProgress.flags & DidCapture) === NoFlags$1
- ) {
- // No pending updates or context. Bail out now.
- didReceiveUpdate = false;
- return attemptEarlyBailoutIfNoScheduledUpdate(
- current,
- workInProgress,
- renderLanes
- );
+ switch (finishedWork.tag) {
+ case FunctionComponent: {
+ {
+ if ((flags & Update) !== NoFlags$1) {
+ commitUseEffectEventMount(finishedWork);
+ }
}
- if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags$1) {
- // This is a special case that only exists for legacy mode.
- // See https://github.com/facebook/react/pull/19216.
- didReceiveUpdate = true;
- } else {
- // An update was scheduled on this fiber, but there are no new props
- // nor legacy context. Set this to false. If an update queue or context
- // consumer produces a changed value, it will set this to true. Otherwise,
- // the component will assume the children have not changed and bail out.
- didReceiveUpdate = false;
- }
+ break;
}
- } else {
- didReceiveUpdate = false;
- if (getIsHydrating() && isForkedChild(workInProgress)) {
- // Check if this child belongs to a list of muliple children in
- // its parent.
- //
- // In a true multi-threaded implementation, we would render children on
- // parallel threads. This would represent the beginning of a new render
- // thread for this subtree.
- //
- // We only use this for id generation during hydration, which is why the
- // logic is located in this special branch.
- var slotIndex = workInProgress.index;
- var numberOfForks = getForksAtLevel();
- pushTreeId(workInProgress, numberOfForks, slotIndex);
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ break;
}
- } // Before entering the begin phase, clear pending update priority.
- // TODO: This assumes that we're about to evaluate the component and process
- // the update queue. However, there's an exception: SimpleMemoComponent
- // sometimes bails out later in the begin phase. This indicates that we should
- // move this assignment out of the common path and into each branch.
- workInProgress.lanes = NoLanes;
+ case ClassComponent: {
+ if ((flags & Snapshot) !== NoFlags$1) {
+ if (current !== null) {
+ var prevProps = current.memoizedProps;
+ var prevState = current.memoizedState;
+ var instance = finishedWork.stateNode; // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
- switch (workInProgress.tag) {
- case IndeterminateComponent: {
- return mountIndeterminateComponent(
- current,
- workInProgress,
- workInProgress.type,
- renderLanes
- );
- }
+ {
+ if (
+ finishedWork.type === finishedWork.elementType &&
+ !didWarnAboutReassigningProps
+ ) {
+ if (instance.props !== finishedWork.memoizedProps) {
+ error(
+ "Expected %s props to match memoized props before " +
+ "getSnapshotBeforeUpdate. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.props`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
- case LazyComponent: {
- var elementType = workInProgress.elementType;
- return mountLazyComponent(
- current,
- workInProgress,
- elementType,
- renderLanes
- );
- }
+ if (instance.state !== finishedWork.memoizedState) {
+ error(
+ "Expected %s state to match memoized state before " +
+ "getSnapshotBeforeUpdate. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.state`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
+ }
+ }
- case FunctionComponent: {
- var Component = workInProgress.type;
- var unresolvedProps = workInProgress.pendingProps;
- var resolvedProps =
- workInProgress.elementType === Component
- ? unresolvedProps
- : resolveDefaultProps(Component, unresolvedProps);
- return updateFunctionComponent(
- current,
- workInProgress,
- Component,
- resolvedProps,
- renderLanes
- );
- }
+ var snapshot = instance.getSnapshotBeforeUpdate(
+ finishedWork.elementType === finishedWork.type
+ ? prevProps
+ : resolveDefaultProps(finishedWork.type, prevProps),
+ prevState
+ );
- case ClassComponent: {
- var _Component = workInProgress.type;
- var _unresolvedProps = workInProgress.pendingProps;
+ {
+ var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate;
- var _resolvedProps =
- workInProgress.elementType === _Component
- ? _unresolvedProps
- : resolveDefaultProps(_Component, _unresolvedProps);
+ if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
+ didWarnSet.add(finishedWork.type);
- return updateClassComponent(
- current,
- workInProgress,
- _Component,
- _resolvedProps,
- renderLanes
- );
- }
+ error(
+ "%s.getSnapshotBeforeUpdate(): A snapshot value (or null) " +
+ "must be returned. You have returned undefined.",
+ getComponentNameFromFiber(finishedWork)
+ );
+ }
+ }
- case HostRoot:
- return updateHostRoot(current, workInProgress, renderLanes);
+ instance.__reactInternalSnapshotBeforeUpdate = snapshot;
+ }
+ }
- case HostHoistable: {
- return updateHostHoistable(current, workInProgress);
+ break;
}
- // eslint-disable-next-line no-fallthrough
+ case HostRoot: {
+ if ((flags & Snapshot) !== NoFlags$1) {
+ {
+ var root = finishedWork.stateNode;
+ clearContainer(root.containerInfo);
+ }
+ }
- case HostSingleton: {
- return updateHostSingleton(current, workInProgress, renderLanes);
+ break;
}
- // eslint-disable-next-line no-fallthrough
-
case HostComponent:
- return updateHostComponent$1(current, workInProgress, renderLanes);
-
+ case HostHoistable:
+ case HostSingleton:
case HostText:
- return updateHostText$1(current, workInProgress);
+ case HostPortal:
+ case IncompleteClassComponent:
+ // Nothing to do for these component types
+ break;
+
+ default: {
+ if ((flags & Snapshot) !== NoFlags$1) {
+ throw new Error(
+ "This unit of work tag should not have side-effects. This error is " +
+ "likely caused by a bug in React. Please file an issue."
+ );
+ }
+ }
+ }
+
+ if ((flags & Snapshot) !== NoFlags$1) {
+ resetCurrentFiber();
+ }
+}
+
+function commitBeforeMutationEffectsDeletion(deletion) {
+ {
+ // TODO (effects) It would be nice to avoid calling doesFiberContain()
+ // Maybe we can repurpose one of the subtreeFlags positions for this instead?
+ // Use it to store which part of the tree the focused instance is in?
+ // This assumes we can safely determine that instance during the "render" phase.
+ if (doesFiberContain(deletion, focusedInstanceHandle)) {
+ shouldFireAfterActiveInstanceBlur = true;
+ beforeActiveInstanceBlur(deletion);
+ }
+ }
+}
- case SuspenseComponent:
- return updateSuspenseComponent(current, workInProgress, renderLanes);
+function commitHookEffectListUnmount(
+ flags,
+ finishedWork,
+ nearestMountedAncestor
+) {
+ var updateQueue = finishedWork.updateQueue;
+ var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
- case HostPortal:
- return updatePortalComponent(current, workInProgress, renderLanes);
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
- case ForwardRef: {
- var type = workInProgress.type;
- var _unresolvedProps2 = workInProgress.pendingProps;
+ do {
+ if ((effect.tag & flags) === flags) {
+ // Unmount
+ var destroy = effect.destroy;
+ effect.destroy = undefined;
- var _resolvedProps2 =
- workInProgress.elementType === type
- ? _unresolvedProps2
- : resolveDefaultProps(type, _unresolvedProps2);
+ if (destroy !== undefined) {
+ if (enableSchedulingProfiler) {
+ if ((flags & Passive) !== NoFlags) {
+ markComponentPassiveEffectUnmountStarted(finishedWork);
+ } else if ((flags & Layout) !== NoFlags) {
+ markComponentLayoutEffectUnmountStarted(finishedWork);
+ }
+ }
- return updateForwardRef(
- current,
- workInProgress,
- type,
- _resolvedProps2,
- renderLanes
- );
- }
+ {
+ if ((flags & Insertion) !== NoFlags) {
+ setIsRunningInsertionEffect(true);
+ }
+ }
- case Fragment:
- return updateFragment(current, workInProgress, renderLanes);
+ safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
- case Mode:
- return updateMode(current, workInProgress, renderLanes);
+ {
+ if ((flags & Insertion) !== NoFlags) {
+ setIsRunningInsertionEffect(false);
+ }
+ }
- case Profiler:
- return updateProfiler(current, workInProgress, renderLanes);
+ if (enableSchedulingProfiler) {
+ if ((flags & Passive) !== NoFlags) {
+ markComponentPassiveEffectUnmountStopped();
+ } else if ((flags & Layout) !== NoFlags) {
+ markComponentLayoutEffectUnmountStopped();
+ }
+ }
+ }
+ }
- case ContextProvider:
- return updateContextProvider(current, workInProgress, renderLanes);
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+}
- case ContextConsumer:
- return updateContextConsumer(current, workInProgress, renderLanes);
+function commitHookEffectListMount(flags, finishedWork) {
+ var updateQueue = finishedWork.updateQueue;
+ var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
- case MemoComponent: {
- var _type2 = workInProgress.type;
- var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
- var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
+ do {
+ if ((effect.tag & flags) === flags) {
+ if (enableSchedulingProfiler) {
+ if ((flags & Passive) !== NoFlags) {
+ markComponentPassiveEffectMountStarted(finishedWork);
+ } else if ((flags & Layout) !== NoFlags) {
+ markComponentLayoutEffectMountStarted(finishedWork);
+ }
+ } // Mount
- {
- if (workInProgress.type !== workInProgress.elementType) {
- var outerPropTypes = _type2.propTypes;
+ var create = effect.create;
- if (outerPropTypes) {
- checkPropTypes(
- outerPropTypes,
- _resolvedProps3, // Resolved for outer only
- "prop",
- getComponentNameFromType(_type2)
- );
+ {
+ if ((flags & Insertion) !== NoFlags) {
+ setIsRunningInsertionEffect(true);
}
}
- }
- _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
- return updateMemoComponent(
- current,
- workInProgress,
- _type2,
- _resolvedProps3,
- renderLanes
- );
- }
+ effect.destroy = create();
- case SimpleMemoComponent: {
- return updateSimpleMemoComponent(
- current,
- workInProgress,
- workInProgress.type,
- workInProgress.pendingProps,
- renderLanes
- );
- }
+ {
+ if ((flags & Insertion) !== NoFlags) {
+ setIsRunningInsertionEffect(false);
+ }
+ }
- case IncompleteClassComponent: {
- var _Component2 = workInProgress.type;
- var _unresolvedProps4 = workInProgress.pendingProps;
+ if (enableSchedulingProfiler) {
+ if ((flags & Passive) !== NoFlags) {
+ markComponentPassiveEffectMountStopped();
+ } else if ((flags & Layout) !== NoFlags) {
+ markComponentLayoutEffectMountStopped();
+ }
+ }
- var _resolvedProps4 =
- workInProgress.elementType === _Component2
- ? _unresolvedProps4
- : resolveDefaultProps(_Component2, _unresolvedProps4);
+ {
+ var destroy = effect.destroy;
- return mountIncompleteClassComponent(
- current,
- workInProgress,
- _Component2,
- _resolvedProps4,
- renderLanes
- );
- }
+ if (destroy !== undefined && typeof destroy !== "function") {
+ var hookName = void 0;
- case SuspenseListComponent: {
- return updateSuspenseListComponent(current, workInProgress, renderLanes);
- }
+ if ((effect.tag & Layout) !== NoFlags$1) {
+ hookName = "useLayoutEffect";
+ } else if ((effect.tag & Insertion) !== NoFlags$1) {
+ hookName = "useInsertionEffect";
+ } else {
+ hookName = "useEffect";
+ }
- case ScopeComponent: {
- {
- return updateScopeComponent(current, workInProgress, renderLanes);
- }
- }
+ var addendum = void 0;
- case OffscreenComponent: {
- return updateOffscreenComponent(current, workInProgress, renderLanes);
- }
+ if (destroy === null) {
+ addendum =
+ " You returned null. If your effect does not require clean " +
+ "up, return undefined (or nothing).";
+ } else if (typeof destroy.then === "function") {
+ addendum =
+ "\n\nIt looks like you wrote " +
+ hookName +
+ "(async () => ...) or returned a Promise. " +
+ "Instead, write the async function inside your effect " +
+ "and call it immediately:\n\n" +
+ hookName +
+ "(() => {\n" +
+ " async function fetchData() {\n" +
+ " // You can await here\n" +
+ " const response = await MyAPI.getData(someId);\n" +
+ " // ...\n" +
+ " }\n" +
+ " fetchData();\n" +
+ "}, [someId]); // Or [] if effect doesn't need props or state\n\n" +
+ "Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching";
+ } else {
+ addendum = " You returned: " + destroy;
+ }
- case LegacyHiddenComponent: {
- {
- return updateLegacyHiddenComponent(
- current,
- workInProgress,
- renderLanes
- );
+ error(
+ "%s must not return anything besides a function, " +
+ "which is used for clean-up.%s",
+ hookName,
+ addendum
+ );
+ }
+ }
}
- }
- case CacheComponent: {
- {
- return updateCacheComponent(current, workInProgress, renderLanes);
- }
- }
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+}
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- return updateTracingMarkerComponent(
- current,
- workInProgress,
- renderLanes
- );
- }
+function commitUseEffectEventMount(finishedWork) {
+ var updateQueue = finishedWork.updateQueue;
+ var eventPayloads = updateQueue !== null ? updateQueue.events : null;
- break;
+ if (eventPayloads !== null) {
+ for (var ii = 0; ii < eventPayloads.length; ii++) {
+ var _eventPayloads$ii = eventPayloads[ii],
+ ref = _eventPayloads$ii.ref,
+ nextImpl = _eventPayloads$ii.nextImpl;
+ ref.impl = nextImpl;
}
}
-
- throw new Error(
- "Unknown unit of work tag (" +
- workInProgress.tag +
- "). This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
}
-var valueCursor = createCursor(null);
-var rendererCursorDEV;
+function commitPassiveEffectDurations(finishedRoot, finishedWork) {
+ if (getExecutionContext() & CommitContext) {
+ // Only Profilers with work in their subtree will have an Update effect scheduled.
+ if ((finishedWork.flags & Update) !== NoFlags$1) {
+ switch (finishedWork.tag) {
+ case Profiler: {
+ var passiveEffectDuration =
+ finishedWork.stateNode.passiveEffectDuration;
+ var _finishedWork$memoize = finishedWork.memoizedProps,
+ id = _finishedWork$memoize.id,
+ onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase.
+ // It does not get reset until the start of the next commit phase.
-{
- rendererCursorDEV = createCursor(null);
-}
+ var commitTime = getCommitTime();
+ var phase = finishedWork.alternate === null ? "mount" : "update";
-var rendererSigil;
+ {
+ if (isCurrentUpdateNested()) {
+ phase = "nested-update";
+ }
+ }
-{
- // Use this to detect multiple renderers using the same context
- rendererSigil = {};
-}
+ if (typeof onPostCommit === "function") {
+ onPostCommit(id, phase, passiveEffectDuration, commitTime);
+ } // Bubble times to the next nearest ancestor Profiler.
+ // After we process that Profiler, we'll bubble further up.
-var currentlyRenderingFiber = null;
-var lastContextDependency = null;
-var lastFullyObservedContext = null;
-var isDisallowedContextReadInDEV = false;
-function resetContextDependencies() {
- // This is called right before React yields execution, to ensure `readContext`
- // cannot be called outside the render phase.
- currentlyRenderingFiber = null;
- lastContextDependency = null;
- lastFullyObservedContext = null;
+ var parentFiber = finishedWork.return;
- {
- isDisallowedContextReadInDEV = false;
- }
-}
-function enterDisallowedContextReadInDEV() {
- {
- isDisallowedContextReadInDEV = true;
- }
-}
-function exitDisallowedContextReadInDEV() {
- {
- isDisallowedContextReadInDEV = false;
- }
-}
-function pushProvider(providerFiber, context, nextValue) {
- {
- push(valueCursor, context._currentValue, providerFiber);
- context._currentValue = nextValue;
+ outer: while (parentFiber !== null) {
+ switch (parentFiber.tag) {
+ case HostRoot:
+ var root = parentFiber.stateNode;
+ root.passiveEffectDuration += passiveEffectDuration;
+ break outer;
- {
- push(rendererCursorDEV, context._currentRenderer, providerFiber);
+ case Profiler:
+ var parentStateNode = parentFiber.stateNode;
+ parentStateNode.passiveEffectDuration += passiveEffectDuration;
+ break outer;
+ }
- if (
- context._currentRenderer !== undefined &&
- context._currentRenderer !== null &&
- context._currentRenderer !== rendererSigil
- ) {
- error(
- "Detected multiple renderers concurrently rendering the " +
- "same context provider. This is currently unsupported."
- );
- }
+ parentFiber = parentFiber.return;
+ }
- context._currentRenderer = rendererSigil;
+ break;
+ }
+ }
}
}
}
-function popProvider(context, providerFiber) {
- var currentValue = valueCursor.current;
- {
- if (currentValue === REACT_SERVER_CONTEXT_DEFAULT_VALUE_NOT_LOADED) {
- context._currentValue = context._defaultValue;
- } else {
- context._currentValue = currentValue;
+function commitHookLayoutEffects(finishedWork, hookFlags) {
+ // At this point layout effects have already been destroyed (during mutation phase).
+ // This is done to prevent sibling component effects from interfering with each other,
+ // e.g. a destroy function in one component should never override a ref set
+ // by a create function in another component during the same commit.
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ commitHookEffectListMount(hookFlags, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
- {
- var currentRenderer = rendererCursorDEV.current;
- pop(rendererCursorDEV, providerFiber);
- context._currentRenderer = currentRenderer;
+ recordLayoutEffectDuration(finishedWork);
+ } else {
+ try {
+ commitHookEffectListMount(hookFlags, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
-
- pop(valueCursor, providerFiber);
}
-function scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {
- // Update the child lanes of all the ancestors, including the alternates.
- var node = parent;
- while (node !== null) {
- var alternate = node.alternate;
+function commitClassLayoutLifecycles(finishedWork, current) {
+ var instance = finishedWork.stateNode;
- if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
- node.childLanes = mergeLanes(node.childLanes, renderLanes);
+ if (current === null) {
+ // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
+ {
+ if (
+ finishedWork.type === finishedWork.elementType &&
+ !didWarnAboutReassigningProps
+ ) {
+ if (instance.props !== finishedWork.memoizedProps) {
+ error(
+ "Expected %s props to match memoized props before " +
+ "componentDidMount. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.props`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
- if (alternate !== null) {
- alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
+ if (instance.state !== finishedWork.memoizedState) {
+ error(
+ "Expected %s state to match memoized state before " +
+ "componentDidMount. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.state`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
}
- } else if (
- alternate !== null &&
- !isSubsetOfLanes(alternate.childLanes, renderLanes)
- ) {
- alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
- } else;
-
- if (node === propagationRoot) {
- break;
}
- node = node.return;
- }
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ instance.componentDidMount();
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
- {
- if (node !== propagationRoot) {
- error(
- "Expected to find the propagation root when scheduling context work. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
+ recordLayoutEffectDuration(finishedWork);
+ } else {
+ try {
+ instance.componentDidMount();
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
}
- }
-}
-function propagateContextChange(workInProgress, context, renderLanes) {
- if (enableLazyContextPropagation) {
- // TODO: This path is only used by Cache components. Update
- // lazilyPropagateParentContextChanges to look for Cache components so they
- // can take advantage of lazy propagation.
- var forcePropagateEntireTree = true;
- propagateContextChanges(
- workInProgress,
- [context],
- renderLanes,
- forcePropagateEntireTree
- );
} else {
- propagateContextChange_eager(workInProgress, context, renderLanes);
- }
-}
-
-function propagateContextChange_eager(workInProgress, context, renderLanes) {
- // Only used by eager implementation
- if (enableLazyContextPropagation) {
- return;
- }
-
- var fiber = workInProgress.child;
-
- if (fiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- fiber.return = workInProgress;
- }
+ var prevProps =
+ finishedWork.elementType === finishedWork.type
+ ? current.memoizedProps
+ : resolveDefaultProps(finishedWork.type, current.memoizedProps);
+ var prevState = current.memoizedState; // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
- while (fiber !== null) {
- var nextFiber = void 0; // Visit this fiber.
+ {
+ if (
+ finishedWork.type === finishedWork.elementType &&
+ !didWarnAboutReassigningProps
+ ) {
+ if (instance.props !== finishedWork.memoizedProps) {
+ error(
+ "Expected %s props to match memoized props before " +
+ "componentDidUpdate. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.props`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
- var list = fiber.dependencies;
+ if (instance.state !== finishedWork.memoizedState) {
+ error(
+ "Expected %s state to match memoized state before " +
+ "componentDidUpdate. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.state`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
+ }
+ }
- if (list !== null) {
- nextFiber = fiber.child;
- var dependency = list.firstContext;
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ instance.componentDidUpdate(
+ prevProps,
+ prevState,
+ instance.__reactInternalSnapshotBeforeUpdate
+ );
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
- while (dependency !== null) {
- // Check if the context matches.
- if (dependency.context === context) {
- // Match! Schedule an update on this fiber.
- if (fiber.tag === ClassComponent) {
- // Schedule a force update on the work-in-progress.
- var lane = pickArbitraryLane(renderLanes);
- var update = createUpdate(lane);
- update.tag = ForceUpdate; // TODO: Because we don't have a work-in-progress, this will add the
- // update to the current fiber, too, which means it will persist even if
- // this render is thrown away. Since it's a race condition, not sure it's
- // worth fixing.
- // Inlined `enqueueUpdate` to remove interleaved update check
+ recordLayoutEffectDuration(finishedWork);
+ } else {
+ try {
+ instance.componentDidUpdate(
+ prevProps,
+ prevState,
+ instance.__reactInternalSnapshotBeforeUpdate
+ );
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
+ }
+}
- var updateQueue = fiber.updateQueue;
+function commitClassCallbacks(finishedWork) {
+ // TODO: I think this is now always non-null by the time it reaches the
+ // commit phase. Consider removing the type check.
+ var updateQueue = finishedWork.updateQueue;
- if (updateQueue === null);
- else {
- var sharedQueue = updateQueue.shared;
- var pending = sharedQueue.pending;
+ if (updateQueue !== null) {
+ var instance = finishedWork.stateNode;
- if (pending === null) {
- // This is the first update. Create a circular list.
- update.next = update;
- } else {
- update.next = pending.next;
- pending.next = update;
- }
+ {
+ if (
+ finishedWork.type === finishedWork.elementType &&
+ !didWarnAboutReassigningProps
+ ) {
+ if (instance.props !== finishedWork.memoizedProps) {
+ error(
+ "Expected %s props to match memoized props before " +
+ "processing the update queue. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.props`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
- sharedQueue.pending = update;
- }
- }
+ if (instance.state !== finishedWork.memoizedState) {
+ error(
+ "Expected %s state to match memoized state before " +
+ "processing the update queue. " +
+ "This might either be because of a bug in React, or because " +
+ "a component reassigns its own `this.state`. " +
+ "Please file an issue.",
+ getComponentNameFromFiber(finishedWork) || "instance"
+ );
+ }
+ }
+ } // We could update instance props and state here,
+ // but instead we rely on them being set during last render.
+ // TODO: revisit this when we implement resuming.
- fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
- var alternate = fiber.alternate;
+ try {
+ commitCallbacks(updateQueue, instance);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
+}
- if (alternate !== null) {
- alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
- }
+function commitHostComponentMount(finishedWork) {
+ var type = finishedWork.type;
+ var props = finishedWork.memoizedProps;
+ var instance = finishedWork.stateNode;
- scheduleContextWorkOnParentPath(
- fiber.return,
- renderLanes,
- workInProgress
- ); // Mark the updated lanes on the list, too.
+ try {
+ commitMount(instance, type, props, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+}
- list.lanes = mergeLanes(list.lanes, renderLanes); // Since we already found a match, we can stop traversing the
- // dependency list.
+function commitProfilerUpdate(finishedWork, current) {
+ if (getExecutionContext() & CommitContext) {
+ try {
+ var _finishedWork$memoize2 = finishedWork.memoizedProps,
+ onCommit = _finishedWork$memoize2.onCommit,
+ onRender = _finishedWork$memoize2.onRender;
+ var effectDuration = finishedWork.stateNode.effectDuration;
+ var commitTime = getCommitTime();
+ var phase = current === null ? "mount" : "update";
- break;
+ if (enableProfilerNestedUpdatePhase) {
+ if (isCurrentUpdateNested()) {
+ phase = "nested-update";
}
-
- dependency = dependency.next;
}
- } else if (fiber.tag === ContextProvider) {
- // Don't scan deeper if this is a matching provider
- nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
- } else if (fiber.tag === DehydratedFragment) {
- // If a dehydrated suspense boundary is in this subtree, we don't know
- // if it will have any context consumers in it. The best we can do is
- // mark it as having updates.
- var parentSuspense = fiber.return;
- if (parentSuspense === null) {
- throw new Error(
- "We just came from a parent so we must have had a parent. This is a bug in React."
+ if (typeof onRender === "function") {
+ onRender(
+ finishedWork.memoizedProps.id,
+ phase,
+ finishedWork.actualDuration,
+ finishedWork.treeBaseDuration,
+ finishedWork.actualStartTime,
+ commitTime
);
}
- parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
- var _alternate = parentSuspense.alternate;
-
- if (_alternate !== null) {
- _alternate.lanes = mergeLanes(_alternate.lanes, renderLanes);
- } // This is intentionally passing this fiber as the parent
- // because we want to schedule this fiber as having work
- // on its children. We'll use the childLanes on
- // this fiber to indicate that a context has changed.
-
- scheduleContextWorkOnParentPath(
- parentSuspense,
- renderLanes,
- workInProgress
- );
- nextFiber = fiber.sibling;
- } else {
- // Traverse down.
- nextFiber = fiber.child;
- }
+ if (enableProfilerCommitHooks) {
+ if (typeof onCommit === "function") {
+ onCommit(
+ finishedWork.memoizedProps.id,
+ phase,
+ effectDuration,
+ commitTime
+ );
+ } // Schedule a passive effect for this Profiler to call onPostCommit hooks.
+ // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
+ // because the effect is also where times bubble to parent Profilers.
- if (nextFiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- nextFiber.return = fiber;
- } else {
- // No child. Traverse to next sibling.
- nextFiber = fiber;
+ enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor.
+ // Do not reset these values until the next render so DevTools has a chance to read them first.
- while (nextFiber !== null) {
- if (nextFiber === workInProgress) {
- // We're back to the root of this subtree. Exit.
- nextFiber = null;
- break;
- }
+ var parentFiber = finishedWork.return;
- var sibling = nextFiber.sibling;
+ outer: while (parentFiber !== null) {
+ switch (parentFiber.tag) {
+ case HostRoot:
+ var root = parentFiber.stateNode;
+ root.effectDuration += effectDuration;
+ break outer;
- if (sibling !== null) {
- // Set the return pointer of the sibling to the work-in-progress fiber.
- sibling.return = nextFiber.return;
- nextFiber = sibling;
- break;
- } // No more siblings. Traverse up.
+ case Profiler:
+ var parentStateNode = parentFiber.stateNode;
+ parentStateNode.effectDuration += effectDuration;
+ break outer;
+ }
- nextFiber = nextFiber.return;
+ parentFiber = parentFiber.return;
+ }
}
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
-
- fiber = nextFiber;
}
}
-function propagateContextChanges(
- workInProgress,
- contexts,
- renderLanes,
- forcePropagateEntireTree
+function commitLayoutEffectOnFiber(
+ finishedRoot,
+ current,
+ finishedWork,
+ committedLanes
) {
- // Only used by lazy implementation
- if (!enableLazyContextPropagation) {
- return;
- }
+ // When updating this function, also update reappearLayoutEffects, which does
+ // most of the same things when an offscreen tree goes from hidden -> visible.
+ var flags = finishedWork.flags;
- var fiber = workInProgress.child;
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- if (fiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- fiber.return = workInProgress;
- }
+ if (flags & Update) {
+ commitHookLayoutEffects(finishedWork, Layout | HasEffect);
+ }
- while (fiber !== null) {
- var nextFiber = void 0; // Visit this fiber.
+ break;
+ }
- var list = fiber.dependencies;
+ case ClassComponent: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- if (list !== null) {
- nextFiber = fiber.child;
- var dep = list.firstContext;
+ if (flags & Update) {
+ commitClassLayoutLifecycles(finishedWork, current);
+ }
- findChangedDep: while (dep !== null) {
- // Assigning these to constants to help Flow
- var dependency = dep;
- var consumer = fiber;
+ if (flags & Callback) {
+ commitClassCallbacks(finishedWork);
+ }
- for (var i = 0; i < contexts.length; i++) {
- var context = contexts[i]; // Check if the context matches.
- // TODO: Compare selected values to bail out early.
+ if (flags & Ref) {
+ safelyAttachRef(finishedWork, finishedWork.return);
+ }
- if (dependency.context === context) {
- // Match! Schedule an update on this fiber.
- // In the lazy implementation, don't mark a dirty flag on the
- // dependency itself. Not all changes are propagated, so we can't
- // rely on the propagation function alone to determine whether
- // something has changed; the consumer will check. In the future, we
- // could add back a dirty flag as an optimization to avoid double
- // checking, but until we have selectors it's not really worth
- // the trouble.
- consumer.lanes = mergeLanes(consumer.lanes, renderLanes);
- var alternate = consumer.alternate;
+ break;
+ }
- if (alternate !== null) {
- alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
- }
+ case HostRoot: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- scheduleContextWorkOnParentPath(
- consumer.return,
- renderLanes,
- workInProgress
- );
+ if (flags & Callback) {
+ // TODO: I think this is now always non-null by the time it reaches the
+ // commit phase. Consider removing the type check.
+ var updateQueue = finishedWork.updateQueue;
- if (!forcePropagateEntireTree) {
- // During lazy propagation, when we find a match, we can defer
- // propagating changes to the children, because we're going to
- // visit them during render. We should continue propagating the
- // siblings, though
- nextFiber = null;
- } // Since we already found a match, we can stop traversing the
- // dependency list.
+ if (updateQueue !== null) {
+ var instance = null;
- break findChangedDep;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostSingleton:
+ case HostComponent:
+ instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+
+ case ClassComponent:
+ instance = finishedWork.child.stateNode;
+ break;
+ }
}
- }
- dep = dependency.next;
+ try {
+ commitCallbacks(updateQueue, instance);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
}
- } else if (fiber.tag === DehydratedFragment) {
- // If a dehydrated suspense boundary is in this subtree, we don't know
- // if it will have any context consumers in it. The best we can do is
- // mark it as having updates.
- var parentSuspense = fiber.return;
- if (parentSuspense === null) {
- throw new Error(
- "We just came from a parent so we must have had a parent. This is a bug in React."
- );
+ break;
+ }
+
+ case HostHoistable: {
+ {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+
+ if (flags & Ref) {
+ safelyAttachRef(finishedWork, finishedWork.return);
+ }
+
+ break;
}
+ }
+ // eslint-disable-next-line-no-fallthrough
- parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
- var _alternate2 = parentSuspense.alternate;
+ case HostSingleton:
+ case HostComponent: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); // 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 (_alternate2 !== null) {
- _alternate2.lanes = mergeLanes(_alternate2.lanes, renderLanes);
- } // This is intentionally passing this fiber as the parent
- // because we want to schedule this fiber as having work
- // on its children. We'll use the childLanes on
- // this fiber to indicate that a context has changed.
+ if (current === null && flags & Update) {
+ commitHostComponentMount(finishedWork);
+ }
- scheduleContextWorkOnParentPath(
- parentSuspense,
- renderLanes,
- workInProgress
- );
- nextFiber = null;
- } else {
- // Traverse down.
- nextFiber = fiber.child;
+ if (flags & Ref) {
+ safelyAttachRef(finishedWork, finishedWork.return);
+ }
+
+ break;
}
- if (nextFiber !== null) {
- // Set the return pointer of the child to the work-in-progress fiber.
- nextFiber.return = fiber;
- } else {
- // No child. Traverse to next sibling.
- nextFiber = fiber;
+ case Profiler: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); // TODO: Should this fire inside an offscreen tree? Or should it wait to
+ // fire when the tree becomes visible again.
- while (nextFiber !== null) {
- if (nextFiber === workInProgress) {
- // We're back to the root of this subtree. Exit.
- nextFiber = null;
- break;
- }
+ if (flags & Update) {
+ commitProfilerUpdate(finishedWork, current);
+ }
- var sibling = nextFiber.sibling;
+ break;
+ }
- if (sibling !== null) {
- // Set the return pointer of the sibling to the work-in-progress fiber.
- sibling.return = nextFiber.return;
- nextFiber = sibling;
- break;
- } // No more siblings. Traverse up.
+ case SuspenseComponent: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- nextFiber = nextFiber.return;
+ if (flags & Update) {
+ commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
}
+
+ break;
}
- fiber = nextFiber;
- }
-}
+ case OffscreenComponent: {
+ var isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
-function lazilyPropagateParentContextChanges(
- current,
- workInProgress,
- renderLanes
-) {
- var forcePropagateEntireTree = false;
- propagateParentContextChanges(
- current,
- workInProgress,
- renderLanes,
- forcePropagateEntireTree
- );
-} // Used for propagating a deferred tree (Suspense, Offscreen). We must propagate
-// to the entire subtree, because we won't revisit it until after the current
-// render has completed, at which point we'll have lost track of which providers
-// have changed.
+ if (isModernRoot) {
+ var isHidden = finishedWork.memoizedState !== null;
+ var newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
-function propagateParentContextChangesToDeferredTree(
- current,
- workInProgress,
- renderLanes
-) {
- var forcePropagateEntireTree = true;
- propagateParentContextChanges(
- current,
- workInProgress,
- renderLanes,
- forcePropagateEntireTree
- );
-}
+ if (newOffscreenSubtreeIsHidden);
+ else {
+ // The Offscreen tree is visible.
+ var wasHidden = current !== null && current.memoizedState !== null;
+ var newOffscreenSubtreeWasHidden =
+ wasHidden || offscreenSubtreeWasHidden;
+ var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
+ var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
+ offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
+ offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
-function propagateParentContextChanges(
- current,
- workInProgress,
- renderLanes,
- forcePropagateEntireTree
-) {
- if (!enableLazyContextPropagation) {
- return;
- } // Collect all the parent providers that changed. Since this is usually small
- // number, we use an Array instead of Set.
+ if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
+ // This is the root of a reappearing boundary. As we continue
+ // traversing the layout effects, we must also re-mount layout
+ // effects that were unmounted when the Offscreen subtree was
+ // hidden. So this is a superset of the normal commitLayoutEffects.
+ var includeWorkInProgressEffects =
+ (finishedWork.subtreeFlags & LayoutMask) !== NoFlags$1;
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ );
+ } else {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+ }
- var contexts = null;
- var parent = workInProgress;
- var isInsidePropagationBailout = false;
+ offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
+ offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
+ }
+ } else {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+ }
+
+ if (flags & Ref) {
+ var props = finishedWork.memoizedProps;
- while (parent !== null) {
- if (!isInsidePropagationBailout) {
- if ((parent.flags & NeedsPropagation) !== NoFlags$1) {
- isInsidePropagationBailout = true;
- } else if ((parent.flags & DidPropagateContext) !== NoFlags$1) {
- break;
+ if (props.mode === "manual") {
+ safelyAttachRef(finishedWork, finishedWork.return);
+ } else {
+ safelyDetachRef(finishedWork, finishedWork.return);
+ }
}
+
+ break;
}
- if (parent.tag === ContextProvider) {
- var currentParent = parent.alternate;
+ default: {
+ recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+ break;
+ }
+ }
+}
- if (currentParent === null) {
- throw new Error("Should have a current fiber. This is a bug in React.");
- }
+function abortRootTransitions(
+ root,
+ abort,
+ deletedTransitions,
+ deletedOffscreenInstance,
+ isInDeletedTree
+) {
+ if (enableTransitionTracing) {
+ var rootTransitions = root.incompleteTransitions;
+ deletedTransitions.forEach(function (transition) {
+ if (rootTransitions.has(transition)) {
+ var transitionInstance = rootTransitions.get(transition);
- var oldProps = currentParent.memoizedProps;
+ if (transitionInstance.aborts === null) {
+ transitionInstance.aborts = [];
+ }
- if (oldProps !== null) {
- var providerType = parent.type;
- var context = providerType._context;
- var newProps = parent.pendingProps;
- var newValue = newProps.value;
- var oldValue = oldProps.value;
+ transitionInstance.aborts.push(abort);
- if (!objectIs(newValue, oldValue)) {
- if (contexts !== null) {
- contexts.push(context);
- } else {
- contexts = [context];
+ if (deletedOffscreenInstance !== null) {
+ if (
+ transitionInstance.pendingBoundaries !== null &&
+ transitionInstance.pendingBoundaries.has(deletedOffscreenInstance)
+ ) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ transitionInstance.pendingBoundaries.delete(
+ deletedOffscreenInstance
+ );
}
}
}
- }
-
- parent = parent.return;
+ });
}
-
- if (contexts !== null) {
- // If there were any changed providers, search through the children and
- // propagate their changes.
- propagateContextChanges(
- workInProgress,
- contexts,
- renderLanes,
- forcePropagateEntireTree
- );
- } // This is an optimization so that we only propagate once per subtree. If a
- // deeply nested child bails out, and it calls this propagation function, it
- // uses this flag to know that the remaining ancestor providers have already
- // been propagated.
- //
- // NOTE: This optimization is only necessary because we sometimes enter the
- // begin phase of nodes that don't have any work scheduled on them —
- // specifically, the siblings of a node that _does_ have scheduled work. The
- // siblings will bail out and call this function again, even though we already
- // propagated content changes to it and its subtree. So we use this flag to
- // mark that the parent providers already propagated.
- //
- // Unfortunately, though, we need to ignore this flag when we're inside a
- // tree whose context propagation was deferred — that's what the
- // `NeedsPropagation` flag is for.
- //
- // If we could instead bail out before entering the siblings' begin phase,
- // then we could remove both `DidPropagateContext` and `NeedsPropagation`.
- // Consider this as part of the next refactor to the fiber tree structure.
-
- workInProgress.flags |= DidPropagateContext;
}
-function checkIfContextChanged(currentDependencies) {
- if (!enableLazyContextPropagation) {
- return false;
- } // Iterate over the current dependencies to see if something changed. This
- // only gets called if props and state has already bailed out, so it's a
- // relatively uncommon path, except at the root of a changed subtree.
- // Alternatively, we could move these comparisons into `readContext`, but
- // that's a much hotter path, so I think this is an appropriate trade off.
-
- var dependency = currentDependencies.firstContext;
+function abortTracingMarkerTransitions(
+ abortedFiber,
+ abort,
+ deletedTransitions,
+ deletedOffscreenInstance,
+ isInDeletedTree
+) {
+ if (enableTransitionTracing) {
+ var markerInstance = abortedFiber.stateNode;
+ var markerTransitions = markerInstance.transitions;
+ var pendingBoundaries = markerInstance.pendingBoundaries;
- while (dependency !== null) {
- var context = dependency.context;
- var newValue = context._currentValue;
- var oldValue = dependency.memoizedValue;
+ if (markerTransitions !== null) {
+ // TODO: Refactor this code. Is there a way to move this code to
+ // the deletions phase instead of calculating it here while making sure
+ // complete is called appropriately?
+ deletedTransitions.forEach(function (transition) {
+ // If one of the transitions on the tracing marker is a transition
+ // that was in an aborted subtree, we will abort that tracing marker
+ if (
+ abortedFiber !== null &&
+ markerTransitions.has(transition) &&
+ (markerInstance.aborts === null ||
+ !markerInstance.aborts.includes(abort))
+ ) {
+ if (markerInstance.transitions !== null) {
+ if (markerInstance.aborts === null) {
+ markerInstance.aborts = [abort];
+ addMarkerIncompleteCallbackToPendingTransition(
+ abortedFiber.memoizedProps.name,
+ markerInstance.transitions,
+ markerInstance.aborts
+ );
+ } else {
+ markerInstance.aborts.push(abort);
+ } // We only want to call onTransitionProgress when the marker hasn't been
+ // deleted
- if (!objectIs(newValue, oldValue)) {
- return true;
+ if (
+ deletedOffscreenInstance !== null &&
+ !isInDeletedTree &&
+ pendingBoundaries !== null &&
+ pendingBoundaries.has(deletedOffscreenInstance)
+ ) {
+ pendingBoundaries.delete(deletedOffscreenInstance);
+ addMarkerProgressCallbackToPendingTransition(
+ abortedFiber.memoizedProps.name,
+ deletedTransitions,
+ pendingBoundaries
+ );
+ }
+ }
+ }
+ });
}
-
- dependency = dependency.next;
}
-
- return false;
}
-function prepareToReadContext(workInProgress, renderLanes) {
- currentlyRenderingFiber = workInProgress;
- lastContextDependency = null;
- lastFullyObservedContext = null;
- var dependencies = workInProgress.dependencies;
- if (dependencies !== null) {
- if (enableLazyContextPropagation) {
- // Reset the work-in-progress list
- dependencies.firstContext = null;
- } else {
- var firstContext = dependencies.firstContext;
+function abortParentMarkerTransitionsForDeletedFiber(
+ abortedFiber,
+ abort,
+ deletedTransitions,
+ deletedOffscreenInstance,
+ isInDeletedTree
+) {
+ if (enableTransitionTracing) {
+ // Find all pending markers that are waiting on child suspense boundaries in the
+ // aborted subtree and cancels them
+ var fiber = abortedFiber;
- if (firstContext !== null) {
- if (includesSomeLane(dependencies.lanes, renderLanes)) {
- // Context list has a pending update. Mark that this fiber performed work.
- markWorkInProgressReceivedUpdate();
- } // Reset the work-in-progress list
+ while (fiber !== null) {
+ switch (fiber.tag) {
+ case TracingMarkerComponent:
+ abortTracingMarkerTransitions(
+ fiber,
+ abort,
+ deletedTransitions,
+ deletedOffscreenInstance,
+ isInDeletedTree
+ );
+ break;
- dependencies.firstContext = null;
+ case HostRoot:
+ var root = fiber.stateNode;
+ abortRootTransitions(
+ root,
+ abort,
+ deletedTransitions,
+ deletedOffscreenInstance
+ );
+ break;
}
- }
- }
-}
-function readContext(context) {
- {
- // This warning would fire if you read context inside a Hook like useMemo.
- // Unlike the class check below, it's not enforced in production for perf.
- if (isDisallowedContextReadInDEV) {
- error(
- "Context can only be read while React is rendering. " +
- "In classes, you can read it in the render method or getDerivedStateFromProps. " +
- "In function components, you can read it directly in the function body, but not " +
- "inside Hooks like useReducer() or useMemo()."
- );
- }
- }
- return readContextForConsumer(currentlyRenderingFiber, context);
-}
-function readContextDuringReconcilation(consumer, context, renderLanes) {
- if (currentlyRenderingFiber === null) {
- prepareToReadContext(consumer, renderLanes);
+ fiber = fiber.return;
+ }
}
-
- return readContextForConsumer(consumer, context);
}
-function readContextForConsumer(consumer, context) {
- var value = context._currentValue;
+function commitTransitionProgress(offscreenFiber) {
+ if (enableTransitionTracing) {
+ // This function adds suspense boundaries to the root
+ // or tracing marker's pendingBoundaries map.
+ // When a suspense boundary goes from a resolved to a fallback
+ // state we add the boundary to the map, and when it goes from
+ // a fallback to a resolved state, we remove the boundary from
+ // the map.
+ // We use stateNode on the Offscreen component as a stable object
+ // that doesnt change from render to render. This way we can
+ // distinguish between different Offscreen instances (vs. the same
+ // Offscreen instance with different fibers)
+ var offscreenInstance = offscreenFiber.stateNode;
+ var prevState = null;
+ var previousFiber = offscreenFiber.alternate;
- if (lastFullyObservedContext === context);
- else {
- var contextItem = {
- context: context,
- memoizedValue: value,
- next: null
- };
+ if (previousFiber !== null && previousFiber.memoizedState !== null) {
+ prevState = previousFiber.memoizedState;
+ }
- if (lastContextDependency === null) {
- if (consumer === null) {
- throw new Error(
- "Context can only be read while React is rendering. " +
- "In classes, you can read it in the render method or getDerivedStateFromProps. " +
- "In function components, you can read it directly in the function body, but not " +
- "inside Hooks like useReducer() or useMemo()."
- );
- } // This is the first dependency for this component. Create a new list.
+ var nextState = offscreenFiber.memoizedState;
+ var wasHidden = prevState !== null;
+ var isHidden = nextState !== null;
+ var pendingMarkers = offscreenInstance._pendingMarkers; // If there is a name on the suspense boundary, store that in
+ // the pending boundaries.
- lastContextDependency = contextItem;
- consumer.dependencies = {
- lanes: NoLanes,
- firstContext: contextItem
- };
+ var name = null;
+ var parent = offscreenFiber.return;
- if (enableLazyContextPropagation) {
- consumer.flags |= NeedsPropagation;
- }
- } else {
- // Append a new context item.
- lastContextDependency = lastContextDependency.next = contextItem;
+ if (
+ parent !== null &&
+ parent.tag === SuspenseComponent &&
+ parent.memoizedProps.unstable_name
+ ) {
+ name = parent.memoizedProps.unstable_name;
}
- }
- return value;
-}
+ if (!wasHidden && isHidden) {
+ // The suspense boundaries was just hidden. Add the boundary
+ // to the pending boundary set if it's there
+ if (pendingMarkers !== null) {
+ pendingMarkers.forEach(function (markerInstance) {
+ var pendingBoundaries = markerInstance.pendingBoundaries;
+ var transitions = markerInstance.transitions;
+ var markerName = markerInstance.name;
-// replace it with a lightweight shim that only has the features we use.
+ if (
+ pendingBoundaries !== null &&
+ !pendingBoundaries.has(offscreenInstance)
+ ) {
+ pendingBoundaries.set(offscreenInstance, {
+ name: name
+ });
-var AbortControllerLocal =
- typeof AbortController !== "undefined"
- ? AbortController // $FlowFixMe[missing-this-annot]
- : function AbortControllerShim() {
- var listeners = [];
- var signal = (this.signal = {
- aborted: false,
- addEventListener: function (type, listener) {
- listeners.push(listener);
+ if (transitions !== null) {
+ if (
+ markerInstance.tag === TransitionTracingMarker &&
+ markerName !== null
+ ) {
+ addMarkerProgressCallbackToPendingTransition(
+ markerName,
+ transitions,
+ pendingBoundaries
+ );
+ } else if (markerInstance.tag === TransitionRoot) {
+ transitions.forEach(function (transition) {
+ addTransitionProgressCallbackToPendingTransition(
+ transition,
+ pendingBoundaries
+ );
+ });
+ }
+ }
}
});
+ }
+ } else if (wasHidden && !isHidden) {
+ // The suspense boundary went from hidden to visible. Remove
+ // the boundary from the pending suspense boundaries set
+ // if it's there
+ if (pendingMarkers !== null) {
+ pendingMarkers.forEach(function (markerInstance) {
+ var pendingBoundaries = markerInstance.pendingBoundaries;
+ var transitions = markerInstance.transitions;
+ var markerName = markerInstance.name;
- this.abort = function () {
- signal.aborted = true;
- listeners.forEach(function (listener) {
- return listener();
- });
- };
- }; // Intentionally not named imports because Rollup would
-// use dynamic dispatch for CommonJS interop named imports.
+ if (
+ pendingBoundaries !== null &&
+ pendingBoundaries.has(offscreenInstance)
+ ) {
+ pendingBoundaries.delete(offscreenInstance);
-var scheduleCallback$1 = Scheduler.unstable_scheduleCallback,
- NormalPriority = Scheduler.unstable_NormalPriority;
-var CacheContext = {
- $$typeof: REACT_CONTEXT_TYPE,
- // We don't use Consumer/Provider for Cache components. So we'll cheat.
- Consumer: null,
- Provider: null,
- // We'll initialize these at the root.
- _currentValue: null,
- _currentValue2: null,
- _threadCount: 0,
- _defaultValue: null,
- _globalName: null
-};
+ if (transitions !== null) {
+ if (
+ markerInstance.tag === TransitionTracingMarker &&
+ markerName !== null
+ ) {
+ addMarkerProgressCallbackToPendingTransition(
+ markerName,
+ transitions,
+ pendingBoundaries
+ ); // If there are no more unresolved suspense boundaries, the interaction
+ // is considered finished
-{
- CacheContext._currentRenderer = null;
- CacheContext._currentRenderer2 = null;
-} // Creates a new empty Cache instance with a ref-count of 0. The caller is responsible
-// for retaining the cache once it is in use (retainCache), and releasing the cache
-// once it is no longer needed (releaseCache).
+ if (pendingBoundaries.size === 0) {
+ if (markerInstance.aborts === null) {
+ addMarkerCompleteCallbackToPendingTransition(
+ markerName,
+ transitions
+ );
+ }
-function createCache() {
- var cache = {
- controller: new AbortControllerLocal(),
- data: new Map(),
- refCount: 0
- };
- return cache;
-}
-function retainCache(cache) {
- {
- if (cache.controller.signal.aborted) {
- warn(
- "A cache instance was retained after it was already freed. " +
- "This likely indicates a bug in React."
- );
+ markerInstance.transitions = null;
+ markerInstance.pendingBoundaries = null;
+ markerInstance.aborts = null;
+ }
+ } else if (markerInstance.tag === TransitionRoot) {
+ transitions.forEach(function (transition) {
+ addTransitionProgressCallbackToPendingTransition(
+ transition,
+ pendingBoundaries
+ );
+ });
+ }
+ }
+ }
+ });
+ }
}
}
+}
- cache.refCount++;
-} // Cleanup a cache instance, potentially freeing it if there are no more references
-
-function releaseCache(cache) {
- cache.refCount--;
+function hideOrUnhideAllChildren(finishedWork, isHidden) {
+ // Only hide or unhide the top-most host nodes.
+ var hostSubtreeRoot = null;
{
- if (cache.refCount < 0) {
- warn(
- "A cache instance was released after it was already freed. " +
- "This likely indicates a bug in React."
- );
- }
- }
+ // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+ var node = finishedWork;
- if (cache.refCount === 0) {
- scheduleCallback$1(NormalPriority, function () {
- cache.controller.abort();
- });
- }
-}
-function pushCacheProvider(workInProgress, cache) {
- pushProvider(workInProgress, CacheContext, cache);
-}
-function popCacheProvider(workInProgress, cache) {
- popProvider(CacheContext, workInProgress);
-}
+ while (true) {
+ if (
+ node.tag === HostComponent ||
+ node.tag === HostHoistable ||
+ node.tag === HostSingleton
+ ) {
+ if (hostSubtreeRoot === null) {
+ hostSubtreeRoot = node;
-var ReactCurrentBatchConfig$1 = ReactSharedInternals.ReactCurrentBatchConfig;
-var NoTransition = null;
-function requestCurrentTransition() {
- return ReactCurrentBatchConfig$1.transition;
-} // When retrying a Suspense/Offscreen boundary, we restore the cache that was
-// used during the previous render by placing it here, on the stack.
+ try {
+ var instance = node.stateNode;
+
+ if (isHidden) {
+ hideInstance(instance);
+ } else {
+ unhideInstance(node.stateNode, node.memoizedProps);
+ }
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
+ } else if (node.tag === HostText) {
+ if (hostSubtreeRoot === null) {
+ try {
+ var _instance = node.stateNode;
+
+ if (isHidden) {
+ hideTextInstance(_instance);
+ } else {
+ unhideTextInstance(_instance, node.memoizedProps);
+ }
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
+ } else if (
+ (node.tag === OffscreenComponent ||
+ node.tag === LegacyHiddenComponent) &&
+ node.memoizedState !== null &&
+ node !== finishedWork
+ );
+ else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
+ }
-var resumedCache = createCursor(null); // During the render/synchronous commit phase, we don't actually process the
-// transitions. Therefore, we want to lazily combine transitions. Instead of
-// comparing the arrays of transitions when we combine them and storing them
-// and filtering out the duplicates, we will instead store the unprocessed transitions
-// in an array and actually filter them in the passive phase.
+ if (node === finishedWork) {
+ return;
+ }
-var transitionStack = createCursor(null);
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return;
+ }
-function peekCacheFromPool() {
- // If we're rendering inside a Suspense boundary that is currently hidden,
- // we should use the same cache that we used during the previous render, if
- // one exists.
+ if (hostSubtreeRoot === node) {
+ hostSubtreeRoot = null;
+ }
- var cacheResumedFromPreviousRender = resumedCache.current;
+ node = node.return;
+ }
- if (cacheResumedFromPreviousRender !== null) {
- return cacheResumedFromPreviousRender;
- } // Otherwise, check the root's cache pool.
+ if (hostSubtreeRoot === node) {
+ hostSubtreeRoot = null;
+ }
- var root = getWorkInProgressRoot();
- var cacheFromRootCachePool = root.pooledCache;
- return cacheFromRootCachePool;
+ node.sibling.return = node.return;
+ node = node.sibling;
+ }
+ }
}
-function requestCacheFromPool(renderLanes) {
- // Similar to previous function, except if there's not already a cache in the
- // pool, we allocate a new one.
- var cacheFromPool = peekCacheFromPool();
+function commitAttachRef(finishedWork) {
+ var ref = finishedWork.ref;
- if (cacheFromPool !== null) {
- return cacheFromPool;
- } // Create a fresh cache and add it to the root cache pool. A cache can have
- // multiple owners:
- // - A cache pool that lives on the FiberRoot. This is where all fresh caches
- // are originally created (TODO: except during refreshes, until we implement
- // this correctly). The root takes ownership immediately when the cache is
- // created. Conceptually, root.pooledCache is an Option> (owned),
- // and the return value of this function is a &Arc (borrowed).
- // - One of several fiber types: host root, cache boundary, suspense
- // component. These retain and release in the commit phase.
+ if (ref !== null) {
+ var instance = finishedWork.stateNode;
+ var instanceToUse;
- var root = getWorkInProgressRoot();
- var freshCache = createCache();
- root.pooledCache = freshCache;
- retainCache(freshCache);
+ switch (finishedWork.tag) {
+ case HostHoistable:
+ case HostSingleton:
+ case HostComponent:
+ instanceToUse = getPublicInstance(instance);
+ break;
- if (freshCache !== null) {
- root.pooledCacheLanes |= renderLanes;
- }
+ default:
+ instanceToUse = instance;
+ } // Moved outside to ensure DCE works with this flag
- return freshCache;
-}
-function pushRootTransition(workInProgress, root, renderLanes) {
- if (enableTransitionTracing) {
- var rootTransitions = getWorkInProgressTransitions();
- push(transitionStack, rootTransitions, workInProgress);
- }
-}
-function popRootTransition(workInProgress, root, renderLanes) {
- if (enableTransitionTracing) {
- pop(transitionStack, workInProgress);
- }
-}
-function pushTransition(
- offscreenWorkInProgress,
- prevCachePool,
- newTransitions
-) {
- {
- if (prevCachePool === null) {
- push(resumedCache, resumedCache.current, offscreenWorkInProgress);
- } else {
- push(resumedCache, prevCachePool.pool, offscreenWorkInProgress);
+ if (finishedWork.tag === ScopeComponent) {
+ instanceToUse = instance;
}
- }
- if (enableTransitionTracing) {
- if (transitionStack.current === null) {
- push(transitionStack, newTransitions, offscreenWorkInProgress);
- } else if (newTransitions === null) {
- push(transitionStack, transitionStack.current, offscreenWorkInProgress);
+ if (typeof ref === "function") {
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ finishedWork.refCleanup = ref(instanceToUse);
+ } finally {
+ recordLayoutEffectDuration(finishedWork);
+ }
+ } else {
+ finishedWork.refCleanup = ref(instanceToUse);
+ }
} else {
- push(
- transitionStack,
- transitionStack.current.concat(newTransitions),
- offscreenWorkInProgress
- );
- }
- }
-}
-function popTransition(workInProgress, current) {
- if (current !== null) {
- if (enableTransitionTracing) {
- pop(transitionStack, workInProgress);
- }
+ {
+ if (!ref.hasOwnProperty("current")) {
+ error(
+ "Unexpected ref object provided for %s. " +
+ "Use either a ref-setter function or React.createRef().",
+ getComponentNameFromFiber(finishedWork)
+ );
+ }
+ } // $FlowFixMe unable to narrow type to the non-function case
- {
- pop(resumedCache, workInProgress);
+ ref.current = instanceToUse;
}
}
}
-function getPendingTransitions() {
- if (!enableTransitionTracing) {
- return null;
+
+function detachFiberMutation(fiber) {
+ // Cut off the return pointer to disconnect it from the tree.
+ // This enables us to detect and warn against state updates on an unmounted component.
+ // It also prevents events from bubbling from within disconnected components.
+ //
+ // Ideally, we should also clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child.
+ // This child itself will be GC:ed when the parent updates the next time.
+ //
+ // Note that we can't clear child or sibling pointers yet.
+ // They're needed for passive effects and for findDOMNode.
+ // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
+ //
+ // Don't reset the alternate yet, either. We need that so we can detach the
+ // alternate's fields in the passive phase. Clearing the return pointer is
+ // sufficient for findDOMNode semantics.
+ var alternate = fiber.alternate;
+
+ if (alternate !== null) {
+ alternate.return = null;
}
- return transitionStack.current;
+ fiber.return = null;
}
-function getSuspendedCache() {
- // cache that would have been used to render fresh data during this render,
- // if there was any, so that we can resume rendering with the same cache when
- // we receive more data.
- var cacheFromPool = peekCacheFromPool();
+function detachFiberAfterEffects(fiber) {
+ var alternate = fiber.alternate;
- if (cacheFromPool === null) {
- return null;
+ if (alternate !== null) {
+ fiber.alternate = null;
+ detachFiberAfterEffects(alternate);
+ } // Clear cyclical Fiber fields. This level alone is designed to roughly
+ // approximate the planned Fiber refactor. In that world, `setState` will be
+ // bound to a special "instance" object instead of a Fiber. The Instance
+ // object will not have any of these fields. It will only be connected to
+ // the fiber tree via a single link at the root. So if this level alone is
+ // sufficient to fix memory issues, that bodes well for our plans.
+
+ fiber.child = null;
+ fiber.deletions = null;
+ fiber.sibling = null; // The `stateNode` is cyclical because on host nodes it points to the host
+ // tree, which has its own pointers to children, parents, and siblings.
+ // The other host nodes also point back to fibers, so we should detach that
+ // one, too.
+
+ if (fiber.tag === HostComponent) {
+ var hostInstance = fiber.stateNode;
+
+ if (hostInstance !== null) {
+ detachDeletedInstance(hostInstance);
+ }
}
- return {
- // We must also save the parent, so that when we resume we can detect
- // a refresh.
- parent: CacheContext._currentValue,
- pool: cacheFromPool
- };
+ fiber.stateNode = null;
+
+ {
+ fiber._debugOwner = null;
+ } // Theoretically, nothing in here should be necessary, because we already
+ // disconnected the fiber from the tree. So even if something leaks this
+ // particular fiber, it won't leak anything else.
+
+ fiber.return = null;
+ fiber.dependencies = null;
+ fiber.memoizedProps = null;
+ fiber.memoizedState = null;
+ fiber.pendingProps = null;
+ fiber.stateNode = null; // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
+
+ fiber.updateQueue = null;
}
-function getOffscreenDeferredCache() {
- var cacheFromPool = peekCacheFromPool();
- if (cacheFromPool === null) {
- return null;
+function getHostParentFiber(fiber) {
+ var parent = fiber.return;
+
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
+ }
+
+ parent = parent.return;
}
- return {
- // We must also store the parent, so that when we resume we can detect
- // a refresh.
- parent: CacheContext._currentValue,
- pool: cacheFromPool
- };
+ throw new Error(
+ "Expected to find a host parent. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
}
-function getSuspenseFallbackChild(fiber) {
- return fiber.child.sibling.child;
+function isHostParent(fiber) {
+ return (
+ fiber.tag === HostComponent ||
+ fiber.tag === HostRoot ||
+ fiber.tag === HostHoistable ||
+ fiber.tag === HostSingleton ||
+ fiber.tag === HostPortal
+ );
}
-var emptyObject = {};
+function getHostSibling(fiber) {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ // TODO: Find a more efficient way to do this.
+ var node = fiber;
-function collectScopedNodes(node, fn, scopedNodes) {
- {
- if (node.tag === HostComponent) {
- var type = node.type,
- memoizedProps = node.memoizedProps,
- stateNode = node.stateNode;
- var instance = getPublicInstance(stateNode);
+ 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;
+ } // $FlowFixMe[incompatible-type] found when upgrading Flow
- if (
- instance !== null &&
- fn(type, memoizedProps || emptyObject, instance) === true
- ) {
- scopedNodes.push(instance);
- }
+ node = node.return;
}
- var child = node.child;
+ node.sibling.return = node.return;
+ node = node.sibling;
- if (isFiberSuspenseAndTimedOut(node)) {
- child = getSuspenseFallbackChild(node);
- }
+ while (
+ node.tag !== HostComponent &&
+ node.tag !== HostText &&
+ node.tag !== HostSingleton &&
+ node.tag !== DehydratedFragment
+ ) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.flags & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ } // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
- if (child !== null) {
- collectScopedNodesFromChildren(child, fn, scopedNodes);
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child.return = node;
+ node = node.child;
+ }
+ } // Check if this host node is stable or about to be placed.
+
+ if (!(node.flags & Placement)) {
+ // Found it!
+ return node.stateNode;
}
}
}
-function collectFirstScopedNode(node, fn) {
+function commitPlacement(finishedWork) {
{
- if (node.tag === HostComponent) {
- var type = node.type,
- memoizedProps = node.memoizedProps,
- stateNode = node.stateNode;
- var instance = getPublicInstance(stateNode);
+ if (finishedWork.tag === HostSingleton) {
+ // Singletons are already in the Host and don't need to be placed
+ // Since they operate somewhat like Portals though their children will
+ // have Placement and will get placed inside them
+ return;
+ }
+ } // Recursively insert all host nodes into the parent.
- if (instance !== null && fn(type, memoizedProps, instance) === true) {
- return instance;
+ var parentFiber = getHostParentFiber(finishedWork);
+
+ switch (parentFiber.tag) {
+ case HostSingleton: {
+ {
+ var parent = parentFiber.stateNode;
+ var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+
+ insertOrAppendPlacementNode(finishedWork, before, parent);
+ break;
}
}
+ // eslint-disable-next-line no-fallthrough
- var child = node.child;
+ case HostComponent: {
+ var _parent = parentFiber.stateNode;
- if (isFiberSuspenseAndTimedOut(node)) {
- child = getSuspenseFallbackChild(node);
+ if (parentFiber.flags & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ resetTextContent(_parent); // Clear ContentReset from the effect tag
+
+ parentFiber.flags &= ~ContentReset;
+ }
+
+ var _before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
+ // children to find all the terminal nodes.
+
+ insertOrAppendPlacementNode(finishedWork, _before, _parent);
+ break;
}
- if (child !== null) {
- return collectFirstScopedNodeFromChildren(child, fn);
+ case HostRoot:
+ case HostPortal: {
+ var _parent2 = parentFiber.stateNode.containerInfo;
+
+ var _before2 = getHostSibling(finishedWork);
+
+ insertOrAppendPlacementNodeIntoContainer(
+ finishedWork,
+ _before2,
+ _parent2
+ );
+ break;
}
- }
+ // eslint-disable-next-line-no-fallthrough
- return null;
+ default:
+ throw new Error(
+ "Invalid host parent fiber. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
+ }
}
-function collectScopedNodesFromChildren(startingChild, fn, scopedNodes) {
- var child = startingChild;
+function insertOrAppendPlacementNodeIntoContainer(node, before, parent) {
+ var tag = node.tag;
+ var isHost = tag === HostComponent || tag === HostText;
- while (child !== null) {
- collectScopedNodes(child, fn, scopedNodes);
- child = child.sibling;
+ if (isHost) {
+ var stateNode = node.stateNode;
+
+ if (before) {
+ insertInContainerBefore(parent, stateNode, before);
+ } else {
+ appendChildToContainer(parent, stateNode);
+ }
+ } else if (tag === HostPortal || tag === HostSingleton);
+ else {
+ var child = node.child;
+
+ if (child !== null) {
+ insertOrAppendPlacementNodeIntoContainer(child, before, parent);
+ var sibling = child.sibling;
+
+ while (sibling !== null) {
+ insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
+ sibling = sibling.sibling;
+ }
+ }
}
}
-function collectFirstScopedNodeFromChildren(startingChild, fn) {
- var child = startingChild;
+function insertOrAppendPlacementNode(node, before, parent) {
+ var tag = node.tag;
+ var isHost = tag === HostComponent || tag === HostText;
- while (child !== null) {
- var scopedNode = collectFirstScopedNode(child, fn);
+ if (isHost) {
+ var stateNode = node.stateNode;
- if (scopedNode !== null) {
- return scopedNode;
+ if (before) {
+ insertBefore(parent, stateNode, before);
+ } else {
+ appendChild(parent, stateNode);
}
+ } else if (tag === HostPortal || tag === HostSingleton);
+ else {
+ var child = node.child;
- child = child.sibling;
+ if (child !== null) {
+ insertOrAppendPlacementNode(child, before, parent);
+ var sibling = child.sibling;
+
+ while (sibling !== null) {
+ insertOrAppendPlacementNode(sibling, before, parent);
+ sibling = sibling.sibling;
+ }
+ }
}
+} // These are tracked on the stack as we recursively traverse a
+// deleted subtree.
+// TODO: Update these during the whole mutation phase, not just during
+// a deletion.
- return null;
-}
+var hostParent = null;
+var hostParentIsContainer = false;
-function collectNearestContextValues(node, context, childContextValues) {
- if (node.tag === ContextProvider && node.type._context === context) {
- var contextValue = node.memoizedProps.value;
- childContextValues.push(contextValue);
- } else {
- var child = node.child;
+function commitDeletionEffects(root, returnFiber, deletedFiber) {
+ {
+ // We only have the top Fiber that was deleted but we need to recurse down its
+ // children to find all the terminal nodes.
+ // Recursively delete all host nodes from the parent, detach refs, clean
+ // up mounted layout effects, and call componentWillUnmount.
+ // We only need to remove the topmost host child in each branch. But then we
+ // still need to keep traversing to unmount effects, refs, and cWU. TODO: We
+ // could split this into two separate traversals functions, where the second
+ // one doesn't include any removeChild logic. This is maybe the same
+ // function as "disappearLayoutEffects" (or whatever that turns into after
+ // the layout phase is refactored to use recursion).
+ // Before starting, find the nearest host parent on the stack so we know
+ // which instance/container to remove the children from.
+ // TODO: Instead of searching up the fiber return path on every deletion, we
+ // can track the nearest host component on the JS stack as we traverse the
+ // tree during the commit phase. This would make insertions faster, too.
+ var parent = returnFiber;
- if (isFiberSuspenseAndTimedOut(node)) {
- child = getSuspenseFallbackChild(node);
+ findParent: while (parent !== null) {
+ switch (parent.tag) {
+ case HostSingleton:
+ case HostComponent: {
+ hostParent = parent.stateNode;
+ hostParentIsContainer = false;
+ break findParent;
+ }
+
+ case HostRoot: {
+ hostParent = parent.stateNode.containerInfo;
+ hostParentIsContainer = true;
+ break findParent;
+ }
+
+ case HostPortal: {
+ hostParent = parent.stateNode.containerInfo;
+ hostParentIsContainer = true;
+ break findParent;
+ }
+ }
+
+ parent = parent.return;
}
- if (child !== null) {
- collectNearestChildContextValues(child, context, childContextValues);
+ if (hostParent === null) {
+ throw new Error(
+ "Expected to find a host parent. This error is likely caused by " +
+ "a bug in React. Please file an issue."
+ );
}
+
+ commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
+ hostParent = null;
+ hostParentIsContainer = false;
}
+
+ detachFiberMutation(deletedFiber);
}
-function collectNearestChildContextValues(
- startingChild,
- context,
- childContextValues
+function recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ parent
) {
- var child = startingChild;
+ // TODO: Use a static flag to skip trees that don't have unmount effects
+ var child = parent.child;
while (child !== null) {
- collectNearestContextValues(child, context, childContextValues);
+ commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
child = child.sibling;
}
}
-function DO_NOT_USE_queryAllNodes(fn) {
- var currentFiber = getInstanceFromScope(this);
+function commitDeletionEffectsOnFiber(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+) {
+ onCommitUnmount(deletedFiber); // The cases in this outer switch modify the stack before they traverse
+ // into their subtree. There are simpler cases in the inner switch
+ // that don't modify the stack.
- if (currentFiber === null) {
- return null;
- }
+ switch (deletedFiber.tag) {
+ case HostHoistable: {
+ {
+ if (!offscreenSubtreeWasHidden) {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
+ }
- var child = currentFiber.child;
- var scopedNodes = [];
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
- if (child !== null) {
- collectScopedNodesFromChildren(child, fn, scopedNodes);
- }
+ if (deletedFiber.memoizedState) {
+ releaseResource(deletedFiber.memoizedState);
+ } else if (deletedFiber.stateNode) {
+ unmountHoistable(deletedFiber.stateNode);
+ }
- return scopedNodes.length === 0 ? null : scopedNodes;
-}
+ return;
+ }
+ }
+ // eslint-disable-next-line no-fallthrough
-function DO_NOT_USE_queryFirstNode(fn) {
- var currentFiber = getInstanceFromScope(this);
+ case HostSingleton: {
+ {
+ if (!offscreenSubtreeWasHidden) {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
+ }
- if (currentFiber === null) {
- return null;
- }
+ var prevHostParent = hostParent;
+ var prevHostParentIsContainer = hostParentIsContainer;
+ hostParent = deletedFiber.stateNode;
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ ); // Normally this is called in passive unmount effect phase however with
+ // HostSingleton we warn if you acquire one that is already associated to
+ // a different fiber. To increase our chances of avoiding this, specifically
+ // if you keyed a HostSingleton so there will be a delete followed by a Placement
+ // we treat detach eagerly here
- var child = currentFiber.child;
+ releaseSingletonInstance(deletedFiber.stateNode);
+ hostParent = prevHostParent;
+ hostParentIsContainer = prevHostParentIsContainer;
+ return;
+ }
+ }
+ // eslint-disable-next-line no-fallthrough
- if (child !== null) {
- return collectFirstScopedNodeFromChildren(child, fn);
- }
+ case HostComponent: {
+ if (!offscreenSubtreeWasHidden) {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
+ } // Intentional fallthrough to next branch
+ }
+ // eslint-disable-next-line-no-fallthrough
- return null;
-}
+ case HostText: {
+ // We only need to remove the nearest host child. Set the host parent
+ // to `null` on the stack to indicate that nested children don't
+ // need to be removed.
+ {
+ var _prevHostParent = hostParent;
+ var _prevHostParentIsContainer = hostParentIsContainer;
+ hostParent = null;
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ hostParent = _prevHostParent;
+ hostParentIsContainer = _prevHostParentIsContainer;
-function containsNode(node) {
- var fiber = getInstanceFromNode$1(node);
+ if (hostParent !== null) {
+ // Now that all the child effects have unmounted, we can remove the
+ // node from the tree.
+ if (hostParentIsContainer) {
+ removeChildFromContainer(hostParent, deletedFiber.stateNode);
+ } else {
+ removeChild(hostParent, deletedFiber.stateNode);
+ }
+ }
+ }
- while (fiber !== null) {
- if (fiber.tag === ScopeComponent && fiber.stateNode === this) {
- return true;
+ return;
}
- fiber = fiber.return;
- }
-
- return false;
-}
-
-function getChildContextValues(context) {
- var currentFiber = getInstanceFromScope(this);
+ case DehydratedFragment: {
+ {
+ var hydrationCallbacks = finishedRoot.hydrationCallbacks;
- if (currentFiber === null) {
- return [];
- }
+ if (hydrationCallbacks !== null) {
+ var onDeleted = hydrationCallbacks.onDeleted;
- var child = currentFiber.child;
- var childContextValues = [];
+ if (onDeleted) {
+ onDeleted(deletedFiber.stateNode);
+ }
+ }
+ } // Dehydrated fragments don't have any children
+ // Delete the dehydrated suspense boundary and all of its content.
- if (child !== null) {
- collectNearestChildContextValues(child, context, childContextValues);
- }
+ {
+ if (hostParent !== null) {
+ if (hostParentIsContainer) {
+ clearSuspenseBoundaryFromContainer(
+ hostParent,
+ deletedFiber.stateNode
+ );
+ } else {
+ clearSuspenseBoundary(hostParent, deletedFiber.stateNode);
+ }
+ }
+ }
- return childContextValues;
-}
+ return;
+ }
-function createScopeInstance() {
- return {
- DO_NOT_USE_queryAllNodes: DO_NOT_USE_queryAllNodes,
- DO_NOT_USE_queryFirstNode: DO_NOT_USE_queryFirstNode,
- containsNode: containsNode,
- getChildContextValues: getChildContextValues
- };
-}
+ case HostPortal: {
+ {
+ // When we go into a portal, it becomes the parent to remove from.
+ var _prevHostParent2 = hostParent;
+ var _prevHostParentIsContainer2 = hostParentIsContainer;
+ hostParent = deletedFiber.stateNode.containerInfo;
+ hostParentIsContainer = true;
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ hostParent = _prevHostParent2;
+ hostParentIsContainer = _prevHostParentIsContainer2;
+ }
-function markUpdate(workInProgress) {
- // Tag the fiber with an update effect. This turns a Placement into
- // a PlacementAndUpdate.
- workInProgress.flags |= Update;
-}
+ return;
+ }
-function markRef(workInProgress) {
- workInProgress.flags |= Ref | RefStatic;
-}
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent: {
+ if (!offscreenSubtreeWasHidden) {
+ var updateQueue = deletedFiber.updateQueue;
-function appendAllChildren(
- parent,
- workInProgress,
- needsVisibilityToggle,
- isHidden
-) {
- {
- // We only have the top Fiber that was created but we need recurse down its
- // children to find all the terminal nodes.
- var node = workInProgress.child;
+ if (updateQueue !== null) {
+ var lastEffect = updateQueue.lastEffect;
- while (node !== null) {
- if (node.tag === HostComponent || node.tag === HostText) {
- appendInitialChild(parent, node.stateNode);
- } else if (node.tag === HostPortal || node.tag === HostSingleton);
- else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
+ if (lastEffect !== null) {
+ var firstEffect = lastEffect.next;
+ var effect = firstEffect;
- if (node === workInProgress) {
- return;
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ do {
+ var _effect = effect,
+ destroy = _effect.destroy,
+ tag = _effect.tag;
- while (node.sibling === null) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (node.return === null || node.return === workInProgress) {
- return;
- }
+ if (destroy !== undefined) {
+ if ((tag & Insertion) !== NoFlags) {
+ safelyCallDestroy(
+ deletedFiber,
+ nearestMountedAncestor,
+ destroy
+ );
+ } else if ((tag & Layout) !== NoFlags) {
+ if (enableSchedulingProfiler) {
+ markComponentLayoutEffectUnmountStarted(deletedFiber);
+ }
- node = node.return;
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (shouldProfile(deletedFiber)) {
+ startLayoutEffectTimer();
+ safelyCallDestroy(
+ deletedFiber,
+ nearestMountedAncestor,
+ destroy
+ );
+ recordLayoutEffectDuration(deletedFiber);
+ } else {
+ safelyCallDestroy(
+ deletedFiber,
+ nearestMountedAncestor,
+ destroy
+ );
+ }
- node.sibling.return = node.return;
- node = node.sibling;
- }
- }
-} // An unfortunate fork of appendAllChildren because we have two different parent types.
+ if (enableSchedulingProfiler) {
+ markComponentLayoutEffectUnmountStopped();
+ }
+ }
+ }
-function updateHostComponent(current, workInProgress, type, newProps) {
- {
- // 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;
+ effect = effect.next;
+ } while (effect !== firstEffect);
+ }
+ }
+ }
- if (oldProps === newProps) {
- // In mutation mode, this is sufficient for a bailout because
- // we won't touch this node even if children changed.
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
return;
- } // If we get updated because one of our children updated, we don't
- // have newProps so we'll have to reuse them.
- // TODO: Split the update API as separate for the props vs. children.
- // Even better would be if children weren't special cased at all tho.
-
- var instance = workInProgress.stateNode;
- var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host
- // component is hitting the resume path. Figure out why. Possibly
- // related to `hidden`.
-
- var updatePayload = prepareUpdate(
- instance,
- type,
- oldProps,
- newProps,
- currentHostContext
- ); // TODO: Type this specific to this type of component.
+ }
- workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
- // is a new ref we mark this as an update. All the work is done in commitWork.
+ case ClassComponent: {
+ if (!offscreenSubtreeWasHidden) {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
+ var instance = deletedFiber.stateNode;
- if (updatePayload) {
- markUpdate(workInProgress);
- }
- }
-}
+ if (typeof instance.componentWillUnmount === "function") {
+ safelyCallComponentWillUnmount(
+ deletedFiber,
+ nearestMountedAncestor,
+ instance
+ );
+ }
+ }
-function updateHostText(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);
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ return;
}
- }
-}
-
-function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {
- if (getIsHydrating()) {
- // If we're hydrating, we should consume as many items as we can
- // so we don't leave any behind.
- return;
- }
- switch (renderState.tailMode) {
- case "hidden": {
- // Any insertions at the end of the tail list after this point
- // should be invisible. If there are already mounted boundaries
- // anything before them are not considered for collapsing.
- // Therefore we need to go through the whole tail to find if
- // there are any.
- var tailNode = renderState.tail;
- var lastTailNode = null;
+ case ScopeComponent: {
+ {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
+ }
- while (tailNode !== null) {
- if (tailNode.alternate !== null) {
- lastTailNode = tailNode;
- }
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ return;
+ }
- tailNode = tailNode.sibling;
- } // Next we're simply going to delete all insertions after the
- // last rendered item.
+ case OffscreenComponent: {
+ safelyDetachRef(deletedFiber, nearestMountedAncestor);
- if (lastTailNode === null) {
- // All remaining items in the tail are insertions.
- renderState.tail = null;
+ if (deletedFiber.mode & ConcurrentMode) {
+ // If this offscreen component is hidden, we already unmounted it. Before
+ // deleting the children, track that it's already unmounted so that we
+ // don't attempt to unmount the effects again.
+ // TODO: If the tree is hidden, in most cases we should be able to skip
+ // over the nested children entirely. An exception is we haven't yet found
+ // the topmost host node to delete, which we already track on the stack.
+ // But the other case is portals, which need to be detached no matter how
+ // deeply they are nested. We should use a subtree flag to track whether a
+ // subtree includes a nested portal.
+ var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
+ offscreenSubtreeWasHidden =
+ prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
} else {
- // Detach the insertion after the last node that was already
- // inserted.
- lastTailNode.sibling = null;
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
}
break;
}
- case "collapsed": {
- // Any insertions at the end of the tail list after this point
- // should be invisible. If there are already mounted boundaries
- // anything before them are not considered for collapsing.
- // Therefore we need to go through the whole tail to find if
- // there are any.
- var _tailNode = renderState.tail;
- var _lastTailNode = null;
+ default: {
+ recursivelyTraverseDeletionEffects(
+ finishedRoot,
+ nearestMountedAncestor,
+ deletedFiber
+ );
+ return;
+ }
+ }
+}
- while (_tailNode !== null) {
- if (_tailNode.alternate !== null) {
- _lastTailNode = _tailNode;
- }
+function commitSuspenseCallback(finishedWork) {
+ // TODO: Move this to passive phase
+ var newState = finishedWork.memoizedState;
- _tailNode = _tailNode.sibling;
- } // Next we're simply going to delete all insertions after the
- // last rendered item.
+ if (newState !== null) {
+ var suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
- if (_lastTailNode === null) {
- // All remaining items in the tail are insertions.
- if (!hasRenderedATailFallback && renderState.tail !== null) {
- // We suspended during the head. We want to show at least one
- // row at the tail. So we'll keep on and cut off the rest.
- renderState.tail.sibling = null;
- } else {
- renderState.tail = null;
- }
- } else {
- // Detach the insertion after the last node that was already
- // inserted.
- _lastTailNode.sibling = null;
- }
+ if (typeof suspenseCallback === "function") {
+ var wakeables = finishedWork.updateQueue;
- break;
+ if (wakeables !== null) {
+ suspenseCallback(new Set(wakeables));
+ }
+ } else {
+ if (suspenseCallback !== undefined) {
+ error("Unexpected type for suspenseCallback.");
+ }
}
}
}
-function bubbleProperties(completedWork) {
- var didBailout =
- completedWork.alternate !== null &&
- completedWork.alternate.child === completedWork.child;
- var newChildLanes = NoLanes;
- var subtreeFlags = NoFlags$1;
+function commitSuspenseHydrationCallbacks(finishedRoot, finishedWork) {
+ var newState = finishedWork.memoizedState;
- if (!didBailout) {
- // Bubble up the earliest expiration time.
- if ((completedWork.mode & ProfileMode) !== NoMode) {
- // In profiling mode, resetChildExpirationTime is also used to reset
- // profiler durations.
- var actualDuration = completedWork.actualDuration;
- var treeBaseDuration = completedWork.selfBaseDuration;
- var child = completedWork.child;
+ if (newState === null) {
+ var current = finishedWork.alternate;
- while (child !== null) {
- newChildLanes = mergeLanes(
- newChildLanes,
- mergeLanes(child.lanes, child.childLanes)
- );
- subtreeFlags |= child.subtreeFlags;
- subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will
- // only be updated if work is done on the fiber (i.e. it doesn't bailout).
- // When work is done, it should bubble to the parent's actualDuration. If
- // the fiber has not been cloned though, (meaning no work was done), then
- // this value will reflect the amount of time spent working on a previous
- // render. In that case it should not bubble. We determine whether it was
- // cloned by comparing the child pointer.
- // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+ if (current !== null) {
+ var prevState = current.memoizedState;
- actualDuration += child.actualDuration; // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+ if (prevState !== null) {
+ var suspenseInstance = prevState.dehydrated;
- treeBaseDuration += child.treeBaseDuration;
- child = child.sibling;
- }
+ if (suspenseInstance !== null) {
+ try {
+ commitHydratedSuspenseInstance(suspenseInstance);
- completedWork.actualDuration = actualDuration;
- completedWork.treeBaseDuration = treeBaseDuration;
- } else {
- var _child = completedWork.child;
+ if (enableSuspenseCallback) {
+ var hydrationCallbacks = finishedRoot.hydrationCallbacks;
- while (_child !== null) {
- newChildLanes = mergeLanes(
- newChildLanes,
- mergeLanes(_child.lanes, _child.childLanes)
- );
- subtreeFlags |= _child.subtreeFlags;
- subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code
- // smell because it assumes the commit phase is never concurrent with
- // the render phase. Will address during refactor to alternate model.
+ if (hydrationCallbacks !== null) {
+ var onHydrated = hydrationCallbacks.onHydrated;
- _child.return = completedWork;
- _child = _child.sibling;
+ if (onHydrated) {
+ onHydrated(suspenseInstance);
+ }
+ }
+ }
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
}
}
+ }
+}
- completedWork.subtreeFlags |= subtreeFlags;
- } else {
- // Bubble up the earliest expiration time.
- if ((completedWork.mode & ProfileMode) !== NoMode) {
- // In profiling mode, resetChildExpirationTime is also used to reset
- // profiler durations.
- var _treeBaseDuration = completedWork.selfBaseDuration;
- var _child2 = completedWork.child;
-
- while (_child2 !== null) {
- newChildLanes = mergeLanes(
- newChildLanes,
- mergeLanes(_child2.lanes, _child2.childLanes)
- ); // "Static" flags share the lifetime of the fiber/hook they belong to,
- // so we should bubble those up even during a bailout. All the other
- // flags have a lifetime only of a single render + commit, so we should
- // ignore them.
-
- subtreeFlags |= _child2.subtreeFlags & StaticMask;
- subtreeFlags |= _child2.flags & StaticMask; // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+function getRetryCache(finishedWork) {
+ // TODO: Unify the interface for the retry cache so we don't have to switch
+ // on the tag like this.
+ switch (finishedWork.tag) {
+ case SuspenseComponent:
+ case SuspenseListComponent: {
+ var retryCache = finishedWork.stateNode;
- _treeBaseDuration += _child2.treeBaseDuration;
- _child2 = _child2.sibling;
+ if (retryCache === null) {
+ retryCache = finishedWork.stateNode = new PossiblyWeakSet();
}
- completedWork.treeBaseDuration = _treeBaseDuration;
- } else {
- var _child3 = completedWork.child;
-
- while (_child3 !== null) {
- newChildLanes = mergeLanes(
- newChildLanes,
- mergeLanes(_child3.lanes, _child3.childLanes)
- ); // "Static" flags share the lifetime of the fiber/hook they belong to,
- // so we should bubble those up even during a bailout. All the other
- // flags have a lifetime only of a single render + commit, so we should
- // ignore them.
+ return retryCache;
+ }
- subtreeFlags |= _child3.subtreeFlags & StaticMask;
- subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code
- // smell because it assumes the commit phase is never concurrent with
- // the render phase. Will address during refactor to alternate model.
+ case OffscreenComponent: {
+ var instance = finishedWork.stateNode;
+ var _retryCache = instance._retryCache;
- _child3.return = completedWork;
- _child3 = _child3.sibling;
+ if (_retryCache === null) {
+ _retryCache = instance._retryCache = new PossiblyWeakSet();
}
+
+ return _retryCache;
}
- completedWork.subtreeFlags |= subtreeFlags;
+ default: {
+ throw new Error(
+ "Unexpected Suspense handler tag (" +
+ finishedWork.tag +
+ "). This is a " +
+ "bug in React."
+ );
+ }
}
+}
- completedWork.childLanes = newChildLanes;
- return didBailout;
+function detachOffscreenInstance(instance) {
+ var fiber = instance._current;
+
+ if (fiber === null) {
+ throw new Error(
+ "Calling Offscreen.detach before instance handle has been set."
+ );
+ }
+
+ if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags$1) {
+ // The instance is already detached, this is a noop.
+ return;
+ } // TODO: There is an opportunity to optimise this by not entering commit phase
+ // and unmounting effects directly.
+
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+
+ if (root !== null) {
+ instance._pendingVisibility |= OffscreenDetached;
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
}
+function attachOffscreenInstance(instance) {
+ var fiber = instance._current;
-function completeDehydratedSuspenseBoundary(
- current,
- workInProgress,
- nextState
-) {
- if (
- hasUnhydratedTailNodes() &&
- (workInProgress.mode & ConcurrentMode) !== NoMode &&
- (workInProgress.flags & DidCapture) === NoFlags$1
- ) {
- warnIfUnhydratedTailNodes(workInProgress);
- resetHydrationState();
- workInProgress.flags |= ForceClientRender | Incomplete | ShouldCapture;
- return false;
+ if (fiber === null) {
+ throw new Error(
+ "Calling Offscreen.detach before instance handle has been set."
+ );
}
- var wasHydrated = popHydrationState(workInProgress);
+ if ((instance._pendingVisibility & OffscreenDetached) === NoFlags$1) {
+ // The instance is already attached, this is a noop.
+ return;
+ }
- if (nextState !== null && nextState.dehydrated !== null) {
- // We might be inside a hydration state the first time we're picking up this
- // Suspense boundary, and also after we've reentered it for further hydration.
- if (current === null) {
- if (!wasHydrated) {
- throw new Error(
- "A dehydrated suspense component was completed without a hydrated node. " +
- "This is probably a bug in React."
- );
- }
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- prepareToHydrateHostSuspenseInstance(workInProgress);
- bubbleProperties(workInProgress);
+ if (root !== null) {
+ instance._pendingVisibility &= ~OffscreenDetached;
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+}
- {
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- var isTimedOutSuspense = nextState !== null;
+function attachSuspenseRetryListeners(finishedWork, wakeables) {
+ // If this boundary just timed out, then it will have a set of wakeables.
+ // For each wakeable, attach a listener so that when it resolves, React
+ // attempts to re-render the boundary in the primary (pre-timeout) state.
+ var retryCache = getRetryCache(finishedWork);
+ wakeables.forEach(function (wakeable) {
+ // Memoize using the boundary fiber to prevent redundant listeners.
+ var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
- if (isTimedOutSuspense) {
- // Don't count time spent in a timed out Suspense subtree as part of the base duration.
- var primaryChildFragment = workInProgress.child;
+ if (!retryCache.has(wakeable)) {
+ retryCache.add(wakeable);
- if (primaryChildFragment !== null) {
- // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
- workInProgress.treeBaseDuration -=
- primaryChildFragment.treeBaseDuration;
- }
+ {
+ if (isDevToolsPresent) {
+ if (inProgressLanes !== null && inProgressRoot !== null) {
+ // If we have pending work still, associate the original updaters with it.
+ restorePendingUpdaters(inProgressRoot, inProgressLanes);
+ } else {
+ throw Error(
+ "Expected finished root and lanes to be set. This is a bug in React."
+ );
}
}
}
- return false;
- } else {
- // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration
- // state since we're now exiting out of it. popHydrationState doesn't do that for us.
- resetHydrationState();
+ wakeable.then(retry, retry);
+ }
+ });
+} // This function detects when a Suspense boundary goes from visible to hidden.
+// It returns false if the boundary is already hidden.
+// TODO: Use an effect tag.
- if ((workInProgress.flags & DidCapture) === NoFlags$1) {
- // This boundary did not suspend so it's now hydrated and unsuspended.
- workInProgress.memoizedState = null;
- } // If nothing suspended, we need to schedule an effect to mark this boundary
- // as having hydrated so events know that they're free to be invoked.
- // It's also a signal to replay events and the suspense callback.
- // If something suspended, schedule an effect to attach retry listeners.
- // So we might as well always mark this.
+function isSuspenseBoundaryBeingHidden(current, finishedWork) {
+ if (current !== null) {
+ var oldState = current.memoizedState;
- workInProgress.flags |= Update;
- bubbleProperties(workInProgress);
+ if (oldState === null || oldState.dehydrated !== null) {
+ var newState = finishedWork.memoizedState;
+ return newState !== null && newState.dehydrated === null;
+ }
+ }
- {
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- var _isTimedOutSuspense = nextState !== null;
+ return false;
+}
+function commitMutationEffects(root, finishedWork, committedLanes) {
+ inProgressLanes = committedLanes;
+ inProgressRoot = root;
+ setCurrentFiber(finishedWork);
+ commitMutationEffectsOnFiber(finishedWork, root);
+ setCurrentFiber(finishedWork);
+ inProgressLanes = null;
+ inProgressRoot = null;
+}
- if (_isTimedOutSuspense) {
- // Don't count time spent in a timed out Suspense subtree as part of the base duration.
- var _primaryChildFragment = workInProgress.child;
+function recursivelyTraverseMutationEffects(root, parentFiber, lanes) {
+ // Deletions effects can be scheduled on any fiber type. They need to happen
+ // before the children effects hae fired.
+ var deletions = parentFiber.deletions;
- if (_primaryChildFragment !== null) {
- // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
- workInProgress.treeBaseDuration -=
- _primaryChildFragment.treeBaseDuration;
- }
- }
- }
- }
+ if (deletions !== null) {
+ for (var i = 0; i < deletions.length; i++) {
+ var childToDelete = deletions[i];
- return false;
+ try {
+ commitDeletionEffects(root, parentFiber, childToDelete);
+ } catch (error) {
+ captureCommitPhaseError(childToDelete, parentFiber, error);
+ }
}
- } else {
- // Successfully completed this tree. If this was a forced client render,
- // there may have been recoverable errors during first hydration
- // attempt. If so, add them to a queue so we can log them in the
- // commit phase.
- upgradeHydrationErrorsToRecoverable(); // Fall through to normal Suspense path
+ }
- return true;
+ var prevDebugFiber = getCurrentFiber();
+
+ if (parentFiber.subtreeFlags & MutationMask) {
+ var child = parentFiber.child;
+
+ while (child !== null) {
+ setCurrentFiber(child);
+ commitMutationEffectsOnFiber(child, root);
+ child = child.sibling;
+ }
}
+
+ setCurrentFiber(prevDebugFiber);
}
-function completeWork(current, workInProgress, renderLanes) {
- var newProps = workInProgress.pendingProps; // Note: This intentionally doesn't check if we're hydrating because comparing
- // to the current tree provider fiber is just as fast and less error-prone.
- // Ideally we would have a special version of the work loop only
- // for hydration.
+var currentHoistableRoot = null;
- popTreeContext(workInProgress);
+function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
+ var current = finishedWork.alternate;
+ var flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber,
+ // because the fiber tag is more specific. An exception is any flag related
+ // to reconciliation, because those can be set on all fiber types.
- switch (workInProgress.tag) {
- case IndeterminateComponent:
- case LazyComponent:
- case SimpleMemoComponent:
+ switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
- case Fragment:
- case Mode:
- case Profiler:
- case ContextConsumer:
case MemoComponent:
- bubbleProperties(workInProgress);
- return null;
-
- case ClassComponent: {
- var Component = workInProgress.type;
-
- if (isContextProvider(Component)) {
- popContext(workInProgress);
- }
-
- bubbleProperties(workInProgress);
- return null;
- }
+ case SimpleMemoComponent: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
- case HostRoot: {
- var fiberRoot = workInProgress.stateNode;
+ if (flags & Update) {
+ try {
+ commitHookEffectListUnmount(
+ Insertion | HasEffect,
+ finishedWork,
+ finishedWork.return
+ );
+ commitHookEffectListMount(Insertion | HasEffect, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ } // Layout effects are destroyed during the mutation phase so that all
+ // destroy functions for all fibers are called before any create functions.
+ // This prevents sibling component effects from interfering with each other,
+ // e.g. a destroy function in one component should never override a ref set
+ // by a create function in another component during the same commit.
- if (enableTransitionTracing) {
- var transitions = getWorkInProgressTransitions(); // We set the Passive flag here because if there are new transitions,
- // we will need to schedule callbacks and process the transitions,
- // which we do in the passive phase
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ commitHookEffectListUnmount(
+ Layout | HasEffect,
+ finishedWork,
+ finishedWork.return
+ );
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
- if (transitions !== null) {
- workInProgress.flags |= Passive$1;
+ recordLayoutEffectDuration(finishedWork);
+ } else {
+ try {
+ commitHookEffectListUnmount(
+ Layout | HasEffect,
+ finishedWork,
+ finishedWork.return
+ );
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
}
}
- {
- var previousCache = null;
+ return;
+ }
+
+ case ClassComponent: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
+ if (flags & Ref) {
if (current !== null) {
- previousCache = current.memoizedState.cache;
+ safelyDetachRef(current, current.return);
}
+ }
- var cache = workInProgress.memoizedState.cache;
+ if (flags & Callback && offscreenSubtreeIsHidden) {
+ var updateQueue = finishedWork.updateQueue;
- if (cache !== previousCache) {
- // Run passive effects to retain/release the cache.
- workInProgress.flags |= Passive$1;
+ if (updateQueue !== null) {
+ deferHiddenCallbacks(updateQueue);
}
-
- popCacheProvider(workInProgress);
}
- if (enableTransitionTracing) {
- popRootMarkerInstance(workInProgress);
- }
+ return;
+ }
- popRootTransition(workInProgress);
- popHostContainer(workInProgress);
- popTopLevelContextObject(workInProgress);
- resetWorkInProgressVersions();
+ case HostHoistable: {
+ {
+ // We cast because we always set the root at the React root and so it cannot be
+ // null while we are processing mutation effects
+ var hoistableRoot = currentHoistableRoot;
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
- if (fiberRoot.pendingContext) {
- fiberRoot.context = fiberRoot.pendingContext;
- fiberRoot.pendingContext = null;
- }
+ if (flags & Ref) {
+ if (current !== null) {
+ safelyDetachRef(current, current.return);
+ }
+ }
- if (current === null || current.child === null) {
- // If we hydrated, pop so that we can delete any remaining children
- // that weren't hydrated.
- var wasHydrated = popHydrationState(workInProgress);
+ if (flags & Update) {
+ var currentResource = current !== null ? current.memoizedState : null;
+ var newResource = finishedWork.memoizedState;
- if (wasHydrated) {
- // If we hydrated, then we'll need to schedule an update for
- // the commit side-effects on the root.
- markUpdate(workInProgress);
- } else {
- if (current !== null) {
- var prevState = current.memoizedState;
+ if (current === null) {
+ // We are mounting a new HostHoistable Fiber. We fork the mount
+ // behavior based on whether this instance is a Hoistable Instance
+ // or a Hoistable Resource
+ if (newResource === null) {
+ if (finishedWork.stateNode === null) {
+ finishedWork.stateNode = hydrateHoistable(
+ hoistableRoot,
+ finishedWork.type,
+ finishedWork.memoizedProps,
+ finishedWork
+ );
+ } else {
+ mountHoistable(
+ hoistableRoot,
+ finishedWork.type,
+ finishedWork.stateNode
+ );
+ }
+ } else {
+ finishedWork.stateNode = acquireResource(
+ hoistableRoot,
+ newResource,
+ finishedWork.memoizedProps
+ );
+ }
+ } else if (currentResource !== newResource) {
+ // We are moving to or from Hoistable Resource, or between different Hoistable Resources
+ if (currentResource === null) {
+ if (current.stateNode !== null) {
+ unmountHoistable(current.stateNode);
+ }
+ } else {
+ releaseResource(currentResource);
+ }
- if (
- // Check if this is a client root
- !prevState.isDehydrated || // Check if we reverted to client rendering (e.g. due to an error)
- (workInProgress.flags & ForceClientRender) !== NoFlags$1
- ) {
- // Schedule an effect to clear this container at the start of the
- // next commit. This handles the case of React rendering into a
- // container with previous children. It's also safe to do for
- // updates too, because current.child would only be null if the
- // previous render was null (so the container would already
- // be empty).
- workInProgress.flags |= Snapshot; // If this was a forced client render, there may have been
- // recoverable errors during first hydration attempt. If so, add
- // them to a queue so we can log them in the commit phase.
+ if (newResource === null) {
+ mountHoistable(
+ hoistableRoot,
+ finishedWork.type,
+ finishedWork.stateNode
+ );
+ } else {
+ acquireResource(
+ hoistableRoot,
+ newResource,
+ finishedWork.memoizedProps
+ );
+ }
+ } else if (newResource === null && finishedWork.stateNode !== null) {
+ // We may have an update on a Hoistable element
+ var updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
- upgradeHydrationErrorsToRecoverable();
+ if (updatePayload !== null) {
+ try {
+ commitUpdate(
+ finishedWork.stateNode,
+ updatePayload,
+ finishedWork.type,
+ current.memoizedProps,
+ finishedWork.memoizedProps,
+ finishedWork
+ );
+ } catch (error) {
+ captureCommitPhaseError(
+ finishedWork,
+ finishedWork.return,
+ error
+ );
+ }
}
}
}
- }
- bubbleProperties(workInProgress);
- if (enableTransitionTracing) {
- if ((workInProgress.subtreeFlags & Visibility) !== NoFlags$1) {
- // If any of our suspense children toggle visibility, this means that
- // the pending boundaries array needs to be updated, which we only
- // do in the passive phase.
- workInProgress.flags |= Passive$1;
- }
+ return;
}
-
- return null;
}
+ // eslint-disable-next-line-no-fallthrough
- case HostHoistable: {
+ case HostSingleton: {
{
- var currentRef = current ? current.ref : null;
+ if (flags & Update) {
+ var previousWork = finishedWork.alternate;
- if (currentRef !== workInProgress.ref) {
- markRef(workInProgress);
- }
+ if (previousWork === null) {
+ var singleton = finishedWork.stateNode;
+ var props = finishedWork.memoizedProps; // This was a new mount, we need to clear and set initial properties
- if (
- // We are mounting and must Update this Hoistable in this commit
- current === null || // We are transitioning to, from, or between Hoistable Resources
- // and require an update
- current.memoizedState !== workInProgress.memoizedState
- ) {
- markUpdate(workInProgress);
- } else if (workInProgress.memoizedState === null) {
- // We may have props to update on the Hoistable instance. We use the
- // updateHostComponent path becuase it produces the update queue
- // we need for Hoistables
- updateHostComponent(
- current,
- workInProgress,
- workInProgress.type,
- workInProgress.pendingProps
- );
+ clearSingleton(singleton);
+ acquireSingletonInstance(
+ finishedWork.type,
+ props,
+ singleton,
+ finishedWork
+ );
+ }
}
-
- bubbleProperties(workInProgress);
- return null;
}
}
// eslint-disable-next-line-no-fallthrough
- case HostSingleton: {
- {
- popHostContext(workInProgress);
- var rootContainerInstance = getRootHostContainer();
- var type = workInProgress.type;
+ case HostComponent: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
- if (current !== null && workInProgress.stateNode != null) {
- updateHostComponent(current, workInProgress, type, newProps);
+ if (flags & Ref) {
+ if (current !== null) {
+ safelyDetachRef(current, current.return);
+ }
+ }
- if (current.ref !== workInProgress.ref) {
- markRef(workInProgress);
- }
- } else {
- if (!newProps) {
- if (workInProgress.stateNode === null) {
- throw new Error(
- "We must have new props for new mounts. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
- } // This can happen when we abort work.
+ {
+ // TODO: ContentReset gets cleared by the children during the commit
+ // phase. This is a refactor hazard because it means we must read
+ // flags the flags after `commitReconciliationEffects` has already run;
+ // the order matters. We should refactor so that ContentReset does not
+ // rely on mutating the flag during commit. Like by setting a flag
+ // during the render phase instead.
+ if (finishedWork.flags & ContentReset) {
+ var instance = finishedWork.stateNode;
- bubbleProperties(workInProgress);
- return null;
+ try {
+ resetTextContent(instance);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
+ }
- var currentHostContext = getHostContext();
+ if (flags & Update) {
+ var _instance2 = finishedWork.stateNode;
- var _wasHydrated = popHydrationState(workInProgress);
+ if (_instance2 != 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.
- if (_wasHydrated) {
- // We ignore the boolean indicating there is an updateQueue because
- // it is used only to set text children and HostSingletons do not
- // use them.
- prepareToHydrateHostInstance(workInProgress, currentHostContext);
- } else {
- workInProgress.stateNode = resolveSingletonInstance(
- type,
- newProps,
- rootContainerInstance,
- currentHostContext,
- true
- );
- markUpdate(workInProgress);
- }
+ var oldProps = current !== null ? current.memoizedProps : newProps;
+ var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components.
- if (workInProgress.ref !== null) {
- // If there is a ref on a host node we need to schedule a callback
- markRef(workInProgress);
+ var _updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
+
+ if (_updatePayload !== null) {
+ try {
+ commitUpdate(
+ _instance2,
+ _updatePayload,
+ type,
+ oldProps,
+ newProps,
+ finishedWork
+ );
+ } catch (error) {
+ captureCommitPhaseError(
+ finishedWork,
+ finishedWork.return,
+ error
+ );
+ }
+ }
}
}
-
- bubbleProperties(workInProgress);
- return null;
}
- }
- // eslint-disable-next-line-no-fallthrough
- case HostComponent: {
- popHostContext(workInProgress);
- var _type = workInProgress.type;
+ return;
+ }
- if (current !== null && workInProgress.stateNode != null) {
- updateHostComponent(current, workInProgress, _type, newProps);
+ case HostText: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
- if (current.ref !== workInProgress.ref) {
- markRef(workInProgress);
- }
- } else {
- if (!newProps) {
- if (workInProgress.stateNode === null) {
+ if (flags & Update) {
+ {
+ if (finishedWork.stateNode === null) {
throw new Error(
- "We must have new props for new mounts. This error is likely " +
+ "This should have a text node initialized. This error is likely " +
"caused by a bug in React. Please file an issue."
);
- } // This can happen when we abort work.
-
- bubbleProperties(workInProgress);
- return null;
- }
+ }
- var _currentHostContext2 = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context
- // "stack" as the parent. Then append children as we go in beginWork
- // or completeWork depending on whether we want to add them top->down or
- // bottom->up. Top->down is faster in IE11.
+ var 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 _wasHydrated2 = popHydrationState(workInProgress);
+ var oldText = current !== null ? current.memoizedProps : newText;
- if (_wasHydrated2) {
- // TODO: Move this and createInstance step into the beginPhase
- // to consolidate.
- if (
- prepareToHydrateHostInstance(workInProgress, _currentHostContext2)
- ) {
- // If changes to the hydrated node need to be applied at the
- // commit-phase we mark this as such.
- markUpdate(workInProgress);
+ try {
+ commitTextUpdate(textInstance, oldText, newText);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
- } else {
- var _rootContainerInstance = getRootHostContainer();
+ }
+ }
- var instance = createInstance(
- _type,
- newProps,
- _rootContainerInstance,
- _currentHostContext2,
- workInProgress
- );
- appendAllChildren(instance, workInProgress);
- workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
- // (eg DOM renderer supports auto-focus for certain elements).
- // Make sure such renderers get scheduled for later work.
+ return;
+ }
- if (finalizeInitialChildren(instance, _type, newProps)) {
- markUpdate(workInProgress);
- }
- }
+ case HostRoot: {
+ {
+ prepareToCommitHoistables();
+ var previousHoistableRoot = currentHoistableRoot;
+ currentHoistableRoot = getHoistableRoot(root.containerInfo);
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ currentHoistableRoot = previousHoistableRoot;
+ commitReconciliationEffects(finishedWork);
+ }
- if (workInProgress.ref !== null) {
- // If there is a ref on a host node we need to schedule a callback
- markRef(workInProgress);
+ if (flags & Update) {
+ {
+ if (current !== null) {
+ var prevRootState = current.memoizedState;
+
+ if (prevRootState.isDehydrated) {
+ try {
+ commitHydratedContainer(root.containerInfo);
+ } catch (error) {
+ captureCommitPhaseError(
+ finishedWork,
+ finishedWork.return,
+ error
+ );
+ }
+ }
+ }
}
}
- bubbleProperties(workInProgress);
- return null;
+ return;
}
- 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.
+ case HostPortal: {
+ {
+ var _previousHoistableRoot = currentHoistableRoot;
+ currentHoistableRoot = getHoistableRoot(
+ finishedWork.stateNode.containerInfo
+ );
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
+ currentHoistableRoot = _previousHoistableRoot;
+ }
- updateHostText(current, workInProgress, oldText, newText);
- } else {
- if (typeof newText !== "string") {
- if (workInProgress.stateNode === null) {
- throw new Error(
- "We must have new props for new mounts. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
- } // This can happen when we abort work.
- }
+ return;
+ }
- var _rootContainerInstance2 = getRootHostContainer();
+ case SuspenseComponent: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
+ var offscreenFiber = finishedWork.child;
- var _currentHostContext3 = getHostContext();
+ if (offscreenFiber.flags & Visibility) {
+ var newState = offscreenFiber.memoizedState;
+ var isHidden = newState !== null;
- var _wasHydrated3 = popHydrationState(workInProgress);
+ if (isHidden) {
+ var wasHidden =
+ offscreenFiber.alternate !== null &&
+ offscreenFiber.alternate.memoizedState !== null;
- if (_wasHydrated3) {
- if (prepareToHydrateHostTextInstance(workInProgress)) {
- markUpdate(workInProgress);
+ if (!wasHidden) {
+ // TODO: Move to passive phase
+ markCommitTimeOfFallback();
}
- } else {
- workInProgress.stateNode = createTextInstance(
- newText,
- _rootContainerInstance2,
- _currentHostContext3,
- workInProgress
- );
}
}
- bubbleProperties(workInProgress);
- return null;
- }
+ if (flags & Update) {
+ try {
+ commitSuspenseCallback(finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
- case SuspenseComponent: {
- popSuspenseHandler(workInProgress);
- var nextState = workInProgress.memoizedState; // Special path for dehydrated boundaries. We may eventually move this
- // to its own fiber type so that we can add other kinds of hydration
- // boundaries that aren't associated with a Suspense tree. In anticipation
- // of such a refactor, all the hydration logic is contained in
- // this branch.
+ var wakeables = finishedWork.updateQueue;
- if (
- current === null ||
- (current.memoizedState !== null &&
- current.memoizedState.dehydrated !== null)
- ) {
- var fallthroughToNormalSuspensePath =
- completeDehydratedSuspenseBoundary(
- current,
- workInProgress,
- nextState
- );
+ if (wakeables !== null) {
+ finishedWork.updateQueue = null;
+ attachSuspenseRetryListeners(finishedWork, wakeables);
+ }
+ }
- if (!fallthroughToNormalSuspensePath) {
- if (workInProgress.flags & ShouldCapture) {
- // Special case. There were remaining unhydrated nodes. We treat
- // this as a mismatch. Revert to client rendering.
- return workInProgress;
- } else {
- // Did not finish hydrating, either because this is the initial
- // render or because something suspended.
- return null;
- }
- } // Continue with the normal Suspense path.
+ return;
+ }
+
+ case OffscreenComponent: {
+ if (flags & Ref) {
+ if (current !== null) {
+ safelyDetachRef(current, current.return);
+ }
}
- if ((workInProgress.flags & DidCapture) !== NoFlags$1) {
- // Something suspended. Re-render with the fallback children.
- workInProgress.lanes = renderLanes; // Do not reset the effect list.
+ var _newState = finishedWork.memoizedState;
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- transferActualDuration(workInProgress);
- } // Don't bubble properties in this case.
+ var _isHidden = _newState !== null;
- return workInProgress;
+ var _wasHidden = current !== null && current.memoizedState !== null;
+
+ if (finishedWork.mode & ConcurrentMode) {
+ // Before committing the children, track on the stack whether this
+ // offscreen subtree was already hidden, so that we don't unmount the
+ // effects again.
+ var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
+ var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
+ offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden || _isHidden;
+ offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || _wasHidden;
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
+ offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
+ } else {
+ recursivelyTraverseMutationEffects(root, finishedWork);
}
- var nextDidTimeout = nextState !== null;
- var prevDidTimeout = current !== null && current.memoizedState !== null;
+ commitReconciliationEffects(finishedWork);
+ var offscreenInstance = finishedWork.stateNode; // TODO: Add explicit effect flag to set _current.
- if (nextDidTimeout) {
- var offscreenFiber = workInProgress.child;
- var _previousCache = null;
+ offscreenInstance._current = finishedWork; // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
+ // to support batching of `attach` and `detach` calls.
- if (
- offscreenFiber.alternate !== null &&
- offscreenFiber.alternate.memoizedState !== null &&
- offscreenFiber.alternate.memoizedState.cachePool !== null
- ) {
- _previousCache =
- offscreenFiber.alternate.memoizedState.cachePool.pool;
+ offscreenInstance._visibility &= ~OffscreenDetached;
+ offscreenInstance._visibility |=
+ offscreenInstance._pendingVisibility & OffscreenDetached;
+
+ if (flags & Visibility) {
+ // Track the current state on the Offscreen instance so we can
+ // read it during an event
+ if (_isHidden) {
+ offscreenInstance._visibility &= ~OffscreenVisible;
+ } else {
+ offscreenInstance._visibility |= OffscreenVisible;
}
- var _cache = null;
+ if (_isHidden) {
+ var isUpdate = current !== null;
+ var wasHiddenByAncestorOffscreen =
+ offscreenSubtreeIsHidden || offscreenSubtreeWasHidden; // Only trigger disapper layout effects if:
+ // - This is an update, not first mount.
+ // - This Offscreen was not hidden before.
+ // - Ancestor Offscreen was not hidden in previous commit.
- if (
- offscreenFiber.memoizedState !== null &&
- offscreenFiber.memoizedState.cachePool !== null
- ) {
- _cache = offscreenFiber.memoizedState.cachePool.pool;
- }
+ if (isUpdate && !_wasHidden && !wasHiddenByAncestorOffscreen) {
+ if ((finishedWork.mode & ConcurrentMode) !== NoMode) {
+ // Disappear the layout effects of all the children
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ }
+ }
+ } // Offscreen with manual mode manages visibility manually.
- if (_cache !== _previousCache) {
- // Run passive effects to retain/release the cache.
- offscreenFiber.flags |= Passive$1;
+ if (!isOffscreenManual(finishedWork)) {
+ // TODO: This needs to run whenever there's an insertion or update
+ // inside a hidden Offscreen tree.
+ hideOrUnhideAllChildren(finishedWork, _isHidden);
}
- } // If the suspended state of the boundary changes, we need to schedule
- // a passive effect, which is when we process the transitions
+ } // TODO: Move to passive phase
- if (nextDidTimeout !== prevDidTimeout) {
- if (enableTransitionTracing) {
- var _offscreenFiber = workInProgress.child;
- _offscreenFiber.flags |= Passive$1;
- } // If the suspended state of the boundary changes, we need to schedule
- // an effect to toggle the subtree's visibility. When we switch from
- // fallback -> primary, the inner Offscreen fiber schedules this effect
- // as part of its normal complete phase. But when we switch from
- // primary -> fallback, the inner Offscreen fiber does not have a complete
- // phase. So we need to schedule its effect here.
- //
- // We also use this flag to connect/disconnect the effects, but the same
- // logic applies: when re-connecting, the Offscreen fiber's complete
- // phase will handle scheduling the effect. It's only when the fallback
- // is active that we have to do anything special.
+ if (flags & Update) {
+ var offscreenQueue = finishedWork.updateQueue;
- if (nextDidTimeout) {
- var _offscreenFiber2 = workInProgress.child;
- _offscreenFiber2.flags |= Visibility;
+ if (offscreenQueue !== null) {
+ var _wakeables = offscreenQueue.wakeables;
+
+ if (_wakeables !== null) {
+ offscreenQueue.wakeables = null;
+ attachSuspenseRetryListeners(finishedWork, _wakeables);
+ }
}
}
- var wakeables = workInProgress.updateQueue;
+ return;
+ }
- if (wakeables !== null) {
- // Schedule an effect to attach a retry listener to the promise.
- // TODO: Move to passive phase
- workInProgress.flags |= Update;
- }
+ case SuspenseListComponent: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
- if (
- workInProgress.updateQueue !== null &&
- workInProgress.memoizedProps.suspenseCallback != null
- ) {
- // Always notify the callback
- // TODO: Move to passive phase
- workInProgress.flags |= Update;
+ if (flags & Update) {
+ var _wakeables2 = finishedWork.updateQueue;
+
+ if (_wakeables2 !== null) {
+ finishedWork.updateQueue = null;
+ attachSuspenseRetryListeners(finishedWork, _wakeables2);
+ }
}
- bubbleProperties(workInProgress);
+ return;
+ }
+ case ScopeComponent: {
{
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- if (nextDidTimeout) {
- // Don't count time spent in a timed out Suspense subtree as part of the base duration.
- var primaryChildFragment = workInProgress.child;
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork); // TODO: This is a temporary solution that allowed us to transition away
+ // from React Flare on www.
- if (primaryChildFragment !== null) {
- // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
- workInProgress.treeBaseDuration -=
- primaryChildFragment.treeBaseDuration;
- }
+ if (flags & Ref) {
+ if (current !== null) {
+ safelyDetachRef(finishedWork, finishedWork.return);
}
+
+ safelyAttachRef(finishedWork, finishedWork.return);
+ }
+
+ if (flags & Update) {
+ var scopeInstance = finishedWork.stateNode;
+ prepareScopeUpdate(scopeInstance, finishedWork);
}
}
- return null;
+ return;
}
- case HostPortal:
- popHostContainer(workInProgress);
+ default: {
+ recursivelyTraverseMutationEffects(root, finishedWork);
+ commitReconciliationEffects(finishedWork);
+ return;
+ }
+ }
+}
- if (current === null) {
- preparePortalMount(workInProgress.stateNode.containerInfo);
- }
+function commitReconciliationEffects(finishedWork) {
+ // Placement effects (insertions, reorders) can be scheduled on any fiber
+ // type. They needs to happen after the children effects have fired, but
+ // before the effects on this fiber have fired.
+ var flags = finishedWork.flags;
- bubbleProperties(workInProgress);
- return null;
+ if (flags & Placement) {
+ try {
+ commitPlacement(finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ } // Clear the "placement" from effect tag so that we know that this is
+ // inserted, before any life-cycles like componentDidMount gets called.
+ // TODO: findDOMNode doesn't rely on this any more but isMounted does
+ // and isMounted is deprecated anyway so we should be able to kill this.
- case ContextProvider:
- // Pop provider fiber
- var context = workInProgress.type._context;
- popProvider(context, workInProgress);
- bubbleProperties(workInProgress);
- return null;
+ finishedWork.flags &= ~Placement;
+ }
- case IncompleteClassComponent: {
- // Same as class component case. I put it down here so that the tags are
- // sequential to ensure this switch is compiled to a jump table.
- var _Component = workInProgress.type;
+ if (flags & Hydrating) {
+ finishedWork.flags &= ~Hydrating;
+ }
+}
- if (isContextProvider(_Component)) {
- popContext(workInProgress);
- }
+function commitLayoutEffects(finishedWork, root, committedLanes) {
+ inProgressLanes = committedLanes;
+ inProgressRoot = root;
+ var current = finishedWork.alternate;
+ commitLayoutEffectOnFiber(root, current, finishedWork);
+ inProgressLanes = null;
+ inProgressRoot = null;
+}
- bubbleProperties(workInProgress);
- return null;
+function recursivelyTraverseLayoutEffects(root, parentFiber, lanes) {
+ var prevDebugFiber = getCurrentFiber();
+
+ if (parentFiber.subtreeFlags & LayoutMask) {
+ var child = parentFiber.child;
+
+ while (child !== null) {
+ setCurrentFiber(child);
+ var current = child.alternate;
+ commitLayoutEffectOnFiber(root, current, child);
+ child = child.sibling;
}
+ }
- case SuspenseListComponent: {
- popSuspenseListContext(workInProgress);
- var renderState = workInProgress.memoizedState;
+ setCurrentFiber(prevDebugFiber);
+}
- if (renderState === null) {
- // We're running in the default, "independent" mode.
- // We don't do anything in this mode.
- bubbleProperties(workInProgress);
- return null;
+function disappearLayoutEffects(finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case MemoComponent:
+ case SimpleMemoComponent: {
+ // TODO (Offscreen) Check: flags & LayoutStatic
+ if (shouldProfile(finishedWork)) {
+ try {
+ startLayoutEffectTimer();
+ commitHookEffectListUnmount(
+ Layout,
+ finishedWork,
+ finishedWork.return
+ );
+ } finally {
+ recordLayoutEffectDuration(finishedWork);
+ }
+ } else {
+ commitHookEffectListUnmount(Layout, finishedWork, finishedWork.return);
}
- var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags$1;
- var renderedTail = renderState.rendering;
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ break;
+ }
- if (renderedTail === null) {
- // We just rendered the head.
- if (!didSuspendAlready) {
- // This is the first pass. We need to figure out if anything is still
- // suspended in the rendered set.
- // If new content unsuspended, but there's still some content that
- // didn't. Then we need to do a second pass that forces everything
- // to keep showing their fallbacks.
- // We might be suspended if something in this render pass suspended, or
- // something in the previous committed pass suspended. Otherwise,
- // there's no chance so we can skip the expensive call to
- // findFirstSuspended.
- var cannotBeSuspended =
- renderHasNotSuspendedYet() &&
- (current === null || (current.flags & DidCapture) === NoFlags$1);
+ case ClassComponent: {
+ // TODO (Offscreen) Check: flags & RefStatic
+ safelyDetachRef(finishedWork, finishedWork.return);
+ var instance = finishedWork.stateNode;
- if (!cannotBeSuspended) {
- var row = workInProgress.child;
+ if (typeof instance.componentWillUnmount === "function") {
+ safelyCallComponentWillUnmount(
+ finishedWork,
+ finishedWork.return,
+ instance
+ );
+ }
- while (row !== null) {
- var suspended = findFirstSuspended(row);
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ break;
+ }
- if (suspended !== null) {
- didSuspendAlready = true;
- workInProgress.flags |= DidCapture;
- cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as
- // part of the second pass. In that case nothing will subscribe to
- // its thenables. Instead, we'll transfer its thenables to the
- // SuspenseList so that it can retry if they resolve.
- // There might be multiple of these in the list but since we're
- // going to wait for all of them anyway, it doesn't really matter
- // which ones gets to ping. In theory we could get clever and keep
- // track of how many dependencies remain but it gets tricky because
- // in the meantime, we can add/remove/change items and dependencies.
- // We might bail out of the loop before finding any but that
- // doesn't matter since that means that the other boundaries that
- // we did find already has their listeners attached.
+ case HostHoistable:
+ case HostSingleton:
+ case HostComponent: {
+ // TODO (Offscreen) Check: flags & RefStatic
+ safelyDetachRef(finishedWork, finishedWork.return);
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ break;
+ }
- var newThenables = suspended.updateQueue;
+ case OffscreenComponent: {
+ // TODO (Offscreen) Check: flags & RefStatic
+ safelyDetachRef(finishedWork, finishedWork.return);
+ var isHidden = finishedWork.memoizedState !== null;
- if (newThenables !== null) {
- workInProgress.updateQueue = newThenables;
- workInProgress.flags |= Update;
- } // Rerender the whole list, but this time, we'll force fallbacks
- // to stay in place.
- // Reset the effect flags before doing the second pass since that's now invalid.
- // Reset the child fibers to their original state.
+ if (isHidden);
+ else {
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ }
- workInProgress.subtreeFlags = NoFlags$1;
- resetChildFibers(workInProgress, renderLanes); // Set up the Suspense List Context to force suspense and
- // immediately rerender the children.
+ break;
+ }
- pushSuspenseListContext(
- workInProgress,
- setShallowSuspenseListContext(
- suspenseStackCursor.current,
- ForceSuspenseFallback
- )
- ); // Don't bubble properties in this case.
+ default: {
+ recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ break;
+ }
+ }
+}
- return workInProgress.child;
- }
+function recursivelyTraverseDisappearLayoutEffects(parentFiber) {
+ // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
+ var child = parentFiber.child;
- row = row.sibling;
- }
- }
+ while (child !== null) {
+ disappearLayoutEffects(child);
+ child = child.sibling;
+ }
+}
- if (renderState.tail !== null && now$1() > getRenderTargetTime()) {
- // We have already passed our CPU deadline but we still have rows
- // left in the tail. We'll just give up further attempts to render
- // the main content and only render fallbacks.
- workInProgress.flags |= DidCapture;
- didSuspendAlready = true;
- cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
- // to get it started back up to attempt the next item. While in terms
- // of priority this work has the same priority as this current render,
- // it's not part of the same transition once the transition has
- // committed. If it's sync, we still want to yield so that it can be
- // painted. Conceptually, this is really the same as pinging.
- // We can use any RetryLane even if it's the one currently rendering
- // since we're leaving it behind on this node.
+function reappearLayoutEffects(
+ finishedRoot,
+ current,
+ finishedWork, // This function visits both newly finished work and nodes that were re-used
+ // from a previously committed tree. We cannot check non-static flags if the
+ // node was reused.
+ includeWorkInProgressEffects
+) {
+ // Turn on layout effects in a tree that previously disappeared.
+ var flags = finishedWork.flags;
- workInProgress.lanes = SomeRetryLane;
- }
- } else {
- cutOffTailIfNeeded(renderState, false);
- } // Next we're going to render the tail.
- } else {
- // Append the rendered row to the child list.
- if (!didSuspendAlready) {
- var _suspended = findFirstSuspended(renderedTail);
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ ); // TODO: Check flags & LayoutStatic
- if (_suspended !== null) {
- workInProgress.flags |= DidCapture;
- didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't
- // get lost if this row ends up dropped during a second pass.
+ commitHookLayoutEffects(finishedWork, Layout);
+ break;
+ }
- var _newThenables = _suspended.updateQueue;
+ case ClassComponent: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ ); // TODO: Check for LayoutStatic flag
- if (_newThenables !== null) {
- workInProgress.updateQueue = _newThenables;
- workInProgress.flags |= Update;
- }
+ var instance = finishedWork.stateNode;
- cutOffTailIfNeeded(renderState, true); // This might have been modified.
+ if (typeof instance.componentDidMount === "function") {
+ try {
+ instance.componentDidMount();
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ } // Commit any callbacks that would have fired while the component
+ // was hidden.
- if (
- renderState.tail === null &&
- renderState.tailMode === "hidden" &&
- !renderedTail.alternate &&
- !getIsHydrating() // We don't cut it if we're hydrating.
- ) {
- // We're done.
- bubbleProperties(workInProgress);
- return null;
- }
- } else if (
- // The time it took to render last row is greater than the remaining
- // time we have to render. So rendering one more row would likely
- // exceed it.
- now$1() * 2 - renderState.renderingStartTime >
- getRenderTargetTime() &&
- renderLanes !== OffscreenLane
- ) {
- // We have now passed our CPU deadline and we'll just give up further
- // attempts to render the main content and only render fallbacks.
- // The assumption is that this is usually faster.
- workInProgress.flags |= DidCapture;
- didSuspendAlready = true;
- cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
- // to get it started back up to attempt the next item. While in terms
- // of priority this work has the same priority as this current render,
- // it's not part of the same transition once the transition has
- // committed. If it's sync, we still want to yield so that it can be
- // painted. Conceptually, this is really the same as pinging.
- // We can use any RetryLane even if it's the one currently rendering
- // since we're leaving it behind on this node.
+ var updateQueue = finishedWork.updateQueue;
- workInProgress.lanes = SomeRetryLane;
- }
- }
+ if (updateQueue !== null) {
+ commitHiddenCallbacks(updateQueue, instance);
+ } // If this is newly finished work, check for setState callbacks
- if (renderState.isBackwards) {
- // The effect list of the backwards tail will have been added
- // to the end. This breaks the guarantee that life-cycles fire in
- // sibling order but that isn't a strong guarantee promised by React.
- // Especially since these might also just pop in during future commits.
- // Append to the beginning of the list.
- renderedTail.sibling = workInProgress.child;
- workInProgress.child = renderedTail;
- } else {
- var previousSibling = renderState.last;
+ if (includeWorkInProgressEffects && flags & Callback) {
+ commitClassCallbacks(finishedWork);
+ } // TODO: Check flags & RefStatic
- if (previousSibling !== null) {
- previousSibling.sibling = renderedTail;
- } else {
- workInProgress.child = renderedTail;
- }
+ safelyAttachRef(finishedWork, finishedWork.return);
+ break;
+ }
+ // Unlike commitLayoutEffectsOnFiber, we don't need to handle HostRoot
+ // because this function only visits nodes that are inside an
+ // Offscreen fiber.
+ // case HostRoot: {
+ // ...
+ // }
- renderState.last = renderedTail;
- }
- }
+ case HostHoistable:
+ case HostSingleton:
+ case HostComponent: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ ); // 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 (renderState.tail !== null) {
- // We still have tail rows to render.
- // Pop a row.
- var next = renderState.tail;
- renderState.rendering = next;
- renderState.tail = next.sibling;
- renderState.renderingStartTime = now$1();
- next.sibling = null; // Restore the context.
- // TODO: We can probably just avoid popping it instead and only
- // setting it the first time we go from not suspended to suspended.
+ if (includeWorkInProgressEffects && current === null && flags & Update) {
+ commitHostComponentMount(finishedWork);
+ } // TODO: Check flags & Ref
- var suspenseContext = suspenseStackCursor.current;
+ safelyAttachRef(finishedWork, finishedWork.return);
+ break;
+ }
- if (didSuspendAlready) {
- suspenseContext = setShallowSuspenseListContext(
- suspenseContext,
- ForceSuspenseFallback
- );
- } else {
- suspenseContext =
- setDefaultShallowSuspenseListContext(suspenseContext);
- }
+ case Profiler: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ ); // TODO: Figure out how Profiler updates should work with Offscreen
- pushSuspenseListContext(workInProgress, suspenseContext); // Do a pass over the next row.
- // Don't bubble properties in this case.
+ if (includeWorkInProgressEffects && flags & Update) {
+ commitProfilerUpdate(finishedWork, current);
+ }
- return next;
+ break;
+ }
+
+ case SuspenseComponent: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ ); // TODO: Figure out how Suspense hydration callbacks should work
+ // with Offscreen.
+
+ if (includeWorkInProgressEffects && flags & Update) {
+ commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
}
- bubbleProperties(workInProgress);
- return null;
+ break;
}
- case ScopeComponent: {
- {
- if (current === null) {
- var scopeInstance = createScopeInstance();
- workInProgress.stateNode = scopeInstance;
- prepareScopeUpdate(scopeInstance, workInProgress);
+ case OffscreenComponent: {
+ var offscreenState = finishedWork.memoizedState;
+ var isHidden = offscreenState !== null;
- if (workInProgress.ref !== null) {
- markRef(workInProgress);
- markUpdate(workInProgress);
- }
- } else {
- if (workInProgress.ref !== null) {
- markUpdate(workInProgress);
- }
+ if (isHidden);
+ else {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ );
+ } // TODO: Check flags & Ref
- if (current.ref !== workInProgress.ref) {
- markRef(workInProgress);
- }
- }
+ safelyAttachRef(finishedWork, finishedWork.return);
+ break;
+ }
- bubbleProperties(workInProgress);
- return null;
- }
+ default: {
+ recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ finishedWork,
+ includeWorkInProgressEffects
+ );
+ break;
}
+ }
+}
- case OffscreenComponent:
- case LegacyHiddenComponent: {
- popSuspenseHandler(workInProgress);
- popHiddenContext(workInProgress);
- var _nextState = workInProgress.memoizedState;
- var nextIsHidden = _nextState !== null; // Schedule a Visibility effect if the visibility has changed
+function recursivelyTraverseReappearLayoutEffects(
+ finishedRoot,
+ parentFiber,
+ includeWorkInProgressEffects
+) {
+ // This function visits both newly finished work and nodes that were re-used
+ // from a previously committed tree. We cannot check non-static flags if the
+ // node was reused.
+ var childShouldIncludeWorkInProgressEffects =
+ includeWorkInProgressEffects &&
+ (parentFiber.subtreeFlags & LayoutMask) !== NoFlags$1; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
- if (workInProgress.tag === LegacyHiddenComponent);
- else {
- if (current !== null) {
- var _prevState = current.memoizedState;
- var prevIsHidden = _prevState !== null;
+ var prevDebugFiber = getCurrentFiber();
+ var child = parentFiber.child;
- if (prevIsHidden !== nextIsHidden) {
- workInProgress.flags |= Visibility;
- }
- } else {
- // On initial mount, we only need a Visibility effect if the tree
- // is hidden.
- if (nextIsHidden) {
- workInProgress.flags |= Visibility;
- }
- }
- }
+ while (child !== null) {
+ var current = child.alternate;
+ reappearLayoutEffects(
+ finishedRoot,
+ current,
+ child,
+ childShouldIncludeWorkInProgressEffects
+ );
+ child = child.sibling;
+ }
+
+ setCurrentFiber(prevDebugFiber);
+}
- if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
- bubbleProperties(workInProgress);
- } else {
- // Don't bubble properties for hidden children unless we're rendering
- // at offscreen priority.
- if (
- includesSomeLane(renderLanes, OffscreenLane) && // Also don't bubble if the tree suspended
- (workInProgress.flags & DidCapture) === NoLanes
- ) {
- bubbleProperties(workInProgress); // Check if there was an insertion or update in the hidden subtree.
- // If so, we need to hide those nodes in the commit phase, so
- // schedule a visibility effect.
+function commitHookPassiveMountEffects(finishedWork, hookFlags) {
+ if (shouldProfile(finishedWork)) {
+ startPassiveEffectTimer();
- if (
- workInProgress.tag !== LegacyHiddenComponent &&
- workInProgress.subtreeFlags & (Placement | Update)
- ) {
- workInProgress.flags |= Visibility;
- }
- }
- }
+ try {
+ commitHookEffectListMount(hookFlags, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
- if (workInProgress.updateQueue !== null) {
- // Schedule an effect to attach Suspense retry listeners
- // TODO: Move to passive phase
- workInProgress.flags |= Update;
- }
+ recordPassiveEffectDuration(finishedWork);
+ } else {
+ try {
+ commitHookEffectListMount(hookFlags, finishedWork);
+ } catch (error) {
+ captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ }
+ }
+}
- {
- var _previousCache2 = null;
+function commitOffscreenPassiveMountEffects(current, finishedWork, instance) {
+ {
+ var previousCache = null;
- if (
- current !== null &&
- current.memoizedState !== null &&
- current.memoizedState.cachePool !== null
- ) {
- _previousCache2 = current.memoizedState.cachePool.pool;
- }
+ if (
+ current !== null &&
+ current.memoizedState !== null &&
+ current.memoizedState.cachePool !== null
+ ) {
+ previousCache = current.memoizedState.cachePool.pool;
+ }
- var _cache2 = null;
+ var nextCache = null;
- if (
- workInProgress.memoizedState !== null &&
- workInProgress.memoizedState.cachePool !== null
- ) {
- _cache2 = workInProgress.memoizedState.cachePool.pool;
- }
+ if (
+ finishedWork.memoizedState !== null &&
+ finishedWork.memoizedState.cachePool !== null
+ ) {
+ nextCache = finishedWork.memoizedState.cachePool.pool;
+ } // Retain/release the cache used for pending (suspended) nodes.
+ // Note that this is only reached in the non-suspended/visible case:
+ // when the content is suspended/hidden, the retain/release occurs
+ // via the parent Suspense component (see case above).
- if (_cache2 !== _previousCache2) {
- // Run passive effects to retain/release the cache.
- workInProgress.flags |= Passive$1;
- }
+ if (nextCache !== previousCache) {
+ if (nextCache != null) {
+ retainCache(nextCache);
}
- popTransition(workInProgress, current);
- return null;
+ if (previousCache != null) {
+ releaseCache(previousCache);
+ }
}
+ }
- case CacheComponent: {
- {
- var _previousCache3 = null;
+ if (enableTransitionTracing) {
+ // TODO: Pre-rendering should not be counted as part of a transition. We
+ // may add separate logs for pre-rendering, but it's not part of the
+ // primary metrics.
+ var offscreenState = finishedWork.memoizedState;
+ var queue = finishedWork.updateQueue;
+ var isHidden = offscreenState !== null;
- if (current !== null) {
- _previousCache3 = current.memoizedState.cache;
- }
+ if (queue !== null) {
+ if (isHidden) {
+ var transitions = queue.transitions;
- var _cache3 = workInProgress.memoizedState.cache;
+ if (transitions !== null) {
+ transitions.forEach(function (transition) {
+ // Add all the transitions saved in the update queue during
+ // the render phase (ie the transitions associated with this boundary)
+ // into the transitions set.
+ if (instance._transitions === null) {
+ instance._transitions = new Set();
+ }
- if (_cache3 !== _previousCache3) {
- // Run passive effects to retain/release the cache.
- workInProgress.flags |= Passive$1;
+ instance._transitions.add(transition);
+ });
}
- popCacheProvider(workInProgress);
- bubbleProperties(workInProgress);
- }
+ var markerInstances = queue.markerInstances;
- return null;
- }
+ if (markerInstances !== null) {
+ markerInstances.forEach(function (markerInstance) {
+ var markerTransitions = markerInstance.transitions; // There should only be a few tracing marker transitions because
+ // they should be only associated with the transition that
+ // caused them
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- var _instance3 = workInProgress.stateNode;
+ if (markerTransitions !== null) {
+ markerTransitions.forEach(function (transition) {
+ if (instance._transitions === null) {
+ instance._transitions = new Set();
+ } else if (instance._transitions.has(transition)) {
+ if (markerInstance.pendingBoundaries === null) {
+ markerInstance.pendingBoundaries = new Map();
+ }
- if (_instance3 !== null) {
- popMarkerInstance(workInProgress);
- }
+ if (instance._pendingMarkers === null) {
+ instance._pendingMarkers = new Set();
+ }
- bubbleProperties(workInProgress);
+ instance._pendingMarkers.add(markerInstance);
+ }
+ });
+ }
+ });
+ }
}
- return null;
+ finishedWork.updateQueue = null;
}
- }
- throw new Error(
- "Unknown unit of work tag (" +
- workInProgress.tag +
- "). This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
-}
-
-function unwindWork(current, workInProgress, renderLanes) {
- // Note: This intentionally doesn't check if we're hydrating because comparing
- // to the current tree provider fiber is just as fast and less error-prone.
- // Ideally we would have a special version of the work loop only
- // for hydration.
- popTreeContext(workInProgress);
+ commitTransitionProgress(finishedWork); // TODO: Refactor this into an if/else branch
- switch (workInProgress.tag) {
- case ClassComponent: {
- var Component = workInProgress.type;
+ if (!isHidden) {
+ instance._transitions = null;
+ instance._pendingMarkers = null;
+ }
+ }
+}
- if (isContextProvider(Component)) {
- popContext(workInProgress);
- }
+function commitCachePassiveMountEffect(current, finishedWork) {
+ {
+ var previousCache = null;
- var flags = workInProgress.flags;
+ if (finishedWork.alternate !== null) {
+ previousCache = finishedWork.alternate.memoizedState.cache;
+ }
- if (flags & ShouldCapture) {
- workInProgress.flags = (flags & ~ShouldCapture) | DidCapture;
+ var nextCache = finishedWork.memoizedState.cache; // Retain/release the cache. In theory the cache component
+ // could be "borrowing" a cache instance owned by some parent,
+ // in which case we could avoid retaining/releasing. But it
+ // is non-trivial to determine when that is the case, so we
+ // always retain/release.
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- transferActualDuration(workInProgress);
- }
+ if (nextCache !== previousCache) {
+ retainCache(nextCache);
- return workInProgress;
+ if (previousCache != null) {
+ releaseCache(previousCache);
}
-
- return null;
}
+ }
+}
- case HostRoot: {
- {
- popCacheProvider(workInProgress);
- }
+function commitTracingMarkerPassiveMountEffect(finishedWork) {
+ // Get the transitions that were initiatized during the render
+ // and add a start transition callback for each of them
+ // We will only call this on initial mount of the tracing marker
+ // only if there are no suspense children
+ var instance = finishedWork.stateNode;
- if (enableTransitionTracing) {
- popRootMarkerInstance(workInProgress);
- }
+ if (instance.transitions !== null && instance.pendingBoundaries === null) {
+ addMarkerCompleteCallbackToPendingTransition(
+ finishedWork.memoizedProps.name,
+ instance.transitions
+ );
+ instance.transitions = null;
+ instance.pendingBoundaries = null;
+ instance.aborts = null;
+ instance.name = null;
+ }
+}
- popRootTransition(workInProgress);
- popHostContainer(workInProgress);
- popTopLevelContextObject(workInProgress);
- resetWorkInProgressVersions();
- var _flags = workInProgress.flags;
+function commitPassiveMountEffects(
+ root,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+) {
+ setCurrentFiber(finishedWork);
+ commitPassiveMountOnFiber(
+ root,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
+ resetCurrentFiber();
+}
- if (
- (_flags & ShouldCapture) !== NoFlags$1 &&
- (_flags & DidCapture) === NoFlags$1
- ) {
- // There was an error during render that wasn't captured by a suspense
- // boundary. Do a second pass on the root to unmount the children.
- workInProgress.flags = (_flags & ~ShouldCapture) | DidCapture;
- return workInProgress;
- } // We unwound to the root without completing it. Exit.
+function recursivelyTraversePassiveMountEffects(
+ root,
+ parentFiber,
+ committedLanes,
+ committedTransitions
+) {
+ var prevDebugFiber = getCurrentFiber();
- return null;
- }
+ if (parentFiber.subtreeFlags & PassiveMask) {
+ var child = parentFiber.child;
- case HostHoistable:
- case HostSingleton:
- case HostComponent: {
- // TODO: popHydrationState
- popHostContext(workInProgress);
- return null;
+ while (child !== null) {
+ setCurrentFiber(child);
+ commitPassiveMountOnFiber(
+ root,
+ child,
+ committedLanes,
+ committedTransitions
+ );
+ child = child.sibling;
}
+ }
- case SuspenseComponent: {
- popSuspenseHandler(workInProgress);
- var suspenseState = workInProgress.memoizedState;
-
- if (suspenseState !== null && suspenseState.dehydrated !== null) {
- if (workInProgress.alternate === null) {
- throw new Error(
- "Threw in newly mounted dehydrated component. This is likely a bug in " +
- "React. Please file an issue."
- );
- }
-
- resetHydrationState();
- }
-
- var _flags2 = workInProgress.flags;
+ setCurrentFiber(prevDebugFiber);
+}
- if (_flags2 & ShouldCapture) {
- workInProgress.flags = (_flags2 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary.
+function commitPassiveMountOnFiber(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+) {
+ // When updating this function, also update reconnectPassiveEffects, which does
+ // most of the same things when an offscreen tree goes from hidden -> visible,
+ // or when toggling effects inside a hidden tree.
+ var flags = finishedWork.flags;
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- transferActualDuration(workInProgress);
- }
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
- return workInProgress;
+ if (flags & Passive$1) {
+ commitHookPassiveMountEffects(finishedWork, Passive | HasEffect);
}
- return null;
+ break;
}
- case SuspenseListComponent: {
- popSuspenseListContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been
- // caught by a nested boundary. If not, it should bubble through.
-
- return null;
- }
+ case HostRoot: {
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
- case HostPortal:
- popHostContainer(workInProgress);
- return null;
+ if (flags & Passive$1) {
+ {
+ var previousCache = null;
- case ContextProvider:
- var context = workInProgress.type._context;
- popProvider(context, workInProgress);
- return null;
+ if (finishedWork.alternate !== null) {
+ previousCache = finishedWork.alternate.memoizedState.cache;
+ }
- case OffscreenComponent:
- case LegacyHiddenComponent: {
- popSuspenseHandler(workInProgress);
- popHiddenContext(workInProgress);
- popTransition(workInProgress, current);
- var _flags3 = workInProgress.flags;
+ var nextCache = finishedWork.memoizedState.cache; // Retain/release the root cache.
+ // Note that on initial mount, previousCache and nextCache will be the same
+ // and this retain won't occur. To counter this, we instead retain the HostRoot's
+ // initial cache when creating the root itself (see createFiberRoot() in
+ // ReactFiberRoot.js). Subsequent updates that change the cache are reflected
+ // here, such that previous/next caches are retained correctly.
- if (_flags3 & ShouldCapture) {
- workInProgress.flags = (_flags3 & ~ShouldCapture) | DidCapture; // Captured a suspense effect. Re-render the boundary.
+ if (nextCache !== previousCache) {
+ retainCache(nextCache);
- if ((workInProgress.mode & ProfileMode) !== NoMode) {
- transferActualDuration(workInProgress);
+ if (previousCache != null) {
+ releaseCache(previousCache);
+ }
+ }
}
- return workInProgress;
- }
+ if (enableTransitionTracing) {
+ // Get the transitions that were initiatized during the render
+ // and add a start transition callback for each of them
+ var root = finishedWork.stateNode;
+ var incompleteTransitions = root.incompleteTransitions; // Initial render
- return null;
- }
+ if (committedTransitions !== null) {
+ committedTransitions.forEach(function (transition) {
+ addTransitionStartCallbackToPendingTransition(transition);
+ });
+ clearTransitionsForLanes(finishedRoot, committedLanes);
+ }
- case CacheComponent:
- {
- popCacheProvider(workInProgress);
- }
+ incompleteTransitions.forEach(function (markerInstance, transition) {
+ var pendingBoundaries = markerInstance.pendingBoundaries;
- return null;
+ if (pendingBoundaries === null || pendingBoundaries.size === 0) {
+ if (markerInstance.aborts === null) {
+ addTransitionCompleteCallbackToPendingTransition(transition);
+ }
- case TracingMarkerComponent:
- if (enableTransitionTracing) {
- if (workInProgress.stateNode !== null) {
- popMarkerInstance(workInProgress);
+ incompleteTransitions.delete(transition);
+ }
+ });
+ clearTransitionsForLanes(finishedRoot, committedLanes);
}
}
- return null;
-
- default:
- return null;
- }
-}
-
-function unwindInterruptedWork(current, interruptedWork, renderLanes) {
- // Note: This intentionally doesn't check if we're hydrating because comparing
- // to the current tree provider fiber is just as fast and less error-prone.
- // Ideally we would have a special version of the work loop only
- // for hydration.
- popTreeContext(interruptedWork);
+ break;
+ }
- switch (interruptedWork.tag) {
- case ClassComponent: {
- var childContextTypes = interruptedWork.type.childContextTypes;
+ case LegacyHiddenComponent: {
+ {
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
- if (childContextTypes !== null && childContextTypes !== undefined) {
- popContext(interruptedWork);
+ if (flags & Passive$1) {
+ var current = finishedWork.alternate;
+ var instance = finishedWork.stateNode;
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
+ }
}
break;
}
- case HostRoot: {
- {
- popCacheProvider(interruptedWork);
+ case OffscreenComponent: {
+ // TODO: Pass `current` as argument to this function
+ var _instance3 = finishedWork.stateNode;
+ var nextState = finishedWork.memoizedState;
+ var isHidden = nextState !== null;
+
+ if (isHidden) {
+ if (_instance3._visibility & OffscreenPassiveEffectsConnected) {
+ // The effects are currently connected. Update them.
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
+ } else {
+ if (finishedWork.mode & ConcurrentMode) {
+ // The effects are currently disconnected. Since the tree is hidden,
+ // don't connect them. This also applies to the initial render.
+ {
+ // "Atomic" effects are ones that need to fire on every commit,
+ // even during pre-rendering. An example is updating the reference
+ // count on cache instances.
+ recursivelyTraverseAtomicPassiveEffects(
+ finishedRoot,
+ finishedWork
+ );
+ }
+ } else {
+ // Legacy Mode: Fire the effects even if the tree is hidden.
+ _instance3._visibility |= OffscreenPassiveEffectsConnected;
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
+ }
+ }
+ } else {
+ // Tree is visible
+ if (_instance3._visibility & OffscreenPassiveEffectsConnected) {
+ // The effects are currently connected. Update them.
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
+ } else {
+ // The effects are currently disconnected. Reconnect them, while also
+ // firing effects inside newly mounted trees. This also applies to
+ // the initial render.
+ _instance3._visibility |= OffscreenPassiveEffectsConnected;
+ var includeWorkInProgressEffects =
+ (finishedWork.subtreeFlags & PassiveMask) !== NoFlags$1;
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
+ }
}
- if (enableTransitionTracing) {
- popRootMarkerInstance(interruptedWork);
+ if (flags & Passive$1) {
+ var _current = finishedWork.alternate;
+ commitOffscreenPassiveMountEffects(_current, finishedWork, _instance3);
}
- popRootTransition(interruptedWork);
- popHostContainer(interruptedWork);
- popTopLevelContextObject(interruptedWork);
- resetWorkInProgressVersions();
- break;
- }
-
- case HostHoistable:
- case HostSingleton:
- case HostComponent: {
- popHostContext(interruptedWork);
break;
}
- case HostPortal:
- popHostContainer(interruptedWork);
- break;
-
- case SuspenseComponent:
- popSuspenseHandler(interruptedWork);
- break;
-
- case SuspenseListComponent:
- popSuspenseListContext(interruptedWork);
- break;
-
- case ContextProvider:
- var context = interruptedWork.type._context;
- popProvider(context, interruptedWork);
- break;
-
- case OffscreenComponent:
- case LegacyHiddenComponent:
- popSuspenseHandler(interruptedWork);
- popHiddenContext(interruptedWork);
- popTransition(interruptedWork, current);
- break;
+ case CacheComponent: {
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
- case CacheComponent:
- {
- popCacheProvider(interruptedWork);
+ if (flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var _current2 = finishedWork.alternate;
+ commitCachePassiveMountEffect(_current2, finishedWork);
}
break;
+ }
- case TracingMarkerComponent:
+ case TracingMarkerComponent: {
if (enableTransitionTracing) {
- var instance = interruptedWork.stateNode;
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
- if (instance !== null) {
- popMarkerInstance(interruptedWork);
+ if (flags & Passive$1) {
+ commitTracingMarkerPassiveMountEffect(finishedWork);
}
- }
+ break;
+ } // Intentional fallthrough to next branch
+ }
+ // eslint-disable-next-line-no-fallthrough
+
+ default: {
+ recursivelyTraversePassiveMountEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+ );
break;
+ }
}
}
-var didWarnAboutUndefinedSnapshotBeforeUpdate = null;
-
-{
- didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
-} // Used during the commit phase to track the state of the Offscreen component stack.
-// Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
-
-var offscreenSubtreeIsHidden = false;
-var offscreenSubtreeWasHidden = false;
-var PossiblyWeakSet = typeof WeakSet === "function" ? WeakSet : Set;
-var nextEffect = null; // Used for Profiling builds to track updaters.
-
-var inProgressLanes = null;
-var inProgressRoot = null;
+function recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ parentFiber,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+) {
+ // This function visits both newly finished work and nodes that were re-used
+ // from a previously committed tree. We cannot check non-static flags if the
+ // node was reused.
+ var childShouldIncludeWorkInProgressEffects =
+ includeWorkInProgressEffects &&
+ (parentFiber.subtreeFlags & PassiveMask) !== NoFlags$1; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
-function shouldProfile(current) {
- return (
- (current.mode & ProfileMode) !== NoMode &&
- (getExecutionContext() & CommitContext) !== NoContext
- );
-}
+ var prevDebugFiber = getCurrentFiber();
+ var child = parentFiber.child;
-function reportUncaughtErrorInDEV(error) {
- // Wrapping each small part of the commit phase into a guarded
- // callback is a bit too slow (https://github.com/facebook/react/pull/21666).
- // But we rely on it to surface errors to DEV tools like overlays
- // (https://github.com/facebook/react/issues/21712).
- // As a compromise, rethrow only caught errors in a guard.
- {
- invokeGuardedCallback(null, function () {
- throw error;
- });
- clearCaughtError();
+ while (child !== null) {
+ reconnectPassiveEffects(
+ finishedRoot,
+ child,
+ committedLanes,
+ committedTransitions,
+ childShouldIncludeWorkInProgressEffects
+ );
+ child = child.sibling;
}
-}
-
-function callComponentWillUnmountWithTimer(current, instance) {
- instance.props = current.memoizedProps;
- instance.state = current.memoizedState;
- if (shouldProfile(current)) {
- try {
- startLayoutEffectTimer();
- instance.componentWillUnmount();
- } finally {
- recordLayoutEffectDuration(current);
- }
- } else {
- instance.componentWillUnmount();
- }
-} // Capture errors so they don't interrupt unmounting.
+ setCurrentFiber(prevDebugFiber);
+}
-function safelyCallComponentWillUnmount(
- current,
- nearestMountedAncestor,
- instance
+function reconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions, // This function visits both newly finished work and nodes that were re-used
+ // from a previously committed tree. We cannot check non-static flags if the
+ // node was reused.
+ includeWorkInProgressEffects
) {
- try {
- callComponentWillUnmountWithTimer(current, instance);
- } catch (error) {
- captureCommitPhaseError(current, nearestMountedAncestor, error);
- }
-} // Capture errors so they don't interrupt mounting.
-
-function safelyAttachRef(current, nearestMountedAncestor) {
- try {
- commitAttachRef(current);
- } catch (error) {
- captureCommitPhaseError(current, nearestMountedAncestor, error);
- }
-}
+ var flags = finishedWork.flags;
-function safelyDetachRef(current, nearestMountedAncestor) {
- var ref = current.ref;
- var refCleanup = current.refCleanup;
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ ); // TODO: Check for PassiveStatic flag
- if (ref !== null) {
- if (typeof refCleanup === "function") {
- try {
- if (shouldProfile(current)) {
- try {
- startLayoutEffectTimer();
- refCleanup();
- } finally {
- recordLayoutEffectDuration(current);
- }
- } else {
- refCleanup();
- }
- } catch (error) {
- captureCommitPhaseError(current, nearestMountedAncestor, error);
- } finally {
- // `refCleanup` has been called. Nullify all references to it to prevent double invocation.
- current.refCleanup = null;
- var finishedWork = current.alternate;
+ commitHookPassiveMountEffects(finishedWork, Passive);
+ break;
+ }
+ // Unlike commitPassiveMountOnFiber, we don't need to handle HostRoot
+ // because this function only visits nodes that are inside an
+ // Offscreen fiber.
+ // case HostRoot: {
+ // ...
+ // }
- if (finishedWork != null) {
- finishedWork.refCleanup = null;
- }
- }
- } else if (typeof ref === "function") {
- var retVal;
+ case LegacyHiddenComponent: {
+ {
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
- try {
- if (shouldProfile(current)) {
- try {
- startLayoutEffectTimer();
- retVal = ref(null);
- } finally {
- recordLayoutEffectDuration(current);
- }
- } else {
- retVal = ref(null);
+ if (includeWorkInProgressEffects && flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var current = finishedWork.alternate;
+ var instance = finishedWork.stateNode;
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
- } catch (error) {
- captureCommitPhaseError(current, nearestMountedAncestor, error);
}
- {
- if (typeof retVal === "function") {
- error(
- "Unexpected return value from a callback ref in %s. " +
- "A callback ref should not return a function.",
- getComponentNameFromFiber(current)
- );
- }
- }
- } else {
- // $FlowFixMe unable to narrow type to RefObject
- ref.current = null;
+ break;
}
- }
-}
-
-function safelyCallDestroy(current, nearestMountedAncestor, destroy) {
- try {
- destroy();
- } catch (error) {
- captureCommitPhaseError(current, nearestMountedAncestor, error);
- }
-}
-
-var focusedInstanceHandle = null;
-var shouldFireAfterActiveInstanceBlur = false;
-function commitBeforeMutationEffects(root, firstChild) {
- focusedInstanceHandle = prepareForCommit();
- nextEffect = firstChild;
- commitBeforeMutationEffects_begin(); // We no longer need to track the active instance fiber
-
- var shouldFire = shouldFireAfterActiveInstanceBlur;
- shouldFireAfterActiveInstanceBlur = false;
- focusedInstanceHandle = null;
- return shouldFire;
-}
-
-function commitBeforeMutationEffects_begin() {
- while (nextEffect !== null) {
- var fiber = nextEffect; // This phase is only used for beforeActiveInstanceBlur.
- // Let's skip the whole loop if it's off.
- {
- // TODO: Should wrap this in flags check, too, as optimization
- var deletions = fiber.deletions;
+ case OffscreenComponent: {
+ var _instance4 = finishedWork.stateNode;
+ var nextState = finishedWork.memoizedState;
+ var isHidden = nextState !== null;
- if (deletions !== null) {
- for (var i = 0; i < deletions.length; i++) {
- var deletion = deletions[i];
- commitBeforeMutationEffectsDeletion(deletion);
+ if (isHidden) {
+ if (_instance4._visibility & OffscreenPassiveEffectsConnected) {
+ // The effects are currently connected. Update them.
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
+ } else {
+ if (finishedWork.mode & ConcurrentMode) {
+ // The effects are currently disconnected. Since the tree is hidden,
+ // don't connect them. This also applies to the initial render.
+ {
+ // "Atomic" effects are ones that need to fire on every commit,
+ // even during pre-rendering. An example is updating the reference
+ // count on cache instances.
+ recursivelyTraverseAtomicPassiveEffects(
+ finishedRoot,
+ finishedWork
+ );
+ }
+ } else {
+ // Legacy Mode: Fire the effects even if the tree is hidden.
+ _instance4._visibility |= OffscreenPassiveEffectsConnected;
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
+ }
}
+ } else {
+ // Tree is visible
+ // Since we're already inside a reconnecting tree, it doesn't matter
+ // whether the effects are currently connected. In either case, we'll
+ // continue traversing the tree and firing all the effects.
+ //
+ // We do need to set the "connected" flag on the instance, though.
+ _instance4._visibility |= OffscreenPassiveEffectsConnected;
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
}
- }
-
- var child = fiber.child;
-
- if (
- (fiber.subtreeFlags & BeforeMutationMask) !== NoFlags$1 &&
- child !== null
- ) {
- child.return = fiber;
- nextEffect = child;
- } else {
- commitBeforeMutationEffects_complete();
- }
- }
-}
-
-function commitBeforeMutationEffects_complete() {
- while (nextEffect !== null) {
- var fiber = nextEffect;
- setCurrentFiber(fiber);
-
- try {
- commitBeforeMutationEffectsOnFiber(fiber);
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
- }
- resetCurrentFiber();
- var sibling = fiber.sibling;
+ if (includeWorkInProgressEffects && flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var _current3 = finishedWork.alternate;
+ commitOffscreenPassiveMountEffects(_current3, finishedWork, _instance4);
+ }
- if (sibling !== null) {
- sibling.return = fiber.return;
- nextEffect = sibling;
- return;
+ break;
}
- nextEffect = fiber.return;
- }
-}
-
-function commitBeforeMutationEffectsOnFiber(finishedWork) {
- var current = finishedWork.alternate;
- var flags = finishedWork.flags;
+ case CacheComponent: {
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
- {
- if (!shouldFireAfterActiveInstanceBlur && focusedInstanceHandle !== null) {
- // Check to see if the focused element was inside of a hidden (Suspense) subtree.
- // TODO: Move this out of the hot path using a dedicated effect tag.
- if (
- finishedWork.tag === SuspenseComponent &&
- isSuspenseBoundaryBeingHidden(current, finishedWork) && // $FlowFixMe[incompatible-call] found when upgrading Flow
- doesFiberContain(finishedWork, focusedInstanceHandle)
- ) {
- shouldFireAfterActiveInstanceBlur = true;
- beforeActiveInstanceBlur(finishedWork);
+ if (includeWorkInProgressEffects && flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var _current4 = finishedWork.alternate;
+ commitCachePassiveMountEffect(_current4, finishedWork);
}
+
+ break;
}
- }
- if ((flags & Snapshot) !== NoFlags$1) {
- setCurrentFiber(finishedWork);
- }
+ case TracingMarkerComponent: {
+ if (enableTransitionTracing) {
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
- switch (finishedWork.tag) {
- case FunctionComponent: {
- {
- if ((flags & Update) !== NoFlags$1) {
- commitUseEffectEventMount(finishedWork);
+ if (includeWorkInProgressEffects && flags & Passive$1) {
+ commitTracingMarkerPassiveMountEffect(finishedWork);
}
- }
- break;
+ break;
+ } // Intentional fallthrough to next branch
}
+ // eslint-disable-next-line-no-fallthrough
- case ForwardRef:
- case SimpleMemoComponent: {
+ default: {
+ recursivelyTraverseReconnectPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions,
+ includeWorkInProgressEffects
+ );
break;
}
+ }
+}
- case ClassComponent: {
- if ((flags & Snapshot) !== NoFlags$1) {
- if (current !== null) {
- var prevProps = current.memoizedProps;
- var prevState = current.memoizedState;
- var instance = finishedWork.stateNode; // We could update instance props and state here,
- // but instead we rely on them being set during last render.
- // TODO: revisit this when we implement resuming.
-
- {
- if (
- finishedWork.type === finishedWork.elementType &&
- !didWarnAboutReassigningProps
- ) {
- if (instance.props !== finishedWork.memoizedProps) {
- error(
- "Expected %s props to match memoized props before " +
- "getSnapshotBeforeUpdate. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.props`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
- }
-
- if (instance.state !== finishedWork.memoizedState) {
- error(
- "Expected %s state to match memoized state before " +
- "getSnapshotBeforeUpdate. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.state`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
- }
- }
- }
+function recursivelyTraverseAtomicPassiveEffects(
+ finishedRoot,
+ parentFiber,
+ committedLanes,
+ committedTransitions
+) {
+ // "Atomic" effects are ones that need to fire on every commit, even during
+ // pre-rendering. We call this function when traversing a hidden tree whose
+ // regular effects are currently disconnected.
+ var prevDebugFiber = getCurrentFiber(); // TODO: Add special flag for atomic effects
- var snapshot = instance.getSnapshotBeforeUpdate(
- finishedWork.elementType === finishedWork.type
- ? prevProps
- : resolveDefaultProps(finishedWork.type, prevProps),
- prevState
- );
+ if (parentFiber.subtreeFlags & PassiveMask) {
+ var child = parentFiber.child;
- {
- var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate;
+ while (child !== null) {
+ setCurrentFiber(child);
+ commitAtomicPassiveEffects(finishedRoot, child);
+ child = child.sibling;
+ }
+ }
- if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
- didWarnSet.add(finishedWork.type);
+ setCurrentFiber(prevDebugFiber);
+}
- error(
- "%s.getSnapshotBeforeUpdate(): A snapshot value (or null) " +
- "must be returned. You have returned undefined.",
- getComponentNameFromFiber(finishedWork)
- );
- }
- }
+function commitAtomicPassiveEffects(
+ finishedRoot,
+ finishedWork,
+ committedLanes,
+ committedTransitions
+) {
+ // "Atomic" effects are ones that need to fire on every commit, even during
+ // pre-rendering. We call this function when traversing a hidden tree whose
+ // regular effects are currently disconnected.
+ var flags = finishedWork.flags;
- instance.__reactInternalSnapshotBeforeUpdate = snapshot;
- }
+ switch (finishedWork.tag) {
+ case OffscreenComponent: {
+ recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
+
+ if (flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var current = finishedWork.alternate;
+ var instance = finishedWork.stateNode;
+ commitOffscreenPassiveMountEffects(current, finishedWork, instance);
}
break;
}
- case HostRoot: {
- if ((flags & Snapshot) !== NoFlags$1) {
- {
- var root = finishedWork.stateNode;
- clearContainer(root.containerInfo);
- }
+ case CacheComponent: {
+ recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
+
+ if (flags & Passive$1) {
+ // TODO: Pass `current` as argument to this function
+ var _current5 = finishedWork.alternate;
+ commitCachePassiveMountEffect(_current5, finishedWork);
}
break;
}
-
- case HostComponent:
- case HostHoistable:
- case HostSingleton:
- case HostText:
- case HostPortal:
- case IncompleteClassComponent:
- // Nothing to do for these component types
- break;
+ // eslint-disable-next-line-no-fallthrough
default: {
- if ((flags & Snapshot) !== NoFlags$1) {
- throw new Error(
- "This unit of work tag should not have side-effects. This error is " +
- "likely caused by a bug in React. Please file an issue."
- );
- }
+ recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
+ break;
}
}
+}
- if ((flags & Snapshot) !== NoFlags$1) {
- resetCurrentFiber();
- }
+function commitPassiveUnmountEffects(finishedWork) {
+ setCurrentFiber(finishedWork);
+ commitPassiveUnmountOnFiber(finishedWork);
+ resetCurrentFiber();
}
-function commitBeforeMutationEffectsDeletion(deletion) {
- {
- // TODO (effects) It would be nice to avoid calling doesFiberContain()
- // Maybe we can repurpose one of the subtreeFlags positions for this instead?
- // Use it to store which part of the tree the focused instance is in?
- // This assumes we can safely determine that instance during the "render" phase.
- if (doesFiberContain(deletion, focusedInstanceHandle)) {
- shouldFireAfterActiveInstanceBlur = true;
- beforeActiveInstanceBlur(deletion);
+function detachAlternateSiblings(parentFiber) {
+ // A fiber was deleted from this parent fiber, but it's still part of the
+ // previous (alternate) parent fiber's list of children. Because children
+ // are a linked list, an earlier sibling that's still alive will be
+ // connected to the deleted fiber via its `alternate`:
+ //
+ // live fiber --alternate--> previous live fiber --sibling--> deleted
+ // fiber
+ //
+ // We can't disconnect `alternate` on nodes that haven't been deleted yet,
+ // but we can disconnect the `sibling` and `child` pointers.
+ var previousFiber = parentFiber.alternate;
+
+ if (previousFiber !== null) {
+ var detachedChild = previousFiber.child;
+
+ if (detachedChild !== null) {
+ previousFiber.child = null;
+
+ do {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ var detachedSibling = detachedChild.sibling; // $FlowFixMe[incompatible-use] found when upgrading Flow
+
+ detachedChild.sibling = null;
+ detachedChild = detachedSibling;
+ } while (detachedChild !== null);
}
}
}
-function commitHookEffectListUnmount(
- flags,
+function commitHookPassiveUnmountEffects(
finishedWork,
- nearestMountedAncestor
+ nearestMountedAncestor,
+ hookFlags
) {
- var updateQueue = finishedWork.updateQueue;
- var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
-
- if (lastEffect !== null) {
- var firstEffect = lastEffect.next;
- var effect = firstEffect;
+ if (shouldProfile(finishedWork)) {
+ startPassiveEffectTimer();
+ commitHookEffectListUnmount(
+ hookFlags,
+ finishedWork,
+ nearestMountedAncestor
+ );
+ recordPassiveEffectDuration(finishedWork);
+ } else {
+ commitHookEffectListUnmount(
+ hookFlags,
+ finishedWork,
+ nearestMountedAncestor
+ );
+ }
+}
- do {
- if ((effect.tag & flags) === flags) {
- // Unmount
- var destroy = effect.destroy;
- effect.destroy = undefined;
+function recursivelyTraversePassiveUnmountEffects(parentFiber) {
+ // Deletions effects can be scheduled on any fiber type. They need to happen
+ // before the children effects have fired.
+ var deletions = parentFiber.deletions;
- if (destroy !== undefined) {
- if (enableSchedulingProfiler) {
- if ((flags & Passive) !== NoFlags) {
- markComponentPassiveEffectUnmountStarted(finishedWork);
- } else if ((flags & Layout) !== NoFlags) {
- markComponentLayoutEffectUnmountStarted(finishedWork);
- }
- }
+ if ((parentFiber.flags & ChildDeletion) !== NoFlags$1) {
+ if (deletions !== null) {
+ for (var i = 0; i < deletions.length; i++) {
+ var childToDelete = deletions[i]; // TODO: Convert this to use recursion
- {
- if ((flags & Insertion) !== NoFlags) {
- setIsRunningInsertionEffect(true);
- }
- }
+ nextEffect = childToDelete;
+ commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
+ childToDelete,
+ parentFiber
+ );
+ }
+ }
- safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
+ detachAlternateSiblings(parentFiber);
+ }
- {
- if ((flags & Insertion) !== NoFlags) {
- setIsRunningInsertionEffect(false);
- }
- }
+ var prevDebugFiber = getCurrentFiber(); // TODO: Split PassiveMask into separate masks for mount and unmount?
- if (enableSchedulingProfiler) {
- if ((flags & Passive) !== NoFlags) {
- markComponentPassiveEffectUnmountStopped();
- } else if ((flags & Layout) !== NoFlags) {
- markComponentLayoutEffectUnmountStopped();
- }
- }
- }
- }
+ if (parentFiber.subtreeFlags & PassiveMask) {
+ var child = parentFiber.child;
- effect = effect.next;
- } while (effect !== firstEffect);
+ while (child !== null) {
+ setCurrentFiber(child);
+ commitPassiveUnmountOnFiber(child);
+ child = child.sibling;
+ }
}
+
+ setCurrentFiber(prevDebugFiber);
}
-function commitHookEffectListMount(flags, finishedWork) {
- var updateQueue = finishedWork.updateQueue;
- var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
+function commitPassiveUnmountOnFiber(finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ recursivelyTraversePassiveUnmountEffects(finishedWork);
- if (lastEffect !== null) {
- var firstEffect = lastEffect.next;
- var effect = firstEffect;
+ if (finishedWork.flags & Passive$1) {
+ commitHookPassiveUnmountEffects(
+ finishedWork,
+ finishedWork.return,
+ Passive | HasEffect
+ );
+ }
- do {
- if ((effect.tag & flags) === flags) {
- if (enableSchedulingProfiler) {
- if ((flags & Passive) !== NoFlags) {
- markComponentPassiveEffectMountStarted(finishedWork);
- } else if ((flags & Layout) !== NoFlags) {
- markComponentLayoutEffectMountStarted(finishedWork);
- }
- } // Mount
+ break;
+ }
- var create = effect.create;
+ case OffscreenComponent: {
+ var instance = finishedWork.stateNode;
+ var nextState = finishedWork.memoizedState;
+ var isHidden = nextState !== null;
- {
- if ((flags & Insertion) !== NoFlags) {
- setIsRunningInsertionEffect(true);
- }
- }
+ if (
+ isHidden &&
+ instance._visibility & OffscreenPassiveEffectsConnected && // For backwards compatibility, don't unmount when a tree suspends. In
+ // the future we may change this to unmount after a delay.
+ (finishedWork.return === null ||
+ finishedWork.return.tag !== SuspenseComponent)
+ ) {
+ // The effects are currently connected. Disconnect them.
+ // TODO: Add option or heuristic to delay before disconnecting the
+ // effects. Then if the tree reappears before the delay has elapsed, we
+ // can skip toggling the effects entirely.
+ instance._visibility &= ~OffscreenPassiveEffectsConnected;
+ recursivelyTraverseDisconnectPassiveEffects(finishedWork);
+ } else {
+ recursivelyTraversePassiveUnmountEffects(finishedWork);
+ }
- effect.destroy = create();
+ break;
+ }
- {
- if ((flags & Insertion) !== NoFlags) {
- setIsRunningInsertionEffect(false);
- }
- }
+ default: {
+ recursivelyTraversePassiveUnmountEffects(finishedWork);
+ break;
+ }
+ }
+}
- if (enableSchedulingProfiler) {
- if ((flags & Passive) !== NoFlags) {
- markComponentPassiveEffectMountStopped();
- } else if ((flags & Layout) !== NoFlags) {
- markComponentLayoutEffectMountStopped();
- }
- }
+function recursivelyTraverseDisconnectPassiveEffects(parentFiber) {
+ // Deletions effects can be scheduled on any fiber type. They need to happen
+ // before the children effects have fired.
+ var deletions = parentFiber.deletions;
- {
- var destroy = effect.destroy;
+ if ((parentFiber.flags & ChildDeletion) !== NoFlags$1) {
+ if (deletions !== null) {
+ for (var i = 0; i < deletions.length; i++) {
+ var childToDelete = deletions[i]; // TODO: Convert this to use recursion
- if (destroy !== undefined && typeof destroy !== "function") {
- var hookName = void 0;
+ nextEffect = childToDelete;
+ commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
+ childToDelete,
+ parentFiber
+ );
+ }
+ }
- if ((effect.tag & Layout) !== NoFlags$1) {
- hookName = "useLayoutEffect";
- } else if ((effect.tag & Insertion) !== NoFlags$1) {
- hookName = "useInsertionEffect";
- } else {
- hookName = "useEffect";
- }
+ detachAlternateSiblings(parentFiber);
+ }
- var addendum = void 0;
+ var prevDebugFiber = getCurrentFiber(); // TODO: Check PassiveStatic flag
- if (destroy === null) {
- addendum =
- " You returned null. If your effect does not require clean " +
- "up, return undefined (or nothing).";
- } else if (typeof destroy.then === "function") {
- addendum =
- "\n\nIt looks like you wrote " +
- hookName +
- "(async () => ...) or returned a Promise. " +
- "Instead, write the async function inside your effect " +
- "and call it immediately:\n\n" +
- hookName +
- "(() => {\n" +
- " async function fetchData() {\n" +
- " // You can await here\n" +
- " const response = await MyAPI.getData(someId);\n" +
- " // ...\n" +
- " }\n" +
- " fetchData();\n" +
- "}, [someId]); // Or [] if effect doesn't need props or state\n\n" +
- "Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching";
- } else {
- addendum = " You returned: " + destroy;
- }
+ var child = parentFiber.child;
- error(
- "%s must not return anything besides a function, " +
- "which is used for clean-up.%s",
- hookName,
- addendum
- );
- }
- }
+ while (child !== null) {
+ setCurrentFiber(child);
+ disconnectPassiveEffect(child);
+ child = child.sibling;
+ }
+
+ setCurrentFiber(prevDebugFiber);
+}
+
+function disconnectPassiveEffect(finishedWork) {
+ switch (finishedWork.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ // TODO: Check PassiveStatic flag
+ commitHookPassiveUnmountEffects(
+ finishedWork,
+ finishedWork.return,
+ Passive
+ ); // When disconnecting passive effects, we fire the effects in the same
+ // order as during a deletiong: parent before child
+
+ recursivelyTraverseDisconnectPassiveEffects(finishedWork);
+ break;
+ }
+
+ case OffscreenComponent: {
+ var instance = finishedWork.stateNode;
+
+ if (instance._visibility & OffscreenPassiveEffectsConnected) {
+ instance._visibility &= ~OffscreenPassiveEffectsConnected;
+ recursivelyTraverseDisconnectPassiveEffects(finishedWork);
}
- effect = effect.next;
- } while (effect !== firstEffect);
+ break;
+ }
+
+ default: {
+ recursivelyTraverseDisconnectPassiveEffects(finishedWork);
+ break;
+ }
}
}
-function commitUseEffectEventMount(finishedWork) {
- var updateQueue = finishedWork.updateQueue;
- var eventPayloads = updateQueue !== null ? updateQueue.events : null;
+function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
+ deletedSubtreeRoot,
+ nearestMountedAncestor
+) {
+ while (nextEffect !== null) {
+ var fiber = nextEffect; // Deletion effects fire in parent -> child order
+ // TODO: Check if fiber has a PassiveStatic flag
- if (eventPayloads !== null) {
- for (var ii = 0; ii < eventPayloads.length; ii++) {
- var _eventPayloads$ii = eventPayloads[ii],
- ref = _eventPayloads$ii.ref,
- nextImpl = _eventPayloads$ii.nextImpl;
- ref.impl = nextImpl;
+ setCurrentFiber(fiber);
+ commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
+ resetCurrentFiber();
+ var child = fiber.child; // TODO: Only traverse subtree if it has a PassiveStatic flag.
+
+ if (child !== null) {
+ child.return = fiber;
+ nextEffect = child;
+ } else {
+ commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
+ deletedSubtreeRoot
+ );
}
}
}
-function commitPassiveEffectDurations(finishedRoot, finishedWork) {
- if (getExecutionContext() & CommitContext) {
- // Only Profilers with work in their subtree will have an Update effect scheduled.
- if ((finishedWork.flags & Update) !== NoFlags$1) {
- switch (finishedWork.tag) {
- case Profiler: {
- var passiveEffectDuration =
- finishedWork.stateNode.passiveEffectDuration;
- var _finishedWork$memoize = finishedWork.memoizedProps,
- id = _finishedWork$memoize.id,
- onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase.
- // It does not get reset until the start of the next commit phase.
+function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
+ deletedSubtreeRoot
+) {
+ while (nextEffect !== null) {
+ var fiber = nextEffect;
+ var sibling = fiber.sibling;
+ var returnFiber = fiber.return; // Recursively traverse the entire deleted tree and clean up fiber fields.
+ // This is more aggressive than ideal, and the long term goal is to only
+ // have to detach the deleted tree at the root.
- var commitTime = getCommitTime();
- var phase = finishedWork.alternate === null ? "mount" : "update";
+ detachFiberAfterEffects(fiber);
- {
- if (isCurrentUpdateNested()) {
- phase = "nested-update";
- }
- }
+ if (fiber === deletedSubtreeRoot) {
+ nextEffect = null;
+ return;
+ }
- if (typeof onPostCommit === "function") {
- onPostCommit(id, phase, passiveEffectDuration, commitTime);
- } // Bubble times to the next nearest ancestor Profiler.
- // After we process that Profiler, we'll bubble further up.
+ if (sibling !== null) {
+ sibling.return = returnFiber;
+ nextEffect = sibling;
+ return;
+ }
- var parentFiber = finishedWork.return;
+ nextEffect = returnFiber;
+ }
+}
- outer: while (parentFiber !== null) {
- switch (parentFiber.tag) {
- case HostRoot:
- var root = parentFiber.stateNode;
- root.passiveEffectDuration += passiveEffectDuration;
- break outer;
+function commitPassiveUnmountInsideDeletedTreeOnFiber(
+ current,
+ nearestMountedAncestor
+) {
+ switch (current.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ commitHookPassiveUnmountEffects(current, nearestMountedAncestor, Passive);
+ break;
+ }
+ // TODO: run passive unmount effects when unmounting a root.
+ // Because passive unmount effects are not currently run,
+ // the cache instance owned by the root will never be freed.
+ // When effects are run, the cache should be freed here:
+ // case HostRoot: {
+ // if (enableCache) {
+ // const cache = current.memoizedState.cache;
+ // releaseCache(cache);
+ // }
+ // break;
+ // }
- case Profiler:
- var parentStateNode = parentFiber.stateNode;
- parentStateNode.passiveEffectDuration += passiveEffectDuration;
- break outer;
- }
+ case LegacyHiddenComponent:
+ case OffscreenComponent: {
+ {
+ if (
+ current.memoizedState !== null &&
+ current.memoizedState.cachePool !== null
+ ) {
+ var cache = current.memoizedState.cachePool.pool; // Retain/release the cache used for pending (suspended) nodes.
+ // Note that this is only reached in the non-suspended/visible case:
+ // when the content is suspended/hidden, the retain/release occurs
+ // via the parent Suspense component (see case above).
- parentFiber = parentFiber.return;
+ if (cache != null) {
+ retainCache(cache);
}
-
- break;
}
}
- }
- }
-}
-function commitHookLayoutEffects(finishedWork, hookFlags) {
- // At this point layout effects have already been destroyed (during mutation phase).
- // This is done to prevent sibling component effects from interfering with each other,
- // e.g. a destroy function in one component should never override a ref set
- // by a create function in another component during the same commit.
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- commitHookEffectListMount(hookFlags, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ break;
}
- recordLayoutEffectDuration(finishedWork);
- } else {
- try {
- commitHookEffectListMount(hookFlags, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
-}
+ case SuspenseComponent: {
+ if (enableTransitionTracing) {
+ // We need to mark this fiber's parents as deleted
+ var offscreenFiber = current.child;
+ var instance = offscreenFiber.stateNode;
+ var transitions = instance._transitions;
-function commitClassLayoutLifecycles(finishedWork, current) {
- var instance = finishedWork.stateNode;
+ if (transitions !== null) {
+ var abortReason = {
+ reason: "suspense",
+ name: current.memoizedProps.unstable_name || null
+ };
- if (current === null) {
- // We could update instance props and state here,
- // but instead we rely on them being set during last render.
- // TODO: revisit this when we implement resuming.
- {
- if (
- finishedWork.type === finishedWork.elementType &&
- !didWarnAboutReassigningProps
- ) {
- if (instance.props !== finishedWork.memoizedProps) {
- error(
- "Expected %s props to match memoized props before " +
- "componentDidMount. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.props`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
- }
+ if (
+ current.memoizedState === null ||
+ current.memoizedState.dehydrated === null
+ ) {
+ abortParentMarkerTransitionsForDeletedFiber(
+ offscreenFiber,
+ abortReason,
+ transitions,
+ instance,
+ true
+ );
- if (instance.state !== finishedWork.memoizedState) {
- error(
- "Expected %s state to match memoized state before " +
- "componentDidMount. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.state`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
+ if (nearestMountedAncestor !== null) {
+ abortParentMarkerTransitionsForDeletedFiber(
+ nearestMountedAncestor,
+ abortReason,
+ transitions,
+ instance,
+ false
+ );
+ }
+ }
}
}
+
+ break;
}
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- instance.componentDidMount();
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ case CacheComponent: {
+ {
+ var _cache = current.memoizedState.cache;
+ releaseCache(_cache);
}
- recordLayoutEffectDuration(finishedWork);
- } else {
- try {
- instance.componentDidMount();
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
+ break;
}
- } else {
- var prevProps =
- finishedWork.elementType === finishedWork.type
- ? current.memoizedProps
- : resolveDefaultProps(finishedWork.type, current.memoizedProps);
- var prevState = current.memoizedState; // We could update instance props and state here,
- // but instead we rely on them being set during last render.
- // TODO: revisit this when we implement resuming.
- {
- if (
- finishedWork.type === finishedWork.elementType &&
- !didWarnAboutReassigningProps
- ) {
- if (instance.props !== finishedWork.memoizedProps) {
- error(
- "Expected %s props to match memoized props before " +
- "componentDidUpdate. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.props`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
- }
+ case TracingMarkerComponent: {
+ if (enableTransitionTracing) {
+ // We need to mark this fiber's parents as deleted
+ var _instance5 = current.stateNode;
+ var _transitions = _instance5.transitions;
- if (instance.state !== finishedWork.memoizedState) {
- error(
- "Expected %s state to match memoized state before " +
- "componentDidUpdate. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.state`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
+ if (_transitions !== null) {
+ var _abortReason = {
+ reason: "marker",
+ name: current.memoizedProps.name
+ };
+ abortParentMarkerTransitionsForDeletedFiber(
+ current,
+ _abortReason,
+ _transitions,
+ null,
+ true
);
- }
- }
- }
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- instance.componentDidUpdate(
- prevProps,
- prevState,
- instance.__reactInternalSnapshotBeforeUpdate
- );
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ if (nearestMountedAncestor !== null) {
+ abortParentMarkerTransitionsForDeletedFiber(
+ nearestMountedAncestor,
+ _abortReason,
+ _transitions,
+ null,
+ false
+ );
+ }
+ }
}
- recordLayoutEffectDuration(finishedWork);
- } else {
- try {
- instance.componentDidUpdate(
- prevProps,
- prevState,
- instance.__reactInternalSnapshotBeforeUpdate
- );
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
+ break;
}
}
}
-function commitClassCallbacks(finishedWork) {
- // TODO: I think this is now always non-null by the time it reaches the
- // commit phase. Consider removing the type check.
- var updateQueue = finishedWork.updateQueue;
+function invokeLayoutEffectMountInDEV(fiber) {
+ {
+ // We don't need to re-check StrictEffectsMode here.
+ // This function is only called if that check has already passed.
+ switch (fiber.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ try {
+ commitHookEffectListMount(Layout | HasEffect, fiber);
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
+ }
- if (updateQueue !== null) {
- var instance = finishedWork.stateNode;
+ break;
+ }
- {
- if (
- finishedWork.type === finishedWork.elementType &&
- !didWarnAboutReassigningProps
- ) {
- if (instance.props !== finishedWork.memoizedProps) {
- error(
- "Expected %s props to match memoized props before " +
- "processing the update queue. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.props`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
- }
+ case ClassComponent: {
+ var instance = fiber.stateNode;
- if (instance.state !== finishedWork.memoizedState) {
- error(
- "Expected %s state to match memoized state before " +
- "processing the update queue. " +
- "This might either be because of a bug in React, or because " +
- "a component reassigns its own `this.state`. " +
- "Please file an issue.",
- getComponentNameFromFiber(finishedWork) || "instance"
- );
+ try {
+ instance.componentDidMount();
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
}
- }
- } // We could update instance props and state here,
- // but instead we rely on them being set during last render.
- // TODO: revisit this when we implement resuming.
- try {
- commitCallbacks(updateQueue, instance);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ break;
+ }
}
}
}
-function commitHostComponentMount(finishedWork) {
- var type = finishedWork.type;
- var props = finishedWork.memoizedProps;
- var instance = finishedWork.stateNode;
+function invokePassiveEffectMountInDEV(fiber) {
+ {
+ // We don't need to re-check StrictEffectsMode here.
+ // This function is only called if that check has already passed.
+ switch (fiber.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ try {
+ commitHookEffectListMount(Passive | HasEffect, fiber);
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
+ }
- try {
- commitMount(instance, type, props, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ break;
+ }
+ }
}
}
-function commitProfilerUpdate(finishedWork, current) {
- if (getExecutionContext() & CommitContext) {
- try {
- var _finishedWork$memoize2 = finishedWork.memoizedProps,
- onCommit = _finishedWork$memoize2.onCommit,
- onRender = _finishedWork$memoize2.onRender;
- var effectDuration = finishedWork.stateNode.effectDuration;
- var commitTime = getCommitTime();
- var phase = current === null ? "mount" : "update";
-
- if (enableProfilerNestedUpdatePhase) {
- if (isCurrentUpdateNested()) {
- phase = "nested-update";
+function invokeLayoutEffectUnmountInDEV(fiber) {
+ {
+ // We don't need to re-check StrictEffectsMode here.
+ // This function is only called if that check has already passed.
+ switch (fiber.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ try {
+ commitHookEffectListUnmount(Layout | HasEffect, fiber, fiber.return);
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
}
- }
- if (typeof onRender === "function") {
- onRender(
- finishedWork.memoizedProps.id,
- phase,
- finishedWork.actualDuration,
- finishedWork.treeBaseDuration,
- finishedWork.actualStartTime,
- commitTime
- );
+ break;
}
- if (enableProfilerCommitHooks) {
- if (typeof onCommit === "function") {
- onCommit(
- finishedWork.memoizedProps.id,
- phase,
- effectDuration,
- commitTime
- );
- } // Schedule a passive effect for this Profiler to call onPostCommit hooks.
- // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
- // because the effect is also where times bubble to parent Profilers.
-
- enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor.
- // Do not reset these values until the next render so DevTools has a chance to read them first.
-
- var parentFiber = finishedWork.return;
+ case ClassComponent: {
+ var instance = fiber.stateNode;
- outer: while (parentFiber !== null) {
- switch (parentFiber.tag) {
- case HostRoot:
- var root = parentFiber.stateNode;
- root.effectDuration += effectDuration;
- break outer;
+ if (typeof instance.componentWillUnmount === "function") {
+ safelyCallComponentWillUnmount(fiber, fiber.return, instance);
+ }
- case Profiler:
- var parentStateNode = parentFiber.stateNode;
- parentStateNode.effectDuration += effectDuration;
- break outer;
- }
+ break;
+ }
+ }
+ }
+}
- parentFiber = parentFiber.return;
+function invokePassiveEffectUnmountInDEV(fiber) {
+ {
+ // We don't need to re-check StrictEffectsMode here.
+ // This function is only called if that check has already passed.
+ switch (fiber.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ try {
+ commitHookEffectListUnmount(Passive | HasEffect, fiber, fiber.return);
+ } catch (error) {
+ captureCommitPhaseError(fiber, fiber.return, error);
}
}
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
}
}
}
-function commitLayoutEffectOnFiber(
- finishedRoot,
- current,
- finishedWork,
- committedLanes
-) {
- // When updating this function, also update reappearLayoutEffects, which does
- // most of the same things when an offscreen tree goes from hidden -> visible.
- var flags = finishedWork.flags;
+function getCacheSignal() {
+ var cache = readContext(CacheContext);
+ return cache.controller.signal;
+}
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+function getCacheForType(resourceType) {
+ var cache = readContext(CacheContext);
+ var cacheForType = cache.data.get(resourceType);
- if (flags & Update) {
- commitHookLayoutEffects(finishedWork, Layout | HasEffect);
- }
+ if (cacheForType === undefined) {
+ cacheForType = resourceType();
+ cache.data.set(resourceType, cacheForType);
+ }
- break;
+ return cacheForType;
+}
+
+var DefaultCacheDispatcher = {
+ getCacheSignal: getCacheSignal,
+ getCacheForType: getCacheForType
+};
+
+if (typeof Symbol === "function" && Symbol.for) {
+ var symbolFor = Symbol.for;
+ symbolFor("selector.component");
+ symbolFor("selector.has_pseudo_class");
+ symbolFor("selector.role");
+ symbolFor("selector.test_id");
+ symbolFor("selector.text");
+}
+var commitHooks = [];
+function onCommitRoot() {
+ {
+ commitHooks.forEach(function (commitHook) {
+ return commitHook();
+ });
+ }
+}
+
+var ReactCurrentActQueue$1 = ReactSharedInternals.ReactCurrentActQueue;
+function isLegacyActEnvironment(fiber) {
+ {
+ // Legacy mode. We preserve the behavior of React 17's act. It assumes an
+ // act environment whenever `jest` is defined, but you can still turn off
+ // spurious warnings by setting IS_REACT_ACT_ENVIRONMENT explicitly
+ // to false.
+ var isReactActEnvironmentGlobal = // $FlowFixMe[cannot-resolve-name] Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
+ typeof IS_REACT_ACT_ENVIRONMENT !== "undefined" // $FlowFixMe[cannot-resolve-name]
+ ? IS_REACT_ACT_ENVIRONMENT
+ : undefined; // $FlowFixMe - Flow doesn't know about jest
+
+ var jestIsDefined = typeof jest !== "undefined";
+ return jestIsDefined && isReactActEnvironmentGlobal !== false;
+ }
+}
+function isConcurrentActEnvironment() {
+ {
+ var isReactActEnvironmentGlobal = // $FlowFixMe[cannot-resolve-name] Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
+ typeof IS_REACT_ACT_ENVIRONMENT !== "undefined" // $FlowFixMe[cannot-resolve-name]
+ ? IS_REACT_ACT_ENVIRONMENT
+ : undefined;
+
+ if (
+ !isReactActEnvironmentGlobal &&
+ ReactCurrentActQueue$1.current !== null
+ ) {
+ // TODO: Include link to relevant documentation page.
+ error(
+ "The current testing environment is not configured to support " +
+ "act(...)"
+ );
}
- case ClassComponent: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+ return isReactActEnvironmentGlobal;
+ }
+}
- if (flags & Update) {
- commitClassLayoutLifecycles(finishedWork, current);
- }
+var postPaintCallbackScheduled = false;
+var callbacks = [];
+function schedulePostPaintCallback(callback) {
+ callbacks.push(callback);
- if (flags & Callback) {
- commitClassCallbacks(finishedWork);
+ if (!postPaintCallbackScheduled) {
+ postPaintCallbackScheduled = true;
+ requestPostPaintCallback(function (endTime) {
+ for (var i = 0; i < callbacks.length; i++) {
+ callbacks[i](endTime);
}
- if (flags & Ref) {
- safelyAttachRef(finishedWork, finishedWork.return);
- }
+ postPaintCallbackScheduled = false;
+ callbacks = [];
+ });
+ }
+}
- break;
- }
+var ceil = Math.ceil;
+var PossiblyWeakMap = typeof WeakMap === "function" ? WeakMap : Map;
+var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher,
+ ReactCurrentCache = ReactSharedInternals.ReactCurrentCache,
+ ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner,
+ ReactCurrentBatchConfig$1 = ReactSharedInternals.ReactCurrentBatchConfig,
+ ReactCurrentActQueue = ReactSharedInternals.ReactCurrentActQueue;
+var NoContext =
+ /* */
+ 0;
+var BatchedContext =
+ /* */
+ 1;
+var RenderContext =
+ /* */
+ 2;
+var CommitContext =
+ /* */
+ 4;
+var RootInProgress = 0;
+var RootFatalErrored = 1;
+var RootErrored = 2;
+var RootSuspended = 3;
+var RootSuspendedWithDelay = 4;
+var RootCompleted = 5;
+var RootDidNotComplete = 6; // Describes where we are in the React execution stack
- case HostRoot: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+var executionContext = NoContext; // The root we're working on
- if (flags & Callback) {
- // TODO: I think this is now always non-null by the time it reaches the
- // commit phase. Consider removing the type check.
- var updateQueue = finishedWork.updateQueue;
+var workInProgressRoot = null; // The fiber we're working on
- if (updateQueue !== null) {
- var instance = null;
+var workInProgress = null; // The lanes we're rendering
- if (finishedWork.child !== null) {
- switch (finishedWork.child.tag) {
- case HostSingleton:
- case HostComponent:
- instance = getPublicInstance(finishedWork.child.stateNode);
- break;
+var workInProgressRootRenderLanes = NoLanes;
+var NotSuspended = 0;
+var SuspendedOnError = 1;
+var SuspendedOnData = 2;
+var SuspendedOnImmediate = 3;
+var SuspendedOnDeprecatedThrowPromise = 4;
+var SuspendedAndReadyToContinue = 5;
+var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and
+// we've yet to unwind the stack. In some cases, we may yield to the main thread
+// after this happens. If the fiber is pinged before we resume, we can retry
+// immediately instead of unwinding the stack.
- case ClassComponent:
- instance = finishedWork.child.stateNode;
- break;
- }
- }
+var workInProgressSuspendedReason = NotSuspended;
+var workInProgressThrownValue = null; // Whether a ping listener was attached during this render. This is slightly
+// different that whether something suspended, because we don't add multiple
+// listeners to a promise we've already seen (per root and lane).
- try {
- commitCallbacks(updateQueue, instance);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
- }
+var workInProgressRootDidAttachPingListener = false; // A contextual version of workInProgressRootRenderLanes. It is a superset of
+// the lanes that we started working on at the root. When we enter a subtree
+// that is currently hidden, we add the lanes that would have committed if
+// the hidden tree hadn't been deferred. This is modified by the
+// HiddenContext module.
+//
+// Most things in the work loop should deal with workInProgressRootRenderLanes.
+// Most things in begin/complete phases should deal with renderLanes.
- break;
- }
+var renderLanes = NoLanes; // Whether to root completed, errored, suspended, etc.
- case HostHoistable: {
- {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
+var workInProgressRootExitStatus = RootInProgress; // A fatal error, if one is thrown
- if (flags & Ref) {
- safelyAttachRef(finishedWork, finishedWork.return);
- }
+var workInProgressRootFatalError = null; // The work left over by components that were visited during this render. Only
+// includes unprocessed updates, not work in bailed out children.
- break;
- }
- }
- // eslint-disable-next-line-no-fallthrough
+var workInProgressRootSkippedLanes = NoLanes; // Lanes that were updated (in an interleaved event) during this render.
- case HostSingleton:
- case HostComponent: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); // 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.
+var workInProgressRootInterleavedUpdatedLanes = NoLanes; // Lanes that were updated during the render phase (*not* an interleaved event).
- if (current === null && flags & Update) {
- commitHostComponentMount(finishedWork);
- }
+var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during the render phase.
- if (flags & Ref) {
- safelyAttachRef(finishedWork, finishedWork.return);
- }
+var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
+// We will log them once the tree commits.
- break;
- }
+var workInProgressRootRecoverableErrors = null; // The most recent time we committed a fallback. This lets us ensure a train
+// model where we don't commit new loading states in too quick succession.
- case Profiler: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork); // TODO: Should this fire inside an offscreen tree? Or should it wait to
- // fire when the tree becomes visible again.
+var globalMostRecentFallbackTime = 0;
+var FALLBACK_THROTTLE_MS = 500; // The absolute time for when we should start giving up on rendering
+// more and prefer CPU suspense heuristics instead.
- if (flags & Update) {
- commitProfilerUpdate(finishedWork, current);
- }
+var workInProgressRootRenderTargetTime = Infinity; // How long a render is supposed to take before we start following CPU
+// suspense heuristics and opt out of rendering more content.
- break;
+var RENDER_TIMEOUT_MS = 500;
+var workInProgressTransitions = null;
+function getWorkInProgressTransitions() {
+ return workInProgressTransitions;
+}
+var currentPendingTransitionCallbacks = null;
+var currentEndTime = null;
+function addTransitionStartCallbackToPendingTransition(transition) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: [],
+ transitionProgress: null,
+ transitionComplete: null,
+ markerProgress: null,
+ markerIncomplete: null,
+ markerComplete: null
+ };
}
- case SuspenseComponent: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
-
- if (flags & Update) {
- commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
- }
+ if (currentPendingTransitionCallbacks.transitionStart === null) {
+ currentPendingTransitionCallbacks.transitionStart = [];
+ }
- break;
+ currentPendingTransitionCallbacks.transitionStart.push(transition);
+ }
+}
+function addMarkerProgressCallbackToPendingTransition(
+ markerName,
+ transitions,
+ pendingBoundaries
+) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: null,
+ transitionProgress: null,
+ transitionComplete: null,
+ markerProgress: new Map(),
+ markerIncomplete: null,
+ markerComplete: null
+ };
}
- case OffscreenComponent: {
- var isModernRoot = (finishedWork.mode & ConcurrentMode) !== NoMode;
+ if (currentPendingTransitionCallbacks.markerProgress === null) {
+ currentPendingTransitionCallbacks.markerProgress = new Map();
+ }
- if (isModernRoot) {
- var isHidden = finishedWork.memoizedState !== null;
- var newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
+ currentPendingTransitionCallbacks.markerProgress.set(markerName, {
+ pendingBoundaries: pendingBoundaries,
+ transitions: transitions
+ });
+ }
+}
+function addMarkerIncompleteCallbackToPendingTransition(
+ markerName,
+ transitions,
+ aborts
+) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: null,
+ transitionProgress: null,
+ transitionComplete: null,
+ markerProgress: null,
+ markerIncomplete: new Map(),
+ markerComplete: null
+ };
+ }
- if (newOffscreenSubtreeIsHidden);
- else {
- // The Offscreen tree is visible.
- var wasHidden = current !== null && current.memoizedState !== null;
- var newOffscreenSubtreeWasHidden =
- wasHidden || offscreenSubtreeWasHidden;
- var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
- var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
- offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
- offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
+ if (currentPendingTransitionCallbacks.markerIncomplete === null) {
+ currentPendingTransitionCallbacks.markerIncomplete = new Map();
+ }
- if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
- // This is the root of a reappearing boundary. As we continue
- // traversing the layout effects, we must also re-mount layout
- // effects that were unmounted when the Offscreen subtree was
- // hidden. So this is a superset of the normal commitLayoutEffects.
- var includeWorkInProgressEffects =
- (finishedWork.subtreeFlags & LayoutMask) !== NoFlags$1;
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- );
- } else {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- }
+ currentPendingTransitionCallbacks.markerIncomplete.set(markerName, {
+ transitions: transitions,
+ aborts: aborts
+ });
+ }
+}
+function addMarkerCompleteCallbackToPendingTransition(markerName, transitions) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: null,
+ transitionProgress: null,
+ transitionComplete: null,
+ markerProgress: null,
+ markerIncomplete: null,
+ markerComplete: new Map()
+ };
+ }
- offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
- offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
- }
- } else {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- }
+ if (currentPendingTransitionCallbacks.markerComplete === null) {
+ currentPendingTransitionCallbacks.markerComplete = new Map();
+ }
- if (flags & Ref) {
- var props = finishedWork.memoizedProps;
+ currentPendingTransitionCallbacks.markerComplete.set(
+ markerName,
+ transitions
+ );
+ }
+}
+function addTransitionProgressCallbackToPendingTransition(
+ transition,
+ boundaries
+) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: null,
+ transitionProgress: new Map(),
+ transitionComplete: null,
+ markerProgress: null,
+ markerIncomplete: null,
+ markerComplete: null
+ };
+ }
- if (props.mode === "manual") {
- safelyAttachRef(finishedWork, finishedWork.return);
- } else {
- safelyDetachRef(finishedWork, finishedWork.return);
- }
- }
+ if (currentPendingTransitionCallbacks.transitionProgress === null) {
+ currentPendingTransitionCallbacks.transitionProgress = new Map();
+ }
- break;
+ currentPendingTransitionCallbacks.transitionProgress.set(
+ transition,
+ boundaries
+ );
+ }
+}
+function addTransitionCompleteCallbackToPendingTransition(transition) {
+ if (enableTransitionTracing) {
+ if (currentPendingTransitionCallbacks === null) {
+ currentPendingTransitionCallbacks = {
+ transitionStart: null,
+ transitionProgress: null,
+ transitionComplete: [],
+ markerProgress: null,
+ markerIncomplete: null,
+ markerComplete: null
+ };
}
- default: {
- recursivelyTraverseLayoutEffects(finishedRoot, finishedWork);
- break;
+ if (currentPendingTransitionCallbacks.transitionComplete === null) {
+ currentPendingTransitionCallbacks.transitionComplete = [];
}
+
+ currentPendingTransitionCallbacks.transitionComplete.push(transition);
}
}
-function abortRootTransitions(
- root,
- abort,
- deletedTransitions,
- deletedOffscreenInstance,
- isInDeletedTree
-) {
- if (enableTransitionTracing) {
- var rootTransitions = root.incompleteTransitions;
- deletedTransitions.forEach(function (transition) {
- if (rootTransitions.has(transition)) {
- var transitionInstance = rootTransitions.get(transition);
+function resetRenderTimer() {
+ workInProgressRootRenderTargetTime = now$1() + RENDER_TIMEOUT_MS;
+}
- if (transitionInstance.aborts === null) {
- transitionInstance.aborts = [];
- }
+function getRenderTargetTime() {
+ return workInProgressRootRenderTargetTime;
+}
+var hasUncaughtError = false;
+var firstUncaughtError = null;
+var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfilerNestedUpdateScheduledHook is true;
+// to track which root is currently committing layout effects.
- transitionInstance.aborts.push(abort);
+var rootCommittingMutationOrLayoutEffects = null;
+var rootDoesHavePassiveEffects = false;
+var rootWithPendingPassiveEffects = null;
+var pendingPassiveEffectsLanes = NoLanes;
+var pendingPassiveProfilerEffects = [];
+var pendingPassiveEffectsRemainingLanes = NoLanes;
+var pendingPassiveTransitions = null; // Use these to prevent an infinite loop of nested updates
- if (deletedOffscreenInstance !== null) {
- if (
- transitionInstance.pendingBoundaries !== null &&
- transitionInstance.pendingBoundaries.has(deletedOffscreenInstance)
- ) {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- transitionInstance.pendingBoundaries.delete(
- deletedOffscreenInstance
- );
- }
- }
- }
- });
- }
+var NESTED_UPDATE_LIMIT = 50;
+var nestedUpdateCount = 0;
+var rootWithNestedUpdates = null;
+var isFlushingPassiveEffects = false;
+var didScheduleUpdateDuringPassiveEffects = false;
+var NESTED_PASSIVE_UPDATE_LIMIT = 50;
+var nestedPassiveUpdateCount = 0;
+var rootWithPassiveNestedUpdates = null; // If two updates are scheduled within the same event, we should treat their
+// event times as simultaneous, even if the actual clock time has advanced
+// between the first and second call.
+
+var currentEventTime = NoTimestamp;
+var currentEventTransitionLane = NoLanes;
+var isRunningInsertionEffect = false;
+function getWorkInProgressRoot() {
+ return workInProgressRoot;
+}
+function getWorkInProgressRootRenderLanes() {
+ return workInProgressRootRenderLanes;
}
+function requestEventTime() {
+ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
+ // We're inside React, so it's fine to read the actual time.
+ return now$1();
+ } // We're not inside React, so we may be in the middle of a browser event.
-function abortTracingMarkerTransitions(
- abortedFiber,
- abort,
- deletedTransitions,
- deletedOffscreenInstance,
- isInDeletedTree
-) {
- if (enableTransitionTracing) {
- var markerInstance = abortedFiber.stateNode;
- var markerTransitions = markerInstance.transitions;
- var pendingBoundaries = markerInstance.pendingBoundaries;
+ if (currentEventTime !== NoTimestamp) {
+ // Use the same start time for all updates until we enter React again.
+ return currentEventTime;
+ } // This is the first update since React yielded. Compute a new start time.
- if (markerTransitions !== null) {
- // TODO: Refactor this code. Is there a way to move this code to
- // the deletions phase instead of calculating it here while making sure
- // complete is called appropriately?
- deletedTransitions.forEach(function (transition) {
- // If one of the transitions on the tracing marker is a transition
- // that was in an aborted subtree, we will abort that tracing marker
- if (
- abortedFiber !== null &&
- markerTransitions.has(transition) &&
- (markerInstance.aborts === null ||
- !markerInstance.aborts.includes(abort))
- ) {
- if (markerInstance.transitions !== null) {
- if (markerInstance.aborts === null) {
- markerInstance.aborts = [abort];
- addMarkerIncompleteCallbackToPendingTransition(
- abortedFiber.memoizedProps.name,
- markerInstance.transitions,
- markerInstance.aborts
- );
- } else {
- markerInstance.aborts.push(abort);
- } // We only want to call onTransitionProgress when the marker hasn't been
- // deleted
+ currentEventTime = now$1();
+ return currentEventTime;
+}
+function requestUpdateLane(fiber) {
+ // Special cases
+ var mode = fiber.mode;
- if (
- deletedOffscreenInstance !== null &&
- !isInDeletedTree &&
- pendingBoundaries !== null &&
- pendingBoundaries.has(deletedOffscreenInstance)
- ) {
- pendingBoundaries.delete(deletedOffscreenInstance);
- addMarkerProgressCallbackToPendingTransition(
- abortedFiber.memoizedProps.name,
- deletedTransitions,
- pendingBoundaries
- );
- }
- }
- }
- });
- }
+ if ((mode & ConcurrentMode) === NoMode) {
+ return SyncLane;
+ } else if (
+ !deferRenderPhaseUpdateToNextBatch &&
+ (executionContext & RenderContext) !== NoContext &&
+ workInProgressRootRenderLanes !== NoLanes
+ ) {
+ // This is a render phase update. These are not officially supported. The
+ // old behavior is to give this the same "thread" (lanes) as
+ // whatever is currently rendering. So if you call `setState` on a component
+ // that happens later in the same render, it will flush. Ideally, we want to
+ // remove the special case and treat them as if they came from an
+ // interleaved event. Regardless, this pattern is not officially supported.
+ // This behavior is only a fallback. The flag only exists until we can roll
+ // out the setState warning, since existing code might accidentally rely on
+ // the current behavior.
+ return pickArbitraryLane(workInProgressRootRenderLanes);
}
-}
-function abortParentMarkerTransitionsForDeletedFiber(
- abortedFiber,
- abort,
- deletedTransitions,
- deletedOffscreenInstance,
- isInDeletedTree
-) {
- if (enableTransitionTracing) {
- // Find all pending markers that are waiting on child suspense boundaries in the
- // aborted subtree and cancels them
- var fiber = abortedFiber;
+ var isTransition = requestCurrentTransition() !== NoTransition;
- while (fiber !== null) {
- switch (fiber.tag) {
- case TracingMarkerComponent:
- abortTracingMarkerTransitions(
- fiber,
- abort,
- deletedTransitions,
- deletedOffscreenInstance,
- isInDeletedTree
- );
- break;
+ if (isTransition) {
+ if (ReactCurrentBatchConfig$1.transition !== null) {
+ var transition = ReactCurrentBatchConfig$1.transition;
- case HostRoot:
- var root = fiber.stateNode;
- abortRootTransitions(
- root,
- abort,
- deletedTransitions,
- deletedOffscreenInstance
- );
- break;
+ if (!transition._updatedFibers) {
+ transition._updatedFibers = new Set();
}
- fiber = fiber.return;
+ transition._updatedFibers.add(fiber);
+ } // The algorithm for assigning an update to a lane should be stable for all
+ // updates at the same priority within the same event. To do this, the
+ // inputs to the algorithm must be the same.
+ //
+ // The trick we use is to cache the first of each of these inputs within an
+ // event. Then reset the cached values once we can be sure the event is
+ // over. Our heuristic for that is whenever we enter a concurrent work loop.
+
+ if (currentEventTransitionLane === NoLane) {
+ // All transitions within the same event are assigned the same lane.
+ currentEventTransitionLane = claimNextTransitionLane();
}
- }
-}
-function commitTransitionProgress(offscreenFiber) {
- if (enableTransitionTracing) {
- // This function adds suspense boundaries to the root
- // or tracing marker's pendingBoundaries map.
- // When a suspense boundary goes from a resolved to a fallback
- // state we add the boundary to the map, and when it goes from
- // a fallback to a resolved state, we remove the boundary from
- // the map.
- // We use stateNode on the Offscreen component as a stable object
- // that doesnt change from render to render. This way we can
- // distinguish between different Offscreen instances (vs. the same
- // Offscreen instance with different fibers)
- var offscreenInstance = offscreenFiber.stateNode;
- var prevState = null;
- var previousFiber = offscreenFiber.alternate;
+ return currentEventTransitionLane;
+ } // Updates originating inside certain React methods, like flushSync, have
+ // their priority set by tracking it with a context variable.
+ //
+ // The opaque type returned by the host config is internally a lane, so we can
+ // use that directly.
+ // TODO: Move this type conversion to the event priority module.
- if (previousFiber !== null && previousFiber.memoizedState !== null) {
- prevState = previousFiber.memoizedState;
- }
+ var updateLane = getCurrentUpdatePriority();
- var nextState = offscreenFiber.memoizedState;
- var wasHidden = prevState !== null;
- var isHidden = nextState !== null;
- var pendingMarkers = offscreenInstance._pendingMarkers; // If there is a name on the suspense boundary, store that in
- // the pending boundaries.
+ if (updateLane !== NoLane) {
+ return updateLane;
+ } // This update originated outside React. Ask the host environment for an
+ // appropriate priority, based on the type of event.
+ //
+ // The opaque type returned by the host config is internally a lane, so we can
+ // use that directly.
+ // TODO: Move this type conversion to the event priority module.
- var name = null;
- var parent = offscreenFiber.return;
+ var eventLane = getCurrentEventPriority();
+ return eventLane;
+}
- if (
- parent !== null &&
- parent.tag === SuspenseComponent &&
- parent.memoizedProps.unstable_name
- ) {
- name = parent.memoizedProps.unstable_name;
- }
+function requestRetryLane(fiber) {
+ // This is a fork of `requestUpdateLane` designed specifically for Suspense
+ // "retries" — a special update that attempts to flip a Suspense boundary
+ // from its placeholder state to its primary/resolved state.
+ // Special cases
+ var mode = fiber.mode;
- if (!wasHidden && isHidden) {
- // The suspense boundaries was just hidden. Add the boundary
- // to the pending boundary set if it's there
- if (pendingMarkers !== null) {
- pendingMarkers.forEach(function (markerInstance) {
- var pendingBoundaries = markerInstance.pendingBoundaries;
- var transitions = markerInstance.transitions;
- var markerName = markerInstance.name;
+ if ((mode & ConcurrentMode) === NoMode) {
+ return SyncLane;
+ }
- if (
- pendingBoundaries !== null &&
- !pendingBoundaries.has(offscreenInstance)
- ) {
- pendingBoundaries.set(offscreenInstance, {
- name: name
- });
+ return claimNextRetryLane();
+}
- if (transitions !== null) {
- if (
- markerInstance.tag === TransitionTracingMarker &&
- markerName !== null
- ) {
- addMarkerProgressCallbackToPendingTransition(
- markerName,
- transitions,
- pendingBoundaries
- );
- } else if (markerInstance.tag === TransitionRoot) {
- transitions.forEach(function (transition) {
- addTransitionProgressCallbackToPendingTransition(
- transition,
- pendingBoundaries
- );
- });
- }
- }
- }
- });
- }
- } else if (wasHidden && !isHidden) {
- // The suspense boundary went from hidden to visible. Remove
- // the boundary from the pending suspense boundaries set
- // if it's there
- if (pendingMarkers !== null) {
- pendingMarkers.forEach(function (markerInstance) {
- var pendingBoundaries = markerInstance.pendingBoundaries;
- var transitions = markerInstance.transitions;
- var markerName = markerInstance.name;
+function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {
+ {
+ if (isRunningInsertionEffect) {
+ error("useInsertionEffect must not schedule updates.");
+ }
+ }
- if (
- pendingBoundaries !== null &&
- pendingBoundaries.has(offscreenInstance)
- ) {
- pendingBoundaries.delete(offscreenInstance);
+ {
+ if (isFlushingPassiveEffects) {
+ didScheduleUpdateDuringPassiveEffects = true;
+ }
+ } // Check if the work loop is currently suspended and waiting for data to
+ // finish loading.
- if (transitions !== null) {
- if (
- markerInstance.tag === TransitionTracingMarker &&
- markerName !== null
- ) {
- addMarkerProgressCallbackToPendingTransition(
- markerName,
- transitions,
- pendingBoundaries
- ); // If there are no more unresolved suspense boundaries, the interaction
- // is considered finished
+ if (
+ workInProgressSuspendedReason === SuspendedOnData &&
+ root === workInProgressRoot
+ ) {
+ // The incoming update might unblock the current render. Interrupt the
+ // current attempt and restart from the top.
+ prepareFreshStack(root, NoLanes);
+ markRootSuspended(root, workInProgressRootRenderLanes);
+ } // Mark that the root has a pending update.
- if (pendingBoundaries.size === 0) {
- if (markerInstance.aborts === null) {
- addMarkerCompleteCallbackToPendingTransition(
- markerName,
- transitions
- );
- }
+ markRootUpdated(root, lane, eventTime);
- markerInstance.transitions = null;
- markerInstance.pendingBoundaries = null;
- markerInstance.aborts = null;
- }
- } else if (markerInstance.tag === TransitionRoot) {
- transitions.forEach(function (transition) {
- addTransitionProgressCallbackToPendingTransition(
- transition,
- pendingBoundaries
- );
- });
- }
- }
- }
- });
+ if (
+ (executionContext & RenderContext) !== NoLanes &&
+ root === workInProgressRoot
+ ) {
+ // This update was dispatched during the render phase. This is a mistake
+ // if the update originates from user space (with the exception of local
+ // hook updates, which are handled differently and don't reach this
+ // function), but there are some internal React features that use this as
+ // an implementation detail, like selective hydration.
+ warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase
+ } else {
+ // This is a normal update, scheduled from outside the render phase. For
+ // example, during an input event.
+ {
+ if (isDevToolsPresent) {
+ addFiberToLanesMap(root, fiber, lane);
}
}
- }
-}
-
-function hideOrUnhideAllChildren(finishedWork, isHidden) {
- // Only hide or unhide the top-most host nodes.
- var hostSubtreeRoot = null;
- {
- // We only have the top Fiber that was inserted but we need to recurse down its
- // children to find all the terminal nodes.
- var node = finishedWork;
+ warnIfUpdatesNotWrappedWithActDEV(fiber);
- while (true) {
+ if (enableProfilerNestedUpdateScheduledHook) {
if (
- node.tag === HostComponent ||
- node.tag === HostHoistable ||
- node.tag === HostSingleton
+ (executionContext & CommitContext) !== NoContext &&
+ root === rootCommittingMutationOrLayoutEffects
) {
- if (hostSubtreeRoot === null) {
- hostSubtreeRoot = node;
+ if (fiber.mode & ProfileMode) {
+ var current = fiber;
- try {
- var instance = node.stateNode;
+ while (current !== null) {
+ if (current.tag === Profiler) {
+ var _current$memoizedProp = current.memoizedProps,
+ id = _current$memoizedProp.id,
+ onNestedUpdateScheduled =
+ _current$memoizedProp.onNestedUpdateScheduled;
- if (isHidden) {
- hideInstance(instance);
- } else {
- unhideInstance(node.stateNode, node.memoizedProps);
+ if (typeof onNestedUpdateScheduled === "function") {
+ onNestedUpdateScheduled(id);
+ }
}
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
- } else if (node.tag === HostText) {
- if (hostSubtreeRoot === null) {
- try {
- var _instance = node.stateNode;
- if (isHidden) {
- hideTextInstance(_instance);
- } else {
- unhideTextInstance(_instance, node.memoizedProps);
- }
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ current = current.return;
}
}
- } else if (
- (node.tag === OffscreenComponent ||
- node.tag === LegacyHiddenComponent) &&
- node.memoizedState !== null &&
- node !== finishedWork
- );
- else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
-
- if (node === finishedWork) {
- return;
}
+ }
- while (node.sibling === null) {
- if (node.return === null || node.return === finishedWork) {
- return;
- }
+ if (enableTransitionTracing) {
+ var transition = ReactCurrentBatchConfig$1.transition;
- if (hostSubtreeRoot === node) {
- hostSubtreeRoot = null;
+ if (transition !== null && transition.name != null) {
+ if (transition.startTime === -1) {
+ transition.startTime = now$1();
}
- node = node.return;
- }
-
- if (hostSubtreeRoot === node) {
- hostSubtreeRoot = null;
+ addTransitionToLanesMap(root, transition, lane);
}
-
- node.sibling.return = node.return;
- node = node.sibling;
}
- }
-}
-
-function commitAttachRef(finishedWork) {
- var ref = finishedWork.ref;
- if (ref !== null) {
- var instance = finishedWork.stateNode;
- var instanceToUse;
-
- switch (finishedWork.tag) {
- case HostHoistable:
- case HostSingleton:
- case HostComponent:
- instanceToUse = getPublicInstance(instance);
- break;
-
- default:
- instanceToUse = instance;
- } // Moved outside to ensure DCE works with this flag
+ if (root === workInProgressRoot) {
+ // Received an update to a tree that's in the middle of rendering. Mark
+ // that there was an interleaved update work on this root. Unless the
+ // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
+ // phase update. In that case, we don't treat render phase updates as if
+ // they were interleaved, for backwards compat reasons.
+ if (
+ deferRenderPhaseUpdateToNextBatch ||
+ (executionContext & RenderContext) === NoContext
+ ) {
+ workInProgressRootInterleavedUpdatedLanes = mergeLanes(
+ workInProgressRootInterleavedUpdatedLanes,
+ lane
+ );
+ }
- if (finishedWork.tag === ScopeComponent) {
- instanceToUse = instance;
+ if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
+ // The root already suspended with a delay, which means this render
+ // definitely won't finish. Since we have a new update, let's mark it as
+ // suspended now, right before marking the incoming update. This has the
+ // effect of interrupting the current render and switching to the update.
+ // TODO: Make sure this doesn't override pings that happen while we've
+ // already started rendering.
+ markRootSuspended(root, workInProgressRootRenderLanes);
+ }
}
- if (typeof ref === "function") {
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- finishedWork.refCleanup = ref(instanceToUse);
- } finally {
- recordLayoutEffectDuration(finishedWork);
- }
- } else {
- finishedWork.refCleanup = ref(instanceToUse);
- }
- } else {
- {
- if (!ref.hasOwnProperty("current")) {
- error(
- "Unexpected ref object provided for %s. " +
- "Use either a ref-setter function or React.createRef().",
- getComponentNameFromFiber(finishedWork)
- );
- }
- } // $FlowFixMe unable to narrow type to the non-function case
+ ensureRootIsScheduled(root, eventTime);
- ref.current = instanceToUse;
+ if (
+ lane === SyncLane &&
+ executionContext === NoContext &&
+ (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
+ !ReactCurrentActQueue.isBatchingLegacy
+ ) {
+ // Flush the synchronous work now, unless we're already working or inside
+ // a batch. This is intentionally inside scheduleUpdateOnFiber instead of
+ // scheduleCallbackForFiber to preserve the ability to schedule a callback
+ // without immediately flushing it. We only do this for user-initiated
+ // updates, to preserve historical behavior of legacy mode.
+ resetRenderTimer();
+ flushSyncCallbacksOnlyInLegacyMode();
}
}
}
-
-function detachFiberMutation(fiber) {
- // Cut off the return pointer to disconnect it from the tree.
- // This enables us to detect and warn against state updates on an unmounted component.
- // It also prevents events from bubbling from within disconnected components.
- //
- // Ideally, we should also clear the child pointer of the parent alternate to let this
- // get GC:ed but we don't know which for sure which parent is the current
- // one so we'll settle for GC:ing the subtree of this child.
- // This child itself will be GC:ed when the parent updates the next time.
- //
- // Note that we can't clear child or sibling pointers yet.
- // They're needed for passive effects and for findDOMNode.
- // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
+function scheduleInitialHydrationOnRoot(root, lane, eventTime) {
+ // This is a special fork of scheduleUpdateOnFiber that is only used to
+ // schedule the initial hydration of a root that has just been created. Most
+ // of the stuff in scheduleUpdateOnFiber can be skipped.
//
- // Don't reset the alternate yet, either. We need that so we can detach the
- // alternate's fields in the passive phase. Clearing the return pointer is
- // sufficient for findDOMNode semantics.
- var alternate = fiber.alternate;
-
- if (alternate !== null) {
- alternate.return = null;
- }
-
- fiber.return = null;
+ // The main reason for this separate path, though, is to distinguish the
+ // initial children from subsequent updates. In fully client-rendered roots
+ // (createRoot instead of hydrateRoot), all top-level renders are modeled as
+ // updates, but hydration roots are special because the initial render must
+ // match what was rendered on the server.
+ var current = root.current;
+ current.lanes = lane;
+ markRootUpdated(root, lane, eventTime);
+ ensureRootIsScheduled(root, eventTime);
}
+function isUnsafeClassRenderPhaseUpdate(fiber) {
+ // Check if this is a render phase update. Only called by class components,
+ // which special (deprecated) behavior for UNSAFE_componentWillReceive props.
+ return (
+ // TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
+ // decided not to enable it.
+ (!deferRenderPhaseUpdateToNextBatch ||
+ (fiber.mode & ConcurrentMode) === NoMode) &&
+ (executionContext & RenderContext) !== NoContext
+ );
+} // Use this function to schedule a task for a root. There's only one task per
+// root; if a task was already scheduled, we'll check to make sure the priority
+// of the existing task is the same as the priority of the next level that the
+// root has work on. This function is called on every update, and right before
+// exiting a task.
-function detachFiberAfterEffects(fiber) {
- var alternate = fiber.alternate;
-
- if (alternate !== null) {
- fiber.alternate = null;
- detachFiberAfterEffects(alternate);
- } // Clear cyclical Fiber fields. This level alone is designed to roughly
- // approximate the planned Fiber refactor. In that world, `setState` will be
- // bound to a special "instance" object instead of a Fiber. The Instance
- // object will not have any of these fields. It will only be connected to
- // the fiber tree via a single link at the root. So if this level alone is
- // sufficient to fix memory issues, that bodes well for our plans.
-
- fiber.child = null;
- fiber.deletions = null;
- fiber.sibling = null; // The `stateNode` is cyclical because on host nodes it points to the host
- // tree, which has its own pointers to children, parents, and siblings.
- // The other host nodes also point back to fibers, so we should detach that
- // one, too.
-
- if (fiber.tag === HostComponent) {
- var hostInstance = fiber.stateNode;
-
- if (hostInstance !== null) {
- detachDeletedInstance(hostInstance);
- }
- }
-
- fiber.stateNode = null;
-
- {
- fiber._debugOwner = null;
- } // Theoretically, nothing in here should be necessary, because we already
- // disconnected the fiber from the tree. So even if something leaks this
- // particular fiber, it won't leak anything else.
-
- fiber.return = null;
- fiber.dependencies = null;
- fiber.memoizedProps = null;
- fiber.memoizedState = null;
- fiber.pendingProps = null;
- fiber.stateNode = null; // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
+function ensureRootIsScheduled(root, currentTime) {
+ var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as
+ // expired so we know to work on those next.
- fiber.updateQueue = null;
-}
+ markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.
-function getHostParentFiber(fiber) {
- var parent = fiber.return;
+ var nextLanes = getNextLanes(
+ root,
+ root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
+ );
- while (parent !== null) {
- if (isHostParent(parent)) {
- return parent;
+ if (nextLanes === NoLanes) {
+ // Special case: There's nothing to work on.
+ if (existingCallbackNode !== null) {
+ cancelCallback(existingCallbackNode);
}
- parent = parent.return;
- }
+ root.callbackNode = null;
+ root.callbackPriority = NoLane;
+ return;
+ } // If this root is currently suspended and waiting for data to resolve, don't
+ // schedule a task to render it. We'll either wait for a ping, or wait to
+ // receive an update.
- throw new Error(
- "Expected to find a host parent. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
-}
+ if (
+ workInProgressSuspendedReason === SuspendedOnData &&
+ workInProgressRoot === root
+ ) {
+ root.callbackPriority = NoLane;
+ root.callbackNode = null;
+ return;
+ } // We use the highest priority lane to represent the priority of the callback.
-function isHostParent(fiber) {
- return (
- fiber.tag === HostComponent ||
- fiber.tag === HostRoot ||
- fiber.tag === HostHoistable ||
- fiber.tag === HostSingleton ||
- fiber.tag === HostPortal
- );
-}
+ var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it.
-function getHostSibling(fiber) {
- // We're going to search forward into the tree until we find a sibling host
- // node. Unfortunately, if multiple insertions are done in a row we have to
- // search past them. This leads to exponential search for the next sibling.
- // TODO: Find a more efficient way to do this.
- var node = fiber;
+ var existingCallbackPriority = root.callbackPriority;
- 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;
- } // $FlowFixMe[incompatible-type] found when upgrading Flow
+ if (
+ existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a
+ // Scheduler task, rather than an `act` task, cancel it and re-scheduled
+ // on the `act` queue.
+ !(
+ ReactCurrentActQueue.current !== null &&
+ existingCallbackNode !== fakeActCallbackNode
+ )
+ ) {
+ {
+ // If we're going to re-use an existing task, it needs to exist.
+ // Assume that discrete update microtasks are non-cancellable and null.
+ // TODO: Temporary until we confirm this warning is not fired.
+ if (
+ existingCallbackNode == null &&
+ !includesSyncLane(existingCallbackPriority)
+ ) {
+ error(
+ "Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
+ } // The priority hasn't changed. We can reuse the existing task. Exit.
- node = node.return;
- }
+ return;
+ }
- node.sibling.return = node.return;
- node = node.sibling;
+ if (existingCallbackNode != null) {
+ // Cancel the existing callback. We'll schedule a new one below.
+ cancelCallback(existingCallbackNode);
+ } // Schedule a new callback.
- while (
- node.tag !== HostComponent &&
- node.tag !== HostText &&
- node.tag !== HostSingleton &&
- node.tag !== DehydratedFragment
- ) {
- // If it is not host node and, we might have a host node inside it.
- // Try to search down until we find one.
- if (node.flags & Placement) {
- // If we don't have a child, try the siblings instead.
- continue siblings;
- } // If we don't have a child, try the siblings instead.
- // We also skip portals because they are not part of this host tree.
+ var newCallbackNode;
- if (node.child === null || node.tag === HostPortal) {
- continue siblings;
- } else {
- node.child.return = node;
- node = node.child;
+ if (includesSyncLane(newCallbackPriority)) {
+ // Special case: Sync React callbacks are scheduled on a special
+ // internal queue
+ if (root.tag === LegacyRoot) {
+ if (ReactCurrentActQueue.isBatchingLegacy !== null) {
+ ReactCurrentActQueue.didScheduleLegacyUpdate = true;
}
- } // Check if this host node is stable or about to be placed.
- if (!(node.flags & Placement)) {
- // Found it!
- return node.stateNode;
+ scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
+ } else {
+ scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}
- }
-}
-function commitPlacement(finishedWork) {
- {
- if (finishedWork.tag === HostSingleton) {
- // Singletons are already in the Host and don't need to be placed
- // Since they operate somewhat like Portals though their children will
- // have Placement and will get placed inside them
- return;
+ {
+ // Flush the queue in a microtask.
+ if (ReactCurrentActQueue.current !== null) {
+ // Inside `act`, use our internal `act` queue so that these get flushed
+ // at the end of the current scope even when using the sync version
+ // of `act`.
+ ReactCurrentActQueue.current.push(flushSyncCallbacks);
+ } else {
+ scheduleMicrotask(function () {
+ // In Safari, appending an iframe forces microtasks to run.
+ // https://github.com/facebook/react/issues/22459
+ // We don't support running callbacks in the middle of render
+ // or commit so we need to check against that.
+ if (
+ (executionContext & (RenderContext | CommitContext)) ===
+ NoContext
+ ) {
+ // Note that this would still prematurely flush the callbacks
+ // if this happens outside render or commit phase (e.g. in an event).
+ flushSyncCallbacks();
+ }
+ });
+ }
}
- } // Recursively insert all host nodes into the parent.
- var parentFiber = getHostParentFiber(finishedWork);
-
- switch (parentFiber.tag) {
- case HostSingleton: {
- {
- var parent = parentFiber.stateNode;
- var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
- // children to find all the terminal nodes.
+ newCallbackNode = null;
+ } else {
+ var schedulerPriorityLevel;
- insertOrAppendPlacementNode(finishedWork, before, parent);
+ switch (lanesToEventPriority(nextLanes)) {
+ case DiscreteEventPriority:
+ schedulerPriorityLevel = ImmediatePriority;
break;
- }
- }
- // eslint-disable-next-line no-fallthrough
-
- case HostComponent: {
- var _parent = parentFiber.stateNode;
- if (parentFiber.flags & ContentReset) {
- // Reset the text content of the parent before doing any insertions
- resetTextContent(_parent); // Clear ContentReset from the effect tag
+ case ContinuousEventPriority:
+ schedulerPriorityLevel = UserBlockingPriority;
+ break;
- parentFiber.flags &= ~ContentReset;
- }
+ case DefaultEventPriority:
+ schedulerPriorityLevel = NormalPriority$1;
+ break;
- var _before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
- // children to find all the terminal nodes.
+ case IdleEventPriority:
+ schedulerPriorityLevel = IdlePriority;
+ break;
- insertOrAppendPlacementNode(finishedWork, _before, _parent);
- break;
+ default:
+ schedulerPriorityLevel = NormalPriority$1;
+ break;
}
- case HostRoot:
- case HostPortal: {
- var _parent2 = parentFiber.stateNode.containerInfo;
+ newCallbackNode = scheduleCallback(
+ schedulerPriorityLevel,
+ performConcurrentWorkOnRoot.bind(null, root)
+ );
+ }
- var _before2 = getHostSibling(finishedWork);
+ root.callbackPriority = newCallbackPriority;
+ root.callbackNode = newCallbackNode;
+} // This is the entry point for every concurrent task, i.e. anything that
+// goes through Scheduler.
- insertOrAppendPlacementNodeIntoContainer(
- finishedWork,
- _before2,
- _parent2
- );
- break;
- }
- // eslint-disable-next-line-no-fallthrough
+function performConcurrentWorkOnRoot(root, didTimeout) {
+ {
+ resetNestedUpdateFlag();
+ } // Since we know we're in a React event, we can clear the current
+ // event time. The next update will compute a new event time.
- default:
- throw new Error(
- "Invalid host parent fiber. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- }
-}
+ currentEventTime = NoTimestamp;
+ currentEventTransitionLane = NoLanes;
-function insertOrAppendPlacementNodeIntoContainer(node, before, parent) {
- var tag = node.tag;
- var isHost = tag === HostComponent || tag === HostText;
+ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
+ throw new Error("Should not already be working.");
+ } // Flush any pending passive effects before deciding which lanes to work on,
+ // in case they schedule additional work.
- if (isHost) {
- var stateNode = node.stateNode;
+ var originalCallbackNode = root.callbackNode;
+ var didFlushPassiveEffects = flushPassiveEffects();
- if (before) {
- insertInContainerBefore(parent, stateNode, before);
- } else {
- appendChildToContainer(parent, stateNode);
+ if (didFlushPassiveEffects) {
+ // Something in the passive effect phase may have canceled the current task.
+ // Check if the task node for this root was changed.
+ if (root.callbackNode !== originalCallbackNode) {
+ // The current task was canceled. Exit. We don't need to call
+ // `ensureRootIsScheduled` because the check above implies either that
+ // there's a new task, or that there's no remaining work on this root.
+ return null;
}
- } else if (tag === HostPortal || tag === HostSingleton);
- else {
- var child = node.child;
+ } // Determine the next lanes to work on, using the fields stored
+ // on the root.
- if (child !== null) {
- insertOrAppendPlacementNodeIntoContainer(child, before, parent);
- var sibling = child.sibling;
+ var lanes = getNextLanes(
+ root,
+ root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
+ );
- while (sibling !== null) {
- insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
- sibling = sibling.sibling;
- }
- }
- }
-}
+ if (lanes === NoLanes) {
+ // Defensive coding. This is never expected to happen.
+ return null;
+ } // We disable time-slicing in some cases: if the work has been CPU-bound
+ // for too long ("expired" work, to prevent starvation), or we're in
+ // sync-updates-by-default mode.
+ // TODO: We only check `didTimeout` defensively, to account for a Scheduler
+ // bug we're still investigating. Once the bug in Scheduler is fixed,
+ // we can remove this, since we track expiration ourselves.
-function insertOrAppendPlacementNode(node, before, parent) {
- var tag = node.tag;
- var isHost = tag === HostComponent || tag === HostText;
+ var shouldTimeSlice =
+ !includesBlockingLane(root, lanes) &&
+ !includesExpiredLane(root, lanes) &&
+ (disableSchedulerTimeoutInWorkLoop || !didTimeout);
+ var exitStatus = shouldTimeSlice
+ ? renderRootConcurrent(root, lanes)
+ : renderRootSync(root, lanes);
- if (isHost) {
- var stateNode = node.stateNode;
+ if (exitStatus !== RootInProgress) {
+ if (exitStatus === RootErrored) {
+ // If something threw an error, try rendering one more time. We'll
+ // render synchronously to block concurrent data mutations, and we'll
+ // includes all pending updates are included. If it still fails after
+ // the second attempt, we'll give up and commit the resulting tree.
+ var originallyAttemptedLanes = lanes;
+ var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
+ root,
+ originallyAttemptedLanes
+ );
- if (before) {
- insertBefore(parent, stateNode, before);
- } else {
- appendChild(parent, stateNode);
+ if (errorRetryLanes !== NoLanes) {
+ lanes = errorRetryLanes;
+ exitStatus = recoverFromConcurrentError(
+ root,
+ originallyAttemptedLanes,
+ errorRetryLanes
+ );
+ }
}
- } else if (tag === HostPortal || tag === HostSingleton);
- else {
- var child = node.child;
-
- if (child !== null) {
- insertOrAppendPlacementNode(child, before, parent);
- var sibling = child.sibling;
- while (sibling !== null) {
- insertOrAppendPlacementNode(sibling, before, parent);
- sibling = sibling.sibling;
- }
+ if (exitStatus === RootFatalErrored) {
+ var fatalError = workInProgressRootFatalError;
+ prepareFreshStack(root, NoLanes);
+ markRootSuspended(root, lanes);
+ ensureRootIsScheduled(root, now$1());
+ throw fatalError;
}
- }
-} // These are tracked on the stack as we recursively traverse a
-// deleted subtree.
-// TODO: Update these during the whole mutation phase, not just during
-// a deletion.
-var hostParent = null;
-var hostParentIsContainer = false;
+ if (exitStatus === RootDidNotComplete) {
+ // The render unwound without completing the tree. This happens in special
+ // cases where need to exit the current render without producing a
+ // consistent tree or committing.
+ markRootSuspended(root, lanes);
+ } else {
+ // The render completed.
+ // Check if this render may have yielded to a concurrent event, and if so,
+ // confirm that any newly rendered stores are consistent.
+ // TODO: It's possible that even a concurrent render may never have yielded
+ // to the main thread, if it was fast enough, or if it expired. We could
+ // skip the consistency check in that case, too.
+ var renderWasConcurrent = !includesBlockingLane(root, lanes);
+ var finishedWork = root.current.alternate;
-function commitDeletionEffects(root, returnFiber, deletedFiber) {
- {
- // We only have the top Fiber that was deleted but we need to recurse down its
- // children to find all the terminal nodes.
- // Recursively delete all host nodes from the parent, detach refs, clean
- // up mounted layout effects, and call componentWillUnmount.
- // We only need to remove the topmost host child in each branch. But then we
- // still need to keep traversing to unmount effects, refs, and cWU. TODO: We
- // could split this into two separate traversals functions, where the second
- // one doesn't include any removeChild logic. This is maybe the same
- // function as "disappearLayoutEffects" (or whatever that turns into after
- // the layout phase is refactored to use recursion).
- // Before starting, find the nearest host parent on the stack so we know
- // which instance/container to remove the children from.
- // TODO: Instead of searching up the fiber return path on every deletion, we
- // can track the nearest host component on the JS stack as we traverse the
- // tree during the commit phase. This would make insertions faster, too.
- var parent = returnFiber;
+ if (
+ renderWasConcurrent &&
+ !isRenderConsistentWithExternalStores(finishedWork)
+ ) {
+ // A store was mutated in an interleaved event. Render again,
+ // synchronously, to block further mutations.
+ exitStatus = renderRootSync(root, lanes); // We need to check again if something threw
- findParent: while (parent !== null) {
- switch (parent.tag) {
- case HostSingleton:
- case HostComponent: {
- hostParent = parent.stateNode;
- hostParentIsContainer = false;
- break findParent;
- }
+ if (exitStatus === RootErrored) {
+ var _originallyAttemptedLanes = lanes;
- case HostRoot: {
- hostParent = parent.stateNode.containerInfo;
- hostParentIsContainer = true;
- break findParent;
- }
+ var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(
+ root,
+ _originallyAttemptedLanes
+ );
- case HostPortal: {
- hostParent = parent.stateNode.containerInfo;
- hostParentIsContainer = true;
- break findParent;
+ if (_errorRetryLanes !== NoLanes) {
+ lanes = _errorRetryLanes;
+ exitStatus = recoverFromConcurrentError(
+ root,
+ _originallyAttemptedLanes,
+ _errorRetryLanes
+ ); // We assume the tree is now consistent because we didn't yield to any
+ // concurrent events.
+ }
}
- }
- parent = parent.return;
- }
+ if (exitStatus === RootFatalErrored) {
+ var _fatalError = workInProgressRootFatalError;
+ prepareFreshStack(root, NoLanes);
+ markRootSuspended(root, lanes);
+ ensureRootIsScheduled(root, now$1());
+ throw _fatalError;
+ } // FIXME: Need to check for RootDidNotComplete again. The factoring here
+ // isn't ideal.
+ } // We now have a consistent tree. The next step is either to commit it,
+ // or, if something suspended, wait to commit it after a timeout.
- if (hostParent === null) {
- throw new Error(
- "Expected to find a host parent. This error is likely caused by " +
- "a bug in React. Please file an issue."
- );
+ root.finishedWork = finishedWork;
+ root.finishedLanes = lanes;
+ finishConcurrentRender(root, exitStatus, lanes);
}
-
- commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
- hostParent = null;
- hostParentIsContainer = false;
}
- detachFiberMutation(deletedFiber);
-}
-
-function recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- parent
-) {
- // TODO: Use a static flag to skip trees that don't have unmount effects
- var child = parent.child;
+ ensureRootIsScheduled(root, now$1());
- while (child !== null) {
- commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
- child = child.sibling;
+ if (root.callbackNode === originalCallbackNode) {
+ // The task node scheduled for this root is the same one that's
+ // currently executed. Need to return a continuation.
+ return performConcurrentWorkOnRoot.bind(null, root);
}
+
+ return null;
}
-function commitDeletionEffectsOnFiber(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
+function recoverFromConcurrentError(
+ root,
+ originallyAttemptedLanes,
+ errorRetryLanes
) {
- onCommitUnmount(deletedFiber); // The cases in this outer switch modify the stack before they traverse
- // into their subtree. There are simpler cases in the inner switch
- // that don't modify the stack.
-
- switch (deletedFiber.tag) {
- case HostHoistable: {
- {
- if (!offscreenSubtreeWasHidden) {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
- }
-
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
-
- if (deletedFiber.memoizedState) {
- releaseResource(deletedFiber.memoizedState);
- } else if (deletedFiber.stateNode) {
- unmountHoistable(deletedFiber.stateNode);
- }
-
- return;
- }
- }
- // eslint-disable-next-line no-fallthrough
-
- case HostSingleton: {
- {
- if (!offscreenSubtreeWasHidden) {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
- }
-
- var prevHostParent = hostParent;
- var prevHostParentIsContainer = hostParentIsContainer;
- hostParent = deletedFiber.stateNode;
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- ); // Normally this is called in passive unmount effect phase however with
- // HostSingleton we warn if you acquire one that is already associated to
- // a different fiber. To increase our chances of avoiding this, specifically
- // if you keyed a HostSingleton so there will be a delete followed by a Placement
- // we treat detach eagerly here
+ // If an error occurred during hydration, discard server response and fall
+ // back to client side render.
+ // Before rendering again, save the errors from the previous attempt.
+ var errorsFromFirstAttempt = workInProgressRootConcurrentErrors;
+ var wasRootDehydrated = isRootDehydrated(root);
- releaseSingletonInstance(deletedFiber.stateNode);
- hostParent = prevHostParent;
- hostParentIsContainer = prevHostParentIsContainer;
- return;
- }
- }
- // eslint-disable-next-line no-fallthrough
+ if (wasRootDehydrated) {
+ // The shell failed to hydrate. Set a flag to force a client rendering
+ // during the next attempt. To do this, we call prepareFreshStack now
+ // to create the root work-in-progress fiber. This is a bit weird in terms
+ // of factoring, because it relies on renderRootSync not calling
+ // prepareFreshStack again in the call below, which happens because the
+ // root and lanes haven't changed.
+ //
+ // TODO: I think what we should do is set ForceClientRender inside
+ // throwException, like we do for nested Suspense boundaries. The reason
+ // it's here instead is so we can switch to the synchronous work loop, too.
+ // Something to consider for a future refactor.
+ var rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);
+ rootWorkInProgress.flags |= ForceClientRender;
- case HostComponent: {
- if (!offscreenSubtreeWasHidden) {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
- } // Intentional fallthrough to next branch
+ {
+ errorHydratingContainer(root.containerInfo);
}
- // eslint-disable-next-line-no-fallthrough
+ }
- case HostText: {
- // We only need to remove the nearest host child. Set the host parent
- // to `null` on the stack to indicate that nested children don't
- // need to be removed.
- {
- var _prevHostParent = hostParent;
- var _prevHostParentIsContainer = hostParentIsContainer;
- hostParent = null;
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- hostParent = _prevHostParent;
- hostParentIsContainer = _prevHostParentIsContainer;
+ var exitStatus = renderRootSync(root, errorRetryLanes);
- if (hostParent !== null) {
- // Now that all the child effects have unmounted, we can remove the
- // node from the tree.
- if (hostParentIsContainer) {
- removeChildFromContainer(hostParent, deletedFiber.stateNode);
- } else {
- removeChild(hostParent, deletedFiber.stateNode);
- }
- }
- }
+ if (exitStatus !== RootErrored) {
+ // Successfully finished rendering on retry
+ if (workInProgressRootDidAttachPingListener && !wasRootDehydrated) {
+ // During the synchronous render, we attached additional ping listeners.
+ // This is highly suggestive of an uncached promise (though it's not the
+ // only reason this would happen). If it was an uncached promise, then
+ // it may have masked a downstream error from ocurring without actually
+ // fixing it. Example:
+ //
+ // use(Promise.resolve('uncached'))
+ // throw new Error('Oops!')
+ //
+ // When this happens, there's a conflict between blocking potential
+ // concurrent data races and unwrapping uncached promise values. We
+ // have to choose one or the other. Because the data race recovery is
+ // a last ditch effort, we'll disable it.
+ root.errorRecoveryDisabledLanes = mergeLanes(
+ root.errorRecoveryDisabledLanes,
+ originallyAttemptedLanes
+ ); // Mark the current render as suspended and force it to restart. Once
+ // these lanes finish successfully, we'll re-enable the error recovery
+ // mechanism for subsequent updates.
- return;
- }
+ workInProgressRootInterleavedUpdatedLanes |= originallyAttemptedLanes;
+ return RootSuspendedWithDelay;
+ } // The errors from the failed first attempt have been recovered. Add
+ // them to the collection of recoverable errors. We'll log them in the
+ // commit phase.
- case DehydratedFragment: {
- {
- var hydrationCallbacks = finishedRoot.hydrationCallbacks;
+ var errorsFromSecondAttempt = workInProgressRootRecoverableErrors;
+ workInProgressRootRecoverableErrors = errorsFromFirstAttempt; // The errors from the second attempt should be queued after the errors
+ // from the first attempt, to preserve the causal sequence.
- if (hydrationCallbacks !== null) {
- var onDeleted = hydrationCallbacks.onDeleted;
+ if (errorsFromSecondAttempt !== null) {
+ queueRecoverableErrors(errorsFromSecondAttempt);
+ }
+ }
- if (onDeleted) {
- onDeleted(deletedFiber.stateNode);
- }
- }
- } // Dehydrated fragments don't have any children
- // Delete the dehydrated suspense boundary and all of its content.
+ return exitStatus;
+}
- {
- if (hostParent !== null) {
- if (hostParentIsContainer) {
- clearSuspenseBoundaryFromContainer(
- hostParent,
- deletedFiber.stateNode
- );
- } else {
- clearSuspenseBoundary(hostParent, deletedFiber.stateNode);
- }
- }
- }
+function queueRecoverableErrors(errors) {
+ if (workInProgressRootRecoverableErrors === null) {
+ workInProgressRootRecoverableErrors = errors;
+ } else {
+ // $FlowFixMe[method-unbinding]
+ workInProgressRootRecoverableErrors.push.apply(
+ workInProgressRootRecoverableErrors,
+ errors
+ );
+ }
+}
- return;
+function finishConcurrentRender(root, exitStatus, lanes) {
+ switch (exitStatus) {
+ case RootInProgress:
+ case RootFatalErrored: {
+ throw new Error("Root did not complete. This is a bug in React.");
}
+ // Flow knows about invariant, so it complains if I add a break
+ // statement, but eslint doesn't know about invariant, so it complains
+ // if I do. eslint-disable-next-line no-fallthrough
- case HostPortal: {
- {
- // When we go into a portal, it becomes the parent to remove from.
- var _prevHostParent2 = hostParent;
- var _prevHostParentIsContainer2 = hostParentIsContainer;
- hostParent = deletedFiber.stateNode.containerInfo;
- hostParentIsContainer = true;
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- hostParent = _prevHostParent2;
- hostParentIsContainer = _prevHostParentIsContainer2;
- }
-
- return;
+ case RootErrored: {
+ // We should have already attempted to retry this tree. If we reached
+ // this point, it errored again. Commit it.
+ commitRoot(
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
+ );
+ break;
}
- case FunctionComponent:
- case ForwardRef:
- case MemoComponent:
- case SimpleMemoComponent: {
- if (!offscreenSubtreeWasHidden) {
- var updateQueue = deletedFiber.updateQueue;
+ case RootSuspended: {
+ markRootSuspended(root, lanes); // We have an acceptable loading state. We need to figure out if we
+ // should immediately commit it or wait a bit.
- if (updateQueue !== null) {
- var lastEffect = updateQueue.lastEffect;
+ if (
+ includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope
+ !shouldForceFlushFallbacksInDEV()
+ ) {
+ // This render only included retries, no updates. Throttle committing
+ // retries so that we don't show too many loading states too quickly.
+ var msUntilTimeout =
+ globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now$1(); // Don't bother with a very short suspense time.
- if (lastEffect !== null) {
- var firstEffect = lastEffect.next;
- var effect = firstEffect;
+ if (msUntilTimeout > 10) {
+ var nextLanes = getNextLanes(root, NoLanes);
- do {
- var _effect = effect,
- destroy = _effect.destroy,
- tag = _effect.tag;
+ if (nextLanes !== NoLanes) {
+ // There's additional work on this root.
+ break;
+ } // The render is suspended, it hasn't timed out, and there's no
+ // lower priority work to do. Instead of committing the fallback
+ // immediately, wait for more data to arrive.
- if (destroy !== undefined) {
- if ((tag & Insertion) !== NoFlags) {
- safelyCallDestroy(
- deletedFiber,
- nearestMountedAncestor,
- destroy
- );
- } else if ((tag & Layout) !== NoFlags) {
- if (enableSchedulingProfiler) {
- markComponentLayoutEffectUnmountStarted(deletedFiber);
- }
+ root.timeoutHandle = scheduleTimeout(
+ commitRoot.bind(
+ null,
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
+ ),
+ msUntilTimeout
+ );
+ break;
+ }
+ } // The work expired. Commit immediately.
- if (shouldProfile(deletedFiber)) {
- startLayoutEffectTimer();
- safelyCallDestroy(
- deletedFiber,
- nearestMountedAncestor,
- destroy
- );
- recordLayoutEffectDuration(deletedFiber);
- } else {
- safelyCallDestroy(
- deletedFiber,
- nearestMountedAncestor,
- destroy
- );
- }
+ commitRoot(
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
+ );
+ break;
+ }
- if (enableSchedulingProfiler) {
- markComponentLayoutEffectUnmountStopped();
- }
- }
- }
+ case RootSuspendedWithDelay: {
+ markRootSuspended(root, lanes);
- effect = effect.next;
- } while (effect !== firstEffect);
- }
- }
+ if (includesOnlyTransitions(lanes)) {
+ // This is a transition, so we should exit without committing a
+ // placeholder and without scheduling a timeout. Delay indefinitely
+ // until we receive more data.
+ break;
}
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- return;
- }
+ if (!shouldForceFlushFallbacksInDEV()) {
+ // This is not a transition, but we did trigger an avoided state.
+ // Schedule a placeholder to display after a short delay, using the Just
+ // Noticeable Difference.
+ // TODO: Is the JND optimization worth the added complexity? If this is
+ // the only reason we track the event time, then probably not.
+ // Consider removing.
+ var mostRecentEventTime = getMostRecentEventTime(root, lanes);
+ var eventTimeMs = mostRecentEventTime;
+ var timeElapsedMs = now$1() - eventTimeMs;
- case ClassComponent: {
- if (!offscreenSubtreeWasHidden) {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
- var instance = deletedFiber.stateNode;
+ var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.
- if (typeof instance.componentWillUnmount === "function") {
- safelyCallComponentWillUnmount(
- deletedFiber,
- nearestMountedAncestor,
- instance
+ if (_msUntilTimeout > 10) {
+ // Instead of committing the fallback immediately, wait for more data
+ // to arrive.
+ root.timeoutHandle = scheduleTimeout(
+ commitRoot.bind(
+ null,
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
+ ),
+ _msUntilTimeout
);
+ break;
}
- }
+ } // Commit the placeholder.
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
+ commitRoot(
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
);
- return;
+ break;
}
- case ScopeComponent: {
- {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
- }
-
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
+ case RootCompleted: {
+ // The work completed. Ready to commit.
+ commitRoot(
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
);
- return;
- }
-
- case OffscreenComponent: {
- safelyDetachRef(deletedFiber, nearestMountedAncestor);
-
- if (deletedFiber.mode & ConcurrentMode) {
- // If this offscreen component is hidden, we already unmounted it. Before
- // deleting the children, track that it's already unmounted so that we
- // don't attempt to unmount the effects again.
- // TODO: If the tree is hidden, in most cases we should be able to skip
- // over the nested children entirely. An exception is we haven't yet found
- // the topmost host node to delete, which we already track on the stack.
- // But the other case is portals, which need to be detached no matter how
- // deeply they are nested. We should use a subtree flag to track whether a
- // subtree includes a nested portal.
- var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
- offscreenSubtreeWasHidden =
- prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
- } else {
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- }
-
break;
}
default: {
- recursivelyTraverseDeletionEffects(
- finishedRoot,
- nearestMountedAncestor,
- deletedFiber
- );
- return;
+ throw new Error("Unknown root exit status.");
}
}
}
-function commitSuspenseCallback(finishedWork) {
- // TODO: Move this to passive phase
- var newState = finishedWork.memoizedState;
+function isRenderConsistentWithExternalStores(finishedWork) {
+ // Search the rendered tree for external store reads, and check whether the
+ // stores were mutated in a concurrent event. Intentionally using an iterative
+ // loop instead of recursion so we can exit early.
+ var node = finishedWork;
- if (newState !== null) {
- var suspenseCallback = finishedWork.memoizedProps.suspenseCallback;
+ while (true) {
+ if (node.flags & StoreConsistency) {
+ var updateQueue = node.updateQueue;
- if (typeof suspenseCallback === "function") {
- var wakeables = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ var checks = updateQueue.stores;
- if (wakeables !== null) {
- suspenseCallback(new Set(wakeables));
- }
- } else {
- if (suspenseCallback !== undefined) {
- error("Unexpected type for suspenseCallback.");
+ if (checks !== null) {
+ for (var i = 0; i < checks.length; i++) {
+ var check = checks[i];
+ var getSnapshot = check.getSnapshot;
+ var renderedValue = check.value;
+
+ try {
+ if (!objectIs(getSnapshot(), renderedValue)) {
+ // Found an inconsistent store.
+ return false;
+ }
+ } catch (error) {
+ // If `getSnapshot` throws, return `false`. This will schedule
+ // a re-render, and the error will be rethrown during render.
+ return false;
+ }
+ }
+ }
}
}
- }
-}
-function commitSuspenseHydrationCallbacks(finishedRoot, finishedWork) {
- var newState = finishedWork.memoizedState;
-
- if (newState === null) {
- var current = finishedWork.alternate;
+ var child = node.child;
- if (current !== null) {
- var prevState = current.memoizedState;
+ if (node.subtreeFlags & StoreConsistency && child !== null) {
+ child.return = node;
+ node = child;
+ continue;
+ }
- if (prevState !== null) {
- var suspenseInstance = prevState.dehydrated;
+ if (node === finishedWork) {
+ return true;
+ }
- if (suspenseInstance !== null) {
- try {
- commitHydratedSuspenseInstance(suspenseInstance);
+ while (node.sibling === null) {
+ if (node.return === null || node.return === finishedWork) {
+ return true;
+ }
- if (enableSuspenseCallback) {
- var hydrationCallbacks = finishedRoot.hydrationCallbacks;
+ node = node.return;
+ }
- if (hydrationCallbacks !== null) {
- var onHydrated = hydrationCallbacks.onHydrated;
+ node.sibling.return = node.return;
+ node = node.sibling;
+ } // Flow doesn't know this is unreachable, but eslint does
+ // eslint-disable-next-line no-unreachable
- if (onHydrated) {
- onHydrated(suspenseInstance);
- }
- }
- }
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
- }
- }
- }
+ return true;
}
-function getRetryCache(finishedWork) {
- // TODO: Unify the interface for the retry cache so we don't have to switch
- // on the tag like this.
- switch (finishedWork.tag) {
- case SuspenseComponent:
- case SuspenseListComponent: {
- var retryCache = finishedWork.stateNode;
+function markRootSuspended(root, suspendedLanes) {
+ // When suspending, we should always exclude lanes that were pinged or (more
+ // rarely, since we try to avoid it) updated during the render phase.
+ // TODO: Lol maybe there's a better way to factor this besides this
+ // obnoxiously named function :)
+ suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
+ suspendedLanes = removeLanes(
+ suspendedLanes,
+ workInProgressRootInterleavedUpdatedLanes
+ );
+ markRootSuspended$1(root, suspendedLanes);
+} // This is the entry point for synchronous tasks that don't go
+// through Scheduler
- if (retryCache === null) {
- retryCache = finishedWork.stateNode = new PossiblyWeakSet();
- }
+function performSyncWorkOnRoot(root) {
+ {
+ syncNestedUpdateFlag();
+ }
- return retryCache;
- }
+ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
+ throw new Error("Should not already be working.");
+ }
- case OffscreenComponent: {
- var instance = finishedWork.stateNode;
- var _retryCache = instance._retryCache;
+ flushPassiveEffects();
+ var lanes = getNextLanes(root, NoLanes);
- if (_retryCache === null) {
- _retryCache = instance._retryCache = new PossiblyWeakSet();
- }
+ if (!includesSyncLane(lanes)) {
+ // There's no remaining sync work left.
+ ensureRootIsScheduled(root, now$1());
+ return null;
+ }
- return _retryCache;
- }
+ var exitStatus = renderRootSync(root, lanes);
- default: {
- throw new Error(
- "Unexpected Suspense handler tag (" +
- finishedWork.tag +
- "). This is a " +
- "bug in React."
+ if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
+ // If something threw an error, try rendering one more time. We'll render
+ // synchronously to block concurrent data mutations, and we'll includes
+ // all pending updates are included. If it still fails after the second
+ // attempt, we'll give up and commit the resulting tree.
+ var originallyAttemptedLanes = lanes;
+ var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
+ root,
+ originallyAttemptedLanes
+ );
+
+ if (errorRetryLanes !== NoLanes) {
+ lanes = errorRetryLanes;
+ exitStatus = recoverFromConcurrentError(
+ root,
+ originallyAttemptedLanes,
+ errorRetryLanes
);
}
}
+
+ if (exitStatus === RootFatalErrored) {
+ var fatalError = workInProgressRootFatalError;
+ prepareFreshStack(root, NoLanes);
+ markRootSuspended(root, lanes);
+ ensureRootIsScheduled(root, now$1());
+ throw fatalError;
+ }
+
+ if (exitStatus === RootDidNotComplete) {
+ // The render unwound without completing the tree. This happens in special
+ // cases where need to exit the current render without producing a
+ // consistent tree or committing.
+ markRootSuspended(root, lanes);
+ ensureRootIsScheduled(root, now$1());
+ return null;
+ } // We now have a consistent tree. Because this is a sync render, we
+ // will commit it even if something suspended.
+
+ var finishedWork = root.current.alternate;
+ root.finishedWork = finishedWork;
+ root.finishedLanes = lanes;
+ commitRoot(
+ root,
+ workInProgressRootRecoverableErrors,
+ workInProgressTransitions
+ ); // Before exiting, make sure there's a callback scheduled for the next
+ // pending level.
+
+ ensureRootIsScheduled(root, now$1());
+ return null;
}
-function detachOffscreenInstance(instance) {
- var fiber = instance._current;
+function flushRoot(root, lanes) {
+ if (lanes !== NoLanes) {
+ markRootEntangled(root, mergeLanes(lanes, SyncLane));
+ ensureRootIsScheduled(root, now$1());
- if (fiber === null) {
- throw new Error(
- "Calling Offscreen.detach before instance handle has been set."
- );
+ if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
+ resetRenderTimer();
+ flushSyncCallbacks();
+ }
}
+}
+function getExecutionContext() {
+ return executionContext;
+}
+function batchedUpdates$1(fn, a) {
+ var prevExecutionContext = executionContext;
+ executionContext |= BatchedContext;
- if ((instance._pendingVisibility & OffscreenDetached) !== NoFlags$1) {
- // The instance is already detached, this is a noop.
- return;
- } // TODO: There is an opportunity to optimise this by not entering commit phase
- // and unmounting effects directly.
-
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ try {
+ return fn(a);
+ } finally {
+ executionContext = prevExecutionContext; // If there were legacy sync updates, flush them at the end of the outer
+ // most batchedUpdates-like method.
- if (root !== null) {
- instance._pendingVisibility |= OffscreenDetached;
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ if (
+ executionContext === NoContext && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
+ !ReactCurrentActQueue.isBatchingLegacy
+ ) {
+ resetRenderTimer();
+ flushSyncCallbacksOnlyInLegacyMode();
+ }
}
}
-function attachOffscreenInstance(instance) {
- var fiber = instance._current;
+// Warning, this opts-out of checking the function body.
+// eslint-disable-next-line no-unused-vars
+// eslint-disable-next-line no-redeclare
+// eslint-disable-next-line no-redeclare
- if (fiber === null) {
- throw new Error(
- "Calling Offscreen.detach before instance handle has been set."
- );
+function flushSync$1(fn) {
+ // In legacy mode, we flush pending passive effects at the beginning of the
+ // next event, not at the end of the previous one.
+ if (
+ rootWithPendingPassiveEffects !== null &&
+ rootWithPendingPassiveEffects.tag === LegacyRoot &&
+ (executionContext & (RenderContext | CommitContext)) === NoContext
+ ) {
+ flushPassiveEffects();
}
- if ((instance._pendingVisibility & OffscreenDetached) === NoFlags$1) {
- // The instance is already attached, this is a noop.
- return;
- }
+ var prevExecutionContext = executionContext;
+ executionContext |= BatchedContext;
+ var prevTransition = ReactCurrentBatchConfig$1.transition;
+ var previousPriority = getCurrentUpdatePriority();
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ try {
+ ReactCurrentBatchConfig$1.transition = null;
+ setCurrentUpdatePriority(DiscreteEventPriority);
- if (root !== null) {
- instance._pendingVisibility &= ~OffscreenDetached;
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ if (fn) {
+ return fn();
+ } else {
+ return undefined;
+ }
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig$1.transition = prevTransition;
+ executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.
+ // Note that this will happen even if batchedUpdates is higher up
+ // the stack.
+
+ if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
+ flushSyncCallbacks();
+ }
}
}
+function isAlreadyRendering() {
+ // Used by the renderer to print a warning if certain APIs are called from
+ // the wrong context.
+ return (executionContext & (RenderContext | CommitContext)) !== NoContext;
+}
+function isInvalidExecutionContextForEventFunction() {
+ // Used to throw if certain APIs are called from the wrong context.
+ return (executionContext & RenderContext) !== NoContext;
+}
+function flushControlled(fn) {
+ var prevExecutionContext = executionContext;
+ executionContext |= BatchedContext;
+ var prevTransition = ReactCurrentBatchConfig$1.transition;
+ var previousPriority = getCurrentUpdatePriority();
-function attachSuspenseRetryListeners(finishedWork, wakeables) {
- // If this boundary just timed out, then it will have a set of wakeables.
- // For each wakeable, attach a listener so that when it resolves, React
- // attempts to re-render the boundary in the primary (pre-timeout) state.
- var retryCache = getRetryCache(finishedWork);
- wakeables.forEach(function (wakeable) {
- // Memoize using the boundary fiber to prevent redundant listeners.
- var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
+ try {
+ ReactCurrentBatchConfig$1.transition = null;
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ fn();
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig$1.transition = prevTransition;
+ executionContext = prevExecutionContext;
- if (!retryCache.has(wakeable)) {
- retryCache.add(wakeable);
+ if (executionContext === NoContext) {
+ // Flush the immediate callbacks that were scheduled during this batch
+ resetRenderTimer();
+ flushSyncCallbacks();
+ }
+ }
+} // This is called by the HiddenContext module when we enter or leave a
+// hidden subtree. The stack logic is managed there because that's the only
+// place that ever modifies it. Which module it lives in doesn't matter for
+// performance because this function will get inlined regardless
- {
- if (isDevToolsPresent) {
- if (inProgressLanes !== null && inProgressRoot !== null) {
- // If we have pending work still, associate the original updaters with it.
- restorePendingUpdaters(inProgressRoot, inProgressLanes);
- } else {
- throw Error(
- "Expected finished root and lanes to be set. This is a bug in React."
- );
- }
- }
- }
+function setRenderLanes(subtreeRenderLanes) {
+ renderLanes = subtreeRenderLanes;
+}
+function getRenderLanes() {
+ return renderLanes;
+}
- wakeable.then(retry, retry);
- }
- });
-} // This function detects when a Suspense boundary goes from visible to hidden.
-// It returns false if the boundary is already hidden.
-// TODO: Use an effect tag.
+function resetWorkInProgressStack() {
+ if (workInProgress === null) return;
+ var interruptedWork;
-function isSuspenseBoundaryBeingHidden(current, finishedWork) {
- if (current !== null) {
- var oldState = current.memoizedState;
+ if (workInProgressSuspendedReason === NotSuspended) {
+ // Normal case. Work-in-progress hasn't started yet. Unwind all
+ // its parents.
+ interruptedWork = workInProgress.return;
+ } else {
+ // Work-in-progress is in suspended state. Reset the work loop and unwind
+ // both the suspended fiber and all its parents.
+ resetSuspendedWorkLoopOnUnwind();
+ interruptedWork = workInProgress;
+ }
- if (oldState === null || oldState.dehydrated !== null) {
- var newState = finishedWork.memoizedState;
- return newState !== null && newState.dehydrated === null;
- }
+ while (interruptedWork !== null) {
+ var current = interruptedWork.alternate;
+ unwindInterruptedWork(current, interruptedWork);
+ interruptedWork = interruptedWork.return;
}
- return false;
-}
-function commitMutationEffects(root, finishedWork, committedLanes) {
- inProgressLanes = committedLanes;
- inProgressRoot = root;
- setCurrentFiber(finishedWork);
- commitMutationEffectsOnFiber(finishedWork, root);
- setCurrentFiber(finishedWork);
- inProgressLanes = null;
- inProgressRoot = null;
+ workInProgress = null;
}
-function recursivelyTraverseMutationEffects(root, parentFiber, lanes) {
- // Deletions effects can be scheduled on any fiber type. They need to happen
- // before the children effects hae fired.
- var deletions = parentFiber.deletions;
+function prepareFreshStack(root, lanes) {
+ root.finishedWork = null;
+ root.finishedLanes = NoLanes;
+ var timeoutHandle = root.timeoutHandle;
- if (deletions !== null) {
- for (var i = 0; i < deletions.length; i++) {
- var childToDelete = deletions[i];
+ if (timeoutHandle !== noTimeout) {
+ // The root previous suspended and scheduled a timeout to commit a fallback
+ // state. Now that we have additional work, cancel the timeout.
+ root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
- try {
- commitDeletionEffects(root, parentFiber, childToDelete);
- } catch (error) {
- captureCommitPhaseError(childToDelete, parentFiber, error);
- }
- }
+ cancelTimeout(timeoutHandle);
}
- var prevDebugFiber = getCurrentFiber();
-
- if (parentFiber.subtreeFlags & MutationMask) {
- var child = parentFiber.child;
+ resetWorkInProgressStack();
+ workInProgressRoot = root;
+ var rootWorkInProgress = createWorkInProgress(root.current, null);
+ workInProgress = rootWorkInProgress;
+ workInProgressRootRenderLanes = renderLanes = lanes;
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ workInProgressRootDidAttachPingListener = false;
+ workInProgressRootExitStatus = RootInProgress;
+ workInProgressRootFatalError = null;
+ workInProgressRootSkippedLanes = NoLanes;
+ workInProgressRootInterleavedUpdatedLanes = NoLanes;
+ workInProgressRootPingedLanes = NoLanes;
+ workInProgressRootConcurrentErrors = null;
+ workInProgressRootRecoverableErrors = null;
+ finishQueueingConcurrentUpdates();
- while (child !== null) {
- setCurrentFiber(child);
- commitMutationEffectsOnFiber(child, root);
- child = child.sibling;
- }
+ {
+ ReactStrictModeWarnings.discardPendingWarnings();
}
- setCurrentFiber(prevDebugFiber);
+ return rootWorkInProgress;
}
-var currentHoistableRoot = null;
-
-function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
- var current = finishedWork.alternate;
- var flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber,
- // because the fiber tag is more specific. An exception is any flag related
- // to reconciliation, because those can be set on all fiber types.
+function resetSuspendedWorkLoopOnUnwind() {
+ // Reset module-level state that was set during the render phase.
+ resetContextDependencies();
+ resetHooksOnUnwind();
+ resetChildReconcilerOnUnwind();
+}
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case MemoComponent:
- case SimpleMemoComponent: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
+function handleThrow(root, thrownValue) {
+ // A component threw an exception. Usually this is because it suspended, but
+ // it also includes regular program errors.
+ //
+ // We're either going to unwind the stack to show a Suspense or error
+ // boundary, or we're going to replay the component again. Like after a
+ // promise resolves.
+ //
+ // Until we decide whether we're going to unwind or replay, we should preserve
+ // the current state of the work loop without resetting anything.
+ //
+ // If we do decide to unwind the stack, module-level variables will be reset
+ // in resetSuspendedWorkLoopOnUnwind.
+ // These should be reset immediately because they're only supposed to be set
+ // when React is executing user code.
+ resetHooksAfterThrow();
+ resetCurrentFiber();
+ ReactCurrentOwner$1.current = null;
- if (flags & Update) {
- try {
- commitHookEffectListUnmount(
- Insertion | HasEffect,
- finishedWork,
- finishedWork.return
- );
- commitHookEffectListMount(Insertion | HasEffect, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- } // Layout effects are destroyed during the mutation phase so that all
- // destroy functions for all fibers are called before any create functions.
- // This prevents sibling component effects from interfering with each other,
- // e.g. a destroy function in one component should never override a ref set
- // by a create function in another component during the same commit.
+ if (thrownValue === SuspenseException) {
+ // This is a special type of exception used for Suspense. For historical
+ // reasons, the rest of the Suspense implementation expects the thrown value
+ // to be a thenable, because before `use` existed that was the (unstable)
+ // API for suspending. This implementation detail can change later, once we
+ // deprecate the old API in favor of `use`.
+ thrownValue = getSuspendedThenable();
+ workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves()
+ ? SuspendedOnData
+ : SuspendedOnImmediate;
+ } else if (thrownValue === SelectiveHydrationException) {
+ // An update flowed into a dehydrated boundary. Before we can apply the
+ // update, we need to finish hydrating. Interrupt the work-in-progress
+ // render so we can restart at the hydration lane.
+ //
+ // The ideal implementation would be able to switch contexts without
+ // unwinding the current stack.
+ //
+ // We could name this something more general but as of now it's the only
+ // case where we think this should happen.
+ workInProgressSuspendedReason = SuspendedOnHydration;
+ } else {
+ // This is a regular error.
+ var isWakeable =
+ thrownValue !== null &&
+ typeof thrownValue === "object" &&
+ typeof thrownValue.then === "function";
+ workInProgressSuspendedReason = isWakeable // A wakeable object was thrown by a legacy Suspense implementation.
+ ? // This has slightly different behavior than suspending with `use`.
+ SuspendedOnDeprecatedThrowPromise // This is a regular error. If something earlier in the component already
+ : // suspended, we must clear the thenable state to unblock the work loop.
+ SuspendedOnError;
+ }
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- commitHookEffectListUnmount(
- Layout | HasEffect,
- finishedWork,
- finishedWork.return
- );
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
+ workInProgressThrownValue = thrownValue;
+ var erroredWork = workInProgress;
- recordLayoutEffectDuration(finishedWork);
- } else {
- try {
- commitHookEffectListUnmount(
- Layout | HasEffect,
- finishedWork,
- finishedWork.return
- );
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
- }
+ if (erroredWork === null) {
+ // This is a fatal error
+ workInProgressRootExitStatus = RootFatalErrored;
+ workInProgressRootFatalError = thrownValue;
+ return;
+ }
- return;
- }
+ if (erroredWork.mode & ProfileMode) {
+ // Record the time spent rendering before an error was thrown. This
+ // avoids inaccurate Profiler durations in the case of a
+ // suspended render.
+ stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);
+ }
- case ClassComponent: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
+ if (enableSchedulingProfiler) {
+ markComponentRenderStopped();
- if (flags & Ref) {
- if (current !== null) {
- safelyDetachRef(current, current.return);
- }
+ switch (workInProgressSuspendedReason) {
+ case SuspendedOnError: {
+ markComponentErrored(
+ erroredWork,
+ thrownValue,
+ workInProgressRootRenderLanes
+ );
+ break;
}
- if (flags & Callback && offscreenSubtreeIsHidden) {
- var updateQueue = finishedWork.updateQueue;
-
- if (updateQueue !== null) {
- deferHiddenCallbacks(updateQueue);
- }
+ case SuspendedOnData:
+ case SuspendedOnImmediate:
+ case SuspendedOnDeprecatedThrowPromise:
+ case SuspendedAndReadyToContinue: {
+ var wakeable = thrownValue;
+ markComponentSuspended(
+ erroredWork,
+ wakeable,
+ workInProgressRootRenderLanes
+ );
+ break;
}
-
- return;
}
+ }
+}
- case HostHoistable: {
- {
- // We cast because we always set the root at the React root and so it cannot be
- // null while we are processing mutation effects
- var hoistableRoot = currentHoistableRoot;
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
-
- if (flags & Ref) {
- if (current !== null) {
- safelyDetachRef(current, current.return);
- }
- }
+function shouldAttemptToSuspendUntilDataResolves() {
+ // Check if there are other pending updates that might possibly unblock this
+ // component from suspending. This mirrors the check in
+ // renderDidSuspendDelayIfPossible. We should attempt to unify them somehow.
+ // TODO: Consider unwinding immediately, using the
+ // SuspendedOnHydration mechanism.
+ if (
+ includesNonIdleWork(workInProgressRootSkippedLanes) ||
+ includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes)
+ ) {
+ // Suspend normally. renderDidSuspendDelayIfPossible will handle
+ // interrupting the work loop.
+ return false;
+ } // TODO: We should be able to remove the equivalent check in
+ // finishConcurrentRender, and rely just on this one.
- if (flags & Update) {
- var currentResource = current !== null ? current.memoizedState : null;
- var newResource = finishedWork.memoizedState;
+ if (includesOnlyTransitions(workInProgressRootRenderLanes)) {
+ // If we're rendering inside the "shell" of the app, it's better to suspend
+ // rendering and wait for the data to resolve. Otherwise, we should switch
+ // to a fallback and continue rendering.
+ return getShellBoundary() === null;
+ }
- if (current === null) {
- // We are mounting a new HostHoistable Fiber. We fork the mount
- // behavior based on whether this instance is a Hoistable Instance
- // or a Hoistable Resource
- if (newResource === null) {
- if (finishedWork.stateNode === null) {
- finishedWork.stateNode = hydrateHoistable(
- hoistableRoot,
- finishedWork.type,
- finishedWork.memoizedProps,
- finishedWork
- );
- } else {
- mountHoistable(
- hoistableRoot,
- finishedWork.type,
- finishedWork.stateNode
- );
- }
- } else {
- finishedWork.stateNode = acquireResource(
- hoistableRoot,
- newResource,
- finishedWork.memoizedProps
- );
- }
- } else if (currentResource !== newResource) {
- // We are moving to or from Hoistable Resource, or between different Hoistable Resources
- if (currentResource === null) {
- if (current.stateNode !== null) {
- unmountHoistable(current.stateNode);
- }
- } else {
- releaseResource(currentResource);
- }
+ var handler = getSuspenseHandler();
- if (newResource === null) {
- mountHoistable(
- hoistableRoot,
- finishedWork.type,
- finishedWork.stateNode
- );
- } else {
- acquireResource(
- hoistableRoot,
- newResource,
- finishedWork.memoizedProps
- );
- }
- } else if (newResource === null && finishedWork.stateNode !== null) {
- // We may have an update on a Hoistable element
- var updatePayload = finishedWork.updateQueue;
- finishedWork.updateQueue = null;
+ if (handler === null);
+ else {
+ if (includesOnlyRetries(workInProgressRootRenderLanes)) {
+ // During a retry, we can suspend rendering if the nearest Suspense boundary
+ // is the boundary of the "shell", because we're guaranteed not to block
+ // any new content from appearing.
+ return handler === getShellBoundary();
+ }
+ } // For all other Lanes besides Transitions and Retries, we should not wait
+ // for the data to load.
+ // TODO: We should wait during Offscreen prerendering, too.
- if (updatePayload !== null) {
- try {
- commitUpdate(
- finishedWork.stateNode,
- updatePayload,
- finishedWork.type,
- current.memoizedProps,
- finishedWork.memoizedProps,
- finishedWork
- );
- } catch (error) {
- captureCommitPhaseError(
- finishedWork,
- finishedWork.return,
- error
- );
- }
- }
- }
- }
+ return false;
+}
- return;
- }
- }
- // eslint-disable-next-line-no-fallthrough
+function pushDispatcher(container) {
+ prepareRendererToRender(container);
+ var prevDispatcher = ReactCurrentDispatcher.current;
+ ReactCurrentDispatcher.current = ContextOnlyDispatcher;
- case HostSingleton: {
- {
- if (flags & Update) {
- var previousWork = finishedWork.alternate;
+ if (prevDispatcher === null) {
+ // The React isomorphic package does not include a default dispatcher.
+ // Instead the first renderer will lazily attach one, in order to give
+ // nicer error messages.
+ return ContextOnlyDispatcher;
+ } else {
+ return prevDispatcher;
+ }
+}
- if (previousWork === null) {
- var singleton = finishedWork.stateNode;
- var props = finishedWork.memoizedProps; // This was a new mount, we need to clear and set initial properties
+function popDispatcher(prevDispatcher) {
+ resetRendererAfterRender();
+ ReactCurrentDispatcher.current = prevDispatcher;
+}
- clearSingleton(singleton);
- acquireSingletonInstance(
- finishedWork.type,
- props,
- singleton,
- finishedWork
- );
- }
- }
- }
- }
- // eslint-disable-next-line-no-fallthrough
+function pushCacheDispatcher() {
+ {
+ var prevCacheDispatcher = ReactCurrentCache.current;
+ ReactCurrentCache.current = DefaultCacheDispatcher;
+ return prevCacheDispatcher;
+ }
+}
- case HostComponent: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
+function popCacheDispatcher(prevCacheDispatcher) {
+ {
+ ReactCurrentCache.current = prevCacheDispatcher;
+ }
+}
- if (flags & Ref) {
- if (current !== null) {
- safelyDetachRef(current, current.return);
- }
- }
+function markCommitTimeOfFallback() {
+ globalMostRecentFallbackTime = now$1();
+}
+function markSkippedUpdateLanes(lane) {
+ workInProgressRootSkippedLanes = mergeLanes(
+ lane,
+ workInProgressRootSkippedLanes
+ );
+}
+function renderDidSuspend() {
+ if (workInProgressRootExitStatus === RootInProgress) {
+ workInProgressRootExitStatus = RootSuspended;
+ }
+}
+function renderDidSuspendDelayIfPossible() {
+ workInProgressRootExitStatus = RootSuspendedWithDelay; // Check if there are updates that we skipped tree that might have unblocked
+ // this render.
- {
- // TODO: ContentReset gets cleared by the children during the commit
- // phase. This is a refactor hazard because it means we must read
- // flags the flags after `commitReconciliationEffects` has already run;
- // the order matters. We should refactor so that ContentReset does not
- // rely on mutating the flag during commit. Like by setting a flag
- // during the render phase instead.
- if (finishedWork.flags & ContentReset) {
- var instance = finishedWork.stateNode;
+ if (
+ workInProgressRoot !== null &&
+ (includesNonIdleWork(workInProgressRootSkippedLanes) ||
+ includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))
+ ) {
+ // Mark the current render as suspended so that we switch to working on
+ // the updates that were skipped. Usually we only suspend at the end of
+ // the render phase.
+ // TODO: We should probably always mark the root as suspended immediately
+ // (inside this function), since by suspending at the end of the render
+ // phase introduces a potential mistake where we suspend lanes that were
+ // pinged or updated while we were rendering.
+ // TODO: Consider unwinding immediately, using the
+ // SuspendedOnHydration mechanism.
+ // $FlowFixMe[incompatible-call] need null check workInProgressRoot
+ markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);
+ }
+}
+function renderDidError(error) {
+ if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {
+ workInProgressRootExitStatus = RootErrored;
+ }
- try {
- resetTextContent(instance);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
+ if (workInProgressRootConcurrentErrors === null) {
+ workInProgressRootConcurrentErrors = [error];
+ } else {
+ workInProgressRootConcurrentErrors.push(error);
+ }
+} // Called during render to determine if anything has suspended.
+// Returns false if we're not sure.
- if (flags & Update) {
- var _instance2 = finishedWork.stateNode;
+function renderHasNotSuspendedYet() {
+ // If something errored or completed, we can't really be sure,
+ // so those are false.
+ return workInProgressRootExitStatus === RootInProgress;
+} // TODO: Over time, this function and renderRootConcurrent have become more
+// and more similar. Not sure it makes sense to maintain forked paths. Consider
+// unifying them again.
- if (_instance2 != 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.
+function renderRootSync(root, lanes) {
+ var prevExecutionContext = executionContext;
+ executionContext |= RenderContext;
+ var prevDispatcher = pushDispatcher(root.containerInfo);
+ var prevCacheDispatcher = pushCacheDispatcher(); // If the root or lanes have changed, throw out the existing stack
+ // and prepare a fresh one. Otherwise we'll continue where we left off.
- var oldProps = current !== null ? current.memoizedProps : newProps;
- var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components.
+ if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
+ {
+ if (isDevToolsPresent) {
+ var memoizedUpdaters = root.memoizedUpdaters;
- var _updatePayload = finishedWork.updateQueue;
- finishedWork.updateQueue = null;
+ if (memoizedUpdaters.size > 0) {
+ restorePendingUpdaters(root, workInProgressRootRenderLanes);
+ memoizedUpdaters.clear();
+ } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
+ // If we bailout on this work, we'll move them back (like above).
+ // It's important to move them now in case the work spawns more work at the same priority with different updaters.
+ // That way we can keep the current update and future updates separate.
- if (_updatePayload !== null) {
- try {
- commitUpdate(
- _instance2,
- _updatePayload,
- type,
- oldProps,
- newProps,
- finishedWork
- );
- } catch (error) {
- captureCommitPhaseError(
- finishedWork,
- finishedWork.return,
- error
- );
- }
- }
- }
- }
+ movePendingFibersToMemoized(root, lanes);
}
-
- return;
}
- case HostText: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
+ workInProgressTransitions = getTransitionsForLanes(root, lanes);
+ prepareFreshStack(root, lanes);
+ }
- if (flags & Update) {
- {
- if (finishedWork.stateNode === null) {
- throw new Error(
- "This should have a text node initialized. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
- }
+ {
+ if (enableDebugTracing) {
+ logRenderStarted(lanes);
+ }
+ }
- 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.
+ if (enableSchedulingProfiler) {
+ markRenderStarted(lanes);
+ }
- var oldText = current !== null ? current.memoizedProps : newText;
+ outer: do {
+ try {
+ if (
+ workInProgressSuspendedReason !== NotSuspended &&
+ workInProgress !== null
+ ) {
+ // The work loop is suspended. During a synchronous render, we don't
+ // yield to the main thread. Immediately unwind the stack. This will
+ // trigger either a fallback or an error boundary.
+ // TODO: For discrete and "default" updates (anything that's not
+ // flushSync), we want to wait for the microtasks the flush before
+ // unwinding. Will probably implement this using renderRootConcurrent,
+ // or merge renderRootSync and renderRootConcurrent into the same
+ // function and fork the behavior some other way.
+ var unitOfWork = workInProgress;
+ var thrownValue = workInProgressThrownValue;
- try {
- commitTextUpdate(textInstance, oldText, newText);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
+ switch (workInProgressSuspendedReason) {
+ case SuspendedOnHydration: {
+ // Selective hydration. An update flowed into a dehydrated tree.
+ // Interrupt the current render so the work loop can switch to the
+ // hydration lane.
+ resetWorkInProgressStack();
+ workInProgressRootExitStatus = RootDidNotComplete;
+ break outer;
+ }
+
+ default: {
+ // Continue with the normal work loop.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
+ break;
}
}
}
- return;
+ workLoopSync();
+ break;
+ } catch (thrownValue) {
+ handleThrow(root, thrownValue);
}
+ } while (true);
- case HostRoot: {
- {
- prepareToCommitHoistables();
- var previousHoistableRoot = currentHoistableRoot;
- currentHoistableRoot = getHoistableRoot(root.containerInfo);
- recursivelyTraverseMutationEffects(root, finishedWork);
- currentHoistableRoot = previousHoistableRoot;
- commitReconciliationEffects(finishedWork);
- }
-
- if (flags & Update) {
- {
- if (current !== null) {
- var prevRootState = current.memoizedState;
+ resetContextDependencies();
+ executionContext = prevExecutionContext;
+ popDispatcher(prevDispatcher);
+ popCacheDispatcher(prevCacheDispatcher);
- if (prevRootState.isDehydrated) {
- try {
- commitHydratedContainer(root.containerInfo);
- } catch (error) {
- captureCommitPhaseError(
- finishedWork,
- finishedWork.return,
- error
- );
- }
- }
- }
- }
- }
+ if (workInProgress !== null) {
+ // This is a sync render, so we should have finished the whole tree.
+ throw new Error(
+ "Cannot commit an incomplete root. This error is likely caused by a " +
+ "bug in React. Please file an issue."
+ );
+ }
- return;
+ {
+ if (enableDebugTracing) {
+ logRenderStopped();
}
+ }
- case HostPortal: {
- {
- var _previousHoistableRoot = currentHoistableRoot;
- currentHoistableRoot = getHoistableRoot(
- finishedWork.stateNode.containerInfo
- );
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
- currentHoistableRoot = _previousHoistableRoot;
- }
+ if (enableSchedulingProfiler) {
+ markRenderStopped();
+ } // Set this to null to indicate there's no in-progress render.
- return;
- }
+ workInProgressRoot = null;
+ workInProgressRootRenderLanes = NoLanes; // It's safe to process the queue now that the render phase is complete.
- case SuspenseComponent: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
- var offscreenFiber = finishedWork.child;
+ finishQueueingConcurrentUpdates();
+ return workInProgressRootExitStatus;
+} // The work loop is an extremely hot path. Tell Closure not to inline it.
- if (offscreenFiber.flags & Visibility) {
- var newState = offscreenFiber.memoizedState;
- var isHidden = newState !== null;
+/** @noinline */
- if (isHidden) {
- var wasHidden =
- offscreenFiber.alternate !== null &&
- offscreenFiber.alternate.memoizedState !== null;
+function workLoopSync() {
+ // Perform work without checking if we need to yield between fiber.
+ while (workInProgress !== null) {
+ performUnitOfWork(workInProgress);
+ }
+}
- if (!wasHidden) {
- // TODO: Move to passive phase
- markCommitTimeOfFallback();
- }
- }
- }
+function renderRootConcurrent(root, lanes) {
+ var prevExecutionContext = executionContext;
+ executionContext |= RenderContext;
+ var prevDispatcher = pushDispatcher(root.containerInfo);
+ var prevCacheDispatcher = pushCacheDispatcher(); // If the root or lanes have changed, throw out the existing stack
+ // and prepare a fresh one. Otherwise we'll continue where we left off.
- if (flags & Update) {
- try {
- commitSuspenseCallback(finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
+ if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
+ {
+ if (isDevToolsPresent) {
+ var memoizedUpdaters = root.memoizedUpdaters;
- var wakeables = finishedWork.updateQueue;
+ if (memoizedUpdaters.size > 0) {
+ restorePendingUpdaters(root, workInProgressRootRenderLanes);
+ memoizedUpdaters.clear();
+ } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
+ // If we bailout on this work, we'll move them back (like above).
+ // It's important to move them now in case the work spawns more work at the same priority with different updaters.
+ // That way we can keep the current update and future updates separate.
- if (wakeables !== null) {
- finishedWork.updateQueue = null;
- attachSuspenseRetryListeners(finishedWork, wakeables);
- }
+ movePendingFibersToMemoized(root, lanes);
}
+ }
- return;
+ workInProgressTransitions = getTransitionsForLanes(root, lanes);
+ resetRenderTimer();
+ prepareFreshStack(root, lanes);
+ }
+
+ {
+ if (enableDebugTracing) {
+ logRenderStarted(lanes);
}
+ }
- case OffscreenComponent: {
- if (flags & Ref) {
- if (current !== null) {
- safelyDetachRef(current, current.return);
- }
- }
+ if (enableSchedulingProfiler) {
+ markRenderStarted(lanes);
+ }
- var _newState = finishedWork.memoizedState;
+ outer: do {
+ try {
+ if (
+ workInProgressSuspendedReason !== NotSuspended &&
+ workInProgress !== null
+ ) {
+ // The work loop is suspended. We need to either unwind the stack or
+ // replay the suspended component.
+ var unitOfWork = workInProgress;
+ var thrownValue = workInProgressThrownValue;
- var _isHidden = _newState !== null;
+ switch (workInProgressSuspendedReason) {
+ case SuspendedOnError: {
+ // Unwind then continue with the normal work loop.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
+ break;
+ }
- var _wasHidden = current !== null && current.memoizedState !== null;
+ case SuspendedOnData: {
+ var thenable = thrownValue;
- if (finishedWork.mode & ConcurrentMode) {
- // Before committing the children, track on the stack whether this
- // offscreen subtree was already hidden, so that we don't unmount the
- // effects again.
- var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
- var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
- offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden || _isHidden;
- offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || _wasHidden;
- recursivelyTraverseMutationEffects(root, finishedWork);
- offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
- offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
- } else {
- recursivelyTraverseMutationEffects(root, finishedWork);
- }
+ if (isThenableResolved(thenable)) {
+ // The data resolved. Try rendering the component again.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ replaySuspendedUnitOfWork(unitOfWork);
+ break;
+ } // The work loop is suspended on data. We should wait for it to
+ // resolve before continuing to render.
+ // TODO: Handle the case where the promise resolves synchronously.
+ // Usually this is handled when we instrument the promise to add a
+ // `status` field, but if the promise already has a status, we won't
+ // have added a listener until right here.
- commitReconciliationEffects(finishedWork);
- var offscreenInstance = finishedWork.stateNode; // TODO: Add explicit effect flag to set _current.
+ var onResolution = function () {
+ // Check if the root is still suspended on this promise.
+ if (
+ workInProgressSuspendedReason === SuspendedOnData &&
+ workInProgressRoot === root
+ ) {
+ // Mark the root as ready to continue rendering.
+ workInProgressSuspendedReason = SuspendedAndReadyToContinue;
+ } // Ensure the root is scheduled. We should do this even if we're
+ // currently working on a different root, so that we resume
+ // rendering later.
- offscreenInstance._current = finishedWork; // Offscreen stores pending changes to visibility in `_pendingVisibility`. This is
- // to support batching of `attach` and `detach` calls.
+ ensureRootIsScheduled(root, now$1());
+ };
- offscreenInstance._visibility &= ~OffscreenDetached;
- offscreenInstance._visibility |=
- offscreenInstance._pendingVisibility & OffscreenDetached;
+ thenable.then(onResolution, onResolution);
+ break outer;
+ }
- if (flags & Visibility) {
- // Track the current state on the Offscreen instance so we can
- // read it during an event
- if (_isHidden) {
- offscreenInstance._visibility &= ~OffscreenVisible;
- } else {
- offscreenInstance._visibility |= OffscreenVisible;
- }
+ case SuspendedOnImmediate: {
+ // If this fiber just suspended, it's possible the data is already
+ // cached. Yield to the main thread to give it a chance to ping. If
+ // it does, we can retry immediately without unwinding the stack.
+ workInProgressSuspendedReason = SuspendedAndReadyToContinue;
+ break outer;
+ }
- if (_isHidden) {
- var isUpdate = current !== null;
- var wasHiddenByAncestorOffscreen =
- offscreenSubtreeIsHidden || offscreenSubtreeWasHidden; // Only trigger disapper layout effects if:
- // - This is an update, not first mount.
- // - This Offscreen was not hidden before.
- // - Ancestor Offscreen was not hidden in previous commit.
+ case SuspendedAndReadyToContinue: {
+ var _thenable = thrownValue;
- if (isUpdate && !_wasHidden && !wasHiddenByAncestorOffscreen) {
- if ((finishedWork.mode & ConcurrentMode) !== NoMode) {
- // Disappear the layout effects of all the children
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ if (isThenableResolved(_thenable)) {
+ // The data resolved. Try rendering the component again.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ replaySuspendedUnitOfWork(unitOfWork);
+ } else {
+ // Otherwise, unwind then continue with the normal work loop.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
}
- }
- } // Offscreen with manual mode manages visibility manually.
- if (!isOffscreenManual(finishedWork)) {
- // TODO: This needs to run whenever there's an insertion or update
- // inside a hidden Offscreen tree.
- hideOrUnhideAllChildren(finishedWork, _isHidden);
- }
- } // TODO: Move to passive phase
+ break;
+ }
- if (flags & Update) {
- var offscreenQueue = finishedWork.updateQueue;
+ case SuspendedOnDeprecatedThrowPromise: {
+ // Suspended by an old implementation that uses the `throw promise`
+ // pattern. The newer replaying behavior can cause subtle issues
+ // like infinite ping loops. So we maintain the old behavior and
+ // always unwind.
+ workInProgressSuspendedReason = NotSuspended;
+ workInProgressThrownValue = null;
+ unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
+ break;
+ }
- if (offscreenQueue !== null) {
- var _wakeables = offscreenQueue.wakeables;
+ case SuspendedOnHydration: {
+ // Selective hydration. An update flowed into a dehydrated tree.
+ // Interrupt the current render so the work loop can switch to the
+ // hydration lane.
+ resetWorkInProgressStack();
+ workInProgressRootExitStatus = RootDidNotComplete;
+ break outer;
+ }
- if (_wakeables !== null) {
- offscreenQueue.wakeables = null;
- attachSuspenseRetryListeners(finishedWork, _wakeables);
+ default: {
+ throw new Error(
+ "Unexpected SuspendedReason. This is a bug in React."
+ );
}
}
}
- return;
- }
-
- case SuspenseListComponent: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
-
- if (flags & Update) {
- var _wakeables2 = finishedWork.updateQueue;
-
- if (_wakeables2 !== null) {
- finishedWork.updateQueue = null;
- attachSuspenseRetryListeners(finishedWork, _wakeables2);
- }
+ if (true && ReactCurrentActQueue.current !== null) {
+ // `act` special case: If we're inside an `act` scope, don't consult
+ // `shouldYield`. Always keep working until the render is complete.
+ // This is not just an optimization: in a unit test environment, we
+ // can't trust the result of `shouldYield`, because the host I/O is
+ // likely mocked.
+ workLoopSync();
+ } else {
+ workLoopConcurrent();
}
- return;
+ break;
+ } catch (thrownValue) {
+ handleThrow(root, thrownValue);
}
+ } while (true);
- case ScopeComponent: {
- {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork); // TODO: This is a temporary solution that allowed us to transition away
- // from React Flare on www.
-
- if (flags & Ref) {
- if (current !== null) {
- safelyDetachRef(finishedWork, finishedWork.return);
- }
-
- safelyAttachRef(finishedWork, finishedWork.return);
- }
-
- if (flags & Update) {
- var scopeInstance = finishedWork.stateNode;
- prepareScopeUpdate(scopeInstance, finishedWork);
- }
- }
+ resetContextDependencies();
+ popDispatcher(prevDispatcher);
+ popCacheDispatcher(prevCacheDispatcher);
+ executionContext = prevExecutionContext;
- return;
+ {
+ if (enableDebugTracing) {
+ logRenderStopped();
}
+ } // Check if the tree has completed.
- default: {
- recursivelyTraverseMutationEffects(root, finishedWork);
- commitReconciliationEffects(finishedWork);
- return;
+ if (workInProgress !== null) {
+ // Still work remaining.
+ if (enableSchedulingProfiler) {
+ markRenderYielded();
}
- }
-}
-function commitReconciliationEffects(finishedWork) {
- // Placement effects (insertions, reorders) can be scheduled on any fiber
- // type. They needs to happen after the children effects have fired, but
- // before the effects on this fiber have fired.
- var flags = finishedWork.flags;
+ return RootInProgress;
+ } else {
+ // Completed the tree.
+ if (enableSchedulingProfiler) {
+ markRenderStopped();
+ } // Set this to null to indicate there's no in-progress render.
- if (flags & Placement) {
- try {
- commitPlacement(finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- } // Clear the "placement" from effect tag so that we know that this is
- // inserted, before any life-cycles like componentDidMount gets called.
- // TODO: findDOMNode doesn't rely on this any more but isMounted does
- // and isMounted is deprecated anyway so we should be able to kill this.
+ workInProgressRoot = null;
+ workInProgressRootRenderLanes = NoLanes; // It's safe to process the queue now that the render phase is complete.
- finishedWork.flags &= ~Placement;
- }
+ finishQueueingConcurrentUpdates(); // Return the final exit status.
- if (flags & Hydrating) {
- finishedWork.flags &= ~Hydrating;
+ return workInProgressRootExitStatus;
}
}
+/** @noinline */
-function commitLayoutEffects(finishedWork, root, committedLanes) {
- inProgressLanes = committedLanes;
- inProgressRoot = root;
- var current = finishedWork.alternate;
- commitLayoutEffectOnFiber(root, current, finishedWork);
- inProgressLanes = null;
- inProgressRoot = null;
+function workLoopConcurrent() {
+ // Perform work until Scheduler asks us to yield
+ while (workInProgress !== null && !shouldYield()) {
+ // $FlowFixMe[incompatible-call] found when upgrading Flow
+ performUnitOfWork(workInProgress);
+ }
}
-function recursivelyTraverseLayoutEffects(root, parentFiber, lanes) {
- var prevDebugFiber = getCurrentFiber();
-
- if (parentFiber.subtreeFlags & LayoutMask) {
- var child = parentFiber.child;
+function performUnitOfWork(unitOfWork) {
+ // The current, flushed, state of this fiber is the alternate. Ideally
+ // nothing should rely on this, but relying on it here means that we don't
+ // need an additional field on the work in progress.
+ var current = unitOfWork.alternate;
+ setCurrentFiber(unitOfWork);
+ var next;
- while (child !== null) {
- setCurrentFiber(child);
- var current = child.alternate;
- commitLayoutEffectOnFiber(root, current, child);
- child = child.sibling;
- }
+ if ((unitOfWork.mode & ProfileMode) !== NoMode) {
+ startProfilerTimer(unitOfWork);
+ next = beginWork(current, unitOfWork, renderLanes);
+ stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
+ } else {
+ next = beginWork(current, unitOfWork, renderLanes);
}
- setCurrentFiber(prevDebugFiber);
-}
+ resetCurrentFiber();
+ unitOfWork.memoizedProps = unitOfWork.pendingProps;
-function disappearLayoutEffects(finishedWork) {
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case MemoComponent:
- case SimpleMemoComponent: {
- // TODO (Offscreen) Check: flags & LayoutStatic
- if (shouldProfile(finishedWork)) {
- try {
- startLayoutEffectTimer();
- commitHookEffectListUnmount(
- Layout,
- finishedWork,
- finishedWork.return
- );
- } finally {
- recordLayoutEffectDuration(finishedWork);
- }
- } else {
- commitHookEffectListUnmount(Layout, finishedWork, finishedWork.return);
- }
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ completeUnitOfWork(unitOfWork);
+ } else {
+ workInProgress = next;
+ }
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
- break;
- }
+ ReactCurrentOwner$1.current = null;
+}
- case ClassComponent: {
- // TODO (Offscreen) Check: flags & RefStatic
- safelyDetachRef(finishedWork, finishedWork.return);
- var instance = finishedWork.stateNode;
+function replaySuspendedUnitOfWork(unitOfWork) {
+ // This is a fork of performUnitOfWork specifcally for replaying a fiber that
+ // just suspended.
+ //
+ var current = unitOfWork.alternate;
+ setCurrentFiber(unitOfWork);
+ var next;
+ setCurrentFiber(unitOfWork);
+ var isProfilingMode = (unitOfWork.mode & ProfileMode) !== NoMode;
- if (typeof instance.componentWillUnmount === "function") {
- safelyCallComponentWillUnmount(
- finishedWork,
- finishedWork.return,
- instance
- );
- }
+ if (isProfilingMode) {
+ startProfilerTimer(unitOfWork);
+ }
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
- break;
+ switch (unitOfWork.tag) {
+ case IndeterminateComponent: {
+ // Because it suspended with `use`, we can assume it's a
+ // function component.
+ unitOfWork.tag = FunctionComponent; // Fallthrough to the next branch.
}
+ // eslint-disable-next-line no-fallthrough
- case HostHoistable:
- case HostSingleton:
- case HostComponent: {
- // TODO (Offscreen) Check: flags & RefStatic
- safelyDetachRef(finishedWork, finishedWork.return);
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ case FunctionComponent:
+ case ForwardRef: {
+ // Resolve `defaultProps`. This logic is copied from `beginWork`.
+ // TODO: Consider moving this switch statement into that module. Also,
+ // could maybe use this as an opportunity to say `use` doesn't work with
+ // `defaultProps` :)
+ var Component = unitOfWork.type;
+ var unresolvedProps = unitOfWork.pendingProps;
+ var resolvedProps =
+ unitOfWork.elementType === Component
+ ? unresolvedProps
+ : resolveDefaultProps(Component, unresolvedProps);
+ next = replayFunctionComponent(
+ current,
+ unitOfWork,
+ resolvedProps,
+ Component,
+ workInProgressRootRenderLanes
+ );
break;
}
- case OffscreenComponent: {
- // TODO (Offscreen) Check: flags & RefStatic
- safelyDetachRef(finishedWork, finishedWork.return);
- var isHidden = finishedWork.memoizedState !== null;
-
- if (isHidden);
- else {
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
- }
-
+ case SimpleMemoComponent: {
+ var _Component = unitOfWork.type;
+ var nextProps = unitOfWork.pendingProps;
+ next = replayFunctionComponent(
+ current,
+ unitOfWork,
+ nextProps,
+ _Component,
+ workInProgressRootRenderLanes
+ );
break;
}
default: {
- recursivelyTraverseDisappearLayoutEffects(finishedWork);
+ // Other types besides function components are reset completely before
+ // being replayed. Currently this only happens when a Usable type is
+ // reconciled — the reconciler will suspend.
+ //
+ // We reset the fiber back to its original state; however, this isn't
+ // a full "unwind" because we're going to reuse the promises that were
+ // reconciled previously. So it's intentional that we don't call
+ // resetSuspendedWorkLoopOnUnwind here.
+ unwindInterruptedWork(current, unitOfWork);
+ unitOfWork = workInProgress = resetWorkInProgress(
+ unitOfWork,
+ renderLanes
+ );
+ next = beginWork(current, unitOfWork, renderLanes);
break;
}
}
+
+ if (isProfilingMode) {
+ stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
+ } // The begin phase finished successfully without suspending. Return to the
+ // normal work loop.
+
+ resetCurrentFiber();
+ unitOfWork.memoizedProps = unitOfWork.pendingProps;
+
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ completeUnitOfWork(unitOfWork);
+ } else {
+ workInProgress = next;
+ }
+
+ ReactCurrentOwner$1.current = null;
}
-function recursivelyTraverseDisappearLayoutEffects(parentFiber) {
- // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
- var child = parentFiber.child;
+function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) {
+ // This is a fork of performUnitOfWork specifcally for unwinding a fiber
+ // that threw an exception.
+ //
+ // Return to the normal work loop. This will unwind the stack, and potentially
+ // result in showing a fallback.
+ resetSuspendedWorkLoopOnUnwind();
+ var returnFiber = unitOfWork.return;
- while (child !== null) {
- disappearLayoutEffects(child);
- child = child.sibling;
+ if (returnFiber === null || workInProgressRoot === null) {
+ // Expected to be working on a non-root fiber. This is a fatal error
+ // because there's no ancestor that can handle it; the root is
+ // supposed to capture all errors that weren't caught by an error
+ // boundary.
+ workInProgressRootExitStatus = RootFatalErrored;
+ workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next
+ // sibling, or the parent if there are no siblings. But since the root
+ // has no siblings nor a parent, we set it to null. Usually this is
+ // handled by `completeUnitOfWork` or `unwindWork`, but since we're
+ // intentionally not calling those, we need set it here.
+ // TODO: Consider calling `unwindWork` to pop the contexts.
+
+ workInProgress = null;
+ return;
}
+
+ try {
+ // Find and mark the nearest Suspense or error boundary that can handle
+ // this "exception".
+ throwException(
+ workInProgressRoot,
+ returnFiber,
+ unitOfWork,
+ thrownValue,
+ workInProgressRootRenderLanes
+ );
+ } catch (error) {
+ // We had trouble processing the error. An example of this happening is
+ // when accessing the `componentDidCatch` property of an error boundary
+ // throws an error. A weird edge case. There's a regression test for this.
+ // To prevent an infinite loop, bubble the error up to the next parent.
+ workInProgress = returnFiber;
+ throw error;
+ } // Return to the normal work loop.
+
+ completeUnitOfWork(unitOfWork);
}
-function reappearLayoutEffects(
- finishedRoot,
- current,
- finishedWork, // This function visits both newly finished work and nodes that were re-used
- // from a previously committed tree. We cannot check non-static flags if the
- // node was reused.
- includeWorkInProgressEffects
-) {
- // Turn on layout effects in a tree that previously disappeared.
- var flags = finishedWork.flags;
+function completeUnitOfWork(unitOfWork) {
+ // Attempt to complete the current unit of work, then move to the next
+ // sibling. If there are no more siblings, return to the parent fiber.
+ var completedWork = unitOfWork;
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- ); // TODO: Check flags & LayoutStatic
+ do {
+ // The current, flushed, state of this fiber is the alternate. Ideally
+ // nothing should rely on this, but relying on it here means that we don't
+ // need an additional field on the work in progress.
+ var current = completedWork.alternate;
+ var returnFiber = completedWork.return; // Check if the work completed or if something threw.
- commitHookLayoutEffects(finishedWork, Layout);
- break;
- }
+ if ((completedWork.flags & Incomplete) === NoFlags$1) {
+ setCurrentFiber(completedWork);
+ var next = void 0;
- case ClassComponent: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- ); // TODO: Check for LayoutStatic flag
+ if ((completedWork.mode & ProfileMode) === NoMode) {
+ next = completeWork(current, completedWork, renderLanes);
+ } else {
+ startProfilerTimer(completedWork);
+ next = completeWork(current, completedWork, renderLanes); // Update render duration assuming we didn't error.
- var instance = finishedWork.stateNode;
+ stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
+ }
- if (typeof instance.componentDidMount === "function") {
- try {
- instance.componentDidMount();
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- } // Commit any callbacks that would have fired while the component
- // was hidden.
+ resetCurrentFiber();
- var updateQueue = finishedWork.updateQueue;
+ if (next !== null) {
+ // Completing this fiber spawned new work. Work on that next.
+ workInProgress = next;
+ return;
+ }
+ } else {
+ // This fiber did not complete because something threw. Pop values off
+ // the stack without entering the complete phase. If this is a boundary,
+ // capture values if possible.
+ var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes.
- if (updateQueue !== null) {
- commitHiddenCallbacks(updateQueue, instance);
- } // If this is newly finished work, check for setState callbacks
+ if (_next !== null) {
+ // If completing this work spawned new work, do that next. We'll come
+ // back here again.
+ // Since we're restarting, remove anything that is not a host effect
+ // from the effect tag.
+ _next.flags &= HostEffectMask;
+ workInProgress = _next;
+ return;
+ }
- if (includeWorkInProgressEffects && flags & Callback) {
- commitClassCallbacks(finishedWork);
- } // TODO: Check flags & RefStatic
+ if ((completedWork.mode & ProfileMode) !== NoMode) {
+ // Record the render duration for the fiber that errored.
+ stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.
- safelyAttachRef(finishedWork, finishedWork.return);
- break;
+ var actualDuration = completedWork.actualDuration;
+ var child = completedWork.child;
+
+ while (child !== null) {
+ // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
+ actualDuration += child.actualDuration;
+ child = child.sibling;
+ }
+
+ completedWork.actualDuration = actualDuration;
+ }
+
+ if (returnFiber !== null) {
+ // Mark the parent fiber as incomplete and clear its subtree flags.
+ returnFiber.flags |= Incomplete;
+ returnFiber.subtreeFlags = NoFlags$1;
+ returnFiber.deletions = null;
+ } else {
+ // We've unwound all the way to the root.
+ workInProgressRootExitStatus = RootDidNotComplete;
+ workInProgress = null;
+ return;
+ }
}
- // Unlike commitLayoutEffectsOnFiber, we don't need to handle HostRoot
- // because this function only visits nodes that are inside an
- // Offscreen fiber.
- // case HostRoot: {
- // ...
- // }
- case HostHoistable:
- case HostSingleton:
- case HostComponent: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- ); // 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.
+ var siblingFiber = completedWork.sibling;
- if (includeWorkInProgressEffects && current === null && flags & Update) {
- commitHostComponentMount(finishedWork);
- } // TODO: Check flags & Ref
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ workInProgress = siblingFiber;
+ return;
+ } // Otherwise, return to the parent
+ // $FlowFixMe[incompatible-type] we bail out when we get a null
+
+ completedWork = returnFiber; // Update the next thing we're working on in case something throws.
+
+ workInProgress = completedWork;
+ } while (completedWork !== null); // We've reached the root.
+
+ if (workInProgressRootExitStatus === RootInProgress) {
+ workInProgressRootExitStatus = RootCompleted;
+ }
+}
+
+function commitRoot(root, recoverableErrors, transitions) {
+ // TODO: This no longer makes any sense. We already wrap the mutation and
+ // layout phases. Should be able to remove.
+ var previousUpdateLanePriority = getCurrentUpdatePriority();
+ var prevTransition = ReactCurrentBatchConfig$1.transition;
+
+ try {
+ ReactCurrentBatchConfig$1.transition = null;
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ commitRootImpl(
+ root,
+ recoverableErrors,
+ transitions,
+ previousUpdateLanePriority
+ );
+ } finally {
+ ReactCurrentBatchConfig$1.transition = prevTransition;
+ setCurrentUpdatePriority(previousUpdateLanePriority);
+ }
+
+ return null;
+}
+
+function commitRootImpl(
+ root,
+ recoverableErrors,
+ transitions,
+ renderPriorityLevel
+) {
+ do {
+ // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
+ // means `flushPassiveEffects` will sometimes result in additional
+ // passive effects. So we need to keep flushing in a loop until there are
+ // no more pending effects.
+ // TODO: Might be better if `flushPassiveEffects` did not automatically
+ // flush synchronous work at the end, to avoid factoring hazards like this.
+ flushPassiveEffects();
+ } while (rootWithPendingPassiveEffects !== null);
- safelyAttachRef(finishedWork, finishedWork.return);
- break;
- }
+ flushRenderPhaseStrictModeWarningsInDEV();
- case Profiler: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- ); // TODO: Figure out how Profiler updates should work with Offscreen
+ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
+ throw new Error("Should not already be working.");
+ }
- if (includeWorkInProgressEffects && flags & Update) {
- commitProfilerUpdate(finishedWork, current);
- }
+ var finishedWork = root.finishedWork;
+ var lanes = root.finishedLanes;
- break;
+ {
+ if (enableDebugTracing) {
+ logCommitStarted(lanes);
}
+ }
- case SuspenseComponent: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- ); // TODO: Figure out how Suspense hydration callbacks should work
- // with Offscreen.
+ if (enableSchedulingProfiler) {
+ markCommitStarted(lanes);
+ }
- if (includeWorkInProgressEffects && flags & Update) {
- commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
+ if (finishedWork === null) {
+ {
+ if (enableDebugTracing) {
+ logCommitStopped();
}
-
- break;
}
- case OffscreenComponent: {
- var offscreenState = finishedWork.memoizedState;
- var isHidden = offscreenState !== null;
-
- if (isHidden);
- else {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- );
- } // TODO: Check flags & Ref
-
- safelyAttachRef(finishedWork, finishedWork.return);
- break;
+ if (enableSchedulingProfiler) {
+ markCommitStopped();
}
- default: {
- recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- finishedWork,
- includeWorkInProgressEffects
- );
- break;
+ return null;
+ } else {
+ {
+ if (lanes === NoLanes) {
+ error(
+ "root.finishedLanes should not be empty during a commit. This is a " +
+ "bug in React."
+ );
+ }
}
}
-}
-
-function recursivelyTraverseReappearLayoutEffects(
- finishedRoot,
- parentFiber,
- includeWorkInProgressEffects
-) {
- // This function visits both newly finished work and nodes that were re-used
- // from a previously committed tree. We cannot check non-static flags if the
- // node was reused.
- var childShouldIncludeWorkInProgressEffects =
- includeWorkInProgressEffects &&
- (parentFiber.subtreeFlags & LayoutMask) !== NoFlags$1; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
- var prevDebugFiber = getCurrentFiber();
- var child = parentFiber.child;
+ root.finishedWork = null;
+ root.finishedLanes = NoLanes;
- while (child !== null) {
- var current = child.alternate;
- reappearLayoutEffects(
- finishedRoot,
- current,
- child,
- childShouldIncludeWorkInProgressEffects
+ if (finishedWork === root.current) {
+ throw new Error(
+ "Cannot commit the same tree as before. This error is likely caused by " +
+ "a bug in React. Please file an issue."
);
- child = child.sibling;
- }
+ } // commitRoot never returns a continuation; it always finishes synchronously.
+ // So we can clear these now to allow a new callback to be scheduled.
- setCurrentFiber(prevDebugFiber);
-}
+ root.callbackNode = null;
+ root.callbackPriority = NoLane; // Check which lanes no longer have any work scheduled on them, and mark
+ // those as finished.
-function commitHookPassiveMountEffects(finishedWork, hookFlags) {
- if (shouldProfile(finishedWork)) {
- startPassiveEffectTimer();
+ var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes); // Make sure to account for lanes that were updated by a concurrent event
+ // during the render phase; don't mark them as finished.
- try {
- commitHookEffectListMount(hookFlags, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
+ var concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
+ remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
+ markRootFinished(root, remainingLanes);
- recordPassiveEffectDuration(finishedWork);
- } else {
- try {
- commitHookEffectListMount(hookFlags, finishedWork);
- } catch (error) {
- captureCommitPhaseError(finishedWork, finishedWork.return, error);
- }
- }
-}
+ if (root === workInProgressRoot) {
+ // We can reset these now that they are finished.
+ workInProgressRoot = null;
+ workInProgress = null;
+ workInProgressRootRenderLanes = NoLanes;
+ } // If there are pending passive effects, schedule a callback to process them.
+ // Do this as early as possible, so it is queued before anything else that
+ // might get scheduled in the commit phase. (See #16714.)
+ // TODO: Delete all other places that schedule the passive effect callback
+ // They're redundant.
-function commitOffscreenPassiveMountEffects(current, finishedWork, instance) {
- {
- var previousCache = null;
+ if (
+ (finishedWork.subtreeFlags & PassiveMask) !== NoFlags$1 ||
+ (finishedWork.flags & PassiveMask) !== NoFlags$1
+ ) {
+ if (!rootDoesHavePassiveEffects) {
+ rootDoesHavePassiveEffects = true;
+ pendingPassiveEffectsRemainingLanes = remainingLanes; // workInProgressTransitions might be overwritten, so we want
+ // to store it in pendingPassiveTransitions until they get processed
+ // We need to pass this through as an argument to commitRoot
+ // because workInProgressTransitions might have changed between
+ // the previous render and commit if we throttle the commit
+ // with setTimeout
- if (
- current !== null &&
- current.memoizedState !== null &&
- current.memoizedState.cachePool !== null
- ) {
- previousCache = current.memoizedState.cachePool.pool;
+ pendingPassiveTransitions = transitions;
+ scheduleCallback(NormalPriority$1, function () {
+ flushPassiveEffects(); // This render triggered passive effects: release the root cache pool
+ // *after* passive effects fire to avoid freeing a cache pool that may
+ // be referenced by a node in the tree (HostRoot, Cache boundary etc)
+
+ return null;
+ });
}
+ } // Check if there are any effects in the whole tree.
+ // TODO: This is left over from the effect list implementation, where we had
+ // to check for the existence of `firstEffect` to satisfy Flow. I think the
+ // only other reason this optimization exists is because it affects profiling.
+ // Reconsider whether this is necessary.
- var nextCache = null;
+ var subtreeHasEffects =
+ (finishedWork.subtreeFlags &
+ (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
+ NoFlags$1;
+ var rootHasEffect =
+ (finishedWork.flags &
+ (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
+ NoFlags$1;
- if (
- finishedWork.memoizedState !== null &&
- finishedWork.memoizedState.cachePool !== null
- ) {
- nextCache = finishedWork.memoizedState.cachePool.pool;
- } // Retain/release the cache used for pending (suspended) nodes.
- // Note that this is only reached in the non-suspended/visible case:
- // when the content is suspended/hidden, the retain/release occurs
- // via the parent Suspense component (see case above).
+ if (subtreeHasEffects || rootHasEffect) {
+ var prevTransition = ReactCurrentBatchConfig$1.transition;
+ ReactCurrentBatchConfig$1.transition = null;
+ var previousPriority = getCurrentUpdatePriority();
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ var prevExecutionContext = executionContext;
+ executionContext |= CommitContext; // Reset this to null before calling lifecycles
- if (nextCache !== previousCache) {
- if (nextCache != null) {
- retainCache(nextCache);
- }
+ ReactCurrentOwner$1.current = null; // The commit phase is broken into several sub-phases. We do a separate pass
+ // of the effect list for each phase: all mutation effects come before all
+ // layout effects, and so on.
+ // The first phase a "before mutation" phase. We use this phase to read the
+ // state of the host tree right before we mutate it. This is where
+ // getSnapshotBeforeUpdate is called.
- if (previousCache != null) {
- releaseCache(previousCache);
- }
+ var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
+ root,
+ finishedWork
+ );
+
+ {
+ // Mark the current commit time to be shared by all Profilers in this
+ // batch. This enables them to be grouped later.
+ recordCommitTime();
}
- }
- if (enableTransitionTracing) {
- // TODO: Pre-rendering should not be counted as part of a transition. We
- // may add separate logs for pre-rendering, but it's not part of the
- // primary metrics.
- var offscreenState = finishedWork.memoizedState;
- var queue = finishedWork.updateQueue;
- var isHidden = offscreenState !== null;
+ if (enableProfilerNestedUpdateScheduledHook) {
+ // Track the root here, rather than in commitLayoutEffects(), because of ref setters.
+ // Updates scheduled during ref detachment should also be flagged.
+ rootCommittingMutationOrLayoutEffects = root;
+ } // The next phase is the mutation phase, where we mutate the host tree.
- if (queue !== null) {
- if (isHidden) {
- var transitions = queue.transitions;
+ commitMutationEffects(root, finishedWork, lanes);
- if (transitions !== null) {
- transitions.forEach(function (transition) {
- // Add all the transitions saved in the update queue during
- // the render phase (ie the transitions associated with this boundary)
- // into the transitions set.
- if (instance._transitions === null) {
- instance._transitions = new Set();
- }
+ {
+ if (shouldFireAfterActiveInstanceBlur) {
+ afterActiveInstanceBlur();
+ }
+ }
- instance._transitions.add(transition);
- });
- }
+ resetAfterCommit(); // The work-in-progress tree is now the current tree. This must come after
+ // the mutation phase, so that the previous tree is still current during
+ // componentWillUnmount, but before the layout phase, so that the finished
+ // work is current during componentDidMount/Update.
- var markerInstances = queue.markerInstances;
+ root.current = finishedWork; // The next phase is the layout phase, where we call effects that read
+ // the host tree after it's been mutated. The idiomatic use case for this is
+ // layout, but class component lifecycles also fire here for legacy reasons.
- if (markerInstances !== null) {
- markerInstances.forEach(function (markerInstance) {
- var markerTransitions = markerInstance.transitions; // There should only be a few tracing marker transitions because
- // they should be only associated with the transition that
- // caused them
+ {
+ if (enableDebugTracing) {
+ logLayoutEffectsStarted(lanes);
+ }
+ }
- if (markerTransitions !== null) {
- markerTransitions.forEach(function (transition) {
- if (instance._transitions === null) {
- instance._transitions = new Set();
- } else if (instance._transitions.has(transition)) {
- if (markerInstance.pendingBoundaries === null) {
- markerInstance.pendingBoundaries = new Map();
- }
+ if (enableSchedulingProfiler) {
+ markLayoutEffectsStarted(lanes);
+ }
- if (instance._pendingMarkers === null) {
- instance._pendingMarkers = new Set();
- }
+ commitLayoutEffects(finishedWork, root, lanes);
- instance._pendingMarkers.add(markerInstance);
- }
- });
- }
- });
- }
+ {
+ if (enableDebugTracing) {
+ logLayoutEffectsStopped();
}
+ }
- finishedWork.updateQueue = null;
+ if (enableSchedulingProfiler) {
+ markLayoutEffectsStopped();
}
- commitTransitionProgress(finishedWork); // TODO: Refactor this into an if/else branch
+ if (enableProfilerNestedUpdateScheduledHook) {
+ rootCommittingMutationOrLayoutEffects = null;
+ } // Tell Scheduler to yield at the end of the frame, so the browser has an
+ // opportunity to paint.
- if (!isHidden) {
- instance._transitions = null;
- instance._pendingMarkers = null;
- }
- }
-}
+ requestPaint();
+ executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value.
-function commitCachePassiveMountEffect(current, finishedWork) {
- {
- var previousCache = null;
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig$1.transition = prevTransition;
+ } else {
+ // No effects.
+ root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were
+ // no effects.
+ // TODO: Maybe there's a better way to report this.
- if (finishedWork.alternate !== null) {
- previousCache = finishedWork.alternate.memoizedState.cache;
+ {
+ recordCommitTime();
}
+ }
- var nextCache = finishedWork.memoizedState.cache; // Retain/release the cache. In theory the cache component
- // could be "borrowing" a cache instance owned by some parent,
- // in which case we could avoid retaining/releasing. But it
- // is non-trivial to determine when that is the case, so we
- // always retain/release.
+ var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
- if (nextCache !== previousCache) {
- retainCache(nextCache);
+ if (rootDoesHavePassiveEffects) {
+ // This commit has passive effects. Stash a reference to them. But don't
+ // schedule a callback until after flushing layout work.
+ rootDoesHavePassiveEffects = false;
+ rootWithPendingPassiveEffects = root;
+ pendingPassiveEffectsLanes = lanes;
+ } else {
+ // There were no passive effects, so we can immediately release the cache
+ // pool for this render.
+ releaseRootPooledCache(root, remainingLanes);
- if (previousCache != null) {
- releaseCache(previousCache);
- }
+ {
+ nestedPassiveUpdateCount = 0;
+ rootWithPassiveNestedUpdates = null;
}
- }
-}
+ } // Read this again, since an effect might have updated it
-function commitTracingMarkerPassiveMountEffect(finishedWork) {
- // Get the transitions that were initiatized during the render
- // and add a start transition callback for each of them
- // We will only call this on initial mount of the tracing marker
- // only if there are no suspense children
- var instance = finishedWork.stateNode;
+ remainingLanes = root.pendingLanes; // Check if there's remaining work on this root
+ // TODO: This is part of the `componentDidCatch` implementation. Its purpose
+ // is to detect whether something might have called setState inside
+ // `componentDidCatch`. The mechanism is known to be flawed because `setState`
+ // inside `componentDidCatch` is itself flawed — that's why we recommend
+ // `getDerivedStateFromError` instead. However, it could be improved by
+ // checking if remainingLanes includes Sync work, instead of whether there's
+ // any work remaining at all (which would also include stuff like Suspense
+ // retries or transitions). It's been like this for a while, though, so fixing
+ // it probably isn't that urgent.
- if (instance.transitions !== null && instance.pendingBoundaries === null) {
- addMarkerCompleteCallbackToPendingTransition(
- finishedWork.memoizedProps.name,
- instance.transitions
- );
- instance.transitions = null;
- instance.pendingBoundaries = null;
- instance.aborts = null;
- instance.name = null;
+ if (remainingLanes === NoLanes) {
+ // If there's no remaining work, we can clear the set of already failed
+ // error boundaries.
+ legacyErrorBoundariesThatAlreadyFailed = null;
}
-}
-function commitPassiveMountEffects(
- root,
- finishedWork,
- committedLanes,
- committedTransitions
-) {
- setCurrentFiber(finishedWork);
- commitPassiveMountOnFiber(
- root,
- finishedWork,
- committedLanes,
- committedTransitions
- );
- resetCurrentFiber();
-}
-
-function recursivelyTraversePassiveMountEffects(
- root,
- parentFiber,
- committedLanes,
- committedTransitions
-) {
- var prevDebugFiber = getCurrentFiber();
+ {
+ if (!rootDidHavePassiveEffects) {
+ commitDoubleInvokeEffectsInDEV(root, false);
+ }
+ }
- if (parentFiber.subtreeFlags & PassiveMask) {
- var child = parentFiber.child;
+ onCommitRoot$1(finishedWork.stateNode, renderPriorityLevel);
- while (child !== null) {
- setCurrentFiber(child);
- commitPassiveMountOnFiber(
- root,
- child,
- committedLanes,
- committedTransitions
- );
- child = child.sibling;
+ {
+ if (isDevToolsPresent) {
+ root.memoizedUpdaters.clear();
}
}
- setCurrentFiber(prevDebugFiber);
-}
-
-function commitPassiveMountOnFiber(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
-) {
- // When updating this function, also update reconnectPassiveEffects, which does
- // most of the same things when an offscreen tree goes from hidden -> visible,
- // or when toggling effects inside a hidden tree.
- var flags = finishedWork.flags;
+ {
+ onCommitRoot();
+ } // Always call this before exiting `commitRoot`, to ensure that any
+ // additional work on this root is scheduled.
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
+ ensureRootIsScheduled(root, now$1());
- if (flags & Passive$1) {
- commitHookPassiveMountEffects(finishedWork, Passive | HasEffect);
- }
+ if (recoverableErrors !== null) {
+ // There were errors during this render, but recovered from them without
+ // needing to surface it to the UI. We log them here.
+ var onRecoverableError = root.onRecoverableError;
- break;
+ for (var i = 0; i < recoverableErrors.length; i++) {
+ var recoverableError = recoverableErrors[i];
+ var errorInfo = makeErrorInfo(
+ recoverableError.digest,
+ recoverableError.stack
+ );
+ onRecoverableError(recoverableError.value, errorInfo);
}
+ }
- case HostRoot: {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
+ if (hasUncaughtError) {
+ hasUncaughtError = false;
+ var error$1 = firstUncaughtError;
+ firstUncaughtError = null;
+ throw error$1;
+ } // If the passive effects are the result of a discrete render, flush them
+ // synchronously at the end of the current task so that the result is
+ // immediately observable. Otherwise, we assume that they are not
+ // order-dependent and do not need to be observed by external systems, so we
+ // can wait until after paint.
+ // TODO: We can optimize this by not scheduling the callback earlier. Since we
+ // currently schedule the callback in multiple places, will wait until those
+ // are consolidated.
- if (flags & Passive$1) {
- {
- var previousCache = null;
+ if (includesSyncLane(pendingPassiveEffectsLanes) && root.tag !== LegacyRoot) {
+ flushPassiveEffects();
+ } // Read this again, since a passive effect might have updated it
- if (finishedWork.alternate !== null) {
- previousCache = finishedWork.alternate.memoizedState.cache;
- }
+ remainingLanes = root.pendingLanes;
- var nextCache = finishedWork.memoizedState.cache; // Retain/release the root cache.
- // Note that on initial mount, previousCache and nextCache will be the same
- // and this retain won't occur. To counter this, we instead retain the HostRoot's
- // initial cache when creating the root itself (see createFiberRoot() in
- // ReactFiberRoot.js). Subsequent updates that change the cache are reflected
- // here, such that previous/next caches are retained correctly.
+ if (includesSyncLane(remainingLanes)) {
+ {
+ markNestedUpdateScheduled();
+ } // Count the number of times the root synchronously re-renders without
+ // finishing. If there are too many, it indicates an infinite update loop.
- if (nextCache !== previousCache) {
- retainCache(nextCache);
+ if (root === rootWithNestedUpdates) {
+ nestedUpdateCount++;
+ } else {
+ nestedUpdateCount = 0;
+ rootWithNestedUpdates = root;
+ }
+ } else {
+ nestedUpdateCount = 0;
+ } // If layout work was scheduled, flush it now.
- if (previousCache != null) {
- releaseCache(previousCache);
- }
- }
- }
+ flushSyncCallbacks();
- if (enableTransitionTracing) {
- // Get the transitions that were initiatized during the render
- // and add a start transition callback for each of them
- var root = finishedWork.stateNode;
- var incompleteTransitions = root.incompleteTransitions; // Initial render
+ {
+ if (enableDebugTracing) {
+ logCommitStopped();
+ }
+ }
- if (committedTransitions !== null) {
- committedTransitions.forEach(function (transition) {
- addTransitionStartCallbackToPendingTransition(transition);
- });
- clearTransitionsForLanes(finishedRoot, committedLanes);
- }
+ if (enableSchedulingProfiler) {
+ markCommitStopped();
+ }
- incompleteTransitions.forEach(function (markerInstance, transition) {
- var pendingBoundaries = markerInstance.pendingBoundaries;
+ if (enableTransitionTracing) {
+ // We process transitions during passive effects. However, passive effects can be
+ // processed synchronously during the commit phase as well as asynchronously after
+ // paint. At the end of the commit phase, we schedule a callback that will be called
+ // after the next paint. If the transitions have already been processed (passive
+ // effect phase happened synchronously), we will schedule a callback to process
+ // the transitions. However, if we don't have any pending transition callbacks, this
+ // means that the transitions have yet to be processed (passive effects processed after paint)
+ // so we will store the end time of paint so that we can process the transitions
+ // and then call the callback via the correct end time.
+ var prevRootTransitionCallbacks = root.transitionCallbacks;
- if (pendingBoundaries === null || pendingBoundaries.size === 0) {
- if (markerInstance.aborts === null) {
- addTransitionCompleteCallbackToPendingTransition(transition);
- }
+ if (prevRootTransitionCallbacks !== null) {
+ schedulePostPaintCallback(function (endTime) {
+ var prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
- incompleteTransitions.delete(transition);
- }
+ if (prevPendingTransitionCallbacks !== null) {
+ currentPendingTransitionCallbacks = null;
+ scheduleCallback(IdlePriority, function () {
+ processTransitionCallbacks(
+ prevPendingTransitionCallbacks,
+ endTime,
+ prevRootTransitionCallbacks
+ );
});
- clearTransitionsForLanes(finishedRoot, committedLanes);
+ } else {
+ currentEndTime = endTime;
}
- }
-
- break;
+ });
}
+ }
- case LegacyHiddenComponent: {
- {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
+ return null;
+}
+
+function makeErrorInfo(digest, componentStack) {
+ {
+ var errorInfo = {
+ componentStack: componentStack,
+ digest: digest
+ };
+ Object.defineProperty(errorInfo, "digest", {
+ configurable: false,
+ enumerable: true,
+ get: function () {
+ error(
+ 'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' +
+ " This property is deprecated and will be removed in a future version of React." +
+ " To access the digest of an Error look for this property on the Error instance itself."
);
- if (flags & Passive$1) {
- var current = finishedWork.alternate;
- var instance = finishedWork.stateNode;
- commitOffscreenPassiveMountEffects(current, finishedWork, instance);
- }
+ return digest;
}
+ });
+ return errorInfo;
+ }
+}
- break;
- }
+function releaseRootPooledCache(root, remainingLanes) {
+ {
+ var pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);
- case OffscreenComponent: {
- // TODO: Pass `current` as argument to this function
- var _instance3 = finishedWork.stateNode;
- var nextState = finishedWork.memoizedState;
- var isHidden = nextState !== null;
+ if (pooledCacheLanes === NoLanes) {
+ // None of the remaining work relies on the cache pool. Clear it so
+ // subsequent requests get a new cache
+ var pooledCache = root.pooledCache;
- if (isHidden) {
- if (_instance3._visibility & OffscreenPassiveEffectsConnected) {
- // The effects are currently connected. Update them.
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
- } else {
- if (finishedWork.mode & ConcurrentMode) {
- // The effects are currently disconnected. Since the tree is hidden,
- // don't connect them. This also applies to the initial render.
- {
- // "Atomic" effects are ones that need to fire on every commit,
- // even during pre-rendering. An example is updating the reference
- // count on cache instances.
- recursivelyTraverseAtomicPassiveEffects(
- finishedRoot,
- finishedWork
- );
- }
- } else {
- // Legacy Mode: Fire the effects even if the tree is hidden.
- _instance3._visibility |= OffscreenPassiveEffectsConnected;
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
- }
- }
- } else {
- // Tree is visible
- if (_instance3._visibility & OffscreenPassiveEffectsConnected) {
- // The effects are currently connected. Update them.
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
- } else {
- // The effects are currently disconnected. Reconnect them, while also
- // firing effects inside newly mounted trees. This also applies to
- // the initial render.
- _instance3._visibility |= OffscreenPassiveEffectsConnected;
- var includeWorkInProgressEffects =
- (finishedWork.subtreeFlags & PassiveMask) !== NoFlags$1;
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
- }
+ if (pooledCache != null) {
+ root.pooledCache = null;
+ releaseCache(pooledCache);
}
+ }
+ }
+}
- if (flags & Passive$1) {
- var _current = finishedWork.alternate;
- commitOffscreenPassiveMountEffects(_current, finishedWork, _instance3);
- }
+function flushPassiveEffects() {
+ // Returns whether passive effects were flushed.
+ // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
+ // probably just combine the two functions. I believe they were only separate
+ // in the first place because we used to wrap it with
+ // `Scheduler.runWithPriority`, which accepts a function. But now we track the
+ // priority within React itself, so we can mutate the variable directly.
+ if (rootWithPendingPassiveEffects !== null) {
+ // Cache the root since rootWithPendingPassiveEffects is cleared in
+ // flushPassiveEffectsImpl
+ var root = rootWithPendingPassiveEffects; // Cache and clear the remaining lanes flag; it must be reset since this
+ // method can be called from various places, not always from commitRoot
+ // where the remaining lanes are known
- break;
- }
+ var remainingLanes = pendingPassiveEffectsRemainingLanes;
+ pendingPassiveEffectsRemainingLanes = NoLanes;
+ var renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
+ var priority = lowerEventPriority(DefaultEventPriority, renderPriority);
+ var prevTransition = ReactCurrentBatchConfig$1.transition;
+ var previousPriority = getCurrentUpdatePriority();
- case CacheComponent: {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
+ try {
+ ReactCurrentBatchConfig$1.transition = null;
+ setCurrentUpdatePriority(priority);
+ return flushPassiveEffectsImpl();
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig$1.transition = prevTransition; // Once passive effects have run for the tree - giving components a
+ // chance to retain cache instances they use - release the pooled
+ // cache at the root (if there is one)
- if (flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var _current2 = finishedWork.alternate;
- commitCachePassiveMountEffect(_current2, finishedWork);
- }
+ releaseRootPooledCache(root, remainingLanes);
+ }
+ }
- break;
+ return false;
+}
+function enqueuePendingPassiveProfilerEffect(fiber) {
+ {
+ pendingPassiveProfilerEffects.push(fiber);
+
+ if (!rootDoesHavePassiveEffects) {
+ rootDoesHavePassiveEffects = true;
+ scheduleCallback(NormalPriority$1, function () {
+ flushPassiveEffects();
+ return null;
+ });
}
+ }
+}
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
+function flushPassiveEffectsImpl() {
+ if (rootWithPendingPassiveEffects === null) {
+ return false;
+ } // Cache and clear the transitions flag
- if (flags & Passive$1) {
- commitTracingMarkerPassiveMountEffect(finishedWork);
- }
+ var transitions = pendingPassiveTransitions;
+ pendingPassiveTransitions = null;
+ var root = rootWithPendingPassiveEffects;
+ var lanes = pendingPassiveEffectsLanes;
+ rootWithPendingPassiveEffects = null; // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.
+ // Figure out why and fix it. It's not causing any known issues (probably
+ // because it's only used for profiling), but it's a refactor hazard.
- break;
- } // Intentional fallthrough to next branch
- }
- // eslint-disable-next-line-no-fallthrough
+ pendingPassiveEffectsLanes = NoLanes;
- default: {
- recursivelyTraversePassiveMountEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
- );
- break;
- }
+ if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
+ throw new Error("Cannot flush passive effects while already rendering.");
}
-}
-function recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- parentFiber,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
-) {
- // This function visits both newly finished work and nodes that were re-used
- // from a previously committed tree. We cannot check non-static flags if the
- // node was reused.
- var childShouldIncludeWorkInProgressEffects =
- includeWorkInProgressEffects &&
- (parentFiber.subtreeFlags & PassiveMask) !== NoFlags$1; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
+ {
+ isFlushingPassiveEffects = true;
+ didScheduleUpdateDuringPassiveEffects = false;
- var prevDebugFiber = getCurrentFiber();
- var child = parentFiber.child;
+ if (enableDebugTracing) {
+ logPassiveEffectsStarted(lanes);
+ }
+ }
- while (child !== null) {
- reconnectPassiveEffects(
- finishedRoot,
- child,
- committedLanes,
- committedTransitions,
- childShouldIncludeWorkInProgressEffects
- );
- child = child.sibling;
+ if (enableSchedulingProfiler) {
+ markPassiveEffectsStarted(lanes);
}
- setCurrentFiber(prevDebugFiber);
-}
+ var prevExecutionContext = executionContext;
+ executionContext |= CommitContext;
+ commitPassiveUnmountEffects(root.current);
+ commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects
-function reconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions, // This function visits both newly finished work and nodes that were re-used
- // from a previously committed tree. We cannot check non-static flags if the
- // node was reused.
- includeWorkInProgressEffects
-) {
- var flags = finishedWork.flags;
+ {
+ var profilerEffects = pendingPassiveProfilerEffects;
+ pendingPassiveProfilerEffects = [];
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- ); // TODO: Check for PassiveStatic flag
+ for (var i = 0; i < profilerEffects.length; i++) {
+ var fiber = profilerEffects[i];
+ commitPassiveEffectDurations(root, fiber);
+ }
+ }
- commitHookPassiveMountEffects(finishedWork, Passive);
- break;
+ {
+ if (enableDebugTracing) {
+ logPassiveEffectsStopped();
}
- // Unlike commitPassiveMountOnFiber, we don't need to handle HostRoot
- // because this function only visits nodes that are inside an
- // Offscreen fiber.
- // case HostRoot: {
- // ...
- // }
+ }
- case LegacyHiddenComponent: {
- {
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
+ if (enableSchedulingProfiler) {
+ markPassiveEffectsStopped();
+ }
- if (includeWorkInProgressEffects && flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var current = finishedWork.alternate;
- var instance = finishedWork.stateNode;
- commitOffscreenPassiveMountEffects(current, finishedWork, instance);
- }
- }
+ {
+ commitDoubleInvokeEffectsInDEV(root, true);
+ }
- break;
- }
+ executionContext = prevExecutionContext;
+ flushSyncCallbacks();
- case OffscreenComponent: {
- var _instance4 = finishedWork.stateNode;
- var nextState = finishedWork.memoizedState;
- var isHidden = nextState !== null;
+ if (enableTransitionTracing) {
+ var prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
+ var prevRootTransitionCallbacks = root.transitionCallbacks;
+ var prevEndTime = currentEndTime;
- if (isHidden) {
- if (_instance4._visibility & OffscreenPassiveEffectsConnected) {
- // The effects are currently connected. Update them.
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
- } else {
- if (finishedWork.mode & ConcurrentMode) {
- // The effects are currently disconnected. Since the tree is hidden,
- // don't connect them. This also applies to the initial render.
- {
- // "Atomic" effects are ones that need to fire on every commit,
- // even during pre-rendering. An example is updating the reference
- // count on cache instances.
- recursivelyTraverseAtomicPassiveEffects(
- finishedRoot,
- finishedWork
- );
- }
- } else {
- // Legacy Mode: Fire the effects even if the tree is hidden.
- _instance4._visibility |= OffscreenPassiveEffectsConnected;
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
- }
- }
- } else {
- // Tree is visible
- // Since we're already inside a reconnecting tree, it doesn't matter
- // whether the effects are currently connected. In either case, we'll
- // continue traversing the tree and firing all the effects.
- //
- // We do need to set the "connected" flag on the instance, though.
- _instance4._visibility |= OffscreenPassiveEffectsConnected;
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
+ if (
+ prevPendingTransitionCallbacks !== null &&
+ prevRootTransitionCallbacks !== null &&
+ prevEndTime !== null
+ ) {
+ currentPendingTransitionCallbacks = null;
+ currentEndTime = null;
+ scheduleCallback(IdlePriority, function () {
+ processTransitionCallbacks(
+ prevPendingTransitionCallbacks,
+ prevEndTime,
+ prevRootTransitionCallbacks
);
- }
+ });
+ }
+ }
- if (includeWorkInProgressEffects && flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var _current3 = finishedWork.alternate;
- commitOffscreenPassiveMountEffects(_current3, finishedWork, _instance4);
+ {
+ // If additional passive effects were scheduled, increment a counter. If this
+ // exceeds the limit, we'll fire a warning.
+ if (didScheduleUpdateDuringPassiveEffects) {
+ if (root === rootWithPassiveNestedUpdates) {
+ nestedPassiveUpdateCount++;
+ } else {
+ nestedPassiveUpdateCount = 0;
+ rootWithPassiveNestedUpdates = root;
}
-
- break;
+ } else {
+ nestedPassiveUpdateCount = 0;
}
- case CacheComponent: {
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
-
- if (includeWorkInProgressEffects && flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var _current4 = finishedWork.alternate;
- commitCachePassiveMountEffect(_current4, finishedWork);
- }
+ isFlushingPassiveEffects = false;
+ didScheduleUpdateDuringPassiveEffects = false;
+ } // TODO: Move to commitPassiveMountEffects
- break;
- }
+ onPostCommitRoot(root);
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
+ {
+ var stateNode = root.current.stateNode;
+ stateNode.effectDuration = 0;
+ stateNode.passiveEffectDuration = 0;
+ }
- if (includeWorkInProgressEffects && flags & Passive$1) {
- commitTracingMarkerPassiveMountEffect(finishedWork);
- }
+ return true;
+}
- break;
- } // Intentional fallthrough to next branch
- }
- // eslint-disable-next-line-no-fallthrough
+function isAlreadyFailedLegacyErrorBoundary(instance) {
+ return (
+ legacyErrorBoundariesThatAlreadyFailed !== null &&
+ legacyErrorBoundariesThatAlreadyFailed.has(instance)
+ );
+}
+function markLegacyErrorBoundaryAsFailed(instance) {
+ if (legacyErrorBoundariesThatAlreadyFailed === null) {
+ legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
+ } else {
+ legacyErrorBoundariesThatAlreadyFailed.add(instance);
+ }
+}
- default: {
- recursivelyTraverseReconnectPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions,
- includeWorkInProgressEffects
- );
- break;
- }
+function prepareToThrowUncaughtError(error) {
+ if (!hasUncaughtError) {
+ hasUncaughtError = true;
+ firstUncaughtError = error;
}
}
-function recursivelyTraverseAtomicPassiveEffects(
- finishedRoot,
- parentFiber,
- committedLanes,
- committedTransitions
-) {
- // "Atomic" effects are ones that need to fire on every commit, even during
- // pre-rendering. We call this function when traversing a hidden tree whose
- // regular effects are currently disconnected.
- var prevDebugFiber = getCurrentFiber(); // TODO: Add special flag for atomic effects
+var onUncaughtError = prepareToThrowUncaughtError;
- if (parentFiber.subtreeFlags & PassiveMask) {
- var child = parentFiber.child;
+function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {
+ var errorInfo = createCapturedValueAtFiber(error, sourceFiber);
+ var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);
+ var root = enqueueUpdate(rootFiber, update, SyncLane);
+ var eventTime = requestEventTime();
- while (child !== null) {
- setCurrentFiber(child);
- commitAtomicPassiveEffects(finishedRoot, child);
- child = child.sibling;
- }
+ if (root !== null) {
+ markRootUpdated(root, SyncLane, eventTime);
+ ensureRootIsScheduled(root, eventTime);
}
-
- setCurrentFiber(prevDebugFiber);
}
-function commitAtomicPassiveEffects(
- finishedRoot,
- finishedWork,
- committedLanes,
- committedTransitions
-) {
- // "Atomic" effects are ones that need to fire on every commit, even during
- // pre-rendering. We call this function when traversing a hidden tree whose
- // regular effects are currently disconnected.
- var flags = finishedWork.flags;
+function captureCommitPhaseError(sourceFiber, nearestMountedAncestor, error$1) {
+ {
+ reportUncaughtErrorInDEV(error$1);
+ setIsRunningInsertionEffect(false);
+ }
- switch (finishedWork.tag) {
- case OffscreenComponent: {
- recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
+ if (sourceFiber.tag === HostRoot) {
+ // Error was thrown at the root. There is no parent, so the root
+ // itself should capture it.
+ captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error$1);
+ return;
+ }
- if (flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var current = finishedWork.alternate;
- var instance = finishedWork.stateNode;
- commitOffscreenPassiveMountEffects(current, finishedWork, instance);
- }
+ var fiber = null;
- break;
- }
+ if (skipUnmountedBoundaries) {
+ fiber = nearestMountedAncestor;
+ } else {
+ fiber = sourceFiber.return;
+ }
- case CacheComponent: {
- recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
+ while (fiber !== null) {
+ if (fiber.tag === HostRoot) {
+ captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error$1);
+ return;
+ } else if (fiber.tag === ClassComponent) {
+ var ctor = fiber.type;
+ var instance = fiber.stateNode;
- if (flags & Passive$1) {
- // TODO: Pass `current` as argument to this function
- var _current5 = finishedWork.alternate;
- commitCachePassiveMountEffect(_current5, finishedWork);
- }
+ if (
+ typeof ctor.getDerivedStateFromError === "function" ||
+ (typeof instance.componentDidCatch === "function" &&
+ !isAlreadyFailedLegacyErrorBoundary(instance))
+ ) {
+ var errorInfo = createCapturedValueAtFiber(error$1, sourceFiber);
+ var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);
+ var root = enqueueUpdate(fiber, update, SyncLane);
+ var eventTime = requestEventTime();
- break;
- }
- // eslint-disable-next-line-no-fallthrough
+ if (root !== null) {
+ markRootUpdated(root, SyncLane, eventTime);
+ ensureRootIsScheduled(root, eventTime);
+ }
- default: {
- recursivelyTraverseAtomicPassiveEffects(finishedRoot, finishedWork);
- break;
+ return;
+ }
}
+
+ fiber = fiber.return;
}
-}
-function commitPassiveUnmountEffects(finishedWork) {
- setCurrentFiber(finishedWork);
- commitPassiveUnmountOnFiber(finishedWork);
- resetCurrentFiber();
+ {
+ // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning
+ // will fire for errors that are thrown by destroy functions inside deleted
+ // trees. What it should instead do is propagate the error to the parent of
+ // the deleted tree. In the meantime, do not add this warning to the
+ // allowlist; this is only for our internal use.
+ error(
+ "Internal React error: Attempted to capture a commit phase error " +
+ "inside a detached tree. This indicates a bug in React. Likely " +
+ "causes include deleting the same fiber more than once, committing an " +
+ "already-finished tree, or an inconsistent return pointer.\n\n" +
+ "Error message:\n\n%s",
+ error$1
+ );
+ }
}
-
-function detachAlternateSiblings(parentFiber) {
- // A fiber was deleted from this parent fiber, but it's still part of the
- // previous (alternate) parent fiber's list of children. Because children
- // are a linked list, an earlier sibling that's still alive will be
- // connected to the deleted fiber via its `alternate`:
+function attachPingListener(root, wakeable, lanes) {
+ // Attach a ping listener
//
- // live fiber --alternate--> previous live fiber --sibling--> deleted
- // fiber
+ // The data might resolve before we have a chance to commit the fallback. Or,
+ // in the case of a refresh, we'll never commit a fallback. So we need to
+ // attach a listener now. When it resolves ("pings"), we can decide whether to
+ // try rendering the tree again.
//
- // We can't disconnect `alternate` on nodes that haven't been deleted yet,
- // but we can disconnect the `sibling` and `child` pointers.
- var previousFiber = parentFiber.alternate;
+ // Only attach a listener if one does not already exist for the lanes
+ // we're currently rendering (which acts like a "thread ID" here).
+ //
+ // We only need to do this in concurrent mode. Legacy Suspense always
+ // commits fallbacks synchronously, so there are no pings.
+ var pingCache = root.pingCache;
+ var threadIDs;
- if (previousFiber !== null) {
- var detachedChild = previousFiber.child;
+ if (pingCache === null) {
+ pingCache = root.pingCache = new PossiblyWeakMap();
+ threadIDs = new Set();
+ pingCache.set(wakeable, threadIDs);
+ } else {
+ threadIDs = pingCache.get(wakeable);
- if (detachedChild !== null) {
- previousFiber.child = null;
+ if (threadIDs === undefined) {
+ threadIDs = new Set();
+ pingCache.set(wakeable, threadIDs);
+ }
+ }
- do {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- var detachedSibling = detachedChild.sibling; // $FlowFixMe[incompatible-use] found when upgrading Flow
+ if (!threadIDs.has(lanes)) {
+ workInProgressRootDidAttachPingListener = true; // Memoize using the thread ID to prevent redundant listeners.
- detachedChild.sibling = null;
- detachedChild = detachedSibling;
- } while (detachedChild !== null);
+ threadIDs.add(lanes);
+ var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
+
+ {
+ if (isDevToolsPresent) {
+ // If we have pending work still, restore the original updaters
+ restorePendingUpdaters(root, lanes);
+ }
}
- }
-}
-function commitHookPassiveUnmountEffects(
- finishedWork,
- nearestMountedAncestor,
- hookFlags
-) {
- if (shouldProfile(finishedWork)) {
- startPassiveEffectTimer();
- commitHookEffectListUnmount(
- hookFlags,
- finishedWork,
- nearestMountedAncestor
- );
- recordPassiveEffectDuration(finishedWork);
- } else {
- commitHookEffectListUnmount(
- hookFlags,
- finishedWork,
- nearestMountedAncestor
- );
+ wakeable.then(ping, ping);
}
}
-function recursivelyTraversePassiveUnmountEffects(parentFiber) {
- // Deletions effects can be scheduled on any fiber type. They need to happen
- // before the children effects have fired.
- var deletions = parentFiber.deletions;
+function pingSuspendedRoot(root, wakeable, pingedLanes) {
+ var pingCache = root.pingCache;
- if ((parentFiber.flags & ChildDeletion) !== NoFlags$1) {
- if (deletions !== null) {
- for (var i = 0; i < deletions.length; i++) {
- var childToDelete = deletions[i]; // TODO: Convert this to use recursion
+ if (pingCache !== null) {
+ // The wakeable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ pingCache.delete(wakeable);
+ }
- nextEffect = childToDelete;
- commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
- childToDelete,
- parentFiber
- );
+ var eventTime = requestEventTime();
+ markRootPinged(root, pingedLanes);
+ warnIfSuspenseResolutionNotWrappedWithActDEV(root);
+
+ if (
+ workInProgressRoot === root &&
+ isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)
+ ) {
+ // Received a ping at the same priority level at which we're currently
+ // rendering. We might want to restart this render. This should mirror
+ // the logic of whether or not a root suspends once it completes.
+ // TODO: If we're rendering sync either due to Sync, Batched or expired,
+ // we should probably never restart.
+ // If we're suspended with delay, or if it's a retry, we'll always suspend
+ // so we can always restart.
+ if (
+ workInProgressRootExitStatus === RootSuspendedWithDelay ||
+ (workInProgressRootExitStatus === RootSuspended &&
+ includesOnlyRetries(workInProgressRootRenderLanes) &&
+ now$1() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)
+ ) {
+ // Force a restart from the root by unwinding the stack. Unless this is
+ // being called from the render phase, because that would cause a crash.
+ if ((executionContext & RenderContext) === NoContext) {
+ prepareFreshStack(root, NoLanes);
}
+ } else {
+ // Even though we can't restart right now, we might get an
+ // opportunity later. So we mark this render as having a ping.
+ workInProgressRootPingedLanes = mergeLanes(
+ workInProgressRootPingedLanes,
+ pingedLanes
+ );
}
-
- detachAlternateSiblings(parentFiber);
}
- var prevDebugFiber = getCurrentFiber(); // TODO: Split PassiveMask into separate masks for mount and unmount?
+ ensureRootIsScheduled(root, eventTime);
+}
- if (parentFiber.subtreeFlags & PassiveMask) {
- var child = parentFiber.child;
+function retryTimedOutBoundary(boundaryFiber, retryLane) {
+ // The boundary fiber (a Suspense component or SuspenseList component)
+ // previously was rendered in its fallback state. One of the promises that
+ // suspended it has resolved, which means at least part of the tree was
+ // likely unblocked. Try rendering again, at a new lanes.
+ if (retryLane === NoLane) {
+ // TODO: Assign this to `suspenseState.retryLane`? to avoid
+ // unnecessary entanglement?
+ retryLane = requestRetryLane(boundaryFiber);
+ } // TODO: Special case idle priority?
- while (child !== null) {
- setCurrentFiber(child);
- commitPassiveUnmountOnFiber(child);
- child = child.sibling;
- }
- }
+ var eventTime = requestEventTime();
+ var root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
- setCurrentFiber(prevDebugFiber);
+ if (root !== null) {
+ markRootUpdated(root, retryLane, eventTime);
+ ensureRootIsScheduled(root, eventTime);
+ }
}
-function commitPassiveUnmountOnFiber(finishedWork) {
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- recursivelyTraversePassiveUnmountEffects(finishedWork);
+function retryDehydratedSuspenseBoundary(boundaryFiber) {
+ var suspenseState = boundaryFiber.memoizedState;
+ var retryLane = NoLane;
- if (finishedWork.flags & Passive$1) {
- commitHookPassiveUnmountEffects(
- finishedWork,
- finishedWork.return,
- Passive | HasEffect
- );
- }
+ if (suspenseState !== null) {
+ retryLane = suspenseState.retryLane;
+ }
- break;
- }
+ retryTimedOutBoundary(boundaryFiber, retryLane);
+}
+function resolveRetryWakeable(boundaryFiber, wakeable) {
+ var retryLane = NoLane; // Default
- case OffscreenComponent: {
- var instance = finishedWork.stateNode;
- var nextState = finishedWork.memoizedState;
- var isHidden = nextState !== null;
+ var retryCache;
- if (
- isHidden &&
- instance._visibility & OffscreenPassiveEffectsConnected && // For backwards compatibility, don't unmount when a tree suspends. In
- // the future we may change this to unmount after a delay.
- (finishedWork.return === null ||
- finishedWork.return.tag !== SuspenseComponent)
- ) {
- // The effects are currently connected. Disconnect them.
- // TODO: Add option or heuristic to delay before disconnecting the
- // effects. Then if the tree reappears before the delay has elapsed, we
- // can skip toggling the effects entirely.
- instance._visibility &= ~OffscreenPassiveEffectsConnected;
- recursivelyTraverseDisconnectPassiveEffects(finishedWork);
- } else {
- recursivelyTraversePassiveUnmountEffects(finishedWork);
+ switch (boundaryFiber.tag) {
+ case SuspenseComponent:
+ retryCache = boundaryFiber.stateNode;
+ var suspenseState = boundaryFiber.memoizedState;
+
+ if (suspenseState !== null) {
+ retryLane = suspenseState.retryLane;
}
break;
- }
- default: {
- recursivelyTraversePassiveUnmountEffects(finishedWork);
+ case SuspenseListComponent:
+ retryCache = boundaryFiber.stateNode;
+ break;
+
+ case OffscreenComponent: {
+ var instance = boundaryFiber.stateNode;
+ retryCache = instance._retryCache;
break;
}
+
+ default:
+ throw new Error(
+ "Pinged unknown suspense boundary type. " +
+ "This is probably a bug in React."
+ );
}
-}
-function recursivelyTraverseDisconnectPassiveEffects(parentFiber) {
- // Deletions effects can be scheduled on any fiber type. They need to happen
- // before the children effects have fired.
- var deletions = parentFiber.deletions;
+ if (retryCache !== null) {
+ // The wakeable resolved, so we no longer need to memoize, because it will
+ // never be thrown again.
+ retryCache.delete(wakeable);
+ }
- if ((parentFiber.flags & ChildDeletion) !== NoFlags$1) {
- if (deletions !== null) {
- for (var i = 0; i < deletions.length; i++) {
- var childToDelete = deletions[i]; // TODO: Convert this to use recursion
+ retryTimedOutBoundary(boundaryFiber, retryLane);
+} // Computes the next Just Noticeable Difference (JND) boundary.
+// The theory is that a person can't tell the difference between small differences in time.
+// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable
+// difference in the experience. However, waiting for longer might mean that we can avoid
+// showing an intermediate loading state. The longer we have already waited, the harder it
+// is to tell small differences in time. Therefore, the longer we've already waited,
+// the longer we can wait additionally. At some point we have to give up though.
+// We pick a train model where the next boundary commits at a consistent schedule.
+// These particular numbers are vague estimates. We expect to adjust them based on research.
- nextEffect = childToDelete;
- commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
- childToDelete,
- parentFiber
- );
- }
- }
+function jnd(timeElapsed) {
+ return timeElapsed < 120
+ ? 120
+ : timeElapsed < 480
+ ? 480
+ : timeElapsed < 1080
+ ? 1080
+ : timeElapsed < 1920
+ ? 1920
+ : timeElapsed < 3000
+ ? 3000
+ : timeElapsed < 4320
+ ? 4320
+ : ceil(timeElapsed / 1960) * 1960;
+}
- detachAlternateSiblings(parentFiber);
+function throwIfInfiniteUpdateLoopDetected() {
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
+ nestedUpdateCount = 0;
+ nestedPassiveUpdateCount = 0;
+ rootWithNestedUpdates = null;
+ rootWithPassiveNestedUpdates = null;
+ throw new Error(
+ "Maximum update depth exceeded. This can happen when a component " +
+ "repeatedly calls setState inside componentWillUpdate or " +
+ "componentDidUpdate. React limits the number of nested updates to " +
+ "prevent infinite loops."
+ );
}
- var prevDebugFiber = getCurrentFiber(); // TODO: Check PassiveStatic flag
-
- var child = parentFiber.child;
+ {
+ if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
+ nestedPassiveUpdateCount = 0;
+ rootWithPassiveNestedUpdates = null;
- while (child !== null) {
- setCurrentFiber(child);
- disconnectPassiveEffect(child);
- child = child.sibling;
+ error(
+ "Maximum update depth exceeded. This can happen when a component " +
+ "calls setState inside useEffect, but useEffect either doesn't " +
+ "have a dependency array, or one of the dependencies changes on " +
+ "every render."
+ );
+ }
}
-
- setCurrentFiber(prevDebugFiber);
}
-function disconnectPassiveEffect(finishedWork) {
- switch (finishedWork.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- // TODO: Check PassiveStatic flag
- commitHookPassiveUnmountEffects(
- finishedWork,
- finishedWork.return,
- Passive
- ); // When disconnecting passive effects, we fire the effects in the same
- // order as during a deletiong: parent before child
+function flushRenderPhaseStrictModeWarningsInDEV() {
+ {
+ ReactStrictModeWarnings.flushLegacyContextWarning();
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
+ }
+}
- recursivelyTraverseDisconnectPassiveEffects(finishedWork);
- break;
+function commitDoubleInvokeEffectsInDEV(root, hasPassiveEffects) {
+ {
+ {
+ legacyCommitDoubleInvokeEffectsInDEV(root.current, hasPassiveEffects);
}
+ }
+}
- case OffscreenComponent: {
- var instance = finishedWork.stateNode;
-
- if (instance._visibility & OffscreenPassiveEffectsConnected) {
- instance._visibility &= ~OffscreenPassiveEffectsConnected;
- recursivelyTraverseDisconnectPassiveEffects(finishedWork);
- }
+function legacyCommitDoubleInvokeEffectsInDEV(fiber, hasPassiveEffects) {
+ // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
+ // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
+ // Maybe not a big deal since this is DEV only behavior.
+ setCurrentFiber(fiber);
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
- break;
- }
+ if (hasPassiveEffects) {
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
+ }
- default: {
- recursivelyTraverseDisconnectPassiveEffects(finishedWork);
- break;
- }
+ invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
+
+ if (hasPassiveEffects) {
+ invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
}
+
+ resetCurrentFiber();
}
-function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(
- deletedSubtreeRoot,
- nearestMountedAncestor
-) {
- while (nextEffect !== null) {
- var fiber = nextEffect; // Deletion effects fire in parent -> child order
- // TODO: Check if fiber has a PassiveStatic flag
+function invokeEffectsInDev(firstChild, fiberFlags, invokeEffectFn) {
+ var current = firstChild;
+ var subtreeRoot = null;
- setCurrentFiber(fiber);
- commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
- resetCurrentFiber();
- var child = fiber.child; // TODO: Only traverse subtree if it has a PassiveStatic flag.
+ while (current != null) {
+ var primarySubtreeFlag = current.subtreeFlags & fiberFlags;
- if (child !== null) {
- child.return = fiber;
- nextEffect = child;
+ if (
+ current !== subtreeRoot &&
+ current.child != null &&
+ primarySubtreeFlag !== NoFlags$1
+ ) {
+ current = current.child;
} else {
- commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
- deletedSubtreeRoot
- );
+ if ((current.flags & fiberFlags) !== NoFlags$1) {
+ invokeEffectFn(current);
+ }
+
+ if (current.sibling !== null) {
+ current = current.sibling;
+ } else {
+ current = subtreeRoot = current.return;
+ }
}
}
}
-function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(
- deletedSubtreeRoot
-) {
- while (nextEffect !== null) {
- var fiber = nextEffect;
- var sibling = fiber.sibling;
- var returnFiber = fiber.return; // Recursively traverse the entire deleted tree and clean up fiber fields.
- // This is more aggressive than ideal, and the long term goal is to only
- // have to detach the deleted tree at the root.
-
- detachFiberAfterEffects(fiber);
+var didWarnStateUpdateForNotYetMountedComponent = null;
+function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
+ {
+ if ((executionContext & RenderContext) !== NoContext) {
+ // We let the other warning about render phase updates deal with this one.
+ return;
+ }
- if (fiber === deletedSubtreeRoot) {
- nextEffect = null;
+ if (!(fiber.mode & ConcurrentMode)) {
return;
}
- if (sibling !== null) {
- sibling.return = returnFiber;
- nextEffect = sibling;
+ var tag = fiber.tag;
+
+ if (
+ tag !== IndeterminateComponent &&
+ tag !== HostRoot &&
+ tag !== ClassComponent &&
+ tag !== FunctionComponent &&
+ tag !== ForwardRef &&
+ tag !== MemoComponent &&
+ tag !== SimpleMemoComponent
+ ) {
+ // Only warn for user-defined components, not internal ones like Suspense.
return;
+ } // We show the whole stack but dedupe on the top component's name because
+ // the problematic code almost always lies inside that component.
+
+ var componentName = getComponentNameFromFiber(fiber) || "ReactComponent";
+
+ if (didWarnStateUpdateForNotYetMountedComponent !== null) {
+ if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {
+ return;
+ } // $FlowFixMe[incompatible-use] found when upgrading Flow
+
+ didWarnStateUpdateForNotYetMountedComponent.add(componentName);
+ } else {
+ didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);
}
- nextEffect = returnFiber;
+ var previousFiber = current;
+
+ try {
+ setCurrentFiber(fiber);
+
+ error(
+ "Can't perform a React state update on a component that hasn't mounted yet. " +
+ "This indicates that you have a side-effect in your render function that " +
+ "asynchronously later calls tries to update the component. Move this work to " +
+ "useEffect instead."
+ );
+ } finally {
+ if (previousFiber) {
+ setCurrentFiber(fiber);
+ } else {
+ resetCurrentFiber();
+ }
+ }
}
}
+var beginWork;
-function commitPassiveUnmountInsideDeletedTreeOnFiber(
- current,
- nearestMountedAncestor
-) {
- switch (current.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- commitHookPassiveUnmountEffects(current, nearestMountedAncestor, Passive);
- break;
- }
- // TODO: run passive unmount effects when unmounting a root.
- // Because passive unmount effects are not currently run,
- // the cache instance owned by the root will never be freed.
- // When effects are run, the cache should be freed here:
- // case HostRoot: {
- // if (enableCache) {
- // const cache = current.memoizedState.cache;
- // releaseCache(cache);
- // }
- // break;
- // }
+if (replayFailedUnitOfWorkWithInvokeGuardedCallback) {
+ var dummyFiber = null;
- case LegacyHiddenComponent:
- case OffscreenComponent: {
- {
- if (
- current.memoizedState !== null &&
- current.memoizedState.cachePool !== null
- ) {
- var cache = current.memoizedState.cachePool.pool; // Retain/release the cache used for pending (suspended) nodes.
- // Note that this is only reached in the non-suspended/visible case:
- // when the content is suspended/hidden, the retain/release occurs
- // via the parent Suspense component (see case above).
+ beginWork = function (current, unitOfWork, lanes) {
+ // If a component throws an error, we replay it again in a synchronously
+ // dispatched event, so that the debugger will treat it as an uncaught
+ // error See ReactErrorUtils for more information.
+ // Before entering the begin phase, copy the work-in-progress onto a dummy
+ // fiber. If beginWork throws, we'll use this to reset the state.
+ var originalWorkInProgressCopy = assignFiberPropertiesInDEV(
+ dummyFiber,
+ unitOfWork
+ );
- if (cache != null) {
- retainCache(cache);
- }
- }
- }
+ try {
+ return beginWork$1(current, unitOfWork, lanes);
+ } catch (originalError) {
+ if (
+ didSuspendOrErrorWhileHydratingDEV() ||
+ originalError === SuspenseException ||
+ originalError === SelectiveHydrationException ||
+ (originalError !== null &&
+ typeof originalError === "object" &&
+ typeof originalError.then === "function")
+ ) {
+ // Don't replay promises.
+ // Don't replay errors if we are hydrating and have already suspended or handled an error
+ throw originalError;
+ } // Don't reset current debug fiber, since we're about to work on the
+ // same fiber again.
+ // Unwind the failed stack frame
- break;
- }
+ resetSuspendedWorkLoopOnUnwind();
+ unwindInterruptedWork(current, unitOfWork); // Restore the original properties of the fiber.
- case SuspenseComponent: {
- if (enableTransitionTracing) {
- // We need to mark this fiber's parents as deleted
- var offscreenFiber = current.child;
- var instance = offscreenFiber.stateNode;
- var transitions = instance._transitions;
+ assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);
- if (transitions !== null) {
- var abortReason = {
- reason: "suspense",
- name: current.memoizedProps.unstable_name || null
- };
+ if (unitOfWork.mode & ProfileMode) {
+ // Reset the profiler timer.
+ startProfilerTimer(unitOfWork);
+ } // Run beginWork again.
- if (
- current.memoizedState === null ||
- current.memoizedState.dehydrated === null
- ) {
- abortParentMarkerTransitionsForDeletedFiber(
- offscreenFiber,
- abortReason,
- transitions,
- instance,
- true
- );
+ invokeGuardedCallback(
+ null,
+ beginWork$1,
+ null,
+ current,
+ unitOfWork,
+ lanes
+ );
- if (nearestMountedAncestor !== null) {
- abortParentMarkerTransitionsForDeletedFiber(
- nearestMountedAncestor,
- abortReason,
- transitions,
- instance,
- false
- );
- }
- }
+ if (hasCaughtError()) {
+ var replayError = clearCaughtError();
+
+ if (
+ typeof replayError === "object" &&
+ replayError !== null &&
+ replayError._suppressLogging &&
+ typeof originalError === "object" &&
+ originalError !== null &&
+ !originalError._suppressLogging
+ ) {
+ // If suppressed, let the flag carry over to the original error which is the one we'll rethrow.
+ originalError._suppressLogging = true;
}
- }
+ } // We always throw the original error in case the second render pass is not idempotent.
+ // This can happen if a memoized function or CommonJS module doesn't throw after first invocation.
- break;
+ throw originalError;
}
+ };
+} else {
+ beginWork = beginWork$1;
+}
- case CacheComponent: {
- {
- var _cache = current.memoizedState.cache;
- releaseCache(_cache);
- }
+var didWarnAboutUpdateInRender = false;
+var didWarnAboutUpdateInRenderForAnotherComponent;
- break;
- }
+{
+ didWarnAboutUpdateInRenderForAnotherComponent = new Set();
+}
- case TracingMarkerComponent: {
- if (enableTransitionTracing) {
- // We need to mark this fiber's parents as deleted
- var _instance5 = current.stateNode;
- var _transitions = _instance5.transitions;
+function warnAboutRenderPhaseUpdatesInDEV(fiber) {
+ {
+ if (isRendering) {
+ switch (fiber.tag) {
+ case FunctionComponent:
+ case ForwardRef:
+ case SimpleMemoComponent: {
+ var renderingComponentName =
+ (workInProgress && getComponentNameFromFiber(workInProgress)) ||
+ "Unknown"; // Dedupe by the rendering component because it's the one that needs to be fixed.
- if (_transitions !== null) {
- var _abortReason = {
- reason: "marker",
- name: current.memoizedProps.name
- };
- abortParentMarkerTransitionsForDeletedFiber(
- current,
- _abortReason,
- _transitions,
- null,
- true
- );
+ var dedupeKey = renderingComponentName;
- if (nearestMountedAncestor !== null) {
- abortParentMarkerTransitionsForDeletedFiber(
- nearestMountedAncestor,
- _abortReason,
- _transitions,
- null,
- false
+ if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
+ didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
+ var setStateComponentName =
+ getComponentNameFromFiber(fiber) || "Unknown";
+
+ error(
+ "Cannot update a component (`%s`) while rendering a " +
+ "different component (`%s`). To locate the bad setState() call inside `%s`, " +
+ "follow the stack trace as described in https://reactjs.org/link/setstate-in-render",
+ setStateComponentName,
+ renderingComponentName,
+ renderingComponentName
);
}
- }
- }
-
- break;
- }
- }
-}
-function invokeLayoutEffectMountInDEV(fiber) {
- {
- // We don't need to re-check StrictEffectsMode here.
- // This function is only called if that check has already passed.
- switch (fiber.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- try {
- commitHookEffectListMount(Layout | HasEffect, fiber);
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
+ break;
}
- break;
- }
+ case ClassComponent: {
+ if (!didWarnAboutUpdateInRender) {
+ error(
+ "Cannot update during an existing state transition (such as " +
+ "within `render`). Render methods should be a pure " +
+ "function of props and state."
+ );
- case ClassComponent: {
- var instance = fiber.stateNode;
+ didWarnAboutUpdateInRender = true;
+ }
- try {
- instance.componentDidMount();
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
+ break;
}
-
- break;
}
}
}
}
-function invokePassiveEffectMountInDEV(fiber) {
+function restorePendingUpdaters(root, lanes) {
{
- // We don't need to re-check StrictEffectsMode here.
- // This function is only called if that check has already passed.
- switch (fiber.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- try {
- commitHookEffectListMount(Passive | HasEffect, fiber);
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
- }
-
- break;
- }
+ if (isDevToolsPresent) {
+ var memoizedUpdaters = root.memoizedUpdaters;
+ memoizedUpdaters.forEach(function (schedulingFiber) {
+ addFiberToLanesMap(root, schedulingFiber, lanes);
+ }); // This function intentionally does not clear memoized updaters.
+ // Those may still be relevant to the current commit
+ // and a future one (e.g. Suspense).
}
}
}
+var fakeActCallbackNode = {}; // $FlowFixMe[missing-local-annot]
-function invokeLayoutEffectUnmountInDEV(fiber) {
+function scheduleCallback(priorityLevel, callback) {
{
- // We don't need to re-check StrictEffectsMode here.
- // This function is only called if that check has already passed.
- switch (fiber.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- try {
- commitHookEffectListUnmount(Layout | HasEffect, fiber, fiber.return);
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
- }
-
- break;
- }
-
- case ClassComponent: {
- var instance = fiber.stateNode;
-
- if (typeof instance.componentWillUnmount === "function") {
- safelyCallComponentWillUnmount(fiber, fiber.return, instance);
- }
+ // If we're currently inside an `act` scope, bypass Scheduler and push to
+ // the `act` queue instead.
+ var actQueue = ReactCurrentActQueue.current;
- break;
- }
+ if (actQueue !== null) {
+ actQueue.push(callback);
+ return fakeActCallbackNode;
+ } else {
+ return scheduleCallback$2(priorityLevel, callback);
}
}
}
-function invokePassiveEffectUnmountInDEV(fiber) {
- {
- // We don't need to re-check StrictEffectsMode here.
- // This function is only called if that check has already passed.
- switch (fiber.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- try {
- commitHookEffectListUnmount(Passive | HasEffect, fiber, fiber.return);
- } catch (error) {
- captureCommitPhaseError(fiber, fiber.return, error);
- }
- }
- }
- }
+function cancelCallback(callbackNode) {
+ if (callbackNode === fakeActCallbackNode) {
+ return;
+ } // In production, always call Scheduler. This function will be stripped out.
+
+ return cancelCallback$1(callbackNode);
}
-function getCacheSignal() {
- var cache = readContext(CacheContext);
- return cache.controller.signal;
+function shouldForceFlushFallbacksInDEV() {
+ // Never force flush in production. This function should get stripped out.
+ return ReactCurrentActQueue.current !== null;
}
-function getCacheForType(resourceType) {
- var cache = readContext(CacheContext);
- var cacheForType = cache.data.get(resourceType);
+function warnIfUpdatesNotWrappedWithActDEV(fiber) {
+ {
+ if (fiber.mode & ConcurrentMode) {
+ if (!isConcurrentActEnvironment()) {
+ // Not in an act environment. No need to warn.
+ return;
+ }
+ } else {
+ // Legacy mode has additional cases where we suppress a warning.
+ if (!isLegacyActEnvironment()) {
+ // Not in an act environment. No need to warn.
+ return;
+ }
- if (cacheForType === undefined) {
- cacheForType = resourceType();
- cache.data.set(resourceType, cacheForType);
- }
+ if (executionContext !== NoContext) {
+ // Legacy mode doesn't warn if the update is batched, i.e.
+ // batchedUpdates or flushSync.
+ return;
+ }
- return cacheForType;
-}
+ if (
+ fiber.tag !== FunctionComponent &&
+ fiber.tag !== ForwardRef &&
+ fiber.tag !== SimpleMemoComponent
+ ) {
+ // For backwards compatibility with pre-hooks code, legacy mode only
+ // warns for updates that originate from a hook.
+ return;
+ }
+ }
-var DefaultCacheDispatcher = {
- getCacheSignal: getCacheSignal,
- getCacheForType: getCacheForType
-};
+ if (ReactCurrentActQueue.current === null) {
+ var previousFiber = current;
-if (typeof Symbol === "function" && Symbol.for) {
- var symbolFor = Symbol.for;
- symbolFor("selector.component");
- symbolFor("selector.has_pseudo_class");
- symbolFor("selector.role");
- symbolFor("selector.test_id");
- symbolFor("selector.text");
-}
-var commitHooks = [];
-function onCommitRoot() {
- {
- commitHooks.forEach(function (commitHook) {
- return commitHook();
- });
+ try {
+ setCurrentFiber(fiber);
+
+ error(
+ "An update to %s inside a test was not wrapped in act(...).\n\n" +
+ "When testing, code that causes React state updates should be " +
+ "wrapped into act(...):\n\n" +
+ "act(() => {\n" +
+ " /* fire events that update state */\n" +
+ "});\n" +
+ "/* assert on the output */\n\n" +
+ "This ensures that you're testing the behavior the user would see " +
+ "in the browser." +
+ " Learn more at https://reactjs.org/link/wrap-tests-with-act",
+ getComponentNameFromFiber(fiber)
+ );
+ } finally {
+ if (previousFiber) {
+ setCurrentFiber(fiber);
+ } else {
+ resetCurrentFiber();
+ }
+ }
+ }
}
}
-var ReactCurrentActQueue$1 = ReactSharedInternals.ReactCurrentActQueue;
-function isLegacyActEnvironment(fiber) {
- {
- // Legacy mode. We preserve the behavior of React 17's act. It assumes an
- // act environment whenever `jest` is defined, but you can still turn off
- // spurious warnings by setting IS_REACT_ACT_ENVIRONMENT explicitly
- // to false.
- var isReactActEnvironmentGlobal = // $FlowFixMe[cannot-resolve-name] Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
- typeof IS_REACT_ACT_ENVIRONMENT !== "undefined" // $FlowFixMe[cannot-resolve-name]
- ? IS_REACT_ACT_ENVIRONMENT
- : undefined; // $FlowFixMe - Flow doesn't know about jest
-
- var jestIsDefined = typeof jest !== "undefined";
- return jestIsDefined && isReactActEnvironmentGlobal !== false;
- }
-}
-function isConcurrentActEnvironment() {
+function warnIfSuspenseResolutionNotWrappedWithActDEV(root) {
{
- var isReactActEnvironmentGlobal = // $FlowFixMe[cannot-resolve-name] Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
- typeof IS_REACT_ACT_ENVIRONMENT !== "undefined" // $FlowFixMe[cannot-resolve-name]
- ? IS_REACT_ACT_ENVIRONMENT
- : undefined;
-
if (
- !isReactActEnvironmentGlobal &&
- ReactCurrentActQueue$1.current !== null
+ root.tag !== LegacyRoot &&
+ isConcurrentActEnvironment() &&
+ ReactCurrentActQueue.current === null
) {
- // TODO: Include link to relevant documentation page.
error(
- "The current testing environment is not configured to support " +
- "act(...)"
+ "A suspended resource finished loading inside a test, but the event " +
+ "was not wrapped in act(...).\n\n" +
+ "When testing, code that resolves suspended data should be wrapped " +
+ "into act(...):\n\n" +
+ "act(() => {\n" +
+ " /* finish loading suspended data */\n" +
+ "});\n" +
+ "/* assert on the output */\n\n" +
+ "This ensures that you're testing the behavior the user would see " +
+ "in the browser." +
+ " Learn more at https://reactjs.org/link/wrap-tests-with-act"
);
}
-
- return isReactActEnvironmentGlobal;
}
}
-var postPaintCallbackScheduled = false;
-var callbacks = [];
-function schedulePostPaintCallback(callback) {
- callbacks.push(callback);
+function setIsRunningInsertionEffect(isRunning) {
+ {
+ isRunningInsertionEffect = isRunning;
+ }
+}
- if (!postPaintCallbackScheduled) {
- postPaintCallbackScheduled = true;
- requestPostPaintCallback(function (endTime) {
- for (var i = 0; i < callbacks.length; i++) {
- callbacks[i](endTime);
- }
+/* eslint-disable react-internal/prod-error-codes */
+// Used by React Refresh runtime through DevTools Global Hook.
- postPaintCallbackScheduled = false;
- callbacks = [];
- });
+var resolveFamily = null;
+var failedBoundaries = null;
+var setRefreshHandler = function (handler) {
+ {
+ resolveFamily = handler;
}
-}
+};
+function resolveFunctionForHotReloading(type) {
+ {
+ if (resolveFamily === null) {
+ // Hot reloading is disabled.
+ return type;
+ }
-var ceil = Math.ceil;
-var PossiblyWeakMap = typeof WeakMap === "function" ? WeakMap : Map;
-var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher,
- ReactCurrentCache = ReactSharedInternals.ReactCurrentCache,
- ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner,
- ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig,
- ReactCurrentActQueue = ReactSharedInternals.ReactCurrentActQueue;
-var NoContext =
- /* */
- 0;
-var BatchedContext =
- /* */
- 1;
-var RenderContext =
- /* */
- 2;
-var CommitContext =
- /* */
- 4;
-var RootInProgress = 0;
-var RootFatalErrored = 1;
-var RootErrored = 2;
-var RootSuspended = 3;
-var RootSuspendedWithDelay = 4;
-var RootCompleted = 5;
-var RootDidNotComplete = 6; // Describes where we are in the React execution stack
+ var family = resolveFamily(type);
-var executionContext = NoContext; // The root we're working on
+ if (family === undefined) {
+ return type;
+ } // Use the latest known implementation.
-var workInProgressRoot = null; // The fiber we're working on
+ return family.current;
+ }
+}
+function resolveClassForHotReloading(type) {
+ // No implementation differences.
+ return resolveFunctionForHotReloading(type);
+}
+function resolveForwardRefForHotReloading(type) {
+ {
+ if (resolveFamily === null) {
+ // Hot reloading is disabled.
+ return type;
+ }
-var workInProgress = null; // The lanes we're rendering
+ var family = resolveFamily(type);
-var workInProgressRootRenderLanes = NoLanes;
-var NotSuspended = 0;
-var SuspendedOnError = 1;
-var SuspendedOnData = 2;
-var SuspendedOnImmediate = 3;
-var SuspendedOnDeprecatedThrowPromise = 4;
-var SuspendedAndReadyToContinue = 5;
-var SuspendedOnHydration = 6; // When this is true, the work-in-progress fiber just suspended (or errored) and
-// we've yet to unwind the stack. In some cases, we may yield to the main thread
-// after this happens. If the fiber is pinged before we resume, we can retry
-// immediately instead of unwinding the stack.
+ if (family === undefined) {
+ // Check if we're dealing with a real forwardRef. Don't want to crash early.
+ if (
+ type !== null &&
+ type !== undefined &&
+ typeof type.render === "function"
+ ) {
+ // ForwardRef is special because its resolved .type is an object,
+ // but it's possible that we only have its inner render function in the map.
+ // If that inner render function is different, we'll build a new forwardRef type.
+ var currentRender = resolveFunctionForHotReloading(type.render);
-var workInProgressSuspendedReason = NotSuspended;
-var workInProgressThrownValue = null; // Whether a ping listener was attached during this render. This is slightly
-// different that whether something suspended, because we don't add multiple
-// listeners to a promise we've already seen (per root and lane).
+ if (type.render !== currentRender) {
+ var syntheticType = {
+ $$typeof: REACT_FORWARD_REF_TYPE,
+ render: currentRender
+ };
-var workInProgressRootDidAttachPingListener = false; // A contextual version of workInProgressRootRenderLanes. It is a superset of
-// the lanes that we started working on at the root. When we enter a subtree
-// that is currently hidden, we add the lanes that would have committed if
-// the hidden tree hadn't been deferred. This is modified by the
-// HiddenContext module.
-//
-// Most things in the work loop should deal with workInProgressRootRenderLanes.
-// Most things in begin/complete phases should deal with renderLanes.
+ if (type.displayName !== undefined) {
+ syntheticType.displayName = type.displayName;
+ }
-var renderLanes = NoLanes; // Whether to root completed, errored, suspended, etc.
+ return syntheticType;
+ }
+ }
-var workInProgressRootExitStatus = RootInProgress; // A fatal error, if one is thrown
+ return type;
+ } // Use the latest known implementation.
-var workInProgressRootFatalError = null; // The work left over by components that were visited during this render. Only
-// includes unprocessed updates, not work in bailed out children.
+ return family.current;
+ }
+}
+function isCompatibleFamilyForHotReloading(fiber, element) {
+ {
+ if (resolveFamily === null) {
+ // Hot reloading is disabled.
+ return false;
+ }
-var workInProgressRootSkippedLanes = NoLanes; // Lanes that were updated (in an interleaved event) during this render.
+ var prevType = fiber.elementType;
+ var nextType = element.type; // If we got here, we know types aren't === equal.
-var workInProgressRootInterleavedUpdatedLanes = NoLanes; // Lanes that were updated during the render phase (*not* an interleaved event).
+ var needsCompareFamilies = false;
+ var $$typeofNextType =
+ typeof nextType === "object" && nextType !== null
+ ? nextType.$$typeof
+ : null;
-var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during the render phase.
+ switch (fiber.tag) {
+ case ClassComponent: {
+ if (typeof nextType === "function") {
+ needsCompareFamilies = true;
+ }
-var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
-// We will log them once the tree commits.
+ break;
+ }
-var workInProgressRootRecoverableErrors = null; // The most recent time we committed a fallback. This lets us ensure a train
-// model where we don't commit new loading states in too quick succession.
+ case FunctionComponent: {
+ if (typeof nextType === "function") {
+ needsCompareFamilies = true;
+ } else if ($$typeofNextType === REACT_LAZY_TYPE) {
+ // We don't know the inner type yet.
+ // We're going to assume that the lazy inner type is stable,
+ // and so it is sufficient to avoid reconciling it away.
+ // We're not going to unwrap or actually use the new lazy type.
+ needsCompareFamilies = true;
+ }
-var globalMostRecentFallbackTime = 0;
-var FALLBACK_THROTTLE_MS = 500; // The absolute time for when we should start giving up on rendering
-// more and prefer CPU suspense heuristics instead.
+ break;
+ }
-var workInProgressRootRenderTargetTime = Infinity; // How long a render is supposed to take before we start following CPU
-// suspense heuristics and opt out of rendering more content.
+ case ForwardRef: {
+ if ($$typeofNextType === REACT_FORWARD_REF_TYPE) {
+ needsCompareFamilies = true;
+ } else if ($$typeofNextType === REACT_LAZY_TYPE) {
+ needsCompareFamilies = true;
+ }
-var RENDER_TIMEOUT_MS = 500;
-var workInProgressTransitions = null;
-function getWorkInProgressTransitions() {
- return workInProgressTransitions;
-}
-var currentPendingTransitionCallbacks = null;
-var currentEndTime = null;
-function addTransitionStartCallbackToPendingTransition(transition) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: [],
- transitionProgress: null,
- transitionComplete: null,
- markerProgress: null,
- markerIncomplete: null,
- markerComplete: null
- };
- }
+ break;
+ }
- if (currentPendingTransitionCallbacks.transitionStart === null) {
- currentPendingTransitionCallbacks.transitionStart = [];
- }
+ case MemoComponent:
+ case SimpleMemoComponent: {
+ if ($$typeofNextType === REACT_MEMO_TYPE) {
+ // TODO: if it was but can no longer be simple,
+ // we shouldn't set this.
+ needsCompareFamilies = true;
+ } else if ($$typeofNextType === REACT_LAZY_TYPE) {
+ needsCompareFamilies = true;
+ }
- currentPendingTransitionCallbacks.transitionStart.push(transition);
- }
-}
-function addMarkerProgressCallbackToPendingTransition(
- markerName,
- transitions,
- pendingBoundaries
-) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: null,
- transitionProgress: null,
- transitionComplete: null,
- markerProgress: new Map(),
- markerIncomplete: null,
- markerComplete: null
- };
- }
+ break;
+ }
- if (currentPendingTransitionCallbacks.markerProgress === null) {
- currentPendingTransitionCallbacks.markerProgress = new Map();
- }
+ default:
+ return false;
+ } // Check if both types have a family and it's the same one.
- currentPendingTransitionCallbacks.markerProgress.set(markerName, {
- pendingBoundaries: pendingBoundaries,
- transitions: transitions
- });
- }
-}
-function addMarkerIncompleteCallbackToPendingTransition(
- markerName,
- transitions,
- aborts
-) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: null,
- transitionProgress: null,
- transitionComplete: null,
- markerProgress: null,
- markerIncomplete: new Map(),
- markerComplete: null
- };
- }
+ if (needsCompareFamilies) {
+ // Note: memo() and forwardRef() we'll compare outer rather than inner type.
+ // This means both of them need to be registered to preserve state.
+ // If we unwrapped and compared the inner types for wrappers instead,
+ // then we would risk falsely saying two separate memo(Foo)
+ // calls are equivalent because they wrap the same Foo function.
+ var prevFamily = resolveFamily(prevType); // $FlowFixMe[not-a-function] found when upgrading Flow
- if (currentPendingTransitionCallbacks.markerIncomplete === null) {
- currentPendingTransitionCallbacks.markerIncomplete = new Map();
+ if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) {
+ return true;
+ }
}
- currentPendingTransitionCallbacks.markerIncomplete.set(markerName, {
- transitions: transitions,
- aborts: aborts
- });
+ return false;
}
}
-function addMarkerCompleteCallbackToPendingTransition(markerName, transitions) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: null,
- transitionProgress: null,
- transitionComplete: null,
- markerProgress: null,
- markerIncomplete: null,
- markerComplete: new Map()
- };
- }
-
- if (currentPendingTransitionCallbacks.markerComplete === null) {
- currentPendingTransitionCallbacks.markerComplete = new Map();
+function markFailedErrorBoundaryForHotReloading(fiber) {
+ {
+ if (resolveFamily === null) {
+ // Hot reloading is disabled.
+ return;
}
- currentPendingTransitionCallbacks.markerComplete.set(
- markerName,
- transitions
- );
- }
-}
-function addTransitionProgressCallbackToPendingTransition(
- transition,
- boundaries
-) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: null,
- transitionProgress: new Map(),
- transitionComplete: null,
- markerProgress: null,
- markerIncomplete: null,
- markerComplete: null
- };
+ if (typeof WeakSet !== "function") {
+ return;
}
- if (currentPendingTransitionCallbacks.transitionProgress === null) {
- currentPendingTransitionCallbacks.transitionProgress = new Map();
+ if (failedBoundaries === null) {
+ failedBoundaries = new WeakSet();
}
- currentPendingTransitionCallbacks.transitionProgress.set(
- transition,
- boundaries
- );
+ failedBoundaries.add(fiber);
}
}
-function addTransitionCompleteCallbackToPendingTransition(transition) {
- if (enableTransitionTracing) {
- if (currentPendingTransitionCallbacks === null) {
- currentPendingTransitionCallbacks = {
- transitionStart: null,
- transitionProgress: null,
- transitionComplete: [],
- markerProgress: null,
- markerIncomplete: null,
- markerComplete: null
- };
+var scheduleRefresh = function (root, update) {
+ {
+ if (resolveFamily === null) {
+ // Hot reloading is disabled.
+ return;
}
- if (currentPendingTransitionCallbacks.transitionComplete === null) {
- currentPendingTransitionCallbacks.transitionComplete = [];
+ var staleFamilies = update.staleFamilies,
+ updatedFamilies = update.updatedFamilies;
+ flushPassiveEffects();
+ flushSync$1(function () {
+ scheduleFibersWithFamiliesRecursively(
+ root.current,
+ updatedFamilies,
+ staleFamilies
+ );
+ });
+ }
+};
+var scheduleRoot = function (root, element) {
+ {
+ if (root.context !== emptyContextObject) {
+ // Super edge case: root has a legacy _renderSubtree context
+ // but we don't know the parentComponent so we can't pass it.
+ // Just ignore. We'll delete this with _renderSubtree code path later.
+ return;
}
- currentPendingTransitionCallbacks.transitionComplete.push(transition);
+ flushPassiveEffects();
+ flushSync$1(function () {
+ updateContainer(element, root, null, null);
+ });
}
-}
-
-function resetRenderTimer() {
- workInProgressRootRenderTargetTime = now$1() + RENDER_TIMEOUT_MS;
-}
-
-function getRenderTargetTime() {
- return workInProgressRootRenderTargetTime;
-}
-var hasUncaughtError = false;
-var firstUncaughtError = null;
-var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfilerNestedUpdateScheduledHook is true;
-// to track which root is currently committing layout effects.
-
-var rootCommittingMutationOrLayoutEffects = null;
-var rootDoesHavePassiveEffects = false;
-var rootWithPendingPassiveEffects = null;
-var pendingPassiveEffectsLanes = NoLanes;
-var pendingPassiveProfilerEffects = [];
-var pendingPassiveEffectsRemainingLanes = NoLanes;
-var pendingPassiveTransitions = null; // Use these to prevent an infinite loop of nested updates
-
-var NESTED_UPDATE_LIMIT = 50;
-var nestedUpdateCount = 0;
-var rootWithNestedUpdates = null;
-var isFlushingPassiveEffects = false;
-var didScheduleUpdateDuringPassiveEffects = false;
-var NESTED_PASSIVE_UPDATE_LIMIT = 50;
-var nestedPassiveUpdateCount = 0;
-var rootWithPassiveNestedUpdates = null; // If two updates are scheduled within the same event, we should treat their
-// event times as simultaneous, even if the actual clock time has advanced
-// between the first and second call.
-
-var currentEventTime = NoTimestamp;
-var currentEventTransitionLane = NoLanes;
-var isRunningInsertionEffect = false;
-function getWorkInProgressRoot() {
- return workInProgressRoot;
-}
-function getWorkInProgressRootRenderLanes() {
- return workInProgressRootRenderLanes;
-}
-function requestEventTime() {
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- // We're inside React, so it's fine to read the actual time.
- return now$1();
- } // We're not inside React, so we may be in the middle of a browser event.
-
- if (currentEventTime !== NoTimestamp) {
- // Use the same start time for all updates until we enter React again.
- return currentEventTime;
- } // This is the first update since React yielded. Compute a new start time.
-
- currentEventTime = now$1();
- return currentEventTime;
-}
-function requestUpdateLane(fiber) {
- // Special cases
- var mode = fiber.mode;
+};
- if ((mode & ConcurrentMode) === NoMode) {
- return SyncLane;
- } else if (
- !deferRenderPhaseUpdateToNextBatch &&
- (executionContext & RenderContext) !== NoContext &&
- workInProgressRootRenderLanes !== NoLanes
- ) {
- // This is a render phase update. These are not officially supported. The
- // old behavior is to give this the same "thread" (lanes) as
- // whatever is currently rendering. So if you call `setState` on a component
- // that happens later in the same render, it will flush. Ideally, we want to
- // remove the special case and treat them as if they came from an
- // interleaved event. Regardless, this pattern is not officially supported.
- // This behavior is only a fallback. The flag only exists until we can roll
- // out the setState warning, since existing code might accidentally rely on
- // the current behavior.
- return pickArbitraryLane(workInProgressRootRenderLanes);
- }
+function scheduleFibersWithFamiliesRecursively(
+ fiber,
+ updatedFamilies,
+ staleFamilies
+) {
+ {
+ var alternate = fiber.alternate,
+ child = fiber.child,
+ sibling = fiber.sibling,
+ tag = fiber.tag,
+ type = fiber.type;
+ var candidateType = null;
- var isTransition = requestCurrentTransition() !== NoTransition;
+ switch (tag) {
+ case FunctionComponent:
+ case SimpleMemoComponent:
+ case ClassComponent:
+ candidateType = type;
+ break;
- if (isTransition) {
- if (ReactCurrentBatchConfig.transition !== null) {
- var transition = ReactCurrentBatchConfig.transition;
+ case ForwardRef:
+ candidateType = type.render;
+ break;
+ }
- if (!transition._updatedFibers) {
- transition._updatedFibers = new Set();
- }
+ if (resolveFamily === null) {
+ throw new Error("Expected resolveFamily to be set during hot reload.");
+ }
- transition._updatedFibers.add(fiber);
- } // The algorithm for assigning an update to a lane should be stable for all
- // updates at the same priority within the same event. To do this, the
- // inputs to the algorithm must be the same.
- //
- // The trick we use is to cache the first of each of these inputs within an
- // event. Then reset the cached values once we can be sure the event is
- // over. Our heuristic for that is whenever we enter a concurrent work loop.
+ var needsRender = false;
+ var needsRemount = false;
- if (currentEventTransitionLane === NoLane) {
- // All transitions within the same event are assigned the same lane.
- currentEventTransitionLane = claimNextTransitionLane();
+ if (candidateType !== null) {
+ var family = resolveFamily(candidateType);
+
+ if (family !== undefined) {
+ if (staleFamilies.has(family)) {
+ needsRemount = true;
+ } else if (updatedFamilies.has(family)) {
+ if (tag === ClassComponent) {
+ needsRemount = true;
+ } else {
+ needsRender = true;
+ }
+ }
+ }
}
- return currentEventTransitionLane;
- } // Updates originating inside certain React methods, like flushSync, have
- // their priority set by tracking it with a context variable.
- //
- // The opaque type returned by the host config is internally a lane, so we can
- // use that directly.
- // TODO: Move this type conversion to the event priority module.
+ if (failedBoundaries !== null) {
+ if (
+ failedBoundaries.has(fiber) || // $FlowFixMe[incompatible-use] found when upgrading Flow
+ (alternate !== null && failedBoundaries.has(alternate))
+ ) {
+ needsRemount = true;
+ }
+ }
- var updateLane = getCurrentUpdatePriority$1();
+ if (needsRemount) {
+ fiber._debugNeedsRemount = true;
+ }
- if (updateLane !== NoLane) {
- return updateLane;
- } // This update originated outside React. Ask the host environment for an
- // appropriate priority, based on the type of event.
- //
- // The opaque type returned by the host config is internally a lane, so we can
- // use that directly.
- // TODO: Move this type conversion to the event priority module.
+ if (needsRemount || needsRender) {
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- var eventLane = getCurrentEventPriority();
- return eventLane;
-}
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+ }
-function requestRetryLane(fiber) {
- // This is a fork of `requestUpdateLane` designed specifically for Suspense
- // "retries" — a special update that attempts to flip a Suspense boundary
- // from its placeholder state to its primary/resolved state.
- // Special cases
- var mode = fiber.mode;
+ if (child !== null && !needsRemount) {
+ scheduleFibersWithFamiliesRecursively(
+ child,
+ updatedFamilies,
+ staleFamilies
+ );
+ }
- if ((mode & ConcurrentMode) === NoMode) {
- return SyncLane;
+ if (sibling !== null) {
+ scheduleFibersWithFamiliesRecursively(
+ sibling,
+ updatedFamilies,
+ staleFamilies
+ );
+ }
}
-
- return claimNextRetryLane();
}
-function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {
+var findHostInstancesForRefresh = function (root, families) {
{
- if (isRunningInsertionEffect) {
- error("useInsertionEffect must not schedule updates.");
- }
+ var hostInstances = new Set();
+ var types = new Set(
+ families.map(function (family) {
+ return family.current;
+ })
+ );
+ findHostInstancesForMatchingFibersRecursively(
+ root.current,
+ types,
+ hostInstances
+ );
+ return hostInstances;
}
+};
+function findHostInstancesForMatchingFibersRecursively(
+ fiber,
+ types,
+ hostInstances
+) {
{
- if (isFlushingPassiveEffects) {
- didScheduleUpdateDuringPassiveEffects = true;
+ var child = fiber.child,
+ sibling = fiber.sibling,
+ tag = fiber.tag,
+ type = fiber.type;
+ var candidateType = null;
+
+ switch (tag) {
+ case FunctionComponent:
+ case SimpleMemoComponent:
+ case ClassComponent:
+ candidateType = type;
+ break;
+
+ case ForwardRef:
+ candidateType = type.render;
+ break;
}
- } // Check if the work loop is currently suspended and waiting for data to
- // finish loading.
- if (
- workInProgressSuspendedReason === SuspendedOnData &&
- root === workInProgressRoot
- ) {
- // The incoming update might unblock the current render. Interrupt the
- // current attempt and restart from the top.
- prepareFreshStack(root, NoLanes);
- markRootSuspended(root, workInProgressRootRenderLanes);
- } // Mark that the root has a pending update.
+ var didMatch = false;
- markRootUpdated(root, lane, eventTime);
+ if (candidateType !== null) {
+ if (types.has(candidateType)) {
+ didMatch = true;
+ }
+ }
- if (
- (executionContext & RenderContext) !== NoLanes &&
- root === workInProgressRoot
- ) {
- // This update was dispatched during the render phase. This is a mistake
- // if the update originates from user space (with the exception of local
- // hook updates, which are handled differently and don't reach this
- // function), but there are some internal React features that use this as
- // an implementation detail, like selective hydration.
- warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase
- } else {
- // This is a normal update, scheduled from outside the render phase. For
- // example, during an input event.
- {
- if (isDevToolsPresent) {
- addFiberToLanesMap(root, fiber, lane);
+ if (didMatch) {
+ // We have a match. This only drills down to the closest host components.
+ // There's no need to search deeper because for the purpose of giving
+ // visual feedback, "flashing" outermost parent rectangles is sufficient.
+ findHostInstancesForFiberShallowly(fiber, hostInstances);
+ } else {
+ // If there's no match, maybe there will be one further down in the child tree.
+ if (child !== null) {
+ findHostInstancesForMatchingFibersRecursively(
+ child,
+ types,
+ hostInstances
+ );
}
}
- warnIfUpdatesNotWrappedWithActDEV(fiber);
+ if (sibling !== null) {
+ findHostInstancesForMatchingFibersRecursively(
+ sibling,
+ types,
+ hostInstances
+ );
+ }
+ }
+}
- if (enableProfilerNestedUpdateScheduledHook) {
- if (
- (executionContext & CommitContext) !== NoContext &&
- root === rootCommittingMutationOrLayoutEffects
- ) {
- if (fiber.mode & ProfileMode) {
- var current = fiber;
+function findHostInstancesForFiberShallowly(fiber, hostInstances) {
+ {
+ var foundHostInstances = findChildHostInstancesForFiberShallowly(
+ fiber,
+ hostInstances
+ );
- while (current !== null) {
- if (current.tag === Profiler) {
- var _current$memoizedProp = current.memoizedProps,
- id = _current$memoizedProp.id,
- onNestedUpdateScheduled =
- _current$memoizedProp.onNestedUpdateScheduled;
+ if (foundHostInstances) {
+ return;
+ } // If we didn't find any host children, fallback to closest host parent.
- if (typeof onNestedUpdateScheduled === "function") {
- onNestedUpdateScheduled(id);
- }
- }
+ var node = fiber;
- current = current.return;
- }
- }
- }
- }
+ while (true) {
+ switch (node.tag) {
+ case HostSingleton:
+ case HostComponent:
+ hostInstances.add(node.stateNode);
+ return;
- if (enableTransitionTracing) {
- var transition = ReactCurrentBatchConfig.transition;
+ case HostPortal:
+ hostInstances.add(node.stateNode.containerInfo);
+ return;
- if (transition !== null && transition.name != null) {
- if (transition.startTime === -1) {
- transition.startTime = now$1();
- }
+ case HostRoot:
+ hostInstances.add(node.stateNode.containerInfo);
+ return;
+ }
- addTransitionToLanesMap(root, transition, lane);
+ if (node.return === null) {
+ throw new Error("Expected to reach root first.");
}
+
+ node = node.return;
}
+ }
+}
- if (root === workInProgressRoot) {
- // Received an update to a tree that's in the middle of rendering. Mark
- // that there was an interleaved update work on this root. Unless the
- // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
- // phase update. In that case, we don't treat render phase updates as if
- // they were interleaved, for backwards compat reasons.
+function findChildHostInstancesForFiberShallowly(fiber, hostInstances) {
+ {
+ var node = fiber;
+ var foundHostInstances = false;
+
+ while (true) {
if (
- deferRenderPhaseUpdateToNextBatch ||
- (executionContext & RenderContext) === NoContext
+ node.tag === HostComponent ||
+ node.tag === HostHoistable ||
+ node.tag === HostSingleton
) {
- workInProgressRootInterleavedUpdatedLanes = mergeLanes(
- workInProgressRootInterleavedUpdatedLanes,
- lane
- );
+ // We got a match.
+ foundHostInstances = true;
+ hostInstances.add(node.stateNode); // There may still be more, so keep searching.
+ } else if (node.child !== null) {
+ node.child.return = node;
+ node = node.child;
+ continue;
}
- if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
- // The root already suspended with a delay, which means this render
- // definitely won't finish. Since we have a new update, let's mark it as
- // suspended now, right before marking the incoming update. This has the
- // effect of interrupting the current render and switching to the update.
- // TODO: Make sure this doesn't override pings that happen while we've
- // already started rendering.
- markRootSuspended(root, workInProgressRootRenderLanes);
+ if (node === fiber) {
+ return foundHostInstances;
}
- }
- ensureRootIsScheduled(root, eventTime);
+ while (node.sibling === null) {
+ if (node.return === null || node.return === fiber) {
+ return foundHostInstances;
+ }
- if (
- lane === SyncLane &&
- executionContext === NoContext &&
- (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
- !ReactCurrentActQueue.isBatchingLegacy
- ) {
- // Flush the synchronous work now, unless we're already working or inside
- // a batch. This is intentionally inside scheduleUpdateOnFiber instead of
- // scheduleCallbackForFiber to preserve the ability to schedule a callback
- // without immediately flushing it. We only do this for user-initiated
- // updates, to preserve historical behavior of legacy mode.
- resetRenderTimer();
- flushSyncCallbacksOnlyInLegacyMode();
+ node = node.return;
+ }
+
+ node.sibling.return = node.return;
+ node = node.sibling;
}
}
+
+ return false;
}
-function scheduleInitialHydrationOnRoot(root, lane, eventTime) {
- // This is a special fork of scheduleUpdateOnFiber that is only used to
- // schedule the initial hydration of a root that has just been created. Most
- // of the stuff in scheduleUpdateOnFiber can be skipped.
- //
- // The main reason for this separate path, though, is to distinguish the
- // initial children from subsequent updates. In fully client-rendered roots
- // (createRoot instead of hydrateRoot), all top-level renders are modeled as
- // updates, but hydration roots are special because the initial render must
- // match what was rendered on the server.
- var current = root.current;
- current.lanes = lane;
- markRootUpdated(root, lane, eventTime);
- ensureRootIsScheduled(root, eventTime);
+
+var hasBadMapPolyfill;
+
+{
+ hasBadMapPolyfill = false;
+
+ try {
+ var nonExtensibleObject = Object.preventExtensions({});
+ /* eslint-disable no-new */
+
+ new Map([[nonExtensibleObject, null]]);
+ new Set([nonExtensibleObject]);
+ /* eslint-enable no-new */
+ } catch (e) {
+ // TODO: Consider warning about bad polyfills
+ hasBadMapPolyfill = true;
+ }
}
-function isUnsafeClassRenderPhaseUpdate(fiber) {
- // Check if this is a render phase update. Only called by class components,
- // which special (deprecated) behavior for UNSAFE_componentWillReceive props.
- return (
- // TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
- // decided not to enable it.
- (!deferRenderPhaseUpdateToNextBatch ||
- (fiber.mode & ConcurrentMode) === NoMode) &&
- (executionContext & RenderContext) !== NoContext
- );
-} // Use this function to schedule a task for a root. There's only one task per
-// root; if a task was already scheduled, we'll check to make sure the priority
-// of the existing task is the same as the priority of the next level that the
-// root has work on. This function is called on every update, and right before
-// exiting a task.
-function ensureRootIsScheduled(root, currentTime) {
- var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as
- // expired so we know to work on those next.
+function FiberNode(tag, pendingProps, key, mode) {
+ // Instance
+ this.tag = tag;
+ this.key = key;
+ this.elementType = null;
+ this.type = null;
+ this.stateNode = null; // Fiber
- markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.
+ this.return = null;
+ this.child = null;
+ this.sibling = null;
+ this.index = 0;
+ this.ref = null;
+ this.refCleanup = null;
+ this.pendingProps = pendingProps;
+ this.memoizedProps = null;
+ this.updateQueue = null;
+ this.memoizedState = null;
+ this.dependencies = null;
+ this.mode = mode; // Effects
- var nextLanes = getNextLanes(
- root,
- root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
- );
+ this.flags = NoFlags$1;
+ this.subtreeFlags = NoFlags$1;
+ this.deletions = null;
+ this.lanes = NoLanes;
+ this.childLanes = NoLanes;
+ this.alternate = null;
- if (nextLanes === NoLanes) {
- // Special case: There's nothing to work on.
- if (existingCallbackNode !== null) {
- cancelCallback(existingCallbackNode);
- }
+ {
+ // Note: The following is done to avoid a v8 performance cliff.
+ //
+ // Initializing the fields below to smis and later updating them with
+ // double values will cause Fibers to end up having separate shapes.
+ // This behavior/bug has something to do with Object.preventExtension().
+ // Fortunately this only impacts DEV builds.
+ // Unfortunately it makes React unusably slow for some applications.
+ // To work around this, initialize the fields below with doubles.
+ //
+ // Learn more about this here:
+ // https://github.com/facebook/react/issues/14365
+ // https://bugs.chromium.org/p/v8/issues/detail?id=8538
+ this.actualDuration = Number.NaN;
+ this.actualStartTime = Number.NaN;
+ this.selfBaseDuration = Number.NaN;
+ this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
+ // This won't trigger the performance cliff mentioned above,
+ // and it simplifies other profiler code (including DevTools).
- root.callbackNode = null;
- root.callbackPriority = NoLane;
- return;
- } // If this root is currently suspended and waiting for data to resolve, don't
- // schedule a task to render it. We'll either wait for a ping, or wait to
- // receive an update.
+ this.actualDuration = 0;
+ this.actualStartTime = -1;
+ this.selfBaseDuration = 0;
+ this.treeBaseDuration = 0;
+ }
- if (
- workInProgressSuspendedReason === SuspendedOnData &&
- workInProgressRoot === root
- ) {
- root.callbackPriority = NoLane;
- root.callbackNode = null;
- return;
- } // We use the highest priority lane to represent the priority of the callback.
+ {
+ // This isn't directly used but is handy for debugging internals:
+ this._debugSource = null;
+ this._debugOwner = null;
+ this._debugNeedsRemount = false;
+ this._debugHookTypes = null;
- var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it.
+ 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 existingCallbackPriority = root.callbackPriority;
+function createFiber(tag, pendingProps, key, mode) {
+ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
+ return new FiberNode(tag, pendingProps, key, mode);
+}
- if (
- existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a
- // Scheduler task, rather than an `act` task, cancel it and re-scheduled
- // on the `act` queue.
- !(
- ReactCurrentActQueue.current !== null &&
- existingCallbackNode !== fakeActCallbackNode
- )
- ) {
- {
- // If we're going to re-use an existing task, it needs to exist.
- // Assume that discrete update microtasks are non-cancellable and null.
- // TODO: Temporary until we confirm this warning is not fired.
- if (
- existingCallbackNode == null &&
- !includesSyncLane(existingCallbackPriority)
- ) {
- error(
- "Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue."
- );
- }
- } // The priority hasn't changed. We can reuse the existing task. Exit.
+function shouldConstruct(Component) {
+ var prototype = Component.prototype;
+ return !!(prototype && prototype.isReactComponent);
+}
+
+function isSimpleFunctionComponent(type) {
+ return (
+ typeof type === "function" &&
+ !shouldConstruct(type) &&
+ type.defaultProps === undefined
+ );
+}
+function resolveLazyComponentTag(Component) {
+ if (typeof Component === "function") {
+ return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
+ } else if (Component !== undefined && Component !== null) {
+ var $$typeof = Component.$$typeof;
- return;
- }
+ if ($$typeof === REACT_FORWARD_REF_TYPE) {
+ return ForwardRef;
+ }
- if (existingCallbackNode != null) {
- // Cancel the existing callback. We'll schedule a new one below.
- cancelCallback(existingCallbackNode);
- } // Schedule a new callback.
+ if ($$typeof === REACT_MEMO_TYPE) {
+ return MemoComponent;
+ }
+ }
- var newCallbackNode;
+ return IndeterminateComponent;
+} // This is used to create an alternate fiber to do work on.
- if (includesSyncLane(newCallbackPriority)) {
- // Special case: Sync React callbacks are scheduled on a special
- // internal queue
- if (root.tag === LegacyRoot) {
- if (ReactCurrentActQueue.isBatchingLegacy !== null) {
- ReactCurrentActQueue.didScheduleLegacyUpdate = true;
- }
+function createWorkInProgress(current, pendingProps) {
+ var workInProgress = current.alternate;
- scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
- } else {
- scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
- }
+ if (workInProgress === null) {
+ // We use a double buffering pooling technique because we know that we'll
+ // only ever need at most two versions of a tree. We pool the "other" unused
+ // node that we're free to reuse. This is lazily created to avoid allocating
+ // extra objects for things that are never updated. It also allow us to
+ // reclaim the extra memory if needed.
+ workInProgress = createFiber(
+ current.tag,
+ pendingProps,
+ current.key,
+ current.mode
+ );
+ workInProgress.elementType = current.elementType;
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
{
- // Flush the queue in a microtask.
- if (ReactCurrentActQueue.current !== null) {
- // Inside `act`, use our internal `act` queue so that these get flushed
- // at the end of the current scope even when using the sync version
- // of `act`.
- ReactCurrentActQueue.current.push(flushSyncCallbacks);
- } else {
- scheduleMicrotask(function () {
- // In Safari, appending an iframe forces microtasks to run.
- // https://github.com/facebook/react/issues/22459
- // We don't support running callbacks in the middle of render
- // or commit so we need to check against that.
- if (
- (executionContext & (RenderContext | CommitContext)) ===
- NoContext
- ) {
- // Note that this would still prematurely flush the callbacks
- // if this happens outside render or commit phase (e.g. in an event).
- flushSyncCallbacks();
- }
- });
- }
+ // DEV-only fields
+ workInProgress._debugSource = current._debugSource;
+ workInProgress._debugOwner = current._debugOwner;
+ workInProgress._debugHookTypes = current._debugHookTypes;
}
- newCallbackNode = null;
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
} else {
- var schedulerPriorityLevel;
-
- switch (lanesToEventPriority(nextLanes)) {
- case DiscreteEventPriority:
- schedulerPriorityLevel = ImmediatePriority;
- break;
+ workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type.
- case ContinuousEventPriority:
- schedulerPriorityLevel = UserBlockingPriority;
- break;
+ workInProgress.type = current.type; // We already have an alternate.
+ // Reset the effect tag.
- case DefaultEventPriority:
- schedulerPriorityLevel = NormalPriority$1;
- break;
+ workInProgress.flags = NoFlags$1; // The effects are no longer valid.
- case IdleEventPriority:
- schedulerPriorityLevel = IdlePriority;
- break;
+ workInProgress.subtreeFlags = NoFlags$1;
+ workInProgress.deletions = null;
- default:
- schedulerPriorityLevel = NormalPriority$1;
- break;
+ {
+ // We intentionally reset, rather than copy, actualDuration & actualStartTime.
+ // This prevents time from endlessly accumulating in new commits.
+ // This has the downside of resetting values for different priority renders,
+ // But works for yielding (the common case) and should support resuming.
+ workInProgress.actualDuration = 0;
+ workInProgress.actualStartTime = -1;
}
+ } // Reset all effects except static ones.
+ // Static effects are not specific to a render.
- newCallbackNode = scheduleCallback(
- schedulerPriorityLevel,
- performConcurrentWorkOnRoot.bind(null, root)
- );
- }
+ workInProgress.flags = current.flags & StaticMask;
+ workInProgress.childLanes = current.childLanes;
+ workInProgress.lanes = current.lanes;
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so
+ // it cannot be shared with the current fiber.
- root.callbackPriority = newCallbackPriority;
- root.callbackNode = newCallbackNode;
-} // This is the entry point for every concurrent task, i.e. anything that
-// goes through Scheduler.
+ var currentDependencies = current.dependencies;
+ workInProgress.dependencies =
+ currentDependencies === null
+ ? null
+ : {
+ lanes: currentDependencies.lanes,
+ firstContext: currentDependencies.firstContext
+ }; // These will be overridden during the parent's reconciliation
+
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ workInProgress.ref = current.ref;
+ workInProgress.refCleanup = current.refCleanup;
-function performConcurrentWorkOnRoot(root, didTimeout) {
{
- resetNestedUpdateFlag();
- } // Since we know we're in a React event, we can clear the current
- // event time. The next update will compute a new event time.
+ workInProgress.selfBaseDuration = current.selfBaseDuration;
+ workInProgress.treeBaseDuration = current.treeBaseDuration;
+ }
- currentEventTime = NoTimestamp;
- currentEventTransitionLane = NoLanes;
+ {
+ workInProgress._debugNeedsRemount = current._debugNeedsRemount;
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- throw new Error("Should not already be working.");
- } // Flush any pending passive effects before deciding which lanes to work on,
- // in case they schedule additional work.
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ case FunctionComponent:
+ case SimpleMemoComponent:
+ workInProgress.type = resolveFunctionForHotReloading(current.type);
+ break;
- var originalCallbackNode = root.callbackNode;
- var didFlushPassiveEffects = flushPassiveEffects();
+ case ClassComponent:
+ workInProgress.type = resolveClassForHotReloading(current.type);
+ break;
- if (didFlushPassiveEffects) {
- // Something in the passive effect phase may have canceled the current task.
- // Check if the task node for this root was changed.
- if (root.callbackNode !== originalCallbackNode) {
- // The current task was canceled. Exit. We don't need to call
- // `ensureRootIsScheduled` because the check above implies either that
- // there's a new task, or that there's no remaining work on this root.
- return null;
+ case ForwardRef:
+ workInProgress.type = resolveForwardRefForHotReloading(current.type);
+ break;
}
- } // Determine the next lanes to work on, using the fields stored
- // on the root.
-
- var lanes = getNextLanes(
- root,
- root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes
- );
+ }
- if (lanes === NoLanes) {
- // Defensive coding. This is never expected to happen.
- return null;
- } // We disable time-slicing in some cases: if the work has been CPU-bound
- // for too long ("expired" work, to prevent starvation), or we're in
- // sync-updates-by-default mode.
- // TODO: We only check `didTimeout` defensively, to account for a Scheduler
- // bug we're still investigating. Once the bug in Scheduler is fixed,
- // we can remove this, since we track expiration ourselves.
+ return workInProgress;
+} // Used to reuse a Fiber for a second pass.
- var shouldTimeSlice =
- !includesBlockingLane(root, lanes) &&
- !includesExpiredLane(root, lanes) &&
- (disableSchedulerTimeoutInWorkLoop || !didTimeout);
- var exitStatus = shouldTimeSlice
- ? renderRootConcurrent(root, lanes)
- : renderRootSync(root, lanes);
+function resetWorkInProgress(workInProgress, renderLanes) {
+ // This resets the Fiber to what createFiber or createWorkInProgress would
+ // have set the values to before during the first pass. Ideally this wouldn't
+ // be necessary but unfortunately many code paths reads from the workInProgress
+ // when they should be reading from current and writing to workInProgress.
+ // We assume pendingProps, index, key, ref, return are still untouched to
+ // avoid doing another reconciliation.
+ // Reset the effect flags but keep any Placement tags, since that's something
+ // that child fiber is setting, not the reconciliation.
+ workInProgress.flags &= StaticMask | Placement; // The effects are no longer valid.
- if (exitStatus !== RootInProgress) {
- if (exitStatus === RootErrored) {
- // If something threw an error, try rendering one more time. We'll
- // render synchronously to block concurrent data mutations, and we'll
- // includes all pending updates are included. If it still fails after
- // the second attempt, we'll give up and commit the resulting tree.
- var originallyAttemptedLanes = lanes;
- var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
- root,
- originallyAttemptedLanes
- );
+ var current = workInProgress.alternate;
- if (errorRetryLanes !== NoLanes) {
- lanes = errorRetryLanes;
- exitStatus = recoverFromConcurrentError(
- root,
- originallyAttemptedLanes,
- errorRetryLanes
- );
- }
- }
+ if (current === null) {
+ // Reset to createFiber's initial values.
+ workInProgress.childLanes = NoLanes;
+ workInProgress.lanes = renderLanes;
+ workInProgress.child = null;
+ workInProgress.subtreeFlags = NoFlags$1;
+ workInProgress.memoizedProps = null;
+ workInProgress.memoizedState = null;
+ workInProgress.updateQueue = null;
+ workInProgress.dependencies = null;
+ workInProgress.stateNode = null;
- if (exitStatus === RootFatalErrored) {
- var fatalError = workInProgressRootFatalError;
- prepareFreshStack(root, NoLanes);
- markRootSuspended(root, lanes);
- ensureRootIsScheduled(root, now$1());
- throw fatalError;
+ {
+ // Note: We don't reset the actualTime counts. It's useful to accumulate
+ // actual time across multiple render passes.
+ workInProgress.selfBaseDuration = 0;
+ workInProgress.treeBaseDuration = 0;
}
+ } else {
+ // Reset to the cloned values that createWorkInProgress would've.
+ workInProgress.childLanes = current.childLanes;
+ workInProgress.lanes = current.lanes;
+ workInProgress.child = current.child;
+ workInProgress.subtreeFlags = NoFlags$1;
+ workInProgress.deletions = null;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue; // Needed because Blocks store data on type.
- if (exitStatus === RootDidNotComplete) {
- // The render unwound without completing the tree. This happens in special
- // cases where need to exit the current render without producing a
- // consistent tree or committing.
- markRootSuspended(root, lanes);
- } else {
- // The render completed.
- // Check if this render may have yielded to a concurrent event, and if so,
- // confirm that any newly rendered stores are consistent.
- // TODO: It's possible that even a concurrent render may never have yielded
- // to the main thread, if it was fast enough, or if it expired. We could
- // skip the consistency check in that case, too.
- var renderWasConcurrent = !includesBlockingLane(root, lanes);
- var finishedWork = root.current.alternate;
-
- if (
- renderWasConcurrent &&
- !isRenderConsistentWithExternalStores(finishedWork)
- ) {
- // A store was mutated in an interleaved event. Render again,
- // synchronously, to block further mutations.
- exitStatus = renderRootSync(root, lanes); // We need to check again if something threw
-
- if (exitStatus === RootErrored) {
- var _originallyAttemptedLanes = lanes;
-
- var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(
- root,
- _originallyAttemptedLanes
- );
-
- if (_errorRetryLanes !== NoLanes) {
- lanes = _errorRetryLanes;
- exitStatus = recoverFromConcurrentError(
- root,
- _originallyAttemptedLanes,
- _errorRetryLanes
- ); // We assume the tree is now consistent because we didn't yield to any
- // concurrent events.
- }
- }
+ workInProgress.type = current.type; // Clone the dependencies object. This is mutated during the render phase, so
+ // it cannot be shared with the current fiber.
- if (exitStatus === RootFatalErrored) {
- var _fatalError = workInProgressRootFatalError;
- prepareFreshStack(root, NoLanes);
- markRootSuspended(root, lanes);
- ensureRootIsScheduled(root, now$1());
- throw _fatalError;
- } // FIXME: Need to check for RootDidNotComplete again. The factoring here
- // isn't ideal.
- } // We now have a consistent tree. The next step is either to commit it,
- // or, if something suspended, wait to commit it after a timeout.
+ var currentDependencies = current.dependencies;
+ workInProgress.dependencies =
+ currentDependencies === null
+ ? null
+ : {
+ lanes: currentDependencies.lanes,
+ firstContext: currentDependencies.firstContext
+ };
- root.finishedWork = finishedWork;
- root.finishedLanes = lanes;
- finishConcurrentRender(root, exitStatus, lanes);
+ {
+ // Note: We don't reset the actualTime counts. It's useful to accumulate
+ // actual time across multiple render passes.
+ workInProgress.selfBaseDuration = current.selfBaseDuration;
+ workInProgress.treeBaseDuration = current.treeBaseDuration;
}
}
- ensureRootIsScheduled(root, now$1());
-
- if (root.callbackNode === originalCallbackNode) {
- // The task node scheduled for this root is the same one that's
- // currently executed. Need to return a continuation.
- return performConcurrentWorkOnRoot.bind(null, root);
- }
-
- return null;
+ return workInProgress;
}
-
-function recoverFromConcurrentError(
- root,
- originallyAttemptedLanes,
- errorRetryLanes
+function createHostRootFiber(
+ tag,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride
) {
- // If an error occurred during hydration, discard server response and fall
- // back to client side render.
- // Before rendering again, save the errors from the previous attempt.
- var errorsFromFirstAttempt = workInProgressRootConcurrentErrors;
- var wasRootDehydrated = isRootDehydrated(root);
+ var mode;
- if (wasRootDehydrated) {
- // The shell failed to hydrate. Set a flag to force a client rendering
- // during the next attempt. To do this, we call prepareFreshStack now
- // to create the root work-in-progress fiber. This is a bit weird in terms
- // of factoring, because it relies on renderRootSync not calling
- // prepareFreshStack again in the call below, which happens because the
- // root and lanes haven't changed.
- //
- // TODO: I think what we should do is set ForceClientRender inside
- // throwException, like we do for nested Suspense boundaries. The reason
- // it's here instead is so we can switch to the synchronous work loop, too.
- // Something to consider for a future refactor.
- var rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);
- rootWorkInProgress.flags |= ForceClientRender;
+ if (tag === ConcurrentRoot) {
+ mode = ConcurrentMode;
- {
- errorHydratingContainer(root.containerInfo);
+ if (isStrictMode === true || createRootStrictEffectsByDefault) {
+ mode |= StrictLegacyMode | StrictEffectsMode;
+ }
+
+ if (
+ // Only for internal experiments.
+ concurrentUpdatesByDefaultOverride
+ ) {
+ mode |= ConcurrentUpdatesByDefaultMode;
}
+ } else {
+ mode = NoMode;
}
- var exitStatus = renderRootSync(root, errorRetryLanes);
+ if (isDevToolsPresent) {
+ // Always collect profile timings when DevTools are present.
+ // This enables DevTools to start capturing timing at any point–
+ // Without some nodes in the tree having empty base times.
+ mode |= ProfileMode;
+ }
- if (exitStatus !== RootErrored) {
- // Successfully finished rendering on retry
- if (workInProgressRootDidAttachPingListener && !wasRootDehydrated) {
- // During the synchronous render, we attached additional ping listeners.
- // This is highly suggestive of an uncached promise (though it's not the
- // only reason this would happen). If it was an uncached promise, then
- // it may have masked a downstream error from ocurring without actually
- // fixing it. Example:
- //
- // use(Promise.resolve('uncached'))
- // throw new Error('Oops!')
- //
- // When this happens, there's a conflict between blocking potential
- // concurrent data races and unwrapping uncached promise values. We
- // have to choose one or the other. Because the data race recovery is
- // a last ditch effort, we'll disable it.
- root.errorRecoveryDisabledLanes = mergeLanes(
- root.errorRecoveryDisabledLanes,
- originallyAttemptedLanes
- ); // Mark the current render as suspended and force it to restart. Once
- // these lanes finish successfully, we'll re-enable the error recovery
- // mechanism for subsequent updates.
+ return createFiber(HostRoot, null, null, mode);
+}
+function createFiberFromTypeAndProps(
+ type, // React$ElementType
+ key,
+ pendingProps,
+ owner,
+ mode,
+ lanes
+) {
+ var fiberTag = IndeterminateComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
- workInProgressRootInterleavedUpdatedLanes |= originallyAttemptedLanes;
- return RootSuspendedWithDelay;
- } // The errors from the failed first attempt have been recovered. Add
- // them to the collection of recoverable errors. We'll log them in the
- // commit phase.
+ var resolvedType = type;
- var errorsFromSecondAttempt = workInProgressRootRecoverableErrors;
- workInProgressRootRecoverableErrors = errorsFromFirstAttempt; // The errors from the second attempt should be queued after the errors
- // from the first attempt, to preserve the causal sequence.
+ if (typeof type === "function") {
+ if (shouldConstruct(type)) {
+ fiberTag = ClassComponent;
- if (errorsFromSecondAttempt !== null) {
- queueRecoverableErrors(errorsFromSecondAttempt);
+ {
+ resolvedType = resolveClassForHotReloading(resolvedType);
+ }
+ } else {
+ {
+ resolvedType = resolveFunctionForHotReloading(resolvedType);
+ }
}
- }
+ } else if (typeof type === "string") {
+ {
+ var hostContext = getHostContext();
+ fiberTag = isHostHoistableType(type, pendingProps, hostContext)
+ ? HostHoistable
+ : isHostSingletonType(type)
+ ? HostSingleton
+ : HostComponent;
+ }
+ } else {
+ getTag: switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(pendingProps.children, mode, lanes, key);
- return exitStatus;
-}
+ case REACT_STRICT_MODE_TYPE:
+ fiberTag = Mode;
+ mode |= StrictLegacyMode;
-function queueRecoverableErrors(errors) {
- if (workInProgressRootRecoverableErrors === null) {
- workInProgressRootRecoverableErrors = errors;
- } else {
- // $FlowFixMe[method-unbinding]
- workInProgressRootRecoverableErrors.push.apply(
- workInProgressRootRecoverableErrors,
- errors
- );
- }
-}
+ if ((mode & ConcurrentMode) !== NoMode) {
+ // Strict effects should never run on legacy roots
+ mode |= StrictEffectsMode;
+ }
-function finishConcurrentRender(root, exitStatus, lanes) {
- switch (exitStatus) {
- case RootInProgress:
- case RootFatalErrored: {
- throw new Error("Root did not complete. This is a bug in React.");
- }
- // Flow knows about invariant, so it complains if I add a break
- // statement, but eslint doesn't know about invariant, so it complains
- // if I do. eslint-disable-next-line no-fallthrough
+ break;
- case RootErrored: {
- // We should have already attempted to retry this tree. If we reached
- // this point, it errored again. Commit it.
- commitRoot(
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- );
- break;
- }
+ case REACT_PROFILER_TYPE:
+ return createFiberFromProfiler(pendingProps, mode, lanes, key);
- case RootSuspended: {
- markRootSuspended(root, lanes); // We have an acceptable loading state. We need to figure out if we
- // should immediately commit it or wait a bit.
+ case REACT_SUSPENSE_TYPE:
+ return createFiberFromSuspense(pendingProps, mode, lanes, key);
- if (
- includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope
- !shouldForceFlushFallbacksInDEV()
- ) {
- // This render only included retries, no updates. Throttle committing
- // retries so that we don't show too many loading states too quickly.
- var msUntilTimeout =
- globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now$1(); // Don't bother with a very short suspense time.
+ case REACT_SUSPENSE_LIST_TYPE:
+ return createFiberFromSuspenseList(pendingProps, mode, lanes, key);
- if (msUntilTimeout > 10) {
- var nextLanes = getNextLanes(root, NoLanes);
+ case REACT_OFFSCREEN_TYPE:
+ return createFiberFromOffscreen(pendingProps, mode, lanes, key);
- if (nextLanes !== NoLanes) {
- // There's additional work on this root.
- break;
- } // The render is suspended, it hasn't timed out, and there's no
- // lower priority work to do. Instead of committing the fallback
- // immediately, wait for more data to arrive.
+ case REACT_LEGACY_HIDDEN_TYPE: {
+ return createFiberFromLegacyHidden(pendingProps, mode, lanes, key);
+ }
- root.timeoutHandle = scheduleTimeout(
- commitRoot.bind(
- null,
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- ),
- msUntilTimeout
- );
- break;
- }
- } // The work expired. Commit immediately.
+ // eslint-disable-next-line no-fallthrough
- commitRoot(
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- );
- break;
- }
+ case REACT_SCOPE_TYPE: {
+ return createFiberFromScope(type, pendingProps, mode, lanes, key);
+ }
- case RootSuspendedWithDelay: {
- markRootSuspended(root, lanes);
+ // eslint-disable-next-line no-fallthrough
- if (includesOnlyTransitions(lanes)) {
- // This is a transition, so we should exit without committing a
- // placeholder and without scheduling a timeout. Delay indefinitely
- // until we receive more data.
- break;
+ case REACT_CACHE_TYPE: {
+ return createFiberFromCache(pendingProps, mode, lanes, key);
}
- if (!shouldForceFlushFallbacksInDEV()) {
- // This is not a transition, but we did trigger an avoided state.
- // Schedule a placeholder to display after a short delay, using the Just
- // Noticeable Difference.
- // TODO: Is the JND optimization worth the added complexity? If this is
- // the only reason we track the event time, then probably not.
- // Consider removing.
- var mostRecentEventTime = getMostRecentEventTime(root, lanes);
- var eventTimeMs = mostRecentEventTime;
- var timeElapsedMs = now$1() - eventTimeMs;
+ // eslint-disable-next-line no-fallthrough
- var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.
+ case REACT_TRACING_MARKER_TYPE:
+ if (enableTransitionTracing) {
+ return createFiberFromTracingMarker(pendingProps, mode, lanes, key);
+ }
- if (_msUntilTimeout > 10) {
- // Instead of committing the fallback immediately, wait for more data
- // to arrive.
- root.timeoutHandle = scheduleTimeout(
- commitRoot.bind(
- null,
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- ),
- _msUntilTimeout
- );
+ // eslint-disable-next-line no-fallthrough
+
+ case REACT_DEBUG_TRACING_MODE_TYPE:
+ if (enableDebugTracing) {
+ fiberTag = Mode;
+ mode |= DebugTracingMode;
break;
}
- } // Commit the placeholder.
- commitRoot(
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- );
- break;
- }
+ // eslint-disable-next-line no-fallthrough
- case RootCompleted: {
- // The work completed. Ready to commit.
- commitRoot(
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- );
- break;
- }
+ default: {
+ if (typeof type === "object" && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = ContextProvider;
+ break getTag;
- default: {
- throw new Error("Unknown root exit status.");
- }
- }
-}
+ case REACT_CONTEXT_TYPE:
+ // This is a consumer
+ fiberTag = ContextConsumer;
+ break getTag;
-function isRenderConsistentWithExternalStores(finishedWork) {
- // Search the rendered tree for external store reads, and check whether the
- // stores were mutated in a concurrent event. Intentionally using an iterative
- // loop instead of recursion so we can exit early.
- var node = finishedWork;
+ case REACT_FORWARD_REF_TYPE:
+ fiberTag = ForwardRef;
- while (true) {
- if (node.flags & StoreConsistency) {
- var updateQueue = node.updateQueue;
+ {
+ resolvedType = resolveForwardRefForHotReloading(resolvedType);
+ }
- if (updateQueue !== null) {
- var checks = updateQueue.stores;
+ break getTag;
- if (checks !== null) {
- for (var i = 0; i < checks.length; i++) {
- var check = checks[i];
- var getSnapshot = check.getSnapshot;
- var renderedValue = check.value;
+ case REACT_MEMO_TYPE:
+ fiberTag = MemoComponent;
+ break getTag;
- try {
- if (!objectIs(getSnapshot(), renderedValue)) {
- // Found an inconsistent store.
- return false;
- }
- } catch (error) {
- // If `getSnapshot` throws, return `false`. This will schedule
- // a re-render, and the error will be rethrown during render.
- return false;
- }
+ case REACT_LAZY_TYPE:
+ fiberTag = LazyComponent;
+ resolvedType = null;
+ break getTag;
}
}
- }
- }
- var child = node.child;
+ var info = "";
- if (node.subtreeFlags & StoreConsistency && child !== null) {
- child.return = node;
- node = child;
- continue;
- }
+ {
+ 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.";
+ }
- if (node === finishedWork) {
- return true;
- }
+ var ownerName = owner ? getComponentNameFromFiber(owner) : null;
- while (node.sibling === null) {
- if (node.return === null || node.return === finishedWork) {
- return true;
- }
+ if (ownerName) {
+ info += "\n\nCheck the render method of `" + ownerName + "`.";
+ }
+ }
- node = node.return;
+ throw new Error(
+ "Element type is invalid: expected a string (for built-in " +
+ "components) or a class/function (for composite components) " +
+ ("but got: " + (type == null ? type : typeof type) + "." + info)
+ );
+ }
}
+ }
- node.sibling.return = node.return;
- node = node.sibling;
- } // Flow doesn't know this is unreachable, but eslint does
- // eslint-disable-next-line no-unreachable
+ var fiber = createFiber(fiberTag, pendingProps, key, mode);
+ fiber.elementType = type;
+ fiber.type = resolvedType;
+ fiber.lanes = lanes;
- return true;
+ {
+ fiber._debugOwner = owner;
+ }
+
+ return fiber;
}
+function createFiberFromElement(element, mode, lanes) {
+ var owner = null;
-function markRootSuspended(root, suspendedLanes) {
- // When suspending, we should always exclude lanes that were pinged or (more
- // rarely, since we try to avoid it) updated during the render phase.
- // TODO: Lol maybe there's a better way to factor this besides this
- // obnoxiously named function :)
- suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
- suspendedLanes = removeLanes(
- suspendedLanes,
- workInProgressRootInterleavedUpdatedLanes
+ {
+ owner = element._owner;
+ }
+
+ var type = element.type;
+ var key = element.key;
+ var pendingProps = element.props;
+ var fiber = createFiberFromTypeAndProps(
+ type,
+ key,
+ pendingProps,
+ owner,
+ mode,
+ lanes
);
- markRootSuspended$1(root, suspendedLanes);
-} // This is the entry point for synchronous tasks that don't go
-// through Scheduler
-function performSyncWorkOnRoot(root) {
{
- syncNestedUpdateFlag();
+ fiber._debugSource = element._source;
+ fiber._debugOwner = element._owner;
}
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- throw new Error("Should not already be working.");
+ return fiber;
+}
+function createFiberFromFragment(elements, mode, lanes, key) {
+ var fiber = createFiber(Fragment, elements, key, mode);
+ fiber.lanes = lanes;
+ return fiber;
+}
+
+function createFiberFromScope(scope, pendingProps, mode, lanes, key) {
+ var fiber = createFiber(ScopeComponent, pendingProps, key, mode);
+ fiber.type = scope;
+ fiber.elementType = scope;
+ fiber.lanes = lanes;
+ return fiber;
+}
+
+function createFiberFromProfiler(pendingProps, mode, lanes, key) {
+ {
+ if (typeof pendingProps.id !== "string") {
+ error(
+ 'Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.',
+ typeof pendingProps.id
+ );
+ }
}
- flushPassiveEffects();
- var lanes = getNextLanes(root, NoLanes);
+ var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
+ fiber.elementType = REACT_PROFILER_TYPE;
+ fiber.lanes = lanes;
- if (!includesSyncLane(lanes)) {
- // There's no remaining sync work left.
- ensureRootIsScheduled(root, now$1());
- return null;
+ {
+ fiber.stateNode = {
+ effectDuration: 0,
+ passiveEffectDuration: 0
+ };
}
- var exitStatus = renderRootSync(root, lanes);
+ return fiber;
+}
- if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
- // If something threw an error, try rendering one more time. We'll render
- // synchronously to block concurrent data mutations, and we'll includes
- // all pending updates are included. If it still fails after the second
- // attempt, we'll give up and commit the resulting tree.
- var originallyAttemptedLanes = lanes;
- var errorRetryLanes = getLanesToRetrySynchronouslyOnError(
- root,
- originallyAttemptedLanes
- );
+function createFiberFromSuspense(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_SUSPENSE_TYPE;
+ fiber.lanes = lanes;
+ return fiber;
+}
+function createFiberFromSuspenseList(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(SuspenseListComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_SUSPENSE_LIST_TYPE;
+ fiber.lanes = lanes;
+ return fiber;
+}
+function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_OFFSCREEN_TYPE;
+ fiber.lanes = lanes;
+ var primaryChildInstance = {
+ _visibility: OffscreenVisible,
+ _pendingVisibility: OffscreenVisible,
+ _pendingMarkers: null,
+ _retryCache: null,
+ _transitions: null,
+ _current: null,
+ detach: function () {
+ return detachOffscreenInstance(primaryChildInstance);
+ },
+ attach: function () {
+ return attachOffscreenInstance(primaryChildInstance);
+ }
+ };
+ fiber.stateNode = primaryChildInstance;
+ return fiber;
+}
+function createFiberFromLegacyHidden(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(LegacyHiddenComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_LEGACY_HIDDEN_TYPE;
+ fiber.lanes = lanes; // Adding a stateNode for legacy hidden because it's currently using
+ // the offscreen implementation, which depends on a state node
- if (errorRetryLanes !== NoLanes) {
- lanes = errorRetryLanes;
- exitStatus = recoverFromConcurrentError(
- root,
- originallyAttemptedLanes,
- errorRetryLanes
- );
+ var instance = {
+ _visibility: OffscreenVisible,
+ _pendingVisibility: OffscreenVisible,
+ _pendingMarkers: null,
+ _transitions: null,
+ _retryCache: null,
+ _current: null,
+ detach: function () {
+ return detachOffscreenInstance(instance);
+ },
+ attach: function () {
+ return attachOffscreenInstance(instance);
}
- }
+ };
+ fiber.stateNode = instance;
+ return fiber;
+}
+function createFiberFromCache(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(CacheComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_CACHE_TYPE;
+ fiber.lanes = lanes;
+ return fiber;
+}
+function createFiberFromTracingMarker(pendingProps, mode, lanes, key) {
+ var fiber = createFiber(TracingMarkerComponent, pendingProps, key, mode);
+ fiber.elementType = REACT_TRACING_MARKER_TYPE;
+ fiber.lanes = lanes;
+ var tracingMarkerInstance = {
+ tag: TransitionTracingMarker,
+ transitions: null,
+ pendingBoundaries: null,
+ aborts: null,
+ name: pendingProps.name
+ };
+ fiber.stateNode = tracingMarkerInstance;
+ return fiber;
+}
+function createFiberFromText(content, mode, lanes) {
+ var fiber = createFiber(HostText, content, null, mode);
+ fiber.lanes = lanes;
+ return fiber;
+}
+function createFiberFromHostInstanceForDeletion() {
+ var fiber = createFiber(HostComponent, null, null, NoMode);
+ fiber.elementType = "DELETED";
+ return fiber;
+}
+function createFiberFromDehydratedFragment(dehydratedNode) {
+ var fiber = createFiber(DehydratedFragment, null, null, NoMode);
+ fiber.stateNode = dehydratedNode;
+ return fiber;
+}
+function createFiberFromPortal(portal, mode, lanes) {
+ var pendingProps = portal.children !== null ? portal.children : [];
+ var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
+ fiber.lanes = lanes;
+ fiber.stateNode = {
+ containerInfo: portal.containerInfo,
+ pendingChildren: null,
+ // Used by persistent updates
+ implementation: portal.implementation
+ };
+ return fiber;
+} // Used for stashing WIP properties to replay failed work in DEV.
- if (exitStatus === RootFatalErrored) {
- var fatalError = workInProgressRootFatalError;
- prepareFreshStack(root, NoLanes);
- markRootSuspended(root, lanes);
- ensureRootIsScheduled(root, now$1());
- throw fatalError;
- }
+function assignFiberPropertiesInDEV(target, source) {
+ if (target === null) {
+ // This Fiber's initial properties will always be overwritten.
+ // We only use a Fiber to ensure the same hidden class so DEV isn't slow.
+ target = createFiber(IndeterminateComponent, null, null, NoMode);
+ } // This is intentionally written as a list of all properties.
+ // We tried to use Object.assign() instead but this is called in
+ // the hottest path, and Object.assign() was too slow:
+ // https://github.com/facebook/react/issues/12502
+ // This code is DEV-only so size is not a concern.
- if (exitStatus === RootDidNotComplete) {
- // The render unwound without completing the tree. This happens in special
- // cases where need to exit the current render without producing a
- // consistent tree or committing.
- markRootSuspended(root, lanes);
- ensureRootIsScheduled(root, now$1());
- return null;
- } // We now have a consistent tree. Because this is a sync render, we
- // will commit it even if something suspended.
+ target.tag = source.tag;
+ target.key = source.key;
+ target.elementType = source.elementType;
+ target.type = source.type;
+ target.stateNode = source.stateNode;
+ target.return = source.return;
+ target.child = source.child;
+ target.sibling = source.sibling;
+ target.index = source.index;
+ target.ref = source.ref;
+ target.refCleanup = source.refCleanup;
+ target.pendingProps = source.pendingProps;
+ target.memoizedProps = source.memoizedProps;
+ target.updateQueue = source.updateQueue;
+ target.memoizedState = source.memoizedState;
+ target.dependencies = source.dependencies;
+ target.mode = source.mode;
+ target.flags = source.flags;
+ target.subtreeFlags = source.subtreeFlags;
+ target.deletions = source.deletions;
+ target.lanes = source.lanes;
+ target.childLanes = source.childLanes;
+ target.alternate = source.alternate;
- var finishedWork = root.current.alternate;
- root.finishedWork = finishedWork;
- root.finishedLanes = lanes;
- commitRoot(
- root,
- workInProgressRootRecoverableErrors,
- workInProgressTransitions
- ); // Before exiting, make sure there's a callback scheduled for the next
- // pending level.
+ {
+ target.actualDuration = source.actualDuration;
+ target.actualStartTime = source.actualStartTime;
+ target.selfBaseDuration = source.selfBaseDuration;
+ target.treeBaseDuration = source.treeBaseDuration;
+ }
- ensureRootIsScheduled(root, now$1());
- return null;
+ target._debugSource = source._debugSource;
+ target._debugOwner = source._debugOwner;
+ target._debugNeedsRemount = source._debugNeedsRemount;
+ target._debugHookTypes = source._debugHookTypes;
+ return target;
}
-function flushRoot(root, lanes) {
- if (lanes !== NoLanes) {
- markRootEntangled(root, mergeLanes(lanes, SyncLane));
- ensureRootIsScheduled(root, now$1());
+function FiberRootNode(
+ containerInfo, // $FlowFixMe[missing-local-annot]
+ tag,
+ hydrate,
+ identifierPrefix,
+ onRecoverableError
+) {
+ this.tag = tag;
+ this.containerInfo = containerInfo;
+ this.pendingChildren = null;
+ this.current = null;
+ this.pingCache = null;
+ this.finishedWork = null;
+ this.timeoutHandle = noTimeout;
+ this.context = null;
+ this.pendingContext = null;
+ this.callbackNode = null;
+ this.callbackPriority = NoLane;
+ this.eventTimes = createLaneMap(NoLanes);
+ this.expirationTimes = createLaneMap(NoTimestamp);
+ this.pendingLanes = NoLanes;
+ this.suspendedLanes = NoLanes;
+ this.pingedLanes = NoLanes;
+ this.expiredLanes = NoLanes;
+ this.mutableReadLanes = NoLanes;
+ this.finishedLanes = NoLanes;
+ this.errorRecoveryDisabledLanes = NoLanes;
+ this.entangledLanes = NoLanes;
+ this.entanglements = createLaneMap(NoLanes);
+ this.hiddenUpdates = createLaneMap(null);
+ this.identifierPrefix = identifierPrefix;
+ this.onRecoverableError = onRecoverableError;
- if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
- resetRenderTimer();
- flushSyncCallbacks();
- }
+ {
+ this.pooledCache = null;
+ this.pooledCacheLanes = NoLanes;
}
-}
-function getExecutionContext() {
- return executionContext;
-}
-function batchedUpdates(fn, a) {
- var prevExecutionContext = executionContext;
- executionContext |= BatchedContext;
- try {
- return fn(a);
- } finally {
- executionContext = prevExecutionContext; // If there were legacy sync updates, flush them at the end of the outer
- // most batchedUpdates-like method.
+ {
+ this.mutableSourceEagerHydrationData = null;
+ }
- if (
- executionContext === NoContext && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
- !ReactCurrentActQueue.isBatchingLegacy
- ) {
- resetRenderTimer();
- flushSyncCallbacksOnlyInLegacyMode();
- }
+ {
+ this.hydrationCallbacks = null;
}
-}
-function discreteUpdates(fn, a, b, c, d) {
- var previousPriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig.transition;
- try {
- ReactCurrentBatchConfig.transition = null;
- setCurrentUpdatePriority(DiscreteEventPriority);
- return fn(a, b, c, d);
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig.transition = prevTransition;
+ this.incompleteTransitions = new Map();
- if (executionContext === NoContext) {
- resetRenderTimer();
+ if (enableTransitionTracing) {
+ this.transitionCallbacks = null;
+ var transitionLanesMap = (this.transitionLanes = []);
+
+ for (var i = 0; i < TotalLanes; i++) {
+ transitionLanesMap.push(null);
}
}
-} // Overload the definition to the two valid signatures.
-// Warning, this opts-out of checking the function body.
-// eslint-disable-next-line no-unused-vars
-// eslint-disable-next-line no-redeclare
-// eslint-disable-next-line no-redeclare
-function flushSync$1(fn) {
- // In legacy mode, we flush pending passive effects at the beginning of the
- // next event, not at the end of the previous one.
- if (
- rootWithPendingPassiveEffects !== null &&
- rootWithPendingPassiveEffects.tag === LegacyRoot &&
- (executionContext & (RenderContext | CommitContext)) === NoContext
- ) {
- flushPassiveEffects();
+ {
+ this.effectDuration = 0;
+ this.passiveEffectDuration = 0;
}
- var prevExecutionContext = executionContext;
- executionContext |= BatchedContext;
- var prevTransition = ReactCurrentBatchConfig.transition;
- var previousPriority = getCurrentUpdatePriority$1();
-
- try {
- ReactCurrentBatchConfig.transition = null;
- setCurrentUpdatePriority(DiscreteEventPriority);
+ {
+ this.memoizedUpdaters = new Set();
+ var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
- if (fn) {
- return fn();
- } else {
- return undefined;
+ for (var _i = 0; _i < TotalLanes; _i++) {
+ pendingUpdatersLaneMap.push(new Set());
}
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig.transition = prevTransition;
- executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.
- // Note that this will happen even if batchedUpdates is higher up
- // the stack.
+ }
- if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
- flushSyncCallbacks();
+ {
+ switch (tag) {
+ case ConcurrentRoot:
+ this._debugRootType = hydrate ? "hydrateRoot()" : "createRoot()";
+ break;
+
+ case LegacyRoot:
+ this._debugRootType = hydrate ? "hydrate()" : "render()";
+ break;
}
}
}
-function isAlreadyRendering() {
- // Used by the renderer to print a warning if certain APIs are called from
- // the wrong context.
- return (executionContext & (RenderContext | CommitContext)) !== NoContext;
-}
-function isInvalidExecutionContextForEventFunction() {
- // Used to throw if certain APIs are called from the wrong context.
- return (executionContext & RenderContext) !== NoContext;
-}
-function flushControlled(fn) {
- var prevExecutionContext = executionContext;
- executionContext |= BatchedContext;
- var prevTransition = ReactCurrentBatchConfig.transition;
- var previousPriority = getCurrentUpdatePriority$1();
- try {
- ReactCurrentBatchConfig.transition = null;
- setCurrentUpdatePriority(DiscreteEventPriority);
- fn();
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig.transition = prevTransition;
- executionContext = prevExecutionContext;
+function createFiberRoot(
+ containerInfo,
+ tag,
+ hydrate,
+ initialChildren,
+ hydrationCallbacks,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the
+ // host config, but because they are passed in at runtime, we have to thread
+ // them through the root constructor. Perhaps we should put them all into a
+ // single type, like a DynamicHostConfig that is defined by the renderer.
+ identifierPrefix,
+ onRecoverableError,
+ transitionCallbacks
+) {
+ // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
+ var root = new FiberRootNode(
+ containerInfo,
+ tag,
+ hydrate,
+ identifierPrefix,
+ onRecoverableError
+ );
- if (executionContext === NoContext) {
- // Flush the immediate callbacks that were scheduled during this batch
- resetRenderTimer();
- flushSyncCallbacks();
- }
+ {
+ root.hydrationCallbacks = hydrationCallbacks;
}
-} // This is called by the HiddenContext module when we enter or leave a
-// hidden subtree. The stack logic is managed there because that's the only
-// place that ever modifies it. Which module it lives in doesn't matter for
-// performance because this function will get inlined regardless
-function setRenderLanes(subtreeRenderLanes) {
- renderLanes = subtreeRenderLanes;
-}
-function getRenderLanes() {
- return renderLanes;
-}
+ if (enableTransitionTracing) {
+ root.transitionCallbacks = transitionCallbacks;
+ } // Cyclic construction. This cheats the type system right now because
+ // stateNode is any.
-function resetWorkInProgressStack() {
- if (workInProgress === null) return;
- var interruptedWork;
+ var uninitializedFiber = createHostRootFiber(
+ tag,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride
+ );
+ root.current = uninitializedFiber;
+ uninitializedFiber.stateNode = root;
- if (workInProgressSuspendedReason === NotSuspended) {
- // Normal case. Work-in-progress hasn't started yet. Unwind all
- // its parents.
- interruptedWork = workInProgress.return;
- } else {
- // Work-in-progress is in suspended state. Reset the work loop and unwind
- // both the suspended fiber and all its parents.
- resetSuspendedWorkLoopOnUnwind();
- interruptedWork = workInProgress;
- }
+ {
+ var initialCache = createCache();
+ retainCache(initialCache); // The pooledCache is a fresh cache instance that is used temporarily
+ // for newly mounted boundaries during a render. In general, the
+ // pooledCache is always cleared from the root at the end of a render:
+ // it is either released when render commits, or moved to an Offscreen
+ // component if rendering suspends. Because the lifetime of the pooled
+ // cache is distinct from the main memoizedState.cache, it must be
+ // retained separately.
- while (interruptedWork !== null) {
- var current = interruptedWork.alternate;
- unwindInterruptedWork(current, interruptedWork);
- interruptedWork = interruptedWork.return;
+ root.pooledCache = initialCache;
+ retainCache(initialCache);
+ var initialState = {
+ element: initialChildren,
+ isDehydrated: hydrate,
+ cache: initialCache
+ };
+ uninitializedFiber.memoizedState = initialState;
}
- workInProgress = null;
+ initializeUpdateQueue(uninitializedFiber);
+ return root;
}
-function prepareFreshStack(root, lanes) {
- root.finishedWork = null;
- root.finishedLanes = NoLanes;
- var timeoutHandle = root.timeoutHandle;
-
- if (timeoutHandle !== noTimeout) {
- // The root previous suspended and scheduled a timeout to commit a fallback
- // state. Now that we have additional work, cancel the timeout.
- root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
-
- cancelTimeout(timeoutHandle);
- }
+var ReactVersion = "18.3.0-www-classic-036f93df";
- resetWorkInProgressStack();
- workInProgressRoot = root;
- var rootWorkInProgress = createWorkInProgress(root.current, null);
- workInProgress = rootWorkInProgress;
- workInProgressRootRenderLanes = renderLanes = lanes;
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- workInProgressRootDidAttachPingListener = false;
- workInProgressRootExitStatus = RootInProgress;
- workInProgressRootFatalError = null;
- workInProgressRootSkippedLanes = NoLanes;
- workInProgressRootInterleavedUpdatedLanes = NoLanes;
- workInProgressRootPingedLanes = NoLanes;
- workInProgressRootConcurrentErrors = null;
- workInProgressRootRecoverableErrors = null;
- finishQueueingConcurrentUpdates();
+function createPortal$1(
+ children,
+ containerInfo, // TODO: figure out the API for cross-renderer implementation.
+ implementation
+) {
+ var key =
+ arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
{
- ReactStrictModeWarnings.discardPendingWarnings();
+ checkKeyStringCoercion(key);
}
- return rootWorkInProgress;
+ return {
+ // This tag allow us to uniquely identify this as a React Portal
+ $$typeof: REACT_PORTAL_TYPE,
+ key: key == null ? null : "" + key,
+ children: children,
+ containerInfo: containerInfo,
+ implementation: implementation
+ };
}
-function resetSuspendedWorkLoopOnUnwind() {
- // Reset module-level state that was set during the render phase.
- resetContextDependencies();
- resetHooksOnUnwind();
- resetChildReconcilerOnUnwind();
-}
+// Might add PROFILE later.
-function handleThrow(root, thrownValue) {
- // A component threw an exception. Usually this is because it suspended, but
- // it also includes regular program errors.
- //
- // We're either going to unwind the stack to show a Suspense or error
- // boundary, or we're going to replay the component again. Like after a
- // promise resolves.
- //
- // Until we decide whether we're going to unwind or replay, we should preserve
- // the current state of the work loop without resetting anything.
- //
- // If we do decide to unwind the stack, module-level variables will be reset
- // in resetSuspendedWorkLoopOnUnwind.
- // These should be reset immediately because they're only supposed to be set
- // when React is executing user code.
- resetHooksAfterThrow();
- resetCurrentFiber();
- ReactCurrentOwner$1.current = null;
+var didWarnAboutNestedUpdates;
+var didWarnAboutFindNodeInStrictMode;
- if (thrownValue === SuspenseException) {
- // This is a special type of exception used for Suspense. For historical
- // reasons, the rest of the Suspense implementation expects the thrown value
- // to be a thenable, because before `use` existed that was the (unstable)
- // API for suspending. This implementation detail can change later, once we
- // deprecate the old API in favor of `use`.
- thrownValue = getSuspendedThenable();
- workInProgressSuspendedReason = shouldAttemptToSuspendUntilDataResolves()
- ? SuspendedOnData
- : SuspendedOnImmediate;
- } else if (thrownValue === SelectiveHydrationException) {
- // An update flowed into a dehydrated boundary. Before we can apply the
- // update, we need to finish hydrating. Interrupt the work-in-progress
- // render so we can restart at the hydration lane.
- //
- // The ideal implementation would be able to switch contexts without
- // unwinding the current stack.
- //
- // We could name this something more general but as of now it's the only
- // case where we think this should happen.
- workInProgressSuspendedReason = SuspendedOnHydration;
- } else {
- // This is a regular error.
- var isWakeable =
- thrownValue !== null &&
- typeof thrownValue === "object" &&
- typeof thrownValue.then === "function";
- workInProgressSuspendedReason = isWakeable // A wakeable object was thrown by a legacy Suspense implementation.
- ? // This has slightly different behavior than suspending with `use`.
- SuspendedOnDeprecatedThrowPromise // This is a regular error. If something earlier in the component already
- : // suspended, we must clear the thenable state to unblock the work loop.
- SuspendedOnError;
+{
+ didWarnAboutNestedUpdates = false;
+ didWarnAboutFindNodeInStrictMode = {};
+}
+
+function getContextForSubtree(parentComponent) {
+ if (!parentComponent) {
+ return emptyContextObject;
}
- workInProgressThrownValue = thrownValue;
- var erroredWork = workInProgress;
+ var fiber = get(parentComponent);
+ var parentContext = findCurrentUnmaskedContext(fiber);
- if (erroredWork === null) {
- // This is a fatal error
- workInProgressRootExitStatus = RootFatalErrored;
- workInProgressRootFatalError = thrownValue;
- return;
- }
+ if (fiber.tag === ClassComponent) {
+ var Component = fiber.type;
- if (erroredWork.mode & ProfileMode) {
- // Record the time spent rendering before an error was thrown. This
- // avoids inaccurate Profiler durations in the case of a
- // suspended render.
- stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);
+ if (isContextProvider(Component)) {
+ return processChildContext(fiber, Component, parentContext);
+ }
}
- if (enableSchedulingProfiler) {
- markComponentRenderStopped();
+ return parentContext;
+}
- switch (workInProgressSuspendedReason) {
- case SuspendedOnError: {
- markComponentErrored(
- erroredWork,
- thrownValue,
- workInProgressRootRenderLanes
- );
- break;
- }
+function findHostInstanceWithWarning(component, methodName) {
+ {
+ var fiber = get(component);
- case SuspendedOnData:
- case SuspendedOnImmediate:
- case SuspendedOnDeprecatedThrowPromise:
- case SuspendedAndReadyToContinue: {
- var wakeable = thrownValue;
- markComponentSuspended(
- erroredWork,
- wakeable,
- workInProgressRootRenderLanes
+ if (fiber === undefined) {
+ if (typeof component.render === "function") {
+ throw new Error("Unable to find node on an unmounted component.");
+ } else {
+ var keys = Object.keys(component).join(",");
+ throw new Error(
+ "Argument appears to not be a ReactComponent. Keys: " + keys
);
- break;
}
}
- }
-}
-function shouldAttemptToSuspendUntilDataResolves() {
- // Check if there are other pending updates that might possibly unblock this
- // component from suspending. This mirrors the check in
- // renderDidSuspendDelayIfPossible. We should attempt to unify them somehow.
- // TODO: Consider unwinding immediately, using the
- // SuspendedOnHydration mechanism.
- if (
- includesNonIdleWork(workInProgressRootSkippedLanes) ||
- includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes)
- ) {
- // Suspend normally. renderDidSuspendDelayIfPossible will handle
- // interrupting the work loop.
- return false;
- } // TODO: We should be able to remove the equivalent check in
- // finishConcurrentRender, and rely just on this one.
+ var hostFiber = findCurrentHostFiber(fiber);
- if (includesOnlyTransitions(workInProgressRootRenderLanes)) {
- // If we're rendering inside the "shell" of the app, it's better to suspend
- // rendering and wait for the data to resolve. Otherwise, we should switch
- // to a fallback and continue rendering.
- return getShellBoundary() === null;
- }
+ if (hostFiber === null) {
+ return null;
+ }
- var handler = getSuspenseHandler();
+ if (hostFiber.mode & StrictLegacyMode) {
+ var componentName = getComponentNameFromFiber(fiber) || "Component";
- if (handler === null);
- else {
- if (includesOnlyRetries(workInProgressRootRenderLanes)) {
- // During a retry, we can suspend rendering if the nearest Suspense boundary
- // is the boundary of the "shell", because we're guaranteed not to block
- // any new content from appearing.
- return handler === getShellBoundary();
+ if (!didWarnAboutFindNodeInStrictMode[componentName]) {
+ didWarnAboutFindNodeInStrictMode[componentName] = true;
+ var previousFiber = current;
+
+ try {
+ setCurrentFiber(hostFiber);
+
+ if (fiber.mode & StrictLegacyMode) {
+ error(
+ "%s is deprecated in StrictMode. " +
+ "%s was passed an instance of %s which is inside StrictMode. " +
+ "Instead, add a ref directly to the element you want to reference. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-find-node",
+ methodName,
+ methodName,
+ componentName
+ );
+ } else {
+ error(
+ "%s is deprecated in StrictMode. " +
+ "%s was passed an instance of %s which renders StrictMode children. " +
+ "Instead, add a ref directly to the element you want to reference. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-find-node",
+ methodName,
+ methodName,
+ componentName
+ );
+ }
+ } finally {
+ // Ideally this should reset to previous but this shouldn't be called in
+ // render and there's another warning for that anyway.
+ if (previousFiber) {
+ setCurrentFiber(previousFiber);
+ } else {
+ resetCurrentFiber();
+ }
+ }
+ }
}
- } // For all other Lanes besides Transitions and Retries, we should not wait
- // for the data to load.
- // TODO: We should wait during Offscreen prerendering, too.
- return false;
+ return getPublicInstance(hostFiber.stateNode);
+ }
}
-function pushDispatcher(container) {
- prepareRendererToRender(container);
- var prevDispatcher = ReactCurrentDispatcher.current;
- ReactCurrentDispatcher.current = ContextOnlyDispatcher;
+function createContainer(
+ containerInfo,
+ tag,
+ hydrationCallbacks,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride,
+ identifierPrefix,
+ onRecoverableError,
+ transitionCallbacks
+) {
+ var hydrate = false;
+ var initialChildren = null;
+ return createFiberRoot(
+ containerInfo,
+ tag,
+ hydrate,
+ initialChildren,
+ hydrationCallbacks,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride,
+ identifierPrefix,
+ onRecoverableError,
+ transitionCallbacks
+ );
+}
+function createHydrationContainer(
+ initialChildren, // TODO: Remove `callback` when we delete legacy mode.
+ callback,
+ containerInfo,
+ tag,
+ hydrationCallbacks,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride,
+ identifierPrefix,
+ onRecoverableError,
+ transitionCallbacks
+) {
+ var hydrate = true;
+ var root = createFiberRoot(
+ containerInfo,
+ tag,
+ hydrate,
+ initialChildren,
+ hydrationCallbacks,
+ isStrictMode,
+ concurrentUpdatesByDefaultOverride,
+ identifierPrefix,
+ onRecoverableError,
+ transitionCallbacks
+ ); // TODO: Move this to FiberRoot constructor
- if (prevDispatcher === null) {
- // The React isomorphic package does not include a default dispatcher.
- // Instead the first renderer will lazily attach one, in order to give
- // nicer error messages.
- return ContextOnlyDispatcher;
- } else {
- return prevDispatcher;
- }
-}
+ root.context = getContextForSubtree(null); // Schedule the initial render. In a hydration root, this is different from
+ // a regular update because the initial render must match was was rendered
+ // on the server.
+ // NOTE: This update intentionally doesn't have a payload. We're only using
+ // the update to schedule work on the root fiber (and, for legacy roots, to
+ // enqueue the callback if one is provided).
-function popDispatcher(prevDispatcher) {
- resetRendererAfterRender();
- ReactCurrentDispatcher.current = prevDispatcher;
+ var current = root.current;
+ var lane = requestUpdateLane(current);
+ var update = createUpdate(lane);
+ update.callback =
+ callback !== undefined && callback !== null ? callback : null;
+ var eventTime = requestEventTime();
+ enqueueUpdate(current, update, lane);
+ scheduleInitialHydrationOnRoot(root, lane, eventTime);
+ return root;
}
-
-function pushCacheDispatcher() {
+function updateContainer(element, container, parentComponent, callback) {
{
- var prevCacheDispatcher = ReactCurrentCache.current;
- ReactCurrentCache.current = DefaultCacheDispatcher;
- return prevCacheDispatcher;
+ onScheduleRoot(container, element);
}
-}
-function popCacheDispatcher(prevCacheDispatcher) {
- {
- ReactCurrentCache.current = prevCacheDispatcher;
- }
-}
+ var current$1 = container.current;
+ var lane = requestUpdateLane(current$1);
-function markCommitTimeOfFallback() {
- globalMostRecentFallbackTime = now$1();
-}
-function markSkippedUpdateLanes(lane) {
- workInProgressRootSkippedLanes = mergeLanes(
- lane,
- workInProgressRootSkippedLanes
- );
-}
-function renderDidSuspend() {
- if (workInProgressRootExitStatus === RootInProgress) {
- workInProgressRootExitStatus = RootSuspended;
+ if (enableSchedulingProfiler) {
+ markRenderScheduled(lane);
}
-}
-function renderDidSuspendDelayIfPossible() {
- workInProgressRootExitStatus = RootSuspendedWithDelay; // Check if there are updates that we skipped tree that might have unblocked
- // this render.
- if (
- workInProgressRoot !== null &&
- (includesNonIdleWork(workInProgressRootSkippedLanes) ||
- includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))
- ) {
- // Mark the current render as suspended so that we switch to working on
- // the updates that were skipped. Usually we only suspend at the end of
- // the render phase.
- // TODO: We should probably always mark the root as suspended immediately
- // (inside this function), since by suspending at the end of the render
- // phase introduces a potential mistake where we suspend lanes that were
- // pinged or updated while we were rendering.
- // TODO: Consider unwinding immediately, using the
- // SuspendedOnHydration mechanism.
- // $FlowFixMe[incompatible-call] need null check workInProgressRoot
- markRootSuspended(workInProgressRoot, workInProgressRootRenderLanes);
- }
-}
-function renderDidError(error) {
- if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {
- workInProgressRootExitStatus = RootErrored;
- }
+ var context = getContextForSubtree(parentComponent);
- if (workInProgressRootConcurrentErrors === null) {
- workInProgressRootConcurrentErrors = [error];
+ if (container.context === null) {
+ container.context = context;
} else {
- workInProgressRootConcurrentErrors.push(error);
+ container.pendingContext = context;
}
-} // Called during render to determine if anything has suspended.
-// Returns false if we're not sure.
-function renderHasNotSuspendedYet() {
- // If something errored or completed, we can't really be sure,
- // so those are false.
- return workInProgressRootExitStatus === RootInProgress;
-} // TODO: Over time, this function and renderRootConcurrent have become more
-// and more similar. Not sure it makes sense to maintain forked paths. Consider
-// unifying them again.
+ {
+ if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
+ didWarnAboutNestedUpdates = true;
-function renderRootSync(root, lanes) {
- var prevExecutionContext = executionContext;
- executionContext |= RenderContext;
- var prevDispatcher = pushDispatcher(root.containerInfo);
- var prevCacheDispatcher = pushCacheDispatcher(); // If the root or lanes have changed, throw out the existing stack
- // and prepare a fresh one. Otherwise we'll continue where we left off.
+ error(
+ "Render methods should be a pure function of props and state; " +
+ "triggering nested component updates from render is not allowed. " +
+ "If necessary, trigger nested updates in componentDidUpdate.\n\n" +
+ "Check the render method of %s.",
+ getComponentNameFromFiber(current) || "Unknown"
+ );
+ }
+ }
- if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
- {
- if (isDevToolsPresent) {
- var memoizedUpdaters = root.memoizedUpdaters;
+ var update = createUpdate(lane); // Caution: React DevTools currently depends on this property
+ // being called "element".
- if (memoizedUpdaters.size > 0) {
- restorePendingUpdaters(root, workInProgressRootRenderLanes);
- memoizedUpdaters.clear();
- } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
- // If we bailout on this work, we'll move them back (like above).
- // It's important to move them now in case the work spawns more work at the same priority with different updaters.
- // That way we can keep the current update and future updates separate.
+ update.payload = {
+ element: element
+ };
+ callback = callback === undefined ? null : callback;
- movePendingFibersToMemoized(root, lanes);
+ if (callback !== null) {
+ {
+ if (typeof callback !== "function") {
+ error(
+ "render(...): Expected the last optional `callback` argument to be a " +
+ "function. Instead received: %s.",
+ callback
+ );
}
}
- workInProgressTransitions = getTransitionsForLanes(root, lanes);
- prepareFreshStack(root, lanes);
+ update.callback = callback;
}
- {
- if (enableDebugTracing) {
- logRenderStarted(lanes);
- }
+ var root = enqueueUpdate(current$1, update, lane);
+
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, current$1, lane, eventTime);
+ entangleTransitions(root, current$1, lane);
}
- if (enableSchedulingProfiler) {
- markRenderStarted(lanes);
+ return lane;
+}
+function getPublicRootInstance(container) {
+ var containerFiber = container.current;
+
+ if (!containerFiber.child) {
+ return null;
}
- outer: do {
- try {
- if (
- workInProgressSuspendedReason !== NotSuspended &&
- workInProgress !== null
- ) {
- // The work loop is suspended. During a synchronous render, we don't
- // yield to the main thread. Immediately unwind the stack. This will
- // trigger either a fallback or an error boundary.
- // TODO: For discrete and "default" updates (anything that's not
- // flushSync), we want to wait for the microtasks the flush before
- // unwinding. Will probably implement this using renderRootConcurrent,
- // or merge renderRootSync and renderRootConcurrent into the same
- // function and fork the behavior some other way.
- var unitOfWork = workInProgress;
- var thrownValue = workInProgressThrownValue;
+ switch (containerFiber.child.tag) {
+ case HostSingleton:
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
- switch (workInProgressSuspendedReason) {
- case SuspendedOnHydration: {
- // Selective hydration. An update flowed into a dehydrated tree.
- // Interrupt the current render so the work loop can switch to the
- // hydration lane.
- resetWorkInProgressStack();
- workInProgressRootExitStatus = RootDidNotComplete;
- break outer;
- }
+ default:
+ return containerFiber.child.stateNode;
+ }
+}
+function attemptSynchronousHydration(fiber) {
+ switch (fiber.tag) {
+ case HostRoot: {
+ var root = fiber.stateNode;
- default: {
- // Continue with the normal work loop.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
- break;
- }
- }
+ if (isRootDehydrated(root)) {
+ // Flush the first scheduled "update".
+ var lanes = getHighestPriorityPendingLanes(root);
+ flushRoot(root, lanes);
}
- workLoopSync();
break;
- } catch (thrownValue) {
- handleThrow(root, thrownValue);
}
- } while (true);
- resetContextDependencies();
- executionContext = prevExecutionContext;
- popDispatcher(prevDispatcher);
- popCacheDispatcher(prevCacheDispatcher);
+ case SuspenseComponent: {
+ flushSync$1(function () {
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- if (workInProgress !== null) {
- // This is a sync render, so we should have finished the whole tree.
- throw new Error(
- "Cannot commit an incomplete root. This error is likely caused by a " +
- "bug in React. Please file an issue."
- );
- }
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
+ }
+ }); // If we're still blocked after this, we need to increase
+ // the priority of any promises resolving within this
+ // boundary so that they next attempt also has higher pri.
- {
- if (enableDebugTracing) {
- logRenderStopped();
+ var retryLane = SyncLane;
+ markRetryLaneIfNotHydrated(fiber, retryLane);
+ break;
}
}
+}
- if (enableSchedulingProfiler) {
- markRenderStopped();
- } // Set this to null to indicate there's no in-progress render.
-
- workInProgressRoot = null;
- workInProgressRootRenderLanes = NoLanes; // It's safe to process the queue now that the render phase is complete.
+function markRetryLaneImpl(fiber, retryLane) {
+ var suspenseState = fiber.memoizedState;
- finishQueueingConcurrentUpdates();
- return workInProgressRootExitStatus;
-} // The work loop is an extremely hot path. Tell Closure not to inline it.
+ if (suspenseState !== null && suspenseState.dehydrated !== null) {
+ suspenseState.retryLane = higherPriorityLane(
+ suspenseState.retryLane,
+ retryLane
+ );
+ }
+} // Increases the priority of thenables when they resolve within this boundary.
-/** @noinline */
+function markRetryLaneIfNotHydrated(fiber, retryLane) {
+ markRetryLaneImpl(fiber, retryLane);
+ var alternate = fiber.alternate;
-function workLoopSync() {
- // Perform work without checking if we need to yield between fiber.
- while (workInProgress !== null) {
- performUnitOfWork(workInProgress);
+ if (alternate) {
+ markRetryLaneImpl(alternate, retryLane);
}
}
-function renderRootConcurrent(root, lanes) {
- var prevExecutionContext = executionContext;
- executionContext |= RenderContext;
- var prevDispatcher = pushDispatcher(root.containerInfo);
- var prevCacheDispatcher = pushCacheDispatcher(); // If the root or lanes have changed, throw out the existing stack
- // and prepare a fresh one. Otherwise we'll continue where we left off.
+function attemptDiscreteHydration(fiber) {
+ if (fiber.tag !== SuspenseComponent) {
+ // We ignore HostRoots here because we can't increase
+ // their priority and they should not suspend on I/O,
+ // since you have to wrap anything that might suspend in
+ // Suspense.
+ return;
+ }
- if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
- {
- if (isDevToolsPresent) {
- var memoizedUpdaters = root.memoizedUpdaters;
+ var lane = SyncLane;
+ var root = enqueueConcurrentRenderForLane(fiber, lane);
- if (memoizedUpdaters.size > 0) {
- restorePendingUpdaters(root, workInProgressRootRenderLanes);
- memoizedUpdaters.clear();
- } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
- // If we bailout on this work, we'll move them back (like above).
- // It's important to move them now in case the work spawns more work at the same priority with different updaters.
- // That way we can keep the current update and future updates separate.
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ }
- movePendingFibersToMemoized(root, lanes);
- }
- }
+ markRetryLaneIfNotHydrated(fiber, lane);
+}
+function attemptContinuousHydration(fiber) {
+ if (fiber.tag !== SuspenseComponent) {
+ // We ignore HostRoots here because we can't increase
+ // their priority and they should not suspend on I/O,
+ // since you have to wrap anything that might suspend in
+ // Suspense.
+ return;
+ }
- workInProgressTransitions = getTransitionsForLanes(root, lanes);
- resetRenderTimer();
- prepareFreshStack(root, lanes);
+ var lane = SelectiveHydrationLane;
+ var root = enqueueConcurrentRenderForLane(fiber, lane);
+
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
}
- {
- if (enableDebugTracing) {
- logRenderStarted(lanes);
- }
+ markRetryLaneIfNotHydrated(fiber, lane);
+}
+function attemptHydrationAtCurrentPriority(fiber) {
+ if (fiber.tag !== SuspenseComponent) {
+ // We ignore HostRoots here because we can't increase
+ // their priority other than synchronously flush it.
+ return;
}
- if (enableSchedulingProfiler) {
- markRenderStarted(lanes);
+ var lane = requestUpdateLane(fiber);
+ var root = enqueueConcurrentRenderForLane(fiber, lane);
+
+ if (root !== null) {
+ var eventTime = requestEventTime();
+ scheduleUpdateOnFiber(root, fiber, lane, eventTime);
}
- outer: do {
- try {
- if (
- workInProgressSuspendedReason !== NotSuspended &&
- workInProgress !== null
- ) {
- // The work loop is suspended. We need to either unwind the stack or
- // replay the suspended component.
- var unitOfWork = workInProgress;
- var thrownValue = workInProgressThrownValue;
+ markRetryLaneIfNotHydrated(fiber, lane);
+}
+function findHostInstanceWithNoPortals(fiber) {
+ var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
- switch (workInProgressSuspendedReason) {
- case SuspendedOnError: {
- // Unwind then continue with the normal work loop.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
- break;
- }
+ if (hostFiber === null) {
+ return null;
+ }
- case SuspendedOnData: {
- var thenable = thrownValue;
+ return getPublicInstance(hostFiber.stateNode);
+}
- if (isThenableResolved(thenable)) {
- // The data resolved. Try rendering the component again.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- replaySuspendedUnitOfWork(unitOfWork);
- break;
- } // The work loop is suspended on data. We should wait for it to
- // resolve before continuing to render.
- // TODO: Handle the case where the promise resolves synchronously.
- // Usually this is handled when we instrument the promise to add a
- // `status` field, but if the promise already has a status, we won't
- // have added a listener until right here.
+var shouldErrorImpl = function (fiber) {
+ return null;
+};
- var onResolution = function () {
- // Check if the root is still suspended on this promise.
- if (
- workInProgressSuspendedReason === SuspendedOnData &&
- workInProgressRoot === root
- ) {
- // Mark the root as ready to continue rendering.
- workInProgressSuspendedReason = SuspendedAndReadyToContinue;
- } // Ensure the root is scheduled. We should do this even if we're
- // currently working on a different root, so that we resume
- // rendering later.
+function shouldError(fiber) {
+ return shouldErrorImpl(fiber);
+}
- ensureRootIsScheduled(root, now$1());
- };
+var shouldSuspendImpl = function (fiber) {
+ return false;
+};
- thenable.then(onResolution, onResolution);
- break outer;
- }
+function shouldSuspend(fiber) {
+ return shouldSuspendImpl(fiber);
+}
+var overrideHookState = null;
+var overrideHookStateDeletePath = null;
+var overrideHookStateRenamePath = null;
+var overrideProps = null;
+var overridePropsDeletePath = null;
+var overridePropsRenamePath = null;
+var scheduleUpdate = null;
+var setErrorHandler = null;
+var setSuspenseHandler = null;
- case SuspendedOnImmediate: {
- // If this fiber just suspended, it's possible the data is already
- // cached. Yield to the main thread to give it a chance to ping. If
- // it does, we can retry immediately without unwinding the stack.
- workInProgressSuspendedReason = SuspendedAndReadyToContinue;
- break outer;
- }
+{
+ var copyWithDeleteImpl = function (obj, path, index) {
+ var key = path[index];
+ var updated = isArray(obj) ? obj.slice() : assign({}, obj);
- case SuspendedAndReadyToContinue: {
- var _thenable = thrownValue;
+ if (index + 1 === path.length) {
+ if (isArray(updated)) {
+ updated.splice(key, 1);
+ } else {
+ delete updated[key];
+ }
- if (isThenableResolved(_thenable)) {
- // The data resolved. Try rendering the component again.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- replaySuspendedUnitOfWork(unitOfWork);
- } else {
- // Otherwise, unwind then continue with the normal work loop.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
- }
+ return updated;
+ } // $FlowFixMe number or string is fine here
- break;
- }
+ updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
+ return updated;
+ };
- case SuspendedOnDeprecatedThrowPromise: {
- // Suspended by an old implementation that uses the `throw promise`
- // pattern. The newer replaying behavior can cause subtle issues
- // like infinite ping loops. So we maintain the old behavior and
- // always unwind.
- workInProgressSuspendedReason = NotSuspended;
- workInProgressThrownValue = null;
- unwindSuspendedUnitOfWork(unitOfWork, thrownValue);
- break;
- }
+ var copyWithDelete = function (obj, path) {
+ return copyWithDeleteImpl(obj, path, 0);
+ };
- case SuspendedOnHydration: {
- // Selective hydration. An update flowed into a dehydrated tree.
- // Interrupt the current render so the work loop can switch to the
- // hydration lane.
- resetWorkInProgressStack();
- workInProgressRootExitStatus = RootDidNotComplete;
- break outer;
- }
+ var copyWithRenameImpl = function (obj, oldPath, newPath, index) {
+ var oldKey = oldPath[index];
+ var updated = isArray(obj) ? obj.slice() : assign({}, obj);
+
+ if (index + 1 === oldPath.length) {
+ var newKey = newPath[index]; // $FlowFixMe number or string is fine here
+
+ updated[newKey] = updated[oldKey];
+
+ if (isArray(updated)) {
+ updated.splice(oldKey, 1);
+ } else {
+ delete updated[oldKey];
+ }
+ } else {
+ // $FlowFixMe number or string is fine here
+ updated[oldKey] = copyWithRenameImpl(
+ // $FlowFixMe number or string is fine here
+ obj[oldKey],
+ oldPath,
+ newPath,
+ index + 1
+ );
+ }
+
+ return updated;
+ };
+
+ var copyWithRename = function (obj, oldPath, newPath) {
+ if (oldPath.length !== newPath.length) {
+ warn("copyWithRename() expects paths of the same length");
- default: {
- throw new Error(
- "Unexpected SuspendedReason. This is a bug in React."
- );
- }
+ return;
+ } else {
+ for (var i = 0; i < newPath.length - 1; i++) {
+ if (oldPath[i] !== newPath[i]) {
+ warn(
+ "copyWithRename() expects paths to be the same except for the deepest key"
+ );
+
+ return;
}
}
+ }
- if (true && ReactCurrentActQueue.current !== null) {
- // `act` special case: If we're inside an `act` scope, don't consult
- // `shouldYield`. Always keep working until the render is complete.
- // This is not just an optimization: in a unit test environment, we
- // can't trust the result of `shouldYield`, because the host I/O is
- // likely mocked.
- workLoopSync();
- } else {
- workLoopConcurrent();
- }
+ return copyWithRenameImpl(obj, oldPath, newPath, 0);
+ };
- break;
- } catch (thrownValue) {
- handleThrow(root, thrownValue);
+ var copyWithSetImpl = function (obj, path, index, value) {
+ if (index >= path.length) {
+ return value;
}
- } while (true);
- resetContextDependencies();
- popDispatcher(prevDispatcher);
- popCacheDispatcher(prevCacheDispatcher);
- executionContext = prevExecutionContext;
+ var key = path[index];
+ var updated = isArray(obj) ? obj.slice() : assign({}, obj); // $FlowFixMe number or string is fine here
- {
- if (enableDebugTracing) {
- logRenderStopped();
+ updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
+ return updated;
+ };
+
+ var copyWithSet = function (obj, path, value) {
+ return copyWithSetImpl(obj, path, 0, value);
+ };
+
+ var findHook = function (fiber, id) {
+ // For now, the "id" of stateful hooks is just the stateful hook index.
+ // This may change in the future with e.g. nested hooks.
+ var currentHook = fiber.memoizedState;
+
+ while (currentHook !== null && id > 0) {
+ currentHook = currentHook.next;
+ id--;
}
- } // Check if the tree has completed.
- if (workInProgress !== null) {
- // Still work remaining.
- if (enableSchedulingProfiler) {
- markRenderYielded();
+ return currentHook;
+ }; // Support DevTools editable values for useState and useReducer.
+
+ overrideHookState = function (fiber, id, path, value) {
+ var hook = findHook(fiber, id);
+
+ if (hook !== null) {
+ var newState = copyWithSet(hook.memoizedState, path, value);
+ hook.memoizedState = newState;
+ hook.baseState = newState; // We aren't actually adding an update to the queue,
+ // because there is no update we can add for useReducer hooks that won't trigger an error.
+ // (There's no appropriate action type for DevTools overrides.)
+ // As a result though, React will see the scheduled update as a noop and bailout.
+ // Shallow cloning props works as a workaround for now to bypass the bailout check.
+
+ fiber.memoizedProps = assign({}, fiber.memoizedProps);
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
}
+ };
- return RootInProgress;
- } else {
- // Completed the tree.
- if (enableSchedulingProfiler) {
- markRenderStopped();
- } // Set this to null to indicate there's no in-progress render.
+ overrideHookStateDeletePath = function (fiber, id, path) {
+ var hook = findHook(fiber, id);
- workInProgressRoot = null;
- workInProgressRootRenderLanes = NoLanes; // It's safe to process the queue now that the render phase is complete.
+ if (hook !== null) {
+ var newState = copyWithDelete(hook.memoizedState, path);
+ hook.memoizedState = newState;
+ hook.baseState = newState; // We aren't actually adding an update to the queue,
+ // because there is no update we can add for useReducer hooks that won't trigger an error.
+ // (There's no appropriate action type for DevTools overrides.)
+ // As a result though, React will see the scheduled update as a noop and bailout.
+ // Shallow cloning props works as a workaround for now to bypass the bailout check.
- finishQueueingConcurrentUpdates(); // Return the final exit status.
+ fiber.memoizedProps = assign({}, fiber.memoizedProps);
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- return workInProgressRootExitStatus;
- }
-}
-/** @noinline */
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+ }
+ };
-function workLoopConcurrent() {
- // Perform work until Scheduler asks us to yield
- while (workInProgress !== null && !shouldYield()) {
- // $FlowFixMe[incompatible-call] found when upgrading Flow
- performUnitOfWork(workInProgress);
- }
-}
+ overrideHookStateRenamePath = function (fiber, id, oldPath, newPath) {
+ var hook = findHook(fiber, id);
-function performUnitOfWork(unitOfWork) {
- // The current, flushed, state of this fiber is the alternate. Ideally
- // nothing should rely on this, but relying on it here means that we don't
- // need an additional field on the work in progress.
- var current = unitOfWork.alternate;
- setCurrentFiber(unitOfWork);
- var next;
+ if (hook !== null) {
+ var newState = copyWithRename(hook.memoizedState, oldPath, newPath);
+ hook.memoizedState = newState;
+ hook.baseState = newState; // We aren't actually adding an update to the queue,
+ // because there is no update we can add for useReducer hooks that won't trigger an error.
+ // (There's no appropriate action type for DevTools overrides.)
+ // As a result though, React will see the scheduled update as a noop and bailout.
+ // Shallow cloning props works as a workaround for now to bypass the bailout check.
- if ((unitOfWork.mode & ProfileMode) !== NoMode) {
- startProfilerTimer(unitOfWork);
- next = beginWork(current, unitOfWork, renderLanes);
- stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
- } else {
- next = beginWork(current, unitOfWork, renderLanes);
- }
+ fiber.memoizedProps = assign({}, fiber.memoizedProps);
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- resetCurrentFiber();
- unitOfWork.memoizedProps = unitOfWork.pendingProps;
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+ }
+ }; // Support DevTools props for function components, forwardRef, memo, host components, etc.
- if (next === null) {
- // If this doesn't spawn new work, complete the current work.
- completeUnitOfWork(unitOfWork);
- } else {
- workInProgress = next;
- }
+ overrideProps = function (fiber, path, value) {
+ fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
- ReactCurrentOwner$1.current = null;
-}
+ if (fiber.alternate) {
+ fiber.alternate.pendingProps = fiber.pendingProps;
+ }
-function replaySuspendedUnitOfWork(unitOfWork) {
- // This is a fork of performUnitOfWork specifcally for replaying a fiber that
- // just suspended.
- //
- var current = unitOfWork.alternate;
- setCurrentFiber(unitOfWork);
- var next;
- setCurrentFiber(unitOfWork);
- var isProfilingMode = (unitOfWork.mode & ProfileMode) !== NoMode;
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- if (isProfilingMode) {
- startProfilerTimer(unitOfWork);
- }
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+ };
- switch (unitOfWork.tag) {
- case IndeterminateComponent: {
- // Because it suspended with `use`, we can assume it's a
- // function component.
- unitOfWork.tag = FunctionComponent; // Fallthrough to the next branch.
+ overridePropsDeletePath = function (fiber, path) {
+ fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
+
+ if (fiber.alternate) {
+ fiber.alternate.pendingProps = fiber.pendingProps;
}
- // eslint-disable-next-line no-fallthrough
- case FunctionComponent:
- case ForwardRef: {
- // Resolve `defaultProps`. This logic is copied from `beginWork`.
- // TODO: Consider moving this switch statement into that module. Also,
- // could maybe use this as an opportunity to say `use` doesn't work with
- // `defaultProps` :)
- var Component = unitOfWork.type;
- var unresolvedProps = unitOfWork.pendingProps;
- var resolvedProps =
- unitOfWork.elementType === Component
- ? unresolvedProps
- : resolveDefaultProps(Component, unresolvedProps);
- next = replayFunctionComponent(
- current,
- unitOfWork,
- resolvedProps,
- Component,
- workInProgressRootRenderLanes
- );
- break;
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
+ };
- case SimpleMemoComponent: {
- var _Component = unitOfWork.type;
- var nextProps = unitOfWork.pendingProps;
- next = replayFunctionComponent(
- current,
- unitOfWork,
- nextProps,
- _Component,
- workInProgressRootRenderLanes
- );
- break;
+ overridePropsRenamePath = function (fiber, oldPath, newPath) {
+ fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
+
+ if (fiber.alternate) {
+ fiber.alternate.pendingProps = fiber.pendingProps;
}
- default: {
- // Other types besides function components are reset completely before
- // being replayed. Currently this only happens when a Usable type is
- // reconciled — the reconciler will suspend.
- //
- // We reset the fiber back to its original state; however, this isn't
- // a full "unwind" because we're going to reuse the promises that were
- // reconciled previously. So it's intentional that we don't call
- // resetSuspendedWorkLoopOnUnwind here.
- unwindInterruptedWork(current, unitOfWork);
- unitOfWork = workInProgress = resetWorkInProgress(
- unitOfWork,
- renderLanes
- );
- next = beginWork(current, unitOfWork, renderLanes);
- break;
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
}
- }
+ };
- if (isProfilingMode) {
- stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
- } // The begin phase finished successfully without suspending. Return to the
- // normal work loop.
+ scheduleUpdate = function (fiber) {
+ var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
- resetCurrentFiber();
- unitOfWork.memoizedProps = unitOfWork.pendingProps;
+ if (root !== null) {
+ scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ }
+ };
- if (next === null) {
- // If this doesn't spawn new work, complete the current work.
- completeUnitOfWork(unitOfWork);
- } else {
- workInProgress = next;
- }
+ setErrorHandler = function (newShouldErrorImpl) {
+ shouldErrorImpl = newShouldErrorImpl;
+ };
- ReactCurrentOwner$1.current = null;
+ setSuspenseHandler = function (newShouldSuspendImpl) {
+ shouldSuspendImpl = newShouldSuspendImpl;
+ };
}
-function unwindSuspendedUnitOfWork(unitOfWork, thrownValue) {
- // This is a fork of performUnitOfWork specifcally for unwinding a fiber
- // that threw an exception.
- //
- // Return to the normal work loop. This will unwind the stack, and potentially
- // result in showing a fallback.
- resetSuspendedWorkLoopOnUnwind();
- var returnFiber = unitOfWork.return;
-
- if (returnFiber === null || workInProgressRoot === null) {
- // Expected to be working on a non-root fiber. This is a fatal error
- // because there's no ancestor that can handle it; the root is
- // supposed to capture all errors that weren't caught by an error
- // boundary.
- workInProgressRootExitStatus = RootFatalErrored;
- workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next
- // sibling, or the parent if there are no siblings. But since the root
- // has no siblings nor a parent, we set it to null. Usually this is
- // handled by `completeUnitOfWork` or `unwindWork`, but since we're
- // intentionally not calling those, we need set it here.
- // TODO: Consider calling `unwindWork` to pop the contexts.
+function findHostInstanceByFiber(fiber) {
+ var hostFiber = findCurrentHostFiber(fiber);
- workInProgress = null;
- return;
+ if (hostFiber === null) {
+ return null;
}
- try {
- // Find and mark the nearest Suspense or error boundary that can handle
- // this "exception".
- throwException(
- workInProgressRoot,
- returnFiber,
- unitOfWork,
- thrownValue,
- workInProgressRootRenderLanes
- );
- } catch (error) {
- // We had trouble processing the error. An example of this happening is
- // when accessing the `componentDidCatch` property of an error boundary
- // throws an error. A weird edge case. There's a regression test for this.
- // To prevent an infinite loop, bubble the error up to the next parent.
- workInProgress = returnFiber;
- throw error;
- } // Return to the normal work loop.
+ return hostFiber.stateNode;
+}
- completeUnitOfWork(unitOfWork);
+function emptyFindFiberByHostInstance(instance) {
+ return null;
}
-function completeUnitOfWork(unitOfWork) {
- // Attempt to complete the current unit of work, then move to the next
- // sibling. If there are no more siblings, return to the parent fiber.
- var completedWork = unitOfWork;
+function getCurrentFiberForDevTools() {
+ return current;
+}
- do {
- // The current, flushed, state of this fiber is the alternate. Ideally
- // nothing should rely on this, but relying on it here means that we don't
- // need an additional field on the work in progress.
- var current = completedWork.alternate;
- var returnFiber = completedWork.return; // Check if the work completed or if something threw.
+function injectIntoDevTools(devToolsConfig) {
+ var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
+ var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
+ return injectInternals({
+ bundleType: devToolsConfig.bundleType,
+ version: devToolsConfig.version,
+ rendererPackageName: devToolsConfig.rendererPackageName,
+ rendererConfig: devToolsConfig.rendererConfig,
+ overrideHookState: overrideHookState,
+ overrideHookStateDeletePath: overrideHookStateDeletePath,
+ overrideHookStateRenamePath: overrideHookStateRenamePath,
+ overrideProps: overrideProps,
+ overridePropsDeletePath: overridePropsDeletePath,
+ overridePropsRenamePath: overridePropsRenamePath,
+ setErrorHandler: setErrorHandler,
+ setSuspenseHandler: setSuspenseHandler,
+ scheduleUpdate: scheduleUpdate,
+ currentDispatcherRef: ReactCurrentDispatcher,
+ findHostInstanceByFiber: findHostInstanceByFiber,
+ findFiberByHostInstance:
+ findFiberByHostInstance || emptyFindFiberByHostInstance,
+ // React Refresh
+ findHostInstancesForRefresh: findHostInstancesForRefresh,
+ scheduleRefresh: scheduleRefresh,
+ scheduleRoot: scheduleRoot,
+ setRefreshHandler: setRefreshHandler,
+ // Enables DevTools to append owner stacks to error messages in DEV mode.
+ getCurrentFiber: getCurrentFiberForDevTools,
+ // Enables DevTools to detect reconciler version rather than renderer version
+ // which may not match for third party renderers.
+ reconcilerVersion: ReactVersion
+ });
+}
- if ((completedWork.flags & Incomplete) === NoFlags$1) {
- setCurrentFiber(completedWork);
- var next = void 0;
+// 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.
- if ((completedWork.mode & ProfileMode) === NoMode) {
- next = completeWork(current, completedWork, renderLanes);
- } else {
- startProfilerTimer(completedWork);
- next = completeWork(current, completedWork, renderLanes); // Update render duration assuming we didn't error.
+var isInsideEventHandler = false;
- stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
- }
+function finishEventHandler() {
+ // 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.
+ var controlledComponentsHavePendingUpdates = needsStateRestore();
- resetCurrentFiber();
+ if (controlledComponentsHavePendingUpdates) {
+ // If a controlled event was fired, we may need to restore the state of
+ // the DOM node back to the controlled value. This is necessary when React
+ // bails out of the update without touching the DOM.
+ // TODO: Restore state in the microtask, after the discrete updates flush,
+ // instead of early flushing them here.
+ flushSync$1();
+ restoreStateIfNeeded();
+ }
+}
- if (next !== null) {
- // Completing this fiber spawned new work. Work on that next.
- workInProgress = next;
- return;
- }
- } else {
- // This fiber did not complete because something threw. Pop values off
- // the stack without entering the complete phase. If this is a boundary,
- // capture values if possible.
- var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes.
+function batchedUpdates(fn, a, b) {
+ if (isInsideEventHandler) {
+ // If we are currently inside another batch, we need to wait until it
+ // fully completes before restoring state.
+ return fn(a, b);
+ }
- if (_next !== null) {
- // If completing this work spawned new work, do that next. We'll come
- // back here again.
- // Since we're restarting, remove anything that is not a host effect
- // from the effect tag.
- _next.flags &= HostEffectMask;
- workInProgress = _next;
- return;
- }
+ isInsideEventHandler = true;
- if ((completedWork.mode & ProfileMode) !== NoMode) {
- // Record the render duration for the fiber that errored.
- stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.
+ try {
+ return batchedUpdates$1(fn, a, b);
+ } finally {
+ isInsideEventHandler = false;
+ finishEventHandler();
+ }
+} // TODO: Replace with flushSync
- var actualDuration = completedWork.actualDuration;
- var child = completedWork.child;
+function isInteractive(tag) {
+ return (
+ tag === "button" ||
+ tag === "input" ||
+ tag === "select" ||
+ tag === "textarea"
+ );
+}
- while (child !== null) {
- // $FlowFixMe[unsafe-addition] addition with possible null/undefined value
- actualDuration += child.actualDuration;
- child = child.sibling;
- }
+function shouldPreventMouseEvent(name, type, props) {
+ switch (name) {
+ case "onClick":
+ case "onClickCapture":
+ case "onDoubleClick":
+ case "onDoubleClickCapture":
+ case "onMouseDown":
+ case "onMouseDownCapture":
+ case "onMouseMove":
+ case "onMouseMoveCapture":
+ case "onMouseUp":
+ case "onMouseUpCapture":
+ case "onMouseEnter":
+ return !!(props.disabled && isInteractive(type));
- completedWork.actualDuration = actualDuration;
- }
+ default:
+ return false;
+ }
+}
+/**
+ * @param {object} inst The instance, which is the source of events.
+ * @param {string} registrationName Name of listener (e.g. `onClick`).
+ * @return {?function} The stored callback.
+ */
- if (returnFiber !== null) {
- // Mark the parent fiber as incomplete and clear its subtree flags.
- returnFiber.flags |= Incomplete;
- returnFiber.subtreeFlags = NoFlags$1;
- returnFiber.deletions = null;
- } else {
- // We've unwound all the way to the root.
- workInProgressRootExitStatus = RootDidNotComplete;
- workInProgress = null;
- return;
- }
- }
+function getListener(inst, registrationName) {
+ var stateNode = inst.stateNode;
- var siblingFiber = completedWork.sibling;
+ if (stateNode === null) {
+ // Work in progress (ex: onload events in incremental mode).
+ return null;
+ }
- if (siblingFiber !== null) {
- // If there is more work to do in this returnFiber, do that next.
- workInProgress = siblingFiber;
- return;
- } // Otherwise, return to the parent
- // $FlowFixMe[incompatible-type] we bail out when we get a null
+ var props = getFiberCurrentPropsFromNode(stateNode);
- completedWork = returnFiber; // Update the next thing we're working on in case something throws.
+ if (props === null) {
+ // Work in progress.
+ return null;
+ }
- workInProgress = completedWork;
- } while (completedWork !== null); // We've reached the root.
+ var listener = props[registrationName];
- if (workInProgressRootExitStatus === RootInProgress) {
- workInProgressRootExitStatus = RootCompleted;
+ if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
+ return null;
+ }
+
+ if (listener && typeof listener !== "function") {
+ throw new Error(
+ "Expected `" +
+ registrationName +
+ "` listener to be a function, instead got a value of `" +
+ typeof listener +
+ "` type."
+ );
}
+
+ return listener;
}
-function commitRoot(root, recoverableErrors, transitions) {
- // TODO: This no longer makes any sense. We already wrap the mutation and
- // layout phases. Should be able to remove.
- var previousUpdateLanePriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig.transition;
+var passiveBrowserEventsSupported = false; // Check if browser support events with passive listeners
+// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
+if (canUseDOM) {
try {
- ReactCurrentBatchConfig.transition = null;
- setCurrentUpdatePriority(DiscreteEventPriority);
- commitRootImpl(
- root,
- recoverableErrors,
- transitions,
- previousUpdateLanePriority
- );
- } finally {
- ReactCurrentBatchConfig.transition = prevTransition;
- setCurrentUpdatePriority(previousUpdateLanePriority);
+ var options = {};
+ Object.defineProperty(options, "passive", {
+ get: function () {
+ passiveBrowserEventsSupported = true;
+ }
+ });
+ window.addEventListener("test", options, options);
+ window.removeEventListener("test", options, options);
+ } catch (e) {
+ passiveBrowserEventsSupported = false;
}
-
- return null;
}
-function commitRootImpl(
- root,
- recoverableErrors,
- transitions,
- renderPriorityLevel
+var EventListenerWWW = require("EventListener");
+
+function addEventBubbleListener(target, eventType, listener) {
+ return EventListenerWWW.listen(target, eventType, listener);
+}
+function addEventCaptureListener(target, eventType, listener) {
+ return EventListenerWWW.capture(target, eventType, listener);
+}
+function addEventCaptureListenerWithPassiveFlag(
+ target,
+ eventType,
+ listener,
+ passive
) {
- do {
- // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
- // means `flushPassiveEffects` will sometimes result in additional
- // passive effects. So we need to keep flushing in a loop until there are
- // no more pending effects.
- // TODO: Might be better if `flushPassiveEffects` did not automatically
- // flush synchronous work at the end, to avoid factoring hazards like this.
- flushPassiveEffects();
- } while (rootWithPendingPassiveEffects !== null);
+ return EventListenerWWW.captureWithPassiveFlag(
+ target,
+ eventType,
+ listener,
+ passive
+ );
+}
+function addEventBubbleListenerWithPassiveFlag(
+ target,
+ eventType,
+ listener,
+ passive
+) {
+ return EventListenerWWW.bubbleWithPassiveFlag(
+ target,
+ eventType,
+ listener,
+ passive
+ );
+}
+function removeEventListener(target, eventType, listener, capture) {
+ listener.remove();
+} // Flow magic to verify the exports of this file match the original version.
- flushRenderPhaseStrictModeWarningsInDEV();
+/**
+ * These variables store information about text content of a target node,
+ * allowing comparison of content before and after a given event.
+ *
+ * Identify the node where selection currently begins, then observe
+ * both its text content and its current position in the DOM. Since the
+ * browser may natively replace the target node during composition, we can
+ * use its position to find its replacement.
+ *
+ *
+ */
+var root = null;
+var startText = null;
+var fallbackText = null;
+function initialize(nativeEventTarget) {
+ root = nativeEventTarget;
+ startText = getText();
+ return true;
+}
+function reset() {
+ root = null;
+ startText = null;
+ fallbackText = null;
+}
+function getData() {
+ if (fallbackText) {
+ return fallbackText;
+ }
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- throw new Error("Should not already be working.");
+ var start;
+ var startValue = startText;
+ var startLength = startValue.length;
+ var end;
+ var endValue = getText();
+ var endLength = endValue.length;
+
+ for (start = 0; start < startLength; start++) {
+ if (startValue[start] !== endValue[start]) {
+ break;
+ }
}
- var finishedWork = root.finishedWork;
- var lanes = root.finishedLanes;
+ var minEnd = startLength - start;
- {
- if (enableDebugTracing) {
- logCommitStarted(lanes);
+ for (end = 1; end <= minEnd; end++) {
+ if (startValue[startLength - end] !== endValue[endLength - end]) {
+ break;
}
}
- if (enableSchedulingProfiler) {
- markCommitStarted(lanes);
+ var sliceTail = end > 1 ? 1 - end : undefined;
+ fallbackText = endValue.slice(start, sliceTail);
+ return fallbackText;
+}
+function getText() {
+ if ("value" in root) {
+ return root.value;
}
- if (finishedWork === null) {
- {
- if (enableDebugTracing) {
- logCommitStopped();
- }
- }
+ return root.textContent;
+}
- if (enableSchedulingProfiler) {
- markCommitStopped();
- }
+/**
+ * `charCode` represents the actual "character code" and is safe to use with
+ * `String.fromCharCode`. As such, only keys that correspond to printable
+ * characters produce a valid `charCode`, the only exception to this is Enter.
+ * The Tab-key is considered non-printable and does not have a `charCode`,
+ * presumably because it does not produce a tab-character in browsers.
+ *
+ * @param {object} nativeEvent Native browser event.
+ * @return {number} Normalized `charCode` property.
+ */
+function getEventCharCode(nativeEvent) {
+ var charCode;
+ var keyCode = nativeEvent.keyCode;
- return null;
- } else {
- {
- if (lanes === NoLanes) {
- error(
- "root.finishedLanes should not be empty during a commit. This is a " +
- "bug in React."
- );
- }
- }
- }
+ if ("charCode" in nativeEvent) {
+ charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`.
- root.finishedWork = null;
- root.finishedLanes = NoLanes;
+ if (charCode === 0 && keyCode === 13) {
+ charCode = 13;
+ }
+ } else {
+ // IE8 does not implement `charCode`, but `keyCode` has the correct value.
+ charCode = keyCode;
+ } // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
+ // report Enter as charCode 10 when ctrl is pressed.
- if (finishedWork === root.current) {
- throw new Error(
- "Cannot commit the same tree as before. This error is likely caused by " +
- "a bug in React. Please file an issue."
- );
- } // commitRoot never returns a continuation; it always finishes synchronously.
- // So we can clear these now to allow a new callback to be scheduled.
+ if (charCode === 10) {
+ charCode = 13;
+ } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
+ // Must not discard the (non-)printable Enter-key.
- root.callbackNode = null;
- root.callbackPriority = NoLane; // Check which lanes no longer have any work scheduled on them, and mark
- // those as finished.
+ if (charCode >= 32 || charCode === 13) {
+ return charCode;
+ }
- var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes); // Make sure to account for lanes that were updated by a concurrent event
- // during the render phase; don't mark them as finished.
+ return 0;
+}
- var concurrentlyUpdatedLanes = getConcurrentlyUpdatedLanes();
- remainingLanes = mergeLanes(remainingLanes, concurrentlyUpdatedLanes);
- markRootFinished(root, remainingLanes);
+function functionThatReturnsTrue() {
+ return true;
+}
- if (root === workInProgressRoot) {
- // We can reset these now that they are finished.
- workInProgressRoot = null;
- workInProgress = null;
- workInProgressRootRenderLanes = NoLanes;
- } // If there are pending passive effects, schedule a callback to process them.
- // Do this as early as possible, so it is queued before anything else that
- // might get scheduled in the commit phase. (See #16714.)
- // TODO: Delete all other places that schedule the passive effect callback
- // They're redundant.
+function functionThatReturnsFalse() {
+ return false;
+} // This is intentionally a factory so that we have different returned constructors.
+// If we had a single constructor, it would be megamorphic and engines would deopt.
- if (
- (finishedWork.subtreeFlags & PassiveMask) !== NoFlags$1 ||
- (finishedWork.flags & PassiveMask) !== NoFlags$1
+function createSyntheticEvent(Interface) {
+ /**
+ * Synthetic events are dispatched by event plugins, typically in response to a
+ * top-level event delegation handler.
+ *
+ * These systems should generally use pooling to reduce the frequency of garbage
+ * collection. The system should check `isPersistent` to determine whether the
+ * event should be released into the pool after being dispatched. Users that
+ * need a persisted event should invoke `persist`.
+ *
+ * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
+ * normalizing browser quirks. Subclasses do not necessarily have to implement a
+ * DOM interface; custom application-specific events can also subclass this.
+ */
+ // $FlowFixMe[missing-this-annot]
+ function SyntheticBaseEvent(
+ reactName,
+ reactEventType,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
) {
- if (!rootDoesHavePassiveEffects) {
- rootDoesHavePassiveEffects = true;
- pendingPassiveEffectsRemainingLanes = remainingLanes; // workInProgressTransitions might be overwritten, so we want
- // to store it in pendingPassiveTransitions until they get processed
- // We need to pass this through as an argument to commitRoot
- // because workInProgressTransitions might have changed between
- // the previous render and commit if we throttle the commit
- // with setTimeout
+ this._reactName = reactName;
+ this._targetInst = targetInst;
+ this.type = reactEventType;
+ this.nativeEvent = nativeEvent;
+ this.target = nativeEventTarget;
+ this.currentTarget = null;
- pendingPassiveTransitions = transitions;
- scheduleCallback(NormalPriority$1, function () {
- flushPassiveEffects(); // This render triggered passive effects: release the root cache pool
- // *after* passive effects fire to avoid freeing a cache pool that may
- // be referenced by a node in the tree (HostRoot, Cache boundary etc)
+ for (var propName in Interface) {
+ if (!Interface.hasOwnProperty(propName)) {
+ continue;
+ }
- return null;
- });
+ var normalize = Interface[propName];
+
+ if (normalize) {
+ this[propName] = normalize(nativeEvent);
+ } else {
+ this[propName] = nativeEvent[propName];
+ }
}
- } // Check if there are any effects in the whole tree.
- // TODO: This is left over from the effect list implementation, where we had
- // to check for the existence of `firstEffect` to satisfy Flow. I think the
- // only other reason this optimization exists is because it affects profiling.
- // Reconsider whether this is necessary.
- var subtreeHasEffects =
- (finishedWork.subtreeFlags &
- (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
- NoFlags$1;
- var rootHasEffect =
- (finishedWork.flags &
- (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !==
- NoFlags$1;
+ var defaultPrevented =
+ nativeEvent.defaultPrevented != null
+ ? nativeEvent.defaultPrevented
+ : nativeEvent.returnValue === false;
- if (subtreeHasEffects || rootHasEffect) {
- var prevTransition = ReactCurrentBatchConfig.transition;
- ReactCurrentBatchConfig.transition = null;
- var previousPriority = getCurrentUpdatePriority$1();
- setCurrentUpdatePriority(DiscreteEventPriority);
- var prevExecutionContext = executionContext;
- executionContext |= CommitContext; // Reset this to null before calling lifecycles
+ if (defaultPrevented) {
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ } else {
+ this.isDefaultPrevented = functionThatReturnsFalse;
+ }
- ReactCurrentOwner$1.current = null; // The commit phase is broken into several sub-phases. We do a separate pass
- // of the effect list for each phase: all mutation effects come before all
- // layout effects, and so on.
- // The first phase a "before mutation" phase. We use this phase to read the
- // state of the host tree right before we mutate it. This is where
- // getSnapshotBeforeUpdate is called.
+ this.isPropagationStopped = functionThatReturnsFalse;
+ return this;
+ } // $FlowFixMe[prop-missing] found when upgrading Flow
- var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(
- root,
- finishedWork
- );
+ assign(SyntheticBaseEvent.prototype, {
+ // $FlowFixMe[missing-this-annot]
+ preventDefault: function () {
+ this.defaultPrevented = true;
+ var event = this.nativeEvent;
- {
- // Mark the current commit time to be shared by all Profilers in this
- // batch. This enables them to be grouped later.
- recordCommitTime();
- }
+ if (!event) {
+ return;
+ }
- if (enableProfilerNestedUpdateScheduledHook) {
- // Track the root here, rather than in commitLayoutEffects(), because of ref setters.
- // Updates scheduled during ref detachment should also be flagged.
- rootCommittingMutationOrLayoutEffects = root;
- } // The next phase is the mutation phase, where we mutate the host tree.
+ if (event.preventDefault) {
+ event.preventDefault(); // $FlowFixMe - flow is not aware of `unknown` in IE
+ } else if (typeof event.returnValue !== "unknown") {
+ event.returnValue = false;
+ }
- commitMutationEffects(root, finishedWork, lanes);
+ this.isDefaultPrevented = functionThatReturnsTrue;
+ },
+ // $FlowFixMe[missing-this-annot]
+ stopPropagation: function () {
+ var event = this.nativeEvent;
- {
- if (shouldFireAfterActiveInstanceBlur) {
- afterActiveInstanceBlur();
+ if (!event) {
+ return;
}
- }
- resetAfterCommit(); // The work-in-progress tree is now the current tree. This must come after
- // the mutation phase, so that the previous tree is still current during
- // componentWillUnmount, but before the layout phase, so that the finished
- // work is current during componentDidMount/Update.
+ if (event.stopPropagation) {
+ event.stopPropagation(); // $FlowFixMe - flow is not aware of `unknown` in IE
+ } else if (typeof event.cancelBubble !== "unknown") {
+ // The ChangeEventPlugin registers a "propertychange" event for
+ // IE. This event does not support bubbling or cancelling, and
+ // any references to cancelBubble throw "Member not found". A
+ // typeof check of "unknown" circumvents this issue (and is also
+ // IE specific).
+ event.cancelBubble = true;
+ }
- root.current = finishedWork; // The next phase is the layout phase, where we call effects that read
- // the host tree after it's been mutated. The idiomatic use case for this is
- // layout, but class component lifecycles also fire here for legacy reasons.
+ this.isPropagationStopped = functionThatReturnsTrue;
+ },
- {
- if (enableDebugTracing) {
- logLayoutEffectsStarted(lanes);
- }
- }
+ /**
+ * We release all dispatched `SyntheticEvent`s after each event loop, adding
+ * them back into the pool. This allows a way to hold onto a reference that
+ * won't be added back into the pool.
+ */
+ persist: function () {
+ // Modern event system doesn't use pooling.
+ },
- if (enableSchedulingProfiler) {
- markLayoutEffectsStarted(lanes);
- }
+ /**
+ * Checks if this event should be released back into the pool.
+ *
+ * @return {boolean} True if this should not be released, false otherwise.
+ */
+ isPersistent: functionThatReturnsTrue
+ });
+ return SyntheticBaseEvent;
+}
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
- commitLayoutEffects(finishedWork, root, lanes);
+var EventInterface = {
+ eventPhase: 0,
+ bubbles: 0,
+ cancelable: 0,
+ timeStamp: function (event) {
+ return event.timeStamp || Date.now();
+ },
+ defaultPrevented: 0,
+ isTrusted: 0
+};
+var SyntheticEvent = createSyntheticEvent(EventInterface);
- {
- if (enableDebugTracing) {
- logLayoutEffectsStopped();
- }
- }
+var UIEventInterface = assign({}, EventInterface, {
+ view: 0,
+ detail: 0
+});
- if (enableSchedulingProfiler) {
- markLayoutEffectsStopped();
- }
+var SyntheticUIEvent = createSyntheticEvent(UIEventInterface);
+var lastMovementX;
+var lastMovementY;
+var lastMouseEvent;
- if (enableProfilerNestedUpdateScheduledHook) {
- rootCommittingMutationOrLayoutEffects = null;
- } // Tell Scheduler to yield at the end of the frame, so the browser has an
- // opportunity to paint.
+function updateMouseMovementPolyfillState(event) {
+ if (event !== lastMouseEvent) {
+ if (lastMouseEvent && event.type === "mousemove") {
+ // $FlowFixMe assuming this is a number
+ lastMovementX = event.screenX - lastMouseEvent.screenX; // $FlowFixMe assuming this is a number
- requestPaint();
- executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value.
+ lastMovementY = event.screenY - lastMouseEvent.screenY;
+ } else {
+ lastMovementX = 0;
+ lastMovementY = 0;
+ }
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig.transition = prevTransition;
- } else {
- // No effects.
- root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were
- // no effects.
- // TODO: Maybe there's a better way to report this.
+ lastMouseEvent = event;
+ }
+}
+/**
+ * @interface MouseEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
- {
- recordCommitTime();
+var MouseEventInterface = assign({}, UIEventInterface, {
+ screenX: 0,
+ screenY: 0,
+ clientX: 0,
+ clientY: 0,
+ pageX: 0,
+ pageY: 0,
+ ctrlKey: 0,
+ shiftKey: 0,
+ altKey: 0,
+ metaKey: 0,
+ getModifierState: getEventModifierState,
+ button: 0,
+ buttons: 0,
+ relatedTarget: function (event) {
+ if (event.relatedTarget === undefined)
+ return event.fromElement === event.srcElement
+ ? event.toElement
+ : event.fromElement;
+ return event.relatedTarget;
+ },
+ movementX: function (event) {
+ if ("movementX" in event) {
+ return event.movementX;
}
+
+ updateMouseMovementPolyfillState(event);
+ return lastMovementX;
+ },
+ movementY: function (event) {
+ if ("movementY" in event) {
+ return event.movementY;
+ } // Don't need to call updateMouseMovementPolyfillState() here
+ // because it's guaranteed to have already run when movementX
+ // was copied.
+
+ return lastMovementY;
}
+});
- var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
+var SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
+/**
+ * @interface DragEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
- if (rootDoesHavePassiveEffects) {
- // This commit has passive effects. Stash a reference to them. But don't
- // schedule a callback until after flushing layout work.
- rootDoesHavePassiveEffects = false;
- rootWithPendingPassiveEffects = root;
- pendingPassiveEffectsLanes = lanes;
- } else {
- // There were no passive effects, so we can immediately release the cache
- // pool for this render.
- releaseRootPooledCache(root, remainingLanes);
+var DragEventInterface = assign({}, MouseEventInterface, {
+ dataTransfer: 0
+});
- {
- nestedPassiveUpdateCount = 0;
- rootWithPassiveNestedUpdates = null;
- }
- } // Read this again, since an effect might have updated it
+var SyntheticDragEvent = createSyntheticEvent(DragEventInterface);
+/**
+ * @interface FocusEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
- remainingLanes = root.pendingLanes; // Check if there's remaining work on this root
- // TODO: This is part of the `componentDidCatch` implementation. Its purpose
- // is to detect whether something might have called setState inside
- // `componentDidCatch`. The mechanism is known to be flawed because `setState`
- // inside `componentDidCatch` is itself flawed — that's why we recommend
- // `getDerivedStateFromError` instead. However, it could be improved by
- // checking if remainingLanes includes Sync work, instead of whether there's
- // any work remaining at all (which would also include stuff like Suspense
- // retries or transitions). It's been like this for a while, though, so fixing
- // it probably isn't that urgent.
+var FocusEventInterface = assign({}, UIEventInterface, {
+ relatedTarget: 0
+});
- if (remainingLanes === NoLanes) {
- // If there's no remaining work, we can clear the set of already failed
- // error boundaries.
- legacyErrorBoundariesThatAlreadyFailed = null;
- }
+var SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface);
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
+ */
- {
- if (!rootDidHavePassiveEffects) {
- commitDoubleInvokeEffectsInDEV(root, false);
- }
- }
+var AnimationEventInterface = assign({}, EventInterface, {
+ animationName: 0,
+ elapsedTime: 0,
+ pseudoElement: 0
+});
- onCommitRoot$1(finishedWork.stateNode, renderPriorityLevel);
+var SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface);
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/clipboard-apis/
+ */
- {
- if (isDevToolsPresent) {
- root.memoizedUpdaters.clear();
- }
+var ClipboardEventInterface = assign({}, EventInterface, {
+ clipboardData: function (event) {
+ return "clipboardData" in event
+ ? event.clipboardData
+ : window.clipboardData;
}
+});
- {
- onCommitRoot();
- } // Always call this before exiting `commitRoot`, to ensure that any
- // additional work on this root is scheduled.
-
- ensureRootIsScheduled(root, now$1());
+var SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface);
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
+ */
- if (recoverableErrors !== null) {
- // There were errors during this render, but recovered from them without
- // needing to surface it to the UI. We log them here.
- var onRecoverableError = root.onRecoverableError;
+var CompositionEventInterface = assign({}, EventInterface, {
+ data: 0
+});
- for (var i = 0; i < recoverableErrors.length; i++) {
- var recoverableError = recoverableErrors[i];
- var errorInfo = makeErrorInfo(
- recoverableError.digest,
- recoverableError.stack
- );
- onRecoverableError(recoverableError.value, errorInfo);
- }
- }
+var SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface);
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
+ * /#events-inputevents
+ */
+// Happens to share the same list for now.
- if (hasUncaughtError) {
- hasUncaughtError = false;
- var error$1 = firstUncaughtError;
- firstUncaughtError = null;
- throw error$1;
- } // If the passive effects are the result of a discrete render, flush them
- // synchronously at the end of the current task so that the result is
- // immediately observable. Otherwise, we assume that they are not
- // order-dependent and do not need to be observed by external systems, so we
- // can wait until after paint.
- // TODO: We can optimize this by not scheduling the callback earlier. Since we
- // currently schedule the callback in multiple places, will wait until those
- // are consolidated.
+var SyntheticInputEvent = SyntheticCompositionEvent;
+/**
+ * Normalization of deprecated HTML5 `key` values
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
- if (includesSyncLane(pendingPassiveEffectsLanes) && root.tag !== LegacyRoot) {
- flushPassiveEffects();
- } // Read this again, since a passive effect might have updated it
+var normalizeKey = {
+ Esc: "Escape",
+ Spacebar: " ",
+ Left: "ArrowLeft",
+ Up: "ArrowUp",
+ Right: "ArrowRight",
+ Down: "ArrowDown",
+ Del: "Delete",
+ Win: "OS",
+ Menu: "ContextMenu",
+ Apps: "ContextMenu",
+ Scroll: "ScrollLock",
+ MozPrintableKey: "Unidentified"
+};
+/**
+ * Translation from legacy `keyCode` to HTML5 `key`
+ * Only special keys supported, all others depend on keyboard layout or browser
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
+ */
- remainingLanes = root.pendingLanes;
+var translateToKey = {
+ "8": "Backspace",
+ "9": "Tab",
+ "12": "Clear",
+ "13": "Enter",
+ "16": "Shift",
+ "17": "Control",
+ "18": "Alt",
+ "19": "Pause",
+ "20": "CapsLock",
+ "27": "Escape",
+ "32": " ",
+ "33": "PageUp",
+ "34": "PageDown",
+ "35": "End",
+ "36": "Home",
+ "37": "ArrowLeft",
+ "38": "ArrowUp",
+ "39": "ArrowRight",
+ "40": "ArrowDown",
+ "45": "Insert",
+ "46": "Delete",
+ "112": "F1",
+ "113": "F2",
+ "114": "F3",
+ "115": "F4",
+ "116": "F5",
+ "117": "F6",
+ "118": "F7",
+ "119": "F8",
+ "120": "F9",
+ "121": "F10",
+ "122": "F11",
+ "123": "F12",
+ "144": "NumLock",
+ "145": "ScrollLock",
+ "224": "Meta"
+};
+/**
+ * @param {object} nativeEvent Native browser event.
+ * @return {string} Normalized `key` property.
+ */
- if (includesSyncLane(remainingLanes)) {
- {
- markNestedUpdateScheduled();
- } // Count the number of times the root synchronously re-renders without
- // finishing. If there are too many, it indicates an infinite update loop.
+function getEventKey(nativeEvent) {
+ if (nativeEvent.key) {
+ // Normalize inconsistent values reported by browsers due to
+ // implementations of a working draft specification.
+ // FireFox implements `key` but returns `MozPrintableKey` for all
+ // printable characters (normalized to `Unidentified`), ignore it.
+ var key = normalizeKey[nativeEvent.key] || nativeEvent.key; // $FlowFixMe unable to index with a `mixed` value
- if (root === rootWithNestedUpdates) {
- nestedUpdateCount++;
- } else {
- nestedUpdateCount = 0;
- rootWithNestedUpdates = root;
+ if (key !== "Unidentified") {
+ return key;
}
- } else {
- nestedUpdateCount = 0;
- } // If layout work was scheduled, flush it now.
+ } // Browser does not implement `key`, polyfill as much of it as we can.
- flushSyncCallbacks();
+ if (nativeEvent.type === "keypress") {
+ var charCode = getEventCharCode(
+ // $FlowFixMe unable to narrow to `KeyboardEvent`
+ nativeEvent
+ ); // The enter-key is technically both printable and non-printable and can
+ // thus be captured by `keypress`, no other non-printable key should.
- {
- if (enableDebugTracing) {
- logCommitStopped();
- }
+ return charCode === 13 ? "Enter" : String.fromCharCode(charCode);
}
- if (enableSchedulingProfiler) {
- markCommitStopped();
+ if (nativeEvent.type === "keydown" || nativeEvent.type === "keyup") {
+ // While user keyboard layout determines the actual meaning of each
+ // `keyCode` value, almost all function keys have a universal value.
+ // $FlowFixMe unable to index with a `mixed` value
+ return translateToKey[nativeEvent.keyCode] || "Unidentified";
}
- if (enableTransitionTracing) {
- // We process transitions during passive effects. However, passive effects can be
- // processed synchronously during the commit phase as well as asynchronously after
- // paint. At the end of the commit phase, we schedule a callback that will be called
- // after the next paint. If the transitions have already been processed (passive
- // effect phase happened synchronously), we will schedule a callback to process
- // the transitions. However, if we don't have any pending transition callbacks, this
- // means that the transitions have yet to be processed (passive effects processed after paint)
- // so we will store the end time of paint so that we can process the transitions
- // and then call the callback via the correct end time.
- var prevRootTransitionCallbacks = root.transitionCallbacks;
+ return "";
+}
+/**
+ * Translation from modifier key to the associated property in the event.
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
+ */
- if (prevRootTransitionCallbacks !== null) {
- schedulePostPaintCallback(function (endTime) {
- var prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
+var modifierKeyToProp = {
+ Alt: "altKey",
+ Control: "ctrlKey",
+ Meta: "metaKey",
+ Shift: "shiftKey"
+}; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
+// getModifierState. If getModifierState is not supported, we map it to a set of
+// modifier keys exposed by the event. In this case, Lock-keys are not supported.
+// $FlowFixMe[missing-local-annot]
+// $FlowFixMe[missing-this-annot]
- if (prevPendingTransitionCallbacks !== null) {
- currentPendingTransitionCallbacks = null;
- scheduleCallback(IdlePriority, function () {
- processTransitionCallbacks(
- prevPendingTransitionCallbacks,
- endTime,
- prevRootTransitionCallbacks
- );
- });
- } else {
- currentEndTime = endTime;
- }
- });
- }
+function modifierStateGetter(keyArg) {
+ var syntheticEvent = this;
+ var nativeEvent = syntheticEvent.nativeEvent;
+
+ if (nativeEvent.getModifierState) {
+ return nativeEvent.getModifierState(keyArg);
}
- return null;
+ var keyProp = modifierKeyToProp[keyArg];
+ return keyProp ? !!nativeEvent[keyProp] : false;
}
-function makeErrorInfo(digest, componentStack) {
- {
- var errorInfo = {
- componentStack: componentStack,
- digest: digest
- };
- Object.defineProperty(errorInfo, "digest", {
- configurable: false,
- enumerable: true,
- get: function () {
- error(
- 'You are accessing "digest" from the errorInfo object passed to onRecoverableError.' +
- " This property is deprecated and will be removed in a future version of React." +
- " To access the digest of an Error look for this property on the Error instance itself."
- );
-
- return digest;
- }
- });
- return errorInfo;
- }
+function getEventModifierState(nativeEvent) {
+ return modifierStateGetter;
}
+/**
+ * @interface KeyboardEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
-function releaseRootPooledCache(root, remainingLanes) {
- {
- var pooledCacheLanes = (root.pooledCacheLanes &= remainingLanes);
+var KeyboardEventInterface = assign({}, UIEventInterface, {
+ key: getEventKey,
+ code: 0,
+ location: 0,
+ ctrlKey: 0,
+ shiftKey: 0,
+ altKey: 0,
+ metaKey: 0,
+ repeat: 0,
+ locale: 0,
+ getModifierState: getEventModifierState,
+ // Legacy Interface
+ charCode: function (event) {
+ // `charCode` is the result of a KeyPress event and represents the value of
+ // the actual printable character.
+ // KeyPress is deprecated, but its replacement is not yet final and not
+ // implemented in any major browser. Only KeyPress has charCode.
+ if (event.type === "keypress") {
+ return getEventCharCode(
+ // $FlowFixMe unable to narrow to `KeyboardEvent`
+ event
+ );
+ }
- if (pooledCacheLanes === NoLanes) {
- // None of the remaining work relies on the cache pool. Clear it so
- // subsequent requests get a new cache
- var pooledCache = root.pooledCache;
+ return 0;
+ },
+ keyCode: function (event) {
+ // `keyCode` is the result of a KeyDown/Up event and represents the value of
+ // physical keyboard key.
+ // The actual meaning of the value depends on the users' keyboard layout
+ // which cannot be detected. Assuming that it is a US keyboard layout
+ // provides a surprisingly accurate mapping for US and European users.
+ // Due to this, it is left to the user to implement at this time.
+ if (event.type === "keydown" || event.type === "keyup") {
+ return event.keyCode;
+ }
- if (pooledCache != null) {
- root.pooledCache = null;
- releaseCache(pooledCache);
- }
+ return 0;
+ },
+ which: function (event) {
+ // `which` is an alias for either `keyCode` or `charCode` depending on the
+ // type of the event.
+ if (event.type === "keypress") {
+ return getEventCharCode(
+ // $FlowFixMe unable to narrow to `KeyboardEvent`
+ event
+ );
+ }
+
+ if (event.type === "keydown" || event.type === "keyup") {
+ return event.keyCode;
}
+
+ return 0;
}
-}
+});
-function flushPassiveEffects() {
- // Returns whether passive effects were flushed.
- // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
- // probably just combine the two functions. I believe they were only separate
- // in the first place because we used to wrap it with
- // `Scheduler.runWithPriority`, which accepts a function. But now we track the
- // priority within React itself, so we can mutate the variable directly.
- if (rootWithPendingPassiveEffects !== null) {
- // Cache the root since rootWithPendingPassiveEffects is cleared in
- // flushPassiveEffectsImpl
- var root = rootWithPendingPassiveEffects; // Cache and clear the remaining lanes flag; it must be reset since this
- // method can be called from various places, not always from commitRoot
- // where the remaining lanes are known
+var SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface);
+/**
+ * @interface PointerEvent
+ * @see http://www.w3.org/TR/pointerevents/
+ */
- var remainingLanes = pendingPassiveEffectsRemainingLanes;
- pendingPassiveEffectsRemainingLanes = NoLanes;
- var renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
- var priority = lowerEventPriority(DefaultEventPriority, renderPriority);
- var prevTransition = ReactCurrentBatchConfig.transition;
- var previousPriority = getCurrentUpdatePriority$1();
+var PointerEventInterface = assign({}, MouseEventInterface, {
+ pointerId: 0,
+ width: 0,
+ height: 0,
+ pressure: 0,
+ tangentialPressure: 0,
+ tiltX: 0,
+ tiltY: 0,
+ twist: 0,
+ pointerType: 0,
+ isPrimary: 0
+});
- try {
- ReactCurrentBatchConfig.transition = null;
- setCurrentUpdatePriority(priority);
- return flushPassiveEffectsImpl();
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig.transition = prevTransition; // Once passive effects have run for the tree - giving components a
- // chance to retain cache instances they use - release the pooled
- // cache at the root (if there is one)
+var SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface);
+/**
+ * @interface TouchEvent
+ * @see http://www.w3.org/TR/touch-events/
+ */
- releaseRootPooledCache(root, remainingLanes);
- }
- }
+var TouchEventInterface = assign({}, UIEventInterface, {
+ touches: 0,
+ targetTouches: 0,
+ changedTouches: 0,
+ altKey: 0,
+ metaKey: 0,
+ ctrlKey: 0,
+ shiftKey: 0,
+ getModifierState: getEventModifierState
+});
- return false;
-}
-function enqueuePendingPassiveProfilerEffect(fiber) {
- {
- pendingPassiveProfilerEffects.push(fiber);
+var SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface);
+/**
+ * @interface Event
+ * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
+ */
- if (!rootDoesHavePassiveEffects) {
- rootDoesHavePassiveEffects = true;
- scheduleCallback(NormalPriority$1, function () {
- flushPassiveEffects();
- return null;
- });
- }
- }
-}
+var TransitionEventInterface = assign({}, EventInterface, {
+ propertyName: 0,
+ elapsedTime: 0,
+ pseudoElement: 0
+});
-function flushPassiveEffectsImpl() {
- if (rootWithPendingPassiveEffects === null) {
- return false;
- } // Cache and clear the transitions flag
+var SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface);
+/**
+ * @interface WheelEvent
+ * @see http://www.w3.org/TR/DOM-Level-3-Events/
+ */
+
+var WheelEventInterface = assign({}, MouseEventInterface, {
+ deltaX: function (event) {
+ return "deltaX" in event
+ ? event.deltaX // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
+ : "wheelDeltaX" in event // $FlowFixMe assuming this is a number
+ ? -event.wheelDeltaX
+ : 0;
+ },
+ deltaY: function (event) {
+ return "deltaY" in event
+ ? event.deltaY // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
+ : "wheelDeltaY" in event // $FlowFixMe assuming this is a number
+ ? -event.wheelDeltaY // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
+ : "wheelDelta" in event // $FlowFixMe assuming this is a number
+ ? -event.wheelDelta
+ : 0;
+ },
+ deltaZ: 0,
+ // Browsers without "deltaMode" is reporting in raw wheel delta where one
+ // notch on the scroll is always +/- 120, roughly equivalent to pixels.
+ // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
+ // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
+ deltaMode: 0
+});
- var transitions = pendingPassiveTransitions;
- pendingPassiveTransitions = null;
- var root = rootWithPendingPassiveEffects;
- var lanes = pendingPassiveEffectsLanes;
- rootWithPendingPassiveEffects = null; // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.
- // Figure out why and fix it. It's not causing any known issues (probably
- // because it's only used for profiling), but it's a refactor hazard.
+var SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface);
- pendingPassiveEffectsLanes = NoLanes;
+var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
- if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
- throw new Error("Cannot flush passive effects while already rendering.");
- }
+var START_KEYCODE = 229;
+var canUseCompositionEvent = canUseDOM && "CompositionEvent" in window;
+var documentMode = null;
- {
- isFlushingPassiveEffects = true;
- didScheduleUpdateDuringPassiveEffects = false;
+if (canUseDOM && "documentMode" in document) {
+ documentMode = document.documentMode;
+} // Webkit offers a very useful `textInput` event that can be used to
+// directly represent `beforeInput`. The IE `textinput` event is not as
+// useful, so we don't use it.
- if (enableDebugTracing) {
- logPassiveEffectsStarted(lanes);
- }
- }
+var canUseTextInputEvent = canUseDOM && "TextEvent" in window && !documentMode; // In IE9+, we have access to composition events, but the data supplied
+// by the native compositionend event may be incorrect. Japanese ideographic
+// spaces, for instance (\u3000) are not recorded correctly.
- if (enableSchedulingProfiler) {
- markPassiveEffectsStarted(lanes);
- }
+var useFallbackCompositionData =
+ canUseDOM &&
+ (!canUseCompositionEvent ||
+ (documentMode && documentMode > 8 && documentMode <= 11));
+var SPACEBAR_CODE = 32;
+var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
- var prevExecutionContext = executionContext;
- executionContext |= CommitContext;
- commitPassiveUnmountEffects(root.current);
- commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects
+function registerEvents$3() {
+ registerTwoPhaseEvent("onBeforeInput", [
+ "compositionend",
+ "keypress",
+ "textInput",
+ "paste"
+ ]);
+ registerTwoPhaseEvent("onCompositionEnd", [
+ "compositionend",
+ "focusout",
+ "keydown",
+ "keypress",
+ "keyup",
+ "mousedown"
+ ]);
+ registerTwoPhaseEvent("onCompositionStart", [
+ "compositionstart",
+ "focusout",
+ "keydown",
+ "keypress",
+ "keyup",
+ "mousedown"
+ ]);
+ registerTwoPhaseEvent("onCompositionUpdate", [
+ "compositionupdate",
+ "focusout",
+ "keydown",
+ "keypress",
+ "keyup",
+ "mousedown"
+ ]);
+} // Track whether we've ever handled a keypress on the space key.
- {
- var profilerEffects = pendingPassiveProfilerEffects;
- pendingPassiveProfilerEffects = [];
+var hasSpaceKeypress = false;
+/**
+ * Return whether a native keypress event is assumed to be a command.
+ * This is required because Firefox fires `keypress` events for key commands
+ * (cut, copy, select-all, etc.) even though no character is inserted.
+ */
- for (var i = 0; i < profilerEffects.length; i++) {
- var fiber = profilerEffects[i];
- commitPassiveEffectDurations(root, fiber);
- }
- }
+function isKeypressCommand(nativeEvent) {
+ return (
+ (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command.
+ !(nativeEvent.ctrlKey && nativeEvent.altKey)
+ );
+}
+/**
+ * Translate native top level events into event types.
+ */
- {
- if (enableDebugTracing) {
- logPassiveEffectsStopped();
- }
- }
+function getCompositionEventType(domEventName) {
+ switch (domEventName) {
+ case "compositionstart":
+ return "onCompositionStart";
- if (enableSchedulingProfiler) {
- markPassiveEffectsStopped();
- }
+ case "compositionend":
+ return "onCompositionEnd";
- {
- commitDoubleInvokeEffectsInDEV(root, true);
+ case "compositionupdate":
+ return "onCompositionUpdate";
}
+}
+/**
+ * Does our fallback best-guess model think this event signifies that
+ * composition has begun?
+ */
- executionContext = prevExecutionContext;
- flushSyncCallbacks();
-
- if (enableTransitionTracing) {
- var prevPendingTransitionCallbacks = currentPendingTransitionCallbacks;
- var prevRootTransitionCallbacks = root.transitionCallbacks;
- var prevEndTime = currentEndTime;
-
- if (
- prevPendingTransitionCallbacks !== null &&
- prevRootTransitionCallbacks !== null &&
- prevEndTime !== null
- ) {
- currentPendingTransitionCallbacks = null;
- currentEndTime = null;
- scheduleCallback(IdlePriority, function () {
- processTransitionCallbacks(
- prevPendingTransitionCallbacks,
- prevEndTime,
- prevRootTransitionCallbacks
- );
- });
- }
- }
+function isFallbackCompositionStart(domEventName, nativeEvent) {
+ return domEventName === "keydown" && nativeEvent.keyCode === START_KEYCODE;
+}
+/**
+ * Does our fallback mode think that this event is the end of composition?
+ */
- {
- // If additional passive effects were scheduled, increment a counter. If this
- // exceeds the limit, we'll fire a warning.
- if (didScheduleUpdateDuringPassiveEffects) {
- if (root === rootWithPassiveNestedUpdates) {
- nestedPassiveUpdateCount++;
- } else {
- nestedPassiveUpdateCount = 0;
- rootWithPassiveNestedUpdates = root;
- }
- } else {
- nestedPassiveUpdateCount = 0;
- }
+function isFallbackCompositionEnd(domEventName, nativeEvent) {
+ switch (domEventName) {
+ case "keyup":
+ // Command keys insert or clear IME input.
+ return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
- isFlushingPassiveEffects = false;
- didScheduleUpdateDuringPassiveEffects = false;
- } // TODO: Move to commitPassiveMountEffects
+ case "keydown":
+ // Expect IME keyCode on each keydown. If we get any other
+ // code we must have exited earlier.
+ return nativeEvent.keyCode !== START_KEYCODE;
- onPostCommitRoot(root);
+ case "keypress":
+ case "mousedown":
+ case "focusout":
+ // Events are not possible without cancelling IME.
+ return true;
- {
- var stateNode = root.current.stateNode;
- stateNode.effectDuration = 0;
- stateNode.passiveEffectDuration = 0;
+ default:
+ return false;
}
-
- return true;
}
+/**
+ * Google Input Tools provides composition data via a CustomEvent,
+ * with the `data` property populated in the `detail` object. If this
+ * is available on the event object, use it. If not, this is a plain
+ * composition event and we have nothing special to extract.
+ *
+ * @param {object} nativeEvent
+ * @return {?string}
+ */
-function isAlreadyFailedLegacyErrorBoundary(instance) {
- return (
- legacyErrorBoundariesThatAlreadyFailed !== null &&
- legacyErrorBoundariesThatAlreadyFailed.has(instance)
- );
-}
-function markLegacyErrorBoundaryAsFailed(instance) {
- if (legacyErrorBoundariesThatAlreadyFailed === null) {
- legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
- } else {
- legacyErrorBoundariesThatAlreadyFailed.add(instance);
- }
-}
+function getDataFromCustomEvent(nativeEvent) {
+ var detail = nativeEvent.detail;
-function prepareToThrowUncaughtError(error) {
- if (!hasUncaughtError) {
- hasUncaughtError = true;
- firstUncaughtError = error;
+ if (typeof detail === "object" && "data" in detail) {
+ return detail.data;
}
+
+ return null;
}
+/**
+ * Check if a composition event was triggered by Korean IME.
+ * Our fallback mode does not work well with IE's Korean IME,
+ * so just use native composition events when Korean IME is used.
+ * Although CompositionEvent.locale property is deprecated,
+ * it is available in IE, where our fallback mode is enabled.
+ *
+ * @param {object} nativeEvent
+ * @return {boolean}
+ */
-var onUncaughtError = prepareToThrowUncaughtError;
+function isUsingKoreanIME(nativeEvent) {
+ return nativeEvent.locale === "ko";
+} // Track the current IME composition status, if any.
-function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {
- var errorInfo = createCapturedValueAtFiber(error, sourceFiber);
- var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);
- var root = enqueueUpdate(rootFiber, update, SyncLane);
- var eventTime = requestEventTime();
+var isComposing = false;
+/**
+ * @return {?object} A SyntheticCompositionEvent.
+ */
- if (root !== null) {
- markRootUpdated(root, SyncLane, eventTime);
- ensureRootIsScheduled(root, eventTime);
- }
-}
+function extractCompositionEvent(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+) {
+ var eventType;
+ var fallbackData;
-function captureCommitPhaseError(sourceFiber, nearestMountedAncestor, error$1) {
- {
- reportUncaughtErrorInDEV(error$1);
- setIsRunningInsertionEffect(false);
+ if (canUseCompositionEvent) {
+ eventType = getCompositionEventType(domEventName);
+ } else if (!isComposing) {
+ if (isFallbackCompositionStart(domEventName, nativeEvent)) {
+ eventType = "onCompositionStart";
+ }
+ } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) {
+ eventType = "onCompositionEnd";
}
- if (sourceFiber.tag === HostRoot) {
- // Error was thrown at the root. There is no parent, so the root
- // itself should capture it.
- captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error$1);
- return;
+ if (!eventType) {
+ return null;
}
- var fiber = null;
-
- if (skipUnmountedBoundaries) {
- fiber = nearestMountedAncestor;
- } else {
- fiber = sourceFiber.return;
+ if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
+ // The current composition is stored statically and must not be
+ // overwritten while composition continues.
+ if (!isComposing && eventType === "onCompositionStart") {
+ isComposing = initialize(nativeEventTarget);
+ } else if (eventType === "onCompositionEnd") {
+ if (isComposing) {
+ fallbackData = getData();
+ }
+ }
}
- while (fiber !== null) {
- if (fiber.tag === HostRoot) {
- captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error$1);
- return;
- } else if (fiber.tag === ClassComponent) {
- var ctor = fiber.type;
- var instance = fiber.stateNode;
+ var listeners = accumulateTwoPhaseListeners(targetInst, eventType);
- if (
- typeof ctor.getDerivedStateFromError === "function" ||
- (typeof instance.componentDidCatch === "function" &&
- !isAlreadyFailedLegacyErrorBoundary(instance))
- ) {
- var errorInfo = createCapturedValueAtFiber(error$1, sourceFiber);
- var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);
- var root = enqueueUpdate(fiber, update, SyncLane);
- var eventTime = requestEventTime();
+ if (listeners.length > 0) {
+ var event = new SyntheticCompositionEvent(
+ eventType,
+ domEventName,
+ null,
+ nativeEvent,
+ nativeEventTarget
+ );
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ });
- if (root !== null) {
- markRootUpdated(root, SyncLane, eventTime);
- ensureRootIsScheduled(root, eventTime);
- }
+ if (fallbackData) {
+ // Inject data generated from fallback path into the synthetic event.
+ // This matches the property of native CompositionEventInterface.
+ // $FlowFixMe[incompatible-use]
+ event.data = fallbackData;
+ } else {
+ var customData = getDataFromCustomEvent(nativeEvent);
- return;
+ if (customData !== null) {
+ // $FlowFixMe[incompatible-use]
+ event.data = customData;
}
}
-
- fiber = fiber.return;
- }
-
- {
- // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning
- // will fire for errors that are thrown by destroy functions inside deleted
- // trees. What it should instead do is propagate the error to the parent of
- // the deleted tree. In the meantime, do not add this warning to the
- // allowlist; this is only for our internal use.
- error(
- "Internal React error: Attempted to capture a commit phase error " +
- "inside a detached tree. This indicates a bug in React. Likely " +
- "causes include deleting the same fiber more than once, committing an " +
- "already-finished tree, or an inconsistent return pointer.\n\n" +
- "Error message:\n\n%s",
- error$1
- );
}
}
-function attachPingListener(root, wakeable, lanes) {
- // Attach a ping listener
- //
- // The data might resolve before we have a chance to commit the fallback. Or,
- // in the case of a refresh, we'll never commit a fallback. So we need to
- // attach a listener now. When it resolves ("pings"), we can decide whether to
- // try rendering the tree again.
- //
- // Only attach a listener if one does not already exist for the lanes
- // we're currently rendering (which acts like a "thread ID" here).
- //
- // We only need to do this in concurrent mode. Legacy Suspense always
- // commits fallbacks synchronously, so there are no pings.
- var pingCache = root.pingCache;
- var threadIDs;
- if (pingCache === null) {
- pingCache = root.pingCache = new PossiblyWeakMap();
- threadIDs = new Set();
- pingCache.set(wakeable, threadIDs);
- } else {
- threadIDs = pingCache.get(wakeable);
+function getNativeBeforeInputChars(domEventName, nativeEvent) {
+ switch (domEventName) {
+ case "compositionend":
+ return getDataFromCustomEvent(nativeEvent);
- if (threadIDs === undefined) {
- threadIDs = new Set();
- pingCache.set(wakeable, threadIDs);
- }
- }
+ case "keypress":
+ /**
+ * If native `textInput` events are available, our goal is to make
+ * use of them. However, there is a special case: the spacebar key.
+ * In Webkit, preventing default on a spacebar `textInput` event
+ * cancels character insertion, but it *also* causes the browser
+ * to fall back to its default spacebar behavior of scrolling the
+ * page.
+ *
+ * Tracking at:
+ * https://code.google.com/p/chromium/issues/detail?id=355103
+ *
+ * To avoid this issue, use the keypress event as if no `textInput`
+ * event is available.
+ */
+ var which = nativeEvent.which;
- if (!threadIDs.has(lanes)) {
- workInProgressRootDidAttachPingListener = true; // Memoize using the thread ID to prevent redundant listeners.
+ if (which !== SPACEBAR_CODE) {
+ return null;
+ }
- threadIDs.add(lanes);
- var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
+ hasSpaceKeypress = true;
+ return SPACEBAR_CHAR;
- {
- if (isDevToolsPresent) {
- // If we have pending work still, restore the original updaters
- restorePendingUpdaters(root, lanes);
+ case "textInput":
+ // Record the characters to be added to the DOM.
+ var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled
+ // it at the keypress level and bail immediately. Android Chrome
+ // doesn't give us keycodes, so we need to ignore it.
+
+ if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
+ return null;
}
- }
- wakeable.then(ping, ping);
+ return chars;
+
+ default:
+ // For other native event types, do nothing.
+ return null;
}
}
+/**
+ * For browsers that do not provide the `textInput` event, extract the
+ * appropriate string to use for SyntheticInputEvent.
+ */
-function pingSuspendedRoot(root, wakeable, pingedLanes) {
- var pingCache = root.pingCache;
+function getFallbackBeforeInputChars(domEventName, nativeEvent) {
+ // If we are currently composing (IME) and using a fallback to do so,
+ // try to extract the composed characters from the fallback object.
+ // If composition event is available, we extract a string only at
+ // compositionevent, otherwise extract it at fallback events.
+ if (isComposing) {
+ if (
+ domEventName === "compositionend" ||
+ (!canUseCompositionEvent &&
+ isFallbackCompositionEnd(domEventName, nativeEvent))
+ ) {
+ var chars = getData();
+ reset();
+ isComposing = false;
+ return chars;
+ }
- if (pingCache !== null) {
- // The wakeable resolved, so we no longer need to memoize, because it will
- // never be thrown again.
- pingCache.delete(wakeable);
+ return null;
}
- var eventTime = requestEventTime();
- markRootPinged(root, pingedLanes);
- warnIfSuspenseResolutionNotWrappedWithActDEV(root);
+ switch (domEventName) {
+ case "paste":
+ // If a paste event occurs after a keypress, throw out the input
+ // chars. Paste events should not lead to BeforeInput events.
+ return null;
- if (
- workInProgressRoot === root &&
- isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)
- ) {
- // Received a ping at the same priority level at which we're currently
- // rendering. We might want to restart this render. This should mirror
- // the logic of whether or not a root suspends once it completes.
- // TODO: If we're rendering sync either due to Sync, Batched or expired,
- // we should probably never restart.
- // If we're suspended with delay, or if it's a retry, we'll always suspend
- // so we can always restart.
- if (
- workInProgressRootExitStatus === RootSuspendedWithDelay ||
- (workInProgressRootExitStatus === RootSuspended &&
- includesOnlyRetries(workInProgressRootRenderLanes) &&
- now$1() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS)
- ) {
- // Force a restart from the root by unwinding the stack. Unless this is
- // being called from the render phase, because that would cause a crash.
- if ((executionContext & RenderContext) === NoContext) {
- prepareFreshStack(root, NoLanes);
+ case "keypress":
+ /**
+ * As of v27, Firefox may fire keypress events even when no character
+ * will be inserted. A few possibilities:
+ *
+ * - `which` is `0`. Arrow keys, Esc key, etc.
+ *
+ * - `which` is the pressed key code, but no char is available.
+ * Ex: 'AltGr + d` in Polish. There is no modified character for
+ * this key combination and no character is inserted into the
+ * document, but FF fires the keypress for char code `100` anyway.
+ * No `input` event will occur.
+ *
+ * - `which` is the pressed key code, but a command combination is
+ * being used. Ex: `Cmd+C`. No character is inserted, and no
+ * `input` event will occur.
+ */
+ if (!isKeypressCommand(nativeEvent)) {
+ // IE fires the `keypress` event when a user types an emoji via
+ // Touch keyboard of Windows. In such a case, the `char` property
+ // holds an emoji character like `\uD83D\uDE0A`. Because its length
+ // is 2, the property `which` does not represent an emoji correctly.
+ // In such a case, we directly return the `char` property instead of
+ // using `which`.
+ if (nativeEvent.char && nativeEvent.char.length > 1) {
+ return nativeEvent.char;
+ } else if (nativeEvent.which) {
+ return String.fromCharCode(nativeEvent.which);
+ }
}
- } else {
- // Even though we can't restart right now, we might get an
- // opportunity later. So we mark this render as having a ping.
- workInProgressRootPingedLanes = mergeLanes(
- workInProgressRootPingedLanes,
- pingedLanes
- );
- }
- }
- ensureRootIsScheduled(root, eventTime);
-}
-
-function retryTimedOutBoundary(boundaryFiber, retryLane) {
- // The boundary fiber (a Suspense component or SuspenseList component)
- // previously was rendered in its fallback state. One of the promises that
- // suspended it has resolved, which means at least part of the tree was
- // likely unblocked. Try rendering again, at a new lanes.
- if (retryLane === NoLane) {
- // TODO: Assign this to `suspenseState.retryLane`? to avoid
- // unnecessary entanglement?
- retryLane = requestRetryLane(boundaryFiber);
- } // TODO: Special case idle priority?
+ return null;
- var eventTime = requestEventTime();
- var root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
+ case "compositionend":
+ return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)
+ ? null
+ : nativeEvent.data;
- if (root !== null) {
- markRootUpdated(root, retryLane, eventTime);
- ensureRootIsScheduled(root, eventTime);
+ default:
+ return null;
}
}
+/**
+ * Extract a SyntheticInputEvent for `beforeInput`, based on either native
+ * `textInput` or fallback behavior.
+ *
+ * @return {?object} A SyntheticInputEvent.
+ */
-function retryDehydratedSuspenseBoundary(boundaryFiber) {
- var suspenseState = boundaryFiber.memoizedState;
- var retryLane = NoLane;
+function extractBeforeInputEvent(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+) {
+ var chars;
- if (suspenseState !== null) {
- retryLane = suspenseState.retryLane;
- }
+ if (canUseTextInputEvent) {
+ chars = getNativeBeforeInputChars(domEventName, nativeEvent);
+ } else {
+ chars = getFallbackBeforeInputChars(domEventName, nativeEvent);
+ } // If no characters are being inserted, no BeforeInput event should
+ // be fired.
- retryTimedOutBoundary(boundaryFiber, retryLane);
-}
-function resolveRetryWakeable(boundaryFiber, wakeable) {
- var retryLane = NoLane; // Default
+ if (!chars) {
+ return null;
+ }
- var retryCache;
+ var listeners = accumulateTwoPhaseListeners(targetInst, "onBeforeInput");
- switch (boundaryFiber.tag) {
- case SuspenseComponent:
- retryCache = boundaryFiber.stateNode;
- var suspenseState = boundaryFiber.memoizedState;
+ if (listeners.length > 0) {
+ var event = new SyntheticInputEvent(
+ "onBeforeInput",
+ "beforeinput",
+ null,
+ nativeEvent,
+ nativeEventTarget
+ );
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ }); // $FlowFixMe[incompatible-use]
- if (suspenseState !== null) {
- retryLane = suspenseState.retryLane;
- }
+ event.data = chars;
+ }
+}
+/**
+ * Create an `onBeforeInput` event to match
+ * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
+ *
+ * This event plugin is based on the native `textInput` event
+ * available in Chrome, Safari, Opera, and IE. This event fires after
+ * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
+ *
+ * `beforeInput` is spec'd but not implemented in any browsers, and
+ * the `input` event does not provide any useful information about what has
+ * actually been added, contrary to the spec. Thus, `textInput` is the best
+ * available event to identify the characters that have actually been inserted
+ * into the target node.
+ *
+ * This plugin is also responsible for emitting `composition` events, thus
+ * allowing us to share composition fallback code for both `beforeInput` and
+ * `composition` event types.
+ */
- break;
+function extractEvents$5(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ extractCompositionEvent(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ extractBeforeInputEvent(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+}
- case SuspenseListComponent:
- retryCache = boundaryFiber.stateNode;
- break;
+/**
+ * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
+ */
+var supportedInputTypes = {
+ color: true,
+ date: true,
+ datetime: true,
+ "datetime-local": true,
+ email: true,
+ month: true,
+ number: true,
+ password: true,
+ range: true,
+ search: true,
+ tel: true,
+ text: true,
+ time: true,
+ url: true,
+ week: true
+};
- case OffscreenComponent: {
- var instance = boundaryFiber.stateNode;
- retryCache = instance._retryCache;
- break;
- }
+function isTextInputElement(elem) {
+ var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
- default:
- throw new Error(
- "Pinged unknown suspense boundary type. " +
- "This is probably a bug in React."
- );
+ if (nodeName === "input") {
+ return !!supportedInputTypes[elem.type];
}
- if (retryCache !== null) {
- // The wakeable resolved, so we no longer need to memoize, because it will
- // never be thrown again.
- retryCache.delete(wakeable);
+ if (nodeName === "textarea") {
+ return true;
}
- retryTimedOutBoundary(boundaryFiber, retryLane);
-} // Computes the next Just Noticeable Difference (JND) boundary.
-// The theory is that a person can't tell the difference between small differences in time.
-// Therefore, if we wait a bit longer than necessary that won't translate to a noticeable
-// difference in the experience. However, waiting for longer might mean that we can avoid
-// showing an intermediate loading state. The longer we have already waited, the harder it
-// is to tell small differences in time. Therefore, the longer we've already waited,
-// the longer we can wait additionally. At some point we have to give up though.
-// We pick a train model where the next boundary commits at a consistent schedule.
-// These particular numbers are vague estimates. We expect to adjust them based on research.
-
-function jnd(timeElapsed) {
- return timeElapsed < 120
- ? 120
- : timeElapsed < 480
- ? 480
- : timeElapsed < 1080
- ? 1080
- : timeElapsed < 1920
- ? 1920
- : timeElapsed < 3000
- ? 3000
- : timeElapsed < 4320
- ? 4320
- : ceil(timeElapsed / 1960) * 1960;
+ return false;
}
-function throwIfInfiniteUpdateLoopDetected() {
- if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
- nestedUpdateCount = 0;
- nestedPassiveUpdateCount = 0;
- rootWithNestedUpdates = null;
- rootWithPassiveNestedUpdates = null;
- throw new Error(
- "Maximum update depth exceeded. This can happen when a component " +
- "repeatedly calls setState inside componentWillUpdate or " +
- "componentDidUpdate. React limits the number of nested updates to " +
- "prevent infinite loops."
- );
+/**
+ * Checks if an event is supported in the current execution environment.
+ *
+ * NOTE: This will not work correctly for non-generic events such as `change`,
+ * `reset`, `load`, `error`, and `select`.
+ *
+ * Borrows from Modernizr.
+ *
+ * @param {string} eventNameSuffix Event name, e.g. "click".
+ * @return {boolean} True if the event is supported.
+ * @internal
+ * @license Modernizr 3.0.0pre (Custom Build) | MIT
+ */
+
+function isEventSupported(eventNameSuffix) {
+ if (!canUseDOM) {
+ return false;
}
- {
- if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
- nestedPassiveUpdateCount = 0;
- rootWithPassiveNestedUpdates = null;
+ var eventName = "on" + eventNameSuffix;
+ var isSupported = eventName in document;
- error(
- "Maximum update depth exceeded. This can happen when a component " +
- "calls setState inside useEffect, but useEffect either doesn't " +
- "have a dependency array, or one of the dependencies changes on " +
- "every render."
- );
- }
+ if (!isSupported) {
+ var element = document.createElement("div");
+ element.setAttribute(eventName, "return;");
+ isSupported = typeof element[eventName] === "function";
}
-}
-function flushRenderPhaseStrictModeWarningsInDEV() {
- {
- ReactStrictModeWarnings.flushLegacyContextWarning();
- ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
- }
+ return isSupported;
}
-function commitDoubleInvokeEffectsInDEV(root, hasPassiveEffects) {
- {
- {
- legacyCommitDoubleInvokeEffectsInDEV(root.current, hasPassiveEffects);
- }
- }
+function registerEvents$2() {
+ registerTwoPhaseEvent("onChange", [
+ "change",
+ "click",
+ "focusin",
+ "focusout",
+ "input",
+ "keydown",
+ "keyup",
+ "selectionchange"
+ ]);
}
-function legacyCommitDoubleInvokeEffectsInDEV(fiber, hasPassiveEffects) {
- // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
- // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
- // Maybe not a big deal since this is DEV only behavior.
- setCurrentFiber(fiber);
- invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
+function createAndAccumulateChangeEvent(
+ dispatchQueue,
+ inst,
+ nativeEvent,
+ target
+) {
+ // Flag this event loop as needing state restore.
+ enqueueStateRestore(target);
+ var listeners = accumulateTwoPhaseListeners(inst, "onChange");
- if (hasPassiveEffects) {
- invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
+ if (listeners.length > 0) {
+ var event = new SyntheticEvent(
+ "onChange",
+ "change",
+ null,
+ nativeEvent,
+ target
+ );
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ });
}
+}
+/**
+ * For IE shims
+ */
- invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
-
- if (hasPassiveEffects) {
- invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
- }
+var activeElement$1 = null;
+var activeElementInst$1 = null;
+/**
+ * SECTION: handle `change` event
+ */
- resetCurrentFiber();
+function shouldUseChangeEvent(elem) {
+ var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
+ return (
+ nodeName === "select" || (nodeName === "input" && elem.type === "file")
+ );
}
-function invokeEffectsInDev(firstChild, fiberFlags, invokeEffectFn) {
- var current = firstChild;
- var subtreeRoot = null;
+function manualDispatchChangeEvent(nativeEvent) {
+ var dispatchQueue = [];
+ createAndAccumulateChangeEvent(
+ dispatchQueue,
+ activeElementInst$1,
+ nativeEvent,
+ getEventTarget(nativeEvent)
+ ); // If change and propertychange bubbled, we'd just bind to it like all the
+ // other events and have it go through ReactBrowserEventEmitter. Since it
+ // doesn't, we manually listen for the events and so we have to enqueue and
+ // process the abstract event manually.
+ //
+ // Batching is necessary here in order to ensure that all event handlers run
+ // before the next rerender (including event handlers attached to ancestor
+ // elements instead of directly on the input). Without this, controlled
+ // components don't work properly in conjunction with event bubbling because
+ // the component is rerendered and the value reverted before all the event
+ // handlers can run. See https://github.com/facebook/react/issues/708.
- while (current != null) {
- var primarySubtreeFlag = current.subtreeFlags & fiberFlags;
+ batchedUpdates(runEventInBatch, dispatchQueue);
+}
- if (
- current !== subtreeRoot &&
- current.child != null &&
- primarySubtreeFlag !== NoFlags$1
- ) {
- current = current.child;
- } else {
- if ((current.flags & fiberFlags) !== NoFlags$1) {
- invokeEffectFn(current);
- }
+function runEventInBatch(dispatchQueue) {
+ processDispatchQueue(dispatchQueue, 0);
+}
- if (current.sibling !== null) {
- current = current.sibling;
- } else {
- current = subtreeRoot = current.return;
- }
- }
+function getInstIfValueChanged(targetInst) {
+ var targetNode = getNodeFromInstance(targetInst);
+
+ if (updateValueIfChanged(targetNode)) {
+ return targetInst;
}
}
-var didWarnStateUpdateForNotYetMountedComponent = null;
-function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
- {
- if ((executionContext & RenderContext) !== NoContext) {
- // We let the other warning about render phase updates deal with this one.
- return;
- }
+function getTargetInstForChangeEvent(domEventName, targetInst) {
+ if (domEventName === "change") {
+ return targetInst;
+ }
+}
+/**
+ * SECTION: handle `input` event
+ */
- if (!(fiber.mode & ConcurrentMode)) {
- return;
- }
+var isInputEventSupported = false;
- var tag = fiber.tag;
+if (canUseDOM) {
+ // IE9 claims to support the input event but fails to trigger it when
+ // deleting text, so we ignore its input events.
+ isInputEventSupported =
+ isEventSupported("input") &&
+ (!document.documentMode || document.documentMode > 9);
+}
+/**
+ * (For IE <=9) Starts tracking propertychange events on the passed-in element
+ * and override the value property so that we can distinguish user events from
+ * value changes in JS.
+ */
- if (
- tag !== IndeterminateComponent &&
- tag !== HostRoot &&
- tag !== ClassComponent &&
- tag !== FunctionComponent &&
- tag !== ForwardRef &&
- tag !== MemoComponent &&
- tag !== SimpleMemoComponent
- ) {
- // Only warn for user-defined components, not internal ones like Suspense.
- return;
- } // We show the whole stack but dedupe on the top component's name because
- // the problematic code almost always lies inside that component.
+function startWatchingForValueChange(target, targetInst) {
+ activeElement$1 = target;
+ activeElementInst$1 = targetInst;
+ activeElement$1.attachEvent("onpropertychange", handlePropertyChange);
+}
+/**
+ * (For IE <=9) Removes the event listeners from the currently-tracked element,
+ * if any exists.
+ */
- var componentName = getComponentNameFromFiber(fiber) || "ReactComponent";
+function stopWatchingForValueChange() {
+ if (!activeElement$1) {
+ return;
+ }
- if (didWarnStateUpdateForNotYetMountedComponent !== null) {
- if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {
- return;
- } // $FlowFixMe[incompatible-use] found when upgrading Flow
+ activeElement$1.detachEvent("onpropertychange", handlePropertyChange);
+ activeElement$1 = null;
+ activeElementInst$1 = null;
+}
+/**
+ * (For IE <=9) Handles a propertychange event, sending a `change` event if
+ * the value of the active element has changed.
+ */
+// $FlowFixMe[missing-local-annot]
- didWarnStateUpdateForNotYetMountedComponent.add(componentName);
- } else {
- didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);
- }
+function handlePropertyChange(nativeEvent) {
+ if (nativeEvent.propertyName !== "value") {
+ return;
+ }
- var previousFiber = current;
+ if (getInstIfValueChanged(activeElementInst$1)) {
+ manualDispatchChangeEvent(nativeEvent);
+ }
+}
- try {
- setCurrentFiber(fiber);
+function handleEventsForInputEventPolyfill(domEventName, target, targetInst) {
+ if (domEventName === "focusin") {
+ // In IE9, propertychange fires for most input events but is buggy and
+ // doesn't fire when text is deleted, but conveniently, selectionchange
+ // appears to fire in all of the remaining cases so we catch those and
+ // forward the event if the value has changed
+ // In either case, we don't want to call the event handler if the value
+ // is changed from JS so we redefine a setter for `.value` that updates
+ // our activeElementValue variable, allowing us to ignore those changes
+ //
+ // stopWatching() should be a noop here but we call it just in case we
+ // missed a blur event somehow.
+ stopWatchingForValueChange();
+ startWatchingForValueChange(target, targetInst);
+ } else if (domEventName === "focusout") {
+ stopWatchingForValueChange();
+ }
+} // For IE8 and IE9.
- error(
- "Can't perform a React state update on a component that hasn't mounted yet. " +
- "This indicates that you have a side-effect in your render function that " +
- "asynchronously later calls tries to update the component. Move this work to " +
- "useEffect instead."
- );
- } finally {
- if (previousFiber) {
- setCurrentFiber(fiber);
- } else {
- resetCurrentFiber();
- }
- }
+function getTargetInstForInputEventPolyfill(domEventName, targetInst) {
+ if (
+ domEventName === "selectionchange" ||
+ domEventName === "keyup" ||
+ domEventName === "keydown"
+ ) {
+ // On the selectionchange event, the target is just document which isn't
+ // helpful for us so just check activeElement instead.
+ //
+ // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
+ // propertychange on the first input event after setting `value` from a
+ // script and fires only keydown, keypress, keyup. Catching keyup usually
+ // gets it and catching keydown lets us fire an event for the first
+ // keystroke if user does a key repeat (it'll be a little delayed: right
+ // before the second keystroke). Other input methods (e.g., paste) seem to
+ // fire selectionchange normally.
+ return getInstIfValueChanged(activeElementInst$1);
}
}
-var beginWork;
+/**
+ * SECTION: handle `click` event
+ */
-if (replayFailedUnitOfWorkWithInvokeGuardedCallback) {
- var dummyFiber = null;
+function shouldUseClickEvent(elem) {
+ // Use the `click` event to detect changes to checkbox and radio inputs.
+ // This approach works across all browsers, whereas `change` does not fire
+ // until `blur` in IE8.
+ var nodeName = elem.nodeName;
+ return (
+ nodeName &&
+ nodeName.toLowerCase() === "input" &&
+ (elem.type === "checkbox" || elem.type === "radio")
+ );
+}
- beginWork = function (current, unitOfWork, lanes) {
- // If a component throws an error, we replay it again in a synchronously
- // dispatched event, so that the debugger will treat it as an uncaught
- // error See ReactErrorUtils for more information.
- // Before entering the begin phase, copy the work-in-progress onto a dummy
- // fiber. If beginWork throws, we'll use this to reset the state.
- var originalWorkInProgressCopy = assignFiberPropertiesInDEV(
- dummyFiber,
- unitOfWork
- );
+function getTargetInstForClickEvent(domEventName, targetInst) {
+ if (domEventName === "click") {
+ return getInstIfValueChanged(targetInst);
+ }
+}
- try {
- return beginWork$1(current, unitOfWork, lanes);
- } catch (originalError) {
- if (
- didSuspendOrErrorWhileHydratingDEV() ||
- originalError === SuspenseException ||
- originalError === SelectiveHydrationException ||
- (originalError !== null &&
- typeof originalError === "object" &&
- typeof originalError.then === "function")
- ) {
- // Don't replay promises.
- // Don't replay errors if we are hydrating and have already suspended or handled an error
- throw originalError;
- } // Don't reset current debug fiber, since we're about to work on the
- // same fiber again.
- // Unwind the failed stack frame
+function getTargetInstForInputOrChangeEvent(domEventName, targetInst) {
+ if (domEventName === "input" || domEventName === "change") {
+ return getInstIfValueChanged(targetInst);
+ }
+}
- resetSuspendedWorkLoopOnUnwind();
- unwindInterruptedWork(current, unitOfWork); // Restore the original properties of the fiber.
+function handleControlledInputBlur(node) {
+ var state = node._wrapperState;
- assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);
+ if (!state || !state.controlled || node.type !== "number") {
+ return;
+ }
- if (unitOfWork.mode & ProfileMode) {
- // Reset the profiler timer.
- startProfilerTimer(unitOfWork);
- } // Run beginWork again.
+ if (!disableInputAttributeSyncing) {
+ // If controlled, assign the value attribute to the current value on blur
+ setDefaultValue(node, "number", node.value);
+ }
+}
+/**
+ * This plugin creates an `onChange` event that normalizes change events
+ * across form elements. This event fires at a time when it's possible to
+ * change the element's value without seeing a flicker.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - select
+ */
- invokeGuardedCallback(
- null,
- beginWork$1,
- null,
- current,
- unitOfWork,
- lanes
- );
+function extractEvents$4(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
+ var getTargetInstFunc, handleEventFunc;
- if (hasCaughtError()) {
- var replayError = clearCaughtError();
+ if (shouldUseChangeEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForChangeEvent;
+ } else if (isTextInputElement(targetNode)) {
+ if (isInputEventSupported) {
+ getTargetInstFunc = getTargetInstForInputOrChangeEvent;
+ } else {
+ getTargetInstFunc = getTargetInstForInputEventPolyfill;
+ handleEventFunc = handleEventsForInputEventPolyfill;
+ }
+ } else if (shouldUseClickEvent(targetNode)) {
+ getTargetInstFunc = getTargetInstForClickEvent;
+ } else if (
+ enableCustomElementPropertySupport &&
+ targetInst &&
+ isCustomComponent(targetInst.elementType, targetInst.memoizedProps)
+ ) {
+ getTargetInstFunc = getTargetInstForChangeEvent;
+ }
- if (
- typeof replayError === "object" &&
- replayError !== null &&
- replayError._suppressLogging &&
- typeof originalError === "object" &&
- originalError !== null &&
- !originalError._suppressLogging
- ) {
- // If suppressed, let the flag carry over to the original error which is the one we'll rethrow.
- originalError._suppressLogging = true;
- }
- } // We always throw the original error in case the second render pass is not idempotent.
- // This can happen if a memoized function or CommonJS module doesn't throw after first invocation.
+ if (getTargetInstFunc) {
+ var inst = getTargetInstFunc(domEventName, targetInst);
- throw originalError;
+ if (inst) {
+ createAndAccumulateChangeEvent(
+ dispatchQueue,
+ inst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ return;
}
- };
-} else {
- beginWork = beginWork$1;
-}
+ }
-var didWarnAboutUpdateInRender = false;
-var didWarnAboutUpdateInRenderForAnotherComponent;
+ if (handleEventFunc) {
+ handleEventFunc(domEventName, targetNode, targetInst);
+ } // When blurring, set the value attribute for number inputs
-{
- didWarnAboutUpdateInRenderForAnotherComponent = new Set();
+ if (domEventName === "focusout") {
+ handleControlledInputBlur(targetNode);
+ }
}
-function warnAboutRenderPhaseUpdatesInDEV(fiber) {
- {
- if (isRendering) {
- switch (fiber.tag) {
- case FunctionComponent:
- case ForwardRef:
- case SimpleMemoComponent: {
- var renderingComponentName =
- (workInProgress && getComponentNameFromFiber(workInProgress)) ||
- "Unknown"; // Dedupe by the rendering component because it's the one that needs to be fixed.
-
- var dedupeKey = renderingComponentName;
-
- if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
- didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
- var setStateComponentName =
- getComponentNameFromFiber(fiber) || "Unknown";
-
- error(
- "Cannot update a component (`%s`) while rendering a " +
- "different component (`%s`). To locate the bad setState() call inside `%s`, " +
- "follow the stack trace as described in https://reactjs.org/link/setstate-in-render",
- setStateComponentName,
- renderingComponentName,
- renderingComponentName
- );
- }
-
- break;
- }
+function registerEvents$1() {
+ registerDirectEvent("onMouseEnter", ["mouseout", "mouseover"]);
+ registerDirectEvent("onMouseLeave", ["mouseout", "mouseover"]);
+ registerDirectEvent("onPointerEnter", ["pointerout", "pointerover"]);
+ registerDirectEvent("onPointerLeave", ["pointerout", "pointerover"]);
+}
+/**
+ * For almost every interaction we care about, there will be both a top-level
+ * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
+ * we do not extract duplicate events. However, moving the mouse into the
+ * browser from outside will not fire a `mouseout` event. In this case, we use
+ * the `mouseover` top-level event.
+ */
- case ClassComponent: {
- if (!didWarnAboutUpdateInRender) {
- error(
- "Cannot update during an existing state transition (such as " +
- "within `render`). Render methods should be a pure " +
- "function of props and state."
- );
+function extractEvents$3(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ var isOverEvent =
+ domEventName === "mouseover" || domEventName === "pointerover";
+ var isOutEvent = domEventName === "mouseout" || domEventName === "pointerout";
- didWarnAboutUpdateInRender = true;
- }
+ if (isOverEvent && !isReplayingEvent(nativeEvent)) {
+ // If this is an over event with a target, we might have already dispatched
+ // the event in the out event of the other target. If this is replayed,
+ // then it's because we couldn't dispatch against this target previously
+ // so we have to do it now instead.
+ var related = nativeEvent.relatedTarget || nativeEvent.fromElement;
- break;
- }
+ if (related) {
+ // If the related node is managed by React, we can assume that we have
+ // already dispatched the corresponding events during its mouseout.
+ if (
+ getClosestInstanceFromNode(related) ||
+ isContainerMarkedAsRoot(related)
+ ) {
+ return;
}
}
}
-}
-function restorePendingUpdaters(root, lanes) {
- {
- if (isDevToolsPresent) {
- var memoizedUpdaters = root.memoizedUpdaters;
- memoizedUpdaters.forEach(function (schedulingFiber) {
- addFiberToLanesMap(root, schedulingFiber, lanes);
- }); // This function intentionally does not clear memoized updaters.
- // Those may still be relevant to the current commit
- // and a future one (e.g. Suspense).
- }
+ if (!isOutEvent && !isOverEvent) {
+ // Must not be a mouse or pointer in or out - ignoring.
+ return;
}
-}
-var fakeActCallbackNode = {}; // $FlowFixMe[missing-local-annot]
-function scheduleCallback(priorityLevel, callback) {
- {
- // If we're currently inside an `act` scope, bypass Scheduler and push to
- // the `act` queue instead.
- var actQueue = ReactCurrentActQueue.current;
+ var win; // TODO: why is this nullable in the types but we read from it?
- if (actQueue !== null) {
- actQueue.push(callback);
- return fakeActCallbackNode;
+ if (nativeEventTarget.window === nativeEventTarget) {
+ // `nativeEventTarget` is probably a window object.
+ win = nativeEventTarget;
+ } else {
+ // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
+ var doc = nativeEventTarget.ownerDocument;
+
+ if (doc) {
+ win = doc.defaultView || doc.parentWindow;
} else {
- return scheduleCallback$2(priorityLevel, callback);
+ win = window;
}
}
-}
-
-function cancelCallback(callbackNode) {
- if (callbackNode === fakeActCallbackNode) {
- return;
- } // In production, always call Scheduler. This function will be stripped out.
- return cancelCallback$1(callbackNode);
-}
+ var from;
+ var to;
-function shouldForceFlushFallbacksInDEV() {
- // Never force flush in production. This function should get stripped out.
- return ReactCurrentActQueue.current !== null;
-}
+ if (isOutEvent) {
+ var _related = nativeEvent.relatedTarget || nativeEvent.toElement;
-function warnIfUpdatesNotWrappedWithActDEV(fiber) {
- {
- if (fiber.mode & ConcurrentMode) {
- if (!isConcurrentActEnvironment()) {
- // Not in an act environment. No need to warn.
- return;
- }
- } else {
- // Legacy mode has additional cases where we suppress a warning.
- if (!isLegacyActEnvironment()) {
- // Not in an act environment. No need to warn.
- return;
- }
+ from = targetInst;
+ to = _related ? getClosestInstanceFromNode(_related) : null;
- if (executionContext !== NoContext) {
- // Legacy mode doesn't warn if the update is batched, i.e.
- // batchedUpdates or flushSync.
- return;
- }
+ if (to !== null) {
+ var nearestMounted = getNearestMountedFiber(to);
+ var tag = to.tag;
if (
- fiber.tag !== FunctionComponent &&
- fiber.tag !== ForwardRef &&
- fiber.tag !== SimpleMemoComponent
+ to !== nearestMounted ||
+ (tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
) {
- // For backwards compatibility with pre-hooks code, legacy mode only
- // warns for updates that originate from a hook.
- return;
+ to = null;
}
}
+ } else {
+ // Moving to a node from outside the window.
+ from = null;
+ to = targetInst;
+ }
- if (ReactCurrentActQueue.current === null) {
- var previousFiber = current;
+ if (from === to) {
+ // Nothing pertains to our managed components.
+ return;
+ }
- try {
- setCurrentFiber(fiber);
+ var SyntheticEventCtor = SyntheticMouseEvent;
+ var leaveEventType = "onMouseLeave";
+ var enterEventType = "onMouseEnter";
+ var eventTypePrefix = "mouse";
- error(
- "An update to %s inside a test was not wrapped in act(...).\n\n" +
- "When testing, code that causes React state updates should be " +
- "wrapped into act(...):\n\n" +
- "act(() => {\n" +
- " /* fire events that update state */\n" +
- "});\n" +
- "/* assert on the output */\n\n" +
- "This ensures that you're testing the behavior the user would see " +
- "in the browser." +
- " Learn more at https://reactjs.org/link/wrap-tests-with-act",
- getComponentNameFromFiber(fiber)
- );
- } finally {
- if (previousFiber) {
- setCurrentFiber(fiber);
- } else {
- resetCurrentFiber();
- }
- }
- }
+ if (domEventName === "pointerout" || domEventName === "pointerover") {
+ SyntheticEventCtor = SyntheticPointerEvent;
+ leaveEventType = "onPointerLeave";
+ enterEventType = "onPointerEnter";
+ eventTypePrefix = "pointer";
}
-}
-function warnIfSuspenseResolutionNotWrappedWithActDEV(root) {
- {
- if (
- root.tag !== LegacyRoot &&
- isConcurrentActEnvironment() &&
- ReactCurrentActQueue.current === null
- ) {
- error(
- "A suspended resource finished loading inside a test, but the event " +
- "was not wrapped in act(...).\n\n" +
- "When testing, code that resolves suspended data should be wrapped " +
- "into act(...):\n\n" +
- "act(() => {\n" +
- " /* finish loading suspended data */\n" +
- "});\n" +
- "/* assert on the output */\n\n" +
- "This ensures that you're testing the behavior the user would see " +
- "in the browser." +
- " Learn more at https://reactjs.org/link/wrap-tests-with-act"
- );
- }
+ var fromNode = from == null ? win : getNodeFromInstance(from);
+ var toNode = to == null ? win : getNodeFromInstance(to);
+ var leave = new SyntheticEventCtor(
+ leaveEventType,
+ eventTypePrefix + "leave",
+ from,
+ nativeEvent,
+ nativeEventTarget
+ );
+ leave.target = fromNode;
+ leave.relatedTarget = toNode;
+ var enter = null; // We should only process this nativeEvent if we are processing
+ // the first ancestor. Next time, we will ignore the event.
+
+ var nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget);
+
+ if (nativeTargetInst === targetInst) {
+ var enterEvent = new SyntheticEventCtor(
+ enterEventType,
+ eventTypePrefix + "enter",
+ to,
+ nativeEvent,
+ nativeEventTarget
+ );
+ enterEvent.target = toNode;
+ enterEvent.relatedTarget = fromNode;
+ enter = enterEvent;
}
+
+ accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}
-function setIsRunningInsertionEffect(isRunning) {
- {
- isRunningInsertionEffect = isRunning;
- }
+var skipSelectionChangeEvent =
+ canUseDOM && "documentMode" in document && document.documentMode <= 11;
+
+function registerEvents() {
+ registerTwoPhaseEvent("onSelect", [
+ "focusout",
+ "contextmenu",
+ "dragend",
+ "focusin",
+ "keydown",
+ "keyup",
+ "mousedown",
+ "mouseup",
+ "selectionchange"
+ ]);
}
-/* eslint-disable react-internal/prod-error-codes */
-// Used by React Refresh runtime through DevTools Global Hook.
+var activeElement = null;
+var activeElementInst = null;
+var lastSelection = null;
+var mouseDown = false;
+/**
+ * Get an object which is a unique representation of the current selection.
+ *
+ * The return value will not be consistent across nodes or browsers, but
+ * two identical selections on the same node will return identical objects.
+ */
-var resolveFamily = null;
-var failedBoundaries = null;
-var setRefreshHandler = function (handler) {
- {
- resolveFamily = handler;
+function getSelection(node) {
+ if ("selectionStart" in node && hasSelectionCapabilities(node)) {
+ return {
+ start: node.selectionStart,
+ end: node.selectionEnd
+ };
+ } else {
+ var win = (node.ownerDocument && node.ownerDocument.defaultView) || window;
+ var selection = win.getSelection();
+ return {
+ anchorNode: selection.anchorNode,
+ anchorOffset: selection.anchorOffset,
+ focusNode: selection.focusNode,
+ focusOffset: selection.focusOffset
+ };
}
-};
-function resolveFunctionForHotReloading(type) {
- {
- if (resolveFamily === null) {
- // Hot reloading is disabled.
- return type;
- }
+}
+/**
+ * Get document associated with the event target.
+ */
- var family = resolveFamily(type);
+function getEventTargetDocument(eventTarget) {
+ return eventTarget.window === eventTarget
+ ? eventTarget.document
+ : eventTarget.nodeType === DOCUMENT_NODE
+ ? eventTarget
+ : eventTarget.ownerDocument;
+}
+/**
+ * Poll selection to see whether it's changed.
+ *
+ * @param {object} nativeEvent
+ * @param {object} nativeEventTarget
+ * @return {?SyntheticEvent}
+ */
- if (family === undefined) {
- return type;
- } // Use the latest known implementation.
+function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
+ // Ensure we have the right element, and that the user is not dragging a
+ // selection (this matches native `select` event behavior). In HTML5, select
+ // fires only on input and textarea thus if there's no focused element we
+ // won't dispatch.
+ var doc = getEventTargetDocument(nativeEventTarget);
+
+ if (
+ mouseDown ||
+ activeElement == null ||
+ activeElement !== getActiveElement(doc)
+ ) {
+ return;
+ } // Only fire when selection has actually changed.
+
+ var currentSelection = getSelection(activeElement);
- return family.current;
+ if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
+ lastSelection = currentSelection;
+ var listeners = accumulateTwoPhaseListeners(activeElementInst, "onSelect");
+
+ if (listeners.length > 0) {
+ var event = new SyntheticEvent(
+ "onSelect",
+ "select",
+ null,
+ nativeEvent,
+ nativeEventTarget
+ );
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ });
+ event.target = activeElement;
+ }
}
}
-function resolveClassForHotReloading(type) {
- // No implementation differences.
- return resolveFunctionForHotReloading(type);
-}
-function resolveForwardRefForHotReloading(type) {
- {
- if (resolveFamily === null) {
- // Hot reloading is disabled.
- return type;
- }
+/**
+ * This plugin creates an `onSelect` event that normalizes select events
+ * across form elements.
+ *
+ * Supported elements are:
+ * - input (see `isTextInputElement`)
+ * - textarea
+ * - contentEditable
+ *
+ * This differs from native browser implementations in the following ways:
+ * - Fires on contentEditable fields as well as inputs.
+ * - Fires for collapsed selection.
+ * - Fires after user input.
+ */
- var family = resolveFamily(type);
+function extractEvents$2(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
- if (family === undefined) {
- // Check if we're dealing with a real forwardRef. Don't want to crash early.
+ switch (domEventName) {
+ // Track the input node that has focus.
+ case "focusin":
if (
- type !== null &&
- type !== undefined &&
- typeof type.render === "function"
+ isTextInputElement(targetNode) ||
+ targetNode.contentEditable === "true"
) {
- // ForwardRef is special because its resolved .type is an object,
- // but it's possible that we only have its inner render function in the map.
- // If that inner render function is different, we'll build a new forwardRef type.
- var currentRender = resolveFunctionForHotReloading(type.render);
-
- if (type.render !== currentRender) {
- var syntheticType = {
- $$typeof: REACT_FORWARD_REF_TYPE,
- render: currentRender
- };
-
- if (type.displayName !== undefined) {
- syntheticType.displayName = type.displayName;
- }
-
- return syntheticType;
- }
+ activeElement = targetNode;
+ activeElementInst = targetInst;
+ lastSelection = null;
}
- return type;
- } // Use the latest known implementation.
-
- return family.current;
- }
-}
-function isCompatibleFamilyForHotReloading(fiber, element) {
- {
- if (resolveFamily === null) {
- // Hot reloading is disabled.
- return false;
- }
+ break;
- var prevType = fiber.elementType;
- var nextType = element.type; // If we got here, we know types aren't === equal.
+ case "focusout":
+ activeElement = null;
+ activeElementInst = null;
+ lastSelection = null;
+ break;
+ // Don't fire the event while the user is dragging. This matches the
+ // semantics of the native select event.
- var needsCompareFamilies = false;
- var $$typeofNextType =
- typeof nextType === "object" && nextType !== null
- ? nextType.$$typeof
- : null;
+ case "mousedown":
+ mouseDown = true;
+ break;
- switch (fiber.tag) {
- case ClassComponent: {
- if (typeof nextType === "function") {
- needsCompareFamilies = true;
- }
+ case "contextmenu":
+ case "mouseup":
+ case "dragend":
+ mouseDown = false;
+ constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
+ break;
+ // Chrome and IE fire non-standard event when selection is changed (and
+ // sometimes when it hasn't). IE's event fires out of order with respect
+ // to key and input events on deletion, so we discard it.
+ //
+ // Firefox doesn't support selectionchange, so check selection status
+ // after each key entry. The selection changes after keydown and before
+ // keyup, but we check on keydown as well in the case of holding down a
+ // key, when multiple keydown events are fired but only one keyup is.
+ // This is also our approach for IE handling, for the reason above.
+ case "selectionchange":
+ if (skipSelectionChangeEvent) {
break;
}
- case FunctionComponent: {
- if (typeof nextType === "function") {
- needsCompareFamilies = true;
- } else if ($$typeofNextType === REACT_LAZY_TYPE) {
- // We don't know the inner type yet.
- // We're going to assume that the lazy inner type is stable,
- // and so it is sufficient to avoid reconciling it away.
- // We're not going to unwrap or actually use the new lazy type.
- needsCompareFamilies = true;
- }
+ // falls through
- break;
- }
+ case "keydown":
+ case "keyup":
+ constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
+ }
+}
- case ForwardRef: {
- if ($$typeofNextType === REACT_FORWARD_REF_TYPE) {
- needsCompareFamilies = true;
- } else if ($$typeofNextType === REACT_LAZY_TYPE) {
- needsCompareFamilies = true;
- }
+/**
+ * Generate a mapping of standard vendor prefixes using the defined style property and event name.
+ *
+ * @param {string} styleProp
+ * @param {string} eventName
+ * @returns {object}
+ */
- break;
- }
+function makePrefixMap(styleProp, eventName) {
+ var prefixes = {};
+ prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
+ prefixes["Webkit" + styleProp] = "webkit" + eventName;
+ prefixes["Moz" + styleProp] = "moz" + eventName;
+ return prefixes;
+}
+/**
+ * A list of event names to a configurable list of vendor prefixes.
+ */
- case MemoComponent:
- case SimpleMemoComponent: {
- if ($$typeofNextType === REACT_MEMO_TYPE) {
- // TODO: if it was but can no longer be simple,
- // we shouldn't set this.
- needsCompareFamilies = true;
- } else if ($$typeofNextType === REACT_LAZY_TYPE) {
- needsCompareFamilies = true;
- }
+var vendorPrefixes = {
+ animationend: makePrefixMap("Animation", "AnimationEnd"),
+ animationiteration: makePrefixMap("Animation", "AnimationIteration"),
+ animationstart: makePrefixMap("Animation", "AnimationStart"),
+ transitionend: makePrefixMap("Transition", "TransitionEnd")
+};
+/**
+ * Event names that have already been detected and prefixed (if applicable).
+ */
- break;
- }
+var prefixedEventNames = {};
+/**
+ * Element to check for prefixes on.
+ */
- default:
- return false;
- } // Check if both types have a family and it's the same one.
+var style = {};
+/**
+ * Bootstrap if a DOM exists.
+ */
- if (needsCompareFamilies) {
- // Note: memo() and forwardRef() we'll compare outer rather than inner type.
- // This means both of them need to be registered to preserve state.
- // If we unwrapped and compared the inner types for wrappers instead,
- // then we would risk falsely saying two separate memo(Foo)
- // calls are equivalent because they wrap the same Foo function.
- var prevFamily = resolveFamily(prevType); // $FlowFixMe[not-a-function] found when upgrading Flow
+if (canUseDOM) {
+ style = document.createElement("div").style; // On some platforms, in particular some releases of Android 4.x,
+ // the un-prefixed "animation" and "transition" properties are defined on the
+ // style object but the events that fire will still be prefixed, so we need
+ // to check if the un-prefixed events are usable, and if not remove them from the map.
- if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) {
- return true;
- }
- }
+ if (!("AnimationEvent" in window)) {
+ delete vendorPrefixes.animationend.animation;
+ delete vendorPrefixes.animationiteration.animation;
+ delete vendorPrefixes.animationstart.animation;
+ } // Same as above
- return false;
+ if (!("TransitionEvent" in window)) {
+ delete vendorPrefixes.transitionend.transition;
}
}
-function markFailedErrorBoundaryForHotReloading(fiber) {
- {
- if (resolveFamily === null) {
- // Hot reloading is disabled.
- return;
- }
+/**
+ * Attempts to determine the correct vendor prefixed event name.
+ *
+ * @param {string} eventName
+ * @returns {string}
+ */
- if (typeof WeakSet !== "function") {
- return;
- }
+function getVendorPrefixedEventName(eventName) {
+ if (prefixedEventNames[eventName]) {
+ return prefixedEventNames[eventName];
+ } else if (!vendorPrefixes[eventName]) {
+ return eventName;
+ }
- if (failedBoundaries === null) {
- failedBoundaries = new WeakSet();
- }
+ var prefixMap = vendorPrefixes[eventName];
- failedBoundaries.add(fiber);
- }
-}
-var scheduleRefresh = function (root, update) {
- {
- if (resolveFamily === null) {
- // Hot reloading is disabled.
- return;
+ for (var styleProp in prefixMap) {
+ if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
+ return (prefixedEventNames[eventName] = prefixMap[styleProp]);
}
-
- var staleFamilies = update.staleFamilies,
- updatedFamilies = update.updatedFamilies;
- flushPassiveEffects();
- flushSync$1(function () {
- scheduleFibersWithFamiliesRecursively(
- root.current,
- updatedFamilies,
- staleFamilies
- );
- });
}
-};
-var scheduleRoot = function (root, element) {
- {
- if (root.context !== emptyContextObject) {
- // Super edge case: root has a legacy _renderSubtree context
- // but we don't know the parentComponent so we can't pass it.
- // Just ignore. We'll delete this with _renderSubtree code path later.
- return;
- }
- flushPassiveEffects();
- flushSync$1(function () {
- updateContainer(element, root, null, null);
- });
- }
-};
+ return eventName;
+}
-function scheduleFibersWithFamiliesRecursively(
- fiber,
- updatedFamilies,
- staleFamilies
-) {
- {
- var alternate = fiber.alternate,
- child = fiber.child,
- sibling = fiber.sibling,
- tag = fiber.tag,
- type = fiber.type;
- var candidateType = null;
+var ANIMATION_END = getVendorPrefixedEventName("animationend");
+var ANIMATION_ITERATION = getVendorPrefixedEventName("animationiteration");
+var ANIMATION_START = getVendorPrefixedEventName("animationstart");
+var TRANSITION_END = getVendorPrefixedEventName("transitionend");
- switch (tag) {
- case FunctionComponent:
- case SimpleMemoComponent:
- case ClassComponent:
- candidateType = type;
- break;
+var topLevelEventsToReactNames = new Map(); // NOTE: Capitalization is important in this list!
+//
+// E.g. it needs "pointerDown", not "pointerdown".
+// This is because we derive both React name ("onPointerDown")
+// and DOM name ("pointerdown") from the same list.
+//
+// Exceptions that don't match this convention are listed separately.
+//
+// prettier-ignore
- case ForwardRef:
- candidateType = type.render;
- break;
- }
+var simpleEventPluginEvents = ['abort', 'auxClick', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'gotPointerCapture', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'lostPointerCapture', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'pointerCancel', 'pointerDown', 'pointerMove', 'pointerOut', 'pointerOver', 'pointerUp', 'progress', 'rateChange', 'reset', 'resize', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'touchCancel', 'touchEnd', 'touchStart', 'volumeChange', 'scroll', 'toggle', 'touchMove', 'waiting', 'wheel'];
- if (resolveFamily === null) {
- throw new Error("Expected resolveFamily to be set during hot reload.");
- }
+{
+ // Special case: these two events don't have on* React handler
+ // and are only accessible via the createEventHandle API.
+ topLevelEventsToReactNames.set("beforeblur", null);
+ topLevelEventsToReactNames.set("afterblur", null);
+}
- var needsRender = false;
- var needsRemount = false;
+function registerSimpleEvent(domEventName, reactName) {
+ topLevelEventsToReactNames.set(domEventName, reactName);
+ registerTwoPhaseEvent(reactName, [domEventName]);
+}
- if (candidateType !== null) {
- var family = resolveFamily(candidateType);
+function registerSimpleEvents() {
+ for (var i = 0; i < simpleEventPluginEvents.length; i++) {
+ var eventName = simpleEventPluginEvents[i];
+ var domEventName = eventName.toLowerCase();
+ var capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
+ registerSimpleEvent(domEventName, "on" + capitalizedEvent);
+ } // Special cases where event names don't match.
- if (family !== undefined) {
- if (staleFamilies.has(family)) {
- needsRemount = true;
- } else if (updatedFamilies.has(family)) {
- if (tag === ClassComponent) {
- needsRemount = true;
- } else {
- needsRender = true;
- }
- }
- }
- }
+ registerSimpleEvent(ANIMATION_END, "onAnimationEnd");
+ registerSimpleEvent(ANIMATION_ITERATION, "onAnimationIteration");
+ registerSimpleEvent(ANIMATION_START, "onAnimationStart");
+ registerSimpleEvent("dblclick", "onDoubleClick");
+ registerSimpleEvent("focusin", "onFocus");
+ registerSimpleEvent("focusout", "onBlur");
+ registerSimpleEvent(TRANSITION_END, "onTransitionEnd");
+}
- if (failedBoundaries !== null) {
- if (
- failedBoundaries.has(fiber) || // $FlowFixMe[incompatible-use] found when upgrading Flow
- (alternate !== null && failedBoundaries.has(alternate))
- ) {
- needsRemount = true;
- }
- }
+function extractEvents$1(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ var reactName = topLevelEventsToReactNames.get(domEventName);
- if (needsRemount) {
- fiber._debugNeedsRemount = true;
- }
+ if (reactName === undefined) {
+ return;
+ }
- if (needsRemount || needsRender) {
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ var SyntheticEventCtor = SyntheticEvent;
+ var reactEventType = domEventName;
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ switch (domEventName) {
+ case "keypress":
+ // Firefox creates a keypress event for function keys too. This removes
+ // the unwanted keypress events. Enter is however both printable and
+ // non-printable. One would expect Tab to be as well (but it isn't).
+ if (getEventCharCode(nativeEvent) === 0) {
+ return;
}
- }
-
- if (child !== null && !needsRemount) {
- scheduleFibersWithFamiliesRecursively(
- child,
- updatedFamilies,
- staleFamilies
- );
- }
-
- if (sibling !== null) {
- scheduleFibersWithFamiliesRecursively(
- sibling,
- updatedFamilies,
- staleFamilies
- );
- }
- }
-}
-
-var findHostInstancesForRefresh = function (root, families) {
- {
- var hostInstances = new Set();
- var types = new Set(
- families.map(function (family) {
- return family.current;
- })
- );
- findHostInstancesForMatchingFibersRecursively(
- root.current,
- types,
- hostInstances
- );
- return hostInstances;
- }
-};
-function findHostInstancesForMatchingFibersRecursively(
- fiber,
- types,
- hostInstances
-) {
- {
- var child = fiber.child,
- sibling = fiber.sibling,
- tag = fiber.tag,
- type = fiber.type;
- var candidateType = null;
+ /* falls through */
- switch (tag) {
- case FunctionComponent:
- case SimpleMemoComponent:
- case ClassComponent:
- candidateType = type;
- break;
+ case "keydown":
+ case "keyup":
+ SyntheticEventCtor = SyntheticKeyboardEvent;
+ break;
- case ForwardRef:
- candidateType = type.render;
- break;
- }
+ case "focusin":
+ reactEventType = "focus";
+ SyntheticEventCtor = SyntheticFocusEvent;
+ break;
- var didMatch = false;
+ case "focusout":
+ reactEventType = "blur";
+ SyntheticEventCtor = SyntheticFocusEvent;
+ break;
- if (candidateType !== null) {
- if (types.has(candidateType)) {
- didMatch = true;
- }
- }
+ case "beforeblur":
+ case "afterblur":
+ SyntheticEventCtor = SyntheticFocusEvent;
+ break;
- if (didMatch) {
- // We have a match. This only drills down to the closest host components.
- // There's no need to search deeper because for the purpose of giving
- // visual feedback, "flashing" outermost parent rectangles is sufficient.
- findHostInstancesForFiberShallowly(fiber, hostInstances);
- } else {
- // If there's no match, maybe there will be one further down in the child tree.
- if (child !== null) {
- findHostInstancesForMatchingFibersRecursively(
- child,
- types,
- hostInstances
- );
+ case "click":
+ // Firefox creates a click event on right mouse clicks. This removes the
+ // unwanted click events.
+ if (nativeEvent.button === 2) {
+ return;
}
- }
- if (sibling !== null) {
- findHostInstancesForMatchingFibersRecursively(
- sibling,
- types,
- hostInstances
- );
- }
- }
-}
+ /* falls through */
-function findHostInstancesForFiberShallowly(fiber, hostInstances) {
- {
- var foundHostInstances = findChildHostInstancesForFiberShallowly(
- fiber,
- hostInstances
- );
+ case "auxclick":
+ case "dblclick":
+ case "mousedown":
+ case "mousemove":
+ case "mouseup": // TODO: Disabled elements should not respond to mouse events
- if (foundHostInstances) {
- return;
- } // If we didn't find any host children, fallback to closest host parent.
+ /* falls through */
- var node = fiber;
+ case "mouseout":
+ case "mouseover":
+ case "contextmenu":
+ SyntheticEventCtor = SyntheticMouseEvent;
+ break;
- while (true) {
- switch (node.tag) {
- case HostSingleton:
- case HostComponent:
- hostInstances.add(node.stateNode);
- return;
+ case "drag":
+ case "dragend":
+ case "dragenter":
+ case "dragexit":
+ case "dragleave":
+ case "dragover":
+ case "dragstart":
+ case "drop":
+ SyntheticEventCtor = SyntheticDragEvent;
+ break;
- case HostPortal:
- hostInstances.add(node.stateNode.containerInfo);
- return;
+ case "touchcancel":
+ case "touchend":
+ case "touchmove":
+ case "touchstart":
+ SyntheticEventCtor = SyntheticTouchEvent;
+ break;
- case HostRoot:
- hostInstances.add(node.stateNode.containerInfo);
- return;
- }
+ case ANIMATION_END:
+ case ANIMATION_ITERATION:
+ case ANIMATION_START:
+ SyntheticEventCtor = SyntheticAnimationEvent;
+ break;
- if (node.return === null) {
- throw new Error("Expected to reach root first.");
- }
+ case TRANSITION_END:
+ SyntheticEventCtor = SyntheticTransitionEvent;
+ break;
- node = node.return;
- }
+ case "scroll":
+ SyntheticEventCtor = SyntheticUIEvent;
+ break;
+
+ case "wheel":
+ SyntheticEventCtor = SyntheticWheelEvent;
+ break;
+
+ case "copy":
+ case "cut":
+ case "paste":
+ SyntheticEventCtor = SyntheticClipboardEvent;
+ break;
+
+ case "gotpointercapture":
+ case "lostpointercapture":
+ case "pointercancel":
+ case "pointerdown":
+ case "pointermove":
+ case "pointerout":
+ case "pointerover":
+ case "pointerup":
+ SyntheticEventCtor = SyntheticPointerEvent;
+ break;
}
-}
-function findChildHostInstancesForFiberShallowly(fiber, hostInstances) {
- {
- var node = fiber;
- var foundHostInstances = false;
+ var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
- while (true) {
- if (
- node.tag === HostComponent ||
- node.tag === HostHoistable ||
- node.tag === HostSingleton
- ) {
- // We got a match.
- foundHostInstances = true;
- hostInstances.add(node.stateNode); // There may still be more, so keep searching.
- } else if (node.child !== null) {
- node.child.return = node;
- node = node.child;
- continue;
- }
+ if (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) {
+ var listeners = accumulateEventHandleNonManagedNodeListeners(
+ // TODO: this cast may not make sense for events like
+ // "focus" where React listens to e.g. "focusin".
+ reactEventType,
+ targetContainer,
+ inCapturePhase
+ );
- if (node === fiber) {
- return foundHostInstances;
- }
+ if (listeners.length > 0) {
+ // Intentionally create event lazily.
+ var event = new SyntheticEventCtor(
+ reactName,
+ reactEventType,
+ null,
+ nativeEvent,
+ nativeEventTarget
+ );
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ });
+ }
+ } else {
+ // Some events don't bubble in the browser.
+ // In the past, React has always bubbled them, but this can be surprising.
+ // We're going to try aligning closer to the browser behavior by not bubbling
+ // them in React either. We'll start by not bubbling onScroll, and then expand.
+ var accumulateTargetOnly =
+ !inCapturePhase && // TODO: ideally, we'd eventually add all events from
+ // nonDelegatedEvents list in DOMPluginEventSystem.
+ // Then we can remove this special list.
+ // This is a breaking change that can wait until React 18.
+ domEventName === "scroll";
- while (node.sibling === null) {
- if (node.return === null || node.return === fiber) {
- return foundHostInstances;
- }
+ var _listeners = accumulateSinglePhaseListeners(
+ targetInst,
+ reactName,
+ nativeEvent.type,
+ inCapturePhase,
+ accumulateTargetOnly,
+ nativeEvent
+ );
- node = node.return;
- }
+ if (_listeners.length > 0) {
+ // Intentionally create event lazily.
+ var _event = new SyntheticEventCtor(
+ reactName,
+ reactEventType,
+ null,
+ nativeEvent,
+ nativeEventTarget
+ );
- node.sibling.return = node.return;
- node = node.sibling;
+ dispatchQueue.push({
+ event: _event,
+ listeners: _listeners
+ });
}
}
-
- return false;
}
-var hasBadMapPolyfill;
-
-{
- hasBadMapPolyfill = false;
+registerSimpleEvents();
+registerEvents$1();
+registerEvents$2();
+registerEvents();
+registerEvents$3();
- try {
- var nonExtensibleObject = Object.preventExtensions({});
- /* eslint-disable no-new */
+function extractEvents(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+) {
+ // TODO: we should remove the concept of a "SimpleEventPlugin".
+ // This is the basic functionality of the event system. All
+ // the other plugins are essentially polyfills. So the plugin
+ // should probably be inlined somewhere and have its logic
+ // be core the to event system. This would potentially allow
+ // us to ship builds of React without the polyfilled plugins below.
+ extractEvents$1(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
+ );
+ var shouldProcessPolyfillPlugins =
+ (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the
+ // event's native "bubble" phase, which means that we're
+ // not in the capture phase. That's because we emulate
+ // the capture phase here still. This is a trade-off,
+ // because in an ideal world we would not emulate and use
+ // the phases properly, like we do with the SimpleEvent
+ // plugin. However, the plugins below either expect
+ // emulation (EnterLeave) or use state localized to that
+ // plugin (BeforeInput, Change, Select). The state in
+ // these modules complicates things, as you'll essentially
+ // get the case where the capture phase event might change
+ // state, only for the following bubble event to come in
+ // later and not trigger anything as the state now
+ // invalidates the heuristics of the event plugin. We
+ // could alter all these plugins to work in such ways, but
+ // that might cause other unknown side-effects that we
+ // can't foresee right now.
- new Map([[nonExtensibleObject, null]]);
- new Set([nonExtensibleObject]);
- /* eslint-enable no-new */
- } catch (e) {
- // TODO: Consider warning about bad polyfills
- hasBadMapPolyfill = true;
+ if (shouldProcessPolyfillPlugins) {
+ extractEvents$3(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ extractEvents$4(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ extractEvents$2(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ extractEvents$5(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
}
-}
+} // List of events that need to be individually attached to media elements.
-function FiberNode(tag, pendingProps, key, mode) {
- // Instance
- this.tag = tag;
- this.key = key;
- this.elementType = null;
- this.type = null;
- this.stateNode = null; // Fiber
+var mediaEventTypes = [
+ "abort",
+ "canplay",
+ "canplaythrough",
+ "durationchange",
+ "emptied",
+ "encrypted",
+ "ended",
+ "error",
+ "loadeddata",
+ "loadedmetadata",
+ "loadstart",
+ "pause",
+ "play",
+ "playing",
+ "progress",
+ "ratechange",
+ "resize",
+ "seeked",
+ "seeking",
+ "stalled",
+ "suspend",
+ "timeupdate",
+ "volumechange",
+ "waiting"
+]; // We should not delegate these events to the container, but rather
+// set them on the actual target element itself. This is primarily
+// because these events do not consistently bubble in the DOM.
- this.return = null;
- this.child = null;
- this.sibling = null;
- this.index = 0;
- this.ref = null;
- this.refCleanup = null;
- this.pendingProps = pendingProps;
- this.memoizedProps = null;
- this.updateQueue = null;
- this.memoizedState = null;
- this.dependencies = null;
- this.mode = mode; // Effects
+var nonDelegatedEvents = new Set(
+ ["cancel", "close", "invalid", "load", "scroll", "toggle"].concat(
+ mediaEventTypes
+ )
+);
- this.flags = NoFlags$1;
- this.subtreeFlags = NoFlags$1;
- this.deletions = null;
- this.lanes = NoLanes;
- this.childLanes = NoLanes;
- this.alternate = null;
+function executeDispatch(event, listener, currentTarget) {
+ var type = event.type || "unknown-event";
+ event.currentTarget = currentTarget;
+ invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
+ event.currentTarget = null;
+}
- {
- // Note: The following is done to avoid a v8 performance cliff.
- //
- // Initializing the fields below to smis and later updating them with
- // double values will cause Fibers to end up having separate shapes.
- // This behavior/bug has something to do with Object.preventExtension().
- // Fortunately this only impacts DEV builds.
- // Unfortunately it makes React unusably slow for some applications.
- // To work around this, initialize the fields below with doubles.
- //
- // Learn more about this here:
- // https://github.com/facebook/react/issues/14365
- // https://bugs.chromium.org/p/v8/issues/detail?id=8538
- this.actualDuration = Number.NaN;
- this.actualStartTime = Number.NaN;
- this.selfBaseDuration = Number.NaN;
- this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
- // This won't trigger the performance cliff mentioned above,
- // and it simplifies other profiler code (including DevTools).
+function processDispatchQueueItemsInOrder(
+ event,
+ dispatchListeners,
+ inCapturePhase
+) {
+ var previousInstance;
- this.actualDuration = 0;
- this.actualStartTime = -1;
- this.selfBaseDuration = 0;
- this.treeBaseDuration = 0;
- }
+ if (inCapturePhase) {
+ for (var i = dispatchListeners.length - 1; i >= 0; i--) {
+ var _dispatchListeners$i = dispatchListeners[i],
+ instance = _dispatchListeners$i.instance,
+ currentTarget = _dispatchListeners$i.currentTarget,
+ listener = _dispatchListeners$i.listener;
- {
- // This isn't directly used but is handy for debugging internals:
- this._debugSource = null;
- this._debugOwner = null;
- this._debugNeedsRemount = false;
- this._debugHookTypes = null;
+ if (instance !== previousInstance && event.isPropagationStopped()) {
+ return;
+ }
- if (!hasBadMapPolyfill && typeof Object.preventExtensions === "function") {
- Object.preventExtensions(this);
+ executeDispatch(event, listener, currentTarget);
+ previousInstance = instance;
}
- }
-} // 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.
+ } else {
+ for (var _i = 0; _i < dispatchListeners.length; _i++) {
+ var _dispatchListeners$_i = dispatchListeners[_i],
+ _instance = _dispatchListeners$_i.instance,
+ _currentTarget = _dispatchListeners$_i.currentTarget,
+ _listener = _dispatchListeners$_i.listener;
-function createFiber(tag, pendingProps, key, mode) {
- // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
- return new FiberNode(tag, pendingProps, key, mode);
+ if (_instance !== previousInstance && event.isPropagationStopped()) {
+ return;
+ }
+
+ executeDispatch(event, _listener, _currentTarget);
+ previousInstance = _instance;
+ }
+ }
}
-function shouldConstruct(Component) {
- var prototype = Component.prototype;
- return !!(prototype && prototype.isReactComponent);
+function processDispatchQueue(dispatchQueue, eventSystemFlags) {
+ var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+
+ for (var i = 0; i < dispatchQueue.length; i++) {
+ var _dispatchQueue$i = dispatchQueue[i],
+ event = _dispatchQueue$i.event,
+ listeners = _dispatchQueue$i.listeners;
+ processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); // event system doesn't use pooling.
+ } // This would be a good time to rethrow if any of the event handlers threw.
+
+ rethrowCaughtError();
}
-function isSimpleFunctionComponent(type) {
- return (
- typeof type === "function" &&
- !shouldConstruct(type) &&
- type.defaultProps === undefined
+function dispatchEventsForPlugins(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ targetInst,
+ targetContainer
+) {
+ var nativeEventTarget = getEventTarget(nativeEvent);
+ var dispatchQueue = [];
+ extractEvents(
+ dispatchQueue,
+ domEventName,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget,
+ eventSystemFlags,
+ targetContainer
);
+ processDispatchQueue(dispatchQueue, eventSystemFlags);
}
-function resolveLazyComponentTag(Component) {
- if (typeof Component === "function") {
- return shouldConstruct(Component) ? ClassComponent : FunctionComponent;
- } else if (Component !== undefined && Component !== null) {
- var $$typeof = Component.$$typeof;
- if ($$typeof === REACT_FORWARD_REF_TYPE) {
- return ForwardRef;
- }
-
- if ($$typeof === REACT_MEMO_TYPE) {
- return MemoComponent;
+function listenToNonDelegatedEvent(domEventName, targetElement) {
+ {
+ if (!nonDelegatedEvents.has(domEventName)) {
+ error(
+ 'Did not expect a listenToNonDelegatedEvent() call for "%s". ' +
+ "This is a bug in React. Please file an issue.",
+ domEventName
+ );
}
}
- return IndeterminateComponent;
-} // This is used to create an alternate fiber to do work on.
-
-function createWorkInProgress(current, pendingProps) {
- var workInProgress = current.alternate;
-
- if (workInProgress === null) {
- // We use a double buffering pooling technique because we know that we'll
- // only ever need at most two versions of a tree. We pool the "other" unused
- // node that we're free to reuse. This is lazily created to avoid allocating
- // extra objects for things that are never updated. It also allow us to
- // reclaim the extra memory if needed.
- workInProgress = createFiber(
- current.tag,
- pendingProps,
- current.key,
- current.mode
- );
- workInProgress.elementType = current.elementType;
- workInProgress.type = current.type;
- workInProgress.stateNode = current.stateNode;
+ var isCapturePhaseListener = false;
+ var listenerSet = getEventListenerSet(targetElement);
+ var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
- {
- // DEV-only fields
- workInProgress._debugSource = current._debugSource;
- workInProgress._debugOwner = current._debugOwner;
- workInProgress._debugHookTypes = current._debugHookTypes;
+ if (!listenerSet.has(listenerSetKey)) {
+ addTrappedEventListener(
+ targetElement,
+ domEventName,
+ IS_NON_DELEGATED,
+ isCapturePhaseListener
+ );
+ listenerSet.add(listenerSetKey);
+ }
+}
+function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
+ {
+ if (nonDelegatedEvents.has(domEventName) && !isCapturePhaseListener) {
+ error(
+ 'Did not expect a listenToNativeEvent() call for "%s" in the bubble phase. ' +
+ "This is a bug in React. Please file an issue.",
+ domEventName
+ );
}
+ }
- workInProgress.alternate = current;
- current.alternate = workInProgress;
- } else {
- workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type.
+ var eventSystemFlags = 0;
- workInProgress.type = current.type; // We already have an alternate.
- // Reset the effect tag.
+ if (isCapturePhaseListener) {
+ eventSystemFlags |= IS_CAPTURE_PHASE;
+ }
- workInProgress.flags = NoFlags$1; // The effects are no longer valid.
+ addTrappedEventListener(
+ target,
+ domEventName,
+ eventSystemFlags,
+ isCapturePhaseListener
+ );
+} // This is only used by createEventHandle when the
+// target is not a DOM element. E.g. window.
- workInProgress.subtreeFlags = NoFlags$1;
- workInProgress.deletions = null;
+function listenToNativeEventForNonManagedEventTarget(
+ domEventName,
+ isCapturePhaseListener,
+ target
+) {
+ var eventSystemFlags = IS_EVENT_HANDLE_NON_MANAGED_NODE;
+ var listenerSet = getEventListenerSet(target);
+ var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
- {
- // We intentionally reset, rather than copy, actualDuration & actualStartTime.
- // This prevents time from endlessly accumulating in new commits.
- // This has the downside of resetting values for different priority renders,
- // But works for yielding (the common case) and should support resuming.
- workInProgress.actualDuration = 0;
- workInProgress.actualStartTime = -1;
+ if (!listenerSet.has(listenerSetKey)) {
+ if (isCapturePhaseListener) {
+ eventSystemFlags |= IS_CAPTURE_PHASE;
}
- } // Reset all effects except static ones.
- // Static effects are not specific to a render.
-
- workInProgress.flags = current.flags & StaticMask;
- workInProgress.childLanes = current.childLanes;
- workInProgress.lanes = current.lanes;
- workInProgress.child = current.child;
- workInProgress.memoizedProps = current.memoizedProps;
- workInProgress.memoizedState = current.memoizedState;
- workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so
- // it cannot be shared with the current fiber.
- var currentDependencies = current.dependencies;
- workInProgress.dependencies =
- currentDependencies === null
- ? null
- : {
- lanes: currentDependencies.lanes,
- firstContext: currentDependencies.firstContext
- }; // These will be overridden during the parent's reconciliation
+ addTrappedEventListener(
+ target,
+ domEventName,
+ eventSystemFlags,
+ isCapturePhaseListener
+ );
+ listenerSet.add(listenerSetKey);
+ }
+}
+var listeningMarker = "_reactListening" + Math.random().toString(36).slice(2);
+function listenToAllSupportedEvents(rootContainerElement) {
+ if (!rootContainerElement[listeningMarker]) {
+ rootContainerElement[listeningMarker] = true;
+ allNativeEvents.forEach(function (domEventName) {
+ // We handle selectionchange separately because it
+ // doesn't bubble and needs to be on the document.
+ if (domEventName !== "selectionchange") {
+ if (!nonDelegatedEvents.has(domEventName)) {
+ listenToNativeEvent(domEventName, false, rootContainerElement);
+ }
- workInProgress.sibling = current.sibling;
- workInProgress.index = current.index;
- workInProgress.ref = current.ref;
- workInProgress.refCleanup = current.refCleanup;
+ listenToNativeEvent(domEventName, true, rootContainerElement);
+ }
+ });
+ var ownerDocument =
+ rootContainerElement.nodeType === DOCUMENT_NODE
+ ? rootContainerElement
+ : rootContainerElement.ownerDocument;
- {
- workInProgress.selfBaseDuration = current.selfBaseDuration;
- workInProgress.treeBaseDuration = current.treeBaseDuration;
+ if (ownerDocument !== null) {
+ // The selectionchange event also needs deduplication
+ // but it is attached to the document.
+ if (!ownerDocument[listeningMarker]) {
+ ownerDocument[listeningMarker] = true;
+ listenToNativeEvent("selectionchange", false, ownerDocument);
+ }
+ }
}
+}
- {
- workInProgress._debugNeedsRemount = current._debugNeedsRemount;
-
- switch (workInProgress.tag) {
- case IndeterminateComponent:
- case FunctionComponent:
- case SimpleMemoComponent:
- workInProgress.type = resolveFunctionForHotReloading(current.type);
- break;
+function addTrappedEventListener(
+ targetContainer,
+ domEventName,
+ eventSystemFlags,
+ isCapturePhaseListener,
+ isDeferredListenerForLegacyFBSupport
+) {
+ var listener = createEventListenerWrapperWithPriority(
+ targetContainer,
+ domEventName,
+ eventSystemFlags
+ ); // If passive option is not supported, then the event will be
+ // active and not passive.
- case ClassComponent:
- workInProgress.type = resolveClassForHotReloading(current.type);
- break;
+ var isPassiveListener = undefined;
- case ForwardRef:
- workInProgress.type = resolveForwardRefForHotReloading(current.type);
- break;
+ if (passiveBrowserEventsSupported) {
+ // Browsers introduced an intervention, making these events
+ // passive by default on document. React doesn't bind them
+ // to document anymore, but changing this now would undo
+ // the performance wins from the change. So we emulate
+ // the existing behavior manually on the roots now.
+ // https://github.com/facebook/react/issues/19651
+ if (
+ domEventName === "touchstart" ||
+ domEventName === "touchmove" ||
+ domEventName === "wheel"
+ ) {
+ isPassiveListener = true;
}
}
- return workInProgress;
-} // Used to reuse a Fiber for a second pass.
+ targetContainer =
+ enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
+ ? targetContainer.ownerDocument
+ : targetContainer;
+ var unsubscribeListener; // When legacyFBSupport is enabled, it's for when we
+ // want to add a one time event listener to a container.
+ // This should only be used with enableLegacyFBSupport
+ // due to requirement to provide compatibility with
+ // internal FB www event tooling. This works by removing
+ // the event listener as soon as it is invoked. We could
+ // also attempt to use the {once: true} param on
+ // addEventListener, but that requires support and some
+ // browsers do not support this today, and given this is
+ // to support legacy code patterns, it's likely they'll
+ // need support for such browsers.
-function resetWorkInProgress(workInProgress, renderLanes) {
- // This resets the Fiber to what createFiber or createWorkInProgress would
- // have set the values to before during the first pass. Ideally this wouldn't
- // be necessary but unfortunately many code paths reads from the workInProgress
- // when they should be reading from current and writing to workInProgress.
- // We assume pendingProps, index, key, ref, return are still untouched to
- // avoid doing another reconciliation.
- // Reset the effect flags but keep any Placement tags, since that's something
- // that child fiber is setting, not the reconciliation.
- workInProgress.flags &= StaticMask | Placement; // The effects are no longer valid.
+ if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
+ var originalListener = listener; // $FlowFixMe[missing-this-annot]
+ // $FlowFixMe[definition-cycle]
- var current = workInProgress.alternate;
+ listener = function () {
+ removeEventListener(targetContainer, domEventName, unsubscribeListener);
- if (current === null) {
- // Reset to createFiber's initial values.
- workInProgress.childLanes = NoLanes;
- workInProgress.lanes = renderLanes;
- workInProgress.child = null;
- workInProgress.subtreeFlags = NoFlags$1;
- workInProgress.memoizedProps = null;
- workInProgress.memoizedState = null;
- workInProgress.updateQueue = null;
- workInProgress.dependencies = null;
- workInProgress.stateNode = null;
+ for (
+ var _len = arguments.length, p = new Array(_len), _key = 0;
+ _key < _len;
+ _key++
+ ) {
+ p[_key] = arguments[_key];
+ }
- {
- // Note: We don't reset the actualTime counts. It's useful to accumulate
- // actual time across multiple render passes.
- workInProgress.selfBaseDuration = 0;
- workInProgress.treeBaseDuration = 0;
+ return originalListener.apply(this, p);
+ };
+ } // TODO: There are too many combinations here. Consolidate them.
+
+ if (isCapturePhaseListener) {
+ if (isPassiveListener !== undefined) {
+ unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
+ targetContainer,
+ domEventName,
+ listener,
+ isPassiveListener
+ );
+ } else {
+ unsubscribeListener = addEventCaptureListener(
+ targetContainer,
+ domEventName,
+ listener
+ );
}
} else {
- // Reset to the cloned values that createWorkInProgress would've.
- workInProgress.childLanes = current.childLanes;
- workInProgress.lanes = current.lanes;
- workInProgress.child = current.child;
- workInProgress.subtreeFlags = NoFlags$1;
- workInProgress.deletions = null;
- workInProgress.memoizedProps = current.memoizedProps;
- workInProgress.memoizedState = current.memoizedState;
- workInProgress.updateQueue = current.updateQueue; // Needed because Blocks store data on type.
-
- workInProgress.type = current.type; // Clone the dependencies object. This is mutated during the render phase, so
- // it cannot be shared with the current fiber.
-
- var currentDependencies = current.dependencies;
- workInProgress.dependencies =
- currentDependencies === null
- ? null
- : {
- lanes: currentDependencies.lanes,
- firstContext: currentDependencies.firstContext
- };
-
- {
- // Note: We don't reset the actualTime counts. It's useful to accumulate
- // actual time across multiple render passes.
- workInProgress.selfBaseDuration = current.selfBaseDuration;
- workInProgress.treeBaseDuration = current.treeBaseDuration;
+ if (isPassiveListener !== undefined) {
+ unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
+ targetContainer,
+ domEventName,
+ listener,
+ isPassiveListener
+ );
+ } else {
+ unsubscribeListener = addEventBubbleListener(
+ targetContainer,
+ domEventName,
+ listener
+ );
}
}
+}
- return workInProgress;
+function deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer) {
+ // We defer all click events with legacy FB support mode on.
+ // This means we add a one time event listener to trigger
+ // after the FB delegated listeners fire.
+ var isDeferredListenerForLegacyFBSupport = true;
+ addTrappedEventListener(
+ targetContainer,
+ domEventName,
+ IS_LEGACY_FB_SUPPORT_MODE,
+ false,
+ isDeferredListenerForLegacyFBSupport
+ );
}
-function createHostRootFiber(
- tag,
- isStrictMode,
- concurrentUpdatesByDefaultOverride
-) {
- var mode;
- if (tag === ConcurrentRoot) {
- mode = ConcurrentMode;
+function isMatchingRootContainer(grandContainer, targetContainer) {
+ return (
+ grandContainer === targetContainer ||
+ (grandContainer.nodeType === COMMENT_NODE &&
+ grandContainer.parentNode === targetContainer)
+ );
+}
- if (isStrictMode === true || createRootStrictEffectsByDefault) {
- mode |= StrictLegacyMode | StrictEffectsMode;
- }
+function dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ targetInst,
+ targetContainer
+) {
+ var ancestorInst = targetInst;
+
+ if (
+ (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 &&
+ (eventSystemFlags & IS_NON_DELEGATED) === 0
+ ) {
+ var targetContainerNode = targetContainer; // If we are using the legacy FB support flag, we
+ // defer the event to the null with a one
+ // time event listener so we can defer the event.
if (
- // Only for internal experiments.
- concurrentUpdatesByDefaultOverride
+ enableLegacyFBSupport && // If our event flags match the required flags for entering
+ // FB legacy mode and we are processing the "click" event,
+ // then we can defer the event to the "document", to allow
+ // for legacy FB support, where the expected behavior was to
+ // match React < 16 behavior of delegated clicks to the doc.
+ domEventName === "click" &&
+ (eventSystemFlags & SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE) === 0 &&
+ !isReplayingEvent(nativeEvent)
) {
- mode |= ConcurrentUpdatesByDefaultMode;
+ deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer);
+ return;
}
- } else {
- mode = NoMode;
- }
- if (isDevToolsPresent) {
- // Always collect profile timings when DevTools are present.
- // This enables DevTools to start capturing timing at any point–
- // Without some nodes in the tree having empty base times.
- mode |= ProfileMode;
- }
+ if (targetInst !== null) {
+ // The below logic attempts to work out if we need to change
+ // the target fiber to a different ancestor. We had similar logic
+ // in the legacy event system, except the big difference between
+ // systems is that the modern event system now has an event listener
+ // attached to each React Root and React Portal Root. Together,
+ // the DOM nodes representing these roots are the "rootContainer".
+ // To figure out which ancestor instance we should use, we traverse
+ // up the fiber tree from the target instance and attempt to find
+ // root boundaries that match that of our current "rootContainer".
+ // If we find that "rootContainer", we find the parent fiber
+ // sub-tree for that root and make that our ancestor instance.
+ var node = targetInst;
- return createFiber(HostRoot, null, null, mode);
-}
-function createFiberFromTypeAndProps(
- type, // React$ElementType
- key,
- pendingProps,
- owner,
- mode,
- lanes
-) {
- var fiberTag = IndeterminateComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
+ mainLoop: while (true) {
+ if (node === null) {
+ return;
+ }
- var resolvedType = type;
+ var nodeTag = node.tag;
- if (typeof type === "function") {
- if (shouldConstruct(type)) {
- fiberTag = ClassComponent;
+ if (nodeTag === HostRoot || nodeTag === HostPortal) {
+ var container = node.stateNode.containerInfo;
- {
- resolvedType = resolveClassForHotReloading(resolvedType);
- }
- } else {
- {
- resolvedType = resolveFunctionForHotReloading(resolvedType);
- }
- }
- } else if (typeof type === "string") {
- {
- var hostContext = getHostContext();
- fiberTag = isHostHoistableType(type, pendingProps, hostContext)
- ? HostHoistable
- : isHostSingletonType(type)
- ? HostSingleton
- : HostComponent;
- }
- } else {
- getTag: switch (type) {
- case REACT_FRAGMENT_TYPE:
- return createFiberFromFragment(pendingProps.children, mode, lanes, key);
+ if (isMatchingRootContainer(container, targetContainerNode)) {
+ break;
+ }
- case REACT_STRICT_MODE_TYPE:
- fiberTag = Mode;
- mode |= StrictLegacyMode;
+ if (nodeTag === HostPortal) {
+ // The target is a portal, but it's not the rootContainer we're looking for.
+ // Normally portals handle their own events all the way down to the root.
+ // So we should be able to stop now. However, we don't know if this portal
+ // was part of *our* root.
+ var grandNode = node.return;
- if ((mode & ConcurrentMode) !== NoMode) {
- // Strict effects should never run on legacy roots
- mode |= StrictEffectsMode;
- }
+ while (grandNode !== null) {
+ var grandTag = grandNode.tag;
- break;
+ if (grandTag === HostRoot || grandTag === HostPortal) {
+ var grandContainer = grandNode.stateNode.containerInfo;
- case REACT_PROFILER_TYPE:
- return createFiberFromProfiler(pendingProps, mode, lanes, key);
+ if (
+ isMatchingRootContainer(grandContainer, targetContainerNode)
+ ) {
+ // This is the rootContainer we're looking for and we found it as
+ // a parent of the Portal. That means we can ignore it because the
+ // Portal will bubble through to us.
+ return;
+ }
+ }
- case REACT_SUSPENSE_TYPE:
- return createFiberFromSuspense(pendingProps, mode, lanes, key);
+ grandNode = grandNode.return;
+ }
+ } // Now we need to find it's corresponding host fiber in the other
+ // tree. To do this we can use getClosestInstanceFromNode, but we
+ // need to validate that the fiber is a host instance, otherwise
+ // we need to traverse up through the DOM till we find the correct
+ // node that is from the other tree.
- case REACT_SUSPENSE_LIST_TYPE:
- return createFiberFromSuspenseList(pendingProps, mode, lanes, key);
+ while (container !== null) {
+ var parentNode = getClosestInstanceFromNode(container);
- case REACT_OFFSCREEN_TYPE:
- return createFiberFromOffscreen(pendingProps, mode, lanes, key);
+ if (parentNode === null) {
+ return;
+ }
- case REACT_LEGACY_HIDDEN_TYPE: {
- return createFiberFromLegacyHidden(pendingProps, mode, lanes, key);
- }
+ var parentTag = parentNode.tag;
- // eslint-disable-next-line no-fallthrough
+ if (
+ parentTag === HostComponent ||
+ parentTag === HostText ||
+ parentTag === HostHoistable ||
+ parentTag === HostSingleton
+ ) {
+ node = ancestorInst = parentNode;
+ continue mainLoop;
+ }
- case REACT_SCOPE_TYPE: {
- return createFiberFromScope(type, pendingProps, mode, lanes, key);
+ container = container.parentNode;
+ }
+ }
+
+ node = node.return;
}
+ }
+ }
- // eslint-disable-next-line no-fallthrough
+ batchedUpdates(function () {
+ return dispatchEventsForPlugins(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ ancestorInst,
+ targetContainer
+ );
+ });
+}
- case REACT_CACHE_TYPE: {
- return createFiberFromCache(pendingProps, mode, lanes, key);
- }
+function createDispatchListener(instance, listener, currentTarget) {
+ return {
+ instance: instance,
+ listener: listener,
+ currentTarget: currentTarget
+ };
+}
+
+function accumulateSinglePhaseListeners(
+ targetFiber,
+ reactName,
+ nativeEventType,
+ inCapturePhase,
+ accumulateTargetOnly,
+ nativeEvent
+) {
+ var captureName = reactName !== null ? reactName + "Capture" : null;
+ var reactEventName = inCapturePhase ? captureName : reactName;
+ var listeners = [];
+ var instance = targetFiber;
+ var lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path.
+
+ while (instance !== null) {
+ var _instance2 = instance,
+ stateNode = _instance2.stateNode,
+ tag = _instance2.tag; // Handle listeners that are on HostComponents (i.e.
)
+
+ if (
+ (tag === HostComponent ||
+ tag === HostHoistable ||
+ tag === HostSingleton) &&
+ stateNode !== null
+ ) {
+ lastHostComponent = stateNode; // createEventHandle listeners
- // eslint-disable-next-line no-fallthrough
+ {
+ var eventHandlerListeners = getEventHandlerListeners(lastHostComponent);
- case REACT_TRACING_MARKER_TYPE:
- if (enableTransitionTracing) {
- return createFiberFromTracingMarker(pendingProps, mode, lanes, key);
+ if (eventHandlerListeners !== null) {
+ eventHandlerListeners.forEach(function (entry) {
+ if (
+ entry.type === nativeEventType &&
+ entry.capture === inCapturePhase
+ ) {
+ listeners.push(
+ createDispatchListener(
+ instance,
+ entry.callback,
+ lastHostComponent
+ )
+ );
+ }
+ });
}
+ } // Standard React on* listeners, i.e. onClick or onClickCapture
- // eslint-disable-next-line no-fallthrough
+ if (reactEventName !== null) {
+ var listener = getListener(instance, reactEventName);
- case REACT_DEBUG_TRACING_MODE_TYPE:
- if (enableDebugTracing) {
- fiberTag = Mode;
- mode |= DebugTracingMode;
- break;
+ if (listener != null) {
+ listeners.push(
+ createDispatchListener(instance, listener, lastHostComponent)
+ );
}
+ }
+ } else if (
+ tag === ScopeComponent &&
+ lastHostComponent !== null &&
+ stateNode !== null
+ ) {
+ // Scopes
+ var reactScopeInstance = stateNode;
- // eslint-disable-next-line no-fallthrough
+ var _eventHandlerListeners = getEventHandlerListeners(reactScopeInstance);
- default: {
- if (typeof type === "object" && type !== null) {
- switch (type.$$typeof) {
- case REACT_PROVIDER_TYPE:
- fiberTag = ContextProvider;
- break getTag;
+ if (_eventHandlerListeners !== null) {
+ _eventHandlerListeners.forEach(function (entry) {
+ if (
+ entry.type === nativeEventType &&
+ entry.capture === inCapturePhase
+ ) {
+ listeners.push(
+ createDispatchListener(
+ instance,
+ entry.callback,
+ lastHostComponent
+ )
+ );
+ }
+ });
+ }
+ } // If we are only accumulating events for the target, then we don't
+ // continue to propagate through the React fiber tree to find other
+ // listeners.
- case REACT_CONTEXT_TYPE:
- // This is a consumer
- fiberTag = ContextConsumer;
- break getTag;
+ if (accumulateTargetOnly) {
+ break;
+ } // If we are processing the onBeforeBlur event, then we need to take
+ // into consideration that part of the React tree might have been hidden
+ // or deleted (as we're invoking this event during commit). We can find
+ // this out by checking if intercept fiber set on the event matches the
+ // current instance fiber. In which case, we should clear all existing
+ // listeners.
- case REACT_FORWARD_REF_TYPE:
- fiberTag = ForwardRef;
+ if (nativeEvent.type === "beforeblur") {
+ // $FlowFixMe[prop-missing] internal field
+ var detachedInterceptFiber = nativeEvent._detachedInterceptFiber;
- {
- resolvedType = resolveForwardRefForHotReloading(resolvedType);
- }
+ if (
+ detachedInterceptFiber !== null &&
+ (detachedInterceptFiber === instance ||
+ detachedInterceptFiber === instance.alternate)
+ ) {
+ listeners = [];
+ }
+ }
- break getTag;
+ instance = instance.return;
+ }
- case REACT_MEMO_TYPE:
- fiberTag = MemoComponent;
- break getTag;
+ return listeners;
+} // We should only use this function for:
+// - BeforeInputEventPlugin
+// - ChangeEventPlugin
+// - SelectEventPlugin
+// This is because we only process these plugins
+// in the bubble phase, so we need to accumulate two
+// phase event listeners (via emulation).
- case REACT_LAZY_TYPE:
- fiberTag = LazyComponent;
- resolvedType = null;
- break getTag;
- }
- }
+function accumulateTwoPhaseListeners(targetFiber, reactName) {
+ var captureName = reactName + "Capture";
+ var listeners = [];
+ var instance = targetFiber; // Accumulate all instances and listeners via the target -> root path.
- var info = "";
+ while (instance !== null) {
+ var _instance3 = instance,
+ stateNode = _instance3.stateNode,
+ tag = _instance3.tag; // Handle listeners that are on HostComponents (i.e.
)
- {
- 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.";
- }
+ if (
+ (tag === HostComponent ||
+ tag === HostHoistable ||
+ tag === HostSingleton) &&
+ stateNode !== null
+ ) {
+ var currentTarget = stateNode;
+ var captureListener = getListener(instance, captureName);
- var ownerName = owner ? getComponentNameFromFiber(owner) : null;
+ if (captureListener != null) {
+ listeners.unshift(
+ createDispatchListener(instance, captureListener, currentTarget)
+ );
+ }
- if (ownerName) {
- info += "\n\nCheck the render method of `" + ownerName + "`.";
- }
- }
+ var bubbleListener = getListener(instance, reactName);
- throw new Error(
- "Element type is invalid: expected a string (for built-in " +
- "components) or a class/function (for composite components) " +
- ("but got: " + (type == null ? type : typeof type) + "." + info)
+ if (bubbleListener != null) {
+ listeners.push(
+ createDispatchListener(instance, bubbleListener, currentTarget)
);
}
}
- }
-
- var fiber = createFiber(fiberTag, pendingProps, key, mode);
- fiber.elementType = type;
- fiber.type = resolvedType;
- fiber.lanes = lanes;
- {
- fiber._debugOwner = owner;
+ instance = instance.return;
}
- return fiber;
+ return listeners;
}
-function createFiberFromElement(element, mode, lanes) {
- var owner = null;
- {
- owner = element._owner;
+function getParent(inst) {
+ if (inst === null) {
+ return null;
}
- var type = element.type;
- var key = element.key;
- var pendingProps = element.props;
- var fiber = createFiberFromTypeAndProps(
- type,
- key,
- pendingProps,
- owner,
- mode,
- lanes
- );
+ do {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ inst = inst.return; // TODO: If this is a HostRoot we might want to bail out.
+ // That is depending on if we want nested subtrees (layers) to bubble
+ // events to their parent. We could also go through parentNode on the
+ // host node but that wouldn't work for React Native and doesn't let us
+ // do the portal feature.
+ } while (inst && inst.tag !== HostComponent && inst.tag !== HostSingleton);
- {
- fiber._debugSource = element._source;
- fiber._debugOwner = element._owner;
+ if (inst) {
+ return inst;
}
- return fiber;
-}
-function createFiberFromFragment(elements, mode, lanes, key) {
- var fiber = createFiber(Fragment, elements, key, mode);
- fiber.lanes = lanes;
- return fiber;
+ return null;
}
+/**
+ * Return the lowest common ancestor of A and B, or null if they are in
+ * different trees.
+ */
-function createFiberFromScope(scope, pendingProps, mode, lanes, key) {
- var fiber = createFiber(ScopeComponent, pendingProps, key, mode);
- fiber.type = scope;
- fiber.elementType = scope;
- fiber.lanes = lanes;
- return fiber;
-}
+function getLowestCommonAncestor(instA, instB) {
+ var nodeA = instA;
+ var nodeB = instB;
+ var depthA = 0;
-function createFiberFromProfiler(pendingProps, mode, lanes, key) {
- {
- if (typeof pendingProps.id !== "string") {
- error(
- 'Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.',
- typeof pendingProps.id
- );
- }
+ for (var tempA = nodeA; tempA; tempA = getParent(tempA)) {
+ depthA++;
}
- var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
- fiber.elementType = REACT_PROFILER_TYPE;
- fiber.lanes = lanes;
-
- {
- fiber.stateNode = {
- effectDuration: 0,
- passiveEffectDuration: 0
- };
- }
+ var depthB = 0;
- return fiber;
-}
+ for (var tempB = nodeB; tempB; tempB = getParent(tempB)) {
+ depthB++;
+ } // If A is deeper, crawl up.
-function createFiberFromSuspense(pendingProps, mode, lanes, key) {
- var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
- fiber.elementType = REACT_SUSPENSE_TYPE;
- fiber.lanes = lanes;
- return fiber;
-}
-function createFiberFromSuspenseList(pendingProps, mode, lanes, key) {
- var fiber = createFiber(SuspenseListComponent, pendingProps, key, mode);
- fiber.elementType = REACT_SUSPENSE_LIST_TYPE;
- fiber.lanes = lanes;
- return fiber;
-}
-function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
- var fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
- fiber.elementType = REACT_OFFSCREEN_TYPE;
- fiber.lanes = lanes;
- var primaryChildInstance = {
- _visibility: OffscreenVisible,
- _pendingVisibility: OffscreenVisible,
- _pendingMarkers: null,
- _retryCache: null,
- _transitions: null,
- _current: null,
- detach: function () {
- return detachOffscreenInstance(primaryChildInstance);
- },
- attach: function () {
- return attachOffscreenInstance(primaryChildInstance);
- }
- };
- fiber.stateNode = primaryChildInstance;
- return fiber;
-}
-function createFiberFromLegacyHidden(pendingProps, mode, lanes, key) {
- var fiber = createFiber(LegacyHiddenComponent, pendingProps, key, mode);
- fiber.elementType = REACT_LEGACY_HIDDEN_TYPE;
- fiber.lanes = lanes; // Adding a stateNode for legacy hidden because it's currently using
- // the offscreen implementation, which depends on a state node
+ while (depthA - depthB > 0) {
+ nodeA = getParent(nodeA);
+ depthA--;
+ } // If B is deeper, crawl up.
- var instance = {
- _visibility: OffscreenVisible,
- _pendingVisibility: OffscreenVisible,
- _pendingMarkers: null,
- _transitions: null,
- _retryCache: null,
- _current: null,
- detach: function () {
- return detachOffscreenInstance(instance);
- },
- attach: function () {
- return attachOffscreenInstance(instance);
- }
- };
- fiber.stateNode = instance;
- return fiber;
-}
-function createFiberFromCache(pendingProps, mode, lanes, key) {
- var fiber = createFiber(CacheComponent, pendingProps, key, mode);
- fiber.elementType = REACT_CACHE_TYPE;
- fiber.lanes = lanes;
- return fiber;
-}
-function createFiberFromTracingMarker(pendingProps, mode, lanes, key) {
- var fiber = createFiber(TracingMarkerComponent, pendingProps, key, mode);
- fiber.elementType = REACT_TRACING_MARKER_TYPE;
- fiber.lanes = lanes;
- var tracingMarkerInstance = {
- tag: TransitionTracingMarker,
- transitions: null,
- pendingBoundaries: null,
- aborts: null,
- name: pendingProps.name
- };
- fiber.stateNode = tracingMarkerInstance;
- return fiber;
-}
-function createFiberFromText(content, mode, lanes) {
- var fiber = createFiber(HostText, content, null, mode);
- fiber.lanes = lanes;
- return fiber;
-}
-function createFiberFromHostInstanceForDeletion() {
- var fiber = createFiber(HostComponent, null, null, NoMode);
- fiber.elementType = "DELETED";
- return fiber;
-}
-function createFiberFromDehydratedFragment(dehydratedNode) {
- var fiber = createFiber(DehydratedFragment, null, null, NoMode);
- fiber.stateNode = dehydratedNode;
- return fiber;
-}
-function createFiberFromPortal(portal, mode, lanes) {
- var pendingProps = portal.children !== null ? portal.children : [];
- var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
- fiber.lanes = lanes;
- fiber.stateNode = {
- containerInfo: portal.containerInfo,
- pendingChildren: null,
- // Used by persistent updates
- implementation: portal.implementation
- };
- return fiber;
-} // Used for stashing WIP properties to replay failed work in DEV.
+ while (depthB - depthA > 0) {
+ nodeB = getParent(nodeB);
+ depthB--;
+ } // Walk in lockstep until we find a match.
-function assignFiberPropertiesInDEV(target, source) {
- if (target === null) {
- // This Fiber's initial properties will always be overwritten.
- // We only use a Fiber to ensure the same hidden class so DEV isn't slow.
- target = createFiber(IndeterminateComponent, null, null, NoMode);
- } // This is intentionally written as a list of all properties.
- // We tried to use Object.assign() instead but this is called in
- // the hottest path, and Object.assign() was too slow:
- // https://github.com/facebook/react/issues/12502
- // This code is DEV-only so size is not a concern.
+ var depth = depthA;
- target.tag = source.tag;
- target.key = source.key;
- target.elementType = source.elementType;
- target.type = source.type;
- target.stateNode = source.stateNode;
- target.return = source.return;
- target.child = source.child;
- target.sibling = source.sibling;
- target.index = source.index;
- target.ref = source.ref;
- target.refCleanup = source.refCleanup;
- target.pendingProps = source.pendingProps;
- target.memoizedProps = source.memoizedProps;
- target.updateQueue = source.updateQueue;
- target.memoizedState = source.memoizedState;
- target.dependencies = source.dependencies;
- target.mode = source.mode;
- target.flags = source.flags;
- target.subtreeFlags = source.subtreeFlags;
- target.deletions = source.deletions;
- target.lanes = source.lanes;
- target.childLanes = source.childLanes;
- target.alternate = source.alternate;
+ while (depth--) {
+ if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
+ return nodeA;
+ }
- {
- target.actualDuration = source.actualDuration;
- target.actualStartTime = source.actualStartTime;
- target.selfBaseDuration = source.selfBaseDuration;
- target.treeBaseDuration = source.treeBaseDuration;
+ nodeA = getParent(nodeA);
+ nodeB = getParent(nodeB);
}
- target._debugSource = source._debugSource;
- target._debugOwner = source._debugOwner;
- target._debugNeedsRemount = source._debugNeedsRemount;
- target._debugHookTypes = source._debugHookTypes;
- return target;
+ return null;
}
-function FiberRootNode(
- containerInfo, // $FlowFixMe[missing-local-annot]
- tag,
- hydrate,
- identifierPrefix,
- onRecoverableError
+function accumulateEnterLeaveListenersForEvent(
+ dispatchQueue,
+ event,
+ target,
+ common,
+ inCapturePhase
) {
- this.tag = tag;
- this.containerInfo = containerInfo;
- this.pendingChildren = null;
- this.current = null;
- this.pingCache = null;
- this.finishedWork = null;
- this.timeoutHandle = noTimeout;
- this.context = null;
- this.pendingContext = null;
- this.callbackNode = null;
- this.callbackPriority = NoLane;
- this.eventTimes = createLaneMap(NoLanes);
- this.expirationTimes = createLaneMap(NoTimestamp);
- this.pendingLanes = NoLanes;
- this.suspendedLanes = NoLanes;
- this.pingedLanes = NoLanes;
- this.expiredLanes = NoLanes;
- this.mutableReadLanes = NoLanes;
- this.finishedLanes = NoLanes;
- this.errorRecoveryDisabledLanes = NoLanes;
- this.entangledLanes = NoLanes;
- this.entanglements = createLaneMap(NoLanes);
- this.hiddenUpdates = createLaneMap(null);
- this.identifierPrefix = identifierPrefix;
- this.onRecoverableError = onRecoverableError;
+ var registrationName = event._reactName;
+ var listeners = [];
+ var instance = target;
- {
- this.pooledCache = null;
- this.pooledCacheLanes = NoLanes;
- }
+ while (instance !== null) {
+ if (instance === common) {
+ break;
+ }
- {
- this.mutableSourceEagerHydrationData = null;
- }
+ var _instance4 = instance,
+ alternate = _instance4.alternate,
+ stateNode = _instance4.stateNode,
+ tag = _instance4.tag;
- {
- this.hydrationCallbacks = null;
- }
+ if (alternate !== null && alternate === common) {
+ break;
+ }
- this.incompleteTransitions = new Map();
+ if (
+ (tag === HostComponent ||
+ tag === HostHoistable ||
+ tag === HostSingleton) &&
+ stateNode !== null
+ ) {
+ var currentTarget = stateNode;
- if (enableTransitionTracing) {
- this.transitionCallbacks = null;
- var transitionLanesMap = (this.transitionLanes = []);
+ if (inCapturePhase) {
+ var captureListener = getListener(instance, registrationName);
- for (var i = 0; i < TotalLanes; i++) {
- transitionLanesMap.push(null);
+ if (captureListener != null) {
+ listeners.unshift(
+ createDispatchListener(instance, captureListener, currentTarget)
+ );
+ }
+ } else if (!inCapturePhase) {
+ var bubbleListener = getListener(instance, registrationName);
+
+ if (bubbleListener != null) {
+ listeners.push(
+ createDispatchListener(instance, bubbleListener, currentTarget)
+ );
+ }
+ }
}
+
+ instance = instance.return;
}
- {
- this.effectDuration = 0;
- this.passiveEffectDuration = 0;
+ if (listeners.length !== 0) {
+ dispatchQueue.push({
+ event: event,
+ listeners: listeners
+ });
}
+} // We should only use this function for:
+// - EnterLeaveEventPlugin
+// This is because we only process this plugin
+// in the bubble phase, so we need to accumulate two
+// phase event listeners.
- {
- this.memoizedUpdaters = new Set();
- var pendingUpdatersLaneMap = (this.pendingUpdatersLaneMap = []);
+function accumulateEnterLeaveTwoPhaseListeners(
+ dispatchQueue,
+ leaveEvent,
+ enterEvent,
+ from,
+ to
+) {
+ var common = from && to ? getLowestCommonAncestor(from, to) : null;
- for (var _i = 0; _i < TotalLanes; _i++) {
- pendingUpdatersLaneMap.push(new Set());
- }
+ if (from !== null) {
+ accumulateEnterLeaveListenersForEvent(
+ dispatchQueue,
+ leaveEvent,
+ from,
+ common,
+ false
+ );
}
- {
- switch (tag) {
- case ConcurrentRoot:
- this._debugRootType = hydrate ? "hydrateRoot()" : "createRoot()";
- break;
-
- case LegacyRoot:
- this._debugRootType = hydrate ? "hydrate()" : "render()";
- break;
- }
+ if (to !== null && enterEvent !== null) {
+ accumulateEnterLeaveListenersForEvent(
+ dispatchQueue,
+ enterEvent,
+ to,
+ common,
+ true
+ );
}
}
-
-function createFiberRoot(
- containerInfo,
- tag,
- hydrate,
- initialChildren,
- hydrationCallbacks,
- isStrictMode,
- concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the
- // host config, but because they are passed in at runtime, we have to thread
- // them through the root constructor. Perhaps we should put them all into a
- // single type, like a DynamicHostConfig that is defined by the renderer.
- identifierPrefix,
- onRecoverableError,
- transitionCallbacks
+function accumulateEventHandleNonManagedNodeListeners(
+ reactEventType,
+ currentTarget,
+ inCapturePhase
) {
- // $FlowFixMe[invalid-constructor] Flow no longer supports calling new on functions
- var root = new FiberRootNode(
- containerInfo,
- tag,
- hydrate,
- identifierPrefix,
- onRecoverableError
- );
+ var listeners = [];
+ var eventListeners = getEventHandlerListeners(currentTarget);
- {
- root.hydrationCallbacks = hydrationCallbacks;
+ if (eventListeners !== null) {
+ eventListeners.forEach(function (entry) {
+ if (entry.type === reactEventType && entry.capture === inCapturePhase) {
+ listeners.push(
+ createDispatchListener(null, entry.callback, currentTarget)
+ );
+ }
+ });
}
- if (enableTransitionTracing) {
- root.transitionCallbacks = transitionCallbacks;
- } // Cyclic construction. This cheats the type system right now because
- // stateNode is any.
-
- var uninitializedFiber = createHostRootFiber(
- tag,
- isStrictMode,
- concurrentUpdatesByDefaultOverride
- );
- root.current = uninitializedFiber;
- uninitializedFiber.stateNode = root;
+ return listeners;
+}
+function getListenerSetKey(domEventName, capture) {
+ return domEventName + "__" + (capture ? "capture" : "bubble");
+}
- {
- var initialCache = createCache();
- retainCache(initialCache); // The pooledCache is a fresh cache instance that is used temporarily
- // for newly mounted boundaries during a render. In general, the
- // pooledCache is always cleared from the root at the end of a render:
- // it is either released when render commits, or moved to an Offscreen
- // component if rendering suspends. Because the lifetime of the pooled
- // cache is distinct from the main memoizedState.cache, it must be
- // retained separately.
+// has this definition built-in.
- root.pooledCache = initialCache;
- retainCache(initialCache);
- var initialState = {
- element: initialChildren,
- isDehydrated: hydrate,
- cache: initialCache
- };
- uninitializedFiber.memoizedState = initialState;
- }
+var hasScheduledReplayAttempt = false; // The queue of discrete events to be replayed.
- initializeUpdateQueue(uninitializedFiber);
- return root;
-}
+var queuedDiscreteEvents = []; // Indicates if any continuous event targets are non-null for early bailout.
+// if the last target was dehydrated.
-var ReactVersion = "18.3.0-www-classic-ef5e99b7";
+var queuedFocus = null;
+var queuedDrag = null;
+var queuedMouse = null; // For pointer events there can be one latest event per pointerId.
-function createPortal$1(
- children,
- containerInfo, // TODO: figure out the API for cross-renderer implementation.
- implementation
-) {
- var key =
- arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
+var queuedPointers = new Map();
+var queuedPointerCaptures = new Map(); // We could consider replaying selectionchange and touchmoves too.
- {
- checkKeyStringCoercion(key);
- }
+var queuedExplicitHydrationTargets = [];
+function hasQueuedDiscreteEvents() {
+ return queuedDiscreteEvents.length > 0;
+}
+var discreteReplayableEvents = [
+ "mousedown",
+ "mouseup",
+ "touchcancel",
+ "touchend",
+ "touchstart",
+ "auxclick",
+ "dblclick",
+ "pointercancel",
+ "pointerdown",
+ "pointerup",
+ "dragend",
+ "dragstart",
+ "drop",
+ "compositionend",
+ "compositionstart",
+ "keydown",
+ "keypress",
+ "keyup",
+ "input",
+ "textInput", // Intentionally camelCase
+ "copy",
+ "cut",
+ "paste",
+ "click",
+ "change",
+ "contextmenu",
+ "reset",
+ "submit"
+];
+function isDiscreteEventThatRequiresHydration(eventType) {
+ return discreteReplayableEvents.indexOf(eventType) > -1;
+}
+function createQueuedReplayableEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
return {
- // This tag allow us to uniquely identify this as a React Portal
- $$typeof: REACT_PORTAL_TYPE,
- key: key == null ? null : "" + key,
- children: children,
- containerInfo: containerInfo,
- implementation: implementation
+ blockedOn: blockedOn,
+ domEventName: domEventName,
+ eventSystemFlags: eventSystemFlags,
+ nativeEvent: nativeEvent,
+ targetContainers: [targetContainer]
};
}
-// Might add PROFILE later.
-
-var didWarnAboutNestedUpdates;
-var didWarnAboutFindNodeInStrictMode;
-
-{
- didWarnAboutNestedUpdates = false;
- didWarnAboutFindNodeInStrictMode = {};
-}
-
-function getContextForSubtree(parentComponent) {
- if (!parentComponent) {
- return emptyContextObject;
+function queueDiscreteEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
+ return;
}
- var fiber = get(parentComponent);
- var parentContext = findCurrentUnmaskedContext(fiber);
+ var queuedEvent = createQueuedReplayableEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
+ queuedDiscreteEvents.push(queuedEvent);
- if (fiber.tag === ClassComponent) {
- var Component = fiber.type;
+ if (queuedDiscreteEvents.length === 1) {
+ // If this was the first discrete event, we might be able to
+ // synchronously unblock it so that preventDefault still works.
+ while (queuedEvent.blockedOn !== null) {
+ var fiber = getInstanceFromNode(queuedEvent.blockedOn);
- if (isContextProvider(Component)) {
- return processChildContext(fiber, Component, parentContext);
- }
- }
+ if (fiber === null) {
+ break;
+ }
- return parentContext;
-}
+ attemptSynchronousHydration(fiber);
-function findHostInstanceWithWarning(component, methodName) {
- {
- var fiber = get(component);
+ if (queuedEvent.blockedOn === null) {
+ // We got unblocked by hydration. Let's try again.
+ replayUnblockedEvents(); // If we're reblocked, on an inner boundary, we might need
+ // to attempt hydrating that one.
- if (fiber === undefined) {
- if (typeof component.render === "function") {
- throw new Error("Unable to find node on an unmounted component.");
+ continue;
} else {
- var keys = Object.keys(component).join(",");
- throw new Error(
- "Argument appears to not be a ReactComponent. Keys: " + keys
- );
+ // We're still blocked from hydration, we have to give up
+ // and replay later.
+ break;
}
}
+ }
+} // Resets the replaying for this type of continuous event to no event.
- var hostFiber = findCurrentHostFiber(fiber);
+function clearIfContinuousEvent(domEventName, nativeEvent) {
+ switch (domEventName) {
+ case "focusin":
+ case "focusout":
+ queuedFocus = null;
+ break;
- if (hostFiber === null) {
- return null;
+ case "dragenter":
+ case "dragleave":
+ queuedDrag = null;
+ break;
+
+ case "mouseover":
+ case "mouseout":
+ queuedMouse = null;
+ break;
+
+ case "pointerover":
+ case "pointerout": {
+ var pointerId = nativeEvent.pointerId;
+ queuedPointers.delete(pointerId);
+ break;
}
- if (hostFiber.mode & StrictLegacyMode) {
- var componentName = getComponentNameFromFiber(fiber) || "Component";
+ case "gotpointercapture":
+ case "lostpointercapture": {
+ var _pointerId = nativeEvent.pointerId;
+ queuedPointerCaptures.delete(_pointerId);
+ break;
+ }
+ }
+}
- if (!didWarnAboutFindNodeInStrictMode[componentName]) {
- didWarnAboutFindNodeInStrictMode[componentName] = true;
- var previousFiber = current;
+function accumulateOrCreateContinuousQueuedReplayableEvent(
+ existingQueuedEvent,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ if (
+ existingQueuedEvent === null ||
+ existingQueuedEvent.nativeEvent !== nativeEvent
+ ) {
+ var queuedEvent = createQueuedReplayableEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
- try {
- setCurrentFiber(hostFiber);
+ if (blockedOn !== null) {
+ var fiber = getInstanceFromNode(blockedOn);
- if (fiber.mode & StrictLegacyMode) {
- error(
- "%s is deprecated in StrictMode. " +
- "%s was passed an instance of %s which is inside StrictMode. " +
- "Instead, add a ref directly to the element you want to reference. " +
- "Learn more about using refs safely here: " +
- "https://reactjs.org/link/strict-mode-find-node",
- methodName,
- methodName,
- componentName
- );
- } else {
- error(
- "%s is deprecated in StrictMode. " +
- "%s was passed an instance of %s which renders StrictMode children. " +
- "Instead, add a ref directly to the element you want to reference. " +
- "Learn more about using refs safely here: " +
- "https://reactjs.org/link/strict-mode-find-node",
- methodName,
- methodName,
- componentName
- );
- }
- } finally {
- // Ideally this should reset to previous but this shouldn't be called in
- // render and there's another warning for that anyway.
- if (previousFiber) {
- setCurrentFiber(previousFiber);
- } else {
- resetCurrentFiber();
- }
- }
+ if (fiber !== null) {
+ // Attempt to increase the priority of this target.
+ attemptContinuousHydration(fiber);
}
}
- return getPublicInstance(hostFiber.stateNode);
+ return queuedEvent;
+ } // If we have already queued this exact event, then it's because
+ // the different event systems have different DOM event listeners.
+ // We can accumulate the flags, and the targetContainers, and
+ // store a single event to be replayed.
+
+ existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
+ var targetContainers = existingQueuedEvent.targetContainers;
+
+ if (
+ targetContainer !== null &&
+ targetContainers.indexOf(targetContainer) === -1
+ ) {
+ targetContainers.push(targetContainer);
}
-}
-function createContainer(
- containerInfo,
- tag,
- hydrationCallbacks,
- isStrictMode,
- concurrentUpdatesByDefaultOverride,
- identifierPrefix,
- onRecoverableError,
- transitionCallbacks
-) {
- var hydrate = false;
- var initialChildren = null;
- return createFiberRoot(
- containerInfo,
- tag,
- hydrate,
- initialChildren,
- hydrationCallbacks,
- isStrictMode,
- concurrentUpdatesByDefaultOverride,
- identifierPrefix,
- onRecoverableError,
- transitionCallbacks
- );
+ return existingQueuedEvent;
}
-function createHydrationContainer(
- initialChildren, // TODO: Remove `callback` when we delete legacy mode.
- callback,
- containerInfo,
- tag,
- hydrationCallbacks,
- isStrictMode,
- concurrentUpdatesByDefaultOverride,
- identifierPrefix,
- onRecoverableError,
- transitionCallbacks
+
+function queueIfContinuousEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
) {
- var hydrate = true;
- var root = createFiberRoot(
- containerInfo,
- tag,
- hydrate,
- initialChildren,
- hydrationCallbacks,
- isStrictMode,
- concurrentUpdatesByDefaultOverride,
- identifierPrefix,
- onRecoverableError,
- transitionCallbacks
- ); // TODO: Move this to FiberRoot constructor
+ // These set relatedTarget to null because the replayed event will be treated as if we
+ // moved from outside the window (no target) onto the target once it hydrates.
+ // Instead of mutating we could clone the event.
+ switch (domEventName) {
+ case "focusin": {
+ var focusEvent = nativeEvent;
+ queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(
+ queuedFocus,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ focusEvent
+ );
+ return true;
+ }
+
+ case "dragenter": {
+ var dragEvent = nativeEvent;
+ queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(
+ queuedDrag,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ dragEvent
+ );
+ return true;
+ }
+
+ case "mouseover": {
+ var mouseEvent = nativeEvent;
+ queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(
+ queuedMouse,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ mouseEvent
+ );
+ return true;
+ }
+
+ case "pointerover": {
+ var pointerEvent = nativeEvent;
+ var pointerId = pointerEvent.pointerId;
+ queuedPointers.set(
+ pointerId,
+ accumulateOrCreateContinuousQueuedReplayableEvent(
+ queuedPointers.get(pointerId) || null,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ pointerEvent
+ )
+ );
+ return true;
+ }
+
+ case "gotpointercapture": {
+ var _pointerEvent = nativeEvent;
+ var _pointerId2 = _pointerEvent.pointerId;
+ queuedPointerCaptures.set(
+ _pointerId2,
+ accumulateOrCreateContinuousQueuedReplayableEvent(
+ queuedPointerCaptures.get(_pointerId2) || null,
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ _pointerEvent
+ )
+ );
+ return true;
+ }
+ }
- root.context = getContextForSubtree(null); // Schedule the initial render. In a hydration root, this is different from
- // a regular update because the initial render must match was was rendered
- // on the server.
- // NOTE: This update intentionally doesn't have a payload. We're only using
- // the update to schedule work on the root fiber (and, for legacy roots, to
- // enqueue the callback if one is provided).
+ return false;
+} // Check if this target is unblocked. Returns true if it's unblocked.
- var current = root.current;
- var lane = requestUpdateLane(current);
- var update = createUpdate(lane);
- update.callback =
- callback !== undefined && callback !== null ? callback : null;
- var eventTime = requestEventTime();
- enqueueUpdate(current, update, lane);
- scheduleInitialHydrationOnRoot(root, lane, eventTime);
- return root;
-}
-function updateContainer(element, container, parentComponent, callback) {
- {
- onScheduleRoot(container, element);
- }
+function attemptExplicitHydrationTarget(queuedTarget) {
+ // TODO: This function shares a lot of logic with findInstanceBlockingEvent.
+ // Try to unify them. It's a bit tricky since it would require two return
+ // values.
+ var targetInst = getClosestInstanceFromNode(queuedTarget.target);
- var current$1 = container.current;
- var lane = requestUpdateLane(current$1);
+ if (targetInst !== null) {
+ var nearestMounted = getNearestMountedFiber(targetInst);
- if (enableSchedulingProfiler) {
- markRenderScheduled(lane);
- }
+ if (nearestMounted !== null) {
+ var tag = nearestMounted.tag;
- var context = getContextForSubtree(parentComponent);
+ if (tag === SuspenseComponent) {
+ var instance = getSuspenseInstanceFromFiber(nearestMounted);
- if (container.context === null) {
- container.context = context;
- } else {
- container.pendingContext = context;
- }
+ if (instance !== null) {
+ // We're blocked on hydrating this boundary.
+ // Increase its priority.
+ queuedTarget.blockedOn = instance;
+ runWithPriority(queuedTarget.priority, function () {
+ attemptHydrationAtCurrentPriority(nearestMounted);
+ });
+ return;
+ }
+ } else if (tag === HostRoot) {
+ var root = nearestMounted.stateNode;
- {
- if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
- didWarnAboutNestedUpdates = true;
+ if (isRootDehydrated(root)) {
+ queuedTarget.blockedOn = getContainerFromFiber(nearestMounted); // We don't currently have a way to increase the priority of
+ // a root other than sync.
- error(
- "Render methods should be a pure function of props and state; " +
- "triggering nested component updates from render is not allowed. " +
- "If necessary, trigger nested updates in componentDidUpdate.\n\n" +
- "Check the render method of %s.",
- getComponentNameFromFiber(current) || "Unknown"
- );
+ return;
+ }
+ }
}
}
- var update = createUpdate(lane); // Caution: React DevTools currently depends on this property
- // being called "element".
+ queuedTarget.blockedOn = null;
+}
- update.payload = {
- element: element
+function queueExplicitHydrationTarget(target) {
+ // TODO: This will read the priority if it's dispatched by the React
+ // event system but not native events. Should read window.event.type, like
+ // we do for updates (getCurrentEventPriority).
+ var updatePriority = getCurrentUpdatePriority();
+ var queuedTarget = {
+ blockedOn: null,
+ target: target,
+ priority: updatePriority
};
- callback = callback === undefined ? null : callback;
+ var i = 0;
- if (callback !== null) {
- {
- if (typeof callback !== "function") {
- error(
- "render(...): Expected the last optional `callback` argument to be a " +
- "function. Instead received: %s.",
- callback
- );
- }
+ for (; i < queuedExplicitHydrationTargets.length; i++) {
+ // Stop once we hit the first target with lower priority than
+ if (
+ !isHigherEventPriority(
+ updatePriority,
+ queuedExplicitHydrationTargets[i].priority
+ )
+ ) {
+ break;
}
-
- update.callback = callback;
}
- var root = enqueueUpdate(current$1, update, lane);
+ queuedExplicitHydrationTargets.splice(i, 0, queuedTarget);
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, current$1, lane, eventTime);
- entangleTransitions(root, current$1, lane);
+ if (i === 0) {
+ attemptExplicitHydrationTarget(queuedTarget);
}
-
- return lane;
}
-function getPublicRootInstance(container) {
- var containerFiber = container.current;
- if (!containerFiber.child) {
- return null;
+function attemptReplayContinuousQueuedEvent(queuedEvent) {
+ if (queuedEvent.blockedOn !== null) {
+ return false;
}
- switch (containerFiber.child.tag) {
- case HostSingleton:
- case HostComponent:
- return getPublicInstance(containerFiber.child.stateNode);
+ var targetContainers = queuedEvent.targetContainers;
- default:
- return containerFiber.child.stateNode;
+ while (targetContainers.length > 0) {
+ var targetContainer = targetContainers[0];
+ var nextBlockedOn = findInstanceBlockingEvent(
+ queuedEvent.domEventName,
+ queuedEvent.eventSystemFlags,
+ targetContainer,
+ queuedEvent.nativeEvent
+ );
+
+ if (nextBlockedOn === null) {
+ if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
+ var nativeEvent = queuedEvent.nativeEvent;
+ var nativeEventClone = new nativeEvent.constructor(
+ nativeEvent.type,
+ nativeEvent
+ );
+ setReplayingEvent(nativeEventClone);
+ nativeEvent.target.dispatchEvent(nativeEventClone);
+ resetReplayingEvent();
+ } else {
+ setReplayingEvent(queuedEvent.nativeEvent);
+ dispatchEventForPluginEventSystem(
+ queuedEvent.domEventName,
+ queuedEvent.eventSystemFlags,
+ queuedEvent.nativeEvent,
+ return_targetInst,
+ targetContainer
+ );
+ resetReplayingEvent();
+ }
+ } else {
+ // We're still blocked. Try again later.
+ var fiber = getInstanceFromNode(nextBlockedOn);
+
+ if (fiber !== null) {
+ attemptContinuousHydration(fiber);
+ }
+
+ queuedEvent.blockedOn = nextBlockedOn;
+ return false;
+ } // This target container was successfully dispatched. Try the next.
+
+ targetContainers.shift();
}
+
+ return true;
}
-function attemptSynchronousHydration(fiber) {
- switch (fiber.tag) {
- case HostRoot: {
- var root = fiber.stateNode;
- if (isRootDehydrated(root)) {
- // Flush the first scheduled "update".
- var lanes = getHighestPriorityPendingLanes(root);
- flushRoot(root, lanes);
- }
+function attemptReplayContinuousQueuedEventInMap(queuedEvent, key, map) {
+ if (attemptReplayContinuousQueuedEvent(queuedEvent)) {
+ map.delete(key);
+ }
+}
- break;
- }
+function replayUnblockedEvents() {
+ hasScheduledReplayAttempt = false;
- case SuspenseComponent: {
- flushSync$1(function () {
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ if (!enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
+ // First replay discrete events.
+ while (queuedDiscreteEvents.length > 0) {
+ var nextDiscreteEvent = queuedDiscreteEvents[0];
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
+ if (nextDiscreteEvent.blockedOn !== null) {
+ // We're still blocked.
+ // Increase the priority of this boundary to unblock
+ // the next discrete event.
+ var fiber = getInstanceFromNode(nextDiscreteEvent.blockedOn);
+
+ if (fiber !== null) {
+ attemptDiscreteHydration(fiber);
}
- }); // If we're still blocked after this, we need to increase
- // the priority of any promises resolving within this
- // boundary so that they next attempt also has higher pri.
- var retryLane = SyncLane;
- markRetryLaneIfNotHydrated(fiber, retryLane);
- break;
+ break;
+ }
+
+ var targetContainers = nextDiscreteEvent.targetContainers;
+
+ while (targetContainers.length > 0) {
+ var targetContainer = targetContainers[0];
+ var nextBlockedOn = findInstanceBlockingEvent(
+ nextDiscreteEvent.domEventName,
+ nextDiscreteEvent.eventSystemFlags,
+ targetContainer,
+ nextDiscreteEvent.nativeEvent
+ );
+
+ if (nextBlockedOn === null) {
+ // This whole function is in !enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
+ // so we don't need the new replay behavior code branch.
+ setReplayingEvent(nextDiscreteEvent.nativeEvent);
+ dispatchEventForPluginEventSystem(
+ nextDiscreteEvent.domEventName,
+ nextDiscreteEvent.eventSystemFlags,
+ nextDiscreteEvent.nativeEvent,
+ return_targetInst,
+ targetContainer
+ );
+ resetReplayingEvent();
+ } else {
+ // We're still blocked. Try again later.
+ nextDiscreteEvent.blockedOn = nextBlockedOn;
+ break;
+ } // This target container was successfully dispatched. Try the next.
+
+ targetContainers.shift();
+ }
+
+ if (nextDiscreteEvent.blockedOn === null) {
+ // We've successfully replayed the first event. Let's try the next one.
+ queuedDiscreteEvents.shift();
+ }
}
+ } // Next replay any continuous events.
+
+ if (queuedFocus !== null && attemptReplayContinuousQueuedEvent(queuedFocus)) {
+ queuedFocus = null;
}
-}
-function markRetryLaneImpl(fiber, retryLane) {
- var suspenseState = fiber.memoizedState;
+ if (queuedDrag !== null && attemptReplayContinuousQueuedEvent(queuedDrag)) {
+ queuedDrag = null;
+ }
- if (suspenseState !== null && suspenseState.dehydrated !== null) {
- suspenseState.retryLane = higherPriorityLane(
- suspenseState.retryLane,
- retryLane
- );
+ if (queuedMouse !== null && attemptReplayContinuousQueuedEvent(queuedMouse)) {
+ queuedMouse = null;
}
-} // Increases the priority of thenables when they resolve within this boundary.
-function markRetryLaneIfNotHydrated(fiber, retryLane) {
- markRetryLaneImpl(fiber, retryLane);
- var alternate = fiber.alternate;
+ queuedPointers.forEach(attemptReplayContinuousQueuedEventInMap);
+ queuedPointerCaptures.forEach(attemptReplayContinuousQueuedEventInMap);
+}
- if (alternate) {
- markRetryLaneImpl(alternate, retryLane);
+function scheduleCallbackIfUnblocked(queuedEvent, unblocked) {
+ if (queuedEvent.blockedOn === unblocked) {
+ queuedEvent.blockedOn = null;
+
+ if (!hasScheduledReplayAttempt) {
+ hasScheduledReplayAttempt = true; // Schedule a callback to attempt replaying as many events as are
+ // now unblocked. This first might not actually be unblocked yet.
+ // We could check it early to avoid scheduling an unnecessary callback.
+
+ Scheduler.unstable_scheduleCallback(
+ Scheduler.unstable_NormalPriority,
+ replayUnblockedEvents
+ );
+ }
}
}
-function attemptDiscreteHydration(fiber) {
- if (fiber.tag !== SuspenseComponent) {
- // We ignore HostRoots here because we can't increase
- // their priority and they should not suspend on I/O,
- // since you have to wrap anything that might suspend in
- // Suspense.
- return;
- }
+function retryIfBlockedOn(unblocked) {
+ // Mark anything that was blocked on this as no longer blocked
+ // and eligible for a replay.
+ if (queuedDiscreteEvents.length > 0) {
+ scheduleCallbackIfUnblocked(queuedDiscreteEvents[0], unblocked); // This is a exponential search for each boundary that commits. I think it's
+ // worth it because we expect very few discrete events to queue up and once
+ // we are actually fully unblocked it will be fast to replay them.
- var lane = SyncLane;
- var root = enqueueConcurrentRenderForLane(fiber, lane);
+ for (var i = 1; i < queuedDiscreteEvents.length; i++) {
+ var queuedEvent = queuedDiscreteEvents[i];
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ if (queuedEvent.blockedOn === unblocked) {
+ queuedEvent.blockedOn = null;
+ }
+ }
}
- markRetryLaneIfNotHydrated(fiber, lane);
-}
-function attemptContinuousHydration(fiber) {
- if (fiber.tag !== SuspenseComponent) {
- // We ignore HostRoots here because we can't increase
- // their priority and they should not suspend on I/O,
- // since you have to wrap anything that might suspend in
- // Suspense.
- return;
+ if (queuedFocus !== null) {
+ scheduleCallbackIfUnblocked(queuedFocus, unblocked);
}
- var lane = SelectiveHydrationLane;
- var root = enqueueConcurrentRenderForLane(fiber, lane);
-
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
+ if (queuedDrag !== null) {
+ scheduleCallbackIfUnblocked(queuedDrag, unblocked);
}
- markRetryLaneIfNotHydrated(fiber, lane);
-}
-function attemptHydrationAtCurrentPriority(fiber) {
- if (fiber.tag !== SuspenseComponent) {
- // We ignore HostRoots here because we can't increase
- // their priority other than synchronously flush it.
- return;
+ if (queuedMouse !== null) {
+ scheduleCallbackIfUnblocked(queuedMouse, unblocked);
}
- var lane = requestUpdateLane(fiber);
- var root = enqueueConcurrentRenderForLane(fiber, lane);
+ var unblock = function (queuedEvent) {
+ return scheduleCallbackIfUnblocked(queuedEvent, unblocked);
+ };
- if (root !== null) {
- var eventTime = requestEventTime();
- scheduleUpdateOnFiber(root, fiber, lane, eventTime);
- }
+ queuedPointers.forEach(unblock);
+ queuedPointerCaptures.forEach(unblock);
- markRetryLaneIfNotHydrated(fiber, lane);
-}
-function findHostInstanceWithNoPortals(fiber) {
- var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
+ for (var _i = 0; _i < queuedExplicitHydrationTargets.length; _i++) {
+ var queuedTarget = queuedExplicitHydrationTargets[_i];
- if (hostFiber === null) {
- return null;
+ if (queuedTarget.blockedOn === unblocked) {
+ queuedTarget.blockedOn = null;
+ }
}
- return getPublicInstance(hostFiber.stateNode);
-}
+ while (queuedExplicitHydrationTargets.length > 0) {
+ var nextExplicitTarget = queuedExplicitHydrationTargets[0];
-var shouldErrorImpl = function (fiber) {
- return null;
-};
+ if (nextExplicitTarget.blockedOn !== null) {
+ // We're still blocked.
+ break;
+ } else {
+ attemptExplicitHydrationTarget(nextExplicitTarget);
-function shouldError(fiber) {
- return shouldErrorImpl(fiber);
+ if (nextExplicitTarget.blockedOn === null) {
+ // We're unblocked.
+ queuedExplicitHydrationTargets.shift();
+ }
+ }
+ }
}
-var shouldSuspendImpl = function (fiber) {
- return false;
-};
+var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; // TODO: can we stop exporting these?
-function shouldSuspend(fiber) {
- return shouldSuspendImpl(fiber);
+var _enabled = true; // This is exported in FB builds for use by legacy FB layer infra.
+// We'd like to remove this but it's not clear if this is safe.
+
+function setEnabled(enabled) {
+ _enabled = !!enabled;
}
-var overrideHookState = null;
-var overrideHookStateDeletePath = null;
-var overrideHookStateRenamePath = null;
-var overrideProps = null;
-var overridePropsDeletePath = null;
-var overridePropsRenamePath = null;
-var scheduleUpdate = null;
-var setErrorHandler = null;
-var setSuspenseHandler = null;
+function isEnabled() {
+ return _enabled;
+}
+function createEventListenerWrapperWithPriority(
+ targetContainer,
+ domEventName,
+ eventSystemFlags
+) {
+ var eventPriority = getEventPriority(domEventName);
+ var listenerWrapper;
-{
- var copyWithDeleteImpl = function (obj, path, index) {
- var key = path[index];
- var updated = isArray(obj) ? obj.slice() : assign({}, obj);
+ switch (eventPriority) {
+ case DiscreteEventPriority:
+ listenerWrapper = dispatchDiscreteEvent;
+ break;
- if (index + 1 === path.length) {
- if (isArray(updated)) {
- updated.splice(key, 1);
- } else {
- delete updated[key];
- }
+ case ContinuousEventPriority:
+ listenerWrapper = dispatchContinuousEvent;
+ break;
- return updated;
- } // $FlowFixMe number or string is fine here
+ case DefaultEventPriority:
+ default:
+ listenerWrapper = dispatchEvent;
+ break;
+ }
- updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
- return updated;
- };
+ return listenerWrapper.bind(
+ null,
+ domEventName,
+ eventSystemFlags,
+ targetContainer
+ );
+}
- var copyWithDelete = function (obj, path) {
- return copyWithDeleteImpl(obj, path, 0);
- };
+function dispatchDiscreteEvent(
+ domEventName,
+ eventSystemFlags,
+ container,
+ nativeEvent
+) {
+ var previousPriority = getCurrentUpdatePriority();
+ var prevTransition = ReactCurrentBatchConfig.transition;
+ ReactCurrentBatchConfig.transition = null;
- var copyWithRenameImpl = function (obj, oldPath, newPath, index) {
- var oldKey = oldPath[index];
- var updated = isArray(obj) ? obj.slice() : assign({}, obj);
+ try {
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig.transition = prevTransition;
+ }
+}
- if (index + 1 === oldPath.length) {
- var newKey = newPath[index]; // $FlowFixMe number or string is fine here
+function dispatchContinuousEvent(
+ domEventName,
+ eventSystemFlags,
+ container,
+ nativeEvent
+) {
+ var previousPriority = getCurrentUpdatePriority();
+ var prevTransition = ReactCurrentBatchConfig.transition;
+ ReactCurrentBatchConfig.transition = null;
- updated[newKey] = updated[oldKey];
+ try {
+ setCurrentUpdatePriority(ContinuousEventPriority);
+ dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
+ } finally {
+ setCurrentUpdatePriority(previousPriority);
+ ReactCurrentBatchConfig.transition = prevTransition;
+ }
+}
+
+function dispatchEvent(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ if (!_enabled) {
+ return;
+ }
+
+ if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
+ dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
+ } else {
+ dispatchEventOriginal(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
+ }
+}
- if (isArray(updated)) {
- updated.splice(oldKey, 1);
- } else {
- delete updated[oldKey];
- }
- } else {
- // $FlowFixMe number or string is fine here
- updated[oldKey] = copyWithRenameImpl(
- // $FlowFixMe number or string is fine here
- obj[oldKey],
- oldPath,
- newPath,
- index + 1
- );
- }
+function dispatchEventOriginal(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ // TODO: replaying capture phase events is currently broken
+ // because we used to do it during top-level native bubble handlers
+ // but now we use different bubble and capture handlers.
+ // In eager mode, we attach capture listeners early, so we need
+ // to filter them out until we fix the logic to handle them correctly.
+ var allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0;
- return updated;
- };
+ if (
+ allowReplay &&
+ hasQueuedDiscreteEvents() &&
+ isDiscreteEventThatRequiresHydration(domEventName)
+ ) {
+ // If we already have a queue of discrete events, and this is another discrete
+ // event, then we can't dispatch it regardless of its target, since they
+ // need to dispatch in order.
+ queueDiscreteEvent(
+ null, // Flags that we're not actually blocked on anything as far as we know.
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
+ return;
+ }
- var copyWithRename = function (obj, oldPath, newPath) {
- if (oldPath.length !== newPath.length) {
- warn("copyWithRename() expects paths of the same length");
+ var blockedOn = findInstanceBlockingEvent(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
- return;
- } else {
- for (var i = 0; i < newPath.length - 1; i++) {
- if (oldPath[i] !== newPath[i]) {
- warn(
- "copyWithRename() expects paths to be the same except for the deepest key"
- );
+ if (blockedOn === null) {
+ dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ return_targetInst,
+ targetContainer
+ );
- return;
- }
- }
+ if (allowReplay) {
+ clearIfContinuousEvent(domEventName, nativeEvent);
}
- return copyWithRenameImpl(obj, oldPath, newPath, 0);
- };
+ return;
+ }
- var copyWithSetImpl = function (obj, path, index, value) {
- if (index >= path.length) {
- return value;
+ if (allowReplay) {
+ if (isDiscreteEventThatRequiresHydration(domEventName)) {
+ // This to be replayed later once the target is available.
+ queueDiscreteEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
+ return;
}
- var key = path[index];
- var updated = isArray(obj) ? obj.slice() : assign({}, obj); // $FlowFixMe number or string is fine here
-
- updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
- return updated;
- };
+ if (
+ queueIfContinuousEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ )
+ ) {
+ return;
+ } // We need to clear only if we didn't queue because
+ // queueing is accumulative.
- var copyWithSet = function (obj, path, value) {
- return copyWithSetImpl(obj, path, 0, value);
- };
+ clearIfContinuousEvent(domEventName, nativeEvent);
+ } // This is not replayable so we'll invoke it but without a target,
+ // in case the event system needs to trace it.
- var findHook = function (fiber, id) {
- // For now, the "id" of stateful hooks is just the stateful hook index.
- // This may change in the future with e.g. nested hooks.
- var currentHook = fiber.memoizedState;
+ dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ null,
+ targetContainer
+ );
+}
- while (currentHook !== null && id > 0) {
- currentHook = currentHook.next;
- id--;
- }
+function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ var blockedOn = findInstanceBlockingEvent(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
- return currentHook;
- }; // Support DevTools editable values for useState and useReducer.
+ if (blockedOn === null) {
+ dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ return_targetInst,
+ targetContainer
+ );
+ clearIfContinuousEvent(domEventName, nativeEvent);
+ return;
+ }
- overrideHookState = function (fiber, id, path, value) {
- var hook = findHook(fiber, id);
+ if (
+ queueIfContinuousEvent(
+ blockedOn,
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ )
+ ) {
+ nativeEvent.stopPropagation();
+ return;
+ } // We need to clear only if we didn't queue because
+ // queueing is accumulative.
- if (hook !== null) {
- var newState = copyWithSet(hook.memoizedState, path, value);
- hook.memoizedState = newState;
- hook.baseState = newState; // We aren't actually adding an update to the queue,
- // because there is no update we can add for useReducer hooks that won't trigger an error.
- // (There's no appropriate action type for DevTools overrides.)
- // As a result though, React will see the scheduled update as a noop and bailout.
- // Shallow cloning props works as a workaround for now to bypass the bailout check.
+ clearIfContinuousEvent(domEventName, nativeEvent);
- fiber.memoizedProps = assign({}, fiber.memoizedProps);
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ if (
+ eventSystemFlags & IS_CAPTURE_PHASE &&
+ isDiscreteEventThatRequiresHydration(domEventName)
+ ) {
+ while (blockedOn !== null) {
+ var fiber = getInstanceFromNode(blockedOn);
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ if (fiber !== null) {
+ attemptSynchronousHydration(fiber);
}
- }
- };
-
- overrideHookStateDeletePath = function (fiber, id, path) {
- var hook = findHook(fiber, id);
- if (hook !== null) {
- var newState = copyWithDelete(hook.memoizedState, path);
- hook.memoizedState = newState;
- hook.baseState = newState; // We aren't actually adding an update to the queue,
- // because there is no update we can add for useReducer hooks that won't trigger an error.
- // (There's no appropriate action type for DevTools overrides.)
- // As a result though, React will see the scheduled update as a noop and bailout.
- // Shallow cloning props works as a workaround for now to bypass the bailout check.
+ var nextBlockedOn = findInstanceBlockingEvent(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+ );
- fiber.memoizedProps = assign({}, fiber.memoizedProps);
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ if (nextBlockedOn === null) {
+ dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ return_targetInst,
+ targetContainer
+ );
+ }
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ if (nextBlockedOn === blockedOn) {
+ break;
}
+
+ blockedOn = nextBlockedOn;
}
- };
- overrideHookStateRenamePath = function (fiber, id, oldPath, newPath) {
- var hook = findHook(fiber, id);
+ if (blockedOn !== null) {
+ nativeEvent.stopPropagation();
+ }
- if (hook !== null) {
- var newState = copyWithRename(hook.memoizedState, oldPath, newPath);
- hook.memoizedState = newState;
- hook.baseState = newState; // We aren't actually adding an update to the queue,
- // because there is no update we can add for useReducer hooks that won't trigger an error.
- // (There's no appropriate action type for DevTools overrides.)
- // As a result though, React will see the scheduled update as a noop and bailout.
- // Shallow cloning props works as a workaround for now to bypass the bailout check.
+ return;
+ } // This is not replayable so we'll invoke it but without a target,
+ // in case the event system needs to trace it.
- fiber.memoizedProps = assign({}, fiber.memoizedProps);
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ dispatchEventForPluginEventSystem(
+ domEventName,
+ eventSystemFlags,
+ nativeEvent,
+ null,
+ targetContainer
+ );
+}
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
- }
- }
- }; // Support DevTools props for function components, forwardRef, memo, host components, etc.
+var return_targetInst = null; // Returns a SuspenseInstance or Container if it's blocked.
+// The return_targetInst field above is conceptually part of the return value.
- overrideProps = function (fiber, path, value) {
- fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
+function findInstanceBlockingEvent(
+ domEventName,
+ eventSystemFlags,
+ targetContainer,
+ nativeEvent
+) {
+ // TODO: Warn if _enabled is false.
+ return_targetInst = null;
+ var nativeEventTarget = getEventTarget(nativeEvent);
+ var targetInst = getClosestInstanceFromNode(nativeEventTarget);
- if (fiber.alternate) {
- fiber.alternate.pendingProps = fiber.pendingProps;
- }
+ if (targetInst !== null) {
+ var nearestMounted = getNearestMountedFiber(targetInst);
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ if (nearestMounted === null) {
+ // This tree has been unmounted already. Dispatch without a target.
+ targetInst = null;
+ } else {
+ var tag = nearestMounted.tag;
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
- }
- };
+ if (tag === SuspenseComponent) {
+ var instance = getSuspenseInstanceFromFiber(nearestMounted);
- overridePropsDeletePath = function (fiber, path) {
- fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
+ if (instance !== null) {
+ // Queue the event to be replayed later. Abort dispatching since we
+ // don't want this event dispatched twice through the event system.
+ // TODO: If this is the first discrete event in the queue. Schedule an increased
+ // priority for this boundary.
+ return instance;
+ } // This shouldn't happen, something went wrong but to avoid blocking
+ // the whole system, dispatch the event without a target.
+ // TODO: Warn.
- if (fiber.alternate) {
- fiber.alternate.pendingProps = fiber.pendingProps;
- }
+ targetInst = null;
+ } else if (tag === HostRoot) {
+ var root = nearestMounted.stateNode;
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ if (isRootDehydrated(root)) {
+ // If this happens during a replay something went wrong and it might block
+ // the whole system.
+ return getContainerFromFiber(nearestMounted);
+ }
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
+ targetInst = null;
+ } else if (nearestMounted !== targetInst) {
+ // If we get an event (ex: img onload) before committing that
+ // component's mount, ignore it for now (that is, treat it as if it was an
+ // event on a non-React tree). We might also consider queueing events and
+ // dispatching them after the mount.
+ targetInst = null;
+ }
}
- };
+ }
- overridePropsRenamePath = function (fiber, oldPath, newPath) {
- fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
+ return_targetInst = targetInst; // We're not blocked on anything.
- if (fiber.alternate) {
- fiber.alternate.pendingProps = fiber.pendingProps;
- }
+ return null;
+}
+function getEventPriority(domEventName) {
+ switch (domEventName) {
+ // Used by SimpleEventPlugin:
+ case "cancel":
+ case "click":
+ case "close":
+ case "contextmenu":
+ case "copy":
+ case "cut":
+ case "auxclick":
+ case "dblclick":
+ case "dragend":
+ case "dragstart":
+ case "drop":
+ case "focusin":
+ case "focusout":
+ case "input":
+ case "invalid":
+ case "keydown":
+ case "keypress":
+ case "keyup":
+ case "mousedown":
+ case "mouseup":
+ case "paste":
+ case "pause":
+ case "play":
+ case "pointercancel":
+ case "pointerdown":
+ case "pointerup":
+ case "ratechange":
+ case "reset":
+ case "resize":
+ case "seeked":
+ case "submit":
+ case "touchcancel":
+ case "touchend":
+ case "touchstart":
+ case "volumechange": // Used by polyfills:
+ // eslint-disable-next-line no-fallthrough
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ case "change":
+ case "selectionchange":
+ case "textInput":
+ case "compositionstart":
+ case "compositionend":
+ case "compositionupdate": // Only enableCreateEventHandleAPI:
+ // eslint-disable-next-line no-fallthrough
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
- }
- };
+ case "beforeblur":
+ case "afterblur": // Not used by React but could be by user code:
+ // eslint-disable-next-line no-fallthrough
- scheduleUpdate = function (fiber) {
- var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
+ case "beforeinput":
+ case "blur":
+ case "fullscreenchange":
+ case "focus":
+ case "hashchange":
+ case "popstate":
+ case "select":
+ case "selectstart":
+ return DiscreteEventPriority;
- if (root !== null) {
- scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
- }
- };
+ case "drag":
+ case "dragenter":
+ case "dragexit":
+ case "dragleave":
+ case "dragover":
+ case "mousemove":
+ case "mouseout":
+ case "mouseover":
+ case "pointermove":
+ case "pointerout":
+ case "pointerover":
+ case "scroll":
+ case "toggle":
+ case "touchmove":
+ case "wheel": // Not used by React but could be by user code:
+ // eslint-disable-next-line no-fallthrough
- setErrorHandler = function (newShouldErrorImpl) {
- shouldErrorImpl = newShouldErrorImpl;
- };
+ case "mouseenter":
+ case "mouseleave":
+ case "pointerenter":
+ case "pointerleave":
+ return ContinuousEventPriority;
- setSuspenseHandler = function (newShouldSuspendImpl) {
- shouldSuspendImpl = newShouldSuspendImpl;
- };
-}
+ case "message": {
+ // We might be in the Scheduler callback.
+ // Eventually this mechanism will be replaced by a check
+ // of the current priority on the native scheduler.
+ var schedulerPriority = getCurrentPriorityLevel();
-function findHostInstanceByFiber(fiber) {
- var hostFiber = findCurrentHostFiber(fiber);
+ switch (schedulerPriority) {
+ case ImmediatePriority:
+ return DiscreteEventPriority;
- if (hostFiber === null) {
- return null;
- }
+ case UserBlockingPriority:
+ return ContinuousEventPriority;
- return hostFiber.stateNode;
-}
+ case NormalPriority$1:
+ case LowPriority:
+ // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
+ return DefaultEventPriority;
-function emptyFindFiberByHostInstance(instance) {
- return null;
-}
+ case IdlePriority:
+ return IdleEventPriority;
-function getCurrentFiberForDevTools() {
- return current;
-}
+ default:
+ return DefaultEventPriority;
+ }
+ }
-function injectIntoDevTools(devToolsConfig) {
- var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
- var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
- return injectInternals({
- bundleType: devToolsConfig.bundleType,
- version: devToolsConfig.version,
- rendererPackageName: devToolsConfig.rendererPackageName,
- rendererConfig: devToolsConfig.rendererConfig,
- overrideHookState: overrideHookState,
- overrideHookStateDeletePath: overrideHookStateDeletePath,
- overrideHookStateRenamePath: overrideHookStateRenamePath,
- overrideProps: overrideProps,
- overridePropsDeletePath: overridePropsDeletePath,
- overridePropsRenamePath: overridePropsRenamePath,
- setErrorHandler: setErrorHandler,
- setSuspenseHandler: setSuspenseHandler,
- scheduleUpdate: scheduleUpdate,
- currentDispatcherRef: ReactCurrentDispatcher,
- findHostInstanceByFiber: findHostInstanceByFiber,
- findFiberByHostInstance:
- findFiberByHostInstance || emptyFindFiberByHostInstance,
- // React Refresh
- findHostInstancesForRefresh: findHostInstancesForRefresh,
- scheduleRefresh: scheduleRefresh,
- scheduleRoot: scheduleRoot,
- setRefreshHandler: setRefreshHandler,
- // Enables DevTools to append owner stacks to error messages in DEV mode.
- getCurrentFiber: getCurrentFiberForDevTools,
- // Enables DevTools to detect reconciler version rather than renderer version
- // which may not match for third party renderers.
- reconcilerVersion: ReactVersion
- });
+ default:
+ return DefaultEventPriority;
+ }
}
var Dispatcher = Internals.Dispatcher;
@@ -44116,13 +44059,6 @@ function preinit() {
// so we favor silent bailout over warning or erroring.
}
-setAttemptSynchronousHydration(attemptSynchronousHydration);
-setAttemptDiscreteHydration(attemptDiscreteHydration);
-setAttemptContinuousHydration(attemptContinuousHydration);
-setAttemptHydrationAtCurrentPriority(attemptHydrationAtCurrentPriority);
-setGetCurrentUpdatePriority(getCurrentUpdatePriority$1);
-setAttemptHydrationAtPriority(runWithPriority);
-
{
if (
typeof Map !== "function" || // $FlowFixMe Flow incorrectly thinks Map has no prototype
@@ -44140,9 +44076,6 @@ setAttemptHydrationAtPriority(runWithPriority);
}
}
-setRestoreImplementation(restoreControlledState);
-setBatchingImplementation(batchedUpdates, discreteUpdates, flushSync$1);
-
function createPortal(children, container) {
var key =
arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
@@ -44219,7 +44152,7 @@ Internals.Events = [
getFiberCurrentPropsFromNode,
enqueueStateRestore,
restoreStateIfNeeded,
- batchedUpdates
+ batchedUpdates$1
];
var foundDevTools = injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
@@ -44274,7 +44207,7 @@ exports.preinit = preinit;
exports.preload = preload;
exports.render = render;
exports.unmountComponentAtNode = unmountComponentAtNode;
-exports.unstable_batchedUpdates = batchedUpdates;
+exports.unstable_batchedUpdates = batchedUpdates$1;
exports.unstable_createEventHandle = createEventHandle;
exports.unstable_flushControlled = flushControlled;
exports.unstable_renderSubtreeIntoContainer = renderSubtreeIntoContainer;
diff --git a/compiled/facebook-www/ReactDOM-dev.modern.js b/compiled/facebook-www/ReactDOM-dev.modern.js
index de04fbe9d85b6..8d577f09ce6f5 100644
--- a/compiled/facebook-www/ReactDOM-dev.modern.js
+++ b/compiled/facebook-www/ReactDOM-dev.modern.js
@@ -141,6 +141,7 @@ var dynamicFeatureFlags = require("ReactFeatureFlags");
var disableInputAttributeSyncing =
dynamicFeatureFlags.disableInputAttributeSyncing,
+ disableIEWorkarounds = dynamicFeatureFlags.disableIEWorkarounds,
enableTrustedTypesIntegration =
dynamicFeatureFlags.enableTrustedTypesIntegration,
replayFailedUnitOfWorkWithInvokeGuardedCallback =
@@ -5109,7 +5110,6 @@ function getEventTarget(nativeEvent) {
return target.nodeType === TEXT_NODE ? target.parentNode : target;
}
-var restoreImpl = null;
var restoreTarget = null;
var restoreQueue = null;
@@ -5123,24 +5123,18 @@ function restoreStateOfTarget(target) {
return;
}
- if (typeof restoreImpl !== "function") {
- throw new Error(
- "setRestoreImplementation() needs to be called to handle a target for controlled " +
- "events. This error is likely caused by a bug in React. Please file an issue."
- );
- }
-
var stateNode = internalInstance.stateNode; // Guard against Fiber being unmounted.
if (stateNode) {
var props = getFiberCurrentPropsFromNode(stateNode);
- restoreImpl(internalInstance.stateNode, internalInstance.type, props);
+ restoreControlledState(
+ internalInstance.stateNode,
+ internalInstance.type,
+ props
+ );
}
}
-function setRestoreImplementation(impl) {
- restoreImpl = impl;
-}
function enqueueStateRestore(target) {
if (restoreTarget) {
if (restoreQueue) {
@@ -5173,256 +5167,6 @@ function restoreStateIfNeeded() {
}
}
-// 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 batchedUpdatesImpl = function (fn, bookkeeping) {
- return fn(bookkeeping);
-};
-
-var flushSyncImpl = function () {};
-
-var isInsideEventHandler = false;
-
-function finishEventHandler() {
- // 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.
- var controlledComponentsHavePendingUpdates = needsStateRestore();
-
- if (controlledComponentsHavePendingUpdates) {
- // If a controlled event was fired, we may need to restore the state of
- // the DOM node back to the controlled value. This is necessary when React
- // bails out of the update without touching the DOM.
- // TODO: Restore state in the microtask, after the discrete updates flush,
- // instead of early flushing them here.
- flushSyncImpl();
- restoreStateIfNeeded();
- }
-}
-
-function batchedUpdates$1(fn, a, b) {
- if (isInsideEventHandler) {
- // If we are currently inside another batch, we need to wait until it
- // fully completes before restoring state.
- return fn(a, b);
- }
-
- isInsideEventHandler = true;
-
- try {
- return batchedUpdatesImpl(fn, a, b);
- } finally {
- isInsideEventHandler = false;
- finishEventHandler();
- }
-} // TODO: Replace with flushSync
-function setBatchingImplementation(
- _batchedUpdatesImpl,
- _discreteUpdatesImpl,
- _flushSyncImpl
-) {
- batchedUpdatesImpl = _batchedUpdatesImpl;
- flushSyncImpl = _flushSyncImpl;
-}
-
-function isInteractive(tag) {
- return (
- tag === "button" ||
- tag === "input" ||
- tag === "select" ||
- tag === "textarea"
- );
-}
-
-function shouldPreventMouseEvent(name, type, props) {
- switch (name) {
- case "onClick":
- case "onClickCapture":
- case "onDoubleClick":
- case "onDoubleClickCapture":
- case "onMouseDown":
- case "onMouseDownCapture":
- case "onMouseMove":
- case "onMouseMoveCapture":
- case "onMouseUp":
- case "onMouseUpCapture":
- case "onMouseEnter":
- return !!(props.disabled && isInteractive(type));
-
- default:
- return false;
- }
-}
-/**
- * @param {object} inst The instance, which is the source of events.
- * @param {string} registrationName Name of listener (e.g. `onClick`).
- * @return {?function} The stored callback.
- */
-
-function getListener(inst, registrationName) {
- var stateNode = inst.stateNode;
-
- if (stateNode === null) {
- // Work in progress (ex: onload events in incremental mode).
- return null;
- }
-
- var props = getFiberCurrentPropsFromNode(stateNode);
-
- if (props === null) {
- // Work in progress.
- return null;
- }
-
- var listener = props[registrationName];
-
- if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
- return null;
- }
-
- if (listener && typeof listener !== "function") {
- throw new Error(
- "Expected `" +
- registrationName +
- "` listener to be a function, instead got a value of `" +
- typeof listener +
- "` type."
- );
- }
-
- return listener;
-}
-
-var passiveBrowserEventsSupported = false; // Check if browser support events with passive listeners
-// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
-
-if (canUseDOM) {
- try {
- var options = {};
- Object.defineProperty(options, "passive", {
- get: function () {
- passiveBrowserEventsSupported = true;
- }
- });
- window.addEventListener("test", options, options);
- window.removeEventListener("test", options, options);
- } catch (e) {
- passiveBrowserEventsSupported = false;
- }
-}
-
-// Provided by www
-var ReactFbErrorUtils = require("ReactFbErrorUtils");
-
-if (typeof ReactFbErrorUtils.invokeGuardedCallback !== "function") {
- throw new Error(
- "Expected ReactFbErrorUtils.invokeGuardedCallback to be a function."
- );
-}
-
-function invokeGuardedCallbackImpl(name, func, context, a, b, c, d, e, f) {
- // This will call `this.onError(err)` if an error was caught.
- ReactFbErrorUtils.invokeGuardedCallback.apply(this, arguments);
-}
-
-var hasError = false;
-var caughtError = null; // Used by event system to capture/rethrow the first error.
-
-var hasRethrowError = false;
-var rethrowError = null;
-var reporter = {
- onError: function (error) {
- hasError = true;
- caughtError = error;
- }
-};
-/**
- * Call a function while guarding against errors that happens within it.
- * Returns an error if it throws, otherwise null.
- *
- * In production, this is implemented using a try-catch. The reason we don't
- * use a try-catch directly is so that we can swap out a different
- * implementation in DEV mode.
- *
- * @param {String} name of the guard to use for logging or debugging
- * @param {Function} func The function to invoke
- * @param {*} context The context to use when calling the function
- * @param {...*} args Arguments for function
- */
-
-function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
- hasError = false;
- caughtError = null;
- invokeGuardedCallbackImpl.apply(reporter, 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
- */
-
-function invokeGuardedCallbackAndCatchFirstError(
- name,
- func,
- context,
- a,
- b,
- c,
- d,
- e,
- f
-) {
- invokeGuardedCallback.apply(this, arguments);
-
- if (hasError) {
- var error = clearCaughtError();
-
- if (!hasRethrowError) {
- hasRethrowError = true;
- 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.
- */
-
-function rethrowCaughtError() {
- if (hasRethrowError) {
- var error = rethrowError;
- hasRethrowError = false;
- rethrowError = null;
- throw error;
- }
-}
-function hasCaughtError() {
- return hasError;
-}
-function clearCaughtError() {
- if (hasError) {
- var error = caughtError;
- hasError = false;
- caughtError = null;
- return error;
- } else {
- throw new Error(
- "clearCaughtError was called but no error was captured. This error " +
- "is likely caused by a bug in React. Please file an issue."
- );
- }
-}
-
/**
* `ReactInstanceMap` maintains a mapping from a public facing stateful
* instance (key) and the internal representation (value). This allows public
@@ -5879,477 +5623,206 @@ function doesFiberContain(parentFiber, childFiber) {
return false;
}
-// This module only exists as an ESM wrapper around the external CommonJS
-var scheduleCallback$2 = Scheduler.unstable_scheduleCallback;
-var cancelCallback$1 = Scheduler.unstable_cancelCallback;
-var shouldYield = Scheduler.unstable_shouldYield;
-var requestPaint = Scheduler.unstable_requestPaint;
-var now$1 = Scheduler.unstable_now;
-var getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel;
-var ImmediatePriority = Scheduler.unstable_ImmediatePriority;
-var UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
-var NormalPriority$1 = Scheduler.unstable_NormalPriority;
-var LowPriority = Scheduler.unstable_LowPriority;
-var IdlePriority = Scheduler.unstable_IdlePriority; // this doesn't actually exist on the scheduler, but it *does*
-// on scheduler/unstable_mock, which we'll need for internal testing
-
-var log$2 = Scheduler.log;
-var unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue;
+var loggedTypeFailures = {};
+var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
-var rendererID = null;
-var injectedHook = null;
-var injectedProfilingHooks = null;
-var hasLoggedError = false;
-var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined";
-function injectInternals(internals) {
- if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined") {
- // No DevTools
- return false;
+function setCurrentlyValidatingElement(element) {
+ {
+ if (element) {
+ var owner = element._owner;
+ var stack = describeUnknownElementTypeFrameInDEV(
+ element.type,
+ element._source,
+ owner ? owner.type : null
+ );
+ ReactDebugCurrentFrame.setExtraStackFrame(stack);
+ } else {
+ ReactDebugCurrentFrame.setExtraStackFrame(null);
+ }
}
+}
- var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
+function checkPropTypes(typeSpecs, values, location, componentName, element) {
+ {
+ // $FlowFixMe This is okay but Flow doesn't know it.
+ var has = Function.call.bind(hasOwnProperty);
- 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;
- }
+ for (var typeSpecName in typeSpecs) {
+ if (has(typeSpecs, typeSpecName)) {
+ var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
+ // fail the render phase where it didn't fail before. So we log it.
+ // After these have been cleaned up, we'll let them throw.
- if (!hook.supportsFiber) {
- {
- error(
- "The installed version of React DevTools is too old and will not work " +
- "with the current version of React. Please update React DevTools. " +
- "https://reactjs.org/link/react-devtools"
- );
- } // DevTools exists, even though it doesn't support Fiber.
+ try {
+ // This is intentionally an invariant that gets caught. It's the same
+ // behavior as without this statement except with a better message.
+ if (typeof typeSpecs[typeSpecName] !== "function") {
+ // eslint-disable-next-line react-internal/prod-error-codes
+ var err = Error(
+ (componentName || "React class") +
+ ": " +
+ location +
+ " type `" +
+ typeSpecName +
+ "` is invalid; " +
+ "it must be a function, usually from the `prop-types` package, but received `" +
+ typeof typeSpecs[typeSpecName] +
+ "`." +
+ "This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`."
+ );
+ err.name = "Invariant Violation";
+ throw err;
+ }
- return true;
- }
+ error$1 = typeSpecs[typeSpecName](
+ values,
+ typeSpecName,
+ componentName,
+ location,
+ null,
+ "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"
+ );
+ } catch (ex) {
+ error$1 = ex;
+ }
- try {
- if (enableSchedulingProfiler) {
- // Conditionally inject these hooks only if Timeline profiler is supported by this build.
- // This gives DevTools a way to feature detect that isn't tied to version number
- // (since profiling and timeline are controlled by different feature flags).
- internals = assign({}, internals, {
- getLaneLabelMap: getLaneLabelMap,
- injectProfilingHooks: injectProfilingHooks
- });
- }
+ if (error$1 && !(error$1 instanceof Error)) {
+ setCurrentlyValidatingElement(element);
- rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks.
+ error(
+ "%s: type specification of %s" +
+ " `%s` is invalid; the type checker " +
+ "function must return `null` or an `Error` but returned a %s. " +
+ "You may have forgotten to pass an argument to the type checker " +
+ "creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and " +
+ "shape all require an argument).",
+ componentName || "React class",
+ location,
+ typeSpecName,
+ typeof error$1
+ );
- injectedHook = hook;
- } catch (err) {
- // Catch all errors because it is unsafe to throw during initialization.
- {
- error("React instrumentation encountered an error: %s.", err);
- }
- }
+ setCurrentlyValidatingElement(null);
+ }
- if (hook.checkDCE) {
- // This is the real DevTools.
- return true;
- } else {
- // This is likely a hook installed by Fast Refresh runtime.
- return false;
- }
-}
-function onScheduleRoot(root, children) {
- {
- if (
- injectedHook &&
- typeof injectedHook.onScheduleFiberRoot === "function"
- ) {
- try {
- injectedHook.onScheduleFiberRoot(rendererID, root, children);
- } catch (err) {
- if (!hasLoggedError) {
- hasLoggedError = true;
+ if (
+ error$1 instanceof Error &&
+ !(error$1.message in loggedTypeFailures)
+ ) {
+ // Only monitor this failure once because there tends to be a lot of the
+ // same error.
+ loggedTypeFailures[error$1.message] = true;
+ setCurrentlyValidatingElement(element);
- error("React instrumentation encountered an error: %s", err);
+ error("Failed %s type: %s", location, error$1.message);
+
+ setCurrentlyValidatingElement(null);
}
}
}
}
}
-function onCommitRoot$1(root, eventPriority) {
- if (injectedHook && typeof injectedHook.onCommitFiberRoot === "function") {
- try {
- var didError = (root.current.flags & DidCapture) === DidCapture;
-
- if (enableProfilerTimer) {
- var schedulerPriority;
- switch (eventPriority) {
- case DiscreteEventPriority:
- schedulerPriority = ImmediatePriority;
- break;
+var valueStack = [];
+var fiberStack;
- case ContinuousEventPriority:
- schedulerPriority = UserBlockingPriority;
- break;
+{
+ fiberStack = [];
+}
- case DefaultEventPriority:
- schedulerPriority = NormalPriority$1;
- break;
+var index = -1;
- case IdleEventPriority:
- schedulerPriority = IdlePriority;
- break;
+function createCursor(defaultValue) {
+ return {
+ current: defaultValue
+ };
+}
- default:
- schedulerPriority = NormalPriority$1;
- break;
- }
+function pop(cursor, fiber) {
+ if (index < 0) {
+ {
+ error("Unexpected pop.");
+ }
- injectedHook.onCommitFiberRoot(
- rendererID,
- root,
- schedulerPriority,
- didError
- );
- }
- } catch (err) {
- {
- if (!hasLoggedError) {
- hasLoggedError = true;
+ return;
+ }
- error("React instrumentation encountered an error: %s", err);
- }
- }
+ {
+ if (fiber !== fiberStack[index]) {
+ error("Unexpected Fiber popped.");
}
}
-}
-function onPostCommitRoot(root) {
- if (
- injectedHook &&
- typeof injectedHook.onPostCommitFiberRoot === "function"
- ) {
- try {
- injectedHook.onPostCommitFiberRoot(rendererID, root);
- } catch (err) {
- {
- if (!hasLoggedError) {
- hasLoggedError = true;
- error("React instrumentation encountered an error: %s", err);
- }
- }
- }
+ cursor.current = valueStack[index];
+ valueStack[index] = null;
+
+ {
+ fiberStack[index] = null;
}
+
+ index--;
}
-function onCommitUnmount(fiber) {
- if (injectedHook && typeof injectedHook.onCommitFiberUnmount === "function") {
- try {
- injectedHook.onCommitFiberUnmount(rendererID, fiber);
- } catch (err) {
- {
- if (!hasLoggedError) {
- hasLoggedError = true;
- error("React instrumentation encountered an error: %s", err);
- }
- }
- }
+function push(cursor, value, fiber) {
+ index++;
+ valueStack[index] = cursor.current;
+
+ {
+ fiberStack[index] = fiber;
}
-}
-function setIsStrictModeForDevtools(newIsStrictMode) {
- {
- if (typeof log$2 === "function") {
- // We're in a test because Scheduler.log only exists
- // in SchedulerMock. To reduce the noise in strict mode tests,
- // suppress warnings and disable scheduler yielding during the double render
- unstable_setDisableYieldValue(newIsStrictMode);
- setSuppressWarning(newIsStrictMode);
- }
-
- if (injectedHook && typeof injectedHook.setStrictMode === "function") {
- try {
- injectedHook.setStrictMode(rendererID, newIsStrictMode);
- } catch (err) {
- {
- if (!hasLoggedError) {
- hasLoggedError = true;
-
- error("React instrumentation encountered an error: %s", err);
- }
- }
- }
- }
- }
-} // Profiler API hooks
-
-function injectProfilingHooks(profilingHooks) {
- injectedProfilingHooks = profilingHooks;
+
+ cursor.current = value;
}
-function getLaneLabelMap() {
- if (enableSchedulingProfiler) {
- var map = new Map();
- var lane = 1;
+var emptyContextObject = {};
- for (var index = 0; index < TotalLanes; index++) {
- var label = getLabelForLane(lane);
- map.set(lane, label);
- lane *= 2;
- }
+{
+ Object.freeze(emptyContextObject);
+} // A cursor to the current merged context object on the stack.
- return map;
- } else {
- return null;
+function hasContextChanged() {
+ {
+ return false;
}
}
-function markCommitStarted(lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markCommitStarted === "function"
- ) {
- injectedProfilingHooks.markCommitStarted(lanes);
- }
- }
-}
-function markCommitStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markCommitStopped === "function"
- ) {
- injectedProfilingHooks.markCommitStopped();
- }
- }
-}
-function markComponentRenderStarted(fiber) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentRenderStarted === "function"
- ) {
- injectedProfilingHooks.markComponentRenderStarted(fiber);
- }
- }
-}
-function markComponentRenderStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentRenderStopped === "function"
- ) {
- injectedProfilingHooks.markComponentRenderStopped();
- }
- }
-}
-function markComponentPassiveEffectMountStarted(fiber) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted ===
- "function"
- ) {
- injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber);
- }
- }
-}
-function markComponentPassiveEffectMountStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped ===
- "function"
- ) {
- injectedProfilingHooks.markComponentPassiveEffectMountStopped();
- }
- }
-}
-function markComponentPassiveEffectUnmountStarted(fiber) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted ===
- "function"
- ) {
- injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber);
- }
- }
-}
-function markComponentPassiveEffectUnmountStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped ===
- "function"
- ) {
- injectedProfilingHooks.markComponentPassiveEffectUnmountStopped();
- }
- }
-}
-function markComponentLayoutEffectMountStarted(fiber) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted ===
- "function"
- ) {
- injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber);
- }
- }
-}
-function markComponentLayoutEffectMountStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped ===
- "function"
- ) {
- injectedProfilingHooks.markComponentLayoutEffectMountStopped();
- }
- }
-}
-function markComponentLayoutEffectUnmountStarted(fiber) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted ===
- "function"
- ) {
- injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber);
- }
- }
-}
-function markComponentLayoutEffectUnmountStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped ===
- "function"
- ) {
- injectedProfilingHooks.markComponentLayoutEffectUnmountStopped();
- }
- }
-}
-function markComponentErrored(fiber, thrownValue, lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentErrored === "function"
- ) {
- injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes);
- }
- }
-}
-function markComponentSuspended(fiber, wakeable, lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markComponentSuspended === "function"
- ) {
- injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes);
- }
- }
-}
-function markLayoutEffectsStarted(lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markLayoutEffectsStarted === "function"
- ) {
- injectedProfilingHooks.markLayoutEffectsStarted(lanes);
- }
- }
-}
-function markLayoutEffectsStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markLayoutEffectsStopped === "function"
- ) {
- injectedProfilingHooks.markLayoutEffectsStopped();
- }
- }
-}
-function markPassiveEffectsStarted(lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markPassiveEffectsStarted === "function"
- ) {
- injectedProfilingHooks.markPassiveEffectsStarted(lanes);
- }
- }
-}
-function markPassiveEffectsStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markPassiveEffectsStopped === "function"
- ) {
- injectedProfilingHooks.markPassiveEffectsStopped();
- }
- }
-}
-function markRenderStarted(lanes) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markRenderStarted === "function"
- ) {
- injectedProfilingHooks.markRenderStarted(lanes);
- }
- }
-}
-function markRenderYielded() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markRenderYielded === "function"
- ) {
- injectedProfilingHooks.markRenderYielded();
- }
- }
-}
-function markRenderStopped() {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markRenderStopped === "function"
- ) {
- injectedProfilingHooks.markRenderStopped();
- }
+function isContextProvider(type) {
+ {
+ return false;
}
}
-function markRenderScheduled(lane) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markRenderScheduled === "function"
- ) {
- injectedProfilingHooks.markRenderScheduled(lane);
- }
+
+function processChildContext(fiber, type, parentContext) {
+ {
+ return parentContext;
}
}
-function markForceUpdateScheduled(fiber, lane) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markForceUpdateScheduled === "function"
- ) {
- injectedProfilingHooks.markForceUpdateScheduled(fiber, lane);
- }
+
+function findCurrentUnmaskedContext(fiber) {
+ {
+ return emptyContextObject;
}
}
-function markStateUpdateScheduled(fiber, lane) {
- if (enableSchedulingProfiler) {
- if (
- injectedProfilingHooks !== null &&
- typeof injectedProfilingHooks.markStateUpdateScheduled === "function"
- ) {
- injectedProfilingHooks.markStateUpdateScheduled(fiber, lane);
- }
- }
+
+var LegacyRoot = 0;
+var ConcurrentRoot = 1;
+
+// We use the existence of the state object as an indicator that the component
+// is hidden.
+var OffscreenVisible =
+ /* */
+ 1;
+var OffscreenDetached =
+ /* */
+ 2;
+var OffscreenPassiveEffectsConnected =
+ /* */
+ 4;
+function isOffscreenManual(offscreenFiber) {
+ return (
+ offscreenFiber.memoizedProps !== null &&
+ offscreenFiber.memoizedProps.mode === "manual"
+ );
}
var NoMode =
@@ -6380,7 +5853,7 @@ var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros.
// Based on:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
-var log$1 = Math.log;
+var log$2 = Math.log;
var LN2 = Math.LN2;
function clz32Fallback(x) {
@@ -6390,7 +5863,7 @@ function clz32Fallback(x) {
return 32;
}
- return (31 - ((log$1(asUint) / LN2) | 0)) | 0;
+ return (31 - ((log$2(asUint) / LN2) | 0)) | 0;
}
// If those values are changed that package should be rebuilt and redeployed.
@@ -7327,7 +6800,7 @@ var ContinuousEventPriority = InputContinuousLane;
var DefaultEventPriority = DefaultLane;
var IdleEventPriority = IdleLane;
var currentUpdatePriority = NoLane;
-function getCurrentUpdatePriority$1() {
+function getCurrentUpdatePriority() {
return currentUpdatePriority;
}
function setCurrentUpdatePriority(newPriority) {
@@ -7370,35426 +6843,35899 @@ function lanesToEventPriority(lanes) {
return IdleEventPriority;
}
-// This is imported by the event replaying implementation in React DOM. It's
-// in a separate file to break a circular dependency between the renderer and
-// the reconciler.
-function isRootDehydrated(root) {
- var currentState = root.current.memoizedState;
- return currentState.isDehydrated;
-}
+// This module only exists as an ESM wrapper around the external CommonJS
+var scheduleCallback$2 = Scheduler.unstable_scheduleCallback;
+var cancelCallback$1 = Scheduler.unstable_cancelCallback;
+var shouldYield = Scheduler.unstable_shouldYield;
+var requestPaint = Scheduler.unstable_requestPaint;
+var now$1 = Scheduler.unstable_now;
+var getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel;
+var ImmediatePriority = Scheduler.unstable_ImmediatePriority;
+var UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
+var NormalPriority$1 = Scheduler.unstable_NormalPriority;
+var LowPriority = Scheduler.unstable_LowPriority;
+var IdlePriority = Scheduler.unstable_IdlePriority; // this doesn't actually exist on the scheduler, but it *does*
+// on scheduler/unstable_mock, which we'll need for internal testing
-var _attemptSynchronousHydration;
+var log$1 = Scheduler.log;
+var unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue;
-function setAttemptSynchronousHydration(fn) {
- _attemptSynchronousHydration = fn;
-}
-function attemptSynchronousHydration$1(fiber) {
- _attemptSynchronousHydration(fiber);
-}
-var attemptDiscreteHydration$1;
-function setAttemptDiscreteHydration(fn) {
- attemptDiscreteHydration$1 = fn;
-}
-var attemptContinuousHydration$1;
-function setAttemptContinuousHydration(fn) {
- attemptContinuousHydration$1 = fn;
-}
-var attemptHydrationAtCurrentPriority$1;
-function setAttemptHydrationAtCurrentPriority(fn) {
- attemptHydrationAtCurrentPriority$1 = fn;
-}
-var getCurrentUpdatePriority;
-function setGetCurrentUpdatePriority(fn) {
- getCurrentUpdatePriority = fn;
-}
-var attemptHydrationAtPriority;
-function setAttemptHydrationAtPriority(fn) {
- attemptHydrationAtPriority = fn;
-} // TODO: Upgrade this definition once we're on a newer version of Flow that
-// has this definition built-in.
+var rendererID = null;
+var injectedHook = null;
+var injectedProfilingHooks = null;
+var hasLoggedError = false;
+var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== "undefined";
+function injectInternals(internals) {
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined") {
+ // No DevTools
+ return false;
+ }
-var hasScheduledReplayAttempt = false; // The queue of discrete events to be replayed.
+ var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
-var queuedDiscreteEvents = []; // Indicates if any continuous event targets are non-null for early bailout.
-// if the last target was dehydrated.
+ 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;
+ }
-var queuedFocus = null;
-var queuedDrag = null;
-var queuedMouse = null; // For pointer events there can be one latest event per pointerId.
+ if (!hook.supportsFiber) {
+ {
+ error(
+ "The installed version of React DevTools is too old and will not work " +
+ "with the current version of React. Please update React DevTools. " +
+ "https://reactjs.org/link/react-devtools"
+ );
+ } // DevTools exists, even though it doesn't support Fiber.
-var queuedPointers = new Map();
-var queuedPointerCaptures = new Map(); // We could consider replaying selectionchange and touchmoves too.
+ return true;
+ }
-var queuedExplicitHydrationTargets = [];
-function hasQueuedDiscreteEvents() {
- return queuedDiscreteEvents.length > 0;
-}
-var discreteReplayableEvents = [
- "mousedown",
- "mouseup",
- "touchcancel",
- "touchend",
- "touchstart",
- "auxclick",
- "dblclick",
- "pointercancel",
- "pointerdown",
- "pointerup",
- "dragend",
- "dragstart",
- "drop",
- "compositionend",
- "compositionstart",
- "keydown",
- "keypress",
- "keyup",
- "input",
- "textInput", // Intentionally camelCase
- "copy",
- "cut",
- "paste",
- "click",
- "change",
- "contextmenu",
- "reset",
- "submit"
-];
-function isDiscreteEventThatRequiresHydration(eventType) {
- return discreteReplayableEvents.indexOf(eventType) > -1;
-}
+ try {
+ if (enableSchedulingProfiler) {
+ // Conditionally inject these hooks only if Timeline profiler is supported by this build.
+ // This gives DevTools a way to feature detect that isn't tied to version number
+ // (since profiling and timeline are controlled by different feature flags).
+ internals = assign({}, internals, {
+ getLaneLabelMap: getLaneLabelMap,
+ injectProfilingHooks: injectProfilingHooks
+ });
+ }
-function createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- return {
- blockedOn: blockedOn,
- domEventName: domEventName,
- eventSystemFlags: eventSystemFlags,
- nativeEvent: nativeEvent,
- targetContainers: [targetContainer]
- };
-}
+ rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks.
-function queueDiscreteEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- return;
+ injectedHook = hook;
+ } catch (err) {
+ // Catch all errors because it is unsafe to throw during initialization.
+ {
+ error("React instrumentation encountered an error: %s.", err);
+ }
}
- var queuedEvent = createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- queuedDiscreteEvents.push(queuedEvent);
-
- if (queuedDiscreteEvents.length === 1) {
- // If this was the first discrete event, we might be able to
- // synchronously unblock it so that preventDefault still works.
- while (queuedEvent.blockedOn !== null) {
- var fiber = getInstanceFromNode$1(queuedEvent.blockedOn);
+ if (hook.checkDCE) {
+ // This is the real DevTools.
+ return true;
+ } else {
+ // This is likely a hook installed by Fast Refresh runtime.
+ return false;
+ }
+}
+function onScheduleRoot(root, children) {
+ {
+ if (
+ injectedHook &&
+ typeof injectedHook.onScheduleFiberRoot === "function"
+ ) {
+ try {
+ injectedHook.onScheduleFiberRoot(rendererID, root, children);
+ } catch (err) {
+ if (!hasLoggedError) {
+ hasLoggedError = true;
- if (fiber === null) {
- break;
+ error("React instrumentation encountered an error: %s", err);
+ }
}
+ }
+ }
+}
+function onCommitRoot$1(root, eventPriority) {
+ if (injectedHook && typeof injectedHook.onCommitFiberRoot === "function") {
+ try {
+ var didError = (root.current.flags & DidCapture) === DidCapture;
- attemptSynchronousHydration$1(fiber);
+ if (enableProfilerTimer) {
+ var schedulerPriority;
- if (queuedEvent.blockedOn === null) {
- // We got unblocked by hydration. Let's try again.
- replayUnblockedEvents(); // If we're reblocked, on an inner boundary, we might need
- // to attempt hydrating that one.
+ switch (eventPriority) {
+ case DiscreteEventPriority:
+ schedulerPriority = ImmediatePriority;
+ break;
- continue;
- } else {
- // We're still blocked from hydration, we have to give up
- // and replay later.
- break;
- }
- }
- }
-} // Resets the replaying for this type of continuous event to no event.
+ case ContinuousEventPriority:
+ schedulerPriority = UserBlockingPriority;
+ break;
-function clearIfContinuousEvent(domEventName, nativeEvent) {
- switch (domEventName) {
- case "focusin":
- case "focusout":
- queuedFocus = null;
- break;
+ case DefaultEventPriority:
+ schedulerPriority = NormalPriority$1;
+ break;
- case "dragenter":
- case "dragleave":
- queuedDrag = null;
- break;
+ case IdleEventPriority:
+ schedulerPriority = IdlePriority;
+ break;
- case "mouseover":
- case "mouseout":
- queuedMouse = null;
- break;
+ default:
+ schedulerPriority = NormalPriority$1;
+ break;
+ }
- case "pointerover":
- case "pointerout": {
- var pointerId = nativeEvent.pointerId;
- queuedPointers.delete(pointerId);
- break;
- }
+ injectedHook.onCommitFiberRoot(
+ rendererID,
+ root,
+ schedulerPriority,
+ didError
+ );
+ }
+ } catch (err) {
+ {
+ if (!hasLoggedError) {
+ hasLoggedError = true;
- case "gotpointercapture":
- case "lostpointercapture": {
- var _pointerId = nativeEvent.pointerId;
- queuedPointerCaptures.delete(_pointerId);
- break;
+ error("React instrumentation encountered an error: %s", err);
+ }
+ }
}
}
}
-
-function accumulateOrCreateContinuousQueuedReplayableEvent(
- existingQueuedEvent,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
+function onPostCommitRoot(root) {
if (
- existingQueuedEvent === null ||
- existingQueuedEvent.nativeEvent !== nativeEvent
+ injectedHook &&
+ typeof injectedHook.onPostCommitFiberRoot === "function"
) {
- var queuedEvent = createQueuedReplayableEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
-
- if (blockedOn !== null) {
- var fiber = getInstanceFromNode$1(blockedOn);
+ try {
+ injectedHook.onPostCommitFiberRoot(rendererID, root);
+ } catch (err) {
+ {
+ if (!hasLoggedError) {
+ hasLoggedError = true;
- if (fiber !== null) {
- // Attempt to increase the priority of this target.
- attemptContinuousHydration$1(fiber);
+ error("React instrumentation encountered an error: %s", err);
+ }
}
}
-
- return queuedEvent;
- } // If we have already queued this exact event, then it's because
- // the different event systems have different DOM event listeners.
- // We can accumulate the flags, and the targetContainers, and
- // store a single event to be replayed.
-
- existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
- var targetContainers = existingQueuedEvent.targetContainers;
-
- if (
- targetContainer !== null &&
- targetContainers.indexOf(targetContainer) === -1
- ) {
- targetContainers.push(targetContainer);
}
-
- return existingQueuedEvent;
}
+function onCommitUnmount(fiber) {
+ if (injectedHook && typeof injectedHook.onCommitFiberUnmount === "function") {
+ try {
+ injectedHook.onCommitFiberUnmount(rendererID, fiber);
+ } catch (err) {
+ {
+ if (!hasLoggedError) {
+ hasLoggedError = true;
-function queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // These set relatedTarget to null because the replayed event will be treated as if we
- // moved from outside the window (no target) onto the target once it hydrates.
- // Instead of mutating we could clone the event.
- switch (domEventName) {
- case "focusin": {
- var focusEvent = nativeEvent;
- queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedFocus,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- focusEvent
- );
- return true;
- }
-
- case "dragenter": {
- var dragEvent = nativeEvent;
- queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedDrag,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- dragEvent
- );
- return true;
+ error("React instrumentation encountered an error: %s", err);
+ }
+ }
}
-
- case "mouseover": {
- var mouseEvent = nativeEvent;
- queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedMouse,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- mouseEvent
- );
- return true;
+ }
+}
+function setIsStrictModeForDevtools(newIsStrictMode) {
+ {
+ if (typeof log$1 === "function") {
+ // We're in a test because Scheduler.log only exists
+ // in SchedulerMock. To reduce the noise in strict mode tests,
+ // suppress warnings and disable scheduler yielding during the double render
+ unstable_setDisableYieldValue(newIsStrictMode);
+ setSuppressWarning(newIsStrictMode);
}
- case "pointerover": {
- var pointerEvent = nativeEvent;
- var pointerId = pointerEvent.pointerId;
- queuedPointers.set(
- pointerId,
- accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedPointers.get(pointerId) || null,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- pointerEvent
- )
- );
- return true;
- }
+ if (injectedHook && typeof injectedHook.setStrictMode === "function") {
+ try {
+ injectedHook.setStrictMode(rendererID, newIsStrictMode);
+ } catch (err) {
+ {
+ if (!hasLoggedError) {
+ hasLoggedError = true;
- case "gotpointercapture": {
- var _pointerEvent = nativeEvent;
- var _pointerId2 = _pointerEvent.pointerId;
- queuedPointerCaptures.set(
- _pointerId2,
- accumulateOrCreateContinuousQueuedReplayableEvent(
- queuedPointerCaptures.get(_pointerId2) || null,
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- _pointerEvent
- )
- );
- return true;
+ error("React instrumentation encountered an error: %s", err);
+ }
+ }
+ }
}
}
+} // Profiler API hooks
- return false;
-} // Check if this target is unblocked. Returns true if it's unblocked.
-
-function attemptExplicitHydrationTarget(queuedTarget) {
- // TODO: This function shares a lot of logic with findInstanceBlockingEvent.
- // Try to unify them. It's a bit tricky since it would require two return
- // values.
- var targetInst = getClosestInstanceFromNode(queuedTarget.target);
-
- if (targetInst !== null) {
- var nearestMounted = getNearestMountedFiber(targetInst);
-
- if (nearestMounted !== null) {
- var tag = nearestMounted.tag;
-
- if (tag === SuspenseComponent) {
- var instance = getSuspenseInstanceFromFiber(nearestMounted);
-
- if (instance !== null) {
- // We're blocked on hydrating this boundary.
- // Increase its priority.
- queuedTarget.blockedOn = instance;
- attemptHydrationAtPriority(queuedTarget.priority, function () {
- attemptHydrationAtCurrentPriority$1(nearestMounted);
- });
- return;
- }
- } else if (tag === HostRoot) {
- var root = nearestMounted.stateNode;
+function injectProfilingHooks(profilingHooks) {
+ injectedProfilingHooks = profilingHooks;
+}
- if (isRootDehydrated(root)) {
- queuedTarget.blockedOn = getContainerFromFiber(nearestMounted); // We don't currently have a way to increase the priority of
- // a root other than sync.
+function getLaneLabelMap() {
+ if (enableSchedulingProfiler) {
+ var map = new Map();
+ var lane = 1;
- return;
- }
- }
+ for (var index = 0; index < TotalLanes; index++) {
+ var label = getLabelForLane(lane);
+ map.set(lane, label);
+ lane *= 2;
}
- }
- queuedTarget.blockedOn = null;
+ return map;
+ } else {
+ return null;
+ }
}
-function queueExplicitHydrationTarget(target) {
- // TODO: This will read the priority if it's dispatched by the React
- // event system but not native events. Should read window.event.type, like
- // we do for updates (getCurrentEventPriority).
- var updatePriority = getCurrentUpdatePriority();
- var queuedTarget = {
- blockedOn: null,
- target: target,
- priority: updatePriority
- };
- var i = 0;
-
- for (; i < queuedExplicitHydrationTargets.length; i++) {
- // Stop once we hit the first target with lower priority than
+function markCommitStarted(lanes) {
+ if (enableSchedulingProfiler) {
if (
- !isHigherEventPriority(
- updatePriority,
- queuedExplicitHydrationTargets[i].priority
- )
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markCommitStarted === "function"
) {
- break;
+ injectedProfilingHooks.markCommitStarted(lanes);
}
}
-
- queuedExplicitHydrationTargets.splice(i, 0, queuedTarget);
-
- if (i === 0) {
- attemptExplicitHydrationTarget(queuedTarget);
- }
}
-
-function attemptReplayContinuousQueuedEvent(queuedEvent) {
- if (queuedEvent.blockedOn !== null) {
- return false;
+function markCommitStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markCommitStopped === "function"
+ ) {
+ injectedProfilingHooks.markCommitStopped();
+ }
}
+}
+function markComponentRenderStarted(fiber) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentRenderStarted === "function"
+ ) {
+ injectedProfilingHooks.markComponentRenderStarted(fiber);
+ }
+ }
+}
+function markComponentRenderStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentRenderStopped === "function"
+ ) {
+ injectedProfilingHooks.markComponentRenderStopped();
+ }
+ }
+}
+function markComponentPassiveEffectMountStarted(fiber) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber);
+ }
+ }
+}
+function markComponentPassiveEffectMountStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentPassiveEffectMountStopped();
+ }
+ }
+}
+function markComponentPassiveEffectUnmountStarted(fiber) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber);
+ }
+ }
+}
+function markComponentPassiveEffectUnmountStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentPassiveEffectUnmountStopped();
+ }
+ }
+}
+function markComponentLayoutEffectMountStarted(fiber) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber);
+ }
+ }
+}
+function markComponentLayoutEffectMountStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentLayoutEffectMountStopped();
+ }
+ }
+}
+function markComponentLayoutEffectUnmountStarted(fiber) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber);
+ }
+ }
+}
+function markComponentLayoutEffectUnmountStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped ===
+ "function"
+ ) {
+ injectedProfilingHooks.markComponentLayoutEffectUnmountStopped();
+ }
+ }
+}
+function markComponentErrored(fiber, thrownValue, lanes) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentErrored === "function"
+ ) {
+ injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes);
+ }
+ }
+}
+function markComponentSuspended(fiber, wakeable, lanes) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markComponentSuspended === "function"
+ ) {
+ injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes);
+ }
+ }
+}
+function markLayoutEffectsStarted(lanes) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markLayoutEffectsStarted === "function"
+ ) {
+ injectedProfilingHooks.markLayoutEffectsStarted(lanes);
+ }
+ }
+}
+function markLayoutEffectsStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markLayoutEffectsStopped === "function"
+ ) {
+ injectedProfilingHooks.markLayoutEffectsStopped();
+ }
+ }
+}
+function markPassiveEffectsStarted(lanes) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markPassiveEffectsStarted === "function"
+ ) {
+ injectedProfilingHooks.markPassiveEffectsStarted(lanes);
+ }
+ }
+}
+function markPassiveEffectsStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markPassiveEffectsStopped === "function"
+ ) {
+ injectedProfilingHooks.markPassiveEffectsStopped();
+ }
+ }
+}
+function markRenderStarted(lanes) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markRenderStarted === "function"
+ ) {
+ injectedProfilingHooks.markRenderStarted(lanes);
+ }
+ }
+}
+function markRenderYielded() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markRenderYielded === "function"
+ ) {
+ injectedProfilingHooks.markRenderYielded();
+ }
+ }
+}
+function markRenderStopped() {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markRenderStopped === "function"
+ ) {
+ injectedProfilingHooks.markRenderStopped();
+ }
+ }
+}
+function markRenderScheduled(lane) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markRenderScheduled === "function"
+ ) {
+ injectedProfilingHooks.markRenderScheduled(lane);
+ }
+ }
+}
+function markForceUpdateScheduled(fiber, lane) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markForceUpdateScheduled === "function"
+ ) {
+ injectedProfilingHooks.markForceUpdateScheduled(fiber, lane);
+ }
+ }
+}
+function markStateUpdateScheduled(fiber, lane) {
+ if (enableSchedulingProfiler) {
+ if (
+ injectedProfilingHooks !== null &&
+ typeof injectedProfilingHooks.markStateUpdateScheduled === "function"
+ ) {
+ injectedProfilingHooks.markStateUpdateScheduled(fiber, lane);
+ }
+ }
+}
- var targetContainers = queuedEvent.targetContainers;
-
- while (targetContainers.length > 0) {
- var targetContainer = targetContainers[0];
- var nextBlockedOn = findInstanceBlockingEvent(
- queuedEvent.domEventName,
- queuedEvent.eventSystemFlags,
- targetContainer,
- queuedEvent.nativeEvent
- );
-
- if (nextBlockedOn === null) {
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- var nativeEvent = queuedEvent.nativeEvent;
- var nativeEventClone = new nativeEvent.constructor(
- nativeEvent.type,
- nativeEvent
- );
- setReplayingEvent(nativeEventClone);
- nativeEvent.target.dispatchEvent(nativeEventClone);
- resetReplayingEvent();
- } else {
- setReplayingEvent(queuedEvent.nativeEvent);
- dispatchEventForPluginEventSystem(
- queuedEvent.domEventName,
- queuedEvent.eventSystemFlags,
- queuedEvent.nativeEvent,
- return_targetInst,
- targetContainer
- );
- resetReplayingEvent();
- }
- } else {
- // We're still blocked. Try again later.
- var fiber = getInstanceFromNode$1(nextBlockedOn);
-
- if (fiber !== null) {
- attemptContinuousHydration$1(fiber);
- }
+/**
+ * inlined Object.is polyfill to avoid requiring consumers ship their own
+ * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ */
+function is(x, y) {
+ return (
+ (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
+ );
+}
- queuedEvent.blockedOn = nextBlockedOn;
- return false;
- } // This target container was successfully dispatched. Try the next.
+var objectIs = typeof Object.is === "function" ? Object.is : is; // $FlowFixMe[method-unbinding]
- targetContainers.shift();
+var syncQueue = null;
+var includesLegacySyncCallbacks = false;
+var isFlushingSyncQueue = false;
+function scheduleSyncCallback(callback) {
+ // Push this callback into an internal queue. We'll flush these either in
+ // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
+ if (syncQueue === null) {
+ syncQueue = [callback];
+ } else {
+ // Push onto existing queue. Don't need to schedule a callback because
+ // we already scheduled one when we created the queue.
+ syncQueue.push(callback);
}
-
- return true;
}
-
-function attemptReplayContinuousQueuedEventInMap(queuedEvent, key, map) {
- if (attemptReplayContinuousQueuedEvent(queuedEvent)) {
- map.delete(key);
+function scheduleLegacySyncCallback(callback) {
+ includesLegacySyncCallbacks = true;
+ scheduleSyncCallback(callback);
+}
+function flushSyncCallbacksOnlyInLegacyMode() {
+ // Only flushes the queue if there's a legacy sync callback scheduled.
+ // TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
+ // it might make more sense for the queue to be a list of roots instead of a
+ // list of generic callbacks. Then we can have two: one for legacy roots, one
+ // for concurrent roots. And this method would only flush the legacy ones.
+ if (includesLegacySyncCallbacks) {
+ flushSyncCallbacks();
}
}
+function flushSyncCallbacks() {
+ if (!isFlushingSyncQueue && syncQueue !== null) {
+ // Prevent re-entrance.
+ isFlushingSyncQueue = true; // Set the event priority to discrete
+ // TODO: Is this necessary anymore? The only user code that runs in this
+ // queue is in the render or commit phases, which already set the
+ // event priority. Should be able to remove.
-function replayUnblockedEvents() {
- hasScheduledReplayAttempt = false;
+ var previousUpdatePriority = getCurrentUpdatePriority();
+ setCurrentUpdatePriority(DiscreteEventPriority);
+ var errors = null;
+ var queue = syncQueue; // $FlowFixMe[incompatible-use] found when upgrading Flow
- if (!enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- // First replay discrete events.
- while (queuedDiscreteEvents.length > 0) {
- var nextDiscreteEvent = queuedDiscreteEvents[0];
+ for (var i = 0; i < queue.length; i++) {
+ // $FlowFixMe[incompatible-use] found when upgrading Flow
+ var callback = queue[i];
- if (nextDiscreteEvent.blockedOn !== null) {
- // We're still blocked.
- // Increase the priority of this boundary to unblock
- // the next discrete event.
- var fiber = getInstanceFromNode$1(nextDiscreteEvent.blockedOn);
+ try {
+ do {
+ var isSync = true; // $FlowFixMe[incompatible-type] we bail out when we get a null
- if (fiber !== null) {
- attemptDiscreteHydration$1(fiber);
+ callback = callback(isSync);
+ } while (callback !== null);
+ } catch (error) {
+ // Collect errors so we can rethrow them at the end
+ if (errors === null) {
+ errors = [error];
+ } else {
+ errors.push(error);
}
-
- break;
}
+ }
- var targetContainers = nextDiscreteEvent.targetContainers;
-
- while (targetContainers.length > 0) {
- var targetContainer = targetContainers[0];
- var nextBlockedOn = findInstanceBlockingEvent(
- nextDiscreteEvent.domEventName,
- nextDiscreteEvent.eventSystemFlags,
- targetContainer,
- nextDiscreteEvent.nativeEvent
- );
+ syncQueue = null;
+ includesLegacySyncCallbacks = false;
+ setCurrentUpdatePriority(previousUpdatePriority);
+ isFlushingSyncQueue = false;
- if (nextBlockedOn === null) {
- // This whole function is in !enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay,
- // so we don't need the new replay behavior code branch.
- setReplayingEvent(nextDiscreteEvent.nativeEvent);
- dispatchEventForPluginEventSystem(
- nextDiscreteEvent.domEventName,
- nextDiscreteEvent.eventSystemFlags,
- nextDiscreteEvent.nativeEvent,
- return_targetInst,
- targetContainer
- );
- resetReplayingEvent();
+ if (errors !== null) {
+ if (errors.length > 1) {
+ if (typeof AggregateError === "function") {
+ // eslint-disable-next-line no-undef
+ throw new AggregateError(errors);
} else {
- // We're still blocked. Try again later.
- nextDiscreteEvent.blockedOn = nextBlockedOn;
- break;
- } // This target container was successfully dispatched. Try the next.
-
- targetContainers.shift();
- }
+ for (var _i = 1; _i < errors.length; _i++) {
+ scheduleCallback$2(
+ ImmediatePriority,
+ throwError.bind(null, errors[_i])
+ );
+ }
- if (nextDiscreteEvent.blockedOn === null) {
- // We've successfully replayed the first event. Let's try the next one.
- queuedDiscreteEvents.shift();
+ var firstError = errors[0];
+ throw firstError;
+ }
+ } else {
+ var error = errors[0];
+ throw error;
}
}
- } // Next replay any continuous events.
-
- if (queuedFocus !== null && attemptReplayContinuousQueuedEvent(queuedFocus)) {
- queuedFocus = null;
- }
-
- if (queuedDrag !== null && attemptReplayContinuousQueuedEvent(queuedDrag)) {
- queuedDrag = null;
}
- if (queuedMouse !== null && attemptReplayContinuousQueuedEvent(queuedMouse)) {
- queuedMouse = null;
- }
-
- queuedPointers.forEach(attemptReplayContinuousQueuedEventInMap);
- queuedPointerCaptures.forEach(attemptReplayContinuousQueuedEventInMap);
+ return null;
}
-function scheduleCallbackIfUnblocked(queuedEvent, unblocked) {
- if (queuedEvent.blockedOn === unblocked) {
- queuedEvent.blockedOn = null;
+function throwError(error) {
+ throw error;
+}
- if (!hasScheduledReplayAttempt) {
- hasScheduledReplayAttempt = true; // Schedule a callback to attempt replaying as many events as are
- // now unblocked. This first might not actually be unblocked yet.
- // We could check it early to avoid scheduling an unnecessary callback.
+var nativeConsole = console;
+var nativeConsoleLog = null;
+var pendingGroupArgs = [];
+var printedGroupIndex = -1;
- Scheduler.unstable_scheduleCallback(
- Scheduler.unstable_NormalPriority,
- replayUnblockedEvents
- );
- }
- }
+function formatLanes(laneOrLanes) {
+ return "0b" + laneOrLanes.toString(2).padStart(31, "0");
}
-function retryIfBlockedOn(unblocked) {
- // Mark anything that was blocked on this as no longer blocked
- // and eligible for a replay.
- if (queuedDiscreteEvents.length > 0) {
- scheduleCallbackIfUnblocked(queuedDiscreteEvents[0], unblocked); // This is a exponential search for each boundary that commits. I think it's
- // worth it because we expect very few discrete events to queue up and once
- // we are actually fully unblocked it will be fast to replay them.
+function group() {
+ for (
+ var _len = arguments.length, groupArgs = new Array(_len), _key = 0;
+ _key < _len;
+ _key++
+ ) {
+ groupArgs[_key] = arguments[_key];
+ }
- for (var i = 1; i < queuedDiscreteEvents.length; i++) {
- var queuedEvent = queuedDiscreteEvents[i];
+ pendingGroupArgs.push(groupArgs);
- if (queuedEvent.blockedOn === unblocked) {
- queuedEvent.blockedOn = null;
- }
- }
+ if (nativeConsoleLog === null) {
+ nativeConsoleLog = nativeConsole.log;
+ nativeConsole.log = log;
}
+}
- if (queuedFocus !== null) {
- scheduleCallbackIfUnblocked(queuedFocus, unblocked);
- }
+function groupEnd() {
+ pendingGroupArgs.pop();
- if (queuedDrag !== null) {
- scheduleCallbackIfUnblocked(queuedDrag, unblocked);
+ while (printedGroupIndex >= pendingGroupArgs.length) {
+ nativeConsole.groupEnd();
+ printedGroupIndex--;
}
- if (queuedMouse !== null) {
- scheduleCallbackIfUnblocked(queuedMouse, unblocked);
+ if (pendingGroupArgs.length === 0) {
+ nativeConsole.log = nativeConsoleLog;
+ nativeConsoleLog = null;
}
+}
- var unblock = function (queuedEvent) {
- return scheduleCallbackIfUnblocked(queuedEvent, unblocked);
- };
-
- queuedPointers.forEach(unblock);
- queuedPointerCaptures.forEach(unblock);
-
- for (var _i = 0; _i < queuedExplicitHydrationTargets.length; _i++) {
- var queuedTarget = queuedExplicitHydrationTargets[_i];
-
- if (queuedTarget.blockedOn === unblocked) {
- queuedTarget.blockedOn = null;
+function log() {
+ if (printedGroupIndex < pendingGroupArgs.length - 1) {
+ for (var i = printedGroupIndex + 1; i < pendingGroupArgs.length; i++) {
+ var groupArgs = pendingGroupArgs[i];
+ nativeConsole.group.apply(nativeConsole, groupArgs);
}
- }
- while (queuedExplicitHydrationTargets.length > 0) {
- var nextExplicitTarget = queuedExplicitHydrationTargets[0];
+ printedGroupIndex = pendingGroupArgs.length - 1;
+ }
- if (nextExplicitTarget.blockedOn !== null) {
- // We're still blocked.
- break;
- } else {
- attemptExplicitHydrationTarget(nextExplicitTarget);
+ if (typeof nativeConsoleLog === "function") {
+ nativeConsoleLog.apply(void 0, arguments);
+ } else {
+ nativeConsole.log.apply(nativeConsole, arguments);
+ }
+}
- if (nextExplicitTarget.blockedOn === null) {
- // We're unblocked.
- queuedExplicitHydrationTargets.shift();
- }
+var REACT_LOGO_STYLE =
+ "background-color: #20232a; color: #61dafb; padding: 0 2px;";
+function logCommitStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c commit%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
}
}
}
-
-var ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig; // TODO: can we stop exporting these?
-
-var _enabled = true; // This is exported in FB builds for use by legacy FB layer infra.
-// We'd like to remove this but it's not clear if this is safe.
-
-function setEnabled(enabled) {
- _enabled = !!enabled;
-}
-function isEnabled() {
- return _enabled;
+function logCommitStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
+ }
}
-function createEventListenerWrapperWithPriority(
- targetContainer,
- domEventName,
- eventSystemFlags
-) {
- var eventPriority = getEventPriority(domEventName);
- var listenerWrapper;
-
- switch (eventPriority) {
- case DiscreteEventPriority:
- listenerWrapper = dispatchDiscreteEvent;
- break;
+var PossiblyWeakMap$1 = typeof WeakMap === "function" ? WeakMap : Map; // $FlowFixMe: Flow cannot handle polymorphic WeakMaps
- case ContinuousEventPriority:
- listenerWrapper = dispatchContinuousEvent;
- break;
+var wakeableIDs = new PossiblyWeakMap$1();
+var wakeableID = 0;
- case DefaultEventPriority:
- default:
- listenerWrapper = dispatchEvent;
- break;
+function getWakeableID(wakeable) {
+ if (!wakeableIDs.has(wakeable)) {
+ wakeableIDs.set(wakeable, wakeableID++);
}
- return listenerWrapper.bind(
- null,
- domEventName,
- eventSystemFlags,
- targetContainer
- );
+ return wakeableIDs.get(wakeable);
}
-function dispatchDiscreteEvent(
- domEventName,
- eventSystemFlags,
- container,
- nativeEvent
-) {
- var previousPriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig$3.transition;
- ReactCurrentBatchConfig$3.transition = null;
-
- try {
- setCurrentUpdatePriority(DiscreteEventPriority);
- dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig$3.transition = prevTransition;
+function logComponentSuspended(componentName, wakeable) {
+ {
+ if (enableDebugTracing) {
+ var id = getWakeableID(wakeable);
+ var display = wakeable.displayName || wakeable;
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " suspended",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ wakeable.then(
+ function () {
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " resolved",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ },
+ function () {
+ log(
+ "%c\u269B\uFE0F%c " + componentName + " rejected",
+ REACT_LOGO_STYLE,
+ "color: #80366d; font-weight: bold;",
+ id,
+ display
+ );
+ }
+ );
+ }
}
}
-
-function dispatchContinuousEvent(
- domEventName,
- eventSystemFlags,
- container,
- nativeEvent
-) {
- var previousPriority = getCurrentUpdatePriority$1();
- var prevTransition = ReactCurrentBatchConfig$3.transition;
- ReactCurrentBatchConfig$3.transition = null;
-
- try {
- setCurrentUpdatePriority(ContinuousEventPriority);
- dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
- } finally {
- setCurrentUpdatePriority(previousPriority);
- ReactCurrentBatchConfig$3.transition = prevTransition;
+function logLayoutEffectsStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c layout effects%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
+ }
}
}
-
-function dispatchEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- if (!_enabled) {
- return;
+function logLayoutEffectsStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
}
-
- if (enableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay) {
- dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- } else {
- dispatchEventOriginal(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
+}
+function logPassiveEffectsStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c passive effects%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
+ }
}
}
-
-function dispatchEventOriginal(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // TODO: replaying capture phase events is currently broken
- // because we used to do it during top-level native bubble handlers
- // but now we use different bubble and capture handlers.
- // In eager mode, we attach capture listeners early, so we need
- // to filter them out until we fix the logic to handle them correctly.
- var allowReplay = (eventSystemFlags & IS_CAPTURE_PHASE) === 0;
-
- if (
- allowReplay &&
- hasQueuedDiscreteEvents() &&
- isDiscreteEventThatRequiresHydration(domEventName)
- ) {
- // If we already have a queue of discrete events, and this is another discrete
- // event, then we can't dispatch it regardless of its target, since they
- // need to dispatch in order.
- queueDiscreteEvent(
- null, // Flags that we're not actually blocked on anything as far as we know.
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
- return;
+function logPassiveEffectsStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
}
-
- var blockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
-
- if (blockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
- );
-
- if (allowReplay) {
- clearIfContinuousEvent(domEventName, nativeEvent);
+}
+function logRenderStarted(lanes) {
+ {
+ if (enableDebugTracing) {
+ group(
+ "%c\u269B\uFE0F%c render%c (" + formatLanes(lanes) + ")",
+ REACT_LOGO_STYLE,
+ "",
+ "font-weight: normal;"
+ );
}
-
- return;
}
-
- if (allowReplay) {
- if (isDiscreteEventThatRequiresHydration(domEventName)) {
- // This to be replayed later once the target is available.
- queueDiscreteEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
+}
+function logRenderStopped() {
+ {
+ if (enableDebugTracing) {
+ groupEnd();
+ }
+ }
+}
+function logForceUpdateScheduled(componentName, lane) {
+ {
+ if (enableDebugTracing) {
+ log(
+ "%c\u269B\uFE0F%c " +
+ componentName +
+ " forced update %c(" +
+ formatLanes(lane) +
+ ")",
+ REACT_LOGO_STYLE,
+ "color: #db2e1f; font-weight: bold;",
+ ""
+ );
+ }
+ }
+}
+function logStateUpdateScheduled(componentName, lane, payloadOrAction) {
+ {
+ if (enableDebugTracing) {
+ log(
+ "%c\u269B\uFE0F%c " +
+ componentName +
+ " updated state %c(" +
+ formatLanes(lane) +
+ ")",
+ REACT_LOGO_STYLE,
+ "color: #01a252; font-weight: bold;",
+ "",
+ payloadOrAction
);
- return;
}
+ }
+}
- if (
- queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- )
- ) {
- return;
- } // We need to clear only if we didn't queue because
- // queueing is accumulative.
+// This is imported by the event replaying implementation in React DOM. It's
+// in a separate file to break a circular dependency between the renderer and
+// the reconciler.
+function isRootDehydrated(root) {
+ var currentState = root.current.memoizedState;
+ return currentState.isDehydrated;
+}
- clearIfContinuousEvent(domEventName, nativeEvent);
- } // This is not replayable so we'll invoke it but without a target,
- // in case the event system needs to trace it.
+// Intentionally not using it yet to derisk the initial implementation, because
+// the way we push/pop these values is a bit unusual. If there's a mistake, I'd
+// rather the ids be wrong than crash the whole reconciler.
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- null,
- targetContainer
- );
+var forkStack = [];
+var forkStackIndex = 0;
+var treeForkProvider = null;
+var treeForkCount = 0;
+var idStack = [];
+var idStackIndex = 0;
+var treeContextProvider = null;
+var treeContextId = 1;
+var treeContextOverflow = "";
+function isForkedChild(workInProgress) {
+ warnIfNotHydrating();
+ return (workInProgress.flags & Forked) !== NoFlags$1;
}
+function getForksAtLevel(workInProgress) {
+ warnIfNotHydrating();
+ return treeForkCount;
+}
+function getTreeId() {
+ var overflow = treeContextOverflow;
+ var idWithLeadingBit = treeContextId;
+ var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit);
+ return id.toString(32) + overflow;
+}
+function pushTreeFork(workInProgress, totalChildren) {
+ // This is called right after we reconcile an array (or iterator) of child
+ // fibers, because that's the only place where we know how many children in
+ // the whole set without doing extra work later, or storing addtional
+ // information on the fiber.
+ //
+ // That's why this function is separate from pushTreeId — it's called during
+ // the render phase of the fork parent, not the child, which is where we push
+ // the other context values.
+ //
+ // In the Fizz implementation this is much simpler because the child is
+ // rendered in the same callstack as the parent.
+ //
+ // It might be better to just add a `forks` field to the Fiber type. It would
+ // make this module simpler.
+ warnIfNotHydrating();
+ forkStack[forkStackIndex++] = treeForkCount;
+ forkStack[forkStackIndex++] = treeForkProvider;
+ treeForkProvider = workInProgress;
+ treeForkCount = totalChildren;
+}
+function pushTreeId(workInProgress, totalChildren, index) {
+ warnIfNotHydrating();
+ idStack[idStackIndex++] = treeContextId;
+ idStack[idStackIndex++] = treeContextOverflow;
+ idStack[idStackIndex++] = treeContextProvider;
+ treeContextProvider = workInProgress;
+ var baseIdWithLeadingBit = treeContextId;
+ var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
+ // of the id; we use it to account for leading 0s.
-function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- var blockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
-
- if (blockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
- );
- clearIfContinuousEvent(domEventName, nativeEvent);
- return;
- }
+ var baseLength = getBitLength(baseIdWithLeadingBit) - 1;
+ var baseId = baseIdWithLeadingBit & ~(1 << baseLength);
+ var slot = index + 1;
+ var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into
+ // consideration the leading 1 we use to mark the end of the sequence.
- if (
- queueIfContinuousEvent(
- blockedOn,
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- )
- ) {
- nativeEvent.stopPropagation();
- return;
- } // We need to clear only if we didn't queue because
- // queueing is accumulative.
+ if (length > 30) {
+ // We overflowed the bitwise-safe range. Fall back to slower algorithm.
+ // This branch assumes the length of the base id is greater than 5; it won't
+ // work for smaller ids, because you need 5 bits per character.
+ //
+ // We encode the id in multiple steps: first the base id, then the
+ // remaining digits.
+ //
+ // Each 5 bit sequence corresponds to a single base 32 character. So for
+ // example, if the current id is 23 bits long, we can convert 20 of those
+ // bits into a string of 4 characters, with 3 bits left over.
+ //
+ // First calculate how many bits in the base id represent a complete
+ // sequence of characters.
+ var numberOfOverflowBits = baseLength - (baseLength % 5); // Then create a bitmask that selects only those bits.
- clearIfContinuousEvent(domEventName, nativeEvent);
+ var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string.
- if (
- eventSystemFlags & IS_CAPTURE_PHASE &&
- isDiscreteEventThatRequiresHydration(domEventName)
- ) {
- while (blockedOn !== null) {
- var fiber = getInstanceFromNode$1(blockedOn);
+ var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id.
- if (fiber !== null) {
- attemptSynchronousHydration$1(fiber);
- }
+ var restOfBaseId = baseId >> numberOfOverflowBits;
+ var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because
+ // we made more room, this time it won't overflow.
- var nextBlockedOn = findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
- );
+ var restOfLength = getBitLength(totalChildren) + restOfBaseLength;
+ var restOfNewBits = slot << restOfBaseLength;
+ var id = restOfNewBits | restOfBaseId;
+ var overflow = newOverflow + baseOverflow;
+ treeContextId = (1 << restOfLength) | id;
+ treeContextOverflow = overflow;
+ } else {
+ // Normal path
+ var newBits = slot << baseLength;
- if (nextBlockedOn === null) {
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- return_targetInst,
- targetContainer
- );
- }
+ var _id = newBits | baseId;
- if (nextBlockedOn === blockedOn) {
- break;
- }
+ var _overflow = baseOverflow;
+ treeContextId = (1 << length) | _id;
+ treeContextOverflow = _overflow;
+ }
+}
+function pushMaterializedTreeId(workInProgress) {
+ warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear
+ // in its children.
- blockedOn = nextBlockedOn;
- }
+ var returnFiber = workInProgress.return;
- if (blockedOn !== null) {
- nativeEvent.stopPropagation();
- }
+ if (returnFiber !== null) {
+ var numberOfForks = 1;
+ var slotIndex = 0;
+ pushTreeFork(workInProgress, numberOfForks);
+ pushTreeId(workInProgress, numberOfForks, slotIndex);
+ }
+}
- return;
- } // This is not replayable so we'll invoke it but without a target,
- // in case the event system needs to trace it.
+function getBitLength(number) {
+ return 32 - clz32(number);
+}
- dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- null,
- targetContainer
- );
+function getLeadingBit(id) {
+ return 1 << (getBitLength(id) - 1);
}
-var return_targetInst = null; // Returns a SuspenseInstance or Container if it's blocked.
-// The return_targetInst field above is conceptually part of the return value.
-
-function findInstanceBlockingEvent(
- domEventName,
- eventSystemFlags,
- targetContainer,
- nativeEvent
-) {
- // TODO: Warn if _enabled is false.
- return_targetInst = null;
- var nativeEventTarget = getEventTarget(nativeEvent);
- var targetInst = getClosestInstanceFromNode(nativeEventTarget);
-
- if (targetInst !== null) {
- var nearestMounted = getNearestMountedFiber(targetInst);
-
- if (nearestMounted === null) {
- // This tree has been unmounted already. Dispatch without a target.
- targetInst = null;
- } else {
- var tag = nearestMounted.tag;
+function popTreeContext(workInProgress) {
+ // Restore the previous values.
+ // This is a bit more complicated than other context-like modules in Fiber
+ // because the same Fiber may appear on the stack multiple times and for
+ // different reasons. We have to keep popping until the work-in-progress is
+ // no longer at the top of the stack.
+ while (workInProgress === treeForkProvider) {
+ treeForkProvider = forkStack[--forkStackIndex];
+ forkStack[forkStackIndex] = null;
+ treeForkCount = forkStack[--forkStackIndex];
+ forkStack[forkStackIndex] = null;
+ }
- if (tag === SuspenseComponent) {
- var instance = getSuspenseInstanceFromFiber(nearestMounted);
+ while (workInProgress === treeContextProvider) {
+ treeContextProvider = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ treeContextOverflow = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ treeContextId = idStack[--idStackIndex];
+ idStack[idStackIndex] = null;
+ }
+}
+function getSuspendedTreeContext() {
+ warnIfNotHydrating();
- if (instance !== null) {
- // Queue the event to be replayed later. Abort dispatching since we
- // don't want this event dispatched twice through the event system.
- // TODO: If this is the first discrete event in the queue. Schedule an increased
- // priority for this boundary.
- return instance;
- } // This shouldn't happen, something went wrong but to avoid blocking
- // the whole system, dispatch the event without a target.
- // TODO: Warn.
+ if (treeContextProvider !== null) {
+ return {
+ id: treeContextId,
+ overflow: treeContextOverflow
+ };
+ } else {
+ return null;
+ }
+}
+function restoreSuspendedTreeContext(workInProgress, suspendedContext) {
+ warnIfNotHydrating();
+ idStack[idStackIndex++] = treeContextId;
+ idStack[idStackIndex++] = treeContextOverflow;
+ idStack[idStackIndex++] = treeContextProvider;
+ treeContextId = suspendedContext.id;
+ treeContextOverflow = suspendedContext.overflow;
+ treeContextProvider = workInProgress;
+}
- targetInst = null;
- } else if (tag === HostRoot) {
- var root = nearestMounted.stateNode;
+function warnIfNotHydrating() {
+ {
+ if (!getIsHydrating()) {
+ error(
+ "Expected to be hydrating. This is a bug in React. Please file " +
+ "an issue."
+ );
+ }
+ }
+}
- if (isRootDehydrated(root)) {
- // If this happens during a replay something went wrong and it might block
- // the whole system.
- return getContainerFromFiber(nearestMounted);
- }
+var contextStackCursor = createCursor(null);
+var contextFiberStackCursor = createCursor(null);
+var rootInstanceStackCursor = createCursor(null);
- targetInst = null;
- } else if (nearestMounted !== targetInst) {
- // If we get an event (ex: img onload) before committing that
- // component's mount, ignore it for now (that is, treat it as if it was an
- // event on a non-React tree). We might also consider queueing events and
- // dispatching them after the mount.
- targetInst = null;
- }
+function requiredContext(c) {
+ {
+ if (c === null) {
+ error(
+ "Expected host context to exist. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
}
}
- return_targetInst = targetInst; // We're not blocked on anything.
-
- return null;
+ return c;
}
-function getEventPriority(domEventName) {
- switch (domEventName) {
- // Used by SimpleEventPlugin:
- case "cancel":
- case "click":
- case "close":
- case "contextmenu":
- case "copy":
- case "cut":
- case "auxclick":
- case "dblclick":
- case "dragend":
- case "dragstart":
- case "drop":
- case "focusin":
- case "focusout":
- case "input":
- case "invalid":
- case "keydown":
- case "keypress":
- case "keyup":
- case "mousedown":
- case "mouseup":
- case "paste":
- case "pause":
- case "play":
- case "pointercancel":
- case "pointerdown":
- case "pointerup":
- case "ratechange":
- case "reset":
- case "resize":
- case "seeked":
- case "submit":
- case "touchcancel":
- case "touchend":
- case "touchstart":
- case "volumechange": // Used by polyfills:
- // eslint-disable-next-line no-fallthrough
- case "change":
- case "selectionchange":
- case "textInput":
- case "compositionstart":
- case "compositionend":
- case "compositionupdate": // Only enableCreateEventHandleAPI:
- // eslint-disable-next-line no-fallthrough
+function getCurrentRootHostContainer() {
+ return rootInstanceStackCursor.current;
+}
- case "beforeblur":
- case "afterblur": // Not used by React but could be by user code:
- // eslint-disable-next-line no-fallthrough
+function getRootHostContainer() {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ return rootInstance;
+}
- case "beforeinput":
- case "blur":
- case "fullscreenchange":
- case "focus":
- case "hashchange":
- case "popstate":
- case "select":
- case "selectstart":
- return DiscreteEventPriority;
+function pushHostContainer(fiber, nextRootInstance) {
+ // Push current root instance onto the stack;
+ // This allows us to reset root when portals are popped.
+ push(rootInstanceStackCursor, nextRootInstance, fiber); // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
- case "drag":
- case "dragenter":
- case "dragexit":
- case "dragleave":
- case "dragover":
- case "mousemove":
- case "mouseout":
- case "mouseover":
- case "pointermove":
- case "pointerout":
- case "pointerover":
- case "scroll":
- case "toggle":
- case "touchmove":
- case "wheel": // Not used by React but could be by user code:
- // eslint-disable-next-line no-fallthrough
+ push(contextFiberStackCursor, fiber, fiber); // Finally, we need to push the host context to the stack.
+ // However, we can't just call getRootHostContext() and push it because
+ // we'd have a different number of entries on the stack depending on
+ // whether getRootHostContext() throws somewhere in renderer code or not.
+ // So we push an empty value first. This lets us safely unwind on errors.
- case "mouseenter":
- case "mouseleave":
- case "pointerenter":
- case "pointerleave":
- return ContinuousEventPriority;
+ push(contextStackCursor, null, fiber);
+ var nextRootContext = getRootHostContext(nextRootInstance); // Now that we know this function doesn't throw, replace it.
- case "message": {
- // We might be in the Scheduler callback.
- // Eventually this mechanism will be replaced by a check
- // of the current priority on the native scheduler.
- var schedulerPriority = getCurrentPriorityLevel();
+ pop(contextStackCursor, fiber);
+ push(contextStackCursor, nextRootContext, fiber);
+}
- switch (schedulerPriority) {
- case ImmediatePriority:
- return DiscreteEventPriority;
+function popHostContainer(fiber) {
+ pop(contextStackCursor, fiber);
+ pop(contextFiberStackCursor, fiber);
+ pop(rootInstanceStackCursor, fiber);
+}
- case UserBlockingPriority:
- return ContinuousEventPriority;
+function getHostContext() {
+ var context = requiredContext(contextStackCursor.current);
+ return context;
+}
- case NormalPriority$1:
- case LowPriority:
- // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
- return DefaultEventPriority;
+function pushHostContext(fiber) {
+ var context = requiredContext(contextStackCursor.current);
+ var nextContext = getChildHostContext(context, fiber.type); // Don't push this Fiber's context unless it's unique.
- case IdlePriority:
- return IdleEventPriority;
+ if (context === nextContext) {
+ return;
+ } // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
- default:
- return DefaultEventPriority;
- }
- }
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor, nextContext, fiber);
+}
- default:
- return DefaultEventPriority;
+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;
}
-}
-function addEventBubbleListener(target, eventType, listener) {
- target.addEventListener(eventType, listener, false);
- return listener;
-}
-function addEventCaptureListener(target, eventType, listener) {
- target.addEventListener(eventType, listener, true);
- return listener;
-}
-function addEventCaptureListenerWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
-) {
- target.addEventListener(eventType, listener, {
- capture: true,
- passive: passive
- });
- return listener;
-}
-function addEventBubbleListenerWithPassiveFlag(
- target,
- eventType,
- listener,
- passive
-) {
- target.addEventListener(eventType, listener, {
- passive: passive
- });
- return listener;
-}
-function removeEventListener(target, eventType, listener, capture) {
- target.removeEventListener(eventType, listener, capture);
+ pop(contextStackCursor, fiber);
+ pop(contextFiberStackCursor, fiber);
}
-/**
- * These variables store information about text content of a target node,
- * allowing comparison of content before and after a given event.
- *
- * Identify the node where selection currently begins, then observe
- * both its text content and its current position in the DOM. Since the
- * browser may natively replace the target node during composition, we can
- * use its position to find its replacement.
- *
- *
- */
-var root = null;
-var startText = null;
-var fallbackText = null;
-function initialize(nativeEventTarget) {
- root = nativeEventTarget;
- startText = getText();
- return true;
-}
-function reset() {
- root = null;
- startText = null;
- fallbackText = null;
-}
-function getData() {
- if (fallbackText) {
- return fallbackText;
- }
+// This may have been an insertion or a hydration.
- var start;
- var startValue = startText;
- var startLength = startValue.length;
- var end;
- var endValue = getText();
- var endLength = endValue.length;
+var hydrationParentFiber = null;
+var nextHydratableInstance = null;
+var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
+// due to earlier mismatches or a suspended fiber.
- for (start = 0; start < startLength; start++) {
- if (startValue[start] !== endValue[start]) {
- break;
- }
- }
+var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary
- var minEnd = startLength - start;
+var hydrationErrors = null;
+var rootOrSingletonContext = false;
- for (end = 1; end <= minEnd; end++) {
- if (startValue[startLength - end] !== endValue[endLength - end]) {
- break;
+function warnIfHydrating() {
+ {
+ if (isHydrating) {
+ error(
+ "We should not be hydrating here. This is a bug in React. Please file a bug."
+ );
}
}
+}
- var sliceTail = end > 1 ? 1 - end : undefined;
- fallbackText = endValue.slice(start, sliceTail);
- return fallbackText;
+function markDidThrowWhileHydratingDEV() {
+ {
+ didSuspendOrErrorDEV = true;
+ }
}
-function getText() {
- if ("value" in root) {
- return root.value;
+function didSuspendOrErrorWhileHydratingDEV() {
+ {
+ return didSuspendOrErrorDEV;
}
-
- return root.textContent;
}
-/**
- * `charCode` represents the actual "character code" and is safe to use with
- * `String.fromCharCode`. As such, only keys that correspond to printable
- * characters produce a valid `charCode`, the only exception to this is Enter.
- * The Tab-key is considered non-printable and does not have a `charCode`,
- * presumably because it does not produce a tab-character in browsers.
- *
- * @param {object} nativeEvent Native browser event.
- * @return {number} Normalized `charCode` property.
- */
-function getEventCharCode(nativeEvent) {
- var charCode;
- var keyCode = nativeEvent.keyCode;
-
- if ("charCode" in nativeEvent) {
- charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`.
-
- if (charCode === 0 && keyCode === 13) {
- charCode = 13;
- }
- } else {
- // IE8 does not implement `charCode`, but `keyCode` has the correct value.
- charCode = keyCode;
- } // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
- // report Enter as charCode 10 when ctrl is pressed.
+function enterHydrationState(fiber) {
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance =
+ getFirstHydratableChildWithinContainer(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ hydrationErrors = null;
+ didSuspendOrErrorDEV = false;
+ rootOrSingletonContext = true;
+ return true;
+}
- if (charCode === 10) {
- charCode = 13;
- } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
- // Must not discard the (non-)printable Enter-key.
+function reenterHydrationStateFromDehydratedSuspenseInstance(
+ fiber,
+ suspenseInstance,
+ treeContext
+) {
+ nextHydratableInstance =
+ getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ hydrationErrors = null;
+ didSuspendOrErrorDEV = false;
+ rootOrSingletonContext = false;
- if (charCode >= 32 || charCode === 13) {
- return charCode;
+ if (treeContext !== null) {
+ restoreSuspendedTreeContext(fiber, treeContext);
}
- return 0;
-}
-
-function functionThatReturnsTrue() {
return true;
}
-function functionThatReturnsFalse() {
- return false;
-} // This is intentionally a factory so that we have different returned constructors.
-// If we had a single constructor, it would be megamorphic and engines would deopt.
-
-function createSyntheticEvent(Interface) {
- /**
- * Synthetic events are dispatched by event plugins, typically in response to a
- * top-level event delegation handler.
- *
- * These systems should generally use pooling to reduce the frequency of garbage
- * collection. The system should check `isPersistent` to determine whether the
- * event should be released into the pool after being dispatched. Users that
- * need a persisted event should invoke `persist`.
- *
- * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
- * normalizing browser quirks. Subclasses do not necessarily have to implement a
- * DOM interface; custom application-specific events can also subclass this.
- */
- // $FlowFixMe[missing-this-annot]
- function SyntheticBaseEvent(
- reactName,
- reactEventType,
- targetInst,
- nativeEvent,
- nativeEventTarget
- ) {
- this._reactName = reactName;
- this._targetInst = targetInst;
- this.type = reactEventType;
- this.nativeEvent = nativeEvent;
- this.target = nativeEventTarget;
- this.currentTarget = null;
-
- for (var propName in Interface) {
- if (!Interface.hasOwnProperty(propName)) {
- continue;
+function warnUnhydratedInstance(returnFiber, instance) {
+ {
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ didNotHydrateInstanceWithinContainer(
+ returnFiber.stateNode.containerInfo,
+ instance
+ );
+ break;
}
- var normalize = Interface[propName];
+ case HostSingleton:
+ case HostComponent: {
+ var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotHydrateInstance(
+ returnFiber.type,
+ returnFiber.memoizedProps,
+ returnFiber.stateNode,
+ instance, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode
+ );
+ break;
+ }
- if (normalize) {
- this[propName] = normalize(nativeEvent);
- } else {
- this[propName] = nativeEvent[propName];
+ case SuspenseComponent: {
+ var suspenseState = returnFiber.memoizedState;
+ if (suspenseState.dehydrated !== null)
+ didNotHydrateInstanceWithinSuspenseInstance(
+ suspenseState.dehydrated,
+ instance
+ );
+ break;
}
}
+ }
+}
- var defaultPrevented =
- nativeEvent.defaultPrevented != null
- ? nativeEvent.defaultPrevented
- : nativeEvent.returnValue === false;
+function deleteHydratableInstance(returnFiber, instance) {
+ warnUnhydratedInstance(returnFiber, instance);
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete.return = returnFiber;
+ var deletions = returnFiber.deletions;
- if (defaultPrevented) {
- this.isDefaultPrevented = functionThatReturnsTrue;
- } else {
- this.isDefaultPrevented = functionThatReturnsFalse;
- }
-
- this.isPropagationStopped = functionThatReturnsFalse;
- return this;
- } // $FlowFixMe[prop-missing] found when upgrading Flow
+ if (deletions === null) {
+ returnFiber.deletions = [childToDelete];
+ returnFiber.flags |= ChildDeletion;
+ } else {
+ deletions.push(childToDelete);
+ }
+}
- assign(SyntheticBaseEvent.prototype, {
- // $FlowFixMe[missing-this-annot]
- preventDefault: function () {
- this.defaultPrevented = true;
- var event = this.nativeEvent;
+function warnNonhydratedInstance(returnFiber, fiber) {
+ {
+ if (didSuspendOrErrorDEV) {
+ // Inside a boundary that already suspended. We're currently rendering the
+ // siblings of a suspended node. The mismatch may be due to the missing
+ // data, so it's probably a false positive.
+ return;
+ }
- if (!event) {
- return;
- }
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ var parentContainer = returnFiber.stateNode.containerInfo;
- if (event.preventDefault) {
- event.preventDefault(); // $FlowFixMe - flow is not aware of `unknown` in IE
- } else if (typeof event.returnValue !== "unknown") {
- event.returnValue = false;
- }
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent:
+ var type = fiber.type;
+ didNotFindHydratableInstanceWithinContainer(parentContainer, type);
+ break;
- this.isDefaultPrevented = functionThatReturnsTrue;
- },
- // $FlowFixMe[missing-this-annot]
- stopPropagation: function () {
- var event = this.nativeEvent;
+ case HostText:
+ var text = fiber.pendingProps;
+ didNotFindHydratableTextInstanceWithinContainer(
+ parentContainer,
+ text
+ );
+ break;
+ }
- if (!event) {
- return;
+ break;
}
- if (event.stopPropagation) {
- event.stopPropagation(); // $FlowFixMe - flow is not aware of `unknown` in IE
- } else if (typeof event.cancelBubble !== "unknown") {
- // The ChangeEventPlugin registers a "propertychange" event for
- // IE. This event does not support bubbling or cancelling, and
- // any references to cancelBubble throw "Member not found". A
- // typeof check of "unknown" circumvents this issue (and is also
- // IE specific).
- event.cancelBubble = true;
- }
+ case HostSingleton:
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
- this.isPropagationStopped = functionThatReturnsTrue;
- },
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent: {
+ var _type = fiber.type;
+ var _props = fiber.pendingProps;
+ var isConcurrentMode =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotFindHydratableInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _type,
+ _props, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode
+ );
+ break;
+ }
- /**
- * We release all dispatched `SyntheticEvent`s after each event loop, adding
- * them back into the pool. This allows a way to hold onto a reference that
- * won't be added back into the pool.
- */
- persist: function () {
- // Modern event system doesn't use pooling.
- },
+ case HostText: {
+ var _text = fiber.pendingProps;
- /**
- * Checks if this event should be released back into the pool.
- *
- * @return {boolean} True if this should not be released, false otherwise.
- */
- isPersistent: functionThatReturnsTrue
- });
- return SyntheticBaseEvent;
-}
-/**
- * @interface Event
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+ var _isConcurrentMode =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
-var EventInterface = {
- eventPhase: 0,
- bubbles: 0,
- cancelable: 0,
- timeStamp: function (event) {
- return event.timeStamp || Date.now();
- },
- defaultPrevented: 0,
- isTrusted: 0
-};
-var SyntheticEvent = createSyntheticEvent(EventInterface);
+ didNotFindHydratableTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _text, // TODO: Delete this argument when we remove the legacy root API.
+ _isConcurrentMode
+ );
+ break;
+ }
+ }
-var UIEventInterface = assign({}, EventInterface, {
- view: 0,
- detail: 0
-});
+ break;
+ }
-var SyntheticUIEvent = createSyntheticEvent(UIEventInterface);
-var lastMovementX;
-var lastMovementY;
-var lastMouseEvent;
+ case SuspenseComponent: {
+ var suspenseState = returnFiber.memoizedState;
+ var _parentInstance = suspenseState.dehydrated;
+ if (_parentInstance !== null)
+ switch (fiber.tag) {
+ case HostSingleton:
+ case HostComponent:
+ var _type2 = fiber.type;
+ didNotFindHydratableInstanceWithinSuspenseInstance(
+ _parentInstance,
+ _type2
+ );
+ break;
-function updateMouseMovementPolyfillState(event) {
- if (event !== lastMouseEvent) {
- if (lastMouseEvent && event.type === "mousemove") {
- // $FlowFixMe assuming this is a number
- lastMovementX = event.screenX - lastMouseEvent.screenX; // $FlowFixMe assuming this is a number
+ case HostText:
+ var _text2 = fiber.pendingProps;
+ didNotFindHydratableTextInstanceWithinSuspenseInstance(
+ _parentInstance,
+ _text2
+ );
+ break;
+ }
+ break;
+ }
- lastMovementY = event.screenY - lastMouseEvent.screenY;
- } else {
- lastMovementX = 0;
- lastMovementY = 0;
+ default:
+ return;
}
-
- lastMouseEvent = event;
}
}
-/**
- * @interface MouseEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
-var MouseEventInterface = assign({}, UIEventInterface, {
- screenX: 0,
- screenY: 0,
- clientX: 0,
- clientY: 0,
- pageX: 0,
- pageY: 0,
- ctrlKey: 0,
- shiftKey: 0,
- altKey: 0,
- metaKey: 0,
- getModifierState: getEventModifierState,
- button: 0,
- buttons: 0,
- relatedTarget: function (event) {
- if (event.relatedTarget === undefined)
- return event.fromElement === event.srcElement
- ? event.toElement
- : event.fromElement;
- return event.relatedTarget;
- },
- movementX: function (event) {
- if ("movementX" in event) {
- return event.movementX;
- }
+function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.flags = (fiber.flags & ~Hydrating) | Placement;
+ warnNonhydratedInstance(returnFiber, fiber);
+}
- updateMouseMovementPolyfillState(event);
- return lastMovementX;
- },
- movementY: function (event) {
- if ("movementY" in event) {
- return event.movementY;
- } // Don't need to call updateMouseMovementPolyfillState() here
- // because it's guaranteed to have already run when movementX
- // was copied.
+function tryHydrateInstance(fiber, nextInstance) {
+ // fiber is a HostComponent Fiber
+ var instance = canHydrateInstance(nextInstance, fiber.type);
- return lastMovementY;
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(instance);
+ rootOrSingletonContext = false;
+ return true;
}
-});
-
-var SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
-/**
- * @interface DragEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
-
-var DragEventInterface = assign({}, MouseEventInterface, {
- dataTransfer: 0
-});
-
-var SyntheticDragEvent = createSyntheticEvent(DragEventInterface);
-/**
- * @interface FocusEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
-var FocusEventInterface = assign({}, UIEventInterface, {
- relatedTarget: 0
-});
-
-var SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
- * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
- */
+ return false;
+}
-var AnimationEventInterface = assign({}, EventInterface, {
- animationName: 0,
- elapsedTime: 0,
- pseudoElement: 0
-});
+function tryHydrateText(fiber, nextInstance) {
+ // fiber is a HostText Fiber
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
-var SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/clipboard-apis/
- */
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate.
-var ClipboardEventInterface = assign({}, EventInterface, {
- clipboardData: function (event) {
- return "clipboardData" in event
- ? event.clipboardData
- : window.clipboardData;
+ nextHydratableInstance = null;
+ return true;
}
-});
-var SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
- */
+ return false;
+}
-var CompositionEventInterface = assign({}, EventInterface, {
- data: 0
-});
+function tryHydrateSuspense(fiber, nextInstance) {
+ // fiber is a SuspenseComponent Fiber
+ var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
-var SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
- * /#events-inputevents
- */
-// Happens to share the same list for now.
+ if (suspenseInstance !== null) {
+ var suspenseState = {
+ dehydrated: suspenseInstance,
+ treeContext: getSuspendedTreeContext(),
+ retryLane: OffscreenLane
+ };
+ fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber.
+ // This simplifies the code for getHostSibling and deleting nodes,
+ // since it doesn't have to consider all Suspense boundaries and
+ // check if they're dehydrated ones or not.
-var SyntheticInputEvent = SyntheticCompositionEvent;
-/**
- * Normalization of deprecated HTML5 `key` values
- * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
- */
+ var dehydratedFragment =
+ createFiberFromDehydratedFragment(suspenseInstance);
+ dehydratedFragment.return = fiber;
+ fiber.child = dehydratedFragment;
+ hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into
+ // it during the first pass. Instead, we'll reenter it later.
-var normalizeKey = {
- Esc: "Escape",
- Spacebar: " ",
- Left: "ArrowLeft",
- Up: "ArrowUp",
- Right: "ArrowRight",
- Down: "ArrowDown",
- Del: "Delete",
- Win: "OS",
- Menu: "ContextMenu",
- Apps: "ContextMenu",
- Scroll: "ScrollLock",
- MozPrintableKey: "Unidentified"
-};
-/**
- * Translation from legacy `keyCode` to HTML5 `key`
- * Only special keys supported, all others depend on keyboard layout or browser
- * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
- */
+ nextHydratableInstance = null;
+ return true;
+ }
-var translateToKey = {
- "8": "Backspace",
- "9": "Tab",
- "12": "Clear",
- "13": "Enter",
- "16": "Shift",
- "17": "Control",
- "18": "Alt",
- "19": "Pause",
- "20": "CapsLock",
- "27": "Escape",
- "32": " ",
- "33": "PageUp",
- "34": "PageDown",
- "35": "End",
- "36": "Home",
- "37": "ArrowLeft",
- "38": "ArrowUp",
- "39": "ArrowRight",
- "40": "ArrowDown",
- "45": "Insert",
- "46": "Delete",
- "112": "F1",
- "113": "F2",
- "114": "F3",
- "115": "F4",
- "116": "F5",
- "117": "F6",
- "118": "F7",
- "119": "F8",
- "120": "F9",
- "121": "F10",
- "122": "F11",
- "123": "F12",
- "144": "NumLock",
- "145": "ScrollLock",
- "224": "Meta"
-};
-/**
- * @param {object} nativeEvent Native browser event.
- * @return {string} Normalized `key` property.
- */
+ return false;
+}
-function getEventKey(nativeEvent) {
- if (nativeEvent.key) {
- // Normalize inconsistent values reported by browsers due to
- // implementations of a working draft specification.
- // FireFox implements `key` but returns `MozPrintableKey` for all
- // printable characters (normalized to `Unidentified`), ignore it.
- var key = normalizeKey[nativeEvent.key] || nativeEvent.key; // $FlowFixMe unable to index with a `mixed` value
+function shouldClientRenderOnMismatch(fiber) {
+ return (
+ (fiber.mode & ConcurrentMode) !== NoMode &&
+ (fiber.flags & DidCapture) === NoFlags$1
+ );
+}
- if (key !== "Unidentified") {
- return key;
- }
- } // Browser does not implement `key`, polyfill as much of it as we can.
+function throwOnHydrationMismatch(fiber) {
+ throw new Error(
+ "Hydration failed because the initial UI does not match what was " +
+ "rendered on the server."
+ );
+}
- if (nativeEvent.type === "keypress") {
- var charCode = getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- nativeEvent
- ); // The enter-key is technically both printable and non-printable and can
- // thus be captured by `keypress`, no other non-printable key should.
+function claimHydratableSingleton(fiber) {
+ {
+ if (!isHydrating) {
+ return;
+ }
- return charCode === 13 ? "Enter" : String.fromCharCode(charCode);
+ var currentRootContainer = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ var instance = (fiber.stateNode = resolveSingletonInstance(
+ fiber.type,
+ fiber.pendingProps,
+ currentRootContainer,
+ currentHostContext,
+ false
+ ));
+ hydrationParentFiber = fiber;
+ rootOrSingletonContext = true;
+ nextHydratableInstance = getFirstHydratableChild(instance);
}
+}
- if (nativeEvent.type === "keydown" || nativeEvent.type === "keyup") {
- // While user keyboard layout determines the actual meaning of each
- // `keyCode` value, almost all function keys have a universal value.
- // $FlowFixMe unable to index with a `mixed` value
- return translateToKey[nativeEvent.keyCode] || "Unidentified";
+function advanceToFirstAttemptableInstance(fiber) {
+ // fiber is HostComponent Fiber
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForInstance(
+ nextHydratableInstance,
+ fiber.type,
+ fiber.pendingProps
+ )
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
}
-
- return "";
}
-/**
- * Translation from modifier key to the associated property in the event.
- * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
- */
-
-var modifierKeyToProp = {
- Alt: "altKey",
- Control: "ctrlKey",
- Meta: "metaKey",
- Shift: "shiftKey"
-}; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
-// getModifierState. If getModifierState is not supported, we map it to a set of
-// modifier keys exposed by the event. In this case, Lock-keys are not supported.
-// $FlowFixMe[missing-local-annot]
-// $FlowFixMe[missing-this-annot]
-
-function modifierStateGetter(keyArg) {
- var syntheticEvent = this;
- var nativeEvent = syntheticEvent.nativeEvent;
- if (nativeEvent.getModifierState) {
- return nativeEvent.getModifierState(keyArg);
+function advanceToFirstAttemptableTextInstance() {
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForTextInstance(nextHydratableInstance)
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
}
-
- var keyProp = modifierKeyToProp[keyArg];
- return keyProp ? !!nativeEvent[keyProp] : false;
}
-function getEventModifierState(nativeEvent) {
- return modifierStateGetter;
+function advanceToFirstAttemptableSuspenseInstance() {
+ while (
+ nextHydratableInstance &&
+ shouldSkipHydratableForSuspenseInstance(nextHydratableInstance)
+ ) {
+ // Flow doesn't understand that inside this block nextHydratableInstance is not null
+ var instance = nextHydratableInstance;
+ nextHydratableInstance = getNextHydratableSibling(instance);
+ }
}
-/**
- * @interface KeyboardEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
-
-var KeyboardEventInterface = assign({}, UIEventInterface, {
- key: getEventKey,
- code: 0,
- location: 0,
- ctrlKey: 0,
- shiftKey: 0,
- altKey: 0,
- metaKey: 0,
- repeat: 0,
- locale: 0,
- getModifierState: getEventModifierState,
- // Legacy Interface
- charCode: function (event) {
- // `charCode` is the result of a KeyPress event and represents the value of
- // the actual printable character.
- // KeyPress is deprecated, but its replacement is not yet final and not
- // implemented in any major browser. Only KeyPress has charCode.
- if (event.type === "keypress") {
- return getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- event
- );
- }
- return 0;
- },
- keyCode: function (event) {
- // `keyCode` is the result of a KeyDown/Up event and represents the value of
- // physical keyboard key.
- // The actual meaning of the value depends on the users' keyboard layout
- // which cannot be detected. Assuming that it is a US keyboard layout
- // provides a surprisingly accurate mapping for US and European users.
- // Due to this, it is left to the user to implement at this time.
- if (event.type === "keydown" || event.type === "keyup") {
- return event.keyCode;
- }
+function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
- return 0;
- },
- which: function (event) {
- // `which` is an alias for either `keyCode` or `charCode` depending on the
- // type of the event.
- if (event.type === "keypress") {
- return getEventCharCode(
- // $FlowFixMe unable to narrow to `KeyboardEvent`
- event
- );
+ {
+ if (!isHydratableType(fiber.type, fiber.pendingProps)) {
+ // This fiber never hydrates from the DOM and always does an insert
+ fiber.flags = (fiber.flags & ~Hydrating) | Placement;
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
}
+ }
- if (event.type === "keydown" || event.type === "keyup") {
- return event.keyCode;
- }
+ var initialInstance = nextHydratableInstance;
- return 0;
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableInstance(fiber);
}
-});
-var SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface);
-/**
- * @interface PointerEvent
- * @see http://www.w3.org/TR/pointerevents/
- */
+ var nextInstance = nextHydratableInstance;
-var PointerEventInterface = assign({}, MouseEventInterface, {
- pointerId: 0,
- width: 0,
- height: 0,
- pressure: 0,
- tangentialPressure: 0,
- tiltX: 0,
- tiltY: 0,
- twist: 0,
- pointerType: 0,
- isPrimary: 0
-});
+ if (!nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
-var SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface);
-/**
- * @interface TouchEvent
- * @see http://www.w3.org/TR/touch-events/
- */
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ return;
+ }
-var TouchEventInterface = assign({}, UIEventInterface, {
- touches: 0,
- targetTouches: 0,
- changedTouches: 0,
- altKey: 0,
- metaKey: 0,
- ctrlKey: 0,
- shiftKey: 0,
- getModifierState: getEventModifierState
-});
+ var firstAttemptedInstance = nextInstance;
-var SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface);
-/**
- * @interface Event
- * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
- * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
- */
+ if (!tryHydrateInstance(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
-var TransitionEventInterface = assign({}, EventInterface, {
- propertyName: 0,
- elapsedTime: 0,
- pseudoElement: 0
-});
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
-var SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface);
-/**
- * @interface WheelEvent
- * @see http://www.w3.org/TR/DOM-Level-3-Events/
- */
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableInstance(fiber);
+ }
-var WheelEventInterface = assign({}, MouseEventInterface, {
- deltaX: function (event) {
- return "deltaX" in event
- ? event.deltaX // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
- : "wheelDeltaX" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDeltaX
- : 0;
- },
- deltaY: function (event) {
- return "deltaY" in event
- ? event.deltaY // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
- : "wheelDeltaY" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDeltaY // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
- : "wheelDelta" in event // $FlowFixMe assuming this is a number
- ? -event.wheelDelta
- : 0;
- },
- deltaZ: 0,
- // Browsers without "deltaMode" is reporting in raw wheel delta where one
- // notch on the scroll is always +/- 120, roughly equivalent to pixels.
- // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
- // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
- deltaMode: 0
-});
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateInstance(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ 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.
-var SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface);
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
+ }
+}
-var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
+function tryToClaimNextHydratableTextInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
-var START_KEYCODE = 229;
-var canUseCompositionEvent = canUseDOM && "CompositionEvent" in window;
-var documentMode = null;
+ var text = fiber.pendingProps;
+ var isHydratable = isHydratableText(text);
+ var initialInstance = nextHydratableInstance;
-if (canUseDOM && "documentMode" in document) {
- documentMode = document.documentMode;
-} // Webkit offers a very useful `textInput` event that can be used to
-// directly represent `beforeInput`. The IE `textinput` event is not as
-// useful, so we don't use it.
+ if (rootOrSingletonContext && isHydratable) {
+ // We may need to skip past certain nodes in these contexts.
+ // We don't skip if the text is not hydratable because we know no hydratables
+ // exist which could match this Fiber
+ advanceToFirstAttemptableTextInstance();
+ }
-var canUseTextInputEvent = canUseDOM && "TextEvent" in window && !documentMode; // In IE9+, we have access to composition events, but the data supplied
-// by the native compositionend event may be incorrect. Japanese ideographic
-// spaces, for instance (\u3000) are not recorded correctly.
+ var nextInstance = nextHydratableInstance;
-var useFallbackCompositionData =
- canUseDOM &&
- (!canUseCompositionEvent ||
- (documentMode && documentMode > 8 && documentMode <= 11));
-var SPACEBAR_CODE = 32;
-var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
+ if (!nextInstance || !isHydratable) {
+ // We exclude non hydrabable text because we know there are no matching hydratables.
+ // We either throw or insert depending on the render mode.
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
-function registerEvents$3() {
- registerTwoPhaseEvent("onBeforeInput", [
- "compositionend",
- "keypress",
- "textInput",
- "paste"
- ]);
- registerTwoPhaseEvent("onCompositionEnd", [
- "compositionend",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
- registerTwoPhaseEvent("onCompositionStart", [
- "compositionstart",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
- registerTwoPhaseEvent("onCompositionUpdate", [
- "compositionupdate",
- "focusout",
- "keydown",
- "keypress",
- "keyup",
- "mousedown"
- ]);
-} // Track whether we've ever handled a keypress on the space key.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ return;
+ }
-var hasSpaceKeypress = false;
-/**
- * Return whether a native keypress event is assumed to be a command.
- * This is required because Firefox fires `keypress` events for key commands
- * (cut, copy, select-all, etc.) even though no character is inserted.
- */
+ var firstAttemptedInstance = nextInstance;
-function isKeypressCommand(nativeEvent) {
- return (
- (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command.
- !(nativeEvent.ctrlKey && nativeEvent.altKey)
- );
-}
-/**
- * Translate native top level events into event types.
- */
+ if (!tryHydrateText(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
-function getCompositionEventType(domEventName) {
- switch (domEventName) {
- case "compositionstart":
- return "onCompositionStart";
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
- case "compositionend":
- return "onCompositionEnd";
+ if (rootOrSingletonContext && isHydratable) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableTextInstance();
+ }
- case "compositionupdate":
- return "onCompositionUpdate";
- }
-}
-/**
- * Does our fallback best-guess model think this event signifies that
- * composition has begun?
- */
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateText(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ 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.
-function isFallbackCompositionStart(domEventName, nativeEvent) {
- return domEventName === "keydown" && nativeEvent.keyCode === START_KEYCODE;
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
+ }
}
-/**
- * Does our fallback mode think that this event is the end of composition?
- */
-
-function isFallbackCompositionEnd(domEventName, nativeEvent) {
- switch (domEventName) {
- case "keyup":
- // Command keys insert or clear IME input.
- return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
- case "keydown":
- // Expect IME keyCode on each keydown. If we get any other
- // code we must have exited earlier.
- return nativeEvent.keyCode !== START_KEYCODE;
+function tryToClaimNextHydratableSuspenseInstance(fiber) {
+ if (!isHydrating) {
+ return;
+ }
- case "keypress":
- case "mousedown":
- case "focusout":
- // Events are not possible without cancelling IME.
- return true;
+ var initialInstance = nextHydratableInstance;
- default:
- return false;
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableSuspenseInstance();
}
-}
-/**
- * Google Input Tools provides composition data via a CustomEvent,
- * with the `data` property populated in the `detail` object. If this
- * is available on the event object, use it. If not, this is a plain
- * composition event and we have nothing special to extract.
- *
- * @param {object} nativeEvent
- * @return {?string}
- */
-function getDataFromCustomEvent(nativeEvent) {
- var detail = nativeEvent.detail;
+ var nextInstance = nextHydratableInstance;
- if (typeof detail === "object" && "data" in detail) {
- return detail.data;
- }
+ if (!nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // Nothing to hydrate. Make it an insertion.
- return null;
-}
-/**
- * Check if a composition event was triggered by Korean IME.
- * Our fallback mode does not work well with IE's Korean IME,
- * so just use native composition events when Korean IME is used.
- * Although CompositionEvent.locale property is deprecated,
- * it is available in IE, where our fallback mode is enabled.
- *
- * @param {object} nativeEvent
- * @return {boolean}
- */
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ return;
+ }
-function isUsingKoreanIME(nativeEvent) {
- return nativeEvent.locale === "ko";
-} // Track the current IME composition status, if any.
+ var firstAttemptedInstance = nextInstance;
-var isComposing = false;
-/**
- * @return {?object} A SyntheticCompositionEvent.
- */
+ if (!tryHydrateSuspense(fiber, nextInstance)) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnNonhydratedInstance(hydrationParentFiber, fiber);
+ throwOnHydrationMismatch();
+ } // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
-function extractCompositionEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
-) {
- var eventType;
- var fallbackData;
+ nextHydratableInstance = getNextHydratableSibling(nextInstance);
+ var prevHydrationParentFiber = hydrationParentFiber;
- if (canUseCompositionEvent) {
- eventType = getCompositionEventType(domEventName);
- } else if (!isComposing) {
- if (isFallbackCompositionStart(domEventName, nativeEvent)) {
- eventType = "onCompositionStart";
+ if (rootOrSingletonContext) {
+ // We may need to skip past certain nodes in these contexts
+ advanceToFirstAttemptableSuspenseInstance();
}
- } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) {
- eventType = "onCompositionEnd";
- }
- if (!eventType) {
- return null;
- }
+ if (
+ !nextHydratableInstance ||
+ !tryHydrateSuspense(fiber, nextHydratableInstance)
+ ) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = initialInstance;
+ 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.
- if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
- // The current composition is stored statically and must not be
- // overwritten while composition continues.
- if (!isComposing && eventType === "onCompositionStart") {
- isComposing = initialize(nativeEventTarget);
- } else if (eventType === "onCompositionEnd") {
- if (isComposing) {
- fallbackData = getData();
- }
- }
+ deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
}
+}
- var listeners = accumulateTwoPhaseListeners(targetInst, eventType);
-
- if (listeners.length > 0) {
- var event = new SyntheticCompositionEvent(
- eventType,
- domEventName,
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
+function prepareToHydrateHostInstance(fiber, hostContext) {
+ var instance = fiber.stateNode;
+ var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
+ var updatePayload = hydrateInstance(
+ instance,
+ fiber.type,
+ fiber.memoizedProps,
+ hostContext,
+ fiber,
+ shouldWarnIfMismatchDev
+ ); // TODO: Type this specific to this type of component.
- if (fallbackData) {
- // Inject data generated from fallback path into the synthetic event.
- // This matches the property of native CompositionEventInterface.
- // $FlowFixMe[incompatible-use]
- event.data = fallbackData;
- } else {
- var customData = getDataFromCustomEvent(nativeEvent);
+ 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 (customData !== null) {
- // $FlowFixMe[incompatible-use]
- event.data = customData;
- }
- }
+ if (updatePayload !== null) {
+ return true;
}
+
+ return false;
}
-function getNativeBeforeInputChars(domEventName, nativeEvent) {
- switch (domEventName) {
- case "compositionend":
- return getDataFromCustomEvent(nativeEvent);
+function prepareToHydrateHostTextInstance(fiber) {
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
+ var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
- case "keypress":
- /**
- * If native `textInput` events are available, our goal is to make
- * use of them. However, there is a special case: the spacebar key.
- * In Webkit, preventing default on a spacebar `textInput` event
- * cancels character insertion, but it *also* causes the browser
- * to fall back to its default spacebar behavior of scrolling the
- * page.
- *
- * Tracking at:
- * https://code.google.com/p/chromium/issues/detail?id=355103
- *
- * To avoid this issue, use the keypress event as if no `textInput`
- * event is available.
- */
- var which = nativeEvent.which;
+ 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 (which !== SPACEBAR_CODE) {
- return null;
- }
+ if (returnFiber !== null) {
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ var parentContainer = returnFiber.stateNode.containerInfo;
+ var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
+ didNotMatchHydratedContainerTextInstance(
+ parentContainer,
+ textInstance,
+ textContent, // TODO: Delete this argument when we remove the legacy root API.
+ isConcurrentMode,
+ shouldWarnIfMismatchDev
+ );
+ break;
+ }
- hasSpaceKeypress = true;
- return SPACEBAR_CHAR;
+ case HostSingleton:
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
- case "textInput":
- // Record the characters to be added to the DOM.
- var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled
- // it at the keypress level and bail immediately. Android Chrome
- // doesn't give us keycodes, so we need to ignore it.
+ var _isConcurrentMode2 =
+ (returnFiber.mode & ConcurrentMode) !== NoMode;
- if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
- return null;
+ didNotMatchHydratedTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ textInstance,
+ textContent, // TODO: Delete this argument when we remove the legacy root API.
+ _isConcurrentMode2,
+ shouldWarnIfMismatchDev
+ );
+ break;
+ }
}
+ }
+ }
- return chars;
+ return shouldUpdate;
+}
- default:
- // For other native event types, do nothing.
- return null;
+function prepareToHydrateHostSuspenseInstance(fiber) {
+ var suspenseState = fiber.memoizedState;
+ var suspenseInstance =
+ suspenseState !== null ? suspenseState.dehydrated : null;
+
+ if (!suspenseInstance) {
+ throw new Error(
+ "Expected to have a hydrated suspense instance. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
}
+
+ hydrateSuspenseInstance(suspenseInstance, fiber);
}
-/**
- * For browsers that do not provide the `textInput` event, extract the
- * appropriate string to use for SyntheticInputEvent.
- */
-function getFallbackBeforeInputChars(domEventName, nativeEvent) {
- // If we are currently composing (IME) and using a fallback to do so,
- // try to extract the composed characters from the fallback object.
- // If composition event is available, we extract a string only at
- // compositionevent, otherwise extract it at fallback events.
- if (isComposing) {
- if (
- domEventName === "compositionend" ||
- (!canUseCompositionEvent &&
- isFallbackCompositionEnd(domEventName, nativeEvent))
- ) {
- var chars = getData();
- reset();
- isComposing = false;
- return chars;
- }
+function skipPastDehydratedSuspenseInstance(fiber) {
+ var suspenseState = fiber.memoizedState;
+ var suspenseInstance =
+ suspenseState !== null ? suspenseState.dehydrated : null;
- return null;
+ if (!suspenseInstance) {
+ throw new Error(
+ "Expected to have a hydrated suspense instance. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
}
- switch (domEventName) {
- case "paste":
- // If a paste event occurs after a keypress, throw out the input
- // chars. Paste events should not lead to BeforeInput events.
- return null;
+ return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
+}
- case "keypress":
- /**
- * As of v27, Firefox may fire keypress events even when no character
- * will be inserted. A few possibilities:
- *
- * - `which` is `0`. Arrow keys, Esc key, etc.
- *
- * - `which` is the pressed key code, but no char is available.
- * Ex: 'AltGr + d` in Polish. There is no modified character for
- * this key combination and no character is inserted into the
- * document, but FF fires the keypress for char code `100` anyway.
- * No `input` event will occur.
- *
- * - `which` is the pressed key code, but a command combination is
- * being used. Ex: `Cmd+C`. No character is inserted, and no
- * `input` event will occur.
- */
- if (!isKeypressCommand(nativeEvent)) {
- // IE fires the `keypress` event when a user types an emoji via
- // Touch keyboard of Windows. In such a case, the `char` property
- // holds an emoji character like `\uD83D\uDE0A`. Because its length
- // is 2, the property `which` does not represent an emoji correctly.
- // In such a case, we directly return the `char` property instead of
- // using `which`.
- if (nativeEvent.char && nativeEvent.char.length > 1) {
- return nativeEvent.char;
- } else if (nativeEvent.which) {
- return String.fromCharCode(nativeEvent.which);
- }
- }
+function popToNextHostParent(fiber) {
+ hydrationParentFiber = fiber.return;
- return null;
+ while (hydrationParentFiber) {
+ switch (hydrationParentFiber.tag) {
+ case HostRoot:
+ case HostSingleton:
+ rootOrSingletonContext = true;
+ return;
- case "compositionend":
- return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)
- ? null
- : nativeEvent.data;
+ case HostComponent:
+ case SuspenseComponent:
+ rootOrSingletonContext = false;
+ return;
- default:
- return null;
+ default:
+ hydrationParentFiber = hydrationParentFiber.return;
+ }
}
}
-/**
- * Extract a SyntheticInputEvent for `beforeInput`, based on either native
- * `textInput` or fallback behavior.
- *
- * @return {?object} A SyntheticInputEvent.
- */
-
-function extractBeforeInputEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
-) {
- var chars;
-
- if (canUseTextInputEvent) {
- chars = getNativeBeforeInputChars(domEventName, nativeEvent);
- } else {
- chars = getFallbackBeforeInputChars(domEventName, nativeEvent);
- } // If no characters are being inserted, no BeforeInput event should
- // be fired.
- if (!chars) {
- return null;
+function popHydrationState(fiber) {
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
+ return false;
}
- var listeners = accumulateTwoPhaseListeners(targetInst, "onBeforeInput");
+ if (!isHydrating) {
+ // If we're not currently hydrating but we're in a hydration context, then
+ // we were an insertion and now need to pop up reenter hydration of our
+ // siblings.
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return false;
+ }
- if (listeners.length > 0) {
- var event = new SyntheticInputEvent(
- "onBeforeInput",
- "beforeinput",
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- }); // $FlowFixMe[incompatible-use]
+ var shouldClear = false;
- event.data = chars;
+ {
+ // With float we never clear the Root, or Singleton instances. We also do not clear Instances
+ // that have singleton text content
+ if (
+ fiber.tag !== HostRoot &&
+ fiber.tag !== HostSingleton &&
+ !(
+ fiber.tag === HostComponent &&
+ shouldSetTextContent(fiber.type, fiber.memoizedProps)
+ )
+ ) {
+ shouldClear = true;
+ }
}
-}
-/**
- * Create an `onBeforeInput` event to match
- * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
- *
- * This event plugin is based on the native `textInput` event
- * available in Chrome, Safari, Opera, and IE. This event fires after
- * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
- *
- * `beforeInput` is spec'd but not implemented in any browsers, and
- * the `input` event does not provide any useful information about what has
- * actually been added, contrary to the spec. Thus, `textInput` is the best
- * available event to identify the characters that have actually been inserted
- * into the target node.
- *
- * This plugin is also responsible for emitting `composition` events, thus
- * allowing us to share composition fallback code for both `beforeInput` and
- * `composition` event types.
- */
-function extractEvents$5(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- extractCompositionEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractBeforeInputEvent(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
-}
+ if (shouldClear) {
+ var nextInstance = nextHydratableInstance;
-/**
- * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
- */
-var supportedInputTypes = {
- color: true,
- date: true,
- datetime: true,
- "datetime-local": true,
- email: true,
- month: true,
- number: true,
- password: true,
- range: true,
- search: true,
- tel: true,
- text: true,
- time: true,
- url: true,
- week: true
-};
+ if (nextInstance) {
+ if (shouldClientRenderOnMismatch(fiber)) {
+ warnIfUnhydratedTailNodes(fiber);
+ throwOnHydrationMismatch();
+ } else {
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
+ }
+ }
+ }
+ }
-function isTextInputElement(elem) {
- var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
+ popToNextHostParent(fiber);
- if (nodeName === "input") {
- return !!supportedInputTypes[elem.type];
+ if (fiber.tag === SuspenseComponent) {
+ nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
+ } else {
+ nextHydratableInstance = hydrationParentFiber
+ ? getNextHydratableSibling(fiber.stateNode)
+ : null;
}
- if (nodeName === "textarea") {
- return true;
- }
+ return true;
+}
- return false;
+function hasUnhydratedTailNodes() {
+ return isHydrating && nextHydratableInstance !== null;
}
-/**
- * Checks if an event is supported in the current execution environment.
- *
- * NOTE: This will not work correctly for non-generic events such as `change`,
- * `reset`, `load`, `error`, and `select`.
- *
- * Borrows from Modernizr.
- *
- * @param {string} eventNameSuffix Event name, e.g. "click".
- * @return {boolean} True if the event is supported.
- * @internal
- * @license Modernizr 3.0.0pre (Custom Build) | MIT
- */
+function warnIfUnhydratedTailNodes(fiber) {
+ var nextInstance = nextHydratableInstance;
-function isEventSupported(eventNameSuffix) {
- if (!canUseDOM) {
- return false;
+ while (nextInstance) {
+ warnUnhydratedInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
}
+}
- var eventName = "on" + eventNameSuffix;
- var isSupported = eventName in document;
+function resetHydrationState() {
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+ didSuspendOrErrorDEV = false;
+}
- if (!isSupported) {
- var element = document.createElement("div");
- element.setAttribute(eventName, "return;");
- isSupported = typeof element[eventName] === "function";
+function upgradeHydrationErrorsToRecoverable() {
+ if (hydrationErrors !== null) {
+ // Successfully completed a forced client render. The errors that occurred
+ // during the hydration attempt are now recovered. We will log them in
+ // commit phase, once the entire tree has finished.
+ queueRecoverableErrors(hydrationErrors);
+ hydrationErrors = null;
}
-
- return isSupported;
}
-function registerEvents$2() {
- registerTwoPhaseEvent("onChange", [
- "change",
- "click",
- "focusin",
- "focusout",
- "input",
- "keydown",
- "keyup",
- "selectionchange"
- ]);
+function getIsHydrating() {
+ return isHydrating;
}
-function createAndAccumulateChangeEvent(
- dispatchQueue,
- inst,
- nativeEvent,
- target
-) {
- // Flag this event loop as needing state restore.
- enqueueStateRestore(target);
- var listeners = accumulateTwoPhaseListeners(inst, "onChange");
-
- if (listeners.length > 0) {
- var event = new SyntheticEvent(
- "onChange",
- "change",
- null,
- nativeEvent,
- target
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
+function queueHydrationError(error) {
+ if (hydrationErrors === null) {
+ hydrationErrors = [error];
+ } else {
+ hydrationErrors.push(error);
}
}
-/**
- * For IE shims
- */
-var activeElement$1 = null;
-var activeElementInst$1 = null;
-/**
- * SECTION: handle `change` event
- */
+// we wait until the current render is over (either finished or interrupted)
+// before adding it to the fiber/hook queue. Push to this array so we can
+// access the queue, fiber, update, et al later.
-function shouldUseChangeEvent(elem) {
- var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
- return (
- nodeName === "select" || (nodeName === "input" && elem.type === "file")
- );
-}
+var concurrentQueues = [];
+var concurrentQueuesIndex = 0;
+var concurrentlyUpdatedLanes = NoLanes;
+function finishQueueingConcurrentUpdates() {
+ var endIndex = concurrentQueuesIndex;
+ concurrentQueuesIndex = 0;
+ concurrentlyUpdatedLanes = NoLanes;
+ var i = 0;
-function manualDispatchChangeEvent(nativeEvent) {
- var dispatchQueue = [];
- createAndAccumulateChangeEvent(
- dispatchQueue,
- activeElementInst$1,
- nativeEvent,
- getEventTarget(nativeEvent)
- ); // If change and propertychange bubbled, we'd just bind to it like all the
- // other events and have it go through ReactBrowserEventEmitter. Since it
- // doesn't, we manually listen for the events and so we have to enqueue and
- // process the abstract event manually.
- //
- // Batching is necessary here in order to ensure that all event handlers run
- // before the next rerender (including event handlers attached to ancestor
- // elements instead of directly on the input). Without this, controlled
- // components don't work properly in conjunction with event bubbling because
- // the component is rerendered and the value reverted before all the event
- // handlers can run. See https://github.com/facebook/react/issues/708.
+ while (i < endIndex) {
+ var fiber = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var queue = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var update = concurrentQueues[i];
+ concurrentQueues[i++] = null;
+ var lane = concurrentQueues[i];
+ concurrentQueues[i++] = null;
- batchedUpdates$1(runEventInBatch, dispatchQueue);
-}
+ if (queue !== null && update !== null) {
+ var pending = queue.pending;
-function runEventInBatch(dispatchQueue) {
- processDispatchQueue(dispatchQueue, 0);
-}
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
+ }
-function getInstIfValueChanged(targetInst) {
- var targetNode = getNodeFromInstance(targetInst);
+ queue.pending = update;
+ }
- if (updateValueIfChanged(targetNode)) {
- return targetInst;
+ if (lane !== NoLane) {
+ markUpdateLaneFromFiberToRoot(fiber, update, lane);
+ }
}
}
-
-function getTargetInstForChangeEvent(domEventName, targetInst) {
- if (domEventName === "change") {
- return targetInst;
- }
+function getConcurrentlyUpdatedLanes() {
+ return concurrentlyUpdatedLanes;
}
-/**
- * SECTION: handle `input` event
- */
-var isInputEventSupported = false;
+function enqueueUpdate$1(fiber, queue, update, lane) {
+ // Don't update the `childLanes` on the return path yet. If we already in
+ // the middle of rendering, wait until after it has completed.
+ concurrentQueues[concurrentQueuesIndex++] = fiber;
+ concurrentQueues[concurrentQueuesIndex++] = queue;
+ concurrentQueues[concurrentQueuesIndex++] = update;
+ concurrentQueues[concurrentQueuesIndex++] = lane;
+ concurrentlyUpdatedLanes = mergeLanes(concurrentlyUpdatedLanes, lane); // The fiber's `lane` field is used in some places to check if any work is
+ // scheduled, to perform an eager bailout, so we need to update it immediately.
+ // TODO: We should probably move this to the "shared" queue instead.
-if (canUseDOM) {
- // IE9 claims to support the input event but fails to trigger it when
- // deleting text, so we ignore its input events.
- isInputEventSupported =
- isEventSupported("input") &&
- (!document.documentMode || document.documentMode > 9);
+ fiber.lanes = mergeLanes(fiber.lanes, lane);
+ var alternate = fiber.alternate;
+
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, lane);
+ }
}
-/**
- * (For IE <=9) Starts tracking propertychange events on the passed-in element
- * and override the value property so that we can distinguish user events from
- * value changes in JS.
- */
-function startWatchingForValueChange(target, targetInst) {
- activeElement$1 = target;
- activeElementInst$1 = targetInst;
- activeElement$1.attachEvent("onpropertychange", handlePropertyChange);
+function enqueueConcurrentHookUpdate(fiber, queue, update, lane) {
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
+ return getRootForUpdatedFiber(fiber);
}
-/**
- * (For IE <=9) Removes the event listeners from the currently-tracked element,
- * if any exists.
- */
+function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update) {
+ // This function is used to queue an update that doesn't need a rerender. The
+ // only reason we queue it is in case there's a subsequent higher priority
+ // update that causes it to be rebased.
+ var lane = NoLane;
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane); // Usually we can rely on the upcoming render phase to process the concurrent
+ // queue. However, since this is a bail out, we're not scheduling any work
+ // here. So the update we just queued will leak until something else happens
+ // to schedule work (if ever).
+ //
+ // Check if we're currently in the middle of rendering a tree, and if not,
+ // process the queue immediately to prevent a leak.
-function stopWatchingForValueChange() {
- if (!activeElement$1) {
- return;
+ var isConcurrentlyRendering = getWorkInProgressRoot() !== null;
+
+ if (!isConcurrentlyRendering) {
+ finishQueueingConcurrentUpdates();
}
+}
+function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
+ var concurrentQueue = queue;
+ var concurrentUpdate = update;
+ enqueueUpdate$1(fiber, concurrentQueue, concurrentUpdate, lane);
+ return getRootForUpdatedFiber(fiber);
+}
+function enqueueConcurrentRenderForLane(fiber, lane) {
+ enqueueUpdate$1(fiber, null, null, lane);
+ return getRootForUpdatedFiber(fiber);
+} // Calling this function outside this module should only be done for backwards
+// compatibility and should always be accompanied by a warning.
- activeElement$1.detachEvent("onpropertychange", handlePropertyChange);
- activeElement$1 = null;
- activeElementInst$1 = null;
+function unsafe_markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
+ // NOTE: For Hyrum's Law reasons, if an infinite update loop is detected, it
+ // should throw before `markUpdateLaneFromFiberToRoot` is called. But this is
+ // undefined behavior and we can change it if we need to; it just so happens
+ // that, at the time of this writing, there's an internal product test that
+ // happens to rely on this.
+ var root = getRootForUpdatedFiber(sourceFiber);
+ markUpdateLaneFromFiberToRoot(sourceFiber, null, lane);
+ return root;
}
-/**
- * (For IE <=9) Handles a propertychange event, sending a `change` event if
- * the value of the active element has changed.
- */
-// $FlowFixMe[missing-local-annot]
-function handlePropertyChange(nativeEvent) {
- if (nativeEvent.propertyName !== "value") {
- return;
- }
+function markUpdateLaneFromFiberToRoot(sourceFiber, update, lane) {
+ // Update the source fiber's lanes
+ sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
+ var alternate = sourceFiber.alternate;
- if (getInstIfValueChanged(activeElementInst$1)) {
- manualDispatchChangeEvent(nativeEvent);
- }
-}
+ if (alternate !== null) {
+ alternate.lanes = mergeLanes(alternate.lanes, lane);
+ } // Walk the parent path to the root and update the child lanes.
-function handleEventsForInputEventPolyfill(domEventName, target, targetInst) {
- if (domEventName === "focusin") {
- // In IE9, propertychange fires for most input events but is buggy and
- // doesn't fire when text is deleted, but conveniently, selectionchange
- // appears to fire in all of the remaining cases so we catch those and
- // forward the event if the value has changed
- // In either case, we don't want to call the event handler if the value
- // is changed from JS so we redefine a setter for `.value` that updates
- // our activeElementValue variable, allowing us to ignore those changes
- //
- // stopWatching() should be a noop here but we call it just in case we
- // missed a blur event somehow.
- stopWatchingForValueChange();
- startWatchingForValueChange(target, targetInst);
- } else if (domEventName === "focusout") {
- stopWatchingForValueChange();
- }
-} // For IE8 and IE9.
+ var isHidden = false;
+ var parent = sourceFiber.return;
+ var node = sourceFiber;
-function getTargetInstForInputEventPolyfill(domEventName, targetInst) {
- if (
- domEventName === "selectionchange" ||
- domEventName === "keyup" ||
- domEventName === "keydown"
- ) {
- // On the selectionchange event, the target is just document which isn't
- // helpful for us so just check activeElement instead.
- //
- // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
- // propertychange on the first input event after setting `value` from a
- // script and fires only keydown, keypress, keyup. Catching keyup usually
- // gets it and catching keydown lets us fire an event for the first
- // keystroke if user does a key repeat (it'll be a little delayed: right
- // before the second keystroke). Other input methods (e.g., paste) seem to
- // fire selectionchange normally.
- return getInstIfValueChanged(activeElementInst$1);
- }
-}
-/**
- * SECTION: handle `click` event
- */
+ while (parent !== null) {
+ parent.childLanes = mergeLanes(parent.childLanes, lane);
+ alternate = parent.alternate;
-function shouldUseClickEvent(elem) {
- // Use the `click` event to detect changes to checkbox and radio inputs.
- // This approach works across all browsers, whereas `change` does not fire
- // until `blur` in IE8.
- var nodeName = elem.nodeName;
- return (
- nodeName &&
- nodeName.toLowerCase() === "input" &&
- (elem.type === "checkbox" || elem.type === "radio")
- );
-}
+ if (alternate !== null) {
+ alternate.childLanes = mergeLanes(alternate.childLanes, lane);
+ }
-function getTargetInstForClickEvent(domEventName, targetInst) {
- if (domEventName === "click") {
- return getInstIfValueChanged(targetInst);
+ if (parent.tag === OffscreenComponent) {
+ // Check if this offscreen boundary is currently hidden.
+ //
+ // The instance may be null if the Offscreen parent was unmounted. Usually
+ // the parent wouldn't be reachable in that case because we disconnect
+ // fibers from the tree when they are deleted. However, there's a weird
+ // edge case where setState is called on a fiber that was interrupted
+ // before it ever mounted. Because it never mounts, it also never gets
+ // deleted. Because it never gets deleted, its return pointer never gets
+ // disconnected. Which means it may be attached to a deleted Offscreen
+ // parent node. (This discovery suggests it may be better for memory usage
+ // if we don't attach the `return` pointer until the commit phase, though
+ // in order to do that we'd need some other way to track the return
+ // pointer during the initial render, like on the stack.)
+ //
+ // This case is always accompanied by a warning, but we still need to
+ // account for it. (There may be other cases that we haven't discovered,
+ // too.)
+ var offscreenInstance = parent.stateNode;
+
+ if (
+ offscreenInstance !== null &&
+ !(offscreenInstance._visibility & OffscreenVisible)
+ ) {
+ isHidden = true;
+ }
+ }
+
+ node = parent;
+ parent = parent.return;
}
-}
-function getTargetInstForInputOrChangeEvent(domEventName, targetInst) {
- if (domEventName === "input" || domEventName === "change") {
- return getInstIfValueChanged(targetInst);
+ if (isHidden && update !== null && node.tag === HostRoot) {
+ var root = node.stateNode;
+ markHiddenUpdate(root, update, lane);
}
}
-function handleControlledInputBlur(node) {
- var state = node._wrapperState;
+function getRootForUpdatedFiber(sourceFiber) {
+ // TODO: We will detect and infinite update loop and throw even if this fiber
+ // has already unmounted. This isn't really necessary but it happens to be the
+ // current behavior we've used for several release cycles. Consider not
+ // performing this check if the updated fiber already unmounted, since it's
+ // not possible for that to cause an infinite update loop.
+ throwIfInfiniteUpdateLoopDetected(); // When a setState happens, we must ensure the root is scheduled. Because
+ // update queues do not have a backpointer to the root, the only way to do
+ // this currently is to walk up the return path. This used to not be a big
+ // deal because we would have to walk up the return path to set
+ // the `childLanes`, anyway, but now those two traversals happen at
+ // different times.
+ // TODO: Consider adding a `root` backpointer on the update queue.
- if (!state || !state.controlled || node.type !== "number") {
- return;
- }
+ detectUpdateOnUnmountedFiber(sourceFiber, sourceFiber);
+ var node = sourceFiber;
+ var parent = node.return;
- if (!disableInputAttributeSyncing) {
- // If controlled, assign the value attribute to the current value on blur
- setDefaultValue(node, "number", node.value);
+ while (parent !== null) {
+ detectUpdateOnUnmountedFiber(sourceFiber, node);
+ node = parent;
+ parent = node.return;
}
+
+ return node.tag === HostRoot ? node.stateNode : null;
}
-/**
- * This plugin creates an `onChange` event that normalizes change events
- * across form elements. This event fires at a time when it's possible to
- * change the element's value without seeing a flicker.
- *
- * Supported elements are:
- * - input (see `isTextInputElement`)
- * - textarea
- * - select
- */
-function extractEvents$4(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
- var getTargetInstFunc, handleEventFunc;
+function detectUpdateOnUnmountedFiber(sourceFiber, parent) {
+ {
+ var alternate = parent.alternate;
- if (shouldUseChangeEvent(targetNode)) {
- getTargetInstFunc = getTargetInstForChangeEvent;
- } else if (isTextInputElement(targetNode)) {
- if (isInputEventSupported) {
- getTargetInstFunc = getTargetInstForInputOrChangeEvent;
- } else {
- getTargetInstFunc = getTargetInstForInputEventPolyfill;
- handleEventFunc = handleEventsForInputEventPolyfill;
+ if (
+ alternate === null &&
+ (parent.flags & (Placement | Hydrating)) !== NoFlags$1
+ ) {
+ warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
}
- } else if (shouldUseClickEvent(targetNode)) {
- getTargetInstFunc = getTargetInstForClickEvent;
- } else if (
- enableCustomElementPropertySupport &&
- targetInst &&
- isCustomComponent(targetInst.elementType, targetInst.memoizedProps)
- ) {
- getTargetInstFunc = getTargetInstForChangeEvent;
}
+}
- if (getTargetInstFunc) {
- var inst = getTargetInstFunc(domEventName, targetInst);
-
- if (inst) {
- createAndAccumulateChangeEvent(
- dispatchQueue,
- inst,
- nativeEvent,
- nativeEventTarget
- );
- return;
- }
- }
+var UpdateState = 0;
+var ReplaceState = 1;
+var ForceUpdate = 2;
+var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`.
+// It should only be read right after calling `processUpdateQueue`, via
+// `checkHasForceUpdateAfterProcessing`.
- if (handleEventFunc) {
- handleEventFunc(domEventName, targetNode, targetInst);
- } // When blurring, set the value attribute for number inputs
+var hasForceUpdate = false;
+var didWarnUpdateInsideUpdate;
+var currentlyProcessingQueue;
- if (domEventName === "focusout") {
- handleControlledInputBlur(targetNode);
- }
+{
+ didWarnUpdateInsideUpdate = false;
+ currentlyProcessingQueue = null;
}
-function registerEvents$1() {
- registerDirectEvent("onMouseEnter", ["mouseout", "mouseover"]);
- registerDirectEvent("onMouseLeave", ["mouseout", "mouseover"]);
- registerDirectEvent("onPointerEnter", ["pointerout", "pointerover"]);
- registerDirectEvent("onPointerLeave", ["pointerout", "pointerover"]);
+function initializeUpdateQueue(fiber) {
+ var queue = {
+ baseState: fiber.memoizedState,
+ firstBaseUpdate: null,
+ lastBaseUpdate: null,
+ shared: {
+ pending: null,
+ lanes: NoLanes,
+ hiddenCallbacks: null
+ },
+ callbacks: null
+ };
+ fiber.updateQueue = queue;
}
-/**
- * For almost every interaction we care about, there will be both a top-level
- * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
- * we do not extract duplicate events. However, moving the mouse into the
- * browser from outside will not fire a `mouseout` event. In this case, we use
- * the `mouseover` top-level event.
- */
-
-function extractEvents$3(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var isOverEvent =
- domEventName === "mouseover" || domEventName === "pointerover";
- var isOutEvent = domEventName === "mouseout" || domEventName === "pointerout";
-
- if (isOverEvent && !isReplayingEvent(nativeEvent)) {
- // If this is an over event with a target, we might have already dispatched
- // the event in the out event of the other target. If this is replayed,
- // then it's because we couldn't dispatch against this target previously
- // so we have to do it now instead.
- var related = nativeEvent.relatedTarget || nativeEvent.fromElement;
+function cloneUpdateQueue(current, workInProgress) {
+ // Clone the update queue from current. Unless it's already a clone.
+ var queue = workInProgress.updateQueue;
+ var currentQueue = current.updateQueue;
- if (related) {
- // If the related node is managed by React, we can assume that we have
- // already dispatched the corresponding events during its mouseout.
- if (
- getClosestInstanceFromNode(related) ||
- isContainerMarkedAsRoot(related)
- ) {
- return;
- }
- }
+ if (queue === currentQueue) {
+ var clone = {
+ baseState: currentQueue.baseState,
+ firstBaseUpdate: currentQueue.firstBaseUpdate,
+ lastBaseUpdate: currentQueue.lastBaseUpdate,
+ shared: currentQueue.shared,
+ callbacks: null
+ };
+ workInProgress.updateQueue = clone;
}
+}
+function createUpdate(lane) {
+ var update = {
+ lane: lane,
+ tag: UpdateState,
+ payload: null,
+ callback: null,
+ next: null
+ };
+ return update;
+}
+function enqueueUpdate(fiber, update, lane) {
+ var updateQueue = fiber.updateQueue;
- if (!isOutEvent && !isOverEvent) {
- // Must not be a mouse or pointer in or out - ignoring.
- return;
+ if (updateQueue === null) {
+ // Only occurs if the fiber has been unmounted.
+ return null;
}
- var win; // TODO: why is this nullable in the types but we read from it?
+ var sharedQueue = updateQueue.shared;
- if (nativeEventTarget.window === nativeEventTarget) {
- // `nativeEventTarget` is probably a window object.
- win = nativeEventTarget;
- } else {
- // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
- var doc = nativeEventTarget.ownerDocument;
+ {
+ if (
+ currentlyProcessingQueue === sharedQueue &&
+ !didWarnUpdateInsideUpdate
+ ) {
+ var componentName = getComponentNameFromFiber(fiber);
- if (doc) {
- win = doc.defaultView || doc.parentWindow;
- } else {
- win = window;
+ error(
+ "An update (setState, replaceState, or forceUpdate) was scheduled " +
+ "from inside an update function. Update functions should be pure, " +
+ "with zero side-effects. Consider using componentDidUpdate or a " +
+ "callback.\n\nPlease update the following component: %s",
+ componentName
+ );
+
+ didWarnUpdateInsideUpdate = true;
}
}
- var from;
- var to;
-
- if (isOutEvent) {
- var _related = nativeEvent.relatedTarget || nativeEvent.toElement;
+ if (isUnsafeClassRenderPhaseUpdate(fiber)) {
+ // This is an unsafe render phase update. Add directly to the update
+ // queue so we can process it immediately during the current render.
+ var pending = sharedQueue.pending;
- from = targetInst;
- to = _related ? getClosestInstanceFromNode(_related) : null;
+ if (pending === null) {
+ // This is the first update. Create a circular list.
+ update.next = update;
+ } else {
+ update.next = pending.next;
+ pending.next = update;
+ }
- if (to !== null) {
- var nearestMounted = getNearestMountedFiber(to);
- var tag = to.tag;
+ sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering
+ // this fiber. This is for backwards compatibility in the case where you
+ // update a different component during render phase than the one that is
+ // currently renderings (a pattern that is accompanied by a warning).
- if (
- to !== nearestMounted ||
- (tag !== HostComponent && tag !== HostSingleton && tag !== HostText)
- ) {
- to = null;
- }
- }
+ return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
} else {
- // Moving to a node from outside the window.
- from = null;
- to = targetInst;
+ return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
}
+}
+function entangleTransitions(root, fiber, lane) {
+ var updateQueue = fiber.updateQueue;
- if (from === to) {
- // Nothing pertains to our managed components.
+ if (updateQueue === null) {
+ // Only occurs if the fiber has been unmounted.
return;
}
- var SyntheticEventCtor = SyntheticMouseEvent;
- var leaveEventType = "onMouseLeave";
- var enterEventType = "onMouseEnter";
- var eventTypePrefix = "mouse";
+ var sharedQueue = updateQueue.shared;
- if (domEventName === "pointerout" || domEventName === "pointerover") {
- SyntheticEventCtor = SyntheticPointerEvent;
- leaveEventType = "onPointerLeave";
- enterEventType = "onPointerEnter";
- eventTypePrefix = "pointer";
- }
+ if (isTransitionLane(lane)) {
+ var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must
+ // have finished. We can remove them from the shared queue, which represents
+ // a superset of the actually pending lanes. In some cases we may entangle
+ // more than we need to, but that's OK. In fact it's worse if we *don't*
+ // entangle when we should.
- var fromNode = from == null ? win : getNodeFromInstance(from);
- var toNode = to == null ? win : getNodeFromInstance(to);
- var leave = new SyntheticEventCtor(
- leaveEventType,
- eventTypePrefix + "leave",
- from,
- nativeEvent,
- nativeEventTarget
- );
- leave.target = fromNode;
- leave.relatedTarget = toNode;
- var enter = null; // We should only process this nativeEvent if we are processing
- // the first ancestor. Next time, we will ignore the event.
+ queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
- var nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget);
+ var newQueueLanes = mergeLanes(queueLanes, lane);
+ sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
+ // the lane finished since the last time we entangled it. So we need to
+ // entangle it again, just to be sure.
- if (nativeTargetInst === targetInst) {
- var enterEvent = new SyntheticEventCtor(
- enterEventType,
- eventTypePrefix + "enter",
- to,
- nativeEvent,
- nativeEventTarget
- );
- enterEvent.target = toNode;
- enterEvent.relatedTarget = fromNode;
- enter = enterEvent;
+ markRootEntangled(root, newQueueLanes);
}
-
- accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
}
+function enqueueCapturedUpdate(workInProgress, capturedUpdate) {
+ // Captured updates are updates that are thrown by a child during the render
+ // phase. They should be discarded if the render is aborted. Therefore,
+ // we should only put them on the work-in-progress queue, not the current one.
+ var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone.
-/**
- * inlined Object.is polyfill to avoid requiring consumers ship their own
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- */
-function is(x, y) {
- return (
- (x === y && (x !== 0 || 1 / x === 1 / y)) || (x !== x && y !== y) // eslint-disable-line no-self-compare
- );
-}
+ var current = workInProgress.alternate;
-var objectIs = typeof Object.is === "function" ? Object.is : is; // $FlowFixMe[method-unbinding]
+ if (current !== null) {
+ var currentQueue = current.updateQueue;
-/**
- * Performs equality by iterating through keys on an object and returning false
- * when any key has values which are not strictly equal between the arguments.
- * Returns true when the values of all keys are strictly equal.
- */
+ if (queue === currentQueue) {
+ // The work-in-progress queue is the same as current. This happens when
+ // we bail out on a parent fiber that then captures an error thrown by
+ // a child. Since we want to append the update only to the work-in
+ // -progress queue, we need to clone the updates. We usually clone during
+ // processUpdateQueue, but that didn't happen in this case because we
+ // skipped over the parent when we bailed out.
+ var newFirst = null;
+ var newLast = null;
+ var firstBaseUpdate = queue.firstBaseUpdate;
-function shallowEqual(objA, objB) {
- if (objectIs(objA, objB)) {
- return true;
- }
+ if (firstBaseUpdate !== null) {
+ // Loop through the updates and clone them.
+ var update = firstBaseUpdate;
- if (
- typeof objA !== "object" ||
- objA === null ||
- typeof objB !== "object" ||
- objB === null
- ) {
- return false;
- }
+ do {
+ var clone = {
+ lane: update.lane,
+ tag: update.tag,
+ payload: update.payload,
+ // When this update is rebased, we should not fire its
+ // callback again.
+ callback: null,
+ next: null
+ };
- var keysA = Object.keys(objA);
- var keysB = Object.keys(objB);
+ if (newLast === null) {
+ newFirst = newLast = clone;
+ } else {
+ newLast.next = clone;
+ newLast = clone;
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
- if (keysA.length !== keysB.length) {
- return false;
- } // Test for A's keys different from B.
+ update = update.next;
+ } while (update !== null); // Append the captured update the end of the cloned list.
- for (var i = 0; i < keysA.length; i++) {
- var currentKey = keysA[i];
+ if (newLast === null) {
+ newFirst = newLast = capturedUpdate;
+ } else {
+ newLast.next = capturedUpdate;
+ newLast = capturedUpdate;
+ }
+ } else {
+ // There are no base updates.
+ newFirst = newLast = capturedUpdate;
+ }
- if (
- !hasOwnProperty.call(objB, currentKey) ||
- !objectIs(objA[currentKey], objB[currentKey])
- ) {
- return false;
+ queue = {
+ baseState: currentQueue.baseState,
+ firstBaseUpdate: newFirst,
+ lastBaseUpdate: newLast,
+ shared: currentQueue.shared,
+ callbacks: currentQueue.callbacks
+ };
+ workInProgress.updateQueue = queue;
+ return;
}
- }
-
- return true;
-}
+ } // Append the update to the end of the list.
-/**
- * Given any node return the first leaf node without children.
- *
- * @param {DOMElement|DOMTextNode} node
- * @return {DOMElement|DOMTextNode}
- */
+ var lastBaseUpdate = queue.lastBaseUpdate;
-function getLeafNode(node) {
- while (node && node.firstChild) {
- node = node.firstChild;
+ if (lastBaseUpdate === null) {
+ queue.firstBaseUpdate = capturedUpdate;
+ } else {
+ lastBaseUpdate.next = capturedUpdate;
}
- return node;
-}
-/**
- * Get the next sibling within a container. This will walk up the
- * DOM if a node's siblings have been exhausted.
- *
- * @param {DOMElement|DOMTextNode} node
- * @return {?DOMElement|DOMTextNode}
- */
-
-function getSiblingNode(node) {
- while (node) {
- if (node.nextSibling) {
- return node.nextSibling;
- }
-
- node = node.parentNode;
- }
+ queue.lastBaseUpdate = capturedUpdate;
}
-/**
- * Get object describing the nodes which contain characters at offset.
- *
- * @param {DOMElement|DOMTextNode} root
- * @param {number} offset
- * @return {?object}
- */
-
-function getNodeForCharacterOffset(root, offset) {
- var node = getLeafNode(root);
- var nodeStart = 0;
- var nodeEnd = 0;
- while (node) {
- if (node.nodeType === TEXT_NODE) {
- nodeEnd = nodeStart + node.textContent.length;
+function getStateFromUpdate(
+ workInProgress,
+ queue,
+ update,
+ prevState,
+ nextProps,
+ instance
+) {
+ switch (update.tag) {
+ case ReplaceState: {
+ var payload = update.payload;
- if (nodeStart <= offset && nodeEnd >= offset) {
- return {
- node: node,
- offset: offset - nodeStart
- };
- }
+ if (typeof payload === "function") {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ }
- nodeStart = nodeEnd;
- }
+ var nextState = payload.call(instance, prevState, nextProps);
- node = getLeafNode(getSiblingNode(node));
- }
-}
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
-/**
- * @param {DOMElement} outerNode
- * @return {?object}
- */
+ try {
+ payload.call(instance, prevState, nextProps);
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
+ }
-function getOffsets(outerNode) {
- var ownerDocument = outerNode.ownerDocument;
- var win = (ownerDocument && ownerDocument.defaultView) || window;
- var selection = win.getSelection && win.getSelection();
+ exitDisallowedContextReadInDEV();
+ }
- if (!selection || selection.rangeCount === 0) {
- return null;
- }
+ return nextState;
+ } // State object
- var anchorNode = selection.anchorNode,
- anchorOffset = selection.anchorOffset,
- focusNode = selection.focusNode,
- focusOffset = selection.focusOffset; // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the
- // up/down buttons on an . Anonymous divs do not seem to
- // expose properties, triggering a "Permission denied error" if any of its
- // properties are accessed. The only seemingly possible way to avoid erroring
- // is to access a property that typically works for non-anonymous divs and
- // catch any error that may otherwise arise. See
- // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
+ return payload;
+ }
- try {
- /* eslint-disable ft-flow/no-unused-expressions */
- anchorNode.nodeType;
- focusNode.nodeType;
- /* eslint-enable ft-flow/no-unused-expressions */
- } catch (e) {
- return null;
- }
+ case CaptureUpdate: {
+ workInProgress.flags =
+ (workInProgress.flags & ~ShouldCapture) | DidCapture;
+ }
+ // Intentional fallthrough
- return getModernOffsetsFromPoints(
- outerNode,
- anchorNode,
- anchorOffset,
- focusNode,
- focusOffset
- );
-}
-/**
- * Returns {start, end} where `start` is the character/codepoint index of
- * (anchorNode, anchorOffset) within the textContent of `outerNode`, and
- * `end` is the index of (focusNode, focusOffset).
- *
- * Returns null if you pass in garbage input but we should probably just crash.
- *
- * Exported only for testing.
- */
+ case UpdateState: {
+ var _payload = update.payload;
+ var partialState;
-function getModernOffsetsFromPoints(
- outerNode,
- anchorNode,
- anchorOffset,
- focusNode,
- focusOffset
-) {
- var length = 0;
- var start = -1;
- var end = -1;
- var indexWithinAnchor = 0;
- var indexWithinFocus = 0;
- var node = outerNode;
- var parentNode = null;
+ if (typeof _payload === "function") {
+ // Updater function
+ {
+ enterDisallowedContextReadInDEV();
+ }
- outer: while (true) {
- var next = null;
+ partialState = _payload.call(instance, prevState, nextProps);
- while (true) {
- if (
- node === anchorNode &&
- (anchorOffset === 0 || node.nodeType === TEXT_NODE)
- ) {
- start = length + anchorOffset;
- }
+ {
+ if (workInProgress.mode & StrictLegacyMode) {
+ setIsStrictModeForDevtools(true);
- if (
- node === focusNode &&
- (focusOffset === 0 || node.nodeType === TEXT_NODE)
- ) {
- end = length + focusOffset;
- }
+ try {
+ _payload.call(instance, prevState, nextProps);
+ } finally {
+ setIsStrictModeForDevtools(false);
+ }
+ }
- if (node.nodeType === TEXT_NODE) {
- length += node.nodeValue.length;
+ exitDisallowedContextReadInDEV();
+ }
+ } else {
+ // Partial state object
+ partialState = _payload;
}
- if ((next = node.firstChild) === null) {
- break;
- } // Moving from `node` to its first child `next`.
+ if (partialState === null || partialState === undefined) {
+ // Null and undefined are treated as no-ops.
+ return prevState;
+ } // Merge the partial state and the previous state.
- parentNode = node;
- node = next;
+ return assign({}, prevState, partialState);
}
- while (true) {
- if (node === outerNode) {
- // If `outerNode` has children, this is always the second time visiting
- // it. If it has no children, this is still the first loop, and the only
- // valid selection is anchorNode and focusNode both equal to this node
- // and both offsets 0, in which case we will have handled above.
- break outer;
- }
-
- if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) {
- start = length;
- }
-
- if (parentNode === focusNode && ++indexWithinFocus === focusOffset) {
- end = length;
- }
-
- if ((next = node.nextSibling) !== null) {
- break;
- }
-
- node = parentNode;
- parentNode = node.parentNode;
- } // Moving from `node` to its next sibling `next`.
-
- node = next;
- }
-
- if (start === -1 || end === -1) {
- // This should never happen. (Would happen if the anchor/focus nodes aren't
- // actually inside the passed-in node.)
- return null;
+ case ForceUpdate: {
+ hasForceUpdate = true;
+ return prevState;
+ }
}
- return {
- start: start,
- end: end
- };
+ return prevState;
}
-/**
- * In modern non-IE browsers, we can support both forward and backward
- * selections.
- *
- * Note: IE10+ supports the Selection object, but it does not support
- * the `extend` method, which means that even in modern IE, it's not possible
- * to programmatically create a backward selection. Thus, for all IE
- * versions, we use the old IE API to create our selections.
- *
- * @param {DOMElement|DOMTextNode} node
- * @param {object} offsets
- */
-function setOffsets(node, offsets) {
- var doc = node.ownerDocument || document;
- var win = (doc && doc.defaultView) || window; // Edge fails with "Object expected" in some scenarios.
- // (For instance: TinyMCE editor used in a list component that supports pasting to add more,
- // fails when pasting 100+ items)
+function processUpdateQueue(workInProgress, props, instance, renderLanes) {
+ // This is always non-null on a ClassComponent or HostRoot
+ var queue = workInProgress.updateQueue;
+ hasForceUpdate = false;
- if (!win.getSelection) {
- return;
+ {
+ currentlyProcessingQueue = queue.shared;
}
- var selection = win.getSelection();
- var length = node.textContent.length;
- var start = Math.min(offsets.start, length);
- var end = offsets.end === undefined ? start : Math.min(offsets.end, length); // IE 11 uses modern selection, but doesn't support the extend method.
- // Flip backward selections, so we can set with a single range.
-
- if (!selection.extend && start > end) {
- var temp = end;
- end = start;
- start = temp;
- }
+ var firstBaseUpdate = queue.firstBaseUpdate;
+ var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue.
- var startMarker = getNodeForCharacterOffset(node, start);
- var endMarker = getNodeForCharacterOffset(node, end);
+ var pendingQueue = queue.shared.pending;
- if (startMarker && endMarker) {
- if (
- selection.rangeCount === 1 &&
- selection.anchorNode === startMarker.node &&
- selection.anchorOffset === startMarker.offset &&
- selection.focusNode === endMarker.node &&
- selection.focusOffset === endMarker.offset
- ) {
- return;
- }
+ if (pendingQueue !== null) {
+ queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first
+ // and last so that it's non-circular.
- var range = doc.createRange();
- range.setStart(startMarker.node, startMarker.offset);
- selection.removeAllRanges();
+ var lastPendingUpdate = pendingQueue;
+ var firstPendingUpdate = lastPendingUpdate.next;
+ lastPendingUpdate.next = null; // Append pending updates to base queue
- if (start > end) {
- selection.addRange(range);
- selection.extend(endMarker.node, endMarker.offset);
+ if (lastBaseUpdate === null) {
+ firstBaseUpdate = firstPendingUpdate;
} else {
- range.setEnd(endMarker.node, endMarker.offset);
- selection.addRange(range);
+ lastBaseUpdate.next = firstPendingUpdate;
}
- }
-}
-
-function isTextNode(node) {
- return node && node.nodeType === TEXT_NODE;
-}
-function containsNode$1(outerNode, innerNode) {
- if (!outerNode || !innerNode) {
- return false;
- } else if (outerNode === innerNode) {
- return true;
- } else if (isTextNode(outerNode)) {
- return false;
- } else if (isTextNode(innerNode)) {
- return containsNode$1(outerNode, innerNode.parentNode);
- } else if ("contains" in outerNode) {
- return outerNode.contains(innerNode);
- } else if (outerNode.compareDocumentPosition) {
- return !!(outerNode.compareDocumentPosition(innerNode) & 16);
- } else {
- return false;
- }
-}
+ lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then
+ // we need to transfer the updates to that queue, too. Because the base
+ // queue is a singly-linked list with no cycles, we can append to both
+ // lists and take advantage of structural sharing.
+ // TODO: Pass `current` as argument
-function isInDocument(node) {
- return (
- node &&
- node.ownerDocument &&
- containsNode$1(node.ownerDocument.documentElement, node)
- );
-}
+ var current = workInProgress.alternate;
-function isSameOriginFrame(iframe) {
- try {
- // Accessing the contentDocument of a HTMLIframeElement can cause the browser
- // to throw, e.g. if it has a cross-origin src attribute.
- // Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g:
- // iframe.contentDocument.defaultView;
- // A safety way is to access one of the cross origin properties: Window or Location
- // Which might result in "SecurityError" DOM Exception and it is compatible to Safari.
- // https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl
- return typeof iframe.contentWindow.location.href === "string";
- } catch (err) {
- return false;
- }
-}
+ if (current !== null) {
+ // This is always non-null on a ClassComponent or HostRoot
+ var currentQueue = current.updateQueue;
+ var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
-function getActiveElementDeep() {
- var win = window;
- var element = getActiveElement();
+ if (currentLastBaseUpdate !== lastBaseUpdate) {
+ if (currentLastBaseUpdate === null) {
+ currentQueue.firstBaseUpdate = firstPendingUpdate;
+ } else {
+ currentLastBaseUpdate.next = firstPendingUpdate;
+ }
- while (element instanceof win.HTMLIFrameElement) {
- if (isSameOriginFrame(element)) {
- win = element.contentWindow;
- } else {
- return element;
+ currentQueue.lastBaseUpdate = lastPendingUpdate;
+ }
}
+ } // These values may change as we process the queue.
- element = getActiveElement(win.document);
- }
+ if (firstBaseUpdate !== null) {
+ // Iterate through the list of updates to compute the result.
+ var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
+ // from the original lanes.
- return element;
-}
-/**
- * @ReactInputSelection: React input selection module. Based on Selection.js,
- * but modified to be suitable for react and has a couple of bug fixes (doesn't
- * assume buttons have range selections allowed).
- * Input selection module for React.
- */
+ var newLanes = NoLanes;
+ var newBaseState = null;
+ var newFirstBaseUpdate = null;
+ var newLastBaseUpdate = null;
+ var update = firstBaseUpdate;
-/**
- * @hasSelectionCapabilities: we get the element types that support selection
- * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
- * and `selectionEnd` rows.
- */
+ do {
+ // An extra OffscreenLane bit is added to updates that were made to
+ // a hidden tree, so that we can distinguish them from updates that were
+ // already there when the tree was hidden.
+ var updateLane = removeLanes(update.lane, OffscreenLane);
+ var isHiddenUpdate = updateLane !== update.lane; // Check if this update was made while the tree was hidden. If so, then
+ // it's not a "base" update and we should disregard the extra base lanes
+ // that were added to renderLanes when we entered the Offscreen tree.
-function hasSelectionCapabilities(elem) {
- var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
- return (
- nodeName &&
- ((nodeName === "input" &&
- (elem.type === "text" ||
- elem.type === "search" ||
- elem.type === "tel" ||
- elem.type === "url" ||
- elem.type === "password")) ||
- nodeName === "textarea" ||
- elem.contentEditable === "true")
- );
-}
-function getSelectionInformation() {
- var focusedElem = getActiveElementDeep();
- return {
- focusedElem: focusedElem,
- selectionRange: hasSelectionCapabilities(focusedElem)
- ? getSelection$1(focusedElem)
- : null
- };
-}
-/**
- * @restoreSelection: If any selection information was potentially lost,
- * restore it. This is useful when performing operations that could remove dom
- * nodes and place them back in, resulting in focus being lost.
- */
+ var shouldSkipUpdate = isHiddenUpdate
+ ? !isSubsetOfLanes(getWorkInProgressRootRenderLanes(), updateLane)
+ : !isSubsetOfLanes(renderLanes, updateLane);
-function restoreSelection(priorSelectionInformation) {
- var curFocusedElem = getActiveElementDeep();
- var priorFocusedElem = priorSelectionInformation.focusedElem;
- var priorSelectionRange = priorSelectionInformation.selectionRange;
+ if (shouldSkipUpdate) {
+ // Priority is insufficient. Skip this update. If this is the first
+ // skipped update, the previous update/state is the new base
+ // update/state.
+ var clone = {
+ lane: updateLane,
+ tag: update.tag,
+ payload: update.payload,
+ callback: update.callback,
+ next: null
+ };
- if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
- if (
- priorSelectionRange !== null &&
- hasSelectionCapabilities(priorFocusedElem)
- ) {
- setSelection(priorFocusedElem, priorSelectionRange);
- } // Focusing a node can change the scroll position, which is undesirable
+ if (newLastBaseUpdate === null) {
+ newFirstBaseUpdate = newLastBaseUpdate = clone;
+ newBaseState = newState;
+ } else {
+ newLastBaseUpdate = newLastBaseUpdate.next = clone;
+ } // Update the remaining priority in the queue.
- var ancestors = [];
- var ancestor = priorFocusedElem;
+ newLanes = mergeLanes(newLanes, updateLane);
+ } else {
+ // This update does have sufficient priority.
+ if (newLastBaseUpdate !== null) {
+ var _clone = {
+ // This update is going to be committed so we never want uncommit
+ // it. Using NoLane works because 0 is a subset of all bitmasks, so
+ // this will never be skipped by the check above.
+ lane: NoLane,
+ tag: update.tag,
+ payload: update.payload,
+ // When this update is rebased, we should not fire its
+ // callback again.
+ callback: null,
+ next: null
+ };
+ newLastBaseUpdate = newLastBaseUpdate.next = _clone;
+ } // Process this update.
- while ((ancestor = ancestor.parentNode)) {
- if (ancestor.nodeType === ELEMENT_NODE) {
- ancestors.push({
- element: ancestor,
- left: ancestor.scrollLeft,
- top: ancestor.scrollTop
- });
+ newState = getStateFromUpdate(
+ workInProgress,
+ queue,
+ update,
+ newState,
+ props,
+ instance
+ );
+ var callback = update.callback;
+
+ if (callback !== null) {
+ workInProgress.flags |= Callback;
+
+ if (isHiddenUpdate) {
+ workInProgress.flags |= Visibility;
+ }
+
+ var callbacks = queue.callbacks;
+
+ if (callbacks === null) {
+ queue.callbacks = [callback];
+ } else {
+ callbacks.push(callback);
+ }
+ }
+ } // $FlowFixMe[incompatible-type] we bail out when we get a null
+
+ update = update.next;
+
+ if (update === null) {
+ pendingQueue = queue.shared.pending;
+
+ if (pendingQueue === null) {
+ break;
+ } else {
+ // An update was scheduled from inside a reducer. Add the new
+ // pending updates to the end of the list and keep processing.
+ var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we
+ // unravel them when transferring them to the base queue.
+
+ var _firstPendingUpdate = _lastPendingUpdate.next;
+ _lastPendingUpdate.next = null;
+ update = _firstPendingUpdate;
+ queue.lastBaseUpdate = _lastPendingUpdate;
+ queue.shared.pending = null;
+ }
}
- }
+ } while (true);
- if (typeof priorFocusedElem.focus === "function") {
- priorFocusedElem.focus();
+ if (newLastBaseUpdate === null) {
+ newBaseState = newState;
}
- for (var i = 0; i < ancestors.length; i++) {
- var info = ancestors[i];
- info.element.scrollLeft = info.left;
- info.element.scrollTop = info.top;
- }
+ queue.baseState = newBaseState;
+ queue.firstBaseUpdate = newFirstBaseUpdate;
+ queue.lastBaseUpdate = newLastBaseUpdate;
+
+ if (firstBaseUpdate === null) {
+ // `queue.lanes` is used for entangling transitions. We can set it back to
+ // zero once the queue is empty.
+ queue.shared.lanes = NoLanes;
+ } // Set the remaining expiration time to be whatever is remaining in the queue.
+ // This should be fine because the only two other things that contribute to
+ // expiration time are props and context. We're already in the middle of the
+ // begin phase by the time we start processing the queue, so we've already
+ // dealt with the props. Context in components that specify
+ // shouldComponentUpdate is tricky; but we'll have to account for
+ // that regardless.
+
+ markSkippedUpdateLanes(newLanes);
+ workInProgress.lanes = newLanes;
+ workInProgress.memoizedState = newState;
}
-}
-/**
- * @getSelection: Gets the selection bounds of a focused textarea, input or
- * contentEditable node.
- * -@input: Look up selection bounds of this input
- * -@return {start: selectionStart, end: selectionEnd}
- */
-function getSelection$1(input) {
- var selection;
+ {
+ currentlyProcessingQueue = null;
+ }
+}
- if ("selectionStart" in input) {
- // Modern browser with input or textarea.
- selection = {
- start: input.selectionStart,
- end: input.selectionEnd
- };
- } else {
- // Content editable or old IE textarea.
- selection = getOffsets(input);
+function callCallback(callback, context) {
+ if (typeof callback !== "function") {
+ throw new Error(
+ "Invalid argument passed as callback. Expected a function. Instead " +
+ ("received: " + callback)
+ );
}
- return (
- selection || {
- start: 0,
- end: 0
- }
- );
+ callback.call(context);
}
-/**
- * @setSelection: Sets the selection bounds of a textarea or input and focuses
- * the input.
- * -@input Set selection bounds of this input or textarea
- * -@offsets Object of same form that is returned from get*
- */
-function setSelection(input, offsets) {
- var start = offsets.start;
- var end = offsets.end;
+function resetHasForceUpdateBeforeProcessing() {
+ hasForceUpdate = false;
+}
+function checkHasForceUpdateAfterProcessing() {
+ return hasForceUpdate;
+}
+function deferHiddenCallbacks(updateQueue) {
+ // When an update finishes on a hidden component, its callback should not
+ // be fired until/unless the component is made visible again. Stash the
+ // callback on the shared queue object so it can be fired later.
+ var newHiddenCallbacks = updateQueue.callbacks;
- if (end === undefined) {
- end = start;
- }
+ if (newHiddenCallbacks !== null) {
+ var existingHiddenCallbacks = updateQueue.shared.hiddenCallbacks;
- if ("selectionStart" in input) {
- input.selectionStart = start;
- input.selectionEnd = Math.min(end, input.value.length);
- } else {
- setOffsets(input, offsets);
+ if (existingHiddenCallbacks === null) {
+ updateQueue.shared.hiddenCallbacks = newHiddenCallbacks;
+ } else {
+ updateQueue.shared.hiddenCallbacks =
+ existingHiddenCallbacks.concat(newHiddenCallbacks);
+ }
}
}
+function commitHiddenCallbacks(updateQueue, context) {
+ // This component is switching from hidden -> visible. Commit any callbacks
+ // that were previously deferred.
+ var hiddenCallbacks = updateQueue.shared.hiddenCallbacks;
-var skipSelectionChangeEvent =
- canUseDOM && "documentMode" in document && document.documentMode <= 11;
+ if (hiddenCallbacks !== null) {
+ updateQueue.shared.hiddenCallbacks = null;
-function registerEvents() {
- registerTwoPhaseEvent("onSelect", [
- "focusout",
- "contextmenu",
- "dragend",
- "focusin",
- "keydown",
- "keyup",
- "mousedown",
- "mouseup",
- "selectionchange"
- ]);
+ for (var i = 0; i < hiddenCallbacks.length; i++) {
+ var callback = hiddenCallbacks[i];
+ callCallback(callback, context);
+ }
+ }
}
+function commitCallbacks(updateQueue, context) {
+ var callbacks = updateQueue.callbacks;
-var activeElement = null;
-var activeElementInst = null;
-var lastSelection = null;
-var mouseDown = false;
-/**
- * Get an object which is a unique representation of the current selection.
- *
- * The return value will not be consistent across nodes or browsers, but
- * two identical selections on the same node will return identical objects.
- */
+ if (callbacks !== null) {
+ updateQueue.callbacks = null;
-function getSelection(node) {
- if ("selectionStart" in node && hasSelectionCapabilities(node)) {
- return {
- start: node.selectionStart,
- end: node.selectionEnd
- };
- } else {
- var win = (node.ownerDocument && node.ownerDocument.defaultView) || window;
- var selection = win.getSelection();
- return {
- anchorNode: selection.anchorNode,
- anchorOffset: selection.anchorOffset,
- focusNode: selection.focusNode,
- focusOffset: selection.focusOffset
- };
+ for (var i = 0; i < callbacks.length; i++) {
+ var callback = callbacks[i];
+ callCallback(callback, context);
+ }
}
}
-/**
- * Get document associated with the event target.
- */
-function getEventTargetDocument(eventTarget) {
- return eventTarget.window === eventTarget
- ? eventTarget.document
- : eventTarget.nodeType === DOCUMENT_NODE
- ? eventTarget
- : eventTarget.ownerDocument;
-}
/**
- * Poll selection to see whether it's changed.
- *
- * @param {object} nativeEvent
- * @param {object} nativeEventTarget
- * @return {?SyntheticEvent}
+ * Performs equality by iterating through keys on an object and returning false
+ * when any key has values which are not strictly equal between the arguments.
+ * Returns true when the values of all keys are strictly equal.
*/
-function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
- // Ensure we have the right element, and that the user is not dragging a
- // selection (this matches native `select` event behavior). In HTML5, select
- // fires only on input and textarea thus if there's no focused element we
- // won't dispatch.
- var doc = getEventTargetDocument(nativeEventTarget);
+function shallowEqual(objA, objB) {
+ if (objectIs(objA, objB)) {
+ return true;
+ }
if (
- mouseDown ||
- activeElement == null ||
- activeElement !== getActiveElement(doc)
+ typeof objA !== "object" ||
+ objA === null ||
+ typeof objB !== "object" ||
+ objB === null
) {
- return;
- } // Only fire when selection has actually changed.
+ return false;
+ }
- var currentSelection = getSelection(activeElement);
+ var keysA = Object.keys(objA);
+ var keysB = Object.keys(objB);
- if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
- lastSelection = currentSelection;
- var listeners = accumulateTwoPhaseListeners(activeElementInst, "onSelect");
+ if (keysA.length !== keysB.length) {
+ return false;
+ } // Test for A's keys different from B.
- if (listeners.length > 0) {
- var event = new SyntheticEvent(
- "onSelect",
- "select",
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- event.target = activeElement;
+ for (var i = 0; i < keysA.length; i++) {
+ var currentKey = keysA[i];
+
+ if (
+ !hasOwnProperty.call(objB, currentKey) ||
+ !objectIs(objA[currentKey], objB[currentKey])
+ ) {
+ return false;
}
}
+
+ return true;
}
-/**
- * This plugin creates an `onSelect` event that normalizes select events
- * across form elements.
- *
- * Supported elements are:
- * - input (see `isTextInputElement`)
- * - textarea
- * - contentEditable
- *
- * This differs from native browser implementations in the following ways:
- * - Fires on contentEditable fields as well as inputs.
- * - Fires for collapsed selection.
- * - Fires after user input.
- */
-function extractEvents$2(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
+var ReactStrictModeWarnings = {
+ recordUnsafeLifecycleWarnings: function (fiber, instance) {},
+ flushPendingUnsafeLifecycleWarnings: function () {},
+ recordLegacyContextWarning: function (fiber, instance) {},
+ flushLegacyContextWarning: function () {},
+ discardPendingWarnings: function () {}
+};
- switch (domEventName) {
- // Track the input node that has focus.
- case "focusin":
- if (
- isTextInputElement(targetNode) ||
- targetNode.contentEditable === "true"
- ) {
- activeElement = targetNode;
- activeElementInst = targetInst;
- lastSelection = null;
+{
+ var findStrictRoot = function (fiber) {
+ var maybeStrictRoot = null;
+ var node = fiber;
+
+ while (node !== null) {
+ if (node.mode & StrictLegacyMode) {
+ maybeStrictRoot = node;
}
- break;
+ node = node.return;
+ }
- case "focusout":
- activeElement = null;
- activeElementInst = null;
- lastSelection = null;
- break;
- // Don't fire the event while the user is dragging. This matches the
- // semantics of the native select event.
+ return maybeStrictRoot;
+ };
- case "mousedown":
- mouseDown = true;
- break;
+ var setToSortedString = function (set) {
+ var array = [];
+ set.forEach(function (value) {
+ array.push(value);
+ });
+ return array.sort().join(", ");
+ };
- case "contextmenu":
- case "mouseup":
- case "dragend":
- mouseDown = false;
- constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
- break;
- // Chrome and IE fire non-standard event when selection is changed (and
- // sometimes when it hasn't). IE's event fires out of order with respect
- // to key and input events on deletion, so we discard it.
- //
- // Firefox doesn't support selectionchange, so check selection status
- // after each key entry. The selection changes after keydown and before
- // keyup, but we check on keydown as well in the case of holding down a
- // key, when multiple keydown events are fired but only one keyup is.
- // This is also our approach for IE handling, for the reason above.
+ var pendingComponentWillMountWarnings = [];
+ var pendingUNSAFE_ComponentWillMountWarnings = [];
+ var pendingComponentWillReceivePropsWarnings = [];
+ var pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ var pendingComponentWillUpdateWarnings = [];
+ var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about.
- case "selectionchange":
- if (skipSelectionChangeEvent) {
- break;
- }
+ var didWarnAboutUnsafeLifecycles = new Set();
- // falls through
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (
+ fiber,
+ instance
+ ) {
+ // Dedupe strategy: Warn once per component.
+ if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
+ return;
+ }
- case "keydown":
- case "keyup":
- constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
- }
-}
+ if (
+ typeof instance.componentWillMount === "function" && // Don't warn about react-lifecycles-compat polyfilled components.
+ instance.componentWillMount.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillMountWarnings.push(fiber);
+ }
-/**
- * Generate a mapping of standard vendor prefixes using the defined style property and event name.
- *
- * @param {string} styleProp
- * @param {string} eventName
- * @returns {object}
- */
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillMount === "function"
+ ) {
+ pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
+ }
-function makePrefixMap(styleProp, eventName) {
- var prefixes = {};
- prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
- prefixes["Webkit" + styleProp] = "webkit" + eventName;
- prefixes["Moz" + styleProp] = "moz" + eventName;
- return prefixes;
-}
-/**
- * A list of event names to a configurable list of vendor prefixes.
- */
+ if (
+ typeof instance.componentWillReceiveProps === "function" &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
+ }
-var vendorPrefixes = {
- animationend: makePrefixMap("Animation", "AnimationEnd"),
- animationiteration: makePrefixMap("Animation", "AnimationIteration"),
- animationstart: makePrefixMap("Animation", "AnimationStart"),
- transitionend: makePrefixMap("Transition", "TransitionEnd")
-};
-/**
- * Event names that have already been detected and prefixed (if applicable).
- */
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillReceiveProps === "function"
+ ) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
+ }
-var prefixedEventNames = {};
-/**
- * Element to check for prefixes on.
- */
+ if (
+ typeof instance.componentWillUpdate === "function" &&
+ instance.componentWillUpdate.__suppressDeprecationWarning !== true
+ ) {
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
-var style = {};
-/**
- * Bootstrap if a DOM exists.
- */
+ if (
+ fiber.mode & StrictLegacyMode &&
+ typeof instance.UNSAFE_componentWillUpdate === "function"
+ ) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
+ }
+ };
-if (canUseDOM) {
- style = document.createElement("div").style; // On some platforms, in particular some releases of Android 4.x,
- // the un-prefixed "animation" and "transition" properties are defined on the
- // style object but the events that fire will still be prefixed, so we need
- // to check if the un-prefixed events are usable, and if not remove them from the map.
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
+ // We do an initial pass to gather component names
+ var componentWillMountUniqueNames = new Set();
- if (!("AnimationEvent" in window)) {
- delete vendorPrefixes.animationend.animation;
- delete vendorPrefixes.animationiteration.animation;
- delete vendorPrefixes.animationstart.animation;
- } // Same as above
+ if (pendingComponentWillMountWarnings.length > 0) {
+ pendingComponentWillMountWarnings.forEach(function (fiber) {
+ componentWillMountUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillMountWarnings = [];
+ }
- if (!("TransitionEvent" in window)) {
- delete vendorPrefixes.transitionend.transition;
- }
-}
-/**
- * Attempts to determine the correct vendor prefixed event name.
- *
- * @param {string} eventName
- * @returns {string}
- */
+ var UNSAFE_componentWillMountUniqueNames = new Set();
-function getVendorPrefixedEventName(eventName) {
- if (prefixedEventNames[eventName]) {
- return prefixedEventNames[eventName];
- } else if (!vendorPrefixes[eventName]) {
- return eventName;
- }
+ if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillMountUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ }
- var prefixMap = vendorPrefixes[eventName];
+ var componentWillReceivePropsUniqueNames = new Set();
- for (var styleProp in prefixMap) {
- if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
- return (prefixedEventNames[eventName] = prefixMap[styleProp]);
+ if (pendingComponentWillReceivePropsWarnings.length > 0) {
+ pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
+ componentWillReceivePropsUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillReceivePropsWarnings = [];
}
- }
- return eventName;
-}
+ var UNSAFE_componentWillReceivePropsUniqueNames = new Set();
-var ANIMATION_END = getVendorPrefixedEventName("animationend");
-var ANIMATION_ITERATION = getVendorPrefixedEventName("animationiteration");
-var ANIMATION_START = getVendorPrefixedEventName("animationstart");
-var TRANSITION_END = getVendorPrefixedEventName("transitionend");
+ if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillReceivePropsUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ }
-var topLevelEventsToReactNames = new Map(); // NOTE: Capitalization is important in this list!
-//
-// E.g. it needs "pointerDown", not "pointerdown".
-// This is because we derive both React name ("onPointerDown")
-// and DOM name ("pointerdown") from the same list.
-//
-// Exceptions that don't match this convention are listed separately.
-//
-// prettier-ignore
+ var componentWillUpdateUniqueNames = new Set();
-var simpleEventPluginEvents = ['abort', 'auxClick', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'gotPointerCapture', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'lostPointerCapture', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'pointerCancel', 'pointerDown', 'pointerMove', 'pointerOut', 'pointerOver', 'pointerUp', 'progress', 'rateChange', 'reset', 'resize', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'touchCancel', 'touchEnd', 'touchStart', 'volumeChange', 'scroll', 'toggle', 'touchMove', 'waiting', 'wheel'];
+ if (pendingComponentWillUpdateWarnings.length > 0) {
+ pendingComponentWillUpdateWarnings.forEach(function (fiber) {
+ componentWillUpdateUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingComponentWillUpdateWarnings = [];
+ }
-{
- // Special case: these two events don't have on* React handler
- // and are only accessible via the createEventHandle API.
- topLevelEventsToReactNames.set("beforeblur", null);
- topLevelEventsToReactNames.set("afterblur", null);
-}
+ var UNSAFE_componentWillUpdateUniqueNames = new Set();
-function registerSimpleEvent(domEventName, reactName) {
- topLevelEventsToReactNames.set(domEventName, reactName);
- registerTwoPhaseEvent(reactName, [domEventName]);
-}
+ if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
+ pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) {
+ UNSAFE_componentWillUpdateUniqueNames.add(
+ getComponentNameFromFiber(fiber) || "Component"
+ );
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
+ } // Finally, we flush all the warnings
+ // UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
-function registerSimpleEvents() {
- for (var i = 0; i < simpleEventPluginEvents.length; i++) {
- var eventName = simpleEventPluginEvents[i];
- var domEventName = eventName.toLowerCase();
- var capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
- registerSimpleEvent(domEventName, "on" + capitalizedEvent);
- } // Special cases where event names don't match.
+ if (UNSAFE_componentWillMountUniqueNames.size > 0) {
+ var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames);
- registerSimpleEvent(ANIMATION_END, "onAnimationEnd");
- registerSimpleEvent(ANIMATION_ITERATION, "onAnimationIteration");
- registerSimpleEvent(ANIMATION_START, "onAnimationStart");
- registerSimpleEvent("dblclick", "onDoubleClick");
- registerSimpleEvent("focusin", "onFocus");
- registerSimpleEvent("focusout", "onBlur");
- registerSimpleEvent(TRANSITION_END, "onTransitionEnd");
-}
+ error(
+ "Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
+ "\nPlease update the following components: %s",
+ sortedNames
+ );
+ }
-function extractEvents$1(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- var reactName = topLevelEventsToReactNames.get(domEventName);
+ if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
+ var _sortedNames = setToSortedString(
+ UNSAFE_componentWillReceivePropsUniqueNames
+ );
- if (reactName === undefined) {
- return;
- }
+ error(
+ "Using UNSAFE_componentWillReceiveProps in strict mode is not recommended " +
+ "and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* If you're updating state whenever props change, " +
+ "refactor your code to use memoization techniques or move it to " +
+ "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames
+ );
+ }
- var SyntheticEventCtor = SyntheticEvent;
- var reactEventType = domEventName;
+ if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
+ var _sortedNames2 = setToSortedString(
+ UNSAFE_componentWillUpdateUniqueNames
+ );
- switch (domEventName) {
- case "keypress":
- // Firefox creates a keypress event for function keys too. This removes
- // the unwanted keypress events. Enter is however both printable and
- // non-printable. One would expect Tab to be as well (but it isn't).
- if (getEventCharCode(nativeEvent) === 0) {
- return;
- }
+ error(
+ "Using UNSAFE_componentWillUpdate in strict mode is not recommended " +
+ "and may indicate bugs in your code. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames2
+ );
+ }
- /* falls through */
+ if (componentWillMountUniqueNames.size > 0) {
+ var _sortedNames3 = setToSortedString(componentWillMountUniqueNames);
- case "keydown":
- case "keyup":
- SyntheticEventCtor = SyntheticKeyboardEvent;
- break;
+ warn(
+ "componentWillMount has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move code with side effects to componentDidMount, and set initial state in the constructor.\n" +
+ "* Rename componentWillMount to UNSAFE_componentWillMount to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames3
+ );
+ }
- case "focusin":
- reactEventType = "focus";
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ if (componentWillReceivePropsUniqueNames.size > 0) {
+ var _sortedNames4 = setToSortedString(
+ componentWillReceivePropsUniqueNames
+ );
- case "focusout":
- reactEventType = "blur";
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ warn(
+ "componentWillReceiveProps has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* If you're updating state whenever props change, refactor your " +
+ "code to use memoization techniques or move it to " +
+ "static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n" +
+ "* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames4
+ );
+ }
- case "beforeblur":
- case "afterblur":
- SyntheticEventCtor = SyntheticFocusEvent;
- break;
+ if (componentWillUpdateUniqueNames.size > 0) {
+ var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames);
- case "click":
- // Firefox creates a click event on right mouse clicks. This removes the
- // unwanted click events.
- if (nativeEvent.button === 2) {
- return;
- }
+ warn(
+ "componentWillUpdate has been renamed, and is not recommended for use. " +
+ "See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n" +
+ "* Move data fetching code or side effects to componentDidUpdate.\n" +
+ "* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress " +
+ "this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. " +
+ "To rename all deprecated lifecycles to their new names, you can run " +
+ "`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n" +
+ "\nPlease update the following components: %s",
+ _sortedNames5
+ );
+ }
+ };
- /* falls through */
+ var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about.
- case "auxclick":
- case "dblclick":
- case "mousedown":
- case "mousemove":
- case "mouseup": // TODO: Disabled elements should not respond to mouse events
+ var didWarnAboutLegacyContext = new Set();
- /* falls through */
+ ReactStrictModeWarnings.recordLegacyContextWarning = function (
+ fiber,
+ instance
+ ) {
+ var strictRoot = findStrictRoot(fiber);
- case "mouseout":
- case "mouseover":
- case "contextmenu":
- SyntheticEventCtor = SyntheticMouseEvent;
- break;
+ if (strictRoot === null) {
+ error(
+ "Expected to find a StrictMode component in a strict mode tree. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
- case "drag":
- case "dragend":
- case "dragenter":
- case "dragexit":
- case "dragleave":
- case "dragover":
- case "dragstart":
- case "drop":
- SyntheticEventCtor = SyntheticDragEvent;
- break;
+ return;
+ } // Dedup strategy: Warn once per component.
- case "touchcancel":
- case "touchend":
- case "touchmove":
- case "touchstart":
- SyntheticEventCtor = SyntheticTouchEvent;
- break;
+ if (didWarnAboutLegacyContext.has(fiber.type)) {
+ return;
+ }
- case ANIMATION_END:
- case ANIMATION_ITERATION:
- case ANIMATION_START:
- SyntheticEventCtor = SyntheticAnimationEvent;
- break;
+ var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
- case TRANSITION_END:
- SyntheticEventCtor = SyntheticTransitionEvent;
- break;
+ if (
+ fiber.type.contextTypes != null ||
+ fiber.type.childContextTypes != null ||
+ (instance !== null && typeof instance.getChildContext === "function")
+ ) {
+ if (warningsForRoot === undefined) {
+ warningsForRoot = [];
+ pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
+ }
- case "scroll":
- SyntheticEventCtor = SyntheticUIEvent;
- break;
+ warningsForRoot.push(fiber);
+ }
+ };
- case "wheel":
- SyntheticEventCtor = SyntheticWheelEvent;
- break;
+ ReactStrictModeWarnings.flushLegacyContextWarning = function () {
+ pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
+ if (fiberArray.length === 0) {
+ return;
+ }
- case "copy":
- case "cut":
- case "paste":
- SyntheticEventCtor = SyntheticClipboardEvent;
- break;
+ var firstFiber = fiberArray[0];
+ var uniqueNames = new Set();
+ fiberArray.forEach(function (fiber) {
+ uniqueNames.add(getComponentNameFromFiber(fiber) || "Component");
+ didWarnAboutLegacyContext.add(fiber.type);
+ });
+ var sortedNames = setToSortedString(uniqueNames);
- case "gotpointercapture":
- case "lostpointercapture":
- case "pointercancel":
- case "pointerdown":
- case "pointermove":
- case "pointerout":
- case "pointerover":
- case "pointerup":
- SyntheticEventCtor = SyntheticPointerEvent;
- break;
- }
+ try {
+ setCurrentFiber(firstFiber);
- var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
+ error(
+ "Legacy context API has been detected within a strict-mode tree." +
+ "\n\nThe old API will be supported in all 16.x releases, but applications " +
+ "using it should migrate to the new version." +
+ "\n\nPlease update the following components: %s" +
+ "\n\nLearn more about this warning here: https://reactjs.org/link/legacy-context",
+ sortedNames
+ );
+ } finally {
+ resetCurrentFiber();
+ }
+ });
+ };
- if (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) {
- var listeners = accumulateEventHandleNonManagedNodeListeners(
- // TODO: this cast may not make sense for events like
- // "focus" where React listens to e.g. "focusin".
- reactEventType,
- targetContainer,
- inCapturePhase
- );
+ ReactStrictModeWarnings.discardPendingWarnings = function () {
+ pendingComponentWillMountWarnings = [];
+ pendingUNSAFE_ComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUNSAFE_ComponentWillUpdateWarnings = [];
+ pendingLegacyContextWarning = new Map();
+ };
+}
- if (listeners.length > 0) {
- // Intentionally create event lazily.
- var event = new SyntheticEventCtor(
- reactName,
- reactEventType,
- null,
- nativeEvent,
- nativeEventTarget
- );
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- }
- } else {
- // Some events don't bubble in the browser.
- // In the past, React has always bubbled them, but this can be surprising.
- // We're going to try aligning closer to the browser behavior by not bubbling
- // them in React either. We'll start by not bubbling onScroll, and then expand.
- var accumulateTargetOnly =
- !inCapturePhase && // TODO: ideally, we'd eventually add all events from
- // nonDelegatedEvents list in DOMPluginEventSystem.
- // Then we can remove this special list.
- // This is a breaking change that can wait until React 18.
- domEventName === "scroll";
+var ReactCurrentActQueue$2 = ReactSharedInternals.ReactCurrentActQueue; // An error that is thrown (e.g. by `use`) to trigger Suspense. If we
+// detect this is caught by userspace, we'll log a warning in development.
- var _listeners = accumulateSinglePhaseListeners(
- targetInst,
- reactName,
- nativeEvent.type,
- inCapturePhase,
- accumulateTargetOnly,
- nativeEvent
- );
+var SuspenseException = new Error(
+ "Suspense Exception: This is not a real error! It's an implementation " +
+ "detail of `use` to interrupt the current render. You must either " +
+ "rethrow it immediately, or move the `use` call outside of the " +
+ "`try/catch` block. Capturing without rethrowing will lead to " +
+ "unexpected behavior.\n\n" +
+ "To handle async errors, wrap your component in an error boundary, or " +
+ "call the promise's `.catch` method and pass the result to `use`"
+);
+function createThenableState() {
+ // The ThenableState is created the first time a component suspends. If it
+ // suspends again, we'll reuse the same state.
+ return [];
+}
+function isThenableResolved(thenable) {
+ var status = thenable.status;
+ return status === "fulfilled" || status === "rejected";
+}
- if (_listeners.length > 0) {
- // Intentionally create event lazily.
- var _event = new SyntheticEventCtor(
- reactName,
- reactEventType,
- null,
- nativeEvent,
- nativeEventTarget
- );
+function noop$1() {}
- dispatchQueue.push({
- event: _event,
- listeners: _listeners
- });
- }
+function trackUsedThenable(thenableState, thenable, index) {
+ if (ReactCurrentActQueue$2.current !== null) {
+ ReactCurrentActQueue$2.didUsePromise = true;
}
-}
-registerSimpleEvents();
-registerEvents$1();
-registerEvents$2();
-registerEvents();
-registerEvents$3();
+ var previous = thenableState[index];
-function extractEvents(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
-) {
- // TODO: we should remove the concept of a "SimpleEventPlugin".
- // This is the basic functionality of the event system. All
- // the other plugins are essentially polyfills. So the plugin
- // should probably be inlined somewhere and have its logic
- // be core the to event system. This would potentially allow
- // us to ship builds of React without the polyfilled plugins below.
- extractEvents$1(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
- );
- var shouldProcessPolyfillPlugins =
- (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the
- // event's native "bubble" phase, which means that we're
- // not in the capture phase. That's because we emulate
- // the capture phase here still. This is a trade-off,
- // because in an ideal world we would not emulate and use
- // the phases properly, like we do with the SimpleEvent
- // plugin. However, the plugins below either expect
- // emulation (EnterLeave) or use state localized to that
- // plugin (BeforeInput, Change, Select). The state in
- // these modules complicates things, as you'll essentially
- // get the case where the capture phase event might change
- // state, only for the following bubble event to come in
- // later and not trigger anything as the state now
- // invalidates the heuristics of the event plugin. We
- // could alter all these plugins to work in such ways, but
- // that might cause other unknown side-effects that we
- // can't foresee right now.
-
- if (shouldProcessPolyfillPlugins) {
- extractEvents$3(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$4(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$2(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- extractEvents$5(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- }
-} // List of events that need to be individually attached to media elements.
-
-var mediaEventTypes = [
- "abort",
- "canplay",
- "canplaythrough",
- "durationchange",
- "emptied",
- "encrypted",
- "ended",
- "error",
- "loadeddata",
- "loadedmetadata",
- "loadstart",
- "pause",
- "play",
- "playing",
- "progress",
- "ratechange",
- "resize",
- "seeked",
- "seeking",
- "stalled",
- "suspend",
- "timeupdate",
- "volumechange",
- "waiting"
-]; // We should not delegate these events to the container, but rather
-// set them on the actual target element itself. This is primarily
-// because these events do not consistently bubble in the DOM.
+ if (previous === undefined) {
+ thenableState.push(thenable);
+ } else {
+ if (previous !== thenable) {
+ // Reuse the previous thenable, and drop the new one. We can assume
+ // they represent the same value, because components are idempotent.
+ // Avoid an unhandled rejection errors for the Promises that we'll
+ // intentionally ignore.
+ thenable.then(noop$1, noop$1);
+ thenable = previous;
+ }
+ } // We use an expando to track the status and result of a thenable so that we
+ // can synchronously unwrap the value. Think of this as an extension of the
+ // Promise API, or a custom interface that is a superset of Thenable.
+ //
+ // If the thenable doesn't have a status, set it to "pending" and attach
+ // a listener that will update its status and result when it resolves.
-var nonDelegatedEvents = new Set(
- ["cancel", "close", "invalid", "load", "scroll", "toggle"].concat(
- mediaEventTypes
- )
-);
+ switch (thenable.status) {
+ case "fulfilled": {
+ var fulfilledValue = thenable.value;
+ return fulfilledValue;
+ }
-function executeDispatch(event, listener, currentTarget) {
- var type = event.type || "unknown-event";
- event.currentTarget = currentTarget;
- invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
- event.currentTarget = null;
-}
+ case "rejected": {
+ var rejectedError = thenable.reason;
+ throw rejectedError;
+ }
-function processDispatchQueueItemsInOrder(
- event,
- dispatchListeners,
- inCapturePhase
-) {
- var previousInstance;
+ default: {
+ if (typeof thenable.status === "string") {
+ // Only instrument the thenable if the status if not defined. If
+ // it's defined, but an unknown value, assume it's been instrumented by
+ // some custom userspace implementation. We treat it as "pending".
+ // Attach a dummy listener, to ensure that any lazy initialization can
+ // happen. Flight lazily parses JSON when the value is actually awaited.
+ thenable.then(noop$1, noop$1);
+ } else {
+ var pendingThenable = thenable;
+ pendingThenable.status = "pending";
+ pendingThenable.then(
+ function (fulfilledValue) {
+ if (thenable.status === "pending") {
+ var fulfilledThenable = thenable;
+ fulfilledThenable.status = "fulfilled";
+ fulfilledThenable.value = fulfilledValue;
+ }
+ },
+ function (error) {
+ if (thenable.status === "pending") {
+ var rejectedThenable = thenable;
+ rejectedThenable.status = "rejected";
+ rejectedThenable.reason = error;
+ }
+ }
+ );
+ } // Check one more time in case the thenable resolved synchronously.
- if (inCapturePhase) {
- for (var i = dispatchListeners.length - 1; i >= 0; i--) {
- var _dispatchListeners$i = dispatchListeners[i],
- instance = _dispatchListeners$i.instance,
- currentTarget = _dispatchListeners$i.currentTarget,
- listener = _dispatchListeners$i.listener;
+ switch (thenable.status) {
+ case "fulfilled": {
+ var fulfilledThenable = thenable;
+ return fulfilledThenable.value;
+ }
- if (instance !== previousInstance && event.isPropagationStopped()) {
- return;
- }
+ case "rejected": {
+ var rejectedThenable = thenable;
+ throw rejectedThenable.reason;
+ }
+ } // Suspend.
+ //
+ // Throwing here is an implementation detail that allows us to unwind the
+ // call stack. But we shouldn't allow it to leak into userspace. Throw an
+ // opaque placeholder value instead of the actual thenable. If it doesn't
+ // get captured by the work loop, log a warning, because that means
+ // something in userspace must have caught it.
- executeDispatch(event, listener, currentTarget);
- previousInstance = instance;
- }
- } else {
- for (var _i = 0; _i < dispatchListeners.length; _i++) {
- var _dispatchListeners$_i = dispatchListeners[_i],
- _instance = _dispatchListeners$_i.instance,
- _currentTarget = _dispatchListeners$_i.currentTarget,
- _listener = _dispatchListeners$_i.listener;
+ suspendedThenable = thenable;
- if (_instance !== previousInstance && event.isPropagationStopped()) {
- return;
+ {
+ needsToResetSuspendedThenableDEV = true;
}
- executeDispatch(event, _listener, _currentTarget);
- previousInstance = _instance;
+ throw SuspenseException;
}
}
-}
-
-function processDispatchQueue(dispatchQueue, eventSystemFlags) {
- var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
-
- for (var i = 0; i < dispatchQueue.length; i++) {
- var _dispatchQueue$i = dispatchQueue[i],
- event = _dispatchQueue$i.event,
- listeners = _dispatchQueue$i.listeners;
- processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); // event system doesn't use pooling.
- } // This would be a good time to rethrow if any of the event handlers threw.
+} // This is used to track the actual thenable that suspended so it can be
+// passed to the rest of the Suspense implementation — which, for historical
+// reasons, expects to receive a thenable.
- rethrowCaughtError();
-}
+var suspendedThenable = null;
+var needsToResetSuspendedThenableDEV = false;
+function getSuspendedThenable() {
+ // This is called right after `use` suspends by throwing an exception. `use`
+ // throws an opaque value instead of the thenable itself so that it can't be
+ // caught in userspace. Then the work loop accesses the actual thenable using
+ // this function.
+ if (suspendedThenable === null) {
+ throw new Error(
+ "Expected a suspended thenable. This is a bug in React. Please file " +
+ "an issue."
+ );
+ }
-function dispatchEventsForPlugins(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- targetInst,
- targetContainer
-) {
- var nativeEventTarget = getEventTarget(nativeEvent);
- var dispatchQueue = [];
- extractEvents(
- dispatchQueue,
- domEventName,
- targetInst,
- nativeEvent,
- nativeEventTarget,
- eventSystemFlags,
- targetContainer
- );
- processDispatchQueue(dispatchQueue, eventSystemFlags);
-}
+ var thenable = suspendedThenable;
+ suspendedThenable = null;
-function listenToNonDelegatedEvent(domEventName, targetElement) {
{
- if (!nonDelegatedEvents.has(domEventName)) {
- error(
- 'Did not expect a listenToNonDelegatedEvent() call for "%s". ' +
- "This is a bug in React. Please file an issue.",
- domEventName
- );
- }
+ needsToResetSuspendedThenableDEV = false;
}
- var isCapturePhaseListener = false;
- var listenerSet = getEventListenerSet(targetElement);
- var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
-
- if (!listenerSet.has(listenerSetKey)) {
- addTrappedEventListener(
- targetElement,
- domEventName,
- IS_NON_DELEGATED,
- isCapturePhaseListener
- );
- listenerSet.add(listenerSetKey);
- }
+ return thenable;
}
-function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
+function checkIfUseWrappedInTryCatch() {
{
- if (nonDelegatedEvents.has(domEventName) && !isCapturePhaseListener) {
- error(
- 'Did not expect a listenToNativeEvent() call for "%s" in the bubble phase. ' +
- "This is a bug in React. Please file an issue.",
- domEventName
- );
+ // This was set right before SuspenseException was thrown, and it should
+ // have been cleared when the exception was handled. If it wasn't,
+ // it must have been caught by userspace.
+ if (needsToResetSuspendedThenableDEV) {
+ needsToResetSuspendedThenableDEV = false;
+ return true;
}
}
- var eventSystemFlags = 0;
+ return false;
+}
- if (isCapturePhaseListener) {
- eventSystemFlags |= IS_CAPTURE_PHASE;
- }
+var thenableState$1 = null;
+var thenableIndexCounter$1 = 0;
+var didWarnAboutMaps;
+var didWarnAboutGenerators;
+var didWarnAboutStringRefs;
+var ownerHasKeyUseWarning;
+var ownerHasFunctionTypeWarning;
- addTrappedEventListener(
- target,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener
- );
-} // This is only used by createEventHandle when the
-// target is not a DOM element. E.g. window.
+var warnForMissingKey = function (child, returnFiber) {};
-function listenToNativeEventForNonManagedEventTarget(
- domEventName,
- isCapturePhaseListener,
- target
-) {
- var eventSystemFlags = IS_EVENT_HANDLE_NON_MANAGED_NODE;
- var listenerSet = getEventListenerSet(target);
- var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
+{
+ didWarnAboutMaps = false;
+ didWarnAboutGenerators = false;
+ didWarnAboutStringRefs = {};
+ /**
+ * Warn if there's no key explicitly set on dynamic arrays of children or
+ * object keys are not valid. This allows us to keep track of children between
+ * updates.
+ */
- if (!listenerSet.has(listenerSetKey)) {
- if (isCapturePhaseListener) {
- eventSystemFlags |= IS_CAPTURE_PHASE;
+ ownerHasKeyUseWarning = {};
+ ownerHasFunctionTypeWarning = {};
+
+ warnForMissingKey = function (child, returnFiber) {
+ if (child === null || typeof child !== "object") {
+ return;
}
- addTrappedEventListener(
- target,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener
- );
- listenerSet.add(listenerSetKey);
- }
-}
-var listeningMarker = "_reactListening" + Math.random().toString(36).slice(2);
-function listenToAllSupportedEvents(rootContainerElement) {
- if (!rootContainerElement[listeningMarker]) {
- rootContainerElement[listeningMarker] = true;
- allNativeEvents.forEach(function (domEventName) {
- // We handle selectionchange separately because it
- // doesn't bubble and needs to be on the document.
- if (domEventName !== "selectionchange") {
- if (!nonDelegatedEvents.has(domEventName)) {
- listenToNativeEvent(domEventName, false, rootContainerElement);
- }
+ if (!child._store || child._store.validated || child.key != null) {
+ return;
+ }
- listenToNativeEvent(domEventName, true, rootContainerElement);
- }
- });
- var ownerDocument =
- rootContainerElement.nodeType === DOCUMENT_NODE
- ? rootContainerElement
- : rootContainerElement.ownerDocument;
+ if (typeof child._store !== "object") {
+ throw new Error(
+ "React Component in warnForMissingKey should have a _store. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ } // $FlowFixMe unable to narrow type from mixed to writable object
- if (ownerDocument !== null) {
- // The selectionchange event also needs deduplication
- // but it is attached to the document.
- if (!ownerDocument[listeningMarker]) {
- ownerDocument[listeningMarker] = true;
- listenToNativeEvent("selectionchange", false, ownerDocument);
- }
+ child._store.validated = true;
+ var componentName = getComponentNameFromFiber(returnFiber) || "Component";
+
+ if (ownerHasKeyUseWarning[componentName]) {
+ return;
}
- }
+
+ ownerHasKeyUseWarning[componentName] = true;
+
+ error(
+ "Each child in a list should have a unique " +
+ '"key" prop. See https://reactjs.org/link/warning-keys for ' +
+ "more information."
+ );
+ };
}
-function addTrappedEventListener(
- targetContainer,
- domEventName,
- eventSystemFlags,
- isCapturePhaseListener,
- isDeferredListenerForLegacyFBSupport
-) {
- var listener = createEventListenerWrapperWithPriority(
- targetContainer,
- domEventName,
- eventSystemFlags
- ); // If passive option is not supported, then the event will be
- // active and not passive.
+function isReactClass(type) {
+ return type.prototype && type.prototype.isReactComponent;
+}
- var isPassiveListener = undefined;
+function unwrapThenable(thenable) {
+ var index = thenableIndexCounter$1;
+ thenableIndexCounter$1 += 1;
- if (passiveBrowserEventsSupported) {
- // Browsers introduced an intervention, making these events
- // passive by default on document. React doesn't bind them
- // to document anymore, but changing this now would undo
- // the performance wins from the change. So we emulate
- // the existing behavior manually on the roots now.
- // https://github.com/facebook/react/issues/19651
- if (
- domEventName === "touchstart" ||
- domEventName === "touchmove" ||
- domEventName === "wheel"
- ) {
- isPassiveListener = true;
- }
+ if (thenableState$1 === null) {
+ thenableState$1 = createThenableState();
}
- targetContainer =
- enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport
- ? targetContainer.ownerDocument
- : targetContainer;
- var unsubscribeListener; // When legacyFBSupport is enabled, it's for when we
- // want to add a one time event listener to a container.
- // This should only be used with enableLegacyFBSupport
- // due to requirement to provide compatibility with
- // internal FB www event tooling. This works by removing
- // the event listener as soon as it is invoked. We could
- // also attempt to use the {once: true} param on
- // addEventListener, but that requires support and some
- // browsers do not support this today, and given this is
- // to support legacy code patterns, it's likely they'll
- // need support for such browsers.
-
- if (enableLegacyFBSupport && isDeferredListenerForLegacyFBSupport) {
- var originalListener = listener; // $FlowFixMe[missing-this-annot]
- // $FlowFixMe[definition-cycle]
+ return trackUsedThenable(thenableState$1, thenable, index);
+}
- listener = function () {
- removeEventListener(
- targetContainer,
- domEventName,
- unsubscribeListener,
- isCapturePhaseListener
- );
+function coerceRef(returnFiber, current, element) {
+ var mixedRef = element.ref;
- for (
- var _len = arguments.length, p = new Array(_len), _key = 0;
- _key < _len;
- _key++
+ if (
+ mixedRef !== null &&
+ typeof mixedRef !== "function" &&
+ typeof mixedRef !== "object"
+ ) {
+ {
+ if (
+ // We warn in ReactElement.js if owner and self are equal for string refs
+ // because these cannot be automatically converted to an arrow function
+ // using a codemod. Therefore, we don't have to warn about string refs again.
+ !(
+ element._owner &&
+ element._self &&
+ element._owner.stateNode !== element._self
+ ) && // Will already throw with "Function components cannot have string refs"
+ !(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs"
+ !(typeof element.type === "function" && !isReactClass(element.type)) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
+ element._owner
) {
- p[_key] = arguments[_key];
- }
+ var componentName =
+ getComponentNameFromFiber(returnFiber) || "Component";
- return originalListener.apply(this, p);
- };
- } // TODO: There are too many combinations here. Consolidate them.
+ if (!didWarnAboutStringRefs[componentName]) {
+ error(
+ 'Component "%s" contains the string ref "%s". Support for string refs ' +
+ "will be removed in a future major release. We recommend using " +
+ "useRef() or createRef() instead. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-string-ref",
+ componentName,
+ mixedRef
+ );
- if (isCapturePhaseListener) {
- if (isPassiveListener !== undefined) {
- unsubscribeListener = addEventCaptureListenerWithPassiveFlag(
- targetContainer,
- domEventName,
- listener,
- isPassiveListener
- );
- } else {
- unsubscribeListener = addEventCaptureListener(
- targetContainer,
- domEventName,
- listener
- );
- }
- } else {
- if (isPassiveListener !== undefined) {
- unsubscribeListener = addEventBubbleListenerWithPassiveFlag(
- targetContainer,
- domEventName,
- listener,
- isPassiveListener
- );
- } else {
- unsubscribeListener = addEventBubbleListener(
- targetContainer,
- domEventName,
- listener
- );
+ didWarnAboutStringRefs[componentName] = true;
+ }
+ }
}
- }
-}
-
-function deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer) {
- // We defer all click events with legacy FB support mode on.
- // This means we add a one time event listener to trigger
- // after the FB delegated listeners fire.
- var isDeferredListenerForLegacyFBSupport = true;
- addTrappedEventListener(
- targetContainer,
- domEventName,
- IS_LEGACY_FB_SUPPORT_MODE,
- false,
- isDeferredListenerForLegacyFBSupport
- );
-}
-
-function isMatchingRootContainer(grandContainer, targetContainer) {
- return (
- grandContainer === targetContainer ||
- (grandContainer.nodeType === COMMENT_NODE &&
- grandContainer.parentNode === targetContainer)
- );
-}
-
-function dispatchEventForPluginEventSystem(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- targetInst,
- targetContainer
-) {
- var ancestorInst = targetInst;
-
- if (
- (eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 &&
- (eventSystemFlags & IS_NON_DELEGATED) === 0
- ) {
- var targetContainerNode = targetContainer; // If we are using the legacy FB support flag, we
- // defer the event to the null with a one
- // time event listener so we can defer the event.
- if (
- enableLegacyFBSupport && // If our event flags match the required flags for entering
- // FB legacy mode and we are processing the "click" event,
- // then we can defer the event to the "document", to allow
- // for legacy FB support, where the expected behavior was to
- // match React < 16 behavior of delegated clicks to the doc.
- domEventName === "click" &&
- (eventSystemFlags & SHOULD_NOT_DEFER_CLICK_FOR_FB_SUPPORT_MODE) === 0 &&
- !isReplayingEvent(nativeEvent)
- ) {
- deferClickToDocumentForLegacyFBSupport(domEventName, targetContainer);
- return;
- }
+ if (element._owner) {
+ var owner = element._owner;
+ var inst;
- if (targetInst !== null) {
- // The below logic attempts to work out if we need to change
- // the target fiber to a different ancestor. We had similar logic
- // in the legacy event system, except the big difference between
- // systems is that the modern event system now has an event listener
- // attached to each React Root and React Portal Root. Together,
- // the DOM nodes representing these roots are the "rootContainer".
- // To figure out which ancestor instance we should use, we traverse
- // up the fiber tree from the target instance and attempt to find
- // root boundaries that match that of our current "rootContainer".
- // If we find that "rootContainer", we find the parent fiber
- // sub-tree for that root and make that our ancestor instance.
- var node = targetInst;
+ if (owner) {
+ var ownerFiber = owner;
- mainLoop: while (true) {
- if (node === null) {
- return;
+ if (ownerFiber.tag !== ClassComponent) {
+ throw new Error(
+ "Function components cannot have string refs. " +
+ "We recommend using useRef() instead. " +
+ "Learn more about using refs safely here: " +
+ "https://reactjs.org/link/strict-mode-string-ref"
+ );
}
- var nodeTag = node.tag;
-
- if (nodeTag === HostRoot || nodeTag === HostPortal) {
- var container = node.stateNode.containerInfo;
-
- if (isMatchingRootContainer(container, targetContainerNode)) {
- break;
- }
-
- if (nodeTag === HostPortal) {
- // The target is a portal, but it's not the rootContainer we're looking for.
- // Normally portals handle their own events all the way down to the root.
- // So we should be able to stop now. However, we don't know if this portal
- // was part of *our* root.
- var grandNode = node.return;
-
- while (grandNode !== null) {
- var grandTag = grandNode.tag;
-
- if (grandTag === HostRoot || grandTag === HostPortal) {
- var grandContainer = grandNode.stateNode.containerInfo;
+ inst = ownerFiber.stateNode;
+ }
- if (
- isMatchingRootContainer(grandContainer, targetContainerNode)
- ) {
- // This is the rootContainer we're looking for and we found it as
- // a parent of the Portal. That means we can ignore it because the
- // Portal will bubble through to us.
- return;
- }
- }
+ if (!inst) {
+ throw new Error(
+ "Missing owner for string ref " +
+ mixedRef +
+ ". This error is likely caused by a " +
+ "bug in React. Please file an issue."
+ );
+ } // Assigning this to a const so Flow knows it won't change in the closure
- grandNode = grandNode.return;
- }
- } // Now we need to find it's corresponding host fiber in the other
- // tree. To do this we can use getClosestInstanceFromNode, but we
- // need to validate that the fiber is a host instance, otherwise
- // we need to traverse up through the DOM till we find the correct
- // node that is from the other tree.
+ var resolvedInst = inst;
- while (container !== null) {
- var parentNode = getClosestInstanceFromNode(container);
+ {
+ checkPropStringCoercion(mixedRef, "ref");
+ }
- if (parentNode === null) {
- return;
- }
+ var stringRef = "" + mixedRef; // Check if previous string ref matches new string ref
- var parentTag = parentNode.tag;
+ if (
+ current !== null &&
+ current.ref !== null &&
+ typeof current.ref === "function" &&
+ current.ref._stringRef === stringRef
+ ) {
+ return current.ref;
+ }
- if (
- parentTag === HostComponent ||
- parentTag === HostText ||
- parentTag === HostHoistable ||
- parentTag === HostSingleton
- ) {
- node = ancestorInst = parentNode;
- continue mainLoop;
- }
+ var ref = function (value) {
+ var refs = resolvedInst.refs;
- container = container.parentNode;
- }
+ if (value === null) {
+ delete refs[stringRef];
+ } else {
+ refs[stringRef] = value;
}
+ };
- node = node.return;
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ if (typeof mixedRef !== "string") {
+ throw new Error(
+ "Expected ref to be a function, a string, an object returned by React.createRef(), or null."
+ );
+ }
+
+ if (!element._owner) {
+ throw new Error(
+ "Element ref was specified as a string (" +
+ mixedRef +
+ ") but no owner was set. This could happen for one of" +
+ " the following reasons:\n" +
+ "1. You may be adding a ref to a function component\n" +
+ "2. You may be adding a ref to a component that was not created inside a component's render method\n" +
+ "3. You have multiple copies of React loaded\n" +
+ "See https://reactjs.org/link/refs-must-have-owner for more information."
+ );
}
}
}
- batchedUpdates$1(function () {
- return dispatchEventsForPlugins(
- domEventName,
- eventSystemFlags,
- nativeEvent,
- ancestorInst,
- targetContainer
- );
- });
+ return mixedRef;
}
-function createDispatchListener(instance, listener, currentTarget) {
- return {
- instance: instance,
- listener: listener,
- currentTarget: currentTarget
- };
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ // $FlowFixMe[method-unbinding]
+ var childString = Object.prototype.toString.call(newChild);
+ throw new Error(
+ "Objects are not valid as a React child (found: " +
+ (childString === "[object Object]"
+ ? "object with keys {" + Object.keys(newChild).join(", ") + "}"
+ : childString) +
+ "). " +
+ "If you meant to render a collection of children, use an array " +
+ "instead."
+ );
}
-function accumulateSinglePhaseListeners(
- targetFiber,
- reactName,
- nativeEventType,
- inCapturePhase,
- accumulateTargetOnly,
- nativeEvent
-) {
- var captureName = reactName !== null ? reactName + "Capture" : null;
- var reactEventName = inCapturePhase ? captureName : reactName;
- var listeners = [];
- var instance = targetFiber;
- var lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path.
-
- while (instance !== null) {
- var _instance2 = instance,
- stateNode = _instance2.stateNode,
- tag = _instance2.tag; // Handle listeners that are on HostComponents (i.e.
)
+function warnOnFunctionType(returnFiber) {
+ {
+ var componentName = getComponentNameFromFiber(returnFiber) || "Component";
- if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
- ) {
- lastHostComponent = stateNode; // createEventHandle listeners
+ if (ownerHasFunctionTypeWarning[componentName]) {
+ return;
+ }
- {
- var eventHandlerListeners = getEventHandlerListeners(lastHostComponent);
+ ownerHasFunctionTypeWarning[componentName] = true;
- if (eventHandlerListeners !== null) {
- eventHandlerListeners.forEach(function (entry) {
- if (
- entry.type === nativeEventType &&
- entry.capture === inCapturePhase
- ) {
- listeners.push(
- createDispatchListener(
- instance,
- entry.callback,
- lastHostComponent
- )
- );
- }
- });
- }
- } // Standard React on* listeners, i.e. onClick or onClickCapture
+ error(
+ "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."
+ );
+ }
+}
- if (reactEventName !== null) {
- var listener = getListener(instance, reactEventName);
+function resolveLazy(lazyType) {
+ var payload = lazyType._payload;
+ var init = lazyType._init;
+ return init(payload);
+} // This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
- if (listener != null) {
- listeners.push(
- createDispatchListener(instance, listener, lastHostComponent)
- );
- }
- }
- } else if (
- tag === ScopeComponent &&
- lastHostComponent !== null &&
- stateNode !== null
- ) {
- // Scopes
- var reactScopeInstance = stateNode;
+function createChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
+ }
- var _eventHandlerListeners = getEventHandlerListeners(reactScopeInstance);
+ var deletions = returnFiber.deletions;
- if (_eventHandlerListeners !== null) {
- _eventHandlerListeners.forEach(function (entry) {
- if (
- entry.type === nativeEventType &&
- entry.capture === inCapturePhase
- ) {
- listeners.push(
- createDispatchListener(
- instance,
- entry.callback,
- lastHostComponent
- )
- );
- }
- });
- }
- } // If we are only accumulating events for the target, then we don't
- // continue to propagate through the React fiber tree to find other
- // listeners.
+ if (deletions === null) {
+ returnFiber.deletions = [childToDelete];
+ returnFiber.flags |= ChildDeletion;
+ } else {
+ deletions.push(childToDelete);
+ }
+ }
- if (accumulateTargetOnly) {
- break;
- } // If we are processing the onBeforeBlur event, then we need to take
- // into consideration that part of the React tree might have been hidden
- // or deleted (as we're invoking this event during commit). We can find
- // this out by checking if intercept fiber set on the event matches the
- // current instance fiber. In which case, we should clear all existing
- // listeners.
+ 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.
- if (nativeEvent.type === "beforeblur") {
- // $FlowFixMe[prop-missing] internal field
- var detachedInterceptFiber = nativeEvent._detachedInterceptFiber;
+ var childToDelete = currentFirstChild;
- if (
- detachedInterceptFiber !== null &&
- (detachedInterceptFiber === instance ||
- detachedInterceptFiber === instance.alternate)
- ) {
- listeners = [];
- }
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
}
- instance = instance.return;
+ return null;
}
- return listeners;
-} // We should only use this function for:
-// - BeforeInputEventPlugin
-// - ChangeEventPlugin
-// - SelectEventPlugin
-// This is because we only process these plugins
-// in the bubble phase, so we need to accumulate two
-// phase event listeners (via emulation).
-
-function accumulateTwoPhaseListeners(targetFiber, reactName) {
- var captureName = reactName + "Capture";
- var listeners = [];
- var instance = targetFiber; // Accumulate all instances and listeners via the target -> root path.
-
- while (instance !== null) {
- var _instance3 = instance,
- stateNode = _instance3.stateNode,
- tag = _instance3.tag; // Handle listeners that are on HostComponents (i.e.
)
-
- if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
- ) {
- var currentTarget = stateNode;
- var captureListener = getListener(instance, captureName);
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ // instead.
+ var existingChildren = new Map();
+ var existingChild = currentFirstChild;
- if (captureListener != null) {
- listeners.unshift(
- createDispatchListener(instance, captureListener, currentTarget)
- );
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
}
- var bubbleListener = getListener(instance, reactName);
-
- if (bubbleListener != null) {
- listeners.push(
- createDispatchListener(instance, bubbleListener, currentTarget)
- );
- }
+ existingChild = existingChild.sibling;
}
- instance = instance.return;
+ return existingChildren;
}
- return listeners;
-}
-
-function getParent(inst) {
- if (inst === null) {
- return null;
+ function useFiber(fiber, pendingProps) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
}
- do {
- // $FlowFixMe[incompatible-use] found when upgrading Flow
- inst = inst.return; // TODO: If this is a HostRoot we might want to bail out.
- // That is depending on if we want nested subtrees (layers) to bubble
- // events to their parent. We could also go through parentNode on the
- // host node but that wouldn't work for React Native and doesn't let us
- // do the portal feature.
- } while (inst && inst.tag !== HostComponent && inst.tag !== HostSingleton);
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
- if (inst) {
- return inst;
- }
+ if (!shouldTrackSideEffects) {
+ // During hydration, the useId algorithm needs to know which fibers are
+ // part of a list of children (arrays, iterators).
+ newFiber.flags |= Forked;
+ return lastPlacedIndex;
+ }
- return null;
-}
-/**
- * Return the lowest common ancestor of A and B, or null if they are in
- * different trees.
- */
+ var current = newFiber.alternate;
-function getLowestCommonAncestor(instA, instB) {
- var nodeA = instA;
- var nodeB = instB;
- var depthA = 0;
+ if (current !== null) {
+ var oldIndex = current.index;
- for (var tempA = nodeA; tempA; tempA = getParent(tempA)) {
- depthA++;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.flags |= Placement | PlacementDEV;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
+ }
+ } else {
+ // This is an insertion.
+ newFiber.flags |= Placement | PlacementDEV;
+ return lastPlacedIndex;
+ }
}
- var depthB = 0;
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.flags |= Placement | PlacementDEV;
+ }
- for (var tempB = nodeB; tempB; tempB = getParent(tempB)) {
- depthB++;
- } // If A is deeper, crawl up.
-
- while (depthA - depthB > 0) {
- nodeA = getParent(nodeA);
- depthA--;
- } // If B is deeper, crawl up.
+ return newFiber;
+ }
- while (depthB - depthA > 0) {
- nodeB = getParent(nodeB);
- depthB--;
- } // Walk in lockstep until we find a match.
+ function updateTextNode(returnFiber, current, textContent, lanes) {
+ if (current === null || current.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(textContent, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, textContent);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
- var depth = depthA;
+ function updateElement(returnFiber, current, element, lanes) {
+ var elementType = element.type;
- while (depth--) {
- if (nodeA === nodeB || (nodeB !== null && nodeA === nodeB.alternate)) {
- return nodeA;
+ if (elementType === REACT_FRAGMENT_TYPE) {
+ return updateFragment(
+ returnFiber,
+ current,
+ element.props.children,
+ lanes,
+ element.key
+ );
}
- nodeA = getParent(nodeA);
- nodeB = getParent(nodeB);
- }
+ if (current !== null) {
+ if (
+ current.elementType === elementType || // Keep this check inline so it only runs on the false path:
+ isCompatibleFamilyForHotReloading(current, element) || // Lazy types should reconcile their resolved type.
+ // We need to do this after the Hot Reloading check above,
+ // because hot reloading has different semantics than prod because
+ // it doesn't resuspend. So we can't let the call below suspend.
+ (typeof elementType === "object" &&
+ elementType !== null &&
+ elementType.$$typeof === REACT_LAZY_TYPE &&
+ resolveLazy(elementType) === current.type)
+ ) {
+ // Move based on index
+ var existing = useFiber(current, element.props);
+ existing.ref = coerceRef(returnFiber, current, element);
+ existing.return = returnFiber;
- return null;
-}
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
-function accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- event,
- target,
- common,
- inCapturePhase
-) {
- var registrationName = event._reactName;
- var listeners = [];
- var instance = target;
+ return existing;
+ }
+ } // Insert
- while (instance !== null) {
- if (instance === common) {
- break;
- }
+ var created = createFiberFromElement(element, returnFiber.mode, lanes);
+ created.ref = coerceRef(returnFiber, current, element);
+ created.return = returnFiber;
+ return created;
+ }
- var _instance4 = instance,
- alternate = _instance4.alternate,
- stateNode = _instance4.stateNode,
- tag = _instance4.tag;
+ function updatePortal(returnFiber, current, portal, lanes) {
+ if (
+ current === null ||
+ current.tag !== HostPortal ||
+ current.stateNode.containerInfo !== portal.containerInfo ||
+ current.stateNode.implementation !== portal.implementation
+ ) {
+ // Insert
+ var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, portal.children || []);
+ existing.return = returnFiber;
+ return existing;
+ }
+ }
- if (alternate !== null && alternate === common) {
- break;
+ function updateFragment(returnFiber, current, fragment, lanes, key) {
+ if (current === null || current.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(
+ fragment,
+ returnFiber.mode,
+ lanes,
+ key
+ );
+ created.return = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, fragment);
+ existing.return = returnFiber;
+ return existing;
}
+ }
+ function createChild(returnFiber, newChild, lanes) {
if (
- (tag === HostComponent ||
- tag === HostHoistable ||
- tag === HostSingleton) &&
- stateNode !== null
+ (typeof newChild === "string" && newChild !== "") ||
+ typeof newChild === "number"
) {
- var currentTarget = stateNode;
-
- if (inCapturePhase) {
- var captureListener = getListener(instance, registrationName);
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText("" + newChild, returnFiber.mode, lanes);
+ created.return = returnFiber;
+ return created;
+ }
- if (captureListener != null) {
- listeners.unshift(
- createDispatchListener(instance, captureListener, currentTarget)
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ var _created = createFiberFromElement(
+ newChild,
+ returnFiber.mode,
+ lanes
);
+
+ _created.ref = coerceRef(returnFiber, null, newChild);
+ _created.return = returnFiber;
+ return _created;
}
- } else if (!inCapturePhase) {
- var bubbleListener = getListener(instance, registrationName);
- if (bubbleListener != null) {
- listeners.push(
- createDispatchListener(instance, bubbleListener, currentTarget)
+ case REACT_PORTAL_TYPE: {
+ var _created2 = createFiberFromPortal(
+ newChild,
+ returnFiber.mode,
+ lanes
);
- }
- }
- }
- instance = instance.return;
- }
+ _created2.return = returnFiber;
+ return _created2;
+ }
- if (listeners.length !== 0) {
- dispatchQueue.push({
- event: event,
- listeners: listeners
- });
- }
-} // We should only use this function for:
-// - EnterLeaveEventPlugin
-// This is because we only process this plugin
-// in the bubble phase, so we need to accumulate two
-// phase event listeners.
+ case REACT_LAZY_TYPE: {
+ var payload = newChild._payload;
+ var init = newChild._init;
+ return createChild(returnFiber, init(payload), lanes);
+ }
+ }
-function accumulateEnterLeaveTwoPhaseListeners(
- dispatchQueue,
- leaveEvent,
- enterEvent,
- from,
- to
-) {
- var common = from && to ? getLowestCommonAncestor(from, to) : null;
+ if (isArray(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(
+ newChild,
+ returnFiber.mode,
+ lanes,
+ null
+ );
- if (from !== null) {
- accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- leaveEvent,
- from,
- common,
- false
- );
- }
+ _created3.return = returnFiber;
+ return _created3;
+ } // Usable node types
+ //
+ // Unwrap the inner value and recursively call this function again.
- if (to !== null && enterEvent !== null) {
- accumulateEnterLeaveListenersForEvent(
- dispatchQueue,
- enterEvent,
- to,
- common,
- true
- );
- }
-}
-function accumulateEventHandleNonManagedNodeListeners(
- reactEventType,
- currentTarget,
- inCapturePhase
-) {
- var listeners = [];
- var eventListeners = getEventHandlerListeners(currentTarget);
+ if (typeof newChild.then === "function") {
+ var thenable = newChild;
+ return createChild(returnFiber, unwrapThenable(thenable), lanes);
+ }
- if (eventListeners !== null) {
- eventListeners.forEach(function (entry) {
- if (entry.type === reactEventType && entry.capture === inCapturePhase) {
- listeners.push(
- createDispatchListener(null, entry.callback, currentTarget)
+ if (
+ newChild.$$typeof === REACT_CONTEXT_TYPE ||
+ newChild.$$typeof === REACT_SERVER_CONTEXT_TYPE
+ ) {
+ var context = newChild;
+ return createChild(
+ returnFiber,
+ readContextDuringReconcilation(returnFiber, context, lanes),
+ lanes
);
}
- });
- }
- return listeners;
-}
-function getListenerSetKey(domEventName, capture) {
- return domEventName + "__" + (capture ? "capture" : "bubble");
-}
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
-var didWarnInvalidHydration = false;
-var didWarnScriptTags = false;
-var DANGEROUSLY_SET_INNER_HTML = "dangerouslySetInnerHTML";
-var SUPPRESS_CONTENT_EDITABLE_WARNING = "suppressContentEditableWarning";
-var SUPPRESS_HYDRATION_WARNING$1 = "suppressHydrationWarning";
-var AUTOFOCUS = "autoFocus";
-var CHILDREN = "children";
-var STYLE$1 = "style";
-var HTML = "__html";
-var warnedUnknownTags;
-var canDiffStyleForHydrationWarning;
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType(returnFiber);
+ }
+ }
-{
- warnedUnknownTags = {
- // There are working polyfills for