diff --git a/Libraries/Renderer/REVISION b/Libraries/Renderer/REVISION
index 3b41417678220a..7a75297ceafe15 100644
--- a/Libraries/Renderer/REVISION
+++ b/Libraries/Renderer/REVISION
@@ -1 +1 @@
-9491dee79586d21c115cd4d5986c81b7d88d2b3f
\ No newline at end of file
+a7b9f98e7abab7305978b4b36881c8267a450097
\ No newline at end of file
diff --git a/Libraries/Renderer/ReactNativeRenderer-dev.js b/Libraries/Renderer/ReactNativeRenderer-dev.js
index e9dcd9a2985bb7..64ed9dba80bab7 100644
--- a/Libraries/Renderer/ReactNativeRenderer-dev.js
+++ b/Libraries/Renderer/ReactNativeRenderer-dev.js
@@ -22,119 +22,24 @@ var emptyFunction = require("fbjs/lib/emptyFunction");
var RCTEventEmitter = require("RCTEventEmitter");
var UIManager = require("UIManager");
var React = require("react");
-var ExceptionsManager = require("ExceptionsManager");
var TextInputState = require("TextInputState");
var deepDiffer = require("deepDiffer");
var flattenStyle = require("flattenStyle");
var emptyObject = require("fbjs/lib/emptyObject");
var checkPropTypes = require("prop-types/checkPropTypes");
var shallowEqual = require("fbjs/lib/shallowEqual");
+var ExceptionsManager = require("ExceptionsManager");
var deepFreezeAndThrowOnMutationInDev = require("deepFreezeAndThrowOnMutationInDev");
-var ReactErrorUtils = {
- // Used by Fiber to simulate a try-catch.
- _caughtError: null,
- _hasCaughtError: false,
-
- // Used by event system to capture/rethrow the first error.
- _rethrowError: null,
- _hasRethrowError: false,
-
- injection: {
- injectErrorUtils: function(injectedErrorUtils) {
- invariant(
- typeof injectedErrorUtils.invokeGuardedCallback === "function",
- "Injected invokeGuardedCallback() must be a function."
- );
- invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback;
- }
- },
-
- /**
- * Call a function while guarding against errors that happens within it.
- * Returns an error if it throws, otherwise null.
- *
- * In production, this is implemented using a try-catch. The reason we don't
- * use a try-catch directly is so that we can swap out a different
- * implementation in DEV mode.
- *
- * @param {String} name of the guard to use for logging or debugging
- * @param {Function} func The function to invoke
- * @param {*} context The context to use when calling the function
- * @param {...*} args Arguments for function
- */
- invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) {
- invokeGuardedCallback.apply(ReactErrorUtils, arguments);
- },
-
- /**
- * Same as invokeGuardedCallback, but instead of returning an error, it stores
- * it in a global so it can be rethrown by `rethrowCaughtError` later.
- * TODO: See if _caughtError and _rethrowError can be unified.
- *
- * @param {String} name of the guard to use for logging or debugging
- * @param {Function} func The function to invoke
- * @param {*} context The context to use when calling the function
- * @param {...*} args Arguments for function
- */
- invokeGuardedCallbackAndCatchFirstError: function(
- name,
- func,
- context,
- a,
- b,
- c,
- d,
- e,
- f
- ) {
- ReactErrorUtils.invokeGuardedCallback.apply(this, arguments);
- if (ReactErrorUtils.hasCaughtError()) {
- var error = ReactErrorUtils.clearCaughtError();
- if (!ReactErrorUtils._hasRethrowError) {
- ReactErrorUtils._hasRethrowError = true;
- ReactErrorUtils._rethrowError = error;
- }
- }
- },
-
- /**
- * During execution of guarded functions we will capture the first error which
- * we will rethrow to be handled by the top level error handler.
- */
- rethrowCaughtError: function() {
- return rethrowCaughtError.apply(ReactErrorUtils, arguments);
- },
-
- hasCaughtError: function() {
- return ReactErrorUtils._hasCaughtError;
- },
-
- clearCaughtError: function() {
- if (ReactErrorUtils._hasCaughtError) {
- var error = ReactErrorUtils._caughtError;
- ReactErrorUtils._caughtError = null;
- ReactErrorUtils._hasCaughtError = false;
- return error;
- } else {
- invariant(
- false,
- "clearCaughtError was called but no error was captured. This error " +
- "is likely caused by a bug in React. Please file an issue."
- );
- }
- }
-};
-
var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) {
- ReactErrorUtils._hasCaughtError = false;
- ReactErrorUtils._caughtError = null;
+ this._hasCaughtError = false;
+ this._caughtError = null;
var funcArgs = Array.prototype.slice.call(arguments, 3);
try {
func.apply(context, funcArgs);
} catch (error) {
- ReactErrorUtils._caughtError = error;
- ReactErrorUtils._hasCaughtError = true;
+ this._caughtError = error;
+ this._hasCaughtError = true;
}
};
@@ -273,11 +178,11 @@ var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) {
"See https://fb.me/react-crossorigin-error for more information."
);
}
- ReactErrorUtils._hasCaughtError = true;
- ReactErrorUtils._caughtError = error;
+ this._hasCaughtError = true;
+ this._caughtError = error;
} else {
- ReactErrorUtils._hasCaughtError = false;
- ReactErrorUtils._caughtError = null;
+ this._hasCaughtError = false;
+ this._caughtError = null;
}
// Remove our event listeners
@@ -288,6 +193,93 @@ var invokeGuardedCallback = function(name, func, context, a, b, c, d, e, f) {
}
}
+var invokeGuardedCallback$1 = invokeGuardedCallback;
+
+var ReactErrorUtils = {
+ // Used by Fiber to simulate a try-catch.
+ _caughtError: null,
+ _hasCaughtError: false,
+
+ // Used by event system to capture/rethrow the first error.
+ _rethrowError: null,
+ _hasRethrowError: false,
+
+ /**
+ * Call a function while guarding against errors that happens within it.
+ * Returns an error if it throws, otherwise null.
+ *
+ * In production, this is implemented using a try-catch. The reason we don't
+ * use a try-catch directly is so that we can swap out a different
+ * implementation in DEV mode.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+ invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) {
+ invokeGuardedCallback$1.apply(ReactErrorUtils, arguments);
+ },
+
+ /**
+ * Same as invokeGuardedCallback, but instead of returning an error, it stores
+ * it in a global so it can be rethrown by `rethrowCaughtError` later.
+ * TODO: See if _caughtError and _rethrowError can be unified.
+ *
+ * @param {String} name of the guard to use for logging or debugging
+ * @param {Function} func The function to invoke
+ * @param {*} context The context to use when calling the function
+ * @param {...*} args Arguments for function
+ */
+ invokeGuardedCallbackAndCatchFirstError: function(
+ name,
+ func,
+ context,
+ a,
+ b,
+ c,
+ d,
+ e,
+ f
+ ) {
+ ReactErrorUtils.invokeGuardedCallback.apply(this, arguments);
+ if (ReactErrorUtils.hasCaughtError()) {
+ var error = ReactErrorUtils.clearCaughtError();
+ if (!ReactErrorUtils._hasRethrowError) {
+ ReactErrorUtils._hasRethrowError = true;
+ ReactErrorUtils._rethrowError = error;
+ }
+ }
+ },
+
+ /**
+ * During execution of guarded functions we will capture the first error which
+ * we will rethrow to be handled by the top level error handler.
+ */
+ rethrowCaughtError: function() {
+ return rethrowCaughtError.apply(ReactErrorUtils, arguments);
+ },
+
+ hasCaughtError: function() {
+ return ReactErrorUtils._hasCaughtError;
+ },
+
+ clearCaughtError: function() {
+ if (ReactErrorUtils._hasCaughtError) {
+ var error = ReactErrorUtils._caughtError;
+ ReactErrorUtils._caughtError = null;
+ ReactErrorUtils._hasCaughtError = false;
+ return error;
+ } else {
+ invariant(
+ false,
+ "clearCaughtError was called but no error was captured. This error " +
+ "is likely caused by a bug in React. Please file an issue."
+ );
+ }
+ }
+};
+
var rethrowCaughtError = function() {
if (ReactErrorUtils._hasRethrowError) {
var error = ReactErrorUtils._rethrowError;
@@ -297,6 +289,78 @@ var rethrowCaughtError = function() {
}
};
+/**
+ * Forked from fbjs/warning:
+ * https://github.com/facebook/fbjs/blob/e66ba20ad5be433eb54423f2b097d829324d9de6/packages/fbjs/src/__forks__/warning.js
+ *
+ * Only change is we use console.warn instead of console.error,
+ * and do nothing when 'console' is not supported.
+ * This really simplifies the code.
+ * ---
+ * Similar to invariant but only logs a warning if the condition is not met.
+ * This can be used to log issues in development environments in critical
+ * paths. Removing the logging code for production environments will keep the
+ * same logic and follow the same code paths.
+ */
+
+var lowPriorityWarning = function() {};
+
+{
+ var printWarning = function(format) {
+ for (
+ var _len = arguments.length,
+ args = Array(_len > 1 ? _len - 1 : 0),
+ _key = 1;
+ _key < _len;
+ _key++
+ ) {
+ args[_key - 1] = arguments[_key];
+ }
+
+ var argIndex = 0;
+ var message =
+ "Warning: " +
+ format.replace(/%s/g, function() {
+ return args[argIndex++];
+ });
+ if (typeof console !== "undefined") {
+ console.warn(message);
+ }
+ try {
+ // --- Welcome to debugging React ---
+ // This error was thrown as a convenience so that you can use this stack
+ // to find the callsite that caused this warning to fire.
+ throw new Error(message);
+ } catch (x) {}
+ };
+
+ lowPriorityWarning = function(condition, format) {
+ if (format === undefined) {
+ throw new Error(
+ "`warning(condition, format, ...args)` requires a warning " +
+ "message argument"
+ );
+ }
+ if (!condition) {
+ for (
+ var _len2 = arguments.length,
+ args = Array(_len2 > 2 ? _len2 - 2 : 0),
+ _key2 = 2;
+ _key2 < _len2;
+ _key2++
+ ) {
+ args[_key2 - 2] = arguments[_key2];
+ }
+
+ printWarning.apply(undefined, [format].concat(args));
+ }
+ };
+}
+
+var lowPriorityWarning$1 = lowPriorityWarning;
+
+var shouldWarnOnInjection = false;
+
/**
* Injectable ordering of event plugins.
*/
@@ -482,6 +546,21 @@ function injectEventPluginOrder(injectedEventPluginOrder) {
* @see {EventPluginHub.injection.injectEventPluginsByName}
*/
function injectEventPluginsByName(injectedNamesToPlugins) {
+ {
+ if (shouldWarnOnInjection) {
+ var names = Object.keys(injectedNamesToPlugins).join(", ");
+ lowPriorityWarning$1(
+ false,
+ "Injecting custom event plugins (%s) is deprecated " +
+ "and will not work in React 17+. Please update your code " +
+ "to not depend on React internals. The stack trace for this " +
+ "warning should reveal the library that is using them. " +
+ "See https://github.com/facebook/react/issues/11689 for a discussion.",
+ names
+ );
+ }
+ }
+
var isOrderingDirty = false;
for (var pluginName in injectedNamesToPlugins) {
if (!injectedNamesToPlugins.hasOwnProperty(pluginName)) {
@@ -542,7 +621,7 @@ function isStartish(topLevelType) {
return topLevelType === "topMouseDown" || topLevelType === "topTouchStart";
}
-var validateEventDispatches;
+var validateEventDispatches = void 0;
{
validateEventDispatches = function(event) {
var dispatchListeners = event._dispatchListeners;
@@ -851,7 +930,7 @@ var injection = {
* @return {?function} The stored callback.
*/
function getListener(inst, registrationName) {
- var listener;
+ var listener = void 0;
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
// live here; needs to be moved to a better place soon
@@ -891,7 +970,7 @@ function extractEvents(
nativeEvent,
nativeEventTarget
) {
- var events;
+ var events = null;
for (var i = 0; i < plugins.length; i++) {
// Not every plugin in the ordering may be loaded at runtime.
var possiblePlugin = plugins[i];
@@ -910,25 +989,11 @@ function extractEvents(
return events;
}
-/**
- * Enqueues a synthetic event that should be dispatched when
- * `processEventQueue` is invoked.
- *
- * @param {*} events An accumulation of synthetic events.
- * @internal
- */
-function enqueueEvents(events) {
- if (events) {
+function runEventsInBatch(events, simulated) {
+ if (events !== null) {
eventQueue = accumulateInto(eventQueue, events);
}
-}
-/**
- * Dispatches all synthetic events on the event queue.
- *
- * @internal
- */
-function processEventQueue(simulated) {
// Set `eventQueue` to null before processing it so that we can tell if more
// events get enqueued while processing.
var processingEventQueue = eventQueue;
@@ -958,6 +1023,21 @@ function processEventQueue(simulated) {
ReactErrorUtils.rethrowCaughtError();
}
+function runExtractedEventsInBatch(
+ topLevelType,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+) {
+ var events = extractEvents(
+ topLevelType,
+ targetInst,
+ nativeEvent,
+ nativeEventTarget
+ );
+ runEventsInBatch(events, false);
+}
+
var IndeterminateComponent = 0; // Before we know whether it is functional or class
var FunctionalComponent = 1;
var ClassComponent = 2;
@@ -969,6 +1049,9 @@ var CallComponent = 7;
var CallHandlerPhase = 8;
var ReturnComponent = 9;
var Fragment = 10;
+var Mode = 11;
+var ContextConsumer = 12;
+var ContextProvider = 13;
function getParent(inst) {
do {
@@ -1052,7 +1135,7 @@ function traverseTwoPhase(inst, fn, arg) {
path.push(inst);
inst = getParent(inst);
}
- var i;
+ var i = void 0;
for (i = path.length; i-- > 0; ) {
fn(path[i], "captured", arg);
}
@@ -1178,7 +1261,6 @@ function accumulateDirectDispatches(events) {
/* eslint valid-typeof: 0 */
var didWarnForAddedNewProperty = false;
-var isProxySupported = typeof Proxy === "function";
var EVENT_POOL_SIZE = 10;
var shouldBeReleasedProperties = [
@@ -1371,24 +1453,26 @@ SyntheticEvent.Interface = EventInterface;
/**
* Helper to reduce boilerplate when creating subclasses.
- *
- * @param {function} Class
- * @param {?object} Interface
*/
-SyntheticEvent.augmentClass = function(Class, Interface) {
+SyntheticEvent.extend = function(Interface) {
var Super = this;
var E = function() {};
E.prototype = Super.prototype;
var prototype = new E();
+ function Class() {
+ return Super.apply(this, arguments);
+ }
Object.assign(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
Class.Interface = Object.assign({}, Super.Interface, Interface);
- Class.augmentClass = Super.augmentClass;
+ Class.extend = Super.extend;
addEventPoolingTo(Class);
+
+ return Class;
};
/** Proxying after everything set on SyntheticEvent
@@ -1396,6 +1480,11 @@ SyntheticEvent.augmentClass = function(Class, Interface) {
* in which some Event properties are set to undefined (GH#10010)
*/
{
+ var isProxySupported =
+ typeof Proxy === "function" &&
+ // https://github.com/facebook/react/issues/12011
+ !Object.isSealed(new Proxy({}, {}));
+
if (isProxySupported) {
/*eslint-disable no-func-assign */
SyntheticEvent = new Proxy(SyntheticEvent, {
@@ -1522,34 +1611,11 @@ var SyntheticEvent$1 = SyntheticEvent;
* interface will ensure that it is cleaned up when pooled/destroyed. The
* `ResponderEventPlugin` will populate it appropriately.
*/
-var ResponderEventInterface = {
+var ResponderSyntheticEvent = SyntheticEvent$1.extend({
touchHistory: function(nativeEvent) {
return null; // Actually doesn't even look at the native event.
}
-};
-
-/**
- * @param {object} dispatchConfig Configuration used to dispatch this event.
- * @param {string} dispatchMarker Marker identifying the event target.
- * @param {object} nativeEvent Native event.
- * @extends {SyntheticEvent}
- */
-function ResponderSyntheticEvent(
- dispatchConfig,
- dispatchMarker,
- nativeEvent,
- nativeEventTarget
-) {
- return SyntheticEvent$1.call(
- this,
- dispatchConfig,
- dispatchMarker,
- nativeEvent,
- nativeEventTarget
- );
-}
-
-SyntheticEvent$1.augmentClass(ResponderSyntheticEvent, ResponderEventInterface);
+});
/**
* Tracks the position and time of each active touch by `touch.identifier`. We
@@ -2091,7 +2157,7 @@ function setResponderAndExtractTransfer(
if (!wantsResponderInst || wantsResponderInst === responderInst) {
return null;
}
- var extracted;
+ var extracted = void 0;
var grantEvent = ResponderSyntheticEvent.getPooled(
eventTypes.responderGrant,
wantsResponderInst,
@@ -2420,11 +2486,19 @@ function uncacheFiberNode(tag) {
}
function getInstanceFromTag(tag) {
- return instanceCache[tag] || null;
+ if (typeof tag === "number") {
+ return instanceCache[tag] || null;
+ } else {
+ // Fabric will invoke event emitters on a direct fiber reference
+ return tag;
+ }
}
function getTagFromInstance(inst) {
var tag = inst.stateNode._nativeTag;
+ if (tag === undefined) {
+ tag = inst.stateNode.canonical._nativeTag;
+ }
invariant(tag, "All native instances should have a tag.");
return tag;
}
@@ -2533,30 +2607,6 @@ var ReactGenericBatchingInjection = {
var injection$2 = ReactGenericBatchingInjection;
-function runEventQueueInBatch(events) {
- enqueueEvents(events);
- processEventQueue(false);
-}
-
-/**
- * Streams a fired top-level event to `EventPluginHub` where plugins have the
- * opportunity to create `ReactEvent`s to be dispatched.
- */
-function handleTopLevel(
- topLevelType,
- targetInst,
- nativeEvent,
- nativeEventTarget
-) {
- var events = extractEvents(
- topLevelType,
- targetInst,
- nativeEvent,
- nativeEventTarget
- );
- runEventQueueInBatch(events);
-}
-
/**
* Keeps track of allocating and associating native "tags" which are numeric,
* unique view IDs. All the native tags are negative numbers, to avoid
@@ -2668,7 +2718,12 @@ function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
var inst = getInstanceFromTag(rootNodeID);
batchedUpdates(function() {
- handleTopLevel(topLevelType, inst, nativeEvent, nativeEvent.target);
+ runExtractedEventsInBatch(
+ topLevelType,
+ inst,
+ nativeEvent,
+ nativeEvent.target
+ );
});
// React Native doesn't use ReactControlledComponent but if it did, here's
// where it would do it.
@@ -2747,8 +2802,7 @@ var ReactNativeEventEmitter = Object.freeze({
registrationNames: registrationNameModules,
_receiveRootNodeIDEvent: _receiveRootNodeIDEvent,
receiveEvent: receiveEvent,
- receiveTouches: receiveTouches,
- handleTopLevel: handleTopLevel
+ receiveTouches: receiveTouches
});
var ReactNativeEventPluginOrder = [
@@ -2800,90 +2854,6 @@ injection.injectEventPluginsByName({
ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin
});
-var defaultShowDialog = function(capturedError) {
- return true;
-};
-
-var showDialog = defaultShowDialog;
-
-function logCapturedError(capturedError) {
- var logError = showDialog(capturedError);
-
- // Allow injected showDialog() to prevent default console.error logging.
- // This enables renderers like ReactNative to better manage redbox behavior.
- if (logError === false) {
- return;
- }
-
- var error = capturedError.error;
- var suppressLogging = error && error.suppressReactErrorLogging;
- if (suppressLogging) {
- return;
- }
-
- {
- var componentName = capturedError.componentName,
- componentStack = capturedError.componentStack,
- errorBoundaryName = capturedError.errorBoundaryName,
- errorBoundaryFound = capturedError.errorBoundaryFound,
- willRetry = capturedError.willRetry;
-
- var componentNameMessage = componentName
- ? "The above error occurred in the <" + componentName + "> component:"
- : "The above error occurred in one of your React components:";
-
- var errorBoundaryMessage = void 0;
- // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow.
- if (errorBoundaryFound && errorBoundaryName) {
- if (willRetry) {
- errorBoundaryMessage =
- "React will try to recreate this component tree from scratch " +
- ("using the error boundary you provided, " + errorBoundaryName + ".");
- } else {
- errorBoundaryMessage =
- "This error was initially handled by the error boundary " +
- errorBoundaryName +
- ".\n" +
- "Recreating the tree from scratch failed so React will unmount the tree.";
- }
- } else {
- errorBoundaryMessage =
- "Consider adding an error boundary to your tree to customize error handling behavior.\n" +
- "Visit https://fb.me/react-error-boundaries to learn more about error boundaries.";
- }
- var combinedMessage =
- "" +
- componentNameMessage +
- componentStack +
- "\n\n" +
- ("" + errorBoundaryMessage);
-
- // In development, we provide our own message with just the component stack.
- // We don't include the original error message and JS stack because the browser
- // has already printed it. Even if the application swallows the error, it is still
- // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
- console.error(combinedMessage);
- }
-}
-
-var injection$4 = {
- /**
- * Display custom dialog for lifecycle errors.
- * Return false to prevent default behavior of logging to console.error.
- */
- injectDialog: function(fn) {
- invariant(
- showDialog === defaultShowDialog,
- "The custom dialog was already injected."
- );
- invariant(
- typeof fn === "function",
- "Injected showDialog() must be a function."
- );
- showDialog = fn;
- }
-};
-
// The Symbol used to tag the ReactElement-like types. If there is no native Symbol
// nor polyfill, then a plain number is used for performance.
var hasSymbol = typeof Symbol === "function" && Symbol["for"];
@@ -2893,6 +2863,11 @@ var REACT_CALL_TYPE = hasSymbol ? Symbol["for"]("react.call") : 0xeac8;
var REACT_RETURN_TYPE = hasSymbol ? Symbol["for"]("react.return") : 0xeac9;
var REACT_PORTAL_TYPE = hasSymbol ? Symbol["for"]("react.portal") : 0xeaca;
var REACT_FRAGMENT_TYPE = hasSymbol ? Symbol["for"]("react.fragment") : 0xeacb;
+var REACT_STRICT_MODE_TYPE = hasSymbol
+ ? Symbol["for"]("react.strict_mode")
+ : 0xeacc;
+var REACT_PROVIDER_TYPE = hasSymbol ? Symbol["for"]("react.provider") : 0xeacd;
+var REACT_CONTEXT_TYPE = hasSymbol ? Symbol["for"]("react.context") : 0xeace;
var MAYBE_ITERATOR_SYMBOL = typeof Symbol === "function" && Symbol.iterator;
var FAUX_ITERATOR_SYMBOL = "@@iterator";
@@ -2985,7 +2960,7 @@ var TouchHistoryMath = {
touchTrack.touchActive &&
touchTrack.currentTimeStamp >= touchesChangedAfter
) {
- var toAdd; // Yuck, program temporarily in invalid state.
+ var toAdd = void 0; // Yuck, program temporarily in invalid state.
if (ofCurrent && isXAxis) {
toAdd = touchTrack.currentPageX;
} else if (ofCurrent && !isXAxis) {
@@ -3086,47 +3061,6 @@ var ReactGlobalSharedState = Object.freeze({
var ReactVersion = "16.2.0";
-// Module provided by RN:
-/**
- * Intercept lifecycle errors and ensure they are shown with the correct stack
- * trace within the native redbox component.
- */
-function showDialog$1(capturedError) {
- var componentStack = capturedError.componentStack,
- error = capturedError.error;
-
- var errorToHandle = void 0;
-
- // Typically Errors are thrown but eg strings or null can be thrown as well.
- if (error instanceof Error) {
- var message = error.message,
- name = error.name;
-
- var summary = message ? name + ": " + message : name;
-
- errorToHandle = error;
-
- try {
- errorToHandle.message =
- summary + "\n\nThis error is located at:" + componentStack;
- } catch (e) {}
- } else if (typeof error === "string") {
- errorToHandle = new Error(
- error + "\n\nThis error is located at:" + componentStack
- );
- } else {
- errorToHandle = new Error("Unspecified error at:" + componentStack);
- }
-
- ExceptionsManager.handleException(errorToHandle, false);
-
- // Return false here to prevent ReactFiberErrorLogger default behavior of
- // logging error details to console.error. Calls to console.error are
- // automatically routed to the native redbox controller, which we've already
- // done above by calling ExceptionsManager.
- return false;
-}
-
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
@@ -3222,8 +3156,8 @@ function restoreDeletedValuesInNestedArray(
if (!removedKeys[propKey]) {
continue;
}
- var nextProp = obj[propKey];
- if (nextProp === undefined) {
+ var _nextProp = obj[propKey];
+ if (_nextProp === undefined) {
continue;
}
@@ -3232,16 +3166,16 @@ function restoreDeletedValuesInNestedArray(
continue; // not a valid native prop
}
- if (typeof nextProp === "function") {
- nextProp = true;
+ if (typeof _nextProp === "function") {
+ _nextProp = true;
}
- if (typeof nextProp === "undefined") {
- nextProp = null;
+ if (typeof _nextProp === "undefined") {
+ _nextProp = null;
}
if (typeof attributeConfig !== "object") {
// case: !Object is the default case
- updatePayload[propKey] = nextProp;
+ updatePayload[propKey] = _nextProp;
} else if (
typeof attributeConfig.diff === "function" ||
typeof attributeConfig.process === "function"
@@ -3249,8 +3183,8 @@ function restoreDeletedValuesInNestedArray(
// case: CustomAttributeConfiguration
var nextValue =
typeof attributeConfig.process === "function"
- ? attributeConfig.process(nextProp)
- : nextProp;
+ ? attributeConfig.process(_nextProp)
+ : _nextProp;
updatePayload[propKey] = nextValue;
}
removedKeys[propKey] = false;
@@ -3267,7 +3201,7 @@ function diffNestedArrayProperty(
) {
var minLength =
prevArray.length < nextArray.length ? prevArray.length : nextArray.length;
- var i;
+ var i = void 0;
for (i = 0; i < minLength; i++) {
// Diff any items in the array in the forward direction. Repeated keys
// will be overwritten by later values.
@@ -3426,9 +3360,9 @@ function clearNestedProperty(updatePayload, prevProp, validAttributes) {
* anything changed.
*/
function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
- var attributeConfig;
- var nextProp;
- var prevProp;
+ var attributeConfig = void 0;
+ var nextProp = void 0;
+ var prevProp = void 0;
for (var propKey in nextProps) {
attributeConfig = validAttributes[propKey];
@@ -3509,11 +3443,11 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
? attributeConfig.diff(prevProp, nextProp)
: defaultDiffer(prevProp, nextProp));
if (shouldUpdate) {
- nextValue =
+ var _nextValue =
typeof attributeConfig.process === "function"
? attributeConfig.process(nextProp)
: nextProp;
- (updatePayload || (updatePayload = {}))[propKey] = nextValue;
+ (updatePayload || (updatePayload = {}))[propKey] = _nextValue;
}
} else {
// default: fallthrough case when nested properties are defined
@@ -3541,21 +3475,21 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
// Also iterate through all the previous props to catch any that have been
// removed and make sure native gets the signal so it can reset them to the
// default.
- for (propKey in prevProps) {
- if (nextProps[propKey] !== undefined) {
+ for (var _propKey in prevProps) {
+ if (nextProps[_propKey] !== undefined) {
continue; // we've already covered this key in the previous pass
}
- attributeConfig = validAttributes[propKey];
+ attributeConfig = validAttributes[_propKey];
if (!attributeConfig) {
continue; // not a valid native prop
}
- if (updatePayload && updatePayload[propKey] !== undefined) {
+ if (updatePayload && updatePayload[_propKey] !== undefined) {
// This was already updated to a diff result earlier.
continue;
}
- prevProp = prevProps[propKey];
+ prevProp = prevProps[_propKey];
if (prevProp === undefined) {
continue; // was already empty anyway
}
@@ -3567,12 +3501,12 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
) {
// case: CustomAttributeConfiguration | !Object
// Flag the leaf property for removal by sending a sentinel.
- (updatePayload || (updatePayload = {}))[propKey] = null;
+ (updatePayload || (updatePayload = {}))[_propKey] = null;
if (!removedKeys) {
removedKeys = {};
}
- if (!removedKeys[propKey]) {
- removedKeys[propKey] = true;
+ if (!removedKeys[_propKey]) {
+ removedKeys[_propKey] = true;
removedKeyCount++;
}
} else {
@@ -3719,5193 +3653,5772 @@ function set(key, value) {
function getComponentName(fiber) {
var type = fiber.type;
+ if (typeof type === "function") {
+ return type.displayName || type.name;
+ }
if (typeof type === "string") {
return type;
}
- if (typeof type === "function") {
- return type.displayName || type.name;
+ switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return "ReactFragment";
+ case REACT_PORTAL_TYPE:
+ return "ReactPortal";
+ case REACT_CALL_TYPE:
+ return "ReactCall";
+ case REACT_RETURN_TYPE:
+ return "ReactReturn";
}
return null;
}
-// Re-export dynamic flags from the fbsource version.
-var _require = require("ReactFeatureFlags");
-
-var debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects;
-
-var enableAsyncSubtreeAPI = true;
-
-var enableUserTimingAPI = true;
-var enableMutatingReconciler = true;
-var enableNoopReconciler = false;
-var enablePersistentReconciler = false;
+// TODO: Share this module between Fabric and React Native renderers
+// so that both can be used in the same tree.
-// Only used in www builds.
-
-// Don't change these two values:
-var NoEffect = 0; // 0b00000000
-var PerformedWork = 1; // 0b00000001
-
-// You can change the rest (and add more).
-var Placement = 2; // 0b00000010
-var Update = 4; // 0b00000100
-var PlacementAndUpdate = 6; // 0b00000110
-var Deletion = 8; // 0b00001000
-var ContentReset = 16; // 0b00010000
-var Callback = 32; // 0b00100000
-var Err = 64; // 0b01000000
-var Ref = 128; // 0b10000000
+var findHostInstance = function(fiber) {
+ return null;
+};
-var MOUNTING = 1;
-var MOUNTED = 2;
-var UNMOUNTED = 3;
+var findHostInstanceFabric = function(fiber) {
+ return null;
+};
-function isFiberMountedImpl(fiber) {
- var node = fiber;
- if (!fiber.alternate) {
- // If there is no alternate, this might be a new tree that isn't inserted
- // yet. If it is, then it will have a pending insertion effect on it.
- if ((node.effectTag & Placement) !== NoEffect) {
- return MOUNTING;
- }
- while (node["return"]) {
- node = node["return"];
- if ((node.effectTag & Placement) !== NoEffect) {
- return MOUNTING;
- }
- }
- } else {
- while (node["return"]) {
- node = node["return"];
- }
- }
- if (node.tag === HostRoot) {
- // TODO: Check if this was a nested HostRoot when used with
- // renderContainerIntoSubtree.
- return MOUNTED;
- }
- // If we didn't hit the root, that means that we're in an disconnected tree
- // that has been unmounted.
- return UNMOUNTED;
+function injectFindHostInstance(impl) {
+ findHostInstance = impl;
}
-function isFiberMounted(fiber) {
- return isFiberMountedImpl(fiber) === MOUNTED;
-}
+/**
+ * ReactNative vs ReactWeb
+ * -----------------------
+ * React treats some pieces of data opaquely. This means that the information
+ * is first class (it can be passed around), but cannot be inspected. This
+ * allows us to build infrastructure that reasons about resources, without
+ * making assumptions about the nature of those resources, and this allows that
+ * infra to be shared across multiple platforms, where the resources are very
+ * different. General infra (such as `ReactMultiChild`) reasons opaquely about
+ * the data, but platform specific code (such as `ReactNativeBaseComponent`) can
+ * make assumptions about the data.
+ *
+ *
+ * `rootNodeID`, uniquely identifies a position in the generated native view
+ * tree. Many layers of composite components (created with `React.createClass`)
+ * can all share the same `rootNodeID`.
+ *
+ * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
+ * resource (dom node, native view etc). The `rootNodeID` is sufficient for web
+ * `nodeHandle`s, because the position in a tree is always enough to uniquely
+ * identify a DOM node (we never have nodes in some bank outside of the
+ * document). The same would be true for `ReactNative`, but we must maintain a
+ * mapping that we can send efficiently serializable
+ * strings across native boundaries.
+ *
+ * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
+ * ----------------------------------------------------------------------------
+ * nodeHandle N/A rootNodeID tag
+ */
-function isMounted(component) {
+// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive
+// eg findInternalHostInstance. This will reduce the likelihood of someone
+// accidentally deep-requiring this version.
+function findNodeHandle(componentOrHandle) {
{
var owner = ReactCurrentOwner.current;
- if (owner !== null && owner.tag === ClassComponent) {
- var ownerFiber = owner;
- var instance = ownerFiber.stateNode;
+ if (owner !== null && owner.stateNode !== null) {
warning(
- instance._warnedAboutRefsInRender,
- "%s is accessing isMounted inside its render() function. " +
+ owner.stateNode._warnedAboutRefsInRender,
+ "%s is accessing findNodeHandle inside its render(). " +
"render() should be a pure function of props and state. It should " +
"never access something that requires stale data from the previous " +
"render, such as refs. Move this logic to componentDidMount and " +
"componentDidUpdate instead.",
- getComponentName(ownerFiber) || "A component"
+ getComponentName(owner) || "A component"
);
- instance._warnedAboutRefsInRender = true;
+
+ owner.stateNode._warnedAboutRefsInRender = true;
}
}
-
- var fiber = get(component);
- if (!fiber) {
- return false;
+ if (componentOrHandle == null) {
+ return null;
+ }
+ if (typeof componentOrHandle === "number") {
+ // Already a node handle
+ return componentOrHandle;
}
- return isFiberMountedImpl(fiber) === MOUNTED;
-}
-function assertIsMounted(fiber) {
- invariant(
- isFiberMountedImpl(fiber) === MOUNTED,
- "Unable to find node on an unmounted component."
- );
-}
+ var component = componentOrHandle;
-function findCurrentFiberUsingSlowPath(fiber) {
- var alternate = fiber.alternate;
- if (!alternate) {
- // If there is no alternate, then we only need to check if it is mounted.
- var state = isFiberMountedImpl(fiber);
- invariant(
- state !== UNMOUNTED,
- "Unable to find node on an unmounted component."
+ // TODO (balpert): Wrap iOS native components in a composite wrapper, then
+ // ReactInstanceMap.get here will always succeed for mounted components
+ var internalInstance = get(component);
+ if (internalInstance) {
+ return (
+ findHostInstance(internalInstance) ||
+ findHostInstanceFabric(internalInstance)
);
- if (state === MOUNTING) {
- return null;
- }
- return fiber;
- }
- // If we have two possible branches, we'll walk backwards up to the root
- // to see what path the root points to. On the way we may hit one of the
- // special cases and we'll deal with them.
- var a = fiber;
- var b = alternate;
- while (true) {
- var parentA = a["return"];
- var parentB = parentA ? parentA.alternate : null;
- if (!parentA || !parentB) {
- // We're at the root.
- break;
- }
-
- // If both copies of the parent fiber point to the same child, we can
- // assume that the child is current. This happens when we bailout on low
- // priority: the bailed out fiber's child reuses the current child.
- if (parentA.child === parentB.child) {
- var child = parentA.child;
- while (child) {
- if (child === a) {
- // We've determined that A is the current branch.
- assertIsMounted(parentA);
- return fiber;
- }
- if (child === b) {
- // We've determined that B is the current branch.
- assertIsMounted(parentA);
- return alternate;
- }
- child = child.sibling;
- }
- // We should never have an alternate for any mounting node. So the only
- // way this could possibly happen is if this was unmounted, if at all.
- invariant(false, "Unable to find node on an unmounted component.");
- }
-
- if (a["return"] !== b["return"]) {
- // The return pointer of A and the return pointer of B point to different
- // fibers. We assume that return pointers never criss-cross, so A must
- // belong to the child set of A.return, and B must belong to the child
- // set of B.return.
- a = parentA;
- b = parentB;
+ } else {
+ if (component) {
+ return component;
} else {
- // The return pointers point to the same fiber. We'll have to use the
- // default, slow path: scan the child sets of each parent alternate to see
- // which child belongs to which set.
- //
- // Search parent A's child set
- var didFindChild = false;
- var _child = parentA.child;
- while (_child) {
- if (_child === a) {
- didFindChild = true;
- a = parentA;
- b = parentB;
- break;
- }
- if (_child === b) {
- didFindChild = true;
- b = parentA;
- a = parentB;
- break;
- }
- _child = _child.sibling;
- }
- if (!didFindChild) {
- // Search parent B's child set
- _child = parentB.child;
- while (_child) {
- if (_child === a) {
- didFindChild = true;
- a = parentB;
- b = parentA;
- break;
- }
- if (_child === b) {
- didFindChild = true;
- b = parentB;
- a = parentA;
- break;
- }
- _child = _child.sibling;
- }
- invariant(
- didFindChild,
- "Child was not found in either parent set. This indicates a bug " +
- "in React related to the return pointer. Please file an issue."
- );
- }
+ invariant(
+ // Native
+ (typeof component === "object" && "_nativeTag" in component) ||
+ // Composite
+ (component.render != null && typeof component.render === "function"),
+ "findNodeHandle(...): Argument is not a component " +
+ "(type: %s, keys: %s)",
+ typeof component,
+ Object.keys(component)
+ );
+ invariant(
+ false,
+ "findNodeHandle(...): Unable to find node handle for unmounted " +
+ "component."
+ );
}
-
- invariant(
- a.alternate === b,
- "Return fibers should always be each others' alternates. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- }
- // If the root is not a host container, we're in a disconnected tree. I.e.
- // unmounted.
- invariant(
- a.tag === HostRoot,
- "Unable to find node on an unmounted component."
- );
- if (a.stateNode.current === a) {
- // We've determined that A is the current branch.
- return fiber;
}
- // Otherwise B has to be current branch.
- return alternate;
}
-function findCurrentHostFiber(parent) {
- var currentParent = findCurrentFiberUsingSlowPath(parent);
- if (!currentParent) {
- return null;
- }
-
- // Next we'll drill down this component to find the first HostComponent/Text.
- var node = currentParent;
- while (true) {
- if (node.tag === HostComponent || node.tag === HostText) {
- return node;
- } else if (node.child) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- if (node === currentParent) {
- return null;
- }
- while (!node.sibling) {
- if (!node["return"] || node["return"] === currentParent) {
- return null;
- }
- node = node["return"];
- }
- node.sibling["return"] = node["return"];
- node = node.sibling;
+/**
+ * External users of findNodeHandle() expect the host tag number return type.
+ * The injected findNodeHandle() strategy returns the instance wrapper though.
+ * See NativeMethodsMixin#setNativeProps for more info on why this is done.
+ */
+function findNumericNodeHandleFiber(componentOrHandle) {
+ var instance = findNodeHandle(componentOrHandle);
+ if (instance == null || typeof instance === "number") {
+ return instance;
}
- // Flow needs the return null here, but ESLint complains about it.
- // eslint-disable-next-line no-unreachable
- return null;
+ return instance._nativeTag;
}
-function findCurrentHostFiberWithNoPortals(parent) {
- var currentParent = findCurrentFiberUsingSlowPath(parent);
- if (!currentParent) {
- return null;
- }
+// Modules provided by RN:
+/**
+ * `NativeMethodsMixin` provides methods to access the underlying native
+ * component directly. This can be useful in cases when you want to focus
+ * a view or measure its on-screen dimensions, for example.
+ *
+ * The methods described here are available on most of the default components
+ * provided by React Native. Note, however, that they are *not* available on
+ * composite components that aren't directly backed by a native view. This will
+ * generally include most components that you define in your own app. For more
+ * information, see [Direct
+ * Manipulation](docs/direct-manipulation.html).
+ *
+ * Note the Flow $Exact<> syntax is required to support mixins.
+ * React createClass mixins can only be used with exact types.
+ */
+var NativeMethodsMixin = {
+ /**
+ * Determines the location on screen, width, and height of the given view and
+ * returns the values via an async callback. If successful, the callback will
+ * be called with the following arguments:
+ *
+ * - x
+ * - y
+ * - width
+ * - height
+ * - pageX
+ * - pageY
+ *
+ * Note that these measurements are not available until after the rendering
+ * has been completed in native. If you need the measurements as soon as
+ * possible, consider using the [`onLayout`
+ * prop](docs/view.html#onlayout) instead.
+ */
+ measure: function(callback) {
+ UIManager.measure(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ },
- // Next we'll drill down this component to find the first HostComponent/Text.
- var node = currentParent;
- while (true) {
- if (node.tag === HostComponent || node.tag === HostText) {
- return node;
- } else if (node.child && node.tag !== HostPortal) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- if (node === currentParent) {
- return null;
- }
- while (!node.sibling) {
- if (!node["return"] || node["return"] === currentParent) {
- return null;
- }
- node = node["return"];
- }
- node.sibling["return"] = node["return"];
- node = node.sibling;
- }
- // Flow needs the return null here, but ESLint complains about it.
- // eslint-disable-next-line no-unreachable
- return null;
-}
+ /**
+ * Determines the location of the given view in the window and returns the
+ * values via an async callback. If the React root view is embedded in
+ * another native view, this will give you the absolute coordinates. If
+ * successful, the callback will be called with the following
+ * arguments:
+ *
+ * - x
+ * - y
+ * - width
+ * - height
+ *
+ * Note that these measurements are not available until after the rendering
+ * has been completed in native.
+ */
+ measureInWindow: function(callback) {
+ UIManager.measureInWindow(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ },
-var valueStack = [];
+ /**
+ * Like [`measure()`](#measure), but measures the view relative an ancestor,
+ * specified as `relativeToNativeNode`. This means that the returned x, y
+ * are relative to the origin x, y of the ancestor view.
+ *
+ * As always, to obtain a native node handle for a component, you can use
+ * `findNumericNodeHandle(component)`.
+ */
+ measureLayout: function(
+ relativeToNativeNode,
+ onSuccess,
+ onFail /* currently unused */
+ ) {
+ UIManager.measureLayout(
+ findNumericNodeHandleFiber(this),
+ relativeToNativeNode,
+ mountSafeCallback(this, onFail),
+ mountSafeCallback(this, onSuccess)
+ );
+ },
-{
- var fiberStack = [];
-}
+ /**
+ * This function sends props straight to native. They will not participate in
+ * future diff process - this means that if you do not include them in the
+ * next render, they will remain active (see [Direct
+ * Manipulation](docs/direct-manipulation.html)).
+ */
+ setNativeProps: function(nativeProps) {
+ // Class components don't have viewConfig -> validateAttributes.
+ // Nor does it make sense to set native props on a non-native component.
+ // Instead, find the nearest host component and set props on it.
+ // Use findNodeHandle() rather than findNumericNodeHandle() because
+ // We want the instance/wrapper (not the native tag).
+ var maybeInstance = void 0;
-var index = -1;
+ // Fiber errors if findNodeHandle is called for an umounted component.
+ // Tests using ReactTestRenderer will trigger this case indirectly.
+ // Mimicking stack behavior, we should silently ignore this case.
+ // TODO Fix ReactTestRenderer so we can remove this try/catch.
+ try {
+ maybeInstance = findNodeHandle(this);
+ } catch (error) {}
-function createCursor(defaultValue) {
- return {
- current: defaultValue
- };
-}
+ // If there is no host component beneath this we should fail silently.
+ // This is not an error; it could mean a class component rendered null.
+ if (maybeInstance == null) {
+ return;
+ }
+
+ var viewConfig = maybeInstance.viewConfig;
-function pop(cursor, fiber) {
- if (index < 0) {
{
- warning(false, "Unexpected pop.");
+ warnForStyleProps(nativeProps, viewConfig.validAttributes);
}
- return;
- }
- {
- if (fiber !== fiberStack[index]) {
- warning(false, "Unexpected Fiber popped.");
- }
- }
+ var updatePayload = create(nativeProps, viewConfig.validAttributes);
- cursor.current = valueStack[index];
+ // Avoid the overhead of bridge calls if there's no update.
+ // This is an expensive no-op for Android, and causes an unnecessary
+ // view invalidation for certain components (eg RCTTextInput) on iOS.
+ if (updatePayload != null) {
+ UIManager.updateView(
+ maybeInstance._nativeTag,
+ viewConfig.uiViewClassName,
+ updatePayload
+ );
+ }
+ },
- valueStack[index] = null;
+ /**
+ * Requests focus for the given input or view. The exact behavior triggered
+ * will depend on the platform and type of view.
+ */
+ focus: function() {
+ TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
+ },
- {
- fiberStack[index] = null;
+ /**
+ * Removes focus from an input or view. This is the opposite of `focus()`.
+ */
+ blur: function() {
+ TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
}
+};
- index--;
+{
+ // hide this from Flow since we can't define these properties outside of
+ // true without actually implementing them (setting them to undefined
+ // isn't allowed by ReactClass)
+ var NativeMethodsMixin_DEV = NativeMethodsMixin;
+ invariant(
+ !NativeMethodsMixin_DEV.componentWillMount &&
+ !NativeMethodsMixin_DEV.componentWillReceiveProps &&
+ !NativeMethodsMixin_DEV.UNSAFE_componentWillMount &&
+ !NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps,
+ "Do not override existing functions."
+ );
+ NativeMethodsMixin_DEV.componentWillMount = function() {
+ throwOnStylesProp(this, this.props);
+ };
+ NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
+ throwOnStylesProp(this, newProps);
+ };
+ NativeMethodsMixin_DEV.UNSAFE_componentWillMount = function() {
+ throwOnStylesProp(this, this.props);
+ };
+ NativeMethodsMixin_DEV.UNSAFE_componentWillReceiveProps = function(newProps) {
+ throwOnStylesProp(this, newProps);
+ };
}
-function push(cursor, value, fiber) {
- index++;
-
- valueStack[index] = cursor.current;
-
- {
- fiberStack[index] = fiber;
+function _classCallCheck$1(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
}
-
- cursor.current = value;
}
-function reset() {
- while (index > -1) {
- valueStack[index] = null;
-
- {
- fiberStack[index] = null;
- }
-
- index--;
+function _possibleConstructorReturn(self, call) {
+ if (!self) {
+ throw new ReferenceError(
+ "this hasn't been initialised - super() hasn't been called"
+ );
}
+ return call && (typeof call === "object" || typeof call === "function")
+ ? call
+ : self;
}
-var describeComponentFrame = function(name, source, ownerName) {
- return (
- "\n in " +
- (name || "Unknown") +
- (source
- ? " (at " +
- source.fileName.replace(/^.*[\\\/]/, "") +
- ":" +
- source.lineNumber +
- ")"
- : ownerName ? " (created by " + ownerName + ")" : "")
- );
-};
-
-function describeFiber(fiber) {
- switch (fiber.tag) {
- case IndeterminateComponent:
- case FunctionalComponent:
- case ClassComponent:
- case HostComponent:
- var owner = fiber._debugOwner;
- var source = fiber._debugSource;
- var name = getComponentName(fiber);
- var ownerName = null;
- if (owner) {
- ownerName = getComponentName(owner);
- }
- return describeComponentFrame(name, source, ownerName);
- default:
- return "";
+function _inherits(subClass, superClass) {
+ if (typeof superClass !== "function" && superClass !== null) {
+ throw new TypeError(
+ "Super expression must either be null or a function, not " +
+ typeof superClass
+ );
}
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: false,
+ writable: true,
+ configurable: true
+ }
+ });
+ if (superClass)
+ Object.setPrototypeOf
+ ? Object.setPrototypeOf(subClass, superClass)
+ : (subClass.__proto__ = superClass);
}
-// This function can only be called with a work-in-progress fiber and
-// only during begin or complete phase. Do not call it under any other
-// circumstances.
-function getStackAddendumByWorkInProgressFiber(workInProgress) {
- var info = "";
- var node = workInProgress;
- do {
- info += describeFiber(node);
- // Otherwise this return pointer might point to the wrong tree:
- node = node["return"];
- } while (node);
- return info;
-}
+// Modules provided by RN:
+/**
+ * Superclass that provides methods to access the underlying native component.
+ * This can be useful when you want to focus a view or measure its dimensions.
+ *
+ * Methods implemented by this class are available on most default components
+ * provided by React Native. However, they are *not* available on composite
+ * components that are not directly backed by a native view. For more
+ * information, see [Direct Manipulation](docs/direct-manipulation.html).
+ *
+ * @abstract
+ */
-function getCurrentFiberOwnerName() {
- {
- var fiber = ReactDebugCurrentFiber.current;
- if (fiber === null) {
- return null;
- }
- var owner = fiber._debugOwner;
- if (owner !== null && typeof owner !== "undefined") {
- return getComponentName(owner);
- }
- }
- return null;
-}
+var ReactNativeComponent = (function(_React$Component) {
+ _inherits(ReactNativeComponent, _React$Component);
-function getCurrentFiberStackAddendum() {
- {
- var fiber = ReactDebugCurrentFiber.current;
- if (fiber === null) {
- return null;
- }
- // Safe because if current fiber exists, we are reconciling,
- // and it is guaranteed to be the work-in-progress version.
- return getStackAddendumByWorkInProgressFiber(fiber);
+ function ReactNativeComponent() {
+ _classCallCheck$1(this, ReactNativeComponent);
+
+ return _possibleConstructorReturn(
+ this,
+ _React$Component.apply(this, arguments)
+ );
}
- return null;
-}
-function resetCurrentFiber() {
- ReactDebugCurrentFrame.getCurrentStack = null;
- ReactDebugCurrentFiber.current = null;
- ReactDebugCurrentFiber.phase = null;
-}
+ /**
+ * Removes focus. This is the opposite of `focus()`.
+ */
+ ReactNativeComponent.prototype.blur = function blur() {
+ TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
+ };
-function setCurrentFiber(fiber) {
- ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum;
- ReactDebugCurrentFiber.current = fiber;
- ReactDebugCurrentFiber.phase = null;
-}
+ /**
+ * Requests focus. The exact behavior depends on the platform and view.
+ */
-function setCurrentPhase(phase) {
- ReactDebugCurrentFiber.phase = phase;
-}
+ ReactNativeComponent.prototype.focus = function focus() {
+ TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
+ };
-var ReactDebugCurrentFiber = {
- current: null,
- phase: null,
- resetCurrentFiber: resetCurrentFiber,
- setCurrentFiber: setCurrentFiber,
- setCurrentPhase: setCurrentPhase,
- getCurrentFiberOwnerName: getCurrentFiberOwnerName,
- getCurrentFiberStackAddendum: getCurrentFiberStackAddendum
-};
+ /**
+ * Measures the on-screen location and dimensions. If successful, the callback
+ * will be called asynchronously with the following arguments:
+ *
+ * - x
+ * - y
+ * - width
+ * - height
+ * - pageX
+ * - pageY
+ *
+ * These values are not available until after natives rendering completes. If
+ * you need the measurements as soon as possible, consider using the
+ * [`onLayout` prop](docs/view.html#onlayout) instead.
+ */
-// Prefix measurements so that it's possible to filter them.
-// Longer prefixes are hard to read in DevTools.
-var reactEmoji = "\u269B";
-var warningEmoji = "\u26D4";
-var supportsUserTiming =
- typeof performance !== "undefined" &&
- typeof performance.mark === "function" &&
- typeof performance.clearMarks === "function" &&
- typeof performance.measure === "function" &&
- typeof performance.clearMeasures === "function";
+ ReactNativeComponent.prototype.measure = function measure(callback) {
+ UIManager.measure(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ };
-// Keep track of current fiber so that we know the path to unwind on pause.
-// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
-var currentFiber = null;
-// If we're in the middle of user code, which fiber and method is it?
-// Reusing `currentFiber` would be confusing for this because user code fiber
-// can change during commit phase too, but we don't need to unwind it (since
-// lifecycles in the commit phase don't resemble a tree).
-var currentPhase = null;
-var currentPhaseFiber = null;
-// Did lifecycle hook schedule an update? This is often a performance problem,
-// so we will keep track of it, and include it in the report.
-// Track commits caused by cascading updates.
-var isCommitting = false;
-var hasScheduledUpdateInCurrentCommit = false;
-var hasScheduledUpdateInCurrentPhase = false;
-var commitCountInCurrentWorkLoop = 0;
-var effectCountInCurrentCommit = 0;
-var isWaitingForCallback = false;
-// During commits, we only show a measurement once per method name
-// to avoid stretch the commit phase with measurement overhead.
-var labelsInCurrentCommit = new Set();
+ /**
+ * Measures the on-screen location and dimensions. Even if the React Native
+ * root view is embedded within another native view, this method will give you
+ * the absolute coordinates measured from the window. If successful, the
+ * callback will be called asynchronously with the following arguments:
+ *
+ * - x
+ * - y
+ * - width
+ * - height
+ *
+ * These values are not available until after natives rendering completes.
+ */
-var formatMarkName = function(markName) {
- return reactEmoji + " " + markName;
-};
+ ReactNativeComponent.prototype.measureInWindow = function measureInWindow(
+ callback
+ ) {
+ UIManager.measureInWindow(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ };
-var formatLabel = function(label, warning$$1) {
- var prefix = warning$$1 ? warningEmoji + " " : reactEmoji + " ";
- var suffix = warning$$1 ? " Warning: " + warning$$1 : "";
- return "" + prefix + label + suffix;
-};
+ /**
+ * Similar to [`measure()`](#measure), but the resulting location will be
+ * relative to the supplied ancestor's location.
+ *
+ * Obtain a native node handle with `ReactNative.findNodeHandle(component)`.
+ */
-var beginMark = function(markName) {
- performance.mark(formatMarkName(markName));
-};
+ ReactNativeComponent.prototype.measureLayout = function measureLayout(
+ relativeToNativeNode,
+ onSuccess,
+ onFail /* currently unused */
+ ) {
+ UIManager.measureLayout(
+ findNumericNodeHandleFiber(this),
+ relativeToNativeNode,
+ mountSafeCallback(this, onFail),
+ mountSafeCallback(this, onSuccess)
+ );
+ };
-var clearMark = function(markName) {
- performance.clearMarks(formatMarkName(markName));
-};
+ /**
+ * This function sends props straight to native. They will not participate in
+ * future diff process - this means that if you do not include them in the
+ * next render, they will remain active (see [Direct
+ * Manipulation](docs/direct-manipulation.html)).
+ */
-var endMark = function(label, markName, warning$$1) {
- var formattedMarkName = formatMarkName(markName);
- var formattedLabel = formatLabel(label, warning$$1);
- try {
- performance.measure(formattedLabel, formattedMarkName);
- } catch (err) {}
- // If previous mark was missing for some reason, this will throw.
- // This could only happen if React crashed in an unexpected place earlier.
- // Don't pile on with more errors.
+ ReactNativeComponent.prototype.setNativeProps = function setNativeProps(
+ nativeProps
+ ) {
+ // Class components don't have viewConfig -> validateAttributes.
+ // Nor does it make sense to set native props on a non-native component.
+ // Instead, find the nearest host component and set props on it.
+ // Use findNodeHandle() rather than ReactNative.findNodeHandle() because
+ // We want the instance/wrapper (not the native tag).
+ var maybeInstance = void 0;
- // Clear marks immediately to avoid growing buffer.
- performance.clearMarks(formattedMarkName);
- performance.clearMeasures(formattedLabel);
-};
+ // Fiber errors if findNodeHandle is called for an umounted component.
+ // Tests using ReactTestRenderer will trigger this case indirectly.
+ // Mimicking stack behavior, we should silently ignore this case.
+ // TODO Fix ReactTestRenderer so we can remove this try/catch.
+ try {
+ maybeInstance = findNodeHandle(this);
+ } catch (error) {}
-var getFiberMarkName = function(label, debugID) {
- return label + " (#" + debugID + ")";
-};
+ // If there is no host component beneath this we should fail silently.
+ // This is not an error; it could mean a class component rendered null.
+ if (maybeInstance == null) {
+ return;
+ }
-var getFiberLabel = function(componentName, isMounted, phase) {
- if (phase === null) {
- // These are composite component total time measurements.
- return componentName + " [" + (isMounted ? "update" : "mount") + "]";
- } else {
- // Composite component methods.
- return componentName + "." + phase;
- }
-};
+ var viewConfig =
+ maybeInstance.viewConfig || maybeInstance.canonical.viewConfig;
-var beginFiberMark = function(fiber, phase) {
- var componentName = getComponentName(fiber) || "Unknown";
- var debugID = fiber._debugID;
- var isMounted = fiber.alternate !== null;
- var label = getFiberLabel(componentName, isMounted, phase);
+ var updatePayload = create(nativeProps, viewConfig.validAttributes);
- if (isCommitting && labelsInCurrentCommit.has(label)) {
- // During the commit phase, we don't show duplicate labels because
- // there is a fixed overhead for every measurement, and we don't
- // want to stretch the commit phase beyond necessary.
- return false;
- }
- labelsInCurrentCommit.add(label);
+ // Avoid the overhead of bridge calls if there's no update.
+ // This is an expensive no-op for Android, and causes an unnecessary
+ // view invalidation for certain components (eg RCTTextInput) on iOS.
+ if (updatePayload != null) {
+ UIManager.updateView(
+ maybeInstance._nativeTag,
+ viewConfig.uiViewClassName,
+ updatePayload
+ );
+ }
+ };
- var markName = getFiberMarkName(label, debugID);
- beginMark(markName);
- return true;
-};
+ return ReactNativeComponent;
+})(React.Component);
-var clearFiberMark = function(fiber, phase) {
- var componentName = getComponentName(fiber) || "Unknown";
- var debugID = fiber._debugID;
- var isMounted = fiber.alternate !== null;
- var label = getFiberLabel(componentName, isMounted, phase);
- var markName = getFiberMarkName(label, debugID);
- clearMark(markName);
-};
+// Don't change these two values:
+var NoEffect = 0;
+var PerformedWork = 1;
-var endFiberMark = function(fiber, phase, warning$$1) {
- var componentName = getComponentName(fiber) || "Unknown";
- var debugID = fiber._debugID;
- var isMounted = fiber.alternate !== null;
- var label = getFiberLabel(componentName, isMounted, phase);
- var markName = getFiberMarkName(label, debugID);
- endMark(label, markName, warning$$1);
-};
+// You can change the rest (and add more).
+var Placement = 2;
+var Update = 4;
+var PlacementAndUpdate = 6;
+var Deletion = 8;
+var ContentReset = 16;
+var Callback = 32;
+var Err = 64;
+var Ref = 128;
-var shouldIgnoreFiber = function(fiber) {
- // Host components should be skipped in the timeline.
- // We could check typeof fiber.type, but does this work with RN?
- switch (fiber.tag) {
- case HostRoot:
- case HostComponent:
- case HostText:
- case HostPortal:
- case ReturnComponent:
- case Fragment:
- return true;
- default:
- return false;
- }
-};
-
-var clearPendingPhaseMeasurement = function() {
- if (currentPhase !== null && currentPhaseFiber !== null) {
- clearFiberMark(currentPhaseFiber, currentPhase);
- }
- currentPhaseFiber = null;
- currentPhase = null;
- hasScheduledUpdateInCurrentPhase = false;
-};
+var MOUNTING = 1;
+var MOUNTED = 2;
+var UNMOUNTED = 3;
-var pauseTimers = function() {
- // Stops all currently active measurements so that they can be resumed
- // if we continue in a later deferred loop from the same unit of work.
- var fiber = currentFiber;
- while (fiber) {
- if (fiber._debugIsCurrentlyTiming) {
- endFiberMark(fiber, null, null);
+function isFiberMountedImpl(fiber) {
+ var node = fiber;
+ if (!fiber.alternate) {
+ // If there is no alternate, this might be a new tree that isn't inserted
+ // yet. If it is, then it will have a pending insertion effect on it.
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
}
- fiber = fiber["return"];
- }
-};
-
-var resumeTimersRecursively = function(fiber) {
- if (fiber["return"] !== null) {
- resumeTimersRecursively(fiber["return"]);
- }
- if (fiber._debugIsCurrentlyTiming) {
- beginFiberMark(fiber, null);
- }
-};
-
-var resumeTimers = function() {
- // Resumes all measurements that were active during the last deferred loop.
- if (currentFiber !== null) {
- resumeTimersRecursively(currentFiber);
- }
-};
-
-function recordEffect() {
- if (enableUserTimingAPI) {
- effectCountInCurrentCommit++;
- }
-}
-
-function recordScheduleUpdate() {
- if (enableUserTimingAPI) {
- if (isCommitting) {
- hasScheduledUpdateInCurrentCommit = true;
+ while (node["return"]) {
+ node = node["return"];
+ if ((node.effectTag & Placement) !== NoEffect) {
+ return MOUNTING;
+ }
}
- if (
- currentPhase !== null &&
- currentPhase !== "componentWillMount" &&
- currentPhase !== "componentWillReceiveProps"
- ) {
- hasScheduledUpdateInCurrentPhase = true;
+ } else {
+ while (node["return"]) {
+ node = node["return"];
}
}
-}
-
-function startRequestCallbackTimer() {
- if (enableUserTimingAPI) {
- if (supportsUserTiming && !isWaitingForCallback) {
- isWaitingForCallback = true;
- beginMark("(Waiting for async callback...)");
- }
+ if (node.tag === HostRoot) {
+ // TODO: Check if this was a nested HostRoot when used with
+ // renderContainerIntoSubtree.
+ return MOUNTED;
}
+ // If we didn't hit the root, that means that we're in an disconnected tree
+ // that has been unmounted.
+ return UNMOUNTED;
}
-function stopRequestCallbackTimer(didExpire) {
- if (enableUserTimingAPI) {
- if (supportsUserTiming) {
- isWaitingForCallback = false;
- var warning$$1 = didExpire ? "React was blocked by main thread" : null;
- endMark(
- "(Waiting for async callback...)",
- "(Waiting for async callback...)",
- warning$$1
- );
- }
- }
+function isFiberMounted(fiber) {
+ return isFiberMountedImpl(fiber) === MOUNTED;
}
-function startWorkTimer(fiber) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
- return;
- }
- // If we pause, this is the fiber to unwind from.
- currentFiber = fiber;
- if (!beginFiberMark(fiber, null)) {
- return;
+function isMounted(component) {
+ {
+ var owner = ReactCurrentOwner.current;
+ if (owner !== null && owner.tag === ClassComponent) {
+ var ownerFiber = owner;
+ var instance = ownerFiber.stateNode;
+ warning(
+ instance._warnedAboutRefsInRender,
+ "%s is accessing isMounted inside its render() function. " +
+ "render() should be a pure function of props and state. It should " +
+ "never access something that requires stale data from the previous " +
+ "render, such as refs. Move this logic to componentDidMount and " +
+ "componentDidUpdate instead.",
+ getComponentName(ownerFiber) || "A component"
+ );
+ instance._warnedAboutRefsInRender = true;
}
- fiber._debugIsCurrentlyTiming = true;
}
-}
-function cancelWorkTimer(fiber) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
- return;
- }
- // Remember we shouldn't complete measurement for this fiber.
- // Otherwise flamechart will be deep even for small updates.
- fiber._debugIsCurrentlyTiming = false;
- clearFiberMark(fiber, null);
+ var fiber = get(component);
+ if (!fiber) {
+ return false;
}
+ return isFiberMountedImpl(fiber) === MOUNTED;
}
-function stopWorkTimer(fiber) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
- return;
- }
- // If we pause, its parent is the fiber to unwind from.
- currentFiber = fiber["return"];
- if (!fiber._debugIsCurrentlyTiming) {
- return;
- }
- fiber._debugIsCurrentlyTiming = false;
- endFiberMark(fiber, null, null);
- }
+function assertIsMounted(fiber) {
+ invariant(
+ isFiberMountedImpl(fiber) === MOUNTED,
+ "Unable to find node on an unmounted component."
+ );
}
-function stopFailedWorkTimer(fiber) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
- return;
- }
- // If we pause, its parent is the fiber to unwind from.
- currentFiber = fiber["return"];
- if (!fiber._debugIsCurrentlyTiming) {
- return;
+function findCurrentFiberUsingSlowPath(fiber) {
+ var alternate = fiber.alternate;
+ if (!alternate) {
+ // If there is no alternate, then we only need to check if it is mounted.
+ var state = isFiberMountedImpl(fiber);
+ invariant(
+ state !== UNMOUNTED,
+ "Unable to find node on an unmounted component."
+ );
+ if (state === MOUNTING) {
+ return null;
}
- fiber._debugIsCurrentlyTiming = false;
- var warning$$1 = "An error was thrown inside this error boundary";
- endFiberMark(fiber, null, warning$$1);
+ return fiber;
}
-}
-
-function startPhaseTimer(fiber, phase) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
- }
- clearPendingPhaseMeasurement();
- if (!beginFiberMark(fiber, phase)) {
- return;
+ // If we have two possible branches, we'll walk backwards up to the root
+ // to see what path the root points to. On the way we may hit one of the
+ // special cases and we'll deal with them.
+ var a = fiber;
+ var b = alternate;
+ while (true) {
+ var parentA = a["return"];
+ var parentB = parentA ? parentA.alternate : null;
+ if (!parentA || !parentB) {
+ // We're at the root.
+ break;
}
- currentPhaseFiber = fiber;
- currentPhase = phase;
- }
-}
-function stopPhaseTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
- }
- if (currentPhase !== null && currentPhaseFiber !== null) {
- var warning$$1 = hasScheduledUpdateInCurrentPhase
- ? "Scheduled a cascading update"
- : null;
- endFiberMark(currentPhaseFiber, currentPhase, warning$$1);
+ // If both copies of the parent fiber point to the same child, we can
+ // assume that the child is current. This happens when we bailout on low
+ // priority: the bailed out fiber's child reuses the current child.
+ if (parentA.child === parentB.child) {
+ var child = parentA.child;
+ while (child) {
+ if (child === a) {
+ // We've determined that A is the current branch.
+ assertIsMounted(parentA);
+ return fiber;
+ }
+ if (child === b) {
+ // We've determined that B is the current branch.
+ assertIsMounted(parentA);
+ return alternate;
+ }
+ child = child.sibling;
+ }
+ // We should never have an alternate for any mounting node. So the only
+ // way this could possibly happen is if this was unmounted, if at all.
+ invariant(false, "Unable to find node on an unmounted component.");
}
- currentPhase = null;
- currentPhaseFiber = null;
- }
-}
-function startWorkLoopTimer(nextUnitOfWork) {
- if (enableUserTimingAPI) {
- currentFiber = nextUnitOfWork;
- if (!supportsUserTiming) {
- return;
+ if (a["return"] !== b["return"]) {
+ // The return pointer of A and the return pointer of B point to different
+ // fibers. We assume that return pointers never criss-cross, so A must
+ // belong to the child set of A.return, and B must belong to the child
+ // set of B.return.
+ a = parentA;
+ b = parentB;
+ } else {
+ // The return pointers point to the same fiber. We'll have to use the
+ // default, slow path: scan the child sets of each parent alternate to see
+ // which child belongs to which set.
+ //
+ // Search parent A's child set
+ var didFindChild = false;
+ var _child = parentA.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentA;
+ b = parentB;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentA;
+ a = parentB;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ if (!didFindChild) {
+ // Search parent B's child set
+ _child = parentB.child;
+ while (_child) {
+ if (_child === a) {
+ didFindChild = true;
+ a = parentB;
+ b = parentA;
+ break;
+ }
+ if (_child === b) {
+ didFindChild = true;
+ b = parentB;
+ a = parentA;
+ break;
+ }
+ _child = _child.sibling;
+ }
+ invariant(
+ didFindChild,
+ "Child was not found in either parent set. This indicates a bug " +
+ "in React related to the return pointer. Please file an issue."
+ );
+ }
}
- commitCountInCurrentWorkLoop = 0;
- // This is top level call.
- // Any other measurements are performed within.
- beginMark("(React Tree Reconciliation)");
- // Resume any measurements that were in progress during the last loop.
- resumeTimers();
+
+ invariant(
+ a.alternate === b,
+ "Return fibers should always be each others' alternates. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
+ // If the root is not a host container, we're in a disconnected tree. I.e.
+ // unmounted.
+ invariant(
+ a.tag === HostRoot,
+ "Unable to find node on an unmounted component."
+ );
+ if (a.stateNode.current === a) {
+ // We've determined that A is the current branch.
+ return fiber;
}
+ // Otherwise B has to be current branch.
+ return alternate;
}
-function stopWorkLoopTimer(interruptedBy) {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
+function findCurrentHostFiber(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
}
- var warning$$1 = null;
- if (interruptedBy !== null) {
- if (interruptedBy.tag === HostRoot) {
- warning$$1 = "A top-level update interrupted the previous render";
- } else {
- var componentName = getComponentName(interruptedBy) || "Unknown";
- warning$$1 =
- "An update to " + componentName + " interrupted the previous render";
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node["return"] || node["return"] === currentParent) {
+ return null;
}
- } else if (commitCountInCurrentWorkLoop > 1) {
- warning$$1 = "There were cascading updates";
+ node = node["return"];
}
- commitCountInCurrentWorkLoop = 0;
- // Pause any measurements until the next loop.
- pauseTimers();
- endMark(
- "(React Tree Reconciliation)",
- "(React Tree Reconciliation)",
- warning$$1
- );
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
}
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
}
-function startCommitTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
+function findCurrentHostFiberWithNoPortals(parent) {
+ var currentParent = findCurrentFiberUsingSlowPath(parent);
+ if (!currentParent) {
+ return null;
+ }
+
+ // Next we'll drill down this component to find the first HostComponent/Text.
+ var node = currentParent;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ return node;
+ } else if (node.child && node.tag !== HostPortal) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
}
- isCommitting = true;
- hasScheduledUpdateInCurrentCommit = false;
- labelsInCurrentCommit.clear();
- beginMark("(Committing Changes)");
+ if (node === currentParent) {
+ return null;
+ }
+ while (!node.sibling) {
+ if (!node["return"] || node["return"] === currentParent) {
+ return null;
+ }
+ node = node["return"];
+ }
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
}
+ // Flow needs the return null here, but ESLint complains about it.
+ // eslint-disable-next-line no-unreachable
+ return null;
}
-function stopCommitTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
- }
+var valueStack = [];
- var warning$$1 = null;
- if (hasScheduledUpdateInCurrentCommit) {
- warning$$1 = "Lifecycle hook scheduled a cascading update";
- } else if (commitCountInCurrentWorkLoop > 0) {
- warning$$1 = "Caused by a cascading update in earlier commit";
- }
- hasScheduledUpdateInCurrentCommit = false;
- commitCountInCurrentWorkLoop++;
- isCommitting = false;
- labelsInCurrentCommit.clear();
+var fiberStack = void 0;
- endMark("(Committing Changes)", "(Committing Changes)", warning$$1);
- }
+{
+ fiberStack = [];
}
-function startCommitHostEffectsTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
- }
- effectCountInCurrentCommit = 0;
- beginMark("(Committing Host Effects)");
- }
+var index = -1;
+
+function createCursor(defaultValue) {
+ return {
+ current: defaultValue
+ };
}
-function stopCommitHostEffectsTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
+function pop(cursor, fiber) {
+ if (index < 0) {
+ {
+ warning(false, "Unexpected pop.");
}
- var count = effectCountInCurrentCommit;
- effectCountInCurrentCommit = 0;
- endMark(
- "(Committing Host Effects: " + count + " Total)",
- "(Committing Host Effects)",
- null
- );
+ return;
}
-}
-function startCommitLifeCyclesTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
+ {
+ if (fiber !== fiberStack[index]) {
+ warning(false, "Unexpected Fiber popped.");
}
- effectCountInCurrentCommit = 0;
- beginMark("(Calling Lifecycle Methods)");
}
-}
-function stopCommitLifeCyclesTimer() {
- if (enableUserTimingAPI) {
- if (!supportsUserTiming) {
- return;
- }
- var count = effectCountInCurrentCommit;
- effectCountInCurrentCommit = 0;
- endMark(
- "(Calling Lifecycle Methods: " + count + " Total)",
- "(Calling Lifecycle Methods)",
- null
- );
+ cursor.current = valueStack[index];
+
+ valueStack[index] = null;
+
+ {
+ fiberStack[index] = null;
}
-}
-{
- var warnedAboutMissingGetChildContext = {};
+ index--;
}
-// A cursor to the current merged context object on the stack.
-var contextStackCursor = createCursor(emptyObject);
-// A cursor to a boolean indicating whether the context has changed.
-var didPerformWorkStackCursor = createCursor(false);
-// Keep track of the previous context object that was on the stack.
-// We use this to get access to the parent context after we have already
-// pushed the next context provider, and now need to merge their contexts.
-var previousContext = emptyObject;
+function push(cursor, value, fiber) {
+ index++;
-function getUnmaskedContext(workInProgress) {
- var hasOwnContext = isContextProvider(workInProgress);
- if (hasOwnContext) {
- // If the fiber is a context provider itself, when we read its context
- // we have already pushed its own child context on the stack. A context
- // provider should not "see" its own child context. Therefore we read the
- // previous (parent) context instead for a context provider.
- return previousContext;
+ valueStack[index] = cursor.current;
+
+ {
+ fiberStack[index] = fiber;
}
- return contextStackCursor.current;
-}
-function cacheContext(workInProgress, unmaskedContext, maskedContext) {
- var instance = workInProgress.stateNode;
- instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
- instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
+ cursor.current = value;
}
-function getMaskedContext(workInProgress, unmaskedContext) {
- var type = workInProgress.type;
- var contextTypes = type.contextTypes;
- if (!contextTypes) {
- return emptyObject;
- }
+function reset() {
+ while (index > -1) {
+ valueStack[index] = null;
- // Avoid recreating masked context unless unmasked context has changed.
- // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
- // This may trigger infinite loops if componentWillReceiveProps calls setState.
- var instance = workInProgress.stateNode;
- if (
- instance &&
- instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
- ) {
- return instance.__reactInternalMemoizedMaskedChildContext;
- }
+ {
+ fiberStack[index] = null;
+ }
- var context = {};
- for (var key in contextTypes) {
- context[key] = unmaskedContext[key];
+ index--;
}
+}
- {
- var name = getComponentName(workInProgress) || "Unknown";
- checkPropTypes(
- contextTypes,
- context,
- "context",
- name,
- ReactDebugCurrentFiber.getCurrentFiberStackAddendum
- );
- }
+var describeComponentFrame = function(name, source, ownerName) {
+ return (
+ "\n in " +
+ (name || "Unknown") +
+ (source
+ ? " (at " +
+ source.fileName.replace(/^.*[\\\/]/, "") +
+ ":" +
+ source.lineNumber +
+ ")"
+ : ownerName ? " (created by " + ownerName + ")" : "")
+ );
+};
- // 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);
+function describeFiber(fiber) {
+ switch (fiber.tag) {
+ case IndeterminateComponent:
+ case FunctionalComponent:
+ case ClassComponent:
+ case HostComponent:
+ var owner = fiber._debugOwner;
+ var source = fiber._debugSource;
+ var name = getComponentName(fiber);
+ var ownerName = null;
+ if (owner) {
+ ownerName = getComponentName(owner);
+ }
+ return describeComponentFrame(name, source, ownerName);
+ default:
+ return "";
}
-
- return context;
-}
-
-function hasContextChanged() {
- return didPerformWorkStackCursor.current;
}
-function isContextConsumer(fiber) {
- return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
+// This function can only be called with a work-in-progress fiber and
+// only during begin or complete phase. Do not call it under any other
+// circumstances.
+function getStackAddendumByWorkInProgressFiber(workInProgress) {
+ var info = "";
+ var node = workInProgress;
+ do {
+ info += describeFiber(node);
+ // Otherwise this return pointer might point to the wrong tree:
+ node = node["return"];
+ } while (node);
+ return info;
}
-function isContextProvider(fiber) {
- return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
+function getCurrentFiberOwnerName() {
+ {
+ var fiber = ReactDebugCurrentFiber.current;
+ if (fiber === null) {
+ return null;
+ }
+ var owner = fiber._debugOwner;
+ if (owner !== null && typeof owner !== "undefined") {
+ return getComponentName(owner);
+ }
+ }
+ return null;
}
-function popContextProvider(fiber) {
- if (!isContextProvider(fiber)) {
- return;
+function getCurrentFiberStackAddendum() {
+ {
+ var fiber = ReactDebugCurrentFiber.current;
+ if (fiber === null) {
+ return null;
+ }
+ // Safe because if current fiber exists, we are reconciling,
+ // and it is guaranteed to be the work-in-progress version.
+ return getStackAddendumByWorkInProgressFiber(fiber);
}
-
- pop(didPerformWorkStackCursor, fiber);
- pop(contextStackCursor, fiber);
+ return null;
}
-function popTopLevelContextObject(fiber) {
- pop(didPerformWorkStackCursor, fiber);
- pop(contextStackCursor, fiber);
+function resetCurrentFiber() {
+ ReactDebugCurrentFrame.getCurrentStack = null;
+ ReactDebugCurrentFiber.current = null;
+ ReactDebugCurrentFiber.phase = null;
}
-function pushTopLevelContextObject(fiber, context, didChange) {
- invariant(
- contextStackCursor.cursor == null,
- "Unexpected context found on stack. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
-
- push(contextStackCursor, context, fiber);
- push(didPerformWorkStackCursor, didChange, fiber);
+function setCurrentFiber(fiber) {
+ ReactDebugCurrentFrame.getCurrentStack = getCurrentFiberStackAddendum;
+ ReactDebugCurrentFiber.current = fiber;
+ ReactDebugCurrentFiber.phase = null;
}
-function processChildContext(fiber, parentContext) {
- var instance = fiber.stateNode;
- var childContextTypes = fiber.type.childContextTypes;
+function setCurrentPhase(phase) {
+ ReactDebugCurrentFiber.phase = phase;
+}
- // TODO (bvaughn) Replace this behavior with an invariant() in the future.
- // It has only been added in Fiber to match the (unintentional) behavior in Stack.
- if (typeof instance.getChildContext !== "function") {
- {
- var componentName = getComponentName(fiber) || "Unknown";
+var ReactDebugCurrentFiber = {
+ current: null,
+ phase: null,
+ resetCurrentFiber: resetCurrentFiber,
+ setCurrentFiber: setCurrentFiber,
+ setCurrentPhase: setCurrentPhase,
+ getCurrentFiberOwnerName: getCurrentFiberOwnerName,
+ getCurrentFiberStackAddendum: getCurrentFiberStackAddendum
+};
- if (!warnedAboutMissingGetChildContext[componentName]) {
- warnedAboutMissingGetChildContext[componentName] = true;
- warning(
- false,
- "%s.childContextTypes is specified but there is no getChildContext() method " +
- "on the instance. You can either define getChildContext() on %s or remove " +
- "childContextTypes from it.",
- componentName,
- componentName
- );
- }
- }
- return parentContext;
- }
+// Re-export dynamic flags from the fbsource version.
+var _require = require("ReactFeatureFlags");
- var childContext = void 0;
- {
- ReactDebugCurrentFiber.setCurrentPhase("getChildContext");
- }
- startPhaseTimer(fiber, "getChildContext");
- childContext = instance.getChildContext();
- stopPhaseTimer();
- {
- ReactDebugCurrentFiber.setCurrentPhase(null);
- }
- for (var contextKey in childContext) {
- invariant(
- contextKey in childContextTypes,
- '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
- getComponentName(fiber) || "Unknown",
- contextKey
- );
- }
- {
- var name = getComponentName(fiber) || "Unknown";
- checkPropTypes(
- childContextTypes,
- childContext,
- "child context",
- name,
- // In practice, there is one case in which we won't get a stack. It's when
- // somebody calls unstable_renderSubtreeIntoContainer() and we process
- // context from the parent component instance. The stack will be missing
- // because it's outside of the reconciliation, and so the pointer has not
- // been set. This is rare and doesn't matter. We'll also remove that API.
- ReactDebugCurrentFiber.getCurrentFiberStackAddendum
- );
- }
+var debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects;
+var debugRenderPhaseSideEffectsForStrictMode =
+ _require.debugRenderPhaseSideEffectsForStrictMode;
+var warnAboutDeprecatedLifecycles = _require.warnAboutDeprecatedLifecycles;
- return Object.assign({}, parentContext, childContext);
-}
+var enableAsyncSubtreeAPI = true;
-function pushContextProvider(workInProgress) {
- if (!isContextProvider(workInProgress)) {
- return false;
- }
+var enableUserTimingAPI = true;
+var enableMutatingReconciler = true;
+var enableNoopReconciler = false;
+var enablePersistentReconciler = false;
+var enableNewContextAPI = false;
- var instance = workInProgress.stateNode;
- // We push the context as early as possible to ensure stack integrity.
- // If the instance does not exist yet, we will push null at first,
- // and replace it on the stack later when invalidating the context.
- var memoizedMergedChildContext =
- (instance && instance.__reactInternalMemoizedMergedChildContext) ||
- emptyObject;
+// Only used in www builds.
- // Remember the parent context so we can merge with it later.
- // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
- previousContext = contextStackCursor.current;
- push(contextStackCursor, memoizedMergedChildContext, workInProgress);
- push(
- didPerformWorkStackCursor,
- didPerformWorkStackCursor.current,
- workInProgress
- );
+// Prefix measurements so that it's possible to filter them.
+// Longer prefixes are hard to read in DevTools.
+var reactEmoji = "\u269B";
+var warningEmoji = "\u26D4";
+var supportsUserTiming =
+ typeof performance !== "undefined" &&
+ typeof performance.mark === "function" &&
+ typeof performance.clearMarks === "function" &&
+ typeof performance.measure === "function" &&
+ typeof performance.clearMeasures === "function";
- return true;
-}
+// Keep track of current fiber so that we know the path to unwind on pause.
+// TODO: this looks the same as nextUnitOfWork in scheduler. Can we unify them?
+var currentFiber = null;
+// If we're in the middle of user code, which fiber and method is it?
+// Reusing `currentFiber` would be confusing for this because user code fiber
+// can change during commit phase too, but we don't need to unwind it (since
+// lifecycles in the commit phase don't resemble a tree).
+var currentPhase = null;
+var currentPhaseFiber = null;
+// Did lifecycle hook schedule an update? This is often a performance problem,
+// so we will keep track of it, and include it in the report.
+// Track commits caused by cascading updates.
+var isCommitting = false;
+var hasScheduledUpdateInCurrentCommit = false;
+var hasScheduledUpdateInCurrentPhase = false;
+var commitCountInCurrentWorkLoop = 0;
+var effectCountInCurrentCommit = 0;
+var isWaitingForCallback = false;
+// During commits, we only show a measurement once per method name
+// to avoid stretch the commit phase with measurement overhead.
+var labelsInCurrentCommit = new Set();
-function invalidateContextProvider(workInProgress, didChange) {
- var instance = workInProgress.stateNode;
- invariant(
- instance,
- "Expected to have an instance by this point. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
+var formatMarkName = function(markName) {
+ return reactEmoji + " " + markName;
+};
- if (didChange) {
- // Merge parent and own context.
- // Skip this if we're not updating due to sCU.
- // This avoids unnecessarily recomputing memoized values.
- var mergedContext = processChildContext(workInProgress, previousContext);
- instance.__reactInternalMemoizedMergedChildContext = mergedContext;
+var formatLabel = function(label, warning$$1) {
+ var prefix = warning$$1 ? warningEmoji + " " : reactEmoji + " ";
+ var suffix = warning$$1 ? " Warning: " + warning$$1 : "";
+ return "" + prefix + label + suffix;
+};
- // Replace the old (or empty) context with the new one.
- // It is important to unwind the context in the reverse order.
- pop(didPerformWorkStackCursor, workInProgress);
- pop(contextStackCursor, workInProgress);
- // Now push the new context and mark that it has changed.
- push(contextStackCursor, mergedContext, workInProgress);
- push(didPerformWorkStackCursor, didChange, workInProgress);
- } else {
- pop(didPerformWorkStackCursor, workInProgress);
- push(didPerformWorkStackCursor, didChange, workInProgress);
- }
-}
+var beginMark = function(markName) {
+ performance.mark(formatMarkName(markName));
+};
-function resetContext() {
- previousContext = emptyObject;
- contextStackCursor.current = emptyObject;
- didPerformWorkStackCursor.current = false;
-}
+var clearMark = function(markName) {
+ performance.clearMarks(formatMarkName(markName));
+};
-function findCurrentUnmaskedContext(fiber) {
- // Currently this is only used with renderSubtreeIntoContainer; not sure if it
- // makes sense elsewhere
- invariant(
- isFiberMounted(fiber) && fiber.tag === ClassComponent,
- "Expected subtree parent to be a mounted class component. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
+var endMark = function(label, markName, warning$$1) {
+ var formattedMarkName = formatMarkName(markName);
+ var formattedLabel = formatLabel(label, warning$$1);
+ try {
+ performance.measure(formattedLabel, formattedMarkName);
+ } catch (err) {}
+ // If previous mark was missing for some reason, this will throw.
+ // This could only happen if React crashed in an unexpected place earlier.
+ // Don't pile on with more errors.
- var node = fiber;
- while (node.tag !== HostRoot) {
- if (isContextProvider(node)) {
- return node.stateNode.__reactInternalMemoizedMergedChildContext;
- }
- var parent = node["return"];
- invariant(
- parent,
- "Found unexpected detached subtree parent. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- node = parent;
+ // Clear marks immediately to avoid growing buffer.
+ performance.clearMarks(formattedMarkName);
+ performance.clearMeasures(formattedLabel);
+};
+
+var getFiberMarkName = function(label, debugID) {
+ return label + " (#" + debugID + ")";
+};
+
+var getFiberLabel = function(componentName, isMounted, phase) {
+ if (phase === null) {
+ // These are composite component total time measurements.
+ return componentName + " [" + (isMounted ? "update" : "mount") + "]";
+ } else {
+ // Composite component methods.
+ return componentName + "." + phase;
}
- return node.stateNode.context;
-}
+};
-var NoWork = 0; // TODO: Use an opaque type once ESLint et al support the syntax
+var beginFiberMark = function(fiber, phase) {
+ var componentName = getComponentName(fiber) || "Unknown";
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
-var Sync = 1;
-var Never = 2147483647; // Max int32: Math.pow(2, 31) - 1
+ if (isCommitting && labelsInCurrentCommit.has(label)) {
+ // During the commit phase, we don't show duplicate labels because
+ // there is a fixed overhead for every measurement, and we don't
+ // want to stretch the commit phase beyond necessary.
+ return false;
+ }
+ labelsInCurrentCommit.add(label);
-var UNIT_SIZE = 10;
-var MAGIC_NUMBER_OFFSET = 2;
+ var markName = getFiberMarkName(label, debugID);
+ beginMark(markName);
+ return true;
+};
-// 1 unit of expiration time represents 10ms.
-function msToExpirationTime(ms) {
- // Always add an offset so that we don't clash with the magic number for NoWork.
- return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET;
-}
+var clearFiberMark = function(fiber, phase) {
+ var componentName = getComponentName(fiber) || "Unknown";
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ clearMark(markName);
+};
-function expirationTimeToMs(expirationTime) {
- return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE;
-}
+var endFiberMark = function(fiber, phase, warning$$1) {
+ var componentName = getComponentName(fiber) || "Unknown";
+ var debugID = fiber._debugID;
+ var isMounted = fiber.alternate !== null;
+ var label = getFiberLabel(componentName, isMounted, phase);
+ var markName = getFiberMarkName(label, debugID);
+ endMark(label, markName, warning$$1);
+};
-function ceiling(num, precision) {
- return (((num / precision) | 0) + 1) * precision;
-}
+var shouldIgnoreFiber = function(fiber) {
+ // Host components should be skipped in the timeline.
+ // We could check typeof fiber.type, but does this work with RN?
+ switch (fiber.tag) {
+ case HostRoot:
+ case HostComponent:
+ case HostText:
+ case HostPortal:
+ case CallComponent:
+ case ReturnComponent:
+ case Fragment:
+ return true;
+ default:
+ return false;
+ }
+};
-function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
- return ceiling(
- currentTime + expirationInMs / UNIT_SIZE,
- bucketSizeMs / UNIT_SIZE
- );
-}
+var clearPendingPhaseMeasurement = function() {
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ clearFiberMark(currentPhaseFiber, currentPhase);
+ }
+ currentPhaseFiber = null;
+ currentPhase = null;
+ hasScheduledUpdateInCurrentPhase = false;
+};
-var NoContext = 0;
-var AsyncUpdates = 1;
+var pauseTimers = function() {
+ // Stops all currently active measurements so that they can be resumed
+ // if we continue in a later deferred loop from the same unit of work.
+ var fiber = currentFiber;
+ while (fiber) {
+ if (fiber._debugIsCurrentlyTiming) {
+ endFiberMark(fiber, null, null);
+ }
+ fiber = fiber["return"];
+ }
+};
-{
- var 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;
+var resumeTimersRecursively = function(fiber) {
+ if (fiber["return"] !== null) {
+ resumeTimersRecursively(fiber["return"]);
}
-}
+ if (fiber._debugIsCurrentlyTiming) {
+ beginFiberMark(fiber, null);
+ }
+};
-// A Fiber is work on a Component that needs to be done or was done. There can
-// be more than one per component.
+var resumeTimers = function() {
+ // Resumes all measurements that were active during the last deferred loop.
+ if (currentFiber !== null) {
+ resumeTimersRecursively(currentFiber);
+ }
+};
-{
- var debugCounter = 1;
+function recordEffect() {
+ if (enableUserTimingAPI) {
+ effectCountInCurrentCommit++;
+ }
}
-function FiberNode(tag, pendingProps, key, internalContextTag) {
- // Instance
- this.tag = tag;
- this.key = key;
- this.type = null;
- this.stateNode = null;
-
- // Fiber
- this["return"] = null;
- this.child = null;
- this.sibling = null;
- this.index = 0;
-
- this.ref = null;
-
- this.pendingProps = pendingProps;
- this.memoizedProps = null;
- this.updateQueue = null;
- this.memoizedState = null;
-
- this.internalContextTag = internalContextTag;
-
- // Effects
- this.effectTag = NoEffect;
- this.nextEffect = null;
-
- this.firstEffect = null;
- this.lastEffect = null;
-
- this.expirationTime = NoWork;
-
- this.alternate = null;
-
- {
- this._debugID = debugCounter++;
- this._debugSource = null;
- this._debugOwner = null;
- this._debugIsCurrentlyTiming = false;
- if (!hasBadMapPolyfill && typeof Object.preventExtensions === "function") {
- Object.preventExtensions(this);
+function recordScheduleUpdate() {
+ if (enableUserTimingAPI) {
+ if (isCommitting) {
+ hasScheduledUpdateInCurrentCommit = true;
+ }
+ if (
+ currentPhase !== null &&
+ currentPhase !== "componentWillMount" &&
+ currentPhase !== "componentWillReceiveProps"
+ ) {
+ hasScheduledUpdateInCurrentPhase = true;
}
}
}
-// This is a constructor function, rather than a POJO constructor, still
-// please ensure we do the following:
-// 1) Nobody should add any instance methods on this. Instance methods can be
-// more difficult to predict when they get optimized and they are almost
-// never inlined properly in static compilers.
-// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
-// always know when it is a fiber.
-// 3) We might want to experiment with using numeric keys since they are easier
-// to optimize in a non-JIT environment.
-// 4) We can easily go from a constructor to a createFiber object literal if that
-// is faster.
-// 5) It should be easy to port this to a C struct and keep a C implementation
-// compatible.
-var createFiber = function(tag, pendingProps, key, internalContextTag) {
- // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
- return new FiberNode(tag, pendingProps, key, internalContextTag);
-};
-
-function shouldConstruct(Component) {
- return !!(Component.prototype && Component.prototype.isReactComponent);
+function startRequestCallbackTimer() {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming && !isWaitingForCallback) {
+ isWaitingForCallback = true;
+ beginMark("(Waiting for async callback...)");
+ }
+ }
}
-// This is used to create an alternate fiber to do work on.
-function createWorkInProgress(current, pendingProps, expirationTime) {
- var workInProgress = current.alternate;
- if (workInProgress === null) {
- // We use a double buffering pooling technique because we know that we'll
- // only ever need at most two versions of a tree. We pool the "other" unused
- // node that we're free to reuse. This is lazily created to avoid allocating
- // extra objects for things that are never updated. It also allow us to
- // reclaim the extra memory if needed.
- workInProgress = createFiber(
- current.tag,
- pendingProps,
- current.key,
- current.internalContextTag
- );
- workInProgress.type = current.type;
- workInProgress.stateNode = current.stateNode;
-
- {
- // DEV-only fields
- workInProgress._debugID = current._debugID;
- workInProgress._debugSource = current._debugSource;
- workInProgress._debugOwner = current._debugOwner;
+function stopRequestCallbackTimer(didExpire) {
+ if (enableUserTimingAPI) {
+ if (supportsUserTiming) {
+ isWaitingForCallback = false;
+ var warning$$1 = didExpire ? "React was blocked by main thread" : null;
+ endMark(
+ "(Waiting for async callback...)",
+ "(Waiting for async callback...)",
+ warning$$1
+ );
}
-
- workInProgress.alternate = current;
- current.alternate = workInProgress;
- } else {
- workInProgress.pendingProps = pendingProps;
-
- // We already have an alternate.
- // Reset the effect tag.
- workInProgress.effectTag = NoEffect;
-
- // The effect list is no longer valid.
- workInProgress.nextEffect = null;
- workInProgress.firstEffect = null;
- workInProgress.lastEffect = null;
}
-
- workInProgress.expirationTime = expirationTime;
-
- workInProgress.child = current.child;
- workInProgress.memoizedProps = current.memoizedProps;
- workInProgress.memoizedState = current.memoizedState;
- workInProgress.updateQueue = current.updateQueue;
-
- // These will be overridden during the parent's reconciliation
- workInProgress.sibling = current.sibling;
- workInProgress.index = current.index;
- workInProgress.ref = current.ref;
-
- return workInProgress;
}
-function createHostRootFiber() {
- var fiber = createFiber(HostRoot, null, NoContext);
- return fiber;
+function startWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, this is the fiber to unwind from.
+ currentFiber = fiber;
+ if (!beginFiberMark(fiber, null)) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = true;
+ }
}
-function createFiberFromElement(element, internalContextTag, expirationTime) {
- var owner = null;
- {
- owner = element._owner;
+function cancelWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // Remember we shouldn't complete measurement for this fiber.
+ // Otherwise flamechart will be deep even for small updates.
+ fiber._debugIsCurrentlyTiming = false;
+ clearFiberMark(fiber, null);
}
+}
- var fiber = void 0;
- var type = element.type;
- var key = element.key;
- var pendingProps = element.props;
- if (typeof type === "function") {
- fiber = shouldConstruct(type)
- ? createFiber(ClassComponent, pendingProps, key, internalContextTag)
- : createFiber(
- IndeterminateComponent,
- pendingProps,
- key,
- internalContextTag
- );
- fiber.type = type;
- } else if (typeof type === "string") {
- fiber = createFiber(HostComponent, pendingProps, key, internalContextTag);
- fiber.type = type;
- } else if (
- typeof type === "object" &&
- type !== null &&
- typeof type.tag === "number"
- ) {
- // Currently assumed to be a continuation and therefore is a fiber already.
- // TODO: The yield system is currently broken for updates in some cases.
- // The reified yield stores a fiber, but we don't know which fiber that is;
- // the current or a workInProgress? When the continuation gets rendered here
- // we don't know if we can reuse that fiber or if we need to clone it.
- // There is probably a clever way to restructure this.
- fiber = type;
- fiber.pendingProps = pendingProps;
- } else {
- var info = "";
- {
- if (
- type === undefined ||
- (typeof type === "object" &&
- type !== null &&
- Object.keys(type).length === 0)
- ) {
- info +=
- " You likely forgot to export your component from the file " +
- "it's defined in, or you might have mixed up default and named imports.";
- }
- var ownerName = owner ? getComponentName(owner) : null;
- if (ownerName) {
- info += "\n\nCheck the render method of `" + ownerName + "`.";
- }
+function stopWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
}
- invariant(
- false,
- "Element type is invalid: expected a string (for built-in components) " +
- "or a class/function (for composite components) but got: %s.%s",
- type == null ? type : typeof type,
- info
- );
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber["return"];
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ endFiberMark(fiber, null, null);
}
+}
- {
- fiber._debugSource = element._source;
- fiber._debugOwner = element._owner;
+function stopFailedWorkTimer(fiber) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming || shouldIgnoreFiber(fiber)) {
+ return;
+ }
+ // If we pause, its parent is the fiber to unwind from.
+ currentFiber = fiber["return"];
+ if (!fiber._debugIsCurrentlyTiming) {
+ return;
+ }
+ fiber._debugIsCurrentlyTiming = false;
+ var warning$$1 = "An error was thrown inside this error boundary";
+ endFiberMark(fiber, null, warning$$1);
}
+}
- fiber.expirationTime = expirationTime;
-
- return fiber;
+function startPhaseTimer(fiber, phase) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ clearPendingPhaseMeasurement();
+ if (!beginFiberMark(fiber, phase)) {
+ return;
+ }
+ currentPhaseFiber = fiber;
+ currentPhase = phase;
+ }
}
-function createFiberFromFragment(
- elements,
- internalContextTag,
- expirationTime,
- key
-) {
- var fiber = createFiber(Fragment, elements, key, internalContextTag);
- fiber.expirationTime = expirationTime;
- return fiber;
+function stopPhaseTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ if (currentPhase !== null && currentPhaseFiber !== null) {
+ var warning$$1 = hasScheduledUpdateInCurrentPhase
+ ? "Scheduled a cascading update"
+ : null;
+ endFiberMark(currentPhaseFiber, currentPhase, warning$$1);
+ }
+ currentPhase = null;
+ currentPhaseFiber = null;
+ }
}
-function createFiberFromText(content, internalContextTag, expirationTime) {
- var fiber = createFiber(HostText, content, null, internalContextTag);
- fiber.expirationTime = expirationTime;
- return fiber;
+function startWorkLoopTimer(nextUnitOfWork) {
+ if (enableUserTimingAPI) {
+ currentFiber = nextUnitOfWork;
+ if (!supportsUserTiming) {
+ return;
+ }
+ commitCountInCurrentWorkLoop = 0;
+ // This is top level call.
+ // Any other measurements are performed within.
+ beginMark("(React Tree Reconciliation)");
+ // Resume any measurements that were in progress during the last loop.
+ resumeTimers();
+ }
}
-function createFiberFromHostInstanceForDeletion() {
- var fiber = createFiber(HostComponent, null, null, NoContext);
- fiber.type = "DELETED";
- return fiber;
-}
-
-function createFiberFromCall(call, internalContextTag, expirationTime) {
- var fiber = createFiber(CallComponent, call, call.key, internalContextTag);
- fiber.type = call.handler;
- fiber.expirationTime = expirationTime;
- return fiber;
+function stopWorkLoopTimer(interruptedBy) {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var warning$$1 = null;
+ if (interruptedBy !== null) {
+ if (interruptedBy.tag === HostRoot) {
+ warning$$1 = "A top-level update interrupted the previous render";
+ } else {
+ var componentName = getComponentName(interruptedBy) || "Unknown";
+ warning$$1 =
+ "An update to " + componentName + " interrupted the previous render";
+ }
+ } else if (commitCountInCurrentWorkLoop > 1) {
+ warning$$1 = "There were cascading updates";
+ }
+ commitCountInCurrentWorkLoop = 0;
+ // Pause any measurements until the next loop.
+ pauseTimers();
+ endMark(
+ "(React Tree Reconciliation)",
+ "(React Tree Reconciliation)",
+ warning$$1
+ );
+ }
}
-function createFiberFromReturn(returnNode, internalContextTag, expirationTime) {
- var fiber = createFiber(ReturnComponent, null, null, internalContextTag);
- fiber.expirationTime = expirationTime;
- return fiber;
+function startCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ isCommitting = true;
+ hasScheduledUpdateInCurrentCommit = false;
+ labelsInCurrentCommit.clear();
+ beginMark("(Committing Changes)");
+ }
}
-function createFiberFromPortal(portal, internalContextTag, expirationTime) {
- var pendingProps = portal.children !== null ? portal.children : [];
- var fiber = createFiber(
- HostPortal,
- pendingProps,
- portal.key,
- internalContextTag
- );
- fiber.expirationTime = expirationTime;
- fiber.stateNode = {
- containerInfo: portal.containerInfo,
- pendingChildren: null, // Used by persistent updates
- implementation: portal.implementation
- };
- return fiber;
-}
+function stopCommitTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
-// TODO: This should be lifted into the renderer.
+ var warning$$1 = null;
+ if (hasScheduledUpdateInCurrentCommit) {
+ warning$$1 = "Lifecycle hook scheduled a cascading update";
+ } else if (commitCountInCurrentWorkLoop > 0) {
+ warning$$1 = "Caused by a cascading update in earlier commit";
+ }
+ hasScheduledUpdateInCurrentCommit = false;
+ commitCountInCurrentWorkLoop++;
+ isCommitting = false;
+ labelsInCurrentCommit.clear();
-function createFiberRoot(containerInfo, hydrate) {
- // Cyclic construction. This cheats the type system right now because
- // stateNode is any.
- var uninitializedFiber = createHostRootFiber();
- var root = {
- current: uninitializedFiber,
- containerInfo: containerInfo,
- pendingChildren: null,
- remainingExpirationTime: NoWork,
- isReadyForCommit: false,
- finishedWork: null,
- context: null,
- pendingContext: null,
- hydrate: hydrate,
- firstBatch: null,
- nextScheduledRoot: null
- };
- uninitializedFiber.stateNode = root;
- return root;
+ endMark("(Committing Changes)", "(Committing Changes)", warning$$1);
+ }
}
-var onCommitFiberRoot = null;
-var onCommitFiberUnmount = null;
-var hasLoggedError = false;
-
-function catchErrors(fn) {
- return function(arg) {
- try {
- return fn(arg);
- } catch (err) {
- if (true && !hasLoggedError) {
- hasLoggedError = true;
- warning(false, "React DevTools encountered an error: %s", err);
- }
+function startCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
}
- };
+ effectCountInCurrentCommit = 0;
+ beginMark("(Committing Host Effects)");
+ }
}
-function injectInternals(internals) {
- if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined") {
- // No DevTools
- return false;
- }
- var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
- if (hook.isDisabled) {
- // This isn't a real property on the hook, but it can be set to opt out
- // of DevTools integration and associated warnings and logs.
- // https://github.com/facebook/react/issues/3877
- return true;
- }
- if (!hook.supportsFiber) {
- {
- warning(
- false,
- "The installed version of React DevTools is too old and will not work " +
- "with the current version of React. Please update React DevTools. " +
- "https://fb.me/react-devtools"
- );
- }
- // DevTools exists, even though it doesn't support Fiber.
- return true;
- }
- try {
- var rendererID = hook.inject(internals);
- // We have successfully injected, so now it is safe to set up hooks.
- onCommitFiberRoot = catchErrors(function(root) {
- return hook.onCommitFiberRoot(rendererID, root);
- });
- onCommitFiberUnmount = catchErrors(function(fiber) {
- return hook.onCommitFiberUnmount(rendererID, fiber);
- });
- } catch (err) {
- // Catch all errors because it is unsafe to throw during initialization.
- {
- warning(false, "React DevTools encountered an error: %s.", err);
+function stopCommitHostEffectsTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
}
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark(
+ "(Committing Host Effects: " + count + " Total)",
+ "(Committing Host Effects)",
+ null
+ );
}
- // DevTools exists
- return true;
}
-function onCommitRoot(root) {
- if (typeof onCommitFiberRoot === "function") {
- onCommitFiberRoot(root);
+function startCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ effectCountInCurrentCommit = 0;
+ beginMark("(Calling Lifecycle Methods)");
}
}
-function onCommitUnmount(fiber) {
- if (typeof onCommitFiberUnmount === "function") {
- onCommitFiberUnmount(fiber);
+function stopCommitLifeCyclesTimer() {
+ if (enableUserTimingAPI) {
+ if (!supportsUserTiming) {
+ return;
+ }
+ var count = effectCountInCurrentCommit;
+ effectCountInCurrentCommit = 0;
+ endMark(
+ "(Calling Lifecycle Methods: " + count + " Total)",
+ "(Calling Lifecycle Methods)",
+ null
+ );
}
}
+var warnedAboutMissingGetChildContext = void 0;
+
{
- var didWarnUpdateInsideUpdate = false;
+ warnedAboutMissingGetChildContext = {};
}
-// Callbacks are not validated until invocation
-
-// Singly linked-list of updates. When an update is scheduled, it is added to
-// the queue of the current fiber and the work-in-progress fiber. The two queues
-// are separate but they share a persistent structure.
-//
-// During reconciliation, updates are removed from the work-in-progress fiber,
-// but they remain on the current fiber. That ensures that if a work-in-progress
-// is aborted, the aborted updates are recovered by cloning from current.
-//
-// The work-in-progress queue is always a subset of the current queue.
-//
-// When the tree is committed, the work-in-progress becomes the current.
+// A cursor to the current merged context object on the stack.
+var contextStackCursor = createCursor(emptyObject);
+// A cursor to a boolean indicating whether the context has changed.
+var didPerformWorkStackCursor = createCursor(false);
+// Keep track of the previous context object that was on the stack.
+// We use this to get access to the parent context after we have already
+// pushed the next context provider, and now need to merge their contexts.
+var previousContext = emptyObject;
-function createUpdateQueue(baseState) {
- var queue = {
- baseState: baseState,
- expirationTime: NoWork,
- first: null,
- last: null,
- callbackList: null,
- hasForceUpdate: false,
- isInitialized: false
- };
- {
- queue.isProcessing = false;
+function getUnmaskedContext(workInProgress) {
+ var hasOwnContext = isContextProvider(workInProgress);
+ if (hasOwnContext) {
+ // If the fiber is a context provider itself, when we read its context
+ // we have already pushed its own child context on the stack. A context
+ // provider should not "see" its own child context. Therefore we read the
+ // previous (parent) context instead for a context provider.
+ return previousContext;
}
- return queue;
+ return contextStackCursor.current;
}
-function insertUpdateIntoQueue(queue, update) {
- // Append the update to the end of the list.
- if (queue.last === null) {
- // Queue is empty
- queue.first = queue.last = update;
- } else {
- queue.last.next = update;
- queue.last = update;
- }
- if (
- queue.expirationTime === NoWork ||
- queue.expirationTime > update.expirationTime
- ) {
- queue.expirationTime = update.expirationTime;
- }
+function cacheContext(workInProgress, unmaskedContext, maskedContext) {
+ var instance = workInProgress.stateNode;
+ instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
+ instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
}
-function insertUpdateIntoFiber(fiber, update) {
- // We'll have at least one and at most two distinct update queues.
- var alternateFiber = fiber.alternate;
- var queue1 = fiber.updateQueue;
- if (queue1 === null) {
- // TODO: We don't know what the base state will be until we begin work.
- // It depends on which fiber is the next current. Initialize with an empty
- // base state, then set to the memoizedState when rendering. Not super
- // happy with this approach.
- queue1 = fiber.updateQueue = createUpdateQueue(null);
+function getMaskedContext(workInProgress, unmaskedContext) {
+ var type = workInProgress.type;
+ var contextTypes = type.contextTypes;
+ if (!contextTypes) {
+ return emptyObject;
}
- var queue2 = void 0;
- if (alternateFiber !== null) {
- queue2 = alternateFiber.updateQueue;
- if (queue2 === null) {
- queue2 = alternateFiber.updateQueue = createUpdateQueue(null);
- }
- } else {
- queue2 = null;
+ // Avoid recreating masked context unless unmasked context has changed.
+ // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
+ // This may trigger infinite loops if componentWillReceiveProps calls setState.
+ var instance = workInProgress.stateNode;
+ if (
+ instance &&
+ instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext
+ ) {
+ return instance.__reactInternalMemoizedMaskedChildContext;
}
- queue2 = queue2 !== queue1 ? queue2 : null;
- // Warn if an update is scheduled from inside an updater function.
- {
- if (
- (queue1.isProcessing || (queue2 !== null && queue2.isProcessing)) &&
- !didWarnUpdateInsideUpdate
- ) {
- warning(
- false,
- "An update (setState, replaceState, or forceUpdate) was scheduled " +
- "from inside an update function. Update functions should be pure, " +
- "with zero side-effects. Consider using componentDidUpdate or a " +
- "callback."
- );
- didWarnUpdateInsideUpdate = true;
- }
+ var context = {};
+ for (var key in contextTypes) {
+ context[key] = unmaskedContext[key];
}
- // If there's only one queue, add the update to that queue and exit.
- if (queue2 === null) {
- insertUpdateIntoQueue(queue1, update);
- return;
+ {
+ var name = getComponentName(workInProgress) || "Unknown";
+ checkPropTypes(
+ contextTypes,
+ context,
+ "context",
+ name,
+ ReactDebugCurrentFiber.getCurrentFiberStackAddendum
+ );
}
- // If either queue is empty, we need to add to both queues.
- if (queue1.last === null || queue2.last === null) {
- insertUpdateIntoQueue(queue1, update);
- insertUpdateIntoQueue(queue2, update);
- return;
+ // 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);
}
- // If both lists are not empty, the last update is the same for both lists
- // because of structural sharing. So, we should only append to one of
- // the lists.
- insertUpdateIntoQueue(queue1, update);
- // But we still need to update the `last` pointer of queue2.
- queue2.last = update;
+ return context;
}
-function getUpdateExpirationTime(fiber) {
- if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) {
- return NoWork;
- }
- var updateQueue = fiber.updateQueue;
- if (updateQueue === null) {
- return NoWork;
- }
- return updateQueue.expirationTime;
+function hasContextChanged() {
+ return didPerformWorkStackCursor.current;
}
-function getStateFromUpdate(update, instance, prevState, props) {
- var partialState = update.partialState;
- if (typeof partialState === "function") {
- var updateFn = partialState;
-
- // Invoke setState callback an extra time to help detect side-effects.
- if (debugRenderPhaseSideEffects) {
- updateFn.call(instance, prevState, props);
- }
+function isContextConsumer(fiber) {
+ return fiber.tag === ClassComponent && fiber.type.contextTypes != null;
+}
- return updateFn.call(instance, prevState, props);
- } else {
- return partialState;
- }
+function isContextProvider(fiber) {
+ return fiber.tag === ClassComponent && fiber.type.childContextTypes != null;
}
-function processUpdateQueue(
- current,
- workInProgress,
- queue,
- instance,
- props,
- renderExpirationTime
-) {
- if (current !== null && current.updateQueue === queue) {
- // We need to create a work-in-progress queue, by cloning the current queue.
- var currentQueue = queue;
- queue = workInProgress.updateQueue = {
- baseState: currentQueue.baseState,
- expirationTime: currentQueue.expirationTime,
- first: currentQueue.first,
- last: currentQueue.last,
- isInitialized: currentQueue.isInitialized,
- // These fields are no longer valid because they were already committed.
- // Reset them.
- callbackList: null,
- hasForceUpdate: false
- };
+function popContextProvider(fiber) {
+ if (!isContextProvider(fiber)) {
+ return;
}
- {
- // Set this flag so we can warn if setState is called inside the update
- // function of another setState.
- queue.isProcessing = true;
- }
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
- // Reset the remaining expiration time. If we skip over any updates, we'll
- // increase this accordingly.
- queue.expirationTime = NoWork;
+function popTopLevelContextObject(fiber) {
+ pop(didPerformWorkStackCursor, fiber);
+ pop(contextStackCursor, fiber);
+}
- // TODO: We don't know what the base state will be until we begin work.
- // It depends on which fiber is the next current. Initialize with an empty
- // base state, then set to the memoizedState when rendering. Not super
- // happy with this approach.
- var state = void 0;
- if (queue.isInitialized) {
- state = queue.baseState;
- } else {
- state = queue.baseState = workInProgress.memoizedState;
- queue.isInitialized = true;
- }
- var dontMutatePrevState = true;
- var update = queue.first;
- var didSkip = false;
- while (update !== null) {
- var updateExpirationTime = update.expirationTime;
- if (updateExpirationTime > renderExpirationTime) {
- // This update does not have sufficient priority. Skip it.
- var remainingExpirationTime = queue.expirationTime;
- if (
- remainingExpirationTime === NoWork ||
- remainingExpirationTime > updateExpirationTime
- ) {
- // Update the remaining expiration time.
- queue.expirationTime = updateExpirationTime;
- }
- if (!didSkip) {
- didSkip = true;
- queue.baseState = state;
- }
- // Continue to the next update.
- update = update.next;
- continue;
- }
+function pushTopLevelContextObject(fiber, context, didChange) {
+ invariant(
+ contextStackCursor.cursor == null,
+ "Unexpected context found on stack. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
- // This update does have sufficient priority.
+ push(contextStackCursor, context, fiber);
+ push(didPerformWorkStackCursor, didChange, fiber);
+}
- // If no previous updates were skipped, drop this update from the queue by
- // advancing the head of the list.
- if (!didSkip) {
- queue.first = update.next;
- if (queue.first === null) {
- queue.last = null;
- }
- }
+function processChildContext(fiber, parentContext) {
+ var instance = fiber.stateNode;
+ var childContextTypes = fiber.type.childContextTypes;
- // Process the update
- var _partialState = void 0;
- if (update.isReplace) {
- state = getStateFromUpdate(update, instance, state, props);
- dontMutatePrevState = true;
- } else {
- _partialState = getStateFromUpdate(update, instance, state, props);
- if (_partialState) {
- if (dontMutatePrevState) {
- // $FlowFixMe: Idk how to type this properly.
- state = Object.assign({}, state, _partialState);
- } else {
- state = Object.assign(state, _partialState);
- }
- dontMutatePrevState = false;
- }
- }
- if (update.isForced) {
- queue.hasForceUpdate = true;
- }
- if (update.callback !== null) {
- // Append to list of callbacks.
- var _callbackList = queue.callbackList;
- if (_callbackList === null) {
- _callbackList = queue.callbackList = [];
+ // TODO (bvaughn) Replace this behavior with an invariant() in the future.
+ // It has only been added in Fiber to match the (unintentional) behavior in Stack.
+ if (typeof instance.getChildContext !== "function") {
+ {
+ var componentName = getComponentName(fiber) || "Unknown";
+
+ if (!warnedAboutMissingGetChildContext[componentName]) {
+ warnedAboutMissingGetChildContext[componentName] = true;
+ warning(
+ false,
+ "%s.childContextTypes is specified but there is no getChildContext() method " +
+ "on the instance. You can either define getChildContext() on %s or remove " +
+ "childContextTypes from it.",
+ componentName,
+ componentName
+ );
}
- _callbackList.push(update);
}
- update = update.next;
+ return parentContext;
}
- if (queue.callbackList !== null) {
- workInProgress.effectTag |= Callback;
- } else if (queue.first === null && !queue.hasForceUpdate) {
- // The queue is empty. We can reset it.
- workInProgress.updateQueue = null;
+ var childContext = void 0;
+ {
+ ReactDebugCurrentFiber.setCurrentPhase("getChildContext");
}
-
- if (!didSkip) {
- didSkip = true;
- queue.baseState = state;
+ startPhaseTimer(fiber, "getChildContext");
+ childContext = instance.getChildContext();
+ stopPhaseTimer();
+ {
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ }
+ for (var contextKey in childContext) {
+ invariant(
+ contextKey in childContextTypes,
+ '%s.getChildContext(): key "%s" is not defined in childContextTypes.',
+ getComponentName(fiber) || "Unknown",
+ contextKey
+ );
}
-
{
- // No longer processing.
- queue.isProcessing = false;
+ var name = getComponentName(fiber) || "Unknown";
+ checkPropTypes(
+ childContextTypes,
+ childContext,
+ "child context",
+ name,
+ // In practice, there is one case in which we won't get a stack. It's when
+ // somebody calls unstable_renderSubtreeIntoContainer() and we process
+ // context from the parent component instance. The stack will be missing
+ // because it's outside of the reconciliation, and so the pointer has not
+ // been set. This is rare and doesn't matter. We'll also remove that API.
+ ReactDebugCurrentFiber.getCurrentFiberStackAddendum
+ );
}
- return state;
+ return Object.assign({}, parentContext, childContext);
}
-function commitCallbacks(queue, context) {
- var callbackList = queue.callbackList;
- if (callbackList === null) {
- return;
- }
- // Set the list to null to make sure they don't get called more than once.
- queue.callbackList = null;
- for (var i = 0; i < callbackList.length; i++) {
- var update = callbackList[i];
- var _callback = update.callback;
- // This update might be processed again. Clear the callback so it's only
- // called once.
- update.callback = null;
- invariant(
- typeof _callback === "function",
- "Invalid argument passed as callback. Expected a function. Instead " +
- "received: %s",
- _callback
- );
- _callback.call(context);
+function pushContextProvider(workInProgress) {
+ if (!isContextProvider(workInProgress)) {
+ return false;
}
+
+ var instance = workInProgress.stateNode;
+ // We push the context as early as possible to ensure stack integrity.
+ // If the instance does not exist yet, we will push null at first,
+ // and replace it on the stack later when invalidating the context.
+ var memoizedMergedChildContext =
+ (instance && instance.__reactInternalMemoizedMergedChildContext) ||
+ emptyObject;
+
+ // Remember the parent context so we can merge with it later.
+ // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
+ previousContext = contextStackCursor.current;
+ push(contextStackCursor, memoizedMergedChildContext, workInProgress);
+ push(
+ didPerformWorkStackCursor,
+ didPerformWorkStackCursor.current,
+ workInProgress
+ );
+
+ return true;
}
-var fakeInternalInstance = {};
-var isArray = Array.isArray;
+function invalidateContextProvider(workInProgress, didChange) {
+ var instance = workInProgress.stateNode;
+ invariant(
+ instance,
+ "Expected to have an instance by this point. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
-{
- var didWarnAboutStateAssignmentForComponent = {};
+ if (didChange) {
+ // Merge parent and own context.
+ // Skip this if we're not updating due to sCU.
+ // This avoids unnecessarily recomputing memoized values.
+ var mergedContext = processChildContext(workInProgress, previousContext);
+ instance.__reactInternalMemoizedMergedChildContext = mergedContext;
- var warnOnInvalidCallback = function(callback, callerName) {
- warning(
- callback === null || typeof callback === "function",
- "%s(...): Expected the last optional `callback` argument to be a " +
- "function. Instead received: %s.",
- callerName,
- callback
- );
- };
+ // Replace the old (or empty) context with the new one.
+ // It is important to unwind the context in the reverse order.
+ pop(didPerformWorkStackCursor, workInProgress);
+ pop(contextStackCursor, workInProgress);
+ // Now push the new context and mark that it has changed.
+ push(contextStackCursor, mergedContext, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ } else {
+ pop(didPerformWorkStackCursor, workInProgress);
+ push(didPerformWorkStackCursor, didChange, workInProgress);
+ }
+}
- // This is so gross but it's at least non-critical and can be removed if
- // it causes problems. This is meant to give a nicer error message for
- // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
- // ...)) which otherwise throws a "_processChildContext is not a function"
- // exception.
- Object.defineProperty(fakeInternalInstance, "_processChildContext", {
- enumerable: false,
- value: function() {
- invariant(
- false,
- "_processChildContext is not available in React 16+. This likely " +
- "means you have multiple copies of React and are attempting to nest " +
- "a React 15 tree inside a React 16 tree using " +
- "unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
- "to make sure you have only one copy of React (and ideally, switch " +
- "to ReactDOM.createPortal)."
- );
- }
- });
- Object.freeze(fakeInternalInstance);
+function resetContext() {
+ previousContext = emptyObject;
+ contextStackCursor.current = emptyObject;
+ didPerformWorkStackCursor.current = false;
}
-var ReactFiberClassComponent = function(
- scheduleWork,
- computeExpirationForFiber,
- memoizeProps,
- memoizeState
-) {
- // Class component state updater
- var updater = {
- isMounted: isMounted,
- enqueueSetState: function(instance, partialState, callback) {
- var fiber = get(instance);
- callback = callback === undefined ? null : callback;
- {
- warnOnInvalidCallback(callback, "setState");
- }
- var expirationTime = computeExpirationForFiber(fiber);
- var update = {
- expirationTime: expirationTime,
- partialState: partialState,
- callback: callback,
- isReplace: false,
- isForced: false,
- nextCallback: null,
- next: null
- };
- insertUpdateIntoFiber(fiber, update);
- scheduleWork(fiber, expirationTime);
- },
- enqueueReplaceState: function(instance, state, callback) {
- var fiber = get(instance);
- callback = callback === undefined ? null : callback;
- {
- warnOnInvalidCallback(callback, "replaceState");
- }
- var expirationTime = computeExpirationForFiber(fiber);
- var update = {
- expirationTime: expirationTime,
- partialState: state,
- callback: callback,
- isReplace: true,
- isForced: false,
- nextCallback: null,
- next: null
- };
- insertUpdateIntoFiber(fiber, update);
- scheduleWork(fiber, expirationTime);
- },
- enqueueForceUpdate: function(instance, callback) {
- var fiber = get(instance);
- callback = callback === undefined ? null : callback;
- {
- warnOnInvalidCallback(callback, "forceUpdate");
- }
- var expirationTime = computeExpirationForFiber(fiber);
- var update = {
- expirationTime: expirationTime,
- partialState: null,
- callback: callback,
- isReplace: false,
- isForced: true,
- nextCallback: null,
- next: null
- };
- insertUpdateIntoFiber(fiber, update);
- scheduleWork(fiber, expirationTime);
- }
- };
+function findCurrentUnmaskedContext(fiber) {
+ // Currently this is only used with renderSubtreeIntoContainer; not sure if it
+ // makes sense elsewhere
+ invariant(
+ isFiberMounted(fiber) && fiber.tag === ClassComponent,
+ "Expected subtree parent to be a mounted class component. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
- function checkShouldComponentUpdate(
- workInProgress,
- oldProps,
- newProps,
- oldState,
- newState,
- newContext
- ) {
- if (
- oldProps === null ||
- (workInProgress.updateQueue !== null &&
- workInProgress.updateQueue.hasForceUpdate)
- ) {
- // If the workInProgress already has an Update effect, return true
- return true;
+ var node = fiber;
+ while (node.tag !== HostRoot) {
+ if (isContextProvider(node)) {
+ return node.stateNode.__reactInternalMemoizedMergedChildContext;
}
+ var parent = node["return"];
+ invariant(
+ parent,
+ "Found unexpected detached subtree parent. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ node = parent;
+ }
+ return node.stateNode.context;
+}
- var instance = workInProgress.stateNode;
- var type = workInProgress.type;
- if (typeof instance.shouldComponentUpdate === "function") {
- startPhaseTimer(workInProgress, "shouldComponentUpdate");
- var shouldUpdate = instance.shouldComponentUpdate(
- newProps,
- newState,
- newContext
- );
- stopPhaseTimer();
-
- // Simulate an async bailout/interruption by invoking lifecycle twice.
- if (debugRenderPhaseSideEffects) {
- instance.shouldComponentUpdate(newProps, newState, newContext);
- }
+// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
+// Math.pow(2, 30) - 1
+// 0b111111111111111111111111111111
+var MAX_SIGNED_31_BIT_INT = 1073741823;
- {
- warning(
- shouldUpdate !== undefined,
- "%s.shouldComponentUpdate(): Returned undefined instead of a " +
- "boolean value. Make sure to return true or false.",
- getComponentName(workInProgress) || "Unknown"
- );
- }
+// TODO: Use an opaque type once ESLint et al support the syntax
- return shouldUpdate;
- }
+var NoWork = 0;
+var Sync = 1;
+var Never = MAX_SIGNED_31_BIT_INT;
- if (type.prototype && type.prototype.isPureReactComponent) {
- return (
- !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
- );
- }
+var UNIT_SIZE = 10;
+var MAGIC_NUMBER_OFFSET = 2;
- return true;
- }
+// 1 unit of expiration time represents 10ms.
+function msToExpirationTime(ms) {
+ // Always add an offset so that we don't clash with the magic number for NoWork.
+ return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET;
+}
- function checkClassInstance(workInProgress) {
- var instance = workInProgress.stateNode;
- var type = workInProgress.type;
- {
- var name = getComponentName(workInProgress);
- var renderPresent = instance.render;
+function expirationTimeToMs(expirationTime) {
+ return (expirationTime - MAGIC_NUMBER_OFFSET) * UNIT_SIZE;
+}
- if (!renderPresent) {
- if (type.prototype && typeof type.prototype.render === "function") {
- warning(
- false,
- "%s(...): No `render` method found on the returned component " +
- "instance: did you accidentally return an object from the constructor?",
- name
- );
- } else {
- warning(
- false,
- "%s(...): No `render` method found on the returned component " +
- "instance: you may have forgotten to define `render`.",
- name
- );
- }
- }
+function ceiling(num, precision) {
+ return (((num / precision) | 0) + 1) * precision;
+}
- var noGetInitialStateOnES6 =
- !instance.getInitialState ||
- instance.getInitialState.isReactClassApproved ||
- instance.state;
- warning(
- noGetInitialStateOnES6,
- "getInitialState was defined on %s, a plain JavaScript class. " +
- "This is only supported for classes created using React.createClass. " +
- "Did you mean to define a state property instead?",
- name
- );
- var noGetDefaultPropsOnES6 =
- !instance.getDefaultProps ||
- instance.getDefaultProps.isReactClassApproved;
- warning(
- noGetDefaultPropsOnES6,
- "getDefaultProps was defined on %s, a plain JavaScript class. " +
- "This is only supported for classes created using React.createClass. " +
- "Use a static property to define defaultProps instead.",
- name
- );
- var noInstancePropTypes = !instance.propTypes;
- warning(
- noInstancePropTypes,
- "propTypes was defined as an instance property on %s. Use a static " +
- "property to define propTypes instead.",
- name
- );
- var noInstanceContextTypes = !instance.contextTypes;
- warning(
- noInstanceContextTypes,
- "contextTypes was defined as an instance property on %s. Use a static " +
- "property to define contextTypes instead.",
- name
- );
- var noComponentShouldUpdate =
- typeof instance.componentShouldUpdate !== "function";
- warning(
- noComponentShouldUpdate,
- "%s has a method called " +
- "componentShouldUpdate(). Did you mean shouldComponentUpdate()? " +
- "The name is phrased as a question because the function is " +
- "expected to return a value.",
- name
- );
- if (
- type.prototype &&
- type.prototype.isPureReactComponent &&
- typeof instance.shouldComponentUpdate !== "undefined"
- ) {
- warning(
- false,
- "%s has a method called shouldComponentUpdate(). " +
- "shouldComponentUpdate should not be used when extending React.PureComponent. " +
- "Please extend React.Component if shouldComponentUpdate is used.",
- getComponentName(workInProgress) || "A pure component"
- );
- }
- var noComponentDidUnmount =
- typeof instance.componentDidUnmount !== "function";
- warning(
- noComponentDidUnmount,
- "%s has a method called " +
- "componentDidUnmount(). But there is no such lifecycle method. " +
- "Did you mean componentWillUnmount()?",
- name
- );
- var noComponentDidReceiveProps =
- typeof instance.componentDidReceiveProps !== "function";
- warning(
- noComponentDidReceiveProps,
- "%s has a method called " +
- "componentDidReceiveProps(). But there is no such lifecycle method. " +
- "If you meant to update the state in response to changing props, " +
- "use componentWillReceiveProps(). If you meant to fetch data or " +
- "run side-effects or mutations after React has updated the UI, use componentDidUpdate().",
- name
- );
- var noComponentWillRecieveProps =
- typeof instance.componentWillRecieveProps !== "function";
- warning(
- noComponentWillRecieveProps,
- "%s has a method called " +
- "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",
- name
- );
- var hasMutatedProps = instance.props !== workInProgress.pendingProps;
- warning(
- instance.props === undefined || !hasMutatedProps,
- "%s(...): When calling super() in `%s`, make sure to pass " +
- "up the same props that your component's constructor was passed.",
- name,
- name
- );
- var noInstanceDefaultProps = !instance.defaultProps;
- warning(
- noInstanceDefaultProps,
- "Setting defaultProps as an instance property on %s is not supported and will be ignored." +
- " Instead, define defaultProps as a static property on %s.",
- name,
- name
- );
- }
+function computeExpirationBucket(currentTime, expirationInMs, bucketSizeMs) {
+ return ceiling(
+ currentTime + expirationInMs / UNIT_SIZE,
+ bucketSizeMs / UNIT_SIZE
+ );
+}
- var state = instance.state;
- if (state && (typeof state !== "object" || isArray(state))) {
- warning(
- false,
- "%s.state: must be set to an object or null",
- getComponentName(workInProgress)
- );
- }
- if (typeof instance.getChildContext === "function") {
- warning(
- typeof workInProgress.type.childContextTypes === "object",
- "%s.getChildContext(): childContextTypes must be defined in order to " +
- "use getChildContext().",
- getComponentName(workInProgress)
- );
- }
- }
+var NoContext = 0;
+var AsyncUpdates = 1;
+var StrictMode = 2;
- function resetInputPointers(workInProgress, instance) {
- instance.props = workInProgress.memoizedProps;
- instance.state = workInProgress.memoizedState;
- }
+var hasBadMapPolyfill = void 0;
- function adoptClassInstance(workInProgress, instance) {
- instance.updater = updater;
- workInProgress.stateNode = instance;
- // The instance needs access to the fiber so that it can schedule updates
- set(instance, workInProgress);
- {
- instance._reactInternalInstance = fakeInternalInstance;
- }
+{
+ hasBadMapPolyfill = false;
+ try {
+ var nonExtensibleObject = Object.preventExtensions({});
+ var testMap = new Map([[nonExtensibleObject, null]]);
+ var testSet = new Set([nonExtensibleObject]);
+ // This is necessary for Rollup to not consider these unused.
+ // https://github.com/rollup/rollup/issues/1771
+ // TODO: we can remove these if Rollup fixes the bug.
+ testMap.set(0, 0);
+ testSet.add(0);
+ } catch (e) {
+ // TODO: Consider warning about bad polyfills
+ hasBadMapPolyfill = true;
}
+}
- function constructClassInstance(workInProgress, props) {
- var ctor = workInProgress.type;
- var unmaskedContext = getUnmaskedContext(workInProgress);
- var needsContext = isContextConsumer(workInProgress);
- var context = needsContext
- ? getMaskedContext(workInProgress, unmaskedContext)
- : emptyObject;
- var instance = new ctor(props, context);
- adoptClassInstance(workInProgress, instance);
+// A Fiber is work on a Component that needs to be done or was done. There can
+// be more than one per component.
- // Cache unmasked context so we can avoid recreating masked context unless necessary.
- // ReactFiberContext usually updates this cache but can't for newly-created instances.
- if (needsContext) {
- cacheContext(workInProgress, unmaskedContext, context);
- }
+var debugCounter = void 0;
- return instance;
- }
+{
+ debugCounter = 1;
+}
- function callComponentWillMount(workInProgress, instance) {
- startPhaseTimer(workInProgress, "componentWillMount");
- var oldState = instance.state;
- instance.componentWillMount();
- stopPhaseTimer();
+function FiberNode(tag, pendingProps, key, internalContextTag) {
+ // Instance
+ this.tag = tag;
+ this.key = key;
+ this.type = null;
+ this.stateNode = null;
- // Simulate an async bailout/interruption by invoking lifecycle twice.
- if (debugRenderPhaseSideEffects) {
- instance.componentWillMount();
- }
+ // Fiber
+ this["return"] = null;
+ this.child = null;
+ this.sibling = null;
+ this.index = 0;
- if (oldState !== instance.state) {
- {
- warning(
- false,
- "%s.componentWillMount(): Assigning directly to this.state is " +
- "deprecated (except inside a component's " +
- "constructor). Use setState instead.",
- getComponentName(workInProgress)
- );
- }
- updater.enqueueReplaceState(instance, instance.state, null);
- }
- }
+ this.ref = null;
- function callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- newContext
- ) {
- startPhaseTimer(workInProgress, "componentWillReceiveProps");
- var oldState = instance.state;
- instance.componentWillReceiveProps(newProps, newContext);
- stopPhaseTimer();
+ this.pendingProps = pendingProps;
+ this.memoizedProps = null;
+ this.updateQueue = null;
+ this.memoizedState = null;
- // Simulate an async bailout/interruption by invoking lifecycle twice.
- if (debugRenderPhaseSideEffects) {
- instance.componentWillReceiveProps(newProps, newContext);
- }
+ this.internalContextTag = internalContextTag;
- if (instance.state !== oldState) {
- {
- var componentName = getComponentName(workInProgress) || "Component";
- if (!didWarnAboutStateAssignmentForComponent[componentName]) {
- warning(
- false,
- "%s.componentWillReceiveProps(): Assigning directly to " +
- "this.state is deprecated (except inside a component's " +
- "constructor). Use setState instead.",
- componentName
- );
- didWarnAboutStateAssignmentForComponent[componentName] = true;
- }
- }
- updater.enqueueReplaceState(instance, instance.state, null);
- }
- }
-
- // Invokes the mount life-cycles on a previously never rendered instance.
- function mountClassInstance(workInProgress, renderExpirationTime) {
- var current = workInProgress.alternate;
-
- {
- checkClassInstance(workInProgress);
- }
+ // Effects
+ this.effectTag = NoEffect;
+ this.nextEffect = null;
- var instance = workInProgress.stateNode;
- var state = instance.state || null;
- var props = workInProgress.pendingProps;
- var unmaskedContext = getUnmaskedContext(workInProgress);
+ this.firstEffect = null;
+ this.lastEffect = null;
- instance.props = props;
- instance.state = workInProgress.memoizedState = state;
- instance.refs = emptyObject;
- instance.context = getMaskedContext(workInProgress, unmaskedContext);
+ this.expirationTime = NoWork;
- if (
- enableAsyncSubtreeAPI &&
- workInProgress.type != null &&
- workInProgress.type.prototype != null &&
- workInProgress.type.prototype.unstable_isAsyncReactComponent === true
- ) {
- workInProgress.internalContextTag |= AsyncUpdates;
- }
+ this.alternate = null;
- if (typeof instance.componentWillMount === "function") {
- callComponentWillMount(workInProgress, instance);
- // If we had additional state updates during this life-cycle, let's
- // process them now.
- var updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- instance.state = processUpdateQueue(
- current,
- workInProgress,
- updateQueue,
- instance,
- props,
- renderExpirationTime
- );
- }
- }
- if (typeof instance.componentDidMount === "function") {
- workInProgress.effectTag |= Update;
+ {
+ this._debugID = debugCounter++;
+ this._debugSource = null;
+ this._debugOwner = null;
+ this._debugIsCurrentlyTiming = false;
+ if (!hasBadMapPolyfill && typeof Object.preventExtensions === "function") {
+ Object.preventExtensions(this);
}
}
+}
- // Called on a preexisting class instance. Returns false if a resumed render
- // could be reused.
- // function resumeMountClassInstance(
- // workInProgress: Fiber,
- // priorityLevel: PriorityLevel,
- // ): boolean {
- // const instance = workInProgress.stateNode;
- // resetInputPointers(workInProgress, instance);
-
- // let newState = workInProgress.memoizedState;
- // let newProps = workInProgress.pendingProps;
- // if (!newProps) {
- // // If there isn't any new props, then we'll reuse the memoized props.
- // // This could be from already completed work.
- // newProps = workInProgress.memoizedProps;
- // invariant(
- // newProps != null,
- // 'There should always be pending or memoized props. This error is ' +
- // 'likely caused by a bug in React. Please file an issue.',
- // );
- // }
- // const newUnmaskedContext = getUnmaskedContext(workInProgress);
- // const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
+// This is a constructor function, rather than a POJO constructor, still
+// please ensure we do the following:
+// 1) Nobody should add any instance methods on this. Instance methods can be
+// more difficult to predict when they get optimized and they are almost
+// never inlined properly in static compilers.
+// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
+// always know when it is a fiber.
+// 3) We might want to experiment with using numeric keys since they are easier
+// to optimize in a non-JIT environment.
+// 4) We can easily go from a constructor to a createFiber object literal if that
+// is faster.
+// 5) It should be easy to port this to a C struct and keep a C implementation
+// compatible.
+var createFiber = function(tag, pendingProps, key, internalContextTag) {
+ // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
+ return new FiberNode(tag, pendingProps, key, internalContextTag);
+};
- // const oldContext = instance.context;
- // const oldProps = workInProgress.memoizedProps;
+function shouldConstruct(Component) {
+ return !!(Component.prototype && Component.prototype.isReactComponent);
+}
- // if (
- // typeof instance.componentWillReceiveProps === 'function' &&
- // (oldProps !== newProps || oldContext !== newContext)
- // ) {
- // callComponentWillReceiveProps(
- // workInProgress,
- // instance,
- // newProps,
- // newContext,
- // );
- // }
+// This is used to create an alternate fiber to do work on.
+function createWorkInProgress(current, pendingProps, expirationTime) {
+ var workInProgress = current.alternate;
+ if (workInProgress === null) {
+ // We use a double buffering pooling technique because we know that we'll
+ // only ever need at most two versions of a tree. We pool the "other" unused
+ // node that we're free to reuse. This is lazily created to avoid allocating
+ // extra objects for things that are never updated. It also allow us to
+ // reclaim the extra memory if needed.
+ workInProgress = createFiber(
+ current.tag,
+ pendingProps,
+ current.key,
+ current.internalContextTag
+ );
+ workInProgress.type = current.type;
+ workInProgress.stateNode = current.stateNode;
- // // Process the update queue before calling shouldComponentUpdate
- // const updateQueue = workInProgress.updateQueue;
- // if (updateQueue !== null) {
- // newState = processUpdateQueue(
- // workInProgress,
- // updateQueue,
- // instance,
- // newState,
- // newProps,
- // priorityLevel,
- // );
- // }
+ {
+ // DEV-only fields
+ workInProgress._debugID = current._debugID;
+ workInProgress._debugSource = current._debugSource;
+ workInProgress._debugOwner = current._debugOwner;
+ }
- // // TODO: Should we deal with a setState that happened after the last
- // // componentWillMount and before this componentWillMount? Probably
- // // unsupported anyway.
+ workInProgress.alternate = current;
+ current.alternate = workInProgress;
+ } else {
+ workInProgress.pendingProps = pendingProps;
- // if (
- // !checkShouldComponentUpdate(
- // workInProgress,
- // workInProgress.memoizedProps,
- // newProps,
- // workInProgress.memoizedState,
- // newState,
- // newContext,
- // )
- // ) {
- // // Update the existing instance's state, props, and context pointers even
- // // though we're bailing out.
- // instance.props = newProps;
- // instance.state = newState;
- // instance.context = newContext;
- // return false;
- // }
+ // We already have an alternate.
+ // Reset the effect tag.
+ workInProgress.effectTag = NoEffect;
- // // Update the input pointers now so that they are correct when we call
- // // componentWillMount
- // instance.props = newProps;
- // instance.state = newState;
- // instance.context = newContext;
+ // The effect list is no longer valid.
+ workInProgress.nextEffect = null;
+ workInProgress.firstEffect = null;
+ workInProgress.lastEffect = null;
+ }
- // if (typeof instance.componentWillMount === 'function') {
- // callComponentWillMount(workInProgress, instance);
- // // componentWillMount may have called setState. Process the update queue.
- // const newUpdateQueue = workInProgress.updateQueue;
- // if (newUpdateQueue !== null) {
- // newState = processUpdateQueue(
- // workInProgress,
- // newUpdateQueue,
- // instance,
- // newState,
- // newProps,
- // priorityLevel,
- // );
- // }
- // }
+ workInProgress.expirationTime = expirationTime;
- // if (typeof instance.componentDidMount === 'function') {
- // workInProgress.effectTag |= Update;
- // }
+ workInProgress.child = current.child;
+ workInProgress.memoizedProps = current.memoizedProps;
+ workInProgress.memoizedState = current.memoizedState;
+ workInProgress.updateQueue = current.updateQueue;
- // instance.state = newState;
+ // These will be overridden during the parent's reconciliation
+ workInProgress.sibling = current.sibling;
+ workInProgress.index = current.index;
+ workInProgress.ref = current.ref;
- // return true;
- // }
+ return workInProgress;
+}
- // Invokes the update life-cycles and returns false if it shouldn't rerender.
- function updateClassInstance(current, workInProgress, renderExpirationTime) {
- var instance = workInProgress.stateNode;
- resetInputPointers(workInProgress, instance);
+function createHostRootFiber(isAsync) {
+ var internalContextTag = isAsync ? AsyncUpdates | StrictMode : NoContext;
+ return createFiber(HostRoot, null, null, internalContextTag);
+}
- var oldProps = workInProgress.memoizedProps;
- var newProps = workInProgress.pendingProps;
- var oldContext = instance.context;
- var newUnmaskedContext = getUnmaskedContext(workInProgress);
- var newContext = getMaskedContext(workInProgress, newUnmaskedContext);
+function createFiberFromElement(element, internalContextTag, expirationTime) {
+ var owner = null;
+ {
+ owner = element._owner;
+ }
- // 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.
+ var fiber = void 0;
+ var type = element.type;
+ var key = element.key;
+ var pendingProps = element.props;
- if (
- typeof instance.componentWillReceiveProps === "function" &&
- (oldProps !== newProps || oldContext !== newContext)
- ) {
- callComponentWillReceiveProps(
- workInProgress,
- instance,
- newProps,
- newContext
- );
- }
-
- // Compute the next state using the memoized state and the update queue.
- var oldState = workInProgress.memoizedState;
- // TODO: Previous state can be null.
- var newState = void 0;
- if (workInProgress.updateQueue !== null) {
- newState = processUpdateQueue(
- current,
- workInProgress,
- workInProgress.updateQueue,
- instance,
- newProps,
- renderExpirationTime
- );
- } else {
- newState = oldState;
- }
-
- if (
- oldProps === newProps &&
- oldState === newState &&
- !hasContextChanged() &&
- !(
- workInProgress.updateQueue !== null &&
- workInProgress.updateQueue.hasForceUpdate
- )
- ) {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === "function") {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Update;
+ var fiberTag = void 0;
+ if (typeof type === "function") {
+ fiberTag = shouldConstruct(type) ? ClassComponent : IndeterminateComponent;
+ } else if (typeof type === "string") {
+ fiberTag = HostComponent;
+ } else {
+ switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(
+ pendingProps.children,
+ internalContextTag,
+ expirationTime,
+ key
+ );
+ case REACT_STRICT_MODE_TYPE:
+ fiberTag = Mode;
+ internalContextTag |= StrictMode;
+ break;
+ case REACT_CALL_TYPE:
+ fiberTag = CallComponent;
+ break;
+ case REACT_RETURN_TYPE:
+ fiberTag = ReturnComponent;
+ break;
+ default: {
+ if (typeof type === "object" && type !== null) {
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = ContextProvider;
+ break;
+ case REACT_CONTEXT_TYPE:
+ // This is a consumer
+ fiberTag = ContextConsumer;
+ break;
+ default:
+ if (typeof type.tag === "number") {
+ // Currently assumed to be a continuation and therefore is a
+ // fiber already.
+ // TODO: The yield system is currently broken for updates in
+ // some cases. The reified yield stores a fiber, but we don't
+ // know which fiber that is; the current or a workInProgress?
+ // When the continuation gets rendered here we don't know if we
+ // can reuse that fiber or if we need to clone it. There is
+ // probably a clever way to restructure this.
+ fiber = type;
+ fiber.pendingProps = pendingProps;
+ fiber.expirationTime = expirationTime;
+ return fiber;
+ } else {
+ throwOnInvalidElementType(type, owner);
+ }
+ break;
+ }
+ } else {
+ throwOnInvalidElementType(type, owner);
}
}
- return false;
}
+ }
- var shouldUpdate = checkShouldComponentUpdate(
- workInProgress,
- oldProps,
- newProps,
- oldState,
- newState,
- newContext
- );
+ fiber = createFiber(fiberTag, pendingProps, key, internalContextTag);
+ fiber.type = type;
+ fiber.expirationTime = expirationTime;
- if (shouldUpdate) {
- if (typeof instance.componentWillUpdate === "function") {
- startPhaseTimer(workInProgress, "componentWillUpdate");
- instance.componentWillUpdate(newProps, newState, newContext);
- stopPhaseTimer();
+ {
+ fiber._debugSource = element._source;
+ fiber._debugOwner = element._owner;
+ }
- // Simulate an async bailout/interruption by invoking lifecycle twice.
- if (debugRenderPhaseSideEffects) {
- instance.componentWillUpdate(newProps, newState, newContext);
- }
- }
- if (typeof instance.componentDidUpdate === "function") {
- workInProgress.effectTag |= Update;
- }
- } else {
- // If an update was already in progress, we should schedule an Update
- // effect even though we're bailing out, so that cWU/cDU are called.
- if (typeof instance.componentDidUpdate === "function") {
- if (
- oldProps !== current.memoizedProps ||
- oldState !== current.memoizedState
- ) {
- workInProgress.effectTag |= Update;
- }
- }
+ return fiber;
+}
- // If shouldComponentUpdate returned false, we should still update the
- // memoized props/state to indicate that this work can be reused.
- memoizeProps(workInProgress, newProps);
- memoizeState(workInProgress, newState);
+function throwOnInvalidElementType(type, owner) {
+ var info = "";
+ {
+ if (
+ type === undefined ||
+ (typeof type === "object" &&
+ type !== null &&
+ Object.keys(type).length === 0)
+ ) {
+ info +=
+ " You likely forgot to export your component from the file " +
+ "it's defined in, or you might have mixed up default and " +
+ "named imports.";
+ }
+ var ownerName = owner ? getComponentName(owner) : null;
+ if (ownerName) {
+ info += "\n\nCheck the render method of `" + ownerName + "`.";
}
-
- // Update the existing instance's state, props, and context pointers even
- // if shouldComponentUpdate returns false.
- instance.props = newProps;
- instance.state = newState;
- instance.context = newContext;
-
- return shouldUpdate;
}
+ invariant(
+ false,
+ "Element type is invalid: expected a string (for built-in " +
+ "components) or a class/function (for composite components) " +
+ "but got: %s.%s",
+ type == null ? type : typeof type,
+ info
+ );
+}
- return {
- adoptClassInstance: adoptClassInstance,
- constructClassInstance: constructClassInstance,
- mountClassInstance: mountClassInstance,
- // resumeMountClassInstance,
- updateClassInstance: updateClassInstance
- };
-};
+function createFiberFromFragment(
+ elements,
+ internalContextTag,
+ expirationTime,
+ key
+) {
+ var fiber = createFiber(Fragment, elements, key, internalContextTag);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
-var getCurrentFiberStackAddendum$1 =
- ReactDebugCurrentFiber.getCurrentFiberStackAddendum;
+function createFiberFromText(content, internalContextTag, expirationTime) {
+ var fiber = createFiber(HostText, content, null, internalContextTag);
+ fiber.expirationTime = expirationTime;
+ return fiber;
+}
-{
- var didWarnAboutMaps = false;
- /**
- * Warn if there's no key explicitly set on dynamic arrays of children or
- * object keys are not valid. This allows us to keep track of children between
- * updates.
- */
- var ownerHasKeyUseWarning = {};
- var ownerHasFunctionTypeWarning = {};
+function createFiberFromHostInstanceForDeletion() {
+ var fiber = createFiber(HostComponent, null, null, NoContext);
+ fiber.type = "DELETED";
+ return fiber;
+}
- var warnForMissingKey = function(child) {
- if (child === null || typeof child !== "object") {
- return;
- }
- if (!child._store || child._store.validated || child.key != null) {
- return;
- }
- invariant(
- typeof child._store === "object",
- "React Component in warnForMissingKey should have a _store. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- child._store.validated = true;
+function createFiberFromPortal(portal, internalContextTag, expirationTime) {
+ var pendingProps = portal.children !== null ? portal.children : [];
+ var fiber = createFiber(
+ HostPortal,
+ pendingProps,
+ portal.key,
+ internalContextTag
+ );
+ fiber.expirationTime = expirationTime;
+ fiber.stateNode = {
+ containerInfo: portal.containerInfo,
+ pendingChildren: null, // Used by persistent updates
+ implementation: portal.implementation
+ };
+ return fiber;
+}
- var currentComponentErrorInfo =
- "Each child in an array or iterator should have a unique " +
- '"key" prop. See https://fb.me/react-warning-keys for ' +
- "more information." +
- (getCurrentFiberStackAddendum$1() || "");
- if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
- return;
- }
- ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
+// TODO: This should be lifted into the renderer.
- warning(
- false,
- "Each child in an array or iterator should have a unique " +
- '"key" prop. See https://fb.me/react-warning-keys for ' +
- "more information.%s",
- getCurrentFiberStackAddendum$1()
- );
+function createFiberRoot(containerInfo, isAsync, hydrate) {
+ // Cyclic construction. This cheats the type system right now because
+ // stateNode is any.
+ var uninitializedFiber = createHostRootFiber(isAsync);
+ var root = {
+ current: uninitializedFiber,
+ containerInfo: containerInfo,
+ pendingChildren: null,
+ remainingExpirationTime: NoWork,
+ isReadyForCommit: false,
+ finishedWork: null,
+ context: null,
+ pendingContext: null,
+ hydrate: hydrate,
+ firstBatch: null,
+ nextScheduledRoot: null
};
+ uninitializedFiber.stateNode = root;
+ return root;
}
-var isArray$1 = Array.isArray;
+var onCommitFiberRoot = null;
+var onCommitFiberUnmount = null;
+var hasLoggedError = false;
-function coerceRef(current, element) {
- var mixedRef = element.ref;
- if (mixedRef !== null && typeof mixedRef !== "function") {
- if (element._owner) {
- var owner = element._owner;
- var inst = void 0;
- if (owner) {
- var ownerFiber = owner;
- invariant(
- ownerFiber.tag === ClassComponent,
- "Stateless function components cannot have refs."
- );
- inst = ownerFiber.stateNode;
+function catchErrors(fn) {
+ return function(arg) {
+ try {
+ return fn(arg);
+ } catch (err) {
+ if (true && !hasLoggedError) {
+ hasLoggedError = true;
+ warning(false, "React DevTools encountered an error: %s", err);
}
- invariant(
- inst,
- "Missing owner for string ref %s. This error is likely caused by a " +
- "bug in React. Please file an issue.",
- mixedRef
- );
- var stringRef = "" + mixedRef;
- // Check if previous string ref matches new string ref
- if (
- current !== null &&
- current.ref !== null &&
- current.ref._stringRef === stringRef
- ) {
- return current.ref;
- }
- var ref = function(value) {
- var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
- if (value === null) {
- delete refs[stringRef];
- } else {
- refs[stringRef] = value;
- }
- };
- ref._stringRef = stringRef;
- return ref;
- } else {
- invariant(
- typeof mixedRef === "string",
- "Expected ref to be a function or a string."
- );
- invariant(
- element._owner,
- "Element ref was specified as a string (%s) but no owner was " +
- "set. You may have multiple copies of React loaded. " +
- "(details: https://fb.me/react-refs-must-have-owner).",
- mixedRef
+ }
+ };
+}
+
+function injectInternals(internals) {
+ if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === "undefined") {
+ // No DevTools
+ return false;
+ }
+ var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
+ if (hook.isDisabled) {
+ // This isn't a real property on the hook, but it can be set to opt out
+ // of DevTools integration and associated warnings and logs.
+ // https://github.com/facebook/react/issues/3877
+ return true;
+ }
+ if (!hook.supportsFiber) {
+ {
+ warning(
+ false,
+ "The installed version of React DevTools is too old and will not work " +
+ "with the current version of React. Please update React DevTools. " +
+ "https://fb.me/react-devtools"
);
}
+ // DevTools exists, even though it doesn't support Fiber.
+ return true;
}
- return mixedRef;
-}
-
-function throwOnInvalidObjectType(returnFiber, newChild) {
- if (returnFiber.type !== "textarea") {
- var addendum = "";
+ try {
+ var rendererID = hook.inject(internals);
+ // We have successfully injected, so now it is safe to set up hooks.
+ onCommitFiberRoot = catchErrors(function(root) {
+ return hook.onCommitFiberRoot(rendererID, root);
+ });
+ onCommitFiberUnmount = catchErrors(function(fiber) {
+ return hook.onCommitFiberUnmount(rendererID, fiber);
+ });
+ } catch (err) {
+ // Catch all errors because it is unsafe to throw during initialization.
{
- addendum =
- " If you meant to render a collection of children, use an array " +
- "instead." +
- (getCurrentFiberStackAddendum$1() || "");
+ warning(false, "React DevTools encountered an error: %s.", err);
}
- invariant(
- false,
- "Objects are not valid as a React child (found: %s).%s",
- Object.prototype.toString.call(newChild) === "[object Object]"
- ? "object with keys {" + Object.keys(newChild).join(", ") + "}"
- : newChild,
- addendum
- );
}
+ // DevTools exists
+ return true;
}
-function warnOnFunctionType() {
- var currentComponentErrorInfo =
- "Functions are not valid as a React child. This may happen if " +
- "you return a Component instead of from render. " +
- "Or maybe you meant to call this function rather than return it." +
- (getCurrentFiberStackAddendum$1() || "");
-
- if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) {
- return;
+function onCommitRoot(root) {
+ if (typeof onCommitFiberRoot === "function") {
+ onCommitFiberRoot(root);
}
- ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true;
-
- warning(
- false,
- "Functions are not valid as a React child. This may happen if " +
- "you return a Component instead of from render. " +
- "Or maybe you meant to call this function rather than return it.%s",
- getCurrentFiberStackAddendum$1() || ""
- );
}
-// This wrapper function exists because I expect to clone the code in each path
-// to be able to optimize each path individually by branching early. This needs
-// a compiler or we can do it manually. Helpers that don't need this branching
-// live outside of this function.
-function ChildReconciler(shouldTrackSideEffects) {
- function deleteChild(returnFiber, childToDelete) {
- if (!shouldTrackSideEffects) {
- // Noop.
- return;
- }
- // Deletions are added in reversed order so we add it to the front.
- // At this point, the return fiber's effect list is empty except for
- // deletions, so we can just append the deletion to the list. The remaining
- // effects aren't added until the complete phase. Once we implement
- // resuming, this may not be true.
- var last = returnFiber.lastEffect;
- if (last !== null) {
- last.nextEffect = childToDelete;
- returnFiber.lastEffect = childToDelete;
- } else {
- returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
- }
- childToDelete.nextEffect = null;
- childToDelete.effectTag = Deletion;
+function onCommitUnmount(fiber) {
+ if (typeof onCommitFiberUnmount === "function") {
+ onCommitFiberUnmount(fiber);
}
+}
- function deleteRemainingChildren(returnFiber, currentFirstChild) {
- if (!shouldTrackSideEffects) {
- // Noop.
- return null;
- }
+var ReactStrictModeWarnings = {
+ discardPendingWarnings: function() {},
+ flushPendingDeprecationWarnings: function() {},
+ flushPendingUnsafeLifecycleWarnings: function() {},
+ recordDeprecationWarnings: function(fiber, instance) {},
+ recordUnsafeLifecycleWarnings: function(fiber, instance) {}
+};
- // TODO: For the shouldClone case, this could be micro-optimized a bit by
- // assuming that after the first child we've already added everything.
- var childToDelete = currentFirstChild;
- while (childToDelete !== null) {
- deleteChild(returnFiber, childToDelete);
- childToDelete = childToDelete.sibling;
- }
- return null;
- }
+{
+ var LIFECYCLE_SUGGESTIONS = {
+ UNSAFE_componentWillMount: "componentDidMount",
+ UNSAFE_componentWillReceiveProps: "static getDerivedStateFromProps",
+ UNSAFE_componentWillUpdate: "componentDidUpdate"
+ };
- function mapRemainingChildren(returnFiber, currentFirstChild) {
- // Add the remaining children to a temporary map so that we can find them by
- // keys quickly. Implicit (null) keys get added to this set with their index
- var existingChildren = new Map();
+ var pendingComponentWillMountWarnings = [];
+ var pendingComponentWillReceivePropsWarnings = [];
+ var pendingComponentWillUpdateWarnings = [];
+ var pendingUnsafeLifecycleWarnings = new Map();
- var existingChild = currentFirstChild;
- while (existingChild !== null) {
- if (existingChild.key !== null) {
- existingChildren.set(existingChild.key, existingChild);
- } else {
- existingChildren.set(existingChild.index, existingChild);
+ // Tracks components we have already warned about.
+ var didWarnAboutDeprecatedLifecycles = new Set();
+ var didWarnAboutUnsafeLifecycles = new Set();
+
+ ReactStrictModeWarnings.discardPendingWarnings = function() {
+ pendingComponentWillMountWarnings = [];
+ pendingComponentWillReceivePropsWarnings = [];
+ pendingComponentWillUpdateWarnings = [];
+ pendingUnsafeLifecycleWarnings = new Map();
+ };
+
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function() {
+ pendingUnsafeLifecycleWarnings.forEach(function(
+ lifecycleWarningsMap,
+ strictRoot
+ ) {
+ var lifecyclesWarningMesages = [];
+
+ Object.keys(lifecycleWarningsMap).forEach(function(lifecycle) {
+ var lifecycleWarnings = lifecycleWarningsMap[lifecycle];
+ if (lifecycleWarnings.length > 0) {
+ var componentNames = new Set();
+ lifecycleWarnings.forEach(function(fiber) {
+ componentNames.add(getComponentName(fiber) || "Component");
+ didWarnAboutUnsafeLifecycles.add(fiber.type);
+ });
+
+ var formatted = lifecycle.replace("UNSAFE_", "");
+ var suggestion = LIFECYCLE_SUGGESTIONS[lifecycle];
+ var sortedComponentNames = Array.from(componentNames)
+ .sort()
+ .join(", ");
+
+ lifecyclesWarningMesages.push(
+ formatted +
+ ": Please update the following components to use " +
+ (suggestion + " instead: " + sortedComponentNames)
+ );
+ }
+ });
+
+ if (lifecyclesWarningMesages.length > 0) {
+ var strictRootComponentStack = getStackAddendumByWorkInProgressFiber(
+ strictRoot
+ );
+
+ warning(
+ false,
+ "Unsafe lifecycle methods were found within a strict-mode tree:%s" +
+ "\n\n%s" +
+ "\n\nLearn more about this warning here:" +
+ "\nhttps://fb.me/react-strict-mode-warnings",
+ strictRootComponentStack,
+ lifecyclesWarningMesages.join("\n\n")
+ );
}
- existingChild = existingChild.sibling;
- }
- return existingChildren;
- }
+ });
- function useFiber(fiber, pendingProps, expirationTime) {
- // We currently set sibling to null and index to 0 here because it is easy
- // to forget to do before returning it. E.g. for the single child case.
- var clone = createWorkInProgress(fiber, pendingProps, expirationTime);
- clone.index = 0;
- clone.sibling = null;
- return clone;
- }
+ pendingUnsafeLifecycleWarnings = new Map();
+ };
- function placeChild(newFiber, lastPlacedIndex, newIndex) {
- newFiber.index = newIndex;
- if (!shouldTrackSideEffects) {
- // Noop.
- return lastPlacedIndex;
- }
- var current = newFiber.alternate;
- if (current !== null) {
- var oldIndex = current.index;
- if (oldIndex < lastPlacedIndex) {
- // This is a move.
- newFiber.effectTag = Placement;
- return lastPlacedIndex;
- } else {
- // This item can stay in place.
- return oldIndex;
+ var getStrictRoot = function(fiber) {
+ var maybeStrictRoot = null;
+
+ while (fiber !== null) {
+ if (fiber.internalContextTag & StrictMode) {
+ maybeStrictRoot = fiber;
}
- } else {
- // This is an insertion.
- newFiber.effectTag = Placement;
- return lastPlacedIndex;
- }
- }
- function placeSingleChild(newFiber) {
- // This is simpler for the single child case. We only need to do a
- // placement for inserting new children.
- if (shouldTrackSideEffects && newFiber.alternate === null) {
- newFiber.effectTag = Placement;
+ fiber = fiber["return"];
}
- return newFiber;
- }
- function updateTextNode(returnFiber, current, textContent, expirationTime) {
- if (current === null || current.tag !== HostText) {
- // Insert
- var created = createFiberFromText(
- textContent,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, textContent, expirationTime);
- existing["return"] = returnFiber;
- return existing;
- }
- }
+ return maybeStrictRoot;
+ };
- function updateElement(returnFiber, current, element, expirationTime) {
- if (current !== null && current.type === element.type) {
- // Move based on index
- var existing = useFiber(current, element.props, expirationTime);
- existing.ref = coerceRef(current, element);
- existing["return"] = returnFiber;
- {
- existing._debugSource = element._source;
- existing._debugOwner = element._owner;
- }
- return existing;
- } else {
- // Insert
- var created = createFiberFromElement(
- element,
- returnFiber.internalContextTag,
- expirationTime
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings = function() {
+ if (pendingComponentWillMountWarnings.length > 0) {
+ var uniqueNames = new Set();
+ pendingComponentWillMountWarnings.forEach(function(fiber) {
+ uniqueNames.add(getComponentName(fiber) || "Component");
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var sortedNames = Array.from(uniqueNames)
+ .sort()
+ .join(", ");
+
+ warning(
+ false,
+ "componentWillMount is deprecated and will be removed in the next major version. " +
+ "Use componentDidMount instead. As a temporary workaround, " +
+ "you can rename to UNSAFE_componentWillMount." +
+ "\n\nPlease update the following components: %s" +
+ "\n\nLearn more about this warning here:" +
+ "\nhttps://fb.me/react-async-component-lifecycle-hooks",
+ sortedNames
);
- created.ref = coerceRef(current, element);
- created["return"] = returnFiber;
- return created;
+
+ pendingComponentWillMountWarnings = [];
}
- }
- function updateCall(returnFiber, current, call, expirationTime) {
- // TODO: Should this also compare handler to determine whether to reuse?
- if (current === null || current.tag !== CallComponent) {
- // Insert
- var created = createFiberFromCall(
- call,
- returnFiber.internalContextTag,
- expirationTime
+ if (pendingComponentWillReceivePropsWarnings.length > 0) {
+ var _uniqueNames = new Set();
+ pendingComponentWillReceivePropsWarnings.forEach(function(fiber) {
+ _uniqueNames.add(getComponentName(fiber) || "Component");
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var _sortedNames = Array.from(_uniqueNames)
+ .sort()
+ .join(", ");
+
+ warning(
+ false,
+ "componentWillReceiveProps is deprecated and will be removed in the next major version. " +
+ "Use static getDerivedStateFromProps instead." +
+ "\n\nPlease update the following components: %s" +
+ "\n\nLearn more about this warning here:" +
+ "\nhttps://fb.me/react-async-component-lifecycle-hooks",
+ _sortedNames
);
- created["return"] = returnFiber;
- return created;
- } else {
- // Move based on index
- var existing = useFiber(current, call, expirationTime);
- existing["return"] = returnFiber;
- return existing;
+
+ pendingComponentWillReceivePropsWarnings = [];
}
- }
- function updateReturn(returnFiber, current, returnNode, expirationTime) {
- if (current === null || current.tag !== ReturnComponent) {
- // Insert
- var created = createFiberFromReturn(
- returnNode,
- returnFiber.internalContextTag,
- expirationTime
+ if (pendingComponentWillUpdateWarnings.length > 0) {
+ var _uniqueNames2 = new Set();
+ pendingComponentWillUpdateWarnings.forEach(function(fiber) {
+ _uniqueNames2.add(getComponentName(fiber) || "Component");
+ didWarnAboutDeprecatedLifecycles.add(fiber.type);
+ });
+
+ var _sortedNames2 = Array.from(_uniqueNames2)
+ .sort()
+ .join(", ");
+
+ warning(
+ false,
+ "componentWillUpdate is deprecated and will be removed in the next major version. " +
+ "Use componentDidUpdate instead. As a temporary workaround, " +
+ "you can rename to UNSAFE_componentWillUpdate." +
+ "\n\nPlease update the following components: %s" +
+ "\n\nLearn more about this warning here:" +
+ "\nhttps://fb.me/react-async-component-lifecycle-hooks",
+ _sortedNames2
);
- created.type = returnNode.value;
- created["return"] = returnFiber;
- return created;
- } else {
- // Move based on index
- var existing = useFiber(current, null, expirationTime);
- existing.type = returnNode.value;
- existing["return"] = returnFiber;
- return existing;
+
+ pendingComponentWillUpdateWarnings = [];
}
- }
+ };
- function updatePortal(returnFiber, current, portal, expirationTime) {
+ ReactStrictModeWarnings.recordDeprecationWarnings = function(
+ fiber,
+ instance
+ ) {
+ // Dedup strategy: Warn once per component.
+ if (didWarnAboutDeprecatedLifecycles.has(fiber.type)) {
+ return;
+ }
+
+ // Don't warn about react-lifecycles-compat polyfilled components.
+ // Note that it is sufficient to check for the presence of a
+ // single lifecycle, componentWillMount, with the polyfill flag.
if (
- current === null ||
- current.tag !== HostPortal ||
- current.stateNode.containerInfo !== portal.containerInfo ||
- current.stateNode.implementation !== portal.implementation
+ typeof instance.componentWillMount === "function" &&
+ instance.componentWillMount.__suppressDeprecationWarning === true
) {
- // Insert
- var created = createFiberFromPortal(
- portal,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, portal.children || [], expirationTime);
- existing["return"] = returnFiber;
- return existing;
+ return;
}
- }
- function updateFragment(returnFiber, current, fragment, expirationTime, key) {
- if (current === null || current.tag !== Fragment) {
- // Insert
- var created = createFiberFromFragment(
- fragment,
- returnFiber.internalContextTag,
- expirationTime,
- key
- );
- created["return"] = returnFiber;
- return created;
- } else {
- // Update
- var existing = useFiber(current, fragment, expirationTime);
- existing["return"] = returnFiber;
- return existing;
+ if (typeof instance.componentWillMount === "function") {
+ pendingComponentWillMountWarnings.push(fiber);
}
- }
-
- function createChild(returnFiber, newChild, expirationTime) {
- if (typeof newChild === "string" || typeof newChild === "number") {
- // Text nodes don't have keys. If the previous node is implicitly keyed
- // we can continue to replace it without aborting even if it is not a text
- // node.
- var created = createFiberFromText(
- "" + newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
+ if (typeof instance.componentWillReceiveProps === "function") {
+ pendingComponentWillReceivePropsWarnings.push(fiber);
}
+ if (typeof instance.componentWillUpdate === "function") {
+ pendingComponentWillUpdateWarnings.push(fiber);
+ }
+ };
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- if (newChild.type === REACT_FRAGMENT_TYPE) {
- var _created = createFiberFromFragment(
- newChild.props.children,
- returnFiber.internalContextTag,
- expirationTime,
- newChild.key
- );
- _created["return"] = returnFiber;
- return _created;
- } else {
- var _created2 = createFiberFromElement(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- _created2.ref = coerceRef(null, newChild);
- _created2["return"] = returnFiber;
- return _created2;
- }
- }
-
- case REACT_CALL_TYPE: {
- var _created3 = createFiberFromCall(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- _created3["return"] = returnFiber;
- return _created3;
- }
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function(
+ fiber,
+ instance
+ ) {
+ var strictRoot = getStrictRoot(fiber);
+
+ // Dedup strategy: Warn once per component.
+ // This is difficult to track any other way since component names
+ // are often vague and are likely to collide between 3rd party libraries.
+ // An expand property is probably okay to use here since it's DEV-only,
+ // and will only be set in the event of serious warnings.
+ if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
+ return;
+ }
- case REACT_RETURN_TYPE: {
- var _created4 = createFiberFromReturn(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- _created4.type = newChild.value;
- _created4["return"] = returnFiber;
- return _created4;
- }
+ // Don't warn about react-lifecycles-compat polyfilled components.
+ // Note that it is sufficient to check for the presence of a
+ // single lifecycle, componentWillMount, with the polyfill flag.
+ if (
+ typeof instance.componentWillMount === "function" &&
+ instance.componentWillMount.__suppressDeprecationWarning === true
+ ) {
+ return;
+ }
- case REACT_PORTAL_TYPE: {
- var _created5 = createFiberFromPortal(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- _created5["return"] = returnFiber;
- return _created5;
- }
- }
+ var warningsForRoot = void 0;
+ if (!pendingUnsafeLifecycleWarnings.has(strictRoot)) {
+ warningsForRoot = {
+ UNSAFE_componentWillMount: [],
+ UNSAFE_componentWillReceiveProps: [],
+ UNSAFE_componentWillUpdate: []
+ };
- if (isArray$1(newChild) || getIteratorFn(newChild)) {
- var _created6 = createFiberFromFragment(
- newChild,
- returnFiber.internalContextTag,
- expirationTime,
- null
- );
- _created6["return"] = returnFiber;
- return _created6;
- }
+ pendingUnsafeLifecycleWarnings.set(strictRoot, warningsForRoot);
+ } else {
+ warningsForRoot = pendingUnsafeLifecycleWarnings.get(strictRoot);
+ }
- throwOnInvalidObjectType(returnFiber, newChild);
+ var unsafeLifecycles = [];
+ if (
+ typeof instance.componentWillMount === "function" ||
+ typeof instance.UNSAFE_componentWillMount === "function"
+ ) {
+ unsafeLifecycles.push("UNSAFE_componentWillMount");
+ }
+ if (
+ typeof instance.componentWillReceiveProps === "function" ||
+ typeof instance.UNSAFE_componentWillReceiveProps === "function"
+ ) {
+ unsafeLifecycles.push("UNSAFE_componentWillReceiveProps");
+ }
+ if (
+ typeof instance.componentWillUpdate === "function" ||
+ typeof instance.UNSAFE_componentWillUpdate === "function"
+ ) {
+ unsafeLifecycles.push("UNSAFE_componentWillUpdate");
}
- {
- if (typeof newChild === "function") {
- warnOnFunctionType();
- }
+ if (unsafeLifecycles.length > 0) {
+ unsafeLifecycles.forEach(function(lifecycle) {
+ warningsForRoot[lifecycle].push(fiber);
+ });
}
+ };
+}
- return null;
- }
+var didWarnUpdateInsideUpdate = void 0;
- function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
- // Update the fiber if the keys match, otherwise return null.
+{
+ didWarnUpdateInsideUpdate = false;
+}
- var key = oldFiber !== null ? oldFiber.key : null;
+// Callbacks are not validated until invocation
- if (typeof newChild === "string" || typeof newChild === "number") {
- // Text nodes don't have keys. If the previous node is implicitly keyed
- // we can continue to replace it without aborting even if it is not a text
- // node.
- if (key !== null) {
- return null;
- }
- return updateTextNode(
- returnFiber,
- oldFiber,
- "" + newChild,
- expirationTime
- );
- }
+// Singly linked-list of updates. When an update is scheduled, it is added to
+// the queue of the current fiber and the work-in-progress fiber. The two queues
+// are separate but they share a persistent structure.
+//
+// During reconciliation, updates are removed from the work-in-progress fiber,
+// but they remain on the current fiber. That ensures that if a work-in-progress
+// is aborted, the aborted updates are recovered by cloning from current.
+//
+// The work-in-progress queue is always a subset of the current queue.
+//
+// When the tree is committed, the work-in-progress becomes the current.
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- if (newChild.key === key) {
- if (newChild.type === REACT_FRAGMENT_TYPE) {
- return updateFragment(
- returnFiber,
- oldFiber,
- newChild.props.children,
- expirationTime,
- key
- );
- }
- return updateElement(
- returnFiber,
- oldFiber,
- newChild,
- expirationTime
- );
- } else {
- return null;
- }
- }
+function createUpdateQueue(baseState) {
+ var queue = {
+ baseState: baseState,
+ expirationTime: NoWork,
+ first: null,
+ last: null,
+ callbackList: null,
+ hasForceUpdate: false,
+ isInitialized: false
+ };
+ {
+ queue.isProcessing = false;
+ }
+ return queue;
+}
- case REACT_CALL_TYPE: {
- if (newChild.key === key) {
- return updateCall(returnFiber, oldFiber, newChild, expirationTime);
- } else {
- return null;
- }
- }
+function insertUpdateIntoQueue(queue, update) {
+ // Append the update to the end of the list.
+ if (queue.last === null) {
+ // Queue is empty
+ queue.first = queue.last = update;
+ } else {
+ queue.last.next = update;
+ queue.last = update;
+ }
+ if (
+ queue.expirationTime === NoWork ||
+ queue.expirationTime > update.expirationTime
+ ) {
+ queue.expirationTime = update.expirationTime;
+ }
+}
- case REACT_RETURN_TYPE: {
- // Returns don't have keys. If the previous node is implicitly keyed
- // we can continue to replace it without aborting even if it is not a
- // yield.
- if (key === null) {
- return updateReturn(
- returnFiber,
- oldFiber,
- newChild,
- expirationTime
- );
- } else {
- return null;
- }
- }
+function insertUpdateIntoFiber(fiber, update) {
+ // We'll have at least one and at most two distinct update queues.
+ var alternateFiber = fiber.alternate;
+ var queue1 = fiber.updateQueue;
+ if (queue1 === null) {
+ // TODO: We don't know what the base state will be until we begin work.
+ // It depends on which fiber is the next current. Initialize with an empty
+ // base state, then set to the memoizedState when rendering. Not super
+ // happy with this approach.
+ queue1 = fiber.updateQueue = createUpdateQueue(null);
+ }
- case REACT_PORTAL_TYPE: {
- if (newChild.key === key) {
- return updatePortal(
- returnFiber,
- oldFiber,
- newChild,
- expirationTime
- );
- } else {
- return null;
- }
- }
- }
+ var queue2 = void 0;
+ if (alternateFiber !== null) {
+ queue2 = alternateFiber.updateQueue;
+ if (queue2 === null) {
+ queue2 = alternateFiber.updateQueue = createUpdateQueue(null);
+ }
+ } else {
+ queue2 = null;
+ }
+ queue2 = queue2 !== queue1 ? queue2 : null;
- if (isArray$1(newChild) || getIteratorFn(newChild)) {
- if (key !== null) {
- return null;
- }
+ // Warn if an update is scheduled from inside an updater function.
+ {
+ if (
+ (queue1.isProcessing || (queue2 !== null && queue2.isProcessing)) &&
+ !didWarnUpdateInsideUpdate
+ ) {
+ warning(
+ false,
+ "An update (setState, replaceState, or forceUpdate) was scheduled " +
+ "from inside an update function. Update functions should be pure, " +
+ "with zero side-effects. Consider using componentDidUpdate or a " +
+ "callback."
+ );
+ didWarnUpdateInsideUpdate = true;
+ }
+ }
- return updateFragment(
- returnFiber,
- oldFiber,
- newChild,
- expirationTime,
- null
- );
- }
+ // If there's only one queue, add the update to that queue and exit.
+ if (queue2 === null) {
+ insertUpdateIntoQueue(queue1, update);
+ return;
+ }
- throwOnInvalidObjectType(returnFiber, newChild);
- }
+ // If either queue is empty, we need to add to both queues.
+ if (queue1.last === null || queue2.last === null) {
+ insertUpdateIntoQueue(queue1, update);
+ insertUpdateIntoQueue(queue2, update);
+ return;
+ }
- {
- if (typeof newChild === "function") {
- warnOnFunctionType();
- }
- }
+ // If both lists are not empty, the last update is the same for both lists
+ // because of structural sharing. So, we should only append to one of
+ // the lists.
+ insertUpdateIntoQueue(queue1, update);
+ // But we still need to update the `last` pointer of queue2.
+ queue2.last = update;
+}
- return null;
+function getUpdateExpirationTime(fiber) {
+ if (fiber.tag !== ClassComponent && fiber.tag !== HostRoot) {
+ return NoWork;
+ }
+ var updateQueue = fiber.updateQueue;
+ if (updateQueue === null) {
+ return NoWork;
}
+ return updateQueue.expirationTime;
+}
- function updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- newChild,
- expirationTime
- ) {
- if (typeof newChild === "string" || typeof newChild === "number") {
- // Text nodes don't have keys, so we neither have to check the old nor
- // new node for the key. If both are text nodes, they match.
- var matchedFiber = existingChildren.get(newIdx) || null;
- return updateTextNode(
- returnFiber,
- matchedFiber,
- "" + newChild,
- expirationTime
- );
- }
+function getStateFromUpdate(update, instance, prevState, props) {
+ var partialState = update.partialState;
+ if (typeof partialState === "function") {
+ return partialState.call(instance, prevState, props);
+ } else {
+ return partialState;
+ }
+}
- if (typeof newChild === "object" && newChild !== null) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE: {
- var _matchedFiber =
- existingChildren.get(
- newChild.key === null ? newIdx : newChild.key
- ) || null;
- if (newChild.type === REACT_FRAGMENT_TYPE) {
- return updateFragment(
- returnFiber,
- _matchedFiber,
- newChild.props.children,
- expirationTime,
- newChild.key
- );
- }
- return updateElement(
- returnFiber,
- _matchedFiber,
- newChild,
- expirationTime
- );
- }
+function processUpdateQueue(
+ current,
+ workInProgress,
+ queue,
+ instance,
+ props,
+ renderExpirationTime
+) {
+ if (current !== null && current.updateQueue === queue) {
+ // We need to create a work-in-progress queue, by cloning the current queue.
+ var currentQueue = queue;
+ queue = workInProgress.updateQueue = {
+ baseState: currentQueue.baseState,
+ expirationTime: currentQueue.expirationTime,
+ first: currentQueue.first,
+ last: currentQueue.last,
+ isInitialized: currentQueue.isInitialized,
+ // These fields are no longer valid because they were already committed.
+ // Reset them.
+ callbackList: null,
+ hasForceUpdate: false
+ };
+ }
- case REACT_CALL_TYPE: {
- var _matchedFiber2 =
- existingChildren.get(
- newChild.key === null ? newIdx : newChild.key
- ) || null;
- return updateCall(
- returnFiber,
- _matchedFiber2,
- newChild,
- expirationTime
- );
- }
+ {
+ // Set this flag so we can warn if setState is called inside the update
+ // function of another setState.
+ queue.isProcessing = true;
+ }
- case REACT_RETURN_TYPE: {
- // Returns don't have keys, so we neither have to check the old nor
- // new node for the key. If both are returns, they match.
- var _matchedFiber3 = existingChildren.get(newIdx) || null;
- return updateReturn(
- returnFiber,
- _matchedFiber3,
- newChild,
- expirationTime
- );
- }
+ // Reset the remaining expiration time. If we skip over any updates, we'll
+ // increase this accordingly.
+ queue.expirationTime = NoWork;
- case REACT_PORTAL_TYPE: {
- var _matchedFiber4 =
- existingChildren.get(
- newChild.key === null ? newIdx : newChild.key
- ) || null;
- return updatePortal(
- returnFiber,
- _matchedFiber4,
- newChild,
- expirationTime
- );
- }
+ // TODO: We don't know what the base state will be until we begin work.
+ // It depends on which fiber is the next current. Initialize with an empty
+ // base state, then set to the memoizedState when rendering. Not super
+ // happy with this approach.
+ var state = void 0;
+ if (queue.isInitialized) {
+ state = queue.baseState;
+ } else {
+ state = queue.baseState = workInProgress.memoizedState;
+ queue.isInitialized = true;
+ }
+ var dontMutatePrevState = true;
+ var update = queue.first;
+ var didSkip = false;
+ while (update !== null) {
+ var updateExpirationTime = update.expirationTime;
+ if (updateExpirationTime > renderExpirationTime) {
+ // This update does not have sufficient priority. Skip it.
+ var remainingExpirationTime = queue.expirationTime;
+ if (
+ remainingExpirationTime === NoWork ||
+ remainingExpirationTime > updateExpirationTime
+ ) {
+ // Update the remaining expiration time.
+ queue.expirationTime = updateExpirationTime;
}
-
- if (isArray$1(newChild) || getIteratorFn(newChild)) {
- var _matchedFiber5 = existingChildren.get(newIdx) || null;
- return updateFragment(
- returnFiber,
- _matchedFiber5,
- newChild,
- expirationTime,
- null
- );
+ if (!didSkip) {
+ didSkip = true;
+ queue.baseState = state;
}
-
- throwOnInvalidObjectType(returnFiber, newChild);
+ // Continue to the next update.
+ update = update.next;
+ continue;
}
- {
- if (typeof newChild === "function") {
- warnOnFunctionType();
+ // This update does have sufficient priority.
+
+ // If no previous updates were skipped, drop this update from the queue by
+ // advancing the head of the list.
+ if (!didSkip) {
+ queue.first = update.next;
+ if (queue.first === null) {
+ queue.last = null;
}
}
- return null;
- }
+ // Invoke setState callback an extra time to help detect side-effects.
+ // Ignore the return value in this case.
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & StrictMode)
+ ) {
+ getStateFromUpdate(update, instance, state, props);
+ }
- /**
- * Warns if there is a duplicate or missing key
- */
- function warnOnInvalidKey(child, knownKeys) {
- {
- if (typeof child !== "object" || child === null) {
- return knownKeys;
- }
- switch (child.$$typeof) {
- case REACT_ELEMENT_TYPE:
- case REACT_CALL_TYPE:
- case REACT_PORTAL_TYPE:
- warnForMissingKey(child);
- var key = child.key;
- if (typeof key !== "string") {
- break;
- }
- if (knownKeys === null) {
- knownKeys = new Set();
- knownKeys.add(key);
- break;
- }
- if (!knownKeys.has(key)) {
- knownKeys.add(key);
- break;
- }
- warning(
- false,
- "Encountered two children with the same key, `%s`. " +
- "Keys should be unique so that components maintain their identity " +
- "across updates. Non-unique keys may cause children to be " +
- "duplicated and/or omitted — the behavior is unsupported and " +
- "could change in a future version.%s",
- key,
- getCurrentFiberStackAddendum$1()
- );
- break;
- default:
- break;
+ // Process the update
+ var _partialState = void 0;
+ if (update.isReplace) {
+ state = getStateFromUpdate(update, instance, state, props);
+ dontMutatePrevState = true;
+ } else {
+ _partialState = getStateFromUpdate(update, instance, state, props);
+ if (_partialState) {
+ if (dontMutatePrevState) {
+ // $FlowFixMe: Idk how to type this properly.
+ state = Object.assign({}, state, _partialState);
+ } else {
+ state = Object.assign(state, _partialState);
+ }
+ dontMutatePrevState = false;
}
}
- return knownKeys;
+ if (update.isForced) {
+ queue.hasForceUpdate = true;
+ }
+ if (update.callback !== null) {
+ // Append to list of callbacks.
+ var _callbackList = queue.callbackList;
+ if (_callbackList === null) {
+ _callbackList = queue.callbackList = [];
+ }
+ _callbackList.push(update);
+ }
+ update = update.next;
}
- function reconcileChildrenArray(
- returnFiber,
- currentFirstChild,
- newChildren,
- expirationTime
- ) {
- // This algorithm can't optimize by searching from boths ends since we
- // don't have backpointers on fibers. I'm trying to see how far we can get
- // with that model. If it ends up not being worth the tradeoffs, we can
- // add it later.
-
- // Even with a two ended optimization, we'd want to optimize for the case
- // where there are few changes and brute force the comparison instead of
- // going for the Map. It'd like to explore hitting that path first in
- // forward-only mode and only go for the Map once we notice that we need
- // lots of look ahead. This doesn't handle reversal as well as two ended
- // search but that's unusual. Besides, for the two ended optimization to
- // work on Iterables, we'd need to copy the whole set.
+ if (queue.callbackList !== null) {
+ workInProgress.effectTag |= Callback;
+ } else if (queue.first === null && !queue.hasForceUpdate) {
+ // The queue is empty. We can reset it.
+ workInProgress.updateQueue = null;
+ }
- // In this first iteration, we'll just live with hitting the bad case
- // (adding everything to a Map) in for every insert/move.
+ if (!didSkip) {
+ didSkip = true;
+ queue.baseState = state;
+ }
- // If you change this code, also update reconcileChildrenIterator() which
- // uses the same algorithm.
+ {
+ // No longer processing.
+ queue.isProcessing = false;
+ }
- {
- // First, validate keys.
- var knownKeys = null;
- for (var i = 0; i < newChildren.length; i++) {
- var child = newChildren[i];
- knownKeys = warnOnInvalidKey(child, knownKeys);
- }
- }
+ return state;
+}
- var resultingFirstChild = null;
- var previousNewFiber = null;
+function commitCallbacks(queue, context) {
+ var callbackList = queue.callbackList;
+ if (callbackList === null) {
+ return;
+ }
+ // Set the list to null to make sure they don't get called more than once.
+ queue.callbackList = null;
+ for (var i = 0; i < callbackList.length; i++) {
+ var update = callbackList[i];
+ var _callback = update.callback;
+ // This update might be processed again. Clear the callback so it's only
+ // called once.
+ update.callback = null;
+ invariant(
+ typeof _callback === "function",
+ "Invalid argument passed as callback. Expected a function. Instead " +
+ "received: %s",
+ _callback
+ );
+ _callback.call(context);
+ }
+}
- var oldFiber = currentFirstChild;
- var lastPlacedIndex = 0;
- var newIdx = 0;
- var nextOldFiber = null;
- for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
- if (oldFiber.index > newIdx) {
- nextOldFiber = oldFiber;
- oldFiber = null;
- } else {
- nextOldFiber = oldFiber.sibling;
- }
- var newFiber = updateSlot(
- returnFiber,
- oldFiber,
- newChildren[newIdx],
- expirationTime
- );
- if (newFiber === null) {
- // TODO: This breaks on empty slots like null children. That's
- // unfortunate because it triggers the slow path all the time. We need
- // a better way to communicate whether this was a miss or null,
- // boolean, undefined, etc.
- if (oldFiber === null) {
- oldFiber = nextOldFiber;
- }
- break;
- }
- if (shouldTrackSideEffects) {
- if (oldFiber && newFiber.alternate === null) {
- // We matched the slot, but we didn't reuse the existing fiber, so we
- // need to delete the existing child.
- deleteChild(returnFiber, oldFiber);
- }
- }
- lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = newFiber;
- } else {
- // TODO: Defer siblings if we're not at the right index for this slot.
- // I.e. if we had null values before, then we want to defer this
- // for each null value. However, we also don't want to call updateSlot
- // with the previous one.
- previousNewFiber.sibling = newFiber;
- }
- previousNewFiber = newFiber;
- oldFiber = nextOldFiber;
- }
+var fakeInternalInstance = {};
+var isArray = Array.isArray;
- if (newIdx === newChildren.length) {
- // We've reached the end of the new children. We can delete the rest.
- deleteRemainingChildren(returnFiber, oldFiber);
- return resultingFirstChild;
- }
+var didWarnAboutStateAssignmentForComponent = void 0;
+var didWarnAboutUndefinedDerivedState = void 0;
+var didWarnAboutUninitializedState = void 0;
+var didWarnAboutWillReceivePropsAndDerivedState = void 0;
+var warnOnInvalidCallback = void 0;
- if (oldFiber === null) {
- // If we don't have any more existing children we can choose a fast path
- // since the rest will all be insertions.
- for (; newIdx < newChildren.length; newIdx++) {
- var _newFiber = createChild(
- returnFiber,
- newChildren[newIdx],
- expirationTime
- );
- if (!_newFiber) {
- continue;
- }
- lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = _newFiber;
- } else {
- previousNewFiber.sibling = _newFiber;
- }
- previousNewFiber = _newFiber;
- }
- return resultingFirstChild;
- }
+{
+ didWarnAboutStateAssignmentForComponent = {};
+ didWarnAboutUndefinedDerivedState = {};
+ didWarnAboutUninitializedState = {};
+ didWarnAboutWillReceivePropsAndDerivedState = {};
- // Add all children to a key map for quick lookups.
- var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+ var didWarnOnInvalidCallback = {};
- // Keep scanning and use the map to restore deleted items as moves.
- for (; newIdx < newChildren.length; newIdx++) {
- var _newFiber2 = updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- newChildren[newIdx],
- expirationTime
- );
- if (_newFiber2) {
- if (shouldTrackSideEffects) {
- if (_newFiber2.alternate !== null) {
- // The new fiber is a work in progress, but if there exists a
- // current, that means that we reused the fiber. We need to delete
- // it from the child list so that we don't add it to the deletion
- // list.
- existingChildren["delete"](
- _newFiber2.key === null ? newIdx : _newFiber2.key
- );
- }
- }
- lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- resultingFirstChild = _newFiber2;
- } else {
- previousNewFiber.sibling = _newFiber2;
- }
- previousNewFiber = _newFiber2;
- }
+ warnOnInvalidCallback = function(callback, callerName) {
+ if (callback === null || typeof callback === "function") {
+ return;
}
-
- 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 key = callerName + "_" + callback;
+ if (!didWarnOnInvalidCallback[key]) {
+ warning(
+ false,
+ "%s(...): Expected the last optional `callback` argument to be a " +
+ "function. Instead received: %s.",
+ callerName,
+ callback
+ );
+ didWarnOnInvalidCallback[key] = true;
}
+ };
- return resultingFirstChild;
- }
-
- function reconcileChildrenIterator(
- returnFiber,
- currentFirstChild,
- newChildrenIterable,
- expirationTime
- ) {
- // This is the same implementation as reconcileChildrenArray(),
- // but using the iterator instead.
-
- var iteratorFn = getIteratorFn(newChildrenIterable);
- invariant(
- typeof iteratorFn === "function",
- "An object is not an iterable. This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
+ // This is so gross but it's at least non-critical and can be removed if
+ // it causes problems. This is meant to give a nicer error message for
+ // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
+ // ...)) which otherwise throws a "_processChildContext is not a function"
+ // exception.
+ Object.defineProperty(fakeInternalInstance, "_processChildContext", {
+ enumerable: false,
+ value: function() {
+ invariant(
+ false,
+ "_processChildContext is not available in React 16+. This likely " +
+ "means you have multiple copies of React and are attempting to nest " +
+ "a React 15 tree inside a React 16 tree using " +
+ "unstable_renderSubtreeIntoContainer, which isn't supported. Try " +
+ "to make sure you have only one copy of React (and ideally, switch " +
+ "to ReactDOM.createPortal)."
+ );
+ }
+ });
+ Object.freeze(fakeInternalInstance);
+}
- {
- // Warn about using Maps as children
- if (typeof newChildrenIterable.entries === "function") {
- var possibleMap = newChildrenIterable;
- if (possibleMap.entries === iteratorFn) {
- warning(
- didWarnAboutMaps,
- "Using Maps as children is unsupported and will likely yield " +
- "unexpected results. Convert it to a sequence/iterable of keyed " +
- "ReactElements instead.%s",
- getCurrentFiberStackAddendum$1()
- );
- didWarnAboutMaps = true;
- }
+var ReactFiberClassComponent = function(
+ scheduleWork,
+ computeExpirationForFiber,
+ memoizeProps,
+ memoizeState
+) {
+ // Class component state updater
+ var updater = {
+ isMounted: isMounted,
+ enqueueSetState: function(instance, partialState, callback) {
+ var fiber = get(instance);
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, "setState");
}
-
- // First, validate keys.
- // We'll get a different iterator later for the main pass.
- var _newChildren = iteratorFn.call(newChildrenIterable);
- if (_newChildren) {
- var knownKeys = null;
- var _step = _newChildren.next();
- for (; !_step.done; _step = _newChildren.next()) {
- var child = _step.value;
- knownKeys = warnOnInvalidKey(child, knownKeys);
- }
+ var expirationTime = computeExpirationForFiber(fiber);
+ var update = {
+ expirationTime: expirationTime,
+ partialState: partialState,
+ callback: callback,
+ isReplace: false,
+ isForced: false,
+ nextCallback: null,
+ next: null
+ };
+ insertUpdateIntoFiber(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueReplaceState: function(instance, state, callback) {
+ var fiber = get(instance);
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, "replaceState");
+ }
+ var expirationTime = computeExpirationForFiber(fiber);
+ var update = {
+ expirationTime: expirationTime,
+ partialState: state,
+ callback: callback,
+ isReplace: true,
+ isForced: false,
+ nextCallback: null,
+ next: null
+ };
+ insertUpdateIntoFiber(fiber, update);
+ scheduleWork(fiber, expirationTime);
+ },
+ enqueueForceUpdate: function(instance, callback) {
+ var fiber = get(instance);
+ callback = callback === undefined ? null : callback;
+ {
+ warnOnInvalidCallback(callback, "forceUpdate");
}
+ var expirationTime = computeExpirationForFiber(fiber);
+ var update = {
+ expirationTime: expirationTime,
+ partialState: null,
+ callback: callback,
+ isReplace: false,
+ isForced: true,
+ nextCallback: null,
+ next: null
+ };
+ insertUpdateIntoFiber(fiber, update);
+ scheduleWork(fiber, expirationTime);
}
+ };
- var newChildren = iteratorFn.call(newChildrenIterable);
- invariant(newChildren != null, "An iterable object provided no iterator.");
-
- var resultingFirstChild = null;
- var previousNewFiber = null;
-
- var oldFiber = currentFirstChild;
- var lastPlacedIndex = 0;
- var newIdx = 0;
- var nextOldFiber = null;
-
- var step = newChildren.next();
- for (
- ;
- oldFiber !== null && !step.done;
- newIdx++, step = newChildren.next()
+ function checkShouldComponentUpdate(
+ workInProgress,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ newContext
+ ) {
+ if (
+ oldProps === null ||
+ (workInProgress.updateQueue !== null &&
+ workInProgress.updateQueue.hasForceUpdate)
) {
- if (oldFiber.index > newIdx) {
- nextOldFiber = oldFiber;
- oldFiber = null;
- } else {
- nextOldFiber = oldFiber.sibling;
- }
- var newFiber = updateSlot(
- returnFiber,
- oldFiber,
- step.value,
- expirationTime
+ // If the workInProgress already has an Update effect, return true
+ return true;
+ }
+
+ var instance = workInProgress.stateNode;
+ var type = workInProgress.type;
+ if (typeof instance.shouldComponentUpdate === "function") {
+ startPhaseTimer(workInProgress, "shouldComponentUpdate");
+ var shouldUpdate = instance.shouldComponentUpdate(
+ newProps,
+ newState,
+ newContext
);
- if (newFiber === null) {
- // TODO: This breaks on empty slots like null children. That's
- // unfortunate because it triggers the slow path all the time. We need
- // a better way to communicate whether this was a miss or null,
- // boolean, undefined, etc.
- if (!oldFiber) {
- oldFiber = nextOldFiber;
- }
- break;
- }
- if (shouldTrackSideEffects) {
- if (oldFiber && newFiber.alternate === null) {
- // We matched the slot, but we didn't reuse the existing fiber, so we
- // need to delete the existing child.
- deleteChild(returnFiber, oldFiber);
- }
- }
- lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = newFiber;
- } else {
- // TODO: Defer siblings if we're not at the right index for this slot.
- // I.e. if we had null values before, then we want to defer this
- // for each null value. However, we also don't want to call updateSlot
- // with the previous one.
- previousNewFiber.sibling = newFiber;
+ stopPhaseTimer();
+
+ {
+ warning(
+ shouldUpdate !== undefined,
+ "%s.shouldComponentUpdate(): Returned undefined instead of a " +
+ "boolean value. Make sure to return true or false.",
+ getComponentName(workInProgress) || "Unknown"
+ );
}
- previousNewFiber = newFiber;
- oldFiber = nextOldFiber;
- }
- if (step.done) {
- // We've reached the end of the new children. We can delete the rest.
- deleteRemainingChildren(returnFiber, oldFiber);
- return resultingFirstChild;
+ return shouldUpdate;
}
- if (oldFiber === null) {
- // If we don't have any more existing children we can choose a fast path
- // since the rest will all be insertions.
- for (; !step.done; newIdx++, step = newChildren.next()) {
- var _newFiber3 = createChild(returnFiber, step.value, expirationTime);
- if (_newFiber3 === null) {
- continue;
- }
- lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- // TODO: Move out of the loop. This only happens for the first run.
- resultingFirstChild = _newFiber3;
- } else {
- previousNewFiber.sibling = _newFiber3;
- }
- previousNewFiber = _newFiber3;
- }
- return resultingFirstChild;
+ if (type.prototype && type.prototype.isPureReactComponent) {
+ return (
+ !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
+ );
}
- // Add all children to a key map for quick lookups.
- var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+ return true;
+ }
- // Keep scanning and use the map to restore deleted items as moves.
- for (; !step.done; newIdx++, step = newChildren.next()) {
- var _newFiber4 = updateFromMap(
- existingChildren,
- returnFiber,
- newIdx,
- step.value,
- expirationTime
- );
- if (_newFiber4 !== null) {
- if (shouldTrackSideEffects) {
- if (_newFiber4.alternate !== null) {
- // The new fiber is a work in progress, but if there exists a
- // current, that means that we reused the fiber. We need to delete
- // it from the child list so that we don't add it to the deletion
- // list.
- existingChildren["delete"](
- _newFiber4.key === null ? newIdx : _newFiber4.key
- );
- }
- }
- lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
- if (previousNewFiber === null) {
- resultingFirstChild = _newFiber4;
+ function checkClassInstance(workInProgress) {
+ var instance = workInProgress.stateNode;
+ var type = workInProgress.type;
+ {
+ var name = getComponentName(workInProgress);
+ var renderPresent = instance.render;
+
+ if (!renderPresent) {
+ if (type.prototype && typeof type.prototype.render === "function") {
+ warning(
+ false,
+ "%s(...): No `render` method found on the returned component " +
+ "instance: did you accidentally return an object from the constructor?",
+ name
+ );
} else {
- previousNewFiber.sibling = _newFiber4;
+ warning(
+ false,
+ "%s(...): No `render` method found on the returned component " +
+ "instance: you may have forgotten to define `render`.",
+ name
+ );
}
- previousNewFiber = _newFiber4;
}
+
+ var noGetInitialStateOnES6 =
+ !instance.getInitialState ||
+ instance.getInitialState.isReactClassApproved ||
+ instance.state;
+ warning(
+ noGetInitialStateOnES6,
+ "getInitialState was defined on %s, a plain JavaScript class. " +
+ "This is only supported for classes created using React.createClass. " +
+ "Did you mean to define a state property instead?",
+ name
+ );
+ var noGetDefaultPropsOnES6 =
+ !instance.getDefaultProps ||
+ instance.getDefaultProps.isReactClassApproved;
+ warning(
+ noGetDefaultPropsOnES6,
+ "getDefaultProps was defined on %s, a plain JavaScript class. " +
+ "This is only supported for classes created using React.createClass. " +
+ "Use a static property to define defaultProps instead.",
+ name
+ );
+ var noInstancePropTypes = !instance.propTypes;
+ warning(
+ noInstancePropTypes,
+ "propTypes was defined as an instance property on %s. Use a static " +
+ "property to define propTypes instead.",
+ name
+ );
+ var noInstanceContextTypes = !instance.contextTypes;
+ warning(
+ noInstanceContextTypes,
+ "contextTypes was defined as an instance property on %s. Use a static " +
+ "property to define contextTypes instead.",
+ name
+ );
+ var noComponentShouldUpdate =
+ typeof instance.componentShouldUpdate !== "function";
+ warning(
+ noComponentShouldUpdate,
+ "%s has a method called " +
+ "componentShouldUpdate(). Did you mean shouldComponentUpdate()? " +
+ "The name is phrased as a question because the function is " +
+ "expected to return a value.",
+ name
+ );
+ if (
+ type.prototype &&
+ type.prototype.isPureReactComponent &&
+ typeof instance.shouldComponentUpdate !== "undefined"
+ ) {
+ warning(
+ false,
+ "%s has a method called shouldComponentUpdate(). " +
+ "shouldComponentUpdate should not be used when extending React.PureComponent. " +
+ "Please extend React.Component if shouldComponentUpdate is used.",
+ getComponentName(workInProgress) || "A pure component"
+ );
+ }
+ var noComponentDidUnmount =
+ typeof instance.componentDidUnmount !== "function";
+ warning(
+ noComponentDidUnmount,
+ "%s has a method called " +
+ "componentDidUnmount(). But there is no such lifecycle method. " +
+ "Did you mean componentWillUnmount()?",
+ name
+ );
+ var noComponentDidReceiveProps =
+ typeof instance.componentDidReceiveProps !== "function";
+ warning(
+ noComponentDidReceiveProps,
+ "%s has a method called " +
+ "componentDidReceiveProps(). But there is no such lifecycle method. " +
+ "If you meant to update the state in response to changing props, " +
+ "use componentWillReceiveProps(). If you meant to fetch data or " +
+ "run side-effects or mutations after React has updated the UI, use componentDidUpdate().",
+ name
+ );
+ var noComponentWillRecieveProps =
+ typeof instance.componentWillRecieveProps !== "function";
+ warning(
+ noComponentWillRecieveProps,
+ "%s has a method called " +
+ "componentWillRecieveProps(). Did you mean componentWillReceiveProps()?",
+ name
+ );
+ var noUnsafeComponentWillRecieveProps =
+ typeof instance.UNSAFE_componentWillRecieveProps !== "function";
+ warning(
+ noUnsafeComponentWillRecieveProps,
+ "%s has a method called " +
+ "UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?",
+ name
+ );
+ var hasMutatedProps = instance.props !== workInProgress.pendingProps;
+ warning(
+ instance.props === undefined || !hasMutatedProps,
+ "%s(...): When calling super() in `%s`, make sure to pass " +
+ "up the same props that your component's constructor was passed.",
+ name,
+ name
+ );
+ var noInstanceDefaultProps = !instance.defaultProps;
+ warning(
+ noInstanceDefaultProps,
+ "Setting defaultProps as an instance property on %s is not supported and will be ignored." +
+ " Instead, define defaultProps as a static property on %s.",
+ name,
+ name
+ );
}
- 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 state = instance.state;
+ if (state && (typeof state !== "object" || isArray(state))) {
+ warning(
+ false,
+ "%s.state: must be set to an object or null",
+ getComponentName(workInProgress)
+ );
+ }
+ if (typeof instance.getChildContext === "function") {
+ warning(
+ typeof workInProgress.type.childContextTypes === "object",
+ "%s.getChildContext(): childContextTypes must be defined in order to " +
+ "use getChildContext().",
+ getComponentName(workInProgress)
+ );
}
+ }
- return resultingFirstChild;
+ function resetInputPointers(workInProgress, instance) {
+ instance.props = workInProgress.memoizedProps;
+ instance.state = workInProgress.memoizedState;
}
- function reconcileSingleTextNode(
- returnFiber,
- currentFirstChild,
- textContent,
- expirationTime
- ) {
- // There's no need to check for keys on text nodes since we don't have a
- // way to define them.
- if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
- // We already have an existing node so let's just update it and delete
- // the rest.
- deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
- var existing = useFiber(currentFirstChild, textContent, expirationTime);
- existing["return"] = returnFiber;
- return existing;
+ function adoptClassInstance(workInProgress, instance) {
+ instance.updater = updater;
+ workInProgress.stateNode = instance;
+ // The instance needs access to the fiber so that it can schedule updates
+ set(instance, workInProgress);
+ {
+ instance._reactInternalInstance = fakeInternalInstance;
}
- // The existing first child is not a text node so we need to create one
- // and delete the existing ones.
- deleteRemainingChildren(returnFiber, currentFirstChild);
- var created = createFiberFromText(
- textContent,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
}
- function reconcileSingleElement(
- returnFiber,
- currentFirstChild,
- element,
- expirationTime
- ) {
- var key = element.key;
- var child = currentFirstChild;
- while (child !== null) {
- // TODO: If key === null and child.key === null, then this only applies to
- // the first item in the list.
- if (child.key === key) {
- if (
- child.tag === Fragment
- ? element.type === REACT_FRAGMENT_TYPE
- : child.type === element.type
- ) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(
- child,
- element.type === REACT_FRAGMENT_TYPE
- ? element.props.children
- : element.props,
- expirationTime
+ function constructClassInstance(workInProgress, props) {
+ var ctor = workInProgress.type;
+ var unmaskedContext = getUnmaskedContext(workInProgress);
+ var needsContext = isContextConsumer(workInProgress);
+ var context = needsContext
+ ? getMaskedContext(workInProgress, unmaskedContext)
+ : emptyObject;
+
+ // Instantiate twice to help detect side-effects.
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & StrictMode)
+ ) {
+ new ctor(props, context); // eslint-disable-line no-new
+ }
+
+ var instance = new ctor(props, context);
+ var state =
+ instance.state !== null && instance.state !== undefined
+ ? instance.state
+ : null;
+ adoptClassInstance(workInProgress, instance);
+
+ {
+ if (
+ typeof ctor.getDerivedStateFromProps === "function" &&
+ state === null
+ ) {
+ var componentName = getComponentName(workInProgress) || "Unknown";
+ if (!didWarnAboutUninitializedState[componentName]) {
+ warning(
+ false,
+ "%s: Did not properly initialize state during construction. " +
+ "Expected state to be an object, but it was %s.",
+ componentName,
+ instance.state === null ? "null" : "undefined"
);
- existing.ref = coerceRef(child, element);
- existing["return"] = returnFiber;
- {
- existing._debugSource = element._source;
- existing._debugOwner = element._owner;
- }
- return existing;
- } else {
- deleteRemainingChildren(returnFiber, child);
- break;
+ didWarnAboutUninitializedState[componentName] = true;
}
- } else {
- deleteChild(returnFiber, child);
}
- child = child.sibling;
}
- if (element.type === REACT_FRAGMENT_TYPE) {
- var created = createFiberFromFragment(
- element.props.children,
- returnFiber.internalContextTag,
- expirationTime,
- element.key
- );
- created["return"] = returnFiber;
- return created;
- } else {
- var _created7 = createFiberFromElement(
- element,
- returnFiber.internalContextTag,
- expirationTime
+ workInProgress.memoizedState = state;
+
+ var partialState = callGetDerivedStateFromProps(
+ workInProgress,
+ instance,
+ props
+ );
+
+ if (partialState !== null && partialState !== undefined) {
+ // Render-phase updates (like this) should not be added to the update queue,
+ // So that multiple render passes do not enqueue multiple updates.
+ // Instead, just synchronously merge the returned state into the instance.
+ workInProgress.memoizedState = Object.assign(
+ {},
+ workInProgress.memoizedState,
+ partialState
);
- _created7.ref = coerceRef(currentFirstChild, element);
- _created7["return"] = returnFiber;
- return _created7;
}
- }
- function reconcileSingleCall(
- returnFiber,
- currentFirstChild,
- call,
- expirationTime
- ) {
- var key = call.key;
- var child = currentFirstChild;
- while (child !== null) {
- // TODO: If key === null and child.key === null, then this only applies to
- // the first item in the list.
- if (child.key === key) {
- if (child.tag === CallComponent) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(child, call, expirationTime);
- existing["return"] = returnFiber;
- return existing;
- } else {
- deleteRemainingChildren(returnFiber, child);
- break;
- }
- } else {
- deleteChild(returnFiber, child);
- }
- child = child.sibling;
+ // Cache unmasked context so we can avoid recreating masked context unless necessary.
+ // ReactFiberContext usually updates this cache but can't for newly-created instances.
+ if (needsContext) {
+ cacheContext(workInProgress, unmaskedContext, context);
}
- var created = createFiberFromCall(
- call,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
+ return instance;
}
- function reconcileSingleReturn(
- returnFiber,
- currentFirstChild,
- returnNode,
- expirationTime
- ) {
- // There's no need to check for keys on yields since they're stateless.
- var child = currentFirstChild;
- if (child !== null) {
- if (child.tag === ReturnComponent) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(child, null, expirationTime);
- existing.type = returnNode.value;
- existing["return"] = returnFiber;
- return existing;
- } else {
- deleteRemainingChildren(returnFiber, child);
- }
+ function callComponentWillMount(workInProgress, instance) {
+ startPhaseTimer(workInProgress, "componentWillMount");
+ var oldState = instance.state;
+
+ if (typeof instance.componentWillMount === "function") {
+ instance.componentWillMount();
+ } else {
+ instance.UNSAFE_componentWillMount();
}
- var created = createFiberFromReturn(
- returnNode,
- returnFiber.internalContextTag,
- expirationTime
- );
- created.type = returnNode.value;
- created["return"] = returnFiber;
- return created;
- }
+ stopPhaseTimer();
- function reconcileSinglePortal(
- returnFiber,
- currentFirstChild,
- portal,
- expirationTime
- ) {
- var key = portal.key;
- var child = currentFirstChild;
- while (child !== null) {
- // TODO: If key === null and child.key === null, then this only applies to
- // the first item in the list.
- if (child.key === key) {
- if (
- child.tag === HostPortal &&
- child.stateNode.containerInfo === portal.containerInfo &&
- child.stateNode.implementation === portal.implementation
- ) {
- deleteRemainingChildren(returnFiber, child.sibling);
- var existing = useFiber(child, portal.children || [], expirationTime);
- existing["return"] = returnFiber;
- return existing;
- } else {
- deleteRemainingChildren(returnFiber, child);
- break;
- }
- } else {
- deleteChild(returnFiber, child);
+ if (oldState !== instance.state) {
+ {
+ warning(
+ false,
+ "%s.componentWillMount(): Assigning directly to this.state is " +
+ "deprecated (except inside a component's " +
+ "constructor). Use setState instead.",
+ getComponentName(workInProgress)
+ );
}
- child = child.sibling;
+ updater.enqueueReplaceState(instance, instance.state, null);
}
-
- var created = createFiberFromPortal(
- portal,
- returnFiber.internalContextTag,
- expirationTime
- );
- created["return"] = returnFiber;
- return created;
}
- // This API will tag the children with the side-effect of the reconciliation
- // itself. They will be added to the side-effect list as we pass through the
- // children and the parent.
- function reconcileChildFibers(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
+ function callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ newContext
) {
- // 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.
+ var oldState = instance.state;
+ if (typeof instance.componentWillReceiveProps === "function") {
+ startPhaseTimer(workInProgress, "componentWillReceiveProps");
+ instance.componentWillReceiveProps(newProps, newContext);
+ stopPhaseTimer();
+ } else {
+ startPhaseTimer(workInProgress, "componentWillReceiveProps");
+ instance.UNSAFE_componentWillReceiveProps(newProps, newContext);
+ stopPhaseTimer();
+ }
- // Handle top level unkeyed fragments as if they were arrays.
- // This leads to an ambiguity between <>{[...]}> and <>...>.
- // We treat the ambiguous cases above the same.
- if (
- typeof newChild === "object" &&
- newChild !== null &&
- newChild.type === REACT_FRAGMENT_TYPE &&
- newChild.key === null
- ) {
- newChild = newChild.props.children;
+ if (instance.state !== oldState) {
+ {
+ var componentName = getComponentName(workInProgress) || "Component";
+ if (!didWarnAboutStateAssignmentForComponent[componentName]) {
+ warning(
+ false,
+ "%s.componentWillReceiveProps(): Assigning directly to " +
+ "this.state is deprecated (except inside a component's " +
+ "constructor). Use setState instead.",
+ componentName
+ );
+ didWarnAboutStateAssignmentForComponent[componentName] = true;
+ }
+ }
+ updater.enqueueReplaceState(instance, instance.state, null);
}
+ }
- // Handle object types
- var isObject = typeof newChild === "object" && newChild !== null;
+ function callGetDerivedStateFromProps(workInProgress, instance, props) {
+ var type = workInProgress.type;
- if (isObject) {
- switch (newChild.$$typeof) {
- case REACT_ELEMENT_TYPE:
- return placeSingleChild(
- reconcileSingleElement(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- )
- );
+ if (typeof type.getDerivedStateFromProps === "function") {
+ {
+ // Don't warn about react-lifecycles-compat polyfilled components
+ if (
+ (typeof instance.componentWillReceiveProps === "function" &&
+ instance.componentWillReceiveProps.__suppressDeprecationWarning !==
+ true) ||
+ typeof instance.UNSAFE_componentWillReceiveProps === "function"
+ ) {
+ var componentName = getComponentName(workInProgress) || "Unknown";
+ if (!didWarnAboutWillReceivePropsAndDerivedState[componentName]) {
+ warning(
+ false,
+ "%s: Defines both componentWillReceiveProps() and static " +
+ "getDerivedStateFromProps() methods. We recommend using " +
+ "only getDerivedStateFromProps().",
+ componentName
+ );
+ didWarnAboutWillReceivePropsAndDerivedState[componentName] = true;
+ }
+ }
+ }
- case REACT_CALL_TYPE:
- return placeSingleChild(
- reconcileSingleCall(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- )
- );
- case REACT_RETURN_TYPE:
- return placeSingleChild(
- reconcileSingleReturn(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- )
- );
- case REACT_PORTAL_TYPE:
- return placeSingleChild(
- reconcileSinglePortal(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- )
- );
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & StrictMode)
+ ) {
+ // Invoke method an extra time to help detect side-effects.
+ type.getDerivedStateFromProps.call(
+ null,
+ props,
+ workInProgress.memoizedState
+ );
}
- }
- if (typeof newChild === "string" || typeof newChild === "number") {
- return placeSingleChild(
- reconcileSingleTextNode(
- returnFiber,
- currentFirstChild,
- "" + newChild,
- expirationTime
- )
+ var partialState = type.getDerivedStateFromProps.call(
+ null,
+ props,
+ workInProgress.memoizedState
);
- }
- if (isArray$1(newChild)) {
- return reconcileChildrenArray(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- );
- }
+ {
+ if (partialState === undefined) {
+ var _componentName = getComponentName(workInProgress) || "Unknown";
+ if (!didWarnAboutUndefinedDerivedState[_componentName]) {
+ warning(
+ false,
+ "%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. " +
+ "You have returned undefined.",
+ _componentName
+ );
+ didWarnAboutUndefinedDerivedState[_componentName] = _componentName;
+ }
+ }
+ }
- if (getIteratorFn(newChild)) {
- return reconcileChildrenIterator(
- returnFiber,
- currentFirstChild,
- newChild,
- expirationTime
- );
+ return partialState;
}
+ }
- if (isObject) {
- throwOnInvalidObjectType(returnFiber, newChild);
- }
+ // Invokes the mount life-cycles on a previously never rendered instance.
+ function mountClassInstance(workInProgress, renderExpirationTime) {
+ var current = workInProgress.alternate;
{
- if (typeof newChild === "function") {
- warnOnFunctionType();
- }
+ checkClassInstance(workInProgress);
}
- if (typeof newChild === "undefined") {
- // If the new child is undefined, and the return fiber is a composite
- // component, throw an error. If Fiber return types are disabled,
- // we already threw above.
- switch (returnFiber.tag) {
- case ClassComponent: {
- {
- var instance = returnFiber.stateNode;
- if (instance.render._isMockFunction) {
- // We allow auto-mocks to proceed as if they're returning null.
- break;
- }
- }
- }
- // Intentionally fall through to the next case, which handles both
- // functions and classes
- // eslint-disable-next-lined no-fallthrough
- case FunctionalComponent: {
- var Component = returnFiber.type;
- invariant(
- false,
- "%s(...): Nothing was returned from render. This usually means a " +
- "return statement is missing. Or, to render nothing, " +
- "return null.",
- Component.displayName || Component.name || "Component"
- );
+
+ var instance = workInProgress.stateNode;
+ var props = workInProgress.pendingProps;
+ var unmaskedContext = getUnmaskedContext(workInProgress);
+
+ instance.props = props;
+ instance.state = workInProgress.memoizedState;
+ instance.refs = emptyObject;
+ instance.context = getMaskedContext(workInProgress, unmaskedContext);
+
+ if (workInProgress.type != null && workInProgress.type.prototype != null) {
+ var prototype = workInProgress.type.prototype;
+
+ if (enableAsyncSubtreeAPI) {
+ if (prototype.unstable_isAsyncReactComponent === true) {
+ workInProgress.internalContextTag |= AsyncUpdates;
+ workInProgress.internalContextTag |= StrictMode;
}
}
}
- // Remaining cases are all treated as empty.
- return deleteRemainingChildren(returnFiber, currentFirstChild);
+ {
+ if (workInProgress.internalContextTag & StrictMode) {
+ ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(
+ workInProgress,
+ instance
+ );
+ }
+
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.recordDeprecationWarnings(
+ workInProgress,
+ instance
+ );
+ }
+ }
+
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ if (
+ (typeof instance.UNSAFE_componentWillMount === "function" ||
+ typeof instance.componentWillMount === "function") &&
+ typeof workInProgress.type.getDerivedStateFromProps !== "function"
+ ) {
+ callComponentWillMount(workInProgress, instance);
+ // If we had additional state updates during this life-cycle, let's
+ // process them now.
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ instance.state = processUpdateQueue(
+ current,
+ workInProgress,
+ updateQueue,
+ instance,
+ props,
+ renderExpirationTime
+ );
+ }
+ }
+ if (typeof instance.componentDidMount === "function") {
+ workInProgress.effectTag |= Update;
+ }
}
- return reconcileChildFibers;
-}
+ // Called on a preexisting class instance. Returns false if a resumed render
+ // could be reused.
+ // function resumeMountClassInstance(
+ // workInProgress: Fiber,
+ // priorityLevel: PriorityLevel,
+ // ): boolean {
+ // const instance = workInProgress.stateNode;
+ // resetInputPointers(workInProgress, instance);
-var reconcileChildFibers = ChildReconciler(true);
-var mountChildFibers = ChildReconciler(false);
+ // let newState = workInProgress.memoizedState;
+ // let newProps = workInProgress.pendingProps;
+ // if (!newProps) {
+ // // If there isn't any new props, then we'll reuse the memoized props.
+ // // This could be from already completed work.
+ // newProps = workInProgress.memoizedProps;
+ // invariant(
+ // newProps != null,
+ // 'There should always be pending or memoized props. This error is ' +
+ // 'likely caused by a bug in React. Please file an issue.',
+ // );
+ // }
+ // const newUnmaskedContext = getUnmaskedContext(workInProgress);
+ // const newContext = getMaskedContext(workInProgress, newUnmaskedContext);
-function cloneChildFibers(current, workInProgress) {
- invariant(
- current === null || workInProgress.child === current.child,
- "Resuming work not yet implemented."
- );
+ // const oldContext = instance.context;
+ // const oldProps = workInProgress.memoizedProps;
- if (workInProgress.child === null) {
- return;
- }
+ // if (
+ // typeof instance.componentWillReceiveProps === 'function' &&
+ // (oldProps !== newProps || oldContext !== newContext)
+ // ) {
+ // callComponentWillReceiveProps(
+ // workInProgress,
+ // instance,
+ // newProps,
+ // newContext,
+ // );
+ // }
- var currentChild = workInProgress.child;
- var newChild = createWorkInProgress(
- currentChild,
- currentChild.pendingProps,
- currentChild.expirationTime
- );
- workInProgress.child = newChild;
+ // // Process the update queue before calling shouldComponentUpdate
+ // const updateQueue = workInProgress.updateQueue;
+ // if (updateQueue !== null) {
+ // newState = processUpdateQueue(
+ // workInProgress,
+ // updateQueue,
+ // instance,
+ // newState,
+ // newProps,
+ // priorityLevel,
+ // );
+ // }
- newChild["return"] = workInProgress;
- while (currentChild.sibling !== null) {
- currentChild = currentChild.sibling;
- newChild = newChild.sibling = createWorkInProgress(
- currentChild,
- currentChild.pendingProps,
- currentChild.expirationTime
- );
- newChild["return"] = workInProgress;
- }
- newChild.sibling = null;
-}
+ // // TODO: Should we deal with a setState that happened after the last
+ // // componentWillMount and before this componentWillMount? Probably
+ // // unsupported anyway.
-{
- var warnedAboutStatelessRefs = {};
-}
+ // if (
+ // !checkShouldComponentUpdate(
+ // workInProgress,
+ // workInProgress.memoizedProps,
+ // newProps,
+ // workInProgress.memoizedState,
+ // newState,
+ // newContext,
+ // )
+ // ) {
+ // // Update the existing instance's state, props, and context pointers even
+ // // though we're bailing out.
+ // instance.props = newProps;
+ // instance.state = newState;
+ // instance.context = newContext;
+ // return false;
+ // }
-var ReactFiberBeginWork = function(
- config,
- hostContext,
- hydrationContext,
- scheduleWork,
- computeExpirationForFiber
-) {
- var shouldSetTextContent = config.shouldSetTextContent,
- useSyncScheduling = config.useSyncScheduling,
- shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree;
- var pushHostContext = hostContext.pushHostContext,
- pushHostContainer = hostContext.pushHostContainer;
- var enterHydrationState = hydrationContext.enterHydrationState,
- resetHydrationState = hydrationContext.resetHydrationState,
- tryToClaimNextHydratableInstance =
- hydrationContext.tryToClaimNextHydratableInstance;
+ // // Update the input pointers now so that they are correct when we call
+ // // componentWillMount
+ // instance.props = newProps;
+ // instance.state = newState;
+ // instance.context = newContext;
- var _ReactFiberClassCompo = ReactFiberClassComponent(
- scheduleWork,
- computeExpirationForFiber,
- memoizeProps,
- memoizeState
- ),
- adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance,
- constructClassInstance = _ReactFiberClassCompo.constructClassInstance,
- mountClassInstance = _ReactFiberClassCompo.mountClassInstance,
- updateClassInstance = _ReactFiberClassCompo.updateClassInstance;
+ // if (typeof instance.componentWillMount === 'function') {
+ // callComponentWillMount(workInProgress, instance);
+ // // componentWillMount may have called setState. Process the update queue.
+ // const newUpdateQueue = workInProgress.updateQueue;
+ // if (newUpdateQueue !== null) {
+ // newState = processUpdateQueue(
+ // workInProgress,
+ // newUpdateQueue,
+ // instance,
+ // newState,
+ // newProps,
+ // priorityLevel,
+ // );
+ // }
+ // }
- // TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
+ // if (typeof instance.componentDidMount === 'function') {
+ // workInProgress.effectTag |= Update;
+ // }
- function reconcileChildren(current, workInProgress, nextChildren) {
- reconcileChildrenAtExpirationTime(
- current,
- workInProgress,
- nextChildren,
- workInProgress.expirationTime
- );
- }
+ // instance.state = newState;
- function reconcileChildrenAtExpirationTime(
- current,
- workInProgress,
- nextChildren,
- renderExpirationTime
- ) {
- if (current === null) {
- // If this is a fresh new component that hasn't been rendered yet, we
- // won't update its child set by applying minimal side-effects. Instead,
- // we will add them all to the child before it gets rendered. That means
- // we can optimize this reconciliation pass by not tracking side-effects.
- workInProgress.child = mountChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderExpirationTime
- );
- } else {
- // If the current child is the same as the work in progress, it means that
- // we haven't yet started any work on these children. Therefore, we use
- // the clone algorithm to create a copy of all the current children.
+ // return true;
+ // }
- // If we had any progressed work already, that is invalid at this point so
- // let's throw it out.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- current.child,
- nextChildren,
- renderExpirationTime
- );
- }
- }
-
- function updateFragment(current, workInProgress) {
- var nextChildren = workInProgress.pendingProps;
- if (hasContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (
- nextChildren === null ||
- workInProgress.memoizedProps === nextChildren
- ) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextChildren);
- return workInProgress.child;
- }
+ // Invokes the update life-cycles and returns false if it shouldn't rerender.
+ function updateClassInstance(current, workInProgress, renderExpirationTime) {
+ var instance = workInProgress.stateNode;
+ resetInputPointers(workInProgress, instance);
- function markRef(current, workInProgress) {
- var ref = workInProgress.ref;
- if (ref !== null && (!current || current.ref !== ref)) {
- // Schedule a Ref effect
- workInProgress.effectTag |= Ref;
- }
- }
+ var oldProps = workInProgress.memoizedProps;
+ var newProps = workInProgress.pendingProps;
+ var oldContext = instance.context;
+ var newUnmaskedContext = getUnmaskedContext(workInProgress);
+ var newContext = getMaskedContext(workInProgress, newUnmaskedContext);
- function updateFunctionalComponent(current, workInProgress) {
- var fn = workInProgress.type;
- var nextProps = workInProgress.pendingProps;
+ // Note: During these life-cycles, instance.props/instance.state are what
+ // ever the previously attempted to render - not the "current". However,
+ // during componentDidUpdate we pass the "current" props.
- if (hasContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else {
- if (workInProgress.memoizedProps === nextProps) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ if (
+ (typeof instance.UNSAFE_componentWillReceiveProps === "function" ||
+ typeof instance.componentWillReceiveProps === "function") &&
+ typeof workInProgress.type.getDerivedStateFromProps !== "function"
+ ) {
+ if (oldProps !== newProps || oldContext !== newContext) {
+ callComponentWillReceiveProps(
+ workInProgress,
+ instance,
+ newProps,
+ newContext
+ );
}
- // TODO: consider bringing fn.shouldComponentUpdate() back.
- // It used to be here.
}
- var unmaskedContext = getUnmaskedContext(workInProgress);
- var context = getMaskedContext(workInProgress, unmaskedContext);
-
- var nextChildren;
-
- {
- ReactCurrentOwner.current = workInProgress;
- ReactDebugCurrentFiber.setCurrentPhase("render");
- nextChildren = fn(nextProps, context);
- ReactDebugCurrentFiber.setCurrentPhase(null);
+ var partialState = void 0;
+ if (oldProps !== newProps) {
+ partialState = callGetDerivedStateFromProps(
+ workInProgress,
+ instance,
+ newProps
+ );
}
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
- }
-
- function updateClassComponent(current, workInProgress, renderExpirationTime) {
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
- var hasContext = pushContextProvider(workInProgress);
- var shouldUpdate = void 0;
- if (current === null) {
- if (!workInProgress.stateNode) {
- // In the initial pass we might need to construct the instance.
- constructClassInstance(workInProgress, workInProgress.pendingProps);
- mountClassInstance(workInProgress, renderExpirationTime);
- shouldUpdate = true;
- } else {
- invariant(false, "Resuming work not yet implemented.");
- // In a resume, we'll already have an instance we can reuse.
- // shouldUpdate = resumeMountClassInstance(workInProgress, renderExpirationTime);
- }
- } else {
- shouldUpdate = updateClassInstance(
+ // Compute the next state using the memoized state and the update queue.
+ var oldState = workInProgress.memoizedState;
+ // TODO: Previous state can be null.
+ var newState = void 0;
+ if (workInProgress.updateQueue !== null) {
+ newState = processUpdateQueue(
current,
workInProgress,
+ workInProgress.updateQueue,
+ instance,
+ newProps,
renderExpirationTime
);
+ } else {
+ newState = oldState;
}
- return finishClassComponent(
- current,
- workInProgress,
- shouldUpdate,
- hasContext
- );
- }
- function finishClassComponent(
- current,
- workInProgress,
- shouldUpdate,
- hasContext
- ) {
- // Refs should update even if shouldComponentUpdate returns false
- markRef(current, workInProgress);
+ if (partialState !== null && partialState !== undefined) {
+ // Render-phase updates (like this) should not be added to the update queue,
+ // So that multiple render passes do not enqueue multiple updates.
+ // Instead, just synchronously merge the returned state into the instance.
+ newState =
+ newState === null || newState === undefined
+ ? partialState
+ : Object.assign({}, newState, partialState);
+ }
- if (!shouldUpdate) {
- // Context providers should defer to sCU for rendering
- if (hasContext) {
- invalidateContextProvider(workInProgress, false);
+ if (
+ oldProps === newProps &&
+ oldState === newState &&
+ !hasContextChanged() &&
+ !(
+ workInProgress.updateQueue !== null &&
+ workInProgress.updateQueue.hasForceUpdate
+ )
+ ) {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === "function") {
+ if (
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.effectTag |= Update;
+ }
}
-
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ return false;
}
- var instance = workInProgress.stateNode;
+ var shouldUpdate = checkShouldComponentUpdate(
+ workInProgress,
+ oldProps,
+ newProps,
+ oldState,
+ newState,
+ newContext
+ );
- // Rerender
- ReactCurrentOwner.current = workInProgress;
- var nextChildren = void 0;
- {
- ReactDebugCurrentFiber.setCurrentPhase("render");
- nextChildren = instance.render();
- if (debugRenderPhaseSideEffects) {
- instance.render();
+ if (shouldUpdate) {
+ // In order to support react-lifecycles-compat polyfilled components,
+ // Unsafe lifecycles should not be invoked for any component with the new gDSFP.
+ if (
+ (typeof instance.UNSAFE_componentWillUpdate === "function" ||
+ typeof instance.componentWillUpdate === "function") &&
+ typeof workInProgress.type.getDerivedStateFromProps !== "function"
+ ) {
+ if (typeof instance.componentWillUpdate === "function") {
+ startPhaseTimer(workInProgress, "componentWillUpdate");
+ instance.componentWillUpdate(newProps, newState, newContext);
+ stopPhaseTimer();
+ } else {
+ startPhaseTimer(workInProgress, "componentWillUpdate");
+ instance.UNSAFE_componentWillUpdate(newProps, newState, newContext);
+ stopPhaseTimer();
+ }
+ }
+ if (typeof instance.componentDidUpdate === "function") {
+ workInProgress.effectTag |= Update;
+ }
+ } else {
+ // If an update was already in progress, we should schedule an Update
+ // effect even though we're bailing out, so that cWU/cDU are called.
+ if (typeof instance.componentDidUpdate === "function") {
+ if (
+ oldProps !== current.memoizedProps ||
+ oldState !== current.memoizedState
+ ) {
+ workInProgress.effectTag |= Update;
+ }
}
- ReactDebugCurrentFiber.setCurrentPhase(null);
- }
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
- reconcileChildren(current, workInProgress, nextChildren);
- // Memoize props and state using the values we just used to render.
- // TODO: Restructure so we never read values from the instance.
- memoizeState(workInProgress, instance.state);
- memoizeProps(workInProgress, instance.props);
- // The context might have changed so we need to recalculate it.
- if (hasContext) {
- invalidateContextProvider(workInProgress, true);
+ // If shouldComponentUpdate returned false, we should still update the
+ // memoized props/state to indicate that this work can be reused.
+ memoizeProps(workInProgress, newProps);
+ memoizeState(workInProgress, newState);
}
- return workInProgress.child;
+ // Update the existing instance's state, props, and context pointers even
+ // if shouldComponentUpdate returns false.
+ instance.props = newProps;
+ instance.state = newState;
+ instance.context = newContext;
+
+ return shouldUpdate;
}
- 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);
+ return {
+ adoptClassInstance: adoptClassInstance,
+ callGetDerivedStateFromProps: callGetDerivedStateFromProps,
+ constructClassInstance: constructClassInstance,
+ mountClassInstance: mountClassInstance,
+ // resumeMountClassInstance,
+ updateClassInstance: updateClassInstance
+ };
+};
+
+var getCurrentFiberStackAddendum$1 =
+ ReactDebugCurrentFiber.getCurrentFiberStackAddendum;
+
+var didWarnAboutMaps = void 0;
+var ownerHasKeyUseWarning = void 0;
+var ownerHasFunctionTypeWarning = void 0;
+var warnForMissingKey = function(child) {};
+
+{
+ didWarnAboutMaps = false;
+ /**
+ * Warn if there's no key explicitly set on dynamic arrays of children or
+ * object keys are not valid. This allows us to keep track of children between
+ * updates.
+ */
+ ownerHasKeyUseWarning = {};
+ ownerHasFunctionTypeWarning = {};
+
+ warnForMissingKey = function(child) {
+ if (child === null || typeof child !== "object") {
+ return;
}
- pushHostContainer(workInProgress, root.containerInfo);
- }
+ if (!child._store || child._store.validated || child.key != null) {
+ return;
+ }
+ invariant(
+ typeof child._store === "object",
+ "React Component in warnForMissingKey should have a _store. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ child._store.validated = true;
- function updateHostRoot(current, workInProgress, renderExpirationTime) {
- pushHostRootContext(workInProgress);
- var updateQueue = workInProgress.updateQueue;
- if (updateQueue !== null) {
- var prevState = workInProgress.memoizedState;
- var state = processUpdateQueue(
- current,
- workInProgress,
- updateQueue,
- null,
- null,
- renderExpirationTime
- );
- if (prevState === state) {
- // If the state is the same as before, that's a bailout because we had
- // no work that expires at this time.
- resetHydrationState();
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
- var element = state.element;
- var root = workInProgress.stateNode;
- if (
- (current === null || current.child === null) &&
- root.hydrate &&
- enterHydrationState(workInProgress)
- ) {
- // If we don't have any current children this might be the first pass.
- // We always try to hydrate. If this isn't a hydration pass there won't
- // be any children to hydrate which is effectively the same thing as
- // not hydrating.
+ var currentComponentErrorInfo =
+ "Each child in an array or iterator should have a unique " +
+ '"key" prop. See https://fb.me/react-warning-keys for ' +
+ "more information." +
+ (getCurrentFiberStackAddendum$1() || "");
+ if (ownerHasKeyUseWarning[currentComponentErrorInfo]) {
+ return;
+ }
+ ownerHasKeyUseWarning[currentComponentErrorInfo] = true;
- // This is a bit of a hack. We track the host root as a placement to
- // know that we're currently in a mounting state. That way isMounted
- // works as expected. We must reset this before committing.
- // TODO: Delete this when we delete isMounted and findDOMNode.
- workInProgress.effectTag |= Placement;
+ warning(
+ false,
+ "Each child in an array or iterator should have a unique " +
+ '"key" prop. See https://fb.me/react-warning-keys for ' +
+ "more information.%s",
+ getCurrentFiberStackAddendum$1()
+ );
+ };
+}
- // Ensure that children mount into this root without tracking
- // side-effects. This ensures that we don't store Placement effects on
- // nodes that will be hydrated.
- workInProgress.child = mountChildFibers(
- workInProgress,
- null,
- element,
- renderExpirationTime
+var isArray$1 = Array.isArray;
+
+function coerceRef(current, element) {
+ var mixedRef = element.ref;
+ if (mixedRef !== null && typeof mixedRef !== "function") {
+ if (element._owner) {
+ var owner = element._owner;
+ var inst = void 0;
+ if (owner) {
+ var ownerFiber = owner;
+ invariant(
+ ownerFiber.tag === ClassComponent,
+ "Stateless function components cannot have refs."
);
- } else {
- // Otherwise reset hydration state in case we aborted and resumed another
- // root.
- resetHydrationState();
- reconcileChildren(current, workInProgress, element);
+ inst = ownerFiber.stateNode;
}
- memoizeState(workInProgress, state);
- return workInProgress.child;
+ invariant(
+ inst,
+ "Missing owner for string ref %s. This error is likely caused by a " +
+ "bug in React. Please file an issue.",
+ mixedRef
+ );
+ var stringRef = "" + mixedRef;
+ // Check if previous string ref matches new string ref
+ if (
+ current !== null &&
+ current.ref !== null &&
+ current.ref._stringRef === stringRef
+ ) {
+ return current.ref;
+ }
+ var ref = function(value) {
+ var refs = inst.refs === emptyObject ? (inst.refs = {}) : inst.refs;
+ if (value === null) {
+ delete refs[stringRef];
+ } else {
+ refs[stringRef] = value;
+ }
+ };
+ ref._stringRef = stringRef;
+ return ref;
+ } else {
+ invariant(
+ typeof mixedRef === "string",
+ "Expected ref to be a function or a string."
+ );
+ invariant(
+ element._owner,
+ "Element ref was specified as a string (%s) but no owner was set. This could happen for one of" +
+ " the following reasons:\n" +
+ "1. You may be adding a ref to a functional 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://fb.me/react-refs-must-have-owner for more information.",
+ mixedRef
+ );
}
- resetHydrationState();
- // If there is no update queue, that's a bailout because the root has no props.
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
}
+ return mixedRef;
+}
- function updateHostComponent(current, workInProgress, renderExpirationTime) {
- pushHostContext(workInProgress);
-
- if (current === null) {
- tryToClaimNextHydratableInstance(workInProgress);
+function throwOnInvalidObjectType(returnFiber, newChild) {
+ if (returnFiber.type !== "textarea") {
+ var addendum = "";
+ {
+ addendum =
+ " If you meant to render a collection of children, use an array " +
+ "instead." +
+ (getCurrentFiberStackAddendum$1() || "");
}
+ invariant(
+ false,
+ "Objects are not valid as a React child (found: %s).%s",
+ Object.prototype.toString.call(newChild) === "[object Object]"
+ ? "object with keys {" + Object.keys(newChild).join(", ") + "}"
+ : newChild,
+ addendum
+ );
+ }
+}
- var type = workInProgress.type;
- var memoizedProps = workInProgress.memoizedProps;
- var nextProps = workInProgress.pendingProps;
- var prevProps = current !== null ? current.memoizedProps : null;
+function warnOnFunctionType() {
+ var currentComponentErrorInfo =
+ "Functions are not valid as a React child. This may happen if " +
+ "you return a Component instead of from render. " +
+ "Or maybe you meant to call this function rather than return it." +
+ (getCurrentFiberStackAddendum$1() || "");
- if (hasContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (memoizedProps === nextProps) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
- }
+ if (ownerHasFunctionTypeWarning[currentComponentErrorInfo]) {
+ return;
+ }
+ ownerHasFunctionTypeWarning[currentComponentErrorInfo] = true;
- var nextChildren = nextProps.children;
- var isDirectTextChild = shouldSetTextContent(type, nextProps);
+ warning(
+ false,
+ "Functions are not valid as a React child. This may happen if " +
+ "you return a Component instead of from render. " +
+ "Or maybe you meant to call this function rather than return it.%s",
+ getCurrentFiberStackAddendum$1() || ""
+ );
+}
- if (isDirectTextChild) {
- // We special case a direct text child of a host node. This is a common
- // case. We won't handle it as a reified child. We will instead handle
- // this in the host environment that also have access to this prop. That
- // avoids allocating another HostText fiber and traversing it.
- nextChildren = null;
- } else if (prevProps && shouldSetTextContent(type, prevProps)) {
- // If we're switching from a direct text child to a normal child, or to
- // empty, we need to schedule the text content to be reset.
- workInProgress.effectTag |= ContentReset;
+// This wrapper function exists because I expect to clone the code in each path
+// to be able to optimize each path individually by branching early. This needs
+// a compiler or we can do it manually. Helpers that don't need this branching
+// live outside of this function.
+function ChildReconciler(shouldTrackSideEffects) {
+ function deleteChild(returnFiber, childToDelete) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return;
}
+ // Deletions are added in reversed order so we add it to the front.
+ // At this point, the return fiber's effect list is empty except for
+ // deletions, so we can just append the deletion to the list. The remaining
+ // effects aren't added until the complete phase. Once we implement
+ // resuming, this may not be true.
+ var last = returnFiber.lastEffect;
+ if (last !== null) {
+ last.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+ childToDelete.nextEffect = null;
+ childToDelete.effectTag = Deletion;
+ }
- markRef(current, workInProgress);
-
- // Check the host config to see if the children are offscreen/hidden.
- if (
- renderExpirationTime !== Never &&
- !useSyncScheduling &&
- shouldDeprioritizeSubtree(type, nextProps)
- ) {
- // Down-prioritize the children.
- workInProgress.expirationTime = Never;
- // Bailout and come back to this fiber later.
+ function deleteRemainingChildren(returnFiber, currentFirstChild) {
+ if (!shouldTrackSideEffects) {
+ // Noop.
return null;
}
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextProps);
- return workInProgress.child;
- }
-
- function updateHostText(current, workInProgress) {
- if (current === null) {
- tryToClaimNextHydratableInstance(workInProgress);
+ // TODO: For the shouldClone case, this could be micro-optimized a bit by
+ // assuming that after the first child we've already added everything.
+ var childToDelete = currentFirstChild;
+ while (childToDelete !== null) {
+ deleteChild(returnFiber, childToDelete);
+ childToDelete = childToDelete.sibling;
}
- var nextProps = workInProgress.pendingProps;
- memoizeProps(workInProgress, nextProps);
- // Nothing to do here. This is terminal. We'll do the completion step
- // immediately after.
return null;
}
- function mountIndeterminateComponent(
- current,
- workInProgress,
- renderExpirationTime
- ) {
- invariant(
- current === null,
- "An indeterminate component should never have mounted. This error is " +
- "likely caused by a bug in React. Please file an issue."
- );
- var fn = workInProgress.type;
- var props = workInProgress.pendingProps;
- var unmaskedContext = getUnmaskedContext(workInProgress);
- var context = getMaskedContext(workInProgress, unmaskedContext);
-
- var value;
+ function mapRemainingChildren(returnFiber, currentFirstChild) {
+ // Add the remaining children to a temporary map so that we can find them by
+ // keys quickly. Implicit (null) keys get added to this set with their index
+ var existingChildren = new Map();
- {
- if (fn.prototype && typeof fn.prototype.render === "function") {
- var componentName = getComponentName(workInProgress);
- warning(
- false,
- "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
- "This is likely to cause errors. Change %s to extend React.Component instead.",
- componentName,
- componentName
- );
+ var existingChild = currentFirstChild;
+ while (existingChild !== null) {
+ if (existingChild.key !== null) {
+ existingChildren.set(existingChild.key, existingChild);
+ } else {
+ existingChildren.set(existingChild.index, existingChild);
}
- ReactCurrentOwner.current = workInProgress;
- value = fn(props, context);
+ existingChild = existingChild.sibling;
}
- // React DevTools reads this flag.
- workInProgress.effectTag |= PerformedWork;
-
- if (
- typeof value === "object" &&
- value !== null &&
- typeof value.render === "function"
- ) {
- // Proceed under the assumption that this is a class instance
- workInProgress.tag = ClassComponent;
-
- // Push context providers early to prevent context stack mismatches.
- // During mounting we don't know the child context yet as the instance doesn't exist.
- // We will invalidate the child context in finishClassComponent() right after rendering.
- var hasContext = pushContextProvider(workInProgress);
- adoptClassInstance(workInProgress, value);
- mountClassInstance(workInProgress, renderExpirationTime);
- return finishClassComponent(current, workInProgress, true, hasContext);
- } else {
- // Proceed under the assumption that this is a functional component
- workInProgress.tag = FunctionalComponent;
- {
- var Component = workInProgress.type;
+ return existingChildren;
+ }
- if (Component) {
- warning(
- !Component.childContextTypes,
- "%s(...): childContextTypes cannot be defined on a functional component.",
- Component.displayName || Component.name || "Component"
- );
- }
- if (workInProgress.ref !== null) {
- var info = "";
- var ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName();
- if (ownerName) {
- info += "\n\nCheck the render method of `" + ownerName + "`.";
- }
+ function useFiber(fiber, pendingProps, expirationTime) {
+ // We currently set sibling to null and index to 0 here because it is easy
+ // to forget to do before returning it. E.g. for the single child case.
+ var clone = createWorkInProgress(fiber, pendingProps, expirationTime);
+ clone.index = 0;
+ clone.sibling = null;
+ return clone;
+ }
- var warningKey = ownerName || workInProgress._debugID || "";
- var debugSource = workInProgress._debugSource;
- if (debugSource) {
- warningKey = debugSource.fileName + ":" + debugSource.lineNumber;
- }
- if (!warnedAboutStatelessRefs[warningKey]) {
- warnedAboutStatelessRefs[warningKey] = true;
- warning(
- false,
- "Stateless function components cannot be given refs. " +
- "Attempts to access this ref will fail.%s%s",
- info,
- ReactDebugCurrentFiber.getCurrentFiberStackAddendum()
- );
- }
- }
+ function placeChild(newFiber, lastPlacedIndex, newIndex) {
+ newFiber.index = newIndex;
+ if (!shouldTrackSideEffects) {
+ // Noop.
+ return lastPlacedIndex;
+ }
+ var current = newFiber.alternate;
+ if (current !== null) {
+ var oldIndex = current.index;
+ if (oldIndex < lastPlacedIndex) {
+ // This is a move.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
+ } else {
+ // This item can stay in place.
+ return oldIndex;
}
- reconcileChildren(current, workInProgress, value);
- memoizeProps(workInProgress, props);
- return workInProgress.child;
+ } else {
+ // This is an insertion.
+ newFiber.effectTag = Placement;
+ return lastPlacedIndex;
}
}
- function updateCallComponent(current, workInProgress, renderExpirationTime) {
- var nextCall = workInProgress.pendingProps;
- if (hasContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (workInProgress.memoizedProps === nextCall) {
- nextCall = workInProgress.memoizedProps;
- // TODO: When bailing out, we might need to return the stateNode instead
- // of the child. To check it for work.
- // return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ function placeSingleChild(newFiber) {
+ // This is simpler for the single child case. We only need to do a
+ // placement for inserting new children.
+ if (shouldTrackSideEffects && newFiber.alternate === null) {
+ newFiber.effectTag = Placement;
}
+ return newFiber;
+ }
- var nextChildren = nextCall.children;
-
- // The following is a fork of reconcileChildrenAtExpirationTime but using
- // stateNode to store the child.
- if (current === null) {
- workInProgress.stateNode = mountChildFibers(
- workInProgress,
- workInProgress.stateNode,
- nextChildren,
- renderExpirationTime
+ function updateTextNode(returnFiber, current, textContent, expirationTime) {
+ if (current === null || current.tag !== HostText) {
+ // Insert
+ var created = createFiberFromText(
+ textContent,
+ returnFiber.internalContextTag,
+ expirationTime
);
+ created["return"] = returnFiber;
+ return created;
} else {
- workInProgress.stateNode = reconcileChildFibers(
- workInProgress,
- workInProgress.stateNode,
- nextChildren,
- renderExpirationTime
- );
+ // Update
+ var existing = useFiber(current, textContent, expirationTime);
+ existing["return"] = returnFiber;
+ return existing;
}
-
- memoizeProps(workInProgress, nextCall);
- // This doesn't take arbitrary time so we could synchronously just begin
- // eagerly do the work of workInProgress.child as an optimization.
- return workInProgress.stateNode;
}
- function updatePortalComponent(
- current,
- workInProgress,
- renderExpirationTime
- ) {
- pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
- var nextChildren = workInProgress.pendingProps;
- if (hasContextChanged()) {
- // Normally we can bail out on props equality but if context has changed
- // we don't do the bailout and we have to reuse existing props instead.
- } else if (workInProgress.memoizedProps === nextChildren) {
- return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ function updateElement(returnFiber, current, element, expirationTime) {
+ if (current !== null && current.type === element.type) {
+ // Move based on index
+ var existing = useFiber(current, element.props, expirationTime);
+ existing.ref = coerceRef(current, element);
+ existing["return"] = returnFiber;
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
+ return existing;
+ } else {
+ // Insert
+ var created = createFiberFromElement(
+ element,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ created.ref = coerceRef(current, element);
+ created["return"] = returnFiber;
+ return created;
}
+ }
- if (current === null) {
- // Portals are special because we don't append the children during mount
- // but at commit. Therefore we need to track insertions which the normal
- // flow doesn't do during mount. This doesn't happen at the root because
- // the root always starts with a "current" with a null child.
- // TODO: Consider unifying this with how the root works.
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- null,
- nextChildren,
- renderExpirationTime
+ function updatePortal(returnFiber, current, portal, expirationTime) {
+ if (
+ current === null ||
+ current.tag !== HostPortal ||
+ current.stateNode.containerInfo !== portal.containerInfo ||
+ current.stateNode.implementation !== portal.implementation
+ ) {
+ // Insert
+ var created = createFiberFromPortal(
+ portal,
+ returnFiber.internalContextTag,
+ expirationTime
);
- memoizeProps(workInProgress, nextChildren);
+ created["return"] = returnFiber;
+ return created;
} else {
- reconcileChildren(current, workInProgress, nextChildren);
- memoizeProps(workInProgress, nextChildren);
+ // Update
+ var existing = useFiber(current, portal.children || [], expirationTime);
+ existing["return"] = returnFiber;
+ return existing;
}
- return workInProgress.child;
}
- /*
- function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
- let child = firstChild;
- do {
- // Ensure that the first and last effect of the parent corresponds
- // to the children's first and last effect.
- if (!returnFiber.firstEffect) {
- returnFiber.firstEffect = child.firstEffect;
- }
- if (child.lastEffect) {
- if (returnFiber.lastEffect) {
- returnFiber.lastEffect.nextEffect = child.firstEffect;
- }
- returnFiber.lastEffect = child.lastEffect;
- }
- } while (child = child.sibling);
+ function updateFragment(returnFiber, current, fragment, expirationTime, key) {
+ if (current === null || current.tag !== Fragment) {
+ // Insert
+ var created = createFiberFromFragment(
+ fragment,
+ returnFiber.internalContextTag,
+ expirationTime,
+ key
+ );
+ created["return"] = returnFiber;
+ return created;
+ } else {
+ // Update
+ var existing = useFiber(current, fragment, expirationTime);
+ existing["return"] = returnFiber;
+ return existing;
+ }
}
- */
- function bailoutOnAlreadyFinishedWork(current, workInProgress) {
- cancelWorkTimer(workInProgress);
+ function createChild(returnFiber, newChild, expirationTime) {
+ if (typeof newChild === "string" || typeof newChild === "number") {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ var created = createFiberFromText(
+ "" + newChild,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ created["return"] = returnFiber;
+ return created;
+ }
- // TODO: We should ideally be able to bail out early if the children have no
- // more work to do. However, since we don't have a separation of this
- // Fiber's priority and its children yet - we don't know without doing lots
- // of the same work we do anyway. Once we have that separation we can just
- // bail out here if the children has no more work at this priority level.
- // if (workInProgress.priorityOfChildren <= priorityLevel) {
- // // If there are side-effects in these children that have not yet been
- // // committed we need to ensure that they get properly transferred up.
- // if (current && current.child !== workInProgress.child) {
- // reuseChildrenEffects(workInProgress, child);
- // }
- // return null;
- // }
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ var _created = createFiberFromElement(
+ newChild,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ _created.ref = coerceRef(null, newChild);
+ _created["return"] = returnFiber;
+ return _created;
+ }
+ case REACT_PORTAL_TYPE: {
+ var _created2 = createFiberFromPortal(
+ newChild,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ _created2["return"] = returnFiber;
+ return _created2;
+ }
+ }
- cloneChildFibers(current, workInProgress);
- return workInProgress.child;
- }
+ if (isArray$1(newChild) || getIteratorFn(newChild)) {
+ var _created3 = createFiberFromFragment(
+ newChild,
+ returnFiber.internalContextTag,
+ expirationTime,
+ null
+ );
+ _created3["return"] = returnFiber;
+ return _created3;
+ }
- function bailoutOnLowPriority(current, workInProgress) {
- cancelWorkTimer(workInProgress);
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
- // TODO: Handle HostComponent tags here as well and call pushHostContext()?
- // See PR 8590 discussion for context
- switch (workInProgress.tag) {
- case HostRoot:
- pushHostRootContext(workInProgress);
- break;
- case ClassComponent:
- pushContextProvider(workInProgress);
- break;
- case HostPortal:
- pushHostContainer(
- workInProgress,
- workInProgress.stateNode.containerInfo
- );
- break;
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType();
+ }
}
- // TODO: What if this is currently in progress?
- // How can that happen? How is this not being cloned?
- return null;
- }
- // TODO: Delete memoizeProps/State and move to reconcile/bailout instead
- function memoizeProps(workInProgress, nextProps) {
- workInProgress.memoizedProps = nextProps;
+ return null;
}
- function memoizeState(workInProgress, nextState) {
- workInProgress.memoizedState = nextState;
- // Don't reset the updateQueue, in case there are pending updates. Resetting
- // is handled by processUpdateQueue.
- }
+ function updateSlot(returnFiber, oldFiber, newChild, expirationTime) {
+ // Update the fiber if the keys match, otherwise return null.
- function beginWork(current, workInProgress, renderExpirationTime) {
- if (
- workInProgress.expirationTime === NoWork ||
- workInProgress.expirationTime > renderExpirationTime
- ) {
- return bailoutOnLowPriority(current, workInProgress);
- }
+ var key = oldFiber !== null ? oldFiber.key : null;
- switch (workInProgress.tag) {
- case IndeterminateComponent:
- return mountIndeterminateComponent(
- current,
- workInProgress,
- renderExpirationTime
- );
- case FunctionalComponent:
- return updateFunctionalComponent(current, workInProgress);
- case ClassComponent:
- return updateClassComponent(
- current,
- workInProgress,
- renderExpirationTime
- );
- case HostRoot:
- return updateHostRoot(current, workInProgress, renderExpirationTime);
- case HostComponent:
- return updateHostComponent(
- current,
- workInProgress,
- renderExpirationTime
- );
- case HostText:
- return updateHostText(current, workInProgress);
- case CallHandlerPhase:
- // This is a restart. Reset the tag to the initial phase.
- workInProgress.tag = CallComponent;
- // Intentionally fall through since this is now the same.
- case CallComponent:
- return updateCallComponent(
- current,
- workInProgress,
- renderExpirationTime
- );
- case ReturnComponent:
- // A return component is just a placeholder, we can just run through the
- // next one immediately.
+ if (typeof newChild === "string" || typeof newChild === "number") {
+ // Text nodes don't have keys. If the previous node is implicitly keyed
+ // we can continue to replace it without aborting even if it is not a text
+ // node.
+ if (key !== null) {
return null;
- case HostPortal:
- return updatePortalComponent(
- current,
- workInProgress,
- renderExpirationTime
- );
- case Fragment:
- return updateFragment(current, workInProgress);
- default:
- invariant(
- false,
- "Unknown unit of work tag. This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
+ }
+ return updateTextNode(
+ returnFiber,
+ oldFiber,
+ "" + newChild,
+ expirationTime
+ );
}
- }
- function beginFailedWork(current, workInProgress, renderExpirationTime) {
- // Push context providers here to avoid a push/pop context mismatch.
- switch (workInProgress.tag) {
- case ClassComponent:
- pushContextProvider(workInProgress);
- break;
- case HostRoot:
- pushHostRootContext(workInProgress);
- break;
- default:
- invariant(
- false,
- "Invalid type of work. This error is likely caused by a bug in React. " +
- "Please file an issue."
- );
- }
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ if (newChild.key === key) {
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(
+ returnFiber,
+ oldFiber,
+ newChild.props.children,
+ expirationTime,
+ key
+ );
+ }
+ return updateElement(
+ returnFiber,
+ oldFiber,
+ newChild,
+ expirationTime
+ );
+ } else {
+ return null;
+ }
+ }
+ case REACT_PORTAL_TYPE: {
+ if (newChild.key === key) {
+ return updatePortal(
+ returnFiber,
+ oldFiber,
+ newChild,
+ expirationTime
+ );
+ } else {
+ return null;
+ }
+ }
+ }
- // Add an error effect so we can handle the error during the commit phase
- workInProgress.effectTag |= Err;
+ if (isArray$1(newChild) || getIteratorFn(newChild)) {
+ if (key !== null) {
+ return null;
+ }
- // This is a weird case where we do "resume" work — work that failed on
- // our first attempt. Because we no longer have a notion of "progressed
- // deletions," reset the child to the current child to make sure we delete
- // it again. TODO: Find a better way to handle this, perhaps during a more
- // general overhaul of error handling.
- if (current === null) {
- workInProgress.child = null;
- } else if (workInProgress.child !== current.child) {
- workInProgress.child = current.child;
- }
+ return updateFragment(
+ returnFiber,
+ oldFiber,
+ newChild,
+ expirationTime,
+ null
+ );
+ }
- if (
- workInProgress.expirationTime === NoWork ||
- workInProgress.expirationTime > renderExpirationTime
- ) {
- return bailoutOnLowPriority(current, workInProgress);
+ throwOnInvalidObjectType(returnFiber, newChild);
}
- // If we don't bail out, we're going be recomputing our children so we need
- // to drop our effect list.
- workInProgress.firstEffect = null;
- workInProgress.lastEffect = null;
-
- // Unmount the current children as if the component rendered null
- var nextChildren = null;
- reconcileChildrenAtExpirationTime(
- current,
- workInProgress,
- nextChildren,
- renderExpirationTime
- );
-
- if (workInProgress.tag === ClassComponent) {
- var instance = workInProgress.stateNode;
- workInProgress.memoizedProps = instance.props;
- workInProgress.memoizedState = instance.state;
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType();
+ }
}
- return workInProgress.child;
+ return null;
}
- return {
- beginWork: beginWork,
- beginFailedWork: beginFailedWork
- };
-};
+ function updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ newChild,
+ expirationTime
+ ) {
+ if (typeof newChild === "string" || typeof newChild === "number") {
+ // Text nodes don't have keys, so we neither have to check the old nor
+ // new node for the key. If both are text nodes, they match.
+ var matchedFiber = existingChildren.get(newIdx) || null;
+ return updateTextNode(
+ returnFiber,
+ matchedFiber,
+ "" + newChild,
+ expirationTime
+ );
+ }
-var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) {
- var createInstance = config.createInstance,
- createTextInstance = config.createTextInstance,
- appendInitialChild = config.appendInitialChild,
- finalizeInitialChildren = config.finalizeInitialChildren,
- prepareUpdate = config.prepareUpdate,
- mutation = config.mutation,
- persistence = config.persistence;
- var getRootHostContainer = hostContext.getRootHostContainer,
- popHostContext = hostContext.popHostContext,
- getHostContext = hostContext.getHostContext,
- popHostContainer = hostContext.popHostContainer;
- var prepareToHydrateHostInstance =
- hydrationContext.prepareToHydrateHostInstance,
- prepareToHydrateHostTextInstance =
- hydrationContext.prepareToHydrateHostTextInstance,
- popHydrationState = hydrationContext.popHydrationState;
+ if (typeof newChild === "object" && newChild !== null) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE: {
+ var _matchedFiber =
+ existingChildren.get(
+ newChild.key === null ? newIdx : newChild.key
+ ) || null;
+ if (newChild.type === REACT_FRAGMENT_TYPE) {
+ return updateFragment(
+ returnFiber,
+ _matchedFiber,
+ newChild.props.children,
+ expirationTime,
+ newChild.key
+ );
+ }
+ return updateElement(
+ returnFiber,
+ _matchedFiber,
+ newChild,
+ expirationTime
+ );
+ }
+ case REACT_PORTAL_TYPE: {
+ var _matchedFiber2 =
+ existingChildren.get(
+ newChild.key === null ? newIdx : newChild.key
+ ) || null;
+ return updatePortal(
+ returnFiber,
+ _matchedFiber2,
+ newChild,
+ expirationTime
+ );
+ }
+ }
- function markUpdate(workInProgress) {
- // Tag the fiber with an update effect. This turns a Placement into
- // an UpdateAndPlacement.
- workInProgress.effectTag |= Update;
- }
+ if (isArray$1(newChild) || getIteratorFn(newChild)) {
+ var _matchedFiber3 = existingChildren.get(newIdx) || null;
+ return updateFragment(
+ returnFiber,
+ _matchedFiber3,
+ newChild,
+ expirationTime,
+ null
+ );
+ }
- function markRef(workInProgress) {
- workInProgress.effectTag |= Ref;
- }
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
- function appendAllReturns(returns, workInProgress) {
- var node = workInProgress.stateNode;
- if (node) {
- node["return"] = workInProgress;
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType();
+ }
}
- while (node !== null) {
- if (
- node.tag === HostComponent ||
- node.tag === HostText ||
- node.tag === HostPortal
- ) {
- invariant(false, "A call cannot have host component children.");
- } else if (node.tag === ReturnComponent) {
- returns.push(node.type);
- } else if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
+
+ return null;
+ }
+
+ /**
+ * Warns if there is a duplicate or missing key
+ */
+ function warnOnInvalidKey(child, knownKeys) {
+ {
+ if (typeof child !== "object" || child === null) {
+ return knownKeys;
}
- while (node.sibling === null) {
- if (node["return"] === null || node["return"] === workInProgress) {
- return;
- }
- node = node["return"];
+ switch (child.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ case REACT_PORTAL_TYPE:
+ warnForMissingKey(child);
+ var key = child.key;
+ if (typeof key !== "string") {
+ break;
+ }
+ if (knownKeys === null) {
+ knownKeys = new Set();
+ knownKeys.add(key);
+ break;
+ }
+ if (!knownKeys.has(key)) {
+ knownKeys.add(key);
+ break;
+ }
+ warning(
+ false,
+ "Encountered two children with the same key, `%s`. " +
+ "Keys should be unique so that components maintain their identity " +
+ "across updates. Non-unique keys may cause children to be " +
+ "duplicated and/or omitted — the behavior is unsupported and " +
+ "could change in a future version.%s",
+ key,
+ getCurrentFiberStackAddendum$1()
+ );
+ break;
+ default:
+ break;
}
- node.sibling["return"] = node["return"];
- node = node.sibling;
}
+ return knownKeys;
}
- function moveCallToHandlerPhase(
- current,
- workInProgress,
- renderExpirationTime
+ function reconcileChildrenArray(
+ returnFiber,
+ currentFirstChild,
+ newChildren,
+ expirationTime
) {
- var call = workInProgress.memoizedProps;
- invariant(
- call,
- "Should be resolved by now. This error is likely caused by a bug in " +
- "React. Please file an issue."
- );
+ // This algorithm can't optimize by searching from boths ends since we
+ // don't have backpointers on fibers. I'm trying to see how far we can get
+ // with that model. If it ends up not being worth the tradeoffs, we can
+ // add it later.
- // First step of the call has completed. Now we need to do the second.
- // TODO: It would be nice to have a multi stage call represented by a
- // single component, or at least tail call optimize nested ones. Currently
- // that requires additional fields that we don't want to add to the fiber.
- // So this requires nested handlers.
- // Note: This doesn't mutate the alternate node. I don't think it needs to
- // since this stage is reset for every pass.
- workInProgress.tag = CallHandlerPhase;
+ // 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.
- // Build up the returns.
- // TODO: Compare this to a generator or opaque helpers like Children.
- var returns = [];
- appendAllReturns(returns, workInProgress);
- var fn = call.handler;
- var props = call.props;
- var nextChildren = fn(props, returns);
+ // In this first iteration, we'll just live with hitting the bad case
+ // (adding everything to a Map) in for every insert/move.
- var currentFirstChild = current !== null ? current.child : null;
- workInProgress.child = reconcileChildFibers(
- workInProgress,
- currentFirstChild,
- nextChildren,
- renderExpirationTime
- );
- return workInProgress.child;
- }
+ // If you change this code, also update reconcileChildrenIterator() which
+ // uses the same algorithm.
- function appendAllChildren(parent, workInProgress) {
- // We only have the top Fiber that was created but we need recurse down its
- // children to find all the terminal nodes.
- var node = workInProgress.child;
- while (node !== null) {
- if (node.tag === HostComponent || node.tag === HostText) {
- appendInitialChild(parent, node.stateNode);
- } else if (node.tag === HostPortal) {
- // If we have a portal child, then we don't want to traverse
- // down its children. Instead, we'll get insertions from each child in
- // the portal directly.
- } else if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- if (node === workInProgress) {
- return;
- }
- while (node.sibling === null) {
- if (node["return"] === null || node["return"] === workInProgress) {
- return;
- }
- node = node["return"];
+ {
+ // First, validate keys.
+ var knownKeys = null;
+ for (var i = 0; i < newChildren.length; i++) {
+ var child = newChildren[i];
+ knownKeys = warnOnInvalidKey(child, knownKeys);
}
- node.sibling["return"] = node["return"];
- node = node.sibling;
}
- }
- var updateHostContainer = void 0;
- var updateHostComponent = void 0;
- var updateHostText = void 0;
- if (mutation) {
- if (enableMutatingReconciler) {
- // Mutation mode
- updateHostContainer = function(workInProgress) {
- // Noop
- };
- updateHostComponent = function(
- current,
- workInProgress,
- updatePayload,
- type,
- oldProps,
- newProps,
- rootContainerInstance
- ) {
- // TODO: Type this specific to this type of component.
- workInProgress.updateQueue = updatePayload;
- // If the update payload indicates that there is a change or if there
- // is a new ref we mark this as an update. All the work is done in commitWork.
- if (updatePayload) {
- markUpdate(workInProgress);
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+ for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(
+ returnFiber,
+ oldFiber,
+ newChildren[newIdx],
+ expirationTime
+ );
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (oldFiber === null) {
+ oldFiber = nextOldFiber;
}
- };
- updateHostText = function(current, workInProgress, oldText, newText) {
- // If the text differs, mark it as an update. All the work in done in commitWork.
- if (oldText !== newText) {
- markUpdate(workInProgress);
+ 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);
}
- };
- } else {
- invariant(false, "Mutating reconciler is disabled.");
+ }
+ 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;
}
- } else if (persistence) {
- if (enablePersistentReconciler) {
- // Persistent host tree mode
- var cloneInstance = persistence.cloneInstance,
- createContainerChildSet = persistence.createContainerChildSet,
- appendChildToContainerChildSet =
- persistence.appendChildToContainerChildSet,
- finalizeContainerChildren = persistence.finalizeContainerChildren;
- // An unfortunate fork of appendAllChildren because we have two different parent types.
+ if (newIdx === newChildren.length) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
- var appendAllChildrenToContainer = function(
- containerChildSet,
- workInProgress
- ) {
- // We only have the top Fiber that was created but we need recurse down its
- // children to find all the terminal nodes.
- var node = workInProgress.child;
- while (node !== null) {
- if (node.tag === HostComponent || node.tag === HostText) {
- appendChildToContainerChildSet(containerChildSet, node.stateNode);
- } else if (node.tag === HostPortal) {
- // If we have a portal child, then we don't want to traverse
- // down its children. Instead, we'll get insertions from each child in
- // the portal directly.
- } else if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- if (node === workInProgress) {
- return;
- }
- while (node.sibling === null) {
- if (node["return"] === null || node["return"] === workInProgress) {
- return;
- }
- node = node["return"];
- }
- node.sibling["return"] = node["return"];
- node = node.sibling;
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber = createChild(
+ returnFiber,
+ newChildren[newIdx],
+ expirationTime
+ );
+ if (!_newFiber) {
+ continue;
}
- };
- updateHostContainer = function(workInProgress) {
- var portalOrRoot = workInProgress.stateNode;
- var childrenUnchanged = workInProgress.firstEffect === null;
- if (childrenUnchanged) {
- // No changes, just reuse the existing instance.
+ lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber;
} else {
- var container = portalOrRoot.containerInfo;
- var newChildSet = createContainerChildSet(container);
- if (finalizeContainerChildren(container, newChildSet)) {
- markUpdate(workInProgress);
- }
- portalOrRoot.pendingChildren = newChildSet;
- // If children might have changed, we have to add them all to the set.
- appendAllChildrenToContainer(newChildSet, workInProgress);
- // Schedule an update on the container to swap out the container.
- markUpdate(workInProgress);
+ previousNewFiber.sibling = _newFiber;
}
- };
- updateHostComponent = function(
- current,
- workInProgress,
- updatePayload,
- type,
- oldProps,
- newProps,
- rootContainerInstance
- ) {
- // If there are no effects associated with this node, then none of our children had any updates.
- // This guarantees that we can reuse all of them.
- var childrenUnchanged = workInProgress.firstEffect === null;
- var currentInstance = current.stateNode;
- if (childrenUnchanged && updatePayload === null) {
- // No changes, just reuse the existing instance.
- // Note that this might release a previous clone.
- workInProgress.stateNode = currentInstance;
- } else {
- var recyclableInstance = workInProgress.stateNode;
- var newInstance = cloneInstance(
- currentInstance,
- updatePayload,
- type,
- oldProps,
- newProps,
- workInProgress,
- childrenUnchanged,
- recyclableInstance
- );
- if (
- finalizeInitialChildren(
- newInstance,
- type,
- newProps,
- rootContainerInstance
- )
- ) {
- markUpdate(workInProgress);
- }
- workInProgress.stateNode = newInstance;
- if (childrenUnchanged) {
- // If there are no other effects in this tree, we need to flag this node as having one.
- // Even though we're not going to use it for anything.
- // Otherwise parents won't know that there are new children to propagate upwards.
- markUpdate(workInProgress);
- } else {
- // If children might have changed, we have to add them all to the set.
- appendAllChildren(newInstance, workInProgress);
+ previousNewFiber = _newFiber;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; newIdx < newChildren.length; newIdx++) {
+ var _newFiber2 = updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ newChildren[newIdx],
+ expirationTime
+ );
+ if (_newFiber2) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber2.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren["delete"](
+ _newFiber2.key === null ? newIdx : _newFiber2.key
+ );
}
}
- };
- updateHostText = function(current, workInProgress, oldText, newText) {
- if (oldText !== newText) {
- // If the text content differs, we'll create a new text instance for it.
- var rootContainerInstance = getRootHostContainer();
- var currentHostContext = getHostContext();
- workInProgress.stateNode = createTextInstance(
- newText,
- rootContainerInstance,
- currentHostContext,
- workInProgress
- );
- // We'll have to mark it as having an effect, even though we won't use the effect for anything.
- // This lets the parents know that at least one of their children has changed.
- markUpdate(workInProgress);
+ lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber2;
+ } else {
+ previousNewFiber.sibling = _newFiber2;
}
- };
- } else {
- invariant(false, "Persistent reconciler is disabled.");
+ previousNewFiber = _newFiber2;
+ }
}
- } else {
- if (enableNoopReconciler) {
- // No host operations
- updateHostContainer = function(workInProgress) {
- // Noop
- };
- updateHostComponent = function(
- current,
- workInProgress,
- updatePayload,
- type,
- oldProps,
- newProps,
- rootContainerInstance
- ) {
- // Noop
- };
- updateHostText = function(current, workInProgress, oldText, newText) {
- // Noop
- };
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function(child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileChildrenIterator(
+ returnFiber,
+ currentFirstChild,
+ newChildrenIterable,
+ expirationTime
+ ) {
+ // This is the same implementation as reconcileChildrenArray(),
+ // but using the iterator instead.
+
+ var iteratorFn = getIteratorFn(newChildrenIterable);
+ invariant(
+ typeof iteratorFn === "function",
+ "An object is not an iterable. This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
+
+ {
+ // Warn about using Maps as children
+ if (typeof newChildrenIterable.entries === "function") {
+ var possibleMap = newChildrenIterable;
+ if (possibleMap.entries === iteratorFn) {
+ warning(
+ didWarnAboutMaps,
+ "Using Maps as children is unsupported and will likely yield " +
+ "unexpected results. Convert it to a sequence/iterable of keyed " +
+ "ReactElements instead.%s",
+ getCurrentFiberStackAddendum$1()
+ );
+ didWarnAboutMaps = true;
+ }
+ }
+
+ // First, validate keys.
+ // We'll get a different iterator later for the main pass.
+ var _newChildren = iteratorFn.call(newChildrenIterable);
+ if (_newChildren) {
+ var knownKeys = null;
+ var _step = _newChildren.next();
+ for (; !_step.done; _step = _newChildren.next()) {
+ var child = _step.value;
+ knownKeys = warnOnInvalidKey(child, knownKeys);
+ }
+ }
+ }
+
+ var newChildren = iteratorFn.call(newChildrenIterable);
+ invariant(newChildren != null, "An iterable object provided no iterator.");
+
+ var resultingFirstChild = null;
+ var previousNewFiber = null;
+
+ var oldFiber = currentFirstChild;
+ var lastPlacedIndex = 0;
+ var newIdx = 0;
+ var nextOldFiber = null;
+
+ var step = newChildren.next();
+ for (
+ ;
+ oldFiber !== null && !step.done;
+ newIdx++, step = newChildren.next()
+ ) {
+ if (oldFiber.index > newIdx) {
+ nextOldFiber = oldFiber;
+ oldFiber = null;
+ } else {
+ nextOldFiber = oldFiber.sibling;
+ }
+ var newFiber = updateSlot(
+ returnFiber,
+ oldFiber,
+ step.value,
+ expirationTime
+ );
+ if (newFiber === null) {
+ // TODO: This breaks on empty slots like null children. That's
+ // unfortunate because it triggers the slow path all the time. We need
+ // a better way to communicate whether this was a miss or null,
+ // boolean, undefined, etc.
+ if (!oldFiber) {
+ oldFiber = nextOldFiber;
+ }
+ break;
+ }
+ if (shouldTrackSideEffects) {
+ if (oldFiber && newFiber.alternate === null) {
+ // We matched the slot, but we didn't reuse the existing fiber, so we
+ // need to delete the existing child.
+ deleteChild(returnFiber, oldFiber);
+ }
+ }
+ lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = newFiber;
+ } else {
+ // TODO: Defer siblings if we're not at the right index for this slot.
+ // I.e. if we had null values before, then we want to defer this
+ // for each null value. However, we also don't want to call updateSlot
+ // with the previous one.
+ previousNewFiber.sibling = newFiber;
+ }
+ previousNewFiber = newFiber;
+ oldFiber = nextOldFiber;
+ }
+
+ if (step.done) {
+ // We've reached the end of the new children. We can delete the rest.
+ deleteRemainingChildren(returnFiber, oldFiber);
+ return resultingFirstChild;
+ }
+
+ if (oldFiber === null) {
+ // If we don't have any more existing children we can choose a fast path
+ // since the rest will all be insertions.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber3 = createChild(returnFiber, step.value, expirationTime);
+ if (_newFiber3 === null) {
+ continue;
+ }
+ lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ // TODO: Move out of the loop. This only happens for the first run.
+ resultingFirstChild = _newFiber3;
+ } else {
+ previousNewFiber.sibling = _newFiber3;
+ }
+ previousNewFiber = _newFiber3;
+ }
+ return resultingFirstChild;
+ }
+
+ // Add all children to a key map for quick lookups.
+ var existingChildren = mapRemainingChildren(returnFiber, oldFiber);
+
+ // Keep scanning and use the map to restore deleted items as moves.
+ for (; !step.done; newIdx++, step = newChildren.next()) {
+ var _newFiber4 = updateFromMap(
+ existingChildren,
+ returnFiber,
+ newIdx,
+ step.value,
+ expirationTime
+ );
+ if (_newFiber4 !== null) {
+ if (shouldTrackSideEffects) {
+ if (_newFiber4.alternate !== null) {
+ // The new fiber is a work in progress, but if there exists a
+ // current, that means that we reused the fiber. We need to delete
+ // it from the child list so that we don't add it to the deletion
+ // list.
+ existingChildren["delete"](
+ _newFiber4.key === null ? newIdx : _newFiber4.key
+ );
+ }
+ }
+ lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
+ if (previousNewFiber === null) {
+ resultingFirstChild = _newFiber4;
+ } else {
+ previousNewFiber.sibling = _newFiber4;
+ }
+ previousNewFiber = _newFiber4;
+ }
+ }
+
+ if (shouldTrackSideEffects) {
+ // Any existing children that weren't consumed above were deleted. We need
+ // to add them to the deletion list.
+ existingChildren.forEach(function(child) {
+ return deleteChild(returnFiber, child);
+ });
+ }
+
+ return resultingFirstChild;
+ }
+
+ function reconcileSingleTextNode(
+ returnFiber,
+ currentFirstChild,
+ textContent,
+ expirationTime
+ ) {
+ // There's no need to check for keys on text nodes since we don't have a
+ // way to define them.
+ if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
+ // We already have an existing node so let's just update it and delete
+ // the rest.
+ deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
+ var existing = useFiber(currentFirstChild, textContent, expirationTime);
+ existing["return"] = returnFiber;
+ return existing;
+ }
+ // The existing first child is not a text node so we need to create one
+ // and delete the existing ones.
+ deleteRemainingChildren(returnFiber, currentFirstChild);
+ var created = createFiberFromText(
+ textContent,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ created["return"] = returnFiber;
+ return created;
+ }
+
+ function reconcileSingleElement(
+ returnFiber,
+ currentFirstChild,
+ element,
+ expirationTime
+ ) {
+ var key = element.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (
+ child.tag === Fragment
+ ? element.type === REACT_FRAGMENT_TYPE
+ : child.type === element.type
+ ) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(
+ child,
+ element.type === REACT_FRAGMENT_TYPE
+ ? element.props.children
+ : element.props,
+ expirationTime
+ );
+ existing.ref = coerceRef(child, element);
+ existing["return"] = returnFiber;
+ {
+ existing._debugSource = element._source;
+ existing._debugOwner = element._owner;
+ }
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ if (element.type === REACT_FRAGMENT_TYPE) {
+ var created = createFiberFromFragment(
+ element.props.children,
+ returnFiber.internalContextTag,
+ expirationTime,
+ element.key
+ );
+ created["return"] = returnFiber;
+ return created;
+ } else {
+ var _created4 = createFiberFromElement(
+ element,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ _created4.ref = coerceRef(currentFirstChild, element);
+ _created4["return"] = returnFiber;
+ return _created4;
+ }
+ }
+
+ function reconcileSinglePortal(
+ returnFiber,
+ currentFirstChild,
+ portal,
+ expirationTime
+ ) {
+ var key = portal.key;
+ var child = currentFirstChild;
+ while (child !== null) {
+ // TODO: If key === null and child.key === null, then this only applies to
+ // the first item in the list.
+ if (child.key === key) {
+ if (
+ child.tag === HostPortal &&
+ child.stateNode.containerInfo === portal.containerInfo &&
+ child.stateNode.implementation === portal.implementation
+ ) {
+ deleteRemainingChildren(returnFiber, child.sibling);
+ var existing = useFiber(child, portal.children || [], expirationTime);
+ existing["return"] = returnFiber;
+ return existing;
+ } else {
+ deleteRemainingChildren(returnFiber, child);
+ break;
+ }
+ } else {
+ deleteChild(returnFiber, child);
+ }
+ child = child.sibling;
+ }
+
+ var created = createFiberFromPortal(
+ portal,
+ returnFiber.internalContextTag,
+ expirationTime
+ );
+ created["return"] = returnFiber;
+ return created;
+ }
+
+ // This API will tag the children with the side-effect of the reconciliation
+ // itself. They will be added to the side-effect list as we pass through the
+ // children and the parent.
+ function reconcileChildFibers(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ expirationTime
+ ) {
+ // This function is not recursive.
+ // If the top level item is an array, we treat it as a set of children,
+ // not as a fragment. Nested arrays on the other hand will be treated as
+ // fragment nodes. Recursion happens at the normal flow.
+
+ // Handle top level unkeyed fragments as if they were arrays.
+ // This leads to an ambiguity between <>{[...]}> and <>...>.
+ // We treat the ambiguous cases above the same.
+ if (
+ typeof newChild === "object" &&
+ newChild !== null &&
+ newChild.type === REACT_FRAGMENT_TYPE &&
+ newChild.key === null
+ ) {
+ newChild = newChild.props.children;
+ }
+
+ // Handle object types
+ var isObject = typeof newChild === "object" && newChild !== null;
+
+ if (isObject) {
+ switch (newChild.$$typeof) {
+ case REACT_ELEMENT_TYPE:
+ return placeSingleChild(
+ reconcileSingleElement(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ expirationTime
+ )
+ );
+ case REACT_PORTAL_TYPE:
+ return placeSingleChild(
+ reconcileSinglePortal(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ expirationTime
+ )
+ );
+ }
+ }
+
+ if (typeof newChild === "string" || typeof newChild === "number") {
+ return placeSingleChild(
+ reconcileSingleTextNode(
+ returnFiber,
+ currentFirstChild,
+ "" + newChild,
+ expirationTime
+ )
+ );
+ }
+
+ if (isArray$1(newChild)) {
+ return reconcileChildrenArray(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ expirationTime
+ );
+ }
+
+ if (getIteratorFn(newChild)) {
+ return reconcileChildrenIterator(
+ returnFiber,
+ currentFirstChild,
+ newChild,
+ expirationTime
+ );
+ }
+
+ if (isObject) {
+ throwOnInvalidObjectType(returnFiber, newChild);
+ }
+
+ {
+ if (typeof newChild === "function") {
+ warnOnFunctionType();
+ }
+ }
+ if (typeof newChild === "undefined") {
+ // If the new child is undefined, and the return fiber is a composite
+ // component, throw an error. If Fiber return types are disabled,
+ // we already threw above.
+ switch (returnFiber.tag) {
+ case ClassComponent: {
+ {
+ var instance = returnFiber.stateNode;
+ if (instance.render._isMockFunction) {
+ // We allow auto-mocks to proceed as if they're returning null.
+ break;
+ }
+ }
+ }
+ // Intentionally fall through to the next case, which handles both
+ // functions and classes
+ // eslint-disable-next-lined no-fallthrough
+ case FunctionalComponent: {
+ var Component = returnFiber.type;
+ invariant(
+ false,
+ "%s(...): Nothing was returned from render. This usually means a " +
+ "return statement is missing. Or, to render nothing, " +
+ "return null.",
+ Component.displayName || Component.name || "Component"
+ );
+ }
+ }
+ }
+
+ // Remaining cases are all treated as empty.
+ return deleteRemainingChildren(returnFiber, currentFirstChild);
+ }
+
+ return reconcileChildFibers;
+}
+
+var reconcileChildFibers = ChildReconciler(true);
+var mountChildFibers = ChildReconciler(false);
+
+function cloneChildFibers(current, workInProgress) {
+ invariant(
+ current === null || workInProgress.child === current.child,
+ "Resuming work not yet implemented."
+ );
+
+ if (workInProgress.child === null) {
+ return;
+ }
+
+ var currentChild = workInProgress.child;
+ var newChild = createWorkInProgress(
+ currentChild,
+ currentChild.pendingProps,
+ currentChild.expirationTime
+ );
+ workInProgress.child = newChild;
+
+ newChild["return"] = workInProgress;
+ while (currentChild.sibling !== null) {
+ currentChild = currentChild.sibling;
+ newChild = newChild.sibling = createWorkInProgress(
+ currentChild,
+ currentChild.pendingProps,
+ currentChild.expirationTime
+ );
+ newChild["return"] = workInProgress;
+ }
+ newChild.sibling = null;
+}
+
+var stack = [];
+var index$1 = -1;
+
+var rendererSigil = void 0;
+{
+ // Use this to detect multiple renderers using the same context
+ rendererSigil = {};
+}
+
+function pushProvider(providerFiber) {
+ index$1 += 1;
+ stack[index$1] = providerFiber;
+ var context = providerFiber.type.context;
+ context.currentValue = providerFiber.pendingProps.value;
+ context.changedBits = providerFiber.stateNode;
+
+ {
+ warning(
+ context._currentRenderer === null ||
+ context._currentRenderer === rendererSigil,
+ "Detected multiple renderers concurrently rendering the " +
+ "same context provider. This is currently unsupported."
+ );
+ context._currentRenderer = rendererSigil;
+ }
+}
+
+function popProvider(providerFiber) {
+ {
+ warning(
+ index$1 > -1 && providerFiber === stack[index$1],
+ "Unexpected pop."
+ );
+ }
+ stack[index$1] = null;
+ index$1 -= 1;
+ var context = providerFiber.type.context;
+ if (index$1 < 0) {
+ context.currentValue = context.defaultValue;
+ context.changedBits = 0;
+ } else {
+ var previousProviderFiber = stack[index$1];
+ context.currentValue = previousProviderFiber.pendingProps.value;
+ context.changedBits = previousProviderFiber.stateNode;
+ }
+}
+
+function resetProviderStack() {
+ for (var i = index$1; i > -1; i--) {
+ var providerFiber = stack[i];
+ var context = providerFiber.type.context;
+ context.currentValue = context.defaultValue;
+ context.changedBits = 0;
+ stack[i] = null;
+ {
+ context._currentRenderer = null;
+ }
+ }
+}
+
+var didWarnAboutBadClass = void 0;
+var didWarnAboutGetDerivedStateOnFunctionalComponent = void 0;
+var didWarnAboutStatelessRefs = void 0;
+
+{
+ didWarnAboutBadClass = {};
+ didWarnAboutGetDerivedStateOnFunctionalComponent = {};
+ didWarnAboutStatelessRefs = {};
+}
+
+var ReactFiberBeginWork = function(
+ config,
+ hostContext,
+ hydrationContext,
+ scheduleWork,
+ computeExpirationForFiber
+) {
+ var shouldSetTextContent = config.shouldSetTextContent,
+ shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree;
+ var pushHostContext = hostContext.pushHostContext,
+ pushHostContainer = hostContext.pushHostContainer;
+ var enterHydrationState = hydrationContext.enterHydrationState,
+ resetHydrationState = hydrationContext.resetHydrationState,
+ tryToClaimNextHydratableInstance =
+ hydrationContext.tryToClaimNextHydratableInstance;
+
+ var _ReactFiberClassCompo = ReactFiberClassComponent(
+ scheduleWork,
+ computeExpirationForFiber,
+ memoizeProps,
+ memoizeState
+ ),
+ adoptClassInstance = _ReactFiberClassCompo.adoptClassInstance,
+ callGetDerivedStateFromProps =
+ _ReactFiberClassCompo.callGetDerivedStateFromProps,
+ constructClassInstance = _ReactFiberClassCompo.constructClassInstance,
+ mountClassInstance = _ReactFiberClassCompo.mountClassInstance,
+ updateClassInstance = _ReactFiberClassCompo.updateClassInstance;
+
+ // TODO: Remove this and use reconcileChildrenAtExpirationTime directly.
+
+ function reconcileChildren(current, workInProgress, nextChildren) {
+ reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ workInProgress.expirationTime
+ );
+ }
+
+ function reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ renderExpirationTime
+ ) {
+ if (current === null) {
+ // If this is a fresh new component that hasn't been rendered yet, we
+ // won't update its child set by applying minimal side-effects. Instead,
+ // we will add them all to the child before it gets rendered. That means
+ // we can optimize this reconciliation pass by not tracking side-effects.
+ workInProgress.child = mountChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderExpirationTime
+ );
+ } else {
+ // If the current child is the same as the work in progress, it means that
+ // we haven't yet started any work on these children. Therefore, we use
+ // the clone algorithm to create a copy of all the current children.
+
+ // If we had any progressed work already, that is invalid at this point so
+ // let's throw it out.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ current.child,
+ nextChildren,
+ renderExpirationTime
+ );
+ }
+ }
+
+ function updateFragment(current, workInProgress) {
+ var nextChildren = workInProgress.pendingProps;
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextChildren) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ return workInProgress.child;
+ }
+
+ function updateMode(current, workInProgress) {
+ var nextChildren = workInProgress.pendingProps.children;
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (
+ nextChildren === null ||
+ workInProgress.memoizedProps === nextChildren
+ ) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ return workInProgress.child;
+ }
+
+ function markRef(current, workInProgress) {
+ var ref = workInProgress.ref;
+ if (ref !== null && (!current || current.ref !== ref)) {
+ // Schedule a Ref effect
+ workInProgress.effectTag |= Ref;
+ }
+ }
+
+ function updateFunctionalComponent(current, workInProgress) {
+ var fn = workInProgress.type;
+ var nextProps = workInProgress.pendingProps;
+
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
} else {
- invariant(false, "Noop reconciler is disabled.");
+ if (workInProgress.memoizedProps === nextProps) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ // TODO: consider bringing fn.shouldComponentUpdate() back.
+ // It used to be here.
+ }
+
+ var unmaskedContext = getUnmaskedContext(workInProgress);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ var nextChildren = void 0;
+
+ {
+ ReactCurrentOwner.current = workInProgress;
+ ReactDebugCurrentFiber.setCurrentPhase("render");
+ nextChildren = fn(nextProps, context);
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
+ }
+
+ function updateClassComponent(current, workInProgress, renderExpirationTime) {
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = pushContextProvider(workInProgress);
+
+ var shouldUpdate = void 0;
+ if (current === null) {
+ if (!workInProgress.stateNode) {
+ // In the initial pass we might need to construct the instance.
+ constructClassInstance(workInProgress, workInProgress.pendingProps);
+ mountClassInstance(workInProgress, renderExpirationTime);
+
+ shouldUpdate = true;
+ } else {
+ invariant(false, "Resuming work not yet implemented.");
+ // In a resume, we'll already have an instance we can reuse.
+ // shouldUpdate = resumeMountClassInstance(workInProgress, renderExpirationTime);
+ }
+ } else {
+ shouldUpdate = updateClassInstance(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ }
+ return finishClassComponent(
+ current,
+ workInProgress,
+ shouldUpdate,
+ hasContext
+ );
+ }
+
+ function finishClassComponent(
+ current,
+ workInProgress,
+ shouldUpdate,
+ hasContext
+ ) {
+ // Refs should update even if shouldComponentUpdate returns false
+ markRef(current, workInProgress);
+
+ if (!shouldUpdate) {
+ // Context providers should defer to sCU for rendering
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, false);
+ }
+
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+
+ var instance = workInProgress.stateNode;
+
+ // Rerender
+ ReactCurrentOwner.current = workInProgress;
+ var nextChildren = void 0;
+ {
+ ReactDebugCurrentFiber.setCurrentPhase("render");
+ nextChildren = instance.render();
+ if (
+ debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & StrictMode)
+ ) {
+ instance.render();
+ }
+ ReactDebugCurrentFiber.setCurrentPhase(null);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+ reconcileChildren(current, workInProgress, nextChildren);
+ // Memoize props and state using the values we just used to render.
+ // TODO: Restructure so we never read values from the instance.
+ memoizeState(workInProgress, instance.state);
+ memoizeProps(workInProgress, instance.props);
+
+ // The context might have changed so we need to recalculate it.
+ if (hasContext) {
+ invalidateContextProvider(workInProgress, true);
+ }
+
+ return workInProgress.child;
+ }
+
+ function pushHostRootContext(workInProgress) {
+ var root = workInProgress.stateNode;
+ if (root.pendingContext) {
+ pushTopLevelContextObject(
+ workInProgress,
+ root.pendingContext,
+ root.pendingContext !== root.context
+ );
+ } else if (root.context) {
+ // Should always be set
+ pushTopLevelContextObject(workInProgress, root.context, false);
+ }
+ pushHostContainer(workInProgress, root.containerInfo);
+ }
+
+ function updateHostRoot(current, workInProgress, renderExpirationTime) {
+ pushHostRootContext(workInProgress);
+ var updateQueue = workInProgress.updateQueue;
+ if (updateQueue !== null) {
+ var prevState = workInProgress.memoizedState;
+ var state = processUpdateQueue(
+ current,
+ workInProgress,
+ updateQueue,
+ null,
+ null,
+ renderExpirationTime
+ );
+ if (prevState === state) {
+ // If the state is the same as before, that's a bailout because we had
+ // no work that expires at this time.
+ resetHydrationState();
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ var element = state.element;
+ var root = workInProgress.stateNode;
+ if (
+ (current === null || current.child === null) &&
+ root.hydrate &&
+ enterHydrationState(workInProgress)
+ ) {
+ // If we don't have any current children this might be the first pass.
+ // We always try to hydrate. If this isn't a hydration pass there won't
+ // be any children to hydrate which is effectively the same thing as
+ // not hydrating.
+
+ // This is a bit of a hack. We track the host root as a placement to
+ // know that we're currently in a mounting state. That way isMounted
+ // works as expected. We must reset this before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag |= Placement;
+
+ // Ensure that children mount into this root without tracking
+ // side-effects. This ensures that we don't store Placement effects on
+ // nodes that will be hydrated.
+ workInProgress.child = mountChildFibers(
+ workInProgress,
+ null,
+ element,
+ renderExpirationTime
+ );
+ } else {
+ // Otherwise reset hydration state in case we aborted and resumed another
+ // root.
+ resetHydrationState();
+ reconcileChildren(current, workInProgress, element);
+ }
+ memoizeState(workInProgress, state);
+ return workInProgress.child;
+ }
+ resetHydrationState();
+ // If there is no update queue, that's a bailout because the root has no props.
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+
+ function updateHostComponent(current, workInProgress, renderExpirationTime) {
+ pushHostContext(workInProgress);
+
+ if (current === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+
+ var type = workInProgress.type;
+ var memoizedProps = workInProgress.memoizedProps;
+ var nextProps = workInProgress.pendingProps;
+ var prevProps = current !== null ? current.memoizedProps : null;
+
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (memoizedProps === nextProps) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+
+ var nextChildren = nextProps.children;
+ var isDirectTextChild = shouldSetTextContent(type, nextProps);
+
+ if (isDirectTextChild) {
+ // We special case a direct text child of a host node. This is a common
+ // case. We won't handle it as a reified child. We will instead handle
+ // this in the host environment that also have access to this prop. That
+ // avoids allocating another HostText fiber and traversing it.
+ nextChildren = null;
+ } else if (prevProps && shouldSetTextContent(type, prevProps)) {
+ // If we're switching from a direct text child to a normal child, or to
+ // empty, we need to schedule the text content to be reset.
+ workInProgress.effectTag |= ContentReset;
+ }
+
+ markRef(current, workInProgress);
+
+ // Check the host config to see if the children are offscreen/hidden.
+ if (
+ renderExpirationTime !== Never &&
+ workInProgress.internalContextTag & AsyncUpdates &&
+ shouldDeprioritizeSubtree(type, nextProps)
+ ) {
+ // Down-prioritize the children.
+ workInProgress.expirationTime = Never;
+ // Bailout and come back to this fiber later.
+ return null;
}
+
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextProps);
+ return workInProgress.child;
}
- function completeWork(current, workInProgress, renderExpirationTime) {
- var newProps = workInProgress.pendingProps;
- switch (workInProgress.tag) {
- case FunctionalComponent:
- return null;
- case ClassComponent: {
- // We are leaving this subtree, so pop context if any.
- popContextProvider(workInProgress);
- return null;
- }
- case HostRoot: {
- popHostContainer(workInProgress);
- popTopLevelContextObject(workInProgress);
- var fiberRoot = workInProgress.stateNode;
- if (fiberRoot.pendingContext) {
- fiberRoot.context = fiberRoot.pendingContext;
- fiberRoot.pendingContext = null;
- }
+ function updateHostText(current, workInProgress) {
+ if (current === null) {
+ tryToClaimNextHydratableInstance(workInProgress);
+ }
+ var nextProps = workInProgress.pendingProps;
+ memoizeProps(workInProgress, nextProps);
+ // Nothing to do here. This is terminal. We'll do the completion step
+ // immediately after.
+ return null;
+ }
- if (current === null || current.child === null) {
- // If we hydrated, pop so that we can delete any remaining children
- // that weren't hydrated.
- popHydrationState(workInProgress);
- // This resets the hacky state to fix isMounted before committing.
- // TODO: Delete this when we delete isMounted and findDOMNode.
- workInProgress.effectTag &= ~Placement;
+ function mountIndeterminateComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ) {
+ invariant(
+ current === null,
+ "An indeterminate component should never have mounted. This error is " +
+ "likely caused by a bug in React. Please file an issue."
+ );
+ var fn = workInProgress.type;
+ var props = workInProgress.pendingProps;
+ var unmaskedContext = getUnmaskedContext(workInProgress);
+ var context = getMaskedContext(workInProgress, unmaskedContext);
+
+ var value = void 0;
+
+ {
+ if (fn.prototype && typeof fn.prototype.render === "function") {
+ var componentName = getComponentName(workInProgress) || "Unknown";
+
+ if (!didWarnAboutBadClass[componentName]) {
+ warning(
+ false,
+ "The <%s /> component appears to have a render method, but doesn't extend React.Component. " +
+ "This is likely to cause errors. Change %s to extend React.Component instead.",
+ componentName,
+ componentName
+ );
+ didWarnAboutBadClass[componentName] = true;
}
- updateHostContainer(workInProgress);
- return null;
}
- case HostComponent: {
- popHostContext(workInProgress);
- var rootContainerInstance = getRootHostContainer();
- var type = workInProgress.type;
- if (current !== null && workInProgress.stateNode != null) {
- // If we have an alternate, that means this is an update and we need to
- // schedule a side-effect to do the updates.
- var oldProps = current.memoizedProps;
- // If we get updated because one of our children updated, we don't
- // have newProps so we'll have to reuse them.
- // TODO: Split the update API as separate for the props vs. children.
- // Even better would be if children weren't special cased at all tho.
- var instance = workInProgress.stateNode;
- var currentHostContext = getHostContext();
- var updatePayload = prepareUpdate(
- instance,
- type,
- oldProps,
- newProps,
- rootContainerInstance,
- currentHostContext
+ ReactCurrentOwner.current = workInProgress;
+ value = fn(props, context);
+ }
+ // React DevTools reads this flag.
+ workInProgress.effectTag |= PerformedWork;
+
+ if (
+ typeof value === "object" &&
+ value !== null &&
+ typeof value.render === "function" &&
+ value.$$typeof === undefined
+ ) {
+ var Component = workInProgress.type;
+
+ // Proceed under the assumption that this is a class instance
+ workInProgress.tag = ClassComponent;
+
+ workInProgress.memoizedState =
+ value.state !== null && value.state !== undefined ? value.state : null;
+
+ if (typeof Component.getDerivedStateFromProps === "function") {
+ var partialState = callGetDerivedStateFromProps(
+ workInProgress,
+ value,
+ props
+ );
+
+ if (partialState !== null && partialState !== undefined) {
+ workInProgress.memoizedState = Object.assign(
+ {},
+ workInProgress.memoizedState,
+ partialState
);
+ }
+ }
- updateHostComponent(
- current,
- workInProgress,
- updatePayload,
- type,
- oldProps,
- newProps,
- rootContainerInstance
+ // Push context providers early to prevent context stack mismatches.
+ // During mounting we don't know the child context yet as the instance doesn't exist.
+ // We will invalidate the child context in finishClassComponent() right after rendering.
+ var hasContext = pushContextProvider(workInProgress);
+ adoptClassInstance(workInProgress, value);
+ mountClassInstance(workInProgress, renderExpirationTime);
+ return finishClassComponent(current, workInProgress, true, hasContext);
+ } else {
+ // Proceed under the assumption that this is a functional component
+ workInProgress.tag = FunctionalComponent;
+ {
+ var _Component = workInProgress.type;
+
+ if (_Component) {
+ warning(
+ !_Component.childContextTypes,
+ "%s(...): childContextTypes cannot be defined on a functional component.",
+ _Component.displayName || _Component.name || "Component"
);
+ }
+ if (workInProgress.ref !== null) {
+ var info = "";
+ var ownerName = ReactDebugCurrentFiber.getCurrentFiberOwnerName();
+ if (ownerName) {
+ info += "\n\nCheck the render method of `" + ownerName + "`.";
+ }
- if (current.ref !== workInProgress.ref) {
- markRef(workInProgress);
+ var warningKey = ownerName || workInProgress._debugID || "";
+ var debugSource = workInProgress._debugSource;
+ if (debugSource) {
+ warningKey = debugSource.fileName + ":" + debugSource.lineNumber;
}
- } else {
- if (!newProps) {
- invariant(
- workInProgress.stateNode !== null,
- "We must have new props for new mounts. This error is likely " +
- "caused by a bug in React. Please file an issue."
+ if (!didWarnAboutStatelessRefs[warningKey]) {
+ didWarnAboutStatelessRefs[warningKey] = true;
+ warning(
+ false,
+ "Stateless function components cannot be given refs. " +
+ "Attempts to access this ref will fail.%s%s",
+ info,
+ ReactDebugCurrentFiber.getCurrentFiberStackAddendum()
);
- // This can happen when we abort work.
- return null;
}
+ }
- var _currentHostContext = getHostContext();
- // TODO: Move createInstance to beginWork and keep it on a context
- // "stack" as the parent. Then append children as we go in beginWork
- // or completeWork depending on we want to add then top->down or
- // bottom->up. Top->down is faster in IE11.
- var wasHydrated = popHydrationState(workInProgress);
- if (wasHydrated) {
- // TODO: Move this and createInstance step into the beginPhase
- // to consolidate.
- if (
- prepareToHydrateHostInstance(
- workInProgress,
- rootContainerInstance,
- _currentHostContext
- )
- ) {
- // If changes to the hydrated node needs to be applied at the
- // commit-phase we mark this as such.
- markUpdate(workInProgress);
- }
- } else {
- var _instance = createInstance(
- type,
- newProps,
- rootContainerInstance,
- _currentHostContext,
- workInProgress
+ if (typeof fn.getDerivedStateFromProps === "function") {
+ var _componentName = getComponentName(workInProgress) || "Unknown";
+
+ if (
+ !didWarnAboutGetDerivedStateOnFunctionalComponent[_componentName]
+ ) {
+ warning(
+ false,
+ "%s: Stateless functional components do not support getDerivedStateFromProps.",
+ _componentName
);
+ didWarnAboutGetDerivedStateOnFunctionalComponent[
+ _componentName
+ ] = true;
+ }
+ }
+ }
+ reconcileChildren(current, workInProgress, value);
+ memoizeProps(workInProgress, props);
+ return workInProgress.child;
+ }
+ }
+
+ function updateCallComponent(current, workInProgress, renderExpirationTime) {
+ var nextProps = workInProgress.pendingProps;
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextProps) {
+ nextProps = workInProgress.memoizedProps;
+ // TODO: When bailing out, we might need to return the stateNode instead
+ // of the child. To check it for work.
+ // return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+
+ var nextChildren = nextProps.children;
+
+ // The following is a fork of reconcileChildrenAtExpirationTime but using
+ // stateNode to store the child.
+ if (current === null) {
+ workInProgress.stateNode = mountChildFibers(
+ workInProgress,
+ workInProgress.stateNode,
+ nextChildren,
+ renderExpirationTime
+ );
+ } else {
+ workInProgress.stateNode = reconcileChildFibers(
+ workInProgress,
+ current.stateNode,
+ nextChildren,
+ renderExpirationTime
+ );
+ }
+
+ memoizeProps(workInProgress, nextProps);
+ // This doesn't take arbitrary time so we could synchronously just begin
+ // eagerly do the work of workInProgress.child as an optimization.
+ return workInProgress.stateNode;
+ }
+
+ function updatePortalComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ) {
+ pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
+ var nextChildren = workInProgress.pendingProps;
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (workInProgress.memoizedProps === nextChildren) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
- appendAllChildren(_instance, workInProgress);
+ if (current === null) {
+ // Portals are special because we don't append the children during mount
+ // but at commit. Therefore we need to track insertions which the normal
+ // flow doesn't do during mount. This doesn't happen at the root because
+ // the root always starts with a "current" with a null child.
+ // TODO: Consider unifying this with how the root works.
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ null,
+ nextChildren,
+ renderExpirationTime
+ );
+ memoizeProps(workInProgress, nextChildren);
+ } else {
+ reconcileChildren(current, workInProgress, nextChildren);
+ memoizeProps(workInProgress, nextChildren);
+ }
+ return workInProgress.child;
+ }
- // Certain renderers require commit-time effects for initial mount.
- // (eg DOM renderer supports auto-focus for certain elements).
- // Make sure such renderers get scheduled for later work.
- if (
- finalizeInitialChildren(
- _instance,
- type,
- newProps,
- rootContainerInstance
- )
- ) {
- markUpdate(workInProgress);
+ function propagateContextChange(
+ workInProgress,
+ context,
+ changedBits,
+ renderExpirationTime
+ ) {
+ if (enableNewContextAPI) {
+ var _fiber = workInProgress.child;
+ while (_fiber !== null) {
+ var nextFiber = void 0;
+ // Visit this fiber.
+ switch (_fiber.tag) {
+ case ContextConsumer:
+ // Check if the context matches.
+ var observedBits = _fiber.stateNode | 0;
+ if (_fiber.type === context && (observedBits & changedBits) !== 0) {
+ // Update the expiration time of all the ancestors, including
+ // the alternates.
+ var node = _fiber;
+ while (node !== null) {
+ var alternate = node.alternate;
+ if (
+ node.expirationTime === NoWork ||
+ node.expirationTime > renderExpirationTime
+ ) {
+ node.expirationTime = renderExpirationTime;
+ if (
+ alternate !== null &&
+ (alternate.expirationTime === NoWork ||
+ alternate.expirationTime > renderExpirationTime)
+ ) {
+ alternate.expirationTime = renderExpirationTime;
+ }
+ } else if (
+ alternate !== null &&
+ (alternate.expirationTime === NoWork ||
+ alternate.expirationTime > renderExpirationTime)
+ ) {
+ alternate.expirationTime = renderExpirationTime;
+ } else {
+ // Neither alternate was updated, which means the rest of the
+ // ancestor path already has sufficient priority.
+ break;
+ }
+ node = node["return"];
+ }
+ // Don't scan deeper than a matching consumer. When we render the
+ // consumer, we'll continue scanning from that point. This way the
+ // scanning work is time-sliced.
+ nextFiber = null;
+ } else {
+ // Traverse down.
+ nextFiber = _fiber.child;
}
- workInProgress.stateNode = _instance;
- }
-
- if (workInProgress.ref !== null) {
- // If there is a ref on a host node we need to schedule a callback
- markRef(workInProgress);
+ break;
+ case ContextProvider:
+ // Don't scan deeper if this is a matching provider
+ nextFiber =
+ _fiber.type === workInProgress.type ? null : _fiber.child;
+ break;
+ default:
+ // Traverse down.
+ nextFiber = _fiber.child;
+ 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;
+ while (nextFiber !== null) {
+ if (nextFiber === workInProgress) {
+ // We're back to the root of this subtree. Exit.
+ nextFiber = null;
+ break;
+ }
+ var sibling = nextFiber.sibling;
+ if (sibling !== null) {
+ nextFiber = sibling;
+ break;
+ }
+ // No more siblings. Traverse up.
+ nextFiber = nextFiber["return"];
}
}
- return null;
+ _fiber = nextFiber;
}
- 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);
+ }
+ }
+
+ function updateContextProvider(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ) {
+ if (enableNewContextAPI) {
+ var providerType = workInProgress.type;
+ var context = providerType.context;
+
+ var newProps = workInProgress.pendingProps;
+ var oldProps = workInProgress.memoizedProps;
+
+ if (hasContextChanged()) {
+ // Normally we can bail out on props equality but if context has changed
+ // we don't do the bailout and we have to reuse existing props instead.
+ } else if (oldProps === newProps) {
+ workInProgress.stateNode = 0;
+ pushProvider(workInProgress);
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ workInProgress.memoizedProps = newProps;
+
+ var newValue = newProps.value;
+
+ var changedBits = void 0;
+ if (oldProps === null) {
+ // Initial render
+ changedBits = MAX_SIGNED_31_BIT_INT;
+ } else {
+ var oldValue = oldProps.value;
+ // Use Object.is to compare the new context value to the old value.
+ // Inlined Object.is polyfill.
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
+ if (
+ (oldValue === newValue &&
+ (oldValue !== 0 || 1 / oldValue === 1 / newValue)) ||
+ (oldValue !== oldValue && newValue !== newValue) // eslint-disable-line no-self-compare
+ ) {
+ // No change.
+ changedBits = 0;
} else {
- if (typeof newText !== "string") {
- invariant(
- workInProgress.stateNode !== null,
- "We must have new props for new mounts. This error is likely " +
- "caused by a bug in React. Please file an issue."
+ changedBits =
+ context.calculateChangedBits !== null
+ ? context.calculateChangedBits(oldValue, newValue)
+ : MAX_SIGNED_31_BIT_INT;
+ {
+ warning(
+ (changedBits & MAX_SIGNED_31_BIT_INT) === changedBits,
+ "calculateChangedBits: Expected the return value to be a " +
+ "31-bit integer. Instead received: %s",
+ changedBits
);
- // This can happen when we abort work.
- return null;
}
- var _rootContainerInstance = getRootHostContainer();
- var _currentHostContext2 = getHostContext();
- var _wasHydrated = popHydrationState(workInProgress);
- if (_wasHydrated) {
- if (prepareToHydrateHostTextInstance(workInProgress)) {
- markUpdate(workInProgress);
- }
- } else {
- workInProgress.stateNode = createTextInstance(
- newText,
- _rootContainerInstance,
- _currentHostContext2,
- workInProgress
+ changedBits |= 0;
+
+ if (changedBits !== 0) {
+ propagateContextChange(
+ workInProgress,
+ context,
+ changedBits,
+ renderExpirationTime
);
}
}
- return null;
}
+
+ workInProgress.stateNode = changedBits;
+ pushProvider(workInProgress);
+
+ if (oldProps !== null && oldProps.children === newProps.children) {
+ return bailoutOnAlreadyFinishedWork(current, workInProgress);
+ }
+ var newChildren = newProps.children;
+ reconcileChildren(current, workInProgress, newChildren);
+ return workInProgress.child;
+ } else {
+ return null;
+ }
+ }
+
+ function updateContextConsumer(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ) {
+ if (enableNewContextAPI) {
+ var context = workInProgress.type;
+ var newProps = workInProgress.pendingProps;
+
+ var newValue = context.currentValue;
+ var changedBits = context.changedBits;
+
+ if (changedBits !== 0) {
+ // Context change propagation stops at matching consumers, for time-
+ // slicing. Continue the propagation here.
+ propagateContextChange(
+ workInProgress,
+ context,
+ changedBits,
+ renderExpirationTime
+ );
+ }
+
+ // Store the observedBits on the fiber's stateNode for quick access.
+ var observedBits = newProps.observedBits;
+ if (observedBits === undefined || observedBits === null) {
+ // Subscribe to all changes by default
+ observedBits = MAX_SIGNED_31_BIT_INT;
+ }
+ workInProgress.stateNode = observedBits;
+
+ var newChildren = newProps.render(newValue);
+ reconcileChildren(current, workInProgress, newChildren);
+ return workInProgress.child;
+ } else {
+ return null;
+ }
+ }
+
+ /*
+ function reuseChildrenEffects(returnFiber : Fiber, firstChild : Fiber) {
+ let child = firstChild;
+ do {
+ // Ensure that the first and last effect of the parent corresponds
+ // to the children's first and last effect.
+ if (!returnFiber.firstEffect) {
+ returnFiber.firstEffect = child.firstEffect;
+ }
+ if (child.lastEffect) {
+ if (returnFiber.lastEffect) {
+ returnFiber.lastEffect.nextEffect = child.firstEffect;
+ }
+ returnFiber.lastEffect = child.lastEffect;
+ }
+ } while (child = child.sibling);
+ }
+ */
+
+ function bailoutOnAlreadyFinishedWork(current, workInProgress) {
+ cancelWorkTimer(workInProgress);
+
+ // TODO: We should ideally be able to bail out early if the children have no
+ // more work to do. However, since we don't have a separation of this
+ // Fiber's priority and its children yet - we don't know without doing lots
+ // of the same work we do anyway. Once we have that separation we can just
+ // bail out here if the children has no more work at this priority level.
+ // if (workInProgress.priorityOfChildren <= priorityLevel) {
+ // // If there are side-effects in these children that have not yet been
+ // // committed we need to ensure that they get properly transferred up.
+ // if (current && current.child !== workInProgress.child) {
+ // reuseChildrenEffects(workInProgress, child);
+ // }
+ // return null;
+ // }
+
+ cloneChildFibers(current, workInProgress);
+ return workInProgress.child;
+ }
+
+ function bailoutOnLowPriority(current, workInProgress) {
+ cancelWorkTimer(workInProgress);
+
+ // TODO: Handle HostComponent tags here as well and call pushHostContext()?
+ // See PR 8590 discussion for context
+ switch (workInProgress.tag) {
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ break;
+ case ClassComponent:
+ pushContextProvider(workInProgress);
+ break;
+ case HostPortal:
+ pushHostContainer(
+ workInProgress,
+ workInProgress.stateNode.containerInfo
+ );
+ break;
+ case ContextProvider:
+ pushProvider(workInProgress);
+ break;
+ }
+ // TODO: What if this is currently in progress?
+ // How can that happen? How is this not being cloned?
+ return null;
+ }
+
+ // TODO: Delete memoizeProps/State and move to reconcile/bailout instead
+ function memoizeProps(workInProgress, nextProps) {
+ workInProgress.memoizedProps = nextProps;
+ }
+
+ function memoizeState(workInProgress, nextState) {
+ workInProgress.memoizedState = nextState;
+ // Don't reset the updateQueue, in case there are pending updates. Resetting
+ // is handled by processUpdateQueue.
+ }
+
+ function beginWork(current, workInProgress, renderExpirationTime) {
+ if (
+ workInProgress.expirationTime === NoWork ||
+ workInProgress.expirationTime > renderExpirationTime
+ ) {
+ return bailoutOnLowPriority(current, workInProgress);
+ }
+
+ switch (workInProgress.tag) {
+ case IndeterminateComponent:
+ return mountIndeterminateComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case FunctionalComponent:
+ return updateFunctionalComponent(current, workInProgress);
+ case ClassComponent:
+ return updateClassComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case HostRoot:
+ return updateHostRoot(current, workInProgress, renderExpirationTime);
+ case HostComponent:
+ return updateHostComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case HostText:
+ return updateHostText(current, workInProgress);
+ case CallHandlerPhase:
+ // This is a restart. Reset the tag to the initial phase.
+ workInProgress.tag = CallComponent;
+ // Intentionally fall through since this is now the same.
case CallComponent:
- return moveCallToHandlerPhase(
+ return updateCallComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case ReturnComponent:
+ // A return component is just a placeholder, we can just run through the
+ // next one immediately.
+ return null;
+ case HostPortal:
+ return updatePortalComponent(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case Fragment:
+ return updateFragment(current, workInProgress);
+ case Mode:
+ return updateMode(current, workInProgress);
+ case ContextProvider:
+ return updateContextProvider(
current,
workInProgress,
renderExpirationTime
);
- case CallHandlerPhase:
- // Reset the tag to now be a first phase call.
- workInProgress.tag = CallComponent;
- return null;
- case ReturnComponent:
- // Does nothing.
- return null;
- case Fragment:
- return null;
- case HostPortal:
- popHostContainer(workInProgress);
- updateHostContainer(workInProgress);
- return null;
- // Error cases
- case IndeterminateComponent:
- invariant(
- false,
- "An indeterminate component should have become determinate before " +
- "completing. This error is likely caused by a bug in React. Please " +
- "file an issue."
+ case ContextConsumer:
+ return updateContextConsumer(
+ current,
+ workInProgress,
+ renderExpirationTime
);
- // eslint-disable-next-line no-fallthrough
default:
invariant(
false,
@@ -8915,212 +9428,191 @@ var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) {
}
}
- return {
- completeWork: completeWork
- };
-};
-
-var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback;
-var hasCaughtError$1 = ReactErrorUtils.hasCaughtError;
-var clearCaughtError$1 = ReactErrorUtils.clearCaughtError;
-
-var ReactFiberCommitWork = function(config, captureError) {
- var getPublicInstance = config.getPublicInstance,
- mutation = config.mutation,
- persistence = config.persistence;
+ function beginFailedWork(current, workInProgress, renderExpirationTime) {
+ // Push context providers here to avoid a push/pop context mismatch.
+ switch (workInProgress.tag) {
+ case ClassComponent:
+ pushContextProvider(workInProgress);
+ break;
+ case HostRoot:
+ pushHostRootContext(workInProgress);
+ break;
+ default:
+ invariant(
+ false,
+ "Invalid type of work. This error is likely caused by a bug in React. " +
+ "Please file an issue."
+ );
+ }
- var callComponentWillUnmountWithTimer = function(current, instance) {
- startPhaseTimer(current, "componentWillUnmount");
- instance.props = current.memoizedProps;
- instance.state = current.memoizedState;
- instance.componentWillUnmount();
- stopPhaseTimer();
- };
+ // Add an error effect so we can handle the error during the commit phase
+ workInProgress.effectTag |= Err;
- // Capture errors so they don't interrupt unmounting.
- function safelyCallComponentWillUnmount(current, instance) {
- {
- invokeGuardedCallback$2(
- null,
- callComponentWillUnmountWithTimer,
- null,
- current,
- instance
- );
- if (hasCaughtError$1()) {
- var unmountError = clearCaughtError$1();
- captureError(current, unmountError);
- }
+ // This is a weird case where we do "resume" work — work that failed on
+ // our first attempt. Because we no longer have a notion of "progressed
+ // deletions," reset the child to the current child to make sure we delete
+ // it again. TODO: Find a better way to handle this, perhaps during a more
+ // general overhaul of error handling.
+ if (current === null) {
+ workInProgress.child = null;
+ } else if (workInProgress.child !== current.child) {
+ workInProgress.child = current.child;
}
- }
- function safelyDetachRef(current) {
- var ref = current.ref;
- if (ref !== null) {
- {
- invokeGuardedCallback$2(null, ref, null, null);
- if (hasCaughtError$1()) {
- var refError = clearCaughtError$1();
- captureError(current, refError);
- }
- }
+ if (
+ workInProgress.expirationTime === NoWork ||
+ workInProgress.expirationTime > renderExpirationTime
+ ) {
+ return bailoutOnLowPriority(current, workInProgress);
}
- }
- function commitLifeCycles(current, finishedWork) {
- switch (finishedWork.tag) {
- case ClassComponent: {
- var instance = finishedWork.stateNode;
- if (finishedWork.effectTag & Update) {
- if (current === null) {
- startPhaseTimer(finishedWork, "componentDidMount");
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidMount();
- stopPhaseTimer();
- } else {
- var prevProps = current.memoizedProps;
- var prevState = current.memoizedState;
- startPhaseTimer(finishedWork, "componentDidUpdate");
- instance.props = finishedWork.memoizedProps;
- instance.state = finishedWork.memoizedState;
- instance.componentDidUpdate(prevProps, prevState);
- stopPhaseTimer();
- }
- }
- var updateQueue = finishedWork.updateQueue;
- if (updateQueue !== null) {
- commitCallbacks(updateQueue, instance);
- }
- return;
- }
- case HostRoot: {
- var _updateQueue = finishedWork.updateQueue;
- if (_updateQueue !== null) {
- var _instance =
- finishedWork.child !== null ? finishedWork.child.stateNode : null;
- commitCallbacks(_updateQueue, _instance);
- }
- return;
- }
- case HostComponent: {
- var _instance2 = finishedWork.stateNode;
+ // If we don't bail out, we're going be recomputing our children so we need
+ // to drop our effect list.
+ workInProgress.firstEffect = null;
+ workInProgress.lastEffect = null;
- // Renderers may schedule work to be done after host components are mounted
- // (eg DOM renderer may schedule auto-focus for inputs and form controls).
- // These effects should only be committed when components are first mounted,
- // aka when there is no current/alternate.
- if (current === null && finishedWork.effectTag & Update) {
- var type = finishedWork.type;
- var props = finishedWork.memoizedProps;
- commitMount(_instance2, type, props, finishedWork);
- }
+ // Unmount the current children as if the component rendered null
+ var nextChildren = null;
+ reconcileChildrenAtExpirationTime(
+ current,
+ workInProgress,
+ nextChildren,
+ renderExpirationTime
+ );
- return;
- }
- case HostText: {
- // We have no life-cycles associated with text.
- return;
- }
- case HostPortal: {
- // We have no life-cycles associated with portals.
- return;
- }
- default: {
- invariant(
- false,
- "This unit of work tag should not have side-effects. This error is " +
- "likely caused by a bug in React. Please file an issue."
- );
- }
+ if (workInProgress.tag === ClassComponent) {
+ var instance = workInProgress.stateNode;
+ workInProgress.memoizedProps = instance.props;
+ workInProgress.memoizedState = instance.state;
}
- }
- function commitAttachRef(finishedWork) {
- var ref = finishedWork.ref;
- if (ref !== null) {
- var instance = finishedWork.stateNode;
- switch (finishedWork.tag) {
- case HostComponent:
- ref(getPublicInstance(instance));
- break;
- default:
- ref(instance);
- }
- }
+ return workInProgress.child;
}
- function commitDetachRef(current) {
- var currentRef = current.ref;
- if (currentRef !== null) {
- currentRef(null);
- }
+ return {
+ beginWork: beginWork,
+ beginFailedWork: beginFailedWork
+ };
+};
+
+var ReactFiberCompleteWork = function(config, hostContext, hydrationContext) {
+ var createInstance = config.createInstance,
+ createTextInstance = config.createTextInstance,
+ appendInitialChild = config.appendInitialChild,
+ finalizeInitialChildren = config.finalizeInitialChildren,
+ prepareUpdate = config.prepareUpdate,
+ mutation = config.mutation,
+ persistence = config.persistence;
+ var getRootHostContainer = hostContext.getRootHostContainer,
+ popHostContext = hostContext.popHostContext,
+ getHostContext = hostContext.getHostContext,
+ popHostContainer = hostContext.popHostContainer;
+ var prepareToHydrateHostInstance =
+ hydrationContext.prepareToHydrateHostInstance,
+ prepareToHydrateHostTextInstance =
+ hydrationContext.prepareToHydrateHostTextInstance,
+ popHydrationState = hydrationContext.popHydrationState;
+
+ function markUpdate(workInProgress) {
+ // Tag the fiber with an update effect. This turns a Placement into
+ // an UpdateAndPlacement.
+ workInProgress.effectTag |= Update;
}
- // User-originating errors (lifecycles and refs) should not interrupt
- // deletion, so don't let them throw. Host-originating errors should
- // interrupt deletion, so it's okay
- function commitUnmount(current) {
- if (typeof onCommitUnmount === "function") {
- onCommitUnmount(current);
- }
+ function markRef(workInProgress) {
+ workInProgress.effectTag |= Ref;
+ }
- switch (current.tag) {
- case ClassComponent: {
- safelyDetachRef(current);
- var instance = current.stateNode;
- if (typeof instance.componentWillUnmount === "function") {
- safelyCallComponentWillUnmount(current, instance);
- }
- return;
- }
- case HostComponent: {
- safelyDetachRef(current);
- return;
- }
- case CallComponent: {
- commitNestedUnmounts(current.stateNode);
- return;
+ function appendAllReturns(returns, workInProgress) {
+ var node = workInProgress.stateNode;
+ if (node) {
+ node["return"] = workInProgress;
+ }
+ while (node !== null) {
+ if (
+ node.tag === HostComponent ||
+ node.tag === HostText ||
+ node.tag === HostPortal
+ ) {
+ invariant(false, "A call cannot have host component children.");
+ } else if (node.tag === ReturnComponent) {
+ returns.push(node.pendingProps.value);
+ } else if (node.child !== null) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
}
- case HostPortal: {
- // TODO: this is recursive.
- // We are also not using this parent because
- // the portal will get pushed immediately.
- if (enableMutatingReconciler && mutation) {
- unmountHostComponents(current);
- } else if (enablePersistentReconciler && persistence) {
- emptyPortalContainer(current);
+ while (node.sibling === null) {
+ if (node["return"] === null || node["return"] === workInProgress) {
+ return;
}
- return;
+ node = node["return"];
}
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
}
}
- function commitNestedUnmounts(root) {
- // While we're inside a removed host node we don't want to call
- // removeChild on the inner nodes because they're removed by the top
- // call anyway. We also want to call componentWillUnmount on all
- // composites before this host node is removed from the tree. Therefore
- var node = root;
- while (true) {
- commitUnmount(node);
- // Visit children because they may contain more composite or host nodes.
- // Skip portals because commitUnmount() currently visits them recursively.
- if (
- node.child !== null &&
- // If we use mutation we drill down into portals using commitUnmount above.
- // If we don't use mutation we drill down into portals here instead.
- (!mutation || node.tag !== HostPortal)
- ) {
+ function moveCallToHandlerPhase(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ) {
+ var props = workInProgress.memoizedProps;
+ invariant(
+ props,
+ "Should be resolved by now. This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
+
+ // First step of the call has completed. Now we need to do the second.
+ // TODO: It would be nice to have a multi stage call represented by a
+ // single component, or at least tail call optimize nested ones. Currently
+ // that requires additional fields that we don't want to add to the fiber.
+ // So this requires nested handlers.
+ // Note: This doesn't mutate the alternate node. I don't think it needs to
+ // since this stage is reset for every pass.
+ workInProgress.tag = CallHandlerPhase;
+
+ // Build up the returns.
+ // TODO: Compare this to a generator or opaque helpers like Children.
+ var returns = [];
+ appendAllReturns(returns, workInProgress);
+ var fn = props.handler;
+ var childProps = props.props;
+ var nextChildren = fn(childProps, returns);
+
+ var currentFirstChild = current !== null ? current.child : null;
+ workInProgress.child = reconcileChildFibers(
+ workInProgress,
+ currentFirstChild,
+ nextChildren,
+ renderExpirationTime
+ );
+ return workInProgress.child;
+ }
+
+ function appendAllChildren(parent, workInProgress) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendInitialChild(parent, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
node.child["return"] = node;
node = node.child;
continue;
}
- if (node === root) {
+ if (node === workInProgress) {
return;
}
while (node.sibling === null) {
- if (node["return"] === null || node["return"] === root) {
+ if (node["return"] === null || node["return"] === workInProgress) {
return;
}
node = node["return"];
@@ -9130,379 +9622,532 @@ var ReactFiberCommitWork = function(config, captureError) {
}
}
- function detachFiber(current) {
- // Cut off the return pointers to disconnect it from the tree. Ideally, we
- // should clear the child pointer of the parent alternate to let this
- // get GC:ed but we don't know which for sure which parent is the current
- // one so we'll settle for GC:ing the subtree of this child. This child
- // itself will be GC:ed when the parent updates the next time.
- current["return"] = null;
- current.child = null;
- if (current.alternate) {
- current.alternate.child = null;
- current.alternate["return"] = null;
+ var updateHostContainer = void 0;
+ var updateHostComponent = void 0;
+ var updateHostText = void 0;
+ if (mutation) {
+ if (enableMutatingReconciler) {
+ // Mutation mode
+ updateHostContainer = function(workInProgress) {
+ // Noop
+ };
+ updateHostComponent = function(
+ current,
+ workInProgress,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ ) {
+ // TODO: Type this specific to this type of component.
+ workInProgress.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update. All the work is done in commitWork.
+ if (updatePayload) {
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostText = function(current, workInProgress, oldText, newText) {
+ // If the text differs, mark it as an update. All the work in done in commitWork.
+ if (oldText !== newText) {
+ markUpdate(workInProgress);
+ }
+ };
+ } else {
+ invariant(false, "Mutating reconciler is disabled.");
}
- }
-
- if (!mutation) {
- var commitContainer = void 0;
- if (persistence) {
- var replaceContainerChildren = persistence.replaceContainerChildren,
- createContainerChildSet = persistence.createContainerChildSet;
+ } else if (persistence) {
+ if (enablePersistentReconciler) {
+ // Persistent host tree mode
+ var cloneInstance = persistence.cloneInstance,
+ createContainerChildSet = persistence.createContainerChildSet,
+ appendChildToContainerChildSet =
+ persistence.appendChildToContainerChildSet,
+ finalizeContainerChildren = persistence.finalizeContainerChildren;
- var emptyPortalContainer = function(current) {
- var portal = current.stateNode;
- var containerInfo = portal.containerInfo;
+ // An unfortunate fork of appendAllChildren because we have two different parent types.
- var emptyChildSet = createContainerChildSet(containerInfo);
- replaceContainerChildren(containerInfo, emptyChildSet);
- };
- commitContainer = function(finishedWork) {
- switch (finishedWork.tag) {
- case ClassComponent: {
- return;
+ var appendAllChildrenToContainer = function(
+ containerChildSet,
+ workInProgress
+ ) {
+ // We only have the top Fiber that was created but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = workInProgress.child;
+ while (node !== null) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ appendChildToContainerChildSet(containerChildSet, node.stateNode);
+ } else if (node.tag === HostPortal) {
+ // If we have a portal child, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
}
- case HostComponent: {
+ if (node === workInProgress) {
return;
}
- case HostText: {
- return;
+ while (node.sibling === null) {
+ if (node["return"] === null || node["return"] === workInProgress) {
+ return;
+ }
+ node = node["return"];
}
- case HostRoot:
- case HostPortal: {
- var portalOrRoot = finishedWork.stateNode;
- var containerInfo = portalOrRoot.containerInfo,
- _pendingChildren = portalOrRoot.pendingChildren;
-
- replaceContainerChildren(containerInfo, _pendingChildren);
- return;
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
+ }
+ };
+ updateHostContainer = function(workInProgress) {
+ var portalOrRoot = workInProgress.stateNode;
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ if (childrenUnchanged) {
+ // No changes, just reuse the existing instance.
+ } else {
+ var container = portalOrRoot.containerInfo;
+ var newChildSet = createContainerChildSet(container);
+ if (finalizeContainerChildren(container, newChildSet)) {
+ markUpdate(workInProgress);
+ }
+ portalOrRoot.pendingChildren = newChildSet;
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildrenToContainer(newChildSet, workInProgress);
+ // Schedule an update on the container to swap out the container.
+ markUpdate(workInProgress);
+ }
+ };
+ updateHostComponent = function(
+ current,
+ workInProgress,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ ) {
+ // If there are no effects associated with this node, then none of our children had any updates.
+ // This guarantees that we can reuse all of them.
+ var childrenUnchanged = workInProgress.firstEffect === null;
+ var currentInstance = current.stateNode;
+ if (childrenUnchanged && updatePayload === null) {
+ // No changes, just reuse the existing instance.
+ // Note that this might release a previous clone.
+ workInProgress.stateNode = currentInstance;
+ } else {
+ var recyclableInstance = workInProgress.stateNode;
+ var newInstance = cloneInstance(
+ currentInstance,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ workInProgress,
+ childrenUnchanged,
+ recyclableInstance
+ );
+ if (
+ finalizeInitialChildren(
+ newInstance,
+ type,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ )
+ ) {
+ markUpdate(workInProgress);
}
- default: {
- invariant(
- false,
- "This unit of work tag should not have side-effects. This error is " +
- "likely caused by a bug in React. Please file an issue."
- );
+ workInProgress.stateNode = newInstance;
+ if (childrenUnchanged) {
+ // If there are no other effects in this tree, we need to flag this node as having one.
+ // Even though we're not going to use it for anything.
+ // Otherwise parents won't know that there are new children to propagate upwards.
+ markUpdate(workInProgress);
+ } else {
+ // If children might have changed, we have to add them all to the set.
+ appendAllChildren(newInstance, workInProgress);
}
}
};
+ updateHostText = function(current, workInProgress, oldText, newText) {
+ if (oldText !== newText) {
+ // If the text content differs, we'll create a new text instance for it.
+ var rootContainerInstance = getRootHostContainer();
+ var currentHostContext = getHostContext();
+ workInProgress.stateNode = createTextInstance(
+ newText,
+ rootContainerInstance,
+ currentHostContext,
+ workInProgress
+ );
+ // We'll have to mark it as having an effect, even though we won't use the effect for anything.
+ // This lets the parents know that at least one of their children has changed.
+ markUpdate(workInProgress);
+ }
+ };
} else {
- commitContainer = function(finishedWork) {
+ invariant(false, "Persistent reconciler is disabled.");
+ }
+ } else {
+ if (enableNoopReconciler) {
+ // No host operations
+ updateHostContainer = function(workInProgress) {
// Noop
};
- }
- if (enablePersistentReconciler || enableNoopReconciler) {
- return {
- commitResetTextContent: function(finishedWork) {},
- commitPlacement: function(finishedWork) {},
- commitDeletion: function(current) {
- // Detach refs and call componentWillUnmount() on the whole subtree.
- commitNestedUnmounts(current);
- detachFiber(current);
- },
- commitWork: function(current, finishedWork) {
- commitContainer(finishedWork);
- },
-
- commitLifeCycles: commitLifeCycles,
- commitAttachRef: commitAttachRef,
- commitDetachRef: commitDetachRef
+ updateHostComponent = function(
+ current,
+ workInProgress,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ ) {
+ // Noop
+ };
+ updateHostText = function(current, workInProgress, oldText, newText) {
+ // Noop
};
- } else if (persistence) {
- invariant(false, "Persistent reconciler is disabled.");
} else {
invariant(false, "Noop reconciler is disabled.");
}
}
- var commitMount = mutation.commitMount,
- commitUpdate = mutation.commitUpdate,
- resetTextContent = mutation.resetTextContent,
- commitTextUpdate = mutation.commitTextUpdate,
- appendChild = mutation.appendChild,
- appendChildToContainer = mutation.appendChildToContainer,
- insertBefore = mutation.insertBefore,
- insertInContainerBefore = mutation.insertInContainerBefore,
- removeChild = mutation.removeChild,
- removeChildFromContainer = mutation.removeChildFromContainer;
- function getHostParentFiber(fiber) {
- var parent = fiber["return"];
- while (parent !== null) {
- if (isHostParent(parent)) {
- return parent;
+ function completeWork(current, workInProgress, renderExpirationTime) {
+ var newProps = workInProgress.pendingProps;
+ switch (workInProgress.tag) {
+ case FunctionalComponent:
+ return null;
+ case ClassComponent: {
+ // We are leaving this subtree, so pop context if any.
+ popContextProvider(workInProgress);
+ return null;
}
- parent = parent["return"];
- }
- invariant(
- false,
- "Expected to find a host parent. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- }
-
- function isHostParent(fiber) {
- return (
- fiber.tag === HostComponent ||
- fiber.tag === HostRoot ||
- fiber.tag === HostPortal
- );
- }
+ case HostRoot: {
+ popHostContainer(workInProgress);
+ popTopLevelContextObject(workInProgress);
+ var fiberRoot = workInProgress.stateNode;
+ if (fiberRoot.pendingContext) {
+ fiberRoot.context = fiberRoot.pendingContext;
+ fiberRoot.pendingContext = null;
+ }
- function getHostSibling(fiber) {
- // We're going to search forward into the tree until we find a sibling host
- // node. Unfortunately, if multiple insertions are done in a row we have to
- // search past them. This leads to exponential search for the next sibling.
- var node = fiber;
- siblings: while (true) {
- // If we didn't find anything, let's try the next sibling.
- while (node.sibling === null) {
- if (node["return"] === null || isHostParent(node["return"])) {
- // If we pop out of the root or hit the parent the fiber we are the
- // last sibling.
- return null;
+ if (current === null || current.child === null) {
+ // If we hydrated, pop so that we can delete any remaining children
+ // that weren't hydrated.
+ popHydrationState(workInProgress);
+ // This resets the hacky state to fix isMounted before committing.
+ // TODO: Delete this when we delete isMounted and findDOMNode.
+ workInProgress.effectTag &= ~Placement;
}
- node = node["return"];
+ updateHostContainer(workInProgress);
+ return null;
}
- node.sibling["return"] = node["return"];
- node = node.sibling;
- while (node.tag !== HostComponent && node.tag !== HostText) {
- // If it is not host node and, we might have a host node inside it.
- // Try to search down until we find one.
- if (node.effectTag & Placement) {
- // If we don't have a child, try the siblings instead.
- continue siblings;
+ case HostComponent: {
+ popHostContext(workInProgress);
+ var rootContainerInstance = getRootHostContainer();
+ var type = workInProgress.type;
+ if (current !== null && workInProgress.stateNode != null) {
+ // If we have an alternate, that means this is an update and we need to
+ // schedule a side-effect to do the updates.
+ var oldProps = current.memoizedProps;
+ // If we get updated because one of our children updated, we don't
+ // have newProps so we'll have to reuse them.
+ // TODO: Split the update API as separate for the props vs. children.
+ // Even better would be if children weren't special cased at all tho.
+ var instance = workInProgress.stateNode;
+ var currentHostContext = getHostContext();
+ var updatePayload = prepareUpdate(
+ instance,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ );
+
+ updateHostComponent(
+ current,
+ workInProgress,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ currentHostContext
+ );
+
+ if (current.ref !== workInProgress.ref) {
+ markRef(workInProgress);
+ }
+ } else {
+ if (!newProps) {
+ invariant(
+ workInProgress.stateNode !== null,
+ "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 null;
+ }
+
+ var _currentHostContext = getHostContext();
+ // TODO: Move createInstance to beginWork and keep it on a context
+ // "stack" as the parent. Then append children as we go in beginWork
+ // or completeWork depending on we want to add then top->down or
+ // bottom->up. Top->down is faster in IE11.
+ var wasHydrated = popHydrationState(workInProgress);
+ if (wasHydrated) {
+ // TODO: Move this and createInstance step into the beginPhase
+ // to consolidate.
+ if (
+ prepareToHydrateHostInstance(
+ workInProgress,
+ rootContainerInstance,
+ _currentHostContext
+ )
+ ) {
+ // If changes to the hydrated node needs to be applied at the
+ // commit-phase we mark this as such.
+ markUpdate(workInProgress);
+ }
+ } else {
+ var _instance = createInstance(
+ type,
+ newProps,
+ rootContainerInstance,
+ _currentHostContext,
+ workInProgress
+ );
+
+ appendAllChildren(_instance, workInProgress);
+
+ // Certain renderers require commit-time effects for initial mount.
+ // (eg DOM renderer supports auto-focus for certain elements).
+ // Make sure such renderers get scheduled for later work.
+ if (
+ finalizeInitialChildren(
+ _instance,
+ type,
+ newProps,
+ rootContainerInstance,
+ _currentHostContext
+ )
+ ) {
+ markUpdate(workInProgress);
+ }
+ workInProgress.stateNode = _instance;
+ }
+
+ if (workInProgress.ref !== null) {
+ // If there is a ref on a host node we need to schedule a callback
+ markRef(workInProgress);
+ }
}
- // If we don't have a child, try the siblings instead.
- // We also skip portals because they are not part of this host tree.
- if (node.child === null || node.tag === HostPortal) {
- continue siblings;
+ return null;
+ }
+ case HostText: {
+ var newText = newProps;
+ if (current && workInProgress.stateNode != null) {
+ var oldText = current.memoizedProps;
+ // If we have an alternate, that means this is an update and we need
+ // to schedule a side-effect to do the updates.
+ updateHostText(current, workInProgress, oldText, newText);
} else {
- node.child["return"] = node;
- node = node.child;
+ if (typeof newText !== "string") {
+ invariant(
+ workInProgress.stateNode !== null,
+ "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 null;
+ }
+ var _rootContainerInstance = getRootHostContainer();
+ var _currentHostContext2 = getHostContext();
+ var _wasHydrated = popHydrationState(workInProgress);
+ if (_wasHydrated) {
+ if (prepareToHydrateHostTextInstance(workInProgress)) {
+ markUpdate(workInProgress);
+ }
+ } else {
+ workInProgress.stateNode = createTextInstance(
+ newText,
+ _rootContainerInstance,
+ _currentHostContext2,
+ workInProgress
+ );
+ }
}
+ return null;
}
- // Check if this host node is stable or about to be placed.
- if (!(node.effectTag & Placement)) {
- // Found it!
- return node.stateNode;
- }
- }
- }
-
- function commitPlacement(finishedWork) {
- // Recursively insert all host nodes into the parent.
- var parentFiber = getHostParentFiber(finishedWork);
- var parent = void 0;
- var isContainer = void 0;
- switch (parentFiber.tag) {
- case HostComponent:
- parent = parentFiber.stateNode;
- isContainer = false;
- break;
- case HostRoot:
- parent = parentFiber.stateNode.containerInfo;
- isContainer = true;
- break;
+ case CallComponent:
+ return moveCallToHandlerPhase(
+ current,
+ workInProgress,
+ renderExpirationTime
+ );
+ case CallHandlerPhase:
+ // Reset the tag to now be a first phase call.
+ workInProgress.tag = CallComponent;
+ return null;
+ case ReturnComponent:
+ // Does nothing.
+ return null;
+ case Fragment:
+ return null;
+ case Mode:
+ return null;
case HostPortal:
- parent = parentFiber.stateNode.containerInfo;
- isContainer = true;
- break;
+ popHostContainer(workInProgress);
+ updateHostContainer(workInProgress);
+ return null;
+ case ContextProvider:
+ // Pop provider fiber
+ popProvider(workInProgress);
+ return null;
+ case ContextConsumer:
+ return null;
+ // Error cases
+ case IndeterminateComponent:
+ invariant(
+ false,
+ "An indeterminate component should have become determinate before " +
+ "completing. This error is likely caused by a bug in React. Please " +
+ "file an issue."
+ );
+ // eslint-disable-next-line no-fallthrough
default:
invariant(
false,
- "Invalid host parent fiber. This error is likely caused by a bug " +
- "in React. Please file an issue."
+ "Unknown unit of work tag. This error is likely caused by a bug in " +
+ "React. Please file an issue."
);
}
- if (parentFiber.effectTag & ContentReset) {
- // Reset the text content of the parent before doing any insertions
- resetTextContent(parent);
- // Clear ContentReset from the effect tag
- parentFiber.effectTag &= ~ContentReset;
- }
-
- var before = getHostSibling(finishedWork);
- // We only have the top Fiber that was inserted but we need recurse down its
- // children to find all the terminal nodes.
- var node = finishedWork;
- while (true) {
- if (node.tag === HostComponent || node.tag === HostText) {
- if (before) {
- if (isContainer) {
- insertInContainerBefore(parent, node.stateNode, before);
- } else {
- insertBefore(parent, node.stateNode, before);
- }
- } else {
- if (isContainer) {
- appendChildToContainer(parent, node.stateNode);
- } else {
- appendChild(parent, node.stateNode);
- }
- }
- } else if (node.tag === HostPortal) {
- // If the insertion itself is a portal, then we don't want to traverse
- // down its children. Instead, we'll get insertions from each child in
- // the portal directly.
- } else if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- if (node === finishedWork) {
- return;
- }
- while (node.sibling === null) {
- if (node["return"] === null || node["return"] === finishedWork) {
- return;
- }
- node = node["return"];
- }
- node.sibling["return"] = node["return"];
- node = node.sibling;
- }
}
- function unmountHostComponents(current) {
- // We only have the top Fiber that was inserted but we need recurse down its
- var node = current;
+ return {
+ completeWork: completeWork
+ };
+};
- // Each iteration, currentParent is populated with node's host parent if not
- // currentParentIsValid.
- var currentParentIsValid = false;
- var currentParent = void 0;
- var currentParentIsContainer = void 0;
+var invokeGuardedCallback$3 = ReactErrorUtils.invokeGuardedCallback;
+var hasCaughtError$1 = ReactErrorUtils.hasCaughtError;
+var clearCaughtError$1 = ReactErrorUtils.clearCaughtError;
- while (true) {
- if (!currentParentIsValid) {
- var parent = node["return"];
- findParent: while (true) {
- invariant(
- parent !== null,
- "Expected to find a host parent. This error is likely caused by " +
- "a bug in React. Please file an issue."
- );
- switch (parent.tag) {
- case HostComponent:
- currentParent = parent.stateNode;
- currentParentIsContainer = false;
- break findParent;
- case HostRoot:
- currentParent = parent.stateNode.containerInfo;
- currentParentIsContainer = true;
- break findParent;
- case HostPortal:
- currentParent = parent.stateNode.containerInfo;
- currentParentIsContainer = true;
- break findParent;
- }
- parent = parent["return"];
- }
- currentParentIsValid = true;
- }
+var ReactFiberCommitWork = function(config, captureError) {
+ var getPublicInstance = config.getPublicInstance,
+ mutation = config.mutation,
+ persistence = config.persistence;
- if (node.tag === HostComponent || node.tag === HostText) {
- commitNestedUnmounts(node);
- // After all the children have unmounted, it is now safe to remove the
- // node from the tree.
- if (currentParentIsContainer) {
- removeChildFromContainer(currentParent, node.stateNode);
- } else {
- removeChild(currentParent, node.stateNode);
- }
- // Don't visit children because we already visited them.
- } else if (node.tag === HostPortal) {
- // When we go into a portal, it becomes the parent to remove from.
- // We will reassign it back when we pop the portal on the way up.
- currentParent = node.stateNode.containerInfo;
- // Visit children because portals might contain host components.
- if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- } else {
- commitUnmount(node);
- // Visit children because we may find more host components below.
- if (node.child !== null) {
- node.child["return"] = node;
- node = node.child;
- continue;
- }
- }
- if (node === current) {
- return;
+ var callComponentWillUnmountWithTimer = function(current, instance) {
+ startPhaseTimer(current, "componentWillUnmount");
+ instance.props = current.memoizedProps;
+ instance.state = current.memoizedState;
+ instance.componentWillUnmount();
+ stopPhaseTimer();
+ };
+
+ // Capture errors so they don't interrupt unmounting.
+ function safelyCallComponentWillUnmount(current, instance) {
+ {
+ invokeGuardedCallback$3(
+ null,
+ callComponentWillUnmountWithTimer,
+ null,
+ current,
+ instance
+ );
+ if (hasCaughtError$1()) {
+ var unmountError = clearCaughtError$1();
+ captureError(current, unmountError);
}
- while (node.sibling === null) {
- if (node["return"] === null || node["return"] === current) {
- return;
- }
- node = node["return"];
- if (node.tag === HostPortal) {
- // When we go out of the portal, we need to restore the parent.
- // Since we don't keep a stack of them, we will search for it.
- currentParentIsValid = false;
+ }
+ }
+
+ function safelyDetachRef(current) {
+ var ref = current.ref;
+ if (ref !== null) {
+ {
+ invokeGuardedCallback$3(null, ref, null, null);
+ if (hasCaughtError$1()) {
+ var refError = clearCaughtError$1();
+ captureError(current, refError);
}
- }
- node.sibling["return"] = node["return"];
- node = node.sibling;
+ }
}
}
- function commitDeletion(current) {
- // Recursively delete all host nodes from the parent.
- // Detach refs and call componentWillUnmount() on the whole subtree.
- unmountHostComponents(current);
- detachFiber(current);
- }
-
- function commitWork(current, finishedWork) {
+ function commitLifeCycles(current, finishedWork) {
switch (finishedWork.tag) {
case ClassComponent: {
+ var instance = finishedWork.stateNode;
+ if (finishedWork.effectTag & Update) {
+ if (current === null) {
+ startPhaseTimer(finishedWork, "componentDidMount");
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ instance.componentDidMount();
+ stopPhaseTimer();
+ } else {
+ var prevProps = current.memoizedProps;
+ var prevState = current.memoizedState;
+ startPhaseTimer(finishedWork, "componentDidUpdate");
+ instance.props = finishedWork.memoizedProps;
+ instance.state = finishedWork.memoizedState;
+ instance.componentDidUpdate(prevProps, prevState);
+ stopPhaseTimer();
+ }
+ }
+ var updateQueue = finishedWork.updateQueue;
+ if (updateQueue !== null) {
+ commitCallbacks(updateQueue, instance);
+ }
+ return;
+ }
+ case HostRoot: {
+ var _updateQueue = finishedWork.updateQueue;
+ if (_updateQueue !== null) {
+ var _instance = null;
+ if (finishedWork.child !== null) {
+ switch (finishedWork.child.tag) {
+ case HostComponent:
+ _instance = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case ClassComponent:
+ _instance = finishedWork.child.stateNode;
+ break;
+ }
+ }
+ commitCallbacks(_updateQueue, _instance);
+ }
return;
}
case HostComponent: {
- var instance = finishedWork.stateNode;
- if (instance != null) {
- // Commit the work prepared earlier.
- var newProps = finishedWork.memoizedProps;
- // For hydration we reuse the update path but we treat the oldProps
- // as the newProps. The updatePayload will contain the real change in
- // this case.
- var oldProps = current !== null ? current.memoizedProps : newProps;
+ var _instance2 = finishedWork.stateNode;
+
+ // Renderers may schedule work to be done after host components are mounted
+ // (eg DOM renderer may schedule auto-focus for inputs and form controls).
+ // These effects should only be committed when components are first mounted,
+ // aka when there is no current/alternate.
+ if (current === null && finishedWork.effectTag & Update) {
var type = finishedWork.type;
- // TODO: Type the updateQueue to be specific to host components.
- var updatePayload = finishedWork.updateQueue;
- finishedWork.updateQueue = null;
- if (updatePayload !== null) {
- commitUpdate(
- instance,
- updatePayload,
- type,
- oldProps,
- newProps,
- finishedWork
- );
- }
+ var props = finishedWork.memoizedProps;
+ commitMount(_instance2, type, props, finishedWork);
}
+
return;
}
case HostText: {
- invariant(
- finishedWork.stateNode !== null,
- "This should have a text node initialized. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
- var textInstance = finishedWork.stateNode;
- var newText = finishedWork.memoizedProps;
- // For hydration we reuse the update path but we treat the oldProps
- // as the newProps. The updatePayload will contain the real change in
- // this case.
- var oldText = current !== null ? current.memoizedProps : newText;
- commitTextUpdate(textInstance, oldText, newText);
+ // We have no life-cycles associated with text.
return;
}
- case HostRoot: {
+ case HostPortal: {
+ // We have no life-cycles associated with portals.
return;
}
default: {
@@ -9515,3240 +10160,3360 @@ var ReactFiberCommitWork = function(config, captureError) {
}
}
- function commitResetTextContent(current) {
- resetTextContent(current.stateNode);
- }
-
- if (enableMutatingReconciler) {
- return {
- commitResetTextContent: commitResetTextContent,
- commitPlacement: commitPlacement,
- commitDeletion: commitDeletion,
- commitWork: commitWork,
- commitLifeCycles: commitLifeCycles,
- commitAttachRef: commitAttachRef,
- commitDetachRef: commitDetachRef
- };
- } else {
- invariant(false, "Mutating reconciler is disabled.");
- }
-};
-
-var NO_CONTEXT = {};
-
-var ReactFiberHostContext = function(config) {
- var getChildHostContext = config.getChildHostContext,
- getRootHostContext = config.getRootHostContext;
-
- var contextStackCursor = createCursor(NO_CONTEXT);
- var contextFiberStackCursor = createCursor(NO_CONTEXT);
- var rootInstanceStackCursor = createCursor(NO_CONTEXT);
-
- function requiredContext(c) {
- invariant(
- c !== NO_CONTEXT,
- "Expected host context to exist. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- return c;
- }
-
- function getRootHostContainer() {
- var rootInstance = requiredContext(rootInstanceStackCursor.current);
- return rootInstance;
- }
-
- function pushHostContainer(fiber, nextRootInstance) {
- // Push current root instance onto the stack;
- // This allows us to reset root when portals are popped.
- push(rootInstanceStackCursor, nextRootInstance, fiber);
-
- var nextRootContext = getRootHostContext(nextRootInstance);
-
- // Track the context and the Fiber that provided it.
- // This enables us to pop only Fibers that provide unique contexts.
- push(contextFiberStackCursor, fiber, fiber);
- push(contextStackCursor, nextRootContext, fiber);
- }
-
- function popHostContainer(fiber) {
- pop(contextStackCursor, fiber);
- pop(contextFiberStackCursor, fiber);
- pop(rootInstanceStackCursor, fiber);
- }
-
- function getHostContext() {
- var context = requiredContext(contextStackCursor.current);
- return context;
+ function commitAttachRef(finishedWork) {
+ var ref = finishedWork.ref;
+ if (ref !== null) {
+ var instance = finishedWork.stateNode;
+ switch (finishedWork.tag) {
+ case HostComponent:
+ ref(getPublicInstance(instance));
+ break;
+ default:
+ ref(instance);
+ }
+ }
}
- function pushHostContext(fiber) {
- var rootInstance = requiredContext(rootInstanceStackCursor.current);
- var context = requiredContext(contextStackCursor.current);
- var nextContext = getChildHostContext(context, fiber.type, rootInstance);
-
- // Don't push this Fiber's context unless it's unique.
- if (context === nextContext) {
- return;
+ function commitDetachRef(current) {
+ var currentRef = current.ref;
+ if (currentRef !== null) {
+ currentRef(null);
}
-
- // Track the context and the Fiber that provided it.
- // This enables us to pop only Fibers that provide unique contexts.
- push(contextFiberStackCursor, fiber, fiber);
- push(contextStackCursor, nextContext, fiber);
}
- function popHostContext(fiber) {
- // Do not pop unless this Fiber provided the current context.
- // pushHostContext() only pushes Fibers that provide unique contexts.
- if (contextFiberStackCursor.current !== fiber) {
- return;
+ // User-originating errors (lifecycles and refs) should not interrupt
+ // deletion, so don't let them throw. Host-originating errors should
+ // interrupt deletion, so it's okay
+ function commitUnmount(current) {
+ if (typeof onCommitUnmount === "function") {
+ onCommitUnmount(current);
}
- pop(contextStackCursor, fiber);
- pop(contextFiberStackCursor, fiber);
+ switch (current.tag) {
+ case ClassComponent: {
+ safelyDetachRef(current);
+ var instance = current.stateNode;
+ if (typeof instance.componentWillUnmount === "function") {
+ safelyCallComponentWillUnmount(current, instance);
+ }
+ return;
+ }
+ case HostComponent: {
+ safelyDetachRef(current);
+ return;
+ }
+ case CallComponent: {
+ commitNestedUnmounts(current.stateNode);
+ return;
+ }
+ case HostPortal: {
+ // TODO: this is recursive.
+ // We are also not using this parent because
+ // the portal will get pushed immediately.
+ if (enableMutatingReconciler && mutation) {
+ unmountHostComponents(current);
+ } else if (enablePersistentReconciler && persistence) {
+ emptyPortalContainer(current);
+ }
+ return;
+ }
+ }
}
- function resetHostContainer() {
- contextStackCursor.current = NO_CONTEXT;
- rootInstanceStackCursor.current = NO_CONTEXT;
+ function commitNestedUnmounts(root) {
+ // While we're inside a removed host node we don't want to call
+ // removeChild on the inner nodes because they're removed by the top
+ // call anyway. We also want to call componentWillUnmount on all
+ // composites before this host node is removed from the tree. Therefore
+ var node = root;
+ while (true) {
+ commitUnmount(node);
+ // Visit children because they may contain more composite or host nodes.
+ // Skip portals because commitUnmount() currently visits them recursively.
+ if (
+ node.child !== null &&
+ // If we use mutation we drill down into portals using commitUnmount above.
+ // If we don't use mutation we drill down into portals here instead.
+ (!mutation || node.tag !== HostPortal)
+ ) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
+ }
+ if (node === root) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node["return"] === null || node["return"] === root) {
+ return;
+ }
+ node = node["return"];
+ }
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
+ }
}
- return {
- getHostContext: getHostContext,
- getRootHostContainer: getRootHostContainer,
- popHostContainer: popHostContainer,
- popHostContext: popHostContext,
- pushHostContainer: pushHostContainer,
- pushHostContext: pushHostContext,
- resetHostContainer: resetHostContainer
- };
-};
-
-var ReactFiberHydrationContext = function(config) {
- var shouldSetTextContent = config.shouldSetTextContent,
- hydration = config.hydration;
-
- // If this doesn't have hydration mode.
-
- if (!hydration) {
- return {
- enterHydrationState: function() {
- return false;
- },
- resetHydrationState: function() {},
- tryToClaimNextHydratableInstance: function() {},
- prepareToHydrateHostInstance: function() {
- invariant(
- false,
- "Expected prepareToHydrateHostInstance() to never be called. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- },
- prepareToHydrateHostTextInstance: function() {
- invariant(
- false,
- "Expected prepareToHydrateHostTextInstance() to never be called. " +
- "This error is likely caused by a bug in React. Please file an issue."
- );
- },
- popHydrationState: function(fiber) {
- return false;
- }
- };
+ function detachFiber(current) {
+ // Cut off the return pointers to disconnect it from the tree. Ideally, we
+ // should clear the child pointer of the parent alternate to let this
+ // get GC:ed but we don't know which for sure which parent is the current
+ // one so we'll settle for GC:ing the subtree of this child. This child
+ // itself will be GC:ed when the parent updates the next time.
+ current["return"] = null;
+ current.child = null;
+ if (current.alternate) {
+ current.alternate.child = null;
+ current.alternate["return"] = null;
+ }
}
- var canHydrateInstance = hydration.canHydrateInstance,
- canHydrateTextInstance = hydration.canHydrateTextInstance,
- getNextHydratableSibling = hydration.getNextHydratableSibling,
- getFirstHydratableChild = hydration.getFirstHydratableChild,
- hydrateInstance = hydration.hydrateInstance,
- hydrateTextInstance = hydration.hydrateTextInstance,
- didNotMatchHydratedContainerTextInstance =
- hydration.didNotMatchHydratedContainerTextInstance,
- didNotMatchHydratedTextInstance = hydration.didNotMatchHydratedTextInstance,
- didNotHydrateContainerInstance = hydration.didNotHydrateContainerInstance,
- didNotHydrateInstance = hydration.didNotHydrateInstance,
- didNotFindHydratableContainerInstance =
- hydration.didNotFindHydratableContainerInstance,
- didNotFindHydratableContainerTextInstance =
- hydration.didNotFindHydratableContainerTextInstance,
- didNotFindHydratableInstance = hydration.didNotFindHydratableInstance,
- didNotFindHydratableTextInstance =
- hydration.didNotFindHydratableTextInstance;
+ var emptyPortalContainer = void 0;
- // The deepest Fiber on the stack involved in a hydration context.
- // This may have been an insertion or a hydration.
+ if (!mutation) {
+ var commitContainer = void 0;
+ if (persistence) {
+ var replaceContainerChildren = persistence.replaceContainerChildren,
+ createContainerChildSet = persistence.createContainerChildSet;
- var hydrationParentFiber = null;
- var nextHydratableInstance = null;
- var isHydrating = false;
+ emptyPortalContainer = function(current) {
+ var portal = current.stateNode;
+ var containerInfo = portal.containerInfo;
- function enterHydrationState(fiber) {
- var parentInstance = fiber.stateNode.containerInfo;
- nextHydratableInstance = getFirstHydratableChild(parentInstance);
- hydrationParentFiber = fiber;
- isHydrating = true;
- return true;
- }
+ var emptyChildSet = createContainerChildSet(containerInfo);
+ replaceContainerChildren(containerInfo, emptyChildSet);
+ };
+ commitContainer = function(finishedWork) {
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ return;
+ }
+ case HostComponent: {
+ return;
+ }
+ case HostText: {
+ return;
+ }
+ case HostRoot:
+ case HostPortal: {
+ var portalOrRoot = finishedWork.stateNode;
+ var containerInfo = portalOrRoot.containerInfo,
+ _pendingChildren = portalOrRoot.pendingChildren;
- function deleteHydratableInstance(returnFiber, instance) {
- {
- switch (returnFiber.tag) {
- case HostRoot:
- didNotHydrateContainerInstance(
- returnFiber.stateNode.containerInfo,
- instance
- );
- break;
- case HostComponent:
- didNotHydrateInstance(
- returnFiber.type,
- returnFiber.memoizedProps,
- returnFiber.stateNode,
- instance
- );
- break;
- }
+ replaceContainerChildren(containerInfo, _pendingChildren);
+ return;
+ }
+ default: {
+ invariant(
+ false,
+ "This unit of work tag should not have side-effects. This error is " +
+ "likely caused by a bug in React. Please file an issue."
+ );
+ }
+ }
+ };
+ } else {
+ commitContainer = function(finishedWork) {
+ // Noop
+ };
}
+ if (enablePersistentReconciler || enableNoopReconciler) {
+ return {
+ commitResetTextContent: function(finishedWork) {},
+ commitPlacement: function(finishedWork) {},
+ commitDeletion: function(current) {
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ commitNestedUnmounts(current);
+ detachFiber(current);
+ },
+ commitWork: function(current, finishedWork) {
+ commitContainer(finishedWork);
+ },
- var childToDelete = createFiberFromHostInstanceForDeletion();
- childToDelete.stateNode = instance;
- childToDelete["return"] = returnFiber;
- childToDelete.effectTag = Deletion;
-
- // This might seem like it belongs on progressedFirstDeletion. However,
- // these children are not part of the reconciliation list of children.
- // Even if we abort and rereconcile the children, that will try to hydrate
- // again and the nodes are still in the host tree so these will be
- // recreated.
- if (returnFiber.lastEffect !== null) {
- returnFiber.lastEffect.nextEffect = childToDelete;
- returnFiber.lastEffect = childToDelete;
+ commitLifeCycles: commitLifeCycles,
+ commitAttachRef: commitAttachRef,
+ commitDetachRef: commitDetachRef
+ };
+ } else if (persistence) {
+ invariant(false, "Persistent reconciler is disabled.");
} else {
- returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ invariant(false, "Noop reconciler is disabled.");
}
}
+ var commitMount = mutation.commitMount,
+ commitUpdate = mutation.commitUpdate,
+ resetTextContent = mutation.resetTextContent,
+ commitTextUpdate = mutation.commitTextUpdate,
+ appendChild = mutation.appendChild,
+ appendChildToContainer = mutation.appendChildToContainer,
+ insertBefore = mutation.insertBefore,
+ insertInContainerBefore = mutation.insertInContainerBefore,
+ removeChild = mutation.removeChild,
+ removeChildFromContainer = mutation.removeChildFromContainer;
- function insertNonHydratedInstance(returnFiber, fiber) {
- fiber.effectTag |= Placement;
- {
- switch (returnFiber.tag) {
- case HostRoot: {
- var parentContainer = returnFiber.stateNode.containerInfo;
- switch (fiber.tag) {
- case HostComponent:
- var type = fiber.type;
- var props = fiber.pendingProps;
- didNotFindHydratableContainerInstance(
- parentContainer,
- type,
- props
- );
- break;
- case HostText:
- var text = fiber.pendingProps;
- didNotFindHydratableContainerTextInstance(parentContainer, text);
- break;
- }
- break;
- }
- case HostComponent: {
- var parentType = returnFiber.type;
- var parentProps = returnFiber.memoizedProps;
- var parentInstance = returnFiber.stateNode;
- switch (fiber.tag) {
- case HostComponent:
- var _type = fiber.type;
- var _props = fiber.pendingProps;
- didNotFindHydratableInstance(
- parentType,
- parentProps,
- parentInstance,
- _type,
- _props
- );
- break;
- case HostText:
- var _text = fiber.pendingProps;
- didNotFindHydratableTextInstance(
- parentType,
- parentProps,
- parentInstance,
- _text
- );
- break;
- }
- break;
- }
- default:
- return;
+ function getHostParentFiber(fiber) {
+ var parent = fiber["return"];
+ while (parent !== null) {
+ if (isHostParent(parent)) {
+ return parent;
}
+ parent = parent["return"];
}
+ invariant(
+ false,
+ "Expected to find a host parent. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
+ }
+
+ function isHostParent(fiber) {
+ return (
+ fiber.tag === HostComponent ||
+ fiber.tag === HostRoot ||
+ fiber.tag === HostPortal
+ );
}
- function tryHydrate(fiber, nextInstance) {
- switch (fiber.tag) {
- case HostComponent: {
- var type = fiber.type;
- var props = fiber.pendingProps;
- var instance = canHydrateInstance(nextInstance, type, props);
- if (instance !== null) {
- fiber.stateNode = instance;
- return true;
+ function getHostSibling(fiber) {
+ // We're going to search forward into the tree until we find a sibling host
+ // node. Unfortunately, if multiple insertions are done in a row we have to
+ // search past them. This leads to exponential search for the next sibling.
+ var node = fiber;
+ siblings: while (true) {
+ // If we didn't find anything, let's try the next sibling.
+ while (node.sibling === null) {
+ if (node["return"] === null || isHostParent(node["return"])) {
+ // If we pop out of the root or hit the parent the fiber we are the
+ // last sibling.
+ return null;
}
- return false;
+ node = node["return"];
}
- case HostText: {
- var text = fiber.pendingProps;
- var textInstance = canHydrateTextInstance(nextInstance, text);
- if (textInstance !== null) {
- fiber.stateNode = textInstance;
- return true;
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
+ while (node.tag !== HostComponent && node.tag !== HostText) {
+ // If it is not host node and, we might have a host node inside it.
+ // Try to search down until we find one.
+ if (node.effectTag & Placement) {
+ // If we don't have a child, try the siblings instead.
+ continue siblings;
+ }
+ // If we don't have a child, try the siblings instead.
+ // We also skip portals because they are not part of this host tree.
+ if (node.child === null || node.tag === HostPortal) {
+ continue siblings;
+ } else {
+ node.child["return"] = node;
+ node = node.child;
}
- return false;
}
- default:
- return false;
+ // Check if this host node is stable or about to be placed.
+ if (!(node.effectTag & Placement)) {
+ // Found it!
+ return node.stateNode;
+ }
}
}
- function tryToClaimNextHydratableInstance(fiber) {
- if (!isHydrating) {
- return;
+ function commitPlacement(finishedWork) {
+ // Recursively insert all host nodes into the parent.
+ var parentFiber = getHostParentFiber(finishedWork);
+ var parent = void 0;
+ var isContainer = void 0;
+ switch (parentFiber.tag) {
+ case HostComponent:
+ parent = parentFiber.stateNode;
+ isContainer = false;
+ break;
+ case HostRoot:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ case HostPortal:
+ parent = parentFiber.stateNode.containerInfo;
+ isContainer = true;
+ break;
+ default:
+ invariant(
+ false,
+ "Invalid host parent fiber. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
}
- var nextInstance = nextHydratableInstance;
- if (!nextInstance) {
- // Nothing to hydrate. Make it an insertion.
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
- return;
+ if (parentFiber.effectTag & ContentReset) {
+ // Reset the text content of the parent before doing any insertions
+ resetTextContent(parent);
+ // Clear ContentReset from the effect tag
+ parentFiber.effectTag &= ~ContentReset;
}
- if (!tryHydrate(fiber, nextInstance)) {
- // If we can't hydrate this instance let's try the next one.
- // We use this as a heuristic. It's based on intuition and not data so it
- // might be flawed or unnecessary.
- nextInstance = getNextHydratableSibling(nextInstance);
- if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
- // Nothing to hydrate. Make it an insertion.
- insertNonHydratedInstance(hydrationParentFiber, fiber);
- isHydrating = false;
- hydrationParentFiber = fiber;
+
+ var before = getHostSibling(finishedWork);
+ // We only have the top Fiber that was inserted but we need recurse down its
+ // children to find all the terminal nodes.
+ var node = finishedWork;
+ while (true) {
+ if (node.tag === HostComponent || node.tag === HostText) {
+ if (before) {
+ if (isContainer) {
+ insertInContainerBefore(parent, node.stateNode, before);
+ } else {
+ insertBefore(parent, node.stateNode, before);
+ }
+ } else {
+ if (isContainer) {
+ appendChildToContainer(parent, node.stateNode);
+ } else {
+ appendChild(parent, node.stateNode);
+ }
+ }
+ } else if (node.tag === HostPortal) {
+ // If the insertion itself is a portal, then we don't want to traverse
+ // down its children. Instead, we'll get insertions from each child in
+ // the portal directly.
+ } else if (node.child !== null) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
+ }
+ if (node === finishedWork) {
return;
}
- // We matched the next one, we'll now assume that the first one was
- // superfluous and we'll delete it. Since we can't eagerly delete it
- // we'll have to schedule a deletion. To do that, this node needs a dummy
- // fiber associated with it.
- deleteHydratableInstance(hydrationParentFiber, nextHydratableInstance);
+ while (node.sibling === null) {
+ if (node["return"] === null || node["return"] === finishedWork) {
+ return;
+ }
+ node = node["return"];
+ }
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
}
- hydrationParentFiber = fiber;
- nextHydratableInstance = getFirstHydratableChild(nextInstance);
}
- function prepareToHydrateHostInstance(
- fiber,
- rootContainerInstance,
- hostContext
- ) {
- var instance = fiber.stateNode;
- var updatePayload = hydrateInstance(
- instance,
- fiber.type,
- fiber.memoizedProps,
- rootContainerInstance,
- hostContext,
- fiber
- );
- // TODO: Type this specific to this type of component.
- fiber.updateQueue = updatePayload;
- // If the update payload indicates that there is a change or if there
- // is a new ref we mark this as an update.
- if (updatePayload !== null) {
- return true;
- }
- return false;
- }
+ function unmountHostComponents(current) {
+ // We only have the top Fiber that was inserted but we need recurse down its
+ var node = current;
- function prepareToHydrateHostTextInstance(fiber) {
- var textInstance = fiber.stateNode;
- var textContent = fiber.memoizedProps;
- var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
- {
- if (shouldUpdate) {
- // We assume that prepareToHydrateHostTextInstance is called in a context where the
- // hydration parent is the parent host component of this host text.
- var returnFiber = hydrationParentFiber;
- if (returnFiber !== null) {
- switch (returnFiber.tag) {
- case HostRoot: {
- var parentContainer = returnFiber.stateNode.containerInfo;
- didNotMatchHydratedContainerTextInstance(
- parentContainer,
- textInstance,
- textContent
- );
- break;
- }
- case HostComponent: {
- var parentType = returnFiber.type;
- var parentProps = returnFiber.memoizedProps;
- var parentInstance = returnFiber.stateNode;
- didNotMatchHydratedTextInstance(
- parentType,
- parentProps,
- parentInstance,
- textInstance,
- textContent
- );
- break;
- }
+ // Each iteration, currentParent is populated with node's host parent if not
+ // currentParentIsValid.
+ var currentParentIsValid = false;
+ var currentParent = void 0;
+ var currentParentIsContainer = void 0;
+
+ while (true) {
+ if (!currentParentIsValid) {
+ var parent = node["return"];
+ findParent: while (true) {
+ invariant(
+ parent !== null,
+ "Expected to find a host parent. This error is likely caused by " +
+ "a bug in React. Please file an issue."
+ );
+ switch (parent.tag) {
+ case HostComponent:
+ currentParent = parent.stateNode;
+ currentParentIsContainer = false;
+ break findParent;
+ case HostRoot:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
+ case HostPortal:
+ currentParent = parent.stateNode.containerInfo;
+ currentParentIsContainer = true;
+ break findParent;
}
+ parent = parent["return"];
+ }
+ currentParentIsValid = true;
+ }
+
+ if (node.tag === HostComponent || node.tag === HostText) {
+ commitNestedUnmounts(node);
+ // After all the children have unmounted, it is now safe to remove the
+ // node from the tree.
+ if (currentParentIsContainer) {
+ removeChildFromContainer(currentParent, node.stateNode);
+ } else {
+ removeChild(currentParent, node.stateNode);
+ }
+ // Don't visit children because we already visited them.
+ } else if (node.tag === HostPortal) {
+ // When we go into a portal, it becomes the parent to remove from.
+ // We will reassign it back when we pop the portal on the way up.
+ currentParent = node.stateNode.containerInfo;
+ // Visit children because portals might contain host components.
+ if (node.child !== null) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
+ }
+ } else {
+ commitUnmount(node);
+ // Visit children because we may find more host components below.
+ if (node.child !== null) {
+ node.child["return"] = node;
+ node = node.child;
+ continue;
}
}
- }
- return shouldUpdate;
- }
-
- function popToNextHostParent(fiber) {
- var parent = fiber["return"];
- while (
- parent !== null &&
- parent.tag !== HostComponent &&
- parent.tag !== HostRoot
- ) {
- parent = parent["return"];
- }
- hydrationParentFiber = parent;
- }
-
- function popHydrationState(fiber) {
- if (fiber !== hydrationParentFiber) {
- // We're deeper than the current hydration context, inside an inserted
- // tree.
- return false;
- }
- if (!isHydrating) {
- // If we're not currently hydrating but we're in a hydration context, then
- // we were an insertion and now need to pop up reenter hydration of our
- // siblings.
- popToNextHostParent(fiber);
- isHydrating = true;
- return false;
- }
-
- var type = fiber.type;
-
- // If we have any remaining hydratable nodes, we need to delete them now.
- // We only do this deeper than head and body since they tend to have random
- // other nodes in them. We also ignore components with pure text content in
- // side of them.
- // TODO: Better heuristic.
- if (
- fiber.tag !== HostComponent ||
- (type !== "head" &&
- type !== "body" &&
- !shouldSetTextContent(type, fiber.memoizedProps))
- ) {
- var nextInstance = nextHydratableInstance;
- while (nextInstance) {
- deleteHydratableInstance(fiber, nextInstance);
- nextInstance = getNextHydratableSibling(nextInstance);
+ if (node === current) {
+ return;
+ }
+ while (node.sibling === null) {
+ if (node["return"] === null || node["return"] === current) {
+ return;
+ }
+ node = node["return"];
+ if (node.tag === HostPortal) {
+ // When we go out of the portal, we need to restore the parent.
+ // Since we don't keep a stack of them, we will search for it.
+ currentParentIsValid = false;
+ }
}
+ node.sibling["return"] = node["return"];
+ node = node.sibling;
}
-
- popToNextHostParent(fiber);
- nextHydratableInstance = hydrationParentFiber
- ? getNextHydratableSibling(fiber.stateNode)
- : null;
- return true;
}
- function resetHydrationState() {
- hydrationParentFiber = null;
- nextHydratableInstance = null;
- isHydrating = false;
+ function commitDeletion(current) {
+ // Recursively delete all host nodes from the parent.
+ // Detach refs and call componentWillUnmount() on the whole subtree.
+ unmountHostComponents(current);
+ detachFiber(current);
}
- return {
- enterHydrationState: enterHydrationState,
- resetHydrationState: resetHydrationState,
- tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance,
- prepareToHydrateHostInstance: prepareToHydrateHostInstance,
- prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance,
- popHydrationState: popHydrationState
- };
-};
-
-// This lets us hook into Fiber to debug what it's doing.
-// See https://github.com/facebook/react/pull/8033.
-// This is not part of the public API, not even for React DevTools.
-// You may only inject a debugTool if you work on React Fiber itself.
-var ReactFiberInstrumentation = {
- debugTool: null
-};
-
-var ReactFiberInstrumentation_1 = ReactFiberInstrumentation;
-
-var invokeGuardedCallback$1 = ReactErrorUtils.invokeGuardedCallback;
-var hasCaughtError = ReactErrorUtils.hasCaughtError;
-var clearCaughtError = ReactErrorUtils.clearCaughtError;
-
-{
- var didWarnAboutStateTransition = false;
- var didWarnSetStateChildContext = false;
- var didWarnStateUpdateForUnmountedComponent = {};
-
- var warnAboutUpdateOnUnmounted = function(fiber) {
- var componentName = getComponentName(fiber) || "ReactClass";
- if (didWarnStateUpdateForUnmountedComponent[componentName]) {
- return;
- }
- warning(
- false,
- "Can only update a mounted or mounting " +
- "component. This usually means you called setState, replaceState, " +
- "or forceUpdate on an unmounted component. This is a no-op.\n\nPlease " +
- "check the code for the %s component.",
- componentName
- );
- didWarnStateUpdateForUnmountedComponent[componentName] = true;
- };
-
- var warnAboutInvalidUpdates = function(instance) {
- switch (ReactDebugCurrentFiber.phase) {
- case "getChildContext":
- if (didWarnSetStateChildContext) {
- return;
+ function commitWork(current, finishedWork) {
+ switch (finishedWork.tag) {
+ case ClassComponent: {
+ return;
+ }
+ case HostComponent: {
+ var instance = finishedWork.stateNode;
+ if (instance != null) {
+ // Commit the work prepared earlier.
+ var newProps = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldProps = current !== null ? current.memoizedProps : newProps;
+ var type = finishedWork.type;
+ // TODO: Type the updateQueue to be specific to host components.
+ var updatePayload = finishedWork.updateQueue;
+ finishedWork.updateQueue = null;
+ if (updatePayload !== null) {
+ commitUpdate(
+ instance,
+ updatePayload,
+ type,
+ oldProps,
+ newProps,
+ finishedWork
+ );
+ }
}
- warning(
- false,
- "setState(...): Cannot call setState() inside getChildContext()"
+ return;
+ }
+ case HostText: {
+ invariant(
+ finishedWork.stateNode !== null,
+ "This should have a text node initialized. This error is likely " +
+ "caused by a bug in React. Please file an issue."
);
- didWarnSetStateChildContext = true;
- break;
- case "render":
- if (didWarnAboutStateTransition) {
- return;
- }
- warning(
+ var textInstance = finishedWork.stateNode;
+ var newText = finishedWork.memoizedProps;
+ // For hydration we reuse the update path but we treat the oldProps
+ // as the newProps. The updatePayload will contain the real change in
+ // this case.
+ var oldText = current !== null ? current.memoizedProps : newText;
+ commitTextUpdate(textInstance, oldText, newText);
+ return;
+ }
+ case HostRoot: {
+ return;
+ }
+ default: {
+ invariant(
false,
- "Cannot update during an existing state transition (such as within " +
- "`render` or another component's constructor). Render methods should " +
- "be a pure function of props and state; constructor side-effects are " +
- "an anti-pattern, but can be moved to `componentWillMount`."
+ "This unit of work tag should not have side-effects. This error is " +
+ "likely caused by a bug in React. Please file an issue."
);
- didWarnAboutStateTransition = true;
- break;
+ }
}
- };
-}
-
-var ReactFiberScheduler = function(config) {
- var hostContext = ReactFiberHostContext(config);
- var hydrationContext = ReactFiberHydrationContext(config);
- var popHostContainer = hostContext.popHostContainer,
- popHostContext = hostContext.popHostContext,
- resetHostContainer = hostContext.resetHostContainer;
-
- var _ReactFiberBeginWork = ReactFiberBeginWork(
- config,
- hostContext,
- hydrationContext,
- scheduleWork,
- computeExpirationForFiber
- ),
- beginWork = _ReactFiberBeginWork.beginWork,
- beginFailedWork = _ReactFiberBeginWork.beginFailedWork;
-
- var _ReactFiberCompleteWo = ReactFiberCompleteWork(
- config,
- hostContext,
- hydrationContext
- ),
- completeWork = _ReactFiberCompleteWo.completeWork;
-
- var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError),
- commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent,
- commitPlacement = _ReactFiberCommitWork.commitPlacement,
- commitDeletion = _ReactFiberCommitWork.commitDeletion,
- commitWork = _ReactFiberCommitWork.commitWork,
- commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles,
- commitAttachRef = _ReactFiberCommitWork.commitAttachRef,
- commitDetachRef = _ReactFiberCommitWork.commitDetachRef;
-
- var now = config.now,
- scheduleDeferredCallback = config.scheduleDeferredCallback,
- cancelDeferredCallback = config.cancelDeferredCallback,
- useSyncScheduling = config.useSyncScheduling,
- prepareForCommit = config.prepareForCommit,
- resetAfterCommit = config.resetAfterCommit;
-
- // Represents the current time in ms.
+ }
- var startTime = now();
- var mostRecentCurrentTime = msToExpirationTime(0);
+ function commitResetTextContent(current) {
+ resetTextContent(current.stateNode);
+ }
- // Used to ensure computeUniqueAsyncExpiration is monotonically increases.
- var lastUniqueAsyncExpiration = 0;
+ if (enableMutatingReconciler) {
+ return {
+ commitResetTextContent: commitResetTextContent,
+ commitPlacement: commitPlacement,
+ commitDeletion: commitDeletion,
+ commitWork: commitWork,
+ commitLifeCycles: commitLifeCycles,
+ commitAttachRef: commitAttachRef,
+ commitDetachRef: commitDetachRef
+ };
+ } else {
+ invariant(false, "Mutating reconciler is disabled.");
+ }
+};
- // Represents the expiration time that incoming updates should use. (If this
- // is NoWork, use the default strategy: async updates in async mode, sync
- // updates in sync mode.)
- var expirationContext = NoWork;
+var NO_CONTEXT = {};
- var isWorking = false;
+var ReactFiberHostContext = function(config) {
+ var getChildHostContext = config.getChildHostContext,
+ getRootHostContext = config.getRootHostContext;
- // The next work in progress fiber that we're currently working on.
- var nextUnitOfWork = null;
- var nextRoot = null;
- // The time at which we're currently rendering work.
- var nextRenderExpirationTime = NoWork;
+ var contextStackCursor = createCursor(NO_CONTEXT);
+ var contextFiberStackCursor = createCursor(NO_CONTEXT);
+ var rootInstanceStackCursor = createCursor(NO_CONTEXT);
- // The next fiber with an effect that we're currently committing.
- var nextEffect = null;
+ function requiredContext(c) {
+ invariant(
+ c !== NO_CONTEXT,
+ "Expected host context to exist. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
+ return c;
+ }
- // Keep track of which fibers have captured an error that need to be handled.
- // Work is removed from this collection after componentDidCatch is called.
- var capturedErrors = null;
- // Keep track of which fibers have failed during the current batch of work.
- // This is a different set than capturedErrors, because it is not reset until
- // the end of the batch. This is needed to propagate errors correctly if a
- // subtree fails more than once.
- var failedBoundaries = null;
- // Error boundaries that captured an error during the current commit.
- var commitPhaseBoundaries = null;
- var firstUncaughtError = null;
- var didFatal = false;
+ function getRootHostContainer() {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ return rootInstance;
+ }
- var isCommitting = false;
- var isUnmounting = false;
+ 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);
- // Used for performance tracking.
- var interruptedBy = null;
+ var nextRootContext = getRootHostContext(nextRootInstance);
- function resetContextStack() {
- // Reset the stack
- reset();
- // Reset the cursors
- resetContext();
- resetHostContainer();
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor, nextRootContext, fiber);
}
- function commitAllHostEffects() {
- while (nextEffect !== null) {
- {
- ReactDebugCurrentFiber.setCurrentFiber(nextEffect);
- }
- recordEffect();
-
- var effectTag = nextEffect.effectTag;
- if (effectTag & ContentReset) {
- commitResetTextContent(nextEffect);
- }
+ function popHostContainer(fiber) {
+ pop(contextStackCursor, fiber);
+ pop(contextFiberStackCursor, fiber);
+ pop(rootInstanceStackCursor, fiber);
+ }
- if (effectTag & Ref) {
- var current = nextEffect.alternate;
- if (current !== null) {
- commitDetachRef(current);
- }
- }
+ function getHostContext() {
+ var context = requiredContext(contextStackCursor.current);
+ return context;
+ }
- // The following switch statement is only concerned about placement,
- // updates, and deletions. To avoid needing to add a case for every
- // possible bitmap value, we remove the secondary effects from the
- // effect tag and switch on that value.
- var primaryEffectTag =
- effectTag & ~(Callback | Err | ContentReset | Ref | PerformedWork);
- switch (primaryEffectTag) {
- case Placement: {
- commitPlacement(nextEffect);
- // Clear the "placement" from effect tag so that we know that this is inserted, before
- // any life-cycles like componentDidMount gets called.
- // TODO: findDOMNode doesn't rely on this any more but isMounted
- // does and isMounted is deprecated anyway so we should be able
- // to kill this.
- nextEffect.effectTag &= ~Placement;
- break;
- }
- case PlacementAndUpdate: {
- // Placement
- commitPlacement(nextEffect);
- // Clear the "placement" from effect tag so that we know that this is inserted, before
- // any life-cycles like componentDidMount gets called.
- nextEffect.effectTag &= ~Placement;
+ function pushHostContext(fiber) {
+ var rootInstance = requiredContext(rootInstanceStackCursor.current);
+ var context = requiredContext(contextStackCursor.current);
+ var nextContext = getChildHostContext(context, fiber.type, rootInstance);
- // Update
- var _current = nextEffect.alternate;
- commitWork(_current, nextEffect);
- break;
- }
- case Update: {
- var _current2 = nextEffect.alternate;
- commitWork(_current2, nextEffect);
- break;
- }
- case Deletion: {
- isUnmounting = true;
- commitDeletion(nextEffect);
- isUnmounting = false;
- break;
- }
- }
- nextEffect = nextEffect.nextEffect;
+ // Don't push this Fiber's context unless it's unique.
+ if (context === nextContext) {
+ return;
}
- {
- ReactDebugCurrentFiber.resetCurrentFiber();
+ // Track the context and the Fiber that provided it.
+ // This enables us to pop only Fibers that provide unique contexts.
+ push(contextFiberStackCursor, fiber, fiber);
+ push(contextStackCursor, nextContext, fiber);
+ }
+
+ function popHostContext(fiber) {
+ // Do not pop unless this Fiber provided the current context.
+ // pushHostContext() only pushes Fibers that provide unique contexts.
+ if (contextFiberStackCursor.current !== fiber) {
+ return;
}
+
+ pop(contextStackCursor, fiber);
+ pop(contextFiberStackCursor, fiber);
}
- function commitAllLifeCycles() {
- while (nextEffect !== null) {
- var effectTag = nextEffect.effectTag;
+ function resetHostContainer() {
+ contextStackCursor.current = NO_CONTEXT;
+ rootInstanceStackCursor.current = NO_CONTEXT;
+ }
- if (effectTag & (Update | Callback)) {
- recordEffect();
- var current = nextEffect.alternate;
- commitLifeCycles(current, nextEffect);
- }
+ return {
+ getHostContext: getHostContext,
+ getRootHostContainer: getRootHostContainer,
+ popHostContainer: popHostContainer,
+ popHostContext: popHostContext,
+ pushHostContainer: pushHostContainer,
+ pushHostContext: pushHostContext,
+ resetHostContainer: resetHostContainer
+ };
+};
- if (effectTag & Ref) {
- recordEffect();
- commitAttachRef(nextEffect);
- }
+var ReactFiberHydrationContext = function(config) {
+ var shouldSetTextContent = config.shouldSetTextContent,
+ hydration = config.hydration;
- if (effectTag & Err) {
- recordEffect();
- commitErrorHandling(nextEffect);
- }
+ // If this doesn't have hydration mode.
- var next = nextEffect.nextEffect;
- // Ensure that we clean these up so that we don't accidentally keep them.
- // I'm not actually sure this matters because we can't reset firstEffect
- // and lastEffect since they're on every node, not just the effectful
- // ones. So we have to clean everything as we reuse nodes anyway.
- nextEffect.nextEffect = null;
- // Ensure that we reset the effectTag here so that we can rely on effect
- // tags to reason about the current life-cycle.
- nextEffect = next;
- }
+ if (!hydration) {
+ return {
+ enterHydrationState: function() {
+ return false;
+ },
+ resetHydrationState: function() {},
+ tryToClaimNextHydratableInstance: function() {},
+ prepareToHydrateHostInstance: function() {
+ invariant(
+ false,
+ "Expected prepareToHydrateHostInstance() to never be called. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ },
+ prepareToHydrateHostTextInstance: function() {
+ invariant(
+ false,
+ "Expected prepareToHydrateHostTextInstance() to never be called. " +
+ "This error is likely caused by a bug in React. Please file an issue."
+ );
+ },
+ popHydrationState: function(fiber) {
+ return false;
+ }
+ };
}
- function commitRoot(finishedWork) {
- // We keep track of this so that captureError can collect any boundaries
- // that capture an error during the commit phase. The reason these aren't
- // local to this function is because errors that occur during cWU are
- // captured elsewhere, to prevent the unmount from being interrupted.
- isWorking = true;
- isCommitting = true;
- startCommitTimer();
+ var canHydrateInstance = hydration.canHydrateInstance,
+ canHydrateTextInstance = hydration.canHydrateTextInstance,
+ getNextHydratableSibling = hydration.getNextHydratableSibling,
+ getFirstHydratableChild = hydration.getFirstHydratableChild,
+ hydrateInstance = hydration.hydrateInstance,
+ hydrateTextInstance = hydration.hydrateTextInstance,
+ didNotMatchHydratedContainerTextInstance =
+ hydration.didNotMatchHydratedContainerTextInstance,
+ didNotMatchHydratedTextInstance = hydration.didNotMatchHydratedTextInstance,
+ didNotHydrateContainerInstance = hydration.didNotHydrateContainerInstance,
+ didNotHydrateInstance = hydration.didNotHydrateInstance,
+ didNotFindHydratableContainerInstance =
+ hydration.didNotFindHydratableContainerInstance,
+ didNotFindHydratableContainerTextInstance =
+ hydration.didNotFindHydratableContainerTextInstance,
+ didNotFindHydratableInstance = hydration.didNotFindHydratableInstance,
+ didNotFindHydratableTextInstance =
+ hydration.didNotFindHydratableTextInstance;
+
+ // The deepest Fiber on the stack involved in a hydration context.
+ // This may have been an insertion or a hydration.
- var root = finishedWork.stateNode;
- invariant(
- root.current !== finishedWork,
- "Cannot commit the same tree as before. This is probably a bug " +
- "related to the return field. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- root.isReadyForCommit = false;
+ var hydrationParentFiber = null;
+ var nextHydratableInstance = null;
+ var isHydrating = false;
- // Reset this to null before calling lifecycles
- ReactCurrentOwner.current = null;
+ function enterHydrationState(fiber) {
+ var parentInstance = fiber.stateNode.containerInfo;
+ nextHydratableInstance = getFirstHydratableChild(parentInstance);
+ hydrationParentFiber = fiber;
+ isHydrating = true;
+ return true;
+ }
- var firstEffect = void 0;
- if (finishedWork.effectTag > PerformedWork) {
- // A fiber's effect list consists only of its children, not itself. So if
- // the root has an effect, we need to add it to the end of the list. The
- // resulting list is the set that would belong to the root's parent, if
- // it had one; that is, all the effects in the tree including the root.
- if (finishedWork.lastEffect !== null) {
- finishedWork.lastEffect.nextEffect = finishedWork;
- firstEffect = finishedWork.firstEffect;
- } else {
- firstEffect = finishedWork;
+ function deleteHydratableInstance(returnFiber, instance) {
+ {
+ switch (returnFiber.tag) {
+ case HostRoot:
+ didNotHydrateContainerInstance(
+ returnFiber.stateNode.containerInfo,
+ instance
+ );
+ break;
+ case HostComponent:
+ didNotHydrateInstance(
+ returnFiber.type,
+ returnFiber.memoizedProps,
+ returnFiber.stateNode,
+ instance
+ );
+ break;
}
- } else {
- // There is no effect on the root.
- firstEffect = finishedWork.firstEffect;
}
- prepareForCommit();
+ var childToDelete = createFiberFromHostInstanceForDeletion();
+ childToDelete.stateNode = instance;
+ childToDelete["return"] = returnFiber;
+ childToDelete.effectTag = Deletion;
- // Commit all the side-effects within a tree. We'll do this in two passes.
- // The first pass performs all the host insertions, updates, deletions and
- // ref unmounts.
- nextEffect = firstEffect;
- startCommitHostEffectsTimer();
- while (nextEffect !== null) {
- var didError = false;
- var _error = void 0;
- {
- invokeGuardedCallback$1(null, commitAllHostEffects, null);
- if (hasCaughtError()) {
- didError = true;
- _error = clearCaughtError();
+ // This might seem like it belongs on progressedFirstDeletion. However,
+ // these children are not part of the reconciliation list of children.
+ // Even if we abort and rereconcile the children, that will try to hydrate
+ // again and the nodes are still in the host tree so these will be
+ // recreated.
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = childToDelete;
+ returnFiber.lastEffect = childToDelete;
+ } else {
+ returnFiber.firstEffect = returnFiber.lastEffect = childToDelete;
+ }
+ }
+
+ function insertNonHydratedInstance(returnFiber, fiber) {
+ fiber.effectTag |= Placement;
+ {
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ var parentContainer = returnFiber.stateNode.containerInfo;
+ switch (fiber.tag) {
+ case HostComponent:
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ didNotFindHydratableContainerInstance(
+ parentContainer,
+ type,
+ props
+ );
+ break;
+ case HostText:
+ var text = fiber.pendingProps;
+ didNotFindHydratableContainerTextInstance(parentContainer, text);
+ break;
+ }
+ break;
}
- }
- if (didError) {
- invariant(
- nextEffect !== null,
- "Should have next effect. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- captureError(nextEffect, _error);
- // Clean-up
- if (nextEffect !== null) {
- nextEffect = nextEffect.nextEffect;
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
+ switch (fiber.tag) {
+ case HostComponent:
+ var _type = fiber.type;
+ var _props = fiber.pendingProps;
+ didNotFindHydratableInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _type,
+ _props
+ );
+ break;
+ case HostText:
+ var _text = fiber.pendingProps;
+ didNotFindHydratableTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ _text
+ );
+ break;
+ }
+ break;
}
+ default:
+ return;
}
}
- stopCommitHostEffectsTimer();
-
- resetAfterCommit();
-
- // The work-in-progress tree is now the current tree. This must come after
- // the first pass of the commit phase, so that the previous tree is still
- // current during componentWillUnmount, but before the second pass, so that
- // the finished work is current during componentDidMount/Update.
- root.current = finishedWork;
+ }
- // In the second pass we'll perform all life-cycles and ref callbacks.
- // Life-cycles happen as a separate pass so that all placements, updates,
- // and deletions in the entire tree have already been invoked.
- // This pass also triggers any renderer-specific initial effects.
- nextEffect = firstEffect;
- startCommitLifeCyclesTimer();
- while (nextEffect !== null) {
- var _didError = false;
- var _error2 = void 0;
- {
- invokeGuardedCallback$1(null, commitAllLifeCycles, null);
- if (hasCaughtError()) {
- _didError = true;
- _error2 = clearCaughtError();
+ function tryHydrate(fiber, nextInstance) {
+ switch (fiber.tag) {
+ case HostComponent: {
+ var type = fiber.type;
+ var props = fiber.pendingProps;
+ var instance = canHydrateInstance(nextInstance, type, props);
+ if (instance !== null) {
+ fiber.stateNode = instance;
+ return true;
}
+ return false;
}
- if (_didError) {
- invariant(
- nextEffect !== null,
- "Should have next effect. This error is likely caused by a bug " +
- "in React. Please file an issue."
- );
- captureError(nextEffect, _error2);
- if (nextEffect !== null) {
- nextEffect = nextEffect.nextEffect;
+ case HostText: {
+ var text = fiber.pendingProps;
+ var textInstance = canHydrateTextInstance(nextInstance, text);
+ if (textInstance !== null) {
+ fiber.stateNode = textInstance;
+ return true;
}
+ return false;
}
+ default:
+ return false;
}
+ }
- isCommitting = false;
- isWorking = false;
- stopCommitLifeCyclesTimer();
- stopCommitTimer();
- if (typeof onCommitRoot === "function") {
- onCommitRoot(finishedWork.stateNode);
- }
- if (true && ReactFiberInstrumentation_1.debugTool) {
- ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork);
- }
-
- // If we caught any errors during this commit, schedule their boundaries
- // to update.
- if (commitPhaseBoundaries) {
- commitPhaseBoundaries.forEach(scheduleErrorRecovery);
- commitPhaseBoundaries = null;
+ function tryToClaimNextHydratableInstance(fiber) {
+ if (!isHydrating) {
+ return;
}
-
- if (firstUncaughtError !== null) {
- var _error3 = firstUncaughtError;
- firstUncaughtError = null;
- onUncaughtError(_error3);
+ var nextInstance = nextHydratableInstance;
+ if (!nextInstance) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
}
-
- var remainingTime = root.current.expirationTime;
-
- if (remainingTime === NoWork) {
- capturedErrors = null;
- failedBoundaries = null;
+ if (!tryHydrate(fiber, nextInstance)) {
+ // If we can't hydrate this instance let's try the next one.
+ // We use this as a heuristic. It's based on intuition and not data so it
+ // might be flawed or unnecessary.
+ nextInstance = getNextHydratableSibling(nextInstance);
+ if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
+ // Nothing to hydrate. Make it an insertion.
+ insertNonHydratedInstance(hydrationParentFiber, fiber);
+ isHydrating = false;
+ hydrationParentFiber = fiber;
+ return;
+ }
+ // We matched the next one, we'll now assume that the first one was
+ // superfluous and we'll delete it. Since we can't eagerly delete it
+ // we'll have to schedule a deletion. To do that, this node needs a dummy
+ // fiber associated with it.
+ deleteHydratableInstance(hydrationParentFiber, nextHydratableInstance);
}
-
- return remainingTime;
+ hydrationParentFiber = fiber;
+ nextHydratableInstance = getFirstHydratableChild(nextInstance);
}
- function resetExpirationTime(workInProgress, renderTime) {
- if (renderTime !== Never && workInProgress.expirationTime === Never) {
- // The children of this component are hidden. Don't bubble their
- // expiration times.
- return;
- }
-
- // Check for pending updates.
- var newExpirationTime = getUpdateExpirationTime(workInProgress);
-
- // TODO: Calls need to visit stateNode
-
- // Bubble up the earliest expiration time.
- var child = workInProgress.child;
- while (child !== null) {
- if (
- child.expirationTime !== NoWork &&
- (newExpirationTime === NoWork ||
- newExpirationTime > child.expirationTime)
- ) {
- newExpirationTime = child.expirationTime;
- }
- child = child.sibling;
+ function prepareToHydrateHostInstance(
+ fiber,
+ rootContainerInstance,
+ hostContext
+ ) {
+ var instance = fiber.stateNode;
+ var updatePayload = hydrateInstance(
+ instance,
+ fiber.type,
+ fiber.memoizedProps,
+ rootContainerInstance,
+ hostContext,
+ fiber
+ );
+ // TODO: Type this specific to this type of component.
+ fiber.updateQueue = updatePayload;
+ // If the update payload indicates that there is a change or if there
+ // is a new ref we mark this as an update.
+ if (updatePayload !== null) {
+ return true;
}
- workInProgress.expirationTime = newExpirationTime;
+ return false;
}
- function completeUnitOfWork(workInProgress) {
- while (true) {
- // The current, flushed, state of this fiber is the alternate.
- // Ideally nothing should rely on this, but relying on it here
- // means that we don't need an additional field on the work in
- // progress.
- var current = workInProgress.alternate;
- {
- ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
- }
- var next = completeWork(
- current,
- workInProgress,
- nextRenderExpirationTime
- );
- {
- ReactDebugCurrentFiber.resetCurrentFiber();
- }
-
- var returnFiber = workInProgress["return"];
- var siblingFiber = workInProgress.sibling;
-
- resetExpirationTime(workInProgress, nextRenderExpirationTime);
-
- if (next !== null) {
- stopWorkTimer(workInProgress);
- if (true && ReactFiberInstrumentation_1.debugTool) {
- ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
+ function prepareToHydrateHostTextInstance(fiber) {
+ var textInstance = fiber.stateNode;
+ var textContent = fiber.memoizedProps;
+ var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
+ {
+ if (shouldUpdate) {
+ // We assume that prepareToHydrateHostTextInstance is called in a context where the
+ // hydration parent is the parent host component of this host text.
+ var returnFiber = hydrationParentFiber;
+ if (returnFiber !== null) {
+ switch (returnFiber.tag) {
+ case HostRoot: {
+ var parentContainer = returnFiber.stateNode.containerInfo;
+ didNotMatchHydratedContainerTextInstance(
+ parentContainer,
+ textInstance,
+ textContent
+ );
+ break;
+ }
+ case HostComponent: {
+ var parentType = returnFiber.type;
+ var parentProps = returnFiber.memoizedProps;
+ var parentInstance = returnFiber.stateNode;
+ didNotMatchHydratedTextInstance(
+ parentType,
+ parentProps,
+ parentInstance,
+ textInstance,
+ textContent
+ );
+ break;
+ }
+ }
}
- // If completing this work spawned new work, do that next. We'll come
- // back here again.
- return next;
}
+ }
+ return shouldUpdate;
+ }
- if (returnFiber !== null) {
- // Append all the effects of the subtree and this fiber onto the effect
- // list of the parent. The completion order of the children affects the
- // side-effect order.
- if (returnFiber.firstEffect === null) {
- returnFiber.firstEffect = workInProgress.firstEffect;
- }
- if (workInProgress.lastEffect !== null) {
- if (returnFiber.lastEffect !== null) {
- returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
- }
- returnFiber.lastEffect = workInProgress.lastEffect;
- }
+ function popToNextHostParent(fiber) {
+ var parent = fiber["return"];
+ while (
+ parent !== null &&
+ parent.tag !== HostComponent &&
+ parent.tag !== HostRoot
+ ) {
+ parent = parent["return"];
+ }
+ hydrationParentFiber = parent;
+ }
- // If this fiber had side-effects, we append it AFTER the children's
- // side-effects. We can perform certain side-effects earlier if
- // needed, by doing multiple passes over the effect list. We don't want
- // to schedule our own side-effect on our own list because if end up
- // reusing children we'll schedule this effect onto itself since we're
- // at the end.
- var effectTag = workInProgress.effectTag;
- // Skip both NoWork and PerformedWork tags when creating the effect list.
- // PerformedWork effect is read by React DevTools but shouldn't be committed.
- if (effectTag > PerformedWork) {
- if (returnFiber.lastEffect !== null) {
- returnFiber.lastEffect.nextEffect = workInProgress;
- } else {
- returnFiber.firstEffect = workInProgress;
- }
- returnFiber.lastEffect = workInProgress;
- }
- }
+ function popHydrationState(fiber) {
+ if (fiber !== hydrationParentFiber) {
+ // We're deeper than the current hydration context, inside an inserted
+ // tree.
+ return false;
+ }
+ if (!isHydrating) {
+ // If we're not currently hydrating but we're in a hydration context, then
+ // we were an insertion and now need to pop up reenter hydration of our
+ // siblings.
+ popToNextHostParent(fiber);
+ isHydrating = true;
+ return false;
+ }
- stopWorkTimer(workInProgress);
- if (true && ReactFiberInstrumentation_1.debugTool) {
- ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
- }
+ var type = fiber.type;
- if (siblingFiber !== null) {
- // If there is more work to do in this returnFiber, do that next.
- return siblingFiber;
- } else if (returnFiber !== null) {
- // If there's no more work in this returnFiber. Complete the returnFiber.
- workInProgress = returnFiber;
- continue;
- } else {
- // We've reached the root.
- var root = workInProgress.stateNode;
- root.isReadyForCommit = true;
- return null;
+ // If we have any remaining hydratable nodes, we need to delete them now.
+ // We only do this deeper than head and body since they tend to have random
+ // other nodes in them. We also ignore components with pure text content in
+ // side of them.
+ // TODO: Better heuristic.
+ if (
+ fiber.tag !== HostComponent ||
+ (type !== "head" &&
+ type !== "body" &&
+ !shouldSetTextContent(type, fiber.memoizedProps))
+ ) {
+ var nextInstance = nextHydratableInstance;
+ while (nextInstance) {
+ deleteHydratableInstance(fiber, nextInstance);
+ nextInstance = getNextHydratableSibling(nextInstance);
}
}
- // Without this explicit null return Flow complains of invalid return type
- // TODO Remove the above while(true) loop
- // eslint-disable-next-line no-unreachable
- return null;
+ popToNextHostParent(fiber);
+ nextHydratableInstance = hydrationParentFiber
+ ? getNextHydratableSibling(fiber.stateNode)
+ : null;
+ return true;
}
- function performUnitOfWork(workInProgress) {
- // The current, flushed, state of this fiber is the alternate.
- // Ideally nothing should rely on this, but relying on it here
- // means that we don't need an additional field on the work in
- // progress.
- var current = workInProgress.alternate;
+ function resetHydrationState() {
+ hydrationParentFiber = null;
+ nextHydratableInstance = null;
+ isHydrating = false;
+ }
- // See if beginning this work spawns more work.
- startWorkTimer(workInProgress);
- {
- ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
- }
+ return {
+ enterHydrationState: enterHydrationState,
+ resetHydrationState: resetHydrationState,
+ tryToClaimNextHydratableInstance: tryToClaimNextHydratableInstance,
+ prepareToHydrateHostInstance: prepareToHydrateHostInstance,
+ prepareToHydrateHostTextInstance: prepareToHydrateHostTextInstance,
+ popHydrationState: popHydrationState
+ };
+};
- var next = beginWork(current, workInProgress, nextRenderExpirationTime);
- {
- ReactDebugCurrentFiber.resetCurrentFiber();
- }
- if (true && ReactFiberInstrumentation_1.debugTool) {
- ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
- }
+// This lets us hook into Fiber to debug what it's doing.
+// See https://github.com/facebook/react/pull/8033.
+// This is not part of the public API, not even for React DevTools.
+// You may only inject a debugTool if you work on React Fiber itself.
+var ReactFiberInstrumentation = {
+ debugTool: null
+};
- if (next === null) {
- // If this doesn't spawn new work, complete the current work.
- next = completeUnitOfWork(workInProgress);
- }
+var ReactFiberInstrumentation_1 = ReactFiberInstrumentation;
- ReactCurrentOwner.current = null;
+// Module provided by RN:
+/**
+ * Intercept lifecycle errors and ensure they are shown with the correct stack
+ * trace within the native redbox component.
+ */
+function showErrorDialog(capturedError) {
+ var componentStack = capturedError.componentStack,
+ error = capturedError.error;
- return next;
- }
+ var errorToHandle = void 0;
- function performFailedUnitOfWork(workInProgress) {
- // The current, flushed, state of this fiber is the alternate.
- // Ideally nothing should rely on this, but relying on it here
- // means that we don't need an additional field on the work in
- // progress.
- var current = workInProgress.alternate;
+ // Typically Errors are thrown but eg strings or null can be thrown as well.
+ if (error instanceof Error) {
+ var message = error.message,
+ name = error.name;
- // See if beginning this work spawns more work.
- startWorkTimer(workInProgress);
- {
- ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
- }
- var next = beginFailedWork(
- current,
- workInProgress,
- nextRenderExpirationTime
+ var summary = message ? name + ": " + message : name;
+
+ errorToHandle = error;
+
+ try {
+ errorToHandle.message =
+ summary + "\n\nThis error is located at:" + componentStack;
+ } catch (e) {}
+ } else if (typeof error === "string") {
+ errorToHandle = new Error(
+ error + "\n\nThis error is located at:" + componentStack
);
- {
- ReactDebugCurrentFiber.resetCurrentFiber();
- }
- if (true && ReactFiberInstrumentation_1.debugTool) {
- ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
- }
+ } else {
+ errorToHandle = new Error("Unspecified error at:" + componentStack);
+ }
- if (next === null) {
- // If this doesn't spawn new work, complete the current work.
- next = completeUnitOfWork(workInProgress);
- }
+ ExceptionsManager.handleException(errorToHandle, false);
- ReactCurrentOwner.current = null;
+ // Return false here to prevent ReactFiberErrorLogger default behavior of
+ // logging error details to console.error. Calls to console.error are
+ // automatically routed to the native redbox controller, which we've already
+ // done above by calling ExceptionsManager.
+ return false;
+}
- return next;
- }
+function logCapturedError(capturedError) {
+ var logError = showErrorDialog(capturedError);
- function workLoop(expirationTime) {
- if (capturedErrors !== null) {
- // If there are unhandled errors, switch to the slow work loop.
- // TODO: How to avoid this check in the fast path? Maybe the renderer
- // could keep track of which roots have unhandled errors and call a
- // forked version of renderRoot.
- slowWorkLoopThatChecksForFailedWork(expirationTime);
- return;
- }
- if (
- nextRenderExpirationTime === NoWork ||
- nextRenderExpirationTime > expirationTime
- ) {
- return;
- }
+ // Allow injected showErrorDialog() to prevent default console.error logging.
+ // This enables renderers like ReactNative to better manage redbox behavior.
+ if (logError === false) {
+ return;
+ }
- if (nextRenderExpirationTime <= mostRecentCurrentTime) {
- // Flush all expired work.
- while (nextUnitOfWork !== null) {
- nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
- }
- } else {
- // Flush asynchronous work until the deadline runs out of time.
- while (nextUnitOfWork !== null && !shouldYield()) {
- nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
- }
- }
+ var error = capturedError.error;
+ var suppressLogging = error && error.suppressReactErrorLogging;
+ if (suppressLogging) {
+ return;
}
- function slowWorkLoopThatChecksForFailedWork(expirationTime) {
- if (
- nextRenderExpirationTime === NoWork ||
- nextRenderExpirationTime > expirationTime
- ) {
- return;
- }
+ {
+ var componentName = capturedError.componentName,
+ componentStack = capturedError.componentStack,
+ errorBoundaryName = capturedError.errorBoundaryName,
+ errorBoundaryFound = capturedError.errorBoundaryFound,
+ willRetry = capturedError.willRetry;
- if (nextRenderExpirationTime <= mostRecentCurrentTime) {
- // Flush all expired work.
- while (nextUnitOfWork !== null) {
- if (hasCapturedError(nextUnitOfWork)) {
- // Use a forked version of performUnitOfWork
- nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
- } else {
- nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
- }
+ var componentNameMessage = componentName
+ ? "The above error occurred in the <" + componentName + "> component:"
+ : "The above error occurred in one of your React components:";
+
+ var errorBoundaryMessage = void 0;
+ // errorBoundaryFound check is sufficient; errorBoundaryName check is to satisfy Flow.
+ if (errorBoundaryFound && errorBoundaryName) {
+ if (willRetry) {
+ errorBoundaryMessage =
+ "React will try to recreate this component tree from scratch " +
+ ("using the error boundary you provided, " + errorBoundaryName + ".");
+ } else {
+ errorBoundaryMessage =
+ "This error was initially handled by the error boundary " +
+ errorBoundaryName +
+ ".\n" +
+ "Recreating the tree from scratch failed so React will unmount the tree.";
}
} else {
- // Flush asynchronous work until the deadline runs out of time.
- while (nextUnitOfWork !== null && !shouldYield()) {
- if (hasCapturedError(nextUnitOfWork)) {
- // Use a forked version of performUnitOfWork
- nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
- } else {
- nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
- }
- }
+ errorBoundaryMessage =
+ "Consider adding an error boundary to your tree to customize error handling behavior.\n" +
+ "Visit https://fb.me/react-error-boundaries to learn more about error boundaries.";
}
+ var combinedMessage =
+ "" +
+ componentNameMessage +
+ componentStack +
+ "\n\n" +
+ ("" + errorBoundaryMessage);
+
+ // In development, we provide our own message with just the component stack.
+ // We don't include the original error message and JS stack because the browser
+ // has already printed it. Even if the application swallows the error, it is still
+ // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
+ console.error(combinedMessage);
}
+}
- function renderRootCatchBlock(root, failedWork, boundary, expirationTime) {
- // We're going to restart the error boundary that captured the error.
- // Conceptually, we're unwinding the stack. We need to unwind the
- // context stack, too.
- unwindContexts(failedWork, boundary);
+var invokeGuardedCallback$2 = ReactErrorUtils.invokeGuardedCallback;
+var hasCaughtError = ReactErrorUtils.hasCaughtError;
+var clearCaughtError = ReactErrorUtils.clearCaughtError;
- // Restart the error boundary using a forked version of
- // performUnitOfWork that deletes the boundary's children. The entire
- // failed subree will be unmounted. During the commit phase, a special
- // lifecycle method is called on the error boundary, which triggers
- // a re-render.
- nextUnitOfWork = performFailedUnitOfWork(boundary);
+var didWarnAboutStateTransition = void 0;
+var didWarnSetStateChildContext = void 0;
+var warnAboutUpdateOnUnmounted = void 0;
+var warnAboutInvalidUpdates = void 0;
- // Continue working.
- workLoop(expirationTime);
- }
+{
+ didWarnAboutStateTransition = false;
+ didWarnSetStateChildContext = false;
+ var didWarnStateUpdateForUnmountedComponent = {};
- function renderRoot(root, expirationTime) {
- invariant(
- !isWorking,
- "renderRoot was called recursively. This error is likely caused " +
- "by a bug in React. Please file an issue."
+ warnAboutUpdateOnUnmounted = function(fiber) {
+ var componentName = getComponentName(fiber) || "ReactClass";
+ if (didWarnStateUpdateForUnmountedComponent[componentName]) {
+ return;
+ }
+ warning(
+ false,
+ "Can only update a mounted or mounting " +
+ "component. This usually means you called setState, replaceState, " +
+ "or forceUpdate on an unmounted component. This is a no-op.\n\nPlease " +
+ "check the code for the %s component.",
+ componentName
);
- isWorking = true;
-
- // We're about to mutate the work-in-progress tree. If the root was pending
- // commit, it no longer is: we'll need to complete it again.
- root.isReadyForCommit = false;
+ didWarnStateUpdateForUnmountedComponent[componentName] = true;
+ };
- // Check if we're starting from a fresh stack, or if we're resuming from
- // previously yielded work.
- if (
- root !== nextRoot ||
- expirationTime !== nextRenderExpirationTime ||
- nextUnitOfWork === null
- ) {
- // Reset the stack and start working from the root.
- resetContextStack();
- nextRoot = root;
- nextRenderExpirationTime = expirationTime;
- nextUnitOfWork = createWorkInProgress(
- nextRoot.current,
- null,
- expirationTime
- );
+ warnAboutInvalidUpdates = function(instance) {
+ switch (ReactDebugCurrentFiber.phase) {
+ case "getChildContext":
+ if (didWarnSetStateChildContext) {
+ return;
+ }
+ warning(
+ false,
+ "setState(...): Cannot call setState() inside getChildContext()"
+ );
+ didWarnSetStateChildContext = true;
+ break;
+ case "render":
+ if (didWarnAboutStateTransition) {
+ return;
+ }
+ warning(
+ false,
+ "Cannot update during an existing state transition (such as within " +
+ "`render` or another component's constructor). Render methods should " +
+ "be a pure function of props and state; constructor side-effects are " +
+ "an anti-pattern, but can be moved to `componentWillMount`."
+ );
+ didWarnAboutStateTransition = true;
+ break;
}
+ };
+}
+
+var ReactFiberScheduler = function(config) {
+ var hostContext = ReactFiberHostContext(config);
+ var hydrationContext = ReactFiberHydrationContext(config);
+ var popHostContainer = hostContext.popHostContainer,
+ popHostContext = hostContext.popHostContext,
+ resetHostContainer = hostContext.resetHostContainer;
- startWorkLoopTimer(nextUnitOfWork);
+ var _ReactFiberBeginWork = ReactFiberBeginWork(
+ config,
+ hostContext,
+ hydrationContext,
+ scheduleWork,
+ computeExpirationForFiber
+ ),
+ beginWork = _ReactFiberBeginWork.beginWork,
+ beginFailedWork = _ReactFiberBeginWork.beginFailedWork;
- var didError = false;
- var error = null;
- {
- invokeGuardedCallback$1(null, workLoop, null, expirationTime);
- if (hasCaughtError()) {
- didError = true;
- error = clearCaughtError();
- }
- }
+ var _ReactFiberCompleteWo = ReactFiberCompleteWork(
+ config,
+ hostContext,
+ hydrationContext
+ ),
+ completeWork = _ReactFiberCompleteWo.completeWork;
- // An error was thrown during the render phase.
- while (didError) {
- if (didFatal) {
- // This was a fatal error. Don't attempt to recover from it.
- firstUncaughtError = error;
- break;
- }
+ var _ReactFiberCommitWork = ReactFiberCommitWork(config, captureError),
+ commitResetTextContent = _ReactFiberCommitWork.commitResetTextContent,
+ commitPlacement = _ReactFiberCommitWork.commitPlacement,
+ commitDeletion = _ReactFiberCommitWork.commitDeletion,
+ commitWork = _ReactFiberCommitWork.commitWork,
+ commitLifeCycles = _ReactFiberCommitWork.commitLifeCycles,
+ commitAttachRef = _ReactFiberCommitWork.commitAttachRef,
+ commitDetachRef = _ReactFiberCommitWork.commitDetachRef;
- var failedWork = nextUnitOfWork;
- if (failedWork === null) {
- // An error was thrown but there's no current unit of work. This can
- // happen during the commit phase if there's a bug in the renderer.
- didFatal = true;
- continue;
- }
+ var now = config.now,
+ scheduleDeferredCallback = config.scheduleDeferredCallback,
+ cancelDeferredCallback = config.cancelDeferredCallback,
+ prepareForCommit = config.prepareForCommit,
+ resetAfterCommit = config.resetAfterCommit;
- // "Capture" the error by finding the nearest boundary. If there is no
- // error boundary, we use the root.
- var boundary = captureError(failedWork, error);
- invariant(
- boundary !== null,
- "Should have found an error boundary. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
+ // Represents the current time in ms.
- if (didFatal) {
- // The error we just captured was a fatal error. This happens
- // when the error propagates to the root more than once.
- continue;
- }
+ var startTime = now();
+ var mostRecentCurrentTime = msToExpirationTime(0);
- didError = false;
- error = null;
- {
- invokeGuardedCallback$1(
- null,
- renderRootCatchBlock,
- null,
- root,
- failedWork,
- boundary,
- expirationTime
- );
- if (hasCaughtError()) {
- didError = true;
- error = clearCaughtError();
- continue;
- }
- }
- // We're finished working. Exit the error loop.
- break;
- }
+ // Used to ensure computeUniqueAsyncExpiration is monotonically increases.
+ var lastUniqueAsyncExpiration = 0;
- var uncaughtError = firstUncaughtError;
+ // Represents the expiration time that incoming updates should use. (If this
+ // is NoWork, use the default strategy: async updates in async mode, sync
+ // updates in sync mode.)
+ var expirationContext = NoWork;
- // We're done performing work. Time to clean up.
- stopWorkLoopTimer(interruptedBy);
- interruptedBy = null;
- isWorking = false;
- didFatal = false;
- firstUncaughtError = null;
+ var isWorking = false;
- if (uncaughtError !== null) {
- onUncaughtError(uncaughtError);
- }
+ // The next work in progress fiber that we're currently working on.
+ var nextUnitOfWork = null;
+ var nextRoot = null;
+ // The time at which we're currently rendering work.
+ var nextRenderExpirationTime = NoWork;
- return root.isReadyForCommit ? root.current.alternate : null;
- }
+ // The next fiber with an effect that we're currently committing.
+ var nextEffect = null;
- // Returns the boundary that captured the error, or null if the error is ignored
- function captureError(failedWork, error) {
- // It is no longer valid because we exited the user code.
- ReactCurrentOwner.current = null;
- {
- ReactDebugCurrentFiber.resetCurrentFiber();
- }
+ // Keep track of which fibers have captured an error that need to be handled.
+ // Work is removed from this collection after componentDidCatch is called.
+ var capturedErrors = null;
+ // Keep track of which fibers have failed during the current batch of work.
+ // This is a different set than capturedErrors, because it is not reset until
+ // the end of the batch. This is needed to propagate errors correctly if a
+ // subtree fails more than once.
+ var failedBoundaries = null;
+ // Error boundaries that captured an error during the current commit.
+ var commitPhaseBoundaries = null;
+ var firstUncaughtError = null;
+ var didFatal = false;
- // Search for the nearest error boundary.
- var boundary = null;
+ var isCommitting = false;
+ var isUnmounting = false;
- // Passed to logCapturedError()
- var errorBoundaryFound = false;
- var willRetry = false;
- var errorBoundaryName = null;
+ // Used for performance tracking.
+ var interruptedBy = null;
- // Host containers are a special case. If the failed work itself is a host
- // container, then it acts as its own boundary. In all other cases, we
- // ignore the work itself and only search through the parents.
- if (failedWork.tag === HostRoot) {
- boundary = failedWork;
+ function resetContextStack() {
+ // Reset the stack
+ reset();
+ // Reset the cursors
+ resetContext();
+ resetProviderStack();
+ resetHostContainer();
+ }
- if (isFailedBoundary(failedWork)) {
- // If this root already failed, there must have been an error when
- // attempting to unmount it. This is a worst-case scenario and
- // should only be possible if there's a bug in the renderer.
- didFatal = true;
+ function commitAllHostEffects() {
+ while (nextEffect !== null) {
+ {
+ ReactDebugCurrentFiber.setCurrentFiber(nextEffect);
}
- } else {
- var node = failedWork["return"];
- while (node !== null && boundary === null) {
- if (node.tag === ClassComponent) {
- var instance = node.stateNode;
- if (typeof instance.componentDidCatch === "function") {
- errorBoundaryFound = true;
- errorBoundaryName = getComponentName(node);
-
- // Found an error boundary!
- boundary = node;
- willRetry = true;
- }
- } else if (node.tag === HostRoot) {
- // Treat the root like a no-op error boundary
- boundary = node;
- }
+ recordEffect();
- if (isFailedBoundary(node)) {
- // This boundary is already in a failed state.
+ var effectTag = nextEffect.effectTag;
+ if (effectTag & ContentReset) {
+ commitResetTextContent(nextEffect);
+ }
- // If we're currently unmounting, that means this error was
- // thrown while unmounting a failed subtree. We should ignore
- // the error.
- if (isUnmounting) {
- return null;
- }
+ if (effectTag & Ref) {
+ var current = nextEffect.alternate;
+ if (current !== null) {
+ commitDetachRef(current);
+ }
+ }
- // If we're in the commit phase, we should check to see if
- // this boundary already captured an error during this commit.
- // This case exists because multiple errors can be thrown during
- // a single commit without interruption.
- if (
- commitPhaseBoundaries !== null &&
- (commitPhaseBoundaries.has(node) ||
- (node.alternate !== null &&
- commitPhaseBoundaries.has(node.alternate)))
- ) {
- // If so, we should ignore this error.
- return null;
- }
+ // The following switch statement is only concerned about placement,
+ // updates, and deletions. To avoid needing to add a case for every
+ // possible bitmap value, we remove the secondary effects from the
+ // effect tag and switch on that value.
+ var primaryEffectTag =
+ effectTag & ~(Callback | Err | ContentReset | Ref | PerformedWork);
+ switch (primaryEffectTag) {
+ case Placement: {
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ // TODO: findDOMNode doesn't rely on this any more but isMounted
+ // does and isMounted is deprecated anyway so we should be able
+ // to kill this.
+ nextEffect.effectTag &= ~Placement;
+ break;
+ }
+ case PlacementAndUpdate: {
+ // Placement
+ commitPlacement(nextEffect);
+ // Clear the "placement" from effect tag so that we know that this is inserted, before
+ // any life-cycles like componentDidMount gets called.
+ nextEffect.effectTag &= ~Placement;
- // The error should propagate to the next boundary -— we keep looking.
- boundary = null;
- willRetry = false;
+ // Update
+ var _current = nextEffect.alternate;
+ commitWork(_current, nextEffect);
+ break;
+ }
+ case Update: {
+ var _current2 = nextEffect.alternate;
+ commitWork(_current2, nextEffect);
+ break;
+ }
+ case Deletion: {
+ isUnmounting = true;
+ commitDeletion(nextEffect);
+ isUnmounting = false;
+ break;
}
-
- node = node["return"];
}
+ nextEffect = nextEffect.nextEffect;
}
- if (boundary !== null) {
- // Add to the collection of failed boundaries. This lets us know that
- // subsequent errors in this subtree should propagate to the next boundary.
- if (failedBoundaries === null) {
- failedBoundaries = new Set();
- }
- failedBoundaries.add(boundary);
+ {
+ ReactDebugCurrentFiber.resetCurrentFiber();
+ }
+ }
- // This method is unsafe outside of the begin and complete phases.
- // We might be in the commit phase when an error is captured.
- // The risk is that the return path from this Fiber may not be accurate.
- // That risk is acceptable given the benefit of providing users more context.
- var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork);
- var _componentName = getComponentName(failedWork);
+ function commitAllLifeCycles() {
+ {
+ ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
- // Add to the collection of captured errors. This is stored as a global
- // map of errors and their component stack location keyed by the boundaries
- // that capture them. We mostly use this Map as a Set; it's a Map only to
- // avoid adding a field to Fiber to store the error.
- if (capturedErrors === null) {
- capturedErrors = new Map();
+ if (warnAboutDeprecatedLifecycles) {
+ ReactStrictModeWarnings.flushPendingDeprecationWarnings();
}
+ }
- var capturedError = {
- componentName: _componentName,
- componentStack: _componentStack,
- error: error,
- errorBoundary: errorBoundaryFound ? boundary.stateNode : null,
- errorBoundaryFound: errorBoundaryFound,
- errorBoundaryName: errorBoundaryName,
- willRetry: willRetry
- };
+ while (nextEffect !== null) {
+ var effectTag = nextEffect.effectTag;
- capturedErrors.set(boundary, capturedError);
+ if (effectTag & (Update | Callback)) {
+ recordEffect();
+ var current = nextEffect.alternate;
+ commitLifeCycles(current, nextEffect);
+ }
- try {
- logCapturedError(capturedError);
- } catch (e) {
- // Prevent cycle if logCapturedError() throws.
- // A cycle may still occur if logCapturedError renders a component that throws.
- var suppressLogging = e && e.suppressReactErrorLogging;
- if (!suppressLogging) {
- console.error(e);
- }
+ if (effectTag & Ref) {
+ recordEffect();
+ commitAttachRef(nextEffect);
}
- // If we're in the commit phase, defer scheduling an update on the
- // boundary until after the commit is complete
- if (isCommitting) {
- if (commitPhaseBoundaries === null) {
- commitPhaseBoundaries = new Set();
- }
- commitPhaseBoundaries.add(boundary);
- } else {
- // Otherwise, schedule an update now.
- // TODO: Is this actually necessary during the render phase? Is it
- // possible to unwind and continue rendering at the same priority,
- // without corrupting internal state?
- scheduleErrorRecovery(boundary);
+ if (effectTag & Err) {
+ recordEffect();
+ commitErrorHandling(nextEffect);
}
- return boundary;
- } else if (firstUncaughtError === null) {
- // If no boundary is found, we'll need to throw the error
- firstUncaughtError = error;
+
+ var next = nextEffect.nextEffect;
+ // Ensure that we clean these up so that we don't accidentally keep them.
+ // I'm not actually sure this matters because we can't reset firstEffect
+ // and lastEffect since they're on every node, not just the effectful
+ // ones. So we have to clean everything as we reuse nodes anyway.
+ nextEffect.nextEffect = null;
+ // Ensure that we reset the effectTag here so that we can rely on effect
+ // tags to reason about the current life-cycle.
+ nextEffect = next;
}
- return null;
}
- function hasCapturedError(fiber) {
- // TODO: capturedErrors should store the boundary instance, to avoid needing
- // to check the alternate.
- return (
- capturedErrors !== null &&
- (capturedErrors.has(fiber) ||
- (fiber.alternate !== null && capturedErrors.has(fiber.alternate)))
- );
- }
+ function commitRoot(finishedWork) {
+ // We keep track of this so that captureError can collect any boundaries
+ // that capture an error during the commit phase. The reason these aren't
+ // local to this function is because errors that occur during cWU are
+ // captured elsewhere, to prevent the unmount from being interrupted.
+ isWorking = true;
+ isCommitting = true;
+ startCommitTimer();
- function isFailedBoundary(fiber) {
- // TODO: failedBoundaries should store the boundary instance, to avoid
- // needing to check the alternate.
- return (
- failedBoundaries !== null &&
- (failedBoundaries.has(fiber) ||
- (fiber.alternate !== null && failedBoundaries.has(fiber.alternate)))
+ var root = finishedWork.stateNode;
+ invariant(
+ root.current !== finishedWork,
+ "Cannot commit the same tree as before. This is probably a bug " +
+ "related to the return field. This error is likely caused by a bug " +
+ "in React. Please file an issue."
);
- }
+ root.isReadyForCommit = false;
- function commitErrorHandling(effectfulFiber) {
- var capturedError = void 0;
- if (capturedErrors !== null) {
- capturedError = capturedErrors.get(effectfulFiber);
- capturedErrors["delete"](effectfulFiber);
- if (capturedError == null) {
- if (effectfulFiber.alternate !== null) {
- effectfulFiber = effectfulFiber.alternate;
- capturedError = capturedErrors.get(effectfulFiber);
- capturedErrors["delete"](effectfulFiber);
- }
+ // Reset this to null before calling lifecycles
+ ReactCurrentOwner.current = null;
+
+ var firstEffect = void 0;
+ if (finishedWork.effectTag > PerformedWork) {
+ // A fiber's effect list consists only of its children, not itself. So if
+ // the root has an effect, we need to add it to the end of the list. The
+ // resulting list is the set that would belong to the root's parent, if
+ // it had one; that is, all the effects in the tree including the root.
+ if (finishedWork.lastEffect !== null) {
+ finishedWork.lastEffect.nextEffect = finishedWork;
+ firstEffect = finishedWork.firstEffect;
+ } else {
+ firstEffect = finishedWork;
}
+ } else {
+ // There is no effect on the root.
+ firstEffect = finishedWork.firstEffect;
}
- invariant(
- capturedError != null,
- "No error for given unit of work. This error is likely caused by a " +
- "bug in React. Please file an issue."
- );
-
- switch (effectfulFiber.tag) {
- case ClassComponent:
- var instance = effectfulFiber.stateNode;
-
- var info = {
- componentStack: capturedError.componentStack
- };
+ prepareForCommit();
- // Allow the boundary to handle the error, usually by scheduling
- // an update to itself
- instance.componentDidCatch(capturedError.error, info);
- return;
- case HostRoot:
- if (firstUncaughtError === null) {
- firstUncaughtError = capturedError.error;
+ // Commit all the side-effects within a tree. We'll do this in two passes.
+ // The first pass performs all the host insertions, updates, deletions and
+ // ref unmounts.
+ nextEffect = firstEffect;
+ startCommitHostEffectsTimer();
+ while (nextEffect !== null) {
+ var didError = false;
+ var _error = void 0;
+ {
+ invokeGuardedCallback$2(null, commitAllHostEffects, null);
+ if (hasCaughtError()) {
+ didError = true;
+ _error = clearCaughtError();
}
- return;
- default:
+ }
+ if (didError) {
invariant(
- false,
- "Invalid type of work. This error is likely caused by a bug in " +
- "React. Please file an issue."
+ nextEffect !== null,
+ "Should have next effect. This error is likely caused by a bug " +
+ "in React. Please file an issue."
);
+ captureError(nextEffect, _error);
+ // Clean-up
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
+ }
}
- }
+ stopCommitHostEffectsTimer();
- function unwindContexts(from, to) {
- var node = from;
- while (node !== null) {
- switch (node.tag) {
- case ClassComponent:
- popContextProvider(node);
- break;
- case HostComponent:
- popHostContext(node);
- break;
- case HostRoot:
- popHostContainer(node);
- break;
- case HostPortal:
- popHostContainer(node);
- break;
+ resetAfterCommit();
+
+ // The work-in-progress tree is now the current tree. This must come after
+ // the first pass of the commit phase, so that the previous tree is still
+ // current during componentWillUnmount, but before the second pass, so that
+ // the finished work is current during componentDidMount/Update.
+ root.current = finishedWork;
+
+ // In the second pass we'll perform all life-cycles and ref callbacks.
+ // Life-cycles happen as a separate pass so that all placements, updates,
+ // and deletions in the entire tree have already been invoked.
+ // This pass also triggers any renderer-specific initial effects.
+ nextEffect = firstEffect;
+ startCommitLifeCyclesTimer();
+ while (nextEffect !== null) {
+ var _didError = false;
+ var _error2 = void 0;
+ {
+ invokeGuardedCallback$2(null, commitAllLifeCycles, null);
+ if (hasCaughtError()) {
+ _didError = true;
+ _error2 = clearCaughtError();
+ }
}
- if (node === to || node.alternate === to) {
- stopFailedWorkTimer(node);
- break;
- } else {
- stopWorkTimer(node);
+ if (_didError) {
+ invariant(
+ nextEffect !== null,
+ "Should have next effect. This error is likely caused by a bug " +
+ "in React. Please file an issue."
+ );
+ captureError(nextEffect, _error2);
+ if (nextEffect !== null) {
+ nextEffect = nextEffect.nextEffect;
+ }
}
- node = node["return"];
}
- }
- function computeAsyncExpiration() {
- // Given the current clock time, returns an expiration time. We use rounding
- // to batch like updates together.
- // Should complete within ~1000ms. 1200ms max.
- var currentTime = recalculateCurrentTime();
- var expirationMs = 1000;
- var bucketSizeMs = 200;
- return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs);
- }
+ isCommitting = false;
+ isWorking = false;
+ stopCommitLifeCyclesTimer();
+ stopCommitTimer();
+ if (typeof onCommitRoot === "function") {
+ onCommitRoot(finishedWork.stateNode);
+ }
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCommitWork(finishedWork);
+ }
+
+ // If we caught any errors during this commit, schedule their boundaries
+ // to update.
+ if (commitPhaseBoundaries) {
+ commitPhaseBoundaries.forEach(scheduleErrorRecovery);
+ commitPhaseBoundaries = null;
+ }
- // Creates a unique async expiration time.
- function computeUniqueAsyncExpiration() {
- var result = computeAsyncExpiration();
- if (result <= lastUniqueAsyncExpiration) {
- // Since we assume the current time monotonically increases, we only hit
- // this branch when computeUniqueAsyncExpiration is fired multiple times
- // within a 200ms window (or whatever the async bucket size is).
- result = lastUniqueAsyncExpiration + 1;
+ if (firstUncaughtError !== null) {
+ var _error3 = firstUncaughtError;
+ firstUncaughtError = null;
+ onUncaughtError(_error3);
}
- lastUniqueAsyncExpiration = result;
- return lastUniqueAsyncExpiration;
- }
- function computeExpirationForFiber(fiber) {
- var expirationTime = void 0;
- if (expirationContext !== NoWork) {
- // An explicit expiration context was set;
- expirationTime = expirationContext;
- } else if (isWorking) {
- if (isCommitting) {
- // Updates that occur during the commit phase should have sync priority
- // by default.
- expirationTime = Sync;
- } else {
- // Updates during the render phase should expire at the same time as
- // the work that is being rendered.
- expirationTime = nextRenderExpirationTime;
- }
- } else {
- // No explicit expiration context was set, and we're not currently
- // performing work. Calculate a new expiration time.
- if (useSyncScheduling && !(fiber.internalContextTag & AsyncUpdates)) {
- // This is a sync update
- expirationTime = Sync;
- } else {
- // This is an async update
- expirationTime = computeAsyncExpiration();
- }
+ var remainingTime = root.current.expirationTime;
+
+ if (remainingTime === NoWork) {
+ capturedErrors = null;
+ failedBoundaries = null;
}
- return expirationTime;
- }
- function scheduleWork(fiber, expirationTime) {
- return scheduleWorkImpl(fiber, expirationTime, false);
+ return remainingTime;
}
- function checkRootNeedsClearing(root, fiber, expirationTime) {
- if (
- !isWorking &&
- root === nextRoot &&
- expirationTime < nextRenderExpirationTime
- ) {
- // Restart the root from the top.
- if (nextUnitOfWork !== null) {
- // This is an interruption. (Used for performance tracking.)
- interruptedBy = fiber;
- }
- nextRoot = null;
- nextUnitOfWork = null;
- nextRenderExpirationTime = NoWork;
+ function resetExpirationTime(workInProgress, renderTime) {
+ if (renderTime !== Never && workInProgress.expirationTime === Never) {
+ // The children of this component are hidden. Don't bubble their
+ // expiration times.
+ return;
}
- }
- function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) {
- recordScheduleUpdate();
+ // Check for pending updates.
+ var newExpirationTime = getUpdateExpirationTime(workInProgress);
- {
- if (!isErrorRecovery && fiber.tag === ClassComponent) {
- var instance = fiber.stateNode;
- warnAboutInvalidUpdates(instance);
- }
- }
+ // TODO: Calls need to visit stateNode
- var node = fiber;
- while (node !== null) {
- // Walk the parent path to the root and update each node's
- // expiration time.
+ // Bubble up the earliest expiration time.
+ var child = workInProgress.child;
+ while (child !== null) {
if (
- node.expirationTime === NoWork ||
- node.expirationTime > expirationTime
+ child.expirationTime !== NoWork &&
+ (newExpirationTime === NoWork ||
+ newExpirationTime > child.expirationTime)
) {
- node.expirationTime = expirationTime;
+ newExpirationTime = child.expirationTime;
}
- if (node.alternate !== null) {
- if (
- node.alternate.expirationTime === NoWork ||
- node.alternate.expirationTime > expirationTime
- ) {
- node.alternate.expirationTime = expirationTime;
+ child = child.sibling;
+ }
+ workInProgress.expirationTime = newExpirationTime;
+ }
+
+ function completeUnitOfWork(workInProgress) {
+ while (true) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current = workInProgress.alternate;
+ {
+ ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
+ }
+ var next = completeWork(
+ current,
+ workInProgress,
+ nextRenderExpirationTime
+ );
+ {
+ ReactDebugCurrentFiber.resetCurrentFiber();
+ }
+
+ var returnFiber = workInProgress["return"];
+ var siblingFiber = workInProgress.sibling;
+
+ resetExpirationTime(workInProgress, nextRenderExpirationTime);
+
+ if (next !== null) {
+ stopWorkTimer(workInProgress);
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
}
+ // If completing this work spawned new work, do that next. We'll come
+ // back here again.
+ return next;
}
- if (node["return"] === null) {
- if (node.tag === HostRoot) {
- var root = node.stateNode;
- checkRootNeedsClearing(root, fiber, expirationTime);
- requestWork(root, expirationTime);
- checkRootNeedsClearing(root, fiber, expirationTime);
- } else {
- {
- if (!isErrorRecovery && fiber.tag === ClassComponent) {
- warnAboutUpdateOnUnmounted(fiber);
- }
+ if (returnFiber !== null) {
+ // Append all the effects of the subtree and this fiber onto the effect
+ // list of the parent. The completion order of the children affects the
+ // side-effect order.
+ if (returnFiber.firstEffect === null) {
+ returnFiber.firstEffect = workInProgress.firstEffect;
+ }
+ if (workInProgress.lastEffect !== null) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
- return;
+ returnFiber.lastEffect = workInProgress.lastEffect;
}
- }
- node = node["return"];
- }
- }
- function scheduleErrorRecovery(fiber) {
- scheduleWorkImpl(fiber, Sync, true);
- }
+ // If this fiber had side-effects, we append it AFTER the children's
+ // side-effects. We can perform certain side-effects earlier if
+ // needed, by doing multiple passes over the effect list. We don't want
+ // to schedule our own side-effect on our own list because if end up
+ // reusing children we'll schedule this effect onto itself since we're
+ // at the end.
+ var effectTag = workInProgress.effectTag;
+ // Skip both NoWork and PerformedWork tags when creating the effect list.
+ // PerformedWork effect is read by React DevTools but shouldn't be committed.
+ if (effectTag > PerformedWork) {
+ if (returnFiber.lastEffect !== null) {
+ returnFiber.lastEffect.nextEffect = workInProgress;
+ } else {
+ returnFiber.firstEffect = workInProgress;
+ }
+ returnFiber.lastEffect = workInProgress;
+ }
+ }
- function recalculateCurrentTime() {
- // Subtract initial time so it fits inside 32bits
- var ms = now() - startTime;
- mostRecentCurrentTime = msToExpirationTime(ms);
- return mostRecentCurrentTime;
- }
+ stopWorkTimer(workInProgress);
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onCompleteWork(workInProgress);
+ }
- function deferredUpdates(fn) {
- var previousExpirationContext = expirationContext;
- expirationContext = computeAsyncExpiration();
- try {
- return fn();
- } finally {
- expirationContext = previousExpirationContext;
+ if (siblingFiber !== null) {
+ // If there is more work to do in this returnFiber, do that next.
+ return siblingFiber;
+ } else if (returnFiber !== null) {
+ // If there's no more work in this returnFiber. Complete the returnFiber.
+ workInProgress = returnFiber;
+ continue;
+ } else {
+ // We've reached the root.
+ var root = workInProgress.stateNode;
+ root.isReadyForCommit = true;
+ return null;
+ }
}
+
+ // Without this explicit null return Flow complains of invalid return type
+ // TODO Remove the above while(true) loop
+ // eslint-disable-next-line no-unreachable
+ return null;
}
- function syncUpdates(fn) {
- var previousExpirationContext = expirationContext;
- expirationContext = Sync;
- try {
- return fn();
- } finally {
- expirationContext = previousExpirationContext;
+ function performUnitOfWork(workInProgress) {
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current = workInProgress.alternate;
+
+ // See if beginning this work spawns more work.
+ startWorkTimer(workInProgress);
+ {
+ ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
}
- }
- // TODO: Everything below this is written as if it has been lifted to the
- // renderers. I'll do this in a follow-up.
+ var next = beginWork(current, workInProgress, nextRenderExpirationTime);
+ {
+ ReactDebugCurrentFiber.resetCurrentFiber();
+ }
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
+ }
- // Linked-list of roots
- var firstScheduledRoot = null;
- var lastScheduledRoot = null;
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ next = completeUnitOfWork(workInProgress);
+ }
- var callbackExpirationTime = NoWork;
- var callbackID = -1;
- var isRendering = false;
- var nextFlushedRoot = null;
- var nextFlushedExpirationTime = NoWork;
- var deadlineDidExpire = false;
- var hasUnhandledError = false;
- var unhandledError = null;
- var deadline = null;
+ ReactCurrentOwner.current = null;
- var isBatchingUpdates = false;
- var isUnbatchingUpdates = false;
+ return next;
+ }
- var completedBatches = null;
+ function performFailedUnitOfWork(workInProgress) {
+ {
+ ReactStrictModeWarnings.discardPendingWarnings();
+ }
- // Use these to prevent an infinite loop of nested updates
- var NESTED_UPDATE_LIMIT = 1000;
- var nestedUpdateCount = 0;
+ // The current, flushed, state of this fiber is the alternate.
+ // Ideally nothing should rely on this, but relying on it here
+ // means that we don't need an additional field on the work in
+ // progress.
+ var current = workInProgress.alternate;
- var timeHeuristicForUnitOfWork = 1;
+ // See if beginning this work spawns more work.
+ startWorkTimer(workInProgress);
+ {
+ ReactDebugCurrentFiber.setCurrentFiber(workInProgress);
+ }
+ var next = beginFailedWork(
+ current,
+ workInProgress,
+ nextRenderExpirationTime
+ );
+ {
+ ReactDebugCurrentFiber.resetCurrentFiber();
+ }
+ if (true && ReactFiberInstrumentation_1.debugTool) {
+ ReactFiberInstrumentation_1.debugTool.onBeginWork(workInProgress);
+ }
- function scheduleCallbackWithExpiration(expirationTime) {
- if (callbackExpirationTime !== NoWork) {
- // A callback is already scheduled. Check its expiration time (timeout).
- if (expirationTime > callbackExpirationTime) {
- // Existing callback has sufficient timeout. Exit.
- return;
- } else {
- // Existing callback has insufficient timeout. Cancel and schedule a
- // new one.
- cancelDeferredCallback(callbackID);
- }
- // The request callback timer is already running. Don't start a new one.
- } else {
- startRequestCallbackTimer();
+ if (next === null) {
+ // If this doesn't spawn new work, complete the current work.
+ next = completeUnitOfWork(workInProgress);
}
- // Compute a timeout for the given expiration time.
- var currentMs = now() - startTime;
- var expirationMs = expirationTimeToMs(expirationTime);
- var timeout = expirationMs - currentMs;
+ ReactCurrentOwner.current = null;
- callbackExpirationTime = expirationTime;
- callbackID = scheduleDeferredCallback(performAsyncWork, {
- timeout: timeout
- });
+ return next;
}
- // requestWork is called by the scheduler whenever a root receives an update.
- // It's up to the renderer to call renderRoot at some point in the future.
- function requestWork(root, expirationTime) {
- if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
- invariant(
- false,
- "Maximum update depth exceeded. This can happen when a " +
- "component repeatedly calls setState inside componentWillUpdate or " +
- "componentDidUpdate. React limits the number of nested updates to " +
- "prevent infinite loops."
- );
+ function workLoop(expirationTime) {
+ if (capturedErrors !== null) {
+ // If there are unhandled errors, switch to the slow work loop.
+ // TODO: How to avoid this check in the fast path? Maybe the renderer
+ // could keep track of which roots have unhandled errors and call a
+ // forked version of renderRoot.
+ slowWorkLoopThatChecksForFailedWork(expirationTime);
+ return;
+ }
+ if (
+ nextRenderExpirationTime === NoWork ||
+ nextRenderExpirationTime > expirationTime
+ ) {
+ return;
}
- // Add the root to the schedule.
- // Check if this root is already part of the schedule.
- if (root.nextScheduledRoot === null) {
- // This root is not already scheduled. Add it.
- root.remainingExpirationTime = expirationTime;
- if (lastScheduledRoot === null) {
- firstScheduledRoot = lastScheduledRoot = root;
- root.nextScheduledRoot = root;
- } else {
- lastScheduledRoot.nextScheduledRoot = root;
- lastScheduledRoot = root;
- lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ if (nextRenderExpirationTime <= mostRecentCurrentTime) {
+ // Flush all expired work.
+ while (nextUnitOfWork !== null) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
} else {
- // This root is already scheduled, but its priority may have increased.
- var remainingExpirationTime = root.remainingExpirationTime;
- if (
- remainingExpirationTime === NoWork ||
- expirationTime < remainingExpirationTime
- ) {
- // Update the priority.
- root.remainingExpirationTime = expirationTime;
+ // Flush asynchronous work until the deadline runs out of time.
+ while (nextUnitOfWork !== null && !shouldYield()) {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
+ }
- if (isRendering) {
- // Prevent reentrancy. Remaining work will be scheduled at the end of
- // the currently rendering batch.
+ function slowWorkLoopThatChecksForFailedWork(expirationTime) {
+ if (
+ nextRenderExpirationTime === NoWork ||
+ nextRenderExpirationTime > expirationTime
+ ) {
return;
}
- if (isBatchingUpdates) {
- // Flush work at the end of the batch.
- if (isUnbatchingUpdates) {
- // ...unless we're inside unbatchedUpdates, in which case we should
- // flush it now.
- nextFlushedRoot = root;
- nextFlushedExpirationTime = Sync;
- performWorkOnRoot(root, Sync, recalculateCurrentTime());
+ if (nextRenderExpirationTime <= mostRecentCurrentTime) {
+ // Flush all expired work.
+ while (nextUnitOfWork !== null) {
+ if (hasCapturedError(nextUnitOfWork)) {
+ // Use a forked version of performUnitOfWork
+ nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
+ } else {
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
+ }
}
- return;
- }
-
- // TODO: Get rid of Sync and use current time?
- if (expirationTime === Sync) {
- performWork(Sync, null);
} else {
- scheduleCallbackWithExpiration(expirationTime);
- }
- }
-
- function findHighestPriorityRoot() {
- var highestPriorityWork = NoWork;
- var highestPriorityRoot = null;
-
- if (lastScheduledRoot !== null) {
- var previousScheduledRoot = lastScheduledRoot;
- var root = firstScheduledRoot;
- while (root !== null) {
- var remainingExpirationTime = root.remainingExpirationTime;
- if (remainingExpirationTime === NoWork) {
- // This root no longer has work. Remove it from the scheduler.
-
- // TODO: This check is redudant, but Flow is confused by the branch
- // below where we set lastScheduledRoot to null, even though we break
- // from the loop right after.
- invariant(
- previousScheduledRoot !== null && lastScheduledRoot !== null,
- "Should have a previous and last root. This error is likely " +
- "caused by a bug in React. Please file an issue."
- );
- if (root === root.nextScheduledRoot) {
- // This is the only root in the list.
- root.nextScheduledRoot = null;
- firstScheduledRoot = lastScheduledRoot = null;
- break;
- } else if (root === firstScheduledRoot) {
- // This is the first root in the list.
- var next = root.nextScheduledRoot;
- firstScheduledRoot = next;
- lastScheduledRoot.nextScheduledRoot = next;
- root.nextScheduledRoot = null;
- } else if (root === lastScheduledRoot) {
- // This is the last root in the list.
- lastScheduledRoot = previousScheduledRoot;
- lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
- root.nextScheduledRoot = null;
- break;
- } else {
- previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
- root.nextScheduledRoot = null;
- }
- root = previousScheduledRoot.nextScheduledRoot;
+ // Flush asynchronous work until the deadline runs out of time.
+ while (nextUnitOfWork !== null && !shouldYield()) {
+ if (hasCapturedError(nextUnitOfWork)) {
+ // Use a forked version of performUnitOfWork
+ nextUnitOfWork = performFailedUnitOfWork(nextUnitOfWork);
} else {
- if (
- highestPriorityWork === NoWork ||
- remainingExpirationTime < highestPriorityWork
- ) {
- // Update the priority, if it's higher
- highestPriorityWork = remainingExpirationTime;
- highestPriorityRoot = root;
- }
- if (root === lastScheduledRoot) {
- break;
- }
- previousScheduledRoot = root;
- root = root.nextScheduledRoot;
+ nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
}
}
+ }
+
+ function renderRootCatchBlock(root, failedWork, boundary, expirationTime) {
+ // We're going to restart the error boundary that captured the error.
+ // Conceptually, we're unwinding the stack. We need to unwind the
+ // context stack, too.
+ unwindContexts(failedWork, boundary);
+
+ // Restart the error boundary using a forked version of
+ // performUnitOfWork that deletes the boundary's children. The entire
+ // failed subree will be unmounted. During the commit phase, a special
+ // lifecycle method is called on the error boundary, which triggers
+ // a re-render.
+ nextUnitOfWork = performFailedUnitOfWork(boundary);
- // If the next root is the same as the previous root, this is a nested
- // update. To prevent an infinite loop, increment the nested update count.
- var previousFlushedRoot = nextFlushedRoot;
+ // Continue working.
+ workLoop(expirationTime);
+ }
+
+ function renderRoot(root, expirationTime) {
+ invariant(
+ !isWorking,
+ "renderRoot was called recursively. This error is likely caused " +
+ "by a bug in React. Please file an issue."
+ );
+ isWorking = true;
+
+ // We're about to mutate the work-in-progress tree. If the root was pending
+ // commit, it no longer is: we'll need to complete it again.
+ root.isReadyForCommit = false;
+
+ // Check if we're starting from a fresh stack, or if we're resuming from
+ // previously yielded work.
if (
- previousFlushedRoot !== null &&
- previousFlushedRoot === highestPriorityRoot
+ root !== nextRoot ||
+ expirationTime !== nextRenderExpirationTime ||
+ nextUnitOfWork === null
) {
- nestedUpdateCount++;
- } else {
- // Reset whenever we switch roots.
- nestedUpdateCount = 0;
+ // Reset the stack and start working from the root.
+ resetContextStack();
+ nextRoot = root;
+ nextRenderExpirationTime = expirationTime;
+ nextUnitOfWork = createWorkInProgress(
+ nextRoot.current,
+ null,
+ expirationTime
+ );
}
- nextFlushedRoot = highestPriorityRoot;
- nextFlushedExpirationTime = highestPriorityWork;
- }
- function performAsyncWork(dl) {
- performWork(NoWork, dl);
- }
+ startWorkLoopTimer(nextUnitOfWork);
- function performWork(minExpirationTime, dl) {
- deadline = dl;
+ var didError = false;
+ var error = null;
+ {
+ invokeGuardedCallback$2(null, workLoop, null, expirationTime);
+ if (hasCaughtError()) {
+ didError = true;
+ error = clearCaughtError();
+ }
+ }
- // Keep working on roots until there's no more work, or until the we reach
- // the deadline.
- findHighestPriorityRoot();
+ // An error was thrown during the render phase.
+ while (didError) {
+ if (didFatal) {
+ // This was a fatal error. Don't attempt to recover from it.
+ firstUncaughtError = error;
+ break;
+ }
- if (enableUserTimingAPI && deadline !== null) {
- var didExpire = nextFlushedExpirationTime < recalculateCurrentTime();
- stopRequestCallbackTimer(didExpire);
- }
+ var failedWork = nextUnitOfWork;
+ if (failedWork === null) {
+ // An error was thrown but there's no current unit of work. This can
+ // happen during the commit phase if there's a bug in the renderer.
+ didFatal = true;
+ continue;
+ }
- while (
- nextFlushedRoot !== null &&
- nextFlushedExpirationTime !== NoWork &&
- (minExpirationTime === NoWork ||
- nextFlushedExpirationTime <= minExpirationTime) &&
- !deadlineDidExpire
- ) {
- performWorkOnRoot(
- nextFlushedRoot,
- nextFlushedExpirationTime,
- recalculateCurrentTime()
+ // "Capture" the error by finding the nearest boundary. If there is no
+ // error boundary, we use the root.
+ var boundary = captureError(failedWork, error);
+ invariant(
+ boundary !== null,
+ "Should have found an error boundary. This error is likely " +
+ "caused by a bug in React. Please file an issue."
);
- // Find the next highest priority work.
- findHighestPriorityRoot();
+
+ if (didFatal) {
+ // The error we just captured was a fatal error. This happens
+ // when the error propagates to the root more than once.
+ continue;
+ }
+
+ didError = false;
+ error = null;
+ {
+ invokeGuardedCallback$2(
+ null,
+ renderRootCatchBlock,
+ null,
+ root,
+ failedWork,
+ boundary,
+ expirationTime
+ );
+ if (hasCaughtError()) {
+ didError = true;
+ error = clearCaughtError();
+ continue;
+ }
+ }
+ // We're finished working. Exit the error loop.
+ break;
}
- // We're done flushing work. Either we ran out of time in this callback,
- // or there's no more work left with sufficient priority.
+ var uncaughtError = firstUncaughtError;
- // If we're inside a callback, set this to false since we just completed it.
- if (deadline !== null) {
- callbackExpirationTime = NoWork;
- callbackID = -1;
+ // We're done performing work. Time to clean up.
+ stopWorkLoopTimer(interruptedBy);
+ interruptedBy = null;
+ isWorking = false;
+ didFatal = false;
+ firstUncaughtError = null;
+
+ if (uncaughtError !== null) {
+ onUncaughtError(uncaughtError);
}
- // If there's work left over, schedule a new callback.
- if (nextFlushedExpirationTime !== NoWork) {
- scheduleCallbackWithExpiration(nextFlushedExpirationTime);
+
+ return root.isReadyForCommit ? root.current.alternate : null;
+ }
+
+ // Returns the boundary that captured the error, or null if the error is ignored
+ function captureError(failedWork, error) {
+ // It is no longer valid because we exited the user code.
+ ReactCurrentOwner.current = null;
+ {
+ ReactDebugCurrentFiber.resetCurrentFiber();
}
- // Clean-up.
- deadline = null;
- deadlineDidExpire = false;
- nestedUpdateCount = 0;
+ // Search for the nearest error boundary.
+ var boundary = null;
- finishRendering();
- }
+ // Passed to logCapturedError()
+ var errorBoundaryFound = false;
+ var willRetry = false;
+ var errorBoundaryName = null;
- function flushRoot(root, expirationTime) {
- invariant(
- !isRendering,
- "work.commit(): Cannot commit while already rendering. This likely " +
- "means you attempted to commit from inside a lifecycle method."
- );
- // Perform work on root as if the given expiration time is the current time.
- // This has the effect of synchronously flushing all work up to and
- // including the given time.
- performWorkOnRoot(root, expirationTime, expirationTime);
- finishRendering();
- }
+ // Host containers are a special case. If the failed work itself is a host
+ // container, then it acts as its own boundary. In all other cases, we
+ // ignore the work itself and only search through the parents.
+ if (failedWork.tag === HostRoot) {
+ boundary = failedWork;
- function finishRendering() {
- if (completedBatches !== null) {
- var batches = completedBatches;
- completedBatches = null;
- for (var i = 0; i < batches.length; i++) {
- var batch = batches[i];
- try {
- batch._onComplete();
- } catch (error) {
- if (!hasUnhandledError) {
- hasUnhandledError = true;
- unhandledError = error;
+ if (isFailedBoundary(failedWork)) {
+ // If this root already failed, there must have been an error when
+ // attempting to unmount it. This is a worst-case scenario and
+ // should only be possible if there's a bug in the renderer.
+ didFatal = true;
+ }
+ } else {
+ var node = failedWork["return"];
+ while (node !== null && boundary === null) {
+ if (node.tag === ClassComponent) {
+ var instance = node.stateNode;
+ if (typeof instance.componentDidCatch === "function") {
+ errorBoundaryFound = true;
+ errorBoundaryName = getComponentName(node);
+
+ // Found an error boundary!
+ boundary = node;
+ willRetry = true;
+ }
+ } else if (node.tag === HostRoot) {
+ // Treat the root like a no-op error boundary
+ boundary = node;
+ }
+
+ if (isFailedBoundary(node)) {
+ // This boundary is already in a failed state.
+
+ // If we're currently unmounting, that means this error was
+ // thrown while unmounting a failed subtree. We should ignore
+ // the error.
+ if (isUnmounting) {
+ return null;
+ }
+
+ // If we're in the commit phase, we should check to see if
+ // this boundary already captured an error during this commit.
+ // This case exists because multiple errors can be thrown during
+ // a single commit without interruption.
+ if (
+ commitPhaseBoundaries !== null &&
+ (commitPhaseBoundaries.has(node) ||
+ (node.alternate !== null &&
+ commitPhaseBoundaries.has(node.alternate)))
+ ) {
+ // If so, we should ignore this error.
+ return null;
}
+
+ // The error should propagate to the next boundary -— we keep looking.
+ boundary = null;
+ willRetry = false;
}
+
+ node = node["return"];
}
}
- if (hasUnhandledError) {
- var _error4 = unhandledError;
- unhandledError = null;
- hasUnhandledError = false;
- throw _error4;
- }
- }
+ if (boundary !== null) {
+ // Add to the collection of failed boundaries. This lets us know that
+ // subsequent errors in this subtree should propagate to the next boundary.
+ if (failedBoundaries === null) {
+ failedBoundaries = new Set();
+ }
+ failedBoundaries.add(boundary);
+
+ // This method is unsafe outside of the begin and complete phases.
+ // We might be in the commit phase when an error is captured.
+ // The risk is that the return path from this Fiber may not be accurate.
+ // That risk is acceptable given the benefit of providing users more context.
+ var _componentStack = getStackAddendumByWorkInProgressFiber(failedWork);
+ var _componentName = getComponentName(failedWork);
+
+ // Add to the collection of captured errors. This is stored as a global
+ // map of errors and their component stack location keyed by the boundaries
+ // that capture them. We mostly use this Map as a Set; it's a Map only to
+ // avoid adding a field to Fiber to store the error.
+ if (capturedErrors === null) {
+ capturedErrors = new Map();
+ }
- function performWorkOnRoot(root, expirationTime, currentTime) {
- invariant(
- !isRendering,
- "performWorkOnRoot was called recursively. This error is likely caused " +
- "by a bug in React. Please file an issue."
- );
+ var capturedError = {
+ componentName: _componentName,
+ componentStack: _componentStack,
+ error: error,
+ errorBoundary: errorBoundaryFound ? boundary.stateNode : null,
+ errorBoundaryFound: errorBoundaryFound,
+ errorBoundaryName: errorBoundaryName,
+ willRetry: willRetry
+ };
- isRendering = true;
+ capturedErrors.set(boundary, capturedError);
- // Check if this is async work or sync/expired work.
- if (expirationTime <= currentTime) {
- // Flush sync work.
- var finishedWork = root.finishedWork;
- if (finishedWork !== null) {
- // This root is already complete. We can commit it.
- completeRoot(root, finishedWork, expirationTime);
- } else {
- root.finishedWork = null;
- finishedWork = renderRoot(root, expirationTime);
- if (finishedWork !== null) {
- // We've completed the root. Commit it.
- completeRoot(root, finishedWork, expirationTime);
- }
- }
- } else {
- // Flush async work.
- var _finishedWork = root.finishedWork;
- if (_finishedWork !== null) {
- // This root is already complete. We can commit it.
- completeRoot(root, _finishedWork, expirationTime);
- } else {
- root.finishedWork = null;
- _finishedWork = renderRoot(root, expirationTime);
- if (_finishedWork !== null) {
- // We've completed the root. Check the deadline one more time
- // before committing.
- if (!shouldYield()) {
- // Still time left. Commit the root.
- completeRoot(root, _finishedWork, expirationTime);
- } else {
- // There's no time left. Mark this root as complete. We'll come
- // back and commit it later.
- root.finishedWork = _finishedWork;
- }
+ try {
+ logCapturedError(capturedError);
+ } catch (e) {
+ // Prevent cycle if logCapturedError() throws.
+ // A cycle may still occur if logCapturedError renders a component that throws.
+ var suppressLogging = e && e.suppressReactErrorLogging;
+ if (!suppressLogging) {
+ console.error(e);
}
}
- }
-
- isRendering = false;
- }
- function completeRoot(root, finishedWork, expirationTime) {
- // Check if there's a batch that matches this expiration time.
- var firstBatch = root.firstBatch;
- if (firstBatch !== null && firstBatch._expirationTime <= expirationTime) {
- if (completedBatches === null) {
- completedBatches = [firstBatch];
+ // If we're in the commit phase, defer scheduling an update on the
+ // boundary until after the commit is complete
+ if (isCommitting) {
+ if (commitPhaseBoundaries === null) {
+ commitPhaseBoundaries = new Set();
+ }
+ commitPhaseBoundaries.add(boundary);
} else {
- completedBatches.push(firstBatch);
- }
- if (firstBatch._defer) {
- // This root is blocked from committing by a batch. Unschedule it until
- // we receive another update.
- root.finishedWork = finishedWork;
- root.remainingExpirationTime = NoWork;
- return;
+ // Otherwise, schedule an update now.
+ // TODO: Is this actually necessary during the render phase? Is it
+ // possible to unwind and continue rendering at the same priority,
+ // without corrupting internal state?
+ scheduleErrorRecovery(boundary);
}
+ return boundary;
+ } else if (firstUncaughtError === null) {
+ // If no boundary is found, we'll need to throw the error
+ firstUncaughtError = error;
}
-
- // Commit the root.
- root.finishedWork = null;
- root.remainingExpirationTime = commitRoot(finishedWork);
+ return null;
}
- // When working on async work, the reconciler asks the renderer if it should
- // yield execution. For DOM, we implement this with requestIdleCallback.
- function shouldYield() {
- if (deadline === null) {
- return false;
- }
- if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
- // Disregard deadline.didTimeout. Only expired work should be flushed
- // during a timeout. This path is only hit for non-expired work.
- return false;
- }
- deadlineDidExpire = true;
- return true;
+ function hasCapturedError(fiber) {
+ // TODO: capturedErrors should store the boundary instance, to avoid needing
+ // to check the alternate.
+ return (
+ capturedErrors !== null &&
+ (capturedErrors.has(fiber) ||
+ (fiber.alternate !== null && capturedErrors.has(fiber.alternate)))
+ );
}
- // TODO: Not happy about this hook. Conceptually, renderRoot should return a
- // tuple of (isReadyForCommit, didError, error)
- function onUncaughtError(error) {
- invariant(
- nextFlushedRoot !== null,
- "Should be working on a root. This error is likely caused by a bug in " +
- "React. Please file an issue."
+ function isFailedBoundary(fiber) {
+ // TODO: failedBoundaries should store the boundary instance, to avoid
+ // needing to check the alternate.
+ return (
+ failedBoundaries !== null &&
+ (failedBoundaries.has(fiber) ||
+ (fiber.alternate !== null && failedBoundaries.has(fiber.alternate)))
);
- // Unschedule this root so we don't work on it again until there's
- // another update.
- nextFlushedRoot.remainingExpirationTime = NoWork;
- if (!hasUnhandledError) {
- hasUnhandledError = true;
- unhandledError = error;
- }
}
- // TODO: Batching should be implemented at the renderer level, not inside
- // the reconciler.
- function batchedUpdates(fn, a) {
- var previousIsBatchingUpdates = isBatchingUpdates;
- isBatchingUpdates = true;
- try {
- return fn(a);
- } finally {
- isBatchingUpdates = previousIsBatchingUpdates;
- if (!isBatchingUpdates && !isRendering) {
- performWork(Sync, null);
+ function commitErrorHandling(effectfulFiber) {
+ var capturedError = void 0;
+ if (capturedErrors !== null) {
+ capturedError = capturedErrors.get(effectfulFiber);
+ capturedErrors["delete"](effectfulFiber);
+ if (capturedError == null) {
+ if (effectfulFiber.alternate !== null) {
+ effectfulFiber = effectfulFiber.alternate;
+ capturedError = capturedErrors.get(effectfulFiber);
+ capturedErrors["delete"](effectfulFiber);
+ }
}
}
- }
- // TODO: Batching should be implemented at the renderer level, not inside
- // the reconciler.
- function unbatchedUpdates(fn) {
- if (isBatchingUpdates && !isUnbatchingUpdates) {
- isUnbatchingUpdates = true;
- try {
- return fn();
- } finally {
- isUnbatchingUpdates = false;
- }
+ invariant(
+ capturedError != null,
+ "No error for given unit of work. This error is likely caused by a " +
+ "bug in React. Please file an issue."
+ );
+
+ switch (effectfulFiber.tag) {
+ case ClassComponent:
+ var instance = effectfulFiber.stateNode;
+
+ var info = {
+ componentStack: capturedError.componentStack
+ };
+
+ // Allow the boundary to handle the error, usually by scheduling
+ // an update to itself
+ instance.componentDidCatch(capturedError.error, info);
+ return;
+ case HostRoot:
+ if (firstUncaughtError === null) {
+ firstUncaughtError = capturedError.error;
+ }
+ return;
+ default:
+ invariant(
+ false,
+ "Invalid type of work. This error is likely caused by a bug in " +
+ "React. Please file an issue."
+ );
}
- return fn();
}
- // TODO: Batching should be implemented at the renderer level, not within
- // the reconciler.
- function flushSync(fn) {
- var previousIsBatchingUpdates = isBatchingUpdates;
- isBatchingUpdates = true;
- try {
- return syncUpdates(fn);
- } finally {
- isBatchingUpdates = previousIsBatchingUpdates;
- invariant(
- !isRendering,
- "flushSync was called from inside a lifecycle method. It cannot be " +
- "called when React is already rendering."
- );
- performWork(Sync, null);
+ function unwindContexts(from, to) {
+ var node = from;
+ while (node !== null) {
+ switch (node.tag) {
+ case ClassComponent:
+ popContextProvider(node);
+ break;
+ case HostComponent:
+ popHostContext(node);
+ break;
+ case HostRoot:
+ popHostContainer(node);
+ break;
+ case HostPortal:
+ popHostContainer(node);
+ break;
+ case ContextProvider:
+ popProvider(node);
+ break;
+ }
+ if (node === to || node.alternate === to) {
+ stopFailedWorkTimer(node);
+ break;
+ } else {
+ stopWorkTimer(node);
+ }
+ node = node["return"];
}
}
- return {
- computeAsyncExpiration: computeAsyncExpiration,
- computeExpirationForFiber: computeExpirationForFiber,
- scheduleWork: scheduleWork,
- requestWork: requestWork,
- flushRoot: flushRoot,
- batchedUpdates: batchedUpdates,
- unbatchedUpdates: unbatchedUpdates,
- flushSync: flushSync,
- deferredUpdates: deferredUpdates,
- computeUniqueAsyncExpiration: computeUniqueAsyncExpiration
- };
-};
-
-{
- var didWarnAboutNestedUpdates = false;
-}
-
-// 0 is PROD, 1 is DEV.
-// Might add PROFILE later.
-
-function getContextForSubtree(parentComponent) {
- if (!parentComponent) {
- return emptyObject;
+ function computeAsyncExpiration() {
+ // Given the current clock time, returns an expiration time. We use rounding
+ // to batch like updates together.
+ // Should complete within ~1000ms. 1200ms max.
+ var currentTime = recalculateCurrentTime();
+ var expirationMs = 1000;
+ var bucketSizeMs = 200;
+ return computeExpirationBucket(currentTime, expirationMs, bucketSizeMs);
}
- var fiber = get(parentComponent);
- var parentContext = findCurrentUnmaskedContext(fiber);
- return isContextProvider(fiber)
- ? processChildContext(fiber, parentContext)
- : parentContext;
-}
-
-var ReactFiberReconciler$1 = function(config) {
- var getPublicInstance = config.getPublicInstance;
-
- var _ReactFiberScheduler = ReactFiberScheduler(config),
- computeAsyncExpiration = _ReactFiberScheduler.computeAsyncExpiration,
- computeUniqueAsyncExpiration =
- _ReactFiberScheduler.computeUniqueAsyncExpiration,
- computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber,
- scheduleWork = _ReactFiberScheduler.scheduleWork,
- requestWork = _ReactFiberScheduler.requestWork,
- flushRoot = _ReactFiberScheduler.flushRoot,
- batchedUpdates = _ReactFiberScheduler.batchedUpdates,
- unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates,
- flushSync = _ReactFiberScheduler.flushSync,
- deferredUpdates = _ReactFiberScheduler.deferredUpdates;
+ // Creates a unique async expiration time.
+ function computeUniqueAsyncExpiration() {
+ var result = computeAsyncExpiration();
+ if (result <= lastUniqueAsyncExpiration) {
+ // Since we assume the current time monotonically increases, we only hit
+ // this branch when computeUniqueAsyncExpiration is fired multiple times
+ // within a 200ms window (or whatever the async bucket size is).
+ result = lastUniqueAsyncExpiration + 1;
+ }
+ lastUniqueAsyncExpiration = result;
+ return lastUniqueAsyncExpiration;
+ }
- function computeRootExpirationTime(current, element) {
+ function computeExpirationForFiber(fiber) {
var expirationTime = void 0;
- // Check if the top-level element is an async wrapper component. If so,
- // treat updates to the root as async. This is a bit weird but lets us
- // avoid a separate `renderAsync` API.
- if (
- enableAsyncSubtreeAPI &&
- element != null &&
- element.type != null &&
- element.type.prototype != null &&
- element.type.prototype.unstable_isAsyncReactComponent === true
- ) {
- expirationTime = computeAsyncExpiration();
+ if (expirationContext !== NoWork) {
+ // An explicit expiration context was set;
+ expirationTime = expirationContext;
+ } else if (isWorking) {
+ if (isCommitting) {
+ // Updates that occur during the commit phase should have sync priority
+ // by default.
+ expirationTime = Sync;
+ } else {
+ // Updates during the render phase should expire at the same time as
+ // the work that is being rendered.
+ expirationTime = nextRenderExpirationTime;
+ }
} else {
- expirationTime = computeExpirationForFiber(current);
+ // No explicit expiration context was set, and we're not currently
+ // performing work. Calculate a new expiration time.
+ if (fiber.internalContextTag & AsyncUpdates) {
+ // This is an async update
+ expirationTime = computeAsyncExpiration();
+ } else {
+ // This is a sync update
+ expirationTime = Sync;
+ }
}
return expirationTime;
}
- function scheduleRootUpdate(current, element, expirationTime, callback) {
- {
- if (
- ReactDebugCurrentFiber.phase === "render" &&
- ReactDebugCurrentFiber.current !== null &&
- !didWarnAboutNestedUpdates
- ) {
- didWarnAboutNestedUpdates = true;
- warning(
- false,
- "Render methods should be a pure function of props and state; " +
- "triggering nested component updates from render is not allowed. " +
- "If necessary, trigger nested updates in componentDidUpdate.\n\n" +
- "Check the render method of %s.",
- getComponentName(ReactDebugCurrentFiber.current) || "Unknown"
- );
+ function scheduleWork(fiber, expirationTime) {
+ return scheduleWorkImpl(fiber, expirationTime, false);
+ }
+
+ function checkRootNeedsClearing(root, fiber, expirationTime) {
+ if (
+ !isWorking &&
+ root === nextRoot &&
+ expirationTime < nextRenderExpirationTime
+ ) {
+ // Restart the root from the top.
+ if (nextUnitOfWork !== null) {
+ // This is an interruption. (Used for performance tracking.)
+ interruptedBy = fiber;
}
+ nextRoot = null;
+ nextUnitOfWork = null;
+ nextRenderExpirationTime = NoWork;
}
+ }
+
+ function scheduleWorkImpl(fiber, expirationTime, isErrorRecovery) {
+ recordScheduleUpdate();
- callback = callback === undefined ? null : callback;
{
- warning(
- callback === null || typeof callback === "function",
- "render(...): Expected the last optional `callback` argument to be a " +
- "function. Instead received: %s.",
- callback
- );
+ if (!isErrorRecovery && fiber.tag === ClassComponent) {
+ var instance = fiber.stateNode;
+ warnAboutInvalidUpdates(instance);
+ }
}
- var update = {
- expirationTime: expirationTime,
- partialState: { element: element },
- callback: callback,
- isReplace: false,
- isForced: false,
- next: null
- };
- insertUpdateIntoFiber(current, update);
- scheduleWork(current, expirationTime);
-
- return expirationTime;
- }
-
- function updateContainerAtExpirationTime(
- element,
- container,
- parentComponent,
- expirationTime,
- callback
- ) {
- // TODO: If this is a nested container, this won't be the root.
- var current = container.current;
+ var node = fiber;
+ while (node !== null) {
+ // Walk the parent path to the root and update each node's
+ // expiration time.
+ if (
+ node.expirationTime === NoWork ||
+ node.expirationTime > expirationTime
+ ) {
+ node.expirationTime = expirationTime;
+ }
+ if (node.alternate !== null) {
+ if (
+ node.alternate.expirationTime === NoWork ||
+ node.alternate.expirationTime > expirationTime
+ ) {
+ node.alternate.expirationTime = expirationTime;
+ }
+ }
+ if (node["return"] === null) {
+ if (node.tag === HostRoot) {
+ var root = node.stateNode;
- {
- if (ReactFiberInstrumentation_1.debugTool) {
- if (current.alternate === null) {
- ReactFiberInstrumentation_1.debugTool.onMountContainer(container);
- } else if (element === null) {
- ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container);
+ checkRootNeedsClearing(root, fiber, expirationTime);
+ requestWork(root, expirationTime);
+ checkRootNeedsClearing(root, fiber, expirationTime);
} else {
- ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container);
+ {
+ if (!isErrorRecovery && fiber.tag === ClassComponent) {
+ warnAboutUpdateOnUnmounted(fiber);
+ }
+ }
+ return;
}
}
+ node = node["return"];
}
+ }
- var context = getContextForSubtree(parentComponent);
- if (container.context === null) {
- container.context = context;
- } else {
- container.pendingContext = context;
- }
+ function scheduleErrorRecovery(fiber) {
+ scheduleWorkImpl(fiber, Sync, true);
+ }
- return scheduleRootUpdate(current, element, expirationTime, callback);
+ function recalculateCurrentTime() {
+ // Subtract initial time so it fits inside 32bits
+ var ms = now() - startTime;
+ mostRecentCurrentTime = msToExpirationTime(ms);
+ return mostRecentCurrentTime;
}
- function findHostInstance(fiber) {
- var hostFiber = findCurrentHostFiber(fiber);
- if (hostFiber === null) {
- return null;
+ function deferredUpdates(fn) {
+ var previousExpirationContext = expirationContext;
+ expirationContext = computeAsyncExpiration();
+ try {
+ return fn();
+ } finally {
+ expirationContext = previousExpirationContext;
}
- return hostFiber.stateNode;
}
- return {
- createContainer: function(containerInfo, hydrate) {
- return createFiberRoot(containerInfo, hydrate);
- },
- updateContainer: function(element, container, parentComponent, callback) {
- var current = container.current;
- var expirationTime = computeRootExpirationTime(current, element);
- return updateContainerAtExpirationTime(
- element,
- container,
- parentComponent,
- expirationTime,
- callback
- );
- },
-
- updateContainerAtExpirationTime: updateContainerAtExpirationTime,
-
- flushRoot: flushRoot,
-
- requestWork: requestWork,
+ function syncUpdates(fn) {
+ var previousExpirationContext = expirationContext;
+ expirationContext = Sync;
+ try {
+ return fn();
+ } finally {
+ expirationContext = previousExpirationContext;
+ }
+ }
- computeUniqueAsyncExpiration: computeUniqueAsyncExpiration,
+ // TODO: Everything below this is written as if it has been lifted to the
+ // renderers. I'll do this in a follow-up.
- batchedUpdates: batchedUpdates,
+ // Linked-list of roots
+ var firstScheduledRoot = null;
+ var lastScheduledRoot = null;
- unbatchedUpdates: unbatchedUpdates,
+ var callbackExpirationTime = NoWork;
+ var callbackID = -1;
+ var isRendering = false;
+ var nextFlushedRoot = null;
+ var nextFlushedExpirationTime = NoWork;
+ var deadlineDidExpire = false;
+ var hasUnhandledError = false;
+ var unhandledError = null;
+ var deadline = null;
- deferredUpdates: deferredUpdates,
+ var isBatchingUpdates = false;
+ var isUnbatchingUpdates = false;
- flushSync: flushSync,
+ var completedBatches = null;
- getPublicRootInstance: function(container) {
- var containerFiber = container.current;
- if (!containerFiber.child) {
- return null;
- }
- switch (containerFiber.child.tag) {
- case HostComponent:
- return getPublicInstance(containerFiber.child.stateNode);
- default:
- return containerFiber.child.stateNode;
- }
- },
+ // Use these to prevent an infinite loop of nested updates
+ var NESTED_UPDATE_LIMIT = 1000;
+ var nestedUpdateCount = 0;
- findHostInstance: findHostInstance,
+ var timeHeuristicForUnitOfWork = 1;
- findHostInstanceWithNoPortals: function(fiber) {
- var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
- if (hostFiber === null) {
- return null;
+ function scheduleCallbackWithExpiration(expirationTime) {
+ if (callbackExpirationTime !== NoWork) {
+ // A callback is already scheduled. Check its expiration time (timeout).
+ if (expirationTime > callbackExpirationTime) {
+ // Existing callback has sufficient timeout. Exit.
+ return;
+ } else {
+ // Existing callback has insufficient timeout. Cancel and schedule a
+ // new one.
+ cancelDeferredCallback(callbackID);
}
- return hostFiber.stateNode;
- },
- injectIntoDevTools: function(devToolsConfig) {
- var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
-
- return injectInternals(
- Object.assign({}, devToolsConfig, {
- findHostInstanceByFiber: function(fiber) {
- return findHostInstance(fiber);
- },
- findFiberByHostInstance: function(instance) {
- if (!findFiberByHostInstance) {
- // Might not be implemented by the renderer.
- return null;
- }
- return findFiberByHostInstance(instance);
- }
- })
- );
+ // The request callback timer is already running. Don't start a new one.
+ } else {
+ startRequestCallbackTimer();
}
- };
-};
-var ReactFiberReconciler$2 = Object.freeze({
- default: ReactFiberReconciler$1
-});
+ // Compute a timeout for the given expiration time.
+ var currentMs = now() - startTime;
+ var expirationMs = expirationTimeToMs(expirationTime);
+ var timeout = expirationMs - currentMs;
-var ReactFiberReconciler$3 =
- (ReactFiberReconciler$2 && ReactFiberReconciler$1) || ReactFiberReconciler$2;
+ callbackExpirationTime = expirationTime;
+ callbackID = scheduleDeferredCallback(performAsyncWork, {
+ timeout: timeout
+ });
+ }
-// TODO: bundle Flow types with the package.
+ // requestWork is called by the scheduler whenever a root receives an update.
+ // It's up to the renderer to call renderRoot at some point in the future.
+ function requestWork(root, expirationTime) {
+ if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
+ invariant(
+ false,
+ "Maximum update depth exceeded. This can happen when a " +
+ "component repeatedly calls setState inside componentWillUpdate or " +
+ "componentDidUpdate. React limits the number of nested updates to " +
+ "prevent infinite loops."
+ );
+ }
-// TODO: decide on the top-level export form.
-// This is hacky but makes it work with both Rollup and Jest.
-var reactReconciler = ReactFiberReconciler$3["default"]
- ? ReactFiberReconciler$3["default"]
- : ReactFiberReconciler$3;
+ // Add the root to the schedule.
+ // Check if this root is already part of the schedule.
+ if (root.nextScheduledRoot === null) {
+ // This root is not already scheduled. Add it.
+ root.remainingExpirationTime = expirationTime;
+ if (lastScheduledRoot === null) {
+ firstScheduledRoot = lastScheduledRoot = root;
+ root.nextScheduledRoot = root;
+ } else {
+ lastScheduledRoot.nextScheduledRoot = root;
+ lastScheduledRoot = root;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ }
+ } else {
+ // This root is already scheduled, but its priority may have increased.
+ var remainingExpirationTime = root.remainingExpirationTime;
+ if (
+ remainingExpirationTime === NoWork ||
+ expirationTime < remainingExpirationTime
+ ) {
+ // Update the priority.
+ root.remainingExpirationTime = expirationTime;
+ }
+ }
-var viewConfigCallbacks = new Map();
-var viewConfigs = new Map();
+ if (isRendering) {
+ // Prevent reentrancy. Remaining work will be scheduled at the end of
+ // the currently rendering batch.
+ return;
+ }
-/**
- * Registers a native view/component by name.
- * A callback is provided to load the view config from UIManager.
- * The callback is deferred until the view is actually rendered.
- * This is done to avoid causing Prepack deopts.
- */
-function register(name, callback) {
- invariant(
- !viewConfigCallbacks.has(name),
- "Tried to register two views with the same name %s",
- name
- );
- viewConfigCallbacks.set(name, callback);
- return name;
-}
+ if (isBatchingUpdates) {
+ // Flush work at the end of the batch.
+ if (isUnbatchingUpdates) {
+ // ...unless we're inside unbatchedUpdates, in which case we should
+ // flush it now.
+ nextFlushedRoot = root;
+ nextFlushedExpirationTime = Sync;
+ performWorkOnRoot(root, Sync, recalculateCurrentTime());
+ }
+ return;
+ }
-/**
- * Retrieves a config for the specified view.
- * If this is the first time the view has been used,
- * This configuration will be lazy-loaded from UIManager.
- */
-function get$1(name) {
- var viewConfig = void 0;
- if (!viewConfigs.has(name)) {
- var callback = viewConfigCallbacks.get(name);
- invariant(
- typeof callback === "function",
- "View config not found for name %s",
- name
- );
- viewConfigCallbacks.set(name, null);
- viewConfig = callback();
- viewConfigs.set(name, viewConfig);
- } else {
- viewConfig = viewConfigs.get(name);
+ // TODO: Get rid of Sync and use current time?
+ if (expirationTime === Sync) {
+ performWork(Sync, null);
+ } else {
+ scheduleCallbackWithExpiration(expirationTime);
+ }
}
- invariant(viewConfig, "View config not found for name %s", name);
- return viewConfig;
-}
-function _classCallCheck$1(instance, Constructor) {
- if (!(instance instanceof Constructor)) {
- throw new TypeError("Cannot call a class as a function");
- }
-}
+ function findHighestPriorityRoot() {
+ var highestPriorityWork = NoWork;
+ var highestPriorityRoot = null;
-// Modules provided by RN:
-/**
- * This component defines the same methods as NativeMethodsMixin but without the
- * findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views
- * and would also result in a circular require.js dependency (since
- * ReactNativeFiber depends on this component and NativeMethodsMixin depends on
- * ReactNativeFiber).
- */
+ if (lastScheduledRoot !== null) {
+ var previousScheduledRoot = lastScheduledRoot;
+ var root = firstScheduledRoot;
+ while (root !== null) {
+ var remainingExpirationTime = root.remainingExpirationTime;
+ if (remainingExpirationTime === NoWork) {
+ // This root no longer has work. Remove it from the scheduler.
-var ReactNativeFiberHostComponent = (function() {
- function ReactNativeFiberHostComponent(tag, viewConfig) {
- _classCallCheck$1(this, ReactNativeFiberHostComponent);
+ // TODO: This check is redudant, but Flow is confused by the branch
+ // below where we set lastScheduledRoot to null, even though we break
+ // from the loop right after.
+ invariant(
+ previousScheduledRoot !== null && lastScheduledRoot !== null,
+ "Should have a previous and last root. This error is likely " +
+ "caused by a bug in React. Please file an issue."
+ );
+ if (root === root.nextScheduledRoot) {
+ // This is the only root in the list.
+ root.nextScheduledRoot = null;
+ firstScheduledRoot = lastScheduledRoot = null;
+ break;
+ } else if (root === firstScheduledRoot) {
+ // This is the first root in the list.
+ var next = root.nextScheduledRoot;
+ firstScheduledRoot = next;
+ lastScheduledRoot.nextScheduledRoot = next;
+ root.nextScheduledRoot = null;
+ } else if (root === lastScheduledRoot) {
+ // This is the last root in the list.
+ lastScheduledRoot = previousScheduledRoot;
+ lastScheduledRoot.nextScheduledRoot = firstScheduledRoot;
+ root.nextScheduledRoot = null;
+ break;
+ } else {
+ previousScheduledRoot.nextScheduledRoot = root.nextScheduledRoot;
+ root.nextScheduledRoot = null;
+ }
+ root = previousScheduledRoot.nextScheduledRoot;
+ } else {
+ if (
+ highestPriorityWork === NoWork ||
+ remainingExpirationTime < highestPriorityWork
+ ) {
+ // Update the priority, if it's higher
+ highestPriorityWork = remainingExpirationTime;
+ highestPriorityRoot = root;
+ }
+ if (root === lastScheduledRoot) {
+ break;
+ }
+ previousScheduledRoot = root;
+ root = root.nextScheduledRoot;
+ }
+ }
+ }
- this._nativeTag = tag;
- this._children = [];
- this.viewConfig = viewConfig;
+ // If the next root is the same as the previous root, this is a nested
+ // update. To prevent an infinite loop, increment the nested update count.
+ var previousFlushedRoot = nextFlushedRoot;
+ if (
+ previousFlushedRoot !== null &&
+ previousFlushedRoot === highestPriorityRoot
+ ) {
+ nestedUpdateCount++;
+ } else {
+ // Reset whenever we switch roots.
+ nestedUpdateCount = 0;
+ }
+ nextFlushedRoot = highestPriorityRoot;
+ nextFlushedExpirationTime = highestPriorityWork;
}
- ReactNativeFiberHostComponent.prototype.blur = function blur() {
- TextInputState.blurTextInput(this._nativeTag);
- };
-
- ReactNativeFiberHostComponent.prototype.focus = function focus() {
- TextInputState.focusTextInput(this._nativeTag);
- };
-
- ReactNativeFiberHostComponent.prototype.measure = function measure(callback) {
- UIManager.measure(this._nativeTag, mountSafeCallback(this, callback));
- };
+ function performAsyncWork(dl) {
+ performWork(NoWork, dl);
+ }
- ReactNativeFiberHostComponent.prototype.measureInWindow = function measureInWindow(
- callback
- ) {
- UIManager.measureInWindow(
- this._nativeTag,
- mountSafeCallback(this, callback)
- );
- };
+ function performWork(minExpirationTime, dl) {
+ deadline = dl;
- ReactNativeFiberHostComponent.prototype.measureLayout = function measureLayout(
- relativeToNativeNode,
- onSuccess,
- onFail /* currently unused */
- ) {
- UIManager.measureLayout(
- this._nativeTag,
- relativeToNativeNode,
- mountSafeCallback(this, onFail),
- mountSafeCallback(this, onSuccess)
- );
- };
+ // Keep working on roots until there's no more work, or until the we reach
+ // the deadline.
+ findHighestPriorityRoot();
- ReactNativeFiberHostComponent.prototype.setNativeProps = function setNativeProps(
- nativeProps
- ) {
- {
- warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
+ if (enableUserTimingAPI && deadline !== null) {
+ var didExpire = nextFlushedExpirationTime < recalculateCurrentTime();
+ stopRequestCallbackTimer(didExpire);
}
- var updatePayload = create(nativeProps, this.viewConfig.validAttributes);
-
- // Avoid the overhead of bridge calls if there's no update.
- // This is an expensive no-op for Android, and causes an unnecessary
- // view invalidation for certain components (eg RCTTextInput) on iOS.
- if (updatePayload != null) {
- UIManager.updateView(
- this._nativeTag,
- this.viewConfig.uiViewClassName,
- updatePayload
+ while (
+ nextFlushedRoot !== null &&
+ nextFlushedExpirationTime !== NoWork &&
+ (minExpirationTime === NoWork ||
+ nextFlushedExpirationTime <= minExpirationTime) &&
+ !deadlineDidExpire
+ ) {
+ performWorkOnRoot(
+ nextFlushedRoot,
+ nextFlushedExpirationTime,
+ recalculateCurrentTime()
);
+ // Find the next highest priority work.
+ findHighestPriorityRoot();
}
- };
-
- return ReactNativeFiberHostComponent;
-})();
-var hasNativePerformanceNow =
- typeof performance === "object" && typeof performance.now === "function";
+ // We're done flushing work. Either we ran out of time in this callback,
+ // or there's no more work left with sufficient priority.
-var now = hasNativePerformanceNow
- ? function() {
- return performance.now();
+ // If we're inside a callback, set this to false since we just completed it.
+ if (deadline !== null) {
+ callbackExpirationTime = NoWork;
+ callbackID = -1;
+ }
+ // If there's work left over, schedule a new callback.
+ if (nextFlushedExpirationTime !== NoWork) {
+ scheduleCallbackWithExpiration(nextFlushedExpirationTime);
}
- : function() {
- return Date.now();
- };
-var scheduledCallback = null;
-var frameDeadline = 0;
+ // Clean-up.
+ deadline = null;
+ deadlineDidExpire = false;
+ nestedUpdateCount = 0;
-var frameDeadlineObject = {
- timeRemaining: function() {
- return frameDeadline - now();
+ finishRendering();
}
-};
-
-function setTimeoutCallback() {
- // TODO (bvaughn) Hard-coded 5ms unblocks initial async testing.
- // React API probably changing to boolean rather than time remaining.
- // Longer-term plan is to rewrite this using shared memory,
- // And just return the value of the bit as the boolean.
- frameDeadline = now() + 5;
- var callback = scheduledCallback;
- scheduledCallback = null;
- if (callback !== null) {
- callback(frameDeadlineObject);
+ function flushRoot(root, expirationTime) {
+ invariant(
+ !isRendering,
+ "work.commit(): Cannot commit while already rendering. This likely " +
+ "means you attempted to commit from inside a lifecycle method."
+ );
+ // Perform work on root as if the given expiration time is the current time.
+ // This has the effect of synchronously flushing all work up to and
+ // including the given time.
+ performWorkOnRoot(root, expirationTime, expirationTime);
+ finishRendering();
}
-}
-
-// RN has a poor polyfill for requestIdleCallback so we aren't using it.
-// This implementation is only intended for short-term use anyway.
-// We also don't implement cancel functionality b'c Fiber doesn't currently need it.
-function scheduleDeferredCallback(callback) {
- // We assume only one callback is scheduled at a time b'c that's how Fiber works.
- scheduledCallback = callback;
- return setTimeout(setTimeoutCallback, 1);
-}
-
-function cancelDeferredCallback(callbackID) {
- scheduledCallback = null;
- clearTimeout(callbackID);
-}
-// Modules provided by RN:
-function recursivelyUncacheFiberNode(node) {
- if (typeof node === "number") {
- // Leaf node (eg text)
- uncacheFiberNode(node);
- } else {
- uncacheFiberNode(node._nativeTag);
+ function finishRendering() {
+ if (completedBatches !== null) {
+ var batches = completedBatches;
+ completedBatches = null;
+ for (var i = 0; i < batches.length; i++) {
+ var batch = batches[i];
+ try {
+ batch._onComplete();
+ } catch (error) {
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
+ }
+ }
- node._children.forEach(recursivelyUncacheFiberNode);
+ if (hasUnhandledError) {
+ var _error4 = unhandledError;
+ unhandledError = null;
+ hasUnhandledError = false;
+ throw _error4;
+ }
}
-}
-var NativeRenderer = reactReconciler({
- appendInitialChild: function(parentInstance, child) {
- parentInstance._children.push(child);
- },
- createInstance: function(
- type,
- props,
- rootContainerInstance,
- hostContext,
- internalInstanceHandle
- ) {
- var tag = ReactNativeTagHandles.allocateTag();
- var viewConfig = get$1(type);
+ function performWorkOnRoot(root, expirationTime, currentTime) {
+ invariant(
+ !isRendering,
+ "performWorkOnRoot was called recursively. This error is likely caused " +
+ "by a bug in React. Please file an issue."
+ );
- {
- for (var key in viewConfig.validAttributes) {
- if (props.hasOwnProperty(key)) {
- deepFreezeAndThrowOnMutationInDev(props[key]);
+ isRendering = true;
+
+ // Check if this is async work or sync/expired work.
+ if (expirationTime <= currentTime) {
+ // Flush sync work.
+ var finishedWork = root.finishedWork;
+ if (finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ finishedWork = renderRoot(root, expirationTime);
+ if (finishedWork !== null) {
+ // We've completed the root. Commit it.
+ completeRoot(root, finishedWork, expirationTime);
+ }
+ }
+ } else {
+ // Flush async work.
+ var _finishedWork = root.finishedWork;
+ if (_finishedWork !== null) {
+ // This root is already complete. We can commit it.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ root.finishedWork = null;
+ _finishedWork = renderRoot(root, expirationTime);
+ if (_finishedWork !== null) {
+ // We've completed the root. Check the deadline one more time
+ // before committing.
+ if (!shouldYield()) {
+ // Still time left. Commit the root.
+ completeRoot(root, _finishedWork, expirationTime);
+ } else {
+ // There's no time left. Mark this root as complete. We'll come
+ // back and commit it later.
+ root.finishedWork = _finishedWork;
+ }
}
}
}
- var updatePayload = create(props, viewConfig.validAttributes);
-
- UIManager.createView(
- tag, // reactTag
- viewConfig.uiViewClassName, // viewName
- rootContainerInstance, // rootTag
- updatePayload
- );
-
- var component = new ReactNativeFiberHostComponent(tag, viewConfig);
-
- precacheFiberNode(internalInstanceHandle, tag);
- updateFiberProps(tag, props);
-
- // Not sure how to avoid this cast. Flow is okay if the component is defined
- // in the same file but if it's external it can't see the types.
- return component;
- },
- createTextInstance: function(
- text,
- rootContainerInstance,
- hostContext,
- internalInstanceHandle
- ) {
- var tag = ReactNativeTagHandles.allocateTag();
+ isRendering = false;
+ }
- UIManager.createView(
- tag, // reactTag
- "RCTRawText", // viewName
- rootContainerInstance, // rootTag
- { text: text }
- );
+ function completeRoot(root, finishedWork, expirationTime) {
+ // Check if there's a batch that matches this expiration time.
+ var firstBatch = root.firstBatch;
+ if (firstBatch !== null && firstBatch._expirationTime <= expirationTime) {
+ if (completedBatches === null) {
+ completedBatches = [firstBatch];
+ } else {
+ completedBatches.push(firstBatch);
+ }
+ if (firstBatch._defer) {
+ // This root is blocked from committing by a batch. Unschedule it until
+ // we receive another update.
+ root.finishedWork = finishedWork;
+ root.remainingExpirationTime = NoWork;
+ return;
+ }
+ }
- precacheFiberNode(internalInstanceHandle, tag);
+ // Commit the root.
+ root.finishedWork = null;
+ root.remainingExpirationTime = commitRoot(finishedWork);
+ }
- return tag;
- },
- finalizeInitialChildren: function(
- parentInstance,
- type,
- props,
- rootContainerInstance
- ) {
- // Don't send a no-op message over the bridge.
- if (parentInstance._children.length === 0) {
+ // When working on async work, the reconciler asks the renderer if it should
+ // yield execution. For DOM, we implement this with requestIdleCallback.
+ function shouldYield() {
+ if (deadline === null) {
return false;
}
+ if (deadline.timeRemaining() > timeHeuristicForUnitOfWork) {
+ // Disregard deadline.didTimeout. Only expired work should be flushed
+ // during a timeout. This path is only hit for non-expired work.
+ return false;
+ }
+ deadlineDidExpire = true;
+ return true;
+ }
- // Map from child objects to native tags.
- // Either way we need to pass a copy of the Array to prevent it from being frozen.
- var nativeTags = parentInstance._children.map(function(child) {
- return typeof child === "number"
- ? child // Leaf node (eg text)
- : child._nativeTag;
- });
-
- UIManager.setChildren(
- parentInstance._nativeTag, // containerTag
- nativeTags
+ // TODO: Not happy about this hook. Conceptually, renderRoot should return a
+ // tuple of (isReadyForCommit, didError, error)
+ function onUncaughtError(error) {
+ invariant(
+ nextFlushedRoot !== null,
+ "Should be working on a root. This error is likely caused by a bug in " +
+ "React. Please file an issue."
);
+ // Unschedule this root so we don't work on it again until there's
+ // another update.
+ nextFlushedRoot.remainingExpirationTime = NoWork;
+ if (!hasUnhandledError) {
+ hasUnhandledError = true;
+ unhandledError = error;
+ }
+ }
- return false;
- },
- getRootHostContext: function() {
- return emptyObject;
- },
- getChildHostContext: function() {
- return emptyObject;
- },
- getPublicInstance: function(instance) {
- return instance;
- },
+ // TODO: Batching should be implemented at the renderer level, not inside
+ // the reconciler.
+ function batchedUpdates(fn, a) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return fn(a);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ if (!isBatchingUpdates && !isRendering) {
+ performWork(Sync, null);
+ }
+ }
+ }
- now: now,
+ // TODO: Batching should be implemented at the renderer level, not inside
+ // the reconciler.
+ function unbatchedUpdates(fn) {
+ if (isBatchingUpdates && !isUnbatchingUpdates) {
+ isUnbatchingUpdates = true;
+ try {
+ return fn();
+ } finally {
+ isUnbatchingUpdates = false;
+ }
+ }
+ return fn();
+ }
- prepareForCommit: function() {
- // Noop
- },
- prepareUpdate: function(
- instance,
- type,
- oldProps,
- newProps,
- rootContainerInstance,
- hostContext
- ) {
- return emptyObject;
- },
- resetAfterCommit: function() {
- // Noop
- },
+ // TODO: Batching should be implemented at the renderer level, not within
+ // the reconciler.
+ function flushSync(fn) {
+ var previousIsBatchingUpdates = isBatchingUpdates;
+ isBatchingUpdates = true;
+ try {
+ return syncUpdates(fn);
+ } finally {
+ isBatchingUpdates = previousIsBatchingUpdates;
+ invariant(
+ !isRendering,
+ "flushSync was called from inside a lifecycle method. It cannot be " +
+ "called when React is already rendering."
+ );
+ performWork(Sync, null);
+ }
+ }
- scheduleDeferredCallback: scheduleDeferredCallback,
- cancelDeferredCallback: cancelDeferredCallback,
+ return {
+ computeExpirationForFiber: computeExpirationForFiber,
+ scheduleWork: scheduleWork,
+ requestWork: requestWork,
+ flushRoot: flushRoot,
+ batchedUpdates: batchedUpdates,
+ unbatchedUpdates: unbatchedUpdates,
+ flushSync: flushSync,
+ deferredUpdates: deferredUpdates,
+ computeUniqueAsyncExpiration: computeUniqueAsyncExpiration
+ };
+};
- shouldDeprioritizeSubtree: function(type, props) {
- return false;
- },
- shouldSetTextContent: function(type, props) {
- // TODO (bvaughn) Revisit this decision.
- // Always returning false simplifies the createInstance() implementation,
- // But creates an additional child Fiber for raw text children.
- // No additional native views are created though.
- // It's not clear to me which is better so I'm deferring for now.
- // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
- return false;
- },
+var didWarnAboutNestedUpdates = void 0;
- useSyncScheduling: true,
+{
+ didWarnAboutNestedUpdates = false;
+}
- mutation: {
- appendChild: function(parentInstance, child) {
- var childTag = typeof child === "number" ? child : child._nativeTag;
- var children = parentInstance._children;
- var index = children.indexOf(child);
+// 0 is PROD, 1 is DEV.
+// Might add PROFILE later.
- if (index >= 0) {
- children.splice(index, 1);
- children.push(child);
+function getContextForSubtree(parentComponent) {
+ if (!parentComponent) {
+ return emptyObject;
+ }
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerTag
- [index], // moveFromIndices
- [children.length - 1], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- []
- );
- } else {
- children.push(child);
+ var fiber = get(parentComponent);
+ var parentContext = findCurrentUnmaskedContext(fiber);
+ return isContextProvider(fiber)
+ ? processChildContext(fiber, parentContext)
+ : parentContext;
+}
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerTag
- [], // moveFromIndices
- [], // moveToIndices
- [childTag], // addChildReactTags
- [children.length - 1], // addAtIndices
- []
+var ReactFiberReconciler$1 = function(config) {
+ var getPublicInstance = config.getPublicInstance;
+
+ var _ReactFiberScheduler = ReactFiberScheduler(config),
+ computeUniqueAsyncExpiration =
+ _ReactFiberScheduler.computeUniqueAsyncExpiration,
+ computeExpirationForFiber = _ReactFiberScheduler.computeExpirationForFiber,
+ scheduleWork = _ReactFiberScheduler.scheduleWork,
+ requestWork = _ReactFiberScheduler.requestWork,
+ flushRoot = _ReactFiberScheduler.flushRoot,
+ batchedUpdates = _ReactFiberScheduler.batchedUpdates,
+ unbatchedUpdates = _ReactFiberScheduler.unbatchedUpdates,
+ flushSync = _ReactFiberScheduler.flushSync,
+ deferredUpdates = _ReactFiberScheduler.deferredUpdates;
+
+ function scheduleRootUpdate(current, element, expirationTime, callback) {
+ {
+ if (
+ ReactDebugCurrentFiber.phase === "render" &&
+ ReactDebugCurrentFiber.current !== null &&
+ !didWarnAboutNestedUpdates
+ ) {
+ didWarnAboutNestedUpdates = true;
+ warning(
+ false,
+ "Render methods should be a pure function of props and state; " +
+ "triggering nested component updates from render is not allowed. " +
+ "If necessary, trigger nested updates in componentDidUpdate.\n\n" +
+ "Check the render method of %s.",
+ getComponentName(ReactDebugCurrentFiber.current) || "Unknown"
);
}
- },
- appendChildToContainer: function(parentInstance, child) {
- var childTag = typeof child === "number" ? child : child._nativeTag;
- UIManager.setChildren(
- parentInstance, // containerTag
- [childTag]
- );
- },
- commitTextUpdate: function(textInstance, oldText, newText) {
- UIManager.updateView(
- textInstance, // reactTag
- "RCTRawText", // viewName
- { text: newText }
+ }
+
+ callback = callback === undefined ? null : callback;
+ {
+ warning(
+ callback === null || typeof callback === "function",
+ "render(...): Expected the last optional `callback` argument to be a " +
+ "function. Instead received: %s.",
+ callback
);
- },
- commitMount: function(instance, type, newProps, internalInstanceHandle) {
- // Noop
- },
- commitUpdate: function(
- instance,
- updatePayloadTODO,
- type,
- oldProps,
- newProps,
- internalInstanceHandle
- ) {
- var viewConfig = instance.viewConfig;
+ }
- updateFiberProps(instance._nativeTag, newProps);
+ var update = {
+ expirationTime: expirationTime,
+ partialState: { element: element },
+ callback: callback,
+ isReplace: false,
+ isForced: false,
+ next: null
+ };
+ insertUpdateIntoFiber(current, update);
+ scheduleWork(current, expirationTime);
- var updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
+ return expirationTime;
+ }
- // Avoid the overhead of bridge calls if there's no update.
- // This is an expensive no-op for Android, and causes an unnecessary
- // view invalidation for certain components (eg RCTTextInput) on iOS.
- if (updatePayload != null) {
- UIManager.updateView(
- instance._nativeTag, // reactTag
- viewConfig.uiViewClassName, // viewName
- updatePayload
- );
+ function updateContainerAtExpirationTime(
+ element,
+ container,
+ parentComponent,
+ expirationTime,
+ callback
+ ) {
+ // TODO: If this is a nested container, this won't be the root.
+ var current = container.current;
+
+ {
+ if (ReactFiberInstrumentation_1.debugTool) {
+ if (current.alternate === null) {
+ ReactFiberInstrumentation_1.debugTool.onMountContainer(container);
+ } else if (element === null) {
+ ReactFiberInstrumentation_1.debugTool.onUnmountContainer(container);
+ } else {
+ ReactFiberInstrumentation_1.debugTool.onUpdateContainer(container);
+ }
}
- },
- insertBefore: function(parentInstance, child, beforeChild) {
- var children = parentInstance._children;
- var index = children.indexOf(child);
+ }
- // Move existing child or add new child?
- if (index >= 0) {
- children.splice(index, 1);
- var beforeChildIndex = children.indexOf(beforeChild);
- children.splice(beforeChildIndex, 0, child);
+ var context = getContextForSubtree(parentComponent);
+ if (container.context === null) {
+ container.context = context;
+ } else {
+ container.pendingContext = context;
+ }
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerID
- [index], // moveFromIndices
- [beforeChildIndex], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- []
- );
- } else {
- var _beforeChildIndex = children.indexOf(beforeChild);
- children.splice(_beforeChildIndex, 0, child);
+ return scheduleRootUpdate(current, element, expirationTime, callback);
+ }
- var childTag = typeof child === "number" ? child : child._nativeTag;
+ function findHostInstance(fiber) {
+ var hostFiber = findCurrentHostFiber(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+ }
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [childTag], // addChildReactTags
- [_beforeChildIndex], // addAtIndices
- []
- );
- }
+ return {
+ createContainer: function(containerInfo, isAsync, hydrate) {
+ return createFiberRoot(containerInfo, isAsync, hydrate);
},
- insertInContainerBefore: function(parentInstance, child, beforeChild) {
- // TODO (bvaughn): Remove this check when...
- // We create a wrapper object for the container in ReactNative render()
- // Or we refactor to remove wrapper objects entirely.
- // For more info on pros/cons see PR #8560 description.
- invariant(
- typeof parentInstance !== "number",
- "Container does not support insertBefore operation"
+ updateContainer: function(element, container, parentComponent, callback) {
+ var current = container.current;
+ var expirationTime = computeExpirationForFiber(current);
+ return updateContainerAtExpirationTime(
+ element,
+ container,
+ parentComponent,
+ expirationTime,
+ callback
);
},
- removeChild: function(parentInstance, child) {
- recursivelyUncacheFiberNode(child);
- var children = parentInstance._children;
- var index = children.indexOf(child);
- children.splice(index, 1);
+ updateContainerAtExpirationTime: updateContainerAtExpirationTime,
- UIManager.manageChildren(
- parentInstance._nativeTag, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [index]
- );
- },
- removeChildFromContainer: function(parentInstance, child) {
- recursivelyUncacheFiberNode(child);
- UIManager.manageChildren(
- parentInstance, // containerID
- [], // moveFromIndices
- [], // moveToIndices
- [], // addChildReactTags
- [], // addAtIndices
- [0]
- );
- },
- resetTextContent: function(instance) {
- // Noop
- }
- }
-});
+ flushRoot: flushRoot,
-/**
- * ReactNative vs ReactWeb
- * -----------------------
- * React treats some pieces of data opaquely. This means that the information
- * is first class (it can be passed around), but cannot be inspected. This
- * allows us to build infrastructure that reasons about resources, without
- * making assumptions about the nature of those resources, and this allows that
- * infra to be shared across multiple platforms, where the resources are very
- * different. General infra (such as `ReactMultiChild`) reasons opaquely about
- * the data, but platform specific code (such as `ReactNativeBaseComponent`) can
- * make assumptions about the data.
- *
- *
- * `rootNodeID`, uniquely identifies a position in the generated native view
- * tree. Many layers of composite components (created with `React.createClass`)
- * can all share the same `rootNodeID`.
- *
- * `nodeHandle`: A sufficiently unambiguous way to refer to a lower level
- * resource (dom node, native view etc). The `rootNodeID` is sufficient for web
- * `nodeHandle`s, because the position in a tree is always enough to uniquely
- * identify a DOM node (we never have nodes in some bank outside of the
- * document). The same would be true for `ReactNative`, but we must maintain a
- * mapping that we can send efficiently serializable
- * strings across native boundaries.
- *
- * Opaque name TodaysWebReact FutureWebWorkerReact ReactNative
- * ----------------------------------------------------------------------------
- * nodeHandle N/A rootNodeID tag
- */
+ requestWork: requestWork,
-// TODO (bvaughn) Rename the findNodeHandle module to something more descriptive
-// eg findInternalHostInstance. This will reduce the likelihood of someone
-// accidentally deep-requiring this version.
-function findNodeHandle(componentOrHandle) {
- {
- var owner = ReactCurrentOwner.current;
- if (owner !== null && owner.stateNode !== null) {
- warning(
- owner.stateNode._warnedAboutRefsInRender,
- "%s is accessing findNodeHandle inside its render(). " +
- "render() should be a pure function of props and state. It should " +
- "never access something that requires stale data from the previous " +
- "render, such as refs. Move this logic to componentDidMount and " +
- "componentDidUpdate instead.",
- getComponentName(owner) || "A component"
- );
+ computeUniqueAsyncExpiration: computeUniqueAsyncExpiration,
+
+ batchedUpdates: batchedUpdates,
+
+ unbatchedUpdates: unbatchedUpdates,
+
+ deferredUpdates: deferredUpdates,
+
+ flushSync: flushSync,
+
+ getPublicRootInstance: function(container) {
+ var containerFiber = container.current;
+ if (!containerFiber.child) {
+ return null;
+ }
+ switch (containerFiber.child.tag) {
+ case HostComponent:
+ return getPublicInstance(containerFiber.child.stateNode);
+ default:
+ return containerFiber.child.stateNode;
+ }
+ },
- owner.stateNode._warnedAboutRefsInRender = true;
- }
- }
- if (componentOrHandle == null) {
- return null;
- }
- if (typeof componentOrHandle === "number") {
- // Already a node handle
- return componentOrHandle;
- }
+ findHostInstance: findHostInstance,
- var component = componentOrHandle;
+ findHostInstanceWithNoPortals: function(fiber) {
+ var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
+ if (hostFiber === null) {
+ return null;
+ }
+ return hostFiber.stateNode;
+ },
+ injectIntoDevTools: function(devToolsConfig) {
+ var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
- // TODO (balpert): Wrap iOS native components in a composite wrapper, then
- // ReactInstanceMap.get here will always succeed for mounted components
- var internalInstance = get(component);
- if (internalInstance) {
- return NativeRenderer.findHostInstance(internalInstance);
- } else {
- if (component) {
- return component;
- } else {
- invariant(
- // Native
- (typeof component === "object" && "_nativeTag" in component) ||
- // Composite
- (component.render != null && typeof component.render === "function"),
- "findNodeHandle(...): Argument is not a component " +
- "(type: %s, keys: %s)",
- typeof component,
- Object.keys(component)
- );
- invariant(
- false,
- "findNodeHandle(...): Unable to find node handle for unmounted " +
- "component."
+ return injectInternals(
+ Object.assign({}, devToolsConfig, {
+ findHostInstanceByFiber: function(fiber) {
+ return findHostInstance(fiber);
+ },
+ findFiberByHostInstance: function(instance) {
+ if (!findFiberByHostInstance) {
+ // Might not be implemented by the renderer.
+ return null;
+ }
+ return findFiberByHostInstance(instance);
+ }
+ })
);
}
- }
+ };
+};
+
+var ReactFiberReconciler$2 = Object.freeze({
+ default: ReactFiberReconciler$1
+});
+
+var ReactFiberReconciler$3 =
+ (ReactFiberReconciler$2 && ReactFiberReconciler$1) || ReactFiberReconciler$2;
+
+// TODO: bundle Flow types with the package.
+
+// TODO: decide on the top-level export form.
+// This is hacky but makes it work with both Rollup and Jest.
+var reactReconciler = ReactFiberReconciler$3["default"]
+ ? ReactFiberReconciler$3["default"]
+ : ReactFiberReconciler$3;
+
+var viewConfigCallbacks = new Map();
+var viewConfigs = new Map();
+
+/**
+ * Registers a native view/component by name.
+ * A callback is provided to load the view config from UIManager.
+ * The callback is deferred until the view is actually rendered.
+ * This is done to avoid causing Prepack deopts.
+ */
+function register(name, callback) {
+ invariant(
+ !viewConfigCallbacks.has(name),
+ "Tried to register two views with the same name %s",
+ name
+ );
+ viewConfigCallbacks.set(name, callback);
+ return name;
}
/**
- * External users of findNodeHandle() expect the host tag number return type.
- * The injected findNodeHandle() strategy returns the instance wrapper though.
- * See NativeMethodsMixin#setNativeProps for more info on why this is done.
+ * Retrieves a config for the specified view.
+ * If this is the first time the view has been used,
+ * This configuration will be lazy-loaded from UIManager.
*/
-function findNumericNodeHandleFiber(componentOrHandle) {
- var instance = findNodeHandle(componentOrHandle);
- if (instance == null || typeof instance === "number") {
- return instance;
+function get$1(name) {
+ var viewConfig = void 0;
+ if (!viewConfigs.has(name)) {
+ var callback = viewConfigCallbacks.get(name);
+ invariant(
+ typeof callback === "function",
+ "View config not found for name %s",
+ name
+ );
+ viewConfigCallbacks.set(name, null);
+ viewConfig = callback();
+ viewConfigs.set(name, viewConfig);
+ } else {
+ viewConfig = viewConfigs.get(name);
+ }
+ invariant(viewConfig, "View config not found for name %s", name);
+ return viewConfig;
+}
+
+function _classCallCheck$2(instance, Constructor) {
+ if (!(instance instanceof Constructor)) {
+ throw new TypeError("Cannot call a class as a function");
}
- return instance._nativeTag;
}
// Modules provided by RN:
/**
- * `NativeMethodsMixin` provides methods to access the underlying native
- * component directly. This can be useful in cases when you want to focus
- * a view or measure its on-screen dimensions, for example.
- *
- * The methods described here are available on most of the default components
- * provided by React Native. Note, however, that they are *not* available on
- * composite components that aren't directly backed by a native view. This will
- * generally include most components that you define in your own app. For more
- * information, see [Direct
- * Manipulation](docs/direct-manipulation.html).
- *
- * Note the Flow $Exact<> syntax is required to support mixins.
- * React createClass mixins can only be used with exact types.
+ * This component defines the same methods as NativeMethodsMixin but without the
+ * findNodeHandle wrapper. This wrapper is unnecessary for HostComponent views
+ * and would also result in a circular require.js dependency (since
+ * ReactNativeFiber depends on this component and NativeMethodsMixin depends on
+ * ReactNativeFiber).
*/
-var NativeMethodsMixin = {
- /**
- * Determines the location on screen, width, and height of the given view and
- * returns the values via an async callback. If successful, the callback will
- * be called with the following arguments:
- *
- * - x
- * - y
- * - width
- * - height
- * - pageX
- * - pageY
- *
- * Note that these measurements are not available until after the rendering
- * has been completed in native. If you need the measurements as soon as
- * possible, consider using the [`onLayout`
- * prop](docs/view.html#onlayout) instead.
- */
- measure: function(callback) {
- UIManager.measure(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
- },
- /**
- * Determines the location of the given view in the window and returns the
- * values via an async callback. If the React root view is embedded in
- * another native view, this will give you the absolute coordinates. If
- * successful, the callback will be called with the following
- * arguments:
- *
- * - x
- * - y
- * - width
- * - height
- *
- * Note that these measurements are not available until after the rendering
- * has been completed in native.
- */
- measureInWindow: function(callback) {
+var ReactNativeFiberHostComponent = (function() {
+ function ReactNativeFiberHostComponent(tag, viewConfig) {
+ _classCallCheck$2(this, ReactNativeFiberHostComponent);
+
+ this._nativeTag = tag;
+ this._children = [];
+ this.viewConfig = viewConfig;
+ }
+
+ ReactNativeFiberHostComponent.prototype.blur = function blur() {
+ TextInputState.blurTextInput(this._nativeTag);
+ };
+
+ ReactNativeFiberHostComponent.prototype.focus = function focus() {
+ TextInputState.focusTextInput(this._nativeTag);
+ };
+
+ ReactNativeFiberHostComponent.prototype.measure = function measure(callback) {
+ UIManager.measure(this._nativeTag, mountSafeCallback(this, callback));
+ };
+
+ ReactNativeFiberHostComponent.prototype.measureInWindow = function measureInWindow(
+ callback
+ ) {
UIManager.measureInWindow(
- findNumericNodeHandleFiber(this),
+ this._nativeTag,
mountSafeCallback(this, callback)
);
- },
+ };
- /**
- * Like [`measure()`](#measure), but measures the view relative an ancestor,
- * specified as `relativeToNativeNode`. This means that the returned x, y
- * are relative to the origin x, y of the ancestor view.
- *
- * As always, to obtain a native node handle for a component, you can use
- * `findNumericNodeHandle(component)`.
- */
- measureLayout: function(
+ ReactNativeFiberHostComponent.prototype.measureLayout = function measureLayout(
relativeToNativeNode,
onSuccess,
onFail /* currently unused */
) {
UIManager.measureLayout(
- findNumericNodeHandleFiber(this),
+ this._nativeTag,
relativeToNativeNode,
mountSafeCallback(this, onFail),
mountSafeCallback(this, onSuccess)
);
- },
-
- /**
- * This function sends props straight to native. They will not participate in
- * future diff process - this means that if you do not include them in the
- * next render, they will remain active (see [Direct
- * Manipulation](docs/direct-manipulation.html)).
- */
- setNativeProps: function(nativeProps) {
- // Class components don't have viewConfig -> validateAttributes.
- // Nor does it make sense to set native props on a non-native component.
- // Instead, find the nearest host component and set props on it.
- // Use findNodeHandle() rather than findNumericNodeHandle() because
- // We want the instance/wrapper (not the native tag).
- var maybeInstance = void 0;
-
- // Fiber errors if findNodeHandle is called for an umounted component.
- // Tests using ReactTestRenderer will trigger this case indirectly.
- // Mimicking stack behavior, we should silently ignore this case.
- // TODO Fix ReactTestRenderer so we can remove this try/catch.
- try {
- maybeInstance = findNodeHandle(this);
- } catch (error) {}
-
- // If there is no host component beneath this we should fail silently.
- // This is not an error; it could mean a class component rendered null.
- if (maybeInstance == null) {
- return;
- }
-
- var viewConfig = maybeInstance.viewConfig;
+ };
+ ReactNativeFiberHostComponent.prototype.setNativeProps = function setNativeProps(
+ nativeProps
+ ) {
{
- warnForStyleProps(nativeProps, viewConfig.validAttributes);
+ warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
}
- var updatePayload = create(nativeProps, viewConfig.validAttributes);
+ var updatePayload = create(nativeProps, this.viewConfig.validAttributes);
// Avoid the overhead of bridge calls if there's no update.
// This is an expensive no-op for Android, and causes an unnecessary
// view invalidation for certain components (eg RCTTextInput) on iOS.
if (updatePayload != null) {
UIManager.updateView(
- maybeInstance._nativeTag,
- viewConfig.uiViewClassName,
+ this._nativeTag,
+ this.viewConfig.uiViewClassName,
updatePayload
);
}
- },
+ };
- /**
- * Requests focus for the given input or view. The exact behavior triggered
- * will depend on the platform and type of view.
- */
- focus: function() {
- TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
- },
+ return ReactNativeFiberHostComponent;
+})();
- /**
- * Removes focus from an input or view. This is the opposite of `focus()`.
- */
- blur: function() {
- TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
+var hasNativePerformanceNow =
+ typeof performance === "object" && typeof performance.now === "function";
+
+var now = hasNativePerformanceNow
+ ? function() {
+ return performance.now();
+ }
+ : function() {
+ return Date.now();
+ };
+
+var scheduledCallback = null;
+var frameDeadline = 0;
+
+var frameDeadlineObject = {
+ timeRemaining: function() {
+ return frameDeadline - now();
}
};
-{
- // hide this from Flow since we can't define these properties outside of
- // true without actually implementing them (setting them to undefined
- // isn't allowed by ReactClass)
- var NativeMethodsMixin_DEV = NativeMethodsMixin;
- invariant(
- !NativeMethodsMixin_DEV.componentWillMount &&
- !NativeMethodsMixin_DEV.componentWillReceiveProps,
- "Do not override existing functions."
- );
- NativeMethodsMixin_DEV.componentWillMount = function() {
- throwOnStylesProp(this, this.props);
- };
- NativeMethodsMixin_DEV.componentWillReceiveProps = function(newProps) {
- throwOnStylesProp(this, newProps);
- };
-}
+function setTimeoutCallback() {
+ // TODO (bvaughn) Hard-coded 5ms unblocks initial async testing.
+ // React API probably changing to boolean rather than time remaining.
+ // Longer-term plan is to rewrite this using shared memory,
+ // And just return the value of the bit as the boolean.
+ frameDeadline = now() + 5;
-function _classCallCheck$2(instance, Constructor) {
- if (!(instance instanceof Constructor)) {
- throw new TypeError("Cannot call a class as a function");
+ var callback = scheduledCallback;
+ scheduledCallback = null;
+ if (callback !== null) {
+ callback(frameDeadlineObject);
}
}
-function _possibleConstructorReturn(self, call) {
- if (!self) {
- throw new ReferenceError(
- "this hasn't been initialised - super() hasn't been called"
- );
- }
- return call && (typeof call === "object" || typeof call === "function")
- ? call
- : self;
+// RN has a poor polyfill for requestIdleCallback so we aren't using it.
+// This implementation is only intended for short-term use anyway.
+// We also don't implement cancel functionality b'c Fiber doesn't currently need it.
+function scheduleDeferredCallback(callback) {
+ // We assume only one callback is scheduled at a time b'c that's how Fiber works.
+ scheduledCallback = callback;
+ return setTimeout(setTimeoutCallback, 1);
}
-function _inherits(subClass, superClass) {
- if (typeof superClass !== "function" && superClass !== null) {
- throw new TypeError(
- "Super expression must either be null or a function, not " +
- typeof superClass
- );
- }
- subClass.prototype = Object.create(superClass && superClass.prototype, {
- constructor: {
- value: subClass,
- enumerable: false,
- writable: true,
- configurable: true
- }
- });
- if (superClass)
- Object.setPrototypeOf
- ? Object.setPrototypeOf(subClass, superClass)
- : (subClass.__proto__ = superClass);
+function cancelDeferredCallback(callbackID) {
+ scheduledCallback = null;
+ clearTimeout(callbackID);
}
// Modules provided by RN:
-/**
- * Superclass that provides methods to access the underlying native component.
- * This can be useful when you want to focus a view or measure its dimensions.
- *
- * Methods implemented by this class are available on most default components
- * provided by React Native. However, they are *not* available on composite
- * components that are not directly backed by a native view. For more
- * information, see [Direct Manipulation](docs/direct-manipulation.html).
- *
- * @abstract
- */
+function recursivelyUncacheFiberNode(node) {
+ if (typeof node === "number") {
+ // Leaf node (eg text)
+ uncacheFiberNode(node);
+ } else {
+ uncacheFiberNode(node._nativeTag);
-var ReactNativeComponent = (function(_React$Component) {
- _inherits(ReactNativeComponent, _React$Component);
+ node._children.forEach(recursivelyUncacheFiberNode);
+ }
+}
- function ReactNativeComponent() {
- _classCallCheck$2(this, ReactNativeComponent);
+var NativeRenderer = reactReconciler({
+ appendInitialChild: function(parentInstance, child) {
+ parentInstance._children.push(child);
+ },
+ createInstance: function(
+ type,
+ props,
+ rootContainerInstance,
+ hostContext,
+ internalInstanceHandle
+ ) {
+ var tag = ReactNativeTagHandles.allocateTag();
+ var viewConfig = get$1(type);
- return _possibleConstructorReturn(
- this,
- _React$Component.apply(this, arguments)
+ {
+ for (var key in viewConfig.validAttributes) {
+ if (props.hasOwnProperty(key)) {
+ deepFreezeAndThrowOnMutationInDev(props[key]);
+ }
+ }
+ }
+
+ var updatePayload = create(props, viewConfig.validAttributes);
+
+ UIManager.createView(
+ tag, // reactTag
+ viewConfig.uiViewClassName, // viewName
+ rootContainerInstance, // rootTag
+ updatePayload
);
- }
- /**
- * Removes focus. This is the opposite of `focus()`.
- */
- ReactNativeComponent.prototype.blur = function blur() {
- TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
- };
+ var component = new ReactNativeFiberHostComponent(tag, viewConfig);
- /**
- * Requests focus. The exact behavior depends on the platform and view.
- */
+ precacheFiberNode(internalInstanceHandle, tag);
+ updateFiberProps(tag, props);
+
+ // Not sure how to avoid this cast. Flow is okay if the component is defined
+ // in the same file but if it's external it can't see the types.
+ return component;
+ },
+ createTextInstance: function(
+ text,
+ rootContainerInstance,
+ hostContext,
+ internalInstanceHandle
+ ) {
+ var tag = ReactNativeTagHandles.allocateTag();
+
+ UIManager.createView(
+ tag, // reactTag
+ "RCTRawText", // viewName
+ rootContainerInstance, // rootTag
+ { text: text }
+ );
+
+ precacheFiberNode(internalInstanceHandle, tag);
+
+ return tag;
+ },
+ finalizeInitialChildren: function(
+ parentInstance,
+ type,
+ props,
+ rootContainerInstance
+ ) {
+ // Don't send a no-op message over the bridge.
+ if (parentInstance._children.length === 0) {
+ return false;
+ }
+
+ // Map from child objects to native tags.
+ // Either way we need to pass a copy of the Array to prevent it from being frozen.
+ var nativeTags = parentInstance._children.map(function(child) {
+ return typeof child === "number"
+ ? child // Leaf node (eg text)
+ : child._nativeTag;
+ });
+
+ UIManager.setChildren(
+ parentInstance._nativeTag, // containerTag
+ nativeTags
+ );
+
+ return false;
+ },
+ getRootHostContext: function() {
+ return emptyObject;
+ },
+ getChildHostContext: function() {
+ return emptyObject;
+ },
+ getPublicInstance: function(instance) {
+ return instance;
+ },
+
+ now: now,
+
+ prepareForCommit: function() {
+ // Noop
+ },
+ prepareUpdate: function(
+ instance,
+ type,
+ oldProps,
+ newProps,
+ rootContainerInstance,
+ hostContext
+ ) {
+ return emptyObject;
+ },
+ resetAfterCommit: function() {
+ // Noop
+ },
- ReactNativeComponent.prototype.focus = function focus() {
- TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
- };
+ scheduleDeferredCallback: scheduleDeferredCallback,
+ cancelDeferredCallback: cancelDeferredCallback,
- /**
- * Measures the on-screen location and dimensions. If successful, the callback
- * will be called asynchronously with the following arguments:
- *
- * - x
- * - y
- * - width
- * - height
- * - pageX
- * - pageY
- *
- * These values are not available until after natives rendering completes. If
- * you need the measurements as soon as possible, consider using the
- * [`onLayout` prop](docs/view.html#onlayout) instead.
- */
+ shouldDeprioritizeSubtree: function(type, props) {
+ return false;
+ },
+ shouldSetTextContent: function(type, props) {
+ // TODO (bvaughn) Revisit this decision.
+ // Always returning false simplifies the createInstance() implementation,
+ // But creates an additional child Fiber for raw text children.
+ // No additional native views are created though.
+ // It's not clear to me which is better so I'm deferring for now.
+ // More context @ github.com/facebook/react/pull/8560#discussion_r92111303
+ return false;
+ },
- ReactNativeComponent.prototype.measure = function measure(callback) {
- UIManager.measure(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
- };
+ mutation: {
+ appendChild: function(parentInstance, child) {
+ var childTag = typeof child === "number" ? child : child._nativeTag;
+ var children = parentInstance._children;
+ var index = children.indexOf(child);
- /**
- * Measures the on-screen location and dimensions. Even if the React Native
- * root view is embedded within another native view, this method will give you
- * the absolute coordinates measured from the window. If successful, the
- * callback will be called asynchronously with the following arguments:
- *
- * - x
- * - y
- * - width
- * - height
- *
- * These values are not available until after natives rendering completes.
- */
+ if (index >= 0) {
+ children.splice(index, 1);
+ children.push(child);
- ReactNativeComponent.prototype.measureInWindow = function measureInWindow(
- callback
- ) {
- UIManager.measureInWindow(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
- };
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerTag
+ [index], // moveFromIndices
+ [children.length - 1], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ []
+ );
+ } else {
+ children.push(child);
- /**
- * Similar to [`measure()`](#measure), but the resulting location will be
- * relative to the supplied ancestor's location.
- *
- * Obtain a native node handle with `ReactNative.findNodeHandle(component)`.
- */
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerTag
+ [], // moveFromIndices
+ [], // moveToIndices
+ [childTag], // addChildReactTags
+ [children.length - 1], // addAtIndices
+ []
+ );
+ }
+ },
+ appendChildToContainer: function(parentInstance, child) {
+ var childTag = typeof child === "number" ? child : child._nativeTag;
+ UIManager.setChildren(
+ parentInstance, // containerTag
+ [childTag]
+ );
+ },
+ commitTextUpdate: function(textInstance, oldText, newText) {
+ UIManager.updateView(
+ textInstance, // reactTag
+ "RCTRawText", // viewName
+ { text: newText }
+ );
+ },
+ commitMount: function(instance, type, newProps, internalInstanceHandle) {
+ // Noop
+ },
+ commitUpdate: function(
+ instance,
+ updatePayloadTODO,
+ type,
+ oldProps,
+ newProps,
+ internalInstanceHandle
+ ) {
+ var viewConfig = instance.viewConfig;
- ReactNativeComponent.prototype.measureLayout = function measureLayout(
- relativeToNativeNode,
- onSuccess,
- onFail /* currently unused */
- ) {
- UIManager.measureLayout(
- findNumericNodeHandleFiber(this),
- relativeToNativeNode,
- mountSafeCallback(this, onFail),
- mountSafeCallback(this, onSuccess)
- );
- };
+ updateFiberProps(instance._nativeTag, newProps);
- /**
- * This function sends props straight to native. They will not participate in
- * future diff process - this means that if you do not include them in the
- * next render, they will remain active (see [Direct
- * Manipulation](docs/direct-manipulation.html)).
- */
+ var updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
- ReactNativeComponent.prototype.setNativeProps = function setNativeProps(
- nativeProps
- ) {
- // Class components don't have viewConfig -> validateAttributes.
- // Nor does it make sense to set native props on a non-native component.
- // Instead, find the nearest host component and set props on it.
- // Use findNodeHandle() rather than ReactNative.findNodeHandle() because
- // We want the instance/wrapper (not the native tag).
- var maybeInstance = void 0;
+ // Avoid the overhead of bridge calls if there's no update.
+ // This is an expensive no-op for Android, and causes an unnecessary
+ // view invalidation for certain components (eg RCTTextInput) on iOS.
+ if (updatePayload != null) {
+ UIManager.updateView(
+ instance._nativeTag, // reactTag
+ viewConfig.uiViewClassName, // viewName
+ updatePayload
+ );
+ }
+ },
+ insertBefore: function(parentInstance, child, beforeChild) {
+ var children = parentInstance._children;
+ var index = children.indexOf(child);
- // Fiber errors if findNodeHandle is called for an umounted component.
- // Tests using ReactTestRenderer will trigger this case indirectly.
- // Mimicking stack behavior, we should silently ignore this case.
- // TODO Fix ReactTestRenderer so we can remove this try/catch.
- try {
- maybeInstance = findNodeHandle(this);
- } catch (error) {}
+ // Move existing child or add new child?
+ if (index >= 0) {
+ children.splice(index, 1);
+ var beforeChildIndex = children.indexOf(beforeChild);
+ children.splice(beforeChildIndex, 0, child);
- // If there is no host component beneath this we should fail silently.
- // This is not an error; it could mean a class component rendered null.
- if (maybeInstance == null) {
- return;
- }
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerID
+ [index], // moveFromIndices
+ [beforeChildIndex], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ []
+ );
+ } else {
+ var _beforeChildIndex = children.indexOf(beforeChild);
+ children.splice(_beforeChildIndex, 0, child);
- var viewConfig = maybeInstance.viewConfig;
+ var childTag = typeof child === "number" ? child : child._nativeTag;
- var updatePayload = create(nativeProps, viewConfig.validAttributes);
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [childTag], // addChildReactTags
+ [_beforeChildIndex], // addAtIndices
+ []
+ );
+ }
+ },
+ insertInContainerBefore: function(parentInstance, child, beforeChild) {
+ // TODO (bvaughn): Remove this check when...
+ // We create a wrapper object for the container in ReactNative render()
+ // Or we refactor to remove wrapper objects entirely.
+ // For more info on pros/cons see PR #8560 description.
+ invariant(
+ typeof parentInstance !== "number",
+ "Container does not support insertBefore operation"
+ );
+ },
+ removeChild: function(parentInstance, child) {
+ recursivelyUncacheFiberNode(child);
+ var children = parentInstance._children;
+ var index = children.indexOf(child);
- // Avoid the overhead of bridge calls if there's no update.
- // This is an expensive no-op for Android, and causes an unnecessary
- // view invalidation for certain components (eg RCTTextInput) on iOS.
- if (updatePayload != null) {
- UIManager.updateView(
- maybeInstance._nativeTag,
- viewConfig.uiViewClassName,
- updatePayload
+ children.splice(index, 1);
+
+ UIManager.manageChildren(
+ parentInstance._nativeTag, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [index]
+ );
+ },
+ removeChildFromContainer: function(parentInstance, child) {
+ recursivelyUncacheFiberNode(child);
+ UIManager.manageChildren(
+ parentInstance, // containerID
+ [], // moveFromIndices
+ [], // moveToIndices
+ [], // addChildReactTags
+ [], // addAtIndices
+ [0]
);
+ },
+ resetTextContent: function(instance) {
+ // Noop
}
- };
-
- return ReactNativeComponent;
-})(React.Component);
+ }
+});
// Module provided by RN:
var getInspectorDataForViewTag = void 0;
@@ -12892,16 +13657,13 @@ function takeSnapshot(view, options) {
return UIManager.__takeSnapshot(view, options);
}
-// TODO: direct imports like some-package/src/* are bad. Fix me.
// Module provided by RN:
+injectFindHostInstance(NativeRenderer.findHostInstance);
+
injection$2.injectFiberBatchedUpdates(NativeRenderer.batchedUpdates);
var roots = new Map();
-// Intercept lifecycle errors and ensure they are shown with the correct stack
-// trace within the native redbox component.
-injection$4.injectDialog(showDialog$1);
-
var ReactNativeRenderer = {
NativeComponent: ReactNativeComponent,
@@ -12913,7 +13675,7 @@ var ReactNativeRenderer = {
if (!root) {
// TODO (bvaughn): If we decide to keep the wrapper component,
// We could create a wrapper for containerTag as well to reduce special casing.
- root = NativeRenderer.createContainer(containerTag, false);
+ root = NativeRenderer.createContainer(containerTag, false, false);
roots.set(containerTag, root);
}
NativeRenderer.updateContainer(element, root, null, callback);
diff --git a/Libraries/Renderer/ReactNativeRenderer-prod.js b/Libraries/Renderer/ReactNativeRenderer-prod.js
index a7497b75b30432..62540b0c53b073 100644
--- a/Libraries/Renderer/ReactNativeRenderer-prod.js
+++ b/Libraries/Renderer/ReactNativeRenderer-prod.js
@@ -16,78 +16,68 @@ var invariant = require("fbjs/lib/invariant"),
RCTEventEmitter = require("RCTEventEmitter"),
UIManager = require("UIManager"),
React = require("react"),
- ExceptionsManager = require("ExceptionsManager"),
TextInputState = require("TextInputState"),
deepDiffer = require("deepDiffer"),
flattenStyle = require("flattenStyle"),
emptyObject = require("fbjs/lib/emptyObject"),
shallowEqual = require("fbjs/lib/shallowEqual"),
- ReactErrorUtils = {
- _caughtError: null,
- _hasCaughtError: !1,
- _rethrowError: null,
- _hasRethrowError: !1,
- injection: {
- injectErrorUtils: function(injectedErrorUtils) {
- invariant(
- "function" === typeof injectedErrorUtils.invokeGuardedCallback,
- "Injected invokeGuardedCallback() must be a function."
- );
- invokeGuardedCallback = injectedErrorUtils.invokeGuardedCallback;
- }
- },
- invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) {
- invokeGuardedCallback.apply(ReactErrorUtils, arguments);
- },
- invokeGuardedCallbackAndCatchFirstError: function(
- name,
- func,
- context,
- a,
- b,
- c,
- d,
- e,
- f
- ) {
- ReactErrorUtils.invokeGuardedCallback.apply(this, arguments);
- if (ReactErrorUtils.hasCaughtError()) {
- var error = ReactErrorUtils.clearCaughtError();
- ReactErrorUtils._hasRethrowError ||
- ((ReactErrorUtils._hasRethrowError = !0),
- (ReactErrorUtils._rethrowError = error));
- }
- },
- rethrowCaughtError: function() {
- return rethrowCaughtError.apply(ReactErrorUtils, arguments);
- },
- hasCaughtError: function() {
- return ReactErrorUtils._hasCaughtError;
- },
- clearCaughtError: function() {
- if (ReactErrorUtils._hasCaughtError) {
- var error = ReactErrorUtils._caughtError;
- ReactErrorUtils._caughtError = null;
- ReactErrorUtils._hasCaughtError = !1;
- return error;
- }
- invariant(
- !1,
- "clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue."
- );
- }
- };
+ ExceptionsManager = require("ExceptionsManager");
function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
- ReactErrorUtils._hasCaughtError = !1;
- ReactErrorUtils._caughtError = null;
+ this._hasCaughtError = !1;
+ this._caughtError = null;
var funcArgs = Array.prototype.slice.call(arguments, 3);
try {
func.apply(context, funcArgs);
} catch (error) {
- (ReactErrorUtils._caughtError = error),
- (ReactErrorUtils._hasCaughtError = !0);
+ (this._caughtError = error), (this._hasCaughtError = !0);
}
}
+var ReactErrorUtils = {
+ _caughtError: null,
+ _hasCaughtError: !1,
+ _rethrowError: null,
+ _hasRethrowError: !1,
+ invokeGuardedCallback: function(name, func, context, a, b, c, d, e, f) {
+ invokeGuardedCallback.apply(ReactErrorUtils, arguments);
+ },
+ invokeGuardedCallbackAndCatchFirstError: function(
+ name,
+ func,
+ context,
+ a,
+ b,
+ c,
+ d,
+ e,
+ f
+ ) {
+ ReactErrorUtils.invokeGuardedCallback.apply(this, arguments);
+ if (ReactErrorUtils.hasCaughtError()) {
+ var error = ReactErrorUtils.clearCaughtError();
+ ReactErrorUtils._hasRethrowError ||
+ ((ReactErrorUtils._hasRethrowError = !0),
+ (ReactErrorUtils._rethrowError = error));
+ }
+ },
+ rethrowCaughtError: function() {
+ return rethrowCaughtError.apply(ReactErrorUtils, arguments);
+ },
+ hasCaughtError: function() {
+ return ReactErrorUtils._hasCaughtError;
+ },
+ clearCaughtError: function() {
+ if (ReactErrorUtils._hasCaughtError) {
+ var error = ReactErrorUtils._caughtError;
+ ReactErrorUtils._caughtError = null;
+ ReactErrorUtils._hasCaughtError = !1;
+ return error;
+ }
+ invariant(
+ !1,
+ "clearCaughtError was called but no error was captured. This error is likely caused by a bug in React. Please file an issue."
+ );
+ }
+};
function rethrowCaughtError() {
if (ReactErrorUtils._hasRethrowError) {
var error = ReactErrorUtils._rethrowError;
@@ -446,16 +436,21 @@ Object.assign(SyntheticEvent.prototype, {
}
});
SyntheticEvent.Interface = EventInterface;
-SyntheticEvent.augmentClass = function(Class, Interface) {
+SyntheticEvent.extend = function(Interface) {
function E() {}
- E.prototype = this.prototype;
+ function Class() {
+ return Super.apply(this, arguments);
+ }
+ var Super = this;
+ E.prototype = Super.prototype;
var prototype = new E();
Object.assign(prototype, Class.prototype);
Class.prototype = prototype;
Class.prototype.constructor = Class;
- Class.Interface = Object.assign({}, this.Interface, Interface);
- Class.augmentClass = this.augmentClass;
+ Class.Interface = Object.assign({}, Super.Interface, Interface);
+ Class.extend = Super.extend;
addEventPoolingTo(Class);
+ return Class;
};
addEventPoolingTo(SyntheticEvent);
function getPooledEvent(dispatchConfig, targetInst, nativeEvent, nativeInst) {
@@ -479,26 +474,12 @@ function addEventPoolingTo(EventConstructor) {
EventConstructor.getPooled = getPooledEvent;
EventConstructor.release = releasePooledEvent;
}
-function ResponderSyntheticEvent(
- dispatchConfig,
- dispatchMarker,
- nativeEvent,
- nativeEventTarget
-) {
- return SyntheticEvent.call(
- this,
- dispatchConfig,
- dispatchMarker,
- nativeEvent,
- nativeEventTarget
- );
-}
-SyntheticEvent.augmentClass(ResponderSyntheticEvent, {
- touchHistory: function() {
- return null;
- }
-});
-var touchBank = [],
+var ResponderSyntheticEvent = SyntheticEvent.extend({
+ touchHistory: function() {
+ return null;
+ }
+ }),
+ touchBank = [],
touchHistory = {
touchBank: touchBank,
numberActiveTouches: 0,
@@ -708,7 +689,7 @@ var eventTypes = {
isStartish(topLevelType) ||
isMoveish(topLevelType))
) {
- var shouldSetEventType = isStartish(topLevelType)
+ var JSCompiler_temp = isStartish(topLevelType)
? eventTypes.startShouldSetResponder
: isMoveish(topLevelType)
? eventTypes.moveShouldSetResponder
@@ -717,9 +698,9 @@ var eventTypes = {
: eventTypes.scrollShouldSetResponder;
if (responderInst)
b: {
- var JSCompiler_temp = responderInst;
+ var JSCompiler_temp$jscomp$0 = responderInst;
for (
- var depthA = 0, tempA = JSCompiler_temp;
+ var depthA = 0, tempA = JSCompiler_temp$jscomp$0;
tempA;
tempA = getParent(tempA)
)
@@ -728,196 +709,188 @@ var eventTypes = {
for (var tempB = targetInst; tempB; tempB = getParent(tempB))
tempA++;
for (; 0 < depthA - tempA; )
- (JSCompiler_temp = getParent(JSCompiler_temp)), depthA--;
+ (JSCompiler_temp$jscomp$0 = getParent(JSCompiler_temp$jscomp$0)),
+ depthA--;
for (; 0 < tempA - depthA; )
(targetInst = getParent(targetInst)), tempA--;
for (; depthA--; ) {
if (
- JSCompiler_temp === targetInst ||
- JSCompiler_temp === targetInst.alternate
+ JSCompiler_temp$jscomp$0 === targetInst ||
+ JSCompiler_temp$jscomp$0 === targetInst.alternate
)
break b;
- JSCompiler_temp = getParent(JSCompiler_temp);
+ JSCompiler_temp$jscomp$0 = getParent(JSCompiler_temp$jscomp$0);
targetInst = getParent(targetInst);
}
- JSCompiler_temp = null;
+ JSCompiler_temp$jscomp$0 = null;
}
- else JSCompiler_temp = targetInst;
- targetInst = JSCompiler_temp === responderInst;
- JSCompiler_temp = ResponderSyntheticEvent.getPooled(
- shouldSetEventType,
+ else JSCompiler_temp$jscomp$0 = targetInst;
+ targetInst = JSCompiler_temp$jscomp$0 === responderInst;
+ JSCompiler_temp$jscomp$0 = ResponderSyntheticEvent.getPooled(
JSCompiler_temp,
+ JSCompiler_temp$jscomp$0,
nativeEvent,
nativeEventTarget
);
- JSCompiler_temp.touchHistory = ResponderTouchHistoryStore.touchHistory;
+ JSCompiler_temp$jscomp$0.touchHistory =
+ ResponderTouchHistoryStore.touchHistory;
targetInst
? forEachAccumulated(
- JSCompiler_temp,
+ JSCompiler_temp$jscomp$0,
accumulateTwoPhaseDispatchesSingleSkipTarget
)
: forEachAccumulated(
- JSCompiler_temp,
+ JSCompiler_temp$jscomp$0,
accumulateTwoPhaseDispatchesSingle
);
b: {
- shouldSetEventType = JSCompiler_temp._dispatchListeners;
- targetInst = JSCompiler_temp._dispatchInstances;
- if (Array.isArray(shouldSetEventType))
+ JSCompiler_temp = JSCompiler_temp$jscomp$0._dispatchListeners;
+ targetInst = JSCompiler_temp$jscomp$0._dispatchInstances;
+ if (Array.isArray(JSCompiler_temp))
for (
depthA = 0;
- depthA < shouldSetEventType.length &&
- !JSCompiler_temp.isPropagationStopped();
+ depthA < JSCompiler_temp.length &&
+ !JSCompiler_temp$jscomp$0.isPropagationStopped();
depthA++
) {
if (
- shouldSetEventType[depthA](JSCompiler_temp, targetInst[depthA])
+ JSCompiler_temp[depthA](
+ JSCompiler_temp$jscomp$0,
+ targetInst[depthA]
+ )
) {
- shouldSetEventType = targetInst[depthA];
+ JSCompiler_temp = targetInst[depthA];
break b;
}
}
else if (
- shouldSetEventType &&
- shouldSetEventType(JSCompiler_temp, targetInst)
+ JSCompiler_temp &&
+ JSCompiler_temp(JSCompiler_temp$jscomp$0, targetInst)
) {
- shouldSetEventType = targetInst;
+ JSCompiler_temp = targetInst;
break b;
}
- shouldSetEventType = null;
+ JSCompiler_temp = null;
}
- JSCompiler_temp._dispatchInstances = null;
- JSCompiler_temp._dispatchListeners = null;
- JSCompiler_temp.isPersistent() ||
- JSCompiler_temp.constructor.release(JSCompiler_temp);
- if (shouldSetEventType && shouldSetEventType !== responderInst)
- if (
- ((JSCompiler_temp = ResponderSyntheticEvent.getPooled(
+ JSCompiler_temp$jscomp$0._dispatchInstances = null;
+ JSCompiler_temp$jscomp$0._dispatchListeners = null;
+ JSCompiler_temp$jscomp$0.isPersistent() ||
+ JSCompiler_temp$jscomp$0.constructor.release(
+ JSCompiler_temp$jscomp$0
+ );
+ JSCompiler_temp && JSCompiler_temp !== responderInst
+ ? ((JSCompiler_temp$jscomp$0 = void 0),
+ (targetInst = ResponderSyntheticEvent.getPooled(
eventTypes.responderGrant,
- shouldSetEventType,
+ JSCompiler_temp,
nativeEvent,
nativeEventTarget
)),
- (JSCompiler_temp.touchHistory =
- ResponderTouchHistoryStore.touchHistory),
- forEachAccumulated(
- JSCompiler_temp,
- accumulateDirectDispatchesSingle
- ),
- (targetInst = !0 === executeDirectDispatch(JSCompiler_temp)),
- responderInst)
- )
- if (
- ((depthA = ResponderSyntheticEvent.getPooled(
- eventTypes.responderTerminationRequest,
- responderInst,
- nativeEvent,
- nativeEventTarget
- )),
- (depthA.touchHistory = ResponderTouchHistoryStore.touchHistory),
- forEachAccumulated(depthA, accumulateDirectDispatchesSingle),
- (tempA =
- !depthA._dispatchListeners || executeDirectDispatch(depthA)),
- depthA.isPersistent() || depthA.constructor.release(depthA),
- tempA)
- ) {
- depthA = ResponderSyntheticEvent.getPooled(
- eventTypes.responderTerminate,
- responderInst,
- nativeEvent,
- nativeEventTarget
- );
- depthA.touchHistory = ResponderTouchHistoryStore.touchHistory;
- forEachAccumulated(depthA, accumulateDirectDispatchesSingle);
- var JSCompiler_temp$jscomp$0 = accumulate(
- JSCompiler_temp$jscomp$0,
- [JSCompiler_temp, depthA]
- );
- changeResponder(shouldSetEventType, targetInst);
- } else
- (shouldSetEventType = ResponderSyntheticEvent.getPooled(
- eventTypes.responderReject,
- shouldSetEventType,
- nativeEvent,
- nativeEventTarget
- )),
- (shouldSetEventType.touchHistory =
- ResponderTouchHistoryStore.touchHistory),
- forEachAccumulated(
- shouldSetEventType,
- accumulateDirectDispatchesSingle
- ),
- (JSCompiler_temp$jscomp$0 = accumulate(
+ (targetInst.touchHistory = ResponderTouchHistoryStore.touchHistory),
+ forEachAccumulated(targetInst, accumulateDirectDispatchesSingle),
+ (depthA = !0 === executeDirectDispatch(targetInst)),
+ responderInst
+ ? ((tempA = ResponderSyntheticEvent.getPooled(
+ eventTypes.responderTerminationRequest,
+ responderInst,
+ nativeEvent,
+ nativeEventTarget
+ )),
+ (tempA.touchHistory = ResponderTouchHistoryStore.touchHistory),
+ forEachAccumulated(tempA, accumulateDirectDispatchesSingle),
+ (tempB =
+ !tempA._dispatchListeners || executeDirectDispatch(tempA)),
+ tempA.isPersistent() || tempA.constructor.release(tempA),
+ tempB
+ ? ((tempA = ResponderSyntheticEvent.getPooled(
+ eventTypes.responderTerminate,
+ responderInst,
+ nativeEvent,
+ nativeEventTarget
+ )),
+ (tempA.touchHistory =
+ ResponderTouchHistoryStore.touchHistory),
+ forEachAccumulated(tempA, accumulateDirectDispatchesSingle),
+ (JSCompiler_temp$jscomp$0 = accumulate(
+ JSCompiler_temp$jscomp$0,
+ [targetInst, tempA]
+ )),
+ changeResponder(JSCompiler_temp, depthA))
+ : ((JSCompiler_temp = ResponderSyntheticEvent.getPooled(
+ eventTypes.responderReject,
+ JSCompiler_temp,
+ nativeEvent,
+ nativeEventTarget
+ )),
+ (JSCompiler_temp.touchHistory =
+ ResponderTouchHistoryStore.touchHistory),
+ forEachAccumulated(
+ JSCompiler_temp,
+ accumulateDirectDispatchesSingle
+ ),
+ (JSCompiler_temp$jscomp$0 = accumulate(
+ JSCompiler_temp$jscomp$0,
+ JSCompiler_temp
+ ))))
+ : ((JSCompiler_temp$jscomp$0 = accumulate(
JSCompiler_temp$jscomp$0,
- shouldSetEventType
- ));
- else
- (JSCompiler_temp$jscomp$0 = accumulate(
- JSCompiler_temp$jscomp$0,
- JSCompiler_temp
- )),
- changeResponder(shouldSetEventType, targetInst);
- else JSCompiler_temp$jscomp$0 = null;
- } else JSCompiler_temp$jscomp$0 = null;
- shouldSetEventType = responderInst && isStartish(topLevelType);
- JSCompiler_temp = responderInst && isMoveish(topLevelType);
- targetInst = responderInst && isEndish(topLevelType);
+ targetInst
+ )),
+ changeResponder(JSCompiler_temp, depthA)),
+ (JSCompiler_temp = JSCompiler_temp$jscomp$0))
+ : (JSCompiler_temp = null);
+ } else JSCompiler_temp = null;
+ JSCompiler_temp$jscomp$0 = responderInst && isStartish(topLevelType);
+ targetInst = responderInst && isMoveish(topLevelType);
+ depthA = responderInst && isEndish(topLevelType);
if (
- (shouldSetEventType = shouldSetEventType
+ (JSCompiler_temp$jscomp$0 = JSCompiler_temp$jscomp$0
? eventTypes.responderStart
- : JSCompiler_temp
+ : targetInst
? eventTypes.responderMove
- : targetInst ? eventTypes.responderEnd : null)
+ : depthA ? eventTypes.responderEnd : null)
)
- (shouldSetEventType = ResponderSyntheticEvent.getPooled(
- shouldSetEventType,
+ (JSCompiler_temp$jscomp$0 = ResponderSyntheticEvent.getPooled(
+ JSCompiler_temp$jscomp$0,
responderInst,
nativeEvent,
nativeEventTarget
)),
- (shouldSetEventType.touchHistory =
+ (JSCompiler_temp$jscomp$0.touchHistory =
ResponderTouchHistoryStore.touchHistory),
forEachAccumulated(
- shouldSetEventType,
+ JSCompiler_temp$jscomp$0,
accumulateDirectDispatchesSingle
),
- (JSCompiler_temp$jscomp$0 = accumulate(
- JSCompiler_temp$jscomp$0,
- shouldSetEventType
+ (JSCompiler_temp = accumulate(
+ JSCompiler_temp,
+ JSCompiler_temp$jscomp$0
));
- shouldSetEventType = responderInst && "topTouchCancel" === topLevelType;
+ JSCompiler_temp$jscomp$0 =
+ responderInst && "topTouchCancel" === topLevelType;
if (
(topLevelType =
- responderInst && !shouldSetEventType && isEndish(topLevelType))
+ responderInst && !JSCompiler_temp$jscomp$0 && isEndish(topLevelType))
)
a: {
if ((topLevelType = nativeEvent.touches) && 0 !== topLevelType.length)
- for (
- JSCompiler_temp = 0;
- JSCompiler_temp < topLevelType.length;
- JSCompiler_temp++
- )
+ for (targetInst = 0; targetInst < topLevelType.length; targetInst++)
if (
- ((targetInst = topLevelType[JSCompiler_temp].target),
- null !== targetInst &&
- void 0 !== targetInst &&
- 0 !== targetInst)
+ ((depthA = topLevelType[targetInst].target),
+ null !== depthA && void 0 !== depthA && 0 !== depthA)
) {
- depthA = getInstanceFromNode(targetInst);
+ tempA = getInstanceFromNode(depthA);
b: {
- for (targetInst = responderInst; depthA; ) {
- if (
- targetInst === depthA ||
- targetInst === depthA.alternate
- ) {
- targetInst = !0;
+ for (depthA = responderInst; tempA; ) {
+ if (depthA === tempA || depthA === tempA.alternate) {
+ depthA = !0;
break b;
}
- depthA = getParent(depthA);
+ tempA = getParent(tempA);
}
- targetInst = !1;
+ depthA = !1;
}
- if (targetInst) {
+ if (depthA) {
topLevelType = !1;
break a;
}
@@ -925,7 +898,7 @@ var eventTypes = {
topLevelType = !0;
}
if (
- (topLevelType = shouldSetEventType
+ (topLevelType = JSCompiler_temp$jscomp$0
? eventTypes.responderTerminate
: topLevelType ? eventTypes.responderRelease : null)
)
@@ -937,10 +910,7 @@ var eventTypes = {
)),
(nativeEvent.touchHistory = ResponderTouchHistoryStore.touchHistory),
forEachAccumulated(nativeEvent, accumulateDirectDispatchesSingle),
- (JSCompiler_temp$jscomp$0 = accumulate(
- JSCompiler_temp$jscomp$0,
- nativeEvent
- )),
+ (JSCompiler_temp = accumulate(JSCompiler_temp, nativeEvent)),
changeResponder(null);
nativeEvent = ResponderTouchHistoryStore.touchHistory.numberActiveTouches;
if (
@@ -949,7 +919,7 @@ var eventTypes = {
)
ResponderEventPlugin.GlobalInteractionHandler.onChange(nativeEvent);
previousActiveTouches = nativeEvent;
- return JSCompiler_temp$jscomp$0;
+ return JSCompiler_temp;
},
GlobalResponderHandler: null,
GlobalInteractionHandler: null,
@@ -1018,7 +988,7 @@ function uncacheFiberNode(tag) {
delete instanceProps[tag];
}
function getInstanceFromTag(tag) {
- return instanceCache[tag] || null;
+ return "number" === typeof tag ? instanceCache[tag] || null : tag;
}
var ReactNativeComponentTree = Object.freeze({
precacheFiberNode: function(hostInst, tag) {
@@ -1028,9 +998,10 @@ var ReactNativeComponentTree = Object.freeze({
getClosestInstanceFromNode: getInstanceFromTag,
getInstanceFromNode: getInstanceFromTag,
getNodeFromInstance: function(inst) {
- inst = inst.stateNode._nativeTag;
- invariant(inst, "All native instances should have a tag.");
- return inst;
+ var tag = inst.stateNode._nativeTag;
+ void 0 === tag && (tag = inst.stateNode.canonical._nativeTag);
+ invariant(tag, "All native instances should have a tag.");
+ return tag;
},
getFiberCurrentPropsFromNode: function(stateNode) {
return instanceProps[stateNode._nativeTag] || null;
@@ -1074,34 +1045,6 @@ function batchedUpdates(fn, bookkeeping) {
restoreStateOfTarget(fn[bookkeeping]);
}
}
-function handleTopLevel(
- topLevelType,
- targetInst,
- nativeEvent,
- nativeEventTarget
-) {
- for (var events, i = 0; i < plugins.length; i++) {
- var possiblePlugin = plugins[i];
- possiblePlugin &&
- (possiblePlugin = possiblePlugin.extractEvents(
- topLevelType,
- targetInst,
- nativeEvent,
- nativeEventTarget
- )) &&
- (events = accumulateInto(events, possiblePlugin));
- }
- events && (eventQueue = accumulateInto(eventQueue, events));
- topLevelType = eventQueue;
- eventQueue = null;
- topLevelType &&
- (forEachAccumulated(topLevelType, executeDispatchesAndReleaseTopLevel),
- invariant(
- !eventQueue,
- "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."
- ),
- ReactErrorUtils.rethrowCaughtError());
-}
var ReactNativeTagHandles = {
tagsStartAt: 1,
tagCount: 1,
@@ -1128,7 +1071,29 @@ function _receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam) {
var nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT,
inst = getInstanceFromTag(rootNodeID);
batchedUpdates(function() {
- handleTopLevel(topLevelType, inst, nativeEvent, nativeEvent.target);
+ var events = nativeEvent.target;
+ for (var events$jscomp$0 = null, i = 0; i < plugins.length; i++) {
+ var possiblePlugin = plugins[i];
+ possiblePlugin &&
+ (possiblePlugin = possiblePlugin.extractEvents(
+ topLevelType,
+ inst,
+ nativeEvent,
+ events
+ )) &&
+ (events$jscomp$0 = accumulateInto(events$jscomp$0, possiblePlugin));
+ }
+ events = events$jscomp$0;
+ null !== events && (eventQueue = accumulateInto(eventQueue, events));
+ events = eventQueue;
+ eventQueue = null;
+ events &&
+ (forEachAccumulated(events, executeDispatchesAndReleaseTopLevel),
+ invariant(
+ !eventQueue,
+ "processEventQueue(): Additional events were enqueued while processing an event queue. Support for this has not yet been implemented."
+ ),
+ ReactErrorUtils.rethrowCaughtError());
});
}
var ReactNativeEventEmitter = Object.freeze({
@@ -1172,8 +1137,7 @@ var ReactNativeEventEmitter = Object.freeze({
(index = target);
_receiveRootNodeIDEvent(index, eventTopLevelType, i);
}
- },
- handleTopLevel: handleTopLevel
+ }
});
RCTEventEmitter.register(ReactNativeEventEmitter);
injection.injectEventPluginOrder([
@@ -1195,16 +1159,17 @@ injection.injectEventPluginsByName({
ResponderEventPlugin: ResponderEventPlugin,
ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin
});
-function defaultShowDialog() {
- return !0;
-}
-var showDialog = defaultShowDialog,
- hasSymbol = "function" === typeof Symbol && Symbol["for"],
+var hasSymbol = "function" === typeof Symbol && Symbol["for"],
REACT_ELEMENT_TYPE = hasSymbol ? Symbol["for"]("react.element") : 60103,
REACT_CALL_TYPE = hasSymbol ? Symbol["for"]("react.call") : 60104,
REACT_RETURN_TYPE = hasSymbol ? Symbol["for"]("react.return") : 60105,
REACT_PORTAL_TYPE = hasSymbol ? Symbol["for"]("react.portal") : 60106,
REACT_FRAGMENT_TYPE = hasSymbol ? Symbol["for"]("react.fragment") : 60107,
+ REACT_STRICT_MODE_TYPE = hasSymbol
+ ? Symbol["for"]("react.strict_mode")
+ : 60108,
+ REACT_PROVIDER_TYPE = hasSymbol ? Symbol["for"]("react.provider") : 60109,
+ REACT_CONTEXT_TYPE = hasSymbol ? Symbol["for"]("react.context") : 60110,
MAYBE_ITERATOR_SYMBOL = "function" === typeof Symbol && Symbol.iterator;
function getIteratorFn(maybeIterable) {
if (null === maybeIterable || "undefined" === typeof maybeIterable)
@@ -1378,23 +1343,23 @@ function restoreDeletedValuesInNestedArray(
else if (node && 0 < removedKeyCount)
for (i in ((node = resolveObject(node)), removedKeys))
if (removedKeys[i]) {
- var nextProp = node[i];
- if (void 0 !== nextProp) {
+ var _nextProp = node[i];
+ if (void 0 !== _nextProp) {
var attributeConfig = validAttributes[i];
if (attributeConfig) {
- "function" === typeof nextProp && (nextProp = !0);
- "undefined" === typeof nextProp && (nextProp = null);
+ "function" === typeof _nextProp && (_nextProp = !0);
+ "undefined" === typeof _nextProp && (_nextProp = null);
if ("object" !== typeof attributeConfig)
- updatePayload[i] = nextProp;
+ updatePayload[i] = _nextProp;
else if (
"function" === typeof attributeConfig.diff ||
"function" === typeof attributeConfig.process
)
- (nextProp =
+ (_nextProp =
"function" === typeof attributeConfig.process
- ? attributeConfig.process(nextProp)
- : nextProp),
- (updatePayload[i] = nextProp);
+ ? attributeConfig.process(_nextProp)
+ : _nextProp),
+ (updatePayload[i] = _nextProp);
removedKeys[i] = !1;
removedKeyCount--;
}
@@ -1559,19 +1524,19 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
),
(removedKeys = null));
}
- for (propKey in prevProps)
- void 0 === nextProps[propKey] &&
- (!(attributeConfig = validAttributes[propKey]) ||
- (updatePayload && void 0 !== updatePayload[propKey]) ||
- ((prevProp = prevProps[propKey]),
+ for (var _propKey in prevProps)
+ void 0 === nextProps[_propKey] &&
+ (!(attributeConfig = validAttributes[_propKey]) ||
+ (updatePayload && void 0 !== updatePayload[_propKey]) ||
+ ((prevProp = prevProps[_propKey]),
void 0 !== prevProp &&
("object" !== typeof attributeConfig ||
"function" === typeof attributeConfig.diff ||
"function" === typeof attributeConfig.process
- ? (((updatePayload || (updatePayload = {}))[propKey] = null),
+ ? (((updatePayload || (updatePayload = {}))[_propKey] = null),
removedKeys || (removedKeys = {}),
- removedKeys[propKey] ||
- ((removedKeys[propKey] = !0), removedKeyCount++))
+ removedKeys[_propKey] ||
+ ((removedKeys[_propKey] = !0), removedKeyCount++))
: (updatePayload = clearNestedProperty(
updatePayload,
prevProp,
@@ -1595,12 +1560,136 @@ function mountSafeCallback(context, callback) {
}
function getComponentName(fiber) {
fiber = fiber.type;
- return "string" === typeof fiber
- ? fiber
- : "function" === typeof fiber ? fiber.displayName || fiber.name : null;
+ if ("function" === typeof fiber) return fiber.displayName || fiber.name;
+ if ("string" === typeof fiber) return fiber;
+ switch (fiber) {
+ case REACT_FRAGMENT_TYPE:
+ return "ReactFragment";
+ case REACT_PORTAL_TYPE:
+ return "ReactPortal";
+ case REACT_CALL_TYPE:
+ return "ReactCall";
+ case REACT_RETURN_TYPE:
+ return "ReactReturn";
+ }
+ return null;
+}
+function findHostInstance() {
+ return null;
+}
+function findNodeHandle(componentOrHandle) {
+ if (null == componentOrHandle) return null;
+ if ("number" === typeof componentOrHandle) return componentOrHandle;
+ var internalInstance = componentOrHandle._reactInternalFiber;
+ if (internalInstance) return findHostInstance(internalInstance) || null;
+ if (componentOrHandle) return componentOrHandle;
+ invariant(
+ ("object" === typeof componentOrHandle &&
+ "_nativeTag" in componentOrHandle) ||
+ (null != componentOrHandle.render &&
+ "function" === typeof componentOrHandle.render),
+ "findNodeHandle(...): Argument is not a component (type: %s, keys: %s)",
+ typeof componentOrHandle,
+ Object.keys(componentOrHandle)
+ );
+ invariant(
+ !1,
+ "findNodeHandle(...): Unable to find node handle for unmounted component."
+ );
+}
+function findNumericNodeHandleFiber(componentOrHandle) {
+ componentOrHandle = findNodeHandle(componentOrHandle);
+ return null == componentOrHandle || "number" === typeof componentOrHandle
+ ? componentOrHandle
+ : componentOrHandle._nativeTag;
}
-var debugRenderPhaseSideEffects = require("ReactFeatureFlags")
- .debugRenderPhaseSideEffects;
+function _inherits(subClass, superClass) {
+ if ("function" !== typeof superClass && null !== superClass)
+ throw new TypeError(
+ "Super expression must either be null or a function, not " +
+ typeof superClass
+ );
+ subClass.prototype = Object.create(superClass && superClass.prototype, {
+ constructor: {
+ value: subClass,
+ enumerable: !1,
+ writable: !0,
+ configurable: !0
+ }
+ });
+ superClass &&
+ (Object.setPrototypeOf
+ ? Object.setPrototypeOf(subClass, superClass)
+ : (subClass.__proto__ = superClass));
+}
+var ReactNativeComponent = (function(_React$Component) {
+ function ReactNativeComponent() {
+ if (!(this instanceof ReactNativeComponent))
+ throw new TypeError("Cannot call a class as a function");
+ var call = _React$Component.apply(this, arguments);
+ if (!this)
+ throw new ReferenceError(
+ "this hasn't been initialised - super() hasn't been called"
+ );
+ return !call || ("object" !== typeof call && "function" !== typeof call)
+ ? this
+ : call;
+ }
+ _inherits(ReactNativeComponent, _React$Component);
+ ReactNativeComponent.prototype.blur = function() {
+ TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
+ };
+ ReactNativeComponent.prototype.focus = function() {
+ TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
+ };
+ ReactNativeComponent.prototype.measure = function(callback) {
+ UIManager.measure(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ };
+ ReactNativeComponent.prototype.measureInWindow = function(callback) {
+ UIManager.measureInWindow(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ };
+ ReactNativeComponent.prototype.measureLayout = function(
+ relativeToNativeNode,
+ onSuccess,
+ onFail
+ ) {
+ UIManager.measureLayout(
+ findNumericNodeHandleFiber(this),
+ relativeToNativeNode,
+ mountSafeCallback(this, onFail),
+ mountSafeCallback(this, onSuccess)
+ );
+ };
+ ReactNativeComponent.prototype.setNativeProps = function(nativeProps) {
+ var maybeInstance = void 0;
+ try {
+ maybeInstance = findNodeHandle(this);
+ } catch (error) {}
+ if (null != maybeInstance) {
+ var viewConfig =
+ maybeInstance.viewConfig || maybeInstance.canonical.viewConfig;
+ nativeProps = diffProperties(
+ null,
+ emptyObject$1,
+ nativeProps,
+ viewConfig.validAttributes
+ );
+ null != nativeProps &&
+ UIManager.updateView(
+ maybeInstance._nativeTag,
+ viewConfig.uiViewClassName,
+ nativeProps
+ );
+ }
+ };
+ return ReactNativeComponent;
+})(React.Component);
function isFiberMountedImpl(fiber) {
var node = fiber;
if (fiber.alternate) for (; node["return"]; ) node = node["return"];
@@ -1741,6 +1830,10 @@ function push(cursor, value) {
valueStack[index] = cursor.current;
cursor.current = value;
}
+var _require = require("ReactFeatureFlags"),
+ debugRenderPhaseSideEffects = _require.debugRenderPhaseSideEffects,
+ debugRenderPhaseSideEffectsForStrictMode =
+ _require.debugRenderPhaseSideEffectsForStrictMode;
new Set();
var contextStackCursor = { current: emptyObject },
didPerformWorkStackCursor = { current: !1 },
@@ -1842,13 +1935,10 @@ function FiberNode(tag, pendingProps, key, internalContextTag) {
this.expirationTime = 0;
this.alternate = null;
}
-function createFiber(tag, pendingProps, key, internalContextTag) {
- return new FiberNode(tag, pendingProps, key, internalContextTag);
-}
function createWorkInProgress(current, pendingProps, expirationTime) {
var workInProgress = current.alternate;
null === workInProgress
- ? ((workInProgress = createFiber(
+ ? ((workInProgress = new FiberNode(
current.tag,
pendingProps,
current.key,
@@ -1874,31 +1964,70 @@ function createWorkInProgress(current, pendingProps, expirationTime) {
return workInProgress;
}
function createFiberFromElement(element, internalContextTag, expirationTime) {
- var fiber = void 0,
- type = element.type,
+ var type = element.type,
key = element.key;
element = element.props;
- "function" === typeof type
- ? ((fiber =
- type.prototype && type.prototype.isReactComponent
- ? createFiber(2, element, key, internalContextTag)
- : createFiber(0, element, key, internalContextTag)),
- (fiber.type = type))
- : "string" === typeof type
- ? ((fiber = createFiber(5, element, key, internalContextTag)),
- (fiber.type = type))
- : "object" === typeof type &&
- null !== type &&
- "number" === typeof type.tag
- ? ((fiber = type), (fiber.pendingProps = element))
- : invariant(
- !1,
- "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
- null == type ? type : typeof type,
- ""
- );
- fiber.expirationTime = expirationTime;
- return fiber;
+ var fiberTag = void 0;
+ if ("function" === typeof type)
+ fiberTag = type.prototype && type.prototype.isReactComponent ? 2 : 0;
+ else if ("string" === typeof type) fiberTag = 5;
+ else
+ switch (type) {
+ case REACT_FRAGMENT_TYPE:
+ return createFiberFromFragment(
+ element.children,
+ internalContextTag,
+ expirationTime,
+ key
+ );
+ case REACT_STRICT_MODE_TYPE:
+ fiberTag = 11;
+ internalContextTag |= 2;
+ break;
+ case REACT_CALL_TYPE:
+ fiberTag = 7;
+ break;
+ case REACT_RETURN_TYPE:
+ fiberTag = 9;
+ break;
+ default:
+ if ("object" === typeof type && null !== type)
+ switch (type.$$typeof) {
+ case REACT_PROVIDER_TYPE:
+ fiberTag = 13;
+ break;
+ case REACT_CONTEXT_TYPE:
+ fiberTag = 12;
+ break;
+ default:
+ if ("number" === typeof type.tag)
+ return (
+ (internalContextTag = type),
+ (internalContextTag.pendingProps = element),
+ (internalContextTag.expirationTime = expirationTime),
+ internalContextTag
+ );
+ throwOnInvalidElementType(type, null);
+ }
+ else throwOnInvalidElementType(type, null);
+ }
+ internalContextTag = new FiberNode(
+ fiberTag,
+ element,
+ key,
+ internalContextTag
+ );
+ internalContextTag.type = type;
+ internalContextTag.expirationTime = expirationTime;
+ return internalContextTag;
+}
+function throwOnInvalidElementType(type) {
+ invariant(
+ !1,
+ "Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s",
+ null == type ? type : typeof type,
+ ""
+ );
}
function createFiberFromFragment(
elements,
@@ -1906,28 +2035,17 @@ function createFiberFromFragment(
expirationTime,
key
) {
- elements = createFiber(10, elements, key, internalContextTag);
+ elements = new FiberNode(10, elements, key, internalContextTag);
elements.expirationTime = expirationTime;
return elements;
}
function createFiberFromText(content, internalContextTag, expirationTime) {
- content = createFiber(6, content, null, internalContextTag);
+ content = new FiberNode(6, content, null, internalContextTag);
content.expirationTime = expirationTime;
return content;
}
-function createFiberFromCall(call, internalContextTag, expirationTime) {
- internalContextTag = createFiber(7, call, call.key, internalContextTag);
- internalContextTag.type = call.handler;
- internalContextTag.expirationTime = expirationTime;
- return internalContextTag;
-}
-function createFiberFromReturn(returnNode, internalContextTag, expirationTime) {
- returnNode = createFiber(9, null, null, internalContextTag);
- returnNode.expirationTime = expirationTime;
- return returnNode;
-}
function createFiberFromPortal(portal, internalContextTag, expirationTime) {
- internalContextTag = createFiber(
+ internalContextTag = new FiberNode(
4,
null !== portal.children ? portal.children : [],
portal.key,
@@ -2012,8 +2130,7 @@ function insertUpdateIntoFiber(fiber, update) {
function getStateFromUpdate(update, instance, prevState, props) {
update = update.partialState;
return "function" === typeof update
- ? (debugRenderPhaseSideEffects && update.call(instance, prevState, props),
- update.call(instance, prevState, props))
+ ? update.call(instance, prevState, props)
: update;
}
function processUpdateQueue(
@@ -2058,6 +2175,10 @@ function processUpdateQueue(
didSkip ||
((queue.first = update.next),
null === queue.first && (queue.last = null));
+ (debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & 2)) &&
+ getStateFromUpdate(update, instance, current, props);
if (update.isReplace)
(current = getStateFromUpdate(update, instance, current, props)),
(dontMutatePrevState = !0);
@@ -2120,6 +2241,25 @@ function ReactFiberClassComponent(
workInProgress.stateNode = instance;
instance._reactInternalFiber = workInProgress;
}
+ function callGetDerivedStateFromProps(workInProgress, instance, props) {
+ instance = workInProgress.type;
+ if ("function" === typeof instance.getDerivedStateFromProps)
+ return (
+ (debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & 2)) &&
+ instance.getDerivedStateFromProps.call(
+ null,
+ props,
+ workInProgress.memoizedState
+ ),
+ instance.getDerivedStateFromProps.call(
+ null,
+ props,
+ workInProgress.memoizedState
+ )
+ );
+ }
var updater = {
isMounted: isMounted,
enqueueSetState: function(instance, partialState, callback) {
@@ -2170,6 +2310,7 @@ function ReactFiberClassComponent(
};
return {
adoptClassInstance: adoptClassInstance,
+ callGetDerivedStateFromProps: callGetDerivedStateFromProps,
constructClassInstance: function(workInProgress, props) {
var ctor = workInProgress.type,
unmaskedContext = getUnmaskedContext(workInProgress),
@@ -2178,40 +2319,58 @@ function ReactFiberClassComponent(
context = needsContext
? getMaskedContext(workInProgress, unmaskedContext)
: emptyObject;
- props = new ctor(props, context);
- adoptClassInstance(workInProgress, props);
+ (debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & 2)) &&
+ new ctor(props, context);
+ ctor = new ctor(props, context);
+ var state =
+ null !== ctor.state && void 0 !== ctor.state ? ctor.state : null;
+ adoptClassInstance(workInProgress, ctor);
+ workInProgress.memoizedState = state;
+ props = callGetDerivedStateFromProps(workInProgress, ctor, props);
+ null !== props &&
+ void 0 !== props &&
+ (workInProgress.memoizedState = Object.assign(
+ {},
+ workInProgress.memoizedState,
+ props
+ ));
needsContext &&
((workInProgress = workInProgress.stateNode),
(workInProgress.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext),
(workInProgress.__reactInternalMemoizedMaskedChildContext = context));
- return props;
+ return ctor;
},
mountClassInstance: function(workInProgress, renderExpirationTime) {
var current = workInProgress.alternate,
instance = workInProgress.stateNode,
- state = instance.state || null,
props = workInProgress.pendingProps,
unmaskedContext = getUnmaskedContext(workInProgress);
instance.props = props;
- instance.state = workInProgress.memoizedState = state;
+ instance.state = workInProgress.memoizedState;
instance.refs = emptyObject;
instance.context = getMaskedContext(workInProgress, unmaskedContext);
null != workInProgress.type &&
null != workInProgress.type.prototype &&
!0 === workInProgress.type.prototype.unstable_isAsyncReactComponent &&
- (workInProgress.internalContextTag |= 1);
- "function" === typeof instance.componentWillMount &&
- ((state = instance.state),
- instance.componentWillMount(),
- debugRenderPhaseSideEffects && instance.componentWillMount(),
- state !== instance.state &&
+ ((workInProgress.internalContextTag |= 1),
+ (workInProgress.internalContextTag |= 2));
+ ("function" !== typeof instance.UNSAFE_componentWillMount &&
+ "function" !== typeof instance.componentWillMount) ||
+ "function" === typeof workInProgress.type.getDerivedStateFromProps ||
+ ((unmaskedContext = instance.state),
+ "function" === typeof instance.componentWillMount
+ ? instance.componentWillMount()
+ : instance.UNSAFE_componentWillMount(),
+ unmaskedContext !== instance.state &&
updater.enqueueReplaceState(instance, instance.state, null),
- (state = workInProgress.updateQueue),
- null !== state &&
+ (unmaskedContext = workInProgress.updateQueue),
+ null !== unmaskedContext &&
(instance.state = processUpdateQueue(
current,
workInProgress,
- state,
+ unmaskedContext,
instance,
props,
renderExpirationTime
@@ -2232,14 +2391,26 @@ function ReactFiberClassComponent(
oldContext = instance.context,
newUnmaskedContext = getUnmaskedContext(workInProgress);
newUnmaskedContext = getMaskedContext(workInProgress, newUnmaskedContext);
- "function" !== typeof instance.componentWillReceiveProps ||
+ ("function" !== typeof instance.UNSAFE_componentWillReceiveProps &&
+ "function" !== typeof instance.componentWillReceiveProps) ||
+ "function" === typeof workInProgress.type.getDerivedStateFromProps ||
(oldProps === newProps && oldContext === newUnmaskedContext) ||
((oldContext = instance.state),
- instance.componentWillReceiveProps(newProps, newUnmaskedContext),
- debugRenderPhaseSideEffects &&
- instance.componentWillReceiveProps(newProps, newUnmaskedContext),
+ "function" === typeof instance.componentWillReceiveProps
+ ? instance.componentWillReceiveProps(newProps, newUnmaskedContext)
+ : instance.UNSAFE_componentWillReceiveProps(
+ newProps,
+ newUnmaskedContext
+ ),
instance.state !== oldContext &&
updater.enqueueReplaceState(instance, instance.state, null));
+ var partialState = void 0;
+ oldProps !== newProps &&
+ (partialState = callGetDerivedStateFromProps(
+ workInProgress,
+ instance,
+ newProps
+ ));
oldContext = workInProgress.memoizedState;
renderExpirationTime =
null !== workInProgress.updateQueue
@@ -2252,6 +2423,12 @@ function ReactFiberClassComponent(
renderExpirationTime
)
: oldContext;
+ null !== partialState &&
+ void 0 !== partialState &&
+ (renderExpirationTime =
+ null === renderExpirationTime || void 0 === renderExpirationTime
+ ? partialState
+ : Object.assign({}, renderExpirationTime, partialState));
if (
!(
oldProps !== newProps ||
@@ -2268,47 +2445,44 @@ function ReactFiberClassComponent(
(workInProgress.effectTag |= 4),
!1
);
+ partialState = renderExpirationTime;
if (
null === oldProps ||
(null !== workInProgress.updateQueue &&
workInProgress.updateQueue.hasForceUpdate)
)
- var shouldUpdate = !0;
+ partialState = !0;
else {
- shouldUpdate = workInProgress.stateNode;
- var type = workInProgress.type;
- "function" === typeof shouldUpdate.shouldComponentUpdate
- ? ((type = shouldUpdate.shouldComponentUpdate(
- newProps,
- renderExpirationTime,
- newUnmaskedContext
- )),
- debugRenderPhaseSideEffects &&
- shouldUpdate.shouldComponentUpdate(
+ var instance$jscomp$0 = workInProgress.stateNode,
+ type = workInProgress.type;
+ partialState =
+ "function" === typeof instance$jscomp$0.shouldComponentUpdate
+ ? instance$jscomp$0.shouldComponentUpdate(
newProps,
- renderExpirationTime,
+ partialState,
newUnmaskedContext
- ),
- (shouldUpdate = type))
- : (shouldUpdate =
- type.prototype && type.prototype.isPureReactComponent
- ? !shallowEqual(oldProps, newProps) ||
- !shallowEqual(oldContext, renderExpirationTime)
- : !0);
+ )
+ : type.prototype && type.prototype.isPureReactComponent
+ ? !shallowEqual(oldProps, newProps) ||
+ !shallowEqual(oldContext, partialState)
+ : !0;
}
- shouldUpdate
- ? ("function" === typeof instance.componentWillUpdate &&
- (instance.componentWillUpdate(
- newProps,
- renderExpirationTime,
- newUnmaskedContext
- ),
- debugRenderPhaseSideEffects &&
- instance.componentWillUpdate(
- newProps,
- renderExpirationTime,
- newUnmaskedContext
- )),
+ partialState
+ ? (("function" !== typeof instance.UNSAFE_componentWillUpdate &&
+ "function" !== typeof instance.componentWillUpdate) ||
+ "function" ===
+ typeof workInProgress.type.getDerivedStateFromProps ||
+ ("function" === typeof instance.componentWillUpdate
+ ? instance.componentWillUpdate(
+ newProps,
+ renderExpirationTime,
+ newUnmaskedContext
+ )
+ : instance.UNSAFE_componentWillUpdate(
+ newProps,
+ renderExpirationTime,
+ newUnmaskedContext
+ )),
"function" === typeof instance.componentDidUpdate &&
(workInProgress.effectTag |= 4))
: ("function" !== typeof instance.componentDidUpdate ||
@@ -2320,7 +2494,7 @@ function ReactFiberClassComponent(
instance.props = newProps;
instance.state = renderExpirationTime;
instance.context = newUnmaskedContext;
- return shouldUpdate;
+ return partialState;
}
};
}
@@ -2362,7 +2536,7 @@ function coerceRef(current, element) {
);
invariant(
element._owner,
- "Element ref was specified as a string (%s) but no owner was set. You may have multiple copies of React loaded. (details: https://fb.me/react-refs-must-have-owner).",
+ "Element ref was specified as a string (%s) but no owner was set. This could happen for one of the following reasons:\n1. You may be adding a ref to a functional component\n2. You may be adding a ref to a component that was not created inside a component's render method\n3. You have multiple copies of React loaded\nSee https://fb.me/react-refs-must-have-owner for more information.",
mixedRef
);
}
@@ -2464,38 +2638,6 @@ function ChildReconciler(shouldTrackSideEffects) {
expirationTime["return"] = returnFiber;
return expirationTime;
}
- function updateCall(returnFiber, current, call, expirationTime) {
- if (null === current || 7 !== current.tag)
- return (
- (current = createFiberFromCall(
- call,
- returnFiber.internalContextTag,
- expirationTime
- )),
- (current["return"] = returnFiber),
- current
- );
- current = useFiber(current, call, expirationTime);
- current["return"] = returnFiber;
- return current;
- }
- function updateReturn(returnFiber, current, returnNode, expirationTime) {
- if (null === current || 9 !== current.tag)
- return (
- (current = createFiberFromReturn(
- returnNode,
- returnFiber.internalContextTag,
- expirationTime
- )),
- (current.type = returnNode.value),
- (current["return"] = returnFiber),
- current
- );
- current = useFiber(current, null, expirationTime);
- current.type = returnNode.value;
- current["return"] = returnFiber;
- return current;
- }
function updatePortal(returnFiber, current, portal, expirationTime) {
if (
null === current ||
@@ -2546,43 +2688,13 @@ function ChildReconciler(shouldTrackSideEffects) {
if ("object" === typeof newChild && null !== newChild) {
switch (newChild.$$typeof) {
case REACT_ELEMENT_TYPE:
- if (newChild.type === REACT_FRAGMENT_TYPE)
- return (
- (newChild = createFiberFromFragment(
- newChild.props.children,
- returnFiber.internalContextTag,
- expirationTime,
- newChild.key
- )),
- (newChild["return"] = returnFiber),
- newChild
- );
- expirationTime = createFiberFromElement(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- expirationTime.ref = coerceRef(null, newChild);
- expirationTime["return"] = returnFiber;
- return expirationTime;
- case REACT_CALL_TYPE:
- return (
- (newChild = createFiberFromCall(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- )),
- (newChild["return"] = returnFiber),
- newChild
- );
- case REACT_RETURN_TYPE:
return (
- (expirationTime = createFiberFromReturn(
+ (expirationTime = createFiberFromElement(
newChild,
returnFiber.internalContextTag,
expirationTime
)),
- (expirationTime.type = newChild.value),
+ (expirationTime.ref = coerceRef(null, newChild)),
(expirationTime["return"] = returnFiber),
expirationTime
);
@@ -2632,14 +2744,6 @@ function ChildReconciler(shouldTrackSideEffects) {
)
: updateElement(returnFiber, oldFiber, newChild, expirationTime)
: null;
- case REACT_CALL_TYPE:
- return newChild.key === key
- ? updateCall(returnFiber, oldFiber, newChild, expirationTime)
- : null;
- case REACT_RETURN_TYPE:
- return null === key
- ? updateReturn(returnFiber, oldFiber, newChild, expirationTime)
- : null;
case REACT_PORTAL_TYPE:
return newChild.key === key
? updatePortal(returnFiber, oldFiber, newChild, expirationTime)
@@ -2699,24 +2803,6 @@ function ChildReconciler(shouldTrackSideEffects) {
expirationTime
)
);
- case REACT_CALL_TYPE:
- return (
- (existingChildren =
- existingChildren.get(
- null === newChild.key ? newIdx : newChild.key
- ) || null),
- updateCall(returnFiber, existingChildren, newChild, expirationTime)
- );
- case REACT_RETURN_TYPE:
- return (
- (existingChildren = existingChildren.get(newIdx) || null),
- updateReturn(
- returnFiber,
- existingChildren,
- newChild,
- expirationTime
- )
- );
case REACT_PORTAL_TYPE:
return (
(existingChildren =
@@ -2973,72 +3059,14 @@ function ChildReconciler(shouldTrackSideEffects) {
)),
(currentFirstChild["return"] = returnFiber),
(returnFiber = currentFirstChild))
- : ((expirationTime = createFiberFromElement(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- )),
- (expirationTime.ref = coerceRef(currentFirstChild, newChild)),
- (expirationTime["return"] = returnFiber),
- (returnFiber = expirationTime));
- }
- return placeSingleChild(returnFiber);
- case REACT_CALL_TYPE:
- a: {
- for (isObject = newChild.key; null !== currentFirstChild; ) {
- if (currentFirstChild.key === isObject)
- if (7 === currentFirstChild.tag) {
- deleteRemainingChildren(
- returnFiber,
- currentFirstChild.sibling
- );
- currentFirstChild = useFiber(
- currentFirstChild,
- newChild,
- expirationTime
- );
- currentFirstChild["return"] = returnFiber;
- returnFiber = currentFirstChild;
- break a;
- } else {
- deleteRemainingChildren(returnFiber, currentFirstChild);
- break;
- }
- else deleteChild(returnFiber, currentFirstChild);
- currentFirstChild = currentFirstChild.sibling;
- }
- currentFirstChild = createFiberFromCall(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- currentFirstChild["return"] = returnFiber;
- returnFiber = currentFirstChild;
- }
- return placeSingleChild(returnFiber);
- case REACT_RETURN_TYPE:
- a: {
- if (null !== currentFirstChild)
- if (9 === currentFirstChild.tag) {
- deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
- currentFirstChild = useFiber(
- currentFirstChild,
- null,
+ : ((expirationTime = createFiberFromElement(
+ newChild,
+ returnFiber.internalContextTag,
expirationTime
- );
- currentFirstChild.type = newChild.value;
- currentFirstChild["return"] = returnFiber;
- returnFiber = currentFirstChild;
- break a;
- } else deleteRemainingChildren(returnFiber, currentFirstChild);
- currentFirstChild = createFiberFromReturn(
- newChild,
- returnFiber.internalContextTag,
- expirationTime
- );
- currentFirstChild.type = newChild.value;
- currentFirstChild["return"] = returnFiber;
- returnFiber = currentFirstChild;
+ )),
+ (expirationTime.ref = coerceRef(currentFirstChild, newChild)),
+ (expirationTime["return"] = returnFiber),
+ (returnFiber = expirationTime));
}
return placeSingleChild(returnFiber);
case REACT_PORTAL_TYPE:
@@ -3131,7 +3159,22 @@ function ChildReconciler(shouldTrackSideEffects) {
};
}
var reconcileChildFibers = ChildReconciler(!0),
- mountChildFibers = ChildReconciler(!1);
+ mountChildFibers = ChildReconciler(!1),
+ stack = [],
+ index$1 = -1;
+function popProvider(providerFiber) {
+ stack[index$1] = null;
+ --index$1;
+ providerFiber = providerFiber.type.context;
+ if (0 > index$1)
+ (providerFiber.currentValue = providerFiber.defaultValue),
+ (providerFiber.changedBits = 0);
+ else {
+ var previousProviderFiber = stack[index$1];
+ providerFiber.currentValue = previousProviderFiber.pendingProps.value;
+ providerFiber.changedBits = previousProviderFiber.stateNode;
+ }
+}
function ReactFiberBeginWork(
config,
hostContext,
@@ -3176,7 +3219,10 @@ function ReactFiberBeginWork(
);
shouldUpdate = workInProgress.stateNode;
ReactCurrentOwner.current = workInProgress;
- debugRenderPhaseSideEffects && shouldUpdate.render();
+ (debugRenderPhaseSideEffects ||
+ (debugRenderPhaseSideEffectsForStrictMode &&
+ workInProgress.internalContextTag & 2)) &&
+ shouldUpdate.render();
var nextChildren = shouldUpdate.render();
workInProgress.effectTag |= 1;
reconcileChildren(current, workInProgress, nextChildren);
@@ -3235,11 +3281,17 @@ function ReactFiberBeginWork(
workInProgress,
workInProgress.stateNode.containerInfo
);
+ break;
+ case 13:
+ (index$1 += 1),
+ (stack[index$1] = workInProgress),
+ (current = workInProgress.type.context),
+ (current.currentValue = workInProgress.pendingProps.value),
+ (current.changedBits = workInProgress.stateNode);
}
return null;
}
var shouldSetTextContent = config.shouldSetTextContent,
- useSyncScheduling = config.useSyncScheduling,
shouldDeprioritizeSubtree = config.shouldDeprioritizeSubtree,
pushHostContext = hostContext.pushHostContext,
pushHostContainer = hostContext.pushHostContainer,
@@ -3258,6 +3310,7 @@ function ReactFiberBeginWork(
}
);
var adoptClassInstance = config.adoptClassInstance,
+ callGetDerivedStateFromProps = config.callGetDerivedStateFromProps,
constructClassInstance = config.constructClassInstance,
mountClassInstance = config.mountClassInstance,
updateClassInstance = config.updateClassInstance;
@@ -3282,8 +3335,25 @@ function ReactFiberBeginWork(
workInProgress.effectTag |= 1;
"object" === typeof fn &&
null !== fn &&
- "function" === typeof fn.render
- ? ((workInProgress.tag = 2),
+ "function" === typeof fn.render &&
+ void 0 === fn.$$typeof
+ ? ((unmaskedContext = workInProgress.type),
+ (workInProgress.tag = 2),
+ (workInProgress.memoizedState =
+ null !== fn.state && void 0 !== fn.state ? fn.state : null),
+ "function" === typeof unmaskedContext.getDerivedStateFromProps &&
+ ((props = callGetDerivedStateFromProps(
+ workInProgress,
+ fn,
+ props
+ )),
+ null !== props &&
+ void 0 !== props &&
+ (workInProgress.memoizedState = Object.assign(
+ {},
+ workInProgress.memoizedState,
+ props
+ ))),
(props = pushContextProvider(workInProgress)),
adoptClassInstance(workInProgress, fn),
mountClassInstance(workInProgress, renderExpirationTime),
@@ -3395,10 +3465,10 @@ function ReactFiberBeginWork(
shouldSetTextContent(props, unmaskedContext) &&
(workInProgress.effectTag |= 16),
markRef(current, workInProgress),
- 2147483647 !== renderExpirationTime &&
- !useSyncScheduling &&
+ 1073741823 !== renderExpirationTime &&
+ workInProgress.internalContextTag & 1 &&
shouldDeprioritizeSubtree(props, fn)
- ? ((workInProgress.expirationTime = 2147483647),
+ ? ((workInProgress.expirationTime = 1073741823),
(current = null))
: (reconcileChildren(current, workInProgress, memoizedProps),
(workInProgress.memoizedProps = fn),
@@ -3431,7 +3501,7 @@ function ReactFiberBeginWork(
)
: reconcileChildFibers(
workInProgress,
- workInProgress.stateNode,
+ current.stateNode,
fn,
renderExpirationTime
)),
@@ -3469,6 +3539,24 @@ function ReactFiberBeginWork(
return (
(renderExpirationTime = workInProgress.pendingProps),
didPerformWorkStackCursor.current ||
+ workInProgress.memoizedProps !== renderExpirationTime
+ ? (reconcileChildren(
+ current,
+ workInProgress,
+ renderExpirationTime
+ ),
+ (workInProgress.memoizedProps = renderExpirationTime),
+ (current = workInProgress.child))
+ : (current = bailoutOnAlreadyFinishedWork(
+ current,
+ workInProgress
+ )),
+ current
+ );
+ case 11:
+ return (
+ (renderExpirationTime = workInProgress.pendingProps.children),
+ didPerformWorkStackCursor.current ||
(null !== renderExpirationTime &&
workInProgress.memoizedProps !== renderExpirationTime)
? (reconcileChildren(
@@ -3484,6 +3572,10 @@ function ReactFiberBeginWork(
)),
current
);
+ case 13:
+ return null;
+ case 12:
+ return null;
default:
invariant(
!1,
@@ -3611,7 +3703,8 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) {
type,
oldProps,
newProps,
- renderExpirationTime
+ renderExpirationTime,
+ currentHostContext
);
current.ref !== workInProgress.ref &&
(workInProgress.effectTag |= 128);
@@ -3632,40 +3725,52 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) {
current
) && markUpdate(workInProgress);
else {
- current = createInstance(
+ oldProps = createInstance(
type,
newProps,
renderExpirationTime,
current,
workInProgress
);
- a: for (oldProps = workInProgress.child; null !== oldProps; ) {
- if (5 === oldProps.tag || 6 === oldProps.tag)
- appendInitialChild(current, oldProps.stateNode);
- else if (4 !== oldProps.tag && null !== oldProps.child) {
- oldProps.child["return"] = oldProps;
- oldProps = oldProps.child;
+ a: for (
+ currentHostContext = workInProgress.child;
+ null !== currentHostContext;
+
+ ) {
+ if (
+ 5 === currentHostContext.tag ||
+ 6 === currentHostContext.tag
+ )
+ appendInitialChild(oldProps, currentHostContext.stateNode);
+ else if (
+ 4 !== currentHostContext.tag &&
+ null !== currentHostContext.child
+ ) {
+ currentHostContext.child["return"] = currentHostContext;
+ currentHostContext = currentHostContext.child;
continue;
}
- if (oldProps === workInProgress) break;
- for (; null === oldProps.sibling; ) {
+ if (currentHostContext === workInProgress) break;
+ for (; null === currentHostContext.sibling; ) {
if (
- null === oldProps["return"] ||
- oldProps["return"] === workInProgress
+ null === currentHostContext["return"] ||
+ currentHostContext["return"] === workInProgress
)
break a;
- oldProps = oldProps["return"];
+ currentHostContext = currentHostContext["return"];
}
- oldProps.sibling["return"] = oldProps["return"];
- oldProps = oldProps.sibling;
+ currentHostContext.sibling["return"] =
+ currentHostContext["return"];
+ currentHostContext = currentHostContext.sibling;
}
finalizeInitialChildren(
- current,
+ oldProps,
type,
newProps,
- renderExpirationTime
+ renderExpirationTime,
+ current
) && markUpdate(workInProgress);
- workInProgress.stateNode = current;
+ workInProgress.stateNode = oldProps;
}
null !== workInProgress.ref && (workInProgress.effectTag |= 128);
}
@@ -3716,7 +3821,7 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) {
) {
if (5 === oldProps.tag || 6 === oldProps.tag || 4 === oldProps.tag)
invariant(!1, "A call cannot have host component children.");
- else if (9 === oldProps.tag) type.push(oldProps.type);
+ else if (9 === oldProps.tag) type.push(oldProps.pendingProps.value);
else if (null !== oldProps.child) {
oldProps.child["return"] = oldProps;
oldProps = oldProps.child;
@@ -3748,12 +3853,18 @@ function ReactFiberCompleteWork(config, hostContext, hydrationContext) {
return null;
case 10:
return null;
+ case 11:
+ return null;
case 4:
return (
popHostContainer(workInProgress),
updateHostContainer(workInProgress),
null
);
+ case 13:
+ return popProvider(workInProgress), null;
+ case 12:
+ return null;
case 0:
invariant(
!1,
@@ -4063,11 +4174,18 @@ function ReactFiberCommitWork(config, captureError) {
break;
case 3:
instance = finishedWork.updateQueue;
- null !== instance &&
- commitCallbacks(
- instance,
- null !== finishedWork.child ? finishedWork.child.stateNode : null
- );
+ if (null !== instance) {
+ current = null;
+ if (null !== finishedWork.child)
+ switch (finishedWork.child.tag) {
+ case 5:
+ current = getPublicInstance(finishedWork.child.stateNode);
+ break;
+ case 2:
+ current = finishedWork.child.stateNode;
+ }
+ commitCallbacks(instance, current);
+ }
break;
case 5:
instance = finishedWork.stateNode;
@@ -4162,7 +4280,7 @@ function ReactFiberHostContext(config) {
}
function ReactFiberHydrationContext(config) {
function deleteHydratableInstance(returnFiber, instance) {
- var fiber = createFiber(5, null, null, 0);
+ var fiber = new FiberNode(5, null, null, 0);
fiber.type = "DELETED";
fiber.stateNode = instance;
fiber["return"] = returnFiber;
@@ -4328,8 +4446,8 @@ function ReactFiberScheduler(config) {
siblingFiber = workInProgress$jscomp$0.sibling;
var workInProgress = workInProgress$jscomp$0;
if (
- 2147483647 === nextRenderExpirationTime ||
- 2147483647 !== workInProgress.expirationTime
+ 1073741823 === nextRenderExpirationTime ||
+ 1073741823 !== workInProgress.expirationTime
) {
if (2 !== workInProgress.tag && 3 !== workInProgress.tag)
var newExpirationTime = 0;
@@ -4437,6 +4555,12 @@ function ReactFiberScheduler(config) {
previousContext = emptyObject;
contextStackCursor.current = emptyObject;
didPerformWorkStackCursor.current = !1;
+ for (var i = index$1; -1 < i; i--) {
+ var context = stack[i].type.context;
+ context.currentValue = context.defaultValue;
+ context.changedBits = 0;
+ stack[i] = null;
+ }
resetHostContainer();
nextRoot = root;
nextRenderExpirationTime = expirationTime;
@@ -4446,52 +4570,54 @@ function ReactFiberScheduler(config) {
expirationTime
);
}
- var didError = !1,
- error = null;
+ context = !1;
+ var error = null;
try {
workLoop(expirationTime);
} catch (e) {
- (didError = !0), (error = e);
+ (context = !0), (error = e);
}
- for (; didError; ) {
+ for (; context; ) {
if (didFatal) {
firstUncaughtError = error;
break;
}
- var failedWork = nextUnitOfWork;
- if (null === failedWork) didFatal = !0;
+ i = nextUnitOfWork;
+ if (null === i) didFatal = !0;
else {
- var boundary = captureError(failedWork, error);
+ var boundary = captureError(i, error);
invariant(
null !== boundary,
"Should have found an error boundary. This error is likely caused by a bug in React. Please file an issue."
);
if (!didFatal) {
try {
- didError = boundary;
+ context = boundary;
error = expirationTime;
- for (boundary = didError; null !== failedWork; ) {
- switch (failedWork.tag) {
+ for (boundary = context; null !== i; ) {
+ switch (i.tag) {
case 2:
- popContextProvider(failedWork);
+ popContextProvider(i);
break;
case 5:
- popHostContext(failedWork);
+ popHostContext(i);
break;
case 3:
- popHostContainer(failedWork);
+ popHostContainer(i);
break;
case 4:
- popHostContainer(failedWork);
+ popHostContainer(i);
+ break;
+ case 13:
+ popProvider(i);
}
- if (failedWork === boundary || failedWork.alternate === boundary)
- break;
- failedWork = failedWork["return"];
+ if (i === boundary || i.alternate === boundary) break;
+ i = i["return"];
}
- nextUnitOfWork = performFailedUnitOfWork(didError);
+ nextUnitOfWork = performFailedUnitOfWork(context);
workLoop(error);
} catch (e) {
- didError = !0;
+ context = !0;
error = e;
continue;
}
@@ -4587,10 +4713,24 @@ function ReactFiberScheduler(config) {
};
capturedErrors.set(boundary, error$jscomp$0);
try {
- if (!1 !== showDialog(error$jscomp$0)) {
- var error = error$jscomp$0.error;
- (error && error.suppressReactErrorLogging) || console.error(error);
- }
+ var componentStack = error$jscomp$0.componentStack,
+ error = error$jscomp$0.error;
+ if (error instanceof Error) {
+ var message = error.message,
+ name = error.name;
+ var errorToHandle = error;
+ try {
+ errorToHandle.message =
+ (message ? name + ": " + message : name) +
+ "\n\nThis error is located at:" +
+ componentStack;
+ } catch (e) {}
+ } else
+ errorToHandle =
+ "string" === typeof error
+ ? Error(error + "\n\nThis error is located at:" + componentStack)
+ : Error("Unspecified error at:" + componentStack);
+ ExceptionsManager.handleException(errorToHandle, !1);
} catch (e) {
(e && e.suppressReactErrorLogging) || console.error(e);
}
@@ -4626,9 +4766,7 @@ function ReactFiberScheduler(config) {
? expirationContext
: isWorking
? isCommitting ? 1 : nextRenderExpirationTime
- : !useSyncScheduling || fiber.internalContextTag & 1
- ? computeAsyncExpiration()
- : 1;
+ : fiber.internalContextTag & 1 ? computeAsyncExpiration() : 1;
}
function scheduleWork(fiber, expirationTime) {
return scheduleWorkImpl(fiber, expirationTime, !1);
@@ -5016,7 +5154,6 @@ function ReactFiberScheduler(config) {
now = config.now,
scheduleDeferredCallback = config.scheduleDeferredCallback,
cancelDeferredCallback = config.cancelDeferredCallback,
- useSyncScheduling = config.useSyncScheduling,
prepareForCommit = config.prepareForCommit,
resetAfterCommit = config.resetAfterCommit,
startTime = now(),
@@ -5053,7 +5190,6 @@ function ReactFiberScheduler(config) {
nestedUpdateCount = 0,
timeHeuristicForUnitOfWork = 1;
return {
- computeAsyncExpiration: computeAsyncExpiration,
computeExpirationForFiber: computeExpirationForFiber,
scheduleWork: scheduleWork,
requestWork: requestWork,
@@ -5186,14 +5322,13 @@ function ReactFiberReconciler$1(config) {
}
var getPublicInstance = config.getPublicInstance;
config = ReactFiberScheduler(config);
- var computeAsyncExpiration = config.computeAsyncExpiration,
- computeExpirationForFiber = config.computeExpirationForFiber,
+ var computeExpirationForFiber = config.computeExpirationForFiber,
scheduleWork = config.scheduleWork;
return {
- createContainer: function(containerInfo, hydrate) {
- var uninitializedFiber = createFiber(3, null, 0);
+ createContainer: function(containerInfo, isAsync, hydrate) {
+ isAsync = new FiberNode(3, null, null, isAsync ? 3 : 0);
containerInfo = {
- current: uninitializedFiber,
+ current: isAsync,
containerInfo: containerInfo,
pendingChildren: null,
remainingExpirationTime: 0,
@@ -5205,22 +5340,15 @@ function ReactFiberReconciler$1(config) {
firstBatch: null,
nextScheduledRoot: null
};
- return (uninitializedFiber.stateNode = containerInfo);
+ return (isAsync.stateNode = containerInfo);
},
updateContainer: function(element, container, parentComponent, callback) {
- var current = container.current;
- current =
- null != element &&
- null != element.type &&
- null != element.type.prototype &&
- !0 === element.type.prototype.unstable_isAsyncReactComponent
- ? computeAsyncExpiration()
- : computeExpirationForFiber(current);
+ var expirationTime = computeExpirationForFiber(container.current);
return updateContainerAtExpirationTime(
element,
container,
parentComponent,
- current,
+ expirationTime,
callback
);
},
@@ -5356,463 +5484,321 @@ function recursivelyUncacheFiberNode(node) {
node._children.forEach(recursivelyUncacheFiberNode));
}
var NativeRenderer = reactReconciler({
- appendInitialChild: function(parentInstance, child) {
- parentInstance._children.push(child);
- },
- createInstance: function(
- type,
- props,
- rootContainerInstance,
- hostContext,
- internalInstanceHandle
- ) {
- hostContext = ReactNativeTagHandles.allocateTag();
- if (viewConfigs.has(type)) var viewConfig = viewConfigs.get(type);
- else
- (viewConfig = viewConfigCallbacks.get(type)),
- invariant(
- "function" === typeof viewConfig,
- "View config not found for name %s",
- type
- ),
- viewConfigCallbacks.set(type, null),
- (viewConfig = viewConfig()),
- viewConfigs.set(type, viewConfig);
- invariant(viewConfig, "View config not found for name %s", type);
- type = viewConfig;
- viewConfig = diffProperties(
- null,
- emptyObject$1,
+ appendInitialChild: function(parentInstance, child) {
+ parentInstance._children.push(child);
+ },
+ createInstance: function(
+ type,
props,
- type.validAttributes
- );
- UIManager.createView(
+ rootContainerInstance,
hostContext,
- type.uiViewClassName,
+ internalInstanceHandle
+ ) {
+ hostContext = ReactNativeTagHandles.allocateTag();
+ if (viewConfigs.has(type)) var viewConfig = viewConfigs.get(type);
+ else
+ (viewConfig = viewConfigCallbacks.get(type)),
+ invariant(
+ "function" === typeof viewConfig,
+ "View config not found for name %s",
+ type
+ ),
+ viewConfigCallbacks.set(type, null),
+ (viewConfig = viewConfig()),
+ viewConfigs.set(type, viewConfig);
+ invariant(viewConfig, "View config not found for name %s", type);
+ type = viewConfig;
+ viewConfig = diffProperties(
+ null,
+ emptyObject$1,
+ props,
+ type.validAttributes
+ );
+ UIManager.createView(
+ hostContext,
+ type.uiViewClassName,
+ rootContainerInstance,
+ viewConfig
+ );
+ rootContainerInstance = new ReactNativeFiberHostComponent(
+ hostContext,
+ type
+ );
+ instanceCache[hostContext] = internalInstanceHandle;
+ instanceProps[hostContext] = props;
+ return rootContainerInstance;
+ },
+ createTextInstance: function(
+ text,
rootContainerInstance,
- viewConfig
- );
- rootContainerInstance = new ReactNativeFiberHostComponent(
hostContext,
- type
- );
- instanceCache[hostContext] = internalInstanceHandle;
- instanceProps[hostContext] = props;
- return rootContainerInstance;
- },
- createTextInstance: function(
- text,
- rootContainerInstance,
- hostContext,
- internalInstanceHandle
- ) {
- hostContext = ReactNativeTagHandles.allocateTag();
- UIManager.createView(hostContext, "RCTRawText", rootContainerInstance, {
- text: text
- });
- instanceCache[hostContext] = internalInstanceHandle;
- return hostContext;
- },
- finalizeInitialChildren: function(parentInstance) {
- if (0 === parentInstance._children.length) return !1;
- var nativeTags = parentInstance._children.map(function(child) {
- return "number" === typeof child ? child : child._nativeTag;
- });
- UIManager.setChildren(parentInstance._nativeTag, nativeTags);
- return !1;
- },
- getRootHostContext: function() {
- return emptyObject;
- },
- getChildHostContext: function() {
- return emptyObject;
- },
- getPublicInstance: function(instance) {
- return instance;
- },
- now: now,
- prepareForCommit: function() {},
- prepareUpdate: function() {
- return emptyObject;
- },
- resetAfterCommit: function() {},
- scheduleDeferredCallback: function(callback) {
- scheduledCallback = callback;
- return setTimeout(setTimeoutCallback, 1);
- },
- cancelDeferredCallback: function(callbackID) {
- scheduledCallback = null;
- clearTimeout(callbackID);
- },
- shouldDeprioritizeSubtree: function() {
- return !1;
- },
- shouldSetTextContent: function() {
- return !1;
- },
- useSyncScheduling: !0,
- mutation: {
- appendChild: function(parentInstance, child) {
- var childTag = "number" === typeof child ? child : child._nativeTag,
- children = parentInstance._children,
- index = children.indexOf(child);
- 0 <= index
- ? (children.splice(index, 1),
- children.push(child),
- UIManager.manageChildren(
- parentInstance._nativeTag,
- [index],
- [children.length - 1],
- [],
- [],
- []
- ))
- : (children.push(child),
- UIManager.manageChildren(
- parentInstance._nativeTag,
- [],
- [],
- [childTag],
- [children.length - 1],
- []
- ));
+ internalInstanceHandle
+ ) {
+ hostContext = ReactNativeTagHandles.allocateTag();
+ UIManager.createView(hostContext, "RCTRawText", rootContainerInstance, {
+ text: text
+ });
+ instanceCache[hostContext] = internalInstanceHandle;
+ return hostContext;
},
- appendChildToContainer: function(parentInstance, child) {
- UIManager.setChildren(parentInstance, [
- "number" === typeof child ? child : child._nativeTag
- ]);
+ finalizeInitialChildren: function(parentInstance) {
+ if (0 === parentInstance._children.length) return !1;
+ var nativeTags = parentInstance._children.map(function(child) {
+ return "number" === typeof child ? child : child._nativeTag;
+ });
+ UIManager.setChildren(parentInstance._nativeTag, nativeTags);
+ return !1;
},
- commitTextUpdate: function(textInstance, oldText, newText) {
- UIManager.updateView(textInstance, "RCTRawText", { text: newText });
+ getRootHostContext: function() {
+ return emptyObject;
},
- commitMount: function() {},
- commitUpdate: function(
- instance,
- updatePayloadTODO,
- type,
- oldProps,
- newProps
- ) {
- updatePayloadTODO = instance.viewConfig;
- instanceProps[instance._nativeTag] = newProps;
- oldProps = diffProperties(
- null,
- oldProps,
- newProps,
- updatePayloadTODO.validAttributes
- );
- null != oldProps &&
- UIManager.updateView(
- instance._nativeTag,
- updatePayloadTODO.uiViewClassName,
- oldProps
- );
+ getChildHostContext: function() {
+ return emptyObject;
},
- insertBefore: function(parentInstance, child, beforeChild) {
- var children = parentInstance._children,
- index = children.indexOf(child);
- 0 <= index
- ? (children.splice(index, 1),
- (beforeChild = children.indexOf(beforeChild)),
- children.splice(beforeChild, 0, child),
- UIManager.manageChildren(
- parentInstance._nativeTag,
- [index],
- [beforeChild],
- [],
- [],
- []
- ))
- : ((index = children.indexOf(beforeChild)),
- children.splice(index, 0, child),
- UIManager.manageChildren(
- parentInstance._nativeTag,
- [],
- [],
- ["number" === typeof child ? child : child._nativeTag],
- [index],
- []
- ));
+ getPublicInstance: function(instance) {
+ return instance;
},
- insertInContainerBefore: function(parentInstance) {
- invariant(
- "number" !== typeof parentInstance,
- "Container does not support insertBefore operation"
- );
+ now: now,
+ prepareForCommit: function() {},
+ prepareUpdate: function() {
+ return emptyObject;
},
- removeChild: function(parentInstance, child) {
- recursivelyUncacheFiberNode(child);
- var children = parentInstance._children;
- child = children.indexOf(child);
- children.splice(child, 1);
- UIManager.manageChildren(
- parentInstance._nativeTag,
- [],
- [],
- [],
- [],
- [child]
- );
+ resetAfterCommit: function() {},
+ scheduleDeferredCallback: function(callback) {
+ scheduledCallback = callback;
+ return setTimeout(setTimeoutCallback, 1);
},
- removeChildFromContainer: function(parentInstance, child) {
- recursivelyUncacheFiberNode(child);
- UIManager.manageChildren(parentInstance, [], [], [], [], [0]);
+ cancelDeferredCallback: function(callbackID) {
+ scheduledCallback = null;
+ clearTimeout(callbackID);
},
- resetTextContent: function() {}
- }
-});
-function findNodeHandle(componentOrHandle) {
- if (null == componentOrHandle) return null;
- if ("number" === typeof componentOrHandle) return componentOrHandle;
- var internalInstance = componentOrHandle._reactInternalFiber;
- if (internalInstance)
- return NativeRenderer.findHostInstance(internalInstance);
- if (componentOrHandle) return componentOrHandle;
- invariant(
- ("object" === typeof componentOrHandle &&
- "_nativeTag" in componentOrHandle) ||
- (null != componentOrHandle.render &&
- "function" === typeof componentOrHandle.render),
- "findNodeHandle(...): Argument is not a component (type: %s, keys: %s)",
- typeof componentOrHandle,
- Object.keys(componentOrHandle)
- );
- invariant(
- !1,
- "findNodeHandle(...): Unable to find node handle for unmounted component."
- );
-}
-function findNumericNodeHandleFiber(componentOrHandle) {
- componentOrHandle = findNodeHandle(componentOrHandle);
- return null == componentOrHandle || "number" === typeof componentOrHandle
- ? componentOrHandle
- : componentOrHandle._nativeTag;
-}
-function _inherits(subClass, superClass) {
- if ("function" !== typeof superClass && null !== superClass)
- throw new TypeError(
- "Super expression must either be null or a function, not " +
- typeof superClass
- );
- subClass.prototype = Object.create(superClass && superClass.prototype, {
- constructor: {
- value: subClass,
- enumerable: !1,
- writable: !0,
- configurable: !0
- }
- });
- superClass &&
- (Object.setPrototypeOf
- ? Object.setPrototypeOf(subClass, superClass)
- : (subClass.__proto__ = superClass));
-}
-var ReactNativeComponent = (function(_React$Component) {
- function ReactNativeComponent() {
- if (!(this instanceof ReactNativeComponent))
- throw new TypeError("Cannot call a class as a function");
- var call = _React$Component.apply(this, arguments);
- if (!this)
- throw new ReferenceError(
- "this hasn't been initialised - super() hasn't been called"
- );
- return !call || ("object" !== typeof call && "function" !== typeof call)
- ? this
- : call;
- }
- _inherits(ReactNativeComponent, _React$Component);
- ReactNativeComponent.prototype.blur = function() {
- TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
- };
- ReactNativeComponent.prototype.focus = function() {
- TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
- };
- ReactNativeComponent.prototype.measure = function(callback) {
- UIManager.measure(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
- };
- ReactNativeComponent.prototype.measureInWindow = function(callback) {
- UIManager.measureInWindow(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
- };
- ReactNativeComponent.prototype.measureLayout = function(
- relativeToNativeNode,
- onSuccess,
- onFail
- ) {
- UIManager.measureLayout(
- findNumericNodeHandleFiber(this),
- relativeToNativeNode,
- mountSafeCallback(this, onFail),
- mountSafeCallback(this, onSuccess)
- );
- };
- ReactNativeComponent.prototype.setNativeProps = function(nativeProps) {
- var maybeInstance = void 0;
- try {
- maybeInstance = findNodeHandle(this);
- } catch (error) {}
- if (null != maybeInstance) {
- var viewConfig = maybeInstance.viewConfig;
- nativeProps = diffProperties(
+ shouldDeprioritizeSubtree: function() {
+ return !1;
+ },
+ shouldSetTextContent: function() {
+ return !1;
+ },
+ mutation: {
+ appendChild: function(parentInstance, child) {
+ var childTag = "number" === typeof child ? child : child._nativeTag,
+ children = parentInstance._children,
+ index = children.indexOf(child);
+ 0 <= index
+ ? (children.splice(index, 1),
+ children.push(child),
+ UIManager.manageChildren(
+ parentInstance._nativeTag,
+ [index],
+ [children.length - 1],
+ [],
+ [],
+ []
+ ))
+ : (children.push(child),
+ UIManager.manageChildren(
+ parentInstance._nativeTag,
+ [],
+ [],
+ [childTag],
+ [children.length - 1],
+ []
+ ));
+ },
+ appendChildToContainer: function(parentInstance, child) {
+ UIManager.setChildren(parentInstance, [
+ "number" === typeof child ? child : child._nativeTag
+ ]);
+ },
+ commitTextUpdate: function(textInstance, oldText, newText) {
+ UIManager.updateView(textInstance, "RCTRawText", { text: newText });
+ },
+ commitMount: function() {},
+ commitUpdate: function(
+ instance,
+ updatePayloadTODO,
+ type,
+ oldProps,
+ newProps
+ ) {
+ updatePayloadTODO = instance.viewConfig;
+ instanceProps[instance._nativeTag] = newProps;
+ oldProps = diffProperties(
null,
- emptyObject$1,
- nativeProps,
- viewConfig.validAttributes
+ oldProps,
+ newProps,
+ updatePayloadTODO.validAttributes
);
- null != nativeProps &&
+ null != oldProps &&
UIManager.updateView(
- maybeInstance._nativeTag,
- viewConfig.uiViewClassName,
- nativeProps
+ instance._nativeTag,
+ updatePayloadTODO.uiViewClassName,
+ oldProps
);
- }
- };
- return ReactNativeComponent;
- })(React.Component),
- getInspectorDataForViewTag = void 0;
-getInspectorDataForViewTag = function() {
- invariant(!1, "getInspectorDataForViewTag() is not available in production");
-};
-fiberBatchedUpdates = NativeRenderer.batchedUpdates;
-var roots = new Map();
-function fn$jscomp$inline_616(capturedError) {
- var componentStack = capturedError.componentStack,
- error = capturedError.error;
- if (error instanceof Error) {
- capturedError = error.message;
- var name = error.name;
- try {
- error.message =
- (capturedError ? name + ": " + capturedError : name) +
- "\n\nThis error is located at:" +
- componentStack;
- } catch (e) {}
- } else
- error =
- "string" === typeof error
- ? Error(error + "\n\nThis error is located at:" + componentStack)
- : Error("Unspecified error at:" + componentStack);
- ExceptionsManager.handleException(error, !1);
- return !1;
-}
-invariant(
- showDialog === defaultShowDialog,
- "The custom dialog was already injected."
-);
-invariant(
- "function" === typeof fn$jscomp$inline_616,
- "Injected showDialog() must be a function."
-);
-showDialog = fn$jscomp$inline_616;
-var ReactNativeRenderer = {
- NativeComponent: ReactNativeComponent,
- findNodeHandle: findNumericNodeHandleFiber,
- render: function(element, containerTag, callback) {
- var root = roots.get(containerTag);
- root ||
- ((root = NativeRenderer.createContainer(containerTag, !1)),
- roots.set(containerTag, root));
- NativeRenderer.updateContainer(element, root, null, callback);
- return NativeRenderer.getPublicRootInstance(root);
- },
- unmountComponentAtNode: function(containerTag) {
- var root = roots.get(containerTag);
- root &&
- NativeRenderer.updateContainer(null, root, null, function() {
- roots["delete"](containerTag);
- });
- },
- unmountComponentAtNodeAndRemoveContainer: function(containerTag) {
- ReactNativeRenderer.unmountComponentAtNode(containerTag);
- UIManager.removeRootView(containerTag);
- },
- createPortal: function(children, containerTag) {
- return createPortal(
- children,
- containerTag,
- null,
- 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null
- );
- },
- unstable_batchedUpdates: batchedUpdates,
- flushSync: NativeRenderer.flushSync,
- __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
- NativeMethodsMixin: {
- measure: function(callback) {
- UIManager.measure(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
- );
},
- measureInWindow: function(callback) {
- UIManager.measureInWindow(
- findNumericNodeHandleFiber(this),
- mountSafeCallback(this, callback)
+ insertBefore: function(parentInstance, child, beforeChild) {
+ var children = parentInstance._children,
+ index = children.indexOf(child);
+ 0 <= index
+ ? (children.splice(index, 1),
+ (beforeChild = children.indexOf(beforeChild)),
+ children.splice(beforeChild, 0, child),
+ UIManager.manageChildren(
+ parentInstance._nativeTag,
+ [index],
+ [beforeChild],
+ [],
+ [],
+ []
+ ))
+ : ((index = children.indexOf(beforeChild)),
+ children.splice(index, 0, child),
+ UIManager.manageChildren(
+ parentInstance._nativeTag,
+ [],
+ [],
+ ["number" === typeof child ? child : child._nativeTag],
+ [index],
+ []
+ ));
+ },
+ insertInContainerBefore: function(parentInstance) {
+ invariant(
+ "number" !== typeof parentInstance,
+ "Container does not support insertBefore operation"
);
},
- measureLayout: function(relativeToNativeNode, onSuccess, onFail) {
- UIManager.measureLayout(
- findNumericNodeHandleFiber(this),
- relativeToNativeNode,
- mountSafeCallback(this, onFail),
- mountSafeCallback(this, onSuccess)
+ removeChild: function(parentInstance, child) {
+ recursivelyUncacheFiberNode(child);
+ var children = parentInstance._children;
+ child = children.indexOf(child);
+ children.splice(child, 1);
+ UIManager.manageChildren(
+ parentInstance._nativeTag,
+ [],
+ [],
+ [],
+ [],
+ [child]
);
},
- setNativeProps: function(nativeProps) {
- var maybeInstance = void 0;
- try {
- maybeInstance = findNodeHandle(this);
- } catch (error) {}
- if (null != maybeInstance) {
- var viewConfig = maybeInstance.viewConfig;
- nativeProps = diffProperties(
- null,
- emptyObject$1,
- nativeProps,
- viewConfig.validAttributes
+ removeChildFromContainer: function(parentInstance, child) {
+ recursivelyUncacheFiberNode(child);
+ UIManager.manageChildren(parentInstance, [], [], [], [], [0]);
+ },
+ resetTextContent: function() {}
+ }
+ }),
+ getInspectorDataForViewTag = void 0;
+getInspectorDataForViewTag = function() {
+ invariant(!1, "getInspectorDataForViewTag() is not available in production");
+};
+findHostInstance = NativeRenderer.findHostInstance;
+fiberBatchedUpdates = NativeRenderer.batchedUpdates;
+var roots = new Map(),
+ ReactNativeRenderer = {
+ NativeComponent: ReactNativeComponent,
+ findNodeHandle: findNumericNodeHandleFiber,
+ render: function(element, containerTag, callback) {
+ var root = roots.get(containerTag);
+ root ||
+ ((root = NativeRenderer.createContainer(containerTag, !1, !1)),
+ roots.set(containerTag, root));
+ NativeRenderer.updateContainer(element, root, null, callback);
+ return NativeRenderer.getPublicRootInstance(root);
+ },
+ unmountComponentAtNode: function(containerTag) {
+ var root = roots.get(containerTag);
+ root &&
+ NativeRenderer.updateContainer(null, root, null, function() {
+ roots["delete"](containerTag);
+ });
+ },
+ unmountComponentAtNodeAndRemoveContainer: function(containerTag) {
+ ReactNativeRenderer.unmountComponentAtNode(containerTag);
+ UIManager.removeRootView(containerTag);
+ },
+ createPortal: function(children, containerTag) {
+ return createPortal(
+ children,
+ containerTag,
+ null,
+ 2 < arguments.length && void 0 !== arguments[2] ? arguments[2] : null
+ );
+ },
+ unstable_batchedUpdates: batchedUpdates,
+ flushSync: NativeRenderer.flushSync,
+ __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
+ NativeMethodsMixin: {
+ measure: function(callback) {
+ UIManager.measure(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ },
+ measureInWindow: function(callback) {
+ UIManager.measureInWindow(
+ findNumericNodeHandleFiber(this),
+ mountSafeCallback(this, callback)
+ );
+ },
+ measureLayout: function(relativeToNativeNode, onSuccess, onFail) {
+ UIManager.measureLayout(
+ findNumericNodeHandleFiber(this),
+ relativeToNativeNode,
+ mountSafeCallback(this, onFail),
+ mountSafeCallback(this, onSuccess)
);
- null != nativeProps &&
- UIManager.updateView(
- maybeInstance._nativeTag,
- viewConfig.uiViewClassName,
- nativeProps
+ },
+ setNativeProps: function(nativeProps) {
+ var maybeInstance = void 0;
+ try {
+ maybeInstance = findNodeHandle(this);
+ } catch (error) {}
+ if (null != maybeInstance) {
+ var viewConfig = maybeInstance.viewConfig;
+ nativeProps = diffProperties(
+ null,
+ emptyObject$1,
+ nativeProps,
+ viewConfig.validAttributes
);
+ null != nativeProps &&
+ UIManager.updateView(
+ maybeInstance._nativeTag,
+ viewConfig.uiViewClassName,
+ nativeProps
+ );
+ }
+ },
+ focus: function() {
+ TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
+ },
+ blur: function() {
+ TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
}
},
- focus: function() {
- TextInputState.focusTextInput(findNumericNodeHandleFiber(this));
+ ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin,
+ ReactGlobalSharedState: ReactGlobalSharedState,
+ ReactNativeComponentTree: ReactNativeComponentTree,
+ ReactNativePropRegistry: ReactNativePropRegistry,
+ TouchHistoryMath: TouchHistoryMath,
+ createReactNativeComponentClass: function(name, callback) {
+ invariant(
+ !viewConfigCallbacks.has(name),
+ "Tried to register two views with the same name %s",
+ name
+ );
+ viewConfigCallbacks.set(name, callback);
+ return name;
},
- blur: function() {
- TextInputState.blurTextInput(findNumericNodeHandleFiber(this));
+ takeSnapshot: function(view, options) {
+ "number" !== typeof view &&
+ "window" !== view &&
+ (view = findNumericNodeHandleFiber(view) || "window");
+ return UIManager.__takeSnapshot(view, options);
}
- },
- ReactNativeBridgeEventPlugin: ReactNativeBridgeEventPlugin,
- ReactGlobalSharedState: ReactGlobalSharedState,
- ReactNativeComponentTree: ReactNativeComponentTree,
- ReactNativePropRegistry: ReactNativePropRegistry,
- TouchHistoryMath: TouchHistoryMath,
- createReactNativeComponentClass: function(name, callback) {
- invariant(
- !viewConfigCallbacks.has(name),
- "Tried to register two views with the same name %s",
- name
- );
- viewConfigCallbacks.set(name, callback);
- return name;
- },
- takeSnapshot: function(view, options) {
- "number" !== typeof view &&
- "window" !== view &&
- (view = findNumericNodeHandleFiber(view) || "window");
- return UIManager.__takeSnapshot(view, options);
}
- }
-};
+ };
NativeRenderer.injectIntoDevTools({
findFiberByHostInstance: getInstanceFromTag,
getInspectorDataForViewTag: getInspectorDataForViewTag,
diff --git a/Libraries/Renderer/shims/ReactFeatureFlags.js b/Libraries/Renderer/shims/ReactFeatureFlags.js
index 276dc914254c55..ac1f582d08db94 100644
--- a/Libraries/Renderer/shims/ReactFeatureFlags.js
+++ b/Libraries/Renderer/shims/ReactFeatureFlags.js
@@ -9,8 +9,12 @@
'use strict';
-var ReactFeatureFlags = {
+const ReactFeatureFlags = {
debugRenderPhaseSideEffects: false,
+ // TODO (T25573762) Hook this up to a GK for Facaebook engineers (DEV + prod).
+ debugRenderPhaseSideEffectsForStrictMode: true,
+ // TODO (T25573607) Enable this warning once deprecation codemod has been run.
+ warnAboutDeprecatedLifecycles: false,
};
module.exports = ReactFeatureFlags;
diff --git a/Libraries/Renderer/shims/ReactTypes.js b/Libraries/Renderer/shims/ReactTypes.js
index 900582e5d5ac83..0e29678f35b000 100644
--- a/Libraries/Renderer/shims/ReactTypes.js
+++ b/Libraries/Renderer/shims/ReactTypes.js
@@ -10,11 +10,13 @@
export type ReactNode =
| React$Element
- | ReactCall
- | ReactReturn
+ | ReactCall
+ | ReactReturn
| ReactPortal
| ReactText
- | ReactFragment;
+ | ReactFragment
+ | ReactProvider
+ | ReactConsumer;
export type ReactFragment = ReactEmpty | Iterable;
@@ -24,18 +26,71 @@ export type ReactText = string | number;
export type ReactEmpty = null | void | boolean;
-export type ReactCall = {
+export type ReactCall = {
$$typeof: Symbol | number,
+ type: Symbol | number,
key: null | string,
- children: any,
- // This should be a more specific CallHandler
- handler: (props: any, returns: Array) => ReactNodeList,
- props: any,
+ ref: null,
+ props: {
+ props: any,
+ // This should be a more specific CallHandler
+ handler: (props: any, returns: Array) => ReactNodeList,
+ children?: ReactNodeList,
+ },
};
-export type ReactReturn = {
+export type ReactReturn = {
$$typeof: Symbol | number,
- value: mixed,
+ type: Symbol | number,
+ key: null,
+ ref: null,
+ props: {
+ value: V,
+ },
+};
+
+export type ReactProvider = {
+ $$typeof: Symbol | number,
+ type: ReactProviderType,
+ key: null | string,
+ ref: null,
+ props: {
+ value: T,
+ children?: ReactNodeList,
+ },
+};
+
+export type ReactProviderType = {
+ $$typeof: Symbol | number,
+ context: ReactContext,
+};
+
+export type ReactConsumer = {
+ $$typeof: Symbol | number,
+ type: ReactContext,
+ key: null | string,
+ ref: null,
+ props: {
+ render: (value: T) => ReactNodeList,
+ bits?: number,
+ },
+};
+
+export type ReactContext = {
+ $$typeof: Symbol | number,
+ provide(value: T, children: ReactNodeList, key?: string): ReactProvider,
+ consume(
+ render: (value: T) => ReactNodeList,
+ observedBits?: number,
+ key?: string,
+ ): ReactConsumer,
+ calculateChangedBits: ((a: T, b: T) => number) | null,
+ defaultValue: T,
+ currentValue: T,
+ changedBits: number,
+
+ // DEV only
+ _currentRenderer?: Object | null,
};
export type ReactPortal = {