| 1 | /**
|
|---|
| 2 | * @license React
|
|---|
| 3 | * react-dom.development.js
|
|---|
| 4 | *
|
|---|
| 5 | * Copyright (c) Facebook, Inc. and its affiliates.
|
|---|
| 6 | *
|
|---|
| 7 | * This source code is licensed under the MIT license found in the
|
|---|
| 8 | * LICENSE file in the root directory of this source tree.
|
|---|
| 9 | */
|
|---|
| 10 |
|
|---|
| 11 | 'use strict';
|
|---|
| 12 |
|
|---|
| 13 | if (process.env.NODE_ENV !== "production") {
|
|---|
| 14 | (function() {
|
|---|
| 15 |
|
|---|
| 16 | 'use strict';
|
|---|
| 17 |
|
|---|
| 18 | /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
|
|---|
| 19 | if (
|
|---|
| 20 | typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
|
|---|
| 21 | typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart ===
|
|---|
| 22 | 'function'
|
|---|
| 23 | ) {
|
|---|
| 24 | __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(new Error());
|
|---|
| 25 | }
|
|---|
| 26 | var React = require('react');
|
|---|
| 27 | var Scheduler = require('scheduler');
|
|---|
| 28 |
|
|---|
| 29 | var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|---|
| 30 |
|
|---|
| 31 | var suppressWarning = false;
|
|---|
| 32 | function setSuppressWarning(newSuppressWarning) {
|
|---|
| 33 | {
|
|---|
| 34 | suppressWarning = newSuppressWarning;
|
|---|
| 35 | }
|
|---|
| 36 | } // In DEV, calls to console.warn and console.error get replaced
|
|---|
| 37 | // by calls to these methods by a Babel plugin.
|
|---|
| 38 | //
|
|---|
| 39 | // In PROD (or in packages without access to React internals),
|
|---|
| 40 | // they are left as they are instead.
|
|---|
| 41 |
|
|---|
| 42 | function warn(format) {
|
|---|
| 43 | {
|
|---|
| 44 | if (!suppressWarning) {
|
|---|
| 45 | for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
|
|---|
| 46 | args[_key - 1] = arguments[_key];
|
|---|
| 47 | }
|
|---|
| 48 |
|
|---|
| 49 | printWarning('warn', format, args);
|
|---|
| 50 | }
|
|---|
| 51 | }
|
|---|
| 52 | }
|
|---|
| 53 | function error(format) {
|
|---|
| 54 | {
|
|---|
| 55 | if (!suppressWarning) {
|
|---|
| 56 | for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
|
|---|
| 57 | args[_key2 - 1] = arguments[_key2];
|
|---|
| 58 | }
|
|---|
| 59 |
|
|---|
| 60 | printWarning('error', format, args);
|
|---|
| 61 | }
|
|---|
| 62 | }
|
|---|
| 63 | }
|
|---|
| 64 |
|
|---|
| 65 | function printWarning(level, format, args) {
|
|---|
| 66 | // When changing this logic, you might want to also
|
|---|
| 67 | // update consoleWithStackDev.www.js as well.
|
|---|
| 68 | {
|
|---|
| 69 | var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
|---|
| 70 | var stack = ReactDebugCurrentFrame.getStackAddendum();
|
|---|
| 71 |
|
|---|
| 72 | if (stack !== '') {
|
|---|
| 73 | format += '%s';
|
|---|
| 74 | args = args.concat([stack]);
|
|---|
| 75 | } // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 76 |
|
|---|
| 77 |
|
|---|
| 78 | var argsWithFormat = args.map(function (item) {
|
|---|
| 79 | return String(item);
|
|---|
| 80 | }); // Careful: RN currently depends on this prefix
|
|---|
| 81 |
|
|---|
| 82 | argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it
|
|---|
| 83 | // breaks IE9: https://github.com/facebook/react/issues/13610
|
|---|
| 84 | // eslint-disable-next-line react-internal/no-production-logging
|
|---|
| 85 |
|
|---|
| 86 | Function.prototype.apply.call(console[level], console, argsWithFormat);
|
|---|
| 87 | }
|
|---|
| 88 | }
|
|---|
| 89 |
|
|---|
| 90 | var FunctionComponent = 0;
|
|---|
| 91 | var ClassComponent = 1;
|
|---|
| 92 | var IndeterminateComponent = 2; // Before we know whether it is function or class
|
|---|
| 93 |
|
|---|
| 94 | var HostRoot = 3; // Root of a host tree. Could be nested inside another node.
|
|---|
| 95 |
|
|---|
| 96 | var HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
|
|---|
| 97 |
|
|---|
| 98 | var HostComponent = 5;
|
|---|
| 99 | var HostText = 6;
|
|---|
| 100 | var Fragment = 7;
|
|---|
| 101 | var Mode = 8;
|
|---|
| 102 | var ContextConsumer = 9;
|
|---|
| 103 | var ContextProvider = 10;
|
|---|
| 104 | var ForwardRef = 11;
|
|---|
| 105 | var Profiler = 12;
|
|---|
| 106 | var SuspenseComponent = 13;
|
|---|
| 107 | var MemoComponent = 14;
|
|---|
| 108 | var SimpleMemoComponent = 15;
|
|---|
| 109 | var LazyComponent = 16;
|
|---|
| 110 | var IncompleteClassComponent = 17;
|
|---|
| 111 | var DehydratedFragment = 18;
|
|---|
| 112 | var SuspenseListComponent = 19;
|
|---|
| 113 | var ScopeComponent = 21;
|
|---|
| 114 | var OffscreenComponent = 22;
|
|---|
| 115 | var LegacyHiddenComponent = 23;
|
|---|
| 116 | var CacheComponent = 24;
|
|---|
| 117 | var TracingMarkerComponent = 25;
|
|---|
| 118 |
|
|---|
| 119 | // -----------------------------------------------------------------------------
|
|---|
| 120 |
|
|---|
| 121 | var enableClientRenderFallbackOnTextMismatch = true; // TODO: Need to review this code one more time before landing
|
|---|
| 122 | // the react-reconciler package.
|
|---|
| 123 |
|
|---|
| 124 | var enableNewReconciler = false; // Support legacy Primer support on internal FB www
|
|---|
| 125 |
|
|---|
| 126 | var enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics.
|
|---|
| 127 |
|
|---|
| 128 | var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber
|
|---|
| 129 |
|
|---|
| 130 | var enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz
|
|---|
| 131 | // React DOM Chopping Block
|
|---|
| 132 | //
|
|---|
| 133 | // Similar to main Chopping Block but only flags related to React DOM. These are
|
|---|
| 134 | // grouped because we will likely batch all of them into a single major release.
|
|---|
| 135 | // -----------------------------------------------------------------------------
|
|---|
| 136 | // Disable support for comment nodes as React DOM containers. Already disabled
|
|---|
| 137 | // in open source, but www codebase still relies on it. Need to remove.
|
|---|
| 138 |
|
|---|
| 139 | var disableCommentsAsDOMContainers = true; // Disable javascript: URL strings in href for XSS protection.
|
|---|
| 140 | // and client rendering, mostly to allow JSX attributes to apply to the custom
|
|---|
| 141 | // element's object properties instead of only HTML attributes.
|
|---|
| 142 | // https://github.com/facebook/react/issues/11347
|
|---|
| 143 |
|
|---|
| 144 | var enableCustomElementPropertySupport = false; // Disables children for <textarea> elements
|
|---|
| 145 | var warnAboutStringRefs = true; // -----------------------------------------------------------------------------
|
|---|
| 146 | // Debugging and DevTools
|
|---|
| 147 | // -----------------------------------------------------------------------------
|
|---|
| 148 | // Adds user timing marks for e.g. state updates, suspense, and work loop stuff,
|
|---|
| 149 | // for an experimental timeline tool.
|
|---|
| 150 |
|
|---|
| 151 | var enableSchedulingProfiler = true; // Helps identify side effects in render-phase lifecycle hooks and setState
|
|---|
| 152 |
|
|---|
| 153 | var enableProfilerTimer = true; // Record durations for commit and passive effects phases.
|
|---|
| 154 |
|
|---|
| 155 | var enableProfilerCommitHooks = true; // Phase param passed to onRender callback differentiates between an "update" and a "cascading-update".
|
|---|
| 156 |
|
|---|
| 157 | var allNativeEvents = new Set();
|
|---|
| 158 | /**
|
|---|
| 159 | * Mapping from registration name to event name
|
|---|
| 160 | */
|
|---|
| 161 |
|
|---|
| 162 |
|
|---|
| 163 | var registrationNameDependencies = {};
|
|---|
| 164 | /**
|
|---|
| 165 | * Mapping from lowercase registration names to the properly cased version,
|
|---|
| 166 | * used to warn in the case of missing event handlers. Available
|
|---|
| 167 | * only in true.
|
|---|
| 168 | * @type {Object}
|
|---|
| 169 | */
|
|---|
| 170 |
|
|---|
| 171 | var possibleRegistrationNames = {} ; // Trust the developer to only use possibleRegistrationNames in true
|
|---|
| 172 |
|
|---|
| 173 | function registerTwoPhaseEvent(registrationName, dependencies) {
|
|---|
| 174 | registerDirectEvent(registrationName, dependencies);
|
|---|
| 175 | registerDirectEvent(registrationName + 'Capture', dependencies);
|
|---|
| 176 | }
|
|---|
| 177 | function registerDirectEvent(registrationName, dependencies) {
|
|---|
| 178 | {
|
|---|
| 179 | if (registrationNameDependencies[registrationName]) {
|
|---|
| 180 | error('EventRegistry: More than one plugin attempted to publish the same ' + 'registration name, `%s`.', registrationName);
|
|---|
| 181 | }
|
|---|
| 182 | }
|
|---|
| 183 |
|
|---|
| 184 | registrationNameDependencies[registrationName] = dependencies;
|
|---|
| 185 |
|
|---|
| 186 | {
|
|---|
| 187 | var lowerCasedName = registrationName.toLowerCase();
|
|---|
| 188 | possibleRegistrationNames[lowerCasedName] = registrationName;
|
|---|
| 189 |
|
|---|
| 190 | if (registrationName === 'onDoubleClick') {
|
|---|
| 191 | possibleRegistrationNames.ondblclick = registrationName;
|
|---|
| 192 | }
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | for (var i = 0; i < dependencies.length; i++) {
|
|---|
| 196 | allNativeEvents.add(dependencies[i]);
|
|---|
| 197 | }
|
|---|
| 198 | }
|
|---|
| 199 |
|
|---|
| 200 | var canUseDOM = !!(typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined');
|
|---|
| 201 |
|
|---|
| 202 | var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|---|
| 203 |
|
|---|
| 204 | /*
|
|---|
| 205 | * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol
|
|---|
| 206 | * and Temporal.* types. See https://github.com/facebook/react/pull/22064.
|
|---|
| 207 | *
|
|---|
| 208 | * The functions in this module will throw an easier-to-understand,
|
|---|
| 209 | * easier-to-debug exception with a clear errors message message explaining the
|
|---|
| 210 | * problem. (Instead of a confusing exception thrown inside the implementation
|
|---|
| 211 | * of the `value` object).
|
|---|
| 212 | */
|
|---|
| 213 | // $FlowFixMe only called in DEV, so void return is not possible.
|
|---|
| 214 | function typeName(value) {
|
|---|
| 215 | {
|
|---|
| 216 | // toStringTag is needed for namespaced types like Temporal.Instant
|
|---|
| 217 | var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag;
|
|---|
| 218 | var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object';
|
|---|
| 219 | return type;
|
|---|
| 220 | }
|
|---|
| 221 | } // $FlowFixMe only called in DEV, so void return is not possible.
|
|---|
| 222 |
|
|---|
| 223 |
|
|---|
| 224 | function willCoercionThrow(value) {
|
|---|
| 225 | {
|
|---|
| 226 | try {
|
|---|
| 227 | testStringCoercion(value);
|
|---|
| 228 | return false;
|
|---|
| 229 | } catch (e) {
|
|---|
| 230 | return true;
|
|---|
| 231 | }
|
|---|
| 232 | }
|
|---|
| 233 | }
|
|---|
| 234 |
|
|---|
| 235 | function testStringCoercion(value) {
|
|---|
| 236 | // If you ended up here by following an exception call stack, here's what's
|
|---|
| 237 | // happened: you supplied an object or symbol value to React (as a prop, key,
|
|---|
| 238 | // DOM attribute, CSS property, string ref, etc.) and when React tried to
|
|---|
| 239 | // coerce it to a string using `'' + value`, an exception was thrown.
|
|---|
| 240 | //
|
|---|
| 241 | // The most common types that will cause this exception are `Symbol` instances
|
|---|
| 242 | // and Temporal objects like `Temporal.Instant`. But any object that has a
|
|---|
| 243 | // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this
|
|---|
| 244 | // exception. (Library authors do this to prevent users from using built-in
|
|---|
| 245 | // numeric operators like `+` or comparison operators like `>=` because custom
|
|---|
| 246 | // methods are needed to perform accurate arithmetic or comparison.)
|
|---|
| 247 | //
|
|---|
| 248 | // To fix the problem, coerce this object or symbol value to a string before
|
|---|
| 249 | // passing it to React. The most reliable way is usually `String(value)`.
|
|---|
| 250 | //
|
|---|
| 251 | // To find which value is throwing, check the browser or debugger console.
|
|---|
| 252 | // Before this exception was thrown, there should be `console.error` output
|
|---|
| 253 | // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the
|
|---|
| 254 | // problem and how that type was used: key, atrribute, input value prop, etc.
|
|---|
| 255 | // In most cases, this console output also shows the component and its
|
|---|
| 256 | // ancestor components where the exception happened.
|
|---|
| 257 | //
|
|---|
| 258 | // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 259 | return '' + value;
|
|---|
| 260 | }
|
|---|
| 261 |
|
|---|
| 262 | function checkAttributeStringCoercion(value, attributeName) {
|
|---|
| 263 | {
|
|---|
| 264 | if (willCoercionThrow(value)) {
|
|---|
| 265 | error('The provided `%s` attribute is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', attributeName, typeName(value));
|
|---|
| 266 |
|
|---|
| 267 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 268 | }
|
|---|
| 269 | }
|
|---|
| 270 | }
|
|---|
| 271 | function checkKeyStringCoercion(value) {
|
|---|
| 272 | {
|
|---|
| 273 | if (willCoercionThrow(value)) {
|
|---|
| 274 | error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));
|
|---|
| 275 |
|
|---|
| 276 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 277 | }
|
|---|
| 278 | }
|
|---|
| 279 | }
|
|---|
| 280 | function checkPropStringCoercion(value, propName) {
|
|---|
| 281 | {
|
|---|
| 282 | if (willCoercionThrow(value)) {
|
|---|
| 283 | error('The provided `%s` prop is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', propName, typeName(value));
|
|---|
| 284 |
|
|---|
| 285 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 286 | }
|
|---|
| 287 | }
|
|---|
| 288 | }
|
|---|
| 289 | function checkCSSPropertyStringCoercion(value, propName) {
|
|---|
| 290 | {
|
|---|
| 291 | if (willCoercionThrow(value)) {
|
|---|
| 292 | error('The provided `%s` CSS property is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', propName, typeName(value));
|
|---|
| 293 |
|
|---|
| 294 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 295 | }
|
|---|
| 296 | }
|
|---|
| 297 | }
|
|---|
| 298 | function checkHtmlStringCoercion(value) {
|
|---|
| 299 | {
|
|---|
| 300 | if (willCoercionThrow(value)) {
|
|---|
| 301 | error('The provided HTML markup uses a value of unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));
|
|---|
| 302 |
|
|---|
| 303 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 304 | }
|
|---|
| 305 | }
|
|---|
| 306 | }
|
|---|
| 307 | function checkFormFieldValueStringCoercion(value) {
|
|---|
| 308 | {
|
|---|
| 309 | if (willCoercionThrow(value)) {
|
|---|
| 310 | error('Form field values (value, checked, defaultValue, or defaultChecked props)' + ' must be strings, not %s.' + ' This value must be coerced to a string before before using it here.', typeName(value));
|
|---|
| 311 |
|
|---|
| 312 | return testStringCoercion(value); // throw (to help callers find troubleshooting comments)
|
|---|
| 313 | }
|
|---|
| 314 | }
|
|---|
| 315 | }
|
|---|
| 316 |
|
|---|
| 317 | // A reserved attribute.
|
|---|
| 318 | // It is handled by React separately and shouldn't be written to the DOM.
|
|---|
| 319 | var RESERVED = 0; // A simple string attribute.
|
|---|
| 320 | // Attributes that aren't in the filter are presumed to have this type.
|
|---|
| 321 |
|
|---|
| 322 | var STRING = 1; // A string attribute that accepts booleans in React. In HTML, these are called
|
|---|
| 323 | // "enumerated" attributes with "true" and "false" as possible values.
|
|---|
| 324 | // When true, it should be set to a "true" string.
|
|---|
| 325 | // When false, it should be set to a "false" string.
|
|---|
| 326 |
|
|---|
| 327 | var BOOLEANISH_STRING = 2; // A real boolean attribute.
|
|---|
| 328 | // When true, it should be present (set either to an empty string or its name).
|
|---|
| 329 | // When false, it should be omitted.
|
|---|
| 330 |
|
|---|
| 331 | var BOOLEAN = 3; // An attribute that can be used as a flag as well as with a value.
|
|---|
| 332 | // When true, it should be present (set either to an empty string or its name).
|
|---|
| 333 | // When false, it should be omitted.
|
|---|
| 334 | // For any other value, should be present with that value.
|
|---|
| 335 |
|
|---|
| 336 | var OVERLOADED_BOOLEAN = 4; // An attribute that must be numeric or parse as a numeric.
|
|---|
| 337 | // When falsy, it should be removed.
|
|---|
| 338 |
|
|---|
| 339 | var NUMERIC = 5; // An attribute that must be positive numeric or parse as a positive numeric.
|
|---|
| 340 | // When falsy, it should be removed.
|
|---|
| 341 |
|
|---|
| 342 | var POSITIVE_NUMERIC = 6;
|
|---|
| 343 |
|
|---|
| 344 | /* eslint-disable max-len */
|
|---|
| 345 | var ATTRIBUTE_NAME_START_CHAR = ":A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
|
|---|
| 346 | /* eslint-enable max-len */
|
|---|
| 347 |
|
|---|
| 348 | var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
|
|---|
| 349 | var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|---|
| 350 | var illegalAttributeNameCache = {};
|
|---|
| 351 | var validatedAttributeNameCache = {};
|
|---|
| 352 | function isAttributeNameSafe(attributeName) {
|
|---|
| 353 | if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) {
|
|---|
| 354 | return true;
|
|---|
| 355 | }
|
|---|
| 356 |
|
|---|
| 357 | if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) {
|
|---|
| 358 | return false;
|
|---|
| 359 | }
|
|---|
| 360 |
|
|---|
| 361 | if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) {
|
|---|
| 362 | validatedAttributeNameCache[attributeName] = true;
|
|---|
| 363 | return true;
|
|---|
| 364 | }
|
|---|
| 365 |
|
|---|
| 366 | illegalAttributeNameCache[attributeName] = true;
|
|---|
| 367 |
|
|---|
| 368 | {
|
|---|
| 369 | error('Invalid attribute name: `%s`', attributeName);
|
|---|
| 370 | }
|
|---|
| 371 |
|
|---|
| 372 | return false;
|
|---|
| 373 | }
|
|---|
| 374 | function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) {
|
|---|
| 375 | if (propertyInfo !== null) {
|
|---|
| 376 | return propertyInfo.type === RESERVED;
|
|---|
| 377 | }
|
|---|
| 378 |
|
|---|
| 379 | if (isCustomComponentTag) {
|
|---|
| 380 | return false;
|
|---|
| 381 | }
|
|---|
| 382 |
|
|---|
| 383 | if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) {
|
|---|
| 384 | return true;
|
|---|
| 385 | }
|
|---|
| 386 |
|
|---|
| 387 | return false;
|
|---|
| 388 | }
|
|---|
| 389 | function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) {
|
|---|
| 390 | if (propertyInfo !== null && propertyInfo.type === RESERVED) {
|
|---|
| 391 | return false;
|
|---|
| 392 | }
|
|---|
| 393 |
|
|---|
| 394 | switch (typeof value) {
|
|---|
| 395 | case 'function': // $FlowIssue symbol is perfectly valid here
|
|---|
| 396 |
|
|---|
| 397 | case 'symbol':
|
|---|
| 398 | // eslint-disable-line
|
|---|
| 399 | return true;
|
|---|
| 400 |
|
|---|
| 401 | case 'boolean':
|
|---|
| 402 | {
|
|---|
| 403 | if (isCustomComponentTag) {
|
|---|
| 404 | return false;
|
|---|
| 405 | }
|
|---|
| 406 |
|
|---|
| 407 | if (propertyInfo !== null) {
|
|---|
| 408 | return !propertyInfo.acceptsBooleans;
|
|---|
| 409 | } else {
|
|---|
| 410 | var prefix = name.toLowerCase().slice(0, 5);
|
|---|
| 411 | return prefix !== 'data-' && prefix !== 'aria-';
|
|---|
| 412 | }
|
|---|
| 413 | }
|
|---|
| 414 |
|
|---|
| 415 | default:
|
|---|
| 416 | return false;
|
|---|
| 417 | }
|
|---|
| 418 | }
|
|---|
| 419 | function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) {
|
|---|
| 420 | if (value === null || typeof value === 'undefined') {
|
|---|
| 421 | return true;
|
|---|
| 422 | }
|
|---|
| 423 |
|
|---|
| 424 | if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) {
|
|---|
| 425 | return true;
|
|---|
| 426 | }
|
|---|
| 427 |
|
|---|
| 428 | if (isCustomComponentTag) {
|
|---|
| 429 |
|
|---|
| 430 | return false;
|
|---|
| 431 | }
|
|---|
| 432 |
|
|---|
| 433 | if (propertyInfo !== null) {
|
|---|
| 434 |
|
|---|
| 435 | switch (propertyInfo.type) {
|
|---|
| 436 | case BOOLEAN:
|
|---|
| 437 | return !value;
|
|---|
| 438 |
|
|---|
| 439 | case OVERLOADED_BOOLEAN:
|
|---|
| 440 | return value === false;
|
|---|
| 441 |
|
|---|
| 442 | case NUMERIC:
|
|---|
| 443 | return isNaN(value);
|
|---|
| 444 |
|
|---|
| 445 | case POSITIVE_NUMERIC:
|
|---|
| 446 | return isNaN(value) || value < 1;
|
|---|
| 447 | }
|
|---|
| 448 | }
|
|---|
| 449 |
|
|---|
| 450 | return false;
|
|---|
| 451 | }
|
|---|
| 452 | function getPropertyInfo(name) {
|
|---|
| 453 | return properties.hasOwnProperty(name) ? properties[name] : null;
|
|---|
| 454 | }
|
|---|
| 455 |
|
|---|
| 456 | function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace, sanitizeURL, removeEmptyString) {
|
|---|
| 457 | this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN;
|
|---|
| 458 | this.attributeName = attributeName;
|
|---|
| 459 | this.attributeNamespace = attributeNamespace;
|
|---|
| 460 | this.mustUseProperty = mustUseProperty;
|
|---|
| 461 | this.propertyName = name;
|
|---|
| 462 | this.type = type;
|
|---|
| 463 | this.sanitizeURL = sanitizeURL;
|
|---|
| 464 | this.removeEmptyString = removeEmptyString;
|
|---|
| 465 | } // When adding attributes to this list, be sure to also add them to
|
|---|
| 466 | // the `possibleStandardNames` module to ensure casing and incorrect
|
|---|
| 467 | // name warnings.
|
|---|
| 468 |
|
|---|
| 469 |
|
|---|
| 470 | var properties = {}; // These props are reserved by React. They shouldn't be written to the DOM.
|
|---|
| 471 |
|
|---|
| 472 | var reservedProps = ['children', 'dangerouslySetInnerHTML', // TODO: This prevents the assignment of defaultValue to regular
|
|---|
| 473 | // elements (not just inputs). Now that ReactDOMInput assigns to the
|
|---|
| 474 | // defaultValue property -- do we need this?
|
|---|
| 475 | 'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style'];
|
|---|
| 476 |
|
|---|
| 477 | reservedProps.forEach(function (name) {
|
|---|
| 478 | properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty
|
|---|
| 479 | name, // attributeName
|
|---|
| 480 | null, // attributeNamespace
|
|---|
| 481 | false, // sanitizeURL
|
|---|
| 482 | false);
|
|---|
| 483 | }); // A few React string attributes have a different name.
|
|---|
| 484 | // This is a mapping from React prop names to the attribute names.
|
|---|
| 485 |
|
|---|
| 486 | [['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) {
|
|---|
| 487 | var name = _ref[0],
|
|---|
| 488 | attributeName = _ref[1];
|
|---|
| 489 | properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
|
|---|
| 490 | attributeName, // attributeName
|
|---|
| 491 | null, // attributeNamespace
|
|---|
| 492 | false, // sanitizeURL
|
|---|
| 493 | false);
|
|---|
| 494 | }); // These are "enumerated" HTML attributes that accept "true" and "false".
|
|---|
| 495 | // In React, we let users pass `true` and `false` even though technically
|
|---|
| 496 | // these aren't boolean attributes (they are coerced to strings).
|
|---|
| 497 |
|
|---|
| 498 | ['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) {
|
|---|
| 499 | properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
|
|---|
| 500 | name.toLowerCase(), // attributeName
|
|---|
| 501 | null, // attributeNamespace
|
|---|
| 502 | false, // sanitizeURL
|
|---|
| 503 | false);
|
|---|
| 504 | }); // These are "enumerated" SVG attributes that accept "true" and "false".
|
|---|
| 505 | // In React, we let users pass `true` and `false` even though technically
|
|---|
| 506 | // these aren't boolean attributes (they are coerced to strings).
|
|---|
| 507 | // Since these are SVG attributes, their attribute names are case-sensitive.
|
|---|
| 508 |
|
|---|
| 509 | ['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) {
|
|---|
| 510 | properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty
|
|---|
| 511 | name, // attributeName
|
|---|
| 512 | null, // attributeNamespace
|
|---|
| 513 | false, // sanitizeURL
|
|---|
| 514 | false);
|
|---|
| 515 | }); // These are HTML boolean attributes.
|
|---|
| 516 |
|
|---|
| 517 | ['allowFullScreen', 'async', // Note: there is a special case that prevents it from being written to the DOM
|
|---|
| 518 | // on the client side because the browsers are inconsistent. Instead we call focus().
|
|---|
| 519 | 'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'disablePictureInPicture', 'disableRemotePlayback', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless', // Microdata
|
|---|
| 520 | 'itemScope'].forEach(function (name) {
|
|---|
| 521 | properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty
|
|---|
| 522 | name.toLowerCase(), // attributeName
|
|---|
| 523 | null, // attributeNamespace
|
|---|
| 524 | false, // sanitizeURL
|
|---|
| 525 | false);
|
|---|
| 526 | }); // These are the few React props that we set as DOM properties
|
|---|
| 527 | // rather than attributes. These are all booleans.
|
|---|
| 528 |
|
|---|
| 529 | ['checked', // Note: `option.selected` is not updated if `select.multiple` is
|
|---|
| 530 | // disabled with `removeAttribute`. We have special logic for handling this.
|
|---|
| 531 | 'multiple', 'muted', 'selected' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 532 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 533 | // instead in the assignment below.
|
|---|
| 534 | ].forEach(function (name) {
|
|---|
| 535 | properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty
|
|---|
| 536 | name, // attributeName
|
|---|
| 537 | null, // attributeNamespace
|
|---|
| 538 | false, // sanitizeURL
|
|---|
| 539 | false);
|
|---|
| 540 | }); // These are HTML attributes that are "overloaded booleans": they behave like
|
|---|
| 541 | // booleans, but can also accept a string value.
|
|---|
| 542 |
|
|---|
| 543 | ['capture', 'download' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 544 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 545 | // instead in the assignment below.
|
|---|
| 546 | ].forEach(function (name) {
|
|---|
| 547 | properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty
|
|---|
| 548 | name, // attributeName
|
|---|
| 549 | null, // attributeNamespace
|
|---|
| 550 | false, // sanitizeURL
|
|---|
| 551 | false);
|
|---|
| 552 | }); // These are HTML attributes that must be positive numbers.
|
|---|
| 553 |
|
|---|
| 554 | ['cols', 'rows', 'size', 'span' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 555 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 556 | // instead in the assignment below.
|
|---|
| 557 | ].forEach(function (name) {
|
|---|
| 558 | properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty
|
|---|
| 559 | name, // attributeName
|
|---|
| 560 | null, // attributeNamespace
|
|---|
| 561 | false, // sanitizeURL
|
|---|
| 562 | false);
|
|---|
| 563 | }); // These are HTML attributes that must be numbers.
|
|---|
| 564 |
|
|---|
| 565 | ['rowSpan', 'start'].forEach(function (name) {
|
|---|
| 566 | properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty
|
|---|
| 567 | name.toLowerCase(), // attributeName
|
|---|
| 568 | null, // attributeNamespace
|
|---|
| 569 | false, // sanitizeURL
|
|---|
| 570 | false);
|
|---|
| 571 | });
|
|---|
| 572 | var CAMELIZE = /[\-\:]([a-z])/g;
|
|---|
| 573 |
|
|---|
| 574 | var capitalize = function (token) {
|
|---|
| 575 | return token[1].toUpperCase();
|
|---|
| 576 | }; // This is a list of all SVG attributes that need special casing, namespacing,
|
|---|
| 577 | // or boolean value assignment. Regular attributes that just accept strings
|
|---|
| 578 | // and have the same names are omitted, just like in the HTML attribute filter.
|
|---|
| 579 | // Some of these attributes can be hard to find. This list was created by
|
|---|
| 580 | // scraping the MDN documentation.
|
|---|
| 581 |
|
|---|
| 582 |
|
|---|
| 583 | ['accent-height', 'alignment-baseline', 'arabic-form', 'baseline-shift', 'cap-height', 'clip-path', 'clip-rule', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'dominant-baseline', 'enable-background', 'fill-opacity', 'fill-rule', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'glyph-name', 'glyph-orientation-horizontal', 'glyph-orientation-vertical', 'horiz-adv-x', 'horiz-origin-x', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start', 'overline-position', 'overline-thickness', 'paint-order', 'panose-1', 'pointer-events', 'rendering-intent', 'shape-rendering', 'stop-color', 'stop-opacity', 'strikethrough-position', 'strikethrough-thickness', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width', 'text-anchor', 'text-decoration', 'text-rendering', 'underline-position', 'underline-thickness', 'unicode-bidi', 'unicode-range', 'units-per-em', 'v-alphabetic', 'v-hanging', 'v-ideographic', 'v-mathematical', 'vector-effect', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'word-spacing', 'writing-mode', 'xmlns:xlink', 'x-height' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 584 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 585 | // instead in the assignment below.
|
|---|
| 586 | ].forEach(function (attributeName) {
|
|---|
| 587 | var name = attributeName.replace(CAMELIZE, capitalize);
|
|---|
| 588 | properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
|
|---|
| 589 | attributeName, null, // attributeNamespace
|
|---|
| 590 | false, // sanitizeURL
|
|---|
| 591 | false);
|
|---|
| 592 | }); // String SVG attributes with the xlink namespace.
|
|---|
| 593 |
|
|---|
| 594 | ['xlink:actuate', 'xlink:arcrole', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 595 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 596 | // instead in the assignment below.
|
|---|
| 597 | ].forEach(function (attributeName) {
|
|---|
| 598 | var name = attributeName.replace(CAMELIZE, capitalize);
|
|---|
| 599 | properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
|
|---|
| 600 | attributeName, 'http://www.w3.org/1999/xlink', false, // sanitizeURL
|
|---|
| 601 | false);
|
|---|
| 602 | }); // String SVG attributes with the xml namespace.
|
|---|
| 603 |
|
|---|
| 604 | ['xml:base', 'xml:lang', 'xml:space' // NOTE: if you add a camelCased prop to this list,
|
|---|
| 605 | // you'll need to set attributeName to name.toLowerCase()
|
|---|
| 606 | // instead in the assignment below.
|
|---|
| 607 | ].forEach(function (attributeName) {
|
|---|
| 608 | var name = attributeName.replace(CAMELIZE, capitalize);
|
|---|
| 609 | properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty
|
|---|
| 610 | attributeName, 'http://www.w3.org/XML/1998/namespace', false, // sanitizeURL
|
|---|
| 611 | false);
|
|---|
| 612 | }); // These attribute exists both in HTML and SVG.
|
|---|
| 613 | // The attribute name is case-sensitive in SVG so we can't just use
|
|---|
| 614 | // the React name like we do for attributes that exist only in HTML.
|
|---|
| 615 |
|
|---|
| 616 | ['tabIndex', 'crossOrigin'].forEach(function (attributeName) {
|
|---|
| 617 | properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
|
|---|
| 618 | attributeName.toLowerCase(), // attributeName
|
|---|
| 619 | null, // attributeNamespace
|
|---|
| 620 | false, // sanitizeURL
|
|---|
| 621 | false);
|
|---|
| 622 | }); // These attributes accept URLs. These must not allow javascript: URLS.
|
|---|
| 623 | // These will also need to accept Trusted Types object in the future.
|
|---|
| 624 |
|
|---|
| 625 | var xlinkHref = 'xlinkHref';
|
|---|
| 626 | properties[xlinkHref] = new PropertyInfoRecord('xlinkHref', STRING, false, // mustUseProperty
|
|---|
| 627 | 'xlink:href', 'http://www.w3.org/1999/xlink', true, // sanitizeURL
|
|---|
| 628 | false);
|
|---|
| 629 | ['src', 'href', 'action', 'formAction'].forEach(function (attributeName) {
|
|---|
| 630 | properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty
|
|---|
| 631 | attributeName.toLowerCase(), // attributeName
|
|---|
| 632 | null, // attributeNamespace
|
|---|
| 633 | true, // sanitizeURL
|
|---|
| 634 | true);
|
|---|
| 635 | });
|
|---|
| 636 |
|
|---|
| 637 | // and any newline or tab are filtered out as if they're not part of the URL.
|
|---|
| 638 | // https://url.spec.whatwg.org/#url-parsing
|
|---|
| 639 | // Tab or newline are defined as \r\n\t:
|
|---|
| 640 | // https://infra.spec.whatwg.org/#ascii-tab-or-newline
|
|---|
| 641 | // A C0 control is a code point in the range \u0000 NULL to \u001F
|
|---|
| 642 | // INFORMATION SEPARATOR ONE, inclusive:
|
|---|
| 643 | // https://infra.spec.whatwg.org/#c0-control-or-space
|
|---|
| 644 |
|
|---|
| 645 | /* eslint-disable max-len */
|
|---|
| 646 |
|
|---|
| 647 | var isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*\:/i;
|
|---|
| 648 | var didWarn = false;
|
|---|
| 649 |
|
|---|
| 650 | function sanitizeURL(url) {
|
|---|
| 651 | {
|
|---|
| 652 | if (!didWarn && isJavaScriptProtocol.test(url)) {
|
|---|
| 653 | didWarn = true;
|
|---|
| 654 |
|
|---|
| 655 | error('A future version of React will block javascript: URLs as a security precaution. ' + 'Use event handlers instead if you can. If you need to generate unsafe HTML try ' + 'using dangerouslySetInnerHTML instead. React was passed %s.', JSON.stringify(url));
|
|---|
| 656 | }
|
|---|
| 657 | }
|
|---|
| 658 | }
|
|---|
| 659 |
|
|---|
| 660 | /**
|
|---|
| 661 | * Get the value for a property on a node. Only used in DEV for SSR validation.
|
|---|
| 662 | * The "expected" argument is used as a hint of what the expected value is.
|
|---|
| 663 | * Some properties have multiple equivalent values.
|
|---|
| 664 | */
|
|---|
| 665 | function getValueForProperty(node, name, expected, propertyInfo) {
|
|---|
| 666 | {
|
|---|
| 667 | if (propertyInfo.mustUseProperty) {
|
|---|
| 668 | var propertyName = propertyInfo.propertyName;
|
|---|
| 669 | return node[propertyName];
|
|---|
| 670 | } else {
|
|---|
| 671 | // This check protects multiple uses of `expected`, which is why the
|
|---|
| 672 | // react-internal/safe-string-coercion rule is disabled in several spots
|
|---|
| 673 | // below.
|
|---|
| 674 | {
|
|---|
| 675 | checkAttributeStringCoercion(expected, name);
|
|---|
| 676 | }
|
|---|
| 677 |
|
|---|
| 678 | if ( propertyInfo.sanitizeURL) {
|
|---|
| 679 | // If we haven't fully disabled javascript: URLs, and if
|
|---|
| 680 | // the hydration is successful of a javascript: URL, we
|
|---|
| 681 | // still want to warn on the client.
|
|---|
| 682 | // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 683 | sanitizeURL('' + expected);
|
|---|
| 684 | }
|
|---|
| 685 |
|
|---|
| 686 | var attributeName = propertyInfo.attributeName;
|
|---|
| 687 | var stringValue = null;
|
|---|
| 688 |
|
|---|
| 689 | if (propertyInfo.type === OVERLOADED_BOOLEAN) {
|
|---|
| 690 | if (node.hasAttribute(attributeName)) {
|
|---|
| 691 | var value = node.getAttribute(attributeName);
|
|---|
| 692 |
|
|---|
| 693 | if (value === '') {
|
|---|
| 694 | return true;
|
|---|
| 695 | }
|
|---|
| 696 |
|
|---|
| 697 | if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
|
|---|
| 698 | return value;
|
|---|
| 699 | } // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 700 |
|
|---|
| 701 |
|
|---|
| 702 | if (value === '' + expected) {
|
|---|
| 703 | return expected;
|
|---|
| 704 | }
|
|---|
| 705 |
|
|---|
| 706 | return value;
|
|---|
| 707 | }
|
|---|
| 708 | } else if (node.hasAttribute(attributeName)) {
|
|---|
| 709 | if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
|
|---|
| 710 | // We had an attribute but shouldn't have had one, so read it
|
|---|
| 711 | // for the error message.
|
|---|
| 712 | return node.getAttribute(attributeName);
|
|---|
| 713 | }
|
|---|
| 714 |
|
|---|
| 715 | if (propertyInfo.type === BOOLEAN) {
|
|---|
| 716 | // If this was a boolean, it doesn't matter what the value is
|
|---|
| 717 | // the fact that we have it is the same as the expected.
|
|---|
| 718 | return expected;
|
|---|
| 719 | } // Even if this property uses a namespace we use getAttribute
|
|---|
| 720 | // because we assume its namespaced name is the same as our config.
|
|---|
| 721 | // To use getAttributeNS we need the local name which we don't have
|
|---|
| 722 | // in our config atm.
|
|---|
| 723 |
|
|---|
| 724 |
|
|---|
| 725 | stringValue = node.getAttribute(attributeName);
|
|---|
| 726 | }
|
|---|
| 727 |
|
|---|
| 728 | if (shouldRemoveAttribute(name, expected, propertyInfo, false)) {
|
|---|
| 729 | return stringValue === null ? expected : stringValue; // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 730 | } else if (stringValue === '' + expected) {
|
|---|
| 731 | return expected;
|
|---|
| 732 | } else {
|
|---|
| 733 | return stringValue;
|
|---|
| 734 | }
|
|---|
| 735 | }
|
|---|
| 736 | }
|
|---|
| 737 | }
|
|---|
| 738 | /**
|
|---|
| 739 | * Get the value for a attribute on a node. Only used in DEV for SSR validation.
|
|---|
| 740 | * The third argument is used as a hint of what the expected value is. Some
|
|---|
| 741 | * attributes have multiple equivalent values.
|
|---|
| 742 | */
|
|---|
| 743 |
|
|---|
| 744 | function getValueForAttribute(node, name, expected, isCustomComponentTag) {
|
|---|
| 745 | {
|
|---|
| 746 | if (!isAttributeNameSafe(name)) {
|
|---|
| 747 | return;
|
|---|
| 748 | }
|
|---|
| 749 |
|
|---|
| 750 | if (!node.hasAttribute(name)) {
|
|---|
| 751 | return expected === undefined ? undefined : null;
|
|---|
| 752 | }
|
|---|
| 753 |
|
|---|
| 754 | var value = node.getAttribute(name);
|
|---|
| 755 |
|
|---|
| 756 | {
|
|---|
| 757 | checkAttributeStringCoercion(expected, name);
|
|---|
| 758 | }
|
|---|
| 759 |
|
|---|
| 760 | if (value === '' + expected) {
|
|---|
| 761 | return expected;
|
|---|
| 762 | }
|
|---|
| 763 |
|
|---|
| 764 | return value;
|
|---|
| 765 | }
|
|---|
| 766 | }
|
|---|
| 767 | /**
|
|---|
| 768 | * Sets the value for a property on a node.
|
|---|
| 769 | *
|
|---|
| 770 | * @param {DOMElement} node
|
|---|
| 771 | * @param {string} name
|
|---|
| 772 | * @param {*} value
|
|---|
| 773 | */
|
|---|
| 774 |
|
|---|
| 775 | function setValueForProperty(node, name, value, isCustomComponentTag) {
|
|---|
| 776 | var propertyInfo = getPropertyInfo(name);
|
|---|
| 777 |
|
|---|
| 778 | if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) {
|
|---|
| 779 | return;
|
|---|
| 780 | }
|
|---|
| 781 |
|
|---|
| 782 | if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) {
|
|---|
| 783 | value = null;
|
|---|
| 784 | }
|
|---|
| 785 |
|
|---|
| 786 |
|
|---|
| 787 | if (isCustomComponentTag || propertyInfo === null) {
|
|---|
| 788 | if (isAttributeNameSafe(name)) {
|
|---|
| 789 | var _attributeName = name;
|
|---|
| 790 |
|
|---|
| 791 | if (value === null) {
|
|---|
| 792 | node.removeAttribute(_attributeName);
|
|---|
| 793 | } else {
|
|---|
| 794 | {
|
|---|
| 795 | checkAttributeStringCoercion(value, name);
|
|---|
| 796 | }
|
|---|
| 797 |
|
|---|
| 798 | node.setAttribute(_attributeName, '' + value);
|
|---|
| 799 | }
|
|---|
| 800 | }
|
|---|
| 801 |
|
|---|
| 802 | return;
|
|---|
| 803 | }
|
|---|
| 804 |
|
|---|
| 805 | var mustUseProperty = propertyInfo.mustUseProperty;
|
|---|
| 806 |
|
|---|
| 807 | if (mustUseProperty) {
|
|---|
| 808 | var propertyName = propertyInfo.propertyName;
|
|---|
| 809 |
|
|---|
| 810 | if (value === null) {
|
|---|
| 811 | var type = propertyInfo.type;
|
|---|
| 812 | node[propertyName] = type === BOOLEAN ? false : '';
|
|---|
| 813 | } else {
|
|---|
| 814 | // Contrary to `setAttribute`, object properties are properly
|
|---|
| 815 | // `toString`ed by IE8/9.
|
|---|
| 816 | node[propertyName] = value;
|
|---|
| 817 | }
|
|---|
| 818 |
|
|---|
| 819 | return;
|
|---|
| 820 | } // The rest are treated as attributes with special cases.
|
|---|
| 821 |
|
|---|
| 822 |
|
|---|
| 823 | var attributeName = propertyInfo.attributeName,
|
|---|
| 824 | attributeNamespace = propertyInfo.attributeNamespace;
|
|---|
| 825 |
|
|---|
| 826 | if (value === null) {
|
|---|
| 827 | node.removeAttribute(attributeName);
|
|---|
| 828 | } else {
|
|---|
| 829 | var _type = propertyInfo.type;
|
|---|
| 830 | var attributeValue;
|
|---|
| 831 |
|
|---|
| 832 | if (_type === BOOLEAN || _type === OVERLOADED_BOOLEAN && value === true) {
|
|---|
| 833 | // If attribute type is boolean, we know for sure it won't be an execution sink
|
|---|
| 834 | // and we won't require Trusted Type here.
|
|---|
| 835 | attributeValue = '';
|
|---|
| 836 | } else {
|
|---|
| 837 | // `setAttribute` with objects becomes only `[object]` in IE8/9,
|
|---|
| 838 | // ('' + value) makes it output the correct toString()-value.
|
|---|
| 839 | {
|
|---|
| 840 | {
|
|---|
| 841 | checkAttributeStringCoercion(value, attributeName);
|
|---|
| 842 | }
|
|---|
| 843 |
|
|---|
| 844 | attributeValue = '' + value;
|
|---|
| 845 | }
|
|---|
| 846 |
|
|---|
| 847 | if (propertyInfo.sanitizeURL) {
|
|---|
| 848 | sanitizeURL(attributeValue.toString());
|
|---|
| 849 | }
|
|---|
| 850 | }
|
|---|
| 851 |
|
|---|
| 852 | if (attributeNamespace) {
|
|---|
| 853 | node.setAttributeNS(attributeNamespace, attributeName, attributeValue);
|
|---|
| 854 | } else {
|
|---|
| 855 | node.setAttribute(attributeName, attributeValue);
|
|---|
| 856 | }
|
|---|
| 857 | }
|
|---|
| 858 | }
|
|---|
| 859 |
|
|---|
| 860 | // ATTENTION
|
|---|
| 861 | // When adding new symbols to this file,
|
|---|
| 862 | // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols'
|
|---|
| 863 | // The Symbol used to tag the ReactElement-like types.
|
|---|
| 864 | var REACT_ELEMENT_TYPE = Symbol.for('react.element');
|
|---|
| 865 | var REACT_PORTAL_TYPE = Symbol.for('react.portal');
|
|---|
| 866 | var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');
|
|---|
| 867 | var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode');
|
|---|
| 868 | var REACT_PROFILER_TYPE = Symbol.for('react.profiler');
|
|---|
| 869 | var REACT_PROVIDER_TYPE = Symbol.for('react.provider');
|
|---|
| 870 | var REACT_CONTEXT_TYPE = Symbol.for('react.context');
|
|---|
| 871 | var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
|
|---|
| 872 | var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
|
|---|
| 873 | var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
|
|---|
| 874 | var REACT_MEMO_TYPE = Symbol.for('react.memo');
|
|---|
| 875 | var REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
|---|
| 876 | var REACT_SCOPE_TYPE = Symbol.for('react.scope');
|
|---|
| 877 | var REACT_DEBUG_TRACING_MODE_TYPE = Symbol.for('react.debug_trace_mode');
|
|---|
| 878 | var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen');
|
|---|
| 879 | var REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden');
|
|---|
| 880 | var REACT_CACHE_TYPE = Symbol.for('react.cache');
|
|---|
| 881 | var REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker');
|
|---|
| 882 | var MAYBE_ITERATOR_SYMBOL = Symbol.iterator;
|
|---|
| 883 | var FAUX_ITERATOR_SYMBOL = '@@iterator';
|
|---|
| 884 | function getIteratorFn(maybeIterable) {
|
|---|
| 885 | if (maybeIterable === null || typeof maybeIterable !== 'object') {
|
|---|
| 886 | return null;
|
|---|
| 887 | }
|
|---|
| 888 |
|
|---|
| 889 | var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL];
|
|---|
| 890 |
|
|---|
| 891 | if (typeof maybeIterator === 'function') {
|
|---|
| 892 | return maybeIterator;
|
|---|
| 893 | }
|
|---|
| 894 |
|
|---|
| 895 | return null;
|
|---|
| 896 | }
|
|---|
| 897 |
|
|---|
| 898 | var assign = Object.assign;
|
|---|
| 899 |
|
|---|
| 900 | // Helpers to patch console.logs to avoid logging during side-effect free
|
|---|
| 901 | // replaying on render function. This currently only patches the object
|
|---|
| 902 | // lazily which won't cover if the log function was extracted eagerly.
|
|---|
| 903 | // We could also eagerly patch the method.
|
|---|
| 904 | var disabledDepth = 0;
|
|---|
| 905 | var prevLog;
|
|---|
| 906 | var prevInfo;
|
|---|
| 907 | var prevWarn;
|
|---|
| 908 | var prevError;
|
|---|
| 909 | var prevGroup;
|
|---|
| 910 | var prevGroupCollapsed;
|
|---|
| 911 | var prevGroupEnd;
|
|---|
| 912 |
|
|---|
| 913 | function disabledLog() {}
|
|---|
| 914 |
|
|---|
| 915 | disabledLog.__reactDisabledLog = true;
|
|---|
| 916 | function disableLogs() {
|
|---|
| 917 | {
|
|---|
| 918 | if (disabledDepth === 0) {
|
|---|
| 919 | /* eslint-disable react-internal/no-production-logging */
|
|---|
| 920 | prevLog = console.log;
|
|---|
| 921 | prevInfo = console.info;
|
|---|
| 922 | prevWarn = console.warn;
|
|---|
| 923 | prevError = console.error;
|
|---|
| 924 | prevGroup = console.group;
|
|---|
| 925 | prevGroupCollapsed = console.groupCollapsed;
|
|---|
| 926 | prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099
|
|---|
| 927 |
|
|---|
| 928 | var props = {
|
|---|
| 929 | configurable: true,
|
|---|
| 930 | enumerable: true,
|
|---|
| 931 | value: disabledLog,
|
|---|
| 932 | writable: true
|
|---|
| 933 | }; // $FlowFixMe Flow thinks console is immutable.
|
|---|
| 934 |
|
|---|
| 935 | Object.defineProperties(console, {
|
|---|
| 936 | info: props,
|
|---|
| 937 | log: props,
|
|---|
| 938 | warn: props,
|
|---|
| 939 | error: props,
|
|---|
| 940 | group: props,
|
|---|
| 941 | groupCollapsed: props,
|
|---|
| 942 | groupEnd: props
|
|---|
| 943 | });
|
|---|
| 944 | /* eslint-enable react-internal/no-production-logging */
|
|---|
| 945 | }
|
|---|
| 946 |
|
|---|
| 947 | disabledDepth++;
|
|---|
| 948 | }
|
|---|
| 949 | }
|
|---|
| 950 | function reenableLogs() {
|
|---|
| 951 | {
|
|---|
| 952 | disabledDepth--;
|
|---|
| 953 |
|
|---|
| 954 | if (disabledDepth === 0) {
|
|---|
| 955 | /* eslint-disable react-internal/no-production-logging */
|
|---|
| 956 | var props = {
|
|---|
| 957 | configurable: true,
|
|---|
| 958 | enumerable: true,
|
|---|
| 959 | writable: true
|
|---|
| 960 | }; // $FlowFixMe Flow thinks console is immutable.
|
|---|
| 961 |
|
|---|
| 962 | Object.defineProperties(console, {
|
|---|
| 963 | log: assign({}, props, {
|
|---|
| 964 | value: prevLog
|
|---|
| 965 | }),
|
|---|
| 966 | info: assign({}, props, {
|
|---|
| 967 | value: prevInfo
|
|---|
| 968 | }),
|
|---|
| 969 | warn: assign({}, props, {
|
|---|
| 970 | value: prevWarn
|
|---|
| 971 | }),
|
|---|
| 972 | error: assign({}, props, {
|
|---|
| 973 | value: prevError
|
|---|
| 974 | }),
|
|---|
| 975 | group: assign({}, props, {
|
|---|
| 976 | value: prevGroup
|
|---|
| 977 | }),
|
|---|
| 978 | groupCollapsed: assign({}, props, {
|
|---|
| 979 | value: prevGroupCollapsed
|
|---|
| 980 | }),
|
|---|
| 981 | groupEnd: assign({}, props, {
|
|---|
| 982 | value: prevGroupEnd
|
|---|
| 983 | })
|
|---|
| 984 | });
|
|---|
| 985 | /* eslint-enable react-internal/no-production-logging */
|
|---|
| 986 | }
|
|---|
| 987 |
|
|---|
| 988 | if (disabledDepth < 0) {
|
|---|
| 989 | error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.');
|
|---|
| 990 | }
|
|---|
| 991 | }
|
|---|
| 992 | }
|
|---|
| 993 |
|
|---|
| 994 | var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
|---|
| 995 | var prefix;
|
|---|
| 996 | function describeBuiltInComponentFrame(name, source, ownerFn) {
|
|---|
| 997 | {
|
|---|
| 998 | if (prefix === undefined) {
|
|---|
| 999 | // Extract the VM specific prefix used by each line.
|
|---|
| 1000 | try {
|
|---|
| 1001 | throw Error();
|
|---|
| 1002 | } catch (x) {
|
|---|
| 1003 | var match = x.stack.trim().match(/\n( *(at )?)/);
|
|---|
| 1004 | prefix = match && match[1] || '';
|
|---|
| 1005 | }
|
|---|
| 1006 | } // We use the prefix to ensure our stacks line up with native stack frames.
|
|---|
| 1007 |
|
|---|
| 1008 |
|
|---|
| 1009 | return '\n' + prefix + name;
|
|---|
| 1010 | }
|
|---|
| 1011 | }
|
|---|
| 1012 | var reentry = false;
|
|---|
| 1013 | var componentFrameCache;
|
|---|
| 1014 |
|
|---|
| 1015 | {
|
|---|
| 1016 | var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
|
|---|
| 1017 | componentFrameCache = new PossiblyWeakMap();
|
|---|
| 1018 | }
|
|---|
| 1019 |
|
|---|
| 1020 | function describeNativeComponentFrame(fn, construct) {
|
|---|
| 1021 | // If something asked for a stack inside a fake render, it should get ignored.
|
|---|
| 1022 | if ( !fn || reentry) {
|
|---|
| 1023 | return '';
|
|---|
| 1024 | }
|
|---|
| 1025 |
|
|---|
| 1026 | {
|
|---|
| 1027 | var frame = componentFrameCache.get(fn);
|
|---|
| 1028 |
|
|---|
| 1029 | if (frame !== undefined) {
|
|---|
| 1030 | return frame;
|
|---|
| 1031 | }
|
|---|
| 1032 | }
|
|---|
| 1033 |
|
|---|
| 1034 | var control;
|
|---|
| 1035 | reentry = true;
|
|---|
| 1036 | var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined.
|
|---|
| 1037 |
|
|---|
| 1038 | Error.prepareStackTrace = undefined;
|
|---|
| 1039 | var previousDispatcher;
|
|---|
| 1040 |
|
|---|
| 1041 | {
|
|---|
| 1042 | previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function
|
|---|
| 1043 | // for warnings.
|
|---|
| 1044 |
|
|---|
| 1045 | ReactCurrentDispatcher.current = null;
|
|---|
| 1046 | disableLogs();
|
|---|
| 1047 | }
|
|---|
| 1048 |
|
|---|
| 1049 | try {
|
|---|
| 1050 | // This should throw.
|
|---|
| 1051 | if (construct) {
|
|---|
| 1052 | // Something should be setting the props in the constructor.
|
|---|
| 1053 | var Fake = function () {
|
|---|
| 1054 | throw Error();
|
|---|
| 1055 | }; // $FlowFixMe
|
|---|
| 1056 |
|
|---|
| 1057 |
|
|---|
| 1058 | Object.defineProperty(Fake.prototype, 'props', {
|
|---|
| 1059 | set: function () {
|
|---|
| 1060 | // We use a throwing setter instead of frozen or non-writable props
|
|---|
| 1061 | // because that won't throw in a non-strict mode function.
|
|---|
| 1062 | throw Error();
|
|---|
| 1063 | }
|
|---|
| 1064 | });
|
|---|
| 1065 |
|
|---|
| 1066 | if (typeof Reflect === 'object' && Reflect.construct) {
|
|---|
| 1067 | // We construct a different control for this case to include any extra
|
|---|
| 1068 | // frames added by the construct call.
|
|---|
| 1069 | try {
|
|---|
| 1070 | Reflect.construct(Fake, []);
|
|---|
| 1071 | } catch (x) {
|
|---|
| 1072 | control = x;
|
|---|
| 1073 | }
|
|---|
| 1074 |
|
|---|
| 1075 | Reflect.construct(fn, [], Fake);
|
|---|
| 1076 | } else {
|
|---|
| 1077 | try {
|
|---|
| 1078 | Fake.call();
|
|---|
| 1079 | } catch (x) {
|
|---|
| 1080 | control = x;
|
|---|
| 1081 | }
|
|---|
| 1082 |
|
|---|
| 1083 | fn.call(Fake.prototype);
|
|---|
| 1084 | }
|
|---|
| 1085 | } else {
|
|---|
| 1086 | try {
|
|---|
| 1087 | throw Error();
|
|---|
| 1088 | } catch (x) {
|
|---|
| 1089 | control = x;
|
|---|
| 1090 | }
|
|---|
| 1091 |
|
|---|
| 1092 | fn();
|
|---|
| 1093 | }
|
|---|
| 1094 | } catch (sample) {
|
|---|
| 1095 | // This is inlined manually because closure doesn't do it for us.
|
|---|
| 1096 | if (sample && control && typeof sample.stack === 'string') {
|
|---|
| 1097 | // This extracts the first frame from the sample that isn't also in the control.
|
|---|
| 1098 | // Skipping one frame that we assume is the frame that calls the two.
|
|---|
| 1099 | var sampleLines = sample.stack.split('\n');
|
|---|
| 1100 | var controlLines = control.stack.split('\n');
|
|---|
| 1101 | var s = sampleLines.length - 1;
|
|---|
| 1102 | var c = controlLines.length - 1;
|
|---|
| 1103 |
|
|---|
| 1104 | while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) {
|
|---|
| 1105 | // We expect at least one stack frame to be shared.
|
|---|
| 1106 | // Typically this will be the root most one. However, stack frames may be
|
|---|
| 1107 | // cut off due to maximum stack limits. In this case, one maybe cut off
|
|---|
| 1108 | // earlier than the other. We assume that the sample is longer or the same
|
|---|
| 1109 | // and there for cut off earlier. So we should find the root most frame in
|
|---|
| 1110 | // the sample somewhere in the control.
|
|---|
| 1111 | c--;
|
|---|
| 1112 | }
|
|---|
| 1113 |
|
|---|
| 1114 | for (; s >= 1 && c >= 0; s--, c--) {
|
|---|
| 1115 | // Next we find the first one that isn't the same which should be the
|
|---|
| 1116 | // frame that called our sample function and the control.
|
|---|
| 1117 | if (sampleLines[s] !== controlLines[c]) {
|
|---|
| 1118 | // In V8, the first line is describing the message but other VMs don't.
|
|---|
| 1119 | // If we're about to return the first line, and the control is also on the same
|
|---|
| 1120 | // line, that's a pretty good indicator that our sample threw at same line as
|
|---|
| 1121 | // the control. I.e. before we entered the sample frame. So we ignore this result.
|
|---|
| 1122 | // This can happen if you passed a class to function component, or non-function.
|
|---|
| 1123 | if (s !== 1 || c !== 1) {
|
|---|
| 1124 | do {
|
|---|
| 1125 | s--;
|
|---|
| 1126 | c--; // We may still have similar intermediate frames from the construct call.
|
|---|
| 1127 | // The next one that isn't the same should be our match though.
|
|---|
| 1128 |
|
|---|
| 1129 | if (c < 0 || sampleLines[s] !== controlLines[c]) {
|
|---|
| 1130 | // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier.
|
|---|
| 1131 | var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "<anonymous>"
|
|---|
| 1132 | // but we have a user-provided "displayName"
|
|---|
| 1133 | // splice it in to make the stack more readable.
|
|---|
| 1134 |
|
|---|
| 1135 |
|
|---|
| 1136 | if (fn.displayName && _frame.includes('<anonymous>')) {
|
|---|
| 1137 | _frame = _frame.replace('<anonymous>', fn.displayName);
|
|---|
| 1138 | }
|
|---|
| 1139 |
|
|---|
| 1140 | {
|
|---|
| 1141 | if (typeof fn === 'function') {
|
|---|
| 1142 | componentFrameCache.set(fn, _frame);
|
|---|
| 1143 | }
|
|---|
| 1144 | } // Return the line we found.
|
|---|
| 1145 |
|
|---|
| 1146 |
|
|---|
| 1147 | return _frame;
|
|---|
| 1148 | }
|
|---|
| 1149 | } while (s >= 1 && c >= 0);
|
|---|
| 1150 | }
|
|---|
| 1151 |
|
|---|
| 1152 | break;
|
|---|
| 1153 | }
|
|---|
| 1154 | }
|
|---|
| 1155 | }
|
|---|
| 1156 | } finally {
|
|---|
| 1157 | reentry = false;
|
|---|
| 1158 |
|
|---|
| 1159 | {
|
|---|
| 1160 | ReactCurrentDispatcher.current = previousDispatcher;
|
|---|
| 1161 | reenableLogs();
|
|---|
| 1162 | }
|
|---|
| 1163 |
|
|---|
| 1164 | Error.prepareStackTrace = previousPrepareStackTrace;
|
|---|
| 1165 | } // Fallback to just using the name if we couldn't make it throw.
|
|---|
| 1166 |
|
|---|
| 1167 |
|
|---|
| 1168 | var name = fn ? fn.displayName || fn.name : '';
|
|---|
| 1169 | var syntheticFrame = name ? describeBuiltInComponentFrame(name) : '';
|
|---|
| 1170 |
|
|---|
| 1171 | {
|
|---|
| 1172 | if (typeof fn === 'function') {
|
|---|
| 1173 | componentFrameCache.set(fn, syntheticFrame);
|
|---|
| 1174 | }
|
|---|
| 1175 | }
|
|---|
| 1176 |
|
|---|
| 1177 | return syntheticFrame;
|
|---|
| 1178 | }
|
|---|
| 1179 |
|
|---|
| 1180 | function describeClassComponentFrame(ctor, source, ownerFn) {
|
|---|
| 1181 | {
|
|---|
| 1182 | return describeNativeComponentFrame(ctor, true);
|
|---|
| 1183 | }
|
|---|
| 1184 | }
|
|---|
| 1185 | function describeFunctionComponentFrame(fn, source, ownerFn) {
|
|---|
| 1186 | {
|
|---|
| 1187 | return describeNativeComponentFrame(fn, false);
|
|---|
| 1188 | }
|
|---|
| 1189 | }
|
|---|
| 1190 |
|
|---|
| 1191 | function shouldConstruct(Component) {
|
|---|
| 1192 | var prototype = Component.prototype;
|
|---|
| 1193 | return !!(prototype && prototype.isReactComponent);
|
|---|
| 1194 | }
|
|---|
| 1195 |
|
|---|
| 1196 | function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) {
|
|---|
| 1197 |
|
|---|
| 1198 | if (type == null) {
|
|---|
| 1199 | return '';
|
|---|
| 1200 | }
|
|---|
| 1201 |
|
|---|
| 1202 | if (typeof type === 'function') {
|
|---|
| 1203 | {
|
|---|
| 1204 | return describeNativeComponentFrame(type, shouldConstruct(type));
|
|---|
| 1205 | }
|
|---|
| 1206 | }
|
|---|
| 1207 |
|
|---|
| 1208 | if (typeof type === 'string') {
|
|---|
| 1209 | return describeBuiltInComponentFrame(type);
|
|---|
| 1210 | }
|
|---|
| 1211 |
|
|---|
| 1212 | switch (type) {
|
|---|
| 1213 | case REACT_SUSPENSE_TYPE:
|
|---|
| 1214 | return describeBuiltInComponentFrame('Suspense');
|
|---|
| 1215 |
|
|---|
| 1216 | case REACT_SUSPENSE_LIST_TYPE:
|
|---|
| 1217 | return describeBuiltInComponentFrame('SuspenseList');
|
|---|
| 1218 | }
|
|---|
| 1219 |
|
|---|
| 1220 | if (typeof type === 'object') {
|
|---|
| 1221 | switch (type.$$typeof) {
|
|---|
| 1222 | case REACT_FORWARD_REF_TYPE:
|
|---|
| 1223 | return describeFunctionComponentFrame(type.render);
|
|---|
| 1224 |
|
|---|
| 1225 | case REACT_MEMO_TYPE:
|
|---|
| 1226 | // Memo may contain any component type so we recursively resolve it.
|
|---|
| 1227 | return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn);
|
|---|
| 1228 |
|
|---|
| 1229 | case REACT_LAZY_TYPE:
|
|---|
| 1230 | {
|
|---|
| 1231 | var lazyComponent = type;
|
|---|
| 1232 | var payload = lazyComponent._payload;
|
|---|
| 1233 | var init = lazyComponent._init;
|
|---|
| 1234 |
|
|---|
| 1235 | try {
|
|---|
| 1236 | // Lazy may contain any component type so we recursively resolve it.
|
|---|
| 1237 | return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn);
|
|---|
| 1238 | } catch (x) {}
|
|---|
| 1239 | }
|
|---|
| 1240 | }
|
|---|
| 1241 | }
|
|---|
| 1242 |
|
|---|
| 1243 | return '';
|
|---|
| 1244 | }
|
|---|
| 1245 |
|
|---|
| 1246 | function describeFiber(fiber) {
|
|---|
| 1247 | var owner = fiber._debugOwner ? fiber._debugOwner.type : null ;
|
|---|
| 1248 | var source = fiber._debugSource ;
|
|---|
| 1249 |
|
|---|
| 1250 | switch (fiber.tag) {
|
|---|
| 1251 | case HostComponent:
|
|---|
| 1252 | return describeBuiltInComponentFrame(fiber.type);
|
|---|
| 1253 |
|
|---|
| 1254 | case LazyComponent:
|
|---|
| 1255 | return describeBuiltInComponentFrame('Lazy');
|
|---|
| 1256 |
|
|---|
| 1257 | case SuspenseComponent:
|
|---|
| 1258 | return describeBuiltInComponentFrame('Suspense');
|
|---|
| 1259 |
|
|---|
| 1260 | case SuspenseListComponent:
|
|---|
| 1261 | return describeBuiltInComponentFrame('SuspenseList');
|
|---|
| 1262 |
|
|---|
| 1263 | case FunctionComponent:
|
|---|
| 1264 | case IndeterminateComponent:
|
|---|
| 1265 | case SimpleMemoComponent:
|
|---|
| 1266 | return describeFunctionComponentFrame(fiber.type);
|
|---|
| 1267 |
|
|---|
| 1268 | case ForwardRef:
|
|---|
| 1269 | return describeFunctionComponentFrame(fiber.type.render);
|
|---|
| 1270 |
|
|---|
| 1271 | case ClassComponent:
|
|---|
| 1272 | return describeClassComponentFrame(fiber.type);
|
|---|
| 1273 |
|
|---|
| 1274 | default:
|
|---|
| 1275 | return '';
|
|---|
| 1276 | }
|
|---|
| 1277 | }
|
|---|
| 1278 |
|
|---|
| 1279 | function getStackByFiberInDevAndProd(workInProgress) {
|
|---|
| 1280 | try {
|
|---|
| 1281 | var info = '';
|
|---|
| 1282 | var node = workInProgress;
|
|---|
| 1283 |
|
|---|
| 1284 | do {
|
|---|
| 1285 | info += describeFiber(node);
|
|---|
| 1286 | node = node.return;
|
|---|
| 1287 | } while (node);
|
|---|
| 1288 |
|
|---|
| 1289 | return info;
|
|---|
| 1290 | } catch (x) {
|
|---|
| 1291 | return '\nError generating stack: ' + x.message + '\n' + x.stack;
|
|---|
| 1292 | }
|
|---|
| 1293 | }
|
|---|
| 1294 |
|
|---|
| 1295 | function getWrappedName(outerType, innerType, wrapperName) {
|
|---|
| 1296 | var displayName = outerType.displayName;
|
|---|
| 1297 |
|
|---|
| 1298 | if (displayName) {
|
|---|
| 1299 | return displayName;
|
|---|
| 1300 | }
|
|---|
| 1301 |
|
|---|
| 1302 | var functionName = innerType.displayName || innerType.name || '';
|
|---|
| 1303 | return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName;
|
|---|
| 1304 | } // Keep in sync with react-reconciler/getComponentNameFromFiber
|
|---|
| 1305 |
|
|---|
| 1306 |
|
|---|
| 1307 | function getContextName(type) {
|
|---|
| 1308 | return type.displayName || 'Context';
|
|---|
| 1309 | } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead.
|
|---|
| 1310 |
|
|---|
| 1311 |
|
|---|
| 1312 | function getComponentNameFromType(type) {
|
|---|
| 1313 | if (type == null) {
|
|---|
| 1314 | // Host root, text node or just invalid type.
|
|---|
| 1315 | return null;
|
|---|
| 1316 | }
|
|---|
| 1317 |
|
|---|
| 1318 | {
|
|---|
| 1319 | if (typeof type.tag === 'number') {
|
|---|
| 1320 | error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.');
|
|---|
| 1321 | }
|
|---|
| 1322 | }
|
|---|
| 1323 |
|
|---|
| 1324 | if (typeof type === 'function') {
|
|---|
| 1325 | return type.displayName || type.name || null;
|
|---|
| 1326 | }
|
|---|
| 1327 |
|
|---|
| 1328 | if (typeof type === 'string') {
|
|---|
| 1329 | return type;
|
|---|
| 1330 | }
|
|---|
| 1331 |
|
|---|
| 1332 | switch (type) {
|
|---|
| 1333 | case REACT_FRAGMENT_TYPE:
|
|---|
| 1334 | return 'Fragment';
|
|---|
| 1335 |
|
|---|
| 1336 | case REACT_PORTAL_TYPE:
|
|---|
| 1337 | return 'Portal';
|
|---|
| 1338 |
|
|---|
| 1339 | case REACT_PROFILER_TYPE:
|
|---|
| 1340 | return 'Profiler';
|
|---|
| 1341 |
|
|---|
| 1342 | case REACT_STRICT_MODE_TYPE:
|
|---|
| 1343 | return 'StrictMode';
|
|---|
| 1344 |
|
|---|
| 1345 | case REACT_SUSPENSE_TYPE:
|
|---|
| 1346 | return 'Suspense';
|
|---|
| 1347 |
|
|---|
| 1348 | case REACT_SUSPENSE_LIST_TYPE:
|
|---|
| 1349 | return 'SuspenseList';
|
|---|
| 1350 |
|
|---|
| 1351 | }
|
|---|
| 1352 |
|
|---|
| 1353 | if (typeof type === 'object') {
|
|---|
| 1354 | switch (type.$$typeof) {
|
|---|
| 1355 | case REACT_CONTEXT_TYPE:
|
|---|
| 1356 | var context = type;
|
|---|
| 1357 | return getContextName(context) + '.Consumer';
|
|---|
| 1358 |
|
|---|
| 1359 | case REACT_PROVIDER_TYPE:
|
|---|
| 1360 | var provider = type;
|
|---|
| 1361 | return getContextName(provider._context) + '.Provider';
|
|---|
| 1362 |
|
|---|
| 1363 | case REACT_FORWARD_REF_TYPE:
|
|---|
| 1364 | return getWrappedName(type, type.render, 'ForwardRef');
|
|---|
| 1365 |
|
|---|
| 1366 | case REACT_MEMO_TYPE:
|
|---|
| 1367 | var outerName = type.displayName || null;
|
|---|
| 1368 |
|
|---|
| 1369 | if (outerName !== null) {
|
|---|
| 1370 | return outerName;
|
|---|
| 1371 | }
|
|---|
| 1372 |
|
|---|
| 1373 | return getComponentNameFromType(type.type) || 'Memo';
|
|---|
| 1374 |
|
|---|
| 1375 | case REACT_LAZY_TYPE:
|
|---|
| 1376 | {
|
|---|
| 1377 | var lazyComponent = type;
|
|---|
| 1378 | var payload = lazyComponent._payload;
|
|---|
| 1379 | var init = lazyComponent._init;
|
|---|
| 1380 |
|
|---|
| 1381 | try {
|
|---|
| 1382 | return getComponentNameFromType(init(payload));
|
|---|
| 1383 | } catch (x) {
|
|---|
| 1384 | return null;
|
|---|
| 1385 | }
|
|---|
| 1386 | }
|
|---|
| 1387 |
|
|---|
| 1388 | // eslint-disable-next-line no-fallthrough
|
|---|
| 1389 | }
|
|---|
| 1390 | }
|
|---|
| 1391 |
|
|---|
| 1392 | return null;
|
|---|
| 1393 | }
|
|---|
| 1394 |
|
|---|
| 1395 | function getWrappedName$1(outerType, innerType, wrapperName) {
|
|---|
| 1396 | var functionName = innerType.displayName || innerType.name || '';
|
|---|
| 1397 | return outerType.displayName || (functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName);
|
|---|
| 1398 | } // Keep in sync with shared/getComponentNameFromType
|
|---|
| 1399 |
|
|---|
| 1400 |
|
|---|
| 1401 | function getContextName$1(type) {
|
|---|
| 1402 | return type.displayName || 'Context';
|
|---|
| 1403 | }
|
|---|
| 1404 |
|
|---|
| 1405 | function getComponentNameFromFiber(fiber) {
|
|---|
| 1406 | var tag = fiber.tag,
|
|---|
| 1407 | type = fiber.type;
|
|---|
| 1408 |
|
|---|
| 1409 | switch (tag) {
|
|---|
| 1410 | case CacheComponent:
|
|---|
| 1411 | return 'Cache';
|
|---|
| 1412 |
|
|---|
| 1413 | case ContextConsumer:
|
|---|
| 1414 | var context = type;
|
|---|
| 1415 | return getContextName$1(context) + '.Consumer';
|
|---|
| 1416 |
|
|---|
| 1417 | case ContextProvider:
|
|---|
| 1418 | var provider = type;
|
|---|
| 1419 | return getContextName$1(provider._context) + '.Provider';
|
|---|
| 1420 |
|
|---|
| 1421 | case DehydratedFragment:
|
|---|
| 1422 | return 'DehydratedFragment';
|
|---|
| 1423 |
|
|---|
| 1424 | case ForwardRef:
|
|---|
| 1425 | return getWrappedName$1(type, type.render, 'ForwardRef');
|
|---|
| 1426 |
|
|---|
| 1427 | case Fragment:
|
|---|
| 1428 | return 'Fragment';
|
|---|
| 1429 |
|
|---|
| 1430 | case HostComponent:
|
|---|
| 1431 | // Host component type is the display name (e.g. "div", "View")
|
|---|
| 1432 | return type;
|
|---|
| 1433 |
|
|---|
| 1434 | case HostPortal:
|
|---|
| 1435 | return 'Portal';
|
|---|
| 1436 |
|
|---|
| 1437 | case HostRoot:
|
|---|
| 1438 | return 'Root';
|
|---|
| 1439 |
|
|---|
| 1440 | case HostText:
|
|---|
| 1441 | return 'Text';
|
|---|
| 1442 |
|
|---|
| 1443 | case LazyComponent:
|
|---|
| 1444 | // Name comes from the type in this case; we don't have a tag.
|
|---|
| 1445 | return getComponentNameFromType(type);
|
|---|
| 1446 |
|
|---|
| 1447 | case Mode:
|
|---|
| 1448 | if (type === REACT_STRICT_MODE_TYPE) {
|
|---|
| 1449 | // Don't be less specific than shared/getComponentNameFromType
|
|---|
| 1450 | return 'StrictMode';
|
|---|
| 1451 | }
|
|---|
| 1452 |
|
|---|
| 1453 | return 'Mode';
|
|---|
| 1454 |
|
|---|
| 1455 | case OffscreenComponent:
|
|---|
| 1456 | return 'Offscreen';
|
|---|
| 1457 |
|
|---|
| 1458 | case Profiler:
|
|---|
| 1459 | return 'Profiler';
|
|---|
| 1460 |
|
|---|
| 1461 | case ScopeComponent:
|
|---|
| 1462 | return 'Scope';
|
|---|
| 1463 |
|
|---|
| 1464 | case SuspenseComponent:
|
|---|
| 1465 | return 'Suspense';
|
|---|
| 1466 |
|
|---|
| 1467 | case SuspenseListComponent:
|
|---|
| 1468 | return 'SuspenseList';
|
|---|
| 1469 |
|
|---|
| 1470 | case TracingMarkerComponent:
|
|---|
| 1471 | return 'TracingMarker';
|
|---|
| 1472 | // The display name for this tags come from the user-provided type:
|
|---|
| 1473 |
|
|---|
| 1474 | case ClassComponent:
|
|---|
| 1475 | case FunctionComponent:
|
|---|
| 1476 | case IncompleteClassComponent:
|
|---|
| 1477 | case IndeterminateComponent:
|
|---|
| 1478 | case MemoComponent:
|
|---|
| 1479 | case SimpleMemoComponent:
|
|---|
| 1480 | if (typeof type === 'function') {
|
|---|
| 1481 | return type.displayName || type.name || null;
|
|---|
| 1482 | }
|
|---|
| 1483 |
|
|---|
| 1484 | if (typeof type === 'string') {
|
|---|
| 1485 | return type;
|
|---|
| 1486 | }
|
|---|
| 1487 |
|
|---|
| 1488 | break;
|
|---|
| 1489 |
|
|---|
| 1490 | }
|
|---|
| 1491 |
|
|---|
| 1492 | return null;
|
|---|
| 1493 | }
|
|---|
| 1494 |
|
|---|
| 1495 | var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
|
|---|
| 1496 | var current = null;
|
|---|
| 1497 | var isRendering = false;
|
|---|
| 1498 | function getCurrentFiberOwnerNameInDevOrNull() {
|
|---|
| 1499 | {
|
|---|
| 1500 | if (current === null) {
|
|---|
| 1501 | return null;
|
|---|
| 1502 | }
|
|---|
| 1503 |
|
|---|
| 1504 | var owner = current._debugOwner;
|
|---|
| 1505 |
|
|---|
| 1506 | if (owner !== null && typeof owner !== 'undefined') {
|
|---|
| 1507 | return getComponentNameFromFiber(owner);
|
|---|
| 1508 | }
|
|---|
| 1509 | }
|
|---|
| 1510 |
|
|---|
| 1511 | return null;
|
|---|
| 1512 | }
|
|---|
| 1513 |
|
|---|
| 1514 | function getCurrentFiberStackInDev() {
|
|---|
| 1515 | {
|
|---|
| 1516 | if (current === null) {
|
|---|
| 1517 | return '';
|
|---|
| 1518 | } // Safe because if current fiber exists, we are reconciling,
|
|---|
| 1519 | // and it is guaranteed to be the work-in-progress version.
|
|---|
| 1520 |
|
|---|
| 1521 |
|
|---|
| 1522 | return getStackByFiberInDevAndProd(current);
|
|---|
| 1523 | }
|
|---|
| 1524 | }
|
|---|
| 1525 |
|
|---|
| 1526 | function resetCurrentFiber() {
|
|---|
| 1527 | {
|
|---|
| 1528 | ReactDebugCurrentFrame.getCurrentStack = null;
|
|---|
| 1529 | current = null;
|
|---|
| 1530 | isRendering = false;
|
|---|
| 1531 | }
|
|---|
| 1532 | }
|
|---|
| 1533 | function setCurrentFiber(fiber) {
|
|---|
| 1534 | {
|
|---|
| 1535 | ReactDebugCurrentFrame.getCurrentStack = fiber === null ? null : getCurrentFiberStackInDev;
|
|---|
| 1536 | current = fiber;
|
|---|
| 1537 | isRendering = false;
|
|---|
| 1538 | }
|
|---|
| 1539 | }
|
|---|
| 1540 | function getCurrentFiber() {
|
|---|
| 1541 | {
|
|---|
| 1542 | return current;
|
|---|
| 1543 | }
|
|---|
| 1544 | }
|
|---|
| 1545 | function setIsRendering(rendering) {
|
|---|
| 1546 | {
|
|---|
| 1547 | isRendering = rendering;
|
|---|
| 1548 | }
|
|---|
| 1549 | }
|
|---|
| 1550 |
|
|---|
| 1551 | // Flow does not allow string concatenation of most non-string types. To work
|
|---|
| 1552 | // around this limitation, we use an opaque type that can only be obtained by
|
|---|
| 1553 | // passing the value through getToStringValue first.
|
|---|
| 1554 | function toString(value) {
|
|---|
| 1555 | // The coercion safety check is performed in getToStringValue().
|
|---|
| 1556 | // eslint-disable-next-line react-internal/safe-string-coercion
|
|---|
| 1557 | return '' + value;
|
|---|
| 1558 | }
|
|---|
| 1559 | function getToStringValue(value) {
|
|---|
| 1560 | switch (typeof value) {
|
|---|
| 1561 | case 'boolean':
|
|---|
| 1562 | case 'number':
|
|---|
| 1563 | case 'string':
|
|---|
| 1564 | case 'undefined':
|
|---|
| 1565 | return value;
|
|---|
| 1566 |
|
|---|
| 1567 | case 'object':
|
|---|
| 1568 | {
|
|---|
| 1569 | checkFormFieldValueStringCoercion(value);
|
|---|
| 1570 | }
|
|---|
| 1571 |
|
|---|
| 1572 | return value;
|
|---|
| 1573 |
|
|---|
| 1574 | default:
|
|---|
| 1575 | // function, symbol are assigned as empty strings
|
|---|
| 1576 | return '';
|
|---|
| 1577 | }
|
|---|
| 1578 | }
|
|---|
| 1579 |
|
|---|
| 1580 | var hasReadOnlyValue = {
|
|---|
| 1581 | button: true,
|
|---|
| 1582 | checkbox: true,
|
|---|
| 1583 | image: true,
|
|---|
| 1584 | hidden: true,
|
|---|
| 1585 | radio: true,
|
|---|
| 1586 | reset: true,
|
|---|
| 1587 | submit: true
|
|---|
| 1588 | };
|
|---|
| 1589 | function checkControlledValueProps(tagName, props) {
|
|---|
| 1590 | {
|
|---|
| 1591 | if (!(hasReadOnlyValue[props.type] || props.onChange || props.onInput || props.readOnly || props.disabled || props.value == null)) {
|
|---|
| 1592 | error('You provided a `value` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultValue`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
|
|---|
| 1593 | }
|
|---|
| 1594 |
|
|---|
| 1595 | if (!(props.onChange || props.readOnly || props.disabled || props.checked == null)) {
|
|---|
| 1596 | error('You provided a `checked` prop to a form field without an ' + '`onChange` handler. This will render a read-only field. If ' + 'the field should be mutable use `defaultChecked`. Otherwise, ' + 'set either `onChange` or `readOnly`.');
|
|---|
| 1597 | }
|
|---|
| 1598 | }
|
|---|
| 1599 | }
|
|---|
| 1600 |
|
|---|
| 1601 | function isCheckable(elem) {
|
|---|
| 1602 | var type = elem.type;
|
|---|
| 1603 | var nodeName = elem.nodeName;
|
|---|
| 1604 | return nodeName && nodeName.toLowerCase() === 'input' && (type === 'checkbox' || type === 'radio');
|
|---|
| 1605 | }
|
|---|
| 1606 |
|
|---|
| 1607 | function getTracker(node) {
|
|---|
| 1608 | return node._valueTracker;
|
|---|
| 1609 | }
|
|---|
| 1610 |
|
|---|
| 1611 | function detachTracker(node) {
|
|---|
| 1612 | node._valueTracker = null;
|
|---|
| 1613 | }
|
|---|
| 1614 |
|
|---|
| 1615 | function getValueFromNode(node) {
|
|---|
| 1616 | var value = '';
|
|---|
| 1617 |
|
|---|
| 1618 | if (!node) {
|
|---|
| 1619 | return value;
|
|---|
| 1620 | }
|
|---|
| 1621 |
|
|---|
| 1622 | if (isCheckable(node)) {
|
|---|
| 1623 | value = node.checked ? 'true' : 'false';
|
|---|
| 1624 | } else {
|
|---|
| 1625 | value = node.value;
|
|---|
| 1626 | }
|
|---|
| 1627 |
|
|---|
| 1628 | return value;
|
|---|
| 1629 | }
|
|---|
| 1630 |
|
|---|
| 1631 | function trackValueOnNode(node) {
|
|---|
| 1632 | var valueField = isCheckable(node) ? 'checked' : 'value';
|
|---|
| 1633 | var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField);
|
|---|
| 1634 |
|
|---|
| 1635 | {
|
|---|
| 1636 | checkFormFieldValueStringCoercion(node[valueField]);
|
|---|
| 1637 | }
|
|---|
| 1638 |
|
|---|
| 1639 | var currentValue = '' + node[valueField]; // if someone has already defined a value or Safari, then bail
|
|---|
| 1640 | // and don't track value will cause over reporting of changes,
|
|---|
| 1641 | // but it's better then a hard failure
|
|---|
| 1642 | // (needed for certain tests that spyOn input values and Safari)
|
|---|
| 1643 |
|
|---|
| 1644 | if (node.hasOwnProperty(valueField) || typeof descriptor === 'undefined' || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') {
|
|---|
| 1645 | return;
|
|---|
| 1646 | }
|
|---|
| 1647 |
|
|---|
| 1648 | var get = descriptor.get,
|
|---|
| 1649 | set = descriptor.set;
|
|---|
| 1650 | Object.defineProperty(node, valueField, {
|
|---|
| 1651 | configurable: true,
|
|---|
| 1652 | get: function () {
|
|---|
| 1653 | return get.call(this);
|
|---|
| 1654 | },
|
|---|
| 1655 | set: function (value) {
|
|---|
| 1656 | {
|
|---|
| 1657 | checkFormFieldValueStringCoercion(value);
|
|---|
| 1658 | }
|
|---|
| 1659 |
|
|---|
| 1660 | currentValue = '' + value;
|
|---|
| 1661 | set.call(this, value);
|
|---|
| 1662 | }
|
|---|
| 1663 | }); // We could've passed this the first time
|
|---|
| 1664 | // but it triggers a bug in IE11 and Edge 14/15.
|
|---|
| 1665 | // Calling defineProperty() again should be equivalent.
|
|---|
| 1666 | // https://github.com/facebook/react/issues/11768
|
|---|
| 1667 |
|
|---|
| 1668 | Object.defineProperty(node, valueField, {
|
|---|
| 1669 | enumerable: descriptor.enumerable
|
|---|
| 1670 | });
|
|---|
| 1671 | var tracker = {
|
|---|
| 1672 | getValue: function () {
|
|---|
| 1673 | return currentValue;
|
|---|
| 1674 | },
|
|---|
| 1675 | setValue: function (value) {
|
|---|
| 1676 | {
|
|---|
| 1677 | checkFormFieldValueStringCoercion(value);
|
|---|
| 1678 | }
|
|---|
| 1679 |
|
|---|
| 1680 | currentValue = '' + value;
|
|---|
| 1681 | },
|
|---|
| 1682 | stopTracking: function () {
|
|---|
| 1683 | detachTracker(node);
|
|---|
| 1684 | delete node[valueField];
|
|---|
| 1685 | }
|
|---|
| 1686 | };
|
|---|
| 1687 | return tracker;
|
|---|
| 1688 | }
|
|---|
| 1689 |
|
|---|
| 1690 | function track(node) {
|
|---|
| 1691 | if (getTracker(node)) {
|
|---|
| 1692 | return;
|
|---|
| 1693 | } // TODO: Once it's just Fiber we can move this to node._wrapperState
|
|---|
| 1694 |
|
|---|
| 1695 |
|
|---|
| 1696 | node._valueTracker = trackValueOnNode(node);
|
|---|
| 1697 | }
|
|---|
| 1698 | function updateValueIfChanged(node) {
|
|---|
| 1699 | if (!node) {
|
|---|
| 1700 | return false;
|
|---|
| 1701 | }
|
|---|
| 1702 |
|
|---|
| 1703 | var tracker = getTracker(node); // if there is no tracker at this point it's unlikely
|
|---|
| 1704 | // that trying again will succeed
|
|---|
| 1705 |
|
|---|
| 1706 | if (!tracker) {
|
|---|
| 1707 | return true;
|
|---|
| 1708 | }
|
|---|
| 1709 |
|
|---|
| 1710 | var lastValue = tracker.getValue();
|
|---|
| 1711 | var nextValue = getValueFromNode(node);
|
|---|
| 1712 |
|
|---|
| 1713 | if (nextValue !== lastValue) {
|
|---|
| 1714 | tracker.setValue(nextValue);
|
|---|
| 1715 | return true;
|
|---|
| 1716 | }
|
|---|
| 1717 |
|
|---|
| 1718 | return false;
|
|---|
| 1719 | }
|
|---|
| 1720 |
|
|---|
| 1721 | function getActiveElement(doc) {
|
|---|
| 1722 | doc = doc || (typeof document !== 'undefined' ? document : undefined);
|
|---|
| 1723 |
|
|---|
| 1724 | if (typeof doc === 'undefined') {
|
|---|
| 1725 | return null;
|
|---|
| 1726 | }
|
|---|
| 1727 |
|
|---|
| 1728 | try {
|
|---|
| 1729 | return doc.activeElement || doc.body;
|
|---|
| 1730 | } catch (e) {
|
|---|
| 1731 | return doc.body;
|
|---|
| 1732 | }
|
|---|
| 1733 | }
|
|---|
| 1734 |
|
|---|
| 1735 | var didWarnValueDefaultValue = false;
|
|---|
| 1736 | var didWarnCheckedDefaultChecked = false;
|
|---|
| 1737 | var didWarnControlledToUncontrolled = false;
|
|---|
| 1738 | var didWarnUncontrolledToControlled = false;
|
|---|
| 1739 |
|
|---|
| 1740 | function isControlled(props) {
|
|---|
| 1741 | var usesChecked = props.type === 'checkbox' || props.type === 'radio';
|
|---|
| 1742 | return usesChecked ? props.checked != null : props.value != null;
|
|---|
| 1743 | }
|
|---|
| 1744 | /**
|
|---|
| 1745 | * Implements an <input> host component that allows setting these optional
|
|---|
| 1746 | * props: `checked`, `value`, `defaultChecked`, and `defaultValue`.
|
|---|
| 1747 | *
|
|---|
| 1748 | * If `checked` or `value` are not supplied (or null/undefined), user actions
|
|---|
| 1749 | * that affect the checked state or value will trigger updates to the element.
|
|---|
| 1750 | *
|
|---|
| 1751 | * If they are supplied (and not null/undefined), the rendered element will not
|
|---|
| 1752 | * trigger updates to the element. Instead, the props must change in order for
|
|---|
| 1753 | * the rendered element to be updated.
|
|---|
| 1754 | *
|
|---|
| 1755 | * The rendered element will be initialized as unchecked (or `defaultChecked`)
|
|---|
| 1756 | * with an empty value (or `defaultValue`).
|
|---|
| 1757 | *
|
|---|
| 1758 | * See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html
|
|---|
| 1759 | */
|
|---|
| 1760 |
|
|---|
| 1761 |
|
|---|
| 1762 | function getHostProps(element, props) {
|
|---|
| 1763 | var node = element;
|
|---|
| 1764 | var checked = props.checked;
|
|---|
| 1765 | var hostProps = assign({}, props, {
|
|---|
| 1766 | defaultChecked: undefined,
|
|---|
| 1767 | defaultValue: undefined,
|
|---|
| 1768 | value: undefined,
|
|---|
| 1769 | checked: checked != null ? checked : node._wrapperState.initialChecked
|
|---|
| 1770 | });
|
|---|
| 1771 | return hostProps;
|
|---|
| 1772 | }
|
|---|
| 1773 | function initWrapperState(element, props) {
|
|---|
| 1774 | {
|
|---|
| 1775 | checkControlledValueProps('input', props);
|
|---|
| 1776 |
|
|---|
| 1777 | if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) {
|
|---|
| 1778 | error('%s contains an input of type %s with both checked and defaultChecked props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the checked prop, or the defaultChecked prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://reactjs.org/link/controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component', props.type);
|
|---|
| 1779 |
|
|---|
| 1780 | didWarnCheckedDefaultChecked = true;
|
|---|
| 1781 | }
|
|---|
| 1782 |
|
|---|
| 1783 | if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) {
|
|---|
| 1784 | error('%s contains an input of type %s with both value and defaultValue props. ' + 'Input elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled input ' + 'element and remove one of these props. More info: ' + 'https://reactjs.org/link/controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component', props.type);
|
|---|
| 1785 |
|
|---|
| 1786 | didWarnValueDefaultValue = true;
|
|---|
| 1787 | }
|
|---|
| 1788 | }
|
|---|
| 1789 |
|
|---|
| 1790 | var node = element;
|
|---|
| 1791 | var defaultValue = props.defaultValue == null ? '' : props.defaultValue;
|
|---|
| 1792 | node._wrapperState = {
|
|---|
| 1793 | initialChecked: props.checked != null ? props.checked : props.defaultChecked,
|
|---|
| 1794 | initialValue: getToStringValue(props.value != null ? props.value : defaultValue),
|
|---|
| 1795 | controlled: isControlled(props)
|
|---|
| 1796 | };
|
|---|
| 1797 | }
|
|---|
| 1798 | function updateChecked(element, props) {
|
|---|
| 1799 | var node = element;
|
|---|
| 1800 | var checked = props.checked;
|
|---|
| 1801 |
|
|---|
| 1802 | if (checked != null) {
|
|---|
| 1803 | setValueForProperty(node, 'checked', checked, false);
|
|---|
| 1804 | }
|
|---|
| 1805 | }
|
|---|
| 1806 | function updateWrapper(element, props) {
|
|---|
| 1807 | var node = element;
|
|---|
| 1808 |
|
|---|
| 1809 | {
|
|---|
| 1810 | var controlled = isControlled(props);
|
|---|
| 1811 |
|
|---|
| 1812 | if (!node._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) {
|
|---|
| 1813 | error('A component is changing an uncontrolled input to be controlled. ' + 'This is likely caused by the value changing from undefined to ' + 'a defined value, which should not happen. ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components');
|
|---|
| 1814 |
|
|---|
| 1815 | didWarnUncontrolledToControlled = true;
|
|---|
| 1816 | }
|
|---|
| 1817 |
|
|---|
| 1818 | if (node._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) {
|
|---|
| 1819 | error('A component is changing a controlled input to be uncontrolled. ' + 'This is likely caused by the value changing from a defined to ' + 'undefined, which should not happen. ' + 'Decide between using a controlled or uncontrolled input ' + 'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components');
|
|---|
| 1820 |
|
|---|
| 1821 | didWarnControlledToUncontrolled = true;
|
|---|
| 1822 | }
|
|---|
| 1823 | }
|
|---|
| 1824 |
|
|---|
| 1825 | updateChecked(element, props);
|
|---|
| 1826 | var value = getToStringValue(props.value);
|
|---|
| 1827 | var type = props.type;
|
|---|
| 1828 |
|
|---|
| 1829 | if (value != null) {
|
|---|
| 1830 | if (type === 'number') {
|
|---|
| 1831 | if (value === 0 && node.value === '' || // We explicitly want to coerce to number here if possible.
|
|---|
| 1832 | // eslint-disable-next-line
|
|---|
| 1833 | node.value != value) {
|
|---|
| 1834 | node.value = toString(value);
|
|---|
| 1835 | }
|
|---|
| 1836 | } else if (node.value !== toString(value)) {
|
|---|
| 1837 | node.value = toString(value);
|
|---|
| 1838 | }
|
|---|
| 1839 | } else if (type === 'submit' || type === 'reset') {
|
|---|
| 1840 | // Submit/reset inputs need the attribute removed completely to avoid
|
|---|
| 1841 | // blank-text buttons.
|
|---|
| 1842 | node.removeAttribute('value');
|
|---|
| 1843 | return;
|
|---|
| 1844 | }
|
|---|
| 1845 |
|
|---|
| 1846 | {
|
|---|
| 1847 | // When syncing the value attribute, the value comes from a cascade of
|
|---|
| 1848 | // properties:
|
|---|
| 1849 | // 1. The value React property
|
|---|
| 1850 | // 2. The defaultValue React property
|
|---|
| 1851 | // 3. Otherwise there should be no change
|
|---|
| 1852 | if (props.hasOwnProperty('value')) {
|
|---|
| 1853 | setDefaultValue(node, props.type, value);
|
|---|
| 1854 | } else if (props.hasOwnProperty('defaultValue')) {
|
|---|
| 1855 | setDefaultValue(node, props.type, getToStringValue(props.defaultValue));
|
|---|
| 1856 | }
|
|---|
| 1857 | }
|
|---|
| 1858 |
|
|---|
| 1859 | {
|
|---|
| 1860 | // When syncing the checked attribute, it only changes when it needs
|
|---|
| 1861 | // to be removed, such as transitioning from a checkbox into a text input
|
|---|
| 1862 | if (props.checked == null && props.defaultChecked != null) {
|
|---|
| 1863 | node.defaultChecked = !!props.defaultChecked;
|
|---|
| 1864 | }
|
|---|
| 1865 | }
|
|---|
| 1866 | }
|
|---|
| 1867 | function postMountWrapper(element, props, isHydrating) {
|
|---|
| 1868 | var node = element; // Do not assign value if it is already set. This prevents user text input
|
|---|
| 1869 | // from being lost during SSR hydration.
|
|---|
| 1870 |
|
|---|
| 1871 | if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
|
|---|
| 1872 | var type = props.type;
|
|---|
| 1873 | var isButton = type === 'submit' || type === 'reset'; // Avoid setting value attribute on submit/reset inputs as it overrides the
|
|---|
| 1874 | // default value provided by the browser. See: #12872
|
|---|
| 1875 |
|
|---|
| 1876 | if (isButton && (props.value === undefined || props.value === null)) {
|
|---|
| 1877 | return;
|
|---|
| 1878 | }
|
|---|
| 1879 |
|
|---|
| 1880 | var initialValue = toString(node._wrapperState.initialValue); // Do not assign value if it is already set. This prevents user text input
|
|---|
| 1881 | // from being lost during SSR hydration.
|
|---|
| 1882 |
|
|---|
| 1883 | if (!isHydrating) {
|
|---|
| 1884 | {
|
|---|
| 1885 | // When syncing the value attribute, the value property should use
|
|---|
| 1886 | // the wrapperState._initialValue property. This uses:
|
|---|
| 1887 | //
|
|---|
| 1888 | // 1. The value React property when present
|
|---|
| 1889 | // 2. The defaultValue React property when present
|
|---|
| 1890 | // 3. An empty string
|
|---|
| 1891 | if (initialValue !== node.value) {
|
|---|
| 1892 | node.value = initialValue;
|
|---|
| 1893 | }
|
|---|
| 1894 | }
|
|---|
| 1895 | }
|
|---|
| 1896 |
|
|---|
| 1897 | {
|
|---|
| 1898 | // Otherwise, the value attribute is synchronized to the property,
|
|---|
| 1899 | // so we assign defaultValue to the same thing as the value property
|
|---|
| 1900 | // assignment step above.
|
|---|
| 1901 | node.defaultValue = initialValue;
|
|---|
| 1902 | }
|
|---|
| 1903 | } // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
|
|---|
| 1904 | // this is needed to work around a chrome bug where setting defaultChecked
|
|---|
| 1905 | // will sometimes influence the value of checked (even after detachment).
|
|---|
| 1906 | // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416
|
|---|
| 1907 | // We need to temporarily unset name to avoid disrupting radio button groups.
|
|---|
| 1908 |
|
|---|
| 1909 |
|
|---|
| 1910 | var name = node.name;
|
|---|
| 1911 |
|
|---|
| 1912 | if (name !== '') {
|
|---|
| 1913 | node.name = '';
|
|---|
| 1914 | }
|
|---|
| 1915 |
|
|---|
| 1916 | {
|
|---|
| 1917 | // When syncing the checked attribute, both the checked property and
|
|---|
| 1918 | // attribute are assigned at the same time using defaultChecked. This uses:
|
|---|
| 1919 | //
|
|---|
| 1920 | // 1. The checked React property when present
|
|---|
| 1921 | // 2. The defaultChecked React property when present
|
|---|
| 1922 | // 3. Otherwise, false
|
|---|
| 1923 | node.defaultChecked = !node.defaultChecked;
|
|---|
| 1924 | node.defaultChecked = !!node._wrapperState.initialChecked;
|
|---|
| 1925 | }
|
|---|
| 1926 |
|
|---|
| 1927 | if (name !== '') {
|
|---|
| 1928 | node.name = name;
|
|---|
| 1929 | }
|
|---|
| 1930 | }
|
|---|
| 1931 | function restoreControlledState(element, props) {
|
|---|
| 1932 | var node = element;
|
|---|
| 1933 | updateWrapper(node, props);
|
|---|
| 1934 | updateNamedCousins(node, props);
|
|---|
| 1935 | }
|
|---|
| 1936 |
|
|---|
| 1937 | function updateNamedCousins(rootNode, props) {
|
|---|
| 1938 | var name = props.name;
|
|---|
| 1939 |
|
|---|
| 1940 | if (props.type === 'radio' && name != null) {
|
|---|
| 1941 | var queryRoot = rootNode;
|
|---|
| 1942 |
|
|---|
| 1943 | while (queryRoot.parentNode) {
|
|---|
| 1944 | queryRoot = queryRoot.parentNode;
|
|---|
| 1945 | } // If `rootNode.form` was non-null, then we could try `form.elements`,
|
|---|
| 1946 | // but that sometimes behaves strangely in IE8. We could also try using
|
|---|
| 1947 | // `form.getElementsByName`, but that will only return direct children
|
|---|
| 1948 | // and won't include inputs that use the HTML5 `form=` attribute. Since
|
|---|
| 1949 | // the input might not even be in a form. It might not even be in the
|
|---|
| 1950 | // document. Let's just use the local `querySelectorAll` to ensure we don't
|
|---|
| 1951 | // miss anything.
|
|---|
| 1952 |
|
|---|
| 1953 |
|
|---|
| 1954 | {
|
|---|
| 1955 | checkAttributeStringCoercion(name, 'name');
|
|---|
| 1956 | }
|
|---|
| 1957 |
|
|---|
| 1958 | var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]');
|
|---|
| 1959 |
|
|---|
| 1960 | for (var i = 0; i < group.length; i++) {
|
|---|
| 1961 | var otherNode = group[i];
|
|---|
| 1962 |
|
|---|
| 1963 | if (otherNode === rootNode || otherNode.form !== rootNode.form) {
|
|---|
| 1964 | continue;
|
|---|
| 1965 | } // This will throw if radio buttons rendered by different copies of React
|
|---|
| 1966 | // and the same name are rendered into the same form (same as #1939).
|
|---|
| 1967 | // That's probably okay; we don't support it just as we don't support
|
|---|
| 1968 | // mixing React radio buttons with non-React ones.
|
|---|
| 1969 |
|
|---|
| 1970 |
|
|---|
| 1971 | var otherProps = getFiberCurrentPropsFromNode(otherNode);
|
|---|
| 1972 |
|
|---|
| 1973 | if (!otherProps) {
|
|---|
| 1974 | throw new Error('ReactDOMInput: Mixing React and non-React radio inputs with the ' + 'same `name` is not supported.');
|
|---|
| 1975 | } // We need update the tracked value on the named cousin since the value
|
|---|
| 1976 | // was changed but the input saw no event or value set
|
|---|
| 1977 |
|
|---|
| 1978 |
|
|---|
| 1979 | updateValueIfChanged(otherNode); // If this is a controlled radio button group, forcing the input that
|
|---|
| 1980 | // was previously checked to update will cause it to be come re-checked
|
|---|
| 1981 | // as appropriate.
|
|---|
| 1982 |
|
|---|
| 1983 | updateWrapper(otherNode, otherProps);
|
|---|
| 1984 | }
|
|---|
| 1985 | }
|
|---|
| 1986 | } // In Chrome, assigning defaultValue to certain input types triggers input validation.
|
|---|
| 1987 | // For number inputs, the display value loses trailing decimal points. For email inputs,
|
|---|
| 1988 | // Chrome raises "The specified value <x> is not a valid email address".
|
|---|
| 1989 | //
|
|---|
| 1990 | // Here we check to see if the defaultValue has actually changed, avoiding these problems
|
|---|
| 1991 | // when the user is inputting text
|
|---|
| 1992 | //
|
|---|
| 1993 | // https://github.com/facebook/react/issues/7253
|
|---|
| 1994 |
|
|---|
| 1995 |
|
|---|
| 1996 | function setDefaultValue(node, type, value) {
|
|---|
| 1997 | if ( // Focused number inputs synchronize on blur. See ChangeEventPlugin.js
|
|---|
| 1998 | type !== 'number' || getActiveElement(node.ownerDocument) !== node) {
|
|---|
| 1999 | if (value == null) {
|
|---|
| 2000 | node.defaultValue = toString(node._wrapperState.initialValue);
|
|---|
| 2001 | } else if (node.defaultValue !== toString(value)) {
|
|---|
| 2002 | node.defaultValue = toString(value);
|
|---|
| 2003 | }
|
|---|
| 2004 | }
|
|---|
| 2005 | }
|
|---|
| 2006 |
|
|---|
| 2007 | var didWarnSelectedSetOnOption = false;
|
|---|
| 2008 | var didWarnInvalidChild = false;
|
|---|
| 2009 | var didWarnInvalidInnerHTML = false;
|
|---|
| 2010 | /**
|
|---|
| 2011 | * Implements an <option> host component that warns when `selected` is set.
|
|---|
| 2012 | */
|
|---|
| 2013 |
|
|---|
| 2014 | function validateProps(element, props) {
|
|---|
| 2015 | {
|
|---|
| 2016 | // If a value is not provided, then the children must be simple.
|
|---|
| 2017 | if (props.value == null) {
|
|---|
| 2018 | if (typeof props.children === 'object' && props.children !== null) {
|
|---|
| 2019 | React.Children.forEach(props.children, function (child) {
|
|---|
| 2020 | if (child == null) {
|
|---|
| 2021 | return;
|
|---|
| 2022 | }
|
|---|
| 2023 |
|
|---|
| 2024 | if (typeof child === 'string' || typeof child === 'number') {
|
|---|
| 2025 | return;
|
|---|
| 2026 | }
|
|---|
| 2027 |
|
|---|
| 2028 | if (!didWarnInvalidChild) {
|
|---|
| 2029 | didWarnInvalidChild = true;
|
|---|
| 2030 |
|
|---|
| 2031 | error('Cannot infer the option value of complex children. ' + 'Pass a `value` prop or use a plain string as children to <option>.');
|
|---|
| 2032 | }
|
|---|
| 2033 | });
|
|---|
| 2034 | } else if (props.dangerouslySetInnerHTML != null) {
|
|---|
| 2035 | if (!didWarnInvalidInnerHTML) {
|
|---|
| 2036 | didWarnInvalidInnerHTML = true;
|
|---|
| 2037 |
|
|---|
| 2038 | error('Pass a `value` prop if you set dangerouslyInnerHTML so React knows ' + 'which value should be selected.');
|
|---|
| 2039 | }
|
|---|
| 2040 | }
|
|---|
| 2041 | } // TODO: Remove support for `selected` in <option>.
|
|---|
| 2042 |
|
|---|
| 2043 |
|
|---|
| 2044 | if (props.selected != null && !didWarnSelectedSetOnOption) {
|
|---|
| 2045 | error('Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.');
|
|---|
| 2046 |
|
|---|
| 2047 | didWarnSelectedSetOnOption = true;
|
|---|
| 2048 | }
|
|---|
| 2049 | }
|
|---|
| 2050 | }
|
|---|
| 2051 | function postMountWrapper$1(element, props) {
|
|---|
| 2052 | // value="" should make a value attribute (#6219)
|
|---|
| 2053 | if (props.value != null) {
|
|---|
| 2054 | element.setAttribute('value', toString(getToStringValue(props.value)));
|
|---|
| 2055 | }
|
|---|
| 2056 | }
|
|---|
| 2057 |
|
|---|
| 2058 | var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare
|
|---|
| 2059 |
|
|---|
| 2060 | function isArray(a) {
|
|---|
| 2061 | return isArrayImpl(a);
|
|---|
| 2062 | }
|
|---|
| 2063 |
|
|---|
| 2064 | var didWarnValueDefaultValue$1;
|
|---|
| 2065 |
|
|---|
| 2066 | {
|
|---|
| 2067 | didWarnValueDefaultValue$1 = false;
|
|---|
| 2068 | }
|
|---|
| 2069 |
|
|---|
| 2070 | function getDeclarationErrorAddendum() {
|
|---|
| 2071 | var ownerName = getCurrentFiberOwnerNameInDevOrNull();
|
|---|
| 2072 |
|
|---|
| 2073 | if (ownerName) {
|
|---|
| 2074 | return '\n\nCheck the render method of `' + ownerName + '`.';
|
|---|
| 2075 | }
|
|---|
| 2076 |
|
|---|
| 2077 | return '';
|
|---|
| 2078 | }
|
|---|
| 2079 |
|
|---|
| 2080 | var valuePropNames = ['value', 'defaultValue'];
|
|---|
| 2081 | /**
|
|---|
| 2082 | * Validation function for `value` and `defaultValue`.
|
|---|
| 2083 | */
|
|---|
| 2084 |
|
|---|
| 2085 | function checkSelectPropTypes(props) {
|
|---|
| 2086 | {
|
|---|
| 2087 | checkControlledValueProps('select', props);
|
|---|
| 2088 |
|
|---|
| 2089 | for (var i = 0; i < valuePropNames.length; i++) {
|
|---|
| 2090 | var propName = valuePropNames[i];
|
|---|
| 2091 |
|
|---|
| 2092 | if (props[propName] == null) {
|
|---|
| 2093 | continue;
|
|---|
| 2094 | }
|
|---|
| 2095 |
|
|---|
| 2096 | var propNameIsArray = isArray(props[propName]);
|
|---|
| 2097 |
|
|---|
| 2098 | if (props.multiple && !propNameIsArray) {
|
|---|
| 2099 | error('The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum());
|
|---|
| 2100 | } else if (!props.multiple && propNameIsArray) {
|
|---|
| 2101 | error('The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum());
|
|---|
| 2102 | }
|
|---|
| 2103 | }
|
|---|
| 2104 | }
|
|---|
| 2105 | }
|
|---|
| 2106 |
|
|---|
| 2107 | function updateOptions(node, multiple, propValue, setDefaultSelected) {
|
|---|
| 2108 | var options = node.options;
|
|---|
| 2109 |
|
|---|
| 2110 | if (multiple) {
|
|---|
| 2111 | var selectedValues = propValue;
|
|---|
| 2112 | var selectedValue = {};
|
|---|
| 2113 |
|
|---|
| 2114 | for (var i = 0; i < selectedValues.length; i++) {
|
|---|
| 2115 | // Prefix to avoid chaos with special keys.
|
|---|
| 2116 | selectedValue['$' + selectedValues[i]] = true;
|
|---|
| 2117 | }
|
|---|
| 2118 |
|
|---|
| 2119 | for (var _i = 0; _i < options.length; _i++) {
|
|---|
| 2120 | var selected = selectedValue.hasOwnProperty('$' + options[_i].value);
|
|---|
| 2121 |
|
|---|
| 2122 | if (options[_i].selected !== selected) {
|
|---|
| 2123 | options[_i].selected = selected;
|
|---|
| 2124 | }
|
|---|
| 2125 |
|
|---|
| 2126 | if (selected && setDefaultSelected) {
|
|---|
| 2127 | options[_i].defaultSelected = true;
|
|---|
| 2128 | }
|
|---|
| 2129 | }
|
|---|
| 2130 | } else {
|
|---|
| 2131 | // Do not set `select.value` as exact behavior isn't consistent across all
|
|---|
| 2132 | // browsers for all cases.
|
|---|
| 2133 | var _selectedValue = toString(getToStringValue(propValue));
|
|---|
| 2134 |
|
|---|
| 2135 | var defaultSelected = null;
|
|---|
| 2136 |
|
|---|
| 2137 | for (var _i2 = 0; _i2 < options.length; _i2++) {
|
|---|
| 2138 | if (options[_i2].value === _selectedValue) {
|
|---|
| 2139 | options[_i2].selected = true;
|
|---|
| 2140 |
|
|---|
| 2141 | if (setDefaultSelected) {
|
|---|
| 2142 | options[_i2].defaultSelected = true;
|
|---|
| 2143 | }
|
|---|
| 2144 |
|
|---|
| 2145 | return;
|
|---|
| 2146 | }
|
|---|
| 2147 |
|
|---|
| 2148 | if (defaultSelected === null && !options[_i2].disabled) {
|
|---|
| 2149 | defaultSelected = options[_i2];
|
|---|
| 2150 | }
|
|---|
| 2151 | }
|
|---|
| 2152 |
|
|---|
| 2153 | if (defaultSelected !== null) {
|
|---|
| 2154 | defaultSelected.selected = true;
|
|---|
| 2155 | }
|
|---|
| 2156 | }
|
|---|
| 2157 | }
|
|---|
| 2158 | /**
|
|---|
| 2159 | * Implements a <select> host component that allows optionally setting the
|
|---|
| 2160 | * props `value` and `defaultValue`. If `multiple` is false, the prop must be a
|
|---|
| 2161 | * stringable. If `multiple` is true, the prop must be an array of stringables.
|
|---|
| 2162 | *
|
|---|
| 2163 | * If `value` is not supplied (or null/undefined), user actions that change the
|
|---|
| 2164 | * selected option will trigger updates to the rendered options.
|
|---|
| 2165 | *
|
|---|
| 2166 | * If it is supplied (and not null/undefined), the rendered options will not
|
|---|
| 2167 | * update in response to user actions. Instead, the `value` prop must change in
|
|---|
| 2168 | * order for the rendered options to update.
|
|---|
| 2169 | *
|
|---|
| 2170 | * If `defaultValue` is provided, any options with the supplied values will be
|
|---|
| 2171 | * selected.
|
|---|
| 2172 | */
|
|---|
| 2173 |
|
|---|
| 2174 |
|
|---|
| 2175 | function getHostProps$1(element, props) {
|
|---|
| 2176 | return assign({}, props, {
|
|---|
| 2177 | value: undefined
|
|---|
| 2178 | });
|
|---|
| 2179 | }
|
|---|
| 2180 | function initWrapperState$1(element, props) {
|
|---|
| 2181 | var node = element;
|
|---|
| 2182 |
|
|---|
| 2183 | {
|
|---|
| 2184 | checkSelectPropTypes(props);
|
|---|
| 2185 | }
|
|---|
| 2186 |
|
|---|
| 2187 | node._wrapperState = {
|
|---|
| 2188 | wasMultiple: !!props.multiple
|
|---|
| 2189 | };
|
|---|
| 2190 |
|
|---|
| 2191 | {
|
|---|
| 2192 | if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue$1) {
|
|---|
| 2193 | error('Select elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled select ' + 'element and remove one of these props. More info: ' + 'https://reactjs.org/link/controlled-components');
|
|---|
| 2194 |
|
|---|
| 2195 | didWarnValueDefaultValue$1 = true;
|
|---|
| 2196 | }
|
|---|
| 2197 | }
|
|---|
| 2198 | }
|
|---|
| 2199 | function postMountWrapper$2(element, props) {
|
|---|
| 2200 | var node = element;
|
|---|
| 2201 | node.multiple = !!props.multiple;
|
|---|
| 2202 | var value = props.value;
|
|---|
| 2203 |
|
|---|
| 2204 | if (value != null) {
|
|---|
| 2205 | updateOptions(node, !!props.multiple, value, false);
|
|---|
| 2206 | } else if (props.defaultValue != null) {
|
|---|
| 2207 | updateOptions(node, !!props.multiple, props.defaultValue, true);
|
|---|
| 2208 | }
|
|---|
| 2209 | }
|
|---|
| 2210 | function postUpdateWrapper(element, props) {
|
|---|
| 2211 | var node = element;
|
|---|
| 2212 | var wasMultiple = node._wrapperState.wasMultiple;
|
|---|
| 2213 | node._wrapperState.wasMultiple = !!props.multiple;
|
|---|
| 2214 | var value = props.value;
|
|---|
| 2215 |
|
|---|
| 2216 | if (value != null) {
|
|---|
| 2217 | updateOptions(node, !!props.multiple, value, false);
|
|---|
| 2218 | } else if (wasMultiple !== !!props.multiple) {
|
|---|
| 2219 | // For simplicity, reapply `defaultValue` if `multiple` is toggled.
|
|---|
| 2220 | if (props.defaultValue != null) {
|
|---|
| 2221 | updateOptions(node, !!props.multiple, props.defaultValue, true);
|
|---|
| 2222 | } else {
|
|---|
| 2223 | // Revert the select back to its default unselected state.
|
|---|
| 2224 | updateOptions(node, !!props.multiple, props.multiple ? [] : '', false);
|
|---|
| 2225 | }
|
|---|
| 2226 | }
|
|---|
| 2227 | }
|
|---|
| 2228 | function restoreControlledState$1(element, props) {
|
|---|
| 2229 | var node = element;
|
|---|
| 2230 | var value = props.value;
|
|---|
| 2231 |
|
|---|
| 2232 | if (value != null) {
|
|---|
| 2233 | updateOptions(node, !!props.multiple, value, false);
|
|---|
| 2234 | }
|
|---|
| 2235 | }
|
|---|
| 2236 |
|
|---|
| 2237 | var didWarnValDefaultVal = false;
|
|---|
| 2238 |
|
|---|
| 2239 | /**
|
|---|
| 2240 | * Implements a <textarea> host component that allows setting `value`, and
|
|---|
| 2241 | * `defaultValue`. This differs from the traditional DOM API because value is
|
|---|
| 2242 | * usually set as PCDATA children.
|
|---|
| 2243 | *
|
|---|
| 2244 | * If `value` is not supplied (or null/undefined), user actions that affect the
|
|---|
| 2245 | * value will trigger updates to the element.
|
|---|
| 2246 | *
|
|---|
| 2247 | * If `value` is supplied (and not null/undefined), the rendered element will
|
|---|
| 2248 | * not trigger updates to the element. Instead, the `value` prop must change in
|
|---|
| 2249 | * order for the rendered element to be updated.
|
|---|
| 2250 | *
|
|---|
| 2251 | * The rendered element will be initialized with an empty value, the prop
|
|---|
| 2252 | * `defaultValue` if specified, or the children content (deprecated).
|
|---|
| 2253 | */
|
|---|
| 2254 | function getHostProps$2(element, props) {
|
|---|
| 2255 | var node = element;
|
|---|
| 2256 |
|
|---|
| 2257 | if (props.dangerouslySetInnerHTML != null) {
|
|---|
| 2258 | throw new Error('`dangerouslySetInnerHTML` does not make sense on <textarea>.');
|
|---|
| 2259 | } // Always set children to the same thing. In IE9, the selection range will
|
|---|
| 2260 | // get reset if `textContent` is mutated. We could add a check in setTextContent
|
|---|
| 2261 | // to only set the value if/when the value differs from the node value (which would
|
|---|
| 2262 | // completely solve this IE9 bug), but Sebastian+Sophie seemed to like this
|
|---|
| 2263 | // solution. The value can be a boolean or object so that's why it's forced
|
|---|
| 2264 | // to be a string.
|
|---|
| 2265 |
|
|---|
| 2266 |
|
|---|
| 2267 | var hostProps = assign({}, props, {
|
|---|
| 2268 | value: undefined,
|
|---|
| 2269 | defaultValue: undefined,
|
|---|
| 2270 | children: toString(node._wrapperState.initialValue)
|
|---|
| 2271 | });
|
|---|
| 2272 |
|
|---|
| 2273 | return hostProps;
|
|---|
| 2274 | }
|
|---|
| 2275 | function initWrapperState$2(element, props) {
|
|---|
| 2276 | var node = element;
|
|---|
| 2277 |
|
|---|
| 2278 | {
|
|---|
| 2279 | checkControlledValueProps('textarea', props);
|
|---|
| 2280 |
|
|---|
| 2281 | if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) {
|
|---|
| 2282 | error('%s contains a textarea with both value and defaultValue props. ' + 'Textarea elements must be either controlled or uncontrolled ' + '(specify either the value prop, or the defaultValue prop, but not ' + 'both). Decide between using a controlled or uncontrolled textarea ' + 'and remove one of these props. More info: ' + 'https://reactjs.org/link/controlled-components', getCurrentFiberOwnerNameInDevOrNull() || 'A component');
|
|---|
| 2283 |
|
|---|
| 2284 | didWarnValDefaultVal = true;
|
|---|
| 2285 | }
|
|---|
| 2286 | }
|
|---|
| 2287 |
|
|---|
| 2288 | var initialValue = props.value; // Only bother fetching default value if we're going to use it
|
|---|
| 2289 |
|
|---|
| 2290 | if (initialValue == null) {
|
|---|
| 2291 | var children = props.children,
|
|---|
| 2292 | defaultValue = props.defaultValue;
|
|---|
| 2293 |
|
|---|
| 2294 | if (children != null) {
|
|---|
| 2295 | {
|
|---|
| 2296 | error('Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.');
|
|---|
| 2297 | }
|
|---|
| 2298 |
|
|---|
| 2299 | {
|
|---|
| 2300 | if (defaultValue != null) {
|
|---|
| 2301 | throw new Error('If you supply `defaultValue` on a <textarea>, do not pass children.');
|
|---|
| 2302 | }
|
|---|
| 2303 |
|
|---|
| 2304 | if (isArray(children)) {
|
|---|
| 2305 | if (children.length > 1) {
|
|---|
| 2306 | throw new Error('<textarea> can only have at most one child.');
|
|---|
| 2307 | }
|
|---|
| 2308 |
|
|---|
| 2309 | children = children[0];
|
|---|
| 2310 | }
|
|---|
| 2311 |
|
|---|
| 2312 | defaultValue = children;
|
|---|
| 2313 | }
|
|---|
| 2314 | }
|
|---|
| 2315 |
|
|---|
| 2316 | if (defaultValue == null) {
|
|---|
| 2317 | defaultValue = '';
|
|---|
| 2318 | }
|
|---|
| 2319 |
|
|---|
| 2320 | initialValue = defaultValue;
|
|---|
| 2321 | }
|
|---|
| 2322 |
|
|---|
| 2323 | node._wrapperState = {
|
|---|
| 2324 | initialValue: getToStringValue(initialValue)
|
|---|
| 2325 | };
|
|---|
| 2326 | }
|
|---|
| 2327 | function updateWrapper$1(element, props) {
|
|---|
| 2328 | var node = element;
|
|---|
| 2329 | var value = getToStringValue(props.value);
|
|---|
| 2330 | var defaultValue = getToStringValue(props.defaultValue);
|
|---|
| 2331 |
|
|---|
| 2332 | if (value != null) {
|
|---|
| 2333 | // Cast `value` to a string to ensure the value is set correctly. While
|
|---|
| 2334 | // browsers typically do this as necessary, jsdom doesn't.
|
|---|
| 2335 | var newValue = toString(value); // To avoid side effects (such as losing text selection), only set value if changed
|
|---|
| 2336 |
|
|---|
| 2337 | if (newValue !== node.value) {
|
|---|
| 2338 | node.value = newValue;
|
|---|
| 2339 | }
|
|---|
| 2340 |
|
|---|
| 2341 | if (props.defaultValue == null && node.defaultValue !== newValue) {
|
|---|
| 2342 | node.defaultValue = newValue;
|
|---|
| 2343 | }
|
|---|
| 2344 | }
|
|---|
| 2345 |
|
|---|
| 2346 | if (defaultValue != null) {
|
|---|
| 2347 | node.defaultValue = toString(defaultValue);
|
|---|
| 2348 | }
|
|---|
| 2349 | }
|
|---|
| 2350 | function postMountWrapper$3(element, props) {
|
|---|
| 2351 | var node = element; // This is in postMount because we need access to the DOM node, which is not
|
|---|
| 2352 | // available until after the component has mounted.
|
|---|
| 2353 |
|
|---|
| 2354 | var textContent = node.textContent; // Only set node.value if textContent is equal to the expected
|
|---|
| 2355 | // initial value. In IE10/IE11 there is a bug where the placeholder attribute
|
|---|
| 2356 | // will populate textContent as well.
|
|---|
| 2357 | // https://developer.microsoft.com/microsoft-edge/platform/issues/101525/
|
|---|
| 2358 |
|
|---|
| 2359 | if (textContent === node._wrapperState.initialValue) {
|
|---|
| 2360 | if (textContent !== '' && textContent !== null) {
|
|---|
| 2361 | node.value = textContent;
|
|---|
| 2362 | }
|
|---|
| 2363 | }
|
|---|
| 2364 | }
|
|---|
| 2365 | function restoreControlledState$2(element, props) {
|
|---|
| 2366 | // DOM component is still mounted; update
|
|---|
| 2367 | updateWrapper$1(element, props);
|
|---|
| 2368 | }
|
|---|
| 2369 |
|
|---|
| 2370 | var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|---|
| 2371 | var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML';
|
|---|
| 2372 | var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; // Assumes there is no parent namespace.
|
|---|
| 2373 |
|
|---|
| 2374 | function getIntrinsicNamespace(type) {
|
|---|
| 2375 | switch (type) {
|
|---|
| 2376 | case 'svg':
|
|---|
| 2377 | return SVG_NAMESPACE;
|
|---|
| 2378 |
|
|---|
| 2379 | case 'math':
|
|---|
| 2380 | return MATH_NAMESPACE;
|
|---|
| 2381 |
|
|---|
| 2382 | default:
|
|---|
| 2383 | return HTML_NAMESPACE;
|
|---|
| 2384 | }
|
|---|
| 2385 | }
|
|---|
| 2386 | function getChildNamespace(parentNamespace, type) {
|
|---|
| 2387 | if (parentNamespace == null || parentNamespace === HTML_NAMESPACE) {
|
|---|
| 2388 | // No (or default) parent namespace: potential entry point.
|
|---|
| 2389 | return getIntrinsicNamespace(type);
|
|---|
| 2390 | }
|
|---|
| 2391 |
|
|---|
| 2392 | if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') {
|
|---|
| 2393 | // We're leaving SVG.
|
|---|
| 2394 | return HTML_NAMESPACE;
|
|---|
| 2395 | } // By default, pass namespace below.
|
|---|
| 2396 |
|
|---|
| 2397 |
|
|---|
| 2398 | return parentNamespace;
|
|---|
| 2399 | }
|
|---|
| 2400 |
|
|---|
| 2401 | /* globals MSApp */
|
|---|
| 2402 |
|
|---|
| 2403 | /**
|
|---|
| 2404 | * Create a function which has 'unsafe' privileges (required by windows8 apps)
|
|---|
| 2405 | */
|
|---|
| 2406 | var createMicrosoftUnsafeLocalFunction = function (func) {
|
|---|
| 2407 | if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
|
|---|
| 2408 | return function (arg0, arg1, arg2, arg3) {
|
|---|
| 2409 | MSApp.execUnsafeLocalFunction(function () {
|
|---|
| 2410 | return func(arg0, arg1, arg2, arg3);
|
|---|
| 2411 | });
|
|---|
| 2412 | };
|
|---|
| 2413 | } else {
|
|---|
| 2414 | return func;
|
|---|
| 2415 | }
|
|---|
| 2416 | };
|
|---|
| 2417 |
|
|---|
| 2418 | var reusableSVGContainer;
|
|---|
| 2419 | /**
|
|---|
| 2420 | * Set the innerHTML property of a node
|
|---|
| 2421 | *
|
|---|
| 2422 | * @param {DOMElement} node
|
|---|
| 2423 | * @param {string} html
|
|---|
| 2424 | * @internal
|
|---|
| 2425 | */
|
|---|
| 2426 |
|
|---|
| 2427 | var setInnerHTML = createMicrosoftUnsafeLocalFunction(function (node, html) {
|
|---|
| 2428 | if (node.namespaceURI === SVG_NAMESPACE) {
|
|---|
| 2429 |
|
|---|
| 2430 | if (!('innerHTML' in node)) {
|
|---|
| 2431 | // IE does not have innerHTML for SVG nodes, so instead we inject the
|
|---|
| 2432 | // new markup in a temp node and then move the child nodes across into
|
|---|
| 2433 | // the target node
|
|---|
| 2434 | reusableSVGContainer = reusableSVGContainer || document.createElement('div');
|
|---|
| 2435 | reusableSVGContainer.innerHTML = '<svg>' + html.valueOf().toString() + '</svg>';
|
|---|
| 2436 | var svgNode = reusableSVGContainer.firstChild;
|
|---|
| 2437 |
|
|---|
| 2438 | while (node.firstChild) {
|
|---|
| 2439 | node.removeChild(node.firstChild);
|
|---|
| 2440 | }
|
|---|
| 2441 |
|
|---|
| 2442 | while (svgNode.firstChild) {
|
|---|
| 2443 | node.appendChild(svgNode.firstChild);
|
|---|
| 2444 | }
|
|---|
| 2445 |
|
|---|
| 2446 | return;
|
|---|
| 2447 | }
|
|---|
| 2448 | }
|
|---|
| 2449 |
|
|---|
| 2450 | node.innerHTML = html;
|
|---|
| 2451 | });
|
|---|
| 2452 |
|
|---|
| 2453 | /**
|
|---|
| 2454 | * HTML nodeType values that represent the type of the node
|
|---|
| 2455 | */
|
|---|
| 2456 | var ELEMENT_NODE = 1;
|
|---|
| 2457 | var TEXT_NODE = 3;
|
|---|
| 2458 | var COMMENT_NODE = 8;
|
|---|
| 2459 | var DOCUMENT_NODE = 9;
|
|---|
| 2460 | var DOCUMENT_FRAGMENT_NODE = 11;
|
|---|
| 2461 |
|
|---|
| 2462 | /**
|
|---|
| 2463 | * Set the textContent property of a node. For text updates, it's faster
|
|---|
| 2464 | * to set the `nodeValue` of the Text node directly instead of using
|
|---|
| 2465 | * `.textContent` which will remove the existing node and create a new one.
|
|---|
| 2466 | *
|
|---|
| 2467 | * @param {DOMElement} node
|
|---|
| 2468 | * @param {string} text
|
|---|
| 2469 | * @internal
|
|---|
| 2470 | */
|
|---|
| 2471 |
|
|---|
| 2472 | var setTextContent = function (node, text) {
|
|---|
| 2473 | if (text) {
|
|---|
| 2474 | var firstChild = node.firstChild;
|
|---|
| 2475 |
|
|---|
| 2476 | if (firstChild && firstChild === node.lastChild && firstChild.nodeType === TEXT_NODE) {
|
|---|
| 2477 | firstChild.nodeValue = text;
|
|---|
| 2478 | return;
|
|---|
| 2479 | }
|
|---|
| 2480 | }
|
|---|
| 2481 |
|
|---|
| 2482 | node.textContent = text;
|
|---|
| 2483 | };
|
|---|
| 2484 |
|
|---|
| 2485 | // List derived from Gecko source code:
|
|---|
| 2486 | // https://github.com/mozilla/gecko-dev/blob/4e638efc71/layout/style/test/property_database.js
|
|---|
| 2487 | var shorthandToLonghand = {
|
|---|
| 2488 | animation: ['animationDelay', 'animationDirection', 'animationDuration', 'animationFillMode', 'animationIterationCount', 'animationName', 'animationPlayState', 'animationTimingFunction'],
|
|---|
| 2489 | background: ['backgroundAttachment', 'backgroundClip', 'backgroundColor', 'backgroundImage', 'backgroundOrigin', 'backgroundPositionX', 'backgroundPositionY', 'backgroundRepeat', 'backgroundSize'],
|
|---|
| 2490 | backgroundPosition: ['backgroundPositionX', 'backgroundPositionY'],
|
|---|
| 2491 | border: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth', 'borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderTopColor', 'borderTopStyle', 'borderTopWidth'],
|
|---|
| 2492 | borderBlockEnd: ['borderBlockEndColor', 'borderBlockEndStyle', 'borderBlockEndWidth'],
|
|---|
| 2493 | borderBlockStart: ['borderBlockStartColor', 'borderBlockStartStyle', 'borderBlockStartWidth'],
|
|---|
| 2494 | borderBottom: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth'],
|
|---|
| 2495 | borderColor: ['borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor'],
|
|---|
| 2496 | borderImage: ['borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth'],
|
|---|
| 2497 | borderInlineEnd: ['borderInlineEndColor', 'borderInlineEndStyle', 'borderInlineEndWidth'],
|
|---|
| 2498 | borderInlineStart: ['borderInlineStartColor', 'borderInlineStartStyle', 'borderInlineStartWidth'],
|
|---|
| 2499 | borderLeft: ['borderLeftColor', 'borderLeftStyle', 'borderLeftWidth'],
|
|---|
| 2500 | borderRadius: ['borderBottomLeftRadius', 'borderBottomRightRadius', 'borderTopLeftRadius', 'borderTopRightRadius'],
|
|---|
| 2501 | borderRight: ['borderRightColor', 'borderRightStyle', 'borderRightWidth'],
|
|---|
| 2502 | borderStyle: ['borderBottomStyle', 'borderLeftStyle', 'borderRightStyle', 'borderTopStyle'],
|
|---|
| 2503 | borderTop: ['borderTopColor', 'borderTopStyle', 'borderTopWidth'],
|
|---|
| 2504 | borderWidth: ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth'],
|
|---|
| 2505 | columnRule: ['columnRuleColor', 'columnRuleStyle', 'columnRuleWidth'],
|
|---|
| 2506 | columns: ['columnCount', 'columnWidth'],
|
|---|
| 2507 | flex: ['flexBasis', 'flexGrow', 'flexShrink'],
|
|---|
| 2508 | flexFlow: ['flexDirection', 'flexWrap'],
|
|---|
| 2509 | font: ['fontFamily', 'fontFeatureSettings', 'fontKerning', 'fontLanguageOverride', 'fontSize', 'fontSizeAdjust', 'fontStretch', 'fontStyle', 'fontVariant', 'fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition', 'fontWeight', 'lineHeight'],
|
|---|
| 2510 | fontVariant: ['fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition'],
|
|---|
| 2511 | gap: ['columnGap', 'rowGap'],
|
|---|
| 2512 | grid: ['gridAutoColumns', 'gridAutoFlow', 'gridAutoRows', 'gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'],
|
|---|
| 2513 | gridArea: ['gridColumnEnd', 'gridColumnStart', 'gridRowEnd', 'gridRowStart'],
|
|---|
| 2514 | gridColumn: ['gridColumnEnd', 'gridColumnStart'],
|
|---|
| 2515 | gridColumnGap: ['columnGap'],
|
|---|
| 2516 | gridGap: ['columnGap', 'rowGap'],
|
|---|
| 2517 | gridRow: ['gridRowEnd', 'gridRowStart'],
|
|---|
| 2518 | gridRowGap: ['rowGap'],
|
|---|
| 2519 | gridTemplate: ['gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'],
|
|---|
| 2520 | listStyle: ['listStyleImage', 'listStylePosition', 'listStyleType'],
|
|---|
| 2521 | margin: ['marginBottom', 'marginLeft', 'marginRight', 'marginTop'],
|
|---|
| 2522 | marker: ['markerEnd', 'markerMid', 'markerStart'],
|
|---|
| 2523 | mask: ['maskClip', 'maskComposite', 'maskImage', 'maskMode', 'maskOrigin', 'maskPositionX', 'maskPositionY', 'maskRepeat', 'maskSize'],
|
|---|
| 2524 | maskPosition: ['maskPositionX', 'maskPositionY'],
|
|---|
| 2525 | outline: ['outlineColor', 'outlineStyle', 'outlineWidth'],
|
|---|
| 2526 | overflow: ['overflowX', 'overflowY'],
|
|---|
| 2527 | padding: ['paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop'],
|
|---|
| 2528 | placeContent: ['alignContent', 'justifyContent'],
|
|---|
| 2529 | placeItems: ['alignItems', 'justifyItems'],
|
|---|
| 2530 | placeSelf: ['alignSelf', 'justifySelf'],
|
|---|
| 2531 | textDecoration: ['textDecorationColor', 'textDecorationLine', 'textDecorationStyle'],
|
|---|
| 2532 | textEmphasis: ['textEmphasisColor', 'textEmphasisStyle'],
|
|---|
| 2533 | transition: ['transitionDelay', 'transitionDuration', 'transitionProperty', 'transitionTimingFunction'],
|
|---|
| 2534 | wordWrap: ['overflowWrap']
|
|---|
| 2535 | };
|
|---|
| 2536 |
|
|---|
| 2537 | /**
|
|---|
| 2538 | * CSS properties which accept numbers but are not in units of "px".
|
|---|
| 2539 | */
|
|---|
| 2540 | var isUnitlessNumber = {
|
|---|
| 2541 | animationIterationCount: true,
|
|---|
| 2542 | aspectRatio: true,
|
|---|
| 2543 | borderImageOutset: true,
|
|---|
| 2544 | borderImageSlice: true,
|
|---|
| 2545 | borderImageWidth: true,
|
|---|
| 2546 | boxFlex: true,
|
|---|
| 2547 | boxFlexGroup: true,
|
|---|
| 2548 | boxOrdinalGroup: true,
|
|---|
| 2549 | columnCount: true,
|
|---|
| 2550 | columns: true,
|
|---|
| 2551 | flex: true,
|
|---|
| 2552 | flexGrow: true,
|
|---|
| 2553 | flexPositive: true,
|
|---|
| 2554 | flexShrink: true,
|
|---|
| 2555 | flexNegative: true,
|
|---|
| 2556 | flexOrder: true,
|
|---|
| 2557 | gridArea: true,
|
|---|
| 2558 | gridRow: true,
|
|---|
| 2559 | gridRowEnd: true,
|
|---|
| 2560 | gridRowSpan: true,
|
|---|
| 2561 | gridRowStart: true,
|
|---|
| 2562 | gridColumn: true,
|
|---|
| 2563 | gridColumnEnd: true,
|
|---|
| 2564 | gridColumnSpan: true,
|
|---|
| 2565 | gridColumnStart: true,
|
|---|
| 2566 | fontWeight: true,
|
|---|
| 2567 | lineClamp: true,
|
|---|
| 2568 | lineHeight: true,
|
|---|
| 2569 | opacity: true,
|
|---|
| 2570 | order: true,
|
|---|
| 2571 | orphans: true,
|
|---|
| 2572 | tabSize: true,
|
|---|
| 2573 | widows: true,
|
|---|
| 2574 | zIndex: true,
|
|---|
| 2575 | zoom: true,
|
|---|
| 2576 | // SVG-related properties
|
|---|
| 2577 | fillOpacity: true,
|
|---|
| 2578 | floodOpacity: true,
|
|---|
| 2579 | stopOpacity: true,
|
|---|
| 2580 | strokeDasharray: true,
|
|---|
| 2581 | strokeDashoffset: true,
|
|---|
| 2582 | strokeMiterlimit: true,
|
|---|
| 2583 | strokeOpacity: true,
|
|---|
| 2584 | strokeWidth: true
|
|---|
| 2585 | };
|
|---|
| 2586 | /**
|
|---|
| 2587 | * @param {string} prefix vendor-specific prefix, eg: Webkit
|
|---|
| 2588 | * @param {string} key style name, eg: transitionDuration
|
|---|
| 2589 | * @return {string} style name prefixed with `prefix`, properly camelCased, eg:
|
|---|
| 2590 | * WebkitTransitionDuration
|
|---|
| 2591 | */
|
|---|
| 2592 |
|
|---|
| 2593 | function prefixKey(prefix, key) {
|
|---|
| 2594 | return prefix + key.charAt(0).toUpperCase() + key.substring(1);
|
|---|
| 2595 | }
|
|---|
| 2596 | /**
|
|---|
| 2597 | * Support style names that may come passed in prefixed by adding permutations
|
|---|
| 2598 | * of vendor prefixes.
|
|---|
| 2599 | */
|
|---|
| 2600 |
|
|---|
| 2601 |
|
|---|
| 2602 | var prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an
|
|---|
| 2603 | // infinite loop, because it iterates over the newly added props too.
|
|---|
| 2604 |
|
|---|
| 2605 | Object.keys(isUnitlessNumber).forEach(function (prop) {
|
|---|
| 2606 | prefixes.forEach(function (prefix) {
|
|---|
| 2607 | isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop];
|
|---|
| 2608 | });
|
|---|
| 2609 | });
|
|---|
| 2610 |
|
|---|
| 2611 | /**
|
|---|
| 2612 | * Convert a value into the proper css writable value. The style name `name`
|
|---|
| 2613 | * should be logical (no hyphens), as specified
|
|---|
| 2614 | * in `CSSProperty.isUnitlessNumber`.
|
|---|
| 2615 | *
|
|---|
| 2616 | * @param {string} name CSS property name such as `topMargin`.
|
|---|
| 2617 | * @param {*} value CSS property value such as `10px`.
|
|---|
| 2618 | * @return {string} Normalized style value with dimensions applied.
|
|---|
| 2619 | */
|
|---|
| 2620 |
|
|---|
| 2621 | function dangerousStyleValue(name, value, isCustomProperty) {
|
|---|
| 2622 | // Note that we've removed escapeTextForBrowser() calls here since the
|
|---|
| 2623 | // whole string will be escaped when the attribute is injected into
|
|---|
| 2624 | // the markup. If you provide unsafe user data here they can inject
|
|---|
| 2625 | // arbitrary CSS which may be problematic (I couldn't repro this):
|
|---|
| 2626 | // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
|---|
| 2627 | // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/
|
|---|
| 2628 | // This is not an XSS hole but instead a potential CSS injection issue
|
|---|
| 2629 | // which has lead to a greater discussion about how we're going to
|
|---|
| 2630 | // trust URLs moving forward. See #2115901
|
|---|
| 2631 | var isEmpty = value == null || typeof value === 'boolean' || value === '';
|
|---|
| 2632 |
|
|---|
| 2633 | if (isEmpty) {
|
|---|
| 2634 | return '';
|
|---|
| 2635 | }
|
|---|
| 2636 |
|
|---|
| 2637 | if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) {
|
|---|
| 2638 | return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers
|
|---|
| 2639 | }
|
|---|
| 2640 |
|
|---|
| 2641 | {
|
|---|
| 2642 | checkCSSPropertyStringCoercion(value, name);
|
|---|
| 2643 | }
|
|---|
| 2644 |
|
|---|
| 2645 | return ('' + value).trim();
|
|---|
| 2646 | }
|
|---|
| 2647 |
|
|---|
| 2648 | var uppercasePattern = /([A-Z])/g;
|
|---|
| 2649 | var msPattern = /^ms-/;
|
|---|
| 2650 | /**
|
|---|
| 2651 | * Hyphenates a camelcased CSS property name, for example:
|
|---|
| 2652 | *
|
|---|
| 2653 | * > hyphenateStyleName('backgroundColor')
|
|---|
| 2654 | * < "background-color"
|
|---|
| 2655 | * > hyphenateStyleName('MozTransition')
|
|---|
| 2656 | * < "-moz-transition"
|
|---|
| 2657 | * > hyphenateStyleName('msTransition')
|
|---|
| 2658 | * < "-ms-transition"
|
|---|
| 2659 | *
|
|---|
| 2660 | * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix
|
|---|
| 2661 | * is converted to `-ms-`.
|
|---|
| 2662 | */
|
|---|
| 2663 |
|
|---|
| 2664 | function hyphenateStyleName(name) {
|
|---|
| 2665 | return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-');
|
|---|
| 2666 | }
|
|---|
| 2667 |
|
|---|
| 2668 | var warnValidStyle = function () {};
|
|---|
| 2669 |
|
|---|
| 2670 | {
|
|---|
| 2671 | // 'msTransform' is correct, but the other prefixes should be capitalized
|
|---|
| 2672 | var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
|
|---|
| 2673 | var msPattern$1 = /^-ms-/;
|
|---|
| 2674 | var hyphenPattern = /-(.)/g; // style values shouldn't contain a semicolon
|
|---|
| 2675 |
|
|---|
| 2676 | var badStyleValueWithSemicolonPattern = /;\s*$/;
|
|---|
| 2677 | var warnedStyleNames = {};
|
|---|
| 2678 | var warnedStyleValues = {};
|
|---|
| 2679 | var warnedForNaNValue = false;
|
|---|
| 2680 | var warnedForInfinityValue = false;
|
|---|
| 2681 |
|
|---|
| 2682 | var camelize = function (string) {
|
|---|
| 2683 | return string.replace(hyphenPattern, function (_, character) {
|
|---|
| 2684 | return character.toUpperCase();
|
|---|
| 2685 | });
|
|---|
| 2686 | };
|
|---|
| 2687 |
|
|---|
| 2688 | var warnHyphenatedStyleName = function (name) {
|
|---|
| 2689 | if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|---|
| 2690 | return;
|
|---|
| 2691 | }
|
|---|
| 2692 |
|
|---|
| 2693 | warnedStyleNames[name] = true;
|
|---|
| 2694 |
|
|---|
| 2695 | error('Unsupported style property %s. Did you mean %s?', name, // As Andi Smith suggests
|
|---|
| 2696 | // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix
|
|---|
| 2697 | // is converted to lowercase `ms`.
|
|---|
| 2698 | camelize(name.replace(msPattern$1, 'ms-')));
|
|---|
| 2699 | };
|
|---|
| 2700 |
|
|---|
| 2701 | var warnBadVendoredStyleName = function (name) {
|
|---|
| 2702 | if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
|
|---|
| 2703 | return;
|
|---|
| 2704 | }
|
|---|
| 2705 |
|
|---|
| 2706 | warnedStyleNames[name] = true;
|
|---|
| 2707 |
|
|---|
| 2708 | error('Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1));
|
|---|
| 2709 | };
|
|---|
| 2710 |
|
|---|
| 2711 | var warnStyleValueWithSemicolon = function (name, value) {
|
|---|
| 2712 | if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
|
|---|
| 2713 | return;
|
|---|
| 2714 | }
|
|---|
| 2715 |
|
|---|
| 2716 | warnedStyleValues[value] = true;
|
|---|
| 2717 |
|
|---|
| 2718 | error("Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, ''));
|
|---|
| 2719 | };
|
|---|
| 2720 |
|
|---|
| 2721 | var warnStyleValueIsNaN = function (name, value) {
|
|---|
| 2722 | if (warnedForNaNValue) {
|
|---|
| 2723 | return;
|
|---|
| 2724 | }
|
|---|
| 2725 |
|
|---|
| 2726 | warnedForNaNValue = true;
|
|---|
| 2727 |
|
|---|
| 2728 | error('`NaN` is an invalid value for the `%s` css style property.', name);
|
|---|
| 2729 | };
|
|---|
| 2730 |
|
|---|
| 2731 | var warnStyleValueIsInfinity = function (name, value) {
|
|---|
| 2732 | if (warnedForInfinityValue) {
|
|---|
| 2733 | return;
|
|---|
| 2734 | }
|
|---|
| 2735 |
|
|---|
| 2736 | warnedForInfinityValue = true;
|
|---|
| 2737 |
|
|---|
| 2738 | error('`Infinity` is an invalid value for the `%s` css style property.', name);
|
|---|
| 2739 | };
|
|---|
| 2740 |
|
|---|
| 2741 | warnValidStyle = function (name, value) {
|
|---|
| 2742 | if (name.indexOf('-') > -1) {
|
|---|
| 2743 | warnHyphenatedStyleName(name);
|
|---|
| 2744 | } else if (badVendoredStyleNamePattern.test(name)) {
|
|---|
| 2745 | warnBadVendoredStyleName(name);
|
|---|
| 2746 | } else if (badStyleValueWithSemicolonPattern.test(value)) {
|
|---|
| 2747 | warnStyleValueWithSemicolon(name, value);
|
|---|
| 2748 | }
|
|---|
| 2749 |
|
|---|
| 2750 | if (typeof value === 'number') {
|
|---|
| 2751 | if (isNaN(value)) {
|
|---|
| 2752 | warnStyleValueIsNaN(name, value);
|
|---|
| 2753 | } else if (!isFinite(value)) {
|
|---|
| 2754 | warnStyleValueIsInfinity(name, value);
|
|---|
| 2755 | }
|
|---|
| 2756 | }
|
|---|
| 2757 | };
|
|---|
| 2758 | }
|
|---|
| 2759 |
|
|---|
| 2760 | var warnValidStyle$1 = warnValidStyle;
|
|---|
| 2761 |
|
|---|
| 2762 | /**
|
|---|
| 2763 | * Operations for dealing with CSS properties.
|
|---|
| 2764 | */
|
|---|
| 2765 |
|
|---|
| 2766 | /**
|
|---|
| 2767 | * This creates a string that is expected to be equivalent to the style
|
|---|
| 2768 | * attribute generated by server-side rendering. It by-passes warnings and
|
|---|
| 2769 | * security checks so it's not safe to use this value for anything other than
|
|---|
| 2770 | * comparison. It is only used in DEV for SSR validation.
|
|---|
| 2771 | */
|
|---|
| 2772 |
|
|---|
| 2773 | function createDangerousStringForStyles(styles) {
|
|---|
| 2774 | {
|
|---|
| 2775 | var serialized = '';
|
|---|
| 2776 | var delimiter = '';
|
|---|
| 2777 |
|
|---|
| 2778 | for (var styleName in styles) {
|
|---|
| 2779 | if (!styles.hasOwnProperty(styleName)) {
|
|---|
| 2780 | continue;
|
|---|
| 2781 | }
|
|---|
| 2782 |
|
|---|
| 2783 | var styleValue = styles[styleName];
|
|---|
| 2784 |
|
|---|
| 2785 | if (styleValue != null) {
|
|---|
| 2786 | var isCustomProperty = styleName.indexOf('--') === 0;
|
|---|
| 2787 | serialized += delimiter + (isCustomProperty ? styleName : hyphenateStyleName(styleName)) + ':';
|
|---|
| 2788 | serialized += dangerousStyleValue(styleName, styleValue, isCustomProperty);
|
|---|
| 2789 | delimiter = ';';
|
|---|
| 2790 | }
|
|---|
| 2791 | }
|
|---|
| 2792 |
|
|---|
| 2793 | return serialized || null;
|
|---|
| 2794 | }
|
|---|
| 2795 | }
|
|---|
| 2796 | /**
|
|---|
| 2797 | * Sets the value for multiple styles on a node. If a value is specified as
|
|---|
| 2798 | * '' (empty string), the corresponding style property will be unset.
|
|---|
| 2799 | *
|
|---|
| 2800 | * @param {DOMElement} node
|
|---|
| 2801 | * @param {object} styles
|
|---|
| 2802 | */
|
|---|
| 2803 |
|
|---|
| 2804 | function setValueForStyles(node, styles) {
|
|---|
| 2805 | var style = node.style;
|
|---|
| 2806 |
|
|---|
| 2807 | for (var styleName in styles) {
|
|---|
| 2808 | if (!styles.hasOwnProperty(styleName)) {
|
|---|
| 2809 | continue;
|
|---|
| 2810 | }
|
|---|
| 2811 |
|
|---|
| 2812 | var isCustomProperty = styleName.indexOf('--') === 0;
|
|---|
| 2813 |
|
|---|
| 2814 | {
|
|---|
| 2815 | if (!isCustomProperty) {
|
|---|
| 2816 | warnValidStyle$1(styleName, styles[styleName]);
|
|---|
| 2817 | }
|
|---|
| 2818 | }
|
|---|
| 2819 |
|
|---|
| 2820 | var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty);
|
|---|
| 2821 |
|
|---|
| 2822 | if (styleName === 'float') {
|
|---|
| 2823 | styleName = 'cssFloat';
|
|---|
| 2824 | }
|
|---|
| 2825 |
|
|---|
| 2826 | if (isCustomProperty) {
|
|---|
| 2827 | style.setProperty(styleName, styleValue);
|
|---|
| 2828 | } else {
|
|---|
| 2829 | style[styleName] = styleValue;
|
|---|
| 2830 | }
|
|---|
| 2831 | }
|
|---|
| 2832 | }
|
|---|
| 2833 |
|
|---|
| 2834 | function isValueEmpty(value) {
|
|---|
| 2835 | return value == null || typeof value === 'boolean' || value === '';
|
|---|
| 2836 | }
|
|---|
| 2837 | /**
|
|---|
| 2838 | * Given {color: 'red', overflow: 'hidden'} returns {
|
|---|
| 2839 | * color: 'color',
|
|---|
| 2840 | * overflowX: 'overflow',
|
|---|
| 2841 | * overflowY: 'overflow',
|
|---|
| 2842 | * }. This can be read as "the overflowY property was set by the overflow
|
|---|
| 2843 | * shorthand". That is, the values are the property that each was derived from.
|
|---|
| 2844 | */
|
|---|
| 2845 |
|
|---|
| 2846 |
|
|---|
| 2847 | function expandShorthandMap(styles) {
|
|---|
| 2848 | var expanded = {};
|
|---|
| 2849 |
|
|---|
| 2850 | for (var key in styles) {
|
|---|
| 2851 | var longhands = shorthandToLonghand[key] || [key];
|
|---|
| 2852 |
|
|---|
| 2853 | for (var i = 0; i < longhands.length; i++) {
|
|---|
| 2854 | expanded[longhands[i]] = key;
|
|---|
| 2855 | }
|
|---|
| 2856 | }
|
|---|
| 2857 |
|
|---|
| 2858 | return expanded;
|
|---|
| 2859 | }
|
|---|
| 2860 | /**
|
|---|
| 2861 | * When mixing shorthand and longhand property names, we warn during updates if
|
|---|
| 2862 | * we expect an incorrect result to occur. In particular, we warn for:
|
|---|
| 2863 | *
|
|---|
| 2864 | * Updating a shorthand property (longhand gets overwritten):
|
|---|
| 2865 | * {font: 'foo', fontVariant: 'bar'} -> {font: 'baz', fontVariant: 'bar'}
|
|---|
| 2866 | * becomes .style.font = 'baz'
|
|---|
| 2867 | * Removing a shorthand property (longhand gets lost too):
|
|---|
| 2868 | * {font: 'foo', fontVariant: 'bar'} -> {fontVariant: 'bar'}
|
|---|
| 2869 | * becomes .style.font = ''
|
|---|
| 2870 | * Removing a longhand property (should revert to shorthand; doesn't):
|
|---|
| 2871 | * {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'}
|
|---|
| 2872 | * becomes .style.fontVariant = ''
|
|---|
| 2873 | */
|
|---|
| 2874 |
|
|---|
| 2875 |
|
|---|
| 2876 | function validateShorthandPropertyCollisionInDev(styleUpdates, nextStyles) {
|
|---|
| 2877 | {
|
|---|
| 2878 | if (!nextStyles) {
|
|---|
| 2879 | return;
|
|---|
| 2880 | }
|
|---|
| 2881 |
|
|---|
| 2882 | var expandedUpdates = expandShorthandMap(styleUpdates);
|
|---|
| 2883 | var expandedStyles = expandShorthandMap(nextStyles);
|
|---|
| 2884 | var warnedAbout = {};
|
|---|
| 2885 |
|
|---|
| 2886 | for (var key in expandedUpdates) {
|
|---|
| 2887 | var originalKey = expandedUpdates[key];
|
|---|
| 2888 | var correctOriginalKey = expandedStyles[key];
|
|---|
| 2889 |
|
|---|
| 2890 | if (correctOriginalKey && originalKey !== correctOriginalKey) {
|
|---|
| 2891 | var warningKey = originalKey + ',' + correctOriginalKey;
|
|---|
| 2892 |
|
|---|
| 2893 | if (warnedAbout[warningKey]) {
|
|---|
| 2894 | continue;
|
|---|
| 2895 | }
|
|---|
| 2896 |
|
|---|
| 2897 | warnedAbout[warningKey] = true;
|
|---|
| 2898 |
|
|---|
| 2899 | error('%s a style property during rerender (%s) when a ' + 'conflicting property is set (%s) can lead to styling bugs. To ' + "avoid this, don't mix shorthand and non-shorthand properties " + 'for the same value; instead, replace the shorthand with ' + 'separate values.', isValueEmpty(styleUpdates[originalKey]) ? 'Removing' : 'Updating', originalKey, correctOriginalKey);
|
|---|
| 2900 | }
|
|---|
| 2901 | }
|
|---|
| 2902 | }
|
|---|
| 2903 | }
|
|---|
| 2904 |
|
|---|
| 2905 | // For HTML, certain tags should omit their close tag. We keep a list for
|
|---|
| 2906 | // those special-case tags.
|
|---|
| 2907 | var omittedCloseTags = {
|
|---|
| 2908 | area: true,
|
|---|
| 2909 | base: true,
|
|---|
| 2910 | br: true,
|
|---|
| 2911 | col: true,
|
|---|
| 2912 | embed: true,
|
|---|
| 2913 | hr: true,
|
|---|
| 2914 | img: true,
|
|---|
| 2915 | input: true,
|
|---|
| 2916 | keygen: true,
|
|---|
| 2917 | link: true,
|
|---|
| 2918 | meta: true,
|
|---|
| 2919 | param: true,
|
|---|
| 2920 | source: true,
|
|---|
| 2921 | track: true,
|
|---|
| 2922 | wbr: true // NOTE: menuitem's close tag should be omitted, but that causes problems.
|
|---|
| 2923 |
|
|---|
| 2924 | };
|
|---|
| 2925 |
|
|---|
| 2926 | // `omittedCloseTags` except that `menuitem` should still have its closing tag.
|
|---|
| 2927 |
|
|---|
| 2928 | var voidElementTags = assign({
|
|---|
| 2929 | menuitem: true
|
|---|
| 2930 | }, omittedCloseTags);
|
|---|
| 2931 |
|
|---|
| 2932 | var HTML = '__html';
|
|---|
| 2933 |
|
|---|
| 2934 | function assertValidProps(tag, props) {
|
|---|
| 2935 | if (!props) {
|
|---|
| 2936 | return;
|
|---|
| 2937 | } // Note the use of `==` which checks for null or undefined.
|
|---|
| 2938 |
|
|---|
| 2939 |
|
|---|
| 2940 | if (voidElementTags[tag]) {
|
|---|
| 2941 | if (props.children != null || props.dangerouslySetInnerHTML != null) {
|
|---|
| 2942 | throw new Error(tag + " is a void element tag and must neither have `children` nor " + 'use `dangerouslySetInnerHTML`.');
|
|---|
| 2943 | }
|
|---|
| 2944 | }
|
|---|
| 2945 |
|
|---|
| 2946 | if (props.dangerouslySetInnerHTML != null) {
|
|---|
| 2947 | if (props.children != null) {
|
|---|
| 2948 | throw new Error('Can only set one of `children` or `props.dangerouslySetInnerHTML`.');
|
|---|
| 2949 | }
|
|---|
| 2950 |
|
|---|
| 2951 | if (typeof props.dangerouslySetInnerHTML !== 'object' || !(HTML in props.dangerouslySetInnerHTML)) {
|
|---|
| 2952 | throw new Error('`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' + 'for more information.');
|
|---|
| 2953 | }
|
|---|
| 2954 | }
|
|---|
| 2955 |
|
|---|
| 2956 | {
|
|---|
| 2957 | if (!props.suppressContentEditableWarning && props.contentEditable && props.children != null) {
|
|---|
| 2958 | error('A component is `contentEditable` and contains `children` managed by ' + 'React. It is now your responsibility to guarantee that none of ' + 'those nodes are unexpectedly modified or duplicated. This is ' + 'probably not intentional.');
|
|---|
| 2959 | }
|
|---|
| 2960 | }
|
|---|
| 2961 |
|
|---|
| 2962 | if (props.style != null && typeof props.style !== 'object') {
|
|---|
| 2963 | throw new Error('The `style` prop expects a mapping from style properties to values, ' + "not a string. For example, style={{marginRight: spacing + 'em'}} when " + 'using JSX.');
|
|---|
| 2964 | }
|
|---|
| 2965 | }
|
|---|
| 2966 |
|
|---|
| 2967 | function isCustomComponent(tagName, props) {
|
|---|
| 2968 | if (tagName.indexOf('-') === -1) {
|
|---|
| 2969 | return typeof props.is === 'string';
|
|---|
| 2970 | }
|
|---|
| 2971 |
|
|---|
| 2972 | switch (tagName) {
|
|---|
| 2973 | // These are reserved SVG and MathML elements.
|
|---|
| 2974 | // We don't mind this list too much because we expect it to never grow.
|
|---|
| 2975 | // The alternative is to track the namespace in a few places which is convoluted.
|
|---|
| 2976 | // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts
|
|---|
| 2977 | case 'annotation-xml':
|
|---|
| 2978 | case 'color-profile':
|
|---|
| 2979 | case 'font-face':
|
|---|
| 2980 | case 'font-face-src':
|
|---|
| 2981 | case 'font-face-uri':
|
|---|
| 2982 | case 'font-face-format':
|
|---|
| 2983 | case 'font-face-name':
|
|---|
| 2984 | case 'missing-glyph':
|
|---|
| 2985 | return false;
|
|---|
| 2986 |
|
|---|
| 2987 | default:
|
|---|
| 2988 | return true;
|
|---|
| 2989 | }
|
|---|
| 2990 | }
|
|---|
| 2991 |
|
|---|
| 2992 | // When adding attributes to the HTML or SVG allowed attribute list, be sure to
|
|---|
| 2993 | // also add them to this module to ensure casing and incorrect name
|
|---|
| 2994 | // warnings.
|
|---|
| 2995 | var possibleStandardNames = {
|
|---|
| 2996 | // HTML
|
|---|
| 2997 | accept: 'accept',
|
|---|
| 2998 | acceptcharset: 'acceptCharset',
|
|---|
| 2999 | 'accept-charset': 'acceptCharset',
|
|---|
| 3000 | accesskey: 'accessKey',
|
|---|
| 3001 | action: 'action',
|
|---|
| 3002 | allowfullscreen: 'allowFullScreen',
|
|---|
| 3003 | alt: 'alt',
|
|---|
| 3004 | as: 'as',
|
|---|
| 3005 | async: 'async',
|
|---|
| 3006 | autocapitalize: 'autoCapitalize',
|
|---|
| 3007 | autocomplete: 'autoComplete',
|
|---|
| 3008 | autocorrect: 'autoCorrect',
|
|---|
| 3009 | autofocus: 'autoFocus',
|
|---|
| 3010 | autoplay: 'autoPlay',
|
|---|
| 3011 | autosave: 'autoSave',
|
|---|
| 3012 | capture: 'capture',
|
|---|
| 3013 | cellpadding: 'cellPadding',
|
|---|
| 3014 | cellspacing: 'cellSpacing',
|
|---|
| 3015 | challenge: 'challenge',
|
|---|
| 3016 | charset: 'charSet',
|
|---|
| 3017 | checked: 'checked',
|
|---|
| 3018 | children: 'children',
|
|---|
| 3019 | cite: 'cite',
|
|---|
| 3020 | class: 'className',
|
|---|
| 3021 | classid: 'classID',
|
|---|
| 3022 | classname: 'className',
|
|---|
| 3023 | cols: 'cols',
|
|---|
| 3024 | colspan: 'colSpan',
|
|---|
| 3025 | content: 'content',
|
|---|
| 3026 | contenteditable: 'contentEditable',
|
|---|
| 3027 | contextmenu: 'contextMenu',
|
|---|
| 3028 | controls: 'controls',
|
|---|
| 3029 | controlslist: 'controlsList',
|
|---|
| 3030 | coords: 'coords',
|
|---|
| 3031 | crossorigin: 'crossOrigin',
|
|---|
| 3032 | dangerouslysetinnerhtml: 'dangerouslySetInnerHTML',
|
|---|
| 3033 | data: 'data',
|
|---|
| 3034 | datetime: 'dateTime',
|
|---|
| 3035 | default: 'default',
|
|---|
| 3036 | defaultchecked: 'defaultChecked',
|
|---|
| 3037 | defaultvalue: 'defaultValue',
|
|---|
| 3038 | defer: 'defer',
|
|---|
| 3039 | dir: 'dir',
|
|---|
| 3040 | disabled: 'disabled',
|
|---|
| 3041 | disablepictureinpicture: 'disablePictureInPicture',
|
|---|
| 3042 | disableremoteplayback: 'disableRemotePlayback',
|
|---|
| 3043 | download: 'download',
|
|---|
| 3044 | draggable: 'draggable',
|
|---|
| 3045 | enctype: 'encType',
|
|---|
| 3046 | enterkeyhint: 'enterKeyHint',
|
|---|
| 3047 | for: 'htmlFor',
|
|---|
| 3048 | form: 'form',
|
|---|
| 3049 | formmethod: 'formMethod',
|
|---|
| 3050 | formaction: 'formAction',
|
|---|
| 3051 | formenctype: 'formEncType',
|
|---|
| 3052 | formnovalidate: 'formNoValidate',
|
|---|
| 3053 | formtarget: 'formTarget',
|
|---|
| 3054 | frameborder: 'frameBorder',
|
|---|
| 3055 | headers: 'headers',
|
|---|
| 3056 | height: 'height',
|
|---|
| 3057 | hidden: 'hidden',
|
|---|
| 3058 | high: 'high',
|
|---|
| 3059 | href: 'href',
|
|---|
| 3060 | hreflang: 'hrefLang',
|
|---|
| 3061 | htmlfor: 'htmlFor',
|
|---|
| 3062 | httpequiv: 'httpEquiv',
|
|---|
| 3063 | 'http-equiv': 'httpEquiv',
|
|---|
| 3064 | icon: 'icon',
|
|---|
| 3065 | id: 'id',
|
|---|
| 3066 | imagesizes: 'imageSizes',
|
|---|
| 3067 | imagesrcset: 'imageSrcSet',
|
|---|
| 3068 | innerhtml: 'innerHTML',
|
|---|
| 3069 | inputmode: 'inputMode',
|
|---|
| 3070 | integrity: 'integrity',
|
|---|
| 3071 | is: 'is',
|
|---|
| 3072 | itemid: 'itemID',
|
|---|
| 3073 | itemprop: 'itemProp',
|
|---|
| 3074 | itemref: 'itemRef',
|
|---|
| 3075 | itemscope: 'itemScope',
|
|---|
| 3076 | itemtype: 'itemType',
|
|---|
| 3077 | keyparams: 'keyParams',
|
|---|
| 3078 | keytype: 'keyType',
|
|---|
| 3079 | kind: 'kind',
|
|---|
| 3080 | label: 'label',
|
|---|
| 3081 | lang: 'lang',
|
|---|
| 3082 | list: 'list',
|
|---|
| 3083 | loop: 'loop',
|
|---|
| 3084 | low: 'low',
|
|---|
| 3085 | manifest: 'manifest',
|
|---|
| 3086 | marginwidth: 'marginWidth',
|
|---|
| 3087 | marginheight: 'marginHeight',
|
|---|
| 3088 | max: 'max',
|
|---|
| 3089 | maxlength: 'maxLength',
|
|---|
| 3090 | media: 'media',
|
|---|
| 3091 | mediagroup: 'mediaGroup',
|
|---|
| 3092 | method: 'method',
|
|---|
| 3093 | min: 'min',
|
|---|
| 3094 | minlength: 'minLength',
|
|---|
| 3095 | multiple: 'multiple',
|
|---|
| 3096 | muted: 'muted',
|
|---|
| 3097 | name: 'name',
|
|---|
| 3098 | nomodule: 'noModule',
|
|---|
| 3099 | nonce: 'nonce',
|
|---|
| 3100 | novalidate: 'noValidate',
|
|---|
| 3101 | open: 'open',
|
|---|
| 3102 | optimum: 'optimum',
|
|---|
| 3103 | pattern: 'pattern',
|
|---|
| 3104 | placeholder: 'placeholder',
|
|---|
| 3105 | playsinline: 'playsInline',
|
|---|
| 3106 | poster: 'poster',
|
|---|
| 3107 | preload: 'preload',
|
|---|
| 3108 | profile: 'profile',
|
|---|
| 3109 | radiogroup: 'radioGroup',
|
|---|
| 3110 | readonly: 'readOnly',
|
|---|
| 3111 | referrerpolicy: 'referrerPolicy',
|
|---|
| 3112 | rel: 'rel',
|
|---|
| 3113 | required: 'required',
|
|---|
| 3114 | reversed: 'reversed',
|
|---|
| 3115 | role: 'role',
|
|---|
| 3116 | rows: 'rows',
|
|---|
| 3117 | rowspan: 'rowSpan',
|
|---|
| 3118 | sandbox: 'sandbox',
|
|---|
| 3119 | scope: 'scope',
|
|---|
| 3120 | scoped: 'scoped',
|
|---|
| 3121 | scrolling: 'scrolling',
|
|---|
| 3122 | seamless: 'seamless',
|
|---|
| 3123 | selected: 'selected',
|
|---|
| 3124 | shape: 'shape',
|
|---|
| 3125 | size: 'size',
|
|---|
| 3126 | sizes: 'sizes',
|
|---|
| 3127 | span: 'span',
|
|---|
| 3128 | spellcheck: 'spellCheck',
|
|---|
| 3129 | src: 'src',
|
|---|
| 3130 | srcdoc: 'srcDoc',
|
|---|
| 3131 | srclang: 'srcLang',
|
|---|
| 3132 | srcset: 'srcSet',
|
|---|
| 3133 | start: 'start',
|
|---|
| 3134 | step: 'step',
|
|---|
| 3135 | style: 'style',
|
|---|
| 3136 | summary: 'summary',
|
|---|
| 3137 | tabindex: 'tabIndex',
|
|---|
| 3138 | target: 'target',
|
|---|
| 3139 | title: 'title',
|
|---|
| 3140 | type: 'type',
|
|---|
| 3141 | usemap: 'useMap',
|
|---|
| 3142 | value: 'value',
|
|---|
| 3143 | width: 'width',
|
|---|
| 3144 | wmode: 'wmode',
|
|---|
| 3145 | wrap: 'wrap',
|
|---|
| 3146 | // SVG
|
|---|
| 3147 | about: 'about',
|
|---|
| 3148 | accentheight: 'accentHeight',
|
|---|
| 3149 | 'accent-height': 'accentHeight',
|
|---|
| 3150 | accumulate: 'accumulate',
|
|---|
| 3151 | additive: 'additive',
|
|---|
| 3152 | alignmentbaseline: 'alignmentBaseline',
|
|---|
| 3153 | 'alignment-baseline': 'alignmentBaseline',
|
|---|
| 3154 | allowreorder: 'allowReorder',
|
|---|
| 3155 | alphabetic: 'alphabetic',
|
|---|
| 3156 | amplitude: 'amplitude',
|
|---|
| 3157 | arabicform: 'arabicForm',
|
|---|
| 3158 | 'arabic-form': 'arabicForm',
|
|---|
| 3159 | ascent: 'ascent',
|
|---|
| 3160 | attributename: 'attributeName',
|
|---|
| 3161 | attributetype: 'attributeType',
|
|---|
| 3162 | autoreverse: 'autoReverse',
|
|---|
| 3163 | azimuth: 'azimuth',
|
|---|
| 3164 | basefrequency: 'baseFrequency',
|
|---|
| 3165 | baselineshift: 'baselineShift',
|
|---|
| 3166 | 'baseline-shift': 'baselineShift',
|
|---|
| 3167 | baseprofile: 'baseProfile',
|
|---|
| 3168 | bbox: 'bbox',
|
|---|
| 3169 | begin: 'begin',
|
|---|
| 3170 | bias: 'bias',
|
|---|
| 3171 | by: 'by',
|
|---|
| 3172 | calcmode: 'calcMode',
|
|---|
| 3173 | capheight: 'capHeight',
|
|---|
| 3174 | 'cap-height': 'capHeight',
|
|---|
| 3175 | clip: 'clip',
|
|---|
| 3176 | clippath: 'clipPath',
|
|---|
| 3177 | 'clip-path': 'clipPath',
|
|---|
| 3178 | clippathunits: 'clipPathUnits',
|
|---|
| 3179 | cliprule: 'clipRule',
|
|---|
| 3180 | 'clip-rule': 'clipRule',
|
|---|
| 3181 | color: 'color',
|
|---|
| 3182 | colorinterpolation: 'colorInterpolation',
|
|---|
| 3183 | 'color-interpolation': 'colorInterpolation',
|
|---|
| 3184 | colorinterpolationfilters: 'colorInterpolationFilters',
|
|---|
| 3185 | 'color-interpolation-filters': 'colorInterpolationFilters',
|
|---|
| 3186 | colorprofile: 'colorProfile',
|
|---|
| 3187 | 'color-profile': 'colorProfile',
|
|---|
| 3188 | colorrendering: 'colorRendering',
|
|---|
| 3189 | 'color-rendering': 'colorRendering',
|
|---|
| 3190 | contentscripttype: 'contentScriptType',
|
|---|
| 3191 | contentstyletype: 'contentStyleType',
|
|---|
| 3192 | cursor: 'cursor',
|
|---|
| 3193 | cx: 'cx',
|
|---|
| 3194 | cy: 'cy',
|
|---|
| 3195 | d: 'd',
|
|---|
| 3196 | datatype: 'datatype',
|
|---|
| 3197 | decelerate: 'decelerate',
|
|---|
| 3198 | descent: 'descent',
|
|---|
| 3199 | diffuseconstant: 'diffuseConstant',
|
|---|
| 3200 | direction: 'direction',
|
|---|
| 3201 | display: 'display',
|
|---|
| 3202 | divisor: 'divisor',
|
|---|
| 3203 | dominantbaseline: 'dominantBaseline',
|
|---|
| 3204 | 'dominant-baseline': 'dominantBaseline',
|
|---|
| 3205 | dur: 'dur',
|
|---|
| 3206 | dx: 'dx',
|
|---|
| 3207 | dy: 'dy',
|
|---|
| 3208 | edgemode: 'edgeMode',
|
|---|
| 3209 | elevation: 'elevation',
|
|---|
| 3210 | enablebackground: 'enableBackground',
|
|---|
| 3211 | 'enable-background': 'enableBackground',
|
|---|
| 3212 | end: 'end',
|
|---|
| 3213 | exponent: 'exponent',
|
|---|
| 3214 | externalresourcesrequired: 'externalResourcesRequired',
|
|---|
| 3215 | fill: 'fill',
|
|---|
| 3216 | fillopacity: 'fillOpacity',
|
|---|
| 3217 | 'fill-opacity': 'fillOpacity',
|
|---|
| 3218 | fillrule: 'fillRule',
|
|---|
| 3219 | 'fill-rule': 'fillRule',
|
|---|
| 3220 | filter: 'filter',
|
|---|
| 3221 | filterres: 'filterRes',
|
|---|
| 3222 | filterunits: 'filterUnits',
|
|---|
| 3223 | floodopacity: 'floodOpacity',
|
|---|
| 3224 | 'flood-opacity': 'floodOpacity',
|
|---|
| 3225 | floodcolor: 'floodColor',
|
|---|
| 3226 | 'flood-color': 'floodColor',
|
|---|
| 3227 | focusable: 'focusable',
|
|---|
| 3228 | fontfamily: 'fontFamily',
|
|---|
| 3229 | 'font-family': 'fontFamily',
|
|---|
| 3230 | fontsize: 'fontSize',
|
|---|
| 3231 | 'font-size': 'fontSize',
|
|---|
| 3232 | fontsizeadjust: 'fontSizeAdjust',
|
|---|
| 3233 | 'font-size-adjust': 'fontSizeAdjust',
|
|---|
| 3234 | fontstretch: 'fontStretch',
|
|---|
| 3235 | 'font-stretch': 'fontStretch',
|
|---|
| 3236 | fontstyle: 'fontStyle',
|
|---|
| 3237 | 'font-style': 'fontStyle',
|
|---|
| 3238 | fontvariant: 'fontVariant',
|
|---|
| 3239 | 'font-variant': 'fontVariant',
|
|---|
| 3240 | fontweight: 'fontWeight',
|
|---|
| 3241 | 'font-weight': 'fontWeight',
|
|---|
| 3242 | format: 'format',
|
|---|
| 3243 | from: 'from',
|
|---|
| 3244 | fx: 'fx',
|
|---|
| 3245 | fy: 'fy',
|
|---|
| 3246 | g1: 'g1',
|
|---|
| 3247 | g2: 'g2',
|
|---|
| 3248 | glyphname: 'glyphName',
|
|---|
| 3249 | 'glyph-name': 'glyphName',
|
|---|
| 3250 | glyphorientationhorizontal: 'glyphOrientationHorizontal',
|
|---|
| 3251 | 'glyph-orientation-horizontal': 'glyphOrientationHorizontal',
|
|---|
| 3252 | glyphorientationvertical: 'glyphOrientationVertical',
|
|---|
| 3253 | 'glyph-orientation-vertical': 'glyphOrientationVertical',
|
|---|
| 3254 | glyphref: 'glyphRef',
|
|---|
| 3255 | gradienttransform: 'gradientTransform',
|
|---|
| 3256 | gradientunits: 'gradientUnits',
|
|---|
| 3257 | hanging: 'hanging',
|
|---|
| 3258 | horizadvx: 'horizAdvX',
|
|---|
| 3259 | 'horiz-adv-x': 'horizAdvX',
|
|---|
| 3260 | horizoriginx: 'horizOriginX',
|
|---|
| 3261 | 'horiz-origin-x': 'horizOriginX',
|
|---|
| 3262 | ideographic: 'ideographic',
|
|---|
| 3263 | imagerendering: 'imageRendering',
|
|---|
| 3264 | 'image-rendering': 'imageRendering',
|
|---|
| 3265 | in2: 'in2',
|
|---|
| 3266 | in: 'in',
|
|---|
| 3267 | inlist: 'inlist',
|
|---|
| 3268 | intercept: 'intercept',
|
|---|
| 3269 | k1: 'k1',
|
|---|
| 3270 | k2: 'k2',
|
|---|
| 3271 | k3: 'k3',
|
|---|
| 3272 | k4: 'k4',
|
|---|
| 3273 | k: 'k',
|
|---|
| 3274 | kernelmatrix: 'kernelMatrix',
|
|---|
| 3275 | kernelunitlength: 'kernelUnitLength',
|
|---|
| 3276 | kerning: 'kerning',
|
|---|
| 3277 | keypoints: 'keyPoints',
|
|---|
| 3278 | keysplines: 'keySplines',
|
|---|
| 3279 | keytimes: 'keyTimes',
|
|---|
| 3280 | lengthadjust: 'lengthAdjust',
|
|---|
| 3281 | letterspacing: 'letterSpacing',
|
|---|
| 3282 | 'letter-spacing': 'letterSpacing',
|
|---|
| 3283 | lightingcolor: 'lightingColor',
|
|---|
| 3284 | 'lighting-color': 'lightingColor',
|
|---|
| 3285 | limitingconeangle: 'limitingConeAngle',
|
|---|
| 3286 | local: 'local',
|
|---|
| 3287 | markerend: 'markerEnd',
|
|---|
| 3288 | 'marker-end': 'markerEnd',
|
|---|
| 3289 | markerheight: 'markerHeight',
|
|---|
| 3290 | markermid: 'markerMid',
|
|---|
| 3291 | 'marker-mid': 'markerMid',
|
|---|
| 3292 | markerstart: 'markerStart',
|
|---|
| 3293 | 'marker-start': 'markerStart',
|
|---|
| 3294 | markerunits: 'markerUnits',
|
|---|
| 3295 | markerwidth: 'markerWidth',
|
|---|
| 3296 | mask: 'mask',
|
|---|
| 3297 | maskcontentunits: 'maskContentUnits',
|
|---|
| 3298 | maskunits: 'maskUnits',
|
|---|
| 3299 | mathematical: 'mathematical',
|
|---|
| 3300 | mode: 'mode',
|
|---|
| 3301 | numoctaves: 'numOctaves',
|
|---|
| 3302 | offset: 'offset',
|
|---|
| 3303 | opacity: 'opacity',
|
|---|
| 3304 | operator: 'operator',
|
|---|
| 3305 | order: 'order',
|
|---|
| 3306 | orient: 'orient',
|
|---|
| 3307 | orientation: 'orientation',
|
|---|
| 3308 | origin: 'origin',
|
|---|
| 3309 | overflow: 'overflow',
|
|---|
| 3310 | overlineposition: 'overlinePosition',
|
|---|
| 3311 | 'overline-position': 'overlinePosition',
|
|---|
| 3312 | overlinethickness: 'overlineThickness',
|
|---|
| 3313 | 'overline-thickness': 'overlineThickness',
|
|---|
| 3314 | paintorder: 'paintOrder',
|
|---|
| 3315 | 'paint-order': 'paintOrder',
|
|---|
| 3316 | panose1: 'panose1',
|
|---|
| 3317 | 'panose-1': 'panose1',
|
|---|
| 3318 | pathlength: 'pathLength',
|
|---|
| 3319 | patterncontentunits: 'patternContentUnits',
|
|---|
| 3320 | patterntransform: 'patternTransform',
|
|---|
| 3321 | patternunits: 'patternUnits',
|
|---|
| 3322 | pointerevents: 'pointerEvents',
|
|---|
| 3323 | 'pointer-events': 'pointerEvents',
|
|---|
| 3324 | points: 'points',
|
|---|
| 3325 | pointsatx: 'pointsAtX',
|
|---|
| 3326 | pointsaty: 'pointsAtY',
|
|---|
| 3327 | pointsatz: 'pointsAtZ',
|
|---|
| 3328 | prefix: 'prefix',
|
|---|
| 3329 | preservealpha: 'preserveAlpha',
|
|---|
| 3330 | preserveaspectratio: 'preserveAspectRatio',
|
|---|
| 3331 | primitiveunits: 'primitiveUnits',
|
|---|
| 3332 | property: 'property',
|
|---|
| 3333 | r: 'r',
|
|---|
| 3334 | radius: 'radius',
|
|---|
| 3335 | refx: 'refX',
|
|---|
| 3336 | refy: 'refY',
|
|---|
| 3337 | renderingintent: 'renderingIntent',
|
|---|
| 3338 | 'rendering-intent': 'renderingIntent',
|
|---|
| 3339 | repeatcount: 'repeatCount',
|
|---|
| 3340 | repeatdur: 'repeatDur',
|
|---|
| 3341 | requiredextensions: 'requiredExtensions',
|
|---|
| 3342 | requiredfeatures: 'requiredFeatures',
|
|---|
| 3343 | resource: 'resource',
|
|---|
| 3344 | restart: 'restart',
|
|---|
| 3345 | result: 'result',
|
|---|
| 3346 | results: 'results',
|
|---|
| 3347 | rotate: 'rotate',
|
|---|
| 3348 | rx: 'rx',
|
|---|
| 3349 | ry: 'ry',
|
|---|
| 3350 | scale: 'scale',
|
|---|
| 3351 | security: 'security',
|
|---|
| 3352 | seed: 'seed',
|
|---|
| 3353 | shaperendering: 'shapeRendering',
|
|---|
| 3354 | 'shape-rendering': 'shapeRendering',
|
|---|
| 3355 | slope: 'slope',
|
|---|
| 3356 | spacing: 'spacing',
|
|---|
| 3357 | specularconstant: 'specularConstant',
|
|---|
| 3358 | specularexponent: 'specularExponent',
|
|---|
| 3359 | speed: 'speed',
|
|---|
| 3360 | spreadmethod: 'spreadMethod',
|
|---|
| 3361 | startoffset: 'startOffset',
|
|---|
| 3362 | stddeviation: 'stdDeviation',
|
|---|
| 3363 | stemh: 'stemh',
|
|---|
| 3364 | stemv: 'stemv',
|
|---|
| 3365 | stitchtiles: 'stitchTiles',
|
|---|
| 3366 | stopcolor: 'stopColor',
|
|---|
| 3367 | 'stop-color': 'stopColor',
|
|---|
| 3368 | stopopacity: 'stopOpacity',
|
|---|
| 3369 | 'stop-opacity': 'stopOpacity',
|
|---|
| 3370 | strikethroughposition: 'strikethroughPosition',
|
|---|
| 3371 | 'strikethrough-position': 'strikethroughPosition',
|
|---|
| 3372 | strikethroughthickness: 'strikethroughThickness',
|
|---|
| 3373 | 'strikethrough-thickness': 'strikethroughThickness',
|
|---|
| 3374 | string: 'string',
|
|---|
| 3375 | stroke: 'stroke',
|
|---|
| 3376 | strokedasharray: 'strokeDasharray',
|
|---|
| 3377 | 'stroke-dasharray': 'strokeDasharray',
|
|---|
| 3378 | strokedashoffset: 'strokeDashoffset',
|
|---|
| 3379 | 'stroke-dashoffset': 'strokeDashoffset',
|
|---|
| 3380 | strokelinecap: 'strokeLinecap',
|
|---|
| 3381 | 'stroke-linecap': 'strokeLinecap',
|
|---|
| 3382 | strokelinejoin: 'strokeLinejoin',
|
|---|
| 3383 | 'stroke-linejoin': 'strokeLinejoin',
|
|---|
| 3384 | strokemiterlimit: 'strokeMiterlimit',
|
|---|
| 3385 | 'stroke-miterlimit': 'strokeMiterlimit',
|
|---|
| 3386 | strokewidth: 'strokeWidth',
|
|---|
| 3387 | 'stroke-width': 'strokeWidth',
|
|---|
| 3388 | strokeopacity: 'strokeOpacity',
|
|---|
| 3389 | 'stroke-opacity': 'strokeOpacity',
|
|---|
| 3390 | suppresscontenteditablewarning: 'suppressContentEditableWarning',
|
|---|
| 3391 | suppresshydrationwarning: 'suppressHydrationWarning',
|
|---|
| 3392 | surfacescale: 'surfaceScale',
|
|---|
| 3393 | systemlanguage: 'systemLanguage',
|
|---|
| 3394 | tablevalues: 'tableValues',
|
|---|
| 3395 | targetx: 'targetX',
|
|---|
| 3396 | targety: 'targetY',
|
|---|
| 3397 | textanchor: 'textAnchor',
|
|---|
| 3398 | 'text-anchor': 'textAnchor',
|
|---|
| 3399 | textdecoration: 'textDecoration',
|
|---|
| 3400 | 'text-decoration': 'textDecoration',
|
|---|
| 3401 | textlength: 'textLength',
|
|---|
| 3402 | textrendering: 'textRendering',
|
|---|
| 3403 | 'text-rendering': 'textRendering',
|
|---|
| 3404 | to: 'to',
|
|---|
| 3405 | transform: 'transform',
|
|---|
| 3406 | typeof: 'typeof',
|
|---|
| 3407 | u1: 'u1',
|
|---|
| 3408 | u2: 'u2',
|
|---|
| 3409 | underlineposition: 'underlinePosition',
|
|---|
| 3410 | 'underline-position': 'underlinePosition',
|
|---|
| 3411 | underlinethickness: 'underlineThickness',
|
|---|
| 3412 | 'underline-thickness': 'underlineThickness',
|
|---|
| 3413 | unicode: 'unicode',
|
|---|
| 3414 | unicodebidi: 'unicodeBidi',
|
|---|
| 3415 | 'unicode-bidi': 'unicodeBidi',
|
|---|
| 3416 | unicoderange: 'unicodeRange',
|
|---|
| 3417 | 'unicode-range': 'unicodeRange',
|
|---|
| 3418 | unitsperem: 'unitsPerEm',
|
|---|
| 3419 | 'units-per-em': 'unitsPerEm',
|
|---|
| 3420 | unselectable: 'unselectable',
|
|---|
| 3421 | valphabetic: 'vAlphabetic',
|
|---|
| 3422 | 'v-alphabetic': 'vAlphabetic',
|
|---|
| 3423 | values: 'values',
|
|---|
| 3424 | vectoreffect: 'vectorEffect',
|
|---|
| 3425 | 'vector-effect': 'vectorEffect',
|
|---|
| 3426 | version: 'version',
|
|---|
| 3427 | vertadvy: 'vertAdvY',
|
|---|
| 3428 | 'vert-adv-y': 'vertAdvY',
|
|---|
| 3429 | vertoriginx: 'vertOriginX',
|
|---|
| 3430 | 'vert-origin-x': 'vertOriginX',
|
|---|
| 3431 | vertoriginy: 'vertOriginY',
|
|---|
| 3432 | 'vert-origin-y': 'vertOriginY',
|
|---|
| 3433 | vhanging: 'vHanging',
|
|---|
| 3434 | 'v-hanging': 'vHanging',
|
|---|
| 3435 | videographic: 'vIdeographic',
|
|---|
| 3436 | 'v-ideographic': 'vIdeographic',
|
|---|
| 3437 | viewbox: 'viewBox',
|
|---|
| 3438 | viewtarget: 'viewTarget',
|
|---|
| 3439 | visibility: 'visibility',
|
|---|
| 3440 | vmathematical: 'vMathematical',
|
|---|
| 3441 | 'v-mathematical': 'vMathematical',
|
|---|
| 3442 | vocab: 'vocab',
|
|---|
| 3443 | widths: 'widths',
|
|---|
| 3444 | wordspacing: 'wordSpacing',
|
|---|
| 3445 | 'word-spacing': 'wordSpacing',
|
|---|
| 3446 | writingmode: 'writingMode',
|
|---|
| 3447 | 'writing-mode': 'writingMode',
|
|---|
| 3448 | x1: 'x1',
|
|---|
| 3449 | x2: 'x2',
|
|---|
| 3450 | x: 'x',
|
|---|
| 3451 | xchannelselector: 'xChannelSelector',
|
|---|
| 3452 | xheight: 'xHeight',
|
|---|
| 3453 | 'x-height': 'xHeight',
|
|---|
| 3454 | xlinkactuate: 'xlinkActuate',
|
|---|
| 3455 | 'xlink:actuate': 'xlinkActuate',
|
|---|
| 3456 | xlinkarcrole: 'xlinkArcrole',
|
|---|
| 3457 | 'xlink:arcrole': 'xlinkArcrole',
|
|---|
| 3458 | xlinkhref: 'xlinkHref',
|
|---|
| 3459 | 'xlink:href': 'xlinkHref',
|
|---|
| 3460 | xlinkrole: 'xlinkRole',
|
|---|
| 3461 | 'xlink:role': 'xlinkRole',
|
|---|
| 3462 | xlinkshow: 'xlinkShow',
|
|---|
| 3463 | 'xlink:show': 'xlinkShow',
|
|---|
| 3464 | xlinktitle: 'xlinkTitle',
|
|---|
| 3465 | 'xlink:title': 'xlinkTitle',
|
|---|
| 3466 | xlinktype: 'xlinkType',
|
|---|
| 3467 | 'xlink:type': 'xlinkType',
|
|---|
| 3468 | xmlbase: 'xmlBase',
|
|---|
| 3469 | 'xml:base': 'xmlBase',
|
|---|
| 3470 | xmllang: 'xmlLang',
|
|---|
| 3471 | 'xml:lang': 'xmlLang',
|
|---|
| 3472 | xmlns: 'xmlns',
|
|---|
| 3473 | 'xml:space': 'xmlSpace',
|
|---|
| 3474 | xmlnsxlink: 'xmlnsXlink',
|
|---|
| 3475 | 'xmlns:xlink': 'xmlnsXlink',
|
|---|
| 3476 | xmlspace: 'xmlSpace',
|
|---|
| 3477 | y1: 'y1',
|
|---|
| 3478 | y2: 'y2',
|
|---|
| 3479 | y: 'y',
|
|---|
| 3480 | ychannelselector: 'yChannelSelector',
|
|---|
| 3481 | z: 'z',
|
|---|
| 3482 | zoomandpan: 'zoomAndPan'
|
|---|
| 3483 | };
|
|---|
| 3484 |
|
|---|
| 3485 | var ariaProperties = {
|
|---|
| 3486 | 'aria-current': 0,
|
|---|
| 3487 | // state
|
|---|
| 3488 | 'aria-description': 0,
|
|---|
| 3489 | 'aria-details': 0,
|
|---|
| 3490 | 'aria-disabled': 0,
|
|---|
| 3491 | // state
|
|---|
| 3492 | 'aria-hidden': 0,
|
|---|
| 3493 | // state
|
|---|
| 3494 | 'aria-invalid': 0,
|
|---|
| 3495 | // state
|
|---|
| 3496 | 'aria-keyshortcuts': 0,
|
|---|
| 3497 | 'aria-label': 0,
|
|---|
| 3498 | 'aria-roledescription': 0,
|
|---|
| 3499 | // Widget Attributes
|
|---|
| 3500 | 'aria-autocomplete': 0,
|
|---|
| 3501 | 'aria-checked': 0,
|
|---|
| 3502 | 'aria-expanded': 0,
|
|---|
| 3503 | 'aria-haspopup': 0,
|
|---|
| 3504 | 'aria-level': 0,
|
|---|
| 3505 | 'aria-modal': 0,
|
|---|
| 3506 | 'aria-multiline': 0,
|
|---|
| 3507 | 'aria-multiselectable': 0,
|
|---|
| 3508 | 'aria-orientation': 0,
|
|---|
| 3509 | 'aria-placeholder': 0,
|
|---|
| 3510 | 'aria-pressed': 0,
|
|---|
| 3511 | 'aria-readonly': 0,
|
|---|
| 3512 | 'aria-required': 0,
|
|---|
| 3513 | 'aria-selected': 0,
|
|---|
| 3514 | 'aria-sort': 0,
|
|---|
| 3515 | 'aria-valuemax': 0,
|
|---|
| 3516 | 'aria-valuemin': 0,
|
|---|
| 3517 | 'aria-valuenow': 0,
|
|---|
| 3518 | 'aria-valuetext': 0,
|
|---|
| 3519 | // Live Region Attributes
|
|---|
| 3520 | 'aria-atomic': 0,
|
|---|
| 3521 | 'aria-busy': 0,
|
|---|
| 3522 | 'aria-live': 0,
|
|---|
| 3523 | 'aria-relevant': 0,
|
|---|
| 3524 | // Drag-and-Drop Attributes
|
|---|
| 3525 | 'aria-dropeffect': 0,
|
|---|
| 3526 | 'aria-grabbed': 0,
|
|---|
| 3527 | // Relationship Attributes
|
|---|
| 3528 | 'aria-activedescendant': 0,
|
|---|
| 3529 | 'aria-colcount': 0,
|
|---|
| 3530 | 'aria-colindex': 0,
|
|---|
| 3531 | 'aria-colspan': 0,
|
|---|
| 3532 | 'aria-controls': 0,
|
|---|
| 3533 | 'aria-describedby': 0,
|
|---|
| 3534 | 'aria-errormessage': 0,
|
|---|
| 3535 | 'aria-flowto': 0,
|
|---|
| 3536 | 'aria-labelledby': 0,
|
|---|
| 3537 | 'aria-owns': 0,
|
|---|
| 3538 | 'aria-posinset': 0,
|
|---|
| 3539 | 'aria-rowcount': 0,
|
|---|
| 3540 | 'aria-rowindex': 0,
|
|---|
| 3541 | 'aria-rowspan': 0,
|
|---|
| 3542 | 'aria-setsize': 0
|
|---|
| 3543 | };
|
|---|
| 3544 |
|
|---|
| 3545 | var warnedProperties = {};
|
|---|
| 3546 | var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|---|
| 3547 | var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|---|
| 3548 |
|
|---|
| 3549 | function validateProperty(tagName, name) {
|
|---|
| 3550 | {
|
|---|
| 3551 | if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
|
|---|
| 3552 | return true;
|
|---|
| 3553 | }
|
|---|
| 3554 |
|
|---|
| 3555 | if (rARIACamel.test(name)) {
|
|---|
| 3556 | var ariaName = 'aria-' + name.slice(4).toLowerCase();
|
|---|
| 3557 | var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null; // If this is an aria-* attribute, but is not listed in the known DOM
|
|---|
| 3558 | // DOM properties, then it is an invalid aria-* attribute.
|
|---|
| 3559 |
|
|---|
| 3560 | if (correctName == null) {
|
|---|
| 3561 | error('Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name);
|
|---|
| 3562 |
|
|---|
| 3563 | warnedProperties[name] = true;
|
|---|
| 3564 | return true;
|
|---|
| 3565 | } // aria-* attributes should be lowercase; suggest the lowercase version.
|
|---|
| 3566 |
|
|---|
| 3567 |
|
|---|
| 3568 | if (name !== correctName) {
|
|---|
| 3569 | error('Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName);
|
|---|
| 3570 |
|
|---|
| 3571 | warnedProperties[name] = true;
|
|---|
| 3572 | return true;
|
|---|
| 3573 | }
|
|---|
| 3574 | }
|
|---|
| 3575 |
|
|---|
| 3576 | if (rARIA.test(name)) {
|
|---|
| 3577 | var lowerCasedName = name.toLowerCase();
|
|---|
| 3578 | var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null; // If this is an aria-* attribute, but is not listed in the known DOM
|
|---|
| 3579 | // DOM properties, then it is an invalid aria-* attribute.
|
|---|
| 3580 |
|
|---|
| 3581 | if (standardName == null) {
|
|---|
| 3582 | warnedProperties[name] = true;
|
|---|
| 3583 | return false;
|
|---|
| 3584 | } // aria-* attributes should be lowercase; suggest the lowercase version.
|
|---|
| 3585 |
|
|---|
| 3586 |
|
|---|
| 3587 | if (name !== standardName) {
|
|---|
| 3588 | error('Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName);
|
|---|
| 3589 |
|
|---|
| 3590 | warnedProperties[name] = true;
|
|---|
| 3591 | return true;
|
|---|
| 3592 | }
|
|---|
| 3593 | }
|
|---|
| 3594 | }
|
|---|
| 3595 |
|
|---|
| 3596 | return true;
|
|---|
| 3597 | }
|
|---|
| 3598 |
|
|---|
| 3599 | function warnInvalidARIAProps(type, props) {
|
|---|
| 3600 | {
|
|---|
| 3601 | var invalidProps = [];
|
|---|
| 3602 |
|
|---|
| 3603 | for (var key in props) {
|
|---|
| 3604 | var isValid = validateProperty(type, key);
|
|---|
| 3605 |
|
|---|
| 3606 | if (!isValid) {
|
|---|
| 3607 | invalidProps.push(key);
|
|---|
| 3608 | }
|
|---|
| 3609 | }
|
|---|
| 3610 |
|
|---|
| 3611 | var unknownPropString = invalidProps.map(function (prop) {
|
|---|
| 3612 | return '`' + prop + '`';
|
|---|
| 3613 | }).join(', ');
|
|---|
| 3614 |
|
|---|
| 3615 | if (invalidProps.length === 1) {
|
|---|
| 3616 | error('Invalid aria prop %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type);
|
|---|
| 3617 | } else if (invalidProps.length > 1) {
|
|---|
| 3618 | error('Invalid aria props %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type);
|
|---|
| 3619 | }
|
|---|
| 3620 | }
|
|---|
| 3621 | }
|
|---|
| 3622 |
|
|---|
| 3623 | function validateProperties(type, props) {
|
|---|
| 3624 | if (isCustomComponent(type, props)) {
|
|---|
| 3625 | return;
|
|---|
| 3626 | }
|
|---|
| 3627 |
|
|---|
| 3628 | warnInvalidARIAProps(type, props);
|
|---|
| 3629 | }
|
|---|
| 3630 |
|
|---|
| 3631 | var didWarnValueNull = false;
|
|---|
| 3632 | function validateProperties$1(type, props) {
|
|---|
| 3633 | {
|
|---|
| 3634 | if (type !== 'input' && type !== 'textarea' && type !== 'select') {
|
|---|
| 3635 | return;
|
|---|
| 3636 | }
|
|---|
| 3637 |
|
|---|
| 3638 | if (props != null && props.value === null && !didWarnValueNull) {
|
|---|
| 3639 | didWarnValueNull = true;
|
|---|
| 3640 |
|
|---|
| 3641 | if (type === 'select' && props.multiple) {
|
|---|
| 3642 | error('`value` prop on `%s` should not be null. ' + 'Consider using an empty array when `multiple` is set to `true` ' + 'to clear the component or `undefined` for uncontrolled components.', type);
|
|---|
| 3643 | } else {
|
|---|
| 3644 | error('`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type);
|
|---|
| 3645 | }
|
|---|
| 3646 | }
|
|---|
| 3647 | }
|
|---|
| 3648 | }
|
|---|
| 3649 |
|
|---|
| 3650 | var validateProperty$1 = function () {};
|
|---|
| 3651 |
|
|---|
| 3652 | {
|
|---|
| 3653 | var warnedProperties$1 = {};
|
|---|
| 3654 | var EVENT_NAME_REGEX = /^on./;
|
|---|
| 3655 | var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
|
|---|
| 3656 | var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|---|
| 3657 | var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
|
|---|
| 3658 |
|
|---|
| 3659 | validateProperty$1 = function (tagName, name, value, eventRegistry) {
|
|---|
| 3660 | if (hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) {
|
|---|
| 3661 | return true;
|
|---|
| 3662 | }
|
|---|
| 3663 |
|
|---|
| 3664 | var lowerCasedName = name.toLowerCase();
|
|---|
| 3665 |
|
|---|
| 3666 | if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') {
|
|---|
| 3667 | error('React uses onFocus and onBlur instead of onFocusIn and onFocusOut. ' + 'All React events are normalized to bubble, so onFocusIn and onFocusOut ' + 'are not needed/supported by React.');
|
|---|
| 3668 |
|
|---|
| 3669 | warnedProperties$1[name] = true;
|
|---|
| 3670 | return true;
|
|---|
| 3671 | } // We can't rely on the event system being injected on the server.
|
|---|
| 3672 |
|
|---|
| 3673 |
|
|---|
| 3674 | if (eventRegistry != null) {
|
|---|
| 3675 | var registrationNameDependencies = eventRegistry.registrationNameDependencies,
|
|---|
| 3676 | possibleRegistrationNames = eventRegistry.possibleRegistrationNames;
|
|---|
| 3677 |
|
|---|
| 3678 | if (registrationNameDependencies.hasOwnProperty(name)) {
|
|---|
| 3679 | return true;
|
|---|
| 3680 | }
|
|---|
| 3681 |
|
|---|
| 3682 | var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null;
|
|---|
| 3683 |
|
|---|
| 3684 | if (registrationName != null) {
|
|---|
| 3685 | error('Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName);
|
|---|
| 3686 |
|
|---|
| 3687 | warnedProperties$1[name] = true;
|
|---|
| 3688 | return true;
|
|---|
| 3689 | }
|
|---|
| 3690 |
|
|---|
| 3691 | if (EVENT_NAME_REGEX.test(name)) {
|
|---|
| 3692 | error('Unknown event handler property `%s`. It will be ignored.', name);
|
|---|
| 3693 |
|
|---|
| 3694 | warnedProperties$1[name] = true;
|
|---|
| 3695 | return true;
|
|---|
| 3696 | }
|
|---|
| 3697 | } else if (EVENT_NAME_REGEX.test(name)) {
|
|---|
| 3698 | // If no event plugins have been injected, we are in a server environment.
|
|---|
| 3699 | // So we can't tell if the event name is correct for sure, but we can filter
|
|---|
| 3700 | // out known bad ones like `onclick`. We can't suggest a specific replacement though.
|
|---|
| 3701 | if (INVALID_EVENT_NAME_REGEX.test(name)) {
|
|---|
| 3702 | error('Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name);
|
|---|
| 3703 | }
|
|---|
| 3704 |
|
|---|
| 3705 | warnedProperties$1[name] = true;
|
|---|
| 3706 | return true;
|
|---|
| 3707 | } // Let the ARIA attribute hook validate ARIA attributes
|
|---|
| 3708 |
|
|---|
| 3709 |
|
|---|
| 3710 | if (rARIA$1.test(name) || rARIACamel$1.test(name)) {
|
|---|
| 3711 | return true;
|
|---|
| 3712 | }
|
|---|
| 3713 |
|
|---|
| 3714 | if (lowerCasedName === 'innerhtml') {
|
|---|
| 3715 | error('Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.');
|
|---|
| 3716 |
|
|---|
| 3717 | warnedProperties$1[name] = true;
|
|---|
| 3718 | return true;
|
|---|
| 3719 | }
|
|---|
| 3720 |
|
|---|
| 3721 | if (lowerCasedName === 'aria') {
|
|---|
| 3722 | error('The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.');
|
|---|
| 3723 |
|
|---|
| 3724 | warnedProperties$1[name] = true;
|
|---|
| 3725 | return true;
|
|---|
| 3726 | }
|
|---|
| 3727 |
|
|---|
| 3728 | if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') {
|
|---|
| 3729 | error('Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value);
|
|---|
| 3730 |
|
|---|
| 3731 | warnedProperties$1[name] = true;
|
|---|
| 3732 | return true;
|
|---|
| 3733 | }
|
|---|
| 3734 |
|
|---|
| 3735 | if (typeof value === 'number' && isNaN(value)) {
|
|---|
| 3736 | error('Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name);
|
|---|
| 3737 |
|
|---|
| 3738 | warnedProperties$1[name] = true;
|
|---|
| 3739 | return true;
|
|---|
| 3740 | }
|
|---|
| 3741 |
|
|---|
| 3742 | var propertyInfo = getPropertyInfo(name);
|
|---|
| 3743 | var isReserved = propertyInfo !== null && propertyInfo.type === RESERVED; // Known attributes should match the casing specified in the property config.
|
|---|
| 3744 |
|
|---|
| 3745 | if (possibleStandardNames.hasOwnProperty(lowerCasedName)) {
|
|---|
| 3746 | var standardName = possibleStandardNames[lowerCasedName];
|
|---|
| 3747 |
|
|---|
| 3748 | if (standardName !== name) {
|
|---|
| 3749 | error('Invalid DOM property `%s`. Did you mean `%s`?', name, standardName);
|
|---|
| 3750 |
|
|---|
| 3751 | warnedProperties$1[name] = true;
|
|---|
| 3752 | return true;
|
|---|
| 3753 | }
|
|---|
| 3754 | } else if (!isReserved && name !== lowerCasedName) {
|
|---|
| 3755 | // Unknown attributes should have lowercase casing since that's how they
|
|---|
| 3756 | // will be cased anyway with server rendering.
|
|---|
| 3757 | error('React does not recognize the `%s` prop on a DOM element. If you ' + 'intentionally want it to appear in the DOM as a custom ' + 'attribute, spell it as lowercase `%s` instead. ' + 'If you accidentally passed it from a parent component, remove ' + 'it from the DOM element.', name, lowerCasedName);
|
|---|
| 3758 |
|
|---|
| 3759 | warnedProperties$1[name] = true;
|
|---|
| 3760 | return true;
|
|---|
| 3761 | }
|
|---|
| 3762 |
|
|---|
| 3763 | if (typeof value === 'boolean' && shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
|
|---|
| 3764 | if (value) {
|
|---|
| 3765 | error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.', value, name, name, value, name);
|
|---|
| 3766 | } else {
|
|---|
| 3767 | error('Received `%s` for a non-boolean attribute `%s`.\n\n' + 'If you want to write it to the DOM, pass a string instead: ' + '%s="%s" or %s={value.toString()}.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', value, name, name, value, name, name, name);
|
|---|
| 3768 | }
|
|---|
| 3769 |
|
|---|
| 3770 | warnedProperties$1[name] = true;
|
|---|
| 3771 | return true;
|
|---|
| 3772 | } // Now that we've validated casing, do not validate
|
|---|
| 3773 | // data types for reserved props
|
|---|
| 3774 |
|
|---|
| 3775 |
|
|---|
| 3776 | if (isReserved) {
|
|---|
| 3777 | return true;
|
|---|
| 3778 | } // Warn when a known attribute is a bad type
|
|---|
| 3779 |
|
|---|
| 3780 |
|
|---|
| 3781 | if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) {
|
|---|
| 3782 | warnedProperties$1[name] = true;
|
|---|
| 3783 | return false;
|
|---|
| 3784 | } // Warn when passing the strings 'false' or 'true' into a boolean prop
|
|---|
| 3785 |
|
|---|
| 3786 |
|
|---|
| 3787 | if ((value === 'false' || value === 'true') && propertyInfo !== null && propertyInfo.type === BOOLEAN) {
|
|---|
| 3788 | error('Received the string `%s` for the boolean attribute `%s`. ' + '%s ' + 'Did you mean %s={%s}?', value, name, value === 'false' ? 'The browser will interpret it as a truthy value.' : 'Although this works, it will not work as expected if you pass the string "false".', name, value);
|
|---|
| 3789 |
|
|---|
| 3790 | warnedProperties$1[name] = true;
|
|---|
| 3791 | return true;
|
|---|
| 3792 | }
|
|---|
| 3793 |
|
|---|
| 3794 | return true;
|
|---|
| 3795 | };
|
|---|
| 3796 | }
|
|---|
| 3797 |
|
|---|
| 3798 | var warnUnknownProperties = function (type, props, eventRegistry) {
|
|---|
| 3799 | {
|
|---|
| 3800 | var unknownProps = [];
|
|---|
| 3801 |
|
|---|
| 3802 | for (var key in props) {
|
|---|
| 3803 | var isValid = validateProperty$1(type, key, props[key], eventRegistry);
|
|---|
| 3804 |
|
|---|
| 3805 | if (!isValid) {
|
|---|
| 3806 | unknownProps.push(key);
|
|---|
| 3807 | }
|
|---|
| 3808 | }
|
|---|
| 3809 |
|
|---|
| 3810 | var unknownPropString = unknownProps.map(function (prop) {
|
|---|
| 3811 | return '`' + prop + '`';
|
|---|
| 3812 | }).join(', ');
|
|---|
| 3813 |
|
|---|
| 3814 | if (unknownProps.length === 1) {
|
|---|
| 3815 | error('Invalid value for prop %s on <%s> tag. Either remove it from the element, ' + 'or pass a string or number value to keep it in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type);
|
|---|
| 3816 | } else if (unknownProps.length > 1) {
|
|---|
| 3817 | error('Invalid values for props %s on <%s> tag. Either remove them from the element, ' + 'or pass a string or number value to keep them in the DOM. ' + 'For details, see https://reactjs.org/link/attribute-behavior ', unknownPropString, type);
|
|---|
| 3818 | }
|
|---|
| 3819 | }
|
|---|
| 3820 | };
|
|---|
| 3821 |
|
|---|
| 3822 | function validateProperties$2(type, props, eventRegistry) {
|
|---|
| 3823 | if (isCustomComponent(type, props)) {
|
|---|
| 3824 | return;
|
|---|
| 3825 | }
|
|---|
| 3826 |
|
|---|
| 3827 | warnUnknownProperties(type, props, eventRegistry);
|
|---|
| 3828 | }
|
|---|
| 3829 |
|
|---|
| 3830 | var IS_EVENT_HANDLE_NON_MANAGED_NODE = 1;
|
|---|
| 3831 | var IS_NON_DELEGATED = 1 << 1;
|
|---|
| 3832 | var IS_CAPTURE_PHASE = 1 << 2;
|
|---|
| 3833 | // set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when
|
|---|
| 3834 | // we call willDeferLaterForLegacyFBSupport, thus not bailing out
|
|---|
| 3835 | // will result in endless cycles like an infinite loop.
|
|---|
| 3836 | // We also don't want to defer during event replaying.
|
|---|
| 3837 |
|
|---|
| 3838 | var SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS = IS_EVENT_HANDLE_NON_MANAGED_NODE | IS_NON_DELEGATED | IS_CAPTURE_PHASE;
|
|---|
| 3839 |
|
|---|
| 3840 | // This exists to avoid circular dependency between ReactDOMEventReplaying
|
|---|
| 3841 | // and DOMPluginEventSystem.
|
|---|
| 3842 | var currentReplayingEvent = null;
|
|---|
| 3843 | function setReplayingEvent(event) {
|
|---|
| 3844 | {
|
|---|
| 3845 | if (currentReplayingEvent !== null) {
|
|---|
| 3846 | error('Expected currently replaying event to be null. This error ' + 'is likely caused by a bug in React. Please file an issue.');
|
|---|
| 3847 | }
|
|---|
| 3848 | }
|
|---|
| 3849 |
|
|---|
| 3850 | currentReplayingEvent = event;
|
|---|
| 3851 | }
|
|---|
| 3852 | function resetReplayingEvent() {
|
|---|
| 3853 | {
|
|---|
| 3854 | if (currentReplayingEvent === null) {
|
|---|
| 3855 | error('Expected currently replaying event to not be null. This error ' + 'is likely caused by a bug in React. Please file an issue.');
|
|---|
| 3856 | }
|
|---|
| 3857 | }
|
|---|
| 3858 |
|
|---|
| 3859 | currentReplayingEvent = null;
|
|---|
| 3860 | }
|
|---|
| 3861 | function isReplayingEvent(event) {
|
|---|
| 3862 | return event === currentReplayingEvent;
|
|---|
| 3863 | }
|
|---|
| 3864 |
|
|---|
| 3865 | /**
|
|---|
| 3866 | * Gets the target node from a native browser event by accounting for
|
|---|
| 3867 | * inconsistencies in browser DOM APIs.
|
|---|
| 3868 | *
|
|---|
| 3869 | * @param {object} nativeEvent Native browser event.
|
|---|
| 3870 | * @return {DOMEventTarget} Target node.
|
|---|
| 3871 | */
|
|---|
| 3872 |
|
|---|
| 3873 | function getEventTarget(nativeEvent) {
|
|---|
| 3874 | // Fallback to nativeEvent.srcElement for IE9
|
|---|
| 3875 | // https://github.com/facebook/react/issues/12506
|
|---|
| 3876 | var target = nativeEvent.target || nativeEvent.srcElement || window; // Normalize SVG <use> element events #4963
|
|---|
| 3877 |
|
|---|
| 3878 | if (target.correspondingUseElement) {
|
|---|
| 3879 | target = target.correspondingUseElement;
|
|---|
| 3880 | } // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
|
|---|
| 3881 | // @see http://www.quirksmode.org/js/events_properties.html
|
|---|
| 3882 |
|
|---|
| 3883 |
|
|---|
| 3884 | return target.nodeType === TEXT_NODE ? target.parentNode : target;
|
|---|
| 3885 | }
|
|---|
| 3886 |
|
|---|
| 3887 | var restoreImpl = null;
|
|---|
| 3888 | var restoreTarget = null;
|
|---|
| 3889 | var restoreQueue = null;
|
|---|
| 3890 |
|
|---|
| 3891 | function restoreStateOfTarget(target) {
|
|---|
| 3892 | // We perform this translation at the end of the event loop so that we
|
|---|
| 3893 | // always receive the correct fiber here
|
|---|
| 3894 | var internalInstance = getInstanceFromNode(target);
|
|---|
| 3895 |
|
|---|
| 3896 | if (!internalInstance) {
|
|---|
| 3897 | // Unmounted
|
|---|
| 3898 | return;
|
|---|
| 3899 | }
|
|---|
| 3900 |
|
|---|
| 3901 | if (typeof restoreImpl !== 'function') {
|
|---|
| 3902 | throw new Error('setRestoreImplementation() needs to be called to handle a target for controlled ' + 'events. This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 3903 | }
|
|---|
| 3904 |
|
|---|
| 3905 | var stateNode = internalInstance.stateNode; // Guard against Fiber being unmounted.
|
|---|
| 3906 |
|
|---|
| 3907 | if (stateNode) {
|
|---|
| 3908 | var _props = getFiberCurrentPropsFromNode(stateNode);
|
|---|
| 3909 |
|
|---|
| 3910 | restoreImpl(internalInstance.stateNode, internalInstance.type, _props);
|
|---|
| 3911 | }
|
|---|
| 3912 | }
|
|---|
| 3913 |
|
|---|
| 3914 | function setRestoreImplementation(impl) {
|
|---|
| 3915 | restoreImpl = impl;
|
|---|
| 3916 | }
|
|---|
| 3917 | function enqueueStateRestore(target) {
|
|---|
| 3918 | if (restoreTarget) {
|
|---|
| 3919 | if (restoreQueue) {
|
|---|
| 3920 | restoreQueue.push(target);
|
|---|
| 3921 | } else {
|
|---|
| 3922 | restoreQueue = [target];
|
|---|
| 3923 | }
|
|---|
| 3924 | } else {
|
|---|
| 3925 | restoreTarget = target;
|
|---|
| 3926 | }
|
|---|
| 3927 | }
|
|---|
| 3928 | function needsStateRestore() {
|
|---|
| 3929 | return restoreTarget !== null || restoreQueue !== null;
|
|---|
| 3930 | }
|
|---|
| 3931 | function restoreStateIfNeeded() {
|
|---|
| 3932 | if (!restoreTarget) {
|
|---|
| 3933 | return;
|
|---|
| 3934 | }
|
|---|
| 3935 |
|
|---|
| 3936 | var target = restoreTarget;
|
|---|
| 3937 | var queuedTargets = restoreQueue;
|
|---|
| 3938 | restoreTarget = null;
|
|---|
| 3939 | restoreQueue = null;
|
|---|
| 3940 | restoreStateOfTarget(target);
|
|---|
| 3941 |
|
|---|
| 3942 | if (queuedTargets) {
|
|---|
| 3943 | for (var i = 0; i < queuedTargets.length; i++) {
|
|---|
| 3944 | restoreStateOfTarget(queuedTargets[i]);
|
|---|
| 3945 | }
|
|---|
| 3946 | }
|
|---|
| 3947 | }
|
|---|
| 3948 |
|
|---|
| 3949 | // the renderer. Such as when we're dispatching events or if third party
|
|---|
| 3950 | // libraries need to call batchedUpdates. Eventually, this API will go away when
|
|---|
| 3951 | // everything is batched by default. We'll then have a similar API to opt-out of
|
|---|
| 3952 | // scheduled work and instead do synchronous work.
|
|---|
| 3953 | // Defaults
|
|---|
| 3954 |
|
|---|
| 3955 | var batchedUpdatesImpl = function (fn, bookkeeping) {
|
|---|
| 3956 | return fn(bookkeeping);
|
|---|
| 3957 | };
|
|---|
| 3958 |
|
|---|
| 3959 | var flushSyncImpl = function () {};
|
|---|
| 3960 |
|
|---|
| 3961 | var isInsideEventHandler = false;
|
|---|
| 3962 |
|
|---|
| 3963 | function finishEventHandler() {
|
|---|
| 3964 | // Here we wait until all updates have propagated, which is important
|
|---|
| 3965 | // when using controlled components within layers:
|
|---|
| 3966 | // https://github.com/facebook/react/issues/1698
|
|---|
| 3967 | // Then we restore state of any controlled component.
|
|---|
| 3968 | var controlledComponentsHavePendingUpdates = needsStateRestore();
|
|---|
| 3969 |
|
|---|
| 3970 | if (controlledComponentsHavePendingUpdates) {
|
|---|
| 3971 | // If a controlled event was fired, we may need to restore the state of
|
|---|
| 3972 | // the DOM node back to the controlled value. This is necessary when React
|
|---|
| 3973 | // bails out of the update without touching the DOM.
|
|---|
| 3974 | // TODO: Restore state in the microtask, after the discrete updates flush,
|
|---|
| 3975 | // instead of early flushing them here.
|
|---|
| 3976 | flushSyncImpl();
|
|---|
| 3977 | restoreStateIfNeeded();
|
|---|
| 3978 | }
|
|---|
| 3979 | }
|
|---|
| 3980 |
|
|---|
| 3981 | function batchedUpdates(fn, a, b) {
|
|---|
| 3982 | if (isInsideEventHandler) {
|
|---|
| 3983 | // If we are currently inside another batch, we need to wait until it
|
|---|
| 3984 | // fully completes before restoring state.
|
|---|
| 3985 | return fn(a, b);
|
|---|
| 3986 | }
|
|---|
| 3987 |
|
|---|
| 3988 | isInsideEventHandler = true;
|
|---|
| 3989 |
|
|---|
| 3990 | try {
|
|---|
| 3991 | return batchedUpdatesImpl(fn, a, b);
|
|---|
| 3992 | } finally {
|
|---|
| 3993 | isInsideEventHandler = false;
|
|---|
| 3994 | finishEventHandler();
|
|---|
| 3995 | }
|
|---|
| 3996 | } // TODO: Replace with flushSync
|
|---|
| 3997 | function setBatchingImplementation(_batchedUpdatesImpl, _discreteUpdatesImpl, _flushSyncImpl) {
|
|---|
| 3998 | batchedUpdatesImpl = _batchedUpdatesImpl;
|
|---|
| 3999 | flushSyncImpl = _flushSyncImpl;
|
|---|
| 4000 | }
|
|---|
| 4001 |
|
|---|
| 4002 | function isInteractive(tag) {
|
|---|
| 4003 | return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea';
|
|---|
| 4004 | }
|
|---|
| 4005 |
|
|---|
| 4006 | function shouldPreventMouseEvent(name, type, props) {
|
|---|
| 4007 | switch (name) {
|
|---|
| 4008 | case 'onClick':
|
|---|
| 4009 | case 'onClickCapture':
|
|---|
| 4010 | case 'onDoubleClick':
|
|---|
| 4011 | case 'onDoubleClickCapture':
|
|---|
| 4012 | case 'onMouseDown':
|
|---|
| 4013 | case 'onMouseDownCapture':
|
|---|
| 4014 | case 'onMouseMove':
|
|---|
| 4015 | case 'onMouseMoveCapture':
|
|---|
| 4016 | case 'onMouseUp':
|
|---|
| 4017 | case 'onMouseUpCapture':
|
|---|
| 4018 | case 'onMouseEnter':
|
|---|
| 4019 | return !!(props.disabled && isInteractive(type));
|
|---|
| 4020 |
|
|---|
| 4021 | default:
|
|---|
| 4022 | return false;
|
|---|
| 4023 | }
|
|---|
| 4024 | }
|
|---|
| 4025 | /**
|
|---|
| 4026 | * @param {object} inst The instance, which is the source of events.
|
|---|
| 4027 | * @param {string} registrationName Name of listener (e.g. `onClick`).
|
|---|
| 4028 | * @return {?function} The stored callback.
|
|---|
| 4029 | */
|
|---|
| 4030 |
|
|---|
| 4031 |
|
|---|
| 4032 | function getListener(inst, registrationName) {
|
|---|
| 4033 | var stateNode = inst.stateNode;
|
|---|
| 4034 |
|
|---|
| 4035 | if (stateNode === null) {
|
|---|
| 4036 | // Work in progress (ex: onload events in incremental mode).
|
|---|
| 4037 | return null;
|
|---|
| 4038 | }
|
|---|
| 4039 |
|
|---|
| 4040 | var props = getFiberCurrentPropsFromNode(stateNode);
|
|---|
| 4041 |
|
|---|
| 4042 | if (props === null) {
|
|---|
| 4043 | // Work in progress.
|
|---|
| 4044 | return null;
|
|---|
| 4045 | }
|
|---|
| 4046 |
|
|---|
| 4047 | var listener = props[registrationName];
|
|---|
| 4048 |
|
|---|
| 4049 | if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
|
|---|
| 4050 | return null;
|
|---|
| 4051 | }
|
|---|
| 4052 |
|
|---|
| 4053 | if (listener && typeof listener !== 'function') {
|
|---|
| 4054 | throw new Error("Expected `" + registrationName + "` listener to be a function, instead got a value of `" + typeof listener + "` type.");
|
|---|
| 4055 | }
|
|---|
| 4056 |
|
|---|
| 4057 | return listener;
|
|---|
| 4058 | }
|
|---|
| 4059 |
|
|---|
| 4060 | var passiveBrowserEventsSupported = false; // Check if browser support events with passive listeners
|
|---|
| 4061 | // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support
|
|---|
| 4062 |
|
|---|
| 4063 | if (canUseDOM) {
|
|---|
| 4064 | try {
|
|---|
| 4065 | var options = {}; // $FlowFixMe: Ignore Flow complaining about needing a value
|
|---|
| 4066 |
|
|---|
| 4067 | Object.defineProperty(options, 'passive', {
|
|---|
| 4068 | get: function () {
|
|---|
| 4069 | passiveBrowserEventsSupported = true;
|
|---|
| 4070 | }
|
|---|
| 4071 | });
|
|---|
| 4072 | window.addEventListener('test', options, options);
|
|---|
| 4073 | window.removeEventListener('test', options, options);
|
|---|
| 4074 | } catch (e) {
|
|---|
| 4075 | passiveBrowserEventsSupported = false;
|
|---|
| 4076 | }
|
|---|
| 4077 | }
|
|---|
| 4078 |
|
|---|
| 4079 | function invokeGuardedCallbackProd(name, func, context, a, b, c, d, e, f) {
|
|---|
| 4080 | var funcArgs = Array.prototype.slice.call(arguments, 3);
|
|---|
| 4081 |
|
|---|
| 4082 | try {
|
|---|
| 4083 | func.apply(context, funcArgs);
|
|---|
| 4084 | } catch (error) {
|
|---|
| 4085 | this.onError(error);
|
|---|
| 4086 | }
|
|---|
| 4087 | }
|
|---|
| 4088 |
|
|---|
| 4089 | var invokeGuardedCallbackImpl = invokeGuardedCallbackProd;
|
|---|
| 4090 |
|
|---|
| 4091 | {
|
|---|
| 4092 | // In DEV mode, we swap out invokeGuardedCallback for a special version
|
|---|
| 4093 | // that plays more nicely with the browser's DevTools. The idea is to preserve
|
|---|
| 4094 | // "Pause on exceptions" behavior. Because React wraps all user-provided
|
|---|
| 4095 | // functions in invokeGuardedCallback, and the production version of
|
|---|
| 4096 | // invokeGuardedCallback uses a try-catch, all user exceptions are treated
|
|---|
| 4097 | // like caught exceptions, and the DevTools won't pause unless the developer
|
|---|
| 4098 | // takes the extra step of enabling pause on caught exceptions. This is
|
|---|
| 4099 | // unintuitive, though, because even though React has caught the error, from
|
|---|
| 4100 | // the developer's perspective, the error is uncaught.
|
|---|
| 4101 | //
|
|---|
| 4102 | // To preserve the expected "Pause on exceptions" behavior, we don't use a
|
|---|
| 4103 | // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake
|
|---|
| 4104 | // DOM node, and call the user-provided callback from inside an event handler
|
|---|
| 4105 | // for that fake event. If the callback throws, the error is "captured" using
|
|---|
| 4106 | // a global event handler. But because the error happens in a different
|
|---|
| 4107 | // event loop context, it does not interrupt the normal program flow.
|
|---|
| 4108 | // Effectively, this gives us try-catch behavior without actually using
|
|---|
| 4109 | // try-catch. Neat!
|
|---|
| 4110 | // Check that the browser supports the APIs we need to implement our special
|
|---|
| 4111 | // DEV version of invokeGuardedCallback
|
|---|
| 4112 | if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') {
|
|---|
| 4113 | var fakeNode = document.createElement('react');
|
|---|
| 4114 |
|
|---|
| 4115 | invokeGuardedCallbackImpl = function invokeGuardedCallbackDev(name, func, context, a, b, c, d, e, f) {
|
|---|
| 4116 | // If document doesn't exist we know for sure we will crash in this method
|
|---|
| 4117 | // when we call document.createEvent(). However this can cause confusing
|
|---|
| 4118 | // errors: https://github.com/facebook/create-react-app/issues/3482
|
|---|
| 4119 | // So we preemptively throw with a better message instead.
|
|---|
| 4120 | if (typeof document === 'undefined' || document === null) {
|
|---|
| 4121 | throw new Error('The `document` global was defined when React was initialized, but is not ' + 'defined anymore. This can happen in a test environment if a component ' + 'schedules an update from an asynchronous callback, but the test has already ' + 'finished running. To solve this, you can either unmount the component at ' + 'the end of your test (and ensure that any asynchronous operations get ' + 'canceled in `componentWillUnmount`), or you can change the test itself ' + 'to be asynchronous.');
|
|---|
| 4122 | }
|
|---|
| 4123 |
|
|---|
| 4124 | var evt = document.createEvent('Event');
|
|---|
| 4125 | var didCall = false; // Keeps track of whether the user-provided callback threw an error. We
|
|---|
| 4126 | // set this to true at the beginning, then set it to false right after
|
|---|
| 4127 | // calling the function. If the function errors, `didError` will never be
|
|---|
| 4128 | // set to false. This strategy works even if the browser is flaky and
|
|---|
| 4129 | // fails to call our global error handler, because it doesn't rely on
|
|---|
| 4130 | // the error event at all.
|
|---|
| 4131 |
|
|---|
| 4132 | var didError = true; // Keeps track of the value of window.event so that we can reset it
|
|---|
| 4133 | // during the callback to let user code access window.event in the
|
|---|
| 4134 | // browsers that support it.
|
|---|
| 4135 |
|
|---|
| 4136 | var windowEvent = window.event; // Keeps track of the descriptor of window.event to restore it after event
|
|---|
| 4137 | // dispatching: https://github.com/facebook/react/issues/13688
|
|---|
| 4138 |
|
|---|
| 4139 | var windowEventDescriptor = Object.getOwnPropertyDescriptor(window, 'event');
|
|---|
| 4140 |
|
|---|
| 4141 | function restoreAfterDispatch() {
|
|---|
| 4142 | // We immediately remove the callback from event listeners so that
|
|---|
| 4143 | // nested `invokeGuardedCallback` calls do not clash. Otherwise, a
|
|---|
| 4144 | // nested call would trigger the fake event handlers of any call higher
|
|---|
| 4145 | // in the stack.
|
|---|
| 4146 | fakeNode.removeEventListener(evtType, callCallback, false); // We check for window.hasOwnProperty('event') to prevent the
|
|---|
| 4147 | // window.event assignment in both IE <= 10 as they throw an error
|
|---|
| 4148 | // "Member not found" in strict mode, and in Firefox which does not
|
|---|
| 4149 | // support window.event.
|
|---|
| 4150 |
|
|---|
| 4151 | if (typeof window.event !== 'undefined' && window.hasOwnProperty('event')) {
|
|---|
| 4152 | window.event = windowEvent;
|
|---|
| 4153 | }
|
|---|
| 4154 | } // Create an event handler for our fake event. We will synchronously
|
|---|
| 4155 | // dispatch our fake event using `dispatchEvent`. Inside the handler, we
|
|---|
| 4156 | // call the user-provided callback.
|
|---|
| 4157 |
|
|---|
| 4158 |
|
|---|
| 4159 | var funcArgs = Array.prototype.slice.call(arguments, 3);
|
|---|
| 4160 |
|
|---|
| 4161 | function callCallback() {
|
|---|
| 4162 | didCall = true;
|
|---|
| 4163 | restoreAfterDispatch();
|
|---|
| 4164 | func.apply(context, funcArgs);
|
|---|
| 4165 | didError = false;
|
|---|
| 4166 | } // Create a global error event handler. We use this to capture the value
|
|---|
| 4167 | // that was thrown. It's possible that this error handler will fire more
|
|---|
| 4168 | // than once; for example, if non-React code also calls `dispatchEvent`
|
|---|
| 4169 | // and a handler for that event throws. We should be resilient to most of
|
|---|
| 4170 | // those cases. Even if our error event handler fires more than once, the
|
|---|
| 4171 | // last error event is always used. If the callback actually does error,
|
|---|
| 4172 | // we know that the last error event is the correct one, because it's not
|
|---|
| 4173 | // possible for anything else to have happened in between our callback
|
|---|
| 4174 | // erroring and the code that follows the `dispatchEvent` call below. If
|
|---|
| 4175 | // the callback doesn't error, but the error event was fired, we know to
|
|---|
| 4176 | // ignore it because `didError` will be false, as described above.
|
|---|
| 4177 |
|
|---|
| 4178 |
|
|---|
| 4179 | var error; // Use this to track whether the error event is ever called.
|
|---|
| 4180 |
|
|---|
| 4181 | var didSetError = false;
|
|---|
| 4182 | var isCrossOriginError = false;
|
|---|
| 4183 |
|
|---|
| 4184 | function handleWindowError(event) {
|
|---|
| 4185 | error = event.error;
|
|---|
| 4186 | didSetError = true;
|
|---|
| 4187 |
|
|---|
| 4188 | if (error === null && event.colno === 0 && event.lineno === 0) {
|
|---|
| 4189 | isCrossOriginError = true;
|
|---|
| 4190 | }
|
|---|
| 4191 |
|
|---|
| 4192 | if (event.defaultPrevented) {
|
|---|
| 4193 | // Some other error handler has prevented default.
|
|---|
| 4194 | // Browsers silence the error report if this happens.
|
|---|
| 4195 | // We'll remember this to later decide whether to log it or not.
|
|---|
| 4196 | if (error != null && typeof error === 'object') {
|
|---|
| 4197 | try {
|
|---|
| 4198 | error._suppressLogging = true;
|
|---|
| 4199 | } catch (inner) {// Ignore.
|
|---|
| 4200 | }
|
|---|
| 4201 | }
|
|---|
| 4202 | }
|
|---|
| 4203 | } // Create a fake event type.
|
|---|
| 4204 |
|
|---|
| 4205 |
|
|---|
| 4206 | var evtType = "react-" + (name ? name : 'invokeguardedcallback'); // Attach our event handlers
|
|---|
| 4207 |
|
|---|
| 4208 | window.addEventListener('error', handleWindowError);
|
|---|
| 4209 | fakeNode.addEventListener(evtType, callCallback, false); // Synchronously dispatch our fake event. If the user-provided function
|
|---|
| 4210 | // errors, it will trigger our global error handler.
|
|---|
| 4211 |
|
|---|
| 4212 | evt.initEvent(evtType, false, false);
|
|---|
| 4213 | fakeNode.dispatchEvent(evt);
|
|---|
| 4214 |
|
|---|
| 4215 | if (windowEventDescriptor) {
|
|---|
| 4216 | Object.defineProperty(window, 'event', windowEventDescriptor);
|
|---|
| 4217 | }
|
|---|
| 4218 |
|
|---|
| 4219 | if (didCall && didError) {
|
|---|
| 4220 | if (!didSetError) {
|
|---|
| 4221 | // The callback errored, but the error event never fired.
|
|---|
| 4222 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 4223 | error = new Error('An error was thrown inside one of your components, but React ' + "doesn't know what it was. This is likely due to browser " + 'flakiness. React does its best to preserve the "Pause on ' + 'exceptions" behavior of the DevTools, which requires some ' + "DEV-mode only tricks. It's possible that these don't work in " + 'your browser. Try triggering the error in production mode, ' + 'or switching to a modern browser. If you suspect that this is ' + 'actually an issue with React, please file an issue.');
|
|---|
| 4224 | } else if (isCrossOriginError) {
|
|---|
| 4225 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 4226 | error = new Error("A cross-origin error was thrown. React doesn't have access to " + 'the actual error object in development. ' + 'See https://reactjs.org/link/crossorigin-error for more information.');
|
|---|
| 4227 | }
|
|---|
| 4228 |
|
|---|
| 4229 | this.onError(error);
|
|---|
| 4230 | } // Remove our event listeners
|
|---|
| 4231 |
|
|---|
| 4232 |
|
|---|
| 4233 | window.removeEventListener('error', handleWindowError);
|
|---|
| 4234 |
|
|---|
| 4235 | if (!didCall) {
|
|---|
| 4236 | // Something went really wrong, and our event was not dispatched.
|
|---|
| 4237 | // https://github.com/facebook/react/issues/16734
|
|---|
| 4238 | // https://github.com/facebook/react/issues/16585
|
|---|
| 4239 | // Fall back to the production implementation.
|
|---|
| 4240 | restoreAfterDispatch();
|
|---|
| 4241 | return invokeGuardedCallbackProd.apply(this, arguments);
|
|---|
| 4242 | }
|
|---|
| 4243 | };
|
|---|
| 4244 | }
|
|---|
| 4245 | }
|
|---|
| 4246 |
|
|---|
| 4247 | var invokeGuardedCallbackImpl$1 = invokeGuardedCallbackImpl;
|
|---|
| 4248 |
|
|---|
| 4249 | var hasError = false;
|
|---|
| 4250 | var caughtError = null; // Used by event system to capture/rethrow the first error.
|
|---|
| 4251 |
|
|---|
| 4252 | var hasRethrowError = false;
|
|---|
| 4253 | var rethrowError = null;
|
|---|
| 4254 | var reporter = {
|
|---|
| 4255 | onError: function (error) {
|
|---|
| 4256 | hasError = true;
|
|---|
| 4257 | caughtError = error;
|
|---|
| 4258 | }
|
|---|
| 4259 | };
|
|---|
| 4260 | /**
|
|---|
| 4261 | * Call a function while guarding against errors that happens within it.
|
|---|
| 4262 | * Returns an error if it throws, otherwise null.
|
|---|
| 4263 | *
|
|---|
| 4264 | * In production, this is implemented using a try-catch. The reason we don't
|
|---|
| 4265 | * use a try-catch directly is so that we can swap out a different
|
|---|
| 4266 | * implementation in DEV mode.
|
|---|
| 4267 | *
|
|---|
| 4268 | * @param {String} name of the guard to use for logging or debugging
|
|---|
| 4269 | * @param {Function} func The function to invoke
|
|---|
| 4270 | * @param {*} context The context to use when calling the function
|
|---|
| 4271 | * @param {...*} args Arguments for function
|
|---|
| 4272 | */
|
|---|
| 4273 |
|
|---|
| 4274 | function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) {
|
|---|
| 4275 | hasError = false;
|
|---|
| 4276 | caughtError = null;
|
|---|
| 4277 | invokeGuardedCallbackImpl$1.apply(reporter, arguments);
|
|---|
| 4278 | }
|
|---|
| 4279 | /**
|
|---|
| 4280 | * Same as invokeGuardedCallback, but instead of returning an error, it stores
|
|---|
| 4281 | * it in a global so it can be rethrown by `rethrowCaughtError` later.
|
|---|
| 4282 | * TODO: See if caughtError and rethrowError can be unified.
|
|---|
| 4283 | *
|
|---|
| 4284 | * @param {String} name of the guard to use for logging or debugging
|
|---|
| 4285 | * @param {Function} func The function to invoke
|
|---|
| 4286 | * @param {*} context The context to use when calling the function
|
|---|
| 4287 | * @param {...*} args Arguments for function
|
|---|
| 4288 | */
|
|---|
| 4289 |
|
|---|
| 4290 | function invokeGuardedCallbackAndCatchFirstError(name, func, context, a, b, c, d, e, f) {
|
|---|
| 4291 | invokeGuardedCallback.apply(this, arguments);
|
|---|
| 4292 |
|
|---|
| 4293 | if (hasError) {
|
|---|
| 4294 | var error = clearCaughtError();
|
|---|
| 4295 |
|
|---|
| 4296 | if (!hasRethrowError) {
|
|---|
| 4297 | hasRethrowError = true;
|
|---|
| 4298 | rethrowError = error;
|
|---|
| 4299 | }
|
|---|
| 4300 | }
|
|---|
| 4301 | }
|
|---|
| 4302 | /**
|
|---|
| 4303 | * During execution of guarded functions we will capture the first error which
|
|---|
| 4304 | * we will rethrow to be handled by the top level error handler.
|
|---|
| 4305 | */
|
|---|
| 4306 |
|
|---|
| 4307 | function rethrowCaughtError() {
|
|---|
| 4308 | if (hasRethrowError) {
|
|---|
| 4309 | var error = rethrowError;
|
|---|
| 4310 | hasRethrowError = false;
|
|---|
| 4311 | rethrowError = null;
|
|---|
| 4312 | throw error;
|
|---|
| 4313 | }
|
|---|
| 4314 | }
|
|---|
| 4315 | function hasCaughtError() {
|
|---|
| 4316 | return hasError;
|
|---|
| 4317 | }
|
|---|
| 4318 | function clearCaughtError() {
|
|---|
| 4319 | if (hasError) {
|
|---|
| 4320 | var error = caughtError;
|
|---|
| 4321 | hasError = false;
|
|---|
| 4322 | caughtError = null;
|
|---|
| 4323 | return error;
|
|---|
| 4324 | } else {
|
|---|
| 4325 | throw new Error('clearCaughtError was called but no error was captured. This error ' + 'is likely caused by a bug in React. Please file an issue.');
|
|---|
| 4326 | }
|
|---|
| 4327 | }
|
|---|
| 4328 |
|
|---|
| 4329 | /**
|
|---|
| 4330 | * `ReactInstanceMap` maintains a mapping from a public facing stateful
|
|---|
| 4331 | * instance (key) and the internal representation (value). This allows public
|
|---|
| 4332 | * methods to accept the user facing instance as an argument and map them back
|
|---|
| 4333 | * to internal methods.
|
|---|
| 4334 | *
|
|---|
| 4335 | * Note that this module is currently shared and assumed to be stateless.
|
|---|
| 4336 | * If this becomes an actual Map, that will break.
|
|---|
| 4337 | */
|
|---|
| 4338 | function get(key) {
|
|---|
| 4339 | return key._reactInternals;
|
|---|
| 4340 | }
|
|---|
| 4341 | function has(key) {
|
|---|
| 4342 | return key._reactInternals !== undefined;
|
|---|
| 4343 | }
|
|---|
| 4344 | function set(key, value) {
|
|---|
| 4345 | key._reactInternals = value;
|
|---|
| 4346 | }
|
|---|
| 4347 |
|
|---|
| 4348 | // Don't change these two values. They're used by React Dev Tools.
|
|---|
| 4349 | var NoFlags =
|
|---|
| 4350 | /* */
|
|---|
| 4351 | 0;
|
|---|
| 4352 | var PerformedWork =
|
|---|
| 4353 | /* */
|
|---|
| 4354 | 1; // You can change the rest (and add more).
|
|---|
| 4355 |
|
|---|
| 4356 | var Placement =
|
|---|
| 4357 | /* */
|
|---|
| 4358 | 2;
|
|---|
| 4359 | var Update =
|
|---|
| 4360 | /* */
|
|---|
| 4361 | 4;
|
|---|
| 4362 | var ChildDeletion =
|
|---|
| 4363 | /* */
|
|---|
| 4364 | 16;
|
|---|
| 4365 | var ContentReset =
|
|---|
| 4366 | /* */
|
|---|
| 4367 | 32;
|
|---|
| 4368 | var Callback =
|
|---|
| 4369 | /* */
|
|---|
| 4370 | 64;
|
|---|
| 4371 | var DidCapture =
|
|---|
| 4372 | /* */
|
|---|
| 4373 | 128;
|
|---|
| 4374 | var ForceClientRender =
|
|---|
| 4375 | /* */
|
|---|
| 4376 | 256;
|
|---|
| 4377 | var Ref =
|
|---|
| 4378 | /* */
|
|---|
| 4379 | 512;
|
|---|
| 4380 | var Snapshot =
|
|---|
| 4381 | /* */
|
|---|
| 4382 | 1024;
|
|---|
| 4383 | var Passive =
|
|---|
| 4384 | /* */
|
|---|
| 4385 | 2048;
|
|---|
| 4386 | var Hydrating =
|
|---|
| 4387 | /* */
|
|---|
| 4388 | 4096;
|
|---|
| 4389 | var Visibility =
|
|---|
| 4390 | /* */
|
|---|
| 4391 | 8192;
|
|---|
| 4392 | var StoreConsistency =
|
|---|
| 4393 | /* */
|
|---|
| 4394 | 16384;
|
|---|
| 4395 | var LifecycleEffectMask = Passive | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit)
|
|---|
| 4396 |
|
|---|
| 4397 | var HostEffectMask =
|
|---|
| 4398 | /* */
|
|---|
| 4399 | 32767; // These are not really side effects, but we still reuse this field.
|
|---|
| 4400 |
|
|---|
| 4401 | var Incomplete =
|
|---|
| 4402 | /* */
|
|---|
| 4403 | 32768;
|
|---|
| 4404 | var ShouldCapture =
|
|---|
| 4405 | /* */
|
|---|
| 4406 | 65536;
|
|---|
| 4407 | var ForceUpdateForLegacySuspense =
|
|---|
| 4408 | /* */
|
|---|
| 4409 | 131072;
|
|---|
| 4410 | var Forked =
|
|---|
| 4411 | /* */
|
|---|
| 4412 | 1048576; // Static tags describe aspects of a fiber that are not specific to a render,
|
|---|
| 4413 | // e.g. a fiber uses a passive effect (even if there are no updates on this particular render).
|
|---|
| 4414 | // This enables us to defer more work in the unmount case,
|
|---|
| 4415 | // since we can defer traversing the tree during layout to look for Passive effects,
|
|---|
| 4416 | // and instead rely on the static flag as a signal that there may be cleanup work.
|
|---|
| 4417 |
|
|---|
| 4418 | var RefStatic =
|
|---|
| 4419 | /* */
|
|---|
| 4420 | 2097152;
|
|---|
| 4421 | var LayoutStatic =
|
|---|
| 4422 | /* */
|
|---|
| 4423 | 4194304;
|
|---|
| 4424 | var PassiveStatic =
|
|---|
| 4425 | /* */
|
|---|
| 4426 | 8388608; // These flags allow us to traverse to fibers that have effects on mount
|
|---|
| 4427 | // without traversing the entire tree after every commit for
|
|---|
| 4428 | // double invoking
|
|---|
| 4429 |
|
|---|
| 4430 | var MountLayoutDev =
|
|---|
| 4431 | /* */
|
|---|
| 4432 | 16777216;
|
|---|
| 4433 | var MountPassiveDev =
|
|---|
| 4434 | /* */
|
|---|
| 4435 | 33554432; // Groups of flags that are used in the commit phase to skip over trees that
|
|---|
| 4436 | // don't contain effects, by checking subtreeFlags.
|
|---|
| 4437 |
|
|---|
| 4438 | var BeforeMutationMask = // TODO: Remove Update flag from before mutation phase by re-landing Visibility
|
|---|
| 4439 | // flag logic (see #20043)
|
|---|
| 4440 | Update | Snapshot | ( 0);
|
|---|
| 4441 | var MutationMask = Placement | Update | ChildDeletion | ContentReset | Ref | Hydrating | Visibility;
|
|---|
| 4442 | var LayoutMask = Update | Callback | Ref | Visibility; // TODO: Split into PassiveMountMask and PassiveUnmountMask
|
|---|
| 4443 |
|
|---|
| 4444 | var PassiveMask = Passive | ChildDeletion; // Union of tags that don't get reset on clones.
|
|---|
| 4445 | // This allows certain concepts to persist without recalculating them,
|
|---|
| 4446 | // e.g. whether a subtree contains passive effects or portals.
|
|---|
| 4447 |
|
|---|
| 4448 | var StaticMask = LayoutStatic | PassiveStatic | RefStatic;
|
|---|
| 4449 |
|
|---|
| 4450 | var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner;
|
|---|
| 4451 | function getNearestMountedFiber(fiber) {
|
|---|
| 4452 | var node = fiber;
|
|---|
| 4453 | var nearestMounted = fiber;
|
|---|
| 4454 |
|
|---|
| 4455 | if (!fiber.alternate) {
|
|---|
| 4456 | // If there is no alternate, this might be a new tree that isn't inserted
|
|---|
| 4457 | // yet. If it is, then it will have a pending insertion effect on it.
|
|---|
| 4458 | var nextNode = node;
|
|---|
| 4459 |
|
|---|
| 4460 | do {
|
|---|
| 4461 | node = nextNode;
|
|---|
| 4462 |
|
|---|
| 4463 | if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
|
|---|
| 4464 | // This is an insertion or in-progress hydration. The nearest possible
|
|---|
| 4465 | // mounted fiber is the parent but we need to continue to figure out
|
|---|
| 4466 | // if that one is still mounted.
|
|---|
| 4467 | nearestMounted = node.return;
|
|---|
| 4468 | }
|
|---|
| 4469 |
|
|---|
| 4470 | nextNode = node.return;
|
|---|
| 4471 | } while (nextNode);
|
|---|
| 4472 | } else {
|
|---|
| 4473 | while (node.return) {
|
|---|
| 4474 | node = node.return;
|
|---|
| 4475 | }
|
|---|
| 4476 | }
|
|---|
| 4477 |
|
|---|
| 4478 | if (node.tag === HostRoot) {
|
|---|
| 4479 | // TODO: Check if this was a nested HostRoot when used with
|
|---|
| 4480 | // renderContainerIntoSubtree.
|
|---|
| 4481 | return nearestMounted;
|
|---|
| 4482 | } // If we didn't hit the root, that means that we're in an disconnected tree
|
|---|
| 4483 | // that has been unmounted.
|
|---|
| 4484 |
|
|---|
| 4485 |
|
|---|
| 4486 | return null;
|
|---|
| 4487 | }
|
|---|
| 4488 | function getSuspenseInstanceFromFiber(fiber) {
|
|---|
| 4489 | if (fiber.tag === SuspenseComponent) {
|
|---|
| 4490 | var suspenseState = fiber.memoizedState;
|
|---|
| 4491 |
|
|---|
| 4492 | if (suspenseState === null) {
|
|---|
| 4493 | var current = fiber.alternate;
|
|---|
| 4494 |
|
|---|
| 4495 | if (current !== null) {
|
|---|
| 4496 | suspenseState = current.memoizedState;
|
|---|
| 4497 | }
|
|---|
| 4498 | }
|
|---|
| 4499 |
|
|---|
| 4500 | if (suspenseState !== null) {
|
|---|
| 4501 | return suspenseState.dehydrated;
|
|---|
| 4502 | }
|
|---|
| 4503 | }
|
|---|
| 4504 |
|
|---|
| 4505 | return null;
|
|---|
| 4506 | }
|
|---|
| 4507 | function getContainerFromFiber(fiber) {
|
|---|
| 4508 | return fiber.tag === HostRoot ? fiber.stateNode.containerInfo : null;
|
|---|
| 4509 | }
|
|---|
| 4510 | function isFiberMounted(fiber) {
|
|---|
| 4511 | return getNearestMountedFiber(fiber) === fiber;
|
|---|
| 4512 | }
|
|---|
| 4513 | function isMounted(component) {
|
|---|
| 4514 | {
|
|---|
| 4515 | var owner = ReactCurrentOwner.current;
|
|---|
| 4516 |
|
|---|
| 4517 | if (owner !== null && owner.tag === ClassComponent) {
|
|---|
| 4518 | var ownerFiber = owner;
|
|---|
| 4519 | var instance = ownerFiber.stateNode;
|
|---|
| 4520 |
|
|---|
| 4521 | if (!instance._warnedAboutRefsInRender) {
|
|---|
| 4522 | error('%s is accessing isMounted inside its render() function. ' + 'render() should be a pure function of props and state. It should ' + 'never access something that requires stale data from the previous ' + 'render, such as refs. Move this logic to componentDidMount and ' + 'componentDidUpdate instead.', getComponentNameFromFiber(ownerFiber) || 'A component');
|
|---|
| 4523 | }
|
|---|
| 4524 |
|
|---|
| 4525 | instance._warnedAboutRefsInRender = true;
|
|---|
| 4526 | }
|
|---|
| 4527 | }
|
|---|
| 4528 |
|
|---|
| 4529 | var fiber = get(component);
|
|---|
| 4530 |
|
|---|
| 4531 | if (!fiber) {
|
|---|
| 4532 | return false;
|
|---|
| 4533 | }
|
|---|
| 4534 |
|
|---|
| 4535 | return getNearestMountedFiber(fiber) === fiber;
|
|---|
| 4536 | }
|
|---|
| 4537 |
|
|---|
| 4538 | function assertIsMounted(fiber) {
|
|---|
| 4539 | if (getNearestMountedFiber(fiber) !== fiber) {
|
|---|
| 4540 | throw new Error('Unable to find node on an unmounted component.');
|
|---|
| 4541 | }
|
|---|
| 4542 | }
|
|---|
| 4543 |
|
|---|
| 4544 | function findCurrentFiberUsingSlowPath(fiber) {
|
|---|
| 4545 | var alternate = fiber.alternate;
|
|---|
| 4546 |
|
|---|
| 4547 | if (!alternate) {
|
|---|
| 4548 | // If there is no alternate, then we only need to check if it is mounted.
|
|---|
| 4549 | var nearestMounted = getNearestMountedFiber(fiber);
|
|---|
| 4550 |
|
|---|
| 4551 | if (nearestMounted === null) {
|
|---|
| 4552 | throw new Error('Unable to find node on an unmounted component.');
|
|---|
| 4553 | }
|
|---|
| 4554 |
|
|---|
| 4555 | if (nearestMounted !== fiber) {
|
|---|
| 4556 | return null;
|
|---|
| 4557 | }
|
|---|
| 4558 |
|
|---|
| 4559 | return fiber;
|
|---|
| 4560 | } // If we have two possible branches, we'll walk backwards up to the root
|
|---|
| 4561 | // to see what path the root points to. On the way we may hit one of the
|
|---|
| 4562 | // special cases and we'll deal with them.
|
|---|
| 4563 |
|
|---|
| 4564 |
|
|---|
| 4565 | var a = fiber;
|
|---|
| 4566 | var b = alternate;
|
|---|
| 4567 |
|
|---|
| 4568 | while (true) {
|
|---|
| 4569 | var parentA = a.return;
|
|---|
| 4570 |
|
|---|
| 4571 | if (parentA === null) {
|
|---|
| 4572 | // We're at the root.
|
|---|
| 4573 | break;
|
|---|
| 4574 | }
|
|---|
| 4575 |
|
|---|
| 4576 | var parentB = parentA.alternate;
|
|---|
| 4577 |
|
|---|
| 4578 | if (parentB === null) {
|
|---|
| 4579 | // There is no alternate. This is an unusual case. Currently, it only
|
|---|
| 4580 | // happens when a Suspense component is hidden. An extra fragment fiber
|
|---|
| 4581 | // is inserted in between the Suspense fiber and its children. Skip
|
|---|
| 4582 | // over this extra fragment fiber and proceed to the next parent.
|
|---|
| 4583 | var nextParent = parentA.return;
|
|---|
| 4584 |
|
|---|
| 4585 | if (nextParent !== null) {
|
|---|
| 4586 | a = b = nextParent;
|
|---|
| 4587 | continue;
|
|---|
| 4588 | } // If there's no parent, we're at the root.
|
|---|
| 4589 |
|
|---|
| 4590 |
|
|---|
| 4591 | break;
|
|---|
| 4592 | } // If both copies of the parent fiber point to the same child, we can
|
|---|
| 4593 | // assume that the child is current. This happens when we bailout on low
|
|---|
| 4594 | // priority: the bailed out fiber's child reuses the current child.
|
|---|
| 4595 |
|
|---|
| 4596 |
|
|---|
| 4597 | if (parentA.child === parentB.child) {
|
|---|
| 4598 | var child = parentA.child;
|
|---|
| 4599 |
|
|---|
| 4600 | while (child) {
|
|---|
| 4601 | if (child === a) {
|
|---|
| 4602 | // We've determined that A is the current branch.
|
|---|
| 4603 | assertIsMounted(parentA);
|
|---|
| 4604 | return fiber;
|
|---|
| 4605 | }
|
|---|
| 4606 |
|
|---|
| 4607 | if (child === b) {
|
|---|
| 4608 | // We've determined that B is the current branch.
|
|---|
| 4609 | assertIsMounted(parentA);
|
|---|
| 4610 | return alternate;
|
|---|
| 4611 | }
|
|---|
| 4612 |
|
|---|
| 4613 | child = child.sibling;
|
|---|
| 4614 | } // We should never have an alternate for any mounting node. So the only
|
|---|
| 4615 | // way this could possibly happen is if this was unmounted, if at all.
|
|---|
| 4616 |
|
|---|
| 4617 |
|
|---|
| 4618 | throw new Error('Unable to find node on an unmounted component.');
|
|---|
| 4619 | }
|
|---|
| 4620 |
|
|---|
| 4621 | if (a.return !== b.return) {
|
|---|
| 4622 | // The return pointer of A and the return pointer of B point to different
|
|---|
| 4623 | // fibers. We assume that return pointers never criss-cross, so A must
|
|---|
| 4624 | // belong to the child set of A.return, and B must belong to the child
|
|---|
| 4625 | // set of B.return.
|
|---|
| 4626 | a = parentA;
|
|---|
| 4627 | b = parentB;
|
|---|
| 4628 | } else {
|
|---|
| 4629 | // The return pointers point to the same fiber. We'll have to use the
|
|---|
| 4630 | // default, slow path: scan the child sets of each parent alternate to see
|
|---|
| 4631 | // which child belongs to which set.
|
|---|
| 4632 | //
|
|---|
| 4633 | // Search parent A's child set
|
|---|
| 4634 | var didFindChild = false;
|
|---|
| 4635 | var _child = parentA.child;
|
|---|
| 4636 |
|
|---|
| 4637 | while (_child) {
|
|---|
| 4638 | if (_child === a) {
|
|---|
| 4639 | didFindChild = true;
|
|---|
| 4640 | a = parentA;
|
|---|
| 4641 | b = parentB;
|
|---|
| 4642 | break;
|
|---|
| 4643 | }
|
|---|
| 4644 |
|
|---|
| 4645 | if (_child === b) {
|
|---|
| 4646 | didFindChild = true;
|
|---|
| 4647 | b = parentA;
|
|---|
| 4648 | a = parentB;
|
|---|
| 4649 | break;
|
|---|
| 4650 | }
|
|---|
| 4651 |
|
|---|
| 4652 | _child = _child.sibling;
|
|---|
| 4653 | }
|
|---|
| 4654 |
|
|---|
| 4655 | if (!didFindChild) {
|
|---|
| 4656 | // Search parent B's child set
|
|---|
| 4657 | _child = parentB.child;
|
|---|
| 4658 |
|
|---|
| 4659 | while (_child) {
|
|---|
| 4660 | if (_child === a) {
|
|---|
| 4661 | didFindChild = true;
|
|---|
| 4662 | a = parentB;
|
|---|
| 4663 | b = parentA;
|
|---|
| 4664 | break;
|
|---|
| 4665 | }
|
|---|
| 4666 |
|
|---|
| 4667 | if (_child === b) {
|
|---|
| 4668 | didFindChild = true;
|
|---|
| 4669 | b = parentB;
|
|---|
| 4670 | a = parentA;
|
|---|
| 4671 | break;
|
|---|
| 4672 | }
|
|---|
| 4673 |
|
|---|
| 4674 | _child = _child.sibling;
|
|---|
| 4675 | }
|
|---|
| 4676 |
|
|---|
| 4677 | if (!didFindChild) {
|
|---|
| 4678 | throw new Error('Child was not found in either parent set. This indicates a bug ' + 'in React related to the return pointer. Please file an issue.');
|
|---|
| 4679 | }
|
|---|
| 4680 | }
|
|---|
| 4681 | }
|
|---|
| 4682 |
|
|---|
| 4683 | if (a.alternate !== b) {
|
|---|
| 4684 | throw new Error("Return fibers should always be each others' alternates. " + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 4685 | }
|
|---|
| 4686 | } // If the root is not a host container, we're in a disconnected tree. I.e.
|
|---|
| 4687 | // unmounted.
|
|---|
| 4688 |
|
|---|
| 4689 |
|
|---|
| 4690 | if (a.tag !== HostRoot) {
|
|---|
| 4691 | throw new Error('Unable to find node on an unmounted component.');
|
|---|
| 4692 | }
|
|---|
| 4693 |
|
|---|
| 4694 | if (a.stateNode.current === a) {
|
|---|
| 4695 | // We've determined that A is the current branch.
|
|---|
| 4696 | return fiber;
|
|---|
| 4697 | } // Otherwise B has to be current branch.
|
|---|
| 4698 |
|
|---|
| 4699 |
|
|---|
| 4700 | return alternate;
|
|---|
| 4701 | }
|
|---|
| 4702 | function findCurrentHostFiber(parent) {
|
|---|
| 4703 | var currentParent = findCurrentFiberUsingSlowPath(parent);
|
|---|
| 4704 | return currentParent !== null ? findCurrentHostFiberImpl(currentParent) : null;
|
|---|
| 4705 | }
|
|---|
| 4706 |
|
|---|
| 4707 | function findCurrentHostFiberImpl(node) {
|
|---|
| 4708 | // Next we'll drill down this component to find the first HostComponent/Text.
|
|---|
| 4709 | if (node.tag === HostComponent || node.tag === HostText) {
|
|---|
| 4710 | return node;
|
|---|
| 4711 | }
|
|---|
| 4712 |
|
|---|
| 4713 | var child = node.child;
|
|---|
| 4714 |
|
|---|
| 4715 | while (child !== null) {
|
|---|
| 4716 | var match = findCurrentHostFiberImpl(child);
|
|---|
| 4717 |
|
|---|
| 4718 | if (match !== null) {
|
|---|
| 4719 | return match;
|
|---|
| 4720 | }
|
|---|
| 4721 |
|
|---|
| 4722 | child = child.sibling;
|
|---|
| 4723 | }
|
|---|
| 4724 |
|
|---|
| 4725 | return null;
|
|---|
| 4726 | }
|
|---|
| 4727 |
|
|---|
| 4728 | function findCurrentHostFiberWithNoPortals(parent) {
|
|---|
| 4729 | var currentParent = findCurrentFiberUsingSlowPath(parent);
|
|---|
| 4730 | return currentParent !== null ? findCurrentHostFiberWithNoPortalsImpl(currentParent) : null;
|
|---|
| 4731 | }
|
|---|
| 4732 |
|
|---|
| 4733 | function findCurrentHostFiberWithNoPortalsImpl(node) {
|
|---|
| 4734 | // Next we'll drill down this component to find the first HostComponent/Text.
|
|---|
| 4735 | if (node.tag === HostComponent || node.tag === HostText) {
|
|---|
| 4736 | return node;
|
|---|
| 4737 | }
|
|---|
| 4738 |
|
|---|
| 4739 | var child = node.child;
|
|---|
| 4740 |
|
|---|
| 4741 | while (child !== null) {
|
|---|
| 4742 | if (child.tag !== HostPortal) {
|
|---|
| 4743 | var match = findCurrentHostFiberWithNoPortalsImpl(child);
|
|---|
| 4744 |
|
|---|
| 4745 | if (match !== null) {
|
|---|
| 4746 | return match;
|
|---|
| 4747 | }
|
|---|
| 4748 | }
|
|---|
| 4749 |
|
|---|
| 4750 | child = child.sibling;
|
|---|
| 4751 | }
|
|---|
| 4752 |
|
|---|
| 4753 | return null;
|
|---|
| 4754 | }
|
|---|
| 4755 |
|
|---|
| 4756 | // This module only exists as an ESM wrapper around the external CommonJS
|
|---|
| 4757 | var scheduleCallback = Scheduler.unstable_scheduleCallback;
|
|---|
| 4758 | var cancelCallback = Scheduler.unstable_cancelCallback;
|
|---|
| 4759 | var shouldYield = Scheduler.unstable_shouldYield;
|
|---|
| 4760 | var requestPaint = Scheduler.unstable_requestPaint;
|
|---|
| 4761 | var now = Scheduler.unstable_now;
|
|---|
| 4762 | var getCurrentPriorityLevel = Scheduler.unstable_getCurrentPriorityLevel;
|
|---|
| 4763 | var ImmediatePriority = Scheduler.unstable_ImmediatePriority;
|
|---|
| 4764 | var UserBlockingPriority = Scheduler.unstable_UserBlockingPriority;
|
|---|
| 4765 | var NormalPriority = Scheduler.unstable_NormalPriority;
|
|---|
| 4766 | var LowPriority = Scheduler.unstable_LowPriority;
|
|---|
| 4767 | var IdlePriority = Scheduler.unstable_IdlePriority;
|
|---|
| 4768 | // this doesn't actually exist on the scheduler, but it *does*
|
|---|
| 4769 | // on scheduler/unstable_mock, which we'll need for internal testing
|
|---|
| 4770 | var unstable_yieldValue = Scheduler.unstable_yieldValue;
|
|---|
| 4771 | var unstable_setDisableYieldValue = Scheduler.unstable_setDisableYieldValue;
|
|---|
| 4772 |
|
|---|
| 4773 | var rendererID = null;
|
|---|
| 4774 | var injectedHook = null;
|
|---|
| 4775 | var injectedProfilingHooks = null;
|
|---|
| 4776 | var hasLoggedError = false;
|
|---|
| 4777 | var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined';
|
|---|
| 4778 | function injectInternals(internals) {
|
|---|
| 4779 | if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') {
|
|---|
| 4780 | // No DevTools
|
|---|
| 4781 | return false;
|
|---|
| 4782 | }
|
|---|
| 4783 |
|
|---|
| 4784 | var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__;
|
|---|
| 4785 |
|
|---|
| 4786 | if (hook.isDisabled) {
|
|---|
| 4787 | // This isn't a real property on the hook, but it can be set to opt out
|
|---|
| 4788 | // of DevTools integration and associated warnings and logs.
|
|---|
| 4789 | // https://github.com/facebook/react/issues/3877
|
|---|
| 4790 | return true;
|
|---|
| 4791 | }
|
|---|
| 4792 |
|
|---|
| 4793 | if (!hook.supportsFiber) {
|
|---|
| 4794 | {
|
|---|
| 4795 | error('The installed version of React DevTools is too old and will not work ' + 'with the current version of React. Please update React DevTools. ' + 'https://reactjs.org/link/react-devtools');
|
|---|
| 4796 | } // DevTools exists, even though it doesn't support Fiber.
|
|---|
| 4797 |
|
|---|
| 4798 |
|
|---|
| 4799 | return true;
|
|---|
| 4800 | }
|
|---|
| 4801 |
|
|---|
| 4802 | try {
|
|---|
| 4803 | if (enableSchedulingProfiler) {
|
|---|
| 4804 | // Conditionally inject these hooks only if Timeline profiler is supported by this build.
|
|---|
| 4805 | // This gives DevTools a way to feature detect that isn't tied to version number
|
|---|
| 4806 | // (since profiling and timeline are controlled by different feature flags).
|
|---|
| 4807 | internals = assign({}, internals, {
|
|---|
| 4808 | getLaneLabelMap: getLaneLabelMap,
|
|---|
| 4809 | injectProfilingHooks: injectProfilingHooks
|
|---|
| 4810 | });
|
|---|
| 4811 | }
|
|---|
| 4812 |
|
|---|
| 4813 | rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks.
|
|---|
| 4814 |
|
|---|
| 4815 | injectedHook = hook;
|
|---|
| 4816 | } catch (err) {
|
|---|
| 4817 | // Catch all errors because it is unsafe to throw during initialization.
|
|---|
| 4818 | {
|
|---|
| 4819 | error('React instrumentation encountered an error: %s.', err);
|
|---|
| 4820 | }
|
|---|
| 4821 | }
|
|---|
| 4822 |
|
|---|
| 4823 | if (hook.checkDCE) {
|
|---|
| 4824 | // This is the real DevTools.
|
|---|
| 4825 | return true;
|
|---|
| 4826 | } else {
|
|---|
| 4827 | // This is likely a hook installed by Fast Refresh runtime.
|
|---|
| 4828 | return false;
|
|---|
| 4829 | }
|
|---|
| 4830 | }
|
|---|
| 4831 | function onScheduleRoot(root, children) {
|
|---|
| 4832 | {
|
|---|
| 4833 | if (injectedHook && typeof injectedHook.onScheduleFiberRoot === 'function') {
|
|---|
| 4834 | try {
|
|---|
| 4835 | injectedHook.onScheduleFiberRoot(rendererID, root, children);
|
|---|
| 4836 | } catch (err) {
|
|---|
| 4837 | if ( !hasLoggedError) {
|
|---|
| 4838 | hasLoggedError = true;
|
|---|
| 4839 |
|
|---|
| 4840 | error('React instrumentation encountered an error: %s', err);
|
|---|
| 4841 | }
|
|---|
| 4842 | }
|
|---|
| 4843 | }
|
|---|
| 4844 | }
|
|---|
| 4845 | }
|
|---|
| 4846 | function onCommitRoot(root, eventPriority) {
|
|---|
| 4847 | if (injectedHook && typeof injectedHook.onCommitFiberRoot === 'function') {
|
|---|
| 4848 | try {
|
|---|
| 4849 | var didError = (root.current.flags & DidCapture) === DidCapture;
|
|---|
| 4850 |
|
|---|
| 4851 | if (enableProfilerTimer) {
|
|---|
| 4852 | var schedulerPriority;
|
|---|
| 4853 |
|
|---|
| 4854 | switch (eventPriority) {
|
|---|
| 4855 | case DiscreteEventPriority:
|
|---|
| 4856 | schedulerPriority = ImmediatePriority;
|
|---|
| 4857 | break;
|
|---|
| 4858 |
|
|---|
| 4859 | case ContinuousEventPriority:
|
|---|
| 4860 | schedulerPriority = UserBlockingPriority;
|
|---|
| 4861 | break;
|
|---|
| 4862 |
|
|---|
| 4863 | case DefaultEventPriority:
|
|---|
| 4864 | schedulerPriority = NormalPriority;
|
|---|
| 4865 | break;
|
|---|
| 4866 |
|
|---|
| 4867 | case IdleEventPriority:
|
|---|
| 4868 | schedulerPriority = IdlePriority;
|
|---|
| 4869 | break;
|
|---|
| 4870 |
|
|---|
| 4871 | default:
|
|---|
| 4872 | schedulerPriority = NormalPriority;
|
|---|
| 4873 | break;
|
|---|
| 4874 | }
|
|---|
| 4875 |
|
|---|
| 4876 | injectedHook.onCommitFiberRoot(rendererID, root, schedulerPriority, didError);
|
|---|
| 4877 | } else {
|
|---|
| 4878 | injectedHook.onCommitFiberRoot(rendererID, root, undefined, didError);
|
|---|
| 4879 | }
|
|---|
| 4880 | } catch (err) {
|
|---|
| 4881 | {
|
|---|
| 4882 | if (!hasLoggedError) {
|
|---|
| 4883 | hasLoggedError = true;
|
|---|
| 4884 |
|
|---|
| 4885 | error('React instrumentation encountered an error: %s', err);
|
|---|
| 4886 | }
|
|---|
| 4887 | }
|
|---|
| 4888 | }
|
|---|
| 4889 | }
|
|---|
| 4890 | }
|
|---|
| 4891 | function onPostCommitRoot(root) {
|
|---|
| 4892 | if (injectedHook && typeof injectedHook.onPostCommitFiberRoot === 'function') {
|
|---|
| 4893 | try {
|
|---|
| 4894 | injectedHook.onPostCommitFiberRoot(rendererID, root);
|
|---|
| 4895 | } catch (err) {
|
|---|
| 4896 | {
|
|---|
| 4897 | if (!hasLoggedError) {
|
|---|
| 4898 | hasLoggedError = true;
|
|---|
| 4899 |
|
|---|
| 4900 | error('React instrumentation encountered an error: %s', err);
|
|---|
| 4901 | }
|
|---|
| 4902 | }
|
|---|
| 4903 | }
|
|---|
| 4904 | }
|
|---|
| 4905 | }
|
|---|
| 4906 | function onCommitUnmount(fiber) {
|
|---|
| 4907 | if (injectedHook && typeof injectedHook.onCommitFiberUnmount === 'function') {
|
|---|
| 4908 | try {
|
|---|
| 4909 | injectedHook.onCommitFiberUnmount(rendererID, fiber);
|
|---|
| 4910 | } catch (err) {
|
|---|
| 4911 | {
|
|---|
| 4912 | if (!hasLoggedError) {
|
|---|
| 4913 | hasLoggedError = true;
|
|---|
| 4914 |
|
|---|
| 4915 | error('React instrumentation encountered an error: %s', err);
|
|---|
| 4916 | }
|
|---|
| 4917 | }
|
|---|
| 4918 | }
|
|---|
| 4919 | }
|
|---|
| 4920 | }
|
|---|
| 4921 | function setIsStrictModeForDevtools(newIsStrictMode) {
|
|---|
| 4922 | {
|
|---|
| 4923 | if (typeof unstable_yieldValue === 'function') {
|
|---|
| 4924 | // We're in a test because Scheduler.unstable_yieldValue only exists
|
|---|
| 4925 | // in SchedulerMock. To reduce the noise in strict mode tests,
|
|---|
| 4926 | // suppress warnings and disable scheduler yielding during the double render
|
|---|
| 4927 | unstable_setDisableYieldValue(newIsStrictMode);
|
|---|
| 4928 | setSuppressWarning(newIsStrictMode);
|
|---|
| 4929 | }
|
|---|
| 4930 |
|
|---|
| 4931 | if (injectedHook && typeof injectedHook.setStrictMode === 'function') {
|
|---|
| 4932 | try {
|
|---|
| 4933 | injectedHook.setStrictMode(rendererID, newIsStrictMode);
|
|---|
| 4934 | } catch (err) {
|
|---|
| 4935 | {
|
|---|
| 4936 | if (!hasLoggedError) {
|
|---|
| 4937 | hasLoggedError = true;
|
|---|
| 4938 |
|
|---|
| 4939 | error('React instrumentation encountered an error: %s', err);
|
|---|
| 4940 | }
|
|---|
| 4941 | }
|
|---|
| 4942 | }
|
|---|
| 4943 | }
|
|---|
| 4944 | }
|
|---|
| 4945 | } // Profiler API hooks
|
|---|
| 4946 |
|
|---|
| 4947 | function injectProfilingHooks(profilingHooks) {
|
|---|
| 4948 | injectedProfilingHooks = profilingHooks;
|
|---|
| 4949 | }
|
|---|
| 4950 |
|
|---|
| 4951 | function getLaneLabelMap() {
|
|---|
| 4952 | {
|
|---|
| 4953 | var map = new Map();
|
|---|
| 4954 | var lane = 1;
|
|---|
| 4955 |
|
|---|
| 4956 | for (var index = 0; index < TotalLanes; index++) {
|
|---|
| 4957 | var label = getLabelForLane(lane);
|
|---|
| 4958 | map.set(lane, label);
|
|---|
| 4959 | lane *= 2;
|
|---|
| 4960 | }
|
|---|
| 4961 |
|
|---|
| 4962 | return map;
|
|---|
| 4963 | }
|
|---|
| 4964 | }
|
|---|
| 4965 |
|
|---|
| 4966 | function markCommitStarted(lanes) {
|
|---|
| 4967 | {
|
|---|
| 4968 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStarted === 'function') {
|
|---|
| 4969 | injectedProfilingHooks.markCommitStarted(lanes);
|
|---|
| 4970 | }
|
|---|
| 4971 | }
|
|---|
| 4972 | }
|
|---|
| 4973 | function markCommitStopped() {
|
|---|
| 4974 | {
|
|---|
| 4975 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStopped === 'function') {
|
|---|
| 4976 | injectedProfilingHooks.markCommitStopped();
|
|---|
| 4977 | }
|
|---|
| 4978 | }
|
|---|
| 4979 | }
|
|---|
| 4980 | function markComponentRenderStarted(fiber) {
|
|---|
| 4981 | {
|
|---|
| 4982 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStarted === 'function') {
|
|---|
| 4983 | injectedProfilingHooks.markComponentRenderStarted(fiber);
|
|---|
| 4984 | }
|
|---|
| 4985 | }
|
|---|
| 4986 | }
|
|---|
| 4987 | function markComponentRenderStopped() {
|
|---|
| 4988 | {
|
|---|
| 4989 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStopped === 'function') {
|
|---|
| 4990 | injectedProfilingHooks.markComponentRenderStopped();
|
|---|
| 4991 | }
|
|---|
| 4992 | }
|
|---|
| 4993 | }
|
|---|
| 4994 | function markComponentPassiveEffectMountStarted(fiber) {
|
|---|
| 4995 | {
|
|---|
| 4996 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted === 'function') {
|
|---|
| 4997 | injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber);
|
|---|
| 4998 | }
|
|---|
| 4999 | }
|
|---|
| 5000 | }
|
|---|
| 5001 | function markComponentPassiveEffectMountStopped() {
|
|---|
| 5002 | {
|
|---|
| 5003 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped === 'function') {
|
|---|
| 5004 | injectedProfilingHooks.markComponentPassiveEffectMountStopped();
|
|---|
| 5005 | }
|
|---|
| 5006 | }
|
|---|
| 5007 | }
|
|---|
| 5008 | function markComponentPassiveEffectUnmountStarted(fiber) {
|
|---|
| 5009 | {
|
|---|
| 5010 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted === 'function') {
|
|---|
| 5011 | injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber);
|
|---|
| 5012 | }
|
|---|
| 5013 | }
|
|---|
| 5014 | }
|
|---|
| 5015 | function markComponentPassiveEffectUnmountStopped() {
|
|---|
| 5016 | {
|
|---|
| 5017 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped === 'function') {
|
|---|
| 5018 | injectedProfilingHooks.markComponentPassiveEffectUnmountStopped();
|
|---|
| 5019 | }
|
|---|
| 5020 | }
|
|---|
| 5021 | }
|
|---|
| 5022 | function markComponentLayoutEffectMountStarted(fiber) {
|
|---|
| 5023 | {
|
|---|
| 5024 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted === 'function') {
|
|---|
| 5025 | injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber);
|
|---|
| 5026 | }
|
|---|
| 5027 | }
|
|---|
| 5028 | }
|
|---|
| 5029 | function markComponentLayoutEffectMountStopped() {
|
|---|
| 5030 | {
|
|---|
| 5031 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped === 'function') {
|
|---|
| 5032 | injectedProfilingHooks.markComponentLayoutEffectMountStopped();
|
|---|
| 5033 | }
|
|---|
| 5034 | }
|
|---|
| 5035 | }
|
|---|
| 5036 | function markComponentLayoutEffectUnmountStarted(fiber) {
|
|---|
| 5037 | {
|
|---|
| 5038 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted === 'function') {
|
|---|
| 5039 | injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber);
|
|---|
| 5040 | }
|
|---|
| 5041 | }
|
|---|
| 5042 | }
|
|---|
| 5043 | function markComponentLayoutEffectUnmountStopped() {
|
|---|
| 5044 | {
|
|---|
| 5045 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped === 'function') {
|
|---|
| 5046 | injectedProfilingHooks.markComponentLayoutEffectUnmountStopped();
|
|---|
| 5047 | }
|
|---|
| 5048 | }
|
|---|
| 5049 | }
|
|---|
| 5050 | function markComponentErrored(fiber, thrownValue, lanes) {
|
|---|
| 5051 | {
|
|---|
| 5052 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentErrored === 'function') {
|
|---|
| 5053 | injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes);
|
|---|
| 5054 | }
|
|---|
| 5055 | }
|
|---|
| 5056 | }
|
|---|
| 5057 | function markComponentSuspended(fiber, wakeable, lanes) {
|
|---|
| 5058 | {
|
|---|
| 5059 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentSuspended === 'function') {
|
|---|
| 5060 | injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes);
|
|---|
| 5061 | }
|
|---|
| 5062 | }
|
|---|
| 5063 | }
|
|---|
| 5064 | function markLayoutEffectsStarted(lanes) {
|
|---|
| 5065 | {
|
|---|
| 5066 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStarted === 'function') {
|
|---|
| 5067 | injectedProfilingHooks.markLayoutEffectsStarted(lanes);
|
|---|
| 5068 | }
|
|---|
| 5069 | }
|
|---|
| 5070 | }
|
|---|
| 5071 | function markLayoutEffectsStopped() {
|
|---|
| 5072 | {
|
|---|
| 5073 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStopped === 'function') {
|
|---|
| 5074 | injectedProfilingHooks.markLayoutEffectsStopped();
|
|---|
| 5075 | }
|
|---|
| 5076 | }
|
|---|
| 5077 | }
|
|---|
| 5078 | function markPassiveEffectsStarted(lanes) {
|
|---|
| 5079 | {
|
|---|
| 5080 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStarted === 'function') {
|
|---|
| 5081 | injectedProfilingHooks.markPassiveEffectsStarted(lanes);
|
|---|
| 5082 | }
|
|---|
| 5083 | }
|
|---|
| 5084 | }
|
|---|
| 5085 | function markPassiveEffectsStopped() {
|
|---|
| 5086 | {
|
|---|
| 5087 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStopped === 'function') {
|
|---|
| 5088 | injectedProfilingHooks.markPassiveEffectsStopped();
|
|---|
| 5089 | }
|
|---|
| 5090 | }
|
|---|
| 5091 | }
|
|---|
| 5092 | function markRenderStarted(lanes) {
|
|---|
| 5093 | {
|
|---|
| 5094 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStarted === 'function') {
|
|---|
| 5095 | injectedProfilingHooks.markRenderStarted(lanes);
|
|---|
| 5096 | }
|
|---|
| 5097 | }
|
|---|
| 5098 | }
|
|---|
| 5099 | function markRenderYielded() {
|
|---|
| 5100 | {
|
|---|
| 5101 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderYielded === 'function') {
|
|---|
| 5102 | injectedProfilingHooks.markRenderYielded();
|
|---|
| 5103 | }
|
|---|
| 5104 | }
|
|---|
| 5105 | }
|
|---|
| 5106 | function markRenderStopped() {
|
|---|
| 5107 | {
|
|---|
| 5108 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStopped === 'function') {
|
|---|
| 5109 | injectedProfilingHooks.markRenderStopped();
|
|---|
| 5110 | }
|
|---|
| 5111 | }
|
|---|
| 5112 | }
|
|---|
| 5113 | function markRenderScheduled(lane) {
|
|---|
| 5114 | {
|
|---|
| 5115 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderScheduled === 'function') {
|
|---|
| 5116 | injectedProfilingHooks.markRenderScheduled(lane);
|
|---|
| 5117 | }
|
|---|
| 5118 | }
|
|---|
| 5119 | }
|
|---|
| 5120 | function markForceUpdateScheduled(fiber, lane) {
|
|---|
| 5121 | {
|
|---|
| 5122 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markForceUpdateScheduled === 'function') {
|
|---|
| 5123 | injectedProfilingHooks.markForceUpdateScheduled(fiber, lane);
|
|---|
| 5124 | }
|
|---|
| 5125 | }
|
|---|
| 5126 | }
|
|---|
| 5127 | function markStateUpdateScheduled(fiber, lane) {
|
|---|
| 5128 | {
|
|---|
| 5129 | if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markStateUpdateScheduled === 'function') {
|
|---|
| 5130 | injectedProfilingHooks.markStateUpdateScheduled(fiber, lane);
|
|---|
| 5131 | }
|
|---|
| 5132 | }
|
|---|
| 5133 | }
|
|---|
| 5134 |
|
|---|
| 5135 | var NoMode =
|
|---|
| 5136 | /* */
|
|---|
| 5137 | 0; // TODO: Remove ConcurrentMode by reading from the root tag instead
|
|---|
| 5138 |
|
|---|
| 5139 | var ConcurrentMode =
|
|---|
| 5140 | /* */
|
|---|
| 5141 | 1;
|
|---|
| 5142 | var ProfileMode =
|
|---|
| 5143 | /* */
|
|---|
| 5144 | 2;
|
|---|
| 5145 | var StrictLegacyMode =
|
|---|
| 5146 | /* */
|
|---|
| 5147 | 8;
|
|---|
| 5148 | var StrictEffectsMode =
|
|---|
| 5149 | /* */
|
|---|
| 5150 | 16;
|
|---|
| 5151 |
|
|---|
| 5152 | // TODO: This is pretty well supported by browsers. Maybe we can drop it.
|
|---|
| 5153 | var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros.
|
|---|
| 5154 | // Based on:
|
|---|
| 5155 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
|
|---|
| 5156 |
|
|---|
| 5157 | var log = Math.log;
|
|---|
| 5158 | var LN2 = Math.LN2;
|
|---|
| 5159 |
|
|---|
| 5160 | function clz32Fallback(x) {
|
|---|
| 5161 | var asUint = x >>> 0;
|
|---|
| 5162 |
|
|---|
| 5163 | if (asUint === 0) {
|
|---|
| 5164 | return 32;
|
|---|
| 5165 | }
|
|---|
| 5166 |
|
|---|
| 5167 | return 31 - (log(asUint) / LN2 | 0) | 0;
|
|---|
| 5168 | }
|
|---|
| 5169 |
|
|---|
| 5170 | // If those values are changed that package should be rebuilt and redeployed.
|
|---|
| 5171 |
|
|---|
| 5172 | var TotalLanes = 31;
|
|---|
| 5173 | var NoLanes =
|
|---|
| 5174 | /* */
|
|---|
| 5175 | 0;
|
|---|
| 5176 | var NoLane =
|
|---|
| 5177 | /* */
|
|---|
| 5178 | 0;
|
|---|
| 5179 | var SyncLane =
|
|---|
| 5180 | /* */
|
|---|
| 5181 | 1;
|
|---|
| 5182 | var InputContinuousHydrationLane =
|
|---|
| 5183 | /* */
|
|---|
| 5184 | 2;
|
|---|
| 5185 | var InputContinuousLane =
|
|---|
| 5186 | /* */
|
|---|
| 5187 | 4;
|
|---|
| 5188 | var DefaultHydrationLane =
|
|---|
| 5189 | /* */
|
|---|
| 5190 | 8;
|
|---|
| 5191 | var DefaultLane =
|
|---|
| 5192 | /* */
|
|---|
| 5193 | 16;
|
|---|
| 5194 | var TransitionHydrationLane =
|
|---|
| 5195 | /* */
|
|---|
| 5196 | 32;
|
|---|
| 5197 | var TransitionLanes =
|
|---|
| 5198 | /* */
|
|---|
| 5199 | 4194240;
|
|---|
| 5200 | var TransitionLane1 =
|
|---|
| 5201 | /* */
|
|---|
| 5202 | 64;
|
|---|
| 5203 | var TransitionLane2 =
|
|---|
| 5204 | /* */
|
|---|
| 5205 | 128;
|
|---|
| 5206 | var TransitionLane3 =
|
|---|
| 5207 | /* */
|
|---|
| 5208 | 256;
|
|---|
| 5209 | var TransitionLane4 =
|
|---|
| 5210 | /* */
|
|---|
| 5211 | 512;
|
|---|
| 5212 | var TransitionLane5 =
|
|---|
| 5213 | /* */
|
|---|
| 5214 | 1024;
|
|---|
| 5215 | var TransitionLane6 =
|
|---|
| 5216 | /* */
|
|---|
| 5217 | 2048;
|
|---|
| 5218 | var TransitionLane7 =
|
|---|
| 5219 | /* */
|
|---|
| 5220 | 4096;
|
|---|
| 5221 | var TransitionLane8 =
|
|---|
| 5222 | /* */
|
|---|
| 5223 | 8192;
|
|---|
| 5224 | var TransitionLane9 =
|
|---|
| 5225 | /* */
|
|---|
| 5226 | 16384;
|
|---|
| 5227 | var TransitionLane10 =
|
|---|
| 5228 | /* */
|
|---|
| 5229 | 32768;
|
|---|
| 5230 | var TransitionLane11 =
|
|---|
| 5231 | /* */
|
|---|
| 5232 | 65536;
|
|---|
| 5233 | var TransitionLane12 =
|
|---|
| 5234 | /* */
|
|---|
| 5235 | 131072;
|
|---|
| 5236 | var TransitionLane13 =
|
|---|
| 5237 | /* */
|
|---|
| 5238 | 262144;
|
|---|
| 5239 | var TransitionLane14 =
|
|---|
| 5240 | /* */
|
|---|
| 5241 | 524288;
|
|---|
| 5242 | var TransitionLane15 =
|
|---|
| 5243 | /* */
|
|---|
| 5244 | 1048576;
|
|---|
| 5245 | var TransitionLane16 =
|
|---|
| 5246 | /* */
|
|---|
| 5247 | 2097152;
|
|---|
| 5248 | var RetryLanes =
|
|---|
| 5249 | /* */
|
|---|
| 5250 | 130023424;
|
|---|
| 5251 | var RetryLane1 =
|
|---|
| 5252 | /* */
|
|---|
| 5253 | 4194304;
|
|---|
| 5254 | var RetryLane2 =
|
|---|
| 5255 | /* */
|
|---|
| 5256 | 8388608;
|
|---|
| 5257 | var RetryLane3 =
|
|---|
| 5258 | /* */
|
|---|
| 5259 | 16777216;
|
|---|
| 5260 | var RetryLane4 =
|
|---|
| 5261 | /* */
|
|---|
| 5262 | 33554432;
|
|---|
| 5263 | var RetryLane5 =
|
|---|
| 5264 | /* */
|
|---|
| 5265 | 67108864;
|
|---|
| 5266 | var SomeRetryLane = RetryLane1;
|
|---|
| 5267 | var SelectiveHydrationLane =
|
|---|
| 5268 | /* */
|
|---|
| 5269 | 134217728;
|
|---|
| 5270 | var NonIdleLanes =
|
|---|
| 5271 | /* */
|
|---|
| 5272 | 268435455;
|
|---|
| 5273 | var IdleHydrationLane =
|
|---|
| 5274 | /* */
|
|---|
| 5275 | 268435456;
|
|---|
| 5276 | var IdleLane =
|
|---|
| 5277 | /* */
|
|---|
| 5278 | 536870912;
|
|---|
| 5279 | var OffscreenLane =
|
|---|
| 5280 | /* */
|
|---|
| 5281 | 1073741824; // This function is used for the experimental timeline (react-devtools-timeline)
|
|---|
| 5282 | // It should be kept in sync with the Lanes values above.
|
|---|
| 5283 |
|
|---|
| 5284 | function getLabelForLane(lane) {
|
|---|
| 5285 | {
|
|---|
| 5286 | if (lane & SyncLane) {
|
|---|
| 5287 | return 'Sync';
|
|---|
| 5288 | }
|
|---|
| 5289 |
|
|---|
| 5290 | if (lane & InputContinuousHydrationLane) {
|
|---|
| 5291 | return 'InputContinuousHydration';
|
|---|
| 5292 | }
|
|---|
| 5293 |
|
|---|
| 5294 | if (lane & InputContinuousLane) {
|
|---|
| 5295 | return 'InputContinuous';
|
|---|
| 5296 | }
|
|---|
| 5297 |
|
|---|
| 5298 | if (lane & DefaultHydrationLane) {
|
|---|
| 5299 | return 'DefaultHydration';
|
|---|
| 5300 | }
|
|---|
| 5301 |
|
|---|
| 5302 | if (lane & DefaultLane) {
|
|---|
| 5303 | return 'Default';
|
|---|
| 5304 | }
|
|---|
| 5305 |
|
|---|
| 5306 | if (lane & TransitionHydrationLane) {
|
|---|
| 5307 | return 'TransitionHydration';
|
|---|
| 5308 | }
|
|---|
| 5309 |
|
|---|
| 5310 | if (lane & TransitionLanes) {
|
|---|
| 5311 | return 'Transition';
|
|---|
| 5312 | }
|
|---|
| 5313 |
|
|---|
| 5314 | if (lane & RetryLanes) {
|
|---|
| 5315 | return 'Retry';
|
|---|
| 5316 | }
|
|---|
| 5317 |
|
|---|
| 5318 | if (lane & SelectiveHydrationLane) {
|
|---|
| 5319 | return 'SelectiveHydration';
|
|---|
| 5320 | }
|
|---|
| 5321 |
|
|---|
| 5322 | if (lane & IdleHydrationLane) {
|
|---|
| 5323 | return 'IdleHydration';
|
|---|
| 5324 | }
|
|---|
| 5325 |
|
|---|
| 5326 | if (lane & IdleLane) {
|
|---|
| 5327 | return 'Idle';
|
|---|
| 5328 | }
|
|---|
| 5329 |
|
|---|
| 5330 | if (lane & OffscreenLane) {
|
|---|
| 5331 | return 'Offscreen';
|
|---|
| 5332 | }
|
|---|
| 5333 | }
|
|---|
| 5334 | }
|
|---|
| 5335 | var NoTimestamp = -1;
|
|---|
| 5336 | var nextTransitionLane = TransitionLane1;
|
|---|
| 5337 | var nextRetryLane = RetryLane1;
|
|---|
| 5338 |
|
|---|
| 5339 | function getHighestPriorityLanes(lanes) {
|
|---|
| 5340 | switch (getHighestPriorityLane(lanes)) {
|
|---|
| 5341 | case SyncLane:
|
|---|
| 5342 | return SyncLane;
|
|---|
| 5343 |
|
|---|
| 5344 | case InputContinuousHydrationLane:
|
|---|
| 5345 | return InputContinuousHydrationLane;
|
|---|
| 5346 |
|
|---|
| 5347 | case InputContinuousLane:
|
|---|
| 5348 | return InputContinuousLane;
|
|---|
| 5349 |
|
|---|
| 5350 | case DefaultHydrationLane:
|
|---|
| 5351 | return DefaultHydrationLane;
|
|---|
| 5352 |
|
|---|
| 5353 | case DefaultLane:
|
|---|
| 5354 | return DefaultLane;
|
|---|
| 5355 |
|
|---|
| 5356 | case TransitionHydrationLane:
|
|---|
| 5357 | return TransitionHydrationLane;
|
|---|
| 5358 |
|
|---|
| 5359 | case TransitionLane1:
|
|---|
| 5360 | case TransitionLane2:
|
|---|
| 5361 | case TransitionLane3:
|
|---|
| 5362 | case TransitionLane4:
|
|---|
| 5363 | case TransitionLane5:
|
|---|
| 5364 | case TransitionLane6:
|
|---|
| 5365 | case TransitionLane7:
|
|---|
| 5366 | case TransitionLane8:
|
|---|
| 5367 | case TransitionLane9:
|
|---|
| 5368 | case TransitionLane10:
|
|---|
| 5369 | case TransitionLane11:
|
|---|
| 5370 | case TransitionLane12:
|
|---|
| 5371 | case TransitionLane13:
|
|---|
| 5372 | case TransitionLane14:
|
|---|
| 5373 | case TransitionLane15:
|
|---|
| 5374 | case TransitionLane16:
|
|---|
| 5375 | return lanes & TransitionLanes;
|
|---|
| 5376 |
|
|---|
| 5377 | case RetryLane1:
|
|---|
| 5378 | case RetryLane2:
|
|---|
| 5379 | case RetryLane3:
|
|---|
| 5380 | case RetryLane4:
|
|---|
| 5381 | case RetryLane5:
|
|---|
| 5382 | return lanes & RetryLanes;
|
|---|
| 5383 |
|
|---|
| 5384 | case SelectiveHydrationLane:
|
|---|
| 5385 | return SelectiveHydrationLane;
|
|---|
| 5386 |
|
|---|
| 5387 | case IdleHydrationLane:
|
|---|
| 5388 | return IdleHydrationLane;
|
|---|
| 5389 |
|
|---|
| 5390 | case IdleLane:
|
|---|
| 5391 | return IdleLane;
|
|---|
| 5392 |
|
|---|
| 5393 | case OffscreenLane:
|
|---|
| 5394 | return OffscreenLane;
|
|---|
| 5395 |
|
|---|
| 5396 | default:
|
|---|
| 5397 | {
|
|---|
| 5398 | error('Should have found matching lanes. This is a bug in React.');
|
|---|
| 5399 | } // This shouldn't be reachable, but as a fallback, return the entire bitmask.
|
|---|
| 5400 |
|
|---|
| 5401 |
|
|---|
| 5402 | return lanes;
|
|---|
| 5403 | }
|
|---|
| 5404 | }
|
|---|
| 5405 |
|
|---|
| 5406 | function getNextLanes(root, wipLanes) {
|
|---|
| 5407 | // Early bailout if there's no pending work left.
|
|---|
| 5408 | var pendingLanes = root.pendingLanes;
|
|---|
| 5409 |
|
|---|
| 5410 | if (pendingLanes === NoLanes) {
|
|---|
| 5411 | return NoLanes;
|
|---|
| 5412 | }
|
|---|
| 5413 |
|
|---|
| 5414 | var nextLanes = NoLanes;
|
|---|
| 5415 | var suspendedLanes = root.suspendedLanes;
|
|---|
| 5416 | var pingedLanes = root.pingedLanes; // Do not work on any idle work until all the non-idle work has finished,
|
|---|
| 5417 | // even if the work is suspended.
|
|---|
| 5418 |
|
|---|
| 5419 | var nonIdlePendingLanes = pendingLanes & NonIdleLanes;
|
|---|
| 5420 |
|
|---|
| 5421 | if (nonIdlePendingLanes !== NoLanes) {
|
|---|
| 5422 | var nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
|
|---|
| 5423 |
|
|---|
| 5424 | if (nonIdleUnblockedLanes !== NoLanes) {
|
|---|
| 5425 | nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
|
|---|
| 5426 | } else {
|
|---|
| 5427 | var nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
|
|---|
| 5428 |
|
|---|
| 5429 | if (nonIdlePingedLanes !== NoLanes) {
|
|---|
| 5430 | nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
|
|---|
| 5431 | }
|
|---|
| 5432 | }
|
|---|
| 5433 | } else {
|
|---|
| 5434 | // The only remaining work is Idle.
|
|---|
| 5435 | var unblockedLanes = pendingLanes & ~suspendedLanes;
|
|---|
| 5436 |
|
|---|
| 5437 | if (unblockedLanes !== NoLanes) {
|
|---|
| 5438 | nextLanes = getHighestPriorityLanes(unblockedLanes);
|
|---|
| 5439 | } else {
|
|---|
| 5440 | if (pingedLanes !== NoLanes) {
|
|---|
| 5441 | nextLanes = getHighestPriorityLanes(pingedLanes);
|
|---|
| 5442 | }
|
|---|
| 5443 | }
|
|---|
| 5444 | }
|
|---|
| 5445 |
|
|---|
| 5446 | if (nextLanes === NoLanes) {
|
|---|
| 5447 | // This should only be reachable if we're suspended
|
|---|
| 5448 | // TODO: Consider warning in this path if a fallback timer is not scheduled.
|
|---|
| 5449 | return NoLanes;
|
|---|
| 5450 | } // If we're already in the middle of a render, switching lanes will interrupt
|
|---|
| 5451 | // it and we'll lose our progress. We should only do this if the new lanes are
|
|---|
| 5452 | // higher priority.
|
|---|
| 5453 |
|
|---|
| 5454 |
|
|---|
| 5455 | if (wipLanes !== NoLanes && wipLanes !== nextLanes && // If we already suspended with a delay, then interrupting is fine. Don't
|
|---|
| 5456 | // bother waiting until the root is complete.
|
|---|
| 5457 | (wipLanes & suspendedLanes) === NoLanes) {
|
|---|
| 5458 | var nextLane = getHighestPriorityLane(nextLanes);
|
|---|
| 5459 | var wipLane = getHighestPriorityLane(wipLanes);
|
|---|
| 5460 |
|
|---|
| 5461 | if ( // Tests whether the next lane is equal or lower priority than the wip
|
|---|
| 5462 | // one. This works because the bits decrease in priority as you go left.
|
|---|
| 5463 | nextLane >= wipLane || // Default priority updates should not interrupt transition updates. The
|
|---|
| 5464 | // only difference between default updates and transition updates is that
|
|---|
| 5465 | // default updates do not support refresh transitions.
|
|---|
| 5466 | nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes) {
|
|---|
| 5467 | // Keep working on the existing in-progress tree. Do not interrupt.
|
|---|
| 5468 | return wipLanes;
|
|---|
| 5469 | }
|
|---|
| 5470 | }
|
|---|
| 5471 |
|
|---|
| 5472 | if ((nextLanes & InputContinuousLane) !== NoLanes) {
|
|---|
| 5473 | // When updates are sync by default, we entangle continuous priority updates
|
|---|
| 5474 | // and default updates, so they render in the same batch. The only reason
|
|---|
| 5475 | // they use separate lanes is because continuous updates should interrupt
|
|---|
| 5476 | // transitions, but default updates should not.
|
|---|
| 5477 | nextLanes |= pendingLanes & DefaultLane;
|
|---|
| 5478 | } // Check for entangled lanes and add them to the batch.
|
|---|
| 5479 | //
|
|---|
| 5480 | // A lane is said to be entangled with another when it's not allowed to render
|
|---|
| 5481 | // in a batch that does not also include the other lane. Typically we do this
|
|---|
| 5482 | // when multiple updates have the same source, and we only want to respond to
|
|---|
| 5483 | // the most recent event from that source.
|
|---|
| 5484 | //
|
|---|
| 5485 | // Note that we apply entanglements *after* checking for partial work above.
|
|---|
| 5486 | // This means that if a lane is entangled during an interleaved event while
|
|---|
| 5487 | // it's already rendering, we won't interrupt it. This is intentional, since
|
|---|
| 5488 | // entanglement is usually "best effort": we'll try our best to render the
|
|---|
| 5489 | // lanes in the same batch, but it's not worth throwing out partially
|
|---|
| 5490 | // completed work in order to do it.
|
|---|
| 5491 | // TODO: Reconsider this. The counter-argument is that the partial work
|
|---|
| 5492 | // represents an intermediate state, which we don't want to show to the user.
|
|---|
| 5493 | // And by spending extra time finishing it, we're increasing the amount of
|
|---|
| 5494 | // time it takes to show the final state, which is what they are actually
|
|---|
| 5495 | // waiting for.
|
|---|
| 5496 | //
|
|---|
| 5497 | // For those exceptions where entanglement is semantically important, like
|
|---|
| 5498 | // useMutableSource, we should ensure that there is no partial work at the
|
|---|
| 5499 | // time we apply the entanglement.
|
|---|
| 5500 |
|
|---|
| 5501 |
|
|---|
| 5502 | var entangledLanes = root.entangledLanes;
|
|---|
| 5503 |
|
|---|
| 5504 | if (entangledLanes !== NoLanes) {
|
|---|
| 5505 | var entanglements = root.entanglements;
|
|---|
| 5506 | var lanes = nextLanes & entangledLanes;
|
|---|
| 5507 |
|
|---|
| 5508 | while (lanes > 0) {
|
|---|
| 5509 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5510 | var lane = 1 << index;
|
|---|
| 5511 | nextLanes |= entanglements[index];
|
|---|
| 5512 | lanes &= ~lane;
|
|---|
| 5513 | }
|
|---|
| 5514 | }
|
|---|
| 5515 |
|
|---|
| 5516 | return nextLanes;
|
|---|
| 5517 | }
|
|---|
| 5518 | function getMostRecentEventTime(root, lanes) {
|
|---|
| 5519 | var eventTimes = root.eventTimes;
|
|---|
| 5520 | var mostRecentEventTime = NoTimestamp;
|
|---|
| 5521 |
|
|---|
| 5522 | while (lanes > 0) {
|
|---|
| 5523 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5524 | var lane = 1 << index;
|
|---|
| 5525 | var eventTime = eventTimes[index];
|
|---|
| 5526 |
|
|---|
| 5527 | if (eventTime > mostRecentEventTime) {
|
|---|
| 5528 | mostRecentEventTime = eventTime;
|
|---|
| 5529 | }
|
|---|
| 5530 |
|
|---|
| 5531 | lanes &= ~lane;
|
|---|
| 5532 | }
|
|---|
| 5533 |
|
|---|
| 5534 | return mostRecentEventTime;
|
|---|
| 5535 | }
|
|---|
| 5536 |
|
|---|
| 5537 | function computeExpirationTime(lane, currentTime) {
|
|---|
| 5538 | switch (lane) {
|
|---|
| 5539 | case SyncLane:
|
|---|
| 5540 | case InputContinuousHydrationLane:
|
|---|
| 5541 | case InputContinuousLane:
|
|---|
| 5542 | // User interactions should expire slightly more quickly.
|
|---|
| 5543 | //
|
|---|
| 5544 | // NOTE: This is set to the corresponding constant as in Scheduler.js.
|
|---|
| 5545 | // When we made it larger, a product metric in www regressed, suggesting
|
|---|
| 5546 | // there's a user interaction that's being starved by a series of
|
|---|
| 5547 | // synchronous updates. If that theory is correct, the proper solution is
|
|---|
| 5548 | // to fix the starvation. However, this scenario supports the idea that
|
|---|
| 5549 | // expiration times are an important safeguard when starvation
|
|---|
| 5550 | // does happen.
|
|---|
| 5551 | return currentTime + 250;
|
|---|
| 5552 |
|
|---|
| 5553 | case DefaultHydrationLane:
|
|---|
| 5554 | case DefaultLane:
|
|---|
| 5555 | case TransitionHydrationLane:
|
|---|
| 5556 | case TransitionLane1:
|
|---|
| 5557 | case TransitionLane2:
|
|---|
| 5558 | case TransitionLane3:
|
|---|
| 5559 | case TransitionLane4:
|
|---|
| 5560 | case TransitionLane5:
|
|---|
| 5561 | case TransitionLane6:
|
|---|
| 5562 | case TransitionLane7:
|
|---|
| 5563 | case TransitionLane8:
|
|---|
| 5564 | case TransitionLane9:
|
|---|
| 5565 | case TransitionLane10:
|
|---|
| 5566 | case TransitionLane11:
|
|---|
| 5567 | case TransitionLane12:
|
|---|
| 5568 | case TransitionLane13:
|
|---|
| 5569 | case TransitionLane14:
|
|---|
| 5570 | case TransitionLane15:
|
|---|
| 5571 | case TransitionLane16:
|
|---|
| 5572 | return currentTime + 5000;
|
|---|
| 5573 |
|
|---|
| 5574 | case RetryLane1:
|
|---|
| 5575 | case RetryLane2:
|
|---|
| 5576 | case RetryLane3:
|
|---|
| 5577 | case RetryLane4:
|
|---|
| 5578 | case RetryLane5:
|
|---|
| 5579 | // TODO: Retries should be allowed to expire if they are CPU bound for
|
|---|
| 5580 | // too long, but when I made this change it caused a spike in browser
|
|---|
| 5581 | // crashes. There must be some other underlying bug; not super urgent but
|
|---|
| 5582 | // ideally should figure out why and fix it. Unfortunately we don't have
|
|---|
| 5583 | // a repro for the crashes, only detected via production metrics.
|
|---|
| 5584 | return NoTimestamp;
|
|---|
| 5585 |
|
|---|
| 5586 | case SelectiveHydrationLane:
|
|---|
| 5587 | case IdleHydrationLane:
|
|---|
| 5588 | case IdleLane:
|
|---|
| 5589 | case OffscreenLane:
|
|---|
| 5590 | // Anything idle priority or lower should never expire.
|
|---|
| 5591 | return NoTimestamp;
|
|---|
| 5592 |
|
|---|
| 5593 | default:
|
|---|
| 5594 | {
|
|---|
| 5595 | error('Should have found matching lanes. This is a bug in React.');
|
|---|
| 5596 | }
|
|---|
| 5597 |
|
|---|
| 5598 | return NoTimestamp;
|
|---|
| 5599 | }
|
|---|
| 5600 | }
|
|---|
| 5601 |
|
|---|
| 5602 | function markStarvedLanesAsExpired(root, currentTime) {
|
|---|
| 5603 | // TODO: This gets called every time we yield. We can optimize by storing
|
|---|
| 5604 | // the earliest expiration time on the root. Then use that to quickly bail out
|
|---|
| 5605 | // of this function.
|
|---|
| 5606 | var pendingLanes = root.pendingLanes;
|
|---|
| 5607 | var suspendedLanes = root.suspendedLanes;
|
|---|
| 5608 | var pingedLanes = root.pingedLanes;
|
|---|
| 5609 | var expirationTimes = root.expirationTimes; // Iterate through the pending lanes and check if we've reached their
|
|---|
| 5610 | // expiration time. If so, we'll assume the update is being starved and mark
|
|---|
| 5611 | // it as expired to force it to finish.
|
|---|
| 5612 |
|
|---|
| 5613 | var lanes = pendingLanes;
|
|---|
| 5614 |
|
|---|
| 5615 | while (lanes > 0) {
|
|---|
| 5616 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5617 | var lane = 1 << index;
|
|---|
| 5618 | var expirationTime = expirationTimes[index];
|
|---|
| 5619 |
|
|---|
| 5620 | if (expirationTime === NoTimestamp) {
|
|---|
| 5621 | // Found a pending lane with no expiration time. If it's not suspended, or
|
|---|
| 5622 | // if it's pinged, assume it's CPU-bound. Compute a new expiration time
|
|---|
| 5623 | // using the current time.
|
|---|
| 5624 | if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) {
|
|---|
| 5625 | // Assumes timestamps are monotonically increasing.
|
|---|
| 5626 | expirationTimes[index] = computeExpirationTime(lane, currentTime);
|
|---|
| 5627 | }
|
|---|
| 5628 | } else if (expirationTime <= currentTime) {
|
|---|
| 5629 | // This lane expired
|
|---|
| 5630 | root.expiredLanes |= lane;
|
|---|
| 5631 | }
|
|---|
| 5632 |
|
|---|
| 5633 | lanes &= ~lane;
|
|---|
| 5634 | }
|
|---|
| 5635 | } // This returns the highest priority pending lanes regardless of whether they
|
|---|
| 5636 | // are suspended.
|
|---|
| 5637 |
|
|---|
| 5638 | function getHighestPriorityPendingLanes(root) {
|
|---|
| 5639 | return getHighestPriorityLanes(root.pendingLanes);
|
|---|
| 5640 | }
|
|---|
| 5641 | function getLanesToRetrySynchronouslyOnError(root) {
|
|---|
| 5642 | var everythingButOffscreen = root.pendingLanes & ~OffscreenLane;
|
|---|
| 5643 |
|
|---|
| 5644 | if (everythingButOffscreen !== NoLanes) {
|
|---|
| 5645 | return everythingButOffscreen;
|
|---|
| 5646 | }
|
|---|
| 5647 |
|
|---|
| 5648 | if (everythingButOffscreen & OffscreenLane) {
|
|---|
| 5649 | return OffscreenLane;
|
|---|
| 5650 | }
|
|---|
| 5651 |
|
|---|
| 5652 | return NoLanes;
|
|---|
| 5653 | }
|
|---|
| 5654 | function includesSyncLane(lanes) {
|
|---|
| 5655 | return (lanes & SyncLane) !== NoLanes;
|
|---|
| 5656 | }
|
|---|
| 5657 | function includesNonIdleWork(lanes) {
|
|---|
| 5658 | return (lanes & NonIdleLanes) !== NoLanes;
|
|---|
| 5659 | }
|
|---|
| 5660 | function includesOnlyRetries(lanes) {
|
|---|
| 5661 | return (lanes & RetryLanes) === lanes;
|
|---|
| 5662 | }
|
|---|
| 5663 | function includesOnlyNonUrgentLanes(lanes) {
|
|---|
| 5664 | var UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
|
|---|
| 5665 | return (lanes & UrgentLanes) === NoLanes;
|
|---|
| 5666 | }
|
|---|
| 5667 | function includesOnlyTransitions(lanes) {
|
|---|
| 5668 | return (lanes & TransitionLanes) === lanes;
|
|---|
| 5669 | }
|
|---|
| 5670 | function includesBlockingLane(root, lanes) {
|
|---|
| 5671 |
|
|---|
| 5672 | var SyncDefaultLanes = InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | DefaultLane;
|
|---|
| 5673 | return (lanes & SyncDefaultLanes) !== NoLanes;
|
|---|
| 5674 | }
|
|---|
| 5675 | function includesExpiredLane(root, lanes) {
|
|---|
| 5676 | // This is a separate check from includesBlockingLane because a lane can
|
|---|
| 5677 | // expire after a render has already started.
|
|---|
| 5678 | return (lanes & root.expiredLanes) !== NoLanes;
|
|---|
| 5679 | }
|
|---|
| 5680 | function isTransitionLane(lane) {
|
|---|
| 5681 | return (lane & TransitionLanes) !== NoLanes;
|
|---|
| 5682 | }
|
|---|
| 5683 | function claimNextTransitionLane() {
|
|---|
| 5684 | // Cycle through the lanes, assigning each new transition to the next lane.
|
|---|
| 5685 | // In most cases, this means every transition gets its own lane, until we
|
|---|
| 5686 | // run out of lanes and cycle back to the beginning.
|
|---|
| 5687 | var lane = nextTransitionLane;
|
|---|
| 5688 | nextTransitionLane <<= 1;
|
|---|
| 5689 |
|
|---|
| 5690 | if ((nextTransitionLane & TransitionLanes) === NoLanes) {
|
|---|
| 5691 | nextTransitionLane = TransitionLane1;
|
|---|
| 5692 | }
|
|---|
| 5693 |
|
|---|
| 5694 | return lane;
|
|---|
| 5695 | }
|
|---|
| 5696 | function claimNextRetryLane() {
|
|---|
| 5697 | var lane = nextRetryLane;
|
|---|
| 5698 | nextRetryLane <<= 1;
|
|---|
| 5699 |
|
|---|
| 5700 | if ((nextRetryLane & RetryLanes) === NoLanes) {
|
|---|
| 5701 | nextRetryLane = RetryLane1;
|
|---|
| 5702 | }
|
|---|
| 5703 |
|
|---|
| 5704 | return lane;
|
|---|
| 5705 | }
|
|---|
| 5706 | function getHighestPriorityLane(lanes) {
|
|---|
| 5707 | return lanes & -lanes;
|
|---|
| 5708 | }
|
|---|
| 5709 | function pickArbitraryLane(lanes) {
|
|---|
| 5710 | // This wrapper function gets inlined. Only exists so to communicate that it
|
|---|
| 5711 | // doesn't matter which bit is selected; you can pick any bit without
|
|---|
| 5712 | // affecting the algorithms where its used. Here I'm using
|
|---|
| 5713 | // getHighestPriorityLane because it requires the fewest operations.
|
|---|
| 5714 | return getHighestPriorityLane(lanes);
|
|---|
| 5715 | }
|
|---|
| 5716 |
|
|---|
| 5717 | function pickArbitraryLaneIndex(lanes) {
|
|---|
| 5718 | return 31 - clz32(lanes);
|
|---|
| 5719 | }
|
|---|
| 5720 |
|
|---|
| 5721 | function laneToIndex(lane) {
|
|---|
| 5722 | return pickArbitraryLaneIndex(lane);
|
|---|
| 5723 | }
|
|---|
| 5724 |
|
|---|
| 5725 | function includesSomeLane(a, b) {
|
|---|
| 5726 | return (a & b) !== NoLanes;
|
|---|
| 5727 | }
|
|---|
| 5728 | function isSubsetOfLanes(set, subset) {
|
|---|
| 5729 | return (set & subset) === subset;
|
|---|
| 5730 | }
|
|---|
| 5731 | function mergeLanes(a, b) {
|
|---|
| 5732 | return a | b;
|
|---|
| 5733 | }
|
|---|
| 5734 | function removeLanes(set, subset) {
|
|---|
| 5735 | return set & ~subset;
|
|---|
| 5736 | }
|
|---|
| 5737 | function intersectLanes(a, b) {
|
|---|
| 5738 | return a & b;
|
|---|
| 5739 | } // Seems redundant, but it changes the type from a single lane (used for
|
|---|
| 5740 | // updates) to a group of lanes (used for flushing work).
|
|---|
| 5741 |
|
|---|
| 5742 | function laneToLanes(lane) {
|
|---|
| 5743 | return lane;
|
|---|
| 5744 | }
|
|---|
| 5745 | function higherPriorityLane(a, b) {
|
|---|
| 5746 | // This works because the bit ranges decrease in priority as you go left.
|
|---|
| 5747 | return a !== NoLane && a < b ? a : b;
|
|---|
| 5748 | }
|
|---|
| 5749 | function createLaneMap(initial) {
|
|---|
| 5750 | // Intentionally pushing one by one.
|
|---|
| 5751 | // https://v8.dev/blog/elements-kinds#avoid-creating-holes
|
|---|
| 5752 | var laneMap = [];
|
|---|
| 5753 |
|
|---|
| 5754 | for (var i = 0; i < TotalLanes; i++) {
|
|---|
| 5755 | laneMap.push(initial);
|
|---|
| 5756 | }
|
|---|
| 5757 |
|
|---|
| 5758 | return laneMap;
|
|---|
| 5759 | }
|
|---|
| 5760 | function markRootUpdated(root, updateLane, eventTime) {
|
|---|
| 5761 | root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update
|
|---|
| 5762 | // could unblock them. Clear the suspended lanes so that we can try rendering
|
|---|
| 5763 | // them again.
|
|---|
| 5764 | //
|
|---|
| 5765 | // TODO: We really only need to unsuspend only lanes that are in the
|
|---|
| 5766 | // `subtreeLanes` of the updated fiber, or the update lanes of the return
|
|---|
| 5767 | // path. This would exclude suspended updates in an unrelated sibling tree,
|
|---|
| 5768 | // since there's no way for this update to unblock it.
|
|---|
| 5769 | //
|
|---|
| 5770 | // We don't do this if the incoming update is idle, because we never process
|
|---|
| 5771 | // idle updates until after all the regular updates have finished; there's no
|
|---|
| 5772 | // way it could unblock a transition.
|
|---|
| 5773 |
|
|---|
| 5774 | if (updateLane !== IdleLane) {
|
|---|
| 5775 | root.suspendedLanes = NoLanes;
|
|---|
| 5776 | root.pingedLanes = NoLanes;
|
|---|
| 5777 | }
|
|---|
| 5778 |
|
|---|
| 5779 | var eventTimes = root.eventTimes;
|
|---|
| 5780 | var index = laneToIndex(updateLane); // We can always overwrite an existing timestamp because we prefer the most
|
|---|
| 5781 | // recent event, and we assume time is monotonically increasing.
|
|---|
| 5782 |
|
|---|
| 5783 | eventTimes[index] = eventTime;
|
|---|
| 5784 | }
|
|---|
| 5785 | function markRootSuspended(root, suspendedLanes) {
|
|---|
| 5786 | root.suspendedLanes |= suspendedLanes;
|
|---|
| 5787 | root.pingedLanes &= ~suspendedLanes; // The suspended lanes are no longer CPU-bound. Clear their expiration times.
|
|---|
| 5788 |
|
|---|
| 5789 | var expirationTimes = root.expirationTimes;
|
|---|
| 5790 | var lanes = suspendedLanes;
|
|---|
| 5791 |
|
|---|
| 5792 | while (lanes > 0) {
|
|---|
| 5793 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5794 | var lane = 1 << index;
|
|---|
| 5795 | expirationTimes[index] = NoTimestamp;
|
|---|
| 5796 | lanes &= ~lane;
|
|---|
| 5797 | }
|
|---|
| 5798 | }
|
|---|
| 5799 | function markRootPinged(root, pingedLanes, eventTime) {
|
|---|
| 5800 | root.pingedLanes |= root.suspendedLanes & pingedLanes;
|
|---|
| 5801 | }
|
|---|
| 5802 | function markRootFinished(root, remainingLanes) {
|
|---|
| 5803 | var noLongerPendingLanes = root.pendingLanes & ~remainingLanes;
|
|---|
| 5804 | root.pendingLanes = remainingLanes; // Let's try everything again
|
|---|
| 5805 |
|
|---|
| 5806 | root.suspendedLanes = NoLanes;
|
|---|
| 5807 | root.pingedLanes = NoLanes;
|
|---|
| 5808 | root.expiredLanes &= remainingLanes;
|
|---|
| 5809 | root.mutableReadLanes &= remainingLanes;
|
|---|
| 5810 | root.entangledLanes &= remainingLanes;
|
|---|
| 5811 | var entanglements = root.entanglements;
|
|---|
| 5812 | var eventTimes = root.eventTimes;
|
|---|
| 5813 | var expirationTimes = root.expirationTimes; // Clear the lanes that no longer have pending work
|
|---|
| 5814 |
|
|---|
| 5815 | var lanes = noLongerPendingLanes;
|
|---|
| 5816 |
|
|---|
| 5817 | while (lanes > 0) {
|
|---|
| 5818 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5819 | var lane = 1 << index;
|
|---|
| 5820 | entanglements[index] = NoLanes;
|
|---|
| 5821 | eventTimes[index] = NoTimestamp;
|
|---|
| 5822 | expirationTimes[index] = NoTimestamp;
|
|---|
| 5823 | lanes &= ~lane;
|
|---|
| 5824 | }
|
|---|
| 5825 | }
|
|---|
| 5826 | function markRootEntangled(root, entangledLanes) {
|
|---|
| 5827 | // In addition to entangling each of the given lanes with each other, we also
|
|---|
| 5828 | // have to consider _transitive_ entanglements. For each lane that is already
|
|---|
| 5829 | // entangled with *any* of the given lanes, that lane is now transitively
|
|---|
| 5830 | // entangled with *all* the given lanes.
|
|---|
| 5831 | //
|
|---|
| 5832 | // Translated: If C is entangled with A, then entangling A with B also
|
|---|
| 5833 | // entangles C with B.
|
|---|
| 5834 | //
|
|---|
| 5835 | // If this is hard to grasp, it might help to intentionally break this
|
|---|
| 5836 | // function and look at the tests that fail in ReactTransition-test.js. Try
|
|---|
| 5837 | // commenting out one of the conditions below.
|
|---|
| 5838 | var rootEntangledLanes = root.entangledLanes |= entangledLanes;
|
|---|
| 5839 | var entanglements = root.entanglements;
|
|---|
| 5840 | var lanes = rootEntangledLanes;
|
|---|
| 5841 |
|
|---|
| 5842 | while (lanes) {
|
|---|
| 5843 | var index = pickArbitraryLaneIndex(lanes);
|
|---|
| 5844 | var lane = 1 << index;
|
|---|
| 5845 |
|
|---|
| 5846 | if ( // Is this one of the newly entangled lanes?
|
|---|
| 5847 | lane & entangledLanes | // Is this lane transitively entangled with the newly entangled lanes?
|
|---|
| 5848 | entanglements[index] & entangledLanes) {
|
|---|
| 5849 | entanglements[index] |= entangledLanes;
|
|---|
| 5850 | }
|
|---|
| 5851 |
|
|---|
| 5852 | lanes &= ~lane;
|
|---|
| 5853 | }
|
|---|
| 5854 | }
|
|---|
| 5855 | function getBumpedLaneForHydration(root, renderLanes) {
|
|---|
| 5856 | var renderLane = getHighestPriorityLane(renderLanes);
|
|---|
| 5857 | var lane;
|
|---|
| 5858 |
|
|---|
| 5859 | switch (renderLane) {
|
|---|
| 5860 | case InputContinuousLane:
|
|---|
| 5861 | lane = InputContinuousHydrationLane;
|
|---|
| 5862 | break;
|
|---|
| 5863 |
|
|---|
| 5864 | case DefaultLane:
|
|---|
| 5865 | lane = DefaultHydrationLane;
|
|---|
| 5866 | break;
|
|---|
| 5867 |
|
|---|
| 5868 | case TransitionLane1:
|
|---|
| 5869 | case TransitionLane2:
|
|---|
| 5870 | case TransitionLane3:
|
|---|
| 5871 | case TransitionLane4:
|
|---|
| 5872 | case TransitionLane5:
|
|---|
| 5873 | case TransitionLane6:
|
|---|
| 5874 | case TransitionLane7:
|
|---|
| 5875 | case TransitionLane8:
|
|---|
| 5876 | case TransitionLane9:
|
|---|
| 5877 | case TransitionLane10:
|
|---|
| 5878 | case TransitionLane11:
|
|---|
| 5879 | case TransitionLane12:
|
|---|
| 5880 | case TransitionLane13:
|
|---|
| 5881 | case TransitionLane14:
|
|---|
| 5882 | case TransitionLane15:
|
|---|
| 5883 | case TransitionLane16:
|
|---|
| 5884 | case RetryLane1:
|
|---|
| 5885 | case RetryLane2:
|
|---|
| 5886 | case RetryLane3:
|
|---|
| 5887 | case RetryLane4:
|
|---|
| 5888 | case RetryLane5:
|
|---|
| 5889 | lane = TransitionHydrationLane;
|
|---|
| 5890 | break;
|
|---|
| 5891 |
|
|---|
| 5892 | case IdleLane:
|
|---|
| 5893 | lane = IdleHydrationLane;
|
|---|
| 5894 | break;
|
|---|
| 5895 |
|
|---|
| 5896 | default:
|
|---|
| 5897 | // Everything else is already either a hydration lane, or shouldn't
|
|---|
| 5898 | // be retried at a hydration lane.
|
|---|
| 5899 | lane = NoLane;
|
|---|
| 5900 | break;
|
|---|
| 5901 | } // Check if the lane we chose is suspended. If so, that indicates that we
|
|---|
| 5902 | // already attempted and failed to hydrate at that level. Also check if we're
|
|---|
| 5903 | // already rendering that lane, which is rare but could happen.
|
|---|
| 5904 |
|
|---|
| 5905 |
|
|---|
| 5906 | if ((lane & (root.suspendedLanes | renderLanes)) !== NoLane) {
|
|---|
| 5907 | // Give up trying to hydrate and fall back to client render.
|
|---|
| 5908 | return NoLane;
|
|---|
| 5909 | }
|
|---|
| 5910 |
|
|---|
| 5911 | return lane;
|
|---|
| 5912 | }
|
|---|
| 5913 | function addFiberToLanesMap(root, fiber, lanes) {
|
|---|
| 5914 |
|
|---|
| 5915 | if (!isDevToolsPresent) {
|
|---|
| 5916 | return;
|
|---|
| 5917 | }
|
|---|
| 5918 |
|
|---|
| 5919 | var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
|
|---|
| 5920 |
|
|---|
| 5921 | while (lanes > 0) {
|
|---|
| 5922 | var index = laneToIndex(lanes);
|
|---|
| 5923 | var lane = 1 << index;
|
|---|
| 5924 | var updaters = pendingUpdatersLaneMap[index];
|
|---|
| 5925 | updaters.add(fiber);
|
|---|
| 5926 | lanes &= ~lane;
|
|---|
| 5927 | }
|
|---|
| 5928 | }
|
|---|
| 5929 | function movePendingFibersToMemoized(root, lanes) {
|
|---|
| 5930 |
|
|---|
| 5931 | if (!isDevToolsPresent) {
|
|---|
| 5932 | return;
|
|---|
| 5933 | }
|
|---|
| 5934 |
|
|---|
| 5935 | var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
|
|---|
| 5936 | var memoizedUpdaters = root.memoizedUpdaters;
|
|---|
| 5937 |
|
|---|
| 5938 | while (lanes > 0) {
|
|---|
| 5939 | var index = laneToIndex(lanes);
|
|---|
| 5940 | var lane = 1 << index;
|
|---|
| 5941 | var updaters = pendingUpdatersLaneMap[index];
|
|---|
| 5942 |
|
|---|
| 5943 | if (updaters.size > 0) {
|
|---|
| 5944 | updaters.forEach(function (fiber) {
|
|---|
| 5945 | var alternate = fiber.alternate;
|
|---|
| 5946 |
|
|---|
| 5947 | if (alternate === null || !memoizedUpdaters.has(alternate)) {
|
|---|
| 5948 | memoizedUpdaters.add(fiber);
|
|---|
| 5949 | }
|
|---|
| 5950 | });
|
|---|
| 5951 | updaters.clear();
|
|---|
| 5952 | }
|
|---|
| 5953 |
|
|---|
| 5954 | lanes &= ~lane;
|
|---|
| 5955 | }
|
|---|
| 5956 | }
|
|---|
| 5957 | function getTransitionsForLanes(root, lanes) {
|
|---|
| 5958 | {
|
|---|
| 5959 | return null;
|
|---|
| 5960 | }
|
|---|
| 5961 | }
|
|---|
| 5962 |
|
|---|
| 5963 | var DiscreteEventPriority = SyncLane;
|
|---|
| 5964 | var ContinuousEventPriority = InputContinuousLane;
|
|---|
| 5965 | var DefaultEventPriority = DefaultLane;
|
|---|
| 5966 | var IdleEventPriority = IdleLane;
|
|---|
| 5967 | var currentUpdatePriority = NoLane;
|
|---|
| 5968 | function getCurrentUpdatePriority() {
|
|---|
| 5969 | return currentUpdatePriority;
|
|---|
| 5970 | }
|
|---|
| 5971 | function setCurrentUpdatePriority(newPriority) {
|
|---|
| 5972 | currentUpdatePriority = newPriority;
|
|---|
| 5973 | }
|
|---|
| 5974 | function runWithPriority(priority, fn) {
|
|---|
| 5975 | var previousPriority = currentUpdatePriority;
|
|---|
| 5976 |
|
|---|
| 5977 | try {
|
|---|
| 5978 | currentUpdatePriority = priority;
|
|---|
| 5979 | return fn();
|
|---|
| 5980 | } finally {
|
|---|
| 5981 | currentUpdatePriority = previousPriority;
|
|---|
| 5982 | }
|
|---|
| 5983 | }
|
|---|
| 5984 | function higherEventPriority(a, b) {
|
|---|
| 5985 | return a !== 0 && a < b ? a : b;
|
|---|
| 5986 | }
|
|---|
| 5987 | function lowerEventPriority(a, b) {
|
|---|
| 5988 | return a === 0 || a > b ? a : b;
|
|---|
| 5989 | }
|
|---|
| 5990 | function isHigherEventPriority(a, b) {
|
|---|
| 5991 | return a !== 0 && a < b;
|
|---|
| 5992 | }
|
|---|
| 5993 | function lanesToEventPriority(lanes) {
|
|---|
| 5994 | var lane = getHighestPriorityLane(lanes);
|
|---|
| 5995 |
|
|---|
| 5996 | if (!isHigherEventPriority(DiscreteEventPriority, lane)) {
|
|---|
| 5997 | return DiscreteEventPriority;
|
|---|
| 5998 | }
|
|---|
| 5999 |
|
|---|
| 6000 | if (!isHigherEventPriority(ContinuousEventPriority, lane)) {
|
|---|
| 6001 | return ContinuousEventPriority;
|
|---|
| 6002 | }
|
|---|
| 6003 |
|
|---|
| 6004 | if (includesNonIdleWork(lane)) {
|
|---|
| 6005 | return DefaultEventPriority;
|
|---|
| 6006 | }
|
|---|
| 6007 |
|
|---|
| 6008 | return IdleEventPriority;
|
|---|
| 6009 | }
|
|---|
| 6010 |
|
|---|
| 6011 | // This is imported by the event replaying implementation in React DOM. It's
|
|---|
| 6012 | // in a separate file to break a circular dependency between the renderer and
|
|---|
| 6013 | // the reconciler.
|
|---|
| 6014 | function isRootDehydrated(root) {
|
|---|
| 6015 | var currentState = root.current.memoizedState;
|
|---|
| 6016 | return currentState.isDehydrated;
|
|---|
| 6017 | }
|
|---|
| 6018 |
|
|---|
| 6019 | var _attemptSynchronousHydration;
|
|---|
| 6020 |
|
|---|
| 6021 | function setAttemptSynchronousHydration(fn) {
|
|---|
| 6022 | _attemptSynchronousHydration = fn;
|
|---|
| 6023 | }
|
|---|
| 6024 | function attemptSynchronousHydration(fiber) {
|
|---|
| 6025 | _attemptSynchronousHydration(fiber);
|
|---|
| 6026 | }
|
|---|
| 6027 | var attemptContinuousHydration;
|
|---|
| 6028 | function setAttemptContinuousHydration(fn) {
|
|---|
| 6029 | attemptContinuousHydration = fn;
|
|---|
| 6030 | }
|
|---|
| 6031 | var attemptHydrationAtCurrentPriority;
|
|---|
| 6032 | function setAttemptHydrationAtCurrentPriority(fn) {
|
|---|
| 6033 | attemptHydrationAtCurrentPriority = fn;
|
|---|
| 6034 | }
|
|---|
| 6035 | var getCurrentUpdatePriority$1;
|
|---|
| 6036 | function setGetCurrentUpdatePriority(fn) {
|
|---|
| 6037 | getCurrentUpdatePriority$1 = fn;
|
|---|
| 6038 | }
|
|---|
| 6039 | var attemptHydrationAtPriority;
|
|---|
| 6040 | function setAttemptHydrationAtPriority(fn) {
|
|---|
| 6041 | attemptHydrationAtPriority = fn;
|
|---|
| 6042 | } // TODO: Upgrade this definition once we're on a newer version of Flow that
|
|---|
| 6043 | // has this definition built-in.
|
|---|
| 6044 |
|
|---|
| 6045 | var hasScheduledReplayAttempt = false; // The queue of discrete events to be replayed.
|
|---|
| 6046 |
|
|---|
| 6047 | var queuedDiscreteEvents = []; // Indicates if any continuous event targets are non-null for early bailout.
|
|---|
| 6048 | // if the last target was dehydrated.
|
|---|
| 6049 |
|
|---|
| 6050 | var queuedFocus = null;
|
|---|
| 6051 | var queuedDrag = null;
|
|---|
| 6052 | var queuedMouse = null; // For pointer events there can be one latest event per pointerId.
|
|---|
| 6053 |
|
|---|
| 6054 | var queuedPointers = new Map();
|
|---|
| 6055 | var queuedPointerCaptures = new Map(); // We could consider replaying selectionchange and touchmoves too.
|
|---|
| 6056 |
|
|---|
| 6057 | var queuedExplicitHydrationTargets = [];
|
|---|
| 6058 | var discreteReplayableEvents = ['mousedown', 'mouseup', 'touchcancel', 'touchend', 'touchstart', 'auxclick', 'dblclick', 'pointercancel', 'pointerdown', 'pointerup', 'dragend', 'dragstart', 'drop', 'compositionend', 'compositionstart', 'keydown', 'keypress', 'keyup', 'input', 'textInput', // Intentionally camelCase
|
|---|
| 6059 | 'copy', 'cut', 'paste', 'click', 'change', 'contextmenu', 'reset', 'submit'];
|
|---|
| 6060 | function isDiscreteEventThatRequiresHydration(eventType) {
|
|---|
| 6061 | return discreteReplayableEvents.indexOf(eventType) > -1;
|
|---|
| 6062 | }
|
|---|
| 6063 |
|
|---|
| 6064 | function createQueuedReplayableEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6065 | return {
|
|---|
| 6066 | blockedOn: blockedOn,
|
|---|
| 6067 | domEventName: domEventName,
|
|---|
| 6068 | eventSystemFlags: eventSystemFlags,
|
|---|
| 6069 | nativeEvent: nativeEvent,
|
|---|
| 6070 | targetContainers: [targetContainer]
|
|---|
| 6071 | };
|
|---|
| 6072 | }
|
|---|
| 6073 |
|
|---|
| 6074 | function clearIfContinuousEvent(domEventName, nativeEvent) {
|
|---|
| 6075 | switch (domEventName) {
|
|---|
| 6076 | case 'focusin':
|
|---|
| 6077 | case 'focusout':
|
|---|
| 6078 | queuedFocus = null;
|
|---|
| 6079 | break;
|
|---|
| 6080 |
|
|---|
| 6081 | case 'dragenter':
|
|---|
| 6082 | case 'dragleave':
|
|---|
| 6083 | queuedDrag = null;
|
|---|
| 6084 | break;
|
|---|
| 6085 |
|
|---|
| 6086 | case 'mouseover':
|
|---|
| 6087 | case 'mouseout':
|
|---|
| 6088 | queuedMouse = null;
|
|---|
| 6089 | break;
|
|---|
| 6090 |
|
|---|
| 6091 | case 'pointerover':
|
|---|
| 6092 | case 'pointerout':
|
|---|
| 6093 | {
|
|---|
| 6094 | var pointerId = nativeEvent.pointerId;
|
|---|
| 6095 | queuedPointers.delete(pointerId);
|
|---|
| 6096 | break;
|
|---|
| 6097 | }
|
|---|
| 6098 |
|
|---|
| 6099 | case 'gotpointercapture':
|
|---|
| 6100 | case 'lostpointercapture':
|
|---|
| 6101 | {
|
|---|
| 6102 | var _pointerId = nativeEvent.pointerId;
|
|---|
| 6103 | queuedPointerCaptures.delete(_pointerId);
|
|---|
| 6104 | break;
|
|---|
| 6105 | }
|
|---|
| 6106 | }
|
|---|
| 6107 | }
|
|---|
| 6108 |
|
|---|
| 6109 | function accumulateOrCreateContinuousQueuedReplayableEvent(existingQueuedEvent, blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6110 | if (existingQueuedEvent === null || existingQueuedEvent.nativeEvent !== nativeEvent) {
|
|---|
| 6111 | var queuedEvent = createQueuedReplayableEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent);
|
|---|
| 6112 |
|
|---|
| 6113 | if (blockedOn !== null) {
|
|---|
| 6114 | var _fiber2 = getInstanceFromNode(blockedOn);
|
|---|
| 6115 |
|
|---|
| 6116 | if (_fiber2 !== null) {
|
|---|
| 6117 | // Attempt to increase the priority of this target.
|
|---|
| 6118 | attemptContinuousHydration(_fiber2);
|
|---|
| 6119 | }
|
|---|
| 6120 | }
|
|---|
| 6121 |
|
|---|
| 6122 | return queuedEvent;
|
|---|
| 6123 | } // If we have already queued this exact event, then it's because
|
|---|
| 6124 | // the different event systems have different DOM event listeners.
|
|---|
| 6125 | // We can accumulate the flags, and the targetContainers, and
|
|---|
| 6126 | // store a single event to be replayed.
|
|---|
| 6127 |
|
|---|
| 6128 |
|
|---|
| 6129 | existingQueuedEvent.eventSystemFlags |= eventSystemFlags;
|
|---|
| 6130 | var targetContainers = existingQueuedEvent.targetContainers;
|
|---|
| 6131 |
|
|---|
| 6132 | if (targetContainer !== null && targetContainers.indexOf(targetContainer) === -1) {
|
|---|
| 6133 | targetContainers.push(targetContainer);
|
|---|
| 6134 | }
|
|---|
| 6135 |
|
|---|
| 6136 | return existingQueuedEvent;
|
|---|
| 6137 | }
|
|---|
| 6138 |
|
|---|
| 6139 | function queueIfContinuousEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6140 | // These set relatedTarget to null because the replayed event will be treated as if we
|
|---|
| 6141 | // moved from outside the window (no target) onto the target once it hydrates.
|
|---|
| 6142 | // Instead of mutating we could clone the event.
|
|---|
| 6143 | switch (domEventName) {
|
|---|
| 6144 | case 'focusin':
|
|---|
| 6145 | {
|
|---|
| 6146 | var focusEvent = nativeEvent;
|
|---|
| 6147 | queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(queuedFocus, blockedOn, domEventName, eventSystemFlags, targetContainer, focusEvent);
|
|---|
| 6148 | return true;
|
|---|
| 6149 | }
|
|---|
| 6150 |
|
|---|
| 6151 | case 'dragenter':
|
|---|
| 6152 | {
|
|---|
| 6153 | var dragEvent = nativeEvent;
|
|---|
| 6154 | queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(queuedDrag, blockedOn, domEventName, eventSystemFlags, targetContainer, dragEvent);
|
|---|
| 6155 | return true;
|
|---|
| 6156 | }
|
|---|
| 6157 |
|
|---|
| 6158 | case 'mouseover':
|
|---|
| 6159 | {
|
|---|
| 6160 | var mouseEvent = nativeEvent;
|
|---|
| 6161 | queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(queuedMouse, blockedOn, domEventName, eventSystemFlags, targetContainer, mouseEvent);
|
|---|
| 6162 | return true;
|
|---|
| 6163 | }
|
|---|
| 6164 |
|
|---|
| 6165 | case 'pointerover':
|
|---|
| 6166 | {
|
|---|
| 6167 | var pointerEvent = nativeEvent;
|
|---|
| 6168 | var pointerId = pointerEvent.pointerId;
|
|---|
| 6169 | queuedPointers.set(pointerId, accumulateOrCreateContinuousQueuedReplayableEvent(queuedPointers.get(pointerId) || null, blockedOn, domEventName, eventSystemFlags, targetContainer, pointerEvent));
|
|---|
| 6170 | return true;
|
|---|
| 6171 | }
|
|---|
| 6172 |
|
|---|
| 6173 | case 'gotpointercapture':
|
|---|
| 6174 | {
|
|---|
| 6175 | var _pointerEvent = nativeEvent;
|
|---|
| 6176 | var _pointerId2 = _pointerEvent.pointerId;
|
|---|
| 6177 | queuedPointerCaptures.set(_pointerId2, accumulateOrCreateContinuousQueuedReplayableEvent(queuedPointerCaptures.get(_pointerId2) || null, blockedOn, domEventName, eventSystemFlags, targetContainer, _pointerEvent));
|
|---|
| 6178 | return true;
|
|---|
| 6179 | }
|
|---|
| 6180 | }
|
|---|
| 6181 |
|
|---|
| 6182 | return false;
|
|---|
| 6183 | } // Check if this target is unblocked. Returns true if it's unblocked.
|
|---|
| 6184 |
|
|---|
| 6185 | function attemptExplicitHydrationTarget(queuedTarget) {
|
|---|
| 6186 | // TODO: This function shares a lot of logic with findInstanceBlockingEvent.
|
|---|
| 6187 | // Try to unify them. It's a bit tricky since it would require two return
|
|---|
| 6188 | // values.
|
|---|
| 6189 | var targetInst = getClosestInstanceFromNode(queuedTarget.target);
|
|---|
| 6190 |
|
|---|
| 6191 | if (targetInst !== null) {
|
|---|
| 6192 | var nearestMounted = getNearestMountedFiber(targetInst);
|
|---|
| 6193 |
|
|---|
| 6194 | if (nearestMounted !== null) {
|
|---|
| 6195 | var tag = nearestMounted.tag;
|
|---|
| 6196 |
|
|---|
| 6197 | if (tag === SuspenseComponent) {
|
|---|
| 6198 | var instance = getSuspenseInstanceFromFiber(nearestMounted);
|
|---|
| 6199 |
|
|---|
| 6200 | if (instance !== null) {
|
|---|
| 6201 | // We're blocked on hydrating this boundary.
|
|---|
| 6202 | // Increase its priority.
|
|---|
| 6203 | queuedTarget.blockedOn = instance;
|
|---|
| 6204 | attemptHydrationAtPriority(queuedTarget.priority, function () {
|
|---|
| 6205 | attemptHydrationAtCurrentPriority(nearestMounted);
|
|---|
| 6206 | });
|
|---|
| 6207 | return;
|
|---|
| 6208 | }
|
|---|
| 6209 | } else if (tag === HostRoot) {
|
|---|
| 6210 | var root = nearestMounted.stateNode;
|
|---|
| 6211 |
|
|---|
| 6212 | if (isRootDehydrated(root)) {
|
|---|
| 6213 | queuedTarget.blockedOn = getContainerFromFiber(nearestMounted); // We don't currently have a way to increase the priority of
|
|---|
| 6214 | // a root other than sync.
|
|---|
| 6215 |
|
|---|
| 6216 | return;
|
|---|
| 6217 | }
|
|---|
| 6218 | }
|
|---|
| 6219 | }
|
|---|
| 6220 | }
|
|---|
| 6221 |
|
|---|
| 6222 | queuedTarget.blockedOn = null;
|
|---|
| 6223 | }
|
|---|
| 6224 |
|
|---|
| 6225 | function queueExplicitHydrationTarget(target) {
|
|---|
| 6226 | // TODO: This will read the priority if it's dispatched by the React
|
|---|
| 6227 | // event system but not native events. Should read window.event.type, like
|
|---|
| 6228 | // we do for updates (getCurrentEventPriority).
|
|---|
| 6229 | var updatePriority = getCurrentUpdatePriority$1();
|
|---|
| 6230 | var queuedTarget = {
|
|---|
| 6231 | blockedOn: null,
|
|---|
| 6232 | target: target,
|
|---|
| 6233 | priority: updatePriority
|
|---|
| 6234 | };
|
|---|
| 6235 | var i = 0;
|
|---|
| 6236 |
|
|---|
| 6237 | for (; i < queuedExplicitHydrationTargets.length; i++) {
|
|---|
| 6238 | // Stop once we hit the first target with lower priority than
|
|---|
| 6239 | if (!isHigherEventPriority(updatePriority, queuedExplicitHydrationTargets[i].priority)) {
|
|---|
| 6240 | break;
|
|---|
| 6241 | }
|
|---|
| 6242 | }
|
|---|
| 6243 |
|
|---|
| 6244 | queuedExplicitHydrationTargets.splice(i, 0, queuedTarget);
|
|---|
| 6245 |
|
|---|
| 6246 | if (i === 0) {
|
|---|
| 6247 | attemptExplicitHydrationTarget(queuedTarget);
|
|---|
| 6248 | }
|
|---|
| 6249 | }
|
|---|
| 6250 |
|
|---|
| 6251 | function attemptReplayContinuousQueuedEvent(queuedEvent) {
|
|---|
| 6252 | if (queuedEvent.blockedOn !== null) {
|
|---|
| 6253 | return false;
|
|---|
| 6254 | }
|
|---|
| 6255 |
|
|---|
| 6256 | var targetContainers = queuedEvent.targetContainers;
|
|---|
| 6257 |
|
|---|
| 6258 | while (targetContainers.length > 0) {
|
|---|
| 6259 | var targetContainer = targetContainers[0];
|
|---|
| 6260 | var nextBlockedOn = findInstanceBlockingEvent(queuedEvent.domEventName, queuedEvent.eventSystemFlags, targetContainer, queuedEvent.nativeEvent);
|
|---|
| 6261 |
|
|---|
| 6262 | if (nextBlockedOn === null) {
|
|---|
| 6263 | {
|
|---|
| 6264 | var nativeEvent = queuedEvent.nativeEvent;
|
|---|
| 6265 | var nativeEventClone = new nativeEvent.constructor(nativeEvent.type, nativeEvent);
|
|---|
| 6266 | setReplayingEvent(nativeEventClone);
|
|---|
| 6267 | nativeEvent.target.dispatchEvent(nativeEventClone);
|
|---|
| 6268 | resetReplayingEvent();
|
|---|
| 6269 | }
|
|---|
| 6270 | } else {
|
|---|
| 6271 | // We're still blocked. Try again later.
|
|---|
| 6272 | var _fiber3 = getInstanceFromNode(nextBlockedOn);
|
|---|
| 6273 |
|
|---|
| 6274 | if (_fiber3 !== null) {
|
|---|
| 6275 | attemptContinuousHydration(_fiber3);
|
|---|
| 6276 | }
|
|---|
| 6277 |
|
|---|
| 6278 | queuedEvent.blockedOn = nextBlockedOn;
|
|---|
| 6279 | return false;
|
|---|
| 6280 | } // This target container was successfully dispatched. Try the next.
|
|---|
| 6281 |
|
|---|
| 6282 |
|
|---|
| 6283 | targetContainers.shift();
|
|---|
| 6284 | }
|
|---|
| 6285 |
|
|---|
| 6286 | return true;
|
|---|
| 6287 | }
|
|---|
| 6288 |
|
|---|
| 6289 | function attemptReplayContinuousQueuedEventInMap(queuedEvent, key, map) {
|
|---|
| 6290 | if (attemptReplayContinuousQueuedEvent(queuedEvent)) {
|
|---|
| 6291 | map.delete(key);
|
|---|
| 6292 | }
|
|---|
| 6293 | }
|
|---|
| 6294 |
|
|---|
| 6295 | function replayUnblockedEvents() {
|
|---|
| 6296 | hasScheduledReplayAttempt = false;
|
|---|
| 6297 |
|
|---|
| 6298 |
|
|---|
| 6299 | if (queuedFocus !== null && attemptReplayContinuousQueuedEvent(queuedFocus)) {
|
|---|
| 6300 | queuedFocus = null;
|
|---|
| 6301 | }
|
|---|
| 6302 |
|
|---|
| 6303 | if (queuedDrag !== null && attemptReplayContinuousQueuedEvent(queuedDrag)) {
|
|---|
| 6304 | queuedDrag = null;
|
|---|
| 6305 | }
|
|---|
| 6306 |
|
|---|
| 6307 | if (queuedMouse !== null && attemptReplayContinuousQueuedEvent(queuedMouse)) {
|
|---|
| 6308 | queuedMouse = null;
|
|---|
| 6309 | }
|
|---|
| 6310 |
|
|---|
| 6311 | queuedPointers.forEach(attemptReplayContinuousQueuedEventInMap);
|
|---|
| 6312 | queuedPointerCaptures.forEach(attemptReplayContinuousQueuedEventInMap);
|
|---|
| 6313 | }
|
|---|
| 6314 |
|
|---|
| 6315 | function scheduleCallbackIfUnblocked(queuedEvent, unblocked) {
|
|---|
| 6316 | if (queuedEvent.blockedOn === unblocked) {
|
|---|
| 6317 | queuedEvent.blockedOn = null;
|
|---|
| 6318 |
|
|---|
| 6319 | if (!hasScheduledReplayAttempt) {
|
|---|
| 6320 | hasScheduledReplayAttempt = true; // Schedule a callback to attempt replaying as many events as are
|
|---|
| 6321 | // now unblocked. This first might not actually be unblocked yet.
|
|---|
| 6322 | // We could check it early to avoid scheduling an unnecessary callback.
|
|---|
| 6323 |
|
|---|
| 6324 | Scheduler.unstable_scheduleCallback(Scheduler.unstable_NormalPriority, replayUnblockedEvents);
|
|---|
| 6325 | }
|
|---|
| 6326 | }
|
|---|
| 6327 | }
|
|---|
| 6328 |
|
|---|
| 6329 | function retryIfBlockedOn(unblocked) {
|
|---|
| 6330 | // Mark anything that was blocked on this as no longer blocked
|
|---|
| 6331 | // and eligible for a replay.
|
|---|
| 6332 | if (queuedDiscreteEvents.length > 0) {
|
|---|
| 6333 | scheduleCallbackIfUnblocked(queuedDiscreteEvents[0], unblocked); // This is a exponential search for each boundary that commits. I think it's
|
|---|
| 6334 | // worth it because we expect very few discrete events to queue up and once
|
|---|
| 6335 | // we are actually fully unblocked it will be fast to replay them.
|
|---|
| 6336 |
|
|---|
| 6337 | for (var i = 1; i < queuedDiscreteEvents.length; i++) {
|
|---|
| 6338 | var queuedEvent = queuedDiscreteEvents[i];
|
|---|
| 6339 |
|
|---|
| 6340 | if (queuedEvent.blockedOn === unblocked) {
|
|---|
| 6341 | queuedEvent.blockedOn = null;
|
|---|
| 6342 | }
|
|---|
| 6343 | }
|
|---|
| 6344 | }
|
|---|
| 6345 |
|
|---|
| 6346 | if (queuedFocus !== null) {
|
|---|
| 6347 | scheduleCallbackIfUnblocked(queuedFocus, unblocked);
|
|---|
| 6348 | }
|
|---|
| 6349 |
|
|---|
| 6350 | if (queuedDrag !== null) {
|
|---|
| 6351 | scheduleCallbackIfUnblocked(queuedDrag, unblocked);
|
|---|
| 6352 | }
|
|---|
| 6353 |
|
|---|
| 6354 | if (queuedMouse !== null) {
|
|---|
| 6355 | scheduleCallbackIfUnblocked(queuedMouse, unblocked);
|
|---|
| 6356 | }
|
|---|
| 6357 |
|
|---|
| 6358 | var unblock = function (queuedEvent) {
|
|---|
| 6359 | return scheduleCallbackIfUnblocked(queuedEvent, unblocked);
|
|---|
| 6360 | };
|
|---|
| 6361 |
|
|---|
| 6362 | queuedPointers.forEach(unblock);
|
|---|
| 6363 | queuedPointerCaptures.forEach(unblock);
|
|---|
| 6364 |
|
|---|
| 6365 | for (var _i = 0; _i < queuedExplicitHydrationTargets.length; _i++) {
|
|---|
| 6366 | var queuedTarget = queuedExplicitHydrationTargets[_i];
|
|---|
| 6367 |
|
|---|
| 6368 | if (queuedTarget.blockedOn === unblocked) {
|
|---|
| 6369 | queuedTarget.blockedOn = null;
|
|---|
| 6370 | }
|
|---|
| 6371 | }
|
|---|
| 6372 |
|
|---|
| 6373 | while (queuedExplicitHydrationTargets.length > 0) {
|
|---|
| 6374 | var nextExplicitTarget = queuedExplicitHydrationTargets[0];
|
|---|
| 6375 |
|
|---|
| 6376 | if (nextExplicitTarget.blockedOn !== null) {
|
|---|
| 6377 | // We're still blocked.
|
|---|
| 6378 | break;
|
|---|
| 6379 | } else {
|
|---|
| 6380 | attemptExplicitHydrationTarget(nextExplicitTarget);
|
|---|
| 6381 |
|
|---|
| 6382 | if (nextExplicitTarget.blockedOn === null) {
|
|---|
| 6383 | // We're unblocked.
|
|---|
| 6384 | queuedExplicitHydrationTargets.shift();
|
|---|
| 6385 | }
|
|---|
| 6386 | }
|
|---|
| 6387 | }
|
|---|
| 6388 | }
|
|---|
| 6389 |
|
|---|
| 6390 | var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; // TODO: can we stop exporting these?
|
|---|
| 6391 |
|
|---|
| 6392 | var _enabled = true; // This is exported in FB builds for use by legacy FB layer infra.
|
|---|
| 6393 | // We'd like to remove this but it's not clear if this is safe.
|
|---|
| 6394 |
|
|---|
| 6395 | function setEnabled(enabled) {
|
|---|
| 6396 | _enabled = !!enabled;
|
|---|
| 6397 | }
|
|---|
| 6398 | function isEnabled() {
|
|---|
| 6399 | return _enabled;
|
|---|
| 6400 | }
|
|---|
| 6401 | function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags) {
|
|---|
| 6402 | var eventPriority = getEventPriority(domEventName);
|
|---|
| 6403 | var listenerWrapper;
|
|---|
| 6404 |
|
|---|
| 6405 | switch (eventPriority) {
|
|---|
| 6406 | case DiscreteEventPriority:
|
|---|
| 6407 | listenerWrapper = dispatchDiscreteEvent;
|
|---|
| 6408 | break;
|
|---|
| 6409 |
|
|---|
| 6410 | case ContinuousEventPriority:
|
|---|
| 6411 | listenerWrapper = dispatchContinuousEvent;
|
|---|
| 6412 | break;
|
|---|
| 6413 |
|
|---|
| 6414 | case DefaultEventPriority:
|
|---|
| 6415 | default:
|
|---|
| 6416 | listenerWrapper = dispatchEvent;
|
|---|
| 6417 | break;
|
|---|
| 6418 | }
|
|---|
| 6419 |
|
|---|
| 6420 | return listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer);
|
|---|
| 6421 | }
|
|---|
| 6422 |
|
|---|
| 6423 | function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) {
|
|---|
| 6424 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 6425 | var prevTransition = ReactCurrentBatchConfig.transition;
|
|---|
| 6426 | ReactCurrentBatchConfig.transition = null;
|
|---|
| 6427 |
|
|---|
| 6428 | try {
|
|---|
| 6429 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 6430 | dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
|
|---|
| 6431 | } finally {
|
|---|
| 6432 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 6433 | ReactCurrentBatchConfig.transition = prevTransition;
|
|---|
| 6434 | }
|
|---|
| 6435 | }
|
|---|
| 6436 |
|
|---|
| 6437 | function dispatchContinuousEvent(domEventName, eventSystemFlags, container, nativeEvent) {
|
|---|
| 6438 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 6439 | var prevTransition = ReactCurrentBatchConfig.transition;
|
|---|
| 6440 | ReactCurrentBatchConfig.transition = null;
|
|---|
| 6441 |
|
|---|
| 6442 | try {
|
|---|
| 6443 | setCurrentUpdatePriority(ContinuousEventPriority);
|
|---|
| 6444 | dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent);
|
|---|
| 6445 | } finally {
|
|---|
| 6446 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 6447 | ReactCurrentBatchConfig.transition = prevTransition;
|
|---|
| 6448 | }
|
|---|
| 6449 | }
|
|---|
| 6450 |
|
|---|
| 6451 | function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6452 | if (!_enabled) {
|
|---|
| 6453 | return;
|
|---|
| 6454 | }
|
|---|
| 6455 |
|
|---|
| 6456 | {
|
|---|
| 6457 | dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(domEventName, eventSystemFlags, targetContainer, nativeEvent);
|
|---|
| 6458 | }
|
|---|
| 6459 | }
|
|---|
| 6460 |
|
|---|
| 6461 | function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6462 | var blockedOn = findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent);
|
|---|
| 6463 |
|
|---|
| 6464 | if (blockedOn === null) {
|
|---|
| 6465 | dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, return_targetInst, targetContainer);
|
|---|
| 6466 | clearIfContinuousEvent(domEventName, nativeEvent);
|
|---|
| 6467 | return;
|
|---|
| 6468 | }
|
|---|
| 6469 |
|
|---|
| 6470 | if (queueIfContinuousEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent)) {
|
|---|
| 6471 | nativeEvent.stopPropagation();
|
|---|
| 6472 | return;
|
|---|
| 6473 | } // We need to clear only if we didn't queue because
|
|---|
| 6474 | // queueing is accumulative.
|
|---|
| 6475 |
|
|---|
| 6476 |
|
|---|
| 6477 | clearIfContinuousEvent(domEventName, nativeEvent);
|
|---|
| 6478 |
|
|---|
| 6479 | if (eventSystemFlags & IS_CAPTURE_PHASE && isDiscreteEventThatRequiresHydration(domEventName)) {
|
|---|
| 6480 | while (blockedOn !== null) {
|
|---|
| 6481 | var fiber = getInstanceFromNode(blockedOn);
|
|---|
| 6482 |
|
|---|
| 6483 | if (fiber !== null) {
|
|---|
| 6484 | attemptSynchronousHydration(fiber);
|
|---|
| 6485 | }
|
|---|
| 6486 |
|
|---|
| 6487 | var nextBlockedOn = findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent);
|
|---|
| 6488 |
|
|---|
| 6489 | if (nextBlockedOn === null) {
|
|---|
| 6490 | dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, return_targetInst, targetContainer);
|
|---|
| 6491 | }
|
|---|
| 6492 |
|
|---|
| 6493 | if (nextBlockedOn === blockedOn) {
|
|---|
| 6494 | break;
|
|---|
| 6495 | }
|
|---|
| 6496 |
|
|---|
| 6497 | blockedOn = nextBlockedOn;
|
|---|
| 6498 | }
|
|---|
| 6499 |
|
|---|
| 6500 | if (blockedOn !== null) {
|
|---|
| 6501 | nativeEvent.stopPropagation();
|
|---|
| 6502 | }
|
|---|
| 6503 |
|
|---|
| 6504 | return;
|
|---|
| 6505 | } // This is not replayable so we'll invoke it but without a target,
|
|---|
| 6506 | // in case the event system needs to trace it.
|
|---|
| 6507 |
|
|---|
| 6508 |
|
|---|
| 6509 | dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, null, targetContainer);
|
|---|
| 6510 | }
|
|---|
| 6511 |
|
|---|
| 6512 | var return_targetInst = null; // Returns a SuspenseInstance or Container if it's blocked.
|
|---|
| 6513 | // The return_targetInst field above is conceptually part of the return value.
|
|---|
| 6514 |
|
|---|
| 6515 | function findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) {
|
|---|
| 6516 | // TODO: Warn if _enabled is false.
|
|---|
| 6517 | return_targetInst = null;
|
|---|
| 6518 | var nativeEventTarget = getEventTarget(nativeEvent);
|
|---|
| 6519 | var targetInst = getClosestInstanceFromNode(nativeEventTarget);
|
|---|
| 6520 |
|
|---|
| 6521 | if (targetInst !== null) {
|
|---|
| 6522 | var nearestMounted = getNearestMountedFiber(targetInst);
|
|---|
| 6523 |
|
|---|
| 6524 | if (nearestMounted === null) {
|
|---|
| 6525 | // This tree has been unmounted already. Dispatch without a target.
|
|---|
| 6526 | targetInst = null;
|
|---|
| 6527 | } else {
|
|---|
| 6528 | var tag = nearestMounted.tag;
|
|---|
| 6529 |
|
|---|
| 6530 | if (tag === SuspenseComponent) {
|
|---|
| 6531 | var instance = getSuspenseInstanceFromFiber(nearestMounted);
|
|---|
| 6532 |
|
|---|
| 6533 | if (instance !== null) {
|
|---|
| 6534 | // Queue the event to be replayed later. Abort dispatching since we
|
|---|
| 6535 | // don't want this event dispatched twice through the event system.
|
|---|
| 6536 | // TODO: If this is the first discrete event in the queue. Schedule an increased
|
|---|
| 6537 | // priority for this boundary.
|
|---|
| 6538 | return instance;
|
|---|
| 6539 | } // This shouldn't happen, something went wrong but to avoid blocking
|
|---|
| 6540 | // the whole system, dispatch the event without a target.
|
|---|
| 6541 | // TODO: Warn.
|
|---|
| 6542 |
|
|---|
| 6543 |
|
|---|
| 6544 | targetInst = null;
|
|---|
| 6545 | } else if (tag === HostRoot) {
|
|---|
| 6546 | var root = nearestMounted.stateNode;
|
|---|
| 6547 |
|
|---|
| 6548 | if (isRootDehydrated(root)) {
|
|---|
| 6549 | // If this happens during a replay something went wrong and it might block
|
|---|
| 6550 | // the whole system.
|
|---|
| 6551 | return getContainerFromFiber(nearestMounted);
|
|---|
| 6552 | }
|
|---|
| 6553 |
|
|---|
| 6554 | targetInst = null;
|
|---|
| 6555 | } else if (nearestMounted !== targetInst) {
|
|---|
| 6556 | // If we get an event (ex: img onload) before committing that
|
|---|
| 6557 | // component's mount, ignore it for now (that is, treat it as if it was an
|
|---|
| 6558 | // event on a non-React tree). We might also consider queueing events and
|
|---|
| 6559 | // dispatching them after the mount.
|
|---|
| 6560 | targetInst = null;
|
|---|
| 6561 | }
|
|---|
| 6562 | }
|
|---|
| 6563 | }
|
|---|
| 6564 |
|
|---|
| 6565 | return_targetInst = targetInst; // We're not blocked on anything.
|
|---|
| 6566 |
|
|---|
| 6567 | return null;
|
|---|
| 6568 | }
|
|---|
| 6569 | function getEventPriority(domEventName) {
|
|---|
| 6570 | switch (domEventName) {
|
|---|
| 6571 | // Used by SimpleEventPlugin:
|
|---|
| 6572 | case 'cancel':
|
|---|
| 6573 | case 'click':
|
|---|
| 6574 | case 'close':
|
|---|
| 6575 | case 'contextmenu':
|
|---|
| 6576 | case 'copy':
|
|---|
| 6577 | case 'cut':
|
|---|
| 6578 | case 'auxclick':
|
|---|
| 6579 | case 'dblclick':
|
|---|
| 6580 | case 'dragend':
|
|---|
| 6581 | case 'dragstart':
|
|---|
| 6582 | case 'drop':
|
|---|
| 6583 | case 'focusin':
|
|---|
| 6584 | case 'focusout':
|
|---|
| 6585 | case 'input':
|
|---|
| 6586 | case 'invalid':
|
|---|
| 6587 | case 'keydown':
|
|---|
| 6588 | case 'keypress':
|
|---|
| 6589 | case 'keyup':
|
|---|
| 6590 | case 'mousedown':
|
|---|
| 6591 | case 'mouseup':
|
|---|
| 6592 | case 'paste':
|
|---|
| 6593 | case 'pause':
|
|---|
| 6594 | case 'play':
|
|---|
| 6595 | case 'pointercancel':
|
|---|
| 6596 | case 'pointerdown':
|
|---|
| 6597 | case 'pointerup':
|
|---|
| 6598 | case 'ratechange':
|
|---|
| 6599 | case 'reset':
|
|---|
| 6600 | case 'resize':
|
|---|
| 6601 | case 'seeked':
|
|---|
| 6602 | case 'submit':
|
|---|
| 6603 | case 'touchcancel':
|
|---|
| 6604 | case 'touchend':
|
|---|
| 6605 | case 'touchstart':
|
|---|
| 6606 | case 'volumechange': // Used by polyfills:
|
|---|
| 6607 | // eslint-disable-next-line no-fallthrough
|
|---|
| 6608 |
|
|---|
| 6609 | case 'change':
|
|---|
| 6610 | case 'selectionchange':
|
|---|
| 6611 | case 'textInput':
|
|---|
| 6612 | case 'compositionstart':
|
|---|
| 6613 | case 'compositionend':
|
|---|
| 6614 | case 'compositionupdate': // Only enableCreateEventHandleAPI:
|
|---|
| 6615 | // eslint-disable-next-line no-fallthrough
|
|---|
| 6616 |
|
|---|
| 6617 | case 'beforeblur':
|
|---|
| 6618 | case 'afterblur': // Not used by React but could be by user code:
|
|---|
| 6619 | // eslint-disable-next-line no-fallthrough
|
|---|
| 6620 |
|
|---|
| 6621 | case 'beforeinput':
|
|---|
| 6622 | case 'blur':
|
|---|
| 6623 | case 'fullscreenchange':
|
|---|
| 6624 | case 'focus':
|
|---|
| 6625 | case 'hashchange':
|
|---|
| 6626 | case 'popstate':
|
|---|
| 6627 | case 'select':
|
|---|
| 6628 | case 'selectstart':
|
|---|
| 6629 | return DiscreteEventPriority;
|
|---|
| 6630 |
|
|---|
| 6631 | case 'drag':
|
|---|
| 6632 | case 'dragenter':
|
|---|
| 6633 | case 'dragexit':
|
|---|
| 6634 | case 'dragleave':
|
|---|
| 6635 | case 'dragover':
|
|---|
| 6636 | case 'mousemove':
|
|---|
| 6637 | case 'mouseout':
|
|---|
| 6638 | case 'mouseover':
|
|---|
| 6639 | case 'pointermove':
|
|---|
| 6640 | case 'pointerout':
|
|---|
| 6641 | case 'pointerover':
|
|---|
| 6642 | case 'scroll':
|
|---|
| 6643 | case 'toggle':
|
|---|
| 6644 | case 'touchmove':
|
|---|
| 6645 | case 'wheel': // Not used by React but could be by user code:
|
|---|
| 6646 | // eslint-disable-next-line no-fallthrough
|
|---|
| 6647 |
|
|---|
| 6648 | case 'mouseenter':
|
|---|
| 6649 | case 'mouseleave':
|
|---|
| 6650 | case 'pointerenter':
|
|---|
| 6651 | case 'pointerleave':
|
|---|
| 6652 | return ContinuousEventPriority;
|
|---|
| 6653 |
|
|---|
| 6654 | case 'message':
|
|---|
| 6655 | {
|
|---|
| 6656 | // We might be in the Scheduler callback.
|
|---|
| 6657 | // Eventually this mechanism will be replaced by a check
|
|---|
| 6658 | // of the current priority on the native scheduler.
|
|---|
| 6659 | var schedulerPriority = getCurrentPriorityLevel();
|
|---|
| 6660 |
|
|---|
| 6661 | switch (schedulerPriority) {
|
|---|
| 6662 | case ImmediatePriority:
|
|---|
| 6663 | return DiscreteEventPriority;
|
|---|
| 6664 |
|
|---|
| 6665 | case UserBlockingPriority:
|
|---|
| 6666 | return ContinuousEventPriority;
|
|---|
| 6667 |
|
|---|
| 6668 | case NormalPriority:
|
|---|
| 6669 | case LowPriority:
|
|---|
| 6670 | // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration.
|
|---|
| 6671 | return DefaultEventPriority;
|
|---|
| 6672 |
|
|---|
| 6673 | case IdlePriority:
|
|---|
| 6674 | return IdleEventPriority;
|
|---|
| 6675 |
|
|---|
| 6676 | default:
|
|---|
| 6677 | return DefaultEventPriority;
|
|---|
| 6678 | }
|
|---|
| 6679 | }
|
|---|
| 6680 |
|
|---|
| 6681 | default:
|
|---|
| 6682 | return DefaultEventPriority;
|
|---|
| 6683 | }
|
|---|
| 6684 | }
|
|---|
| 6685 |
|
|---|
| 6686 | function addEventBubbleListener(target, eventType, listener) {
|
|---|
| 6687 | target.addEventListener(eventType, listener, false);
|
|---|
| 6688 | return listener;
|
|---|
| 6689 | }
|
|---|
| 6690 | function addEventCaptureListener(target, eventType, listener) {
|
|---|
| 6691 | target.addEventListener(eventType, listener, true);
|
|---|
| 6692 | return listener;
|
|---|
| 6693 | }
|
|---|
| 6694 | function addEventCaptureListenerWithPassiveFlag(target, eventType, listener, passive) {
|
|---|
| 6695 | target.addEventListener(eventType, listener, {
|
|---|
| 6696 | capture: true,
|
|---|
| 6697 | passive: passive
|
|---|
| 6698 | });
|
|---|
| 6699 | return listener;
|
|---|
| 6700 | }
|
|---|
| 6701 | function addEventBubbleListenerWithPassiveFlag(target, eventType, listener, passive) {
|
|---|
| 6702 | target.addEventListener(eventType, listener, {
|
|---|
| 6703 | passive: passive
|
|---|
| 6704 | });
|
|---|
| 6705 | return listener;
|
|---|
| 6706 | }
|
|---|
| 6707 |
|
|---|
| 6708 | /**
|
|---|
| 6709 | * These variables store information about text content of a target node,
|
|---|
| 6710 | * allowing comparison of content before and after a given event.
|
|---|
| 6711 | *
|
|---|
| 6712 | * Identify the node where selection currently begins, then observe
|
|---|
| 6713 | * both its text content and its current position in the DOM. Since the
|
|---|
| 6714 | * browser may natively replace the target node during composition, we can
|
|---|
| 6715 | * use its position to find its replacement.
|
|---|
| 6716 | *
|
|---|
| 6717 | *
|
|---|
| 6718 | */
|
|---|
| 6719 | var root = null;
|
|---|
| 6720 | var startText = null;
|
|---|
| 6721 | var fallbackText = null;
|
|---|
| 6722 | function initialize(nativeEventTarget) {
|
|---|
| 6723 | root = nativeEventTarget;
|
|---|
| 6724 | startText = getText();
|
|---|
| 6725 | return true;
|
|---|
| 6726 | }
|
|---|
| 6727 | function reset() {
|
|---|
| 6728 | root = null;
|
|---|
| 6729 | startText = null;
|
|---|
| 6730 | fallbackText = null;
|
|---|
| 6731 | }
|
|---|
| 6732 | function getData() {
|
|---|
| 6733 | if (fallbackText) {
|
|---|
| 6734 | return fallbackText;
|
|---|
| 6735 | }
|
|---|
| 6736 |
|
|---|
| 6737 | var start;
|
|---|
| 6738 | var startValue = startText;
|
|---|
| 6739 | var startLength = startValue.length;
|
|---|
| 6740 | var end;
|
|---|
| 6741 | var endValue = getText();
|
|---|
| 6742 | var endLength = endValue.length;
|
|---|
| 6743 |
|
|---|
| 6744 | for (start = 0; start < startLength; start++) {
|
|---|
| 6745 | if (startValue[start] !== endValue[start]) {
|
|---|
| 6746 | break;
|
|---|
| 6747 | }
|
|---|
| 6748 | }
|
|---|
| 6749 |
|
|---|
| 6750 | var minEnd = startLength - start;
|
|---|
| 6751 |
|
|---|
| 6752 | for (end = 1; end <= minEnd; end++) {
|
|---|
| 6753 | if (startValue[startLength - end] !== endValue[endLength - end]) {
|
|---|
| 6754 | break;
|
|---|
| 6755 | }
|
|---|
| 6756 | }
|
|---|
| 6757 |
|
|---|
| 6758 | var sliceTail = end > 1 ? 1 - end : undefined;
|
|---|
| 6759 | fallbackText = endValue.slice(start, sliceTail);
|
|---|
| 6760 | return fallbackText;
|
|---|
| 6761 | }
|
|---|
| 6762 | function getText() {
|
|---|
| 6763 | if ('value' in root) {
|
|---|
| 6764 | return root.value;
|
|---|
| 6765 | }
|
|---|
| 6766 |
|
|---|
| 6767 | return root.textContent;
|
|---|
| 6768 | }
|
|---|
| 6769 |
|
|---|
| 6770 | /**
|
|---|
| 6771 | * `charCode` represents the actual "character code" and is safe to use with
|
|---|
| 6772 | * `String.fromCharCode`. As such, only keys that correspond to printable
|
|---|
| 6773 | * characters produce a valid `charCode`, the only exception to this is Enter.
|
|---|
| 6774 | * The Tab-key is considered non-printable and does not have a `charCode`,
|
|---|
| 6775 | * presumably because it does not produce a tab-character in browsers.
|
|---|
| 6776 | *
|
|---|
| 6777 | * @param {object} nativeEvent Native browser event.
|
|---|
| 6778 | * @return {number} Normalized `charCode` property.
|
|---|
| 6779 | */
|
|---|
| 6780 | function getEventCharCode(nativeEvent) {
|
|---|
| 6781 | var charCode;
|
|---|
| 6782 | var keyCode = nativeEvent.keyCode;
|
|---|
| 6783 |
|
|---|
| 6784 | if ('charCode' in nativeEvent) {
|
|---|
| 6785 | charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`.
|
|---|
| 6786 |
|
|---|
| 6787 | if (charCode === 0 && keyCode === 13) {
|
|---|
| 6788 | charCode = 13;
|
|---|
| 6789 | }
|
|---|
| 6790 | } else {
|
|---|
| 6791 | // IE8 does not implement `charCode`, but `keyCode` has the correct value.
|
|---|
| 6792 | charCode = keyCode;
|
|---|
| 6793 | } // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux)
|
|---|
| 6794 | // report Enter as charCode 10 when ctrl is pressed.
|
|---|
| 6795 |
|
|---|
| 6796 |
|
|---|
| 6797 | if (charCode === 10) {
|
|---|
| 6798 | charCode = 13;
|
|---|
| 6799 | } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them.
|
|---|
| 6800 | // Must not discard the (non-)printable Enter-key.
|
|---|
| 6801 |
|
|---|
| 6802 |
|
|---|
| 6803 | if (charCode >= 32 || charCode === 13) {
|
|---|
| 6804 | return charCode;
|
|---|
| 6805 | }
|
|---|
| 6806 |
|
|---|
| 6807 | return 0;
|
|---|
| 6808 | }
|
|---|
| 6809 |
|
|---|
| 6810 | function functionThatReturnsTrue() {
|
|---|
| 6811 | return true;
|
|---|
| 6812 | }
|
|---|
| 6813 |
|
|---|
| 6814 | function functionThatReturnsFalse() {
|
|---|
| 6815 | return false;
|
|---|
| 6816 | } // This is intentionally a factory so that we have different returned constructors.
|
|---|
| 6817 | // If we had a single constructor, it would be megamorphic and engines would deopt.
|
|---|
| 6818 |
|
|---|
| 6819 |
|
|---|
| 6820 | function createSyntheticEvent(Interface) {
|
|---|
| 6821 | /**
|
|---|
| 6822 | * Synthetic events are dispatched by event plugins, typically in response to a
|
|---|
| 6823 | * top-level event delegation handler.
|
|---|
| 6824 | *
|
|---|
| 6825 | * These systems should generally use pooling to reduce the frequency of garbage
|
|---|
| 6826 | * collection. The system should check `isPersistent` to determine whether the
|
|---|
| 6827 | * event should be released into the pool after being dispatched. Users that
|
|---|
| 6828 | * need a persisted event should invoke `persist`.
|
|---|
| 6829 | *
|
|---|
| 6830 | * Synthetic events (and subclasses) implement the DOM Level 3 Events API by
|
|---|
| 6831 | * normalizing browser quirks. Subclasses do not necessarily have to implement a
|
|---|
| 6832 | * DOM interface; custom application-specific events can also subclass this.
|
|---|
| 6833 | */
|
|---|
| 6834 | function SyntheticBaseEvent(reactName, reactEventType, targetInst, nativeEvent, nativeEventTarget) {
|
|---|
| 6835 | this._reactName = reactName;
|
|---|
| 6836 | this._targetInst = targetInst;
|
|---|
| 6837 | this.type = reactEventType;
|
|---|
| 6838 | this.nativeEvent = nativeEvent;
|
|---|
| 6839 | this.target = nativeEventTarget;
|
|---|
| 6840 | this.currentTarget = null;
|
|---|
| 6841 |
|
|---|
| 6842 | for (var _propName in Interface) {
|
|---|
| 6843 | if (!Interface.hasOwnProperty(_propName)) {
|
|---|
| 6844 | continue;
|
|---|
| 6845 | }
|
|---|
| 6846 |
|
|---|
| 6847 | var normalize = Interface[_propName];
|
|---|
| 6848 |
|
|---|
| 6849 | if (normalize) {
|
|---|
| 6850 | this[_propName] = normalize(nativeEvent);
|
|---|
| 6851 | } else {
|
|---|
| 6852 | this[_propName] = nativeEvent[_propName];
|
|---|
| 6853 | }
|
|---|
| 6854 | }
|
|---|
| 6855 |
|
|---|
| 6856 | var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false;
|
|---|
| 6857 |
|
|---|
| 6858 | if (defaultPrevented) {
|
|---|
| 6859 | this.isDefaultPrevented = functionThatReturnsTrue;
|
|---|
| 6860 | } else {
|
|---|
| 6861 | this.isDefaultPrevented = functionThatReturnsFalse;
|
|---|
| 6862 | }
|
|---|
| 6863 |
|
|---|
| 6864 | this.isPropagationStopped = functionThatReturnsFalse;
|
|---|
| 6865 | return this;
|
|---|
| 6866 | }
|
|---|
| 6867 |
|
|---|
| 6868 | assign(SyntheticBaseEvent.prototype, {
|
|---|
| 6869 | preventDefault: function () {
|
|---|
| 6870 | this.defaultPrevented = true;
|
|---|
| 6871 | var event = this.nativeEvent;
|
|---|
| 6872 |
|
|---|
| 6873 | if (!event) {
|
|---|
| 6874 | return;
|
|---|
| 6875 | }
|
|---|
| 6876 |
|
|---|
| 6877 | if (event.preventDefault) {
|
|---|
| 6878 | event.preventDefault(); // $FlowFixMe - flow is not aware of `unknown` in IE
|
|---|
| 6879 | } else if (typeof event.returnValue !== 'unknown') {
|
|---|
| 6880 | event.returnValue = false;
|
|---|
| 6881 | }
|
|---|
| 6882 |
|
|---|
| 6883 | this.isDefaultPrevented = functionThatReturnsTrue;
|
|---|
| 6884 | },
|
|---|
| 6885 | stopPropagation: function () {
|
|---|
| 6886 | var event = this.nativeEvent;
|
|---|
| 6887 |
|
|---|
| 6888 | if (!event) {
|
|---|
| 6889 | return;
|
|---|
| 6890 | }
|
|---|
| 6891 |
|
|---|
| 6892 | if (event.stopPropagation) {
|
|---|
| 6893 | event.stopPropagation(); // $FlowFixMe - flow is not aware of `unknown` in IE
|
|---|
| 6894 | } else if (typeof event.cancelBubble !== 'unknown') {
|
|---|
| 6895 | // The ChangeEventPlugin registers a "propertychange" event for
|
|---|
| 6896 | // IE. This event does not support bubbling or cancelling, and
|
|---|
| 6897 | // any references to cancelBubble throw "Member not found". A
|
|---|
| 6898 | // typeof check of "unknown" circumvents this issue (and is also
|
|---|
| 6899 | // IE specific).
|
|---|
| 6900 | event.cancelBubble = true;
|
|---|
| 6901 | }
|
|---|
| 6902 |
|
|---|
| 6903 | this.isPropagationStopped = functionThatReturnsTrue;
|
|---|
| 6904 | },
|
|---|
| 6905 |
|
|---|
| 6906 | /**
|
|---|
| 6907 | * We release all dispatched `SyntheticEvent`s after each event loop, adding
|
|---|
| 6908 | * them back into the pool. This allows a way to hold onto a reference that
|
|---|
| 6909 | * won't be added back into the pool.
|
|---|
| 6910 | */
|
|---|
| 6911 | persist: function () {// Modern event system doesn't use pooling.
|
|---|
| 6912 | },
|
|---|
| 6913 |
|
|---|
| 6914 | /**
|
|---|
| 6915 | * Checks if this event should be released back into the pool.
|
|---|
| 6916 | *
|
|---|
| 6917 | * @return {boolean} True if this should not be released, false otherwise.
|
|---|
| 6918 | */
|
|---|
| 6919 | isPersistent: functionThatReturnsTrue
|
|---|
| 6920 | });
|
|---|
| 6921 | return SyntheticBaseEvent;
|
|---|
| 6922 | }
|
|---|
| 6923 | /**
|
|---|
| 6924 | * @interface Event
|
|---|
| 6925 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 6926 | */
|
|---|
| 6927 |
|
|---|
| 6928 |
|
|---|
| 6929 | var EventInterface = {
|
|---|
| 6930 | eventPhase: 0,
|
|---|
| 6931 | bubbles: 0,
|
|---|
| 6932 | cancelable: 0,
|
|---|
| 6933 | timeStamp: function (event) {
|
|---|
| 6934 | return event.timeStamp || Date.now();
|
|---|
| 6935 | },
|
|---|
| 6936 | defaultPrevented: 0,
|
|---|
| 6937 | isTrusted: 0
|
|---|
| 6938 | };
|
|---|
| 6939 | var SyntheticEvent = createSyntheticEvent(EventInterface);
|
|---|
| 6940 |
|
|---|
| 6941 | var UIEventInterface = assign({}, EventInterface, {
|
|---|
| 6942 | view: 0,
|
|---|
| 6943 | detail: 0
|
|---|
| 6944 | });
|
|---|
| 6945 |
|
|---|
| 6946 | var SyntheticUIEvent = createSyntheticEvent(UIEventInterface);
|
|---|
| 6947 | var lastMovementX;
|
|---|
| 6948 | var lastMovementY;
|
|---|
| 6949 | var lastMouseEvent;
|
|---|
| 6950 |
|
|---|
| 6951 | function updateMouseMovementPolyfillState(event) {
|
|---|
| 6952 | if (event !== lastMouseEvent) {
|
|---|
| 6953 | if (lastMouseEvent && event.type === 'mousemove') {
|
|---|
| 6954 | lastMovementX = event.screenX - lastMouseEvent.screenX;
|
|---|
| 6955 | lastMovementY = event.screenY - lastMouseEvent.screenY;
|
|---|
| 6956 | } else {
|
|---|
| 6957 | lastMovementX = 0;
|
|---|
| 6958 | lastMovementY = 0;
|
|---|
| 6959 | }
|
|---|
| 6960 |
|
|---|
| 6961 | lastMouseEvent = event;
|
|---|
| 6962 | }
|
|---|
| 6963 | }
|
|---|
| 6964 | /**
|
|---|
| 6965 | * @interface MouseEvent
|
|---|
| 6966 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 6967 | */
|
|---|
| 6968 |
|
|---|
| 6969 |
|
|---|
| 6970 | var MouseEventInterface = assign({}, UIEventInterface, {
|
|---|
| 6971 | screenX: 0,
|
|---|
| 6972 | screenY: 0,
|
|---|
| 6973 | clientX: 0,
|
|---|
| 6974 | clientY: 0,
|
|---|
| 6975 | pageX: 0,
|
|---|
| 6976 | pageY: 0,
|
|---|
| 6977 | ctrlKey: 0,
|
|---|
| 6978 | shiftKey: 0,
|
|---|
| 6979 | altKey: 0,
|
|---|
| 6980 | metaKey: 0,
|
|---|
| 6981 | getModifierState: getEventModifierState,
|
|---|
| 6982 | button: 0,
|
|---|
| 6983 | buttons: 0,
|
|---|
| 6984 | relatedTarget: function (event) {
|
|---|
| 6985 | if (event.relatedTarget === undefined) return event.fromElement === event.srcElement ? event.toElement : event.fromElement;
|
|---|
| 6986 | return event.relatedTarget;
|
|---|
| 6987 | },
|
|---|
| 6988 | movementX: function (event) {
|
|---|
| 6989 | if ('movementX' in event) {
|
|---|
| 6990 | return event.movementX;
|
|---|
| 6991 | }
|
|---|
| 6992 |
|
|---|
| 6993 | updateMouseMovementPolyfillState(event);
|
|---|
| 6994 | return lastMovementX;
|
|---|
| 6995 | },
|
|---|
| 6996 | movementY: function (event) {
|
|---|
| 6997 | if ('movementY' in event) {
|
|---|
| 6998 | return event.movementY;
|
|---|
| 6999 | } // Don't need to call updateMouseMovementPolyfillState() here
|
|---|
| 7000 | // because it's guaranteed to have already run when movementX
|
|---|
| 7001 | // was copied.
|
|---|
| 7002 |
|
|---|
| 7003 |
|
|---|
| 7004 | return lastMovementY;
|
|---|
| 7005 | }
|
|---|
| 7006 | });
|
|---|
| 7007 |
|
|---|
| 7008 | var SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface);
|
|---|
| 7009 | /**
|
|---|
| 7010 | * @interface DragEvent
|
|---|
| 7011 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 7012 | */
|
|---|
| 7013 |
|
|---|
| 7014 | var DragEventInterface = assign({}, MouseEventInterface, {
|
|---|
| 7015 | dataTransfer: 0
|
|---|
| 7016 | });
|
|---|
| 7017 |
|
|---|
| 7018 | var SyntheticDragEvent = createSyntheticEvent(DragEventInterface);
|
|---|
| 7019 | /**
|
|---|
| 7020 | * @interface FocusEvent
|
|---|
| 7021 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 7022 | */
|
|---|
| 7023 |
|
|---|
| 7024 | var FocusEventInterface = assign({}, UIEventInterface, {
|
|---|
| 7025 | relatedTarget: 0
|
|---|
| 7026 | });
|
|---|
| 7027 |
|
|---|
| 7028 | var SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface);
|
|---|
| 7029 | /**
|
|---|
| 7030 | * @interface Event
|
|---|
| 7031 | * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
|
|---|
| 7032 | * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
|
|---|
| 7033 | */
|
|---|
| 7034 |
|
|---|
| 7035 | var AnimationEventInterface = assign({}, EventInterface, {
|
|---|
| 7036 | animationName: 0,
|
|---|
| 7037 | elapsedTime: 0,
|
|---|
| 7038 | pseudoElement: 0
|
|---|
| 7039 | });
|
|---|
| 7040 |
|
|---|
| 7041 | var SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface);
|
|---|
| 7042 | /**
|
|---|
| 7043 | * @interface Event
|
|---|
| 7044 | * @see http://www.w3.org/TR/clipboard-apis/
|
|---|
| 7045 | */
|
|---|
| 7046 |
|
|---|
| 7047 | var ClipboardEventInterface = assign({}, EventInterface, {
|
|---|
| 7048 | clipboardData: function (event) {
|
|---|
| 7049 | return 'clipboardData' in event ? event.clipboardData : window.clipboardData;
|
|---|
| 7050 | }
|
|---|
| 7051 | });
|
|---|
| 7052 |
|
|---|
| 7053 | var SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface);
|
|---|
| 7054 | /**
|
|---|
| 7055 | * @interface Event
|
|---|
| 7056 | * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
|
|---|
| 7057 | */
|
|---|
| 7058 |
|
|---|
| 7059 | var CompositionEventInterface = assign({}, EventInterface, {
|
|---|
| 7060 | data: 0
|
|---|
| 7061 | });
|
|---|
| 7062 |
|
|---|
| 7063 | var SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface);
|
|---|
| 7064 | /**
|
|---|
| 7065 | * @interface Event
|
|---|
| 7066 | * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105
|
|---|
| 7067 | * /#events-inputevents
|
|---|
| 7068 | */
|
|---|
| 7069 | // Happens to share the same list for now.
|
|---|
| 7070 |
|
|---|
| 7071 | var SyntheticInputEvent = SyntheticCompositionEvent;
|
|---|
| 7072 | /**
|
|---|
| 7073 | * Normalization of deprecated HTML5 `key` values
|
|---|
| 7074 | * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
|
|---|
| 7075 | */
|
|---|
| 7076 |
|
|---|
| 7077 | var normalizeKey = {
|
|---|
| 7078 | Esc: 'Escape',
|
|---|
| 7079 | Spacebar: ' ',
|
|---|
| 7080 | Left: 'ArrowLeft',
|
|---|
| 7081 | Up: 'ArrowUp',
|
|---|
| 7082 | Right: 'ArrowRight',
|
|---|
| 7083 | Down: 'ArrowDown',
|
|---|
| 7084 | Del: 'Delete',
|
|---|
| 7085 | Win: 'OS',
|
|---|
| 7086 | Menu: 'ContextMenu',
|
|---|
| 7087 | Apps: 'ContextMenu',
|
|---|
| 7088 | Scroll: 'ScrollLock',
|
|---|
| 7089 | MozPrintableKey: 'Unidentified'
|
|---|
| 7090 | };
|
|---|
| 7091 | /**
|
|---|
| 7092 | * Translation from legacy `keyCode` to HTML5 `key`
|
|---|
| 7093 | * Only special keys supported, all others depend on keyboard layout or browser
|
|---|
| 7094 | * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names
|
|---|
| 7095 | */
|
|---|
| 7096 |
|
|---|
| 7097 | var translateToKey = {
|
|---|
| 7098 | '8': 'Backspace',
|
|---|
| 7099 | '9': 'Tab',
|
|---|
| 7100 | '12': 'Clear',
|
|---|
| 7101 | '13': 'Enter',
|
|---|
| 7102 | '16': 'Shift',
|
|---|
| 7103 | '17': 'Control',
|
|---|
| 7104 | '18': 'Alt',
|
|---|
| 7105 | '19': 'Pause',
|
|---|
| 7106 | '20': 'CapsLock',
|
|---|
| 7107 | '27': 'Escape',
|
|---|
| 7108 | '32': ' ',
|
|---|
| 7109 | '33': 'PageUp',
|
|---|
| 7110 | '34': 'PageDown',
|
|---|
| 7111 | '35': 'End',
|
|---|
| 7112 | '36': 'Home',
|
|---|
| 7113 | '37': 'ArrowLeft',
|
|---|
| 7114 | '38': 'ArrowUp',
|
|---|
| 7115 | '39': 'ArrowRight',
|
|---|
| 7116 | '40': 'ArrowDown',
|
|---|
| 7117 | '45': 'Insert',
|
|---|
| 7118 | '46': 'Delete',
|
|---|
| 7119 | '112': 'F1',
|
|---|
| 7120 | '113': 'F2',
|
|---|
| 7121 | '114': 'F3',
|
|---|
| 7122 | '115': 'F4',
|
|---|
| 7123 | '116': 'F5',
|
|---|
| 7124 | '117': 'F6',
|
|---|
| 7125 | '118': 'F7',
|
|---|
| 7126 | '119': 'F8',
|
|---|
| 7127 | '120': 'F9',
|
|---|
| 7128 | '121': 'F10',
|
|---|
| 7129 | '122': 'F11',
|
|---|
| 7130 | '123': 'F12',
|
|---|
| 7131 | '144': 'NumLock',
|
|---|
| 7132 | '145': 'ScrollLock',
|
|---|
| 7133 | '224': 'Meta'
|
|---|
| 7134 | };
|
|---|
| 7135 | /**
|
|---|
| 7136 | * @param {object} nativeEvent Native browser event.
|
|---|
| 7137 | * @return {string} Normalized `key` property.
|
|---|
| 7138 | */
|
|---|
| 7139 |
|
|---|
| 7140 | function getEventKey(nativeEvent) {
|
|---|
| 7141 | if (nativeEvent.key) {
|
|---|
| 7142 | // Normalize inconsistent values reported by browsers due to
|
|---|
| 7143 | // implementations of a working draft specification.
|
|---|
| 7144 | // FireFox implements `key` but returns `MozPrintableKey` for all
|
|---|
| 7145 | // printable characters (normalized to `Unidentified`), ignore it.
|
|---|
| 7146 | var key = normalizeKey[nativeEvent.key] || nativeEvent.key;
|
|---|
| 7147 |
|
|---|
| 7148 | if (key !== 'Unidentified') {
|
|---|
| 7149 | return key;
|
|---|
| 7150 | }
|
|---|
| 7151 | } // Browser does not implement `key`, polyfill as much of it as we can.
|
|---|
| 7152 |
|
|---|
| 7153 |
|
|---|
| 7154 | if (nativeEvent.type === 'keypress') {
|
|---|
| 7155 | var charCode = getEventCharCode(nativeEvent); // The enter-key is technically both printable and non-printable and can
|
|---|
| 7156 | // thus be captured by `keypress`, no other non-printable key should.
|
|---|
| 7157 |
|
|---|
| 7158 | return charCode === 13 ? 'Enter' : String.fromCharCode(charCode);
|
|---|
| 7159 | }
|
|---|
| 7160 |
|
|---|
| 7161 | if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') {
|
|---|
| 7162 | // While user keyboard layout determines the actual meaning of each
|
|---|
| 7163 | // `keyCode` value, almost all function keys have a universal value.
|
|---|
| 7164 | return translateToKey[nativeEvent.keyCode] || 'Unidentified';
|
|---|
| 7165 | }
|
|---|
| 7166 |
|
|---|
| 7167 | return '';
|
|---|
| 7168 | }
|
|---|
| 7169 | /**
|
|---|
| 7170 | * Translation from modifier key to the associated property in the event.
|
|---|
| 7171 | * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers
|
|---|
| 7172 | */
|
|---|
| 7173 |
|
|---|
| 7174 |
|
|---|
| 7175 | var modifierKeyToProp = {
|
|---|
| 7176 | Alt: 'altKey',
|
|---|
| 7177 | Control: 'ctrlKey',
|
|---|
| 7178 | Meta: 'metaKey',
|
|---|
| 7179 | Shift: 'shiftKey'
|
|---|
| 7180 | }; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support
|
|---|
| 7181 | // getModifierState. If getModifierState is not supported, we map it to a set of
|
|---|
| 7182 | // modifier keys exposed by the event. In this case, Lock-keys are not supported.
|
|---|
| 7183 |
|
|---|
| 7184 | function modifierStateGetter(keyArg) {
|
|---|
| 7185 | var syntheticEvent = this;
|
|---|
| 7186 | var nativeEvent = syntheticEvent.nativeEvent;
|
|---|
| 7187 |
|
|---|
| 7188 | if (nativeEvent.getModifierState) {
|
|---|
| 7189 | return nativeEvent.getModifierState(keyArg);
|
|---|
| 7190 | }
|
|---|
| 7191 |
|
|---|
| 7192 | var keyProp = modifierKeyToProp[keyArg];
|
|---|
| 7193 | return keyProp ? !!nativeEvent[keyProp] : false;
|
|---|
| 7194 | }
|
|---|
| 7195 |
|
|---|
| 7196 | function getEventModifierState(nativeEvent) {
|
|---|
| 7197 | return modifierStateGetter;
|
|---|
| 7198 | }
|
|---|
| 7199 | /**
|
|---|
| 7200 | * @interface KeyboardEvent
|
|---|
| 7201 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 7202 | */
|
|---|
| 7203 |
|
|---|
| 7204 |
|
|---|
| 7205 | var KeyboardEventInterface = assign({}, UIEventInterface, {
|
|---|
| 7206 | key: getEventKey,
|
|---|
| 7207 | code: 0,
|
|---|
| 7208 | location: 0,
|
|---|
| 7209 | ctrlKey: 0,
|
|---|
| 7210 | shiftKey: 0,
|
|---|
| 7211 | altKey: 0,
|
|---|
| 7212 | metaKey: 0,
|
|---|
| 7213 | repeat: 0,
|
|---|
| 7214 | locale: 0,
|
|---|
| 7215 | getModifierState: getEventModifierState,
|
|---|
| 7216 | // Legacy Interface
|
|---|
| 7217 | charCode: function (event) {
|
|---|
| 7218 | // `charCode` is the result of a KeyPress event and represents the value of
|
|---|
| 7219 | // the actual printable character.
|
|---|
| 7220 | // KeyPress is deprecated, but its replacement is not yet final and not
|
|---|
| 7221 | // implemented in any major browser. Only KeyPress has charCode.
|
|---|
| 7222 | if (event.type === 'keypress') {
|
|---|
| 7223 | return getEventCharCode(event);
|
|---|
| 7224 | }
|
|---|
| 7225 |
|
|---|
| 7226 | return 0;
|
|---|
| 7227 | },
|
|---|
| 7228 | keyCode: function (event) {
|
|---|
| 7229 | // `keyCode` is the result of a KeyDown/Up event and represents the value of
|
|---|
| 7230 | // physical keyboard key.
|
|---|
| 7231 | // The actual meaning of the value depends on the users' keyboard layout
|
|---|
| 7232 | // which cannot be detected. Assuming that it is a US keyboard layout
|
|---|
| 7233 | // provides a surprisingly accurate mapping for US and European users.
|
|---|
| 7234 | // Due to this, it is left to the user to implement at this time.
|
|---|
| 7235 | if (event.type === 'keydown' || event.type === 'keyup') {
|
|---|
| 7236 | return event.keyCode;
|
|---|
| 7237 | }
|
|---|
| 7238 |
|
|---|
| 7239 | return 0;
|
|---|
| 7240 | },
|
|---|
| 7241 | which: function (event) {
|
|---|
| 7242 | // `which` is an alias for either `keyCode` or `charCode` depending on the
|
|---|
| 7243 | // type of the event.
|
|---|
| 7244 | if (event.type === 'keypress') {
|
|---|
| 7245 | return getEventCharCode(event);
|
|---|
| 7246 | }
|
|---|
| 7247 |
|
|---|
| 7248 | if (event.type === 'keydown' || event.type === 'keyup') {
|
|---|
| 7249 | return event.keyCode;
|
|---|
| 7250 | }
|
|---|
| 7251 |
|
|---|
| 7252 | return 0;
|
|---|
| 7253 | }
|
|---|
| 7254 | });
|
|---|
| 7255 |
|
|---|
| 7256 | var SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface);
|
|---|
| 7257 | /**
|
|---|
| 7258 | * @interface PointerEvent
|
|---|
| 7259 | * @see http://www.w3.org/TR/pointerevents/
|
|---|
| 7260 | */
|
|---|
| 7261 |
|
|---|
| 7262 | var PointerEventInterface = assign({}, MouseEventInterface, {
|
|---|
| 7263 | pointerId: 0,
|
|---|
| 7264 | width: 0,
|
|---|
| 7265 | height: 0,
|
|---|
| 7266 | pressure: 0,
|
|---|
| 7267 | tangentialPressure: 0,
|
|---|
| 7268 | tiltX: 0,
|
|---|
| 7269 | tiltY: 0,
|
|---|
| 7270 | twist: 0,
|
|---|
| 7271 | pointerType: 0,
|
|---|
| 7272 | isPrimary: 0
|
|---|
| 7273 | });
|
|---|
| 7274 |
|
|---|
| 7275 | var SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface);
|
|---|
| 7276 | /**
|
|---|
| 7277 | * @interface TouchEvent
|
|---|
| 7278 | * @see http://www.w3.org/TR/touch-events/
|
|---|
| 7279 | */
|
|---|
| 7280 |
|
|---|
| 7281 | var TouchEventInterface = assign({}, UIEventInterface, {
|
|---|
| 7282 | touches: 0,
|
|---|
| 7283 | targetTouches: 0,
|
|---|
| 7284 | changedTouches: 0,
|
|---|
| 7285 | altKey: 0,
|
|---|
| 7286 | metaKey: 0,
|
|---|
| 7287 | ctrlKey: 0,
|
|---|
| 7288 | shiftKey: 0,
|
|---|
| 7289 | getModifierState: getEventModifierState
|
|---|
| 7290 | });
|
|---|
| 7291 |
|
|---|
| 7292 | var SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface);
|
|---|
| 7293 | /**
|
|---|
| 7294 | * @interface Event
|
|---|
| 7295 | * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events-
|
|---|
| 7296 | * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent
|
|---|
| 7297 | */
|
|---|
| 7298 |
|
|---|
| 7299 | var TransitionEventInterface = assign({}, EventInterface, {
|
|---|
| 7300 | propertyName: 0,
|
|---|
| 7301 | elapsedTime: 0,
|
|---|
| 7302 | pseudoElement: 0
|
|---|
| 7303 | });
|
|---|
| 7304 |
|
|---|
| 7305 | var SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface);
|
|---|
| 7306 | /**
|
|---|
| 7307 | * @interface WheelEvent
|
|---|
| 7308 | * @see http://www.w3.org/TR/DOM-Level-3-Events/
|
|---|
| 7309 | */
|
|---|
| 7310 |
|
|---|
| 7311 | var WheelEventInterface = assign({}, MouseEventInterface, {
|
|---|
| 7312 | deltaX: function (event) {
|
|---|
| 7313 | return 'deltaX' in event ? event.deltaX : // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive).
|
|---|
| 7314 | 'wheelDeltaX' in event ? -event.wheelDeltaX : 0;
|
|---|
| 7315 | },
|
|---|
| 7316 | deltaY: function (event) {
|
|---|
| 7317 | return 'deltaY' in event ? event.deltaY : // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive).
|
|---|
| 7318 | 'wheelDeltaY' in event ? -event.wheelDeltaY : // Fallback to `wheelDelta` for IE<9 and normalize (down is positive).
|
|---|
| 7319 | 'wheelDelta' in event ? -event.wheelDelta : 0;
|
|---|
| 7320 | },
|
|---|
| 7321 | deltaZ: 0,
|
|---|
| 7322 | // Browsers without "deltaMode" is reporting in raw wheel delta where one
|
|---|
| 7323 | // notch on the scroll is always +/- 120, roughly equivalent to pixels.
|
|---|
| 7324 | // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or
|
|---|
| 7325 | // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size.
|
|---|
| 7326 | deltaMode: 0
|
|---|
| 7327 | });
|
|---|
| 7328 |
|
|---|
| 7329 | var SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface);
|
|---|
| 7330 |
|
|---|
| 7331 | var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space
|
|---|
| 7332 |
|
|---|
| 7333 | var START_KEYCODE = 229;
|
|---|
| 7334 | var canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window;
|
|---|
| 7335 | var documentMode = null;
|
|---|
| 7336 |
|
|---|
| 7337 | if (canUseDOM && 'documentMode' in document) {
|
|---|
| 7338 | documentMode = document.documentMode;
|
|---|
| 7339 | } // Webkit offers a very useful `textInput` event that can be used to
|
|---|
| 7340 | // directly represent `beforeInput`. The IE `textinput` event is not as
|
|---|
| 7341 | // useful, so we don't use it.
|
|---|
| 7342 |
|
|---|
| 7343 |
|
|---|
| 7344 | var canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode; // In IE9+, we have access to composition events, but the data supplied
|
|---|
| 7345 | // by the native compositionend event may be incorrect. Japanese ideographic
|
|---|
| 7346 | // spaces, for instance (\u3000) are not recorded correctly.
|
|---|
| 7347 |
|
|---|
| 7348 | var useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11);
|
|---|
| 7349 | var SPACEBAR_CODE = 32;
|
|---|
| 7350 | var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE);
|
|---|
| 7351 |
|
|---|
| 7352 | function registerEvents() {
|
|---|
| 7353 | registerTwoPhaseEvent('onBeforeInput', ['compositionend', 'keypress', 'textInput', 'paste']);
|
|---|
| 7354 | registerTwoPhaseEvent('onCompositionEnd', ['compositionend', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']);
|
|---|
| 7355 | registerTwoPhaseEvent('onCompositionStart', ['compositionstart', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']);
|
|---|
| 7356 | registerTwoPhaseEvent('onCompositionUpdate', ['compositionupdate', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']);
|
|---|
| 7357 | } // Track whether we've ever handled a keypress on the space key.
|
|---|
| 7358 |
|
|---|
| 7359 |
|
|---|
| 7360 | var hasSpaceKeypress = false;
|
|---|
| 7361 | /**
|
|---|
| 7362 | * Return whether a native keypress event is assumed to be a command.
|
|---|
| 7363 | * This is required because Firefox fires `keypress` events for key commands
|
|---|
| 7364 | * (cut, copy, select-all, etc.) even though no character is inserted.
|
|---|
| 7365 | */
|
|---|
| 7366 |
|
|---|
| 7367 | function isKeypressCommand(nativeEvent) {
|
|---|
| 7368 | return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command.
|
|---|
| 7369 | !(nativeEvent.ctrlKey && nativeEvent.altKey);
|
|---|
| 7370 | }
|
|---|
| 7371 | /**
|
|---|
| 7372 | * Translate native top level events into event types.
|
|---|
| 7373 | */
|
|---|
| 7374 |
|
|---|
| 7375 |
|
|---|
| 7376 | function getCompositionEventType(domEventName) {
|
|---|
| 7377 | switch (domEventName) {
|
|---|
| 7378 | case 'compositionstart':
|
|---|
| 7379 | return 'onCompositionStart';
|
|---|
| 7380 |
|
|---|
| 7381 | case 'compositionend':
|
|---|
| 7382 | return 'onCompositionEnd';
|
|---|
| 7383 |
|
|---|
| 7384 | case 'compositionupdate':
|
|---|
| 7385 | return 'onCompositionUpdate';
|
|---|
| 7386 | }
|
|---|
| 7387 | }
|
|---|
| 7388 | /**
|
|---|
| 7389 | * Does our fallback best-guess model think this event signifies that
|
|---|
| 7390 | * composition has begun?
|
|---|
| 7391 | */
|
|---|
| 7392 |
|
|---|
| 7393 |
|
|---|
| 7394 | function isFallbackCompositionStart(domEventName, nativeEvent) {
|
|---|
| 7395 | return domEventName === 'keydown' && nativeEvent.keyCode === START_KEYCODE;
|
|---|
| 7396 | }
|
|---|
| 7397 | /**
|
|---|
| 7398 | * Does our fallback mode think that this event is the end of composition?
|
|---|
| 7399 | */
|
|---|
| 7400 |
|
|---|
| 7401 |
|
|---|
| 7402 | function isFallbackCompositionEnd(domEventName, nativeEvent) {
|
|---|
| 7403 | switch (domEventName) {
|
|---|
| 7404 | case 'keyup':
|
|---|
| 7405 | // Command keys insert or clear IME input.
|
|---|
| 7406 | return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1;
|
|---|
| 7407 |
|
|---|
| 7408 | case 'keydown':
|
|---|
| 7409 | // Expect IME keyCode on each keydown. If we get any other
|
|---|
| 7410 | // code we must have exited earlier.
|
|---|
| 7411 | return nativeEvent.keyCode !== START_KEYCODE;
|
|---|
| 7412 |
|
|---|
| 7413 | case 'keypress':
|
|---|
| 7414 | case 'mousedown':
|
|---|
| 7415 | case 'focusout':
|
|---|
| 7416 | // Events are not possible without cancelling IME.
|
|---|
| 7417 | return true;
|
|---|
| 7418 |
|
|---|
| 7419 | default:
|
|---|
| 7420 | return false;
|
|---|
| 7421 | }
|
|---|
| 7422 | }
|
|---|
| 7423 | /**
|
|---|
| 7424 | * Google Input Tools provides composition data via a CustomEvent,
|
|---|
| 7425 | * with the `data` property populated in the `detail` object. If this
|
|---|
| 7426 | * is available on the event object, use it. If not, this is a plain
|
|---|
| 7427 | * composition event and we have nothing special to extract.
|
|---|
| 7428 | *
|
|---|
| 7429 | * @param {object} nativeEvent
|
|---|
| 7430 | * @return {?string}
|
|---|
| 7431 | */
|
|---|
| 7432 |
|
|---|
| 7433 |
|
|---|
| 7434 | function getDataFromCustomEvent(nativeEvent) {
|
|---|
| 7435 | var detail = nativeEvent.detail;
|
|---|
| 7436 |
|
|---|
| 7437 | if (typeof detail === 'object' && 'data' in detail) {
|
|---|
| 7438 | return detail.data;
|
|---|
| 7439 | }
|
|---|
| 7440 |
|
|---|
| 7441 | return null;
|
|---|
| 7442 | }
|
|---|
| 7443 | /**
|
|---|
| 7444 | * Check if a composition event was triggered by Korean IME.
|
|---|
| 7445 | * Our fallback mode does not work well with IE's Korean IME,
|
|---|
| 7446 | * so just use native composition events when Korean IME is used.
|
|---|
| 7447 | * Although CompositionEvent.locale property is deprecated,
|
|---|
| 7448 | * it is available in IE, where our fallback mode is enabled.
|
|---|
| 7449 | *
|
|---|
| 7450 | * @param {object} nativeEvent
|
|---|
| 7451 | * @return {boolean}
|
|---|
| 7452 | */
|
|---|
| 7453 |
|
|---|
| 7454 |
|
|---|
| 7455 | function isUsingKoreanIME(nativeEvent) {
|
|---|
| 7456 | return nativeEvent.locale === 'ko';
|
|---|
| 7457 | } // Track the current IME composition status, if any.
|
|---|
| 7458 |
|
|---|
| 7459 |
|
|---|
| 7460 | var isComposing = false;
|
|---|
| 7461 | /**
|
|---|
| 7462 | * @return {?object} A SyntheticCompositionEvent.
|
|---|
| 7463 | */
|
|---|
| 7464 |
|
|---|
| 7465 | function extractCompositionEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget) {
|
|---|
| 7466 | var eventType;
|
|---|
| 7467 | var fallbackData;
|
|---|
| 7468 |
|
|---|
| 7469 | if (canUseCompositionEvent) {
|
|---|
| 7470 | eventType = getCompositionEventType(domEventName);
|
|---|
| 7471 | } else if (!isComposing) {
|
|---|
| 7472 | if (isFallbackCompositionStart(domEventName, nativeEvent)) {
|
|---|
| 7473 | eventType = 'onCompositionStart';
|
|---|
| 7474 | }
|
|---|
| 7475 | } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) {
|
|---|
| 7476 | eventType = 'onCompositionEnd';
|
|---|
| 7477 | }
|
|---|
| 7478 |
|
|---|
| 7479 | if (!eventType) {
|
|---|
| 7480 | return null;
|
|---|
| 7481 | }
|
|---|
| 7482 |
|
|---|
| 7483 | if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) {
|
|---|
| 7484 | // The current composition is stored statically and must not be
|
|---|
| 7485 | // overwritten while composition continues.
|
|---|
| 7486 | if (!isComposing && eventType === 'onCompositionStart') {
|
|---|
| 7487 | isComposing = initialize(nativeEventTarget);
|
|---|
| 7488 | } else if (eventType === 'onCompositionEnd') {
|
|---|
| 7489 | if (isComposing) {
|
|---|
| 7490 | fallbackData = getData();
|
|---|
| 7491 | }
|
|---|
| 7492 | }
|
|---|
| 7493 | }
|
|---|
| 7494 |
|
|---|
| 7495 | var listeners = accumulateTwoPhaseListeners(targetInst, eventType);
|
|---|
| 7496 |
|
|---|
| 7497 | if (listeners.length > 0) {
|
|---|
| 7498 | var event = new SyntheticCompositionEvent(eventType, domEventName, null, nativeEvent, nativeEventTarget);
|
|---|
| 7499 | dispatchQueue.push({
|
|---|
| 7500 | event: event,
|
|---|
| 7501 | listeners: listeners
|
|---|
| 7502 | });
|
|---|
| 7503 |
|
|---|
| 7504 | if (fallbackData) {
|
|---|
| 7505 | // Inject data generated from fallback path into the synthetic event.
|
|---|
| 7506 | // This matches the property of native CompositionEventInterface.
|
|---|
| 7507 | event.data = fallbackData;
|
|---|
| 7508 | } else {
|
|---|
| 7509 | var customData = getDataFromCustomEvent(nativeEvent);
|
|---|
| 7510 |
|
|---|
| 7511 | if (customData !== null) {
|
|---|
| 7512 | event.data = customData;
|
|---|
| 7513 | }
|
|---|
| 7514 | }
|
|---|
| 7515 | }
|
|---|
| 7516 | }
|
|---|
| 7517 |
|
|---|
| 7518 | function getNativeBeforeInputChars(domEventName, nativeEvent) {
|
|---|
| 7519 | switch (domEventName) {
|
|---|
| 7520 | case 'compositionend':
|
|---|
| 7521 | return getDataFromCustomEvent(nativeEvent);
|
|---|
| 7522 |
|
|---|
| 7523 | case 'keypress':
|
|---|
| 7524 | /**
|
|---|
| 7525 | * If native `textInput` events are available, our goal is to make
|
|---|
| 7526 | * use of them. However, there is a special case: the spacebar key.
|
|---|
| 7527 | * In Webkit, preventing default on a spacebar `textInput` event
|
|---|
| 7528 | * cancels character insertion, but it *also* causes the browser
|
|---|
| 7529 | * to fall back to its default spacebar behavior of scrolling the
|
|---|
| 7530 | * page.
|
|---|
| 7531 | *
|
|---|
| 7532 | * Tracking at:
|
|---|
| 7533 | * https://code.google.com/p/chromium/issues/detail?id=355103
|
|---|
| 7534 | *
|
|---|
| 7535 | * To avoid this issue, use the keypress event as if no `textInput`
|
|---|
| 7536 | * event is available.
|
|---|
| 7537 | */
|
|---|
| 7538 | var which = nativeEvent.which;
|
|---|
| 7539 |
|
|---|
| 7540 | if (which !== SPACEBAR_CODE) {
|
|---|
| 7541 | return null;
|
|---|
| 7542 | }
|
|---|
| 7543 |
|
|---|
| 7544 | hasSpaceKeypress = true;
|
|---|
| 7545 | return SPACEBAR_CHAR;
|
|---|
| 7546 |
|
|---|
| 7547 | case 'textInput':
|
|---|
| 7548 | // Record the characters to be added to the DOM.
|
|---|
| 7549 | var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled
|
|---|
| 7550 | // it at the keypress level and bail immediately. Android Chrome
|
|---|
| 7551 | // doesn't give us keycodes, so we need to ignore it.
|
|---|
| 7552 |
|
|---|
| 7553 | if (chars === SPACEBAR_CHAR && hasSpaceKeypress) {
|
|---|
| 7554 | return null;
|
|---|
| 7555 | }
|
|---|
| 7556 |
|
|---|
| 7557 | return chars;
|
|---|
| 7558 |
|
|---|
| 7559 | default:
|
|---|
| 7560 | // For other native event types, do nothing.
|
|---|
| 7561 | return null;
|
|---|
| 7562 | }
|
|---|
| 7563 | }
|
|---|
| 7564 | /**
|
|---|
| 7565 | * For browsers that do not provide the `textInput` event, extract the
|
|---|
| 7566 | * appropriate string to use for SyntheticInputEvent.
|
|---|
| 7567 | */
|
|---|
| 7568 |
|
|---|
| 7569 |
|
|---|
| 7570 | function getFallbackBeforeInputChars(domEventName, nativeEvent) {
|
|---|
| 7571 | // If we are currently composing (IME) and using a fallback to do so,
|
|---|
| 7572 | // try to extract the composed characters from the fallback object.
|
|---|
| 7573 | // If composition event is available, we extract a string only at
|
|---|
| 7574 | // compositionevent, otherwise extract it at fallback events.
|
|---|
| 7575 | if (isComposing) {
|
|---|
| 7576 | if (domEventName === 'compositionend' || !canUseCompositionEvent && isFallbackCompositionEnd(domEventName, nativeEvent)) {
|
|---|
| 7577 | var chars = getData();
|
|---|
| 7578 | reset();
|
|---|
| 7579 | isComposing = false;
|
|---|
| 7580 | return chars;
|
|---|
| 7581 | }
|
|---|
| 7582 |
|
|---|
| 7583 | return null;
|
|---|
| 7584 | }
|
|---|
| 7585 |
|
|---|
| 7586 | switch (domEventName) {
|
|---|
| 7587 | case 'paste':
|
|---|
| 7588 | // If a paste event occurs after a keypress, throw out the input
|
|---|
| 7589 | // chars. Paste events should not lead to BeforeInput events.
|
|---|
| 7590 | return null;
|
|---|
| 7591 |
|
|---|
| 7592 | case 'keypress':
|
|---|
| 7593 | /**
|
|---|
| 7594 | * As of v27, Firefox may fire keypress events even when no character
|
|---|
| 7595 | * will be inserted. A few possibilities:
|
|---|
| 7596 | *
|
|---|
| 7597 | * - `which` is `0`. Arrow keys, Esc key, etc.
|
|---|
| 7598 | *
|
|---|
| 7599 | * - `which` is the pressed key code, but no char is available.
|
|---|
| 7600 | * Ex: 'AltGr + d` in Polish. There is no modified character for
|
|---|
| 7601 | * this key combination and no character is inserted into the
|
|---|
| 7602 | * document, but FF fires the keypress for char code `100` anyway.
|
|---|
| 7603 | * No `input` event will occur.
|
|---|
| 7604 | *
|
|---|
| 7605 | * - `which` is the pressed key code, but a command combination is
|
|---|
| 7606 | * being used. Ex: `Cmd+C`. No character is inserted, and no
|
|---|
| 7607 | * `input` event will occur.
|
|---|
| 7608 | */
|
|---|
| 7609 | if (!isKeypressCommand(nativeEvent)) {
|
|---|
| 7610 | // IE fires the `keypress` event when a user types an emoji via
|
|---|
| 7611 | // Touch keyboard of Windows. In such a case, the `char` property
|
|---|
| 7612 | // holds an emoji character like `\uD83D\uDE0A`. Because its length
|
|---|
| 7613 | // is 2, the property `which` does not represent an emoji correctly.
|
|---|
| 7614 | // In such a case, we directly return the `char` property instead of
|
|---|
| 7615 | // using `which`.
|
|---|
| 7616 | if (nativeEvent.char && nativeEvent.char.length > 1) {
|
|---|
| 7617 | return nativeEvent.char;
|
|---|
| 7618 | } else if (nativeEvent.which) {
|
|---|
| 7619 | return String.fromCharCode(nativeEvent.which);
|
|---|
| 7620 | }
|
|---|
| 7621 | }
|
|---|
| 7622 |
|
|---|
| 7623 | return null;
|
|---|
| 7624 |
|
|---|
| 7625 | case 'compositionend':
|
|---|
| 7626 | return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data;
|
|---|
| 7627 |
|
|---|
| 7628 | default:
|
|---|
| 7629 | return null;
|
|---|
| 7630 | }
|
|---|
| 7631 | }
|
|---|
| 7632 | /**
|
|---|
| 7633 | * Extract a SyntheticInputEvent for `beforeInput`, based on either native
|
|---|
| 7634 | * `textInput` or fallback behavior.
|
|---|
| 7635 | *
|
|---|
| 7636 | * @return {?object} A SyntheticInputEvent.
|
|---|
| 7637 | */
|
|---|
| 7638 |
|
|---|
| 7639 |
|
|---|
| 7640 | function extractBeforeInputEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget) {
|
|---|
| 7641 | var chars;
|
|---|
| 7642 |
|
|---|
| 7643 | if (canUseTextInputEvent) {
|
|---|
| 7644 | chars = getNativeBeforeInputChars(domEventName, nativeEvent);
|
|---|
| 7645 | } else {
|
|---|
| 7646 | chars = getFallbackBeforeInputChars(domEventName, nativeEvent);
|
|---|
| 7647 | } // If no characters are being inserted, no BeforeInput event should
|
|---|
| 7648 | // be fired.
|
|---|
| 7649 |
|
|---|
| 7650 |
|
|---|
| 7651 | if (!chars) {
|
|---|
| 7652 | return null;
|
|---|
| 7653 | }
|
|---|
| 7654 |
|
|---|
| 7655 | var listeners = accumulateTwoPhaseListeners(targetInst, 'onBeforeInput');
|
|---|
| 7656 |
|
|---|
| 7657 | if (listeners.length > 0) {
|
|---|
| 7658 | var event = new SyntheticInputEvent('onBeforeInput', 'beforeinput', null, nativeEvent, nativeEventTarget);
|
|---|
| 7659 | dispatchQueue.push({
|
|---|
| 7660 | event: event,
|
|---|
| 7661 | listeners: listeners
|
|---|
| 7662 | });
|
|---|
| 7663 | event.data = chars;
|
|---|
| 7664 | }
|
|---|
| 7665 | }
|
|---|
| 7666 | /**
|
|---|
| 7667 | * Create an `onBeforeInput` event to match
|
|---|
| 7668 | * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents.
|
|---|
| 7669 | *
|
|---|
| 7670 | * This event plugin is based on the native `textInput` event
|
|---|
| 7671 | * available in Chrome, Safari, Opera, and IE. This event fires after
|
|---|
| 7672 | * `onKeyPress` and `onCompositionEnd`, but before `onInput`.
|
|---|
| 7673 | *
|
|---|
| 7674 | * `beforeInput` is spec'd but not implemented in any browsers, and
|
|---|
| 7675 | * the `input` event does not provide any useful information about what has
|
|---|
| 7676 | * actually been added, contrary to the spec. Thus, `textInput` is the best
|
|---|
| 7677 | * available event to identify the characters that have actually been inserted
|
|---|
| 7678 | * into the target node.
|
|---|
| 7679 | *
|
|---|
| 7680 | * This plugin is also responsible for emitting `composition` events, thus
|
|---|
| 7681 | * allowing us to share composition fallback code for both `beforeInput` and
|
|---|
| 7682 | * `composition` event types.
|
|---|
| 7683 | */
|
|---|
| 7684 |
|
|---|
| 7685 |
|
|---|
| 7686 | function extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 7687 | extractCompositionEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 7688 | extractBeforeInputEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 7689 | }
|
|---|
| 7690 |
|
|---|
| 7691 | /**
|
|---|
| 7692 | * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary
|
|---|
| 7693 | */
|
|---|
| 7694 | var supportedInputTypes = {
|
|---|
| 7695 | color: true,
|
|---|
| 7696 | date: true,
|
|---|
| 7697 | datetime: true,
|
|---|
| 7698 | 'datetime-local': true,
|
|---|
| 7699 | email: true,
|
|---|
| 7700 | month: true,
|
|---|
| 7701 | number: true,
|
|---|
| 7702 | password: true,
|
|---|
| 7703 | range: true,
|
|---|
| 7704 | search: true,
|
|---|
| 7705 | tel: true,
|
|---|
| 7706 | text: true,
|
|---|
| 7707 | time: true,
|
|---|
| 7708 | url: true,
|
|---|
| 7709 | week: true
|
|---|
| 7710 | };
|
|---|
| 7711 |
|
|---|
| 7712 | function isTextInputElement(elem) {
|
|---|
| 7713 | var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
|
|---|
| 7714 |
|
|---|
| 7715 | if (nodeName === 'input') {
|
|---|
| 7716 | return !!supportedInputTypes[elem.type];
|
|---|
| 7717 | }
|
|---|
| 7718 |
|
|---|
| 7719 | if (nodeName === 'textarea') {
|
|---|
| 7720 | return true;
|
|---|
| 7721 | }
|
|---|
| 7722 |
|
|---|
| 7723 | return false;
|
|---|
| 7724 | }
|
|---|
| 7725 |
|
|---|
| 7726 | /**
|
|---|
| 7727 | * Checks if an event is supported in the current execution environment.
|
|---|
| 7728 | *
|
|---|
| 7729 | * NOTE: This will not work correctly for non-generic events such as `change`,
|
|---|
| 7730 | * `reset`, `load`, `error`, and `select`.
|
|---|
| 7731 | *
|
|---|
| 7732 | * Borrows from Modernizr.
|
|---|
| 7733 | *
|
|---|
| 7734 | * @param {string} eventNameSuffix Event name, e.g. "click".
|
|---|
| 7735 | * @return {boolean} True if the event is supported.
|
|---|
| 7736 | * @internal
|
|---|
| 7737 | * @license Modernizr 3.0.0pre (Custom Build) | MIT
|
|---|
| 7738 | */
|
|---|
| 7739 |
|
|---|
| 7740 | function isEventSupported(eventNameSuffix) {
|
|---|
| 7741 | if (!canUseDOM) {
|
|---|
| 7742 | return false;
|
|---|
| 7743 | }
|
|---|
| 7744 |
|
|---|
| 7745 | var eventName = 'on' + eventNameSuffix;
|
|---|
| 7746 | var isSupported = (eventName in document);
|
|---|
| 7747 |
|
|---|
| 7748 | if (!isSupported) {
|
|---|
| 7749 | var element = document.createElement('div');
|
|---|
| 7750 | element.setAttribute(eventName, 'return;');
|
|---|
| 7751 | isSupported = typeof element[eventName] === 'function';
|
|---|
| 7752 | }
|
|---|
| 7753 |
|
|---|
| 7754 | return isSupported;
|
|---|
| 7755 | }
|
|---|
| 7756 |
|
|---|
| 7757 | function registerEvents$1() {
|
|---|
| 7758 | registerTwoPhaseEvent('onChange', ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']);
|
|---|
| 7759 | }
|
|---|
| 7760 |
|
|---|
| 7761 | function createAndAccumulateChangeEvent(dispatchQueue, inst, nativeEvent, target) {
|
|---|
| 7762 | // Flag this event loop as needing state restore.
|
|---|
| 7763 | enqueueStateRestore(target);
|
|---|
| 7764 | var listeners = accumulateTwoPhaseListeners(inst, 'onChange');
|
|---|
| 7765 |
|
|---|
| 7766 | if (listeners.length > 0) {
|
|---|
| 7767 | var event = new SyntheticEvent('onChange', 'change', null, nativeEvent, target);
|
|---|
| 7768 | dispatchQueue.push({
|
|---|
| 7769 | event: event,
|
|---|
| 7770 | listeners: listeners
|
|---|
| 7771 | });
|
|---|
| 7772 | }
|
|---|
| 7773 | }
|
|---|
| 7774 | /**
|
|---|
| 7775 | * For IE shims
|
|---|
| 7776 | */
|
|---|
| 7777 |
|
|---|
| 7778 |
|
|---|
| 7779 | var activeElement = null;
|
|---|
| 7780 | var activeElementInst = null;
|
|---|
| 7781 | /**
|
|---|
| 7782 | * SECTION: handle `change` event
|
|---|
| 7783 | */
|
|---|
| 7784 |
|
|---|
| 7785 | function shouldUseChangeEvent(elem) {
|
|---|
| 7786 | var nodeName = elem.nodeName && elem.nodeName.toLowerCase();
|
|---|
| 7787 | return nodeName === 'select' || nodeName === 'input' && elem.type === 'file';
|
|---|
| 7788 | }
|
|---|
| 7789 |
|
|---|
| 7790 | function manualDispatchChangeEvent(nativeEvent) {
|
|---|
| 7791 | var dispatchQueue = [];
|
|---|
| 7792 | createAndAccumulateChangeEvent(dispatchQueue, activeElementInst, nativeEvent, getEventTarget(nativeEvent)); // If change and propertychange bubbled, we'd just bind to it like all the
|
|---|
| 7793 | // other events and have it go through ReactBrowserEventEmitter. Since it
|
|---|
| 7794 | // doesn't, we manually listen for the events and so we have to enqueue and
|
|---|
| 7795 | // process the abstract event manually.
|
|---|
| 7796 | //
|
|---|
| 7797 | // Batching is necessary here in order to ensure that all event handlers run
|
|---|
| 7798 | // before the next rerender (including event handlers attached to ancestor
|
|---|
| 7799 | // elements instead of directly on the input). Without this, controlled
|
|---|
| 7800 | // components don't work properly in conjunction with event bubbling because
|
|---|
| 7801 | // the component is rerendered and the value reverted before all the event
|
|---|
| 7802 | // handlers can run. See https://github.com/facebook/react/issues/708.
|
|---|
| 7803 |
|
|---|
| 7804 | batchedUpdates(runEventInBatch, dispatchQueue);
|
|---|
| 7805 | }
|
|---|
| 7806 |
|
|---|
| 7807 | function runEventInBatch(dispatchQueue) {
|
|---|
| 7808 | processDispatchQueue(dispatchQueue, 0);
|
|---|
| 7809 | }
|
|---|
| 7810 |
|
|---|
| 7811 | function getInstIfValueChanged(targetInst) {
|
|---|
| 7812 | var targetNode = getNodeFromInstance(targetInst);
|
|---|
| 7813 |
|
|---|
| 7814 | if (updateValueIfChanged(targetNode)) {
|
|---|
| 7815 | return targetInst;
|
|---|
| 7816 | }
|
|---|
| 7817 | }
|
|---|
| 7818 |
|
|---|
| 7819 | function getTargetInstForChangeEvent(domEventName, targetInst) {
|
|---|
| 7820 | if (domEventName === 'change') {
|
|---|
| 7821 | return targetInst;
|
|---|
| 7822 | }
|
|---|
| 7823 | }
|
|---|
| 7824 | /**
|
|---|
| 7825 | * SECTION: handle `input` event
|
|---|
| 7826 | */
|
|---|
| 7827 |
|
|---|
| 7828 |
|
|---|
| 7829 | var isInputEventSupported = false;
|
|---|
| 7830 |
|
|---|
| 7831 | if (canUseDOM) {
|
|---|
| 7832 | // IE9 claims to support the input event but fails to trigger it when
|
|---|
| 7833 | // deleting text, so we ignore its input events.
|
|---|
| 7834 | isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 9);
|
|---|
| 7835 | }
|
|---|
| 7836 | /**
|
|---|
| 7837 | * (For IE <=9) Starts tracking propertychange events on the passed-in element
|
|---|
| 7838 | * and override the value property so that we can distinguish user events from
|
|---|
| 7839 | * value changes in JS.
|
|---|
| 7840 | */
|
|---|
| 7841 |
|
|---|
| 7842 |
|
|---|
| 7843 | function startWatchingForValueChange(target, targetInst) {
|
|---|
| 7844 | activeElement = target;
|
|---|
| 7845 | activeElementInst = targetInst;
|
|---|
| 7846 | activeElement.attachEvent('onpropertychange', handlePropertyChange);
|
|---|
| 7847 | }
|
|---|
| 7848 | /**
|
|---|
| 7849 | * (For IE <=9) Removes the event listeners from the currently-tracked element,
|
|---|
| 7850 | * if any exists.
|
|---|
| 7851 | */
|
|---|
| 7852 |
|
|---|
| 7853 |
|
|---|
| 7854 | function stopWatchingForValueChange() {
|
|---|
| 7855 | if (!activeElement) {
|
|---|
| 7856 | return;
|
|---|
| 7857 | }
|
|---|
| 7858 |
|
|---|
| 7859 | activeElement.detachEvent('onpropertychange', handlePropertyChange);
|
|---|
| 7860 | activeElement = null;
|
|---|
| 7861 | activeElementInst = null;
|
|---|
| 7862 | }
|
|---|
| 7863 | /**
|
|---|
| 7864 | * (For IE <=9) Handles a propertychange event, sending a `change` event if
|
|---|
| 7865 | * the value of the active element has changed.
|
|---|
| 7866 | */
|
|---|
| 7867 |
|
|---|
| 7868 |
|
|---|
| 7869 | function handlePropertyChange(nativeEvent) {
|
|---|
| 7870 | if (nativeEvent.propertyName !== 'value') {
|
|---|
| 7871 | return;
|
|---|
| 7872 | }
|
|---|
| 7873 |
|
|---|
| 7874 | if (getInstIfValueChanged(activeElementInst)) {
|
|---|
| 7875 | manualDispatchChangeEvent(nativeEvent);
|
|---|
| 7876 | }
|
|---|
| 7877 | }
|
|---|
| 7878 |
|
|---|
| 7879 | function handleEventsForInputEventPolyfill(domEventName, target, targetInst) {
|
|---|
| 7880 | if (domEventName === 'focusin') {
|
|---|
| 7881 | // In IE9, propertychange fires for most input events but is buggy and
|
|---|
| 7882 | // doesn't fire when text is deleted, but conveniently, selectionchange
|
|---|
| 7883 | // appears to fire in all of the remaining cases so we catch those and
|
|---|
| 7884 | // forward the event if the value has changed
|
|---|
| 7885 | // In either case, we don't want to call the event handler if the value
|
|---|
| 7886 | // is changed from JS so we redefine a setter for `.value` that updates
|
|---|
| 7887 | // our activeElementValue variable, allowing us to ignore those changes
|
|---|
| 7888 | //
|
|---|
| 7889 | // stopWatching() should be a noop here but we call it just in case we
|
|---|
| 7890 | // missed a blur event somehow.
|
|---|
| 7891 | stopWatchingForValueChange();
|
|---|
| 7892 | startWatchingForValueChange(target, targetInst);
|
|---|
| 7893 | } else if (domEventName === 'focusout') {
|
|---|
| 7894 | stopWatchingForValueChange();
|
|---|
| 7895 | }
|
|---|
| 7896 | } // For IE8 and IE9.
|
|---|
| 7897 |
|
|---|
| 7898 |
|
|---|
| 7899 | function getTargetInstForInputEventPolyfill(domEventName, targetInst) {
|
|---|
| 7900 | if (domEventName === 'selectionchange' || domEventName === 'keyup' || domEventName === 'keydown') {
|
|---|
| 7901 | // On the selectionchange event, the target is just document which isn't
|
|---|
| 7902 | // helpful for us so just check activeElement instead.
|
|---|
| 7903 | //
|
|---|
| 7904 | // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire
|
|---|
| 7905 | // propertychange on the first input event after setting `value` from a
|
|---|
| 7906 | // script and fires only keydown, keypress, keyup. Catching keyup usually
|
|---|
| 7907 | // gets it and catching keydown lets us fire an event for the first
|
|---|
| 7908 | // keystroke if user does a key repeat (it'll be a little delayed: right
|
|---|
| 7909 | // before the second keystroke). Other input methods (e.g., paste) seem to
|
|---|
| 7910 | // fire selectionchange normally.
|
|---|
| 7911 | return getInstIfValueChanged(activeElementInst);
|
|---|
| 7912 | }
|
|---|
| 7913 | }
|
|---|
| 7914 | /**
|
|---|
| 7915 | * SECTION: handle `click` event
|
|---|
| 7916 | */
|
|---|
| 7917 |
|
|---|
| 7918 |
|
|---|
| 7919 | function shouldUseClickEvent(elem) {
|
|---|
| 7920 | // Use the `click` event to detect changes to checkbox and radio inputs.
|
|---|
| 7921 | // This approach works across all browsers, whereas `change` does not fire
|
|---|
| 7922 | // until `blur` in IE8.
|
|---|
| 7923 | var nodeName = elem.nodeName;
|
|---|
| 7924 | return nodeName && nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio');
|
|---|
| 7925 | }
|
|---|
| 7926 |
|
|---|
| 7927 | function getTargetInstForClickEvent(domEventName, targetInst) {
|
|---|
| 7928 | if (domEventName === 'click') {
|
|---|
| 7929 | return getInstIfValueChanged(targetInst);
|
|---|
| 7930 | }
|
|---|
| 7931 | }
|
|---|
| 7932 |
|
|---|
| 7933 | function getTargetInstForInputOrChangeEvent(domEventName, targetInst) {
|
|---|
| 7934 | if (domEventName === 'input' || domEventName === 'change') {
|
|---|
| 7935 | return getInstIfValueChanged(targetInst);
|
|---|
| 7936 | }
|
|---|
| 7937 | }
|
|---|
| 7938 |
|
|---|
| 7939 | function handleControlledInputBlur(node) {
|
|---|
| 7940 | var state = node._wrapperState;
|
|---|
| 7941 |
|
|---|
| 7942 | if (!state || !state.controlled || node.type !== 'number') {
|
|---|
| 7943 | return;
|
|---|
| 7944 | }
|
|---|
| 7945 |
|
|---|
| 7946 | {
|
|---|
| 7947 | // If controlled, assign the value attribute to the current value on blur
|
|---|
| 7948 | setDefaultValue(node, 'number', node.value);
|
|---|
| 7949 | }
|
|---|
| 7950 | }
|
|---|
| 7951 | /**
|
|---|
| 7952 | * This plugin creates an `onChange` event that normalizes change events
|
|---|
| 7953 | * across form elements. This event fires at a time when it's possible to
|
|---|
| 7954 | * change the element's value without seeing a flicker.
|
|---|
| 7955 | *
|
|---|
| 7956 | * Supported elements are:
|
|---|
| 7957 | * - input (see `isTextInputElement`)
|
|---|
| 7958 | * - textarea
|
|---|
| 7959 | * - select
|
|---|
| 7960 | */
|
|---|
| 7961 |
|
|---|
| 7962 |
|
|---|
| 7963 | function extractEvents$1(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 7964 | var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
|
|---|
| 7965 | var getTargetInstFunc, handleEventFunc;
|
|---|
| 7966 |
|
|---|
| 7967 | if (shouldUseChangeEvent(targetNode)) {
|
|---|
| 7968 | getTargetInstFunc = getTargetInstForChangeEvent;
|
|---|
| 7969 | } else if (isTextInputElement(targetNode)) {
|
|---|
| 7970 | if (isInputEventSupported) {
|
|---|
| 7971 | getTargetInstFunc = getTargetInstForInputOrChangeEvent;
|
|---|
| 7972 | } else {
|
|---|
| 7973 | getTargetInstFunc = getTargetInstForInputEventPolyfill;
|
|---|
| 7974 | handleEventFunc = handleEventsForInputEventPolyfill;
|
|---|
| 7975 | }
|
|---|
| 7976 | } else if (shouldUseClickEvent(targetNode)) {
|
|---|
| 7977 | getTargetInstFunc = getTargetInstForClickEvent;
|
|---|
| 7978 | }
|
|---|
| 7979 |
|
|---|
| 7980 | if (getTargetInstFunc) {
|
|---|
| 7981 | var inst = getTargetInstFunc(domEventName, targetInst);
|
|---|
| 7982 |
|
|---|
| 7983 | if (inst) {
|
|---|
| 7984 | createAndAccumulateChangeEvent(dispatchQueue, inst, nativeEvent, nativeEventTarget);
|
|---|
| 7985 | return;
|
|---|
| 7986 | }
|
|---|
| 7987 | }
|
|---|
| 7988 |
|
|---|
| 7989 | if (handleEventFunc) {
|
|---|
| 7990 | handleEventFunc(domEventName, targetNode, targetInst);
|
|---|
| 7991 | } // When blurring, set the value attribute for number inputs
|
|---|
| 7992 |
|
|---|
| 7993 |
|
|---|
| 7994 | if (domEventName === 'focusout') {
|
|---|
| 7995 | handleControlledInputBlur(targetNode);
|
|---|
| 7996 | }
|
|---|
| 7997 | }
|
|---|
| 7998 |
|
|---|
| 7999 | function registerEvents$2() {
|
|---|
| 8000 | registerDirectEvent('onMouseEnter', ['mouseout', 'mouseover']);
|
|---|
| 8001 | registerDirectEvent('onMouseLeave', ['mouseout', 'mouseover']);
|
|---|
| 8002 | registerDirectEvent('onPointerEnter', ['pointerout', 'pointerover']);
|
|---|
| 8003 | registerDirectEvent('onPointerLeave', ['pointerout', 'pointerover']);
|
|---|
| 8004 | }
|
|---|
| 8005 | /**
|
|---|
| 8006 | * For almost every interaction we care about, there will be both a top-level
|
|---|
| 8007 | * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that
|
|---|
| 8008 | * we do not extract duplicate events. However, moving the mouse into the
|
|---|
| 8009 | * browser from outside will not fire a `mouseout` event. In this case, we use
|
|---|
| 8010 | * the `mouseover` top-level event.
|
|---|
| 8011 | */
|
|---|
| 8012 |
|
|---|
| 8013 |
|
|---|
| 8014 | function extractEvents$2(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 8015 | var isOverEvent = domEventName === 'mouseover' || domEventName === 'pointerover';
|
|---|
| 8016 | var isOutEvent = domEventName === 'mouseout' || domEventName === 'pointerout';
|
|---|
| 8017 |
|
|---|
| 8018 | if (isOverEvent && !isReplayingEvent(nativeEvent)) {
|
|---|
| 8019 | // If this is an over event with a target, we might have already dispatched
|
|---|
| 8020 | // the event in the out event of the other target. If this is replayed,
|
|---|
| 8021 | // then it's because we couldn't dispatch against this target previously
|
|---|
| 8022 | // so we have to do it now instead.
|
|---|
| 8023 | var related = nativeEvent.relatedTarget || nativeEvent.fromElement;
|
|---|
| 8024 |
|
|---|
| 8025 | if (related) {
|
|---|
| 8026 | // If the related node is managed by React, we can assume that we have
|
|---|
| 8027 | // already dispatched the corresponding events during its mouseout.
|
|---|
| 8028 | if (getClosestInstanceFromNode(related) || isContainerMarkedAsRoot(related)) {
|
|---|
| 8029 | return;
|
|---|
| 8030 | }
|
|---|
| 8031 | }
|
|---|
| 8032 | }
|
|---|
| 8033 |
|
|---|
| 8034 | if (!isOutEvent && !isOverEvent) {
|
|---|
| 8035 | // Must not be a mouse or pointer in or out - ignoring.
|
|---|
| 8036 | return;
|
|---|
| 8037 | }
|
|---|
| 8038 |
|
|---|
| 8039 | var win; // TODO: why is this nullable in the types but we read from it?
|
|---|
| 8040 |
|
|---|
| 8041 | if (nativeEventTarget.window === nativeEventTarget) {
|
|---|
| 8042 | // `nativeEventTarget` is probably a window object.
|
|---|
| 8043 | win = nativeEventTarget;
|
|---|
| 8044 | } else {
|
|---|
| 8045 | // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8.
|
|---|
| 8046 | var doc = nativeEventTarget.ownerDocument;
|
|---|
| 8047 |
|
|---|
| 8048 | if (doc) {
|
|---|
| 8049 | win = doc.defaultView || doc.parentWindow;
|
|---|
| 8050 | } else {
|
|---|
| 8051 | win = window;
|
|---|
| 8052 | }
|
|---|
| 8053 | }
|
|---|
| 8054 |
|
|---|
| 8055 | var from;
|
|---|
| 8056 | var to;
|
|---|
| 8057 |
|
|---|
| 8058 | if (isOutEvent) {
|
|---|
| 8059 | var _related = nativeEvent.relatedTarget || nativeEvent.toElement;
|
|---|
| 8060 |
|
|---|
| 8061 | from = targetInst;
|
|---|
| 8062 | to = _related ? getClosestInstanceFromNode(_related) : null;
|
|---|
| 8063 |
|
|---|
| 8064 | if (to !== null) {
|
|---|
| 8065 | var nearestMounted = getNearestMountedFiber(to);
|
|---|
| 8066 |
|
|---|
| 8067 | if (to !== nearestMounted || to.tag !== HostComponent && to.tag !== HostText) {
|
|---|
| 8068 | to = null;
|
|---|
| 8069 | }
|
|---|
| 8070 | }
|
|---|
| 8071 | } else {
|
|---|
| 8072 | // Moving to a node from outside the window.
|
|---|
| 8073 | from = null;
|
|---|
| 8074 | to = targetInst;
|
|---|
| 8075 | }
|
|---|
| 8076 |
|
|---|
| 8077 | if (from === to) {
|
|---|
| 8078 | // Nothing pertains to our managed components.
|
|---|
| 8079 | return;
|
|---|
| 8080 | }
|
|---|
| 8081 |
|
|---|
| 8082 | var SyntheticEventCtor = SyntheticMouseEvent;
|
|---|
| 8083 | var leaveEventType = 'onMouseLeave';
|
|---|
| 8084 | var enterEventType = 'onMouseEnter';
|
|---|
| 8085 | var eventTypePrefix = 'mouse';
|
|---|
| 8086 |
|
|---|
| 8087 | if (domEventName === 'pointerout' || domEventName === 'pointerover') {
|
|---|
| 8088 | SyntheticEventCtor = SyntheticPointerEvent;
|
|---|
| 8089 | leaveEventType = 'onPointerLeave';
|
|---|
| 8090 | enterEventType = 'onPointerEnter';
|
|---|
| 8091 | eventTypePrefix = 'pointer';
|
|---|
| 8092 | }
|
|---|
| 8093 |
|
|---|
| 8094 | var fromNode = from == null ? win : getNodeFromInstance(from);
|
|---|
| 8095 | var toNode = to == null ? win : getNodeFromInstance(to);
|
|---|
| 8096 | var leave = new SyntheticEventCtor(leaveEventType, eventTypePrefix + 'leave', from, nativeEvent, nativeEventTarget);
|
|---|
| 8097 | leave.target = fromNode;
|
|---|
| 8098 | leave.relatedTarget = toNode;
|
|---|
| 8099 | var enter = null; // We should only process this nativeEvent if we are processing
|
|---|
| 8100 | // the first ancestor. Next time, we will ignore the event.
|
|---|
| 8101 |
|
|---|
| 8102 | var nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget);
|
|---|
| 8103 |
|
|---|
| 8104 | if (nativeTargetInst === targetInst) {
|
|---|
| 8105 | var enterEvent = new SyntheticEventCtor(enterEventType, eventTypePrefix + 'enter', to, nativeEvent, nativeEventTarget);
|
|---|
| 8106 | enterEvent.target = toNode;
|
|---|
| 8107 | enterEvent.relatedTarget = fromNode;
|
|---|
| 8108 | enter = enterEvent;
|
|---|
| 8109 | }
|
|---|
| 8110 |
|
|---|
| 8111 | accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);
|
|---|
| 8112 | }
|
|---|
| 8113 |
|
|---|
| 8114 | /**
|
|---|
| 8115 | * inlined Object.is polyfill to avoid requiring consumers ship their own
|
|---|
| 8116 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
|
|---|
| 8117 | */
|
|---|
| 8118 | function is(x, y) {
|
|---|
| 8119 | return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
|
|---|
| 8120 | ;
|
|---|
| 8121 | }
|
|---|
| 8122 |
|
|---|
| 8123 | var objectIs = typeof Object.is === 'function' ? Object.is : is;
|
|---|
| 8124 |
|
|---|
| 8125 | /**
|
|---|
| 8126 | * Performs equality by iterating through keys on an object and returning false
|
|---|
| 8127 | * when any key has values which are not strictly equal between the arguments.
|
|---|
| 8128 | * Returns true when the values of all keys are strictly equal.
|
|---|
| 8129 | */
|
|---|
| 8130 |
|
|---|
| 8131 | function shallowEqual(objA, objB) {
|
|---|
| 8132 | if (objectIs(objA, objB)) {
|
|---|
| 8133 | return true;
|
|---|
| 8134 | }
|
|---|
| 8135 |
|
|---|
| 8136 | if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
|
|---|
| 8137 | return false;
|
|---|
| 8138 | }
|
|---|
| 8139 |
|
|---|
| 8140 | var keysA = Object.keys(objA);
|
|---|
| 8141 | var keysB = Object.keys(objB);
|
|---|
| 8142 |
|
|---|
| 8143 | if (keysA.length !== keysB.length) {
|
|---|
| 8144 | return false;
|
|---|
| 8145 | } // Test for A's keys different from B.
|
|---|
| 8146 |
|
|---|
| 8147 |
|
|---|
| 8148 | for (var i = 0; i < keysA.length; i++) {
|
|---|
| 8149 | var currentKey = keysA[i];
|
|---|
| 8150 |
|
|---|
| 8151 | if (!hasOwnProperty.call(objB, currentKey) || !objectIs(objA[currentKey], objB[currentKey])) {
|
|---|
| 8152 | return false;
|
|---|
| 8153 | }
|
|---|
| 8154 | }
|
|---|
| 8155 |
|
|---|
| 8156 | return true;
|
|---|
| 8157 | }
|
|---|
| 8158 |
|
|---|
| 8159 | /**
|
|---|
| 8160 | * Given any node return the first leaf node without children.
|
|---|
| 8161 | *
|
|---|
| 8162 | * @param {DOMElement|DOMTextNode} node
|
|---|
| 8163 | * @return {DOMElement|DOMTextNode}
|
|---|
| 8164 | */
|
|---|
| 8165 |
|
|---|
| 8166 | function getLeafNode(node) {
|
|---|
| 8167 | while (node && node.firstChild) {
|
|---|
| 8168 | node = node.firstChild;
|
|---|
| 8169 | }
|
|---|
| 8170 |
|
|---|
| 8171 | return node;
|
|---|
| 8172 | }
|
|---|
| 8173 | /**
|
|---|
| 8174 | * Get the next sibling within a container. This will walk up the
|
|---|
| 8175 | * DOM if a node's siblings have been exhausted.
|
|---|
| 8176 | *
|
|---|
| 8177 | * @param {DOMElement|DOMTextNode} node
|
|---|
| 8178 | * @return {?DOMElement|DOMTextNode}
|
|---|
| 8179 | */
|
|---|
| 8180 |
|
|---|
| 8181 |
|
|---|
| 8182 | function getSiblingNode(node) {
|
|---|
| 8183 | while (node) {
|
|---|
| 8184 | if (node.nextSibling) {
|
|---|
| 8185 | return node.nextSibling;
|
|---|
| 8186 | }
|
|---|
| 8187 |
|
|---|
| 8188 | node = node.parentNode;
|
|---|
| 8189 | }
|
|---|
| 8190 | }
|
|---|
| 8191 | /**
|
|---|
| 8192 | * Get object describing the nodes which contain characters at offset.
|
|---|
| 8193 | *
|
|---|
| 8194 | * @param {DOMElement|DOMTextNode} root
|
|---|
| 8195 | * @param {number} offset
|
|---|
| 8196 | * @return {?object}
|
|---|
| 8197 | */
|
|---|
| 8198 |
|
|---|
| 8199 |
|
|---|
| 8200 | function getNodeForCharacterOffset(root, offset) {
|
|---|
| 8201 | var node = getLeafNode(root);
|
|---|
| 8202 | var nodeStart = 0;
|
|---|
| 8203 | var nodeEnd = 0;
|
|---|
| 8204 |
|
|---|
| 8205 | while (node) {
|
|---|
| 8206 | if (node.nodeType === TEXT_NODE) {
|
|---|
| 8207 | nodeEnd = nodeStart + node.textContent.length;
|
|---|
| 8208 |
|
|---|
| 8209 | if (nodeStart <= offset && nodeEnd >= offset) {
|
|---|
| 8210 | return {
|
|---|
| 8211 | node: node,
|
|---|
| 8212 | offset: offset - nodeStart
|
|---|
| 8213 | };
|
|---|
| 8214 | }
|
|---|
| 8215 |
|
|---|
| 8216 | nodeStart = nodeEnd;
|
|---|
| 8217 | }
|
|---|
| 8218 |
|
|---|
| 8219 | node = getLeafNode(getSiblingNode(node));
|
|---|
| 8220 | }
|
|---|
| 8221 | }
|
|---|
| 8222 |
|
|---|
| 8223 | /**
|
|---|
| 8224 | * @param {DOMElement} outerNode
|
|---|
| 8225 | * @return {?object}
|
|---|
| 8226 | */
|
|---|
| 8227 |
|
|---|
| 8228 | function getOffsets(outerNode) {
|
|---|
| 8229 | var ownerDocument = outerNode.ownerDocument;
|
|---|
| 8230 | var win = ownerDocument && ownerDocument.defaultView || window;
|
|---|
| 8231 | var selection = win.getSelection && win.getSelection();
|
|---|
| 8232 |
|
|---|
| 8233 | if (!selection || selection.rangeCount === 0) {
|
|---|
| 8234 | return null;
|
|---|
| 8235 | }
|
|---|
| 8236 |
|
|---|
| 8237 | var anchorNode = selection.anchorNode,
|
|---|
| 8238 | anchorOffset = selection.anchorOffset,
|
|---|
| 8239 | focusNode = selection.focusNode,
|
|---|
| 8240 | focusOffset = selection.focusOffset; // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the
|
|---|
| 8241 | // up/down buttons on an <input type="number">. Anonymous divs do not seem to
|
|---|
| 8242 | // expose properties, triggering a "Permission denied error" if any of its
|
|---|
| 8243 | // properties are accessed. The only seemingly possible way to avoid erroring
|
|---|
| 8244 | // is to access a property that typically works for non-anonymous divs and
|
|---|
| 8245 | // catch any error that may otherwise arise. See
|
|---|
| 8246 | // https://bugzilla.mozilla.org/show_bug.cgi?id=208427
|
|---|
| 8247 |
|
|---|
| 8248 | try {
|
|---|
| 8249 | /* eslint-disable no-unused-expressions */
|
|---|
| 8250 | anchorNode.nodeType;
|
|---|
| 8251 | focusNode.nodeType;
|
|---|
| 8252 | /* eslint-enable no-unused-expressions */
|
|---|
| 8253 | } catch (e) {
|
|---|
| 8254 | return null;
|
|---|
| 8255 | }
|
|---|
| 8256 |
|
|---|
| 8257 | return getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset);
|
|---|
| 8258 | }
|
|---|
| 8259 | /**
|
|---|
| 8260 | * Returns {start, end} where `start` is the character/codepoint index of
|
|---|
| 8261 | * (anchorNode, anchorOffset) within the textContent of `outerNode`, and
|
|---|
| 8262 | * `end` is the index of (focusNode, focusOffset).
|
|---|
| 8263 | *
|
|---|
| 8264 | * Returns null if you pass in garbage input but we should probably just crash.
|
|---|
| 8265 | *
|
|---|
| 8266 | * Exported only for testing.
|
|---|
| 8267 | */
|
|---|
| 8268 |
|
|---|
| 8269 | function getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset) {
|
|---|
| 8270 | var length = 0;
|
|---|
| 8271 | var start = -1;
|
|---|
| 8272 | var end = -1;
|
|---|
| 8273 | var indexWithinAnchor = 0;
|
|---|
| 8274 | var indexWithinFocus = 0;
|
|---|
| 8275 | var node = outerNode;
|
|---|
| 8276 | var parentNode = null;
|
|---|
| 8277 |
|
|---|
| 8278 | outer: while (true) {
|
|---|
| 8279 | var next = null;
|
|---|
| 8280 |
|
|---|
| 8281 | while (true) {
|
|---|
| 8282 | if (node === anchorNode && (anchorOffset === 0 || node.nodeType === TEXT_NODE)) {
|
|---|
| 8283 | start = length + anchorOffset;
|
|---|
| 8284 | }
|
|---|
| 8285 |
|
|---|
| 8286 | if (node === focusNode && (focusOffset === 0 || node.nodeType === TEXT_NODE)) {
|
|---|
| 8287 | end = length + focusOffset;
|
|---|
| 8288 | }
|
|---|
| 8289 |
|
|---|
| 8290 | if (node.nodeType === TEXT_NODE) {
|
|---|
| 8291 | length += node.nodeValue.length;
|
|---|
| 8292 | }
|
|---|
| 8293 |
|
|---|
| 8294 | if ((next = node.firstChild) === null) {
|
|---|
| 8295 | break;
|
|---|
| 8296 | } // Moving from `node` to its first child `next`.
|
|---|
| 8297 |
|
|---|
| 8298 |
|
|---|
| 8299 | parentNode = node;
|
|---|
| 8300 | node = next;
|
|---|
| 8301 | }
|
|---|
| 8302 |
|
|---|
| 8303 | while (true) {
|
|---|
| 8304 | if (node === outerNode) {
|
|---|
| 8305 | // If `outerNode` has children, this is always the second time visiting
|
|---|
| 8306 | // it. If it has no children, this is still the first loop, and the only
|
|---|
| 8307 | // valid selection is anchorNode and focusNode both equal to this node
|
|---|
| 8308 | // and both offsets 0, in which case we will have handled above.
|
|---|
| 8309 | break outer;
|
|---|
| 8310 | }
|
|---|
| 8311 |
|
|---|
| 8312 | if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) {
|
|---|
| 8313 | start = length;
|
|---|
| 8314 | }
|
|---|
| 8315 |
|
|---|
| 8316 | if (parentNode === focusNode && ++indexWithinFocus === focusOffset) {
|
|---|
| 8317 | end = length;
|
|---|
| 8318 | }
|
|---|
| 8319 |
|
|---|
| 8320 | if ((next = node.nextSibling) !== null) {
|
|---|
| 8321 | break;
|
|---|
| 8322 | }
|
|---|
| 8323 |
|
|---|
| 8324 | node = parentNode;
|
|---|
| 8325 | parentNode = node.parentNode;
|
|---|
| 8326 | } // Moving from `node` to its next sibling `next`.
|
|---|
| 8327 |
|
|---|
| 8328 |
|
|---|
| 8329 | node = next;
|
|---|
| 8330 | }
|
|---|
| 8331 |
|
|---|
| 8332 | if (start === -1 || end === -1) {
|
|---|
| 8333 | // This should never happen. (Would happen if the anchor/focus nodes aren't
|
|---|
| 8334 | // actually inside the passed-in node.)
|
|---|
| 8335 | return null;
|
|---|
| 8336 | }
|
|---|
| 8337 |
|
|---|
| 8338 | return {
|
|---|
| 8339 | start: start,
|
|---|
| 8340 | end: end
|
|---|
| 8341 | };
|
|---|
| 8342 | }
|
|---|
| 8343 | /**
|
|---|
| 8344 | * In modern non-IE browsers, we can support both forward and backward
|
|---|
| 8345 | * selections.
|
|---|
| 8346 | *
|
|---|
| 8347 | * Note: IE10+ supports the Selection object, but it does not support
|
|---|
| 8348 | * the `extend` method, which means that even in modern IE, it's not possible
|
|---|
| 8349 | * to programmatically create a backward selection. Thus, for all IE
|
|---|
| 8350 | * versions, we use the old IE API to create our selections.
|
|---|
| 8351 | *
|
|---|
| 8352 | * @param {DOMElement|DOMTextNode} node
|
|---|
| 8353 | * @param {object} offsets
|
|---|
| 8354 | */
|
|---|
| 8355 |
|
|---|
| 8356 | function setOffsets(node, offsets) {
|
|---|
| 8357 | var doc = node.ownerDocument || document;
|
|---|
| 8358 | var win = doc && doc.defaultView || window; // Edge fails with "Object expected" in some scenarios.
|
|---|
| 8359 | // (For instance: TinyMCE editor used in a list component that supports pasting to add more,
|
|---|
| 8360 | // fails when pasting 100+ items)
|
|---|
| 8361 |
|
|---|
| 8362 | if (!win.getSelection) {
|
|---|
| 8363 | return;
|
|---|
| 8364 | }
|
|---|
| 8365 |
|
|---|
| 8366 | var selection = win.getSelection();
|
|---|
| 8367 | var length = node.textContent.length;
|
|---|
| 8368 | var start = Math.min(offsets.start, length);
|
|---|
| 8369 | var end = offsets.end === undefined ? start : Math.min(offsets.end, length); // IE 11 uses modern selection, but doesn't support the extend method.
|
|---|
| 8370 | // Flip backward selections, so we can set with a single range.
|
|---|
| 8371 |
|
|---|
| 8372 | if (!selection.extend && start > end) {
|
|---|
| 8373 | var temp = end;
|
|---|
| 8374 | end = start;
|
|---|
| 8375 | start = temp;
|
|---|
| 8376 | }
|
|---|
| 8377 |
|
|---|
| 8378 | var startMarker = getNodeForCharacterOffset(node, start);
|
|---|
| 8379 | var endMarker = getNodeForCharacterOffset(node, end);
|
|---|
| 8380 |
|
|---|
| 8381 | if (startMarker && endMarker) {
|
|---|
| 8382 | if (selection.rangeCount === 1 && selection.anchorNode === startMarker.node && selection.anchorOffset === startMarker.offset && selection.focusNode === endMarker.node && selection.focusOffset === endMarker.offset) {
|
|---|
| 8383 | return;
|
|---|
| 8384 | }
|
|---|
| 8385 |
|
|---|
| 8386 | var range = doc.createRange();
|
|---|
| 8387 | range.setStart(startMarker.node, startMarker.offset);
|
|---|
| 8388 | selection.removeAllRanges();
|
|---|
| 8389 |
|
|---|
| 8390 | if (start > end) {
|
|---|
| 8391 | selection.addRange(range);
|
|---|
| 8392 | selection.extend(endMarker.node, endMarker.offset);
|
|---|
| 8393 | } else {
|
|---|
| 8394 | range.setEnd(endMarker.node, endMarker.offset);
|
|---|
| 8395 | selection.addRange(range);
|
|---|
| 8396 | }
|
|---|
| 8397 | }
|
|---|
| 8398 | }
|
|---|
| 8399 |
|
|---|
| 8400 | function isTextNode(node) {
|
|---|
| 8401 | return node && node.nodeType === TEXT_NODE;
|
|---|
| 8402 | }
|
|---|
| 8403 |
|
|---|
| 8404 | function containsNode(outerNode, innerNode) {
|
|---|
| 8405 | if (!outerNode || !innerNode) {
|
|---|
| 8406 | return false;
|
|---|
| 8407 | } else if (outerNode === innerNode) {
|
|---|
| 8408 | return true;
|
|---|
| 8409 | } else if (isTextNode(outerNode)) {
|
|---|
| 8410 | return false;
|
|---|
| 8411 | } else if (isTextNode(innerNode)) {
|
|---|
| 8412 | return containsNode(outerNode, innerNode.parentNode);
|
|---|
| 8413 | } else if ('contains' in outerNode) {
|
|---|
| 8414 | return outerNode.contains(innerNode);
|
|---|
| 8415 | } else if (outerNode.compareDocumentPosition) {
|
|---|
| 8416 | return !!(outerNode.compareDocumentPosition(innerNode) & 16);
|
|---|
| 8417 | } else {
|
|---|
| 8418 | return false;
|
|---|
| 8419 | }
|
|---|
| 8420 | }
|
|---|
| 8421 |
|
|---|
| 8422 | function isInDocument(node) {
|
|---|
| 8423 | return node && node.ownerDocument && containsNode(node.ownerDocument.documentElement, node);
|
|---|
| 8424 | }
|
|---|
| 8425 |
|
|---|
| 8426 | function isSameOriginFrame(iframe) {
|
|---|
| 8427 | try {
|
|---|
| 8428 | // Accessing the contentDocument of a HTMLIframeElement can cause the browser
|
|---|
| 8429 | // to throw, e.g. if it has a cross-origin src attribute.
|
|---|
| 8430 | // Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g:
|
|---|
| 8431 | // iframe.contentDocument.defaultView;
|
|---|
| 8432 | // A safety way is to access one of the cross origin properties: Window or Location
|
|---|
| 8433 | // Which might result in "SecurityError" DOM Exception and it is compatible to Safari.
|
|---|
| 8434 | // https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl
|
|---|
| 8435 | return typeof iframe.contentWindow.location.href === 'string';
|
|---|
| 8436 | } catch (err) {
|
|---|
| 8437 | return false;
|
|---|
| 8438 | }
|
|---|
| 8439 | }
|
|---|
| 8440 |
|
|---|
| 8441 | function getActiveElementDeep() {
|
|---|
| 8442 | var win = window;
|
|---|
| 8443 | var element = getActiveElement();
|
|---|
| 8444 |
|
|---|
| 8445 | while (element instanceof win.HTMLIFrameElement) {
|
|---|
| 8446 | if (isSameOriginFrame(element)) {
|
|---|
| 8447 | win = element.contentWindow;
|
|---|
| 8448 | } else {
|
|---|
| 8449 | return element;
|
|---|
| 8450 | }
|
|---|
| 8451 |
|
|---|
| 8452 | element = getActiveElement(win.document);
|
|---|
| 8453 | }
|
|---|
| 8454 |
|
|---|
| 8455 | return element;
|
|---|
| 8456 | }
|
|---|
| 8457 | /**
|
|---|
| 8458 | * @ReactInputSelection: React input selection module. Based on Selection.js,
|
|---|
| 8459 | * but modified to be suitable for react and has a couple of bug fixes (doesn't
|
|---|
| 8460 | * assume buttons have range selections allowed).
|
|---|
| 8461 | * Input selection module for React.
|
|---|
| 8462 | */
|
|---|
| 8463 |
|
|---|
| 8464 | /**
|
|---|
| 8465 | * @hasSelectionCapabilities: we get the element types that support selection
|
|---|
| 8466 | * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart`
|
|---|
| 8467 | * and `selectionEnd` rows.
|
|---|
| 8468 | */
|
|---|
| 8469 |
|
|---|
| 8470 |
|
|---|
| 8471 | function hasSelectionCapabilities(elem) {
|
|---|
| 8472 | var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase();
|
|---|
| 8473 | return nodeName && (nodeName === 'input' && (elem.type === 'text' || elem.type === 'search' || elem.type === 'tel' || elem.type === 'url' || elem.type === 'password') || nodeName === 'textarea' || elem.contentEditable === 'true');
|
|---|
| 8474 | }
|
|---|
| 8475 | function getSelectionInformation() {
|
|---|
| 8476 | var focusedElem = getActiveElementDeep();
|
|---|
| 8477 | return {
|
|---|
| 8478 | focusedElem: focusedElem,
|
|---|
| 8479 | selectionRange: hasSelectionCapabilities(focusedElem) ? getSelection(focusedElem) : null
|
|---|
| 8480 | };
|
|---|
| 8481 | }
|
|---|
| 8482 | /**
|
|---|
| 8483 | * @restoreSelection: If any selection information was potentially lost,
|
|---|
| 8484 | * restore it. This is useful when performing operations that could remove dom
|
|---|
| 8485 | * nodes and place them back in, resulting in focus being lost.
|
|---|
| 8486 | */
|
|---|
| 8487 |
|
|---|
| 8488 | function restoreSelection(priorSelectionInformation) {
|
|---|
| 8489 | var curFocusedElem = getActiveElementDeep();
|
|---|
| 8490 | var priorFocusedElem = priorSelectionInformation.focusedElem;
|
|---|
| 8491 | var priorSelectionRange = priorSelectionInformation.selectionRange;
|
|---|
| 8492 |
|
|---|
| 8493 | if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) {
|
|---|
| 8494 | if (priorSelectionRange !== null && hasSelectionCapabilities(priorFocusedElem)) {
|
|---|
| 8495 | setSelection(priorFocusedElem, priorSelectionRange);
|
|---|
| 8496 | } // Focusing a node can change the scroll position, which is undesirable
|
|---|
| 8497 |
|
|---|
| 8498 |
|
|---|
| 8499 | var ancestors = [];
|
|---|
| 8500 | var ancestor = priorFocusedElem;
|
|---|
| 8501 |
|
|---|
| 8502 | while (ancestor = ancestor.parentNode) {
|
|---|
| 8503 | if (ancestor.nodeType === ELEMENT_NODE) {
|
|---|
| 8504 | ancestors.push({
|
|---|
| 8505 | element: ancestor,
|
|---|
| 8506 | left: ancestor.scrollLeft,
|
|---|
| 8507 | top: ancestor.scrollTop
|
|---|
| 8508 | });
|
|---|
| 8509 | }
|
|---|
| 8510 | }
|
|---|
| 8511 |
|
|---|
| 8512 | if (typeof priorFocusedElem.focus === 'function') {
|
|---|
| 8513 | priorFocusedElem.focus();
|
|---|
| 8514 | }
|
|---|
| 8515 |
|
|---|
| 8516 | for (var i = 0; i < ancestors.length; i++) {
|
|---|
| 8517 | var info = ancestors[i];
|
|---|
| 8518 | info.element.scrollLeft = info.left;
|
|---|
| 8519 | info.element.scrollTop = info.top;
|
|---|
| 8520 | }
|
|---|
| 8521 | }
|
|---|
| 8522 | }
|
|---|
| 8523 | /**
|
|---|
| 8524 | * @getSelection: Gets the selection bounds of a focused textarea, input or
|
|---|
| 8525 | * contentEditable node.
|
|---|
| 8526 | * -@input: Look up selection bounds of this input
|
|---|
| 8527 | * -@return {start: selectionStart, end: selectionEnd}
|
|---|
| 8528 | */
|
|---|
| 8529 |
|
|---|
| 8530 | function getSelection(input) {
|
|---|
| 8531 | var selection;
|
|---|
| 8532 |
|
|---|
| 8533 | if ('selectionStart' in input) {
|
|---|
| 8534 | // Modern browser with input or textarea.
|
|---|
| 8535 | selection = {
|
|---|
| 8536 | start: input.selectionStart,
|
|---|
| 8537 | end: input.selectionEnd
|
|---|
| 8538 | };
|
|---|
| 8539 | } else {
|
|---|
| 8540 | // Content editable or old IE textarea.
|
|---|
| 8541 | selection = getOffsets(input);
|
|---|
| 8542 | }
|
|---|
| 8543 |
|
|---|
| 8544 | return selection || {
|
|---|
| 8545 | start: 0,
|
|---|
| 8546 | end: 0
|
|---|
| 8547 | };
|
|---|
| 8548 | }
|
|---|
| 8549 | /**
|
|---|
| 8550 | * @setSelection: Sets the selection bounds of a textarea or input and focuses
|
|---|
| 8551 | * the input.
|
|---|
| 8552 | * -@input Set selection bounds of this input or textarea
|
|---|
| 8553 | * -@offsets Object of same form that is returned from get*
|
|---|
| 8554 | */
|
|---|
| 8555 |
|
|---|
| 8556 | function setSelection(input, offsets) {
|
|---|
| 8557 | var start = offsets.start;
|
|---|
| 8558 | var end = offsets.end;
|
|---|
| 8559 |
|
|---|
| 8560 | if (end === undefined) {
|
|---|
| 8561 | end = start;
|
|---|
| 8562 | }
|
|---|
| 8563 |
|
|---|
| 8564 | if ('selectionStart' in input) {
|
|---|
| 8565 | input.selectionStart = start;
|
|---|
| 8566 | input.selectionEnd = Math.min(end, input.value.length);
|
|---|
| 8567 | } else {
|
|---|
| 8568 | setOffsets(input, offsets);
|
|---|
| 8569 | }
|
|---|
| 8570 | }
|
|---|
| 8571 |
|
|---|
| 8572 | var skipSelectionChangeEvent = canUseDOM && 'documentMode' in document && document.documentMode <= 11;
|
|---|
| 8573 |
|
|---|
| 8574 | function registerEvents$3() {
|
|---|
| 8575 | registerTwoPhaseEvent('onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']);
|
|---|
| 8576 | }
|
|---|
| 8577 |
|
|---|
| 8578 | var activeElement$1 = null;
|
|---|
| 8579 | var activeElementInst$1 = null;
|
|---|
| 8580 | var lastSelection = null;
|
|---|
| 8581 | var mouseDown = false;
|
|---|
| 8582 | /**
|
|---|
| 8583 | * Get an object which is a unique representation of the current selection.
|
|---|
| 8584 | *
|
|---|
| 8585 | * The return value will not be consistent across nodes or browsers, but
|
|---|
| 8586 | * two identical selections on the same node will return identical objects.
|
|---|
| 8587 | */
|
|---|
| 8588 |
|
|---|
| 8589 | function getSelection$1(node) {
|
|---|
| 8590 | if ('selectionStart' in node && hasSelectionCapabilities(node)) {
|
|---|
| 8591 | return {
|
|---|
| 8592 | start: node.selectionStart,
|
|---|
| 8593 | end: node.selectionEnd
|
|---|
| 8594 | };
|
|---|
| 8595 | } else {
|
|---|
| 8596 | var win = node.ownerDocument && node.ownerDocument.defaultView || window;
|
|---|
| 8597 | var selection = win.getSelection();
|
|---|
| 8598 | return {
|
|---|
| 8599 | anchorNode: selection.anchorNode,
|
|---|
| 8600 | anchorOffset: selection.anchorOffset,
|
|---|
| 8601 | focusNode: selection.focusNode,
|
|---|
| 8602 | focusOffset: selection.focusOffset
|
|---|
| 8603 | };
|
|---|
| 8604 | }
|
|---|
| 8605 | }
|
|---|
| 8606 | /**
|
|---|
| 8607 | * Get document associated with the event target.
|
|---|
| 8608 | */
|
|---|
| 8609 |
|
|---|
| 8610 |
|
|---|
| 8611 | function getEventTargetDocument(eventTarget) {
|
|---|
| 8612 | return eventTarget.window === eventTarget ? eventTarget.document : eventTarget.nodeType === DOCUMENT_NODE ? eventTarget : eventTarget.ownerDocument;
|
|---|
| 8613 | }
|
|---|
| 8614 | /**
|
|---|
| 8615 | * Poll selection to see whether it's changed.
|
|---|
| 8616 | *
|
|---|
| 8617 | * @param {object} nativeEvent
|
|---|
| 8618 | * @param {object} nativeEventTarget
|
|---|
| 8619 | * @return {?SyntheticEvent}
|
|---|
| 8620 | */
|
|---|
| 8621 |
|
|---|
| 8622 |
|
|---|
| 8623 | function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) {
|
|---|
| 8624 | // Ensure we have the right element, and that the user is not dragging a
|
|---|
| 8625 | // selection (this matches native `select` event behavior). In HTML5, select
|
|---|
| 8626 | // fires only on input and textarea thus if there's no focused element we
|
|---|
| 8627 | // won't dispatch.
|
|---|
| 8628 | var doc = getEventTargetDocument(nativeEventTarget);
|
|---|
| 8629 |
|
|---|
| 8630 | if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement(doc)) {
|
|---|
| 8631 | return;
|
|---|
| 8632 | } // Only fire when selection has actually changed.
|
|---|
| 8633 |
|
|---|
| 8634 |
|
|---|
| 8635 | var currentSelection = getSelection$1(activeElement$1);
|
|---|
| 8636 |
|
|---|
| 8637 | if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) {
|
|---|
| 8638 | lastSelection = currentSelection;
|
|---|
| 8639 | var listeners = accumulateTwoPhaseListeners(activeElementInst$1, 'onSelect');
|
|---|
| 8640 |
|
|---|
| 8641 | if (listeners.length > 0) {
|
|---|
| 8642 | var event = new SyntheticEvent('onSelect', 'select', null, nativeEvent, nativeEventTarget);
|
|---|
| 8643 | dispatchQueue.push({
|
|---|
| 8644 | event: event,
|
|---|
| 8645 | listeners: listeners
|
|---|
| 8646 | });
|
|---|
| 8647 | event.target = activeElement$1;
|
|---|
| 8648 | }
|
|---|
| 8649 | }
|
|---|
| 8650 | }
|
|---|
| 8651 | /**
|
|---|
| 8652 | * This plugin creates an `onSelect` event that normalizes select events
|
|---|
| 8653 | * across form elements.
|
|---|
| 8654 | *
|
|---|
| 8655 | * Supported elements are:
|
|---|
| 8656 | * - input (see `isTextInputElement`)
|
|---|
| 8657 | * - textarea
|
|---|
| 8658 | * - contentEditable
|
|---|
| 8659 | *
|
|---|
| 8660 | * This differs from native browser implementations in the following ways:
|
|---|
| 8661 | * - Fires on contentEditable fields as well as inputs.
|
|---|
| 8662 | * - Fires for collapsed selection.
|
|---|
| 8663 | * - Fires after user input.
|
|---|
| 8664 | */
|
|---|
| 8665 |
|
|---|
| 8666 |
|
|---|
| 8667 | function extractEvents$3(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 8668 | var targetNode = targetInst ? getNodeFromInstance(targetInst) : window;
|
|---|
| 8669 |
|
|---|
| 8670 | switch (domEventName) {
|
|---|
| 8671 | // Track the input node that has focus.
|
|---|
| 8672 | case 'focusin':
|
|---|
| 8673 | if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') {
|
|---|
| 8674 | activeElement$1 = targetNode;
|
|---|
| 8675 | activeElementInst$1 = targetInst;
|
|---|
| 8676 | lastSelection = null;
|
|---|
| 8677 | }
|
|---|
| 8678 |
|
|---|
| 8679 | break;
|
|---|
| 8680 |
|
|---|
| 8681 | case 'focusout':
|
|---|
| 8682 | activeElement$1 = null;
|
|---|
| 8683 | activeElementInst$1 = null;
|
|---|
| 8684 | lastSelection = null;
|
|---|
| 8685 | break;
|
|---|
| 8686 | // Don't fire the event while the user is dragging. This matches the
|
|---|
| 8687 | // semantics of the native select event.
|
|---|
| 8688 |
|
|---|
| 8689 | case 'mousedown':
|
|---|
| 8690 | mouseDown = true;
|
|---|
| 8691 | break;
|
|---|
| 8692 |
|
|---|
| 8693 | case 'contextmenu':
|
|---|
| 8694 | case 'mouseup':
|
|---|
| 8695 | case 'dragend':
|
|---|
| 8696 | mouseDown = false;
|
|---|
| 8697 | constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
|
|---|
| 8698 | break;
|
|---|
| 8699 | // Chrome and IE fire non-standard event when selection is changed (and
|
|---|
| 8700 | // sometimes when it hasn't). IE's event fires out of order with respect
|
|---|
| 8701 | // to key and input events on deletion, so we discard it.
|
|---|
| 8702 | //
|
|---|
| 8703 | // Firefox doesn't support selectionchange, so check selection status
|
|---|
| 8704 | // after each key entry. The selection changes after keydown and before
|
|---|
| 8705 | // keyup, but we check on keydown as well in the case of holding down a
|
|---|
| 8706 | // key, when multiple keydown events are fired but only one keyup is.
|
|---|
| 8707 | // This is also our approach for IE handling, for the reason above.
|
|---|
| 8708 |
|
|---|
| 8709 | case 'selectionchange':
|
|---|
| 8710 | if (skipSelectionChangeEvent) {
|
|---|
| 8711 | break;
|
|---|
| 8712 | }
|
|---|
| 8713 |
|
|---|
| 8714 | // falls through
|
|---|
| 8715 |
|
|---|
| 8716 | case 'keydown':
|
|---|
| 8717 | case 'keyup':
|
|---|
| 8718 | constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget);
|
|---|
| 8719 | }
|
|---|
| 8720 | }
|
|---|
| 8721 |
|
|---|
| 8722 | /**
|
|---|
| 8723 | * Generate a mapping of standard vendor prefixes using the defined style property and event name.
|
|---|
| 8724 | *
|
|---|
| 8725 | * @param {string} styleProp
|
|---|
| 8726 | * @param {string} eventName
|
|---|
| 8727 | * @returns {object}
|
|---|
| 8728 | */
|
|---|
| 8729 |
|
|---|
| 8730 | function makePrefixMap(styleProp, eventName) {
|
|---|
| 8731 | var prefixes = {};
|
|---|
| 8732 | prefixes[styleProp.toLowerCase()] = eventName.toLowerCase();
|
|---|
| 8733 | prefixes['Webkit' + styleProp] = 'webkit' + eventName;
|
|---|
| 8734 | prefixes['Moz' + styleProp] = 'moz' + eventName;
|
|---|
| 8735 | return prefixes;
|
|---|
| 8736 | }
|
|---|
| 8737 | /**
|
|---|
| 8738 | * A list of event names to a configurable list of vendor prefixes.
|
|---|
| 8739 | */
|
|---|
| 8740 |
|
|---|
| 8741 |
|
|---|
| 8742 | var vendorPrefixes = {
|
|---|
| 8743 | animationend: makePrefixMap('Animation', 'AnimationEnd'),
|
|---|
| 8744 | animationiteration: makePrefixMap('Animation', 'AnimationIteration'),
|
|---|
| 8745 | animationstart: makePrefixMap('Animation', 'AnimationStart'),
|
|---|
| 8746 | transitionend: makePrefixMap('Transition', 'TransitionEnd')
|
|---|
| 8747 | };
|
|---|
| 8748 | /**
|
|---|
| 8749 | * Event names that have already been detected and prefixed (if applicable).
|
|---|
| 8750 | */
|
|---|
| 8751 |
|
|---|
| 8752 | var prefixedEventNames = {};
|
|---|
| 8753 | /**
|
|---|
| 8754 | * Element to check for prefixes on.
|
|---|
| 8755 | */
|
|---|
| 8756 |
|
|---|
| 8757 | var style = {};
|
|---|
| 8758 | /**
|
|---|
| 8759 | * Bootstrap if a DOM exists.
|
|---|
| 8760 | */
|
|---|
| 8761 |
|
|---|
| 8762 | if (canUseDOM) {
|
|---|
| 8763 | style = document.createElement('div').style; // On some platforms, in particular some releases of Android 4.x,
|
|---|
| 8764 | // the un-prefixed "animation" and "transition" properties are defined on the
|
|---|
| 8765 | // style object but the events that fire will still be prefixed, so we need
|
|---|
| 8766 | // to check if the un-prefixed events are usable, and if not remove them from the map.
|
|---|
| 8767 |
|
|---|
| 8768 | if (!('AnimationEvent' in window)) {
|
|---|
| 8769 | delete vendorPrefixes.animationend.animation;
|
|---|
| 8770 | delete vendorPrefixes.animationiteration.animation;
|
|---|
| 8771 | delete vendorPrefixes.animationstart.animation;
|
|---|
| 8772 | } // Same as above
|
|---|
| 8773 |
|
|---|
| 8774 |
|
|---|
| 8775 | if (!('TransitionEvent' in window)) {
|
|---|
| 8776 | delete vendorPrefixes.transitionend.transition;
|
|---|
| 8777 | }
|
|---|
| 8778 | }
|
|---|
| 8779 | /**
|
|---|
| 8780 | * Attempts to determine the correct vendor prefixed event name.
|
|---|
| 8781 | *
|
|---|
| 8782 | * @param {string} eventName
|
|---|
| 8783 | * @returns {string}
|
|---|
| 8784 | */
|
|---|
| 8785 |
|
|---|
| 8786 |
|
|---|
| 8787 | function getVendorPrefixedEventName(eventName) {
|
|---|
| 8788 | if (prefixedEventNames[eventName]) {
|
|---|
| 8789 | return prefixedEventNames[eventName];
|
|---|
| 8790 | } else if (!vendorPrefixes[eventName]) {
|
|---|
| 8791 | return eventName;
|
|---|
| 8792 | }
|
|---|
| 8793 |
|
|---|
| 8794 | var prefixMap = vendorPrefixes[eventName];
|
|---|
| 8795 |
|
|---|
| 8796 | for (var styleProp in prefixMap) {
|
|---|
| 8797 | if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) {
|
|---|
| 8798 | return prefixedEventNames[eventName] = prefixMap[styleProp];
|
|---|
| 8799 | }
|
|---|
| 8800 | }
|
|---|
| 8801 |
|
|---|
| 8802 | return eventName;
|
|---|
| 8803 | }
|
|---|
| 8804 |
|
|---|
| 8805 | var ANIMATION_END = getVendorPrefixedEventName('animationend');
|
|---|
| 8806 | var ANIMATION_ITERATION = getVendorPrefixedEventName('animationiteration');
|
|---|
| 8807 | var ANIMATION_START = getVendorPrefixedEventName('animationstart');
|
|---|
| 8808 | var TRANSITION_END = getVendorPrefixedEventName('transitionend');
|
|---|
| 8809 |
|
|---|
| 8810 | var topLevelEventsToReactNames = new Map(); // NOTE: Capitalization is important in this list!
|
|---|
| 8811 | //
|
|---|
| 8812 | // E.g. it needs "pointerDown", not "pointerdown".
|
|---|
| 8813 | // This is because we derive both React name ("onPointerDown")
|
|---|
| 8814 | // and DOM name ("pointerdown") from the same list.
|
|---|
| 8815 | //
|
|---|
| 8816 | // Exceptions that don't match this convention are listed separately.
|
|---|
| 8817 | //
|
|---|
| 8818 | // prettier-ignore
|
|---|
| 8819 |
|
|---|
| 8820 | var simpleEventPluginEvents = ['abort', 'auxClick', 'cancel', 'canPlay', 'canPlayThrough', 'click', 'close', 'contextMenu', 'copy', 'cut', 'drag', 'dragEnd', 'dragEnter', 'dragExit', 'dragLeave', 'dragOver', 'dragStart', 'drop', 'durationChange', 'emptied', 'encrypted', 'ended', 'error', 'gotPointerCapture', 'input', 'invalid', 'keyDown', 'keyPress', 'keyUp', 'load', 'loadedData', 'loadedMetadata', 'loadStart', 'lostPointerCapture', 'mouseDown', 'mouseMove', 'mouseOut', 'mouseOver', 'mouseUp', 'paste', 'pause', 'play', 'playing', 'pointerCancel', 'pointerDown', 'pointerMove', 'pointerOut', 'pointerOver', 'pointerUp', 'progress', 'rateChange', 'reset', 'resize', 'seeked', 'seeking', 'stalled', 'submit', 'suspend', 'timeUpdate', 'touchCancel', 'touchEnd', 'touchStart', 'volumeChange', 'scroll', 'toggle', 'touchMove', 'waiting', 'wheel'];
|
|---|
| 8821 |
|
|---|
| 8822 | function registerSimpleEvent(domEventName, reactName) {
|
|---|
| 8823 | topLevelEventsToReactNames.set(domEventName, reactName);
|
|---|
| 8824 | registerTwoPhaseEvent(reactName, [domEventName]);
|
|---|
| 8825 | }
|
|---|
| 8826 |
|
|---|
| 8827 | function registerSimpleEvents() {
|
|---|
| 8828 | for (var i = 0; i < simpleEventPluginEvents.length; i++) {
|
|---|
| 8829 | var eventName = simpleEventPluginEvents[i];
|
|---|
| 8830 | var domEventName = eventName.toLowerCase();
|
|---|
| 8831 | var capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
|
|---|
| 8832 | registerSimpleEvent(domEventName, 'on' + capitalizedEvent);
|
|---|
| 8833 | } // Special cases where event names don't match.
|
|---|
| 8834 |
|
|---|
| 8835 |
|
|---|
| 8836 | registerSimpleEvent(ANIMATION_END, 'onAnimationEnd');
|
|---|
| 8837 | registerSimpleEvent(ANIMATION_ITERATION, 'onAnimationIteration');
|
|---|
| 8838 | registerSimpleEvent(ANIMATION_START, 'onAnimationStart');
|
|---|
| 8839 | registerSimpleEvent('dblclick', 'onDoubleClick');
|
|---|
| 8840 | registerSimpleEvent('focusin', 'onFocus');
|
|---|
| 8841 | registerSimpleEvent('focusout', 'onBlur');
|
|---|
| 8842 | registerSimpleEvent(TRANSITION_END, 'onTransitionEnd');
|
|---|
| 8843 | }
|
|---|
| 8844 |
|
|---|
| 8845 | function extractEvents$4(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 8846 | var reactName = topLevelEventsToReactNames.get(domEventName);
|
|---|
| 8847 |
|
|---|
| 8848 | if (reactName === undefined) {
|
|---|
| 8849 | return;
|
|---|
| 8850 | }
|
|---|
| 8851 |
|
|---|
| 8852 | var SyntheticEventCtor = SyntheticEvent;
|
|---|
| 8853 | var reactEventType = domEventName;
|
|---|
| 8854 |
|
|---|
| 8855 | switch (domEventName) {
|
|---|
| 8856 | case 'keypress':
|
|---|
| 8857 | // Firefox creates a keypress event for function keys too. This removes
|
|---|
| 8858 | // the unwanted keypress events. Enter is however both printable and
|
|---|
| 8859 | // non-printable. One would expect Tab to be as well (but it isn't).
|
|---|
| 8860 | if (getEventCharCode(nativeEvent) === 0) {
|
|---|
| 8861 | return;
|
|---|
| 8862 | }
|
|---|
| 8863 |
|
|---|
| 8864 | /* falls through */
|
|---|
| 8865 |
|
|---|
| 8866 | case 'keydown':
|
|---|
| 8867 | case 'keyup':
|
|---|
| 8868 | SyntheticEventCtor = SyntheticKeyboardEvent;
|
|---|
| 8869 | break;
|
|---|
| 8870 |
|
|---|
| 8871 | case 'focusin':
|
|---|
| 8872 | reactEventType = 'focus';
|
|---|
| 8873 | SyntheticEventCtor = SyntheticFocusEvent;
|
|---|
| 8874 | break;
|
|---|
| 8875 |
|
|---|
| 8876 | case 'focusout':
|
|---|
| 8877 | reactEventType = 'blur';
|
|---|
| 8878 | SyntheticEventCtor = SyntheticFocusEvent;
|
|---|
| 8879 | break;
|
|---|
| 8880 |
|
|---|
| 8881 | case 'beforeblur':
|
|---|
| 8882 | case 'afterblur':
|
|---|
| 8883 | SyntheticEventCtor = SyntheticFocusEvent;
|
|---|
| 8884 | break;
|
|---|
| 8885 |
|
|---|
| 8886 | case 'click':
|
|---|
| 8887 | // Firefox creates a click event on right mouse clicks. This removes the
|
|---|
| 8888 | // unwanted click events.
|
|---|
| 8889 | if (nativeEvent.button === 2) {
|
|---|
| 8890 | return;
|
|---|
| 8891 | }
|
|---|
| 8892 |
|
|---|
| 8893 | /* falls through */
|
|---|
| 8894 |
|
|---|
| 8895 | case 'auxclick':
|
|---|
| 8896 | case 'dblclick':
|
|---|
| 8897 | case 'mousedown':
|
|---|
| 8898 | case 'mousemove':
|
|---|
| 8899 | case 'mouseup': // TODO: Disabled elements should not respond to mouse events
|
|---|
| 8900 |
|
|---|
| 8901 | /* falls through */
|
|---|
| 8902 |
|
|---|
| 8903 | case 'mouseout':
|
|---|
| 8904 | case 'mouseover':
|
|---|
| 8905 | case 'contextmenu':
|
|---|
| 8906 | SyntheticEventCtor = SyntheticMouseEvent;
|
|---|
| 8907 | break;
|
|---|
| 8908 |
|
|---|
| 8909 | case 'drag':
|
|---|
| 8910 | case 'dragend':
|
|---|
| 8911 | case 'dragenter':
|
|---|
| 8912 | case 'dragexit':
|
|---|
| 8913 | case 'dragleave':
|
|---|
| 8914 | case 'dragover':
|
|---|
| 8915 | case 'dragstart':
|
|---|
| 8916 | case 'drop':
|
|---|
| 8917 | SyntheticEventCtor = SyntheticDragEvent;
|
|---|
| 8918 | break;
|
|---|
| 8919 |
|
|---|
| 8920 | case 'touchcancel':
|
|---|
| 8921 | case 'touchend':
|
|---|
| 8922 | case 'touchmove':
|
|---|
| 8923 | case 'touchstart':
|
|---|
| 8924 | SyntheticEventCtor = SyntheticTouchEvent;
|
|---|
| 8925 | break;
|
|---|
| 8926 |
|
|---|
| 8927 | case ANIMATION_END:
|
|---|
| 8928 | case ANIMATION_ITERATION:
|
|---|
| 8929 | case ANIMATION_START:
|
|---|
| 8930 | SyntheticEventCtor = SyntheticAnimationEvent;
|
|---|
| 8931 | break;
|
|---|
| 8932 |
|
|---|
| 8933 | case TRANSITION_END:
|
|---|
| 8934 | SyntheticEventCtor = SyntheticTransitionEvent;
|
|---|
| 8935 | break;
|
|---|
| 8936 |
|
|---|
| 8937 | case 'scroll':
|
|---|
| 8938 | SyntheticEventCtor = SyntheticUIEvent;
|
|---|
| 8939 | break;
|
|---|
| 8940 |
|
|---|
| 8941 | case 'wheel':
|
|---|
| 8942 | SyntheticEventCtor = SyntheticWheelEvent;
|
|---|
| 8943 | break;
|
|---|
| 8944 |
|
|---|
| 8945 | case 'copy':
|
|---|
| 8946 | case 'cut':
|
|---|
| 8947 | case 'paste':
|
|---|
| 8948 | SyntheticEventCtor = SyntheticClipboardEvent;
|
|---|
| 8949 | break;
|
|---|
| 8950 |
|
|---|
| 8951 | case 'gotpointercapture':
|
|---|
| 8952 | case 'lostpointercapture':
|
|---|
| 8953 | case 'pointercancel':
|
|---|
| 8954 | case 'pointerdown':
|
|---|
| 8955 | case 'pointermove':
|
|---|
| 8956 | case 'pointerout':
|
|---|
| 8957 | case 'pointerover':
|
|---|
| 8958 | case 'pointerup':
|
|---|
| 8959 | SyntheticEventCtor = SyntheticPointerEvent;
|
|---|
| 8960 | break;
|
|---|
| 8961 | }
|
|---|
| 8962 |
|
|---|
| 8963 | var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
|
|---|
| 8964 |
|
|---|
| 8965 | {
|
|---|
| 8966 | // Some events don't bubble in the browser.
|
|---|
| 8967 | // In the past, React has always bubbled them, but this can be surprising.
|
|---|
| 8968 | // We're going to try aligning closer to the browser behavior by not bubbling
|
|---|
| 8969 | // them in React either. We'll start by not bubbling onScroll, and then expand.
|
|---|
| 8970 | var accumulateTargetOnly = !inCapturePhase && // TODO: ideally, we'd eventually add all events from
|
|---|
| 8971 | // nonDelegatedEvents list in DOMPluginEventSystem.
|
|---|
| 8972 | // Then we can remove this special list.
|
|---|
| 8973 | // This is a breaking change that can wait until React 18.
|
|---|
| 8974 | domEventName === 'scroll';
|
|---|
| 8975 |
|
|---|
| 8976 | var _listeners = accumulateSinglePhaseListeners(targetInst, reactName, nativeEvent.type, inCapturePhase, accumulateTargetOnly);
|
|---|
| 8977 |
|
|---|
| 8978 | if (_listeners.length > 0) {
|
|---|
| 8979 | // Intentionally create event lazily.
|
|---|
| 8980 | var _event = new SyntheticEventCtor(reactName, reactEventType, null, nativeEvent, nativeEventTarget);
|
|---|
| 8981 |
|
|---|
| 8982 | dispatchQueue.push({
|
|---|
| 8983 | event: _event,
|
|---|
| 8984 | listeners: _listeners
|
|---|
| 8985 | });
|
|---|
| 8986 | }
|
|---|
| 8987 | }
|
|---|
| 8988 | }
|
|---|
| 8989 |
|
|---|
| 8990 | // TODO: remove top-level side effect.
|
|---|
| 8991 | registerSimpleEvents();
|
|---|
| 8992 | registerEvents$2();
|
|---|
| 8993 | registerEvents$1();
|
|---|
| 8994 | registerEvents$3();
|
|---|
| 8995 | registerEvents();
|
|---|
| 8996 |
|
|---|
| 8997 | function extractEvents$5(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) {
|
|---|
| 8998 | // TODO: we should remove the concept of a "SimpleEventPlugin".
|
|---|
| 8999 | // This is the basic functionality of the event system. All
|
|---|
| 9000 | // the other plugins are essentially polyfills. So the plugin
|
|---|
| 9001 | // should probably be inlined somewhere and have its logic
|
|---|
| 9002 | // be core the to event system. This would potentially allow
|
|---|
| 9003 | // us to ship builds of React without the polyfilled plugins below.
|
|---|
| 9004 | extractEvents$4(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags);
|
|---|
| 9005 | var shouldProcessPolyfillPlugins = (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the
|
|---|
| 9006 | // event's native "bubble" phase, which means that we're
|
|---|
| 9007 | // not in the capture phase. That's because we emulate
|
|---|
| 9008 | // the capture phase here still. This is a trade-off,
|
|---|
| 9009 | // because in an ideal world we would not emulate and use
|
|---|
| 9010 | // the phases properly, like we do with the SimpleEvent
|
|---|
| 9011 | // plugin. However, the plugins below either expect
|
|---|
| 9012 | // emulation (EnterLeave) or use state localized to that
|
|---|
| 9013 | // plugin (BeforeInput, Change, Select). The state in
|
|---|
| 9014 | // these modules complicates things, as you'll essentially
|
|---|
| 9015 | // get the case where the capture phase event might change
|
|---|
| 9016 | // state, only for the following bubble event to come in
|
|---|
| 9017 | // later and not trigger anything as the state now
|
|---|
| 9018 | // invalidates the heuristics of the event plugin. We
|
|---|
| 9019 | // could alter all these plugins to work in such ways, but
|
|---|
| 9020 | // that might cause other unknown side-effects that we
|
|---|
| 9021 | // can't foresee right now.
|
|---|
| 9022 |
|
|---|
| 9023 | if (shouldProcessPolyfillPlugins) {
|
|---|
| 9024 | extractEvents$2(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 9025 | extractEvents$1(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 9026 | extractEvents$3(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 9027 | extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget);
|
|---|
| 9028 | }
|
|---|
| 9029 | } // List of events that need to be individually attached to media elements.
|
|---|
| 9030 |
|
|---|
| 9031 |
|
|---|
| 9032 | var mediaEventTypes = ['abort', 'canplay', 'canplaythrough', 'durationchange', 'emptied', 'encrypted', 'ended', 'error', 'loadeddata', 'loadedmetadata', 'loadstart', 'pause', 'play', 'playing', 'progress', 'ratechange', 'resize', 'seeked', 'seeking', 'stalled', 'suspend', 'timeupdate', 'volumechange', 'waiting']; // We should not delegate these events to the container, but rather
|
|---|
| 9033 | // set them on the actual target element itself. This is primarily
|
|---|
| 9034 | // because these events do not consistently bubble in the DOM.
|
|---|
| 9035 |
|
|---|
| 9036 | var nonDelegatedEvents = new Set(['cancel', 'close', 'invalid', 'load', 'scroll', 'toggle'].concat(mediaEventTypes));
|
|---|
| 9037 |
|
|---|
| 9038 | function executeDispatch(event, listener, currentTarget) {
|
|---|
| 9039 | var type = event.type || 'unknown-event';
|
|---|
| 9040 | event.currentTarget = currentTarget;
|
|---|
| 9041 | invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
|
|---|
| 9042 | event.currentTarget = null;
|
|---|
| 9043 | }
|
|---|
| 9044 |
|
|---|
| 9045 | function processDispatchQueueItemsInOrder(event, dispatchListeners, inCapturePhase) {
|
|---|
| 9046 | var previousInstance;
|
|---|
| 9047 |
|
|---|
| 9048 | if (inCapturePhase) {
|
|---|
| 9049 | for (var i = dispatchListeners.length - 1; i >= 0; i--) {
|
|---|
| 9050 | var _dispatchListeners$i = dispatchListeners[i],
|
|---|
| 9051 | instance = _dispatchListeners$i.instance,
|
|---|
| 9052 | currentTarget = _dispatchListeners$i.currentTarget,
|
|---|
| 9053 | listener = _dispatchListeners$i.listener;
|
|---|
| 9054 |
|
|---|
| 9055 | if (instance !== previousInstance && event.isPropagationStopped()) {
|
|---|
| 9056 | return;
|
|---|
| 9057 | }
|
|---|
| 9058 |
|
|---|
| 9059 | executeDispatch(event, listener, currentTarget);
|
|---|
| 9060 | previousInstance = instance;
|
|---|
| 9061 | }
|
|---|
| 9062 | } else {
|
|---|
| 9063 | for (var _i = 0; _i < dispatchListeners.length; _i++) {
|
|---|
| 9064 | var _dispatchListeners$_i = dispatchListeners[_i],
|
|---|
| 9065 | _instance = _dispatchListeners$_i.instance,
|
|---|
| 9066 | _currentTarget = _dispatchListeners$_i.currentTarget,
|
|---|
| 9067 | _listener = _dispatchListeners$_i.listener;
|
|---|
| 9068 |
|
|---|
| 9069 | if (_instance !== previousInstance && event.isPropagationStopped()) {
|
|---|
| 9070 | return;
|
|---|
| 9071 | }
|
|---|
| 9072 |
|
|---|
| 9073 | executeDispatch(event, _listener, _currentTarget);
|
|---|
| 9074 | previousInstance = _instance;
|
|---|
| 9075 | }
|
|---|
| 9076 | }
|
|---|
| 9077 | }
|
|---|
| 9078 |
|
|---|
| 9079 | function processDispatchQueue(dispatchQueue, eventSystemFlags) {
|
|---|
| 9080 | var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0;
|
|---|
| 9081 |
|
|---|
| 9082 | for (var i = 0; i < dispatchQueue.length; i++) {
|
|---|
| 9083 | var _dispatchQueue$i = dispatchQueue[i],
|
|---|
| 9084 | event = _dispatchQueue$i.event,
|
|---|
| 9085 | listeners = _dispatchQueue$i.listeners;
|
|---|
| 9086 | processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); // event system doesn't use pooling.
|
|---|
| 9087 | } // This would be a good time to rethrow if any of the event handlers threw.
|
|---|
| 9088 |
|
|---|
| 9089 |
|
|---|
| 9090 | rethrowCaughtError();
|
|---|
| 9091 | }
|
|---|
| 9092 |
|
|---|
| 9093 | function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
|
|---|
| 9094 | var nativeEventTarget = getEventTarget(nativeEvent);
|
|---|
| 9095 | var dispatchQueue = [];
|
|---|
| 9096 | extractEvents$5(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags);
|
|---|
| 9097 | processDispatchQueue(dispatchQueue, eventSystemFlags);
|
|---|
| 9098 | }
|
|---|
| 9099 |
|
|---|
| 9100 | function listenToNonDelegatedEvent(domEventName, targetElement) {
|
|---|
| 9101 | {
|
|---|
| 9102 | if (!nonDelegatedEvents.has(domEventName)) {
|
|---|
| 9103 | error('Did not expect a listenToNonDelegatedEvent() call for "%s". ' + 'This is a bug in React. Please file an issue.', domEventName);
|
|---|
| 9104 | }
|
|---|
| 9105 | }
|
|---|
| 9106 |
|
|---|
| 9107 | var isCapturePhaseListener = false;
|
|---|
| 9108 | var listenerSet = getEventListenerSet(targetElement);
|
|---|
| 9109 | var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener);
|
|---|
| 9110 |
|
|---|
| 9111 | if (!listenerSet.has(listenerSetKey)) {
|
|---|
| 9112 | addTrappedEventListener(targetElement, domEventName, IS_NON_DELEGATED, isCapturePhaseListener);
|
|---|
| 9113 | listenerSet.add(listenerSetKey);
|
|---|
| 9114 | }
|
|---|
| 9115 | }
|
|---|
| 9116 | function listenToNativeEvent(domEventName, isCapturePhaseListener, target) {
|
|---|
| 9117 | {
|
|---|
| 9118 | if (nonDelegatedEvents.has(domEventName) && !isCapturePhaseListener) {
|
|---|
| 9119 | error('Did not expect a listenToNativeEvent() call for "%s" in the bubble phase. ' + 'This is a bug in React. Please file an issue.', domEventName);
|
|---|
| 9120 | }
|
|---|
| 9121 | }
|
|---|
| 9122 |
|
|---|
| 9123 | var eventSystemFlags = 0;
|
|---|
| 9124 |
|
|---|
| 9125 | if (isCapturePhaseListener) {
|
|---|
| 9126 | eventSystemFlags |= IS_CAPTURE_PHASE;
|
|---|
| 9127 | }
|
|---|
| 9128 |
|
|---|
| 9129 | addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener);
|
|---|
| 9130 | } // This is only used by createEventHandle when the
|
|---|
| 9131 | var listeningMarker = '_reactListening' + Math.random().toString(36).slice(2);
|
|---|
| 9132 | function listenToAllSupportedEvents(rootContainerElement) {
|
|---|
| 9133 | if (!rootContainerElement[listeningMarker]) {
|
|---|
| 9134 | rootContainerElement[listeningMarker] = true;
|
|---|
| 9135 | allNativeEvents.forEach(function (domEventName) {
|
|---|
| 9136 | // We handle selectionchange separately because it
|
|---|
| 9137 | // doesn't bubble and needs to be on the document.
|
|---|
| 9138 | if (domEventName !== 'selectionchange') {
|
|---|
| 9139 | if (!nonDelegatedEvents.has(domEventName)) {
|
|---|
| 9140 | listenToNativeEvent(domEventName, false, rootContainerElement);
|
|---|
| 9141 | }
|
|---|
| 9142 |
|
|---|
| 9143 | listenToNativeEvent(domEventName, true, rootContainerElement);
|
|---|
| 9144 | }
|
|---|
| 9145 | });
|
|---|
| 9146 | var ownerDocument = rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument;
|
|---|
| 9147 |
|
|---|
| 9148 | if (ownerDocument !== null) {
|
|---|
| 9149 | // The selectionchange event also needs deduplication
|
|---|
| 9150 | // but it is attached to the document.
|
|---|
| 9151 | if (!ownerDocument[listeningMarker]) {
|
|---|
| 9152 | ownerDocument[listeningMarker] = true;
|
|---|
| 9153 | listenToNativeEvent('selectionchange', false, ownerDocument);
|
|---|
| 9154 | }
|
|---|
| 9155 | }
|
|---|
| 9156 | }
|
|---|
| 9157 | }
|
|---|
| 9158 |
|
|---|
| 9159 | function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener, isDeferredListenerForLegacyFBSupport) {
|
|---|
| 9160 | var listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags); // If passive option is not supported, then the event will be
|
|---|
| 9161 | // active and not passive.
|
|---|
| 9162 |
|
|---|
| 9163 | var isPassiveListener = undefined;
|
|---|
| 9164 |
|
|---|
| 9165 | if (passiveBrowserEventsSupported) {
|
|---|
| 9166 | // Browsers introduced an intervention, making these events
|
|---|
| 9167 | // passive by default on document. React doesn't bind them
|
|---|
| 9168 | // to document anymore, but changing this now would undo
|
|---|
| 9169 | // the performance wins from the change. So we emulate
|
|---|
| 9170 | // the existing behavior manually on the roots now.
|
|---|
| 9171 | // https://github.com/facebook/react/issues/19651
|
|---|
| 9172 | if (domEventName === 'touchstart' || domEventName === 'touchmove' || domEventName === 'wheel') {
|
|---|
| 9173 | isPassiveListener = true;
|
|---|
| 9174 | }
|
|---|
| 9175 | }
|
|---|
| 9176 |
|
|---|
| 9177 | targetContainer = targetContainer;
|
|---|
| 9178 | var unsubscribeListener; // When legacyFBSupport is enabled, it's for when we
|
|---|
| 9179 |
|
|---|
| 9180 |
|
|---|
| 9181 | if (isCapturePhaseListener) {
|
|---|
| 9182 | if (isPassiveListener !== undefined) {
|
|---|
| 9183 | unsubscribeListener = addEventCaptureListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
|
|---|
| 9184 | } else {
|
|---|
| 9185 | unsubscribeListener = addEventCaptureListener(targetContainer, domEventName, listener);
|
|---|
| 9186 | }
|
|---|
| 9187 | } else {
|
|---|
| 9188 | if (isPassiveListener !== undefined) {
|
|---|
| 9189 | unsubscribeListener = addEventBubbleListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener);
|
|---|
| 9190 | } else {
|
|---|
| 9191 | unsubscribeListener = addEventBubbleListener(targetContainer, domEventName, listener);
|
|---|
| 9192 | }
|
|---|
| 9193 | }
|
|---|
| 9194 | }
|
|---|
| 9195 |
|
|---|
| 9196 | function isMatchingRootContainer(grandContainer, targetContainer) {
|
|---|
| 9197 | return grandContainer === targetContainer || grandContainer.nodeType === COMMENT_NODE && grandContainer.parentNode === targetContainer;
|
|---|
| 9198 | }
|
|---|
| 9199 |
|
|---|
| 9200 | function dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) {
|
|---|
| 9201 | var ancestorInst = targetInst;
|
|---|
| 9202 |
|
|---|
| 9203 | if ((eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 && (eventSystemFlags & IS_NON_DELEGATED) === 0) {
|
|---|
| 9204 | var targetContainerNode = targetContainer; // If we are using the legacy FB support flag, we
|
|---|
| 9205 |
|
|---|
| 9206 | if (targetInst !== null) {
|
|---|
| 9207 | // The below logic attempts to work out if we need to change
|
|---|
| 9208 | // the target fiber to a different ancestor. We had similar logic
|
|---|
| 9209 | // in the legacy event system, except the big difference between
|
|---|
| 9210 | // systems is that the modern event system now has an event listener
|
|---|
| 9211 | // attached to each React Root and React Portal Root. Together,
|
|---|
| 9212 | // the DOM nodes representing these roots are the "rootContainer".
|
|---|
| 9213 | // To figure out which ancestor instance we should use, we traverse
|
|---|
| 9214 | // up the fiber tree from the target instance and attempt to find
|
|---|
| 9215 | // root boundaries that match that of our current "rootContainer".
|
|---|
| 9216 | // If we find that "rootContainer", we find the parent fiber
|
|---|
| 9217 | // sub-tree for that root and make that our ancestor instance.
|
|---|
| 9218 | var node = targetInst;
|
|---|
| 9219 |
|
|---|
| 9220 | mainLoop: while (true) {
|
|---|
| 9221 | if (node === null) {
|
|---|
| 9222 | return;
|
|---|
| 9223 | }
|
|---|
| 9224 |
|
|---|
| 9225 | var nodeTag = node.tag;
|
|---|
| 9226 |
|
|---|
| 9227 | if (nodeTag === HostRoot || nodeTag === HostPortal) {
|
|---|
| 9228 | var container = node.stateNode.containerInfo;
|
|---|
| 9229 |
|
|---|
| 9230 | if (isMatchingRootContainer(container, targetContainerNode)) {
|
|---|
| 9231 | break;
|
|---|
| 9232 | }
|
|---|
| 9233 |
|
|---|
| 9234 | if (nodeTag === HostPortal) {
|
|---|
| 9235 | // The target is a portal, but it's not the rootContainer we're looking for.
|
|---|
| 9236 | // Normally portals handle their own events all the way down to the root.
|
|---|
| 9237 | // So we should be able to stop now. However, we don't know if this portal
|
|---|
| 9238 | // was part of *our* root.
|
|---|
| 9239 | var grandNode = node.return;
|
|---|
| 9240 |
|
|---|
| 9241 | while (grandNode !== null) {
|
|---|
| 9242 | var grandTag = grandNode.tag;
|
|---|
| 9243 |
|
|---|
| 9244 | if (grandTag === HostRoot || grandTag === HostPortal) {
|
|---|
| 9245 | var grandContainer = grandNode.stateNode.containerInfo;
|
|---|
| 9246 |
|
|---|
| 9247 | if (isMatchingRootContainer(grandContainer, targetContainerNode)) {
|
|---|
| 9248 | // This is the rootContainer we're looking for and we found it as
|
|---|
| 9249 | // a parent of the Portal. That means we can ignore it because the
|
|---|
| 9250 | // Portal will bubble through to us.
|
|---|
| 9251 | return;
|
|---|
| 9252 | }
|
|---|
| 9253 | }
|
|---|
| 9254 |
|
|---|
| 9255 | grandNode = grandNode.return;
|
|---|
| 9256 | }
|
|---|
| 9257 | } // Now we need to find it's corresponding host fiber in the other
|
|---|
| 9258 | // tree. To do this we can use getClosestInstanceFromNode, but we
|
|---|
| 9259 | // need to validate that the fiber is a host instance, otherwise
|
|---|
| 9260 | // we need to traverse up through the DOM till we find the correct
|
|---|
| 9261 | // node that is from the other tree.
|
|---|
| 9262 |
|
|---|
| 9263 |
|
|---|
| 9264 | while (container !== null) {
|
|---|
| 9265 | var parentNode = getClosestInstanceFromNode(container);
|
|---|
| 9266 |
|
|---|
| 9267 | if (parentNode === null) {
|
|---|
| 9268 | return;
|
|---|
| 9269 | }
|
|---|
| 9270 |
|
|---|
| 9271 | var parentTag = parentNode.tag;
|
|---|
| 9272 |
|
|---|
| 9273 | if (parentTag === HostComponent || parentTag === HostText) {
|
|---|
| 9274 | node = ancestorInst = parentNode;
|
|---|
| 9275 | continue mainLoop;
|
|---|
| 9276 | }
|
|---|
| 9277 |
|
|---|
| 9278 | container = container.parentNode;
|
|---|
| 9279 | }
|
|---|
| 9280 | }
|
|---|
| 9281 |
|
|---|
| 9282 | node = node.return;
|
|---|
| 9283 | }
|
|---|
| 9284 | }
|
|---|
| 9285 | }
|
|---|
| 9286 |
|
|---|
| 9287 | batchedUpdates(function () {
|
|---|
| 9288 | return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, ancestorInst);
|
|---|
| 9289 | });
|
|---|
| 9290 | }
|
|---|
| 9291 |
|
|---|
| 9292 | function createDispatchListener(instance, listener, currentTarget) {
|
|---|
| 9293 | return {
|
|---|
| 9294 | instance: instance,
|
|---|
| 9295 | listener: listener,
|
|---|
| 9296 | currentTarget: currentTarget
|
|---|
| 9297 | };
|
|---|
| 9298 | }
|
|---|
| 9299 |
|
|---|
| 9300 | function accumulateSinglePhaseListeners(targetFiber, reactName, nativeEventType, inCapturePhase, accumulateTargetOnly, nativeEvent) {
|
|---|
| 9301 | var captureName = reactName !== null ? reactName + 'Capture' : null;
|
|---|
| 9302 | var reactEventName = inCapturePhase ? captureName : reactName;
|
|---|
| 9303 | var listeners = [];
|
|---|
| 9304 | var instance = targetFiber;
|
|---|
| 9305 | var lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path.
|
|---|
| 9306 |
|
|---|
| 9307 | while (instance !== null) {
|
|---|
| 9308 | var _instance2 = instance,
|
|---|
| 9309 | stateNode = _instance2.stateNode,
|
|---|
| 9310 | tag = _instance2.tag; // Handle listeners that are on HostComponents (i.e. <div>)
|
|---|
| 9311 |
|
|---|
| 9312 | if (tag === HostComponent && stateNode !== null) {
|
|---|
| 9313 | lastHostComponent = stateNode; // createEventHandle listeners
|
|---|
| 9314 |
|
|---|
| 9315 |
|
|---|
| 9316 | if (reactEventName !== null) {
|
|---|
| 9317 | var listener = getListener(instance, reactEventName);
|
|---|
| 9318 |
|
|---|
| 9319 | if (listener != null) {
|
|---|
| 9320 | listeners.push(createDispatchListener(instance, listener, lastHostComponent));
|
|---|
| 9321 | }
|
|---|
| 9322 | }
|
|---|
| 9323 | } // If we are only accumulating events for the target, then we don't
|
|---|
| 9324 | // continue to propagate through the React fiber tree to find other
|
|---|
| 9325 | // listeners.
|
|---|
| 9326 |
|
|---|
| 9327 |
|
|---|
| 9328 | if (accumulateTargetOnly) {
|
|---|
| 9329 | break;
|
|---|
| 9330 | } // If we are processing the onBeforeBlur event, then we need to take
|
|---|
| 9331 |
|
|---|
| 9332 | instance = instance.return;
|
|---|
| 9333 | }
|
|---|
| 9334 |
|
|---|
| 9335 | return listeners;
|
|---|
| 9336 | } // We should only use this function for:
|
|---|
| 9337 | // - BeforeInputEventPlugin
|
|---|
| 9338 | // - ChangeEventPlugin
|
|---|
| 9339 | // - SelectEventPlugin
|
|---|
| 9340 | // This is because we only process these plugins
|
|---|
| 9341 | // in the bubble phase, so we need to accumulate two
|
|---|
| 9342 | // phase event listeners (via emulation).
|
|---|
| 9343 |
|
|---|
| 9344 | function accumulateTwoPhaseListeners(targetFiber, reactName) {
|
|---|
| 9345 | var captureName = reactName + 'Capture';
|
|---|
| 9346 | var listeners = [];
|
|---|
| 9347 | var instance = targetFiber; // Accumulate all instances and listeners via the target -> root path.
|
|---|
| 9348 |
|
|---|
| 9349 | while (instance !== null) {
|
|---|
| 9350 | var _instance3 = instance,
|
|---|
| 9351 | stateNode = _instance3.stateNode,
|
|---|
| 9352 | tag = _instance3.tag; // Handle listeners that are on HostComponents (i.e. <div>)
|
|---|
| 9353 |
|
|---|
| 9354 | if (tag === HostComponent && stateNode !== null) {
|
|---|
| 9355 | var currentTarget = stateNode;
|
|---|
| 9356 | var captureListener = getListener(instance, captureName);
|
|---|
| 9357 |
|
|---|
| 9358 | if (captureListener != null) {
|
|---|
| 9359 | listeners.unshift(createDispatchListener(instance, captureListener, currentTarget));
|
|---|
| 9360 | }
|
|---|
| 9361 |
|
|---|
| 9362 | var bubbleListener = getListener(instance, reactName);
|
|---|
| 9363 |
|
|---|
| 9364 | if (bubbleListener != null) {
|
|---|
| 9365 | listeners.push(createDispatchListener(instance, bubbleListener, currentTarget));
|
|---|
| 9366 | }
|
|---|
| 9367 | }
|
|---|
| 9368 |
|
|---|
| 9369 | instance = instance.return;
|
|---|
| 9370 | }
|
|---|
| 9371 |
|
|---|
| 9372 | return listeners;
|
|---|
| 9373 | }
|
|---|
| 9374 |
|
|---|
| 9375 | function getParent(inst) {
|
|---|
| 9376 | if (inst === null) {
|
|---|
| 9377 | return null;
|
|---|
| 9378 | }
|
|---|
| 9379 |
|
|---|
| 9380 | do {
|
|---|
| 9381 | inst = inst.return; // TODO: If this is a HostRoot we might want to bail out.
|
|---|
| 9382 | // That is depending on if we want nested subtrees (layers) to bubble
|
|---|
| 9383 | // events to their parent. We could also go through parentNode on the
|
|---|
| 9384 | // host node but that wouldn't work for React Native and doesn't let us
|
|---|
| 9385 | // do the portal feature.
|
|---|
| 9386 | } while (inst && inst.tag !== HostComponent);
|
|---|
| 9387 |
|
|---|
| 9388 | if (inst) {
|
|---|
| 9389 | return inst;
|
|---|
| 9390 | }
|
|---|
| 9391 |
|
|---|
| 9392 | return null;
|
|---|
| 9393 | }
|
|---|
| 9394 | /**
|
|---|
| 9395 | * Return the lowest common ancestor of A and B, or null if they are in
|
|---|
| 9396 | * different trees.
|
|---|
| 9397 | */
|
|---|
| 9398 |
|
|---|
| 9399 |
|
|---|
| 9400 | function getLowestCommonAncestor(instA, instB) {
|
|---|
| 9401 | var nodeA = instA;
|
|---|
| 9402 | var nodeB = instB;
|
|---|
| 9403 | var depthA = 0;
|
|---|
| 9404 |
|
|---|
| 9405 | for (var tempA = nodeA; tempA; tempA = getParent(tempA)) {
|
|---|
| 9406 | depthA++;
|
|---|
| 9407 | }
|
|---|
| 9408 |
|
|---|
| 9409 | var depthB = 0;
|
|---|
| 9410 |
|
|---|
| 9411 | for (var tempB = nodeB; tempB; tempB = getParent(tempB)) {
|
|---|
| 9412 | depthB++;
|
|---|
| 9413 | } // If A is deeper, crawl up.
|
|---|
| 9414 |
|
|---|
| 9415 |
|
|---|
| 9416 | while (depthA - depthB > 0) {
|
|---|
| 9417 | nodeA = getParent(nodeA);
|
|---|
| 9418 | depthA--;
|
|---|
| 9419 | } // If B is deeper, crawl up.
|
|---|
| 9420 |
|
|---|
| 9421 |
|
|---|
| 9422 | while (depthB - depthA > 0) {
|
|---|
| 9423 | nodeB = getParent(nodeB);
|
|---|
| 9424 | depthB--;
|
|---|
| 9425 | } // Walk in lockstep until we find a match.
|
|---|
| 9426 |
|
|---|
| 9427 |
|
|---|
| 9428 | var depth = depthA;
|
|---|
| 9429 |
|
|---|
| 9430 | while (depth--) {
|
|---|
| 9431 | if (nodeA === nodeB || nodeB !== null && nodeA === nodeB.alternate) {
|
|---|
| 9432 | return nodeA;
|
|---|
| 9433 | }
|
|---|
| 9434 |
|
|---|
| 9435 | nodeA = getParent(nodeA);
|
|---|
| 9436 | nodeB = getParent(nodeB);
|
|---|
| 9437 | }
|
|---|
| 9438 |
|
|---|
| 9439 | return null;
|
|---|
| 9440 | }
|
|---|
| 9441 |
|
|---|
| 9442 | function accumulateEnterLeaveListenersForEvent(dispatchQueue, event, target, common, inCapturePhase) {
|
|---|
| 9443 | var registrationName = event._reactName;
|
|---|
| 9444 | var listeners = [];
|
|---|
| 9445 | var instance = target;
|
|---|
| 9446 |
|
|---|
| 9447 | while (instance !== null) {
|
|---|
| 9448 | if (instance === common) {
|
|---|
| 9449 | break;
|
|---|
| 9450 | }
|
|---|
| 9451 |
|
|---|
| 9452 | var _instance4 = instance,
|
|---|
| 9453 | alternate = _instance4.alternate,
|
|---|
| 9454 | stateNode = _instance4.stateNode,
|
|---|
| 9455 | tag = _instance4.tag;
|
|---|
| 9456 |
|
|---|
| 9457 | if (alternate !== null && alternate === common) {
|
|---|
| 9458 | break;
|
|---|
| 9459 | }
|
|---|
| 9460 |
|
|---|
| 9461 | if (tag === HostComponent && stateNode !== null) {
|
|---|
| 9462 | var currentTarget = stateNode;
|
|---|
| 9463 |
|
|---|
| 9464 | if (inCapturePhase) {
|
|---|
| 9465 | var captureListener = getListener(instance, registrationName);
|
|---|
| 9466 |
|
|---|
| 9467 | if (captureListener != null) {
|
|---|
| 9468 | listeners.unshift(createDispatchListener(instance, captureListener, currentTarget));
|
|---|
| 9469 | }
|
|---|
| 9470 | } else if (!inCapturePhase) {
|
|---|
| 9471 | var bubbleListener = getListener(instance, registrationName);
|
|---|
| 9472 |
|
|---|
| 9473 | if (bubbleListener != null) {
|
|---|
| 9474 | listeners.push(createDispatchListener(instance, bubbleListener, currentTarget));
|
|---|
| 9475 | }
|
|---|
| 9476 | }
|
|---|
| 9477 | }
|
|---|
| 9478 |
|
|---|
| 9479 | instance = instance.return;
|
|---|
| 9480 | }
|
|---|
| 9481 |
|
|---|
| 9482 | if (listeners.length !== 0) {
|
|---|
| 9483 | dispatchQueue.push({
|
|---|
| 9484 | event: event,
|
|---|
| 9485 | listeners: listeners
|
|---|
| 9486 | });
|
|---|
| 9487 | }
|
|---|
| 9488 | } // We should only use this function for:
|
|---|
| 9489 | // - EnterLeaveEventPlugin
|
|---|
| 9490 | // This is because we only process this plugin
|
|---|
| 9491 | // in the bubble phase, so we need to accumulate two
|
|---|
| 9492 | // phase event listeners.
|
|---|
| 9493 |
|
|---|
| 9494 |
|
|---|
| 9495 | function accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leaveEvent, enterEvent, from, to) {
|
|---|
| 9496 | var common = from && to ? getLowestCommonAncestor(from, to) : null;
|
|---|
| 9497 |
|
|---|
| 9498 | if (from !== null) {
|
|---|
| 9499 | accumulateEnterLeaveListenersForEvent(dispatchQueue, leaveEvent, from, common, false);
|
|---|
| 9500 | }
|
|---|
| 9501 |
|
|---|
| 9502 | if (to !== null && enterEvent !== null) {
|
|---|
| 9503 | accumulateEnterLeaveListenersForEvent(dispatchQueue, enterEvent, to, common, true);
|
|---|
| 9504 | }
|
|---|
| 9505 | }
|
|---|
| 9506 | function getListenerSetKey(domEventName, capture) {
|
|---|
| 9507 | return domEventName + "__" + (capture ? 'capture' : 'bubble');
|
|---|
| 9508 | }
|
|---|
| 9509 |
|
|---|
| 9510 | var didWarnInvalidHydration = false;
|
|---|
| 9511 | var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML';
|
|---|
| 9512 | var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning';
|
|---|
| 9513 | var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning';
|
|---|
| 9514 | var AUTOFOCUS = 'autoFocus';
|
|---|
| 9515 | var CHILDREN = 'children';
|
|---|
| 9516 | var STYLE = 'style';
|
|---|
| 9517 | var HTML$1 = '__html';
|
|---|
| 9518 | var warnedUnknownTags;
|
|---|
| 9519 | var validatePropertiesInDevelopment;
|
|---|
| 9520 | var warnForPropDifference;
|
|---|
| 9521 | var warnForExtraAttributes;
|
|---|
| 9522 | var warnForInvalidEventListener;
|
|---|
| 9523 | var canDiffStyleForHydrationWarning;
|
|---|
| 9524 | var normalizeHTML;
|
|---|
| 9525 |
|
|---|
| 9526 | {
|
|---|
| 9527 | warnedUnknownTags = {
|
|---|
| 9528 | // There are working polyfills for <dialog>. Let people use it.
|
|---|
| 9529 | dialog: true,
|
|---|
| 9530 | // Electron ships a custom <webview> tag to display external web content in
|
|---|
| 9531 | // an isolated frame and process.
|
|---|
| 9532 | // This tag is not present in non Electron environments such as JSDom which
|
|---|
| 9533 | // is often used for testing purposes.
|
|---|
| 9534 | // @see https://electronjs.org/docs/api/webview-tag
|
|---|
| 9535 | webview: true
|
|---|
| 9536 | };
|
|---|
| 9537 |
|
|---|
| 9538 | validatePropertiesInDevelopment = function (type, props) {
|
|---|
| 9539 | validateProperties(type, props);
|
|---|
| 9540 | validateProperties$1(type, props);
|
|---|
| 9541 | validateProperties$2(type, props, {
|
|---|
| 9542 | registrationNameDependencies: registrationNameDependencies,
|
|---|
| 9543 | possibleRegistrationNames: possibleRegistrationNames
|
|---|
| 9544 | });
|
|---|
| 9545 | }; // IE 11 parses & normalizes the style attribute as opposed to other
|
|---|
| 9546 | // browsers. It adds spaces and sorts the properties in some
|
|---|
| 9547 | // non-alphabetical order. Handling that would require sorting CSS
|
|---|
| 9548 | // properties in the client & server versions or applying
|
|---|
| 9549 | // `expectedStyle` to a temporary DOM node to read its `style` attribute
|
|---|
| 9550 | // normalized. Since it only affects IE, we're skipping style warnings
|
|---|
| 9551 | // in that browser completely in favor of doing all that work.
|
|---|
| 9552 | // See https://github.com/facebook/react/issues/11807
|
|---|
| 9553 |
|
|---|
| 9554 |
|
|---|
| 9555 | canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode;
|
|---|
| 9556 |
|
|---|
| 9557 | warnForPropDifference = function (propName, serverValue, clientValue) {
|
|---|
| 9558 | if (didWarnInvalidHydration) {
|
|---|
| 9559 | return;
|
|---|
| 9560 | }
|
|---|
| 9561 |
|
|---|
| 9562 | var normalizedClientValue = normalizeMarkupForTextOrAttribute(clientValue);
|
|---|
| 9563 | var normalizedServerValue = normalizeMarkupForTextOrAttribute(serverValue);
|
|---|
| 9564 |
|
|---|
| 9565 | if (normalizedServerValue === normalizedClientValue) {
|
|---|
| 9566 | return;
|
|---|
| 9567 | }
|
|---|
| 9568 |
|
|---|
| 9569 | didWarnInvalidHydration = true;
|
|---|
| 9570 |
|
|---|
| 9571 | error('Prop `%s` did not match. Server: %s Client: %s', propName, JSON.stringify(normalizedServerValue), JSON.stringify(normalizedClientValue));
|
|---|
| 9572 | };
|
|---|
| 9573 |
|
|---|
| 9574 | warnForExtraAttributes = function (attributeNames) {
|
|---|
| 9575 | if (didWarnInvalidHydration) {
|
|---|
| 9576 | return;
|
|---|
| 9577 | }
|
|---|
| 9578 |
|
|---|
| 9579 | didWarnInvalidHydration = true;
|
|---|
| 9580 | var names = [];
|
|---|
| 9581 | attributeNames.forEach(function (name) {
|
|---|
| 9582 | names.push(name);
|
|---|
| 9583 | });
|
|---|
| 9584 |
|
|---|
| 9585 | error('Extra attributes from the server: %s', names);
|
|---|
| 9586 | };
|
|---|
| 9587 |
|
|---|
| 9588 | warnForInvalidEventListener = function (registrationName, listener) {
|
|---|
| 9589 | if (listener === false) {
|
|---|
| 9590 | error('Expected `%s` listener to be a function, instead got `false`.\n\n' + 'If you used to conditionally omit it with %s={condition && value}, ' + 'pass %s={condition ? value : undefined} instead.', registrationName, registrationName, registrationName);
|
|---|
| 9591 | } else {
|
|---|
| 9592 | error('Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener);
|
|---|
| 9593 | }
|
|---|
| 9594 | }; // Parse the HTML and read it back to normalize the HTML string so that it
|
|---|
| 9595 | // can be used for comparison.
|
|---|
| 9596 |
|
|---|
| 9597 |
|
|---|
| 9598 | normalizeHTML = function (parent, html) {
|
|---|
| 9599 | // We could have created a separate document here to avoid
|
|---|
| 9600 | // re-initializing custom elements if they exist. But this breaks
|
|---|
| 9601 | // how <noscript> is being handled. So we use the same document.
|
|---|
| 9602 | // See the discussion in https://github.com/facebook/react/pull/11157.
|
|---|
| 9603 | var testElement = parent.namespaceURI === HTML_NAMESPACE ? parent.ownerDocument.createElement(parent.tagName) : parent.ownerDocument.createElementNS(parent.namespaceURI, parent.tagName);
|
|---|
| 9604 | testElement.innerHTML = html;
|
|---|
| 9605 | return testElement.innerHTML;
|
|---|
| 9606 | };
|
|---|
| 9607 | } // HTML parsing normalizes CR and CRLF to LF.
|
|---|
| 9608 | // It also can turn \u0000 into \uFFFD inside attributes.
|
|---|
| 9609 | // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream
|
|---|
| 9610 | // If we have a mismatch, it might be caused by that.
|
|---|
| 9611 | // We will still patch up in this case but not fire the warning.
|
|---|
| 9612 |
|
|---|
| 9613 |
|
|---|
| 9614 | var NORMALIZE_NEWLINES_REGEX = /\r\n?/g;
|
|---|
| 9615 | var NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g;
|
|---|
| 9616 |
|
|---|
| 9617 | function normalizeMarkupForTextOrAttribute(markup) {
|
|---|
| 9618 | {
|
|---|
| 9619 | checkHtmlStringCoercion(markup);
|
|---|
| 9620 | }
|
|---|
| 9621 |
|
|---|
| 9622 | var markupString = typeof markup === 'string' ? markup : '' + markup;
|
|---|
| 9623 | return markupString.replace(NORMALIZE_NEWLINES_REGEX, '\n').replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, '');
|
|---|
| 9624 | }
|
|---|
| 9625 |
|
|---|
| 9626 | function checkForUnmatchedText(serverText, clientText, isConcurrentMode, shouldWarnDev) {
|
|---|
| 9627 | var normalizedClientText = normalizeMarkupForTextOrAttribute(clientText);
|
|---|
| 9628 | var normalizedServerText = normalizeMarkupForTextOrAttribute(serverText);
|
|---|
| 9629 |
|
|---|
| 9630 | if (normalizedServerText === normalizedClientText) {
|
|---|
| 9631 | return;
|
|---|
| 9632 | }
|
|---|
| 9633 |
|
|---|
| 9634 | if (shouldWarnDev) {
|
|---|
| 9635 | {
|
|---|
| 9636 | if (!didWarnInvalidHydration) {
|
|---|
| 9637 | didWarnInvalidHydration = true;
|
|---|
| 9638 |
|
|---|
| 9639 | error('Text content did not match. Server: "%s" Client: "%s"', normalizedServerText, normalizedClientText);
|
|---|
| 9640 | }
|
|---|
| 9641 | }
|
|---|
| 9642 | }
|
|---|
| 9643 |
|
|---|
| 9644 | if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) {
|
|---|
| 9645 | // In concurrent roots, we throw when there's a text mismatch and revert to
|
|---|
| 9646 | // client rendering, up to the nearest Suspense boundary.
|
|---|
| 9647 | throw new Error('Text content does not match server-rendered HTML.');
|
|---|
| 9648 | }
|
|---|
| 9649 | }
|
|---|
| 9650 |
|
|---|
| 9651 | function getOwnerDocumentFromRootContainer(rootContainerElement) {
|
|---|
| 9652 | return rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument;
|
|---|
| 9653 | }
|
|---|
| 9654 |
|
|---|
| 9655 | function noop() {}
|
|---|
| 9656 |
|
|---|
| 9657 | function trapClickOnNonInteractiveElement(node) {
|
|---|
| 9658 | // Mobile Safari does not fire properly bubble click events on
|
|---|
| 9659 | // non-interactive elements, which means delegated click listeners do not
|
|---|
| 9660 | // fire. The workaround for this bug involves attaching an empty click
|
|---|
| 9661 | // listener on the target node.
|
|---|
| 9662 | // https://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
|
|---|
| 9663 | // Just set it using the onclick property so that we don't have to manage any
|
|---|
| 9664 | // bookkeeping for it. Not sure if we need to clear it when the listener is
|
|---|
| 9665 | // removed.
|
|---|
| 9666 | // TODO: Only do this for the relevant Safaris maybe?
|
|---|
| 9667 | node.onclick = noop;
|
|---|
| 9668 | }
|
|---|
| 9669 |
|
|---|
| 9670 | function setInitialDOMProperties(tag, domElement, rootContainerElement, nextProps, isCustomComponentTag) {
|
|---|
| 9671 | for (var propKey in nextProps) {
|
|---|
| 9672 | if (!nextProps.hasOwnProperty(propKey)) {
|
|---|
| 9673 | continue;
|
|---|
| 9674 | }
|
|---|
| 9675 |
|
|---|
| 9676 | var nextProp = nextProps[propKey];
|
|---|
| 9677 |
|
|---|
| 9678 | if (propKey === STYLE) {
|
|---|
| 9679 | {
|
|---|
| 9680 | if (nextProp) {
|
|---|
| 9681 | // Freeze the next style object so that we can assume it won't be
|
|---|
| 9682 | // mutated. We have already warned for this in the past.
|
|---|
| 9683 | Object.freeze(nextProp);
|
|---|
| 9684 | }
|
|---|
| 9685 | } // Relies on `updateStylesByID` not mutating `styleUpdates`.
|
|---|
| 9686 |
|
|---|
| 9687 |
|
|---|
| 9688 | setValueForStyles(domElement, nextProp);
|
|---|
| 9689 | } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|---|
| 9690 | var nextHtml = nextProp ? nextProp[HTML$1] : undefined;
|
|---|
| 9691 |
|
|---|
| 9692 | if (nextHtml != null) {
|
|---|
| 9693 | setInnerHTML(domElement, nextHtml);
|
|---|
| 9694 | }
|
|---|
| 9695 | } else if (propKey === CHILDREN) {
|
|---|
| 9696 | if (typeof nextProp === 'string') {
|
|---|
| 9697 | // Avoid setting initial textContent when the text is empty. In IE11 setting
|
|---|
| 9698 | // textContent on a <textarea> will cause the placeholder to not
|
|---|
| 9699 | // show within the <textarea> until it has been focused and blurred again.
|
|---|
| 9700 | // https://github.com/facebook/react/issues/6731#issuecomment-254874553
|
|---|
| 9701 | var canSetTextContent = tag !== 'textarea' || nextProp !== '';
|
|---|
| 9702 |
|
|---|
| 9703 | if (canSetTextContent) {
|
|---|
| 9704 | setTextContent(domElement, nextProp);
|
|---|
| 9705 | }
|
|---|
| 9706 | } else if (typeof nextProp === 'number') {
|
|---|
| 9707 | setTextContent(domElement, '' + nextProp);
|
|---|
| 9708 | }
|
|---|
| 9709 | } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING) ; else if (propKey === AUTOFOCUS) ; else if (registrationNameDependencies.hasOwnProperty(propKey)) {
|
|---|
| 9710 | if (nextProp != null) {
|
|---|
| 9711 | if ( typeof nextProp !== 'function') {
|
|---|
| 9712 | warnForInvalidEventListener(propKey, nextProp);
|
|---|
| 9713 | }
|
|---|
| 9714 |
|
|---|
| 9715 | if (propKey === 'onScroll') {
|
|---|
| 9716 | listenToNonDelegatedEvent('scroll', domElement);
|
|---|
| 9717 | }
|
|---|
| 9718 | }
|
|---|
| 9719 | } else if (nextProp != null) {
|
|---|
| 9720 | setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag);
|
|---|
| 9721 | }
|
|---|
| 9722 | }
|
|---|
| 9723 | }
|
|---|
| 9724 |
|
|---|
| 9725 | function updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag) {
|
|---|
| 9726 | // TODO: Handle wasCustomComponentTag
|
|---|
| 9727 | for (var i = 0; i < updatePayload.length; i += 2) {
|
|---|
| 9728 | var propKey = updatePayload[i];
|
|---|
| 9729 | var propValue = updatePayload[i + 1];
|
|---|
| 9730 |
|
|---|
| 9731 | if (propKey === STYLE) {
|
|---|
| 9732 | setValueForStyles(domElement, propValue);
|
|---|
| 9733 | } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|---|
| 9734 | setInnerHTML(domElement, propValue);
|
|---|
| 9735 | } else if (propKey === CHILDREN) {
|
|---|
| 9736 | setTextContent(domElement, propValue);
|
|---|
| 9737 | } else {
|
|---|
| 9738 | setValueForProperty(domElement, propKey, propValue, isCustomComponentTag);
|
|---|
| 9739 | }
|
|---|
| 9740 | }
|
|---|
| 9741 | }
|
|---|
| 9742 |
|
|---|
| 9743 | function createElement(type, props, rootContainerElement, parentNamespace) {
|
|---|
| 9744 | var isCustomComponentTag; // We create tags in the namespace of their parent container, except HTML
|
|---|
| 9745 | // tags get no namespace.
|
|---|
| 9746 |
|
|---|
| 9747 | var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement);
|
|---|
| 9748 | var domElement;
|
|---|
| 9749 | var namespaceURI = parentNamespace;
|
|---|
| 9750 |
|
|---|
| 9751 | if (namespaceURI === HTML_NAMESPACE) {
|
|---|
| 9752 | namespaceURI = getIntrinsicNamespace(type);
|
|---|
| 9753 | }
|
|---|
| 9754 |
|
|---|
| 9755 | if (namespaceURI === HTML_NAMESPACE) {
|
|---|
| 9756 | {
|
|---|
| 9757 | isCustomComponentTag = isCustomComponent(type, props); // Should this check be gated by parent namespace? Not sure we want to
|
|---|
| 9758 | // allow <SVG> or <mATH>.
|
|---|
| 9759 |
|
|---|
| 9760 | if (!isCustomComponentTag && type !== type.toLowerCase()) {
|
|---|
| 9761 | error('<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', type);
|
|---|
| 9762 | }
|
|---|
| 9763 | }
|
|---|
| 9764 |
|
|---|
| 9765 | if (type === 'script') {
|
|---|
| 9766 | // Create the script via .innerHTML so its "parser-inserted" flag is
|
|---|
| 9767 | // set to true and it does not execute
|
|---|
| 9768 | var div = ownerDocument.createElement('div');
|
|---|
| 9769 |
|
|---|
| 9770 | div.innerHTML = '<script><' + '/script>'; // eslint-disable-line
|
|---|
| 9771 | // This is guaranteed to yield a script element.
|
|---|
| 9772 |
|
|---|
| 9773 | var firstChild = div.firstChild;
|
|---|
| 9774 | domElement = div.removeChild(firstChild);
|
|---|
| 9775 | } else if (typeof props.is === 'string') {
|
|---|
| 9776 | // $FlowIssue `createElement` should be updated for Web Components
|
|---|
| 9777 | domElement = ownerDocument.createElement(type, {
|
|---|
| 9778 | is: props.is
|
|---|
| 9779 | });
|
|---|
| 9780 | } else {
|
|---|
| 9781 | // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug.
|
|---|
| 9782 | // See discussion in https://github.com/facebook/react/pull/6896
|
|---|
| 9783 | // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240
|
|---|
| 9784 | domElement = ownerDocument.createElement(type); // Normally attributes are assigned in `setInitialDOMProperties`, however the `multiple` and `size`
|
|---|
| 9785 | // attributes on `select`s needs to be added before `option`s are inserted.
|
|---|
| 9786 | // This prevents:
|
|---|
| 9787 | // - a bug where the `select` does not scroll to the correct option because singular
|
|---|
| 9788 | // `select` elements automatically pick the first item #13222
|
|---|
| 9789 | // - a bug where the `select` set the first item as selected despite the `size` attribute #14239
|
|---|
| 9790 | // See https://github.com/facebook/react/issues/13222
|
|---|
| 9791 | // and https://github.com/facebook/react/issues/14239
|
|---|
| 9792 |
|
|---|
| 9793 | if (type === 'select') {
|
|---|
| 9794 | var node = domElement;
|
|---|
| 9795 |
|
|---|
| 9796 | if (props.multiple) {
|
|---|
| 9797 | node.multiple = true;
|
|---|
| 9798 | } else if (props.size) {
|
|---|
| 9799 | // Setting a size greater than 1 causes a select to behave like `multiple=true`, where
|
|---|
| 9800 | // it is possible that no option is selected.
|
|---|
| 9801 | //
|
|---|
| 9802 | // This is only necessary when a select in "single selection mode".
|
|---|
| 9803 | node.size = props.size;
|
|---|
| 9804 | }
|
|---|
| 9805 | }
|
|---|
| 9806 | }
|
|---|
| 9807 | } else {
|
|---|
| 9808 | domElement = ownerDocument.createElementNS(namespaceURI, type);
|
|---|
| 9809 | }
|
|---|
| 9810 |
|
|---|
| 9811 | {
|
|---|
| 9812 | if (namespaceURI === HTML_NAMESPACE) {
|
|---|
| 9813 | if (!isCustomComponentTag && Object.prototype.toString.call(domElement) === '[object HTMLUnknownElement]' && !hasOwnProperty.call(warnedUnknownTags, type)) {
|
|---|
| 9814 | warnedUnknownTags[type] = true;
|
|---|
| 9815 |
|
|---|
| 9816 | error('The tag <%s> is unrecognized in this browser. ' + 'If you meant to render a React component, start its name with ' + 'an uppercase letter.', type);
|
|---|
| 9817 | }
|
|---|
| 9818 | }
|
|---|
| 9819 | }
|
|---|
| 9820 |
|
|---|
| 9821 | return domElement;
|
|---|
| 9822 | }
|
|---|
| 9823 | function createTextNode(text, rootContainerElement) {
|
|---|
| 9824 | return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode(text);
|
|---|
| 9825 | }
|
|---|
| 9826 | function setInitialProperties(domElement, tag, rawProps, rootContainerElement) {
|
|---|
| 9827 | var isCustomComponentTag = isCustomComponent(tag, rawProps);
|
|---|
| 9828 |
|
|---|
| 9829 | {
|
|---|
| 9830 | validatePropertiesInDevelopment(tag, rawProps);
|
|---|
| 9831 | } // TODO: Make sure that we check isMounted before firing any of these events.
|
|---|
| 9832 |
|
|---|
| 9833 |
|
|---|
| 9834 | var props;
|
|---|
| 9835 |
|
|---|
| 9836 | switch (tag) {
|
|---|
| 9837 | case 'dialog':
|
|---|
| 9838 | listenToNonDelegatedEvent('cancel', domElement);
|
|---|
| 9839 | listenToNonDelegatedEvent('close', domElement);
|
|---|
| 9840 | props = rawProps;
|
|---|
| 9841 | break;
|
|---|
| 9842 |
|
|---|
| 9843 | case 'iframe':
|
|---|
| 9844 | case 'object':
|
|---|
| 9845 | case 'embed':
|
|---|
| 9846 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 9847 | // listeners still fire for the load event.
|
|---|
| 9848 | listenToNonDelegatedEvent('load', domElement);
|
|---|
| 9849 | props = rawProps;
|
|---|
| 9850 | break;
|
|---|
| 9851 |
|
|---|
| 9852 | case 'video':
|
|---|
| 9853 | case 'audio':
|
|---|
| 9854 | // We listen to these events in case to ensure emulated bubble
|
|---|
| 9855 | // listeners still fire for all the media events.
|
|---|
| 9856 | for (var i = 0; i < mediaEventTypes.length; i++) {
|
|---|
| 9857 | listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
|
|---|
| 9858 | }
|
|---|
| 9859 |
|
|---|
| 9860 | props = rawProps;
|
|---|
| 9861 | break;
|
|---|
| 9862 |
|
|---|
| 9863 | case 'source':
|
|---|
| 9864 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 9865 | // listeners still fire for the error event.
|
|---|
| 9866 | listenToNonDelegatedEvent('error', domElement);
|
|---|
| 9867 | props = rawProps;
|
|---|
| 9868 | break;
|
|---|
| 9869 |
|
|---|
| 9870 | case 'img':
|
|---|
| 9871 | case 'image':
|
|---|
| 9872 | case 'link':
|
|---|
| 9873 | // We listen to these events in case to ensure emulated bubble
|
|---|
| 9874 | // listeners still fire for error and load events.
|
|---|
| 9875 | listenToNonDelegatedEvent('error', domElement);
|
|---|
| 9876 | listenToNonDelegatedEvent('load', domElement);
|
|---|
| 9877 | props = rawProps;
|
|---|
| 9878 | break;
|
|---|
| 9879 |
|
|---|
| 9880 | case 'details':
|
|---|
| 9881 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 9882 | // listeners still fire for the toggle event.
|
|---|
| 9883 | listenToNonDelegatedEvent('toggle', domElement);
|
|---|
| 9884 | props = rawProps;
|
|---|
| 9885 | break;
|
|---|
| 9886 |
|
|---|
| 9887 | case 'input':
|
|---|
| 9888 | initWrapperState(domElement, rawProps);
|
|---|
| 9889 | props = getHostProps(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 9890 | // listeners still fire for the invalid event.
|
|---|
| 9891 |
|
|---|
| 9892 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 9893 | break;
|
|---|
| 9894 |
|
|---|
| 9895 | case 'option':
|
|---|
| 9896 | validateProps(domElement, rawProps);
|
|---|
| 9897 | props = rawProps;
|
|---|
| 9898 | break;
|
|---|
| 9899 |
|
|---|
| 9900 | case 'select':
|
|---|
| 9901 | initWrapperState$1(domElement, rawProps);
|
|---|
| 9902 | props = getHostProps$1(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 9903 | // listeners still fire for the invalid event.
|
|---|
| 9904 |
|
|---|
| 9905 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 9906 | break;
|
|---|
| 9907 |
|
|---|
| 9908 | case 'textarea':
|
|---|
| 9909 | initWrapperState$2(domElement, rawProps);
|
|---|
| 9910 | props = getHostProps$2(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 9911 | // listeners still fire for the invalid event.
|
|---|
| 9912 |
|
|---|
| 9913 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 9914 | break;
|
|---|
| 9915 |
|
|---|
| 9916 | default:
|
|---|
| 9917 | props = rawProps;
|
|---|
| 9918 | }
|
|---|
| 9919 |
|
|---|
| 9920 | assertValidProps(tag, props);
|
|---|
| 9921 | setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag);
|
|---|
| 9922 |
|
|---|
| 9923 | switch (tag) {
|
|---|
| 9924 | case 'input':
|
|---|
| 9925 | // TODO: Make sure we check if this is still unmounted or do any clean
|
|---|
| 9926 | // up necessary since we never stop tracking anymore.
|
|---|
| 9927 | track(domElement);
|
|---|
| 9928 | postMountWrapper(domElement, rawProps, false);
|
|---|
| 9929 | break;
|
|---|
| 9930 |
|
|---|
| 9931 | case 'textarea':
|
|---|
| 9932 | // TODO: Make sure we check if this is still unmounted or do any clean
|
|---|
| 9933 | // up necessary since we never stop tracking anymore.
|
|---|
| 9934 | track(domElement);
|
|---|
| 9935 | postMountWrapper$3(domElement);
|
|---|
| 9936 | break;
|
|---|
| 9937 |
|
|---|
| 9938 | case 'option':
|
|---|
| 9939 | postMountWrapper$1(domElement, rawProps);
|
|---|
| 9940 | break;
|
|---|
| 9941 |
|
|---|
| 9942 | case 'select':
|
|---|
| 9943 | postMountWrapper$2(domElement, rawProps);
|
|---|
| 9944 | break;
|
|---|
| 9945 |
|
|---|
| 9946 | default:
|
|---|
| 9947 | if (typeof props.onClick === 'function') {
|
|---|
| 9948 | // TODO: This cast may not be sound for SVG, MathML or custom elements.
|
|---|
| 9949 | trapClickOnNonInteractiveElement(domElement);
|
|---|
| 9950 | }
|
|---|
| 9951 |
|
|---|
| 9952 | break;
|
|---|
| 9953 | }
|
|---|
| 9954 | } // Calculate the diff between the two objects.
|
|---|
| 9955 |
|
|---|
| 9956 | function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) {
|
|---|
| 9957 | {
|
|---|
| 9958 | validatePropertiesInDevelopment(tag, nextRawProps);
|
|---|
| 9959 | }
|
|---|
| 9960 |
|
|---|
| 9961 | var updatePayload = null;
|
|---|
| 9962 | var lastProps;
|
|---|
| 9963 | var nextProps;
|
|---|
| 9964 |
|
|---|
| 9965 | switch (tag) {
|
|---|
| 9966 | case 'input':
|
|---|
| 9967 | lastProps = getHostProps(domElement, lastRawProps);
|
|---|
| 9968 | nextProps = getHostProps(domElement, nextRawProps);
|
|---|
| 9969 | updatePayload = [];
|
|---|
| 9970 | break;
|
|---|
| 9971 |
|
|---|
| 9972 | case 'select':
|
|---|
| 9973 | lastProps = getHostProps$1(domElement, lastRawProps);
|
|---|
| 9974 | nextProps = getHostProps$1(domElement, nextRawProps);
|
|---|
| 9975 | updatePayload = [];
|
|---|
| 9976 | break;
|
|---|
| 9977 |
|
|---|
| 9978 | case 'textarea':
|
|---|
| 9979 | lastProps = getHostProps$2(domElement, lastRawProps);
|
|---|
| 9980 | nextProps = getHostProps$2(domElement, nextRawProps);
|
|---|
| 9981 | updatePayload = [];
|
|---|
| 9982 | break;
|
|---|
| 9983 |
|
|---|
| 9984 | default:
|
|---|
| 9985 | lastProps = lastRawProps;
|
|---|
| 9986 | nextProps = nextRawProps;
|
|---|
| 9987 |
|
|---|
| 9988 | if (typeof lastProps.onClick !== 'function' && typeof nextProps.onClick === 'function') {
|
|---|
| 9989 | // TODO: This cast may not be sound for SVG, MathML or custom elements.
|
|---|
| 9990 | trapClickOnNonInteractiveElement(domElement);
|
|---|
| 9991 | }
|
|---|
| 9992 |
|
|---|
| 9993 | break;
|
|---|
| 9994 | }
|
|---|
| 9995 |
|
|---|
| 9996 | assertValidProps(tag, nextProps);
|
|---|
| 9997 | var propKey;
|
|---|
| 9998 | var styleName;
|
|---|
| 9999 | var styleUpdates = null;
|
|---|
| 10000 |
|
|---|
| 10001 | for (propKey in lastProps) {
|
|---|
| 10002 | if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) {
|
|---|
| 10003 | continue;
|
|---|
| 10004 | }
|
|---|
| 10005 |
|
|---|
| 10006 | if (propKey === STYLE) {
|
|---|
| 10007 | var lastStyle = lastProps[propKey];
|
|---|
| 10008 |
|
|---|
| 10009 | for (styleName in lastStyle) {
|
|---|
| 10010 | if (lastStyle.hasOwnProperty(styleName)) {
|
|---|
| 10011 | if (!styleUpdates) {
|
|---|
| 10012 | styleUpdates = {};
|
|---|
| 10013 | }
|
|---|
| 10014 |
|
|---|
| 10015 | styleUpdates[styleName] = '';
|
|---|
| 10016 | }
|
|---|
| 10017 | }
|
|---|
| 10018 | } else if (propKey === DANGEROUSLY_SET_INNER_HTML || propKey === CHILDREN) ; else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING) ; else if (propKey === AUTOFOCUS) ; else if (registrationNameDependencies.hasOwnProperty(propKey)) {
|
|---|
| 10019 | // This is a special case. If any listener updates we need to ensure
|
|---|
| 10020 | // that the "current" fiber pointer gets updated so we need a commit
|
|---|
| 10021 | // to update this element.
|
|---|
| 10022 | if (!updatePayload) {
|
|---|
| 10023 | updatePayload = [];
|
|---|
| 10024 | }
|
|---|
| 10025 | } else {
|
|---|
| 10026 | // For all other deleted properties we add it to the queue. We use
|
|---|
| 10027 | // the allowed property list in the commit phase instead.
|
|---|
| 10028 | (updatePayload = updatePayload || []).push(propKey, null);
|
|---|
| 10029 | }
|
|---|
| 10030 | }
|
|---|
| 10031 |
|
|---|
| 10032 | for (propKey in nextProps) {
|
|---|
| 10033 | var nextProp = nextProps[propKey];
|
|---|
| 10034 | var lastProp = lastProps != null ? lastProps[propKey] : undefined;
|
|---|
| 10035 |
|
|---|
| 10036 | if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) {
|
|---|
| 10037 | continue;
|
|---|
| 10038 | }
|
|---|
| 10039 |
|
|---|
| 10040 | if (propKey === STYLE) {
|
|---|
| 10041 | {
|
|---|
| 10042 | if (nextProp) {
|
|---|
| 10043 | // Freeze the next style object so that we can assume it won't be
|
|---|
| 10044 | // mutated. We have already warned for this in the past.
|
|---|
| 10045 | Object.freeze(nextProp);
|
|---|
| 10046 | }
|
|---|
| 10047 | }
|
|---|
| 10048 |
|
|---|
| 10049 | if (lastProp) {
|
|---|
| 10050 | // Unset styles on `lastProp` but not on `nextProp`.
|
|---|
| 10051 | for (styleName in lastProp) {
|
|---|
| 10052 | if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) {
|
|---|
| 10053 | if (!styleUpdates) {
|
|---|
| 10054 | styleUpdates = {};
|
|---|
| 10055 | }
|
|---|
| 10056 |
|
|---|
| 10057 | styleUpdates[styleName] = '';
|
|---|
| 10058 | }
|
|---|
| 10059 | } // Update styles that changed since `lastProp`.
|
|---|
| 10060 |
|
|---|
| 10061 |
|
|---|
| 10062 | for (styleName in nextProp) {
|
|---|
| 10063 | if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) {
|
|---|
| 10064 | if (!styleUpdates) {
|
|---|
| 10065 | styleUpdates = {};
|
|---|
| 10066 | }
|
|---|
| 10067 |
|
|---|
| 10068 | styleUpdates[styleName] = nextProp[styleName];
|
|---|
| 10069 | }
|
|---|
| 10070 | }
|
|---|
| 10071 | } else {
|
|---|
| 10072 | // Relies on `updateStylesByID` not mutating `styleUpdates`.
|
|---|
| 10073 | if (!styleUpdates) {
|
|---|
| 10074 | if (!updatePayload) {
|
|---|
| 10075 | updatePayload = [];
|
|---|
| 10076 | }
|
|---|
| 10077 |
|
|---|
| 10078 | updatePayload.push(propKey, styleUpdates);
|
|---|
| 10079 | }
|
|---|
| 10080 |
|
|---|
| 10081 | styleUpdates = nextProp;
|
|---|
| 10082 | }
|
|---|
| 10083 | } else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|---|
| 10084 | var nextHtml = nextProp ? nextProp[HTML$1] : undefined;
|
|---|
| 10085 | var lastHtml = lastProp ? lastProp[HTML$1] : undefined;
|
|---|
| 10086 |
|
|---|
| 10087 | if (nextHtml != null) {
|
|---|
| 10088 | if (lastHtml !== nextHtml) {
|
|---|
| 10089 | (updatePayload = updatePayload || []).push(propKey, nextHtml);
|
|---|
| 10090 | }
|
|---|
| 10091 | }
|
|---|
| 10092 | } else if (propKey === CHILDREN) {
|
|---|
| 10093 | if (typeof nextProp === 'string' || typeof nextProp === 'number') {
|
|---|
| 10094 | (updatePayload = updatePayload || []).push(propKey, '' + nextProp);
|
|---|
| 10095 | }
|
|---|
| 10096 | } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING) ; else if (registrationNameDependencies.hasOwnProperty(propKey)) {
|
|---|
| 10097 | if (nextProp != null) {
|
|---|
| 10098 | // We eagerly listen to this even though we haven't committed yet.
|
|---|
| 10099 | if ( typeof nextProp !== 'function') {
|
|---|
| 10100 | warnForInvalidEventListener(propKey, nextProp);
|
|---|
| 10101 | }
|
|---|
| 10102 |
|
|---|
| 10103 | if (propKey === 'onScroll') {
|
|---|
| 10104 | listenToNonDelegatedEvent('scroll', domElement);
|
|---|
| 10105 | }
|
|---|
| 10106 | }
|
|---|
| 10107 |
|
|---|
| 10108 | if (!updatePayload && lastProp !== nextProp) {
|
|---|
| 10109 | // This is a special case. If any listener updates we need to ensure
|
|---|
| 10110 | // that the "current" props pointer gets updated so we need a commit
|
|---|
| 10111 | // to update this element.
|
|---|
| 10112 | updatePayload = [];
|
|---|
| 10113 | }
|
|---|
| 10114 | } else {
|
|---|
| 10115 | // For any other property we always add it to the queue and then we
|
|---|
| 10116 | // filter it out using the allowed property list during the commit.
|
|---|
| 10117 | (updatePayload = updatePayload || []).push(propKey, nextProp);
|
|---|
| 10118 | }
|
|---|
| 10119 | }
|
|---|
| 10120 |
|
|---|
| 10121 | if (styleUpdates) {
|
|---|
| 10122 | {
|
|---|
| 10123 | validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE]);
|
|---|
| 10124 | }
|
|---|
| 10125 |
|
|---|
| 10126 | (updatePayload = updatePayload || []).push(STYLE, styleUpdates);
|
|---|
| 10127 | }
|
|---|
| 10128 |
|
|---|
| 10129 | return updatePayload;
|
|---|
| 10130 | } // Apply the diff.
|
|---|
| 10131 |
|
|---|
| 10132 | function updateProperties(domElement, updatePayload, tag, lastRawProps, nextRawProps) {
|
|---|
| 10133 | // Update checked *before* name.
|
|---|
| 10134 | // In the middle of an update, it is possible to have multiple checked.
|
|---|
| 10135 | // When a checked radio tries to change name, browser makes another radio's checked false.
|
|---|
| 10136 | if (tag === 'input' && nextRawProps.type === 'radio' && nextRawProps.name != null) {
|
|---|
| 10137 | updateChecked(domElement, nextRawProps);
|
|---|
| 10138 | }
|
|---|
| 10139 |
|
|---|
| 10140 | var wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
|
|---|
| 10141 | var isCustomComponentTag = isCustomComponent(tag, nextRawProps); // Apply the diff.
|
|---|
| 10142 |
|
|---|
| 10143 | updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag); // TODO: Ensure that an update gets scheduled if any of the special props
|
|---|
| 10144 | // changed.
|
|---|
| 10145 |
|
|---|
| 10146 | switch (tag) {
|
|---|
| 10147 | case 'input':
|
|---|
| 10148 | // Update the wrapper around inputs *after* updating props. This has to
|
|---|
| 10149 | // happen after `updateDOMProperties`. Otherwise HTML5 input validations
|
|---|
| 10150 | // raise warnings and prevent the new value from being assigned.
|
|---|
| 10151 | updateWrapper(domElement, nextRawProps);
|
|---|
| 10152 | break;
|
|---|
| 10153 |
|
|---|
| 10154 | case 'textarea':
|
|---|
| 10155 | updateWrapper$1(domElement, nextRawProps);
|
|---|
| 10156 | break;
|
|---|
| 10157 |
|
|---|
| 10158 | case 'select':
|
|---|
| 10159 | // <select> value update needs to occur after <option> children
|
|---|
| 10160 | // reconciliation
|
|---|
| 10161 | postUpdateWrapper(domElement, nextRawProps);
|
|---|
| 10162 | break;
|
|---|
| 10163 | }
|
|---|
| 10164 | }
|
|---|
| 10165 |
|
|---|
| 10166 | function getPossibleStandardName(propName) {
|
|---|
| 10167 | {
|
|---|
| 10168 | var lowerCasedName = propName.toLowerCase();
|
|---|
| 10169 |
|
|---|
| 10170 | if (!possibleStandardNames.hasOwnProperty(lowerCasedName)) {
|
|---|
| 10171 | return null;
|
|---|
| 10172 | }
|
|---|
| 10173 |
|
|---|
| 10174 | return possibleStandardNames[lowerCasedName] || null;
|
|---|
| 10175 | }
|
|---|
| 10176 | }
|
|---|
| 10177 |
|
|---|
| 10178 | function diffHydratedProperties(domElement, tag, rawProps, parentNamespace, rootContainerElement, isConcurrentMode, shouldWarnDev) {
|
|---|
| 10179 | var isCustomComponentTag;
|
|---|
| 10180 | var extraAttributeNames;
|
|---|
| 10181 |
|
|---|
| 10182 | {
|
|---|
| 10183 | isCustomComponentTag = isCustomComponent(tag, rawProps);
|
|---|
| 10184 | validatePropertiesInDevelopment(tag, rawProps);
|
|---|
| 10185 | } // TODO: Make sure that we check isMounted before firing any of these events.
|
|---|
| 10186 |
|
|---|
| 10187 |
|
|---|
| 10188 | switch (tag) {
|
|---|
| 10189 | case 'dialog':
|
|---|
| 10190 | listenToNonDelegatedEvent('cancel', domElement);
|
|---|
| 10191 | listenToNonDelegatedEvent('close', domElement);
|
|---|
| 10192 | break;
|
|---|
| 10193 |
|
|---|
| 10194 | case 'iframe':
|
|---|
| 10195 | case 'object':
|
|---|
| 10196 | case 'embed':
|
|---|
| 10197 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 10198 | // listeners still fire for the load event.
|
|---|
| 10199 | listenToNonDelegatedEvent('load', domElement);
|
|---|
| 10200 | break;
|
|---|
| 10201 |
|
|---|
| 10202 | case 'video':
|
|---|
| 10203 | case 'audio':
|
|---|
| 10204 | // We listen to these events in case to ensure emulated bubble
|
|---|
| 10205 | // listeners still fire for all the media events.
|
|---|
| 10206 | for (var i = 0; i < mediaEventTypes.length; i++) {
|
|---|
| 10207 | listenToNonDelegatedEvent(mediaEventTypes[i], domElement);
|
|---|
| 10208 | }
|
|---|
| 10209 |
|
|---|
| 10210 | break;
|
|---|
| 10211 |
|
|---|
| 10212 | case 'source':
|
|---|
| 10213 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 10214 | // listeners still fire for the error event.
|
|---|
| 10215 | listenToNonDelegatedEvent('error', domElement);
|
|---|
| 10216 | break;
|
|---|
| 10217 |
|
|---|
| 10218 | case 'img':
|
|---|
| 10219 | case 'image':
|
|---|
| 10220 | case 'link':
|
|---|
| 10221 | // We listen to these events in case to ensure emulated bubble
|
|---|
| 10222 | // listeners still fire for error and load events.
|
|---|
| 10223 | listenToNonDelegatedEvent('error', domElement);
|
|---|
| 10224 | listenToNonDelegatedEvent('load', domElement);
|
|---|
| 10225 | break;
|
|---|
| 10226 |
|
|---|
| 10227 | case 'details':
|
|---|
| 10228 | // We listen to this event in case to ensure emulated bubble
|
|---|
| 10229 | // listeners still fire for the toggle event.
|
|---|
| 10230 | listenToNonDelegatedEvent('toggle', domElement);
|
|---|
| 10231 | break;
|
|---|
| 10232 |
|
|---|
| 10233 | case 'input':
|
|---|
| 10234 | initWrapperState(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 10235 | // listeners still fire for the invalid event.
|
|---|
| 10236 |
|
|---|
| 10237 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 10238 | break;
|
|---|
| 10239 |
|
|---|
| 10240 | case 'option':
|
|---|
| 10241 | validateProps(domElement, rawProps);
|
|---|
| 10242 | break;
|
|---|
| 10243 |
|
|---|
| 10244 | case 'select':
|
|---|
| 10245 | initWrapperState$1(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 10246 | // listeners still fire for the invalid event.
|
|---|
| 10247 |
|
|---|
| 10248 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 10249 | break;
|
|---|
| 10250 |
|
|---|
| 10251 | case 'textarea':
|
|---|
| 10252 | initWrapperState$2(domElement, rawProps); // We listen to this event in case to ensure emulated bubble
|
|---|
| 10253 | // listeners still fire for the invalid event.
|
|---|
| 10254 |
|
|---|
| 10255 | listenToNonDelegatedEvent('invalid', domElement);
|
|---|
| 10256 | break;
|
|---|
| 10257 | }
|
|---|
| 10258 |
|
|---|
| 10259 | assertValidProps(tag, rawProps);
|
|---|
| 10260 |
|
|---|
| 10261 | {
|
|---|
| 10262 | extraAttributeNames = new Set();
|
|---|
| 10263 | var attributes = domElement.attributes;
|
|---|
| 10264 |
|
|---|
| 10265 | for (var _i = 0; _i < attributes.length; _i++) {
|
|---|
| 10266 | var name = attributes[_i].name.toLowerCase();
|
|---|
| 10267 |
|
|---|
| 10268 | switch (name) {
|
|---|
| 10269 | // Controlled attributes are not validated
|
|---|
| 10270 | // TODO: Only ignore them on controlled tags.
|
|---|
| 10271 | case 'value':
|
|---|
| 10272 | break;
|
|---|
| 10273 |
|
|---|
| 10274 | case 'checked':
|
|---|
| 10275 | break;
|
|---|
| 10276 |
|
|---|
| 10277 | case 'selected':
|
|---|
| 10278 | break;
|
|---|
| 10279 |
|
|---|
| 10280 | default:
|
|---|
| 10281 | // Intentionally use the original name.
|
|---|
| 10282 | // See discussion in https://github.com/facebook/react/pull/10676.
|
|---|
| 10283 | extraAttributeNames.add(attributes[_i].name);
|
|---|
| 10284 | }
|
|---|
| 10285 | }
|
|---|
| 10286 | }
|
|---|
| 10287 |
|
|---|
| 10288 | var updatePayload = null;
|
|---|
| 10289 |
|
|---|
| 10290 | for (var propKey in rawProps) {
|
|---|
| 10291 | if (!rawProps.hasOwnProperty(propKey)) {
|
|---|
| 10292 | continue;
|
|---|
| 10293 | }
|
|---|
| 10294 |
|
|---|
| 10295 | var nextProp = rawProps[propKey];
|
|---|
| 10296 |
|
|---|
| 10297 | if (propKey === CHILDREN) {
|
|---|
| 10298 | // For text content children we compare against textContent. This
|
|---|
| 10299 | // might match additional HTML that is hidden when we read it using
|
|---|
| 10300 | // textContent. E.g. "foo" will match "f<span>oo</span>" but that still
|
|---|
| 10301 | // satisfies our requirement. Our requirement is not to produce perfect
|
|---|
| 10302 | // HTML and attributes. Ideally we should preserve structure but it's
|
|---|
| 10303 | // ok not to if the visible content is still enough to indicate what
|
|---|
| 10304 | // even listeners these nodes might be wired up to.
|
|---|
| 10305 | // TODO: Warn if there is more than a single textNode as a child.
|
|---|
| 10306 | // TODO: Should we use domElement.firstChild.nodeValue to compare?
|
|---|
| 10307 | if (typeof nextProp === 'string') {
|
|---|
| 10308 | if (domElement.textContent !== nextProp) {
|
|---|
| 10309 | if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
|
|---|
| 10310 | checkForUnmatchedText(domElement.textContent, nextProp, isConcurrentMode, shouldWarnDev);
|
|---|
| 10311 | }
|
|---|
| 10312 |
|
|---|
| 10313 | updatePayload = [CHILDREN, nextProp];
|
|---|
| 10314 | }
|
|---|
| 10315 | } else if (typeof nextProp === 'number') {
|
|---|
| 10316 | if (domElement.textContent !== '' + nextProp) {
|
|---|
| 10317 | if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
|
|---|
| 10318 | checkForUnmatchedText(domElement.textContent, nextProp, isConcurrentMode, shouldWarnDev);
|
|---|
| 10319 | }
|
|---|
| 10320 |
|
|---|
| 10321 | updatePayload = [CHILDREN, '' + nextProp];
|
|---|
| 10322 | }
|
|---|
| 10323 | }
|
|---|
| 10324 | } else if (registrationNameDependencies.hasOwnProperty(propKey)) {
|
|---|
| 10325 | if (nextProp != null) {
|
|---|
| 10326 | if ( typeof nextProp !== 'function') {
|
|---|
| 10327 | warnForInvalidEventListener(propKey, nextProp);
|
|---|
| 10328 | }
|
|---|
| 10329 |
|
|---|
| 10330 | if (propKey === 'onScroll') {
|
|---|
| 10331 | listenToNonDelegatedEvent('scroll', domElement);
|
|---|
| 10332 | }
|
|---|
| 10333 | }
|
|---|
| 10334 | } else if (shouldWarnDev && true && // Convince Flow we've calculated it (it's DEV-only in this method.)
|
|---|
| 10335 | typeof isCustomComponentTag === 'boolean') {
|
|---|
| 10336 | // Validate that the properties correspond to their expected values.
|
|---|
| 10337 | var serverValue = void 0;
|
|---|
| 10338 | var propertyInfo = isCustomComponentTag && enableCustomElementPropertySupport ? null : getPropertyInfo(propKey);
|
|---|
| 10339 |
|
|---|
| 10340 | if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) ; else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING || // Controlled attributes are not validated
|
|---|
| 10341 | // TODO: Only ignore them on controlled tags.
|
|---|
| 10342 | propKey === 'value' || propKey === 'checked' || propKey === 'selected') ; else if (propKey === DANGEROUSLY_SET_INNER_HTML) {
|
|---|
| 10343 | var serverHTML = domElement.innerHTML;
|
|---|
| 10344 | var nextHtml = nextProp ? nextProp[HTML$1] : undefined;
|
|---|
| 10345 |
|
|---|
| 10346 | if (nextHtml != null) {
|
|---|
| 10347 | var expectedHTML = normalizeHTML(domElement, nextHtml);
|
|---|
| 10348 |
|
|---|
| 10349 | if (expectedHTML !== serverHTML) {
|
|---|
| 10350 | warnForPropDifference(propKey, serverHTML, expectedHTML);
|
|---|
| 10351 | }
|
|---|
| 10352 | }
|
|---|
| 10353 | } else if (propKey === STYLE) {
|
|---|
| 10354 | // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10355 | extraAttributeNames.delete(propKey);
|
|---|
| 10356 |
|
|---|
| 10357 | if (canDiffStyleForHydrationWarning) {
|
|---|
| 10358 | var expectedStyle = createDangerousStringForStyles(nextProp);
|
|---|
| 10359 | serverValue = domElement.getAttribute('style');
|
|---|
| 10360 |
|
|---|
| 10361 | if (expectedStyle !== serverValue) {
|
|---|
| 10362 | warnForPropDifference(propKey, serverValue, expectedStyle);
|
|---|
| 10363 | }
|
|---|
| 10364 | }
|
|---|
| 10365 | } else if (isCustomComponentTag && !enableCustomElementPropertySupport) {
|
|---|
| 10366 | // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10367 | extraAttributeNames.delete(propKey.toLowerCase());
|
|---|
| 10368 | serverValue = getValueForAttribute(domElement, propKey, nextProp);
|
|---|
| 10369 |
|
|---|
| 10370 | if (nextProp !== serverValue) {
|
|---|
| 10371 | warnForPropDifference(propKey, serverValue, nextProp);
|
|---|
| 10372 | }
|
|---|
| 10373 | } else if (!shouldIgnoreAttribute(propKey, propertyInfo, isCustomComponentTag) && !shouldRemoveAttribute(propKey, nextProp, propertyInfo, isCustomComponentTag)) {
|
|---|
| 10374 | var isMismatchDueToBadCasing = false;
|
|---|
| 10375 |
|
|---|
| 10376 | if (propertyInfo !== null) {
|
|---|
| 10377 | // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10378 | extraAttributeNames.delete(propertyInfo.attributeName);
|
|---|
| 10379 | serverValue = getValueForProperty(domElement, propKey, nextProp, propertyInfo);
|
|---|
| 10380 | } else {
|
|---|
| 10381 | var ownNamespace = parentNamespace;
|
|---|
| 10382 |
|
|---|
| 10383 | if (ownNamespace === HTML_NAMESPACE) {
|
|---|
| 10384 | ownNamespace = getIntrinsicNamespace(tag);
|
|---|
| 10385 | }
|
|---|
| 10386 |
|
|---|
| 10387 | if (ownNamespace === HTML_NAMESPACE) {
|
|---|
| 10388 | // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10389 | extraAttributeNames.delete(propKey.toLowerCase());
|
|---|
| 10390 | } else {
|
|---|
| 10391 | var standardName = getPossibleStandardName(propKey);
|
|---|
| 10392 |
|
|---|
| 10393 | if (standardName !== null && standardName !== propKey) {
|
|---|
| 10394 | // If an SVG prop is supplied with bad casing, it will
|
|---|
| 10395 | // be successfully parsed from HTML, but will produce a mismatch
|
|---|
| 10396 | // (and would be incorrectly rendered on the client).
|
|---|
| 10397 | // However, we already warn about bad casing elsewhere.
|
|---|
| 10398 | // So we'll skip the misleading extra mismatch warning in this case.
|
|---|
| 10399 | isMismatchDueToBadCasing = true; // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10400 |
|
|---|
| 10401 | extraAttributeNames.delete(standardName);
|
|---|
| 10402 | } // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10403 |
|
|---|
| 10404 |
|
|---|
| 10405 | extraAttributeNames.delete(propKey);
|
|---|
| 10406 | }
|
|---|
| 10407 |
|
|---|
| 10408 | serverValue = getValueForAttribute(domElement, propKey, nextProp);
|
|---|
| 10409 | }
|
|---|
| 10410 |
|
|---|
| 10411 | var dontWarnCustomElement = enableCustomElementPropertySupport ;
|
|---|
| 10412 |
|
|---|
| 10413 | if (!dontWarnCustomElement && nextProp !== serverValue && !isMismatchDueToBadCasing) {
|
|---|
| 10414 | warnForPropDifference(propKey, serverValue, nextProp);
|
|---|
| 10415 | }
|
|---|
| 10416 | }
|
|---|
| 10417 | }
|
|---|
| 10418 | }
|
|---|
| 10419 |
|
|---|
| 10420 | {
|
|---|
| 10421 | if (shouldWarnDev) {
|
|---|
| 10422 | if ( // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10423 | extraAttributeNames.size > 0 && rawProps[SUPPRESS_HYDRATION_WARNING] !== true) {
|
|---|
| 10424 | // $FlowFixMe - Should be inferred as not undefined.
|
|---|
| 10425 | warnForExtraAttributes(extraAttributeNames);
|
|---|
| 10426 | }
|
|---|
| 10427 | }
|
|---|
| 10428 | }
|
|---|
| 10429 |
|
|---|
| 10430 | switch (tag) {
|
|---|
| 10431 | case 'input':
|
|---|
| 10432 | // TODO: Make sure we check if this is still unmounted or do any clean
|
|---|
| 10433 | // up necessary since we never stop tracking anymore.
|
|---|
| 10434 | track(domElement);
|
|---|
| 10435 | postMountWrapper(domElement, rawProps, true);
|
|---|
| 10436 | break;
|
|---|
| 10437 |
|
|---|
| 10438 | case 'textarea':
|
|---|
| 10439 | // TODO: Make sure we check if this is still unmounted or do any clean
|
|---|
| 10440 | // up necessary since we never stop tracking anymore.
|
|---|
| 10441 | track(domElement);
|
|---|
| 10442 | postMountWrapper$3(domElement);
|
|---|
| 10443 | break;
|
|---|
| 10444 |
|
|---|
| 10445 | case 'select':
|
|---|
| 10446 | case 'option':
|
|---|
| 10447 | // For input and textarea we current always set the value property at
|
|---|
| 10448 | // post mount to force it to diverge from attributes. However, for
|
|---|
| 10449 | // option and select we don't quite do the same thing and select
|
|---|
| 10450 | // is not resilient to the DOM state changing so we don't do that here.
|
|---|
| 10451 | // TODO: Consider not doing this for input and textarea.
|
|---|
| 10452 | break;
|
|---|
| 10453 |
|
|---|
| 10454 | default:
|
|---|
| 10455 | if (typeof rawProps.onClick === 'function') {
|
|---|
| 10456 | // TODO: This cast may not be sound for SVG, MathML or custom elements.
|
|---|
| 10457 | trapClickOnNonInteractiveElement(domElement);
|
|---|
| 10458 | }
|
|---|
| 10459 |
|
|---|
| 10460 | break;
|
|---|
| 10461 | }
|
|---|
| 10462 |
|
|---|
| 10463 | return updatePayload;
|
|---|
| 10464 | }
|
|---|
| 10465 | function diffHydratedText(textNode, text, isConcurrentMode) {
|
|---|
| 10466 | var isDifferent = textNode.nodeValue !== text;
|
|---|
| 10467 | return isDifferent;
|
|---|
| 10468 | }
|
|---|
| 10469 | function warnForDeletedHydratableElement(parentNode, child) {
|
|---|
| 10470 | {
|
|---|
| 10471 | if (didWarnInvalidHydration) {
|
|---|
| 10472 | return;
|
|---|
| 10473 | }
|
|---|
| 10474 |
|
|---|
| 10475 | didWarnInvalidHydration = true;
|
|---|
| 10476 |
|
|---|
| 10477 | error('Did not expect server HTML to contain a <%s> in <%s>.', child.nodeName.toLowerCase(), parentNode.nodeName.toLowerCase());
|
|---|
| 10478 | }
|
|---|
| 10479 | }
|
|---|
| 10480 | function warnForDeletedHydratableText(parentNode, child) {
|
|---|
| 10481 | {
|
|---|
| 10482 | if (didWarnInvalidHydration) {
|
|---|
| 10483 | return;
|
|---|
| 10484 | }
|
|---|
| 10485 |
|
|---|
| 10486 | didWarnInvalidHydration = true;
|
|---|
| 10487 |
|
|---|
| 10488 | error('Did not expect server HTML to contain the text node "%s" in <%s>.', child.nodeValue, parentNode.nodeName.toLowerCase());
|
|---|
| 10489 | }
|
|---|
| 10490 | }
|
|---|
| 10491 | function warnForInsertedHydratedElement(parentNode, tag, props) {
|
|---|
| 10492 | {
|
|---|
| 10493 | if (didWarnInvalidHydration) {
|
|---|
| 10494 | return;
|
|---|
| 10495 | }
|
|---|
| 10496 |
|
|---|
| 10497 | didWarnInvalidHydration = true;
|
|---|
| 10498 |
|
|---|
| 10499 | error('Expected server HTML to contain a matching <%s> in <%s>.', tag, parentNode.nodeName.toLowerCase());
|
|---|
| 10500 | }
|
|---|
| 10501 | }
|
|---|
| 10502 | function warnForInsertedHydratedText(parentNode, text) {
|
|---|
| 10503 | {
|
|---|
| 10504 | if (text === '') {
|
|---|
| 10505 | // We expect to insert empty text nodes since they're not represented in
|
|---|
| 10506 | // the HTML.
|
|---|
| 10507 | // TODO: Remove this special case if we can just avoid inserting empty
|
|---|
| 10508 | // text nodes.
|
|---|
| 10509 | return;
|
|---|
| 10510 | }
|
|---|
| 10511 |
|
|---|
| 10512 | if (didWarnInvalidHydration) {
|
|---|
| 10513 | return;
|
|---|
| 10514 | }
|
|---|
| 10515 |
|
|---|
| 10516 | didWarnInvalidHydration = true;
|
|---|
| 10517 |
|
|---|
| 10518 | error('Expected server HTML to contain a matching text node for "%s" in <%s>.', text, parentNode.nodeName.toLowerCase());
|
|---|
| 10519 | }
|
|---|
| 10520 | }
|
|---|
| 10521 | function restoreControlledState$3(domElement, tag, props) {
|
|---|
| 10522 | switch (tag) {
|
|---|
| 10523 | case 'input':
|
|---|
| 10524 | restoreControlledState(domElement, props);
|
|---|
| 10525 | return;
|
|---|
| 10526 |
|
|---|
| 10527 | case 'textarea':
|
|---|
| 10528 | restoreControlledState$2(domElement, props);
|
|---|
| 10529 | return;
|
|---|
| 10530 |
|
|---|
| 10531 | case 'select':
|
|---|
| 10532 | restoreControlledState$1(domElement, props);
|
|---|
| 10533 | return;
|
|---|
| 10534 | }
|
|---|
| 10535 | }
|
|---|
| 10536 |
|
|---|
| 10537 | var validateDOMNesting = function () {};
|
|---|
| 10538 |
|
|---|
| 10539 | var updatedAncestorInfo = function () {};
|
|---|
| 10540 |
|
|---|
| 10541 | {
|
|---|
| 10542 | // This validation code was written based on the HTML5 parsing spec:
|
|---|
| 10543 | // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
|
|---|
| 10544 | //
|
|---|
| 10545 | // Note: this does not catch all invalid nesting, nor does it try to (as it's
|
|---|
| 10546 | // not clear what practical benefit doing so provides); instead, we warn only
|
|---|
| 10547 | // for cases where the parser will give a parse tree differing from what React
|
|---|
| 10548 | // intended. For example, <b><div></div></b> is invalid but we don't warn
|
|---|
| 10549 | // because it still parses correctly; we do warn for other cases like nested
|
|---|
| 10550 | // <p> tags where the beginning of the second element implicitly closes the
|
|---|
| 10551 | // first, causing a confusing mess.
|
|---|
| 10552 | // https://html.spec.whatwg.org/multipage/syntax.html#special
|
|---|
| 10553 | var specialTags = ['address', 'applet', 'area', 'article', 'aside', 'base', 'basefont', 'bgsound', 'blockquote', 'body', 'br', 'button', 'caption', 'center', 'col', 'colgroup', 'dd', 'details', 'dir', 'div', 'dl', 'dt', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'frame', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'hr', 'html', 'iframe', 'img', 'input', 'isindex', 'li', 'link', 'listing', 'main', 'marquee', 'menu', 'menuitem', 'meta', 'nav', 'noembed', 'noframes', 'noscript', 'object', 'ol', 'p', 'param', 'plaintext', 'pre', 'script', 'section', 'select', 'source', 'style', 'summary', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'title', 'tr', 'track', 'ul', 'wbr', 'xmp']; // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
|
|---|
| 10554 |
|
|---|
| 10555 | var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template', // https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
|
|---|
| 10556 | // TODO: Distinguish by namespace here -- for <title>, including it here
|
|---|
| 10557 | // errs on the side of fewer warnings
|
|---|
| 10558 | 'foreignObject', 'desc', 'title']; // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
|
|---|
| 10559 |
|
|---|
| 10560 | var buttonScopeTags = inScopeTags.concat(['button']); // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
|
|---|
| 10561 |
|
|---|
| 10562 | var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt'];
|
|---|
| 10563 | var emptyAncestorInfo = {
|
|---|
| 10564 | current: null,
|
|---|
| 10565 | formTag: null,
|
|---|
| 10566 | aTagInScope: null,
|
|---|
| 10567 | buttonTagInScope: null,
|
|---|
| 10568 | nobrTagInScope: null,
|
|---|
| 10569 | pTagInButtonScope: null,
|
|---|
| 10570 | listItemTagAutoclosing: null,
|
|---|
| 10571 | dlItemTagAutoclosing: null
|
|---|
| 10572 | };
|
|---|
| 10573 |
|
|---|
| 10574 | updatedAncestorInfo = function (oldInfo, tag) {
|
|---|
| 10575 | var ancestorInfo = assign({}, oldInfo || emptyAncestorInfo);
|
|---|
| 10576 |
|
|---|
| 10577 | var info = {
|
|---|
| 10578 | tag: tag
|
|---|
| 10579 | };
|
|---|
| 10580 |
|
|---|
| 10581 | if (inScopeTags.indexOf(tag) !== -1) {
|
|---|
| 10582 | ancestorInfo.aTagInScope = null;
|
|---|
| 10583 | ancestorInfo.buttonTagInScope = null;
|
|---|
| 10584 | ancestorInfo.nobrTagInScope = null;
|
|---|
| 10585 | }
|
|---|
| 10586 |
|
|---|
| 10587 | if (buttonScopeTags.indexOf(tag) !== -1) {
|
|---|
| 10588 | ancestorInfo.pTagInButtonScope = null;
|
|---|
| 10589 | } // See rules for 'li', 'dd', 'dt' start tags in
|
|---|
| 10590 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
|
|---|
| 10591 |
|
|---|
| 10592 |
|
|---|
| 10593 | if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') {
|
|---|
| 10594 | ancestorInfo.listItemTagAutoclosing = null;
|
|---|
| 10595 | ancestorInfo.dlItemTagAutoclosing = null;
|
|---|
| 10596 | }
|
|---|
| 10597 |
|
|---|
| 10598 | ancestorInfo.current = info;
|
|---|
| 10599 |
|
|---|
| 10600 | if (tag === 'form') {
|
|---|
| 10601 | ancestorInfo.formTag = info;
|
|---|
| 10602 | }
|
|---|
| 10603 |
|
|---|
| 10604 | if (tag === 'a') {
|
|---|
| 10605 | ancestorInfo.aTagInScope = info;
|
|---|
| 10606 | }
|
|---|
| 10607 |
|
|---|
| 10608 | if (tag === 'button') {
|
|---|
| 10609 | ancestorInfo.buttonTagInScope = info;
|
|---|
| 10610 | }
|
|---|
| 10611 |
|
|---|
| 10612 | if (tag === 'nobr') {
|
|---|
| 10613 | ancestorInfo.nobrTagInScope = info;
|
|---|
| 10614 | }
|
|---|
| 10615 |
|
|---|
| 10616 | if (tag === 'p') {
|
|---|
| 10617 | ancestorInfo.pTagInButtonScope = info;
|
|---|
| 10618 | }
|
|---|
| 10619 |
|
|---|
| 10620 | if (tag === 'li') {
|
|---|
| 10621 | ancestorInfo.listItemTagAutoclosing = info;
|
|---|
| 10622 | }
|
|---|
| 10623 |
|
|---|
| 10624 | if (tag === 'dd' || tag === 'dt') {
|
|---|
| 10625 | ancestorInfo.dlItemTagAutoclosing = info;
|
|---|
| 10626 | }
|
|---|
| 10627 |
|
|---|
| 10628 | return ancestorInfo;
|
|---|
| 10629 | };
|
|---|
| 10630 | /**
|
|---|
| 10631 | * Returns whether
|
|---|
| 10632 | */
|
|---|
| 10633 |
|
|---|
| 10634 |
|
|---|
| 10635 | var isTagValidWithParent = function (tag, parentTag) {
|
|---|
| 10636 | // First, let's check if we're in an unusual parsing mode...
|
|---|
| 10637 | switch (parentTag) {
|
|---|
| 10638 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
|
|---|
| 10639 | case 'select':
|
|---|
| 10640 | return tag === 'option' || tag === 'optgroup' || tag === '#text';
|
|---|
| 10641 |
|
|---|
| 10642 | case 'optgroup':
|
|---|
| 10643 | return tag === 'option' || tag === '#text';
|
|---|
| 10644 | // Strictly speaking, seeing an <option> doesn't mean we're in a <select>
|
|---|
| 10645 | // but
|
|---|
| 10646 |
|
|---|
| 10647 | case 'option':
|
|---|
| 10648 | return tag === '#text';
|
|---|
| 10649 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
|
|---|
| 10650 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
|
|---|
| 10651 | // No special behavior since these rules fall back to "in body" mode for
|
|---|
| 10652 | // all except special table nodes which cause bad parsing behavior anyway.
|
|---|
| 10653 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
|
|---|
| 10654 |
|
|---|
| 10655 | case 'tr':
|
|---|
| 10656 | return tag === 'th' || tag === 'td' || tag === 'style' || tag === 'script' || tag === 'template';
|
|---|
| 10657 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
|
|---|
| 10658 |
|
|---|
| 10659 | case 'tbody':
|
|---|
| 10660 | case 'thead':
|
|---|
| 10661 | case 'tfoot':
|
|---|
| 10662 | return tag === 'tr' || tag === 'style' || tag === 'script' || tag === 'template';
|
|---|
| 10663 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
|
|---|
| 10664 |
|
|---|
| 10665 | case 'colgroup':
|
|---|
| 10666 | return tag === 'col' || tag === 'template';
|
|---|
| 10667 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
|
|---|
| 10668 |
|
|---|
| 10669 | case 'table':
|
|---|
| 10670 | return tag === 'caption' || tag === 'colgroup' || tag === 'tbody' || tag === 'tfoot' || tag === 'thead' || tag === 'style' || tag === 'script' || tag === 'template';
|
|---|
| 10671 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
|
|---|
| 10672 |
|
|---|
| 10673 | case 'head':
|
|---|
| 10674 | return tag === 'base' || tag === 'basefont' || tag === 'bgsound' || tag === 'link' || tag === 'meta' || tag === 'title' || tag === 'noscript' || tag === 'noframes' || tag === 'style' || tag === 'script' || tag === 'template';
|
|---|
| 10675 | // https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
|
|---|
| 10676 |
|
|---|
| 10677 | case 'html':
|
|---|
| 10678 | return tag === 'head' || tag === 'body' || tag === 'frameset';
|
|---|
| 10679 |
|
|---|
| 10680 | case 'frameset':
|
|---|
| 10681 | return tag === 'frame';
|
|---|
| 10682 |
|
|---|
| 10683 | case '#document':
|
|---|
| 10684 | return tag === 'html';
|
|---|
| 10685 | } // Probably in the "in body" parsing mode, so we outlaw only tag combos
|
|---|
| 10686 | // where the parsing rules cause implicit opens or closes to be added.
|
|---|
| 10687 | // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
|
|---|
| 10688 |
|
|---|
| 10689 |
|
|---|
| 10690 | switch (tag) {
|
|---|
| 10691 | case 'h1':
|
|---|
| 10692 | case 'h2':
|
|---|
| 10693 | case 'h3':
|
|---|
| 10694 | case 'h4':
|
|---|
| 10695 | case 'h5':
|
|---|
| 10696 | case 'h6':
|
|---|
| 10697 | return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6';
|
|---|
| 10698 |
|
|---|
| 10699 | case 'rp':
|
|---|
| 10700 | case 'rt':
|
|---|
| 10701 | return impliedEndTags.indexOf(parentTag) === -1;
|
|---|
| 10702 |
|
|---|
| 10703 | case 'body':
|
|---|
| 10704 | case 'caption':
|
|---|
| 10705 | case 'col':
|
|---|
| 10706 | case 'colgroup':
|
|---|
| 10707 | case 'frameset':
|
|---|
| 10708 | case 'frame':
|
|---|
| 10709 | case 'head':
|
|---|
| 10710 | case 'html':
|
|---|
| 10711 | case 'tbody':
|
|---|
| 10712 | case 'td':
|
|---|
| 10713 | case 'tfoot':
|
|---|
| 10714 | case 'th':
|
|---|
| 10715 | case 'thead':
|
|---|
| 10716 | case 'tr':
|
|---|
| 10717 | // These tags are only valid with a few parents that have special child
|
|---|
| 10718 | // parsing rules -- if we're down here, then none of those matched and
|
|---|
| 10719 | // so we allow it only if we don't know what the parent is, as all other
|
|---|
| 10720 | // cases are invalid.
|
|---|
| 10721 | return parentTag == null;
|
|---|
| 10722 | }
|
|---|
| 10723 |
|
|---|
| 10724 | return true;
|
|---|
| 10725 | };
|
|---|
| 10726 | /**
|
|---|
| 10727 | * Returns whether
|
|---|
| 10728 | */
|
|---|
| 10729 |
|
|---|
| 10730 |
|
|---|
| 10731 | var findInvalidAncestorForTag = function (tag, ancestorInfo) {
|
|---|
| 10732 | switch (tag) {
|
|---|
| 10733 | case 'address':
|
|---|
| 10734 | case 'article':
|
|---|
| 10735 | case 'aside':
|
|---|
| 10736 | case 'blockquote':
|
|---|
| 10737 | case 'center':
|
|---|
| 10738 | case 'details':
|
|---|
| 10739 | case 'dialog':
|
|---|
| 10740 | case 'dir':
|
|---|
| 10741 | case 'div':
|
|---|
| 10742 | case 'dl':
|
|---|
| 10743 | case 'fieldset':
|
|---|
| 10744 | case 'figcaption':
|
|---|
| 10745 | case 'figure':
|
|---|
| 10746 | case 'footer':
|
|---|
| 10747 | case 'header':
|
|---|
| 10748 | case 'hgroup':
|
|---|
| 10749 | case 'main':
|
|---|
| 10750 | case 'menu':
|
|---|
| 10751 | case 'nav':
|
|---|
| 10752 | case 'ol':
|
|---|
| 10753 | case 'p':
|
|---|
| 10754 | case 'section':
|
|---|
| 10755 | case 'summary':
|
|---|
| 10756 | case 'ul':
|
|---|
| 10757 | case 'pre':
|
|---|
| 10758 | case 'listing':
|
|---|
| 10759 | case 'table':
|
|---|
| 10760 | case 'hr':
|
|---|
| 10761 | case 'xmp':
|
|---|
| 10762 | case 'h1':
|
|---|
| 10763 | case 'h2':
|
|---|
| 10764 | case 'h3':
|
|---|
| 10765 | case 'h4':
|
|---|
| 10766 | case 'h5':
|
|---|
| 10767 | case 'h6':
|
|---|
| 10768 | return ancestorInfo.pTagInButtonScope;
|
|---|
| 10769 |
|
|---|
| 10770 | case 'form':
|
|---|
| 10771 | return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
|
|---|
| 10772 |
|
|---|
| 10773 | case 'li':
|
|---|
| 10774 | return ancestorInfo.listItemTagAutoclosing;
|
|---|
| 10775 |
|
|---|
| 10776 | case 'dd':
|
|---|
| 10777 | case 'dt':
|
|---|
| 10778 | return ancestorInfo.dlItemTagAutoclosing;
|
|---|
| 10779 |
|
|---|
| 10780 | case 'button':
|
|---|
| 10781 | return ancestorInfo.buttonTagInScope;
|
|---|
| 10782 |
|
|---|
| 10783 | case 'a':
|
|---|
| 10784 | // Spec says something about storing a list of markers, but it sounds
|
|---|
| 10785 | // equivalent to this check.
|
|---|
| 10786 | return ancestorInfo.aTagInScope;
|
|---|
| 10787 |
|
|---|
| 10788 | case 'nobr':
|
|---|
| 10789 | return ancestorInfo.nobrTagInScope;
|
|---|
| 10790 | }
|
|---|
| 10791 |
|
|---|
| 10792 | return null;
|
|---|
| 10793 | };
|
|---|
| 10794 |
|
|---|
| 10795 | var didWarn$1 = {};
|
|---|
| 10796 |
|
|---|
| 10797 | validateDOMNesting = function (childTag, childText, ancestorInfo) {
|
|---|
| 10798 | ancestorInfo = ancestorInfo || emptyAncestorInfo;
|
|---|
| 10799 | var parentInfo = ancestorInfo.current;
|
|---|
| 10800 | var parentTag = parentInfo && parentInfo.tag;
|
|---|
| 10801 |
|
|---|
| 10802 | if (childText != null) {
|
|---|
| 10803 | if (childTag != null) {
|
|---|
| 10804 | error('validateDOMNesting: when childText is passed, childTag should be null');
|
|---|
| 10805 | }
|
|---|
| 10806 |
|
|---|
| 10807 | childTag = '#text';
|
|---|
| 10808 | }
|
|---|
| 10809 |
|
|---|
| 10810 | var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo;
|
|---|
| 10811 | var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo);
|
|---|
| 10812 | var invalidParentOrAncestor = invalidParent || invalidAncestor;
|
|---|
| 10813 |
|
|---|
| 10814 | if (!invalidParentOrAncestor) {
|
|---|
| 10815 | return;
|
|---|
| 10816 | }
|
|---|
| 10817 |
|
|---|
| 10818 | var ancestorTag = invalidParentOrAncestor.tag;
|
|---|
| 10819 | var warnKey = !!invalidParent + '|' + childTag + '|' + ancestorTag;
|
|---|
| 10820 |
|
|---|
| 10821 | if (didWarn$1[warnKey]) {
|
|---|
| 10822 | return;
|
|---|
| 10823 | }
|
|---|
| 10824 |
|
|---|
| 10825 | didWarn$1[warnKey] = true;
|
|---|
| 10826 | var tagDisplayName = childTag;
|
|---|
| 10827 | var whitespaceInfo = '';
|
|---|
| 10828 |
|
|---|
| 10829 | if (childTag === '#text') {
|
|---|
| 10830 | if (/\S/.test(childText)) {
|
|---|
| 10831 | tagDisplayName = 'Text nodes';
|
|---|
| 10832 | } else {
|
|---|
| 10833 | tagDisplayName = 'Whitespace text nodes';
|
|---|
| 10834 | whitespaceInfo = " Make sure you don't have any extra whitespace between tags on " + 'each line of your source code.';
|
|---|
| 10835 | }
|
|---|
| 10836 | } else {
|
|---|
| 10837 | tagDisplayName = '<' + childTag + '>';
|
|---|
| 10838 | }
|
|---|
| 10839 |
|
|---|
| 10840 | if (invalidParent) {
|
|---|
| 10841 | var info = '';
|
|---|
| 10842 |
|
|---|
| 10843 | if (ancestorTag === 'table' && childTag === 'tr') {
|
|---|
| 10844 | info += ' Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by ' + 'the browser.';
|
|---|
| 10845 | }
|
|---|
| 10846 |
|
|---|
| 10847 | error('validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s', tagDisplayName, ancestorTag, whitespaceInfo, info);
|
|---|
| 10848 | } else {
|
|---|
| 10849 | error('validateDOMNesting(...): %s cannot appear as a descendant of ' + '<%s>.', tagDisplayName, ancestorTag);
|
|---|
| 10850 | }
|
|---|
| 10851 | };
|
|---|
| 10852 | }
|
|---|
| 10853 |
|
|---|
| 10854 | var SUPPRESS_HYDRATION_WARNING$1 = 'suppressHydrationWarning';
|
|---|
| 10855 | var SUSPENSE_START_DATA = '$';
|
|---|
| 10856 | var SUSPENSE_END_DATA = '/$';
|
|---|
| 10857 | var SUSPENSE_PENDING_START_DATA = '$?';
|
|---|
| 10858 | var SUSPENSE_FALLBACK_START_DATA = '$!';
|
|---|
| 10859 | var STYLE$1 = 'style';
|
|---|
| 10860 | var eventsEnabled = null;
|
|---|
| 10861 | var selectionInformation = null;
|
|---|
| 10862 | function getRootHostContext(rootContainerInstance) {
|
|---|
| 10863 | var type;
|
|---|
| 10864 | var namespace;
|
|---|
| 10865 | var nodeType = rootContainerInstance.nodeType;
|
|---|
| 10866 |
|
|---|
| 10867 | switch (nodeType) {
|
|---|
| 10868 | case DOCUMENT_NODE:
|
|---|
| 10869 | case DOCUMENT_FRAGMENT_NODE:
|
|---|
| 10870 | {
|
|---|
| 10871 | type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment';
|
|---|
| 10872 | var root = rootContainerInstance.documentElement;
|
|---|
| 10873 | namespace = root ? root.namespaceURI : getChildNamespace(null, '');
|
|---|
| 10874 | break;
|
|---|
| 10875 | }
|
|---|
| 10876 |
|
|---|
| 10877 | default:
|
|---|
| 10878 | {
|
|---|
| 10879 | var container = nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance;
|
|---|
| 10880 | var ownNamespace = container.namespaceURI || null;
|
|---|
| 10881 | type = container.tagName;
|
|---|
| 10882 | namespace = getChildNamespace(ownNamespace, type);
|
|---|
| 10883 | break;
|
|---|
| 10884 | }
|
|---|
| 10885 | }
|
|---|
| 10886 |
|
|---|
| 10887 | {
|
|---|
| 10888 | var validatedTag = type.toLowerCase();
|
|---|
| 10889 | var ancestorInfo = updatedAncestorInfo(null, validatedTag);
|
|---|
| 10890 | return {
|
|---|
| 10891 | namespace: namespace,
|
|---|
| 10892 | ancestorInfo: ancestorInfo
|
|---|
| 10893 | };
|
|---|
| 10894 | }
|
|---|
| 10895 | }
|
|---|
| 10896 | function getChildHostContext(parentHostContext, type, rootContainerInstance) {
|
|---|
| 10897 | {
|
|---|
| 10898 | var parentHostContextDev = parentHostContext;
|
|---|
| 10899 | var namespace = getChildNamespace(parentHostContextDev.namespace, type);
|
|---|
| 10900 | var ancestorInfo = updatedAncestorInfo(parentHostContextDev.ancestorInfo, type);
|
|---|
| 10901 | return {
|
|---|
| 10902 | namespace: namespace,
|
|---|
| 10903 | ancestorInfo: ancestorInfo
|
|---|
| 10904 | };
|
|---|
| 10905 | }
|
|---|
| 10906 | }
|
|---|
| 10907 | function getPublicInstance(instance) {
|
|---|
| 10908 | return instance;
|
|---|
| 10909 | }
|
|---|
| 10910 | function prepareForCommit(containerInfo) {
|
|---|
| 10911 | eventsEnabled = isEnabled();
|
|---|
| 10912 | selectionInformation = getSelectionInformation();
|
|---|
| 10913 | var activeInstance = null;
|
|---|
| 10914 |
|
|---|
| 10915 | setEnabled(false);
|
|---|
| 10916 | return activeInstance;
|
|---|
| 10917 | }
|
|---|
| 10918 | function resetAfterCommit(containerInfo) {
|
|---|
| 10919 | restoreSelection(selectionInformation);
|
|---|
| 10920 | setEnabled(eventsEnabled);
|
|---|
| 10921 | eventsEnabled = null;
|
|---|
| 10922 | selectionInformation = null;
|
|---|
| 10923 | }
|
|---|
| 10924 | function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) {
|
|---|
| 10925 | var parentNamespace;
|
|---|
| 10926 |
|
|---|
| 10927 | {
|
|---|
| 10928 | // TODO: take namespace into account when validating.
|
|---|
| 10929 | var hostContextDev = hostContext;
|
|---|
| 10930 | validateDOMNesting(type, null, hostContextDev.ancestorInfo);
|
|---|
| 10931 |
|
|---|
| 10932 | if (typeof props.children === 'string' || typeof props.children === 'number') {
|
|---|
| 10933 | var string = '' + props.children;
|
|---|
| 10934 | var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type);
|
|---|
| 10935 | validateDOMNesting(null, string, ownAncestorInfo);
|
|---|
| 10936 | }
|
|---|
| 10937 |
|
|---|
| 10938 | parentNamespace = hostContextDev.namespace;
|
|---|
| 10939 | }
|
|---|
| 10940 |
|
|---|
| 10941 | var domElement = createElement(type, props, rootContainerInstance, parentNamespace);
|
|---|
| 10942 | precacheFiberNode(internalInstanceHandle, domElement);
|
|---|
| 10943 | updateFiberProps(domElement, props);
|
|---|
| 10944 | return domElement;
|
|---|
| 10945 | }
|
|---|
| 10946 | function appendInitialChild(parentInstance, child) {
|
|---|
| 10947 | parentInstance.appendChild(child);
|
|---|
| 10948 | }
|
|---|
| 10949 | function finalizeInitialChildren(domElement, type, props, rootContainerInstance, hostContext) {
|
|---|
| 10950 | setInitialProperties(domElement, type, props, rootContainerInstance);
|
|---|
| 10951 |
|
|---|
| 10952 | switch (type) {
|
|---|
| 10953 | case 'button':
|
|---|
| 10954 | case 'input':
|
|---|
| 10955 | case 'select':
|
|---|
| 10956 | case 'textarea':
|
|---|
| 10957 | return !!props.autoFocus;
|
|---|
| 10958 |
|
|---|
| 10959 | case 'img':
|
|---|
| 10960 | return true;
|
|---|
| 10961 |
|
|---|
| 10962 | default:
|
|---|
| 10963 | return false;
|
|---|
| 10964 | }
|
|---|
| 10965 | }
|
|---|
| 10966 | function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) {
|
|---|
| 10967 | {
|
|---|
| 10968 | var hostContextDev = hostContext;
|
|---|
| 10969 |
|
|---|
| 10970 | if (typeof newProps.children !== typeof oldProps.children && (typeof newProps.children === 'string' || typeof newProps.children === 'number')) {
|
|---|
| 10971 | var string = '' + newProps.children;
|
|---|
| 10972 | var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type);
|
|---|
| 10973 | validateDOMNesting(null, string, ownAncestorInfo);
|
|---|
| 10974 | }
|
|---|
| 10975 | }
|
|---|
| 10976 |
|
|---|
| 10977 | return diffProperties(domElement, type, oldProps, newProps);
|
|---|
| 10978 | }
|
|---|
| 10979 | function shouldSetTextContent(type, props) {
|
|---|
| 10980 | return type === 'textarea' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && props.dangerouslySetInnerHTML.__html != null;
|
|---|
| 10981 | }
|
|---|
| 10982 | function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) {
|
|---|
| 10983 | {
|
|---|
| 10984 | var hostContextDev = hostContext;
|
|---|
| 10985 | validateDOMNesting(null, text, hostContextDev.ancestorInfo);
|
|---|
| 10986 | }
|
|---|
| 10987 |
|
|---|
| 10988 | var textNode = createTextNode(text, rootContainerInstance);
|
|---|
| 10989 | precacheFiberNode(internalInstanceHandle, textNode);
|
|---|
| 10990 | return textNode;
|
|---|
| 10991 | }
|
|---|
| 10992 | function getCurrentEventPriority() {
|
|---|
| 10993 | var currentEvent = window.event;
|
|---|
| 10994 |
|
|---|
| 10995 | if (currentEvent === undefined) {
|
|---|
| 10996 | return DefaultEventPriority;
|
|---|
| 10997 | }
|
|---|
| 10998 |
|
|---|
| 10999 | return getEventPriority(currentEvent.type);
|
|---|
| 11000 | }
|
|---|
| 11001 | // if a component just imports ReactDOM (e.g. for findDOMNode).
|
|---|
| 11002 | // Some environments might not have setTimeout or clearTimeout.
|
|---|
| 11003 |
|
|---|
| 11004 | var scheduleTimeout = typeof setTimeout === 'function' ? setTimeout : undefined;
|
|---|
| 11005 | var cancelTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined;
|
|---|
| 11006 | var noTimeout = -1;
|
|---|
| 11007 | var localPromise = typeof Promise === 'function' ? Promise : undefined; // -------------------
|
|---|
| 11008 | var scheduleMicrotask = typeof queueMicrotask === 'function' ? queueMicrotask : typeof localPromise !== 'undefined' ? function (callback) {
|
|---|
| 11009 | return localPromise.resolve(null).then(callback).catch(handleErrorInNextTick);
|
|---|
| 11010 | } : scheduleTimeout; // TODO: Determine the best fallback here.
|
|---|
| 11011 |
|
|---|
| 11012 | function handleErrorInNextTick(error) {
|
|---|
| 11013 | setTimeout(function () {
|
|---|
| 11014 | throw error;
|
|---|
| 11015 | });
|
|---|
| 11016 | } // -------------------
|
|---|
| 11017 | function commitMount(domElement, type, newProps, internalInstanceHandle) {
|
|---|
| 11018 | // Despite the naming that might imply otherwise, this method only
|
|---|
| 11019 | // fires if there is an `Update` effect scheduled during mounting.
|
|---|
| 11020 | // This happens if `finalizeInitialChildren` returns `true` (which it
|
|---|
| 11021 | // does to implement the `autoFocus` attribute on the client). But
|
|---|
| 11022 | // there are also other cases when this might happen (such as patching
|
|---|
| 11023 | // up text content during hydration mismatch). So we'll check this again.
|
|---|
| 11024 | switch (type) {
|
|---|
| 11025 | case 'button':
|
|---|
| 11026 | case 'input':
|
|---|
| 11027 | case 'select':
|
|---|
| 11028 | case 'textarea':
|
|---|
| 11029 | if (newProps.autoFocus) {
|
|---|
| 11030 | domElement.focus();
|
|---|
| 11031 | }
|
|---|
| 11032 |
|
|---|
| 11033 | return;
|
|---|
| 11034 |
|
|---|
| 11035 | case 'img':
|
|---|
| 11036 | {
|
|---|
| 11037 | if (newProps.src) {
|
|---|
| 11038 | domElement.src = newProps.src;
|
|---|
| 11039 | }
|
|---|
| 11040 |
|
|---|
| 11041 | return;
|
|---|
| 11042 | }
|
|---|
| 11043 | }
|
|---|
| 11044 | }
|
|---|
| 11045 | function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) {
|
|---|
| 11046 | // Apply the diff to the DOM node.
|
|---|
| 11047 | updateProperties(domElement, updatePayload, type, oldProps, newProps); // Update the props handle so that we know which props are the ones with
|
|---|
| 11048 | // with current event handlers.
|
|---|
| 11049 |
|
|---|
| 11050 | updateFiberProps(domElement, newProps);
|
|---|
| 11051 | }
|
|---|
| 11052 | function resetTextContent(domElement) {
|
|---|
| 11053 | setTextContent(domElement, '');
|
|---|
| 11054 | }
|
|---|
| 11055 | function commitTextUpdate(textInstance, oldText, newText) {
|
|---|
| 11056 | textInstance.nodeValue = newText;
|
|---|
| 11057 | }
|
|---|
| 11058 | function appendChild(parentInstance, child) {
|
|---|
| 11059 | parentInstance.appendChild(child);
|
|---|
| 11060 | }
|
|---|
| 11061 | function appendChildToContainer(container, child) {
|
|---|
| 11062 | var parentNode;
|
|---|
| 11063 |
|
|---|
| 11064 | if (container.nodeType === COMMENT_NODE) {
|
|---|
| 11065 | parentNode = container.parentNode;
|
|---|
| 11066 | parentNode.insertBefore(child, container);
|
|---|
| 11067 | } else {
|
|---|
| 11068 | parentNode = container;
|
|---|
| 11069 | parentNode.appendChild(child);
|
|---|
| 11070 | } // This container might be used for a portal.
|
|---|
| 11071 | // If something inside a portal is clicked, that click should bubble
|
|---|
| 11072 | // through the React tree. However, on Mobile Safari the click would
|
|---|
| 11073 | // never bubble through the *DOM* tree unless an ancestor with onclick
|
|---|
| 11074 | // event exists. So we wouldn't see it and dispatch it.
|
|---|
| 11075 | // This is why we ensure that non React root containers have inline onclick
|
|---|
| 11076 | // defined.
|
|---|
| 11077 | // https://github.com/facebook/react/issues/11918
|
|---|
| 11078 |
|
|---|
| 11079 |
|
|---|
| 11080 | var reactRootContainer = container._reactRootContainer;
|
|---|
| 11081 |
|
|---|
| 11082 | if ((reactRootContainer === null || reactRootContainer === undefined) && parentNode.onclick === null) {
|
|---|
| 11083 | // TODO: This cast may not be sound for SVG, MathML or custom elements.
|
|---|
| 11084 | trapClickOnNonInteractiveElement(parentNode);
|
|---|
| 11085 | }
|
|---|
| 11086 | }
|
|---|
| 11087 | function insertBefore(parentInstance, child, beforeChild) {
|
|---|
| 11088 | parentInstance.insertBefore(child, beforeChild);
|
|---|
| 11089 | }
|
|---|
| 11090 | function insertInContainerBefore(container, child, beforeChild) {
|
|---|
| 11091 | if (container.nodeType === COMMENT_NODE) {
|
|---|
| 11092 | container.parentNode.insertBefore(child, beforeChild);
|
|---|
| 11093 | } else {
|
|---|
| 11094 | container.insertBefore(child, beforeChild);
|
|---|
| 11095 | }
|
|---|
| 11096 | }
|
|---|
| 11097 |
|
|---|
| 11098 | function removeChild(parentInstance, child) {
|
|---|
| 11099 | parentInstance.removeChild(child);
|
|---|
| 11100 | }
|
|---|
| 11101 | function removeChildFromContainer(container, child) {
|
|---|
| 11102 | if (container.nodeType === COMMENT_NODE) {
|
|---|
| 11103 | container.parentNode.removeChild(child);
|
|---|
| 11104 | } else {
|
|---|
| 11105 | container.removeChild(child);
|
|---|
| 11106 | }
|
|---|
| 11107 | }
|
|---|
| 11108 | function clearSuspenseBoundary(parentInstance, suspenseInstance) {
|
|---|
| 11109 | var node = suspenseInstance; // Delete all nodes within this suspense boundary.
|
|---|
| 11110 | // There might be nested nodes so we need to keep track of how
|
|---|
| 11111 | // deep we are and only break out when we're back on top.
|
|---|
| 11112 |
|
|---|
| 11113 | var depth = 0;
|
|---|
| 11114 |
|
|---|
| 11115 | do {
|
|---|
| 11116 | var nextNode = node.nextSibling;
|
|---|
| 11117 | parentInstance.removeChild(node);
|
|---|
| 11118 |
|
|---|
| 11119 | if (nextNode && nextNode.nodeType === COMMENT_NODE) {
|
|---|
| 11120 | var data = nextNode.data;
|
|---|
| 11121 |
|
|---|
| 11122 | if (data === SUSPENSE_END_DATA) {
|
|---|
| 11123 | if (depth === 0) {
|
|---|
| 11124 | parentInstance.removeChild(nextNode); // Retry if any event replaying was blocked on this.
|
|---|
| 11125 |
|
|---|
| 11126 | retryIfBlockedOn(suspenseInstance);
|
|---|
| 11127 | return;
|
|---|
| 11128 | } else {
|
|---|
| 11129 | depth--;
|
|---|
| 11130 | }
|
|---|
| 11131 | } else if (data === SUSPENSE_START_DATA || data === SUSPENSE_PENDING_START_DATA || data === SUSPENSE_FALLBACK_START_DATA) {
|
|---|
| 11132 | depth++;
|
|---|
| 11133 | }
|
|---|
| 11134 | }
|
|---|
| 11135 |
|
|---|
| 11136 | node = nextNode;
|
|---|
| 11137 | } while (node); // TODO: Warn, we didn't find the end comment boundary.
|
|---|
| 11138 | // Retry if any event replaying was blocked on this.
|
|---|
| 11139 |
|
|---|
| 11140 |
|
|---|
| 11141 | retryIfBlockedOn(suspenseInstance);
|
|---|
| 11142 | }
|
|---|
| 11143 | function clearSuspenseBoundaryFromContainer(container, suspenseInstance) {
|
|---|
| 11144 | if (container.nodeType === COMMENT_NODE) {
|
|---|
| 11145 | clearSuspenseBoundary(container.parentNode, suspenseInstance);
|
|---|
| 11146 | } else if (container.nodeType === ELEMENT_NODE) {
|
|---|
| 11147 | clearSuspenseBoundary(container, suspenseInstance);
|
|---|
| 11148 | } // Retry if any event replaying was blocked on this.
|
|---|
| 11149 |
|
|---|
| 11150 |
|
|---|
| 11151 | retryIfBlockedOn(container);
|
|---|
| 11152 | }
|
|---|
| 11153 | function hideInstance(instance) {
|
|---|
| 11154 | // TODO: Does this work for all element types? What about MathML? Should we
|
|---|
| 11155 | // pass host context to this method?
|
|---|
| 11156 | instance = instance;
|
|---|
| 11157 | var style = instance.style;
|
|---|
| 11158 |
|
|---|
| 11159 | if (typeof style.setProperty === 'function') {
|
|---|
| 11160 | style.setProperty('display', 'none', 'important');
|
|---|
| 11161 | } else {
|
|---|
| 11162 | style.display = 'none';
|
|---|
| 11163 | }
|
|---|
| 11164 | }
|
|---|
| 11165 | function hideTextInstance(textInstance) {
|
|---|
| 11166 | textInstance.nodeValue = '';
|
|---|
| 11167 | }
|
|---|
| 11168 | function unhideInstance(instance, props) {
|
|---|
| 11169 | instance = instance;
|
|---|
| 11170 | var styleProp = props[STYLE$1];
|
|---|
| 11171 | var display = styleProp !== undefined && styleProp !== null && styleProp.hasOwnProperty('display') ? styleProp.display : null;
|
|---|
| 11172 | instance.style.display = dangerousStyleValue('display', display);
|
|---|
| 11173 | }
|
|---|
| 11174 | function unhideTextInstance(textInstance, text) {
|
|---|
| 11175 | textInstance.nodeValue = text;
|
|---|
| 11176 | }
|
|---|
| 11177 | function clearContainer(container) {
|
|---|
| 11178 | if (container.nodeType === ELEMENT_NODE) {
|
|---|
| 11179 | container.textContent = '';
|
|---|
| 11180 | } else if (container.nodeType === DOCUMENT_NODE) {
|
|---|
| 11181 | if (container.documentElement) {
|
|---|
| 11182 | container.removeChild(container.documentElement);
|
|---|
| 11183 | }
|
|---|
| 11184 | }
|
|---|
| 11185 | } // -------------------
|
|---|
| 11186 | function canHydrateInstance(instance, type, props) {
|
|---|
| 11187 | if (instance.nodeType !== ELEMENT_NODE || type.toLowerCase() !== instance.nodeName.toLowerCase()) {
|
|---|
| 11188 | return null;
|
|---|
| 11189 | } // This has now been refined to an element node.
|
|---|
| 11190 |
|
|---|
| 11191 |
|
|---|
| 11192 | return instance;
|
|---|
| 11193 | }
|
|---|
| 11194 | function canHydrateTextInstance(instance, text) {
|
|---|
| 11195 | if (text === '' || instance.nodeType !== TEXT_NODE) {
|
|---|
| 11196 | // Empty strings are not parsed by HTML so there won't be a correct match here.
|
|---|
| 11197 | return null;
|
|---|
| 11198 | } // This has now been refined to a text node.
|
|---|
| 11199 |
|
|---|
| 11200 |
|
|---|
| 11201 | return instance;
|
|---|
| 11202 | }
|
|---|
| 11203 | function canHydrateSuspenseInstance(instance) {
|
|---|
| 11204 | if (instance.nodeType !== COMMENT_NODE) {
|
|---|
| 11205 | // Empty strings are not parsed by HTML so there won't be a correct match here.
|
|---|
| 11206 | return null;
|
|---|
| 11207 | } // This has now been refined to a suspense node.
|
|---|
| 11208 |
|
|---|
| 11209 |
|
|---|
| 11210 | return instance;
|
|---|
| 11211 | }
|
|---|
| 11212 | function isSuspenseInstancePending(instance) {
|
|---|
| 11213 | return instance.data === SUSPENSE_PENDING_START_DATA;
|
|---|
| 11214 | }
|
|---|
| 11215 | function isSuspenseInstanceFallback(instance) {
|
|---|
| 11216 | return instance.data === SUSPENSE_FALLBACK_START_DATA;
|
|---|
| 11217 | }
|
|---|
| 11218 | function getSuspenseInstanceFallbackErrorDetails(instance) {
|
|---|
| 11219 | var dataset = instance.nextSibling && instance.nextSibling.dataset;
|
|---|
| 11220 | var digest, message, stack;
|
|---|
| 11221 |
|
|---|
| 11222 | if (dataset) {
|
|---|
| 11223 | digest = dataset.dgst;
|
|---|
| 11224 |
|
|---|
| 11225 | {
|
|---|
| 11226 | message = dataset.msg;
|
|---|
| 11227 | stack = dataset.stck;
|
|---|
| 11228 | }
|
|---|
| 11229 | }
|
|---|
| 11230 |
|
|---|
| 11231 | {
|
|---|
| 11232 | return {
|
|---|
| 11233 | message: message,
|
|---|
| 11234 | digest: digest,
|
|---|
| 11235 | stack: stack
|
|---|
| 11236 | };
|
|---|
| 11237 | } // let value = {message: undefined, hash: undefined};
|
|---|
| 11238 | // const nextSibling = instance.nextSibling;
|
|---|
| 11239 | // if (nextSibling) {
|
|---|
| 11240 | // const dataset = ((nextSibling: any): HTMLTemplateElement).dataset;
|
|---|
| 11241 | // value.message = dataset.msg;
|
|---|
| 11242 | // value.hash = dataset.hash;
|
|---|
| 11243 | // if (true) {
|
|---|
| 11244 | // value.stack = dataset.stack;
|
|---|
| 11245 | // }
|
|---|
| 11246 | // }
|
|---|
| 11247 | // return value;
|
|---|
| 11248 |
|
|---|
| 11249 | }
|
|---|
| 11250 | function registerSuspenseInstanceRetry(instance, callback) {
|
|---|
| 11251 | instance._reactRetry = callback;
|
|---|
| 11252 | }
|
|---|
| 11253 |
|
|---|
| 11254 | function getNextHydratable(node) {
|
|---|
| 11255 | // Skip non-hydratable nodes.
|
|---|
| 11256 | for (; node != null; node = node.nextSibling) {
|
|---|
| 11257 | var nodeType = node.nodeType;
|
|---|
| 11258 |
|
|---|
| 11259 | if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) {
|
|---|
| 11260 | break;
|
|---|
| 11261 | }
|
|---|
| 11262 |
|
|---|
| 11263 | if (nodeType === COMMENT_NODE) {
|
|---|
| 11264 | var nodeData = node.data;
|
|---|
| 11265 |
|
|---|
| 11266 | if (nodeData === SUSPENSE_START_DATA || nodeData === SUSPENSE_FALLBACK_START_DATA || nodeData === SUSPENSE_PENDING_START_DATA) {
|
|---|
| 11267 | break;
|
|---|
| 11268 | }
|
|---|
| 11269 |
|
|---|
| 11270 | if (nodeData === SUSPENSE_END_DATA) {
|
|---|
| 11271 | return null;
|
|---|
| 11272 | }
|
|---|
| 11273 | }
|
|---|
| 11274 | }
|
|---|
| 11275 |
|
|---|
| 11276 | return node;
|
|---|
| 11277 | }
|
|---|
| 11278 |
|
|---|
| 11279 | function getNextHydratableSibling(instance) {
|
|---|
| 11280 | return getNextHydratable(instance.nextSibling);
|
|---|
| 11281 | }
|
|---|
| 11282 | function getFirstHydratableChild(parentInstance) {
|
|---|
| 11283 | return getNextHydratable(parentInstance.firstChild);
|
|---|
| 11284 | }
|
|---|
| 11285 | function getFirstHydratableChildWithinContainer(parentContainer) {
|
|---|
| 11286 | return getNextHydratable(parentContainer.firstChild);
|
|---|
| 11287 | }
|
|---|
| 11288 | function getFirstHydratableChildWithinSuspenseInstance(parentInstance) {
|
|---|
| 11289 | return getNextHydratable(parentInstance.nextSibling);
|
|---|
| 11290 | }
|
|---|
| 11291 | function hydrateInstance(instance, type, props, rootContainerInstance, hostContext, internalInstanceHandle, shouldWarnDev) {
|
|---|
| 11292 | precacheFiberNode(internalInstanceHandle, instance); // TODO: Possibly defer this until the commit phase where all the events
|
|---|
| 11293 | // get attached.
|
|---|
| 11294 |
|
|---|
| 11295 | updateFiberProps(instance, props);
|
|---|
| 11296 | var parentNamespace;
|
|---|
| 11297 |
|
|---|
| 11298 | {
|
|---|
| 11299 | var hostContextDev = hostContext;
|
|---|
| 11300 | parentNamespace = hostContextDev.namespace;
|
|---|
| 11301 | } // TODO: Temporary hack to check if we're in a concurrent root. We can delete
|
|---|
| 11302 | // when the legacy root API is removed.
|
|---|
| 11303 |
|
|---|
| 11304 |
|
|---|
| 11305 | var isConcurrentMode = (internalInstanceHandle.mode & ConcurrentMode) !== NoMode;
|
|---|
| 11306 | return diffHydratedProperties(instance, type, props, parentNamespace, rootContainerInstance, isConcurrentMode, shouldWarnDev);
|
|---|
| 11307 | }
|
|---|
| 11308 | function hydrateTextInstance(textInstance, text, internalInstanceHandle, shouldWarnDev) {
|
|---|
| 11309 | precacheFiberNode(internalInstanceHandle, textInstance); // TODO: Temporary hack to check if we're in a concurrent root. We can delete
|
|---|
| 11310 | // when the legacy root API is removed.
|
|---|
| 11311 |
|
|---|
| 11312 | var isConcurrentMode = (internalInstanceHandle.mode & ConcurrentMode) !== NoMode;
|
|---|
| 11313 | return diffHydratedText(textInstance, text);
|
|---|
| 11314 | }
|
|---|
| 11315 | function hydrateSuspenseInstance(suspenseInstance, internalInstanceHandle) {
|
|---|
| 11316 | precacheFiberNode(internalInstanceHandle, suspenseInstance);
|
|---|
| 11317 | }
|
|---|
| 11318 | function getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance) {
|
|---|
| 11319 | var node = suspenseInstance.nextSibling; // Skip past all nodes within this suspense boundary.
|
|---|
| 11320 | // There might be nested nodes so we need to keep track of how
|
|---|
| 11321 | // deep we are and only break out when we're back on top.
|
|---|
| 11322 |
|
|---|
| 11323 | var depth = 0;
|
|---|
| 11324 |
|
|---|
| 11325 | while (node) {
|
|---|
| 11326 | if (node.nodeType === COMMENT_NODE) {
|
|---|
| 11327 | var data = node.data;
|
|---|
| 11328 |
|
|---|
| 11329 | if (data === SUSPENSE_END_DATA) {
|
|---|
| 11330 | if (depth === 0) {
|
|---|
| 11331 | return getNextHydratableSibling(node);
|
|---|
| 11332 | } else {
|
|---|
| 11333 | depth--;
|
|---|
| 11334 | }
|
|---|
| 11335 | } else if (data === SUSPENSE_START_DATA || data === SUSPENSE_FALLBACK_START_DATA || data === SUSPENSE_PENDING_START_DATA) {
|
|---|
| 11336 | depth++;
|
|---|
| 11337 | }
|
|---|
| 11338 | }
|
|---|
| 11339 |
|
|---|
| 11340 | node = node.nextSibling;
|
|---|
| 11341 | } // TODO: Warn, we didn't find the end comment boundary.
|
|---|
| 11342 |
|
|---|
| 11343 |
|
|---|
| 11344 | return null;
|
|---|
| 11345 | } // Returns the SuspenseInstance if this node is a direct child of a
|
|---|
| 11346 | // SuspenseInstance. I.e. if its previous sibling is a Comment with
|
|---|
| 11347 | // SUSPENSE_x_START_DATA. Otherwise, null.
|
|---|
| 11348 |
|
|---|
| 11349 | function getParentSuspenseInstance(targetInstance) {
|
|---|
| 11350 | var node = targetInstance.previousSibling; // Skip past all nodes within this suspense boundary.
|
|---|
| 11351 | // There might be nested nodes so we need to keep track of how
|
|---|
| 11352 | // deep we are and only break out when we're back on top.
|
|---|
| 11353 |
|
|---|
| 11354 | var depth = 0;
|
|---|
| 11355 |
|
|---|
| 11356 | while (node) {
|
|---|
| 11357 | if (node.nodeType === COMMENT_NODE) {
|
|---|
| 11358 | var data = node.data;
|
|---|
| 11359 |
|
|---|
| 11360 | if (data === SUSPENSE_START_DATA || data === SUSPENSE_FALLBACK_START_DATA || data === SUSPENSE_PENDING_START_DATA) {
|
|---|
| 11361 | if (depth === 0) {
|
|---|
| 11362 | return node;
|
|---|
| 11363 | } else {
|
|---|
| 11364 | depth--;
|
|---|
| 11365 | }
|
|---|
| 11366 | } else if (data === SUSPENSE_END_DATA) {
|
|---|
| 11367 | depth++;
|
|---|
| 11368 | }
|
|---|
| 11369 | }
|
|---|
| 11370 |
|
|---|
| 11371 | node = node.previousSibling;
|
|---|
| 11372 | }
|
|---|
| 11373 |
|
|---|
| 11374 | return null;
|
|---|
| 11375 | }
|
|---|
| 11376 | function commitHydratedContainer(container) {
|
|---|
| 11377 | // Retry if any event replaying was blocked on this.
|
|---|
| 11378 | retryIfBlockedOn(container);
|
|---|
| 11379 | }
|
|---|
| 11380 | function commitHydratedSuspenseInstance(suspenseInstance) {
|
|---|
| 11381 | // Retry if any event replaying was blocked on this.
|
|---|
| 11382 | retryIfBlockedOn(suspenseInstance);
|
|---|
| 11383 | }
|
|---|
| 11384 | function shouldDeleteUnhydratedTailInstances(parentType) {
|
|---|
| 11385 | return parentType !== 'head' && parentType !== 'body';
|
|---|
| 11386 | }
|
|---|
| 11387 | function didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, text, isConcurrentMode) {
|
|---|
| 11388 | var shouldWarnDev = true;
|
|---|
| 11389 | checkForUnmatchedText(textInstance.nodeValue, text, isConcurrentMode, shouldWarnDev);
|
|---|
| 11390 | }
|
|---|
| 11391 | function didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, text, isConcurrentMode) {
|
|---|
| 11392 | if (parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) {
|
|---|
| 11393 | var shouldWarnDev = true;
|
|---|
| 11394 | checkForUnmatchedText(textInstance.nodeValue, text, isConcurrentMode, shouldWarnDev);
|
|---|
| 11395 | }
|
|---|
| 11396 | }
|
|---|
| 11397 | function didNotHydrateInstanceWithinContainer(parentContainer, instance) {
|
|---|
| 11398 | {
|
|---|
| 11399 | if (instance.nodeType === ELEMENT_NODE) {
|
|---|
| 11400 | warnForDeletedHydratableElement(parentContainer, instance);
|
|---|
| 11401 | } else if (instance.nodeType === COMMENT_NODE) ; else {
|
|---|
| 11402 | warnForDeletedHydratableText(parentContainer, instance);
|
|---|
| 11403 | }
|
|---|
| 11404 | }
|
|---|
| 11405 | }
|
|---|
| 11406 | function didNotHydrateInstanceWithinSuspenseInstance(parentInstance, instance) {
|
|---|
| 11407 | {
|
|---|
| 11408 | // $FlowFixMe: Only Element or Document can be parent nodes.
|
|---|
| 11409 | var parentNode = parentInstance.parentNode;
|
|---|
| 11410 |
|
|---|
| 11411 | if (parentNode !== null) {
|
|---|
| 11412 | if (instance.nodeType === ELEMENT_NODE) {
|
|---|
| 11413 | warnForDeletedHydratableElement(parentNode, instance);
|
|---|
| 11414 | } else if (instance.nodeType === COMMENT_NODE) ; else {
|
|---|
| 11415 | warnForDeletedHydratableText(parentNode, instance);
|
|---|
| 11416 | }
|
|---|
| 11417 | }
|
|---|
| 11418 | }
|
|---|
| 11419 | }
|
|---|
| 11420 | function didNotHydrateInstance(parentType, parentProps, parentInstance, instance, isConcurrentMode) {
|
|---|
| 11421 | {
|
|---|
| 11422 | if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) {
|
|---|
| 11423 | if (instance.nodeType === ELEMENT_NODE) {
|
|---|
| 11424 | warnForDeletedHydratableElement(parentInstance, instance);
|
|---|
| 11425 | } else if (instance.nodeType === COMMENT_NODE) ; else {
|
|---|
| 11426 | warnForDeletedHydratableText(parentInstance, instance);
|
|---|
| 11427 | }
|
|---|
| 11428 | }
|
|---|
| 11429 | }
|
|---|
| 11430 | }
|
|---|
| 11431 | function didNotFindHydratableInstanceWithinContainer(parentContainer, type, props) {
|
|---|
| 11432 | {
|
|---|
| 11433 | warnForInsertedHydratedElement(parentContainer, type);
|
|---|
| 11434 | }
|
|---|
| 11435 | }
|
|---|
| 11436 | function didNotFindHydratableTextInstanceWithinContainer(parentContainer, text) {
|
|---|
| 11437 | {
|
|---|
| 11438 | warnForInsertedHydratedText(parentContainer, text);
|
|---|
| 11439 | }
|
|---|
| 11440 | }
|
|---|
| 11441 | function didNotFindHydratableInstanceWithinSuspenseInstance(parentInstance, type, props) {
|
|---|
| 11442 | {
|
|---|
| 11443 | // $FlowFixMe: Only Element or Document can be parent nodes.
|
|---|
| 11444 | var parentNode = parentInstance.parentNode;
|
|---|
| 11445 | if (parentNode !== null) warnForInsertedHydratedElement(parentNode, type);
|
|---|
| 11446 | }
|
|---|
| 11447 | }
|
|---|
| 11448 | function didNotFindHydratableTextInstanceWithinSuspenseInstance(parentInstance, text) {
|
|---|
| 11449 | {
|
|---|
| 11450 | // $FlowFixMe: Only Element or Document can be parent nodes.
|
|---|
| 11451 | var parentNode = parentInstance.parentNode;
|
|---|
| 11452 | if (parentNode !== null) warnForInsertedHydratedText(parentNode, text);
|
|---|
| 11453 | }
|
|---|
| 11454 | }
|
|---|
| 11455 | function didNotFindHydratableInstance(parentType, parentProps, parentInstance, type, props, isConcurrentMode) {
|
|---|
| 11456 | {
|
|---|
| 11457 | if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) {
|
|---|
| 11458 | warnForInsertedHydratedElement(parentInstance, type);
|
|---|
| 11459 | }
|
|---|
| 11460 | }
|
|---|
| 11461 | }
|
|---|
| 11462 | function didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, text, isConcurrentMode) {
|
|---|
| 11463 | {
|
|---|
| 11464 | if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) {
|
|---|
| 11465 | warnForInsertedHydratedText(parentInstance, text);
|
|---|
| 11466 | }
|
|---|
| 11467 | }
|
|---|
| 11468 | }
|
|---|
| 11469 | function errorHydratingContainer(parentContainer) {
|
|---|
| 11470 | {
|
|---|
| 11471 | // TODO: This gets logged by onRecoverableError, too, so we should be
|
|---|
| 11472 | // able to remove it.
|
|---|
| 11473 | error('An error occurred during hydration. The server HTML was replaced with client content in <%s>.', parentContainer.nodeName.toLowerCase());
|
|---|
| 11474 | }
|
|---|
| 11475 | }
|
|---|
| 11476 | function preparePortalMount(portalInstance) {
|
|---|
| 11477 | listenToAllSupportedEvents(portalInstance);
|
|---|
| 11478 | }
|
|---|
| 11479 |
|
|---|
| 11480 | var randomKey = Math.random().toString(36).slice(2);
|
|---|
| 11481 | var internalInstanceKey = '__reactFiber$' + randomKey;
|
|---|
| 11482 | var internalPropsKey = '__reactProps$' + randomKey;
|
|---|
| 11483 | var internalContainerInstanceKey = '__reactContainer$' + randomKey;
|
|---|
| 11484 | var internalEventHandlersKey = '__reactEvents$' + randomKey;
|
|---|
| 11485 | var internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
|
|---|
| 11486 | var internalEventHandlesSetKey = '__reactHandles$' + randomKey;
|
|---|
| 11487 | function detachDeletedInstance(node) {
|
|---|
| 11488 | // TODO: This function is only called on host components. I don't think all of
|
|---|
| 11489 | // these fields are relevant.
|
|---|
| 11490 | delete node[internalInstanceKey];
|
|---|
| 11491 | delete node[internalPropsKey];
|
|---|
| 11492 | delete node[internalEventHandlersKey];
|
|---|
| 11493 | delete node[internalEventHandlerListenersKey];
|
|---|
| 11494 | delete node[internalEventHandlesSetKey];
|
|---|
| 11495 | }
|
|---|
| 11496 | function precacheFiberNode(hostInst, node) {
|
|---|
| 11497 | node[internalInstanceKey] = hostInst;
|
|---|
| 11498 | }
|
|---|
| 11499 | function markContainerAsRoot(hostRoot, node) {
|
|---|
| 11500 | node[internalContainerInstanceKey] = hostRoot;
|
|---|
| 11501 | }
|
|---|
| 11502 | function unmarkContainerAsRoot(node) {
|
|---|
| 11503 | node[internalContainerInstanceKey] = null;
|
|---|
| 11504 | }
|
|---|
| 11505 | function isContainerMarkedAsRoot(node) {
|
|---|
| 11506 | return !!node[internalContainerInstanceKey];
|
|---|
| 11507 | } // Given a DOM node, return the closest HostComponent or HostText fiber ancestor.
|
|---|
| 11508 | // If the target node is part of a hydrated or not yet rendered subtree, then
|
|---|
| 11509 | // this may also return a SuspenseComponent or HostRoot to indicate that.
|
|---|
| 11510 | // Conceptually the HostRoot fiber is a child of the Container node. So if you
|
|---|
| 11511 | // pass the Container node as the targetNode, you will not actually get the
|
|---|
| 11512 | // HostRoot back. To get to the HostRoot, you need to pass a child of it.
|
|---|
| 11513 | // The same thing applies to Suspense boundaries.
|
|---|
| 11514 |
|
|---|
| 11515 | function getClosestInstanceFromNode(targetNode) {
|
|---|
| 11516 | var targetInst = targetNode[internalInstanceKey];
|
|---|
| 11517 |
|
|---|
| 11518 | if (targetInst) {
|
|---|
| 11519 | // Don't return HostRoot or SuspenseComponent here.
|
|---|
| 11520 | return targetInst;
|
|---|
| 11521 | } // If the direct event target isn't a React owned DOM node, we need to look
|
|---|
| 11522 | // to see if one of its parents is a React owned DOM node.
|
|---|
| 11523 |
|
|---|
| 11524 |
|
|---|
| 11525 | var parentNode = targetNode.parentNode;
|
|---|
| 11526 |
|
|---|
| 11527 | while (parentNode) {
|
|---|
| 11528 | // We'll check if this is a container root that could include
|
|---|
| 11529 | // React nodes in the future. We need to check this first because
|
|---|
| 11530 | // if we're a child of a dehydrated container, we need to first
|
|---|
| 11531 | // find that inner container before moving on to finding the parent
|
|---|
| 11532 | // instance. Note that we don't check this field on the targetNode
|
|---|
| 11533 | // itself because the fibers are conceptually between the container
|
|---|
| 11534 | // node and the first child. It isn't surrounding the container node.
|
|---|
| 11535 | // If it's not a container, we check if it's an instance.
|
|---|
| 11536 | targetInst = parentNode[internalContainerInstanceKey] || parentNode[internalInstanceKey];
|
|---|
| 11537 |
|
|---|
| 11538 | if (targetInst) {
|
|---|
| 11539 | // Since this wasn't the direct target of the event, we might have
|
|---|
| 11540 | // stepped past dehydrated DOM nodes to get here. However they could
|
|---|
| 11541 | // also have been non-React nodes. We need to answer which one.
|
|---|
| 11542 | // If we the instance doesn't have any children, then there can't be
|
|---|
| 11543 | // a nested suspense boundary within it. So we can use this as a fast
|
|---|
| 11544 | // bailout. Most of the time, when people add non-React children to
|
|---|
| 11545 | // the tree, it is using a ref to a child-less DOM node.
|
|---|
| 11546 | // Normally we'd only need to check one of the fibers because if it
|
|---|
| 11547 | // has ever gone from having children to deleting them or vice versa
|
|---|
| 11548 | // it would have deleted the dehydrated boundary nested inside already.
|
|---|
| 11549 | // However, since the HostRoot starts out with an alternate it might
|
|---|
| 11550 | // have one on the alternate so we need to check in case this was a
|
|---|
| 11551 | // root.
|
|---|
| 11552 | var alternate = targetInst.alternate;
|
|---|
| 11553 |
|
|---|
| 11554 | if (targetInst.child !== null || alternate !== null && alternate.child !== null) {
|
|---|
| 11555 | // Next we need to figure out if the node that skipped past is
|
|---|
| 11556 | // nested within a dehydrated boundary and if so, which one.
|
|---|
| 11557 | var suspenseInstance = getParentSuspenseInstance(targetNode);
|
|---|
| 11558 |
|
|---|
| 11559 | while (suspenseInstance !== null) {
|
|---|
| 11560 | // We found a suspense instance. That means that we haven't
|
|---|
| 11561 | // hydrated it yet. Even though we leave the comments in the
|
|---|
| 11562 | // DOM after hydrating, and there are boundaries in the DOM
|
|---|
| 11563 | // that could already be hydrated, we wouldn't have found them
|
|---|
| 11564 | // through this pass since if the target is hydrated it would
|
|---|
| 11565 | // have had an internalInstanceKey on it.
|
|---|
| 11566 | // Let's get the fiber associated with the SuspenseComponent
|
|---|
| 11567 | // as the deepest instance.
|
|---|
| 11568 | var targetSuspenseInst = suspenseInstance[internalInstanceKey];
|
|---|
| 11569 |
|
|---|
| 11570 | if (targetSuspenseInst) {
|
|---|
| 11571 | return targetSuspenseInst;
|
|---|
| 11572 | } // If we don't find a Fiber on the comment, it might be because
|
|---|
| 11573 | // we haven't gotten to hydrate it yet. There might still be a
|
|---|
| 11574 | // parent boundary that hasn't above this one so we need to find
|
|---|
| 11575 | // the outer most that is known.
|
|---|
| 11576 |
|
|---|
| 11577 |
|
|---|
| 11578 | suspenseInstance = getParentSuspenseInstance(suspenseInstance); // If we don't find one, then that should mean that the parent
|
|---|
| 11579 | // host component also hasn't hydrated yet. We can return it
|
|---|
| 11580 | // below since it will bail out on the isMounted check later.
|
|---|
| 11581 | }
|
|---|
| 11582 | }
|
|---|
| 11583 |
|
|---|
| 11584 | return targetInst;
|
|---|
| 11585 | }
|
|---|
| 11586 |
|
|---|
| 11587 | targetNode = parentNode;
|
|---|
| 11588 | parentNode = targetNode.parentNode;
|
|---|
| 11589 | }
|
|---|
| 11590 |
|
|---|
| 11591 | return null;
|
|---|
| 11592 | }
|
|---|
| 11593 | /**
|
|---|
| 11594 | * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent
|
|---|
| 11595 | * instance, or null if the node was not rendered by this React.
|
|---|
| 11596 | */
|
|---|
| 11597 |
|
|---|
| 11598 | function getInstanceFromNode(node) {
|
|---|
| 11599 | var inst = node[internalInstanceKey] || node[internalContainerInstanceKey];
|
|---|
| 11600 |
|
|---|
| 11601 | if (inst) {
|
|---|
| 11602 | if (inst.tag === HostComponent || inst.tag === HostText || inst.tag === SuspenseComponent || inst.tag === HostRoot) {
|
|---|
| 11603 | return inst;
|
|---|
| 11604 | } else {
|
|---|
| 11605 | return null;
|
|---|
| 11606 | }
|
|---|
| 11607 | }
|
|---|
| 11608 |
|
|---|
| 11609 | return null;
|
|---|
| 11610 | }
|
|---|
| 11611 | /**
|
|---|
| 11612 | * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding
|
|---|
| 11613 | * DOM node.
|
|---|
| 11614 | */
|
|---|
| 11615 |
|
|---|
| 11616 | function getNodeFromInstance(inst) {
|
|---|
| 11617 | if (inst.tag === HostComponent || inst.tag === HostText) {
|
|---|
| 11618 | // In Fiber this, is just the state node right now. We assume it will be
|
|---|
| 11619 | // a host component or host text.
|
|---|
| 11620 | return inst.stateNode;
|
|---|
| 11621 | } // Without this first invariant, passing a non-DOM-component triggers the next
|
|---|
| 11622 | // invariant for a missing parent, which is super confusing.
|
|---|
| 11623 |
|
|---|
| 11624 |
|
|---|
| 11625 | throw new Error('getNodeFromInstance: Invalid argument.');
|
|---|
| 11626 | }
|
|---|
| 11627 | function getFiberCurrentPropsFromNode(node) {
|
|---|
| 11628 | return node[internalPropsKey] || null;
|
|---|
| 11629 | }
|
|---|
| 11630 | function updateFiberProps(node, props) {
|
|---|
| 11631 | node[internalPropsKey] = props;
|
|---|
| 11632 | }
|
|---|
| 11633 | function getEventListenerSet(node) {
|
|---|
| 11634 | var elementListenerSet = node[internalEventHandlersKey];
|
|---|
| 11635 |
|
|---|
| 11636 | if (elementListenerSet === undefined) {
|
|---|
| 11637 | elementListenerSet = node[internalEventHandlersKey] = new Set();
|
|---|
| 11638 | }
|
|---|
| 11639 |
|
|---|
| 11640 | return elementListenerSet;
|
|---|
| 11641 | }
|
|---|
| 11642 |
|
|---|
| 11643 | var loggedTypeFailures = {};
|
|---|
| 11644 | var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame;
|
|---|
| 11645 |
|
|---|
| 11646 | function setCurrentlyValidatingElement(element) {
|
|---|
| 11647 | {
|
|---|
| 11648 | if (element) {
|
|---|
| 11649 | var owner = element._owner;
|
|---|
| 11650 | var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null);
|
|---|
| 11651 | ReactDebugCurrentFrame$1.setExtraStackFrame(stack);
|
|---|
| 11652 | } else {
|
|---|
| 11653 | ReactDebugCurrentFrame$1.setExtraStackFrame(null);
|
|---|
| 11654 | }
|
|---|
| 11655 | }
|
|---|
| 11656 | }
|
|---|
| 11657 |
|
|---|
| 11658 | function checkPropTypes(typeSpecs, values, location, componentName, element) {
|
|---|
| 11659 | {
|
|---|
| 11660 | // $FlowFixMe This is okay but Flow doesn't know it.
|
|---|
| 11661 | var has = Function.call.bind(hasOwnProperty);
|
|---|
| 11662 |
|
|---|
| 11663 | for (var typeSpecName in typeSpecs) {
|
|---|
| 11664 | if (has(typeSpecs, typeSpecName)) {
|
|---|
| 11665 | var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to
|
|---|
| 11666 | // fail the render phase where it didn't fail before. So we log it.
|
|---|
| 11667 | // After these have been cleaned up, we'll let them throw.
|
|---|
| 11668 |
|
|---|
| 11669 | try {
|
|---|
| 11670 | // This is intentionally an invariant that gets caught. It's the same
|
|---|
| 11671 | // behavior as without this statement except with a better message.
|
|---|
| 11672 | if (typeof typeSpecs[typeSpecName] !== 'function') {
|
|---|
| 11673 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 11674 | var err = Error((componentName || 'React class') + ': ' + location + ' type `' + typeSpecName + '` is invalid; ' + 'it must be a function, usually from the `prop-types` package, but received `' + typeof typeSpecs[typeSpecName] + '`.' + 'This often happens because of typos such as `PropTypes.function` instead of `PropTypes.func`.');
|
|---|
| 11675 | err.name = 'Invariant Violation';
|
|---|
| 11676 | throw err;
|
|---|
| 11677 | }
|
|---|
| 11678 |
|
|---|
| 11679 | error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED');
|
|---|
| 11680 | } catch (ex) {
|
|---|
| 11681 | error$1 = ex;
|
|---|
| 11682 | }
|
|---|
| 11683 |
|
|---|
| 11684 | if (error$1 && !(error$1 instanceof Error)) {
|
|---|
| 11685 | setCurrentlyValidatingElement(element);
|
|---|
| 11686 |
|
|---|
| 11687 | error('%s: type specification of %s' + ' `%s` is invalid; the type checker ' + 'function must return `null` or an `Error` but returned a %s. ' + 'You may have forgotten to pass an argument to the type checker ' + 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' + 'shape all require an argument).', componentName || 'React class', location, typeSpecName, typeof error$1);
|
|---|
| 11688 |
|
|---|
| 11689 | setCurrentlyValidatingElement(null);
|
|---|
| 11690 | }
|
|---|
| 11691 |
|
|---|
| 11692 | if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) {
|
|---|
| 11693 | // Only monitor this failure once because there tends to be a lot of the
|
|---|
| 11694 | // same error.
|
|---|
| 11695 | loggedTypeFailures[error$1.message] = true;
|
|---|
| 11696 | setCurrentlyValidatingElement(element);
|
|---|
| 11697 |
|
|---|
| 11698 | error('Failed %s type: %s', location, error$1.message);
|
|---|
| 11699 |
|
|---|
| 11700 | setCurrentlyValidatingElement(null);
|
|---|
| 11701 | }
|
|---|
| 11702 | }
|
|---|
| 11703 | }
|
|---|
| 11704 | }
|
|---|
| 11705 | }
|
|---|
| 11706 |
|
|---|
| 11707 | var valueStack = [];
|
|---|
| 11708 | var fiberStack;
|
|---|
| 11709 |
|
|---|
| 11710 | {
|
|---|
| 11711 | fiberStack = [];
|
|---|
| 11712 | }
|
|---|
| 11713 |
|
|---|
| 11714 | var index = -1;
|
|---|
| 11715 |
|
|---|
| 11716 | function createCursor(defaultValue) {
|
|---|
| 11717 | return {
|
|---|
| 11718 | current: defaultValue
|
|---|
| 11719 | };
|
|---|
| 11720 | }
|
|---|
| 11721 |
|
|---|
| 11722 | function pop(cursor, fiber) {
|
|---|
| 11723 | if (index < 0) {
|
|---|
| 11724 | {
|
|---|
| 11725 | error('Unexpected pop.');
|
|---|
| 11726 | }
|
|---|
| 11727 |
|
|---|
| 11728 | return;
|
|---|
| 11729 | }
|
|---|
| 11730 |
|
|---|
| 11731 | {
|
|---|
| 11732 | if (fiber !== fiberStack[index]) {
|
|---|
| 11733 | error('Unexpected Fiber popped.');
|
|---|
| 11734 | }
|
|---|
| 11735 | }
|
|---|
| 11736 |
|
|---|
| 11737 | cursor.current = valueStack[index];
|
|---|
| 11738 | valueStack[index] = null;
|
|---|
| 11739 |
|
|---|
| 11740 | {
|
|---|
| 11741 | fiberStack[index] = null;
|
|---|
| 11742 | }
|
|---|
| 11743 |
|
|---|
| 11744 | index--;
|
|---|
| 11745 | }
|
|---|
| 11746 |
|
|---|
| 11747 | function push(cursor, value, fiber) {
|
|---|
| 11748 | index++;
|
|---|
| 11749 | valueStack[index] = cursor.current;
|
|---|
| 11750 |
|
|---|
| 11751 | {
|
|---|
| 11752 | fiberStack[index] = fiber;
|
|---|
| 11753 | }
|
|---|
| 11754 |
|
|---|
| 11755 | cursor.current = value;
|
|---|
| 11756 | }
|
|---|
| 11757 |
|
|---|
| 11758 | var warnedAboutMissingGetChildContext;
|
|---|
| 11759 |
|
|---|
| 11760 | {
|
|---|
| 11761 | warnedAboutMissingGetChildContext = {};
|
|---|
| 11762 | }
|
|---|
| 11763 |
|
|---|
| 11764 | var emptyContextObject = {};
|
|---|
| 11765 |
|
|---|
| 11766 | {
|
|---|
| 11767 | Object.freeze(emptyContextObject);
|
|---|
| 11768 | } // A cursor to the current merged context object on the stack.
|
|---|
| 11769 |
|
|---|
| 11770 |
|
|---|
| 11771 | var contextStackCursor = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed.
|
|---|
| 11772 |
|
|---|
| 11773 | var didPerformWorkStackCursor = createCursor(false); // Keep track of the previous context object that was on the stack.
|
|---|
| 11774 | // We use this to get access to the parent context after we have already
|
|---|
| 11775 | // pushed the next context provider, and now need to merge their contexts.
|
|---|
| 11776 |
|
|---|
| 11777 | var previousContext = emptyContextObject;
|
|---|
| 11778 |
|
|---|
| 11779 | function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) {
|
|---|
| 11780 | {
|
|---|
| 11781 | if (didPushOwnContextIfProvider && isContextProvider(Component)) {
|
|---|
| 11782 | // If the fiber is a context provider itself, when we read its context
|
|---|
| 11783 | // we may have already pushed its own child context on the stack. A context
|
|---|
| 11784 | // provider should not "see" its own child context. Therefore we read the
|
|---|
| 11785 | // previous (parent) context instead for a context provider.
|
|---|
| 11786 | return previousContext;
|
|---|
| 11787 | }
|
|---|
| 11788 |
|
|---|
| 11789 | return contextStackCursor.current;
|
|---|
| 11790 | }
|
|---|
| 11791 | }
|
|---|
| 11792 |
|
|---|
| 11793 | function cacheContext(workInProgress, unmaskedContext, maskedContext) {
|
|---|
| 11794 | {
|
|---|
| 11795 | var instance = workInProgress.stateNode;
|
|---|
| 11796 | instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext;
|
|---|
| 11797 | instance.__reactInternalMemoizedMaskedChildContext = maskedContext;
|
|---|
| 11798 | }
|
|---|
| 11799 | }
|
|---|
| 11800 |
|
|---|
| 11801 | function getMaskedContext(workInProgress, unmaskedContext) {
|
|---|
| 11802 | {
|
|---|
| 11803 | var type = workInProgress.type;
|
|---|
| 11804 | var contextTypes = type.contextTypes;
|
|---|
| 11805 |
|
|---|
| 11806 | if (!contextTypes) {
|
|---|
| 11807 | return emptyContextObject;
|
|---|
| 11808 | } // Avoid recreating masked context unless unmasked context has changed.
|
|---|
| 11809 | // Failing to do this will result in unnecessary calls to componentWillReceiveProps.
|
|---|
| 11810 | // This may trigger infinite loops if componentWillReceiveProps calls setState.
|
|---|
| 11811 |
|
|---|
| 11812 |
|
|---|
| 11813 | var instance = workInProgress.stateNode;
|
|---|
| 11814 |
|
|---|
| 11815 | if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) {
|
|---|
| 11816 | return instance.__reactInternalMemoizedMaskedChildContext;
|
|---|
| 11817 | }
|
|---|
| 11818 |
|
|---|
| 11819 | var context = {};
|
|---|
| 11820 |
|
|---|
| 11821 | for (var key in contextTypes) {
|
|---|
| 11822 | context[key] = unmaskedContext[key];
|
|---|
| 11823 | }
|
|---|
| 11824 |
|
|---|
| 11825 | {
|
|---|
| 11826 | var name = getComponentNameFromFiber(workInProgress) || 'Unknown';
|
|---|
| 11827 | checkPropTypes(contextTypes, context, 'context', name);
|
|---|
| 11828 | } // Cache unmasked context so we can avoid recreating masked context unless necessary.
|
|---|
| 11829 | // Context is created before the class component is instantiated so check for instance.
|
|---|
| 11830 |
|
|---|
| 11831 |
|
|---|
| 11832 | if (instance) {
|
|---|
| 11833 | cacheContext(workInProgress, unmaskedContext, context);
|
|---|
| 11834 | }
|
|---|
| 11835 |
|
|---|
| 11836 | return context;
|
|---|
| 11837 | }
|
|---|
| 11838 | }
|
|---|
| 11839 |
|
|---|
| 11840 | function hasContextChanged() {
|
|---|
| 11841 | {
|
|---|
| 11842 | return didPerformWorkStackCursor.current;
|
|---|
| 11843 | }
|
|---|
| 11844 | }
|
|---|
| 11845 |
|
|---|
| 11846 | function isContextProvider(type) {
|
|---|
| 11847 | {
|
|---|
| 11848 | var childContextTypes = type.childContextTypes;
|
|---|
| 11849 | return childContextTypes !== null && childContextTypes !== undefined;
|
|---|
| 11850 | }
|
|---|
| 11851 | }
|
|---|
| 11852 |
|
|---|
| 11853 | function popContext(fiber) {
|
|---|
| 11854 | {
|
|---|
| 11855 | pop(didPerformWorkStackCursor, fiber);
|
|---|
| 11856 | pop(contextStackCursor, fiber);
|
|---|
| 11857 | }
|
|---|
| 11858 | }
|
|---|
| 11859 |
|
|---|
| 11860 | function popTopLevelContextObject(fiber) {
|
|---|
| 11861 | {
|
|---|
| 11862 | pop(didPerformWorkStackCursor, fiber);
|
|---|
| 11863 | pop(contextStackCursor, fiber);
|
|---|
| 11864 | }
|
|---|
| 11865 | }
|
|---|
| 11866 |
|
|---|
| 11867 | function pushTopLevelContextObject(fiber, context, didChange) {
|
|---|
| 11868 | {
|
|---|
| 11869 | if (contextStackCursor.current !== emptyContextObject) {
|
|---|
| 11870 | throw new Error('Unexpected context found on stack. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 11871 | }
|
|---|
| 11872 |
|
|---|
| 11873 | push(contextStackCursor, context, fiber);
|
|---|
| 11874 | push(didPerformWorkStackCursor, didChange, fiber);
|
|---|
| 11875 | }
|
|---|
| 11876 | }
|
|---|
| 11877 |
|
|---|
| 11878 | function processChildContext(fiber, type, parentContext) {
|
|---|
| 11879 | {
|
|---|
| 11880 | var instance = fiber.stateNode;
|
|---|
| 11881 | var childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future.
|
|---|
| 11882 | // It has only been added in Fiber to match the (unintentional) behavior in Stack.
|
|---|
| 11883 |
|
|---|
| 11884 | if (typeof instance.getChildContext !== 'function') {
|
|---|
| 11885 | {
|
|---|
| 11886 | var componentName = getComponentNameFromFiber(fiber) || 'Unknown';
|
|---|
| 11887 |
|
|---|
| 11888 | if (!warnedAboutMissingGetChildContext[componentName]) {
|
|---|
| 11889 | warnedAboutMissingGetChildContext[componentName] = true;
|
|---|
| 11890 |
|
|---|
| 11891 | error('%s.childContextTypes is specified but there is no getChildContext() method ' + 'on the instance. You can either define getChildContext() on %s or remove ' + 'childContextTypes from it.', componentName, componentName);
|
|---|
| 11892 | }
|
|---|
| 11893 | }
|
|---|
| 11894 |
|
|---|
| 11895 | return parentContext;
|
|---|
| 11896 | }
|
|---|
| 11897 |
|
|---|
| 11898 | var childContext = instance.getChildContext();
|
|---|
| 11899 |
|
|---|
| 11900 | for (var contextKey in childContext) {
|
|---|
| 11901 | if (!(contextKey in childContextTypes)) {
|
|---|
| 11902 | throw new Error((getComponentNameFromFiber(fiber) || 'Unknown') + ".getChildContext(): key \"" + contextKey + "\" is not defined in childContextTypes.");
|
|---|
| 11903 | }
|
|---|
| 11904 | }
|
|---|
| 11905 |
|
|---|
| 11906 | {
|
|---|
| 11907 | var name = getComponentNameFromFiber(fiber) || 'Unknown';
|
|---|
| 11908 | checkPropTypes(childContextTypes, childContext, 'child context', name);
|
|---|
| 11909 | }
|
|---|
| 11910 |
|
|---|
| 11911 | return assign({}, parentContext, childContext);
|
|---|
| 11912 | }
|
|---|
| 11913 | }
|
|---|
| 11914 |
|
|---|
| 11915 | function pushContextProvider(workInProgress) {
|
|---|
| 11916 | {
|
|---|
| 11917 | var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity.
|
|---|
| 11918 | // If the instance does not exist yet, we will push null at first,
|
|---|
| 11919 | // and replace it on the stack later when invalidating the context.
|
|---|
| 11920 |
|
|---|
| 11921 | var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject; // Remember the parent context so we can merge with it later.
|
|---|
| 11922 | // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates.
|
|---|
| 11923 |
|
|---|
| 11924 | previousContext = contextStackCursor.current;
|
|---|
| 11925 | push(contextStackCursor, memoizedMergedChildContext, workInProgress);
|
|---|
| 11926 | push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress);
|
|---|
| 11927 | return true;
|
|---|
| 11928 | }
|
|---|
| 11929 | }
|
|---|
| 11930 |
|
|---|
| 11931 | function invalidateContextProvider(workInProgress, type, didChange) {
|
|---|
| 11932 | {
|
|---|
| 11933 | var instance = workInProgress.stateNode;
|
|---|
| 11934 |
|
|---|
| 11935 | if (!instance) {
|
|---|
| 11936 | throw new Error('Expected to have an instance by this point. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 11937 | }
|
|---|
| 11938 |
|
|---|
| 11939 | if (didChange) {
|
|---|
| 11940 | // Merge parent and own context.
|
|---|
| 11941 | // Skip this if we're not updating due to sCU.
|
|---|
| 11942 | // This avoids unnecessarily recomputing memoized values.
|
|---|
| 11943 | var mergedContext = processChildContext(workInProgress, type, previousContext);
|
|---|
| 11944 | instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one.
|
|---|
| 11945 | // It is important to unwind the context in the reverse order.
|
|---|
| 11946 |
|
|---|
| 11947 | pop(didPerformWorkStackCursor, workInProgress);
|
|---|
| 11948 | pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed.
|
|---|
| 11949 |
|
|---|
| 11950 | push(contextStackCursor, mergedContext, workInProgress);
|
|---|
| 11951 | push(didPerformWorkStackCursor, didChange, workInProgress);
|
|---|
| 11952 | } else {
|
|---|
| 11953 | pop(didPerformWorkStackCursor, workInProgress);
|
|---|
| 11954 | push(didPerformWorkStackCursor, didChange, workInProgress);
|
|---|
| 11955 | }
|
|---|
| 11956 | }
|
|---|
| 11957 | }
|
|---|
| 11958 |
|
|---|
| 11959 | function findCurrentUnmaskedContext(fiber) {
|
|---|
| 11960 | {
|
|---|
| 11961 | // Currently this is only used with renderSubtreeIntoContainer; not sure if it
|
|---|
| 11962 | // makes sense elsewhere
|
|---|
| 11963 | if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) {
|
|---|
| 11964 | throw new Error('Expected subtree parent to be a mounted class component. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 11965 | }
|
|---|
| 11966 |
|
|---|
| 11967 | var node = fiber;
|
|---|
| 11968 |
|
|---|
| 11969 | do {
|
|---|
| 11970 | switch (node.tag) {
|
|---|
| 11971 | case HostRoot:
|
|---|
| 11972 | return node.stateNode.context;
|
|---|
| 11973 |
|
|---|
| 11974 | case ClassComponent:
|
|---|
| 11975 | {
|
|---|
| 11976 | var Component = node.type;
|
|---|
| 11977 |
|
|---|
| 11978 | if (isContextProvider(Component)) {
|
|---|
| 11979 | return node.stateNode.__reactInternalMemoizedMergedChildContext;
|
|---|
| 11980 | }
|
|---|
| 11981 |
|
|---|
| 11982 | break;
|
|---|
| 11983 | }
|
|---|
| 11984 | }
|
|---|
| 11985 |
|
|---|
| 11986 | node = node.return;
|
|---|
| 11987 | } while (node !== null);
|
|---|
| 11988 |
|
|---|
| 11989 | throw new Error('Found unexpected detached subtree parent. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 11990 | }
|
|---|
| 11991 | }
|
|---|
| 11992 |
|
|---|
| 11993 | var LegacyRoot = 0;
|
|---|
| 11994 | var ConcurrentRoot = 1;
|
|---|
| 11995 |
|
|---|
| 11996 | var syncQueue = null;
|
|---|
| 11997 | var includesLegacySyncCallbacks = false;
|
|---|
| 11998 | var isFlushingSyncQueue = false;
|
|---|
| 11999 | function scheduleSyncCallback(callback) {
|
|---|
| 12000 | // Push this callback into an internal queue. We'll flush these either in
|
|---|
| 12001 | // the next tick, or earlier if something calls `flushSyncCallbackQueue`.
|
|---|
| 12002 | if (syncQueue === null) {
|
|---|
| 12003 | syncQueue = [callback];
|
|---|
| 12004 | } else {
|
|---|
| 12005 | // Push onto existing queue. Don't need to schedule a callback because
|
|---|
| 12006 | // we already scheduled one when we created the queue.
|
|---|
| 12007 | syncQueue.push(callback);
|
|---|
| 12008 | }
|
|---|
| 12009 | }
|
|---|
| 12010 | function scheduleLegacySyncCallback(callback) {
|
|---|
| 12011 | includesLegacySyncCallbacks = true;
|
|---|
| 12012 | scheduleSyncCallback(callback);
|
|---|
| 12013 | }
|
|---|
| 12014 | function flushSyncCallbacksOnlyInLegacyMode() {
|
|---|
| 12015 | // Only flushes the queue if there's a legacy sync callback scheduled.
|
|---|
| 12016 | // TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So
|
|---|
| 12017 | // it might make more sense for the queue to be a list of roots instead of a
|
|---|
| 12018 | // list of generic callbacks. Then we can have two: one for legacy roots, one
|
|---|
| 12019 | // for concurrent roots. And this method would only flush the legacy ones.
|
|---|
| 12020 | if (includesLegacySyncCallbacks) {
|
|---|
| 12021 | flushSyncCallbacks();
|
|---|
| 12022 | }
|
|---|
| 12023 | }
|
|---|
| 12024 | function flushSyncCallbacks() {
|
|---|
| 12025 | if (!isFlushingSyncQueue && syncQueue !== null) {
|
|---|
| 12026 | // Prevent re-entrance.
|
|---|
| 12027 | isFlushingSyncQueue = true;
|
|---|
| 12028 | var i = 0;
|
|---|
| 12029 | var previousUpdatePriority = getCurrentUpdatePriority();
|
|---|
| 12030 |
|
|---|
| 12031 | try {
|
|---|
| 12032 | var isSync = true;
|
|---|
| 12033 | var queue = syncQueue; // TODO: Is this necessary anymore? The only user code that runs in this
|
|---|
| 12034 | // queue is in the render or commit phases.
|
|---|
| 12035 |
|
|---|
| 12036 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 12037 |
|
|---|
| 12038 | for (; i < queue.length; i++) {
|
|---|
| 12039 | var callback = queue[i];
|
|---|
| 12040 |
|
|---|
| 12041 | do {
|
|---|
| 12042 | callback = callback(isSync);
|
|---|
| 12043 | } while (callback !== null);
|
|---|
| 12044 | }
|
|---|
| 12045 |
|
|---|
| 12046 | syncQueue = null;
|
|---|
| 12047 | includesLegacySyncCallbacks = false;
|
|---|
| 12048 | } catch (error) {
|
|---|
| 12049 | // If something throws, leave the remaining callbacks on the queue.
|
|---|
| 12050 | if (syncQueue !== null) {
|
|---|
| 12051 | syncQueue = syncQueue.slice(i + 1);
|
|---|
| 12052 | } // Resume flushing in the next tick
|
|---|
| 12053 |
|
|---|
| 12054 |
|
|---|
| 12055 | scheduleCallback(ImmediatePriority, flushSyncCallbacks);
|
|---|
| 12056 | throw error;
|
|---|
| 12057 | } finally {
|
|---|
| 12058 | setCurrentUpdatePriority(previousUpdatePriority);
|
|---|
| 12059 | isFlushingSyncQueue = false;
|
|---|
| 12060 | }
|
|---|
| 12061 | }
|
|---|
| 12062 |
|
|---|
| 12063 | return null;
|
|---|
| 12064 | }
|
|---|
| 12065 |
|
|---|
| 12066 | // TODO: Use the unified fiber stack module instead of this local one?
|
|---|
| 12067 | // Intentionally not using it yet to derisk the initial implementation, because
|
|---|
| 12068 | // the way we push/pop these values is a bit unusual. If there's a mistake, I'd
|
|---|
| 12069 | // rather the ids be wrong than crash the whole reconciler.
|
|---|
| 12070 | var forkStack = [];
|
|---|
| 12071 | var forkStackIndex = 0;
|
|---|
| 12072 | var treeForkProvider = null;
|
|---|
| 12073 | var treeForkCount = 0;
|
|---|
| 12074 | var idStack = [];
|
|---|
| 12075 | var idStackIndex = 0;
|
|---|
| 12076 | var treeContextProvider = null;
|
|---|
| 12077 | var treeContextId = 1;
|
|---|
| 12078 | var treeContextOverflow = '';
|
|---|
| 12079 | function isForkedChild(workInProgress) {
|
|---|
| 12080 | warnIfNotHydrating();
|
|---|
| 12081 | return (workInProgress.flags & Forked) !== NoFlags;
|
|---|
| 12082 | }
|
|---|
| 12083 | function getForksAtLevel(workInProgress) {
|
|---|
| 12084 | warnIfNotHydrating();
|
|---|
| 12085 | return treeForkCount;
|
|---|
| 12086 | }
|
|---|
| 12087 | function getTreeId() {
|
|---|
| 12088 | var overflow = treeContextOverflow;
|
|---|
| 12089 | var idWithLeadingBit = treeContextId;
|
|---|
| 12090 | var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit);
|
|---|
| 12091 | return id.toString(32) + overflow;
|
|---|
| 12092 | }
|
|---|
| 12093 | function pushTreeFork(workInProgress, totalChildren) {
|
|---|
| 12094 | // This is called right after we reconcile an array (or iterator) of child
|
|---|
| 12095 | // fibers, because that's the only place where we know how many children in
|
|---|
| 12096 | // the whole set without doing extra work later, or storing addtional
|
|---|
| 12097 | // information on the fiber.
|
|---|
| 12098 | //
|
|---|
| 12099 | // That's why this function is separate from pushTreeId — it's called during
|
|---|
| 12100 | // the render phase of the fork parent, not the child, which is where we push
|
|---|
| 12101 | // the other context values.
|
|---|
| 12102 | //
|
|---|
| 12103 | // In the Fizz implementation this is much simpler because the child is
|
|---|
| 12104 | // rendered in the same callstack as the parent.
|
|---|
| 12105 | //
|
|---|
| 12106 | // It might be better to just add a `forks` field to the Fiber type. It would
|
|---|
| 12107 | // make this module simpler.
|
|---|
| 12108 | warnIfNotHydrating();
|
|---|
| 12109 | forkStack[forkStackIndex++] = treeForkCount;
|
|---|
| 12110 | forkStack[forkStackIndex++] = treeForkProvider;
|
|---|
| 12111 | treeForkProvider = workInProgress;
|
|---|
| 12112 | treeForkCount = totalChildren;
|
|---|
| 12113 | }
|
|---|
| 12114 | function pushTreeId(workInProgress, totalChildren, index) {
|
|---|
| 12115 | warnIfNotHydrating();
|
|---|
| 12116 | idStack[idStackIndex++] = treeContextId;
|
|---|
| 12117 | idStack[idStackIndex++] = treeContextOverflow;
|
|---|
| 12118 | idStack[idStackIndex++] = treeContextProvider;
|
|---|
| 12119 | treeContextProvider = workInProgress;
|
|---|
| 12120 | var baseIdWithLeadingBit = treeContextId;
|
|---|
| 12121 | var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part
|
|---|
| 12122 | // of the id; we use it to account for leading 0s.
|
|---|
| 12123 |
|
|---|
| 12124 | var baseLength = getBitLength(baseIdWithLeadingBit) - 1;
|
|---|
| 12125 | var baseId = baseIdWithLeadingBit & ~(1 << baseLength);
|
|---|
| 12126 | var slot = index + 1;
|
|---|
| 12127 | var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into
|
|---|
| 12128 | // consideration the leading 1 we use to mark the end of the sequence.
|
|---|
| 12129 |
|
|---|
| 12130 | if (length > 30) {
|
|---|
| 12131 | // We overflowed the bitwise-safe range. Fall back to slower algorithm.
|
|---|
| 12132 | // This branch assumes the length of the base id is greater than 5; it won't
|
|---|
| 12133 | // work for smaller ids, because you need 5 bits per character.
|
|---|
| 12134 | //
|
|---|
| 12135 | // We encode the id in multiple steps: first the base id, then the
|
|---|
| 12136 | // remaining digits.
|
|---|
| 12137 | //
|
|---|
| 12138 | // Each 5 bit sequence corresponds to a single base 32 character. So for
|
|---|
| 12139 | // example, if the current id is 23 bits long, we can convert 20 of those
|
|---|
| 12140 | // bits into a string of 4 characters, with 3 bits left over.
|
|---|
| 12141 | //
|
|---|
| 12142 | // First calculate how many bits in the base id represent a complete
|
|---|
| 12143 | // sequence of characters.
|
|---|
| 12144 | var numberOfOverflowBits = baseLength - baseLength % 5; // Then create a bitmask that selects only those bits.
|
|---|
| 12145 |
|
|---|
| 12146 | var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string.
|
|---|
| 12147 |
|
|---|
| 12148 | var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id.
|
|---|
| 12149 |
|
|---|
| 12150 | var restOfBaseId = baseId >> numberOfOverflowBits;
|
|---|
| 12151 | var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because
|
|---|
| 12152 | // we made more room, this time it won't overflow.
|
|---|
| 12153 |
|
|---|
| 12154 | var restOfLength = getBitLength(totalChildren) + restOfBaseLength;
|
|---|
| 12155 | var restOfNewBits = slot << restOfBaseLength;
|
|---|
| 12156 | var id = restOfNewBits | restOfBaseId;
|
|---|
| 12157 | var overflow = newOverflow + baseOverflow;
|
|---|
| 12158 | treeContextId = 1 << restOfLength | id;
|
|---|
| 12159 | treeContextOverflow = overflow;
|
|---|
| 12160 | } else {
|
|---|
| 12161 | // Normal path
|
|---|
| 12162 | var newBits = slot << baseLength;
|
|---|
| 12163 |
|
|---|
| 12164 | var _id = newBits | baseId;
|
|---|
| 12165 |
|
|---|
| 12166 | var _overflow = baseOverflow;
|
|---|
| 12167 | treeContextId = 1 << length | _id;
|
|---|
| 12168 | treeContextOverflow = _overflow;
|
|---|
| 12169 | }
|
|---|
| 12170 | }
|
|---|
| 12171 | function pushMaterializedTreeId(workInProgress) {
|
|---|
| 12172 | warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear
|
|---|
| 12173 | // in its children.
|
|---|
| 12174 |
|
|---|
| 12175 | var returnFiber = workInProgress.return;
|
|---|
| 12176 |
|
|---|
| 12177 | if (returnFiber !== null) {
|
|---|
| 12178 | var numberOfForks = 1;
|
|---|
| 12179 | var slotIndex = 0;
|
|---|
| 12180 | pushTreeFork(workInProgress, numberOfForks);
|
|---|
| 12181 | pushTreeId(workInProgress, numberOfForks, slotIndex);
|
|---|
| 12182 | }
|
|---|
| 12183 | }
|
|---|
| 12184 |
|
|---|
| 12185 | function getBitLength(number) {
|
|---|
| 12186 | return 32 - clz32(number);
|
|---|
| 12187 | }
|
|---|
| 12188 |
|
|---|
| 12189 | function getLeadingBit(id) {
|
|---|
| 12190 | return 1 << getBitLength(id) - 1;
|
|---|
| 12191 | }
|
|---|
| 12192 |
|
|---|
| 12193 | function popTreeContext(workInProgress) {
|
|---|
| 12194 | // Restore the previous values.
|
|---|
| 12195 | // This is a bit more complicated than other context-like modules in Fiber
|
|---|
| 12196 | // because the same Fiber may appear on the stack multiple times and for
|
|---|
| 12197 | // different reasons. We have to keep popping until the work-in-progress is
|
|---|
| 12198 | // no longer at the top of the stack.
|
|---|
| 12199 | while (workInProgress === treeForkProvider) {
|
|---|
| 12200 | treeForkProvider = forkStack[--forkStackIndex];
|
|---|
| 12201 | forkStack[forkStackIndex] = null;
|
|---|
| 12202 | treeForkCount = forkStack[--forkStackIndex];
|
|---|
| 12203 | forkStack[forkStackIndex] = null;
|
|---|
| 12204 | }
|
|---|
| 12205 |
|
|---|
| 12206 | while (workInProgress === treeContextProvider) {
|
|---|
| 12207 | treeContextProvider = idStack[--idStackIndex];
|
|---|
| 12208 | idStack[idStackIndex] = null;
|
|---|
| 12209 | treeContextOverflow = idStack[--idStackIndex];
|
|---|
| 12210 | idStack[idStackIndex] = null;
|
|---|
| 12211 | treeContextId = idStack[--idStackIndex];
|
|---|
| 12212 | idStack[idStackIndex] = null;
|
|---|
| 12213 | }
|
|---|
| 12214 | }
|
|---|
| 12215 | function getSuspendedTreeContext() {
|
|---|
| 12216 | warnIfNotHydrating();
|
|---|
| 12217 |
|
|---|
| 12218 | if (treeContextProvider !== null) {
|
|---|
| 12219 | return {
|
|---|
| 12220 | id: treeContextId,
|
|---|
| 12221 | overflow: treeContextOverflow
|
|---|
| 12222 | };
|
|---|
| 12223 | } else {
|
|---|
| 12224 | return null;
|
|---|
| 12225 | }
|
|---|
| 12226 | }
|
|---|
| 12227 | function restoreSuspendedTreeContext(workInProgress, suspendedContext) {
|
|---|
| 12228 | warnIfNotHydrating();
|
|---|
| 12229 | idStack[idStackIndex++] = treeContextId;
|
|---|
| 12230 | idStack[idStackIndex++] = treeContextOverflow;
|
|---|
| 12231 | idStack[idStackIndex++] = treeContextProvider;
|
|---|
| 12232 | treeContextId = suspendedContext.id;
|
|---|
| 12233 | treeContextOverflow = suspendedContext.overflow;
|
|---|
| 12234 | treeContextProvider = workInProgress;
|
|---|
| 12235 | }
|
|---|
| 12236 |
|
|---|
| 12237 | function warnIfNotHydrating() {
|
|---|
| 12238 | {
|
|---|
| 12239 | if (!getIsHydrating()) {
|
|---|
| 12240 | error('Expected to be hydrating. This is a bug in React. Please file ' + 'an issue.');
|
|---|
| 12241 | }
|
|---|
| 12242 | }
|
|---|
| 12243 | }
|
|---|
| 12244 |
|
|---|
| 12245 | // This may have been an insertion or a hydration.
|
|---|
| 12246 |
|
|---|
| 12247 | var hydrationParentFiber = null;
|
|---|
| 12248 | var nextHydratableInstance = null;
|
|---|
| 12249 | var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches
|
|---|
| 12250 | // due to earlier mismatches or a suspended fiber.
|
|---|
| 12251 |
|
|---|
| 12252 | var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary
|
|---|
| 12253 |
|
|---|
| 12254 | var hydrationErrors = null;
|
|---|
| 12255 |
|
|---|
| 12256 | function warnIfHydrating() {
|
|---|
| 12257 | {
|
|---|
| 12258 | if (isHydrating) {
|
|---|
| 12259 | error('We should not be hydrating here. This is a bug in React. Please file a bug.');
|
|---|
| 12260 | }
|
|---|
| 12261 | }
|
|---|
| 12262 | }
|
|---|
| 12263 |
|
|---|
| 12264 | function markDidThrowWhileHydratingDEV() {
|
|---|
| 12265 | {
|
|---|
| 12266 | didSuspendOrErrorDEV = true;
|
|---|
| 12267 | }
|
|---|
| 12268 | }
|
|---|
| 12269 | function didSuspendOrErrorWhileHydratingDEV() {
|
|---|
| 12270 | {
|
|---|
| 12271 | return didSuspendOrErrorDEV;
|
|---|
| 12272 | }
|
|---|
| 12273 | }
|
|---|
| 12274 |
|
|---|
| 12275 | function enterHydrationState(fiber) {
|
|---|
| 12276 |
|
|---|
| 12277 | var parentInstance = fiber.stateNode.containerInfo;
|
|---|
| 12278 | nextHydratableInstance = getFirstHydratableChildWithinContainer(parentInstance);
|
|---|
| 12279 | hydrationParentFiber = fiber;
|
|---|
| 12280 | isHydrating = true;
|
|---|
| 12281 | hydrationErrors = null;
|
|---|
| 12282 | didSuspendOrErrorDEV = false;
|
|---|
| 12283 | return true;
|
|---|
| 12284 | }
|
|---|
| 12285 |
|
|---|
| 12286 | function reenterHydrationStateFromDehydratedSuspenseInstance(fiber, suspenseInstance, treeContext) {
|
|---|
| 12287 |
|
|---|
| 12288 | nextHydratableInstance = getFirstHydratableChildWithinSuspenseInstance(suspenseInstance);
|
|---|
| 12289 | hydrationParentFiber = fiber;
|
|---|
| 12290 | isHydrating = true;
|
|---|
| 12291 | hydrationErrors = null;
|
|---|
| 12292 | didSuspendOrErrorDEV = false;
|
|---|
| 12293 |
|
|---|
| 12294 | if (treeContext !== null) {
|
|---|
| 12295 | restoreSuspendedTreeContext(fiber, treeContext);
|
|---|
| 12296 | }
|
|---|
| 12297 |
|
|---|
| 12298 | return true;
|
|---|
| 12299 | }
|
|---|
| 12300 |
|
|---|
| 12301 | function warnUnhydratedInstance(returnFiber, instance) {
|
|---|
| 12302 | {
|
|---|
| 12303 | switch (returnFiber.tag) {
|
|---|
| 12304 | case HostRoot:
|
|---|
| 12305 | {
|
|---|
| 12306 | didNotHydrateInstanceWithinContainer(returnFiber.stateNode.containerInfo, instance);
|
|---|
| 12307 | break;
|
|---|
| 12308 | }
|
|---|
| 12309 |
|
|---|
| 12310 | case HostComponent:
|
|---|
| 12311 | {
|
|---|
| 12312 | var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
|---|
| 12313 | didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, // TODO: Delete this argument when we remove the legacy root API.
|
|---|
| 12314 | isConcurrentMode);
|
|---|
| 12315 | break;
|
|---|
| 12316 | }
|
|---|
| 12317 |
|
|---|
| 12318 | case SuspenseComponent:
|
|---|
| 12319 | {
|
|---|
| 12320 | var suspenseState = returnFiber.memoizedState;
|
|---|
| 12321 | if (suspenseState.dehydrated !== null) didNotHydrateInstanceWithinSuspenseInstance(suspenseState.dehydrated, instance);
|
|---|
| 12322 | break;
|
|---|
| 12323 | }
|
|---|
| 12324 | }
|
|---|
| 12325 | }
|
|---|
| 12326 | }
|
|---|
| 12327 |
|
|---|
| 12328 | function deleteHydratableInstance(returnFiber, instance) {
|
|---|
| 12329 | warnUnhydratedInstance(returnFiber, instance);
|
|---|
| 12330 | var childToDelete = createFiberFromHostInstanceForDeletion();
|
|---|
| 12331 | childToDelete.stateNode = instance;
|
|---|
| 12332 | childToDelete.return = returnFiber;
|
|---|
| 12333 | var deletions = returnFiber.deletions;
|
|---|
| 12334 |
|
|---|
| 12335 | if (deletions === null) {
|
|---|
| 12336 | returnFiber.deletions = [childToDelete];
|
|---|
| 12337 | returnFiber.flags |= ChildDeletion;
|
|---|
| 12338 | } else {
|
|---|
| 12339 | deletions.push(childToDelete);
|
|---|
| 12340 | }
|
|---|
| 12341 | }
|
|---|
| 12342 |
|
|---|
| 12343 | function warnNonhydratedInstance(returnFiber, fiber) {
|
|---|
| 12344 | {
|
|---|
| 12345 | if (didSuspendOrErrorDEV) {
|
|---|
| 12346 | // Inside a boundary that already suspended. We're currently rendering the
|
|---|
| 12347 | // siblings of a suspended node. The mismatch may be due to the missing
|
|---|
| 12348 | // data, so it's probably a false positive.
|
|---|
| 12349 | return;
|
|---|
| 12350 | }
|
|---|
| 12351 |
|
|---|
| 12352 | switch (returnFiber.tag) {
|
|---|
| 12353 | case HostRoot:
|
|---|
| 12354 | {
|
|---|
| 12355 | var parentContainer = returnFiber.stateNode.containerInfo;
|
|---|
| 12356 |
|
|---|
| 12357 | switch (fiber.tag) {
|
|---|
| 12358 | case HostComponent:
|
|---|
| 12359 | var type = fiber.type;
|
|---|
| 12360 | var props = fiber.pendingProps;
|
|---|
| 12361 | didNotFindHydratableInstanceWithinContainer(parentContainer, type);
|
|---|
| 12362 | break;
|
|---|
| 12363 |
|
|---|
| 12364 | case HostText:
|
|---|
| 12365 | var text = fiber.pendingProps;
|
|---|
| 12366 | didNotFindHydratableTextInstanceWithinContainer(parentContainer, text);
|
|---|
| 12367 | break;
|
|---|
| 12368 | }
|
|---|
| 12369 |
|
|---|
| 12370 | break;
|
|---|
| 12371 | }
|
|---|
| 12372 |
|
|---|
| 12373 | case HostComponent:
|
|---|
| 12374 | {
|
|---|
| 12375 | var parentType = returnFiber.type;
|
|---|
| 12376 | var parentProps = returnFiber.memoizedProps;
|
|---|
| 12377 | var parentInstance = returnFiber.stateNode;
|
|---|
| 12378 |
|
|---|
| 12379 | switch (fiber.tag) {
|
|---|
| 12380 | case HostComponent:
|
|---|
| 12381 | {
|
|---|
| 12382 | var _type = fiber.type;
|
|---|
| 12383 | var _props = fiber.pendingProps;
|
|---|
| 12384 | var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
|---|
| 12385 | didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props, // TODO: Delete this argument when we remove the legacy root API.
|
|---|
| 12386 | isConcurrentMode);
|
|---|
| 12387 | break;
|
|---|
| 12388 | }
|
|---|
| 12389 |
|
|---|
| 12390 | case HostText:
|
|---|
| 12391 | {
|
|---|
| 12392 | var _text = fiber.pendingProps;
|
|---|
| 12393 |
|
|---|
| 12394 | var _isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
|---|
| 12395 |
|
|---|
| 12396 | didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text, // TODO: Delete this argument when we remove the legacy root API.
|
|---|
| 12397 | _isConcurrentMode);
|
|---|
| 12398 | break;
|
|---|
| 12399 | }
|
|---|
| 12400 | }
|
|---|
| 12401 |
|
|---|
| 12402 | break;
|
|---|
| 12403 | }
|
|---|
| 12404 |
|
|---|
| 12405 | case SuspenseComponent:
|
|---|
| 12406 | {
|
|---|
| 12407 | var suspenseState = returnFiber.memoizedState;
|
|---|
| 12408 | var _parentInstance = suspenseState.dehydrated;
|
|---|
| 12409 | if (_parentInstance !== null) switch (fiber.tag) {
|
|---|
| 12410 | case HostComponent:
|
|---|
| 12411 | var _type2 = fiber.type;
|
|---|
| 12412 | var _props2 = fiber.pendingProps;
|
|---|
| 12413 | didNotFindHydratableInstanceWithinSuspenseInstance(_parentInstance, _type2);
|
|---|
| 12414 | break;
|
|---|
| 12415 |
|
|---|
| 12416 | case HostText:
|
|---|
| 12417 | var _text2 = fiber.pendingProps;
|
|---|
| 12418 | didNotFindHydratableTextInstanceWithinSuspenseInstance(_parentInstance, _text2);
|
|---|
| 12419 | break;
|
|---|
| 12420 | }
|
|---|
| 12421 | break;
|
|---|
| 12422 | }
|
|---|
| 12423 |
|
|---|
| 12424 | default:
|
|---|
| 12425 | return;
|
|---|
| 12426 | }
|
|---|
| 12427 | }
|
|---|
| 12428 | }
|
|---|
| 12429 |
|
|---|
| 12430 | function insertNonHydratedInstance(returnFiber, fiber) {
|
|---|
| 12431 | fiber.flags = fiber.flags & ~Hydrating | Placement;
|
|---|
| 12432 | warnNonhydratedInstance(returnFiber, fiber);
|
|---|
| 12433 | }
|
|---|
| 12434 |
|
|---|
| 12435 | function tryHydrate(fiber, nextInstance) {
|
|---|
| 12436 | switch (fiber.tag) {
|
|---|
| 12437 | case HostComponent:
|
|---|
| 12438 | {
|
|---|
| 12439 | var type = fiber.type;
|
|---|
| 12440 | var props = fiber.pendingProps;
|
|---|
| 12441 | var instance = canHydrateInstance(nextInstance, type);
|
|---|
| 12442 |
|
|---|
| 12443 | if (instance !== null) {
|
|---|
| 12444 | fiber.stateNode = instance;
|
|---|
| 12445 | hydrationParentFiber = fiber;
|
|---|
| 12446 | nextHydratableInstance = getFirstHydratableChild(instance);
|
|---|
| 12447 | return true;
|
|---|
| 12448 | }
|
|---|
| 12449 |
|
|---|
| 12450 | return false;
|
|---|
| 12451 | }
|
|---|
| 12452 |
|
|---|
| 12453 | case HostText:
|
|---|
| 12454 | {
|
|---|
| 12455 | var text = fiber.pendingProps;
|
|---|
| 12456 | var textInstance = canHydrateTextInstance(nextInstance, text);
|
|---|
| 12457 |
|
|---|
| 12458 | if (textInstance !== null) {
|
|---|
| 12459 | fiber.stateNode = textInstance;
|
|---|
| 12460 | hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate.
|
|---|
| 12461 |
|
|---|
| 12462 | nextHydratableInstance = null;
|
|---|
| 12463 | return true;
|
|---|
| 12464 | }
|
|---|
| 12465 |
|
|---|
| 12466 | return false;
|
|---|
| 12467 | }
|
|---|
| 12468 |
|
|---|
| 12469 | case SuspenseComponent:
|
|---|
| 12470 | {
|
|---|
| 12471 | var suspenseInstance = canHydrateSuspenseInstance(nextInstance);
|
|---|
| 12472 |
|
|---|
| 12473 | if (suspenseInstance !== null) {
|
|---|
| 12474 | var suspenseState = {
|
|---|
| 12475 | dehydrated: suspenseInstance,
|
|---|
| 12476 | treeContext: getSuspendedTreeContext(),
|
|---|
| 12477 | retryLane: OffscreenLane
|
|---|
| 12478 | };
|
|---|
| 12479 | fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber.
|
|---|
| 12480 | // This simplifies the code for getHostSibling and deleting nodes,
|
|---|
| 12481 | // since it doesn't have to consider all Suspense boundaries and
|
|---|
| 12482 | // check if they're dehydrated ones or not.
|
|---|
| 12483 |
|
|---|
| 12484 | var dehydratedFragment = createFiberFromDehydratedFragment(suspenseInstance);
|
|---|
| 12485 | dehydratedFragment.return = fiber;
|
|---|
| 12486 | fiber.child = dehydratedFragment;
|
|---|
| 12487 | hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into
|
|---|
| 12488 | // it during the first pass. Instead, we'll reenter it later.
|
|---|
| 12489 |
|
|---|
| 12490 | nextHydratableInstance = null;
|
|---|
| 12491 | return true;
|
|---|
| 12492 | }
|
|---|
| 12493 |
|
|---|
| 12494 | return false;
|
|---|
| 12495 | }
|
|---|
| 12496 |
|
|---|
| 12497 | default:
|
|---|
| 12498 | return false;
|
|---|
| 12499 | }
|
|---|
| 12500 | }
|
|---|
| 12501 |
|
|---|
| 12502 | function shouldClientRenderOnMismatch(fiber) {
|
|---|
| 12503 | return (fiber.mode & ConcurrentMode) !== NoMode && (fiber.flags & DidCapture) === NoFlags;
|
|---|
| 12504 | }
|
|---|
| 12505 |
|
|---|
| 12506 | function throwOnHydrationMismatch(fiber) {
|
|---|
| 12507 | throw new Error('Hydration failed because the initial UI does not match what was ' + 'rendered on the server.');
|
|---|
| 12508 | }
|
|---|
| 12509 |
|
|---|
| 12510 | function tryToClaimNextHydratableInstance(fiber) {
|
|---|
| 12511 | if (!isHydrating) {
|
|---|
| 12512 | return;
|
|---|
| 12513 | }
|
|---|
| 12514 |
|
|---|
| 12515 | var nextInstance = nextHydratableInstance;
|
|---|
| 12516 |
|
|---|
| 12517 | if (!nextInstance) {
|
|---|
| 12518 | if (shouldClientRenderOnMismatch(fiber)) {
|
|---|
| 12519 | warnNonhydratedInstance(hydrationParentFiber, fiber);
|
|---|
| 12520 | throwOnHydrationMismatch();
|
|---|
| 12521 | } // Nothing to hydrate. Make it an insertion.
|
|---|
| 12522 |
|
|---|
| 12523 |
|
|---|
| 12524 | insertNonHydratedInstance(hydrationParentFiber, fiber);
|
|---|
| 12525 | isHydrating = false;
|
|---|
| 12526 | hydrationParentFiber = fiber;
|
|---|
| 12527 | return;
|
|---|
| 12528 | }
|
|---|
| 12529 |
|
|---|
| 12530 | var firstAttemptedInstance = nextInstance;
|
|---|
| 12531 |
|
|---|
| 12532 | if (!tryHydrate(fiber, nextInstance)) {
|
|---|
| 12533 | if (shouldClientRenderOnMismatch(fiber)) {
|
|---|
| 12534 | warnNonhydratedInstance(hydrationParentFiber, fiber);
|
|---|
| 12535 | throwOnHydrationMismatch();
|
|---|
| 12536 | } // If we can't hydrate this instance let's try the next one.
|
|---|
| 12537 | // We use this as a heuristic. It's based on intuition and not data so it
|
|---|
| 12538 | // might be flawed or unnecessary.
|
|---|
| 12539 |
|
|---|
| 12540 |
|
|---|
| 12541 | nextInstance = getNextHydratableSibling(firstAttemptedInstance);
|
|---|
| 12542 | var prevHydrationParentFiber = hydrationParentFiber;
|
|---|
| 12543 |
|
|---|
| 12544 | if (!nextInstance || !tryHydrate(fiber, nextInstance)) {
|
|---|
| 12545 | // Nothing to hydrate. Make it an insertion.
|
|---|
| 12546 | insertNonHydratedInstance(hydrationParentFiber, fiber);
|
|---|
| 12547 | isHydrating = false;
|
|---|
| 12548 | hydrationParentFiber = fiber;
|
|---|
| 12549 | return;
|
|---|
| 12550 | } // We matched the next one, we'll now assume that the first one was
|
|---|
| 12551 | // superfluous and we'll delete it. Since we can't eagerly delete it
|
|---|
| 12552 | // we'll have to schedule a deletion. To do that, this node needs a dummy
|
|---|
| 12553 | // fiber associated with it.
|
|---|
| 12554 |
|
|---|
| 12555 |
|
|---|
| 12556 | deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance);
|
|---|
| 12557 | }
|
|---|
| 12558 | }
|
|---|
| 12559 |
|
|---|
| 12560 | function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) {
|
|---|
| 12561 |
|
|---|
| 12562 | var instance = fiber.stateNode;
|
|---|
| 12563 | var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
|
|---|
| 12564 | var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber, shouldWarnIfMismatchDev); // TODO: Type this specific to this type of component.
|
|---|
| 12565 |
|
|---|
| 12566 | fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
|
|---|
| 12567 | // is a new ref we mark this as an update.
|
|---|
| 12568 |
|
|---|
| 12569 | if (updatePayload !== null) {
|
|---|
| 12570 | return true;
|
|---|
| 12571 | }
|
|---|
| 12572 |
|
|---|
| 12573 | return false;
|
|---|
| 12574 | }
|
|---|
| 12575 |
|
|---|
| 12576 | function prepareToHydrateHostTextInstance(fiber) {
|
|---|
| 12577 |
|
|---|
| 12578 | var textInstance = fiber.stateNode;
|
|---|
| 12579 | var textContent = fiber.memoizedProps;
|
|---|
| 12580 | var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber);
|
|---|
| 12581 |
|
|---|
| 12582 | if (shouldUpdate) {
|
|---|
| 12583 | // We assume that prepareToHydrateHostTextInstance is called in a context where the
|
|---|
| 12584 | // hydration parent is the parent host component of this host text.
|
|---|
| 12585 | var returnFiber = hydrationParentFiber;
|
|---|
| 12586 |
|
|---|
| 12587 | if (returnFiber !== null) {
|
|---|
| 12588 | switch (returnFiber.tag) {
|
|---|
| 12589 | case HostRoot:
|
|---|
| 12590 | {
|
|---|
| 12591 | var parentContainer = returnFiber.stateNode.containerInfo;
|
|---|
| 12592 | var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
|---|
| 12593 | didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API.
|
|---|
| 12594 | isConcurrentMode);
|
|---|
| 12595 | break;
|
|---|
| 12596 | }
|
|---|
| 12597 |
|
|---|
| 12598 | case HostComponent:
|
|---|
| 12599 | {
|
|---|
| 12600 | var parentType = returnFiber.type;
|
|---|
| 12601 | var parentProps = returnFiber.memoizedProps;
|
|---|
| 12602 | var parentInstance = returnFiber.stateNode;
|
|---|
| 12603 |
|
|---|
| 12604 | var _isConcurrentMode2 = (returnFiber.mode & ConcurrentMode) !== NoMode;
|
|---|
| 12605 |
|
|---|
| 12606 | didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API.
|
|---|
| 12607 | _isConcurrentMode2);
|
|---|
| 12608 | break;
|
|---|
| 12609 | }
|
|---|
| 12610 | }
|
|---|
| 12611 | }
|
|---|
| 12612 | }
|
|---|
| 12613 |
|
|---|
| 12614 | return shouldUpdate;
|
|---|
| 12615 | }
|
|---|
| 12616 |
|
|---|
| 12617 | function prepareToHydrateHostSuspenseInstance(fiber) {
|
|---|
| 12618 |
|
|---|
| 12619 | var suspenseState = fiber.memoizedState;
|
|---|
| 12620 | var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null;
|
|---|
| 12621 |
|
|---|
| 12622 | if (!suspenseInstance) {
|
|---|
| 12623 | throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 12624 | }
|
|---|
| 12625 |
|
|---|
| 12626 | hydrateSuspenseInstance(suspenseInstance, fiber);
|
|---|
| 12627 | }
|
|---|
| 12628 |
|
|---|
| 12629 | function skipPastDehydratedSuspenseInstance(fiber) {
|
|---|
| 12630 |
|
|---|
| 12631 | var suspenseState = fiber.memoizedState;
|
|---|
| 12632 | var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null;
|
|---|
| 12633 |
|
|---|
| 12634 | if (!suspenseInstance) {
|
|---|
| 12635 | throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 12636 | }
|
|---|
| 12637 |
|
|---|
| 12638 | return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance);
|
|---|
| 12639 | }
|
|---|
| 12640 |
|
|---|
| 12641 | function popToNextHostParent(fiber) {
|
|---|
| 12642 | var parent = fiber.return;
|
|---|
| 12643 |
|
|---|
| 12644 | while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== SuspenseComponent) {
|
|---|
| 12645 | parent = parent.return;
|
|---|
| 12646 | }
|
|---|
| 12647 |
|
|---|
| 12648 | hydrationParentFiber = parent;
|
|---|
| 12649 | }
|
|---|
| 12650 |
|
|---|
| 12651 | function popHydrationState(fiber) {
|
|---|
| 12652 |
|
|---|
| 12653 | if (fiber !== hydrationParentFiber) {
|
|---|
| 12654 | // We're deeper than the current hydration context, inside an inserted
|
|---|
| 12655 | // tree.
|
|---|
| 12656 | return false;
|
|---|
| 12657 | }
|
|---|
| 12658 |
|
|---|
| 12659 | if (!isHydrating) {
|
|---|
| 12660 | // If we're not currently hydrating but we're in a hydration context, then
|
|---|
| 12661 | // we were an insertion and now need to pop up reenter hydration of our
|
|---|
| 12662 | // siblings.
|
|---|
| 12663 | popToNextHostParent(fiber);
|
|---|
| 12664 | isHydrating = true;
|
|---|
| 12665 | return false;
|
|---|
| 12666 | } // If we have any remaining hydratable nodes, we need to delete them now.
|
|---|
| 12667 | // We only do this deeper than head and body since they tend to have random
|
|---|
| 12668 | // other nodes in them. We also ignore components with pure text content in
|
|---|
| 12669 | // side of them. We also don't delete anything inside the root container.
|
|---|
| 12670 |
|
|---|
| 12671 |
|
|---|
| 12672 | if (fiber.tag !== HostRoot && (fiber.tag !== HostComponent || shouldDeleteUnhydratedTailInstances(fiber.type) && !shouldSetTextContent(fiber.type, fiber.memoizedProps))) {
|
|---|
| 12673 | var nextInstance = nextHydratableInstance;
|
|---|
| 12674 |
|
|---|
| 12675 | if (nextInstance) {
|
|---|
| 12676 | if (shouldClientRenderOnMismatch(fiber)) {
|
|---|
| 12677 | warnIfUnhydratedTailNodes(fiber);
|
|---|
| 12678 | throwOnHydrationMismatch();
|
|---|
| 12679 | } else {
|
|---|
| 12680 | while (nextInstance) {
|
|---|
| 12681 | deleteHydratableInstance(fiber, nextInstance);
|
|---|
| 12682 | nextInstance = getNextHydratableSibling(nextInstance);
|
|---|
| 12683 | }
|
|---|
| 12684 | }
|
|---|
| 12685 | }
|
|---|
| 12686 | }
|
|---|
| 12687 |
|
|---|
| 12688 | popToNextHostParent(fiber);
|
|---|
| 12689 |
|
|---|
| 12690 | if (fiber.tag === SuspenseComponent) {
|
|---|
| 12691 | nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber);
|
|---|
| 12692 | } else {
|
|---|
| 12693 | nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null;
|
|---|
| 12694 | }
|
|---|
| 12695 |
|
|---|
| 12696 | return true;
|
|---|
| 12697 | }
|
|---|
| 12698 |
|
|---|
| 12699 | function hasUnhydratedTailNodes() {
|
|---|
| 12700 | return isHydrating && nextHydratableInstance !== null;
|
|---|
| 12701 | }
|
|---|
| 12702 |
|
|---|
| 12703 | function warnIfUnhydratedTailNodes(fiber) {
|
|---|
| 12704 | var nextInstance = nextHydratableInstance;
|
|---|
| 12705 |
|
|---|
| 12706 | while (nextInstance) {
|
|---|
| 12707 | warnUnhydratedInstance(fiber, nextInstance);
|
|---|
| 12708 | nextInstance = getNextHydratableSibling(nextInstance);
|
|---|
| 12709 | }
|
|---|
| 12710 | }
|
|---|
| 12711 |
|
|---|
| 12712 | function resetHydrationState() {
|
|---|
| 12713 |
|
|---|
| 12714 | hydrationParentFiber = null;
|
|---|
| 12715 | nextHydratableInstance = null;
|
|---|
| 12716 | isHydrating = false;
|
|---|
| 12717 | didSuspendOrErrorDEV = false;
|
|---|
| 12718 | }
|
|---|
| 12719 |
|
|---|
| 12720 | function upgradeHydrationErrorsToRecoverable() {
|
|---|
| 12721 | if (hydrationErrors !== null) {
|
|---|
| 12722 | // Successfully completed a forced client render. The errors that occurred
|
|---|
| 12723 | // during the hydration attempt are now recovered. We will log them in
|
|---|
| 12724 | // commit phase, once the entire tree has finished.
|
|---|
| 12725 | queueRecoverableErrors(hydrationErrors);
|
|---|
| 12726 | hydrationErrors = null;
|
|---|
| 12727 | }
|
|---|
| 12728 | }
|
|---|
| 12729 |
|
|---|
| 12730 | function getIsHydrating() {
|
|---|
| 12731 | return isHydrating;
|
|---|
| 12732 | }
|
|---|
| 12733 |
|
|---|
| 12734 | function queueHydrationError(error) {
|
|---|
| 12735 | if (hydrationErrors === null) {
|
|---|
| 12736 | hydrationErrors = [error];
|
|---|
| 12737 | } else {
|
|---|
| 12738 | hydrationErrors.push(error);
|
|---|
| 12739 | }
|
|---|
| 12740 | }
|
|---|
| 12741 |
|
|---|
| 12742 | var ReactCurrentBatchConfig$1 = ReactSharedInternals.ReactCurrentBatchConfig;
|
|---|
| 12743 | var NoTransition = null;
|
|---|
| 12744 | function requestCurrentTransition() {
|
|---|
| 12745 | return ReactCurrentBatchConfig$1.transition;
|
|---|
| 12746 | }
|
|---|
| 12747 |
|
|---|
| 12748 | var ReactStrictModeWarnings = {
|
|---|
| 12749 | recordUnsafeLifecycleWarnings: function (fiber, instance) {},
|
|---|
| 12750 | flushPendingUnsafeLifecycleWarnings: function () {},
|
|---|
| 12751 | recordLegacyContextWarning: function (fiber, instance) {},
|
|---|
| 12752 | flushLegacyContextWarning: function () {},
|
|---|
| 12753 | discardPendingWarnings: function () {}
|
|---|
| 12754 | };
|
|---|
| 12755 |
|
|---|
| 12756 | {
|
|---|
| 12757 | var findStrictRoot = function (fiber) {
|
|---|
| 12758 | var maybeStrictRoot = null;
|
|---|
| 12759 | var node = fiber;
|
|---|
| 12760 |
|
|---|
| 12761 | while (node !== null) {
|
|---|
| 12762 | if (node.mode & StrictLegacyMode) {
|
|---|
| 12763 | maybeStrictRoot = node;
|
|---|
| 12764 | }
|
|---|
| 12765 |
|
|---|
| 12766 | node = node.return;
|
|---|
| 12767 | }
|
|---|
| 12768 |
|
|---|
| 12769 | return maybeStrictRoot;
|
|---|
| 12770 | };
|
|---|
| 12771 |
|
|---|
| 12772 | var setToSortedString = function (set) {
|
|---|
| 12773 | var array = [];
|
|---|
| 12774 | set.forEach(function (value) {
|
|---|
| 12775 | array.push(value);
|
|---|
| 12776 | });
|
|---|
| 12777 | return array.sort().join(', ');
|
|---|
| 12778 | };
|
|---|
| 12779 |
|
|---|
| 12780 | var pendingComponentWillMountWarnings = [];
|
|---|
| 12781 | var pendingUNSAFE_ComponentWillMountWarnings = [];
|
|---|
| 12782 | var pendingComponentWillReceivePropsWarnings = [];
|
|---|
| 12783 | var pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
|---|
| 12784 | var pendingComponentWillUpdateWarnings = [];
|
|---|
| 12785 | var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about.
|
|---|
| 12786 |
|
|---|
| 12787 | var didWarnAboutUnsafeLifecycles = new Set();
|
|---|
| 12788 |
|
|---|
| 12789 | ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (fiber, instance) {
|
|---|
| 12790 | // Dedupe strategy: Warn once per component.
|
|---|
| 12791 | if (didWarnAboutUnsafeLifecycles.has(fiber.type)) {
|
|---|
| 12792 | return;
|
|---|
| 12793 | }
|
|---|
| 12794 |
|
|---|
| 12795 | if (typeof instance.componentWillMount === 'function' && // Don't warn about react-lifecycles-compat polyfilled components.
|
|---|
| 12796 | instance.componentWillMount.__suppressDeprecationWarning !== true) {
|
|---|
| 12797 | pendingComponentWillMountWarnings.push(fiber);
|
|---|
| 12798 | }
|
|---|
| 12799 |
|
|---|
| 12800 | if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillMount === 'function') {
|
|---|
| 12801 | pendingUNSAFE_ComponentWillMountWarnings.push(fiber);
|
|---|
| 12802 | }
|
|---|
| 12803 |
|
|---|
| 12804 | if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
|
|---|
| 12805 | pendingComponentWillReceivePropsWarnings.push(fiber);
|
|---|
| 12806 | }
|
|---|
| 12807 |
|
|---|
| 12808 | if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
|---|
| 12809 | pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber);
|
|---|
| 12810 | }
|
|---|
| 12811 |
|
|---|
| 12812 | if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
|
|---|
| 12813 | pendingComponentWillUpdateWarnings.push(fiber);
|
|---|
| 12814 | }
|
|---|
| 12815 |
|
|---|
| 12816 | if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
|---|
| 12817 | pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber);
|
|---|
| 12818 | }
|
|---|
| 12819 | };
|
|---|
| 12820 |
|
|---|
| 12821 | ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () {
|
|---|
| 12822 | // We do an initial pass to gather component names
|
|---|
| 12823 | var componentWillMountUniqueNames = new Set();
|
|---|
| 12824 |
|
|---|
| 12825 | if (pendingComponentWillMountWarnings.length > 0) {
|
|---|
| 12826 | pendingComponentWillMountWarnings.forEach(function (fiber) {
|
|---|
| 12827 | componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12828 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12829 | });
|
|---|
| 12830 | pendingComponentWillMountWarnings = [];
|
|---|
| 12831 | }
|
|---|
| 12832 |
|
|---|
| 12833 | var UNSAFE_componentWillMountUniqueNames = new Set();
|
|---|
| 12834 |
|
|---|
| 12835 | if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) {
|
|---|
| 12836 | pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) {
|
|---|
| 12837 | UNSAFE_componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12838 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12839 | });
|
|---|
| 12840 | pendingUNSAFE_ComponentWillMountWarnings = [];
|
|---|
| 12841 | }
|
|---|
| 12842 |
|
|---|
| 12843 | var componentWillReceivePropsUniqueNames = new Set();
|
|---|
| 12844 |
|
|---|
| 12845 | if (pendingComponentWillReceivePropsWarnings.length > 0) {
|
|---|
| 12846 | pendingComponentWillReceivePropsWarnings.forEach(function (fiber) {
|
|---|
| 12847 | componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12848 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12849 | });
|
|---|
| 12850 | pendingComponentWillReceivePropsWarnings = [];
|
|---|
| 12851 | }
|
|---|
| 12852 |
|
|---|
| 12853 | var UNSAFE_componentWillReceivePropsUniqueNames = new Set();
|
|---|
| 12854 |
|
|---|
| 12855 | if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) {
|
|---|
| 12856 | pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) {
|
|---|
| 12857 | UNSAFE_componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12858 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12859 | });
|
|---|
| 12860 | pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
|---|
| 12861 | }
|
|---|
| 12862 |
|
|---|
| 12863 | var componentWillUpdateUniqueNames = new Set();
|
|---|
| 12864 |
|
|---|
| 12865 | if (pendingComponentWillUpdateWarnings.length > 0) {
|
|---|
| 12866 | pendingComponentWillUpdateWarnings.forEach(function (fiber) {
|
|---|
| 12867 | componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12868 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12869 | });
|
|---|
| 12870 | pendingComponentWillUpdateWarnings = [];
|
|---|
| 12871 | }
|
|---|
| 12872 |
|
|---|
| 12873 | var UNSAFE_componentWillUpdateUniqueNames = new Set();
|
|---|
| 12874 |
|
|---|
| 12875 | if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) {
|
|---|
| 12876 | pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) {
|
|---|
| 12877 | UNSAFE_componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12878 | didWarnAboutUnsafeLifecycles.add(fiber.type);
|
|---|
| 12879 | });
|
|---|
| 12880 | pendingUNSAFE_ComponentWillUpdateWarnings = [];
|
|---|
| 12881 | } // Finally, we flush all the warnings
|
|---|
| 12882 | // UNSAFE_ ones before the deprecated ones, since they'll be 'louder'
|
|---|
| 12883 |
|
|---|
| 12884 |
|
|---|
| 12885 | if (UNSAFE_componentWillMountUniqueNames.size > 0) {
|
|---|
| 12886 | var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames);
|
|---|
| 12887 |
|
|---|
| 12888 | error('Using UNSAFE_componentWillMount in strict mode is not recommended and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move code with side effects to componentDidMount, and set initial state in the constructor.\n' + '\nPlease update the following components: %s', sortedNames);
|
|---|
| 12889 | }
|
|---|
| 12890 |
|
|---|
| 12891 | if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) {
|
|---|
| 12892 | var _sortedNames = setToSortedString(UNSAFE_componentWillReceivePropsUniqueNames);
|
|---|
| 12893 |
|
|---|
| 12894 | error('Using UNSAFE_componentWillReceiveProps in strict mode is not recommended ' + 'and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + "* If you're updating state whenever props change, " + 'refactor your code to use memoization techniques or move it to ' + 'static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n' + '\nPlease update the following components: %s', _sortedNames);
|
|---|
| 12895 | }
|
|---|
| 12896 |
|
|---|
| 12897 | if (UNSAFE_componentWillUpdateUniqueNames.size > 0) {
|
|---|
| 12898 | var _sortedNames2 = setToSortedString(UNSAFE_componentWillUpdateUniqueNames);
|
|---|
| 12899 |
|
|---|
| 12900 | error('Using UNSAFE_componentWillUpdate in strict mode is not recommended ' + 'and may indicate bugs in your code. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + '\nPlease update the following components: %s', _sortedNames2);
|
|---|
| 12901 | }
|
|---|
| 12902 |
|
|---|
| 12903 | if (componentWillMountUniqueNames.size > 0) {
|
|---|
| 12904 | var _sortedNames3 = setToSortedString(componentWillMountUniqueNames);
|
|---|
| 12905 |
|
|---|
| 12906 | warn('componentWillMount has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move code with side effects to componentDidMount, and set initial state in the constructor.\n' + '* Rename componentWillMount to UNSAFE_componentWillMount to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames3);
|
|---|
| 12907 | }
|
|---|
| 12908 |
|
|---|
| 12909 | if (componentWillReceivePropsUniqueNames.size > 0) {
|
|---|
| 12910 | var _sortedNames4 = setToSortedString(componentWillReceivePropsUniqueNames);
|
|---|
| 12911 |
|
|---|
| 12912 | warn('componentWillReceiveProps has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + "* If you're updating state whenever props change, refactor your " + 'code to use memoization techniques or move it to ' + 'static getDerivedStateFromProps. Learn more at: https://reactjs.org/link/derived-state\n' + '* Rename componentWillReceiveProps to UNSAFE_componentWillReceiveProps to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames4);
|
|---|
| 12913 | }
|
|---|
| 12914 |
|
|---|
| 12915 | if (componentWillUpdateUniqueNames.size > 0) {
|
|---|
| 12916 | var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames);
|
|---|
| 12917 |
|
|---|
| 12918 | warn('componentWillUpdate has been renamed, and is not recommended for use. ' + 'See https://reactjs.org/link/unsafe-component-lifecycles for details.\n\n' + '* Move data fetching code or side effects to componentDidUpdate.\n' + '* Rename componentWillUpdate to UNSAFE_componentWillUpdate to suppress ' + 'this warning in non-strict mode. In React 18.x, only the UNSAFE_ name will work. ' + 'To rename all deprecated lifecycles to their new names, you can run ' + '`npx react-codemod rename-unsafe-lifecycles` in your project source folder.\n' + '\nPlease update the following components: %s', _sortedNames5);
|
|---|
| 12919 | }
|
|---|
| 12920 | };
|
|---|
| 12921 |
|
|---|
| 12922 | var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about.
|
|---|
| 12923 |
|
|---|
| 12924 | var didWarnAboutLegacyContext = new Set();
|
|---|
| 12925 |
|
|---|
| 12926 | ReactStrictModeWarnings.recordLegacyContextWarning = function (fiber, instance) {
|
|---|
| 12927 | var strictRoot = findStrictRoot(fiber);
|
|---|
| 12928 |
|
|---|
| 12929 | if (strictRoot === null) {
|
|---|
| 12930 | error('Expected to find a StrictMode component in a strict mode tree. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 12931 |
|
|---|
| 12932 | return;
|
|---|
| 12933 | } // Dedup strategy: Warn once per component.
|
|---|
| 12934 |
|
|---|
| 12935 |
|
|---|
| 12936 | if (didWarnAboutLegacyContext.has(fiber.type)) {
|
|---|
| 12937 | return;
|
|---|
| 12938 | }
|
|---|
| 12939 |
|
|---|
| 12940 | var warningsForRoot = pendingLegacyContextWarning.get(strictRoot);
|
|---|
| 12941 |
|
|---|
| 12942 | if (fiber.type.contextTypes != null || fiber.type.childContextTypes != null || instance !== null && typeof instance.getChildContext === 'function') {
|
|---|
| 12943 | if (warningsForRoot === undefined) {
|
|---|
| 12944 | warningsForRoot = [];
|
|---|
| 12945 | pendingLegacyContextWarning.set(strictRoot, warningsForRoot);
|
|---|
| 12946 | }
|
|---|
| 12947 |
|
|---|
| 12948 | warningsForRoot.push(fiber);
|
|---|
| 12949 | }
|
|---|
| 12950 | };
|
|---|
| 12951 |
|
|---|
| 12952 | ReactStrictModeWarnings.flushLegacyContextWarning = function () {
|
|---|
| 12953 | pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) {
|
|---|
| 12954 | if (fiberArray.length === 0) {
|
|---|
| 12955 | return;
|
|---|
| 12956 | }
|
|---|
| 12957 |
|
|---|
| 12958 | var firstFiber = fiberArray[0];
|
|---|
| 12959 | var uniqueNames = new Set();
|
|---|
| 12960 | fiberArray.forEach(function (fiber) {
|
|---|
| 12961 | uniqueNames.add(getComponentNameFromFiber(fiber) || 'Component');
|
|---|
| 12962 | didWarnAboutLegacyContext.add(fiber.type);
|
|---|
| 12963 | });
|
|---|
| 12964 | var sortedNames = setToSortedString(uniqueNames);
|
|---|
| 12965 |
|
|---|
| 12966 | try {
|
|---|
| 12967 | setCurrentFiber(firstFiber);
|
|---|
| 12968 |
|
|---|
| 12969 | error('Legacy context API has been detected within a strict-mode tree.' + '\n\nThe old API will be supported in all 16.x releases, but applications ' + 'using it should migrate to the new version.' + '\n\nPlease update the following components: %s' + '\n\nLearn more about this warning here: https://reactjs.org/link/legacy-context', sortedNames);
|
|---|
| 12970 | } finally {
|
|---|
| 12971 | resetCurrentFiber();
|
|---|
| 12972 | }
|
|---|
| 12973 | });
|
|---|
| 12974 | };
|
|---|
| 12975 |
|
|---|
| 12976 | ReactStrictModeWarnings.discardPendingWarnings = function () {
|
|---|
| 12977 | pendingComponentWillMountWarnings = [];
|
|---|
| 12978 | pendingUNSAFE_ComponentWillMountWarnings = [];
|
|---|
| 12979 | pendingComponentWillReceivePropsWarnings = [];
|
|---|
| 12980 | pendingUNSAFE_ComponentWillReceivePropsWarnings = [];
|
|---|
| 12981 | pendingComponentWillUpdateWarnings = [];
|
|---|
| 12982 | pendingUNSAFE_ComponentWillUpdateWarnings = [];
|
|---|
| 12983 | pendingLegacyContextWarning = new Map();
|
|---|
| 12984 | };
|
|---|
| 12985 | }
|
|---|
| 12986 |
|
|---|
| 12987 | var didWarnAboutMaps;
|
|---|
| 12988 | var didWarnAboutGenerators;
|
|---|
| 12989 | var didWarnAboutStringRefs;
|
|---|
| 12990 | var ownerHasKeyUseWarning;
|
|---|
| 12991 | var ownerHasFunctionTypeWarning;
|
|---|
| 12992 |
|
|---|
| 12993 | var warnForMissingKey = function (child, returnFiber) {};
|
|---|
| 12994 |
|
|---|
| 12995 | {
|
|---|
| 12996 | didWarnAboutMaps = false;
|
|---|
| 12997 | didWarnAboutGenerators = false;
|
|---|
| 12998 | didWarnAboutStringRefs = {};
|
|---|
| 12999 | /**
|
|---|
| 13000 | * Warn if there's no key explicitly set on dynamic arrays of children or
|
|---|
| 13001 | * object keys are not valid. This allows us to keep track of children between
|
|---|
| 13002 | * updates.
|
|---|
| 13003 | */
|
|---|
| 13004 |
|
|---|
| 13005 | ownerHasKeyUseWarning = {};
|
|---|
| 13006 | ownerHasFunctionTypeWarning = {};
|
|---|
| 13007 |
|
|---|
| 13008 | warnForMissingKey = function (child, returnFiber) {
|
|---|
| 13009 | if (child === null || typeof child !== 'object') {
|
|---|
| 13010 | return;
|
|---|
| 13011 | }
|
|---|
| 13012 |
|
|---|
| 13013 | if (!child._store || child._store.validated || child.key != null) {
|
|---|
| 13014 | return;
|
|---|
| 13015 | }
|
|---|
| 13016 |
|
|---|
| 13017 | if (typeof child._store !== 'object') {
|
|---|
| 13018 | throw new Error('React Component in warnForMissingKey should have a _store. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 13019 | }
|
|---|
| 13020 |
|
|---|
| 13021 | child._store.validated = true;
|
|---|
| 13022 | var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
|---|
| 13023 |
|
|---|
| 13024 | if (ownerHasKeyUseWarning[componentName]) {
|
|---|
| 13025 | return;
|
|---|
| 13026 | }
|
|---|
| 13027 |
|
|---|
| 13028 | ownerHasKeyUseWarning[componentName] = true;
|
|---|
| 13029 |
|
|---|
| 13030 | error('Each child in a list should have a unique ' + '"key" prop. See https://reactjs.org/link/warning-keys for ' + 'more information.');
|
|---|
| 13031 | };
|
|---|
| 13032 | }
|
|---|
| 13033 |
|
|---|
| 13034 | function isReactClass(type) {
|
|---|
| 13035 | return type.prototype && type.prototype.isReactComponent;
|
|---|
| 13036 | }
|
|---|
| 13037 |
|
|---|
| 13038 | function coerceRef(returnFiber, current, element) {
|
|---|
| 13039 | var mixedRef = element.ref;
|
|---|
| 13040 |
|
|---|
| 13041 | if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') {
|
|---|
| 13042 | {
|
|---|
| 13043 | // TODO: Clean this up once we turn on the string ref warning for
|
|---|
| 13044 | // everyone, because the strict mode case will no longer be relevant
|
|---|
| 13045 | if ((returnFiber.mode & StrictLegacyMode || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs
|
|---|
| 13046 | // because these cannot be automatically converted to an arrow function
|
|---|
| 13047 | // using a codemod. Therefore, we don't have to warn about string refs again.
|
|---|
| 13048 | !(element._owner && element._self && element._owner.stateNode !== element._self) && // Will already throw with "Function components cannot have string refs"
|
|---|
| 13049 | !(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs"
|
|---|
| 13050 | !(typeof element.type === 'function' && !isReactClass(element.type)) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set"
|
|---|
| 13051 | element._owner) {
|
|---|
| 13052 | var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
|---|
| 13053 |
|
|---|
| 13054 | if (!didWarnAboutStringRefs[componentName]) {
|
|---|
| 13055 | {
|
|---|
| 13056 | error('Component "%s" contains the string ref "%s". Support for string refs ' + 'will be removed in a future major release. We recommend using ' + 'useRef() or createRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref', componentName, mixedRef);
|
|---|
| 13057 | }
|
|---|
| 13058 |
|
|---|
| 13059 | didWarnAboutStringRefs[componentName] = true;
|
|---|
| 13060 | }
|
|---|
| 13061 | }
|
|---|
| 13062 | }
|
|---|
| 13063 |
|
|---|
| 13064 | if (element._owner) {
|
|---|
| 13065 | var owner = element._owner;
|
|---|
| 13066 | var inst;
|
|---|
| 13067 |
|
|---|
| 13068 | if (owner) {
|
|---|
| 13069 | var ownerFiber = owner;
|
|---|
| 13070 |
|
|---|
| 13071 | if (ownerFiber.tag !== ClassComponent) {
|
|---|
| 13072 | throw new Error('Function components cannot have string refs. ' + 'We recommend using useRef() instead. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-string-ref');
|
|---|
| 13073 | }
|
|---|
| 13074 |
|
|---|
| 13075 | inst = ownerFiber.stateNode;
|
|---|
| 13076 | }
|
|---|
| 13077 |
|
|---|
| 13078 | if (!inst) {
|
|---|
| 13079 | throw new Error("Missing owner for string ref " + mixedRef + ". This error is likely caused by a " + 'bug in React. Please file an issue.');
|
|---|
| 13080 | } // Assigning this to a const so Flow knows it won't change in the closure
|
|---|
| 13081 |
|
|---|
| 13082 |
|
|---|
| 13083 | var resolvedInst = inst;
|
|---|
| 13084 |
|
|---|
| 13085 | {
|
|---|
| 13086 | checkPropStringCoercion(mixedRef, 'ref');
|
|---|
| 13087 | }
|
|---|
| 13088 |
|
|---|
| 13089 | var stringRef = '' + mixedRef; // Check if previous string ref matches new string ref
|
|---|
| 13090 |
|
|---|
| 13091 | if (current !== null && current.ref !== null && typeof current.ref === 'function' && current.ref._stringRef === stringRef) {
|
|---|
| 13092 | return current.ref;
|
|---|
| 13093 | }
|
|---|
| 13094 |
|
|---|
| 13095 | var ref = function (value) {
|
|---|
| 13096 | var refs = resolvedInst.refs;
|
|---|
| 13097 |
|
|---|
| 13098 | if (value === null) {
|
|---|
| 13099 | delete refs[stringRef];
|
|---|
| 13100 | } else {
|
|---|
| 13101 | refs[stringRef] = value;
|
|---|
| 13102 | }
|
|---|
| 13103 | };
|
|---|
| 13104 |
|
|---|
| 13105 | ref._stringRef = stringRef;
|
|---|
| 13106 | return ref;
|
|---|
| 13107 | } else {
|
|---|
| 13108 | if (typeof mixedRef !== 'string') {
|
|---|
| 13109 | throw new Error('Expected ref to be a function, a string, an object returned by React.createRef(), or null.');
|
|---|
| 13110 | }
|
|---|
| 13111 |
|
|---|
| 13112 | if (!element._owner) {
|
|---|
| 13113 | throw new Error("Element ref was specified as a string (" + mixedRef + ") but no owner was set. This could happen for one of" + ' the following reasons:\n' + '1. You may be adding a ref to a function component\n' + "2. You may be adding a ref to a component that was not created inside a component's render method\n" + '3. You have multiple copies of React loaded\n' + 'See https://reactjs.org/link/refs-must-have-owner for more information.');
|
|---|
| 13114 | }
|
|---|
| 13115 | }
|
|---|
| 13116 | }
|
|---|
| 13117 |
|
|---|
| 13118 | return mixedRef;
|
|---|
| 13119 | }
|
|---|
| 13120 |
|
|---|
| 13121 | function throwOnInvalidObjectType(returnFiber, newChild) {
|
|---|
| 13122 | var childString = Object.prototype.toString.call(newChild);
|
|---|
| 13123 | throw new Error("Objects are not valid as a React child (found: " + (childString === '[object Object]' ? 'object with keys {' + Object.keys(newChild).join(', ') + '}' : childString) + "). " + 'If you meant to render a collection of children, use an array ' + 'instead.');
|
|---|
| 13124 | }
|
|---|
| 13125 |
|
|---|
| 13126 | function warnOnFunctionType(returnFiber) {
|
|---|
| 13127 | {
|
|---|
| 13128 | var componentName = getComponentNameFromFiber(returnFiber) || 'Component';
|
|---|
| 13129 |
|
|---|
| 13130 | if (ownerHasFunctionTypeWarning[componentName]) {
|
|---|
| 13131 | return;
|
|---|
| 13132 | }
|
|---|
| 13133 |
|
|---|
| 13134 | ownerHasFunctionTypeWarning[componentName] = true;
|
|---|
| 13135 |
|
|---|
| 13136 | error('Functions are not valid as a React child. This may happen if ' + 'you return a Component instead of <Component /> from render. ' + 'Or maybe you meant to call this function rather than return it.');
|
|---|
| 13137 | }
|
|---|
| 13138 | }
|
|---|
| 13139 |
|
|---|
| 13140 | function resolveLazy(lazyType) {
|
|---|
| 13141 | var payload = lazyType._payload;
|
|---|
| 13142 | var init = lazyType._init;
|
|---|
| 13143 | return init(payload);
|
|---|
| 13144 | } // This wrapper function exists because I expect to clone the code in each path
|
|---|
| 13145 | // to be able to optimize each path individually by branching early. This needs
|
|---|
| 13146 | // a compiler or we can do it manually. Helpers that don't need this branching
|
|---|
| 13147 | // live outside of this function.
|
|---|
| 13148 |
|
|---|
| 13149 |
|
|---|
| 13150 | function ChildReconciler(shouldTrackSideEffects) {
|
|---|
| 13151 | function deleteChild(returnFiber, childToDelete) {
|
|---|
| 13152 | if (!shouldTrackSideEffects) {
|
|---|
| 13153 | // Noop.
|
|---|
| 13154 | return;
|
|---|
| 13155 | }
|
|---|
| 13156 |
|
|---|
| 13157 | var deletions = returnFiber.deletions;
|
|---|
| 13158 |
|
|---|
| 13159 | if (deletions === null) {
|
|---|
| 13160 | returnFiber.deletions = [childToDelete];
|
|---|
| 13161 | returnFiber.flags |= ChildDeletion;
|
|---|
| 13162 | } else {
|
|---|
| 13163 | deletions.push(childToDelete);
|
|---|
| 13164 | }
|
|---|
| 13165 | }
|
|---|
| 13166 |
|
|---|
| 13167 | function deleteRemainingChildren(returnFiber, currentFirstChild) {
|
|---|
| 13168 | if (!shouldTrackSideEffects) {
|
|---|
| 13169 | // Noop.
|
|---|
| 13170 | return null;
|
|---|
| 13171 | } // TODO: For the shouldClone case, this could be micro-optimized a bit by
|
|---|
| 13172 | // assuming that after the first child we've already added everything.
|
|---|
| 13173 |
|
|---|
| 13174 |
|
|---|
| 13175 | var childToDelete = currentFirstChild;
|
|---|
| 13176 |
|
|---|
| 13177 | while (childToDelete !== null) {
|
|---|
| 13178 | deleteChild(returnFiber, childToDelete);
|
|---|
| 13179 | childToDelete = childToDelete.sibling;
|
|---|
| 13180 | }
|
|---|
| 13181 |
|
|---|
| 13182 | return null;
|
|---|
| 13183 | }
|
|---|
| 13184 |
|
|---|
| 13185 | function mapRemainingChildren(returnFiber, currentFirstChild) {
|
|---|
| 13186 | // Add the remaining children to a temporary map so that we can find them by
|
|---|
| 13187 | // keys quickly. Implicit (null) keys get added to this set with their index
|
|---|
| 13188 | // instead.
|
|---|
| 13189 | var existingChildren = new Map();
|
|---|
| 13190 | var existingChild = currentFirstChild;
|
|---|
| 13191 |
|
|---|
| 13192 | while (existingChild !== null) {
|
|---|
| 13193 | if (existingChild.key !== null) {
|
|---|
| 13194 | existingChildren.set(existingChild.key, existingChild);
|
|---|
| 13195 | } else {
|
|---|
| 13196 | existingChildren.set(existingChild.index, existingChild);
|
|---|
| 13197 | }
|
|---|
| 13198 |
|
|---|
| 13199 | existingChild = existingChild.sibling;
|
|---|
| 13200 | }
|
|---|
| 13201 |
|
|---|
| 13202 | return existingChildren;
|
|---|
| 13203 | }
|
|---|
| 13204 |
|
|---|
| 13205 | function useFiber(fiber, pendingProps) {
|
|---|
| 13206 | // We currently set sibling to null and index to 0 here because it is easy
|
|---|
| 13207 | // to forget to do before returning it. E.g. for the single child case.
|
|---|
| 13208 | var clone = createWorkInProgress(fiber, pendingProps);
|
|---|
| 13209 | clone.index = 0;
|
|---|
| 13210 | clone.sibling = null;
|
|---|
| 13211 | return clone;
|
|---|
| 13212 | }
|
|---|
| 13213 |
|
|---|
| 13214 | function placeChild(newFiber, lastPlacedIndex, newIndex) {
|
|---|
| 13215 | newFiber.index = newIndex;
|
|---|
| 13216 |
|
|---|
| 13217 | if (!shouldTrackSideEffects) {
|
|---|
| 13218 | // During hydration, the useId algorithm needs to know which fibers are
|
|---|
| 13219 | // part of a list of children (arrays, iterators).
|
|---|
| 13220 | newFiber.flags |= Forked;
|
|---|
| 13221 | return lastPlacedIndex;
|
|---|
| 13222 | }
|
|---|
| 13223 |
|
|---|
| 13224 | var current = newFiber.alternate;
|
|---|
| 13225 |
|
|---|
| 13226 | if (current !== null) {
|
|---|
| 13227 | var oldIndex = current.index;
|
|---|
| 13228 |
|
|---|
| 13229 | if (oldIndex < lastPlacedIndex) {
|
|---|
| 13230 | // This is a move.
|
|---|
| 13231 | newFiber.flags |= Placement;
|
|---|
| 13232 | return lastPlacedIndex;
|
|---|
| 13233 | } else {
|
|---|
| 13234 | // This item can stay in place.
|
|---|
| 13235 | return oldIndex;
|
|---|
| 13236 | }
|
|---|
| 13237 | } else {
|
|---|
| 13238 | // This is an insertion.
|
|---|
| 13239 | newFiber.flags |= Placement;
|
|---|
| 13240 | return lastPlacedIndex;
|
|---|
| 13241 | }
|
|---|
| 13242 | }
|
|---|
| 13243 |
|
|---|
| 13244 | function placeSingleChild(newFiber) {
|
|---|
| 13245 | // This is simpler for the single child case. We only need to do a
|
|---|
| 13246 | // placement for inserting new children.
|
|---|
| 13247 | if (shouldTrackSideEffects && newFiber.alternate === null) {
|
|---|
| 13248 | newFiber.flags |= Placement;
|
|---|
| 13249 | }
|
|---|
| 13250 |
|
|---|
| 13251 | return newFiber;
|
|---|
| 13252 | }
|
|---|
| 13253 |
|
|---|
| 13254 | function updateTextNode(returnFiber, current, textContent, lanes) {
|
|---|
| 13255 | if (current === null || current.tag !== HostText) {
|
|---|
| 13256 | // Insert
|
|---|
| 13257 | var created = createFiberFromText(textContent, returnFiber.mode, lanes);
|
|---|
| 13258 | created.return = returnFiber;
|
|---|
| 13259 | return created;
|
|---|
| 13260 | } else {
|
|---|
| 13261 | // Update
|
|---|
| 13262 | var existing = useFiber(current, textContent);
|
|---|
| 13263 | existing.return = returnFiber;
|
|---|
| 13264 | return existing;
|
|---|
| 13265 | }
|
|---|
| 13266 | }
|
|---|
| 13267 |
|
|---|
| 13268 | function updateElement(returnFiber, current, element, lanes) {
|
|---|
| 13269 | var elementType = element.type;
|
|---|
| 13270 |
|
|---|
| 13271 | if (elementType === REACT_FRAGMENT_TYPE) {
|
|---|
| 13272 | return updateFragment(returnFiber, current, element.props.children, lanes, element.key);
|
|---|
| 13273 | }
|
|---|
| 13274 |
|
|---|
| 13275 | if (current !== null) {
|
|---|
| 13276 | if (current.elementType === elementType || ( // Keep this check inline so it only runs on the false path:
|
|---|
| 13277 | isCompatibleFamilyForHotReloading(current, element) ) || // Lazy types should reconcile their resolved type.
|
|---|
| 13278 | // We need to do this after the Hot Reloading check above,
|
|---|
| 13279 | // because hot reloading has different semantics than prod because
|
|---|
| 13280 | // it doesn't resuspend. So we can't let the call below suspend.
|
|---|
| 13281 | typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === current.type) {
|
|---|
| 13282 | // Move based on index
|
|---|
| 13283 | var existing = useFiber(current, element.props);
|
|---|
| 13284 | existing.ref = coerceRef(returnFiber, current, element);
|
|---|
| 13285 | existing.return = returnFiber;
|
|---|
| 13286 |
|
|---|
| 13287 | {
|
|---|
| 13288 | existing._debugSource = element._source;
|
|---|
| 13289 | existing._debugOwner = element._owner;
|
|---|
| 13290 | }
|
|---|
| 13291 |
|
|---|
| 13292 | return existing;
|
|---|
| 13293 | }
|
|---|
| 13294 | } // Insert
|
|---|
| 13295 |
|
|---|
| 13296 |
|
|---|
| 13297 | var created = createFiberFromElement(element, returnFiber.mode, lanes);
|
|---|
| 13298 | created.ref = coerceRef(returnFiber, current, element);
|
|---|
| 13299 | created.return = returnFiber;
|
|---|
| 13300 | return created;
|
|---|
| 13301 | }
|
|---|
| 13302 |
|
|---|
| 13303 | function updatePortal(returnFiber, current, portal, lanes) {
|
|---|
| 13304 | if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) {
|
|---|
| 13305 | // Insert
|
|---|
| 13306 | var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
|
|---|
| 13307 | created.return = returnFiber;
|
|---|
| 13308 | return created;
|
|---|
| 13309 | } else {
|
|---|
| 13310 | // Update
|
|---|
| 13311 | var existing = useFiber(current, portal.children || []);
|
|---|
| 13312 | existing.return = returnFiber;
|
|---|
| 13313 | return existing;
|
|---|
| 13314 | }
|
|---|
| 13315 | }
|
|---|
| 13316 |
|
|---|
| 13317 | function updateFragment(returnFiber, current, fragment, lanes, key) {
|
|---|
| 13318 | if (current === null || current.tag !== Fragment) {
|
|---|
| 13319 | // Insert
|
|---|
| 13320 | var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key);
|
|---|
| 13321 | created.return = returnFiber;
|
|---|
| 13322 | return created;
|
|---|
| 13323 | } else {
|
|---|
| 13324 | // Update
|
|---|
| 13325 | var existing = useFiber(current, fragment);
|
|---|
| 13326 | existing.return = returnFiber;
|
|---|
| 13327 | return existing;
|
|---|
| 13328 | }
|
|---|
| 13329 | }
|
|---|
| 13330 |
|
|---|
| 13331 | function createChild(returnFiber, newChild, lanes) {
|
|---|
| 13332 | if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
|---|
| 13333 | // Text nodes don't have keys. If the previous node is implicitly keyed
|
|---|
| 13334 | // we can continue to replace it without aborting even if it is not a text
|
|---|
| 13335 | // node.
|
|---|
| 13336 | var created = createFiberFromText('' + newChild, returnFiber.mode, lanes);
|
|---|
| 13337 | created.return = returnFiber;
|
|---|
| 13338 | return created;
|
|---|
| 13339 | }
|
|---|
| 13340 |
|
|---|
| 13341 | if (typeof newChild === 'object' && newChild !== null) {
|
|---|
| 13342 | switch (newChild.$$typeof) {
|
|---|
| 13343 | case REACT_ELEMENT_TYPE:
|
|---|
| 13344 | {
|
|---|
| 13345 | var _created = createFiberFromElement(newChild, returnFiber.mode, lanes);
|
|---|
| 13346 |
|
|---|
| 13347 | _created.ref = coerceRef(returnFiber, null, newChild);
|
|---|
| 13348 | _created.return = returnFiber;
|
|---|
| 13349 | return _created;
|
|---|
| 13350 | }
|
|---|
| 13351 |
|
|---|
| 13352 | case REACT_PORTAL_TYPE:
|
|---|
| 13353 | {
|
|---|
| 13354 | var _created2 = createFiberFromPortal(newChild, returnFiber.mode, lanes);
|
|---|
| 13355 |
|
|---|
| 13356 | _created2.return = returnFiber;
|
|---|
| 13357 | return _created2;
|
|---|
| 13358 | }
|
|---|
| 13359 |
|
|---|
| 13360 | case REACT_LAZY_TYPE:
|
|---|
| 13361 | {
|
|---|
| 13362 | var payload = newChild._payload;
|
|---|
| 13363 | var init = newChild._init;
|
|---|
| 13364 | return createChild(returnFiber, init(payload), lanes);
|
|---|
| 13365 | }
|
|---|
| 13366 | }
|
|---|
| 13367 |
|
|---|
| 13368 | if (isArray(newChild) || getIteratorFn(newChild)) {
|
|---|
| 13369 | var _created3 = createFiberFromFragment(newChild, returnFiber.mode, lanes, null);
|
|---|
| 13370 |
|
|---|
| 13371 | _created3.return = returnFiber;
|
|---|
| 13372 | return _created3;
|
|---|
| 13373 | }
|
|---|
| 13374 |
|
|---|
| 13375 | throwOnInvalidObjectType(returnFiber, newChild);
|
|---|
| 13376 | }
|
|---|
| 13377 |
|
|---|
| 13378 | {
|
|---|
| 13379 | if (typeof newChild === 'function') {
|
|---|
| 13380 | warnOnFunctionType(returnFiber);
|
|---|
| 13381 | }
|
|---|
| 13382 | }
|
|---|
| 13383 |
|
|---|
| 13384 | return null;
|
|---|
| 13385 | }
|
|---|
| 13386 |
|
|---|
| 13387 | function updateSlot(returnFiber, oldFiber, newChild, lanes) {
|
|---|
| 13388 | // Update the fiber if the keys match, otherwise return null.
|
|---|
| 13389 | var key = oldFiber !== null ? oldFiber.key : null;
|
|---|
| 13390 |
|
|---|
| 13391 | if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
|---|
| 13392 | // Text nodes don't have keys. If the previous node is implicitly keyed
|
|---|
| 13393 | // we can continue to replace it without aborting even if it is not a text
|
|---|
| 13394 | // node.
|
|---|
| 13395 | if (key !== null) {
|
|---|
| 13396 | return null;
|
|---|
| 13397 | }
|
|---|
| 13398 |
|
|---|
| 13399 | return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes);
|
|---|
| 13400 | }
|
|---|
| 13401 |
|
|---|
| 13402 | if (typeof newChild === 'object' && newChild !== null) {
|
|---|
| 13403 | switch (newChild.$$typeof) {
|
|---|
| 13404 | case REACT_ELEMENT_TYPE:
|
|---|
| 13405 | {
|
|---|
| 13406 | if (newChild.key === key) {
|
|---|
| 13407 | return updateElement(returnFiber, oldFiber, newChild, lanes);
|
|---|
| 13408 | } else {
|
|---|
| 13409 | return null;
|
|---|
| 13410 | }
|
|---|
| 13411 | }
|
|---|
| 13412 |
|
|---|
| 13413 | case REACT_PORTAL_TYPE:
|
|---|
| 13414 | {
|
|---|
| 13415 | if (newChild.key === key) {
|
|---|
| 13416 | return updatePortal(returnFiber, oldFiber, newChild, lanes);
|
|---|
| 13417 | } else {
|
|---|
| 13418 | return null;
|
|---|
| 13419 | }
|
|---|
| 13420 | }
|
|---|
| 13421 |
|
|---|
| 13422 | case REACT_LAZY_TYPE:
|
|---|
| 13423 | {
|
|---|
| 13424 | var payload = newChild._payload;
|
|---|
| 13425 | var init = newChild._init;
|
|---|
| 13426 | return updateSlot(returnFiber, oldFiber, init(payload), lanes);
|
|---|
| 13427 | }
|
|---|
| 13428 | }
|
|---|
| 13429 |
|
|---|
| 13430 | if (isArray(newChild) || getIteratorFn(newChild)) {
|
|---|
| 13431 | if (key !== null) {
|
|---|
| 13432 | return null;
|
|---|
| 13433 | }
|
|---|
| 13434 |
|
|---|
| 13435 | return updateFragment(returnFiber, oldFiber, newChild, lanes, null);
|
|---|
| 13436 | }
|
|---|
| 13437 |
|
|---|
| 13438 | throwOnInvalidObjectType(returnFiber, newChild);
|
|---|
| 13439 | }
|
|---|
| 13440 |
|
|---|
| 13441 | {
|
|---|
| 13442 | if (typeof newChild === 'function') {
|
|---|
| 13443 | warnOnFunctionType(returnFiber);
|
|---|
| 13444 | }
|
|---|
| 13445 | }
|
|---|
| 13446 |
|
|---|
| 13447 | return null;
|
|---|
| 13448 | }
|
|---|
| 13449 |
|
|---|
| 13450 | function updateFromMap(existingChildren, returnFiber, newIdx, newChild, lanes) {
|
|---|
| 13451 | if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
|---|
| 13452 | // Text nodes don't have keys, so we neither have to check the old nor
|
|---|
| 13453 | // new node for the key. If both are text nodes, they match.
|
|---|
| 13454 | var matchedFiber = existingChildren.get(newIdx) || null;
|
|---|
| 13455 | return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes);
|
|---|
| 13456 | }
|
|---|
| 13457 |
|
|---|
| 13458 | if (typeof newChild === 'object' && newChild !== null) {
|
|---|
| 13459 | switch (newChild.$$typeof) {
|
|---|
| 13460 | case REACT_ELEMENT_TYPE:
|
|---|
| 13461 | {
|
|---|
| 13462 | var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
|---|
| 13463 |
|
|---|
| 13464 | return updateElement(returnFiber, _matchedFiber, newChild, lanes);
|
|---|
| 13465 | }
|
|---|
| 13466 |
|
|---|
| 13467 | case REACT_PORTAL_TYPE:
|
|---|
| 13468 | {
|
|---|
| 13469 | var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null;
|
|---|
| 13470 |
|
|---|
| 13471 | return updatePortal(returnFiber, _matchedFiber2, newChild, lanes);
|
|---|
| 13472 | }
|
|---|
| 13473 |
|
|---|
| 13474 | case REACT_LAZY_TYPE:
|
|---|
| 13475 | var payload = newChild._payload;
|
|---|
| 13476 | var init = newChild._init;
|
|---|
| 13477 | return updateFromMap(existingChildren, returnFiber, newIdx, init(payload), lanes);
|
|---|
| 13478 | }
|
|---|
| 13479 |
|
|---|
| 13480 | if (isArray(newChild) || getIteratorFn(newChild)) {
|
|---|
| 13481 | var _matchedFiber3 = existingChildren.get(newIdx) || null;
|
|---|
| 13482 |
|
|---|
| 13483 | return updateFragment(returnFiber, _matchedFiber3, newChild, lanes, null);
|
|---|
| 13484 | }
|
|---|
| 13485 |
|
|---|
| 13486 | throwOnInvalidObjectType(returnFiber, newChild);
|
|---|
| 13487 | }
|
|---|
| 13488 |
|
|---|
| 13489 | {
|
|---|
| 13490 | if (typeof newChild === 'function') {
|
|---|
| 13491 | warnOnFunctionType(returnFiber);
|
|---|
| 13492 | }
|
|---|
| 13493 | }
|
|---|
| 13494 |
|
|---|
| 13495 | return null;
|
|---|
| 13496 | }
|
|---|
| 13497 | /**
|
|---|
| 13498 | * Warns if there is a duplicate or missing key
|
|---|
| 13499 | */
|
|---|
| 13500 |
|
|---|
| 13501 |
|
|---|
| 13502 | function warnOnInvalidKey(child, knownKeys, returnFiber) {
|
|---|
| 13503 | {
|
|---|
| 13504 | if (typeof child !== 'object' || child === null) {
|
|---|
| 13505 | return knownKeys;
|
|---|
| 13506 | }
|
|---|
| 13507 |
|
|---|
| 13508 | switch (child.$$typeof) {
|
|---|
| 13509 | case REACT_ELEMENT_TYPE:
|
|---|
| 13510 | case REACT_PORTAL_TYPE:
|
|---|
| 13511 | warnForMissingKey(child, returnFiber);
|
|---|
| 13512 | var key = child.key;
|
|---|
| 13513 |
|
|---|
| 13514 | if (typeof key !== 'string') {
|
|---|
| 13515 | break;
|
|---|
| 13516 | }
|
|---|
| 13517 |
|
|---|
| 13518 | if (knownKeys === null) {
|
|---|
| 13519 | knownKeys = new Set();
|
|---|
| 13520 | knownKeys.add(key);
|
|---|
| 13521 | break;
|
|---|
| 13522 | }
|
|---|
| 13523 |
|
|---|
| 13524 | if (!knownKeys.has(key)) {
|
|---|
| 13525 | knownKeys.add(key);
|
|---|
| 13526 | break;
|
|---|
| 13527 | }
|
|---|
| 13528 |
|
|---|
| 13529 | error('Encountered two children with the same key, `%s`. ' + 'Keys should be unique so that components maintain their identity ' + 'across updates. Non-unique keys may cause children to be ' + 'duplicated and/or omitted — the behavior is unsupported and ' + 'could change in a future version.', key);
|
|---|
| 13530 |
|
|---|
| 13531 | break;
|
|---|
| 13532 |
|
|---|
| 13533 | case REACT_LAZY_TYPE:
|
|---|
| 13534 | var payload = child._payload;
|
|---|
| 13535 | var init = child._init;
|
|---|
| 13536 | warnOnInvalidKey(init(payload), knownKeys, returnFiber);
|
|---|
| 13537 | break;
|
|---|
| 13538 | }
|
|---|
| 13539 | }
|
|---|
| 13540 |
|
|---|
| 13541 | return knownKeys;
|
|---|
| 13542 | }
|
|---|
| 13543 |
|
|---|
| 13544 | function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) {
|
|---|
| 13545 | // This algorithm can't optimize by searching from both ends since we
|
|---|
| 13546 | // don't have backpointers on fibers. I'm trying to see how far we can get
|
|---|
| 13547 | // with that model. If it ends up not being worth the tradeoffs, we can
|
|---|
| 13548 | // add it later.
|
|---|
| 13549 | // Even with a two ended optimization, we'd want to optimize for the case
|
|---|
| 13550 | // where there are few changes and brute force the comparison instead of
|
|---|
| 13551 | // going for the Map. It'd like to explore hitting that path first in
|
|---|
| 13552 | // forward-only mode and only go for the Map once we notice that we need
|
|---|
| 13553 | // lots of look ahead. This doesn't handle reversal as well as two ended
|
|---|
| 13554 | // search but that's unusual. Besides, for the two ended optimization to
|
|---|
| 13555 | // work on Iterables, we'd need to copy the whole set.
|
|---|
| 13556 | // In this first iteration, we'll just live with hitting the bad case
|
|---|
| 13557 | // (adding everything to a Map) in for every insert/move.
|
|---|
| 13558 | // If you change this code, also update reconcileChildrenIterator() which
|
|---|
| 13559 | // uses the same algorithm.
|
|---|
| 13560 | {
|
|---|
| 13561 | // First, validate keys.
|
|---|
| 13562 | var knownKeys = null;
|
|---|
| 13563 |
|
|---|
| 13564 | for (var i = 0; i < newChildren.length; i++) {
|
|---|
| 13565 | var child = newChildren[i];
|
|---|
| 13566 | knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
|
|---|
| 13567 | }
|
|---|
| 13568 | }
|
|---|
| 13569 |
|
|---|
| 13570 | var resultingFirstChild = null;
|
|---|
| 13571 | var previousNewFiber = null;
|
|---|
| 13572 | var oldFiber = currentFirstChild;
|
|---|
| 13573 | var lastPlacedIndex = 0;
|
|---|
| 13574 | var newIdx = 0;
|
|---|
| 13575 | var nextOldFiber = null;
|
|---|
| 13576 |
|
|---|
| 13577 | for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) {
|
|---|
| 13578 | if (oldFiber.index > newIdx) {
|
|---|
| 13579 | nextOldFiber = oldFiber;
|
|---|
| 13580 | oldFiber = null;
|
|---|
| 13581 | } else {
|
|---|
| 13582 | nextOldFiber = oldFiber.sibling;
|
|---|
| 13583 | }
|
|---|
| 13584 |
|
|---|
| 13585 | var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes);
|
|---|
| 13586 |
|
|---|
| 13587 | if (newFiber === null) {
|
|---|
| 13588 | // TODO: This breaks on empty slots like null children. That's
|
|---|
| 13589 | // unfortunate because it triggers the slow path all the time. We need
|
|---|
| 13590 | // a better way to communicate whether this was a miss or null,
|
|---|
| 13591 | // boolean, undefined, etc.
|
|---|
| 13592 | if (oldFiber === null) {
|
|---|
| 13593 | oldFiber = nextOldFiber;
|
|---|
| 13594 | }
|
|---|
| 13595 |
|
|---|
| 13596 | break;
|
|---|
| 13597 | }
|
|---|
| 13598 |
|
|---|
| 13599 | if (shouldTrackSideEffects) {
|
|---|
| 13600 | if (oldFiber && newFiber.alternate === null) {
|
|---|
| 13601 | // We matched the slot, but we didn't reuse the existing fiber, so we
|
|---|
| 13602 | // need to delete the existing child.
|
|---|
| 13603 | deleteChild(returnFiber, oldFiber);
|
|---|
| 13604 | }
|
|---|
| 13605 | }
|
|---|
| 13606 |
|
|---|
| 13607 | lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
|
|---|
| 13608 |
|
|---|
| 13609 | if (previousNewFiber === null) {
|
|---|
| 13610 | // TODO: Move out of the loop. This only happens for the first run.
|
|---|
| 13611 | resultingFirstChild = newFiber;
|
|---|
| 13612 | } else {
|
|---|
| 13613 | // TODO: Defer siblings if we're not at the right index for this slot.
|
|---|
| 13614 | // I.e. if we had null values before, then we want to defer this
|
|---|
| 13615 | // for each null value. However, we also don't want to call updateSlot
|
|---|
| 13616 | // with the previous one.
|
|---|
| 13617 | previousNewFiber.sibling = newFiber;
|
|---|
| 13618 | }
|
|---|
| 13619 |
|
|---|
| 13620 | previousNewFiber = newFiber;
|
|---|
| 13621 | oldFiber = nextOldFiber;
|
|---|
| 13622 | }
|
|---|
| 13623 |
|
|---|
| 13624 | if (newIdx === newChildren.length) {
|
|---|
| 13625 | // We've reached the end of the new children. We can delete the rest.
|
|---|
| 13626 | deleteRemainingChildren(returnFiber, oldFiber);
|
|---|
| 13627 |
|
|---|
| 13628 | if (getIsHydrating()) {
|
|---|
| 13629 | var numberOfForks = newIdx;
|
|---|
| 13630 | pushTreeFork(returnFiber, numberOfForks);
|
|---|
| 13631 | }
|
|---|
| 13632 |
|
|---|
| 13633 | return resultingFirstChild;
|
|---|
| 13634 | }
|
|---|
| 13635 |
|
|---|
| 13636 | if (oldFiber === null) {
|
|---|
| 13637 | // If we don't have any more existing children we can choose a fast path
|
|---|
| 13638 | // since the rest will all be insertions.
|
|---|
| 13639 | for (; newIdx < newChildren.length; newIdx++) {
|
|---|
| 13640 | var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes);
|
|---|
| 13641 |
|
|---|
| 13642 | if (_newFiber === null) {
|
|---|
| 13643 | continue;
|
|---|
| 13644 | }
|
|---|
| 13645 |
|
|---|
| 13646 | lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx);
|
|---|
| 13647 |
|
|---|
| 13648 | if (previousNewFiber === null) {
|
|---|
| 13649 | // TODO: Move out of the loop. This only happens for the first run.
|
|---|
| 13650 | resultingFirstChild = _newFiber;
|
|---|
| 13651 | } else {
|
|---|
| 13652 | previousNewFiber.sibling = _newFiber;
|
|---|
| 13653 | }
|
|---|
| 13654 |
|
|---|
| 13655 | previousNewFiber = _newFiber;
|
|---|
| 13656 | }
|
|---|
| 13657 |
|
|---|
| 13658 | if (getIsHydrating()) {
|
|---|
| 13659 | var _numberOfForks = newIdx;
|
|---|
| 13660 | pushTreeFork(returnFiber, _numberOfForks);
|
|---|
| 13661 | }
|
|---|
| 13662 |
|
|---|
| 13663 | return resultingFirstChild;
|
|---|
| 13664 | } // Add all children to a key map for quick lookups.
|
|---|
| 13665 |
|
|---|
| 13666 |
|
|---|
| 13667 | var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
|
|---|
| 13668 |
|
|---|
| 13669 | for (; newIdx < newChildren.length; newIdx++) {
|
|---|
| 13670 | var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], lanes);
|
|---|
| 13671 |
|
|---|
| 13672 | if (_newFiber2 !== null) {
|
|---|
| 13673 | if (shouldTrackSideEffects) {
|
|---|
| 13674 | if (_newFiber2.alternate !== null) {
|
|---|
| 13675 | // The new fiber is a work in progress, but if there exists a
|
|---|
| 13676 | // current, that means that we reused the fiber. We need to delete
|
|---|
| 13677 | // it from the child list so that we don't add it to the deletion
|
|---|
| 13678 | // list.
|
|---|
| 13679 | existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key);
|
|---|
| 13680 | }
|
|---|
| 13681 | }
|
|---|
| 13682 |
|
|---|
| 13683 | lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx);
|
|---|
| 13684 |
|
|---|
| 13685 | if (previousNewFiber === null) {
|
|---|
| 13686 | resultingFirstChild = _newFiber2;
|
|---|
| 13687 | } else {
|
|---|
| 13688 | previousNewFiber.sibling = _newFiber2;
|
|---|
| 13689 | }
|
|---|
| 13690 |
|
|---|
| 13691 | previousNewFiber = _newFiber2;
|
|---|
| 13692 | }
|
|---|
| 13693 | }
|
|---|
| 13694 |
|
|---|
| 13695 | if (shouldTrackSideEffects) {
|
|---|
| 13696 | // Any existing children that weren't consumed above were deleted. We need
|
|---|
| 13697 | // to add them to the deletion list.
|
|---|
| 13698 | existingChildren.forEach(function (child) {
|
|---|
| 13699 | return deleteChild(returnFiber, child);
|
|---|
| 13700 | });
|
|---|
| 13701 | }
|
|---|
| 13702 |
|
|---|
| 13703 | if (getIsHydrating()) {
|
|---|
| 13704 | var _numberOfForks2 = newIdx;
|
|---|
| 13705 | pushTreeFork(returnFiber, _numberOfForks2);
|
|---|
| 13706 | }
|
|---|
| 13707 |
|
|---|
| 13708 | return resultingFirstChild;
|
|---|
| 13709 | }
|
|---|
| 13710 |
|
|---|
| 13711 | function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, lanes) {
|
|---|
| 13712 | // This is the same implementation as reconcileChildrenArray(),
|
|---|
| 13713 | // but using the iterator instead.
|
|---|
| 13714 | var iteratorFn = getIteratorFn(newChildrenIterable);
|
|---|
| 13715 |
|
|---|
| 13716 | if (typeof iteratorFn !== 'function') {
|
|---|
| 13717 | throw new Error('An object is not an iterable. This error is likely caused by a bug in ' + 'React. Please file an issue.');
|
|---|
| 13718 | }
|
|---|
| 13719 |
|
|---|
| 13720 | {
|
|---|
| 13721 | // We don't support rendering Generators because it's a mutation.
|
|---|
| 13722 | // See https://github.com/facebook/react/issues/12995
|
|---|
| 13723 | if (typeof Symbol === 'function' && // $FlowFixMe Flow doesn't know about toStringTag
|
|---|
| 13724 | newChildrenIterable[Symbol.toStringTag] === 'Generator') {
|
|---|
| 13725 | if (!didWarnAboutGenerators) {
|
|---|
| 13726 | error('Using Generators as children is unsupported and will likely yield ' + 'unexpected results because enumerating a generator mutates it. ' + 'You may convert it to an array with `Array.from()` or the ' + '`[...spread]` operator before rendering. Keep in mind ' + 'you might need to polyfill these features for older browsers.');
|
|---|
| 13727 | }
|
|---|
| 13728 |
|
|---|
| 13729 | didWarnAboutGenerators = true;
|
|---|
| 13730 | } // Warn about using Maps as children
|
|---|
| 13731 |
|
|---|
| 13732 |
|
|---|
| 13733 | if (newChildrenIterable.entries === iteratorFn) {
|
|---|
| 13734 | if (!didWarnAboutMaps) {
|
|---|
| 13735 | error('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.');
|
|---|
| 13736 | }
|
|---|
| 13737 |
|
|---|
| 13738 | didWarnAboutMaps = true;
|
|---|
| 13739 | } // First, validate keys.
|
|---|
| 13740 | // We'll get a different iterator later for the main pass.
|
|---|
| 13741 |
|
|---|
| 13742 |
|
|---|
| 13743 | var _newChildren = iteratorFn.call(newChildrenIterable);
|
|---|
| 13744 |
|
|---|
| 13745 | if (_newChildren) {
|
|---|
| 13746 | var knownKeys = null;
|
|---|
| 13747 |
|
|---|
| 13748 | var _step = _newChildren.next();
|
|---|
| 13749 |
|
|---|
| 13750 | for (; !_step.done; _step = _newChildren.next()) {
|
|---|
| 13751 | var child = _step.value;
|
|---|
| 13752 | knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber);
|
|---|
| 13753 | }
|
|---|
| 13754 | }
|
|---|
| 13755 | }
|
|---|
| 13756 |
|
|---|
| 13757 | var newChildren = iteratorFn.call(newChildrenIterable);
|
|---|
| 13758 |
|
|---|
| 13759 | if (newChildren == null) {
|
|---|
| 13760 | throw new Error('An iterable object provided no iterator.');
|
|---|
| 13761 | }
|
|---|
| 13762 |
|
|---|
| 13763 | var resultingFirstChild = null;
|
|---|
| 13764 | var previousNewFiber = null;
|
|---|
| 13765 | var oldFiber = currentFirstChild;
|
|---|
| 13766 | var lastPlacedIndex = 0;
|
|---|
| 13767 | var newIdx = 0;
|
|---|
| 13768 | var nextOldFiber = null;
|
|---|
| 13769 | var step = newChildren.next();
|
|---|
| 13770 |
|
|---|
| 13771 | for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) {
|
|---|
| 13772 | if (oldFiber.index > newIdx) {
|
|---|
| 13773 | nextOldFiber = oldFiber;
|
|---|
| 13774 | oldFiber = null;
|
|---|
| 13775 | } else {
|
|---|
| 13776 | nextOldFiber = oldFiber.sibling;
|
|---|
| 13777 | }
|
|---|
| 13778 |
|
|---|
| 13779 | var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes);
|
|---|
| 13780 |
|
|---|
| 13781 | if (newFiber === null) {
|
|---|
| 13782 | // TODO: This breaks on empty slots like null children. That's
|
|---|
| 13783 | // unfortunate because it triggers the slow path all the time. We need
|
|---|
| 13784 | // a better way to communicate whether this was a miss or null,
|
|---|
| 13785 | // boolean, undefined, etc.
|
|---|
| 13786 | if (oldFiber === null) {
|
|---|
| 13787 | oldFiber = nextOldFiber;
|
|---|
| 13788 | }
|
|---|
| 13789 |
|
|---|
| 13790 | break;
|
|---|
| 13791 | }
|
|---|
| 13792 |
|
|---|
| 13793 | if (shouldTrackSideEffects) {
|
|---|
| 13794 | if (oldFiber && newFiber.alternate === null) {
|
|---|
| 13795 | // We matched the slot, but we didn't reuse the existing fiber, so we
|
|---|
| 13796 | // need to delete the existing child.
|
|---|
| 13797 | deleteChild(returnFiber, oldFiber);
|
|---|
| 13798 | }
|
|---|
| 13799 | }
|
|---|
| 13800 |
|
|---|
| 13801 | lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx);
|
|---|
| 13802 |
|
|---|
| 13803 | if (previousNewFiber === null) {
|
|---|
| 13804 | // TODO: Move out of the loop. This only happens for the first run.
|
|---|
| 13805 | resultingFirstChild = newFiber;
|
|---|
| 13806 | } else {
|
|---|
| 13807 | // TODO: Defer siblings if we're not at the right index for this slot.
|
|---|
| 13808 | // I.e. if we had null values before, then we want to defer this
|
|---|
| 13809 | // for each null value. However, we also don't want to call updateSlot
|
|---|
| 13810 | // with the previous one.
|
|---|
| 13811 | previousNewFiber.sibling = newFiber;
|
|---|
| 13812 | }
|
|---|
| 13813 |
|
|---|
| 13814 | previousNewFiber = newFiber;
|
|---|
| 13815 | oldFiber = nextOldFiber;
|
|---|
| 13816 | }
|
|---|
| 13817 |
|
|---|
| 13818 | if (step.done) {
|
|---|
| 13819 | // We've reached the end of the new children. We can delete the rest.
|
|---|
| 13820 | deleteRemainingChildren(returnFiber, oldFiber);
|
|---|
| 13821 |
|
|---|
| 13822 | if (getIsHydrating()) {
|
|---|
| 13823 | var numberOfForks = newIdx;
|
|---|
| 13824 | pushTreeFork(returnFiber, numberOfForks);
|
|---|
| 13825 | }
|
|---|
| 13826 |
|
|---|
| 13827 | return resultingFirstChild;
|
|---|
| 13828 | }
|
|---|
| 13829 |
|
|---|
| 13830 | if (oldFiber === null) {
|
|---|
| 13831 | // If we don't have any more existing children we can choose a fast path
|
|---|
| 13832 | // since the rest will all be insertions.
|
|---|
| 13833 | for (; !step.done; newIdx++, step = newChildren.next()) {
|
|---|
| 13834 | var _newFiber3 = createChild(returnFiber, step.value, lanes);
|
|---|
| 13835 |
|
|---|
| 13836 | if (_newFiber3 === null) {
|
|---|
| 13837 | continue;
|
|---|
| 13838 | }
|
|---|
| 13839 |
|
|---|
| 13840 | lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx);
|
|---|
| 13841 |
|
|---|
| 13842 | if (previousNewFiber === null) {
|
|---|
| 13843 | // TODO: Move out of the loop. This only happens for the first run.
|
|---|
| 13844 | resultingFirstChild = _newFiber3;
|
|---|
| 13845 | } else {
|
|---|
| 13846 | previousNewFiber.sibling = _newFiber3;
|
|---|
| 13847 | }
|
|---|
| 13848 |
|
|---|
| 13849 | previousNewFiber = _newFiber3;
|
|---|
| 13850 | }
|
|---|
| 13851 |
|
|---|
| 13852 | if (getIsHydrating()) {
|
|---|
| 13853 | var _numberOfForks3 = newIdx;
|
|---|
| 13854 | pushTreeFork(returnFiber, _numberOfForks3);
|
|---|
| 13855 | }
|
|---|
| 13856 |
|
|---|
| 13857 | return resultingFirstChild;
|
|---|
| 13858 | } // Add all children to a key map for quick lookups.
|
|---|
| 13859 |
|
|---|
| 13860 |
|
|---|
| 13861 | var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves.
|
|---|
| 13862 |
|
|---|
| 13863 | for (; !step.done; newIdx++, step = newChildren.next()) {
|
|---|
| 13864 | var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, lanes);
|
|---|
| 13865 |
|
|---|
| 13866 | if (_newFiber4 !== null) {
|
|---|
| 13867 | if (shouldTrackSideEffects) {
|
|---|
| 13868 | if (_newFiber4.alternate !== null) {
|
|---|
| 13869 | // The new fiber is a work in progress, but if there exists a
|
|---|
| 13870 | // current, that means that we reused the fiber. We need to delete
|
|---|
| 13871 | // it from the child list so that we don't add it to the deletion
|
|---|
| 13872 | // list.
|
|---|
| 13873 | existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key);
|
|---|
| 13874 | }
|
|---|
| 13875 | }
|
|---|
| 13876 |
|
|---|
| 13877 | lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx);
|
|---|
| 13878 |
|
|---|
| 13879 | if (previousNewFiber === null) {
|
|---|
| 13880 | resultingFirstChild = _newFiber4;
|
|---|
| 13881 | } else {
|
|---|
| 13882 | previousNewFiber.sibling = _newFiber4;
|
|---|
| 13883 | }
|
|---|
| 13884 |
|
|---|
| 13885 | previousNewFiber = _newFiber4;
|
|---|
| 13886 | }
|
|---|
| 13887 | }
|
|---|
| 13888 |
|
|---|
| 13889 | if (shouldTrackSideEffects) {
|
|---|
| 13890 | // Any existing children that weren't consumed above were deleted. We need
|
|---|
| 13891 | // to add them to the deletion list.
|
|---|
| 13892 | existingChildren.forEach(function (child) {
|
|---|
| 13893 | return deleteChild(returnFiber, child);
|
|---|
| 13894 | });
|
|---|
| 13895 | }
|
|---|
| 13896 |
|
|---|
| 13897 | if (getIsHydrating()) {
|
|---|
| 13898 | var _numberOfForks4 = newIdx;
|
|---|
| 13899 | pushTreeFork(returnFiber, _numberOfForks4);
|
|---|
| 13900 | }
|
|---|
| 13901 |
|
|---|
| 13902 | return resultingFirstChild;
|
|---|
| 13903 | }
|
|---|
| 13904 |
|
|---|
| 13905 | function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, lanes) {
|
|---|
| 13906 | // There's no need to check for keys on text nodes since we don't have a
|
|---|
| 13907 | // way to define them.
|
|---|
| 13908 | if (currentFirstChild !== null && currentFirstChild.tag === HostText) {
|
|---|
| 13909 | // We already have an existing node so let's just update it and delete
|
|---|
| 13910 | // the rest.
|
|---|
| 13911 | deleteRemainingChildren(returnFiber, currentFirstChild.sibling);
|
|---|
| 13912 | var existing = useFiber(currentFirstChild, textContent);
|
|---|
| 13913 | existing.return = returnFiber;
|
|---|
| 13914 | return existing;
|
|---|
| 13915 | } // The existing first child is not a text node so we need to create one
|
|---|
| 13916 | // and delete the existing ones.
|
|---|
| 13917 |
|
|---|
| 13918 |
|
|---|
| 13919 | deleteRemainingChildren(returnFiber, currentFirstChild);
|
|---|
| 13920 | var created = createFiberFromText(textContent, returnFiber.mode, lanes);
|
|---|
| 13921 | created.return = returnFiber;
|
|---|
| 13922 | return created;
|
|---|
| 13923 | }
|
|---|
| 13924 |
|
|---|
| 13925 | function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) {
|
|---|
| 13926 | var key = element.key;
|
|---|
| 13927 | var child = currentFirstChild;
|
|---|
| 13928 |
|
|---|
| 13929 | while (child !== null) {
|
|---|
| 13930 | // TODO: If key === null and child.key === null, then this only applies to
|
|---|
| 13931 | // the first item in the list.
|
|---|
| 13932 | if (child.key === key) {
|
|---|
| 13933 | var elementType = element.type;
|
|---|
| 13934 |
|
|---|
| 13935 | if (elementType === REACT_FRAGMENT_TYPE) {
|
|---|
| 13936 | if (child.tag === Fragment) {
|
|---|
| 13937 | deleteRemainingChildren(returnFiber, child.sibling);
|
|---|
| 13938 | var existing = useFiber(child, element.props.children);
|
|---|
| 13939 | existing.return = returnFiber;
|
|---|
| 13940 |
|
|---|
| 13941 | {
|
|---|
| 13942 | existing._debugSource = element._source;
|
|---|
| 13943 | existing._debugOwner = element._owner;
|
|---|
| 13944 | }
|
|---|
| 13945 |
|
|---|
| 13946 | return existing;
|
|---|
| 13947 | }
|
|---|
| 13948 | } else {
|
|---|
| 13949 | if (child.elementType === elementType || ( // Keep this check inline so it only runs on the false path:
|
|---|
| 13950 | isCompatibleFamilyForHotReloading(child, element) ) || // Lazy types should reconcile their resolved type.
|
|---|
| 13951 | // We need to do this after the Hot Reloading check above,
|
|---|
| 13952 | // because hot reloading has different semantics than prod because
|
|---|
| 13953 | // it doesn't resuspend. So we can't let the call below suspend.
|
|---|
| 13954 | typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === child.type) {
|
|---|
| 13955 | deleteRemainingChildren(returnFiber, child.sibling);
|
|---|
| 13956 |
|
|---|
| 13957 | var _existing = useFiber(child, element.props);
|
|---|
| 13958 |
|
|---|
| 13959 | _existing.ref = coerceRef(returnFiber, child, element);
|
|---|
| 13960 | _existing.return = returnFiber;
|
|---|
| 13961 |
|
|---|
| 13962 | {
|
|---|
| 13963 | _existing._debugSource = element._source;
|
|---|
| 13964 | _existing._debugOwner = element._owner;
|
|---|
| 13965 | }
|
|---|
| 13966 |
|
|---|
| 13967 | return _existing;
|
|---|
| 13968 | }
|
|---|
| 13969 | } // Didn't match.
|
|---|
| 13970 |
|
|---|
| 13971 |
|
|---|
| 13972 | deleteRemainingChildren(returnFiber, child);
|
|---|
| 13973 | break;
|
|---|
| 13974 | } else {
|
|---|
| 13975 | deleteChild(returnFiber, child);
|
|---|
| 13976 | }
|
|---|
| 13977 |
|
|---|
| 13978 | child = child.sibling;
|
|---|
| 13979 | }
|
|---|
| 13980 |
|
|---|
| 13981 | if (element.type === REACT_FRAGMENT_TYPE) {
|
|---|
| 13982 | var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key);
|
|---|
| 13983 | created.return = returnFiber;
|
|---|
| 13984 | return created;
|
|---|
| 13985 | } else {
|
|---|
| 13986 | var _created4 = createFiberFromElement(element, returnFiber.mode, lanes);
|
|---|
| 13987 |
|
|---|
| 13988 | _created4.ref = coerceRef(returnFiber, currentFirstChild, element);
|
|---|
| 13989 | _created4.return = returnFiber;
|
|---|
| 13990 | return _created4;
|
|---|
| 13991 | }
|
|---|
| 13992 | }
|
|---|
| 13993 |
|
|---|
| 13994 | function reconcileSinglePortal(returnFiber, currentFirstChild, portal, lanes) {
|
|---|
| 13995 | var key = portal.key;
|
|---|
| 13996 | var child = currentFirstChild;
|
|---|
| 13997 |
|
|---|
| 13998 | while (child !== null) {
|
|---|
| 13999 | // TODO: If key === null and child.key === null, then this only applies to
|
|---|
| 14000 | // the first item in the list.
|
|---|
| 14001 | if (child.key === key) {
|
|---|
| 14002 | if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) {
|
|---|
| 14003 | deleteRemainingChildren(returnFiber, child.sibling);
|
|---|
| 14004 | var existing = useFiber(child, portal.children || []);
|
|---|
| 14005 | existing.return = returnFiber;
|
|---|
| 14006 | return existing;
|
|---|
| 14007 | } else {
|
|---|
| 14008 | deleteRemainingChildren(returnFiber, child);
|
|---|
| 14009 | break;
|
|---|
| 14010 | }
|
|---|
| 14011 | } else {
|
|---|
| 14012 | deleteChild(returnFiber, child);
|
|---|
| 14013 | }
|
|---|
| 14014 |
|
|---|
| 14015 | child = child.sibling;
|
|---|
| 14016 | }
|
|---|
| 14017 |
|
|---|
| 14018 | var created = createFiberFromPortal(portal, returnFiber.mode, lanes);
|
|---|
| 14019 | created.return = returnFiber;
|
|---|
| 14020 | return created;
|
|---|
| 14021 | } // This API will tag the children with the side-effect of the reconciliation
|
|---|
| 14022 | // itself. They will be added to the side-effect list as we pass through the
|
|---|
| 14023 | // children and the parent.
|
|---|
| 14024 |
|
|---|
| 14025 |
|
|---|
| 14026 | function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) {
|
|---|
| 14027 | // This function is not recursive.
|
|---|
| 14028 | // If the top level item is an array, we treat it as a set of children,
|
|---|
| 14029 | // not as a fragment. Nested arrays on the other hand will be treated as
|
|---|
| 14030 | // fragment nodes. Recursion happens at the normal flow.
|
|---|
| 14031 | // Handle top level unkeyed fragments as if they were arrays.
|
|---|
| 14032 | // This leads to an ambiguity between <>{[...]}</> and <>...</>.
|
|---|
| 14033 | // We treat the ambiguous cases above the same.
|
|---|
| 14034 | var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null;
|
|---|
| 14035 |
|
|---|
| 14036 | if (isUnkeyedTopLevelFragment) {
|
|---|
| 14037 | newChild = newChild.props.children;
|
|---|
| 14038 | } // Handle object types
|
|---|
| 14039 |
|
|---|
| 14040 |
|
|---|
| 14041 | if (typeof newChild === 'object' && newChild !== null) {
|
|---|
| 14042 | switch (newChild.$$typeof) {
|
|---|
| 14043 | case REACT_ELEMENT_TYPE:
|
|---|
| 14044 | return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes));
|
|---|
| 14045 |
|
|---|
| 14046 | case REACT_PORTAL_TYPE:
|
|---|
| 14047 | return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes));
|
|---|
| 14048 |
|
|---|
| 14049 | case REACT_LAZY_TYPE:
|
|---|
| 14050 | var payload = newChild._payload;
|
|---|
| 14051 | var init = newChild._init; // TODO: This function is supposed to be non-recursive.
|
|---|
| 14052 |
|
|---|
| 14053 | return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes);
|
|---|
| 14054 | }
|
|---|
| 14055 |
|
|---|
| 14056 | if (isArray(newChild)) {
|
|---|
| 14057 | return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes);
|
|---|
| 14058 | }
|
|---|
| 14059 |
|
|---|
| 14060 | if (getIteratorFn(newChild)) {
|
|---|
| 14061 | return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes);
|
|---|
| 14062 | }
|
|---|
| 14063 |
|
|---|
| 14064 | throwOnInvalidObjectType(returnFiber, newChild);
|
|---|
| 14065 | }
|
|---|
| 14066 |
|
|---|
| 14067 | if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') {
|
|---|
| 14068 | return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, lanes));
|
|---|
| 14069 | }
|
|---|
| 14070 |
|
|---|
| 14071 | {
|
|---|
| 14072 | if (typeof newChild === 'function') {
|
|---|
| 14073 | warnOnFunctionType(returnFiber);
|
|---|
| 14074 | }
|
|---|
| 14075 | } // Remaining cases are all treated as empty.
|
|---|
| 14076 |
|
|---|
| 14077 |
|
|---|
| 14078 | return deleteRemainingChildren(returnFiber, currentFirstChild);
|
|---|
| 14079 | }
|
|---|
| 14080 |
|
|---|
| 14081 | return reconcileChildFibers;
|
|---|
| 14082 | }
|
|---|
| 14083 |
|
|---|
| 14084 | var reconcileChildFibers = ChildReconciler(true);
|
|---|
| 14085 | var mountChildFibers = ChildReconciler(false);
|
|---|
| 14086 | function cloneChildFibers(current, workInProgress) {
|
|---|
| 14087 | if (current !== null && workInProgress.child !== current.child) {
|
|---|
| 14088 | throw new Error('Resuming work not yet implemented.');
|
|---|
| 14089 | }
|
|---|
| 14090 |
|
|---|
| 14091 | if (workInProgress.child === null) {
|
|---|
| 14092 | return;
|
|---|
| 14093 | }
|
|---|
| 14094 |
|
|---|
| 14095 | var currentChild = workInProgress.child;
|
|---|
| 14096 | var newChild = createWorkInProgress(currentChild, currentChild.pendingProps);
|
|---|
| 14097 | workInProgress.child = newChild;
|
|---|
| 14098 | newChild.return = workInProgress;
|
|---|
| 14099 |
|
|---|
| 14100 | while (currentChild.sibling !== null) {
|
|---|
| 14101 | currentChild = currentChild.sibling;
|
|---|
| 14102 | newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps);
|
|---|
| 14103 | newChild.return = workInProgress;
|
|---|
| 14104 | }
|
|---|
| 14105 |
|
|---|
| 14106 | newChild.sibling = null;
|
|---|
| 14107 | } // Reset a workInProgress child set to prepare it for a second pass.
|
|---|
| 14108 |
|
|---|
| 14109 | function resetChildFibers(workInProgress, lanes) {
|
|---|
| 14110 | var child = workInProgress.child;
|
|---|
| 14111 |
|
|---|
| 14112 | while (child !== null) {
|
|---|
| 14113 | resetWorkInProgress(child, lanes);
|
|---|
| 14114 | child = child.sibling;
|
|---|
| 14115 | }
|
|---|
| 14116 | }
|
|---|
| 14117 |
|
|---|
| 14118 | var valueCursor = createCursor(null);
|
|---|
| 14119 | var rendererSigil;
|
|---|
| 14120 |
|
|---|
| 14121 | {
|
|---|
| 14122 | // Use this to detect multiple renderers using the same context
|
|---|
| 14123 | rendererSigil = {};
|
|---|
| 14124 | }
|
|---|
| 14125 |
|
|---|
| 14126 | var currentlyRenderingFiber = null;
|
|---|
| 14127 | var lastContextDependency = null;
|
|---|
| 14128 | var lastFullyObservedContext = null;
|
|---|
| 14129 | var isDisallowedContextReadInDEV = false;
|
|---|
| 14130 | function resetContextDependencies() {
|
|---|
| 14131 | // This is called right before React yields execution, to ensure `readContext`
|
|---|
| 14132 | // cannot be called outside the render phase.
|
|---|
| 14133 | currentlyRenderingFiber = null;
|
|---|
| 14134 | lastContextDependency = null;
|
|---|
| 14135 | lastFullyObservedContext = null;
|
|---|
| 14136 |
|
|---|
| 14137 | {
|
|---|
| 14138 | isDisallowedContextReadInDEV = false;
|
|---|
| 14139 | }
|
|---|
| 14140 | }
|
|---|
| 14141 | function enterDisallowedContextReadInDEV() {
|
|---|
| 14142 | {
|
|---|
| 14143 | isDisallowedContextReadInDEV = true;
|
|---|
| 14144 | }
|
|---|
| 14145 | }
|
|---|
| 14146 | function exitDisallowedContextReadInDEV() {
|
|---|
| 14147 | {
|
|---|
| 14148 | isDisallowedContextReadInDEV = false;
|
|---|
| 14149 | }
|
|---|
| 14150 | }
|
|---|
| 14151 | function pushProvider(providerFiber, context, nextValue) {
|
|---|
| 14152 | {
|
|---|
| 14153 | push(valueCursor, context._currentValue, providerFiber);
|
|---|
| 14154 | context._currentValue = nextValue;
|
|---|
| 14155 |
|
|---|
| 14156 | {
|
|---|
| 14157 | if (context._currentRenderer !== undefined && context._currentRenderer !== null && context._currentRenderer !== rendererSigil) {
|
|---|
| 14158 | error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.');
|
|---|
| 14159 | }
|
|---|
| 14160 |
|
|---|
| 14161 | context._currentRenderer = rendererSigil;
|
|---|
| 14162 | }
|
|---|
| 14163 | }
|
|---|
| 14164 | }
|
|---|
| 14165 | function popProvider(context, providerFiber) {
|
|---|
| 14166 | var currentValue = valueCursor.current;
|
|---|
| 14167 | pop(valueCursor, providerFiber);
|
|---|
| 14168 |
|
|---|
| 14169 | {
|
|---|
| 14170 | {
|
|---|
| 14171 | context._currentValue = currentValue;
|
|---|
| 14172 | }
|
|---|
| 14173 | }
|
|---|
| 14174 | }
|
|---|
| 14175 | function scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) {
|
|---|
| 14176 | // Update the child lanes of all the ancestors, including the alternates.
|
|---|
| 14177 | var node = parent;
|
|---|
| 14178 |
|
|---|
| 14179 | while (node !== null) {
|
|---|
| 14180 | var alternate = node.alternate;
|
|---|
| 14181 |
|
|---|
| 14182 | if (!isSubsetOfLanes(node.childLanes, renderLanes)) {
|
|---|
| 14183 | node.childLanes = mergeLanes(node.childLanes, renderLanes);
|
|---|
| 14184 |
|
|---|
| 14185 | if (alternate !== null) {
|
|---|
| 14186 | alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
|
|---|
| 14187 | }
|
|---|
| 14188 | } else if (alternate !== null && !isSubsetOfLanes(alternate.childLanes, renderLanes)) {
|
|---|
| 14189 | alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes);
|
|---|
| 14190 | }
|
|---|
| 14191 |
|
|---|
| 14192 | if (node === propagationRoot) {
|
|---|
| 14193 | break;
|
|---|
| 14194 | }
|
|---|
| 14195 |
|
|---|
| 14196 | node = node.return;
|
|---|
| 14197 | }
|
|---|
| 14198 |
|
|---|
| 14199 | {
|
|---|
| 14200 | if (node !== propagationRoot) {
|
|---|
| 14201 | error('Expected to find the propagation root when scheduling context work. ' + 'This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 14202 | }
|
|---|
| 14203 | }
|
|---|
| 14204 | }
|
|---|
| 14205 | function propagateContextChange(workInProgress, context, renderLanes) {
|
|---|
| 14206 | {
|
|---|
| 14207 | propagateContextChange_eager(workInProgress, context, renderLanes);
|
|---|
| 14208 | }
|
|---|
| 14209 | }
|
|---|
| 14210 |
|
|---|
| 14211 | function propagateContextChange_eager(workInProgress, context, renderLanes) {
|
|---|
| 14212 |
|
|---|
| 14213 | var fiber = workInProgress.child;
|
|---|
| 14214 |
|
|---|
| 14215 | if (fiber !== null) {
|
|---|
| 14216 | // Set the return pointer of the child to the work-in-progress fiber.
|
|---|
| 14217 | fiber.return = workInProgress;
|
|---|
| 14218 | }
|
|---|
| 14219 |
|
|---|
| 14220 | while (fiber !== null) {
|
|---|
| 14221 | var nextFiber = void 0; // Visit this fiber.
|
|---|
| 14222 |
|
|---|
| 14223 | var list = fiber.dependencies;
|
|---|
| 14224 |
|
|---|
| 14225 | if (list !== null) {
|
|---|
| 14226 | nextFiber = fiber.child;
|
|---|
| 14227 | var dependency = list.firstContext;
|
|---|
| 14228 |
|
|---|
| 14229 | while (dependency !== null) {
|
|---|
| 14230 | // Check if the context matches.
|
|---|
| 14231 | if (dependency.context === context) {
|
|---|
| 14232 | // Match! Schedule an update on this fiber.
|
|---|
| 14233 | if (fiber.tag === ClassComponent) {
|
|---|
| 14234 | // Schedule a force update on the work-in-progress.
|
|---|
| 14235 | var lane = pickArbitraryLane(renderLanes);
|
|---|
| 14236 | var update = createUpdate(NoTimestamp, lane);
|
|---|
| 14237 | update.tag = ForceUpdate; // TODO: Because we don't have a work-in-progress, this will add the
|
|---|
| 14238 | // update to the current fiber, too, which means it will persist even if
|
|---|
| 14239 | // this render is thrown away. Since it's a race condition, not sure it's
|
|---|
| 14240 | // worth fixing.
|
|---|
| 14241 | // Inlined `enqueueUpdate` to remove interleaved update check
|
|---|
| 14242 |
|
|---|
| 14243 | var updateQueue = fiber.updateQueue;
|
|---|
| 14244 |
|
|---|
| 14245 | if (updateQueue === null) ; else {
|
|---|
| 14246 | var sharedQueue = updateQueue.shared;
|
|---|
| 14247 | var pending = sharedQueue.pending;
|
|---|
| 14248 |
|
|---|
| 14249 | if (pending === null) {
|
|---|
| 14250 | // This is the first update. Create a circular list.
|
|---|
| 14251 | update.next = update;
|
|---|
| 14252 | } else {
|
|---|
| 14253 | update.next = pending.next;
|
|---|
| 14254 | pending.next = update;
|
|---|
| 14255 | }
|
|---|
| 14256 |
|
|---|
| 14257 | sharedQueue.pending = update;
|
|---|
| 14258 | }
|
|---|
| 14259 | }
|
|---|
| 14260 |
|
|---|
| 14261 | fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
|
|---|
| 14262 | var alternate = fiber.alternate;
|
|---|
| 14263 |
|
|---|
| 14264 | if (alternate !== null) {
|
|---|
| 14265 | alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
|
|---|
| 14266 | }
|
|---|
| 14267 |
|
|---|
| 14268 | scheduleContextWorkOnParentPath(fiber.return, renderLanes, workInProgress); // Mark the updated lanes on the list, too.
|
|---|
| 14269 |
|
|---|
| 14270 | list.lanes = mergeLanes(list.lanes, renderLanes); // Since we already found a match, we can stop traversing the
|
|---|
| 14271 | // dependency list.
|
|---|
| 14272 |
|
|---|
| 14273 | break;
|
|---|
| 14274 | }
|
|---|
| 14275 |
|
|---|
| 14276 | dependency = dependency.next;
|
|---|
| 14277 | }
|
|---|
| 14278 | } else if (fiber.tag === ContextProvider) {
|
|---|
| 14279 | // Don't scan deeper if this is a matching provider
|
|---|
| 14280 | nextFiber = fiber.type === workInProgress.type ? null : fiber.child;
|
|---|
| 14281 | } else if (fiber.tag === DehydratedFragment) {
|
|---|
| 14282 | // If a dehydrated suspense boundary is in this subtree, we don't know
|
|---|
| 14283 | // if it will have any context consumers in it. The best we can do is
|
|---|
| 14284 | // mark it as having updates.
|
|---|
| 14285 | var parentSuspense = fiber.return;
|
|---|
| 14286 |
|
|---|
| 14287 | if (parentSuspense === null) {
|
|---|
| 14288 | throw new Error('We just came from a parent so we must have had a parent. This is a bug in React.');
|
|---|
| 14289 | }
|
|---|
| 14290 |
|
|---|
| 14291 | parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes);
|
|---|
| 14292 | var _alternate = parentSuspense.alternate;
|
|---|
| 14293 |
|
|---|
| 14294 | if (_alternate !== null) {
|
|---|
| 14295 | _alternate.lanes = mergeLanes(_alternate.lanes, renderLanes);
|
|---|
| 14296 | } // This is intentionally passing this fiber as the parent
|
|---|
| 14297 | // because we want to schedule this fiber as having work
|
|---|
| 14298 | // on its children. We'll use the childLanes on
|
|---|
| 14299 | // this fiber to indicate that a context has changed.
|
|---|
| 14300 |
|
|---|
| 14301 |
|
|---|
| 14302 | scheduleContextWorkOnParentPath(parentSuspense, renderLanes, workInProgress);
|
|---|
| 14303 | nextFiber = fiber.sibling;
|
|---|
| 14304 | } else {
|
|---|
| 14305 | // Traverse down.
|
|---|
| 14306 | nextFiber = fiber.child;
|
|---|
| 14307 | }
|
|---|
| 14308 |
|
|---|
| 14309 | if (nextFiber !== null) {
|
|---|
| 14310 | // Set the return pointer of the child to the work-in-progress fiber.
|
|---|
| 14311 | nextFiber.return = fiber;
|
|---|
| 14312 | } else {
|
|---|
| 14313 | // No child. Traverse to next sibling.
|
|---|
| 14314 | nextFiber = fiber;
|
|---|
| 14315 |
|
|---|
| 14316 | while (nextFiber !== null) {
|
|---|
| 14317 | if (nextFiber === workInProgress) {
|
|---|
| 14318 | // We're back to the root of this subtree. Exit.
|
|---|
| 14319 | nextFiber = null;
|
|---|
| 14320 | break;
|
|---|
| 14321 | }
|
|---|
| 14322 |
|
|---|
| 14323 | var sibling = nextFiber.sibling;
|
|---|
| 14324 |
|
|---|
| 14325 | if (sibling !== null) {
|
|---|
| 14326 | // Set the return pointer of the sibling to the work-in-progress fiber.
|
|---|
| 14327 | sibling.return = nextFiber.return;
|
|---|
| 14328 | nextFiber = sibling;
|
|---|
| 14329 | break;
|
|---|
| 14330 | } // No more siblings. Traverse up.
|
|---|
| 14331 |
|
|---|
| 14332 |
|
|---|
| 14333 | nextFiber = nextFiber.return;
|
|---|
| 14334 | }
|
|---|
| 14335 | }
|
|---|
| 14336 |
|
|---|
| 14337 | fiber = nextFiber;
|
|---|
| 14338 | }
|
|---|
| 14339 | }
|
|---|
| 14340 | function prepareToReadContext(workInProgress, renderLanes) {
|
|---|
| 14341 | currentlyRenderingFiber = workInProgress;
|
|---|
| 14342 | lastContextDependency = null;
|
|---|
| 14343 | lastFullyObservedContext = null;
|
|---|
| 14344 | var dependencies = workInProgress.dependencies;
|
|---|
| 14345 |
|
|---|
| 14346 | if (dependencies !== null) {
|
|---|
| 14347 | {
|
|---|
| 14348 | var firstContext = dependencies.firstContext;
|
|---|
| 14349 |
|
|---|
| 14350 | if (firstContext !== null) {
|
|---|
| 14351 | if (includesSomeLane(dependencies.lanes, renderLanes)) {
|
|---|
| 14352 | // Context list has a pending update. Mark that this fiber performed work.
|
|---|
| 14353 | markWorkInProgressReceivedUpdate();
|
|---|
| 14354 | } // Reset the work-in-progress list
|
|---|
| 14355 |
|
|---|
| 14356 |
|
|---|
| 14357 | dependencies.firstContext = null;
|
|---|
| 14358 | }
|
|---|
| 14359 | }
|
|---|
| 14360 | }
|
|---|
| 14361 | }
|
|---|
| 14362 | function readContext(context) {
|
|---|
| 14363 | {
|
|---|
| 14364 | // This warning would fire if you read context inside a Hook like useMemo.
|
|---|
| 14365 | // Unlike the class check below, it's not enforced in production for perf.
|
|---|
| 14366 | if (isDisallowedContextReadInDEV) {
|
|---|
| 14367 | error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
|---|
| 14368 | }
|
|---|
| 14369 | }
|
|---|
| 14370 |
|
|---|
| 14371 | var value = context._currentValue ;
|
|---|
| 14372 |
|
|---|
| 14373 | if (lastFullyObservedContext === context) ; else {
|
|---|
| 14374 | var contextItem = {
|
|---|
| 14375 | context: context,
|
|---|
| 14376 | memoizedValue: value,
|
|---|
| 14377 | next: null
|
|---|
| 14378 | };
|
|---|
| 14379 |
|
|---|
| 14380 | if (lastContextDependency === null) {
|
|---|
| 14381 | if (currentlyRenderingFiber === null) {
|
|---|
| 14382 | throw new Error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
|---|
| 14383 | } // This is the first dependency for this component. Create a new list.
|
|---|
| 14384 |
|
|---|
| 14385 |
|
|---|
| 14386 | lastContextDependency = contextItem;
|
|---|
| 14387 | currentlyRenderingFiber.dependencies = {
|
|---|
| 14388 | lanes: NoLanes,
|
|---|
| 14389 | firstContext: contextItem
|
|---|
| 14390 | };
|
|---|
| 14391 | } else {
|
|---|
| 14392 | // Append a new context item.
|
|---|
| 14393 | lastContextDependency = lastContextDependency.next = contextItem;
|
|---|
| 14394 | }
|
|---|
| 14395 | }
|
|---|
| 14396 |
|
|---|
| 14397 | return value;
|
|---|
| 14398 | }
|
|---|
| 14399 |
|
|---|
| 14400 | // render. When this render exits, either because it finishes or because it is
|
|---|
| 14401 | // interrupted, the interleaved updates will be transferred onto the main part
|
|---|
| 14402 | // of the queue.
|
|---|
| 14403 |
|
|---|
| 14404 | var concurrentQueues = null;
|
|---|
| 14405 | function pushConcurrentUpdateQueue(queue) {
|
|---|
| 14406 | if (concurrentQueues === null) {
|
|---|
| 14407 | concurrentQueues = [queue];
|
|---|
| 14408 | } else {
|
|---|
| 14409 | concurrentQueues.push(queue);
|
|---|
| 14410 | }
|
|---|
| 14411 | }
|
|---|
| 14412 | function finishQueueingConcurrentUpdates() {
|
|---|
| 14413 | // Transfer the interleaved updates onto the main queue. Each queue has a
|
|---|
| 14414 | // `pending` field and an `interleaved` field. When they are not null, they
|
|---|
| 14415 | // point to the last node in a circular linked list. We need to append the
|
|---|
| 14416 | // interleaved list to the end of the pending list by joining them into a
|
|---|
| 14417 | // single, circular list.
|
|---|
| 14418 | if (concurrentQueues !== null) {
|
|---|
| 14419 | for (var i = 0; i < concurrentQueues.length; i++) {
|
|---|
| 14420 | var queue = concurrentQueues[i];
|
|---|
| 14421 | var lastInterleavedUpdate = queue.interleaved;
|
|---|
| 14422 |
|
|---|
| 14423 | if (lastInterleavedUpdate !== null) {
|
|---|
| 14424 | queue.interleaved = null;
|
|---|
| 14425 | var firstInterleavedUpdate = lastInterleavedUpdate.next;
|
|---|
| 14426 | var lastPendingUpdate = queue.pending;
|
|---|
| 14427 |
|
|---|
| 14428 | if (lastPendingUpdate !== null) {
|
|---|
| 14429 | var firstPendingUpdate = lastPendingUpdate.next;
|
|---|
| 14430 | lastPendingUpdate.next = firstInterleavedUpdate;
|
|---|
| 14431 | lastInterleavedUpdate.next = firstPendingUpdate;
|
|---|
| 14432 | }
|
|---|
| 14433 |
|
|---|
| 14434 | queue.pending = lastInterleavedUpdate;
|
|---|
| 14435 | }
|
|---|
| 14436 | }
|
|---|
| 14437 |
|
|---|
| 14438 | concurrentQueues = null;
|
|---|
| 14439 | }
|
|---|
| 14440 | }
|
|---|
| 14441 | function enqueueConcurrentHookUpdate(fiber, queue, update, lane) {
|
|---|
| 14442 | var interleaved = queue.interleaved;
|
|---|
| 14443 |
|
|---|
| 14444 | if (interleaved === null) {
|
|---|
| 14445 | // This is the first update. Create a circular list.
|
|---|
| 14446 | update.next = update; // At the end of the current render, this queue's interleaved updates will
|
|---|
| 14447 | // be transferred to the pending queue.
|
|---|
| 14448 |
|
|---|
| 14449 | pushConcurrentUpdateQueue(queue);
|
|---|
| 14450 | } else {
|
|---|
| 14451 | update.next = interleaved.next;
|
|---|
| 14452 | interleaved.next = update;
|
|---|
| 14453 | }
|
|---|
| 14454 |
|
|---|
| 14455 | queue.interleaved = update;
|
|---|
| 14456 | return markUpdateLaneFromFiberToRoot(fiber, lane);
|
|---|
| 14457 | }
|
|---|
| 14458 | function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane) {
|
|---|
| 14459 | var interleaved = queue.interleaved;
|
|---|
| 14460 |
|
|---|
| 14461 | if (interleaved === null) {
|
|---|
| 14462 | // This is the first update. Create a circular list.
|
|---|
| 14463 | update.next = update; // At the end of the current render, this queue's interleaved updates will
|
|---|
| 14464 | // be transferred to the pending queue.
|
|---|
| 14465 |
|
|---|
| 14466 | pushConcurrentUpdateQueue(queue);
|
|---|
| 14467 | } else {
|
|---|
| 14468 | update.next = interleaved.next;
|
|---|
| 14469 | interleaved.next = update;
|
|---|
| 14470 | }
|
|---|
| 14471 |
|
|---|
| 14472 | queue.interleaved = update;
|
|---|
| 14473 | }
|
|---|
| 14474 | function enqueueConcurrentClassUpdate(fiber, queue, update, lane) {
|
|---|
| 14475 | var interleaved = queue.interleaved;
|
|---|
| 14476 |
|
|---|
| 14477 | if (interleaved === null) {
|
|---|
| 14478 | // This is the first update. Create a circular list.
|
|---|
| 14479 | update.next = update; // At the end of the current render, this queue's interleaved updates will
|
|---|
| 14480 | // be transferred to the pending queue.
|
|---|
| 14481 |
|
|---|
| 14482 | pushConcurrentUpdateQueue(queue);
|
|---|
| 14483 | } else {
|
|---|
| 14484 | update.next = interleaved.next;
|
|---|
| 14485 | interleaved.next = update;
|
|---|
| 14486 | }
|
|---|
| 14487 |
|
|---|
| 14488 | queue.interleaved = update;
|
|---|
| 14489 | return markUpdateLaneFromFiberToRoot(fiber, lane);
|
|---|
| 14490 | }
|
|---|
| 14491 | function enqueueConcurrentRenderForLane(fiber, lane) {
|
|---|
| 14492 | return markUpdateLaneFromFiberToRoot(fiber, lane);
|
|---|
| 14493 | } // Calling this function outside this module should only be done for backwards
|
|---|
| 14494 | // compatibility and should always be accompanied by a warning.
|
|---|
| 14495 |
|
|---|
| 14496 | var unsafe_markUpdateLaneFromFiberToRoot = markUpdateLaneFromFiberToRoot;
|
|---|
| 14497 |
|
|---|
| 14498 | function markUpdateLaneFromFiberToRoot(sourceFiber, lane) {
|
|---|
| 14499 | // Update the source fiber's lanes
|
|---|
| 14500 | sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane);
|
|---|
| 14501 | var alternate = sourceFiber.alternate;
|
|---|
| 14502 |
|
|---|
| 14503 | if (alternate !== null) {
|
|---|
| 14504 | alternate.lanes = mergeLanes(alternate.lanes, lane);
|
|---|
| 14505 | }
|
|---|
| 14506 |
|
|---|
| 14507 | {
|
|---|
| 14508 | if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) {
|
|---|
| 14509 | warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
|
|---|
| 14510 | }
|
|---|
| 14511 | } // Walk the parent path to the root and update the child lanes.
|
|---|
| 14512 |
|
|---|
| 14513 |
|
|---|
| 14514 | var node = sourceFiber;
|
|---|
| 14515 | var parent = sourceFiber.return;
|
|---|
| 14516 |
|
|---|
| 14517 | while (parent !== null) {
|
|---|
| 14518 | parent.childLanes = mergeLanes(parent.childLanes, lane);
|
|---|
| 14519 | alternate = parent.alternate;
|
|---|
| 14520 |
|
|---|
| 14521 | if (alternate !== null) {
|
|---|
| 14522 | alternate.childLanes = mergeLanes(alternate.childLanes, lane);
|
|---|
| 14523 | } else {
|
|---|
| 14524 | {
|
|---|
| 14525 | if ((parent.flags & (Placement | Hydrating)) !== NoFlags) {
|
|---|
| 14526 | warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber);
|
|---|
| 14527 | }
|
|---|
| 14528 | }
|
|---|
| 14529 | }
|
|---|
| 14530 |
|
|---|
| 14531 | node = parent;
|
|---|
| 14532 | parent = parent.return;
|
|---|
| 14533 | }
|
|---|
| 14534 |
|
|---|
| 14535 | if (node.tag === HostRoot) {
|
|---|
| 14536 | var root = node.stateNode;
|
|---|
| 14537 | return root;
|
|---|
| 14538 | } else {
|
|---|
| 14539 | return null;
|
|---|
| 14540 | }
|
|---|
| 14541 | }
|
|---|
| 14542 |
|
|---|
| 14543 | var UpdateState = 0;
|
|---|
| 14544 | var ReplaceState = 1;
|
|---|
| 14545 | var ForceUpdate = 2;
|
|---|
| 14546 | var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`.
|
|---|
| 14547 | // It should only be read right after calling `processUpdateQueue`, via
|
|---|
| 14548 | // `checkHasForceUpdateAfterProcessing`.
|
|---|
| 14549 |
|
|---|
| 14550 | var hasForceUpdate = false;
|
|---|
| 14551 | var didWarnUpdateInsideUpdate;
|
|---|
| 14552 | var currentlyProcessingQueue;
|
|---|
| 14553 |
|
|---|
| 14554 | {
|
|---|
| 14555 | didWarnUpdateInsideUpdate = false;
|
|---|
| 14556 | currentlyProcessingQueue = null;
|
|---|
| 14557 | }
|
|---|
| 14558 |
|
|---|
| 14559 | function initializeUpdateQueue(fiber) {
|
|---|
| 14560 | var queue = {
|
|---|
| 14561 | baseState: fiber.memoizedState,
|
|---|
| 14562 | firstBaseUpdate: null,
|
|---|
| 14563 | lastBaseUpdate: null,
|
|---|
| 14564 | shared: {
|
|---|
| 14565 | pending: null,
|
|---|
| 14566 | interleaved: null,
|
|---|
| 14567 | lanes: NoLanes
|
|---|
| 14568 | },
|
|---|
| 14569 | effects: null
|
|---|
| 14570 | };
|
|---|
| 14571 | fiber.updateQueue = queue;
|
|---|
| 14572 | }
|
|---|
| 14573 | function cloneUpdateQueue(current, workInProgress) {
|
|---|
| 14574 | // Clone the update queue from current. Unless it's already a clone.
|
|---|
| 14575 | var queue = workInProgress.updateQueue;
|
|---|
| 14576 | var currentQueue = current.updateQueue;
|
|---|
| 14577 |
|
|---|
| 14578 | if (queue === currentQueue) {
|
|---|
| 14579 | var clone = {
|
|---|
| 14580 | baseState: currentQueue.baseState,
|
|---|
| 14581 | firstBaseUpdate: currentQueue.firstBaseUpdate,
|
|---|
| 14582 | lastBaseUpdate: currentQueue.lastBaseUpdate,
|
|---|
| 14583 | shared: currentQueue.shared,
|
|---|
| 14584 | effects: currentQueue.effects
|
|---|
| 14585 | };
|
|---|
| 14586 | workInProgress.updateQueue = clone;
|
|---|
| 14587 | }
|
|---|
| 14588 | }
|
|---|
| 14589 | function createUpdate(eventTime, lane) {
|
|---|
| 14590 | var update = {
|
|---|
| 14591 | eventTime: eventTime,
|
|---|
| 14592 | lane: lane,
|
|---|
| 14593 | tag: UpdateState,
|
|---|
| 14594 | payload: null,
|
|---|
| 14595 | callback: null,
|
|---|
| 14596 | next: null
|
|---|
| 14597 | };
|
|---|
| 14598 | return update;
|
|---|
| 14599 | }
|
|---|
| 14600 | function enqueueUpdate(fiber, update, lane) {
|
|---|
| 14601 | var updateQueue = fiber.updateQueue;
|
|---|
| 14602 |
|
|---|
| 14603 | if (updateQueue === null) {
|
|---|
| 14604 | // Only occurs if the fiber has been unmounted.
|
|---|
| 14605 | return null;
|
|---|
| 14606 | }
|
|---|
| 14607 |
|
|---|
| 14608 | var sharedQueue = updateQueue.shared;
|
|---|
| 14609 |
|
|---|
| 14610 | {
|
|---|
| 14611 | if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) {
|
|---|
| 14612 | error('An update (setState, replaceState, or forceUpdate) was scheduled ' + 'from inside an update function. Update functions should be pure, ' + 'with zero side-effects. Consider using componentDidUpdate or a ' + 'callback.');
|
|---|
| 14613 |
|
|---|
| 14614 | didWarnUpdateInsideUpdate = true;
|
|---|
| 14615 | }
|
|---|
| 14616 | }
|
|---|
| 14617 |
|
|---|
| 14618 | if (isUnsafeClassRenderPhaseUpdate()) {
|
|---|
| 14619 | // This is an unsafe render phase update. Add directly to the update
|
|---|
| 14620 | // queue so we can process it immediately during the current render.
|
|---|
| 14621 | var pending = sharedQueue.pending;
|
|---|
| 14622 |
|
|---|
| 14623 | if (pending === null) {
|
|---|
| 14624 | // This is the first update. Create a circular list.
|
|---|
| 14625 | update.next = update;
|
|---|
| 14626 | } else {
|
|---|
| 14627 | update.next = pending.next;
|
|---|
| 14628 | pending.next = update;
|
|---|
| 14629 | }
|
|---|
| 14630 |
|
|---|
| 14631 | sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering
|
|---|
| 14632 | // this fiber. This is for backwards compatibility in the case where you
|
|---|
| 14633 | // update a different component during render phase than the one that is
|
|---|
| 14634 | // currently renderings (a pattern that is accompanied by a warning).
|
|---|
| 14635 |
|
|---|
| 14636 | return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane);
|
|---|
| 14637 | } else {
|
|---|
| 14638 | return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane);
|
|---|
| 14639 | }
|
|---|
| 14640 | }
|
|---|
| 14641 | function entangleTransitions(root, fiber, lane) {
|
|---|
| 14642 | var updateQueue = fiber.updateQueue;
|
|---|
| 14643 |
|
|---|
| 14644 | if (updateQueue === null) {
|
|---|
| 14645 | // Only occurs if the fiber has been unmounted.
|
|---|
| 14646 | return;
|
|---|
| 14647 | }
|
|---|
| 14648 |
|
|---|
| 14649 | var sharedQueue = updateQueue.shared;
|
|---|
| 14650 |
|
|---|
| 14651 | if (isTransitionLane(lane)) {
|
|---|
| 14652 | var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must
|
|---|
| 14653 | // have finished. We can remove them from the shared queue, which represents
|
|---|
| 14654 | // a superset of the actually pending lanes. In some cases we may entangle
|
|---|
| 14655 | // more than we need to, but that's OK. In fact it's worse if we *don't*
|
|---|
| 14656 | // entangle when we should.
|
|---|
| 14657 |
|
|---|
| 14658 | queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
|
|---|
| 14659 |
|
|---|
| 14660 | var newQueueLanes = mergeLanes(queueLanes, lane);
|
|---|
| 14661 | sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
|
|---|
| 14662 | // the lane finished since the last time we entangled it. So we need to
|
|---|
| 14663 | // entangle it again, just to be sure.
|
|---|
| 14664 |
|
|---|
| 14665 | markRootEntangled(root, newQueueLanes);
|
|---|
| 14666 | }
|
|---|
| 14667 | }
|
|---|
| 14668 | function enqueueCapturedUpdate(workInProgress, capturedUpdate) {
|
|---|
| 14669 | // Captured updates are updates that are thrown by a child during the render
|
|---|
| 14670 | // phase. They should be discarded if the render is aborted. Therefore,
|
|---|
| 14671 | // we should only put them on the work-in-progress queue, not the current one.
|
|---|
| 14672 | var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone.
|
|---|
| 14673 |
|
|---|
| 14674 | var current = workInProgress.alternate;
|
|---|
| 14675 |
|
|---|
| 14676 | if (current !== null) {
|
|---|
| 14677 | var currentQueue = current.updateQueue;
|
|---|
| 14678 |
|
|---|
| 14679 | if (queue === currentQueue) {
|
|---|
| 14680 | // The work-in-progress queue is the same as current. This happens when
|
|---|
| 14681 | // we bail out on a parent fiber that then captures an error thrown by
|
|---|
| 14682 | // a child. Since we want to append the update only to the work-in
|
|---|
| 14683 | // -progress queue, we need to clone the updates. We usually clone during
|
|---|
| 14684 | // processUpdateQueue, but that didn't happen in this case because we
|
|---|
| 14685 | // skipped over the parent when we bailed out.
|
|---|
| 14686 | var newFirst = null;
|
|---|
| 14687 | var newLast = null;
|
|---|
| 14688 | var firstBaseUpdate = queue.firstBaseUpdate;
|
|---|
| 14689 |
|
|---|
| 14690 | if (firstBaseUpdate !== null) {
|
|---|
| 14691 | // Loop through the updates and clone them.
|
|---|
| 14692 | var update = firstBaseUpdate;
|
|---|
| 14693 |
|
|---|
| 14694 | do {
|
|---|
| 14695 | var clone = {
|
|---|
| 14696 | eventTime: update.eventTime,
|
|---|
| 14697 | lane: update.lane,
|
|---|
| 14698 | tag: update.tag,
|
|---|
| 14699 | payload: update.payload,
|
|---|
| 14700 | callback: update.callback,
|
|---|
| 14701 | next: null
|
|---|
| 14702 | };
|
|---|
| 14703 |
|
|---|
| 14704 | if (newLast === null) {
|
|---|
| 14705 | newFirst = newLast = clone;
|
|---|
| 14706 | } else {
|
|---|
| 14707 | newLast.next = clone;
|
|---|
| 14708 | newLast = clone;
|
|---|
| 14709 | }
|
|---|
| 14710 |
|
|---|
| 14711 | update = update.next;
|
|---|
| 14712 | } while (update !== null); // Append the captured update the end of the cloned list.
|
|---|
| 14713 |
|
|---|
| 14714 |
|
|---|
| 14715 | if (newLast === null) {
|
|---|
| 14716 | newFirst = newLast = capturedUpdate;
|
|---|
| 14717 | } else {
|
|---|
| 14718 | newLast.next = capturedUpdate;
|
|---|
| 14719 | newLast = capturedUpdate;
|
|---|
| 14720 | }
|
|---|
| 14721 | } else {
|
|---|
| 14722 | // There are no base updates.
|
|---|
| 14723 | newFirst = newLast = capturedUpdate;
|
|---|
| 14724 | }
|
|---|
| 14725 |
|
|---|
| 14726 | queue = {
|
|---|
| 14727 | baseState: currentQueue.baseState,
|
|---|
| 14728 | firstBaseUpdate: newFirst,
|
|---|
| 14729 | lastBaseUpdate: newLast,
|
|---|
| 14730 | shared: currentQueue.shared,
|
|---|
| 14731 | effects: currentQueue.effects
|
|---|
| 14732 | };
|
|---|
| 14733 | workInProgress.updateQueue = queue;
|
|---|
| 14734 | return;
|
|---|
| 14735 | }
|
|---|
| 14736 | } // Append the update to the end of the list.
|
|---|
| 14737 |
|
|---|
| 14738 |
|
|---|
| 14739 | var lastBaseUpdate = queue.lastBaseUpdate;
|
|---|
| 14740 |
|
|---|
| 14741 | if (lastBaseUpdate === null) {
|
|---|
| 14742 | queue.firstBaseUpdate = capturedUpdate;
|
|---|
| 14743 | } else {
|
|---|
| 14744 | lastBaseUpdate.next = capturedUpdate;
|
|---|
| 14745 | }
|
|---|
| 14746 |
|
|---|
| 14747 | queue.lastBaseUpdate = capturedUpdate;
|
|---|
| 14748 | }
|
|---|
| 14749 |
|
|---|
| 14750 | function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) {
|
|---|
| 14751 | switch (update.tag) {
|
|---|
| 14752 | case ReplaceState:
|
|---|
| 14753 | {
|
|---|
| 14754 | var payload = update.payload;
|
|---|
| 14755 |
|
|---|
| 14756 | if (typeof payload === 'function') {
|
|---|
| 14757 | // Updater function
|
|---|
| 14758 | {
|
|---|
| 14759 | enterDisallowedContextReadInDEV();
|
|---|
| 14760 | }
|
|---|
| 14761 |
|
|---|
| 14762 | var nextState = payload.call(instance, prevState, nextProps);
|
|---|
| 14763 |
|
|---|
| 14764 | {
|
|---|
| 14765 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 14766 | setIsStrictModeForDevtools(true);
|
|---|
| 14767 |
|
|---|
| 14768 | try {
|
|---|
| 14769 | payload.call(instance, prevState, nextProps);
|
|---|
| 14770 | } finally {
|
|---|
| 14771 | setIsStrictModeForDevtools(false);
|
|---|
| 14772 | }
|
|---|
| 14773 | }
|
|---|
| 14774 |
|
|---|
| 14775 | exitDisallowedContextReadInDEV();
|
|---|
| 14776 | }
|
|---|
| 14777 |
|
|---|
| 14778 | return nextState;
|
|---|
| 14779 | } // State object
|
|---|
| 14780 |
|
|---|
| 14781 |
|
|---|
| 14782 | return payload;
|
|---|
| 14783 | }
|
|---|
| 14784 |
|
|---|
| 14785 | case CaptureUpdate:
|
|---|
| 14786 | {
|
|---|
| 14787 | workInProgress.flags = workInProgress.flags & ~ShouldCapture | DidCapture;
|
|---|
| 14788 | }
|
|---|
| 14789 | // Intentional fallthrough
|
|---|
| 14790 |
|
|---|
| 14791 | case UpdateState:
|
|---|
| 14792 | {
|
|---|
| 14793 | var _payload = update.payload;
|
|---|
| 14794 | var partialState;
|
|---|
| 14795 |
|
|---|
| 14796 | if (typeof _payload === 'function') {
|
|---|
| 14797 | // Updater function
|
|---|
| 14798 | {
|
|---|
| 14799 | enterDisallowedContextReadInDEV();
|
|---|
| 14800 | }
|
|---|
| 14801 |
|
|---|
| 14802 | partialState = _payload.call(instance, prevState, nextProps);
|
|---|
| 14803 |
|
|---|
| 14804 | {
|
|---|
| 14805 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 14806 | setIsStrictModeForDevtools(true);
|
|---|
| 14807 |
|
|---|
| 14808 | try {
|
|---|
| 14809 | _payload.call(instance, prevState, nextProps);
|
|---|
| 14810 | } finally {
|
|---|
| 14811 | setIsStrictModeForDevtools(false);
|
|---|
| 14812 | }
|
|---|
| 14813 | }
|
|---|
| 14814 |
|
|---|
| 14815 | exitDisallowedContextReadInDEV();
|
|---|
| 14816 | }
|
|---|
| 14817 | } else {
|
|---|
| 14818 | // Partial state object
|
|---|
| 14819 | partialState = _payload;
|
|---|
| 14820 | }
|
|---|
| 14821 |
|
|---|
| 14822 | if (partialState === null || partialState === undefined) {
|
|---|
| 14823 | // Null and undefined are treated as no-ops.
|
|---|
| 14824 | return prevState;
|
|---|
| 14825 | } // Merge the partial state and the previous state.
|
|---|
| 14826 |
|
|---|
| 14827 |
|
|---|
| 14828 | return assign({}, prevState, partialState);
|
|---|
| 14829 | }
|
|---|
| 14830 |
|
|---|
| 14831 | case ForceUpdate:
|
|---|
| 14832 | {
|
|---|
| 14833 | hasForceUpdate = true;
|
|---|
| 14834 | return prevState;
|
|---|
| 14835 | }
|
|---|
| 14836 | }
|
|---|
| 14837 |
|
|---|
| 14838 | return prevState;
|
|---|
| 14839 | }
|
|---|
| 14840 |
|
|---|
| 14841 | function processUpdateQueue(workInProgress, props, instance, renderLanes) {
|
|---|
| 14842 | // This is always non-null on a ClassComponent or HostRoot
|
|---|
| 14843 | var queue = workInProgress.updateQueue;
|
|---|
| 14844 | hasForceUpdate = false;
|
|---|
| 14845 |
|
|---|
| 14846 | {
|
|---|
| 14847 | currentlyProcessingQueue = queue.shared;
|
|---|
| 14848 | }
|
|---|
| 14849 |
|
|---|
| 14850 | var firstBaseUpdate = queue.firstBaseUpdate;
|
|---|
| 14851 | var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue.
|
|---|
| 14852 |
|
|---|
| 14853 | var pendingQueue = queue.shared.pending;
|
|---|
| 14854 |
|
|---|
| 14855 | if (pendingQueue !== null) {
|
|---|
| 14856 | queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first
|
|---|
| 14857 | // and last so that it's non-circular.
|
|---|
| 14858 |
|
|---|
| 14859 | var lastPendingUpdate = pendingQueue;
|
|---|
| 14860 | var firstPendingUpdate = lastPendingUpdate.next;
|
|---|
| 14861 | lastPendingUpdate.next = null; // Append pending updates to base queue
|
|---|
| 14862 |
|
|---|
| 14863 | if (lastBaseUpdate === null) {
|
|---|
| 14864 | firstBaseUpdate = firstPendingUpdate;
|
|---|
| 14865 | } else {
|
|---|
| 14866 | lastBaseUpdate.next = firstPendingUpdate;
|
|---|
| 14867 | }
|
|---|
| 14868 |
|
|---|
| 14869 | lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then
|
|---|
| 14870 | // we need to transfer the updates to that queue, too. Because the base
|
|---|
| 14871 | // queue is a singly-linked list with no cycles, we can append to both
|
|---|
| 14872 | // lists and take advantage of structural sharing.
|
|---|
| 14873 | // TODO: Pass `current` as argument
|
|---|
| 14874 |
|
|---|
| 14875 | var current = workInProgress.alternate;
|
|---|
| 14876 |
|
|---|
| 14877 | if (current !== null) {
|
|---|
| 14878 | // This is always non-null on a ClassComponent or HostRoot
|
|---|
| 14879 | var currentQueue = current.updateQueue;
|
|---|
| 14880 | var currentLastBaseUpdate = currentQueue.lastBaseUpdate;
|
|---|
| 14881 |
|
|---|
| 14882 | if (currentLastBaseUpdate !== lastBaseUpdate) {
|
|---|
| 14883 | if (currentLastBaseUpdate === null) {
|
|---|
| 14884 | currentQueue.firstBaseUpdate = firstPendingUpdate;
|
|---|
| 14885 | } else {
|
|---|
| 14886 | currentLastBaseUpdate.next = firstPendingUpdate;
|
|---|
| 14887 | }
|
|---|
| 14888 |
|
|---|
| 14889 | currentQueue.lastBaseUpdate = lastPendingUpdate;
|
|---|
| 14890 | }
|
|---|
| 14891 | }
|
|---|
| 14892 | } // These values may change as we process the queue.
|
|---|
| 14893 |
|
|---|
| 14894 |
|
|---|
| 14895 | if (firstBaseUpdate !== null) {
|
|---|
| 14896 | // Iterate through the list of updates to compute the result.
|
|---|
| 14897 | var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes
|
|---|
| 14898 | // from the original lanes.
|
|---|
| 14899 |
|
|---|
| 14900 | var newLanes = NoLanes;
|
|---|
| 14901 | var newBaseState = null;
|
|---|
| 14902 | var newFirstBaseUpdate = null;
|
|---|
| 14903 | var newLastBaseUpdate = null;
|
|---|
| 14904 | var update = firstBaseUpdate;
|
|---|
| 14905 |
|
|---|
| 14906 | do {
|
|---|
| 14907 | var updateLane = update.lane;
|
|---|
| 14908 | var updateEventTime = update.eventTime;
|
|---|
| 14909 |
|
|---|
| 14910 | if (!isSubsetOfLanes(renderLanes, updateLane)) {
|
|---|
| 14911 | // Priority is insufficient. Skip this update. If this is the first
|
|---|
| 14912 | // skipped update, the previous update/state is the new base
|
|---|
| 14913 | // update/state.
|
|---|
| 14914 | var clone = {
|
|---|
| 14915 | eventTime: updateEventTime,
|
|---|
| 14916 | lane: updateLane,
|
|---|
| 14917 | tag: update.tag,
|
|---|
| 14918 | payload: update.payload,
|
|---|
| 14919 | callback: update.callback,
|
|---|
| 14920 | next: null
|
|---|
| 14921 | };
|
|---|
| 14922 |
|
|---|
| 14923 | if (newLastBaseUpdate === null) {
|
|---|
| 14924 | newFirstBaseUpdate = newLastBaseUpdate = clone;
|
|---|
| 14925 | newBaseState = newState;
|
|---|
| 14926 | } else {
|
|---|
| 14927 | newLastBaseUpdate = newLastBaseUpdate.next = clone;
|
|---|
| 14928 | } // Update the remaining priority in the queue.
|
|---|
| 14929 |
|
|---|
| 14930 |
|
|---|
| 14931 | newLanes = mergeLanes(newLanes, updateLane);
|
|---|
| 14932 | } else {
|
|---|
| 14933 | // This update does have sufficient priority.
|
|---|
| 14934 | if (newLastBaseUpdate !== null) {
|
|---|
| 14935 | var _clone = {
|
|---|
| 14936 | eventTime: updateEventTime,
|
|---|
| 14937 | // This update is going to be committed so we never want uncommit
|
|---|
| 14938 | // it. Using NoLane works because 0 is a subset of all bitmasks, so
|
|---|
| 14939 | // this will never be skipped by the check above.
|
|---|
| 14940 | lane: NoLane,
|
|---|
| 14941 | tag: update.tag,
|
|---|
| 14942 | payload: update.payload,
|
|---|
| 14943 | callback: update.callback,
|
|---|
| 14944 | next: null
|
|---|
| 14945 | };
|
|---|
| 14946 | newLastBaseUpdate = newLastBaseUpdate.next = _clone;
|
|---|
| 14947 | } // Process this update.
|
|---|
| 14948 |
|
|---|
| 14949 |
|
|---|
| 14950 | newState = getStateFromUpdate(workInProgress, queue, update, newState, props, instance);
|
|---|
| 14951 | var callback = update.callback;
|
|---|
| 14952 |
|
|---|
| 14953 | if (callback !== null && // If the update was already committed, we should not queue its
|
|---|
| 14954 | // callback again.
|
|---|
| 14955 | update.lane !== NoLane) {
|
|---|
| 14956 | workInProgress.flags |= Callback;
|
|---|
| 14957 | var effects = queue.effects;
|
|---|
| 14958 |
|
|---|
| 14959 | if (effects === null) {
|
|---|
| 14960 | queue.effects = [update];
|
|---|
| 14961 | } else {
|
|---|
| 14962 | effects.push(update);
|
|---|
| 14963 | }
|
|---|
| 14964 | }
|
|---|
| 14965 | }
|
|---|
| 14966 |
|
|---|
| 14967 | update = update.next;
|
|---|
| 14968 |
|
|---|
| 14969 | if (update === null) {
|
|---|
| 14970 | pendingQueue = queue.shared.pending;
|
|---|
| 14971 |
|
|---|
| 14972 | if (pendingQueue === null) {
|
|---|
| 14973 | break;
|
|---|
| 14974 | } else {
|
|---|
| 14975 | // An update was scheduled from inside a reducer. Add the new
|
|---|
| 14976 | // pending updates to the end of the list and keep processing.
|
|---|
| 14977 | var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we
|
|---|
| 14978 | // unravel them when transferring them to the base queue.
|
|---|
| 14979 |
|
|---|
| 14980 | var _firstPendingUpdate = _lastPendingUpdate.next;
|
|---|
| 14981 | _lastPendingUpdate.next = null;
|
|---|
| 14982 | update = _firstPendingUpdate;
|
|---|
| 14983 | queue.lastBaseUpdate = _lastPendingUpdate;
|
|---|
| 14984 | queue.shared.pending = null;
|
|---|
| 14985 | }
|
|---|
| 14986 | }
|
|---|
| 14987 | } while (true);
|
|---|
| 14988 |
|
|---|
| 14989 | if (newLastBaseUpdate === null) {
|
|---|
| 14990 | newBaseState = newState;
|
|---|
| 14991 | }
|
|---|
| 14992 |
|
|---|
| 14993 | queue.baseState = newBaseState;
|
|---|
| 14994 | queue.firstBaseUpdate = newFirstBaseUpdate;
|
|---|
| 14995 | queue.lastBaseUpdate = newLastBaseUpdate; // Interleaved updates are stored on a separate queue. We aren't going to
|
|---|
| 14996 | // process them during this render, but we do need to track which lanes
|
|---|
| 14997 | // are remaining.
|
|---|
| 14998 |
|
|---|
| 14999 | var lastInterleaved = queue.shared.interleaved;
|
|---|
| 15000 |
|
|---|
| 15001 | if (lastInterleaved !== null) {
|
|---|
| 15002 | var interleaved = lastInterleaved;
|
|---|
| 15003 |
|
|---|
| 15004 | do {
|
|---|
| 15005 | newLanes = mergeLanes(newLanes, interleaved.lane);
|
|---|
| 15006 | interleaved = interleaved.next;
|
|---|
| 15007 | } while (interleaved !== lastInterleaved);
|
|---|
| 15008 | } else if (firstBaseUpdate === null) {
|
|---|
| 15009 | // `queue.lanes` is used for entangling transitions. We can set it back to
|
|---|
| 15010 | // zero once the queue is empty.
|
|---|
| 15011 | queue.shared.lanes = NoLanes;
|
|---|
| 15012 | } // Set the remaining expiration time to be whatever is remaining in the queue.
|
|---|
| 15013 | // This should be fine because the only two other things that contribute to
|
|---|
| 15014 | // expiration time are props and context. We're already in the middle of the
|
|---|
| 15015 | // begin phase by the time we start processing the queue, so we've already
|
|---|
| 15016 | // dealt with the props. Context in components that specify
|
|---|
| 15017 | // shouldComponentUpdate is tricky; but we'll have to account for
|
|---|
| 15018 | // that regardless.
|
|---|
| 15019 |
|
|---|
| 15020 |
|
|---|
| 15021 | markSkippedUpdateLanes(newLanes);
|
|---|
| 15022 | workInProgress.lanes = newLanes;
|
|---|
| 15023 | workInProgress.memoizedState = newState;
|
|---|
| 15024 | }
|
|---|
| 15025 |
|
|---|
| 15026 | {
|
|---|
| 15027 | currentlyProcessingQueue = null;
|
|---|
| 15028 | }
|
|---|
| 15029 | }
|
|---|
| 15030 |
|
|---|
| 15031 | function callCallback(callback, context) {
|
|---|
| 15032 | if (typeof callback !== 'function') {
|
|---|
| 15033 | throw new Error('Invalid argument passed as callback. Expected a function. Instead ' + ("received: " + callback));
|
|---|
| 15034 | }
|
|---|
| 15035 |
|
|---|
| 15036 | callback.call(context);
|
|---|
| 15037 | }
|
|---|
| 15038 |
|
|---|
| 15039 | function resetHasForceUpdateBeforeProcessing() {
|
|---|
| 15040 | hasForceUpdate = false;
|
|---|
| 15041 | }
|
|---|
| 15042 | function checkHasForceUpdateAfterProcessing() {
|
|---|
| 15043 | return hasForceUpdate;
|
|---|
| 15044 | }
|
|---|
| 15045 | function commitUpdateQueue(finishedWork, finishedQueue, instance) {
|
|---|
| 15046 | // Commit the effects
|
|---|
| 15047 | var effects = finishedQueue.effects;
|
|---|
| 15048 | finishedQueue.effects = null;
|
|---|
| 15049 |
|
|---|
| 15050 | if (effects !== null) {
|
|---|
| 15051 | for (var i = 0; i < effects.length; i++) {
|
|---|
| 15052 | var effect = effects[i];
|
|---|
| 15053 | var callback = effect.callback;
|
|---|
| 15054 |
|
|---|
| 15055 | if (callback !== null) {
|
|---|
| 15056 | effect.callback = null;
|
|---|
| 15057 | callCallback(callback, instance);
|
|---|
| 15058 | }
|
|---|
| 15059 | }
|
|---|
| 15060 | }
|
|---|
| 15061 | }
|
|---|
| 15062 |
|
|---|
| 15063 | var NO_CONTEXT = {};
|
|---|
| 15064 | var contextStackCursor$1 = createCursor(NO_CONTEXT);
|
|---|
| 15065 | var contextFiberStackCursor = createCursor(NO_CONTEXT);
|
|---|
| 15066 | var rootInstanceStackCursor = createCursor(NO_CONTEXT);
|
|---|
| 15067 |
|
|---|
| 15068 | function requiredContext(c) {
|
|---|
| 15069 | if (c === NO_CONTEXT) {
|
|---|
| 15070 | throw new Error('Expected host context to exist. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
|---|
| 15071 | }
|
|---|
| 15072 |
|
|---|
| 15073 | return c;
|
|---|
| 15074 | }
|
|---|
| 15075 |
|
|---|
| 15076 | function getRootHostContainer() {
|
|---|
| 15077 | var rootInstance = requiredContext(rootInstanceStackCursor.current);
|
|---|
| 15078 | return rootInstance;
|
|---|
| 15079 | }
|
|---|
| 15080 |
|
|---|
| 15081 | function pushHostContainer(fiber, nextRootInstance) {
|
|---|
| 15082 | // Push current root instance onto the stack;
|
|---|
| 15083 | // This allows us to reset root when portals are popped.
|
|---|
| 15084 | push(rootInstanceStackCursor, nextRootInstance, fiber); // Track the context and the Fiber that provided it.
|
|---|
| 15085 | // This enables us to pop only Fibers that provide unique contexts.
|
|---|
| 15086 |
|
|---|
| 15087 | push(contextFiberStackCursor, fiber, fiber); // Finally, we need to push the host context to the stack.
|
|---|
| 15088 | // However, we can't just call getRootHostContext() and push it because
|
|---|
| 15089 | // we'd have a different number of entries on the stack depending on
|
|---|
| 15090 | // whether getRootHostContext() throws somewhere in renderer code or not.
|
|---|
| 15091 | // So we push an empty value first. This lets us safely unwind on errors.
|
|---|
| 15092 |
|
|---|
| 15093 | push(contextStackCursor$1, NO_CONTEXT, fiber);
|
|---|
| 15094 | var nextRootContext = getRootHostContext(nextRootInstance); // Now that we know this function doesn't throw, replace it.
|
|---|
| 15095 |
|
|---|
| 15096 | pop(contextStackCursor$1, fiber);
|
|---|
| 15097 | push(contextStackCursor$1, nextRootContext, fiber);
|
|---|
| 15098 | }
|
|---|
| 15099 |
|
|---|
| 15100 | function popHostContainer(fiber) {
|
|---|
| 15101 | pop(contextStackCursor$1, fiber);
|
|---|
| 15102 | pop(contextFiberStackCursor, fiber);
|
|---|
| 15103 | pop(rootInstanceStackCursor, fiber);
|
|---|
| 15104 | }
|
|---|
| 15105 |
|
|---|
| 15106 | function getHostContext() {
|
|---|
| 15107 | var context = requiredContext(contextStackCursor$1.current);
|
|---|
| 15108 | return context;
|
|---|
| 15109 | }
|
|---|
| 15110 |
|
|---|
| 15111 | function pushHostContext(fiber) {
|
|---|
| 15112 | var rootInstance = requiredContext(rootInstanceStackCursor.current);
|
|---|
| 15113 | var context = requiredContext(contextStackCursor$1.current);
|
|---|
| 15114 | var nextContext = getChildHostContext(context, fiber.type); // Don't push this Fiber's context unless it's unique.
|
|---|
| 15115 |
|
|---|
| 15116 | if (context === nextContext) {
|
|---|
| 15117 | return;
|
|---|
| 15118 | } // Track the context and the Fiber that provided it.
|
|---|
| 15119 | // This enables us to pop only Fibers that provide unique contexts.
|
|---|
| 15120 |
|
|---|
| 15121 |
|
|---|
| 15122 | push(contextFiberStackCursor, fiber, fiber);
|
|---|
| 15123 | push(contextStackCursor$1, nextContext, fiber);
|
|---|
| 15124 | }
|
|---|
| 15125 |
|
|---|
| 15126 | function popHostContext(fiber) {
|
|---|
| 15127 | // Do not pop unless this Fiber provided the current context.
|
|---|
| 15128 | // pushHostContext() only pushes Fibers that provide unique contexts.
|
|---|
| 15129 | if (contextFiberStackCursor.current !== fiber) {
|
|---|
| 15130 | return;
|
|---|
| 15131 | }
|
|---|
| 15132 |
|
|---|
| 15133 | pop(contextStackCursor$1, fiber);
|
|---|
| 15134 | pop(contextFiberStackCursor, fiber);
|
|---|
| 15135 | }
|
|---|
| 15136 |
|
|---|
| 15137 | var DefaultSuspenseContext = 0; // The Suspense Context is split into two parts. The lower bits is
|
|---|
| 15138 | // inherited deeply down the subtree. The upper bits only affect
|
|---|
| 15139 | // this immediate suspense boundary and gets reset each new
|
|---|
| 15140 | // boundary or suspense list.
|
|---|
| 15141 |
|
|---|
| 15142 | var SubtreeSuspenseContextMask = 1; // Subtree Flags:
|
|---|
| 15143 | // InvisibleParentSuspenseContext indicates that one of our parent Suspense
|
|---|
| 15144 | // boundaries is not currently showing visible main content.
|
|---|
| 15145 | // Either because it is already showing a fallback or is not mounted at all.
|
|---|
| 15146 | // We can use this to determine if it is desirable to trigger a fallback at
|
|---|
| 15147 | // the parent. If not, then we might need to trigger undesirable boundaries
|
|---|
| 15148 | // and/or suspend the commit to avoid hiding the parent content.
|
|---|
| 15149 |
|
|---|
| 15150 | var InvisibleParentSuspenseContext = 1; // Shallow Flags:
|
|---|
| 15151 | // ForceSuspenseFallback can be used by SuspenseList to force newly added
|
|---|
| 15152 | // items into their fallback state during one of the render passes.
|
|---|
| 15153 |
|
|---|
| 15154 | var ForceSuspenseFallback = 2;
|
|---|
| 15155 | var suspenseStackCursor = createCursor(DefaultSuspenseContext);
|
|---|
| 15156 | function hasSuspenseContext(parentContext, flag) {
|
|---|
| 15157 | return (parentContext & flag) !== 0;
|
|---|
| 15158 | }
|
|---|
| 15159 | function setDefaultShallowSuspenseContext(parentContext) {
|
|---|
| 15160 | return parentContext & SubtreeSuspenseContextMask;
|
|---|
| 15161 | }
|
|---|
| 15162 | function setShallowSuspenseContext(parentContext, shallowContext) {
|
|---|
| 15163 | return parentContext & SubtreeSuspenseContextMask | shallowContext;
|
|---|
| 15164 | }
|
|---|
| 15165 | function addSubtreeSuspenseContext(parentContext, subtreeContext) {
|
|---|
| 15166 | return parentContext | subtreeContext;
|
|---|
| 15167 | }
|
|---|
| 15168 | function pushSuspenseContext(fiber, newContext) {
|
|---|
| 15169 | push(suspenseStackCursor, newContext, fiber);
|
|---|
| 15170 | }
|
|---|
| 15171 | function popSuspenseContext(fiber) {
|
|---|
| 15172 | pop(suspenseStackCursor, fiber);
|
|---|
| 15173 | }
|
|---|
| 15174 |
|
|---|
| 15175 | function shouldCaptureSuspense(workInProgress, hasInvisibleParent) {
|
|---|
| 15176 | // If it was the primary children that just suspended, capture and render the
|
|---|
| 15177 | // fallback. Otherwise, don't capture and bubble to the next boundary.
|
|---|
| 15178 | var nextState = workInProgress.memoizedState;
|
|---|
| 15179 |
|
|---|
| 15180 | if (nextState !== null) {
|
|---|
| 15181 | if (nextState.dehydrated !== null) {
|
|---|
| 15182 | // A dehydrated boundary always captures.
|
|---|
| 15183 | return true;
|
|---|
| 15184 | }
|
|---|
| 15185 |
|
|---|
| 15186 | return false;
|
|---|
| 15187 | }
|
|---|
| 15188 |
|
|---|
| 15189 | var props = workInProgress.memoizedProps; // Regular boundaries always capture.
|
|---|
| 15190 |
|
|---|
| 15191 | {
|
|---|
| 15192 | return true;
|
|---|
| 15193 | } // If it's a boundary we should avoid, then we prefer to bubble up to the
|
|---|
| 15194 | }
|
|---|
| 15195 | function findFirstSuspended(row) {
|
|---|
| 15196 | var node = row;
|
|---|
| 15197 |
|
|---|
| 15198 | while (node !== null) {
|
|---|
| 15199 | if (node.tag === SuspenseComponent) {
|
|---|
| 15200 | var state = node.memoizedState;
|
|---|
| 15201 |
|
|---|
| 15202 | if (state !== null) {
|
|---|
| 15203 | var dehydrated = state.dehydrated;
|
|---|
| 15204 |
|
|---|
| 15205 | if (dehydrated === null || isSuspenseInstancePending(dehydrated) || isSuspenseInstanceFallback(dehydrated)) {
|
|---|
| 15206 | return node;
|
|---|
| 15207 | }
|
|---|
| 15208 | }
|
|---|
| 15209 | } else if (node.tag === SuspenseListComponent && // revealOrder undefined can't be trusted because it don't
|
|---|
| 15210 | // keep track of whether it suspended or not.
|
|---|
| 15211 | node.memoizedProps.revealOrder !== undefined) {
|
|---|
| 15212 | var didSuspend = (node.flags & DidCapture) !== NoFlags;
|
|---|
| 15213 |
|
|---|
| 15214 | if (didSuspend) {
|
|---|
| 15215 | return node;
|
|---|
| 15216 | }
|
|---|
| 15217 | } else if (node.child !== null) {
|
|---|
| 15218 | node.child.return = node;
|
|---|
| 15219 | node = node.child;
|
|---|
| 15220 | continue;
|
|---|
| 15221 | }
|
|---|
| 15222 |
|
|---|
| 15223 | if (node === row) {
|
|---|
| 15224 | return null;
|
|---|
| 15225 | }
|
|---|
| 15226 |
|
|---|
| 15227 | while (node.sibling === null) {
|
|---|
| 15228 | if (node.return === null || node.return === row) {
|
|---|
| 15229 | return null;
|
|---|
| 15230 | }
|
|---|
| 15231 |
|
|---|
| 15232 | node = node.return;
|
|---|
| 15233 | }
|
|---|
| 15234 |
|
|---|
| 15235 | node.sibling.return = node.return;
|
|---|
| 15236 | node = node.sibling;
|
|---|
| 15237 | }
|
|---|
| 15238 |
|
|---|
| 15239 | return null;
|
|---|
| 15240 | }
|
|---|
| 15241 |
|
|---|
| 15242 | var NoFlags$1 =
|
|---|
| 15243 | /* */
|
|---|
| 15244 | 0; // Represents whether effect should fire.
|
|---|
| 15245 |
|
|---|
| 15246 | var HasEffect =
|
|---|
| 15247 | /* */
|
|---|
| 15248 | 1; // Represents the phase in which the effect (not the clean-up) fires.
|
|---|
| 15249 |
|
|---|
| 15250 | var Insertion =
|
|---|
| 15251 | /* */
|
|---|
| 15252 | 2;
|
|---|
| 15253 | var Layout =
|
|---|
| 15254 | /* */
|
|---|
| 15255 | 4;
|
|---|
| 15256 | var Passive$1 =
|
|---|
| 15257 | /* */
|
|---|
| 15258 | 8;
|
|---|
| 15259 |
|
|---|
| 15260 | // and should be reset before starting a new render.
|
|---|
| 15261 | // This tracks which mutable sources need to be reset after a render.
|
|---|
| 15262 |
|
|---|
| 15263 | var workInProgressSources = [];
|
|---|
| 15264 | function resetWorkInProgressVersions() {
|
|---|
| 15265 | for (var i = 0; i < workInProgressSources.length; i++) {
|
|---|
| 15266 | var mutableSource = workInProgressSources[i];
|
|---|
| 15267 |
|
|---|
| 15268 | {
|
|---|
| 15269 | mutableSource._workInProgressVersionPrimary = null;
|
|---|
| 15270 | }
|
|---|
| 15271 | }
|
|---|
| 15272 |
|
|---|
| 15273 | workInProgressSources.length = 0;
|
|---|
| 15274 | }
|
|---|
| 15275 | // This ensures that the version used for server rendering matches the one
|
|---|
| 15276 | // that is eventually read during hydration.
|
|---|
| 15277 | // If they don't match there's a potential tear and a full deopt render is required.
|
|---|
| 15278 |
|
|---|
| 15279 | function registerMutableSourceForHydration(root, mutableSource) {
|
|---|
| 15280 | var getVersion = mutableSource._getVersion;
|
|---|
| 15281 | var version = getVersion(mutableSource._source); // TODO Clear this data once all pending hydration work is finished.
|
|---|
| 15282 | // Retaining it forever may interfere with GC.
|
|---|
| 15283 |
|
|---|
| 15284 | if (root.mutableSourceEagerHydrationData == null) {
|
|---|
| 15285 | root.mutableSourceEagerHydrationData = [mutableSource, version];
|
|---|
| 15286 | } else {
|
|---|
| 15287 | root.mutableSourceEagerHydrationData.push(mutableSource, version);
|
|---|
| 15288 | }
|
|---|
| 15289 | }
|
|---|
| 15290 |
|
|---|
| 15291 | var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher,
|
|---|
| 15292 | ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig;
|
|---|
| 15293 | var didWarnAboutMismatchedHooksForComponent;
|
|---|
| 15294 | var didWarnUncachedGetSnapshot;
|
|---|
| 15295 |
|
|---|
| 15296 | {
|
|---|
| 15297 | didWarnAboutMismatchedHooksForComponent = new Set();
|
|---|
| 15298 | }
|
|---|
| 15299 |
|
|---|
| 15300 | // These are set right before calling the component.
|
|---|
| 15301 | var renderLanes = NoLanes; // The work-in-progress fiber. I've named it differently to distinguish it from
|
|---|
| 15302 | // the work-in-progress hook.
|
|---|
| 15303 |
|
|---|
| 15304 | var currentlyRenderingFiber$1 = null; // Hooks are stored as a linked list on the fiber's memoizedState field. The
|
|---|
| 15305 | // current hook list is the list that belongs to the current fiber. The
|
|---|
| 15306 | // work-in-progress hook list is a new list that will be added to the
|
|---|
| 15307 | // work-in-progress fiber.
|
|---|
| 15308 |
|
|---|
| 15309 | var currentHook = null;
|
|---|
| 15310 | var workInProgressHook = null; // Whether an update was scheduled at any point during the render phase. This
|
|---|
| 15311 | // does not get reset if we do another render pass; only when we're completely
|
|---|
| 15312 | // finished evaluating this component. This is an optimization so we know
|
|---|
| 15313 | // whether we need to clear render phase updates after a throw.
|
|---|
| 15314 |
|
|---|
| 15315 | var didScheduleRenderPhaseUpdate = false; // Where an update was scheduled only during the current render pass. This
|
|---|
| 15316 | // gets reset after each attempt.
|
|---|
| 15317 | // TODO: Maybe there's some way to consolidate this with
|
|---|
| 15318 | // `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`.
|
|---|
| 15319 |
|
|---|
| 15320 | var didScheduleRenderPhaseUpdateDuringThisPass = false; // Counts the number of useId hooks in this component.
|
|---|
| 15321 |
|
|---|
| 15322 | var localIdCounter = 0; // Used for ids that are generated completely client-side (i.e. not during
|
|---|
| 15323 | // hydration). This counter is global, so client ids are not stable across
|
|---|
| 15324 | // render attempts.
|
|---|
| 15325 |
|
|---|
| 15326 | var globalClientIdCounter = 0;
|
|---|
| 15327 | var RE_RENDER_LIMIT = 25; // In DEV, this is the name of the currently executing primitive hook
|
|---|
| 15328 |
|
|---|
| 15329 | var currentHookNameInDev = null; // In DEV, this list ensures that hooks are called in the same order between renders.
|
|---|
| 15330 | // The list stores the order of hooks used during the initial render (mount).
|
|---|
| 15331 | // Subsequent renders (updates) reference this list.
|
|---|
| 15332 |
|
|---|
| 15333 | var hookTypesDev = null;
|
|---|
| 15334 | var hookTypesUpdateIndexDev = -1; // In DEV, this tracks whether currently rendering component needs to ignore
|
|---|
| 15335 | // the dependencies for Hooks that need them (e.g. useEffect or useMemo).
|
|---|
| 15336 | // When true, such Hooks will always be "remounted". Only used during hot reload.
|
|---|
| 15337 |
|
|---|
| 15338 | var ignorePreviousDependencies = false;
|
|---|
| 15339 |
|
|---|
| 15340 | function mountHookTypesDev() {
|
|---|
| 15341 | {
|
|---|
| 15342 | var hookName = currentHookNameInDev;
|
|---|
| 15343 |
|
|---|
| 15344 | if (hookTypesDev === null) {
|
|---|
| 15345 | hookTypesDev = [hookName];
|
|---|
| 15346 | } else {
|
|---|
| 15347 | hookTypesDev.push(hookName);
|
|---|
| 15348 | }
|
|---|
| 15349 | }
|
|---|
| 15350 | }
|
|---|
| 15351 |
|
|---|
| 15352 | function updateHookTypesDev() {
|
|---|
| 15353 | {
|
|---|
| 15354 | var hookName = currentHookNameInDev;
|
|---|
| 15355 |
|
|---|
| 15356 | if (hookTypesDev !== null) {
|
|---|
| 15357 | hookTypesUpdateIndexDev++;
|
|---|
| 15358 |
|
|---|
| 15359 | if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) {
|
|---|
| 15360 | warnOnHookMismatchInDev(hookName);
|
|---|
| 15361 | }
|
|---|
| 15362 | }
|
|---|
| 15363 | }
|
|---|
| 15364 | }
|
|---|
| 15365 |
|
|---|
| 15366 | function checkDepsAreArrayDev(deps) {
|
|---|
| 15367 | {
|
|---|
| 15368 | if (deps !== undefined && deps !== null && !isArray(deps)) {
|
|---|
| 15369 | // Verify deps, but only on mount to avoid extra checks.
|
|---|
| 15370 | // It's unlikely their type would change as usually you define them inline.
|
|---|
| 15371 | error('%s received a final argument that is not an array (instead, received `%s`). When ' + 'specified, the final argument must be an array.', currentHookNameInDev, typeof deps);
|
|---|
| 15372 | }
|
|---|
| 15373 | }
|
|---|
| 15374 | }
|
|---|
| 15375 |
|
|---|
| 15376 | function warnOnHookMismatchInDev(currentHookName) {
|
|---|
| 15377 | {
|
|---|
| 15378 | var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1);
|
|---|
| 15379 |
|
|---|
| 15380 | if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) {
|
|---|
| 15381 | didWarnAboutMismatchedHooksForComponent.add(componentName);
|
|---|
| 15382 |
|
|---|
| 15383 | if (hookTypesDev !== null) {
|
|---|
| 15384 | var table = '';
|
|---|
| 15385 | var secondColumnStart = 30;
|
|---|
| 15386 |
|
|---|
| 15387 | for (var i = 0; i <= hookTypesUpdateIndexDev; i++) {
|
|---|
| 15388 | var oldHookName = hookTypesDev[i];
|
|---|
| 15389 | var newHookName = i === hookTypesUpdateIndexDev ? currentHookName : oldHookName;
|
|---|
| 15390 | var row = i + 1 + ". " + oldHookName; // Extra space so second column lines up
|
|---|
| 15391 | // lol @ IE not supporting String#repeat
|
|---|
| 15392 |
|
|---|
| 15393 | while (row.length < secondColumnStart) {
|
|---|
| 15394 | row += ' ';
|
|---|
| 15395 | }
|
|---|
| 15396 |
|
|---|
| 15397 | row += newHookName + '\n';
|
|---|
| 15398 | table += row;
|
|---|
| 15399 | }
|
|---|
| 15400 |
|
|---|
| 15401 | error('React has detected a change in the order of Hooks called by %s. ' + 'This will lead to bugs and errors if not fixed. ' + 'For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks\n\n' + ' Previous render Next render\n' + ' ------------------------------------------------------\n' + '%s' + ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n', componentName, table);
|
|---|
| 15402 | }
|
|---|
| 15403 | }
|
|---|
| 15404 | }
|
|---|
| 15405 | }
|
|---|
| 15406 |
|
|---|
| 15407 | function throwInvalidHookError() {
|
|---|
| 15408 | throw new Error('Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' + ' one of the following reasons:\n' + '1. You might have mismatching versions of React and the renderer (such as React DOM)\n' + '2. You might be breaking the Rules of Hooks\n' + '3. You might have more than one copy of React in the same app\n' + 'See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem.');
|
|---|
| 15409 | }
|
|---|
| 15410 |
|
|---|
| 15411 | function areHookInputsEqual(nextDeps, prevDeps) {
|
|---|
| 15412 | {
|
|---|
| 15413 | if (ignorePreviousDependencies) {
|
|---|
| 15414 | // Only true when this component is being hot reloaded.
|
|---|
| 15415 | return false;
|
|---|
| 15416 | }
|
|---|
| 15417 | }
|
|---|
| 15418 |
|
|---|
| 15419 | if (prevDeps === null) {
|
|---|
| 15420 | {
|
|---|
| 15421 | error('%s received a final argument during this render, but not during ' + 'the previous render. Even though the final argument is optional, ' + 'its type cannot change between renders.', currentHookNameInDev);
|
|---|
| 15422 | }
|
|---|
| 15423 |
|
|---|
| 15424 | return false;
|
|---|
| 15425 | }
|
|---|
| 15426 |
|
|---|
| 15427 | {
|
|---|
| 15428 | // Don't bother comparing lengths in prod because these arrays should be
|
|---|
| 15429 | // passed inline.
|
|---|
| 15430 | if (nextDeps.length !== prevDeps.length) {
|
|---|
| 15431 | error('The final argument passed to %s changed size between renders. The ' + 'order and size of this array must remain constant.\n\n' + 'Previous: %s\n' + 'Incoming: %s', currentHookNameInDev, "[" + prevDeps.join(', ') + "]", "[" + nextDeps.join(', ') + "]");
|
|---|
| 15432 | }
|
|---|
| 15433 | }
|
|---|
| 15434 |
|
|---|
| 15435 | for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) {
|
|---|
| 15436 | if (objectIs(nextDeps[i], prevDeps[i])) {
|
|---|
| 15437 | continue;
|
|---|
| 15438 | }
|
|---|
| 15439 |
|
|---|
| 15440 | return false;
|
|---|
| 15441 | }
|
|---|
| 15442 |
|
|---|
| 15443 | return true;
|
|---|
| 15444 | }
|
|---|
| 15445 |
|
|---|
| 15446 | function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) {
|
|---|
| 15447 | renderLanes = nextRenderLanes;
|
|---|
| 15448 | currentlyRenderingFiber$1 = workInProgress;
|
|---|
| 15449 |
|
|---|
| 15450 | {
|
|---|
| 15451 | hookTypesDev = current !== null ? current._debugHookTypes : null;
|
|---|
| 15452 | hookTypesUpdateIndexDev = -1; // Used for hot reloading:
|
|---|
| 15453 |
|
|---|
| 15454 | ignorePreviousDependencies = current !== null && current.type !== workInProgress.type;
|
|---|
| 15455 | }
|
|---|
| 15456 |
|
|---|
| 15457 | workInProgress.memoizedState = null;
|
|---|
| 15458 | workInProgress.updateQueue = null;
|
|---|
| 15459 | workInProgress.lanes = NoLanes; // The following should have already been reset
|
|---|
| 15460 | // currentHook = null;
|
|---|
| 15461 | // workInProgressHook = null;
|
|---|
| 15462 | // didScheduleRenderPhaseUpdate = false;
|
|---|
| 15463 | // localIdCounter = 0;
|
|---|
| 15464 | // TODO Warn if no hooks are used at all during mount, then some are used during update.
|
|---|
| 15465 | // Currently we will identify the update render as a mount because memoizedState === null.
|
|---|
| 15466 | // This is tricky because it's valid for certain types of components (e.g. React.lazy)
|
|---|
| 15467 | // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used.
|
|---|
| 15468 | // Non-stateful hooks (e.g. context) don't get added to memoizedState,
|
|---|
| 15469 | // so memoizedState would be null during updates and mounts.
|
|---|
| 15470 |
|
|---|
| 15471 | {
|
|---|
| 15472 | if (current !== null && current.memoizedState !== null) {
|
|---|
| 15473 | ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV;
|
|---|
| 15474 | } else if (hookTypesDev !== null) {
|
|---|
| 15475 | // This dispatcher handles an edge case where a component is updating,
|
|---|
| 15476 | // but no stateful hooks have been used.
|
|---|
| 15477 | // We want to match the production code behavior (which will use HooksDispatcherOnMount),
|
|---|
| 15478 | // but with the extra DEV validation to ensure hooks ordering hasn't changed.
|
|---|
| 15479 | // This dispatcher does that.
|
|---|
| 15480 | ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV;
|
|---|
| 15481 | } else {
|
|---|
| 15482 | ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV;
|
|---|
| 15483 | }
|
|---|
| 15484 | }
|
|---|
| 15485 |
|
|---|
| 15486 | var children = Component(props, secondArg); // Check if there was a render phase update
|
|---|
| 15487 |
|
|---|
| 15488 | if (didScheduleRenderPhaseUpdateDuringThisPass) {
|
|---|
| 15489 | // Keep rendering in a loop for as long as render phase updates continue to
|
|---|
| 15490 | // be scheduled. Use a counter to prevent infinite loops.
|
|---|
| 15491 | var numberOfReRenders = 0;
|
|---|
| 15492 |
|
|---|
| 15493 | do {
|
|---|
| 15494 | didScheduleRenderPhaseUpdateDuringThisPass = false;
|
|---|
| 15495 | localIdCounter = 0;
|
|---|
| 15496 |
|
|---|
| 15497 | if (numberOfReRenders >= RE_RENDER_LIMIT) {
|
|---|
| 15498 | throw new Error('Too many re-renders. React limits the number of renders to prevent ' + 'an infinite loop.');
|
|---|
| 15499 | }
|
|---|
| 15500 |
|
|---|
| 15501 | numberOfReRenders += 1;
|
|---|
| 15502 |
|
|---|
| 15503 | {
|
|---|
| 15504 | // Even when hot reloading, allow dependencies to stabilize
|
|---|
| 15505 | // after first render to prevent infinite render phase updates.
|
|---|
| 15506 | ignorePreviousDependencies = false;
|
|---|
| 15507 | } // Start over from the beginning of the list
|
|---|
| 15508 |
|
|---|
| 15509 |
|
|---|
| 15510 | currentHook = null;
|
|---|
| 15511 | workInProgressHook = null;
|
|---|
| 15512 | workInProgress.updateQueue = null;
|
|---|
| 15513 |
|
|---|
| 15514 | {
|
|---|
| 15515 | // Also validate hook order for cascading updates.
|
|---|
| 15516 | hookTypesUpdateIndexDev = -1;
|
|---|
| 15517 | }
|
|---|
| 15518 |
|
|---|
| 15519 | ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV ;
|
|---|
| 15520 | children = Component(props, secondArg);
|
|---|
| 15521 | } while (didScheduleRenderPhaseUpdateDuringThisPass);
|
|---|
| 15522 | } // We can assume the previous dispatcher is always this one, since we set it
|
|---|
| 15523 | // at the beginning of the render phase and there's no re-entrance.
|
|---|
| 15524 |
|
|---|
| 15525 |
|
|---|
| 15526 | ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
|---|
| 15527 |
|
|---|
| 15528 | {
|
|---|
| 15529 | workInProgress._debugHookTypes = hookTypesDev;
|
|---|
| 15530 | } // This check uses currentHook so that it works the same in DEV and prod bundles.
|
|---|
| 15531 | // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles.
|
|---|
| 15532 |
|
|---|
| 15533 |
|
|---|
| 15534 | var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null;
|
|---|
| 15535 | renderLanes = NoLanes;
|
|---|
| 15536 | currentlyRenderingFiber$1 = null;
|
|---|
| 15537 | currentHook = null;
|
|---|
| 15538 | workInProgressHook = null;
|
|---|
| 15539 |
|
|---|
| 15540 | {
|
|---|
| 15541 | currentHookNameInDev = null;
|
|---|
| 15542 | hookTypesDev = null;
|
|---|
| 15543 | hookTypesUpdateIndexDev = -1; // Confirm that a static flag was not added or removed since the last
|
|---|
| 15544 | // render. If this fires, it suggests that we incorrectly reset the static
|
|---|
| 15545 | // flags in some other part of the codebase. This has happened before, for
|
|---|
| 15546 | // example, in the SuspenseList implementation.
|
|---|
| 15547 |
|
|---|
| 15548 | if (current !== null && (current.flags & StaticMask) !== (workInProgress.flags & StaticMask) && // Disable this warning in legacy mode, because legacy Suspense is weird
|
|---|
| 15549 | // and creates false positives. To make this work in legacy mode, we'd
|
|---|
| 15550 | // need to mark fibers that commit in an incomplete state, somehow. For
|
|---|
| 15551 | // now I'll disable the warning that most of the bugs that would trigger
|
|---|
| 15552 | // it are either exclusive to concurrent mode or exist in both.
|
|---|
| 15553 | (current.mode & ConcurrentMode) !== NoMode) {
|
|---|
| 15554 | error('Internal React error: Expected static flag was missing. Please ' + 'notify the React team.');
|
|---|
| 15555 | }
|
|---|
| 15556 | }
|
|---|
| 15557 |
|
|---|
| 15558 | didScheduleRenderPhaseUpdate = false; // This is reset by checkDidRenderIdHook
|
|---|
| 15559 | // localIdCounter = 0;
|
|---|
| 15560 |
|
|---|
| 15561 | if (didRenderTooFewHooks) {
|
|---|
| 15562 | throw new Error('Rendered fewer hooks than expected. This may be caused by an accidental ' + 'early return statement.');
|
|---|
| 15563 | }
|
|---|
| 15564 |
|
|---|
| 15565 | return children;
|
|---|
| 15566 | }
|
|---|
| 15567 | function checkDidRenderIdHook() {
|
|---|
| 15568 | // This should be called immediately after every renderWithHooks call.
|
|---|
| 15569 | // Conceptually, it's part of the return value of renderWithHooks; it's only a
|
|---|
| 15570 | // separate function to avoid using an array tuple.
|
|---|
| 15571 | var didRenderIdHook = localIdCounter !== 0;
|
|---|
| 15572 | localIdCounter = 0;
|
|---|
| 15573 | return didRenderIdHook;
|
|---|
| 15574 | }
|
|---|
| 15575 | function bailoutHooks(current, workInProgress, lanes) {
|
|---|
| 15576 | workInProgress.updateQueue = current.updateQueue; // TODO: Don't need to reset the flags here, because they're reset in the
|
|---|
| 15577 | // complete phase (bubbleProperties).
|
|---|
| 15578 |
|
|---|
| 15579 | if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 15580 | workInProgress.flags &= ~(MountPassiveDev | MountLayoutDev | Passive | Update);
|
|---|
| 15581 | } else {
|
|---|
| 15582 | workInProgress.flags &= ~(Passive | Update);
|
|---|
| 15583 | }
|
|---|
| 15584 |
|
|---|
| 15585 | current.lanes = removeLanes(current.lanes, lanes);
|
|---|
| 15586 | }
|
|---|
| 15587 | function resetHooksAfterThrow() {
|
|---|
| 15588 | // We can assume the previous dispatcher is always this one, since we set it
|
|---|
| 15589 | // at the beginning of the render phase and there's no re-entrance.
|
|---|
| 15590 | ReactCurrentDispatcher$1.current = ContextOnlyDispatcher;
|
|---|
| 15591 |
|
|---|
| 15592 | if (didScheduleRenderPhaseUpdate) {
|
|---|
| 15593 | // There were render phase updates. These are only valid for this render
|
|---|
| 15594 | // phase, which we are now aborting. Remove the updates from the queues so
|
|---|
| 15595 | // they do not persist to the next render. Do not remove updates from hooks
|
|---|
| 15596 | // that weren't processed.
|
|---|
| 15597 | //
|
|---|
| 15598 | // Only reset the updates from the queue if it has a clone. If it does
|
|---|
| 15599 | // not have a clone, that means it wasn't processed, and the updates were
|
|---|
| 15600 | // scheduled before we entered the render phase.
|
|---|
| 15601 | var hook = currentlyRenderingFiber$1.memoizedState;
|
|---|
| 15602 |
|
|---|
| 15603 | while (hook !== null) {
|
|---|
| 15604 | var queue = hook.queue;
|
|---|
| 15605 |
|
|---|
| 15606 | if (queue !== null) {
|
|---|
| 15607 | queue.pending = null;
|
|---|
| 15608 | }
|
|---|
| 15609 |
|
|---|
| 15610 | hook = hook.next;
|
|---|
| 15611 | }
|
|---|
| 15612 |
|
|---|
| 15613 | didScheduleRenderPhaseUpdate = false;
|
|---|
| 15614 | }
|
|---|
| 15615 |
|
|---|
| 15616 | renderLanes = NoLanes;
|
|---|
| 15617 | currentlyRenderingFiber$1 = null;
|
|---|
| 15618 | currentHook = null;
|
|---|
| 15619 | workInProgressHook = null;
|
|---|
| 15620 |
|
|---|
| 15621 | {
|
|---|
| 15622 | hookTypesDev = null;
|
|---|
| 15623 | hookTypesUpdateIndexDev = -1;
|
|---|
| 15624 | currentHookNameInDev = null;
|
|---|
| 15625 | isUpdatingOpaqueValueInRenderPhase = false;
|
|---|
| 15626 | }
|
|---|
| 15627 |
|
|---|
| 15628 | didScheduleRenderPhaseUpdateDuringThisPass = false;
|
|---|
| 15629 | localIdCounter = 0;
|
|---|
| 15630 | }
|
|---|
| 15631 |
|
|---|
| 15632 | function mountWorkInProgressHook() {
|
|---|
| 15633 | var hook = {
|
|---|
| 15634 | memoizedState: null,
|
|---|
| 15635 | baseState: null,
|
|---|
| 15636 | baseQueue: null,
|
|---|
| 15637 | queue: null,
|
|---|
| 15638 | next: null
|
|---|
| 15639 | };
|
|---|
| 15640 |
|
|---|
| 15641 | if (workInProgressHook === null) {
|
|---|
| 15642 | // This is the first hook in the list
|
|---|
| 15643 | currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook;
|
|---|
| 15644 | } else {
|
|---|
| 15645 | // Append to the end of the list
|
|---|
| 15646 | workInProgressHook = workInProgressHook.next = hook;
|
|---|
| 15647 | }
|
|---|
| 15648 |
|
|---|
| 15649 | return workInProgressHook;
|
|---|
| 15650 | }
|
|---|
| 15651 |
|
|---|
| 15652 | function updateWorkInProgressHook() {
|
|---|
| 15653 | // This function is used both for updates and for re-renders triggered by a
|
|---|
| 15654 | // render phase update. It assumes there is either a current hook we can
|
|---|
| 15655 | // clone, or a work-in-progress hook from a previous render pass that we can
|
|---|
| 15656 | // use as a base. When we reach the end of the base list, we must switch to
|
|---|
| 15657 | // the dispatcher used for mounts.
|
|---|
| 15658 | var nextCurrentHook;
|
|---|
| 15659 |
|
|---|
| 15660 | if (currentHook === null) {
|
|---|
| 15661 | var current = currentlyRenderingFiber$1.alternate;
|
|---|
| 15662 |
|
|---|
| 15663 | if (current !== null) {
|
|---|
| 15664 | nextCurrentHook = current.memoizedState;
|
|---|
| 15665 | } else {
|
|---|
| 15666 | nextCurrentHook = null;
|
|---|
| 15667 | }
|
|---|
| 15668 | } else {
|
|---|
| 15669 | nextCurrentHook = currentHook.next;
|
|---|
| 15670 | }
|
|---|
| 15671 |
|
|---|
| 15672 | var nextWorkInProgressHook;
|
|---|
| 15673 |
|
|---|
| 15674 | if (workInProgressHook === null) {
|
|---|
| 15675 | nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState;
|
|---|
| 15676 | } else {
|
|---|
| 15677 | nextWorkInProgressHook = workInProgressHook.next;
|
|---|
| 15678 | }
|
|---|
| 15679 |
|
|---|
| 15680 | if (nextWorkInProgressHook !== null) {
|
|---|
| 15681 | // There's already a work-in-progress. Reuse it.
|
|---|
| 15682 | workInProgressHook = nextWorkInProgressHook;
|
|---|
| 15683 | nextWorkInProgressHook = workInProgressHook.next;
|
|---|
| 15684 | currentHook = nextCurrentHook;
|
|---|
| 15685 | } else {
|
|---|
| 15686 | // Clone from the current hook.
|
|---|
| 15687 | if (nextCurrentHook === null) {
|
|---|
| 15688 | throw new Error('Rendered more hooks than during the previous render.');
|
|---|
| 15689 | }
|
|---|
| 15690 |
|
|---|
| 15691 | currentHook = nextCurrentHook;
|
|---|
| 15692 | var newHook = {
|
|---|
| 15693 | memoizedState: currentHook.memoizedState,
|
|---|
| 15694 | baseState: currentHook.baseState,
|
|---|
| 15695 | baseQueue: currentHook.baseQueue,
|
|---|
| 15696 | queue: currentHook.queue,
|
|---|
| 15697 | next: null
|
|---|
| 15698 | };
|
|---|
| 15699 |
|
|---|
| 15700 | if (workInProgressHook === null) {
|
|---|
| 15701 | // This is the first hook in the list.
|
|---|
| 15702 | currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook;
|
|---|
| 15703 | } else {
|
|---|
| 15704 | // Append to the end of the list.
|
|---|
| 15705 | workInProgressHook = workInProgressHook.next = newHook;
|
|---|
| 15706 | }
|
|---|
| 15707 | }
|
|---|
| 15708 |
|
|---|
| 15709 | return workInProgressHook;
|
|---|
| 15710 | }
|
|---|
| 15711 |
|
|---|
| 15712 | function createFunctionComponentUpdateQueue() {
|
|---|
| 15713 | return {
|
|---|
| 15714 | lastEffect: null,
|
|---|
| 15715 | stores: null
|
|---|
| 15716 | };
|
|---|
| 15717 | }
|
|---|
| 15718 |
|
|---|
| 15719 | function basicStateReducer(state, action) {
|
|---|
| 15720 | // $FlowFixMe: Flow doesn't like mixed types
|
|---|
| 15721 | return typeof action === 'function' ? action(state) : action;
|
|---|
| 15722 | }
|
|---|
| 15723 |
|
|---|
| 15724 | function mountReducer(reducer, initialArg, init) {
|
|---|
| 15725 | var hook = mountWorkInProgressHook();
|
|---|
| 15726 | var initialState;
|
|---|
| 15727 |
|
|---|
| 15728 | if (init !== undefined) {
|
|---|
| 15729 | initialState = init(initialArg);
|
|---|
| 15730 | } else {
|
|---|
| 15731 | initialState = initialArg;
|
|---|
| 15732 | }
|
|---|
| 15733 |
|
|---|
| 15734 | hook.memoizedState = hook.baseState = initialState;
|
|---|
| 15735 | var queue = {
|
|---|
| 15736 | pending: null,
|
|---|
| 15737 | interleaved: null,
|
|---|
| 15738 | lanes: NoLanes,
|
|---|
| 15739 | dispatch: null,
|
|---|
| 15740 | lastRenderedReducer: reducer,
|
|---|
| 15741 | lastRenderedState: initialState
|
|---|
| 15742 | };
|
|---|
| 15743 | hook.queue = queue;
|
|---|
| 15744 | var dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber$1, queue);
|
|---|
| 15745 | return [hook.memoizedState, dispatch];
|
|---|
| 15746 | }
|
|---|
| 15747 |
|
|---|
| 15748 | function updateReducer(reducer, initialArg, init) {
|
|---|
| 15749 | var hook = updateWorkInProgressHook();
|
|---|
| 15750 | var queue = hook.queue;
|
|---|
| 15751 |
|
|---|
| 15752 | if (queue === null) {
|
|---|
| 15753 | throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
|
|---|
| 15754 | }
|
|---|
| 15755 |
|
|---|
| 15756 | queue.lastRenderedReducer = reducer;
|
|---|
| 15757 | var current = currentHook; // The last rebase update that is NOT part of the base state.
|
|---|
| 15758 |
|
|---|
| 15759 | var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet.
|
|---|
| 15760 |
|
|---|
| 15761 | var pendingQueue = queue.pending;
|
|---|
| 15762 |
|
|---|
| 15763 | if (pendingQueue !== null) {
|
|---|
| 15764 | // We have new updates that haven't been processed yet.
|
|---|
| 15765 | // We'll add them to the base queue.
|
|---|
| 15766 | if (baseQueue !== null) {
|
|---|
| 15767 | // Merge the pending queue and the base queue.
|
|---|
| 15768 | var baseFirst = baseQueue.next;
|
|---|
| 15769 | var pendingFirst = pendingQueue.next;
|
|---|
| 15770 | baseQueue.next = pendingFirst;
|
|---|
| 15771 | pendingQueue.next = baseFirst;
|
|---|
| 15772 | }
|
|---|
| 15773 |
|
|---|
| 15774 | {
|
|---|
| 15775 | if (current.baseQueue !== baseQueue) {
|
|---|
| 15776 | // Internal invariant that should never happen, but feasibly could in
|
|---|
| 15777 | // the future if we implement resuming, or some form of that.
|
|---|
| 15778 | error('Internal error: Expected work-in-progress queue to be a clone. ' + 'This is a bug in React.');
|
|---|
| 15779 | }
|
|---|
| 15780 | }
|
|---|
| 15781 |
|
|---|
| 15782 | current.baseQueue = baseQueue = pendingQueue;
|
|---|
| 15783 | queue.pending = null;
|
|---|
| 15784 | }
|
|---|
| 15785 |
|
|---|
| 15786 | if (baseQueue !== null) {
|
|---|
| 15787 | // We have a queue to process.
|
|---|
| 15788 | var first = baseQueue.next;
|
|---|
| 15789 | var newState = current.baseState;
|
|---|
| 15790 | var newBaseState = null;
|
|---|
| 15791 | var newBaseQueueFirst = null;
|
|---|
| 15792 | var newBaseQueueLast = null;
|
|---|
| 15793 | var update = first;
|
|---|
| 15794 |
|
|---|
| 15795 | do {
|
|---|
| 15796 | var updateLane = update.lane;
|
|---|
| 15797 |
|
|---|
| 15798 | if (!isSubsetOfLanes(renderLanes, updateLane)) {
|
|---|
| 15799 | // Priority is insufficient. Skip this update. If this is the first
|
|---|
| 15800 | // skipped update, the previous update/state is the new base
|
|---|
| 15801 | // update/state.
|
|---|
| 15802 | var clone = {
|
|---|
| 15803 | lane: updateLane,
|
|---|
| 15804 | action: update.action,
|
|---|
| 15805 | hasEagerState: update.hasEagerState,
|
|---|
| 15806 | eagerState: update.eagerState,
|
|---|
| 15807 | next: null
|
|---|
| 15808 | };
|
|---|
| 15809 |
|
|---|
| 15810 | if (newBaseQueueLast === null) {
|
|---|
| 15811 | newBaseQueueFirst = newBaseQueueLast = clone;
|
|---|
| 15812 | newBaseState = newState;
|
|---|
| 15813 | } else {
|
|---|
| 15814 | newBaseQueueLast = newBaseQueueLast.next = clone;
|
|---|
| 15815 | } // Update the remaining priority in the queue.
|
|---|
| 15816 | // TODO: Don't need to accumulate this. Instead, we can remove
|
|---|
| 15817 | // renderLanes from the original lanes.
|
|---|
| 15818 |
|
|---|
| 15819 |
|
|---|
| 15820 | currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane);
|
|---|
| 15821 | markSkippedUpdateLanes(updateLane);
|
|---|
| 15822 | } else {
|
|---|
| 15823 | // This update does have sufficient priority.
|
|---|
| 15824 | if (newBaseQueueLast !== null) {
|
|---|
| 15825 | var _clone = {
|
|---|
| 15826 | // This update is going to be committed so we never want uncommit
|
|---|
| 15827 | // it. Using NoLane works because 0 is a subset of all bitmasks, so
|
|---|
| 15828 | // this will never be skipped by the check above.
|
|---|
| 15829 | lane: NoLane,
|
|---|
| 15830 | action: update.action,
|
|---|
| 15831 | hasEagerState: update.hasEagerState,
|
|---|
| 15832 | eagerState: update.eagerState,
|
|---|
| 15833 | next: null
|
|---|
| 15834 | };
|
|---|
| 15835 | newBaseQueueLast = newBaseQueueLast.next = _clone;
|
|---|
| 15836 | } // Process this update.
|
|---|
| 15837 |
|
|---|
| 15838 |
|
|---|
| 15839 | if (update.hasEagerState) {
|
|---|
| 15840 | // If this update is a state update (not a reducer) and was processed eagerly,
|
|---|
| 15841 | // we can use the eagerly computed state
|
|---|
| 15842 | newState = update.eagerState;
|
|---|
| 15843 | } else {
|
|---|
| 15844 | var action = update.action;
|
|---|
| 15845 | newState = reducer(newState, action);
|
|---|
| 15846 | }
|
|---|
| 15847 | }
|
|---|
| 15848 |
|
|---|
| 15849 | update = update.next;
|
|---|
| 15850 | } while (update !== null && update !== first);
|
|---|
| 15851 |
|
|---|
| 15852 | if (newBaseQueueLast === null) {
|
|---|
| 15853 | newBaseState = newState;
|
|---|
| 15854 | } else {
|
|---|
| 15855 | newBaseQueueLast.next = newBaseQueueFirst;
|
|---|
| 15856 | } // Mark that the fiber performed work, but only if the new state is
|
|---|
| 15857 | // different from the current state.
|
|---|
| 15858 |
|
|---|
| 15859 |
|
|---|
| 15860 | if (!objectIs(newState, hook.memoizedState)) {
|
|---|
| 15861 | markWorkInProgressReceivedUpdate();
|
|---|
| 15862 | }
|
|---|
| 15863 |
|
|---|
| 15864 | hook.memoizedState = newState;
|
|---|
| 15865 | hook.baseState = newBaseState;
|
|---|
| 15866 | hook.baseQueue = newBaseQueueLast;
|
|---|
| 15867 | queue.lastRenderedState = newState;
|
|---|
| 15868 | } // Interleaved updates are stored on a separate queue. We aren't going to
|
|---|
| 15869 | // process them during this render, but we do need to track which lanes
|
|---|
| 15870 | // are remaining.
|
|---|
| 15871 |
|
|---|
| 15872 |
|
|---|
| 15873 | var lastInterleaved = queue.interleaved;
|
|---|
| 15874 |
|
|---|
| 15875 | if (lastInterleaved !== null) {
|
|---|
| 15876 | var interleaved = lastInterleaved;
|
|---|
| 15877 |
|
|---|
| 15878 | do {
|
|---|
| 15879 | var interleavedLane = interleaved.lane;
|
|---|
| 15880 | currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, interleavedLane);
|
|---|
| 15881 | markSkippedUpdateLanes(interleavedLane);
|
|---|
| 15882 | interleaved = interleaved.next;
|
|---|
| 15883 | } while (interleaved !== lastInterleaved);
|
|---|
| 15884 | } else if (baseQueue === null) {
|
|---|
| 15885 | // `queue.lanes` is used for entangling transitions. We can set it back to
|
|---|
| 15886 | // zero once the queue is empty.
|
|---|
| 15887 | queue.lanes = NoLanes;
|
|---|
| 15888 | }
|
|---|
| 15889 |
|
|---|
| 15890 | var dispatch = queue.dispatch;
|
|---|
| 15891 | return [hook.memoizedState, dispatch];
|
|---|
| 15892 | }
|
|---|
| 15893 |
|
|---|
| 15894 | function rerenderReducer(reducer, initialArg, init) {
|
|---|
| 15895 | var hook = updateWorkInProgressHook();
|
|---|
| 15896 | var queue = hook.queue;
|
|---|
| 15897 |
|
|---|
| 15898 | if (queue === null) {
|
|---|
| 15899 | throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.');
|
|---|
| 15900 | }
|
|---|
| 15901 |
|
|---|
| 15902 | queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous
|
|---|
| 15903 | // work-in-progress hook.
|
|---|
| 15904 |
|
|---|
| 15905 | var dispatch = queue.dispatch;
|
|---|
| 15906 | var lastRenderPhaseUpdate = queue.pending;
|
|---|
| 15907 | var newState = hook.memoizedState;
|
|---|
| 15908 |
|
|---|
| 15909 | if (lastRenderPhaseUpdate !== null) {
|
|---|
| 15910 | // The queue doesn't persist past this render pass.
|
|---|
| 15911 | queue.pending = null;
|
|---|
| 15912 | var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next;
|
|---|
| 15913 | var update = firstRenderPhaseUpdate;
|
|---|
| 15914 |
|
|---|
| 15915 | do {
|
|---|
| 15916 | // Process this render phase update. We don't have to check the
|
|---|
| 15917 | // priority because it will always be the same as the current
|
|---|
| 15918 | // render's.
|
|---|
| 15919 | var action = update.action;
|
|---|
| 15920 | newState = reducer(newState, action);
|
|---|
| 15921 | update = update.next;
|
|---|
| 15922 | } while (update !== firstRenderPhaseUpdate); // Mark that the fiber performed work, but only if the new state is
|
|---|
| 15923 | // different from the current state.
|
|---|
| 15924 |
|
|---|
| 15925 |
|
|---|
| 15926 | if (!objectIs(newState, hook.memoizedState)) {
|
|---|
| 15927 | markWorkInProgressReceivedUpdate();
|
|---|
| 15928 | }
|
|---|
| 15929 |
|
|---|
| 15930 | hook.memoizedState = newState; // Don't persist the state accumulated from the render phase updates to
|
|---|
| 15931 | // the base state unless the queue is empty.
|
|---|
| 15932 | // TODO: Not sure if this is the desired semantics, but it's what we
|
|---|
| 15933 | // do for gDSFP. I can't remember why.
|
|---|
| 15934 |
|
|---|
| 15935 | if (hook.baseQueue === null) {
|
|---|
| 15936 | hook.baseState = newState;
|
|---|
| 15937 | }
|
|---|
| 15938 |
|
|---|
| 15939 | queue.lastRenderedState = newState;
|
|---|
| 15940 | }
|
|---|
| 15941 |
|
|---|
| 15942 | return [newState, dispatch];
|
|---|
| 15943 | }
|
|---|
| 15944 |
|
|---|
| 15945 | function mountMutableSource(source, getSnapshot, subscribe) {
|
|---|
| 15946 | {
|
|---|
| 15947 | return undefined;
|
|---|
| 15948 | }
|
|---|
| 15949 | }
|
|---|
| 15950 |
|
|---|
| 15951 | function updateMutableSource(source, getSnapshot, subscribe) {
|
|---|
| 15952 | {
|
|---|
| 15953 | return undefined;
|
|---|
| 15954 | }
|
|---|
| 15955 | }
|
|---|
| 15956 |
|
|---|
| 15957 | function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 15958 | var fiber = currentlyRenderingFiber$1;
|
|---|
| 15959 | var hook = mountWorkInProgressHook();
|
|---|
| 15960 | var nextSnapshot;
|
|---|
| 15961 | var isHydrating = getIsHydrating();
|
|---|
| 15962 |
|
|---|
| 15963 | if (isHydrating) {
|
|---|
| 15964 | if (getServerSnapshot === undefined) {
|
|---|
| 15965 | throw new Error('Missing getServerSnapshot, which is required for ' + 'server-rendered content. Will revert to client rendering.');
|
|---|
| 15966 | }
|
|---|
| 15967 |
|
|---|
| 15968 | nextSnapshot = getServerSnapshot();
|
|---|
| 15969 |
|
|---|
| 15970 | {
|
|---|
| 15971 | if (!didWarnUncachedGetSnapshot) {
|
|---|
| 15972 | if (nextSnapshot !== getServerSnapshot()) {
|
|---|
| 15973 | error('The result of getServerSnapshot should be cached to avoid an infinite loop');
|
|---|
| 15974 |
|
|---|
| 15975 | didWarnUncachedGetSnapshot = true;
|
|---|
| 15976 | }
|
|---|
| 15977 | }
|
|---|
| 15978 | }
|
|---|
| 15979 | } else {
|
|---|
| 15980 | nextSnapshot = getSnapshot();
|
|---|
| 15981 |
|
|---|
| 15982 | {
|
|---|
| 15983 | if (!didWarnUncachedGetSnapshot) {
|
|---|
| 15984 | var cachedSnapshot = getSnapshot();
|
|---|
| 15985 |
|
|---|
| 15986 | if (!objectIs(nextSnapshot, cachedSnapshot)) {
|
|---|
| 15987 | error('The result of getSnapshot should be cached to avoid an infinite loop');
|
|---|
| 15988 |
|
|---|
| 15989 | didWarnUncachedGetSnapshot = true;
|
|---|
| 15990 | }
|
|---|
| 15991 | }
|
|---|
| 15992 | } // Unless we're rendering a blocking lane, schedule a consistency check.
|
|---|
| 15993 | // Right before committing, we will walk the tree and check if any of the
|
|---|
| 15994 | // stores were mutated.
|
|---|
| 15995 | //
|
|---|
| 15996 | // We won't do this if we're hydrating server-rendered content, because if
|
|---|
| 15997 | // the content is stale, it's already visible anyway. Instead we'll patch
|
|---|
| 15998 | // it up in a passive effect.
|
|---|
| 15999 |
|
|---|
| 16000 |
|
|---|
| 16001 | var root = getWorkInProgressRoot();
|
|---|
| 16002 |
|
|---|
| 16003 | if (root === null) {
|
|---|
| 16004 | throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.');
|
|---|
| 16005 | }
|
|---|
| 16006 |
|
|---|
| 16007 | if (!includesBlockingLane(root, renderLanes)) {
|
|---|
| 16008 | pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
|
|---|
| 16009 | }
|
|---|
| 16010 | } // Read the current snapshot from the store on every render. This breaks the
|
|---|
| 16011 | // normal rules of React, and only works because store updates are
|
|---|
| 16012 | // always synchronous.
|
|---|
| 16013 |
|
|---|
| 16014 |
|
|---|
| 16015 | hook.memoizedState = nextSnapshot;
|
|---|
| 16016 | var inst = {
|
|---|
| 16017 | value: nextSnapshot,
|
|---|
| 16018 | getSnapshot: getSnapshot
|
|---|
| 16019 | };
|
|---|
| 16020 | hook.queue = inst; // Schedule an effect to subscribe to the store.
|
|---|
| 16021 |
|
|---|
| 16022 | mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Schedule an effect to update the mutable instance fields. We will update
|
|---|
| 16023 | // this whenever subscribe, getSnapshot, or value changes. Because there's no
|
|---|
| 16024 | // clean-up function, and we track the deps correctly, we can call pushEffect
|
|---|
| 16025 | // directly, without storing any additional state. For the same reason, we
|
|---|
| 16026 | // don't need to set a static flag, either.
|
|---|
| 16027 | // TODO: We can move this to the passive phase once we add a pre-commit
|
|---|
| 16028 | // consistency check. See the next comment.
|
|---|
| 16029 |
|
|---|
| 16030 | fiber.flags |= Passive;
|
|---|
| 16031 | pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null);
|
|---|
| 16032 | return nextSnapshot;
|
|---|
| 16033 | }
|
|---|
| 16034 |
|
|---|
| 16035 | function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 16036 | var fiber = currentlyRenderingFiber$1;
|
|---|
| 16037 | var hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the
|
|---|
| 16038 | // normal rules of React, and only works because store updates are
|
|---|
| 16039 | // always synchronous.
|
|---|
| 16040 |
|
|---|
| 16041 | var nextSnapshot = getSnapshot();
|
|---|
| 16042 |
|
|---|
| 16043 | {
|
|---|
| 16044 | if (!didWarnUncachedGetSnapshot) {
|
|---|
| 16045 | var cachedSnapshot = getSnapshot();
|
|---|
| 16046 |
|
|---|
| 16047 | if (!objectIs(nextSnapshot, cachedSnapshot)) {
|
|---|
| 16048 | error('The result of getSnapshot should be cached to avoid an infinite loop');
|
|---|
| 16049 |
|
|---|
| 16050 | didWarnUncachedGetSnapshot = true;
|
|---|
| 16051 | }
|
|---|
| 16052 | }
|
|---|
| 16053 | }
|
|---|
| 16054 |
|
|---|
| 16055 | var prevSnapshot = hook.memoizedState;
|
|---|
| 16056 | var snapshotChanged = !objectIs(prevSnapshot, nextSnapshot);
|
|---|
| 16057 |
|
|---|
| 16058 | if (snapshotChanged) {
|
|---|
| 16059 | hook.memoizedState = nextSnapshot;
|
|---|
| 16060 | markWorkInProgressReceivedUpdate();
|
|---|
| 16061 | }
|
|---|
| 16062 |
|
|---|
| 16063 | var inst = hook.queue;
|
|---|
| 16064 | updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Whenever getSnapshot or subscribe changes, we need to check in the
|
|---|
| 16065 | // commit phase if there was an interleaved mutation. In concurrent mode
|
|---|
| 16066 | // this can happen all the time, but even in synchronous mode, an earlier
|
|---|
| 16067 | // effect may have mutated the store.
|
|---|
| 16068 |
|
|---|
| 16069 | if (inst.getSnapshot !== getSnapshot || snapshotChanged || // Check if the susbcribe function changed. We can save some memory by
|
|---|
| 16070 | // checking whether we scheduled a subscription effect above.
|
|---|
| 16071 | workInProgressHook !== null && workInProgressHook.memoizedState.tag & HasEffect) {
|
|---|
| 16072 | fiber.flags |= Passive;
|
|---|
| 16073 | pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null); // Unless we're rendering a blocking lane, schedule a consistency check.
|
|---|
| 16074 | // Right before committing, we will walk the tree and check if any of the
|
|---|
| 16075 | // stores were mutated.
|
|---|
| 16076 |
|
|---|
| 16077 | var root = getWorkInProgressRoot();
|
|---|
| 16078 |
|
|---|
| 16079 | if (root === null) {
|
|---|
| 16080 | throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.');
|
|---|
| 16081 | }
|
|---|
| 16082 |
|
|---|
| 16083 | if (!includesBlockingLane(root, renderLanes)) {
|
|---|
| 16084 | pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot);
|
|---|
| 16085 | }
|
|---|
| 16086 | }
|
|---|
| 16087 |
|
|---|
| 16088 | return nextSnapshot;
|
|---|
| 16089 | }
|
|---|
| 16090 |
|
|---|
| 16091 | function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) {
|
|---|
| 16092 | fiber.flags |= StoreConsistency;
|
|---|
| 16093 | var check = {
|
|---|
| 16094 | getSnapshot: getSnapshot,
|
|---|
| 16095 | value: renderedSnapshot
|
|---|
| 16096 | };
|
|---|
| 16097 | var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
|
|---|
| 16098 |
|
|---|
| 16099 | if (componentUpdateQueue === null) {
|
|---|
| 16100 | componentUpdateQueue = createFunctionComponentUpdateQueue();
|
|---|
| 16101 | currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
|
|---|
| 16102 | componentUpdateQueue.stores = [check];
|
|---|
| 16103 | } else {
|
|---|
| 16104 | var stores = componentUpdateQueue.stores;
|
|---|
| 16105 |
|
|---|
| 16106 | if (stores === null) {
|
|---|
| 16107 | componentUpdateQueue.stores = [check];
|
|---|
| 16108 | } else {
|
|---|
| 16109 | stores.push(check);
|
|---|
| 16110 | }
|
|---|
| 16111 | }
|
|---|
| 16112 | }
|
|---|
| 16113 |
|
|---|
| 16114 | function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) {
|
|---|
| 16115 | // These are updated in the passive phase
|
|---|
| 16116 | inst.value = nextSnapshot;
|
|---|
| 16117 | inst.getSnapshot = getSnapshot; // Something may have been mutated in between render and commit. This could
|
|---|
| 16118 | // have been in an event that fired before the passive effects, or it could
|
|---|
| 16119 | // have been in a layout effect. In that case, we would have used the old
|
|---|
| 16120 | // snapsho and getSnapshot values to bail out. We need to check one more time.
|
|---|
| 16121 |
|
|---|
| 16122 | if (checkIfSnapshotChanged(inst)) {
|
|---|
| 16123 | // Force a re-render.
|
|---|
| 16124 | forceStoreRerender(fiber);
|
|---|
| 16125 | }
|
|---|
| 16126 | }
|
|---|
| 16127 |
|
|---|
| 16128 | function subscribeToStore(fiber, inst, subscribe) {
|
|---|
| 16129 | var handleStoreChange = function () {
|
|---|
| 16130 | // The store changed. Check if the snapshot changed since the last time we
|
|---|
| 16131 | // read from the store.
|
|---|
| 16132 | if (checkIfSnapshotChanged(inst)) {
|
|---|
| 16133 | // Force a re-render.
|
|---|
| 16134 | forceStoreRerender(fiber);
|
|---|
| 16135 | }
|
|---|
| 16136 | }; // Subscribe to the store and return a clean-up function.
|
|---|
| 16137 |
|
|---|
| 16138 |
|
|---|
| 16139 | return subscribe(handleStoreChange);
|
|---|
| 16140 | }
|
|---|
| 16141 |
|
|---|
| 16142 | function checkIfSnapshotChanged(inst) {
|
|---|
| 16143 | var latestGetSnapshot = inst.getSnapshot;
|
|---|
| 16144 | var prevValue = inst.value;
|
|---|
| 16145 |
|
|---|
| 16146 | try {
|
|---|
| 16147 | var nextValue = latestGetSnapshot();
|
|---|
| 16148 | return !objectIs(prevValue, nextValue);
|
|---|
| 16149 | } catch (error) {
|
|---|
| 16150 | return true;
|
|---|
| 16151 | }
|
|---|
| 16152 | }
|
|---|
| 16153 |
|
|---|
| 16154 | function forceStoreRerender(fiber) {
|
|---|
| 16155 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 16156 |
|
|---|
| 16157 | if (root !== null) {
|
|---|
| 16158 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 16159 | }
|
|---|
| 16160 | }
|
|---|
| 16161 |
|
|---|
| 16162 | function mountState(initialState) {
|
|---|
| 16163 | var hook = mountWorkInProgressHook();
|
|---|
| 16164 |
|
|---|
| 16165 | if (typeof initialState === 'function') {
|
|---|
| 16166 | // $FlowFixMe: Flow doesn't like mixed types
|
|---|
| 16167 | initialState = initialState();
|
|---|
| 16168 | }
|
|---|
| 16169 |
|
|---|
| 16170 | hook.memoizedState = hook.baseState = initialState;
|
|---|
| 16171 | var queue = {
|
|---|
| 16172 | pending: null,
|
|---|
| 16173 | interleaved: null,
|
|---|
| 16174 | lanes: NoLanes,
|
|---|
| 16175 | dispatch: null,
|
|---|
| 16176 | lastRenderedReducer: basicStateReducer,
|
|---|
| 16177 | lastRenderedState: initialState
|
|---|
| 16178 | };
|
|---|
| 16179 | hook.queue = queue;
|
|---|
| 16180 | var dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue);
|
|---|
| 16181 | return [hook.memoizedState, dispatch];
|
|---|
| 16182 | }
|
|---|
| 16183 |
|
|---|
| 16184 | function updateState(initialState) {
|
|---|
| 16185 | return updateReducer(basicStateReducer);
|
|---|
| 16186 | }
|
|---|
| 16187 |
|
|---|
| 16188 | function rerenderState(initialState) {
|
|---|
| 16189 | return rerenderReducer(basicStateReducer);
|
|---|
| 16190 | }
|
|---|
| 16191 |
|
|---|
| 16192 | function pushEffect(tag, create, destroy, deps) {
|
|---|
| 16193 | var effect = {
|
|---|
| 16194 | tag: tag,
|
|---|
| 16195 | create: create,
|
|---|
| 16196 | destroy: destroy,
|
|---|
| 16197 | deps: deps,
|
|---|
| 16198 | // Circular
|
|---|
| 16199 | next: null
|
|---|
| 16200 | };
|
|---|
| 16201 | var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue;
|
|---|
| 16202 |
|
|---|
| 16203 | if (componentUpdateQueue === null) {
|
|---|
| 16204 | componentUpdateQueue = createFunctionComponentUpdateQueue();
|
|---|
| 16205 | currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;
|
|---|
| 16206 | componentUpdateQueue.lastEffect = effect.next = effect;
|
|---|
| 16207 | } else {
|
|---|
| 16208 | var lastEffect = componentUpdateQueue.lastEffect;
|
|---|
| 16209 |
|
|---|
| 16210 | if (lastEffect === null) {
|
|---|
| 16211 | componentUpdateQueue.lastEffect = effect.next = effect;
|
|---|
| 16212 | } else {
|
|---|
| 16213 | var firstEffect = lastEffect.next;
|
|---|
| 16214 | lastEffect.next = effect;
|
|---|
| 16215 | effect.next = firstEffect;
|
|---|
| 16216 | componentUpdateQueue.lastEffect = effect;
|
|---|
| 16217 | }
|
|---|
| 16218 | }
|
|---|
| 16219 |
|
|---|
| 16220 | return effect;
|
|---|
| 16221 | }
|
|---|
| 16222 |
|
|---|
| 16223 | function mountRef(initialValue) {
|
|---|
| 16224 | var hook = mountWorkInProgressHook();
|
|---|
| 16225 |
|
|---|
| 16226 | {
|
|---|
| 16227 | var _ref2 = {
|
|---|
| 16228 | current: initialValue
|
|---|
| 16229 | };
|
|---|
| 16230 | hook.memoizedState = _ref2;
|
|---|
| 16231 | return _ref2;
|
|---|
| 16232 | }
|
|---|
| 16233 | }
|
|---|
| 16234 |
|
|---|
| 16235 | function updateRef(initialValue) {
|
|---|
| 16236 | var hook = updateWorkInProgressHook();
|
|---|
| 16237 | return hook.memoizedState;
|
|---|
| 16238 | }
|
|---|
| 16239 |
|
|---|
| 16240 | function mountEffectImpl(fiberFlags, hookFlags, create, deps) {
|
|---|
| 16241 | var hook = mountWorkInProgressHook();
|
|---|
| 16242 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16243 | currentlyRenderingFiber$1.flags |= fiberFlags;
|
|---|
| 16244 | hook.memoizedState = pushEffect(HasEffect | hookFlags, create, undefined, nextDeps);
|
|---|
| 16245 | }
|
|---|
| 16246 |
|
|---|
| 16247 | function updateEffectImpl(fiberFlags, hookFlags, create, deps) {
|
|---|
| 16248 | var hook = updateWorkInProgressHook();
|
|---|
| 16249 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16250 | var destroy = undefined;
|
|---|
| 16251 |
|
|---|
| 16252 | if (currentHook !== null) {
|
|---|
| 16253 | var prevEffect = currentHook.memoizedState;
|
|---|
| 16254 | destroy = prevEffect.destroy;
|
|---|
| 16255 |
|
|---|
| 16256 | if (nextDeps !== null) {
|
|---|
| 16257 | var prevDeps = prevEffect.deps;
|
|---|
| 16258 |
|
|---|
| 16259 | if (areHookInputsEqual(nextDeps, prevDeps)) {
|
|---|
| 16260 | hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps);
|
|---|
| 16261 | return;
|
|---|
| 16262 | }
|
|---|
| 16263 | }
|
|---|
| 16264 | }
|
|---|
| 16265 |
|
|---|
| 16266 | currentlyRenderingFiber$1.flags |= fiberFlags;
|
|---|
| 16267 | hook.memoizedState = pushEffect(HasEffect | hookFlags, create, destroy, nextDeps);
|
|---|
| 16268 | }
|
|---|
| 16269 |
|
|---|
| 16270 | function mountEffect(create, deps) {
|
|---|
| 16271 | if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 16272 | return mountEffectImpl(MountPassiveDev | Passive | PassiveStatic, Passive$1, create, deps);
|
|---|
| 16273 | } else {
|
|---|
| 16274 | return mountEffectImpl(Passive | PassiveStatic, Passive$1, create, deps);
|
|---|
| 16275 | }
|
|---|
| 16276 | }
|
|---|
| 16277 |
|
|---|
| 16278 | function updateEffect(create, deps) {
|
|---|
| 16279 | return updateEffectImpl(Passive, Passive$1, create, deps);
|
|---|
| 16280 | }
|
|---|
| 16281 |
|
|---|
| 16282 | function mountInsertionEffect(create, deps) {
|
|---|
| 16283 | return mountEffectImpl(Update, Insertion, create, deps);
|
|---|
| 16284 | }
|
|---|
| 16285 |
|
|---|
| 16286 | function updateInsertionEffect(create, deps) {
|
|---|
| 16287 | return updateEffectImpl(Update, Insertion, create, deps);
|
|---|
| 16288 | }
|
|---|
| 16289 |
|
|---|
| 16290 | function mountLayoutEffect(create, deps) {
|
|---|
| 16291 | var fiberFlags = Update;
|
|---|
| 16292 |
|
|---|
| 16293 | {
|
|---|
| 16294 | fiberFlags |= LayoutStatic;
|
|---|
| 16295 | }
|
|---|
| 16296 |
|
|---|
| 16297 | if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 16298 | fiberFlags |= MountLayoutDev;
|
|---|
| 16299 | }
|
|---|
| 16300 |
|
|---|
| 16301 | return mountEffectImpl(fiberFlags, Layout, create, deps);
|
|---|
| 16302 | }
|
|---|
| 16303 |
|
|---|
| 16304 | function updateLayoutEffect(create, deps) {
|
|---|
| 16305 | return updateEffectImpl(Update, Layout, create, deps);
|
|---|
| 16306 | }
|
|---|
| 16307 |
|
|---|
| 16308 | function imperativeHandleEffect(create, ref) {
|
|---|
| 16309 | if (typeof ref === 'function') {
|
|---|
| 16310 | var refCallback = ref;
|
|---|
| 16311 |
|
|---|
| 16312 | var _inst = create();
|
|---|
| 16313 |
|
|---|
| 16314 | refCallback(_inst);
|
|---|
| 16315 | return function () {
|
|---|
| 16316 | refCallback(null);
|
|---|
| 16317 | };
|
|---|
| 16318 | } else if (ref !== null && ref !== undefined) {
|
|---|
| 16319 | var refObject = ref;
|
|---|
| 16320 |
|
|---|
| 16321 | {
|
|---|
| 16322 | if (!refObject.hasOwnProperty('current')) {
|
|---|
| 16323 | error('Expected useImperativeHandle() first argument to either be a ' + 'ref callback or React.createRef() object. Instead received: %s.', 'an object with keys {' + Object.keys(refObject).join(', ') + '}');
|
|---|
| 16324 | }
|
|---|
| 16325 | }
|
|---|
| 16326 |
|
|---|
| 16327 | var _inst2 = create();
|
|---|
| 16328 |
|
|---|
| 16329 | refObject.current = _inst2;
|
|---|
| 16330 | return function () {
|
|---|
| 16331 | refObject.current = null;
|
|---|
| 16332 | };
|
|---|
| 16333 | }
|
|---|
| 16334 | }
|
|---|
| 16335 |
|
|---|
| 16336 | function mountImperativeHandle(ref, create, deps) {
|
|---|
| 16337 | {
|
|---|
| 16338 | if (typeof create !== 'function') {
|
|---|
| 16339 | error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null');
|
|---|
| 16340 | }
|
|---|
| 16341 | } // TODO: If deps are provided, should we skip comparing the ref itself?
|
|---|
| 16342 |
|
|---|
| 16343 |
|
|---|
| 16344 | var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
|---|
| 16345 | var fiberFlags = Update;
|
|---|
| 16346 |
|
|---|
| 16347 | {
|
|---|
| 16348 | fiberFlags |= LayoutStatic;
|
|---|
| 16349 | }
|
|---|
| 16350 |
|
|---|
| 16351 | if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 16352 | fiberFlags |= MountLayoutDev;
|
|---|
| 16353 | }
|
|---|
| 16354 |
|
|---|
| 16355 | return mountEffectImpl(fiberFlags, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
|---|
| 16356 | }
|
|---|
| 16357 |
|
|---|
| 16358 | function updateImperativeHandle(ref, create, deps) {
|
|---|
| 16359 | {
|
|---|
| 16360 | if (typeof create !== 'function') {
|
|---|
| 16361 | error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null');
|
|---|
| 16362 | }
|
|---|
| 16363 | } // TODO: If deps are provided, should we skip comparing the ref itself?
|
|---|
| 16364 |
|
|---|
| 16365 |
|
|---|
| 16366 | var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null;
|
|---|
| 16367 | return updateEffectImpl(Update, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps);
|
|---|
| 16368 | }
|
|---|
| 16369 |
|
|---|
| 16370 | function mountDebugValue(value, formatterFn) {// This hook is normally a no-op.
|
|---|
| 16371 | // The react-debug-hooks package injects its own implementation
|
|---|
| 16372 | // so that e.g. DevTools can display custom hook values.
|
|---|
| 16373 | }
|
|---|
| 16374 |
|
|---|
| 16375 | var updateDebugValue = mountDebugValue;
|
|---|
| 16376 |
|
|---|
| 16377 | function mountCallback(callback, deps) {
|
|---|
| 16378 | var hook = mountWorkInProgressHook();
|
|---|
| 16379 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16380 | hook.memoizedState = [callback, nextDeps];
|
|---|
| 16381 | return callback;
|
|---|
| 16382 | }
|
|---|
| 16383 |
|
|---|
| 16384 | function updateCallback(callback, deps) {
|
|---|
| 16385 | var hook = updateWorkInProgressHook();
|
|---|
| 16386 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16387 | var prevState = hook.memoizedState;
|
|---|
| 16388 |
|
|---|
| 16389 | if (prevState !== null) {
|
|---|
| 16390 | if (nextDeps !== null) {
|
|---|
| 16391 | var prevDeps = prevState[1];
|
|---|
| 16392 |
|
|---|
| 16393 | if (areHookInputsEqual(nextDeps, prevDeps)) {
|
|---|
| 16394 | return prevState[0];
|
|---|
| 16395 | }
|
|---|
| 16396 | }
|
|---|
| 16397 | }
|
|---|
| 16398 |
|
|---|
| 16399 | hook.memoizedState = [callback, nextDeps];
|
|---|
| 16400 | return callback;
|
|---|
| 16401 | }
|
|---|
| 16402 |
|
|---|
| 16403 | function mountMemo(nextCreate, deps) {
|
|---|
| 16404 | var hook = mountWorkInProgressHook();
|
|---|
| 16405 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16406 | var nextValue = nextCreate();
|
|---|
| 16407 | hook.memoizedState = [nextValue, nextDeps];
|
|---|
| 16408 | return nextValue;
|
|---|
| 16409 | }
|
|---|
| 16410 |
|
|---|
| 16411 | function updateMemo(nextCreate, deps) {
|
|---|
| 16412 | var hook = updateWorkInProgressHook();
|
|---|
| 16413 | var nextDeps = deps === undefined ? null : deps;
|
|---|
| 16414 | var prevState = hook.memoizedState;
|
|---|
| 16415 |
|
|---|
| 16416 | if (prevState !== null) {
|
|---|
| 16417 | // Assume these are defined. If they're not, areHookInputsEqual will warn.
|
|---|
| 16418 | if (nextDeps !== null) {
|
|---|
| 16419 | var prevDeps = prevState[1];
|
|---|
| 16420 |
|
|---|
| 16421 | if (areHookInputsEqual(nextDeps, prevDeps)) {
|
|---|
| 16422 | return prevState[0];
|
|---|
| 16423 | }
|
|---|
| 16424 | }
|
|---|
| 16425 | }
|
|---|
| 16426 |
|
|---|
| 16427 | var nextValue = nextCreate();
|
|---|
| 16428 | hook.memoizedState = [nextValue, nextDeps];
|
|---|
| 16429 | return nextValue;
|
|---|
| 16430 | }
|
|---|
| 16431 |
|
|---|
| 16432 | function mountDeferredValue(value) {
|
|---|
| 16433 | var hook = mountWorkInProgressHook();
|
|---|
| 16434 | hook.memoizedState = value;
|
|---|
| 16435 | return value;
|
|---|
| 16436 | }
|
|---|
| 16437 |
|
|---|
| 16438 | function updateDeferredValue(value) {
|
|---|
| 16439 | var hook = updateWorkInProgressHook();
|
|---|
| 16440 | var resolvedCurrentHook = currentHook;
|
|---|
| 16441 | var prevValue = resolvedCurrentHook.memoizedState;
|
|---|
| 16442 | return updateDeferredValueImpl(hook, prevValue, value);
|
|---|
| 16443 | }
|
|---|
| 16444 |
|
|---|
| 16445 | function rerenderDeferredValue(value) {
|
|---|
| 16446 | var hook = updateWorkInProgressHook();
|
|---|
| 16447 |
|
|---|
| 16448 | if (currentHook === null) {
|
|---|
| 16449 | // This is a rerender during a mount.
|
|---|
| 16450 | hook.memoizedState = value;
|
|---|
| 16451 | return value;
|
|---|
| 16452 | } else {
|
|---|
| 16453 | // This is a rerender during an update.
|
|---|
| 16454 | var prevValue = currentHook.memoizedState;
|
|---|
| 16455 | return updateDeferredValueImpl(hook, prevValue, value);
|
|---|
| 16456 | }
|
|---|
| 16457 | }
|
|---|
| 16458 |
|
|---|
| 16459 | function updateDeferredValueImpl(hook, prevValue, value) {
|
|---|
| 16460 | var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes);
|
|---|
| 16461 |
|
|---|
| 16462 | if (shouldDeferValue) {
|
|---|
| 16463 | // This is an urgent update. If the value has changed, keep using the
|
|---|
| 16464 | // previous value and spawn a deferred render to update it later.
|
|---|
| 16465 | if (!objectIs(value, prevValue)) {
|
|---|
| 16466 | // Schedule a deferred render
|
|---|
| 16467 | var deferredLane = claimNextTransitionLane();
|
|---|
| 16468 | currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, deferredLane);
|
|---|
| 16469 | markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent
|
|---|
| 16470 | // from the latest value. The name "baseState" doesn't really match how we
|
|---|
| 16471 | // use it because we're reusing a state hook field instead of creating a
|
|---|
| 16472 | // new one.
|
|---|
| 16473 |
|
|---|
| 16474 | hook.baseState = true;
|
|---|
| 16475 | } // Reuse the previous value
|
|---|
| 16476 |
|
|---|
| 16477 |
|
|---|
| 16478 | return prevValue;
|
|---|
| 16479 | } else {
|
|---|
| 16480 | // This is not an urgent update, so we can use the latest value regardless
|
|---|
| 16481 | // of what it is. No need to defer it.
|
|---|
| 16482 | // However, if we're currently inside a spawned render, then we need to mark
|
|---|
| 16483 | // this as an update to prevent the fiber from bailing out.
|
|---|
| 16484 | //
|
|---|
| 16485 | // `baseState` is true when the current value is different from the rendered
|
|---|
| 16486 | // value. The name doesn't really match how we use it because we're reusing
|
|---|
| 16487 | // a state hook field instead of creating a new one.
|
|---|
| 16488 | if (hook.baseState) {
|
|---|
| 16489 | // Flip this back to false.
|
|---|
| 16490 | hook.baseState = false;
|
|---|
| 16491 | markWorkInProgressReceivedUpdate();
|
|---|
| 16492 | }
|
|---|
| 16493 |
|
|---|
| 16494 | hook.memoizedState = value;
|
|---|
| 16495 | return value;
|
|---|
| 16496 | }
|
|---|
| 16497 | }
|
|---|
| 16498 |
|
|---|
| 16499 | function startTransition(setPending, callback, options) {
|
|---|
| 16500 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 16501 | setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority));
|
|---|
| 16502 | setPending(true);
|
|---|
| 16503 | var prevTransition = ReactCurrentBatchConfig$2.transition;
|
|---|
| 16504 | ReactCurrentBatchConfig$2.transition = {};
|
|---|
| 16505 | var currentTransition = ReactCurrentBatchConfig$2.transition;
|
|---|
| 16506 |
|
|---|
| 16507 | {
|
|---|
| 16508 | ReactCurrentBatchConfig$2.transition._updatedFibers = new Set();
|
|---|
| 16509 | }
|
|---|
| 16510 |
|
|---|
| 16511 | try {
|
|---|
| 16512 | setPending(false);
|
|---|
| 16513 | callback();
|
|---|
| 16514 | } finally {
|
|---|
| 16515 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 16516 | ReactCurrentBatchConfig$2.transition = prevTransition;
|
|---|
| 16517 |
|
|---|
| 16518 | {
|
|---|
| 16519 | if (prevTransition === null && currentTransition._updatedFibers) {
|
|---|
| 16520 | var updatedFibersCount = currentTransition._updatedFibers.size;
|
|---|
| 16521 |
|
|---|
| 16522 | if (updatedFibersCount > 10) {
|
|---|
| 16523 | warn('Detected a large number of updates inside startTransition. ' + 'If this is due to a subscription please re-write it to use React provided hooks. ' + 'Otherwise concurrent mode guarantees are off the table.');
|
|---|
| 16524 | }
|
|---|
| 16525 |
|
|---|
| 16526 | currentTransition._updatedFibers.clear();
|
|---|
| 16527 | }
|
|---|
| 16528 | }
|
|---|
| 16529 | }
|
|---|
| 16530 | }
|
|---|
| 16531 |
|
|---|
| 16532 | function mountTransition() {
|
|---|
| 16533 | var _mountState = mountState(false),
|
|---|
| 16534 | isPending = _mountState[0],
|
|---|
| 16535 | setPending = _mountState[1]; // The `start` method never changes.
|
|---|
| 16536 |
|
|---|
| 16537 |
|
|---|
| 16538 | var start = startTransition.bind(null, setPending);
|
|---|
| 16539 | var hook = mountWorkInProgressHook();
|
|---|
| 16540 | hook.memoizedState = start;
|
|---|
| 16541 | return [isPending, start];
|
|---|
| 16542 | }
|
|---|
| 16543 |
|
|---|
| 16544 | function updateTransition() {
|
|---|
| 16545 | var _updateState = updateState(),
|
|---|
| 16546 | isPending = _updateState[0];
|
|---|
| 16547 |
|
|---|
| 16548 | var hook = updateWorkInProgressHook();
|
|---|
| 16549 | var start = hook.memoizedState;
|
|---|
| 16550 | return [isPending, start];
|
|---|
| 16551 | }
|
|---|
| 16552 |
|
|---|
| 16553 | function rerenderTransition() {
|
|---|
| 16554 | var _rerenderState = rerenderState(),
|
|---|
| 16555 | isPending = _rerenderState[0];
|
|---|
| 16556 |
|
|---|
| 16557 | var hook = updateWorkInProgressHook();
|
|---|
| 16558 | var start = hook.memoizedState;
|
|---|
| 16559 | return [isPending, start];
|
|---|
| 16560 | }
|
|---|
| 16561 |
|
|---|
| 16562 | var isUpdatingOpaqueValueInRenderPhase = false;
|
|---|
| 16563 | function getIsUpdatingOpaqueValueInRenderPhaseInDEV() {
|
|---|
| 16564 | {
|
|---|
| 16565 | return isUpdatingOpaqueValueInRenderPhase;
|
|---|
| 16566 | }
|
|---|
| 16567 | }
|
|---|
| 16568 |
|
|---|
| 16569 | function mountId() {
|
|---|
| 16570 | var hook = mountWorkInProgressHook();
|
|---|
| 16571 | var root = getWorkInProgressRoot(); // TODO: In Fizz, id generation is specific to each server config. Maybe we
|
|---|
| 16572 | // should do this in Fiber, too? Deferring this decision for now because
|
|---|
| 16573 | // there's no other place to store the prefix except for an internal field on
|
|---|
| 16574 | // the public createRoot object, which the fiber tree does not currently have
|
|---|
| 16575 | // a reference to.
|
|---|
| 16576 |
|
|---|
| 16577 | var identifierPrefix = root.identifierPrefix;
|
|---|
| 16578 | var id;
|
|---|
| 16579 |
|
|---|
| 16580 | if (getIsHydrating()) {
|
|---|
| 16581 | var treeId = getTreeId(); // Use a captial R prefix for server-generated ids.
|
|---|
| 16582 |
|
|---|
| 16583 | id = ':' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end
|
|---|
| 16584 | // that represents the position of this useId hook among all the useId
|
|---|
| 16585 | // hooks for this fiber.
|
|---|
| 16586 |
|
|---|
| 16587 | var localId = localIdCounter++;
|
|---|
| 16588 |
|
|---|
| 16589 | if (localId > 0) {
|
|---|
| 16590 | id += 'H' + localId.toString(32);
|
|---|
| 16591 | }
|
|---|
| 16592 |
|
|---|
| 16593 | id += ':';
|
|---|
| 16594 | } else {
|
|---|
| 16595 | // Use a lowercase r prefix for client-generated ids.
|
|---|
| 16596 | var globalClientId = globalClientIdCounter++;
|
|---|
| 16597 | id = ':' + identifierPrefix + 'r' + globalClientId.toString(32) + ':';
|
|---|
| 16598 | }
|
|---|
| 16599 |
|
|---|
| 16600 | hook.memoizedState = id;
|
|---|
| 16601 | return id;
|
|---|
| 16602 | }
|
|---|
| 16603 |
|
|---|
| 16604 | function updateId() {
|
|---|
| 16605 | var hook = updateWorkInProgressHook();
|
|---|
| 16606 | var id = hook.memoizedState;
|
|---|
| 16607 | return id;
|
|---|
| 16608 | }
|
|---|
| 16609 |
|
|---|
| 16610 | function dispatchReducerAction(fiber, queue, action) {
|
|---|
| 16611 | {
|
|---|
| 16612 | if (typeof arguments[3] === 'function') {
|
|---|
| 16613 | error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
|
|---|
| 16614 | }
|
|---|
| 16615 | }
|
|---|
| 16616 |
|
|---|
| 16617 | var lane = requestUpdateLane(fiber);
|
|---|
| 16618 | var update = {
|
|---|
| 16619 | lane: lane,
|
|---|
| 16620 | action: action,
|
|---|
| 16621 | hasEagerState: false,
|
|---|
| 16622 | eagerState: null,
|
|---|
| 16623 | next: null
|
|---|
| 16624 | };
|
|---|
| 16625 |
|
|---|
| 16626 | if (isRenderPhaseUpdate(fiber)) {
|
|---|
| 16627 | enqueueRenderPhaseUpdate(queue, update);
|
|---|
| 16628 | } else {
|
|---|
| 16629 | var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
|---|
| 16630 |
|
|---|
| 16631 | if (root !== null) {
|
|---|
| 16632 | var eventTime = requestEventTime();
|
|---|
| 16633 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 16634 | entangleTransitionUpdate(root, queue, lane);
|
|---|
| 16635 | }
|
|---|
| 16636 | }
|
|---|
| 16637 |
|
|---|
| 16638 | markUpdateInDevTools(fiber, lane);
|
|---|
| 16639 | }
|
|---|
| 16640 |
|
|---|
| 16641 | function dispatchSetState(fiber, queue, action) {
|
|---|
| 16642 | {
|
|---|
| 16643 | if (typeof arguments[3] === 'function') {
|
|---|
| 16644 | error("State updates from the useState() and useReducer() Hooks don't support the " + 'second callback argument. To execute a side effect after ' + 'rendering, declare it in the component body with useEffect().');
|
|---|
| 16645 | }
|
|---|
| 16646 | }
|
|---|
| 16647 |
|
|---|
| 16648 | var lane = requestUpdateLane(fiber);
|
|---|
| 16649 | var update = {
|
|---|
| 16650 | lane: lane,
|
|---|
| 16651 | action: action,
|
|---|
| 16652 | hasEagerState: false,
|
|---|
| 16653 | eagerState: null,
|
|---|
| 16654 | next: null
|
|---|
| 16655 | };
|
|---|
| 16656 |
|
|---|
| 16657 | if (isRenderPhaseUpdate(fiber)) {
|
|---|
| 16658 | enqueueRenderPhaseUpdate(queue, update);
|
|---|
| 16659 | } else {
|
|---|
| 16660 | var alternate = fiber.alternate;
|
|---|
| 16661 |
|
|---|
| 16662 | if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) {
|
|---|
| 16663 | // The queue is currently empty, which means we can eagerly compute the
|
|---|
| 16664 | // next state before entering the render phase. If the new state is the
|
|---|
| 16665 | // same as the current state, we may be able to bail out entirely.
|
|---|
| 16666 | var lastRenderedReducer = queue.lastRenderedReducer;
|
|---|
| 16667 |
|
|---|
| 16668 | if (lastRenderedReducer !== null) {
|
|---|
| 16669 | var prevDispatcher;
|
|---|
| 16670 |
|
|---|
| 16671 | {
|
|---|
| 16672 | prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16673 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 16674 | }
|
|---|
| 16675 |
|
|---|
| 16676 | try {
|
|---|
| 16677 | var currentState = queue.lastRenderedState;
|
|---|
| 16678 | var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute
|
|---|
| 16679 | // it, on the update object. If the reducer hasn't changed by the
|
|---|
| 16680 | // time we enter the render phase, then the eager state can be used
|
|---|
| 16681 | // without calling the reducer again.
|
|---|
| 16682 |
|
|---|
| 16683 | update.hasEagerState = true;
|
|---|
| 16684 | update.eagerState = eagerState;
|
|---|
| 16685 |
|
|---|
| 16686 | if (objectIs(eagerState, currentState)) {
|
|---|
| 16687 | // Fast path. We can bail out without scheduling React to re-render.
|
|---|
| 16688 | // It's still possible that we'll need to rebase this update later,
|
|---|
| 16689 | // if the component re-renders for a different reason and by that
|
|---|
| 16690 | // time the reducer has changed.
|
|---|
| 16691 | // TODO: Do we still need to entangle transitions in this case?
|
|---|
| 16692 | enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane);
|
|---|
| 16693 | return;
|
|---|
| 16694 | }
|
|---|
| 16695 | } catch (error) {// Suppress the error. It will throw again in the render phase.
|
|---|
| 16696 | } finally {
|
|---|
| 16697 | {
|
|---|
| 16698 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16699 | }
|
|---|
| 16700 | }
|
|---|
| 16701 | }
|
|---|
| 16702 | }
|
|---|
| 16703 |
|
|---|
| 16704 | var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane);
|
|---|
| 16705 |
|
|---|
| 16706 | if (root !== null) {
|
|---|
| 16707 | var eventTime = requestEventTime();
|
|---|
| 16708 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 16709 | entangleTransitionUpdate(root, queue, lane);
|
|---|
| 16710 | }
|
|---|
| 16711 | }
|
|---|
| 16712 |
|
|---|
| 16713 | markUpdateInDevTools(fiber, lane);
|
|---|
| 16714 | }
|
|---|
| 16715 |
|
|---|
| 16716 | function isRenderPhaseUpdate(fiber) {
|
|---|
| 16717 | var alternate = fiber.alternate;
|
|---|
| 16718 | return fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1;
|
|---|
| 16719 | }
|
|---|
| 16720 |
|
|---|
| 16721 | function enqueueRenderPhaseUpdate(queue, update) {
|
|---|
| 16722 | // This is a render phase update. Stash it in a lazily-created map of
|
|---|
| 16723 | // queue -> linked list of updates. After this render pass, we'll restart
|
|---|
| 16724 | // and apply the stashed updates on top of the work-in-progress hook.
|
|---|
| 16725 | didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true;
|
|---|
| 16726 | var pending = queue.pending;
|
|---|
| 16727 |
|
|---|
| 16728 | if (pending === null) {
|
|---|
| 16729 | // This is the first update. Create a circular list.
|
|---|
| 16730 | update.next = update;
|
|---|
| 16731 | } else {
|
|---|
| 16732 | update.next = pending.next;
|
|---|
| 16733 | pending.next = update;
|
|---|
| 16734 | }
|
|---|
| 16735 |
|
|---|
| 16736 | queue.pending = update;
|
|---|
| 16737 | } // TODO: Move to ReactFiberConcurrentUpdates?
|
|---|
| 16738 |
|
|---|
| 16739 |
|
|---|
| 16740 | function entangleTransitionUpdate(root, queue, lane) {
|
|---|
| 16741 | if (isTransitionLane(lane)) {
|
|---|
| 16742 | var queueLanes = queue.lanes; // If any entangled lanes are no longer pending on the root, then they
|
|---|
| 16743 | // must have finished. We can remove them from the shared queue, which
|
|---|
| 16744 | // represents a superset of the actually pending lanes. In some cases we
|
|---|
| 16745 | // may entangle more than we need to, but that's OK. In fact it's worse if
|
|---|
| 16746 | // we *don't* entangle when we should.
|
|---|
| 16747 |
|
|---|
| 16748 | queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes.
|
|---|
| 16749 |
|
|---|
| 16750 | var newQueueLanes = mergeLanes(queueLanes, lane);
|
|---|
| 16751 | queue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if
|
|---|
| 16752 | // the lane finished since the last time we entangled it. So we need to
|
|---|
| 16753 | // entangle it again, just to be sure.
|
|---|
| 16754 |
|
|---|
| 16755 | markRootEntangled(root, newQueueLanes);
|
|---|
| 16756 | }
|
|---|
| 16757 | }
|
|---|
| 16758 |
|
|---|
| 16759 | function markUpdateInDevTools(fiber, lane, action) {
|
|---|
| 16760 |
|
|---|
| 16761 | {
|
|---|
| 16762 | markStateUpdateScheduled(fiber, lane);
|
|---|
| 16763 | }
|
|---|
| 16764 | }
|
|---|
| 16765 |
|
|---|
| 16766 | var ContextOnlyDispatcher = {
|
|---|
| 16767 | readContext: readContext,
|
|---|
| 16768 | useCallback: throwInvalidHookError,
|
|---|
| 16769 | useContext: throwInvalidHookError,
|
|---|
| 16770 | useEffect: throwInvalidHookError,
|
|---|
| 16771 | useImperativeHandle: throwInvalidHookError,
|
|---|
| 16772 | useInsertionEffect: throwInvalidHookError,
|
|---|
| 16773 | useLayoutEffect: throwInvalidHookError,
|
|---|
| 16774 | useMemo: throwInvalidHookError,
|
|---|
| 16775 | useReducer: throwInvalidHookError,
|
|---|
| 16776 | useRef: throwInvalidHookError,
|
|---|
| 16777 | useState: throwInvalidHookError,
|
|---|
| 16778 | useDebugValue: throwInvalidHookError,
|
|---|
| 16779 | useDeferredValue: throwInvalidHookError,
|
|---|
| 16780 | useTransition: throwInvalidHookError,
|
|---|
| 16781 | useMutableSource: throwInvalidHookError,
|
|---|
| 16782 | useSyncExternalStore: throwInvalidHookError,
|
|---|
| 16783 | useId: throwInvalidHookError,
|
|---|
| 16784 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 16785 | };
|
|---|
| 16786 |
|
|---|
| 16787 | var HooksDispatcherOnMountInDEV = null;
|
|---|
| 16788 | var HooksDispatcherOnMountWithHookTypesInDEV = null;
|
|---|
| 16789 | var HooksDispatcherOnUpdateInDEV = null;
|
|---|
| 16790 | var HooksDispatcherOnRerenderInDEV = null;
|
|---|
| 16791 | var InvalidNestedHooksDispatcherOnMountInDEV = null;
|
|---|
| 16792 | var InvalidNestedHooksDispatcherOnUpdateInDEV = null;
|
|---|
| 16793 | var InvalidNestedHooksDispatcherOnRerenderInDEV = null;
|
|---|
| 16794 |
|
|---|
| 16795 | {
|
|---|
| 16796 | var warnInvalidContextAccess = function () {
|
|---|
| 16797 | error('Context can only be read while React is rendering. ' + 'In classes, you can read it in the render method or getDerivedStateFromProps. ' + 'In function components, you can read it directly in the function body, but not ' + 'inside Hooks like useReducer() or useMemo().');
|
|---|
| 16798 | };
|
|---|
| 16799 |
|
|---|
| 16800 | var warnInvalidHookAccess = function () {
|
|---|
| 16801 | error('Do not call Hooks inside useEffect(...), useMemo(...), or other built-in Hooks. ' + 'You can only call Hooks at the top level of your React function. ' + 'For more information, see ' + 'https://reactjs.org/link/rules-of-hooks');
|
|---|
| 16802 | };
|
|---|
| 16803 |
|
|---|
| 16804 | HooksDispatcherOnMountInDEV = {
|
|---|
| 16805 | readContext: function (context) {
|
|---|
| 16806 | return readContext(context);
|
|---|
| 16807 | },
|
|---|
| 16808 | useCallback: function (callback, deps) {
|
|---|
| 16809 | currentHookNameInDev = 'useCallback';
|
|---|
| 16810 | mountHookTypesDev();
|
|---|
| 16811 | checkDepsAreArrayDev(deps);
|
|---|
| 16812 | return mountCallback(callback, deps);
|
|---|
| 16813 | },
|
|---|
| 16814 | useContext: function (context) {
|
|---|
| 16815 | currentHookNameInDev = 'useContext';
|
|---|
| 16816 | mountHookTypesDev();
|
|---|
| 16817 | return readContext(context);
|
|---|
| 16818 | },
|
|---|
| 16819 | useEffect: function (create, deps) {
|
|---|
| 16820 | currentHookNameInDev = 'useEffect';
|
|---|
| 16821 | mountHookTypesDev();
|
|---|
| 16822 | checkDepsAreArrayDev(deps);
|
|---|
| 16823 | return mountEffect(create, deps);
|
|---|
| 16824 | },
|
|---|
| 16825 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 16826 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 16827 | mountHookTypesDev();
|
|---|
| 16828 | checkDepsAreArrayDev(deps);
|
|---|
| 16829 | return mountImperativeHandle(ref, create, deps);
|
|---|
| 16830 | },
|
|---|
| 16831 | useInsertionEffect: function (create, deps) {
|
|---|
| 16832 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 16833 | mountHookTypesDev();
|
|---|
| 16834 | checkDepsAreArrayDev(deps);
|
|---|
| 16835 | return mountInsertionEffect(create, deps);
|
|---|
| 16836 | },
|
|---|
| 16837 | useLayoutEffect: function (create, deps) {
|
|---|
| 16838 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 16839 | mountHookTypesDev();
|
|---|
| 16840 | checkDepsAreArrayDev(deps);
|
|---|
| 16841 | return mountLayoutEffect(create, deps);
|
|---|
| 16842 | },
|
|---|
| 16843 | useMemo: function (create, deps) {
|
|---|
| 16844 | currentHookNameInDev = 'useMemo';
|
|---|
| 16845 | mountHookTypesDev();
|
|---|
| 16846 | checkDepsAreArrayDev(deps);
|
|---|
| 16847 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16848 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16849 |
|
|---|
| 16850 | try {
|
|---|
| 16851 | return mountMemo(create, deps);
|
|---|
| 16852 | } finally {
|
|---|
| 16853 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16854 | }
|
|---|
| 16855 | },
|
|---|
| 16856 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 16857 | currentHookNameInDev = 'useReducer';
|
|---|
| 16858 | mountHookTypesDev();
|
|---|
| 16859 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16860 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16861 |
|
|---|
| 16862 | try {
|
|---|
| 16863 | return mountReducer(reducer, initialArg, init);
|
|---|
| 16864 | } finally {
|
|---|
| 16865 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16866 | }
|
|---|
| 16867 | },
|
|---|
| 16868 | useRef: function (initialValue) {
|
|---|
| 16869 | currentHookNameInDev = 'useRef';
|
|---|
| 16870 | mountHookTypesDev();
|
|---|
| 16871 | return mountRef(initialValue);
|
|---|
| 16872 | },
|
|---|
| 16873 | useState: function (initialState) {
|
|---|
| 16874 | currentHookNameInDev = 'useState';
|
|---|
| 16875 | mountHookTypesDev();
|
|---|
| 16876 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16877 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16878 |
|
|---|
| 16879 | try {
|
|---|
| 16880 | return mountState(initialState);
|
|---|
| 16881 | } finally {
|
|---|
| 16882 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16883 | }
|
|---|
| 16884 | },
|
|---|
| 16885 | useDebugValue: function (value, formatterFn) {
|
|---|
| 16886 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 16887 | mountHookTypesDev();
|
|---|
| 16888 | return mountDebugValue();
|
|---|
| 16889 | },
|
|---|
| 16890 | useDeferredValue: function (value) {
|
|---|
| 16891 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 16892 | mountHookTypesDev();
|
|---|
| 16893 | return mountDeferredValue(value);
|
|---|
| 16894 | },
|
|---|
| 16895 | useTransition: function () {
|
|---|
| 16896 | currentHookNameInDev = 'useTransition';
|
|---|
| 16897 | mountHookTypesDev();
|
|---|
| 16898 | return mountTransition();
|
|---|
| 16899 | },
|
|---|
| 16900 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 16901 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 16902 | mountHookTypesDev();
|
|---|
| 16903 | return mountMutableSource();
|
|---|
| 16904 | },
|
|---|
| 16905 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 16906 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 16907 | mountHookTypesDev();
|
|---|
| 16908 | return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|---|
| 16909 | },
|
|---|
| 16910 | useId: function () {
|
|---|
| 16911 | currentHookNameInDev = 'useId';
|
|---|
| 16912 | mountHookTypesDev();
|
|---|
| 16913 | return mountId();
|
|---|
| 16914 | },
|
|---|
| 16915 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 16916 | };
|
|---|
| 16917 |
|
|---|
| 16918 | HooksDispatcherOnMountWithHookTypesInDEV = {
|
|---|
| 16919 | readContext: function (context) {
|
|---|
| 16920 | return readContext(context);
|
|---|
| 16921 | },
|
|---|
| 16922 | useCallback: function (callback, deps) {
|
|---|
| 16923 | currentHookNameInDev = 'useCallback';
|
|---|
| 16924 | updateHookTypesDev();
|
|---|
| 16925 | return mountCallback(callback, deps);
|
|---|
| 16926 | },
|
|---|
| 16927 | useContext: function (context) {
|
|---|
| 16928 | currentHookNameInDev = 'useContext';
|
|---|
| 16929 | updateHookTypesDev();
|
|---|
| 16930 | return readContext(context);
|
|---|
| 16931 | },
|
|---|
| 16932 | useEffect: function (create, deps) {
|
|---|
| 16933 | currentHookNameInDev = 'useEffect';
|
|---|
| 16934 | updateHookTypesDev();
|
|---|
| 16935 | return mountEffect(create, deps);
|
|---|
| 16936 | },
|
|---|
| 16937 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 16938 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 16939 | updateHookTypesDev();
|
|---|
| 16940 | return mountImperativeHandle(ref, create, deps);
|
|---|
| 16941 | },
|
|---|
| 16942 | useInsertionEffect: function (create, deps) {
|
|---|
| 16943 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 16944 | updateHookTypesDev();
|
|---|
| 16945 | return mountInsertionEffect(create, deps);
|
|---|
| 16946 | },
|
|---|
| 16947 | useLayoutEffect: function (create, deps) {
|
|---|
| 16948 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 16949 | updateHookTypesDev();
|
|---|
| 16950 | return mountLayoutEffect(create, deps);
|
|---|
| 16951 | },
|
|---|
| 16952 | useMemo: function (create, deps) {
|
|---|
| 16953 | currentHookNameInDev = 'useMemo';
|
|---|
| 16954 | updateHookTypesDev();
|
|---|
| 16955 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16956 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16957 |
|
|---|
| 16958 | try {
|
|---|
| 16959 | return mountMemo(create, deps);
|
|---|
| 16960 | } finally {
|
|---|
| 16961 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16962 | }
|
|---|
| 16963 | },
|
|---|
| 16964 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 16965 | currentHookNameInDev = 'useReducer';
|
|---|
| 16966 | updateHookTypesDev();
|
|---|
| 16967 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16968 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16969 |
|
|---|
| 16970 | try {
|
|---|
| 16971 | return mountReducer(reducer, initialArg, init);
|
|---|
| 16972 | } finally {
|
|---|
| 16973 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16974 | }
|
|---|
| 16975 | },
|
|---|
| 16976 | useRef: function (initialValue) {
|
|---|
| 16977 | currentHookNameInDev = 'useRef';
|
|---|
| 16978 | updateHookTypesDev();
|
|---|
| 16979 | return mountRef(initialValue);
|
|---|
| 16980 | },
|
|---|
| 16981 | useState: function (initialState) {
|
|---|
| 16982 | currentHookNameInDev = 'useState';
|
|---|
| 16983 | updateHookTypesDev();
|
|---|
| 16984 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 16985 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 16986 |
|
|---|
| 16987 | try {
|
|---|
| 16988 | return mountState(initialState);
|
|---|
| 16989 | } finally {
|
|---|
| 16990 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 16991 | }
|
|---|
| 16992 | },
|
|---|
| 16993 | useDebugValue: function (value, formatterFn) {
|
|---|
| 16994 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 16995 | updateHookTypesDev();
|
|---|
| 16996 | return mountDebugValue();
|
|---|
| 16997 | },
|
|---|
| 16998 | useDeferredValue: function (value) {
|
|---|
| 16999 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17000 | updateHookTypesDev();
|
|---|
| 17001 | return mountDeferredValue(value);
|
|---|
| 17002 | },
|
|---|
| 17003 | useTransition: function () {
|
|---|
| 17004 | currentHookNameInDev = 'useTransition';
|
|---|
| 17005 | updateHookTypesDev();
|
|---|
| 17006 | return mountTransition();
|
|---|
| 17007 | },
|
|---|
| 17008 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17009 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17010 | updateHookTypesDev();
|
|---|
| 17011 | return mountMutableSource();
|
|---|
| 17012 | },
|
|---|
| 17013 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17014 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17015 | updateHookTypesDev();
|
|---|
| 17016 | return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|---|
| 17017 | },
|
|---|
| 17018 | useId: function () {
|
|---|
| 17019 | currentHookNameInDev = 'useId';
|
|---|
| 17020 | updateHookTypesDev();
|
|---|
| 17021 | return mountId();
|
|---|
| 17022 | },
|
|---|
| 17023 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17024 | };
|
|---|
| 17025 |
|
|---|
| 17026 | HooksDispatcherOnUpdateInDEV = {
|
|---|
| 17027 | readContext: function (context) {
|
|---|
| 17028 | return readContext(context);
|
|---|
| 17029 | },
|
|---|
| 17030 | useCallback: function (callback, deps) {
|
|---|
| 17031 | currentHookNameInDev = 'useCallback';
|
|---|
| 17032 | updateHookTypesDev();
|
|---|
| 17033 | return updateCallback(callback, deps);
|
|---|
| 17034 | },
|
|---|
| 17035 | useContext: function (context) {
|
|---|
| 17036 | currentHookNameInDev = 'useContext';
|
|---|
| 17037 | updateHookTypesDev();
|
|---|
| 17038 | return readContext(context);
|
|---|
| 17039 | },
|
|---|
| 17040 | useEffect: function (create, deps) {
|
|---|
| 17041 | currentHookNameInDev = 'useEffect';
|
|---|
| 17042 | updateHookTypesDev();
|
|---|
| 17043 | return updateEffect(create, deps);
|
|---|
| 17044 | },
|
|---|
| 17045 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 17046 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 17047 | updateHookTypesDev();
|
|---|
| 17048 | return updateImperativeHandle(ref, create, deps);
|
|---|
| 17049 | },
|
|---|
| 17050 | useInsertionEffect: function (create, deps) {
|
|---|
| 17051 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 17052 | updateHookTypesDev();
|
|---|
| 17053 | return updateInsertionEffect(create, deps);
|
|---|
| 17054 | },
|
|---|
| 17055 | useLayoutEffect: function (create, deps) {
|
|---|
| 17056 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 17057 | updateHookTypesDev();
|
|---|
| 17058 | return updateLayoutEffect(create, deps);
|
|---|
| 17059 | },
|
|---|
| 17060 | useMemo: function (create, deps) {
|
|---|
| 17061 | currentHookNameInDev = 'useMemo';
|
|---|
| 17062 | updateHookTypesDev();
|
|---|
| 17063 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17064 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17065 |
|
|---|
| 17066 | try {
|
|---|
| 17067 | return updateMemo(create, deps);
|
|---|
| 17068 | } finally {
|
|---|
| 17069 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17070 | }
|
|---|
| 17071 | },
|
|---|
| 17072 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 17073 | currentHookNameInDev = 'useReducer';
|
|---|
| 17074 | updateHookTypesDev();
|
|---|
| 17075 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17076 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17077 |
|
|---|
| 17078 | try {
|
|---|
| 17079 | return updateReducer(reducer, initialArg, init);
|
|---|
| 17080 | } finally {
|
|---|
| 17081 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17082 | }
|
|---|
| 17083 | },
|
|---|
| 17084 | useRef: function (initialValue) {
|
|---|
| 17085 | currentHookNameInDev = 'useRef';
|
|---|
| 17086 | updateHookTypesDev();
|
|---|
| 17087 | return updateRef();
|
|---|
| 17088 | },
|
|---|
| 17089 | useState: function (initialState) {
|
|---|
| 17090 | currentHookNameInDev = 'useState';
|
|---|
| 17091 | updateHookTypesDev();
|
|---|
| 17092 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17093 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17094 |
|
|---|
| 17095 | try {
|
|---|
| 17096 | return updateState(initialState);
|
|---|
| 17097 | } finally {
|
|---|
| 17098 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17099 | }
|
|---|
| 17100 | },
|
|---|
| 17101 | useDebugValue: function (value, formatterFn) {
|
|---|
| 17102 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 17103 | updateHookTypesDev();
|
|---|
| 17104 | return updateDebugValue();
|
|---|
| 17105 | },
|
|---|
| 17106 | useDeferredValue: function (value) {
|
|---|
| 17107 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17108 | updateHookTypesDev();
|
|---|
| 17109 | return updateDeferredValue(value);
|
|---|
| 17110 | },
|
|---|
| 17111 | useTransition: function () {
|
|---|
| 17112 | currentHookNameInDev = 'useTransition';
|
|---|
| 17113 | updateHookTypesDev();
|
|---|
| 17114 | return updateTransition();
|
|---|
| 17115 | },
|
|---|
| 17116 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17117 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17118 | updateHookTypesDev();
|
|---|
| 17119 | return updateMutableSource();
|
|---|
| 17120 | },
|
|---|
| 17121 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17122 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17123 | updateHookTypesDev();
|
|---|
| 17124 | return updateSyncExternalStore(subscribe, getSnapshot);
|
|---|
| 17125 | },
|
|---|
| 17126 | useId: function () {
|
|---|
| 17127 | currentHookNameInDev = 'useId';
|
|---|
| 17128 | updateHookTypesDev();
|
|---|
| 17129 | return updateId();
|
|---|
| 17130 | },
|
|---|
| 17131 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17132 | };
|
|---|
| 17133 |
|
|---|
| 17134 | HooksDispatcherOnRerenderInDEV = {
|
|---|
| 17135 | readContext: function (context) {
|
|---|
| 17136 | return readContext(context);
|
|---|
| 17137 | },
|
|---|
| 17138 | useCallback: function (callback, deps) {
|
|---|
| 17139 | currentHookNameInDev = 'useCallback';
|
|---|
| 17140 | updateHookTypesDev();
|
|---|
| 17141 | return updateCallback(callback, deps);
|
|---|
| 17142 | },
|
|---|
| 17143 | useContext: function (context) {
|
|---|
| 17144 | currentHookNameInDev = 'useContext';
|
|---|
| 17145 | updateHookTypesDev();
|
|---|
| 17146 | return readContext(context);
|
|---|
| 17147 | },
|
|---|
| 17148 | useEffect: function (create, deps) {
|
|---|
| 17149 | currentHookNameInDev = 'useEffect';
|
|---|
| 17150 | updateHookTypesDev();
|
|---|
| 17151 | return updateEffect(create, deps);
|
|---|
| 17152 | },
|
|---|
| 17153 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 17154 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 17155 | updateHookTypesDev();
|
|---|
| 17156 | return updateImperativeHandle(ref, create, deps);
|
|---|
| 17157 | },
|
|---|
| 17158 | useInsertionEffect: function (create, deps) {
|
|---|
| 17159 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 17160 | updateHookTypesDev();
|
|---|
| 17161 | return updateInsertionEffect(create, deps);
|
|---|
| 17162 | },
|
|---|
| 17163 | useLayoutEffect: function (create, deps) {
|
|---|
| 17164 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 17165 | updateHookTypesDev();
|
|---|
| 17166 | return updateLayoutEffect(create, deps);
|
|---|
| 17167 | },
|
|---|
| 17168 | useMemo: function (create, deps) {
|
|---|
| 17169 | currentHookNameInDev = 'useMemo';
|
|---|
| 17170 | updateHookTypesDev();
|
|---|
| 17171 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17172 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
|---|
| 17173 |
|
|---|
| 17174 | try {
|
|---|
| 17175 | return updateMemo(create, deps);
|
|---|
| 17176 | } finally {
|
|---|
| 17177 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17178 | }
|
|---|
| 17179 | },
|
|---|
| 17180 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 17181 | currentHookNameInDev = 'useReducer';
|
|---|
| 17182 | updateHookTypesDev();
|
|---|
| 17183 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17184 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
|---|
| 17185 |
|
|---|
| 17186 | try {
|
|---|
| 17187 | return rerenderReducer(reducer, initialArg, init);
|
|---|
| 17188 | } finally {
|
|---|
| 17189 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17190 | }
|
|---|
| 17191 | },
|
|---|
| 17192 | useRef: function (initialValue) {
|
|---|
| 17193 | currentHookNameInDev = 'useRef';
|
|---|
| 17194 | updateHookTypesDev();
|
|---|
| 17195 | return updateRef();
|
|---|
| 17196 | },
|
|---|
| 17197 | useState: function (initialState) {
|
|---|
| 17198 | currentHookNameInDev = 'useState';
|
|---|
| 17199 | updateHookTypesDev();
|
|---|
| 17200 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17201 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV;
|
|---|
| 17202 |
|
|---|
| 17203 | try {
|
|---|
| 17204 | return rerenderState(initialState);
|
|---|
| 17205 | } finally {
|
|---|
| 17206 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17207 | }
|
|---|
| 17208 | },
|
|---|
| 17209 | useDebugValue: function (value, formatterFn) {
|
|---|
| 17210 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 17211 | updateHookTypesDev();
|
|---|
| 17212 | return updateDebugValue();
|
|---|
| 17213 | },
|
|---|
| 17214 | useDeferredValue: function (value) {
|
|---|
| 17215 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17216 | updateHookTypesDev();
|
|---|
| 17217 | return rerenderDeferredValue(value);
|
|---|
| 17218 | },
|
|---|
| 17219 | useTransition: function () {
|
|---|
| 17220 | currentHookNameInDev = 'useTransition';
|
|---|
| 17221 | updateHookTypesDev();
|
|---|
| 17222 | return rerenderTransition();
|
|---|
| 17223 | },
|
|---|
| 17224 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17225 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17226 | updateHookTypesDev();
|
|---|
| 17227 | return updateMutableSource();
|
|---|
| 17228 | },
|
|---|
| 17229 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17230 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17231 | updateHookTypesDev();
|
|---|
| 17232 | return updateSyncExternalStore(subscribe, getSnapshot);
|
|---|
| 17233 | },
|
|---|
| 17234 | useId: function () {
|
|---|
| 17235 | currentHookNameInDev = 'useId';
|
|---|
| 17236 | updateHookTypesDev();
|
|---|
| 17237 | return updateId();
|
|---|
| 17238 | },
|
|---|
| 17239 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17240 | };
|
|---|
| 17241 |
|
|---|
| 17242 | InvalidNestedHooksDispatcherOnMountInDEV = {
|
|---|
| 17243 | readContext: function (context) {
|
|---|
| 17244 | warnInvalidContextAccess();
|
|---|
| 17245 | return readContext(context);
|
|---|
| 17246 | },
|
|---|
| 17247 | useCallback: function (callback, deps) {
|
|---|
| 17248 | currentHookNameInDev = 'useCallback';
|
|---|
| 17249 | warnInvalidHookAccess();
|
|---|
| 17250 | mountHookTypesDev();
|
|---|
| 17251 | return mountCallback(callback, deps);
|
|---|
| 17252 | },
|
|---|
| 17253 | useContext: function (context) {
|
|---|
| 17254 | currentHookNameInDev = 'useContext';
|
|---|
| 17255 | warnInvalidHookAccess();
|
|---|
| 17256 | mountHookTypesDev();
|
|---|
| 17257 | return readContext(context);
|
|---|
| 17258 | },
|
|---|
| 17259 | useEffect: function (create, deps) {
|
|---|
| 17260 | currentHookNameInDev = 'useEffect';
|
|---|
| 17261 | warnInvalidHookAccess();
|
|---|
| 17262 | mountHookTypesDev();
|
|---|
| 17263 | return mountEffect(create, deps);
|
|---|
| 17264 | },
|
|---|
| 17265 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 17266 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 17267 | warnInvalidHookAccess();
|
|---|
| 17268 | mountHookTypesDev();
|
|---|
| 17269 | return mountImperativeHandle(ref, create, deps);
|
|---|
| 17270 | },
|
|---|
| 17271 | useInsertionEffect: function (create, deps) {
|
|---|
| 17272 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 17273 | warnInvalidHookAccess();
|
|---|
| 17274 | mountHookTypesDev();
|
|---|
| 17275 | return mountInsertionEffect(create, deps);
|
|---|
| 17276 | },
|
|---|
| 17277 | useLayoutEffect: function (create, deps) {
|
|---|
| 17278 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 17279 | warnInvalidHookAccess();
|
|---|
| 17280 | mountHookTypesDev();
|
|---|
| 17281 | return mountLayoutEffect(create, deps);
|
|---|
| 17282 | },
|
|---|
| 17283 | useMemo: function (create, deps) {
|
|---|
| 17284 | currentHookNameInDev = 'useMemo';
|
|---|
| 17285 | warnInvalidHookAccess();
|
|---|
| 17286 | mountHookTypesDev();
|
|---|
| 17287 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17288 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 17289 |
|
|---|
| 17290 | try {
|
|---|
| 17291 | return mountMemo(create, deps);
|
|---|
| 17292 | } finally {
|
|---|
| 17293 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17294 | }
|
|---|
| 17295 | },
|
|---|
| 17296 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 17297 | currentHookNameInDev = 'useReducer';
|
|---|
| 17298 | warnInvalidHookAccess();
|
|---|
| 17299 | mountHookTypesDev();
|
|---|
| 17300 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17301 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 17302 |
|
|---|
| 17303 | try {
|
|---|
| 17304 | return mountReducer(reducer, initialArg, init);
|
|---|
| 17305 | } finally {
|
|---|
| 17306 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17307 | }
|
|---|
| 17308 | },
|
|---|
| 17309 | useRef: function (initialValue) {
|
|---|
| 17310 | currentHookNameInDev = 'useRef';
|
|---|
| 17311 | warnInvalidHookAccess();
|
|---|
| 17312 | mountHookTypesDev();
|
|---|
| 17313 | return mountRef(initialValue);
|
|---|
| 17314 | },
|
|---|
| 17315 | useState: function (initialState) {
|
|---|
| 17316 | currentHookNameInDev = 'useState';
|
|---|
| 17317 | warnInvalidHookAccess();
|
|---|
| 17318 | mountHookTypesDev();
|
|---|
| 17319 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17320 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV;
|
|---|
| 17321 |
|
|---|
| 17322 | try {
|
|---|
| 17323 | return mountState(initialState);
|
|---|
| 17324 | } finally {
|
|---|
| 17325 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17326 | }
|
|---|
| 17327 | },
|
|---|
| 17328 | useDebugValue: function (value, formatterFn) {
|
|---|
| 17329 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 17330 | warnInvalidHookAccess();
|
|---|
| 17331 | mountHookTypesDev();
|
|---|
| 17332 | return mountDebugValue();
|
|---|
| 17333 | },
|
|---|
| 17334 | useDeferredValue: function (value) {
|
|---|
| 17335 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17336 | warnInvalidHookAccess();
|
|---|
| 17337 | mountHookTypesDev();
|
|---|
| 17338 | return mountDeferredValue(value);
|
|---|
| 17339 | },
|
|---|
| 17340 | useTransition: function () {
|
|---|
| 17341 | currentHookNameInDev = 'useTransition';
|
|---|
| 17342 | warnInvalidHookAccess();
|
|---|
| 17343 | mountHookTypesDev();
|
|---|
| 17344 | return mountTransition();
|
|---|
| 17345 | },
|
|---|
| 17346 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17347 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17348 | warnInvalidHookAccess();
|
|---|
| 17349 | mountHookTypesDev();
|
|---|
| 17350 | return mountMutableSource();
|
|---|
| 17351 | },
|
|---|
| 17352 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17353 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17354 | warnInvalidHookAccess();
|
|---|
| 17355 | mountHookTypesDev();
|
|---|
| 17356 | return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|---|
| 17357 | },
|
|---|
| 17358 | useId: function () {
|
|---|
| 17359 | currentHookNameInDev = 'useId';
|
|---|
| 17360 | warnInvalidHookAccess();
|
|---|
| 17361 | mountHookTypesDev();
|
|---|
| 17362 | return mountId();
|
|---|
| 17363 | },
|
|---|
| 17364 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17365 | };
|
|---|
| 17366 |
|
|---|
| 17367 | InvalidNestedHooksDispatcherOnUpdateInDEV = {
|
|---|
| 17368 | readContext: function (context) {
|
|---|
| 17369 | warnInvalidContextAccess();
|
|---|
| 17370 | return readContext(context);
|
|---|
| 17371 | },
|
|---|
| 17372 | useCallback: function (callback, deps) {
|
|---|
| 17373 | currentHookNameInDev = 'useCallback';
|
|---|
| 17374 | warnInvalidHookAccess();
|
|---|
| 17375 | updateHookTypesDev();
|
|---|
| 17376 | return updateCallback(callback, deps);
|
|---|
| 17377 | },
|
|---|
| 17378 | useContext: function (context) {
|
|---|
| 17379 | currentHookNameInDev = 'useContext';
|
|---|
| 17380 | warnInvalidHookAccess();
|
|---|
| 17381 | updateHookTypesDev();
|
|---|
| 17382 | return readContext(context);
|
|---|
| 17383 | },
|
|---|
| 17384 | useEffect: function (create, deps) {
|
|---|
| 17385 | currentHookNameInDev = 'useEffect';
|
|---|
| 17386 | warnInvalidHookAccess();
|
|---|
| 17387 | updateHookTypesDev();
|
|---|
| 17388 | return updateEffect(create, deps);
|
|---|
| 17389 | },
|
|---|
| 17390 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 17391 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 17392 | warnInvalidHookAccess();
|
|---|
| 17393 | updateHookTypesDev();
|
|---|
| 17394 | return updateImperativeHandle(ref, create, deps);
|
|---|
| 17395 | },
|
|---|
| 17396 | useInsertionEffect: function (create, deps) {
|
|---|
| 17397 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 17398 | warnInvalidHookAccess();
|
|---|
| 17399 | updateHookTypesDev();
|
|---|
| 17400 | return updateInsertionEffect(create, deps);
|
|---|
| 17401 | },
|
|---|
| 17402 | useLayoutEffect: function (create, deps) {
|
|---|
| 17403 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 17404 | warnInvalidHookAccess();
|
|---|
| 17405 | updateHookTypesDev();
|
|---|
| 17406 | return updateLayoutEffect(create, deps);
|
|---|
| 17407 | },
|
|---|
| 17408 | useMemo: function (create, deps) {
|
|---|
| 17409 | currentHookNameInDev = 'useMemo';
|
|---|
| 17410 | warnInvalidHookAccess();
|
|---|
| 17411 | updateHookTypesDev();
|
|---|
| 17412 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17413 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17414 |
|
|---|
| 17415 | try {
|
|---|
| 17416 | return updateMemo(create, deps);
|
|---|
| 17417 | } finally {
|
|---|
| 17418 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17419 | }
|
|---|
| 17420 | },
|
|---|
| 17421 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 17422 | currentHookNameInDev = 'useReducer';
|
|---|
| 17423 | warnInvalidHookAccess();
|
|---|
| 17424 | updateHookTypesDev();
|
|---|
| 17425 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17426 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17427 |
|
|---|
| 17428 | try {
|
|---|
| 17429 | return updateReducer(reducer, initialArg, init);
|
|---|
| 17430 | } finally {
|
|---|
| 17431 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17432 | }
|
|---|
| 17433 | },
|
|---|
| 17434 | useRef: function (initialValue) {
|
|---|
| 17435 | currentHookNameInDev = 'useRef';
|
|---|
| 17436 | warnInvalidHookAccess();
|
|---|
| 17437 | updateHookTypesDev();
|
|---|
| 17438 | return updateRef();
|
|---|
| 17439 | },
|
|---|
| 17440 | useState: function (initialState) {
|
|---|
| 17441 | currentHookNameInDev = 'useState';
|
|---|
| 17442 | warnInvalidHookAccess();
|
|---|
| 17443 | updateHookTypesDev();
|
|---|
| 17444 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17445 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17446 |
|
|---|
| 17447 | try {
|
|---|
| 17448 | return updateState(initialState);
|
|---|
| 17449 | } finally {
|
|---|
| 17450 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17451 | }
|
|---|
| 17452 | },
|
|---|
| 17453 | useDebugValue: function (value, formatterFn) {
|
|---|
| 17454 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 17455 | warnInvalidHookAccess();
|
|---|
| 17456 | updateHookTypesDev();
|
|---|
| 17457 | return updateDebugValue();
|
|---|
| 17458 | },
|
|---|
| 17459 | useDeferredValue: function (value) {
|
|---|
| 17460 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17461 | warnInvalidHookAccess();
|
|---|
| 17462 | updateHookTypesDev();
|
|---|
| 17463 | return updateDeferredValue(value);
|
|---|
| 17464 | },
|
|---|
| 17465 | useTransition: function () {
|
|---|
| 17466 | currentHookNameInDev = 'useTransition';
|
|---|
| 17467 | warnInvalidHookAccess();
|
|---|
| 17468 | updateHookTypesDev();
|
|---|
| 17469 | return updateTransition();
|
|---|
| 17470 | },
|
|---|
| 17471 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17472 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17473 | warnInvalidHookAccess();
|
|---|
| 17474 | updateHookTypesDev();
|
|---|
| 17475 | return updateMutableSource();
|
|---|
| 17476 | },
|
|---|
| 17477 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17478 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17479 | warnInvalidHookAccess();
|
|---|
| 17480 | updateHookTypesDev();
|
|---|
| 17481 | return updateSyncExternalStore(subscribe, getSnapshot);
|
|---|
| 17482 | },
|
|---|
| 17483 | useId: function () {
|
|---|
| 17484 | currentHookNameInDev = 'useId';
|
|---|
| 17485 | warnInvalidHookAccess();
|
|---|
| 17486 | updateHookTypesDev();
|
|---|
| 17487 | return updateId();
|
|---|
| 17488 | },
|
|---|
| 17489 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17490 | };
|
|---|
| 17491 |
|
|---|
| 17492 | InvalidNestedHooksDispatcherOnRerenderInDEV = {
|
|---|
| 17493 | readContext: function (context) {
|
|---|
| 17494 | warnInvalidContextAccess();
|
|---|
| 17495 | return readContext(context);
|
|---|
| 17496 | },
|
|---|
| 17497 | useCallback: function (callback, deps) {
|
|---|
| 17498 | currentHookNameInDev = 'useCallback';
|
|---|
| 17499 | warnInvalidHookAccess();
|
|---|
| 17500 | updateHookTypesDev();
|
|---|
| 17501 | return updateCallback(callback, deps);
|
|---|
| 17502 | },
|
|---|
| 17503 | useContext: function (context) {
|
|---|
| 17504 | currentHookNameInDev = 'useContext';
|
|---|
| 17505 | warnInvalidHookAccess();
|
|---|
| 17506 | updateHookTypesDev();
|
|---|
| 17507 | return readContext(context);
|
|---|
| 17508 | },
|
|---|
| 17509 | useEffect: function (create, deps) {
|
|---|
| 17510 | currentHookNameInDev = 'useEffect';
|
|---|
| 17511 | warnInvalidHookAccess();
|
|---|
| 17512 | updateHookTypesDev();
|
|---|
| 17513 | return updateEffect(create, deps);
|
|---|
| 17514 | },
|
|---|
| 17515 | useImperativeHandle: function (ref, create, deps) {
|
|---|
| 17516 | currentHookNameInDev = 'useImperativeHandle';
|
|---|
| 17517 | warnInvalidHookAccess();
|
|---|
| 17518 | updateHookTypesDev();
|
|---|
| 17519 | return updateImperativeHandle(ref, create, deps);
|
|---|
| 17520 | },
|
|---|
| 17521 | useInsertionEffect: function (create, deps) {
|
|---|
| 17522 | currentHookNameInDev = 'useInsertionEffect';
|
|---|
| 17523 | warnInvalidHookAccess();
|
|---|
| 17524 | updateHookTypesDev();
|
|---|
| 17525 | return updateInsertionEffect(create, deps);
|
|---|
| 17526 | },
|
|---|
| 17527 | useLayoutEffect: function (create, deps) {
|
|---|
| 17528 | currentHookNameInDev = 'useLayoutEffect';
|
|---|
| 17529 | warnInvalidHookAccess();
|
|---|
| 17530 | updateHookTypesDev();
|
|---|
| 17531 | return updateLayoutEffect(create, deps);
|
|---|
| 17532 | },
|
|---|
| 17533 | useMemo: function (create, deps) {
|
|---|
| 17534 | currentHookNameInDev = 'useMemo';
|
|---|
| 17535 | warnInvalidHookAccess();
|
|---|
| 17536 | updateHookTypesDev();
|
|---|
| 17537 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17538 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17539 |
|
|---|
| 17540 | try {
|
|---|
| 17541 | return updateMemo(create, deps);
|
|---|
| 17542 | } finally {
|
|---|
| 17543 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17544 | }
|
|---|
| 17545 | },
|
|---|
| 17546 | useReducer: function (reducer, initialArg, init) {
|
|---|
| 17547 | currentHookNameInDev = 'useReducer';
|
|---|
| 17548 | warnInvalidHookAccess();
|
|---|
| 17549 | updateHookTypesDev();
|
|---|
| 17550 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17551 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17552 |
|
|---|
| 17553 | try {
|
|---|
| 17554 | return rerenderReducer(reducer, initialArg, init);
|
|---|
| 17555 | } finally {
|
|---|
| 17556 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17557 | }
|
|---|
| 17558 | },
|
|---|
| 17559 | useRef: function (initialValue) {
|
|---|
| 17560 | currentHookNameInDev = 'useRef';
|
|---|
| 17561 | warnInvalidHookAccess();
|
|---|
| 17562 | updateHookTypesDev();
|
|---|
| 17563 | return updateRef();
|
|---|
| 17564 | },
|
|---|
| 17565 | useState: function (initialState) {
|
|---|
| 17566 | currentHookNameInDev = 'useState';
|
|---|
| 17567 | warnInvalidHookAccess();
|
|---|
| 17568 | updateHookTypesDev();
|
|---|
| 17569 | var prevDispatcher = ReactCurrentDispatcher$1.current;
|
|---|
| 17570 | ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV;
|
|---|
| 17571 |
|
|---|
| 17572 | try {
|
|---|
| 17573 | return rerenderState(initialState);
|
|---|
| 17574 | } finally {
|
|---|
| 17575 | ReactCurrentDispatcher$1.current = prevDispatcher;
|
|---|
| 17576 | }
|
|---|
| 17577 | },
|
|---|
| 17578 | useDebugValue: function (value, formatterFn) {
|
|---|
| 17579 | currentHookNameInDev = 'useDebugValue';
|
|---|
| 17580 | warnInvalidHookAccess();
|
|---|
| 17581 | updateHookTypesDev();
|
|---|
| 17582 | return updateDebugValue();
|
|---|
| 17583 | },
|
|---|
| 17584 | useDeferredValue: function (value) {
|
|---|
| 17585 | currentHookNameInDev = 'useDeferredValue';
|
|---|
| 17586 | warnInvalidHookAccess();
|
|---|
| 17587 | updateHookTypesDev();
|
|---|
| 17588 | return rerenderDeferredValue(value);
|
|---|
| 17589 | },
|
|---|
| 17590 | useTransition: function () {
|
|---|
| 17591 | currentHookNameInDev = 'useTransition';
|
|---|
| 17592 | warnInvalidHookAccess();
|
|---|
| 17593 | updateHookTypesDev();
|
|---|
| 17594 | return rerenderTransition();
|
|---|
| 17595 | },
|
|---|
| 17596 | useMutableSource: function (source, getSnapshot, subscribe) {
|
|---|
| 17597 | currentHookNameInDev = 'useMutableSource';
|
|---|
| 17598 | warnInvalidHookAccess();
|
|---|
| 17599 | updateHookTypesDev();
|
|---|
| 17600 | return updateMutableSource();
|
|---|
| 17601 | },
|
|---|
| 17602 | useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) {
|
|---|
| 17603 | currentHookNameInDev = 'useSyncExternalStore';
|
|---|
| 17604 | warnInvalidHookAccess();
|
|---|
| 17605 | updateHookTypesDev();
|
|---|
| 17606 | return updateSyncExternalStore(subscribe, getSnapshot);
|
|---|
| 17607 | },
|
|---|
| 17608 | useId: function () {
|
|---|
| 17609 | currentHookNameInDev = 'useId';
|
|---|
| 17610 | warnInvalidHookAccess();
|
|---|
| 17611 | updateHookTypesDev();
|
|---|
| 17612 | return updateId();
|
|---|
| 17613 | },
|
|---|
| 17614 | unstable_isNewReconciler: enableNewReconciler
|
|---|
| 17615 | };
|
|---|
| 17616 | }
|
|---|
| 17617 |
|
|---|
| 17618 | var now$1 = Scheduler.unstable_now;
|
|---|
| 17619 | var commitTime = 0;
|
|---|
| 17620 | var layoutEffectStartTime = -1;
|
|---|
| 17621 | var profilerStartTime = -1;
|
|---|
| 17622 | var passiveEffectStartTime = -1;
|
|---|
| 17623 | /**
|
|---|
| 17624 | * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect).
|
|---|
| 17625 | *
|
|---|
| 17626 | * The overall sequence is:
|
|---|
| 17627 | * 1. render
|
|---|
| 17628 | * 2. commit (and call `onRender`, `onCommit`)
|
|---|
| 17629 | * 3. check for nested updates
|
|---|
| 17630 | * 4. flush passive effects (and call `onPostCommit`)
|
|---|
| 17631 | *
|
|---|
| 17632 | * Nested updates are identified in step 3 above,
|
|---|
| 17633 | * but step 4 still applies to the work that was just committed.
|
|---|
| 17634 | * We use two flags to track nested updates then:
|
|---|
| 17635 | * one tracks whether the upcoming update is a nested update,
|
|---|
| 17636 | * and the other tracks whether the current update was a nested update.
|
|---|
| 17637 | * The first value gets synced to the second at the start of the render phase.
|
|---|
| 17638 | */
|
|---|
| 17639 |
|
|---|
| 17640 | var currentUpdateIsNested = false;
|
|---|
| 17641 | var nestedUpdateScheduled = false;
|
|---|
| 17642 |
|
|---|
| 17643 | function isCurrentUpdateNested() {
|
|---|
| 17644 | return currentUpdateIsNested;
|
|---|
| 17645 | }
|
|---|
| 17646 |
|
|---|
| 17647 | function markNestedUpdateScheduled() {
|
|---|
| 17648 | {
|
|---|
| 17649 | nestedUpdateScheduled = true;
|
|---|
| 17650 | }
|
|---|
| 17651 | }
|
|---|
| 17652 |
|
|---|
| 17653 | function resetNestedUpdateFlag() {
|
|---|
| 17654 | {
|
|---|
| 17655 | currentUpdateIsNested = false;
|
|---|
| 17656 | nestedUpdateScheduled = false;
|
|---|
| 17657 | }
|
|---|
| 17658 | }
|
|---|
| 17659 |
|
|---|
| 17660 | function syncNestedUpdateFlag() {
|
|---|
| 17661 | {
|
|---|
| 17662 | currentUpdateIsNested = nestedUpdateScheduled;
|
|---|
| 17663 | nestedUpdateScheduled = false;
|
|---|
| 17664 | }
|
|---|
| 17665 | }
|
|---|
| 17666 |
|
|---|
| 17667 | function getCommitTime() {
|
|---|
| 17668 | return commitTime;
|
|---|
| 17669 | }
|
|---|
| 17670 |
|
|---|
| 17671 | function recordCommitTime() {
|
|---|
| 17672 |
|
|---|
| 17673 | commitTime = now$1();
|
|---|
| 17674 | }
|
|---|
| 17675 |
|
|---|
| 17676 | function startProfilerTimer(fiber) {
|
|---|
| 17677 |
|
|---|
| 17678 | profilerStartTime = now$1();
|
|---|
| 17679 |
|
|---|
| 17680 | if (fiber.actualStartTime < 0) {
|
|---|
| 17681 | fiber.actualStartTime = now$1();
|
|---|
| 17682 | }
|
|---|
| 17683 | }
|
|---|
| 17684 |
|
|---|
| 17685 | function stopProfilerTimerIfRunning(fiber) {
|
|---|
| 17686 |
|
|---|
| 17687 | profilerStartTime = -1;
|
|---|
| 17688 | }
|
|---|
| 17689 |
|
|---|
| 17690 | function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) {
|
|---|
| 17691 |
|
|---|
| 17692 | if (profilerStartTime >= 0) {
|
|---|
| 17693 | var elapsedTime = now$1() - profilerStartTime;
|
|---|
| 17694 | fiber.actualDuration += elapsedTime;
|
|---|
| 17695 |
|
|---|
| 17696 | if (overrideBaseTime) {
|
|---|
| 17697 | fiber.selfBaseDuration = elapsedTime;
|
|---|
| 17698 | }
|
|---|
| 17699 |
|
|---|
| 17700 | profilerStartTime = -1;
|
|---|
| 17701 | }
|
|---|
| 17702 | }
|
|---|
| 17703 |
|
|---|
| 17704 | function recordLayoutEffectDuration(fiber) {
|
|---|
| 17705 |
|
|---|
| 17706 | if (layoutEffectStartTime >= 0) {
|
|---|
| 17707 | var elapsedTime = now$1() - layoutEffectStartTime;
|
|---|
| 17708 | layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
|
|---|
| 17709 | // Or the root (for the DevTools Profiler to read)
|
|---|
| 17710 |
|
|---|
| 17711 | var parentFiber = fiber.return;
|
|---|
| 17712 |
|
|---|
| 17713 | while (parentFiber !== null) {
|
|---|
| 17714 | switch (parentFiber.tag) {
|
|---|
| 17715 | case HostRoot:
|
|---|
| 17716 | var root = parentFiber.stateNode;
|
|---|
| 17717 | root.effectDuration += elapsedTime;
|
|---|
| 17718 | return;
|
|---|
| 17719 |
|
|---|
| 17720 | case Profiler:
|
|---|
| 17721 | var parentStateNode = parentFiber.stateNode;
|
|---|
| 17722 | parentStateNode.effectDuration += elapsedTime;
|
|---|
| 17723 | return;
|
|---|
| 17724 | }
|
|---|
| 17725 |
|
|---|
| 17726 | parentFiber = parentFiber.return;
|
|---|
| 17727 | }
|
|---|
| 17728 | }
|
|---|
| 17729 | }
|
|---|
| 17730 |
|
|---|
| 17731 | function recordPassiveEffectDuration(fiber) {
|
|---|
| 17732 |
|
|---|
| 17733 | if (passiveEffectStartTime >= 0) {
|
|---|
| 17734 | var elapsedTime = now$1() - passiveEffectStartTime;
|
|---|
| 17735 | passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor
|
|---|
| 17736 | // Or the root (for the DevTools Profiler to read)
|
|---|
| 17737 |
|
|---|
| 17738 | var parentFiber = fiber.return;
|
|---|
| 17739 |
|
|---|
| 17740 | while (parentFiber !== null) {
|
|---|
| 17741 | switch (parentFiber.tag) {
|
|---|
| 17742 | case HostRoot:
|
|---|
| 17743 | var root = parentFiber.stateNode;
|
|---|
| 17744 |
|
|---|
| 17745 | if (root !== null) {
|
|---|
| 17746 | root.passiveEffectDuration += elapsedTime;
|
|---|
| 17747 | }
|
|---|
| 17748 |
|
|---|
| 17749 | return;
|
|---|
| 17750 |
|
|---|
| 17751 | case Profiler:
|
|---|
| 17752 | var parentStateNode = parentFiber.stateNode;
|
|---|
| 17753 |
|
|---|
| 17754 | if (parentStateNode !== null) {
|
|---|
| 17755 | // Detached fibers have their state node cleared out.
|
|---|
| 17756 | // In this case, the return pointer is also cleared out,
|
|---|
| 17757 | // so we won't be able to report the time spent in this Profiler's subtree.
|
|---|
| 17758 | parentStateNode.passiveEffectDuration += elapsedTime;
|
|---|
| 17759 | }
|
|---|
| 17760 |
|
|---|
| 17761 | return;
|
|---|
| 17762 | }
|
|---|
| 17763 |
|
|---|
| 17764 | parentFiber = parentFiber.return;
|
|---|
| 17765 | }
|
|---|
| 17766 | }
|
|---|
| 17767 | }
|
|---|
| 17768 |
|
|---|
| 17769 | function startLayoutEffectTimer() {
|
|---|
| 17770 |
|
|---|
| 17771 | layoutEffectStartTime = now$1();
|
|---|
| 17772 | }
|
|---|
| 17773 |
|
|---|
| 17774 | function startPassiveEffectTimer() {
|
|---|
| 17775 |
|
|---|
| 17776 | passiveEffectStartTime = now$1();
|
|---|
| 17777 | }
|
|---|
| 17778 |
|
|---|
| 17779 | function transferActualDuration(fiber) {
|
|---|
| 17780 | // Transfer time spent rendering these children so we don't lose it
|
|---|
| 17781 | // after we rerender. This is used as a helper in special cases
|
|---|
| 17782 | // where we should count the work of multiple passes.
|
|---|
| 17783 | var child = fiber.child;
|
|---|
| 17784 |
|
|---|
| 17785 | while (child) {
|
|---|
| 17786 | fiber.actualDuration += child.actualDuration;
|
|---|
| 17787 | child = child.sibling;
|
|---|
| 17788 | }
|
|---|
| 17789 | }
|
|---|
| 17790 |
|
|---|
| 17791 | function resolveDefaultProps(Component, baseProps) {
|
|---|
| 17792 | if (Component && Component.defaultProps) {
|
|---|
| 17793 | // Resolve default props. Taken from ReactElement
|
|---|
| 17794 | var props = assign({}, baseProps);
|
|---|
| 17795 | var defaultProps = Component.defaultProps;
|
|---|
| 17796 |
|
|---|
| 17797 | for (var propName in defaultProps) {
|
|---|
| 17798 | if (props[propName] === undefined) {
|
|---|
| 17799 | props[propName] = defaultProps[propName];
|
|---|
| 17800 | }
|
|---|
| 17801 | }
|
|---|
| 17802 |
|
|---|
| 17803 | return props;
|
|---|
| 17804 | }
|
|---|
| 17805 |
|
|---|
| 17806 | return baseProps;
|
|---|
| 17807 | }
|
|---|
| 17808 |
|
|---|
| 17809 | var fakeInternalInstance = {};
|
|---|
| 17810 | var didWarnAboutStateAssignmentForComponent;
|
|---|
| 17811 | var didWarnAboutUninitializedState;
|
|---|
| 17812 | var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
|
|---|
| 17813 | var didWarnAboutLegacyLifecyclesAndDerivedState;
|
|---|
| 17814 | var didWarnAboutUndefinedDerivedState;
|
|---|
| 17815 | var warnOnUndefinedDerivedState;
|
|---|
| 17816 | var warnOnInvalidCallback;
|
|---|
| 17817 | var didWarnAboutDirectlyAssigningPropsToState;
|
|---|
| 17818 | var didWarnAboutContextTypeAndContextTypes;
|
|---|
| 17819 | var didWarnAboutInvalidateContextType;
|
|---|
| 17820 | var didWarnAboutLegacyContext$1;
|
|---|
| 17821 |
|
|---|
| 17822 | {
|
|---|
| 17823 | didWarnAboutStateAssignmentForComponent = new Set();
|
|---|
| 17824 | didWarnAboutUninitializedState = new Set();
|
|---|
| 17825 | didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set();
|
|---|
| 17826 | didWarnAboutLegacyLifecyclesAndDerivedState = new Set();
|
|---|
| 17827 | didWarnAboutDirectlyAssigningPropsToState = new Set();
|
|---|
| 17828 | didWarnAboutUndefinedDerivedState = new Set();
|
|---|
| 17829 | didWarnAboutContextTypeAndContextTypes = new Set();
|
|---|
| 17830 | didWarnAboutInvalidateContextType = new Set();
|
|---|
| 17831 | didWarnAboutLegacyContext$1 = new Set();
|
|---|
| 17832 | var didWarnOnInvalidCallback = new Set();
|
|---|
| 17833 |
|
|---|
| 17834 | warnOnInvalidCallback = function (callback, callerName) {
|
|---|
| 17835 | if (callback === null || typeof callback === 'function') {
|
|---|
| 17836 | return;
|
|---|
| 17837 | }
|
|---|
| 17838 |
|
|---|
| 17839 | var key = callerName + '_' + callback;
|
|---|
| 17840 |
|
|---|
| 17841 | if (!didWarnOnInvalidCallback.has(key)) {
|
|---|
| 17842 | didWarnOnInvalidCallback.add(key);
|
|---|
| 17843 |
|
|---|
| 17844 | error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
|
|---|
| 17845 | }
|
|---|
| 17846 | };
|
|---|
| 17847 |
|
|---|
| 17848 | warnOnUndefinedDerivedState = function (type, partialState) {
|
|---|
| 17849 | if (partialState === undefined) {
|
|---|
| 17850 | var componentName = getComponentNameFromType(type) || 'Component';
|
|---|
| 17851 |
|
|---|
| 17852 | if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
|
|---|
| 17853 | didWarnAboutUndefinedDerivedState.add(componentName);
|
|---|
| 17854 |
|
|---|
| 17855 | error('%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + 'You have returned undefined.', componentName);
|
|---|
| 17856 | }
|
|---|
| 17857 | }
|
|---|
| 17858 | }; // This is so gross but it's at least non-critical and can be removed if
|
|---|
| 17859 | // it causes problems. This is meant to give a nicer error message for
|
|---|
| 17860 | // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component,
|
|---|
| 17861 | // ...)) which otherwise throws a "_processChildContext is not a function"
|
|---|
| 17862 | // exception.
|
|---|
| 17863 |
|
|---|
| 17864 |
|
|---|
| 17865 | Object.defineProperty(fakeInternalInstance, '_processChildContext', {
|
|---|
| 17866 | enumerable: false,
|
|---|
| 17867 | value: function () {
|
|---|
| 17868 | throw new Error('_processChildContext is not available in React 16+. This likely ' + 'means you have multiple copies of React and are attempting to nest ' + 'a React 15 tree inside a React 16 tree using ' + "unstable_renderSubtreeIntoContainer, which isn't supported. Try " + 'to make sure you have only one copy of React (and ideally, switch ' + 'to ReactDOM.createPortal).');
|
|---|
| 17869 | }
|
|---|
| 17870 | });
|
|---|
| 17871 | Object.freeze(fakeInternalInstance);
|
|---|
| 17872 | }
|
|---|
| 17873 |
|
|---|
| 17874 | function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) {
|
|---|
| 17875 | var prevState = workInProgress.memoizedState;
|
|---|
| 17876 | var partialState = getDerivedStateFromProps(nextProps, prevState);
|
|---|
| 17877 |
|
|---|
| 17878 | {
|
|---|
| 17879 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 17880 | setIsStrictModeForDevtools(true);
|
|---|
| 17881 |
|
|---|
| 17882 | try {
|
|---|
| 17883 | // Invoke the function an extra time to help detect side-effects.
|
|---|
| 17884 | partialState = getDerivedStateFromProps(nextProps, prevState);
|
|---|
| 17885 | } finally {
|
|---|
| 17886 | setIsStrictModeForDevtools(false);
|
|---|
| 17887 | }
|
|---|
| 17888 | }
|
|---|
| 17889 |
|
|---|
| 17890 | warnOnUndefinedDerivedState(ctor, partialState);
|
|---|
| 17891 | } // Merge the partial state and the previous state.
|
|---|
| 17892 |
|
|---|
| 17893 |
|
|---|
| 17894 | var memoizedState = partialState === null || partialState === undefined ? prevState : assign({}, prevState, partialState);
|
|---|
| 17895 | workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the
|
|---|
| 17896 | // base state.
|
|---|
| 17897 |
|
|---|
| 17898 | if (workInProgress.lanes === NoLanes) {
|
|---|
| 17899 | // Queue is always non-null for classes
|
|---|
| 17900 | var updateQueue = workInProgress.updateQueue;
|
|---|
| 17901 | updateQueue.baseState = memoizedState;
|
|---|
| 17902 | }
|
|---|
| 17903 | }
|
|---|
| 17904 |
|
|---|
| 17905 | var classComponentUpdater = {
|
|---|
| 17906 | isMounted: isMounted,
|
|---|
| 17907 | enqueueSetState: function (inst, payload, callback) {
|
|---|
| 17908 | var fiber = get(inst);
|
|---|
| 17909 | var eventTime = requestEventTime();
|
|---|
| 17910 | var lane = requestUpdateLane(fiber);
|
|---|
| 17911 | var update = createUpdate(eventTime, lane);
|
|---|
| 17912 | update.payload = payload;
|
|---|
| 17913 |
|
|---|
| 17914 | if (callback !== undefined && callback !== null) {
|
|---|
| 17915 | {
|
|---|
| 17916 | warnOnInvalidCallback(callback, 'setState');
|
|---|
| 17917 | }
|
|---|
| 17918 |
|
|---|
| 17919 | update.callback = callback;
|
|---|
| 17920 | }
|
|---|
| 17921 |
|
|---|
| 17922 | var root = enqueueUpdate(fiber, update, lane);
|
|---|
| 17923 |
|
|---|
| 17924 | if (root !== null) {
|
|---|
| 17925 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 17926 | entangleTransitions(root, fiber, lane);
|
|---|
| 17927 | }
|
|---|
| 17928 |
|
|---|
| 17929 | {
|
|---|
| 17930 | markStateUpdateScheduled(fiber, lane);
|
|---|
| 17931 | }
|
|---|
| 17932 | },
|
|---|
| 17933 | enqueueReplaceState: function (inst, payload, callback) {
|
|---|
| 17934 | var fiber = get(inst);
|
|---|
| 17935 | var eventTime = requestEventTime();
|
|---|
| 17936 | var lane = requestUpdateLane(fiber);
|
|---|
| 17937 | var update = createUpdate(eventTime, lane);
|
|---|
| 17938 | update.tag = ReplaceState;
|
|---|
| 17939 | update.payload = payload;
|
|---|
| 17940 |
|
|---|
| 17941 | if (callback !== undefined && callback !== null) {
|
|---|
| 17942 | {
|
|---|
| 17943 | warnOnInvalidCallback(callback, 'replaceState');
|
|---|
| 17944 | }
|
|---|
| 17945 |
|
|---|
| 17946 | update.callback = callback;
|
|---|
| 17947 | }
|
|---|
| 17948 |
|
|---|
| 17949 | var root = enqueueUpdate(fiber, update, lane);
|
|---|
| 17950 |
|
|---|
| 17951 | if (root !== null) {
|
|---|
| 17952 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 17953 | entangleTransitions(root, fiber, lane);
|
|---|
| 17954 | }
|
|---|
| 17955 |
|
|---|
| 17956 | {
|
|---|
| 17957 | markStateUpdateScheduled(fiber, lane);
|
|---|
| 17958 | }
|
|---|
| 17959 | },
|
|---|
| 17960 | enqueueForceUpdate: function (inst, callback) {
|
|---|
| 17961 | var fiber = get(inst);
|
|---|
| 17962 | var eventTime = requestEventTime();
|
|---|
| 17963 | var lane = requestUpdateLane(fiber);
|
|---|
| 17964 | var update = createUpdate(eventTime, lane);
|
|---|
| 17965 | update.tag = ForceUpdate;
|
|---|
| 17966 |
|
|---|
| 17967 | if (callback !== undefined && callback !== null) {
|
|---|
| 17968 | {
|
|---|
| 17969 | warnOnInvalidCallback(callback, 'forceUpdate');
|
|---|
| 17970 | }
|
|---|
| 17971 |
|
|---|
| 17972 | update.callback = callback;
|
|---|
| 17973 | }
|
|---|
| 17974 |
|
|---|
| 17975 | var root = enqueueUpdate(fiber, update, lane);
|
|---|
| 17976 |
|
|---|
| 17977 | if (root !== null) {
|
|---|
| 17978 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 17979 | entangleTransitions(root, fiber, lane);
|
|---|
| 17980 | }
|
|---|
| 17981 |
|
|---|
| 17982 | {
|
|---|
| 17983 | markForceUpdateScheduled(fiber, lane);
|
|---|
| 17984 | }
|
|---|
| 17985 | }
|
|---|
| 17986 | };
|
|---|
| 17987 |
|
|---|
| 17988 | function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) {
|
|---|
| 17989 | var instance = workInProgress.stateNode;
|
|---|
| 17990 |
|
|---|
| 17991 | if (typeof instance.shouldComponentUpdate === 'function') {
|
|---|
| 17992 | var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
|
|---|
| 17993 |
|
|---|
| 17994 | {
|
|---|
| 17995 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 17996 | setIsStrictModeForDevtools(true);
|
|---|
| 17997 |
|
|---|
| 17998 | try {
|
|---|
| 17999 | // Invoke the function an extra time to help detect side-effects.
|
|---|
| 18000 | shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext);
|
|---|
| 18001 | } finally {
|
|---|
| 18002 | setIsStrictModeForDevtools(false);
|
|---|
| 18003 | }
|
|---|
| 18004 | }
|
|---|
| 18005 |
|
|---|
| 18006 | if (shouldUpdate === undefined) {
|
|---|
| 18007 | error('%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentNameFromType(ctor) || 'Component');
|
|---|
| 18008 | }
|
|---|
| 18009 | }
|
|---|
| 18010 |
|
|---|
| 18011 | return shouldUpdate;
|
|---|
| 18012 | }
|
|---|
| 18013 |
|
|---|
| 18014 | if (ctor.prototype && ctor.prototype.isPureReactComponent) {
|
|---|
| 18015 | return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState);
|
|---|
| 18016 | }
|
|---|
| 18017 |
|
|---|
| 18018 | return true;
|
|---|
| 18019 | }
|
|---|
| 18020 |
|
|---|
| 18021 | function checkClassInstance(workInProgress, ctor, newProps) {
|
|---|
| 18022 | var instance = workInProgress.stateNode;
|
|---|
| 18023 |
|
|---|
| 18024 | {
|
|---|
| 18025 | var name = getComponentNameFromType(ctor) || 'Component';
|
|---|
| 18026 | var renderPresent = instance.render;
|
|---|
| 18027 |
|
|---|
| 18028 | if (!renderPresent) {
|
|---|
| 18029 | if (ctor.prototype && typeof ctor.prototype.render === 'function') {
|
|---|
| 18030 | error('%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name);
|
|---|
| 18031 | } else {
|
|---|
| 18032 | error('%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name);
|
|---|
| 18033 | }
|
|---|
| 18034 | }
|
|---|
| 18035 |
|
|---|
| 18036 | if (instance.getInitialState && !instance.getInitialState.isReactClassApproved && !instance.state) {
|
|---|
| 18037 | error('getInitialState was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Did you mean to define a state property instead?', name);
|
|---|
| 18038 | }
|
|---|
| 18039 |
|
|---|
| 18040 | if (instance.getDefaultProps && !instance.getDefaultProps.isReactClassApproved) {
|
|---|
| 18041 | error('getDefaultProps was defined on %s, a plain JavaScript class. ' + 'This is only supported for classes created using React.createClass. ' + 'Use a static property to define defaultProps instead.', name);
|
|---|
| 18042 | }
|
|---|
| 18043 |
|
|---|
| 18044 | if (instance.propTypes) {
|
|---|
| 18045 | error('propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name);
|
|---|
| 18046 | }
|
|---|
| 18047 |
|
|---|
| 18048 | if (instance.contextType) {
|
|---|
| 18049 | error('contextType was defined as an instance property on %s. Use a static ' + 'property to define contextType instead.', name);
|
|---|
| 18050 | }
|
|---|
| 18051 |
|
|---|
| 18052 | {
|
|---|
| 18053 | if (ctor.childContextTypes && !didWarnAboutLegacyContext$1.has(ctor) && // Strict Mode has its own warning for legacy context, so we can skip
|
|---|
| 18054 | // this one.
|
|---|
| 18055 | (workInProgress.mode & StrictLegacyMode) === NoMode) {
|
|---|
| 18056 | didWarnAboutLegacyContext$1.add(ctor);
|
|---|
| 18057 |
|
|---|
| 18058 | error('%s uses the legacy childContextTypes API which is no longer ' + 'supported and will be removed in the next major release. Use ' + 'React.createContext() instead\n\n.' + 'Learn more about this warning here: https://reactjs.org/link/legacy-context', name);
|
|---|
| 18059 | }
|
|---|
| 18060 |
|
|---|
| 18061 | if (ctor.contextTypes && !didWarnAboutLegacyContext$1.has(ctor) && // Strict Mode has its own warning for legacy context, so we can skip
|
|---|
| 18062 | // this one.
|
|---|
| 18063 | (workInProgress.mode & StrictLegacyMode) === NoMode) {
|
|---|
| 18064 | didWarnAboutLegacyContext$1.add(ctor);
|
|---|
| 18065 |
|
|---|
| 18066 | error('%s uses the legacy contextTypes API which is no longer supported ' + 'and will be removed in the next major release. Use ' + 'React.createContext() with static contextType instead.\n\n' + 'Learn more about this warning here: https://reactjs.org/link/legacy-context', name);
|
|---|
| 18067 | }
|
|---|
| 18068 |
|
|---|
| 18069 | if (instance.contextTypes) {
|
|---|
| 18070 | error('contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name);
|
|---|
| 18071 | }
|
|---|
| 18072 |
|
|---|
| 18073 | if (ctor.contextType && ctor.contextTypes && !didWarnAboutContextTypeAndContextTypes.has(ctor)) {
|
|---|
| 18074 | didWarnAboutContextTypeAndContextTypes.add(ctor);
|
|---|
| 18075 |
|
|---|
| 18076 | error('%s declares both contextTypes and contextType static properties. ' + 'The legacy contextTypes property will be ignored.', name);
|
|---|
| 18077 | }
|
|---|
| 18078 | }
|
|---|
| 18079 |
|
|---|
| 18080 | if (typeof instance.componentShouldUpdate === 'function') {
|
|---|
| 18081 | error('%s has a method called ' + 'componentShouldUpdate(). Did you mean shouldComponentUpdate()? ' + 'The name is phrased as a question because the function is ' + 'expected to return a value.', name);
|
|---|
| 18082 | }
|
|---|
| 18083 |
|
|---|
| 18084 | if (ctor.prototype && ctor.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') {
|
|---|
| 18085 | error('%s has a method called shouldComponentUpdate(). ' + 'shouldComponentUpdate should not be used when extending React.PureComponent. ' + 'Please extend React.Component if shouldComponentUpdate is used.', getComponentNameFromType(ctor) || 'A pure component');
|
|---|
| 18086 | }
|
|---|
| 18087 |
|
|---|
| 18088 | if (typeof instance.componentDidUnmount === 'function') {
|
|---|
| 18089 | error('%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name);
|
|---|
| 18090 | }
|
|---|
| 18091 |
|
|---|
| 18092 | if (typeof instance.componentDidReceiveProps === 'function') {
|
|---|
| 18093 | error('%s has a method called ' + 'componentDidReceiveProps(). But there is no such lifecycle method. ' + 'If you meant to update the state in response to changing props, ' + 'use componentWillReceiveProps(). If you meant to fetch data or ' + 'run side-effects or mutations after React has updated the UI, use componentDidUpdate().', name);
|
|---|
| 18094 | }
|
|---|
| 18095 |
|
|---|
| 18096 | if (typeof instance.componentWillRecieveProps === 'function') {
|
|---|
| 18097 | error('%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name);
|
|---|
| 18098 | }
|
|---|
| 18099 |
|
|---|
| 18100 | if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') {
|
|---|
| 18101 | error('%s has a method called ' + 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', name);
|
|---|
| 18102 | }
|
|---|
| 18103 |
|
|---|
| 18104 | var hasMutatedProps = instance.props !== newProps;
|
|---|
| 18105 |
|
|---|
| 18106 | if (instance.props !== undefined && hasMutatedProps) {
|
|---|
| 18107 | error('%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name);
|
|---|
| 18108 | }
|
|---|
| 18109 |
|
|---|
| 18110 | if (instance.defaultProps) {
|
|---|
| 18111 | error('Setting defaultProps as an instance property on %s is not supported and will be ignored.' + ' Instead, define defaultProps as a static property on %s.', name, name);
|
|---|
| 18112 | }
|
|---|
| 18113 |
|
|---|
| 18114 | if (typeof instance.getSnapshotBeforeUpdate === 'function' && typeof instance.componentDidUpdate !== 'function' && !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)) {
|
|---|
| 18115 | didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor);
|
|---|
| 18116 |
|
|---|
| 18117 | error('%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' + 'This component defines getSnapshotBeforeUpdate() only.', getComponentNameFromType(ctor));
|
|---|
| 18118 | }
|
|---|
| 18119 |
|
|---|
| 18120 | if (typeof instance.getDerivedStateFromProps === 'function') {
|
|---|
| 18121 | error('%s: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name);
|
|---|
| 18122 | }
|
|---|
| 18123 |
|
|---|
| 18124 | if (typeof instance.getDerivedStateFromError === 'function') {
|
|---|
| 18125 | error('%s: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name);
|
|---|
| 18126 | }
|
|---|
| 18127 |
|
|---|
| 18128 | if (typeof ctor.getSnapshotBeforeUpdate === 'function') {
|
|---|
| 18129 | error('%s: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.', name);
|
|---|
| 18130 | }
|
|---|
| 18131 |
|
|---|
| 18132 | var _state = instance.state;
|
|---|
| 18133 |
|
|---|
| 18134 | if (_state && (typeof _state !== 'object' || isArray(_state))) {
|
|---|
| 18135 | error('%s.state: must be set to an object or null', name);
|
|---|
| 18136 | }
|
|---|
| 18137 |
|
|---|
| 18138 | if (typeof instance.getChildContext === 'function' && typeof ctor.childContextTypes !== 'object') {
|
|---|
| 18139 | error('%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', name);
|
|---|
| 18140 | }
|
|---|
| 18141 | }
|
|---|
| 18142 | }
|
|---|
| 18143 |
|
|---|
| 18144 | function adoptClassInstance(workInProgress, instance) {
|
|---|
| 18145 | instance.updater = classComponentUpdater;
|
|---|
| 18146 | workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates
|
|---|
| 18147 |
|
|---|
| 18148 | set(instance, workInProgress);
|
|---|
| 18149 |
|
|---|
| 18150 | {
|
|---|
| 18151 | instance._reactInternalInstance = fakeInternalInstance;
|
|---|
| 18152 | }
|
|---|
| 18153 | }
|
|---|
| 18154 |
|
|---|
| 18155 | function constructClassInstance(workInProgress, ctor, props) {
|
|---|
| 18156 | var isLegacyContextConsumer = false;
|
|---|
| 18157 | var unmaskedContext = emptyContextObject;
|
|---|
| 18158 | var context = emptyContextObject;
|
|---|
| 18159 | var contextType = ctor.contextType;
|
|---|
| 18160 |
|
|---|
| 18161 | {
|
|---|
| 18162 | if ('contextType' in ctor) {
|
|---|
| 18163 | var isValid = // Allow null for conditional declaration
|
|---|
| 18164 | contextType === null || contextType !== undefined && contextType.$$typeof === REACT_CONTEXT_TYPE && contextType._context === undefined; // Not a <Context.Consumer>
|
|---|
| 18165 |
|
|---|
| 18166 | if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) {
|
|---|
| 18167 | didWarnAboutInvalidateContextType.add(ctor);
|
|---|
| 18168 | var addendum = '';
|
|---|
| 18169 |
|
|---|
| 18170 | if (contextType === undefined) {
|
|---|
| 18171 | addendum = ' However, it is set to undefined. ' + 'This can be caused by a typo or by mixing up named and default imports. ' + 'This can also happen due to a circular dependency, so ' + 'try moving the createContext() call to a separate file.';
|
|---|
| 18172 | } else if (typeof contextType !== 'object') {
|
|---|
| 18173 | addendum = ' However, it is set to a ' + typeof contextType + '.';
|
|---|
| 18174 | } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) {
|
|---|
| 18175 | addendum = ' Did you accidentally pass the Context.Provider instead?';
|
|---|
| 18176 | } else if (contextType._context !== undefined) {
|
|---|
| 18177 | // <Context.Consumer>
|
|---|
| 18178 | addendum = ' Did you accidentally pass the Context.Consumer instead?';
|
|---|
| 18179 | } else {
|
|---|
| 18180 | addendum = ' However, it is set to an object with keys {' + Object.keys(contextType).join(', ') + '}.';
|
|---|
| 18181 | }
|
|---|
| 18182 |
|
|---|
| 18183 | error('%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext().%s', getComponentNameFromType(ctor) || 'Component', addendum);
|
|---|
| 18184 | }
|
|---|
| 18185 | }
|
|---|
| 18186 | }
|
|---|
| 18187 |
|
|---|
| 18188 | if (typeof contextType === 'object' && contextType !== null) {
|
|---|
| 18189 | context = readContext(contextType);
|
|---|
| 18190 | } else {
|
|---|
| 18191 | unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|---|
| 18192 | var contextTypes = ctor.contextTypes;
|
|---|
| 18193 | isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined;
|
|---|
| 18194 | context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject;
|
|---|
| 18195 | }
|
|---|
| 18196 |
|
|---|
| 18197 | var instance = new ctor(props, context); // Instantiate twice to help detect side-effects.
|
|---|
| 18198 |
|
|---|
| 18199 | {
|
|---|
| 18200 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 18201 | setIsStrictModeForDevtools(true);
|
|---|
| 18202 |
|
|---|
| 18203 | try {
|
|---|
| 18204 | instance = new ctor(props, context); // eslint-disable-line no-new
|
|---|
| 18205 | } finally {
|
|---|
| 18206 | setIsStrictModeForDevtools(false);
|
|---|
| 18207 | }
|
|---|
| 18208 | }
|
|---|
| 18209 | }
|
|---|
| 18210 |
|
|---|
| 18211 | var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null;
|
|---|
| 18212 | adoptClassInstance(workInProgress, instance);
|
|---|
| 18213 |
|
|---|
| 18214 | {
|
|---|
| 18215 | if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) {
|
|---|
| 18216 | var componentName = getComponentNameFromType(ctor) || 'Component';
|
|---|
| 18217 |
|
|---|
| 18218 | if (!didWarnAboutUninitializedState.has(componentName)) {
|
|---|
| 18219 | didWarnAboutUninitializedState.add(componentName);
|
|---|
| 18220 |
|
|---|
| 18221 | error('`%s` uses `getDerivedStateFromProps` but its initial state is ' + '%s. This is not recommended. Instead, define the initial state by ' + 'assigning an object to `this.state` in the constructor of `%s`. ' + 'This ensures that `getDerivedStateFromProps` arguments have a consistent shape.', componentName, instance.state === null ? 'null' : 'undefined', componentName);
|
|---|
| 18222 | }
|
|---|
| 18223 | } // If new component APIs are defined, "unsafe" lifecycles won't be called.
|
|---|
| 18224 | // Warn about these lifecycles if they are present.
|
|---|
| 18225 | // Don't warn about react-lifecycles-compat polyfilled methods though.
|
|---|
| 18226 |
|
|---|
| 18227 |
|
|---|
| 18228 | if (typeof ctor.getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|---|
| 18229 | var foundWillMountName = null;
|
|---|
| 18230 | var foundWillReceivePropsName = null;
|
|---|
| 18231 | var foundWillUpdateName = null;
|
|---|
| 18232 |
|
|---|
| 18233 | if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true) {
|
|---|
| 18234 | foundWillMountName = 'componentWillMount';
|
|---|
| 18235 | } else if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|---|
| 18236 | foundWillMountName = 'UNSAFE_componentWillMount';
|
|---|
| 18237 | }
|
|---|
| 18238 |
|
|---|
| 18239 | if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) {
|
|---|
| 18240 | foundWillReceivePropsName = 'componentWillReceiveProps';
|
|---|
| 18241 | } else if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
|---|
| 18242 | foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps';
|
|---|
| 18243 | }
|
|---|
| 18244 |
|
|---|
| 18245 | if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) {
|
|---|
| 18246 | foundWillUpdateName = 'componentWillUpdate';
|
|---|
| 18247 | } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
|---|
| 18248 | foundWillUpdateName = 'UNSAFE_componentWillUpdate';
|
|---|
| 18249 | }
|
|---|
| 18250 |
|
|---|
| 18251 | if (foundWillMountName !== null || foundWillReceivePropsName !== null || foundWillUpdateName !== null) {
|
|---|
| 18252 | var _componentName = getComponentNameFromType(ctor) || 'Component';
|
|---|
| 18253 |
|
|---|
| 18254 | var newApiName = typeof ctor.getDerivedStateFromProps === 'function' ? 'getDerivedStateFromProps()' : 'getSnapshotBeforeUpdate()';
|
|---|
| 18255 |
|
|---|
| 18256 | if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) {
|
|---|
| 18257 | didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName);
|
|---|
| 18258 |
|
|---|
| 18259 | error('Unsafe legacy lifecycles will not be called for components using new component APIs.\n\n' + '%s uses %s but also contains the following legacy lifecycles:%s%s%s\n\n' + 'The above lifecycles should be removed. Learn more about this warning here:\n' + 'https://reactjs.org/link/unsafe-component-lifecycles', _componentName, newApiName, foundWillMountName !== null ? "\n " + foundWillMountName : '', foundWillReceivePropsName !== null ? "\n " + foundWillReceivePropsName : '', foundWillUpdateName !== null ? "\n " + foundWillUpdateName : '');
|
|---|
| 18260 | }
|
|---|
| 18261 | }
|
|---|
| 18262 | }
|
|---|
| 18263 | } // Cache unmasked context so we can avoid recreating masked context unless necessary.
|
|---|
| 18264 | // ReactFiberContext usually updates this cache but can't for newly-created instances.
|
|---|
| 18265 |
|
|---|
| 18266 |
|
|---|
| 18267 | if (isLegacyContextConsumer) {
|
|---|
| 18268 | cacheContext(workInProgress, unmaskedContext, context);
|
|---|
| 18269 | }
|
|---|
| 18270 |
|
|---|
| 18271 | return instance;
|
|---|
| 18272 | }
|
|---|
| 18273 |
|
|---|
| 18274 | function callComponentWillMount(workInProgress, instance) {
|
|---|
| 18275 | var oldState = instance.state;
|
|---|
| 18276 |
|
|---|
| 18277 | if (typeof instance.componentWillMount === 'function') {
|
|---|
| 18278 | instance.componentWillMount();
|
|---|
| 18279 | }
|
|---|
| 18280 |
|
|---|
| 18281 | if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|---|
| 18282 | instance.UNSAFE_componentWillMount();
|
|---|
| 18283 | }
|
|---|
| 18284 |
|
|---|
| 18285 | if (oldState !== instance.state) {
|
|---|
| 18286 | {
|
|---|
| 18287 | error('%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentNameFromFiber(workInProgress) || 'Component');
|
|---|
| 18288 | }
|
|---|
| 18289 |
|
|---|
| 18290 | classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
|---|
| 18291 | }
|
|---|
| 18292 | }
|
|---|
| 18293 |
|
|---|
| 18294 | function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) {
|
|---|
| 18295 | var oldState = instance.state;
|
|---|
| 18296 |
|
|---|
| 18297 | if (typeof instance.componentWillReceiveProps === 'function') {
|
|---|
| 18298 | instance.componentWillReceiveProps(newProps, nextContext);
|
|---|
| 18299 | }
|
|---|
| 18300 |
|
|---|
| 18301 | if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') {
|
|---|
| 18302 | instance.UNSAFE_componentWillReceiveProps(newProps, nextContext);
|
|---|
| 18303 | }
|
|---|
| 18304 |
|
|---|
| 18305 | if (instance.state !== oldState) {
|
|---|
| 18306 | {
|
|---|
| 18307 | var componentName = getComponentNameFromFiber(workInProgress) || 'Component';
|
|---|
| 18308 |
|
|---|
| 18309 | if (!didWarnAboutStateAssignmentForComponent.has(componentName)) {
|
|---|
| 18310 | didWarnAboutStateAssignmentForComponent.add(componentName);
|
|---|
| 18311 |
|
|---|
| 18312 | error('%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName);
|
|---|
| 18313 | }
|
|---|
| 18314 | }
|
|---|
| 18315 |
|
|---|
| 18316 | classComponentUpdater.enqueueReplaceState(instance, instance.state, null);
|
|---|
| 18317 | }
|
|---|
| 18318 | } // Invokes the mount life-cycles on a previously never rendered instance.
|
|---|
| 18319 |
|
|---|
| 18320 |
|
|---|
| 18321 | function mountClassInstance(workInProgress, ctor, newProps, renderLanes) {
|
|---|
| 18322 | {
|
|---|
| 18323 | checkClassInstance(workInProgress, ctor, newProps);
|
|---|
| 18324 | }
|
|---|
| 18325 |
|
|---|
| 18326 | var instance = workInProgress.stateNode;
|
|---|
| 18327 | instance.props = newProps;
|
|---|
| 18328 | instance.state = workInProgress.memoizedState;
|
|---|
| 18329 | instance.refs = {};
|
|---|
| 18330 | initializeUpdateQueue(workInProgress);
|
|---|
| 18331 | var contextType = ctor.contextType;
|
|---|
| 18332 |
|
|---|
| 18333 | if (typeof contextType === 'object' && contextType !== null) {
|
|---|
| 18334 | instance.context = readContext(contextType);
|
|---|
| 18335 | } else {
|
|---|
| 18336 | var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|---|
| 18337 | instance.context = getMaskedContext(workInProgress, unmaskedContext);
|
|---|
| 18338 | }
|
|---|
| 18339 |
|
|---|
| 18340 | {
|
|---|
| 18341 | if (instance.state === newProps) {
|
|---|
| 18342 | var componentName = getComponentNameFromType(ctor) || 'Component';
|
|---|
| 18343 |
|
|---|
| 18344 | if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) {
|
|---|
| 18345 | didWarnAboutDirectlyAssigningPropsToState.add(componentName);
|
|---|
| 18346 |
|
|---|
| 18347 | error('%s: It is not recommended to assign props directly to state ' + "because updates to props won't be reflected in state. " + 'In most cases, it is better to use props directly.', componentName);
|
|---|
| 18348 | }
|
|---|
| 18349 | }
|
|---|
| 18350 |
|
|---|
| 18351 | if (workInProgress.mode & StrictLegacyMode) {
|
|---|
| 18352 | ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, instance);
|
|---|
| 18353 | }
|
|---|
| 18354 |
|
|---|
| 18355 | {
|
|---|
| 18356 | ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress, instance);
|
|---|
| 18357 | }
|
|---|
| 18358 | }
|
|---|
| 18359 |
|
|---|
| 18360 | instance.state = workInProgress.memoizedState;
|
|---|
| 18361 | var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
|---|
| 18362 |
|
|---|
| 18363 | if (typeof getDerivedStateFromProps === 'function') {
|
|---|
| 18364 | applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
|---|
| 18365 | instance.state = workInProgress.memoizedState;
|
|---|
| 18366 | } // In order to support react-lifecycles-compat polyfilled components,
|
|---|
| 18367 | // Unsafe lifecycles should not be invoked for components using the new APIs.
|
|---|
| 18368 |
|
|---|
| 18369 |
|
|---|
| 18370 | if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
|
|---|
| 18371 | callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's
|
|---|
| 18372 | // process them now.
|
|---|
| 18373 |
|
|---|
| 18374 | processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
|---|
| 18375 | instance.state = workInProgress.memoizedState;
|
|---|
| 18376 | }
|
|---|
| 18377 |
|
|---|
| 18378 | if (typeof instance.componentDidMount === 'function') {
|
|---|
| 18379 | var fiberFlags = Update;
|
|---|
| 18380 |
|
|---|
| 18381 | {
|
|---|
| 18382 | fiberFlags |= LayoutStatic;
|
|---|
| 18383 | }
|
|---|
| 18384 |
|
|---|
| 18385 | if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 18386 | fiberFlags |= MountLayoutDev;
|
|---|
| 18387 | }
|
|---|
| 18388 |
|
|---|
| 18389 | workInProgress.flags |= fiberFlags;
|
|---|
| 18390 | }
|
|---|
| 18391 | }
|
|---|
| 18392 |
|
|---|
| 18393 | function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) {
|
|---|
| 18394 | var instance = workInProgress.stateNode;
|
|---|
| 18395 | var oldProps = workInProgress.memoizedProps;
|
|---|
| 18396 | instance.props = oldProps;
|
|---|
| 18397 | var oldContext = instance.context;
|
|---|
| 18398 | var contextType = ctor.contextType;
|
|---|
| 18399 | var nextContext = emptyContextObject;
|
|---|
| 18400 |
|
|---|
| 18401 | if (typeof contextType === 'object' && contextType !== null) {
|
|---|
| 18402 | nextContext = readContext(contextType);
|
|---|
| 18403 | } else {
|
|---|
| 18404 | var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|---|
| 18405 | nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext);
|
|---|
| 18406 | }
|
|---|
| 18407 |
|
|---|
| 18408 | var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
|---|
| 18409 | var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what
|
|---|
| 18410 | // ever the previously attempted to render - not the "current". However,
|
|---|
| 18411 | // during componentDidUpdate we pass the "current" props.
|
|---|
| 18412 | // In order to support react-lifecycles-compat polyfilled components,
|
|---|
| 18413 | // Unsafe lifecycles should not be invoked for components using the new APIs.
|
|---|
| 18414 |
|
|---|
| 18415 | if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
|
|---|
| 18416 | if (oldProps !== newProps || oldContext !== nextContext) {
|
|---|
| 18417 | callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
|
|---|
| 18418 | }
|
|---|
| 18419 | }
|
|---|
| 18420 |
|
|---|
| 18421 | resetHasForceUpdateBeforeProcessing();
|
|---|
| 18422 | var oldState = workInProgress.memoizedState;
|
|---|
| 18423 | var newState = instance.state = oldState;
|
|---|
| 18424 | processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
|---|
| 18425 | newState = workInProgress.memoizedState;
|
|---|
| 18426 |
|
|---|
| 18427 | if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) {
|
|---|
| 18428 | // If an update was already in progress, we should schedule an Update
|
|---|
| 18429 | // effect even though we're bailing out, so that cWU/cDU are called.
|
|---|
| 18430 | if (typeof instance.componentDidMount === 'function') {
|
|---|
| 18431 | var fiberFlags = Update;
|
|---|
| 18432 |
|
|---|
| 18433 | {
|
|---|
| 18434 | fiberFlags |= LayoutStatic;
|
|---|
| 18435 | }
|
|---|
| 18436 |
|
|---|
| 18437 | if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 18438 | fiberFlags |= MountLayoutDev;
|
|---|
| 18439 | }
|
|---|
| 18440 |
|
|---|
| 18441 | workInProgress.flags |= fiberFlags;
|
|---|
| 18442 | }
|
|---|
| 18443 |
|
|---|
| 18444 | return false;
|
|---|
| 18445 | }
|
|---|
| 18446 |
|
|---|
| 18447 | if (typeof getDerivedStateFromProps === 'function') {
|
|---|
| 18448 | applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
|---|
| 18449 | newState = workInProgress.memoizedState;
|
|---|
| 18450 | }
|
|---|
| 18451 |
|
|---|
| 18452 | var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext);
|
|---|
| 18453 |
|
|---|
| 18454 | if (shouldUpdate) {
|
|---|
| 18455 | // In order to support react-lifecycles-compat polyfilled components,
|
|---|
| 18456 | // Unsafe lifecycles should not be invoked for components using the new APIs.
|
|---|
| 18457 | if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) {
|
|---|
| 18458 | if (typeof instance.componentWillMount === 'function') {
|
|---|
| 18459 | instance.componentWillMount();
|
|---|
| 18460 | }
|
|---|
| 18461 |
|
|---|
| 18462 | if (typeof instance.UNSAFE_componentWillMount === 'function') {
|
|---|
| 18463 | instance.UNSAFE_componentWillMount();
|
|---|
| 18464 | }
|
|---|
| 18465 | }
|
|---|
| 18466 |
|
|---|
| 18467 | if (typeof instance.componentDidMount === 'function') {
|
|---|
| 18468 | var _fiberFlags = Update;
|
|---|
| 18469 |
|
|---|
| 18470 | {
|
|---|
| 18471 | _fiberFlags |= LayoutStatic;
|
|---|
| 18472 | }
|
|---|
| 18473 |
|
|---|
| 18474 | if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 18475 | _fiberFlags |= MountLayoutDev;
|
|---|
| 18476 | }
|
|---|
| 18477 |
|
|---|
| 18478 | workInProgress.flags |= _fiberFlags;
|
|---|
| 18479 | }
|
|---|
| 18480 | } else {
|
|---|
| 18481 | // If an update was already in progress, we should schedule an Update
|
|---|
| 18482 | // effect even though we're bailing out, so that cWU/cDU are called.
|
|---|
| 18483 | if (typeof instance.componentDidMount === 'function') {
|
|---|
| 18484 | var _fiberFlags2 = Update;
|
|---|
| 18485 |
|
|---|
| 18486 | {
|
|---|
| 18487 | _fiberFlags2 |= LayoutStatic;
|
|---|
| 18488 | }
|
|---|
| 18489 |
|
|---|
| 18490 | if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) {
|
|---|
| 18491 | _fiberFlags2 |= MountLayoutDev;
|
|---|
| 18492 | }
|
|---|
| 18493 |
|
|---|
| 18494 | workInProgress.flags |= _fiberFlags2;
|
|---|
| 18495 | } // If shouldComponentUpdate returned false, we should still update the
|
|---|
| 18496 | // memoized state to indicate that this work can be reused.
|
|---|
| 18497 |
|
|---|
| 18498 |
|
|---|
| 18499 | workInProgress.memoizedProps = newProps;
|
|---|
| 18500 | workInProgress.memoizedState = newState;
|
|---|
| 18501 | } // Update the existing instance's state, props, and context pointers even
|
|---|
| 18502 | // if shouldComponentUpdate returns false.
|
|---|
| 18503 |
|
|---|
| 18504 |
|
|---|
| 18505 | instance.props = newProps;
|
|---|
| 18506 | instance.state = newState;
|
|---|
| 18507 | instance.context = nextContext;
|
|---|
| 18508 | return shouldUpdate;
|
|---|
| 18509 | } // Invokes the update life-cycles and returns false if it shouldn't rerender.
|
|---|
| 18510 |
|
|---|
| 18511 |
|
|---|
| 18512 | function updateClassInstance(current, workInProgress, ctor, newProps, renderLanes) {
|
|---|
| 18513 | var instance = workInProgress.stateNode;
|
|---|
| 18514 | cloneUpdateQueue(current, workInProgress);
|
|---|
| 18515 | var unresolvedOldProps = workInProgress.memoizedProps;
|
|---|
| 18516 | var oldProps = workInProgress.type === workInProgress.elementType ? unresolvedOldProps : resolveDefaultProps(workInProgress.type, unresolvedOldProps);
|
|---|
| 18517 | instance.props = oldProps;
|
|---|
| 18518 | var unresolvedNewProps = workInProgress.pendingProps;
|
|---|
| 18519 | var oldContext = instance.context;
|
|---|
| 18520 | var contextType = ctor.contextType;
|
|---|
| 18521 | var nextContext = emptyContextObject;
|
|---|
| 18522 |
|
|---|
| 18523 | if (typeof contextType === 'object' && contextType !== null) {
|
|---|
| 18524 | nextContext = readContext(contextType);
|
|---|
| 18525 | } else {
|
|---|
| 18526 | var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true);
|
|---|
| 18527 | nextContext = getMaskedContext(workInProgress, nextUnmaskedContext);
|
|---|
| 18528 | }
|
|---|
| 18529 |
|
|---|
| 18530 | var getDerivedStateFromProps = ctor.getDerivedStateFromProps;
|
|---|
| 18531 | var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what
|
|---|
| 18532 | // ever the previously attempted to render - not the "current". However,
|
|---|
| 18533 | // during componentDidUpdate we pass the "current" props.
|
|---|
| 18534 | // In order to support react-lifecycles-compat polyfilled components,
|
|---|
| 18535 | // Unsafe lifecycles should not be invoked for components using the new APIs.
|
|---|
| 18536 |
|
|---|
| 18537 | if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) {
|
|---|
| 18538 | if (unresolvedOldProps !== unresolvedNewProps || oldContext !== nextContext) {
|
|---|
| 18539 | callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext);
|
|---|
| 18540 | }
|
|---|
| 18541 | }
|
|---|
| 18542 |
|
|---|
| 18543 | resetHasForceUpdateBeforeProcessing();
|
|---|
| 18544 | var oldState = workInProgress.memoizedState;
|
|---|
| 18545 | var newState = instance.state = oldState;
|
|---|
| 18546 | processUpdateQueue(workInProgress, newProps, instance, renderLanes);
|
|---|
| 18547 | newState = workInProgress.memoizedState;
|
|---|
| 18548 |
|
|---|
| 18549 | if (unresolvedOldProps === unresolvedNewProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing() && !(enableLazyContextPropagation )) {
|
|---|
| 18550 | // If an update was already in progress, we should schedule an Update
|
|---|
| 18551 | // effect even though we're bailing out, so that cWU/cDU are called.
|
|---|
| 18552 | if (typeof instance.componentDidUpdate === 'function') {
|
|---|
| 18553 | if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
|---|
| 18554 | workInProgress.flags |= Update;
|
|---|
| 18555 | }
|
|---|
| 18556 | }
|
|---|
| 18557 |
|
|---|
| 18558 | if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|---|
| 18559 | if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
|---|
| 18560 | workInProgress.flags |= Snapshot;
|
|---|
| 18561 | }
|
|---|
| 18562 | }
|
|---|
| 18563 |
|
|---|
| 18564 | return false;
|
|---|
| 18565 | }
|
|---|
| 18566 |
|
|---|
| 18567 | if (typeof getDerivedStateFromProps === 'function') {
|
|---|
| 18568 | applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps);
|
|---|
| 18569 | newState = workInProgress.memoizedState;
|
|---|
| 18570 | }
|
|---|
| 18571 |
|
|---|
| 18572 | var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) || // TODO: In some cases, we'll end up checking if context has changed twice,
|
|---|
| 18573 | // both before and after `shouldComponentUpdate` has been called. Not ideal,
|
|---|
| 18574 | // but I'm loath to refactor this function. This only happens for memoized
|
|---|
| 18575 | // components so it's not that common.
|
|---|
| 18576 | enableLazyContextPropagation ;
|
|---|
| 18577 |
|
|---|
| 18578 | if (shouldUpdate) {
|
|---|
| 18579 | // In order to support react-lifecycles-compat polyfilled components,
|
|---|
| 18580 | // Unsafe lifecycles should not be invoked for components using the new APIs.
|
|---|
| 18581 | if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) {
|
|---|
| 18582 | if (typeof instance.componentWillUpdate === 'function') {
|
|---|
| 18583 | instance.componentWillUpdate(newProps, newState, nextContext);
|
|---|
| 18584 | }
|
|---|
| 18585 |
|
|---|
| 18586 | if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
|
|---|
| 18587 | instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext);
|
|---|
| 18588 | }
|
|---|
| 18589 | }
|
|---|
| 18590 |
|
|---|
| 18591 | if (typeof instance.componentDidUpdate === 'function') {
|
|---|
| 18592 | workInProgress.flags |= Update;
|
|---|
| 18593 | }
|
|---|
| 18594 |
|
|---|
| 18595 | if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|---|
| 18596 | workInProgress.flags |= Snapshot;
|
|---|
| 18597 | }
|
|---|
| 18598 | } else {
|
|---|
| 18599 | // If an update was already in progress, we should schedule an Update
|
|---|
| 18600 | // effect even though we're bailing out, so that cWU/cDU are called.
|
|---|
| 18601 | if (typeof instance.componentDidUpdate === 'function') {
|
|---|
| 18602 | if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
|---|
| 18603 | workInProgress.flags |= Update;
|
|---|
| 18604 | }
|
|---|
| 18605 | }
|
|---|
| 18606 |
|
|---|
| 18607 | if (typeof instance.getSnapshotBeforeUpdate === 'function') {
|
|---|
| 18608 | if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) {
|
|---|
| 18609 | workInProgress.flags |= Snapshot;
|
|---|
| 18610 | }
|
|---|
| 18611 | } // If shouldComponentUpdate returned false, we should still update the
|
|---|
| 18612 | // memoized props/state to indicate that this work can be reused.
|
|---|
| 18613 |
|
|---|
| 18614 |
|
|---|
| 18615 | workInProgress.memoizedProps = newProps;
|
|---|
| 18616 | workInProgress.memoizedState = newState;
|
|---|
| 18617 | } // Update the existing instance's state, props, and context pointers even
|
|---|
| 18618 | // if shouldComponentUpdate returns false.
|
|---|
| 18619 |
|
|---|
| 18620 |
|
|---|
| 18621 | instance.props = newProps;
|
|---|
| 18622 | instance.state = newState;
|
|---|
| 18623 | instance.context = nextContext;
|
|---|
| 18624 | return shouldUpdate;
|
|---|
| 18625 | }
|
|---|
| 18626 |
|
|---|
| 18627 | function createCapturedValueAtFiber(value, source) {
|
|---|
| 18628 | // If the value is an error, call this function immediately after it is thrown
|
|---|
| 18629 | // so the stack is accurate.
|
|---|
| 18630 | return {
|
|---|
| 18631 | value: value,
|
|---|
| 18632 | source: source,
|
|---|
| 18633 | stack: getStackByFiberInDevAndProd(source),
|
|---|
| 18634 | digest: null
|
|---|
| 18635 | };
|
|---|
| 18636 | }
|
|---|
| 18637 | function createCapturedValue(value, digest, stack) {
|
|---|
| 18638 | return {
|
|---|
| 18639 | value: value,
|
|---|
| 18640 | source: null,
|
|---|
| 18641 | stack: stack != null ? stack : null,
|
|---|
| 18642 | digest: digest != null ? digest : null
|
|---|
| 18643 | };
|
|---|
| 18644 | }
|
|---|
| 18645 |
|
|---|
| 18646 | // This module is forked in different environments.
|
|---|
| 18647 | // By default, return `true` to log errors to the console.
|
|---|
| 18648 | // Forks can return `false` if this isn't desirable.
|
|---|
| 18649 | function showErrorDialog(boundary, errorInfo) {
|
|---|
| 18650 | return true;
|
|---|
| 18651 | }
|
|---|
| 18652 |
|
|---|
| 18653 | function logCapturedError(boundary, errorInfo) {
|
|---|
| 18654 | try {
|
|---|
| 18655 | var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging.
|
|---|
| 18656 | // This enables renderers like ReactNative to better manage redbox behavior.
|
|---|
| 18657 |
|
|---|
| 18658 | if (logError === false) {
|
|---|
| 18659 | return;
|
|---|
| 18660 | }
|
|---|
| 18661 |
|
|---|
| 18662 | var error = errorInfo.value;
|
|---|
| 18663 |
|
|---|
| 18664 | if (true) {
|
|---|
| 18665 | var source = errorInfo.source;
|
|---|
| 18666 | var stack = errorInfo.stack;
|
|---|
| 18667 | var componentStack = stack !== null ? stack : ''; // Browsers support silencing uncaught errors by calling
|
|---|
| 18668 | // `preventDefault()` in window `error` handler.
|
|---|
| 18669 | // We record this information as an expando on the error.
|
|---|
| 18670 |
|
|---|
| 18671 | if (error != null && error._suppressLogging) {
|
|---|
| 18672 | if (boundary.tag === ClassComponent) {
|
|---|
| 18673 | // The error is recoverable and was silenced.
|
|---|
| 18674 | // Ignore it and don't print the stack addendum.
|
|---|
| 18675 | // This is handy for testing error boundaries without noise.
|
|---|
| 18676 | return;
|
|---|
| 18677 | } // The error is fatal. Since the silencing might have
|
|---|
| 18678 | // been accidental, we'll surface it anyway.
|
|---|
| 18679 | // However, the browser would have silenced the original error
|
|---|
| 18680 | // so we'll print it first, and then print the stack addendum.
|
|---|
| 18681 |
|
|---|
| 18682 |
|
|---|
| 18683 | console['error'](error); // Don't transform to our wrapper
|
|---|
| 18684 | // For a more detailed description of this block, see:
|
|---|
| 18685 | // https://github.com/facebook/react/pull/13384
|
|---|
| 18686 | }
|
|---|
| 18687 |
|
|---|
| 18688 | var componentName = source ? getComponentNameFromFiber(source) : null;
|
|---|
| 18689 | var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component:" : 'The above error occurred in one of your React components:';
|
|---|
| 18690 | var errorBoundaryMessage;
|
|---|
| 18691 |
|
|---|
| 18692 | if (boundary.tag === HostRoot) {
|
|---|
| 18693 | errorBoundaryMessage = 'Consider adding an error boundary to your tree to customize error handling behavior.\n' + 'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.';
|
|---|
| 18694 | } else {
|
|---|
| 18695 | var errorBoundaryName = getComponentNameFromFiber(boundary) || 'Anonymous';
|
|---|
| 18696 | errorBoundaryMessage = "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + errorBoundaryName + ".");
|
|---|
| 18697 | }
|
|---|
| 18698 |
|
|---|
| 18699 | var combinedMessage = componentNameMessage + "\n" + componentStack + "\n\n" + ("" + errorBoundaryMessage); // In development, we provide our own message with just the component stack.
|
|---|
| 18700 | // We don't include the original error message and JS stack because the browser
|
|---|
| 18701 | // has already printed it. Even if the application swallows the error, it is still
|
|---|
| 18702 | // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils.
|
|---|
| 18703 |
|
|---|
| 18704 | console['error'](combinedMessage); // Don't transform to our wrapper
|
|---|
| 18705 | } else {
|
|---|
| 18706 | // In production, we print the error directly.
|
|---|
| 18707 | // This will include the message, the JS stack, and anything the browser wants to show.
|
|---|
| 18708 | // We pass the error object instead of custom message so that the browser displays the error natively.
|
|---|
| 18709 | console['error'](error); // Don't transform to our wrapper
|
|---|
| 18710 | }
|
|---|
| 18711 | } catch (e) {
|
|---|
| 18712 | // This method must not throw, or React internal state will get messed up.
|
|---|
| 18713 | // If console.error is overridden, or logCapturedError() shows a dialog that throws,
|
|---|
| 18714 | // we want to report this error outside of the normal stack as a last resort.
|
|---|
| 18715 | // https://github.com/facebook/react/issues/13188
|
|---|
| 18716 | setTimeout(function () {
|
|---|
| 18717 | throw e;
|
|---|
| 18718 | });
|
|---|
| 18719 | }
|
|---|
| 18720 | }
|
|---|
| 18721 |
|
|---|
| 18722 | var PossiblyWeakMap$1 = typeof WeakMap === 'function' ? WeakMap : Map;
|
|---|
| 18723 |
|
|---|
| 18724 | function createRootErrorUpdate(fiber, errorInfo, lane) {
|
|---|
| 18725 | var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null.
|
|---|
| 18726 |
|
|---|
| 18727 | update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property
|
|---|
| 18728 | // being called "element".
|
|---|
| 18729 |
|
|---|
| 18730 | update.payload = {
|
|---|
| 18731 | element: null
|
|---|
| 18732 | };
|
|---|
| 18733 | var error = errorInfo.value;
|
|---|
| 18734 |
|
|---|
| 18735 | update.callback = function () {
|
|---|
| 18736 | onUncaughtError(error);
|
|---|
| 18737 | logCapturedError(fiber, errorInfo);
|
|---|
| 18738 | };
|
|---|
| 18739 |
|
|---|
| 18740 | return update;
|
|---|
| 18741 | }
|
|---|
| 18742 |
|
|---|
| 18743 | function createClassErrorUpdate(fiber, errorInfo, lane) {
|
|---|
| 18744 | var update = createUpdate(NoTimestamp, lane);
|
|---|
| 18745 | update.tag = CaptureUpdate;
|
|---|
| 18746 | var getDerivedStateFromError = fiber.type.getDerivedStateFromError;
|
|---|
| 18747 |
|
|---|
| 18748 | if (typeof getDerivedStateFromError === 'function') {
|
|---|
| 18749 | var error$1 = errorInfo.value;
|
|---|
| 18750 |
|
|---|
| 18751 | update.payload = function () {
|
|---|
| 18752 | return getDerivedStateFromError(error$1);
|
|---|
| 18753 | };
|
|---|
| 18754 |
|
|---|
| 18755 | update.callback = function () {
|
|---|
| 18756 | {
|
|---|
| 18757 | markFailedErrorBoundaryForHotReloading(fiber);
|
|---|
| 18758 | }
|
|---|
| 18759 |
|
|---|
| 18760 | logCapturedError(fiber, errorInfo);
|
|---|
| 18761 | };
|
|---|
| 18762 | }
|
|---|
| 18763 |
|
|---|
| 18764 | var inst = fiber.stateNode;
|
|---|
| 18765 |
|
|---|
| 18766 | if (inst !== null && typeof inst.componentDidCatch === 'function') {
|
|---|
| 18767 | update.callback = function callback() {
|
|---|
| 18768 | {
|
|---|
| 18769 | markFailedErrorBoundaryForHotReloading(fiber);
|
|---|
| 18770 | }
|
|---|
| 18771 |
|
|---|
| 18772 | logCapturedError(fiber, errorInfo);
|
|---|
| 18773 |
|
|---|
| 18774 | if (typeof getDerivedStateFromError !== 'function') {
|
|---|
| 18775 | // To preserve the preexisting retry behavior of error boundaries,
|
|---|
| 18776 | // we keep track of which ones already failed during this batch.
|
|---|
| 18777 | // This gets reset before we yield back to the browser.
|
|---|
| 18778 | // TODO: Warn in strict mode if getDerivedStateFromError is
|
|---|
| 18779 | // not defined.
|
|---|
| 18780 | markLegacyErrorBoundaryAsFailed(this);
|
|---|
| 18781 | }
|
|---|
| 18782 |
|
|---|
| 18783 | var error$1 = errorInfo.value;
|
|---|
| 18784 | var stack = errorInfo.stack;
|
|---|
| 18785 | this.componentDidCatch(error$1, {
|
|---|
| 18786 | componentStack: stack !== null ? stack : ''
|
|---|
| 18787 | });
|
|---|
| 18788 |
|
|---|
| 18789 | {
|
|---|
| 18790 | if (typeof getDerivedStateFromError !== 'function') {
|
|---|
| 18791 | // If componentDidCatch is the only error boundary method defined,
|
|---|
| 18792 | // then it needs to call setState to recover from errors.
|
|---|
| 18793 | // If no state update is scheduled then the boundary will swallow the error.
|
|---|
| 18794 | if (!includesSomeLane(fiber.lanes, SyncLane)) {
|
|---|
| 18795 | error('%s: Error boundaries should implement getDerivedStateFromError(). ' + 'In that method, return a state update to display an error message or fallback UI.', getComponentNameFromFiber(fiber) || 'Unknown');
|
|---|
| 18796 | }
|
|---|
| 18797 | }
|
|---|
| 18798 | }
|
|---|
| 18799 | };
|
|---|
| 18800 | }
|
|---|
| 18801 |
|
|---|
| 18802 | return update;
|
|---|
| 18803 | }
|
|---|
| 18804 |
|
|---|
| 18805 | function attachPingListener(root, wakeable, lanes) {
|
|---|
| 18806 | // Attach a ping listener
|
|---|
| 18807 | //
|
|---|
| 18808 | // The data might resolve before we have a chance to commit the fallback. Or,
|
|---|
| 18809 | // in the case of a refresh, we'll never commit a fallback. So we need to
|
|---|
| 18810 | // attach a listener now. When it resolves ("pings"), we can decide whether to
|
|---|
| 18811 | // try rendering the tree again.
|
|---|
| 18812 | //
|
|---|
| 18813 | // Only attach a listener if one does not already exist for the lanes
|
|---|
| 18814 | // we're currently rendering (which acts like a "thread ID" here).
|
|---|
| 18815 | //
|
|---|
| 18816 | // We only need to do this in concurrent mode. Legacy Suspense always
|
|---|
| 18817 | // commits fallbacks synchronously, so there are no pings.
|
|---|
| 18818 | var pingCache = root.pingCache;
|
|---|
| 18819 | var threadIDs;
|
|---|
| 18820 |
|
|---|
| 18821 | if (pingCache === null) {
|
|---|
| 18822 | pingCache = root.pingCache = new PossiblyWeakMap$1();
|
|---|
| 18823 | threadIDs = new Set();
|
|---|
| 18824 | pingCache.set(wakeable, threadIDs);
|
|---|
| 18825 | } else {
|
|---|
| 18826 | threadIDs = pingCache.get(wakeable);
|
|---|
| 18827 |
|
|---|
| 18828 | if (threadIDs === undefined) {
|
|---|
| 18829 | threadIDs = new Set();
|
|---|
| 18830 | pingCache.set(wakeable, threadIDs);
|
|---|
| 18831 | }
|
|---|
| 18832 | }
|
|---|
| 18833 |
|
|---|
| 18834 | if (!threadIDs.has(lanes)) {
|
|---|
| 18835 | // Memoize using the thread ID to prevent redundant listeners.
|
|---|
| 18836 | threadIDs.add(lanes);
|
|---|
| 18837 | var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes);
|
|---|
| 18838 |
|
|---|
| 18839 | {
|
|---|
| 18840 | if (isDevToolsPresent) {
|
|---|
| 18841 | // If we have pending work still, restore the original updaters
|
|---|
| 18842 | restorePendingUpdaters(root, lanes);
|
|---|
| 18843 | }
|
|---|
| 18844 | }
|
|---|
| 18845 |
|
|---|
| 18846 | wakeable.then(ping, ping);
|
|---|
| 18847 | }
|
|---|
| 18848 | }
|
|---|
| 18849 |
|
|---|
| 18850 | function attachRetryListener(suspenseBoundary, root, wakeable, lanes) {
|
|---|
| 18851 | // Retry listener
|
|---|
| 18852 | //
|
|---|
| 18853 | // If the fallback does commit, we need to attach a different type of
|
|---|
| 18854 | // listener. This one schedules an update on the Suspense boundary to turn
|
|---|
| 18855 | // the fallback state off.
|
|---|
| 18856 | //
|
|---|
| 18857 | // Stash the wakeable on the boundary fiber so we can access it in the
|
|---|
| 18858 | // commit phase.
|
|---|
| 18859 | //
|
|---|
| 18860 | // When the wakeable resolves, we'll attempt to render the boundary
|
|---|
| 18861 | // again ("retry").
|
|---|
| 18862 | var wakeables = suspenseBoundary.updateQueue;
|
|---|
| 18863 |
|
|---|
| 18864 | if (wakeables === null) {
|
|---|
| 18865 | var updateQueue = new Set();
|
|---|
| 18866 | updateQueue.add(wakeable);
|
|---|
| 18867 | suspenseBoundary.updateQueue = updateQueue;
|
|---|
| 18868 | } else {
|
|---|
| 18869 | wakeables.add(wakeable);
|
|---|
| 18870 | }
|
|---|
| 18871 | }
|
|---|
| 18872 |
|
|---|
| 18873 | function resetSuspendedComponent(sourceFiber, rootRenderLanes) {
|
|---|
| 18874 | // A legacy mode Suspense quirk, only relevant to hook components.
|
|---|
| 18875 |
|
|---|
| 18876 |
|
|---|
| 18877 | var tag = sourceFiber.tag;
|
|---|
| 18878 |
|
|---|
| 18879 | if ((sourceFiber.mode & ConcurrentMode) === NoMode && (tag === FunctionComponent || tag === ForwardRef || tag === SimpleMemoComponent)) {
|
|---|
| 18880 | var currentSource = sourceFiber.alternate;
|
|---|
| 18881 |
|
|---|
| 18882 | if (currentSource) {
|
|---|
| 18883 | sourceFiber.updateQueue = currentSource.updateQueue;
|
|---|
| 18884 | sourceFiber.memoizedState = currentSource.memoizedState;
|
|---|
| 18885 | sourceFiber.lanes = currentSource.lanes;
|
|---|
| 18886 | } else {
|
|---|
| 18887 | sourceFiber.updateQueue = null;
|
|---|
| 18888 | sourceFiber.memoizedState = null;
|
|---|
| 18889 | }
|
|---|
| 18890 | }
|
|---|
| 18891 | }
|
|---|
| 18892 |
|
|---|
| 18893 | function getNearestSuspenseBoundaryToCapture(returnFiber) {
|
|---|
| 18894 | var node = returnFiber;
|
|---|
| 18895 |
|
|---|
| 18896 | do {
|
|---|
| 18897 | if (node.tag === SuspenseComponent && shouldCaptureSuspense(node)) {
|
|---|
| 18898 | return node;
|
|---|
| 18899 | } // This boundary already captured during this render. Continue to the next
|
|---|
| 18900 | // boundary.
|
|---|
| 18901 |
|
|---|
| 18902 |
|
|---|
| 18903 | node = node.return;
|
|---|
| 18904 | } while (node !== null);
|
|---|
| 18905 |
|
|---|
| 18906 | return null;
|
|---|
| 18907 | }
|
|---|
| 18908 |
|
|---|
| 18909 | function markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes) {
|
|---|
| 18910 | // This marks a Suspense boundary so that when we're unwinding the stack,
|
|---|
| 18911 | // it captures the suspended "exception" and does a second (fallback) pass.
|
|---|
| 18912 | if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) {
|
|---|
| 18913 | // Legacy Mode Suspense
|
|---|
| 18914 | //
|
|---|
| 18915 | // If the boundary is in legacy mode, we should *not*
|
|---|
| 18916 | // suspend the commit. Pretend as if the suspended component rendered
|
|---|
| 18917 | // null and keep rendering. When the Suspense boundary completes,
|
|---|
| 18918 | // we'll do a second pass to render the fallback.
|
|---|
| 18919 | if (suspenseBoundary === returnFiber) {
|
|---|
| 18920 | // Special case where we suspended while reconciling the children of
|
|---|
| 18921 | // a Suspense boundary's inner Offscreen wrapper fiber. This happens
|
|---|
| 18922 | // when a React.lazy component is a direct child of a
|
|---|
| 18923 | // Suspense boundary.
|
|---|
| 18924 | //
|
|---|
| 18925 | // Suspense boundaries are implemented as multiple fibers, but they
|
|---|
| 18926 | // are a single conceptual unit. The legacy mode behavior where we
|
|---|
| 18927 | // pretend the suspended fiber committed as `null` won't work,
|
|---|
| 18928 | // because in this case the "suspended" fiber is the inner
|
|---|
| 18929 | // Offscreen wrapper.
|
|---|
| 18930 | //
|
|---|
| 18931 | // Because the contents of the boundary haven't started rendering
|
|---|
| 18932 | // yet (i.e. nothing in the tree has partially rendered) we can
|
|---|
| 18933 | // switch to the regular, concurrent mode behavior: mark the
|
|---|
| 18934 | // boundary with ShouldCapture and enter the unwind phase.
|
|---|
| 18935 | suspenseBoundary.flags |= ShouldCapture;
|
|---|
| 18936 | } else {
|
|---|
| 18937 | suspenseBoundary.flags |= DidCapture;
|
|---|
| 18938 | sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete.
|
|---|
| 18939 | // But we shouldn't call any lifecycle methods or callbacks. Remove
|
|---|
| 18940 | // all lifecycle effect tags.
|
|---|
| 18941 |
|
|---|
| 18942 | sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete);
|
|---|
| 18943 |
|
|---|
| 18944 | if (sourceFiber.tag === ClassComponent) {
|
|---|
| 18945 | var currentSourceFiber = sourceFiber.alternate;
|
|---|
| 18946 |
|
|---|
| 18947 | if (currentSourceFiber === null) {
|
|---|
| 18948 | // This is a new mount. Change the tag so it's not mistaken for a
|
|---|
| 18949 | // completed class component. For example, we should not call
|
|---|
| 18950 | // componentWillUnmount if it is deleted.
|
|---|
| 18951 | sourceFiber.tag = IncompleteClassComponent;
|
|---|
| 18952 | } else {
|
|---|
| 18953 | // When we try rendering again, we should not reuse the current fiber,
|
|---|
| 18954 | // since it's known to be in an inconsistent state. Use a force update to
|
|---|
| 18955 | // prevent a bail out.
|
|---|
| 18956 | var update = createUpdate(NoTimestamp, SyncLane);
|
|---|
| 18957 | update.tag = ForceUpdate;
|
|---|
| 18958 | enqueueUpdate(sourceFiber, update, SyncLane);
|
|---|
| 18959 | }
|
|---|
| 18960 | } // The source fiber did not complete. Mark it with Sync priority to
|
|---|
| 18961 | // indicate that it still has pending work.
|
|---|
| 18962 |
|
|---|
| 18963 |
|
|---|
| 18964 | sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane);
|
|---|
| 18965 | }
|
|---|
| 18966 |
|
|---|
| 18967 | return suspenseBoundary;
|
|---|
| 18968 | } // Confirmed that the boundary is in a concurrent mode tree. Continue
|
|---|
| 18969 | // with the normal suspend path.
|
|---|
| 18970 | //
|
|---|
| 18971 | // After this we'll use a set of heuristics to determine whether this
|
|---|
| 18972 | // render pass will run to completion or restart or "suspend" the commit.
|
|---|
| 18973 | // The actual logic for this is spread out in different places.
|
|---|
| 18974 | //
|
|---|
| 18975 | // This first principle is that if we're going to suspend when we complete
|
|---|
| 18976 | // a root, then we should also restart if we get an update or ping that
|
|---|
| 18977 | // might unsuspend it, and vice versa. The only reason to suspend is
|
|---|
| 18978 | // because you think you might want to restart before committing. However,
|
|---|
| 18979 | // it doesn't make sense to restart only while in the period we're suspended.
|
|---|
| 18980 | //
|
|---|
| 18981 | // Restarting too aggressively is also not good because it starves out any
|
|---|
| 18982 | // intermediate loading state. So we use heuristics to determine when.
|
|---|
| 18983 | // Suspense Heuristics
|
|---|
| 18984 | //
|
|---|
| 18985 | // If nothing threw a Promise or all the same fallbacks are already showing,
|
|---|
| 18986 | // then don't suspend/restart.
|
|---|
| 18987 | //
|
|---|
| 18988 | // If this is an initial render of a new tree of Suspense boundaries and
|
|---|
| 18989 | // those trigger a fallback, then don't suspend/restart. We want to ensure
|
|---|
| 18990 | // that we can show the initial loading state as quickly as possible.
|
|---|
| 18991 | //
|
|---|
| 18992 | // If we hit a "Delayed" case, such as when we'd switch from content back into
|
|---|
| 18993 | // a fallback, then we should always suspend/restart. Transitions apply
|
|---|
| 18994 | // to this case. If none is defined, JND is used instead.
|
|---|
| 18995 | //
|
|---|
| 18996 | // If we're already showing a fallback and it gets "retried", allowing us to show
|
|---|
| 18997 | // another level, but there's still an inner boundary that would show a fallback,
|
|---|
| 18998 | // then we suspend/restart for 500ms since the last time we showed a fallback
|
|---|
| 18999 | // anywhere in the tree. This effectively throttles progressive loading into a
|
|---|
| 19000 | // consistent train of commits. This also gives us an opportunity to restart to
|
|---|
| 19001 | // get to the completed state slightly earlier.
|
|---|
| 19002 | //
|
|---|
| 19003 | // If there's ambiguity due to batching it's resolved in preference of:
|
|---|
| 19004 | // 1) "delayed", 2) "initial render", 3) "retry".
|
|---|
| 19005 | //
|
|---|
| 19006 | // We want to ensure that a "busy" state doesn't get force committed. We want to
|
|---|
| 19007 | // ensure that new initial loading states can commit as soon as possible.
|
|---|
| 19008 |
|
|---|
| 19009 |
|
|---|
| 19010 | suspenseBoundary.flags |= ShouldCapture; // TODO: I think we can remove this, since we now use `DidCapture` in
|
|---|
| 19011 | // the begin phase to prevent an early bailout.
|
|---|
| 19012 |
|
|---|
| 19013 | suspenseBoundary.lanes = rootRenderLanes;
|
|---|
| 19014 | return suspenseBoundary;
|
|---|
| 19015 | }
|
|---|
| 19016 |
|
|---|
| 19017 | function throwException(root, returnFiber, sourceFiber, value, rootRenderLanes) {
|
|---|
| 19018 | // The source fiber did not complete.
|
|---|
| 19019 | sourceFiber.flags |= Incomplete;
|
|---|
| 19020 |
|
|---|
| 19021 | {
|
|---|
| 19022 | if (isDevToolsPresent) {
|
|---|
| 19023 | // If we have pending work still, restore the original updaters
|
|---|
| 19024 | restorePendingUpdaters(root, rootRenderLanes);
|
|---|
| 19025 | }
|
|---|
| 19026 | }
|
|---|
| 19027 |
|
|---|
| 19028 | if (value !== null && typeof value === 'object' && typeof value.then === 'function') {
|
|---|
| 19029 | // This is a wakeable. The component suspended.
|
|---|
| 19030 | var wakeable = value;
|
|---|
| 19031 | resetSuspendedComponent(sourceFiber);
|
|---|
| 19032 |
|
|---|
| 19033 | {
|
|---|
| 19034 | if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
|
|---|
| 19035 | markDidThrowWhileHydratingDEV();
|
|---|
| 19036 | }
|
|---|
| 19037 | }
|
|---|
| 19038 |
|
|---|
| 19039 |
|
|---|
| 19040 | var suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber);
|
|---|
| 19041 |
|
|---|
| 19042 | if (suspenseBoundary !== null) {
|
|---|
| 19043 | suspenseBoundary.flags &= ~ForceClientRender;
|
|---|
| 19044 | markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // We only attach ping listeners in concurrent mode. Legacy Suspense always
|
|---|
| 19045 | // commits fallbacks synchronously, so there are no pings.
|
|---|
| 19046 |
|
|---|
| 19047 | if (suspenseBoundary.mode & ConcurrentMode) {
|
|---|
| 19048 | attachPingListener(root, wakeable, rootRenderLanes);
|
|---|
| 19049 | }
|
|---|
| 19050 |
|
|---|
| 19051 | attachRetryListener(suspenseBoundary, root, wakeable);
|
|---|
| 19052 | return;
|
|---|
| 19053 | } else {
|
|---|
| 19054 | // No boundary was found. Unless this is a sync update, this is OK.
|
|---|
| 19055 | // We can suspend and wait for more data to arrive.
|
|---|
| 19056 | if (!includesSyncLane(rootRenderLanes)) {
|
|---|
| 19057 | // This is not a sync update. Suspend. Since we're not activating a
|
|---|
| 19058 | // Suspense boundary, this will unwind all the way to the root without
|
|---|
| 19059 | // performing a second pass to render a fallback. (This is arguably how
|
|---|
| 19060 | // refresh transitions should work, too, since we're not going to commit
|
|---|
| 19061 | // the fallbacks anyway.)
|
|---|
| 19062 | //
|
|---|
| 19063 | // This case also applies to initial hydration.
|
|---|
| 19064 | attachPingListener(root, wakeable, rootRenderLanes);
|
|---|
| 19065 | renderDidSuspendDelayIfPossible();
|
|---|
| 19066 | return;
|
|---|
| 19067 | } // This is a sync/discrete update. We treat this case like an error
|
|---|
| 19068 | // because discrete renders are expected to produce a complete tree
|
|---|
| 19069 | // synchronously to maintain consistency with external state.
|
|---|
| 19070 |
|
|---|
| 19071 |
|
|---|
| 19072 | var uncaughtSuspenseError = new Error('A component suspended while responding to synchronous input. This ' + 'will cause the UI to be replaced with a loading indicator. To ' + 'fix, updates that suspend should be wrapped ' + 'with startTransition.'); // If we're outside a transition, fall through to the regular error path.
|
|---|
| 19073 | // The error will be caught by the nearest suspense boundary.
|
|---|
| 19074 |
|
|---|
| 19075 | value = uncaughtSuspenseError;
|
|---|
| 19076 | }
|
|---|
| 19077 | } else {
|
|---|
| 19078 | // This is a regular error, not a Suspense wakeable.
|
|---|
| 19079 | if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) {
|
|---|
| 19080 | markDidThrowWhileHydratingDEV();
|
|---|
| 19081 |
|
|---|
| 19082 | var _suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber); // If the error was thrown during hydration, we may be able to recover by
|
|---|
| 19083 | // discarding the dehydrated content and switching to a client render.
|
|---|
| 19084 | // Instead of surfacing the error, find the nearest Suspense boundary
|
|---|
| 19085 | // and render it again without hydration.
|
|---|
| 19086 |
|
|---|
| 19087 |
|
|---|
| 19088 | if (_suspenseBoundary !== null) {
|
|---|
| 19089 | if ((_suspenseBoundary.flags & ShouldCapture) === NoFlags) {
|
|---|
| 19090 | // Set a flag to indicate that we should try rendering the normal
|
|---|
| 19091 | // children again, not the fallback.
|
|---|
| 19092 | _suspenseBoundary.flags |= ForceClientRender;
|
|---|
| 19093 | }
|
|---|
| 19094 |
|
|---|
| 19095 | markSuspenseBoundaryShouldCapture(_suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // Even though the user may not be affected by this error, we should
|
|---|
| 19096 | // still log it so it can be fixed.
|
|---|
| 19097 |
|
|---|
| 19098 | queueHydrationError(createCapturedValueAtFiber(value, sourceFiber));
|
|---|
| 19099 | return;
|
|---|
| 19100 | }
|
|---|
| 19101 | }
|
|---|
| 19102 | }
|
|---|
| 19103 |
|
|---|
| 19104 | value = createCapturedValueAtFiber(value, sourceFiber);
|
|---|
| 19105 | renderDidError(value); // We didn't find a boundary that could handle this type of exception. Start
|
|---|
| 19106 | // over and traverse parent path again, this time treating the exception
|
|---|
| 19107 | // as an error.
|
|---|
| 19108 |
|
|---|
| 19109 | var workInProgress = returnFiber;
|
|---|
| 19110 |
|
|---|
| 19111 | do {
|
|---|
| 19112 | switch (workInProgress.tag) {
|
|---|
| 19113 | case HostRoot:
|
|---|
| 19114 | {
|
|---|
| 19115 | var _errorInfo = value;
|
|---|
| 19116 | workInProgress.flags |= ShouldCapture;
|
|---|
| 19117 | var lane = pickArbitraryLane(rootRenderLanes);
|
|---|
| 19118 | workInProgress.lanes = mergeLanes(workInProgress.lanes, lane);
|
|---|
| 19119 | var update = createRootErrorUpdate(workInProgress, _errorInfo, lane);
|
|---|
| 19120 | enqueueCapturedUpdate(workInProgress, update);
|
|---|
| 19121 | return;
|
|---|
| 19122 | }
|
|---|
| 19123 |
|
|---|
| 19124 | case ClassComponent:
|
|---|
| 19125 | // Capture and retry
|
|---|
| 19126 | var errorInfo = value;
|
|---|
| 19127 | var ctor = workInProgress.type;
|
|---|
| 19128 | var instance = workInProgress.stateNode;
|
|---|
| 19129 |
|
|---|
| 19130 | if ((workInProgress.flags & DidCapture) === NoFlags && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) {
|
|---|
| 19131 | workInProgress.flags |= ShouldCapture;
|
|---|
| 19132 |
|
|---|
| 19133 | var _lane = pickArbitraryLane(rootRenderLanes);
|
|---|
| 19134 |
|
|---|
| 19135 | workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state
|
|---|
| 19136 |
|
|---|
| 19137 | var _update = createClassErrorUpdate(workInProgress, errorInfo, _lane);
|
|---|
| 19138 |
|
|---|
| 19139 | enqueueCapturedUpdate(workInProgress, _update);
|
|---|
| 19140 | return;
|
|---|
| 19141 | }
|
|---|
| 19142 |
|
|---|
| 19143 | break;
|
|---|
| 19144 | }
|
|---|
| 19145 |
|
|---|
| 19146 | workInProgress = workInProgress.return;
|
|---|
| 19147 | } while (workInProgress !== null);
|
|---|
| 19148 | }
|
|---|
| 19149 |
|
|---|
| 19150 | function getSuspendedCache() {
|
|---|
| 19151 | {
|
|---|
| 19152 | return null;
|
|---|
| 19153 | } // This function is called when a Suspense boundary suspends. It returns the
|
|---|
| 19154 | }
|
|---|
| 19155 |
|
|---|
| 19156 | var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner;
|
|---|
| 19157 | var didReceiveUpdate = false;
|
|---|
| 19158 | var didWarnAboutBadClass;
|
|---|
| 19159 | var didWarnAboutModulePatternComponent;
|
|---|
| 19160 | var didWarnAboutContextTypeOnFunctionComponent;
|
|---|
| 19161 | var didWarnAboutGetDerivedStateOnFunctionComponent;
|
|---|
| 19162 | var didWarnAboutFunctionRefs;
|
|---|
| 19163 | var didWarnAboutReassigningProps;
|
|---|
| 19164 | var didWarnAboutRevealOrder;
|
|---|
| 19165 | var didWarnAboutTailOptions;
|
|---|
| 19166 | var didWarnAboutDefaultPropsOnFunctionComponent;
|
|---|
| 19167 |
|
|---|
| 19168 | {
|
|---|
| 19169 | didWarnAboutBadClass = {};
|
|---|
| 19170 | didWarnAboutModulePatternComponent = {};
|
|---|
| 19171 | didWarnAboutContextTypeOnFunctionComponent = {};
|
|---|
| 19172 | didWarnAboutGetDerivedStateOnFunctionComponent = {};
|
|---|
| 19173 | didWarnAboutFunctionRefs = {};
|
|---|
| 19174 | didWarnAboutReassigningProps = false;
|
|---|
| 19175 | didWarnAboutRevealOrder = {};
|
|---|
| 19176 | didWarnAboutTailOptions = {};
|
|---|
| 19177 | didWarnAboutDefaultPropsOnFunctionComponent = {};
|
|---|
| 19178 | }
|
|---|
| 19179 |
|
|---|
| 19180 | function reconcileChildren(current, workInProgress, nextChildren, renderLanes) {
|
|---|
| 19181 | if (current === null) {
|
|---|
| 19182 | // If this is a fresh new component that hasn't been rendered yet, we
|
|---|
| 19183 | // won't update its child set by applying minimal side-effects. Instead,
|
|---|
| 19184 | // we will add them all to the child before it gets rendered. That means
|
|---|
| 19185 | // we can optimize this reconciliation pass by not tracking side-effects.
|
|---|
| 19186 | workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
|
|---|
| 19187 | } else {
|
|---|
| 19188 | // If the current child is the same as the work in progress, it means that
|
|---|
| 19189 | // we haven't yet started any work on these children. Therefore, we use
|
|---|
| 19190 | // the clone algorithm to create a copy of all the current children.
|
|---|
| 19191 | // If we had any progressed work already, that is invalid at this point so
|
|---|
| 19192 | // let's throw it out.
|
|---|
| 19193 | workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes);
|
|---|
| 19194 | }
|
|---|
| 19195 | }
|
|---|
| 19196 |
|
|---|
| 19197 | function forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes) {
|
|---|
| 19198 | // This function is fork of reconcileChildren. It's used in cases where we
|
|---|
| 19199 | // want to reconcile without matching against the existing set. This has the
|
|---|
| 19200 | // effect of all current children being unmounted; even if the type and key
|
|---|
| 19201 | // are the same, the old child is unmounted and a new child is created.
|
|---|
| 19202 | //
|
|---|
| 19203 | // To do this, we're going to go through the reconcile algorithm twice. In
|
|---|
| 19204 | // the first pass, we schedule a deletion for all the current children by
|
|---|
| 19205 | // passing null.
|
|---|
| 19206 | workInProgress.child = reconcileChildFibers(workInProgress, current.child, null, renderLanes); // In the second pass, we mount the new children. The trick here is that we
|
|---|
| 19207 | // pass null in place of where we usually pass the current child set. This has
|
|---|
| 19208 | // the effect of remounting all children regardless of whether their
|
|---|
| 19209 | // identities match.
|
|---|
| 19210 |
|
|---|
| 19211 | workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes);
|
|---|
| 19212 | }
|
|---|
| 19213 |
|
|---|
| 19214 | function updateForwardRef(current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 19215 | // TODO: current can be non-null here even if the component
|
|---|
| 19216 | // hasn't yet mounted. This happens after the first render suspends.
|
|---|
| 19217 | // We'll need to figure out if this is fine or can cause issues.
|
|---|
| 19218 | {
|
|---|
| 19219 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 19220 | // Lazy component props can't be validated in createElement
|
|---|
| 19221 | // because they're only guaranteed to be resolved here.
|
|---|
| 19222 | var innerPropTypes = Component.propTypes;
|
|---|
| 19223 |
|
|---|
| 19224 | if (innerPropTypes) {
|
|---|
| 19225 | checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
|---|
| 19226 | 'prop', getComponentNameFromType(Component));
|
|---|
| 19227 | }
|
|---|
| 19228 | }
|
|---|
| 19229 | }
|
|---|
| 19230 |
|
|---|
| 19231 | var render = Component.render;
|
|---|
| 19232 | var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent
|
|---|
| 19233 |
|
|---|
| 19234 | var nextChildren;
|
|---|
| 19235 | var hasId;
|
|---|
| 19236 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 19237 |
|
|---|
| 19238 | {
|
|---|
| 19239 | markComponentRenderStarted(workInProgress);
|
|---|
| 19240 | }
|
|---|
| 19241 |
|
|---|
| 19242 | {
|
|---|
| 19243 | ReactCurrentOwner$1.current = workInProgress;
|
|---|
| 19244 | setIsRendering(true);
|
|---|
| 19245 | nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes);
|
|---|
| 19246 | hasId = checkDidRenderIdHook();
|
|---|
| 19247 |
|
|---|
| 19248 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 19249 | setIsStrictModeForDevtools(true);
|
|---|
| 19250 |
|
|---|
| 19251 | try {
|
|---|
| 19252 | nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes);
|
|---|
| 19253 | hasId = checkDidRenderIdHook();
|
|---|
| 19254 | } finally {
|
|---|
| 19255 | setIsStrictModeForDevtools(false);
|
|---|
| 19256 | }
|
|---|
| 19257 | }
|
|---|
| 19258 |
|
|---|
| 19259 | setIsRendering(false);
|
|---|
| 19260 | }
|
|---|
| 19261 |
|
|---|
| 19262 | {
|
|---|
| 19263 | markComponentRenderStopped();
|
|---|
| 19264 | }
|
|---|
| 19265 |
|
|---|
| 19266 | if (current !== null && !didReceiveUpdate) {
|
|---|
| 19267 | bailoutHooks(current, workInProgress, renderLanes);
|
|---|
| 19268 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19269 | }
|
|---|
| 19270 |
|
|---|
| 19271 | if (getIsHydrating() && hasId) {
|
|---|
| 19272 | pushMaterializedTreeId(workInProgress);
|
|---|
| 19273 | } // React DevTools reads this flag.
|
|---|
| 19274 |
|
|---|
| 19275 |
|
|---|
| 19276 | workInProgress.flags |= PerformedWork;
|
|---|
| 19277 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19278 | return workInProgress.child;
|
|---|
| 19279 | }
|
|---|
| 19280 |
|
|---|
| 19281 | function updateMemoComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 19282 | if (current === null) {
|
|---|
| 19283 | var type = Component.type;
|
|---|
| 19284 |
|
|---|
| 19285 | if (isSimpleFunctionComponent(type) && Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either.
|
|---|
| 19286 | Component.defaultProps === undefined) {
|
|---|
| 19287 | var resolvedType = type;
|
|---|
| 19288 |
|
|---|
| 19289 | {
|
|---|
| 19290 | resolvedType = resolveFunctionForHotReloading(type);
|
|---|
| 19291 | } // If this is a plain function component without default props,
|
|---|
| 19292 | // and with only the default shallow comparison, we upgrade it
|
|---|
| 19293 | // to a SimpleMemoComponent to allow fast path updates.
|
|---|
| 19294 |
|
|---|
| 19295 |
|
|---|
| 19296 | workInProgress.tag = SimpleMemoComponent;
|
|---|
| 19297 | workInProgress.type = resolvedType;
|
|---|
| 19298 |
|
|---|
| 19299 | {
|
|---|
| 19300 | validateFunctionComponentInDev(workInProgress, type);
|
|---|
| 19301 | }
|
|---|
| 19302 |
|
|---|
| 19303 | return updateSimpleMemoComponent(current, workInProgress, resolvedType, nextProps, renderLanes);
|
|---|
| 19304 | }
|
|---|
| 19305 |
|
|---|
| 19306 | {
|
|---|
| 19307 | var innerPropTypes = type.propTypes;
|
|---|
| 19308 |
|
|---|
| 19309 | if (innerPropTypes) {
|
|---|
| 19310 | // Inner memo component props aren't currently validated in createElement.
|
|---|
| 19311 | // We could move it there, but we'd still need this for lazy code path.
|
|---|
| 19312 | checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
|---|
| 19313 | 'prop', getComponentNameFromType(type));
|
|---|
| 19314 | }
|
|---|
| 19315 |
|
|---|
| 19316 | if ( Component.defaultProps !== undefined) {
|
|---|
| 19317 | var componentName = getComponentNameFromType(type) || 'Unknown';
|
|---|
| 19318 |
|
|---|
| 19319 | if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
|
|---|
| 19320 | error('%s: Support for defaultProps will be removed from memo components ' + 'in a future major release. Use JavaScript default parameters instead.', componentName);
|
|---|
| 19321 |
|
|---|
| 19322 | didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
|
|---|
| 19323 | }
|
|---|
| 19324 | }
|
|---|
| 19325 | }
|
|---|
| 19326 |
|
|---|
| 19327 | var child = createFiberFromTypeAndProps(Component.type, null, nextProps, workInProgress, workInProgress.mode, renderLanes);
|
|---|
| 19328 | child.ref = workInProgress.ref;
|
|---|
| 19329 | child.return = workInProgress;
|
|---|
| 19330 | workInProgress.child = child;
|
|---|
| 19331 | return child;
|
|---|
| 19332 | }
|
|---|
| 19333 |
|
|---|
| 19334 | {
|
|---|
| 19335 | var _type = Component.type;
|
|---|
| 19336 | var _innerPropTypes = _type.propTypes;
|
|---|
| 19337 |
|
|---|
| 19338 | if (_innerPropTypes) {
|
|---|
| 19339 | // Inner memo component props aren't currently validated in createElement.
|
|---|
| 19340 | // We could move it there, but we'd still need this for lazy code path.
|
|---|
| 19341 | checkPropTypes(_innerPropTypes, nextProps, // Resolved props
|
|---|
| 19342 | 'prop', getComponentNameFromType(_type));
|
|---|
| 19343 | }
|
|---|
| 19344 | }
|
|---|
| 19345 |
|
|---|
| 19346 | var currentChild = current.child; // This is always exactly one child
|
|---|
| 19347 |
|
|---|
| 19348 | var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);
|
|---|
| 19349 |
|
|---|
| 19350 | if (!hasScheduledUpdateOrContext) {
|
|---|
| 19351 | // This will be the props with resolved defaultProps,
|
|---|
| 19352 | // unlike current.memoizedProps which will be the unresolved ones.
|
|---|
| 19353 | var prevProps = currentChild.memoizedProps; // Default to shallow comparison
|
|---|
| 19354 |
|
|---|
| 19355 | var compare = Component.compare;
|
|---|
| 19356 | compare = compare !== null ? compare : shallowEqual;
|
|---|
| 19357 |
|
|---|
| 19358 | if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {
|
|---|
| 19359 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19360 | }
|
|---|
| 19361 | } // React DevTools reads this flag.
|
|---|
| 19362 |
|
|---|
| 19363 |
|
|---|
| 19364 | workInProgress.flags |= PerformedWork;
|
|---|
| 19365 | var newChild = createWorkInProgress(currentChild, nextProps);
|
|---|
| 19366 | newChild.ref = workInProgress.ref;
|
|---|
| 19367 | newChild.return = workInProgress;
|
|---|
| 19368 | workInProgress.child = newChild;
|
|---|
| 19369 | return newChild;
|
|---|
| 19370 | }
|
|---|
| 19371 |
|
|---|
| 19372 | function updateSimpleMemoComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 19373 | // TODO: current can be non-null here even if the component
|
|---|
| 19374 | // hasn't yet mounted. This happens when the inner render suspends.
|
|---|
| 19375 | // We'll need to figure out if this is fine or can cause issues.
|
|---|
| 19376 | {
|
|---|
| 19377 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 19378 | // Lazy component props can't be validated in createElement
|
|---|
| 19379 | // because they're only guaranteed to be resolved here.
|
|---|
| 19380 | var outerMemoType = workInProgress.elementType;
|
|---|
| 19381 |
|
|---|
| 19382 | if (outerMemoType.$$typeof === REACT_LAZY_TYPE) {
|
|---|
| 19383 | // We warn when you define propTypes on lazy()
|
|---|
| 19384 | // so let's just skip over it to find memo() outer wrapper.
|
|---|
| 19385 | // Inner props for memo are validated later.
|
|---|
| 19386 | var lazyComponent = outerMemoType;
|
|---|
| 19387 | var payload = lazyComponent._payload;
|
|---|
| 19388 | var init = lazyComponent._init;
|
|---|
| 19389 |
|
|---|
| 19390 | try {
|
|---|
| 19391 | outerMemoType = init(payload);
|
|---|
| 19392 | } catch (x) {
|
|---|
| 19393 | outerMemoType = null;
|
|---|
| 19394 | } // Inner propTypes will be validated in the function component path.
|
|---|
| 19395 |
|
|---|
| 19396 |
|
|---|
| 19397 | var outerPropTypes = outerMemoType && outerMemoType.propTypes;
|
|---|
| 19398 |
|
|---|
| 19399 | if (outerPropTypes) {
|
|---|
| 19400 | checkPropTypes(outerPropTypes, nextProps, // Resolved (SimpleMemoComponent has no defaultProps)
|
|---|
| 19401 | 'prop', getComponentNameFromType(outerMemoType));
|
|---|
| 19402 | }
|
|---|
| 19403 | }
|
|---|
| 19404 | }
|
|---|
| 19405 | }
|
|---|
| 19406 |
|
|---|
| 19407 | if (current !== null) {
|
|---|
| 19408 | var prevProps = current.memoizedProps;
|
|---|
| 19409 |
|
|---|
| 19410 | if (shallowEqual(prevProps, nextProps) && current.ref === workInProgress.ref && ( // Prevent bailout if the implementation changed due to hot reload.
|
|---|
| 19411 | workInProgress.type === current.type )) {
|
|---|
| 19412 | didReceiveUpdate = false; // The props are shallowly equal. Reuse the previous props object, like we
|
|---|
| 19413 | // would during a normal fiber bailout.
|
|---|
| 19414 | //
|
|---|
| 19415 | // We don't have strong guarantees that the props object is referentially
|
|---|
| 19416 | // equal during updates where we can't bail out anyway — like if the props
|
|---|
| 19417 | // are shallowly equal, but there's a local state or context update in the
|
|---|
| 19418 | // same batch.
|
|---|
| 19419 | //
|
|---|
| 19420 | // However, as a principle, we should aim to make the behavior consistent
|
|---|
| 19421 | // across different ways of memoizing a component. For example, React.memo
|
|---|
| 19422 | // has a different internal Fiber layout if you pass a normal function
|
|---|
| 19423 | // component (SimpleMemoComponent) versus if you pass a different type
|
|---|
| 19424 | // like forwardRef (MemoComponent). But this is an implementation detail.
|
|---|
| 19425 | // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't
|
|---|
| 19426 | // affect whether the props object is reused during a bailout.
|
|---|
| 19427 |
|
|---|
| 19428 | workInProgress.pendingProps = nextProps = prevProps;
|
|---|
| 19429 |
|
|---|
| 19430 | if (!checkScheduledUpdateOrContext(current, renderLanes)) {
|
|---|
| 19431 | // The pending lanes were cleared at the beginning of beginWork. We're
|
|---|
| 19432 | // about to bail out, but there might be other lanes that weren't
|
|---|
| 19433 | // included in the current render. Usually, the priority level of the
|
|---|
| 19434 | // remaining updates is accumulated during the evaluation of the
|
|---|
| 19435 | // component (i.e. when processing the update queue). But since since
|
|---|
| 19436 | // we're bailing out early *without* evaluating the component, we need
|
|---|
| 19437 | // to account for it here, too. Reset to the value of the current fiber.
|
|---|
| 19438 | // NOTE: This only applies to SimpleMemoComponent, not MemoComponent,
|
|---|
| 19439 | // because a MemoComponent fiber does not have hooks or an update queue;
|
|---|
| 19440 | // rather, it wraps around an inner component, which may or may not
|
|---|
| 19441 | // contains hooks.
|
|---|
| 19442 | // TODO: Move the reset at in beginWork out of the common path so that
|
|---|
| 19443 | // this is no longer necessary.
|
|---|
| 19444 | workInProgress.lanes = current.lanes;
|
|---|
| 19445 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19446 | } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
|
|---|
| 19447 | // This is a special case that only exists for legacy mode.
|
|---|
| 19448 | // See https://github.com/facebook/react/pull/19216.
|
|---|
| 19449 | didReceiveUpdate = true;
|
|---|
| 19450 | }
|
|---|
| 19451 | }
|
|---|
| 19452 | }
|
|---|
| 19453 |
|
|---|
| 19454 | return updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes);
|
|---|
| 19455 | }
|
|---|
| 19456 |
|
|---|
| 19457 | function updateOffscreenComponent(current, workInProgress, renderLanes) {
|
|---|
| 19458 | var nextProps = workInProgress.pendingProps;
|
|---|
| 19459 | var nextChildren = nextProps.children;
|
|---|
| 19460 | var prevState = current !== null ? current.memoizedState : null;
|
|---|
| 19461 |
|
|---|
| 19462 | if (nextProps.mode === 'hidden' || enableLegacyHidden ) {
|
|---|
| 19463 | // Rendering a hidden tree.
|
|---|
| 19464 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 19465 | // In legacy sync mode, don't defer the subtree. Render it now.
|
|---|
| 19466 | // TODO: Consider how Offscreen should work with transitions in the future
|
|---|
| 19467 | var nextState = {
|
|---|
| 19468 | baseLanes: NoLanes,
|
|---|
| 19469 | cachePool: null,
|
|---|
| 19470 | transitions: null
|
|---|
| 19471 | };
|
|---|
| 19472 | workInProgress.memoizedState = nextState;
|
|---|
| 19473 |
|
|---|
| 19474 | pushRenderLanes(workInProgress, renderLanes);
|
|---|
| 19475 | } else if (!includesSomeLane(renderLanes, OffscreenLane)) {
|
|---|
| 19476 | var spawnedCachePool = null; // We're hidden, and we're not rendering at Offscreen. We will bail out
|
|---|
| 19477 | // and resume this tree later.
|
|---|
| 19478 |
|
|---|
| 19479 | var nextBaseLanes;
|
|---|
| 19480 |
|
|---|
| 19481 | if (prevState !== null) {
|
|---|
| 19482 | var prevBaseLanes = prevState.baseLanes;
|
|---|
| 19483 | nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes);
|
|---|
| 19484 | } else {
|
|---|
| 19485 | nextBaseLanes = renderLanes;
|
|---|
| 19486 | } // Schedule this fiber to re-render at offscreen priority. Then bailout.
|
|---|
| 19487 |
|
|---|
| 19488 |
|
|---|
| 19489 | workInProgress.lanes = workInProgress.childLanes = laneToLanes(OffscreenLane);
|
|---|
| 19490 | var _nextState = {
|
|---|
| 19491 | baseLanes: nextBaseLanes,
|
|---|
| 19492 | cachePool: spawnedCachePool,
|
|---|
| 19493 | transitions: null
|
|---|
| 19494 | };
|
|---|
| 19495 | workInProgress.memoizedState = _nextState;
|
|---|
| 19496 | workInProgress.updateQueue = null;
|
|---|
| 19497 | // to avoid a push/pop misalignment.
|
|---|
| 19498 |
|
|---|
| 19499 |
|
|---|
| 19500 | pushRenderLanes(workInProgress, nextBaseLanes);
|
|---|
| 19501 |
|
|---|
| 19502 | return null;
|
|---|
| 19503 | } else {
|
|---|
| 19504 | // This is the second render. The surrounding visible content has already
|
|---|
| 19505 | // committed. Now we resume rendering the hidden tree.
|
|---|
| 19506 | // Rendering at offscreen, so we can clear the base lanes.
|
|---|
| 19507 | var _nextState2 = {
|
|---|
| 19508 | baseLanes: NoLanes,
|
|---|
| 19509 | cachePool: null,
|
|---|
| 19510 | transitions: null
|
|---|
| 19511 | };
|
|---|
| 19512 | workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out.
|
|---|
| 19513 |
|
|---|
| 19514 | var subtreeRenderLanes = prevState !== null ? prevState.baseLanes : renderLanes;
|
|---|
| 19515 |
|
|---|
| 19516 | pushRenderLanes(workInProgress, subtreeRenderLanes);
|
|---|
| 19517 | }
|
|---|
| 19518 | } else {
|
|---|
| 19519 | // Rendering a visible tree.
|
|---|
| 19520 | var _subtreeRenderLanes;
|
|---|
| 19521 |
|
|---|
| 19522 | if (prevState !== null) {
|
|---|
| 19523 | // We're going from hidden -> visible.
|
|---|
| 19524 | _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes);
|
|---|
| 19525 |
|
|---|
| 19526 | workInProgress.memoizedState = null;
|
|---|
| 19527 | } else {
|
|---|
| 19528 | // We weren't previously hidden, and we still aren't, so there's nothing
|
|---|
| 19529 | // special to do. Need to push to the stack regardless, though, to avoid
|
|---|
| 19530 | // a push/pop misalignment.
|
|---|
| 19531 | _subtreeRenderLanes = renderLanes;
|
|---|
| 19532 | }
|
|---|
| 19533 |
|
|---|
| 19534 | pushRenderLanes(workInProgress, _subtreeRenderLanes);
|
|---|
| 19535 | }
|
|---|
| 19536 |
|
|---|
| 19537 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19538 | return workInProgress.child;
|
|---|
| 19539 | } // Note: These happen to have identical begin phases, for now. We shouldn't hold
|
|---|
| 19540 |
|
|---|
| 19541 | function updateFragment(current, workInProgress, renderLanes) {
|
|---|
| 19542 | var nextChildren = workInProgress.pendingProps;
|
|---|
| 19543 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19544 | return workInProgress.child;
|
|---|
| 19545 | }
|
|---|
| 19546 |
|
|---|
| 19547 | function updateMode(current, workInProgress, renderLanes) {
|
|---|
| 19548 | var nextChildren = workInProgress.pendingProps.children;
|
|---|
| 19549 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19550 | return workInProgress.child;
|
|---|
| 19551 | }
|
|---|
| 19552 |
|
|---|
| 19553 | function updateProfiler(current, workInProgress, renderLanes) {
|
|---|
| 19554 | {
|
|---|
| 19555 | workInProgress.flags |= Update;
|
|---|
| 19556 |
|
|---|
| 19557 | {
|
|---|
| 19558 | // Reset effect durations for the next eventual effect phase.
|
|---|
| 19559 | // These are reset during render to allow the DevTools commit hook a chance to read them,
|
|---|
| 19560 | var stateNode = workInProgress.stateNode;
|
|---|
| 19561 | stateNode.effectDuration = 0;
|
|---|
| 19562 | stateNode.passiveEffectDuration = 0;
|
|---|
| 19563 | }
|
|---|
| 19564 | }
|
|---|
| 19565 |
|
|---|
| 19566 | var nextProps = workInProgress.pendingProps;
|
|---|
| 19567 | var nextChildren = nextProps.children;
|
|---|
| 19568 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19569 | return workInProgress.child;
|
|---|
| 19570 | }
|
|---|
| 19571 |
|
|---|
| 19572 | function markRef(current, workInProgress) {
|
|---|
| 19573 | var ref = workInProgress.ref;
|
|---|
| 19574 |
|
|---|
| 19575 | if (current === null && ref !== null || current !== null && current.ref !== ref) {
|
|---|
| 19576 | // Schedule a Ref effect
|
|---|
| 19577 | workInProgress.flags |= Ref;
|
|---|
| 19578 |
|
|---|
| 19579 | {
|
|---|
| 19580 | workInProgress.flags |= RefStatic;
|
|---|
| 19581 | }
|
|---|
| 19582 | }
|
|---|
| 19583 | }
|
|---|
| 19584 |
|
|---|
| 19585 | function updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 19586 | {
|
|---|
| 19587 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 19588 | // Lazy component props can't be validated in createElement
|
|---|
| 19589 | // because they're only guaranteed to be resolved here.
|
|---|
| 19590 | var innerPropTypes = Component.propTypes;
|
|---|
| 19591 |
|
|---|
| 19592 | if (innerPropTypes) {
|
|---|
| 19593 | checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
|---|
| 19594 | 'prop', getComponentNameFromType(Component));
|
|---|
| 19595 | }
|
|---|
| 19596 | }
|
|---|
| 19597 | }
|
|---|
| 19598 |
|
|---|
| 19599 | var context;
|
|---|
| 19600 |
|
|---|
| 19601 | {
|
|---|
| 19602 | var unmaskedContext = getUnmaskedContext(workInProgress, Component, true);
|
|---|
| 19603 | context = getMaskedContext(workInProgress, unmaskedContext);
|
|---|
| 19604 | }
|
|---|
| 19605 |
|
|---|
| 19606 | var nextChildren;
|
|---|
| 19607 | var hasId;
|
|---|
| 19608 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 19609 |
|
|---|
| 19610 | {
|
|---|
| 19611 | markComponentRenderStarted(workInProgress);
|
|---|
| 19612 | }
|
|---|
| 19613 |
|
|---|
| 19614 | {
|
|---|
| 19615 | ReactCurrentOwner$1.current = workInProgress;
|
|---|
| 19616 | setIsRendering(true);
|
|---|
| 19617 | nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
|
|---|
| 19618 | hasId = checkDidRenderIdHook();
|
|---|
| 19619 |
|
|---|
| 19620 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 19621 | setIsStrictModeForDevtools(true);
|
|---|
| 19622 |
|
|---|
| 19623 | try {
|
|---|
| 19624 | nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes);
|
|---|
| 19625 | hasId = checkDidRenderIdHook();
|
|---|
| 19626 | } finally {
|
|---|
| 19627 | setIsStrictModeForDevtools(false);
|
|---|
| 19628 | }
|
|---|
| 19629 | }
|
|---|
| 19630 |
|
|---|
| 19631 | setIsRendering(false);
|
|---|
| 19632 | }
|
|---|
| 19633 |
|
|---|
| 19634 | {
|
|---|
| 19635 | markComponentRenderStopped();
|
|---|
| 19636 | }
|
|---|
| 19637 |
|
|---|
| 19638 | if (current !== null && !didReceiveUpdate) {
|
|---|
| 19639 | bailoutHooks(current, workInProgress, renderLanes);
|
|---|
| 19640 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19641 | }
|
|---|
| 19642 |
|
|---|
| 19643 | if (getIsHydrating() && hasId) {
|
|---|
| 19644 | pushMaterializedTreeId(workInProgress);
|
|---|
| 19645 | } // React DevTools reads this flag.
|
|---|
| 19646 |
|
|---|
| 19647 |
|
|---|
| 19648 | workInProgress.flags |= PerformedWork;
|
|---|
| 19649 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19650 | return workInProgress.child;
|
|---|
| 19651 | }
|
|---|
| 19652 |
|
|---|
| 19653 | function updateClassComponent(current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 19654 | {
|
|---|
| 19655 | // This is used by DevTools to force a boundary to error.
|
|---|
| 19656 | switch (shouldError(workInProgress)) {
|
|---|
| 19657 | case false:
|
|---|
| 19658 | {
|
|---|
| 19659 | var _instance = workInProgress.stateNode;
|
|---|
| 19660 | var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack.
|
|---|
| 19661 | // Is there a better way to do this?
|
|---|
| 19662 |
|
|---|
| 19663 | var tempInstance = new ctor(workInProgress.memoizedProps, _instance.context);
|
|---|
| 19664 | var state = tempInstance.state;
|
|---|
| 19665 |
|
|---|
| 19666 | _instance.updater.enqueueSetState(_instance, state, null);
|
|---|
| 19667 |
|
|---|
| 19668 | break;
|
|---|
| 19669 | }
|
|---|
| 19670 |
|
|---|
| 19671 | case true:
|
|---|
| 19672 | {
|
|---|
| 19673 | workInProgress.flags |= DidCapture;
|
|---|
| 19674 | workInProgress.flags |= ShouldCapture; // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 19675 |
|
|---|
| 19676 | var error$1 = new Error('Simulated error coming from DevTools');
|
|---|
| 19677 | var lane = pickArbitraryLane(renderLanes);
|
|---|
| 19678 | workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state
|
|---|
| 19679 |
|
|---|
| 19680 | var update = createClassErrorUpdate(workInProgress, createCapturedValueAtFiber(error$1, workInProgress), lane);
|
|---|
| 19681 | enqueueCapturedUpdate(workInProgress, update);
|
|---|
| 19682 | break;
|
|---|
| 19683 | }
|
|---|
| 19684 | }
|
|---|
| 19685 |
|
|---|
| 19686 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 19687 | // Lazy component props can't be validated in createElement
|
|---|
| 19688 | // because they're only guaranteed to be resolved here.
|
|---|
| 19689 | var innerPropTypes = Component.propTypes;
|
|---|
| 19690 |
|
|---|
| 19691 | if (innerPropTypes) {
|
|---|
| 19692 | checkPropTypes(innerPropTypes, nextProps, // Resolved props
|
|---|
| 19693 | 'prop', getComponentNameFromType(Component));
|
|---|
| 19694 | }
|
|---|
| 19695 | }
|
|---|
| 19696 | } // Push context providers early to prevent context stack mismatches.
|
|---|
| 19697 | // During mounting we don't know the child context yet as the instance doesn't exist.
|
|---|
| 19698 | // We will invalidate the child context in finishClassComponent() right after rendering.
|
|---|
| 19699 |
|
|---|
| 19700 |
|
|---|
| 19701 | var hasContext;
|
|---|
| 19702 |
|
|---|
| 19703 | if (isContextProvider(Component)) {
|
|---|
| 19704 | hasContext = true;
|
|---|
| 19705 | pushContextProvider(workInProgress);
|
|---|
| 19706 | } else {
|
|---|
| 19707 | hasContext = false;
|
|---|
| 19708 | }
|
|---|
| 19709 |
|
|---|
| 19710 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 19711 | var instance = workInProgress.stateNode;
|
|---|
| 19712 | var shouldUpdate;
|
|---|
| 19713 |
|
|---|
| 19714 | if (instance === null) {
|
|---|
| 19715 | resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress); // In the initial pass we might need to construct the instance.
|
|---|
| 19716 |
|
|---|
| 19717 | constructClassInstance(workInProgress, Component, nextProps);
|
|---|
| 19718 | mountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
|---|
| 19719 | shouldUpdate = true;
|
|---|
| 19720 | } else if (current === null) {
|
|---|
| 19721 | // In a resume, we'll already have an instance we can reuse.
|
|---|
| 19722 | shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
|---|
| 19723 | } else {
|
|---|
| 19724 | shouldUpdate = updateClassInstance(current, workInProgress, Component, nextProps, renderLanes);
|
|---|
| 19725 | }
|
|---|
| 19726 |
|
|---|
| 19727 | var nextUnitOfWork = finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes);
|
|---|
| 19728 |
|
|---|
| 19729 | {
|
|---|
| 19730 | var inst = workInProgress.stateNode;
|
|---|
| 19731 |
|
|---|
| 19732 | if (shouldUpdate && inst.props !== nextProps) {
|
|---|
| 19733 | if (!didWarnAboutReassigningProps) {
|
|---|
| 19734 | error('It looks like %s is reassigning its own `this.props` while rendering. ' + 'This is not supported and can lead to confusing bugs.', getComponentNameFromFiber(workInProgress) || 'a component');
|
|---|
| 19735 | }
|
|---|
| 19736 |
|
|---|
| 19737 | didWarnAboutReassigningProps = true;
|
|---|
| 19738 | }
|
|---|
| 19739 | }
|
|---|
| 19740 |
|
|---|
| 19741 | return nextUnitOfWork;
|
|---|
| 19742 | }
|
|---|
| 19743 |
|
|---|
| 19744 | function finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes) {
|
|---|
| 19745 | // Refs should update even if shouldComponentUpdate returns false
|
|---|
| 19746 | markRef(current, workInProgress);
|
|---|
| 19747 | var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags;
|
|---|
| 19748 |
|
|---|
| 19749 | if (!shouldUpdate && !didCaptureError) {
|
|---|
| 19750 | // Context providers should defer to sCU for rendering
|
|---|
| 19751 | if (hasContext) {
|
|---|
| 19752 | invalidateContextProvider(workInProgress, Component, false);
|
|---|
| 19753 | }
|
|---|
| 19754 |
|
|---|
| 19755 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19756 | }
|
|---|
| 19757 |
|
|---|
| 19758 | var instance = workInProgress.stateNode; // Rerender
|
|---|
| 19759 |
|
|---|
| 19760 | ReactCurrentOwner$1.current = workInProgress;
|
|---|
| 19761 | var nextChildren;
|
|---|
| 19762 |
|
|---|
| 19763 | if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') {
|
|---|
| 19764 | // If we captured an error, but getDerivedStateFromError is not defined,
|
|---|
| 19765 | // unmount all the children. componentDidCatch will schedule an update to
|
|---|
| 19766 | // re-render a fallback. This is temporary until we migrate everyone to
|
|---|
| 19767 | // the new API.
|
|---|
| 19768 | // TODO: Warn in a future release.
|
|---|
| 19769 | nextChildren = null;
|
|---|
| 19770 |
|
|---|
| 19771 | {
|
|---|
| 19772 | stopProfilerTimerIfRunning();
|
|---|
| 19773 | }
|
|---|
| 19774 | } else {
|
|---|
| 19775 | {
|
|---|
| 19776 | markComponentRenderStarted(workInProgress);
|
|---|
| 19777 | }
|
|---|
| 19778 |
|
|---|
| 19779 | {
|
|---|
| 19780 | setIsRendering(true);
|
|---|
| 19781 | nextChildren = instance.render();
|
|---|
| 19782 |
|
|---|
| 19783 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 19784 | setIsStrictModeForDevtools(true);
|
|---|
| 19785 |
|
|---|
| 19786 | try {
|
|---|
| 19787 | instance.render();
|
|---|
| 19788 | } finally {
|
|---|
| 19789 | setIsStrictModeForDevtools(false);
|
|---|
| 19790 | }
|
|---|
| 19791 | }
|
|---|
| 19792 |
|
|---|
| 19793 | setIsRendering(false);
|
|---|
| 19794 | }
|
|---|
| 19795 |
|
|---|
| 19796 | {
|
|---|
| 19797 | markComponentRenderStopped();
|
|---|
| 19798 | }
|
|---|
| 19799 | } // React DevTools reads this flag.
|
|---|
| 19800 |
|
|---|
| 19801 |
|
|---|
| 19802 | workInProgress.flags |= PerformedWork;
|
|---|
| 19803 |
|
|---|
| 19804 | if (current !== null && didCaptureError) {
|
|---|
| 19805 | // If we're recovering from an error, reconcile without reusing any of
|
|---|
| 19806 | // the existing children. Conceptually, the normal children and the children
|
|---|
| 19807 | // that are shown on error are two different sets, so we shouldn't reuse
|
|---|
| 19808 | // normal children even if their identities match.
|
|---|
| 19809 | forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19810 | } else {
|
|---|
| 19811 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19812 | } // Memoize state using the values we just used to render.
|
|---|
| 19813 | // TODO: Restructure so we never read values from the instance.
|
|---|
| 19814 |
|
|---|
| 19815 |
|
|---|
| 19816 | workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it.
|
|---|
| 19817 |
|
|---|
| 19818 | if (hasContext) {
|
|---|
| 19819 | invalidateContextProvider(workInProgress, Component, true);
|
|---|
| 19820 | }
|
|---|
| 19821 |
|
|---|
| 19822 | return workInProgress.child;
|
|---|
| 19823 | }
|
|---|
| 19824 |
|
|---|
| 19825 | function pushHostRootContext(workInProgress) {
|
|---|
| 19826 | var root = workInProgress.stateNode;
|
|---|
| 19827 |
|
|---|
| 19828 | if (root.pendingContext) {
|
|---|
| 19829 | pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context);
|
|---|
| 19830 | } else if (root.context) {
|
|---|
| 19831 | // Should always be set
|
|---|
| 19832 | pushTopLevelContextObject(workInProgress, root.context, false);
|
|---|
| 19833 | }
|
|---|
| 19834 |
|
|---|
| 19835 | pushHostContainer(workInProgress, root.containerInfo);
|
|---|
| 19836 | }
|
|---|
| 19837 |
|
|---|
| 19838 | function updateHostRoot(current, workInProgress, renderLanes) {
|
|---|
| 19839 | pushHostRootContext(workInProgress);
|
|---|
| 19840 |
|
|---|
| 19841 | if (current === null) {
|
|---|
| 19842 | throw new Error('Should have a current fiber. This is a bug in React.');
|
|---|
| 19843 | }
|
|---|
| 19844 |
|
|---|
| 19845 | var nextProps = workInProgress.pendingProps;
|
|---|
| 19846 | var prevState = workInProgress.memoizedState;
|
|---|
| 19847 | var prevChildren = prevState.element;
|
|---|
| 19848 | cloneUpdateQueue(current, workInProgress);
|
|---|
| 19849 | processUpdateQueue(workInProgress, nextProps, null, renderLanes);
|
|---|
| 19850 | var nextState = workInProgress.memoizedState;
|
|---|
| 19851 | var root = workInProgress.stateNode;
|
|---|
| 19852 | // being called "element".
|
|---|
| 19853 |
|
|---|
| 19854 |
|
|---|
| 19855 | var nextChildren = nextState.element;
|
|---|
| 19856 |
|
|---|
| 19857 | if ( prevState.isDehydrated) {
|
|---|
| 19858 | // This is a hydration root whose shell has not yet hydrated. We should
|
|---|
| 19859 | // attempt to hydrate.
|
|---|
| 19860 | // Flip isDehydrated to false to indicate that when this render
|
|---|
| 19861 | // finishes, the root will no longer be dehydrated.
|
|---|
| 19862 | var overrideState = {
|
|---|
| 19863 | element: nextChildren,
|
|---|
| 19864 | isDehydrated: false,
|
|---|
| 19865 | cache: nextState.cache,
|
|---|
| 19866 | pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries,
|
|---|
| 19867 | transitions: nextState.transitions
|
|---|
| 19868 | };
|
|---|
| 19869 | var updateQueue = workInProgress.updateQueue; // `baseState` can always be the last state because the root doesn't
|
|---|
| 19870 | // have reducer functions so it doesn't need rebasing.
|
|---|
| 19871 |
|
|---|
| 19872 | updateQueue.baseState = overrideState;
|
|---|
| 19873 | workInProgress.memoizedState = overrideState;
|
|---|
| 19874 |
|
|---|
| 19875 | if (workInProgress.flags & ForceClientRender) {
|
|---|
| 19876 | // Something errored during a previous attempt to hydrate the shell, so we
|
|---|
| 19877 | // forced a client render.
|
|---|
| 19878 | var recoverableError = createCapturedValueAtFiber(new Error('There was an error while hydrating. Because the error happened outside ' + 'of a Suspense boundary, the entire root will switch to ' + 'client rendering.'), workInProgress);
|
|---|
| 19879 | return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError);
|
|---|
| 19880 | } else if (nextChildren !== prevChildren) {
|
|---|
| 19881 | var _recoverableError = createCapturedValueAtFiber(new Error('This root received an early update, before anything was able ' + 'hydrate. Switched the entire root to client rendering.'), workInProgress);
|
|---|
| 19882 |
|
|---|
| 19883 | return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, _recoverableError);
|
|---|
| 19884 | } else {
|
|---|
| 19885 | // The outermost shell has not hydrated yet. Start hydrating.
|
|---|
| 19886 | enterHydrationState(workInProgress);
|
|---|
| 19887 |
|
|---|
| 19888 | var child = mountChildFibers(workInProgress, null, nextChildren, renderLanes);
|
|---|
| 19889 | workInProgress.child = child;
|
|---|
| 19890 | var node = child;
|
|---|
| 19891 |
|
|---|
| 19892 | while (node) {
|
|---|
| 19893 | // Mark each child as hydrating. This is a fast path to know whether this
|
|---|
| 19894 | // tree is part of a hydrating tree. This is used to determine if a child
|
|---|
| 19895 | // node has fully mounted yet, and for scheduling event replaying.
|
|---|
| 19896 | // Conceptually this is similar to Placement in that a new subtree is
|
|---|
| 19897 | // inserted into the React tree here. It just happens to not need DOM
|
|---|
| 19898 | // mutations because it already exists.
|
|---|
| 19899 | node.flags = node.flags & ~Placement | Hydrating;
|
|---|
| 19900 | node = node.sibling;
|
|---|
| 19901 | }
|
|---|
| 19902 | }
|
|---|
| 19903 | } else {
|
|---|
| 19904 | // Root is not dehydrated. Either this is a client-only root, or it
|
|---|
| 19905 | // already hydrated.
|
|---|
| 19906 | resetHydrationState();
|
|---|
| 19907 |
|
|---|
| 19908 | if (nextChildren === prevChildren) {
|
|---|
| 19909 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 19910 | }
|
|---|
| 19911 |
|
|---|
| 19912 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19913 | }
|
|---|
| 19914 |
|
|---|
| 19915 | return workInProgress.child;
|
|---|
| 19916 | }
|
|---|
| 19917 |
|
|---|
| 19918 | function mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError) {
|
|---|
| 19919 | // Revert to client rendering.
|
|---|
| 19920 | resetHydrationState();
|
|---|
| 19921 | queueHydrationError(recoverableError);
|
|---|
| 19922 | workInProgress.flags |= ForceClientRender;
|
|---|
| 19923 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19924 | return workInProgress.child;
|
|---|
| 19925 | }
|
|---|
| 19926 |
|
|---|
| 19927 | function updateHostComponent(current, workInProgress, renderLanes) {
|
|---|
| 19928 | pushHostContext(workInProgress);
|
|---|
| 19929 |
|
|---|
| 19930 | if (current === null) {
|
|---|
| 19931 | tryToClaimNextHydratableInstance(workInProgress);
|
|---|
| 19932 | }
|
|---|
| 19933 |
|
|---|
| 19934 | var type = workInProgress.type;
|
|---|
| 19935 | var nextProps = workInProgress.pendingProps;
|
|---|
| 19936 | var prevProps = current !== null ? current.memoizedProps : null;
|
|---|
| 19937 | var nextChildren = nextProps.children;
|
|---|
| 19938 | var isDirectTextChild = shouldSetTextContent(type, nextProps);
|
|---|
| 19939 |
|
|---|
| 19940 | if (isDirectTextChild) {
|
|---|
| 19941 | // We special case a direct text child of a host node. This is a common
|
|---|
| 19942 | // case. We won't handle it as a reified child. We will instead handle
|
|---|
| 19943 | // this in the host environment that also has access to this prop. That
|
|---|
| 19944 | // avoids allocating another HostText fiber and traversing it.
|
|---|
| 19945 | nextChildren = null;
|
|---|
| 19946 | } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) {
|
|---|
| 19947 | // If we're switching from a direct text child to a normal child, or to
|
|---|
| 19948 | // empty, we need to schedule the text content to be reset.
|
|---|
| 19949 | workInProgress.flags |= ContentReset;
|
|---|
| 19950 | }
|
|---|
| 19951 |
|
|---|
| 19952 | markRef(current, workInProgress);
|
|---|
| 19953 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 19954 | return workInProgress.child;
|
|---|
| 19955 | }
|
|---|
| 19956 |
|
|---|
| 19957 | function updateHostText(current, workInProgress) {
|
|---|
| 19958 | if (current === null) {
|
|---|
| 19959 | tryToClaimNextHydratableInstance(workInProgress);
|
|---|
| 19960 | } // Nothing to do here. This is terminal. We'll do the completion step
|
|---|
| 19961 | // immediately after.
|
|---|
| 19962 |
|
|---|
| 19963 |
|
|---|
| 19964 | return null;
|
|---|
| 19965 | }
|
|---|
| 19966 |
|
|---|
| 19967 | function mountLazyComponent(_current, workInProgress, elementType, renderLanes) {
|
|---|
| 19968 | resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
|
|---|
| 19969 | var props = workInProgress.pendingProps;
|
|---|
| 19970 | var lazyComponent = elementType;
|
|---|
| 19971 | var payload = lazyComponent._payload;
|
|---|
| 19972 | var init = lazyComponent._init;
|
|---|
| 19973 | var Component = init(payload); // Store the unwrapped component in the type.
|
|---|
| 19974 |
|
|---|
| 19975 | workInProgress.type = Component;
|
|---|
| 19976 | var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component);
|
|---|
| 19977 | var resolvedProps = resolveDefaultProps(Component, props);
|
|---|
| 19978 | var child;
|
|---|
| 19979 |
|
|---|
| 19980 | switch (resolvedTag) {
|
|---|
| 19981 | case FunctionComponent:
|
|---|
| 19982 | {
|
|---|
| 19983 | {
|
|---|
| 19984 | validateFunctionComponentInDev(workInProgress, Component);
|
|---|
| 19985 | workInProgress.type = Component = resolveFunctionForHotReloading(Component);
|
|---|
| 19986 | }
|
|---|
| 19987 |
|
|---|
| 19988 | child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderLanes);
|
|---|
| 19989 | return child;
|
|---|
| 19990 | }
|
|---|
| 19991 |
|
|---|
| 19992 | case ClassComponent:
|
|---|
| 19993 | {
|
|---|
| 19994 | {
|
|---|
| 19995 | workInProgress.type = Component = resolveClassForHotReloading(Component);
|
|---|
| 19996 | }
|
|---|
| 19997 |
|
|---|
| 19998 | child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderLanes);
|
|---|
| 19999 | return child;
|
|---|
| 20000 | }
|
|---|
| 20001 |
|
|---|
| 20002 | case ForwardRef:
|
|---|
| 20003 | {
|
|---|
| 20004 | {
|
|---|
| 20005 | workInProgress.type = Component = resolveForwardRefForHotReloading(Component);
|
|---|
| 20006 | }
|
|---|
| 20007 |
|
|---|
| 20008 | child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderLanes);
|
|---|
| 20009 | return child;
|
|---|
| 20010 | }
|
|---|
| 20011 |
|
|---|
| 20012 | case MemoComponent:
|
|---|
| 20013 | {
|
|---|
| 20014 | {
|
|---|
| 20015 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 20016 | var outerPropTypes = Component.propTypes;
|
|---|
| 20017 |
|
|---|
| 20018 | if (outerPropTypes) {
|
|---|
| 20019 | checkPropTypes(outerPropTypes, resolvedProps, // Resolved for outer only
|
|---|
| 20020 | 'prop', getComponentNameFromType(Component));
|
|---|
| 20021 | }
|
|---|
| 20022 | }
|
|---|
| 20023 | }
|
|---|
| 20024 |
|
|---|
| 20025 | child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too
|
|---|
| 20026 | renderLanes);
|
|---|
| 20027 | return child;
|
|---|
| 20028 | }
|
|---|
| 20029 | }
|
|---|
| 20030 |
|
|---|
| 20031 | var hint = '';
|
|---|
| 20032 |
|
|---|
| 20033 | {
|
|---|
| 20034 | if (Component !== null && typeof Component === 'object' && Component.$$typeof === REACT_LAZY_TYPE) {
|
|---|
| 20035 | hint = ' Did you wrap a component in React.lazy() more than once?';
|
|---|
| 20036 | }
|
|---|
| 20037 | } // This message intentionally doesn't mention ForwardRef or MemoComponent
|
|---|
| 20038 | // because the fact that it's a separate type of work is an
|
|---|
| 20039 | // implementation detail.
|
|---|
| 20040 |
|
|---|
| 20041 |
|
|---|
| 20042 | throw new Error("Element type is invalid. Received a promise that resolves to: " + Component + ". " + ("Lazy element type must resolve to a class or function." + hint));
|
|---|
| 20043 | }
|
|---|
| 20044 |
|
|---|
| 20045 | function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderLanes) {
|
|---|
| 20046 | resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); // Promote the fiber to a class and try rendering again.
|
|---|
| 20047 |
|
|---|
| 20048 | workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent`
|
|---|
| 20049 | // Push context providers early to prevent context stack mismatches.
|
|---|
| 20050 | // During mounting we don't know the child context yet as the instance doesn't exist.
|
|---|
| 20051 | // We will invalidate the child context in finishClassComponent() right after rendering.
|
|---|
| 20052 |
|
|---|
| 20053 | var hasContext;
|
|---|
| 20054 |
|
|---|
| 20055 | if (isContextProvider(Component)) {
|
|---|
| 20056 | hasContext = true;
|
|---|
| 20057 | pushContextProvider(workInProgress);
|
|---|
| 20058 | } else {
|
|---|
| 20059 | hasContext = false;
|
|---|
| 20060 | }
|
|---|
| 20061 |
|
|---|
| 20062 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 20063 | constructClassInstance(workInProgress, Component, nextProps);
|
|---|
| 20064 | mountClassInstance(workInProgress, Component, nextProps, renderLanes);
|
|---|
| 20065 | return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes);
|
|---|
| 20066 | }
|
|---|
| 20067 |
|
|---|
| 20068 | function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) {
|
|---|
| 20069 | resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress);
|
|---|
| 20070 | var props = workInProgress.pendingProps;
|
|---|
| 20071 | var context;
|
|---|
| 20072 |
|
|---|
| 20073 | {
|
|---|
| 20074 | var unmaskedContext = getUnmaskedContext(workInProgress, Component, false);
|
|---|
| 20075 | context = getMaskedContext(workInProgress, unmaskedContext);
|
|---|
| 20076 | }
|
|---|
| 20077 |
|
|---|
| 20078 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 20079 | var value;
|
|---|
| 20080 | var hasId;
|
|---|
| 20081 |
|
|---|
| 20082 | {
|
|---|
| 20083 | markComponentRenderStarted(workInProgress);
|
|---|
| 20084 | }
|
|---|
| 20085 |
|
|---|
| 20086 | {
|
|---|
| 20087 | if (Component.prototype && typeof Component.prototype.render === 'function') {
|
|---|
| 20088 | var componentName = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20089 |
|
|---|
| 20090 | if (!didWarnAboutBadClass[componentName]) {
|
|---|
| 20091 | error("The <%s /> component appears to have a render method, but doesn't extend React.Component. " + 'This is likely to cause errors. Change %s to extend React.Component instead.', componentName, componentName);
|
|---|
| 20092 |
|
|---|
| 20093 | didWarnAboutBadClass[componentName] = true;
|
|---|
| 20094 | }
|
|---|
| 20095 | }
|
|---|
| 20096 |
|
|---|
| 20097 | if (workInProgress.mode & StrictLegacyMode) {
|
|---|
| 20098 | ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null);
|
|---|
| 20099 | }
|
|---|
| 20100 |
|
|---|
| 20101 | setIsRendering(true);
|
|---|
| 20102 | ReactCurrentOwner$1.current = workInProgress;
|
|---|
| 20103 | value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes);
|
|---|
| 20104 | hasId = checkDidRenderIdHook();
|
|---|
| 20105 | setIsRendering(false);
|
|---|
| 20106 | }
|
|---|
| 20107 |
|
|---|
| 20108 | {
|
|---|
| 20109 | markComponentRenderStopped();
|
|---|
| 20110 | } // React DevTools reads this flag.
|
|---|
| 20111 |
|
|---|
| 20112 |
|
|---|
| 20113 | workInProgress.flags |= PerformedWork;
|
|---|
| 20114 |
|
|---|
| 20115 | {
|
|---|
| 20116 | // Support for module components is deprecated and is removed behind a flag.
|
|---|
| 20117 | // Whether or not it would crash later, we want to show a good message in DEV first.
|
|---|
| 20118 | if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
|
|---|
| 20119 | var _componentName = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20120 |
|
|---|
| 20121 | if (!didWarnAboutModulePatternComponent[_componentName]) {
|
|---|
| 20122 | error('The <%s /> component appears to be a function component that returns a class instance. ' + 'Change %s to a class that extends React.Component instead. ' + "If you can't use a class try assigning the prototype on the function as a workaround. " + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + 'cannot be called with `new` by React.', _componentName, _componentName, _componentName);
|
|---|
| 20123 |
|
|---|
| 20124 | didWarnAboutModulePatternComponent[_componentName] = true;
|
|---|
| 20125 | }
|
|---|
| 20126 | }
|
|---|
| 20127 | }
|
|---|
| 20128 |
|
|---|
| 20129 | if ( // Run these checks in production only if the flag is off.
|
|---|
| 20130 | // Eventually we'll delete this branch altogether.
|
|---|
| 20131 | typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) {
|
|---|
| 20132 | {
|
|---|
| 20133 | var _componentName2 = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20134 |
|
|---|
| 20135 | if (!didWarnAboutModulePatternComponent[_componentName2]) {
|
|---|
| 20136 | error('The <%s /> component appears to be a function component that returns a class instance. ' + 'Change %s to a class that extends React.Component instead. ' + "If you can't use a class try assigning the prototype on the function as a workaround. " + "`%s.prototype = React.Component.prototype`. Don't use an arrow function since it " + 'cannot be called with `new` by React.', _componentName2, _componentName2, _componentName2);
|
|---|
| 20137 |
|
|---|
| 20138 | didWarnAboutModulePatternComponent[_componentName2] = true;
|
|---|
| 20139 | }
|
|---|
| 20140 | } // Proceed under the assumption that this is a class instance
|
|---|
| 20141 |
|
|---|
| 20142 |
|
|---|
| 20143 | workInProgress.tag = ClassComponent; // Throw out any hooks that were used.
|
|---|
| 20144 |
|
|---|
| 20145 | workInProgress.memoizedState = null;
|
|---|
| 20146 | workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches.
|
|---|
| 20147 | // During mounting we don't know the child context yet as the instance doesn't exist.
|
|---|
| 20148 | // We will invalidate the child context in finishClassComponent() right after rendering.
|
|---|
| 20149 |
|
|---|
| 20150 | var hasContext = false;
|
|---|
| 20151 |
|
|---|
| 20152 | if (isContextProvider(Component)) {
|
|---|
| 20153 | hasContext = true;
|
|---|
| 20154 | pushContextProvider(workInProgress);
|
|---|
| 20155 | } else {
|
|---|
| 20156 | hasContext = false;
|
|---|
| 20157 | }
|
|---|
| 20158 |
|
|---|
| 20159 | workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null;
|
|---|
| 20160 | initializeUpdateQueue(workInProgress);
|
|---|
| 20161 | adoptClassInstance(workInProgress, value);
|
|---|
| 20162 | mountClassInstance(workInProgress, Component, props, renderLanes);
|
|---|
| 20163 | return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes);
|
|---|
| 20164 | } else {
|
|---|
| 20165 | // Proceed under the assumption that this is a function component
|
|---|
| 20166 | workInProgress.tag = FunctionComponent;
|
|---|
| 20167 |
|
|---|
| 20168 | {
|
|---|
| 20169 |
|
|---|
| 20170 | if ( workInProgress.mode & StrictLegacyMode) {
|
|---|
| 20171 | setIsStrictModeForDevtools(true);
|
|---|
| 20172 |
|
|---|
| 20173 | try {
|
|---|
| 20174 | value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes);
|
|---|
| 20175 | hasId = checkDidRenderIdHook();
|
|---|
| 20176 | } finally {
|
|---|
| 20177 | setIsStrictModeForDevtools(false);
|
|---|
| 20178 | }
|
|---|
| 20179 | }
|
|---|
| 20180 | }
|
|---|
| 20181 |
|
|---|
| 20182 | if (getIsHydrating() && hasId) {
|
|---|
| 20183 | pushMaterializedTreeId(workInProgress);
|
|---|
| 20184 | }
|
|---|
| 20185 |
|
|---|
| 20186 | reconcileChildren(null, workInProgress, value, renderLanes);
|
|---|
| 20187 |
|
|---|
| 20188 | {
|
|---|
| 20189 | validateFunctionComponentInDev(workInProgress, Component);
|
|---|
| 20190 | }
|
|---|
| 20191 |
|
|---|
| 20192 | return workInProgress.child;
|
|---|
| 20193 | }
|
|---|
| 20194 | }
|
|---|
| 20195 |
|
|---|
| 20196 | function validateFunctionComponentInDev(workInProgress, Component) {
|
|---|
| 20197 | {
|
|---|
| 20198 | if (Component) {
|
|---|
| 20199 | if (Component.childContextTypes) {
|
|---|
| 20200 | error('%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component');
|
|---|
| 20201 | }
|
|---|
| 20202 | }
|
|---|
| 20203 |
|
|---|
| 20204 | if (workInProgress.ref !== null) {
|
|---|
| 20205 | var info = '';
|
|---|
| 20206 | var ownerName = getCurrentFiberOwnerNameInDevOrNull();
|
|---|
| 20207 |
|
|---|
| 20208 | if (ownerName) {
|
|---|
| 20209 | info += '\n\nCheck the render method of `' + ownerName + '`.';
|
|---|
| 20210 | }
|
|---|
| 20211 |
|
|---|
| 20212 | var warningKey = ownerName || '';
|
|---|
| 20213 | var debugSource = workInProgress._debugSource;
|
|---|
| 20214 |
|
|---|
| 20215 | if (debugSource) {
|
|---|
| 20216 | warningKey = debugSource.fileName + ':' + debugSource.lineNumber;
|
|---|
| 20217 | }
|
|---|
| 20218 |
|
|---|
| 20219 | if (!didWarnAboutFunctionRefs[warningKey]) {
|
|---|
| 20220 | didWarnAboutFunctionRefs[warningKey] = true;
|
|---|
| 20221 |
|
|---|
| 20222 | error('Function components cannot be given refs. ' + 'Attempts to access this ref will fail. ' + 'Did you mean to use React.forwardRef()?%s', info);
|
|---|
| 20223 | }
|
|---|
| 20224 | }
|
|---|
| 20225 |
|
|---|
| 20226 | if ( Component.defaultProps !== undefined) {
|
|---|
| 20227 | var componentName = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20228 |
|
|---|
| 20229 | if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) {
|
|---|
| 20230 | error('%s: Support for defaultProps will be removed from function components ' + 'in a future major release. Use JavaScript default parameters instead.', componentName);
|
|---|
| 20231 |
|
|---|
| 20232 | didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true;
|
|---|
| 20233 | }
|
|---|
| 20234 | }
|
|---|
| 20235 |
|
|---|
| 20236 | if (typeof Component.getDerivedStateFromProps === 'function') {
|
|---|
| 20237 | var _componentName3 = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20238 |
|
|---|
| 20239 | if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) {
|
|---|
| 20240 | error('%s: Function components do not support getDerivedStateFromProps.', _componentName3);
|
|---|
| 20241 |
|
|---|
| 20242 | didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true;
|
|---|
| 20243 | }
|
|---|
| 20244 | }
|
|---|
| 20245 |
|
|---|
| 20246 | if (typeof Component.contextType === 'object' && Component.contextType !== null) {
|
|---|
| 20247 | var _componentName4 = getComponentNameFromType(Component) || 'Unknown';
|
|---|
| 20248 |
|
|---|
| 20249 | if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) {
|
|---|
| 20250 | error('%s: Function components do not support contextType.', _componentName4);
|
|---|
| 20251 |
|
|---|
| 20252 | didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true;
|
|---|
| 20253 | }
|
|---|
| 20254 | }
|
|---|
| 20255 | }
|
|---|
| 20256 | }
|
|---|
| 20257 |
|
|---|
| 20258 | var SUSPENDED_MARKER = {
|
|---|
| 20259 | dehydrated: null,
|
|---|
| 20260 | treeContext: null,
|
|---|
| 20261 | retryLane: NoLane
|
|---|
| 20262 | };
|
|---|
| 20263 |
|
|---|
| 20264 | function mountSuspenseOffscreenState(renderLanes) {
|
|---|
| 20265 | return {
|
|---|
| 20266 | baseLanes: renderLanes,
|
|---|
| 20267 | cachePool: getSuspendedCache(),
|
|---|
| 20268 | transitions: null
|
|---|
| 20269 | };
|
|---|
| 20270 | }
|
|---|
| 20271 |
|
|---|
| 20272 | function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) {
|
|---|
| 20273 | var cachePool = null;
|
|---|
| 20274 |
|
|---|
| 20275 | return {
|
|---|
| 20276 | baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes),
|
|---|
| 20277 | cachePool: cachePool,
|
|---|
| 20278 | transitions: prevOffscreenState.transitions
|
|---|
| 20279 | };
|
|---|
| 20280 | } // TODO: Probably should inline this back
|
|---|
| 20281 |
|
|---|
| 20282 |
|
|---|
| 20283 | function shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) {
|
|---|
| 20284 | // If we're already showing a fallback, there are cases where we need to
|
|---|
| 20285 | // remain on that fallback regardless of whether the content has resolved.
|
|---|
| 20286 | // For example, SuspenseList coordinates when nested content appears.
|
|---|
| 20287 | if (current !== null) {
|
|---|
| 20288 | var suspenseState = current.memoizedState;
|
|---|
| 20289 |
|
|---|
| 20290 | if (suspenseState === null) {
|
|---|
| 20291 | // Currently showing content. Don't hide it, even if ForceSuspenseFallback
|
|---|
| 20292 | // is true. More precise name might be "ForceRemainSuspenseFallback".
|
|---|
| 20293 | // Note: This is a factoring smell. Can't remain on a fallback if there's
|
|---|
| 20294 | // no fallback to remain on.
|
|---|
| 20295 | return false;
|
|---|
| 20296 | }
|
|---|
| 20297 | } // Not currently showing content. Consult the Suspense context.
|
|---|
| 20298 |
|
|---|
| 20299 |
|
|---|
| 20300 | return hasSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
|---|
| 20301 | }
|
|---|
| 20302 |
|
|---|
| 20303 | function getRemainingWorkInPrimaryTree(current, renderLanes) {
|
|---|
| 20304 | // TODO: Should not remove render lanes that were pinged during this render
|
|---|
| 20305 | return removeLanes(current.childLanes, renderLanes);
|
|---|
| 20306 | }
|
|---|
| 20307 |
|
|---|
| 20308 | function updateSuspenseComponent(current, workInProgress, renderLanes) {
|
|---|
| 20309 | var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend.
|
|---|
| 20310 |
|
|---|
| 20311 | {
|
|---|
| 20312 | if (shouldSuspend(workInProgress)) {
|
|---|
| 20313 | workInProgress.flags |= DidCapture;
|
|---|
| 20314 | }
|
|---|
| 20315 | }
|
|---|
| 20316 |
|
|---|
| 20317 | var suspenseContext = suspenseStackCursor.current;
|
|---|
| 20318 | var showFallback = false;
|
|---|
| 20319 | var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags;
|
|---|
| 20320 |
|
|---|
| 20321 | if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) {
|
|---|
| 20322 | // Something in this boundary's subtree already suspended. Switch to
|
|---|
| 20323 | // rendering the fallback children.
|
|---|
| 20324 | showFallback = true;
|
|---|
| 20325 | workInProgress.flags &= ~DidCapture;
|
|---|
| 20326 | } else {
|
|---|
| 20327 | // Attempting the main content
|
|---|
| 20328 | if (current === null || current.memoizedState !== null) {
|
|---|
| 20329 | // This is a new mount or this boundary is already showing a fallback state.
|
|---|
| 20330 | // Mark this subtree context as having at least one invisible parent that could
|
|---|
| 20331 | // handle the fallback state.
|
|---|
| 20332 | // Avoided boundaries are not considered since they cannot handle preferred fallback states.
|
|---|
| 20333 | {
|
|---|
| 20334 | suspenseContext = addSubtreeSuspenseContext(suspenseContext, InvisibleParentSuspenseContext);
|
|---|
| 20335 | }
|
|---|
| 20336 | }
|
|---|
| 20337 | }
|
|---|
| 20338 |
|
|---|
| 20339 | suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
|---|
| 20340 | pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense
|
|---|
| 20341 | // boundary's children. This involves some custom reconciliation logic. Two
|
|---|
| 20342 | // main reasons this is so complicated.
|
|---|
| 20343 | //
|
|---|
| 20344 | // First, Legacy Mode has different semantics for backwards compatibility. The
|
|---|
| 20345 | // primary tree will commit in an inconsistent state, so when we do the
|
|---|
| 20346 | // second pass to render the fallback, we do some exceedingly, uh, clever
|
|---|
| 20347 | // hacks to make that not totally break. Like transferring effects and
|
|---|
| 20348 | // deletions from hidden tree. In Concurrent Mode, it's much simpler,
|
|---|
| 20349 | // because we bailout on the primary tree completely and leave it in its old
|
|---|
| 20350 | // state, no effects. Same as what we do for Offscreen (except that
|
|---|
| 20351 | // Offscreen doesn't have the first render pass).
|
|---|
| 20352 | //
|
|---|
| 20353 | // Second is hydration. During hydration, the Suspense fiber has a slightly
|
|---|
| 20354 | // different layout, where the child points to a dehydrated fragment, which
|
|---|
| 20355 | // contains the DOM rendered by the server.
|
|---|
| 20356 | //
|
|---|
| 20357 | // Third, even if you set all that aside, Suspense is like error boundaries in
|
|---|
| 20358 | // that we first we try to render one tree, and if that fails, we render again
|
|---|
| 20359 | // and switch to a different tree. Like a try/catch block. So we have to track
|
|---|
| 20360 | // which branch we're currently rendering. Ideally we would model this using
|
|---|
| 20361 | // a stack.
|
|---|
| 20362 |
|
|---|
| 20363 | if (current === null) {
|
|---|
| 20364 | // Initial mount
|
|---|
| 20365 | // Special path for hydration
|
|---|
| 20366 | // If we're currently hydrating, try to hydrate this boundary.
|
|---|
| 20367 | tryToClaimNextHydratableInstance(workInProgress); // This could've been a dehydrated suspense component.
|
|---|
| 20368 |
|
|---|
| 20369 | var suspenseState = workInProgress.memoizedState;
|
|---|
| 20370 |
|
|---|
| 20371 | if (suspenseState !== null) {
|
|---|
| 20372 | var dehydrated = suspenseState.dehydrated;
|
|---|
| 20373 |
|
|---|
| 20374 | if (dehydrated !== null) {
|
|---|
| 20375 | return mountDehydratedSuspenseComponent(workInProgress, dehydrated);
|
|---|
| 20376 | }
|
|---|
| 20377 | }
|
|---|
| 20378 |
|
|---|
| 20379 | var nextPrimaryChildren = nextProps.children;
|
|---|
| 20380 | var nextFallbackChildren = nextProps.fallback;
|
|---|
| 20381 |
|
|---|
| 20382 | if (showFallback) {
|
|---|
| 20383 | var fallbackFragment = mountSuspenseFallbackChildren(workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes);
|
|---|
| 20384 | var primaryChildFragment = workInProgress.child;
|
|---|
| 20385 | primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes);
|
|---|
| 20386 | workInProgress.memoizedState = SUSPENDED_MARKER;
|
|---|
| 20387 |
|
|---|
| 20388 | return fallbackFragment;
|
|---|
| 20389 | } else {
|
|---|
| 20390 | return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren);
|
|---|
| 20391 | }
|
|---|
| 20392 | } else {
|
|---|
| 20393 | // This is an update.
|
|---|
| 20394 | // Special path for hydration
|
|---|
| 20395 | var prevState = current.memoizedState;
|
|---|
| 20396 |
|
|---|
| 20397 | if (prevState !== null) {
|
|---|
| 20398 | var _dehydrated = prevState.dehydrated;
|
|---|
| 20399 |
|
|---|
| 20400 | if (_dehydrated !== null) {
|
|---|
| 20401 | return updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, _dehydrated, prevState, renderLanes);
|
|---|
| 20402 | }
|
|---|
| 20403 | }
|
|---|
| 20404 |
|
|---|
| 20405 | if (showFallback) {
|
|---|
| 20406 | var _nextFallbackChildren = nextProps.fallback;
|
|---|
| 20407 | var _nextPrimaryChildren = nextProps.children;
|
|---|
| 20408 | var fallbackChildFragment = updateSuspenseFallbackChildren(current, workInProgress, _nextPrimaryChildren, _nextFallbackChildren, renderLanes);
|
|---|
| 20409 | var _primaryChildFragment2 = workInProgress.child;
|
|---|
| 20410 | var prevOffscreenState = current.child.memoizedState;
|
|---|
| 20411 | _primaryChildFragment2.memoizedState = prevOffscreenState === null ? mountSuspenseOffscreenState(renderLanes) : updateSuspenseOffscreenState(prevOffscreenState, renderLanes);
|
|---|
| 20412 |
|
|---|
| 20413 | _primaryChildFragment2.childLanes = getRemainingWorkInPrimaryTree(current, renderLanes);
|
|---|
| 20414 | workInProgress.memoizedState = SUSPENDED_MARKER;
|
|---|
| 20415 | return fallbackChildFragment;
|
|---|
| 20416 | } else {
|
|---|
| 20417 | var _nextPrimaryChildren2 = nextProps.children;
|
|---|
| 20418 |
|
|---|
| 20419 | var _primaryChildFragment3 = updateSuspensePrimaryChildren(current, workInProgress, _nextPrimaryChildren2, renderLanes);
|
|---|
| 20420 |
|
|---|
| 20421 | workInProgress.memoizedState = null;
|
|---|
| 20422 | return _primaryChildFragment3;
|
|---|
| 20423 | }
|
|---|
| 20424 | }
|
|---|
| 20425 | }
|
|---|
| 20426 |
|
|---|
| 20427 | function mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) {
|
|---|
| 20428 | var mode = workInProgress.mode;
|
|---|
| 20429 | var primaryChildProps = {
|
|---|
| 20430 | mode: 'visible',
|
|---|
| 20431 | children: primaryChildren
|
|---|
| 20432 | };
|
|---|
| 20433 | var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode);
|
|---|
| 20434 | primaryChildFragment.return = workInProgress;
|
|---|
| 20435 | workInProgress.child = primaryChildFragment;
|
|---|
| 20436 | return primaryChildFragment;
|
|---|
| 20437 | }
|
|---|
| 20438 |
|
|---|
| 20439 | function mountSuspenseFallbackChildren(workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
|---|
| 20440 | var mode = workInProgress.mode;
|
|---|
| 20441 | var progressedPrimaryFragment = workInProgress.child;
|
|---|
| 20442 | var primaryChildProps = {
|
|---|
| 20443 | mode: 'hidden',
|
|---|
| 20444 | children: primaryChildren
|
|---|
| 20445 | };
|
|---|
| 20446 | var primaryChildFragment;
|
|---|
| 20447 | var fallbackChildFragment;
|
|---|
| 20448 |
|
|---|
| 20449 | if ((mode & ConcurrentMode) === NoMode && progressedPrimaryFragment !== null) {
|
|---|
| 20450 | // In legacy mode, we commit the primary tree as if it successfully
|
|---|
| 20451 | // completed, even though it's in an inconsistent state.
|
|---|
| 20452 | primaryChildFragment = progressedPrimaryFragment;
|
|---|
| 20453 | primaryChildFragment.childLanes = NoLanes;
|
|---|
| 20454 | primaryChildFragment.pendingProps = primaryChildProps;
|
|---|
| 20455 |
|
|---|
| 20456 | if ( workInProgress.mode & ProfileMode) {
|
|---|
| 20457 | // Reset the durations from the first pass so they aren't included in the
|
|---|
| 20458 | // final amounts. This seems counterintuitive, since we're intentionally
|
|---|
| 20459 | // not measuring part of the render phase, but this makes it match what we
|
|---|
| 20460 | // do in Concurrent Mode.
|
|---|
| 20461 | primaryChildFragment.actualDuration = 0;
|
|---|
| 20462 | primaryChildFragment.actualStartTime = -1;
|
|---|
| 20463 | primaryChildFragment.selfBaseDuration = 0;
|
|---|
| 20464 | primaryChildFragment.treeBaseDuration = 0;
|
|---|
| 20465 | }
|
|---|
| 20466 |
|
|---|
| 20467 | fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null);
|
|---|
| 20468 | } else {
|
|---|
| 20469 | primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode);
|
|---|
| 20470 | fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null);
|
|---|
| 20471 | }
|
|---|
| 20472 |
|
|---|
| 20473 | primaryChildFragment.return = workInProgress;
|
|---|
| 20474 | fallbackChildFragment.return = workInProgress;
|
|---|
| 20475 | primaryChildFragment.sibling = fallbackChildFragment;
|
|---|
| 20476 | workInProgress.child = primaryChildFragment;
|
|---|
| 20477 | return fallbackChildFragment;
|
|---|
| 20478 | }
|
|---|
| 20479 |
|
|---|
| 20480 | function mountWorkInProgressOffscreenFiber(offscreenProps, mode, renderLanes) {
|
|---|
| 20481 | // The props argument to `createFiberFromOffscreen` is `any` typed, so we use
|
|---|
| 20482 | // this wrapper function to constrain it.
|
|---|
| 20483 | return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null);
|
|---|
| 20484 | }
|
|---|
| 20485 |
|
|---|
| 20486 | function updateWorkInProgressOffscreenFiber(current, offscreenProps) {
|
|---|
| 20487 | // The props argument to `createWorkInProgress` is `any` typed, so we use this
|
|---|
| 20488 | // wrapper function to constrain it.
|
|---|
| 20489 | return createWorkInProgress(current, offscreenProps);
|
|---|
| 20490 | }
|
|---|
| 20491 |
|
|---|
| 20492 | function updateSuspensePrimaryChildren(current, workInProgress, primaryChildren, renderLanes) {
|
|---|
| 20493 | var currentPrimaryChildFragment = current.child;
|
|---|
| 20494 | var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
|
|---|
| 20495 | var primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, {
|
|---|
| 20496 | mode: 'visible',
|
|---|
| 20497 | children: primaryChildren
|
|---|
| 20498 | });
|
|---|
| 20499 |
|
|---|
| 20500 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 20501 | primaryChildFragment.lanes = renderLanes;
|
|---|
| 20502 | }
|
|---|
| 20503 |
|
|---|
| 20504 | primaryChildFragment.return = workInProgress;
|
|---|
| 20505 | primaryChildFragment.sibling = null;
|
|---|
| 20506 |
|
|---|
| 20507 | if (currentFallbackChildFragment !== null) {
|
|---|
| 20508 | // Delete the fallback child fragment
|
|---|
| 20509 | var deletions = workInProgress.deletions;
|
|---|
| 20510 |
|
|---|
| 20511 | if (deletions === null) {
|
|---|
| 20512 | workInProgress.deletions = [currentFallbackChildFragment];
|
|---|
| 20513 | workInProgress.flags |= ChildDeletion;
|
|---|
| 20514 | } else {
|
|---|
| 20515 | deletions.push(currentFallbackChildFragment);
|
|---|
| 20516 | }
|
|---|
| 20517 | }
|
|---|
| 20518 |
|
|---|
| 20519 | workInProgress.child = primaryChildFragment;
|
|---|
| 20520 | return primaryChildFragment;
|
|---|
| 20521 | }
|
|---|
| 20522 |
|
|---|
| 20523 | function updateSuspenseFallbackChildren(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
|---|
| 20524 | var mode = workInProgress.mode;
|
|---|
| 20525 | var currentPrimaryChildFragment = current.child;
|
|---|
| 20526 | var currentFallbackChildFragment = currentPrimaryChildFragment.sibling;
|
|---|
| 20527 | var primaryChildProps = {
|
|---|
| 20528 | mode: 'hidden',
|
|---|
| 20529 | children: primaryChildren
|
|---|
| 20530 | };
|
|---|
| 20531 | var primaryChildFragment;
|
|---|
| 20532 |
|
|---|
| 20533 | if ( // In legacy mode, we commit the primary tree as if it successfully
|
|---|
| 20534 | // completed, even though it's in an inconsistent state.
|
|---|
| 20535 | (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was
|
|---|
| 20536 | // already cloned. In legacy mode, the only case where this isn't true is
|
|---|
| 20537 | // when DevTools forces us to display a fallback; we skip the first render
|
|---|
| 20538 | // pass entirely and go straight to rendering the fallback. (In Concurrent
|
|---|
| 20539 | // Mode, SuspenseList can also trigger this scenario, but this is a legacy-
|
|---|
| 20540 | // only codepath.)
|
|---|
| 20541 | workInProgress.child !== currentPrimaryChildFragment) {
|
|---|
| 20542 | var progressedPrimaryFragment = workInProgress.child;
|
|---|
| 20543 | primaryChildFragment = progressedPrimaryFragment;
|
|---|
| 20544 | primaryChildFragment.childLanes = NoLanes;
|
|---|
| 20545 | primaryChildFragment.pendingProps = primaryChildProps;
|
|---|
| 20546 |
|
|---|
| 20547 | if ( workInProgress.mode & ProfileMode) {
|
|---|
| 20548 | // Reset the durations from the first pass so they aren't included in the
|
|---|
| 20549 | // final amounts. This seems counterintuitive, since we're intentionally
|
|---|
| 20550 | // not measuring part of the render phase, but this makes it match what we
|
|---|
| 20551 | // do in Concurrent Mode.
|
|---|
| 20552 | primaryChildFragment.actualDuration = 0;
|
|---|
| 20553 | primaryChildFragment.actualStartTime = -1;
|
|---|
| 20554 | primaryChildFragment.selfBaseDuration = currentPrimaryChildFragment.selfBaseDuration;
|
|---|
| 20555 | primaryChildFragment.treeBaseDuration = currentPrimaryChildFragment.treeBaseDuration;
|
|---|
| 20556 | } // The fallback fiber was added as a deletion during the first pass.
|
|---|
| 20557 | // However, since we're going to remain on the fallback, we no longer want
|
|---|
| 20558 | // to delete it.
|
|---|
| 20559 |
|
|---|
| 20560 |
|
|---|
| 20561 | workInProgress.deletions = null;
|
|---|
| 20562 | } else {
|
|---|
| 20563 | primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, primaryChildProps); // Since we're reusing a current tree, we need to reuse the flags, too.
|
|---|
| 20564 | // (We don't do this in legacy mode, because in legacy mode we don't re-use
|
|---|
| 20565 | // the current tree; see previous branch.)
|
|---|
| 20566 |
|
|---|
| 20567 | primaryChildFragment.subtreeFlags = currentPrimaryChildFragment.subtreeFlags & StaticMask;
|
|---|
| 20568 | }
|
|---|
| 20569 |
|
|---|
| 20570 | var fallbackChildFragment;
|
|---|
| 20571 |
|
|---|
| 20572 | if (currentFallbackChildFragment !== null) {
|
|---|
| 20573 | fallbackChildFragment = createWorkInProgress(currentFallbackChildFragment, fallbackChildren);
|
|---|
| 20574 | } else {
|
|---|
| 20575 | fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null); // Needs a placement effect because the parent (the Suspense boundary) already
|
|---|
| 20576 | // mounted but this is a new fiber.
|
|---|
| 20577 |
|
|---|
| 20578 | fallbackChildFragment.flags |= Placement;
|
|---|
| 20579 | }
|
|---|
| 20580 |
|
|---|
| 20581 | fallbackChildFragment.return = workInProgress;
|
|---|
| 20582 | primaryChildFragment.return = workInProgress;
|
|---|
| 20583 | primaryChildFragment.sibling = fallbackChildFragment;
|
|---|
| 20584 | workInProgress.child = primaryChildFragment;
|
|---|
| 20585 | return fallbackChildFragment;
|
|---|
| 20586 | }
|
|---|
| 20587 |
|
|---|
| 20588 | function retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, recoverableError) {
|
|---|
| 20589 | // Falling back to client rendering. Because this has performance
|
|---|
| 20590 | // implications, it's considered a recoverable error, even though the user
|
|---|
| 20591 | // likely won't observe anything wrong with the UI.
|
|---|
| 20592 | //
|
|---|
| 20593 | // The error is passed in as an argument to enforce that every caller provide
|
|---|
| 20594 | // a custom message, or explicitly opt out (currently the only path that opts
|
|---|
| 20595 | // out is legacy mode; every concurrent path provides an error).
|
|---|
| 20596 | if (recoverableError !== null) {
|
|---|
| 20597 | queueHydrationError(recoverableError);
|
|---|
| 20598 | } // This will add the old fiber to the deletion list
|
|---|
| 20599 |
|
|---|
| 20600 |
|
|---|
| 20601 | reconcileChildFibers(workInProgress, current.child, null, renderLanes); // We're now not suspended nor dehydrated.
|
|---|
| 20602 |
|
|---|
| 20603 | var nextProps = workInProgress.pendingProps;
|
|---|
| 20604 | var primaryChildren = nextProps.children;
|
|---|
| 20605 | var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Needs a placement effect because the parent (the Suspense boundary) already
|
|---|
| 20606 | // mounted but this is a new fiber.
|
|---|
| 20607 |
|
|---|
| 20608 | primaryChildFragment.flags |= Placement;
|
|---|
| 20609 | workInProgress.memoizedState = null;
|
|---|
| 20610 | return primaryChildFragment;
|
|---|
| 20611 | }
|
|---|
| 20612 |
|
|---|
| 20613 | function mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) {
|
|---|
| 20614 | var fiberMode = workInProgress.mode;
|
|---|
| 20615 | var primaryChildProps = {
|
|---|
| 20616 | mode: 'visible',
|
|---|
| 20617 | children: primaryChildren
|
|---|
| 20618 | };
|
|---|
| 20619 | var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, fiberMode);
|
|---|
| 20620 | var fallbackChildFragment = createFiberFromFragment(fallbackChildren, fiberMode, renderLanes, null); // Needs a placement effect because the parent (the Suspense
|
|---|
| 20621 | // boundary) already mounted but this is a new fiber.
|
|---|
| 20622 |
|
|---|
| 20623 | fallbackChildFragment.flags |= Placement;
|
|---|
| 20624 | primaryChildFragment.return = workInProgress;
|
|---|
| 20625 | fallbackChildFragment.return = workInProgress;
|
|---|
| 20626 | primaryChildFragment.sibling = fallbackChildFragment;
|
|---|
| 20627 | workInProgress.child = primaryChildFragment;
|
|---|
| 20628 |
|
|---|
| 20629 | if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
|
|---|
| 20630 | // We will have dropped the effect list which contains the
|
|---|
| 20631 | // deletion. We need to reconcile to delete the current child.
|
|---|
| 20632 | reconcileChildFibers(workInProgress, current.child, null, renderLanes);
|
|---|
| 20633 | }
|
|---|
| 20634 |
|
|---|
| 20635 | return fallbackChildFragment;
|
|---|
| 20636 | }
|
|---|
| 20637 |
|
|---|
| 20638 | function mountDehydratedSuspenseComponent(workInProgress, suspenseInstance, renderLanes) {
|
|---|
| 20639 | // During the first pass, we'll bail out and not drill into the children.
|
|---|
| 20640 | // Instead, we'll leave the content in place and try to hydrate it later.
|
|---|
| 20641 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 20642 | {
|
|---|
| 20643 | error('Cannot hydrate Suspense in legacy mode. Switch from ' + 'ReactDOM.hydrate(element, container) to ' + 'ReactDOMClient.hydrateRoot(container, <App />)' + '.render(element) or remove the Suspense components from ' + 'the server rendered components.');
|
|---|
| 20644 | }
|
|---|
| 20645 |
|
|---|
| 20646 | workInProgress.lanes = laneToLanes(SyncLane);
|
|---|
| 20647 | } else if (isSuspenseInstanceFallback(suspenseInstance)) {
|
|---|
| 20648 | // This is a client-only boundary. Since we won't get any content from the server
|
|---|
| 20649 | // for this, we need to schedule that at a higher priority based on when it would
|
|---|
| 20650 | // have timed out. In theory we could render it in this pass but it would have the
|
|---|
| 20651 | // wrong priority associated with it and will prevent hydration of parent path.
|
|---|
| 20652 | // Instead, we'll leave work left on it to render it in a separate commit.
|
|---|
| 20653 | // TODO This time should be the time at which the server rendered response that is
|
|---|
| 20654 | // a parent to this boundary was displayed. However, since we currently don't have
|
|---|
| 20655 | // a protocol to transfer that time, we'll just estimate it by using the current
|
|---|
| 20656 | // time. This will mean that Suspense timeouts are slightly shifted to later than
|
|---|
| 20657 | // they should be.
|
|---|
| 20658 | // Schedule a normal pri update to render this content.
|
|---|
| 20659 | workInProgress.lanes = laneToLanes(DefaultHydrationLane);
|
|---|
| 20660 | } else {
|
|---|
| 20661 | // We'll continue hydrating the rest at offscreen priority since we'll already
|
|---|
| 20662 | // be showing the right content coming from the server, it is no rush.
|
|---|
| 20663 | workInProgress.lanes = laneToLanes(OffscreenLane);
|
|---|
| 20664 | }
|
|---|
| 20665 |
|
|---|
| 20666 | return null;
|
|---|
| 20667 | }
|
|---|
| 20668 |
|
|---|
| 20669 | function updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, suspenseInstance, suspenseState, renderLanes) {
|
|---|
| 20670 | if (!didSuspend) {
|
|---|
| 20671 | // This is the first render pass. Attempt to hydrate.
|
|---|
| 20672 | // We should never be hydrating at this point because it is the first pass,
|
|---|
| 20673 | // but after we've already committed once.
|
|---|
| 20674 | warnIfHydrating();
|
|---|
| 20675 |
|
|---|
| 20676 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 20677 | return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, // TODO: When we delete legacy mode, we should make this error argument
|
|---|
| 20678 | // required — every concurrent mode path that causes hydration to
|
|---|
| 20679 | // de-opt to client rendering should have an error message.
|
|---|
| 20680 | null);
|
|---|
| 20681 | }
|
|---|
| 20682 |
|
|---|
| 20683 | if (isSuspenseInstanceFallback(suspenseInstance)) {
|
|---|
| 20684 | // This boundary is in a permanent fallback state. In this case, we'll never
|
|---|
| 20685 | // get an update and we'll never be able to hydrate the final content. Let's just try the
|
|---|
| 20686 | // client side render instead.
|
|---|
| 20687 | var digest, message, stack;
|
|---|
| 20688 |
|
|---|
| 20689 | {
|
|---|
| 20690 | var _getSuspenseInstanceF = getSuspenseInstanceFallbackErrorDetails(suspenseInstance);
|
|---|
| 20691 |
|
|---|
| 20692 | digest = _getSuspenseInstanceF.digest;
|
|---|
| 20693 | message = _getSuspenseInstanceF.message;
|
|---|
| 20694 | stack = _getSuspenseInstanceF.stack;
|
|---|
| 20695 | }
|
|---|
| 20696 |
|
|---|
| 20697 | var error;
|
|---|
| 20698 |
|
|---|
| 20699 | if (message) {
|
|---|
| 20700 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 20701 | error = new Error(message);
|
|---|
| 20702 | } else {
|
|---|
| 20703 | error = new Error('The server could not finish this Suspense boundary, likely ' + 'due to an error during server rendering. Switched to ' + 'client rendering.');
|
|---|
| 20704 | }
|
|---|
| 20705 |
|
|---|
| 20706 | var capturedValue = createCapturedValue(error, digest, stack);
|
|---|
| 20707 | return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, capturedValue);
|
|---|
| 20708 | }
|
|---|
| 20709 | // any context has changed, we need to treat is as if the input might have changed.
|
|---|
| 20710 |
|
|---|
| 20711 |
|
|---|
| 20712 | var hasContextChanged = includesSomeLane(renderLanes, current.childLanes);
|
|---|
| 20713 |
|
|---|
| 20714 | if (didReceiveUpdate || hasContextChanged) {
|
|---|
| 20715 | // This boundary has changed since the first render. This means that we are now unable to
|
|---|
| 20716 | // hydrate it. We might still be able to hydrate it using a higher priority lane.
|
|---|
| 20717 | var root = getWorkInProgressRoot();
|
|---|
| 20718 |
|
|---|
| 20719 | if (root !== null) {
|
|---|
| 20720 | var attemptHydrationAtLane = getBumpedLaneForHydration(root, renderLanes);
|
|---|
| 20721 |
|
|---|
| 20722 | if (attemptHydrationAtLane !== NoLane && attemptHydrationAtLane !== suspenseState.retryLane) {
|
|---|
| 20723 | // Intentionally mutating since this render will get interrupted. This
|
|---|
| 20724 | // is one of the very rare times where we mutate the current tree
|
|---|
| 20725 | // during the render phase.
|
|---|
| 20726 | suspenseState.retryLane = attemptHydrationAtLane; // TODO: Ideally this would inherit the event time of the current render
|
|---|
| 20727 |
|
|---|
| 20728 | var eventTime = NoTimestamp;
|
|---|
| 20729 | enqueueConcurrentRenderForLane(current, attemptHydrationAtLane);
|
|---|
| 20730 | scheduleUpdateOnFiber(root, current, attemptHydrationAtLane, eventTime);
|
|---|
| 20731 | }
|
|---|
| 20732 | } // If we have scheduled higher pri work above, this will probably just abort the render
|
|---|
| 20733 | // since we now have higher priority work, but in case it doesn't, we need to prepare to
|
|---|
| 20734 | // render something, if we time out. Even if that requires us to delete everything and
|
|---|
| 20735 | // skip hydration.
|
|---|
| 20736 | // Delay having to do this as long as the suspense timeout allows us.
|
|---|
| 20737 |
|
|---|
| 20738 |
|
|---|
| 20739 | renderDidSuspendDelayIfPossible();
|
|---|
| 20740 |
|
|---|
| 20741 | var _capturedValue = createCapturedValue(new Error('This Suspense boundary received an update before it finished ' + 'hydrating. This caused the boundary to switch to client rendering. ' + 'The usual way to fix this is to wrap the original update ' + 'in startTransition.'));
|
|---|
| 20742 |
|
|---|
| 20743 | return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue);
|
|---|
| 20744 | } else if (isSuspenseInstancePending(suspenseInstance)) {
|
|---|
| 20745 | // This component is still pending more data from the server, so we can't hydrate its
|
|---|
| 20746 | // content. We treat it as if this component suspended itself. It might seem as if
|
|---|
| 20747 | // we could just try to render it client-side instead. However, this will perform a
|
|---|
| 20748 | // lot of unnecessary work and is unlikely to complete since it often will suspend
|
|---|
| 20749 | // on missing data anyway. Additionally, the server might be able to render more
|
|---|
| 20750 | // than we can on the client yet. In that case we'd end up with more fallback states
|
|---|
| 20751 | // on the client than if we just leave it alone. If the server times out or errors
|
|---|
| 20752 | // these should update this boundary to the permanent Fallback state instead.
|
|---|
| 20753 | // Mark it as having captured (i.e. suspended).
|
|---|
| 20754 | workInProgress.flags |= DidCapture; // Leave the child in place. I.e. the dehydrated fragment.
|
|---|
| 20755 |
|
|---|
| 20756 | workInProgress.child = current.child; // Register a callback to retry this boundary once the server has sent the result.
|
|---|
| 20757 |
|
|---|
| 20758 | var retry = retryDehydratedSuspenseBoundary.bind(null, current);
|
|---|
| 20759 | registerSuspenseInstanceRetry(suspenseInstance, retry);
|
|---|
| 20760 | return null;
|
|---|
| 20761 | } else {
|
|---|
| 20762 | // This is the first attempt.
|
|---|
| 20763 | reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress, suspenseInstance, suspenseState.treeContext);
|
|---|
| 20764 | var primaryChildren = nextProps.children;
|
|---|
| 20765 | var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Mark the children as hydrating. This is a fast path to know whether this
|
|---|
| 20766 | // tree is part of a hydrating tree. This is used to determine if a child
|
|---|
| 20767 | // node has fully mounted yet, and for scheduling event replaying.
|
|---|
| 20768 | // Conceptually this is similar to Placement in that a new subtree is
|
|---|
| 20769 | // inserted into the React tree here. It just happens to not need DOM
|
|---|
| 20770 | // mutations because it already exists.
|
|---|
| 20771 |
|
|---|
| 20772 | primaryChildFragment.flags |= Hydrating;
|
|---|
| 20773 | return primaryChildFragment;
|
|---|
| 20774 | }
|
|---|
| 20775 | } else {
|
|---|
| 20776 | // This is the second render pass. We already attempted to hydrated, but
|
|---|
| 20777 | // something either suspended or errored.
|
|---|
| 20778 | if (workInProgress.flags & ForceClientRender) {
|
|---|
| 20779 | // Something errored during hydration. Try again without hydrating.
|
|---|
| 20780 | workInProgress.flags &= ~ForceClientRender;
|
|---|
| 20781 |
|
|---|
| 20782 | var _capturedValue2 = createCapturedValue(new Error('There was an error while hydrating this Suspense boundary. ' + 'Switched to client rendering.'));
|
|---|
| 20783 |
|
|---|
| 20784 | return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue2);
|
|---|
| 20785 | } else if (workInProgress.memoizedState !== null) {
|
|---|
| 20786 | // Something suspended and we should still be in dehydrated mode.
|
|---|
| 20787 | // Leave the existing child in place.
|
|---|
| 20788 | workInProgress.child = current.child; // The dehydrated completion pass expects this flag to be there
|
|---|
| 20789 | // but the normal suspense pass doesn't.
|
|---|
| 20790 |
|
|---|
| 20791 | workInProgress.flags |= DidCapture;
|
|---|
| 20792 | return null;
|
|---|
| 20793 | } else {
|
|---|
| 20794 | // Suspended but we should no longer be in dehydrated mode.
|
|---|
| 20795 | // Therefore we now have to render the fallback.
|
|---|
| 20796 | var nextPrimaryChildren = nextProps.children;
|
|---|
| 20797 | var nextFallbackChildren = nextProps.fallback;
|
|---|
| 20798 | var fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes);
|
|---|
| 20799 | var _primaryChildFragment4 = workInProgress.child;
|
|---|
| 20800 | _primaryChildFragment4.memoizedState = mountSuspenseOffscreenState(renderLanes);
|
|---|
| 20801 | workInProgress.memoizedState = SUSPENDED_MARKER;
|
|---|
| 20802 | return fallbackChildFragment;
|
|---|
| 20803 | }
|
|---|
| 20804 | }
|
|---|
| 20805 | }
|
|---|
| 20806 |
|
|---|
| 20807 | function scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) {
|
|---|
| 20808 | fiber.lanes = mergeLanes(fiber.lanes, renderLanes);
|
|---|
| 20809 | var alternate = fiber.alternate;
|
|---|
| 20810 |
|
|---|
| 20811 | if (alternate !== null) {
|
|---|
| 20812 | alternate.lanes = mergeLanes(alternate.lanes, renderLanes);
|
|---|
| 20813 | }
|
|---|
| 20814 |
|
|---|
| 20815 | scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot);
|
|---|
| 20816 | }
|
|---|
| 20817 |
|
|---|
| 20818 | function propagateSuspenseContextChange(workInProgress, firstChild, renderLanes) {
|
|---|
| 20819 | // Mark any Suspense boundaries with fallbacks as having work to do.
|
|---|
| 20820 | // If they were previously forced into fallbacks, they may now be able
|
|---|
| 20821 | // to unblock.
|
|---|
| 20822 | var node = firstChild;
|
|---|
| 20823 |
|
|---|
| 20824 | while (node !== null) {
|
|---|
| 20825 | if (node.tag === SuspenseComponent) {
|
|---|
| 20826 | var state = node.memoizedState;
|
|---|
| 20827 |
|
|---|
| 20828 | if (state !== null) {
|
|---|
| 20829 | scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
|
|---|
| 20830 | }
|
|---|
| 20831 | } else if (node.tag === SuspenseListComponent) {
|
|---|
| 20832 | // If the tail is hidden there might not be an Suspense boundaries
|
|---|
| 20833 | // to schedule work on. In this case we have to schedule it on the
|
|---|
| 20834 | // list itself.
|
|---|
| 20835 | // We don't have to traverse to the children of the list since
|
|---|
| 20836 | // the list will propagate the change when it rerenders.
|
|---|
| 20837 | scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress);
|
|---|
| 20838 | } else if (node.child !== null) {
|
|---|
| 20839 | node.child.return = node;
|
|---|
| 20840 | node = node.child;
|
|---|
| 20841 | continue;
|
|---|
| 20842 | }
|
|---|
| 20843 |
|
|---|
| 20844 | if (node === workInProgress) {
|
|---|
| 20845 | return;
|
|---|
| 20846 | }
|
|---|
| 20847 |
|
|---|
| 20848 | while (node.sibling === null) {
|
|---|
| 20849 | if (node.return === null || node.return === workInProgress) {
|
|---|
| 20850 | return;
|
|---|
| 20851 | }
|
|---|
| 20852 |
|
|---|
| 20853 | node = node.return;
|
|---|
| 20854 | }
|
|---|
| 20855 |
|
|---|
| 20856 | node.sibling.return = node.return;
|
|---|
| 20857 | node = node.sibling;
|
|---|
| 20858 | }
|
|---|
| 20859 | }
|
|---|
| 20860 |
|
|---|
| 20861 | function findLastContentRow(firstChild) {
|
|---|
| 20862 | // This is going to find the last row among these children that is already
|
|---|
| 20863 | // showing content on the screen, as opposed to being in fallback state or
|
|---|
| 20864 | // new. If a row has multiple Suspense boundaries, any of them being in the
|
|---|
| 20865 | // fallback state, counts as the whole row being in a fallback state.
|
|---|
| 20866 | // Note that the "rows" will be workInProgress, but any nested children
|
|---|
| 20867 | // will still be current since we haven't rendered them yet. The mounted
|
|---|
| 20868 | // order may not be the same as the new order. We use the new order.
|
|---|
| 20869 | var row = firstChild;
|
|---|
| 20870 | var lastContentRow = null;
|
|---|
| 20871 |
|
|---|
| 20872 | while (row !== null) {
|
|---|
| 20873 | var currentRow = row.alternate; // New rows can't be content rows.
|
|---|
| 20874 |
|
|---|
| 20875 | if (currentRow !== null && findFirstSuspended(currentRow) === null) {
|
|---|
| 20876 | lastContentRow = row;
|
|---|
| 20877 | }
|
|---|
| 20878 |
|
|---|
| 20879 | row = row.sibling;
|
|---|
| 20880 | }
|
|---|
| 20881 |
|
|---|
| 20882 | return lastContentRow;
|
|---|
| 20883 | }
|
|---|
| 20884 |
|
|---|
| 20885 | function validateRevealOrder(revealOrder) {
|
|---|
| 20886 | {
|
|---|
| 20887 | if (revealOrder !== undefined && revealOrder !== 'forwards' && revealOrder !== 'backwards' && revealOrder !== 'together' && !didWarnAboutRevealOrder[revealOrder]) {
|
|---|
| 20888 | didWarnAboutRevealOrder[revealOrder] = true;
|
|---|
| 20889 |
|
|---|
| 20890 | if (typeof revealOrder === 'string') {
|
|---|
| 20891 | switch (revealOrder.toLowerCase()) {
|
|---|
| 20892 | case 'together':
|
|---|
| 20893 | case 'forwards':
|
|---|
| 20894 | case 'backwards':
|
|---|
| 20895 | {
|
|---|
| 20896 | error('"%s" is not a valid value for revealOrder on <SuspenseList />. ' + 'Use lowercase "%s" instead.', revealOrder, revealOrder.toLowerCase());
|
|---|
| 20897 |
|
|---|
| 20898 | break;
|
|---|
| 20899 | }
|
|---|
| 20900 |
|
|---|
| 20901 | case 'forward':
|
|---|
| 20902 | case 'backward':
|
|---|
| 20903 | {
|
|---|
| 20904 | error('"%s" is not a valid value for revealOrder on <SuspenseList />. ' + 'React uses the -s suffix in the spelling. Use "%ss" instead.', revealOrder, revealOrder.toLowerCase());
|
|---|
| 20905 |
|
|---|
| 20906 | break;
|
|---|
| 20907 | }
|
|---|
| 20908 |
|
|---|
| 20909 | default:
|
|---|
| 20910 | error('"%s" is not a supported revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder);
|
|---|
| 20911 |
|
|---|
| 20912 | break;
|
|---|
| 20913 | }
|
|---|
| 20914 | } else {
|
|---|
| 20915 | error('%s is not a supported value for revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder);
|
|---|
| 20916 | }
|
|---|
| 20917 | }
|
|---|
| 20918 | }
|
|---|
| 20919 | }
|
|---|
| 20920 |
|
|---|
| 20921 | function validateTailOptions(tailMode, revealOrder) {
|
|---|
| 20922 | {
|
|---|
| 20923 | if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) {
|
|---|
| 20924 | if (tailMode !== 'collapsed' && tailMode !== 'hidden') {
|
|---|
| 20925 | didWarnAboutTailOptions[tailMode] = true;
|
|---|
| 20926 |
|
|---|
| 20927 | error('"%s" is not a supported value for tail on <SuspenseList />. ' + 'Did you mean "collapsed" or "hidden"?', tailMode);
|
|---|
| 20928 | } else if (revealOrder !== 'forwards' && revealOrder !== 'backwards') {
|
|---|
| 20929 | didWarnAboutTailOptions[tailMode] = true;
|
|---|
| 20930 |
|
|---|
| 20931 | error('<SuspenseList tail="%s" /> is only valid if revealOrder is ' + '"forwards" or "backwards". ' + 'Did you mean to specify revealOrder="forwards"?', tailMode);
|
|---|
| 20932 | }
|
|---|
| 20933 | }
|
|---|
| 20934 | }
|
|---|
| 20935 | }
|
|---|
| 20936 |
|
|---|
| 20937 | function validateSuspenseListNestedChild(childSlot, index) {
|
|---|
| 20938 | {
|
|---|
| 20939 | var isAnArray = isArray(childSlot);
|
|---|
| 20940 | var isIterable = !isAnArray && typeof getIteratorFn(childSlot) === 'function';
|
|---|
| 20941 |
|
|---|
| 20942 | if (isAnArray || isIterable) {
|
|---|
| 20943 | var type = isAnArray ? 'array' : 'iterable';
|
|---|
| 20944 |
|
|---|
| 20945 | error('A nested %s was passed to row #%s in <SuspenseList />. Wrap it in ' + 'an additional SuspenseList to configure its revealOrder: ' + '<SuspenseList revealOrder=...> ... ' + '<SuspenseList revealOrder=...>{%s}</SuspenseList> ... ' + '</SuspenseList>', type, index, type);
|
|---|
| 20946 |
|
|---|
| 20947 | return false;
|
|---|
| 20948 | }
|
|---|
| 20949 | }
|
|---|
| 20950 |
|
|---|
| 20951 | return true;
|
|---|
| 20952 | }
|
|---|
| 20953 |
|
|---|
| 20954 | function validateSuspenseListChildren(children, revealOrder) {
|
|---|
| 20955 | {
|
|---|
| 20956 | if ((revealOrder === 'forwards' || revealOrder === 'backwards') && children !== undefined && children !== null && children !== false) {
|
|---|
| 20957 | if (isArray(children)) {
|
|---|
| 20958 | for (var i = 0; i < children.length; i++) {
|
|---|
| 20959 | if (!validateSuspenseListNestedChild(children[i], i)) {
|
|---|
| 20960 | return;
|
|---|
| 20961 | }
|
|---|
| 20962 | }
|
|---|
| 20963 | } else {
|
|---|
| 20964 | var iteratorFn = getIteratorFn(children);
|
|---|
| 20965 |
|
|---|
| 20966 | if (typeof iteratorFn === 'function') {
|
|---|
| 20967 | var childrenIterator = iteratorFn.call(children);
|
|---|
| 20968 |
|
|---|
| 20969 | if (childrenIterator) {
|
|---|
| 20970 | var step = childrenIterator.next();
|
|---|
| 20971 | var _i = 0;
|
|---|
| 20972 |
|
|---|
| 20973 | for (; !step.done; step = childrenIterator.next()) {
|
|---|
| 20974 | if (!validateSuspenseListNestedChild(step.value, _i)) {
|
|---|
| 20975 | return;
|
|---|
| 20976 | }
|
|---|
| 20977 |
|
|---|
| 20978 | _i++;
|
|---|
| 20979 | }
|
|---|
| 20980 | }
|
|---|
| 20981 | } else {
|
|---|
| 20982 | error('A single row was passed to a <SuspenseList revealOrder="%s" />. ' + 'This is not useful since it needs multiple rows. ' + 'Did you mean to pass multiple children or an array?', revealOrder);
|
|---|
| 20983 | }
|
|---|
| 20984 | }
|
|---|
| 20985 | }
|
|---|
| 20986 | }
|
|---|
| 20987 | }
|
|---|
| 20988 |
|
|---|
| 20989 | function initSuspenseListRenderState(workInProgress, isBackwards, tail, lastContentRow, tailMode) {
|
|---|
| 20990 | var renderState = workInProgress.memoizedState;
|
|---|
| 20991 |
|
|---|
| 20992 | if (renderState === null) {
|
|---|
| 20993 | workInProgress.memoizedState = {
|
|---|
| 20994 | isBackwards: isBackwards,
|
|---|
| 20995 | rendering: null,
|
|---|
| 20996 | renderingStartTime: 0,
|
|---|
| 20997 | last: lastContentRow,
|
|---|
| 20998 | tail: tail,
|
|---|
| 20999 | tailMode: tailMode
|
|---|
| 21000 | };
|
|---|
| 21001 | } else {
|
|---|
| 21002 | // We can reuse the existing object from previous renders.
|
|---|
| 21003 | renderState.isBackwards = isBackwards;
|
|---|
| 21004 | renderState.rendering = null;
|
|---|
| 21005 | renderState.renderingStartTime = 0;
|
|---|
| 21006 | renderState.last = lastContentRow;
|
|---|
| 21007 | renderState.tail = tail;
|
|---|
| 21008 | renderState.tailMode = tailMode;
|
|---|
| 21009 | }
|
|---|
| 21010 | } // This can end up rendering this component multiple passes.
|
|---|
| 21011 | // The first pass splits the children fibers into two sets. A head and tail.
|
|---|
| 21012 | // We first render the head. If anything is in fallback state, we do another
|
|---|
| 21013 | // pass through beginWork to rerender all children (including the tail) with
|
|---|
| 21014 | // the force suspend context. If the first render didn't have anything in
|
|---|
| 21015 | // in fallback state. Then we render each row in the tail one-by-one.
|
|---|
| 21016 | // That happens in the completeWork phase without going back to beginWork.
|
|---|
| 21017 |
|
|---|
| 21018 |
|
|---|
| 21019 | function updateSuspenseListComponent(current, workInProgress, renderLanes) {
|
|---|
| 21020 | var nextProps = workInProgress.pendingProps;
|
|---|
| 21021 | var revealOrder = nextProps.revealOrder;
|
|---|
| 21022 | var tailMode = nextProps.tail;
|
|---|
| 21023 | var newChildren = nextProps.children;
|
|---|
| 21024 | validateRevealOrder(revealOrder);
|
|---|
| 21025 | validateTailOptions(tailMode, revealOrder);
|
|---|
| 21026 | validateSuspenseListChildren(newChildren, revealOrder);
|
|---|
| 21027 | reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
|---|
| 21028 | var suspenseContext = suspenseStackCursor.current;
|
|---|
| 21029 | var shouldForceFallback = hasSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
|---|
| 21030 |
|
|---|
| 21031 | if (shouldForceFallback) {
|
|---|
| 21032 | suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
|---|
| 21033 | workInProgress.flags |= DidCapture;
|
|---|
| 21034 | } else {
|
|---|
| 21035 | var didSuspendBefore = current !== null && (current.flags & DidCapture) !== NoFlags;
|
|---|
| 21036 |
|
|---|
| 21037 | if (didSuspendBefore) {
|
|---|
| 21038 | // If we previously forced a fallback, we need to schedule work
|
|---|
| 21039 | // on any nested boundaries to let them know to try to render
|
|---|
| 21040 | // again. This is the same as context updating.
|
|---|
| 21041 | propagateSuspenseContextChange(workInProgress, workInProgress.child, renderLanes);
|
|---|
| 21042 | }
|
|---|
| 21043 |
|
|---|
| 21044 | suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
|---|
| 21045 | }
|
|---|
| 21046 |
|
|---|
| 21047 | pushSuspenseContext(workInProgress, suspenseContext);
|
|---|
| 21048 |
|
|---|
| 21049 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 21050 | // In legacy mode, SuspenseList doesn't work so we just
|
|---|
| 21051 | // use make it a noop by treating it as the default revealOrder.
|
|---|
| 21052 | workInProgress.memoizedState = null;
|
|---|
| 21053 | } else {
|
|---|
| 21054 | switch (revealOrder) {
|
|---|
| 21055 | case 'forwards':
|
|---|
| 21056 | {
|
|---|
| 21057 | var lastContentRow = findLastContentRow(workInProgress.child);
|
|---|
| 21058 | var tail;
|
|---|
| 21059 |
|
|---|
| 21060 | if (lastContentRow === null) {
|
|---|
| 21061 | // The whole list is part of the tail.
|
|---|
| 21062 | // TODO: We could fast path by just rendering the tail now.
|
|---|
| 21063 | tail = workInProgress.child;
|
|---|
| 21064 | workInProgress.child = null;
|
|---|
| 21065 | } else {
|
|---|
| 21066 | // Disconnect the tail rows after the content row.
|
|---|
| 21067 | // We're going to render them separately later.
|
|---|
| 21068 | tail = lastContentRow.sibling;
|
|---|
| 21069 | lastContentRow.sibling = null;
|
|---|
| 21070 | }
|
|---|
| 21071 |
|
|---|
| 21072 | initSuspenseListRenderState(workInProgress, false, // isBackwards
|
|---|
| 21073 | tail, lastContentRow, tailMode);
|
|---|
| 21074 | break;
|
|---|
| 21075 | }
|
|---|
| 21076 |
|
|---|
| 21077 | case 'backwards':
|
|---|
| 21078 | {
|
|---|
| 21079 | // We're going to find the first row that has existing content.
|
|---|
| 21080 | // At the same time we're going to reverse the list of everything
|
|---|
| 21081 | // we pass in the meantime. That's going to be our tail in reverse
|
|---|
| 21082 | // order.
|
|---|
| 21083 | var _tail = null;
|
|---|
| 21084 | var row = workInProgress.child;
|
|---|
| 21085 | workInProgress.child = null;
|
|---|
| 21086 |
|
|---|
| 21087 | while (row !== null) {
|
|---|
| 21088 | var currentRow = row.alternate; // New rows can't be content rows.
|
|---|
| 21089 |
|
|---|
| 21090 | if (currentRow !== null && findFirstSuspended(currentRow) === null) {
|
|---|
| 21091 | // This is the beginning of the main content.
|
|---|
| 21092 | workInProgress.child = row;
|
|---|
| 21093 | break;
|
|---|
| 21094 | }
|
|---|
| 21095 |
|
|---|
| 21096 | var nextRow = row.sibling;
|
|---|
| 21097 | row.sibling = _tail;
|
|---|
| 21098 | _tail = row;
|
|---|
| 21099 | row = nextRow;
|
|---|
| 21100 | } // TODO: If workInProgress.child is null, we can continue on the tail immediately.
|
|---|
| 21101 |
|
|---|
| 21102 |
|
|---|
| 21103 | initSuspenseListRenderState(workInProgress, true, // isBackwards
|
|---|
| 21104 | _tail, null, // last
|
|---|
| 21105 | tailMode);
|
|---|
| 21106 | break;
|
|---|
| 21107 | }
|
|---|
| 21108 |
|
|---|
| 21109 | case 'together':
|
|---|
| 21110 | {
|
|---|
| 21111 | initSuspenseListRenderState(workInProgress, false, // isBackwards
|
|---|
| 21112 | null, // tail
|
|---|
| 21113 | null, // last
|
|---|
| 21114 | undefined);
|
|---|
| 21115 | break;
|
|---|
| 21116 | }
|
|---|
| 21117 |
|
|---|
| 21118 | default:
|
|---|
| 21119 | {
|
|---|
| 21120 | // The default reveal order is the same as not having
|
|---|
| 21121 | // a boundary.
|
|---|
| 21122 | workInProgress.memoizedState = null;
|
|---|
| 21123 | }
|
|---|
| 21124 | }
|
|---|
| 21125 | }
|
|---|
| 21126 |
|
|---|
| 21127 | return workInProgress.child;
|
|---|
| 21128 | }
|
|---|
| 21129 |
|
|---|
| 21130 | function updatePortalComponent(current, workInProgress, renderLanes) {
|
|---|
| 21131 | pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
|---|
| 21132 | var nextChildren = workInProgress.pendingProps;
|
|---|
| 21133 |
|
|---|
| 21134 | if (current === null) {
|
|---|
| 21135 | // Portals are special because we don't append the children during mount
|
|---|
| 21136 | // but at commit. Therefore we need to track insertions which the normal
|
|---|
| 21137 | // flow doesn't do during mount. This doesn't happen at the root because
|
|---|
| 21138 | // the root always starts with a "current" with a null child.
|
|---|
| 21139 | // TODO: Consider unifying this with how the root works.
|
|---|
| 21140 | workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes);
|
|---|
| 21141 | } else {
|
|---|
| 21142 | reconcileChildren(current, workInProgress, nextChildren, renderLanes);
|
|---|
| 21143 | }
|
|---|
| 21144 |
|
|---|
| 21145 | return workInProgress.child;
|
|---|
| 21146 | }
|
|---|
| 21147 |
|
|---|
| 21148 | var hasWarnedAboutUsingNoValuePropOnContextProvider = false;
|
|---|
| 21149 |
|
|---|
| 21150 | function updateContextProvider(current, workInProgress, renderLanes) {
|
|---|
| 21151 | var providerType = workInProgress.type;
|
|---|
| 21152 | var context = providerType._context;
|
|---|
| 21153 | var newProps = workInProgress.pendingProps;
|
|---|
| 21154 | var oldProps = workInProgress.memoizedProps;
|
|---|
| 21155 | var newValue = newProps.value;
|
|---|
| 21156 |
|
|---|
| 21157 | {
|
|---|
| 21158 | if (!('value' in newProps)) {
|
|---|
| 21159 | if (!hasWarnedAboutUsingNoValuePropOnContextProvider) {
|
|---|
| 21160 | hasWarnedAboutUsingNoValuePropOnContextProvider = true;
|
|---|
| 21161 |
|
|---|
| 21162 | error('The `value` prop is required for the `<Context.Provider>`. Did you misspell it or forget to pass it?');
|
|---|
| 21163 | }
|
|---|
| 21164 | }
|
|---|
| 21165 |
|
|---|
| 21166 | var providerPropTypes = workInProgress.type.propTypes;
|
|---|
| 21167 |
|
|---|
| 21168 | if (providerPropTypes) {
|
|---|
| 21169 | checkPropTypes(providerPropTypes, newProps, 'prop', 'Context.Provider');
|
|---|
| 21170 | }
|
|---|
| 21171 | }
|
|---|
| 21172 |
|
|---|
| 21173 | pushProvider(workInProgress, context, newValue);
|
|---|
| 21174 |
|
|---|
| 21175 | {
|
|---|
| 21176 | if (oldProps !== null) {
|
|---|
| 21177 | var oldValue = oldProps.value;
|
|---|
| 21178 |
|
|---|
| 21179 | if (objectIs(oldValue, newValue)) {
|
|---|
| 21180 | // No change. Bailout early if children are the same.
|
|---|
| 21181 | if (oldProps.children === newProps.children && !hasContextChanged()) {
|
|---|
| 21182 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 21183 | }
|
|---|
| 21184 | } else {
|
|---|
| 21185 | // The context value changed. Search for matching consumers and schedule
|
|---|
| 21186 | // them to update.
|
|---|
| 21187 | propagateContextChange(workInProgress, context, renderLanes);
|
|---|
| 21188 | }
|
|---|
| 21189 | }
|
|---|
| 21190 | }
|
|---|
| 21191 |
|
|---|
| 21192 | var newChildren = newProps.children;
|
|---|
| 21193 | reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
|---|
| 21194 | return workInProgress.child;
|
|---|
| 21195 | }
|
|---|
| 21196 |
|
|---|
| 21197 | var hasWarnedAboutUsingContextAsConsumer = false;
|
|---|
| 21198 |
|
|---|
| 21199 | function updateContextConsumer(current, workInProgress, renderLanes) {
|
|---|
| 21200 | var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In
|
|---|
| 21201 | // DEV mode, we create a separate object for Context.Consumer that acts
|
|---|
| 21202 | // like a proxy to Context. This proxy object adds unnecessary code in PROD
|
|---|
| 21203 | // so we use the old behaviour (Context.Consumer references Context) to
|
|---|
| 21204 | // reduce size and overhead. The separate object references context via
|
|---|
| 21205 | // a property called "_context", which also gives us the ability to check
|
|---|
| 21206 | // in DEV mode if this property exists or not and warn if it does not.
|
|---|
| 21207 |
|
|---|
| 21208 | {
|
|---|
| 21209 | if (context._context === undefined) {
|
|---|
| 21210 | // This may be because it's a Context (rather than a Consumer).
|
|---|
| 21211 | // Or it may be because it's older React where they're the same thing.
|
|---|
| 21212 | // We only want to warn if we're sure it's a new React.
|
|---|
| 21213 | if (context !== context.Consumer) {
|
|---|
| 21214 | if (!hasWarnedAboutUsingContextAsConsumer) {
|
|---|
| 21215 | hasWarnedAboutUsingContextAsConsumer = true;
|
|---|
| 21216 |
|
|---|
| 21217 | error('Rendering <Context> directly is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?');
|
|---|
| 21218 | }
|
|---|
| 21219 | }
|
|---|
| 21220 | } else {
|
|---|
| 21221 | context = context._context;
|
|---|
| 21222 | }
|
|---|
| 21223 | }
|
|---|
| 21224 |
|
|---|
| 21225 | var newProps = workInProgress.pendingProps;
|
|---|
| 21226 | var render = newProps.children;
|
|---|
| 21227 |
|
|---|
| 21228 | {
|
|---|
| 21229 | if (typeof render !== 'function') {
|
|---|
| 21230 | error('A context consumer was rendered with multiple children, or a child ' + "that isn't a function. A context consumer expects a single child " + 'that is a function. If you did pass a function, make sure there ' + 'is no trailing or leading whitespace around it.');
|
|---|
| 21231 | }
|
|---|
| 21232 | }
|
|---|
| 21233 |
|
|---|
| 21234 | prepareToReadContext(workInProgress, renderLanes);
|
|---|
| 21235 | var newValue = readContext(context);
|
|---|
| 21236 |
|
|---|
| 21237 | {
|
|---|
| 21238 | markComponentRenderStarted(workInProgress);
|
|---|
| 21239 | }
|
|---|
| 21240 |
|
|---|
| 21241 | var newChildren;
|
|---|
| 21242 |
|
|---|
| 21243 | {
|
|---|
| 21244 | ReactCurrentOwner$1.current = workInProgress;
|
|---|
| 21245 | setIsRendering(true);
|
|---|
| 21246 | newChildren = render(newValue);
|
|---|
| 21247 | setIsRendering(false);
|
|---|
| 21248 | }
|
|---|
| 21249 |
|
|---|
| 21250 | {
|
|---|
| 21251 | markComponentRenderStopped();
|
|---|
| 21252 | } // React DevTools reads this flag.
|
|---|
| 21253 |
|
|---|
| 21254 |
|
|---|
| 21255 | workInProgress.flags |= PerformedWork;
|
|---|
| 21256 | reconcileChildren(current, workInProgress, newChildren, renderLanes);
|
|---|
| 21257 | return workInProgress.child;
|
|---|
| 21258 | }
|
|---|
| 21259 |
|
|---|
| 21260 | function markWorkInProgressReceivedUpdate() {
|
|---|
| 21261 | didReceiveUpdate = true;
|
|---|
| 21262 | }
|
|---|
| 21263 |
|
|---|
| 21264 | function resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress) {
|
|---|
| 21265 | if ((workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 21266 | if (current !== null) {
|
|---|
| 21267 | // A lazy component only mounts if it suspended inside a non-
|
|---|
| 21268 | // concurrent tree, in an inconsistent state. We want to treat it like
|
|---|
| 21269 | // a new mount, even though an empty version of it already committed.
|
|---|
| 21270 | // Disconnect the alternate pointers.
|
|---|
| 21271 | current.alternate = null;
|
|---|
| 21272 | workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect
|
|---|
| 21273 |
|
|---|
| 21274 | workInProgress.flags |= Placement;
|
|---|
| 21275 | }
|
|---|
| 21276 | }
|
|---|
| 21277 | }
|
|---|
| 21278 |
|
|---|
| 21279 | function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) {
|
|---|
| 21280 | if (current !== null) {
|
|---|
| 21281 | // Reuse previous dependencies
|
|---|
| 21282 | workInProgress.dependencies = current.dependencies;
|
|---|
| 21283 | }
|
|---|
| 21284 |
|
|---|
| 21285 | {
|
|---|
| 21286 | // Don't update "base" render times for bailouts.
|
|---|
| 21287 | stopProfilerTimerIfRunning();
|
|---|
| 21288 | }
|
|---|
| 21289 |
|
|---|
| 21290 | markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work.
|
|---|
| 21291 |
|
|---|
| 21292 | if (!includesSomeLane(renderLanes, workInProgress.childLanes)) {
|
|---|
| 21293 | // The children don't have any work either. We can skip them.
|
|---|
| 21294 | // TODO: Once we add back resuming, we should check if the children are
|
|---|
| 21295 | // a work-in-progress set. If so, we need to transfer their effects.
|
|---|
| 21296 | {
|
|---|
| 21297 | return null;
|
|---|
| 21298 | }
|
|---|
| 21299 | } // This fiber doesn't have work, but its subtree does. Clone the child
|
|---|
| 21300 | // fibers and continue.
|
|---|
| 21301 |
|
|---|
| 21302 |
|
|---|
| 21303 | cloneChildFibers(current, workInProgress);
|
|---|
| 21304 | return workInProgress.child;
|
|---|
| 21305 | }
|
|---|
| 21306 |
|
|---|
| 21307 | function remountFiber(current, oldWorkInProgress, newWorkInProgress) {
|
|---|
| 21308 | {
|
|---|
| 21309 | var returnFiber = oldWorkInProgress.return;
|
|---|
| 21310 |
|
|---|
| 21311 | if (returnFiber === null) {
|
|---|
| 21312 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 21313 | throw new Error('Cannot swap the root fiber.');
|
|---|
| 21314 | } // Disconnect from the old current.
|
|---|
| 21315 | // It will get deleted.
|
|---|
| 21316 |
|
|---|
| 21317 |
|
|---|
| 21318 | current.alternate = null;
|
|---|
| 21319 | oldWorkInProgress.alternate = null; // Connect to the new tree.
|
|---|
| 21320 |
|
|---|
| 21321 | newWorkInProgress.index = oldWorkInProgress.index;
|
|---|
| 21322 | newWorkInProgress.sibling = oldWorkInProgress.sibling;
|
|---|
| 21323 | newWorkInProgress.return = oldWorkInProgress.return;
|
|---|
| 21324 | newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it.
|
|---|
| 21325 |
|
|---|
| 21326 | if (oldWorkInProgress === returnFiber.child) {
|
|---|
| 21327 | returnFiber.child = newWorkInProgress;
|
|---|
| 21328 | } else {
|
|---|
| 21329 | var prevSibling = returnFiber.child;
|
|---|
| 21330 |
|
|---|
| 21331 | if (prevSibling === null) {
|
|---|
| 21332 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 21333 | throw new Error('Expected parent to have a child.');
|
|---|
| 21334 | }
|
|---|
| 21335 |
|
|---|
| 21336 | while (prevSibling.sibling !== oldWorkInProgress) {
|
|---|
| 21337 | prevSibling = prevSibling.sibling;
|
|---|
| 21338 |
|
|---|
| 21339 | if (prevSibling === null) {
|
|---|
| 21340 | // eslint-disable-next-line react-internal/prod-error-codes
|
|---|
| 21341 | throw new Error('Expected to find the previous sibling.');
|
|---|
| 21342 | }
|
|---|
| 21343 | }
|
|---|
| 21344 |
|
|---|
| 21345 | prevSibling.sibling = newWorkInProgress;
|
|---|
| 21346 | } // Delete the old fiber and place the new one.
|
|---|
| 21347 | // Since the old fiber is disconnected, we have to schedule it manually.
|
|---|
| 21348 |
|
|---|
| 21349 |
|
|---|
| 21350 | var deletions = returnFiber.deletions;
|
|---|
| 21351 |
|
|---|
| 21352 | if (deletions === null) {
|
|---|
| 21353 | returnFiber.deletions = [current];
|
|---|
| 21354 | returnFiber.flags |= ChildDeletion;
|
|---|
| 21355 | } else {
|
|---|
| 21356 | deletions.push(current);
|
|---|
| 21357 | }
|
|---|
| 21358 |
|
|---|
| 21359 | newWorkInProgress.flags |= Placement; // Restart work from the new fiber.
|
|---|
| 21360 |
|
|---|
| 21361 | return newWorkInProgress;
|
|---|
| 21362 | }
|
|---|
| 21363 | }
|
|---|
| 21364 |
|
|---|
| 21365 | function checkScheduledUpdateOrContext(current, renderLanes) {
|
|---|
| 21366 | // Before performing an early bailout, we must check if there are pending
|
|---|
| 21367 | // updates or context.
|
|---|
| 21368 | var updateLanes = current.lanes;
|
|---|
| 21369 |
|
|---|
| 21370 | if (includesSomeLane(updateLanes, renderLanes)) {
|
|---|
| 21371 | return true;
|
|---|
| 21372 | } // No pending update, but because context is propagated lazily, we need
|
|---|
| 21373 |
|
|---|
| 21374 | return false;
|
|---|
| 21375 | }
|
|---|
| 21376 |
|
|---|
| 21377 | function attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes) {
|
|---|
| 21378 | // This fiber does not have any pending work. Bailout without entering
|
|---|
| 21379 | // the begin phase. There's still some bookkeeping we that needs to be done
|
|---|
| 21380 | // in this optimized path, mostly pushing stuff onto the stack.
|
|---|
| 21381 | switch (workInProgress.tag) {
|
|---|
| 21382 | case HostRoot:
|
|---|
| 21383 | pushHostRootContext(workInProgress);
|
|---|
| 21384 | var root = workInProgress.stateNode;
|
|---|
| 21385 |
|
|---|
| 21386 | resetHydrationState();
|
|---|
| 21387 | break;
|
|---|
| 21388 |
|
|---|
| 21389 | case HostComponent:
|
|---|
| 21390 | pushHostContext(workInProgress);
|
|---|
| 21391 | break;
|
|---|
| 21392 |
|
|---|
| 21393 | case ClassComponent:
|
|---|
| 21394 | {
|
|---|
| 21395 | var Component = workInProgress.type;
|
|---|
| 21396 |
|
|---|
| 21397 | if (isContextProvider(Component)) {
|
|---|
| 21398 | pushContextProvider(workInProgress);
|
|---|
| 21399 | }
|
|---|
| 21400 |
|
|---|
| 21401 | break;
|
|---|
| 21402 | }
|
|---|
| 21403 |
|
|---|
| 21404 | case HostPortal:
|
|---|
| 21405 | pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
|
|---|
| 21406 | break;
|
|---|
| 21407 |
|
|---|
| 21408 | case ContextProvider:
|
|---|
| 21409 | {
|
|---|
| 21410 | var newValue = workInProgress.memoizedProps.value;
|
|---|
| 21411 | var context = workInProgress.type._context;
|
|---|
| 21412 | pushProvider(workInProgress, context, newValue);
|
|---|
| 21413 | break;
|
|---|
| 21414 | }
|
|---|
| 21415 |
|
|---|
| 21416 | case Profiler:
|
|---|
| 21417 | {
|
|---|
| 21418 | // Profiler should only call onRender when one of its descendants actually rendered.
|
|---|
| 21419 | var hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
|
|---|
| 21420 |
|
|---|
| 21421 | if (hasChildWork) {
|
|---|
| 21422 | workInProgress.flags |= Update;
|
|---|
| 21423 | }
|
|---|
| 21424 |
|
|---|
| 21425 | {
|
|---|
| 21426 | // Reset effect durations for the next eventual effect phase.
|
|---|
| 21427 | // These are reset during render to allow the DevTools commit hook a chance to read them,
|
|---|
| 21428 | var stateNode = workInProgress.stateNode;
|
|---|
| 21429 | stateNode.effectDuration = 0;
|
|---|
| 21430 | stateNode.passiveEffectDuration = 0;
|
|---|
| 21431 | }
|
|---|
| 21432 | }
|
|---|
| 21433 |
|
|---|
| 21434 | break;
|
|---|
| 21435 |
|
|---|
| 21436 | case SuspenseComponent:
|
|---|
| 21437 | {
|
|---|
| 21438 | var state = workInProgress.memoizedState;
|
|---|
| 21439 |
|
|---|
| 21440 | if (state !== null) {
|
|---|
| 21441 | if (state.dehydrated !== null) {
|
|---|
| 21442 | pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // We know that this component will suspend again because if it has
|
|---|
| 21443 | // been unsuspended it has committed as a resolved Suspense component.
|
|---|
| 21444 | // If it needs to be retried, it should have work scheduled on it.
|
|---|
| 21445 |
|
|---|
| 21446 | workInProgress.flags |= DidCapture; // We should never render the children of a dehydrated boundary until we
|
|---|
| 21447 | // upgrade it. We return null instead of bailoutOnAlreadyFinishedWork.
|
|---|
| 21448 |
|
|---|
| 21449 | return null;
|
|---|
| 21450 | } // If this boundary is currently timed out, we need to decide
|
|---|
| 21451 | // whether to retry the primary children, or to skip over it and
|
|---|
| 21452 | // go straight to the fallback. Check the priority of the primary
|
|---|
| 21453 | // child fragment.
|
|---|
| 21454 |
|
|---|
| 21455 |
|
|---|
| 21456 | var primaryChildFragment = workInProgress.child;
|
|---|
| 21457 | var primaryChildLanes = primaryChildFragment.childLanes;
|
|---|
| 21458 |
|
|---|
| 21459 | if (includesSomeLane(renderLanes, primaryChildLanes)) {
|
|---|
| 21460 | // The primary children have pending work. Use the normal path
|
|---|
| 21461 | // to attempt to render the primary children again.
|
|---|
| 21462 | return updateSuspenseComponent(current, workInProgress, renderLanes);
|
|---|
| 21463 | } else {
|
|---|
| 21464 | // The primary child fragment does not have pending work marked
|
|---|
| 21465 | // on it
|
|---|
| 21466 | pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // The primary children do not have pending work with sufficient
|
|---|
| 21467 | // priority. Bailout.
|
|---|
| 21468 |
|
|---|
| 21469 | var child = bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 21470 |
|
|---|
| 21471 | if (child !== null) {
|
|---|
| 21472 | // The fallback children have pending work. Skip over the
|
|---|
| 21473 | // primary children and work on the fallback.
|
|---|
| 21474 | return child.sibling;
|
|---|
| 21475 | } else {
|
|---|
| 21476 | // Note: We can return `null` here because we already checked
|
|---|
| 21477 | // whether there were nested context consumers, via the call to
|
|---|
| 21478 | // `bailoutOnAlreadyFinishedWork` above.
|
|---|
| 21479 | return null;
|
|---|
| 21480 | }
|
|---|
| 21481 | }
|
|---|
| 21482 | } else {
|
|---|
| 21483 | pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current));
|
|---|
| 21484 | }
|
|---|
| 21485 |
|
|---|
| 21486 | break;
|
|---|
| 21487 | }
|
|---|
| 21488 |
|
|---|
| 21489 | case SuspenseListComponent:
|
|---|
| 21490 | {
|
|---|
| 21491 | var didSuspendBefore = (current.flags & DidCapture) !== NoFlags;
|
|---|
| 21492 |
|
|---|
| 21493 | var _hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes);
|
|---|
| 21494 |
|
|---|
| 21495 | if (didSuspendBefore) {
|
|---|
| 21496 | if (_hasChildWork) {
|
|---|
| 21497 | // If something was in fallback state last time, and we have all the
|
|---|
| 21498 | // same children then we're still in progressive loading state.
|
|---|
| 21499 | // Something might get unblocked by state updates or retries in the
|
|---|
| 21500 | // tree which will affect the tail. So we need to use the normal
|
|---|
| 21501 | // path to compute the correct tail.
|
|---|
| 21502 | return updateSuspenseListComponent(current, workInProgress, renderLanes);
|
|---|
| 21503 | } // If none of the children had any work, that means that none of
|
|---|
| 21504 | // them got retried so they'll still be blocked in the same way
|
|---|
| 21505 | // as before. We can fast bail out.
|
|---|
| 21506 |
|
|---|
| 21507 |
|
|---|
| 21508 | workInProgress.flags |= DidCapture;
|
|---|
| 21509 | } // If nothing suspended before and we're rendering the same children,
|
|---|
| 21510 | // then the tail doesn't matter. Anything new that suspends will work
|
|---|
| 21511 | // in the "together" mode, so we can continue from the state we had.
|
|---|
| 21512 |
|
|---|
| 21513 |
|
|---|
| 21514 | var renderState = workInProgress.memoizedState;
|
|---|
| 21515 |
|
|---|
| 21516 | if (renderState !== null) {
|
|---|
| 21517 | // Reset to the "together" mode in case we've started a different
|
|---|
| 21518 | // update in the past but didn't complete it.
|
|---|
| 21519 | renderState.rendering = null;
|
|---|
| 21520 | renderState.tail = null;
|
|---|
| 21521 | renderState.lastEffect = null;
|
|---|
| 21522 | }
|
|---|
| 21523 |
|
|---|
| 21524 | pushSuspenseContext(workInProgress, suspenseStackCursor.current);
|
|---|
| 21525 |
|
|---|
| 21526 | if (_hasChildWork) {
|
|---|
| 21527 | break;
|
|---|
| 21528 | } else {
|
|---|
| 21529 | // If none of the children had any work, that means that none of
|
|---|
| 21530 | // them got retried so they'll still be blocked in the same way
|
|---|
| 21531 | // as before. We can fast bail out.
|
|---|
| 21532 | return null;
|
|---|
| 21533 | }
|
|---|
| 21534 | }
|
|---|
| 21535 |
|
|---|
| 21536 | case OffscreenComponent:
|
|---|
| 21537 | case LegacyHiddenComponent:
|
|---|
| 21538 | {
|
|---|
| 21539 | // Need to check if the tree still needs to be deferred. This is
|
|---|
| 21540 | // almost identical to the logic used in the normal update path,
|
|---|
| 21541 | // so we'll just enter that. The only difference is we'll bail out
|
|---|
| 21542 | // at the next level instead of this one, because the child props
|
|---|
| 21543 | // have not changed. Which is fine.
|
|---|
| 21544 | // TODO: Probably should refactor `beginWork` to split the bailout
|
|---|
| 21545 | // path from the normal path. I'm tempted to do a labeled break here
|
|---|
| 21546 | // but I won't :)
|
|---|
| 21547 | workInProgress.lanes = NoLanes;
|
|---|
| 21548 | return updateOffscreenComponent(current, workInProgress, renderLanes);
|
|---|
| 21549 | }
|
|---|
| 21550 | }
|
|---|
| 21551 |
|
|---|
| 21552 | return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
|
|---|
| 21553 | }
|
|---|
| 21554 |
|
|---|
| 21555 | function beginWork(current, workInProgress, renderLanes) {
|
|---|
| 21556 | {
|
|---|
| 21557 | if (workInProgress._debugNeedsRemount && current !== null) {
|
|---|
| 21558 | // This will restart the begin phase with a new fiber.
|
|---|
| 21559 | return remountFiber(current, workInProgress, createFiberFromTypeAndProps(workInProgress.type, workInProgress.key, workInProgress.pendingProps, workInProgress._debugOwner || null, workInProgress.mode, workInProgress.lanes));
|
|---|
| 21560 | }
|
|---|
| 21561 | }
|
|---|
| 21562 |
|
|---|
| 21563 | if (current !== null) {
|
|---|
| 21564 | var oldProps = current.memoizedProps;
|
|---|
| 21565 | var newProps = workInProgress.pendingProps;
|
|---|
| 21566 |
|
|---|
| 21567 | if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload:
|
|---|
| 21568 | workInProgress.type !== current.type )) {
|
|---|
| 21569 | // If props or context changed, mark the fiber as having performed work.
|
|---|
| 21570 | // This may be unset if the props are determined to be equal later (memo).
|
|---|
| 21571 | didReceiveUpdate = true;
|
|---|
| 21572 | } else {
|
|---|
| 21573 | // Neither props nor legacy context changes. Check if there's a pending
|
|---|
| 21574 | // update or context change.
|
|---|
| 21575 | var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);
|
|---|
| 21576 |
|
|---|
| 21577 | if (!hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there
|
|---|
| 21578 | // may not be work scheduled on `current`, so we check for this flag.
|
|---|
| 21579 | (workInProgress.flags & DidCapture) === NoFlags) {
|
|---|
| 21580 | // No pending updates or context. Bail out now.
|
|---|
| 21581 | didReceiveUpdate = false;
|
|---|
| 21582 | return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);
|
|---|
| 21583 | }
|
|---|
| 21584 |
|
|---|
| 21585 | if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {
|
|---|
| 21586 | // This is a special case that only exists for legacy mode.
|
|---|
| 21587 | // See https://github.com/facebook/react/pull/19216.
|
|---|
| 21588 | didReceiveUpdate = true;
|
|---|
| 21589 | } else {
|
|---|
| 21590 | // An update was scheduled on this fiber, but there are no new props
|
|---|
| 21591 | // nor legacy context. Set this to false. If an update queue or context
|
|---|
| 21592 | // consumer produces a changed value, it will set this to true. Otherwise,
|
|---|
| 21593 | // the component will assume the children have not changed and bail out.
|
|---|
| 21594 | didReceiveUpdate = false;
|
|---|
| 21595 | }
|
|---|
| 21596 | }
|
|---|
| 21597 | } else {
|
|---|
| 21598 | didReceiveUpdate = false;
|
|---|
| 21599 |
|
|---|
| 21600 | if (getIsHydrating() && isForkedChild(workInProgress)) {
|
|---|
| 21601 | // Check if this child belongs to a list of muliple children in
|
|---|
| 21602 | // its parent.
|
|---|
| 21603 | //
|
|---|
| 21604 | // In a true multi-threaded implementation, we would render children on
|
|---|
| 21605 | // parallel threads. This would represent the beginning of a new render
|
|---|
| 21606 | // thread for this subtree.
|
|---|
| 21607 | //
|
|---|
| 21608 | // We only use this for id generation during hydration, which is why the
|
|---|
| 21609 | // logic is located in this special branch.
|
|---|
| 21610 | var slotIndex = workInProgress.index;
|
|---|
| 21611 | var numberOfForks = getForksAtLevel();
|
|---|
| 21612 | pushTreeId(workInProgress, numberOfForks, slotIndex);
|
|---|
| 21613 | }
|
|---|
| 21614 | } // Before entering the begin phase, clear pending update priority.
|
|---|
| 21615 | // TODO: This assumes that we're about to evaluate the component and process
|
|---|
| 21616 | // the update queue. However, there's an exception: SimpleMemoComponent
|
|---|
| 21617 | // sometimes bails out later in the begin phase. This indicates that we should
|
|---|
| 21618 | // move this assignment out of the common path and into each branch.
|
|---|
| 21619 |
|
|---|
| 21620 |
|
|---|
| 21621 | workInProgress.lanes = NoLanes;
|
|---|
| 21622 |
|
|---|
| 21623 | switch (workInProgress.tag) {
|
|---|
| 21624 | case IndeterminateComponent:
|
|---|
| 21625 | {
|
|---|
| 21626 | return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes);
|
|---|
| 21627 | }
|
|---|
| 21628 |
|
|---|
| 21629 | case LazyComponent:
|
|---|
| 21630 | {
|
|---|
| 21631 | var elementType = workInProgress.elementType;
|
|---|
| 21632 | return mountLazyComponent(current, workInProgress, elementType, renderLanes);
|
|---|
| 21633 | }
|
|---|
| 21634 |
|
|---|
| 21635 | case FunctionComponent:
|
|---|
| 21636 | {
|
|---|
| 21637 | var Component = workInProgress.type;
|
|---|
| 21638 | var unresolvedProps = workInProgress.pendingProps;
|
|---|
| 21639 | var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps);
|
|---|
| 21640 | return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes);
|
|---|
| 21641 | }
|
|---|
| 21642 |
|
|---|
| 21643 | case ClassComponent:
|
|---|
| 21644 | {
|
|---|
| 21645 | var _Component = workInProgress.type;
|
|---|
| 21646 | var _unresolvedProps = workInProgress.pendingProps;
|
|---|
| 21647 |
|
|---|
| 21648 | var _resolvedProps = workInProgress.elementType === _Component ? _unresolvedProps : resolveDefaultProps(_Component, _unresolvedProps);
|
|---|
| 21649 |
|
|---|
| 21650 | return updateClassComponent(current, workInProgress, _Component, _resolvedProps, renderLanes);
|
|---|
| 21651 | }
|
|---|
| 21652 |
|
|---|
| 21653 | case HostRoot:
|
|---|
| 21654 | return updateHostRoot(current, workInProgress, renderLanes);
|
|---|
| 21655 |
|
|---|
| 21656 | case HostComponent:
|
|---|
| 21657 | return updateHostComponent(current, workInProgress, renderLanes);
|
|---|
| 21658 |
|
|---|
| 21659 | case HostText:
|
|---|
| 21660 | return updateHostText(current, workInProgress);
|
|---|
| 21661 |
|
|---|
| 21662 | case SuspenseComponent:
|
|---|
| 21663 | return updateSuspenseComponent(current, workInProgress, renderLanes);
|
|---|
| 21664 |
|
|---|
| 21665 | case HostPortal:
|
|---|
| 21666 | return updatePortalComponent(current, workInProgress, renderLanes);
|
|---|
| 21667 |
|
|---|
| 21668 | case ForwardRef:
|
|---|
| 21669 | {
|
|---|
| 21670 | var type = workInProgress.type;
|
|---|
| 21671 | var _unresolvedProps2 = workInProgress.pendingProps;
|
|---|
| 21672 |
|
|---|
| 21673 | var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);
|
|---|
| 21674 |
|
|---|
| 21675 | return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes);
|
|---|
| 21676 | }
|
|---|
| 21677 |
|
|---|
| 21678 | case Fragment:
|
|---|
| 21679 | return updateFragment(current, workInProgress, renderLanes);
|
|---|
| 21680 |
|
|---|
| 21681 | case Mode:
|
|---|
| 21682 | return updateMode(current, workInProgress, renderLanes);
|
|---|
| 21683 |
|
|---|
| 21684 | case Profiler:
|
|---|
| 21685 | return updateProfiler(current, workInProgress, renderLanes);
|
|---|
| 21686 |
|
|---|
| 21687 | case ContextProvider:
|
|---|
| 21688 | return updateContextProvider(current, workInProgress, renderLanes);
|
|---|
| 21689 |
|
|---|
| 21690 | case ContextConsumer:
|
|---|
| 21691 | return updateContextConsumer(current, workInProgress, renderLanes);
|
|---|
| 21692 |
|
|---|
| 21693 | case MemoComponent:
|
|---|
| 21694 | {
|
|---|
| 21695 | var _type2 = workInProgress.type;
|
|---|
| 21696 | var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.
|
|---|
| 21697 |
|
|---|
| 21698 | var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);
|
|---|
| 21699 |
|
|---|
| 21700 | {
|
|---|
| 21701 | if (workInProgress.type !== workInProgress.elementType) {
|
|---|
| 21702 | var outerPropTypes = _type2.propTypes;
|
|---|
| 21703 |
|
|---|
| 21704 | if (outerPropTypes) {
|
|---|
| 21705 | checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only
|
|---|
| 21706 | 'prop', getComponentNameFromType(_type2));
|
|---|
| 21707 | }
|
|---|
| 21708 | }
|
|---|
| 21709 | }
|
|---|
| 21710 |
|
|---|
| 21711 | _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);
|
|---|
| 21712 | return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, renderLanes);
|
|---|
| 21713 | }
|
|---|
| 21714 |
|
|---|
| 21715 | case SimpleMemoComponent:
|
|---|
| 21716 | {
|
|---|
| 21717 | return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, renderLanes);
|
|---|
| 21718 | }
|
|---|
| 21719 |
|
|---|
| 21720 | case IncompleteClassComponent:
|
|---|
| 21721 | {
|
|---|
| 21722 | var _Component2 = workInProgress.type;
|
|---|
| 21723 | var _unresolvedProps4 = workInProgress.pendingProps;
|
|---|
| 21724 |
|
|---|
| 21725 | var _resolvedProps4 = workInProgress.elementType === _Component2 ? _unresolvedProps4 : resolveDefaultProps(_Component2, _unresolvedProps4);
|
|---|
| 21726 |
|
|---|
| 21727 | return mountIncompleteClassComponent(current, workInProgress, _Component2, _resolvedProps4, renderLanes);
|
|---|
| 21728 | }
|
|---|
| 21729 |
|
|---|
| 21730 | case SuspenseListComponent:
|
|---|
| 21731 | {
|
|---|
| 21732 | return updateSuspenseListComponent(current, workInProgress, renderLanes);
|
|---|
| 21733 | }
|
|---|
| 21734 |
|
|---|
| 21735 | case ScopeComponent:
|
|---|
| 21736 | {
|
|---|
| 21737 |
|
|---|
| 21738 | break;
|
|---|
| 21739 | }
|
|---|
| 21740 |
|
|---|
| 21741 | case OffscreenComponent:
|
|---|
| 21742 | {
|
|---|
| 21743 | return updateOffscreenComponent(current, workInProgress, renderLanes);
|
|---|
| 21744 | }
|
|---|
| 21745 | }
|
|---|
| 21746 |
|
|---|
| 21747 | throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.');
|
|---|
| 21748 | }
|
|---|
| 21749 |
|
|---|
| 21750 | function markUpdate(workInProgress) {
|
|---|
| 21751 | // Tag the fiber with an update effect. This turns a Placement into
|
|---|
| 21752 | // a PlacementAndUpdate.
|
|---|
| 21753 | workInProgress.flags |= Update;
|
|---|
| 21754 | }
|
|---|
| 21755 |
|
|---|
| 21756 | function markRef$1(workInProgress) {
|
|---|
| 21757 | workInProgress.flags |= Ref;
|
|---|
| 21758 |
|
|---|
| 21759 | {
|
|---|
| 21760 | workInProgress.flags |= RefStatic;
|
|---|
| 21761 | }
|
|---|
| 21762 | }
|
|---|
| 21763 |
|
|---|
| 21764 | var appendAllChildren;
|
|---|
| 21765 | var updateHostContainer;
|
|---|
| 21766 | var updateHostComponent$1;
|
|---|
| 21767 | var updateHostText$1;
|
|---|
| 21768 |
|
|---|
| 21769 | {
|
|---|
| 21770 | // Mutation mode
|
|---|
| 21771 | appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) {
|
|---|
| 21772 | // We only have the top Fiber that was created but we need recurse down its
|
|---|
| 21773 | // children to find all the terminal nodes.
|
|---|
| 21774 | var node = workInProgress.child;
|
|---|
| 21775 |
|
|---|
| 21776 | while (node !== null) {
|
|---|
| 21777 | if (node.tag === HostComponent || node.tag === HostText) {
|
|---|
| 21778 | appendInitialChild(parent, node.stateNode);
|
|---|
| 21779 | } else if (node.tag === HostPortal) ; else if (node.child !== null) {
|
|---|
| 21780 | node.child.return = node;
|
|---|
| 21781 | node = node.child;
|
|---|
| 21782 | continue;
|
|---|
| 21783 | }
|
|---|
| 21784 |
|
|---|
| 21785 | if (node === workInProgress) {
|
|---|
| 21786 | return;
|
|---|
| 21787 | }
|
|---|
| 21788 |
|
|---|
| 21789 | while (node.sibling === null) {
|
|---|
| 21790 | if (node.return === null || node.return === workInProgress) {
|
|---|
| 21791 | return;
|
|---|
| 21792 | }
|
|---|
| 21793 |
|
|---|
| 21794 | node = node.return;
|
|---|
| 21795 | }
|
|---|
| 21796 |
|
|---|
| 21797 | node.sibling.return = node.return;
|
|---|
| 21798 | node = node.sibling;
|
|---|
| 21799 | }
|
|---|
| 21800 | };
|
|---|
| 21801 |
|
|---|
| 21802 | updateHostContainer = function (current, workInProgress) {// Noop
|
|---|
| 21803 | };
|
|---|
| 21804 |
|
|---|
| 21805 | updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) {
|
|---|
| 21806 | // If we have an alternate, that means this is an update and we need to
|
|---|
| 21807 | // schedule a side-effect to do the updates.
|
|---|
| 21808 | var oldProps = current.memoizedProps;
|
|---|
| 21809 |
|
|---|
| 21810 | if (oldProps === newProps) {
|
|---|
| 21811 | // In mutation mode, this is sufficient for a bailout because
|
|---|
| 21812 | // we won't touch this node even if children changed.
|
|---|
| 21813 | return;
|
|---|
| 21814 | } // If we get updated because one of our children updated, we don't
|
|---|
| 21815 | // have newProps so we'll have to reuse them.
|
|---|
| 21816 | // TODO: Split the update API as separate for the props vs. children.
|
|---|
| 21817 | // Even better would be if children weren't special cased at all tho.
|
|---|
| 21818 |
|
|---|
| 21819 |
|
|---|
| 21820 | var instance = workInProgress.stateNode;
|
|---|
| 21821 | var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host
|
|---|
| 21822 | // component is hitting the resume path. Figure out why. Possibly
|
|---|
| 21823 | // related to `hidden`.
|
|---|
| 21824 |
|
|---|
| 21825 | var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); // TODO: Type this specific to this type of component.
|
|---|
| 21826 |
|
|---|
| 21827 | workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there
|
|---|
| 21828 | // is a new ref we mark this as an update. All the work is done in commitWork.
|
|---|
| 21829 |
|
|---|
| 21830 | if (updatePayload) {
|
|---|
| 21831 | markUpdate(workInProgress);
|
|---|
| 21832 | }
|
|---|
| 21833 | };
|
|---|
| 21834 |
|
|---|
| 21835 | updateHostText$1 = function (current, workInProgress, oldText, newText) {
|
|---|
| 21836 | // If the text differs, mark it as an update. All the work in done in commitWork.
|
|---|
| 21837 | if (oldText !== newText) {
|
|---|
| 21838 | markUpdate(workInProgress);
|
|---|
| 21839 | }
|
|---|
| 21840 | };
|
|---|
| 21841 | }
|
|---|
| 21842 |
|
|---|
| 21843 | function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) {
|
|---|
| 21844 | if (getIsHydrating()) {
|
|---|
| 21845 | // If we're hydrating, we should consume as many items as we can
|
|---|
| 21846 | // so we don't leave any behind.
|
|---|
| 21847 | return;
|
|---|
| 21848 | }
|
|---|
| 21849 |
|
|---|
| 21850 | switch (renderState.tailMode) {
|
|---|
| 21851 | case 'hidden':
|
|---|
| 21852 | {
|
|---|
| 21853 | // Any insertions at the end of the tail list after this point
|
|---|
| 21854 | // should be invisible. If there are already mounted boundaries
|
|---|
| 21855 | // anything before them are not considered for collapsing.
|
|---|
| 21856 | // Therefore we need to go through the whole tail to find if
|
|---|
| 21857 | // there are any.
|
|---|
| 21858 | var tailNode = renderState.tail;
|
|---|
| 21859 | var lastTailNode = null;
|
|---|
| 21860 |
|
|---|
| 21861 | while (tailNode !== null) {
|
|---|
| 21862 | if (tailNode.alternate !== null) {
|
|---|
| 21863 | lastTailNode = tailNode;
|
|---|
| 21864 | }
|
|---|
| 21865 |
|
|---|
| 21866 | tailNode = tailNode.sibling;
|
|---|
| 21867 | } // Next we're simply going to delete all insertions after the
|
|---|
| 21868 | // last rendered item.
|
|---|
| 21869 |
|
|---|
| 21870 |
|
|---|
| 21871 | if (lastTailNode === null) {
|
|---|
| 21872 | // All remaining items in the tail are insertions.
|
|---|
| 21873 | renderState.tail = null;
|
|---|
| 21874 | } else {
|
|---|
| 21875 | // Detach the insertion after the last node that was already
|
|---|
| 21876 | // inserted.
|
|---|
| 21877 | lastTailNode.sibling = null;
|
|---|
| 21878 | }
|
|---|
| 21879 |
|
|---|
| 21880 | break;
|
|---|
| 21881 | }
|
|---|
| 21882 |
|
|---|
| 21883 | case 'collapsed':
|
|---|
| 21884 | {
|
|---|
| 21885 | // Any insertions at the end of the tail list after this point
|
|---|
| 21886 | // should be invisible. If there are already mounted boundaries
|
|---|
| 21887 | // anything before them are not considered for collapsing.
|
|---|
| 21888 | // Therefore we need to go through the whole tail to find if
|
|---|
| 21889 | // there are any.
|
|---|
| 21890 | var _tailNode = renderState.tail;
|
|---|
| 21891 | var _lastTailNode = null;
|
|---|
| 21892 |
|
|---|
| 21893 | while (_tailNode !== null) {
|
|---|
| 21894 | if (_tailNode.alternate !== null) {
|
|---|
| 21895 | _lastTailNode = _tailNode;
|
|---|
| 21896 | }
|
|---|
| 21897 |
|
|---|
| 21898 | _tailNode = _tailNode.sibling;
|
|---|
| 21899 | } // Next we're simply going to delete all insertions after the
|
|---|
| 21900 | // last rendered item.
|
|---|
| 21901 |
|
|---|
| 21902 |
|
|---|
| 21903 | if (_lastTailNode === null) {
|
|---|
| 21904 | // All remaining items in the tail are insertions.
|
|---|
| 21905 | if (!hasRenderedATailFallback && renderState.tail !== null) {
|
|---|
| 21906 | // We suspended during the head. We want to show at least one
|
|---|
| 21907 | // row at the tail. So we'll keep on and cut off the rest.
|
|---|
| 21908 | renderState.tail.sibling = null;
|
|---|
| 21909 | } else {
|
|---|
| 21910 | renderState.tail = null;
|
|---|
| 21911 | }
|
|---|
| 21912 | } else {
|
|---|
| 21913 | // Detach the insertion after the last node that was already
|
|---|
| 21914 | // inserted.
|
|---|
| 21915 | _lastTailNode.sibling = null;
|
|---|
| 21916 | }
|
|---|
| 21917 |
|
|---|
| 21918 | break;
|
|---|
| 21919 | }
|
|---|
| 21920 | }
|
|---|
| 21921 | }
|
|---|
| 21922 |
|
|---|
| 21923 | function bubbleProperties(completedWork) {
|
|---|
| 21924 | var didBailout = completedWork.alternate !== null && completedWork.alternate.child === completedWork.child;
|
|---|
| 21925 | var newChildLanes = NoLanes;
|
|---|
| 21926 | var subtreeFlags = NoFlags;
|
|---|
| 21927 |
|
|---|
| 21928 | if (!didBailout) {
|
|---|
| 21929 | // Bubble up the earliest expiration time.
|
|---|
| 21930 | if ( (completedWork.mode & ProfileMode) !== NoMode) {
|
|---|
| 21931 | // In profiling mode, resetChildExpirationTime is also used to reset
|
|---|
| 21932 | // profiler durations.
|
|---|
| 21933 | var actualDuration = completedWork.actualDuration;
|
|---|
| 21934 | var treeBaseDuration = completedWork.selfBaseDuration;
|
|---|
| 21935 | var child = completedWork.child;
|
|---|
| 21936 |
|
|---|
| 21937 | while (child !== null) {
|
|---|
| 21938 | newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes));
|
|---|
| 21939 | subtreeFlags |= child.subtreeFlags;
|
|---|
| 21940 | subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will
|
|---|
| 21941 | // only be updated if work is done on the fiber (i.e. it doesn't bailout).
|
|---|
| 21942 | // When work is done, it should bubble to the parent's actualDuration. If
|
|---|
| 21943 | // the fiber has not been cloned though, (meaning no work was done), then
|
|---|
| 21944 | // this value will reflect the amount of time spent working on a previous
|
|---|
| 21945 | // render. In that case it should not bubble. We determine whether it was
|
|---|
| 21946 | // cloned by comparing the child pointer.
|
|---|
| 21947 |
|
|---|
| 21948 | actualDuration += child.actualDuration;
|
|---|
| 21949 | treeBaseDuration += child.treeBaseDuration;
|
|---|
| 21950 | child = child.sibling;
|
|---|
| 21951 | }
|
|---|
| 21952 |
|
|---|
| 21953 | completedWork.actualDuration = actualDuration;
|
|---|
| 21954 | completedWork.treeBaseDuration = treeBaseDuration;
|
|---|
| 21955 | } else {
|
|---|
| 21956 | var _child = completedWork.child;
|
|---|
| 21957 |
|
|---|
| 21958 | while (_child !== null) {
|
|---|
| 21959 | newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes));
|
|---|
| 21960 | subtreeFlags |= _child.subtreeFlags;
|
|---|
| 21961 | subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code
|
|---|
| 21962 | // smell because it assumes the commit phase is never concurrent with
|
|---|
| 21963 | // the render phase. Will address during refactor to alternate model.
|
|---|
| 21964 |
|
|---|
| 21965 | _child.return = completedWork;
|
|---|
| 21966 | _child = _child.sibling;
|
|---|
| 21967 | }
|
|---|
| 21968 | }
|
|---|
| 21969 |
|
|---|
| 21970 | completedWork.subtreeFlags |= subtreeFlags;
|
|---|
| 21971 | } else {
|
|---|
| 21972 | // Bubble up the earliest expiration time.
|
|---|
| 21973 | if ( (completedWork.mode & ProfileMode) !== NoMode) {
|
|---|
| 21974 | // In profiling mode, resetChildExpirationTime is also used to reset
|
|---|
| 21975 | // profiler durations.
|
|---|
| 21976 | var _treeBaseDuration = completedWork.selfBaseDuration;
|
|---|
| 21977 | var _child2 = completedWork.child;
|
|---|
| 21978 |
|
|---|
| 21979 | while (_child2 !== null) {
|
|---|
| 21980 | newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child2.lanes, _child2.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to,
|
|---|
| 21981 | // so we should bubble those up even during a bailout. All the other
|
|---|
| 21982 | // flags have a lifetime only of a single render + commit, so we should
|
|---|
| 21983 | // ignore them.
|
|---|
| 21984 |
|
|---|
| 21985 | subtreeFlags |= _child2.subtreeFlags & StaticMask;
|
|---|
| 21986 | subtreeFlags |= _child2.flags & StaticMask;
|
|---|
| 21987 | _treeBaseDuration += _child2.treeBaseDuration;
|
|---|
| 21988 | _child2 = _child2.sibling;
|
|---|
| 21989 | }
|
|---|
| 21990 |
|
|---|
| 21991 | completedWork.treeBaseDuration = _treeBaseDuration;
|
|---|
| 21992 | } else {
|
|---|
| 21993 | var _child3 = completedWork.child;
|
|---|
| 21994 |
|
|---|
| 21995 | while (_child3 !== null) {
|
|---|
| 21996 | newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child3.lanes, _child3.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to,
|
|---|
| 21997 | // so we should bubble those up even during a bailout. All the other
|
|---|
| 21998 | // flags have a lifetime only of a single render + commit, so we should
|
|---|
| 21999 | // ignore them.
|
|---|
| 22000 |
|
|---|
| 22001 | subtreeFlags |= _child3.subtreeFlags & StaticMask;
|
|---|
| 22002 | subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code
|
|---|
| 22003 | // smell because it assumes the commit phase is never concurrent with
|
|---|
| 22004 | // the render phase. Will address during refactor to alternate model.
|
|---|
| 22005 |
|
|---|
| 22006 | _child3.return = completedWork;
|
|---|
| 22007 | _child3 = _child3.sibling;
|
|---|
| 22008 | }
|
|---|
| 22009 | }
|
|---|
| 22010 |
|
|---|
| 22011 | completedWork.subtreeFlags |= subtreeFlags;
|
|---|
| 22012 | }
|
|---|
| 22013 |
|
|---|
| 22014 | completedWork.childLanes = newChildLanes;
|
|---|
| 22015 | return didBailout;
|
|---|
| 22016 | }
|
|---|
| 22017 |
|
|---|
| 22018 | function completeDehydratedSuspenseBoundary(current, workInProgress, nextState) {
|
|---|
| 22019 | if (hasUnhydratedTailNodes() && (workInProgress.mode & ConcurrentMode) !== NoMode && (workInProgress.flags & DidCapture) === NoFlags) {
|
|---|
| 22020 | warnIfUnhydratedTailNodes(workInProgress);
|
|---|
| 22021 | resetHydrationState();
|
|---|
| 22022 | workInProgress.flags |= ForceClientRender | Incomplete | ShouldCapture;
|
|---|
| 22023 | return false;
|
|---|
| 22024 | }
|
|---|
| 22025 |
|
|---|
| 22026 | var wasHydrated = popHydrationState(workInProgress);
|
|---|
| 22027 |
|
|---|
| 22028 | if (nextState !== null && nextState.dehydrated !== null) {
|
|---|
| 22029 | // We might be inside a hydration state the first time we're picking up this
|
|---|
| 22030 | // Suspense boundary, and also after we've reentered it for further hydration.
|
|---|
| 22031 | if (current === null) {
|
|---|
| 22032 | if (!wasHydrated) {
|
|---|
| 22033 | throw new Error('A dehydrated suspense component was completed without a hydrated node. ' + 'This is probably a bug in React.');
|
|---|
| 22034 | }
|
|---|
| 22035 |
|
|---|
| 22036 | prepareToHydrateHostSuspenseInstance(workInProgress);
|
|---|
| 22037 | bubbleProperties(workInProgress);
|
|---|
| 22038 |
|
|---|
| 22039 | {
|
|---|
| 22040 | if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22041 | var isTimedOutSuspense = nextState !== null;
|
|---|
| 22042 |
|
|---|
| 22043 | if (isTimedOutSuspense) {
|
|---|
| 22044 | // Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
|---|
| 22045 | var primaryChildFragment = workInProgress.child;
|
|---|
| 22046 |
|
|---|
| 22047 | if (primaryChildFragment !== null) {
|
|---|
| 22048 | // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
|---|
| 22049 | workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration;
|
|---|
| 22050 | }
|
|---|
| 22051 | }
|
|---|
| 22052 | }
|
|---|
| 22053 | }
|
|---|
| 22054 |
|
|---|
| 22055 | return false;
|
|---|
| 22056 | } else {
|
|---|
| 22057 | // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration
|
|---|
| 22058 | // state since we're now exiting out of it. popHydrationState doesn't do that for us.
|
|---|
| 22059 | resetHydrationState();
|
|---|
| 22060 |
|
|---|
| 22061 | if ((workInProgress.flags & DidCapture) === NoFlags) {
|
|---|
| 22062 | // This boundary did not suspend so it's now hydrated and unsuspended.
|
|---|
| 22063 | workInProgress.memoizedState = null;
|
|---|
| 22064 | } // If nothing suspended, we need to schedule an effect to mark this boundary
|
|---|
| 22065 | // as having hydrated so events know that they're free to be invoked.
|
|---|
| 22066 | // It's also a signal to replay events and the suspense callback.
|
|---|
| 22067 | // If something suspended, schedule an effect to attach retry listeners.
|
|---|
| 22068 | // So we might as well always mark this.
|
|---|
| 22069 |
|
|---|
| 22070 |
|
|---|
| 22071 | workInProgress.flags |= Update;
|
|---|
| 22072 | bubbleProperties(workInProgress);
|
|---|
| 22073 |
|
|---|
| 22074 | {
|
|---|
| 22075 | if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22076 | var _isTimedOutSuspense = nextState !== null;
|
|---|
| 22077 |
|
|---|
| 22078 | if (_isTimedOutSuspense) {
|
|---|
| 22079 | // Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
|---|
| 22080 | var _primaryChildFragment = workInProgress.child;
|
|---|
| 22081 |
|
|---|
| 22082 | if (_primaryChildFragment !== null) {
|
|---|
| 22083 | // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
|---|
| 22084 | workInProgress.treeBaseDuration -= _primaryChildFragment.treeBaseDuration;
|
|---|
| 22085 | }
|
|---|
| 22086 | }
|
|---|
| 22087 | }
|
|---|
| 22088 | }
|
|---|
| 22089 |
|
|---|
| 22090 | return false;
|
|---|
| 22091 | }
|
|---|
| 22092 | } else {
|
|---|
| 22093 | // Successfully completed this tree. If this was a forced client render,
|
|---|
| 22094 | // there may have been recoverable errors during first hydration
|
|---|
| 22095 | // attempt. If so, add them to a queue so we can log them in the
|
|---|
| 22096 | // commit phase.
|
|---|
| 22097 | upgradeHydrationErrorsToRecoverable(); // Fall through to normal Suspense path
|
|---|
| 22098 |
|
|---|
| 22099 | return true;
|
|---|
| 22100 | }
|
|---|
| 22101 | }
|
|---|
| 22102 |
|
|---|
| 22103 | function completeWork(current, workInProgress, renderLanes) {
|
|---|
| 22104 | var newProps = workInProgress.pendingProps; // Note: This intentionally doesn't check if we're hydrating because comparing
|
|---|
| 22105 | // to the current tree provider fiber is just as fast and less error-prone.
|
|---|
| 22106 | // Ideally we would have a special version of the work loop only
|
|---|
| 22107 | // for hydration.
|
|---|
| 22108 |
|
|---|
| 22109 | popTreeContext(workInProgress);
|
|---|
| 22110 |
|
|---|
| 22111 | switch (workInProgress.tag) {
|
|---|
| 22112 | case IndeterminateComponent:
|
|---|
| 22113 | case LazyComponent:
|
|---|
| 22114 | case SimpleMemoComponent:
|
|---|
| 22115 | case FunctionComponent:
|
|---|
| 22116 | case ForwardRef:
|
|---|
| 22117 | case Fragment:
|
|---|
| 22118 | case Mode:
|
|---|
| 22119 | case Profiler:
|
|---|
| 22120 | case ContextConsumer:
|
|---|
| 22121 | case MemoComponent:
|
|---|
| 22122 | bubbleProperties(workInProgress);
|
|---|
| 22123 | return null;
|
|---|
| 22124 |
|
|---|
| 22125 | case ClassComponent:
|
|---|
| 22126 | {
|
|---|
| 22127 | var Component = workInProgress.type;
|
|---|
| 22128 |
|
|---|
| 22129 | if (isContextProvider(Component)) {
|
|---|
| 22130 | popContext(workInProgress);
|
|---|
| 22131 | }
|
|---|
| 22132 |
|
|---|
| 22133 | bubbleProperties(workInProgress);
|
|---|
| 22134 | return null;
|
|---|
| 22135 | }
|
|---|
| 22136 |
|
|---|
| 22137 | case HostRoot:
|
|---|
| 22138 | {
|
|---|
| 22139 | var fiberRoot = workInProgress.stateNode;
|
|---|
| 22140 | popHostContainer(workInProgress);
|
|---|
| 22141 | popTopLevelContextObject(workInProgress);
|
|---|
| 22142 | resetWorkInProgressVersions();
|
|---|
| 22143 |
|
|---|
| 22144 | if (fiberRoot.pendingContext) {
|
|---|
| 22145 | fiberRoot.context = fiberRoot.pendingContext;
|
|---|
| 22146 | fiberRoot.pendingContext = null;
|
|---|
| 22147 | }
|
|---|
| 22148 |
|
|---|
| 22149 | if (current === null || current.child === null) {
|
|---|
| 22150 | // If we hydrated, pop so that we can delete any remaining children
|
|---|
| 22151 | // that weren't hydrated.
|
|---|
| 22152 | var wasHydrated = popHydrationState(workInProgress);
|
|---|
| 22153 |
|
|---|
| 22154 | if (wasHydrated) {
|
|---|
| 22155 | // If we hydrated, then we'll need to schedule an update for
|
|---|
| 22156 | // the commit side-effects on the root.
|
|---|
| 22157 | markUpdate(workInProgress);
|
|---|
| 22158 | } else {
|
|---|
| 22159 | if (current !== null) {
|
|---|
| 22160 | var prevState = current.memoizedState;
|
|---|
| 22161 |
|
|---|
| 22162 | if ( // Check if this is a client root
|
|---|
| 22163 | !prevState.isDehydrated || // Check if we reverted to client rendering (e.g. due to an error)
|
|---|
| 22164 | (workInProgress.flags & ForceClientRender) !== NoFlags) {
|
|---|
| 22165 | // Schedule an effect to clear this container at the start of the
|
|---|
| 22166 | // next commit. This handles the case of React rendering into a
|
|---|
| 22167 | // container with previous children. It's also safe to do for
|
|---|
| 22168 | // updates too, because current.child would only be null if the
|
|---|
| 22169 | // previous render was null (so the container would already
|
|---|
| 22170 | // be empty).
|
|---|
| 22171 | workInProgress.flags |= Snapshot; // If this was a forced client render, there may have been
|
|---|
| 22172 | // recoverable errors during first hydration attempt. If so, add
|
|---|
| 22173 | // them to a queue so we can log them in the commit phase.
|
|---|
| 22174 |
|
|---|
| 22175 | upgradeHydrationErrorsToRecoverable();
|
|---|
| 22176 | }
|
|---|
| 22177 | }
|
|---|
| 22178 | }
|
|---|
| 22179 | }
|
|---|
| 22180 |
|
|---|
| 22181 | updateHostContainer(current, workInProgress);
|
|---|
| 22182 | bubbleProperties(workInProgress);
|
|---|
| 22183 |
|
|---|
| 22184 | return null;
|
|---|
| 22185 | }
|
|---|
| 22186 |
|
|---|
| 22187 | case HostComponent:
|
|---|
| 22188 | {
|
|---|
| 22189 | popHostContext(workInProgress);
|
|---|
| 22190 | var rootContainerInstance = getRootHostContainer();
|
|---|
| 22191 | var type = workInProgress.type;
|
|---|
| 22192 |
|
|---|
| 22193 | if (current !== null && workInProgress.stateNode != null) {
|
|---|
| 22194 | updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance);
|
|---|
| 22195 |
|
|---|
| 22196 | if (current.ref !== workInProgress.ref) {
|
|---|
| 22197 | markRef$1(workInProgress);
|
|---|
| 22198 | }
|
|---|
| 22199 | } else {
|
|---|
| 22200 | if (!newProps) {
|
|---|
| 22201 | if (workInProgress.stateNode === null) {
|
|---|
| 22202 | throw new Error('We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
|---|
| 22203 | } // This can happen when we abort work.
|
|---|
| 22204 |
|
|---|
| 22205 |
|
|---|
| 22206 | bubbleProperties(workInProgress);
|
|---|
| 22207 | return null;
|
|---|
| 22208 | }
|
|---|
| 22209 |
|
|---|
| 22210 | var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context
|
|---|
| 22211 | // "stack" as the parent. Then append children as we go in beginWork
|
|---|
| 22212 | // or completeWork depending on whether we want to add them top->down or
|
|---|
| 22213 | // bottom->up. Top->down is faster in IE11.
|
|---|
| 22214 |
|
|---|
| 22215 | var _wasHydrated = popHydrationState(workInProgress);
|
|---|
| 22216 |
|
|---|
| 22217 | if (_wasHydrated) {
|
|---|
| 22218 | // TODO: Move this and createInstance step into the beginPhase
|
|---|
| 22219 | // to consolidate.
|
|---|
| 22220 | if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) {
|
|---|
| 22221 | // If changes to the hydrated node need to be applied at the
|
|---|
| 22222 | // commit-phase we mark this as such.
|
|---|
| 22223 | markUpdate(workInProgress);
|
|---|
| 22224 | }
|
|---|
| 22225 | } else {
|
|---|
| 22226 | var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress);
|
|---|
| 22227 | appendAllChildren(instance, workInProgress, false, false);
|
|---|
| 22228 | workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount.
|
|---|
| 22229 | // (eg DOM renderer supports auto-focus for certain elements).
|
|---|
| 22230 | // Make sure such renderers get scheduled for later work.
|
|---|
| 22231 |
|
|---|
| 22232 | if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) {
|
|---|
| 22233 | markUpdate(workInProgress);
|
|---|
| 22234 | }
|
|---|
| 22235 | }
|
|---|
| 22236 |
|
|---|
| 22237 | if (workInProgress.ref !== null) {
|
|---|
| 22238 | // If there is a ref on a host node we need to schedule a callback
|
|---|
| 22239 | markRef$1(workInProgress);
|
|---|
| 22240 | }
|
|---|
| 22241 | }
|
|---|
| 22242 |
|
|---|
| 22243 | bubbleProperties(workInProgress);
|
|---|
| 22244 | return null;
|
|---|
| 22245 | }
|
|---|
| 22246 |
|
|---|
| 22247 | case HostText:
|
|---|
| 22248 | {
|
|---|
| 22249 | var newText = newProps;
|
|---|
| 22250 |
|
|---|
| 22251 | if (current && workInProgress.stateNode != null) {
|
|---|
| 22252 | var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need
|
|---|
| 22253 | // to schedule a side-effect to do the updates.
|
|---|
| 22254 |
|
|---|
| 22255 | updateHostText$1(current, workInProgress, oldText, newText);
|
|---|
| 22256 | } else {
|
|---|
| 22257 | if (typeof newText !== 'string') {
|
|---|
| 22258 | if (workInProgress.stateNode === null) {
|
|---|
| 22259 | throw new Error('We must have new props for new mounts. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
|---|
| 22260 | } // This can happen when we abort work.
|
|---|
| 22261 |
|
|---|
| 22262 | }
|
|---|
| 22263 |
|
|---|
| 22264 | var _rootContainerInstance = getRootHostContainer();
|
|---|
| 22265 |
|
|---|
| 22266 | var _currentHostContext = getHostContext();
|
|---|
| 22267 |
|
|---|
| 22268 | var _wasHydrated2 = popHydrationState(workInProgress);
|
|---|
| 22269 |
|
|---|
| 22270 | if (_wasHydrated2) {
|
|---|
| 22271 | if (prepareToHydrateHostTextInstance(workInProgress)) {
|
|---|
| 22272 | markUpdate(workInProgress);
|
|---|
| 22273 | }
|
|---|
| 22274 | } else {
|
|---|
| 22275 | workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress);
|
|---|
| 22276 | }
|
|---|
| 22277 | }
|
|---|
| 22278 |
|
|---|
| 22279 | bubbleProperties(workInProgress);
|
|---|
| 22280 | return null;
|
|---|
| 22281 | }
|
|---|
| 22282 |
|
|---|
| 22283 | case SuspenseComponent:
|
|---|
| 22284 | {
|
|---|
| 22285 | popSuspenseContext(workInProgress);
|
|---|
| 22286 | var nextState = workInProgress.memoizedState; // Special path for dehydrated boundaries. We may eventually move this
|
|---|
| 22287 | // to its own fiber type so that we can add other kinds of hydration
|
|---|
| 22288 | // boundaries that aren't associated with a Suspense tree. In anticipation
|
|---|
| 22289 | // of such a refactor, all the hydration logic is contained in
|
|---|
| 22290 | // this branch.
|
|---|
| 22291 |
|
|---|
| 22292 | if (current === null || current.memoizedState !== null && current.memoizedState.dehydrated !== null) {
|
|---|
| 22293 | var fallthroughToNormalSuspensePath = completeDehydratedSuspenseBoundary(current, workInProgress, nextState);
|
|---|
| 22294 |
|
|---|
| 22295 | if (!fallthroughToNormalSuspensePath) {
|
|---|
| 22296 | if (workInProgress.flags & ShouldCapture) {
|
|---|
| 22297 | // Special case. There were remaining unhydrated nodes. We treat
|
|---|
| 22298 | // this as a mismatch. Revert to client rendering.
|
|---|
| 22299 | return workInProgress;
|
|---|
| 22300 | } else {
|
|---|
| 22301 | // Did not finish hydrating, either because this is the initial
|
|---|
| 22302 | // render or because something suspended.
|
|---|
| 22303 | return null;
|
|---|
| 22304 | }
|
|---|
| 22305 | } // Continue with the normal Suspense path.
|
|---|
| 22306 |
|
|---|
| 22307 | }
|
|---|
| 22308 |
|
|---|
| 22309 | if ((workInProgress.flags & DidCapture) !== NoFlags) {
|
|---|
| 22310 | // Something suspended. Re-render with the fallback children.
|
|---|
| 22311 | workInProgress.lanes = renderLanes; // Do not reset the effect list.
|
|---|
| 22312 |
|
|---|
| 22313 | if ( (workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22314 | transferActualDuration(workInProgress);
|
|---|
| 22315 | } // Don't bubble properties in this case.
|
|---|
| 22316 |
|
|---|
| 22317 |
|
|---|
| 22318 | return workInProgress;
|
|---|
| 22319 | }
|
|---|
| 22320 |
|
|---|
| 22321 | var nextDidTimeout = nextState !== null;
|
|---|
| 22322 | var prevDidTimeout = current !== null && current.memoizedState !== null;
|
|---|
| 22323 | // a passive effect, which is when we process the transitions
|
|---|
| 22324 |
|
|---|
| 22325 |
|
|---|
| 22326 | if (nextDidTimeout !== prevDidTimeout) {
|
|---|
| 22327 | // an effect to toggle the subtree's visibility. When we switch from
|
|---|
| 22328 | // fallback -> primary, the inner Offscreen fiber schedules this effect
|
|---|
| 22329 | // as part of its normal complete phase. But when we switch from
|
|---|
| 22330 | // primary -> fallback, the inner Offscreen fiber does not have a complete
|
|---|
| 22331 | // phase. So we need to schedule its effect here.
|
|---|
| 22332 | //
|
|---|
| 22333 | // We also use this flag to connect/disconnect the effects, but the same
|
|---|
| 22334 | // logic applies: when re-connecting, the Offscreen fiber's complete
|
|---|
| 22335 | // phase will handle scheduling the effect. It's only when the fallback
|
|---|
| 22336 | // is active that we have to do anything special.
|
|---|
| 22337 |
|
|---|
| 22338 |
|
|---|
| 22339 | if (nextDidTimeout) {
|
|---|
| 22340 | var _offscreenFiber2 = workInProgress.child;
|
|---|
| 22341 | _offscreenFiber2.flags |= Visibility; // TODO: This will still suspend a synchronous tree if anything
|
|---|
| 22342 | // in the concurrent tree already suspended during this render.
|
|---|
| 22343 | // This is a known bug.
|
|---|
| 22344 |
|
|---|
| 22345 | if ((workInProgress.mode & ConcurrentMode) !== NoMode) {
|
|---|
| 22346 | // TODO: Move this back to throwException because this is too late
|
|---|
| 22347 | // if this is a large tree which is common for initial loads. We
|
|---|
| 22348 | // don't know if we should restart a render or not until we get
|
|---|
| 22349 | // this marker, and this is too late.
|
|---|
| 22350 | // If this render already had a ping or lower pri updates,
|
|---|
| 22351 | // and this is the first time we know we're going to suspend we
|
|---|
| 22352 | // should be able to immediately restart from within throwException.
|
|---|
| 22353 | var hasInvisibleChildContext = current === null && (workInProgress.memoizedProps.unstable_avoidThisFallback !== true || !enableSuspenseAvoidThisFallback);
|
|---|
| 22354 |
|
|---|
| 22355 | if (hasInvisibleChildContext || hasSuspenseContext(suspenseStackCursor.current, InvisibleParentSuspenseContext)) {
|
|---|
| 22356 | // If this was in an invisible tree or a new render, then showing
|
|---|
| 22357 | // this boundary is ok.
|
|---|
| 22358 | renderDidSuspend();
|
|---|
| 22359 | } else {
|
|---|
| 22360 | // Otherwise, we're going to have to hide content so we should
|
|---|
| 22361 | // suspend for longer if possible.
|
|---|
| 22362 | renderDidSuspendDelayIfPossible();
|
|---|
| 22363 | }
|
|---|
| 22364 | }
|
|---|
| 22365 | }
|
|---|
| 22366 | }
|
|---|
| 22367 |
|
|---|
| 22368 | var wakeables = workInProgress.updateQueue;
|
|---|
| 22369 |
|
|---|
| 22370 | if (wakeables !== null) {
|
|---|
| 22371 | // Schedule an effect to attach a retry listener to the promise.
|
|---|
| 22372 | // TODO: Move to passive phase
|
|---|
| 22373 | workInProgress.flags |= Update;
|
|---|
| 22374 | }
|
|---|
| 22375 |
|
|---|
| 22376 | bubbleProperties(workInProgress);
|
|---|
| 22377 |
|
|---|
| 22378 | {
|
|---|
| 22379 | if ((workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22380 | if (nextDidTimeout) {
|
|---|
| 22381 | // Don't count time spent in a timed out Suspense subtree as part of the base duration.
|
|---|
| 22382 | var primaryChildFragment = workInProgress.child;
|
|---|
| 22383 |
|
|---|
| 22384 | if (primaryChildFragment !== null) {
|
|---|
| 22385 | // $FlowFixMe Flow doesn't support type casting in combination with the -= operator
|
|---|
| 22386 | workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration;
|
|---|
| 22387 | }
|
|---|
| 22388 | }
|
|---|
| 22389 | }
|
|---|
| 22390 | }
|
|---|
| 22391 |
|
|---|
| 22392 | return null;
|
|---|
| 22393 | }
|
|---|
| 22394 |
|
|---|
| 22395 | case HostPortal:
|
|---|
| 22396 | popHostContainer(workInProgress);
|
|---|
| 22397 | updateHostContainer(current, workInProgress);
|
|---|
| 22398 |
|
|---|
| 22399 | if (current === null) {
|
|---|
| 22400 | preparePortalMount(workInProgress.stateNode.containerInfo);
|
|---|
| 22401 | }
|
|---|
| 22402 |
|
|---|
| 22403 | bubbleProperties(workInProgress);
|
|---|
| 22404 | return null;
|
|---|
| 22405 |
|
|---|
| 22406 | case ContextProvider:
|
|---|
| 22407 | // Pop provider fiber
|
|---|
| 22408 | var context = workInProgress.type._context;
|
|---|
| 22409 | popProvider(context, workInProgress);
|
|---|
| 22410 | bubbleProperties(workInProgress);
|
|---|
| 22411 | return null;
|
|---|
| 22412 |
|
|---|
| 22413 | case IncompleteClassComponent:
|
|---|
| 22414 | {
|
|---|
| 22415 | // Same as class component case. I put it down here so that the tags are
|
|---|
| 22416 | // sequential to ensure this switch is compiled to a jump table.
|
|---|
| 22417 | var _Component = workInProgress.type;
|
|---|
| 22418 |
|
|---|
| 22419 | if (isContextProvider(_Component)) {
|
|---|
| 22420 | popContext(workInProgress);
|
|---|
| 22421 | }
|
|---|
| 22422 |
|
|---|
| 22423 | bubbleProperties(workInProgress);
|
|---|
| 22424 | return null;
|
|---|
| 22425 | }
|
|---|
| 22426 |
|
|---|
| 22427 | case SuspenseListComponent:
|
|---|
| 22428 | {
|
|---|
| 22429 | popSuspenseContext(workInProgress);
|
|---|
| 22430 | var renderState = workInProgress.memoizedState;
|
|---|
| 22431 |
|
|---|
| 22432 | if (renderState === null) {
|
|---|
| 22433 | // We're running in the default, "independent" mode.
|
|---|
| 22434 | // We don't do anything in this mode.
|
|---|
| 22435 | bubbleProperties(workInProgress);
|
|---|
| 22436 | return null;
|
|---|
| 22437 | }
|
|---|
| 22438 |
|
|---|
| 22439 | var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags;
|
|---|
| 22440 | var renderedTail = renderState.rendering;
|
|---|
| 22441 |
|
|---|
| 22442 | if (renderedTail === null) {
|
|---|
| 22443 | // We just rendered the head.
|
|---|
| 22444 | if (!didSuspendAlready) {
|
|---|
| 22445 | // This is the first pass. We need to figure out if anything is still
|
|---|
| 22446 | // suspended in the rendered set.
|
|---|
| 22447 | // If new content unsuspended, but there's still some content that
|
|---|
| 22448 | // didn't. Then we need to do a second pass that forces everything
|
|---|
| 22449 | // to keep showing their fallbacks.
|
|---|
| 22450 | // We might be suspended if something in this render pass suspended, or
|
|---|
| 22451 | // something in the previous committed pass suspended. Otherwise,
|
|---|
| 22452 | // there's no chance so we can skip the expensive call to
|
|---|
| 22453 | // findFirstSuspended.
|
|---|
| 22454 | var cannotBeSuspended = renderHasNotSuspendedYet() && (current === null || (current.flags & DidCapture) === NoFlags);
|
|---|
| 22455 |
|
|---|
| 22456 | if (!cannotBeSuspended) {
|
|---|
| 22457 | var row = workInProgress.child;
|
|---|
| 22458 |
|
|---|
| 22459 | while (row !== null) {
|
|---|
| 22460 | var suspended = findFirstSuspended(row);
|
|---|
| 22461 |
|
|---|
| 22462 | if (suspended !== null) {
|
|---|
| 22463 | didSuspendAlready = true;
|
|---|
| 22464 | workInProgress.flags |= DidCapture;
|
|---|
| 22465 | cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as
|
|---|
| 22466 | // part of the second pass. In that case nothing will subscribe to
|
|---|
| 22467 | // its thenables. Instead, we'll transfer its thenables to the
|
|---|
| 22468 | // SuspenseList so that it can retry if they resolve.
|
|---|
| 22469 | // There might be multiple of these in the list but since we're
|
|---|
| 22470 | // going to wait for all of them anyway, it doesn't really matter
|
|---|
| 22471 | // which ones gets to ping. In theory we could get clever and keep
|
|---|
| 22472 | // track of how many dependencies remain but it gets tricky because
|
|---|
| 22473 | // in the meantime, we can add/remove/change items and dependencies.
|
|---|
| 22474 | // We might bail out of the loop before finding any but that
|
|---|
| 22475 | // doesn't matter since that means that the other boundaries that
|
|---|
| 22476 | // we did find already has their listeners attached.
|
|---|
| 22477 |
|
|---|
| 22478 | var newThenables = suspended.updateQueue;
|
|---|
| 22479 |
|
|---|
| 22480 | if (newThenables !== null) {
|
|---|
| 22481 | workInProgress.updateQueue = newThenables;
|
|---|
| 22482 | workInProgress.flags |= Update;
|
|---|
| 22483 | } // Rerender the whole list, but this time, we'll force fallbacks
|
|---|
| 22484 | // to stay in place.
|
|---|
| 22485 | // Reset the effect flags before doing the second pass since that's now invalid.
|
|---|
| 22486 | // Reset the child fibers to their original state.
|
|---|
| 22487 |
|
|---|
| 22488 |
|
|---|
| 22489 | workInProgress.subtreeFlags = NoFlags;
|
|---|
| 22490 | resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately
|
|---|
| 22491 | // rerender the children.
|
|---|
| 22492 |
|
|---|
| 22493 | pushSuspenseContext(workInProgress, setShallowSuspenseContext(suspenseStackCursor.current, ForceSuspenseFallback)); // Don't bubble properties in this case.
|
|---|
| 22494 |
|
|---|
| 22495 | return workInProgress.child;
|
|---|
| 22496 | }
|
|---|
| 22497 |
|
|---|
| 22498 | row = row.sibling;
|
|---|
| 22499 | }
|
|---|
| 22500 | }
|
|---|
| 22501 |
|
|---|
| 22502 | if (renderState.tail !== null && now() > getRenderTargetTime()) {
|
|---|
| 22503 | // We have already passed our CPU deadline but we still have rows
|
|---|
| 22504 | // left in the tail. We'll just give up further attempts to render
|
|---|
| 22505 | // the main content and only render fallbacks.
|
|---|
| 22506 | workInProgress.flags |= DidCapture;
|
|---|
| 22507 | didSuspendAlready = true;
|
|---|
| 22508 | cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
|
|---|
| 22509 | // to get it started back up to attempt the next item. While in terms
|
|---|
| 22510 | // of priority this work has the same priority as this current render,
|
|---|
| 22511 | // it's not part of the same transition once the transition has
|
|---|
| 22512 | // committed. If it's sync, we still want to yield so that it can be
|
|---|
| 22513 | // painted. Conceptually, this is really the same as pinging.
|
|---|
| 22514 | // We can use any RetryLane even if it's the one currently rendering
|
|---|
| 22515 | // since we're leaving it behind on this node.
|
|---|
| 22516 |
|
|---|
| 22517 | workInProgress.lanes = SomeRetryLane;
|
|---|
| 22518 | }
|
|---|
| 22519 | } else {
|
|---|
| 22520 | cutOffTailIfNeeded(renderState, false);
|
|---|
| 22521 | } // Next we're going to render the tail.
|
|---|
| 22522 |
|
|---|
| 22523 | } else {
|
|---|
| 22524 | // Append the rendered row to the child list.
|
|---|
| 22525 | if (!didSuspendAlready) {
|
|---|
| 22526 | var _suspended = findFirstSuspended(renderedTail);
|
|---|
| 22527 |
|
|---|
| 22528 | if (_suspended !== null) {
|
|---|
| 22529 | workInProgress.flags |= DidCapture;
|
|---|
| 22530 | didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't
|
|---|
| 22531 | // get lost if this row ends up dropped during a second pass.
|
|---|
| 22532 |
|
|---|
| 22533 | var _newThenables = _suspended.updateQueue;
|
|---|
| 22534 |
|
|---|
| 22535 | if (_newThenables !== null) {
|
|---|
| 22536 | workInProgress.updateQueue = _newThenables;
|
|---|
| 22537 | workInProgress.flags |= Update;
|
|---|
| 22538 | }
|
|---|
| 22539 |
|
|---|
| 22540 | cutOffTailIfNeeded(renderState, true); // This might have been modified.
|
|---|
| 22541 |
|
|---|
| 22542 | if (renderState.tail === null && renderState.tailMode === 'hidden' && !renderedTail.alternate && !getIsHydrating() // We don't cut it if we're hydrating.
|
|---|
| 22543 | ) {
|
|---|
| 22544 | // We're done.
|
|---|
| 22545 | bubbleProperties(workInProgress);
|
|---|
| 22546 | return null;
|
|---|
| 22547 | }
|
|---|
| 22548 | } else if ( // The time it took to render last row is greater than the remaining
|
|---|
| 22549 | // time we have to render. So rendering one more row would likely
|
|---|
| 22550 | // exceed it.
|
|---|
| 22551 | now() * 2 - renderState.renderingStartTime > getRenderTargetTime() && renderLanes !== OffscreenLane) {
|
|---|
| 22552 | // We have now passed our CPU deadline and we'll just give up further
|
|---|
| 22553 | // attempts to render the main content and only render fallbacks.
|
|---|
| 22554 | // The assumption is that this is usually faster.
|
|---|
| 22555 | workInProgress.flags |= DidCapture;
|
|---|
| 22556 | didSuspendAlready = true;
|
|---|
| 22557 | cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this
|
|---|
| 22558 | // to get it started back up to attempt the next item. While in terms
|
|---|
| 22559 | // of priority this work has the same priority as this current render,
|
|---|
| 22560 | // it's not part of the same transition once the transition has
|
|---|
| 22561 | // committed. If it's sync, we still want to yield so that it can be
|
|---|
| 22562 | // painted. Conceptually, this is really the same as pinging.
|
|---|
| 22563 | // We can use any RetryLane even if it's the one currently rendering
|
|---|
| 22564 | // since we're leaving it behind on this node.
|
|---|
| 22565 |
|
|---|
| 22566 | workInProgress.lanes = SomeRetryLane;
|
|---|
| 22567 | }
|
|---|
| 22568 | }
|
|---|
| 22569 |
|
|---|
| 22570 | if (renderState.isBackwards) {
|
|---|
| 22571 | // The effect list of the backwards tail will have been added
|
|---|
| 22572 | // to the end. This breaks the guarantee that life-cycles fire in
|
|---|
| 22573 | // sibling order but that isn't a strong guarantee promised by React.
|
|---|
| 22574 | // Especially since these might also just pop in during future commits.
|
|---|
| 22575 | // Append to the beginning of the list.
|
|---|
| 22576 | renderedTail.sibling = workInProgress.child;
|
|---|
| 22577 | workInProgress.child = renderedTail;
|
|---|
| 22578 | } else {
|
|---|
| 22579 | var previousSibling = renderState.last;
|
|---|
| 22580 |
|
|---|
| 22581 | if (previousSibling !== null) {
|
|---|
| 22582 | previousSibling.sibling = renderedTail;
|
|---|
| 22583 | } else {
|
|---|
| 22584 | workInProgress.child = renderedTail;
|
|---|
| 22585 | }
|
|---|
| 22586 |
|
|---|
| 22587 | renderState.last = renderedTail;
|
|---|
| 22588 | }
|
|---|
| 22589 | }
|
|---|
| 22590 |
|
|---|
| 22591 | if (renderState.tail !== null) {
|
|---|
| 22592 | // We still have tail rows to render.
|
|---|
| 22593 | // Pop a row.
|
|---|
| 22594 | var next = renderState.tail;
|
|---|
| 22595 | renderState.rendering = next;
|
|---|
| 22596 | renderState.tail = next.sibling;
|
|---|
| 22597 | renderState.renderingStartTime = now();
|
|---|
| 22598 | next.sibling = null; // Restore the context.
|
|---|
| 22599 | // TODO: We can probably just avoid popping it instead and only
|
|---|
| 22600 | // setting it the first time we go from not suspended to suspended.
|
|---|
| 22601 |
|
|---|
| 22602 | var suspenseContext = suspenseStackCursor.current;
|
|---|
| 22603 |
|
|---|
| 22604 | if (didSuspendAlready) {
|
|---|
| 22605 | suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback);
|
|---|
| 22606 | } else {
|
|---|
| 22607 | suspenseContext = setDefaultShallowSuspenseContext(suspenseContext);
|
|---|
| 22608 | }
|
|---|
| 22609 |
|
|---|
| 22610 | pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row.
|
|---|
| 22611 | // Don't bubble properties in this case.
|
|---|
| 22612 |
|
|---|
| 22613 | return next;
|
|---|
| 22614 | }
|
|---|
| 22615 |
|
|---|
| 22616 | bubbleProperties(workInProgress);
|
|---|
| 22617 | return null;
|
|---|
| 22618 | }
|
|---|
| 22619 |
|
|---|
| 22620 | case ScopeComponent:
|
|---|
| 22621 | {
|
|---|
| 22622 |
|
|---|
| 22623 | break;
|
|---|
| 22624 | }
|
|---|
| 22625 |
|
|---|
| 22626 | case OffscreenComponent:
|
|---|
| 22627 | case LegacyHiddenComponent:
|
|---|
| 22628 | {
|
|---|
| 22629 | popRenderLanes(workInProgress);
|
|---|
| 22630 | var _nextState = workInProgress.memoizedState;
|
|---|
| 22631 | var nextIsHidden = _nextState !== null;
|
|---|
| 22632 |
|
|---|
| 22633 | if (current !== null) {
|
|---|
| 22634 | var _prevState = current.memoizedState;
|
|---|
| 22635 | var prevIsHidden = _prevState !== null;
|
|---|
| 22636 |
|
|---|
| 22637 | if (prevIsHidden !== nextIsHidden && ( // LegacyHidden doesn't do any hiding — it only pre-renders.
|
|---|
| 22638 | !enableLegacyHidden )) {
|
|---|
| 22639 | workInProgress.flags |= Visibility;
|
|---|
| 22640 | }
|
|---|
| 22641 | }
|
|---|
| 22642 |
|
|---|
| 22643 | if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) {
|
|---|
| 22644 | bubbleProperties(workInProgress);
|
|---|
| 22645 | } else {
|
|---|
| 22646 | // Don't bubble properties for hidden children unless we're rendering
|
|---|
| 22647 | // at offscreen priority.
|
|---|
| 22648 | if (includesSomeLane(subtreeRenderLanes, OffscreenLane)) {
|
|---|
| 22649 | bubbleProperties(workInProgress);
|
|---|
| 22650 |
|
|---|
| 22651 | {
|
|---|
| 22652 | // Check if there was an insertion or update in the hidden subtree.
|
|---|
| 22653 | // If so, we need to hide those nodes in the commit phase, so
|
|---|
| 22654 | // schedule a visibility effect.
|
|---|
| 22655 | if ( workInProgress.subtreeFlags & (Placement | Update)) {
|
|---|
| 22656 | workInProgress.flags |= Visibility;
|
|---|
| 22657 | }
|
|---|
| 22658 | }
|
|---|
| 22659 | }
|
|---|
| 22660 | }
|
|---|
| 22661 | return null;
|
|---|
| 22662 | }
|
|---|
| 22663 |
|
|---|
| 22664 | case CacheComponent:
|
|---|
| 22665 | {
|
|---|
| 22666 |
|
|---|
| 22667 | return null;
|
|---|
| 22668 | }
|
|---|
| 22669 |
|
|---|
| 22670 | case TracingMarkerComponent:
|
|---|
| 22671 | {
|
|---|
| 22672 |
|
|---|
| 22673 | return null;
|
|---|
| 22674 | }
|
|---|
| 22675 | }
|
|---|
| 22676 |
|
|---|
| 22677 | throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.');
|
|---|
| 22678 | }
|
|---|
| 22679 |
|
|---|
| 22680 | function unwindWork(current, workInProgress, renderLanes) {
|
|---|
| 22681 | // Note: This intentionally doesn't check if we're hydrating because comparing
|
|---|
| 22682 | // to the current tree provider fiber is just as fast and less error-prone.
|
|---|
| 22683 | // Ideally we would have a special version of the work loop only
|
|---|
| 22684 | // for hydration.
|
|---|
| 22685 | popTreeContext(workInProgress);
|
|---|
| 22686 |
|
|---|
| 22687 | switch (workInProgress.tag) {
|
|---|
| 22688 | case ClassComponent:
|
|---|
| 22689 | {
|
|---|
| 22690 | var Component = workInProgress.type;
|
|---|
| 22691 |
|
|---|
| 22692 | if (isContextProvider(Component)) {
|
|---|
| 22693 | popContext(workInProgress);
|
|---|
| 22694 | }
|
|---|
| 22695 |
|
|---|
| 22696 | var flags = workInProgress.flags;
|
|---|
| 22697 |
|
|---|
| 22698 | if (flags & ShouldCapture) {
|
|---|
| 22699 | workInProgress.flags = flags & ~ShouldCapture | DidCapture;
|
|---|
| 22700 |
|
|---|
| 22701 | if ( (workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22702 | transferActualDuration(workInProgress);
|
|---|
| 22703 | }
|
|---|
| 22704 |
|
|---|
| 22705 | return workInProgress;
|
|---|
| 22706 | }
|
|---|
| 22707 |
|
|---|
| 22708 | return null;
|
|---|
| 22709 | }
|
|---|
| 22710 |
|
|---|
| 22711 | case HostRoot:
|
|---|
| 22712 | {
|
|---|
| 22713 | var root = workInProgress.stateNode;
|
|---|
| 22714 | popHostContainer(workInProgress);
|
|---|
| 22715 | popTopLevelContextObject(workInProgress);
|
|---|
| 22716 | resetWorkInProgressVersions();
|
|---|
| 22717 | var _flags = workInProgress.flags;
|
|---|
| 22718 |
|
|---|
| 22719 | if ((_flags & ShouldCapture) !== NoFlags && (_flags & DidCapture) === NoFlags) {
|
|---|
| 22720 | // There was an error during render that wasn't captured by a suspense
|
|---|
| 22721 | // boundary. Do a second pass on the root to unmount the children.
|
|---|
| 22722 | workInProgress.flags = _flags & ~ShouldCapture | DidCapture;
|
|---|
| 22723 | return workInProgress;
|
|---|
| 22724 | } // We unwound to the root without completing it. Exit.
|
|---|
| 22725 |
|
|---|
| 22726 |
|
|---|
| 22727 | return null;
|
|---|
| 22728 | }
|
|---|
| 22729 |
|
|---|
| 22730 | case HostComponent:
|
|---|
| 22731 | {
|
|---|
| 22732 | // TODO: popHydrationState
|
|---|
| 22733 | popHostContext(workInProgress);
|
|---|
| 22734 | return null;
|
|---|
| 22735 | }
|
|---|
| 22736 |
|
|---|
| 22737 | case SuspenseComponent:
|
|---|
| 22738 | {
|
|---|
| 22739 | popSuspenseContext(workInProgress);
|
|---|
| 22740 | var suspenseState = workInProgress.memoizedState;
|
|---|
| 22741 |
|
|---|
| 22742 | if (suspenseState !== null && suspenseState.dehydrated !== null) {
|
|---|
| 22743 | if (workInProgress.alternate === null) {
|
|---|
| 22744 | throw new Error('Threw in newly mounted dehydrated component. This is likely a bug in ' + 'React. Please file an issue.');
|
|---|
| 22745 | }
|
|---|
| 22746 |
|
|---|
| 22747 | resetHydrationState();
|
|---|
| 22748 | }
|
|---|
| 22749 |
|
|---|
| 22750 | var _flags2 = workInProgress.flags;
|
|---|
| 22751 |
|
|---|
| 22752 | if (_flags2 & ShouldCapture) {
|
|---|
| 22753 | workInProgress.flags = _flags2 & ~ShouldCapture | DidCapture; // Captured a suspense effect. Re-render the boundary.
|
|---|
| 22754 |
|
|---|
| 22755 | if ( (workInProgress.mode & ProfileMode) !== NoMode) {
|
|---|
| 22756 | transferActualDuration(workInProgress);
|
|---|
| 22757 | }
|
|---|
| 22758 |
|
|---|
| 22759 | return workInProgress;
|
|---|
| 22760 | }
|
|---|
| 22761 |
|
|---|
| 22762 | return null;
|
|---|
| 22763 | }
|
|---|
| 22764 |
|
|---|
| 22765 | case SuspenseListComponent:
|
|---|
| 22766 | {
|
|---|
| 22767 | popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been
|
|---|
| 22768 | // caught by a nested boundary. If not, it should bubble through.
|
|---|
| 22769 |
|
|---|
| 22770 | return null;
|
|---|
| 22771 | }
|
|---|
| 22772 |
|
|---|
| 22773 | case HostPortal:
|
|---|
| 22774 | popHostContainer(workInProgress);
|
|---|
| 22775 | return null;
|
|---|
| 22776 |
|
|---|
| 22777 | case ContextProvider:
|
|---|
| 22778 | var context = workInProgress.type._context;
|
|---|
| 22779 | popProvider(context, workInProgress);
|
|---|
| 22780 | return null;
|
|---|
| 22781 |
|
|---|
| 22782 | case OffscreenComponent:
|
|---|
| 22783 | case LegacyHiddenComponent:
|
|---|
| 22784 | popRenderLanes(workInProgress);
|
|---|
| 22785 | return null;
|
|---|
| 22786 |
|
|---|
| 22787 | case CacheComponent:
|
|---|
| 22788 |
|
|---|
| 22789 | return null;
|
|---|
| 22790 |
|
|---|
| 22791 | default:
|
|---|
| 22792 | return null;
|
|---|
| 22793 | }
|
|---|
| 22794 | }
|
|---|
| 22795 |
|
|---|
| 22796 | function unwindInterruptedWork(current, interruptedWork, renderLanes) {
|
|---|
| 22797 | // Note: This intentionally doesn't check if we're hydrating because comparing
|
|---|
| 22798 | // to the current tree provider fiber is just as fast and less error-prone.
|
|---|
| 22799 | // Ideally we would have a special version of the work loop only
|
|---|
| 22800 | // for hydration.
|
|---|
| 22801 | popTreeContext(interruptedWork);
|
|---|
| 22802 |
|
|---|
| 22803 | switch (interruptedWork.tag) {
|
|---|
| 22804 | case ClassComponent:
|
|---|
| 22805 | {
|
|---|
| 22806 | var childContextTypes = interruptedWork.type.childContextTypes;
|
|---|
| 22807 |
|
|---|
| 22808 | if (childContextTypes !== null && childContextTypes !== undefined) {
|
|---|
| 22809 | popContext(interruptedWork);
|
|---|
| 22810 | }
|
|---|
| 22811 |
|
|---|
| 22812 | break;
|
|---|
| 22813 | }
|
|---|
| 22814 |
|
|---|
| 22815 | case HostRoot:
|
|---|
| 22816 | {
|
|---|
| 22817 | var root = interruptedWork.stateNode;
|
|---|
| 22818 | popHostContainer(interruptedWork);
|
|---|
| 22819 | popTopLevelContextObject(interruptedWork);
|
|---|
| 22820 | resetWorkInProgressVersions();
|
|---|
| 22821 | break;
|
|---|
| 22822 | }
|
|---|
| 22823 |
|
|---|
| 22824 | case HostComponent:
|
|---|
| 22825 | {
|
|---|
| 22826 | popHostContext(interruptedWork);
|
|---|
| 22827 | break;
|
|---|
| 22828 | }
|
|---|
| 22829 |
|
|---|
| 22830 | case HostPortal:
|
|---|
| 22831 | popHostContainer(interruptedWork);
|
|---|
| 22832 | break;
|
|---|
| 22833 |
|
|---|
| 22834 | case SuspenseComponent:
|
|---|
| 22835 | popSuspenseContext(interruptedWork);
|
|---|
| 22836 | break;
|
|---|
| 22837 |
|
|---|
| 22838 | case SuspenseListComponent:
|
|---|
| 22839 | popSuspenseContext(interruptedWork);
|
|---|
| 22840 | break;
|
|---|
| 22841 |
|
|---|
| 22842 | case ContextProvider:
|
|---|
| 22843 | var context = interruptedWork.type._context;
|
|---|
| 22844 | popProvider(context, interruptedWork);
|
|---|
| 22845 | break;
|
|---|
| 22846 |
|
|---|
| 22847 | case OffscreenComponent:
|
|---|
| 22848 | case LegacyHiddenComponent:
|
|---|
| 22849 | popRenderLanes(interruptedWork);
|
|---|
| 22850 | break;
|
|---|
| 22851 | }
|
|---|
| 22852 | }
|
|---|
| 22853 |
|
|---|
| 22854 | var didWarnAboutUndefinedSnapshotBeforeUpdate = null;
|
|---|
| 22855 |
|
|---|
| 22856 | {
|
|---|
| 22857 | didWarnAboutUndefinedSnapshotBeforeUpdate = new Set();
|
|---|
| 22858 | } // Used during the commit phase to track the state of the Offscreen component stack.
|
|---|
| 22859 | // Allows us to avoid traversing the return path to find the nearest Offscreen ancestor.
|
|---|
| 22860 | // Only used when enableSuspenseLayoutEffectSemantics is enabled.
|
|---|
| 22861 |
|
|---|
| 22862 |
|
|---|
| 22863 | var offscreenSubtreeIsHidden = false;
|
|---|
| 22864 | var offscreenSubtreeWasHidden = false;
|
|---|
| 22865 | var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
|
|---|
| 22866 | var nextEffect = null; // Used for Profiling builds to track updaters.
|
|---|
| 22867 |
|
|---|
| 22868 | var inProgressLanes = null;
|
|---|
| 22869 | var inProgressRoot = null;
|
|---|
| 22870 | function reportUncaughtErrorInDEV(error) {
|
|---|
| 22871 | // Wrapping each small part of the commit phase into a guarded
|
|---|
| 22872 | // callback is a bit too slow (https://github.com/facebook/react/pull/21666).
|
|---|
| 22873 | // But we rely on it to surface errors to DEV tools like overlays
|
|---|
| 22874 | // (https://github.com/facebook/react/issues/21712).
|
|---|
| 22875 | // As a compromise, rethrow only caught errors in a guard.
|
|---|
| 22876 | {
|
|---|
| 22877 | invokeGuardedCallback(null, function () {
|
|---|
| 22878 | throw error;
|
|---|
| 22879 | });
|
|---|
| 22880 | clearCaughtError();
|
|---|
| 22881 | }
|
|---|
| 22882 | }
|
|---|
| 22883 |
|
|---|
| 22884 | var callComponentWillUnmountWithTimer = function (current, instance) {
|
|---|
| 22885 | instance.props = current.memoizedProps;
|
|---|
| 22886 | instance.state = current.memoizedState;
|
|---|
| 22887 |
|
|---|
| 22888 | if ( current.mode & ProfileMode) {
|
|---|
| 22889 | try {
|
|---|
| 22890 | startLayoutEffectTimer();
|
|---|
| 22891 | instance.componentWillUnmount();
|
|---|
| 22892 | } finally {
|
|---|
| 22893 | recordLayoutEffectDuration(current);
|
|---|
| 22894 | }
|
|---|
| 22895 | } else {
|
|---|
| 22896 | instance.componentWillUnmount();
|
|---|
| 22897 | }
|
|---|
| 22898 | }; // Capture errors so they don't interrupt mounting.
|
|---|
| 22899 |
|
|---|
| 22900 |
|
|---|
| 22901 | function safelyCallCommitHookLayoutEffectListMount(current, nearestMountedAncestor) {
|
|---|
| 22902 | try {
|
|---|
| 22903 | commitHookEffectListMount(Layout, current);
|
|---|
| 22904 | } catch (error) {
|
|---|
| 22905 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22906 | }
|
|---|
| 22907 | } // Capture errors so they don't interrupt unmounting.
|
|---|
| 22908 |
|
|---|
| 22909 |
|
|---|
| 22910 | function safelyCallComponentWillUnmount(current, nearestMountedAncestor, instance) {
|
|---|
| 22911 | try {
|
|---|
| 22912 | callComponentWillUnmountWithTimer(current, instance);
|
|---|
| 22913 | } catch (error) {
|
|---|
| 22914 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22915 | }
|
|---|
| 22916 | } // Capture errors so they don't interrupt mounting.
|
|---|
| 22917 |
|
|---|
| 22918 |
|
|---|
| 22919 | function safelyCallComponentDidMount(current, nearestMountedAncestor, instance) {
|
|---|
| 22920 | try {
|
|---|
| 22921 | instance.componentDidMount();
|
|---|
| 22922 | } catch (error) {
|
|---|
| 22923 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22924 | }
|
|---|
| 22925 | } // Capture errors so they don't interrupt mounting.
|
|---|
| 22926 |
|
|---|
| 22927 |
|
|---|
| 22928 | function safelyAttachRef(current, nearestMountedAncestor) {
|
|---|
| 22929 | try {
|
|---|
| 22930 | commitAttachRef(current);
|
|---|
| 22931 | } catch (error) {
|
|---|
| 22932 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22933 | }
|
|---|
| 22934 | }
|
|---|
| 22935 |
|
|---|
| 22936 | function safelyDetachRef(current, nearestMountedAncestor) {
|
|---|
| 22937 | var ref = current.ref;
|
|---|
| 22938 |
|
|---|
| 22939 | if (ref !== null) {
|
|---|
| 22940 | if (typeof ref === 'function') {
|
|---|
| 22941 | var retVal;
|
|---|
| 22942 |
|
|---|
| 22943 | try {
|
|---|
| 22944 | if (enableProfilerTimer && enableProfilerCommitHooks && current.mode & ProfileMode) {
|
|---|
| 22945 | try {
|
|---|
| 22946 | startLayoutEffectTimer();
|
|---|
| 22947 | retVal = ref(null);
|
|---|
| 22948 | } finally {
|
|---|
| 22949 | recordLayoutEffectDuration(current);
|
|---|
| 22950 | }
|
|---|
| 22951 | } else {
|
|---|
| 22952 | retVal = ref(null);
|
|---|
| 22953 | }
|
|---|
| 22954 | } catch (error) {
|
|---|
| 22955 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22956 | }
|
|---|
| 22957 |
|
|---|
| 22958 | {
|
|---|
| 22959 | if (typeof retVal === 'function') {
|
|---|
| 22960 | error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(current));
|
|---|
| 22961 | }
|
|---|
| 22962 | }
|
|---|
| 22963 | } else {
|
|---|
| 22964 | ref.current = null;
|
|---|
| 22965 | }
|
|---|
| 22966 | }
|
|---|
| 22967 | }
|
|---|
| 22968 |
|
|---|
| 22969 | function safelyCallDestroy(current, nearestMountedAncestor, destroy) {
|
|---|
| 22970 | try {
|
|---|
| 22971 | destroy();
|
|---|
| 22972 | } catch (error) {
|
|---|
| 22973 | captureCommitPhaseError(current, nearestMountedAncestor, error);
|
|---|
| 22974 | }
|
|---|
| 22975 | }
|
|---|
| 22976 |
|
|---|
| 22977 | var focusedInstanceHandle = null;
|
|---|
| 22978 | var shouldFireAfterActiveInstanceBlur = false;
|
|---|
| 22979 | function commitBeforeMutationEffects(root, firstChild) {
|
|---|
| 22980 | focusedInstanceHandle = prepareForCommit(root.containerInfo);
|
|---|
| 22981 | nextEffect = firstChild;
|
|---|
| 22982 | commitBeforeMutationEffects_begin(); // We no longer need to track the active instance fiber
|
|---|
| 22983 |
|
|---|
| 22984 | var shouldFire = shouldFireAfterActiveInstanceBlur;
|
|---|
| 22985 | shouldFireAfterActiveInstanceBlur = false;
|
|---|
| 22986 | focusedInstanceHandle = null;
|
|---|
| 22987 | return shouldFire;
|
|---|
| 22988 | }
|
|---|
| 22989 |
|
|---|
| 22990 | function commitBeforeMutationEffects_begin() {
|
|---|
| 22991 | while (nextEffect !== null) {
|
|---|
| 22992 | var fiber = nextEffect; // This phase is only used for beforeActiveInstanceBlur.
|
|---|
| 22993 |
|
|---|
| 22994 | var child = fiber.child;
|
|---|
| 22995 |
|
|---|
| 22996 | if ((fiber.subtreeFlags & BeforeMutationMask) !== NoFlags && child !== null) {
|
|---|
| 22997 | child.return = fiber;
|
|---|
| 22998 | nextEffect = child;
|
|---|
| 22999 | } else {
|
|---|
| 23000 | commitBeforeMutationEffects_complete();
|
|---|
| 23001 | }
|
|---|
| 23002 | }
|
|---|
| 23003 | }
|
|---|
| 23004 |
|
|---|
| 23005 | function commitBeforeMutationEffects_complete() {
|
|---|
| 23006 | while (nextEffect !== null) {
|
|---|
| 23007 | var fiber = nextEffect;
|
|---|
| 23008 | setCurrentFiber(fiber);
|
|---|
| 23009 |
|
|---|
| 23010 | try {
|
|---|
| 23011 | commitBeforeMutationEffectsOnFiber(fiber);
|
|---|
| 23012 | } catch (error) {
|
|---|
| 23013 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 23014 | }
|
|---|
| 23015 |
|
|---|
| 23016 | resetCurrentFiber();
|
|---|
| 23017 | var sibling = fiber.sibling;
|
|---|
| 23018 |
|
|---|
| 23019 | if (sibling !== null) {
|
|---|
| 23020 | sibling.return = fiber.return;
|
|---|
| 23021 | nextEffect = sibling;
|
|---|
| 23022 | return;
|
|---|
| 23023 | }
|
|---|
| 23024 |
|
|---|
| 23025 | nextEffect = fiber.return;
|
|---|
| 23026 | }
|
|---|
| 23027 | }
|
|---|
| 23028 |
|
|---|
| 23029 | function commitBeforeMutationEffectsOnFiber(finishedWork) {
|
|---|
| 23030 | var current = finishedWork.alternate;
|
|---|
| 23031 | var flags = finishedWork.flags;
|
|---|
| 23032 |
|
|---|
| 23033 | if ((flags & Snapshot) !== NoFlags) {
|
|---|
| 23034 | setCurrentFiber(finishedWork);
|
|---|
| 23035 |
|
|---|
| 23036 | switch (finishedWork.tag) {
|
|---|
| 23037 | case FunctionComponent:
|
|---|
| 23038 | case ForwardRef:
|
|---|
| 23039 | case SimpleMemoComponent:
|
|---|
| 23040 | {
|
|---|
| 23041 | break;
|
|---|
| 23042 | }
|
|---|
| 23043 |
|
|---|
| 23044 | case ClassComponent:
|
|---|
| 23045 | {
|
|---|
| 23046 | if (current !== null) {
|
|---|
| 23047 | var prevProps = current.memoizedProps;
|
|---|
| 23048 | var prevState = current.memoizedState;
|
|---|
| 23049 | var instance = finishedWork.stateNode; // We could update instance props and state here,
|
|---|
| 23050 | // but instead we rely on them being set during last render.
|
|---|
| 23051 | // TODO: revisit this when we implement resuming.
|
|---|
| 23052 |
|
|---|
| 23053 | {
|
|---|
| 23054 | if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
|---|
| 23055 | if (instance.props !== finishedWork.memoizedProps) {
|
|---|
| 23056 | error('Expected %s props to match memoized props before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23057 | }
|
|---|
| 23058 |
|
|---|
| 23059 | if (instance.state !== finishedWork.memoizedState) {
|
|---|
| 23060 | error('Expected %s state to match memoized state before ' + 'getSnapshotBeforeUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23061 | }
|
|---|
| 23062 | }
|
|---|
| 23063 | }
|
|---|
| 23064 |
|
|---|
| 23065 | var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState);
|
|---|
| 23066 |
|
|---|
| 23067 | {
|
|---|
| 23068 | var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate;
|
|---|
| 23069 |
|
|---|
| 23070 | if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) {
|
|---|
| 23071 | didWarnSet.add(finishedWork.type);
|
|---|
| 23072 |
|
|---|
| 23073 | error('%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + 'must be returned. You have returned undefined.', getComponentNameFromFiber(finishedWork));
|
|---|
| 23074 | }
|
|---|
| 23075 | }
|
|---|
| 23076 |
|
|---|
| 23077 | instance.__reactInternalSnapshotBeforeUpdate = snapshot;
|
|---|
| 23078 | }
|
|---|
| 23079 |
|
|---|
| 23080 | break;
|
|---|
| 23081 | }
|
|---|
| 23082 |
|
|---|
| 23083 | case HostRoot:
|
|---|
| 23084 | {
|
|---|
| 23085 | {
|
|---|
| 23086 | var root = finishedWork.stateNode;
|
|---|
| 23087 | clearContainer(root.containerInfo);
|
|---|
| 23088 | }
|
|---|
| 23089 |
|
|---|
| 23090 | break;
|
|---|
| 23091 | }
|
|---|
| 23092 |
|
|---|
| 23093 | case HostComponent:
|
|---|
| 23094 | case HostText:
|
|---|
| 23095 | case HostPortal:
|
|---|
| 23096 | case IncompleteClassComponent:
|
|---|
| 23097 | // Nothing to do for these component types
|
|---|
| 23098 | break;
|
|---|
| 23099 |
|
|---|
| 23100 | default:
|
|---|
| 23101 | {
|
|---|
| 23102 | throw new Error('This unit of work tag should not have side-effects. This error is ' + 'likely caused by a bug in React. Please file an issue.');
|
|---|
| 23103 | }
|
|---|
| 23104 | }
|
|---|
| 23105 |
|
|---|
| 23106 | resetCurrentFiber();
|
|---|
| 23107 | }
|
|---|
| 23108 | }
|
|---|
| 23109 |
|
|---|
| 23110 | function commitHookEffectListUnmount(flags, finishedWork, nearestMountedAncestor) {
|
|---|
| 23111 | var updateQueue = finishedWork.updateQueue;
|
|---|
| 23112 | var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
|---|
| 23113 |
|
|---|
| 23114 | if (lastEffect !== null) {
|
|---|
| 23115 | var firstEffect = lastEffect.next;
|
|---|
| 23116 | var effect = firstEffect;
|
|---|
| 23117 |
|
|---|
| 23118 | do {
|
|---|
| 23119 | if ((effect.tag & flags) === flags) {
|
|---|
| 23120 | // Unmount
|
|---|
| 23121 | var destroy = effect.destroy;
|
|---|
| 23122 | effect.destroy = undefined;
|
|---|
| 23123 |
|
|---|
| 23124 | if (destroy !== undefined) {
|
|---|
| 23125 | {
|
|---|
| 23126 | if ((flags & Passive$1) !== NoFlags$1) {
|
|---|
| 23127 | markComponentPassiveEffectUnmountStarted(finishedWork);
|
|---|
| 23128 | } else if ((flags & Layout) !== NoFlags$1) {
|
|---|
| 23129 | markComponentLayoutEffectUnmountStarted(finishedWork);
|
|---|
| 23130 | }
|
|---|
| 23131 | }
|
|---|
| 23132 |
|
|---|
| 23133 | {
|
|---|
| 23134 | if ((flags & Insertion) !== NoFlags$1) {
|
|---|
| 23135 | setIsRunningInsertionEffect(true);
|
|---|
| 23136 | }
|
|---|
| 23137 | }
|
|---|
| 23138 |
|
|---|
| 23139 | safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy);
|
|---|
| 23140 |
|
|---|
| 23141 | {
|
|---|
| 23142 | if ((flags & Insertion) !== NoFlags$1) {
|
|---|
| 23143 | setIsRunningInsertionEffect(false);
|
|---|
| 23144 | }
|
|---|
| 23145 | }
|
|---|
| 23146 |
|
|---|
| 23147 | {
|
|---|
| 23148 | if ((flags & Passive$1) !== NoFlags$1) {
|
|---|
| 23149 | markComponentPassiveEffectUnmountStopped();
|
|---|
| 23150 | } else if ((flags & Layout) !== NoFlags$1) {
|
|---|
| 23151 | markComponentLayoutEffectUnmountStopped();
|
|---|
| 23152 | }
|
|---|
| 23153 | }
|
|---|
| 23154 | }
|
|---|
| 23155 | }
|
|---|
| 23156 |
|
|---|
| 23157 | effect = effect.next;
|
|---|
| 23158 | } while (effect !== firstEffect);
|
|---|
| 23159 | }
|
|---|
| 23160 | }
|
|---|
| 23161 |
|
|---|
| 23162 | function commitHookEffectListMount(flags, finishedWork) {
|
|---|
| 23163 | var updateQueue = finishedWork.updateQueue;
|
|---|
| 23164 | var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null;
|
|---|
| 23165 |
|
|---|
| 23166 | if (lastEffect !== null) {
|
|---|
| 23167 | var firstEffect = lastEffect.next;
|
|---|
| 23168 | var effect = firstEffect;
|
|---|
| 23169 |
|
|---|
| 23170 | do {
|
|---|
| 23171 | if ((effect.tag & flags) === flags) {
|
|---|
| 23172 | {
|
|---|
| 23173 | if ((flags & Passive$1) !== NoFlags$1) {
|
|---|
| 23174 | markComponentPassiveEffectMountStarted(finishedWork);
|
|---|
| 23175 | } else if ((flags & Layout) !== NoFlags$1) {
|
|---|
| 23176 | markComponentLayoutEffectMountStarted(finishedWork);
|
|---|
| 23177 | }
|
|---|
| 23178 | } // Mount
|
|---|
| 23179 |
|
|---|
| 23180 |
|
|---|
| 23181 | var create = effect.create;
|
|---|
| 23182 |
|
|---|
| 23183 | {
|
|---|
| 23184 | if ((flags & Insertion) !== NoFlags$1) {
|
|---|
| 23185 | setIsRunningInsertionEffect(true);
|
|---|
| 23186 | }
|
|---|
| 23187 | }
|
|---|
| 23188 |
|
|---|
| 23189 | effect.destroy = create();
|
|---|
| 23190 |
|
|---|
| 23191 | {
|
|---|
| 23192 | if ((flags & Insertion) !== NoFlags$1) {
|
|---|
| 23193 | setIsRunningInsertionEffect(false);
|
|---|
| 23194 | }
|
|---|
| 23195 | }
|
|---|
| 23196 |
|
|---|
| 23197 | {
|
|---|
| 23198 | if ((flags & Passive$1) !== NoFlags$1) {
|
|---|
| 23199 | markComponentPassiveEffectMountStopped();
|
|---|
| 23200 | } else if ((flags & Layout) !== NoFlags$1) {
|
|---|
| 23201 | markComponentLayoutEffectMountStopped();
|
|---|
| 23202 | }
|
|---|
| 23203 | }
|
|---|
| 23204 |
|
|---|
| 23205 | {
|
|---|
| 23206 | var destroy = effect.destroy;
|
|---|
| 23207 |
|
|---|
| 23208 | if (destroy !== undefined && typeof destroy !== 'function') {
|
|---|
| 23209 | var hookName = void 0;
|
|---|
| 23210 |
|
|---|
| 23211 | if ((effect.tag & Layout) !== NoFlags) {
|
|---|
| 23212 | hookName = 'useLayoutEffect';
|
|---|
| 23213 | } else if ((effect.tag & Insertion) !== NoFlags) {
|
|---|
| 23214 | hookName = 'useInsertionEffect';
|
|---|
| 23215 | } else {
|
|---|
| 23216 | hookName = 'useEffect';
|
|---|
| 23217 | }
|
|---|
| 23218 |
|
|---|
| 23219 | var addendum = void 0;
|
|---|
| 23220 |
|
|---|
| 23221 | if (destroy === null) {
|
|---|
| 23222 | addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).';
|
|---|
| 23223 | } else if (typeof destroy.then === 'function') {
|
|---|
| 23224 | addendum = '\n\nIt looks like you wrote ' + hookName + '(async () => ...) or returned a Promise. ' + 'Instead, write the async function inside your effect ' + 'and call it immediately:\n\n' + hookName + '(() => {\n' + ' async function fetchData() {\n' + ' // You can await here\n' + ' const response = await MyAPI.getData(someId);\n' + ' // ...\n' + ' }\n' + ' fetchData();\n' + "}, [someId]); // Or [] if effect doesn't need props or state\n\n" + 'Learn more about data fetching with Hooks: https://reactjs.org/link/hooks-data-fetching';
|
|---|
| 23225 | } else {
|
|---|
| 23226 | addendum = ' You returned: ' + destroy;
|
|---|
| 23227 | }
|
|---|
| 23228 |
|
|---|
| 23229 | error('%s must not return anything besides a function, ' + 'which is used for clean-up.%s', hookName, addendum);
|
|---|
| 23230 | }
|
|---|
| 23231 | }
|
|---|
| 23232 | }
|
|---|
| 23233 |
|
|---|
| 23234 | effect = effect.next;
|
|---|
| 23235 | } while (effect !== firstEffect);
|
|---|
| 23236 | }
|
|---|
| 23237 | }
|
|---|
| 23238 |
|
|---|
| 23239 | function commitPassiveEffectDurations(finishedRoot, finishedWork) {
|
|---|
| 23240 | {
|
|---|
| 23241 | // Only Profilers with work in their subtree will have an Update effect scheduled.
|
|---|
| 23242 | if ((finishedWork.flags & Update) !== NoFlags) {
|
|---|
| 23243 | switch (finishedWork.tag) {
|
|---|
| 23244 | case Profiler:
|
|---|
| 23245 | {
|
|---|
| 23246 | var passiveEffectDuration = finishedWork.stateNode.passiveEffectDuration;
|
|---|
| 23247 | var _finishedWork$memoize = finishedWork.memoizedProps,
|
|---|
| 23248 | id = _finishedWork$memoize.id,
|
|---|
| 23249 | onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase.
|
|---|
| 23250 | // It does not get reset until the start of the next commit phase.
|
|---|
| 23251 |
|
|---|
| 23252 | var commitTime = getCommitTime();
|
|---|
| 23253 | var phase = finishedWork.alternate === null ? 'mount' : 'update';
|
|---|
| 23254 |
|
|---|
| 23255 | {
|
|---|
| 23256 | if (isCurrentUpdateNested()) {
|
|---|
| 23257 | phase = 'nested-update';
|
|---|
| 23258 | }
|
|---|
| 23259 | }
|
|---|
| 23260 |
|
|---|
| 23261 | if (typeof onPostCommit === 'function') {
|
|---|
| 23262 | onPostCommit(id, phase, passiveEffectDuration, commitTime);
|
|---|
| 23263 | } // Bubble times to the next nearest ancestor Profiler.
|
|---|
| 23264 | // After we process that Profiler, we'll bubble further up.
|
|---|
| 23265 |
|
|---|
| 23266 |
|
|---|
| 23267 | var parentFiber = finishedWork.return;
|
|---|
| 23268 |
|
|---|
| 23269 | outer: while (parentFiber !== null) {
|
|---|
| 23270 | switch (parentFiber.tag) {
|
|---|
| 23271 | case HostRoot:
|
|---|
| 23272 | var root = parentFiber.stateNode;
|
|---|
| 23273 | root.passiveEffectDuration += passiveEffectDuration;
|
|---|
| 23274 | break outer;
|
|---|
| 23275 |
|
|---|
| 23276 | case Profiler:
|
|---|
| 23277 | var parentStateNode = parentFiber.stateNode;
|
|---|
| 23278 | parentStateNode.passiveEffectDuration += passiveEffectDuration;
|
|---|
| 23279 | break outer;
|
|---|
| 23280 | }
|
|---|
| 23281 |
|
|---|
| 23282 | parentFiber = parentFiber.return;
|
|---|
| 23283 | }
|
|---|
| 23284 |
|
|---|
| 23285 | break;
|
|---|
| 23286 | }
|
|---|
| 23287 | }
|
|---|
| 23288 | }
|
|---|
| 23289 | }
|
|---|
| 23290 | }
|
|---|
| 23291 |
|
|---|
| 23292 | function commitLayoutEffectOnFiber(finishedRoot, current, finishedWork, committedLanes) {
|
|---|
| 23293 | if ((finishedWork.flags & LayoutMask) !== NoFlags) {
|
|---|
| 23294 | switch (finishedWork.tag) {
|
|---|
| 23295 | case FunctionComponent:
|
|---|
| 23296 | case ForwardRef:
|
|---|
| 23297 | case SimpleMemoComponent:
|
|---|
| 23298 | {
|
|---|
| 23299 | if ( !offscreenSubtreeWasHidden) {
|
|---|
| 23300 | // At this point layout effects have already been destroyed (during mutation phase).
|
|---|
| 23301 | // This is done to prevent sibling component effects from interfering with each other,
|
|---|
| 23302 | // e.g. a destroy function in one component should never override a ref set
|
|---|
| 23303 | // by a create function in another component during the same commit.
|
|---|
| 23304 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 23305 | try {
|
|---|
| 23306 | startLayoutEffectTimer();
|
|---|
| 23307 | commitHookEffectListMount(Layout | HasEffect, finishedWork);
|
|---|
| 23308 | } finally {
|
|---|
| 23309 | recordLayoutEffectDuration(finishedWork);
|
|---|
| 23310 | }
|
|---|
| 23311 | } else {
|
|---|
| 23312 | commitHookEffectListMount(Layout | HasEffect, finishedWork);
|
|---|
| 23313 | }
|
|---|
| 23314 | }
|
|---|
| 23315 |
|
|---|
| 23316 | break;
|
|---|
| 23317 | }
|
|---|
| 23318 |
|
|---|
| 23319 | case ClassComponent:
|
|---|
| 23320 | {
|
|---|
| 23321 | var instance = finishedWork.stateNode;
|
|---|
| 23322 |
|
|---|
| 23323 | if (finishedWork.flags & Update) {
|
|---|
| 23324 | if (!offscreenSubtreeWasHidden) {
|
|---|
| 23325 | if (current === null) {
|
|---|
| 23326 | // We could update instance props and state here,
|
|---|
| 23327 | // but instead we rely on them being set during last render.
|
|---|
| 23328 | // TODO: revisit this when we implement resuming.
|
|---|
| 23329 | {
|
|---|
| 23330 | if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
|---|
| 23331 | if (instance.props !== finishedWork.memoizedProps) {
|
|---|
| 23332 | error('Expected %s props to match memoized props before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23333 | }
|
|---|
| 23334 |
|
|---|
| 23335 | if (instance.state !== finishedWork.memoizedState) {
|
|---|
| 23336 | error('Expected %s state to match memoized state before ' + 'componentDidMount. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23337 | }
|
|---|
| 23338 | }
|
|---|
| 23339 | }
|
|---|
| 23340 |
|
|---|
| 23341 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 23342 | try {
|
|---|
| 23343 | startLayoutEffectTimer();
|
|---|
| 23344 | instance.componentDidMount();
|
|---|
| 23345 | } finally {
|
|---|
| 23346 | recordLayoutEffectDuration(finishedWork);
|
|---|
| 23347 | }
|
|---|
| 23348 | } else {
|
|---|
| 23349 | instance.componentDidMount();
|
|---|
| 23350 | }
|
|---|
| 23351 | } else {
|
|---|
| 23352 | var prevProps = finishedWork.elementType === finishedWork.type ? current.memoizedProps : resolveDefaultProps(finishedWork.type, current.memoizedProps);
|
|---|
| 23353 | var prevState = current.memoizedState; // We could update instance props and state here,
|
|---|
| 23354 | // but instead we rely on them being set during last render.
|
|---|
| 23355 | // TODO: revisit this when we implement resuming.
|
|---|
| 23356 |
|
|---|
| 23357 | {
|
|---|
| 23358 | if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
|---|
| 23359 | if (instance.props !== finishedWork.memoizedProps) {
|
|---|
| 23360 | error('Expected %s props to match memoized props before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23361 | }
|
|---|
| 23362 |
|
|---|
| 23363 | if (instance.state !== finishedWork.memoizedState) {
|
|---|
| 23364 | error('Expected %s state to match memoized state before ' + 'componentDidUpdate. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23365 | }
|
|---|
| 23366 | }
|
|---|
| 23367 | }
|
|---|
| 23368 |
|
|---|
| 23369 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 23370 | try {
|
|---|
| 23371 | startLayoutEffectTimer();
|
|---|
| 23372 | instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
|
|---|
| 23373 | } finally {
|
|---|
| 23374 | recordLayoutEffectDuration(finishedWork);
|
|---|
| 23375 | }
|
|---|
| 23376 | } else {
|
|---|
| 23377 | instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate);
|
|---|
| 23378 | }
|
|---|
| 23379 | }
|
|---|
| 23380 | }
|
|---|
| 23381 | } // TODO: I think this is now always non-null by the time it reaches the
|
|---|
| 23382 | // commit phase. Consider removing the type check.
|
|---|
| 23383 |
|
|---|
| 23384 |
|
|---|
| 23385 | var updateQueue = finishedWork.updateQueue;
|
|---|
| 23386 |
|
|---|
| 23387 | if (updateQueue !== null) {
|
|---|
| 23388 | {
|
|---|
| 23389 | if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) {
|
|---|
| 23390 | if (instance.props !== finishedWork.memoizedProps) {
|
|---|
| 23391 | error('Expected %s props to match memoized props before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.props`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23392 | }
|
|---|
| 23393 |
|
|---|
| 23394 | if (instance.state !== finishedWork.memoizedState) {
|
|---|
| 23395 | error('Expected %s state to match memoized state before ' + 'processing the update queue. ' + 'This might either be because of a bug in React, or because ' + 'a component reassigns its own `this.state`. ' + 'Please file an issue.', getComponentNameFromFiber(finishedWork) || 'instance');
|
|---|
| 23396 | }
|
|---|
| 23397 | }
|
|---|
| 23398 | } // We could update instance props and state here,
|
|---|
| 23399 | // but instead we rely on them being set during last render.
|
|---|
| 23400 | // TODO: revisit this when we implement resuming.
|
|---|
| 23401 |
|
|---|
| 23402 |
|
|---|
| 23403 | commitUpdateQueue(finishedWork, updateQueue, instance);
|
|---|
| 23404 | }
|
|---|
| 23405 |
|
|---|
| 23406 | break;
|
|---|
| 23407 | }
|
|---|
| 23408 |
|
|---|
| 23409 | case HostRoot:
|
|---|
| 23410 | {
|
|---|
| 23411 | // TODO: I think this is now always non-null by the time it reaches the
|
|---|
| 23412 | // commit phase. Consider removing the type check.
|
|---|
| 23413 | var _updateQueue = finishedWork.updateQueue;
|
|---|
| 23414 |
|
|---|
| 23415 | if (_updateQueue !== null) {
|
|---|
| 23416 | var _instance = null;
|
|---|
| 23417 |
|
|---|
| 23418 | if (finishedWork.child !== null) {
|
|---|
| 23419 | switch (finishedWork.child.tag) {
|
|---|
| 23420 | case HostComponent:
|
|---|
| 23421 | _instance = getPublicInstance(finishedWork.child.stateNode);
|
|---|
| 23422 | break;
|
|---|
| 23423 |
|
|---|
| 23424 | case ClassComponent:
|
|---|
| 23425 | _instance = finishedWork.child.stateNode;
|
|---|
| 23426 | break;
|
|---|
| 23427 | }
|
|---|
| 23428 | }
|
|---|
| 23429 |
|
|---|
| 23430 | commitUpdateQueue(finishedWork, _updateQueue, _instance);
|
|---|
| 23431 | }
|
|---|
| 23432 |
|
|---|
| 23433 | break;
|
|---|
| 23434 | }
|
|---|
| 23435 |
|
|---|
| 23436 | case HostComponent:
|
|---|
| 23437 | {
|
|---|
| 23438 | var _instance2 = finishedWork.stateNode; // Renderers may schedule work to be done after host components are mounted
|
|---|
| 23439 | // (eg DOM renderer may schedule auto-focus for inputs and form controls).
|
|---|
| 23440 | // These effects should only be committed when components are first mounted,
|
|---|
| 23441 | // aka when there is no current/alternate.
|
|---|
| 23442 |
|
|---|
| 23443 | if (current === null && finishedWork.flags & Update) {
|
|---|
| 23444 | var type = finishedWork.type;
|
|---|
| 23445 | var props = finishedWork.memoizedProps;
|
|---|
| 23446 | commitMount(_instance2, type, props);
|
|---|
| 23447 | }
|
|---|
| 23448 |
|
|---|
| 23449 | break;
|
|---|
| 23450 | }
|
|---|
| 23451 |
|
|---|
| 23452 | case HostText:
|
|---|
| 23453 | {
|
|---|
| 23454 | // We have no life-cycles associated with text.
|
|---|
| 23455 | break;
|
|---|
| 23456 | }
|
|---|
| 23457 |
|
|---|
| 23458 | case HostPortal:
|
|---|
| 23459 | {
|
|---|
| 23460 | // We have no life-cycles associated with portals.
|
|---|
| 23461 | break;
|
|---|
| 23462 | }
|
|---|
| 23463 |
|
|---|
| 23464 | case Profiler:
|
|---|
| 23465 | {
|
|---|
| 23466 | {
|
|---|
| 23467 | var _finishedWork$memoize2 = finishedWork.memoizedProps,
|
|---|
| 23468 | onCommit = _finishedWork$memoize2.onCommit,
|
|---|
| 23469 | onRender = _finishedWork$memoize2.onRender;
|
|---|
| 23470 | var effectDuration = finishedWork.stateNode.effectDuration;
|
|---|
| 23471 | var commitTime = getCommitTime();
|
|---|
| 23472 | var phase = current === null ? 'mount' : 'update';
|
|---|
| 23473 |
|
|---|
| 23474 | {
|
|---|
| 23475 | if (isCurrentUpdateNested()) {
|
|---|
| 23476 | phase = 'nested-update';
|
|---|
| 23477 | }
|
|---|
| 23478 | }
|
|---|
| 23479 |
|
|---|
| 23480 | if (typeof onRender === 'function') {
|
|---|
| 23481 | onRender(finishedWork.memoizedProps.id, phase, finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, commitTime);
|
|---|
| 23482 | }
|
|---|
| 23483 |
|
|---|
| 23484 | {
|
|---|
| 23485 | if (typeof onCommit === 'function') {
|
|---|
| 23486 | onCommit(finishedWork.memoizedProps.id, phase, effectDuration, commitTime);
|
|---|
| 23487 | } // Schedule a passive effect for this Profiler to call onPostCommit hooks.
|
|---|
| 23488 | // This effect should be scheduled even if there is no onPostCommit callback for this Profiler,
|
|---|
| 23489 | // because the effect is also where times bubble to parent Profilers.
|
|---|
| 23490 |
|
|---|
| 23491 |
|
|---|
| 23492 | enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor.
|
|---|
| 23493 | // Do not reset these values until the next render so DevTools has a chance to read them first.
|
|---|
| 23494 |
|
|---|
| 23495 | var parentFiber = finishedWork.return;
|
|---|
| 23496 |
|
|---|
| 23497 | outer: while (parentFiber !== null) {
|
|---|
| 23498 | switch (parentFiber.tag) {
|
|---|
| 23499 | case HostRoot:
|
|---|
| 23500 | var root = parentFiber.stateNode;
|
|---|
| 23501 | root.effectDuration += effectDuration;
|
|---|
| 23502 | break outer;
|
|---|
| 23503 |
|
|---|
| 23504 | case Profiler:
|
|---|
| 23505 | var parentStateNode = parentFiber.stateNode;
|
|---|
| 23506 | parentStateNode.effectDuration += effectDuration;
|
|---|
| 23507 | break outer;
|
|---|
| 23508 | }
|
|---|
| 23509 |
|
|---|
| 23510 | parentFiber = parentFiber.return;
|
|---|
| 23511 | }
|
|---|
| 23512 | }
|
|---|
| 23513 | }
|
|---|
| 23514 |
|
|---|
| 23515 | break;
|
|---|
| 23516 | }
|
|---|
| 23517 |
|
|---|
| 23518 | case SuspenseComponent:
|
|---|
| 23519 | {
|
|---|
| 23520 | commitSuspenseHydrationCallbacks(finishedRoot, finishedWork);
|
|---|
| 23521 | break;
|
|---|
| 23522 | }
|
|---|
| 23523 |
|
|---|
| 23524 | case SuspenseListComponent:
|
|---|
| 23525 | case IncompleteClassComponent:
|
|---|
| 23526 | case ScopeComponent:
|
|---|
| 23527 | case OffscreenComponent:
|
|---|
| 23528 | case LegacyHiddenComponent:
|
|---|
| 23529 | case TracingMarkerComponent:
|
|---|
| 23530 | {
|
|---|
| 23531 | break;
|
|---|
| 23532 | }
|
|---|
| 23533 |
|
|---|
| 23534 | default:
|
|---|
| 23535 | throw new Error('This unit of work tag should not have side-effects. This error is ' + 'likely caused by a bug in React. Please file an issue.');
|
|---|
| 23536 | }
|
|---|
| 23537 | }
|
|---|
| 23538 |
|
|---|
| 23539 | if ( !offscreenSubtreeWasHidden) {
|
|---|
| 23540 | {
|
|---|
| 23541 | if (finishedWork.flags & Ref) {
|
|---|
| 23542 | commitAttachRef(finishedWork);
|
|---|
| 23543 | }
|
|---|
| 23544 | }
|
|---|
| 23545 | }
|
|---|
| 23546 | }
|
|---|
| 23547 |
|
|---|
| 23548 | function reappearLayoutEffectsOnFiber(node) {
|
|---|
| 23549 | // Turn on layout effects in a tree that previously disappeared.
|
|---|
| 23550 | // TODO (Offscreen) Check: flags & LayoutStatic
|
|---|
| 23551 | switch (node.tag) {
|
|---|
| 23552 | case FunctionComponent:
|
|---|
| 23553 | case ForwardRef:
|
|---|
| 23554 | case SimpleMemoComponent:
|
|---|
| 23555 | {
|
|---|
| 23556 | if ( node.mode & ProfileMode) {
|
|---|
| 23557 | try {
|
|---|
| 23558 | startLayoutEffectTimer();
|
|---|
| 23559 | safelyCallCommitHookLayoutEffectListMount(node, node.return);
|
|---|
| 23560 | } finally {
|
|---|
| 23561 | recordLayoutEffectDuration(node);
|
|---|
| 23562 | }
|
|---|
| 23563 | } else {
|
|---|
| 23564 | safelyCallCommitHookLayoutEffectListMount(node, node.return);
|
|---|
| 23565 | }
|
|---|
| 23566 |
|
|---|
| 23567 | break;
|
|---|
| 23568 | }
|
|---|
| 23569 |
|
|---|
| 23570 | case ClassComponent:
|
|---|
| 23571 | {
|
|---|
| 23572 | var instance = node.stateNode;
|
|---|
| 23573 |
|
|---|
| 23574 | if (typeof instance.componentDidMount === 'function') {
|
|---|
| 23575 | safelyCallComponentDidMount(node, node.return, instance);
|
|---|
| 23576 | }
|
|---|
| 23577 |
|
|---|
| 23578 | safelyAttachRef(node, node.return);
|
|---|
| 23579 | break;
|
|---|
| 23580 | }
|
|---|
| 23581 |
|
|---|
| 23582 | case HostComponent:
|
|---|
| 23583 | {
|
|---|
| 23584 | safelyAttachRef(node, node.return);
|
|---|
| 23585 | break;
|
|---|
| 23586 | }
|
|---|
| 23587 | }
|
|---|
| 23588 | }
|
|---|
| 23589 |
|
|---|
| 23590 | function hideOrUnhideAllChildren(finishedWork, isHidden) {
|
|---|
| 23591 | // Only hide or unhide the top-most host nodes.
|
|---|
| 23592 | var hostSubtreeRoot = null;
|
|---|
| 23593 |
|
|---|
| 23594 | {
|
|---|
| 23595 | // We only have the top Fiber that was inserted but we need to recurse down its
|
|---|
| 23596 | // children to find all the terminal nodes.
|
|---|
| 23597 | var node = finishedWork;
|
|---|
| 23598 |
|
|---|
| 23599 | while (true) {
|
|---|
| 23600 | if (node.tag === HostComponent) {
|
|---|
| 23601 | if (hostSubtreeRoot === null) {
|
|---|
| 23602 | hostSubtreeRoot = node;
|
|---|
| 23603 |
|
|---|
| 23604 | try {
|
|---|
| 23605 | var instance = node.stateNode;
|
|---|
| 23606 |
|
|---|
| 23607 | if (isHidden) {
|
|---|
| 23608 | hideInstance(instance);
|
|---|
| 23609 | } else {
|
|---|
| 23610 | unhideInstance(node.stateNode, node.memoizedProps);
|
|---|
| 23611 | }
|
|---|
| 23612 | } catch (error) {
|
|---|
| 23613 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 23614 | }
|
|---|
| 23615 | }
|
|---|
| 23616 | } else if (node.tag === HostText) {
|
|---|
| 23617 | if (hostSubtreeRoot === null) {
|
|---|
| 23618 | try {
|
|---|
| 23619 | var _instance3 = node.stateNode;
|
|---|
| 23620 |
|
|---|
| 23621 | if (isHidden) {
|
|---|
| 23622 | hideTextInstance(_instance3);
|
|---|
| 23623 | } else {
|
|---|
| 23624 | unhideTextInstance(_instance3, node.memoizedProps);
|
|---|
| 23625 | }
|
|---|
| 23626 | } catch (error) {
|
|---|
| 23627 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 23628 | }
|
|---|
| 23629 | }
|
|---|
| 23630 | } else if ((node.tag === OffscreenComponent || node.tag === LegacyHiddenComponent) && node.memoizedState !== null && node !== finishedWork) ; else if (node.child !== null) {
|
|---|
| 23631 | node.child.return = node;
|
|---|
| 23632 | node = node.child;
|
|---|
| 23633 | continue;
|
|---|
| 23634 | }
|
|---|
| 23635 |
|
|---|
| 23636 | if (node === finishedWork) {
|
|---|
| 23637 | return;
|
|---|
| 23638 | }
|
|---|
| 23639 |
|
|---|
| 23640 | while (node.sibling === null) {
|
|---|
| 23641 | if (node.return === null || node.return === finishedWork) {
|
|---|
| 23642 | return;
|
|---|
| 23643 | }
|
|---|
| 23644 |
|
|---|
| 23645 | if (hostSubtreeRoot === node) {
|
|---|
| 23646 | hostSubtreeRoot = null;
|
|---|
| 23647 | }
|
|---|
| 23648 |
|
|---|
| 23649 | node = node.return;
|
|---|
| 23650 | }
|
|---|
| 23651 |
|
|---|
| 23652 | if (hostSubtreeRoot === node) {
|
|---|
| 23653 | hostSubtreeRoot = null;
|
|---|
| 23654 | }
|
|---|
| 23655 |
|
|---|
| 23656 | node.sibling.return = node.return;
|
|---|
| 23657 | node = node.sibling;
|
|---|
| 23658 | }
|
|---|
| 23659 | }
|
|---|
| 23660 | }
|
|---|
| 23661 |
|
|---|
| 23662 | function commitAttachRef(finishedWork) {
|
|---|
| 23663 | var ref = finishedWork.ref;
|
|---|
| 23664 |
|
|---|
| 23665 | if (ref !== null) {
|
|---|
| 23666 | var instance = finishedWork.stateNode;
|
|---|
| 23667 | var instanceToUse;
|
|---|
| 23668 |
|
|---|
| 23669 | switch (finishedWork.tag) {
|
|---|
| 23670 | case HostComponent:
|
|---|
| 23671 | instanceToUse = getPublicInstance(instance);
|
|---|
| 23672 | break;
|
|---|
| 23673 |
|
|---|
| 23674 | default:
|
|---|
| 23675 | instanceToUse = instance;
|
|---|
| 23676 | } // Moved outside to ensure DCE works with this flag
|
|---|
| 23677 |
|
|---|
| 23678 | if (typeof ref === 'function') {
|
|---|
| 23679 | var retVal;
|
|---|
| 23680 |
|
|---|
| 23681 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 23682 | try {
|
|---|
| 23683 | startLayoutEffectTimer();
|
|---|
| 23684 | retVal = ref(instanceToUse);
|
|---|
| 23685 | } finally {
|
|---|
| 23686 | recordLayoutEffectDuration(finishedWork);
|
|---|
| 23687 | }
|
|---|
| 23688 | } else {
|
|---|
| 23689 | retVal = ref(instanceToUse);
|
|---|
| 23690 | }
|
|---|
| 23691 |
|
|---|
| 23692 | {
|
|---|
| 23693 | if (typeof retVal === 'function') {
|
|---|
| 23694 | error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(finishedWork));
|
|---|
| 23695 | }
|
|---|
| 23696 | }
|
|---|
| 23697 | } else {
|
|---|
| 23698 | {
|
|---|
| 23699 | if (!ref.hasOwnProperty('current')) {
|
|---|
| 23700 | error('Unexpected ref object provided for %s. ' + 'Use either a ref-setter function or React.createRef().', getComponentNameFromFiber(finishedWork));
|
|---|
| 23701 | }
|
|---|
| 23702 | }
|
|---|
| 23703 |
|
|---|
| 23704 | ref.current = instanceToUse;
|
|---|
| 23705 | }
|
|---|
| 23706 | }
|
|---|
| 23707 | }
|
|---|
| 23708 |
|
|---|
| 23709 | function detachFiberMutation(fiber) {
|
|---|
| 23710 | // Cut off the return pointer to disconnect it from the tree.
|
|---|
| 23711 | // This enables us to detect and warn against state updates on an unmounted component.
|
|---|
| 23712 | // It also prevents events from bubbling from within disconnected components.
|
|---|
| 23713 | //
|
|---|
| 23714 | // Ideally, we should also clear the child pointer of the parent alternate to let this
|
|---|
| 23715 | // get GC:ed but we don't know which for sure which parent is the current
|
|---|
| 23716 | // one so we'll settle for GC:ing the subtree of this child.
|
|---|
| 23717 | // This child itself will be GC:ed when the parent updates the next time.
|
|---|
| 23718 | //
|
|---|
| 23719 | // Note that we can't clear child or sibling pointers yet.
|
|---|
| 23720 | // They're needed for passive effects and for findDOMNode.
|
|---|
| 23721 | // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects).
|
|---|
| 23722 | //
|
|---|
| 23723 | // Don't reset the alternate yet, either. We need that so we can detach the
|
|---|
| 23724 | // alternate's fields in the passive phase. Clearing the return pointer is
|
|---|
| 23725 | // sufficient for findDOMNode semantics.
|
|---|
| 23726 | var alternate = fiber.alternate;
|
|---|
| 23727 |
|
|---|
| 23728 | if (alternate !== null) {
|
|---|
| 23729 | alternate.return = null;
|
|---|
| 23730 | }
|
|---|
| 23731 |
|
|---|
| 23732 | fiber.return = null;
|
|---|
| 23733 | }
|
|---|
| 23734 |
|
|---|
| 23735 | function detachFiberAfterEffects(fiber) {
|
|---|
| 23736 | var alternate = fiber.alternate;
|
|---|
| 23737 |
|
|---|
| 23738 | if (alternate !== null) {
|
|---|
| 23739 | fiber.alternate = null;
|
|---|
| 23740 | detachFiberAfterEffects(alternate);
|
|---|
| 23741 | } // Note: Defensively using negation instead of < in case
|
|---|
| 23742 | // `deletedTreeCleanUpLevel` is undefined.
|
|---|
| 23743 |
|
|---|
| 23744 |
|
|---|
| 23745 | {
|
|---|
| 23746 | // Clear cyclical Fiber fields. This level alone is designed to roughly
|
|---|
| 23747 | // approximate the planned Fiber refactor. In that world, `setState` will be
|
|---|
| 23748 | // bound to a special "instance" object instead of a Fiber. The Instance
|
|---|
| 23749 | // object will not have any of these fields. It will only be connected to
|
|---|
| 23750 | // the fiber tree via a single link at the root. So if this level alone is
|
|---|
| 23751 | // sufficient to fix memory issues, that bodes well for our plans.
|
|---|
| 23752 | fiber.child = null;
|
|---|
| 23753 | fiber.deletions = null;
|
|---|
| 23754 | fiber.sibling = null; // The `stateNode` is cyclical because on host nodes it points to the host
|
|---|
| 23755 | // tree, which has its own pointers to children, parents, and siblings.
|
|---|
| 23756 | // The other host nodes also point back to fibers, so we should detach that
|
|---|
| 23757 | // one, too.
|
|---|
| 23758 |
|
|---|
| 23759 | if (fiber.tag === HostComponent) {
|
|---|
| 23760 | var hostInstance = fiber.stateNode;
|
|---|
| 23761 |
|
|---|
| 23762 | if (hostInstance !== null) {
|
|---|
| 23763 | detachDeletedInstance(hostInstance);
|
|---|
| 23764 | }
|
|---|
| 23765 | }
|
|---|
| 23766 |
|
|---|
| 23767 | fiber.stateNode = null; // I'm intentionally not clearing the `return` field in this level. We
|
|---|
| 23768 | // already disconnect the `return` pointer at the root of the deleted
|
|---|
| 23769 | // subtree (in `detachFiberMutation`). Besides, `return` by itself is not
|
|---|
| 23770 | // cyclical — it's only cyclical when combined with `child`, `sibling`, and
|
|---|
| 23771 | // `alternate`. But we'll clear it in the next level anyway, just in case.
|
|---|
| 23772 |
|
|---|
| 23773 | {
|
|---|
| 23774 | fiber._debugOwner = null;
|
|---|
| 23775 | }
|
|---|
| 23776 |
|
|---|
| 23777 | {
|
|---|
| 23778 | // Theoretically, nothing in here should be necessary, because we already
|
|---|
| 23779 | // disconnected the fiber from the tree. So even if something leaks this
|
|---|
| 23780 | // particular fiber, it won't leak anything else
|
|---|
| 23781 | //
|
|---|
| 23782 | // The purpose of this branch is to be super aggressive so we can measure
|
|---|
| 23783 | // if there's any difference in memory impact. If there is, that could
|
|---|
| 23784 | // indicate a React leak we don't know about.
|
|---|
| 23785 | fiber.return = null;
|
|---|
| 23786 | fiber.dependencies = null;
|
|---|
| 23787 | fiber.memoizedProps = null;
|
|---|
| 23788 | fiber.memoizedState = null;
|
|---|
| 23789 | fiber.pendingProps = null;
|
|---|
| 23790 | fiber.stateNode = null; // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead.
|
|---|
| 23791 |
|
|---|
| 23792 | fiber.updateQueue = null;
|
|---|
| 23793 | }
|
|---|
| 23794 | }
|
|---|
| 23795 | }
|
|---|
| 23796 |
|
|---|
| 23797 | function getHostParentFiber(fiber) {
|
|---|
| 23798 | var parent = fiber.return;
|
|---|
| 23799 |
|
|---|
| 23800 | while (parent !== null) {
|
|---|
| 23801 | if (isHostParent(parent)) {
|
|---|
| 23802 | return parent;
|
|---|
| 23803 | }
|
|---|
| 23804 |
|
|---|
| 23805 | parent = parent.return;
|
|---|
| 23806 | }
|
|---|
| 23807 |
|
|---|
| 23808 | throw new Error('Expected to find a host parent. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
|---|
| 23809 | }
|
|---|
| 23810 |
|
|---|
| 23811 | function isHostParent(fiber) {
|
|---|
| 23812 | return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal;
|
|---|
| 23813 | }
|
|---|
| 23814 |
|
|---|
| 23815 | function getHostSibling(fiber) {
|
|---|
| 23816 | // We're going to search forward into the tree until we find a sibling host
|
|---|
| 23817 | // node. Unfortunately, if multiple insertions are done in a row we have to
|
|---|
| 23818 | // search past them. This leads to exponential search for the next sibling.
|
|---|
| 23819 | // TODO: Find a more efficient way to do this.
|
|---|
| 23820 | var node = fiber;
|
|---|
| 23821 |
|
|---|
| 23822 | siblings: while (true) {
|
|---|
| 23823 | // If we didn't find anything, let's try the next sibling.
|
|---|
| 23824 | while (node.sibling === null) {
|
|---|
| 23825 | if (node.return === null || isHostParent(node.return)) {
|
|---|
| 23826 | // If we pop out of the root or hit the parent the fiber we are the
|
|---|
| 23827 | // last sibling.
|
|---|
| 23828 | return null;
|
|---|
| 23829 | }
|
|---|
| 23830 |
|
|---|
| 23831 | node = node.return;
|
|---|
| 23832 | }
|
|---|
| 23833 |
|
|---|
| 23834 | node.sibling.return = node.return;
|
|---|
| 23835 | node = node.sibling;
|
|---|
| 23836 |
|
|---|
| 23837 | while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedFragment) {
|
|---|
| 23838 | // If it is not host node and, we might have a host node inside it.
|
|---|
| 23839 | // Try to search down until we find one.
|
|---|
| 23840 | if (node.flags & Placement) {
|
|---|
| 23841 | // If we don't have a child, try the siblings instead.
|
|---|
| 23842 | continue siblings;
|
|---|
| 23843 | } // If we don't have a child, try the siblings instead.
|
|---|
| 23844 | // We also skip portals because they are not part of this host tree.
|
|---|
| 23845 |
|
|---|
| 23846 |
|
|---|
| 23847 | if (node.child === null || node.tag === HostPortal) {
|
|---|
| 23848 | continue siblings;
|
|---|
| 23849 | } else {
|
|---|
| 23850 | node.child.return = node;
|
|---|
| 23851 | node = node.child;
|
|---|
| 23852 | }
|
|---|
| 23853 | } // Check if this host node is stable or about to be placed.
|
|---|
| 23854 |
|
|---|
| 23855 |
|
|---|
| 23856 | if (!(node.flags & Placement)) {
|
|---|
| 23857 | // Found it!
|
|---|
| 23858 | return node.stateNode;
|
|---|
| 23859 | }
|
|---|
| 23860 | }
|
|---|
| 23861 | }
|
|---|
| 23862 |
|
|---|
| 23863 | function commitPlacement(finishedWork) {
|
|---|
| 23864 |
|
|---|
| 23865 |
|
|---|
| 23866 | var parentFiber = getHostParentFiber(finishedWork); // Note: these two variables *must* always be updated together.
|
|---|
| 23867 |
|
|---|
| 23868 | switch (parentFiber.tag) {
|
|---|
| 23869 | case HostComponent:
|
|---|
| 23870 | {
|
|---|
| 23871 | var parent = parentFiber.stateNode;
|
|---|
| 23872 |
|
|---|
| 23873 | if (parentFiber.flags & ContentReset) {
|
|---|
| 23874 | // Reset the text content of the parent before doing any insertions
|
|---|
| 23875 | resetTextContent(parent); // Clear ContentReset from the effect tag
|
|---|
| 23876 |
|
|---|
| 23877 | parentFiber.flags &= ~ContentReset;
|
|---|
| 23878 | }
|
|---|
| 23879 |
|
|---|
| 23880 | var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its
|
|---|
| 23881 | // children to find all the terminal nodes.
|
|---|
| 23882 |
|
|---|
| 23883 | insertOrAppendPlacementNode(finishedWork, before, parent);
|
|---|
| 23884 | break;
|
|---|
| 23885 | }
|
|---|
| 23886 |
|
|---|
| 23887 | case HostRoot:
|
|---|
| 23888 | case HostPortal:
|
|---|
| 23889 | {
|
|---|
| 23890 | var _parent = parentFiber.stateNode.containerInfo;
|
|---|
| 23891 |
|
|---|
| 23892 | var _before = getHostSibling(finishedWork);
|
|---|
| 23893 |
|
|---|
| 23894 | insertOrAppendPlacementNodeIntoContainer(finishedWork, _before, _parent);
|
|---|
| 23895 | break;
|
|---|
| 23896 | }
|
|---|
| 23897 | // eslint-disable-next-line-no-fallthrough
|
|---|
| 23898 |
|
|---|
| 23899 | default:
|
|---|
| 23900 | throw new Error('Invalid host parent fiber. This error is likely caused by a bug ' + 'in React. Please file an issue.');
|
|---|
| 23901 | }
|
|---|
| 23902 | }
|
|---|
| 23903 |
|
|---|
| 23904 | function insertOrAppendPlacementNodeIntoContainer(node, before, parent) {
|
|---|
| 23905 | var tag = node.tag;
|
|---|
| 23906 | var isHost = tag === HostComponent || tag === HostText;
|
|---|
| 23907 |
|
|---|
| 23908 | if (isHost) {
|
|---|
| 23909 | var stateNode = node.stateNode;
|
|---|
| 23910 |
|
|---|
| 23911 | if (before) {
|
|---|
| 23912 | insertInContainerBefore(parent, stateNode, before);
|
|---|
| 23913 | } else {
|
|---|
| 23914 | appendChildToContainer(parent, stateNode);
|
|---|
| 23915 | }
|
|---|
| 23916 | } else if (tag === HostPortal) ; else {
|
|---|
| 23917 | var child = node.child;
|
|---|
| 23918 |
|
|---|
| 23919 | if (child !== null) {
|
|---|
| 23920 | insertOrAppendPlacementNodeIntoContainer(child, before, parent);
|
|---|
| 23921 | var sibling = child.sibling;
|
|---|
| 23922 |
|
|---|
| 23923 | while (sibling !== null) {
|
|---|
| 23924 | insertOrAppendPlacementNodeIntoContainer(sibling, before, parent);
|
|---|
| 23925 | sibling = sibling.sibling;
|
|---|
| 23926 | }
|
|---|
| 23927 | }
|
|---|
| 23928 | }
|
|---|
| 23929 | }
|
|---|
| 23930 |
|
|---|
| 23931 | function insertOrAppendPlacementNode(node, before, parent) {
|
|---|
| 23932 | var tag = node.tag;
|
|---|
| 23933 | var isHost = tag === HostComponent || tag === HostText;
|
|---|
| 23934 |
|
|---|
| 23935 | if (isHost) {
|
|---|
| 23936 | var stateNode = node.stateNode;
|
|---|
| 23937 |
|
|---|
| 23938 | if (before) {
|
|---|
| 23939 | insertBefore(parent, stateNode, before);
|
|---|
| 23940 | } else {
|
|---|
| 23941 | appendChild(parent, stateNode);
|
|---|
| 23942 | }
|
|---|
| 23943 | } else if (tag === HostPortal) ; else {
|
|---|
| 23944 | var child = node.child;
|
|---|
| 23945 |
|
|---|
| 23946 | if (child !== null) {
|
|---|
| 23947 | insertOrAppendPlacementNode(child, before, parent);
|
|---|
| 23948 | var sibling = child.sibling;
|
|---|
| 23949 |
|
|---|
| 23950 | while (sibling !== null) {
|
|---|
| 23951 | insertOrAppendPlacementNode(sibling, before, parent);
|
|---|
| 23952 | sibling = sibling.sibling;
|
|---|
| 23953 | }
|
|---|
| 23954 | }
|
|---|
| 23955 | }
|
|---|
| 23956 | } // These are tracked on the stack as we recursively traverse a
|
|---|
| 23957 | // deleted subtree.
|
|---|
| 23958 | // TODO: Update these during the whole mutation phase, not just during
|
|---|
| 23959 | // a deletion.
|
|---|
| 23960 |
|
|---|
| 23961 |
|
|---|
| 23962 | var hostParent = null;
|
|---|
| 23963 | var hostParentIsContainer = false;
|
|---|
| 23964 |
|
|---|
| 23965 | function commitDeletionEffects(root, returnFiber, deletedFiber) {
|
|---|
| 23966 | {
|
|---|
| 23967 | // We only have the top Fiber that was deleted but we need to recurse down its
|
|---|
| 23968 | // children to find all the terminal nodes.
|
|---|
| 23969 | // Recursively delete all host nodes from the parent, detach refs, clean
|
|---|
| 23970 | // up mounted layout effects, and call componentWillUnmount.
|
|---|
| 23971 | // We only need to remove the topmost host child in each branch. But then we
|
|---|
| 23972 | // still need to keep traversing to unmount effects, refs, and cWU. TODO: We
|
|---|
| 23973 | // could split this into two separate traversals functions, where the second
|
|---|
| 23974 | // one doesn't include any removeChild logic. This is maybe the same
|
|---|
| 23975 | // function as "disappearLayoutEffects" (or whatever that turns into after
|
|---|
| 23976 | // the layout phase is refactored to use recursion).
|
|---|
| 23977 | // Before starting, find the nearest host parent on the stack so we know
|
|---|
| 23978 | // which instance/container to remove the children from.
|
|---|
| 23979 | // TODO: Instead of searching up the fiber return path on every deletion, we
|
|---|
| 23980 | // can track the nearest host component on the JS stack as we traverse the
|
|---|
| 23981 | // tree during the commit phase. This would make insertions faster, too.
|
|---|
| 23982 | var parent = returnFiber;
|
|---|
| 23983 |
|
|---|
| 23984 | findParent: while (parent !== null) {
|
|---|
| 23985 | switch (parent.tag) {
|
|---|
| 23986 | case HostComponent:
|
|---|
| 23987 | {
|
|---|
| 23988 | hostParent = parent.stateNode;
|
|---|
| 23989 | hostParentIsContainer = false;
|
|---|
| 23990 | break findParent;
|
|---|
| 23991 | }
|
|---|
| 23992 |
|
|---|
| 23993 | case HostRoot:
|
|---|
| 23994 | {
|
|---|
| 23995 | hostParent = parent.stateNode.containerInfo;
|
|---|
| 23996 | hostParentIsContainer = true;
|
|---|
| 23997 | break findParent;
|
|---|
| 23998 | }
|
|---|
| 23999 |
|
|---|
| 24000 | case HostPortal:
|
|---|
| 24001 | {
|
|---|
| 24002 | hostParent = parent.stateNode.containerInfo;
|
|---|
| 24003 | hostParentIsContainer = true;
|
|---|
| 24004 | break findParent;
|
|---|
| 24005 | }
|
|---|
| 24006 | }
|
|---|
| 24007 |
|
|---|
| 24008 | parent = parent.return;
|
|---|
| 24009 | }
|
|---|
| 24010 |
|
|---|
| 24011 | if (hostParent === null) {
|
|---|
| 24012 | throw new Error('Expected to find a host parent. This error is likely caused by ' + 'a bug in React. Please file an issue.');
|
|---|
| 24013 | }
|
|---|
| 24014 |
|
|---|
| 24015 | commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber);
|
|---|
| 24016 | hostParent = null;
|
|---|
| 24017 | hostParentIsContainer = false;
|
|---|
| 24018 | }
|
|---|
| 24019 |
|
|---|
| 24020 | detachFiberMutation(deletedFiber);
|
|---|
| 24021 | }
|
|---|
| 24022 |
|
|---|
| 24023 | function recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, parent) {
|
|---|
| 24024 | // TODO: Use a static flag to skip trees that don't have unmount effects
|
|---|
| 24025 | var child = parent.child;
|
|---|
| 24026 |
|
|---|
| 24027 | while (child !== null) {
|
|---|
| 24028 | commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child);
|
|---|
| 24029 | child = child.sibling;
|
|---|
| 24030 | }
|
|---|
| 24031 | }
|
|---|
| 24032 |
|
|---|
| 24033 | function commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, deletedFiber) {
|
|---|
| 24034 | onCommitUnmount(deletedFiber); // The cases in this outer switch modify the stack before they traverse
|
|---|
| 24035 | // into their subtree. There are simpler cases in the inner switch
|
|---|
| 24036 | // that don't modify the stack.
|
|---|
| 24037 |
|
|---|
| 24038 | switch (deletedFiber.tag) {
|
|---|
| 24039 | case HostComponent:
|
|---|
| 24040 | {
|
|---|
| 24041 | if (!offscreenSubtreeWasHidden) {
|
|---|
| 24042 | safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|---|
| 24043 | } // Intentional fallthrough to next branch
|
|---|
| 24044 |
|
|---|
| 24045 | }
|
|---|
| 24046 | // eslint-disable-next-line-no-fallthrough
|
|---|
| 24047 |
|
|---|
| 24048 | case HostText:
|
|---|
| 24049 | {
|
|---|
| 24050 | // We only need to remove the nearest host child. Set the host parent
|
|---|
| 24051 | // to `null` on the stack to indicate that nested children don't
|
|---|
| 24052 | // need to be removed.
|
|---|
| 24053 | {
|
|---|
| 24054 | var prevHostParent = hostParent;
|
|---|
| 24055 | var prevHostParentIsContainer = hostParentIsContainer;
|
|---|
| 24056 | hostParent = null;
|
|---|
| 24057 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24058 | hostParent = prevHostParent;
|
|---|
| 24059 | hostParentIsContainer = prevHostParentIsContainer;
|
|---|
| 24060 |
|
|---|
| 24061 | if (hostParent !== null) {
|
|---|
| 24062 | // Now that all the child effects have unmounted, we can remove the
|
|---|
| 24063 | // node from the tree.
|
|---|
| 24064 | if (hostParentIsContainer) {
|
|---|
| 24065 | removeChildFromContainer(hostParent, deletedFiber.stateNode);
|
|---|
| 24066 | } else {
|
|---|
| 24067 | removeChild(hostParent, deletedFiber.stateNode);
|
|---|
| 24068 | }
|
|---|
| 24069 | }
|
|---|
| 24070 | }
|
|---|
| 24071 |
|
|---|
| 24072 | return;
|
|---|
| 24073 | }
|
|---|
| 24074 |
|
|---|
| 24075 | case DehydratedFragment:
|
|---|
| 24076 | {
|
|---|
| 24077 | // Delete the dehydrated suspense boundary and all of its content.
|
|---|
| 24078 |
|
|---|
| 24079 |
|
|---|
| 24080 | {
|
|---|
| 24081 | if (hostParent !== null) {
|
|---|
| 24082 | if (hostParentIsContainer) {
|
|---|
| 24083 | clearSuspenseBoundaryFromContainer(hostParent, deletedFiber.stateNode);
|
|---|
| 24084 | } else {
|
|---|
| 24085 | clearSuspenseBoundary(hostParent, deletedFiber.stateNode);
|
|---|
| 24086 | }
|
|---|
| 24087 | }
|
|---|
| 24088 | }
|
|---|
| 24089 |
|
|---|
| 24090 | return;
|
|---|
| 24091 | }
|
|---|
| 24092 |
|
|---|
| 24093 | case HostPortal:
|
|---|
| 24094 | {
|
|---|
| 24095 | {
|
|---|
| 24096 | // When we go into a portal, it becomes the parent to remove from.
|
|---|
| 24097 | var _prevHostParent = hostParent;
|
|---|
| 24098 | var _prevHostParentIsContainer = hostParentIsContainer;
|
|---|
| 24099 | hostParent = deletedFiber.stateNode.containerInfo;
|
|---|
| 24100 | hostParentIsContainer = true;
|
|---|
| 24101 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24102 | hostParent = _prevHostParent;
|
|---|
| 24103 | hostParentIsContainer = _prevHostParentIsContainer;
|
|---|
| 24104 | }
|
|---|
| 24105 |
|
|---|
| 24106 | return;
|
|---|
| 24107 | }
|
|---|
| 24108 |
|
|---|
| 24109 | case FunctionComponent:
|
|---|
| 24110 | case ForwardRef:
|
|---|
| 24111 | case MemoComponent:
|
|---|
| 24112 | case SimpleMemoComponent:
|
|---|
| 24113 | {
|
|---|
| 24114 | if (!offscreenSubtreeWasHidden) {
|
|---|
| 24115 | var updateQueue = deletedFiber.updateQueue;
|
|---|
| 24116 |
|
|---|
| 24117 | if (updateQueue !== null) {
|
|---|
| 24118 | var lastEffect = updateQueue.lastEffect;
|
|---|
| 24119 |
|
|---|
| 24120 | if (lastEffect !== null) {
|
|---|
| 24121 | var firstEffect = lastEffect.next;
|
|---|
| 24122 | var effect = firstEffect;
|
|---|
| 24123 |
|
|---|
| 24124 | do {
|
|---|
| 24125 | var _effect = effect,
|
|---|
| 24126 | destroy = _effect.destroy,
|
|---|
| 24127 | tag = _effect.tag;
|
|---|
| 24128 |
|
|---|
| 24129 | if (destroy !== undefined) {
|
|---|
| 24130 | if ((tag & Insertion) !== NoFlags$1) {
|
|---|
| 24131 | safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
|---|
| 24132 | } else if ((tag & Layout) !== NoFlags$1) {
|
|---|
| 24133 | {
|
|---|
| 24134 | markComponentLayoutEffectUnmountStarted(deletedFiber);
|
|---|
| 24135 | }
|
|---|
| 24136 |
|
|---|
| 24137 | if ( deletedFiber.mode & ProfileMode) {
|
|---|
| 24138 | startLayoutEffectTimer();
|
|---|
| 24139 | safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
|---|
| 24140 | recordLayoutEffectDuration(deletedFiber);
|
|---|
| 24141 | } else {
|
|---|
| 24142 | safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy);
|
|---|
| 24143 | }
|
|---|
| 24144 |
|
|---|
| 24145 | {
|
|---|
| 24146 | markComponentLayoutEffectUnmountStopped();
|
|---|
| 24147 | }
|
|---|
| 24148 | }
|
|---|
| 24149 | }
|
|---|
| 24150 |
|
|---|
| 24151 | effect = effect.next;
|
|---|
| 24152 | } while (effect !== firstEffect);
|
|---|
| 24153 | }
|
|---|
| 24154 | }
|
|---|
| 24155 | }
|
|---|
| 24156 |
|
|---|
| 24157 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24158 | return;
|
|---|
| 24159 | }
|
|---|
| 24160 |
|
|---|
| 24161 | case ClassComponent:
|
|---|
| 24162 | {
|
|---|
| 24163 | if (!offscreenSubtreeWasHidden) {
|
|---|
| 24164 | safelyDetachRef(deletedFiber, nearestMountedAncestor);
|
|---|
| 24165 | var instance = deletedFiber.stateNode;
|
|---|
| 24166 |
|
|---|
| 24167 | if (typeof instance.componentWillUnmount === 'function') {
|
|---|
| 24168 | safelyCallComponentWillUnmount(deletedFiber, nearestMountedAncestor, instance);
|
|---|
| 24169 | }
|
|---|
| 24170 | }
|
|---|
| 24171 |
|
|---|
| 24172 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24173 | return;
|
|---|
| 24174 | }
|
|---|
| 24175 |
|
|---|
| 24176 | case ScopeComponent:
|
|---|
| 24177 | {
|
|---|
| 24178 |
|
|---|
| 24179 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24180 | return;
|
|---|
| 24181 | }
|
|---|
| 24182 |
|
|---|
| 24183 | case OffscreenComponent:
|
|---|
| 24184 | {
|
|---|
| 24185 | if ( // TODO: Remove this dead flag
|
|---|
| 24186 | deletedFiber.mode & ConcurrentMode) {
|
|---|
| 24187 | // If this offscreen component is hidden, we already unmounted it. Before
|
|---|
| 24188 | // deleting the children, track that it's already unmounted so that we
|
|---|
| 24189 | // don't attempt to unmount the effects again.
|
|---|
| 24190 | // TODO: If the tree is hidden, in most cases we should be able to skip
|
|---|
| 24191 | // over the nested children entirely. An exception is we haven't yet found
|
|---|
| 24192 | // the topmost host node to delete, which we already track on the stack.
|
|---|
| 24193 | // But the other case is portals, which need to be detached no matter how
|
|---|
| 24194 | // deeply they are nested. We should use a subtree flag to track whether a
|
|---|
| 24195 | // subtree includes a nested portal.
|
|---|
| 24196 | var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
|---|
| 24197 | offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null;
|
|---|
| 24198 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24199 | offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|---|
| 24200 | } else {
|
|---|
| 24201 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24202 | }
|
|---|
| 24203 |
|
|---|
| 24204 | break;
|
|---|
| 24205 | }
|
|---|
| 24206 |
|
|---|
| 24207 | default:
|
|---|
| 24208 | {
|
|---|
| 24209 | recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber);
|
|---|
| 24210 | return;
|
|---|
| 24211 | }
|
|---|
| 24212 | }
|
|---|
| 24213 | }
|
|---|
| 24214 |
|
|---|
| 24215 | function commitSuspenseCallback(finishedWork) {
|
|---|
| 24216 | // TODO: Move this to passive phase
|
|---|
| 24217 | var newState = finishedWork.memoizedState;
|
|---|
| 24218 | }
|
|---|
| 24219 |
|
|---|
| 24220 | function commitSuspenseHydrationCallbacks(finishedRoot, finishedWork) {
|
|---|
| 24221 |
|
|---|
| 24222 | var newState = finishedWork.memoizedState;
|
|---|
| 24223 |
|
|---|
| 24224 | if (newState === null) {
|
|---|
| 24225 | var current = finishedWork.alternate;
|
|---|
| 24226 |
|
|---|
| 24227 | if (current !== null) {
|
|---|
| 24228 | var prevState = current.memoizedState;
|
|---|
| 24229 |
|
|---|
| 24230 | if (prevState !== null) {
|
|---|
| 24231 | var suspenseInstance = prevState.dehydrated;
|
|---|
| 24232 |
|
|---|
| 24233 | if (suspenseInstance !== null) {
|
|---|
| 24234 | commitHydratedSuspenseInstance(suspenseInstance);
|
|---|
| 24235 | }
|
|---|
| 24236 | }
|
|---|
| 24237 | }
|
|---|
| 24238 | }
|
|---|
| 24239 | }
|
|---|
| 24240 |
|
|---|
| 24241 | function attachSuspenseRetryListeners(finishedWork) {
|
|---|
| 24242 | // If this boundary just timed out, then it will have a set of wakeables.
|
|---|
| 24243 | // For each wakeable, attach a listener so that when it resolves, React
|
|---|
| 24244 | // attempts to re-render the boundary in the primary (pre-timeout) state.
|
|---|
| 24245 | var wakeables = finishedWork.updateQueue;
|
|---|
| 24246 |
|
|---|
| 24247 | if (wakeables !== null) {
|
|---|
| 24248 | finishedWork.updateQueue = null;
|
|---|
| 24249 | var retryCache = finishedWork.stateNode;
|
|---|
| 24250 |
|
|---|
| 24251 | if (retryCache === null) {
|
|---|
| 24252 | retryCache = finishedWork.stateNode = new PossiblyWeakSet();
|
|---|
| 24253 | }
|
|---|
| 24254 |
|
|---|
| 24255 | wakeables.forEach(function (wakeable) {
|
|---|
| 24256 | // Memoize using the boundary fiber to prevent redundant listeners.
|
|---|
| 24257 | var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable);
|
|---|
| 24258 |
|
|---|
| 24259 | if (!retryCache.has(wakeable)) {
|
|---|
| 24260 | retryCache.add(wakeable);
|
|---|
| 24261 |
|
|---|
| 24262 | {
|
|---|
| 24263 | if (isDevToolsPresent) {
|
|---|
| 24264 | if (inProgressLanes !== null && inProgressRoot !== null) {
|
|---|
| 24265 | // If we have pending work still, associate the original updaters with it.
|
|---|
| 24266 | restorePendingUpdaters(inProgressRoot, inProgressLanes);
|
|---|
| 24267 | } else {
|
|---|
| 24268 | throw Error('Expected finished root and lanes to be set. This is a bug in React.');
|
|---|
| 24269 | }
|
|---|
| 24270 | }
|
|---|
| 24271 | }
|
|---|
| 24272 |
|
|---|
| 24273 | wakeable.then(retry, retry);
|
|---|
| 24274 | }
|
|---|
| 24275 | });
|
|---|
| 24276 | }
|
|---|
| 24277 | } // This function detects when a Suspense boundary goes from visible to hidden.
|
|---|
| 24278 | function commitMutationEffects(root, finishedWork, committedLanes) {
|
|---|
| 24279 | inProgressLanes = committedLanes;
|
|---|
| 24280 | inProgressRoot = root;
|
|---|
| 24281 | setCurrentFiber(finishedWork);
|
|---|
| 24282 | commitMutationEffectsOnFiber(finishedWork, root);
|
|---|
| 24283 | setCurrentFiber(finishedWork);
|
|---|
| 24284 | inProgressLanes = null;
|
|---|
| 24285 | inProgressRoot = null;
|
|---|
| 24286 | }
|
|---|
| 24287 |
|
|---|
| 24288 | function recursivelyTraverseMutationEffects(root, parentFiber, lanes) {
|
|---|
| 24289 | // Deletions effects can be scheduled on any fiber type. They need to happen
|
|---|
| 24290 | // before the children effects hae fired.
|
|---|
| 24291 | var deletions = parentFiber.deletions;
|
|---|
| 24292 |
|
|---|
| 24293 | if (deletions !== null) {
|
|---|
| 24294 | for (var i = 0; i < deletions.length; i++) {
|
|---|
| 24295 | var childToDelete = deletions[i];
|
|---|
| 24296 |
|
|---|
| 24297 | try {
|
|---|
| 24298 | commitDeletionEffects(root, parentFiber, childToDelete);
|
|---|
| 24299 | } catch (error) {
|
|---|
| 24300 | captureCommitPhaseError(childToDelete, parentFiber, error);
|
|---|
| 24301 | }
|
|---|
| 24302 | }
|
|---|
| 24303 | }
|
|---|
| 24304 |
|
|---|
| 24305 | var prevDebugFiber = getCurrentFiber();
|
|---|
| 24306 |
|
|---|
| 24307 | if (parentFiber.subtreeFlags & MutationMask) {
|
|---|
| 24308 | var child = parentFiber.child;
|
|---|
| 24309 |
|
|---|
| 24310 | while (child !== null) {
|
|---|
| 24311 | setCurrentFiber(child);
|
|---|
| 24312 | commitMutationEffectsOnFiber(child, root);
|
|---|
| 24313 | child = child.sibling;
|
|---|
| 24314 | }
|
|---|
| 24315 | }
|
|---|
| 24316 |
|
|---|
| 24317 | setCurrentFiber(prevDebugFiber);
|
|---|
| 24318 | }
|
|---|
| 24319 |
|
|---|
| 24320 | function commitMutationEffectsOnFiber(finishedWork, root, lanes) {
|
|---|
| 24321 | var current = finishedWork.alternate;
|
|---|
| 24322 | var flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber,
|
|---|
| 24323 | // because the fiber tag is more specific. An exception is any flag related
|
|---|
| 24324 | // to reconcilation, because those can be set on all fiber types.
|
|---|
| 24325 |
|
|---|
| 24326 | switch (finishedWork.tag) {
|
|---|
| 24327 | case FunctionComponent:
|
|---|
| 24328 | case ForwardRef:
|
|---|
| 24329 | case MemoComponent:
|
|---|
| 24330 | case SimpleMemoComponent:
|
|---|
| 24331 | {
|
|---|
| 24332 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24333 | commitReconciliationEffects(finishedWork);
|
|---|
| 24334 |
|
|---|
| 24335 | if (flags & Update) {
|
|---|
| 24336 | try {
|
|---|
| 24337 | commitHookEffectListUnmount(Insertion | HasEffect, finishedWork, finishedWork.return);
|
|---|
| 24338 | commitHookEffectListMount(Insertion | HasEffect, finishedWork);
|
|---|
| 24339 | } catch (error) {
|
|---|
| 24340 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24341 | } // Layout effects are destroyed during the mutation phase so that all
|
|---|
| 24342 | // destroy functions for all fibers are called before any create functions.
|
|---|
| 24343 | // This prevents sibling component effects from interfering with each other,
|
|---|
| 24344 | // e.g. a destroy function in one component should never override a ref set
|
|---|
| 24345 | // by a create function in another component during the same commit.
|
|---|
| 24346 |
|
|---|
| 24347 |
|
|---|
| 24348 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 24349 | try {
|
|---|
| 24350 | startLayoutEffectTimer();
|
|---|
| 24351 | commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return);
|
|---|
| 24352 | } catch (error) {
|
|---|
| 24353 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24354 | }
|
|---|
| 24355 |
|
|---|
| 24356 | recordLayoutEffectDuration(finishedWork);
|
|---|
| 24357 | } else {
|
|---|
| 24358 | try {
|
|---|
| 24359 | commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return);
|
|---|
| 24360 | } catch (error) {
|
|---|
| 24361 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24362 | }
|
|---|
| 24363 | }
|
|---|
| 24364 | }
|
|---|
| 24365 |
|
|---|
| 24366 | return;
|
|---|
| 24367 | }
|
|---|
| 24368 |
|
|---|
| 24369 | case ClassComponent:
|
|---|
| 24370 | {
|
|---|
| 24371 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24372 | commitReconciliationEffects(finishedWork);
|
|---|
| 24373 |
|
|---|
| 24374 | if (flags & Ref) {
|
|---|
| 24375 | if (current !== null) {
|
|---|
| 24376 | safelyDetachRef(current, current.return);
|
|---|
| 24377 | }
|
|---|
| 24378 | }
|
|---|
| 24379 |
|
|---|
| 24380 | return;
|
|---|
| 24381 | }
|
|---|
| 24382 |
|
|---|
| 24383 | case HostComponent:
|
|---|
| 24384 | {
|
|---|
| 24385 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24386 | commitReconciliationEffects(finishedWork);
|
|---|
| 24387 |
|
|---|
| 24388 | if (flags & Ref) {
|
|---|
| 24389 | if (current !== null) {
|
|---|
| 24390 | safelyDetachRef(current, current.return);
|
|---|
| 24391 | }
|
|---|
| 24392 | }
|
|---|
| 24393 |
|
|---|
| 24394 | {
|
|---|
| 24395 | // TODO: ContentReset gets cleared by the children during the commit
|
|---|
| 24396 | // phase. This is a refactor hazard because it means we must read
|
|---|
| 24397 | // flags the flags after `commitReconciliationEffects` has already run;
|
|---|
| 24398 | // the order matters. We should refactor so that ContentReset does not
|
|---|
| 24399 | // rely on mutating the flag during commit. Like by setting a flag
|
|---|
| 24400 | // during the render phase instead.
|
|---|
| 24401 | if (finishedWork.flags & ContentReset) {
|
|---|
| 24402 | var instance = finishedWork.stateNode;
|
|---|
| 24403 |
|
|---|
| 24404 | try {
|
|---|
| 24405 | resetTextContent(instance);
|
|---|
| 24406 | } catch (error) {
|
|---|
| 24407 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24408 | }
|
|---|
| 24409 | }
|
|---|
| 24410 |
|
|---|
| 24411 | if (flags & Update) {
|
|---|
| 24412 | var _instance4 = finishedWork.stateNode;
|
|---|
| 24413 |
|
|---|
| 24414 | if (_instance4 != null) {
|
|---|
| 24415 | // Commit the work prepared earlier.
|
|---|
| 24416 | var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps
|
|---|
| 24417 | // as the newProps. The updatePayload will contain the real change in
|
|---|
| 24418 | // this case.
|
|---|
| 24419 |
|
|---|
| 24420 | var oldProps = current !== null ? current.memoizedProps : newProps;
|
|---|
| 24421 | var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components.
|
|---|
| 24422 |
|
|---|
| 24423 | var updatePayload = finishedWork.updateQueue;
|
|---|
| 24424 | finishedWork.updateQueue = null;
|
|---|
| 24425 |
|
|---|
| 24426 | if (updatePayload !== null) {
|
|---|
| 24427 | try {
|
|---|
| 24428 | commitUpdate(_instance4, updatePayload, type, oldProps, newProps, finishedWork);
|
|---|
| 24429 | } catch (error) {
|
|---|
| 24430 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24431 | }
|
|---|
| 24432 | }
|
|---|
| 24433 | }
|
|---|
| 24434 | }
|
|---|
| 24435 | }
|
|---|
| 24436 |
|
|---|
| 24437 | return;
|
|---|
| 24438 | }
|
|---|
| 24439 |
|
|---|
| 24440 | case HostText:
|
|---|
| 24441 | {
|
|---|
| 24442 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24443 | commitReconciliationEffects(finishedWork);
|
|---|
| 24444 |
|
|---|
| 24445 | if (flags & Update) {
|
|---|
| 24446 | {
|
|---|
| 24447 | if (finishedWork.stateNode === null) {
|
|---|
| 24448 | throw new Error('This should have a text node initialized. This error is likely ' + 'caused by a bug in React. Please file an issue.');
|
|---|
| 24449 | }
|
|---|
| 24450 |
|
|---|
| 24451 | var textInstance = finishedWork.stateNode;
|
|---|
| 24452 | var newText = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps
|
|---|
| 24453 | // as the newProps. The updatePayload will contain the real change in
|
|---|
| 24454 | // this case.
|
|---|
| 24455 |
|
|---|
| 24456 | var oldText = current !== null ? current.memoizedProps : newText;
|
|---|
| 24457 |
|
|---|
| 24458 | try {
|
|---|
| 24459 | commitTextUpdate(textInstance, oldText, newText);
|
|---|
| 24460 | } catch (error) {
|
|---|
| 24461 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24462 | }
|
|---|
| 24463 | }
|
|---|
| 24464 | }
|
|---|
| 24465 |
|
|---|
| 24466 | return;
|
|---|
| 24467 | }
|
|---|
| 24468 |
|
|---|
| 24469 | case HostRoot:
|
|---|
| 24470 | {
|
|---|
| 24471 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24472 | commitReconciliationEffects(finishedWork);
|
|---|
| 24473 |
|
|---|
| 24474 | if (flags & Update) {
|
|---|
| 24475 | {
|
|---|
| 24476 | if (current !== null) {
|
|---|
| 24477 | var prevRootState = current.memoizedState;
|
|---|
| 24478 |
|
|---|
| 24479 | if (prevRootState.isDehydrated) {
|
|---|
| 24480 | try {
|
|---|
| 24481 | commitHydratedContainer(root.containerInfo);
|
|---|
| 24482 | } catch (error) {
|
|---|
| 24483 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24484 | }
|
|---|
| 24485 | }
|
|---|
| 24486 | }
|
|---|
| 24487 | }
|
|---|
| 24488 | }
|
|---|
| 24489 |
|
|---|
| 24490 | return;
|
|---|
| 24491 | }
|
|---|
| 24492 |
|
|---|
| 24493 | case HostPortal:
|
|---|
| 24494 | {
|
|---|
| 24495 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24496 | commitReconciliationEffects(finishedWork);
|
|---|
| 24497 |
|
|---|
| 24498 | return;
|
|---|
| 24499 | }
|
|---|
| 24500 |
|
|---|
| 24501 | case SuspenseComponent:
|
|---|
| 24502 | {
|
|---|
| 24503 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24504 | commitReconciliationEffects(finishedWork);
|
|---|
| 24505 | var offscreenFiber = finishedWork.child;
|
|---|
| 24506 |
|
|---|
| 24507 | if (offscreenFiber.flags & Visibility) {
|
|---|
| 24508 | var offscreenInstance = offscreenFiber.stateNode;
|
|---|
| 24509 | var newState = offscreenFiber.memoizedState;
|
|---|
| 24510 | var isHidden = newState !== null; // Track the current state on the Offscreen instance so we can
|
|---|
| 24511 | // read it during an event
|
|---|
| 24512 |
|
|---|
| 24513 | offscreenInstance.isHidden = isHidden;
|
|---|
| 24514 |
|
|---|
| 24515 | if (isHidden) {
|
|---|
| 24516 | var wasHidden = offscreenFiber.alternate !== null && offscreenFiber.alternate.memoizedState !== null;
|
|---|
| 24517 |
|
|---|
| 24518 | if (!wasHidden) {
|
|---|
| 24519 | // TODO: Move to passive phase
|
|---|
| 24520 | markCommitTimeOfFallback();
|
|---|
| 24521 | }
|
|---|
| 24522 | }
|
|---|
| 24523 | }
|
|---|
| 24524 |
|
|---|
| 24525 | if (flags & Update) {
|
|---|
| 24526 | try {
|
|---|
| 24527 | commitSuspenseCallback(finishedWork);
|
|---|
| 24528 | } catch (error) {
|
|---|
| 24529 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24530 | }
|
|---|
| 24531 |
|
|---|
| 24532 | attachSuspenseRetryListeners(finishedWork);
|
|---|
| 24533 | }
|
|---|
| 24534 |
|
|---|
| 24535 | return;
|
|---|
| 24536 | }
|
|---|
| 24537 |
|
|---|
| 24538 | case OffscreenComponent:
|
|---|
| 24539 | {
|
|---|
| 24540 | var _wasHidden = current !== null && current.memoizedState !== null;
|
|---|
| 24541 |
|
|---|
| 24542 | if ( // TODO: Remove this dead flag
|
|---|
| 24543 | finishedWork.mode & ConcurrentMode) {
|
|---|
| 24544 | // Before committing the children, track on the stack whether this
|
|---|
| 24545 | // offscreen subtree was already hidden, so that we don't unmount the
|
|---|
| 24546 | // effects again.
|
|---|
| 24547 | var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden;
|
|---|
| 24548 | offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || _wasHidden;
|
|---|
| 24549 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24550 | offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|---|
| 24551 | } else {
|
|---|
| 24552 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24553 | }
|
|---|
| 24554 |
|
|---|
| 24555 | commitReconciliationEffects(finishedWork);
|
|---|
| 24556 |
|
|---|
| 24557 | if (flags & Visibility) {
|
|---|
| 24558 | var _offscreenInstance = finishedWork.stateNode;
|
|---|
| 24559 | var _newState = finishedWork.memoizedState;
|
|---|
| 24560 |
|
|---|
| 24561 | var _isHidden = _newState !== null;
|
|---|
| 24562 |
|
|---|
| 24563 | var offscreenBoundary = finishedWork; // Track the current state on the Offscreen instance so we can
|
|---|
| 24564 | // read it during an event
|
|---|
| 24565 |
|
|---|
| 24566 | _offscreenInstance.isHidden = _isHidden;
|
|---|
| 24567 |
|
|---|
| 24568 | {
|
|---|
| 24569 | if (_isHidden) {
|
|---|
| 24570 | if (!_wasHidden) {
|
|---|
| 24571 | if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) {
|
|---|
| 24572 | nextEffect = offscreenBoundary;
|
|---|
| 24573 | var offscreenChild = offscreenBoundary.child;
|
|---|
| 24574 |
|
|---|
| 24575 | while (offscreenChild !== null) {
|
|---|
| 24576 | nextEffect = offscreenChild;
|
|---|
| 24577 | disappearLayoutEffects_begin(offscreenChild);
|
|---|
| 24578 | offscreenChild = offscreenChild.sibling;
|
|---|
| 24579 | }
|
|---|
| 24580 | }
|
|---|
| 24581 | }
|
|---|
| 24582 | }
|
|---|
| 24583 | }
|
|---|
| 24584 |
|
|---|
| 24585 | {
|
|---|
| 24586 | // TODO: This needs to run whenever there's an insertion or update
|
|---|
| 24587 | // inside a hidden Offscreen tree.
|
|---|
| 24588 | hideOrUnhideAllChildren(offscreenBoundary, _isHidden);
|
|---|
| 24589 | }
|
|---|
| 24590 | }
|
|---|
| 24591 |
|
|---|
| 24592 | return;
|
|---|
| 24593 | }
|
|---|
| 24594 |
|
|---|
| 24595 | case SuspenseListComponent:
|
|---|
| 24596 | {
|
|---|
| 24597 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24598 | commitReconciliationEffects(finishedWork);
|
|---|
| 24599 |
|
|---|
| 24600 | if (flags & Update) {
|
|---|
| 24601 | attachSuspenseRetryListeners(finishedWork);
|
|---|
| 24602 | }
|
|---|
| 24603 |
|
|---|
| 24604 | return;
|
|---|
| 24605 | }
|
|---|
| 24606 |
|
|---|
| 24607 | case ScopeComponent:
|
|---|
| 24608 | {
|
|---|
| 24609 |
|
|---|
| 24610 | return;
|
|---|
| 24611 | }
|
|---|
| 24612 |
|
|---|
| 24613 | default:
|
|---|
| 24614 | {
|
|---|
| 24615 | recursivelyTraverseMutationEffects(root, finishedWork);
|
|---|
| 24616 | commitReconciliationEffects(finishedWork);
|
|---|
| 24617 | return;
|
|---|
| 24618 | }
|
|---|
| 24619 | }
|
|---|
| 24620 | }
|
|---|
| 24621 |
|
|---|
| 24622 | function commitReconciliationEffects(finishedWork) {
|
|---|
| 24623 | // Placement effects (insertions, reorders) can be scheduled on any fiber
|
|---|
| 24624 | // type. They needs to happen after the children effects have fired, but
|
|---|
| 24625 | // before the effects on this fiber have fired.
|
|---|
| 24626 | var flags = finishedWork.flags;
|
|---|
| 24627 |
|
|---|
| 24628 | if (flags & Placement) {
|
|---|
| 24629 | try {
|
|---|
| 24630 | commitPlacement(finishedWork);
|
|---|
| 24631 | } catch (error) {
|
|---|
| 24632 | captureCommitPhaseError(finishedWork, finishedWork.return, error);
|
|---|
| 24633 | } // Clear the "placement" from effect tag so that we know that this is
|
|---|
| 24634 | // inserted, before any life-cycles like componentDidMount gets called.
|
|---|
| 24635 | // TODO: findDOMNode doesn't rely on this any more but isMounted does
|
|---|
| 24636 | // and isMounted is deprecated anyway so we should be able to kill this.
|
|---|
| 24637 |
|
|---|
| 24638 |
|
|---|
| 24639 | finishedWork.flags &= ~Placement;
|
|---|
| 24640 | }
|
|---|
| 24641 |
|
|---|
| 24642 | if (flags & Hydrating) {
|
|---|
| 24643 | finishedWork.flags &= ~Hydrating;
|
|---|
| 24644 | }
|
|---|
| 24645 | }
|
|---|
| 24646 |
|
|---|
| 24647 | function commitLayoutEffects(finishedWork, root, committedLanes) {
|
|---|
| 24648 | inProgressLanes = committedLanes;
|
|---|
| 24649 | inProgressRoot = root;
|
|---|
| 24650 | nextEffect = finishedWork;
|
|---|
| 24651 | commitLayoutEffects_begin(finishedWork, root, committedLanes);
|
|---|
| 24652 | inProgressLanes = null;
|
|---|
| 24653 | inProgressRoot = null;
|
|---|
| 24654 | }
|
|---|
| 24655 |
|
|---|
| 24656 | function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) {
|
|---|
| 24657 | // Suspense layout effects semantics don't change for legacy roots.
|
|---|
| 24658 | var isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode;
|
|---|
| 24659 |
|
|---|
| 24660 | while (nextEffect !== null) {
|
|---|
| 24661 | var fiber = nextEffect;
|
|---|
| 24662 | var firstChild = fiber.child;
|
|---|
| 24663 |
|
|---|
| 24664 | if ( fiber.tag === OffscreenComponent && isModernRoot) {
|
|---|
| 24665 | // Keep track of the current Offscreen stack's state.
|
|---|
| 24666 | var isHidden = fiber.memoizedState !== null;
|
|---|
| 24667 | var newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden;
|
|---|
| 24668 |
|
|---|
| 24669 | if (newOffscreenSubtreeIsHidden) {
|
|---|
| 24670 | // The Offscreen tree is hidden. Skip over its layout effects.
|
|---|
| 24671 | commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
|---|
| 24672 | continue;
|
|---|
| 24673 | } else {
|
|---|
| 24674 | // TODO (Offscreen) Also check: subtreeFlags & LayoutMask
|
|---|
| 24675 | var current = fiber.alternate;
|
|---|
| 24676 | var wasHidden = current !== null && current.memoizedState !== null;
|
|---|
| 24677 | var newOffscreenSubtreeWasHidden = wasHidden || offscreenSubtreeWasHidden;
|
|---|
| 24678 | var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden;
|
|---|
| 24679 | var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden; // Traverse the Offscreen subtree with the current Offscreen as the root.
|
|---|
| 24680 |
|
|---|
| 24681 | offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden;
|
|---|
| 24682 | offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden;
|
|---|
| 24683 |
|
|---|
| 24684 | if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) {
|
|---|
| 24685 | // This is the root of a reappearing boundary. Turn its layout effects
|
|---|
| 24686 | // back on.
|
|---|
| 24687 | nextEffect = fiber;
|
|---|
| 24688 | reappearLayoutEffects_begin(fiber);
|
|---|
| 24689 | }
|
|---|
| 24690 |
|
|---|
| 24691 | var child = firstChild;
|
|---|
| 24692 |
|
|---|
| 24693 | while (child !== null) {
|
|---|
| 24694 | nextEffect = child;
|
|---|
| 24695 | commitLayoutEffects_begin(child, // New root; bubble back up to here and stop.
|
|---|
| 24696 | root, committedLanes);
|
|---|
| 24697 | child = child.sibling;
|
|---|
| 24698 | } // Restore Offscreen state and resume in our-progress traversal.
|
|---|
| 24699 |
|
|---|
| 24700 |
|
|---|
| 24701 | nextEffect = fiber;
|
|---|
| 24702 | offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden;
|
|---|
| 24703 | offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden;
|
|---|
| 24704 | commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
|---|
| 24705 | continue;
|
|---|
| 24706 | }
|
|---|
| 24707 | }
|
|---|
| 24708 |
|
|---|
| 24709 | if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) {
|
|---|
| 24710 | firstChild.return = fiber;
|
|---|
| 24711 | nextEffect = firstChild;
|
|---|
| 24712 | } else {
|
|---|
| 24713 | commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes);
|
|---|
| 24714 | }
|
|---|
| 24715 | }
|
|---|
| 24716 | }
|
|---|
| 24717 |
|
|---|
| 24718 | function commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes) {
|
|---|
| 24719 | while (nextEffect !== null) {
|
|---|
| 24720 | var fiber = nextEffect;
|
|---|
| 24721 |
|
|---|
| 24722 | if ((fiber.flags & LayoutMask) !== NoFlags) {
|
|---|
| 24723 | var current = fiber.alternate;
|
|---|
| 24724 | setCurrentFiber(fiber);
|
|---|
| 24725 |
|
|---|
| 24726 | try {
|
|---|
| 24727 | commitLayoutEffectOnFiber(root, current, fiber, committedLanes);
|
|---|
| 24728 | } catch (error) {
|
|---|
| 24729 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 24730 | }
|
|---|
| 24731 |
|
|---|
| 24732 | resetCurrentFiber();
|
|---|
| 24733 | }
|
|---|
| 24734 |
|
|---|
| 24735 | if (fiber === subtreeRoot) {
|
|---|
| 24736 | nextEffect = null;
|
|---|
| 24737 | return;
|
|---|
| 24738 | }
|
|---|
| 24739 |
|
|---|
| 24740 | var sibling = fiber.sibling;
|
|---|
| 24741 |
|
|---|
| 24742 | if (sibling !== null) {
|
|---|
| 24743 | sibling.return = fiber.return;
|
|---|
| 24744 | nextEffect = sibling;
|
|---|
| 24745 | return;
|
|---|
| 24746 | }
|
|---|
| 24747 |
|
|---|
| 24748 | nextEffect = fiber.return;
|
|---|
| 24749 | }
|
|---|
| 24750 | }
|
|---|
| 24751 |
|
|---|
| 24752 | function disappearLayoutEffects_begin(subtreeRoot) {
|
|---|
| 24753 | while (nextEffect !== null) {
|
|---|
| 24754 | var fiber = nextEffect;
|
|---|
| 24755 | var firstChild = fiber.child; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic)
|
|---|
| 24756 |
|
|---|
| 24757 | switch (fiber.tag) {
|
|---|
| 24758 | case FunctionComponent:
|
|---|
| 24759 | case ForwardRef:
|
|---|
| 24760 | case MemoComponent:
|
|---|
| 24761 | case SimpleMemoComponent:
|
|---|
| 24762 | {
|
|---|
| 24763 | if ( fiber.mode & ProfileMode) {
|
|---|
| 24764 | try {
|
|---|
| 24765 | startLayoutEffectTimer();
|
|---|
| 24766 | commitHookEffectListUnmount(Layout, fiber, fiber.return);
|
|---|
| 24767 | } finally {
|
|---|
| 24768 | recordLayoutEffectDuration(fiber);
|
|---|
| 24769 | }
|
|---|
| 24770 | } else {
|
|---|
| 24771 | commitHookEffectListUnmount(Layout, fiber, fiber.return);
|
|---|
| 24772 | }
|
|---|
| 24773 |
|
|---|
| 24774 | break;
|
|---|
| 24775 | }
|
|---|
| 24776 |
|
|---|
| 24777 | case ClassComponent:
|
|---|
| 24778 | {
|
|---|
| 24779 | // TODO (Offscreen) Check: flags & RefStatic
|
|---|
| 24780 | safelyDetachRef(fiber, fiber.return);
|
|---|
| 24781 | var instance = fiber.stateNode;
|
|---|
| 24782 |
|
|---|
| 24783 | if (typeof instance.componentWillUnmount === 'function') {
|
|---|
| 24784 | safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
|---|
| 24785 | }
|
|---|
| 24786 |
|
|---|
| 24787 | break;
|
|---|
| 24788 | }
|
|---|
| 24789 |
|
|---|
| 24790 | case HostComponent:
|
|---|
| 24791 | {
|
|---|
| 24792 | safelyDetachRef(fiber, fiber.return);
|
|---|
| 24793 | break;
|
|---|
| 24794 | }
|
|---|
| 24795 |
|
|---|
| 24796 | case OffscreenComponent:
|
|---|
| 24797 | {
|
|---|
| 24798 | // Check if this is a
|
|---|
| 24799 | var isHidden = fiber.memoizedState !== null;
|
|---|
| 24800 |
|
|---|
| 24801 | if (isHidden) {
|
|---|
| 24802 | // Nested Offscreen tree is already hidden. Don't disappear
|
|---|
| 24803 | // its effects.
|
|---|
| 24804 | disappearLayoutEffects_complete(subtreeRoot);
|
|---|
| 24805 | continue;
|
|---|
| 24806 | }
|
|---|
| 24807 |
|
|---|
| 24808 | break;
|
|---|
| 24809 | }
|
|---|
| 24810 | } // TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
|---|
| 24811 |
|
|---|
| 24812 |
|
|---|
| 24813 | if (firstChild !== null) {
|
|---|
| 24814 | firstChild.return = fiber;
|
|---|
| 24815 | nextEffect = firstChild;
|
|---|
| 24816 | } else {
|
|---|
| 24817 | disappearLayoutEffects_complete(subtreeRoot);
|
|---|
| 24818 | }
|
|---|
| 24819 | }
|
|---|
| 24820 | }
|
|---|
| 24821 |
|
|---|
| 24822 | function disappearLayoutEffects_complete(subtreeRoot) {
|
|---|
| 24823 | while (nextEffect !== null) {
|
|---|
| 24824 | var fiber = nextEffect;
|
|---|
| 24825 |
|
|---|
| 24826 | if (fiber === subtreeRoot) {
|
|---|
| 24827 | nextEffect = null;
|
|---|
| 24828 | return;
|
|---|
| 24829 | }
|
|---|
| 24830 |
|
|---|
| 24831 | var sibling = fiber.sibling;
|
|---|
| 24832 |
|
|---|
| 24833 | if (sibling !== null) {
|
|---|
| 24834 | sibling.return = fiber.return;
|
|---|
| 24835 | nextEffect = sibling;
|
|---|
| 24836 | return;
|
|---|
| 24837 | }
|
|---|
| 24838 |
|
|---|
| 24839 | nextEffect = fiber.return;
|
|---|
| 24840 | }
|
|---|
| 24841 | }
|
|---|
| 24842 |
|
|---|
| 24843 | function reappearLayoutEffects_begin(subtreeRoot) {
|
|---|
| 24844 | while (nextEffect !== null) {
|
|---|
| 24845 | var fiber = nextEffect;
|
|---|
| 24846 | var firstChild = fiber.child;
|
|---|
| 24847 |
|
|---|
| 24848 | if (fiber.tag === OffscreenComponent) {
|
|---|
| 24849 | var isHidden = fiber.memoizedState !== null;
|
|---|
| 24850 |
|
|---|
| 24851 | if (isHidden) {
|
|---|
| 24852 | // Nested Offscreen tree is still hidden. Don't re-appear its effects.
|
|---|
| 24853 | reappearLayoutEffects_complete(subtreeRoot);
|
|---|
| 24854 | continue;
|
|---|
| 24855 | }
|
|---|
| 24856 | } // TODO (Offscreen) Check: subtreeFlags & LayoutStatic
|
|---|
| 24857 |
|
|---|
| 24858 |
|
|---|
| 24859 | if (firstChild !== null) {
|
|---|
| 24860 | // This node may have been reused from a previous render, so we can't
|
|---|
| 24861 | // assume its return pointer is correct.
|
|---|
| 24862 | firstChild.return = fiber;
|
|---|
| 24863 | nextEffect = firstChild;
|
|---|
| 24864 | } else {
|
|---|
| 24865 | reappearLayoutEffects_complete(subtreeRoot);
|
|---|
| 24866 | }
|
|---|
| 24867 | }
|
|---|
| 24868 | }
|
|---|
| 24869 |
|
|---|
| 24870 | function reappearLayoutEffects_complete(subtreeRoot) {
|
|---|
| 24871 | while (nextEffect !== null) {
|
|---|
| 24872 | var fiber = nextEffect; // TODO (Offscreen) Check: flags & LayoutStatic
|
|---|
| 24873 |
|
|---|
| 24874 | setCurrentFiber(fiber);
|
|---|
| 24875 |
|
|---|
| 24876 | try {
|
|---|
| 24877 | reappearLayoutEffectsOnFiber(fiber);
|
|---|
| 24878 | } catch (error) {
|
|---|
| 24879 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 24880 | }
|
|---|
| 24881 |
|
|---|
| 24882 | resetCurrentFiber();
|
|---|
| 24883 |
|
|---|
| 24884 | if (fiber === subtreeRoot) {
|
|---|
| 24885 | nextEffect = null;
|
|---|
| 24886 | return;
|
|---|
| 24887 | }
|
|---|
| 24888 |
|
|---|
| 24889 | var sibling = fiber.sibling;
|
|---|
| 24890 |
|
|---|
| 24891 | if (sibling !== null) {
|
|---|
| 24892 | // This node may have been reused from a previous render, so we can't
|
|---|
| 24893 | // assume its return pointer is correct.
|
|---|
| 24894 | sibling.return = fiber.return;
|
|---|
| 24895 | nextEffect = sibling;
|
|---|
| 24896 | return;
|
|---|
| 24897 | }
|
|---|
| 24898 |
|
|---|
| 24899 | nextEffect = fiber.return;
|
|---|
| 24900 | }
|
|---|
| 24901 | }
|
|---|
| 24902 |
|
|---|
| 24903 | function commitPassiveMountEffects(root, finishedWork, committedLanes, committedTransitions) {
|
|---|
| 24904 | nextEffect = finishedWork;
|
|---|
| 24905 | commitPassiveMountEffects_begin(finishedWork, root, committedLanes, committedTransitions);
|
|---|
| 24906 | }
|
|---|
| 24907 |
|
|---|
| 24908 | function commitPassiveMountEffects_begin(subtreeRoot, root, committedLanes, committedTransitions) {
|
|---|
| 24909 | while (nextEffect !== null) {
|
|---|
| 24910 | var fiber = nextEffect;
|
|---|
| 24911 | var firstChild = fiber.child;
|
|---|
| 24912 |
|
|---|
| 24913 | if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) {
|
|---|
| 24914 | firstChild.return = fiber;
|
|---|
| 24915 | nextEffect = firstChild;
|
|---|
| 24916 | } else {
|
|---|
| 24917 | commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions);
|
|---|
| 24918 | }
|
|---|
| 24919 | }
|
|---|
| 24920 | }
|
|---|
| 24921 |
|
|---|
| 24922 | function commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions) {
|
|---|
| 24923 | while (nextEffect !== null) {
|
|---|
| 24924 | var fiber = nextEffect;
|
|---|
| 24925 |
|
|---|
| 24926 | if ((fiber.flags & Passive) !== NoFlags) {
|
|---|
| 24927 | setCurrentFiber(fiber);
|
|---|
| 24928 |
|
|---|
| 24929 | try {
|
|---|
| 24930 | commitPassiveMountOnFiber(root, fiber, committedLanes, committedTransitions);
|
|---|
| 24931 | } catch (error) {
|
|---|
| 24932 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 24933 | }
|
|---|
| 24934 |
|
|---|
| 24935 | resetCurrentFiber();
|
|---|
| 24936 | }
|
|---|
| 24937 |
|
|---|
| 24938 | if (fiber === subtreeRoot) {
|
|---|
| 24939 | nextEffect = null;
|
|---|
| 24940 | return;
|
|---|
| 24941 | }
|
|---|
| 24942 |
|
|---|
| 24943 | var sibling = fiber.sibling;
|
|---|
| 24944 |
|
|---|
| 24945 | if (sibling !== null) {
|
|---|
| 24946 | sibling.return = fiber.return;
|
|---|
| 24947 | nextEffect = sibling;
|
|---|
| 24948 | return;
|
|---|
| 24949 | }
|
|---|
| 24950 |
|
|---|
| 24951 | nextEffect = fiber.return;
|
|---|
| 24952 | }
|
|---|
| 24953 | }
|
|---|
| 24954 |
|
|---|
| 24955 | function commitPassiveMountOnFiber(finishedRoot, finishedWork, committedLanes, committedTransitions) {
|
|---|
| 24956 | switch (finishedWork.tag) {
|
|---|
| 24957 | case FunctionComponent:
|
|---|
| 24958 | case ForwardRef:
|
|---|
| 24959 | case SimpleMemoComponent:
|
|---|
| 24960 | {
|
|---|
| 24961 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 24962 | startPassiveEffectTimer();
|
|---|
| 24963 |
|
|---|
| 24964 | try {
|
|---|
| 24965 | commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);
|
|---|
| 24966 | } finally {
|
|---|
| 24967 | recordPassiveEffectDuration(finishedWork);
|
|---|
| 24968 | }
|
|---|
| 24969 | } else {
|
|---|
| 24970 | commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);
|
|---|
| 24971 | }
|
|---|
| 24972 |
|
|---|
| 24973 | break;
|
|---|
| 24974 | }
|
|---|
| 24975 | }
|
|---|
| 24976 | }
|
|---|
| 24977 |
|
|---|
| 24978 | function commitPassiveUnmountEffects(firstChild) {
|
|---|
| 24979 | nextEffect = firstChild;
|
|---|
| 24980 | commitPassiveUnmountEffects_begin();
|
|---|
| 24981 | }
|
|---|
| 24982 |
|
|---|
| 24983 | function commitPassiveUnmountEffects_begin() {
|
|---|
| 24984 | while (nextEffect !== null) {
|
|---|
| 24985 | var fiber = nextEffect;
|
|---|
| 24986 | var child = fiber.child;
|
|---|
| 24987 |
|
|---|
| 24988 | if ((nextEffect.flags & ChildDeletion) !== NoFlags) {
|
|---|
| 24989 | var deletions = fiber.deletions;
|
|---|
| 24990 |
|
|---|
| 24991 | if (deletions !== null) {
|
|---|
| 24992 | for (var i = 0; i < deletions.length; i++) {
|
|---|
| 24993 | var fiberToDelete = deletions[i];
|
|---|
| 24994 | nextEffect = fiberToDelete;
|
|---|
| 24995 | commitPassiveUnmountEffectsInsideOfDeletedTree_begin(fiberToDelete, fiber);
|
|---|
| 24996 | }
|
|---|
| 24997 |
|
|---|
| 24998 | {
|
|---|
| 24999 | // A fiber was deleted from this parent fiber, but it's still part of
|
|---|
| 25000 | // the previous (alternate) parent fiber's list of children. Because
|
|---|
| 25001 | // children are a linked list, an earlier sibling that's still alive
|
|---|
| 25002 | // will be connected to the deleted fiber via its `alternate`:
|
|---|
| 25003 | //
|
|---|
| 25004 | // live fiber
|
|---|
| 25005 | // --alternate--> previous live fiber
|
|---|
| 25006 | // --sibling--> deleted fiber
|
|---|
| 25007 | //
|
|---|
| 25008 | // We can't disconnect `alternate` on nodes that haven't been deleted
|
|---|
| 25009 | // yet, but we can disconnect the `sibling` and `child` pointers.
|
|---|
| 25010 | var previousFiber = fiber.alternate;
|
|---|
| 25011 |
|
|---|
| 25012 | if (previousFiber !== null) {
|
|---|
| 25013 | var detachedChild = previousFiber.child;
|
|---|
| 25014 |
|
|---|
| 25015 | if (detachedChild !== null) {
|
|---|
| 25016 | previousFiber.child = null;
|
|---|
| 25017 |
|
|---|
| 25018 | do {
|
|---|
| 25019 | var detachedSibling = detachedChild.sibling;
|
|---|
| 25020 | detachedChild.sibling = null;
|
|---|
| 25021 | detachedChild = detachedSibling;
|
|---|
| 25022 | } while (detachedChild !== null);
|
|---|
| 25023 | }
|
|---|
| 25024 | }
|
|---|
| 25025 | }
|
|---|
| 25026 |
|
|---|
| 25027 | nextEffect = fiber;
|
|---|
| 25028 | }
|
|---|
| 25029 | }
|
|---|
| 25030 |
|
|---|
| 25031 | if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) {
|
|---|
| 25032 | child.return = fiber;
|
|---|
| 25033 | nextEffect = child;
|
|---|
| 25034 | } else {
|
|---|
| 25035 | commitPassiveUnmountEffects_complete();
|
|---|
| 25036 | }
|
|---|
| 25037 | }
|
|---|
| 25038 | }
|
|---|
| 25039 |
|
|---|
| 25040 | function commitPassiveUnmountEffects_complete() {
|
|---|
| 25041 | while (nextEffect !== null) {
|
|---|
| 25042 | var fiber = nextEffect;
|
|---|
| 25043 |
|
|---|
| 25044 | if ((fiber.flags & Passive) !== NoFlags) {
|
|---|
| 25045 | setCurrentFiber(fiber);
|
|---|
| 25046 | commitPassiveUnmountOnFiber(fiber);
|
|---|
| 25047 | resetCurrentFiber();
|
|---|
| 25048 | }
|
|---|
| 25049 |
|
|---|
| 25050 | var sibling = fiber.sibling;
|
|---|
| 25051 |
|
|---|
| 25052 | if (sibling !== null) {
|
|---|
| 25053 | sibling.return = fiber.return;
|
|---|
| 25054 | nextEffect = sibling;
|
|---|
| 25055 | return;
|
|---|
| 25056 | }
|
|---|
| 25057 |
|
|---|
| 25058 | nextEffect = fiber.return;
|
|---|
| 25059 | }
|
|---|
| 25060 | }
|
|---|
| 25061 |
|
|---|
| 25062 | function commitPassiveUnmountOnFiber(finishedWork) {
|
|---|
| 25063 | switch (finishedWork.tag) {
|
|---|
| 25064 | case FunctionComponent:
|
|---|
| 25065 | case ForwardRef:
|
|---|
| 25066 | case SimpleMemoComponent:
|
|---|
| 25067 | {
|
|---|
| 25068 | if ( finishedWork.mode & ProfileMode) {
|
|---|
| 25069 | startPassiveEffectTimer();
|
|---|
| 25070 | commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return);
|
|---|
| 25071 | recordPassiveEffectDuration(finishedWork);
|
|---|
| 25072 | } else {
|
|---|
| 25073 | commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return);
|
|---|
| 25074 | }
|
|---|
| 25075 |
|
|---|
| 25076 | break;
|
|---|
| 25077 | }
|
|---|
| 25078 | }
|
|---|
| 25079 | }
|
|---|
| 25080 |
|
|---|
| 25081 | function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(deletedSubtreeRoot, nearestMountedAncestor) {
|
|---|
| 25082 | while (nextEffect !== null) {
|
|---|
| 25083 | var fiber = nextEffect; // Deletion effects fire in parent -> child order
|
|---|
| 25084 | // TODO: Check if fiber has a PassiveStatic flag
|
|---|
| 25085 |
|
|---|
| 25086 | setCurrentFiber(fiber);
|
|---|
| 25087 | commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor);
|
|---|
| 25088 | resetCurrentFiber();
|
|---|
| 25089 | var child = fiber.child; // TODO: Only traverse subtree if it has a PassiveStatic flag. (But, if we
|
|---|
| 25090 | // do this, still need to handle `deletedTreeCleanUpLevel` correctly.)
|
|---|
| 25091 |
|
|---|
| 25092 | if (child !== null) {
|
|---|
| 25093 | child.return = fiber;
|
|---|
| 25094 | nextEffect = child;
|
|---|
| 25095 | } else {
|
|---|
| 25096 | commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot);
|
|---|
| 25097 | }
|
|---|
| 25098 | }
|
|---|
| 25099 | }
|
|---|
| 25100 |
|
|---|
| 25101 | function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot) {
|
|---|
| 25102 | while (nextEffect !== null) {
|
|---|
| 25103 | var fiber = nextEffect;
|
|---|
| 25104 | var sibling = fiber.sibling;
|
|---|
| 25105 | var returnFiber = fiber.return;
|
|---|
| 25106 |
|
|---|
| 25107 | {
|
|---|
| 25108 | // Recursively traverse the entire deleted tree and clean up fiber fields.
|
|---|
| 25109 | // This is more aggressive than ideal, and the long term goal is to only
|
|---|
| 25110 | // have to detach the deleted tree at the root.
|
|---|
| 25111 | detachFiberAfterEffects(fiber);
|
|---|
| 25112 |
|
|---|
| 25113 | if (fiber === deletedSubtreeRoot) {
|
|---|
| 25114 | nextEffect = null;
|
|---|
| 25115 | return;
|
|---|
| 25116 | }
|
|---|
| 25117 | }
|
|---|
| 25118 |
|
|---|
| 25119 | if (sibling !== null) {
|
|---|
| 25120 | sibling.return = returnFiber;
|
|---|
| 25121 | nextEffect = sibling;
|
|---|
| 25122 | return;
|
|---|
| 25123 | }
|
|---|
| 25124 |
|
|---|
| 25125 | nextEffect = returnFiber;
|
|---|
| 25126 | }
|
|---|
| 25127 | }
|
|---|
| 25128 |
|
|---|
| 25129 | function commitPassiveUnmountInsideDeletedTreeOnFiber(current, nearestMountedAncestor) {
|
|---|
| 25130 | switch (current.tag) {
|
|---|
| 25131 | case FunctionComponent:
|
|---|
| 25132 | case ForwardRef:
|
|---|
| 25133 | case SimpleMemoComponent:
|
|---|
| 25134 | {
|
|---|
| 25135 | if ( current.mode & ProfileMode) {
|
|---|
| 25136 | startPassiveEffectTimer();
|
|---|
| 25137 | commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor);
|
|---|
| 25138 | recordPassiveEffectDuration(current);
|
|---|
| 25139 | } else {
|
|---|
| 25140 | commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor);
|
|---|
| 25141 | }
|
|---|
| 25142 |
|
|---|
| 25143 | break;
|
|---|
| 25144 | }
|
|---|
| 25145 | }
|
|---|
| 25146 | } // TODO: Reuse reappearLayoutEffects traversal here?
|
|---|
| 25147 |
|
|---|
| 25148 |
|
|---|
| 25149 | function invokeLayoutEffectMountInDEV(fiber) {
|
|---|
| 25150 | {
|
|---|
| 25151 | // We don't need to re-check StrictEffectsMode here.
|
|---|
| 25152 | // This function is only called if that check has already passed.
|
|---|
| 25153 | switch (fiber.tag) {
|
|---|
| 25154 | case FunctionComponent:
|
|---|
| 25155 | case ForwardRef:
|
|---|
| 25156 | case SimpleMemoComponent:
|
|---|
| 25157 | {
|
|---|
| 25158 | try {
|
|---|
| 25159 | commitHookEffectListMount(Layout | HasEffect, fiber);
|
|---|
| 25160 | } catch (error) {
|
|---|
| 25161 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 25162 | }
|
|---|
| 25163 |
|
|---|
| 25164 | break;
|
|---|
| 25165 | }
|
|---|
| 25166 |
|
|---|
| 25167 | case ClassComponent:
|
|---|
| 25168 | {
|
|---|
| 25169 | var instance = fiber.stateNode;
|
|---|
| 25170 |
|
|---|
| 25171 | try {
|
|---|
| 25172 | instance.componentDidMount();
|
|---|
| 25173 | } catch (error) {
|
|---|
| 25174 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 25175 | }
|
|---|
| 25176 |
|
|---|
| 25177 | break;
|
|---|
| 25178 | }
|
|---|
| 25179 | }
|
|---|
| 25180 | }
|
|---|
| 25181 | }
|
|---|
| 25182 |
|
|---|
| 25183 | function invokePassiveEffectMountInDEV(fiber) {
|
|---|
| 25184 | {
|
|---|
| 25185 | // We don't need to re-check StrictEffectsMode here.
|
|---|
| 25186 | // This function is only called if that check has already passed.
|
|---|
| 25187 | switch (fiber.tag) {
|
|---|
| 25188 | case FunctionComponent:
|
|---|
| 25189 | case ForwardRef:
|
|---|
| 25190 | case SimpleMemoComponent:
|
|---|
| 25191 | {
|
|---|
| 25192 | try {
|
|---|
| 25193 | commitHookEffectListMount(Passive$1 | HasEffect, fiber);
|
|---|
| 25194 | } catch (error) {
|
|---|
| 25195 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 25196 | }
|
|---|
| 25197 |
|
|---|
| 25198 | break;
|
|---|
| 25199 | }
|
|---|
| 25200 | }
|
|---|
| 25201 | }
|
|---|
| 25202 | }
|
|---|
| 25203 |
|
|---|
| 25204 | function invokeLayoutEffectUnmountInDEV(fiber) {
|
|---|
| 25205 | {
|
|---|
| 25206 | // We don't need to re-check StrictEffectsMode here.
|
|---|
| 25207 | // This function is only called if that check has already passed.
|
|---|
| 25208 | switch (fiber.tag) {
|
|---|
| 25209 | case FunctionComponent:
|
|---|
| 25210 | case ForwardRef:
|
|---|
| 25211 | case SimpleMemoComponent:
|
|---|
| 25212 | {
|
|---|
| 25213 | try {
|
|---|
| 25214 | commitHookEffectListUnmount(Layout | HasEffect, fiber, fiber.return);
|
|---|
| 25215 | } catch (error) {
|
|---|
| 25216 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 25217 | }
|
|---|
| 25218 |
|
|---|
| 25219 | break;
|
|---|
| 25220 | }
|
|---|
| 25221 |
|
|---|
| 25222 | case ClassComponent:
|
|---|
| 25223 | {
|
|---|
| 25224 | var instance = fiber.stateNode;
|
|---|
| 25225 |
|
|---|
| 25226 | if (typeof instance.componentWillUnmount === 'function') {
|
|---|
| 25227 | safelyCallComponentWillUnmount(fiber, fiber.return, instance);
|
|---|
| 25228 | }
|
|---|
| 25229 |
|
|---|
| 25230 | break;
|
|---|
| 25231 | }
|
|---|
| 25232 | }
|
|---|
| 25233 | }
|
|---|
| 25234 | }
|
|---|
| 25235 |
|
|---|
| 25236 | function invokePassiveEffectUnmountInDEV(fiber) {
|
|---|
| 25237 | {
|
|---|
| 25238 | // We don't need to re-check StrictEffectsMode here.
|
|---|
| 25239 | // This function is only called if that check has already passed.
|
|---|
| 25240 | switch (fiber.tag) {
|
|---|
| 25241 | case FunctionComponent:
|
|---|
| 25242 | case ForwardRef:
|
|---|
| 25243 | case SimpleMemoComponent:
|
|---|
| 25244 | {
|
|---|
| 25245 | try {
|
|---|
| 25246 | commitHookEffectListUnmount(Passive$1 | HasEffect, fiber, fiber.return);
|
|---|
| 25247 | } catch (error) {
|
|---|
| 25248 | captureCommitPhaseError(fiber, fiber.return, error);
|
|---|
| 25249 | }
|
|---|
| 25250 | }
|
|---|
| 25251 | }
|
|---|
| 25252 | }
|
|---|
| 25253 | }
|
|---|
| 25254 |
|
|---|
| 25255 | var COMPONENT_TYPE = 0;
|
|---|
| 25256 | var HAS_PSEUDO_CLASS_TYPE = 1;
|
|---|
| 25257 | var ROLE_TYPE = 2;
|
|---|
| 25258 | var TEST_NAME_TYPE = 3;
|
|---|
| 25259 | var TEXT_TYPE = 4;
|
|---|
| 25260 |
|
|---|
| 25261 | if (typeof Symbol === 'function' && Symbol.for) {
|
|---|
| 25262 | var symbolFor = Symbol.for;
|
|---|
| 25263 | COMPONENT_TYPE = symbolFor('selector.component');
|
|---|
| 25264 | HAS_PSEUDO_CLASS_TYPE = symbolFor('selector.has_pseudo_class');
|
|---|
| 25265 | ROLE_TYPE = symbolFor('selector.role');
|
|---|
| 25266 | TEST_NAME_TYPE = symbolFor('selector.test_id');
|
|---|
| 25267 | TEXT_TYPE = symbolFor('selector.text');
|
|---|
| 25268 | }
|
|---|
| 25269 | var commitHooks = [];
|
|---|
| 25270 | function onCommitRoot$1() {
|
|---|
| 25271 | {
|
|---|
| 25272 | commitHooks.forEach(function (commitHook) {
|
|---|
| 25273 | return commitHook();
|
|---|
| 25274 | });
|
|---|
| 25275 | }
|
|---|
| 25276 | }
|
|---|
| 25277 |
|
|---|
| 25278 | var ReactCurrentActQueue = ReactSharedInternals.ReactCurrentActQueue;
|
|---|
| 25279 | function isLegacyActEnvironment(fiber) {
|
|---|
| 25280 | {
|
|---|
| 25281 | // Legacy mode. We preserve the behavior of React 17's act. It assumes an
|
|---|
| 25282 | // act environment whenever `jest` is defined, but you can still turn off
|
|---|
| 25283 | // spurious warnings by setting IS_REACT_ACT_ENVIRONMENT explicitly
|
|---|
| 25284 | // to false.
|
|---|
| 25285 | var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
|
|---|
| 25286 | typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined; // $FlowExpectedError - Flow doesn't know about jest
|
|---|
| 25287 |
|
|---|
| 25288 | var jestIsDefined = typeof jest !== 'undefined';
|
|---|
| 25289 | return jestIsDefined && isReactActEnvironmentGlobal !== false;
|
|---|
| 25290 | }
|
|---|
| 25291 | }
|
|---|
| 25292 | function isConcurrentActEnvironment() {
|
|---|
| 25293 | {
|
|---|
| 25294 | var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global
|
|---|
| 25295 | typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined;
|
|---|
| 25296 |
|
|---|
| 25297 | if (!isReactActEnvironmentGlobal && ReactCurrentActQueue.current !== null) {
|
|---|
| 25298 | // TODO: Include link to relevant documentation page.
|
|---|
| 25299 | error('The current testing environment is not configured to support ' + 'act(...)');
|
|---|
| 25300 | }
|
|---|
| 25301 |
|
|---|
| 25302 | return isReactActEnvironmentGlobal;
|
|---|
| 25303 | }
|
|---|
| 25304 | }
|
|---|
| 25305 |
|
|---|
| 25306 | var ceil = Math.ceil;
|
|---|
| 25307 | var ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher,
|
|---|
| 25308 | ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner,
|
|---|
| 25309 | ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig,
|
|---|
| 25310 | ReactCurrentActQueue$1 = ReactSharedInternals.ReactCurrentActQueue;
|
|---|
| 25311 | var NoContext =
|
|---|
| 25312 | /* */
|
|---|
| 25313 | 0;
|
|---|
| 25314 | var BatchedContext =
|
|---|
| 25315 | /* */
|
|---|
| 25316 | 1;
|
|---|
| 25317 | var RenderContext =
|
|---|
| 25318 | /* */
|
|---|
| 25319 | 2;
|
|---|
| 25320 | var CommitContext =
|
|---|
| 25321 | /* */
|
|---|
| 25322 | 4;
|
|---|
| 25323 | var RootInProgress = 0;
|
|---|
| 25324 | var RootFatalErrored = 1;
|
|---|
| 25325 | var RootErrored = 2;
|
|---|
| 25326 | var RootSuspended = 3;
|
|---|
| 25327 | var RootSuspendedWithDelay = 4;
|
|---|
| 25328 | var RootCompleted = 5;
|
|---|
| 25329 | var RootDidNotComplete = 6; // Describes where we are in the React execution stack
|
|---|
| 25330 |
|
|---|
| 25331 | var executionContext = NoContext; // The root we're working on
|
|---|
| 25332 |
|
|---|
| 25333 | var workInProgressRoot = null; // The fiber we're working on
|
|---|
| 25334 |
|
|---|
| 25335 | var workInProgress = null; // The lanes we're rendering
|
|---|
| 25336 |
|
|---|
| 25337 | var workInProgressRootRenderLanes = NoLanes; // Stack that allows components to change the render lanes for its subtree
|
|---|
| 25338 | // This is a superset of the lanes we started working on at the root. The only
|
|---|
| 25339 | // case where it's different from `workInProgressRootRenderLanes` is when we
|
|---|
| 25340 | // enter a subtree that is hidden and needs to be unhidden: Suspense and
|
|---|
| 25341 | // Offscreen component.
|
|---|
| 25342 | //
|
|---|
| 25343 | // Most things in the work loop should deal with workInProgressRootRenderLanes.
|
|---|
| 25344 | // Most things in begin/complete phases should deal with subtreeRenderLanes.
|
|---|
| 25345 |
|
|---|
| 25346 | var subtreeRenderLanes = NoLanes;
|
|---|
| 25347 | var subtreeRenderLanesCursor = createCursor(NoLanes); // Whether to root completed, errored, suspended, etc.
|
|---|
| 25348 |
|
|---|
| 25349 | var workInProgressRootExitStatus = RootInProgress; // A fatal error, if one is thrown
|
|---|
| 25350 |
|
|---|
| 25351 | var workInProgressRootFatalError = null; // "Included" lanes refer to lanes that were worked on during this render. It's
|
|---|
| 25352 | // slightly different than `renderLanes` because `renderLanes` can change as you
|
|---|
| 25353 | // enter and exit an Offscreen tree. This value is the combination of all render
|
|---|
| 25354 | // lanes for the entire render phase.
|
|---|
| 25355 |
|
|---|
| 25356 | var workInProgressRootIncludedLanes = NoLanes; // The work left over by components that were visited during this render. Only
|
|---|
| 25357 | // includes unprocessed updates, not work in bailed out children.
|
|---|
| 25358 |
|
|---|
| 25359 | var workInProgressRootSkippedLanes = NoLanes; // Lanes that were updated (in an interleaved event) during this render.
|
|---|
| 25360 |
|
|---|
| 25361 | var workInProgressRootInterleavedUpdatedLanes = NoLanes; // Lanes that were updated during the render phase (*not* an interleaved event).
|
|---|
| 25362 |
|
|---|
| 25363 | var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during the render phase.
|
|---|
| 25364 |
|
|---|
| 25365 | var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI.
|
|---|
| 25366 | // We will log them once the tree commits.
|
|---|
| 25367 |
|
|---|
| 25368 | var workInProgressRootRecoverableErrors = null; // The most recent time we committed a fallback. This lets us ensure a train
|
|---|
| 25369 | // model where we don't commit new loading states in too quick succession.
|
|---|
| 25370 |
|
|---|
| 25371 | var globalMostRecentFallbackTime = 0;
|
|---|
| 25372 | var FALLBACK_THROTTLE_MS = 500; // The absolute time for when we should start giving up on rendering
|
|---|
| 25373 | // more and prefer CPU suspense heuristics instead.
|
|---|
| 25374 |
|
|---|
| 25375 | var workInProgressRootRenderTargetTime = Infinity; // How long a render is supposed to take before we start following CPU
|
|---|
| 25376 | // suspense heuristics and opt out of rendering more content.
|
|---|
| 25377 |
|
|---|
| 25378 | var RENDER_TIMEOUT_MS = 500;
|
|---|
| 25379 | var workInProgressTransitions = null;
|
|---|
| 25380 |
|
|---|
| 25381 | function resetRenderTimer() {
|
|---|
| 25382 | workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS;
|
|---|
| 25383 | }
|
|---|
| 25384 |
|
|---|
| 25385 | function getRenderTargetTime() {
|
|---|
| 25386 | return workInProgressRootRenderTargetTime;
|
|---|
| 25387 | }
|
|---|
| 25388 | var hasUncaughtError = false;
|
|---|
| 25389 | var firstUncaughtError = null;
|
|---|
| 25390 | var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfilerNestedUpdateScheduledHook is true;
|
|---|
| 25391 | var rootDoesHavePassiveEffects = false;
|
|---|
| 25392 | var rootWithPendingPassiveEffects = null;
|
|---|
| 25393 | var pendingPassiveEffectsLanes = NoLanes;
|
|---|
| 25394 | var pendingPassiveProfilerEffects = [];
|
|---|
| 25395 | var pendingPassiveTransitions = null; // Use these to prevent an infinite loop of nested updates
|
|---|
| 25396 |
|
|---|
| 25397 | var NESTED_UPDATE_LIMIT = 50;
|
|---|
| 25398 | var nestedUpdateCount = 0;
|
|---|
| 25399 | var rootWithNestedUpdates = null;
|
|---|
| 25400 | var isFlushingPassiveEffects = false;
|
|---|
| 25401 | var didScheduleUpdateDuringPassiveEffects = false;
|
|---|
| 25402 | var NESTED_PASSIVE_UPDATE_LIMIT = 50;
|
|---|
| 25403 | var nestedPassiveUpdateCount = 0;
|
|---|
| 25404 | var rootWithPassiveNestedUpdates = null; // If two updates are scheduled within the same event, we should treat their
|
|---|
| 25405 | // event times as simultaneous, even if the actual clock time has advanced
|
|---|
| 25406 | // between the first and second call.
|
|---|
| 25407 |
|
|---|
| 25408 | var currentEventTime = NoTimestamp;
|
|---|
| 25409 | var currentEventTransitionLane = NoLanes;
|
|---|
| 25410 | var isRunningInsertionEffect = false;
|
|---|
| 25411 | function getWorkInProgressRoot() {
|
|---|
| 25412 | return workInProgressRoot;
|
|---|
| 25413 | }
|
|---|
| 25414 | function requestEventTime() {
|
|---|
| 25415 | if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|---|
| 25416 | // We're inside React, so it's fine to read the actual time.
|
|---|
| 25417 | return now();
|
|---|
| 25418 | } // We're not inside React, so we may be in the middle of a browser event.
|
|---|
| 25419 |
|
|---|
| 25420 |
|
|---|
| 25421 | if (currentEventTime !== NoTimestamp) {
|
|---|
| 25422 | // Use the same start time for all updates until we enter React again.
|
|---|
| 25423 | return currentEventTime;
|
|---|
| 25424 | } // This is the first update since React yielded. Compute a new start time.
|
|---|
| 25425 |
|
|---|
| 25426 |
|
|---|
| 25427 | currentEventTime = now();
|
|---|
| 25428 | return currentEventTime;
|
|---|
| 25429 | }
|
|---|
| 25430 | function requestUpdateLane(fiber) {
|
|---|
| 25431 | // Special cases
|
|---|
| 25432 | var mode = fiber.mode;
|
|---|
| 25433 |
|
|---|
| 25434 | if ((mode & ConcurrentMode) === NoMode) {
|
|---|
| 25435 | return SyncLane;
|
|---|
| 25436 | } else if ( (executionContext & RenderContext) !== NoContext && workInProgressRootRenderLanes !== NoLanes) {
|
|---|
| 25437 | // This is a render phase update. These are not officially supported. The
|
|---|
| 25438 | // old behavior is to give this the same "thread" (lanes) as
|
|---|
| 25439 | // whatever is currently rendering. So if you call `setState` on a component
|
|---|
| 25440 | // that happens later in the same render, it will flush. Ideally, we want to
|
|---|
| 25441 | // remove the special case and treat them as if they came from an
|
|---|
| 25442 | // interleaved event. Regardless, this pattern is not officially supported.
|
|---|
| 25443 | // This behavior is only a fallback. The flag only exists until we can roll
|
|---|
| 25444 | // out the setState warning, since existing code might accidentally rely on
|
|---|
| 25445 | // the current behavior.
|
|---|
| 25446 | return pickArbitraryLane(workInProgressRootRenderLanes);
|
|---|
| 25447 | }
|
|---|
| 25448 |
|
|---|
| 25449 | var isTransition = requestCurrentTransition() !== NoTransition;
|
|---|
| 25450 |
|
|---|
| 25451 | if (isTransition) {
|
|---|
| 25452 | if ( ReactCurrentBatchConfig$3.transition !== null) {
|
|---|
| 25453 | var transition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 25454 |
|
|---|
| 25455 | if (!transition._updatedFibers) {
|
|---|
| 25456 | transition._updatedFibers = new Set();
|
|---|
| 25457 | }
|
|---|
| 25458 |
|
|---|
| 25459 | transition._updatedFibers.add(fiber);
|
|---|
| 25460 | } // The algorithm for assigning an update to a lane should be stable for all
|
|---|
| 25461 | // updates at the same priority within the same event. To do this, the
|
|---|
| 25462 | // inputs to the algorithm must be the same.
|
|---|
| 25463 | //
|
|---|
| 25464 | // The trick we use is to cache the first of each of these inputs within an
|
|---|
| 25465 | // event. Then reset the cached values once we can be sure the event is
|
|---|
| 25466 | // over. Our heuristic for that is whenever we enter a concurrent work loop.
|
|---|
| 25467 |
|
|---|
| 25468 |
|
|---|
| 25469 | if (currentEventTransitionLane === NoLane) {
|
|---|
| 25470 | // All transitions within the same event are assigned the same lane.
|
|---|
| 25471 | currentEventTransitionLane = claimNextTransitionLane();
|
|---|
| 25472 | }
|
|---|
| 25473 |
|
|---|
| 25474 | return currentEventTransitionLane;
|
|---|
| 25475 | } // Updates originating inside certain React methods, like flushSync, have
|
|---|
| 25476 | // their priority set by tracking it with a context variable.
|
|---|
| 25477 | //
|
|---|
| 25478 | // The opaque type returned by the host config is internally a lane, so we can
|
|---|
| 25479 | // use that directly.
|
|---|
| 25480 | // TODO: Move this type conversion to the event priority module.
|
|---|
| 25481 |
|
|---|
| 25482 |
|
|---|
| 25483 | var updateLane = getCurrentUpdatePriority();
|
|---|
| 25484 |
|
|---|
| 25485 | if (updateLane !== NoLane) {
|
|---|
| 25486 | return updateLane;
|
|---|
| 25487 | } // This update originated outside React. Ask the host environment for an
|
|---|
| 25488 | // appropriate priority, based on the type of event.
|
|---|
| 25489 | //
|
|---|
| 25490 | // The opaque type returned by the host config is internally a lane, so we can
|
|---|
| 25491 | // use that directly.
|
|---|
| 25492 | // TODO: Move this type conversion to the event priority module.
|
|---|
| 25493 |
|
|---|
| 25494 |
|
|---|
| 25495 | var eventLane = getCurrentEventPriority();
|
|---|
| 25496 | return eventLane;
|
|---|
| 25497 | }
|
|---|
| 25498 |
|
|---|
| 25499 | function requestRetryLane(fiber) {
|
|---|
| 25500 | // This is a fork of `requestUpdateLane` designed specifically for Suspense
|
|---|
| 25501 | // "retries" — a special update that attempts to flip a Suspense boundary
|
|---|
| 25502 | // from its placeholder state to its primary/resolved state.
|
|---|
| 25503 | // Special cases
|
|---|
| 25504 | var mode = fiber.mode;
|
|---|
| 25505 |
|
|---|
| 25506 | if ((mode & ConcurrentMode) === NoMode) {
|
|---|
| 25507 | return SyncLane;
|
|---|
| 25508 | }
|
|---|
| 25509 |
|
|---|
| 25510 | return claimNextRetryLane();
|
|---|
| 25511 | }
|
|---|
| 25512 |
|
|---|
| 25513 | function scheduleUpdateOnFiber(root, fiber, lane, eventTime) {
|
|---|
| 25514 | checkForNestedUpdates();
|
|---|
| 25515 |
|
|---|
| 25516 | {
|
|---|
| 25517 | if (isRunningInsertionEffect) {
|
|---|
| 25518 | error('useInsertionEffect must not schedule updates.');
|
|---|
| 25519 | }
|
|---|
| 25520 | }
|
|---|
| 25521 |
|
|---|
| 25522 | {
|
|---|
| 25523 | if (isFlushingPassiveEffects) {
|
|---|
| 25524 | didScheduleUpdateDuringPassiveEffects = true;
|
|---|
| 25525 | }
|
|---|
| 25526 | } // Mark that the root has a pending update.
|
|---|
| 25527 |
|
|---|
| 25528 |
|
|---|
| 25529 | markRootUpdated(root, lane, eventTime);
|
|---|
| 25530 |
|
|---|
| 25531 | if ((executionContext & RenderContext) !== NoLanes && root === workInProgressRoot) {
|
|---|
| 25532 | // This update was dispatched during the render phase. This is a mistake
|
|---|
| 25533 | // if the update originates from user space (with the exception of local
|
|---|
| 25534 | // hook updates, which are handled differently and don't reach this
|
|---|
| 25535 | // function), but there are some internal React features that use this as
|
|---|
| 25536 | // an implementation detail, like selective hydration.
|
|---|
| 25537 | warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase
|
|---|
| 25538 | } else {
|
|---|
| 25539 | // This is a normal update, scheduled from outside the render phase. For
|
|---|
| 25540 | // example, during an input event.
|
|---|
| 25541 | {
|
|---|
| 25542 | if (isDevToolsPresent) {
|
|---|
| 25543 | addFiberToLanesMap(root, fiber, lane);
|
|---|
| 25544 | }
|
|---|
| 25545 | }
|
|---|
| 25546 |
|
|---|
| 25547 | warnIfUpdatesNotWrappedWithActDEV(fiber);
|
|---|
| 25548 |
|
|---|
| 25549 | if (root === workInProgressRoot) {
|
|---|
| 25550 | // Received an update to a tree that's in the middle of rendering. Mark
|
|---|
| 25551 | // that there was an interleaved update work on this root. Unless the
|
|---|
| 25552 | // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render
|
|---|
| 25553 | // phase update. In that case, we don't treat render phase updates as if
|
|---|
| 25554 | // they were interleaved, for backwards compat reasons.
|
|---|
| 25555 | if ( (executionContext & RenderContext) === NoContext) {
|
|---|
| 25556 | workInProgressRootInterleavedUpdatedLanes = mergeLanes(workInProgressRootInterleavedUpdatedLanes, lane);
|
|---|
| 25557 | }
|
|---|
| 25558 |
|
|---|
| 25559 | if (workInProgressRootExitStatus === RootSuspendedWithDelay) {
|
|---|
| 25560 | // The root already suspended with a delay, which means this render
|
|---|
| 25561 | // definitely won't finish. Since we have a new update, let's mark it as
|
|---|
| 25562 | // suspended now, right before marking the incoming update. This has the
|
|---|
| 25563 | // effect of interrupting the current render and switching to the update.
|
|---|
| 25564 | // TODO: Make sure this doesn't override pings that happen while we've
|
|---|
| 25565 | // already started rendering.
|
|---|
| 25566 | markRootSuspended$1(root, workInProgressRootRenderLanes);
|
|---|
| 25567 | }
|
|---|
| 25568 | }
|
|---|
| 25569 |
|
|---|
| 25570 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 25571 |
|
|---|
| 25572 | if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
|
|---|
| 25573 | !( ReactCurrentActQueue$1.isBatchingLegacy)) {
|
|---|
| 25574 | // Flush the synchronous work now, unless we're already working or inside
|
|---|
| 25575 | // a batch. This is intentionally inside scheduleUpdateOnFiber instead of
|
|---|
| 25576 | // scheduleCallbackForFiber to preserve the ability to schedule a callback
|
|---|
| 25577 | // without immediately flushing it. We only do this for user-initiated
|
|---|
| 25578 | // updates, to preserve historical behavior of legacy mode.
|
|---|
| 25579 | resetRenderTimer();
|
|---|
| 25580 | flushSyncCallbacksOnlyInLegacyMode();
|
|---|
| 25581 | }
|
|---|
| 25582 | }
|
|---|
| 25583 | }
|
|---|
| 25584 | function scheduleInitialHydrationOnRoot(root, lane, eventTime) {
|
|---|
| 25585 | // This is a special fork of scheduleUpdateOnFiber that is only used to
|
|---|
| 25586 | // schedule the initial hydration of a root that has just been created. Most
|
|---|
| 25587 | // of the stuff in scheduleUpdateOnFiber can be skipped.
|
|---|
| 25588 | //
|
|---|
| 25589 | // The main reason for this separate path, though, is to distinguish the
|
|---|
| 25590 | // initial children from subsequent updates. In fully client-rendered roots
|
|---|
| 25591 | // (createRoot instead of hydrateRoot), all top-level renders are modeled as
|
|---|
| 25592 | // updates, but hydration roots are special because the initial render must
|
|---|
| 25593 | // match what was rendered on the server.
|
|---|
| 25594 | var current = root.current;
|
|---|
| 25595 | current.lanes = lane;
|
|---|
| 25596 | markRootUpdated(root, lane, eventTime);
|
|---|
| 25597 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 25598 | }
|
|---|
| 25599 | function isUnsafeClassRenderPhaseUpdate(fiber) {
|
|---|
| 25600 | // Check if this is a render phase update. Only called by class components,
|
|---|
| 25601 | // which special (deprecated) behavior for UNSAFE_componentWillReceive props.
|
|---|
| 25602 | return (// TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We
|
|---|
| 25603 | // decided not to enable it.
|
|---|
| 25604 | (executionContext & RenderContext) !== NoContext
|
|---|
| 25605 | );
|
|---|
| 25606 | } // Use this function to schedule a task for a root. There's only one task per
|
|---|
| 25607 | // root; if a task was already scheduled, we'll check to make sure the priority
|
|---|
| 25608 | // of the existing task is the same as the priority of the next level that the
|
|---|
| 25609 | // root has work on. This function is called on every update, and right before
|
|---|
| 25610 | // exiting a task.
|
|---|
| 25611 |
|
|---|
| 25612 | function ensureRootIsScheduled(root, currentTime) {
|
|---|
| 25613 | var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as
|
|---|
| 25614 | // expired so we know to work on those next.
|
|---|
| 25615 |
|
|---|
| 25616 | markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority.
|
|---|
| 25617 |
|
|---|
| 25618 | var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);
|
|---|
| 25619 |
|
|---|
| 25620 | if (nextLanes === NoLanes) {
|
|---|
| 25621 | // Special case: There's nothing to work on.
|
|---|
| 25622 | if (existingCallbackNode !== null) {
|
|---|
| 25623 | cancelCallback$1(existingCallbackNode);
|
|---|
| 25624 | }
|
|---|
| 25625 |
|
|---|
| 25626 | root.callbackNode = null;
|
|---|
| 25627 | root.callbackPriority = NoLane;
|
|---|
| 25628 | return;
|
|---|
| 25629 | } // We use the highest priority lane to represent the priority of the callback.
|
|---|
| 25630 |
|
|---|
| 25631 |
|
|---|
| 25632 | var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it.
|
|---|
| 25633 |
|
|---|
| 25634 | var existingCallbackPriority = root.callbackPriority;
|
|---|
| 25635 |
|
|---|
| 25636 | if (existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a
|
|---|
| 25637 | // Scheduler task, rather than an `act` task, cancel it and re-scheduled
|
|---|
| 25638 | // on the `act` queue.
|
|---|
| 25639 | !( ReactCurrentActQueue$1.current !== null && existingCallbackNode !== fakeActCallbackNode)) {
|
|---|
| 25640 | {
|
|---|
| 25641 | // If we're going to re-use an existing task, it needs to exist.
|
|---|
| 25642 | // Assume that discrete update microtasks are non-cancellable and null.
|
|---|
| 25643 | // TODO: Temporary until we confirm this warning is not fired.
|
|---|
| 25644 | if (existingCallbackNode == null && existingCallbackPriority !== SyncLane) {
|
|---|
| 25645 | error('Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.');
|
|---|
| 25646 | }
|
|---|
| 25647 | } // The priority hasn't changed. We can reuse the existing task. Exit.
|
|---|
| 25648 |
|
|---|
| 25649 |
|
|---|
| 25650 | return;
|
|---|
| 25651 | }
|
|---|
| 25652 |
|
|---|
| 25653 | if (existingCallbackNode != null) {
|
|---|
| 25654 | // Cancel the existing callback. We'll schedule a new one below.
|
|---|
| 25655 | cancelCallback$1(existingCallbackNode);
|
|---|
| 25656 | } // Schedule a new callback.
|
|---|
| 25657 |
|
|---|
| 25658 |
|
|---|
| 25659 | var newCallbackNode;
|
|---|
| 25660 |
|
|---|
| 25661 | if (newCallbackPriority === SyncLane) {
|
|---|
| 25662 | // Special case: Sync React callbacks are scheduled on a special
|
|---|
| 25663 | // internal queue
|
|---|
| 25664 | if (root.tag === LegacyRoot) {
|
|---|
| 25665 | if ( ReactCurrentActQueue$1.isBatchingLegacy !== null) {
|
|---|
| 25666 | ReactCurrentActQueue$1.didScheduleLegacyUpdate = true;
|
|---|
| 25667 | }
|
|---|
| 25668 |
|
|---|
| 25669 | scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root));
|
|---|
| 25670 | } else {
|
|---|
| 25671 | scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
|
|---|
| 25672 | }
|
|---|
| 25673 |
|
|---|
| 25674 | {
|
|---|
| 25675 | // Flush the queue in a microtask.
|
|---|
| 25676 | if ( ReactCurrentActQueue$1.current !== null) {
|
|---|
| 25677 | // Inside `act`, use our internal `act` queue so that these get flushed
|
|---|
| 25678 | // at the end of the current scope even when using the sync version
|
|---|
| 25679 | // of `act`.
|
|---|
| 25680 | ReactCurrentActQueue$1.current.push(flushSyncCallbacks);
|
|---|
| 25681 | } else {
|
|---|
| 25682 | scheduleMicrotask(function () {
|
|---|
| 25683 | // In Safari, appending an iframe forces microtasks to run.
|
|---|
| 25684 | // https://github.com/facebook/react/issues/22459
|
|---|
| 25685 | // We don't support running callbacks in the middle of render
|
|---|
| 25686 | // or commit so we need to check against that.
|
|---|
| 25687 | if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
|---|
| 25688 | // Note that this would still prematurely flush the callbacks
|
|---|
| 25689 | // if this happens outside render or commit phase (e.g. in an event).
|
|---|
| 25690 | flushSyncCallbacks();
|
|---|
| 25691 | }
|
|---|
| 25692 | });
|
|---|
| 25693 | }
|
|---|
| 25694 | }
|
|---|
| 25695 |
|
|---|
| 25696 | newCallbackNode = null;
|
|---|
| 25697 | } else {
|
|---|
| 25698 | var schedulerPriorityLevel;
|
|---|
| 25699 |
|
|---|
| 25700 | switch (lanesToEventPriority(nextLanes)) {
|
|---|
| 25701 | case DiscreteEventPriority:
|
|---|
| 25702 | schedulerPriorityLevel = ImmediatePriority;
|
|---|
| 25703 | break;
|
|---|
| 25704 |
|
|---|
| 25705 | case ContinuousEventPriority:
|
|---|
| 25706 | schedulerPriorityLevel = UserBlockingPriority;
|
|---|
| 25707 | break;
|
|---|
| 25708 |
|
|---|
| 25709 | case DefaultEventPriority:
|
|---|
| 25710 | schedulerPriorityLevel = NormalPriority;
|
|---|
| 25711 | break;
|
|---|
| 25712 |
|
|---|
| 25713 | case IdleEventPriority:
|
|---|
| 25714 | schedulerPriorityLevel = IdlePriority;
|
|---|
| 25715 | break;
|
|---|
| 25716 |
|
|---|
| 25717 | default:
|
|---|
| 25718 | schedulerPriorityLevel = NormalPriority;
|
|---|
| 25719 | break;
|
|---|
| 25720 | }
|
|---|
| 25721 |
|
|---|
| 25722 | newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root));
|
|---|
| 25723 | }
|
|---|
| 25724 |
|
|---|
| 25725 | root.callbackPriority = newCallbackPriority;
|
|---|
| 25726 | root.callbackNode = newCallbackNode;
|
|---|
| 25727 | } // This is the entry point for every concurrent task, i.e. anything that
|
|---|
| 25728 | // goes through Scheduler.
|
|---|
| 25729 |
|
|---|
| 25730 |
|
|---|
| 25731 | function performConcurrentWorkOnRoot(root, didTimeout) {
|
|---|
| 25732 | {
|
|---|
| 25733 | resetNestedUpdateFlag();
|
|---|
| 25734 | } // Since we know we're in a React event, we can clear the current
|
|---|
| 25735 | // event time. The next update will compute a new event time.
|
|---|
| 25736 |
|
|---|
| 25737 |
|
|---|
| 25738 | currentEventTime = NoTimestamp;
|
|---|
| 25739 | currentEventTransitionLane = NoLanes;
|
|---|
| 25740 |
|
|---|
| 25741 | if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|---|
| 25742 | throw new Error('Should not already be working.');
|
|---|
| 25743 | } // Flush any pending passive effects before deciding which lanes to work on,
|
|---|
| 25744 | // in case they schedule additional work.
|
|---|
| 25745 |
|
|---|
| 25746 |
|
|---|
| 25747 | var originalCallbackNode = root.callbackNode;
|
|---|
| 25748 | var didFlushPassiveEffects = flushPassiveEffects();
|
|---|
| 25749 |
|
|---|
| 25750 | if (didFlushPassiveEffects) {
|
|---|
| 25751 | // Something in the passive effect phase may have canceled the current task.
|
|---|
| 25752 | // Check if the task node for this root was changed.
|
|---|
| 25753 | if (root.callbackNode !== originalCallbackNode) {
|
|---|
| 25754 | // The current task was canceled. Exit. We don't need to call
|
|---|
| 25755 | // `ensureRootIsScheduled` because the check above implies either that
|
|---|
| 25756 | // there's a new task, or that there's no remaining work on this root.
|
|---|
| 25757 | return null;
|
|---|
| 25758 | }
|
|---|
| 25759 | } // Determine the next lanes to work on, using the fields stored
|
|---|
| 25760 | // on the root.
|
|---|
| 25761 |
|
|---|
| 25762 |
|
|---|
| 25763 | var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes);
|
|---|
| 25764 |
|
|---|
| 25765 | if (lanes === NoLanes) {
|
|---|
| 25766 | // Defensive coding. This is never expected to happen.
|
|---|
| 25767 | return null;
|
|---|
| 25768 | } // We disable time-slicing in some cases: if the work has been CPU-bound
|
|---|
| 25769 | // for too long ("expired" work, to prevent starvation), or we're in
|
|---|
| 25770 | // sync-updates-by-default mode.
|
|---|
| 25771 | // TODO: We only check `didTimeout` defensively, to account for a Scheduler
|
|---|
| 25772 | // bug we're still investigating. Once the bug in Scheduler is fixed,
|
|---|
| 25773 | // we can remove this, since we track expiration ourselves.
|
|---|
| 25774 |
|
|---|
| 25775 |
|
|---|
| 25776 | var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && ( !didTimeout);
|
|---|
| 25777 | var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);
|
|---|
| 25778 |
|
|---|
| 25779 | if (exitStatus !== RootInProgress) {
|
|---|
| 25780 | if (exitStatus === RootErrored) {
|
|---|
| 25781 | // If something threw an error, try rendering one more time. We'll
|
|---|
| 25782 | // render synchronously to block concurrent data mutations, and we'll
|
|---|
| 25783 | // includes all pending updates are included. If it still fails after
|
|---|
| 25784 | // the second attempt, we'll give up and commit the resulting tree.
|
|---|
| 25785 | var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
|---|
| 25786 |
|
|---|
| 25787 | if (errorRetryLanes !== NoLanes) {
|
|---|
| 25788 | lanes = errorRetryLanes;
|
|---|
| 25789 | exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
|
|---|
| 25790 | }
|
|---|
| 25791 | }
|
|---|
| 25792 |
|
|---|
| 25793 | if (exitStatus === RootFatalErrored) {
|
|---|
| 25794 | var fatalError = workInProgressRootFatalError;
|
|---|
| 25795 | prepareFreshStack(root, NoLanes);
|
|---|
| 25796 | markRootSuspended$1(root, lanes);
|
|---|
| 25797 | ensureRootIsScheduled(root, now());
|
|---|
| 25798 | throw fatalError;
|
|---|
| 25799 | }
|
|---|
| 25800 |
|
|---|
| 25801 | if (exitStatus === RootDidNotComplete) {
|
|---|
| 25802 | // The render unwound without completing the tree. This happens in special
|
|---|
| 25803 | // cases where need to exit the current render without producing a
|
|---|
| 25804 | // consistent tree or committing.
|
|---|
| 25805 | //
|
|---|
| 25806 | // This should only happen during a concurrent render, not a discrete or
|
|---|
| 25807 | // synchronous update. We should have already checked for this when we
|
|---|
| 25808 | // unwound the stack.
|
|---|
| 25809 | markRootSuspended$1(root, lanes);
|
|---|
| 25810 | } else {
|
|---|
| 25811 | // The render completed.
|
|---|
| 25812 | // Check if this render may have yielded to a concurrent event, and if so,
|
|---|
| 25813 | // confirm that any newly rendered stores are consistent.
|
|---|
| 25814 | // TODO: It's possible that even a concurrent render may never have yielded
|
|---|
| 25815 | // to the main thread, if it was fast enough, or if it expired. We could
|
|---|
| 25816 | // skip the consistency check in that case, too.
|
|---|
| 25817 | var renderWasConcurrent = !includesBlockingLane(root, lanes);
|
|---|
| 25818 | var finishedWork = root.current.alternate;
|
|---|
| 25819 |
|
|---|
| 25820 | if (renderWasConcurrent && !isRenderConsistentWithExternalStores(finishedWork)) {
|
|---|
| 25821 | // A store was mutated in an interleaved event. Render again,
|
|---|
| 25822 | // synchronously, to block further mutations.
|
|---|
| 25823 | exitStatus = renderRootSync(root, lanes); // We need to check again if something threw
|
|---|
| 25824 |
|
|---|
| 25825 | if (exitStatus === RootErrored) {
|
|---|
| 25826 | var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
|---|
| 25827 |
|
|---|
| 25828 | if (_errorRetryLanes !== NoLanes) {
|
|---|
| 25829 | lanes = _errorRetryLanes;
|
|---|
| 25830 | exitStatus = recoverFromConcurrentError(root, _errorRetryLanes); // We assume the tree is now consistent because we didn't yield to any
|
|---|
| 25831 | // concurrent events.
|
|---|
| 25832 | }
|
|---|
| 25833 | }
|
|---|
| 25834 |
|
|---|
| 25835 | if (exitStatus === RootFatalErrored) {
|
|---|
| 25836 | var _fatalError = workInProgressRootFatalError;
|
|---|
| 25837 | prepareFreshStack(root, NoLanes);
|
|---|
| 25838 | markRootSuspended$1(root, lanes);
|
|---|
| 25839 | ensureRootIsScheduled(root, now());
|
|---|
| 25840 | throw _fatalError;
|
|---|
| 25841 | }
|
|---|
| 25842 | } // We now have a consistent tree. The next step is either to commit it,
|
|---|
| 25843 | // or, if something suspended, wait to commit it after a timeout.
|
|---|
| 25844 |
|
|---|
| 25845 |
|
|---|
| 25846 | root.finishedWork = finishedWork;
|
|---|
| 25847 | root.finishedLanes = lanes;
|
|---|
| 25848 | finishConcurrentRender(root, exitStatus, lanes);
|
|---|
| 25849 | }
|
|---|
| 25850 | }
|
|---|
| 25851 |
|
|---|
| 25852 | ensureRootIsScheduled(root, now());
|
|---|
| 25853 |
|
|---|
| 25854 | if (root.callbackNode === originalCallbackNode) {
|
|---|
| 25855 | // The task node scheduled for this root is the same one that's
|
|---|
| 25856 | // currently executed. Need to return a continuation.
|
|---|
| 25857 | return performConcurrentWorkOnRoot.bind(null, root);
|
|---|
| 25858 | }
|
|---|
| 25859 |
|
|---|
| 25860 | return null;
|
|---|
| 25861 | }
|
|---|
| 25862 |
|
|---|
| 25863 | function recoverFromConcurrentError(root, errorRetryLanes) {
|
|---|
| 25864 | // If an error occurred during hydration, discard server response and fall
|
|---|
| 25865 | // back to client side render.
|
|---|
| 25866 | // Before rendering again, save the errors from the previous attempt.
|
|---|
| 25867 | var errorsFromFirstAttempt = workInProgressRootConcurrentErrors;
|
|---|
| 25868 |
|
|---|
| 25869 | if (isRootDehydrated(root)) {
|
|---|
| 25870 | // The shell failed to hydrate. Set a flag to force a client rendering
|
|---|
| 25871 | // during the next attempt. To do this, we call prepareFreshStack now
|
|---|
| 25872 | // to create the root work-in-progress fiber. This is a bit weird in terms
|
|---|
| 25873 | // of factoring, because it relies on renderRootSync not calling
|
|---|
| 25874 | // prepareFreshStack again in the call below, which happens because the
|
|---|
| 25875 | // root and lanes haven't changed.
|
|---|
| 25876 | //
|
|---|
| 25877 | // TODO: I think what we should do is set ForceClientRender inside
|
|---|
| 25878 | // throwException, like we do for nested Suspense boundaries. The reason
|
|---|
| 25879 | // it's here instead is so we can switch to the synchronous work loop, too.
|
|---|
| 25880 | // Something to consider for a future refactor.
|
|---|
| 25881 | var rootWorkInProgress = prepareFreshStack(root, errorRetryLanes);
|
|---|
| 25882 | rootWorkInProgress.flags |= ForceClientRender;
|
|---|
| 25883 |
|
|---|
| 25884 | {
|
|---|
| 25885 | errorHydratingContainer(root.containerInfo);
|
|---|
| 25886 | }
|
|---|
| 25887 | }
|
|---|
| 25888 |
|
|---|
| 25889 | var exitStatus = renderRootSync(root, errorRetryLanes);
|
|---|
| 25890 |
|
|---|
| 25891 | if (exitStatus !== RootErrored) {
|
|---|
| 25892 | // Successfully finished rendering on retry
|
|---|
| 25893 | // The errors from the failed first attempt have been recovered. Add
|
|---|
| 25894 | // them to the collection of recoverable errors. We'll log them in the
|
|---|
| 25895 | // commit phase.
|
|---|
| 25896 | var errorsFromSecondAttempt = workInProgressRootRecoverableErrors;
|
|---|
| 25897 | workInProgressRootRecoverableErrors = errorsFromFirstAttempt; // The errors from the second attempt should be queued after the errors
|
|---|
| 25898 | // from the first attempt, to preserve the causal sequence.
|
|---|
| 25899 |
|
|---|
| 25900 | if (errorsFromSecondAttempt !== null) {
|
|---|
| 25901 | queueRecoverableErrors(errorsFromSecondAttempt);
|
|---|
| 25902 | }
|
|---|
| 25903 | }
|
|---|
| 25904 |
|
|---|
| 25905 | return exitStatus;
|
|---|
| 25906 | }
|
|---|
| 25907 |
|
|---|
| 25908 | function queueRecoverableErrors(errors) {
|
|---|
| 25909 | if (workInProgressRootRecoverableErrors === null) {
|
|---|
| 25910 | workInProgressRootRecoverableErrors = errors;
|
|---|
| 25911 | } else {
|
|---|
| 25912 | workInProgressRootRecoverableErrors.push.apply(workInProgressRootRecoverableErrors, errors);
|
|---|
| 25913 | }
|
|---|
| 25914 | }
|
|---|
| 25915 |
|
|---|
| 25916 | function finishConcurrentRender(root, exitStatus, lanes) {
|
|---|
| 25917 | switch (exitStatus) {
|
|---|
| 25918 | case RootInProgress:
|
|---|
| 25919 | case RootFatalErrored:
|
|---|
| 25920 | {
|
|---|
| 25921 | throw new Error('Root did not complete. This is a bug in React.');
|
|---|
| 25922 | }
|
|---|
| 25923 | // Flow knows about invariant, so it complains if I add a break
|
|---|
| 25924 | // statement, but eslint doesn't know about invariant, so it complains
|
|---|
| 25925 | // if I do. eslint-disable-next-line no-fallthrough
|
|---|
| 25926 |
|
|---|
| 25927 | case RootErrored:
|
|---|
| 25928 | {
|
|---|
| 25929 | // We should have already attempted to retry this tree. If we reached
|
|---|
| 25930 | // this point, it errored again. Commit it.
|
|---|
| 25931 | commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
|---|
| 25932 | break;
|
|---|
| 25933 | }
|
|---|
| 25934 |
|
|---|
| 25935 | case RootSuspended:
|
|---|
| 25936 | {
|
|---|
| 25937 | markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we
|
|---|
| 25938 | // should immediately commit it or wait a bit.
|
|---|
| 25939 |
|
|---|
| 25940 | if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope
|
|---|
| 25941 | !shouldForceFlushFallbacksInDEV()) {
|
|---|
| 25942 | // This render only included retries, no updates. Throttle committing
|
|---|
| 25943 | // retries so that we don't show too many loading states too quickly.
|
|---|
| 25944 | var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time.
|
|---|
| 25945 |
|
|---|
| 25946 | if (msUntilTimeout > 10) {
|
|---|
| 25947 | var nextLanes = getNextLanes(root, NoLanes);
|
|---|
| 25948 |
|
|---|
| 25949 | if (nextLanes !== NoLanes) {
|
|---|
| 25950 | // There's additional work on this root.
|
|---|
| 25951 | break;
|
|---|
| 25952 | }
|
|---|
| 25953 |
|
|---|
| 25954 | var suspendedLanes = root.suspendedLanes;
|
|---|
| 25955 |
|
|---|
| 25956 | if (!isSubsetOfLanes(suspendedLanes, lanes)) {
|
|---|
| 25957 | // We should prefer to render the fallback of at the last
|
|---|
| 25958 | // suspended level. Ping the last suspended level to try
|
|---|
| 25959 | // rendering it again.
|
|---|
| 25960 | // FIXME: What if the suspended lanes are Idle? Should not restart.
|
|---|
| 25961 | var eventTime = requestEventTime();
|
|---|
| 25962 | markRootPinged(root, suspendedLanes);
|
|---|
| 25963 | break;
|
|---|
| 25964 | } // The render is suspended, it hasn't timed out, and there's no
|
|---|
| 25965 | // lower priority work to do. Instead of committing the fallback
|
|---|
| 25966 | // immediately, wait for more data to arrive.
|
|---|
| 25967 |
|
|---|
| 25968 |
|
|---|
| 25969 | root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), msUntilTimeout);
|
|---|
| 25970 | break;
|
|---|
| 25971 | }
|
|---|
| 25972 | } // The work expired. Commit immediately.
|
|---|
| 25973 |
|
|---|
| 25974 |
|
|---|
| 25975 | commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
|---|
| 25976 | break;
|
|---|
| 25977 | }
|
|---|
| 25978 |
|
|---|
| 25979 | case RootSuspendedWithDelay:
|
|---|
| 25980 | {
|
|---|
| 25981 | markRootSuspended$1(root, lanes);
|
|---|
| 25982 |
|
|---|
| 25983 | if (includesOnlyTransitions(lanes)) {
|
|---|
| 25984 | // This is a transition, so we should exit without committing a
|
|---|
| 25985 | // placeholder and without scheduling a timeout. Delay indefinitely
|
|---|
| 25986 | // until we receive more data.
|
|---|
| 25987 | break;
|
|---|
| 25988 | }
|
|---|
| 25989 |
|
|---|
| 25990 | if (!shouldForceFlushFallbacksInDEV()) {
|
|---|
| 25991 | // This is not a transition, but we did trigger an avoided state.
|
|---|
| 25992 | // Schedule a placeholder to display after a short delay, using the Just
|
|---|
| 25993 | // Noticeable Difference.
|
|---|
| 25994 | // TODO: Is the JND optimization worth the added complexity? If this is
|
|---|
| 25995 | // the only reason we track the event time, then probably not.
|
|---|
| 25996 | // Consider removing.
|
|---|
| 25997 | var mostRecentEventTime = getMostRecentEventTime(root, lanes);
|
|---|
| 25998 | var eventTimeMs = mostRecentEventTime;
|
|---|
| 25999 | var timeElapsedMs = now() - eventTimeMs;
|
|---|
| 26000 |
|
|---|
| 26001 | var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time.
|
|---|
| 26002 |
|
|---|
| 26003 |
|
|---|
| 26004 | if (_msUntilTimeout > 10) {
|
|---|
| 26005 | // Instead of committing the fallback immediately, wait for more data
|
|---|
| 26006 | // to arrive.
|
|---|
| 26007 | root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), _msUntilTimeout);
|
|---|
| 26008 | break;
|
|---|
| 26009 | }
|
|---|
| 26010 | } // Commit the placeholder.
|
|---|
| 26011 |
|
|---|
| 26012 |
|
|---|
| 26013 | commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
|---|
| 26014 | break;
|
|---|
| 26015 | }
|
|---|
| 26016 |
|
|---|
| 26017 | case RootCompleted:
|
|---|
| 26018 | {
|
|---|
| 26019 | // The work completed. Ready to commit.
|
|---|
| 26020 | commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions);
|
|---|
| 26021 | break;
|
|---|
| 26022 | }
|
|---|
| 26023 |
|
|---|
| 26024 | default:
|
|---|
| 26025 | {
|
|---|
| 26026 | throw new Error('Unknown root exit status.');
|
|---|
| 26027 | }
|
|---|
| 26028 | }
|
|---|
| 26029 | }
|
|---|
| 26030 |
|
|---|
| 26031 | function isRenderConsistentWithExternalStores(finishedWork) {
|
|---|
| 26032 | // Search the rendered tree for external store reads, and check whether the
|
|---|
| 26033 | // stores were mutated in a concurrent event. Intentionally using an iterative
|
|---|
| 26034 | // loop instead of recursion so we can exit early.
|
|---|
| 26035 | var node = finishedWork;
|
|---|
| 26036 |
|
|---|
| 26037 | while (true) {
|
|---|
| 26038 | if (node.flags & StoreConsistency) {
|
|---|
| 26039 | var updateQueue = node.updateQueue;
|
|---|
| 26040 |
|
|---|
| 26041 | if (updateQueue !== null) {
|
|---|
| 26042 | var checks = updateQueue.stores;
|
|---|
| 26043 |
|
|---|
| 26044 | if (checks !== null) {
|
|---|
| 26045 | for (var i = 0; i < checks.length; i++) {
|
|---|
| 26046 | var check = checks[i];
|
|---|
| 26047 | var getSnapshot = check.getSnapshot;
|
|---|
| 26048 | var renderedValue = check.value;
|
|---|
| 26049 |
|
|---|
| 26050 | try {
|
|---|
| 26051 | if (!objectIs(getSnapshot(), renderedValue)) {
|
|---|
| 26052 | // Found an inconsistent store.
|
|---|
| 26053 | return false;
|
|---|
| 26054 | }
|
|---|
| 26055 | } catch (error) {
|
|---|
| 26056 | // If `getSnapshot` throws, return `false`. This will schedule
|
|---|
| 26057 | // a re-render, and the error will be rethrown during render.
|
|---|
| 26058 | return false;
|
|---|
| 26059 | }
|
|---|
| 26060 | }
|
|---|
| 26061 | }
|
|---|
| 26062 | }
|
|---|
| 26063 | }
|
|---|
| 26064 |
|
|---|
| 26065 | var child = node.child;
|
|---|
| 26066 |
|
|---|
| 26067 | if (node.subtreeFlags & StoreConsistency && child !== null) {
|
|---|
| 26068 | child.return = node;
|
|---|
| 26069 | node = child;
|
|---|
| 26070 | continue;
|
|---|
| 26071 | }
|
|---|
| 26072 |
|
|---|
| 26073 | if (node === finishedWork) {
|
|---|
| 26074 | return true;
|
|---|
| 26075 | }
|
|---|
| 26076 |
|
|---|
| 26077 | while (node.sibling === null) {
|
|---|
| 26078 | if (node.return === null || node.return === finishedWork) {
|
|---|
| 26079 | return true;
|
|---|
| 26080 | }
|
|---|
| 26081 |
|
|---|
| 26082 | node = node.return;
|
|---|
| 26083 | }
|
|---|
| 26084 |
|
|---|
| 26085 | node.sibling.return = node.return;
|
|---|
| 26086 | node = node.sibling;
|
|---|
| 26087 | } // Flow doesn't know this is unreachable, but eslint does
|
|---|
| 26088 | // eslint-disable-next-line no-unreachable
|
|---|
| 26089 |
|
|---|
| 26090 |
|
|---|
| 26091 | return true;
|
|---|
| 26092 | }
|
|---|
| 26093 |
|
|---|
| 26094 | function markRootSuspended$1(root, suspendedLanes) {
|
|---|
| 26095 | // When suspending, we should always exclude lanes that were pinged or (more
|
|---|
| 26096 | // rarely, since we try to avoid it) updated during the render phase.
|
|---|
| 26097 | // TODO: Lol maybe there's a better way to factor this besides this
|
|---|
| 26098 | // obnoxiously named function :)
|
|---|
| 26099 | suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes);
|
|---|
| 26100 | suspendedLanes = removeLanes(suspendedLanes, workInProgressRootInterleavedUpdatedLanes);
|
|---|
| 26101 | markRootSuspended(root, suspendedLanes);
|
|---|
| 26102 | } // This is the entry point for synchronous tasks that don't go
|
|---|
| 26103 | // through Scheduler
|
|---|
| 26104 |
|
|---|
| 26105 |
|
|---|
| 26106 | function performSyncWorkOnRoot(root) {
|
|---|
| 26107 | {
|
|---|
| 26108 | syncNestedUpdateFlag();
|
|---|
| 26109 | }
|
|---|
| 26110 |
|
|---|
| 26111 | if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|---|
| 26112 | throw new Error('Should not already be working.');
|
|---|
| 26113 | }
|
|---|
| 26114 |
|
|---|
| 26115 | flushPassiveEffects();
|
|---|
| 26116 | var lanes = getNextLanes(root, NoLanes);
|
|---|
| 26117 |
|
|---|
| 26118 | if (!includesSomeLane(lanes, SyncLane)) {
|
|---|
| 26119 | // There's no remaining sync work left.
|
|---|
| 26120 | ensureRootIsScheduled(root, now());
|
|---|
| 26121 | return null;
|
|---|
| 26122 | }
|
|---|
| 26123 |
|
|---|
| 26124 | var exitStatus = renderRootSync(root, lanes);
|
|---|
| 26125 |
|
|---|
| 26126 | if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
|
|---|
| 26127 | // If something threw an error, try rendering one more time. We'll render
|
|---|
| 26128 | // synchronously to block concurrent data mutations, and we'll includes
|
|---|
| 26129 | // all pending updates are included. If it still fails after the second
|
|---|
| 26130 | // attempt, we'll give up and commit the resulting tree.
|
|---|
| 26131 | var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root);
|
|---|
| 26132 |
|
|---|
| 26133 | if (errorRetryLanes !== NoLanes) {
|
|---|
| 26134 | lanes = errorRetryLanes;
|
|---|
| 26135 | exitStatus = recoverFromConcurrentError(root, errorRetryLanes);
|
|---|
| 26136 | }
|
|---|
| 26137 | }
|
|---|
| 26138 |
|
|---|
| 26139 | if (exitStatus === RootFatalErrored) {
|
|---|
| 26140 | var fatalError = workInProgressRootFatalError;
|
|---|
| 26141 | prepareFreshStack(root, NoLanes);
|
|---|
| 26142 | markRootSuspended$1(root, lanes);
|
|---|
| 26143 | ensureRootIsScheduled(root, now());
|
|---|
| 26144 | throw fatalError;
|
|---|
| 26145 | }
|
|---|
| 26146 |
|
|---|
| 26147 | if (exitStatus === RootDidNotComplete) {
|
|---|
| 26148 | throw new Error('Root did not complete. This is a bug in React.');
|
|---|
| 26149 | } // We now have a consistent tree. Because this is a sync render, we
|
|---|
| 26150 | // will commit it even if something suspended.
|
|---|
| 26151 |
|
|---|
| 26152 |
|
|---|
| 26153 | var finishedWork = root.current.alternate;
|
|---|
| 26154 | root.finishedWork = finishedWork;
|
|---|
| 26155 | root.finishedLanes = lanes;
|
|---|
| 26156 | commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); // Before exiting, make sure there's a callback scheduled for the next
|
|---|
| 26157 | // pending level.
|
|---|
| 26158 |
|
|---|
| 26159 | ensureRootIsScheduled(root, now());
|
|---|
| 26160 | return null;
|
|---|
| 26161 | }
|
|---|
| 26162 |
|
|---|
| 26163 | function flushRoot(root, lanes) {
|
|---|
| 26164 | if (lanes !== NoLanes) {
|
|---|
| 26165 | markRootEntangled(root, mergeLanes(lanes, SyncLane));
|
|---|
| 26166 | ensureRootIsScheduled(root, now());
|
|---|
| 26167 |
|
|---|
| 26168 | if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
|---|
| 26169 | resetRenderTimer();
|
|---|
| 26170 | flushSyncCallbacks();
|
|---|
| 26171 | }
|
|---|
| 26172 | }
|
|---|
| 26173 | }
|
|---|
| 26174 | function batchedUpdates$1(fn, a) {
|
|---|
| 26175 | var prevExecutionContext = executionContext;
|
|---|
| 26176 | executionContext |= BatchedContext;
|
|---|
| 26177 |
|
|---|
| 26178 | try {
|
|---|
| 26179 | return fn(a);
|
|---|
| 26180 | } finally {
|
|---|
| 26181 | executionContext = prevExecutionContext; // If there were legacy sync updates, flush them at the end of the outer
|
|---|
| 26182 | // most batchedUpdates-like method.
|
|---|
| 26183 |
|
|---|
| 26184 | if (executionContext === NoContext && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode.
|
|---|
| 26185 | !( ReactCurrentActQueue$1.isBatchingLegacy)) {
|
|---|
| 26186 | resetRenderTimer();
|
|---|
| 26187 | flushSyncCallbacksOnlyInLegacyMode();
|
|---|
| 26188 | }
|
|---|
| 26189 | }
|
|---|
| 26190 | }
|
|---|
| 26191 | function discreteUpdates(fn, a, b, c, d) {
|
|---|
| 26192 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 26193 | var prevTransition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 26194 |
|
|---|
| 26195 | try {
|
|---|
| 26196 | ReactCurrentBatchConfig$3.transition = null;
|
|---|
| 26197 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 26198 | return fn(a, b, c, d);
|
|---|
| 26199 | } finally {
|
|---|
| 26200 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 26201 | ReactCurrentBatchConfig$3.transition = prevTransition;
|
|---|
| 26202 |
|
|---|
| 26203 | if (executionContext === NoContext) {
|
|---|
| 26204 | resetRenderTimer();
|
|---|
| 26205 | }
|
|---|
| 26206 | }
|
|---|
| 26207 | } // Overload the definition to the two valid signatures.
|
|---|
| 26208 | // Warning, this opts-out of checking the function body.
|
|---|
| 26209 |
|
|---|
| 26210 | // eslint-disable-next-line no-redeclare
|
|---|
| 26211 | function flushSync(fn) {
|
|---|
| 26212 | // In legacy mode, we flush pending passive effects at the beginning of the
|
|---|
| 26213 | // next event, not at the end of the previous one.
|
|---|
| 26214 | if (rootWithPendingPassiveEffects !== null && rootWithPendingPassiveEffects.tag === LegacyRoot && (executionContext & (RenderContext | CommitContext)) === NoContext) {
|
|---|
| 26215 | flushPassiveEffects();
|
|---|
| 26216 | }
|
|---|
| 26217 |
|
|---|
| 26218 | var prevExecutionContext = executionContext;
|
|---|
| 26219 | executionContext |= BatchedContext;
|
|---|
| 26220 | var prevTransition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 26221 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 26222 |
|
|---|
| 26223 | try {
|
|---|
| 26224 | ReactCurrentBatchConfig$3.transition = null;
|
|---|
| 26225 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 26226 |
|
|---|
| 26227 | if (fn) {
|
|---|
| 26228 | return fn();
|
|---|
| 26229 | } else {
|
|---|
| 26230 | return undefined;
|
|---|
| 26231 | }
|
|---|
| 26232 | } finally {
|
|---|
| 26233 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 26234 | ReactCurrentBatchConfig$3.transition = prevTransition;
|
|---|
| 26235 | executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch.
|
|---|
| 26236 | // Note that this will happen even if batchedUpdates is higher up
|
|---|
| 26237 | // the stack.
|
|---|
| 26238 |
|
|---|
| 26239 | if ((executionContext & (RenderContext | CommitContext)) === NoContext) {
|
|---|
| 26240 | flushSyncCallbacks();
|
|---|
| 26241 | }
|
|---|
| 26242 | }
|
|---|
| 26243 | }
|
|---|
| 26244 | function isAlreadyRendering() {
|
|---|
| 26245 | // Used by the renderer to print a warning if certain APIs are called from
|
|---|
| 26246 | // the wrong context.
|
|---|
| 26247 | return (executionContext & (RenderContext | CommitContext)) !== NoContext;
|
|---|
| 26248 | }
|
|---|
| 26249 | function pushRenderLanes(fiber, lanes) {
|
|---|
| 26250 | push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber);
|
|---|
| 26251 | subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes);
|
|---|
| 26252 | workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes);
|
|---|
| 26253 | }
|
|---|
| 26254 | function popRenderLanes(fiber) {
|
|---|
| 26255 | subtreeRenderLanes = subtreeRenderLanesCursor.current;
|
|---|
| 26256 | pop(subtreeRenderLanesCursor, fiber);
|
|---|
| 26257 | }
|
|---|
| 26258 |
|
|---|
| 26259 | function prepareFreshStack(root, lanes) {
|
|---|
| 26260 | root.finishedWork = null;
|
|---|
| 26261 | root.finishedLanes = NoLanes;
|
|---|
| 26262 | var timeoutHandle = root.timeoutHandle;
|
|---|
| 26263 |
|
|---|
| 26264 | if (timeoutHandle !== noTimeout) {
|
|---|
| 26265 | // The root previous suspended and scheduled a timeout to commit a fallback
|
|---|
| 26266 | // state. Now that we have additional work, cancel the timeout.
|
|---|
| 26267 | root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above
|
|---|
| 26268 |
|
|---|
| 26269 | cancelTimeout(timeoutHandle);
|
|---|
| 26270 | }
|
|---|
| 26271 |
|
|---|
| 26272 | if (workInProgress !== null) {
|
|---|
| 26273 | var interruptedWork = workInProgress.return;
|
|---|
| 26274 |
|
|---|
| 26275 | while (interruptedWork !== null) {
|
|---|
| 26276 | var current = interruptedWork.alternate;
|
|---|
| 26277 | unwindInterruptedWork(current, interruptedWork);
|
|---|
| 26278 | interruptedWork = interruptedWork.return;
|
|---|
| 26279 | }
|
|---|
| 26280 | }
|
|---|
| 26281 |
|
|---|
| 26282 | workInProgressRoot = root;
|
|---|
| 26283 | var rootWorkInProgress = createWorkInProgress(root.current, null);
|
|---|
| 26284 | workInProgress = rootWorkInProgress;
|
|---|
| 26285 | workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes;
|
|---|
| 26286 | workInProgressRootExitStatus = RootInProgress;
|
|---|
| 26287 | workInProgressRootFatalError = null;
|
|---|
| 26288 | workInProgressRootSkippedLanes = NoLanes;
|
|---|
| 26289 | workInProgressRootInterleavedUpdatedLanes = NoLanes;
|
|---|
| 26290 | workInProgressRootPingedLanes = NoLanes;
|
|---|
| 26291 | workInProgressRootConcurrentErrors = null;
|
|---|
| 26292 | workInProgressRootRecoverableErrors = null;
|
|---|
| 26293 | finishQueueingConcurrentUpdates();
|
|---|
| 26294 |
|
|---|
| 26295 | {
|
|---|
| 26296 | ReactStrictModeWarnings.discardPendingWarnings();
|
|---|
| 26297 | }
|
|---|
| 26298 |
|
|---|
| 26299 | return rootWorkInProgress;
|
|---|
| 26300 | }
|
|---|
| 26301 |
|
|---|
| 26302 | function handleError(root, thrownValue) {
|
|---|
| 26303 | do {
|
|---|
| 26304 | var erroredWork = workInProgress;
|
|---|
| 26305 |
|
|---|
| 26306 | try {
|
|---|
| 26307 | // Reset module-level state that was set during the render phase.
|
|---|
| 26308 | resetContextDependencies();
|
|---|
| 26309 | resetHooksAfterThrow();
|
|---|
| 26310 | resetCurrentFiber(); // TODO: I found and added this missing line while investigating a
|
|---|
| 26311 | // separate issue. Write a regression test using string refs.
|
|---|
| 26312 |
|
|---|
| 26313 | ReactCurrentOwner$2.current = null;
|
|---|
| 26314 |
|
|---|
| 26315 | if (erroredWork === null || erroredWork.return === null) {
|
|---|
| 26316 | // Expected to be working on a non-root fiber. This is a fatal error
|
|---|
| 26317 | // because there's no ancestor that can handle it; the root is
|
|---|
| 26318 | // supposed to capture all errors that weren't caught by an error
|
|---|
| 26319 | // boundary.
|
|---|
| 26320 | workInProgressRootExitStatus = RootFatalErrored;
|
|---|
| 26321 | workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next
|
|---|
| 26322 | // sibling, or the parent if there are no siblings. But since the root
|
|---|
| 26323 | // has no siblings nor a parent, we set it to null. Usually this is
|
|---|
| 26324 | // handled by `completeUnitOfWork` or `unwindWork`, but since we're
|
|---|
| 26325 | // intentionally not calling those, we need set it here.
|
|---|
| 26326 | // TODO: Consider calling `unwindWork` to pop the contexts.
|
|---|
| 26327 |
|
|---|
| 26328 | workInProgress = null;
|
|---|
| 26329 | return;
|
|---|
| 26330 | }
|
|---|
| 26331 |
|
|---|
| 26332 | if (enableProfilerTimer && erroredWork.mode & ProfileMode) {
|
|---|
| 26333 | // Record the time spent rendering before an error was thrown. This
|
|---|
| 26334 | // avoids inaccurate Profiler durations in the case of a
|
|---|
| 26335 | // suspended render.
|
|---|
| 26336 | stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true);
|
|---|
| 26337 | }
|
|---|
| 26338 |
|
|---|
| 26339 | if (enableSchedulingProfiler) {
|
|---|
| 26340 | markComponentRenderStopped();
|
|---|
| 26341 |
|
|---|
| 26342 | if (thrownValue !== null && typeof thrownValue === 'object' && typeof thrownValue.then === 'function') {
|
|---|
| 26343 | var wakeable = thrownValue;
|
|---|
| 26344 | markComponentSuspended(erroredWork, wakeable, workInProgressRootRenderLanes);
|
|---|
| 26345 | } else {
|
|---|
| 26346 | markComponentErrored(erroredWork, thrownValue, workInProgressRootRenderLanes);
|
|---|
| 26347 | }
|
|---|
| 26348 | }
|
|---|
| 26349 |
|
|---|
| 26350 | throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes);
|
|---|
| 26351 | completeUnitOfWork(erroredWork);
|
|---|
| 26352 | } catch (yetAnotherThrownValue) {
|
|---|
| 26353 | // Something in the return path also threw.
|
|---|
| 26354 | thrownValue = yetAnotherThrownValue;
|
|---|
| 26355 |
|
|---|
| 26356 | if (workInProgress === erroredWork && erroredWork !== null) {
|
|---|
| 26357 | // If this boundary has already errored, then we had trouble processing
|
|---|
| 26358 | // the error. Bubble it to the next boundary.
|
|---|
| 26359 | erroredWork = erroredWork.return;
|
|---|
| 26360 | workInProgress = erroredWork;
|
|---|
| 26361 | } else {
|
|---|
| 26362 | erroredWork = workInProgress;
|
|---|
| 26363 | }
|
|---|
| 26364 |
|
|---|
| 26365 | continue;
|
|---|
| 26366 | } // Return to the normal work loop.
|
|---|
| 26367 |
|
|---|
| 26368 |
|
|---|
| 26369 | return;
|
|---|
| 26370 | } while (true);
|
|---|
| 26371 | }
|
|---|
| 26372 |
|
|---|
| 26373 | function pushDispatcher() {
|
|---|
| 26374 | var prevDispatcher = ReactCurrentDispatcher$2.current;
|
|---|
| 26375 | ReactCurrentDispatcher$2.current = ContextOnlyDispatcher;
|
|---|
| 26376 |
|
|---|
| 26377 | if (prevDispatcher === null) {
|
|---|
| 26378 | // The React isomorphic package does not include a default dispatcher.
|
|---|
| 26379 | // Instead the first renderer will lazily attach one, in order to give
|
|---|
| 26380 | // nicer error messages.
|
|---|
| 26381 | return ContextOnlyDispatcher;
|
|---|
| 26382 | } else {
|
|---|
| 26383 | return prevDispatcher;
|
|---|
| 26384 | }
|
|---|
| 26385 | }
|
|---|
| 26386 |
|
|---|
| 26387 | function popDispatcher(prevDispatcher) {
|
|---|
| 26388 | ReactCurrentDispatcher$2.current = prevDispatcher;
|
|---|
| 26389 | }
|
|---|
| 26390 |
|
|---|
| 26391 | function markCommitTimeOfFallback() {
|
|---|
| 26392 | globalMostRecentFallbackTime = now();
|
|---|
| 26393 | }
|
|---|
| 26394 | function markSkippedUpdateLanes(lane) {
|
|---|
| 26395 | workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes);
|
|---|
| 26396 | }
|
|---|
| 26397 | function renderDidSuspend() {
|
|---|
| 26398 | if (workInProgressRootExitStatus === RootInProgress) {
|
|---|
| 26399 | workInProgressRootExitStatus = RootSuspended;
|
|---|
| 26400 | }
|
|---|
| 26401 | }
|
|---|
| 26402 | function renderDidSuspendDelayIfPossible() {
|
|---|
| 26403 | if (workInProgressRootExitStatus === RootInProgress || workInProgressRootExitStatus === RootSuspended || workInProgressRootExitStatus === RootErrored) {
|
|---|
| 26404 | workInProgressRootExitStatus = RootSuspendedWithDelay;
|
|---|
| 26405 | } // Check if there are updates that we skipped tree that might have unblocked
|
|---|
| 26406 | // this render.
|
|---|
| 26407 |
|
|---|
| 26408 |
|
|---|
| 26409 | if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))) {
|
|---|
| 26410 | // Mark the current render as suspended so that we switch to working on
|
|---|
| 26411 | // the updates that were skipped. Usually we only suspend at the end of
|
|---|
| 26412 | // the render phase.
|
|---|
| 26413 | // TODO: We should probably always mark the root as suspended immediately
|
|---|
| 26414 | // (inside this function), since by suspending at the end of the render
|
|---|
| 26415 | // phase introduces a potential mistake where we suspend lanes that were
|
|---|
| 26416 | // pinged or updated while we were rendering.
|
|---|
| 26417 | markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes);
|
|---|
| 26418 | }
|
|---|
| 26419 | }
|
|---|
| 26420 | function renderDidError(error) {
|
|---|
| 26421 | if (workInProgressRootExitStatus !== RootSuspendedWithDelay) {
|
|---|
| 26422 | workInProgressRootExitStatus = RootErrored;
|
|---|
| 26423 | }
|
|---|
| 26424 |
|
|---|
| 26425 | if (workInProgressRootConcurrentErrors === null) {
|
|---|
| 26426 | workInProgressRootConcurrentErrors = [error];
|
|---|
| 26427 | } else {
|
|---|
| 26428 | workInProgressRootConcurrentErrors.push(error);
|
|---|
| 26429 | }
|
|---|
| 26430 | } // Called during render to determine if anything has suspended.
|
|---|
| 26431 | // Returns false if we're not sure.
|
|---|
| 26432 |
|
|---|
| 26433 | function renderHasNotSuspendedYet() {
|
|---|
| 26434 | // If something errored or completed, we can't really be sure,
|
|---|
| 26435 | // so those are false.
|
|---|
| 26436 | return workInProgressRootExitStatus === RootInProgress;
|
|---|
| 26437 | }
|
|---|
| 26438 |
|
|---|
| 26439 | function renderRootSync(root, lanes) {
|
|---|
| 26440 | var prevExecutionContext = executionContext;
|
|---|
| 26441 | executionContext |= RenderContext;
|
|---|
| 26442 | var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack
|
|---|
| 26443 | // and prepare a fresh one. Otherwise we'll continue where we left off.
|
|---|
| 26444 |
|
|---|
| 26445 | if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
|
|---|
| 26446 | {
|
|---|
| 26447 | if (isDevToolsPresent) {
|
|---|
| 26448 | var memoizedUpdaters = root.memoizedUpdaters;
|
|---|
| 26449 |
|
|---|
| 26450 | if (memoizedUpdaters.size > 0) {
|
|---|
| 26451 | restorePendingUpdaters(root, workInProgressRootRenderLanes);
|
|---|
| 26452 | memoizedUpdaters.clear();
|
|---|
| 26453 | } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
|
|---|
| 26454 | // If we bailout on this work, we'll move them back (like above).
|
|---|
| 26455 | // It's important to move them now in case the work spawns more work at the same priority with different updaters.
|
|---|
| 26456 | // That way we can keep the current update and future updates separate.
|
|---|
| 26457 |
|
|---|
| 26458 |
|
|---|
| 26459 | movePendingFibersToMemoized(root, lanes);
|
|---|
| 26460 | }
|
|---|
| 26461 | }
|
|---|
| 26462 |
|
|---|
| 26463 | workInProgressTransitions = getTransitionsForLanes();
|
|---|
| 26464 | prepareFreshStack(root, lanes);
|
|---|
| 26465 | }
|
|---|
| 26466 |
|
|---|
| 26467 | {
|
|---|
| 26468 | markRenderStarted(lanes);
|
|---|
| 26469 | }
|
|---|
| 26470 |
|
|---|
| 26471 | do {
|
|---|
| 26472 | try {
|
|---|
| 26473 | workLoopSync();
|
|---|
| 26474 | break;
|
|---|
| 26475 | } catch (thrownValue) {
|
|---|
| 26476 | handleError(root, thrownValue);
|
|---|
| 26477 | }
|
|---|
| 26478 | } while (true);
|
|---|
| 26479 |
|
|---|
| 26480 | resetContextDependencies();
|
|---|
| 26481 | executionContext = prevExecutionContext;
|
|---|
| 26482 | popDispatcher(prevDispatcher);
|
|---|
| 26483 |
|
|---|
| 26484 | if (workInProgress !== null) {
|
|---|
| 26485 | // This is a sync render, so we should have finished the whole tree.
|
|---|
| 26486 | throw new Error('Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.');
|
|---|
| 26487 | }
|
|---|
| 26488 |
|
|---|
| 26489 | {
|
|---|
| 26490 | markRenderStopped();
|
|---|
| 26491 | } // Set this to null to indicate there's no in-progress render.
|
|---|
| 26492 |
|
|---|
| 26493 |
|
|---|
| 26494 | workInProgressRoot = null;
|
|---|
| 26495 | workInProgressRootRenderLanes = NoLanes;
|
|---|
| 26496 | return workInProgressRootExitStatus;
|
|---|
| 26497 | } // The work loop is an extremely hot path. Tell Closure not to inline it.
|
|---|
| 26498 |
|
|---|
| 26499 | /** @noinline */
|
|---|
| 26500 |
|
|---|
| 26501 |
|
|---|
| 26502 | function workLoopSync() {
|
|---|
| 26503 | // Already timed out, so perform work without checking if we need to yield.
|
|---|
| 26504 | while (workInProgress !== null) {
|
|---|
| 26505 | performUnitOfWork(workInProgress);
|
|---|
| 26506 | }
|
|---|
| 26507 | }
|
|---|
| 26508 |
|
|---|
| 26509 | function renderRootConcurrent(root, lanes) {
|
|---|
| 26510 | var prevExecutionContext = executionContext;
|
|---|
| 26511 | executionContext |= RenderContext;
|
|---|
| 26512 | var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack
|
|---|
| 26513 | // and prepare a fresh one. Otherwise we'll continue where we left off.
|
|---|
| 26514 |
|
|---|
| 26515 | if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
|
|---|
| 26516 | {
|
|---|
| 26517 | if (isDevToolsPresent) {
|
|---|
| 26518 | var memoizedUpdaters = root.memoizedUpdaters;
|
|---|
| 26519 |
|
|---|
| 26520 | if (memoizedUpdaters.size > 0) {
|
|---|
| 26521 | restorePendingUpdaters(root, workInProgressRootRenderLanes);
|
|---|
| 26522 | memoizedUpdaters.clear();
|
|---|
| 26523 | } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.
|
|---|
| 26524 | // If we bailout on this work, we'll move them back (like above).
|
|---|
| 26525 | // It's important to move them now in case the work spawns more work at the same priority with different updaters.
|
|---|
| 26526 | // That way we can keep the current update and future updates separate.
|
|---|
| 26527 |
|
|---|
| 26528 |
|
|---|
| 26529 | movePendingFibersToMemoized(root, lanes);
|
|---|
| 26530 | }
|
|---|
| 26531 | }
|
|---|
| 26532 |
|
|---|
| 26533 | workInProgressTransitions = getTransitionsForLanes();
|
|---|
| 26534 | resetRenderTimer();
|
|---|
| 26535 | prepareFreshStack(root, lanes);
|
|---|
| 26536 | }
|
|---|
| 26537 |
|
|---|
| 26538 | {
|
|---|
| 26539 | markRenderStarted(lanes);
|
|---|
| 26540 | }
|
|---|
| 26541 |
|
|---|
| 26542 | do {
|
|---|
| 26543 | try {
|
|---|
| 26544 | workLoopConcurrent();
|
|---|
| 26545 | break;
|
|---|
| 26546 | } catch (thrownValue) {
|
|---|
| 26547 | handleError(root, thrownValue);
|
|---|
| 26548 | }
|
|---|
| 26549 | } while (true);
|
|---|
| 26550 |
|
|---|
| 26551 | resetContextDependencies();
|
|---|
| 26552 | popDispatcher(prevDispatcher);
|
|---|
| 26553 | executionContext = prevExecutionContext;
|
|---|
| 26554 |
|
|---|
| 26555 |
|
|---|
| 26556 | if (workInProgress !== null) {
|
|---|
| 26557 | // Still work remaining.
|
|---|
| 26558 | {
|
|---|
| 26559 | markRenderYielded();
|
|---|
| 26560 | }
|
|---|
| 26561 |
|
|---|
| 26562 | return RootInProgress;
|
|---|
| 26563 | } else {
|
|---|
| 26564 | // Completed the tree.
|
|---|
| 26565 | {
|
|---|
| 26566 | markRenderStopped();
|
|---|
| 26567 | } // Set this to null to indicate there's no in-progress render.
|
|---|
| 26568 |
|
|---|
| 26569 |
|
|---|
| 26570 | workInProgressRoot = null;
|
|---|
| 26571 | workInProgressRootRenderLanes = NoLanes; // Return the final exit status.
|
|---|
| 26572 |
|
|---|
| 26573 | return workInProgressRootExitStatus;
|
|---|
| 26574 | }
|
|---|
| 26575 | }
|
|---|
| 26576 | /** @noinline */
|
|---|
| 26577 |
|
|---|
| 26578 |
|
|---|
| 26579 | function workLoopConcurrent() {
|
|---|
| 26580 | // Perform work until Scheduler asks us to yield
|
|---|
| 26581 | while (workInProgress !== null && !shouldYield()) {
|
|---|
| 26582 | performUnitOfWork(workInProgress);
|
|---|
| 26583 | }
|
|---|
| 26584 | }
|
|---|
| 26585 |
|
|---|
| 26586 | function performUnitOfWork(unitOfWork) {
|
|---|
| 26587 | // The current, flushed, state of this fiber is the alternate. Ideally
|
|---|
| 26588 | // nothing should rely on this, but relying on it here means that we don't
|
|---|
| 26589 | // need an additional field on the work in progress.
|
|---|
| 26590 | var current = unitOfWork.alternate;
|
|---|
| 26591 | setCurrentFiber(unitOfWork);
|
|---|
| 26592 | var next;
|
|---|
| 26593 |
|
|---|
| 26594 | if ( (unitOfWork.mode & ProfileMode) !== NoMode) {
|
|---|
| 26595 | startProfilerTimer(unitOfWork);
|
|---|
| 26596 | next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
|
|---|
| 26597 | stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);
|
|---|
| 26598 | } else {
|
|---|
| 26599 | next = beginWork$1(current, unitOfWork, subtreeRenderLanes);
|
|---|
| 26600 | }
|
|---|
| 26601 |
|
|---|
| 26602 | resetCurrentFiber();
|
|---|
| 26603 | unitOfWork.memoizedProps = unitOfWork.pendingProps;
|
|---|
| 26604 |
|
|---|
| 26605 | if (next === null) {
|
|---|
| 26606 | // If this doesn't spawn new work, complete the current work.
|
|---|
| 26607 | completeUnitOfWork(unitOfWork);
|
|---|
| 26608 | } else {
|
|---|
| 26609 | workInProgress = next;
|
|---|
| 26610 | }
|
|---|
| 26611 |
|
|---|
| 26612 | ReactCurrentOwner$2.current = null;
|
|---|
| 26613 | }
|
|---|
| 26614 |
|
|---|
| 26615 | function completeUnitOfWork(unitOfWork) {
|
|---|
| 26616 | // Attempt to complete the current unit of work, then move to the next
|
|---|
| 26617 | // sibling. If there are no more siblings, return to the parent fiber.
|
|---|
| 26618 | var completedWork = unitOfWork;
|
|---|
| 26619 |
|
|---|
| 26620 | do {
|
|---|
| 26621 | // The current, flushed, state of this fiber is the alternate. Ideally
|
|---|
| 26622 | // nothing should rely on this, but relying on it here means that we don't
|
|---|
| 26623 | // need an additional field on the work in progress.
|
|---|
| 26624 | var current = completedWork.alternate;
|
|---|
| 26625 | var returnFiber = completedWork.return; // Check if the work completed or if something threw.
|
|---|
| 26626 |
|
|---|
| 26627 | if ((completedWork.flags & Incomplete) === NoFlags) {
|
|---|
| 26628 | setCurrentFiber(completedWork);
|
|---|
| 26629 | var next = void 0;
|
|---|
| 26630 |
|
|---|
| 26631 | if ( (completedWork.mode & ProfileMode) === NoMode) {
|
|---|
| 26632 | next = completeWork(current, completedWork, subtreeRenderLanes);
|
|---|
| 26633 | } else {
|
|---|
| 26634 | startProfilerTimer(completedWork);
|
|---|
| 26635 | next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.
|
|---|
| 26636 |
|
|---|
| 26637 | stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);
|
|---|
| 26638 | }
|
|---|
| 26639 |
|
|---|
| 26640 | resetCurrentFiber();
|
|---|
| 26641 |
|
|---|
| 26642 | if (next !== null) {
|
|---|
| 26643 | // Completing this fiber spawned new work. Work on that next.
|
|---|
| 26644 | workInProgress = next;
|
|---|
| 26645 | return;
|
|---|
| 26646 | }
|
|---|
| 26647 | } else {
|
|---|
| 26648 | // This fiber did not complete because something threw. Pop values off
|
|---|
| 26649 | // the stack without entering the complete phase. If this is a boundary,
|
|---|
| 26650 | // capture values if possible.
|
|---|
| 26651 | var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes.
|
|---|
| 26652 |
|
|---|
| 26653 |
|
|---|
| 26654 | if (_next !== null) {
|
|---|
| 26655 | // If completing this work spawned new work, do that next. We'll come
|
|---|
| 26656 | // back here again.
|
|---|
| 26657 | // Since we're restarting, remove anything that is not a host effect
|
|---|
| 26658 | // from the effect tag.
|
|---|
| 26659 | _next.flags &= HostEffectMask;
|
|---|
| 26660 | workInProgress = _next;
|
|---|
| 26661 | return;
|
|---|
| 26662 | }
|
|---|
| 26663 |
|
|---|
| 26664 | if ( (completedWork.mode & ProfileMode) !== NoMode) {
|
|---|
| 26665 | // Record the render duration for the fiber that errored.
|
|---|
| 26666 | stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.
|
|---|
| 26667 |
|
|---|
| 26668 | var actualDuration = completedWork.actualDuration;
|
|---|
| 26669 | var child = completedWork.child;
|
|---|
| 26670 |
|
|---|
| 26671 | while (child !== null) {
|
|---|
| 26672 | actualDuration += child.actualDuration;
|
|---|
| 26673 | child = child.sibling;
|
|---|
| 26674 | }
|
|---|
| 26675 |
|
|---|
| 26676 | completedWork.actualDuration = actualDuration;
|
|---|
| 26677 | }
|
|---|
| 26678 |
|
|---|
| 26679 | if (returnFiber !== null) {
|
|---|
| 26680 | // Mark the parent fiber as incomplete and clear its subtree flags.
|
|---|
| 26681 | returnFiber.flags |= Incomplete;
|
|---|
| 26682 | returnFiber.subtreeFlags = NoFlags;
|
|---|
| 26683 | returnFiber.deletions = null;
|
|---|
| 26684 | } else {
|
|---|
| 26685 | // We've unwound all the way to the root.
|
|---|
| 26686 | workInProgressRootExitStatus = RootDidNotComplete;
|
|---|
| 26687 | workInProgress = null;
|
|---|
| 26688 | return;
|
|---|
| 26689 | }
|
|---|
| 26690 | }
|
|---|
| 26691 |
|
|---|
| 26692 | var siblingFiber = completedWork.sibling;
|
|---|
| 26693 |
|
|---|
| 26694 | if (siblingFiber !== null) {
|
|---|
| 26695 | // If there is more work to do in this returnFiber, do that next.
|
|---|
| 26696 | workInProgress = siblingFiber;
|
|---|
| 26697 | return;
|
|---|
| 26698 | } // Otherwise, return to the parent
|
|---|
| 26699 |
|
|---|
| 26700 |
|
|---|
| 26701 | completedWork = returnFiber; // Update the next thing we're working on in case something throws.
|
|---|
| 26702 |
|
|---|
| 26703 | workInProgress = completedWork;
|
|---|
| 26704 | } while (completedWork !== null); // We've reached the root.
|
|---|
| 26705 |
|
|---|
| 26706 |
|
|---|
| 26707 | if (workInProgressRootExitStatus === RootInProgress) {
|
|---|
| 26708 | workInProgressRootExitStatus = RootCompleted;
|
|---|
| 26709 | }
|
|---|
| 26710 | }
|
|---|
| 26711 |
|
|---|
| 26712 | function commitRoot(root, recoverableErrors, transitions) {
|
|---|
| 26713 | // TODO: This no longer makes any sense. We already wrap the mutation and
|
|---|
| 26714 | // layout phases. Should be able to remove.
|
|---|
| 26715 | var previousUpdateLanePriority = getCurrentUpdatePriority();
|
|---|
| 26716 | var prevTransition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 26717 |
|
|---|
| 26718 | try {
|
|---|
| 26719 | ReactCurrentBatchConfig$3.transition = null;
|
|---|
| 26720 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 26721 | commitRootImpl(root, recoverableErrors, transitions, previousUpdateLanePriority);
|
|---|
| 26722 | } finally {
|
|---|
| 26723 | ReactCurrentBatchConfig$3.transition = prevTransition;
|
|---|
| 26724 | setCurrentUpdatePriority(previousUpdateLanePriority);
|
|---|
| 26725 | }
|
|---|
| 26726 |
|
|---|
| 26727 | return null;
|
|---|
| 26728 | }
|
|---|
| 26729 |
|
|---|
| 26730 | function commitRootImpl(root, recoverableErrors, transitions, renderPriorityLevel) {
|
|---|
| 26731 | do {
|
|---|
| 26732 | // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which
|
|---|
| 26733 | // means `flushPassiveEffects` will sometimes result in additional
|
|---|
| 26734 | // passive effects. So we need to keep flushing in a loop until there are
|
|---|
| 26735 | // no more pending effects.
|
|---|
| 26736 | // TODO: Might be better if `flushPassiveEffects` did not automatically
|
|---|
| 26737 | // flush synchronous work at the end, to avoid factoring hazards like this.
|
|---|
| 26738 | flushPassiveEffects();
|
|---|
| 26739 | } while (rootWithPendingPassiveEffects !== null);
|
|---|
| 26740 |
|
|---|
| 26741 | flushRenderPhaseStrictModeWarningsInDEV();
|
|---|
| 26742 |
|
|---|
| 26743 | if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|---|
| 26744 | throw new Error('Should not already be working.');
|
|---|
| 26745 | }
|
|---|
| 26746 |
|
|---|
| 26747 | var finishedWork = root.finishedWork;
|
|---|
| 26748 | var lanes = root.finishedLanes;
|
|---|
| 26749 |
|
|---|
| 26750 | {
|
|---|
| 26751 | markCommitStarted(lanes);
|
|---|
| 26752 | }
|
|---|
| 26753 |
|
|---|
| 26754 | if (finishedWork === null) {
|
|---|
| 26755 |
|
|---|
| 26756 | {
|
|---|
| 26757 | markCommitStopped();
|
|---|
| 26758 | }
|
|---|
| 26759 |
|
|---|
| 26760 | return null;
|
|---|
| 26761 | } else {
|
|---|
| 26762 | {
|
|---|
| 26763 | if (lanes === NoLanes) {
|
|---|
| 26764 | error('root.finishedLanes should not be empty during a commit. This is a ' + 'bug in React.');
|
|---|
| 26765 | }
|
|---|
| 26766 | }
|
|---|
| 26767 | }
|
|---|
| 26768 |
|
|---|
| 26769 | root.finishedWork = null;
|
|---|
| 26770 | root.finishedLanes = NoLanes;
|
|---|
| 26771 |
|
|---|
| 26772 | if (finishedWork === root.current) {
|
|---|
| 26773 | throw new Error('Cannot commit the same tree as before. This error is likely caused by ' + 'a bug in React. Please file an issue.');
|
|---|
| 26774 | } // commitRoot never returns a continuation; it always finishes synchronously.
|
|---|
| 26775 | // So we can clear these now to allow a new callback to be scheduled.
|
|---|
| 26776 |
|
|---|
| 26777 |
|
|---|
| 26778 | root.callbackNode = null;
|
|---|
| 26779 | root.callbackPriority = NoLane; // Update the first and last pending times on this root. The new first
|
|---|
| 26780 | // pending time is whatever is left on the root fiber.
|
|---|
| 26781 |
|
|---|
| 26782 | var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes);
|
|---|
| 26783 | markRootFinished(root, remainingLanes);
|
|---|
| 26784 |
|
|---|
| 26785 | if (root === workInProgressRoot) {
|
|---|
| 26786 | // We can reset these now that they are finished.
|
|---|
| 26787 | workInProgressRoot = null;
|
|---|
| 26788 | workInProgress = null;
|
|---|
| 26789 | workInProgressRootRenderLanes = NoLanes;
|
|---|
| 26790 | } // If there are pending passive effects, schedule a callback to process them.
|
|---|
| 26791 | // Do this as early as possible, so it is queued before anything else that
|
|---|
| 26792 | // might get scheduled in the commit phase. (See #16714.)
|
|---|
| 26793 | // TODO: Delete all other places that schedule the passive effect callback
|
|---|
| 26794 | // They're redundant.
|
|---|
| 26795 |
|
|---|
| 26796 |
|
|---|
| 26797 | if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags) {
|
|---|
| 26798 | if (!rootDoesHavePassiveEffects) {
|
|---|
| 26799 | rootDoesHavePassiveEffects = true;
|
|---|
| 26800 | // to store it in pendingPassiveTransitions until they get processed
|
|---|
| 26801 | // We need to pass this through as an argument to commitRoot
|
|---|
| 26802 | // because workInProgressTransitions might have changed between
|
|---|
| 26803 | // the previous render and commit if we throttle the commit
|
|---|
| 26804 | // with setTimeout
|
|---|
| 26805 |
|
|---|
| 26806 | pendingPassiveTransitions = transitions;
|
|---|
| 26807 | scheduleCallback$1(NormalPriority, function () {
|
|---|
| 26808 | flushPassiveEffects(); // This render triggered passive effects: release the root cache pool
|
|---|
| 26809 | // *after* passive effects fire to avoid freeing a cache pool that may
|
|---|
| 26810 | // be referenced by a node in the tree (HostRoot, Cache boundary etc)
|
|---|
| 26811 |
|
|---|
| 26812 | return null;
|
|---|
| 26813 | });
|
|---|
| 26814 | }
|
|---|
| 26815 | } // Check if there are any effects in the whole tree.
|
|---|
| 26816 | // TODO: This is left over from the effect list implementation, where we had
|
|---|
| 26817 | // to check for the existence of `firstEffect` to satisfy Flow. I think the
|
|---|
| 26818 | // only other reason this optimization exists is because it affects profiling.
|
|---|
| 26819 | // Reconsider whether this is necessary.
|
|---|
| 26820 |
|
|---|
| 26821 |
|
|---|
| 26822 | var subtreeHasEffects = (finishedWork.subtreeFlags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;
|
|---|
| 26823 | var rootHasEffect = (finishedWork.flags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags;
|
|---|
| 26824 |
|
|---|
| 26825 | if (subtreeHasEffects || rootHasEffect) {
|
|---|
| 26826 | var prevTransition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 26827 | ReactCurrentBatchConfig$3.transition = null;
|
|---|
| 26828 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 26829 | setCurrentUpdatePriority(DiscreteEventPriority);
|
|---|
| 26830 | var prevExecutionContext = executionContext;
|
|---|
| 26831 | executionContext |= CommitContext; // Reset this to null before calling lifecycles
|
|---|
| 26832 |
|
|---|
| 26833 | ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass
|
|---|
| 26834 | // of the effect list for each phase: all mutation effects come before all
|
|---|
| 26835 | // layout effects, and so on.
|
|---|
| 26836 | // The first phase a "before mutation" phase. We use this phase to read the
|
|---|
| 26837 | // state of the host tree right before we mutate it. This is where
|
|---|
| 26838 | // getSnapshotBeforeUpdate is called.
|
|---|
| 26839 |
|
|---|
| 26840 | var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork);
|
|---|
| 26841 |
|
|---|
| 26842 | {
|
|---|
| 26843 | // Mark the current commit time to be shared by all Profilers in this
|
|---|
| 26844 | // batch. This enables them to be grouped later.
|
|---|
| 26845 | recordCommitTime();
|
|---|
| 26846 | }
|
|---|
| 26847 |
|
|---|
| 26848 |
|
|---|
| 26849 | commitMutationEffects(root, finishedWork, lanes);
|
|---|
| 26850 |
|
|---|
| 26851 | resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after
|
|---|
| 26852 | // the mutation phase, so that the previous tree is still current during
|
|---|
| 26853 | // componentWillUnmount, but before the layout phase, so that the finished
|
|---|
| 26854 | // work is current during componentDidMount/Update.
|
|---|
| 26855 |
|
|---|
| 26856 | root.current = finishedWork; // The next phase is the layout phase, where we call effects that read
|
|---|
| 26857 |
|
|---|
| 26858 | {
|
|---|
| 26859 | markLayoutEffectsStarted(lanes);
|
|---|
| 26860 | }
|
|---|
| 26861 |
|
|---|
| 26862 | commitLayoutEffects(finishedWork, root, lanes);
|
|---|
| 26863 |
|
|---|
| 26864 | {
|
|---|
| 26865 | markLayoutEffectsStopped();
|
|---|
| 26866 | }
|
|---|
| 26867 | // opportunity to paint.
|
|---|
| 26868 |
|
|---|
| 26869 |
|
|---|
| 26870 | requestPaint();
|
|---|
| 26871 | executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value.
|
|---|
| 26872 |
|
|---|
| 26873 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 26874 | ReactCurrentBatchConfig$3.transition = prevTransition;
|
|---|
| 26875 | } else {
|
|---|
| 26876 | // No effects.
|
|---|
| 26877 | root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were
|
|---|
| 26878 | // no effects.
|
|---|
| 26879 | // TODO: Maybe there's a better way to report this.
|
|---|
| 26880 |
|
|---|
| 26881 | {
|
|---|
| 26882 | recordCommitTime();
|
|---|
| 26883 | }
|
|---|
| 26884 | }
|
|---|
| 26885 |
|
|---|
| 26886 | var rootDidHavePassiveEffects = rootDoesHavePassiveEffects;
|
|---|
| 26887 |
|
|---|
| 26888 | if (rootDoesHavePassiveEffects) {
|
|---|
| 26889 | // This commit has passive effects. Stash a reference to them. But don't
|
|---|
| 26890 | // schedule a callback until after flushing layout work.
|
|---|
| 26891 | rootDoesHavePassiveEffects = false;
|
|---|
| 26892 | rootWithPendingPassiveEffects = root;
|
|---|
| 26893 | pendingPassiveEffectsLanes = lanes;
|
|---|
| 26894 | } else {
|
|---|
| 26895 |
|
|---|
| 26896 | {
|
|---|
| 26897 | nestedPassiveUpdateCount = 0;
|
|---|
| 26898 | rootWithPassiveNestedUpdates = null;
|
|---|
| 26899 | }
|
|---|
| 26900 | } // Read this again, since an effect might have updated it
|
|---|
| 26901 |
|
|---|
| 26902 |
|
|---|
| 26903 | remainingLanes = root.pendingLanes; // Check if there's remaining work on this root
|
|---|
| 26904 | // TODO: This is part of the `componentDidCatch` implementation. Its purpose
|
|---|
| 26905 | // is to detect whether something might have called setState inside
|
|---|
| 26906 | // `componentDidCatch`. The mechanism is known to be flawed because `setState`
|
|---|
| 26907 | // inside `componentDidCatch` is itself flawed — that's why we recommend
|
|---|
| 26908 | // `getDerivedStateFromError` instead. However, it could be improved by
|
|---|
| 26909 | // checking if remainingLanes includes Sync work, instead of whether there's
|
|---|
| 26910 | // any work remaining at all (which would also include stuff like Suspense
|
|---|
| 26911 | // retries or transitions). It's been like this for a while, though, so fixing
|
|---|
| 26912 | // it probably isn't that urgent.
|
|---|
| 26913 |
|
|---|
| 26914 | if (remainingLanes === NoLanes) {
|
|---|
| 26915 | // If there's no remaining work, we can clear the set of already failed
|
|---|
| 26916 | // error boundaries.
|
|---|
| 26917 | legacyErrorBoundariesThatAlreadyFailed = null;
|
|---|
| 26918 | }
|
|---|
| 26919 |
|
|---|
| 26920 | {
|
|---|
| 26921 | if (!rootDidHavePassiveEffects) {
|
|---|
| 26922 | commitDoubleInvokeEffectsInDEV(root.current, false);
|
|---|
| 26923 | }
|
|---|
| 26924 | }
|
|---|
| 26925 |
|
|---|
| 26926 | onCommitRoot(finishedWork.stateNode, renderPriorityLevel);
|
|---|
| 26927 |
|
|---|
| 26928 | {
|
|---|
| 26929 | if (isDevToolsPresent) {
|
|---|
| 26930 | root.memoizedUpdaters.clear();
|
|---|
| 26931 | }
|
|---|
| 26932 | }
|
|---|
| 26933 |
|
|---|
| 26934 | {
|
|---|
| 26935 | onCommitRoot$1();
|
|---|
| 26936 | } // Always call this before exiting `commitRoot`, to ensure that any
|
|---|
| 26937 | // additional work on this root is scheduled.
|
|---|
| 26938 |
|
|---|
| 26939 |
|
|---|
| 26940 | ensureRootIsScheduled(root, now());
|
|---|
| 26941 |
|
|---|
| 26942 | if (recoverableErrors !== null) {
|
|---|
| 26943 | // There were errors during this render, but recovered from them without
|
|---|
| 26944 | // needing to surface it to the UI. We log them here.
|
|---|
| 26945 | var onRecoverableError = root.onRecoverableError;
|
|---|
| 26946 |
|
|---|
| 26947 | for (var i = 0; i < recoverableErrors.length; i++) {
|
|---|
| 26948 | var recoverableError = recoverableErrors[i];
|
|---|
| 26949 | var componentStack = recoverableError.stack;
|
|---|
| 26950 | var digest = recoverableError.digest;
|
|---|
| 26951 | onRecoverableError(recoverableError.value, {
|
|---|
| 26952 | componentStack: componentStack,
|
|---|
| 26953 | digest: digest
|
|---|
| 26954 | });
|
|---|
| 26955 | }
|
|---|
| 26956 | }
|
|---|
| 26957 |
|
|---|
| 26958 | if (hasUncaughtError) {
|
|---|
| 26959 | hasUncaughtError = false;
|
|---|
| 26960 | var error$1 = firstUncaughtError;
|
|---|
| 26961 | firstUncaughtError = null;
|
|---|
| 26962 | throw error$1;
|
|---|
| 26963 | } // If the passive effects are the result of a discrete render, flush them
|
|---|
| 26964 | // synchronously at the end of the current task so that the result is
|
|---|
| 26965 | // immediately observable. Otherwise, we assume that they are not
|
|---|
| 26966 | // order-dependent and do not need to be observed by external systems, so we
|
|---|
| 26967 | // can wait until after paint.
|
|---|
| 26968 | // TODO: We can optimize this by not scheduling the callback earlier. Since we
|
|---|
| 26969 | // currently schedule the callback in multiple places, will wait until those
|
|---|
| 26970 | // are consolidated.
|
|---|
| 26971 |
|
|---|
| 26972 |
|
|---|
| 26973 | if (includesSomeLane(pendingPassiveEffectsLanes, SyncLane) && root.tag !== LegacyRoot) {
|
|---|
| 26974 | flushPassiveEffects();
|
|---|
| 26975 | } // Read this again, since a passive effect might have updated it
|
|---|
| 26976 |
|
|---|
| 26977 |
|
|---|
| 26978 | remainingLanes = root.pendingLanes;
|
|---|
| 26979 |
|
|---|
| 26980 | if (includesSomeLane(remainingLanes, SyncLane)) {
|
|---|
| 26981 | {
|
|---|
| 26982 | markNestedUpdateScheduled();
|
|---|
| 26983 | } // Count the number of times the root synchronously re-renders without
|
|---|
| 26984 | // finishing. If there are too many, it indicates an infinite update loop.
|
|---|
| 26985 |
|
|---|
| 26986 |
|
|---|
| 26987 | if (root === rootWithNestedUpdates) {
|
|---|
| 26988 | nestedUpdateCount++;
|
|---|
| 26989 | } else {
|
|---|
| 26990 | nestedUpdateCount = 0;
|
|---|
| 26991 | rootWithNestedUpdates = root;
|
|---|
| 26992 | }
|
|---|
| 26993 | } else {
|
|---|
| 26994 | nestedUpdateCount = 0;
|
|---|
| 26995 | } // If layout work was scheduled, flush it now.
|
|---|
| 26996 |
|
|---|
| 26997 |
|
|---|
| 26998 | flushSyncCallbacks();
|
|---|
| 26999 |
|
|---|
| 27000 | {
|
|---|
| 27001 | markCommitStopped();
|
|---|
| 27002 | }
|
|---|
| 27003 |
|
|---|
| 27004 | return null;
|
|---|
| 27005 | }
|
|---|
| 27006 |
|
|---|
| 27007 | function flushPassiveEffects() {
|
|---|
| 27008 | // Returns whether passive effects were flushed.
|
|---|
| 27009 | // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should
|
|---|
| 27010 | // probably just combine the two functions. I believe they were only separate
|
|---|
| 27011 | // in the first place because we used to wrap it with
|
|---|
| 27012 | // `Scheduler.runWithPriority`, which accepts a function. But now we track the
|
|---|
| 27013 | // priority within React itself, so we can mutate the variable directly.
|
|---|
| 27014 | if (rootWithPendingPassiveEffects !== null) {
|
|---|
| 27015 | var renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes);
|
|---|
| 27016 | var priority = lowerEventPriority(DefaultEventPriority, renderPriority);
|
|---|
| 27017 | var prevTransition = ReactCurrentBatchConfig$3.transition;
|
|---|
| 27018 | var previousPriority = getCurrentUpdatePriority();
|
|---|
| 27019 |
|
|---|
| 27020 | try {
|
|---|
| 27021 | ReactCurrentBatchConfig$3.transition = null;
|
|---|
| 27022 | setCurrentUpdatePriority(priority);
|
|---|
| 27023 | return flushPassiveEffectsImpl();
|
|---|
| 27024 | } finally {
|
|---|
| 27025 | setCurrentUpdatePriority(previousPriority);
|
|---|
| 27026 | ReactCurrentBatchConfig$3.transition = prevTransition; // Once passive effects have run for the tree - giving components a
|
|---|
| 27027 | }
|
|---|
| 27028 | }
|
|---|
| 27029 |
|
|---|
| 27030 | return false;
|
|---|
| 27031 | }
|
|---|
| 27032 | function enqueuePendingPassiveProfilerEffect(fiber) {
|
|---|
| 27033 | {
|
|---|
| 27034 | pendingPassiveProfilerEffects.push(fiber);
|
|---|
| 27035 |
|
|---|
| 27036 | if (!rootDoesHavePassiveEffects) {
|
|---|
| 27037 | rootDoesHavePassiveEffects = true;
|
|---|
| 27038 | scheduleCallback$1(NormalPriority, function () {
|
|---|
| 27039 | flushPassiveEffects();
|
|---|
| 27040 | return null;
|
|---|
| 27041 | });
|
|---|
| 27042 | }
|
|---|
| 27043 | }
|
|---|
| 27044 | }
|
|---|
| 27045 |
|
|---|
| 27046 | function flushPassiveEffectsImpl() {
|
|---|
| 27047 | if (rootWithPendingPassiveEffects === null) {
|
|---|
| 27048 | return false;
|
|---|
| 27049 | } // Cache and clear the transitions flag
|
|---|
| 27050 |
|
|---|
| 27051 |
|
|---|
| 27052 | var transitions = pendingPassiveTransitions;
|
|---|
| 27053 | pendingPassiveTransitions = null;
|
|---|
| 27054 | var root = rootWithPendingPassiveEffects;
|
|---|
| 27055 | var lanes = pendingPassiveEffectsLanes;
|
|---|
| 27056 | rootWithPendingPassiveEffects = null; // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects.
|
|---|
| 27057 | // Figure out why and fix it. It's not causing any known issues (probably
|
|---|
| 27058 | // because it's only used for profiling), but it's a refactor hazard.
|
|---|
| 27059 |
|
|---|
| 27060 | pendingPassiveEffectsLanes = NoLanes;
|
|---|
| 27061 |
|
|---|
| 27062 | if ((executionContext & (RenderContext | CommitContext)) !== NoContext) {
|
|---|
| 27063 | throw new Error('Cannot flush passive effects while already rendering.');
|
|---|
| 27064 | }
|
|---|
| 27065 |
|
|---|
| 27066 | {
|
|---|
| 27067 | isFlushingPassiveEffects = true;
|
|---|
| 27068 | didScheduleUpdateDuringPassiveEffects = false;
|
|---|
| 27069 | }
|
|---|
| 27070 |
|
|---|
| 27071 | {
|
|---|
| 27072 | markPassiveEffectsStarted(lanes);
|
|---|
| 27073 | }
|
|---|
| 27074 |
|
|---|
| 27075 | var prevExecutionContext = executionContext;
|
|---|
| 27076 | executionContext |= CommitContext;
|
|---|
| 27077 | commitPassiveUnmountEffects(root.current);
|
|---|
| 27078 | commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects
|
|---|
| 27079 |
|
|---|
| 27080 | {
|
|---|
| 27081 | var profilerEffects = pendingPassiveProfilerEffects;
|
|---|
| 27082 | pendingPassiveProfilerEffects = [];
|
|---|
| 27083 |
|
|---|
| 27084 | for (var i = 0; i < profilerEffects.length; i++) {
|
|---|
| 27085 | var _fiber = profilerEffects[i];
|
|---|
| 27086 | commitPassiveEffectDurations(root, _fiber);
|
|---|
| 27087 | }
|
|---|
| 27088 | }
|
|---|
| 27089 |
|
|---|
| 27090 | {
|
|---|
| 27091 | markPassiveEffectsStopped();
|
|---|
| 27092 | }
|
|---|
| 27093 |
|
|---|
| 27094 | {
|
|---|
| 27095 | commitDoubleInvokeEffectsInDEV(root.current, true);
|
|---|
| 27096 | }
|
|---|
| 27097 |
|
|---|
| 27098 | executionContext = prevExecutionContext;
|
|---|
| 27099 | flushSyncCallbacks();
|
|---|
| 27100 |
|
|---|
| 27101 | {
|
|---|
| 27102 | // If additional passive effects were scheduled, increment a counter. If this
|
|---|
| 27103 | // exceeds the limit, we'll fire a warning.
|
|---|
| 27104 | if (didScheduleUpdateDuringPassiveEffects) {
|
|---|
| 27105 | if (root === rootWithPassiveNestedUpdates) {
|
|---|
| 27106 | nestedPassiveUpdateCount++;
|
|---|
| 27107 | } else {
|
|---|
| 27108 | nestedPassiveUpdateCount = 0;
|
|---|
| 27109 | rootWithPassiveNestedUpdates = root;
|
|---|
| 27110 | }
|
|---|
| 27111 | } else {
|
|---|
| 27112 | nestedPassiveUpdateCount = 0;
|
|---|
| 27113 | }
|
|---|
| 27114 |
|
|---|
| 27115 | isFlushingPassiveEffects = false;
|
|---|
| 27116 | didScheduleUpdateDuringPassiveEffects = false;
|
|---|
| 27117 | } // TODO: Move to commitPassiveMountEffects
|
|---|
| 27118 |
|
|---|
| 27119 |
|
|---|
| 27120 | onPostCommitRoot(root);
|
|---|
| 27121 |
|
|---|
| 27122 | {
|
|---|
| 27123 | var stateNode = root.current.stateNode;
|
|---|
| 27124 | stateNode.effectDuration = 0;
|
|---|
| 27125 | stateNode.passiveEffectDuration = 0;
|
|---|
| 27126 | }
|
|---|
| 27127 |
|
|---|
| 27128 | return true;
|
|---|
| 27129 | }
|
|---|
| 27130 |
|
|---|
| 27131 | function isAlreadyFailedLegacyErrorBoundary(instance) {
|
|---|
| 27132 | return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance);
|
|---|
| 27133 | }
|
|---|
| 27134 | function markLegacyErrorBoundaryAsFailed(instance) {
|
|---|
| 27135 | if (legacyErrorBoundariesThatAlreadyFailed === null) {
|
|---|
| 27136 | legacyErrorBoundariesThatAlreadyFailed = new Set([instance]);
|
|---|
| 27137 | } else {
|
|---|
| 27138 | legacyErrorBoundariesThatAlreadyFailed.add(instance);
|
|---|
| 27139 | }
|
|---|
| 27140 | }
|
|---|
| 27141 |
|
|---|
| 27142 | function prepareToThrowUncaughtError(error) {
|
|---|
| 27143 | if (!hasUncaughtError) {
|
|---|
| 27144 | hasUncaughtError = true;
|
|---|
| 27145 | firstUncaughtError = error;
|
|---|
| 27146 | }
|
|---|
| 27147 | }
|
|---|
| 27148 |
|
|---|
| 27149 | var onUncaughtError = prepareToThrowUncaughtError;
|
|---|
| 27150 |
|
|---|
| 27151 | function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) {
|
|---|
| 27152 | var errorInfo = createCapturedValueAtFiber(error, sourceFiber);
|
|---|
| 27153 | var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane);
|
|---|
| 27154 | var root = enqueueUpdate(rootFiber, update, SyncLane);
|
|---|
| 27155 | var eventTime = requestEventTime();
|
|---|
| 27156 |
|
|---|
| 27157 | if (root !== null) {
|
|---|
| 27158 | markRootUpdated(root, SyncLane, eventTime);
|
|---|
| 27159 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 27160 | }
|
|---|
| 27161 | }
|
|---|
| 27162 |
|
|---|
| 27163 | function captureCommitPhaseError(sourceFiber, nearestMountedAncestor, error$1) {
|
|---|
| 27164 | {
|
|---|
| 27165 | reportUncaughtErrorInDEV(error$1);
|
|---|
| 27166 | setIsRunningInsertionEffect(false);
|
|---|
| 27167 | }
|
|---|
| 27168 |
|
|---|
| 27169 | if (sourceFiber.tag === HostRoot) {
|
|---|
| 27170 | // Error was thrown at the root. There is no parent, so the root
|
|---|
| 27171 | // itself should capture it.
|
|---|
| 27172 | captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error$1);
|
|---|
| 27173 | return;
|
|---|
| 27174 | }
|
|---|
| 27175 |
|
|---|
| 27176 | var fiber = null;
|
|---|
| 27177 |
|
|---|
| 27178 | {
|
|---|
| 27179 | fiber = nearestMountedAncestor;
|
|---|
| 27180 | }
|
|---|
| 27181 |
|
|---|
| 27182 | while (fiber !== null) {
|
|---|
| 27183 | if (fiber.tag === HostRoot) {
|
|---|
| 27184 | captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error$1);
|
|---|
| 27185 | return;
|
|---|
| 27186 | } else if (fiber.tag === ClassComponent) {
|
|---|
| 27187 | var ctor = fiber.type;
|
|---|
| 27188 | var instance = fiber.stateNode;
|
|---|
| 27189 |
|
|---|
| 27190 | if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) {
|
|---|
| 27191 | var errorInfo = createCapturedValueAtFiber(error$1, sourceFiber);
|
|---|
| 27192 | var update = createClassErrorUpdate(fiber, errorInfo, SyncLane);
|
|---|
| 27193 | var root = enqueueUpdate(fiber, update, SyncLane);
|
|---|
| 27194 | var eventTime = requestEventTime();
|
|---|
| 27195 |
|
|---|
| 27196 | if (root !== null) {
|
|---|
| 27197 | markRootUpdated(root, SyncLane, eventTime);
|
|---|
| 27198 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 27199 | }
|
|---|
| 27200 |
|
|---|
| 27201 | return;
|
|---|
| 27202 | }
|
|---|
| 27203 | }
|
|---|
| 27204 |
|
|---|
| 27205 | fiber = fiber.return;
|
|---|
| 27206 | }
|
|---|
| 27207 |
|
|---|
| 27208 | {
|
|---|
| 27209 | // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning
|
|---|
| 27210 | // will fire for errors that are thrown by destroy functions inside deleted
|
|---|
| 27211 | // trees. What it should instead do is propagate the error to the parent of
|
|---|
| 27212 | // the deleted tree. In the meantime, do not add this warning to the
|
|---|
| 27213 | // allowlist; this is only for our internal use.
|
|---|
| 27214 | error('Internal React error: Attempted to capture a commit phase error ' + 'inside a detached tree. This indicates a bug in React. Likely ' + 'causes include deleting the same fiber more than once, committing an ' + 'already-finished tree, or an inconsistent return pointer.\n\n' + 'Error message:\n\n%s', error$1);
|
|---|
| 27215 | }
|
|---|
| 27216 | }
|
|---|
| 27217 | function pingSuspendedRoot(root, wakeable, pingedLanes) {
|
|---|
| 27218 | var pingCache = root.pingCache;
|
|---|
| 27219 |
|
|---|
| 27220 | if (pingCache !== null) {
|
|---|
| 27221 | // The wakeable resolved, so we no longer need to memoize, because it will
|
|---|
| 27222 | // never be thrown again.
|
|---|
| 27223 | pingCache.delete(wakeable);
|
|---|
| 27224 | }
|
|---|
| 27225 |
|
|---|
| 27226 | var eventTime = requestEventTime();
|
|---|
| 27227 | markRootPinged(root, pingedLanes);
|
|---|
| 27228 | warnIfSuspenseResolutionNotWrappedWithActDEV(root);
|
|---|
| 27229 |
|
|---|
| 27230 | if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) {
|
|---|
| 27231 | // Received a ping at the same priority level at which we're currently
|
|---|
| 27232 | // rendering. We might want to restart this render. This should mirror
|
|---|
| 27233 | // the logic of whether or not a root suspends once it completes.
|
|---|
| 27234 | // TODO: If we're rendering sync either due to Sync, Batched or expired,
|
|---|
| 27235 | // we should probably never restart.
|
|---|
| 27236 | // If we're suspended with delay, or if it's a retry, we'll always suspend
|
|---|
| 27237 | // so we can always restart.
|
|---|
| 27238 | if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) {
|
|---|
| 27239 | // Restart from the root.
|
|---|
| 27240 | prepareFreshStack(root, NoLanes);
|
|---|
| 27241 | } else {
|
|---|
| 27242 | // Even though we can't restart right now, we might get an
|
|---|
| 27243 | // opportunity later. So we mark this render as having a ping.
|
|---|
| 27244 | workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes);
|
|---|
| 27245 | }
|
|---|
| 27246 | }
|
|---|
| 27247 |
|
|---|
| 27248 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 27249 | }
|
|---|
| 27250 |
|
|---|
| 27251 | function retryTimedOutBoundary(boundaryFiber, retryLane) {
|
|---|
| 27252 | // The boundary fiber (a Suspense component or SuspenseList component)
|
|---|
| 27253 | // previously was rendered in its fallback state. One of the promises that
|
|---|
| 27254 | // suspended it has resolved, which means at least part of the tree was
|
|---|
| 27255 | // likely unblocked. Try rendering again, at a new lanes.
|
|---|
| 27256 | if (retryLane === NoLane) {
|
|---|
| 27257 | // TODO: Assign this to `suspenseState.retryLane`? to avoid
|
|---|
| 27258 | // unnecessary entanglement?
|
|---|
| 27259 | retryLane = requestRetryLane(boundaryFiber);
|
|---|
| 27260 | } // TODO: Special case idle priority?
|
|---|
| 27261 |
|
|---|
| 27262 |
|
|---|
| 27263 | var eventTime = requestEventTime();
|
|---|
| 27264 | var root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane);
|
|---|
| 27265 |
|
|---|
| 27266 | if (root !== null) {
|
|---|
| 27267 | markRootUpdated(root, retryLane, eventTime);
|
|---|
| 27268 | ensureRootIsScheduled(root, eventTime);
|
|---|
| 27269 | }
|
|---|
| 27270 | }
|
|---|
| 27271 |
|
|---|
| 27272 | function retryDehydratedSuspenseBoundary(boundaryFiber) {
|
|---|
| 27273 | var suspenseState = boundaryFiber.memoizedState;
|
|---|
| 27274 | var retryLane = NoLane;
|
|---|
| 27275 |
|
|---|
| 27276 | if (suspenseState !== null) {
|
|---|
| 27277 | retryLane = suspenseState.retryLane;
|
|---|
| 27278 | }
|
|---|
| 27279 |
|
|---|
| 27280 | retryTimedOutBoundary(boundaryFiber, retryLane);
|
|---|
| 27281 | }
|
|---|
| 27282 | function resolveRetryWakeable(boundaryFiber, wakeable) {
|
|---|
| 27283 | var retryLane = NoLane; // Default
|
|---|
| 27284 |
|
|---|
| 27285 | var retryCache;
|
|---|
| 27286 |
|
|---|
| 27287 | switch (boundaryFiber.tag) {
|
|---|
| 27288 | case SuspenseComponent:
|
|---|
| 27289 | retryCache = boundaryFiber.stateNode;
|
|---|
| 27290 | var suspenseState = boundaryFiber.memoizedState;
|
|---|
| 27291 |
|
|---|
| 27292 | if (suspenseState !== null) {
|
|---|
| 27293 | retryLane = suspenseState.retryLane;
|
|---|
| 27294 | }
|
|---|
| 27295 |
|
|---|
| 27296 | break;
|
|---|
| 27297 |
|
|---|
| 27298 | case SuspenseListComponent:
|
|---|
| 27299 | retryCache = boundaryFiber.stateNode;
|
|---|
| 27300 | break;
|
|---|
| 27301 |
|
|---|
| 27302 | default:
|
|---|
| 27303 | throw new Error('Pinged unknown suspense boundary type. ' + 'This is probably a bug in React.');
|
|---|
| 27304 | }
|
|---|
| 27305 |
|
|---|
| 27306 | if (retryCache !== null) {
|
|---|
| 27307 | // The wakeable resolved, so we no longer need to memoize, because it will
|
|---|
| 27308 | // never be thrown again.
|
|---|
| 27309 | retryCache.delete(wakeable);
|
|---|
| 27310 | }
|
|---|
| 27311 |
|
|---|
| 27312 | retryTimedOutBoundary(boundaryFiber, retryLane);
|
|---|
| 27313 | } // Computes the next Just Noticeable Difference (JND) boundary.
|
|---|
| 27314 | // The theory is that a person can't tell the difference between small differences in time.
|
|---|
| 27315 | // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable
|
|---|
| 27316 | // difference in the experience. However, waiting for longer might mean that we can avoid
|
|---|
| 27317 | // showing an intermediate loading state. The longer we have already waited, the harder it
|
|---|
| 27318 | // is to tell small differences in time. Therefore, the longer we've already waited,
|
|---|
| 27319 | // the longer we can wait additionally. At some point we have to give up though.
|
|---|
| 27320 | // We pick a train model where the next boundary commits at a consistent schedule.
|
|---|
| 27321 | // These particular numbers are vague estimates. We expect to adjust them based on research.
|
|---|
| 27322 |
|
|---|
| 27323 | function jnd(timeElapsed) {
|
|---|
| 27324 | return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960;
|
|---|
| 27325 | }
|
|---|
| 27326 |
|
|---|
| 27327 | function checkForNestedUpdates() {
|
|---|
| 27328 | if (nestedUpdateCount > NESTED_UPDATE_LIMIT) {
|
|---|
| 27329 | nestedUpdateCount = 0;
|
|---|
| 27330 | rootWithNestedUpdates = null;
|
|---|
| 27331 | throw new Error('Maximum update depth exceeded. This can happen when a component ' + 'repeatedly calls setState inside componentWillUpdate or ' + 'componentDidUpdate. React limits the number of nested updates to ' + 'prevent infinite loops.');
|
|---|
| 27332 | }
|
|---|
| 27333 |
|
|---|
| 27334 | {
|
|---|
| 27335 | if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) {
|
|---|
| 27336 | nestedPassiveUpdateCount = 0;
|
|---|
| 27337 | rootWithPassiveNestedUpdates = null;
|
|---|
| 27338 |
|
|---|
| 27339 | error('Maximum update depth exceeded. This can happen when a component ' + "calls setState inside useEffect, but useEffect either doesn't " + 'have a dependency array, or one of the dependencies changes on ' + 'every render.');
|
|---|
| 27340 | }
|
|---|
| 27341 | }
|
|---|
| 27342 | }
|
|---|
| 27343 |
|
|---|
| 27344 | function flushRenderPhaseStrictModeWarningsInDEV() {
|
|---|
| 27345 | {
|
|---|
| 27346 | ReactStrictModeWarnings.flushLegacyContextWarning();
|
|---|
| 27347 |
|
|---|
| 27348 | {
|
|---|
| 27349 | ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings();
|
|---|
| 27350 | }
|
|---|
| 27351 | }
|
|---|
| 27352 | }
|
|---|
| 27353 |
|
|---|
| 27354 | function commitDoubleInvokeEffectsInDEV(fiber, hasPassiveEffects) {
|
|---|
| 27355 | {
|
|---|
| 27356 | // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects
|
|---|
| 27357 | // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level.
|
|---|
| 27358 | // Maybe not a big deal since this is DEV only behavior.
|
|---|
| 27359 | setCurrentFiber(fiber);
|
|---|
| 27360 | invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV);
|
|---|
| 27361 |
|
|---|
| 27362 | if (hasPassiveEffects) {
|
|---|
| 27363 | invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV);
|
|---|
| 27364 | }
|
|---|
| 27365 |
|
|---|
| 27366 | invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV);
|
|---|
| 27367 |
|
|---|
| 27368 | if (hasPassiveEffects) {
|
|---|
| 27369 | invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV);
|
|---|
| 27370 | }
|
|---|
| 27371 |
|
|---|
| 27372 | resetCurrentFiber();
|
|---|
| 27373 | }
|
|---|
| 27374 | }
|
|---|
| 27375 |
|
|---|
| 27376 | function invokeEffectsInDev(firstChild, fiberFlags, invokeEffectFn) {
|
|---|
| 27377 | {
|
|---|
| 27378 | // We don't need to re-check StrictEffectsMode here.
|
|---|
| 27379 | // This function is only called if that check has already passed.
|
|---|
| 27380 | var current = firstChild;
|
|---|
| 27381 | var subtreeRoot = null;
|
|---|
| 27382 |
|
|---|
| 27383 | while (current !== null) {
|
|---|
| 27384 | var primarySubtreeFlag = current.subtreeFlags & fiberFlags;
|
|---|
| 27385 |
|
|---|
| 27386 | if (current !== subtreeRoot && current.child !== null && primarySubtreeFlag !== NoFlags) {
|
|---|
| 27387 | current = current.child;
|
|---|
| 27388 | } else {
|
|---|
| 27389 | if ((current.flags & fiberFlags) !== NoFlags) {
|
|---|
| 27390 | invokeEffectFn(current);
|
|---|
| 27391 | }
|
|---|
| 27392 |
|
|---|
| 27393 | if (current.sibling !== null) {
|
|---|
| 27394 | current = current.sibling;
|
|---|
| 27395 | } else {
|
|---|
| 27396 | current = subtreeRoot = current.return;
|
|---|
| 27397 | }
|
|---|
| 27398 | }
|
|---|
| 27399 | }
|
|---|
| 27400 | }
|
|---|
| 27401 | }
|
|---|
| 27402 |
|
|---|
| 27403 | var didWarnStateUpdateForNotYetMountedComponent = null;
|
|---|
| 27404 | function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) {
|
|---|
| 27405 | {
|
|---|
| 27406 | if ((executionContext & RenderContext) !== NoContext) {
|
|---|
| 27407 | // We let the other warning about render phase updates deal with this one.
|
|---|
| 27408 | return;
|
|---|
| 27409 | }
|
|---|
| 27410 |
|
|---|
| 27411 | if (!(fiber.mode & ConcurrentMode)) {
|
|---|
| 27412 | return;
|
|---|
| 27413 | }
|
|---|
| 27414 |
|
|---|
| 27415 | var tag = fiber.tag;
|
|---|
| 27416 |
|
|---|
| 27417 | if (tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent) {
|
|---|
| 27418 | // Only warn for user-defined components, not internal ones like Suspense.
|
|---|
| 27419 | return;
|
|---|
| 27420 | } // We show the whole stack but dedupe on the top component's name because
|
|---|
| 27421 | // the problematic code almost always lies inside that component.
|
|---|
| 27422 |
|
|---|
| 27423 |
|
|---|
| 27424 | var componentName = getComponentNameFromFiber(fiber) || 'ReactComponent';
|
|---|
| 27425 |
|
|---|
| 27426 | if (didWarnStateUpdateForNotYetMountedComponent !== null) {
|
|---|
| 27427 | if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) {
|
|---|
| 27428 | return;
|
|---|
| 27429 | }
|
|---|
| 27430 |
|
|---|
| 27431 | didWarnStateUpdateForNotYetMountedComponent.add(componentName);
|
|---|
| 27432 | } else {
|
|---|
| 27433 | didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]);
|
|---|
| 27434 | }
|
|---|
| 27435 |
|
|---|
| 27436 | var previousFiber = current;
|
|---|
| 27437 |
|
|---|
| 27438 | try {
|
|---|
| 27439 | setCurrentFiber(fiber);
|
|---|
| 27440 |
|
|---|
| 27441 | error("Can't perform a React state update on a component that hasn't mounted yet. " + 'This indicates that you have a side-effect in your render function that ' + 'asynchronously later calls tries to update the component. Move this work to ' + 'useEffect instead.');
|
|---|
| 27442 | } finally {
|
|---|
| 27443 | if (previousFiber) {
|
|---|
| 27444 | setCurrentFiber(fiber);
|
|---|
| 27445 | } else {
|
|---|
| 27446 | resetCurrentFiber();
|
|---|
| 27447 | }
|
|---|
| 27448 | }
|
|---|
| 27449 | }
|
|---|
| 27450 | }
|
|---|
| 27451 | var beginWork$1;
|
|---|
| 27452 |
|
|---|
| 27453 | {
|
|---|
| 27454 | var dummyFiber = null;
|
|---|
| 27455 |
|
|---|
| 27456 | beginWork$1 = function (current, unitOfWork, lanes) {
|
|---|
| 27457 | // If a component throws an error, we replay it again in a synchronously
|
|---|
| 27458 | // dispatched event, so that the debugger will treat it as an uncaught
|
|---|
| 27459 | // error See ReactErrorUtils for more information.
|
|---|
| 27460 | // Before entering the begin phase, copy the work-in-progress onto a dummy
|
|---|
| 27461 | // fiber. If beginWork throws, we'll use this to reset the state.
|
|---|
| 27462 | var originalWorkInProgressCopy = assignFiberPropertiesInDEV(dummyFiber, unitOfWork);
|
|---|
| 27463 |
|
|---|
| 27464 | try {
|
|---|
| 27465 | return beginWork(current, unitOfWork, lanes);
|
|---|
| 27466 | } catch (originalError) {
|
|---|
| 27467 | if (didSuspendOrErrorWhileHydratingDEV() || originalError !== null && typeof originalError === 'object' && typeof originalError.then === 'function') {
|
|---|
| 27468 | // Don't replay promises.
|
|---|
| 27469 | // Don't replay errors if we are hydrating and have already suspended or handled an error
|
|---|
| 27470 | throw originalError;
|
|---|
| 27471 | } // Keep this code in sync with handleError; any changes here must have
|
|---|
| 27472 | // corresponding changes there.
|
|---|
| 27473 |
|
|---|
| 27474 |
|
|---|
| 27475 | resetContextDependencies();
|
|---|
| 27476 | resetHooksAfterThrow(); // Don't reset current debug fiber, since we're about to work on the
|
|---|
| 27477 | // same fiber again.
|
|---|
| 27478 | // Unwind the failed stack frame
|
|---|
| 27479 |
|
|---|
| 27480 | unwindInterruptedWork(current, unitOfWork); // Restore the original properties of the fiber.
|
|---|
| 27481 |
|
|---|
| 27482 | assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy);
|
|---|
| 27483 |
|
|---|
| 27484 | if ( unitOfWork.mode & ProfileMode) {
|
|---|
| 27485 | // Reset the profiler timer.
|
|---|
| 27486 | startProfilerTimer(unitOfWork);
|
|---|
| 27487 | } // Run beginWork again.
|
|---|
| 27488 |
|
|---|
| 27489 |
|
|---|
| 27490 | invokeGuardedCallback(null, beginWork, null, current, unitOfWork, lanes);
|
|---|
| 27491 |
|
|---|
| 27492 | if (hasCaughtError()) {
|
|---|
| 27493 | var replayError = clearCaughtError();
|
|---|
| 27494 |
|
|---|
| 27495 | if (typeof replayError === 'object' && replayError !== null && replayError._suppressLogging && typeof originalError === 'object' && originalError !== null && !originalError._suppressLogging) {
|
|---|
| 27496 | // If suppressed, let the flag carry over to the original error which is the one we'll rethrow.
|
|---|
| 27497 | originalError._suppressLogging = true;
|
|---|
| 27498 | }
|
|---|
| 27499 | } // We always throw the original error in case the second render pass is not idempotent.
|
|---|
| 27500 | // This can happen if a memoized function or CommonJS module doesn't throw after first invocation.
|
|---|
| 27501 |
|
|---|
| 27502 |
|
|---|
| 27503 | throw originalError;
|
|---|
| 27504 | }
|
|---|
| 27505 | };
|
|---|
| 27506 | }
|
|---|
| 27507 |
|
|---|
| 27508 | var didWarnAboutUpdateInRender = false;
|
|---|
| 27509 | var didWarnAboutUpdateInRenderForAnotherComponent;
|
|---|
| 27510 |
|
|---|
| 27511 | {
|
|---|
| 27512 | didWarnAboutUpdateInRenderForAnotherComponent = new Set();
|
|---|
| 27513 | }
|
|---|
| 27514 |
|
|---|
| 27515 | function warnAboutRenderPhaseUpdatesInDEV(fiber) {
|
|---|
| 27516 | {
|
|---|
| 27517 | if (isRendering && !getIsUpdatingOpaqueValueInRenderPhaseInDEV()) {
|
|---|
| 27518 | switch (fiber.tag) {
|
|---|
| 27519 | case FunctionComponent:
|
|---|
| 27520 | case ForwardRef:
|
|---|
| 27521 | case SimpleMemoComponent:
|
|---|
| 27522 | {
|
|---|
| 27523 | var renderingComponentName = workInProgress && getComponentNameFromFiber(workInProgress) || 'Unknown'; // Dedupe by the rendering component because it's the one that needs to be fixed.
|
|---|
| 27524 |
|
|---|
| 27525 | var dedupeKey = renderingComponentName;
|
|---|
| 27526 |
|
|---|
| 27527 | if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) {
|
|---|
| 27528 | didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey);
|
|---|
| 27529 | var setStateComponentName = getComponentNameFromFiber(fiber) || 'Unknown';
|
|---|
| 27530 |
|
|---|
| 27531 | error('Cannot update a component (`%s`) while rendering a ' + 'different component (`%s`). To locate the bad setState() call inside `%s`, ' + 'follow the stack trace as described in https://reactjs.org/link/setstate-in-render', setStateComponentName, renderingComponentName, renderingComponentName);
|
|---|
| 27532 | }
|
|---|
| 27533 |
|
|---|
| 27534 | break;
|
|---|
| 27535 | }
|
|---|
| 27536 |
|
|---|
| 27537 | case ClassComponent:
|
|---|
| 27538 | {
|
|---|
| 27539 | if (!didWarnAboutUpdateInRender) {
|
|---|
| 27540 | error('Cannot update during an existing state transition (such as ' + 'within `render`). Render methods should be a pure ' + 'function of props and state.');
|
|---|
| 27541 |
|
|---|
| 27542 | didWarnAboutUpdateInRender = true;
|
|---|
| 27543 | }
|
|---|
| 27544 |
|
|---|
| 27545 | break;
|
|---|
| 27546 | }
|
|---|
| 27547 | }
|
|---|
| 27548 | }
|
|---|
| 27549 | }
|
|---|
| 27550 | }
|
|---|
| 27551 |
|
|---|
| 27552 | function restorePendingUpdaters(root, lanes) {
|
|---|
| 27553 | {
|
|---|
| 27554 | if (isDevToolsPresent) {
|
|---|
| 27555 | var memoizedUpdaters = root.memoizedUpdaters;
|
|---|
| 27556 | memoizedUpdaters.forEach(function (schedulingFiber) {
|
|---|
| 27557 | addFiberToLanesMap(root, schedulingFiber, lanes);
|
|---|
| 27558 | }); // This function intentionally does not clear memoized updaters.
|
|---|
| 27559 | // Those may still be relevant to the current commit
|
|---|
| 27560 | // and a future one (e.g. Suspense).
|
|---|
| 27561 | }
|
|---|
| 27562 | }
|
|---|
| 27563 | }
|
|---|
| 27564 | var fakeActCallbackNode = {};
|
|---|
| 27565 |
|
|---|
| 27566 | function scheduleCallback$1(priorityLevel, callback) {
|
|---|
| 27567 | {
|
|---|
| 27568 | // If we're currently inside an `act` scope, bypass Scheduler and push to
|
|---|
| 27569 | // the `act` queue instead.
|
|---|
| 27570 | var actQueue = ReactCurrentActQueue$1.current;
|
|---|
| 27571 |
|
|---|
| 27572 | if (actQueue !== null) {
|
|---|
| 27573 | actQueue.push(callback);
|
|---|
| 27574 | return fakeActCallbackNode;
|
|---|
| 27575 | } else {
|
|---|
| 27576 | return scheduleCallback(priorityLevel, callback);
|
|---|
| 27577 | }
|
|---|
| 27578 | }
|
|---|
| 27579 | }
|
|---|
| 27580 |
|
|---|
| 27581 | function cancelCallback$1(callbackNode) {
|
|---|
| 27582 | if ( callbackNode === fakeActCallbackNode) {
|
|---|
| 27583 | return;
|
|---|
| 27584 | } // In production, always call Scheduler. This function will be stripped out.
|
|---|
| 27585 |
|
|---|
| 27586 |
|
|---|
| 27587 | return cancelCallback(callbackNode);
|
|---|
| 27588 | }
|
|---|
| 27589 |
|
|---|
| 27590 | function shouldForceFlushFallbacksInDEV() {
|
|---|
| 27591 | // Never force flush in production. This function should get stripped out.
|
|---|
| 27592 | return ReactCurrentActQueue$1.current !== null;
|
|---|
| 27593 | }
|
|---|
| 27594 |
|
|---|
| 27595 | function warnIfUpdatesNotWrappedWithActDEV(fiber) {
|
|---|
| 27596 | {
|
|---|
| 27597 | if (fiber.mode & ConcurrentMode) {
|
|---|
| 27598 | if (!isConcurrentActEnvironment()) {
|
|---|
| 27599 | // Not in an act environment. No need to warn.
|
|---|
| 27600 | return;
|
|---|
| 27601 | }
|
|---|
| 27602 | } else {
|
|---|
| 27603 | // Legacy mode has additional cases where we suppress a warning.
|
|---|
| 27604 | if (!isLegacyActEnvironment()) {
|
|---|
| 27605 | // Not in an act environment. No need to warn.
|
|---|
| 27606 | return;
|
|---|
| 27607 | }
|
|---|
| 27608 |
|
|---|
| 27609 | if (executionContext !== NoContext) {
|
|---|
| 27610 | // Legacy mode doesn't warn if the update is batched, i.e.
|
|---|
| 27611 | // batchedUpdates or flushSync.
|
|---|
| 27612 | return;
|
|---|
| 27613 | }
|
|---|
| 27614 |
|
|---|
| 27615 | if (fiber.tag !== FunctionComponent && fiber.tag !== ForwardRef && fiber.tag !== SimpleMemoComponent) {
|
|---|
| 27616 | // For backwards compatibility with pre-hooks code, legacy mode only
|
|---|
| 27617 | // warns for updates that originate from a hook.
|
|---|
| 27618 | return;
|
|---|
| 27619 | }
|
|---|
| 27620 | }
|
|---|
| 27621 |
|
|---|
| 27622 | if (ReactCurrentActQueue$1.current === null) {
|
|---|
| 27623 | var previousFiber = current;
|
|---|
| 27624 |
|
|---|
| 27625 | try {
|
|---|
| 27626 | setCurrentFiber(fiber);
|
|---|
| 27627 |
|
|---|
| 27628 | error('An update to %s inside a test was not wrapped in act(...).\n\n' + 'When testing, code that causes React state updates should be ' + 'wrapped into act(...):\n\n' + 'act(() => {\n' + ' /* fire events that update state */\n' + '});\n' + '/* assert on the output */\n\n' + "This ensures that you're testing the behavior the user would see " + 'in the browser.' + ' Learn more at https://reactjs.org/link/wrap-tests-with-act', getComponentNameFromFiber(fiber));
|
|---|
| 27629 | } finally {
|
|---|
| 27630 | if (previousFiber) {
|
|---|
| 27631 | setCurrentFiber(fiber);
|
|---|
| 27632 | } else {
|
|---|
| 27633 | resetCurrentFiber();
|
|---|
| 27634 | }
|
|---|
| 27635 | }
|
|---|
| 27636 | }
|
|---|
| 27637 | }
|
|---|
| 27638 | }
|
|---|
| 27639 |
|
|---|
| 27640 | function warnIfSuspenseResolutionNotWrappedWithActDEV(root) {
|
|---|
| 27641 | {
|
|---|
| 27642 | if (root.tag !== LegacyRoot && isConcurrentActEnvironment() && ReactCurrentActQueue$1.current === null) {
|
|---|
| 27643 | error('A suspended resource finished loading inside a test, but the event ' + 'was not wrapped in act(...).\n\n' + 'When testing, code that resolves suspended data should be wrapped ' + 'into act(...):\n\n' + 'act(() => {\n' + ' /* finish loading suspended data */\n' + '});\n' + '/* assert on the output */\n\n' + "This ensures that you're testing the behavior the user would see " + 'in the browser.' + ' Learn more at https://reactjs.org/link/wrap-tests-with-act');
|
|---|
| 27644 | }
|
|---|
| 27645 | }
|
|---|
| 27646 | }
|
|---|
| 27647 |
|
|---|
| 27648 | function setIsRunningInsertionEffect(isRunning) {
|
|---|
| 27649 | {
|
|---|
| 27650 | isRunningInsertionEffect = isRunning;
|
|---|
| 27651 | }
|
|---|
| 27652 | }
|
|---|
| 27653 |
|
|---|
| 27654 | /* eslint-disable react-internal/prod-error-codes */
|
|---|
| 27655 | var resolveFamily = null; // $FlowFixMe Flow gets confused by a WeakSet feature check below.
|
|---|
| 27656 |
|
|---|
| 27657 | var failedBoundaries = null;
|
|---|
| 27658 | var setRefreshHandler = function (handler) {
|
|---|
| 27659 | {
|
|---|
| 27660 | resolveFamily = handler;
|
|---|
| 27661 | }
|
|---|
| 27662 | };
|
|---|
| 27663 | function resolveFunctionForHotReloading(type) {
|
|---|
| 27664 | {
|
|---|
| 27665 | if (resolveFamily === null) {
|
|---|
| 27666 | // Hot reloading is disabled.
|
|---|
| 27667 | return type;
|
|---|
| 27668 | }
|
|---|
| 27669 |
|
|---|
| 27670 | var family = resolveFamily(type);
|
|---|
| 27671 |
|
|---|
| 27672 | if (family === undefined) {
|
|---|
| 27673 | return type;
|
|---|
| 27674 | } // Use the latest known implementation.
|
|---|
| 27675 |
|
|---|
| 27676 |
|
|---|
| 27677 | return family.current;
|
|---|
| 27678 | }
|
|---|
| 27679 | }
|
|---|
| 27680 | function resolveClassForHotReloading(type) {
|
|---|
| 27681 | // No implementation differences.
|
|---|
| 27682 | return resolveFunctionForHotReloading(type);
|
|---|
| 27683 | }
|
|---|
| 27684 | function resolveForwardRefForHotReloading(type) {
|
|---|
| 27685 | {
|
|---|
| 27686 | if (resolveFamily === null) {
|
|---|
| 27687 | // Hot reloading is disabled.
|
|---|
| 27688 | return type;
|
|---|
| 27689 | }
|
|---|
| 27690 |
|
|---|
| 27691 | var family = resolveFamily(type);
|
|---|
| 27692 |
|
|---|
| 27693 | if (family === undefined) {
|
|---|
| 27694 | // Check if we're dealing with a real forwardRef. Don't want to crash early.
|
|---|
| 27695 | if (type !== null && type !== undefined && typeof type.render === 'function') {
|
|---|
| 27696 | // ForwardRef is special because its resolved .type is an object,
|
|---|
| 27697 | // but it's possible that we only have its inner render function in the map.
|
|---|
| 27698 | // If that inner render function is different, we'll build a new forwardRef type.
|
|---|
| 27699 | var currentRender = resolveFunctionForHotReloading(type.render);
|
|---|
| 27700 |
|
|---|
| 27701 | if (type.render !== currentRender) {
|
|---|
| 27702 | var syntheticType = {
|
|---|
| 27703 | $$typeof: REACT_FORWARD_REF_TYPE,
|
|---|
| 27704 | render: currentRender
|
|---|
| 27705 | };
|
|---|
| 27706 |
|
|---|
| 27707 | if (type.displayName !== undefined) {
|
|---|
| 27708 | syntheticType.displayName = type.displayName;
|
|---|
| 27709 | }
|
|---|
| 27710 |
|
|---|
| 27711 | return syntheticType;
|
|---|
| 27712 | }
|
|---|
| 27713 | }
|
|---|
| 27714 |
|
|---|
| 27715 | return type;
|
|---|
| 27716 | } // Use the latest known implementation.
|
|---|
| 27717 |
|
|---|
| 27718 |
|
|---|
| 27719 | return family.current;
|
|---|
| 27720 | }
|
|---|
| 27721 | }
|
|---|
| 27722 | function isCompatibleFamilyForHotReloading(fiber, element) {
|
|---|
| 27723 | {
|
|---|
| 27724 | if (resolveFamily === null) {
|
|---|
| 27725 | // Hot reloading is disabled.
|
|---|
| 27726 | return false;
|
|---|
| 27727 | }
|
|---|
| 27728 |
|
|---|
| 27729 | var prevType = fiber.elementType;
|
|---|
| 27730 | var nextType = element.type; // If we got here, we know types aren't === equal.
|
|---|
| 27731 |
|
|---|
| 27732 | var needsCompareFamilies = false;
|
|---|
| 27733 | var $$typeofNextType = typeof nextType === 'object' && nextType !== null ? nextType.$$typeof : null;
|
|---|
| 27734 |
|
|---|
| 27735 | switch (fiber.tag) {
|
|---|
| 27736 | case ClassComponent:
|
|---|
| 27737 | {
|
|---|
| 27738 | if (typeof nextType === 'function') {
|
|---|
| 27739 | needsCompareFamilies = true;
|
|---|
| 27740 | }
|
|---|
| 27741 |
|
|---|
| 27742 | break;
|
|---|
| 27743 | }
|
|---|
| 27744 |
|
|---|
| 27745 | case FunctionComponent:
|
|---|
| 27746 | {
|
|---|
| 27747 | if (typeof nextType === 'function') {
|
|---|
| 27748 | needsCompareFamilies = true;
|
|---|
| 27749 | } else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
|---|
| 27750 | // We don't know the inner type yet.
|
|---|
| 27751 | // We're going to assume that the lazy inner type is stable,
|
|---|
| 27752 | // and so it is sufficient to avoid reconciling it away.
|
|---|
| 27753 | // We're not going to unwrap or actually use the new lazy type.
|
|---|
| 27754 | needsCompareFamilies = true;
|
|---|
| 27755 | }
|
|---|
| 27756 |
|
|---|
| 27757 | break;
|
|---|
| 27758 | }
|
|---|
| 27759 |
|
|---|
| 27760 | case ForwardRef:
|
|---|
| 27761 | {
|
|---|
| 27762 | if ($$typeofNextType === REACT_FORWARD_REF_TYPE) {
|
|---|
| 27763 | needsCompareFamilies = true;
|
|---|
| 27764 | } else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
|---|
| 27765 | needsCompareFamilies = true;
|
|---|
| 27766 | }
|
|---|
| 27767 |
|
|---|
| 27768 | break;
|
|---|
| 27769 | }
|
|---|
| 27770 |
|
|---|
| 27771 | case MemoComponent:
|
|---|
| 27772 | case SimpleMemoComponent:
|
|---|
| 27773 | {
|
|---|
| 27774 | if ($$typeofNextType === REACT_MEMO_TYPE) {
|
|---|
| 27775 | // TODO: if it was but can no longer be simple,
|
|---|
| 27776 | // we shouldn't set this.
|
|---|
| 27777 | needsCompareFamilies = true;
|
|---|
| 27778 | } else if ($$typeofNextType === REACT_LAZY_TYPE) {
|
|---|
| 27779 | needsCompareFamilies = true;
|
|---|
| 27780 | }
|
|---|
| 27781 |
|
|---|
| 27782 | break;
|
|---|
| 27783 | }
|
|---|
| 27784 |
|
|---|
| 27785 | default:
|
|---|
| 27786 | return false;
|
|---|
| 27787 | } // Check if both types have a family and it's the same one.
|
|---|
| 27788 |
|
|---|
| 27789 |
|
|---|
| 27790 | if (needsCompareFamilies) {
|
|---|
| 27791 | // Note: memo() and forwardRef() we'll compare outer rather than inner type.
|
|---|
| 27792 | // This means both of them need to be registered to preserve state.
|
|---|
| 27793 | // If we unwrapped and compared the inner types for wrappers instead,
|
|---|
| 27794 | // then we would risk falsely saying two separate memo(Foo)
|
|---|
| 27795 | // calls are equivalent because they wrap the same Foo function.
|
|---|
| 27796 | var prevFamily = resolveFamily(prevType);
|
|---|
| 27797 |
|
|---|
| 27798 | if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) {
|
|---|
| 27799 | return true;
|
|---|
| 27800 | }
|
|---|
| 27801 | }
|
|---|
| 27802 |
|
|---|
| 27803 | return false;
|
|---|
| 27804 | }
|
|---|
| 27805 | }
|
|---|
| 27806 | function markFailedErrorBoundaryForHotReloading(fiber) {
|
|---|
| 27807 | {
|
|---|
| 27808 | if (resolveFamily === null) {
|
|---|
| 27809 | // Hot reloading is disabled.
|
|---|
| 27810 | return;
|
|---|
| 27811 | }
|
|---|
| 27812 |
|
|---|
| 27813 | if (typeof WeakSet !== 'function') {
|
|---|
| 27814 | return;
|
|---|
| 27815 | }
|
|---|
| 27816 |
|
|---|
| 27817 | if (failedBoundaries === null) {
|
|---|
| 27818 | failedBoundaries = new WeakSet();
|
|---|
| 27819 | }
|
|---|
| 27820 |
|
|---|
| 27821 | failedBoundaries.add(fiber);
|
|---|
| 27822 | }
|
|---|
| 27823 | }
|
|---|
| 27824 | var scheduleRefresh = function (root, update) {
|
|---|
| 27825 | {
|
|---|
| 27826 | if (resolveFamily === null) {
|
|---|
| 27827 | // Hot reloading is disabled.
|
|---|
| 27828 | return;
|
|---|
| 27829 | }
|
|---|
| 27830 |
|
|---|
| 27831 | var staleFamilies = update.staleFamilies,
|
|---|
| 27832 | updatedFamilies = update.updatedFamilies;
|
|---|
| 27833 | flushPassiveEffects();
|
|---|
| 27834 | flushSync(function () {
|
|---|
| 27835 | scheduleFibersWithFamiliesRecursively(root.current, updatedFamilies, staleFamilies);
|
|---|
| 27836 | });
|
|---|
| 27837 | }
|
|---|
| 27838 | };
|
|---|
| 27839 | var scheduleRoot = function (root, element) {
|
|---|
| 27840 | {
|
|---|
| 27841 | if (root.context !== emptyContextObject) {
|
|---|
| 27842 | // Super edge case: root has a legacy _renderSubtree context
|
|---|
| 27843 | // but we don't know the parentComponent so we can't pass it.
|
|---|
| 27844 | // Just ignore. We'll delete this with _renderSubtree code path later.
|
|---|
| 27845 | return;
|
|---|
| 27846 | }
|
|---|
| 27847 |
|
|---|
| 27848 | flushPassiveEffects();
|
|---|
| 27849 | flushSync(function () {
|
|---|
| 27850 | updateContainer(element, root, null, null);
|
|---|
| 27851 | });
|
|---|
| 27852 | }
|
|---|
| 27853 | };
|
|---|
| 27854 |
|
|---|
| 27855 | function scheduleFibersWithFamiliesRecursively(fiber, updatedFamilies, staleFamilies) {
|
|---|
| 27856 | {
|
|---|
| 27857 | var alternate = fiber.alternate,
|
|---|
| 27858 | child = fiber.child,
|
|---|
| 27859 | sibling = fiber.sibling,
|
|---|
| 27860 | tag = fiber.tag,
|
|---|
| 27861 | type = fiber.type;
|
|---|
| 27862 | var candidateType = null;
|
|---|
| 27863 |
|
|---|
| 27864 | switch (tag) {
|
|---|
| 27865 | case FunctionComponent:
|
|---|
| 27866 | case SimpleMemoComponent:
|
|---|
| 27867 | case ClassComponent:
|
|---|
| 27868 | candidateType = type;
|
|---|
| 27869 | break;
|
|---|
| 27870 |
|
|---|
| 27871 | case ForwardRef:
|
|---|
| 27872 | candidateType = type.render;
|
|---|
| 27873 | break;
|
|---|
| 27874 | }
|
|---|
| 27875 |
|
|---|
| 27876 | if (resolveFamily === null) {
|
|---|
| 27877 | throw new Error('Expected resolveFamily to be set during hot reload.');
|
|---|
| 27878 | }
|
|---|
| 27879 |
|
|---|
| 27880 | var needsRender = false;
|
|---|
| 27881 | var needsRemount = false;
|
|---|
| 27882 |
|
|---|
| 27883 | if (candidateType !== null) {
|
|---|
| 27884 | var family = resolveFamily(candidateType);
|
|---|
| 27885 |
|
|---|
| 27886 | if (family !== undefined) {
|
|---|
| 27887 | if (staleFamilies.has(family)) {
|
|---|
| 27888 | needsRemount = true;
|
|---|
| 27889 | } else if (updatedFamilies.has(family)) {
|
|---|
| 27890 | if (tag === ClassComponent) {
|
|---|
| 27891 | needsRemount = true;
|
|---|
| 27892 | } else {
|
|---|
| 27893 | needsRender = true;
|
|---|
| 27894 | }
|
|---|
| 27895 | }
|
|---|
| 27896 | }
|
|---|
| 27897 | }
|
|---|
| 27898 |
|
|---|
| 27899 | if (failedBoundaries !== null) {
|
|---|
| 27900 | if (failedBoundaries.has(fiber) || alternate !== null && failedBoundaries.has(alternate)) {
|
|---|
| 27901 | needsRemount = true;
|
|---|
| 27902 | }
|
|---|
| 27903 | }
|
|---|
| 27904 |
|
|---|
| 27905 | if (needsRemount) {
|
|---|
| 27906 | fiber._debugNeedsRemount = true;
|
|---|
| 27907 | }
|
|---|
| 27908 |
|
|---|
| 27909 | if (needsRemount || needsRender) {
|
|---|
| 27910 | var _root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 27911 |
|
|---|
| 27912 | if (_root !== null) {
|
|---|
| 27913 | scheduleUpdateOnFiber(_root, fiber, SyncLane, NoTimestamp);
|
|---|
| 27914 | }
|
|---|
| 27915 | }
|
|---|
| 27916 |
|
|---|
| 27917 | if (child !== null && !needsRemount) {
|
|---|
| 27918 | scheduleFibersWithFamiliesRecursively(child, updatedFamilies, staleFamilies);
|
|---|
| 27919 | }
|
|---|
| 27920 |
|
|---|
| 27921 | if (sibling !== null) {
|
|---|
| 27922 | scheduleFibersWithFamiliesRecursively(sibling, updatedFamilies, staleFamilies);
|
|---|
| 27923 | }
|
|---|
| 27924 | }
|
|---|
| 27925 | }
|
|---|
| 27926 |
|
|---|
| 27927 | var findHostInstancesForRefresh = function (root, families) {
|
|---|
| 27928 | {
|
|---|
| 27929 | var hostInstances = new Set();
|
|---|
| 27930 | var types = new Set(families.map(function (family) {
|
|---|
| 27931 | return family.current;
|
|---|
| 27932 | }));
|
|---|
| 27933 | findHostInstancesForMatchingFibersRecursively(root.current, types, hostInstances);
|
|---|
| 27934 | return hostInstances;
|
|---|
| 27935 | }
|
|---|
| 27936 | };
|
|---|
| 27937 |
|
|---|
| 27938 | function findHostInstancesForMatchingFibersRecursively(fiber, types, hostInstances) {
|
|---|
| 27939 | {
|
|---|
| 27940 | var child = fiber.child,
|
|---|
| 27941 | sibling = fiber.sibling,
|
|---|
| 27942 | tag = fiber.tag,
|
|---|
| 27943 | type = fiber.type;
|
|---|
| 27944 | var candidateType = null;
|
|---|
| 27945 |
|
|---|
| 27946 | switch (tag) {
|
|---|
| 27947 | case FunctionComponent:
|
|---|
| 27948 | case SimpleMemoComponent:
|
|---|
| 27949 | case ClassComponent:
|
|---|
| 27950 | candidateType = type;
|
|---|
| 27951 | break;
|
|---|
| 27952 |
|
|---|
| 27953 | case ForwardRef:
|
|---|
| 27954 | candidateType = type.render;
|
|---|
| 27955 | break;
|
|---|
| 27956 | }
|
|---|
| 27957 |
|
|---|
| 27958 | var didMatch = false;
|
|---|
| 27959 |
|
|---|
| 27960 | if (candidateType !== null) {
|
|---|
| 27961 | if (types.has(candidateType)) {
|
|---|
| 27962 | didMatch = true;
|
|---|
| 27963 | }
|
|---|
| 27964 | }
|
|---|
| 27965 |
|
|---|
| 27966 | if (didMatch) {
|
|---|
| 27967 | // We have a match. This only drills down to the closest host components.
|
|---|
| 27968 | // There's no need to search deeper because for the purpose of giving
|
|---|
| 27969 | // visual feedback, "flashing" outermost parent rectangles is sufficient.
|
|---|
| 27970 | findHostInstancesForFiberShallowly(fiber, hostInstances);
|
|---|
| 27971 | } else {
|
|---|
| 27972 | // If there's no match, maybe there will be one further down in the child tree.
|
|---|
| 27973 | if (child !== null) {
|
|---|
| 27974 | findHostInstancesForMatchingFibersRecursively(child, types, hostInstances);
|
|---|
| 27975 | }
|
|---|
| 27976 | }
|
|---|
| 27977 |
|
|---|
| 27978 | if (sibling !== null) {
|
|---|
| 27979 | findHostInstancesForMatchingFibersRecursively(sibling, types, hostInstances);
|
|---|
| 27980 | }
|
|---|
| 27981 | }
|
|---|
| 27982 | }
|
|---|
| 27983 |
|
|---|
| 27984 | function findHostInstancesForFiberShallowly(fiber, hostInstances) {
|
|---|
| 27985 | {
|
|---|
| 27986 | var foundHostInstances = findChildHostInstancesForFiberShallowly(fiber, hostInstances);
|
|---|
| 27987 |
|
|---|
| 27988 | if (foundHostInstances) {
|
|---|
| 27989 | return;
|
|---|
| 27990 | } // If we didn't find any host children, fallback to closest host parent.
|
|---|
| 27991 |
|
|---|
| 27992 |
|
|---|
| 27993 | var node = fiber;
|
|---|
| 27994 |
|
|---|
| 27995 | while (true) {
|
|---|
| 27996 | switch (node.tag) {
|
|---|
| 27997 | case HostComponent:
|
|---|
| 27998 | hostInstances.add(node.stateNode);
|
|---|
| 27999 | return;
|
|---|
| 28000 |
|
|---|
| 28001 | case HostPortal:
|
|---|
| 28002 | hostInstances.add(node.stateNode.containerInfo);
|
|---|
| 28003 | return;
|
|---|
| 28004 |
|
|---|
| 28005 | case HostRoot:
|
|---|
| 28006 | hostInstances.add(node.stateNode.containerInfo);
|
|---|
| 28007 | return;
|
|---|
| 28008 | }
|
|---|
| 28009 |
|
|---|
| 28010 | if (node.return === null) {
|
|---|
| 28011 | throw new Error('Expected to reach root first.');
|
|---|
| 28012 | }
|
|---|
| 28013 |
|
|---|
| 28014 | node = node.return;
|
|---|
| 28015 | }
|
|---|
| 28016 | }
|
|---|
| 28017 | }
|
|---|
| 28018 |
|
|---|
| 28019 | function findChildHostInstancesForFiberShallowly(fiber, hostInstances) {
|
|---|
| 28020 | {
|
|---|
| 28021 | var node = fiber;
|
|---|
| 28022 | var foundHostInstances = false;
|
|---|
| 28023 |
|
|---|
| 28024 | while (true) {
|
|---|
| 28025 | if (node.tag === HostComponent) {
|
|---|
| 28026 | // We got a match.
|
|---|
| 28027 | foundHostInstances = true;
|
|---|
| 28028 | hostInstances.add(node.stateNode); // There may still be more, so keep searching.
|
|---|
| 28029 | } else if (node.child !== null) {
|
|---|
| 28030 | node.child.return = node;
|
|---|
| 28031 | node = node.child;
|
|---|
| 28032 | continue;
|
|---|
| 28033 | }
|
|---|
| 28034 |
|
|---|
| 28035 | if (node === fiber) {
|
|---|
| 28036 | return foundHostInstances;
|
|---|
| 28037 | }
|
|---|
| 28038 |
|
|---|
| 28039 | while (node.sibling === null) {
|
|---|
| 28040 | if (node.return === null || node.return === fiber) {
|
|---|
| 28041 | return foundHostInstances;
|
|---|
| 28042 | }
|
|---|
| 28043 |
|
|---|
| 28044 | node = node.return;
|
|---|
| 28045 | }
|
|---|
| 28046 |
|
|---|
| 28047 | node.sibling.return = node.return;
|
|---|
| 28048 | node = node.sibling;
|
|---|
| 28049 | }
|
|---|
| 28050 | }
|
|---|
| 28051 |
|
|---|
| 28052 | return false;
|
|---|
| 28053 | }
|
|---|
| 28054 |
|
|---|
| 28055 | var hasBadMapPolyfill;
|
|---|
| 28056 |
|
|---|
| 28057 | {
|
|---|
| 28058 | hasBadMapPolyfill = false;
|
|---|
| 28059 |
|
|---|
| 28060 | try {
|
|---|
| 28061 | var nonExtensibleObject = Object.preventExtensions({});
|
|---|
| 28062 | /* eslint-disable no-new */
|
|---|
| 28063 |
|
|---|
| 28064 | new Map([[nonExtensibleObject, null]]);
|
|---|
| 28065 | new Set([nonExtensibleObject]);
|
|---|
| 28066 | /* eslint-enable no-new */
|
|---|
| 28067 | } catch (e) {
|
|---|
| 28068 | // TODO: Consider warning about bad polyfills
|
|---|
| 28069 | hasBadMapPolyfill = true;
|
|---|
| 28070 | }
|
|---|
| 28071 | }
|
|---|
| 28072 |
|
|---|
| 28073 | function FiberNode(tag, pendingProps, key, mode) {
|
|---|
| 28074 | // Instance
|
|---|
| 28075 | this.tag = tag;
|
|---|
| 28076 | this.key = key;
|
|---|
| 28077 | this.elementType = null;
|
|---|
| 28078 | this.type = null;
|
|---|
| 28079 | this.stateNode = null; // Fiber
|
|---|
| 28080 |
|
|---|
| 28081 | this.return = null;
|
|---|
| 28082 | this.child = null;
|
|---|
| 28083 | this.sibling = null;
|
|---|
| 28084 | this.index = 0;
|
|---|
| 28085 | this.ref = null;
|
|---|
| 28086 | this.pendingProps = pendingProps;
|
|---|
| 28087 | this.memoizedProps = null;
|
|---|
| 28088 | this.updateQueue = null;
|
|---|
| 28089 | this.memoizedState = null;
|
|---|
| 28090 | this.dependencies = null;
|
|---|
| 28091 | this.mode = mode; // Effects
|
|---|
| 28092 |
|
|---|
| 28093 | this.flags = NoFlags;
|
|---|
| 28094 | this.subtreeFlags = NoFlags;
|
|---|
| 28095 | this.deletions = null;
|
|---|
| 28096 | this.lanes = NoLanes;
|
|---|
| 28097 | this.childLanes = NoLanes;
|
|---|
| 28098 | this.alternate = null;
|
|---|
| 28099 |
|
|---|
| 28100 | {
|
|---|
| 28101 | // Note: The following is done to avoid a v8 performance cliff.
|
|---|
| 28102 | //
|
|---|
| 28103 | // Initializing the fields below to smis and later updating them with
|
|---|
| 28104 | // double values will cause Fibers to end up having separate shapes.
|
|---|
| 28105 | // This behavior/bug has something to do with Object.preventExtension().
|
|---|
| 28106 | // Fortunately this only impacts DEV builds.
|
|---|
| 28107 | // Unfortunately it makes React unusably slow for some applications.
|
|---|
| 28108 | // To work around this, initialize the fields below with doubles.
|
|---|
| 28109 | //
|
|---|
| 28110 | // Learn more about this here:
|
|---|
| 28111 | // https://github.com/facebook/react/issues/14365
|
|---|
| 28112 | // https://bugs.chromium.org/p/v8/issues/detail?id=8538
|
|---|
| 28113 | this.actualDuration = Number.NaN;
|
|---|
| 28114 | this.actualStartTime = Number.NaN;
|
|---|
| 28115 | this.selfBaseDuration = Number.NaN;
|
|---|
| 28116 | this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
|
|---|
| 28117 | // This won't trigger the performance cliff mentioned above,
|
|---|
| 28118 | // and it simplifies other profiler code (including DevTools).
|
|---|
| 28119 |
|
|---|
| 28120 | this.actualDuration = 0;
|
|---|
| 28121 | this.actualStartTime = -1;
|
|---|
| 28122 | this.selfBaseDuration = 0;
|
|---|
| 28123 | this.treeBaseDuration = 0;
|
|---|
| 28124 | }
|
|---|
| 28125 |
|
|---|
| 28126 | {
|
|---|
| 28127 | // This isn't directly used but is handy for debugging internals:
|
|---|
| 28128 | this._debugSource = null;
|
|---|
| 28129 | this._debugOwner = null;
|
|---|
| 28130 | this._debugNeedsRemount = false;
|
|---|
| 28131 | this._debugHookTypes = null;
|
|---|
| 28132 |
|
|---|
| 28133 | if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
|
|---|
| 28134 | Object.preventExtensions(this);
|
|---|
| 28135 | }
|
|---|
| 28136 | }
|
|---|
| 28137 | } // This is a constructor function, rather than a POJO constructor, still
|
|---|
| 28138 | // please ensure we do the following:
|
|---|
| 28139 | // 1) Nobody should add any instance methods on this. Instance methods can be
|
|---|
| 28140 | // more difficult to predict when they get optimized and they are almost
|
|---|
| 28141 | // never inlined properly in static compilers.
|
|---|
| 28142 | // 2) Nobody should rely on `instanceof Fiber` for type testing. We should
|
|---|
| 28143 | // always know when it is a fiber.
|
|---|
| 28144 | // 3) We might want to experiment with using numeric keys since they are easier
|
|---|
| 28145 | // to optimize in a non-JIT environment.
|
|---|
| 28146 | // 4) We can easily go from a constructor to a createFiber object literal if that
|
|---|
| 28147 | // is faster.
|
|---|
| 28148 | // 5) It should be easy to port this to a C struct and keep a C implementation
|
|---|
| 28149 | // compatible.
|
|---|
| 28150 |
|
|---|
| 28151 |
|
|---|
| 28152 | var createFiber = function (tag, pendingProps, key, mode) {
|
|---|
| 28153 | // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
|
|---|
| 28154 | return new FiberNode(tag, pendingProps, key, mode);
|
|---|
| 28155 | };
|
|---|
| 28156 |
|
|---|
| 28157 | function shouldConstruct$1(Component) {
|
|---|
| 28158 | var prototype = Component.prototype;
|
|---|
| 28159 | return !!(prototype && prototype.isReactComponent);
|
|---|
| 28160 | }
|
|---|
| 28161 |
|
|---|
| 28162 | function isSimpleFunctionComponent(type) {
|
|---|
| 28163 | return typeof type === 'function' && !shouldConstruct$1(type) && type.defaultProps === undefined;
|
|---|
| 28164 | }
|
|---|
| 28165 | function resolveLazyComponentTag(Component) {
|
|---|
| 28166 | if (typeof Component === 'function') {
|
|---|
| 28167 | return shouldConstruct$1(Component) ? ClassComponent : FunctionComponent;
|
|---|
| 28168 | } else if (Component !== undefined && Component !== null) {
|
|---|
| 28169 | var $$typeof = Component.$$typeof;
|
|---|
| 28170 |
|
|---|
| 28171 | if ($$typeof === REACT_FORWARD_REF_TYPE) {
|
|---|
| 28172 | return ForwardRef;
|
|---|
| 28173 | }
|
|---|
| 28174 |
|
|---|
| 28175 | if ($$typeof === REACT_MEMO_TYPE) {
|
|---|
| 28176 | return MemoComponent;
|
|---|
| 28177 | }
|
|---|
| 28178 | }
|
|---|
| 28179 |
|
|---|
| 28180 | return IndeterminateComponent;
|
|---|
| 28181 | } // This is used to create an alternate fiber to do work on.
|
|---|
| 28182 |
|
|---|
| 28183 | function createWorkInProgress(current, pendingProps) {
|
|---|
| 28184 | var workInProgress = current.alternate;
|
|---|
| 28185 |
|
|---|
| 28186 | if (workInProgress === null) {
|
|---|
| 28187 | // We use a double buffering pooling technique because we know that we'll
|
|---|
| 28188 | // only ever need at most two versions of a tree. We pool the "other" unused
|
|---|
| 28189 | // node that we're free to reuse. This is lazily created to avoid allocating
|
|---|
| 28190 | // extra objects for things that are never updated. It also allow us to
|
|---|
| 28191 | // reclaim the extra memory if needed.
|
|---|
| 28192 | workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode);
|
|---|
| 28193 | workInProgress.elementType = current.elementType;
|
|---|
| 28194 | workInProgress.type = current.type;
|
|---|
| 28195 | workInProgress.stateNode = current.stateNode;
|
|---|
| 28196 |
|
|---|
| 28197 | {
|
|---|
| 28198 | // DEV-only fields
|
|---|
| 28199 | workInProgress._debugSource = current._debugSource;
|
|---|
| 28200 | workInProgress._debugOwner = current._debugOwner;
|
|---|
| 28201 | workInProgress._debugHookTypes = current._debugHookTypes;
|
|---|
| 28202 | }
|
|---|
| 28203 |
|
|---|
| 28204 | workInProgress.alternate = current;
|
|---|
| 28205 | current.alternate = workInProgress;
|
|---|
| 28206 | } else {
|
|---|
| 28207 | workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type.
|
|---|
| 28208 |
|
|---|
| 28209 | workInProgress.type = current.type; // We already have an alternate.
|
|---|
| 28210 | // Reset the effect tag.
|
|---|
| 28211 |
|
|---|
| 28212 | workInProgress.flags = NoFlags; // The effects are no longer valid.
|
|---|
| 28213 |
|
|---|
| 28214 | workInProgress.subtreeFlags = NoFlags;
|
|---|
| 28215 | workInProgress.deletions = null;
|
|---|
| 28216 |
|
|---|
| 28217 | {
|
|---|
| 28218 | // We intentionally reset, rather than copy, actualDuration & actualStartTime.
|
|---|
| 28219 | // This prevents time from endlessly accumulating in new commits.
|
|---|
| 28220 | // This has the downside of resetting values for different priority renders,
|
|---|
| 28221 | // But works for yielding (the common case) and should support resuming.
|
|---|
| 28222 | workInProgress.actualDuration = 0;
|
|---|
| 28223 | workInProgress.actualStartTime = -1;
|
|---|
| 28224 | }
|
|---|
| 28225 | } // Reset all effects except static ones.
|
|---|
| 28226 | // Static effects are not specific to a render.
|
|---|
| 28227 |
|
|---|
| 28228 |
|
|---|
| 28229 | workInProgress.flags = current.flags & StaticMask;
|
|---|
| 28230 | workInProgress.childLanes = current.childLanes;
|
|---|
| 28231 | workInProgress.lanes = current.lanes;
|
|---|
| 28232 | workInProgress.child = current.child;
|
|---|
| 28233 | workInProgress.memoizedProps = current.memoizedProps;
|
|---|
| 28234 | workInProgress.memoizedState = current.memoizedState;
|
|---|
| 28235 | workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so
|
|---|
| 28236 | // it cannot be shared with the current fiber.
|
|---|
| 28237 |
|
|---|
| 28238 | var currentDependencies = current.dependencies;
|
|---|
| 28239 | workInProgress.dependencies = currentDependencies === null ? null : {
|
|---|
| 28240 | lanes: currentDependencies.lanes,
|
|---|
| 28241 | firstContext: currentDependencies.firstContext
|
|---|
| 28242 | }; // These will be overridden during the parent's reconciliation
|
|---|
| 28243 |
|
|---|
| 28244 | workInProgress.sibling = current.sibling;
|
|---|
| 28245 | workInProgress.index = current.index;
|
|---|
| 28246 | workInProgress.ref = current.ref;
|
|---|
| 28247 |
|
|---|
| 28248 | {
|
|---|
| 28249 | workInProgress.selfBaseDuration = current.selfBaseDuration;
|
|---|
| 28250 | workInProgress.treeBaseDuration = current.treeBaseDuration;
|
|---|
| 28251 | }
|
|---|
| 28252 |
|
|---|
| 28253 | {
|
|---|
| 28254 | workInProgress._debugNeedsRemount = current._debugNeedsRemount;
|
|---|
| 28255 |
|
|---|
| 28256 | switch (workInProgress.tag) {
|
|---|
| 28257 | case IndeterminateComponent:
|
|---|
| 28258 | case FunctionComponent:
|
|---|
| 28259 | case SimpleMemoComponent:
|
|---|
| 28260 | workInProgress.type = resolveFunctionForHotReloading(current.type);
|
|---|
| 28261 | break;
|
|---|
| 28262 |
|
|---|
| 28263 | case ClassComponent:
|
|---|
| 28264 | workInProgress.type = resolveClassForHotReloading(current.type);
|
|---|
| 28265 | break;
|
|---|
| 28266 |
|
|---|
| 28267 | case ForwardRef:
|
|---|
| 28268 | workInProgress.type = resolveForwardRefForHotReloading(current.type);
|
|---|
| 28269 | break;
|
|---|
| 28270 | }
|
|---|
| 28271 | }
|
|---|
| 28272 |
|
|---|
| 28273 | return workInProgress;
|
|---|
| 28274 | } // Used to reuse a Fiber for a second pass.
|
|---|
| 28275 |
|
|---|
| 28276 | function resetWorkInProgress(workInProgress, renderLanes) {
|
|---|
| 28277 | // This resets the Fiber to what createFiber or createWorkInProgress would
|
|---|
| 28278 | // have set the values to before during the first pass. Ideally this wouldn't
|
|---|
| 28279 | // be necessary but unfortunately many code paths reads from the workInProgress
|
|---|
| 28280 | // when they should be reading from current and writing to workInProgress.
|
|---|
| 28281 | // We assume pendingProps, index, key, ref, return are still untouched to
|
|---|
| 28282 | // avoid doing another reconciliation.
|
|---|
| 28283 | // Reset the effect flags but keep any Placement tags, since that's something
|
|---|
| 28284 | // that child fiber is setting, not the reconciliation.
|
|---|
| 28285 | workInProgress.flags &= StaticMask | Placement; // The effects are no longer valid.
|
|---|
| 28286 |
|
|---|
| 28287 | var current = workInProgress.alternate;
|
|---|
| 28288 |
|
|---|
| 28289 | if (current === null) {
|
|---|
| 28290 | // Reset to createFiber's initial values.
|
|---|
| 28291 | workInProgress.childLanes = NoLanes;
|
|---|
| 28292 | workInProgress.lanes = renderLanes;
|
|---|
| 28293 | workInProgress.child = null;
|
|---|
| 28294 | workInProgress.subtreeFlags = NoFlags;
|
|---|
| 28295 | workInProgress.memoizedProps = null;
|
|---|
| 28296 | workInProgress.memoizedState = null;
|
|---|
| 28297 | workInProgress.updateQueue = null;
|
|---|
| 28298 | workInProgress.dependencies = null;
|
|---|
| 28299 | workInProgress.stateNode = null;
|
|---|
| 28300 |
|
|---|
| 28301 | {
|
|---|
| 28302 | // Note: We don't reset the actualTime counts. It's useful to accumulate
|
|---|
| 28303 | // actual time across multiple render passes.
|
|---|
| 28304 | workInProgress.selfBaseDuration = 0;
|
|---|
| 28305 | workInProgress.treeBaseDuration = 0;
|
|---|
| 28306 | }
|
|---|
| 28307 | } else {
|
|---|
| 28308 | // Reset to the cloned values that createWorkInProgress would've.
|
|---|
| 28309 | workInProgress.childLanes = current.childLanes;
|
|---|
| 28310 | workInProgress.lanes = current.lanes;
|
|---|
| 28311 | workInProgress.child = current.child;
|
|---|
| 28312 | workInProgress.subtreeFlags = NoFlags;
|
|---|
| 28313 | workInProgress.deletions = null;
|
|---|
| 28314 | workInProgress.memoizedProps = current.memoizedProps;
|
|---|
| 28315 | workInProgress.memoizedState = current.memoizedState;
|
|---|
| 28316 | workInProgress.updateQueue = current.updateQueue; // Needed because Blocks store data on type.
|
|---|
| 28317 |
|
|---|
| 28318 | workInProgress.type = current.type; // Clone the dependencies object. This is mutated during the render phase, so
|
|---|
| 28319 | // it cannot be shared with the current fiber.
|
|---|
| 28320 |
|
|---|
| 28321 | var currentDependencies = current.dependencies;
|
|---|
| 28322 | workInProgress.dependencies = currentDependencies === null ? null : {
|
|---|
| 28323 | lanes: currentDependencies.lanes,
|
|---|
| 28324 | firstContext: currentDependencies.firstContext
|
|---|
| 28325 | };
|
|---|
| 28326 |
|
|---|
| 28327 | {
|
|---|
| 28328 | // Note: We don't reset the actualTime counts. It's useful to accumulate
|
|---|
| 28329 | // actual time across multiple render passes.
|
|---|
| 28330 | workInProgress.selfBaseDuration = current.selfBaseDuration;
|
|---|
| 28331 | workInProgress.treeBaseDuration = current.treeBaseDuration;
|
|---|
| 28332 | }
|
|---|
| 28333 | }
|
|---|
| 28334 |
|
|---|
| 28335 | return workInProgress;
|
|---|
| 28336 | }
|
|---|
| 28337 | function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) {
|
|---|
| 28338 | var mode;
|
|---|
| 28339 |
|
|---|
| 28340 | if (tag === ConcurrentRoot) {
|
|---|
| 28341 | mode = ConcurrentMode;
|
|---|
| 28342 |
|
|---|
| 28343 | if (isStrictMode === true) {
|
|---|
| 28344 | mode |= StrictLegacyMode;
|
|---|
| 28345 |
|
|---|
| 28346 | {
|
|---|
| 28347 | mode |= StrictEffectsMode;
|
|---|
| 28348 | }
|
|---|
| 28349 | }
|
|---|
| 28350 | } else {
|
|---|
| 28351 | mode = NoMode;
|
|---|
| 28352 | }
|
|---|
| 28353 |
|
|---|
| 28354 | if ( isDevToolsPresent) {
|
|---|
| 28355 | // Always collect profile timings when DevTools are present.
|
|---|
| 28356 | // This enables DevTools to start capturing timing at any point–
|
|---|
| 28357 | // Without some nodes in the tree having empty base times.
|
|---|
| 28358 | mode |= ProfileMode;
|
|---|
| 28359 | }
|
|---|
| 28360 |
|
|---|
| 28361 | return createFiber(HostRoot, null, null, mode);
|
|---|
| 28362 | }
|
|---|
| 28363 | function createFiberFromTypeAndProps(type, // React$ElementType
|
|---|
| 28364 | key, pendingProps, owner, mode, lanes) {
|
|---|
| 28365 | var fiberTag = IndeterminateComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy.
|
|---|
| 28366 |
|
|---|
| 28367 | var resolvedType = type;
|
|---|
| 28368 |
|
|---|
| 28369 | if (typeof type === 'function') {
|
|---|
| 28370 | if (shouldConstruct$1(type)) {
|
|---|
| 28371 | fiberTag = ClassComponent;
|
|---|
| 28372 |
|
|---|
| 28373 | {
|
|---|
| 28374 | resolvedType = resolveClassForHotReloading(resolvedType);
|
|---|
| 28375 | }
|
|---|
| 28376 | } else {
|
|---|
| 28377 | {
|
|---|
| 28378 | resolvedType = resolveFunctionForHotReloading(resolvedType);
|
|---|
| 28379 | }
|
|---|
| 28380 | }
|
|---|
| 28381 | } else if (typeof type === 'string') {
|
|---|
| 28382 | fiberTag = HostComponent;
|
|---|
| 28383 | } else {
|
|---|
| 28384 | getTag: switch (type) {
|
|---|
| 28385 | case REACT_FRAGMENT_TYPE:
|
|---|
| 28386 | return createFiberFromFragment(pendingProps.children, mode, lanes, key);
|
|---|
| 28387 |
|
|---|
| 28388 | case REACT_STRICT_MODE_TYPE:
|
|---|
| 28389 | fiberTag = Mode;
|
|---|
| 28390 | mode |= StrictLegacyMode;
|
|---|
| 28391 |
|
|---|
| 28392 | if ( (mode & ConcurrentMode) !== NoMode) {
|
|---|
| 28393 | // Strict effects should never run on legacy roots
|
|---|
| 28394 | mode |= StrictEffectsMode;
|
|---|
| 28395 | }
|
|---|
| 28396 |
|
|---|
| 28397 | break;
|
|---|
| 28398 |
|
|---|
| 28399 | case REACT_PROFILER_TYPE:
|
|---|
| 28400 | return createFiberFromProfiler(pendingProps, mode, lanes, key);
|
|---|
| 28401 |
|
|---|
| 28402 | case REACT_SUSPENSE_TYPE:
|
|---|
| 28403 | return createFiberFromSuspense(pendingProps, mode, lanes, key);
|
|---|
| 28404 |
|
|---|
| 28405 | case REACT_SUSPENSE_LIST_TYPE:
|
|---|
| 28406 | return createFiberFromSuspenseList(pendingProps, mode, lanes, key);
|
|---|
| 28407 |
|
|---|
| 28408 | case REACT_OFFSCREEN_TYPE:
|
|---|
| 28409 | return createFiberFromOffscreen(pendingProps, mode, lanes, key);
|
|---|
| 28410 |
|
|---|
| 28411 | case REACT_LEGACY_HIDDEN_TYPE:
|
|---|
| 28412 |
|
|---|
| 28413 | // eslint-disable-next-line no-fallthrough
|
|---|
| 28414 |
|
|---|
| 28415 | case REACT_SCOPE_TYPE:
|
|---|
| 28416 |
|
|---|
| 28417 | // eslint-disable-next-line no-fallthrough
|
|---|
| 28418 |
|
|---|
| 28419 | case REACT_CACHE_TYPE:
|
|---|
| 28420 |
|
|---|
| 28421 | // eslint-disable-next-line no-fallthrough
|
|---|
| 28422 |
|
|---|
| 28423 | case REACT_TRACING_MARKER_TYPE:
|
|---|
| 28424 |
|
|---|
| 28425 | // eslint-disable-next-line no-fallthrough
|
|---|
| 28426 |
|
|---|
| 28427 | case REACT_DEBUG_TRACING_MODE_TYPE:
|
|---|
| 28428 |
|
|---|
| 28429 | // eslint-disable-next-line no-fallthrough
|
|---|
| 28430 |
|
|---|
| 28431 | default:
|
|---|
| 28432 | {
|
|---|
| 28433 | if (typeof type === 'object' && type !== null) {
|
|---|
| 28434 | switch (type.$$typeof) {
|
|---|
| 28435 | case REACT_PROVIDER_TYPE:
|
|---|
| 28436 | fiberTag = ContextProvider;
|
|---|
| 28437 | break getTag;
|
|---|
| 28438 |
|
|---|
| 28439 | case REACT_CONTEXT_TYPE:
|
|---|
| 28440 | // This is a consumer
|
|---|
| 28441 | fiberTag = ContextConsumer;
|
|---|
| 28442 | break getTag;
|
|---|
| 28443 |
|
|---|
| 28444 | case REACT_FORWARD_REF_TYPE:
|
|---|
| 28445 | fiberTag = ForwardRef;
|
|---|
| 28446 |
|
|---|
| 28447 | {
|
|---|
| 28448 | resolvedType = resolveForwardRefForHotReloading(resolvedType);
|
|---|
| 28449 | }
|
|---|
| 28450 |
|
|---|
| 28451 | break getTag;
|
|---|
| 28452 |
|
|---|
| 28453 | case REACT_MEMO_TYPE:
|
|---|
| 28454 | fiberTag = MemoComponent;
|
|---|
| 28455 | break getTag;
|
|---|
| 28456 |
|
|---|
| 28457 | case REACT_LAZY_TYPE:
|
|---|
| 28458 | fiberTag = LazyComponent;
|
|---|
| 28459 | resolvedType = null;
|
|---|
| 28460 | break getTag;
|
|---|
| 28461 | }
|
|---|
| 28462 | }
|
|---|
| 28463 |
|
|---|
| 28464 | var info = '';
|
|---|
| 28465 |
|
|---|
| 28466 | {
|
|---|
| 28467 | if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) {
|
|---|
| 28468 | 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.';
|
|---|
| 28469 | }
|
|---|
| 28470 |
|
|---|
| 28471 | var ownerName = owner ? getComponentNameFromFiber(owner) : null;
|
|---|
| 28472 |
|
|---|
| 28473 | if (ownerName) {
|
|---|
| 28474 | info += '\n\nCheck the render method of `' + ownerName + '`.';
|
|---|
| 28475 | }
|
|---|
| 28476 | }
|
|---|
| 28477 |
|
|---|
| 28478 | throw new Error('Element type is invalid: expected a string (for built-in ' + 'components) or a class/function (for composite components) ' + ("but got: " + (type == null ? type : typeof type) + "." + info));
|
|---|
| 28479 | }
|
|---|
| 28480 | }
|
|---|
| 28481 | }
|
|---|
| 28482 |
|
|---|
| 28483 | var fiber = createFiber(fiberTag, pendingProps, key, mode);
|
|---|
| 28484 | fiber.elementType = type;
|
|---|
| 28485 | fiber.type = resolvedType;
|
|---|
| 28486 | fiber.lanes = lanes;
|
|---|
| 28487 |
|
|---|
| 28488 | {
|
|---|
| 28489 | fiber._debugOwner = owner;
|
|---|
| 28490 | }
|
|---|
| 28491 |
|
|---|
| 28492 | return fiber;
|
|---|
| 28493 | }
|
|---|
| 28494 | function createFiberFromElement(element, mode, lanes) {
|
|---|
| 28495 | var owner = null;
|
|---|
| 28496 |
|
|---|
| 28497 | {
|
|---|
| 28498 | owner = element._owner;
|
|---|
| 28499 | }
|
|---|
| 28500 |
|
|---|
| 28501 | var type = element.type;
|
|---|
| 28502 | var key = element.key;
|
|---|
| 28503 | var pendingProps = element.props;
|
|---|
| 28504 | var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes);
|
|---|
| 28505 |
|
|---|
| 28506 | {
|
|---|
| 28507 | fiber._debugSource = element._source;
|
|---|
| 28508 | fiber._debugOwner = element._owner;
|
|---|
| 28509 | }
|
|---|
| 28510 |
|
|---|
| 28511 | return fiber;
|
|---|
| 28512 | }
|
|---|
| 28513 | function createFiberFromFragment(elements, mode, lanes, key) {
|
|---|
| 28514 | var fiber = createFiber(Fragment, elements, key, mode);
|
|---|
| 28515 | fiber.lanes = lanes;
|
|---|
| 28516 | return fiber;
|
|---|
| 28517 | }
|
|---|
| 28518 |
|
|---|
| 28519 | function createFiberFromProfiler(pendingProps, mode, lanes, key) {
|
|---|
| 28520 | {
|
|---|
| 28521 | if (typeof pendingProps.id !== 'string') {
|
|---|
| 28522 | error('Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.', typeof pendingProps.id);
|
|---|
| 28523 | }
|
|---|
| 28524 | }
|
|---|
| 28525 |
|
|---|
| 28526 | var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
|
|---|
| 28527 | fiber.elementType = REACT_PROFILER_TYPE;
|
|---|
| 28528 | fiber.lanes = lanes;
|
|---|
| 28529 |
|
|---|
| 28530 | {
|
|---|
| 28531 | fiber.stateNode = {
|
|---|
| 28532 | effectDuration: 0,
|
|---|
| 28533 | passiveEffectDuration: 0
|
|---|
| 28534 | };
|
|---|
| 28535 | }
|
|---|
| 28536 |
|
|---|
| 28537 | return fiber;
|
|---|
| 28538 | }
|
|---|
| 28539 |
|
|---|
| 28540 | function createFiberFromSuspense(pendingProps, mode, lanes, key) {
|
|---|
| 28541 | var fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
|
|---|
| 28542 | fiber.elementType = REACT_SUSPENSE_TYPE;
|
|---|
| 28543 | fiber.lanes = lanes;
|
|---|
| 28544 | return fiber;
|
|---|
| 28545 | }
|
|---|
| 28546 | function createFiberFromSuspenseList(pendingProps, mode, lanes, key) {
|
|---|
| 28547 | var fiber = createFiber(SuspenseListComponent, pendingProps, key, mode);
|
|---|
| 28548 | fiber.elementType = REACT_SUSPENSE_LIST_TYPE;
|
|---|
| 28549 | fiber.lanes = lanes;
|
|---|
| 28550 | return fiber;
|
|---|
| 28551 | }
|
|---|
| 28552 | function createFiberFromOffscreen(pendingProps, mode, lanes, key) {
|
|---|
| 28553 | var fiber = createFiber(OffscreenComponent, pendingProps, key, mode);
|
|---|
| 28554 | fiber.elementType = REACT_OFFSCREEN_TYPE;
|
|---|
| 28555 | fiber.lanes = lanes;
|
|---|
| 28556 | var primaryChildInstance = {
|
|---|
| 28557 | isHidden: false
|
|---|
| 28558 | };
|
|---|
| 28559 | fiber.stateNode = primaryChildInstance;
|
|---|
| 28560 | return fiber;
|
|---|
| 28561 | }
|
|---|
| 28562 | function createFiberFromText(content, mode, lanes) {
|
|---|
| 28563 | var fiber = createFiber(HostText, content, null, mode);
|
|---|
| 28564 | fiber.lanes = lanes;
|
|---|
| 28565 | return fiber;
|
|---|
| 28566 | }
|
|---|
| 28567 | function createFiberFromHostInstanceForDeletion() {
|
|---|
| 28568 | var fiber = createFiber(HostComponent, null, null, NoMode);
|
|---|
| 28569 | fiber.elementType = 'DELETED';
|
|---|
| 28570 | return fiber;
|
|---|
| 28571 | }
|
|---|
| 28572 | function createFiberFromDehydratedFragment(dehydratedNode) {
|
|---|
| 28573 | var fiber = createFiber(DehydratedFragment, null, null, NoMode);
|
|---|
| 28574 | fiber.stateNode = dehydratedNode;
|
|---|
| 28575 | return fiber;
|
|---|
| 28576 | }
|
|---|
| 28577 | function createFiberFromPortal(portal, mode, lanes) {
|
|---|
| 28578 | var pendingProps = portal.children !== null ? portal.children : [];
|
|---|
| 28579 | var fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
|
|---|
| 28580 | fiber.lanes = lanes;
|
|---|
| 28581 | fiber.stateNode = {
|
|---|
| 28582 | containerInfo: portal.containerInfo,
|
|---|
| 28583 | pendingChildren: null,
|
|---|
| 28584 | // Used by persistent updates
|
|---|
| 28585 | implementation: portal.implementation
|
|---|
| 28586 | };
|
|---|
| 28587 | return fiber;
|
|---|
| 28588 | } // Used for stashing WIP properties to replay failed work in DEV.
|
|---|
| 28589 |
|
|---|
| 28590 | function assignFiberPropertiesInDEV(target, source) {
|
|---|
| 28591 | if (target === null) {
|
|---|
| 28592 | // This Fiber's initial properties will always be overwritten.
|
|---|
| 28593 | // We only use a Fiber to ensure the same hidden class so DEV isn't slow.
|
|---|
| 28594 | target = createFiber(IndeterminateComponent, null, null, NoMode);
|
|---|
| 28595 | } // This is intentionally written as a list of all properties.
|
|---|
| 28596 | // We tried to use Object.assign() instead but this is called in
|
|---|
| 28597 | // the hottest path, and Object.assign() was too slow:
|
|---|
| 28598 | // https://github.com/facebook/react/issues/12502
|
|---|
| 28599 | // This code is DEV-only so size is not a concern.
|
|---|
| 28600 |
|
|---|
| 28601 |
|
|---|
| 28602 | target.tag = source.tag;
|
|---|
| 28603 | target.key = source.key;
|
|---|
| 28604 | target.elementType = source.elementType;
|
|---|
| 28605 | target.type = source.type;
|
|---|
| 28606 | target.stateNode = source.stateNode;
|
|---|
| 28607 | target.return = source.return;
|
|---|
| 28608 | target.child = source.child;
|
|---|
| 28609 | target.sibling = source.sibling;
|
|---|
| 28610 | target.index = source.index;
|
|---|
| 28611 | target.ref = source.ref;
|
|---|
| 28612 | target.pendingProps = source.pendingProps;
|
|---|
| 28613 | target.memoizedProps = source.memoizedProps;
|
|---|
| 28614 | target.updateQueue = source.updateQueue;
|
|---|
| 28615 | target.memoizedState = source.memoizedState;
|
|---|
| 28616 | target.dependencies = source.dependencies;
|
|---|
| 28617 | target.mode = source.mode;
|
|---|
| 28618 | target.flags = source.flags;
|
|---|
| 28619 | target.subtreeFlags = source.subtreeFlags;
|
|---|
| 28620 | target.deletions = source.deletions;
|
|---|
| 28621 | target.lanes = source.lanes;
|
|---|
| 28622 | target.childLanes = source.childLanes;
|
|---|
| 28623 | target.alternate = source.alternate;
|
|---|
| 28624 |
|
|---|
| 28625 | {
|
|---|
| 28626 | target.actualDuration = source.actualDuration;
|
|---|
| 28627 | target.actualStartTime = source.actualStartTime;
|
|---|
| 28628 | target.selfBaseDuration = source.selfBaseDuration;
|
|---|
| 28629 | target.treeBaseDuration = source.treeBaseDuration;
|
|---|
| 28630 | }
|
|---|
| 28631 |
|
|---|
| 28632 | target._debugSource = source._debugSource;
|
|---|
| 28633 | target._debugOwner = source._debugOwner;
|
|---|
| 28634 | target._debugNeedsRemount = source._debugNeedsRemount;
|
|---|
| 28635 | target._debugHookTypes = source._debugHookTypes;
|
|---|
| 28636 | return target;
|
|---|
| 28637 | }
|
|---|
| 28638 |
|
|---|
| 28639 | function FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError) {
|
|---|
| 28640 | this.tag = tag;
|
|---|
| 28641 | this.containerInfo = containerInfo;
|
|---|
| 28642 | this.pendingChildren = null;
|
|---|
| 28643 | this.current = null;
|
|---|
| 28644 | this.pingCache = null;
|
|---|
| 28645 | this.finishedWork = null;
|
|---|
| 28646 | this.timeoutHandle = noTimeout;
|
|---|
| 28647 | this.context = null;
|
|---|
| 28648 | this.pendingContext = null;
|
|---|
| 28649 | this.callbackNode = null;
|
|---|
| 28650 | this.callbackPriority = NoLane;
|
|---|
| 28651 | this.eventTimes = createLaneMap(NoLanes);
|
|---|
| 28652 | this.expirationTimes = createLaneMap(NoTimestamp);
|
|---|
| 28653 | this.pendingLanes = NoLanes;
|
|---|
| 28654 | this.suspendedLanes = NoLanes;
|
|---|
| 28655 | this.pingedLanes = NoLanes;
|
|---|
| 28656 | this.expiredLanes = NoLanes;
|
|---|
| 28657 | this.mutableReadLanes = NoLanes;
|
|---|
| 28658 | this.finishedLanes = NoLanes;
|
|---|
| 28659 | this.entangledLanes = NoLanes;
|
|---|
| 28660 | this.entanglements = createLaneMap(NoLanes);
|
|---|
| 28661 | this.identifierPrefix = identifierPrefix;
|
|---|
| 28662 | this.onRecoverableError = onRecoverableError;
|
|---|
| 28663 |
|
|---|
| 28664 | {
|
|---|
| 28665 | this.mutableSourceEagerHydrationData = null;
|
|---|
| 28666 | }
|
|---|
| 28667 |
|
|---|
| 28668 | {
|
|---|
| 28669 | this.effectDuration = 0;
|
|---|
| 28670 | this.passiveEffectDuration = 0;
|
|---|
| 28671 | }
|
|---|
| 28672 |
|
|---|
| 28673 | {
|
|---|
| 28674 | this.memoizedUpdaters = new Set();
|
|---|
| 28675 | var pendingUpdatersLaneMap = this.pendingUpdatersLaneMap = [];
|
|---|
| 28676 |
|
|---|
| 28677 | for (var _i = 0; _i < TotalLanes; _i++) {
|
|---|
| 28678 | pendingUpdatersLaneMap.push(new Set());
|
|---|
| 28679 | }
|
|---|
| 28680 | }
|
|---|
| 28681 |
|
|---|
| 28682 | {
|
|---|
| 28683 | switch (tag) {
|
|---|
| 28684 | case ConcurrentRoot:
|
|---|
| 28685 | this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()';
|
|---|
| 28686 | break;
|
|---|
| 28687 |
|
|---|
| 28688 | case LegacyRoot:
|
|---|
| 28689 | this._debugRootType = hydrate ? 'hydrate()' : 'render()';
|
|---|
| 28690 | break;
|
|---|
| 28691 | }
|
|---|
| 28692 | }
|
|---|
| 28693 | }
|
|---|
| 28694 |
|
|---|
| 28695 | function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the
|
|---|
| 28696 | // host config, but because they are passed in at runtime, we have to thread
|
|---|
| 28697 | // them through the root constructor. Perhaps we should put them all into a
|
|---|
| 28698 | // single type, like a DynamicHostConfig that is defined by the renderer.
|
|---|
| 28699 | identifierPrefix, onRecoverableError, transitionCallbacks) {
|
|---|
| 28700 | var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError);
|
|---|
| 28701 | // stateNode is any.
|
|---|
| 28702 |
|
|---|
| 28703 |
|
|---|
| 28704 | var uninitializedFiber = createHostRootFiber(tag, isStrictMode);
|
|---|
| 28705 | root.current = uninitializedFiber;
|
|---|
| 28706 | uninitializedFiber.stateNode = root;
|
|---|
| 28707 |
|
|---|
| 28708 | {
|
|---|
| 28709 | var _initialState = {
|
|---|
| 28710 | element: initialChildren,
|
|---|
| 28711 | isDehydrated: hydrate,
|
|---|
| 28712 | cache: null,
|
|---|
| 28713 | // not enabled yet
|
|---|
| 28714 | transitions: null,
|
|---|
| 28715 | pendingSuspenseBoundaries: null
|
|---|
| 28716 | };
|
|---|
| 28717 | uninitializedFiber.memoizedState = _initialState;
|
|---|
| 28718 | }
|
|---|
| 28719 |
|
|---|
| 28720 | initializeUpdateQueue(uninitializedFiber);
|
|---|
| 28721 | return root;
|
|---|
| 28722 | }
|
|---|
| 28723 |
|
|---|
| 28724 | var ReactVersion = '18.3.1';
|
|---|
| 28725 |
|
|---|
| 28726 | function createPortal(children, containerInfo, // TODO: figure out the API for cross-renderer implementation.
|
|---|
| 28727 | implementation) {
|
|---|
| 28728 | var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
|
|---|
| 28729 |
|
|---|
| 28730 | {
|
|---|
| 28731 | checkKeyStringCoercion(key);
|
|---|
| 28732 | }
|
|---|
| 28733 |
|
|---|
| 28734 | return {
|
|---|
| 28735 | // This tag allow us to uniquely identify this as a React Portal
|
|---|
| 28736 | $$typeof: REACT_PORTAL_TYPE,
|
|---|
| 28737 | key: key == null ? null : '' + key,
|
|---|
| 28738 | children: children,
|
|---|
| 28739 | containerInfo: containerInfo,
|
|---|
| 28740 | implementation: implementation
|
|---|
| 28741 | };
|
|---|
| 28742 | }
|
|---|
| 28743 |
|
|---|
| 28744 | var didWarnAboutNestedUpdates;
|
|---|
| 28745 | var didWarnAboutFindNodeInStrictMode;
|
|---|
| 28746 |
|
|---|
| 28747 | {
|
|---|
| 28748 | didWarnAboutNestedUpdates = false;
|
|---|
| 28749 | didWarnAboutFindNodeInStrictMode = {};
|
|---|
| 28750 | }
|
|---|
| 28751 |
|
|---|
| 28752 | function getContextForSubtree(parentComponent) {
|
|---|
| 28753 | if (!parentComponent) {
|
|---|
| 28754 | return emptyContextObject;
|
|---|
| 28755 | }
|
|---|
| 28756 |
|
|---|
| 28757 | var fiber = get(parentComponent);
|
|---|
| 28758 | var parentContext = findCurrentUnmaskedContext(fiber);
|
|---|
| 28759 |
|
|---|
| 28760 | if (fiber.tag === ClassComponent) {
|
|---|
| 28761 | var Component = fiber.type;
|
|---|
| 28762 |
|
|---|
| 28763 | if (isContextProvider(Component)) {
|
|---|
| 28764 | return processChildContext(fiber, Component, parentContext);
|
|---|
| 28765 | }
|
|---|
| 28766 | }
|
|---|
| 28767 |
|
|---|
| 28768 | return parentContext;
|
|---|
| 28769 | }
|
|---|
| 28770 |
|
|---|
| 28771 | function findHostInstanceWithWarning(component, methodName) {
|
|---|
| 28772 | {
|
|---|
| 28773 | var fiber = get(component);
|
|---|
| 28774 |
|
|---|
| 28775 | if (fiber === undefined) {
|
|---|
| 28776 | if (typeof component.render === 'function') {
|
|---|
| 28777 | throw new Error('Unable to find node on an unmounted component.');
|
|---|
| 28778 | } else {
|
|---|
| 28779 | var keys = Object.keys(component).join(',');
|
|---|
| 28780 | throw new Error("Argument appears to not be a ReactComponent. Keys: " + keys);
|
|---|
| 28781 | }
|
|---|
| 28782 | }
|
|---|
| 28783 |
|
|---|
| 28784 | var hostFiber = findCurrentHostFiber(fiber);
|
|---|
| 28785 |
|
|---|
| 28786 | if (hostFiber === null) {
|
|---|
| 28787 | return null;
|
|---|
| 28788 | }
|
|---|
| 28789 |
|
|---|
| 28790 | if (hostFiber.mode & StrictLegacyMode) {
|
|---|
| 28791 | var componentName = getComponentNameFromFiber(fiber) || 'Component';
|
|---|
| 28792 |
|
|---|
| 28793 | if (!didWarnAboutFindNodeInStrictMode[componentName]) {
|
|---|
| 28794 | didWarnAboutFindNodeInStrictMode[componentName] = true;
|
|---|
| 28795 | var previousFiber = current;
|
|---|
| 28796 |
|
|---|
| 28797 | try {
|
|---|
| 28798 | setCurrentFiber(hostFiber);
|
|---|
| 28799 |
|
|---|
| 28800 | if (fiber.mode & StrictLegacyMode) {
|
|---|
| 28801 | error('%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which is inside StrictMode. ' + 'Instead, add a ref directly to the element you want to reference. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-find-node', methodName, methodName, componentName);
|
|---|
| 28802 | } else {
|
|---|
| 28803 | error('%s is deprecated in StrictMode. ' + '%s was passed an instance of %s which renders StrictMode children. ' + 'Instead, add a ref directly to the element you want to reference. ' + 'Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-find-node', methodName, methodName, componentName);
|
|---|
| 28804 | }
|
|---|
| 28805 | } finally {
|
|---|
| 28806 | // Ideally this should reset to previous but this shouldn't be called in
|
|---|
| 28807 | // render and there's another warning for that anyway.
|
|---|
| 28808 | if (previousFiber) {
|
|---|
| 28809 | setCurrentFiber(previousFiber);
|
|---|
| 28810 | } else {
|
|---|
| 28811 | resetCurrentFiber();
|
|---|
| 28812 | }
|
|---|
| 28813 | }
|
|---|
| 28814 | }
|
|---|
| 28815 | }
|
|---|
| 28816 |
|
|---|
| 28817 | return hostFiber.stateNode;
|
|---|
| 28818 | }
|
|---|
| 28819 | }
|
|---|
| 28820 |
|
|---|
| 28821 | function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
|
|---|
| 28822 | var hydrate = false;
|
|---|
| 28823 | var initialChildren = null;
|
|---|
| 28824 | return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
|
|---|
| 28825 | }
|
|---|
| 28826 | function createHydrationContainer(initialChildren, // TODO: Remove `callback` when we delete legacy mode.
|
|---|
| 28827 | callback, containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) {
|
|---|
| 28828 | var hydrate = true;
|
|---|
| 28829 | var root = createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); // TODO: Move this to FiberRoot constructor
|
|---|
| 28830 |
|
|---|
| 28831 | root.context = getContextForSubtree(null); // Schedule the initial render. In a hydration root, this is different from
|
|---|
| 28832 | // a regular update because the initial render must match was was rendered
|
|---|
| 28833 | // on the server.
|
|---|
| 28834 | // NOTE: This update intentionally doesn't have a payload. We're only using
|
|---|
| 28835 | // the update to schedule work on the root fiber (and, for legacy roots, to
|
|---|
| 28836 | // enqueue the callback if one is provided).
|
|---|
| 28837 |
|
|---|
| 28838 | var current = root.current;
|
|---|
| 28839 | var eventTime = requestEventTime();
|
|---|
| 28840 | var lane = requestUpdateLane(current);
|
|---|
| 28841 | var update = createUpdate(eventTime, lane);
|
|---|
| 28842 | update.callback = callback !== undefined && callback !== null ? callback : null;
|
|---|
| 28843 | enqueueUpdate(current, update, lane);
|
|---|
| 28844 | scheduleInitialHydrationOnRoot(root, lane, eventTime);
|
|---|
| 28845 | return root;
|
|---|
| 28846 | }
|
|---|
| 28847 | function updateContainer(element, container, parentComponent, callback) {
|
|---|
| 28848 | {
|
|---|
| 28849 | onScheduleRoot(container, element);
|
|---|
| 28850 | }
|
|---|
| 28851 |
|
|---|
| 28852 | var current$1 = container.current;
|
|---|
| 28853 | var eventTime = requestEventTime();
|
|---|
| 28854 | var lane = requestUpdateLane(current$1);
|
|---|
| 28855 |
|
|---|
| 28856 | {
|
|---|
| 28857 | markRenderScheduled(lane);
|
|---|
| 28858 | }
|
|---|
| 28859 |
|
|---|
| 28860 | var context = getContextForSubtree(parentComponent);
|
|---|
| 28861 |
|
|---|
| 28862 | if (container.context === null) {
|
|---|
| 28863 | container.context = context;
|
|---|
| 28864 | } else {
|
|---|
| 28865 | container.pendingContext = context;
|
|---|
| 28866 | }
|
|---|
| 28867 |
|
|---|
| 28868 | {
|
|---|
| 28869 | if (isRendering && current !== null && !didWarnAboutNestedUpdates) {
|
|---|
| 28870 | didWarnAboutNestedUpdates = true;
|
|---|
| 28871 |
|
|---|
| 28872 | error('Render methods should be a pure function of props and state; ' + 'triggering nested component updates from render is not allowed. ' + 'If necessary, trigger nested updates in componentDidUpdate.\n\n' + 'Check the render method of %s.', getComponentNameFromFiber(current) || 'Unknown');
|
|---|
| 28873 | }
|
|---|
| 28874 | }
|
|---|
| 28875 |
|
|---|
| 28876 | var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property
|
|---|
| 28877 | // being called "element".
|
|---|
| 28878 |
|
|---|
| 28879 | update.payload = {
|
|---|
| 28880 | element: element
|
|---|
| 28881 | };
|
|---|
| 28882 | callback = callback === undefined ? null : callback;
|
|---|
| 28883 |
|
|---|
| 28884 | if (callback !== null) {
|
|---|
| 28885 | {
|
|---|
| 28886 | if (typeof callback !== 'function') {
|
|---|
| 28887 | error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback);
|
|---|
| 28888 | }
|
|---|
| 28889 | }
|
|---|
| 28890 |
|
|---|
| 28891 | update.callback = callback;
|
|---|
| 28892 | }
|
|---|
| 28893 |
|
|---|
| 28894 | var root = enqueueUpdate(current$1, update, lane);
|
|---|
| 28895 |
|
|---|
| 28896 | if (root !== null) {
|
|---|
| 28897 | scheduleUpdateOnFiber(root, current$1, lane, eventTime);
|
|---|
| 28898 | entangleTransitions(root, current$1, lane);
|
|---|
| 28899 | }
|
|---|
| 28900 |
|
|---|
| 28901 | return lane;
|
|---|
| 28902 | }
|
|---|
| 28903 | function getPublicRootInstance(container) {
|
|---|
| 28904 | var containerFiber = container.current;
|
|---|
| 28905 |
|
|---|
| 28906 | if (!containerFiber.child) {
|
|---|
| 28907 | return null;
|
|---|
| 28908 | }
|
|---|
| 28909 |
|
|---|
| 28910 | switch (containerFiber.child.tag) {
|
|---|
| 28911 | case HostComponent:
|
|---|
| 28912 | return getPublicInstance(containerFiber.child.stateNode);
|
|---|
| 28913 |
|
|---|
| 28914 | default:
|
|---|
| 28915 | return containerFiber.child.stateNode;
|
|---|
| 28916 | }
|
|---|
| 28917 | }
|
|---|
| 28918 | function attemptSynchronousHydration$1(fiber) {
|
|---|
| 28919 | switch (fiber.tag) {
|
|---|
| 28920 | case HostRoot:
|
|---|
| 28921 | {
|
|---|
| 28922 | var root = fiber.stateNode;
|
|---|
| 28923 |
|
|---|
| 28924 | if (isRootDehydrated(root)) {
|
|---|
| 28925 | // Flush the first scheduled "update".
|
|---|
| 28926 | var lanes = getHighestPriorityPendingLanes(root);
|
|---|
| 28927 | flushRoot(root, lanes);
|
|---|
| 28928 | }
|
|---|
| 28929 |
|
|---|
| 28930 | break;
|
|---|
| 28931 | }
|
|---|
| 28932 |
|
|---|
| 28933 | case SuspenseComponent:
|
|---|
| 28934 | {
|
|---|
| 28935 | flushSync(function () {
|
|---|
| 28936 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 28937 |
|
|---|
| 28938 | if (root !== null) {
|
|---|
| 28939 | var eventTime = requestEventTime();
|
|---|
| 28940 | scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime);
|
|---|
| 28941 | }
|
|---|
| 28942 | }); // If we're still blocked after this, we need to increase
|
|---|
| 28943 | // the priority of any promises resolving within this
|
|---|
| 28944 | // boundary so that they next attempt also has higher pri.
|
|---|
| 28945 |
|
|---|
| 28946 | var retryLane = SyncLane;
|
|---|
| 28947 | markRetryLaneIfNotHydrated(fiber, retryLane);
|
|---|
| 28948 | break;
|
|---|
| 28949 | }
|
|---|
| 28950 | }
|
|---|
| 28951 | }
|
|---|
| 28952 |
|
|---|
| 28953 | function markRetryLaneImpl(fiber, retryLane) {
|
|---|
| 28954 | var suspenseState = fiber.memoizedState;
|
|---|
| 28955 |
|
|---|
| 28956 | if (suspenseState !== null && suspenseState.dehydrated !== null) {
|
|---|
| 28957 | suspenseState.retryLane = higherPriorityLane(suspenseState.retryLane, retryLane);
|
|---|
| 28958 | }
|
|---|
| 28959 | } // Increases the priority of thenables when they resolve within this boundary.
|
|---|
| 28960 |
|
|---|
| 28961 |
|
|---|
| 28962 | function markRetryLaneIfNotHydrated(fiber, retryLane) {
|
|---|
| 28963 | markRetryLaneImpl(fiber, retryLane);
|
|---|
| 28964 | var alternate = fiber.alternate;
|
|---|
| 28965 |
|
|---|
| 28966 | if (alternate) {
|
|---|
| 28967 | markRetryLaneImpl(alternate, retryLane);
|
|---|
| 28968 | }
|
|---|
| 28969 | }
|
|---|
| 28970 | function attemptContinuousHydration$1(fiber) {
|
|---|
| 28971 | if (fiber.tag !== SuspenseComponent) {
|
|---|
| 28972 | // We ignore HostRoots here because we can't increase
|
|---|
| 28973 | // their priority and they should not suspend on I/O,
|
|---|
| 28974 | // since you have to wrap anything that might suspend in
|
|---|
| 28975 | // Suspense.
|
|---|
| 28976 | return;
|
|---|
| 28977 | }
|
|---|
| 28978 |
|
|---|
| 28979 | var lane = SelectiveHydrationLane;
|
|---|
| 28980 | var root = enqueueConcurrentRenderForLane(fiber, lane);
|
|---|
| 28981 |
|
|---|
| 28982 | if (root !== null) {
|
|---|
| 28983 | var eventTime = requestEventTime();
|
|---|
| 28984 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 28985 | }
|
|---|
| 28986 |
|
|---|
| 28987 | markRetryLaneIfNotHydrated(fiber, lane);
|
|---|
| 28988 | }
|
|---|
| 28989 | function attemptHydrationAtCurrentPriority$1(fiber) {
|
|---|
| 28990 | if (fiber.tag !== SuspenseComponent) {
|
|---|
| 28991 | // We ignore HostRoots here because we can't increase
|
|---|
| 28992 | // their priority other than synchronously flush it.
|
|---|
| 28993 | return;
|
|---|
| 28994 | }
|
|---|
| 28995 |
|
|---|
| 28996 | var lane = requestUpdateLane(fiber);
|
|---|
| 28997 | var root = enqueueConcurrentRenderForLane(fiber, lane);
|
|---|
| 28998 |
|
|---|
| 28999 | if (root !== null) {
|
|---|
| 29000 | var eventTime = requestEventTime();
|
|---|
| 29001 | scheduleUpdateOnFiber(root, fiber, lane, eventTime);
|
|---|
| 29002 | }
|
|---|
| 29003 |
|
|---|
| 29004 | markRetryLaneIfNotHydrated(fiber, lane);
|
|---|
| 29005 | }
|
|---|
| 29006 | function findHostInstanceWithNoPortals(fiber) {
|
|---|
| 29007 | var hostFiber = findCurrentHostFiberWithNoPortals(fiber);
|
|---|
| 29008 |
|
|---|
| 29009 | if (hostFiber === null) {
|
|---|
| 29010 | return null;
|
|---|
| 29011 | }
|
|---|
| 29012 |
|
|---|
| 29013 | return hostFiber.stateNode;
|
|---|
| 29014 | }
|
|---|
| 29015 |
|
|---|
| 29016 | var shouldErrorImpl = function (fiber) {
|
|---|
| 29017 | return null;
|
|---|
| 29018 | };
|
|---|
| 29019 |
|
|---|
| 29020 | function shouldError(fiber) {
|
|---|
| 29021 | return shouldErrorImpl(fiber);
|
|---|
| 29022 | }
|
|---|
| 29023 |
|
|---|
| 29024 | var shouldSuspendImpl = function (fiber) {
|
|---|
| 29025 | return false;
|
|---|
| 29026 | };
|
|---|
| 29027 |
|
|---|
| 29028 | function shouldSuspend(fiber) {
|
|---|
| 29029 | return shouldSuspendImpl(fiber);
|
|---|
| 29030 | }
|
|---|
| 29031 | var overrideHookState = null;
|
|---|
| 29032 | var overrideHookStateDeletePath = null;
|
|---|
| 29033 | var overrideHookStateRenamePath = null;
|
|---|
| 29034 | var overrideProps = null;
|
|---|
| 29035 | var overridePropsDeletePath = null;
|
|---|
| 29036 | var overridePropsRenamePath = null;
|
|---|
| 29037 | var scheduleUpdate = null;
|
|---|
| 29038 | var setErrorHandler = null;
|
|---|
| 29039 | var setSuspenseHandler = null;
|
|---|
| 29040 |
|
|---|
| 29041 | {
|
|---|
| 29042 | var copyWithDeleteImpl = function (obj, path, index) {
|
|---|
| 29043 | var key = path[index];
|
|---|
| 29044 | var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|---|
| 29045 |
|
|---|
| 29046 | if (index + 1 === path.length) {
|
|---|
| 29047 | if (isArray(updated)) {
|
|---|
| 29048 | updated.splice(key, 1);
|
|---|
| 29049 | } else {
|
|---|
| 29050 | delete updated[key];
|
|---|
| 29051 | }
|
|---|
| 29052 |
|
|---|
| 29053 | return updated;
|
|---|
| 29054 | } // $FlowFixMe number or string is fine here
|
|---|
| 29055 |
|
|---|
| 29056 |
|
|---|
| 29057 | updated[key] = copyWithDeleteImpl(obj[key], path, index + 1);
|
|---|
| 29058 | return updated;
|
|---|
| 29059 | };
|
|---|
| 29060 |
|
|---|
| 29061 | var copyWithDelete = function (obj, path) {
|
|---|
| 29062 | return copyWithDeleteImpl(obj, path, 0);
|
|---|
| 29063 | };
|
|---|
| 29064 |
|
|---|
| 29065 | var copyWithRenameImpl = function (obj, oldPath, newPath, index) {
|
|---|
| 29066 | var oldKey = oldPath[index];
|
|---|
| 29067 | var updated = isArray(obj) ? obj.slice() : assign({}, obj);
|
|---|
| 29068 |
|
|---|
| 29069 | if (index + 1 === oldPath.length) {
|
|---|
| 29070 | var newKey = newPath[index]; // $FlowFixMe number or string is fine here
|
|---|
| 29071 |
|
|---|
| 29072 | updated[newKey] = updated[oldKey];
|
|---|
| 29073 |
|
|---|
| 29074 | if (isArray(updated)) {
|
|---|
| 29075 | updated.splice(oldKey, 1);
|
|---|
| 29076 | } else {
|
|---|
| 29077 | delete updated[oldKey];
|
|---|
| 29078 | }
|
|---|
| 29079 | } else {
|
|---|
| 29080 | // $FlowFixMe number or string is fine here
|
|---|
| 29081 | updated[oldKey] = copyWithRenameImpl( // $FlowFixMe number or string is fine here
|
|---|
| 29082 | obj[oldKey], oldPath, newPath, index + 1);
|
|---|
| 29083 | }
|
|---|
| 29084 |
|
|---|
| 29085 | return updated;
|
|---|
| 29086 | };
|
|---|
| 29087 |
|
|---|
| 29088 | var copyWithRename = function (obj, oldPath, newPath) {
|
|---|
| 29089 | if (oldPath.length !== newPath.length) {
|
|---|
| 29090 | warn('copyWithRename() expects paths of the same length');
|
|---|
| 29091 |
|
|---|
| 29092 | return;
|
|---|
| 29093 | } else {
|
|---|
| 29094 | for (var i = 0; i < newPath.length - 1; i++) {
|
|---|
| 29095 | if (oldPath[i] !== newPath[i]) {
|
|---|
| 29096 | warn('copyWithRename() expects paths to be the same except for the deepest key');
|
|---|
| 29097 |
|
|---|
| 29098 | return;
|
|---|
| 29099 | }
|
|---|
| 29100 | }
|
|---|
| 29101 | }
|
|---|
| 29102 |
|
|---|
| 29103 | return copyWithRenameImpl(obj, oldPath, newPath, 0);
|
|---|
| 29104 | };
|
|---|
| 29105 |
|
|---|
| 29106 | var copyWithSetImpl = function (obj, path, index, value) {
|
|---|
| 29107 | if (index >= path.length) {
|
|---|
| 29108 | return value;
|
|---|
| 29109 | }
|
|---|
| 29110 |
|
|---|
| 29111 | var key = path[index];
|
|---|
| 29112 | var updated = isArray(obj) ? obj.slice() : assign({}, obj); // $FlowFixMe number or string is fine here
|
|---|
| 29113 |
|
|---|
| 29114 | updated[key] = copyWithSetImpl(obj[key], path, index + 1, value);
|
|---|
| 29115 | return updated;
|
|---|
| 29116 | };
|
|---|
| 29117 |
|
|---|
| 29118 | var copyWithSet = function (obj, path, value) {
|
|---|
| 29119 | return copyWithSetImpl(obj, path, 0, value);
|
|---|
| 29120 | };
|
|---|
| 29121 |
|
|---|
| 29122 | var findHook = function (fiber, id) {
|
|---|
| 29123 | // For now, the "id" of stateful hooks is just the stateful hook index.
|
|---|
| 29124 | // This may change in the future with e.g. nested hooks.
|
|---|
| 29125 | var currentHook = fiber.memoizedState;
|
|---|
| 29126 |
|
|---|
| 29127 | while (currentHook !== null && id > 0) {
|
|---|
| 29128 | currentHook = currentHook.next;
|
|---|
| 29129 | id--;
|
|---|
| 29130 | }
|
|---|
| 29131 |
|
|---|
| 29132 | return currentHook;
|
|---|
| 29133 | }; // Support DevTools editable values for useState and useReducer.
|
|---|
| 29134 |
|
|---|
| 29135 |
|
|---|
| 29136 | overrideHookState = function (fiber, id, path, value) {
|
|---|
| 29137 | var hook = findHook(fiber, id);
|
|---|
| 29138 |
|
|---|
| 29139 | if (hook !== null) {
|
|---|
| 29140 | var newState = copyWithSet(hook.memoizedState, path, value);
|
|---|
| 29141 | hook.memoizedState = newState;
|
|---|
| 29142 | hook.baseState = newState; // We aren't actually adding an update to the queue,
|
|---|
| 29143 | // because there is no update we can add for useReducer hooks that won't trigger an error.
|
|---|
| 29144 | // (There's no appropriate action type for DevTools overrides.)
|
|---|
| 29145 | // As a result though, React will see the scheduled update as a noop and bailout.
|
|---|
| 29146 | // Shallow cloning props works as a workaround for now to bypass the bailout check.
|
|---|
| 29147 |
|
|---|
| 29148 | fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|---|
| 29149 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29150 |
|
|---|
| 29151 | if (root !== null) {
|
|---|
| 29152 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29153 | }
|
|---|
| 29154 | }
|
|---|
| 29155 | };
|
|---|
| 29156 |
|
|---|
| 29157 | overrideHookStateDeletePath = function (fiber, id, path) {
|
|---|
| 29158 | var hook = findHook(fiber, id);
|
|---|
| 29159 |
|
|---|
| 29160 | if (hook !== null) {
|
|---|
| 29161 | var newState = copyWithDelete(hook.memoizedState, path);
|
|---|
| 29162 | hook.memoizedState = newState;
|
|---|
| 29163 | hook.baseState = newState; // We aren't actually adding an update to the queue,
|
|---|
| 29164 | // because there is no update we can add for useReducer hooks that won't trigger an error.
|
|---|
| 29165 | // (There's no appropriate action type for DevTools overrides.)
|
|---|
| 29166 | // As a result though, React will see the scheduled update as a noop and bailout.
|
|---|
| 29167 | // Shallow cloning props works as a workaround for now to bypass the bailout check.
|
|---|
| 29168 |
|
|---|
| 29169 | fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|---|
| 29170 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29171 |
|
|---|
| 29172 | if (root !== null) {
|
|---|
| 29173 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29174 | }
|
|---|
| 29175 | }
|
|---|
| 29176 | };
|
|---|
| 29177 |
|
|---|
| 29178 | overrideHookStateRenamePath = function (fiber, id, oldPath, newPath) {
|
|---|
| 29179 | var hook = findHook(fiber, id);
|
|---|
| 29180 |
|
|---|
| 29181 | if (hook !== null) {
|
|---|
| 29182 | var newState = copyWithRename(hook.memoizedState, oldPath, newPath);
|
|---|
| 29183 | hook.memoizedState = newState;
|
|---|
| 29184 | hook.baseState = newState; // We aren't actually adding an update to the queue,
|
|---|
| 29185 | // because there is no update we can add for useReducer hooks that won't trigger an error.
|
|---|
| 29186 | // (There's no appropriate action type for DevTools overrides.)
|
|---|
| 29187 | // As a result though, React will see the scheduled update as a noop and bailout.
|
|---|
| 29188 | // Shallow cloning props works as a workaround for now to bypass the bailout check.
|
|---|
| 29189 |
|
|---|
| 29190 | fiber.memoizedProps = assign({}, fiber.memoizedProps);
|
|---|
| 29191 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29192 |
|
|---|
| 29193 | if (root !== null) {
|
|---|
| 29194 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29195 | }
|
|---|
| 29196 | }
|
|---|
| 29197 | }; // Support DevTools props for function components, forwardRef, memo, host components, etc.
|
|---|
| 29198 |
|
|---|
| 29199 |
|
|---|
| 29200 | overrideProps = function (fiber, path, value) {
|
|---|
| 29201 | fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value);
|
|---|
| 29202 |
|
|---|
| 29203 | if (fiber.alternate) {
|
|---|
| 29204 | fiber.alternate.pendingProps = fiber.pendingProps;
|
|---|
| 29205 | }
|
|---|
| 29206 |
|
|---|
| 29207 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29208 |
|
|---|
| 29209 | if (root !== null) {
|
|---|
| 29210 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29211 | }
|
|---|
| 29212 | };
|
|---|
| 29213 |
|
|---|
| 29214 | overridePropsDeletePath = function (fiber, path) {
|
|---|
| 29215 | fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path);
|
|---|
| 29216 |
|
|---|
| 29217 | if (fiber.alternate) {
|
|---|
| 29218 | fiber.alternate.pendingProps = fiber.pendingProps;
|
|---|
| 29219 | }
|
|---|
| 29220 |
|
|---|
| 29221 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29222 |
|
|---|
| 29223 | if (root !== null) {
|
|---|
| 29224 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29225 | }
|
|---|
| 29226 | };
|
|---|
| 29227 |
|
|---|
| 29228 | overridePropsRenamePath = function (fiber, oldPath, newPath) {
|
|---|
| 29229 | fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath);
|
|---|
| 29230 |
|
|---|
| 29231 | if (fiber.alternate) {
|
|---|
| 29232 | fiber.alternate.pendingProps = fiber.pendingProps;
|
|---|
| 29233 | }
|
|---|
| 29234 |
|
|---|
| 29235 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29236 |
|
|---|
| 29237 | if (root !== null) {
|
|---|
| 29238 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29239 | }
|
|---|
| 29240 | };
|
|---|
| 29241 |
|
|---|
| 29242 | scheduleUpdate = function (fiber) {
|
|---|
| 29243 | var root = enqueueConcurrentRenderForLane(fiber, SyncLane);
|
|---|
| 29244 |
|
|---|
| 29245 | if (root !== null) {
|
|---|
| 29246 | scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp);
|
|---|
| 29247 | }
|
|---|
| 29248 | };
|
|---|
| 29249 |
|
|---|
| 29250 | setErrorHandler = function (newShouldErrorImpl) {
|
|---|
| 29251 | shouldErrorImpl = newShouldErrorImpl;
|
|---|
| 29252 | };
|
|---|
| 29253 |
|
|---|
| 29254 | setSuspenseHandler = function (newShouldSuspendImpl) {
|
|---|
| 29255 | shouldSuspendImpl = newShouldSuspendImpl;
|
|---|
| 29256 | };
|
|---|
| 29257 | }
|
|---|
| 29258 |
|
|---|
| 29259 | function findHostInstanceByFiber(fiber) {
|
|---|
| 29260 | var hostFiber = findCurrentHostFiber(fiber);
|
|---|
| 29261 |
|
|---|
| 29262 | if (hostFiber === null) {
|
|---|
| 29263 | return null;
|
|---|
| 29264 | }
|
|---|
| 29265 |
|
|---|
| 29266 | return hostFiber.stateNode;
|
|---|
| 29267 | }
|
|---|
| 29268 |
|
|---|
| 29269 | function emptyFindFiberByHostInstance(instance) {
|
|---|
| 29270 | return null;
|
|---|
| 29271 | }
|
|---|
| 29272 |
|
|---|
| 29273 | function getCurrentFiberForDevTools() {
|
|---|
| 29274 | return current;
|
|---|
| 29275 | }
|
|---|
| 29276 |
|
|---|
| 29277 | function injectIntoDevTools(devToolsConfig) {
|
|---|
| 29278 | var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance;
|
|---|
| 29279 | var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher;
|
|---|
| 29280 | return injectInternals({
|
|---|
| 29281 | bundleType: devToolsConfig.bundleType,
|
|---|
| 29282 | version: devToolsConfig.version,
|
|---|
| 29283 | rendererPackageName: devToolsConfig.rendererPackageName,
|
|---|
| 29284 | rendererConfig: devToolsConfig.rendererConfig,
|
|---|
| 29285 | overrideHookState: overrideHookState,
|
|---|
| 29286 | overrideHookStateDeletePath: overrideHookStateDeletePath,
|
|---|
| 29287 | overrideHookStateRenamePath: overrideHookStateRenamePath,
|
|---|
| 29288 | overrideProps: overrideProps,
|
|---|
| 29289 | overridePropsDeletePath: overridePropsDeletePath,
|
|---|
| 29290 | overridePropsRenamePath: overridePropsRenamePath,
|
|---|
| 29291 | setErrorHandler: setErrorHandler,
|
|---|
| 29292 | setSuspenseHandler: setSuspenseHandler,
|
|---|
| 29293 | scheduleUpdate: scheduleUpdate,
|
|---|
| 29294 | currentDispatcherRef: ReactCurrentDispatcher,
|
|---|
| 29295 | findHostInstanceByFiber: findHostInstanceByFiber,
|
|---|
| 29296 | findFiberByHostInstance: findFiberByHostInstance || emptyFindFiberByHostInstance,
|
|---|
| 29297 | // React Refresh
|
|---|
| 29298 | findHostInstancesForRefresh: findHostInstancesForRefresh ,
|
|---|
| 29299 | scheduleRefresh: scheduleRefresh ,
|
|---|
| 29300 | scheduleRoot: scheduleRoot ,
|
|---|
| 29301 | setRefreshHandler: setRefreshHandler ,
|
|---|
| 29302 | // Enables DevTools to append owner stacks to error messages in DEV mode.
|
|---|
| 29303 | getCurrentFiber: getCurrentFiberForDevTools ,
|
|---|
| 29304 | // Enables DevTools to detect reconciler version rather than renderer version
|
|---|
| 29305 | // which may not match for third party renderers.
|
|---|
| 29306 | reconcilerVersion: ReactVersion
|
|---|
| 29307 | });
|
|---|
| 29308 | }
|
|---|
| 29309 |
|
|---|
| 29310 | /* global reportError */
|
|---|
| 29311 |
|
|---|
| 29312 | var defaultOnRecoverableError = typeof reportError === 'function' ? // In modern browsers, reportError will dispatch an error event,
|
|---|
| 29313 | // emulating an uncaught JavaScript error.
|
|---|
| 29314 | reportError : function (error) {
|
|---|
| 29315 | // In older browsers and test environments, fallback to console.error.
|
|---|
| 29316 | // eslint-disable-next-line react-internal/no-production-logging
|
|---|
| 29317 | console['error'](error);
|
|---|
| 29318 | };
|
|---|
| 29319 |
|
|---|
| 29320 | function ReactDOMRoot(internalRoot) {
|
|---|
| 29321 | this._internalRoot = internalRoot;
|
|---|
| 29322 | }
|
|---|
| 29323 |
|
|---|
| 29324 | ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render = function (children) {
|
|---|
| 29325 | var root = this._internalRoot;
|
|---|
| 29326 |
|
|---|
| 29327 | if (root === null) {
|
|---|
| 29328 | throw new Error('Cannot update an unmounted root.');
|
|---|
| 29329 | }
|
|---|
| 29330 |
|
|---|
| 29331 | {
|
|---|
| 29332 | if (typeof arguments[1] === 'function') {
|
|---|
| 29333 | error('render(...): does not support the second callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().');
|
|---|
| 29334 | } else if (isValidContainer(arguments[1])) {
|
|---|
| 29335 | error('You passed a container to the second argument of root.render(...). ' + "You don't need to pass it again since you already passed it to create the root.");
|
|---|
| 29336 | } else if (typeof arguments[1] !== 'undefined') {
|
|---|
| 29337 | error('You passed a second argument to root.render(...) but it only accepts ' + 'one argument.');
|
|---|
| 29338 | }
|
|---|
| 29339 |
|
|---|
| 29340 | var container = root.containerInfo;
|
|---|
| 29341 |
|
|---|
| 29342 | if (container.nodeType !== COMMENT_NODE) {
|
|---|
| 29343 | var hostInstance = findHostInstanceWithNoPortals(root.current);
|
|---|
| 29344 |
|
|---|
| 29345 | if (hostInstance) {
|
|---|
| 29346 | if (hostInstance.parentNode !== container) {
|
|---|
| 29347 | error('render(...): It looks like the React-rendered content of the ' + 'root container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + "root.unmount() to empty a root's container.");
|
|---|
| 29348 | }
|
|---|
| 29349 | }
|
|---|
| 29350 | }
|
|---|
| 29351 | }
|
|---|
| 29352 |
|
|---|
| 29353 | updateContainer(children, root, null, null);
|
|---|
| 29354 | };
|
|---|
| 29355 |
|
|---|
| 29356 | ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount = function () {
|
|---|
| 29357 | {
|
|---|
| 29358 | if (typeof arguments[0] === 'function') {
|
|---|
| 29359 | error('unmount(...): does not support a callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().');
|
|---|
| 29360 | }
|
|---|
| 29361 | }
|
|---|
| 29362 |
|
|---|
| 29363 | var root = this._internalRoot;
|
|---|
| 29364 |
|
|---|
| 29365 | if (root !== null) {
|
|---|
| 29366 | this._internalRoot = null;
|
|---|
| 29367 | var container = root.containerInfo;
|
|---|
| 29368 |
|
|---|
| 29369 | {
|
|---|
| 29370 | if (isAlreadyRendering()) {
|
|---|
| 29371 | error('Attempted to synchronously unmount a root while React was already ' + 'rendering. React cannot finish unmounting the root until the ' + 'current render has completed, which may lead to a race condition.');
|
|---|
| 29372 | }
|
|---|
| 29373 | }
|
|---|
| 29374 |
|
|---|
| 29375 | flushSync(function () {
|
|---|
| 29376 | updateContainer(null, root, null, null);
|
|---|
| 29377 | });
|
|---|
| 29378 | unmarkContainerAsRoot(container);
|
|---|
| 29379 | }
|
|---|
| 29380 | };
|
|---|
| 29381 |
|
|---|
| 29382 | function createRoot(container, options) {
|
|---|
| 29383 | if (!isValidContainer(container)) {
|
|---|
| 29384 | throw new Error('createRoot(...): Target container is not a DOM element.');
|
|---|
| 29385 | }
|
|---|
| 29386 |
|
|---|
| 29387 | warnIfReactDOMContainerInDEV(container);
|
|---|
| 29388 | var isStrictMode = false;
|
|---|
| 29389 | var concurrentUpdatesByDefaultOverride = false;
|
|---|
| 29390 | var identifierPrefix = '';
|
|---|
| 29391 | var onRecoverableError = defaultOnRecoverableError;
|
|---|
| 29392 | var transitionCallbacks = null;
|
|---|
| 29393 |
|
|---|
| 29394 | if (options !== null && options !== undefined) {
|
|---|
| 29395 | {
|
|---|
| 29396 | if (options.hydrate) {
|
|---|
| 29397 | warn('hydrate through createRoot is deprecated. Use ReactDOMClient.hydrateRoot(container, <App />) instead.');
|
|---|
| 29398 | } else {
|
|---|
| 29399 | if (typeof options === 'object' && options !== null && options.$$typeof === REACT_ELEMENT_TYPE) {
|
|---|
| 29400 | error('You passed a JSX element to createRoot. You probably meant to ' + 'call root.render instead. ' + 'Example usage:\n\n' + ' let root = createRoot(domContainer);\n' + ' root.render(<App />);');
|
|---|
| 29401 | }
|
|---|
| 29402 | }
|
|---|
| 29403 | }
|
|---|
| 29404 |
|
|---|
| 29405 | if (options.unstable_strictMode === true) {
|
|---|
| 29406 | isStrictMode = true;
|
|---|
| 29407 | }
|
|---|
| 29408 |
|
|---|
| 29409 | if (options.identifierPrefix !== undefined) {
|
|---|
| 29410 | identifierPrefix = options.identifierPrefix;
|
|---|
| 29411 | }
|
|---|
| 29412 |
|
|---|
| 29413 | if (options.onRecoverableError !== undefined) {
|
|---|
| 29414 | onRecoverableError = options.onRecoverableError;
|
|---|
| 29415 | }
|
|---|
| 29416 |
|
|---|
| 29417 | if (options.transitionCallbacks !== undefined) {
|
|---|
| 29418 | transitionCallbacks = options.transitionCallbacks;
|
|---|
| 29419 | }
|
|---|
| 29420 | }
|
|---|
| 29421 |
|
|---|
| 29422 | var root = createContainer(container, ConcurrentRoot, null, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
|
|---|
| 29423 | markContainerAsRoot(root.current, container);
|
|---|
| 29424 | var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
|
|---|
| 29425 | listenToAllSupportedEvents(rootContainerElement);
|
|---|
| 29426 | return new ReactDOMRoot(root);
|
|---|
| 29427 | }
|
|---|
| 29428 |
|
|---|
| 29429 | function ReactDOMHydrationRoot(internalRoot) {
|
|---|
| 29430 | this._internalRoot = internalRoot;
|
|---|
| 29431 | }
|
|---|
| 29432 |
|
|---|
| 29433 | function scheduleHydration(target) {
|
|---|
| 29434 | if (target) {
|
|---|
| 29435 | queueExplicitHydrationTarget(target);
|
|---|
| 29436 | }
|
|---|
| 29437 | }
|
|---|
| 29438 |
|
|---|
| 29439 | ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = scheduleHydration;
|
|---|
| 29440 | function hydrateRoot(container, initialChildren, options) {
|
|---|
| 29441 | if (!isValidContainer(container)) {
|
|---|
| 29442 | throw new Error('hydrateRoot(...): Target container is not a DOM element.');
|
|---|
| 29443 | }
|
|---|
| 29444 |
|
|---|
| 29445 | warnIfReactDOMContainerInDEV(container);
|
|---|
| 29446 |
|
|---|
| 29447 | {
|
|---|
| 29448 | if (initialChildren === undefined) {
|
|---|
| 29449 | error('Must provide initial children as second argument to hydrateRoot. ' + 'Example usage: hydrateRoot(domContainer, <App />)');
|
|---|
| 29450 | }
|
|---|
| 29451 | } // For now we reuse the whole bag of options since they contain
|
|---|
| 29452 | // the hydration callbacks.
|
|---|
| 29453 |
|
|---|
| 29454 |
|
|---|
| 29455 | var hydrationCallbacks = options != null ? options : null; // TODO: Delete this option
|
|---|
| 29456 |
|
|---|
| 29457 | var mutableSources = options != null && options.hydratedSources || null;
|
|---|
| 29458 | var isStrictMode = false;
|
|---|
| 29459 | var concurrentUpdatesByDefaultOverride = false;
|
|---|
| 29460 | var identifierPrefix = '';
|
|---|
| 29461 | var onRecoverableError = defaultOnRecoverableError;
|
|---|
| 29462 |
|
|---|
| 29463 | if (options !== null && options !== undefined) {
|
|---|
| 29464 | if (options.unstable_strictMode === true) {
|
|---|
| 29465 | isStrictMode = true;
|
|---|
| 29466 | }
|
|---|
| 29467 |
|
|---|
| 29468 | if (options.identifierPrefix !== undefined) {
|
|---|
| 29469 | identifierPrefix = options.identifierPrefix;
|
|---|
| 29470 | }
|
|---|
| 29471 |
|
|---|
| 29472 | if (options.onRecoverableError !== undefined) {
|
|---|
| 29473 | onRecoverableError = options.onRecoverableError;
|
|---|
| 29474 | }
|
|---|
| 29475 | }
|
|---|
| 29476 |
|
|---|
| 29477 | var root = createHydrationContainer(initialChildren, null, container, ConcurrentRoot, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError);
|
|---|
| 29478 | markContainerAsRoot(root.current, container); // This can't be a comment node since hydration doesn't work on comment nodes anyway.
|
|---|
| 29479 |
|
|---|
| 29480 | listenToAllSupportedEvents(container);
|
|---|
| 29481 |
|
|---|
| 29482 | if (mutableSources) {
|
|---|
| 29483 | for (var i = 0; i < mutableSources.length; i++) {
|
|---|
| 29484 | var mutableSource = mutableSources[i];
|
|---|
| 29485 | registerMutableSourceForHydration(root, mutableSource);
|
|---|
| 29486 | }
|
|---|
| 29487 | }
|
|---|
| 29488 |
|
|---|
| 29489 | return new ReactDOMHydrationRoot(root);
|
|---|
| 29490 | }
|
|---|
| 29491 | function isValidContainer(node) {
|
|---|
| 29492 | return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || !disableCommentsAsDOMContainers ));
|
|---|
| 29493 | } // TODO: Remove this function which also includes comment nodes.
|
|---|
| 29494 | // We only use it in places that are currently more relaxed.
|
|---|
| 29495 |
|
|---|
| 29496 | function isValidContainerLegacy(node) {
|
|---|
| 29497 | return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || node.nodeType === COMMENT_NODE && node.nodeValue === ' react-mount-point-unstable '));
|
|---|
| 29498 | }
|
|---|
| 29499 |
|
|---|
| 29500 | function warnIfReactDOMContainerInDEV(container) {
|
|---|
| 29501 | {
|
|---|
| 29502 | if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') {
|
|---|
| 29503 | error('createRoot(): Creating roots directly with document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try using a container element created ' + 'for your app.');
|
|---|
| 29504 | }
|
|---|
| 29505 |
|
|---|
| 29506 | if (isContainerMarkedAsRoot(container)) {
|
|---|
| 29507 | if (container._reactRootContainer) {
|
|---|
| 29508 | error('You are calling ReactDOMClient.createRoot() on a container that was previously ' + 'passed to ReactDOM.render(). This is not supported.');
|
|---|
| 29509 | } else {
|
|---|
| 29510 | error('You are calling ReactDOMClient.createRoot() on a container that ' + 'has already been passed to createRoot() before. Instead, call ' + 'root.render() on the existing root instead if you want to update it.');
|
|---|
| 29511 | }
|
|---|
| 29512 | }
|
|---|
| 29513 | }
|
|---|
| 29514 | }
|
|---|
| 29515 |
|
|---|
| 29516 | var ReactCurrentOwner$3 = ReactSharedInternals.ReactCurrentOwner;
|
|---|
| 29517 | var topLevelUpdateWarnings;
|
|---|
| 29518 |
|
|---|
| 29519 | {
|
|---|
| 29520 | topLevelUpdateWarnings = function (container) {
|
|---|
| 29521 | if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) {
|
|---|
| 29522 | var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer.current);
|
|---|
| 29523 |
|
|---|
| 29524 | if (hostInstance) {
|
|---|
| 29525 | if (hostInstance.parentNode !== container) {
|
|---|
| 29526 | error('render(...): It looks like the React-rendered content of this ' + 'container was removed without using React. This is not ' + 'supported and will cause errors. Instead, call ' + 'ReactDOM.unmountComponentAtNode to empty a container.');
|
|---|
| 29527 | }
|
|---|
| 29528 | }
|
|---|
| 29529 | }
|
|---|
| 29530 |
|
|---|
| 29531 | var isRootRenderedBySomeReact = !!container._reactRootContainer;
|
|---|
| 29532 | var rootEl = getReactRootElementInContainer(container);
|
|---|
| 29533 | var hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl));
|
|---|
| 29534 |
|
|---|
| 29535 | if (hasNonRootReactChild && !isRootRenderedBySomeReact) {
|
|---|
| 29536 | error('render(...): Replacing React-rendered children with a new root ' + 'component. If you intended to update the children of this node, ' + 'you should instead have the existing children update their state ' + 'and render the new components instead of calling ReactDOM.render.');
|
|---|
| 29537 | }
|
|---|
| 29538 |
|
|---|
| 29539 | if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') {
|
|---|
| 29540 | error('render(): Rendering components directly into document.body is ' + 'discouraged, since its children are often manipulated by third-party ' + 'scripts and browser extensions. This may lead to subtle ' + 'reconciliation issues. Try rendering into a container element created ' + 'for your app.');
|
|---|
| 29541 | }
|
|---|
| 29542 | };
|
|---|
| 29543 | }
|
|---|
| 29544 |
|
|---|
| 29545 | function getReactRootElementInContainer(container) {
|
|---|
| 29546 | if (!container) {
|
|---|
| 29547 | return null;
|
|---|
| 29548 | }
|
|---|
| 29549 |
|
|---|
| 29550 | if (container.nodeType === DOCUMENT_NODE) {
|
|---|
| 29551 | return container.documentElement;
|
|---|
| 29552 | } else {
|
|---|
| 29553 | return container.firstChild;
|
|---|
| 29554 | }
|
|---|
| 29555 | }
|
|---|
| 29556 |
|
|---|
| 29557 | function noopOnRecoverableError() {// This isn't reachable because onRecoverableError isn't called in the
|
|---|
| 29558 | // legacy API.
|
|---|
| 29559 | }
|
|---|
| 29560 |
|
|---|
| 29561 | function legacyCreateRootFromDOMContainer(container, initialChildren, parentComponent, callback, isHydrationContainer) {
|
|---|
| 29562 | if (isHydrationContainer) {
|
|---|
| 29563 | if (typeof callback === 'function') {
|
|---|
| 29564 | var originalCallback = callback;
|
|---|
| 29565 |
|
|---|
| 29566 | callback = function () {
|
|---|
| 29567 | var instance = getPublicRootInstance(root);
|
|---|
| 29568 | originalCallback.call(instance);
|
|---|
| 29569 | };
|
|---|
| 29570 | }
|
|---|
| 29571 |
|
|---|
| 29572 | var root = createHydrationContainer(initialChildren, callback, container, LegacyRoot, null, // hydrationCallbacks
|
|---|
| 29573 | false, // isStrictMode
|
|---|
| 29574 | false, // concurrentUpdatesByDefaultOverride,
|
|---|
| 29575 | '', // identifierPrefix
|
|---|
| 29576 | noopOnRecoverableError);
|
|---|
| 29577 | container._reactRootContainer = root;
|
|---|
| 29578 | markContainerAsRoot(root.current, container);
|
|---|
| 29579 | var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
|
|---|
| 29580 | listenToAllSupportedEvents(rootContainerElement);
|
|---|
| 29581 | flushSync();
|
|---|
| 29582 | return root;
|
|---|
| 29583 | } else {
|
|---|
| 29584 | // First clear any existing content.
|
|---|
| 29585 | var rootSibling;
|
|---|
| 29586 |
|
|---|
| 29587 | while (rootSibling = container.lastChild) {
|
|---|
| 29588 | container.removeChild(rootSibling);
|
|---|
| 29589 | }
|
|---|
| 29590 |
|
|---|
| 29591 | if (typeof callback === 'function') {
|
|---|
| 29592 | var _originalCallback = callback;
|
|---|
| 29593 |
|
|---|
| 29594 | callback = function () {
|
|---|
| 29595 | var instance = getPublicRootInstance(_root);
|
|---|
| 29596 |
|
|---|
| 29597 | _originalCallback.call(instance);
|
|---|
| 29598 | };
|
|---|
| 29599 | }
|
|---|
| 29600 |
|
|---|
| 29601 | var _root = createContainer(container, LegacyRoot, null, // hydrationCallbacks
|
|---|
| 29602 | false, // isStrictMode
|
|---|
| 29603 | false, // concurrentUpdatesByDefaultOverride,
|
|---|
| 29604 | '', // identifierPrefix
|
|---|
| 29605 | noopOnRecoverableError);
|
|---|
| 29606 |
|
|---|
| 29607 | container._reactRootContainer = _root;
|
|---|
| 29608 | markContainerAsRoot(_root.current, container);
|
|---|
| 29609 |
|
|---|
| 29610 | var _rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container;
|
|---|
| 29611 |
|
|---|
| 29612 | listenToAllSupportedEvents(_rootContainerElement); // Initial mount should not be batched.
|
|---|
| 29613 |
|
|---|
| 29614 | flushSync(function () {
|
|---|
| 29615 | updateContainer(initialChildren, _root, parentComponent, callback);
|
|---|
| 29616 | });
|
|---|
| 29617 | return _root;
|
|---|
| 29618 | }
|
|---|
| 29619 | }
|
|---|
| 29620 |
|
|---|
| 29621 | function warnOnInvalidCallback$1(callback, callerName) {
|
|---|
| 29622 | {
|
|---|
| 29623 | if (callback !== null && typeof callback !== 'function') {
|
|---|
| 29624 | error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback);
|
|---|
| 29625 | }
|
|---|
| 29626 | }
|
|---|
| 29627 | }
|
|---|
| 29628 |
|
|---|
| 29629 | function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
|
|---|
| 29630 | {
|
|---|
| 29631 | topLevelUpdateWarnings(container);
|
|---|
| 29632 | warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render');
|
|---|
| 29633 | }
|
|---|
| 29634 |
|
|---|
| 29635 | var maybeRoot = container._reactRootContainer;
|
|---|
| 29636 | var root;
|
|---|
| 29637 |
|
|---|
| 29638 | if (!maybeRoot) {
|
|---|
| 29639 | // Initial mount
|
|---|
| 29640 | root = legacyCreateRootFromDOMContainer(container, children, parentComponent, callback, forceHydrate);
|
|---|
| 29641 | } else {
|
|---|
| 29642 | root = maybeRoot;
|
|---|
| 29643 |
|
|---|
| 29644 | if (typeof callback === 'function') {
|
|---|
| 29645 | var originalCallback = callback;
|
|---|
| 29646 |
|
|---|
| 29647 | callback = function () {
|
|---|
| 29648 | var instance = getPublicRootInstance(root);
|
|---|
| 29649 | originalCallback.call(instance);
|
|---|
| 29650 | };
|
|---|
| 29651 | } // Update
|
|---|
| 29652 |
|
|---|
| 29653 |
|
|---|
| 29654 | updateContainer(children, root, parentComponent, callback);
|
|---|
| 29655 | }
|
|---|
| 29656 |
|
|---|
| 29657 | return getPublicRootInstance(root);
|
|---|
| 29658 | }
|
|---|
| 29659 |
|
|---|
| 29660 | var didWarnAboutFindDOMNode = false;
|
|---|
| 29661 | function findDOMNode(componentOrElement) {
|
|---|
| 29662 | {
|
|---|
| 29663 | if (!didWarnAboutFindDOMNode) {
|
|---|
| 29664 | didWarnAboutFindDOMNode = true;
|
|---|
| 29665 |
|
|---|
| 29666 | error('findDOMNode is deprecated and will be removed in the next major ' + 'release. Instead, add a ref directly to the element you want ' + 'to reference. Learn more about using refs safely here: ' + 'https://reactjs.org/link/strict-mode-find-node');
|
|---|
| 29667 | }
|
|---|
| 29668 |
|
|---|
| 29669 | var owner = ReactCurrentOwner$3.current;
|
|---|
| 29670 |
|
|---|
| 29671 | if (owner !== null && owner.stateNode !== null) {
|
|---|
| 29672 | var warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender;
|
|---|
| 29673 |
|
|---|
| 29674 | if (!warnedAboutRefsInRender) {
|
|---|
| 29675 | error('%s is accessing findDOMNode 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.', getComponentNameFromType(owner.type) || 'A component');
|
|---|
| 29676 | }
|
|---|
| 29677 |
|
|---|
| 29678 | owner.stateNode._warnedAboutRefsInRender = true;
|
|---|
| 29679 | }
|
|---|
| 29680 | }
|
|---|
| 29681 |
|
|---|
| 29682 | if (componentOrElement == null) {
|
|---|
| 29683 | return null;
|
|---|
| 29684 | }
|
|---|
| 29685 |
|
|---|
| 29686 | if (componentOrElement.nodeType === ELEMENT_NODE) {
|
|---|
| 29687 | return componentOrElement;
|
|---|
| 29688 | }
|
|---|
| 29689 |
|
|---|
| 29690 | {
|
|---|
| 29691 | return findHostInstanceWithWarning(componentOrElement, 'findDOMNode');
|
|---|
| 29692 | }
|
|---|
| 29693 | }
|
|---|
| 29694 | function hydrate(element, container, callback) {
|
|---|
| 29695 | {
|
|---|
| 29696 | error('ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot ' + 'instead. Until you switch to the new API, your app will behave as ' + "if it's running React 17. Learn " + 'more: https://reactjs.org/link/switch-to-createroot');
|
|---|
| 29697 | }
|
|---|
| 29698 |
|
|---|
| 29699 | if (!isValidContainerLegacy(container)) {
|
|---|
| 29700 | throw new Error('Target container is not a DOM element.');
|
|---|
| 29701 | }
|
|---|
| 29702 |
|
|---|
| 29703 | {
|
|---|
| 29704 | var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
|
|---|
| 29705 |
|
|---|
| 29706 | if (isModernRoot) {
|
|---|
| 29707 | error('You are calling ReactDOM.hydrate() on a container that was previously ' + 'passed to ReactDOMClient.createRoot(). This is not supported. ' + 'Did you mean to call hydrateRoot(container, element)?');
|
|---|
| 29708 | }
|
|---|
| 29709 | } // TODO: throw or warn if we couldn't hydrate?
|
|---|
| 29710 |
|
|---|
| 29711 |
|
|---|
| 29712 | return legacyRenderSubtreeIntoContainer(null, element, container, true, callback);
|
|---|
| 29713 | }
|
|---|
| 29714 | function render(element, container, callback) {
|
|---|
| 29715 | {
|
|---|
| 29716 | error('ReactDOM.render is no longer supported in React 18. Use createRoot ' + 'instead. Until you switch to the new API, your app will behave as ' + "if it's running React 17. Learn " + 'more: https://reactjs.org/link/switch-to-createroot');
|
|---|
| 29717 | }
|
|---|
| 29718 |
|
|---|
| 29719 | if (!isValidContainerLegacy(container)) {
|
|---|
| 29720 | throw new Error('Target container is not a DOM element.');
|
|---|
| 29721 | }
|
|---|
| 29722 |
|
|---|
| 29723 | {
|
|---|
| 29724 | var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
|
|---|
| 29725 |
|
|---|
| 29726 | if (isModernRoot) {
|
|---|
| 29727 | error('You are calling ReactDOM.render() on a container that was previously ' + 'passed to ReactDOMClient.createRoot(). This is not supported. ' + 'Did you mean to call root.render(element)?');
|
|---|
| 29728 | }
|
|---|
| 29729 | }
|
|---|
| 29730 |
|
|---|
| 29731 | return legacyRenderSubtreeIntoContainer(null, element, container, false, callback);
|
|---|
| 29732 | }
|
|---|
| 29733 | function unstable_renderSubtreeIntoContainer(parentComponent, element, containerNode, callback) {
|
|---|
| 29734 | {
|
|---|
| 29735 | error('ReactDOM.unstable_renderSubtreeIntoContainer() is no longer supported ' + 'in React 18. Consider using a portal instead. Until you switch to ' + "the createRoot API, your app will behave as if it's running React " + '17. Learn more: https://reactjs.org/link/switch-to-createroot');
|
|---|
| 29736 | }
|
|---|
| 29737 |
|
|---|
| 29738 | if (!isValidContainerLegacy(containerNode)) {
|
|---|
| 29739 | throw new Error('Target container is not a DOM element.');
|
|---|
| 29740 | }
|
|---|
| 29741 |
|
|---|
| 29742 | if (parentComponent == null || !has(parentComponent)) {
|
|---|
| 29743 | throw new Error('parentComponent must be a valid React Component');
|
|---|
| 29744 | }
|
|---|
| 29745 |
|
|---|
| 29746 | return legacyRenderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback);
|
|---|
| 29747 | }
|
|---|
| 29748 | var didWarnAboutUnmountComponentAtNode = false;
|
|---|
| 29749 | function unmountComponentAtNode(container) {
|
|---|
| 29750 | {
|
|---|
| 29751 | if (!didWarnAboutUnmountComponentAtNode) {
|
|---|
| 29752 | didWarnAboutUnmountComponentAtNode = true;
|
|---|
| 29753 |
|
|---|
| 29754 | error('unmountComponentAtNode is deprecated and will be removed in the ' + 'next major release. Switch to the createRoot API. Learn ' + 'more: https://reactjs.org/link/switch-to-createroot');
|
|---|
| 29755 | }
|
|---|
| 29756 | }
|
|---|
| 29757 |
|
|---|
| 29758 | if (!isValidContainerLegacy(container)) {
|
|---|
| 29759 | throw new Error('unmountComponentAtNode(...): Target container is not a DOM element.');
|
|---|
| 29760 | }
|
|---|
| 29761 |
|
|---|
| 29762 | {
|
|---|
| 29763 | var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined;
|
|---|
| 29764 |
|
|---|
| 29765 | if (isModernRoot) {
|
|---|
| 29766 | error('You are calling ReactDOM.unmountComponentAtNode() on a container that was previously ' + 'passed to ReactDOMClient.createRoot(). This is not supported. Did you mean to call root.unmount()?');
|
|---|
| 29767 | }
|
|---|
| 29768 | }
|
|---|
| 29769 |
|
|---|
| 29770 | if (container._reactRootContainer) {
|
|---|
| 29771 | {
|
|---|
| 29772 | var rootEl = getReactRootElementInContainer(container);
|
|---|
| 29773 | var renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl);
|
|---|
| 29774 |
|
|---|
| 29775 | if (renderedByDifferentReact) {
|
|---|
| 29776 | error("unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by another copy of React.');
|
|---|
| 29777 | }
|
|---|
| 29778 | } // Unmount should not be batched.
|
|---|
| 29779 |
|
|---|
| 29780 |
|
|---|
| 29781 | flushSync(function () {
|
|---|
| 29782 | legacyRenderSubtreeIntoContainer(null, null, container, false, function () {
|
|---|
| 29783 | // $FlowFixMe This should probably use `delete container._reactRootContainer`
|
|---|
| 29784 | container._reactRootContainer = null;
|
|---|
| 29785 | unmarkContainerAsRoot(container);
|
|---|
| 29786 | });
|
|---|
| 29787 | }); // If you call unmountComponentAtNode twice in quick succession, you'll
|
|---|
| 29788 | // get `true` twice. That's probably fine?
|
|---|
| 29789 |
|
|---|
| 29790 | return true;
|
|---|
| 29791 | } else {
|
|---|
| 29792 | {
|
|---|
| 29793 | var _rootEl = getReactRootElementInContainer(container);
|
|---|
| 29794 |
|
|---|
| 29795 | var hasNonRootReactChild = !!(_rootEl && getInstanceFromNode(_rootEl)); // Check if the container itself is a React root node.
|
|---|
| 29796 |
|
|---|
| 29797 | var isContainerReactRoot = container.nodeType === ELEMENT_NODE && isValidContainerLegacy(container.parentNode) && !!container.parentNode._reactRootContainer;
|
|---|
| 29798 |
|
|---|
| 29799 | if (hasNonRootReactChild) {
|
|---|
| 29800 | error("unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by React and is not a top-level container. %s', isContainerReactRoot ? 'You may have accidentally passed in a React root node instead ' + 'of its container.' : 'Instead, have the parent component update its state and ' + 'rerender in order to remove this component.');
|
|---|
| 29801 | }
|
|---|
| 29802 | }
|
|---|
| 29803 |
|
|---|
| 29804 | return false;
|
|---|
| 29805 | }
|
|---|
| 29806 | }
|
|---|
| 29807 |
|
|---|
| 29808 | setAttemptSynchronousHydration(attemptSynchronousHydration$1);
|
|---|
| 29809 | setAttemptContinuousHydration(attemptContinuousHydration$1);
|
|---|
| 29810 | setAttemptHydrationAtCurrentPriority(attemptHydrationAtCurrentPriority$1);
|
|---|
| 29811 | setGetCurrentUpdatePriority(getCurrentUpdatePriority);
|
|---|
| 29812 | setAttemptHydrationAtPriority(runWithPriority);
|
|---|
| 29813 |
|
|---|
| 29814 | {
|
|---|
| 29815 | if (typeof Map !== 'function' || // $FlowIssue Flow incorrectly thinks Map has no prototype
|
|---|
| 29816 | Map.prototype == null || typeof Map.prototype.forEach !== 'function' || typeof Set !== 'function' || // $FlowIssue Flow incorrectly thinks Set has no prototype
|
|---|
| 29817 | Set.prototype == null || typeof Set.prototype.clear !== 'function' || typeof Set.prototype.forEach !== 'function') {
|
|---|
| 29818 | error('React depends on Map and Set built-in types. Make sure that you load a ' + 'polyfill in older browsers. https://reactjs.org/link/react-polyfills');
|
|---|
| 29819 | }
|
|---|
| 29820 | }
|
|---|
| 29821 |
|
|---|
| 29822 | setRestoreImplementation(restoreControlledState$3);
|
|---|
| 29823 | setBatchingImplementation(batchedUpdates$1, discreteUpdates, flushSync);
|
|---|
| 29824 |
|
|---|
| 29825 | function createPortal$1(children, container) {
|
|---|
| 29826 | var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
|
|---|
| 29827 |
|
|---|
| 29828 | if (!isValidContainer(container)) {
|
|---|
| 29829 | throw new Error('Target container is not a DOM element.');
|
|---|
| 29830 | } // TODO: pass ReactDOM portal implementation as third argument
|
|---|
| 29831 | // $FlowFixMe The Flow type is opaque but there's no way to actually create it.
|
|---|
| 29832 |
|
|---|
| 29833 |
|
|---|
| 29834 | return createPortal(children, container, null, key);
|
|---|
| 29835 | }
|
|---|
| 29836 |
|
|---|
| 29837 | function renderSubtreeIntoContainer(parentComponent, element, containerNode, callback) {
|
|---|
| 29838 | return unstable_renderSubtreeIntoContainer(parentComponent, element, containerNode, callback);
|
|---|
| 29839 | }
|
|---|
| 29840 |
|
|---|
| 29841 | var Internals = {
|
|---|
| 29842 | usingClientEntryPoint: false,
|
|---|
| 29843 | // Keep in sync with ReactTestUtils.js.
|
|---|
| 29844 | // This is an array for better minification.
|
|---|
| 29845 | Events: [getInstanceFromNode, getNodeFromInstance, getFiberCurrentPropsFromNode, enqueueStateRestore, restoreStateIfNeeded, batchedUpdates$1]
|
|---|
| 29846 | };
|
|---|
| 29847 |
|
|---|
| 29848 | function createRoot$1(container, options) {
|
|---|
| 29849 | {
|
|---|
| 29850 | if (!Internals.usingClientEntryPoint && !false) {
|
|---|
| 29851 | error('You are importing createRoot from "react-dom" which is not supported. ' + 'You should instead import it from "react-dom/client".');
|
|---|
| 29852 | }
|
|---|
| 29853 | }
|
|---|
| 29854 |
|
|---|
| 29855 | return createRoot(container, options);
|
|---|
| 29856 | }
|
|---|
| 29857 |
|
|---|
| 29858 | function hydrateRoot$1(container, initialChildren, options) {
|
|---|
| 29859 | {
|
|---|
| 29860 | if (!Internals.usingClientEntryPoint && !false) {
|
|---|
| 29861 | error('You are importing hydrateRoot from "react-dom" which is not supported. ' + 'You should instead import it from "react-dom/client".');
|
|---|
| 29862 | }
|
|---|
| 29863 | }
|
|---|
| 29864 |
|
|---|
| 29865 | return hydrateRoot(container, initialChildren, options);
|
|---|
| 29866 | } // Overload the definition to the two valid signatures.
|
|---|
| 29867 | // Warning, this opts-out of checking the function body.
|
|---|
| 29868 |
|
|---|
| 29869 |
|
|---|
| 29870 | // eslint-disable-next-line no-redeclare
|
|---|
| 29871 | function flushSync$1(fn) {
|
|---|
| 29872 | {
|
|---|
| 29873 | if (isAlreadyRendering()) {
|
|---|
| 29874 | error('flushSync was called from inside a lifecycle method. React cannot ' + 'flush when React is already rendering. Consider moving this call to ' + 'a scheduler task or micro task.');
|
|---|
| 29875 | }
|
|---|
| 29876 | }
|
|---|
| 29877 |
|
|---|
| 29878 | return flushSync(fn);
|
|---|
| 29879 | }
|
|---|
| 29880 | var foundDevTools = injectIntoDevTools({
|
|---|
| 29881 | findFiberByHostInstance: getClosestInstanceFromNode,
|
|---|
| 29882 | bundleType: 1 ,
|
|---|
| 29883 | version: ReactVersion,
|
|---|
| 29884 | rendererPackageName: 'react-dom'
|
|---|
| 29885 | });
|
|---|
| 29886 |
|
|---|
| 29887 | {
|
|---|
| 29888 | if (!foundDevTools && canUseDOM && window.top === window.self) {
|
|---|
| 29889 | // If we're in Chrome or Firefox, provide a download link if not installed.
|
|---|
| 29890 | if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) {
|
|---|
| 29891 | var protocol = window.location.protocol; // Don't warn in exotic cases like chrome-extension://.
|
|---|
| 29892 |
|
|---|
| 29893 | if (/^(https?|file):$/.test(protocol)) {
|
|---|
| 29894 | // eslint-disable-next-line react-internal/no-production-logging
|
|---|
| 29895 | console.info('%cDownload the React DevTools ' + 'for a better development experience: ' + 'https://reactjs.org/link/react-devtools' + (protocol === 'file:' ? '\nYou might need to use a local HTTP server (instead of file://): ' + 'https://reactjs.org/link/react-devtools-faq' : ''), 'font-weight:bold');
|
|---|
| 29896 | }
|
|---|
| 29897 | }
|
|---|
| 29898 | }
|
|---|
| 29899 | }
|
|---|
| 29900 |
|
|---|
| 29901 | exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals;
|
|---|
| 29902 | exports.createPortal = createPortal$1;
|
|---|
| 29903 | exports.createRoot = createRoot$1;
|
|---|
| 29904 | exports.findDOMNode = findDOMNode;
|
|---|
| 29905 | exports.flushSync = flushSync$1;
|
|---|
| 29906 | exports.hydrate = hydrate;
|
|---|
| 29907 | exports.hydrateRoot = hydrateRoot$1;
|
|---|
| 29908 | exports.render = render;
|
|---|
| 29909 | exports.unmountComponentAtNode = unmountComponentAtNode;
|
|---|
| 29910 | exports.unstable_batchedUpdates = batchedUpdates$1;
|
|---|
| 29911 | exports.unstable_renderSubtreeIntoContainer = renderSubtreeIntoContainer;
|
|---|
| 29912 | exports.version = ReactVersion;
|
|---|
| 29913 | /* global __REACT_DEVTOOLS_GLOBAL_HOOK__ */
|
|---|
| 29914 | if (
|
|---|
| 29915 | typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined' &&
|
|---|
| 29916 | typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop ===
|
|---|
| 29917 | 'function'
|
|---|
| 29918 | ) {
|
|---|
| 29919 | __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(new Error());
|
|---|
| 29920 | }
|
|---|
| 29921 |
|
|---|
| 29922 | })();
|
|---|
| 29923 | }
|
|---|