Mercurial
comparison third_party/bun/node_modules/react-dom/umd/react-dom.development.js @ 12:de54585a40f1
Adding bun and node modules.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Thu, 02 Oct 2025 14:39:48 -0700 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 11:f33d9ff8b6e8 | 12:de54585a40f1 |
|---|---|
| 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 (function (global, factory) { | |
| 11 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : | |
| 12 typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : | |
| 13 (global = global || self, factory(global.ReactDOM = {}, global.React)); | |
| 14 }(this, (function (exports, React) { 'use strict'; | |
| 15 | |
| 16 var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; | |
| 17 | |
| 18 var suppressWarning = false; | |
| 19 function setSuppressWarning(newSuppressWarning) { | |
| 20 { | |
| 21 suppressWarning = newSuppressWarning; | |
| 22 } | |
| 23 } // In DEV, calls to console.warn and console.error get replaced | |
| 24 // by calls to these methods by a Babel plugin. | |
| 25 // | |
| 26 // In PROD (or in packages without access to React internals), | |
| 27 // they are left as they are instead. | |
| 28 | |
| 29 function warn(format) { | |
| 30 { | |
| 31 if (!suppressWarning) { | |
| 32 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { | |
| 33 args[_key - 1] = arguments[_key]; | |
| 34 } | |
| 35 | |
| 36 printWarning('warn', format, args); | |
| 37 } | |
| 38 } | |
| 39 } | |
| 40 function error(format) { | |
| 41 { | |
| 42 if (!suppressWarning) { | |
| 43 for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | |
| 44 args[_key2 - 1] = arguments[_key2]; | |
| 45 } | |
| 46 | |
| 47 printWarning('error', format, args); | |
| 48 } | |
| 49 } | |
| 50 } | |
| 51 | |
| 52 function printWarning(level, format, args) { | |
| 53 // When changing this logic, you might want to also | |
| 54 // update consoleWithStackDev.www.js as well. | |
| 55 { | |
| 56 var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; | |
| 57 var stack = ReactDebugCurrentFrame.getStackAddendum(); | |
| 58 | |
| 59 if (stack !== '') { | |
| 60 format += '%s'; | |
| 61 args = args.concat([stack]); | |
| 62 } // eslint-disable-next-line react-internal/safe-string-coercion | |
| 63 | |
| 64 | |
| 65 var argsWithFormat = args.map(function (item) { | |
| 66 return String(item); | |
| 67 }); // Careful: RN currently depends on this prefix | |
| 68 | |
| 69 argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it | |
| 70 // breaks IE9: https://github.com/facebook/react/issues/13610 | |
| 71 // eslint-disable-next-line react-internal/no-production-logging | |
| 72 | |
| 73 Function.prototype.apply.call(console[level], console, argsWithFormat); | |
| 74 } | |
| 75 } | |
| 76 | |
| 77 var FunctionComponent = 0; | |
| 78 var ClassComponent = 1; | |
| 79 var IndeterminateComponent = 2; // Before we know whether it is function or class | |
| 80 | |
| 81 var HostRoot = 3; // Root of a host tree. Could be nested inside another node. | |
| 82 | |
| 83 var HostPortal = 4; // A subtree. Could be an entry point to a different renderer. | |
| 84 | |
| 85 var HostComponent = 5; | |
| 86 var HostText = 6; | |
| 87 var Fragment = 7; | |
| 88 var Mode = 8; | |
| 89 var ContextConsumer = 9; | |
| 90 var ContextProvider = 10; | |
| 91 var ForwardRef = 11; | |
| 92 var Profiler = 12; | |
| 93 var SuspenseComponent = 13; | |
| 94 var MemoComponent = 14; | |
| 95 var SimpleMemoComponent = 15; | |
| 96 var LazyComponent = 16; | |
| 97 var IncompleteClassComponent = 17; | |
| 98 var DehydratedFragment = 18; | |
| 99 var SuspenseListComponent = 19; | |
| 100 var ScopeComponent = 21; | |
| 101 var OffscreenComponent = 22; | |
| 102 var LegacyHiddenComponent = 23; | |
| 103 var CacheComponent = 24; | |
| 104 var TracingMarkerComponent = 25; | |
| 105 | |
| 106 // ----------------------------------------------------------------------------- | |
| 107 | |
| 108 var enableClientRenderFallbackOnTextMismatch = true; // TODO: Need to review this code one more time before landing | |
| 109 // the react-reconciler package. | |
| 110 | |
| 111 var enableNewReconciler = false; // Support legacy Primer support on internal FB www | |
| 112 | |
| 113 var enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics. | |
| 114 | |
| 115 var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber | |
| 116 | |
| 117 var enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz | |
| 118 // React DOM Chopping Block | |
| 119 // | |
| 120 // Similar to main Chopping Block but only flags related to React DOM. These are | |
| 121 // grouped because we will likely batch all of them into a single major release. | |
| 122 // ----------------------------------------------------------------------------- | |
| 123 // Disable support for comment nodes as React DOM containers. Already disabled | |
| 124 // in open source, but www codebase still relies on it. Need to remove. | |
| 125 | |
| 126 var disableCommentsAsDOMContainers = true; // Disable javascript: URL strings in href for XSS protection. | |
| 127 // and client rendering, mostly to allow JSX attributes to apply to the custom | |
| 128 // element's object properties instead of only HTML attributes. | |
| 129 // https://github.com/facebook/react/issues/11347 | |
| 130 | |
| 131 var enableCustomElementPropertySupport = false; // Disables children for <textarea> elements | |
| 132 var warnAboutStringRefs = true; // ----------------------------------------------------------------------------- | |
| 133 // Debugging and DevTools | |
| 134 // ----------------------------------------------------------------------------- | |
| 135 // Adds user timing marks for e.g. state updates, suspense, and work loop stuff, | |
| 136 // for an experimental timeline tool. | |
| 137 | |
| 138 var enableSchedulingProfiler = true; // Helps identify side effects in render-phase lifecycle hooks and setState | |
| 139 | |
| 140 var enableProfilerTimer = true; // Record durations for commit and passive effects phases. | |
| 141 | |
| 142 var enableProfilerCommitHooks = true; // Phase param passed to onRender callback differentiates between an "update" and a "cascading-update". | |
| 143 | |
| 144 var allNativeEvents = new Set(); | |
| 145 /** | |
| 146 * Mapping from registration name to event name | |
| 147 */ | |
| 148 | |
| 149 | |
| 150 var registrationNameDependencies = {}; | |
| 151 /** | |
| 152 * Mapping from lowercase registration names to the properly cased version, | |
| 153 * used to warn in the case of missing event handlers. Available | |
| 154 * only in true. | |
| 155 * @type {Object} | |
| 156 */ | |
| 157 | |
| 158 var possibleRegistrationNames = {} ; // Trust the developer to only use possibleRegistrationNames in true | |
| 159 | |
| 160 function registerTwoPhaseEvent(registrationName, dependencies) { | |
| 161 registerDirectEvent(registrationName, dependencies); | |
| 162 registerDirectEvent(registrationName + 'Capture', dependencies); | |
| 163 } | |
| 164 function registerDirectEvent(registrationName, dependencies) { | |
| 165 { | |
| 166 if (registrationNameDependencies[registrationName]) { | |
| 167 error('EventRegistry: More than one plugin attempted to publish the same ' + 'registration name, `%s`.', registrationName); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 registrationNameDependencies[registrationName] = dependencies; | |
| 172 | |
| 173 { | |
| 174 var lowerCasedName = registrationName.toLowerCase(); | |
| 175 possibleRegistrationNames[lowerCasedName] = registrationName; | |
| 176 | |
| 177 if (registrationName === 'onDoubleClick') { | |
| 178 possibleRegistrationNames.ondblclick = registrationName; | |
| 179 } | |
| 180 } | |
| 181 | |
| 182 for (var i = 0; i < dependencies.length; i++) { | |
| 183 allNativeEvents.add(dependencies[i]); | |
| 184 } | |
| 185 } | |
| 186 | |
| 187 var canUseDOM = !!(typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined'); | |
| 188 | |
| 189 var hasOwnProperty = Object.prototype.hasOwnProperty; | |
| 190 | |
| 191 /* | |
| 192 * The `'' + value` pattern (used in in perf-sensitive code) throws for Symbol | |
| 193 * and Temporal.* types. See https://github.com/facebook/react/pull/22064. | |
| 194 * | |
| 195 * The functions in this module will throw an easier-to-understand, | |
| 196 * easier-to-debug exception with a clear errors message message explaining the | |
| 197 * problem. (Instead of a confusing exception thrown inside the implementation | |
| 198 * of the `value` object). | |
| 199 */ | |
| 200 // $FlowFixMe only called in DEV, so void return is not possible. | |
| 201 function typeName(value) { | |
| 202 { | |
| 203 // toStringTag is needed for namespaced types like Temporal.Instant | |
| 204 var hasToStringTag = typeof Symbol === 'function' && Symbol.toStringTag; | |
| 205 var type = hasToStringTag && value[Symbol.toStringTag] || value.constructor.name || 'Object'; | |
| 206 return type; | |
| 207 } | |
| 208 } // $FlowFixMe only called in DEV, so void return is not possible. | |
| 209 | |
| 210 | |
| 211 function willCoercionThrow(value) { | |
| 212 { | |
| 213 try { | |
| 214 testStringCoercion(value); | |
| 215 return false; | |
| 216 } catch (e) { | |
| 217 return true; | |
| 218 } | |
| 219 } | |
| 220 } | |
| 221 | |
| 222 function testStringCoercion(value) { | |
| 223 // If you ended up here by following an exception call stack, here's what's | |
| 224 // happened: you supplied an object or symbol value to React (as a prop, key, | |
| 225 // DOM attribute, CSS property, string ref, etc.) and when React tried to | |
| 226 // coerce it to a string using `'' + value`, an exception was thrown. | |
| 227 // | |
| 228 // The most common types that will cause this exception are `Symbol` instances | |
| 229 // and Temporal objects like `Temporal.Instant`. But any object that has a | |
| 230 // `valueOf` or `[Symbol.toPrimitive]` method that throws will also cause this | |
| 231 // exception. (Library authors do this to prevent users from using built-in | |
| 232 // numeric operators like `+` or comparison operators like `>=` because custom | |
| 233 // methods are needed to perform accurate arithmetic or comparison.) | |
| 234 // | |
| 235 // To fix the problem, coerce this object or symbol value to a string before | |
| 236 // passing it to React. The most reliable way is usually `String(value)`. | |
| 237 // | |
| 238 // To find which value is throwing, check the browser or debugger console. | |
| 239 // Before this exception was thrown, there should be `console.error` output | |
| 240 // that shows the type (Symbol, Temporal.PlainDate, etc.) that caused the | |
| 241 // problem and how that type was used: key, atrribute, input value prop, etc. | |
| 242 // In most cases, this console output also shows the component and its | |
| 243 // ancestor components where the exception happened. | |
| 244 // | |
| 245 // eslint-disable-next-line react-internal/safe-string-coercion | |
| 246 return '' + value; | |
| 247 } | |
| 248 | |
| 249 function checkAttributeStringCoercion(value, attributeName) { | |
| 250 { | |
| 251 if (willCoercionThrow(value)) { | |
| 252 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)); | |
| 253 | |
| 254 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 255 } | |
| 256 } | |
| 257 } | |
| 258 function checkKeyStringCoercion(value) { | |
| 259 { | |
| 260 if (willCoercionThrow(value)) { | |
| 261 error('The provided key is an unsupported type %s.' + ' This value must be coerced to a string before before using it here.', typeName(value)); | |
| 262 | |
| 263 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 264 } | |
| 265 } | |
| 266 } | |
| 267 function checkPropStringCoercion(value, propName) { | |
| 268 { | |
| 269 if (willCoercionThrow(value)) { | |
| 270 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)); | |
| 271 | |
| 272 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 273 } | |
| 274 } | |
| 275 } | |
| 276 function checkCSSPropertyStringCoercion(value, propName) { | |
| 277 { | |
| 278 if (willCoercionThrow(value)) { | |
| 279 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)); | |
| 280 | |
| 281 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 282 } | |
| 283 } | |
| 284 } | |
| 285 function checkHtmlStringCoercion(value) { | |
| 286 { | |
| 287 if (willCoercionThrow(value)) { | |
| 288 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)); | |
| 289 | |
| 290 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 291 } | |
| 292 } | |
| 293 } | |
| 294 function checkFormFieldValueStringCoercion(value) { | |
| 295 { | |
| 296 if (willCoercionThrow(value)) { | |
| 297 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)); | |
| 298 | |
| 299 return testStringCoercion(value); // throw (to help callers find troubleshooting comments) | |
| 300 } | |
| 301 } | |
| 302 } | |
| 303 | |
| 304 // A reserved attribute. | |
| 305 // It is handled by React separately and shouldn't be written to the DOM. | |
| 306 var RESERVED = 0; // A simple string attribute. | |
| 307 // Attributes that aren't in the filter are presumed to have this type. | |
| 308 | |
| 309 var STRING = 1; // A string attribute that accepts booleans in React. In HTML, these are called | |
| 310 // "enumerated" attributes with "true" and "false" as possible values. | |
| 311 // When true, it should be set to a "true" string. | |
| 312 // When false, it should be set to a "false" string. | |
| 313 | |
| 314 var BOOLEANISH_STRING = 2; // A real boolean attribute. | |
| 315 // When true, it should be present (set either to an empty string or its name). | |
| 316 // When false, it should be omitted. | |
| 317 | |
| 318 var BOOLEAN = 3; // An attribute that can be used as a flag as well as with a value. | |
| 319 // When true, it should be present (set either to an empty string or its name). | |
| 320 // When false, it should be omitted. | |
| 321 // For any other value, should be present with that value. | |
| 322 | |
| 323 var OVERLOADED_BOOLEAN = 4; // An attribute that must be numeric or parse as a numeric. | |
| 324 // When falsy, it should be removed. | |
| 325 | |
| 326 var NUMERIC = 5; // An attribute that must be positive numeric or parse as a positive numeric. | |
| 327 // When falsy, it should be removed. | |
| 328 | |
| 329 var POSITIVE_NUMERIC = 6; | |
| 330 | |
| 331 /* eslint-disable max-len */ | |
| 332 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"; | |
| 333 /* eslint-enable max-len */ | |
| 334 | |
| 335 var ATTRIBUTE_NAME_CHAR = ATTRIBUTE_NAME_START_CHAR + "\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040"; | |
| 336 var VALID_ATTRIBUTE_NAME_REGEX = new RegExp('^[' + ATTRIBUTE_NAME_START_CHAR + '][' + ATTRIBUTE_NAME_CHAR + ']*$'); | |
| 337 var illegalAttributeNameCache = {}; | |
| 338 var validatedAttributeNameCache = {}; | |
| 339 function isAttributeNameSafe(attributeName) { | |
| 340 if (hasOwnProperty.call(validatedAttributeNameCache, attributeName)) { | |
| 341 return true; | |
| 342 } | |
| 343 | |
| 344 if (hasOwnProperty.call(illegalAttributeNameCache, attributeName)) { | |
| 345 return false; | |
| 346 } | |
| 347 | |
| 348 if (VALID_ATTRIBUTE_NAME_REGEX.test(attributeName)) { | |
| 349 validatedAttributeNameCache[attributeName] = true; | |
| 350 return true; | |
| 351 } | |
| 352 | |
| 353 illegalAttributeNameCache[attributeName] = true; | |
| 354 | |
| 355 { | |
| 356 error('Invalid attribute name: `%s`', attributeName); | |
| 357 } | |
| 358 | |
| 359 return false; | |
| 360 } | |
| 361 function shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag) { | |
| 362 if (propertyInfo !== null) { | |
| 363 return propertyInfo.type === RESERVED; | |
| 364 } | |
| 365 | |
| 366 if (isCustomComponentTag) { | |
| 367 return false; | |
| 368 } | |
| 369 | |
| 370 if (name.length > 2 && (name[0] === 'o' || name[0] === 'O') && (name[1] === 'n' || name[1] === 'N')) { | |
| 371 return true; | |
| 372 } | |
| 373 | |
| 374 return false; | |
| 375 } | |
| 376 function shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag) { | |
| 377 if (propertyInfo !== null && propertyInfo.type === RESERVED) { | |
| 378 return false; | |
| 379 } | |
| 380 | |
| 381 switch (typeof value) { | |
| 382 case 'function': // $FlowIssue symbol is perfectly valid here | |
| 383 | |
| 384 case 'symbol': | |
| 385 // eslint-disable-line | |
| 386 return true; | |
| 387 | |
| 388 case 'boolean': | |
| 389 { | |
| 390 if (isCustomComponentTag) { | |
| 391 return false; | |
| 392 } | |
| 393 | |
| 394 if (propertyInfo !== null) { | |
| 395 return !propertyInfo.acceptsBooleans; | |
| 396 } else { | |
| 397 var prefix = name.toLowerCase().slice(0, 5); | |
| 398 return prefix !== 'data-' && prefix !== 'aria-'; | |
| 399 } | |
| 400 } | |
| 401 | |
| 402 default: | |
| 403 return false; | |
| 404 } | |
| 405 } | |
| 406 function shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag) { | |
| 407 if (value === null || typeof value === 'undefined') { | |
| 408 return true; | |
| 409 } | |
| 410 | |
| 411 if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, isCustomComponentTag)) { | |
| 412 return true; | |
| 413 } | |
| 414 | |
| 415 if (isCustomComponentTag) { | |
| 416 | |
| 417 return false; | |
| 418 } | |
| 419 | |
| 420 if (propertyInfo !== null) { | |
| 421 | |
| 422 switch (propertyInfo.type) { | |
| 423 case BOOLEAN: | |
| 424 return !value; | |
| 425 | |
| 426 case OVERLOADED_BOOLEAN: | |
| 427 return value === false; | |
| 428 | |
| 429 case NUMERIC: | |
| 430 return isNaN(value); | |
| 431 | |
| 432 case POSITIVE_NUMERIC: | |
| 433 return isNaN(value) || value < 1; | |
| 434 } | |
| 435 } | |
| 436 | |
| 437 return false; | |
| 438 } | |
| 439 function getPropertyInfo(name) { | |
| 440 return properties.hasOwnProperty(name) ? properties[name] : null; | |
| 441 } | |
| 442 | |
| 443 function PropertyInfoRecord(name, type, mustUseProperty, attributeName, attributeNamespace, sanitizeURL, removeEmptyString) { | |
| 444 this.acceptsBooleans = type === BOOLEANISH_STRING || type === BOOLEAN || type === OVERLOADED_BOOLEAN; | |
| 445 this.attributeName = attributeName; | |
| 446 this.attributeNamespace = attributeNamespace; | |
| 447 this.mustUseProperty = mustUseProperty; | |
| 448 this.propertyName = name; | |
| 449 this.type = type; | |
| 450 this.sanitizeURL = sanitizeURL; | |
| 451 this.removeEmptyString = removeEmptyString; | |
| 452 } // When adding attributes to this list, be sure to also add them to | |
| 453 // the `possibleStandardNames` module to ensure casing and incorrect | |
| 454 // name warnings. | |
| 455 | |
| 456 | |
| 457 var properties = {}; // These props are reserved by React. They shouldn't be written to the DOM. | |
| 458 | |
| 459 var reservedProps = ['children', 'dangerouslySetInnerHTML', // TODO: This prevents the assignment of defaultValue to regular | |
| 460 // elements (not just inputs). Now that ReactDOMInput assigns to the | |
| 461 // defaultValue property -- do we need this? | |
| 462 'defaultValue', 'defaultChecked', 'innerHTML', 'suppressContentEditableWarning', 'suppressHydrationWarning', 'style']; | |
| 463 | |
| 464 reservedProps.forEach(function (name) { | |
| 465 properties[name] = new PropertyInfoRecord(name, RESERVED, false, // mustUseProperty | |
| 466 name, // attributeName | |
| 467 null, // attributeNamespace | |
| 468 false, // sanitizeURL | |
| 469 false); | |
| 470 }); // A few React string attributes have a different name. | |
| 471 // This is a mapping from React prop names to the attribute names. | |
| 472 | |
| 473 [['acceptCharset', 'accept-charset'], ['className', 'class'], ['htmlFor', 'for'], ['httpEquiv', 'http-equiv']].forEach(function (_ref) { | |
| 474 var name = _ref[0], | |
| 475 attributeName = _ref[1]; | |
| 476 properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty | |
| 477 attributeName, // attributeName | |
| 478 null, // attributeNamespace | |
| 479 false, // sanitizeURL | |
| 480 false); | |
| 481 }); // These are "enumerated" HTML attributes that accept "true" and "false". | |
| 482 // In React, we let users pass `true` and `false` even though technically | |
| 483 // these aren't boolean attributes (they are coerced to strings). | |
| 484 | |
| 485 ['contentEditable', 'draggable', 'spellCheck', 'value'].forEach(function (name) { | |
| 486 properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty | |
| 487 name.toLowerCase(), // attributeName | |
| 488 null, // attributeNamespace | |
| 489 false, // sanitizeURL | |
| 490 false); | |
| 491 }); // These are "enumerated" SVG attributes that accept "true" and "false". | |
| 492 // In React, we let users pass `true` and `false` even though technically | |
| 493 // these aren't boolean attributes (they are coerced to strings). | |
| 494 // Since these are SVG attributes, their attribute names are case-sensitive. | |
| 495 | |
| 496 ['autoReverse', 'externalResourcesRequired', 'focusable', 'preserveAlpha'].forEach(function (name) { | |
| 497 properties[name] = new PropertyInfoRecord(name, BOOLEANISH_STRING, false, // mustUseProperty | |
| 498 name, // attributeName | |
| 499 null, // attributeNamespace | |
| 500 false, // sanitizeURL | |
| 501 false); | |
| 502 }); // These are HTML boolean attributes. | |
| 503 | |
| 504 ['allowFullScreen', 'async', // Note: there is a special case that prevents it from being written to the DOM | |
| 505 // on the client side because the browsers are inconsistent. Instead we call focus(). | |
| 506 'autoFocus', 'autoPlay', 'controls', 'default', 'defer', 'disabled', 'disablePictureInPicture', 'disableRemotePlayback', 'formNoValidate', 'hidden', 'loop', 'noModule', 'noValidate', 'open', 'playsInline', 'readOnly', 'required', 'reversed', 'scoped', 'seamless', // Microdata | |
| 507 'itemScope'].forEach(function (name) { | |
| 508 properties[name] = new PropertyInfoRecord(name, BOOLEAN, false, // mustUseProperty | |
| 509 name.toLowerCase(), // attributeName | |
| 510 null, // attributeNamespace | |
| 511 false, // sanitizeURL | |
| 512 false); | |
| 513 }); // These are the few React props that we set as DOM properties | |
| 514 // rather than attributes. These are all booleans. | |
| 515 | |
| 516 ['checked', // Note: `option.selected` is not updated if `select.multiple` is | |
| 517 // disabled with `removeAttribute`. We have special logic for handling this. | |
| 518 'multiple', 'muted', 'selected' // NOTE: if you add a camelCased prop to this list, | |
| 519 // you'll need to set attributeName to name.toLowerCase() | |
| 520 // instead in the assignment below. | |
| 521 ].forEach(function (name) { | |
| 522 properties[name] = new PropertyInfoRecord(name, BOOLEAN, true, // mustUseProperty | |
| 523 name, // attributeName | |
| 524 null, // attributeNamespace | |
| 525 false, // sanitizeURL | |
| 526 false); | |
| 527 }); // These are HTML attributes that are "overloaded booleans": they behave like | |
| 528 // booleans, but can also accept a string value. | |
| 529 | |
| 530 ['capture', 'download' // NOTE: if you add a camelCased prop to this list, | |
| 531 // you'll need to set attributeName to name.toLowerCase() | |
| 532 // instead in the assignment below. | |
| 533 ].forEach(function (name) { | |
| 534 properties[name] = new PropertyInfoRecord(name, OVERLOADED_BOOLEAN, false, // mustUseProperty | |
| 535 name, // attributeName | |
| 536 null, // attributeNamespace | |
| 537 false, // sanitizeURL | |
| 538 false); | |
| 539 }); // These are HTML attributes that must be positive numbers. | |
| 540 | |
| 541 ['cols', 'rows', 'size', 'span' // NOTE: if you add a camelCased prop to this list, | |
| 542 // you'll need to set attributeName to name.toLowerCase() | |
| 543 // instead in the assignment below. | |
| 544 ].forEach(function (name) { | |
| 545 properties[name] = new PropertyInfoRecord(name, POSITIVE_NUMERIC, false, // mustUseProperty | |
| 546 name, // attributeName | |
| 547 null, // attributeNamespace | |
| 548 false, // sanitizeURL | |
| 549 false); | |
| 550 }); // These are HTML attributes that must be numbers. | |
| 551 | |
| 552 ['rowSpan', 'start'].forEach(function (name) { | |
| 553 properties[name] = new PropertyInfoRecord(name, NUMERIC, false, // mustUseProperty | |
| 554 name.toLowerCase(), // attributeName | |
| 555 null, // attributeNamespace | |
| 556 false, // sanitizeURL | |
| 557 false); | |
| 558 }); | |
| 559 var CAMELIZE = /[\-\:]([a-z])/g; | |
| 560 | |
| 561 var capitalize = function (token) { | |
| 562 return token[1].toUpperCase(); | |
| 563 }; // This is a list of all SVG attributes that need special casing, namespacing, | |
| 564 // or boolean value assignment. Regular attributes that just accept strings | |
| 565 // and have the same names are omitted, just like in the HTML attribute filter. | |
| 566 // Some of these attributes can be hard to find. This list was created by | |
| 567 // scraping the MDN documentation. | |
| 568 | |
| 569 | |
| 570 ['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, | |
| 571 // you'll need to set attributeName to name.toLowerCase() | |
| 572 // instead in the assignment below. | |
| 573 ].forEach(function (attributeName) { | |
| 574 var name = attributeName.replace(CAMELIZE, capitalize); | |
| 575 properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty | |
| 576 attributeName, null, // attributeNamespace | |
| 577 false, // sanitizeURL | |
| 578 false); | |
| 579 }); // String SVG attributes with the xlink namespace. | |
| 580 | |
| 581 ['xlink:actuate', 'xlink:arcrole', 'xlink:role', 'xlink:show', 'xlink:title', 'xlink:type' // NOTE: if you add a camelCased prop to this list, | |
| 582 // you'll need to set attributeName to name.toLowerCase() | |
| 583 // instead in the assignment below. | |
| 584 ].forEach(function (attributeName) { | |
| 585 var name = attributeName.replace(CAMELIZE, capitalize); | |
| 586 properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty | |
| 587 attributeName, 'http://www.w3.org/1999/xlink', false, // sanitizeURL | |
| 588 false); | |
| 589 }); // String SVG attributes with the xml namespace. | |
| 590 | |
| 591 ['xml:base', 'xml:lang', 'xml:space' // NOTE: if you add a camelCased prop to this list, | |
| 592 // you'll need to set attributeName to name.toLowerCase() | |
| 593 // instead in the assignment below. | |
| 594 ].forEach(function (attributeName) { | |
| 595 var name = attributeName.replace(CAMELIZE, capitalize); | |
| 596 properties[name] = new PropertyInfoRecord(name, STRING, false, // mustUseProperty | |
| 597 attributeName, 'http://www.w3.org/XML/1998/namespace', false, // sanitizeURL | |
| 598 false); | |
| 599 }); // These attribute exists both in HTML and SVG. | |
| 600 // The attribute name is case-sensitive in SVG so we can't just use | |
| 601 // the React name like we do for attributes that exist only in HTML. | |
| 602 | |
| 603 ['tabIndex', 'crossOrigin'].forEach(function (attributeName) { | |
| 604 properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty | |
| 605 attributeName.toLowerCase(), // attributeName | |
| 606 null, // attributeNamespace | |
| 607 false, // sanitizeURL | |
| 608 false); | |
| 609 }); // These attributes accept URLs. These must not allow javascript: URLS. | |
| 610 // These will also need to accept Trusted Types object in the future. | |
| 611 | |
| 612 var xlinkHref = 'xlinkHref'; | |
| 613 properties[xlinkHref] = new PropertyInfoRecord('xlinkHref', STRING, false, // mustUseProperty | |
| 614 'xlink:href', 'http://www.w3.org/1999/xlink', true, // sanitizeURL | |
| 615 false); | |
| 616 ['src', 'href', 'action', 'formAction'].forEach(function (attributeName) { | |
| 617 properties[attributeName] = new PropertyInfoRecord(attributeName, STRING, false, // mustUseProperty | |
| 618 attributeName.toLowerCase(), // attributeName | |
| 619 null, // attributeNamespace | |
| 620 true, // sanitizeURL | |
| 621 true); | |
| 622 }); | |
| 623 | |
| 624 // and any newline or tab are filtered out as if they're not part of the URL. | |
| 625 // https://url.spec.whatwg.org/#url-parsing | |
| 626 // Tab or newline are defined as \r\n\t: | |
| 627 // https://infra.spec.whatwg.org/#ascii-tab-or-newline | |
| 628 // A C0 control is a code point in the range \u0000 NULL to \u001F | |
| 629 // INFORMATION SEPARATOR ONE, inclusive: | |
| 630 // https://infra.spec.whatwg.org/#c0-control-or-space | |
| 631 | |
| 632 /* eslint-disable max-len */ | |
| 633 | |
| 634 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; | |
| 635 var didWarn = false; | |
| 636 | |
| 637 function sanitizeURL(url) { | |
| 638 { | |
| 639 if (!didWarn && isJavaScriptProtocol.test(url)) { | |
| 640 didWarn = true; | |
| 641 | |
| 642 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)); | |
| 643 } | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 /** | |
| 648 * Get the value for a property on a node. Only used in DEV for SSR validation. | |
| 649 * The "expected" argument is used as a hint of what the expected value is. | |
| 650 * Some properties have multiple equivalent values. | |
| 651 */ | |
| 652 function getValueForProperty(node, name, expected, propertyInfo) { | |
| 653 { | |
| 654 if (propertyInfo.mustUseProperty) { | |
| 655 var propertyName = propertyInfo.propertyName; | |
| 656 return node[propertyName]; | |
| 657 } else { | |
| 658 // This check protects multiple uses of `expected`, which is why the | |
| 659 // react-internal/safe-string-coercion rule is disabled in several spots | |
| 660 // below. | |
| 661 { | |
| 662 checkAttributeStringCoercion(expected, name); | |
| 663 } | |
| 664 | |
| 665 if ( propertyInfo.sanitizeURL) { | |
| 666 // If we haven't fully disabled javascript: URLs, and if | |
| 667 // the hydration is successful of a javascript: URL, we | |
| 668 // still want to warn on the client. | |
| 669 // eslint-disable-next-line react-internal/safe-string-coercion | |
| 670 sanitizeURL('' + expected); | |
| 671 } | |
| 672 | |
| 673 var attributeName = propertyInfo.attributeName; | |
| 674 var stringValue = null; | |
| 675 | |
| 676 if (propertyInfo.type === OVERLOADED_BOOLEAN) { | |
| 677 if (node.hasAttribute(attributeName)) { | |
| 678 var value = node.getAttribute(attributeName); | |
| 679 | |
| 680 if (value === '') { | |
| 681 return true; | |
| 682 } | |
| 683 | |
| 684 if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { | |
| 685 return value; | |
| 686 } // eslint-disable-next-line react-internal/safe-string-coercion | |
| 687 | |
| 688 | |
| 689 if (value === '' + expected) { | |
| 690 return expected; | |
| 691 } | |
| 692 | |
| 693 return value; | |
| 694 } | |
| 695 } else if (node.hasAttribute(attributeName)) { | |
| 696 if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { | |
| 697 // We had an attribute but shouldn't have had one, so read it | |
| 698 // for the error message. | |
| 699 return node.getAttribute(attributeName); | |
| 700 } | |
| 701 | |
| 702 if (propertyInfo.type === BOOLEAN) { | |
| 703 // If this was a boolean, it doesn't matter what the value is | |
| 704 // the fact that we have it is the same as the expected. | |
| 705 return expected; | |
| 706 } // Even if this property uses a namespace we use getAttribute | |
| 707 // because we assume its namespaced name is the same as our config. | |
| 708 // To use getAttributeNS we need the local name which we don't have | |
| 709 // in our config atm. | |
| 710 | |
| 711 | |
| 712 stringValue = node.getAttribute(attributeName); | |
| 713 } | |
| 714 | |
| 715 if (shouldRemoveAttribute(name, expected, propertyInfo, false)) { | |
| 716 return stringValue === null ? expected : stringValue; // eslint-disable-next-line react-internal/safe-string-coercion | |
| 717 } else if (stringValue === '' + expected) { | |
| 718 return expected; | |
| 719 } else { | |
| 720 return stringValue; | |
| 721 } | |
| 722 } | |
| 723 } | |
| 724 } | |
| 725 /** | |
| 726 * Get the value for a attribute on a node. Only used in DEV for SSR validation. | |
| 727 * The third argument is used as a hint of what the expected value is. Some | |
| 728 * attributes have multiple equivalent values. | |
| 729 */ | |
| 730 | |
| 731 function getValueForAttribute(node, name, expected, isCustomComponentTag) { | |
| 732 { | |
| 733 if (!isAttributeNameSafe(name)) { | |
| 734 return; | |
| 735 } | |
| 736 | |
| 737 if (!node.hasAttribute(name)) { | |
| 738 return expected === undefined ? undefined : null; | |
| 739 } | |
| 740 | |
| 741 var value = node.getAttribute(name); | |
| 742 | |
| 743 { | |
| 744 checkAttributeStringCoercion(expected, name); | |
| 745 } | |
| 746 | |
| 747 if (value === '' + expected) { | |
| 748 return expected; | |
| 749 } | |
| 750 | |
| 751 return value; | |
| 752 } | |
| 753 } | |
| 754 /** | |
| 755 * Sets the value for a property on a node. | |
| 756 * | |
| 757 * @param {DOMElement} node | |
| 758 * @param {string} name | |
| 759 * @param {*} value | |
| 760 */ | |
| 761 | |
| 762 function setValueForProperty(node, name, value, isCustomComponentTag) { | |
| 763 var propertyInfo = getPropertyInfo(name); | |
| 764 | |
| 765 if (shouldIgnoreAttribute(name, propertyInfo, isCustomComponentTag)) { | |
| 766 return; | |
| 767 } | |
| 768 | |
| 769 if (shouldRemoveAttribute(name, value, propertyInfo, isCustomComponentTag)) { | |
| 770 value = null; | |
| 771 } | |
| 772 | |
| 773 | |
| 774 if (isCustomComponentTag || propertyInfo === null) { | |
| 775 if (isAttributeNameSafe(name)) { | |
| 776 var _attributeName = name; | |
| 777 | |
| 778 if (value === null) { | |
| 779 node.removeAttribute(_attributeName); | |
| 780 } else { | |
| 781 { | |
| 782 checkAttributeStringCoercion(value, name); | |
| 783 } | |
| 784 | |
| 785 node.setAttribute(_attributeName, '' + value); | |
| 786 } | |
| 787 } | |
| 788 | |
| 789 return; | |
| 790 } | |
| 791 | |
| 792 var mustUseProperty = propertyInfo.mustUseProperty; | |
| 793 | |
| 794 if (mustUseProperty) { | |
| 795 var propertyName = propertyInfo.propertyName; | |
| 796 | |
| 797 if (value === null) { | |
| 798 var type = propertyInfo.type; | |
| 799 node[propertyName] = type === BOOLEAN ? false : ''; | |
| 800 } else { | |
| 801 // Contrary to `setAttribute`, object properties are properly | |
| 802 // `toString`ed by IE8/9. | |
| 803 node[propertyName] = value; | |
| 804 } | |
| 805 | |
| 806 return; | |
| 807 } // The rest are treated as attributes with special cases. | |
| 808 | |
| 809 | |
| 810 var attributeName = propertyInfo.attributeName, | |
| 811 attributeNamespace = propertyInfo.attributeNamespace; | |
| 812 | |
| 813 if (value === null) { | |
| 814 node.removeAttribute(attributeName); | |
| 815 } else { | |
| 816 var _type = propertyInfo.type; | |
| 817 var attributeValue; | |
| 818 | |
| 819 if (_type === BOOLEAN || _type === OVERLOADED_BOOLEAN && value === true) { | |
| 820 // If attribute type is boolean, we know for sure it won't be an execution sink | |
| 821 // and we won't require Trusted Type here. | |
| 822 attributeValue = ''; | |
| 823 } else { | |
| 824 // `setAttribute` with objects becomes only `[object]` in IE8/9, | |
| 825 // ('' + value) makes it output the correct toString()-value. | |
| 826 { | |
| 827 { | |
| 828 checkAttributeStringCoercion(value, attributeName); | |
| 829 } | |
| 830 | |
| 831 attributeValue = '' + value; | |
| 832 } | |
| 833 | |
| 834 if (propertyInfo.sanitizeURL) { | |
| 835 sanitizeURL(attributeValue.toString()); | |
| 836 } | |
| 837 } | |
| 838 | |
| 839 if (attributeNamespace) { | |
| 840 node.setAttributeNS(attributeNamespace, attributeName, attributeValue); | |
| 841 } else { | |
| 842 node.setAttribute(attributeName, attributeValue); | |
| 843 } | |
| 844 } | |
| 845 } | |
| 846 | |
| 847 // ATTENTION | |
| 848 // When adding new symbols to this file, | |
| 849 // Please consider also adding to 'react-devtools-shared/src/backend/ReactSymbols' | |
| 850 // The Symbol used to tag the ReactElement-like types. | |
| 851 var REACT_ELEMENT_TYPE = Symbol.for('react.element'); | |
| 852 var REACT_PORTAL_TYPE = Symbol.for('react.portal'); | |
| 853 var REACT_FRAGMENT_TYPE = Symbol.for('react.fragment'); | |
| 854 var REACT_STRICT_MODE_TYPE = Symbol.for('react.strict_mode'); | |
| 855 var REACT_PROFILER_TYPE = Symbol.for('react.profiler'); | |
| 856 var REACT_PROVIDER_TYPE = Symbol.for('react.provider'); | |
| 857 var REACT_CONTEXT_TYPE = Symbol.for('react.context'); | |
| 858 var REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref'); | |
| 859 var REACT_SUSPENSE_TYPE = Symbol.for('react.suspense'); | |
| 860 var REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list'); | |
| 861 var REACT_MEMO_TYPE = Symbol.for('react.memo'); | |
| 862 var REACT_LAZY_TYPE = Symbol.for('react.lazy'); | |
| 863 var REACT_SCOPE_TYPE = Symbol.for('react.scope'); | |
| 864 var REACT_DEBUG_TRACING_MODE_TYPE = Symbol.for('react.debug_trace_mode'); | |
| 865 var REACT_OFFSCREEN_TYPE = Symbol.for('react.offscreen'); | |
| 866 var REACT_LEGACY_HIDDEN_TYPE = Symbol.for('react.legacy_hidden'); | |
| 867 var REACT_CACHE_TYPE = Symbol.for('react.cache'); | |
| 868 var REACT_TRACING_MARKER_TYPE = Symbol.for('react.tracing_marker'); | |
| 869 var MAYBE_ITERATOR_SYMBOL = Symbol.iterator; | |
| 870 var FAUX_ITERATOR_SYMBOL = '@@iterator'; | |
| 871 function getIteratorFn(maybeIterable) { | |
| 872 if (maybeIterable === null || typeof maybeIterable !== 'object') { | |
| 873 return null; | |
| 874 } | |
| 875 | |
| 876 var maybeIterator = MAYBE_ITERATOR_SYMBOL && maybeIterable[MAYBE_ITERATOR_SYMBOL] || maybeIterable[FAUX_ITERATOR_SYMBOL]; | |
| 877 | |
| 878 if (typeof maybeIterator === 'function') { | |
| 879 return maybeIterator; | |
| 880 } | |
| 881 | |
| 882 return null; | |
| 883 } | |
| 884 | |
| 885 var assign = Object.assign; | |
| 886 | |
| 887 // Helpers to patch console.logs to avoid logging during side-effect free | |
| 888 // replaying on render function. This currently only patches the object | |
| 889 // lazily which won't cover if the log function was extracted eagerly. | |
| 890 // We could also eagerly patch the method. | |
| 891 var disabledDepth = 0; | |
| 892 var prevLog; | |
| 893 var prevInfo; | |
| 894 var prevWarn; | |
| 895 var prevError; | |
| 896 var prevGroup; | |
| 897 var prevGroupCollapsed; | |
| 898 var prevGroupEnd; | |
| 899 | |
| 900 function disabledLog() {} | |
| 901 | |
| 902 disabledLog.__reactDisabledLog = true; | |
| 903 function disableLogs() { | |
| 904 { | |
| 905 if (disabledDepth === 0) { | |
| 906 /* eslint-disable react-internal/no-production-logging */ | |
| 907 prevLog = console.log; | |
| 908 prevInfo = console.info; | |
| 909 prevWarn = console.warn; | |
| 910 prevError = console.error; | |
| 911 prevGroup = console.group; | |
| 912 prevGroupCollapsed = console.groupCollapsed; | |
| 913 prevGroupEnd = console.groupEnd; // https://github.com/facebook/react/issues/19099 | |
| 914 | |
| 915 var props = { | |
| 916 configurable: true, | |
| 917 enumerable: true, | |
| 918 value: disabledLog, | |
| 919 writable: true | |
| 920 }; // $FlowFixMe Flow thinks console is immutable. | |
| 921 | |
| 922 Object.defineProperties(console, { | |
| 923 info: props, | |
| 924 log: props, | |
| 925 warn: props, | |
| 926 error: props, | |
| 927 group: props, | |
| 928 groupCollapsed: props, | |
| 929 groupEnd: props | |
| 930 }); | |
| 931 /* eslint-enable react-internal/no-production-logging */ | |
| 932 } | |
| 933 | |
| 934 disabledDepth++; | |
| 935 } | |
| 936 } | |
| 937 function reenableLogs() { | |
| 938 { | |
| 939 disabledDepth--; | |
| 940 | |
| 941 if (disabledDepth === 0) { | |
| 942 /* eslint-disable react-internal/no-production-logging */ | |
| 943 var props = { | |
| 944 configurable: true, | |
| 945 enumerable: true, | |
| 946 writable: true | |
| 947 }; // $FlowFixMe Flow thinks console is immutable. | |
| 948 | |
| 949 Object.defineProperties(console, { | |
| 950 log: assign({}, props, { | |
| 951 value: prevLog | |
| 952 }), | |
| 953 info: assign({}, props, { | |
| 954 value: prevInfo | |
| 955 }), | |
| 956 warn: assign({}, props, { | |
| 957 value: prevWarn | |
| 958 }), | |
| 959 error: assign({}, props, { | |
| 960 value: prevError | |
| 961 }), | |
| 962 group: assign({}, props, { | |
| 963 value: prevGroup | |
| 964 }), | |
| 965 groupCollapsed: assign({}, props, { | |
| 966 value: prevGroupCollapsed | |
| 967 }), | |
| 968 groupEnd: assign({}, props, { | |
| 969 value: prevGroupEnd | |
| 970 }) | |
| 971 }); | |
| 972 /* eslint-enable react-internal/no-production-logging */ | |
| 973 } | |
| 974 | |
| 975 if (disabledDepth < 0) { | |
| 976 error('disabledDepth fell below zero. ' + 'This is a bug in React. Please file an issue.'); | |
| 977 } | |
| 978 } | |
| 979 } | |
| 980 | |
| 981 var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; | |
| 982 var prefix; | |
| 983 function describeBuiltInComponentFrame(name, source, ownerFn) { | |
| 984 { | |
| 985 if (prefix === undefined) { | |
| 986 // Extract the VM specific prefix used by each line. | |
| 987 try { | |
| 988 throw Error(); | |
| 989 } catch (x) { | |
| 990 var match = x.stack.trim().match(/\n( *(at )?)/); | |
| 991 prefix = match && match[1] || ''; | |
| 992 } | |
| 993 } // We use the prefix to ensure our stacks line up with native stack frames. | |
| 994 | |
| 995 | |
| 996 return '\n' + prefix + name; | |
| 997 } | |
| 998 } | |
| 999 var reentry = false; | |
| 1000 var componentFrameCache; | |
| 1001 | |
| 1002 { | |
| 1003 var PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map; | |
| 1004 componentFrameCache = new PossiblyWeakMap(); | |
| 1005 } | |
| 1006 | |
| 1007 function describeNativeComponentFrame(fn, construct) { | |
| 1008 // If something asked for a stack inside a fake render, it should get ignored. | |
| 1009 if ( !fn || reentry) { | |
| 1010 return ''; | |
| 1011 } | |
| 1012 | |
| 1013 { | |
| 1014 var frame = componentFrameCache.get(fn); | |
| 1015 | |
| 1016 if (frame !== undefined) { | |
| 1017 return frame; | |
| 1018 } | |
| 1019 } | |
| 1020 | |
| 1021 var control; | |
| 1022 reentry = true; | |
| 1023 var previousPrepareStackTrace = Error.prepareStackTrace; // $FlowFixMe It does accept undefined. | |
| 1024 | |
| 1025 Error.prepareStackTrace = undefined; | |
| 1026 var previousDispatcher; | |
| 1027 | |
| 1028 { | |
| 1029 previousDispatcher = ReactCurrentDispatcher.current; // Set the dispatcher in DEV because this might be call in the render function | |
| 1030 // for warnings. | |
| 1031 | |
| 1032 ReactCurrentDispatcher.current = null; | |
| 1033 disableLogs(); | |
| 1034 } | |
| 1035 | |
| 1036 try { | |
| 1037 // This should throw. | |
| 1038 if (construct) { | |
| 1039 // Something should be setting the props in the constructor. | |
| 1040 var Fake = function () { | |
| 1041 throw Error(); | |
| 1042 }; // $FlowFixMe | |
| 1043 | |
| 1044 | |
| 1045 Object.defineProperty(Fake.prototype, 'props', { | |
| 1046 set: function () { | |
| 1047 // We use a throwing setter instead of frozen or non-writable props | |
| 1048 // because that won't throw in a non-strict mode function. | |
| 1049 throw Error(); | |
| 1050 } | |
| 1051 }); | |
| 1052 | |
| 1053 if (typeof Reflect === 'object' && Reflect.construct) { | |
| 1054 // We construct a different control for this case to include any extra | |
| 1055 // frames added by the construct call. | |
| 1056 try { | |
| 1057 Reflect.construct(Fake, []); | |
| 1058 } catch (x) { | |
| 1059 control = x; | |
| 1060 } | |
| 1061 | |
| 1062 Reflect.construct(fn, [], Fake); | |
| 1063 } else { | |
| 1064 try { | |
| 1065 Fake.call(); | |
| 1066 } catch (x) { | |
| 1067 control = x; | |
| 1068 } | |
| 1069 | |
| 1070 fn.call(Fake.prototype); | |
| 1071 } | |
| 1072 } else { | |
| 1073 try { | |
| 1074 throw Error(); | |
| 1075 } catch (x) { | |
| 1076 control = x; | |
| 1077 } | |
| 1078 | |
| 1079 fn(); | |
| 1080 } | |
| 1081 } catch (sample) { | |
| 1082 // This is inlined manually because closure doesn't do it for us. | |
| 1083 if (sample && control && typeof sample.stack === 'string') { | |
| 1084 // This extracts the first frame from the sample that isn't also in the control. | |
| 1085 // Skipping one frame that we assume is the frame that calls the two. | |
| 1086 var sampleLines = sample.stack.split('\n'); | |
| 1087 var controlLines = control.stack.split('\n'); | |
| 1088 var s = sampleLines.length - 1; | |
| 1089 var c = controlLines.length - 1; | |
| 1090 | |
| 1091 while (s >= 1 && c >= 0 && sampleLines[s] !== controlLines[c]) { | |
| 1092 // We expect at least one stack frame to be shared. | |
| 1093 // Typically this will be the root most one. However, stack frames may be | |
| 1094 // cut off due to maximum stack limits. In this case, one maybe cut off | |
| 1095 // earlier than the other. We assume that the sample is longer or the same | |
| 1096 // and there for cut off earlier. So we should find the root most frame in | |
| 1097 // the sample somewhere in the control. | |
| 1098 c--; | |
| 1099 } | |
| 1100 | |
| 1101 for (; s >= 1 && c >= 0; s--, c--) { | |
| 1102 // Next we find the first one that isn't the same which should be the | |
| 1103 // frame that called our sample function and the control. | |
| 1104 if (sampleLines[s] !== controlLines[c]) { | |
| 1105 // In V8, the first line is describing the message but other VMs don't. | |
| 1106 // If we're about to return the first line, and the control is also on the same | |
| 1107 // line, that's a pretty good indicator that our sample threw at same line as | |
| 1108 // the control. I.e. before we entered the sample frame. So we ignore this result. | |
| 1109 // This can happen if you passed a class to function component, or non-function. | |
| 1110 if (s !== 1 || c !== 1) { | |
| 1111 do { | |
| 1112 s--; | |
| 1113 c--; // We may still have similar intermediate frames from the construct call. | |
| 1114 // The next one that isn't the same should be our match though. | |
| 1115 | |
| 1116 if (c < 0 || sampleLines[s] !== controlLines[c]) { | |
| 1117 // V8 adds a "new" prefix for native classes. Let's remove it to make it prettier. | |
| 1118 var _frame = '\n' + sampleLines[s].replace(' at new ', ' at '); // If our component frame is labeled "<anonymous>" | |
| 1119 // but we have a user-provided "displayName" | |
| 1120 // splice it in to make the stack more readable. | |
| 1121 | |
| 1122 | |
| 1123 if (fn.displayName && _frame.includes('<anonymous>')) { | |
| 1124 _frame = _frame.replace('<anonymous>', fn.displayName); | |
| 1125 } | |
| 1126 | |
| 1127 { | |
| 1128 if (typeof fn === 'function') { | |
| 1129 componentFrameCache.set(fn, _frame); | |
| 1130 } | |
| 1131 } // Return the line we found. | |
| 1132 | |
| 1133 | |
| 1134 return _frame; | |
| 1135 } | |
| 1136 } while (s >= 1 && c >= 0); | |
| 1137 } | |
| 1138 | |
| 1139 break; | |
| 1140 } | |
| 1141 } | |
| 1142 } | |
| 1143 } finally { | |
| 1144 reentry = false; | |
| 1145 | |
| 1146 { | |
| 1147 ReactCurrentDispatcher.current = previousDispatcher; | |
| 1148 reenableLogs(); | |
| 1149 } | |
| 1150 | |
| 1151 Error.prepareStackTrace = previousPrepareStackTrace; | |
| 1152 } // Fallback to just using the name if we couldn't make it throw. | |
| 1153 | |
| 1154 | |
| 1155 var name = fn ? fn.displayName || fn.name : ''; | |
| 1156 var syntheticFrame = name ? describeBuiltInComponentFrame(name) : ''; | |
| 1157 | |
| 1158 { | |
| 1159 if (typeof fn === 'function') { | |
| 1160 componentFrameCache.set(fn, syntheticFrame); | |
| 1161 } | |
| 1162 } | |
| 1163 | |
| 1164 return syntheticFrame; | |
| 1165 } | |
| 1166 | |
| 1167 function describeClassComponentFrame(ctor, source, ownerFn) { | |
| 1168 { | |
| 1169 return describeNativeComponentFrame(ctor, true); | |
| 1170 } | |
| 1171 } | |
| 1172 function describeFunctionComponentFrame(fn, source, ownerFn) { | |
| 1173 { | |
| 1174 return describeNativeComponentFrame(fn, false); | |
| 1175 } | |
| 1176 } | |
| 1177 | |
| 1178 function shouldConstruct(Component) { | |
| 1179 var prototype = Component.prototype; | |
| 1180 return !!(prototype && prototype.isReactComponent); | |
| 1181 } | |
| 1182 | |
| 1183 function describeUnknownElementTypeFrameInDEV(type, source, ownerFn) { | |
| 1184 | |
| 1185 if (type == null) { | |
| 1186 return ''; | |
| 1187 } | |
| 1188 | |
| 1189 if (typeof type === 'function') { | |
| 1190 { | |
| 1191 return describeNativeComponentFrame(type, shouldConstruct(type)); | |
| 1192 } | |
| 1193 } | |
| 1194 | |
| 1195 if (typeof type === 'string') { | |
| 1196 return describeBuiltInComponentFrame(type); | |
| 1197 } | |
| 1198 | |
| 1199 switch (type) { | |
| 1200 case REACT_SUSPENSE_TYPE: | |
| 1201 return describeBuiltInComponentFrame('Suspense'); | |
| 1202 | |
| 1203 case REACT_SUSPENSE_LIST_TYPE: | |
| 1204 return describeBuiltInComponentFrame('SuspenseList'); | |
| 1205 } | |
| 1206 | |
| 1207 if (typeof type === 'object') { | |
| 1208 switch (type.$$typeof) { | |
| 1209 case REACT_FORWARD_REF_TYPE: | |
| 1210 return describeFunctionComponentFrame(type.render); | |
| 1211 | |
| 1212 case REACT_MEMO_TYPE: | |
| 1213 // Memo may contain any component type so we recursively resolve it. | |
| 1214 return describeUnknownElementTypeFrameInDEV(type.type, source, ownerFn); | |
| 1215 | |
| 1216 case REACT_LAZY_TYPE: | |
| 1217 { | |
| 1218 var lazyComponent = type; | |
| 1219 var payload = lazyComponent._payload; | |
| 1220 var init = lazyComponent._init; | |
| 1221 | |
| 1222 try { | |
| 1223 // Lazy may contain any component type so we recursively resolve it. | |
| 1224 return describeUnknownElementTypeFrameInDEV(init(payload), source, ownerFn); | |
| 1225 } catch (x) {} | |
| 1226 } | |
| 1227 } | |
| 1228 } | |
| 1229 | |
| 1230 return ''; | |
| 1231 } | |
| 1232 | |
| 1233 function describeFiber(fiber) { | |
| 1234 var owner = fiber._debugOwner ? fiber._debugOwner.type : null ; | |
| 1235 var source = fiber._debugSource ; | |
| 1236 | |
| 1237 switch (fiber.tag) { | |
| 1238 case HostComponent: | |
| 1239 return describeBuiltInComponentFrame(fiber.type); | |
| 1240 | |
| 1241 case LazyComponent: | |
| 1242 return describeBuiltInComponentFrame('Lazy'); | |
| 1243 | |
| 1244 case SuspenseComponent: | |
| 1245 return describeBuiltInComponentFrame('Suspense'); | |
| 1246 | |
| 1247 case SuspenseListComponent: | |
| 1248 return describeBuiltInComponentFrame('SuspenseList'); | |
| 1249 | |
| 1250 case FunctionComponent: | |
| 1251 case IndeterminateComponent: | |
| 1252 case SimpleMemoComponent: | |
| 1253 return describeFunctionComponentFrame(fiber.type); | |
| 1254 | |
| 1255 case ForwardRef: | |
| 1256 return describeFunctionComponentFrame(fiber.type.render); | |
| 1257 | |
| 1258 case ClassComponent: | |
| 1259 return describeClassComponentFrame(fiber.type); | |
| 1260 | |
| 1261 default: | |
| 1262 return ''; | |
| 1263 } | |
| 1264 } | |
| 1265 | |
| 1266 function getStackByFiberInDevAndProd(workInProgress) { | |
| 1267 try { | |
| 1268 var info = ''; | |
| 1269 var node = workInProgress; | |
| 1270 | |
| 1271 do { | |
| 1272 info += describeFiber(node); | |
| 1273 node = node.return; | |
| 1274 } while (node); | |
| 1275 | |
| 1276 return info; | |
| 1277 } catch (x) { | |
| 1278 return '\nError generating stack: ' + x.message + '\n' + x.stack; | |
| 1279 } | |
| 1280 } | |
| 1281 | |
| 1282 function getWrappedName(outerType, innerType, wrapperName) { | |
| 1283 var displayName = outerType.displayName; | |
| 1284 | |
| 1285 if (displayName) { | |
| 1286 return displayName; | |
| 1287 } | |
| 1288 | |
| 1289 var functionName = innerType.displayName || innerType.name || ''; | |
| 1290 return functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName; | |
| 1291 } // Keep in sync with react-reconciler/getComponentNameFromFiber | |
| 1292 | |
| 1293 | |
| 1294 function getContextName(type) { | |
| 1295 return type.displayName || 'Context'; | |
| 1296 } // Note that the reconciler package should generally prefer to use getComponentNameFromFiber() instead. | |
| 1297 | |
| 1298 | |
| 1299 function getComponentNameFromType(type) { | |
| 1300 if (type == null) { | |
| 1301 // Host root, text node or just invalid type. | |
| 1302 return null; | |
| 1303 } | |
| 1304 | |
| 1305 { | |
| 1306 if (typeof type.tag === 'number') { | |
| 1307 error('Received an unexpected object in getComponentNameFromType(). ' + 'This is likely a bug in React. Please file an issue.'); | |
| 1308 } | |
| 1309 } | |
| 1310 | |
| 1311 if (typeof type === 'function') { | |
| 1312 return type.displayName || type.name || null; | |
| 1313 } | |
| 1314 | |
| 1315 if (typeof type === 'string') { | |
| 1316 return type; | |
| 1317 } | |
| 1318 | |
| 1319 switch (type) { | |
| 1320 case REACT_FRAGMENT_TYPE: | |
| 1321 return 'Fragment'; | |
| 1322 | |
| 1323 case REACT_PORTAL_TYPE: | |
| 1324 return 'Portal'; | |
| 1325 | |
| 1326 case REACT_PROFILER_TYPE: | |
| 1327 return 'Profiler'; | |
| 1328 | |
| 1329 case REACT_STRICT_MODE_TYPE: | |
| 1330 return 'StrictMode'; | |
| 1331 | |
| 1332 case REACT_SUSPENSE_TYPE: | |
| 1333 return 'Suspense'; | |
| 1334 | |
| 1335 case REACT_SUSPENSE_LIST_TYPE: | |
| 1336 return 'SuspenseList'; | |
| 1337 | |
| 1338 } | |
| 1339 | |
| 1340 if (typeof type === 'object') { | |
| 1341 switch (type.$$typeof) { | |
| 1342 case REACT_CONTEXT_TYPE: | |
| 1343 var context = type; | |
| 1344 return getContextName(context) + '.Consumer'; | |
| 1345 | |
| 1346 case REACT_PROVIDER_TYPE: | |
| 1347 var provider = type; | |
| 1348 return getContextName(provider._context) + '.Provider'; | |
| 1349 | |
| 1350 case REACT_FORWARD_REF_TYPE: | |
| 1351 return getWrappedName(type, type.render, 'ForwardRef'); | |
| 1352 | |
| 1353 case REACT_MEMO_TYPE: | |
| 1354 var outerName = type.displayName || null; | |
| 1355 | |
| 1356 if (outerName !== null) { | |
| 1357 return outerName; | |
| 1358 } | |
| 1359 | |
| 1360 return getComponentNameFromType(type.type) || 'Memo'; | |
| 1361 | |
| 1362 case REACT_LAZY_TYPE: | |
| 1363 { | |
| 1364 var lazyComponent = type; | |
| 1365 var payload = lazyComponent._payload; | |
| 1366 var init = lazyComponent._init; | |
| 1367 | |
| 1368 try { | |
| 1369 return getComponentNameFromType(init(payload)); | |
| 1370 } catch (x) { | |
| 1371 return null; | |
| 1372 } | |
| 1373 } | |
| 1374 | |
| 1375 // eslint-disable-next-line no-fallthrough | |
| 1376 } | |
| 1377 } | |
| 1378 | |
| 1379 return null; | |
| 1380 } | |
| 1381 | |
| 1382 function getWrappedName$1(outerType, innerType, wrapperName) { | |
| 1383 var functionName = innerType.displayName || innerType.name || ''; | |
| 1384 return outerType.displayName || (functionName !== '' ? wrapperName + "(" + functionName + ")" : wrapperName); | |
| 1385 } // Keep in sync with shared/getComponentNameFromType | |
| 1386 | |
| 1387 | |
| 1388 function getContextName$1(type) { | |
| 1389 return type.displayName || 'Context'; | |
| 1390 } | |
| 1391 | |
| 1392 function getComponentNameFromFiber(fiber) { | |
| 1393 var tag = fiber.tag, | |
| 1394 type = fiber.type; | |
| 1395 | |
| 1396 switch (tag) { | |
| 1397 case CacheComponent: | |
| 1398 return 'Cache'; | |
| 1399 | |
| 1400 case ContextConsumer: | |
| 1401 var context = type; | |
| 1402 return getContextName$1(context) + '.Consumer'; | |
| 1403 | |
| 1404 case ContextProvider: | |
| 1405 var provider = type; | |
| 1406 return getContextName$1(provider._context) + '.Provider'; | |
| 1407 | |
| 1408 case DehydratedFragment: | |
| 1409 return 'DehydratedFragment'; | |
| 1410 | |
| 1411 case ForwardRef: | |
| 1412 return getWrappedName$1(type, type.render, 'ForwardRef'); | |
| 1413 | |
| 1414 case Fragment: | |
| 1415 return 'Fragment'; | |
| 1416 | |
| 1417 case HostComponent: | |
| 1418 // Host component type is the display name (e.g. "div", "View") | |
| 1419 return type; | |
| 1420 | |
| 1421 case HostPortal: | |
| 1422 return 'Portal'; | |
| 1423 | |
| 1424 case HostRoot: | |
| 1425 return 'Root'; | |
| 1426 | |
| 1427 case HostText: | |
| 1428 return 'Text'; | |
| 1429 | |
| 1430 case LazyComponent: | |
| 1431 // Name comes from the type in this case; we don't have a tag. | |
| 1432 return getComponentNameFromType(type); | |
| 1433 | |
| 1434 case Mode: | |
| 1435 if (type === REACT_STRICT_MODE_TYPE) { | |
| 1436 // Don't be less specific than shared/getComponentNameFromType | |
| 1437 return 'StrictMode'; | |
| 1438 } | |
| 1439 | |
| 1440 return 'Mode'; | |
| 1441 | |
| 1442 case OffscreenComponent: | |
| 1443 return 'Offscreen'; | |
| 1444 | |
| 1445 case Profiler: | |
| 1446 return 'Profiler'; | |
| 1447 | |
| 1448 case ScopeComponent: | |
| 1449 return 'Scope'; | |
| 1450 | |
| 1451 case SuspenseComponent: | |
| 1452 return 'Suspense'; | |
| 1453 | |
| 1454 case SuspenseListComponent: | |
| 1455 return 'SuspenseList'; | |
| 1456 | |
| 1457 case TracingMarkerComponent: | |
| 1458 return 'TracingMarker'; | |
| 1459 // The display name for this tags come from the user-provided type: | |
| 1460 | |
| 1461 case ClassComponent: | |
| 1462 case FunctionComponent: | |
| 1463 case IncompleteClassComponent: | |
| 1464 case IndeterminateComponent: | |
| 1465 case MemoComponent: | |
| 1466 case SimpleMemoComponent: | |
| 1467 if (typeof type === 'function') { | |
| 1468 return type.displayName || type.name || null; | |
| 1469 } | |
| 1470 | |
| 1471 if (typeof type === 'string') { | |
| 1472 return type; | |
| 1473 } | |
| 1474 | |
| 1475 break; | |
| 1476 | |
| 1477 } | |
| 1478 | |
| 1479 return null; | |
| 1480 } | |
| 1481 | |
| 1482 var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; | |
| 1483 var current = null; | |
| 1484 var isRendering = false; | |
| 1485 function getCurrentFiberOwnerNameInDevOrNull() { | |
| 1486 { | |
| 1487 if (current === null) { | |
| 1488 return null; | |
| 1489 } | |
| 1490 | |
| 1491 var owner = current._debugOwner; | |
| 1492 | |
| 1493 if (owner !== null && typeof owner !== 'undefined') { | |
| 1494 return getComponentNameFromFiber(owner); | |
| 1495 } | |
| 1496 } | |
| 1497 | |
| 1498 return null; | |
| 1499 } | |
| 1500 | |
| 1501 function getCurrentFiberStackInDev() { | |
| 1502 { | |
| 1503 if (current === null) { | |
| 1504 return ''; | |
| 1505 } // Safe because if current fiber exists, we are reconciling, | |
| 1506 // and it is guaranteed to be the work-in-progress version. | |
| 1507 | |
| 1508 | |
| 1509 return getStackByFiberInDevAndProd(current); | |
| 1510 } | |
| 1511 } | |
| 1512 | |
| 1513 function resetCurrentFiber() { | |
| 1514 { | |
| 1515 ReactDebugCurrentFrame.getCurrentStack = null; | |
| 1516 current = null; | |
| 1517 isRendering = false; | |
| 1518 } | |
| 1519 } | |
| 1520 function setCurrentFiber(fiber) { | |
| 1521 { | |
| 1522 ReactDebugCurrentFrame.getCurrentStack = fiber === null ? null : getCurrentFiberStackInDev; | |
| 1523 current = fiber; | |
| 1524 isRendering = false; | |
| 1525 } | |
| 1526 } | |
| 1527 function getCurrentFiber() { | |
| 1528 { | |
| 1529 return current; | |
| 1530 } | |
| 1531 } | |
| 1532 function setIsRendering(rendering) { | |
| 1533 { | |
| 1534 isRendering = rendering; | |
| 1535 } | |
| 1536 } | |
| 1537 | |
| 1538 // Flow does not allow string concatenation of most non-string types. To work | |
| 1539 // around this limitation, we use an opaque type that can only be obtained by | |
| 1540 // passing the value through getToStringValue first. | |
| 1541 function toString(value) { | |
| 1542 // The coercion safety check is performed in getToStringValue(). | |
| 1543 // eslint-disable-next-line react-internal/safe-string-coercion | |
| 1544 return '' + value; | |
| 1545 } | |
| 1546 function getToStringValue(value) { | |
| 1547 switch (typeof value) { | |
| 1548 case 'boolean': | |
| 1549 case 'number': | |
| 1550 case 'string': | |
| 1551 case 'undefined': | |
| 1552 return value; | |
| 1553 | |
| 1554 case 'object': | |
| 1555 { | |
| 1556 checkFormFieldValueStringCoercion(value); | |
| 1557 } | |
| 1558 | |
| 1559 return value; | |
| 1560 | |
| 1561 default: | |
| 1562 // function, symbol are assigned as empty strings | |
| 1563 return ''; | |
| 1564 } | |
| 1565 } | |
| 1566 | |
| 1567 var hasReadOnlyValue = { | |
| 1568 button: true, | |
| 1569 checkbox: true, | |
| 1570 image: true, | |
| 1571 hidden: true, | |
| 1572 radio: true, | |
| 1573 reset: true, | |
| 1574 submit: true | |
| 1575 }; | |
| 1576 function checkControlledValueProps(tagName, props) { | |
| 1577 { | |
| 1578 if (!(hasReadOnlyValue[props.type] || props.onChange || props.onInput || props.readOnly || props.disabled || props.value == null)) { | |
| 1579 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`.'); | |
| 1580 } | |
| 1581 | |
| 1582 if (!(props.onChange || props.readOnly || props.disabled || props.checked == null)) { | |
| 1583 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`.'); | |
| 1584 } | |
| 1585 } | |
| 1586 } | |
| 1587 | |
| 1588 function isCheckable(elem) { | |
| 1589 var type = elem.type; | |
| 1590 var nodeName = elem.nodeName; | |
| 1591 return nodeName && nodeName.toLowerCase() === 'input' && (type === 'checkbox' || type === 'radio'); | |
| 1592 } | |
| 1593 | |
| 1594 function getTracker(node) { | |
| 1595 return node._valueTracker; | |
| 1596 } | |
| 1597 | |
| 1598 function detachTracker(node) { | |
| 1599 node._valueTracker = null; | |
| 1600 } | |
| 1601 | |
| 1602 function getValueFromNode(node) { | |
| 1603 var value = ''; | |
| 1604 | |
| 1605 if (!node) { | |
| 1606 return value; | |
| 1607 } | |
| 1608 | |
| 1609 if (isCheckable(node)) { | |
| 1610 value = node.checked ? 'true' : 'false'; | |
| 1611 } else { | |
| 1612 value = node.value; | |
| 1613 } | |
| 1614 | |
| 1615 return value; | |
| 1616 } | |
| 1617 | |
| 1618 function trackValueOnNode(node) { | |
| 1619 var valueField = isCheckable(node) ? 'checked' : 'value'; | |
| 1620 var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField); | |
| 1621 | |
| 1622 { | |
| 1623 checkFormFieldValueStringCoercion(node[valueField]); | |
| 1624 } | |
| 1625 | |
| 1626 var currentValue = '' + node[valueField]; // if someone has already defined a value or Safari, then bail | |
| 1627 // and don't track value will cause over reporting of changes, | |
| 1628 // but it's better then a hard failure | |
| 1629 // (needed for certain tests that spyOn input values and Safari) | |
| 1630 | |
| 1631 if (node.hasOwnProperty(valueField) || typeof descriptor === 'undefined' || typeof descriptor.get !== 'function' || typeof descriptor.set !== 'function') { | |
| 1632 return; | |
| 1633 } | |
| 1634 | |
| 1635 var get = descriptor.get, | |
| 1636 set = descriptor.set; | |
| 1637 Object.defineProperty(node, valueField, { | |
| 1638 configurable: true, | |
| 1639 get: function () { | |
| 1640 return get.call(this); | |
| 1641 }, | |
| 1642 set: function (value) { | |
| 1643 { | |
| 1644 checkFormFieldValueStringCoercion(value); | |
| 1645 } | |
| 1646 | |
| 1647 currentValue = '' + value; | |
| 1648 set.call(this, value); | |
| 1649 } | |
| 1650 }); // We could've passed this the first time | |
| 1651 // but it triggers a bug in IE11 and Edge 14/15. | |
| 1652 // Calling defineProperty() again should be equivalent. | |
| 1653 // https://github.com/facebook/react/issues/11768 | |
| 1654 | |
| 1655 Object.defineProperty(node, valueField, { | |
| 1656 enumerable: descriptor.enumerable | |
| 1657 }); | |
| 1658 var tracker = { | |
| 1659 getValue: function () { | |
| 1660 return currentValue; | |
| 1661 }, | |
| 1662 setValue: function (value) { | |
| 1663 { | |
| 1664 checkFormFieldValueStringCoercion(value); | |
| 1665 } | |
| 1666 | |
| 1667 currentValue = '' + value; | |
| 1668 }, | |
| 1669 stopTracking: function () { | |
| 1670 detachTracker(node); | |
| 1671 delete node[valueField]; | |
| 1672 } | |
| 1673 }; | |
| 1674 return tracker; | |
| 1675 } | |
| 1676 | |
| 1677 function track(node) { | |
| 1678 if (getTracker(node)) { | |
| 1679 return; | |
| 1680 } // TODO: Once it's just Fiber we can move this to node._wrapperState | |
| 1681 | |
| 1682 | |
| 1683 node._valueTracker = trackValueOnNode(node); | |
| 1684 } | |
| 1685 function updateValueIfChanged(node) { | |
| 1686 if (!node) { | |
| 1687 return false; | |
| 1688 } | |
| 1689 | |
| 1690 var tracker = getTracker(node); // if there is no tracker at this point it's unlikely | |
| 1691 // that trying again will succeed | |
| 1692 | |
| 1693 if (!tracker) { | |
| 1694 return true; | |
| 1695 } | |
| 1696 | |
| 1697 var lastValue = tracker.getValue(); | |
| 1698 var nextValue = getValueFromNode(node); | |
| 1699 | |
| 1700 if (nextValue !== lastValue) { | |
| 1701 tracker.setValue(nextValue); | |
| 1702 return true; | |
| 1703 } | |
| 1704 | |
| 1705 return false; | |
| 1706 } | |
| 1707 | |
| 1708 function getActiveElement(doc) { | |
| 1709 doc = doc || (typeof document !== 'undefined' ? document : undefined); | |
| 1710 | |
| 1711 if (typeof doc === 'undefined') { | |
| 1712 return null; | |
| 1713 } | |
| 1714 | |
| 1715 try { | |
| 1716 return doc.activeElement || doc.body; | |
| 1717 } catch (e) { | |
| 1718 return doc.body; | |
| 1719 } | |
| 1720 } | |
| 1721 | |
| 1722 var didWarnValueDefaultValue = false; | |
| 1723 var didWarnCheckedDefaultChecked = false; | |
| 1724 var didWarnControlledToUncontrolled = false; | |
| 1725 var didWarnUncontrolledToControlled = false; | |
| 1726 | |
| 1727 function isControlled(props) { | |
| 1728 var usesChecked = props.type === 'checkbox' || props.type === 'radio'; | |
| 1729 return usesChecked ? props.checked != null : props.value != null; | |
| 1730 } | |
| 1731 /** | |
| 1732 * Implements an <input> host component that allows setting these optional | |
| 1733 * props: `checked`, `value`, `defaultChecked`, and `defaultValue`. | |
| 1734 * | |
| 1735 * If `checked` or `value` are not supplied (or null/undefined), user actions | |
| 1736 * that affect the checked state or value will trigger updates to the element. | |
| 1737 * | |
| 1738 * If they are supplied (and not null/undefined), the rendered element will not | |
| 1739 * trigger updates to the element. Instead, the props must change in order for | |
| 1740 * the rendered element to be updated. | |
| 1741 * | |
| 1742 * The rendered element will be initialized as unchecked (or `defaultChecked`) | |
| 1743 * with an empty value (or `defaultValue`). | |
| 1744 * | |
| 1745 * See http://www.w3.org/TR/2012/WD-html5-20121025/the-input-element.html | |
| 1746 */ | |
| 1747 | |
| 1748 | |
| 1749 function getHostProps(element, props) { | |
| 1750 var node = element; | |
| 1751 var checked = props.checked; | |
| 1752 var hostProps = assign({}, props, { | |
| 1753 defaultChecked: undefined, | |
| 1754 defaultValue: undefined, | |
| 1755 value: undefined, | |
| 1756 checked: checked != null ? checked : node._wrapperState.initialChecked | |
| 1757 }); | |
| 1758 return hostProps; | |
| 1759 } | |
| 1760 function initWrapperState(element, props) { | |
| 1761 { | |
| 1762 checkControlledValueProps('input', props); | |
| 1763 | |
| 1764 if (props.checked !== undefined && props.defaultChecked !== undefined && !didWarnCheckedDefaultChecked) { | |
| 1765 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); | |
| 1766 | |
| 1767 didWarnCheckedDefaultChecked = true; | |
| 1768 } | |
| 1769 | |
| 1770 if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue) { | |
| 1771 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); | |
| 1772 | |
| 1773 didWarnValueDefaultValue = true; | |
| 1774 } | |
| 1775 } | |
| 1776 | |
| 1777 var node = element; | |
| 1778 var defaultValue = props.defaultValue == null ? '' : props.defaultValue; | |
| 1779 node._wrapperState = { | |
| 1780 initialChecked: props.checked != null ? props.checked : props.defaultChecked, | |
| 1781 initialValue: getToStringValue(props.value != null ? props.value : defaultValue), | |
| 1782 controlled: isControlled(props) | |
| 1783 }; | |
| 1784 } | |
| 1785 function updateChecked(element, props) { | |
| 1786 var node = element; | |
| 1787 var checked = props.checked; | |
| 1788 | |
| 1789 if (checked != null) { | |
| 1790 setValueForProperty(node, 'checked', checked, false); | |
| 1791 } | |
| 1792 } | |
| 1793 function updateWrapper(element, props) { | |
| 1794 var node = element; | |
| 1795 | |
| 1796 { | |
| 1797 var controlled = isControlled(props); | |
| 1798 | |
| 1799 if (!node._wrapperState.controlled && controlled && !didWarnUncontrolledToControlled) { | |
| 1800 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'); | |
| 1801 | |
| 1802 didWarnUncontrolledToControlled = true; | |
| 1803 } | |
| 1804 | |
| 1805 if (node._wrapperState.controlled && !controlled && !didWarnControlledToUncontrolled) { | |
| 1806 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'); | |
| 1807 | |
| 1808 didWarnControlledToUncontrolled = true; | |
| 1809 } | |
| 1810 } | |
| 1811 | |
| 1812 updateChecked(element, props); | |
| 1813 var value = getToStringValue(props.value); | |
| 1814 var type = props.type; | |
| 1815 | |
| 1816 if (value != null) { | |
| 1817 if (type === 'number') { | |
| 1818 if (value === 0 && node.value === '' || // We explicitly want to coerce to number here if possible. | |
| 1819 // eslint-disable-next-line | |
| 1820 node.value != value) { | |
| 1821 node.value = toString(value); | |
| 1822 } | |
| 1823 } else if (node.value !== toString(value)) { | |
| 1824 node.value = toString(value); | |
| 1825 } | |
| 1826 } else if (type === 'submit' || type === 'reset') { | |
| 1827 // Submit/reset inputs need the attribute removed completely to avoid | |
| 1828 // blank-text buttons. | |
| 1829 node.removeAttribute('value'); | |
| 1830 return; | |
| 1831 } | |
| 1832 | |
| 1833 { | |
| 1834 // When syncing the value attribute, the value comes from a cascade of | |
| 1835 // properties: | |
| 1836 // 1. The value React property | |
| 1837 // 2. The defaultValue React property | |
| 1838 // 3. Otherwise there should be no change | |
| 1839 if (props.hasOwnProperty('value')) { | |
| 1840 setDefaultValue(node, props.type, value); | |
| 1841 } else if (props.hasOwnProperty('defaultValue')) { | |
| 1842 setDefaultValue(node, props.type, getToStringValue(props.defaultValue)); | |
| 1843 } | |
| 1844 } | |
| 1845 | |
| 1846 { | |
| 1847 // When syncing the checked attribute, it only changes when it needs | |
| 1848 // to be removed, such as transitioning from a checkbox into a text input | |
| 1849 if (props.checked == null && props.defaultChecked != null) { | |
| 1850 node.defaultChecked = !!props.defaultChecked; | |
| 1851 } | |
| 1852 } | |
| 1853 } | |
| 1854 function postMountWrapper(element, props, isHydrating) { | |
| 1855 var node = element; // Do not assign value if it is already set. This prevents user text input | |
| 1856 // from being lost during SSR hydration. | |
| 1857 | |
| 1858 if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) { | |
| 1859 var type = props.type; | |
| 1860 var isButton = type === 'submit' || type === 'reset'; // Avoid setting value attribute on submit/reset inputs as it overrides the | |
| 1861 // default value provided by the browser. See: #12872 | |
| 1862 | |
| 1863 if (isButton && (props.value === undefined || props.value === null)) { | |
| 1864 return; | |
| 1865 } | |
| 1866 | |
| 1867 var initialValue = toString(node._wrapperState.initialValue); // Do not assign value if it is already set. This prevents user text input | |
| 1868 // from being lost during SSR hydration. | |
| 1869 | |
| 1870 if (!isHydrating) { | |
| 1871 { | |
| 1872 // When syncing the value attribute, the value property should use | |
| 1873 // the wrapperState._initialValue property. This uses: | |
| 1874 // | |
| 1875 // 1. The value React property when present | |
| 1876 // 2. The defaultValue React property when present | |
| 1877 // 3. An empty string | |
| 1878 if (initialValue !== node.value) { | |
| 1879 node.value = initialValue; | |
| 1880 } | |
| 1881 } | |
| 1882 } | |
| 1883 | |
| 1884 { | |
| 1885 // Otherwise, the value attribute is synchronized to the property, | |
| 1886 // so we assign defaultValue to the same thing as the value property | |
| 1887 // assignment step above. | |
| 1888 node.defaultValue = initialValue; | |
| 1889 } | |
| 1890 } // Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug | |
| 1891 // this is needed to work around a chrome bug where setting defaultChecked | |
| 1892 // will sometimes influence the value of checked (even after detachment). | |
| 1893 // Reference: https://bugs.chromium.org/p/chromium/issues/detail?id=608416 | |
| 1894 // We need to temporarily unset name to avoid disrupting radio button groups. | |
| 1895 | |
| 1896 | |
| 1897 var name = node.name; | |
| 1898 | |
| 1899 if (name !== '') { | |
| 1900 node.name = ''; | |
| 1901 } | |
| 1902 | |
| 1903 { | |
| 1904 // When syncing the checked attribute, both the checked property and | |
| 1905 // attribute are assigned at the same time using defaultChecked. This uses: | |
| 1906 // | |
| 1907 // 1. The checked React property when present | |
| 1908 // 2. The defaultChecked React property when present | |
| 1909 // 3. Otherwise, false | |
| 1910 node.defaultChecked = !node.defaultChecked; | |
| 1911 node.defaultChecked = !!node._wrapperState.initialChecked; | |
| 1912 } | |
| 1913 | |
| 1914 if (name !== '') { | |
| 1915 node.name = name; | |
| 1916 } | |
| 1917 } | |
| 1918 function restoreControlledState(element, props) { | |
| 1919 var node = element; | |
| 1920 updateWrapper(node, props); | |
| 1921 updateNamedCousins(node, props); | |
| 1922 } | |
| 1923 | |
| 1924 function updateNamedCousins(rootNode, props) { | |
| 1925 var name = props.name; | |
| 1926 | |
| 1927 if (props.type === 'radio' && name != null) { | |
| 1928 var queryRoot = rootNode; | |
| 1929 | |
| 1930 while (queryRoot.parentNode) { | |
| 1931 queryRoot = queryRoot.parentNode; | |
| 1932 } // If `rootNode.form` was non-null, then we could try `form.elements`, | |
| 1933 // but that sometimes behaves strangely in IE8. We could also try using | |
| 1934 // `form.getElementsByName`, but that will only return direct children | |
| 1935 // and won't include inputs that use the HTML5 `form=` attribute. Since | |
| 1936 // the input might not even be in a form. It might not even be in the | |
| 1937 // document. Let's just use the local `querySelectorAll` to ensure we don't | |
| 1938 // miss anything. | |
| 1939 | |
| 1940 | |
| 1941 { | |
| 1942 checkAttributeStringCoercion(name, 'name'); | |
| 1943 } | |
| 1944 | |
| 1945 var group = queryRoot.querySelectorAll('input[name=' + JSON.stringify('' + name) + '][type="radio"]'); | |
| 1946 | |
| 1947 for (var i = 0; i < group.length; i++) { | |
| 1948 var otherNode = group[i]; | |
| 1949 | |
| 1950 if (otherNode === rootNode || otherNode.form !== rootNode.form) { | |
| 1951 continue; | |
| 1952 } // This will throw if radio buttons rendered by different copies of React | |
| 1953 // and the same name are rendered into the same form (same as #1939). | |
| 1954 // That's probably okay; we don't support it just as we don't support | |
| 1955 // mixing React radio buttons with non-React ones. | |
| 1956 | |
| 1957 | |
| 1958 var otherProps = getFiberCurrentPropsFromNode(otherNode); | |
| 1959 | |
| 1960 if (!otherProps) { | |
| 1961 throw new Error('ReactDOMInput: Mixing React and non-React radio inputs with the ' + 'same `name` is not supported.'); | |
| 1962 } // We need update the tracked value on the named cousin since the value | |
| 1963 // was changed but the input saw no event or value set | |
| 1964 | |
| 1965 | |
| 1966 updateValueIfChanged(otherNode); // If this is a controlled radio button group, forcing the input that | |
| 1967 // was previously checked to update will cause it to be come re-checked | |
| 1968 // as appropriate. | |
| 1969 | |
| 1970 updateWrapper(otherNode, otherProps); | |
| 1971 } | |
| 1972 } | |
| 1973 } // In Chrome, assigning defaultValue to certain input types triggers input validation. | |
| 1974 // For number inputs, the display value loses trailing decimal points. For email inputs, | |
| 1975 // Chrome raises "The specified value <x> is not a valid email address". | |
| 1976 // | |
| 1977 // Here we check to see if the defaultValue has actually changed, avoiding these problems | |
| 1978 // when the user is inputting text | |
| 1979 // | |
| 1980 // https://github.com/facebook/react/issues/7253 | |
| 1981 | |
| 1982 | |
| 1983 function setDefaultValue(node, type, value) { | |
| 1984 if ( // Focused number inputs synchronize on blur. See ChangeEventPlugin.js | |
| 1985 type !== 'number' || getActiveElement(node.ownerDocument) !== node) { | |
| 1986 if (value == null) { | |
| 1987 node.defaultValue = toString(node._wrapperState.initialValue); | |
| 1988 } else if (node.defaultValue !== toString(value)) { | |
| 1989 node.defaultValue = toString(value); | |
| 1990 } | |
| 1991 } | |
| 1992 } | |
| 1993 | |
| 1994 var didWarnSelectedSetOnOption = false; | |
| 1995 var didWarnInvalidChild = false; | |
| 1996 var didWarnInvalidInnerHTML = false; | |
| 1997 /** | |
| 1998 * Implements an <option> host component that warns when `selected` is set. | |
| 1999 */ | |
| 2000 | |
| 2001 function validateProps(element, props) { | |
| 2002 { | |
| 2003 // If a value is not provided, then the children must be simple. | |
| 2004 if (props.value == null) { | |
| 2005 if (typeof props.children === 'object' && props.children !== null) { | |
| 2006 React.Children.forEach(props.children, function (child) { | |
| 2007 if (child == null) { | |
| 2008 return; | |
| 2009 } | |
| 2010 | |
| 2011 if (typeof child === 'string' || typeof child === 'number') { | |
| 2012 return; | |
| 2013 } | |
| 2014 | |
| 2015 if (!didWarnInvalidChild) { | |
| 2016 didWarnInvalidChild = true; | |
| 2017 | |
| 2018 error('Cannot infer the option value of complex children. ' + 'Pass a `value` prop or use a plain string as children to <option>.'); | |
| 2019 } | |
| 2020 }); | |
| 2021 } else if (props.dangerouslySetInnerHTML != null) { | |
| 2022 if (!didWarnInvalidInnerHTML) { | |
| 2023 didWarnInvalidInnerHTML = true; | |
| 2024 | |
| 2025 error('Pass a `value` prop if you set dangerouslyInnerHTML so React knows ' + 'which value should be selected.'); | |
| 2026 } | |
| 2027 } | |
| 2028 } // TODO: Remove support for `selected` in <option>. | |
| 2029 | |
| 2030 | |
| 2031 if (props.selected != null && !didWarnSelectedSetOnOption) { | |
| 2032 error('Use the `defaultValue` or `value` props on <select> instead of ' + 'setting `selected` on <option>.'); | |
| 2033 | |
| 2034 didWarnSelectedSetOnOption = true; | |
| 2035 } | |
| 2036 } | |
| 2037 } | |
| 2038 function postMountWrapper$1(element, props) { | |
| 2039 // value="" should make a value attribute (#6219) | |
| 2040 if (props.value != null) { | |
| 2041 element.setAttribute('value', toString(getToStringValue(props.value))); | |
| 2042 } | |
| 2043 } | |
| 2044 | |
| 2045 var isArrayImpl = Array.isArray; // eslint-disable-next-line no-redeclare | |
| 2046 | |
| 2047 function isArray(a) { | |
| 2048 return isArrayImpl(a); | |
| 2049 } | |
| 2050 | |
| 2051 var didWarnValueDefaultValue$1; | |
| 2052 | |
| 2053 { | |
| 2054 didWarnValueDefaultValue$1 = false; | |
| 2055 } | |
| 2056 | |
| 2057 function getDeclarationErrorAddendum() { | |
| 2058 var ownerName = getCurrentFiberOwnerNameInDevOrNull(); | |
| 2059 | |
| 2060 if (ownerName) { | |
| 2061 return '\n\nCheck the render method of `' + ownerName + '`.'; | |
| 2062 } | |
| 2063 | |
| 2064 return ''; | |
| 2065 } | |
| 2066 | |
| 2067 var valuePropNames = ['value', 'defaultValue']; | |
| 2068 /** | |
| 2069 * Validation function for `value` and `defaultValue`. | |
| 2070 */ | |
| 2071 | |
| 2072 function checkSelectPropTypes(props) { | |
| 2073 { | |
| 2074 checkControlledValueProps('select', props); | |
| 2075 | |
| 2076 for (var i = 0; i < valuePropNames.length; i++) { | |
| 2077 var propName = valuePropNames[i]; | |
| 2078 | |
| 2079 if (props[propName] == null) { | |
| 2080 continue; | |
| 2081 } | |
| 2082 | |
| 2083 var propNameIsArray = isArray(props[propName]); | |
| 2084 | |
| 2085 if (props.multiple && !propNameIsArray) { | |
| 2086 error('The `%s` prop supplied to <select> must be an array if ' + '`multiple` is true.%s', propName, getDeclarationErrorAddendum()); | |
| 2087 } else if (!props.multiple && propNameIsArray) { | |
| 2088 error('The `%s` prop supplied to <select> must be a scalar ' + 'value if `multiple` is false.%s', propName, getDeclarationErrorAddendum()); | |
| 2089 } | |
| 2090 } | |
| 2091 } | |
| 2092 } | |
| 2093 | |
| 2094 function updateOptions(node, multiple, propValue, setDefaultSelected) { | |
| 2095 var options = node.options; | |
| 2096 | |
| 2097 if (multiple) { | |
| 2098 var selectedValues = propValue; | |
| 2099 var selectedValue = {}; | |
| 2100 | |
| 2101 for (var i = 0; i < selectedValues.length; i++) { | |
| 2102 // Prefix to avoid chaos with special keys. | |
| 2103 selectedValue['$' + selectedValues[i]] = true; | |
| 2104 } | |
| 2105 | |
| 2106 for (var _i = 0; _i < options.length; _i++) { | |
| 2107 var selected = selectedValue.hasOwnProperty('$' + options[_i].value); | |
| 2108 | |
| 2109 if (options[_i].selected !== selected) { | |
| 2110 options[_i].selected = selected; | |
| 2111 } | |
| 2112 | |
| 2113 if (selected && setDefaultSelected) { | |
| 2114 options[_i].defaultSelected = true; | |
| 2115 } | |
| 2116 } | |
| 2117 } else { | |
| 2118 // Do not set `select.value` as exact behavior isn't consistent across all | |
| 2119 // browsers for all cases. | |
| 2120 var _selectedValue = toString(getToStringValue(propValue)); | |
| 2121 | |
| 2122 var defaultSelected = null; | |
| 2123 | |
| 2124 for (var _i2 = 0; _i2 < options.length; _i2++) { | |
| 2125 if (options[_i2].value === _selectedValue) { | |
| 2126 options[_i2].selected = true; | |
| 2127 | |
| 2128 if (setDefaultSelected) { | |
| 2129 options[_i2].defaultSelected = true; | |
| 2130 } | |
| 2131 | |
| 2132 return; | |
| 2133 } | |
| 2134 | |
| 2135 if (defaultSelected === null && !options[_i2].disabled) { | |
| 2136 defaultSelected = options[_i2]; | |
| 2137 } | |
| 2138 } | |
| 2139 | |
| 2140 if (defaultSelected !== null) { | |
| 2141 defaultSelected.selected = true; | |
| 2142 } | |
| 2143 } | |
| 2144 } | |
| 2145 /** | |
| 2146 * Implements a <select> host component that allows optionally setting the | |
| 2147 * props `value` and `defaultValue`. If `multiple` is false, the prop must be a | |
| 2148 * stringable. If `multiple` is true, the prop must be an array of stringables. | |
| 2149 * | |
| 2150 * If `value` is not supplied (or null/undefined), user actions that change the | |
| 2151 * selected option will trigger updates to the rendered options. | |
| 2152 * | |
| 2153 * If it is supplied (and not null/undefined), the rendered options will not | |
| 2154 * update in response to user actions. Instead, the `value` prop must change in | |
| 2155 * order for the rendered options to update. | |
| 2156 * | |
| 2157 * If `defaultValue` is provided, any options with the supplied values will be | |
| 2158 * selected. | |
| 2159 */ | |
| 2160 | |
| 2161 | |
| 2162 function getHostProps$1(element, props) { | |
| 2163 return assign({}, props, { | |
| 2164 value: undefined | |
| 2165 }); | |
| 2166 } | |
| 2167 function initWrapperState$1(element, props) { | |
| 2168 var node = element; | |
| 2169 | |
| 2170 { | |
| 2171 checkSelectPropTypes(props); | |
| 2172 } | |
| 2173 | |
| 2174 node._wrapperState = { | |
| 2175 wasMultiple: !!props.multiple | |
| 2176 }; | |
| 2177 | |
| 2178 { | |
| 2179 if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValueDefaultValue$1) { | |
| 2180 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'); | |
| 2181 | |
| 2182 didWarnValueDefaultValue$1 = true; | |
| 2183 } | |
| 2184 } | |
| 2185 } | |
| 2186 function postMountWrapper$2(element, props) { | |
| 2187 var node = element; | |
| 2188 node.multiple = !!props.multiple; | |
| 2189 var value = props.value; | |
| 2190 | |
| 2191 if (value != null) { | |
| 2192 updateOptions(node, !!props.multiple, value, false); | |
| 2193 } else if (props.defaultValue != null) { | |
| 2194 updateOptions(node, !!props.multiple, props.defaultValue, true); | |
| 2195 } | |
| 2196 } | |
| 2197 function postUpdateWrapper(element, props) { | |
| 2198 var node = element; | |
| 2199 var wasMultiple = node._wrapperState.wasMultiple; | |
| 2200 node._wrapperState.wasMultiple = !!props.multiple; | |
| 2201 var value = props.value; | |
| 2202 | |
| 2203 if (value != null) { | |
| 2204 updateOptions(node, !!props.multiple, value, false); | |
| 2205 } else if (wasMultiple !== !!props.multiple) { | |
| 2206 // For simplicity, reapply `defaultValue` if `multiple` is toggled. | |
| 2207 if (props.defaultValue != null) { | |
| 2208 updateOptions(node, !!props.multiple, props.defaultValue, true); | |
| 2209 } else { | |
| 2210 // Revert the select back to its default unselected state. | |
| 2211 updateOptions(node, !!props.multiple, props.multiple ? [] : '', false); | |
| 2212 } | |
| 2213 } | |
| 2214 } | |
| 2215 function restoreControlledState$1(element, props) { | |
| 2216 var node = element; | |
| 2217 var value = props.value; | |
| 2218 | |
| 2219 if (value != null) { | |
| 2220 updateOptions(node, !!props.multiple, value, false); | |
| 2221 } | |
| 2222 } | |
| 2223 | |
| 2224 var didWarnValDefaultVal = false; | |
| 2225 | |
| 2226 /** | |
| 2227 * Implements a <textarea> host component that allows setting `value`, and | |
| 2228 * `defaultValue`. This differs from the traditional DOM API because value is | |
| 2229 * usually set as PCDATA children. | |
| 2230 * | |
| 2231 * If `value` is not supplied (or null/undefined), user actions that affect the | |
| 2232 * value will trigger updates to the element. | |
| 2233 * | |
| 2234 * If `value` is supplied (and not null/undefined), the rendered element will | |
| 2235 * not trigger updates to the element. Instead, the `value` prop must change in | |
| 2236 * order for the rendered element to be updated. | |
| 2237 * | |
| 2238 * The rendered element will be initialized with an empty value, the prop | |
| 2239 * `defaultValue` if specified, or the children content (deprecated). | |
| 2240 */ | |
| 2241 function getHostProps$2(element, props) { | |
| 2242 var node = element; | |
| 2243 | |
| 2244 if (props.dangerouslySetInnerHTML != null) { | |
| 2245 throw new Error('`dangerouslySetInnerHTML` does not make sense on <textarea>.'); | |
| 2246 } // Always set children to the same thing. In IE9, the selection range will | |
| 2247 // get reset if `textContent` is mutated. We could add a check in setTextContent | |
| 2248 // to only set the value if/when the value differs from the node value (which would | |
| 2249 // completely solve this IE9 bug), but Sebastian+Sophie seemed to like this | |
| 2250 // solution. The value can be a boolean or object so that's why it's forced | |
| 2251 // to be a string. | |
| 2252 | |
| 2253 | |
| 2254 var hostProps = assign({}, props, { | |
| 2255 value: undefined, | |
| 2256 defaultValue: undefined, | |
| 2257 children: toString(node._wrapperState.initialValue) | |
| 2258 }); | |
| 2259 | |
| 2260 return hostProps; | |
| 2261 } | |
| 2262 function initWrapperState$2(element, props) { | |
| 2263 var node = element; | |
| 2264 | |
| 2265 { | |
| 2266 checkControlledValueProps('textarea', props); | |
| 2267 | |
| 2268 if (props.value !== undefined && props.defaultValue !== undefined && !didWarnValDefaultVal) { | |
| 2269 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'); | |
| 2270 | |
| 2271 didWarnValDefaultVal = true; | |
| 2272 } | |
| 2273 } | |
| 2274 | |
| 2275 var initialValue = props.value; // Only bother fetching default value if we're going to use it | |
| 2276 | |
| 2277 if (initialValue == null) { | |
| 2278 var children = props.children, | |
| 2279 defaultValue = props.defaultValue; | |
| 2280 | |
| 2281 if (children != null) { | |
| 2282 { | |
| 2283 error('Use the `defaultValue` or `value` props instead of setting ' + 'children on <textarea>.'); | |
| 2284 } | |
| 2285 | |
| 2286 { | |
| 2287 if (defaultValue != null) { | |
| 2288 throw new Error('If you supply `defaultValue` on a <textarea>, do not pass children.'); | |
| 2289 } | |
| 2290 | |
| 2291 if (isArray(children)) { | |
| 2292 if (children.length > 1) { | |
| 2293 throw new Error('<textarea> can only have at most one child.'); | |
| 2294 } | |
| 2295 | |
| 2296 children = children[0]; | |
| 2297 } | |
| 2298 | |
| 2299 defaultValue = children; | |
| 2300 } | |
| 2301 } | |
| 2302 | |
| 2303 if (defaultValue == null) { | |
| 2304 defaultValue = ''; | |
| 2305 } | |
| 2306 | |
| 2307 initialValue = defaultValue; | |
| 2308 } | |
| 2309 | |
| 2310 node._wrapperState = { | |
| 2311 initialValue: getToStringValue(initialValue) | |
| 2312 }; | |
| 2313 } | |
| 2314 function updateWrapper$1(element, props) { | |
| 2315 var node = element; | |
| 2316 var value = getToStringValue(props.value); | |
| 2317 var defaultValue = getToStringValue(props.defaultValue); | |
| 2318 | |
| 2319 if (value != null) { | |
| 2320 // Cast `value` to a string to ensure the value is set correctly. While | |
| 2321 // browsers typically do this as necessary, jsdom doesn't. | |
| 2322 var newValue = toString(value); // To avoid side effects (such as losing text selection), only set value if changed | |
| 2323 | |
| 2324 if (newValue !== node.value) { | |
| 2325 node.value = newValue; | |
| 2326 } | |
| 2327 | |
| 2328 if (props.defaultValue == null && node.defaultValue !== newValue) { | |
| 2329 node.defaultValue = newValue; | |
| 2330 } | |
| 2331 } | |
| 2332 | |
| 2333 if (defaultValue != null) { | |
| 2334 node.defaultValue = toString(defaultValue); | |
| 2335 } | |
| 2336 } | |
| 2337 function postMountWrapper$3(element, props) { | |
| 2338 var node = element; // This is in postMount because we need access to the DOM node, which is not | |
| 2339 // available until after the component has mounted. | |
| 2340 | |
| 2341 var textContent = node.textContent; // Only set node.value if textContent is equal to the expected | |
| 2342 // initial value. In IE10/IE11 there is a bug where the placeholder attribute | |
| 2343 // will populate textContent as well. | |
| 2344 // https://developer.microsoft.com/microsoft-edge/platform/issues/101525/ | |
| 2345 | |
| 2346 if (textContent === node._wrapperState.initialValue) { | |
| 2347 if (textContent !== '' && textContent !== null) { | |
| 2348 node.value = textContent; | |
| 2349 } | |
| 2350 } | |
| 2351 } | |
| 2352 function restoreControlledState$2(element, props) { | |
| 2353 // DOM component is still mounted; update | |
| 2354 updateWrapper$1(element, props); | |
| 2355 } | |
| 2356 | |
| 2357 var HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml'; | |
| 2358 var MATH_NAMESPACE = 'http://www.w3.org/1998/Math/MathML'; | |
| 2359 var SVG_NAMESPACE = 'http://www.w3.org/2000/svg'; // Assumes there is no parent namespace. | |
| 2360 | |
| 2361 function getIntrinsicNamespace(type) { | |
| 2362 switch (type) { | |
| 2363 case 'svg': | |
| 2364 return SVG_NAMESPACE; | |
| 2365 | |
| 2366 case 'math': | |
| 2367 return MATH_NAMESPACE; | |
| 2368 | |
| 2369 default: | |
| 2370 return HTML_NAMESPACE; | |
| 2371 } | |
| 2372 } | |
| 2373 function getChildNamespace(parentNamespace, type) { | |
| 2374 if (parentNamespace == null || parentNamespace === HTML_NAMESPACE) { | |
| 2375 // No (or default) parent namespace: potential entry point. | |
| 2376 return getIntrinsicNamespace(type); | |
| 2377 } | |
| 2378 | |
| 2379 if (parentNamespace === SVG_NAMESPACE && type === 'foreignObject') { | |
| 2380 // We're leaving SVG. | |
| 2381 return HTML_NAMESPACE; | |
| 2382 } // By default, pass namespace below. | |
| 2383 | |
| 2384 | |
| 2385 return parentNamespace; | |
| 2386 } | |
| 2387 | |
| 2388 /* globals MSApp */ | |
| 2389 | |
| 2390 /** | |
| 2391 * Create a function which has 'unsafe' privileges (required by windows8 apps) | |
| 2392 */ | |
| 2393 var createMicrosoftUnsafeLocalFunction = function (func) { | |
| 2394 if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) { | |
| 2395 return function (arg0, arg1, arg2, arg3) { | |
| 2396 MSApp.execUnsafeLocalFunction(function () { | |
| 2397 return func(arg0, arg1, arg2, arg3); | |
| 2398 }); | |
| 2399 }; | |
| 2400 } else { | |
| 2401 return func; | |
| 2402 } | |
| 2403 }; | |
| 2404 | |
| 2405 var reusableSVGContainer; | |
| 2406 /** | |
| 2407 * Set the innerHTML property of a node | |
| 2408 * | |
| 2409 * @param {DOMElement} node | |
| 2410 * @param {string} html | |
| 2411 * @internal | |
| 2412 */ | |
| 2413 | |
| 2414 var setInnerHTML = createMicrosoftUnsafeLocalFunction(function (node, html) { | |
| 2415 if (node.namespaceURI === SVG_NAMESPACE) { | |
| 2416 | |
| 2417 if (!('innerHTML' in node)) { | |
| 2418 // IE does not have innerHTML for SVG nodes, so instead we inject the | |
| 2419 // new markup in a temp node and then move the child nodes across into | |
| 2420 // the target node | |
| 2421 reusableSVGContainer = reusableSVGContainer || document.createElement('div'); | |
| 2422 reusableSVGContainer.innerHTML = '<svg>' + html.valueOf().toString() + '</svg>'; | |
| 2423 var svgNode = reusableSVGContainer.firstChild; | |
| 2424 | |
| 2425 while (node.firstChild) { | |
| 2426 node.removeChild(node.firstChild); | |
| 2427 } | |
| 2428 | |
| 2429 while (svgNode.firstChild) { | |
| 2430 node.appendChild(svgNode.firstChild); | |
| 2431 } | |
| 2432 | |
| 2433 return; | |
| 2434 } | |
| 2435 } | |
| 2436 | |
| 2437 node.innerHTML = html; | |
| 2438 }); | |
| 2439 | |
| 2440 /** | |
| 2441 * HTML nodeType values that represent the type of the node | |
| 2442 */ | |
| 2443 var ELEMENT_NODE = 1; | |
| 2444 var TEXT_NODE = 3; | |
| 2445 var COMMENT_NODE = 8; | |
| 2446 var DOCUMENT_NODE = 9; | |
| 2447 var DOCUMENT_FRAGMENT_NODE = 11; | |
| 2448 | |
| 2449 /** | |
| 2450 * Set the textContent property of a node. For text updates, it's faster | |
| 2451 * to set the `nodeValue` of the Text node directly instead of using | |
| 2452 * `.textContent` which will remove the existing node and create a new one. | |
| 2453 * | |
| 2454 * @param {DOMElement} node | |
| 2455 * @param {string} text | |
| 2456 * @internal | |
| 2457 */ | |
| 2458 | |
| 2459 var setTextContent = function (node, text) { | |
| 2460 if (text) { | |
| 2461 var firstChild = node.firstChild; | |
| 2462 | |
| 2463 if (firstChild && firstChild === node.lastChild && firstChild.nodeType === TEXT_NODE) { | |
| 2464 firstChild.nodeValue = text; | |
| 2465 return; | |
| 2466 } | |
| 2467 } | |
| 2468 | |
| 2469 node.textContent = text; | |
| 2470 }; | |
| 2471 | |
| 2472 // List derived from Gecko source code: | |
| 2473 // https://github.com/mozilla/gecko-dev/blob/4e638efc71/layout/style/test/property_database.js | |
| 2474 var shorthandToLonghand = { | |
| 2475 animation: ['animationDelay', 'animationDirection', 'animationDuration', 'animationFillMode', 'animationIterationCount', 'animationName', 'animationPlayState', 'animationTimingFunction'], | |
| 2476 background: ['backgroundAttachment', 'backgroundClip', 'backgroundColor', 'backgroundImage', 'backgroundOrigin', 'backgroundPositionX', 'backgroundPositionY', 'backgroundRepeat', 'backgroundSize'], | |
| 2477 backgroundPosition: ['backgroundPositionX', 'backgroundPositionY'], | |
| 2478 border: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth', 'borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth', 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderTopColor', 'borderTopStyle', 'borderTopWidth'], | |
| 2479 borderBlockEnd: ['borderBlockEndColor', 'borderBlockEndStyle', 'borderBlockEndWidth'], | |
| 2480 borderBlockStart: ['borderBlockStartColor', 'borderBlockStartStyle', 'borderBlockStartWidth'], | |
| 2481 borderBottom: ['borderBottomColor', 'borderBottomStyle', 'borderBottomWidth'], | |
| 2482 borderColor: ['borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor'], | |
| 2483 borderImage: ['borderImageOutset', 'borderImageRepeat', 'borderImageSlice', 'borderImageSource', 'borderImageWidth'], | |
| 2484 borderInlineEnd: ['borderInlineEndColor', 'borderInlineEndStyle', 'borderInlineEndWidth'], | |
| 2485 borderInlineStart: ['borderInlineStartColor', 'borderInlineStartStyle', 'borderInlineStartWidth'], | |
| 2486 borderLeft: ['borderLeftColor', 'borderLeftStyle', 'borderLeftWidth'], | |
| 2487 borderRadius: ['borderBottomLeftRadius', 'borderBottomRightRadius', 'borderTopLeftRadius', 'borderTopRightRadius'], | |
| 2488 borderRight: ['borderRightColor', 'borderRightStyle', 'borderRightWidth'], | |
| 2489 borderStyle: ['borderBottomStyle', 'borderLeftStyle', 'borderRightStyle', 'borderTopStyle'], | |
| 2490 borderTop: ['borderTopColor', 'borderTopStyle', 'borderTopWidth'], | |
| 2491 borderWidth: ['borderBottomWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth'], | |
| 2492 columnRule: ['columnRuleColor', 'columnRuleStyle', 'columnRuleWidth'], | |
| 2493 columns: ['columnCount', 'columnWidth'], | |
| 2494 flex: ['flexBasis', 'flexGrow', 'flexShrink'], | |
| 2495 flexFlow: ['flexDirection', 'flexWrap'], | |
| 2496 font: ['fontFamily', 'fontFeatureSettings', 'fontKerning', 'fontLanguageOverride', 'fontSize', 'fontSizeAdjust', 'fontStretch', 'fontStyle', 'fontVariant', 'fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition', 'fontWeight', 'lineHeight'], | |
| 2497 fontVariant: ['fontVariantAlternates', 'fontVariantCaps', 'fontVariantEastAsian', 'fontVariantLigatures', 'fontVariantNumeric', 'fontVariantPosition'], | |
| 2498 gap: ['columnGap', 'rowGap'], | |
| 2499 grid: ['gridAutoColumns', 'gridAutoFlow', 'gridAutoRows', 'gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'], | |
| 2500 gridArea: ['gridColumnEnd', 'gridColumnStart', 'gridRowEnd', 'gridRowStart'], | |
| 2501 gridColumn: ['gridColumnEnd', 'gridColumnStart'], | |
| 2502 gridColumnGap: ['columnGap'], | |
| 2503 gridGap: ['columnGap', 'rowGap'], | |
| 2504 gridRow: ['gridRowEnd', 'gridRowStart'], | |
| 2505 gridRowGap: ['rowGap'], | |
| 2506 gridTemplate: ['gridTemplateAreas', 'gridTemplateColumns', 'gridTemplateRows'], | |
| 2507 listStyle: ['listStyleImage', 'listStylePosition', 'listStyleType'], | |
| 2508 margin: ['marginBottom', 'marginLeft', 'marginRight', 'marginTop'], | |
| 2509 marker: ['markerEnd', 'markerMid', 'markerStart'], | |
| 2510 mask: ['maskClip', 'maskComposite', 'maskImage', 'maskMode', 'maskOrigin', 'maskPositionX', 'maskPositionY', 'maskRepeat', 'maskSize'], | |
| 2511 maskPosition: ['maskPositionX', 'maskPositionY'], | |
| 2512 outline: ['outlineColor', 'outlineStyle', 'outlineWidth'], | |
| 2513 overflow: ['overflowX', 'overflowY'], | |
| 2514 padding: ['paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop'], | |
| 2515 placeContent: ['alignContent', 'justifyContent'], | |
| 2516 placeItems: ['alignItems', 'justifyItems'], | |
| 2517 placeSelf: ['alignSelf', 'justifySelf'], | |
| 2518 textDecoration: ['textDecorationColor', 'textDecorationLine', 'textDecorationStyle'], | |
| 2519 textEmphasis: ['textEmphasisColor', 'textEmphasisStyle'], | |
| 2520 transition: ['transitionDelay', 'transitionDuration', 'transitionProperty', 'transitionTimingFunction'], | |
| 2521 wordWrap: ['overflowWrap'] | |
| 2522 }; | |
| 2523 | |
| 2524 /** | |
| 2525 * CSS properties which accept numbers but are not in units of "px". | |
| 2526 */ | |
| 2527 var isUnitlessNumber = { | |
| 2528 animationIterationCount: true, | |
| 2529 aspectRatio: true, | |
| 2530 borderImageOutset: true, | |
| 2531 borderImageSlice: true, | |
| 2532 borderImageWidth: true, | |
| 2533 boxFlex: true, | |
| 2534 boxFlexGroup: true, | |
| 2535 boxOrdinalGroup: true, | |
| 2536 columnCount: true, | |
| 2537 columns: true, | |
| 2538 flex: true, | |
| 2539 flexGrow: true, | |
| 2540 flexPositive: true, | |
| 2541 flexShrink: true, | |
| 2542 flexNegative: true, | |
| 2543 flexOrder: true, | |
| 2544 gridArea: true, | |
| 2545 gridRow: true, | |
| 2546 gridRowEnd: true, | |
| 2547 gridRowSpan: true, | |
| 2548 gridRowStart: true, | |
| 2549 gridColumn: true, | |
| 2550 gridColumnEnd: true, | |
| 2551 gridColumnSpan: true, | |
| 2552 gridColumnStart: true, | |
| 2553 fontWeight: true, | |
| 2554 lineClamp: true, | |
| 2555 lineHeight: true, | |
| 2556 opacity: true, | |
| 2557 order: true, | |
| 2558 orphans: true, | |
| 2559 tabSize: true, | |
| 2560 widows: true, | |
| 2561 zIndex: true, | |
| 2562 zoom: true, | |
| 2563 // SVG-related properties | |
| 2564 fillOpacity: true, | |
| 2565 floodOpacity: true, | |
| 2566 stopOpacity: true, | |
| 2567 strokeDasharray: true, | |
| 2568 strokeDashoffset: true, | |
| 2569 strokeMiterlimit: true, | |
| 2570 strokeOpacity: true, | |
| 2571 strokeWidth: true | |
| 2572 }; | |
| 2573 /** | |
| 2574 * @param {string} prefix vendor-specific prefix, eg: Webkit | |
| 2575 * @param {string} key style name, eg: transitionDuration | |
| 2576 * @return {string} style name prefixed with `prefix`, properly camelCased, eg: | |
| 2577 * WebkitTransitionDuration | |
| 2578 */ | |
| 2579 | |
| 2580 function prefixKey(prefix, key) { | |
| 2581 return prefix + key.charAt(0).toUpperCase() + key.substring(1); | |
| 2582 } | |
| 2583 /** | |
| 2584 * Support style names that may come passed in prefixed by adding permutations | |
| 2585 * of vendor prefixes. | |
| 2586 */ | |
| 2587 | |
| 2588 | |
| 2589 var prefixes = ['Webkit', 'ms', 'Moz', 'O']; // Using Object.keys here, or else the vanilla for-in loop makes IE8 go into an | |
| 2590 // infinite loop, because it iterates over the newly added props too. | |
| 2591 | |
| 2592 Object.keys(isUnitlessNumber).forEach(function (prop) { | |
| 2593 prefixes.forEach(function (prefix) { | |
| 2594 isUnitlessNumber[prefixKey(prefix, prop)] = isUnitlessNumber[prop]; | |
| 2595 }); | |
| 2596 }); | |
| 2597 | |
| 2598 /** | |
| 2599 * Convert a value into the proper css writable value. The style name `name` | |
| 2600 * should be logical (no hyphens), as specified | |
| 2601 * in `CSSProperty.isUnitlessNumber`. | |
| 2602 * | |
| 2603 * @param {string} name CSS property name such as `topMargin`. | |
| 2604 * @param {*} value CSS property value such as `10px`. | |
| 2605 * @return {string} Normalized style value with dimensions applied. | |
| 2606 */ | |
| 2607 | |
| 2608 function dangerousStyleValue(name, value, isCustomProperty) { | |
| 2609 // Note that we've removed escapeTextForBrowser() calls here since the | |
| 2610 // whole string will be escaped when the attribute is injected into | |
| 2611 // the markup. If you provide unsafe user data here they can inject | |
| 2612 // arbitrary CSS which may be problematic (I couldn't repro this): | |
| 2613 // https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet | |
| 2614 // http://www.thespanner.co.uk/2007/11/26/ultimate-xss-css-injection/ | |
| 2615 // This is not an XSS hole but instead a potential CSS injection issue | |
| 2616 // which has lead to a greater discussion about how we're going to | |
| 2617 // trust URLs moving forward. See #2115901 | |
| 2618 var isEmpty = value == null || typeof value === 'boolean' || value === ''; | |
| 2619 | |
| 2620 if (isEmpty) { | |
| 2621 return ''; | |
| 2622 } | |
| 2623 | |
| 2624 if (!isCustomProperty && typeof value === 'number' && value !== 0 && !(isUnitlessNumber.hasOwnProperty(name) && isUnitlessNumber[name])) { | |
| 2625 return value + 'px'; // Presumes implicit 'px' suffix for unitless numbers | |
| 2626 } | |
| 2627 | |
| 2628 { | |
| 2629 checkCSSPropertyStringCoercion(value, name); | |
| 2630 } | |
| 2631 | |
| 2632 return ('' + value).trim(); | |
| 2633 } | |
| 2634 | |
| 2635 var uppercasePattern = /([A-Z])/g; | |
| 2636 var msPattern = /^ms-/; | |
| 2637 /** | |
| 2638 * Hyphenates a camelcased CSS property name, for example: | |
| 2639 * | |
| 2640 * > hyphenateStyleName('backgroundColor') | |
| 2641 * < "background-color" | |
| 2642 * > hyphenateStyleName('MozTransition') | |
| 2643 * < "-moz-transition" | |
| 2644 * > hyphenateStyleName('msTransition') | |
| 2645 * < "-ms-transition" | |
| 2646 * | |
| 2647 * As Modernizr suggests (http://modernizr.com/docs/#prefixed), an `ms` prefix | |
| 2648 * is converted to `-ms-`. | |
| 2649 */ | |
| 2650 | |
| 2651 function hyphenateStyleName(name) { | |
| 2652 return name.replace(uppercasePattern, '-$1').toLowerCase().replace(msPattern, '-ms-'); | |
| 2653 } | |
| 2654 | |
| 2655 var warnValidStyle = function () {}; | |
| 2656 | |
| 2657 { | |
| 2658 // 'msTransform' is correct, but the other prefixes should be capitalized | |
| 2659 var badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/; | |
| 2660 var msPattern$1 = /^-ms-/; | |
| 2661 var hyphenPattern = /-(.)/g; // style values shouldn't contain a semicolon | |
| 2662 | |
| 2663 var badStyleValueWithSemicolonPattern = /;\s*$/; | |
| 2664 var warnedStyleNames = {}; | |
| 2665 var warnedStyleValues = {}; | |
| 2666 var warnedForNaNValue = false; | |
| 2667 var warnedForInfinityValue = false; | |
| 2668 | |
| 2669 var camelize = function (string) { | |
| 2670 return string.replace(hyphenPattern, function (_, character) { | |
| 2671 return character.toUpperCase(); | |
| 2672 }); | |
| 2673 }; | |
| 2674 | |
| 2675 var warnHyphenatedStyleName = function (name) { | |
| 2676 if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { | |
| 2677 return; | |
| 2678 } | |
| 2679 | |
| 2680 warnedStyleNames[name] = true; | |
| 2681 | |
| 2682 error('Unsupported style property %s. Did you mean %s?', name, // As Andi Smith suggests | |
| 2683 // (http://www.andismith.com/blog/2012/02/modernizr-prefixed/), an `-ms` prefix | |
| 2684 // is converted to lowercase `ms`. | |
| 2685 camelize(name.replace(msPattern$1, 'ms-'))); | |
| 2686 }; | |
| 2687 | |
| 2688 var warnBadVendoredStyleName = function (name) { | |
| 2689 if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) { | |
| 2690 return; | |
| 2691 } | |
| 2692 | |
| 2693 warnedStyleNames[name] = true; | |
| 2694 | |
| 2695 error('Unsupported vendor-prefixed style property %s. Did you mean %s?', name, name.charAt(0).toUpperCase() + name.slice(1)); | |
| 2696 }; | |
| 2697 | |
| 2698 var warnStyleValueWithSemicolon = function (name, value) { | |
| 2699 if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) { | |
| 2700 return; | |
| 2701 } | |
| 2702 | |
| 2703 warnedStyleValues[value] = true; | |
| 2704 | |
| 2705 error("Style property values shouldn't contain a semicolon. " + 'Try "%s: %s" instead.', name, value.replace(badStyleValueWithSemicolonPattern, '')); | |
| 2706 }; | |
| 2707 | |
| 2708 var warnStyleValueIsNaN = function (name, value) { | |
| 2709 if (warnedForNaNValue) { | |
| 2710 return; | |
| 2711 } | |
| 2712 | |
| 2713 warnedForNaNValue = true; | |
| 2714 | |
| 2715 error('`NaN` is an invalid value for the `%s` css style property.', name); | |
| 2716 }; | |
| 2717 | |
| 2718 var warnStyleValueIsInfinity = function (name, value) { | |
| 2719 if (warnedForInfinityValue) { | |
| 2720 return; | |
| 2721 } | |
| 2722 | |
| 2723 warnedForInfinityValue = true; | |
| 2724 | |
| 2725 error('`Infinity` is an invalid value for the `%s` css style property.', name); | |
| 2726 }; | |
| 2727 | |
| 2728 warnValidStyle = function (name, value) { | |
| 2729 if (name.indexOf('-') > -1) { | |
| 2730 warnHyphenatedStyleName(name); | |
| 2731 } else if (badVendoredStyleNamePattern.test(name)) { | |
| 2732 warnBadVendoredStyleName(name); | |
| 2733 } else if (badStyleValueWithSemicolonPattern.test(value)) { | |
| 2734 warnStyleValueWithSemicolon(name, value); | |
| 2735 } | |
| 2736 | |
| 2737 if (typeof value === 'number') { | |
| 2738 if (isNaN(value)) { | |
| 2739 warnStyleValueIsNaN(name, value); | |
| 2740 } else if (!isFinite(value)) { | |
| 2741 warnStyleValueIsInfinity(name, value); | |
| 2742 } | |
| 2743 } | |
| 2744 }; | |
| 2745 } | |
| 2746 | |
| 2747 var warnValidStyle$1 = warnValidStyle; | |
| 2748 | |
| 2749 /** | |
| 2750 * Operations for dealing with CSS properties. | |
| 2751 */ | |
| 2752 | |
| 2753 /** | |
| 2754 * This creates a string that is expected to be equivalent to the style | |
| 2755 * attribute generated by server-side rendering. It by-passes warnings and | |
| 2756 * security checks so it's not safe to use this value for anything other than | |
| 2757 * comparison. It is only used in DEV for SSR validation. | |
| 2758 */ | |
| 2759 | |
| 2760 function createDangerousStringForStyles(styles) { | |
| 2761 { | |
| 2762 var serialized = ''; | |
| 2763 var delimiter = ''; | |
| 2764 | |
| 2765 for (var styleName in styles) { | |
| 2766 if (!styles.hasOwnProperty(styleName)) { | |
| 2767 continue; | |
| 2768 } | |
| 2769 | |
| 2770 var styleValue = styles[styleName]; | |
| 2771 | |
| 2772 if (styleValue != null) { | |
| 2773 var isCustomProperty = styleName.indexOf('--') === 0; | |
| 2774 serialized += delimiter + (isCustomProperty ? styleName : hyphenateStyleName(styleName)) + ':'; | |
| 2775 serialized += dangerousStyleValue(styleName, styleValue, isCustomProperty); | |
| 2776 delimiter = ';'; | |
| 2777 } | |
| 2778 } | |
| 2779 | |
| 2780 return serialized || null; | |
| 2781 } | |
| 2782 } | |
| 2783 /** | |
| 2784 * Sets the value for multiple styles on a node. If a value is specified as | |
| 2785 * '' (empty string), the corresponding style property will be unset. | |
| 2786 * | |
| 2787 * @param {DOMElement} node | |
| 2788 * @param {object} styles | |
| 2789 */ | |
| 2790 | |
| 2791 function setValueForStyles(node, styles) { | |
| 2792 var style = node.style; | |
| 2793 | |
| 2794 for (var styleName in styles) { | |
| 2795 if (!styles.hasOwnProperty(styleName)) { | |
| 2796 continue; | |
| 2797 } | |
| 2798 | |
| 2799 var isCustomProperty = styleName.indexOf('--') === 0; | |
| 2800 | |
| 2801 { | |
| 2802 if (!isCustomProperty) { | |
| 2803 warnValidStyle$1(styleName, styles[styleName]); | |
| 2804 } | |
| 2805 } | |
| 2806 | |
| 2807 var styleValue = dangerousStyleValue(styleName, styles[styleName], isCustomProperty); | |
| 2808 | |
| 2809 if (styleName === 'float') { | |
| 2810 styleName = 'cssFloat'; | |
| 2811 } | |
| 2812 | |
| 2813 if (isCustomProperty) { | |
| 2814 style.setProperty(styleName, styleValue); | |
| 2815 } else { | |
| 2816 style[styleName] = styleValue; | |
| 2817 } | |
| 2818 } | |
| 2819 } | |
| 2820 | |
| 2821 function isValueEmpty(value) { | |
| 2822 return value == null || typeof value === 'boolean' || value === ''; | |
| 2823 } | |
| 2824 /** | |
| 2825 * Given {color: 'red', overflow: 'hidden'} returns { | |
| 2826 * color: 'color', | |
| 2827 * overflowX: 'overflow', | |
| 2828 * overflowY: 'overflow', | |
| 2829 * }. This can be read as "the overflowY property was set by the overflow | |
| 2830 * shorthand". That is, the values are the property that each was derived from. | |
| 2831 */ | |
| 2832 | |
| 2833 | |
| 2834 function expandShorthandMap(styles) { | |
| 2835 var expanded = {}; | |
| 2836 | |
| 2837 for (var key in styles) { | |
| 2838 var longhands = shorthandToLonghand[key] || [key]; | |
| 2839 | |
| 2840 for (var i = 0; i < longhands.length; i++) { | |
| 2841 expanded[longhands[i]] = key; | |
| 2842 } | |
| 2843 } | |
| 2844 | |
| 2845 return expanded; | |
| 2846 } | |
| 2847 /** | |
| 2848 * When mixing shorthand and longhand property names, we warn during updates if | |
| 2849 * we expect an incorrect result to occur. In particular, we warn for: | |
| 2850 * | |
| 2851 * Updating a shorthand property (longhand gets overwritten): | |
| 2852 * {font: 'foo', fontVariant: 'bar'} -> {font: 'baz', fontVariant: 'bar'} | |
| 2853 * becomes .style.font = 'baz' | |
| 2854 * Removing a shorthand property (longhand gets lost too): | |
| 2855 * {font: 'foo', fontVariant: 'bar'} -> {fontVariant: 'bar'} | |
| 2856 * becomes .style.font = '' | |
| 2857 * Removing a longhand property (should revert to shorthand; doesn't): | |
| 2858 * {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'} | |
| 2859 * becomes .style.fontVariant = '' | |
| 2860 */ | |
| 2861 | |
| 2862 | |
| 2863 function validateShorthandPropertyCollisionInDev(styleUpdates, nextStyles) { | |
| 2864 { | |
| 2865 if (!nextStyles) { | |
| 2866 return; | |
| 2867 } | |
| 2868 | |
| 2869 var expandedUpdates = expandShorthandMap(styleUpdates); | |
| 2870 var expandedStyles = expandShorthandMap(nextStyles); | |
| 2871 var warnedAbout = {}; | |
| 2872 | |
| 2873 for (var key in expandedUpdates) { | |
| 2874 var originalKey = expandedUpdates[key]; | |
| 2875 var correctOriginalKey = expandedStyles[key]; | |
| 2876 | |
| 2877 if (correctOriginalKey && originalKey !== correctOriginalKey) { | |
| 2878 var warningKey = originalKey + ',' + correctOriginalKey; | |
| 2879 | |
| 2880 if (warnedAbout[warningKey]) { | |
| 2881 continue; | |
| 2882 } | |
| 2883 | |
| 2884 warnedAbout[warningKey] = true; | |
| 2885 | |
| 2886 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); | |
| 2887 } | |
| 2888 } | |
| 2889 } | |
| 2890 } | |
| 2891 | |
| 2892 // For HTML, certain tags should omit their close tag. We keep a list for | |
| 2893 // those special-case tags. | |
| 2894 var omittedCloseTags = { | |
| 2895 area: true, | |
| 2896 base: true, | |
| 2897 br: true, | |
| 2898 col: true, | |
| 2899 embed: true, | |
| 2900 hr: true, | |
| 2901 img: true, | |
| 2902 input: true, | |
| 2903 keygen: true, | |
| 2904 link: true, | |
| 2905 meta: true, | |
| 2906 param: true, | |
| 2907 source: true, | |
| 2908 track: true, | |
| 2909 wbr: true // NOTE: menuitem's close tag should be omitted, but that causes problems. | |
| 2910 | |
| 2911 }; | |
| 2912 | |
| 2913 // `omittedCloseTags` except that `menuitem` should still have its closing tag. | |
| 2914 | |
| 2915 var voidElementTags = assign({ | |
| 2916 menuitem: true | |
| 2917 }, omittedCloseTags); | |
| 2918 | |
| 2919 var HTML = '__html'; | |
| 2920 | |
| 2921 function assertValidProps(tag, props) { | |
| 2922 if (!props) { | |
| 2923 return; | |
| 2924 } // Note the use of `==` which checks for null or undefined. | |
| 2925 | |
| 2926 | |
| 2927 if (voidElementTags[tag]) { | |
| 2928 if (props.children != null || props.dangerouslySetInnerHTML != null) { | |
| 2929 throw new Error(tag + " is a void element tag and must neither have `children` nor " + 'use `dangerouslySetInnerHTML`.'); | |
| 2930 } | |
| 2931 } | |
| 2932 | |
| 2933 if (props.dangerouslySetInnerHTML != null) { | |
| 2934 if (props.children != null) { | |
| 2935 throw new Error('Can only set one of `children` or `props.dangerouslySetInnerHTML`.'); | |
| 2936 } | |
| 2937 | |
| 2938 if (typeof props.dangerouslySetInnerHTML !== 'object' || !(HTML in props.dangerouslySetInnerHTML)) { | |
| 2939 throw new Error('`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' + 'Please visit https://reactjs.org/link/dangerously-set-inner-html ' + 'for more information.'); | |
| 2940 } | |
| 2941 } | |
| 2942 | |
| 2943 { | |
| 2944 if (!props.suppressContentEditableWarning && props.contentEditable && props.children != null) { | |
| 2945 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.'); | |
| 2946 } | |
| 2947 } | |
| 2948 | |
| 2949 if (props.style != null && typeof props.style !== 'object') { | |
| 2950 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.'); | |
| 2951 } | |
| 2952 } | |
| 2953 | |
| 2954 function isCustomComponent(tagName, props) { | |
| 2955 if (tagName.indexOf('-') === -1) { | |
| 2956 return typeof props.is === 'string'; | |
| 2957 } | |
| 2958 | |
| 2959 switch (tagName) { | |
| 2960 // These are reserved SVG and MathML elements. | |
| 2961 // We don't mind this list too much because we expect it to never grow. | |
| 2962 // The alternative is to track the namespace in a few places which is convoluted. | |
| 2963 // https://w3c.github.io/webcomponents/spec/custom/#custom-elements-core-concepts | |
| 2964 case 'annotation-xml': | |
| 2965 case 'color-profile': | |
| 2966 case 'font-face': | |
| 2967 case 'font-face-src': | |
| 2968 case 'font-face-uri': | |
| 2969 case 'font-face-format': | |
| 2970 case 'font-face-name': | |
| 2971 case 'missing-glyph': | |
| 2972 return false; | |
| 2973 | |
| 2974 default: | |
| 2975 return true; | |
| 2976 } | |
| 2977 } | |
| 2978 | |
| 2979 // When adding attributes to the HTML or SVG allowed attribute list, be sure to | |
| 2980 // also add them to this module to ensure casing and incorrect name | |
| 2981 // warnings. | |
| 2982 var possibleStandardNames = { | |
| 2983 // HTML | |
| 2984 accept: 'accept', | |
| 2985 acceptcharset: 'acceptCharset', | |
| 2986 'accept-charset': 'acceptCharset', | |
| 2987 accesskey: 'accessKey', | |
| 2988 action: 'action', | |
| 2989 allowfullscreen: 'allowFullScreen', | |
| 2990 alt: 'alt', | |
| 2991 as: 'as', | |
| 2992 async: 'async', | |
| 2993 autocapitalize: 'autoCapitalize', | |
| 2994 autocomplete: 'autoComplete', | |
| 2995 autocorrect: 'autoCorrect', | |
| 2996 autofocus: 'autoFocus', | |
| 2997 autoplay: 'autoPlay', | |
| 2998 autosave: 'autoSave', | |
| 2999 capture: 'capture', | |
| 3000 cellpadding: 'cellPadding', | |
| 3001 cellspacing: 'cellSpacing', | |
| 3002 challenge: 'challenge', | |
| 3003 charset: 'charSet', | |
| 3004 checked: 'checked', | |
| 3005 children: 'children', | |
| 3006 cite: 'cite', | |
| 3007 class: 'className', | |
| 3008 classid: 'classID', | |
| 3009 classname: 'className', | |
| 3010 cols: 'cols', | |
| 3011 colspan: 'colSpan', | |
| 3012 content: 'content', | |
| 3013 contenteditable: 'contentEditable', | |
| 3014 contextmenu: 'contextMenu', | |
| 3015 controls: 'controls', | |
| 3016 controlslist: 'controlsList', | |
| 3017 coords: 'coords', | |
| 3018 crossorigin: 'crossOrigin', | |
| 3019 dangerouslysetinnerhtml: 'dangerouslySetInnerHTML', | |
| 3020 data: 'data', | |
| 3021 datetime: 'dateTime', | |
| 3022 default: 'default', | |
| 3023 defaultchecked: 'defaultChecked', | |
| 3024 defaultvalue: 'defaultValue', | |
| 3025 defer: 'defer', | |
| 3026 dir: 'dir', | |
| 3027 disabled: 'disabled', | |
| 3028 disablepictureinpicture: 'disablePictureInPicture', | |
| 3029 disableremoteplayback: 'disableRemotePlayback', | |
| 3030 download: 'download', | |
| 3031 draggable: 'draggable', | |
| 3032 enctype: 'encType', | |
| 3033 enterkeyhint: 'enterKeyHint', | |
| 3034 for: 'htmlFor', | |
| 3035 form: 'form', | |
| 3036 formmethod: 'formMethod', | |
| 3037 formaction: 'formAction', | |
| 3038 formenctype: 'formEncType', | |
| 3039 formnovalidate: 'formNoValidate', | |
| 3040 formtarget: 'formTarget', | |
| 3041 frameborder: 'frameBorder', | |
| 3042 headers: 'headers', | |
| 3043 height: 'height', | |
| 3044 hidden: 'hidden', | |
| 3045 high: 'high', | |
| 3046 href: 'href', | |
| 3047 hreflang: 'hrefLang', | |
| 3048 htmlfor: 'htmlFor', | |
| 3049 httpequiv: 'httpEquiv', | |
| 3050 'http-equiv': 'httpEquiv', | |
| 3051 icon: 'icon', | |
| 3052 id: 'id', | |
| 3053 imagesizes: 'imageSizes', | |
| 3054 imagesrcset: 'imageSrcSet', | |
| 3055 innerhtml: 'innerHTML', | |
| 3056 inputmode: 'inputMode', | |
| 3057 integrity: 'integrity', | |
| 3058 is: 'is', | |
| 3059 itemid: 'itemID', | |
| 3060 itemprop: 'itemProp', | |
| 3061 itemref: 'itemRef', | |
| 3062 itemscope: 'itemScope', | |
| 3063 itemtype: 'itemType', | |
| 3064 keyparams: 'keyParams', | |
| 3065 keytype: 'keyType', | |
| 3066 kind: 'kind', | |
| 3067 label: 'label', | |
| 3068 lang: 'lang', | |
| 3069 list: 'list', | |
| 3070 loop: 'loop', | |
| 3071 low: 'low', | |
| 3072 manifest: 'manifest', | |
| 3073 marginwidth: 'marginWidth', | |
| 3074 marginheight: 'marginHeight', | |
| 3075 max: 'max', | |
| 3076 maxlength: 'maxLength', | |
| 3077 media: 'media', | |
| 3078 mediagroup: 'mediaGroup', | |
| 3079 method: 'method', | |
| 3080 min: 'min', | |
| 3081 minlength: 'minLength', | |
| 3082 multiple: 'multiple', | |
| 3083 muted: 'muted', | |
| 3084 name: 'name', | |
| 3085 nomodule: 'noModule', | |
| 3086 nonce: 'nonce', | |
| 3087 novalidate: 'noValidate', | |
| 3088 open: 'open', | |
| 3089 optimum: 'optimum', | |
| 3090 pattern: 'pattern', | |
| 3091 placeholder: 'placeholder', | |
| 3092 playsinline: 'playsInline', | |
| 3093 poster: 'poster', | |
| 3094 preload: 'preload', | |
| 3095 profile: 'profile', | |
| 3096 radiogroup: 'radioGroup', | |
| 3097 readonly: 'readOnly', | |
| 3098 referrerpolicy: 'referrerPolicy', | |
| 3099 rel: 'rel', | |
| 3100 required: 'required', | |
| 3101 reversed: 'reversed', | |
| 3102 role: 'role', | |
| 3103 rows: 'rows', | |
| 3104 rowspan: 'rowSpan', | |
| 3105 sandbox: 'sandbox', | |
| 3106 scope: 'scope', | |
| 3107 scoped: 'scoped', | |
| 3108 scrolling: 'scrolling', | |
| 3109 seamless: 'seamless', | |
| 3110 selected: 'selected', | |
| 3111 shape: 'shape', | |
| 3112 size: 'size', | |
| 3113 sizes: 'sizes', | |
| 3114 span: 'span', | |
| 3115 spellcheck: 'spellCheck', | |
| 3116 src: 'src', | |
| 3117 srcdoc: 'srcDoc', | |
| 3118 srclang: 'srcLang', | |
| 3119 srcset: 'srcSet', | |
| 3120 start: 'start', | |
| 3121 step: 'step', | |
| 3122 style: 'style', | |
| 3123 summary: 'summary', | |
| 3124 tabindex: 'tabIndex', | |
| 3125 target: 'target', | |
| 3126 title: 'title', | |
| 3127 type: 'type', | |
| 3128 usemap: 'useMap', | |
| 3129 value: 'value', | |
| 3130 width: 'width', | |
| 3131 wmode: 'wmode', | |
| 3132 wrap: 'wrap', | |
| 3133 // SVG | |
| 3134 about: 'about', | |
| 3135 accentheight: 'accentHeight', | |
| 3136 'accent-height': 'accentHeight', | |
| 3137 accumulate: 'accumulate', | |
| 3138 additive: 'additive', | |
| 3139 alignmentbaseline: 'alignmentBaseline', | |
| 3140 'alignment-baseline': 'alignmentBaseline', | |
| 3141 allowreorder: 'allowReorder', | |
| 3142 alphabetic: 'alphabetic', | |
| 3143 amplitude: 'amplitude', | |
| 3144 arabicform: 'arabicForm', | |
| 3145 'arabic-form': 'arabicForm', | |
| 3146 ascent: 'ascent', | |
| 3147 attributename: 'attributeName', | |
| 3148 attributetype: 'attributeType', | |
| 3149 autoreverse: 'autoReverse', | |
| 3150 azimuth: 'azimuth', | |
| 3151 basefrequency: 'baseFrequency', | |
| 3152 baselineshift: 'baselineShift', | |
| 3153 'baseline-shift': 'baselineShift', | |
| 3154 baseprofile: 'baseProfile', | |
| 3155 bbox: 'bbox', | |
| 3156 begin: 'begin', | |
| 3157 bias: 'bias', | |
| 3158 by: 'by', | |
| 3159 calcmode: 'calcMode', | |
| 3160 capheight: 'capHeight', | |
| 3161 'cap-height': 'capHeight', | |
| 3162 clip: 'clip', | |
| 3163 clippath: 'clipPath', | |
| 3164 'clip-path': 'clipPath', | |
| 3165 clippathunits: 'clipPathUnits', | |
| 3166 cliprule: 'clipRule', | |
| 3167 'clip-rule': 'clipRule', | |
| 3168 color: 'color', | |
| 3169 colorinterpolation: 'colorInterpolation', | |
| 3170 'color-interpolation': 'colorInterpolation', | |
| 3171 colorinterpolationfilters: 'colorInterpolationFilters', | |
| 3172 'color-interpolation-filters': 'colorInterpolationFilters', | |
| 3173 colorprofile: 'colorProfile', | |
| 3174 'color-profile': 'colorProfile', | |
| 3175 colorrendering: 'colorRendering', | |
| 3176 'color-rendering': 'colorRendering', | |
| 3177 contentscripttype: 'contentScriptType', | |
| 3178 contentstyletype: 'contentStyleType', | |
| 3179 cursor: 'cursor', | |
| 3180 cx: 'cx', | |
| 3181 cy: 'cy', | |
| 3182 d: 'd', | |
| 3183 datatype: 'datatype', | |
| 3184 decelerate: 'decelerate', | |
| 3185 descent: 'descent', | |
| 3186 diffuseconstant: 'diffuseConstant', | |
| 3187 direction: 'direction', | |
| 3188 display: 'display', | |
| 3189 divisor: 'divisor', | |
| 3190 dominantbaseline: 'dominantBaseline', | |
| 3191 'dominant-baseline': 'dominantBaseline', | |
| 3192 dur: 'dur', | |
| 3193 dx: 'dx', | |
| 3194 dy: 'dy', | |
| 3195 edgemode: 'edgeMode', | |
| 3196 elevation: 'elevation', | |
| 3197 enablebackground: 'enableBackground', | |
| 3198 'enable-background': 'enableBackground', | |
| 3199 end: 'end', | |
| 3200 exponent: 'exponent', | |
| 3201 externalresourcesrequired: 'externalResourcesRequired', | |
| 3202 fill: 'fill', | |
| 3203 fillopacity: 'fillOpacity', | |
| 3204 'fill-opacity': 'fillOpacity', | |
| 3205 fillrule: 'fillRule', | |
| 3206 'fill-rule': 'fillRule', | |
| 3207 filter: 'filter', | |
| 3208 filterres: 'filterRes', | |
| 3209 filterunits: 'filterUnits', | |
| 3210 floodopacity: 'floodOpacity', | |
| 3211 'flood-opacity': 'floodOpacity', | |
| 3212 floodcolor: 'floodColor', | |
| 3213 'flood-color': 'floodColor', | |
| 3214 focusable: 'focusable', | |
| 3215 fontfamily: 'fontFamily', | |
| 3216 'font-family': 'fontFamily', | |
| 3217 fontsize: 'fontSize', | |
| 3218 'font-size': 'fontSize', | |
| 3219 fontsizeadjust: 'fontSizeAdjust', | |
| 3220 'font-size-adjust': 'fontSizeAdjust', | |
| 3221 fontstretch: 'fontStretch', | |
| 3222 'font-stretch': 'fontStretch', | |
| 3223 fontstyle: 'fontStyle', | |
| 3224 'font-style': 'fontStyle', | |
| 3225 fontvariant: 'fontVariant', | |
| 3226 'font-variant': 'fontVariant', | |
| 3227 fontweight: 'fontWeight', | |
| 3228 'font-weight': 'fontWeight', | |
| 3229 format: 'format', | |
| 3230 from: 'from', | |
| 3231 fx: 'fx', | |
| 3232 fy: 'fy', | |
| 3233 g1: 'g1', | |
| 3234 g2: 'g2', | |
| 3235 glyphname: 'glyphName', | |
| 3236 'glyph-name': 'glyphName', | |
| 3237 glyphorientationhorizontal: 'glyphOrientationHorizontal', | |
| 3238 'glyph-orientation-horizontal': 'glyphOrientationHorizontal', | |
| 3239 glyphorientationvertical: 'glyphOrientationVertical', | |
| 3240 'glyph-orientation-vertical': 'glyphOrientationVertical', | |
| 3241 glyphref: 'glyphRef', | |
| 3242 gradienttransform: 'gradientTransform', | |
| 3243 gradientunits: 'gradientUnits', | |
| 3244 hanging: 'hanging', | |
| 3245 horizadvx: 'horizAdvX', | |
| 3246 'horiz-adv-x': 'horizAdvX', | |
| 3247 horizoriginx: 'horizOriginX', | |
| 3248 'horiz-origin-x': 'horizOriginX', | |
| 3249 ideographic: 'ideographic', | |
| 3250 imagerendering: 'imageRendering', | |
| 3251 'image-rendering': 'imageRendering', | |
| 3252 in2: 'in2', | |
| 3253 in: 'in', | |
| 3254 inlist: 'inlist', | |
| 3255 intercept: 'intercept', | |
| 3256 k1: 'k1', | |
| 3257 k2: 'k2', | |
| 3258 k3: 'k3', | |
| 3259 k4: 'k4', | |
| 3260 k: 'k', | |
| 3261 kernelmatrix: 'kernelMatrix', | |
| 3262 kernelunitlength: 'kernelUnitLength', | |
| 3263 kerning: 'kerning', | |
| 3264 keypoints: 'keyPoints', | |
| 3265 keysplines: 'keySplines', | |
| 3266 keytimes: 'keyTimes', | |
| 3267 lengthadjust: 'lengthAdjust', | |
| 3268 letterspacing: 'letterSpacing', | |
| 3269 'letter-spacing': 'letterSpacing', | |
| 3270 lightingcolor: 'lightingColor', | |
| 3271 'lighting-color': 'lightingColor', | |
| 3272 limitingconeangle: 'limitingConeAngle', | |
| 3273 local: 'local', | |
| 3274 markerend: 'markerEnd', | |
| 3275 'marker-end': 'markerEnd', | |
| 3276 markerheight: 'markerHeight', | |
| 3277 markermid: 'markerMid', | |
| 3278 'marker-mid': 'markerMid', | |
| 3279 markerstart: 'markerStart', | |
| 3280 'marker-start': 'markerStart', | |
| 3281 markerunits: 'markerUnits', | |
| 3282 markerwidth: 'markerWidth', | |
| 3283 mask: 'mask', | |
| 3284 maskcontentunits: 'maskContentUnits', | |
| 3285 maskunits: 'maskUnits', | |
| 3286 mathematical: 'mathematical', | |
| 3287 mode: 'mode', | |
| 3288 numoctaves: 'numOctaves', | |
| 3289 offset: 'offset', | |
| 3290 opacity: 'opacity', | |
| 3291 operator: 'operator', | |
| 3292 order: 'order', | |
| 3293 orient: 'orient', | |
| 3294 orientation: 'orientation', | |
| 3295 origin: 'origin', | |
| 3296 overflow: 'overflow', | |
| 3297 overlineposition: 'overlinePosition', | |
| 3298 'overline-position': 'overlinePosition', | |
| 3299 overlinethickness: 'overlineThickness', | |
| 3300 'overline-thickness': 'overlineThickness', | |
| 3301 paintorder: 'paintOrder', | |
| 3302 'paint-order': 'paintOrder', | |
| 3303 panose1: 'panose1', | |
| 3304 'panose-1': 'panose1', | |
| 3305 pathlength: 'pathLength', | |
| 3306 patterncontentunits: 'patternContentUnits', | |
| 3307 patterntransform: 'patternTransform', | |
| 3308 patternunits: 'patternUnits', | |
| 3309 pointerevents: 'pointerEvents', | |
| 3310 'pointer-events': 'pointerEvents', | |
| 3311 points: 'points', | |
| 3312 pointsatx: 'pointsAtX', | |
| 3313 pointsaty: 'pointsAtY', | |
| 3314 pointsatz: 'pointsAtZ', | |
| 3315 prefix: 'prefix', | |
| 3316 preservealpha: 'preserveAlpha', | |
| 3317 preserveaspectratio: 'preserveAspectRatio', | |
| 3318 primitiveunits: 'primitiveUnits', | |
| 3319 property: 'property', | |
| 3320 r: 'r', | |
| 3321 radius: 'radius', | |
| 3322 refx: 'refX', | |
| 3323 refy: 'refY', | |
| 3324 renderingintent: 'renderingIntent', | |
| 3325 'rendering-intent': 'renderingIntent', | |
| 3326 repeatcount: 'repeatCount', | |
| 3327 repeatdur: 'repeatDur', | |
| 3328 requiredextensions: 'requiredExtensions', | |
| 3329 requiredfeatures: 'requiredFeatures', | |
| 3330 resource: 'resource', | |
| 3331 restart: 'restart', | |
| 3332 result: 'result', | |
| 3333 results: 'results', | |
| 3334 rotate: 'rotate', | |
| 3335 rx: 'rx', | |
| 3336 ry: 'ry', | |
| 3337 scale: 'scale', | |
| 3338 security: 'security', | |
| 3339 seed: 'seed', | |
| 3340 shaperendering: 'shapeRendering', | |
| 3341 'shape-rendering': 'shapeRendering', | |
| 3342 slope: 'slope', | |
| 3343 spacing: 'spacing', | |
| 3344 specularconstant: 'specularConstant', | |
| 3345 specularexponent: 'specularExponent', | |
| 3346 speed: 'speed', | |
| 3347 spreadmethod: 'spreadMethod', | |
| 3348 startoffset: 'startOffset', | |
| 3349 stddeviation: 'stdDeviation', | |
| 3350 stemh: 'stemh', | |
| 3351 stemv: 'stemv', | |
| 3352 stitchtiles: 'stitchTiles', | |
| 3353 stopcolor: 'stopColor', | |
| 3354 'stop-color': 'stopColor', | |
| 3355 stopopacity: 'stopOpacity', | |
| 3356 'stop-opacity': 'stopOpacity', | |
| 3357 strikethroughposition: 'strikethroughPosition', | |
| 3358 'strikethrough-position': 'strikethroughPosition', | |
| 3359 strikethroughthickness: 'strikethroughThickness', | |
| 3360 'strikethrough-thickness': 'strikethroughThickness', | |
| 3361 string: 'string', | |
| 3362 stroke: 'stroke', | |
| 3363 strokedasharray: 'strokeDasharray', | |
| 3364 'stroke-dasharray': 'strokeDasharray', | |
| 3365 strokedashoffset: 'strokeDashoffset', | |
| 3366 'stroke-dashoffset': 'strokeDashoffset', | |
| 3367 strokelinecap: 'strokeLinecap', | |
| 3368 'stroke-linecap': 'strokeLinecap', | |
| 3369 strokelinejoin: 'strokeLinejoin', | |
| 3370 'stroke-linejoin': 'strokeLinejoin', | |
| 3371 strokemiterlimit: 'strokeMiterlimit', | |
| 3372 'stroke-miterlimit': 'strokeMiterlimit', | |
| 3373 strokewidth: 'strokeWidth', | |
| 3374 'stroke-width': 'strokeWidth', | |
| 3375 strokeopacity: 'strokeOpacity', | |
| 3376 'stroke-opacity': 'strokeOpacity', | |
| 3377 suppresscontenteditablewarning: 'suppressContentEditableWarning', | |
| 3378 suppresshydrationwarning: 'suppressHydrationWarning', | |
| 3379 surfacescale: 'surfaceScale', | |
| 3380 systemlanguage: 'systemLanguage', | |
| 3381 tablevalues: 'tableValues', | |
| 3382 targetx: 'targetX', | |
| 3383 targety: 'targetY', | |
| 3384 textanchor: 'textAnchor', | |
| 3385 'text-anchor': 'textAnchor', | |
| 3386 textdecoration: 'textDecoration', | |
| 3387 'text-decoration': 'textDecoration', | |
| 3388 textlength: 'textLength', | |
| 3389 textrendering: 'textRendering', | |
| 3390 'text-rendering': 'textRendering', | |
| 3391 to: 'to', | |
| 3392 transform: 'transform', | |
| 3393 typeof: 'typeof', | |
| 3394 u1: 'u1', | |
| 3395 u2: 'u2', | |
| 3396 underlineposition: 'underlinePosition', | |
| 3397 'underline-position': 'underlinePosition', | |
| 3398 underlinethickness: 'underlineThickness', | |
| 3399 'underline-thickness': 'underlineThickness', | |
| 3400 unicode: 'unicode', | |
| 3401 unicodebidi: 'unicodeBidi', | |
| 3402 'unicode-bidi': 'unicodeBidi', | |
| 3403 unicoderange: 'unicodeRange', | |
| 3404 'unicode-range': 'unicodeRange', | |
| 3405 unitsperem: 'unitsPerEm', | |
| 3406 'units-per-em': 'unitsPerEm', | |
| 3407 unselectable: 'unselectable', | |
| 3408 valphabetic: 'vAlphabetic', | |
| 3409 'v-alphabetic': 'vAlphabetic', | |
| 3410 values: 'values', | |
| 3411 vectoreffect: 'vectorEffect', | |
| 3412 'vector-effect': 'vectorEffect', | |
| 3413 version: 'version', | |
| 3414 vertadvy: 'vertAdvY', | |
| 3415 'vert-adv-y': 'vertAdvY', | |
| 3416 vertoriginx: 'vertOriginX', | |
| 3417 'vert-origin-x': 'vertOriginX', | |
| 3418 vertoriginy: 'vertOriginY', | |
| 3419 'vert-origin-y': 'vertOriginY', | |
| 3420 vhanging: 'vHanging', | |
| 3421 'v-hanging': 'vHanging', | |
| 3422 videographic: 'vIdeographic', | |
| 3423 'v-ideographic': 'vIdeographic', | |
| 3424 viewbox: 'viewBox', | |
| 3425 viewtarget: 'viewTarget', | |
| 3426 visibility: 'visibility', | |
| 3427 vmathematical: 'vMathematical', | |
| 3428 'v-mathematical': 'vMathematical', | |
| 3429 vocab: 'vocab', | |
| 3430 widths: 'widths', | |
| 3431 wordspacing: 'wordSpacing', | |
| 3432 'word-spacing': 'wordSpacing', | |
| 3433 writingmode: 'writingMode', | |
| 3434 'writing-mode': 'writingMode', | |
| 3435 x1: 'x1', | |
| 3436 x2: 'x2', | |
| 3437 x: 'x', | |
| 3438 xchannelselector: 'xChannelSelector', | |
| 3439 xheight: 'xHeight', | |
| 3440 'x-height': 'xHeight', | |
| 3441 xlinkactuate: 'xlinkActuate', | |
| 3442 'xlink:actuate': 'xlinkActuate', | |
| 3443 xlinkarcrole: 'xlinkArcrole', | |
| 3444 'xlink:arcrole': 'xlinkArcrole', | |
| 3445 xlinkhref: 'xlinkHref', | |
| 3446 'xlink:href': 'xlinkHref', | |
| 3447 xlinkrole: 'xlinkRole', | |
| 3448 'xlink:role': 'xlinkRole', | |
| 3449 xlinkshow: 'xlinkShow', | |
| 3450 'xlink:show': 'xlinkShow', | |
| 3451 xlinktitle: 'xlinkTitle', | |
| 3452 'xlink:title': 'xlinkTitle', | |
| 3453 xlinktype: 'xlinkType', | |
| 3454 'xlink:type': 'xlinkType', | |
| 3455 xmlbase: 'xmlBase', | |
| 3456 'xml:base': 'xmlBase', | |
| 3457 xmllang: 'xmlLang', | |
| 3458 'xml:lang': 'xmlLang', | |
| 3459 xmlns: 'xmlns', | |
| 3460 'xml:space': 'xmlSpace', | |
| 3461 xmlnsxlink: 'xmlnsXlink', | |
| 3462 'xmlns:xlink': 'xmlnsXlink', | |
| 3463 xmlspace: 'xmlSpace', | |
| 3464 y1: 'y1', | |
| 3465 y2: 'y2', | |
| 3466 y: 'y', | |
| 3467 ychannelselector: 'yChannelSelector', | |
| 3468 z: 'z', | |
| 3469 zoomandpan: 'zoomAndPan' | |
| 3470 }; | |
| 3471 | |
| 3472 var ariaProperties = { | |
| 3473 'aria-current': 0, | |
| 3474 // state | |
| 3475 'aria-description': 0, | |
| 3476 'aria-details': 0, | |
| 3477 'aria-disabled': 0, | |
| 3478 // state | |
| 3479 'aria-hidden': 0, | |
| 3480 // state | |
| 3481 'aria-invalid': 0, | |
| 3482 // state | |
| 3483 'aria-keyshortcuts': 0, | |
| 3484 'aria-label': 0, | |
| 3485 'aria-roledescription': 0, | |
| 3486 // Widget Attributes | |
| 3487 'aria-autocomplete': 0, | |
| 3488 'aria-checked': 0, | |
| 3489 'aria-expanded': 0, | |
| 3490 'aria-haspopup': 0, | |
| 3491 'aria-level': 0, | |
| 3492 'aria-modal': 0, | |
| 3493 'aria-multiline': 0, | |
| 3494 'aria-multiselectable': 0, | |
| 3495 'aria-orientation': 0, | |
| 3496 'aria-placeholder': 0, | |
| 3497 'aria-pressed': 0, | |
| 3498 'aria-readonly': 0, | |
| 3499 'aria-required': 0, | |
| 3500 'aria-selected': 0, | |
| 3501 'aria-sort': 0, | |
| 3502 'aria-valuemax': 0, | |
| 3503 'aria-valuemin': 0, | |
| 3504 'aria-valuenow': 0, | |
| 3505 'aria-valuetext': 0, | |
| 3506 // Live Region Attributes | |
| 3507 'aria-atomic': 0, | |
| 3508 'aria-busy': 0, | |
| 3509 'aria-live': 0, | |
| 3510 'aria-relevant': 0, | |
| 3511 // Drag-and-Drop Attributes | |
| 3512 'aria-dropeffect': 0, | |
| 3513 'aria-grabbed': 0, | |
| 3514 // Relationship Attributes | |
| 3515 'aria-activedescendant': 0, | |
| 3516 'aria-colcount': 0, | |
| 3517 'aria-colindex': 0, | |
| 3518 'aria-colspan': 0, | |
| 3519 'aria-controls': 0, | |
| 3520 'aria-describedby': 0, | |
| 3521 'aria-errormessage': 0, | |
| 3522 'aria-flowto': 0, | |
| 3523 'aria-labelledby': 0, | |
| 3524 'aria-owns': 0, | |
| 3525 'aria-posinset': 0, | |
| 3526 'aria-rowcount': 0, | |
| 3527 'aria-rowindex': 0, | |
| 3528 'aria-rowspan': 0, | |
| 3529 'aria-setsize': 0 | |
| 3530 }; | |
| 3531 | |
| 3532 var warnedProperties = {}; | |
| 3533 var rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$'); | |
| 3534 var rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$'); | |
| 3535 | |
| 3536 function validateProperty(tagName, name) { | |
| 3537 { | |
| 3538 if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) { | |
| 3539 return true; | |
| 3540 } | |
| 3541 | |
| 3542 if (rARIACamel.test(name)) { | |
| 3543 var ariaName = 'aria-' + name.slice(4).toLowerCase(); | |
| 3544 var correctName = ariaProperties.hasOwnProperty(ariaName) ? ariaName : null; // If this is an aria-* attribute, but is not listed in the known DOM | |
| 3545 // DOM properties, then it is an invalid aria-* attribute. | |
| 3546 | |
| 3547 if (correctName == null) { | |
| 3548 error('Invalid ARIA attribute `%s`. ARIA attributes follow the pattern aria-* and must be lowercase.', name); | |
| 3549 | |
| 3550 warnedProperties[name] = true; | |
| 3551 return true; | |
| 3552 } // aria-* attributes should be lowercase; suggest the lowercase version. | |
| 3553 | |
| 3554 | |
| 3555 if (name !== correctName) { | |
| 3556 error('Invalid ARIA attribute `%s`. Did you mean `%s`?', name, correctName); | |
| 3557 | |
| 3558 warnedProperties[name] = true; | |
| 3559 return true; | |
| 3560 } | |
| 3561 } | |
| 3562 | |
| 3563 if (rARIA.test(name)) { | |
| 3564 var lowerCasedName = name.toLowerCase(); | |
| 3565 var standardName = ariaProperties.hasOwnProperty(lowerCasedName) ? lowerCasedName : null; // If this is an aria-* attribute, but is not listed in the known DOM | |
| 3566 // DOM properties, then it is an invalid aria-* attribute. | |
| 3567 | |
| 3568 if (standardName == null) { | |
| 3569 warnedProperties[name] = true; | |
| 3570 return false; | |
| 3571 } // aria-* attributes should be lowercase; suggest the lowercase version. | |
| 3572 | |
| 3573 | |
| 3574 if (name !== standardName) { | |
| 3575 error('Unknown ARIA attribute `%s`. Did you mean `%s`?', name, standardName); | |
| 3576 | |
| 3577 warnedProperties[name] = true; | |
| 3578 return true; | |
| 3579 } | |
| 3580 } | |
| 3581 } | |
| 3582 | |
| 3583 return true; | |
| 3584 } | |
| 3585 | |
| 3586 function warnInvalidARIAProps(type, props) { | |
| 3587 { | |
| 3588 var invalidProps = []; | |
| 3589 | |
| 3590 for (var key in props) { | |
| 3591 var isValid = validateProperty(type, key); | |
| 3592 | |
| 3593 if (!isValid) { | |
| 3594 invalidProps.push(key); | |
| 3595 } | |
| 3596 } | |
| 3597 | |
| 3598 var unknownPropString = invalidProps.map(function (prop) { | |
| 3599 return '`' + prop + '`'; | |
| 3600 }).join(', '); | |
| 3601 | |
| 3602 if (invalidProps.length === 1) { | |
| 3603 error('Invalid aria prop %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); | |
| 3604 } else if (invalidProps.length > 1) { | |
| 3605 error('Invalid aria props %s on <%s> tag. ' + 'For details, see https://reactjs.org/link/invalid-aria-props', unknownPropString, type); | |
| 3606 } | |
| 3607 } | |
| 3608 } | |
| 3609 | |
| 3610 function validateProperties(type, props) { | |
| 3611 if (isCustomComponent(type, props)) { | |
| 3612 return; | |
| 3613 } | |
| 3614 | |
| 3615 warnInvalidARIAProps(type, props); | |
| 3616 } | |
| 3617 | |
| 3618 var didWarnValueNull = false; | |
| 3619 function validateProperties$1(type, props) { | |
| 3620 { | |
| 3621 if (type !== 'input' && type !== 'textarea' && type !== 'select') { | |
| 3622 return; | |
| 3623 } | |
| 3624 | |
| 3625 if (props != null && props.value === null && !didWarnValueNull) { | |
| 3626 didWarnValueNull = true; | |
| 3627 | |
| 3628 if (type === 'select' && props.multiple) { | |
| 3629 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); | |
| 3630 } else { | |
| 3631 error('`value` prop on `%s` should not be null. ' + 'Consider using an empty string to clear the component or `undefined` ' + 'for uncontrolled components.', type); | |
| 3632 } | |
| 3633 } | |
| 3634 } | |
| 3635 } | |
| 3636 | |
| 3637 var validateProperty$1 = function () {}; | |
| 3638 | |
| 3639 { | |
| 3640 var warnedProperties$1 = {}; | |
| 3641 var EVENT_NAME_REGEX = /^on./; | |
| 3642 var INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/; | |
| 3643 var rARIA$1 = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$'); | |
| 3644 var rARIACamel$1 = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$'); | |
| 3645 | |
| 3646 validateProperty$1 = function (tagName, name, value, eventRegistry) { | |
| 3647 if (hasOwnProperty.call(warnedProperties$1, name) && warnedProperties$1[name]) { | |
| 3648 return true; | |
| 3649 } | |
| 3650 | |
| 3651 var lowerCasedName = name.toLowerCase(); | |
| 3652 | |
| 3653 if (lowerCasedName === 'onfocusin' || lowerCasedName === 'onfocusout') { | |
| 3654 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.'); | |
| 3655 | |
| 3656 warnedProperties$1[name] = true; | |
| 3657 return true; | |
| 3658 } // We can't rely on the event system being injected on the server. | |
| 3659 | |
| 3660 | |
| 3661 if (eventRegistry != null) { | |
| 3662 var registrationNameDependencies = eventRegistry.registrationNameDependencies, | |
| 3663 possibleRegistrationNames = eventRegistry.possibleRegistrationNames; | |
| 3664 | |
| 3665 if (registrationNameDependencies.hasOwnProperty(name)) { | |
| 3666 return true; | |
| 3667 } | |
| 3668 | |
| 3669 var registrationName = possibleRegistrationNames.hasOwnProperty(lowerCasedName) ? possibleRegistrationNames[lowerCasedName] : null; | |
| 3670 | |
| 3671 if (registrationName != null) { | |
| 3672 error('Invalid event handler property `%s`. Did you mean `%s`?', name, registrationName); | |
| 3673 | |
| 3674 warnedProperties$1[name] = true; | |
| 3675 return true; | |
| 3676 } | |
| 3677 | |
| 3678 if (EVENT_NAME_REGEX.test(name)) { | |
| 3679 error('Unknown event handler property `%s`. It will be ignored.', name); | |
| 3680 | |
| 3681 warnedProperties$1[name] = true; | |
| 3682 return true; | |
| 3683 } | |
| 3684 } else if (EVENT_NAME_REGEX.test(name)) { | |
| 3685 // If no event plugins have been injected, we are in a server environment. | |
| 3686 // So we can't tell if the event name is correct for sure, but we can filter | |
| 3687 // out known bad ones like `onclick`. We can't suggest a specific replacement though. | |
| 3688 if (INVALID_EVENT_NAME_REGEX.test(name)) { | |
| 3689 error('Invalid event handler property `%s`. ' + 'React events use the camelCase naming convention, for example `onClick`.', name); | |
| 3690 } | |
| 3691 | |
| 3692 warnedProperties$1[name] = true; | |
| 3693 return true; | |
| 3694 } // Let the ARIA attribute hook validate ARIA attributes | |
| 3695 | |
| 3696 | |
| 3697 if (rARIA$1.test(name) || rARIACamel$1.test(name)) { | |
| 3698 return true; | |
| 3699 } | |
| 3700 | |
| 3701 if (lowerCasedName === 'innerhtml') { | |
| 3702 error('Directly setting property `innerHTML` is not permitted. ' + 'For more information, lookup documentation on `dangerouslySetInnerHTML`.'); | |
| 3703 | |
| 3704 warnedProperties$1[name] = true; | |
| 3705 return true; | |
| 3706 } | |
| 3707 | |
| 3708 if (lowerCasedName === 'aria') { | |
| 3709 error('The `aria` attribute is reserved for future use in React. ' + 'Pass individual `aria-` attributes instead.'); | |
| 3710 | |
| 3711 warnedProperties$1[name] = true; | |
| 3712 return true; | |
| 3713 } | |
| 3714 | |
| 3715 if (lowerCasedName === 'is' && value !== null && value !== undefined && typeof value !== 'string') { | |
| 3716 error('Received a `%s` for a string attribute `is`. If this is expected, cast ' + 'the value to a string.', typeof value); | |
| 3717 | |
| 3718 warnedProperties$1[name] = true; | |
| 3719 return true; | |
| 3720 } | |
| 3721 | |
| 3722 if (typeof value === 'number' && isNaN(value)) { | |
| 3723 error('Received NaN for the `%s` attribute. If this is expected, cast ' + 'the value to a string.', name); | |
| 3724 | |
| 3725 warnedProperties$1[name] = true; | |
| 3726 return true; | |
| 3727 } | |
| 3728 | |
| 3729 var propertyInfo = getPropertyInfo(name); | |
| 3730 var isReserved = propertyInfo !== null && propertyInfo.type === RESERVED; // Known attributes should match the casing specified in the property config. | |
| 3731 | |
| 3732 if (possibleStandardNames.hasOwnProperty(lowerCasedName)) { | |
| 3733 var standardName = possibleStandardNames[lowerCasedName]; | |
| 3734 | |
| 3735 if (standardName !== name) { | |
| 3736 error('Invalid DOM property `%s`. Did you mean `%s`?', name, standardName); | |
| 3737 | |
| 3738 warnedProperties$1[name] = true; | |
| 3739 return true; | |
| 3740 } | |
| 3741 } else if (!isReserved && name !== lowerCasedName) { | |
| 3742 // Unknown attributes should have lowercase casing since that's how they | |
| 3743 // will be cased anyway with server rendering. | |
| 3744 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); | |
| 3745 | |
| 3746 warnedProperties$1[name] = true; | |
| 3747 return true; | |
| 3748 } | |
| 3749 | |
| 3750 if (typeof value === 'boolean' && shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) { | |
| 3751 if (value) { | |
| 3752 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); | |
| 3753 } else { | |
| 3754 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); | |
| 3755 } | |
| 3756 | |
| 3757 warnedProperties$1[name] = true; | |
| 3758 return true; | |
| 3759 } // Now that we've validated casing, do not validate | |
| 3760 // data types for reserved props | |
| 3761 | |
| 3762 | |
| 3763 if (isReserved) { | |
| 3764 return true; | |
| 3765 } // Warn when a known attribute is a bad type | |
| 3766 | |
| 3767 | |
| 3768 if (shouldRemoveAttributeWithWarning(name, value, propertyInfo, false)) { | |
| 3769 warnedProperties$1[name] = true; | |
| 3770 return false; | |
| 3771 } // Warn when passing the strings 'false' or 'true' into a boolean prop | |
| 3772 | |
| 3773 | |
| 3774 if ((value === 'false' || value === 'true') && propertyInfo !== null && propertyInfo.type === BOOLEAN) { | |
| 3775 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); | |
| 3776 | |
| 3777 warnedProperties$1[name] = true; | |
| 3778 return true; | |
| 3779 } | |
| 3780 | |
| 3781 return true; | |
| 3782 }; | |
| 3783 } | |
| 3784 | |
| 3785 var warnUnknownProperties = function (type, props, eventRegistry) { | |
| 3786 { | |
| 3787 var unknownProps = []; | |
| 3788 | |
| 3789 for (var key in props) { | |
| 3790 var isValid = validateProperty$1(type, key, props[key], eventRegistry); | |
| 3791 | |
| 3792 if (!isValid) { | |
| 3793 unknownProps.push(key); | |
| 3794 } | |
| 3795 } | |
| 3796 | |
| 3797 var unknownPropString = unknownProps.map(function (prop) { | |
| 3798 return '`' + prop + '`'; | |
| 3799 }).join(', '); | |
| 3800 | |
| 3801 if (unknownProps.length === 1) { | |
| 3802 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); | |
| 3803 } else if (unknownProps.length > 1) { | |
| 3804 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); | |
| 3805 } | |
| 3806 } | |
| 3807 }; | |
| 3808 | |
| 3809 function validateProperties$2(type, props, eventRegistry) { | |
| 3810 if (isCustomComponent(type, props)) { | |
| 3811 return; | |
| 3812 } | |
| 3813 | |
| 3814 warnUnknownProperties(type, props, eventRegistry); | |
| 3815 } | |
| 3816 | |
| 3817 var IS_EVENT_HANDLE_NON_MANAGED_NODE = 1; | |
| 3818 var IS_NON_DELEGATED = 1 << 1; | |
| 3819 var IS_CAPTURE_PHASE = 1 << 2; | |
| 3820 // set to LEGACY_FB_SUPPORT. LEGACY_FB_SUPPORT only gets set when | |
| 3821 // we call willDeferLaterForLegacyFBSupport, thus not bailing out | |
| 3822 // will result in endless cycles like an infinite loop. | |
| 3823 // We also don't want to defer during event replaying. | |
| 3824 | |
| 3825 var SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS = IS_EVENT_HANDLE_NON_MANAGED_NODE | IS_NON_DELEGATED | IS_CAPTURE_PHASE; | |
| 3826 | |
| 3827 // This exists to avoid circular dependency between ReactDOMEventReplaying | |
| 3828 // and DOMPluginEventSystem. | |
| 3829 var currentReplayingEvent = null; | |
| 3830 function setReplayingEvent(event) { | |
| 3831 { | |
| 3832 if (currentReplayingEvent !== null) { | |
| 3833 error('Expected currently replaying event to be null. This error ' + 'is likely caused by a bug in React. Please file an issue.'); | |
| 3834 } | |
| 3835 } | |
| 3836 | |
| 3837 currentReplayingEvent = event; | |
| 3838 } | |
| 3839 function resetReplayingEvent() { | |
| 3840 { | |
| 3841 if (currentReplayingEvent === null) { | |
| 3842 error('Expected currently replaying event to not be null. This error ' + 'is likely caused by a bug in React. Please file an issue.'); | |
| 3843 } | |
| 3844 } | |
| 3845 | |
| 3846 currentReplayingEvent = null; | |
| 3847 } | |
| 3848 function isReplayingEvent(event) { | |
| 3849 return event === currentReplayingEvent; | |
| 3850 } | |
| 3851 | |
| 3852 /** | |
| 3853 * Gets the target node from a native browser event by accounting for | |
| 3854 * inconsistencies in browser DOM APIs. | |
| 3855 * | |
| 3856 * @param {object} nativeEvent Native browser event. | |
| 3857 * @return {DOMEventTarget} Target node. | |
| 3858 */ | |
| 3859 | |
| 3860 function getEventTarget(nativeEvent) { | |
| 3861 // Fallback to nativeEvent.srcElement for IE9 | |
| 3862 // https://github.com/facebook/react/issues/12506 | |
| 3863 var target = nativeEvent.target || nativeEvent.srcElement || window; // Normalize SVG <use> element events #4963 | |
| 3864 | |
| 3865 if (target.correspondingUseElement) { | |
| 3866 target = target.correspondingUseElement; | |
| 3867 } // Safari may fire events on text nodes (Node.TEXT_NODE is 3). | |
| 3868 // @see http://www.quirksmode.org/js/events_properties.html | |
| 3869 | |
| 3870 | |
| 3871 return target.nodeType === TEXT_NODE ? target.parentNode : target; | |
| 3872 } | |
| 3873 | |
| 3874 var restoreImpl = null; | |
| 3875 var restoreTarget = null; | |
| 3876 var restoreQueue = null; | |
| 3877 | |
| 3878 function restoreStateOfTarget(target) { | |
| 3879 // We perform this translation at the end of the event loop so that we | |
| 3880 // always receive the correct fiber here | |
| 3881 var internalInstance = getInstanceFromNode(target); | |
| 3882 | |
| 3883 if (!internalInstance) { | |
| 3884 // Unmounted | |
| 3885 return; | |
| 3886 } | |
| 3887 | |
| 3888 if (typeof restoreImpl !== 'function') { | |
| 3889 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.'); | |
| 3890 } | |
| 3891 | |
| 3892 var stateNode = internalInstance.stateNode; // Guard against Fiber being unmounted. | |
| 3893 | |
| 3894 if (stateNode) { | |
| 3895 var _props = getFiberCurrentPropsFromNode(stateNode); | |
| 3896 | |
| 3897 restoreImpl(internalInstance.stateNode, internalInstance.type, _props); | |
| 3898 } | |
| 3899 } | |
| 3900 | |
| 3901 function setRestoreImplementation(impl) { | |
| 3902 restoreImpl = impl; | |
| 3903 } | |
| 3904 function enqueueStateRestore(target) { | |
| 3905 if (restoreTarget) { | |
| 3906 if (restoreQueue) { | |
| 3907 restoreQueue.push(target); | |
| 3908 } else { | |
| 3909 restoreQueue = [target]; | |
| 3910 } | |
| 3911 } else { | |
| 3912 restoreTarget = target; | |
| 3913 } | |
| 3914 } | |
| 3915 function needsStateRestore() { | |
| 3916 return restoreTarget !== null || restoreQueue !== null; | |
| 3917 } | |
| 3918 function restoreStateIfNeeded() { | |
| 3919 if (!restoreTarget) { | |
| 3920 return; | |
| 3921 } | |
| 3922 | |
| 3923 var target = restoreTarget; | |
| 3924 var queuedTargets = restoreQueue; | |
| 3925 restoreTarget = null; | |
| 3926 restoreQueue = null; | |
| 3927 restoreStateOfTarget(target); | |
| 3928 | |
| 3929 if (queuedTargets) { | |
| 3930 for (var i = 0; i < queuedTargets.length; i++) { | |
| 3931 restoreStateOfTarget(queuedTargets[i]); | |
| 3932 } | |
| 3933 } | |
| 3934 } | |
| 3935 | |
| 3936 // the renderer. Such as when we're dispatching events or if third party | |
| 3937 // libraries need to call batchedUpdates. Eventually, this API will go away when | |
| 3938 // everything is batched by default. We'll then have a similar API to opt-out of | |
| 3939 // scheduled work and instead do synchronous work. | |
| 3940 // Defaults | |
| 3941 | |
| 3942 var batchedUpdatesImpl = function (fn, bookkeeping) { | |
| 3943 return fn(bookkeeping); | |
| 3944 }; | |
| 3945 | |
| 3946 var flushSyncImpl = function () {}; | |
| 3947 | |
| 3948 var isInsideEventHandler = false; | |
| 3949 | |
| 3950 function finishEventHandler() { | |
| 3951 // Here we wait until all updates have propagated, which is important | |
| 3952 // when using controlled components within layers: | |
| 3953 // https://github.com/facebook/react/issues/1698 | |
| 3954 // Then we restore state of any controlled component. | |
| 3955 var controlledComponentsHavePendingUpdates = needsStateRestore(); | |
| 3956 | |
| 3957 if (controlledComponentsHavePendingUpdates) { | |
| 3958 // If a controlled event was fired, we may need to restore the state of | |
| 3959 // the DOM node back to the controlled value. This is necessary when React | |
| 3960 // bails out of the update without touching the DOM. | |
| 3961 // TODO: Restore state in the microtask, after the discrete updates flush, | |
| 3962 // instead of early flushing them here. | |
| 3963 flushSyncImpl(); | |
| 3964 restoreStateIfNeeded(); | |
| 3965 } | |
| 3966 } | |
| 3967 | |
| 3968 function batchedUpdates(fn, a, b) { | |
| 3969 if (isInsideEventHandler) { | |
| 3970 // If we are currently inside another batch, we need to wait until it | |
| 3971 // fully completes before restoring state. | |
| 3972 return fn(a, b); | |
| 3973 } | |
| 3974 | |
| 3975 isInsideEventHandler = true; | |
| 3976 | |
| 3977 try { | |
| 3978 return batchedUpdatesImpl(fn, a, b); | |
| 3979 } finally { | |
| 3980 isInsideEventHandler = false; | |
| 3981 finishEventHandler(); | |
| 3982 } | |
| 3983 } // TODO: Replace with flushSync | |
| 3984 function setBatchingImplementation(_batchedUpdatesImpl, _discreteUpdatesImpl, _flushSyncImpl) { | |
| 3985 batchedUpdatesImpl = _batchedUpdatesImpl; | |
| 3986 flushSyncImpl = _flushSyncImpl; | |
| 3987 } | |
| 3988 | |
| 3989 function isInteractive(tag) { | |
| 3990 return tag === 'button' || tag === 'input' || tag === 'select' || tag === 'textarea'; | |
| 3991 } | |
| 3992 | |
| 3993 function shouldPreventMouseEvent(name, type, props) { | |
| 3994 switch (name) { | |
| 3995 case 'onClick': | |
| 3996 case 'onClickCapture': | |
| 3997 case 'onDoubleClick': | |
| 3998 case 'onDoubleClickCapture': | |
| 3999 case 'onMouseDown': | |
| 4000 case 'onMouseDownCapture': | |
| 4001 case 'onMouseMove': | |
| 4002 case 'onMouseMoveCapture': | |
| 4003 case 'onMouseUp': | |
| 4004 case 'onMouseUpCapture': | |
| 4005 case 'onMouseEnter': | |
| 4006 return !!(props.disabled && isInteractive(type)); | |
| 4007 | |
| 4008 default: | |
| 4009 return false; | |
| 4010 } | |
| 4011 } | |
| 4012 /** | |
| 4013 * @param {object} inst The instance, which is the source of events. | |
| 4014 * @param {string} registrationName Name of listener (e.g. `onClick`). | |
| 4015 * @return {?function} The stored callback. | |
| 4016 */ | |
| 4017 | |
| 4018 | |
| 4019 function getListener(inst, registrationName) { | |
| 4020 var stateNode = inst.stateNode; | |
| 4021 | |
| 4022 if (stateNode === null) { | |
| 4023 // Work in progress (ex: onload events in incremental mode). | |
| 4024 return null; | |
| 4025 } | |
| 4026 | |
| 4027 var props = getFiberCurrentPropsFromNode(stateNode); | |
| 4028 | |
| 4029 if (props === null) { | |
| 4030 // Work in progress. | |
| 4031 return null; | |
| 4032 } | |
| 4033 | |
| 4034 var listener = props[registrationName]; | |
| 4035 | |
| 4036 if (shouldPreventMouseEvent(registrationName, inst.type, props)) { | |
| 4037 return null; | |
| 4038 } | |
| 4039 | |
| 4040 if (listener && typeof listener !== 'function') { | |
| 4041 throw new Error("Expected `" + registrationName + "` listener to be a function, instead got a value of `" + typeof listener + "` type."); | |
| 4042 } | |
| 4043 | |
| 4044 return listener; | |
| 4045 } | |
| 4046 | |
| 4047 var passiveBrowserEventsSupported = false; // Check if browser support events with passive listeners | |
| 4048 // https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Safely_detecting_option_support | |
| 4049 | |
| 4050 if (canUseDOM) { | |
| 4051 try { | |
| 4052 var options = {}; // $FlowFixMe: Ignore Flow complaining about needing a value | |
| 4053 | |
| 4054 Object.defineProperty(options, 'passive', { | |
| 4055 get: function () { | |
| 4056 passiveBrowserEventsSupported = true; | |
| 4057 } | |
| 4058 }); | |
| 4059 window.addEventListener('test', options, options); | |
| 4060 window.removeEventListener('test', options, options); | |
| 4061 } catch (e) { | |
| 4062 passiveBrowserEventsSupported = false; | |
| 4063 } | |
| 4064 } | |
| 4065 | |
| 4066 function invokeGuardedCallbackProd(name, func, context, a, b, c, d, e, f) { | |
| 4067 var funcArgs = Array.prototype.slice.call(arguments, 3); | |
| 4068 | |
| 4069 try { | |
| 4070 func.apply(context, funcArgs); | |
| 4071 } catch (error) { | |
| 4072 this.onError(error); | |
| 4073 } | |
| 4074 } | |
| 4075 | |
| 4076 var invokeGuardedCallbackImpl = invokeGuardedCallbackProd; | |
| 4077 | |
| 4078 { | |
| 4079 // In DEV mode, we swap out invokeGuardedCallback for a special version | |
| 4080 // that plays more nicely with the browser's DevTools. The idea is to preserve | |
| 4081 // "Pause on exceptions" behavior. Because React wraps all user-provided | |
| 4082 // functions in invokeGuardedCallback, and the production version of | |
| 4083 // invokeGuardedCallback uses a try-catch, all user exceptions are treated | |
| 4084 // like caught exceptions, and the DevTools won't pause unless the developer | |
| 4085 // takes the extra step of enabling pause on caught exceptions. This is | |
| 4086 // unintuitive, though, because even though React has caught the error, from | |
| 4087 // the developer's perspective, the error is uncaught. | |
| 4088 // | |
| 4089 // To preserve the expected "Pause on exceptions" behavior, we don't use a | |
| 4090 // try-catch in DEV. Instead, we synchronously dispatch a fake event to a fake | |
| 4091 // DOM node, and call the user-provided callback from inside an event handler | |
| 4092 // for that fake event. If the callback throws, the error is "captured" using | |
| 4093 // a global event handler. But because the error happens in a different | |
| 4094 // event loop context, it does not interrupt the normal program flow. | |
| 4095 // Effectively, this gives us try-catch behavior without actually using | |
| 4096 // try-catch. Neat! | |
| 4097 // Check that the browser supports the APIs we need to implement our special | |
| 4098 // DEV version of invokeGuardedCallback | |
| 4099 if (typeof window !== 'undefined' && typeof window.dispatchEvent === 'function' && typeof document !== 'undefined' && typeof document.createEvent === 'function') { | |
| 4100 var fakeNode = document.createElement('react'); | |
| 4101 | |
| 4102 invokeGuardedCallbackImpl = function invokeGuardedCallbackDev(name, func, context, a, b, c, d, e, f) { | |
| 4103 // If document doesn't exist we know for sure we will crash in this method | |
| 4104 // when we call document.createEvent(). However this can cause confusing | |
| 4105 // errors: https://github.com/facebook/create-react-app/issues/3482 | |
| 4106 // So we preemptively throw with a better message instead. | |
| 4107 if (typeof document === 'undefined' || document === null) { | |
| 4108 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.'); | |
| 4109 } | |
| 4110 | |
| 4111 var evt = document.createEvent('Event'); | |
| 4112 var didCall = false; // Keeps track of whether the user-provided callback threw an error. We | |
| 4113 // set this to true at the beginning, then set it to false right after | |
| 4114 // calling the function. If the function errors, `didError` will never be | |
| 4115 // set to false. This strategy works even if the browser is flaky and | |
| 4116 // fails to call our global error handler, because it doesn't rely on | |
| 4117 // the error event at all. | |
| 4118 | |
| 4119 var didError = true; // Keeps track of the value of window.event so that we can reset it | |
| 4120 // during the callback to let user code access window.event in the | |
| 4121 // browsers that support it. | |
| 4122 | |
| 4123 var windowEvent = window.event; // Keeps track of the descriptor of window.event to restore it after event | |
| 4124 // dispatching: https://github.com/facebook/react/issues/13688 | |
| 4125 | |
| 4126 var windowEventDescriptor = Object.getOwnPropertyDescriptor(window, 'event'); | |
| 4127 | |
| 4128 function restoreAfterDispatch() { | |
| 4129 // We immediately remove the callback from event listeners so that | |
| 4130 // nested `invokeGuardedCallback` calls do not clash. Otherwise, a | |
| 4131 // nested call would trigger the fake event handlers of any call higher | |
| 4132 // in the stack. | |
| 4133 fakeNode.removeEventListener(evtType, callCallback, false); // We check for window.hasOwnProperty('event') to prevent the | |
| 4134 // window.event assignment in both IE <= 10 as they throw an error | |
| 4135 // "Member not found" in strict mode, and in Firefox which does not | |
| 4136 // support window.event. | |
| 4137 | |
| 4138 if (typeof window.event !== 'undefined' && window.hasOwnProperty('event')) { | |
| 4139 window.event = windowEvent; | |
| 4140 } | |
| 4141 } // Create an event handler for our fake event. We will synchronously | |
| 4142 // dispatch our fake event using `dispatchEvent`. Inside the handler, we | |
| 4143 // call the user-provided callback. | |
| 4144 | |
| 4145 | |
| 4146 var funcArgs = Array.prototype.slice.call(arguments, 3); | |
| 4147 | |
| 4148 function callCallback() { | |
| 4149 didCall = true; | |
| 4150 restoreAfterDispatch(); | |
| 4151 func.apply(context, funcArgs); | |
| 4152 didError = false; | |
| 4153 } // Create a global error event handler. We use this to capture the value | |
| 4154 // that was thrown. It's possible that this error handler will fire more | |
| 4155 // than once; for example, if non-React code also calls `dispatchEvent` | |
| 4156 // and a handler for that event throws. We should be resilient to most of | |
| 4157 // those cases. Even if our error event handler fires more than once, the | |
| 4158 // last error event is always used. If the callback actually does error, | |
| 4159 // we know that the last error event is the correct one, because it's not | |
| 4160 // possible for anything else to have happened in between our callback | |
| 4161 // erroring and the code that follows the `dispatchEvent` call below. If | |
| 4162 // the callback doesn't error, but the error event was fired, we know to | |
| 4163 // ignore it because `didError` will be false, as described above. | |
| 4164 | |
| 4165 | |
| 4166 var error; // Use this to track whether the error event is ever called. | |
| 4167 | |
| 4168 var didSetError = false; | |
| 4169 var isCrossOriginError = false; | |
| 4170 | |
| 4171 function handleWindowError(event) { | |
| 4172 error = event.error; | |
| 4173 didSetError = true; | |
| 4174 | |
| 4175 if (error === null && event.colno === 0 && event.lineno === 0) { | |
| 4176 isCrossOriginError = true; | |
| 4177 } | |
| 4178 | |
| 4179 if (event.defaultPrevented) { | |
| 4180 // Some other error handler has prevented default. | |
| 4181 // Browsers silence the error report if this happens. | |
| 4182 // We'll remember this to later decide whether to log it or not. | |
| 4183 if (error != null && typeof error === 'object') { | |
| 4184 try { | |
| 4185 error._suppressLogging = true; | |
| 4186 } catch (inner) {// Ignore. | |
| 4187 } | |
| 4188 } | |
| 4189 } | |
| 4190 } // Create a fake event type. | |
| 4191 | |
| 4192 | |
| 4193 var evtType = "react-" + (name ? name : 'invokeguardedcallback'); // Attach our event handlers | |
| 4194 | |
| 4195 window.addEventListener('error', handleWindowError); | |
| 4196 fakeNode.addEventListener(evtType, callCallback, false); // Synchronously dispatch our fake event. If the user-provided function | |
| 4197 // errors, it will trigger our global error handler. | |
| 4198 | |
| 4199 evt.initEvent(evtType, false, false); | |
| 4200 fakeNode.dispatchEvent(evt); | |
| 4201 | |
| 4202 if (windowEventDescriptor) { | |
| 4203 Object.defineProperty(window, 'event', windowEventDescriptor); | |
| 4204 } | |
| 4205 | |
| 4206 if (didCall && didError) { | |
| 4207 if (!didSetError) { | |
| 4208 // The callback errored, but the error event never fired. | |
| 4209 // eslint-disable-next-line react-internal/prod-error-codes | |
| 4210 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.'); | |
| 4211 } else if (isCrossOriginError) { | |
| 4212 // eslint-disable-next-line react-internal/prod-error-codes | |
| 4213 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.'); | |
| 4214 } | |
| 4215 | |
| 4216 this.onError(error); | |
| 4217 } // Remove our event listeners | |
| 4218 | |
| 4219 | |
| 4220 window.removeEventListener('error', handleWindowError); | |
| 4221 | |
| 4222 if (!didCall) { | |
| 4223 // Something went really wrong, and our event was not dispatched. | |
| 4224 // https://github.com/facebook/react/issues/16734 | |
| 4225 // https://github.com/facebook/react/issues/16585 | |
| 4226 // Fall back to the production implementation. | |
| 4227 restoreAfterDispatch(); | |
| 4228 return invokeGuardedCallbackProd.apply(this, arguments); | |
| 4229 } | |
| 4230 }; | |
| 4231 } | |
| 4232 } | |
| 4233 | |
| 4234 var invokeGuardedCallbackImpl$1 = invokeGuardedCallbackImpl; | |
| 4235 | |
| 4236 var hasError = false; | |
| 4237 var caughtError = null; // Used by event system to capture/rethrow the first error. | |
| 4238 | |
| 4239 var hasRethrowError = false; | |
| 4240 var rethrowError = null; | |
| 4241 var reporter = { | |
| 4242 onError: function (error) { | |
| 4243 hasError = true; | |
| 4244 caughtError = error; | |
| 4245 } | |
| 4246 }; | |
| 4247 /** | |
| 4248 * Call a function while guarding against errors that happens within it. | |
| 4249 * Returns an error if it throws, otherwise null. | |
| 4250 * | |
| 4251 * In production, this is implemented using a try-catch. The reason we don't | |
| 4252 * use a try-catch directly is so that we can swap out a different | |
| 4253 * implementation in DEV mode. | |
| 4254 * | |
| 4255 * @param {String} name of the guard to use for logging or debugging | |
| 4256 * @param {Function} func The function to invoke | |
| 4257 * @param {*} context The context to use when calling the function | |
| 4258 * @param {...*} args Arguments for function | |
| 4259 */ | |
| 4260 | |
| 4261 function invokeGuardedCallback(name, func, context, a, b, c, d, e, f) { | |
| 4262 hasError = false; | |
| 4263 caughtError = null; | |
| 4264 invokeGuardedCallbackImpl$1.apply(reporter, arguments); | |
| 4265 } | |
| 4266 /** | |
| 4267 * Same as invokeGuardedCallback, but instead of returning an error, it stores | |
| 4268 * it in a global so it can be rethrown by `rethrowCaughtError` later. | |
| 4269 * TODO: See if caughtError and rethrowError can be unified. | |
| 4270 * | |
| 4271 * @param {String} name of the guard to use for logging or debugging | |
| 4272 * @param {Function} func The function to invoke | |
| 4273 * @param {*} context The context to use when calling the function | |
| 4274 * @param {...*} args Arguments for function | |
| 4275 */ | |
| 4276 | |
| 4277 function invokeGuardedCallbackAndCatchFirstError(name, func, context, a, b, c, d, e, f) { | |
| 4278 invokeGuardedCallback.apply(this, arguments); | |
| 4279 | |
| 4280 if (hasError) { | |
| 4281 var error = clearCaughtError(); | |
| 4282 | |
| 4283 if (!hasRethrowError) { | |
| 4284 hasRethrowError = true; | |
| 4285 rethrowError = error; | |
| 4286 } | |
| 4287 } | |
| 4288 } | |
| 4289 /** | |
| 4290 * During execution of guarded functions we will capture the first error which | |
| 4291 * we will rethrow to be handled by the top level error handler. | |
| 4292 */ | |
| 4293 | |
| 4294 function rethrowCaughtError() { | |
| 4295 if (hasRethrowError) { | |
| 4296 var error = rethrowError; | |
| 4297 hasRethrowError = false; | |
| 4298 rethrowError = null; | |
| 4299 throw error; | |
| 4300 } | |
| 4301 } | |
| 4302 function hasCaughtError() { | |
| 4303 return hasError; | |
| 4304 } | |
| 4305 function clearCaughtError() { | |
| 4306 if (hasError) { | |
| 4307 var error = caughtError; | |
| 4308 hasError = false; | |
| 4309 caughtError = null; | |
| 4310 return error; | |
| 4311 } else { | |
| 4312 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.'); | |
| 4313 } | |
| 4314 } | |
| 4315 | |
| 4316 var ReactInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; | |
| 4317 var _ReactInternals$Sched = ReactInternals.Scheduler, | |
| 4318 unstable_cancelCallback = _ReactInternals$Sched.unstable_cancelCallback, | |
| 4319 unstable_now = _ReactInternals$Sched.unstable_now, | |
| 4320 unstable_scheduleCallback = _ReactInternals$Sched.unstable_scheduleCallback, | |
| 4321 unstable_shouldYield = _ReactInternals$Sched.unstable_shouldYield, | |
| 4322 unstable_requestPaint = _ReactInternals$Sched.unstable_requestPaint, | |
| 4323 unstable_getFirstCallbackNode = _ReactInternals$Sched.unstable_getFirstCallbackNode, | |
| 4324 unstable_runWithPriority = _ReactInternals$Sched.unstable_runWithPriority, | |
| 4325 unstable_next = _ReactInternals$Sched.unstable_next, | |
| 4326 unstable_continueExecution = _ReactInternals$Sched.unstable_continueExecution, | |
| 4327 unstable_pauseExecution = _ReactInternals$Sched.unstable_pauseExecution, | |
| 4328 unstable_getCurrentPriorityLevel = _ReactInternals$Sched.unstable_getCurrentPriorityLevel, | |
| 4329 unstable_ImmediatePriority = _ReactInternals$Sched.unstable_ImmediatePriority, | |
| 4330 unstable_UserBlockingPriority = _ReactInternals$Sched.unstable_UserBlockingPriority, | |
| 4331 unstable_NormalPriority = _ReactInternals$Sched.unstable_NormalPriority, | |
| 4332 unstable_LowPriority = _ReactInternals$Sched.unstable_LowPriority, | |
| 4333 unstable_IdlePriority = _ReactInternals$Sched.unstable_IdlePriority, | |
| 4334 unstable_forceFrameRate = _ReactInternals$Sched.unstable_forceFrameRate, | |
| 4335 unstable_flushAllWithoutAsserting = _ReactInternals$Sched.unstable_flushAllWithoutAsserting, | |
| 4336 unstable_yieldValue = _ReactInternals$Sched.unstable_yieldValue, | |
| 4337 unstable_setDisableYieldValue = _ReactInternals$Sched.unstable_setDisableYieldValue; | |
| 4338 | |
| 4339 /** | |
| 4340 * `ReactInstanceMap` maintains a mapping from a public facing stateful | |
| 4341 * instance (key) and the internal representation (value). This allows public | |
| 4342 * methods to accept the user facing instance as an argument and map them back | |
| 4343 * to internal methods. | |
| 4344 * | |
| 4345 * Note that this module is currently shared and assumed to be stateless. | |
| 4346 * If this becomes an actual Map, that will break. | |
| 4347 */ | |
| 4348 function get(key) { | |
| 4349 return key._reactInternals; | |
| 4350 } | |
| 4351 function has(key) { | |
| 4352 return key._reactInternals !== undefined; | |
| 4353 } | |
| 4354 function set(key, value) { | |
| 4355 key._reactInternals = value; | |
| 4356 } | |
| 4357 | |
| 4358 // Don't change these two values. They're used by React Dev Tools. | |
| 4359 var NoFlags = | |
| 4360 /* */ | |
| 4361 0; | |
| 4362 var PerformedWork = | |
| 4363 /* */ | |
| 4364 1; // You can change the rest (and add more). | |
| 4365 | |
| 4366 var Placement = | |
| 4367 /* */ | |
| 4368 2; | |
| 4369 var Update = | |
| 4370 /* */ | |
| 4371 4; | |
| 4372 var ChildDeletion = | |
| 4373 /* */ | |
| 4374 16; | |
| 4375 var ContentReset = | |
| 4376 /* */ | |
| 4377 32; | |
| 4378 var Callback = | |
| 4379 /* */ | |
| 4380 64; | |
| 4381 var DidCapture = | |
| 4382 /* */ | |
| 4383 128; | |
| 4384 var ForceClientRender = | |
| 4385 /* */ | |
| 4386 256; | |
| 4387 var Ref = | |
| 4388 /* */ | |
| 4389 512; | |
| 4390 var Snapshot = | |
| 4391 /* */ | |
| 4392 1024; | |
| 4393 var Passive = | |
| 4394 /* */ | |
| 4395 2048; | |
| 4396 var Hydrating = | |
| 4397 /* */ | |
| 4398 4096; | |
| 4399 var Visibility = | |
| 4400 /* */ | |
| 4401 8192; | |
| 4402 var StoreConsistency = | |
| 4403 /* */ | |
| 4404 16384; | |
| 4405 var LifecycleEffectMask = Passive | Update | Callback | Ref | Snapshot | StoreConsistency; // Union of all commit flags (flags with the lifetime of a particular commit) | |
| 4406 | |
| 4407 var HostEffectMask = | |
| 4408 /* */ | |
| 4409 32767; // These are not really side effects, but we still reuse this field. | |
| 4410 | |
| 4411 var Incomplete = | |
| 4412 /* */ | |
| 4413 32768; | |
| 4414 var ShouldCapture = | |
| 4415 /* */ | |
| 4416 65536; | |
| 4417 var ForceUpdateForLegacySuspense = | |
| 4418 /* */ | |
| 4419 131072; | |
| 4420 var Forked = | |
| 4421 /* */ | |
| 4422 1048576; // Static tags describe aspects of a fiber that are not specific to a render, | |
| 4423 // e.g. a fiber uses a passive effect (even if there are no updates on this particular render). | |
| 4424 // This enables us to defer more work in the unmount case, | |
| 4425 // since we can defer traversing the tree during layout to look for Passive effects, | |
| 4426 // and instead rely on the static flag as a signal that there may be cleanup work. | |
| 4427 | |
| 4428 var RefStatic = | |
| 4429 /* */ | |
| 4430 2097152; | |
| 4431 var LayoutStatic = | |
| 4432 /* */ | |
| 4433 4194304; | |
| 4434 var PassiveStatic = | |
| 4435 /* */ | |
| 4436 8388608; // These flags allow us to traverse to fibers that have effects on mount | |
| 4437 // without traversing the entire tree after every commit for | |
| 4438 // double invoking | |
| 4439 | |
| 4440 var MountLayoutDev = | |
| 4441 /* */ | |
| 4442 16777216; | |
| 4443 var MountPassiveDev = | |
| 4444 /* */ | |
| 4445 33554432; // Groups of flags that are used in the commit phase to skip over trees that | |
| 4446 // don't contain effects, by checking subtreeFlags. | |
| 4447 | |
| 4448 var BeforeMutationMask = // TODO: Remove Update flag from before mutation phase by re-landing Visibility | |
| 4449 // flag logic (see #20043) | |
| 4450 Update | Snapshot | ( 0); | |
| 4451 var MutationMask = Placement | Update | ChildDeletion | ContentReset | Ref | Hydrating | Visibility; | |
| 4452 var LayoutMask = Update | Callback | Ref | Visibility; // TODO: Split into PassiveMountMask and PassiveUnmountMask | |
| 4453 | |
| 4454 var PassiveMask = Passive | ChildDeletion; // Union of tags that don't get reset on clones. | |
| 4455 // This allows certain concepts to persist without recalculating them, | |
| 4456 // e.g. whether a subtree contains passive effects or portals. | |
| 4457 | |
| 4458 var StaticMask = LayoutStatic | PassiveStatic | RefStatic; | |
| 4459 | |
| 4460 var ReactCurrentOwner = ReactSharedInternals.ReactCurrentOwner; | |
| 4461 function getNearestMountedFiber(fiber) { | |
| 4462 var node = fiber; | |
| 4463 var nearestMounted = fiber; | |
| 4464 | |
| 4465 if (!fiber.alternate) { | |
| 4466 // If there is no alternate, this might be a new tree that isn't inserted | |
| 4467 // yet. If it is, then it will have a pending insertion effect on it. | |
| 4468 var nextNode = node; | |
| 4469 | |
| 4470 do { | |
| 4471 node = nextNode; | |
| 4472 | |
| 4473 if ((node.flags & (Placement | Hydrating)) !== NoFlags) { | |
| 4474 // This is an insertion or in-progress hydration. The nearest possible | |
| 4475 // mounted fiber is the parent but we need to continue to figure out | |
| 4476 // if that one is still mounted. | |
| 4477 nearestMounted = node.return; | |
| 4478 } | |
| 4479 | |
| 4480 nextNode = node.return; | |
| 4481 } while (nextNode); | |
| 4482 } else { | |
| 4483 while (node.return) { | |
| 4484 node = node.return; | |
| 4485 } | |
| 4486 } | |
| 4487 | |
| 4488 if (node.tag === HostRoot) { | |
| 4489 // TODO: Check if this was a nested HostRoot when used with | |
| 4490 // renderContainerIntoSubtree. | |
| 4491 return nearestMounted; | |
| 4492 } // If we didn't hit the root, that means that we're in an disconnected tree | |
| 4493 // that has been unmounted. | |
| 4494 | |
| 4495 | |
| 4496 return null; | |
| 4497 } | |
| 4498 function getSuspenseInstanceFromFiber(fiber) { | |
| 4499 if (fiber.tag === SuspenseComponent) { | |
| 4500 var suspenseState = fiber.memoizedState; | |
| 4501 | |
| 4502 if (suspenseState === null) { | |
| 4503 var current = fiber.alternate; | |
| 4504 | |
| 4505 if (current !== null) { | |
| 4506 suspenseState = current.memoizedState; | |
| 4507 } | |
| 4508 } | |
| 4509 | |
| 4510 if (suspenseState !== null) { | |
| 4511 return suspenseState.dehydrated; | |
| 4512 } | |
| 4513 } | |
| 4514 | |
| 4515 return null; | |
| 4516 } | |
| 4517 function getContainerFromFiber(fiber) { | |
| 4518 return fiber.tag === HostRoot ? fiber.stateNode.containerInfo : null; | |
| 4519 } | |
| 4520 function isFiberMounted(fiber) { | |
| 4521 return getNearestMountedFiber(fiber) === fiber; | |
| 4522 } | |
| 4523 function isMounted(component) { | |
| 4524 { | |
| 4525 var owner = ReactCurrentOwner.current; | |
| 4526 | |
| 4527 if (owner !== null && owner.tag === ClassComponent) { | |
| 4528 var ownerFiber = owner; | |
| 4529 var instance = ownerFiber.stateNode; | |
| 4530 | |
| 4531 if (!instance._warnedAboutRefsInRender) { | |
| 4532 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'); | |
| 4533 } | |
| 4534 | |
| 4535 instance._warnedAboutRefsInRender = true; | |
| 4536 } | |
| 4537 } | |
| 4538 | |
| 4539 var fiber = get(component); | |
| 4540 | |
| 4541 if (!fiber) { | |
| 4542 return false; | |
| 4543 } | |
| 4544 | |
| 4545 return getNearestMountedFiber(fiber) === fiber; | |
| 4546 } | |
| 4547 | |
| 4548 function assertIsMounted(fiber) { | |
| 4549 if (getNearestMountedFiber(fiber) !== fiber) { | |
| 4550 throw new Error('Unable to find node on an unmounted component.'); | |
| 4551 } | |
| 4552 } | |
| 4553 | |
| 4554 function findCurrentFiberUsingSlowPath(fiber) { | |
| 4555 var alternate = fiber.alternate; | |
| 4556 | |
| 4557 if (!alternate) { | |
| 4558 // If there is no alternate, then we only need to check if it is mounted. | |
| 4559 var nearestMounted = getNearestMountedFiber(fiber); | |
| 4560 | |
| 4561 if (nearestMounted === null) { | |
| 4562 throw new Error('Unable to find node on an unmounted component.'); | |
| 4563 } | |
| 4564 | |
| 4565 if (nearestMounted !== fiber) { | |
| 4566 return null; | |
| 4567 } | |
| 4568 | |
| 4569 return fiber; | |
| 4570 } // If we have two possible branches, we'll walk backwards up to the root | |
| 4571 // to see what path the root points to. On the way we may hit one of the | |
| 4572 // special cases and we'll deal with them. | |
| 4573 | |
| 4574 | |
| 4575 var a = fiber; | |
| 4576 var b = alternate; | |
| 4577 | |
| 4578 while (true) { | |
| 4579 var parentA = a.return; | |
| 4580 | |
| 4581 if (parentA === null) { | |
| 4582 // We're at the root. | |
| 4583 break; | |
| 4584 } | |
| 4585 | |
| 4586 var parentB = parentA.alternate; | |
| 4587 | |
| 4588 if (parentB === null) { | |
| 4589 // There is no alternate. This is an unusual case. Currently, it only | |
| 4590 // happens when a Suspense component is hidden. An extra fragment fiber | |
| 4591 // is inserted in between the Suspense fiber and its children. Skip | |
| 4592 // over this extra fragment fiber and proceed to the next parent. | |
| 4593 var nextParent = parentA.return; | |
| 4594 | |
| 4595 if (nextParent !== null) { | |
| 4596 a = b = nextParent; | |
| 4597 continue; | |
| 4598 } // If there's no parent, we're at the root. | |
| 4599 | |
| 4600 | |
| 4601 break; | |
| 4602 } // If both copies of the parent fiber point to the same child, we can | |
| 4603 // assume that the child is current. This happens when we bailout on low | |
| 4604 // priority: the bailed out fiber's child reuses the current child. | |
| 4605 | |
| 4606 | |
| 4607 if (parentA.child === parentB.child) { | |
| 4608 var child = parentA.child; | |
| 4609 | |
| 4610 while (child) { | |
| 4611 if (child === a) { | |
| 4612 // We've determined that A is the current branch. | |
| 4613 assertIsMounted(parentA); | |
| 4614 return fiber; | |
| 4615 } | |
| 4616 | |
| 4617 if (child === b) { | |
| 4618 // We've determined that B is the current branch. | |
| 4619 assertIsMounted(parentA); | |
| 4620 return alternate; | |
| 4621 } | |
| 4622 | |
| 4623 child = child.sibling; | |
| 4624 } // We should never have an alternate for any mounting node. So the only | |
| 4625 // way this could possibly happen is if this was unmounted, if at all. | |
| 4626 | |
| 4627 | |
| 4628 throw new Error('Unable to find node on an unmounted component.'); | |
| 4629 } | |
| 4630 | |
| 4631 if (a.return !== b.return) { | |
| 4632 // The return pointer of A and the return pointer of B point to different | |
| 4633 // fibers. We assume that return pointers never criss-cross, so A must | |
| 4634 // belong to the child set of A.return, and B must belong to the child | |
| 4635 // set of B.return. | |
| 4636 a = parentA; | |
| 4637 b = parentB; | |
| 4638 } else { | |
| 4639 // The return pointers point to the same fiber. We'll have to use the | |
| 4640 // default, slow path: scan the child sets of each parent alternate to see | |
| 4641 // which child belongs to which set. | |
| 4642 // | |
| 4643 // Search parent A's child set | |
| 4644 var didFindChild = false; | |
| 4645 var _child = parentA.child; | |
| 4646 | |
| 4647 while (_child) { | |
| 4648 if (_child === a) { | |
| 4649 didFindChild = true; | |
| 4650 a = parentA; | |
| 4651 b = parentB; | |
| 4652 break; | |
| 4653 } | |
| 4654 | |
| 4655 if (_child === b) { | |
| 4656 didFindChild = true; | |
| 4657 b = parentA; | |
| 4658 a = parentB; | |
| 4659 break; | |
| 4660 } | |
| 4661 | |
| 4662 _child = _child.sibling; | |
| 4663 } | |
| 4664 | |
| 4665 if (!didFindChild) { | |
| 4666 // Search parent B's child set | |
| 4667 _child = parentB.child; | |
| 4668 | |
| 4669 while (_child) { | |
| 4670 if (_child === a) { | |
| 4671 didFindChild = true; | |
| 4672 a = parentB; | |
| 4673 b = parentA; | |
| 4674 break; | |
| 4675 } | |
| 4676 | |
| 4677 if (_child === b) { | |
| 4678 didFindChild = true; | |
| 4679 b = parentB; | |
| 4680 a = parentA; | |
| 4681 break; | |
| 4682 } | |
| 4683 | |
| 4684 _child = _child.sibling; | |
| 4685 } | |
| 4686 | |
| 4687 if (!didFindChild) { | |
| 4688 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.'); | |
| 4689 } | |
| 4690 } | |
| 4691 } | |
| 4692 | |
| 4693 if (a.alternate !== b) { | |
| 4694 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.'); | |
| 4695 } | |
| 4696 } // If the root is not a host container, we're in a disconnected tree. I.e. | |
| 4697 // unmounted. | |
| 4698 | |
| 4699 | |
| 4700 if (a.tag !== HostRoot) { | |
| 4701 throw new Error('Unable to find node on an unmounted component.'); | |
| 4702 } | |
| 4703 | |
| 4704 if (a.stateNode.current === a) { | |
| 4705 // We've determined that A is the current branch. | |
| 4706 return fiber; | |
| 4707 } // Otherwise B has to be current branch. | |
| 4708 | |
| 4709 | |
| 4710 return alternate; | |
| 4711 } | |
| 4712 function findCurrentHostFiber(parent) { | |
| 4713 var currentParent = findCurrentFiberUsingSlowPath(parent); | |
| 4714 return currentParent !== null ? findCurrentHostFiberImpl(currentParent) : null; | |
| 4715 } | |
| 4716 | |
| 4717 function findCurrentHostFiberImpl(node) { | |
| 4718 // Next we'll drill down this component to find the first HostComponent/Text. | |
| 4719 if (node.tag === HostComponent || node.tag === HostText) { | |
| 4720 return node; | |
| 4721 } | |
| 4722 | |
| 4723 var child = node.child; | |
| 4724 | |
| 4725 while (child !== null) { | |
| 4726 var match = findCurrentHostFiberImpl(child); | |
| 4727 | |
| 4728 if (match !== null) { | |
| 4729 return match; | |
| 4730 } | |
| 4731 | |
| 4732 child = child.sibling; | |
| 4733 } | |
| 4734 | |
| 4735 return null; | |
| 4736 } | |
| 4737 | |
| 4738 function findCurrentHostFiberWithNoPortals(parent) { | |
| 4739 var currentParent = findCurrentFiberUsingSlowPath(parent); | |
| 4740 return currentParent !== null ? findCurrentHostFiberWithNoPortalsImpl(currentParent) : null; | |
| 4741 } | |
| 4742 | |
| 4743 function findCurrentHostFiberWithNoPortalsImpl(node) { | |
| 4744 // Next we'll drill down this component to find the first HostComponent/Text. | |
| 4745 if (node.tag === HostComponent || node.tag === HostText) { | |
| 4746 return node; | |
| 4747 } | |
| 4748 | |
| 4749 var child = node.child; | |
| 4750 | |
| 4751 while (child !== null) { | |
| 4752 if (child.tag !== HostPortal) { | |
| 4753 var match = findCurrentHostFiberWithNoPortalsImpl(child); | |
| 4754 | |
| 4755 if (match !== null) { | |
| 4756 return match; | |
| 4757 } | |
| 4758 } | |
| 4759 | |
| 4760 child = child.sibling; | |
| 4761 } | |
| 4762 | |
| 4763 return null; | |
| 4764 } | |
| 4765 | |
| 4766 // This module only exists as an ESM wrapper around the external CommonJS | |
| 4767 var scheduleCallback = unstable_scheduleCallback; | |
| 4768 var cancelCallback = unstable_cancelCallback; | |
| 4769 var shouldYield = unstable_shouldYield; | |
| 4770 var requestPaint = unstable_requestPaint; | |
| 4771 var now = unstable_now; | |
| 4772 var getCurrentPriorityLevel = unstable_getCurrentPriorityLevel; | |
| 4773 var ImmediatePriority = unstable_ImmediatePriority; | |
| 4774 var UserBlockingPriority = unstable_UserBlockingPriority; | |
| 4775 var NormalPriority = unstable_NormalPriority; | |
| 4776 var LowPriority = unstable_LowPriority; | |
| 4777 var IdlePriority = unstable_IdlePriority; | |
| 4778 // this doesn't actually exist on the scheduler, but it *does* | |
| 4779 // on scheduler/unstable_mock, which we'll need for internal testing | |
| 4780 var unstable_yieldValue$1 = unstable_yieldValue; | |
| 4781 var unstable_setDisableYieldValue$1 = unstable_setDisableYieldValue; | |
| 4782 | |
| 4783 var rendererID = null; | |
| 4784 var injectedHook = null; | |
| 4785 var injectedProfilingHooks = null; | |
| 4786 var hasLoggedError = false; | |
| 4787 var isDevToolsPresent = typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ !== 'undefined'; | |
| 4788 function injectInternals(internals) { | |
| 4789 if (typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ === 'undefined') { | |
| 4790 // No DevTools | |
| 4791 return false; | |
| 4792 } | |
| 4793 | |
| 4794 var hook = __REACT_DEVTOOLS_GLOBAL_HOOK__; | |
| 4795 | |
| 4796 if (hook.isDisabled) { | |
| 4797 // This isn't a real property on the hook, but it can be set to opt out | |
| 4798 // of DevTools integration and associated warnings and logs. | |
| 4799 // https://github.com/facebook/react/issues/3877 | |
| 4800 return true; | |
| 4801 } | |
| 4802 | |
| 4803 if (!hook.supportsFiber) { | |
| 4804 { | |
| 4805 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'); | |
| 4806 } // DevTools exists, even though it doesn't support Fiber. | |
| 4807 | |
| 4808 | |
| 4809 return true; | |
| 4810 } | |
| 4811 | |
| 4812 try { | |
| 4813 if (enableSchedulingProfiler) { | |
| 4814 // Conditionally inject these hooks only if Timeline profiler is supported by this build. | |
| 4815 // This gives DevTools a way to feature detect that isn't tied to version number | |
| 4816 // (since profiling and timeline are controlled by different feature flags). | |
| 4817 internals = assign({}, internals, { | |
| 4818 getLaneLabelMap: getLaneLabelMap, | |
| 4819 injectProfilingHooks: injectProfilingHooks | |
| 4820 }); | |
| 4821 } | |
| 4822 | |
| 4823 rendererID = hook.inject(internals); // We have successfully injected, so now it is safe to set up hooks. | |
| 4824 | |
| 4825 injectedHook = hook; | |
| 4826 } catch (err) { | |
| 4827 // Catch all errors because it is unsafe to throw during initialization. | |
| 4828 { | |
| 4829 error('React instrumentation encountered an error: %s.', err); | |
| 4830 } | |
| 4831 } | |
| 4832 | |
| 4833 if (hook.checkDCE) { | |
| 4834 // This is the real DevTools. | |
| 4835 return true; | |
| 4836 } else { | |
| 4837 // This is likely a hook installed by Fast Refresh runtime. | |
| 4838 return false; | |
| 4839 } | |
| 4840 } | |
| 4841 function onScheduleRoot(root, children) { | |
| 4842 { | |
| 4843 if (injectedHook && typeof injectedHook.onScheduleFiberRoot === 'function') { | |
| 4844 try { | |
| 4845 injectedHook.onScheduleFiberRoot(rendererID, root, children); | |
| 4846 } catch (err) { | |
| 4847 if ( !hasLoggedError) { | |
| 4848 hasLoggedError = true; | |
| 4849 | |
| 4850 error('React instrumentation encountered an error: %s', err); | |
| 4851 } | |
| 4852 } | |
| 4853 } | |
| 4854 } | |
| 4855 } | |
| 4856 function onCommitRoot(root, eventPriority) { | |
| 4857 if (injectedHook && typeof injectedHook.onCommitFiberRoot === 'function') { | |
| 4858 try { | |
| 4859 var didError = (root.current.flags & DidCapture) === DidCapture; | |
| 4860 | |
| 4861 if (enableProfilerTimer) { | |
| 4862 var schedulerPriority; | |
| 4863 | |
| 4864 switch (eventPriority) { | |
| 4865 case DiscreteEventPriority: | |
| 4866 schedulerPriority = ImmediatePriority; | |
| 4867 break; | |
| 4868 | |
| 4869 case ContinuousEventPriority: | |
| 4870 schedulerPriority = UserBlockingPriority; | |
| 4871 break; | |
| 4872 | |
| 4873 case DefaultEventPriority: | |
| 4874 schedulerPriority = NormalPriority; | |
| 4875 break; | |
| 4876 | |
| 4877 case IdleEventPriority: | |
| 4878 schedulerPriority = IdlePriority; | |
| 4879 break; | |
| 4880 | |
| 4881 default: | |
| 4882 schedulerPriority = NormalPriority; | |
| 4883 break; | |
| 4884 } | |
| 4885 | |
| 4886 injectedHook.onCommitFiberRoot(rendererID, root, schedulerPriority, didError); | |
| 4887 } else { | |
| 4888 injectedHook.onCommitFiberRoot(rendererID, root, undefined, didError); | |
| 4889 } | |
| 4890 } catch (err) { | |
| 4891 { | |
| 4892 if (!hasLoggedError) { | |
| 4893 hasLoggedError = true; | |
| 4894 | |
| 4895 error('React instrumentation encountered an error: %s', err); | |
| 4896 } | |
| 4897 } | |
| 4898 } | |
| 4899 } | |
| 4900 } | |
| 4901 function onPostCommitRoot(root) { | |
| 4902 if (injectedHook && typeof injectedHook.onPostCommitFiberRoot === 'function') { | |
| 4903 try { | |
| 4904 injectedHook.onPostCommitFiberRoot(rendererID, root); | |
| 4905 } catch (err) { | |
| 4906 { | |
| 4907 if (!hasLoggedError) { | |
| 4908 hasLoggedError = true; | |
| 4909 | |
| 4910 error('React instrumentation encountered an error: %s', err); | |
| 4911 } | |
| 4912 } | |
| 4913 } | |
| 4914 } | |
| 4915 } | |
| 4916 function onCommitUnmount(fiber) { | |
| 4917 if (injectedHook && typeof injectedHook.onCommitFiberUnmount === 'function') { | |
| 4918 try { | |
| 4919 injectedHook.onCommitFiberUnmount(rendererID, fiber); | |
| 4920 } catch (err) { | |
| 4921 { | |
| 4922 if (!hasLoggedError) { | |
| 4923 hasLoggedError = true; | |
| 4924 | |
| 4925 error('React instrumentation encountered an error: %s', err); | |
| 4926 } | |
| 4927 } | |
| 4928 } | |
| 4929 } | |
| 4930 } | |
| 4931 function setIsStrictModeForDevtools(newIsStrictMode) { | |
| 4932 { | |
| 4933 if (typeof unstable_yieldValue$1 === 'function') { | |
| 4934 // We're in a test because Scheduler.unstable_yieldValue only exists | |
| 4935 // in SchedulerMock. To reduce the noise in strict mode tests, | |
| 4936 // suppress warnings and disable scheduler yielding during the double render | |
| 4937 unstable_setDisableYieldValue$1(newIsStrictMode); | |
| 4938 setSuppressWarning(newIsStrictMode); | |
| 4939 } | |
| 4940 | |
| 4941 if (injectedHook && typeof injectedHook.setStrictMode === 'function') { | |
| 4942 try { | |
| 4943 injectedHook.setStrictMode(rendererID, newIsStrictMode); | |
| 4944 } catch (err) { | |
| 4945 { | |
| 4946 if (!hasLoggedError) { | |
| 4947 hasLoggedError = true; | |
| 4948 | |
| 4949 error('React instrumentation encountered an error: %s', err); | |
| 4950 } | |
| 4951 } | |
| 4952 } | |
| 4953 } | |
| 4954 } | |
| 4955 } // Profiler API hooks | |
| 4956 | |
| 4957 function injectProfilingHooks(profilingHooks) { | |
| 4958 injectedProfilingHooks = profilingHooks; | |
| 4959 } | |
| 4960 | |
| 4961 function getLaneLabelMap() { | |
| 4962 { | |
| 4963 var map = new Map(); | |
| 4964 var lane = 1; | |
| 4965 | |
| 4966 for (var index = 0; index < TotalLanes; index++) { | |
| 4967 var label = getLabelForLane(lane); | |
| 4968 map.set(lane, label); | |
| 4969 lane *= 2; | |
| 4970 } | |
| 4971 | |
| 4972 return map; | |
| 4973 } | |
| 4974 } | |
| 4975 | |
| 4976 function markCommitStarted(lanes) { | |
| 4977 { | |
| 4978 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStarted === 'function') { | |
| 4979 injectedProfilingHooks.markCommitStarted(lanes); | |
| 4980 } | |
| 4981 } | |
| 4982 } | |
| 4983 function markCommitStopped() { | |
| 4984 { | |
| 4985 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markCommitStopped === 'function') { | |
| 4986 injectedProfilingHooks.markCommitStopped(); | |
| 4987 } | |
| 4988 } | |
| 4989 } | |
| 4990 function markComponentRenderStarted(fiber) { | |
| 4991 { | |
| 4992 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStarted === 'function') { | |
| 4993 injectedProfilingHooks.markComponentRenderStarted(fiber); | |
| 4994 } | |
| 4995 } | |
| 4996 } | |
| 4997 function markComponentRenderStopped() { | |
| 4998 { | |
| 4999 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentRenderStopped === 'function') { | |
| 5000 injectedProfilingHooks.markComponentRenderStopped(); | |
| 5001 } | |
| 5002 } | |
| 5003 } | |
| 5004 function markComponentPassiveEffectMountStarted(fiber) { | |
| 5005 { | |
| 5006 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStarted === 'function') { | |
| 5007 injectedProfilingHooks.markComponentPassiveEffectMountStarted(fiber); | |
| 5008 } | |
| 5009 } | |
| 5010 } | |
| 5011 function markComponentPassiveEffectMountStopped() { | |
| 5012 { | |
| 5013 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectMountStopped === 'function') { | |
| 5014 injectedProfilingHooks.markComponentPassiveEffectMountStopped(); | |
| 5015 } | |
| 5016 } | |
| 5017 } | |
| 5018 function markComponentPassiveEffectUnmountStarted(fiber) { | |
| 5019 { | |
| 5020 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStarted === 'function') { | |
| 5021 injectedProfilingHooks.markComponentPassiveEffectUnmountStarted(fiber); | |
| 5022 } | |
| 5023 } | |
| 5024 } | |
| 5025 function markComponentPassiveEffectUnmountStopped() { | |
| 5026 { | |
| 5027 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentPassiveEffectUnmountStopped === 'function') { | |
| 5028 injectedProfilingHooks.markComponentPassiveEffectUnmountStopped(); | |
| 5029 } | |
| 5030 } | |
| 5031 } | |
| 5032 function markComponentLayoutEffectMountStarted(fiber) { | |
| 5033 { | |
| 5034 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStarted === 'function') { | |
| 5035 injectedProfilingHooks.markComponentLayoutEffectMountStarted(fiber); | |
| 5036 } | |
| 5037 } | |
| 5038 } | |
| 5039 function markComponentLayoutEffectMountStopped() { | |
| 5040 { | |
| 5041 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectMountStopped === 'function') { | |
| 5042 injectedProfilingHooks.markComponentLayoutEffectMountStopped(); | |
| 5043 } | |
| 5044 } | |
| 5045 } | |
| 5046 function markComponentLayoutEffectUnmountStarted(fiber) { | |
| 5047 { | |
| 5048 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStarted === 'function') { | |
| 5049 injectedProfilingHooks.markComponentLayoutEffectUnmountStarted(fiber); | |
| 5050 } | |
| 5051 } | |
| 5052 } | |
| 5053 function markComponentLayoutEffectUnmountStopped() { | |
| 5054 { | |
| 5055 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentLayoutEffectUnmountStopped === 'function') { | |
| 5056 injectedProfilingHooks.markComponentLayoutEffectUnmountStopped(); | |
| 5057 } | |
| 5058 } | |
| 5059 } | |
| 5060 function markComponentErrored(fiber, thrownValue, lanes) { | |
| 5061 { | |
| 5062 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentErrored === 'function') { | |
| 5063 injectedProfilingHooks.markComponentErrored(fiber, thrownValue, lanes); | |
| 5064 } | |
| 5065 } | |
| 5066 } | |
| 5067 function markComponentSuspended(fiber, wakeable, lanes) { | |
| 5068 { | |
| 5069 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markComponentSuspended === 'function') { | |
| 5070 injectedProfilingHooks.markComponentSuspended(fiber, wakeable, lanes); | |
| 5071 } | |
| 5072 } | |
| 5073 } | |
| 5074 function markLayoutEffectsStarted(lanes) { | |
| 5075 { | |
| 5076 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStarted === 'function') { | |
| 5077 injectedProfilingHooks.markLayoutEffectsStarted(lanes); | |
| 5078 } | |
| 5079 } | |
| 5080 } | |
| 5081 function markLayoutEffectsStopped() { | |
| 5082 { | |
| 5083 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markLayoutEffectsStopped === 'function') { | |
| 5084 injectedProfilingHooks.markLayoutEffectsStopped(); | |
| 5085 } | |
| 5086 } | |
| 5087 } | |
| 5088 function markPassiveEffectsStarted(lanes) { | |
| 5089 { | |
| 5090 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStarted === 'function') { | |
| 5091 injectedProfilingHooks.markPassiveEffectsStarted(lanes); | |
| 5092 } | |
| 5093 } | |
| 5094 } | |
| 5095 function markPassiveEffectsStopped() { | |
| 5096 { | |
| 5097 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markPassiveEffectsStopped === 'function') { | |
| 5098 injectedProfilingHooks.markPassiveEffectsStopped(); | |
| 5099 } | |
| 5100 } | |
| 5101 } | |
| 5102 function markRenderStarted(lanes) { | |
| 5103 { | |
| 5104 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStarted === 'function') { | |
| 5105 injectedProfilingHooks.markRenderStarted(lanes); | |
| 5106 } | |
| 5107 } | |
| 5108 } | |
| 5109 function markRenderYielded() { | |
| 5110 { | |
| 5111 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderYielded === 'function') { | |
| 5112 injectedProfilingHooks.markRenderYielded(); | |
| 5113 } | |
| 5114 } | |
| 5115 } | |
| 5116 function markRenderStopped() { | |
| 5117 { | |
| 5118 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderStopped === 'function') { | |
| 5119 injectedProfilingHooks.markRenderStopped(); | |
| 5120 } | |
| 5121 } | |
| 5122 } | |
| 5123 function markRenderScheduled(lane) { | |
| 5124 { | |
| 5125 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markRenderScheduled === 'function') { | |
| 5126 injectedProfilingHooks.markRenderScheduled(lane); | |
| 5127 } | |
| 5128 } | |
| 5129 } | |
| 5130 function markForceUpdateScheduled(fiber, lane) { | |
| 5131 { | |
| 5132 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markForceUpdateScheduled === 'function') { | |
| 5133 injectedProfilingHooks.markForceUpdateScheduled(fiber, lane); | |
| 5134 } | |
| 5135 } | |
| 5136 } | |
| 5137 function markStateUpdateScheduled(fiber, lane) { | |
| 5138 { | |
| 5139 if (injectedProfilingHooks !== null && typeof injectedProfilingHooks.markStateUpdateScheduled === 'function') { | |
| 5140 injectedProfilingHooks.markStateUpdateScheduled(fiber, lane); | |
| 5141 } | |
| 5142 } | |
| 5143 } | |
| 5144 | |
| 5145 var NoMode = | |
| 5146 /* */ | |
| 5147 0; // TODO: Remove ConcurrentMode by reading from the root tag instead | |
| 5148 | |
| 5149 var ConcurrentMode = | |
| 5150 /* */ | |
| 5151 1; | |
| 5152 var ProfileMode = | |
| 5153 /* */ | |
| 5154 2; | |
| 5155 var StrictLegacyMode = | |
| 5156 /* */ | |
| 5157 8; | |
| 5158 var StrictEffectsMode = | |
| 5159 /* */ | |
| 5160 16; | |
| 5161 | |
| 5162 // TODO: This is pretty well supported by browsers. Maybe we can drop it. | |
| 5163 var clz32 = Math.clz32 ? Math.clz32 : clz32Fallback; // Count leading zeros. | |
| 5164 // Based on: | |
| 5165 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 | |
| 5166 | |
| 5167 var log = Math.log; | |
| 5168 var LN2 = Math.LN2; | |
| 5169 | |
| 5170 function clz32Fallback(x) { | |
| 5171 var asUint = x >>> 0; | |
| 5172 | |
| 5173 if (asUint === 0) { | |
| 5174 return 32; | |
| 5175 } | |
| 5176 | |
| 5177 return 31 - (log(asUint) / LN2 | 0) | 0; | |
| 5178 } | |
| 5179 | |
| 5180 // If those values are changed that package should be rebuilt and redeployed. | |
| 5181 | |
| 5182 var TotalLanes = 31; | |
| 5183 var NoLanes = | |
| 5184 /* */ | |
| 5185 0; | |
| 5186 var NoLane = | |
| 5187 /* */ | |
| 5188 0; | |
| 5189 var SyncLane = | |
| 5190 /* */ | |
| 5191 1; | |
| 5192 var InputContinuousHydrationLane = | |
| 5193 /* */ | |
| 5194 2; | |
| 5195 var InputContinuousLane = | |
| 5196 /* */ | |
| 5197 4; | |
| 5198 var DefaultHydrationLane = | |
| 5199 /* */ | |
| 5200 8; | |
| 5201 var DefaultLane = | |
| 5202 /* */ | |
| 5203 16; | |
| 5204 var TransitionHydrationLane = | |
| 5205 /* */ | |
| 5206 32; | |
| 5207 var TransitionLanes = | |
| 5208 /* */ | |
| 5209 4194240; | |
| 5210 var TransitionLane1 = | |
| 5211 /* */ | |
| 5212 64; | |
| 5213 var TransitionLane2 = | |
| 5214 /* */ | |
| 5215 128; | |
| 5216 var TransitionLane3 = | |
| 5217 /* */ | |
| 5218 256; | |
| 5219 var TransitionLane4 = | |
| 5220 /* */ | |
| 5221 512; | |
| 5222 var TransitionLane5 = | |
| 5223 /* */ | |
| 5224 1024; | |
| 5225 var TransitionLane6 = | |
| 5226 /* */ | |
| 5227 2048; | |
| 5228 var TransitionLane7 = | |
| 5229 /* */ | |
| 5230 4096; | |
| 5231 var TransitionLane8 = | |
| 5232 /* */ | |
| 5233 8192; | |
| 5234 var TransitionLane9 = | |
| 5235 /* */ | |
| 5236 16384; | |
| 5237 var TransitionLane10 = | |
| 5238 /* */ | |
| 5239 32768; | |
| 5240 var TransitionLane11 = | |
| 5241 /* */ | |
| 5242 65536; | |
| 5243 var TransitionLane12 = | |
| 5244 /* */ | |
| 5245 131072; | |
| 5246 var TransitionLane13 = | |
| 5247 /* */ | |
| 5248 262144; | |
| 5249 var TransitionLane14 = | |
| 5250 /* */ | |
| 5251 524288; | |
| 5252 var TransitionLane15 = | |
| 5253 /* */ | |
| 5254 1048576; | |
| 5255 var TransitionLane16 = | |
| 5256 /* */ | |
| 5257 2097152; | |
| 5258 var RetryLanes = | |
| 5259 /* */ | |
| 5260 130023424; | |
| 5261 var RetryLane1 = | |
| 5262 /* */ | |
| 5263 4194304; | |
| 5264 var RetryLane2 = | |
| 5265 /* */ | |
| 5266 8388608; | |
| 5267 var RetryLane3 = | |
| 5268 /* */ | |
| 5269 16777216; | |
| 5270 var RetryLane4 = | |
| 5271 /* */ | |
| 5272 33554432; | |
| 5273 var RetryLane5 = | |
| 5274 /* */ | |
| 5275 67108864; | |
| 5276 var SomeRetryLane = RetryLane1; | |
| 5277 var SelectiveHydrationLane = | |
| 5278 /* */ | |
| 5279 134217728; | |
| 5280 var NonIdleLanes = | |
| 5281 /* */ | |
| 5282 268435455; | |
| 5283 var IdleHydrationLane = | |
| 5284 /* */ | |
| 5285 268435456; | |
| 5286 var IdleLane = | |
| 5287 /* */ | |
| 5288 536870912; | |
| 5289 var OffscreenLane = | |
| 5290 /* */ | |
| 5291 1073741824; // This function is used for the experimental timeline (react-devtools-timeline) | |
| 5292 // It should be kept in sync with the Lanes values above. | |
| 5293 | |
| 5294 function getLabelForLane(lane) { | |
| 5295 { | |
| 5296 if (lane & SyncLane) { | |
| 5297 return 'Sync'; | |
| 5298 } | |
| 5299 | |
| 5300 if (lane & InputContinuousHydrationLane) { | |
| 5301 return 'InputContinuousHydration'; | |
| 5302 } | |
| 5303 | |
| 5304 if (lane & InputContinuousLane) { | |
| 5305 return 'InputContinuous'; | |
| 5306 } | |
| 5307 | |
| 5308 if (lane & DefaultHydrationLane) { | |
| 5309 return 'DefaultHydration'; | |
| 5310 } | |
| 5311 | |
| 5312 if (lane & DefaultLane) { | |
| 5313 return 'Default'; | |
| 5314 } | |
| 5315 | |
| 5316 if (lane & TransitionHydrationLane) { | |
| 5317 return 'TransitionHydration'; | |
| 5318 } | |
| 5319 | |
| 5320 if (lane & TransitionLanes) { | |
| 5321 return 'Transition'; | |
| 5322 } | |
| 5323 | |
| 5324 if (lane & RetryLanes) { | |
| 5325 return 'Retry'; | |
| 5326 } | |
| 5327 | |
| 5328 if (lane & SelectiveHydrationLane) { | |
| 5329 return 'SelectiveHydration'; | |
| 5330 } | |
| 5331 | |
| 5332 if (lane & IdleHydrationLane) { | |
| 5333 return 'IdleHydration'; | |
| 5334 } | |
| 5335 | |
| 5336 if (lane & IdleLane) { | |
| 5337 return 'Idle'; | |
| 5338 } | |
| 5339 | |
| 5340 if (lane & OffscreenLane) { | |
| 5341 return 'Offscreen'; | |
| 5342 } | |
| 5343 } | |
| 5344 } | |
| 5345 var NoTimestamp = -1; | |
| 5346 var nextTransitionLane = TransitionLane1; | |
| 5347 var nextRetryLane = RetryLane1; | |
| 5348 | |
| 5349 function getHighestPriorityLanes(lanes) { | |
| 5350 switch (getHighestPriorityLane(lanes)) { | |
| 5351 case SyncLane: | |
| 5352 return SyncLane; | |
| 5353 | |
| 5354 case InputContinuousHydrationLane: | |
| 5355 return InputContinuousHydrationLane; | |
| 5356 | |
| 5357 case InputContinuousLane: | |
| 5358 return InputContinuousLane; | |
| 5359 | |
| 5360 case DefaultHydrationLane: | |
| 5361 return DefaultHydrationLane; | |
| 5362 | |
| 5363 case DefaultLane: | |
| 5364 return DefaultLane; | |
| 5365 | |
| 5366 case TransitionHydrationLane: | |
| 5367 return TransitionHydrationLane; | |
| 5368 | |
| 5369 case TransitionLane1: | |
| 5370 case TransitionLane2: | |
| 5371 case TransitionLane3: | |
| 5372 case TransitionLane4: | |
| 5373 case TransitionLane5: | |
| 5374 case TransitionLane6: | |
| 5375 case TransitionLane7: | |
| 5376 case TransitionLane8: | |
| 5377 case TransitionLane9: | |
| 5378 case TransitionLane10: | |
| 5379 case TransitionLane11: | |
| 5380 case TransitionLane12: | |
| 5381 case TransitionLane13: | |
| 5382 case TransitionLane14: | |
| 5383 case TransitionLane15: | |
| 5384 case TransitionLane16: | |
| 5385 return lanes & TransitionLanes; | |
| 5386 | |
| 5387 case RetryLane1: | |
| 5388 case RetryLane2: | |
| 5389 case RetryLane3: | |
| 5390 case RetryLane4: | |
| 5391 case RetryLane5: | |
| 5392 return lanes & RetryLanes; | |
| 5393 | |
| 5394 case SelectiveHydrationLane: | |
| 5395 return SelectiveHydrationLane; | |
| 5396 | |
| 5397 case IdleHydrationLane: | |
| 5398 return IdleHydrationLane; | |
| 5399 | |
| 5400 case IdleLane: | |
| 5401 return IdleLane; | |
| 5402 | |
| 5403 case OffscreenLane: | |
| 5404 return OffscreenLane; | |
| 5405 | |
| 5406 default: | |
| 5407 { | |
| 5408 error('Should have found matching lanes. This is a bug in React.'); | |
| 5409 } // This shouldn't be reachable, but as a fallback, return the entire bitmask. | |
| 5410 | |
| 5411 | |
| 5412 return lanes; | |
| 5413 } | |
| 5414 } | |
| 5415 | |
| 5416 function getNextLanes(root, wipLanes) { | |
| 5417 // Early bailout if there's no pending work left. | |
| 5418 var pendingLanes = root.pendingLanes; | |
| 5419 | |
| 5420 if (pendingLanes === NoLanes) { | |
| 5421 return NoLanes; | |
| 5422 } | |
| 5423 | |
| 5424 var nextLanes = NoLanes; | |
| 5425 var suspendedLanes = root.suspendedLanes; | |
| 5426 var pingedLanes = root.pingedLanes; // Do not work on any idle work until all the non-idle work has finished, | |
| 5427 // even if the work is suspended. | |
| 5428 | |
| 5429 var nonIdlePendingLanes = pendingLanes & NonIdleLanes; | |
| 5430 | |
| 5431 if (nonIdlePendingLanes !== NoLanes) { | |
| 5432 var nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes; | |
| 5433 | |
| 5434 if (nonIdleUnblockedLanes !== NoLanes) { | |
| 5435 nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes); | |
| 5436 } else { | |
| 5437 var nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes; | |
| 5438 | |
| 5439 if (nonIdlePingedLanes !== NoLanes) { | |
| 5440 nextLanes = getHighestPriorityLanes(nonIdlePingedLanes); | |
| 5441 } | |
| 5442 } | |
| 5443 } else { | |
| 5444 // The only remaining work is Idle. | |
| 5445 var unblockedLanes = pendingLanes & ~suspendedLanes; | |
| 5446 | |
| 5447 if (unblockedLanes !== NoLanes) { | |
| 5448 nextLanes = getHighestPriorityLanes(unblockedLanes); | |
| 5449 } else { | |
| 5450 if (pingedLanes !== NoLanes) { | |
| 5451 nextLanes = getHighestPriorityLanes(pingedLanes); | |
| 5452 } | |
| 5453 } | |
| 5454 } | |
| 5455 | |
| 5456 if (nextLanes === NoLanes) { | |
| 5457 // This should only be reachable if we're suspended | |
| 5458 // TODO: Consider warning in this path if a fallback timer is not scheduled. | |
| 5459 return NoLanes; | |
| 5460 } // If we're already in the middle of a render, switching lanes will interrupt | |
| 5461 // it and we'll lose our progress. We should only do this if the new lanes are | |
| 5462 // higher priority. | |
| 5463 | |
| 5464 | |
| 5465 if (wipLanes !== NoLanes && wipLanes !== nextLanes && // If we already suspended with a delay, then interrupting is fine. Don't | |
| 5466 // bother waiting until the root is complete. | |
| 5467 (wipLanes & suspendedLanes) === NoLanes) { | |
| 5468 var nextLane = getHighestPriorityLane(nextLanes); | |
| 5469 var wipLane = getHighestPriorityLane(wipLanes); | |
| 5470 | |
| 5471 if ( // Tests whether the next lane is equal or lower priority than the wip | |
| 5472 // one. This works because the bits decrease in priority as you go left. | |
| 5473 nextLane >= wipLane || // Default priority updates should not interrupt transition updates. The | |
| 5474 // only difference between default updates and transition updates is that | |
| 5475 // default updates do not support refresh transitions. | |
| 5476 nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes) { | |
| 5477 // Keep working on the existing in-progress tree. Do not interrupt. | |
| 5478 return wipLanes; | |
| 5479 } | |
| 5480 } | |
| 5481 | |
| 5482 if ((nextLanes & InputContinuousLane) !== NoLanes) { | |
| 5483 // When updates are sync by default, we entangle continuous priority updates | |
| 5484 // and default updates, so they render in the same batch. The only reason | |
| 5485 // they use separate lanes is because continuous updates should interrupt | |
| 5486 // transitions, but default updates should not. | |
| 5487 nextLanes |= pendingLanes & DefaultLane; | |
| 5488 } // Check for entangled lanes and add them to the batch. | |
| 5489 // | |
| 5490 // A lane is said to be entangled with another when it's not allowed to render | |
| 5491 // in a batch that does not also include the other lane. Typically we do this | |
| 5492 // when multiple updates have the same source, and we only want to respond to | |
| 5493 // the most recent event from that source. | |
| 5494 // | |
| 5495 // Note that we apply entanglements *after* checking for partial work above. | |
| 5496 // This means that if a lane is entangled during an interleaved event while | |
| 5497 // it's already rendering, we won't interrupt it. This is intentional, since | |
| 5498 // entanglement is usually "best effort": we'll try our best to render the | |
| 5499 // lanes in the same batch, but it's not worth throwing out partially | |
| 5500 // completed work in order to do it. | |
| 5501 // TODO: Reconsider this. The counter-argument is that the partial work | |
| 5502 // represents an intermediate state, which we don't want to show to the user. | |
| 5503 // And by spending extra time finishing it, we're increasing the amount of | |
| 5504 // time it takes to show the final state, which is what they are actually | |
| 5505 // waiting for. | |
| 5506 // | |
| 5507 // For those exceptions where entanglement is semantically important, like | |
| 5508 // useMutableSource, we should ensure that there is no partial work at the | |
| 5509 // time we apply the entanglement. | |
| 5510 | |
| 5511 | |
| 5512 var entangledLanes = root.entangledLanes; | |
| 5513 | |
| 5514 if (entangledLanes !== NoLanes) { | |
| 5515 var entanglements = root.entanglements; | |
| 5516 var lanes = nextLanes & entangledLanes; | |
| 5517 | |
| 5518 while (lanes > 0) { | |
| 5519 var index = pickArbitraryLaneIndex(lanes); | |
| 5520 var lane = 1 << index; | |
| 5521 nextLanes |= entanglements[index]; | |
| 5522 lanes &= ~lane; | |
| 5523 } | |
| 5524 } | |
| 5525 | |
| 5526 return nextLanes; | |
| 5527 } | |
| 5528 function getMostRecentEventTime(root, lanes) { | |
| 5529 var eventTimes = root.eventTimes; | |
| 5530 var mostRecentEventTime = NoTimestamp; | |
| 5531 | |
| 5532 while (lanes > 0) { | |
| 5533 var index = pickArbitraryLaneIndex(lanes); | |
| 5534 var lane = 1 << index; | |
| 5535 var eventTime = eventTimes[index]; | |
| 5536 | |
| 5537 if (eventTime > mostRecentEventTime) { | |
| 5538 mostRecentEventTime = eventTime; | |
| 5539 } | |
| 5540 | |
| 5541 lanes &= ~lane; | |
| 5542 } | |
| 5543 | |
| 5544 return mostRecentEventTime; | |
| 5545 } | |
| 5546 | |
| 5547 function computeExpirationTime(lane, currentTime) { | |
| 5548 switch (lane) { | |
| 5549 case SyncLane: | |
| 5550 case InputContinuousHydrationLane: | |
| 5551 case InputContinuousLane: | |
| 5552 // User interactions should expire slightly more quickly. | |
| 5553 // | |
| 5554 // NOTE: This is set to the corresponding constant as in Scheduler.js. | |
| 5555 // When we made it larger, a product metric in www regressed, suggesting | |
| 5556 // there's a user interaction that's being starved by a series of | |
| 5557 // synchronous updates. If that theory is correct, the proper solution is | |
| 5558 // to fix the starvation. However, this scenario supports the idea that | |
| 5559 // expiration times are an important safeguard when starvation | |
| 5560 // does happen. | |
| 5561 return currentTime + 250; | |
| 5562 | |
| 5563 case DefaultHydrationLane: | |
| 5564 case DefaultLane: | |
| 5565 case TransitionHydrationLane: | |
| 5566 case TransitionLane1: | |
| 5567 case TransitionLane2: | |
| 5568 case TransitionLane3: | |
| 5569 case TransitionLane4: | |
| 5570 case TransitionLane5: | |
| 5571 case TransitionLane6: | |
| 5572 case TransitionLane7: | |
| 5573 case TransitionLane8: | |
| 5574 case TransitionLane9: | |
| 5575 case TransitionLane10: | |
| 5576 case TransitionLane11: | |
| 5577 case TransitionLane12: | |
| 5578 case TransitionLane13: | |
| 5579 case TransitionLane14: | |
| 5580 case TransitionLane15: | |
| 5581 case TransitionLane16: | |
| 5582 return currentTime + 5000; | |
| 5583 | |
| 5584 case RetryLane1: | |
| 5585 case RetryLane2: | |
| 5586 case RetryLane3: | |
| 5587 case RetryLane4: | |
| 5588 case RetryLane5: | |
| 5589 // TODO: Retries should be allowed to expire if they are CPU bound for | |
| 5590 // too long, but when I made this change it caused a spike in browser | |
| 5591 // crashes. There must be some other underlying bug; not super urgent but | |
| 5592 // ideally should figure out why and fix it. Unfortunately we don't have | |
| 5593 // a repro for the crashes, only detected via production metrics. | |
| 5594 return NoTimestamp; | |
| 5595 | |
| 5596 case SelectiveHydrationLane: | |
| 5597 case IdleHydrationLane: | |
| 5598 case IdleLane: | |
| 5599 case OffscreenLane: | |
| 5600 // Anything idle priority or lower should never expire. | |
| 5601 return NoTimestamp; | |
| 5602 | |
| 5603 default: | |
| 5604 { | |
| 5605 error('Should have found matching lanes. This is a bug in React.'); | |
| 5606 } | |
| 5607 | |
| 5608 return NoTimestamp; | |
| 5609 } | |
| 5610 } | |
| 5611 | |
| 5612 function markStarvedLanesAsExpired(root, currentTime) { | |
| 5613 // TODO: This gets called every time we yield. We can optimize by storing | |
| 5614 // the earliest expiration time on the root. Then use that to quickly bail out | |
| 5615 // of this function. | |
| 5616 var pendingLanes = root.pendingLanes; | |
| 5617 var suspendedLanes = root.suspendedLanes; | |
| 5618 var pingedLanes = root.pingedLanes; | |
| 5619 var expirationTimes = root.expirationTimes; // Iterate through the pending lanes and check if we've reached their | |
| 5620 // expiration time. If so, we'll assume the update is being starved and mark | |
| 5621 // it as expired to force it to finish. | |
| 5622 | |
| 5623 var lanes = pendingLanes; | |
| 5624 | |
| 5625 while (lanes > 0) { | |
| 5626 var index = pickArbitraryLaneIndex(lanes); | |
| 5627 var lane = 1 << index; | |
| 5628 var expirationTime = expirationTimes[index]; | |
| 5629 | |
| 5630 if (expirationTime === NoTimestamp) { | |
| 5631 // Found a pending lane with no expiration time. If it's not suspended, or | |
| 5632 // if it's pinged, assume it's CPU-bound. Compute a new expiration time | |
| 5633 // using the current time. | |
| 5634 if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) { | |
| 5635 // Assumes timestamps are monotonically increasing. | |
| 5636 expirationTimes[index] = computeExpirationTime(lane, currentTime); | |
| 5637 } | |
| 5638 } else if (expirationTime <= currentTime) { | |
| 5639 // This lane expired | |
| 5640 root.expiredLanes |= lane; | |
| 5641 } | |
| 5642 | |
| 5643 lanes &= ~lane; | |
| 5644 } | |
| 5645 } // This returns the highest priority pending lanes regardless of whether they | |
| 5646 // are suspended. | |
| 5647 | |
| 5648 function getHighestPriorityPendingLanes(root) { | |
| 5649 return getHighestPriorityLanes(root.pendingLanes); | |
| 5650 } | |
| 5651 function getLanesToRetrySynchronouslyOnError(root) { | |
| 5652 var everythingButOffscreen = root.pendingLanes & ~OffscreenLane; | |
| 5653 | |
| 5654 if (everythingButOffscreen !== NoLanes) { | |
| 5655 return everythingButOffscreen; | |
| 5656 } | |
| 5657 | |
| 5658 if (everythingButOffscreen & OffscreenLane) { | |
| 5659 return OffscreenLane; | |
| 5660 } | |
| 5661 | |
| 5662 return NoLanes; | |
| 5663 } | |
| 5664 function includesSyncLane(lanes) { | |
| 5665 return (lanes & SyncLane) !== NoLanes; | |
| 5666 } | |
| 5667 function includesNonIdleWork(lanes) { | |
| 5668 return (lanes & NonIdleLanes) !== NoLanes; | |
| 5669 } | |
| 5670 function includesOnlyRetries(lanes) { | |
| 5671 return (lanes & RetryLanes) === lanes; | |
| 5672 } | |
| 5673 function includesOnlyNonUrgentLanes(lanes) { | |
| 5674 var UrgentLanes = SyncLane | InputContinuousLane | DefaultLane; | |
| 5675 return (lanes & UrgentLanes) === NoLanes; | |
| 5676 } | |
| 5677 function includesOnlyTransitions(lanes) { | |
| 5678 return (lanes & TransitionLanes) === lanes; | |
| 5679 } | |
| 5680 function includesBlockingLane(root, lanes) { | |
| 5681 | |
| 5682 var SyncDefaultLanes = InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | DefaultLane; | |
| 5683 return (lanes & SyncDefaultLanes) !== NoLanes; | |
| 5684 } | |
| 5685 function includesExpiredLane(root, lanes) { | |
| 5686 // This is a separate check from includesBlockingLane because a lane can | |
| 5687 // expire after a render has already started. | |
| 5688 return (lanes & root.expiredLanes) !== NoLanes; | |
| 5689 } | |
| 5690 function isTransitionLane(lane) { | |
| 5691 return (lane & TransitionLanes) !== NoLanes; | |
| 5692 } | |
| 5693 function claimNextTransitionLane() { | |
| 5694 // Cycle through the lanes, assigning each new transition to the next lane. | |
| 5695 // In most cases, this means every transition gets its own lane, until we | |
| 5696 // run out of lanes and cycle back to the beginning. | |
| 5697 var lane = nextTransitionLane; | |
| 5698 nextTransitionLane <<= 1; | |
| 5699 | |
| 5700 if ((nextTransitionLane & TransitionLanes) === NoLanes) { | |
| 5701 nextTransitionLane = TransitionLane1; | |
| 5702 } | |
| 5703 | |
| 5704 return lane; | |
| 5705 } | |
| 5706 function claimNextRetryLane() { | |
| 5707 var lane = nextRetryLane; | |
| 5708 nextRetryLane <<= 1; | |
| 5709 | |
| 5710 if ((nextRetryLane & RetryLanes) === NoLanes) { | |
| 5711 nextRetryLane = RetryLane1; | |
| 5712 } | |
| 5713 | |
| 5714 return lane; | |
| 5715 } | |
| 5716 function getHighestPriorityLane(lanes) { | |
| 5717 return lanes & -lanes; | |
| 5718 } | |
| 5719 function pickArbitraryLane(lanes) { | |
| 5720 // This wrapper function gets inlined. Only exists so to communicate that it | |
| 5721 // doesn't matter which bit is selected; you can pick any bit without | |
| 5722 // affecting the algorithms where its used. Here I'm using | |
| 5723 // getHighestPriorityLane because it requires the fewest operations. | |
| 5724 return getHighestPriorityLane(lanes); | |
| 5725 } | |
| 5726 | |
| 5727 function pickArbitraryLaneIndex(lanes) { | |
| 5728 return 31 - clz32(lanes); | |
| 5729 } | |
| 5730 | |
| 5731 function laneToIndex(lane) { | |
| 5732 return pickArbitraryLaneIndex(lane); | |
| 5733 } | |
| 5734 | |
| 5735 function includesSomeLane(a, b) { | |
| 5736 return (a & b) !== NoLanes; | |
| 5737 } | |
| 5738 function isSubsetOfLanes(set, subset) { | |
| 5739 return (set & subset) === subset; | |
| 5740 } | |
| 5741 function mergeLanes(a, b) { | |
| 5742 return a | b; | |
| 5743 } | |
| 5744 function removeLanes(set, subset) { | |
| 5745 return set & ~subset; | |
| 5746 } | |
| 5747 function intersectLanes(a, b) { | |
| 5748 return a & b; | |
| 5749 } // Seems redundant, but it changes the type from a single lane (used for | |
| 5750 // updates) to a group of lanes (used for flushing work). | |
| 5751 | |
| 5752 function laneToLanes(lane) { | |
| 5753 return lane; | |
| 5754 } | |
| 5755 function higherPriorityLane(a, b) { | |
| 5756 // This works because the bit ranges decrease in priority as you go left. | |
| 5757 return a !== NoLane && a < b ? a : b; | |
| 5758 } | |
| 5759 function createLaneMap(initial) { | |
| 5760 // Intentionally pushing one by one. | |
| 5761 // https://v8.dev/blog/elements-kinds#avoid-creating-holes | |
| 5762 var laneMap = []; | |
| 5763 | |
| 5764 for (var i = 0; i < TotalLanes; i++) { | |
| 5765 laneMap.push(initial); | |
| 5766 } | |
| 5767 | |
| 5768 return laneMap; | |
| 5769 } | |
| 5770 function markRootUpdated(root, updateLane, eventTime) { | |
| 5771 root.pendingLanes |= updateLane; // If there are any suspended transitions, it's possible this new update | |
| 5772 // could unblock them. Clear the suspended lanes so that we can try rendering | |
| 5773 // them again. | |
| 5774 // | |
| 5775 // TODO: We really only need to unsuspend only lanes that are in the | |
| 5776 // `subtreeLanes` of the updated fiber, or the update lanes of the return | |
| 5777 // path. This would exclude suspended updates in an unrelated sibling tree, | |
| 5778 // since there's no way for this update to unblock it. | |
| 5779 // | |
| 5780 // We don't do this if the incoming update is idle, because we never process | |
| 5781 // idle updates until after all the regular updates have finished; there's no | |
| 5782 // way it could unblock a transition. | |
| 5783 | |
| 5784 if (updateLane !== IdleLane) { | |
| 5785 root.suspendedLanes = NoLanes; | |
| 5786 root.pingedLanes = NoLanes; | |
| 5787 } | |
| 5788 | |
| 5789 var eventTimes = root.eventTimes; | |
| 5790 var index = laneToIndex(updateLane); // We can always overwrite an existing timestamp because we prefer the most | |
| 5791 // recent event, and we assume time is monotonically increasing. | |
| 5792 | |
| 5793 eventTimes[index] = eventTime; | |
| 5794 } | |
| 5795 function markRootSuspended(root, suspendedLanes) { | |
| 5796 root.suspendedLanes |= suspendedLanes; | |
| 5797 root.pingedLanes &= ~suspendedLanes; // The suspended lanes are no longer CPU-bound. Clear their expiration times. | |
| 5798 | |
| 5799 var expirationTimes = root.expirationTimes; | |
| 5800 var lanes = suspendedLanes; | |
| 5801 | |
| 5802 while (lanes > 0) { | |
| 5803 var index = pickArbitraryLaneIndex(lanes); | |
| 5804 var lane = 1 << index; | |
| 5805 expirationTimes[index] = NoTimestamp; | |
| 5806 lanes &= ~lane; | |
| 5807 } | |
| 5808 } | |
| 5809 function markRootPinged(root, pingedLanes, eventTime) { | |
| 5810 root.pingedLanes |= root.suspendedLanes & pingedLanes; | |
| 5811 } | |
| 5812 function markRootFinished(root, remainingLanes) { | |
| 5813 var noLongerPendingLanes = root.pendingLanes & ~remainingLanes; | |
| 5814 root.pendingLanes = remainingLanes; // Let's try everything again | |
| 5815 | |
| 5816 root.suspendedLanes = NoLanes; | |
| 5817 root.pingedLanes = NoLanes; | |
| 5818 root.expiredLanes &= remainingLanes; | |
| 5819 root.mutableReadLanes &= remainingLanes; | |
| 5820 root.entangledLanes &= remainingLanes; | |
| 5821 var entanglements = root.entanglements; | |
| 5822 var eventTimes = root.eventTimes; | |
| 5823 var expirationTimes = root.expirationTimes; // Clear the lanes that no longer have pending work | |
| 5824 | |
| 5825 var lanes = noLongerPendingLanes; | |
| 5826 | |
| 5827 while (lanes > 0) { | |
| 5828 var index = pickArbitraryLaneIndex(lanes); | |
| 5829 var lane = 1 << index; | |
| 5830 entanglements[index] = NoLanes; | |
| 5831 eventTimes[index] = NoTimestamp; | |
| 5832 expirationTimes[index] = NoTimestamp; | |
| 5833 lanes &= ~lane; | |
| 5834 } | |
| 5835 } | |
| 5836 function markRootEntangled(root, entangledLanes) { | |
| 5837 // In addition to entangling each of the given lanes with each other, we also | |
| 5838 // have to consider _transitive_ entanglements. For each lane that is already | |
| 5839 // entangled with *any* of the given lanes, that lane is now transitively | |
| 5840 // entangled with *all* the given lanes. | |
| 5841 // | |
| 5842 // Translated: If C is entangled with A, then entangling A with B also | |
| 5843 // entangles C with B. | |
| 5844 // | |
| 5845 // If this is hard to grasp, it might help to intentionally break this | |
| 5846 // function and look at the tests that fail in ReactTransition-test.js. Try | |
| 5847 // commenting out one of the conditions below. | |
| 5848 var rootEntangledLanes = root.entangledLanes |= entangledLanes; | |
| 5849 var entanglements = root.entanglements; | |
| 5850 var lanes = rootEntangledLanes; | |
| 5851 | |
| 5852 while (lanes) { | |
| 5853 var index = pickArbitraryLaneIndex(lanes); | |
| 5854 var lane = 1 << index; | |
| 5855 | |
| 5856 if ( // Is this one of the newly entangled lanes? | |
| 5857 lane & entangledLanes | // Is this lane transitively entangled with the newly entangled lanes? | |
| 5858 entanglements[index] & entangledLanes) { | |
| 5859 entanglements[index] |= entangledLanes; | |
| 5860 } | |
| 5861 | |
| 5862 lanes &= ~lane; | |
| 5863 } | |
| 5864 } | |
| 5865 function getBumpedLaneForHydration(root, renderLanes) { | |
| 5866 var renderLane = getHighestPriorityLane(renderLanes); | |
| 5867 var lane; | |
| 5868 | |
| 5869 switch (renderLane) { | |
| 5870 case InputContinuousLane: | |
| 5871 lane = InputContinuousHydrationLane; | |
| 5872 break; | |
| 5873 | |
| 5874 case DefaultLane: | |
| 5875 lane = DefaultHydrationLane; | |
| 5876 break; | |
| 5877 | |
| 5878 case TransitionLane1: | |
| 5879 case TransitionLane2: | |
| 5880 case TransitionLane3: | |
| 5881 case TransitionLane4: | |
| 5882 case TransitionLane5: | |
| 5883 case TransitionLane6: | |
| 5884 case TransitionLane7: | |
| 5885 case TransitionLane8: | |
| 5886 case TransitionLane9: | |
| 5887 case TransitionLane10: | |
| 5888 case TransitionLane11: | |
| 5889 case TransitionLane12: | |
| 5890 case TransitionLane13: | |
| 5891 case TransitionLane14: | |
| 5892 case TransitionLane15: | |
| 5893 case TransitionLane16: | |
| 5894 case RetryLane1: | |
| 5895 case RetryLane2: | |
| 5896 case RetryLane3: | |
| 5897 case RetryLane4: | |
| 5898 case RetryLane5: | |
| 5899 lane = TransitionHydrationLane; | |
| 5900 break; | |
| 5901 | |
| 5902 case IdleLane: | |
| 5903 lane = IdleHydrationLane; | |
| 5904 break; | |
| 5905 | |
| 5906 default: | |
| 5907 // Everything else is already either a hydration lane, or shouldn't | |
| 5908 // be retried at a hydration lane. | |
| 5909 lane = NoLane; | |
| 5910 break; | |
| 5911 } // Check if the lane we chose is suspended. If so, that indicates that we | |
| 5912 // already attempted and failed to hydrate at that level. Also check if we're | |
| 5913 // already rendering that lane, which is rare but could happen. | |
| 5914 | |
| 5915 | |
| 5916 if ((lane & (root.suspendedLanes | renderLanes)) !== NoLane) { | |
| 5917 // Give up trying to hydrate and fall back to client render. | |
| 5918 return NoLane; | |
| 5919 } | |
| 5920 | |
| 5921 return lane; | |
| 5922 } | |
| 5923 function addFiberToLanesMap(root, fiber, lanes) { | |
| 5924 | |
| 5925 if (!isDevToolsPresent) { | |
| 5926 return; | |
| 5927 } | |
| 5928 | |
| 5929 var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; | |
| 5930 | |
| 5931 while (lanes > 0) { | |
| 5932 var index = laneToIndex(lanes); | |
| 5933 var lane = 1 << index; | |
| 5934 var updaters = pendingUpdatersLaneMap[index]; | |
| 5935 updaters.add(fiber); | |
| 5936 lanes &= ~lane; | |
| 5937 } | |
| 5938 } | |
| 5939 function movePendingFibersToMemoized(root, lanes) { | |
| 5940 | |
| 5941 if (!isDevToolsPresent) { | |
| 5942 return; | |
| 5943 } | |
| 5944 | |
| 5945 var pendingUpdatersLaneMap = root.pendingUpdatersLaneMap; | |
| 5946 var memoizedUpdaters = root.memoizedUpdaters; | |
| 5947 | |
| 5948 while (lanes > 0) { | |
| 5949 var index = laneToIndex(lanes); | |
| 5950 var lane = 1 << index; | |
| 5951 var updaters = pendingUpdatersLaneMap[index]; | |
| 5952 | |
| 5953 if (updaters.size > 0) { | |
| 5954 updaters.forEach(function (fiber) { | |
| 5955 var alternate = fiber.alternate; | |
| 5956 | |
| 5957 if (alternate === null || !memoizedUpdaters.has(alternate)) { | |
| 5958 memoizedUpdaters.add(fiber); | |
| 5959 } | |
| 5960 }); | |
| 5961 updaters.clear(); | |
| 5962 } | |
| 5963 | |
| 5964 lanes &= ~lane; | |
| 5965 } | |
| 5966 } | |
| 5967 function getTransitionsForLanes(root, lanes) { | |
| 5968 { | |
| 5969 return null; | |
| 5970 } | |
| 5971 } | |
| 5972 | |
| 5973 var DiscreteEventPriority = SyncLane; | |
| 5974 var ContinuousEventPriority = InputContinuousLane; | |
| 5975 var DefaultEventPriority = DefaultLane; | |
| 5976 var IdleEventPriority = IdleLane; | |
| 5977 var currentUpdatePriority = NoLane; | |
| 5978 function getCurrentUpdatePriority() { | |
| 5979 return currentUpdatePriority; | |
| 5980 } | |
| 5981 function setCurrentUpdatePriority(newPriority) { | |
| 5982 currentUpdatePriority = newPriority; | |
| 5983 } | |
| 5984 function runWithPriority(priority, fn) { | |
| 5985 var previousPriority = currentUpdatePriority; | |
| 5986 | |
| 5987 try { | |
| 5988 currentUpdatePriority = priority; | |
| 5989 return fn(); | |
| 5990 } finally { | |
| 5991 currentUpdatePriority = previousPriority; | |
| 5992 } | |
| 5993 } | |
| 5994 function higherEventPriority(a, b) { | |
| 5995 return a !== 0 && a < b ? a : b; | |
| 5996 } | |
| 5997 function lowerEventPriority(a, b) { | |
| 5998 return a === 0 || a > b ? a : b; | |
| 5999 } | |
| 6000 function isHigherEventPriority(a, b) { | |
| 6001 return a !== 0 && a < b; | |
| 6002 } | |
| 6003 function lanesToEventPriority(lanes) { | |
| 6004 var lane = getHighestPriorityLane(lanes); | |
| 6005 | |
| 6006 if (!isHigherEventPriority(DiscreteEventPriority, lane)) { | |
| 6007 return DiscreteEventPriority; | |
| 6008 } | |
| 6009 | |
| 6010 if (!isHigherEventPriority(ContinuousEventPriority, lane)) { | |
| 6011 return ContinuousEventPriority; | |
| 6012 } | |
| 6013 | |
| 6014 if (includesNonIdleWork(lane)) { | |
| 6015 return DefaultEventPriority; | |
| 6016 } | |
| 6017 | |
| 6018 return IdleEventPriority; | |
| 6019 } | |
| 6020 | |
| 6021 // This is imported by the event replaying implementation in React DOM. It's | |
| 6022 // in a separate file to break a circular dependency between the renderer and | |
| 6023 // the reconciler. | |
| 6024 function isRootDehydrated(root) { | |
| 6025 var currentState = root.current.memoizedState; | |
| 6026 return currentState.isDehydrated; | |
| 6027 } | |
| 6028 | |
| 6029 var _attemptSynchronousHydration; | |
| 6030 | |
| 6031 function setAttemptSynchronousHydration(fn) { | |
| 6032 _attemptSynchronousHydration = fn; | |
| 6033 } | |
| 6034 function attemptSynchronousHydration(fiber) { | |
| 6035 _attemptSynchronousHydration(fiber); | |
| 6036 } | |
| 6037 var attemptContinuousHydration; | |
| 6038 function setAttemptContinuousHydration(fn) { | |
| 6039 attemptContinuousHydration = fn; | |
| 6040 } | |
| 6041 var attemptHydrationAtCurrentPriority; | |
| 6042 function setAttemptHydrationAtCurrentPriority(fn) { | |
| 6043 attemptHydrationAtCurrentPriority = fn; | |
| 6044 } | |
| 6045 var getCurrentUpdatePriority$1; | |
| 6046 function setGetCurrentUpdatePriority(fn) { | |
| 6047 getCurrentUpdatePriority$1 = fn; | |
| 6048 } | |
| 6049 var attemptHydrationAtPriority; | |
| 6050 function setAttemptHydrationAtPriority(fn) { | |
| 6051 attemptHydrationAtPriority = fn; | |
| 6052 } // TODO: Upgrade this definition once we're on a newer version of Flow that | |
| 6053 // has this definition built-in. | |
| 6054 | |
| 6055 var hasScheduledReplayAttempt = false; // The queue of discrete events to be replayed. | |
| 6056 | |
| 6057 var queuedDiscreteEvents = []; // Indicates if any continuous event targets are non-null for early bailout. | |
| 6058 // if the last target was dehydrated. | |
| 6059 | |
| 6060 var queuedFocus = null; | |
| 6061 var queuedDrag = null; | |
| 6062 var queuedMouse = null; // For pointer events there can be one latest event per pointerId. | |
| 6063 | |
| 6064 var queuedPointers = new Map(); | |
| 6065 var queuedPointerCaptures = new Map(); // We could consider replaying selectionchange and touchmoves too. | |
| 6066 | |
| 6067 var queuedExplicitHydrationTargets = []; | |
| 6068 var discreteReplayableEvents = ['mousedown', 'mouseup', 'touchcancel', 'touchend', 'touchstart', 'auxclick', 'dblclick', 'pointercancel', 'pointerdown', 'pointerup', 'dragend', 'dragstart', 'drop', 'compositionend', 'compositionstart', 'keydown', 'keypress', 'keyup', 'input', 'textInput', // Intentionally camelCase | |
| 6069 'copy', 'cut', 'paste', 'click', 'change', 'contextmenu', 'reset', 'submit']; | |
| 6070 function isDiscreteEventThatRequiresHydration(eventType) { | |
| 6071 return discreteReplayableEvents.indexOf(eventType) > -1; | |
| 6072 } | |
| 6073 | |
| 6074 function createQueuedReplayableEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6075 return { | |
| 6076 blockedOn: blockedOn, | |
| 6077 domEventName: domEventName, | |
| 6078 eventSystemFlags: eventSystemFlags, | |
| 6079 nativeEvent: nativeEvent, | |
| 6080 targetContainers: [targetContainer] | |
| 6081 }; | |
| 6082 } | |
| 6083 | |
| 6084 function clearIfContinuousEvent(domEventName, nativeEvent) { | |
| 6085 switch (domEventName) { | |
| 6086 case 'focusin': | |
| 6087 case 'focusout': | |
| 6088 queuedFocus = null; | |
| 6089 break; | |
| 6090 | |
| 6091 case 'dragenter': | |
| 6092 case 'dragleave': | |
| 6093 queuedDrag = null; | |
| 6094 break; | |
| 6095 | |
| 6096 case 'mouseover': | |
| 6097 case 'mouseout': | |
| 6098 queuedMouse = null; | |
| 6099 break; | |
| 6100 | |
| 6101 case 'pointerover': | |
| 6102 case 'pointerout': | |
| 6103 { | |
| 6104 var pointerId = nativeEvent.pointerId; | |
| 6105 queuedPointers.delete(pointerId); | |
| 6106 break; | |
| 6107 } | |
| 6108 | |
| 6109 case 'gotpointercapture': | |
| 6110 case 'lostpointercapture': | |
| 6111 { | |
| 6112 var _pointerId = nativeEvent.pointerId; | |
| 6113 queuedPointerCaptures.delete(_pointerId); | |
| 6114 break; | |
| 6115 } | |
| 6116 } | |
| 6117 } | |
| 6118 | |
| 6119 function accumulateOrCreateContinuousQueuedReplayableEvent(existingQueuedEvent, blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6120 if (existingQueuedEvent === null || existingQueuedEvent.nativeEvent !== nativeEvent) { | |
| 6121 var queuedEvent = createQueuedReplayableEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent); | |
| 6122 | |
| 6123 if (blockedOn !== null) { | |
| 6124 var _fiber2 = getInstanceFromNode(blockedOn); | |
| 6125 | |
| 6126 if (_fiber2 !== null) { | |
| 6127 // Attempt to increase the priority of this target. | |
| 6128 attemptContinuousHydration(_fiber2); | |
| 6129 } | |
| 6130 } | |
| 6131 | |
| 6132 return queuedEvent; | |
| 6133 } // If we have already queued this exact event, then it's because | |
| 6134 // the different event systems have different DOM event listeners. | |
| 6135 // We can accumulate the flags, and the targetContainers, and | |
| 6136 // store a single event to be replayed. | |
| 6137 | |
| 6138 | |
| 6139 existingQueuedEvent.eventSystemFlags |= eventSystemFlags; | |
| 6140 var targetContainers = existingQueuedEvent.targetContainers; | |
| 6141 | |
| 6142 if (targetContainer !== null && targetContainers.indexOf(targetContainer) === -1) { | |
| 6143 targetContainers.push(targetContainer); | |
| 6144 } | |
| 6145 | |
| 6146 return existingQueuedEvent; | |
| 6147 } | |
| 6148 | |
| 6149 function queueIfContinuousEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6150 // These set relatedTarget to null because the replayed event will be treated as if we | |
| 6151 // moved from outside the window (no target) onto the target once it hydrates. | |
| 6152 // Instead of mutating we could clone the event. | |
| 6153 switch (domEventName) { | |
| 6154 case 'focusin': | |
| 6155 { | |
| 6156 var focusEvent = nativeEvent; | |
| 6157 queuedFocus = accumulateOrCreateContinuousQueuedReplayableEvent(queuedFocus, blockedOn, domEventName, eventSystemFlags, targetContainer, focusEvent); | |
| 6158 return true; | |
| 6159 } | |
| 6160 | |
| 6161 case 'dragenter': | |
| 6162 { | |
| 6163 var dragEvent = nativeEvent; | |
| 6164 queuedDrag = accumulateOrCreateContinuousQueuedReplayableEvent(queuedDrag, blockedOn, domEventName, eventSystemFlags, targetContainer, dragEvent); | |
| 6165 return true; | |
| 6166 } | |
| 6167 | |
| 6168 case 'mouseover': | |
| 6169 { | |
| 6170 var mouseEvent = nativeEvent; | |
| 6171 queuedMouse = accumulateOrCreateContinuousQueuedReplayableEvent(queuedMouse, blockedOn, domEventName, eventSystemFlags, targetContainer, mouseEvent); | |
| 6172 return true; | |
| 6173 } | |
| 6174 | |
| 6175 case 'pointerover': | |
| 6176 { | |
| 6177 var pointerEvent = nativeEvent; | |
| 6178 var pointerId = pointerEvent.pointerId; | |
| 6179 queuedPointers.set(pointerId, accumulateOrCreateContinuousQueuedReplayableEvent(queuedPointers.get(pointerId) || null, blockedOn, domEventName, eventSystemFlags, targetContainer, pointerEvent)); | |
| 6180 return true; | |
| 6181 } | |
| 6182 | |
| 6183 case 'gotpointercapture': | |
| 6184 { | |
| 6185 var _pointerEvent = nativeEvent; | |
| 6186 var _pointerId2 = _pointerEvent.pointerId; | |
| 6187 queuedPointerCaptures.set(_pointerId2, accumulateOrCreateContinuousQueuedReplayableEvent(queuedPointerCaptures.get(_pointerId2) || null, blockedOn, domEventName, eventSystemFlags, targetContainer, _pointerEvent)); | |
| 6188 return true; | |
| 6189 } | |
| 6190 } | |
| 6191 | |
| 6192 return false; | |
| 6193 } // Check if this target is unblocked. Returns true if it's unblocked. | |
| 6194 | |
| 6195 function attemptExplicitHydrationTarget(queuedTarget) { | |
| 6196 // TODO: This function shares a lot of logic with findInstanceBlockingEvent. | |
| 6197 // Try to unify them. It's a bit tricky since it would require two return | |
| 6198 // values. | |
| 6199 var targetInst = getClosestInstanceFromNode(queuedTarget.target); | |
| 6200 | |
| 6201 if (targetInst !== null) { | |
| 6202 var nearestMounted = getNearestMountedFiber(targetInst); | |
| 6203 | |
| 6204 if (nearestMounted !== null) { | |
| 6205 var tag = nearestMounted.tag; | |
| 6206 | |
| 6207 if (tag === SuspenseComponent) { | |
| 6208 var instance = getSuspenseInstanceFromFiber(nearestMounted); | |
| 6209 | |
| 6210 if (instance !== null) { | |
| 6211 // We're blocked on hydrating this boundary. | |
| 6212 // Increase its priority. | |
| 6213 queuedTarget.blockedOn = instance; | |
| 6214 attemptHydrationAtPriority(queuedTarget.priority, function () { | |
| 6215 attemptHydrationAtCurrentPriority(nearestMounted); | |
| 6216 }); | |
| 6217 return; | |
| 6218 } | |
| 6219 } else if (tag === HostRoot) { | |
| 6220 var root = nearestMounted.stateNode; | |
| 6221 | |
| 6222 if (isRootDehydrated(root)) { | |
| 6223 queuedTarget.blockedOn = getContainerFromFiber(nearestMounted); // We don't currently have a way to increase the priority of | |
| 6224 // a root other than sync. | |
| 6225 | |
| 6226 return; | |
| 6227 } | |
| 6228 } | |
| 6229 } | |
| 6230 } | |
| 6231 | |
| 6232 queuedTarget.blockedOn = null; | |
| 6233 } | |
| 6234 | |
| 6235 function queueExplicitHydrationTarget(target) { | |
| 6236 // TODO: This will read the priority if it's dispatched by the React | |
| 6237 // event system but not native events. Should read window.event.type, like | |
| 6238 // we do for updates (getCurrentEventPriority). | |
| 6239 var updatePriority = getCurrentUpdatePriority$1(); | |
| 6240 var queuedTarget = { | |
| 6241 blockedOn: null, | |
| 6242 target: target, | |
| 6243 priority: updatePriority | |
| 6244 }; | |
| 6245 var i = 0; | |
| 6246 | |
| 6247 for (; i < queuedExplicitHydrationTargets.length; i++) { | |
| 6248 // Stop once we hit the first target with lower priority than | |
| 6249 if (!isHigherEventPriority(updatePriority, queuedExplicitHydrationTargets[i].priority)) { | |
| 6250 break; | |
| 6251 } | |
| 6252 } | |
| 6253 | |
| 6254 queuedExplicitHydrationTargets.splice(i, 0, queuedTarget); | |
| 6255 | |
| 6256 if (i === 0) { | |
| 6257 attemptExplicitHydrationTarget(queuedTarget); | |
| 6258 } | |
| 6259 } | |
| 6260 | |
| 6261 function attemptReplayContinuousQueuedEvent(queuedEvent) { | |
| 6262 if (queuedEvent.blockedOn !== null) { | |
| 6263 return false; | |
| 6264 } | |
| 6265 | |
| 6266 var targetContainers = queuedEvent.targetContainers; | |
| 6267 | |
| 6268 while (targetContainers.length > 0) { | |
| 6269 var targetContainer = targetContainers[0]; | |
| 6270 var nextBlockedOn = findInstanceBlockingEvent(queuedEvent.domEventName, queuedEvent.eventSystemFlags, targetContainer, queuedEvent.nativeEvent); | |
| 6271 | |
| 6272 if (nextBlockedOn === null) { | |
| 6273 { | |
| 6274 var nativeEvent = queuedEvent.nativeEvent; | |
| 6275 var nativeEventClone = new nativeEvent.constructor(nativeEvent.type, nativeEvent); | |
| 6276 setReplayingEvent(nativeEventClone); | |
| 6277 nativeEvent.target.dispatchEvent(nativeEventClone); | |
| 6278 resetReplayingEvent(); | |
| 6279 } | |
| 6280 } else { | |
| 6281 // We're still blocked. Try again later. | |
| 6282 var _fiber3 = getInstanceFromNode(nextBlockedOn); | |
| 6283 | |
| 6284 if (_fiber3 !== null) { | |
| 6285 attemptContinuousHydration(_fiber3); | |
| 6286 } | |
| 6287 | |
| 6288 queuedEvent.blockedOn = nextBlockedOn; | |
| 6289 return false; | |
| 6290 } // This target container was successfully dispatched. Try the next. | |
| 6291 | |
| 6292 | |
| 6293 targetContainers.shift(); | |
| 6294 } | |
| 6295 | |
| 6296 return true; | |
| 6297 } | |
| 6298 | |
| 6299 function attemptReplayContinuousQueuedEventInMap(queuedEvent, key, map) { | |
| 6300 if (attemptReplayContinuousQueuedEvent(queuedEvent)) { | |
| 6301 map.delete(key); | |
| 6302 } | |
| 6303 } | |
| 6304 | |
| 6305 function replayUnblockedEvents() { | |
| 6306 hasScheduledReplayAttempt = false; | |
| 6307 | |
| 6308 | |
| 6309 if (queuedFocus !== null && attemptReplayContinuousQueuedEvent(queuedFocus)) { | |
| 6310 queuedFocus = null; | |
| 6311 } | |
| 6312 | |
| 6313 if (queuedDrag !== null && attemptReplayContinuousQueuedEvent(queuedDrag)) { | |
| 6314 queuedDrag = null; | |
| 6315 } | |
| 6316 | |
| 6317 if (queuedMouse !== null && attemptReplayContinuousQueuedEvent(queuedMouse)) { | |
| 6318 queuedMouse = null; | |
| 6319 } | |
| 6320 | |
| 6321 queuedPointers.forEach(attemptReplayContinuousQueuedEventInMap); | |
| 6322 queuedPointerCaptures.forEach(attemptReplayContinuousQueuedEventInMap); | |
| 6323 } | |
| 6324 | |
| 6325 function scheduleCallbackIfUnblocked(queuedEvent, unblocked) { | |
| 6326 if (queuedEvent.blockedOn === unblocked) { | |
| 6327 queuedEvent.blockedOn = null; | |
| 6328 | |
| 6329 if (!hasScheduledReplayAttempt) { | |
| 6330 hasScheduledReplayAttempt = true; // Schedule a callback to attempt replaying as many events as are | |
| 6331 // now unblocked. This first might not actually be unblocked yet. | |
| 6332 // We could check it early to avoid scheduling an unnecessary callback. | |
| 6333 | |
| 6334 unstable_scheduleCallback(unstable_NormalPriority, replayUnblockedEvents); | |
| 6335 } | |
| 6336 } | |
| 6337 } | |
| 6338 | |
| 6339 function retryIfBlockedOn(unblocked) { | |
| 6340 // Mark anything that was blocked on this as no longer blocked | |
| 6341 // and eligible for a replay. | |
| 6342 if (queuedDiscreteEvents.length > 0) { | |
| 6343 scheduleCallbackIfUnblocked(queuedDiscreteEvents[0], unblocked); // This is a exponential search for each boundary that commits. I think it's | |
| 6344 // worth it because we expect very few discrete events to queue up and once | |
| 6345 // we are actually fully unblocked it will be fast to replay them. | |
| 6346 | |
| 6347 for (var i = 1; i < queuedDiscreteEvents.length; i++) { | |
| 6348 var queuedEvent = queuedDiscreteEvents[i]; | |
| 6349 | |
| 6350 if (queuedEvent.blockedOn === unblocked) { | |
| 6351 queuedEvent.blockedOn = null; | |
| 6352 } | |
| 6353 } | |
| 6354 } | |
| 6355 | |
| 6356 if (queuedFocus !== null) { | |
| 6357 scheduleCallbackIfUnblocked(queuedFocus, unblocked); | |
| 6358 } | |
| 6359 | |
| 6360 if (queuedDrag !== null) { | |
| 6361 scheduleCallbackIfUnblocked(queuedDrag, unblocked); | |
| 6362 } | |
| 6363 | |
| 6364 if (queuedMouse !== null) { | |
| 6365 scheduleCallbackIfUnblocked(queuedMouse, unblocked); | |
| 6366 } | |
| 6367 | |
| 6368 var unblock = function (queuedEvent) { | |
| 6369 return scheduleCallbackIfUnblocked(queuedEvent, unblocked); | |
| 6370 }; | |
| 6371 | |
| 6372 queuedPointers.forEach(unblock); | |
| 6373 queuedPointerCaptures.forEach(unblock); | |
| 6374 | |
| 6375 for (var _i = 0; _i < queuedExplicitHydrationTargets.length; _i++) { | |
| 6376 var queuedTarget = queuedExplicitHydrationTargets[_i]; | |
| 6377 | |
| 6378 if (queuedTarget.blockedOn === unblocked) { | |
| 6379 queuedTarget.blockedOn = null; | |
| 6380 } | |
| 6381 } | |
| 6382 | |
| 6383 while (queuedExplicitHydrationTargets.length > 0) { | |
| 6384 var nextExplicitTarget = queuedExplicitHydrationTargets[0]; | |
| 6385 | |
| 6386 if (nextExplicitTarget.blockedOn !== null) { | |
| 6387 // We're still blocked. | |
| 6388 break; | |
| 6389 } else { | |
| 6390 attemptExplicitHydrationTarget(nextExplicitTarget); | |
| 6391 | |
| 6392 if (nextExplicitTarget.blockedOn === null) { | |
| 6393 // We're unblocked. | |
| 6394 queuedExplicitHydrationTargets.shift(); | |
| 6395 } | |
| 6396 } | |
| 6397 } | |
| 6398 } | |
| 6399 | |
| 6400 var ReactCurrentBatchConfig = ReactSharedInternals.ReactCurrentBatchConfig; // TODO: can we stop exporting these? | |
| 6401 | |
| 6402 var _enabled = true; // This is exported in FB builds for use by legacy FB layer infra. | |
| 6403 // We'd like to remove this but it's not clear if this is safe. | |
| 6404 | |
| 6405 function setEnabled(enabled) { | |
| 6406 _enabled = !!enabled; | |
| 6407 } | |
| 6408 function isEnabled() { | |
| 6409 return _enabled; | |
| 6410 } | |
| 6411 function createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags) { | |
| 6412 var eventPriority = getEventPriority(domEventName); | |
| 6413 var listenerWrapper; | |
| 6414 | |
| 6415 switch (eventPriority) { | |
| 6416 case DiscreteEventPriority: | |
| 6417 listenerWrapper = dispatchDiscreteEvent; | |
| 6418 break; | |
| 6419 | |
| 6420 case ContinuousEventPriority: | |
| 6421 listenerWrapper = dispatchContinuousEvent; | |
| 6422 break; | |
| 6423 | |
| 6424 case DefaultEventPriority: | |
| 6425 default: | |
| 6426 listenerWrapper = dispatchEvent; | |
| 6427 break; | |
| 6428 } | |
| 6429 | |
| 6430 return listenerWrapper.bind(null, domEventName, eventSystemFlags, targetContainer); | |
| 6431 } | |
| 6432 | |
| 6433 function dispatchDiscreteEvent(domEventName, eventSystemFlags, container, nativeEvent) { | |
| 6434 var previousPriority = getCurrentUpdatePriority(); | |
| 6435 var prevTransition = ReactCurrentBatchConfig.transition; | |
| 6436 ReactCurrentBatchConfig.transition = null; | |
| 6437 | |
| 6438 try { | |
| 6439 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 6440 dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent); | |
| 6441 } finally { | |
| 6442 setCurrentUpdatePriority(previousPriority); | |
| 6443 ReactCurrentBatchConfig.transition = prevTransition; | |
| 6444 } | |
| 6445 } | |
| 6446 | |
| 6447 function dispatchContinuousEvent(domEventName, eventSystemFlags, container, nativeEvent) { | |
| 6448 var previousPriority = getCurrentUpdatePriority(); | |
| 6449 var prevTransition = ReactCurrentBatchConfig.transition; | |
| 6450 ReactCurrentBatchConfig.transition = null; | |
| 6451 | |
| 6452 try { | |
| 6453 setCurrentUpdatePriority(ContinuousEventPriority); | |
| 6454 dispatchEvent(domEventName, eventSystemFlags, container, nativeEvent); | |
| 6455 } finally { | |
| 6456 setCurrentUpdatePriority(previousPriority); | |
| 6457 ReactCurrentBatchConfig.transition = prevTransition; | |
| 6458 } | |
| 6459 } | |
| 6460 | |
| 6461 function dispatchEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6462 if (!_enabled) { | |
| 6463 return; | |
| 6464 } | |
| 6465 | |
| 6466 { | |
| 6467 dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(domEventName, eventSystemFlags, targetContainer, nativeEvent); | |
| 6468 } | |
| 6469 } | |
| 6470 | |
| 6471 function dispatchEventWithEnableCapturePhaseSelectiveHydrationWithoutDiscreteEventReplay(domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6472 var blockedOn = findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent); | |
| 6473 | |
| 6474 if (blockedOn === null) { | |
| 6475 dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, return_targetInst, targetContainer); | |
| 6476 clearIfContinuousEvent(domEventName, nativeEvent); | |
| 6477 return; | |
| 6478 } | |
| 6479 | |
| 6480 if (queueIfContinuousEvent(blockedOn, domEventName, eventSystemFlags, targetContainer, nativeEvent)) { | |
| 6481 nativeEvent.stopPropagation(); | |
| 6482 return; | |
| 6483 } // We need to clear only if we didn't queue because | |
| 6484 // queueing is accumulative. | |
| 6485 | |
| 6486 | |
| 6487 clearIfContinuousEvent(domEventName, nativeEvent); | |
| 6488 | |
| 6489 if (eventSystemFlags & IS_CAPTURE_PHASE && isDiscreteEventThatRequiresHydration(domEventName)) { | |
| 6490 while (blockedOn !== null) { | |
| 6491 var fiber = getInstanceFromNode(blockedOn); | |
| 6492 | |
| 6493 if (fiber !== null) { | |
| 6494 attemptSynchronousHydration(fiber); | |
| 6495 } | |
| 6496 | |
| 6497 var nextBlockedOn = findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent); | |
| 6498 | |
| 6499 if (nextBlockedOn === null) { | |
| 6500 dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, return_targetInst, targetContainer); | |
| 6501 } | |
| 6502 | |
| 6503 if (nextBlockedOn === blockedOn) { | |
| 6504 break; | |
| 6505 } | |
| 6506 | |
| 6507 blockedOn = nextBlockedOn; | |
| 6508 } | |
| 6509 | |
| 6510 if (blockedOn !== null) { | |
| 6511 nativeEvent.stopPropagation(); | |
| 6512 } | |
| 6513 | |
| 6514 return; | |
| 6515 } // This is not replayable so we'll invoke it but without a target, | |
| 6516 // in case the event system needs to trace it. | |
| 6517 | |
| 6518 | |
| 6519 dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, null, targetContainer); | |
| 6520 } | |
| 6521 | |
| 6522 var return_targetInst = null; // Returns a SuspenseInstance or Container if it's blocked. | |
| 6523 // The return_targetInst field above is conceptually part of the return value. | |
| 6524 | |
| 6525 function findInstanceBlockingEvent(domEventName, eventSystemFlags, targetContainer, nativeEvent) { | |
| 6526 // TODO: Warn if _enabled is false. | |
| 6527 return_targetInst = null; | |
| 6528 var nativeEventTarget = getEventTarget(nativeEvent); | |
| 6529 var targetInst = getClosestInstanceFromNode(nativeEventTarget); | |
| 6530 | |
| 6531 if (targetInst !== null) { | |
| 6532 var nearestMounted = getNearestMountedFiber(targetInst); | |
| 6533 | |
| 6534 if (nearestMounted === null) { | |
| 6535 // This tree has been unmounted already. Dispatch without a target. | |
| 6536 targetInst = null; | |
| 6537 } else { | |
| 6538 var tag = nearestMounted.tag; | |
| 6539 | |
| 6540 if (tag === SuspenseComponent) { | |
| 6541 var instance = getSuspenseInstanceFromFiber(nearestMounted); | |
| 6542 | |
| 6543 if (instance !== null) { | |
| 6544 // Queue the event to be replayed later. Abort dispatching since we | |
| 6545 // don't want this event dispatched twice through the event system. | |
| 6546 // TODO: If this is the first discrete event in the queue. Schedule an increased | |
| 6547 // priority for this boundary. | |
| 6548 return instance; | |
| 6549 } // This shouldn't happen, something went wrong but to avoid blocking | |
| 6550 // the whole system, dispatch the event without a target. | |
| 6551 // TODO: Warn. | |
| 6552 | |
| 6553 | |
| 6554 targetInst = null; | |
| 6555 } else if (tag === HostRoot) { | |
| 6556 var root = nearestMounted.stateNode; | |
| 6557 | |
| 6558 if (isRootDehydrated(root)) { | |
| 6559 // If this happens during a replay something went wrong and it might block | |
| 6560 // the whole system. | |
| 6561 return getContainerFromFiber(nearestMounted); | |
| 6562 } | |
| 6563 | |
| 6564 targetInst = null; | |
| 6565 } else if (nearestMounted !== targetInst) { | |
| 6566 // If we get an event (ex: img onload) before committing that | |
| 6567 // component's mount, ignore it for now (that is, treat it as if it was an | |
| 6568 // event on a non-React tree). We might also consider queueing events and | |
| 6569 // dispatching them after the mount. | |
| 6570 targetInst = null; | |
| 6571 } | |
| 6572 } | |
| 6573 } | |
| 6574 | |
| 6575 return_targetInst = targetInst; // We're not blocked on anything. | |
| 6576 | |
| 6577 return null; | |
| 6578 } | |
| 6579 function getEventPriority(domEventName) { | |
| 6580 switch (domEventName) { | |
| 6581 // Used by SimpleEventPlugin: | |
| 6582 case 'cancel': | |
| 6583 case 'click': | |
| 6584 case 'close': | |
| 6585 case 'contextmenu': | |
| 6586 case 'copy': | |
| 6587 case 'cut': | |
| 6588 case 'auxclick': | |
| 6589 case 'dblclick': | |
| 6590 case 'dragend': | |
| 6591 case 'dragstart': | |
| 6592 case 'drop': | |
| 6593 case 'focusin': | |
| 6594 case 'focusout': | |
| 6595 case 'input': | |
| 6596 case 'invalid': | |
| 6597 case 'keydown': | |
| 6598 case 'keypress': | |
| 6599 case 'keyup': | |
| 6600 case 'mousedown': | |
| 6601 case 'mouseup': | |
| 6602 case 'paste': | |
| 6603 case 'pause': | |
| 6604 case 'play': | |
| 6605 case 'pointercancel': | |
| 6606 case 'pointerdown': | |
| 6607 case 'pointerup': | |
| 6608 case 'ratechange': | |
| 6609 case 'reset': | |
| 6610 case 'resize': | |
| 6611 case 'seeked': | |
| 6612 case 'submit': | |
| 6613 case 'touchcancel': | |
| 6614 case 'touchend': | |
| 6615 case 'touchstart': | |
| 6616 case 'volumechange': // Used by polyfills: | |
| 6617 // eslint-disable-next-line no-fallthrough | |
| 6618 | |
| 6619 case 'change': | |
| 6620 case 'selectionchange': | |
| 6621 case 'textInput': | |
| 6622 case 'compositionstart': | |
| 6623 case 'compositionend': | |
| 6624 case 'compositionupdate': // Only enableCreateEventHandleAPI: | |
| 6625 // eslint-disable-next-line no-fallthrough | |
| 6626 | |
| 6627 case 'beforeblur': | |
| 6628 case 'afterblur': // Not used by React but could be by user code: | |
| 6629 // eslint-disable-next-line no-fallthrough | |
| 6630 | |
| 6631 case 'beforeinput': | |
| 6632 case 'blur': | |
| 6633 case 'fullscreenchange': | |
| 6634 case 'focus': | |
| 6635 case 'hashchange': | |
| 6636 case 'popstate': | |
| 6637 case 'select': | |
| 6638 case 'selectstart': | |
| 6639 return DiscreteEventPriority; | |
| 6640 | |
| 6641 case 'drag': | |
| 6642 case 'dragenter': | |
| 6643 case 'dragexit': | |
| 6644 case 'dragleave': | |
| 6645 case 'dragover': | |
| 6646 case 'mousemove': | |
| 6647 case 'mouseout': | |
| 6648 case 'mouseover': | |
| 6649 case 'pointermove': | |
| 6650 case 'pointerout': | |
| 6651 case 'pointerover': | |
| 6652 case 'scroll': | |
| 6653 case 'toggle': | |
| 6654 case 'touchmove': | |
| 6655 case 'wheel': // Not used by React but could be by user code: | |
| 6656 // eslint-disable-next-line no-fallthrough | |
| 6657 | |
| 6658 case 'mouseenter': | |
| 6659 case 'mouseleave': | |
| 6660 case 'pointerenter': | |
| 6661 case 'pointerleave': | |
| 6662 return ContinuousEventPriority; | |
| 6663 | |
| 6664 case 'message': | |
| 6665 { | |
| 6666 // We might be in the Scheduler callback. | |
| 6667 // Eventually this mechanism will be replaced by a check | |
| 6668 // of the current priority on the native scheduler. | |
| 6669 var schedulerPriority = getCurrentPriorityLevel(); | |
| 6670 | |
| 6671 switch (schedulerPriority) { | |
| 6672 case ImmediatePriority: | |
| 6673 return DiscreteEventPriority; | |
| 6674 | |
| 6675 case UserBlockingPriority: | |
| 6676 return ContinuousEventPriority; | |
| 6677 | |
| 6678 case NormalPriority: | |
| 6679 case LowPriority: | |
| 6680 // TODO: Handle LowSchedulerPriority, somehow. Maybe the same lane as hydration. | |
| 6681 return DefaultEventPriority; | |
| 6682 | |
| 6683 case IdlePriority: | |
| 6684 return IdleEventPriority; | |
| 6685 | |
| 6686 default: | |
| 6687 return DefaultEventPriority; | |
| 6688 } | |
| 6689 } | |
| 6690 | |
| 6691 default: | |
| 6692 return DefaultEventPriority; | |
| 6693 } | |
| 6694 } | |
| 6695 | |
| 6696 function addEventBubbleListener(target, eventType, listener) { | |
| 6697 target.addEventListener(eventType, listener, false); | |
| 6698 return listener; | |
| 6699 } | |
| 6700 function addEventCaptureListener(target, eventType, listener) { | |
| 6701 target.addEventListener(eventType, listener, true); | |
| 6702 return listener; | |
| 6703 } | |
| 6704 function addEventCaptureListenerWithPassiveFlag(target, eventType, listener, passive) { | |
| 6705 target.addEventListener(eventType, listener, { | |
| 6706 capture: true, | |
| 6707 passive: passive | |
| 6708 }); | |
| 6709 return listener; | |
| 6710 } | |
| 6711 function addEventBubbleListenerWithPassiveFlag(target, eventType, listener, passive) { | |
| 6712 target.addEventListener(eventType, listener, { | |
| 6713 passive: passive | |
| 6714 }); | |
| 6715 return listener; | |
| 6716 } | |
| 6717 | |
| 6718 /** | |
| 6719 * These variables store information about text content of a target node, | |
| 6720 * allowing comparison of content before and after a given event. | |
| 6721 * | |
| 6722 * Identify the node where selection currently begins, then observe | |
| 6723 * both its text content and its current position in the DOM. Since the | |
| 6724 * browser may natively replace the target node during composition, we can | |
| 6725 * use its position to find its replacement. | |
| 6726 * | |
| 6727 * | |
| 6728 */ | |
| 6729 var root = null; | |
| 6730 var startText = null; | |
| 6731 var fallbackText = null; | |
| 6732 function initialize(nativeEventTarget) { | |
| 6733 root = nativeEventTarget; | |
| 6734 startText = getText(); | |
| 6735 return true; | |
| 6736 } | |
| 6737 function reset() { | |
| 6738 root = null; | |
| 6739 startText = null; | |
| 6740 fallbackText = null; | |
| 6741 } | |
| 6742 function getData() { | |
| 6743 if (fallbackText) { | |
| 6744 return fallbackText; | |
| 6745 } | |
| 6746 | |
| 6747 var start; | |
| 6748 var startValue = startText; | |
| 6749 var startLength = startValue.length; | |
| 6750 var end; | |
| 6751 var endValue = getText(); | |
| 6752 var endLength = endValue.length; | |
| 6753 | |
| 6754 for (start = 0; start < startLength; start++) { | |
| 6755 if (startValue[start] !== endValue[start]) { | |
| 6756 break; | |
| 6757 } | |
| 6758 } | |
| 6759 | |
| 6760 var minEnd = startLength - start; | |
| 6761 | |
| 6762 for (end = 1; end <= minEnd; end++) { | |
| 6763 if (startValue[startLength - end] !== endValue[endLength - end]) { | |
| 6764 break; | |
| 6765 } | |
| 6766 } | |
| 6767 | |
| 6768 var sliceTail = end > 1 ? 1 - end : undefined; | |
| 6769 fallbackText = endValue.slice(start, sliceTail); | |
| 6770 return fallbackText; | |
| 6771 } | |
| 6772 function getText() { | |
| 6773 if ('value' in root) { | |
| 6774 return root.value; | |
| 6775 } | |
| 6776 | |
| 6777 return root.textContent; | |
| 6778 } | |
| 6779 | |
| 6780 /** | |
| 6781 * `charCode` represents the actual "character code" and is safe to use with | |
| 6782 * `String.fromCharCode`. As such, only keys that correspond to printable | |
| 6783 * characters produce a valid `charCode`, the only exception to this is Enter. | |
| 6784 * The Tab-key is considered non-printable and does not have a `charCode`, | |
| 6785 * presumably because it does not produce a tab-character in browsers. | |
| 6786 * | |
| 6787 * @param {object} nativeEvent Native browser event. | |
| 6788 * @return {number} Normalized `charCode` property. | |
| 6789 */ | |
| 6790 function getEventCharCode(nativeEvent) { | |
| 6791 var charCode; | |
| 6792 var keyCode = nativeEvent.keyCode; | |
| 6793 | |
| 6794 if ('charCode' in nativeEvent) { | |
| 6795 charCode = nativeEvent.charCode; // FF does not set `charCode` for the Enter-key, check against `keyCode`. | |
| 6796 | |
| 6797 if (charCode === 0 && keyCode === 13) { | |
| 6798 charCode = 13; | |
| 6799 } | |
| 6800 } else { | |
| 6801 // IE8 does not implement `charCode`, but `keyCode` has the correct value. | |
| 6802 charCode = keyCode; | |
| 6803 } // IE and Edge (on Windows) and Chrome / Safari (on Windows and Linux) | |
| 6804 // report Enter as charCode 10 when ctrl is pressed. | |
| 6805 | |
| 6806 | |
| 6807 if (charCode === 10) { | |
| 6808 charCode = 13; | |
| 6809 } // Some non-printable keys are reported in `charCode`/`keyCode`, discard them. | |
| 6810 // Must not discard the (non-)printable Enter-key. | |
| 6811 | |
| 6812 | |
| 6813 if (charCode >= 32 || charCode === 13) { | |
| 6814 return charCode; | |
| 6815 } | |
| 6816 | |
| 6817 return 0; | |
| 6818 } | |
| 6819 | |
| 6820 function functionThatReturnsTrue() { | |
| 6821 return true; | |
| 6822 } | |
| 6823 | |
| 6824 function functionThatReturnsFalse() { | |
| 6825 return false; | |
| 6826 } // This is intentionally a factory so that we have different returned constructors. | |
| 6827 // If we had a single constructor, it would be megamorphic and engines would deopt. | |
| 6828 | |
| 6829 | |
| 6830 function createSyntheticEvent(Interface) { | |
| 6831 /** | |
| 6832 * Synthetic events are dispatched by event plugins, typically in response to a | |
| 6833 * top-level event delegation handler. | |
| 6834 * | |
| 6835 * These systems should generally use pooling to reduce the frequency of garbage | |
| 6836 * collection. The system should check `isPersistent` to determine whether the | |
| 6837 * event should be released into the pool after being dispatched. Users that | |
| 6838 * need a persisted event should invoke `persist`. | |
| 6839 * | |
| 6840 * Synthetic events (and subclasses) implement the DOM Level 3 Events API by | |
| 6841 * normalizing browser quirks. Subclasses do not necessarily have to implement a | |
| 6842 * DOM interface; custom application-specific events can also subclass this. | |
| 6843 */ | |
| 6844 function SyntheticBaseEvent(reactName, reactEventType, targetInst, nativeEvent, nativeEventTarget) { | |
| 6845 this._reactName = reactName; | |
| 6846 this._targetInst = targetInst; | |
| 6847 this.type = reactEventType; | |
| 6848 this.nativeEvent = nativeEvent; | |
| 6849 this.target = nativeEventTarget; | |
| 6850 this.currentTarget = null; | |
| 6851 | |
| 6852 for (var _propName in Interface) { | |
| 6853 if (!Interface.hasOwnProperty(_propName)) { | |
| 6854 continue; | |
| 6855 } | |
| 6856 | |
| 6857 var normalize = Interface[_propName]; | |
| 6858 | |
| 6859 if (normalize) { | |
| 6860 this[_propName] = normalize(nativeEvent); | |
| 6861 } else { | |
| 6862 this[_propName] = nativeEvent[_propName]; | |
| 6863 } | |
| 6864 } | |
| 6865 | |
| 6866 var defaultPrevented = nativeEvent.defaultPrevented != null ? nativeEvent.defaultPrevented : nativeEvent.returnValue === false; | |
| 6867 | |
| 6868 if (defaultPrevented) { | |
| 6869 this.isDefaultPrevented = functionThatReturnsTrue; | |
| 6870 } else { | |
| 6871 this.isDefaultPrevented = functionThatReturnsFalse; | |
| 6872 } | |
| 6873 | |
| 6874 this.isPropagationStopped = functionThatReturnsFalse; | |
| 6875 return this; | |
| 6876 } | |
| 6877 | |
| 6878 assign(SyntheticBaseEvent.prototype, { | |
| 6879 preventDefault: function () { | |
| 6880 this.defaultPrevented = true; | |
| 6881 var event = this.nativeEvent; | |
| 6882 | |
| 6883 if (!event) { | |
| 6884 return; | |
| 6885 } | |
| 6886 | |
| 6887 if (event.preventDefault) { | |
| 6888 event.preventDefault(); // $FlowFixMe - flow is not aware of `unknown` in IE | |
| 6889 } else if (typeof event.returnValue !== 'unknown') { | |
| 6890 event.returnValue = false; | |
| 6891 } | |
| 6892 | |
| 6893 this.isDefaultPrevented = functionThatReturnsTrue; | |
| 6894 }, | |
| 6895 stopPropagation: function () { | |
| 6896 var event = this.nativeEvent; | |
| 6897 | |
| 6898 if (!event) { | |
| 6899 return; | |
| 6900 } | |
| 6901 | |
| 6902 if (event.stopPropagation) { | |
| 6903 event.stopPropagation(); // $FlowFixMe - flow is not aware of `unknown` in IE | |
| 6904 } else if (typeof event.cancelBubble !== 'unknown') { | |
| 6905 // The ChangeEventPlugin registers a "propertychange" event for | |
| 6906 // IE. This event does not support bubbling or cancelling, and | |
| 6907 // any references to cancelBubble throw "Member not found". A | |
| 6908 // typeof check of "unknown" circumvents this issue (and is also | |
| 6909 // IE specific). | |
| 6910 event.cancelBubble = true; | |
| 6911 } | |
| 6912 | |
| 6913 this.isPropagationStopped = functionThatReturnsTrue; | |
| 6914 }, | |
| 6915 | |
| 6916 /** | |
| 6917 * We release all dispatched `SyntheticEvent`s after each event loop, adding | |
| 6918 * them back into the pool. This allows a way to hold onto a reference that | |
| 6919 * won't be added back into the pool. | |
| 6920 */ | |
| 6921 persist: function () {// Modern event system doesn't use pooling. | |
| 6922 }, | |
| 6923 | |
| 6924 /** | |
| 6925 * Checks if this event should be released back into the pool. | |
| 6926 * | |
| 6927 * @return {boolean} True if this should not be released, false otherwise. | |
| 6928 */ | |
| 6929 isPersistent: functionThatReturnsTrue | |
| 6930 }); | |
| 6931 return SyntheticBaseEvent; | |
| 6932 } | |
| 6933 /** | |
| 6934 * @interface Event | |
| 6935 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 6936 */ | |
| 6937 | |
| 6938 | |
| 6939 var EventInterface = { | |
| 6940 eventPhase: 0, | |
| 6941 bubbles: 0, | |
| 6942 cancelable: 0, | |
| 6943 timeStamp: function (event) { | |
| 6944 return event.timeStamp || Date.now(); | |
| 6945 }, | |
| 6946 defaultPrevented: 0, | |
| 6947 isTrusted: 0 | |
| 6948 }; | |
| 6949 var SyntheticEvent = createSyntheticEvent(EventInterface); | |
| 6950 | |
| 6951 var UIEventInterface = assign({}, EventInterface, { | |
| 6952 view: 0, | |
| 6953 detail: 0 | |
| 6954 }); | |
| 6955 | |
| 6956 var SyntheticUIEvent = createSyntheticEvent(UIEventInterface); | |
| 6957 var lastMovementX; | |
| 6958 var lastMovementY; | |
| 6959 var lastMouseEvent; | |
| 6960 | |
| 6961 function updateMouseMovementPolyfillState(event) { | |
| 6962 if (event !== lastMouseEvent) { | |
| 6963 if (lastMouseEvent && event.type === 'mousemove') { | |
| 6964 lastMovementX = event.screenX - lastMouseEvent.screenX; | |
| 6965 lastMovementY = event.screenY - lastMouseEvent.screenY; | |
| 6966 } else { | |
| 6967 lastMovementX = 0; | |
| 6968 lastMovementY = 0; | |
| 6969 } | |
| 6970 | |
| 6971 lastMouseEvent = event; | |
| 6972 } | |
| 6973 } | |
| 6974 /** | |
| 6975 * @interface MouseEvent | |
| 6976 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 6977 */ | |
| 6978 | |
| 6979 | |
| 6980 var MouseEventInterface = assign({}, UIEventInterface, { | |
| 6981 screenX: 0, | |
| 6982 screenY: 0, | |
| 6983 clientX: 0, | |
| 6984 clientY: 0, | |
| 6985 pageX: 0, | |
| 6986 pageY: 0, | |
| 6987 ctrlKey: 0, | |
| 6988 shiftKey: 0, | |
| 6989 altKey: 0, | |
| 6990 metaKey: 0, | |
| 6991 getModifierState: getEventModifierState, | |
| 6992 button: 0, | |
| 6993 buttons: 0, | |
| 6994 relatedTarget: function (event) { | |
| 6995 if (event.relatedTarget === undefined) return event.fromElement === event.srcElement ? event.toElement : event.fromElement; | |
| 6996 return event.relatedTarget; | |
| 6997 }, | |
| 6998 movementX: function (event) { | |
| 6999 if ('movementX' in event) { | |
| 7000 return event.movementX; | |
| 7001 } | |
| 7002 | |
| 7003 updateMouseMovementPolyfillState(event); | |
| 7004 return lastMovementX; | |
| 7005 }, | |
| 7006 movementY: function (event) { | |
| 7007 if ('movementY' in event) { | |
| 7008 return event.movementY; | |
| 7009 } // Don't need to call updateMouseMovementPolyfillState() here | |
| 7010 // because it's guaranteed to have already run when movementX | |
| 7011 // was copied. | |
| 7012 | |
| 7013 | |
| 7014 return lastMovementY; | |
| 7015 } | |
| 7016 }); | |
| 7017 | |
| 7018 var SyntheticMouseEvent = createSyntheticEvent(MouseEventInterface); | |
| 7019 /** | |
| 7020 * @interface DragEvent | |
| 7021 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 7022 */ | |
| 7023 | |
| 7024 var DragEventInterface = assign({}, MouseEventInterface, { | |
| 7025 dataTransfer: 0 | |
| 7026 }); | |
| 7027 | |
| 7028 var SyntheticDragEvent = createSyntheticEvent(DragEventInterface); | |
| 7029 /** | |
| 7030 * @interface FocusEvent | |
| 7031 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 7032 */ | |
| 7033 | |
| 7034 var FocusEventInterface = assign({}, UIEventInterface, { | |
| 7035 relatedTarget: 0 | |
| 7036 }); | |
| 7037 | |
| 7038 var SyntheticFocusEvent = createSyntheticEvent(FocusEventInterface); | |
| 7039 /** | |
| 7040 * @interface Event | |
| 7041 * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface | |
| 7042 * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent | |
| 7043 */ | |
| 7044 | |
| 7045 var AnimationEventInterface = assign({}, EventInterface, { | |
| 7046 animationName: 0, | |
| 7047 elapsedTime: 0, | |
| 7048 pseudoElement: 0 | |
| 7049 }); | |
| 7050 | |
| 7051 var SyntheticAnimationEvent = createSyntheticEvent(AnimationEventInterface); | |
| 7052 /** | |
| 7053 * @interface Event | |
| 7054 * @see http://www.w3.org/TR/clipboard-apis/ | |
| 7055 */ | |
| 7056 | |
| 7057 var ClipboardEventInterface = assign({}, EventInterface, { | |
| 7058 clipboardData: function (event) { | |
| 7059 return 'clipboardData' in event ? event.clipboardData : window.clipboardData; | |
| 7060 } | |
| 7061 }); | |
| 7062 | |
| 7063 var SyntheticClipboardEvent = createSyntheticEvent(ClipboardEventInterface); | |
| 7064 /** | |
| 7065 * @interface Event | |
| 7066 * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents | |
| 7067 */ | |
| 7068 | |
| 7069 var CompositionEventInterface = assign({}, EventInterface, { | |
| 7070 data: 0 | |
| 7071 }); | |
| 7072 | |
| 7073 var SyntheticCompositionEvent = createSyntheticEvent(CompositionEventInterface); | |
| 7074 /** | |
| 7075 * @interface Event | |
| 7076 * @see http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105 | |
| 7077 * /#events-inputevents | |
| 7078 */ | |
| 7079 // Happens to share the same list for now. | |
| 7080 | |
| 7081 var SyntheticInputEvent = SyntheticCompositionEvent; | |
| 7082 /** | |
| 7083 * Normalization of deprecated HTML5 `key` values | |
| 7084 * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names | |
| 7085 */ | |
| 7086 | |
| 7087 var normalizeKey = { | |
| 7088 Esc: 'Escape', | |
| 7089 Spacebar: ' ', | |
| 7090 Left: 'ArrowLeft', | |
| 7091 Up: 'ArrowUp', | |
| 7092 Right: 'ArrowRight', | |
| 7093 Down: 'ArrowDown', | |
| 7094 Del: 'Delete', | |
| 7095 Win: 'OS', | |
| 7096 Menu: 'ContextMenu', | |
| 7097 Apps: 'ContextMenu', | |
| 7098 Scroll: 'ScrollLock', | |
| 7099 MozPrintableKey: 'Unidentified' | |
| 7100 }; | |
| 7101 /** | |
| 7102 * Translation from legacy `keyCode` to HTML5 `key` | |
| 7103 * Only special keys supported, all others depend on keyboard layout or browser | |
| 7104 * @see https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent#Key_names | |
| 7105 */ | |
| 7106 | |
| 7107 var translateToKey = { | |
| 7108 '8': 'Backspace', | |
| 7109 '9': 'Tab', | |
| 7110 '12': 'Clear', | |
| 7111 '13': 'Enter', | |
| 7112 '16': 'Shift', | |
| 7113 '17': 'Control', | |
| 7114 '18': 'Alt', | |
| 7115 '19': 'Pause', | |
| 7116 '20': 'CapsLock', | |
| 7117 '27': 'Escape', | |
| 7118 '32': ' ', | |
| 7119 '33': 'PageUp', | |
| 7120 '34': 'PageDown', | |
| 7121 '35': 'End', | |
| 7122 '36': 'Home', | |
| 7123 '37': 'ArrowLeft', | |
| 7124 '38': 'ArrowUp', | |
| 7125 '39': 'ArrowRight', | |
| 7126 '40': 'ArrowDown', | |
| 7127 '45': 'Insert', | |
| 7128 '46': 'Delete', | |
| 7129 '112': 'F1', | |
| 7130 '113': 'F2', | |
| 7131 '114': 'F3', | |
| 7132 '115': 'F4', | |
| 7133 '116': 'F5', | |
| 7134 '117': 'F6', | |
| 7135 '118': 'F7', | |
| 7136 '119': 'F8', | |
| 7137 '120': 'F9', | |
| 7138 '121': 'F10', | |
| 7139 '122': 'F11', | |
| 7140 '123': 'F12', | |
| 7141 '144': 'NumLock', | |
| 7142 '145': 'ScrollLock', | |
| 7143 '224': 'Meta' | |
| 7144 }; | |
| 7145 /** | |
| 7146 * @param {object} nativeEvent Native browser event. | |
| 7147 * @return {string} Normalized `key` property. | |
| 7148 */ | |
| 7149 | |
| 7150 function getEventKey(nativeEvent) { | |
| 7151 if (nativeEvent.key) { | |
| 7152 // Normalize inconsistent values reported by browsers due to | |
| 7153 // implementations of a working draft specification. | |
| 7154 // FireFox implements `key` but returns `MozPrintableKey` for all | |
| 7155 // printable characters (normalized to `Unidentified`), ignore it. | |
| 7156 var key = normalizeKey[nativeEvent.key] || nativeEvent.key; | |
| 7157 | |
| 7158 if (key !== 'Unidentified') { | |
| 7159 return key; | |
| 7160 } | |
| 7161 } // Browser does not implement `key`, polyfill as much of it as we can. | |
| 7162 | |
| 7163 | |
| 7164 if (nativeEvent.type === 'keypress') { | |
| 7165 var charCode = getEventCharCode(nativeEvent); // The enter-key is technically both printable and non-printable and can | |
| 7166 // thus be captured by `keypress`, no other non-printable key should. | |
| 7167 | |
| 7168 return charCode === 13 ? 'Enter' : String.fromCharCode(charCode); | |
| 7169 } | |
| 7170 | |
| 7171 if (nativeEvent.type === 'keydown' || nativeEvent.type === 'keyup') { | |
| 7172 // While user keyboard layout determines the actual meaning of each | |
| 7173 // `keyCode` value, almost all function keys have a universal value. | |
| 7174 return translateToKey[nativeEvent.keyCode] || 'Unidentified'; | |
| 7175 } | |
| 7176 | |
| 7177 return ''; | |
| 7178 } | |
| 7179 /** | |
| 7180 * Translation from modifier key to the associated property in the event. | |
| 7181 * @see http://www.w3.org/TR/DOM-Level-3-Events/#keys-Modifiers | |
| 7182 */ | |
| 7183 | |
| 7184 | |
| 7185 var modifierKeyToProp = { | |
| 7186 Alt: 'altKey', | |
| 7187 Control: 'ctrlKey', | |
| 7188 Meta: 'metaKey', | |
| 7189 Shift: 'shiftKey' | |
| 7190 }; // Older browsers (Safari <= 10, iOS Safari <= 10.2) do not support | |
| 7191 // getModifierState. If getModifierState is not supported, we map it to a set of | |
| 7192 // modifier keys exposed by the event. In this case, Lock-keys are not supported. | |
| 7193 | |
| 7194 function modifierStateGetter(keyArg) { | |
| 7195 var syntheticEvent = this; | |
| 7196 var nativeEvent = syntheticEvent.nativeEvent; | |
| 7197 | |
| 7198 if (nativeEvent.getModifierState) { | |
| 7199 return nativeEvent.getModifierState(keyArg); | |
| 7200 } | |
| 7201 | |
| 7202 var keyProp = modifierKeyToProp[keyArg]; | |
| 7203 return keyProp ? !!nativeEvent[keyProp] : false; | |
| 7204 } | |
| 7205 | |
| 7206 function getEventModifierState(nativeEvent) { | |
| 7207 return modifierStateGetter; | |
| 7208 } | |
| 7209 /** | |
| 7210 * @interface KeyboardEvent | |
| 7211 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 7212 */ | |
| 7213 | |
| 7214 | |
| 7215 var KeyboardEventInterface = assign({}, UIEventInterface, { | |
| 7216 key: getEventKey, | |
| 7217 code: 0, | |
| 7218 location: 0, | |
| 7219 ctrlKey: 0, | |
| 7220 shiftKey: 0, | |
| 7221 altKey: 0, | |
| 7222 metaKey: 0, | |
| 7223 repeat: 0, | |
| 7224 locale: 0, | |
| 7225 getModifierState: getEventModifierState, | |
| 7226 // Legacy Interface | |
| 7227 charCode: function (event) { | |
| 7228 // `charCode` is the result of a KeyPress event and represents the value of | |
| 7229 // the actual printable character. | |
| 7230 // KeyPress is deprecated, but its replacement is not yet final and not | |
| 7231 // implemented in any major browser. Only KeyPress has charCode. | |
| 7232 if (event.type === 'keypress') { | |
| 7233 return getEventCharCode(event); | |
| 7234 } | |
| 7235 | |
| 7236 return 0; | |
| 7237 }, | |
| 7238 keyCode: function (event) { | |
| 7239 // `keyCode` is the result of a KeyDown/Up event and represents the value of | |
| 7240 // physical keyboard key. | |
| 7241 // The actual meaning of the value depends on the users' keyboard layout | |
| 7242 // which cannot be detected. Assuming that it is a US keyboard layout | |
| 7243 // provides a surprisingly accurate mapping for US and European users. | |
| 7244 // Due to this, it is left to the user to implement at this time. | |
| 7245 if (event.type === 'keydown' || event.type === 'keyup') { | |
| 7246 return event.keyCode; | |
| 7247 } | |
| 7248 | |
| 7249 return 0; | |
| 7250 }, | |
| 7251 which: function (event) { | |
| 7252 // `which` is an alias for either `keyCode` or `charCode` depending on the | |
| 7253 // type of the event. | |
| 7254 if (event.type === 'keypress') { | |
| 7255 return getEventCharCode(event); | |
| 7256 } | |
| 7257 | |
| 7258 if (event.type === 'keydown' || event.type === 'keyup') { | |
| 7259 return event.keyCode; | |
| 7260 } | |
| 7261 | |
| 7262 return 0; | |
| 7263 } | |
| 7264 }); | |
| 7265 | |
| 7266 var SyntheticKeyboardEvent = createSyntheticEvent(KeyboardEventInterface); | |
| 7267 /** | |
| 7268 * @interface PointerEvent | |
| 7269 * @see http://www.w3.org/TR/pointerevents/ | |
| 7270 */ | |
| 7271 | |
| 7272 var PointerEventInterface = assign({}, MouseEventInterface, { | |
| 7273 pointerId: 0, | |
| 7274 width: 0, | |
| 7275 height: 0, | |
| 7276 pressure: 0, | |
| 7277 tangentialPressure: 0, | |
| 7278 tiltX: 0, | |
| 7279 tiltY: 0, | |
| 7280 twist: 0, | |
| 7281 pointerType: 0, | |
| 7282 isPrimary: 0 | |
| 7283 }); | |
| 7284 | |
| 7285 var SyntheticPointerEvent = createSyntheticEvent(PointerEventInterface); | |
| 7286 /** | |
| 7287 * @interface TouchEvent | |
| 7288 * @see http://www.w3.org/TR/touch-events/ | |
| 7289 */ | |
| 7290 | |
| 7291 var TouchEventInterface = assign({}, UIEventInterface, { | |
| 7292 touches: 0, | |
| 7293 targetTouches: 0, | |
| 7294 changedTouches: 0, | |
| 7295 altKey: 0, | |
| 7296 metaKey: 0, | |
| 7297 ctrlKey: 0, | |
| 7298 shiftKey: 0, | |
| 7299 getModifierState: getEventModifierState | |
| 7300 }); | |
| 7301 | |
| 7302 var SyntheticTouchEvent = createSyntheticEvent(TouchEventInterface); | |
| 7303 /** | |
| 7304 * @interface Event | |
| 7305 * @see http://www.w3.org/TR/2009/WD-css3-transitions-20090320/#transition-events- | |
| 7306 * @see https://developer.mozilla.org/en-US/docs/Web/API/TransitionEvent | |
| 7307 */ | |
| 7308 | |
| 7309 var TransitionEventInterface = assign({}, EventInterface, { | |
| 7310 propertyName: 0, | |
| 7311 elapsedTime: 0, | |
| 7312 pseudoElement: 0 | |
| 7313 }); | |
| 7314 | |
| 7315 var SyntheticTransitionEvent = createSyntheticEvent(TransitionEventInterface); | |
| 7316 /** | |
| 7317 * @interface WheelEvent | |
| 7318 * @see http://www.w3.org/TR/DOM-Level-3-Events/ | |
| 7319 */ | |
| 7320 | |
| 7321 var WheelEventInterface = assign({}, MouseEventInterface, { | |
| 7322 deltaX: function (event) { | |
| 7323 return 'deltaX' in event ? event.deltaX : // Fallback to `wheelDeltaX` for Webkit and normalize (right is positive). | |
| 7324 'wheelDeltaX' in event ? -event.wheelDeltaX : 0; | |
| 7325 }, | |
| 7326 deltaY: function (event) { | |
| 7327 return 'deltaY' in event ? event.deltaY : // Fallback to `wheelDeltaY` for Webkit and normalize (down is positive). | |
| 7328 'wheelDeltaY' in event ? -event.wheelDeltaY : // Fallback to `wheelDelta` for IE<9 and normalize (down is positive). | |
| 7329 'wheelDelta' in event ? -event.wheelDelta : 0; | |
| 7330 }, | |
| 7331 deltaZ: 0, | |
| 7332 // Browsers without "deltaMode" is reporting in raw wheel delta where one | |
| 7333 // notch on the scroll is always +/- 120, roughly equivalent to pixels. | |
| 7334 // A good approximation of DOM_DELTA_LINE (1) is 5% of viewport size or | |
| 7335 // ~40 pixels, for DOM_DELTA_SCREEN (2) it is 87.5% of viewport size. | |
| 7336 deltaMode: 0 | |
| 7337 }); | |
| 7338 | |
| 7339 var SyntheticWheelEvent = createSyntheticEvent(WheelEventInterface); | |
| 7340 | |
| 7341 var END_KEYCODES = [9, 13, 27, 32]; // Tab, Return, Esc, Space | |
| 7342 | |
| 7343 var START_KEYCODE = 229; | |
| 7344 var canUseCompositionEvent = canUseDOM && 'CompositionEvent' in window; | |
| 7345 var documentMode = null; | |
| 7346 | |
| 7347 if (canUseDOM && 'documentMode' in document) { | |
| 7348 documentMode = document.documentMode; | |
| 7349 } // Webkit offers a very useful `textInput` event that can be used to | |
| 7350 // directly represent `beforeInput`. The IE `textinput` event is not as | |
| 7351 // useful, so we don't use it. | |
| 7352 | |
| 7353 | |
| 7354 var canUseTextInputEvent = canUseDOM && 'TextEvent' in window && !documentMode; // In IE9+, we have access to composition events, but the data supplied | |
| 7355 // by the native compositionend event may be incorrect. Japanese ideographic | |
| 7356 // spaces, for instance (\u3000) are not recorded correctly. | |
| 7357 | |
| 7358 var useFallbackCompositionData = canUseDOM && (!canUseCompositionEvent || documentMode && documentMode > 8 && documentMode <= 11); | |
| 7359 var SPACEBAR_CODE = 32; | |
| 7360 var SPACEBAR_CHAR = String.fromCharCode(SPACEBAR_CODE); | |
| 7361 | |
| 7362 function registerEvents() { | |
| 7363 registerTwoPhaseEvent('onBeforeInput', ['compositionend', 'keypress', 'textInput', 'paste']); | |
| 7364 registerTwoPhaseEvent('onCompositionEnd', ['compositionend', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']); | |
| 7365 registerTwoPhaseEvent('onCompositionStart', ['compositionstart', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']); | |
| 7366 registerTwoPhaseEvent('onCompositionUpdate', ['compositionupdate', 'focusout', 'keydown', 'keypress', 'keyup', 'mousedown']); | |
| 7367 } // Track whether we've ever handled a keypress on the space key. | |
| 7368 | |
| 7369 | |
| 7370 var hasSpaceKeypress = false; | |
| 7371 /** | |
| 7372 * Return whether a native keypress event is assumed to be a command. | |
| 7373 * This is required because Firefox fires `keypress` events for key commands | |
| 7374 * (cut, copy, select-all, etc.) even though no character is inserted. | |
| 7375 */ | |
| 7376 | |
| 7377 function isKeypressCommand(nativeEvent) { | |
| 7378 return (nativeEvent.ctrlKey || nativeEvent.altKey || nativeEvent.metaKey) && // ctrlKey && altKey is equivalent to AltGr, and is not a command. | |
| 7379 !(nativeEvent.ctrlKey && nativeEvent.altKey); | |
| 7380 } | |
| 7381 /** | |
| 7382 * Translate native top level events into event types. | |
| 7383 */ | |
| 7384 | |
| 7385 | |
| 7386 function getCompositionEventType(domEventName) { | |
| 7387 switch (domEventName) { | |
| 7388 case 'compositionstart': | |
| 7389 return 'onCompositionStart'; | |
| 7390 | |
| 7391 case 'compositionend': | |
| 7392 return 'onCompositionEnd'; | |
| 7393 | |
| 7394 case 'compositionupdate': | |
| 7395 return 'onCompositionUpdate'; | |
| 7396 } | |
| 7397 } | |
| 7398 /** | |
| 7399 * Does our fallback best-guess model think this event signifies that | |
| 7400 * composition has begun? | |
| 7401 */ | |
| 7402 | |
| 7403 | |
| 7404 function isFallbackCompositionStart(domEventName, nativeEvent) { | |
| 7405 return domEventName === 'keydown' && nativeEvent.keyCode === START_KEYCODE; | |
| 7406 } | |
| 7407 /** | |
| 7408 * Does our fallback mode think that this event is the end of composition? | |
| 7409 */ | |
| 7410 | |
| 7411 | |
| 7412 function isFallbackCompositionEnd(domEventName, nativeEvent) { | |
| 7413 switch (domEventName) { | |
| 7414 case 'keyup': | |
| 7415 // Command keys insert or clear IME input. | |
| 7416 return END_KEYCODES.indexOf(nativeEvent.keyCode) !== -1; | |
| 7417 | |
| 7418 case 'keydown': | |
| 7419 // Expect IME keyCode on each keydown. If we get any other | |
| 7420 // code we must have exited earlier. | |
| 7421 return nativeEvent.keyCode !== START_KEYCODE; | |
| 7422 | |
| 7423 case 'keypress': | |
| 7424 case 'mousedown': | |
| 7425 case 'focusout': | |
| 7426 // Events are not possible without cancelling IME. | |
| 7427 return true; | |
| 7428 | |
| 7429 default: | |
| 7430 return false; | |
| 7431 } | |
| 7432 } | |
| 7433 /** | |
| 7434 * Google Input Tools provides composition data via a CustomEvent, | |
| 7435 * with the `data` property populated in the `detail` object. If this | |
| 7436 * is available on the event object, use it. If not, this is a plain | |
| 7437 * composition event and we have nothing special to extract. | |
| 7438 * | |
| 7439 * @param {object} nativeEvent | |
| 7440 * @return {?string} | |
| 7441 */ | |
| 7442 | |
| 7443 | |
| 7444 function getDataFromCustomEvent(nativeEvent) { | |
| 7445 var detail = nativeEvent.detail; | |
| 7446 | |
| 7447 if (typeof detail === 'object' && 'data' in detail) { | |
| 7448 return detail.data; | |
| 7449 } | |
| 7450 | |
| 7451 return null; | |
| 7452 } | |
| 7453 /** | |
| 7454 * Check if a composition event was triggered by Korean IME. | |
| 7455 * Our fallback mode does not work well with IE's Korean IME, | |
| 7456 * so just use native composition events when Korean IME is used. | |
| 7457 * Although CompositionEvent.locale property is deprecated, | |
| 7458 * it is available in IE, where our fallback mode is enabled. | |
| 7459 * | |
| 7460 * @param {object} nativeEvent | |
| 7461 * @return {boolean} | |
| 7462 */ | |
| 7463 | |
| 7464 | |
| 7465 function isUsingKoreanIME(nativeEvent) { | |
| 7466 return nativeEvent.locale === 'ko'; | |
| 7467 } // Track the current IME composition status, if any. | |
| 7468 | |
| 7469 | |
| 7470 var isComposing = false; | |
| 7471 /** | |
| 7472 * @return {?object} A SyntheticCompositionEvent. | |
| 7473 */ | |
| 7474 | |
| 7475 function extractCompositionEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget) { | |
| 7476 var eventType; | |
| 7477 var fallbackData; | |
| 7478 | |
| 7479 if (canUseCompositionEvent) { | |
| 7480 eventType = getCompositionEventType(domEventName); | |
| 7481 } else if (!isComposing) { | |
| 7482 if (isFallbackCompositionStart(domEventName, nativeEvent)) { | |
| 7483 eventType = 'onCompositionStart'; | |
| 7484 } | |
| 7485 } else if (isFallbackCompositionEnd(domEventName, nativeEvent)) { | |
| 7486 eventType = 'onCompositionEnd'; | |
| 7487 } | |
| 7488 | |
| 7489 if (!eventType) { | |
| 7490 return null; | |
| 7491 } | |
| 7492 | |
| 7493 if (useFallbackCompositionData && !isUsingKoreanIME(nativeEvent)) { | |
| 7494 // The current composition is stored statically and must not be | |
| 7495 // overwritten while composition continues. | |
| 7496 if (!isComposing && eventType === 'onCompositionStart') { | |
| 7497 isComposing = initialize(nativeEventTarget); | |
| 7498 } else if (eventType === 'onCompositionEnd') { | |
| 7499 if (isComposing) { | |
| 7500 fallbackData = getData(); | |
| 7501 } | |
| 7502 } | |
| 7503 } | |
| 7504 | |
| 7505 var listeners = accumulateTwoPhaseListeners(targetInst, eventType); | |
| 7506 | |
| 7507 if (listeners.length > 0) { | |
| 7508 var event = new SyntheticCompositionEvent(eventType, domEventName, null, nativeEvent, nativeEventTarget); | |
| 7509 dispatchQueue.push({ | |
| 7510 event: event, | |
| 7511 listeners: listeners | |
| 7512 }); | |
| 7513 | |
| 7514 if (fallbackData) { | |
| 7515 // Inject data generated from fallback path into the synthetic event. | |
| 7516 // This matches the property of native CompositionEventInterface. | |
| 7517 event.data = fallbackData; | |
| 7518 } else { | |
| 7519 var customData = getDataFromCustomEvent(nativeEvent); | |
| 7520 | |
| 7521 if (customData !== null) { | |
| 7522 event.data = customData; | |
| 7523 } | |
| 7524 } | |
| 7525 } | |
| 7526 } | |
| 7527 | |
| 7528 function getNativeBeforeInputChars(domEventName, nativeEvent) { | |
| 7529 switch (domEventName) { | |
| 7530 case 'compositionend': | |
| 7531 return getDataFromCustomEvent(nativeEvent); | |
| 7532 | |
| 7533 case 'keypress': | |
| 7534 /** | |
| 7535 * If native `textInput` events are available, our goal is to make | |
| 7536 * use of them. However, there is a special case: the spacebar key. | |
| 7537 * In Webkit, preventing default on a spacebar `textInput` event | |
| 7538 * cancels character insertion, but it *also* causes the browser | |
| 7539 * to fall back to its default spacebar behavior of scrolling the | |
| 7540 * page. | |
| 7541 * | |
| 7542 * Tracking at: | |
| 7543 * https://code.google.com/p/chromium/issues/detail?id=355103 | |
| 7544 * | |
| 7545 * To avoid this issue, use the keypress event as if no `textInput` | |
| 7546 * event is available. | |
| 7547 */ | |
| 7548 var which = nativeEvent.which; | |
| 7549 | |
| 7550 if (which !== SPACEBAR_CODE) { | |
| 7551 return null; | |
| 7552 } | |
| 7553 | |
| 7554 hasSpaceKeypress = true; | |
| 7555 return SPACEBAR_CHAR; | |
| 7556 | |
| 7557 case 'textInput': | |
| 7558 // Record the characters to be added to the DOM. | |
| 7559 var chars = nativeEvent.data; // If it's a spacebar character, assume that we have already handled | |
| 7560 // it at the keypress level and bail immediately. Android Chrome | |
| 7561 // doesn't give us keycodes, so we need to ignore it. | |
| 7562 | |
| 7563 if (chars === SPACEBAR_CHAR && hasSpaceKeypress) { | |
| 7564 return null; | |
| 7565 } | |
| 7566 | |
| 7567 return chars; | |
| 7568 | |
| 7569 default: | |
| 7570 // For other native event types, do nothing. | |
| 7571 return null; | |
| 7572 } | |
| 7573 } | |
| 7574 /** | |
| 7575 * For browsers that do not provide the `textInput` event, extract the | |
| 7576 * appropriate string to use for SyntheticInputEvent. | |
| 7577 */ | |
| 7578 | |
| 7579 | |
| 7580 function getFallbackBeforeInputChars(domEventName, nativeEvent) { | |
| 7581 // If we are currently composing (IME) and using a fallback to do so, | |
| 7582 // try to extract the composed characters from the fallback object. | |
| 7583 // If composition event is available, we extract a string only at | |
| 7584 // compositionevent, otherwise extract it at fallback events. | |
| 7585 if (isComposing) { | |
| 7586 if (domEventName === 'compositionend' || !canUseCompositionEvent && isFallbackCompositionEnd(domEventName, nativeEvent)) { | |
| 7587 var chars = getData(); | |
| 7588 reset(); | |
| 7589 isComposing = false; | |
| 7590 return chars; | |
| 7591 } | |
| 7592 | |
| 7593 return null; | |
| 7594 } | |
| 7595 | |
| 7596 switch (domEventName) { | |
| 7597 case 'paste': | |
| 7598 // If a paste event occurs after a keypress, throw out the input | |
| 7599 // chars. Paste events should not lead to BeforeInput events. | |
| 7600 return null; | |
| 7601 | |
| 7602 case 'keypress': | |
| 7603 /** | |
| 7604 * As of v27, Firefox may fire keypress events even when no character | |
| 7605 * will be inserted. A few possibilities: | |
| 7606 * | |
| 7607 * - `which` is `0`. Arrow keys, Esc key, etc. | |
| 7608 * | |
| 7609 * - `which` is the pressed key code, but no char is available. | |
| 7610 * Ex: 'AltGr + d` in Polish. There is no modified character for | |
| 7611 * this key combination and no character is inserted into the | |
| 7612 * document, but FF fires the keypress for char code `100` anyway. | |
| 7613 * No `input` event will occur. | |
| 7614 * | |
| 7615 * - `which` is the pressed key code, but a command combination is | |
| 7616 * being used. Ex: `Cmd+C`. No character is inserted, and no | |
| 7617 * `input` event will occur. | |
| 7618 */ | |
| 7619 if (!isKeypressCommand(nativeEvent)) { | |
| 7620 // IE fires the `keypress` event when a user types an emoji via | |
| 7621 // Touch keyboard of Windows. In such a case, the `char` property | |
| 7622 // holds an emoji character like `\uD83D\uDE0A`. Because its length | |
| 7623 // is 2, the property `which` does not represent an emoji correctly. | |
| 7624 // In such a case, we directly return the `char` property instead of | |
| 7625 // using `which`. | |
| 7626 if (nativeEvent.char && nativeEvent.char.length > 1) { | |
| 7627 return nativeEvent.char; | |
| 7628 } else if (nativeEvent.which) { | |
| 7629 return String.fromCharCode(nativeEvent.which); | |
| 7630 } | |
| 7631 } | |
| 7632 | |
| 7633 return null; | |
| 7634 | |
| 7635 case 'compositionend': | |
| 7636 return useFallbackCompositionData && !isUsingKoreanIME(nativeEvent) ? null : nativeEvent.data; | |
| 7637 | |
| 7638 default: | |
| 7639 return null; | |
| 7640 } | |
| 7641 } | |
| 7642 /** | |
| 7643 * Extract a SyntheticInputEvent for `beforeInput`, based on either native | |
| 7644 * `textInput` or fallback behavior. | |
| 7645 * | |
| 7646 * @return {?object} A SyntheticInputEvent. | |
| 7647 */ | |
| 7648 | |
| 7649 | |
| 7650 function extractBeforeInputEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget) { | |
| 7651 var chars; | |
| 7652 | |
| 7653 if (canUseTextInputEvent) { | |
| 7654 chars = getNativeBeforeInputChars(domEventName, nativeEvent); | |
| 7655 } else { | |
| 7656 chars = getFallbackBeforeInputChars(domEventName, nativeEvent); | |
| 7657 } // If no characters are being inserted, no BeforeInput event should | |
| 7658 // be fired. | |
| 7659 | |
| 7660 | |
| 7661 if (!chars) { | |
| 7662 return null; | |
| 7663 } | |
| 7664 | |
| 7665 var listeners = accumulateTwoPhaseListeners(targetInst, 'onBeforeInput'); | |
| 7666 | |
| 7667 if (listeners.length > 0) { | |
| 7668 var event = new SyntheticInputEvent('onBeforeInput', 'beforeinput', null, nativeEvent, nativeEventTarget); | |
| 7669 dispatchQueue.push({ | |
| 7670 event: event, | |
| 7671 listeners: listeners | |
| 7672 }); | |
| 7673 event.data = chars; | |
| 7674 } | |
| 7675 } | |
| 7676 /** | |
| 7677 * Create an `onBeforeInput` event to match | |
| 7678 * http://www.w3.org/TR/2013/WD-DOM-Level-3-Events-20131105/#events-inputevents. | |
| 7679 * | |
| 7680 * This event plugin is based on the native `textInput` event | |
| 7681 * available in Chrome, Safari, Opera, and IE. This event fires after | |
| 7682 * `onKeyPress` and `onCompositionEnd`, but before `onInput`. | |
| 7683 * | |
| 7684 * `beforeInput` is spec'd but not implemented in any browsers, and | |
| 7685 * the `input` event does not provide any useful information about what has | |
| 7686 * actually been added, contrary to the spec. Thus, `textInput` is the best | |
| 7687 * available event to identify the characters that have actually been inserted | |
| 7688 * into the target node. | |
| 7689 * | |
| 7690 * This plugin is also responsible for emitting `composition` events, thus | |
| 7691 * allowing us to share composition fallback code for both `beforeInput` and | |
| 7692 * `composition` event types. | |
| 7693 */ | |
| 7694 | |
| 7695 | |
| 7696 function extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 7697 extractCompositionEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 7698 extractBeforeInputEvent(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 7699 } | |
| 7700 | |
| 7701 /** | |
| 7702 * @see http://www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary | |
| 7703 */ | |
| 7704 var supportedInputTypes = { | |
| 7705 color: true, | |
| 7706 date: true, | |
| 7707 datetime: true, | |
| 7708 'datetime-local': true, | |
| 7709 email: true, | |
| 7710 month: true, | |
| 7711 number: true, | |
| 7712 password: true, | |
| 7713 range: true, | |
| 7714 search: true, | |
| 7715 tel: true, | |
| 7716 text: true, | |
| 7717 time: true, | |
| 7718 url: true, | |
| 7719 week: true | |
| 7720 }; | |
| 7721 | |
| 7722 function isTextInputElement(elem) { | |
| 7723 var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase(); | |
| 7724 | |
| 7725 if (nodeName === 'input') { | |
| 7726 return !!supportedInputTypes[elem.type]; | |
| 7727 } | |
| 7728 | |
| 7729 if (nodeName === 'textarea') { | |
| 7730 return true; | |
| 7731 } | |
| 7732 | |
| 7733 return false; | |
| 7734 } | |
| 7735 | |
| 7736 /** | |
| 7737 * Checks if an event is supported in the current execution environment. | |
| 7738 * | |
| 7739 * NOTE: This will not work correctly for non-generic events such as `change`, | |
| 7740 * `reset`, `load`, `error`, and `select`. | |
| 7741 * | |
| 7742 * Borrows from Modernizr. | |
| 7743 * | |
| 7744 * @param {string} eventNameSuffix Event name, e.g. "click". | |
| 7745 * @return {boolean} True if the event is supported. | |
| 7746 * @internal | |
| 7747 * @license Modernizr 3.0.0pre (Custom Build) | MIT | |
| 7748 */ | |
| 7749 | |
| 7750 function isEventSupported(eventNameSuffix) { | |
| 7751 if (!canUseDOM) { | |
| 7752 return false; | |
| 7753 } | |
| 7754 | |
| 7755 var eventName = 'on' + eventNameSuffix; | |
| 7756 var isSupported = (eventName in document); | |
| 7757 | |
| 7758 if (!isSupported) { | |
| 7759 var element = document.createElement('div'); | |
| 7760 element.setAttribute(eventName, 'return;'); | |
| 7761 isSupported = typeof element[eventName] === 'function'; | |
| 7762 } | |
| 7763 | |
| 7764 return isSupported; | |
| 7765 } | |
| 7766 | |
| 7767 function registerEvents$1() { | |
| 7768 registerTwoPhaseEvent('onChange', ['change', 'click', 'focusin', 'focusout', 'input', 'keydown', 'keyup', 'selectionchange']); | |
| 7769 } | |
| 7770 | |
| 7771 function createAndAccumulateChangeEvent(dispatchQueue, inst, nativeEvent, target) { | |
| 7772 // Flag this event loop as needing state restore. | |
| 7773 enqueueStateRestore(target); | |
| 7774 var listeners = accumulateTwoPhaseListeners(inst, 'onChange'); | |
| 7775 | |
| 7776 if (listeners.length > 0) { | |
| 7777 var event = new SyntheticEvent('onChange', 'change', null, nativeEvent, target); | |
| 7778 dispatchQueue.push({ | |
| 7779 event: event, | |
| 7780 listeners: listeners | |
| 7781 }); | |
| 7782 } | |
| 7783 } | |
| 7784 /** | |
| 7785 * For IE shims | |
| 7786 */ | |
| 7787 | |
| 7788 | |
| 7789 var activeElement = null; | |
| 7790 var activeElementInst = null; | |
| 7791 /** | |
| 7792 * SECTION: handle `change` event | |
| 7793 */ | |
| 7794 | |
| 7795 function shouldUseChangeEvent(elem) { | |
| 7796 var nodeName = elem.nodeName && elem.nodeName.toLowerCase(); | |
| 7797 return nodeName === 'select' || nodeName === 'input' && elem.type === 'file'; | |
| 7798 } | |
| 7799 | |
| 7800 function manualDispatchChangeEvent(nativeEvent) { | |
| 7801 var dispatchQueue = []; | |
| 7802 createAndAccumulateChangeEvent(dispatchQueue, activeElementInst, nativeEvent, getEventTarget(nativeEvent)); // If change and propertychange bubbled, we'd just bind to it like all the | |
| 7803 // other events and have it go through ReactBrowserEventEmitter. Since it | |
| 7804 // doesn't, we manually listen for the events and so we have to enqueue and | |
| 7805 // process the abstract event manually. | |
| 7806 // | |
| 7807 // Batching is necessary here in order to ensure that all event handlers run | |
| 7808 // before the next rerender (including event handlers attached to ancestor | |
| 7809 // elements instead of directly on the input). Without this, controlled | |
| 7810 // components don't work properly in conjunction with event bubbling because | |
| 7811 // the component is rerendered and the value reverted before all the event | |
| 7812 // handlers can run. See https://github.com/facebook/react/issues/708. | |
| 7813 | |
| 7814 batchedUpdates(runEventInBatch, dispatchQueue); | |
| 7815 } | |
| 7816 | |
| 7817 function runEventInBatch(dispatchQueue) { | |
| 7818 processDispatchQueue(dispatchQueue, 0); | |
| 7819 } | |
| 7820 | |
| 7821 function getInstIfValueChanged(targetInst) { | |
| 7822 var targetNode = getNodeFromInstance(targetInst); | |
| 7823 | |
| 7824 if (updateValueIfChanged(targetNode)) { | |
| 7825 return targetInst; | |
| 7826 } | |
| 7827 } | |
| 7828 | |
| 7829 function getTargetInstForChangeEvent(domEventName, targetInst) { | |
| 7830 if (domEventName === 'change') { | |
| 7831 return targetInst; | |
| 7832 } | |
| 7833 } | |
| 7834 /** | |
| 7835 * SECTION: handle `input` event | |
| 7836 */ | |
| 7837 | |
| 7838 | |
| 7839 var isInputEventSupported = false; | |
| 7840 | |
| 7841 if (canUseDOM) { | |
| 7842 // IE9 claims to support the input event but fails to trigger it when | |
| 7843 // deleting text, so we ignore its input events. | |
| 7844 isInputEventSupported = isEventSupported('input') && (!document.documentMode || document.documentMode > 9); | |
| 7845 } | |
| 7846 /** | |
| 7847 * (For IE <=9) Starts tracking propertychange events on the passed-in element | |
| 7848 * and override the value property so that we can distinguish user events from | |
| 7849 * value changes in JS. | |
| 7850 */ | |
| 7851 | |
| 7852 | |
| 7853 function startWatchingForValueChange(target, targetInst) { | |
| 7854 activeElement = target; | |
| 7855 activeElementInst = targetInst; | |
| 7856 activeElement.attachEvent('onpropertychange', handlePropertyChange); | |
| 7857 } | |
| 7858 /** | |
| 7859 * (For IE <=9) Removes the event listeners from the currently-tracked element, | |
| 7860 * if any exists. | |
| 7861 */ | |
| 7862 | |
| 7863 | |
| 7864 function stopWatchingForValueChange() { | |
| 7865 if (!activeElement) { | |
| 7866 return; | |
| 7867 } | |
| 7868 | |
| 7869 activeElement.detachEvent('onpropertychange', handlePropertyChange); | |
| 7870 activeElement = null; | |
| 7871 activeElementInst = null; | |
| 7872 } | |
| 7873 /** | |
| 7874 * (For IE <=9) Handles a propertychange event, sending a `change` event if | |
| 7875 * the value of the active element has changed. | |
| 7876 */ | |
| 7877 | |
| 7878 | |
| 7879 function handlePropertyChange(nativeEvent) { | |
| 7880 if (nativeEvent.propertyName !== 'value') { | |
| 7881 return; | |
| 7882 } | |
| 7883 | |
| 7884 if (getInstIfValueChanged(activeElementInst)) { | |
| 7885 manualDispatchChangeEvent(nativeEvent); | |
| 7886 } | |
| 7887 } | |
| 7888 | |
| 7889 function handleEventsForInputEventPolyfill(domEventName, target, targetInst) { | |
| 7890 if (domEventName === 'focusin') { | |
| 7891 // In IE9, propertychange fires for most input events but is buggy and | |
| 7892 // doesn't fire when text is deleted, but conveniently, selectionchange | |
| 7893 // appears to fire in all of the remaining cases so we catch those and | |
| 7894 // forward the event if the value has changed | |
| 7895 // In either case, we don't want to call the event handler if the value | |
| 7896 // is changed from JS so we redefine a setter for `.value` that updates | |
| 7897 // our activeElementValue variable, allowing us to ignore those changes | |
| 7898 // | |
| 7899 // stopWatching() should be a noop here but we call it just in case we | |
| 7900 // missed a blur event somehow. | |
| 7901 stopWatchingForValueChange(); | |
| 7902 startWatchingForValueChange(target, targetInst); | |
| 7903 } else if (domEventName === 'focusout') { | |
| 7904 stopWatchingForValueChange(); | |
| 7905 } | |
| 7906 } // For IE8 and IE9. | |
| 7907 | |
| 7908 | |
| 7909 function getTargetInstForInputEventPolyfill(domEventName, targetInst) { | |
| 7910 if (domEventName === 'selectionchange' || domEventName === 'keyup' || domEventName === 'keydown') { | |
| 7911 // On the selectionchange event, the target is just document which isn't | |
| 7912 // helpful for us so just check activeElement instead. | |
| 7913 // | |
| 7914 // 99% of the time, keydown and keyup aren't necessary. IE8 fails to fire | |
| 7915 // propertychange on the first input event after setting `value` from a | |
| 7916 // script and fires only keydown, keypress, keyup. Catching keyup usually | |
| 7917 // gets it and catching keydown lets us fire an event for the first | |
| 7918 // keystroke if user does a key repeat (it'll be a little delayed: right | |
| 7919 // before the second keystroke). Other input methods (e.g., paste) seem to | |
| 7920 // fire selectionchange normally. | |
| 7921 return getInstIfValueChanged(activeElementInst); | |
| 7922 } | |
| 7923 } | |
| 7924 /** | |
| 7925 * SECTION: handle `click` event | |
| 7926 */ | |
| 7927 | |
| 7928 | |
| 7929 function shouldUseClickEvent(elem) { | |
| 7930 // Use the `click` event to detect changes to checkbox and radio inputs. | |
| 7931 // This approach works across all browsers, whereas `change` does not fire | |
| 7932 // until `blur` in IE8. | |
| 7933 var nodeName = elem.nodeName; | |
| 7934 return nodeName && nodeName.toLowerCase() === 'input' && (elem.type === 'checkbox' || elem.type === 'radio'); | |
| 7935 } | |
| 7936 | |
| 7937 function getTargetInstForClickEvent(domEventName, targetInst) { | |
| 7938 if (domEventName === 'click') { | |
| 7939 return getInstIfValueChanged(targetInst); | |
| 7940 } | |
| 7941 } | |
| 7942 | |
| 7943 function getTargetInstForInputOrChangeEvent(domEventName, targetInst) { | |
| 7944 if (domEventName === 'input' || domEventName === 'change') { | |
| 7945 return getInstIfValueChanged(targetInst); | |
| 7946 } | |
| 7947 } | |
| 7948 | |
| 7949 function handleControlledInputBlur(node) { | |
| 7950 var state = node._wrapperState; | |
| 7951 | |
| 7952 if (!state || !state.controlled || node.type !== 'number') { | |
| 7953 return; | |
| 7954 } | |
| 7955 | |
| 7956 { | |
| 7957 // If controlled, assign the value attribute to the current value on blur | |
| 7958 setDefaultValue(node, 'number', node.value); | |
| 7959 } | |
| 7960 } | |
| 7961 /** | |
| 7962 * This plugin creates an `onChange` event that normalizes change events | |
| 7963 * across form elements. This event fires at a time when it's possible to | |
| 7964 * change the element's value without seeing a flicker. | |
| 7965 * | |
| 7966 * Supported elements are: | |
| 7967 * - input (see `isTextInputElement`) | |
| 7968 * - textarea | |
| 7969 * - select | |
| 7970 */ | |
| 7971 | |
| 7972 | |
| 7973 function extractEvents$1(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 7974 var targetNode = targetInst ? getNodeFromInstance(targetInst) : window; | |
| 7975 var getTargetInstFunc, handleEventFunc; | |
| 7976 | |
| 7977 if (shouldUseChangeEvent(targetNode)) { | |
| 7978 getTargetInstFunc = getTargetInstForChangeEvent; | |
| 7979 } else if (isTextInputElement(targetNode)) { | |
| 7980 if (isInputEventSupported) { | |
| 7981 getTargetInstFunc = getTargetInstForInputOrChangeEvent; | |
| 7982 } else { | |
| 7983 getTargetInstFunc = getTargetInstForInputEventPolyfill; | |
| 7984 handleEventFunc = handleEventsForInputEventPolyfill; | |
| 7985 } | |
| 7986 } else if (shouldUseClickEvent(targetNode)) { | |
| 7987 getTargetInstFunc = getTargetInstForClickEvent; | |
| 7988 } | |
| 7989 | |
| 7990 if (getTargetInstFunc) { | |
| 7991 var inst = getTargetInstFunc(domEventName, targetInst); | |
| 7992 | |
| 7993 if (inst) { | |
| 7994 createAndAccumulateChangeEvent(dispatchQueue, inst, nativeEvent, nativeEventTarget); | |
| 7995 return; | |
| 7996 } | |
| 7997 } | |
| 7998 | |
| 7999 if (handleEventFunc) { | |
| 8000 handleEventFunc(domEventName, targetNode, targetInst); | |
| 8001 } // When blurring, set the value attribute for number inputs | |
| 8002 | |
| 8003 | |
| 8004 if (domEventName === 'focusout') { | |
| 8005 handleControlledInputBlur(targetNode); | |
| 8006 } | |
| 8007 } | |
| 8008 | |
| 8009 function registerEvents$2() { | |
| 8010 registerDirectEvent('onMouseEnter', ['mouseout', 'mouseover']); | |
| 8011 registerDirectEvent('onMouseLeave', ['mouseout', 'mouseover']); | |
| 8012 registerDirectEvent('onPointerEnter', ['pointerout', 'pointerover']); | |
| 8013 registerDirectEvent('onPointerLeave', ['pointerout', 'pointerover']); | |
| 8014 } | |
| 8015 /** | |
| 8016 * For almost every interaction we care about, there will be both a top-level | |
| 8017 * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that | |
| 8018 * we do not extract duplicate events. However, moving the mouse into the | |
| 8019 * browser from outside will not fire a `mouseout` event. In this case, we use | |
| 8020 * the `mouseover` top-level event. | |
| 8021 */ | |
| 8022 | |
| 8023 | |
| 8024 function extractEvents$2(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 8025 var isOverEvent = domEventName === 'mouseover' || domEventName === 'pointerover'; | |
| 8026 var isOutEvent = domEventName === 'mouseout' || domEventName === 'pointerout'; | |
| 8027 | |
| 8028 if (isOverEvent && !isReplayingEvent(nativeEvent)) { | |
| 8029 // If this is an over event with a target, we might have already dispatched | |
| 8030 // the event in the out event of the other target. If this is replayed, | |
| 8031 // then it's because we couldn't dispatch against this target previously | |
| 8032 // so we have to do it now instead. | |
| 8033 var related = nativeEvent.relatedTarget || nativeEvent.fromElement; | |
| 8034 | |
| 8035 if (related) { | |
| 8036 // If the related node is managed by React, we can assume that we have | |
| 8037 // already dispatched the corresponding events during its mouseout. | |
| 8038 if (getClosestInstanceFromNode(related) || isContainerMarkedAsRoot(related)) { | |
| 8039 return; | |
| 8040 } | |
| 8041 } | |
| 8042 } | |
| 8043 | |
| 8044 if (!isOutEvent && !isOverEvent) { | |
| 8045 // Must not be a mouse or pointer in or out - ignoring. | |
| 8046 return; | |
| 8047 } | |
| 8048 | |
| 8049 var win; // TODO: why is this nullable in the types but we read from it? | |
| 8050 | |
| 8051 if (nativeEventTarget.window === nativeEventTarget) { | |
| 8052 // `nativeEventTarget` is probably a window object. | |
| 8053 win = nativeEventTarget; | |
| 8054 } else { | |
| 8055 // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8. | |
| 8056 var doc = nativeEventTarget.ownerDocument; | |
| 8057 | |
| 8058 if (doc) { | |
| 8059 win = doc.defaultView || doc.parentWindow; | |
| 8060 } else { | |
| 8061 win = window; | |
| 8062 } | |
| 8063 } | |
| 8064 | |
| 8065 var from; | |
| 8066 var to; | |
| 8067 | |
| 8068 if (isOutEvent) { | |
| 8069 var _related = nativeEvent.relatedTarget || nativeEvent.toElement; | |
| 8070 | |
| 8071 from = targetInst; | |
| 8072 to = _related ? getClosestInstanceFromNode(_related) : null; | |
| 8073 | |
| 8074 if (to !== null) { | |
| 8075 var nearestMounted = getNearestMountedFiber(to); | |
| 8076 | |
| 8077 if (to !== nearestMounted || to.tag !== HostComponent && to.tag !== HostText) { | |
| 8078 to = null; | |
| 8079 } | |
| 8080 } | |
| 8081 } else { | |
| 8082 // Moving to a node from outside the window. | |
| 8083 from = null; | |
| 8084 to = targetInst; | |
| 8085 } | |
| 8086 | |
| 8087 if (from === to) { | |
| 8088 // Nothing pertains to our managed components. | |
| 8089 return; | |
| 8090 } | |
| 8091 | |
| 8092 var SyntheticEventCtor = SyntheticMouseEvent; | |
| 8093 var leaveEventType = 'onMouseLeave'; | |
| 8094 var enterEventType = 'onMouseEnter'; | |
| 8095 var eventTypePrefix = 'mouse'; | |
| 8096 | |
| 8097 if (domEventName === 'pointerout' || domEventName === 'pointerover') { | |
| 8098 SyntheticEventCtor = SyntheticPointerEvent; | |
| 8099 leaveEventType = 'onPointerLeave'; | |
| 8100 enterEventType = 'onPointerEnter'; | |
| 8101 eventTypePrefix = 'pointer'; | |
| 8102 } | |
| 8103 | |
| 8104 var fromNode = from == null ? win : getNodeFromInstance(from); | |
| 8105 var toNode = to == null ? win : getNodeFromInstance(to); | |
| 8106 var leave = new SyntheticEventCtor(leaveEventType, eventTypePrefix + 'leave', from, nativeEvent, nativeEventTarget); | |
| 8107 leave.target = fromNode; | |
| 8108 leave.relatedTarget = toNode; | |
| 8109 var enter = null; // We should only process this nativeEvent if we are processing | |
| 8110 // the first ancestor. Next time, we will ignore the event. | |
| 8111 | |
| 8112 var nativeTargetInst = getClosestInstanceFromNode(nativeEventTarget); | |
| 8113 | |
| 8114 if (nativeTargetInst === targetInst) { | |
| 8115 var enterEvent = new SyntheticEventCtor(enterEventType, eventTypePrefix + 'enter', to, nativeEvent, nativeEventTarget); | |
| 8116 enterEvent.target = toNode; | |
| 8117 enterEvent.relatedTarget = fromNode; | |
| 8118 enter = enterEvent; | |
| 8119 } | |
| 8120 | |
| 8121 accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to); | |
| 8122 } | |
| 8123 | |
| 8124 /** | |
| 8125 * inlined Object.is polyfill to avoid requiring consumers ship their own | |
| 8126 * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is | |
| 8127 */ | |
| 8128 function is(x, y) { | |
| 8129 return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare | |
| 8130 ; | |
| 8131 } | |
| 8132 | |
| 8133 var objectIs = typeof Object.is === 'function' ? Object.is : is; | |
| 8134 | |
| 8135 /** | |
| 8136 * Performs equality by iterating through keys on an object and returning false | |
| 8137 * when any key has values which are not strictly equal between the arguments. | |
| 8138 * Returns true when the values of all keys are strictly equal. | |
| 8139 */ | |
| 8140 | |
| 8141 function shallowEqual(objA, objB) { | |
| 8142 if (objectIs(objA, objB)) { | |
| 8143 return true; | |
| 8144 } | |
| 8145 | |
| 8146 if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { | |
| 8147 return false; | |
| 8148 } | |
| 8149 | |
| 8150 var keysA = Object.keys(objA); | |
| 8151 var keysB = Object.keys(objB); | |
| 8152 | |
| 8153 if (keysA.length !== keysB.length) { | |
| 8154 return false; | |
| 8155 } // Test for A's keys different from B. | |
| 8156 | |
| 8157 | |
| 8158 for (var i = 0; i < keysA.length; i++) { | |
| 8159 var currentKey = keysA[i]; | |
| 8160 | |
| 8161 if (!hasOwnProperty.call(objB, currentKey) || !objectIs(objA[currentKey], objB[currentKey])) { | |
| 8162 return false; | |
| 8163 } | |
| 8164 } | |
| 8165 | |
| 8166 return true; | |
| 8167 } | |
| 8168 | |
| 8169 /** | |
| 8170 * Given any node return the first leaf node without children. | |
| 8171 * | |
| 8172 * @param {DOMElement|DOMTextNode} node | |
| 8173 * @return {DOMElement|DOMTextNode} | |
| 8174 */ | |
| 8175 | |
| 8176 function getLeafNode(node) { | |
| 8177 while (node && node.firstChild) { | |
| 8178 node = node.firstChild; | |
| 8179 } | |
| 8180 | |
| 8181 return node; | |
| 8182 } | |
| 8183 /** | |
| 8184 * Get the next sibling within a container. This will walk up the | |
| 8185 * DOM if a node's siblings have been exhausted. | |
| 8186 * | |
| 8187 * @param {DOMElement|DOMTextNode} node | |
| 8188 * @return {?DOMElement|DOMTextNode} | |
| 8189 */ | |
| 8190 | |
| 8191 | |
| 8192 function getSiblingNode(node) { | |
| 8193 while (node) { | |
| 8194 if (node.nextSibling) { | |
| 8195 return node.nextSibling; | |
| 8196 } | |
| 8197 | |
| 8198 node = node.parentNode; | |
| 8199 } | |
| 8200 } | |
| 8201 /** | |
| 8202 * Get object describing the nodes which contain characters at offset. | |
| 8203 * | |
| 8204 * @param {DOMElement|DOMTextNode} root | |
| 8205 * @param {number} offset | |
| 8206 * @return {?object} | |
| 8207 */ | |
| 8208 | |
| 8209 | |
| 8210 function getNodeForCharacterOffset(root, offset) { | |
| 8211 var node = getLeafNode(root); | |
| 8212 var nodeStart = 0; | |
| 8213 var nodeEnd = 0; | |
| 8214 | |
| 8215 while (node) { | |
| 8216 if (node.nodeType === TEXT_NODE) { | |
| 8217 nodeEnd = nodeStart + node.textContent.length; | |
| 8218 | |
| 8219 if (nodeStart <= offset && nodeEnd >= offset) { | |
| 8220 return { | |
| 8221 node: node, | |
| 8222 offset: offset - nodeStart | |
| 8223 }; | |
| 8224 } | |
| 8225 | |
| 8226 nodeStart = nodeEnd; | |
| 8227 } | |
| 8228 | |
| 8229 node = getLeafNode(getSiblingNode(node)); | |
| 8230 } | |
| 8231 } | |
| 8232 | |
| 8233 /** | |
| 8234 * @param {DOMElement} outerNode | |
| 8235 * @return {?object} | |
| 8236 */ | |
| 8237 | |
| 8238 function getOffsets(outerNode) { | |
| 8239 var ownerDocument = outerNode.ownerDocument; | |
| 8240 var win = ownerDocument && ownerDocument.defaultView || window; | |
| 8241 var selection = win.getSelection && win.getSelection(); | |
| 8242 | |
| 8243 if (!selection || selection.rangeCount === 0) { | |
| 8244 return null; | |
| 8245 } | |
| 8246 | |
| 8247 var anchorNode = selection.anchorNode, | |
| 8248 anchorOffset = selection.anchorOffset, | |
| 8249 focusNode = selection.focusNode, | |
| 8250 focusOffset = selection.focusOffset; // In Firefox, anchorNode and focusNode can be "anonymous divs", e.g. the | |
| 8251 // up/down buttons on an <input type="number">. Anonymous divs do not seem to | |
| 8252 // expose properties, triggering a "Permission denied error" if any of its | |
| 8253 // properties are accessed. The only seemingly possible way to avoid erroring | |
| 8254 // is to access a property that typically works for non-anonymous divs and | |
| 8255 // catch any error that may otherwise arise. See | |
| 8256 // https://bugzilla.mozilla.org/show_bug.cgi?id=208427 | |
| 8257 | |
| 8258 try { | |
| 8259 /* eslint-disable no-unused-expressions */ | |
| 8260 anchorNode.nodeType; | |
| 8261 focusNode.nodeType; | |
| 8262 /* eslint-enable no-unused-expressions */ | |
| 8263 } catch (e) { | |
| 8264 return null; | |
| 8265 } | |
| 8266 | |
| 8267 return getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset); | |
| 8268 } | |
| 8269 /** | |
| 8270 * Returns {start, end} where `start` is the character/codepoint index of | |
| 8271 * (anchorNode, anchorOffset) within the textContent of `outerNode`, and | |
| 8272 * `end` is the index of (focusNode, focusOffset). | |
| 8273 * | |
| 8274 * Returns null if you pass in garbage input but we should probably just crash. | |
| 8275 * | |
| 8276 * Exported only for testing. | |
| 8277 */ | |
| 8278 | |
| 8279 function getModernOffsetsFromPoints(outerNode, anchorNode, anchorOffset, focusNode, focusOffset) { | |
| 8280 var length = 0; | |
| 8281 var start = -1; | |
| 8282 var end = -1; | |
| 8283 var indexWithinAnchor = 0; | |
| 8284 var indexWithinFocus = 0; | |
| 8285 var node = outerNode; | |
| 8286 var parentNode = null; | |
| 8287 | |
| 8288 outer: while (true) { | |
| 8289 var next = null; | |
| 8290 | |
| 8291 while (true) { | |
| 8292 if (node === anchorNode && (anchorOffset === 0 || node.nodeType === TEXT_NODE)) { | |
| 8293 start = length + anchorOffset; | |
| 8294 } | |
| 8295 | |
| 8296 if (node === focusNode && (focusOffset === 0 || node.nodeType === TEXT_NODE)) { | |
| 8297 end = length + focusOffset; | |
| 8298 } | |
| 8299 | |
| 8300 if (node.nodeType === TEXT_NODE) { | |
| 8301 length += node.nodeValue.length; | |
| 8302 } | |
| 8303 | |
| 8304 if ((next = node.firstChild) === null) { | |
| 8305 break; | |
| 8306 } // Moving from `node` to its first child `next`. | |
| 8307 | |
| 8308 | |
| 8309 parentNode = node; | |
| 8310 node = next; | |
| 8311 } | |
| 8312 | |
| 8313 while (true) { | |
| 8314 if (node === outerNode) { | |
| 8315 // If `outerNode` has children, this is always the second time visiting | |
| 8316 // it. If it has no children, this is still the first loop, and the only | |
| 8317 // valid selection is anchorNode and focusNode both equal to this node | |
| 8318 // and both offsets 0, in which case we will have handled above. | |
| 8319 break outer; | |
| 8320 } | |
| 8321 | |
| 8322 if (parentNode === anchorNode && ++indexWithinAnchor === anchorOffset) { | |
| 8323 start = length; | |
| 8324 } | |
| 8325 | |
| 8326 if (parentNode === focusNode && ++indexWithinFocus === focusOffset) { | |
| 8327 end = length; | |
| 8328 } | |
| 8329 | |
| 8330 if ((next = node.nextSibling) !== null) { | |
| 8331 break; | |
| 8332 } | |
| 8333 | |
| 8334 node = parentNode; | |
| 8335 parentNode = node.parentNode; | |
| 8336 } // Moving from `node` to its next sibling `next`. | |
| 8337 | |
| 8338 | |
| 8339 node = next; | |
| 8340 } | |
| 8341 | |
| 8342 if (start === -1 || end === -1) { | |
| 8343 // This should never happen. (Would happen if the anchor/focus nodes aren't | |
| 8344 // actually inside the passed-in node.) | |
| 8345 return null; | |
| 8346 } | |
| 8347 | |
| 8348 return { | |
| 8349 start: start, | |
| 8350 end: end | |
| 8351 }; | |
| 8352 } | |
| 8353 /** | |
| 8354 * In modern non-IE browsers, we can support both forward and backward | |
| 8355 * selections. | |
| 8356 * | |
| 8357 * Note: IE10+ supports the Selection object, but it does not support | |
| 8358 * the `extend` method, which means that even in modern IE, it's not possible | |
| 8359 * to programmatically create a backward selection. Thus, for all IE | |
| 8360 * versions, we use the old IE API to create our selections. | |
| 8361 * | |
| 8362 * @param {DOMElement|DOMTextNode} node | |
| 8363 * @param {object} offsets | |
| 8364 */ | |
| 8365 | |
| 8366 function setOffsets(node, offsets) { | |
| 8367 var doc = node.ownerDocument || document; | |
| 8368 var win = doc && doc.defaultView || window; // Edge fails with "Object expected" in some scenarios. | |
| 8369 // (For instance: TinyMCE editor used in a list component that supports pasting to add more, | |
| 8370 // fails when pasting 100+ items) | |
| 8371 | |
| 8372 if (!win.getSelection) { | |
| 8373 return; | |
| 8374 } | |
| 8375 | |
| 8376 var selection = win.getSelection(); | |
| 8377 var length = node.textContent.length; | |
| 8378 var start = Math.min(offsets.start, length); | |
| 8379 var end = offsets.end === undefined ? start : Math.min(offsets.end, length); // IE 11 uses modern selection, but doesn't support the extend method. | |
| 8380 // Flip backward selections, so we can set with a single range. | |
| 8381 | |
| 8382 if (!selection.extend && start > end) { | |
| 8383 var temp = end; | |
| 8384 end = start; | |
| 8385 start = temp; | |
| 8386 } | |
| 8387 | |
| 8388 var startMarker = getNodeForCharacterOffset(node, start); | |
| 8389 var endMarker = getNodeForCharacterOffset(node, end); | |
| 8390 | |
| 8391 if (startMarker && endMarker) { | |
| 8392 if (selection.rangeCount === 1 && selection.anchorNode === startMarker.node && selection.anchorOffset === startMarker.offset && selection.focusNode === endMarker.node && selection.focusOffset === endMarker.offset) { | |
| 8393 return; | |
| 8394 } | |
| 8395 | |
| 8396 var range = doc.createRange(); | |
| 8397 range.setStart(startMarker.node, startMarker.offset); | |
| 8398 selection.removeAllRanges(); | |
| 8399 | |
| 8400 if (start > end) { | |
| 8401 selection.addRange(range); | |
| 8402 selection.extend(endMarker.node, endMarker.offset); | |
| 8403 } else { | |
| 8404 range.setEnd(endMarker.node, endMarker.offset); | |
| 8405 selection.addRange(range); | |
| 8406 } | |
| 8407 } | |
| 8408 } | |
| 8409 | |
| 8410 function isTextNode(node) { | |
| 8411 return node && node.nodeType === TEXT_NODE; | |
| 8412 } | |
| 8413 | |
| 8414 function containsNode(outerNode, innerNode) { | |
| 8415 if (!outerNode || !innerNode) { | |
| 8416 return false; | |
| 8417 } else if (outerNode === innerNode) { | |
| 8418 return true; | |
| 8419 } else if (isTextNode(outerNode)) { | |
| 8420 return false; | |
| 8421 } else if (isTextNode(innerNode)) { | |
| 8422 return containsNode(outerNode, innerNode.parentNode); | |
| 8423 } else if ('contains' in outerNode) { | |
| 8424 return outerNode.contains(innerNode); | |
| 8425 } else if (outerNode.compareDocumentPosition) { | |
| 8426 return !!(outerNode.compareDocumentPosition(innerNode) & 16); | |
| 8427 } else { | |
| 8428 return false; | |
| 8429 } | |
| 8430 } | |
| 8431 | |
| 8432 function isInDocument(node) { | |
| 8433 return node && node.ownerDocument && containsNode(node.ownerDocument.documentElement, node); | |
| 8434 } | |
| 8435 | |
| 8436 function isSameOriginFrame(iframe) { | |
| 8437 try { | |
| 8438 // Accessing the contentDocument of a HTMLIframeElement can cause the browser | |
| 8439 // to throw, e.g. if it has a cross-origin src attribute. | |
| 8440 // Safari will show an error in the console when the access results in "Blocked a frame with origin". e.g: | |
| 8441 // iframe.contentDocument.defaultView; | |
| 8442 // A safety way is to access one of the cross origin properties: Window or Location | |
| 8443 // Which might result in "SecurityError" DOM Exception and it is compatible to Safari. | |
| 8444 // https://html.spec.whatwg.org/multipage/browsers.html#integration-with-idl | |
| 8445 return typeof iframe.contentWindow.location.href === 'string'; | |
| 8446 } catch (err) { | |
| 8447 return false; | |
| 8448 } | |
| 8449 } | |
| 8450 | |
| 8451 function getActiveElementDeep() { | |
| 8452 var win = window; | |
| 8453 var element = getActiveElement(); | |
| 8454 | |
| 8455 while (element instanceof win.HTMLIFrameElement) { | |
| 8456 if (isSameOriginFrame(element)) { | |
| 8457 win = element.contentWindow; | |
| 8458 } else { | |
| 8459 return element; | |
| 8460 } | |
| 8461 | |
| 8462 element = getActiveElement(win.document); | |
| 8463 } | |
| 8464 | |
| 8465 return element; | |
| 8466 } | |
| 8467 /** | |
| 8468 * @ReactInputSelection: React input selection module. Based on Selection.js, | |
| 8469 * but modified to be suitable for react and has a couple of bug fixes (doesn't | |
| 8470 * assume buttons have range selections allowed). | |
| 8471 * Input selection module for React. | |
| 8472 */ | |
| 8473 | |
| 8474 /** | |
| 8475 * @hasSelectionCapabilities: we get the element types that support selection | |
| 8476 * from https://html.spec.whatwg.org/#do-not-apply, looking at `selectionStart` | |
| 8477 * and `selectionEnd` rows. | |
| 8478 */ | |
| 8479 | |
| 8480 | |
| 8481 function hasSelectionCapabilities(elem) { | |
| 8482 var nodeName = elem && elem.nodeName && elem.nodeName.toLowerCase(); | |
| 8483 return nodeName && (nodeName === 'input' && (elem.type === 'text' || elem.type === 'search' || elem.type === 'tel' || elem.type === 'url' || elem.type === 'password') || nodeName === 'textarea' || elem.contentEditable === 'true'); | |
| 8484 } | |
| 8485 function getSelectionInformation() { | |
| 8486 var focusedElem = getActiveElementDeep(); | |
| 8487 return { | |
| 8488 focusedElem: focusedElem, | |
| 8489 selectionRange: hasSelectionCapabilities(focusedElem) ? getSelection(focusedElem) : null | |
| 8490 }; | |
| 8491 } | |
| 8492 /** | |
| 8493 * @restoreSelection: If any selection information was potentially lost, | |
| 8494 * restore it. This is useful when performing operations that could remove dom | |
| 8495 * nodes and place them back in, resulting in focus being lost. | |
| 8496 */ | |
| 8497 | |
| 8498 function restoreSelection(priorSelectionInformation) { | |
| 8499 var curFocusedElem = getActiveElementDeep(); | |
| 8500 var priorFocusedElem = priorSelectionInformation.focusedElem; | |
| 8501 var priorSelectionRange = priorSelectionInformation.selectionRange; | |
| 8502 | |
| 8503 if (curFocusedElem !== priorFocusedElem && isInDocument(priorFocusedElem)) { | |
| 8504 if (priorSelectionRange !== null && hasSelectionCapabilities(priorFocusedElem)) { | |
| 8505 setSelection(priorFocusedElem, priorSelectionRange); | |
| 8506 } // Focusing a node can change the scroll position, which is undesirable | |
| 8507 | |
| 8508 | |
| 8509 var ancestors = []; | |
| 8510 var ancestor = priorFocusedElem; | |
| 8511 | |
| 8512 while (ancestor = ancestor.parentNode) { | |
| 8513 if (ancestor.nodeType === ELEMENT_NODE) { | |
| 8514 ancestors.push({ | |
| 8515 element: ancestor, | |
| 8516 left: ancestor.scrollLeft, | |
| 8517 top: ancestor.scrollTop | |
| 8518 }); | |
| 8519 } | |
| 8520 } | |
| 8521 | |
| 8522 if (typeof priorFocusedElem.focus === 'function') { | |
| 8523 priorFocusedElem.focus(); | |
| 8524 } | |
| 8525 | |
| 8526 for (var i = 0; i < ancestors.length; i++) { | |
| 8527 var info = ancestors[i]; | |
| 8528 info.element.scrollLeft = info.left; | |
| 8529 info.element.scrollTop = info.top; | |
| 8530 } | |
| 8531 } | |
| 8532 } | |
| 8533 /** | |
| 8534 * @getSelection: Gets the selection bounds of a focused textarea, input or | |
| 8535 * contentEditable node. | |
| 8536 * -@input: Look up selection bounds of this input | |
| 8537 * -@return {start: selectionStart, end: selectionEnd} | |
| 8538 */ | |
| 8539 | |
| 8540 function getSelection(input) { | |
| 8541 var selection; | |
| 8542 | |
| 8543 if ('selectionStart' in input) { | |
| 8544 // Modern browser with input or textarea. | |
| 8545 selection = { | |
| 8546 start: input.selectionStart, | |
| 8547 end: input.selectionEnd | |
| 8548 }; | |
| 8549 } else { | |
| 8550 // Content editable or old IE textarea. | |
| 8551 selection = getOffsets(input); | |
| 8552 } | |
| 8553 | |
| 8554 return selection || { | |
| 8555 start: 0, | |
| 8556 end: 0 | |
| 8557 }; | |
| 8558 } | |
| 8559 /** | |
| 8560 * @setSelection: Sets the selection bounds of a textarea or input and focuses | |
| 8561 * the input. | |
| 8562 * -@input Set selection bounds of this input or textarea | |
| 8563 * -@offsets Object of same form that is returned from get* | |
| 8564 */ | |
| 8565 | |
| 8566 function setSelection(input, offsets) { | |
| 8567 var start = offsets.start; | |
| 8568 var end = offsets.end; | |
| 8569 | |
| 8570 if (end === undefined) { | |
| 8571 end = start; | |
| 8572 } | |
| 8573 | |
| 8574 if ('selectionStart' in input) { | |
| 8575 input.selectionStart = start; | |
| 8576 input.selectionEnd = Math.min(end, input.value.length); | |
| 8577 } else { | |
| 8578 setOffsets(input, offsets); | |
| 8579 } | |
| 8580 } | |
| 8581 | |
| 8582 var skipSelectionChangeEvent = canUseDOM && 'documentMode' in document && document.documentMode <= 11; | |
| 8583 | |
| 8584 function registerEvents$3() { | |
| 8585 registerTwoPhaseEvent('onSelect', ['focusout', 'contextmenu', 'dragend', 'focusin', 'keydown', 'keyup', 'mousedown', 'mouseup', 'selectionchange']); | |
| 8586 } | |
| 8587 | |
| 8588 var activeElement$1 = null; | |
| 8589 var activeElementInst$1 = null; | |
| 8590 var lastSelection = null; | |
| 8591 var mouseDown = false; | |
| 8592 /** | |
| 8593 * Get an object which is a unique representation of the current selection. | |
| 8594 * | |
| 8595 * The return value will not be consistent across nodes or browsers, but | |
| 8596 * two identical selections on the same node will return identical objects. | |
| 8597 */ | |
| 8598 | |
| 8599 function getSelection$1(node) { | |
| 8600 if ('selectionStart' in node && hasSelectionCapabilities(node)) { | |
| 8601 return { | |
| 8602 start: node.selectionStart, | |
| 8603 end: node.selectionEnd | |
| 8604 }; | |
| 8605 } else { | |
| 8606 var win = node.ownerDocument && node.ownerDocument.defaultView || window; | |
| 8607 var selection = win.getSelection(); | |
| 8608 return { | |
| 8609 anchorNode: selection.anchorNode, | |
| 8610 anchorOffset: selection.anchorOffset, | |
| 8611 focusNode: selection.focusNode, | |
| 8612 focusOffset: selection.focusOffset | |
| 8613 }; | |
| 8614 } | |
| 8615 } | |
| 8616 /** | |
| 8617 * Get document associated with the event target. | |
| 8618 */ | |
| 8619 | |
| 8620 | |
| 8621 function getEventTargetDocument(eventTarget) { | |
| 8622 return eventTarget.window === eventTarget ? eventTarget.document : eventTarget.nodeType === DOCUMENT_NODE ? eventTarget : eventTarget.ownerDocument; | |
| 8623 } | |
| 8624 /** | |
| 8625 * Poll selection to see whether it's changed. | |
| 8626 * | |
| 8627 * @param {object} nativeEvent | |
| 8628 * @param {object} nativeEventTarget | |
| 8629 * @return {?SyntheticEvent} | |
| 8630 */ | |
| 8631 | |
| 8632 | |
| 8633 function constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget) { | |
| 8634 // Ensure we have the right element, and that the user is not dragging a | |
| 8635 // selection (this matches native `select` event behavior). In HTML5, select | |
| 8636 // fires only on input and textarea thus if there's no focused element we | |
| 8637 // won't dispatch. | |
| 8638 var doc = getEventTargetDocument(nativeEventTarget); | |
| 8639 | |
| 8640 if (mouseDown || activeElement$1 == null || activeElement$1 !== getActiveElement(doc)) { | |
| 8641 return; | |
| 8642 } // Only fire when selection has actually changed. | |
| 8643 | |
| 8644 | |
| 8645 var currentSelection = getSelection$1(activeElement$1); | |
| 8646 | |
| 8647 if (!lastSelection || !shallowEqual(lastSelection, currentSelection)) { | |
| 8648 lastSelection = currentSelection; | |
| 8649 var listeners = accumulateTwoPhaseListeners(activeElementInst$1, 'onSelect'); | |
| 8650 | |
| 8651 if (listeners.length > 0) { | |
| 8652 var event = new SyntheticEvent('onSelect', 'select', null, nativeEvent, nativeEventTarget); | |
| 8653 dispatchQueue.push({ | |
| 8654 event: event, | |
| 8655 listeners: listeners | |
| 8656 }); | |
| 8657 event.target = activeElement$1; | |
| 8658 } | |
| 8659 } | |
| 8660 } | |
| 8661 /** | |
| 8662 * This plugin creates an `onSelect` event that normalizes select events | |
| 8663 * across form elements. | |
| 8664 * | |
| 8665 * Supported elements are: | |
| 8666 * - input (see `isTextInputElement`) | |
| 8667 * - textarea | |
| 8668 * - contentEditable | |
| 8669 * | |
| 8670 * This differs from native browser implementations in the following ways: | |
| 8671 * - Fires on contentEditable fields as well as inputs. | |
| 8672 * - Fires for collapsed selection. | |
| 8673 * - Fires after user input. | |
| 8674 */ | |
| 8675 | |
| 8676 | |
| 8677 function extractEvents$3(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 8678 var targetNode = targetInst ? getNodeFromInstance(targetInst) : window; | |
| 8679 | |
| 8680 switch (domEventName) { | |
| 8681 // Track the input node that has focus. | |
| 8682 case 'focusin': | |
| 8683 if (isTextInputElement(targetNode) || targetNode.contentEditable === 'true') { | |
| 8684 activeElement$1 = targetNode; | |
| 8685 activeElementInst$1 = targetInst; | |
| 8686 lastSelection = null; | |
| 8687 } | |
| 8688 | |
| 8689 break; | |
| 8690 | |
| 8691 case 'focusout': | |
| 8692 activeElement$1 = null; | |
| 8693 activeElementInst$1 = null; | |
| 8694 lastSelection = null; | |
| 8695 break; | |
| 8696 // Don't fire the event while the user is dragging. This matches the | |
| 8697 // semantics of the native select event. | |
| 8698 | |
| 8699 case 'mousedown': | |
| 8700 mouseDown = true; | |
| 8701 break; | |
| 8702 | |
| 8703 case 'contextmenu': | |
| 8704 case 'mouseup': | |
| 8705 case 'dragend': | |
| 8706 mouseDown = false; | |
| 8707 constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget); | |
| 8708 break; | |
| 8709 // Chrome and IE fire non-standard event when selection is changed (and | |
| 8710 // sometimes when it hasn't). IE's event fires out of order with respect | |
| 8711 // to key and input events on deletion, so we discard it. | |
| 8712 // | |
| 8713 // Firefox doesn't support selectionchange, so check selection status | |
| 8714 // after each key entry. The selection changes after keydown and before | |
| 8715 // keyup, but we check on keydown as well in the case of holding down a | |
| 8716 // key, when multiple keydown events are fired but only one keyup is. | |
| 8717 // This is also our approach for IE handling, for the reason above. | |
| 8718 | |
| 8719 case 'selectionchange': | |
| 8720 if (skipSelectionChangeEvent) { | |
| 8721 break; | |
| 8722 } | |
| 8723 | |
| 8724 // falls through | |
| 8725 | |
| 8726 case 'keydown': | |
| 8727 case 'keyup': | |
| 8728 constructSelectEvent(dispatchQueue, nativeEvent, nativeEventTarget); | |
| 8729 } | |
| 8730 } | |
| 8731 | |
| 8732 /** | |
| 8733 * Generate a mapping of standard vendor prefixes using the defined style property and event name. | |
| 8734 * | |
| 8735 * @param {string} styleProp | |
| 8736 * @param {string} eventName | |
| 8737 * @returns {object} | |
| 8738 */ | |
| 8739 | |
| 8740 function makePrefixMap(styleProp, eventName) { | |
| 8741 var prefixes = {}; | |
| 8742 prefixes[styleProp.toLowerCase()] = eventName.toLowerCase(); | |
| 8743 prefixes['Webkit' + styleProp] = 'webkit' + eventName; | |
| 8744 prefixes['Moz' + styleProp] = 'moz' + eventName; | |
| 8745 return prefixes; | |
| 8746 } | |
| 8747 /** | |
| 8748 * A list of event names to a configurable list of vendor prefixes. | |
| 8749 */ | |
| 8750 | |
| 8751 | |
| 8752 var vendorPrefixes = { | |
| 8753 animationend: makePrefixMap('Animation', 'AnimationEnd'), | |
| 8754 animationiteration: makePrefixMap('Animation', 'AnimationIteration'), | |
| 8755 animationstart: makePrefixMap('Animation', 'AnimationStart'), | |
| 8756 transitionend: makePrefixMap('Transition', 'TransitionEnd') | |
| 8757 }; | |
| 8758 /** | |
| 8759 * Event names that have already been detected and prefixed (if applicable). | |
| 8760 */ | |
| 8761 | |
| 8762 var prefixedEventNames = {}; | |
| 8763 /** | |
| 8764 * Element to check for prefixes on. | |
| 8765 */ | |
| 8766 | |
| 8767 var style = {}; | |
| 8768 /** | |
| 8769 * Bootstrap if a DOM exists. | |
| 8770 */ | |
| 8771 | |
| 8772 if (canUseDOM) { | |
| 8773 style = document.createElement('div').style; // On some platforms, in particular some releases of Android 4.x, | |
| 8774 // the un-prefixed "animation" and "transition" properties are defined on the | |
| 8775 // style object but the events that fire will still be prefixed, so we need | |
| 8776 // to check if the un-prefixed events are usable, and if not remove them from the map. | |
| 8777 | |
| 8778 if (!('AnimationEvent' in window)) { | |
| 8779 delete vendorPrefixes.animationend.animation; | |
| 8780 delete vendorPrefixes.animationiteration.animation; | |
| 8781 delete vendorPrefixes.animationstart.animation; | |
| 8782 } // Same as above | |
| 8783 | |
| 8784 | |
| 8785 if (!('TransitionEvent' in window)) { | |
| 8786 delete vendorPrefixes.transitionend.transition; | |
| 8787 } | |
| 8788 } | |
| 8789 /** | |
| 8790 * Attempts to determine the correct vendor prefixed event name. | |
| 8791 * | |
| 8792 * @param {string} eventName | |
| 8793 * @returns {string} | |
| 8794 */ | |
| 8795 | |
| 8796 | |
| 8797 function getVendorPrefixedEventName(eventName) { | |
| 8798 if (prefixedEventNames[eventName]) { | |
| 8799 return prefixedEventNames[eventName]; | |
| 8800 } else if (!vendorPrefixes[eventName]) { | |
| 8801 return eventName; | |
| 8802 } | |
| 8803 | |
| 8804 var prefixMap = vendorPrefixes[eventName]; | |
| 8805 | |
| 8806 for (var styleProp in prefixMap) { | |
| 8807 if (prefixMap.hasOwnProperty(styleProp) && styleProp in style) { | |
| 8808 return prefixedEventNames[eventName] = prefixMap[styleProp]; | |
| 8809 } | |
| 8810 } | |
| 8811 | |
| 8812 return eventName; | |
| 8813 } | |
| 8814 | |
| 8815 var ANIMATION_END = getVendorPrefixedEventName('animationend'); | |
| 8816 var ANIMATION_ITERATION = getVendorPrefixedEventName('animationiteration'); | |
| 8817 var ANIMATION_START = getVendorPrefixedEventName('animationstart'); | |
| 8818 var TRANSITION_END = getVendorPrefixedEventName('transitionend'); | |
| 8819 | |
| 8820 var topLevelEventsToReactNames = new Map(); // NOTE: Capitalization is important in this list! | |
| 8821 // | |
| 8822 // E.g. it needs "pointerDown", not "pointerdown". | |
| 8823 // This is because we derive both React name ("onPointerDown") | |
| 8824 // and DOM name ("pointerdown") from the same list. | |
| 8825 // | |
| 8826 // Exceptions that don't match this convention are listed separately. | |
| 8827 // | |
| 8828 // prettier-ignore | |
| 8829 | |
| 8830 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']; | |
| 8831 | |
| 8832 function registerSimpleEvent(domEventName, reactName) { | |
| 8833 topLevelEventsToReactNames.set(domEventName, reactName); | |
| 8834 registerTwoPhaseEvent(reactName, [domEventName]); | |
| 8835 } | |
| 8836 | |
| 8837 function registerSimpleEvents() { | |
| 8838 for (var i = 0; i < simpleEventPluginEvents.length; i++) { | |
| 8839 var eventName = simpleEventPluginEvents[i]; | |
| 8840 var domEventName = eventName.toLowerCase(); | |
| 8841 var capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1); | |
| 8842 registerSimpleEvent(domEventName, 'on' + capitalizedEvent); | |
| 8843 } // Special cases where event names don't match. | |
| 8844 | |
| 8845 | |
| 8846 registerSimpleEvent(ANIMATION_END, 'onAnimationEnd'); | |
| 8847 registerSimpleEvent(ANIMATION_ITERATION, 'onAnimationIteration'); | |
| 8848 registerSimpleEvent(ANIMATION_START, 'onAnimationStart'); | |
| 8849 registerSimpleEvent('dblclick', 'onDoubleClick'); | |
| 8850 registerSimpleEvent('focusin', 'onFocus'); | |
| 8851 registerSimpleEvent('focusout', 'onBlur'); | |
| 8852 registerSimpleEvent(TRANSITION_END, 'onTransitionEnd'); | |
| 8853 } | |
| 8854 | |
| 8855 function extractEvents$4(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 8856 var reactName = topLevelEventsToReactNames.get(domEventName); | |
| 8857 | |
| 8858 if (reactName === undefined) { | |
| 8859 return; | |
| 8860 } | |
| 8861 | |
| 8862 var SyntheticEventCtor = SyntheticEvent; | |
| 8863 var reactEventType = domEventName; | |
| 8864 | |
| 8865 switch (domEventName) { | |
| 8866 case 'keypress': | |
| 8867 // Firefox creates a keypress event for function keys too. This removes | |
| 8868 // the unwanted keypress events. Enter is however both printable and | |
| 8869 // non-printable. One would expect Tab to be as well (but it isn't). | |
| 8870 if (getEventCharCode(nativeEvent) === 0) { | |
| 8871 return; | |
| 8872 } | |
| 8873 | |
| 8874 /* falls through */ | |
| 8875 | |
| 8876 case 'keydown': | |
| 8877 case 'keyup': | |
| 8878 SyntheticEventCtor = SyntheticKeyboardEvent; | |
| 8879 break; | |
| 8880 | |
| 8881 case 'focusin': | |
| 8882 reactEventType = 'focus'; | |
| 8883 SyntheticEventCtor = SyntheticFocusEvent; | |
| 8884 break; | |
| 8885 | |
| 8886 case 'focusout': | |
| 8887 reactEventType = 'blur'; | |
| 8888 SyntheticEventCtor = SyntheticFocusEvent; | |
| 8889 break; | |
| 8890 | |
| 8891 case 'beforeblur': | |
| 8892 case 'afterblur': | |
| 8893 SyntheticEventCtor = SyntheticFocusEvent; | |
| 8894 break; | |
| 8895 | |
| 8896 case 'click': | |
| 8897 // Firefox creates a click event on right mouse clicks. This removes the | |
| 8898 // unwanted click events. | |
| 8899 if (nativeEvent.button === 2) { | |
| 8900 return; | |
| 8901 } | |
| 8902 | |
| 8903 /* falls through */ | |
| 8904 | |
| 8905 case 'auxclick': | |
| 8906 case 'dblclick': | |
| 8907 case 'mousedown': | |
| 8908 case 'mousemove': | |
| 8909 case 'mouseup': // TODO: Disabled elements should not respond to mouse events | |
| 8910 | |
| 8911 /* falls through */ | |
| 8912 | |
| 8913 case 'mouseout': | |
| 8914 case 'mouseover': | |
| 8915 case 'contextmenu': | |
| 8916 SyntheticEventCtor = SyntheticMouseEvent; | |
| 8917 break; | |
| 8918 | |
| 8919 case 'drag': | |
| 8920 case 'dragend': | |
| 8921 case 'dragenter': | |
| 8922 case 'dragexit': | |
| 8923 case 'dragleave': | |
| 8924 case 'dragover': | |
| 8925 case 'dragstart': | |
| 8926 case 'drop': | |
| 8927 SyntheticEventCtor = SyntheticDragEvent; | |
| 8928 break; | |
| 8929 | |
| 8930 case 'touchcancel': | |
| 8931 case 'touchend': | |
| 8932 case 'touchmove': | |
| 8933 case 'touchstart': | |
| 8934 SyntheticEventCtor = SyntheticTouchEvent; | |
| 8935 break; | |
| 8936 | |
| 8937 case ANIMATION_END: | |
| 8938 case ANIMATION_ITERATION: | |
| 8939 case ANIMATION_START: | |
| 8940 SyntheticEventCtor = SyntheticAnimationEvent; | |
| 8941 break; | |
| 8942 | |
| 8943 case TRANSITION_END: | |
| 8944 SyntheticEventCtor = SyntheticTransitionEvent; | |
| 8945 break; | |
| 8946 | |
| 8947 case 'scroll': | |
| 8948 SyntheticEventCtor = SyntheticUIEvent; | |
| 8949 break; | |
| 8950 | |
| 8951 case 'wheel': | |
| 8952 SyntheticEventCtor = SyntheticWheelEvent; | |
| 8953 break; | |
| 8954 | |
| 8955 case 'copy': | |
| 8956 case 'cut': | |
| 8957 case 'paste': | |
| 8958 SyntheticEventCtor = SyntheticClipboardEvent; | |
| 8959 break; | |
| 8960 | |
| 8961 case 'gotpointercapture': | |
| 8962 case 'lostpointercapture': | |
| 8963 case 'pointercancel': | |
| 8964 case 'pointerdown': | |
| 8965 case 'pointermove': | |
| 8966 case 'pointerout': | |
| 8967 case 'pointerover': | |
| 8968 case 'pointerup': | |
| 8969 SyntheticEventCtor = SyntheticPointerEvent; | |
| 8970 break; | |
| 8971 } | |
| 8972 | |
| 8973 var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0; | |
| 8974 | |
| 8975 { | |
| 8976 // Some events don't bubble in the browser. | |
| 8977 // In the past, React has always bubbled them, but this can be surprising. | |
| 8978 // We're going to try aligning closer to the browser behavior by not bubbling | |
| 8979 // them in React either. We'll start by not bubbling onScroll, and then expand. | |
| 8980 var accumulateTargetOnly = !inCapturePhase && // TODO: ideally, we'd eventually add all events from | |
| 8981 // nonDelegatedEvents list in DOMPluginEventSystem. | |
| 8982 // Then we can remove this special list. | |
| 8983 // This is a breaking change that can wait until React 18. | |
| 8984 domEventName === 'scroll'; | |
| 8985 | |
| 8986 var _listeners = accumulateSinglePhaseListeners(targetInst, reactName, nativeEvent.type, inCapturePhase, accumulateTargetOnly); | |
| 8987 | |
| 8988 if (_listeners.length > 0) { | |
| 8989 // Intentionally create event lazily. | |
| 8990 var _event = new SyntheticEventCtor(reactName, reactEventType, null, nativeEvent, nativeEventTarget); | |
| 8991 | |
| 8992 dispatchQueue.push({ | |
| 8993 event: _event, | |
| 8994 listeners: _listeners | |
| 8995 }); | |
| 8996 } | |
| 8997 } | |
| 8998 } | |
| 8999 | |
| 9000 // TODO: remove top-level side effect. | |
| 9001 registerSimpleEvents(); | |
| 9002 registerEvents$2(); | |
| 9003 registerEvents$1(); | |
| 9004 registerEvents$3(); | |
| 9005 registerEvents(); | |
| 9006 | |
| 9007 function extractEvents$5(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags, targetContainer) { | |
| 9008 // TODO: we should remove the concept of a "SimpleEventPlugin". | |
| 9009 // This is the basic functionality of the event system. All | |
| 9010 // the other plugins are essentially polyfills. So the plugin | |
| 9011 // should probably be inlined somewhere and have its logic | |
| 9012 // be core the to event system. This would potentially allow | |
| 9013 // us to ship builds of React without the polyfilled plugins below. | |
| 9014 extractEvents$4(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); | |
| 9015 var shouldProcessPolyfillPlugins = (eventSystemFlags & SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS) === 0; // We don't process these events unless we are in the | |
| 9016 // event's native "bubble" phase, which means that we're | |
| 9017 // not in the capture phase. That's because we emulate | |
| 9018 // the capture phase here still. This is a trade-off, | |
| 9019 // because in an ideal world we would not emulate and use | |
| 9020 // the phases properly, like we do with the SimpleEvent | |
| 9021 // plugin. However, the plugins below either expect | |
| 9022 // emulation (EnterLeave) or use state localized to that | |
| 9023 // plugin (BeforeInput, Change, Select). The state in | |
| 9024 // these modules complicates things, as you'll essentially | |
| 9025 // get the case where the capture phase event might change | |
| 9026 // state, only for the following bubble event to come in | |
| 9027 // later and not trigger anything as the state now | |
| 9028 // invalidates the heuristics of the event plugin. We | |
| 9029 // could alter all these plugins to work in such ways, but | |
| 9030 // that might cause other unknown side-effects that we | |
| 9031 // can't foresee right now. | |
| 9032 | |
| 9033 if (shouldProcessPolyfillPlugins) { | |
| 9034 extractEvents$2(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 9035 extractEvents$1(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 9036 extractEvents$3(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 9037 extractEvents(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget); | |
| 9038 } | |
| 9039 } // List of events that need to be individually attached to media elements. | |
| 9040 | |
| 9041 | |
| 9042 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 | |
| 9043 // set them on the actual target element itself. This is primarily | |
| 9044 // because these events do not consistently bubble in the DOM. | |
| 9045 | |
| 9046 var nonDelegatedEvents = new Set(['cancel', 'close', 'invalid', 'load', 'scroll', 'toggle'].concat(mediaEventTypes)); | |
| 9047 | |
| 9048 function executeDispatch(event, listener, currentTarget) { | |
| 9049 var type = event.type || 'unknown-event'; | |
| 9050 event.currentTarget = currentTarget; | |
| 9051 invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event); | |
| 9052 event.currentTarget = null; | |
| 9053 } | |
| 9054 | |
| 9055 function processDispatchQueueItemsInOrder(event, dispatchListeners, inCapturePhase) { | |
| 9056 var previousInstance; | |
| 9057 | |
| 9058 if (inCapturePhase) { | |
| 9059 for (var i = dispatchListeners.length - 1; i >= 0; i--) { | |
| 9060 var _dispatchListeners$i = dispatchListeners[i], | |
| 9061 instance = _dispatchListeners$i.instance, | |
| 9062 currentTarget = _dispatchListeners$i.currentTarget, | |
| 9063 listener = _dispatchListeners$i.listener; | |
| 9064 | |
| 9065 if (instance !== previousInstance && event.isPropagationStopped()) { | |
| 9066 return; | |
| 9067 } | |
| 9068 | |
| 9069 executeDispatch(event, listener, currentTarget); | |
| 9070 previousInstance = instance; | |
| 9071 } | |
| 9072 } else { | |
| 9073 for (var _i = 0; _i < dispatchListeners.length; _i++) { | |
| 9074 var _dispatchListeners$_i = dispatchListeners[_i], | |
| 9075 _instance = _dispatchListeners$_i.instance, | |
| 9076 _currentTarget = _dispatchListeners$_i.currentTarget, | |
| 9077 _listener = _dispatchListeners$_i.listener; | |
| 9078 | |
| 9079 if (_instance !== previousInstance && event.isPropagationStopped()) { | |
| 9080 return; | |
| 9081 } | |
| 9082 | |
| 9083 executeDispatch(event, _listener, _currentTarget); | |
| 9084 previousInstance = _instance; | |
| 9085 } | |
| 9086 } | |
| 9087 } | |
| 9088 | |
| 9089 function processDispatchQueue(dispatchQueue, eventSystemFlags) { | |
| 9090 var inCapturePhase = (eventSystemFlags & IS_CAPTURE_PHASE) !== 0; | |
| 9091 | |
| 9092 for (var i = 0; i < dispatchQueue.length; i++) { | |
| 9093 var _dispatchQueue$i = dispatchQueue[i], | |
| 9094 event = _dispatchQueue$i.event, | |
| 9095 listeners = _dispatchQueue$i.listeners; | |
| 9096 processDispatchQueueItemsInOrder(event, listeners, inCapturePhase); // event system doesn't use pooling. | |
| 9097 } // This would be a good time to rethrow if any of the event handlers threw. | |
| 9098 | |
| 9099 | |
| 9100 rethrowCaughtError(); | |
| 9101 } | |
| 9102 | |
| 9103 function dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) { | |
| 9104 var nativeEventTarget = getEventTarget(nativeEvent); | |
| 9105 var dispatchQueue = []; | |
| 9106 extractEvents$5(dispatchQueue, domEventName, targetInst, nativeEvent, nativeEventTarget, eventSystemFlags); | |
| 9107 processDispatchQueue(dispatchQueue, eventSystemFlags); | |
| 9108 } | |
| 9109 | |
| 9110 function listenToNonDelegatedEvent(domEventName, targetElement) { | |
| 9111 { | |
| 9112 if (!nonDelegatedEvents.has(domEventName)) { | |
| 9113 error('Did not expect a listenToNonDelegatedEvent() call for "%s". ' + 'This is a bug in React. Please file an issue.', domEventName); | |
| 9114 } | |
| 9115 } | |
| 9116 | |
| 9117 var isCapturePhaseListener = false; | |
| 9118 var listenerSet = getEventListenerSet(targetElement); | |
| 9119 var listenerSetKey = getListenerSetKey(domEventName, isCapturePhaseListener); | |
| 9120 | |
| 9121 if (!listenerSet.has(listenerSetKey)) { | |
| 9122 addTrappedEventListener(targetElement, domEventName, IS_NON_DELEGATED, isCapturePhaseListener); | |
| 9123 listenerSet.add(listenerSetKey); | |
| 9124 } | |
| 9125 } | |
| 9126 function listenToNativeEvent(domEventName, isCapturePhaseListener, target) { | |
| 9127 { | |
| 9128 if (nonDelegatedEvents.has(domEventName) && !isCapturePhaseListener) { | |
| 9129 error('Did not expect a listenToNativeEvent() call for "%s" in the bubble phase. ' + 'This is a bug in React. Please file an issue.', domEventName); | |
| 9130 } | |
| 9131 } | |
| 9132 | |
| 9133 var eventSystemFlags = 0; | |
| 9134 | |
| 9135 if (isCapturePhaseListener) { | |
| 9136 eventSystemFlags |= IS_CAPTURE_PHASE; | |
| 9137 } | |
| 9138 | |
| 9139 addTrappedEventListener(target, domEventName, eventSystemFlags, isCapturePhaseListener); | |
| 9140 } // This is only used by createEventHandle when the | |
| 9141 var listeningMarker = '_reactListening' + Math.random().toString(36).slice(2); | |
| 9142 function listenToAllSupportedEvents(rootContainerElement) { | |
| 9143 if (!rootContainerElement[listeningMarker]) { | |
| 9144 rootContainerElement[listeningMarker] = true; | |
| 9145 allNativeEvents.forEach(function (domEventName) { | |
| 9146 // We handle selectionchange separately because it | |
| 9147 // doesn't bubble and needs to be on the document. | |
| 9148 if (domEventName !== 'selectionchange') { | |
| 9149 if (!nonDelegatedEvents.has(domEventName)) { | |
| 9150 listenToNativeEvent(domEventName, false, rootContainerElement); | |
| 9151 } | |
| 9152 | |
| 9153 listenToNativeEvent(domEventName, true, rootContainerElement); | |
| 9154 } | |
| 9155 }); | |
| 9156 var ownerDocument = rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument; | |
| 9157 | |
| 9158 if (ownerDocument !== null) { | |
| 9159 // The selectionchange event also needs deduplication | |
| 9160 // but it is attached to the document. | |
| 9161 if (!ownerDocument[listeningMarker]) { | |
| 9162 ownerDocument[listeningMarker] = true; | |
| 9163 listenToNativeEvent('selectionchange', false, ownerDocument); | |
| 9164 } | |
| 9165 } | |
| 9166 } | |
| 9167 } | |
| 9168 | |
| 9169 function addTrappedEventListener(targetContainer, domEventName, eventSystemFlags, isCapturePhaseListener, isDeferredListenerForLegacyFBSupport) { | |
| 9170 var listener = createEventListenerWrapperWithPriority(targetContainer, domEventName, eventSystemFlags); // If passive option is not supported, then the event will be | |
| 9171 // active and not passive. | |
| 9172 | |
| 9173 var isPassiveListener = undefined; | |
| 9174 | |
| 9175 if (passiveBrowserEventsSupported) { | |
| 9176 // Browsers introduced an intervention, making these events | |
| 9177 // passive by default on document. React doesn't bind them | |
| 9178 // to document anymore, but changing this now would undo | |
| 9179 // the performance wins from the change. So we emulate | |
| 9180 // the existing behavior manually on the roots now. | |
| 9181 // https://github.com/facebook/react/issues/19651 | |
| 9182 if (domEventName === 'touchstart' || domEventName === 'touchmove' || domEventName === 'wheel') { | |
| 9183 isPassiveListener = true; | |
| 9184 } | |
| 9185 } | |
| 9186 | |
| 9187 targetContainer = targetContainer; | |
| 9188 var unsubscribeListener; // When legacyFBSupport is enabled, it's for when we | |
| 9189 | |
| 9190 | |
| 9191 if (isCapturePhaseListener) { | |
| 9192 if (isPassiveListener !== undefined) { | |
| 9193 unsubscribeListener = addEventCaptureListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener); | |
| 9194 } else { | |
| 9195 unsubscribeListener = addEventCaptureListener(targetContainer, domEventName, listener); | |
| 9196 } | |
| 9197 } else { | |
| 9198 if (isPassiveListener !== undefined) { | |
| 9199 unsubscribeListener = addEventBubbleListenerWithPassiveFlag(targetContainer, domEventName, listener, isPassiveListener); | |
| 9200 } else { | |
| 9201 unsubscribeListener = addEventBubbleListener(targetContainer, domEventName, listener); | |
| 9202 } | |
| 9203 } | |
| 9204 } | |
| 9205 | |
| 9206 function isMatchingRootContainer(grandContainer, targetContainer) { | |
| 9207 return grandContainer === targetContainer || grandContainer.nodeType === COMMENT_NODE && grandContainer.parentNode === targetContainer; | |
| 9208 } | |
| 9209 | |
| 9210 function dispatchEventForPluginEventSystem(domEventName, eventSystemFlags, nativeEvent, targetInst, targetContainer) { | |
| 9211 var ancestorInst = targetInst; | |
| 9212 | |
| 9213 if ((eventSystemFlags & IS_EVENT_HANDLE_NON_MANAGED_NODE) === 0 && (eventSystemFlags & IS_NON_DELEGATED) === 0) { | |
| 9214 var targetContainerNode = targetContainer; // If we are using the legacy FB support flag, we | |
| 9215 | |
| 9216 if (targetInst !== null) { | |
| 9217 // The below logic attempts to work out if we need to change | |
| 9218 // the target fiber to a different ancestor. We had similar logic | |
| 9219 // in the legacy event system, except the big difference between | |
| 9220 // systems is that the modern event system now has an event listener | |
| 9221 // attached to each React Root and React Portal Root. Together, | |
| 9222 // the DOM nodes representing these roots are the "rootContainer". | |
| 9223 // To figure out which ancestor instance we should use, we traverse | |
| 9224 // up the fiber tree from the target instance and attempt to find | |
| 9225 // root boundaries that match that of our current "rootContainer". | |
| 9226 // If we find that "rootContainer", we find the parent fiber | |
| 9227 // sub-tree for that root and make that our ancestor instance. | |
| 9228 var node = targetInst; | |
| 9229 | |
| 9230 mainLoop: while (true) { | |
| 9231 if (node === null) { | |
| 9232 return; | |
| 9233 } | |
| 9234 | |
| 9235 var nodeTag = node.tag; | |
| 9236 | |
| 9237 if (nodeTag === HostRoot || nodeTag === HostPortal) { | |
| 9238 var container = node.stateNode.containerInfo; | |
| 9239 | |
| 9240 if (isMatchingRootContainer(container, targetContainerNode)) { | |
| 9241 break; | |
| 9242 } | |
| 9243 | |
| 9244 if (nodeTag === HostPortal) { | |
| 9245 // The target is a portal, but it's not the rootContainer we're looking for. | |
| 9246 // Normally portals handle their own events all the way down to the root. | |
| 9247 // So we should be able to stop now. However, we don't know if this portal | |
| 9248 // was part of *our* root. | |
| 9249 var grandNode = node.return; | |
| 9250 | |
| 9251 while (grandNode !== null) { | |
| 9252 var grandTag = grandNode.tag; | |
| 9253 | |
| 9254 if (grandTag === HostRoot || grandTag === HostPortal) { | |
| 9255 var grandContainer = grandNode.stateNode.containerInfo; | |
| 9256 | |
| 9257 if (isMatchingRootContainer(grandContainer, targetContainerNode)) { | |
| 9258 // This is the rootContainer we're looking for and we found it as | |
| 9259 // a parent of the Portal. That means we can ignore it because the | |
| 9260 // Portal will bubble through to us. | |
| 9261 return; | |
| 9262 } | |
| 9263 } | |
| 9264 | |
| 9265 grandNode = grandNode.return; | |
| 9266 } | |
| 9267 } // Now we need to find it's corresponding host fiber in the other | |
| 9268 // tree. To do this we can use getClosestInstanceFromNode, but we | |
| 9269 // need to validate that the fiber is a host instance, otherwise | |
| 9270 // we need to traverse up through the DOM till we find the correct | |
| 9271 // node that is from the other tree. | |
| 9272 | |
| 9273 | |
| 9274 while (container !== null) { | |
| 9275 var parentNode = getClosestInstanceFromNode(container); | |
| 9276 | |
| 9277 if (parentNode === null) { | |
| 9278 return; | |
| 9279 } | |
| 9280 | |
| 9281 var parentTag = parentNode.tag; | |
| 9282 | |
| 9283 if (parentTag === HostComponent || parentTag === HostText) { | |
| 9284 node = ancestorInst = parentNode; | |
| 9285 continue mainLoop; | |
| 9286 } | |
| 9287 | |
| 9288 container = container.parentNode; | |
| 9289 } | |
| 9290 } | |
| 9291 | |
| 9292 node = node.return; | |
| 9293 } | |
| 9294 } | |
| 9295 } | |
| 9296 | |
| 9297 batchedUpdates(function () { | |
| 9298 return dispatchEventsForPlugins(domEventName, eventSystemFlags, nativeEvent, ancestorInst); | |
| 9299 }); | |
| 9300 } | |
| 9301 | |
| 9302 function createDispatchListener(instance, listener, currentTarget) { | |
| 9303 return { | |
| 9304 instance: instance, | |
| 9305 listener: listener, | |
| 9306 currentTarget: currentTarget | |
| 9307 }; | |
| 9308 } | |
| 9309 | |
| 9310 function accumulateSinglePhaseListeners(targetFiber, reactName, nativeEventType, inCapturePhase, accumulateTargetOnly, nativeEvent) { | |
| 9311 var captureName = reactName !== null ? reactName + 'Capture' : null; | |
| 9312 var reactEventName = inCapturePhase ? captureName : reactName; | |
| 9313 var listeners = []; | |
| 9314 var instance = targetFiber; | |
| 9315 var lastHostComponent = null; // Accumulate all instances and listeners via the target -> root path. | |
| 9316 | |
| 9317 while (instance !== null) { | |
| 9318 var _instance2 = instance, | |
| 9319 stateNode = _instance2.stateNode, | |
| 9320 tag = _instance2.tag; // Handle listeners that are on HostComponents (i.e. <div>) | |
| 9321 | |
| 9322 if (tag === HostComponent && stateNode !== null) { | |
| 9323 lastHostComponent = stateNode; // createEventHandle listeners | |
| 9324 | |
| 9325 | |
| 9326 if (reactEventName !== null) { | |
| 9327 var listener = getListener(instance, reactEventName); | |
| 9328 | |
| 9329 if (listener != null) { | |
| 9330 listeners.push(createDispatchListener(instance, listener, lastHostComponent)); | |
| 9331 } | |
| 9332 } | |
| 9333 } // If we are only accumulating events for the target, then we don't | |
| 9334 // continue to propagate through the React fiber tree to find other | |
| 9335 // listeners. | |
| 9336 | |
| 9337 | |
| 9338 if (accumulateTargetOnly) { | |
| 9339 break; | |
| 9340 } // If we are processing the onBeforeBlur event, then we need to take | |
| 9341 | |
| 9342 instance = instance.return; | |
| 9343 } | |
| 9344 | |
| 9345 return listeners; | |
| 9346 } // We should only use this function for: | |
| 9347 // - BeforeInputEventPlugin | |
| 9348 // - ChangeEventPlugin | |
| 9349 // - SelectEventPlugin | |
| 9350 // This is because we only process these plugins | |
| 9351 // in the bubble phase, so we need to accumulate two | |
| 9352 // phase event listeners (via emulation). | |
| 9353 | |
| 9354 function accumulateTwoPhaseListeners(targetFiber, reactName) { | |
| 9355 var captureName = reactName + 'Capture'; | |
| 9356 var listeners = []; | |
| 9357 var instance = targetFiber; // Accumulate all instances and listeners via the target -> root path. | |
| 9358 | |
| 9359 while (instance !== null) { | |
| 9360 var _instance3 = instance, | |
| 9361 stateNode = _instance3.stateNode, | |
| 9362 tag = _instance3.tag; // Handle listeners that are on HostComponents (i.e. <div>) | |
| 9363 | |
| 9364 if (tag === HostComponent && stateNode !== null) { | |
| 9365 var currentTarget = stateNode; | |
| 9366 var captureListener = getListener(instance, captureName); | |
| 9367 | |
| 9368 if (captureListener != null) { | |
| 9369 listeners.unshift(createDispatchListener(instance, captureListener, currentTarget)); | |
| 9370 } | |
| 9371 | |
| 9372 var bubbleListener = getListener(instance, reactName); | |
| 9373 | |
| 9374 if (bubbleListener != null) { | |
| 9375 listeners.push(createDispatchListener(instance, bubbleListener, currentTarget)); | |
| 9376 } | |
| 9377 } | |
| 9378 | |
| 9379 instance = instance.return; | |
| 9380 } | |
| 9381 | |
| 9382 return listeners; | |
| 9383 } | |
| 9384 | |
| 9385 function getParent(inst) { | |
| 9386 if (inst === null) { | |
| 9387 return null; | |
| 9388 } | |
| 9389 | |
| 9390 do { | |
| 9391 inst = inst.return; // TODO: If this is a HostRoot we might want to bail out. | |
| 9392 // That is depending on if we want nested subtrees (layers) to bubble | |
| 9393 // events to their parent. We could also go through parentNode on the | |
| 9394 // host node but that wouldn't work for React Native and doesn't let us | |
| 9395 // do the portal feature. | |
| 9396 } while (inst && inst.tag !== HostComponent); | |
| 9397 | |
| 9398 if (inst) { | |
| 9399 return inst; | |
| 9400 } | |
| 9401 | |
| 9402 return null; | |
| 9403 } | |
| 9404 /** | |
| 9405 * Return the lowest common ancestor of A and B, or null if they are in | |
| 9406 * different trees. | |
| 9407 */ | |
| 9408 | |
| 9409 | |
| 9410 function getLowestCommonAncestor(instA, instB) { | |
| 9411 var nodeA = instA; | |
| 9412 var nodeB = instB; | |
| 9413 var depthA = 0; | |
| 9414 | |
| 9415 for (var tempA = nodeA; tempA; tempA = getParent(tempA)) { | |
| 9416 depthA++; | |
| 9417 } | |
| 9418 | |
| 9419 var depthB = 0; | |
| 9420 | |
| 9421 for (var tempB = nodeB; tempB; tempB = getParent(tempB)) { | |
| 9422 depthB++; | |
| 9423 } // If A is deeper, crawl up. | |
| 9424 | |
| 9425 | |
| 9426 while (depthA - depthB > 0) { | |
| 9427 nodeA = getParent(nodeA); | |
| 9428 depthA--; | |
| 9429 } // If B is deeper, crawl up. | |
| 9430 | |
| 9431 | |
| 9432 while (depthB - depthA > 0) { | |
| 9433 nodeB = getParent(nodeB); | |
| 9434 depthB--; | |
| 9435 } // Walk in lockstep until we find a match. | |
| 9436 | |
| 9437 | |
| 9438 var depth = depthA; | |
| 9439 | |
| 9440 while (depth--) { | |
| 9441 if (nodeA === nodeB || nodeB !== null && nodeA === nodeB.alternate) { | |
| 9442 return nodeA; | |
| 9443 } | |
| 9444 | |
| 9445 nodeA = getParent(nodeA); | |
| 9446 nodeB = getParent(nodeB); | |
| 9447 } | |
| 9448 | |
| 9449 return null; | |
| 9450 } | |
| 9451 | |
| 9452 function accumulateEnterLeaveListenersForEvent(dispatchQueue, event, target, common, inCapturePhase) { | |
| 9453 var registrationName = event._reactName; | |
| 9454 var listeners = []; | |
| 9455 var instance = target; | |
| 9456 | |
| 9457 while (instance !== null) { | |
| 9458 if (instance === common) { | |
| 9459 break; | |
| 9460 } | |
| 9461 | |
| 9462 var _instance4 = instance, | |
| 9463 alternate = _instance4.alternate, | |
| 9464 stateNode = _instance4.stateNode, | |
| 9465 tag = _instance4.tag; | |
| 9466 | |
| 9467 if (alternate !== null && alternate === common) { | |
| 9468 break; | |
| 9469 } | |
| 9470 | |
| 9471 if (tag === HostComponent && stateNode !== null) { | |
| 9472 var currentTarget = stateNode; | |
| 9473 | |
| 9474 if (inCapturePhase) { | |
| 9475 var captureListener = getListener(instance, registrationName); | |
| 9476 | |
| 9477 if (captureListener != null) { | |
| 9478 listeners.unshift(createDispatchListener(instance, captureListener, currentTarget)); | |
| 9479 } | |
| 9480 } else if (!inCapturePhase) { | |
| 9481 var bubbleListener = getListener(instance, registrationName); | |
| 9482 | |
| 9483 if (bubbleListener != null) { | |
| 9484 listeners.push(createDispatchListener(instance, bubbleListener, currentTarget)); | |
| 9485 } | |
| 9486 } | |
| 9487 } | |
| 9488 | |
| 9489 instance = instance.return; | |
| 9490 } | |
| 9491 | |
| 9492 if (listeners.length !== 0) { | |
| 9493 dispatchQueue.push({ | |
| 9494 event: event, | |
| 9495 listeners: listeners | |
| 9496 }); | |
| 9497 } | |
| 9498 } // We should only use this function for: | |
| 9499 // - EnterLeaveEventPlugin | |
| 9500 // This is because we only process this plugin | |
| 9501 // in the bubble phase, so we need to accumulate two | |
| 9502 // phase event listeners. | |
| 9503 | |
| 9504 | |
| 9505 function accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leaveEvent, enterEvent, from, to) { | |
| 9506 var common = from && to ? getLowestCommonAncestor(from, to) : null; | |
| 9507 | |
| 9508 if (from !== null) { | |
| 9509 accumulateEnterLeaveListenersForEvent(dispatchQueue, leaveEvent, from, common, false); | |
| 9510 } | |
| 9511 | |
| 9512 if (to !== null && enterEvent !== null) { | |
| 9513 accumulateEnterLeaveListenersForEvent(dispatchQueue, enterEvent, to, common, true); | |
| 9514 } | |
| 9515 } | |
| 9516 function getListenerSetKey(domEventName, capture) { | |
| 9517 return domEventName + "__" + (capture ? 'capture' : 'bubble'); | |
| 9518 } | |
| 9519 | |
| 9520 var didWarnInvalidHydration = false; | |
| 9521 var DANGEROUSLY_SET_INNER_HTML = 'dangerouslySetInnerHTML'; | |
| 9522 var SUPPRESS_CONTENT_EDITABLE_WARNING = 'suppressContentEditableWarning'; | |
| 9523 var SUPPRESS_HYDRATION_WARNING = 'suppressHydrationWarning'; | |
| 9524 var AUTOFOCUS = 'autoFocus'; | |
| 9525 var CHILDREN = 'children'; | |
| 9526 var STYLE = 'style'; | |
| 9527 var HTML$1 = '__html'; | |
| 9528 var warnedUnknownTags; | |
| 9529 var validatePropertiesInDevelopment; | |
| 9530 var warnForPropDifference; | |
| 9531 var warnForExtraAttributes; | |
| 9532 var warnForInvalidEventListener; | |
| 9533 var canDiffStyleForHydrationWarning; | |
| 9534 var normalizeHTML; | |
| 9535 | |
| 9536 { | |
| 9537 warnedUnknownTags = { | |
| 9538 // There are working polyfills for <dialog>. Let people use it. | |
| 9539 dialog: true, | |
| 9540 // Electron ships a custom <webview> tag to display external web content in | |
| 9541 // an isolated frame and process. | |
| 9542 // This tag is not present in non Electron environments such as JSDom which | |
| 9543 // is often used for testing purposes. | |
| 9544 // @see https://electronjs.org/docs/api/webview-tag | |
| 9545 webview: true | |
| 9546 }; | |
| 9547 | |
| 9548 validatePropertiesInDevelopment = function (type, props) { | |
| 9549 validateProperties(type, props); | |
| 9550 validateProperties$1(type, props); | |
| 9551 validateProperties$2(type, props, { | |
| 9552 registrationNameDependencies: registrationNameDependencies, | |
| 9553 possibleRegistrationNames: possibleRegistrationNames | |
| 9554 }); | |
| 9555 }; // IE 11 parses & normalizes the style attribute as opposed to other | |
| 9556 // browsers. It adds spaces and sorts the properties in some | |
| 9557 // non-alphabetical order. Handling that would require sorting CSS | |
| 9558 // properties in the client & server versions or applying | |
| 9559 // `expectedStyle` to a temporary DOM node to read its `style` attribute | |
| 9560 // normalized. Since it only affects IE, we're skipping style warnings | |
| 9561 // in that browser completely in favor of doing all that work. | |
| 9562 // See https://github.com/facebook/react/issues/11807 | |
| 9563 | |
| 9564 | |
| 9565 canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode; | |
| 9566 | |
| 9567 warnForPropDifference = function (propName, serverValue, clientValue) { | |
| 9568 if (didWarnInvalidHydration) { | |
| 9569 return; | |
| 9570 } | |
| 9571 | |
| 9572 var normalizedClientValue = normalizeMarkupForTextOrAttribute(clientValue); | |
| 9573 var normalizedServerValue = normalizeMarkupForTextOrAttribute(serverValue); | |
| 9574 | |
| 9575 if (normalizedServerValue === normalizedClientValue) { | |
| 9576 return; | |
| 9577 } | |
| 9578 | |
| 9579 didWarnInvalidHydration = true; | |
| 9580 | |
| 9581 error('Prop `%s` did not match. Server: %s Client: %s', propName, JSON.stringify(normalizedServerValue), JSON.stringify(normalizedClientValue)); | |
| 9582 }; | |
| 9583 | |
| 9584 warnForExtraAttributes = function (attributeNames) { | |
| 9585 if (didWarnInvalidHydration) { | |
| 9586 return; | |
| 9587 } | |
| 9588 | |
| 9589 didWarnInvalidHydration = true; | |
| 9590 var names = []; | |
| 9591 attributeNames.forEach(function (name) { | |
| 9592 names.push(name); | |
| 9593 }); | |
| 9594 | |
| 9595 error('Extra attributes from the server: %s', names); | |
| 9596 }; | |
| 9597 | |
| 9598 warnForInvalidEventListener = function (registrationName, listener) { | |
| 9599 if (listener === false) { | |
| 9600 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); | |
| 9601 } else { | |
| 9602 error('Expected `%s` listener to be a function, instead got a value of `%s` type.', registrationName, typeof listener); | |
| 9603 } | |
| 9604 }; // Parse the HTML and read it back to normalize the HTML string so that it | |
| 9605 // can be used for comparison. | |
| 9606 | |
| 9607 | |
| 9608 normalizeHTML = function (parent, html) { | |
| 9609 // We could have created a separate document here to avoid | |
| 9610 // re-initializing custom elements if they exist. But this breaks | |
| 9611 // how <noscript> is being handled. So we use the same document. | |
| 9612 // See the discussion in https://github.com/facebook/react/pull/11157. | |
| 9613 var testElement = parent.namespaceURI === HTML_NAMESPACE ? parent.ownerDocument.createElement(parent.tagName) : parent.ownerDocument.createElementNS(parent.namespaceURI, parent.tagName); | |
| 9614 testElement.innerHTML = html; | |
| 9615 return testElement.innerHTML; | |
| 9616 }; | |
| 9617 } // HTML parsing normalizes CR and CRLF to LF. | |
| 9618 // It also can turn \u0000 into \uFFFD inside attributes. | |
| 9619 // https://www.w3.org/TR/html5/single-page.html#preprocessing-the-input-stream | |
| 9620 // If we have a mismatch, it might be caused by that. | |
| 9621 // We will still patch up in this case but not fire the warning. | |
| 9622 | |
| 9623 | |
| 9624 var NORMALIZE_NEWLINES_REGEX = /\r\n?/g; | |
| 9625 var NORMALIZE_NULL_AND_REPLACEMENT_REGEX = /\u0000|\uFFFD/g; | |
| 9626 | |
| 9627 function normalizeMarkupForTextOrAttribute(markup) { | |
| 9628 { | |
| 9629 checkHtmlStringCoercion(markup); | |
| 9630 } | |
| 9631 | |
| 9632 var markupString = typeof markup === 'string' ? markup : '' + markup; | |
| 9633 return markupString.replace(NORMALIZE_NEWLINES_REGEX, '\n').replace(NORMALIZE_NULL_AND_REPLACEMENT_REGEX, ''); | |
| 9634 } | |
| 9635 | |
| 9636 function checkForUnmatchedText(serverText, clientText, isConcurrentMode, shouldWarnDev) { | |
| 9637 var normalizedClientText = normalizeMarkupForTextOrAttribute(clientText); | |
| 9638 var normalizedServerText = normalizeMarkupForTextOrAttribute(serverText); | |
| 9639 | |
| 9640 if (normalizedServerText === normalizedClientText) { | |
| 9641 return; | |
| 9642 } | |
| 9643 | |
| 9644 if (shouldWarnDev) { | |
| 9645 { | |
| 9646 if (!didWarnInvalidHydration) { | |
| 9647 didWarnInvalidHydration = true; | |
| 9648 | |
| 9649 error('Text content did not match. Server: "%s" Client: "%s"', normalizedServerText, normalizedClientText); | |
| 9650 } | |
| 9651 } | |
| 9652 } | |
| 9653 | |
| 9654 if (isConcurrentMode && enableClientRenderFallbackOnTextMismatch) { | |
| 9655 // In concurrent roots, we throw when there's a text mismatch and revert to | |
| 9656 // client rendering, up to the nearest Suspense boundary. | |
| 9657 throw new Error('Text content does not match server-rendered HTML.'); | |
| 9658 } | |
| 9659 } | |
| 9660 | |
| 9661 function getOwnerDocumentFromRootContainer(rootContainerElement) { | |
| 9662 return rootContainerElement.nodeType === DOCUMENT_NODE ? rootContainerElement : rootContainerElement.ownerDocument; | |
| 9663 } | |
| 9664 | |
| 9665 function noop() {} | |
| 9666 | |
| 9667 function trapClickOnNonInteractiveElement(node) { | |
| 9668 // Mobile Safari does not fire properly bubble click events on | |
| 9669 // non-interactive elements, which means delegated click listeners do not | |
| 9670 // fire. The workaround for this bug involves attaching an empty click | |
| 9671 // listener on the target node. | |
| 9672 // https://www.quirksmode.org/blog/archives/2010/09/click_event_del.html | |
| 9673 // Just set it using the onclick property so that we don't have to manage any | |
| 9674 // bookkeeping for it. Not sure if we need to clear it when the listener is | |
| 9675 // removed. | |
| 9676 // TODO: Only do this for the relevant Safaris maybe? | |
| 9677 node.onclick = noop; | |
| 9678 } | |
| 9679 | |
| 9680 function setInitialDOMProperties(tag, domElement, rootContainerElement, nextProps, isCustomComponentTag) { | |
| 9681 for (var propKey in nextProps) { | |
| 9682 if (!nextProps.hasOwnProperty(propKey)) { | |
| 9683 continue; | |
| 9684 } | |
| 9685 | |
| 9686 var nextProp = nextProps[propKey]; | |
| 9687 | |
| 9688 if (propKey === STYLE) { | |
| 9689 { | |
| 9690 if (nextProp) { | |
| 9691 // Freeze the next style object so that we can assume it won't be | |
| 9692 // mutated. We have already warned for this in the past. | |
| 9693 Object.freeze(nextProp); | |
| 9694 } | |
| 9695 } // Relies on `updateStylesByID` not mutating `styleUpdates`. | |
| 9696 | |
| 9697 | |
| 9698 setValueForStyles(domElement, nextProp); | |
| 9699 } else if (propKey === DANGEROUSLY_SET_INNER_HTML) { | |
| 9700 var nextHtml = nextProp ? nextProp[HTML$1] : undefined; | |
| 9701 | |
| 9702 if (nextHtml != null) { | |
| 9703 setInnerHTML(domElement, nextHtml); | |
| 9704 } | |
| 9705 } else if (propKey === CHILDREN) { | |
| 9706 if (typeof nextProp === 'string') { | |
| 9707 // Avoid setting initial textContent when the text is empty. In IE11 setting | |
| 9708 // textContent on a <textarea> will cause the placeholder to not | |
| 9709 // show within the <textarea> until it has been focused and blurred again. | |
| 9710 // https://github.com/facebook/react/issues/6731#issuecomment-254874553 | |
| 9711 var canSetTextContent = tag !== 'textarea' || nextProp !== ''; | |
| 9712 | |
| 9713 if (canSetTextContent) { | |
| 9714 setTextContent(domElement, nextProp); | |
| 9715 } | |
| 9716 } else if (typeof nextProp === 'number') { | |
| 9717 setTextContent(domElement, '' + nextProp); | |
| 9718 } | |
| 9719 } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING) ; else if (propKey === AUTOFOCUS) ; else if (registrationNameDependencies.hasOwnProperty(propKey)) { | |
| 9720 if (nextProp != null) { | |
| 9721 if ( typeof nextProp !== 'function') { | |
| 9722 warnForInvalidEventListener(propKey, nextProp); | |
| 9723 } | |
| 9724 | |
| 9725 if (propKey === 'onScroll') { | |
| 9726 listenToNonDelegatedEvent('scroll', domElement); | |
| 9727 } | |
| 9728 } | |
| 9729 } else if (nextProp != null) { | |
| 9730 setValueForProperty(domElement, propKey, nextProp, isCustomComponentTag); | |
| 9731 } | |
| 9732 } | |
| 9733 } | |
| 9734 | |
| 9735 function updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag) { | |
| 9736 // TODO: Handle wasCustomComponentTag | |
| 9737 for (var i = 0; i < updatePayload.length; i += 2) { | |
| 9738 var propKey = updatePayload[i]; | |
| 9739 var propValue = updatePayload[i + 1]; | |
| 9740 | |
| 9741 if (propKey === STYLE) { | |
| 9742 setValueForStyles(domElement, propValue); | |
| 9743 } else if (propKey === DANGEROUSLY_SET_INNER_HTML) { | |
| 9744 setInnerHTML(domElement, propValue); | |
| 9745 } else if (propKey === CHILDREN) { | |
| 9746 setTextContent(domElement, propValue); | |
| 9747 } else { | |
| 9748 setValueForProperty(domElement, propKey, propValue, isCustomComponentTag); | |
| 9749 } | |
| 9750 } | |
| 9751 } | |
| 9752 | |
| 9753 function createElement(type, props, rootContainerElement, parentNamespace) { | |
| 9754 var isCustomComponentTag; // We create tags in the namespace of their parent container, except HTML | |
| 9755 // tags get no namespace. | |
| 9756 | |
| 9757 var ownerDocument = getOwnerDocumentFromRootContainer(rootContainerElement); | |
| 9758 var domElement; | |
| 9759 var namespaceURI = parentNamespace; | |
| 9760 | |
| 9761 if (namespaceURI === HTML_NAMESPACE) { | |
| 9762 namespaceURI = getIntrinsicNamespace(type); | |
| 9763 } | |
| 9764 | |
| 9765 if (namespaceURI === HTML_NAMESPACE) { | |
| 9766 { | |
| 9767 isCustomComponentTag = isCustomComponent(type, props); // Should this check be gated by parent namespace? Not sure we want to | |
| 9768 // allow <SVG> or <mATH>. | |
| 9769 | |
| 9770 if (!isCustomComponentTag && type !== type.toLowerCase()) { | |
| 9771 error('<%s /> is using incorrect casing. ' + 'Use PascalCase for React components, ' + 'or lowercase for HTML elements.', type); | |
| 9772 } | |
| 9773 } | |
| 9774 | |
| 9775 if (type === 'script') { | |
| 9776 // Create the script via .innerHTML so its "parser-inserted" flag is | |
| 9777 // set to true and it does not execute | |
| 9778 var div = ownerDocument.createElement('div'); | |
| 9779 | |
| 9780 div.innerHTML = '<script><' + '/script>'; // eslint-disable-line | |
| 9781 // This is guaranteed to yield a script element. | |
| 9782 | |
| 9783 var firstChild = div.firstChild; | |
| 9784 domElement = div.removeChild(firstChild); | |
| 9785 } else if (typeof props.is === 'string') { | |
| 9786 // $FlowIssue `createElement` should be updated for Web Components | |
| 9787 domElement = ownerDocument.createElement(type, { | |
| 9788 is: props.is | |
| 9789 }); | |
| 9790 } else { | |
| 9791 // Separate else branch instead of using `props.is || undefined` above because of a Firefox bug. | |
| 9792 // See discussion in https://github.com/facebook/react/pull/6896 | |
| 9793 // and discussion in https://bugzilla.mozilla.org/show_bug.cgi?id=1276240 | |
| 9794 domElement = ownerDocument.createElement(type); // Normally attributes are assigned in `setInitialDOMProperties`, however the `multiple` and `size` | |
| 9795 // attributes on `select`s needs to be added before `option`s are inserted. | |
| 9796 // This prevents: | |
| 9797 // - a bug where the `select` does not scroll to the correct option because singular | |
| 9798 // `select` elements automatically pick the first item #13222 | |
| 9799 // - a bug where the `select` set the first item as selected despite the `size` attribute #14239 | |
| 9800 // See https://github.com/facebook/react/issues/13222 | |
| 9801 // and https://github.com/facebook/react/issues/14239 | |
| 9802 | |
| 9803 if (type === 'select') { | |
| 9804 var node = domElement; | |
| 9805 | |
| 9806 if (props.multiple) { | |
| 9807 node.multiple = true; | |
| 9808 } else if (props.size) { | |
| 9809 // Setting a size greater than 1 causes a select to behave like `multiple=true`, where | |
| 9810 // it is possible that no option is selected. | |
| 9811 // | |
| 9812 // This is only necessary when a select in "single selection mode". | |
| 9813 node.size = props.size; | |
| 9814 } | |
| 9815 } | |
| 9816 } | |
| 9817 } else { | |
| 9818 domElement = ownerDocument.createElementNS(namespaceURI, type); | |
| 9819 } | |
| 9820 | |
| 9821 { | |
| 9822 if (namespaceURI === HTML_NAMESPACE) { | |
| 9823 if (!isCustomComponentTag && Object.prototype.toString.call(domElement) === '[object HTMLUnknownElement]' && !hasOwnProperty.call(warnedUnknownTags, type)) { | |
| 9824 warnedUnknownTags[type] = true; | |
| 9825 | |
| 9826 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); | |
| 9827 } | |
| 9828 } | |
| 9829 } | |
| 9830 | |
| 9831 return domElement; | |
| 9832 } | |
| 9833 function createTextNode(text, rootContainerElement) { | |
| 9834 return getOwnerDocumentFromRootContainer(rootContainerElement).createTextNode(text); | |
| 9835 } | |
| 9836 function setInitialProperties(domElement, tag, rawProps, rootContainerElement) { | |
| 9837 var isCustomComponentTag = isCustomComponent(tag, rawProps); | |
| 9838 | |
| 9839 { | |
| 9840 validatePropertiesInDevelopment(tag, rawProps); | |
| 9841 } // TODO: Make sure that we check isMounted before firing any of these events. | |
| 9842 | |
| 9843 | |
| 9844 var props; | |
| 9845 | |
| 9846 switch (tag) { | |
| 9847 case 'dialog': | |
| 9848 listenToNonDelegatedEvent('cancel', domElement); | |
| 9849 listenToNonDelegatedEvent('close', domElement); | |
| 9850 props = rawProps; | |
| 9851 break; | |
| 9852 | |
| 9853 case 'iframe': | |
| 9854 case 'object': | |
| 9855 case 'embed': | |
| 9856 // We listen to this event in case to ensure emulated bubble | |
| 9857 // listeners still fire for the load event. | |
| 9858 listenToNonDelegatedEvent('load', domElement); | |
| 9859 props = rawProps; | |
| 9860 break; | |
| 9861 | |
| 9862 case 'video': | |
| 9863 case 'audio': | |
| 9864 // We listen to these events in case to ensure emulated bubble | |
| 9865 // listeners still fire for all the media events. | |
| 9866 for (var i = 0; i < mediaEventTypes.length; i++) { | |
| 9867 listenToNonDelegatedEvent(mediaEventTypes[i], domElement); | |
| 9868 } | |
| 9869 | |
| 9870 props = rawProps; | |
| 9871 break; | |
| 9872 | |
| 9873 case 'source': | |
| 9874 // We listen to this event in case to ensure emulated bubble | |
| 9875 // listeners still fire for the error event. | |
| 9876 listenToNonDelegatedEvent('error', domElement); | |
| 9877 props = rawProps; | |
| 9878 break; | |
| 9879 | |
| 9880 case 'img': | |
| 9881 case 'image': | |
| 9882 case 'link': | |
| 9883 // We listen to these events in case to ensure emulated bubble | |
| 9884 // listeners still fire for error and load events. | |
| 9885 listenToNonDelegatedEvent('error', domElement); | |
| 9886 listenToNonDelegatedEvent('load', domElement); | |
| 9887 props = rawProps; | |
| 9888 break; | |
| 9889 | |
| 9890 case 'details': | |
| 9891 // We listen to this event in case to ensure emulated bubble | |
| 9892 // listeners still fire for the toggle event. | |
| 9893 listenToNonDelegatedEvent('toggle', domElement); | |
| 9894 props = rawProps; | |
| 9895 break; | |
| 9896 | |
| 9897 case 'input': | |
| 9898 initWrapperState(domElement, rawProps); | |
| 9899 props = getHostProps(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 9900 // listeners still fire for the invalid event. | |
| 9901 | |
| 9902 listenToNonDelegatedEvent('invalid', domElement); | |
| 9903 break; | |
| 9904 | |
| 9905 case 'option': | |
| 9906 validateProps(domElement, rawProps); | |
| 9907 props = rawProps; | |
| 9908 break; | |
| 9909 | |
| 9910 case 'select': | |
| 9911 initWrapperState$1(domElement, rawProps); | |
| 9912 props = getHostProps$1(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 9913 // listeners still fire for the invalid event. | |
| 9914 | |
| 9915 listenToNonDelegatedEvent('invalid', domElement); | |
| 9916 break; | |
| 9917 | |
| 9918 case 'textarea': | |
| 9919 initWrapperState$2(domElement, rawProps); | |
| 9920 props = getHostProps$2(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 9921 // listeners still fire for the invalid event. | |
| 9922 | |
| 9923 listenToNonDelegatedEvent('invalid', domElement); | |
| 9924 break; | |
| 9925 | |
| 9926 default: | |
| 9927 props = rawProps; | |
| 9928 } | |
| 9929 | |
| 9930 assertValidProps(tag, props); | |
| 9931 setInitialDOMProperties(tag, domElement, rootContainerElement, props, isCustomComponentTag); | |
| 9932 | |
| 9933 switch (tag) { | |
| 9934 case 'input': | |
| 9935 // TODO: Make sure we check if this is still unmounted or do any clean | |
| 9936 // up necessary since we never stop tracking anymore. | |
| 9937 track(domElement); | |
| 9938 postMountWrapper(domElement, rawProps, false); | |
| 9939 break; | |
| 9940 | |
| 9941 case 'textarea': | |
| 9942 // TODO: Make sure we check if this is still unmounted or do any clean | |
| 9943 // up necessary since we never stop tracking anymore. | |
| 9944 track(domElement); | |
| 9945 postMountWrapper$3(domElement); | |
| 9946 break; | |
| 9947 | |
| 9948 case 'option': | |
| 9949 postMountWrapper$1(domElement, rawProps); | |
| 9950 break; | |
| 9951 | |
| 9952 case 'select': | |
| 9953 postMountWrapper$2(domElement, rawProps); | |
| 9954 break; | |
| 9955 | |
| 9956 default: | |
| 9957 if (typeof props.onClick === 'function') { | |
| 9958 // TODO: This cast may not be sound for SVG, MathML or custom elements. | |
| 9959 trapClickOnNonInteractiveElement(domElement); | |
| 9960 } | |
| 9961 | |
| 9962 break; | |
| 9963 } | |
| 9964 } // Calculate the diff between the two objects. | |
| 9965 | |
| 9966 function diffProperties(domElement, tag, lastRawProps, nextRawProps, rootContainerElement) { | |
| 9967 { | |
| 9968 validatePropertiesInDevelopment(tag, nextRawProps); | |
| 9969 } | |
| 9970 | |
| 9971 var updatePayload = null; | |
| 9972 var lastProps; | |
| 9973 var nextProps; | |
| 9974 | |
| 9975 switch (tag) { | |
| 9976 case 'input': | |
| 9977 lastProps = getHostProps(domElement, lastRawProps); | |
| 9978 nextProps = getHostProps(domElement, nextRawProps); | |
| 9979 updatePayload = []; | |
| 9980 break; | |
| 9981 | |
| 9982 case 'select': | |
| 9983 lastProps = getHostProps$1(domElement, lastRawProps); | |
| 9984 nextProps = getHostProps$1(domElement, nextRawProps); | |
| 9985 updatePayload = []; | |
| 9986 break; | |
| 9987 | |
| 9988 case 'textarea': | |
| 9989 lastProps = getHostProps$2(domElement, lastRawProps); | |
| 9990 nextProps = getHostProps$2(domElement, nextRawProps); | |
| 9991 updatePayload = []; | |
| 9992 break; | |
| 9993 | |
| 9994 default: | |
| 9995 lastProps = lastRawProps; | |
| 9996 nextProps = nextRawProps; | |
| 9997 | |
| 9998 if (typeof lastProps.onClick !== 'function' && typeof nextProps.onClick === 'function') { | |
| 9999 // TODO: This cast may not be sound for SVG, MathML or custom elements. | |
| 10000 trapClickOnNonInteractiveElement(domElement); | |
| 10001 } | |
| 10002 | |
| 10003 break; | |
| 10004 } | |
| 10005 | |
| 10006 assertValidProps(tag, nextProps); | |
| 10007 var propKey; | |
| 10008 var styleName; | |
| 10009 var styleUpdates = null; | |
| 10010 | |
| 10011 for (propKey in lastProps) { | |
| 10012 if (nextProps.hasOwnProperty(propKey) || !lastProps.hasOwnProperty(propKey) || lastProps[propKey] == null) { | |
| 10013 continue; | |
| 10014 } | |
| 10015 | |
| 10016 if (propKey === STYLE) { | |
| 10017 var lastStyle = lastProps[propKey]; | |
| 10018 | |
| 10019 for (styleName in lastStyle) { | |
| 10020 if (lastStyle.hasOwnProperty(styleName)) { | |
| 10021 if (!styleUpdates) { | |
| 10022 styleUpdates = {}; | |
| 10023 } | |
| 10024 | |
| 10025 styleUpdates[styleName] = ''; | |
| 10026 } | |
| 10027 } | |
| 10028 } 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)) { | |
| 10029 // This is a special case. If any listener updates we need to ensure | |
| 10030 // that the "current" fiber pointer gets updated so we need a commit | |
| 10031 // to update this element. | |
| 10032 if (!updatePayload) { | |
| 10033 updatePayload = []; | |
| 10034 } | |
| 10035 } else { | |
| 10036 // For all other deleted properties we add it to the queue. We use | |
| 10037 // the allowed property list in the commit phase instead. | |
| 10038 (updatePayload = updatePayload || []).push(propKey, null); | |
| 10039 } | |
| 10040 } | |
| 10041 | |
| 10042 for (propKey in nextProps) { | |
| 10043 var nextProp = nextProps[propKey]; | |
| 10044 var lastProp = lastProps != null ? lastProps[propKey] : undefined; | |
| 10045 | |
| 10046 if (!nextProps.hasOwnProperty(propKey) || nextProp === lastProp || nextProp == null && lastProp == null) { | |
| 10047 continue; | |
| 10048 } | |
| 10049 | |
| 10050 if (propKey === STYLE) { | |
| 10051 { | |
| 10052 if (nextProp) { | |
| 10053 // Freeze the next style object so that we can assume it won't be | |
| 10054 // mutated. We have already warned for this in the past. | |
| 10055 Object.freeze(nextProp); | |
| 10056 } | |
| 10057 } | |
| 10058 | |
| 10059 if (lastProp) { | |
| 10060 // Unset styles on `lastProp` but not on `nextProp`. | |
| 10061 for (styleName in lastProp) { | |
| 10062 if (lastProp.hasOwnProperty(styleName) && (!nextProp || !nextProp.hasOwnProperty(styleName))) { | |
| 10063 if (!styleUpdates) { | |
| 10064 styleUpdates = {}; | |
| 10065 } | |
| 10066 | |
| 10067 styleUpdates[styleName] = ''; | |
| 10068 } | |
| 10069 } // Update styles that changed since `lastProp`. | |
| 10070 | |
| 10071 | |
| 10072 for (styleName in nextProp) { | |
| 10073 if (nextProp.hasOwnProperty(styleName) && lastProp[styleName] !== nextProp[styleName]) { | |
| 10074 if (!styleUpdates) { | |
| 10075 styleUpdates = {}; | |
| 10076 } | |
| 10077 | |
| 10078 styleUpdates[styleName] = nextProp[styleName]; | |
| 10079 } | |
| 10080 } | |
| 10081 } else { | |
| 10082 // Relies on `updateStylesByID` not mutating `styleUpdates`. | |
| 10083 if (!styleUpdates) { | |
| 10084 if (!updatePayload) { | |
| 10085 updatePayload = []; | |
| 10086 } | |
| 10087 | |
| 10088 updatePayload.push(propKey, styleUpdates); | |
| 10089 } | |
| 10090 | |
| 10091 styleUpdates = nextProp; | |
| 10092 } | |
| 10093 } else if (propKey === DANGEROUSLY_SET_INNER_HTML) { | |
| 10094 var nextHtml = nextProp ? nextProp[HTML$1] : undefined; | |
| 10095 var lastHtml = lastProp ? lastProp[HTML$1] : undefined; | |
| 10096 | |
| 10097 if (nextHtml != null) { | |
| 10098 if (lastHtml !== nextHtml) { | |
| 10099 (updatePayload = updatePayload || []).push(propKey, nextHtml); | |
| 10100 } | |
| 10101 } | |
| 10102 } else if (propKey === CHILDREN) { | |
| 10103 if (typeof nextProp === 'string' || typeof nextProp === 'number') { | |
| 10104 (updatePayload = updatePayload || []).push(propKey, '' + nextProp); | |
| 10105 } | |
| 10106 } else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING) ; else if (registrationNameDependencies.hasOwnProperty(propKey)) { | |
| 10107 if (nextProp != null) { | |
| 10108 // We eagerly listen to this even though we haven't committed yet. | |
| 10109 if ( typeof nextProp !== 'function') { | |
| 10110 warnForInvalidEventListener(propKey, nextProp); | |
| 10111 } | |
| 10112 | |
| 10113 if (propKey === 'onScroll') { | |
| 10114 listenToNonDelegatedEvent('scroll', domElement); | |
| 10115 } | |
| 10116 } | |
| 10117 | |
| 10118 if (!updatePayload && lastProp !== nextProp) { | |
| 10119 // This is a special case. If any listener updates we need to ensure | |
| 10120 // that the "current" props pointer gets updated so we need a commit | |
| 10121 // to update this element. | |
| 10122 updatePayload = []; | |
| 10123 } | |
| 10124 } else { | |
| 10125 // For any other property we always add it to the queue and then we | |
| 10126 // filter it out using the allowed property list during the commit. | |
| 10127 (updatePayload = updatePayload || []).push(propKey, nextProp); | |
| 10128 } | |
| 10129 } | |
| 10130 | |
| 10131 if (styleUpdates) { | |
| 10132 { | |
| 10133 validateShorthandPropertyCollisionInDev(styleUpdates, nextProps[STYLE]); | |
| 10134 } | |
| 10135 | |
| 10136 (updatePayload = updatePayload || []).push(STYLE, styleUpdates); | |
| 10137 } | |
| 10138 | |
| 10139 return updatePayload; | |
| 10140 } // Apply the diff. | |
| 10141 | |
| 10142 function updateProperties(domElement, updatePayload, tag, lastRawProps, nextRawProps) { | |
| 10143 // Update checked *before* name. | |
| 10144 // In the middle of an update, it is possible to have multiple checked. | |
| 10145 // When a checked radio tries to change name, browser makes another radio's checked false. | |
| 10146 if (tag === 'input' && nextRawProps.type === 'radio' && nextRawProps.name != null) { | |
| 10147 updateChecked(domElement, nextRawProps); | |
| 10148 } | |
| 10149 | |
| 10150 var wasCustomComponentTag = isCustomComponent(tag, lastRawProps); | |
| 10151 var isCustomComponentTag = isCustomComponent(tag, nextRawProps); // Apply the diff. | |
| 10152 | |
| 10153 updateDOMProperties(domElement, updatePayload, wasCustomComponentTag, isCustomComponentTag); // TODO: Ensure that an update gets scheduled if any of the special props | |
| 10154 // changed. | |
| 10155 | |
| 10156 switch (tag) { | |
| 10157 case 'input': | |
| 10158 // Update the wrapper around inputs *after* updating props. This has to | |
| 10159 // happen after `updateDOMProperties`. Otherwise HTML5 input validations | |
| 10160 // raise warnings and prevent the new value from being assigned. | |
| 10161 updateWrapper(domElement, nextRawProps); | |
| 10162 break; | |
| 10163 | |
| 10164 case 'textarea': | |
| 10165 updateWrapper$1(domElement, nextRawProps); | |
| 10166 break; | |
| 10167 | |
| 10168 case 'select': | |
| 10169 // <select> value update needs to occur after <option> children | |
| 10170 // reconciliation | |
| 10171 postUpdateWrapper(domElement, nextRawProps); | |
| 10172 break; | |
| 10173 } | |
| 10174 } | |
| 10175 | |
| 10176 function getPossibleStandardName(propName) { | |
| 10177 { | |
| 10178 var lowerCasedName = propName.toLowerCase(); | |
| 10179 | |
| 10180 if (!possibleStandardNames.hasOwnProperty(lowerCasedName)) { | |
| 10181 return null; | |
| 10182 } | |
| 10183 | |
| 10184 return possibleStandardNames[lowerCasedName] || null; | |
| 10185 } | |
| 10186 } | |
| 10187 | |
| 10188 function diffHydratedProperties(domElement, tag, rawProps, parentNamespace, rootContainerElement, isConcurrentMode, shouldWarnDev) { | |
| 10189 var isCustomComponentTag; | |
| 10190 var extraAttributeNames; | |
| 10191 | |
| 10192 { | |
| 10193 isCustomComponentTag = isCustomComponent(tag, rawProps); | |
| 10194 validatePropertiesInDevelopment(tag, rawProps); | |
| 10195 } // TODO: Make sure that we check isMounted before firing any of these events. | |
| 10196 | |
| 10197 | |
| 10198 switch (tag) { | |
| 10199 case 'dialog': | |
| 10200 listenToNonDelegatedEvent('cancel', domElement); | |
| 10201 listenToNonDelegatedEvent('close', domElement); | |
| 10202 break; | |
| 10203 | |
| 10204 case 'iframe': | |
| 10205 case 'object': | |
| 10206 case 'embed': | |
| 10207 // We listen to this event in case to ensure emulated bubble | |
| 10208 // listeners still fire for the load event. | |
| 10209 listenToNonDelegatedEvent('load', domElement); | |
| 10210 break; | |
| 10211 | |
| 10212 case 'video': | |
| 10213 case 'audio': | |
| 10214 // We listen to these events in case to ensure emulated bubble | |
| 10215 // listeners still fire for all the media events. | |
| 10216 for (var i = 0; i < mediaEventTypes.length; i++) { | |
| 10217 listenToNonDelegatedEvent(mediaEventTypes[i], domElement); | |
| 10218 } | |
| 10219 | |
| 10220 break; | |
| 10221 | |
| 10222 case 'source': | |
| 10223 // We listen to this event in case to ensure emulated bubble | |
| 10224 // listeners still fire for the error event. | |
| 10225 listenToNonDelegatedEvent('error', domElement); | |
| 10226 break; | |
| 10227 | |
| 10228 case 'img': | |
| 10229 case 'image': | |
| 10230 case 'link': | |
| 10231 // We listen to these events in case to ensure emulated bubble | |
| 10232 // listeners still fire for error and load events. | |
| 10233 listenToNonDelegatedEvent('error', domElement); | |
| 10234 listenToNonDelegatedEvent('load', domElement); | |
| 10235 break; | |
| 10236 | |
| 10237 case 'details': | |
| 10238 // We listen to this event in case to ensure emulated bubble | |
| 10239 // listeners still fire for the toggle event. | |
| 10240 listenToNonDelegatedEvent('toggle', domElement); | |
| 10241 break; | |
| 10242 | |
| 10243 case 'input': | |
| 10244 initWrapperState(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 10245 // listeners still fire for the invalid event. | |
| 10246 | |
| 10247 listenToNonDelegatedEvent('invalid', domElement); | |
| 10248 break; | |
| 10249 | |
| 10250 case 'option': | |
| 10251 validateProps(domElement, rawProps); | |
| 10252 break; | |
| 10253 | |
| 10254 case 'select': | |
| 10255 initWrapperState$1(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 10256 // listeners still fire for the invalid event. | |
| 10257 | |
| 10258 listenToNonDelegatedEvent('invalid', domElement); | |
| 10259 break; | |
| 10260 | |
| 10261 case 'textarea': | |
| 10262 initWrapperState$2(domElement, rawProps); // We listen to this event in case to ensure emulated bubble | |
| 10263 // listeners still fire for the invalid event. | |
| 10264 | |
| 10265 listenToNonDelegatedEvent('invalid', domElement); | |
| 10266 break; | |
| 10267 } | |
| 10268 | |
| 10269 assertValidProps(tag, rawProps); | |
| 10270 | |
| 10271 { | |
| 10272 extraAttributeNames = new Set(); | |
| 10273 var attributes = domElement.attributes; | |
| 10274 | |
| 10275 for (var _i = 0; _i < attributes.length; _i++) { | |
| 10276 var name = attributes[_i].name.toLowerCase(); | |
| 10277 | |
| 10278 switch (name) { | |
| 10279 // Controlled attributes are not validated | |
| 10280 // TODO: Only ignore them on controlled tags. | |
| 10281 case 'value': | |
| 10282 break; | |
| 10283 | |
| 10284 case 'checked': | |
| 10285 break; | |
| 10286 | |
| 10287 case 'selected': | |
| 10288 break; | |
| 10289 | |
| 10290 default: | |
| 10291 // Intentionally use the original name. | |
| 10292 // See discussion in https://github.com/facebook/react/pull/10676. | |
| 10293 extraAttributeNames.add(attributes[_i].name); | |
| 10294 } | |
| 10295 } | |
| 10296 } | |
| 10297 | |
| 10298 var updatePayload = null; | |
| 10299 | |
| 10300 for (var propKey in rawProps) { | |
| 10301 if (!rawProps.hasOwnProperty(propKey)) { | |
| 10302 continue; | |
| 10303 } | |
| 10304 | |
| 10305 var nextProp = rawProps[propKey]; | |
| 10306 | |
| 10307 if (propKey === CHILDREN) { | |
| 10308 // For text content children we compare against textContent. This | |
| 10309 // might match additional HTML that is hidden when we read it using | |
| 10310 // textContent. E.g. "foo" will match "f<span>oo</span>" but that still | |
| 10311 // satisfies our requirement. Our requirement is not to produce perfect | |
| 10312 // HTML and attributes. Ideally we should preserve structure but it's | |
| 10313 // ok not to if the visible content is still enough to indicate what | |
| 10314 // even listeners these nodes might be wired up to. | |
| 10315 // TODO: Warn if there is more than a single textNode as a child. | |
| 10316 // TODO: Should we use domElement.firstChild.nodeValue to compare? | |
| 10317 if (typeof nextProp === 'string') { | |
| 10318 if (domElement.textContent !== nextProp) { | |
| 10319 if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) { | |
| 10320 checkForUnmatchedText(domElement.textContent, nextProp, isConcurrentMode, shouldWarnDev); | |
| 10321 } | |
| 10322 | |
| 10323 updatePayload = [CHILDREN, nextProp]; | |
| 10324 } | |
| 10325 } else if (typeof nextProp === 'number') { | |
| 10326 if (domElement.textContent !== '' + nextProp) { | |
| 10327 if (rawProps[SUPPRESS_HYDRATION_WARNING] !== true) { | |
| 10328 checkForUnmatchedText(domElement.textContent, nextProp, isConcurrentMode, shouldWarnDev); | |
| 10329 } | |
| 10330 | |
| 10331 updatePayload = [CHILDREN, '' + nextProp]; | |
| 10332 } | |
| 10333 } | |
| 10334 } else if (registrationNameDependencies.hasOwnProperty(propKey)) { | |
| 10335 if (nextProp != null) { | |
| 10336 if ( typeof nextProp !== 'function') { | |
| 10337 warnForInvalidEventListener(propKey, nextProp); | |
| 10338 } | |
| 10339 | |
| 10340 if (propKey === 'onScroll') { | |
| 10341 listenToNonDelegatedEvent('scroll', domElement); | |
| 10342 } | |
| 10343 } | |
| 10344 } else if (shouldWarnDev && true && // Convince Flow we've calculated it (it's DEV-only in this method.) | |
| 10345 typeof isCustomComponentTag === 'boolean') { | |
| 10346 // Validate that the properties correspond to their expected values. | |
| 10347 var serverValue = void 0; | |
| 10348 var propertyInfo = isCustomComponentTag && enableCustomElementPropertySupport ? null : getPropertyInfo(propKey); | |
| 10349 | |
| 10350 if (rawProps[SUPPRESS_HYDRATION_WARNING] === true) ; else if (propKey === SUPPRESS_CONTENT_EDITABLE_WARNING || propKey === SUPPRESS_HYDRATION_WARNING || // Controlled attributes are not validated | |
| 10351 // TODO: Only ignore them on controlled tags. | |
| 10352 propKey === 'value' || propKey === 'checked' || propKey === 'selected') ; else if (propKey === DANGEROUSLY_SET_INNER_HTML) { | |
| 10353 var serverHTML = domElement.innerHTML; | |
| 10354 var nextHtml = nextProp ? nextProp[HTML$1] : undefined; | |
| 10355 | |
| 10356 if (nextHtml != null) { | |
| 10357 var expectedHTML = normalizeHTML(domElement, nextHtml); | |
| 10358 | |
| 10359 if (expectedHTML !== serverHTML) { | |
| 10360 warnForPropDifference(propKey, serverHTML, expectedHTML); | |
| 10361 } | |
| 10362 } | |
| 10363 } else if (propKey === STYLE) { | |
| 10364 // $FlowFixMe - Should be inferred as not undefined. | |
| 10365 extraAttributeNames.delete(propKey); | |
| 10366 | |
| 10367 if (canDiffStyleForHydrationWarning) { | |
| 10368 var expectedStyle = createDangerousStringForStyles(nextProp); | |
| 10369 serverValue = domElement.getAttribute('style'); | |
| 10370 | |
| 10371 if (expectedStyle !== serverValue) { | |
| 10372 warnForPropDifference(propKey, serverValue, expectedStyle); | |
| 10373 } | |
| 10374 } | |
| 10375 } else if (isCustomComponentTag && !enableCustomElementPropertySupport) { | |
| 10376 // $FlowFixMe - Should be inferred as not undefined. | |
| 10377 extraAttributeNames.delete(propKey.toLowerCase()); | |
| 10378 serverValue = getValueForAttribute(domElement, propKey, nextProp); | |
| 10379 | |
| 10380 if (nextProp !== serverValue) { | |
| 10381 warnForPropDifference(propKey, serverValue, nextProp); | |
| 10382 } | |
| 10383 } else if (!shouldIgnoreAttribute(propKey, propertyInfo, isCustomComponentTag) && !shouldRemoveAttribute(propKey, nextProp, propertyInfo, isCustomComponentTag)) { | |
| 10384 var isMismatchDueToBadCasing = false; | |
| 10385 | |
| 10386 if (propertyInfo !== null) { | |
| 10387 // $FlowFixMe - Should be inferred as not undefined. | |
| 10388 extraAttributeNames.delete(propertyInfo.attributeName); | |
| 10389 serverValue = getValueForProperty(domElement, propKey, nextProp, propertyInfo); | |
| 10390 } else { | |
| 10391 var ownNamespace = parentNamespace; | |
| 10392 | |
| 10393 if (ownNamespace === HTML_NAMESPACE) { | |
| 10394 ownNamespace = getIntrinsicNamespace(tag); | |
| 10395 } | |
| 10396 | |
| 10397 if (ownNamespace === HTML_NAMESPACE) { | |
| 10398 // $FlowFixMe - Should be inferred as not undefined. | |
| 10399 extraAttributeNames.delete(propKey.toLowerCase()); | |
| 10400 } else { | |
| 10401 var standardName = getPossibleStandardName(propKey); | |
| 10402 | |
| 10403 if (standardName !== null && standardName !== propKey) { | |
| 10404 // If an SVG prop is supplied with bad casing, it will | |
| 10405 // be successfully parsed from HTML, but will produce a mismatch | |
| 10406 // (and would be incorrectly rendered on the client). | |
| 10407 // However, we already warn about bad casing elsewhere. | |
| 10408 // So we'll skip the misleading extra mismatch warning in this case. | |
| 10409 isMismatchDueToBadCasing = true; // $FlowFixMe - Should be inferred as not undefined. | |
| 10410 | |
| 10411 extraAttributeNames.delete(standardName); | |
| 10412 } // $FlowFixMe - Should be inferred as not undefined. | |
| 10413 | |
| 10414 | |
| 10415 extraAttributeNames.delete(propKey); | |
| 10416 } | |
| 10417 | |
| 10418 serverValue = getValueForAttribute(domElement, propKey, nextProp); | |
| 10419 } | |
| 10420 | |
| 10421 var dontWarnCustomElement = enableCustomElementPropertySupport ; | |
| 10422 | |
| 10423 if (!dontWarnCustomElement && nextProp !== serverValue && !isMismatchDueToBadCasing) { | |
| 10424 warnForPropDifference(propKey, serverValue, nextProp); | |
| 10425 } | |
| 10426 } | |
| 10427 } | |
| 10428 } | |
| 10429 | |
| 10430 { | |
| 10431 if (shouldWarnDev) { | |
| 10432 if ( // $FlowFixMe - Should be inferred as not undefined. | |
| 10433 extraAttributeNames.size > 0 && rawProps[SUPPRESS_HYDRATION_WARNING] !== true) { | |
| 10434 // $FlowFixMe - Should be inferred as not undefined. | |
| 10435 warnForExtraAttributes(extraAttributeNames); | |
| 10436 } | |
| 10437 } | |
| 10438 } | |
| 10439 | |
| 10440 switch (tag) { | |
| 10441 case 'input': | |
| 10442 // TODO: Make sure we check if this is still unmounted or do any clean | |
| 10443 // up necessary since we never stop tracking anymore. | |
| 10444 track(domElement); | |
| 10445 postMountWrapper(domElement, rawProps, true); | |
| 10446 break; | |
| 10447 | |
| 10448 case 'textarea': | |
| 10449 // TODO: Make sure we check if this is still unmounted or do any clean | |
| 10450 // up necessary since we never stop tracking anymore. | |
| 10451 track(domElement); | |
| 10452 postMountWrapper$3(domElement); | |
| 10453 break; | |
| 10454 | |
| 10455 case 'select': | |
| 10456 case 'option': | |
| 10457 // For input and textarea we current always set the value property at | |
| 10458 // post mount to force it to diverge from attributes. However, for | |
| 10459 // option and select we don't quite do the same thing and select | |
| 10460 // is not resilient to the DOM state changing so we don't do that here. | |
| 10461 // TODO: Consider not doing this for input and textarea. | |
| 10462 break; | |
| 10463 | |
| 10464 default: | |
| 10465 if (typeof rawProps.onClick === 'function') { | |
| 10466 // TODO: This cast may not be sound for SVG, MathML or custom elements. | |
| 10467 trapClickOnNonInteractiveElement(domElement); | |
| 10468 } | |
| 10469 | |
| 10470 break; | |
| 10471 } | |
| 10472 | |
| 10473 return updatePayload; | |
| 10474 } | |
| 10475 function diffHydratedText(textNode, text, isConcurrentMode) { | |
| 10476 var isDifferent = textNode.nodeValue !== text; | |
| 10477 return isDifferent; | |
| 10478 } | |
| 10479 function warnForDeletedHydratableElement(parentNode, child) { | |
| 10480 { | |
| 10481 if (didWarnInvalidHydration) { | |
| 10482 return; | |
| 10483 } | |
| 10484 | |
| 10485 didWarnInvalidHydration = true; | |
| 10486 | |
| 10487 error('Did not expect server HTML to contain a <%s> in <%s>.', child.nodeName.toLowerCase(), parentNode.nodeName.toLowerCase()); | |
| 10488 } | |
| 10489 } | |
| 10490 function warnForDeletedHydratableText(parentNode, child) { | |
| 10491 { | |
| 10492 if (didWarnInvalidHydration) { | |
| 10493 return; | |
| 10494 } | |
| 10495 | |
| 10496 didWarnInvalidHydration = true; | |
| 10497 | |
| 10498 error('Did not expect server HTML to contain the text node "%s" in <%s>.', child.nodeValue, parentNode.nodeName.toLowerCase()); | |
| 10499 } | |
| 10500 } | |
| 10501 function warnForInsertedHydratedElement(parentNode, tag, props) { | |
| 10502 { | |
| 10503 if (didWarnInvalidHydration) { | |
| 10504 return; | |
| 10505 } | |
| 10506 | |
| 10507 didWarnInvalidHydration = true; | |
| 10508 | |
| 10509 error('Expected server HTML to contain a matching <%s> in <%s>.', tag, parentNode.nodeName.toLowerCase()); | |
| 10510 } | |
| 10511 } | |
| 10512 function warnForInsertedHydratedText(parentNode, text) { | |
| 10513 { | |
| 10514 if (text === '') { | |
| 10515 // We expect to insert empty text nodes since they're not represented in | |
| 10516 // the HTML. | |
| 10517 // TODO: Remove this special case if we can just avoid inserting empty | |
| 10518 // text nodes. | |
| 10519 return; | |
| 10520 } | |
| 10521 | |
| 10522 if (didWarnInvalidHydration) { | |
| 10523 return; | |
| 10524 } | |
| 10525 | |
| 10526 didWarnInvalidHydration = true; | |
| 10527 | |
| 10528 error('Expected server HTML to contain a matching text node for "%s" in <%s>.', text, parentNode.nodeName.toLowerCase()); | |
| 10529 } | |
| 10530 } | |
| 10531 function restoreControlledState$3(domElement, tag, props) { | |
| 10532 switch (tag) { | |
| 10533 case 'input': | |
| 10534 restoreControlledState(domElement, props); | |
| 10535 return; | |
| 10536 | |
| 10537 case 'textarea': | |
| 10538 restoreControlledState$2(domElement, props); | |
| 10539 return; | |
| 10540 | |
| 10541 case 'select': | |
| 10542 restoreControlledState$1(domElement, props); | |
| 10543 return; | |
| 10544 } | |
| 10545 } | |
| 10546 | |
| 10547 var validateDOMNesting = function () {}; | |
| 10548 | |
| 10549 var updatedAncestorInfo = function () {}; | |
| 10550 | |
| 10551 { | |
| 10552 // This validation code was written based on the HTML5 parsing spec: | |
| 10553 // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope | |
| 10554 // | |
| 10555 // Note: this does not catch all invalid nesting, nor does it try to (as it's | |
| 10556 // not clear what practical benefit doing so provides); instead, we warn only | |
| 10557 // for cases where the parser will give a parse tree differing from what React | |
| 10558 // intended. For example, <b><div></div></b> is invalid but we don't warn | |
| 10559 // because it still parses correctly; we do warn for other cases like nested | |
| 10560 // <p> tags where the beginning of the second element implicitly closes the | |
| 10561 // first, causing a confusing mess. | |
| 10562 // https://html.spec.whatwg.org/multipage/syntax.html#special | |
| 10563 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 | |
| 10564 | |
| 10565 var inScopeTags = ['applet', 'caption', 'html', 'table', 'td', 'th', 'marquee', 'object', 'template', // https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point | |
| 10566 // TODO: Distinguish by namespace here -- for <title>, including it here | |
| 10567 // errs on the side of fewer warnings | |
| 10568 'foreignObject', 'desc', 'title']; // https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope | |
| 10569 | |
| 10570 var buttonScopeTags = inScopeTags.concat(['button']); // https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags | |
| 10571 | |
| 10572 var impliedEndTags = ['dd', 'dt', 'li', 'option', 'optgroup', 'p', 'rp', 'rt']; | |
| 10573 var emptyAncestorInfo = { | |
| 10574 current: null, | |
| 10575 formTag: null, | |
| 10576 aTagInScope: null, | |
| 10577 buttonTagInScope: null, | |
| 10578 nobrTagInScope: null, | |
| 10579 pTagInButtonScope: null, | |
| 10580 listItemTagAutoclosing: null, | |
| 10581 dlItemTagAutoclosing: null | |
| 10582 }; | |
| 10583 | |
| 10584 updatedAncestorInfo = function (oldInfo, tag) { | |
| 10585 var ancestorInfo = assign({}, oldInfo || emptyAncestorInfo); | |
| 10586 | |
| 10587 var info = { | |
| 10588 tag: tag | |
| 10589 }; | |
| 10590 | |
| 10591 if (inScopeTags.indexOf(tag) !== -1) { | |
| 10592 ancestorInfo.aTagInScope = null; | |
| 10593 ancestorInfo.buttonTagInScope = null; | |
| 10594 ancestorInfo.nobrTagInScope = null; | |
| 10595 } | |
| 10596 | |
| 10597 if (buttonScopeTags.indexOf(tag) !== -1) { | |
| 10598 ancestorInfo.pTagInButtonScope = null; | |
| 10599 } // See rules for 'li', 'dd', 'dt' start tags in | |
| 10600 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody | |
| 10601 | |
| 10602 | |
| 10603 if (specialTags.indexOf(tag) !== -1 && tag !== 'address' && tag !== 'div' && tag !== 'p') { | |
| 10604 ancestorInfo.listItemTagAutoclosing = null; | |
| 10605 ancestorInfo.dlItemTagAutoclosing = null; | |
| 10606 } | |
| 10607 | |
| 10608 ancestorInfo.current = info; | |
| 10609 | |
| 10610 if (tag === 'form') { | |
| 10611 ancestorInfo.formTag = info; | |
| 10612 } | |
| 10613 | |
| 10614 if (tag === 'a') { | |
| 10615 ancestorInfo.aTagInScope = info; | |
| 10616 } | |
| 10617 | |
| 10618 if (tag === 'button') { | |
| 10619 ancestorInfo.buttonTagInScope = info; | |
| 10620 } | |
| 10621 | |
| 10622 if (tag === 'nobr') { | |
| 10623 ancestorInfo.nobrTagInScope = info; | |
| 10624 } | |
| 10625 | |
| 10626 if (tag === 'p') { | |
| 10627 ancestorInfo.pTagInButtonScope = info; | |
| 10628 } | |
| 10629 | |
| 10630 if (tag === 'li') { | |
| 10631 ancestorInfo.listItemTagAutoclosing = info; | |
| 10632 } | |
| 10633 | |
| 10634 if (tag === 'dd' || tag === 'dt') { | |
| 10635 ancestorInfo.dlItemTagAutoclosing = info; | |
| 10636 } | |
| 10637 | |
| 10638 return ancestorInfo; | |
| 10639 }; | |
| 10640 /** | |
| 10641 * Returns whether | |
| 10642 */ | |
| 10643 | |
| 10644 | |
| 10645 var isTagValidWithParent = function (tag, parentTag) { | |
| 10646 // First, let's check if we're in an unusual parsing mode... | |
| 10647 switch (parentTag) { | |
| 10648 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect | |
| 10649 case 'select': | |
| 10650 return tag === 'option' || tag === 'optgroup' || tag === '#text'; | |
| 10651 | |
| 10652 case 'optgroup': | |
| 10653 return tag === 'option' || tag === '#text'; | |
| 10654 // Strictly speaking, seeing an <option> doesn't mean we're in a <select> | |
| 10655 // but | |
| 10656 | |
| 10657 case 'option': | |
| 10658 return tag === '#text'; | |
| 10659 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd | |
| 10660 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption | |
| 10661 // No special behavior since these rules fall back to "in body" mode for | |
| 10662 // all except special table nodes which cause bad parsing behavior anyway. | |
| 10663 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr | |
| 10664 | |
| 10665 case 'tr': | |
| 10666 return tag === 'th' || tag === 'td' || tag === 'style' || tag === 'script' || tag === 'template'; | |
| 10667 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody | |
| 10668 | |
| 10669 case 'tbody': | |
| 10670 case 'thead': | |
| 10671 case 'tfoot': | |
| 10672 return tag === 'tr' || tag === 'style' || tag === 'script' || tag === 'template'; | |
| 10673 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup | |
| 10674 | |
| 10675 case 'colgroup': | |
| 10676 return tag === 'col' || tag === 'template'; | |
| 10677 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable | |
| 10678 | |
| 10679 case 'table': | |
| 10680 return tag === 'caption' || tag === 'colgroup' || tag === 'tbody' || tag === 'tfoot' || tag === 'thead' || tag === 'style' || tag === 'script' || tag === 'template'; | |
| 10681 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead | |
| 10682 | |
| 10683 case 'head': | |
| 10684 return tag === 'base' || tag === 'basefont' || tag === 'bgsound' || tag === 'link' || tag === 'meta' || tag === 'title' || tag === 'noscript' || tag === 'noframes' || tag === 'style' || tag === 'script' || tag === 'template'; | |
| 10685 // https://html.spec.whatwg.org/multipage/semantics.html#the-html-element | |
| 10686 | |
| 10687 case 'html': | |
| 10688 return tag === 'head' || tag === 'body' || tag === 'frameset'; | |
| 10689 | |
| 10690 case 'frameset': | |
| 10691 return tag === 'frame'; | |
| 10692 | |
| 10693 case '#document': | |
| 10694 return tag === 'html'; | |
| 10695 } // Probably in the "in body" parsing mode, so we outlaw only tag combos | |
| 10696 // where the parsing rules cause implicit opens or closes to be added. | |
| 10697 // https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody | |
| 10698 | |
| 10699 | |
| 10700 switch (tag) { | |
| 10701 case 'h1': | |
| 10702 case 'h2': | |
| 10703 case 'h3': | |
| 10704 case 'h4': | |
| 10705 case 'h5': | |
| 10706 case 'h6': | |
| 10707 return parentTag !== 'h1' && parentTag !== 'h2' && parentTag !== 'h3' && parentTag !== 'h4' && parentTag !== 'h5' && parentTag !== 'h6'; | |
| 10708 | |
| 10709 case 'rp': | |
| 10710 case 'rt': | |
| 10711 return impliedEndTags.indexOf(parentTag) === -1; | |
| 10712 | |
| 10713 case 'body': | |
| 10714 case 'caption': | |
| 10715 case 'col': | |
| 10716 case 'colgroup': | |
| 10717 case 'frameset': | |
| 10718 case 'frame': | |
| 10719 case 'head': | |
| 10720 case 'html': | |
| 10721 case 'tbody': | |
| 10722 case 'td': | |
| 10723 case 'tfoot': | |
| 10724 case 'th': | |
| 10725 case 'thead': | |
| 10726 case 'tr': | |
| 10727 // These tags are only valid with a few parents that have special child | |
| 10728 // parsing rules -- if we're down here, then none of those matched and | |
| 10729 // so we allow it only if we don't know what the parent is, as all other | |
| 10730 // cases are invalid. | |
| 10731 return parentTag == null; | |
| 10732 } | |
| 10733 | |
| 10734 return true; | |
| 10735 }; | |
| 10736 /** | |
| 10737 * Returns whether | |
| 10738 */ | |
| 10739 | |
| 10740 | |
| 10741 var findInvalidAncestorForTag = function (tag, ancestorInfo) { | |
| 10742 switch (tag) { | |
| 10743 case 'address': | |
| 10744 case 'article': | |
| 10745 case 'aside': | |
| 10746 case 'blockquote': | |
| 10747 case 'center': | |
| 10748 case 'details': | |
| 10749 case 'dialog': | |
| 10750 case 'dir': | |
| 10751 case 'div': | |
| 10752 case 'dl': | |
| 10753 case 'fieldset': | |
| 10754 case 'figcaption': | |
| 10755 case 'figure': | |
| 10756 case 'footer': | |
| 10757 case 'header': | |
| 10758 case 'hgroup': | |
| 10759 case 'main': | |
| 10760 case 'menu': | |
| 10761 case 'nav': | |
| 10762 case 'ol': | |
| 10763 case 'p': | |
| 10764 case 'section': | |
| 10765 case 'summary': | |
| 10766 case 'ul': | |
| 10767 case 'pre': | |
| 10768 case 'listing': | |
| 10769 case 'table': | |
| 10770 case 'hr': | |
| 10771 case 'xmp': | |
| 10772 case 'h1': | |
| 10773 case 'h2': | |
| 10774 case 'h3': | |
| 10775 case 'h4': | |
| 10776 case 'h5': | |
| 10777 case 'h6': | |
| 10778 return ancestorInfo.pTagInButtonScope; | |
| 10779 | |
| 10780 case 'form': | |
| 10781 return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope; | |
| 10782 | |
| 10783 case 'li': | |
| 10784 return ancestorInfo.listItemTagAutoclosing; | |
| 10785 | |
| 10786 case 'dd': | |
| 10787 case 'dt': | |
| 10788 return ancestorInfo.dlItemTagAutoclosing; | |
| 10789 | |
| 10790 case 'button': | |
| 10791 return ancestorInfo.buttonTagInScope; | |
| 10792 | |
| 10793 case 'a': | |
| 10794 // Spec says something about storing a list of markers, but it sounds | |
| 10795 // equivalent to this check. | |
| 10796 return ancestorInfo.aTagInScope; | |
| 10797 | |
| 10798 case 'nobr': | |
| 10799 return ancestorInfo.nobrTagInScope; | |
| 10800 } | |
| 10801 | |
| 10802 return null; | |
| 10803 }; | |
| 10804 | |
| 10805 var didWarn$1 = {}; | |
| 10806 | |
| 10807 validateDOMNesting = function (childTag, childText, ancestorInfo) { | |
| 10808 ancestorInfo = ancestorInfo || emptyAncestorInfo; | |
| 10809 var parentInfo = ancestorInfo.current; | |
| 10810 var parentTag = parentInfo && parentInfo.tag; | |
| 10811 | |
| 10812 if (childText != null) { | |
| 10813 if (childTag != null) { | |
| 10814 error('validateDOMNesting: when childText is passed, childTag should be null'); | |
| 10815 } | |
| 10816 | |
| 10817 childTag = '#text'; | |
| 10818 } | |
| 10819 | |
| 10820 var invalidParent = isTagValidWithParent(childTag, parentTag) ? null : parentInfo; | |
| 10821 var invalidAncestor = invalidParent ? null : findInvalidAncestorForTag(childTag, ancestorInfo); | |
| 10822 var invalidParentOrAncestor = invalidParent || invalidAncestor; | |
| 10823 | |
| 10824 if (!invalidParentOrAncestor) { | |
| 10825 return; | |
| 10826 } | |
| 10827 | |
| 10828 var ancestorTag = invalidParentOrAncestor.tag; | |
| 10829 var warnKey = !!invalidParent + '|' + childTag + '|' + ancestorTag; | |
| 10830 | |
| 10831 if (didWarn$1[warnKey]) { | |
| 10832 return; | |
| 10833 } | |
| 10834 | |
| 10835 didWarn$1[warnKey] = true; | |
| 10836 var tagDisplayName = childTag; | |
| 10837 var whitespaceInfo = ''; | |
| 10838 | |
| 10839 if (childTag === '#text') { | |
| 10840 if (/\S/.test(childText)) { | |
| 10841 tagDisplayName = 'Text nodes'; | |
| 10842 } else { | |
| 10843 tagDisplayName = 'Whitespace text nodes'; | |
| 10844 whitespaceInfo = " Make sure you don't have any extra whitespace between tags on " + 'each line of your source code.'; | |
| 10845 } | |
| 10846 } else { | |
| 10847 tagDisplayName = '<' + childTag + '>'; | |
| 10848 } | |
| 10849 | |
| 10850 if (invalidParent) { | |
| 10851 var info = ''; | |
| 10852 | |
| 10853 if (ancestorTag === 'table' && childTag === 'tr') { | |
| 10854 info += ' Add a <tbody>, <thead> or <tfoot> to your code to match the DOM tree generated by ' + 'the browser.'; | |
| 10855 } | |
| 10856 | |
| 10857 error('validateDOMNesting(...): %s cannot appear as a child of <%s>.%s%s', tagDisplayName, ancestorTag, whitespaceInfo, info); | |
| 10858 } else { | |
| 10859 error('validateDOMNesting(...): %s cannot appear as a descendant of ' + '<%s>.', tagDisplayName, ancestorTag); | |
| 10860 } | |
| 10861 }; | |
| 10862 } | |
| 10863 | |
| 10864 var SUPPRESS_HYDRATION_WARNING$1 = 'suppressHydrationWarning'; | |
| 10865 var SUSPENSE_START_DATA = '$'; | |
| 10866 var SUSPENSE_END_DATA = '/$'; | |
| 10867 var SUSPENSE_PENDING_START_DATA = '$?'; | |
| 10868 var SUSPENSE_FALLBACK_START_DATA = '$!'; | |
| 10869 var STYLE$1 = 'style'; | |
| 10870 var eventsEnabled = null; | |
| 10871 var selectionInformation = null; | |
| 10872 function getRootHostContext(rootContainerInstance) { | |
| 10873 var type; | |
| 10874 var namespace; | |
| 10875 var nodeType = rootContainerInstance.nodeType; | |
| 10876 | |
| 10877 switch (nodeType) { | |
| 10878 case DOCUMENT_NODE: | |
| 10879 case DOCUMENT_FRAGMENT_NODE: | |
| 10880 { | |
| 10881 type = nodeType === DOCUMENT_NODE ? '#document' : '#fragment'; | |
| 10882 var root = rootContainerInstance.documentElement; | |
| 10883 namespace = root ? root.namespaceURI : getChildNamespace(null, ''); | |
| 10884 break; | |
| 10885 } | |
| 10886 | |
| 10887 default: | |
| 10888 { | |
| 10889 var container = nodeType === COMMENT_NODE ? rootContainerInstance.parentNode : rootContainerInstance; | |
| 10890 var ownNamespace = container.namespaceURI || null; | |
| 10891 type = container.tagName; | |
| 10892 namespace = getChildNamespace(ownNamespace, type); | |
| 10893 break; | |
| 10894 } | |
| 10895 } | |
| 10896 | |
| 10897 { | |
| 10898 var validatedTag = type.toLowerCase(); | |
| 10899 var ancestorInfo = updatedAncestorInfo(null, validatedTag); | |
| 10900 return { | |
| 10901 namespace: namespace, | |
| 10902 ancestorInfo: ancestorInfo | |
| 10903 }; | |
| 10904 } | |
| 10905 } | |
| 10906 function getChildHostContext(parentHostContext, type, rootContainerInstance) { | |
| 10907 { | |
| 10908 var parentHostContextDev = parentHostContext; | |
| 10909 var namespace = getChildNamespace(parentHostContextDev.namespace, type); | |
| 10910 var ancestorInfo = updatedAncestorInfo(parentHostContextDev.ancestorInfo, type); | |
| 10911 return { | |
| 10912 namespace: namespace, | |
| 10913 ancestorInfo: ancestorInfo | |
| 10914 }; | |
| 10915 } | |
| 10916 } | |
| 10917 function getPublicInstance(instance) { | |
| 10918 return instance; | |
| 10919 } | |
| 10920 function prepareForCommit(containerInfo) { | |
| 10921 eventsEnabled = isEnabled(); | |
| 10922 selectionInformation = getSelectionInformation(); | |
| 10923 var activeInstance = null; | |
| 10924 | |
| 10925 setEnabled(false); | |
| 10926 return activeInstance; | |
| 10927 } | |
| 10928 function resetAfterCommit(containerInfo) { | |
| 10929 restoreSelection(selectionInformation); | |
| 10930 setEnabled(eventsEnabled); | |
| 10931 eventsEnabled = null; | |
| 10932 selectionInformation = null; | |
| 10933 } | |
| 10934 function createInstance(type, props, rootContainerInstance, hostContext, internalInstanceHandle) { | |
| 10935 var parentNamespace; | |
| 10936 | |
| 10937 { | |
| 10938 // TODO: take namespace into account when validating. | |
| 10939 var hostContextDev = hostContext; | |
| 10940 validateDOMNesting(type, null, hostContextDev.ancestorInfo); | |
| 10941 | |
| 10942 if (typeof props.children === 'string' || typeof props.children === 'number') { | |
| 10943 var string = '' + props.children; | |
| 10944 var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type); | |
| 10945 validateDOMNesting(null, string, ownAncestorInfo); | |
| 10946 } | |
| 10947 | |
| 10948 parentNamespace = hostContextDev.namespace; | |
| 10949 } | |
| 10950 | |
| 10951 var domElement = createElement(type, props, rootContainerInstance, parentNamespace); | |
| 10952 precacheFiberNode(internalInstanceHandle, domElement); | |
| 10953 updateFiberProps(domElement, props); | |
| 10954 return domElement; | |
| 10955 } | |
| 10956 function appendInitialChild(parentInstance, child) { | |
| 10957 parentInstance.appendChild(child); | |
| 10958 } | |
| 10959 function finalizeInitialChildren(domElement, type, props, rootContainerInstance, hostContext) { | |
| 10960 setInitialProperties(domElement, type, props, rootContainerInstance); | |
| 10961 | |
| 10962 switch (type) { | |
| 10963 case 'button': | |
| 10964 case 'input': | |
| 10965 case 'select': | |
| 10966 case 'textarea': | |
| 10967 return !!props.autoFocus; | |
| 10968 | |
| 10969 case 'img': | |
| 10970 return true; | |
| 10971 | |
| 10972 default: | |
| 10973 return false; | |
| 10974 } | |
| 10975 } | |
| 10976 function prepareUpdate(domElement, type, oldProps, newProps, rootContainerInstance, hostContext) { | |
| 10977 { | |
| 10978 var hostContextDev = hostContext; | |
| 10979 | |
| 10980 if (typeof newProps.children !== typeof oldProps.children && (typeof newProps.children === 'string' || typeof newProps.children === 'number')) { | |
| 10981 var string = '' + newProps.children; | |
| 10982 var ownAncestorInfo = updatedAncestorInfo(hostContextDev.ancestorInfo, type); | |
| 10983 validateDOMNesting(null, string, ownAncestorInfo); | |
| 10984 } | |
| 10985 } | |
| 10986 | |
| 10987 return diffProperties(domElement, type, oldProps, newProps); | |
| 10988 } | |
| 10989 function shouldSetTextContent(type, props) { | |
| 10990 return type === 'textarea' || type === 'noscript' || typeof props.children === 'string' || typeof props.children === 'number' || typeof props.dangerouslySetInnerHTML === 'object' && props.dangerouslySetInnerHTML !== null && props.dangerouslySetInnerHTML.__html != null; | |
| 10991 } | |
| 10992 function createTextInstance(text, rootContainerInstance, hostContext, internalInstanceHandle) { | |
| 10993 { | |
| 10994 var hostContextDev = hostContext; | |
| 10995 validateDOMNesting(null, text, hostContextDev.ancestorInfo); | |
| 10996 } | |
| 10997 | |
| 10998 var textNode = createTextNode(text, rootContainerInstance); | |
| 10999 precacheFiberNode(internalInstanceHandle, textNode); | |
| 11000 return textNode; | |
| 11001 } | |
| 11002 function getCurrentEventPriority() { | |
| 11003 var currentEvent = window.event; | |
| 11004 | |
| 11005 if (currentEvent === undefined) { | |
| 11006 return DefaultEventPriority; | |
| 11007 } | |
| 11008 | |
| 11009 return getEventPriority(currentEvent.type); | |
| 11010 } | |
| 11011 // if a component just imports ReactDOM (e.g. for findDOMNode). | |
| 11012 // Some environments might not have setTimeout or clearTimeout. | |
| 11013 | |
| 11014 var scheduleTimeout = typeof setTimeout === 'function' ? setTimeout : undefined; | |
| 11015 var cancelTimeout = typeof clearTimeout === 'function' ? clearTimeout : undefined; | |
| 11016 var noTimeout = -1; | |
| 11017 var localPromise = typeof Promise === 'function' ? Promise : undefined; // ------------------- | |
| 11018 var scheduleMicrotask = typeof queueMicrotask === 'function' ? queueMicrotask : typeof localPromise !== 'undefined' ? function (callback) { | |
| 11019 return localPromise.resolve(null).then(callback).catch(handleErrorInNextTick); | |
| 11020 } : scheduleTimeout; // TODO: Determine the best fallback here. | |
| 11021 | |
| 11022 function handleErrorInNextTick(error) { | |
| 11023 setTimeout(function () { | |
| 11024 throw error; | |
| 11025 }); | |
| 11026 } // ------------------- | |
| 11027 function commitMount(domElement, type, newProps, internalInstanceHandle) { | |
| 11028 // Despite the naming that might imply otherwise, this method only | |
| 11029 // fires if there is an `Update` effect scheduled during mounting. | |
| 11030 // This happens if `finalizeInitialChildren` returns `true` (which it | |
| 11031 // does to implement the `autoFocus` attribute on the client). But | |
| 11032 // there are also other cases when this might happen (such as patching | |
| 11033 // up text content during hydration mismatch). So we'll check this again. | |
| 11034 switch (type) { | |
| 11035 case 'button': | |
| 11036 case 'input': | |
| 11037 case 'select': | |
| 11038 case 'textarea': | |
| 11039 if (newProps.autoFocus) { | |
| 11040 domElement.focus(); | |
| 11041 } | |
| 11042 | |
| 11043 return; | |
| 11044 | |
| 11045 case 'img': | |
| 11046 { | |
| 11047 if (newProps.src) { | |
| 11048 domElement.src = newProps.src; | |
| 11049 } | |
| 11050 | |
| 11051 return; | |
| 11052 } | |
| 11053 } | |
| 11054 } | |
| 11055 function commitUpdate(domElement, updatePayload, type, oldProps, newProps, internalInstanceHandle) { | |
| 11056 // Apply the diff to the DOM node. | |
| 11057 updateProperties(domElement, updatePayload, type, oldProps, newProps); // Update the props handle so that we know which props are the ones with | |
| 11058 // with current event handlers. | |
| 11059 | |
| 11060 updateFiberProps(domElement, newProps); | |
| 11061 } | |
| 11062 function resetTextContent(domElement) { | |
| 11063 setTextContent(domElement, ''); | |
| 11064 } | |
| 11065 function commitTextUpdate(textInstance, oldText, newText) { | |
| 11066 textInstance.nodeValue = newText; | |
| 11067 } | |
| 11068 function appendChild(parentInstance, child) { | |
| 11069 parentInstance.appendChild(child); | |
| 11070 } | |
| 11071 function appendChildToContainer(container, child) { | |
| 11072 var parentNode; | |
| 11073 | |
| 11074 if (container.nodeType === COMMENT_NODE) { | |
| 11075 parentNode = container.parentNode; | |
| 11076 parentNode.insertBefore(child, container); | |
| 11077 } else { | |
| 11078 parentNode = container; | |
| 11079 parentNode.appendChild(child); | |
| 11080 } // This container might be used for a portal. | |
| 11081 // If something inside a portal is clicked, that click should bubble | |
| 11082 // through the React tree. However, on Mobile Safari the click would | |
| 11083 // never bubble through the *DOM* tree unless an ancestor with onclick | |
| 11084 // event exists. So we wouldn't see it and dispatch it. | |
| 11085 // This is why we ensure that non React root containers have inline onclick | |
| 11086 // defined. | |
| 11087 // https://github.com/facebook/react/issues/11918 | |
| 11088 | |
| 11089 | |
| 11090 var reactRootContainer = container._reactRootContainer; | |
| 11091 | |
| 11092 if ((reactRootContainer === null || reactRootContainer === undefined) && parentNode.onclick === null) { | |
| 11093 // TODO: This cast may not be sound for SVG, MathML or custom elements. | |
| 11094 trapClickOnNonInteractiveElement(parentNode); | |
| 11095 } | |
| 11096 } | |
| 11097 function insertBefore(parentInstance, child, beforeChild) { | |
| 11098 parentInstance.insertBefore(child, beforeChild); | |
| 11099 } | |
| 11100 function insertInContainerBefore(container, child, beforeChild) { | |
| 11101 if (container.nodeType === COMMENT_NODE) { | |
| 11102 container.parentNode.insertBefore(child, beforeChild); | |
| 11103 } else { | |
| 11104 container.insertBefore(child, beforeChild); | |
| 11105 } | |
| 11106 } | |
| 11107 | |
| 11108 function removeChild(parentInstance, child) { | |
| 11109 parentInstance.removeChild(child); | |
| 11110 } | |
| 11111 function removeChildFromContainer(container, child) { | |
| 11112 if (container.nodeType === COMMENT_NODE) { | |
| 11113 container.parentNode.removeChild(child); | |
| 11114 } else { | |
| 11115 container.removeChild(child); | |
| 11116 } | |
| 11117 } | |
| 11118 function clearSuspenseBoundary(parentInstance, suspenseInstance) { | |
| 11119 var node = suspenseInstance; // Delete all nodes within this suspense boundary. | |
| 11120 // There might be nested nodes so we need to keep track of how | |
| 11121 // deep we are and only break out when we're back on top. | |
| 11122 | |
| 11123 var depth = 0; | |
| 11124 | |
| 11125 do { | |
| 11126 var nextNode = node.nextSibling; | |
| 11127 parentInstance.removeChild(node); | |
| 11128 | |
| 11129 if (nextNode && nextNode.nodeType === COMMENT_NODE) { | |
| 11130 var data = nextNode.data; | |
| 11131 | |
| 11132 if (data === SUSPENSE_END_DATA) { | |
| 11133 if (depth === 0) { | |
| 11134 parentInstance.removeChild(nextNode); // Retry if any event replaying was blocked on this. | |
| 11135 | |
| 11136 retryIfBlockedOn(suspenseInstance); | |
| 11137 return; | |
| 11138 } else { | |
| 11139 depth--; | |
| 11140 } | |
| 11141 } else if (data === SUSPENSE_START_DATA || data === SUSPENSE_PENDING_START_DATA || data === SUSPENSE_FALLBACK_START_DATA) { | |
| 11142 depth++; | |
| 11143 } | |
| 11144 } | |
| 11145 | |
| 11146 node = nextNode; | |
| 11147 } while (node); // TODO: Warn, we didn't find the end comment boundary. | |
| 11148 // Retry if any event replaying was blocked on this. | |
| 11149 | |
| 11150 | |
| 11151 retryIfBlockedOn(suspenseInstance); | |
| 11152 } | |
| 11153 function clearSuspenseBoundaryFromContainer(container, suspenseInstance) { | |
| 11154 if (container.nodeType === COMMENT_NODE) { | |
| 11155 clearSuspenseBoundary(container.parentNode, suspenseInstance); | |
| 11156 } else if (container.nodeType === ELEMENT_NODE) { | |
| 11157 clearSuspenseBoundary(container, suspenseInstance); | |
| 11158 } // Retry if any event replaying was blocked on this. | |
| 11159 | |
| 11160 | |
| 11161 retryIfBlockedOn(container); | |
| 11162 } | |
| 11163 function hideInstance(instance) { | |
| 11164 // TODO: Does this work for all element types? What about MathML? Should we | |
| 11165 // pass host context to this method? | |
| 11166 instance = instance; | |
| 11167 var style = instance.style; | |
| 11168 | |
| 11169 if (typeof style.setProperty === 'function') { | |
| 11170 style.setProperty('display', 'none', 'important'); | |
| 11171 } else { | |
| 11172 style.display = 'none'; | |
| 11173 } | |
| 11174 } | |
| 11175 function hideTextInstance(textInstance) { | |
| 11176 textInstance.nodeValue = ''; | |
| 11177 } | |
| 11178 function unhideInstance(instance, props) { | |
| 11179 instance = instance; | |
| 11180 var styleProp = props[STYLE$1]; | |
| 11181 var display = styleProp !== undefined && styleProp !== null && styleProp.hasOwnProperty('display') ? styleProp.display : null; | |
| 11182 instance.style.display = dangerousStyleValue('display', display); | |
| 11183 } | |
| 11184 function unhideTextInstance(textInstance, text) { | |
| 11185 textInstance.nodeValue = text; | |
| 11186 } | |
| 11187 function clearContainer(container) { | |
| 11188 if (container.nodeType === ELEMENT_NODE) { | |
| 11189 container.textContent = ''; | |
| 11190 } else if (container.nodeType === DOCUMENT_NODE) { | |
| 11191 if (container.documentElement) { | |
| 11192 container.removeChild(container.documentElement); | |
| 11193 } | |
| 11194 } | |
| 11195 } // ------------------- | |
| 11196 function canHydrateInstance(instance, type, props) { | |
| 11197 if (instance.nodeType !== ELEMENT_NODE || type.toLowerCase() !== instance.nodeName.toLowerCase()) { | |
| 11198 return null; | |
| 11199 } // This has now been refined to an element node. | |
| 11200 | |
| 11201 | |
| 11202 return instance; | |
| 11203 } | |
| 11204 function canHydrateTextInstance(instance, text) { | |
| 11205 if (text === '' || instance.nodeType !== TEXT_NODE) { | |
| 11206 // Empty strings are not parsed by HTML so there won't be a correct match here. | |
| 11207 return null; | |
| 11208 } // This has now been refined to a text node. | |
| 11209 | |
| 11210 | |
| 11211 return instance; | |
| 11212 } | |
| 11213 function canHydrateSuspenseInstance(instance) { | |
| 11214 if (instance.nodeType !== COMMENT_NODE) { | |
| 11215 // Empty strings are not parsed by HTML so there won't be a correct match here. | |
| 11216 return null; | |
| 11217 } // This has now been refined to a suspense node. | |
| 11218 | |
| 11219 | |
| 11220 return instance; | |
| 11221 } | |
| 11222 function isSuspenseInstancePending(instance) { | |
| 11223 return instance.data === SUSPENSE_PENDING_START_DATA; | |
| 11224 } | |
| 11225 function isSuspenseInstanceFallback(instance) { | |
| 11226 return instance.data === SUSPENSE_FALLBACK_START_DATA; | |
| 11227 } | |
| 11228 function getSuspenseInstanceFallbackErrorDetails(instance) { | |
| 11229 var dataset = instance.nextSibling && instance.nextSibling.dataset; | |
| 11230 var digest, message, stack; | |
| 11231 | |
| 11232 if (dataset) { | |
| 11233 digest = dataset.dgst; | |
| 11234 | |
| 11235 { | |
| 11236 message = dataset.msg; | |
| 11237 stack = dataset.stck; | |
| 11238 } | |
| 11239 } | |
| 11240 | |
| 11241 { | |
| 11242 return { | |
| 11243 message: message, | |
| 11244 digest: digest, | |
| 11245 stack: stack | |
| 11246 }; | |
| 11247 } // let value = {message: undefined, hash: undefined}; | |
| 11248 // const nextSibling = instance.nextSibling; | |
| 11249 // if (nextSibling) { | |
| 11250 // const dataset = ((nextSibling: any): HTMLTemplateElement).dataset; | |
| 11251 // value.message = dataset.msg; | |
| 11252 // value.hash = dataset.hash; | |
| 11253 // if (true) { | |
| 11254 // value.stack = dataset.stack; | |
| 11255 // } | |
| 11256 // } | |
| 11257 // return value; | |
| 11258 | |
| 11259 } | |
| 11260 function registerSuspenseInstanceRetry(instance, callback) { | |
| 11261 instance._reactRetry = callback; | |
| 11262 } | |
| 11263 | |
| 11264 function getNextHydratable(node) { | |
| 11265 // Skip non-hydratable nodes. | |
| 11266 for (; node != null; node = node.nextSibling) { | |
| 11267 var nodeType = node.nodeType; | |
| 11268 | |
| 11269 if (nodeType === ELEMENT_NODE || nodeType === TEXT_NODE) { | |
| 11270 break; | |
| 11271 } | |
| 11272 | |
| 11273 if (nodeType === COMMENT_NODE) { | |
| 11274 var nodeData = node.data; | |
| 11275 | |
| 11276 if (nodeData === SUSPENSE_START_DATA || nodeData === SUSPENSE_FALLBACK_START_DATA || nodeData === SUSPENSE_PENDING_START_DATA) { | |
| 11277 break; | |
| 11278 } | |
| 11279 | |
| 11280 if (nodeData === SUSPENSE_END_DATA) { | |
| 11281 return null; | |
| 11282 } | |
| 11283 } | |
| 11284 } | |
| 11285 | |
| 11286 return node; | |
| 11287 } | |
| 11288 | |
| 11289 function getNextHydratableSibling(instance) { | |
| 11290 return getNextHydratable(instance.nextSibling); | |
| 11291 } | |
| 11292 function getFirstHydratableChild(parentInstance) { | |
| 11293 return getNextHydratable(parentInstance.firstChild); | |
| 11294 } | |
| 11295 function getFirstHydratableChildWithinContainer(parentContainer) { | |
| 11296 return getNextHydratable(parentContainer.firstChild); | |
| 11297 } | |
| 11298 function getFirstHydratableChildWithinSuspenseInstance(parentInstance) { | |
| 11299 return getNextHydratable(parentInstance.nextSibling); | |
| 11300 } | |
| 11301 function hydrateInstance(instance, type, props, rootContainerInstance, hostContext, internalInstanceHandle, shouldWarnDev) { | |
| 11302 precacheFiberNode(internalInstanceHandle, instance); // TODO: Possibly defer this until the commit phase where all the events | |
| 11303 // get attached. | |
| 11304 | |
| 11305 updateFiberProps(instance, props); | |
| 11306 var parentNamespace; | |
| 11307 | |
| 11308 { | |
| 11309 var hostContextDev = hostContext; | |
| 11310 parentNamespace = hostContextDev.namespace; | |
| 11311 } // TODO: Temporary hack to check if we're in a concurrent root. We can delete | |
| 11312 // when the legacy root API is removed. | |
| 11313 | |
| 11314 | |
| 11315 var isConcurrentMode = (internalInstanceHandle.mode & ConcurrentMode) !== NoMode; | |
| 11316 return diffHydratedProperties(instance, type, props, parentNamespace, rootContainerInstance, isConcurrentMode, shouldWarnDev); | |
| 11317 } | |
| 11318 function hydrateTextInstance(textInstance, text, internalInstanceHandle, shouldWarnDev) { | |
| 11319 precacheFiberNode(internalInstanceHandle, textInstance); // TODO: Temporary hack to check if we're in a concurrent root. We can delete | |
| 11320 // when the legacy root API is removed. | |
| 11321 | |
| 11322 var isConcurrentMode = (internalInstanceHandle.mode & ConcurrentMode) !== NoMode; | |
| 11323 return diffHydratedText(textInstance, text); | |
| 11324 } | |
| 11325 function hydrateSuspenseInstance(suspenseInstance, internalInstanceHandle) { | |
| 11326 precacheFiberNode(internalInstanceHandle, suspenseInstance); | |
| 11327 } | |
| 11328 function getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance) { | |
| 11329 var node = suspenseInstance.nextSibling; // Skip past all nodes within this suspense boundary. | |
| 11330 // There might be nested nodes so we need to keep track of how | |
| 11331 // deep we are and only break out when we're back on top. | |
| 11332 | |
| 11333 var depth = 0; | |
| 11334 | |
| 11335 while (node) { | |
| 11336 if (node.nodeType === COMMENT_NODE) { | |
| 11337 var data = node.data; | |
| 11338 | |
| 11339 if (data === SUSPENSE_END_DATA) { | |
| 11340 if (depth === 0) { | |
| 11341 return getNextHydratableSibling(node); | |
| 11342 } else { | |
| 11343 depth--; | |
| 11344 } | |
| 11345 } else if (data === SUSPENSE_START_DATA || data === SUSPENSE_FALLBACK_START_DATA || data === SUSPENSE_PENDING_START_DATA) { | |
| 11346 depth++; | |
| 11347 } | |
| 11348 } | |
| 11349 | |
| 11350 node = node.nextSibling; | |
| 11351 } // TODO: Warn, we didn't find the end comment boundary. | |
| 11352 | |
| 11353 | |
| 11354 return null; | |
| 11355 } // Returns the SuspenseInstance if this node is a direct child of a | |
| 11356 // SuspenseInstance. I.e. if its previous sibling is a Comment with | |
| 11357 // SUSPENSE_x_START_DATA. Otherwise, null. | |
| 11358 | |
| 11359 function getParentSuspenseInstance(targetInstance) { | |
| 11360 var node = targetInstance.previousSibling; // Skip past all nodes within this suspense boundary. | |
| 11361 // There might be nested nodes so we need to keep track of how | |
| 11362 // deep we are and only break out when we're back on top. | |
| 11363 | |
| 11364 var depth = 0; | |
| 11365 | |
| 11366 while (node) { | |
| 11367 if (node.nodeType === COMMENT_NODE) { | |
| 11368 var data = node.data; | |
| 11369 | |
| 11370 if (data === SUSPENSE_START_DATA || data === SUSPENSE_FALLBACK_START_DATA || data === SUSPENSE_PENDING_START_DATA) { | |
| 11371 if (depth === 0) { | |
| 11372 return node; | |
| 11373 } else { | |
| 11374 depth--; | |
| 11375 } | |
| 11376 } else if (data === SUSPENSE_END_DATA) { | |
| 11377 depth++; | |
| 11378 } | |
| 11379 } | |
| 11380 | |
| 11381 node = node.previousSibling; | |
| 11382 } | |
| 11383 | |
| 11384 return null; | |
| 11385 } | |
| 11386 function commitHydratedContainer(container) { | |
| 11387 // Retry if any event replaying was blocked on this. | |
| 11388 retryIfBlockedOn(container); | |
| 11389 } | |
| 11390 function commitHydratedSuspenseInstance(suspenseInstance) { | |
| 11391 // Retry if any event replaying was blocked on this. | |
| 11392 retryIfBlockedOn(suspenseInstance); | |
| 11393 } | |
| 11394 function shouldDeleteUnhydratedTailInstances(parentType) { | |
| 11395 return parentType !== 'head' && parentType !== 'body'; | |
| 11396 } | |
| 11397 function didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, text, isConcurrentMode) { | |
| 11398 var shouldWarnDev = true; | |
| 11399 checkForUnmatchedText(textInstance.nodeValue, text, isConcurrentMode, shouldWarnDev); | |
| 11400 } | |
| 11401 function didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, text, isConcurrentMode) { | |
| 11402 if (parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) { | |
| 11403 var shouldWarnDev = true; | |
| 11404 checkForUnmatchedText(textInstance.nodeValue, text, isConcurrentMode, shouldWarnDev); | |
| 11405 } | |
| 11406 } | |
| 11407 function didNotHydrateInstanceWithinContainer(parentContainer, instance) { | |
| 11408 { | |
| 11409 if (instance.nodeType === ELEMENT_NODE) { | |
| 11410 warnForDeletedHydratableElement(parentContainer, instance); | |
| 11411 } else if (instance.nodeType === COMMENT_NODE) ; else { | |
| 11412 warnForDeletedHydratableText(parentContainer, instance); | |
| 11413 } | |
| 11414 } | |
| 11415 } | |
| 11416 function didNotHydrateInstanceWithinSuspenseInstance(parentInstance, instance) { | |
| 11417 { | |
| 11418 // $FlowFixMe: Only Element or Document can be parent nodes. | |
| 11419 var parentNode = parentInstance.parentNode; | |
| 11420 | |
| 11421 if (parentNode !== null) { | |
| 11422 if (instance.nodeType === ELEMENT_NODE) { | |
| 11423 warnForDeletedHydratableElement(parentNode, instance); | |
| 11424 } else if (instance.nodeType === COMMENT_NODE) ; else { | |
| 11425 warnForDeletedHydratableText(parentNode, instance); | |
| 11426 } | |
| 11427 } | |
| 11428 } | |
| 11429 } | |
| 11430 function didNotHydrateInstance(parentType, parentProps, parentInstance, instance, isConcurrentMode) { | |
| 11431 { | |
| 11432 if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) { | |
| 11433 if (instance.nodeType === ELEMENT_NODE) { | |
| 11434 warnForDeletedHydratableElement(parentInstance, instance); | |
| 11435 } else if (instance.nodeType === COMMENT_NODE) ; else { | |
| 11436 warnForDeletedHydratableText(parentInstance, instance); | |
| 11437 } | |
| 11438 } | |
| 11439 } | |
| 11440 } | |
| 11441 function didNotFindHydratableInstanceWithinContainer(parentContainer, type, props) { | |
| 11442 { | |
| 11443 warnForInsertedHydratedElement(parentContainer, type); | |
| 11444 } | |
| 11445 } | |
| 11446 function didNotFindHydratableTextInstanceWithinContainer(parentContainer, text) { | |
| 11447 { | |
| 11448 warnForInsertedHydratedText(parentContainer, text); | |
| 11449 } | |
| 11450 } | |
| 11451 function didNotFindHydratableInstanceWithinSuspenseInstance(parentInstance, type, props) { | |
| 11452 { | |
| 11453 // $FlowFixMe: Only Element or Document can be parent nodes. | |
| 11454 var parentNode = parentInstance.parentNode; | |
| 11455 if (parentNode !== null) warnForInsertedHydratedElement(parentNode, type); | |
| 11456 } | |
| 11457 } | |
| 11458 function didNotFindHydratableTextInstanceWithinSuspenseInstance(parentInstance, text) { | |
| 11459 { | |
| 11460 // $FlowFixMe: Only Element or Document can be parent nodes. | |
| 11461 var parentNode = parentInstance.parentNode; | |
| 11462 if (parentNode !== null) warnForInsertedHydratedText(parentNode, text); | |
| 11463 } | |
| 11464 } | |
| 11465 function didNotFindHydratableInstance(parentType, parentProps, parentInstance, type, props, isConcurrentMode) { | |
| 11466 { | |
| 11467 if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) { | |
| 11468 warnForInsertedHydratedElement(parentInstance, type); | |
| 11469 } | |
| 11470 } | |
| 11471 } | |
| 11472 function didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, text, isConcurrentMode) { | |
| 11473 { | |
| 11474 if (isConcurrentMode || parentProps[SUPPRESS_HYDRATION_WARNING$1] !== true) { | |
| 11475 warnForInsertedHydratedText(parentInstance, text); | |
| 11476 } | |
| 11477 } | |
| 11478 } | |
| 11479 function errorHydratingContainer(parentContainer) { | |
| 11480 { | |
| 11481 // TODO: This gets logged by onRecoverableError, too, so we should be | |
| 11482 // able to remove it. | |
| 11483 error('An error occurred during hydration. The server HTML was replaced with client content in <%s>.', parentContainer.nodeName.toLowerCase()); | |
| 11484 } | |
| 11485 } | |
| 11486 function preparePortalMount(portalInstance) { | |
| 11487 listenToAllSupportedEvents(portalInstance); | |
| 11488 } | |
| 11489 | |
| 11490 var randomKey = Math.random().toString(36).slice(2); | |
| 11491 var internalInstanceKey = '__reactFiber$' + randomKey; | |
| 11492 var internalPropsKey = '__reactProps$' + randomKey; | |
| 11493 var internalContainerInstanceKey = '__reactContainer$' + randomKey; | |
| 11494 var internalEventHandlersKey = '__reactEvents$' + randomKey; | |
| 11495 var internalEventHandlerListenersKey = '__reactListeners$' + randomKey; | |
| 11496 var internalEventHandlesSetKey = '__reactHandles$' + randomKey; | |
| 11497 function detachDeletedInstance(node) { | |
| 11498 // TODO: This function is only called on host components. I don't think all of | |
| 11499 // these fields are relevant. | |
| 11500 delete node[internalInstanceKey]; | |
| 11501 delete node[internalPropsKey]; | |
| 11502 delete node[internalEventHandlersKey]; | |
| 11503 delete node[internalEventHandlerListenersKey]; | |
| 11504 delete node[internalEventHandlesSetKey]; | |
| 11505 } | |
| 11506 function precacheFiberNode(hostInst, node) { | |
| 11507 node[internalInstanceKey] = hostInst; | |
| 11508 } | |
| 11509 function markContainerAsRoot(hostRoot, node) { | |
| 11510 node[internalContainerInstanceKey] = hostRoot; | |
| 11511 } | |
| 11512 function unmarkContainerAsRoot(node) { | |
| 11513 node[internalContainerInstanceKey] = null; | |
| 11514 } | |
| 11515 function isContainerMarkedAsRoot(node) { | |
| 11516 return !!node[internalContainerInstanceKey]; | |
| 11517 } // Given a DOM node, return the closest HostComponent or HostText fiber ancestor. | |
| 11518 // If the target node is part of a hydrated or not yet rendered subtree, then | |
| 11519 // this may also return a SuspenseComponent or HostRoot to indicate that. | |
| 11520 // Conceptually the HostRoot fiber is a child of the Container node. So if you | |
| 11521 // pass the Container node as the targetNode, you will not actually get the | |
| 11522 // HostRoot back. To get to the HostRoot, you need to pass a child of it. | |
| 11523 // The same thing applies to Suspense boundaries. | |
| 11524 | |
| 11525 function getClosestInstanceFromNode(targetNode) { | |
| 11526 var targetInst = targetNode[internalInstanceKey]; | |
| 11527 | |
| 11528 if (targetInst) { | |
| 11529 // Don't return HostRoot or SuspenseComponent here. | |
| 11530 return targetInst; | |
| 11531 } // If the direct event target isn't a React owned DOM node, we need to look | |
| 11532 // to see if one of its parents is a React owned DOM node. | |
| 11533 | |
| 11534 | |
| 11535 var parentNode = targetNode.parentNode; | |
| 11536 | |
| 11537 while (parentNode) { | |
| 11538 // We'll check if this is a container root that could include | |
| 11539 // React nodes in the future. We need to check this first because | |
| 11540 // if we're a child of a dehydrated container, we need to first | |
| 11541 // find that inner container before moving on to finding the parent | |
| 11542 // instance. Note that we don't check this field on the targetNode | |
| 11543 // itself because the fibers are conceptually between the container | |
| 11544 // node and the first child. It isn't surrounding the container node. | |
| 11545 // If it's not a container, we check if it's an instance. | |
| 11546 targetInst = parentNode[internalContainerInstanceKey] || parentNode[internalInstanceKey]; | |
| 11547 | |
| 11548 if (targetInst) { | |
| 11549 // Since this wasn't the direct target of the event, we might have | |
| 11550 // stepped past dehydrated DOM nodes to get here. However they could | |
| 11551 // also have been non-React nodes. We need to answer which one. | |
| 11552 // If we the instance doesn't have any children, then there can't be | |
| 11553 // a nested suspense boundary within it. So we can use this as a fast | |
| 11554 // bailout. Most of the time, when people add non-React children to | |
| 11555 // the tree, it is using a ref to a child-less DOM node. | |
| 11556 // Normally we'd only need to check one of the fibers because if it | |
| 11557 // has ever gone from having children to deleting them or vice versa | |
| 11558 // it would have deleted the dehydrated boundary nested inside already. | |
| 11559 // However, since the HostRoot starts out with an alternate it might | |
| 11560 // have one on the alternate so we need to check in case this was a | |
| 11561 // root. | |
| 11562 var alternate = targetInst.alternate; | |
| 11563 | |
| 11564 if (targetInst.child !== null || alternate !== null && alternate.child !== null) { | |
| 11565 // Next we need to figure out if the node that skipped past is | |
| 11566 // nested within a dehydrated boundary and if so, which one. | |
| 11567 var suspenseInstance = getParentSuspenseInstance(targetNode); | |
| 11568 | |
| 11569 while (suspenseInstance !== null) { | |
| 11570 // We found a suspense instance. That means that we haven't | |
| 11571 // hydrated it yet. Even though we leave the comments in the | |
| 11572 // DOM after hydrating, and there are boundaries in the DOM | |
| 11573 // that could already be hydrated, we wouldn't have found them | |
| 11574 // through this pass since if the target is hydrated it would | |
| 11575 // have had an internalInstanceKey on it. | |
| 11576 // Let's get the fiber associated with the SuspenseComponent | |
| 11577 // as the deepest instance. | |
| 11578 var targetSuspenseInst = suspenseInstance[internalInstanceKey]; | |
| 11579 | |
| 11580 if (targetSuspenseInst) { | |
| 11581 return targetSuspenseInst; | |
| 11582 } // If we don't find a Fiber on the comment, it might be because | |
| 11583 // we haven't gotten to hydrate it yet. There might still be a | |
| 11584 // parent boundary that hasn't above this one so we need to find | |
| 11585 // the outer most that is known. | |
| 11586 | |
| 11587 | |
| 11588 suspenseInstance = getParentSuspenseInstance(suspenseInstance); // If we don't find one, then that should mean that the parent | |
| 11589 // host component also hasn't hydrated yet. We can return it | |
| 11590 // below since it will bail out on the isMounted check later. | |
| 11591 } | |
| 11592 } | |
| 11593 | |
| 11594 return targetInst; | |
| 11595 } | |
| 11596 | |
| 11597 targetNode = parentNode; | |
| 11598 parentNode = targetNode.parentNode; | |
| 11599 } | |
| 11600 | |
| 11601 return null; | |
| 11602 } | |
| 11603 /** | |
| 11604 * Given a DOM node, return the ReactDOMComponent or ReactDOMTextComponent | |
| 11605 * instance, or null if the node was not rendered by this React. | |
| 11606 */ | |
| 11607 | |
| 11608 function getInstanceFromNode(node) { | |
| 11609 var inst = node[internalInstanceKey] || node[internalContainerInstanceKey]; | |
| 11610 | |
| 11611 if (inst) { | |
| 11612 if (inst.tag === HostComponent || inst.tag === HostText || inst.tag === SuspenseComponent || inst.tag === HostRoot) { | |
| 11613 return inst; | |
| 11614 } else { | |
| 11615 return null; | |
| 11616 } | |
| 11617 } | |
| 11618 | |
| 11619 return null; | |
| 11620 } | |
| 11621 /** | |
| 11622 * Given a ReactDOMComponent or ReactDOMTextComponent, return the corresponding | |
| 11623 * DOM node. | |
| 11624 */ | |
| 11625 | |
| 11626 function getNodeFromInstance(inst) { | |
| 11627 if (inst.tag === HostComponent || inst.tag === HostText) { | |
| 11628 // In Fiber this, is just the state node right now. We assume it will be | |
| 11629 // a host component or host text. | |
| 11630 return inst.stateNode; | |
| 11631 } // Without this first invariant, passing a non-DOM-component triggers the next | |
| 11632 // invariant for a missing parent, which is super confusing. | |
| 11633 | |
| 11634 | |
| 11635 throw new Error('getNodeFromInstance: Invalid argument.'); | |
| 11636 } | |
| 11637 function getFiberCurrentPropsFromNode(node) { | |
| 11638 return node[internalPropsKey] || null; | |
| 11639 } | |
| 11640 function updateFiberProps(node, props) { | |
| 11641 node[internalPropsKey] = props; | |
| 11642 } | |
| 11643 function getEventListenerSet(node) { | |
| 11644 var elementListenerSet = node[internalEventHandlersKey]; | |
| 11645 | |
| 11646 if (elementListenerSet === undefined) { | |
| 11647 elementListenerSet = node[internalEventHandlersKey] = new Set(); | |
| 11648 } | |
| 11649 | |
| 11650 return elementListenerSet; | |
| 11651 } | |
| 11652 | |
| 11653 var loggedTypeFailures = {}; | |
| 11654 var ReactDebugCurrentFrame$1 = ReactSharedInternals.ReactDebugCurrentFrame; | |
| 11655 | |
| 11656 function setCurrentlyValidatingElement(element) { | |
| 11657 { | |
| 11658 if (element) { | |
| 11659 var owner = element._owner; | |
| 11660 var stack = describeUnknownElementTypeFrameInDEV(element.type, element._source, owner ? owner.type : null); | |
| 11661 ReactDebugCurrentFrame$1.setExtraStackFrame(stack); | |
| 11662 } else { | |
| 11663 ReactDebugCurrentFrame$1.setExtraStackFrame(null); | |
| 11664 } | |
| 11665 } | |
| 11666 } | |
| 11667 | |
| 11668 function checkPropTypes(typeSpecs, values, location, componentName, element) { | |
| 11669 { | |
| 11670 // $FlowFixMe This is okay but Flow doesn't know it. | |
| 11671 var has = Function.call.bind(hasOwnProperty); | |
| 11672 | |
| 11673 for (var typeSpecName in typeSpecs) { | |
| 11674 if (has(typeSpecs, typeSpecName)) { | |
| 11675 var error$1 = void 0; // Prop type validation may throw. In case they do, we don't want to | |
| 11676 // fail the render phase where it didn't fail before. So we log it. | |
| 11677 // After these have been cleaned up, we'll let them throw. | |
| 11678 | |
| 11679 try { | |
| 11680 // This is intentionally an invariant that gets caught. It's the same | |
| 11681 // behavior as without this statement except with a better message. | |
| 11682 if (typeof typeSpecs[typeSpecName] !== 'function') { | |
| 11683 // eslint-disable-next-line react-internal/prod-error-codes | |
| 11684 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`.'); | |
| 11685 err.name = 'Invariant Violation'; | |
| 11686 throw err; | |
| 11687 } | |
| 11688 | |
| 11689 error$1 = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, 'SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED'); | |
| 11690 } catch (ex) { | |
| 11691 error$1 = ex; | |
| 11692 } | |
| 11693 | |
| 11694 if (error$1 && !(error$1 instanceof Error)) { | |
| 11695 setCurrentlyValidatingElement(element); | |
| 11696 | |
| 11697 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); | |
| 11698 | |
| 11699 setCurrentlyValidatingElement(null); | |
| 11700 } | |
| 11701 | |
| 11702 if (error$1 instanceof Error && !(error$1.message in loggedTypeFailures)) { | |
| 11703 // Only monitor this failure once because there tends to be a lot of the | |
| 11704 // same error. | |
| 11705 loggedTypeFailures[error$1.message] = true; | |
| 11706 setCurrentlyValidatingElement(element); | |
| 11707 | |
| 11708 error('Failed %s type: %s', location, error$1.message); | |
| 11709 | |
| 11710 setCurrentlyValidatingElement(null); | |
| 11711 } | |
| 11712 } | |
| 11713 } | |
| 11714 } | |
| 11715 } | |
| 11716 | |
| 11717 var valueStack = []; | |
| 11718 var fiberStack; | |
| 11719 | |
| 11720 { | |
| 11721 fiberStack = []; | |
| 11722 } | |
| 11723 | |
| 11724 var index = -1; | |
| 11725 | |
| 11726 function createCursor(defaultValue) { | |
| 11727 return { | |
| 11728 current: defaultValue | |
| 11729 }; | |
| 11730 } | |
| 11731 | |
| 11732 function pop(cursor, fiber) { | |
| 11733 if (index < 0) { | |
| 11734 { | |
| 11735 error('Unexpected pop.'); | |
| 11736 } | |
| 11737 | |
| 11738 return; | |
| 11739 } | |
| 11740 | |
| 11741 { | |
| 11742 if (fiber !== fiberStack[index]) { | |
| 11743 error('Unexpected Fiber popped.'); | |
| 11744 } | |
| 11745 } | |
| 11746 | |
| 11747 cursor.current = valueStack[index]; | |
| 11748 valueStack[index] = null; | |
| 11749 | |
| 11750 { | |
| 11751 fiberStack[index] = null; | |
| 11752 } | |
| 11753 | |
| 11754 index--; | |
| 11755 } | |
| 11756 | |
| 11757 function push(cursor, value, fiber) { | |
| 11758 index++; | |
| 11759 valueStack[index] = cursor.current; | |
| 11760 | |
| 11761 { | |
| 11762 fiberStack[index] = fiber; | |
| 11763 } | |
| 11764 | |
| 11765 cursor.current = value; | |
| 11766 } | |
| 11767 | |
| 11768 var warnedAboutMissingGetChildContext; | |
| 11769 | |
| 11770 { | |
| 11771 warnedAboutMissingGetChildContext = {}; | |
| 11772 } | |
| 11773 | |
| 11774 var emptyContextObject = {}; | |
| 11775 | |
| 11776 { | |
| 11777 Object.freeze(emptyContextObject); | |
| 11778 } // A cursor to the current merged context object on the stack. | |
| 11779 | |
| 11780 | |
| 11781 var contextStackCursor = createCursor(emptyContextObject); // A cursor to a boolean indicating whether the context has changed. | |
| 11782 | |
| 11783 var didPerformWorkStackCursor = createCursor(false); // Keep track of the previous context object that was on the stack. | |
| 11784 // We use this to get access to the parent context after we have already | |
| 11785 // pushed the next context provider, and now need to merge their contexts. | |
| 11786 | |
| 11787 var previousContext = emptyContextObject; | |
| 11788 | |
| 11789 function getUnmaskedContext(workInProgress, Component, didPushOwnContextIfProvider) { | |
| 11790 { | |
| 11791 if (didPushOwnContextIfProvider && isContextProvider(Component)) { | |
| 11792 // If the fiber is a context provider itself, when we read its context | |
| 11793 // we may have already pushed its own child context on the stack. A context | |
| 11794 // provider should not "see" its own child context. Therefore we read the | |
| 11795 // previous (parent) context instead for a context provider. | |
| 11796 return previousContext; | |
| 11797 } | |
| 11798 | |
| 11799 return contextStackCursor.current; | |
| 11800 } | |
| 11801 } | |
| 11802 | |
| 11803 function cacheContext(workInProgress, unmaskedContext, maskedContext) { | |
| 11804 { | |
| 11805 var instance = workInProgress.stateNode; | |
| 11806 instance.__reactInternalMemoizedUnmaskedChildContext = unmaskedContext; | |
| 11807 instance.__reactInternalMemoizedMaskedChildContext = maskedContext; | |
| 11808 } | |
| 11809 } | |
| 11810 | |
| 11811 function getMaskedContext(workInProgress, unmaskedContext) { | |
| 11812 { | |
| 11813 var type = workInProgress.type; | |
| 11814 var contextTypes = type.contextTypes; | |
| 11815 | |
| 11816 if (!contextTypes) { | |
| 11817 return emptyContextObject; | |
| 11818 } // Avoid recreating masked context unless unmasked context has changed. | |
| 11819 // Failing to do this will result in unnecessary calls to componentWillReceiveProps. | |
| 11820 // This may trigger infinite loops if componentWillReceiveProps calls setState. | |
| 11821 | |
| 11822 | |
| 11823 var instance = workInProgress.stateNode; | |
| 11824 | |
| 11825 if (instance && instance.__reactInternalMemoizedUnmaskedChildContext === unmaskedContext) { | |
| 11826 return instance.__reactInternalMemoizedMaskedChildContext; | |
| 11827 } | |
| 11828 | |
| 11829 var context = {}; | |
| 11830 | |
| 11831 for (var key in contextTypes) { | |
| 11832 context[key] = unmaskedContext[key]; | |
| 11833 } | |
| 11834 | |
| 11835 { | |
| 11836 var name = getComponentNameFromFiber(workInProgress) || 'Unknown'; | |
| 11837 checkPropTypes(contextTypes, context, 'context', name); | |
| 11838 } // Cache unmasked context so we can avoid recreating masked context unless necessary. | |
| 11839 // Context is created before the class component is instantiated so check for instance. | |
| 11840 | |
| 11841 | |
| 11842 if (instance) { | |
| 11843 cacheContext(workInProgress, unmaskedContext, context); | |
| 11844 } | |
| 11845 | |
| 11846 return context; | |
| 11847 } | |
| 11848 } | |
| 11849 | |
| 11850 function hasContextChanged() { | |
| 11851 { | |
| 11852 return didPerformWorkStackCursor.current; | |
| 11853 } | |
| 11854 } | |
| 11855 | |
| 11856 function isContextProvider(type) { | |
| 11857 { | |
| 11858 var childContextTypes = type.childContextTypes; | |
| 11859 return childContextTypes !== null && childContextTypes !== undefined; | |
| 11860 } | |
| 11861 } | |
| 11862 | |
| 11863 function popContext(fiber) { | |
| 11864 { | |
| 11865 pop(didPerformWorkStackCursor, fiber); | |
| 11866 pop(contextStackCursor, fiber); | |
| 11867 } | |
| 11868 } | |
| 11869 | |
| 11870 function popTopLevelContextObject(fiber) { | |
| 11871 { | |
| 11872 pop(didPerformWorkStackCursor, fiber); | |
| 11873 pop(contextStackCursor, fiber); | |
| 11874 } | |
| 11875 } | |
| 11876 | |
| 11877 function pushTopLevelContextObject(fiber, context, didChange) { | |
| 11878 { | |
| 11879 if (contextStackCursor.current !== emptyContextObject) { | |
| 11880 throw new Error('Unexpected context found on stack. ' + 'This error is likely caused by a bug in React. Please file an issue.'); | |
| 11881 } | |
| 11882 | |
| 11883 push(contextStackCursor, context, fiber); | |
| 11884 push(didPerformWorkStackCursor, didChange, fiber); | |
| 11885 } | |
| 11886 } | |
| 11887 | |
| 11888 function processChildContext(fiber, type, parentContext) { | |
| 11889 { | |
| 11890 var instance = fiber.stateNode; | |
| 11891 var childContextTypes = type.childContextTypes; // TODO (bvaughn) Replace this behavior with an invariant() in the future. | |
| 11892 // It has only been added in Fiber to match the (unintentional) behavior in Stack. | |
| 11893 | |
| 11894 if (typeof instance.getChildContext !== 'function') { | |
| 11895 { | |
| 11896 var componentName = getComponentNameFromFiber(fiber) || 'Unknown'; | |
| 11897 | |
| 11898 if (!warnedAboutMissingGetChildContext[componentName]) { | |
| 11899 warnedAboutMissingGetChildContext[componentName] = true; | |
| 11900 | |
| 11901 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); | |
| 11902 } | |
| 11903 } | |
| 11904 | |
| 11905 return parentContext; | |
| 11906 } | |
| 11907 | |
| 11908 var childContext = instance.getChildContext(); | |
| 11909 | |
| 11910 for (var contextKey in childContext) { | |
| 11911 if (!(contextKey in childContextTypes)) { | |
| 11912 throw new Error((getComponentNameFromFiber(fiber) || 'Unknown') + ".getChildContext(): key \"" + contextKey + "\" is not defined in childContextTypes."); | |
| 11913 } | |
| 11914 } | |
| 11915 | |
| 11916 { | |
| 11917 var name = getComponentNameFromFiber(fiber) || 'Unknown'; | |
| 11918 checkPropTypes(childContextTypes, childContext, 'child context', name); | |
| 11919 } | |
| 11920 | |
| 11921 return assign({}, parentContext, childContext); | |
| 11922 } | |
| 11923 } | |
| 11924 | |
| 11925 function pushContextProvider(workInProgress) { | |
| 11926 { | |
| 11927 var instance = workInProgress.stateNode; // We push the context as early as possible to ensure stack integrity. | |
| 11928 // If the instance does not exist yet, we will push null at first, | |
| 11929 // and replace it on the stack later when invalidating the context. | |
| 11930 | |
| 11931 var memoizedMergedChildContext = instance && instance.__reactInternalMemoizedMergedChildContext || emptyContextObject; // Remember the parent context so we can merge with it later. | |
| 11932 // Inherit the parent's did-perform-work value to avoid inadvertently blocking updates. | |
| 11933 | |
| 11934 previousContext = contextStackCursor.current; | |
| 11935 push(contextStackCursor, memoizedMergedChildContext, workInProgress); | |
| 11936 push(didPerformWorkStackCursor, didPerformWorkStackCursor.current, workInProgress); | |
| 11937 return true; | |
| 11938 } | |
| 11939 } | |
| 11940 | |
| 11941 function invalidateContextProvider(workInProgress, type, didChange) { | |
| 11942 { | |
| 11943 var instance = workInProgress.stateNode; | |
| 11944 | |
| 11945 if (!instance) { | |
| 11946 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.'); | |
| 11947 } | |
| 11948 | |
| 11949 if (didChange) { | |
| 11950 // Merge parent and own context. | |
| 11951 // Skip this if we're not updating due to sCU. | |
| 11952 // This avoids unnecessarily recomputing memoized values. | |
| 11953 var mergedContext = processChildContext(workInProgress, type, previousContext); | |
| 11954 instance.__reactInternalMemoizedMergedChildContext = mergedContext; // Replace the old (or empty) context with the new one. | |
| 11955 // It is important to unwind the context in the reverse order. | |
| 11956 | |
| 11957 pop(didPerformWorkStackCursor, workInProgress); | |
| 11958 pop(contextStackCursor, workInProgress); // Now push the new context and mark that it has changed. | |
| 11959 | |
| 11960 push(contextStackCursor, mergedContext, workInProgress); | |
| 11961 push(didPerformWorkStackCursor, didChange, workInProgress); | |
| 11962 } else { | |
| 11963 pop(didPerformWorkStackCursor, workInProgress); | |
| 11964 push(didPerformWorkStackCursor, didChange, workInProgress); | |
| 11965 } | |
| 11966 } | |
| 11967 } | |
| 11968 | |
| 11969 function findCurrentUnmaskedContext(fiber) { | |
| 11970 { | |
| 11971 // Currently this is only used with renderSubtreeIntoContainer; not sure if it | |
| 11972 // makes sense elsewhere | |
| 11973 if (!isFiberMounted(fiber) || fiber.tag !== ClassComponent) { | |
| 11974 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.'); | |
| 11975 } | |
| 11976 | |
| 11977 var node = fiber; | |
| 11978 | |
| 11979 do { | |
| 11980 switch (node.tag) { | |
| 11981 case HostRoot: | |
| 11982 return node.stateNode.context; | |
| 11983 | |
| 11984 case ClassComponent: | |
| 11985 { | |
| 11986 var Component = node.type; | |
| 11987 | |
| 11988 if (isContextProvider(Component)) { | |
| 11989 return node.stateNode.__reactInternalMemoizedMergedChildContext; | |
| 11990 } | |
| 11991 | |
| 11992 break; | |
| 11993 } | |
| 11994 } | |
| 11995 | |
| 11996 node = node.return; | |
| 11997 } while (node !== null); | |
| 11998 | |
| 11999 throw new Error('Found unexpected detached subtree parent. ' + 'This error is likely caused by a bug in React. Please file an issue.'); | |
| 12000 } | |
| 12001 } | |
| 12002 | |
| 12003 var LegacyRoot = 0; | |
| 12004 var ConcurrentRoot = 1; | |
| 12005 | |
| 12006 var syncQueue = null; | |
| 12007 var includesLegacySyncCallbacks = false; | |
| 12008 var isFlushingSyncQueue = false; | |
| 12009 function scheduleSyncCallback(callback) { | |
| 12010 // Push this callback into an internal queue. We'll flush these either in | |
| 12011 // the next tick, or earlier if something calls `flushSyncCallbackQueue`. | |
| 12012 if (syncQueue === null) { | |
| 12013 syncQueue = [callback]; | |
| 12014 } else { | |
| 12015 // Push onto existing queue. Don't need to schedule a callback because | |
| 12016 // we already scheduled one when we created the queue. | |
| 12017 syncQueue.push(callback); | |
| 12018 } | |
| 12019 } | |
| 12020 function scheduleLegacySyncCallback(callback) { | |
| 12021 includesLegacySyncCallbacks = true; | |
| 12022 scheduleSyncCallback(callback); | |
| 12023 } | |
| 12024 function flushSyncCallbacksOnlyInLegacyMode() { | |
| 12025 // Only flushes the queue if there's a legacy sync callback scheduled. | |
| 12026 // TODO: There's only a single type of callback: performSyncOnWorkOnRoot. So | |
| 12027 // it might make more sense for the queue to be a list of roots instead of a | |
| 12028 // list of generic callbacks. Then we can have two: one for legacy roots, one | |
| 12029 // for concurrent roots. And this method would only flush the legacy ones. | |
| 12030 if (includesLegacySyncCallbacks) { | |
| 12031 flushSyncCallbacks(); | |
| 12032 } | |
| 12033 } | |
| 12034 function flushSyncCallbacks() { | |
| 12035 if (!isFlushingSyncQueue && syncQueue !== null) { | |
| 12036 // Prevent re-entrance. | |
| 12037 isFlushingSyncQueue = true; | |
| 12038 var i = 0; | |
| 12039 var previousUpdatePriority = getCurrentUpdatePriority(); | |
| 12040 | |
| 12041 try { | |
| 12042 var isSync = true; | |
| 12043 var queue = syncQueue; // TODO: Is this necessary anymore? The only user code that runs in this | |
| 12044 // queue is in the render or commit phases. | |
| 12045 | |
| 12046 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 12047 | |
| 12048 for (; i < queue.length; i++) { | |
| 12049 var callback = queue[i]; | |
| 12050 | |
| 12051 do { | |
| 12052 callback = callback(isSync); | |
| 12053 } while (callback !== null); | |
| 12054 } | |
| 12055 | |
| 12056 syncQueue = null; | |
| 12057 includesLegacySyncCallbacks = false; | |
| 12058 } catch (error) { | |
| 12059 // If something throws, leave the remaining callbacks on the queue. | |
| 12060 if (syncQueue !== null) { | |
| 12061 syncQueue = syncQueue.slice(i + 1); | |
| 12062 } // Resume flushing in the next tick | |
| 12063 | |
| 12064 | |
| 12065 scheduleCallback(ImmediatePriority, flushSyncCallbacks); | |
| 12066 throw error; | |
| 12067 } finally { | |
| 12068 setCurrentUpdatePriority(previousUpdatePriority); | |
| 12069 isFlushingSyncQueue = false; | |
| 12070 } | |
| 12071 } | |
| 12072 | |
| 12073 return null; | |
| 12074 } | |
| 12075 | |
| 12076 // TODO: Use the unified fiber stack module instead of this local one? | |
| 12077 // Intentionally not using it yet to derisk the initial implementation, because | |
| 12078 // the way we push/pop these values is a bit unusual. If there's a mistake, I'd | |
| 12079 // rather the ids be wrong than crash the whole reconciler. | |
| 12080 var forkStack = []; | |
| 12081 var forkStackIndex = 0; | |
| 12082 var treeForkProvider = null; | |
| 12083 var treeForkCount = 0; | |
| 12084 var idStack = []; | |
| 12085 var idStackIndex = 0; | |
| 12086 var treeContextProvider = null; | |
| 12087 var treeContextId = 1; | |
| 12088 var treeContextOverflow = ''; | |
| 12089 function isForkedChild(workInProgress) { | |
| 12090 warnIfNotHydrating(); | |
| 12091 return (workInProgress.flags & Forked) !== NoFlags; | |
| 12092 } | |
| 12093 function getForksAtLevel(workInProgress) { | |
| 12094 warnIfNotHydrating(); | |
| 12095 return treeForkCount; | |
| 12096 } | |
| 12097 function getTreeId() { | |
| 12098 var overflow = treeContextOverflow; | |
| 12099 var idWithLeadingBit = treeContextId; | |
| 12100 var id = idWithLeadingBit & ~getLeadingBit(idWithLeadingBit); | |
| 12101 return id.toString(32) + overflow; | |
| 12102 } | |
| 12103 function pushTreeFork(workInProgress, totalChildren) { | |
| 12104 // This is called right after we reconcile an array (or iterator) of child | |
| 12105 // fibers, because that's the only place where we know how many children in | |
| 12106 // the whole set without doing extra work later, or storing addtional | |
| 12107 // information on the fiber. | |
| 12108 // | |
| 12109 // That's why this function is separate from pushTreeId — it's called during | |
| 12110 // the render phase of the fork parent, not the child, which is where we push | |
| 12111 // the other context values. | |
| 12112 // | |
| 12113 // In the Fizz implementation this is much simpler because the child is | |
| 12114 // rendered in the same callstack as the parent. | |
| 12115 // | |
| 12116 // It might be better to just add a `forks` field to the Fiber type. It would | |
| 12117 // make this module simpler. | |
| 12118 warnIfNotHydrating(); | |
| 12119 forkStack[forkStackIndex++] = treeForkCount; | |
| 12120 forkStack[forkStackIndex++] = treeForkProvider; | |
| 12121 treeForkProvider = workInProgress; | |
| 12122 treeForkCount = totalChildren; | |
| 12123 } | |
| 12124 function pushTreeId(workInProgress, totalChildren, index) { | |
| 12125 warnIfNotHydrating(); | |
| 12126 idStack[idStackIndex++] = treeContextId; | |
| 12127 idStack[idStackIndex++] = treeContextOverflow; | |
| 12128 idStack[idStackIndex++] = treeContextProvider; | |
| 12129 treeContextProvider = workInProgress; | |
| 12130 var baseIdWithLeadingBit = treeContextId; | |
| 12131 var baseOverflow = treeContextOverflow; // The leftmost 1 marks the end of the sequence, non-inclusive. It's not part | |
| 12132 // of the id; we use it to account for leading 0s. | |
| 12133 | |
| 12134 var baseLength = getBitLength(baseIdWithLeadingBit) - 1; | |
| 12135 var baseId = baseIdWithLeadingBit & ~(1 << baseLength); | |
| 12136 var slot = index + 1; | |
| 12137 var length = getBitLength(totalChildren) + baseLength; // 30 is the max length we can store without overflowing, taking into | |
| 12138 // consideration the leading 1 we use to mark the end of the sequence. | |
| 12139 | |
| 12140 if (length > 30) { | |
| 12141 // We overflowed the bitwise-safe range. Fall back to slower algorithm. | |
| 12142 // This branch assumes the length of the base id is greater than 5; it won't | |
| 12143 // work for smaller ids, because you need 5 bits per character. | |
| 12144 // | |
| 12145 // We encode the id in multiple steps: first the base id, then the | |
| 12146 // remaining digits. | |
| 12147 // | |
| 12148 // Each 5 bit sequence corresponds to a single base 32 character. So for | |
| 12149 // example, if the current id is 23 bits long, we can convert 20 of those | |
| 12150 // bits into a string of 4 characters, with 3 bits left over. | |
| 12151 // | |
| 12152 // First calculate how many bits in the base id represent a complete | |
| 12153 // sequence of characters. | |
| 12154 var numberOfOverflowBits = baseLength - baseLength % 5; // Then create a bitmask that selects only those bits. | |
| 12155 | |
| 12156 var newOverflowBits = (1 << numberOfOverflowBits) - 1; // Select the bits, and convert them to a base 32 string. | |
| 12157 | |
| 12158 var newOverflow = (baseId & newOverflowBits).toString(32); // Now we can remove those bits from the base id. | |
| 12159 | |
| 12160 var restOfBaseId = baseId >> numberOfOverflowBits; | |
| 12161 var restOfBaseLength = baseLength - numberOfOverflowBits; // Finally, encode the rest of the bits using the normal algorithm. Because | |
| 12162 // we made more room, this time it won't overflow. | |
| 12163 | |
| 12164 var restOfLength = getBitLength(totalChildren) + restOfBaseLength; | |
| 12165 var restOfNewBits = slot << restOfBaseLength; | |
| 12166 var id = restOfNewBits | restOfBaseId; | |
| 12167 var overflow = newOverflow + baseOverflow; | |
| 12168 treeContextId = 1 << restOfLength | id; | |
| 12169 treeContextOverflow = overflow; | |
| 12170 } else { | |
| 12171 // Normal path | |
| 12172 var newBits = slot << baseLength; | |
| 12173 | |
| 12174 var _id = newBits | baseId; | |
| 12175 | |
| 12176 var _overflow = baseOverflow; | |
| 12177 treeContextId = 1 << length | _id; | |
| 12178 treeContextOverflow = _overflow; | |
| 12179 } | |
| 12180 } | |
| 12181 function pushMaterializedTreeId(workInProgress) { | |
| 12182 warnIfNotHydrating(); // This component materialized an id. This will affect any ids that appear | |
| 12183 // in its children. | |
| 12184 | |
| 12185 var returnFiber = workInProgress.return; | |
| 12186 | |
| 12187 if (returnFiber !== null) { | |
| 12188 var numberOfForks = 1; | |
| 12189 var slotIndex = 0; | |
| 12190 pushTreeFork(workInProgress, numberOfForks); | |
| 12191 pushTreeId(workInProgress, numberOfForks, slotIndex); | |
| 12192 } | |
| 12193 } | |
| 12194 | |
| 12195 function getBitLength(number) { | |
| 12196 return 32 - clz32(number); | |
| 12197 } | |
| 12198 | |
| 12199 function getLeadingBit(id) { | |
| 12200 return 1 << getBitLength(id) - 1; | |
| 12201 } | |
| 12202 | |
| 12203 function popTreeContext(workInProgress) { | |
| 12204 // Restore the previous values. | |
| 12205 // This is a bit more complicated than other context-like modules in Fiber | |
| 12206 // because the same Fiber may appear on the stack multiple times and for | |
| 12207 // different reasons. We have to keep popping until the work-in-progress is | |
| 12208 // no longer at the top of the stack. | |
| 12209 while (workInProgress === treeForkProvider) { | |
| 12210 treeForkProvider = forkStack[--forkStackIndex]; | |
| 12211 forkStack[forkStackIndex] = null; | |
| 12212 treeForkCount = forkStack[--forkStackIndex]; | |
| 12213 forkStack[forkStackIndex] = null; | |
| 12214 } | |
| 12215 | |
| 12216 while (workInProgress === treeContextProvider) { | |
| 12217 treeContextProvider = idStack[--idStackIndex]; | |
| 12218 idStack[idStackIndex] = null; | |
| 12219 treeContextOverflow = idStack[--idStackIndex]; | |
| 12220 idStack[idStackIndex] = null; | |
| 12221 treeContextId = idStack[--idStackIndex]; | |
| 12222 idStack[idStackIndex] = null; | |
| 12223 } | |
| 12224 } | |
| 12225 function getSuspendedTreeContext() { | |
| 12226 warnIfNotHydrating(); | |
| 12227 | |
| 12228 if (treeContextProvider !== null) { | |
| 12229 return { | |
| 12230 id: treeContextId, | |
| 12231 overflow: treeContextOverflow | |
| 12232 }; | |
| 12233 } else { | |
| 12234 return null; | |
| 12235 } | |
| 12236 } | |
| 12237 function restoreSuspendedTreeContext(workInProgress, suspendedContext) { | |
| 12238 warnIfNotHydrating(); | |
| 12239 idStack[idStackIndex++] = treeContextId; | |
| 12240 idStack[idStackIndex++] = treeContextOverflow; | |
| 12241 idStack[idStackIndex++] = treeContextProvider; | |
| 12242 treeContextId = suspendedContext.id; | |
| 12243 treeContextOverflow = suspendedContext.overflow; | |
| 12244 treeContextProvider = workInProgress; | |
| 12245 } | |
| 12246 | |
| 12247 function warnIfNotHydrating() { | |
| 12248 { | |
| 12249 if (!getIsHydrating()) { | |
| 12250 error('Expected to be hydrating. This is a bug in React. Please file ' + 'an issue.'); | |
| 12251 } | |
| 12252 } | |
| 12253 } | |
| 12254 | |
| 12255 // This may have been an insertion or a hydration. | |
| 12256 | |
| 12257 var hydrationParentFiber = null; | |
| 12258 var nextHydratableInstance = null; | |
| 12259 var isHydrating = false; // This flag allows for warning supression when we expect there to be mismatches | |
| 12260 // due to earlier mismatches or a suspended fiber. | |
| 12261 | |
| 12262 var didSuspendOrErrorDEV = false; // Hydration errors that were thrown inside this boundary | |
| 12263 | |
| 12264 var hydrationErrors = null; | |
| 12265 | |
| 12266 function warnIfHydrating() { | |
| 12267 { | |
| 12268 if (isHydrating) { | |
| 12269 error('We should not be hydrating here. This is a bug in React. Please file a bug.'); | |
| 12270 } | |
| 12271 } | |
| 12272 } | |
| 12273 | |
| 12274 function markDidThrowWhileHydratingDEV() { | |
| 12275 { | |
| 12276 didSuspendOrErrorDEV = true; | |
| 12277 } | |
| 12278 } | |
| 12279 function didSuspendOrErrorWhileHydratingDEV() { | |
| 12280 { | |
| 12281 return didSuspendOrErrorDEV; | |
| 12282 } | |
| 12283 } | |
| 12284 | |
| 12285 function enterHydrationState(fiber) { | |
| 12286 | |
| 12287 var parentInstance = fiber.stateNode.containerInfo; | |
| 12288 nextHydratableInstance = getFirstHydratableChildWithinContainer(parentInstance); | |
| 12289 hydrationParentFiber = fiber; | |
| 12290 isHydrating = true; | |
| 12291 hydrationErrors = null; | |
| 12292 didSuspendOrErrorDEV = false; | |
| 12293 return true; | |
| 12294 } | |
| 12295 | |
| 12296 function reenterHydrationStateFromDehydratedSuspenseInstance(fiber, suspenseInstance, treeContext) { | |
| 12297 | |
| 12298 nextHydratableInstance = getFirstHydratableChildWithinSuspenseInstance(suspenseInstance); | |
| 12299 hydrationParentFiber = fiber; | |
| 12300 isHydrating = true; | |
| 12301 hydrationErrors = null; | |
| 12302 didSuspendOrErrorDEV = false; | |
| 12303 | |
| 12304 if (treeContext !== null) { | |
| 12305 restoreSuspendedTreeContext(fiber, treeContext); | |
| 12306 } | |
| 12307 | |
| 12308 return true; | |
| 12309 } | |
| 12310 | |
| 12311 function warnUnhydratedInstance(returnFiber, instance) { | |
| 12312 { | |
| 12313 switch (returnFiber.tag) { | |
| 12314 case HostRoot: | |
| 12315 { | |
| 12316 didNotHydrateInstanceWithinContainer(returnFiber.stateNode.containerInfo, instance); | |
| 12317 break; | |
| 12318 } | |
| 12319 | |
| 12320 case HostComponent: | |
| 12321 { | |
| 12322 var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; | |
| 12323 didNotHydrateInstance(returnFiber.type, returnFiber.memoizedProps, returnFiber.stateNode, instance, // TODO: Delete this argument when we remove the legacy root API. | |
| 12324 isConcurrentMode); | |
| 12325 break; | |
| 12326 } | |
| 12327 | |
| 12328 case SuspenseComponent: | |
| 12329 { | |
| 12330 var suspenseState = returnFiber.memoizedState; | |
| 12331 if (suspenseState.dehydrated !== null) didNotHydrateInstanceWithinSuspenseInstance(suspenseState.dehydrated, instance); | |
| 12332 break; | |
| 12333 } | |
| 12334 } | |
| 12335 } | |
| 12336 } | |
| 12337 | |
| 12338 function deleteHydratableInstance(returnFiber, instance) { | |
| 12339 warnUnhydratedInstance(returnFiber, instance); | |
| 12340 var childToDelete = createFiberFromHostInstanceForDeletion(); | |
| 12341 childToDelete.stateNode = instance; | |
| 12342 childToDelete.return = returnFiber; | |
| 12343 var deletions = returnFiber.deletions; | |
| 12344 | |
| 12345 if (deletions === null) { | |
| 12346 returnFiber.deletions = [childToDelete]; | |
| 12347 returnFiber.flags |= ChildDeletion; | |
| 12348 } else { | |
| 12349 deletions.push(childToDelete); | |
| 12350 } | |
| 12351 } | |
| 12352 | |
| 12353 function warnNonhydratedInstance(returnFiber, fiber) { | |
| 12354 { | |
| 12355 if (didSuspendOrErrorDEV) { | |
| 12356 // Inside a boundary that already suspended. We're currently rendering the | |
| 12357 // siblings of a suspended node. The mismatch may be due to the missing | |
| 12358 // data, so it's probably a false positive. | |
| 12359 return; | |
| 12360 } | |
| 12361 | |
| 12362 switch (returnFiber.tag) { | |
| 12363 case HostRoot: | |
| 12364 { | |
| 12365 var parentContainer = returnFiber.stateNode.containerInfo; | |
| 12366 | |
| 12367 switch (fiber.tag) { | |
| 12368 case HostComponent: | |
| 12369 var type = fiber.type; | |
| 12370 var props = fiber.pendingProps; | |
| 12371 didNotFindHydratableInstanceWithinContainer(parentContainer, type); | |
| 12372 break; | |
| 12373 | |
| 12374 case HostText: | |
| 12375 var text = fiber.pendingProps; | |
| 12376 didNotFindHydratableTextInstanceWithinContainer(parentContainer, text); | |
| 12377 break; | |
| 12378 } | |
| 12379 | |
| 12380 break; | |
| 12381 } | |
| 12382 | |
| 12383 case HostComponent: | |
| 12384 { | |
| 12385 var parentType = returnFiber.type; | |
| 12386 var parentProps = returnFiber.memoizedProps; | |
| 12387 var parentInstance = returnFiber.stateNode; | |
| 12388 | |
| 12389 switch (fiber.tag) { | |
| 12390 case HostComponent: | |
| 12391 { | |
| 12392 var _type = fiber.type; | |
| 12393 var _props = fiber.pendingProps; | |
| 12394 var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; | |
| 12395 didNotFindHydratableInstance(parentType, parentProps, parentInstance, _type, _props, // TODO: Delete this argument when we remove the legacy root API. | |
| 12396 isConcurrentMode); | |
| 12397 break; | |
| 12398 } | |
| 12399 | |
| 12400 case HostText: | |
| 12401 { | |
| 12402 var _text = fiber.pendingProps; | |
| 12403 | |
| 12404 var _isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; | |
| 12405 | |
| 12406 didNotFindHydratableTextInstance(parentType, parentProps, parentInstance, _text, // TODO: Delete this argument when we remove the legacy root API. | |
| 12407 _isConcurrentMode); | |
| 12408 break; | |
| 12409 } | |
| 12410 } | |
| 12411 | |
| 12412 break; | |
| 12413 } | |
| 12414 | |
| 12415 case SuspenseComponent: | |
| 12416 { | |
| 12417 var suspenseState = returnFiber.memoizedState; | |
| 12418 var _parentInstance = suspenseState.dehydrated; | |
| 12419 if (_parentInstance !== null) switch (fiber.tag) { | |
| 12420 case HostComponent: | |
| 12421 var _type2 = fiber.type; | |
| 12422 var _props2 = fiber.pendingProps; | |
| 12423 didNotFindHydratableInstanceWithinSuspenseInstance(_parentInstance, _type2); | |
| 12424 break; | |
| 12425 | |
| 12426 case HostText: | |
| 12427 var _text2 = fiber.pendingProps; | |
| 12428 didNotFindHydratableTextInstanceWithinSuspenseInstance(_parentInstance, _text2); | |
| 12429 break; | |
| 12430 } | |
| 12431 break; | |
| 12432 } | |
| 12433 | |
| 12434 default: | |
| 12435 return; | |
| 12436 } | |
| 12437 } | |
| 12438 } | |
| 12439 | |
| 12440 function insertNonHydratedInstance(returnFiber, fiber) { | |
| 12441 fiber.flags = fiber.flags & ~Hydrating | Placement; | |
| 12442 warnNonhydratedInstance(returnFiber, fiber); | |
| 12443 } | |
| 12444 | |
| 12445 function tryHydrate(fiber, nextInstance) { | |
| 12446 switch (fiber.tag) { | |
| 12447 case HostComponent: | |
| 12448 { | |
| 12449 var type = fiber.type; | |
| 12450 var props = fiber.pendingProps; | |
| 12451 var instance = canHydrateInstance(nextInstance, type); | |
| 12452 | |
| 12453 if (instance !== null) { | |
| 12454 fiber.stateNode = instance; | |
| 12455 hydrationParentFiber = fiber; | |
| 12456 nextHydratableInstance = getFirstHydratableChild(instance); | |
| 12457 return true; | |
| 12458 } | |
| 12459 | |
| 12460 return false; | |
| 12461 } | |
| 12462 | |
| 12463 case HostText: | |
| 12464 { | |
| 12465 var text = fiber.pendingProps; | |
| 12466 var textInstance = canHydrateTextInstance(nextInstance, text); | |
| 12467 | |
| 12468 if (textInstance !== null) { | |
| 12469 fiber.stateNode = textInstance; | |
| 12470 hydrationParentFiber = fiber; // Text Instances don't have children so there's nothing to hydrate. | |
| 12471 | |
| 12472 nextHydratableInstance = null; | |
| 12473 return true; | |
| 12474 } | |
| 12475 | |
| 12476 return false; | |
| 12477 } | |
| 12478 | |
| 12479 case SuspenseComponent: | |
| 12480 { | |
| 12481 var suspenseInstance = canHydrateSuspenseInstance(nextInstance); | |
| 12482 | |
| 12483 if (suspenseInstance !== null) { | |
| 12484 var suspenseState = { | |
| 12485 dehydrated: suspenseInstance, | |
| 12486 treeContext: getSuspendedTreeContext(), | |
| 12487 retryLane: OffscreenLane | |
| 12488 }; | |
| 12489 fiber.memoizedState = suspenseState; // Store the dehydrated fragment as a child fiber. | |
| 12490 // This simplifies the code for getHostSibling and deleting nodes, | |
| 12491 // since it doesn't have to consider all Suspense boundaries and | |
| 12492 // check if they're dehydrated ones or not. | |
| 12493 | |
| 12494 var dehydratedFragment = createFiberFromDehydratedFragment(suspenseInstance); | |
| 12495 dehydratedFragment.return = fiber; | |
| 12496 fiber.child = dehydratedFragment; | |
| 12497 hydrationParentFiber = fiber; // While a Suspense Instance does have children, we won't step into | |
| 12498 // it during the first pass. Instead, we'll reenter it later. | |
| 12499 | |
| 12500 nextHydratableInstance = null; | |
| 12501 return true; | |
| 12502 } | |
| 12503 | |
| 12504 return false; | |
| 12505 } | |
| 12506 | |
| 12507 default: | |
| 12508 return false; | |
| 12509 } | |
| 12510 } | |
| 12511 | |
| 12512 function shouldClientRenderOnMismatch(fiber) { | |
| 12513 return (fiber.mode & ConcurrentMode) !== NoMode && (fiber.flags & DidCapture) === NoFlags; | |
| 12514 } | |
| 12515 | |
| 12516 function throwOnHydrationMismatch(fiber) { | |
| 12517 throw new Error('Hydration failed because the initial UI does not match what was ' + 'rendered on the server.'); | |
| 12518 } | |
| 12519 | |
| 12520 function tryToClaimNextHydratableInstance(fiber) { | |
| 12521 if (!isHydrating) { | |
| 12522 return; | |
| 12523 } | |
| 12524 | |
| 12525 var nextInstance = nextHydratableInstance; | |
| 12526 | |
| 12527 if (!nextInstance) { | |
| 12528 if (shouldClientRenderOnMismatch(fiber)) { | |
| 12529 warnNonhydratedInstance(hydrationParentFiber, fiber); | |
| 12530 throwOnHydrationMismatch(); | |
| 12531 } // Nothing to hydrate. Make it an insertion. | |
| 12532 | |
| 12533 | |
| 12534 insertNonHydratedInstance(hydrationParentFiber, fiber); | |
| 12535 isHydrating = false; | |
| 12536 hydrationParentFiber = fiber; | |
| 12537 return; | |
| 12538 } | |
| 12539 | |
| 12540 var firstAttemptedInstance = nextInstance; | |
| 12541 | |
| 12542 if (!tryHydrate(fiber, nextInstance)) { | |
| 12543 if (shouldClientRenderOnMismatch(fiber)) { | |
| 12544 warnNonhydratedInstance(hydrationParentFiber, fiber); | |
| 12545 throwOnHydrationMismatch(); | |
| 12546 } // If we can't hydrate this instance let's try the next one. | |
| 12547 // We use this as a heuristic. It's based on intuition and not data so it | |
| 12548 // might be flawed or unnecessary. | |
| 12549 | |
| 12550 | |
| 12551 nextInstance = getNextHydratableSibling(firstAttemptedInstance); | |
| 12552 var prevHydrationParentFiber = hydrationParentFiber; | |
| 12553 | |
| 12554 if (!nextInstance || !tryHydrate(fiber, nextInstance)) { | |
| 12555 // Nothing to hydrate. Make it an insertion. | |
| 12556 insertNonHydratedInstance(hydrationParentFiber, fiber); | |
| 12557 isHydrating = false; | |
| 12558 hydrationParentFiber = fiber; | |
| 12559 return; | |
| 12560 } // We matched the next one, we'll now assume that the first one was | |
| 12561 // superfluous and we'll delete it. Since we can't eagerly delete it | |
| 12562 // we'll have to schedule a deletion. To do that, this node needs a dummy | |
| 12563 // fiber associated with it. | |
| 12564 | |
| 12565 | |
| 12566 deleteHydratableInstance(prevHydrationParentFiber, firstAttemptedInstance); | |
| 12567 } | |
| 12568 } | |
| 12569 | |
| 12570 function prepareToHydrateHostInstance(fiber, rootContainerInstance, hostContext) { | |
| 12571 | |
| 12572 var instance = fiber.stateNode; | |
| 12573 var shouldWarnIfMismatchDev = !didSuspendOrErrorDEV; | |
| 12574 var updatePayload = hydrateInstance(instance, fiber.type, fiber.memoizedProps, rootContainerInstance, hostContext, fiber, shouldWarnIfMismatchDev); // TODO: Type this specific to this type of component. | |
| 12575 | |
| 12576 fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there | |
| 12577 // is a new ref we mark this as an update. | |
| 12578 | |
| 12579 if (updatePayload !== null) { | |
| 12580 return true; | |
| 12581 } | |
| 12582 | |
| 12583 return false; | |
| 12584 } | |
| 12585 | |
| 12586 function prepareToHydrateHostTextInstance(fiber) { | |
| 12587 | |
| 12588 var textInstance = fiber.stateNode; | |
| 12589 var textContent = fiber.memoizedProps; | |
| 12590 var shouldUpdate = hydrateTextInstance(textInstance, textContent, fiber); | |
| 12591 | |
| 12592 if (shouldUpdate) { | |
| 12593 // We assume that prepareToHydrateHostTextInstance is called in a context where the | |
| 12594 // hydration parent is the parent host component of this host text. | |
| 12595 var returnFiber = hydrationParentFiber; | |
| 12596 | |
| 12597 if (returnFiber !== null) { | |
| 12598 switch (returnFiber.tag) { | |
| 12599 case HostRoot: | |
| 12600 { | |
| 12601 var parentContainer = returnFiber.stateNode.containerInfo; | |
| 12602 var isConcurrentMode = (returnFiber.mode & ConcurrentMode) !== NoMode; | |
| 12603 didNotMatchHydratedContainerTextInstance(parentContainer, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API. | |
| 12604 isConcurrentMode); | |
| 12605 break; | |
| 12606 } | |
| 12607 | |
| 12608 case HostComponent: | |
| 12609 { | |
| 12610 var parentType = returnFiber.type; | |
| 12611 var parentProps = returnFiber.memoizedProps; | |
| 12612 var parentInstance = returnFiber.stateNode; | |
| 12613 | |
| 12614 var _isConcurrentMode2 = (returnFiber.mode & ConcurrentMode) !== NoMode; | |
| 12615 | |
| 12616 didNotMatchHydratedTextInstance(parentType, parentProps, parentInstance, textInstance, textContent, // TODO: Delete this argument when we remove the legacy root API. | |
| 12617 _isConcurrentMode2); | |
| 12618 break; | |
| 12619 } | |
| 12620 } | |
| 12621 } | |
| 12622 } | |
| 12623 | |
| 12624 return shouldUpdate; | |
| 12625 } | |
| 12626 | |
| 12627 function prepareToHydrateHostSuspenseInstance(fiber) { | |
| 12628 | |
| 12629 var suspenseState = fiber.memoizedState; | |
| 12630 var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null; | |
| 12631 | |
| 12632 if (!suspenseInstance) { | |
| 12633 throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.'); | |
| 12634 } | |
| 12635 | |
| 12636 hydrateSuspenseInstance(suspenseInstance, fiber); | |
| 12637 } | |
| 12638 | |
| 12639 function skipPastDehydratedSuspenseInstance(fiber) { | |
| 12640 | |
| 12641 var suspenseState = fiber.memoizedState; | |
| 12642 var suspenseInstance = suspenseState !== null ? suspenseState.dehydrated : null; | |
| 12643 | |
| 12644 if (!suspenseInstance) { | |
| 12645 throw new Error('Expected to have a hydrated suspense instance. ' + 'This error is likely caused by a bug in React. Please file an issue.'); | |
| 12646 } | |
| 12647 | |
| 12648 return getNextHydratableInstanceAfterSuspenseInstance(suspenseInstance); | |
| 12649 } | |
| 12650 | |
| 12651 function popToNextHostParent(fiber) { | |
| 12652 var parent = fiber.return; | |
| 12653 | |
| 12654 while (parent !== null && parent.tag !== HostComponent && parent.tag !== HostRoot && parent.tag !== SuspenseComponent) { | |
| 12655 parent = parent.return; | |
| 12656 } | |
| 12657 | |
| 12658 hydrationParentFiber = parent; | |
| 12659 } | |
| 12660 | |
| 12661 function popHydrationState(fiber) { | |
| 12662 | |
| 12663 if (fiber !== hydrationParentFiber) { | |
| 12664 // We're deeper than the current hydration context, inside an inserted | |
| 12665 // tree. | |
| 12666 return false; | |
| 12667 } | |
| 12668 | |
| 12669 if (!isHydrating) { | |
| 12670 // If we're not currently hydrating but we're in a hydration context, then | |
| 12671 // we were an insertion and now need to pop up reenter hydration of our | |
| 12672 // siblings. | |
| 12673 popToNextHostParent(fiber); | |
| 12674 isHydrating = true; | |
| 12675 return false; | |
| 12676 } // If we have any remaining hydratable nodes, we need to delete them now. | |
| 12677 // We only do this deeper than head and body since they tend to have random | |
| 12678 // other nodes in them. We also ignore components with pure text content in | |
| 12679 // side of them. We also don't delete anything inside the root container. | |
| 12680 | |
| 12681 | |
| 12682 if (fiber.tag !== HostRoot && (fiber.tag !== HostComponent || shouldDeleteUnhydratedTailInstances(fiber.type) && !shouldSetTextContent(fiber.type, fiber.memoizedProps))) { | |
| 12683 var nextInstance = nextHydratableInstance; | |
| 12684 | |
| 12685 if (nextInstance) { | |
| 12686 if (shouldClientRenderOnMismatch(fiber)) { | |
| 12687 warnIfUnhydratedTailNodes(fiber); | |
| 12688 throwOnHydrationMismatch(); | |
| 12689 } else { | |
| 12690 while (nextInstance) { | |
| 12691 deleteHydratableInstance(fiber, nextInstance); | |
| 12692 nextInstance = getNextHydratableSibling(nextInstance); | |
| 12693 } | |
| 12694 } | |
| 12695 } | |
| 12696 } | |
| 12697 | |
| 12698 popToNextHostParent(fiber); | |
| 12699 | |
| 12700 if (fiber.tag === SuspenseComponent) { | |
| 12701 nextHydratableInstance = skipPastDehydratedSuspenseInstance(fiber); | |
| 12702 } else { | |
| 12703 nextHydratableInstance = hydrationParentFiber ? getNextHydratableSibling(fiber.stateNode) : null; | |
| 12704 } | |
| 12705 | |
| 12706 return true; | |
| 12707 } | |
| 12708 | |
| 12709 function hasUnhydratedTailNodes() { | |
| 12710 return isHydrating && nextHydratableInstance !== null; | |
| 12711 } | |
| 12712 | |
| 12713 function warnIfUnhydratedTailNodes(fiber) { | |
| 12714 var nextInstance = nextHydratableInstance; | |
| 12715 | |
| 12716 while (nextInstance) { | |
| 12717 warnUnhydratedInstance(fiber, nextInstance); | |
| 12718 nextInstance = getNextHydratableSibling(nextInstance); | |
| 12719 } | |
| 12720 } | |
| 12721 | |
| 12722 function resetHydrationState() { | |
| 12723 | |
| 12724 hydrationParentFiber = null; | |
| 12725 nextHydratableInstance = null; | |
| 12726 isHydrating = false; | |
| 12727 didSuspendOrErrorDEV = false; | |
| 12728 } | |
| 12729 | |
| 12730 function upgradeHydrationErrorsToRecoverable() { | |
| 12731 if (hydrationErrors !== null) { | |
| 12732 // Successfully completed a forced client render. The errors that occurred | |
| 12733 // during the hydration attempt are now recovered. We will log them in | |
| 12734 // commit phase, once the entire tree has finished. | |
| 12735 queueRecoverableErrors(hydrationErrors); | |
| 12736 hydrationErrors = null; | |
| 12737 } | |
| 12738 } | |
| 12739 | |
| 12740 function getIsHydrating() { | |
| 12741 return isHydrating; | |
| 12742 } | |
| 12743 | |
| 12744 function queueHydrationError(error) { | |
| 12745 if (hydrationErrors === null) { | |
| 12746 hydrationErrors = [error]; | |
| 12747 } else { | |
| 12748 hydrationErrors.push(error); | |
| 12749 } | |
| 12750 } | |
| 12751 | |
| 12752 var ReactCurrentBatchConfig$1 = ReactSharedInternals.ReactCurrentBatchConfig; | |
| 12753 var NoTransition = null; | |
| 12754 function requestCurrentTransition() { | |
| 12755 return ReactCurrentBatchConfig$1.transition; | |
| 12756 } | |
| 12757 | |
| 12758 var ReactStrictModeWarnings = { | |
| 12759 recordUnsafeLifecycleWarnings: function (fiber, instance) {}, | |
| 12760 flushPendingUnsafeLifecycleWarnings: function () {}, | |
| 12761 recordLegacyContextWarning: function (fiber, instance) {}, | |
| 12762 flushLegacyContextWarning: function () {}, | |
| 12763 discardPendingWarnings: function () {} | |
| 12764 }; | |
| 12765 | |
| 12766 { | |
| 12767 var findStrictRoot = function (fiber) { | |
| 12768 var maybeStrictRoot = null; | |
| 12769 var node = fiber; | |
| 12770 | |
| 12771 while (node !== null) { | |
| 12772 if (node.mode & StrictLegacyMode) { | |
| 12773 maybeStrictRoot = node; | |
| 12774 } | |
| 12775 | |
| 12776 node = node.return; | |
| 12777 } | |
| 12778 | |
| 12779 return maybeStrictRoot; | |
| 12780 }; | |
| 12781 | |
| 12782 var setToSortedString = function (set) { | |
| 12783 var array = []; | |
| 12784 set.forEach(function (value) { | |
| 12785 array.push(value); | |
| 12786 }); | |
| 12787 return array.sort().join(', '); | |
| 12788 }; | |
| 12789 | |
| 12790 var pendingComponentWillMountWarnings = []; | |
| 12791 var pendingUNSAFE_ComponentWillMountWarnings = []; | |
| 12792 var pendingComponentWillReceivePropsWarnings = []; | |
| 12793 var pendingUNSAFE_ComponentWillReceivePropsWarnings = []; | |
| 12794 var pendingComponentWillUpdateWarnings = []; | |
| 12795 var pendingUNSAFE_ComponentWillUpdateWarnings = []; // Tracks components we have already warned about. | |
| 12796 | |
| 12797 var didWarnAboutUnsafeLifecycles = new Set(); | |
| 12798 | |
| 12799 ReactStrictModeWarnings.recordUnsafeLifecycleWarnings = function (fiber, instance) { | |
| 12800 // Dedupe strategy: Warn once per component. | |
| 12801 if (didWarnAboutUnsafeLifecycles.has(fiber.type)) { | |
| 12802 return; | |
| 12803 } | |
| 12804 | |
| 12805 if (typeof instance.componentWillMount === 'function' && // Don't warn about react-lifecycles-compat polyfilled components. | |
| 12806 instance.componentWillMount.__suppressDeprecationWarning !== true) { | |
| 12807 pendingComponentWillMountWarnings.push(fiber); | |
| 12808 } | |
| 12809 | |
| 12810 if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillMount === 'function') { | |
| 12811 pendingUNSAFE_ComponentWillMountWarnings.push(fiber); | |
| 12812 } | |
| 12813 | |
| 12814 if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) { | |
| 12815 pendingComponentWillReceivePropsWarnings.push(fiber); | |
| 12816 } | |
| 12817 | |
| 12818 if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillReceiveProps === 'function') { | |
| 12819 pendingUNSAFE_ComponentWillReceivePropsWarnings.push(fiber); | |
| 12820 } | |
| 12821 | |
| 12822 if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) { | |
| 12823 pendingComponentWillUpdateWarnings.push(fiber); | |
| 12824 } | |
| 12825 | |
| 12826 if (fiber.mode & StrictLegacyMode && typeof instance.UNSAFE_componentWillUpdate === 'function') { | |
| 12827 pendingUNSAFE_ComponentWillUpdateWarnings.push(fiber); | |
| 12828 } | |
| 12829 }; | |
| 12830 | |
| 12831 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings = function () { | |
| 12832 // We do an initial pass to gather component names | |
| 12833 var componentWillMountUniqueNames = new Set(); | |
| 12834 | |
| 12835 if (pendingComponentWillMountWarnings.length > 0) { | |
| 12836 pendingComponentWillMountWarnings.forEach(function (fiber) { | |
| 12837 componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12838 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12839 }); | |
| 12840 pendingComponentWillMountWarnings = []; | |
| 12841 } | |
| 12842 | |
| 12843 var UNSAFE_componentWillMountUniqueNames = new Set(); | |
| 12844 | |
| 12845 if (pendingUNSAFE_ComponentWillMountWarnings.length > 0) { | |
| 12846 pendingUNSAFE_ComponentWillMountWarnings.forEach(function (fiber) { | |
| 12847 UNSAFE_componentWillMountUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12848 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12849 }); | |
| 12850 pendingUNSAFE_ComponentWillMountWarnings = []; | |
| 12851 } | |
| 12852 | |
| 12853 var componentWillReceivePropsUniqueNames = new Set(); | |
| 12854 | |
| 12855 if (pendingComponentWillReceivePropsWarnings.length > 0) { | |
| 12856 pendingComponentWillReceivePropsWarnings.forEach(function (fiber) { | |
| 12857 componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12858 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12859 }); | |
| 12860 pendingComponentWillReceivePropsWarnings = []; | |
| 12861 } | |
| 12862 | |
| 12863 var UNSAFE_componentWillReceivePropsUniqueNames = new Set(); | |
| 12864 | |
| 12865 if (pendingUNSAFE_ComponentWillReceivePropsWarnings.length > 0) { | |
| 12866 pendingUNSAFE_ComponentWillReceivePropsWarnings.forEach(function (fiber) { | |
| 12867 UNSAFE_componentWillReceivePropsUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12868 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12869 }); | |
| 12870 pendingUNSAFE_ComponentWillReceivePropsWarnings = []; | |
| 12871 } | |
| 12872 | |
| 12873 var componentWillUpdateUniqueNames = new Set(); | |
| 12874 | |
| 12875 if (pendingComponentWillUpdateWarnings.length > 0) { | |
| 12876 pendingComponentWillUpdateWarnings.forEach(function (fiber) { | |
| 12877 componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12878 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12879 }); | |
| 12880 pendingComponentWillUpdateWarnings = []; | |
| 12881 } | |
| 12882 | |
| 12883 var UNSAFE_componentWillUpdateUniqueNames = new Set(); | |
| 12884 | |
| 12885 if (pendingUNSAFE_ComponentWillUpdateWarnings.length > 0) { | |
| 12886 pendingUNSAFE_ComponentWillUpdateWarnings.forEach(function (fiber) { | |
| 12887 UNSAFE_componentWillUpdateUniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12888 didWarnAboutUnsafeLifecycles.add(fiber.type); | |
| 12889 }); | |
| 12890 pendingUNSAFE_ComponentWillUpdateWarnings = []; | |
| 12891 } // Finally, we flush all the warnings | |
| 12892 // UNSAFE_ ones before the deprecated ones, since they'll be 'louder' | |
| 12893 | |
| 12894 | |
| 12895 if (UNSAFE_componentWillMountUniqueNames.size > 0) { | |
| 12896 var sortedNames = setToSortedString(UNSAFE_componentWillMountUniqueNames); | |
| 12897 | |
| 12898 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); | |
| 12899 } | |
| 12900 | |
| 12901 if (UNSAFE_componentWillReceivePropsUniqueNames.size > 0) { | |
| 12902 var _sortedNames = setToSortedString(UNSAFE_componentWillReceivePropsUniqueNames); | |
| 12903 | |
| 12904 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); | |
| 12905 } | |
| 12906 | |
| 12907 if (UNSAFE_componentWillUpdateUniqueNames.size > 0) { | |
| 12908 var _sortedNames2 = setToSortedString(UNSAFE_componentWillUpdateUniqueNames); | |
| 12909 | |
| 12910 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); | |
| 12911 } | |
| 12912 | |
| 12913 if (componentWillMountUniqueNames.size > 0) { | |
| 12914 var _sortedNames3 = setToSortedString(componentWillMountUniqueNames); | |
| 12915 | |
| 12916 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); | |
| 12917 } | |
| 12918 | |
| 12919 if (componentWillReceivePropsUniqueNames.size > 0) { | |
| 12920 var _sortedNames4 = setToSortedString(componentWillReceivePropsUniqueNames); | |
| 12921 | |
| 12922 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); | |
| 12923 } | |
| 12924 | |
| 12925 if (componentWillUpdateUniqueNames.size > 0) { | |
| 12926 var _sortedNames5 = setToSortedString(componentWillUpdateUniqueNames); | |
| 12927 | |
| 12928 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); | |
| 12929 } | |
| 12930 }; | |
| 12931 | |
| 12932 var pendingLegacyContextWarning = new Map(); // Tracks components we have already warned about. | |
| 12933 | |
| 12934 var didWarnAboutLegacyContext = new Set(); | |
| 12935 | |
| 12936 ReactStrictModeWarnings.recordLegacyContextWarning = function (fiber, instance) { | |
| 12937 var strictRoot = findStrictRoot(fiber); | |
| 12938 | |
| 12939 if (strictRoot === null) { | |
| 12940 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.'); | |
| 12941 | |
| 12942 return; | |
| 12943 } // Dedup strategy: Warn once per component. | |
| 12944 | |
| 12945 | |
| 12946 if (didWarnAboutLegacyContext.has(fiber.type)) { | |
| 12947 return; | |
| 12948 } | |
| 12949 | |
| 12950 var warningsForRoot = pendingLegacyContextWarning.get(strictRoot); | |
| 12951 | |
| 12952 if (fiber.type.contextTypes != null || fiber.type.childContextTypes != null || instance !== null && typeof instance.getChildContext === 'function') { | |
| 12953 if (warningsForRoot === undefined) { | |
| 12954 warningsForRoot = []; | |
| 12955 pendingLegacyContextWarning.set(strictRoot, warningsForRoot); | |
| 12956 } | |
| 12957 | |
| 12958 warningsForRoot.push(fiber); | |
| 12959 } | |
| 12960 }; | |
| 12961 | |
| 12962 ReactStrictModeWarnings.flushLegacyContextWarning = function () { | |
| 12963 pendingLegacyContextWarning.forEach(function (fiberArray, strictRoot) { | |
| 12964 if (fiberArray.length === 0) { | |
| 12965 return; | |
| 12966 } | |
| 12967 | |
| 12968 var firstFiber = fiberArray[0]; | |
| 12969 var uniqueNames = new Set(); | |
| 12970 fiberArray.forEach(function (fiber) { | |
| 12971 uniqueNames.add(getComponentNameFromFiber(fiber) || 'Component'); | |
| 12972 didWarnAboutLegacyContext.add(fiber.type); | |
| 12973 }); | |
| 12974 var sortedNames = setToSortedString(uniqueNames); | |
| 12975 | |
| 12976 try { | |
| 12977 setCurrentFiber(firstFiber); | |
| 12978 | |
| 12979 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); | |
| 12980 } finally { | |
| 12981 resetCurrentFiber(); | |
| 12982 } | |
| 12983 }); | |
| 12984 }; | |
| 12985 | |
| 12986 ReactStrictModeWarnings.discardPendingWarnings = function () { | |
| 12987 pendingComponentWillMountWarnings = []; | |
| 12988 pendingUNSAFE_ComponentWillMountWarnings = []; | |
| 12989 pendingComponentWillReceivePropsWarnings = []; | |
| 12990 pendingUNSAFE_ComponentWillReceivePropsWarnings = []; | |
| 12991 pendingComponentWillUpdateWarnings = []; | |
| 12992 pendingUNSAFE_ComponentWillUpdateWarnings = []; | |
| 12993 pendingLegacyContextWarning = new Map(); | |
| 12994 }; | |
| 12995 } | |
| 12996 | |
| 12997 var didWarnAboutMaps; | |
| 12998 var didWarnAboutGenerators; | |
| 12999 var didWarnAboutStringRefs; | |
| 13000 var ownerHasKeyUseWarning; | |
| 13001 var ownerHasFunctionTypeWarning; | |
| 13002 | |
| 13003 var warnForMissingKey = function (child, returnFiber) {}; | |
| 13004 | |
| 13005 { | |
| 13006 didWarnAboutMaps = false; | |
| 13007 didWarnAboutGenerators = false; | |
| 13008 didWarnAboutStringRefs = {}; | |
| 13009 /** | |
| 13010 * Warn if there's no key explicitly set on dynamic arrays of children or | |
| 13011 * object keys are not valid. This allows us to keep track of children between | |
| 13012 * updates. | |
| 13013 */ | |
| 13014 | |
| 13015 ownerHasKeyUseWarning = {}; | |
| 13016 ownerHasFunctionTypeWarning = {}; | |
| 13017 | |
| 13018 warnForMissingKey = function (child, returnFiber) { | |
| 13019 if (child === null || typeof child !== 'object') { | |
| 13020 return; | |
| 13021 } | |
| 13022 | |
| 13023 if (!child._store || child._store.validated || child.key != null) { | |
| 13024 return; | |
| 13025 } | |
| 13026 | |
| 13027 if (typeof child._store !== 'object') { | |
| 13028 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.'); | |
| 13029 } | |
| 13030 | |
| 13031 child._store.validated = true; | |
| 13032 var componentName = getComponentNameFromFiber(returnFiber) || 'Component'; | |
| 13033 | |
| 13034 if (ownerHasKeyUseWarning[componentName]) { | |
| 13035 return; | |
| 13036 } | |
| 13037 | |
| 13038 ownerHasKeyUseWarning[componentName] = true; | |
| 13039 | |
| 13040 error('Each child in a list should have a unique ' + '"key" prop. See https://reactjs.org/link/warning-keys for ' + 'more information.'); | |
| 13041 }; | |
| 13042 } | |
| 13043 | |
| 13044 function isReactClass(type) { | |
| 13045 return type.prototype && type.prototype.isReactComponent; | |
| 13046 } | |
| 13047 | |
| 13048 function coerceRef(returnFiber, current, element) { | |
| 13049 var mixedRef = element.ref; | |
| 13050 | |
| 13051 if (mixedRef !== null && typeof mixedRef !== 'function' && typeof mixedRef !== 'object') { | |
| 13052 { | |
| 13053 // TODO: Clean this up once we turn on the string ref warning for | |
| 13054 // everyone, because the strict mode case will no longer be relevant | |
| 13055 if ((returnFiber.mode & StrictLegacyMode || warnAboutStringRefs) && // We warn in ReactElement.js if owner and self are equal for string refs | |
| 13056 // because these cannot be automatically converted to an arrow function | |
| 13057 // using a codemod. Therefore, we don't have to warn about string refs again. | |
| 13058 !(element._owner && element._self && element._owner.stateNode !== element._self) && // Will already throw with "Function components cannot have string refs" | |
| 13059 !(element._owner && element._owner.tag !== ClassComponent) && // Will already warn with "Function components cannot be given refs" | |
| 13060 !(typeof element.type === 'function' && !isReactClass(element.type)) && // Will already throw with "Element ref was specified as a string (someStringRef) but no owner was set" | |
| 13061 element._owner) { | |
| 13062 var componentName = getComponentNameFromFiber(returnFiber) || 'Component'; | |
| 13063 | |
| 13064 if (!didWarnAboutStringRefs[componentName]) { | |
| 13065 { | |
| 13066 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); | |
| 13067 } | |
| 13068 | |
| 13069 didWarnAboutStringRefs[componentName] = true; | |
| 13070 } | |
| 13071 } | |
| 13072 } | |
| 13073 | |
| 13074 if (element._owner) { | |
| 13075 var owner = element._owner; | |
| 13076 var inst; | |
| 13077 | |
| 13078 if (owner) { | |
| 13079 var ownerFiber = owner; | |
| 13080 | |
| 13081 if (ownerFiber.tag !== ClassComponent) { | |
| 13082 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'); | |
| 13083 } | |
| 13084 | |
| 13085 inst = ownerFiber.stateNode; | |
| 13086 } | |
| 13087 | |
| 13088 if (!inst) { | |
| 13089 throw new Error("Missing owner for string ref " + mixedRef + ". This error is likely caused by a " + 'bug in React. Please file an issue.'); | |
| 13090 } // Assigning this to a const so Flow knows it won't change in the closure | |
| 13091 | |
| 13092 | |
| 13093 var resolvedInst = inst; | |
| 13094 | |
| 13095 { | |
| 13096 checkPropStringCoercion(mixedRef, 'ref'); | |
| 13097 } | |
| 13098 | |
| 13099 var stringRef = '' + mixedRef; // Check if previous string ref matches new string ref | |
| 13100 | |
| 13101 if (current !== null && current.ref !== null && typeof current.ref === 'function' && current.ref._stringRef === stringRef) { | |
| 13102 return current.ref; | |
| 13103 } | |
| 13104 | |
| 13105 var ref = function (value) { | |
| 13106 var refs = resolvedInst.refs; | |
| 13107 | |
| 13108 if (value === null) { | |
| 13109 delete refs[stringRef]; | |
| 13110 } else { | |
| 13111 refs[stringRef] = value; | |
| 13112 } | |
| 13113 }; | |
| 13114 | |
| 13115 ref._stringRef = stringRef; | |
| 13116 return ref; | |
| 13117 } else { | |
| 13118 if (typeof mixedRef !== 'string') { | |
| 13119 throw new Error('Expected ref to be a function, a string, an object returned by React.createRef(), or null.'); | |
| 13120 } | |
| 13121 | |
| 13122 if (!element._owner) { | |
| 13123 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.'); | |
| 13124 } | |
| 13125 } | |
| 13126 } | |
| 13127 | |
| 13128 return mixedRef; | |
| 13129 } | |
| 13130 | |
| 13131 function throwOnInvalidObjectType(returnFiber, newChild) { | |
| 13132 var childString = Object.prototype.toString.call(newChild); | |
| 13133 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.'); | |
| 13134 } | |
| 13135 | |
| 13136 function warnOnFunctionType(returnFiber) { | |
| 13137 { | |
| 13138 var componentName = getComponentNameFromFiber(returnFiber) || 'Component'; | |
| 13139 | |
| 13140 if (ownerHasFunctionTypeWarning[componentName]) { | |
| 13141 return; | |
| 13142 } | |
| 13143 | |
| 13144 ownerHasFunctionTypeWarning[componentName] = true; | |
| 13145 | |
| 13146 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.'); | |
| 13147 } | |
| 13148 } | |
| 13149 | |
| 13150 function resolveLazy(lazyType) { | |
| 13151 var payload = lazyType._payload; | |
| 13152 var init = lazyType._init; | |
| 13153 return init(payload); | |
| 13154 } // This wrapper function exists because I expect to clone the code in each path | |
| 13155 // to be able to optimize each path individually by branching early. This needs | |
| 13156 // a compiler or we can do it manually. Helpers that don't need this branching | |
| 13157 // live outside of this function. | |
| 13158 | |
| 13159 | |
| 13160 function ChildReconciler(shouldTrackSideEffects) { | |
| 13161 function deleteChild(returnFiber, childToDelete) { | |
| 13162 if (!shouldTrackSideEffects) { | |
| 13163 // Noop. | |
| 13164 return; | |
| 13165 } | |
| 13166 | |
| 13167 var deletions = returnFiber.deletions; | |
| 13168 | |
| 13169 if (deletions === null) { | |
| 13170 returnFiber.deletions = [childToDelete]; | |
| 13171 returnFiber.flags |= ChildDeletion; | |
| 13172 } else { | |
| 13173 deletions.push(childToDelete); | |
| 13174 } | |
| 13175 } | |
| 13176 | |
| 13177 function deleteRemainingChildren(returnFiber, currentFirstChild) { | |
| 13178 if (!shouldTrackSideEffects) { | |
| 13179 // Noop. | |
| 13180 return null; | |
| 13181 } // TODO: For the shouldClone case, this could be micro-optimized a bit by | |
| 13182 // assuming that after the first child we've already added everything. | |
| 13183 | |
| 13184 | |
| 13185 var childToDelete = currentFirstChild; | |
| 13186 | |
| 13187 while (childToDelete !== null) { | |
| 13188 deleteChild(returnFiber, childToDelete); | |
| 13189 childToDelete = childToDelete.sibling; | |
| 13190 } | |
| 13191 | |
| 13192 return null; | |
| 13193 } | |
| 13194 | |
| 13195 function mapRemainingChildren(returnFiber, currentFirstChild) { | |
| 13196 // Add the remaining children to a temporary map so that we can find them by | |
| 13197 // keys quickly. Implicit (null) keys get added to this set with their index | |
| 13198 // instead. | |
| 13199 var existingChildren = new Map(); | |
| 13200 var existingChild = currentFirstChild; | |
| 13201 | |
| 13202 while (existingChild !== null) { | |
| 13203 if (existingChild.key !== null) { | |
| 13204 existingChildren.set(existingChild.key, existingChild); | |
| 13205 } else { | |
| 13206 existingChildren.set(existingChild.index, existingChild); | |
| 13207 } | |
| 13208 | |
| 13209 existingChild = existingChild.sibling; | |
| 13210 } | |
| 13211 | |
| 13212 return existingChildren; | |
| 13213 } | |
| 13214 | |
| 13215 function useFiber(fiber, pendingProps) { | |
| 13216 // We currently set sibling to null and index to 0 here because it is easy | |
| 13217 // to forget to do before returning it. E.g. for the single child case. | |
| 13218 var clone = createWorkInProgress(fiber, pendingProps); | |
| 13219 clone.index = 0; | |
| 13220 clone.sibling = null; | |
| 13221 return clone; | |
| 13222 } | |
| 13223 | |
| 13224 function placeChild(newFiber, lastPlacedIndex, newIndex) { | |
| 13225 newFiber.index = newIndex; | |
| 13226 | |
| 13227 if (!shouldTrackSideEffects) { | |
| 13228 // During hydration, the useId algorithm needs to know which fibers are | |
| 13229 // part of a list of children (arrays, iterators). | |
| 13230 newFiber.flags |= Forked; | |
| 13231 return lastPlacedIndex; | |
| 13232 } | |
| 13233 | |
| 13234 var current = newFiber.alternate; | |
| 13235 | |
| 13236 if (current !== null) { | |
| 13237 var oldIndex = current.index; | |
| 13238 | |
| 13239 if (oldIndex < lastPlacedIndex) { | |
| 13240 // This is a move. | |
| 13241 newFiber.flags |= Placement; | |
| 13242 return lastPlacedIndex; | |
| 13243 } else { | |
| 13244 // This item can stay in place. | |
| 13245 return oldIndex; | |
| 13246 } | |
| 13247 } else { | |
| 13248 // This is an insertion. | |
| 13249 newFiber.flags |= Placement; | |
| 13250 return lastPlacedIndex; | |
| 13251 } | |
| 13252 } | |
| 13253 | |
| 13254 function placeSingleChild(newFiber) { | |
| 13255 // This is simpler for the single child case. We only need to do a | |
| 13256 // placement for inserting new children. | |
| 13257 if (shouldTrackSideEffects && newFiber.alternate === null) { | |
| 13258 newFiber.flags |= Placement; | |
| 13259 } | |
| 13260 | |
| 13261 return newFiber; | |
| 13262 } | |
| 13263 | |
| 13264 function updateTextNode(returnFiber, current, textContent, lanes) { | |
| 13265 if (current === null || current.tag !== HostText) { | |
| 13266 // Insert | |
| 13267 var created = createFiberFromText(textContent, returnFiber.mode, lanes); | |
| 13268 created.return = returnFiber; | |
| 13269 return created; | |
| 13270 } else { | |
| 13271 // Update | |
| 13272 var existing = useFiber(current, textContent); | |
| 13273 existing.return = returnFiber; | |
| 13274 return existing; | |
| 13275 } | |
| 13276 } | |
| 13277 | |
| 13278 function updateElement(returnFiber, current, element, lanes) { | |
| 13279 var elementType = element.type; | |
| 13280 | |
| 13281 if (elementType === REACT_FRAGMENT_TYPE) { | |
| 13282 return updateFragment(returnFiber, current, element.props.children, lanes, element.key); | |
| 13283 } | |
| 13284 | |
| 13285 if (current !== null) { | |
| 13286 if (current.elementType === elementType || ( // Keep this check inline so it only runs on the false path: | |
| 13287 isCompatibleFamilyForHotReloading(current, element) ) || // Lazy types should reconcile their resolved type. | |
| 13288 // We need to do this after the Hot Reloading check above, | |
| 13289 // because hot reloading has different semantics than prod because | |
| 13290 // it doesn't resuspend. So we can't let the call below suspend. | |
| 13291 typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === current.type) { | |
| 13292 // Move based on index | |
| 13293 var existing = useFiber(current, element.props); | |
| 13294 existing.ref = coerceRef(returnFiber, current, element); | |
| 13295 existing.return = returnFiber; | |
| 13296 | |
| 13297 { | |
| 13298 existing._debugSource = element._source; | |
| 13299 existing._debugOwner = element._owner; | |
| 13300 } | |
| 13301 | |
| 13302 return existing; | |
| 13303 } | |
| 13304 } // Insert | |
| 13305 | |
| 13306 | |
| 13307 var created = createFiberFromElement(element, returnFiber.mode, lanes); | |
| 13308 created.ref = coerceRef(returnFiber, current, element); | |
| 13309 created.return = returnFiber; | |
| 13310 return created; | |
| 13311 } | |
| 13312 | |
| 13313 function updatePortal(returnFiber, current, portal, lanes) { | |
| 13314 if (current === null || current.tag !== HostPortal || current.stateNode.containerInfo !== portal.containerInfo || current.stateNode.implementation !== portal.implementation) { | |
| 13315 // Insert | |
| 13316 var created = createFiberFromPortal(portal, returnFiber.mode, lanes); | |
| 13317 created.return = returnFiber; | |
| 13318 return created; | |
| 13319 } else { | |
| 13320 // Update | |
| 13321 var existing = useFiber(current, portal.children || []); | |
| 13322 existing.return = returnFiber; | |
| 13323 return existing; | |
| 13324 } | |
| 13325 } | |
| 13326 | |
| 13327 function updateFragment(returnFiber, current, fragment, lanes, key) { | |
| 13328 if (current === null || current.tag !== Fragment) { | |
| 13329 // Insert | |
| 13330 var created = createFiberFromFragment(fragment, returnFiber.mode, lanes, key); | |
| 13331 created.return = returnFiber; | |
| 13332 return created; | |
| 13333 } else { | |
| 13334 // Update | |
| 13335 var existing = useFiber(current, fragment); | |
| 13336 existing.return = returnFiber; | |
| 13337 return existing; | |
| 13338 } | |
| 13339 } | |
| 13340 | |
| 13341 function createChild(returnFiber, newChild, lanes) { | |
| 13342 if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') { | |
| 13343 // Text nodes don't have keys. If the previous node is implicitly keyed | |
| 13344 // we can continue to replace it without aborting even if it is not a text | |
| 13345 // node. | |
| 13346 var created = createFiberFromText('' + newChild, returnFiber.mode, lanes); | |
| 13347 created.return = returnFiber; | |
| 13348 return created; | |
| 13349 } | |
| 13350 | |
| 13351 if (typeof newChild === 'object' && newChild !== null) { | |
| 13352 switch (newChild.$$typeof) { | |
| 13353 case REACT_ELEMENT_TYPE: | |
| 13354 { | |
| 13355 var _created = createFiberFromElement(newChild, returnFiber.mode, lanes); | |
| 13356 | |
| 13357 _created.ref = coerceRef(returnFiber, null, newChild); | |
| 13358 _created.return = returnFiber; | |
| 13359 return _created; | |
| 13360 } | |
| 13361 | |
| 13362 case REACT_PORTAL_TYPE: | |
| 13363 { | |
| 13364 var _created2 = createFiberFromPortal(newChild, returnFiber.mode, lanes); | |
| 13365 | |
| 13366 _created2.return = returnFiber; | |
| 13367 return _created2; | |
| 13368 } | |
| 13369 | |
| 13370 case REACT_LAZY_TYPE: | |
| 13371 { | |
| 13372 var payload = newChild._payload; | |
| 13373 var init = newChild._init; | |
| 13374 return createChild(returnFiber, init(payload), lanes); | |
| 13375 } | |
| 13376 } | |
| 13377 | |
| 13378 if (isArray(newChild) || getIteratorFn(newChild)) { | |
| 13379 var _created3 = createFiberFromFragment(newChild, returnFiber.mode, lanes, null); | |
| 13380 | |
| 13381 _created3.return = returnFiber; | |
| 13382 return _created3; | |
| 13383 } | |
| 13384 | |
| 13385 throwOnInvalidObjectType(returnFiber, newChild); | |
| 13386 } | |
| 13387 | |
| 13388 { | |
| 13389 if (typeof newChild === 'function') { | |
| 13390 warnOnFunctionType(returnFiber); | |
| 13391 } | |
| 13392 } | |
| 13393 | |
| 13394 return null; | |
| 13395 } | |
| 13396 | |
| 13397 function updateSlot(returnFiber, oldFiber, newChild, lanes) { | |
| 13398 // Update the fiber if the keys match, otherwise return null. | |
| 13399 var key = oldFiber !== null ? oldFiber.key : null; | |
| 13400 | |
| 13401 if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') { | |
| 13402 // Text nodes don't have keys. If the previous node is implicitly keyed | |
| 13403 // we can continue to replace it without aborting even if it is not a text | |
| 13404 // node. | |
| 13405 if (key !== null) { | |
| 13406 return null; | |
| 13407 } | |
| 13408 | |
| 13409 return updateTextNode(returnFiber, oldFiber, '' + newChild, lanes); | |
| 13410 } | |
| 13411 | |
| 13412 if (typeof newChild === 'object' && newChild !== null) { | |
| 13413 switch (newChild.$$typeof) { | |
| 13414 case REACT_ELEMENT_TYPE: | |
| 13415 { | |
| 13416 if (newChild.key === key) { | |
| 13417 return updateElement(returnFiber, oldFiber, newChild, lanes); | |
| 13418 } else { | |
| 13419 return null; | |
| 13420 } | |
| 13421 } | |
| 13422 | |
| 13423 case REACT_PORTAL_TYPE: | |
| 13424 { | |
| 13425 if (newChild.key === key) { | |
| 13426 return updatePortal(returnFiber, oldFiber, newChild, lanes); | |
| 13427 } else { | |
| 13428 return null; | |
| 13429 } | |
| 13430 } | |
| 13431 | |
| 13432 case REACT_LAZY_TYPE: | |
| 13433 { | |
| 13434 var payload = newChild._payload; | |
| 13435 var init = newChild._init; | |
| 13436 return updateSlot(returnFiber, oldFiber, init(payload), lanes); | |
| 13437 } | |
| 13438 } | |
| 13439 | |
| 13440 if (isArray(newChild) || getIteratorFn(newChild)) { | |
| 13441 if (key !== null) { | |
| 13442 return null; | |
| 13443 } | |
| 13444 | |
| 13445 return updateFragment(returnFiber, oldFiber, newChild, lanes, null); | |
| 13446 } | |
| 13447 | |
| 13448 throwOnInvalidObjectType(returnFiber, newChild); | |
| 13449 } | |
| 13450 | |
| 13451 { | |
| 13452 if (typeof newChild === 'function') { | |
| 13453 warnOnFunctionType(returnFiber); | |
| 13454 } | |
| 13455 } | |
| 13456 | |
| 13457 return null; | |
| 13458 } | |
| 13459 | |
| 13460 function updateFromMap(existingChildren, returnFiber, newIdx, newChild, lanes) { | |
| 13461 if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') { | |
| 13462 // Text nodes don't have keys, so we neither have to check the old nor | |
| 13463 // new node for the key. If both are text nodes, they match. | |
| 13464 var matchedFiber = existingChildren.get(newIdx) || null; | |
| 13465 return updateTextNode(returnFiber, matchedFiber, '' + newChild, lanes); | |
| 13466 } | |
| 13467 | |
| 13468 if (typeof newChild === 'object' && newChild !== null) { | |
| 13469 switch (newChild.$$typeof) { | |
| 13470 case REACT_ELEMENT_TYPE: | |
| 13471 { | |
| 13472 var _matchedFiber = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; | |
| 13473 | |
| 13474 return updateElement(returnFiber, _matchedFiber, newChild, lanes); | |
| 13475 } | |
| 13476 | |
| 13477 case REACT_PORTAL_TYPE: | |
| 13478 { | |
| 13479 var _matchedFiber2 = existingChildren.get(newChild.key === null ? newIdx : newChild.key) || null; | |
| 13480 | |
| 13481 return updatePortal(returnFiber, _matchedFiber2, newChild, lanes); | |
| 13482 } | |
| 13483 | |
| 13484 case REACT_LAZY_TYPE: | |
| 13485 var payload = newChild._payload; | |
| 13486 var init = newChild._init; | |
| 13487 return updateFromMap(existingChildren, returnFiber, newIdx, init(payload), lanes); | |
| 13488 } | |
| 13489 | |
| 13490 if (isArray(newChild) || getIteratorFn(newChild)) { | |
| 13491 var _matchedFiber3 = existingChildren.get(newIdx) || null; | |
| 13492 | |
| 13493 return updateFragment(returnFiber, _matchedFiber3, newChild, lanes, null); | |
| 13494 } | |
| 13495 | |
| 13496 throwOnInvalidObjectType(returnFiber, newChild); | |
| 13497 } | |
| 13498 | |
| 13499 { | |
| 13500 if (typeof newChild === 'function') { | |
| 13501 warnOnFunctionType(returnFiber); | |
| 13502 } | |
| 13503 } | |
| 13504 | |
| 13505 return null; | |
| 13506 } | |
| 13507 /** | |
| 13508 * Warns if there is a duplicate or missing key | |
| 13509 */ | |
| 13510 | |
| 13511 | |
| 13512 function warnOnInvalidKey(child, knownKeys, returnFiber) { | |
| 13513 { | |
| 13514 if (typeof child !== 'object' || child === null) { | |
| 13515 return knownKeys; | |
| 13516 } | |
| 13517 | |
| 13518 switch (child.$$typeof) { | |
| 13519 case REACT_ELEMENT_TYPE: | |
| 13520 case REACT_PORTAL_TYPE: | |
| 13521 warnForMissingKey(child, returnFiber); | |
| 13522 var key = child.key; | |
| 13523 | |
| 13524 if (typeof key !== 'string') { | |
| 13525 break; | |
| 13526 } | |
| 13527 | |
| 13528 if (knownKeys === null) { | |
| 13529 knownKeys = new Set(); | |
| 13530 knownKeys.add(key); | |
| 13531 break; | |
| 13532 } | |
| 13533 | |
| 13534 if (!knownKeys.has(key)) { | |
| 13535 knownKeys.add(key); | |
| 13536 break; | |
| 13537 } | |
| 13538 | |
| 13539 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); | |
| 13540 | |
| 13541 break; | |
| 13542 | |
| 13543 case REACT_LAZY_TYPE: | |
| 13544 var payload = child._payload; | |
| 13545 var init = child._init; | |
| 13546 warnOnInvalidKey(init(payload), knownKeys, returnFiber); | |
| 13547 break; | |
| 13548 } | |
| 13549 } | |
| 13550 | |
| 13551 return knownKeys; | |
| 13552 } | |
| 13553 | |
| 13554 function reconcileChildrenArray(returnFiber, currentFirstChild, newChildren, lanes) { | |
| 13555 // This algorithm can't optimize by searching from both ends since we | |
| 13556 // don't have backpointers on fibers. I'm trying to see how far we can get | |
| 13557 // with that model. If it ends up not being worth the tradeoffs, we can | |
| 13558 // add it later. | |
| 13559 // Even with a two ended optimization, we'd want to optimize for the case | |
| 13560 // where there are few changes and brute force the comparison instead of | |
| 13561 // going for the Map. It'd like to explore hitting that path first in | |
| 13562 // forward-only mode and only go for the Map once we notice that we need | |
| 13563 // lots of look ahead. This doesn't handle reversal as well as two ended | |
| 13564 // search but that's unusual. Besides, for the two ended optimization to | |
| 13565 // work on Iterables, we'd need to copy the whole set. | |
| 13566 // In this first iteration, we'll just live with hitting the bad case | |
| 13567 // (adding everything to a Map) in for every insert/move. | |
| 13568 // If you change this code, also update reconcileChildrenIterator() which | |
| 13569 // uses the same algorithm. | |
| 13570 { | |
| 13571 // First, validate keys. | |
| 13572 var knownKeys = null; | |
| 13573 | |
| 13574 for (var i = 0; i < newChildren.length; i++) { | |
| 13575 var child = newChildren[i]; | |
| 13576 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber); | |
| 13577 } | |
| 13578 } | |
| 13579 | |
| 13580 var resultingFirstChild = null; | |
| 13581 var previousNewFiber = null; | |
| 13582 var oldFiber = currentFirstChild; | |
| 13583 var lastPlacedIndex = 0; | |
| 13584 var newIdx = 0; | |
| 13585 var nextOldFiber = null; | |
| 13586 | |
| 13587 for (; oldFiber !== null && newIdx < newChildren.length; newIdx++) { | |
| 13588 if (oldFiber.index > newIdx) { | |
| 13589 nextOldFiber = oldFiber; | |
| 13590 oldFiber = null; | |
| 13591 } else { | |
| 13592 nextOldFiber = oldFiber.sibling; | |
| 13593 } | |
| 13594 | |
| 13595 var newFiber = updateSlot(returnFiber, oldFiber, newChildren[newIdx], lanes); | |
| 13596 | |
| 13597 if (newFiber === null) { | |
| 13598 // TODO: This breaks on empty slots like null children. That's | |
| 13599 // unfortunate because it triggers the slow path all the time. We need | |
| 13600 // a better way to communicate whether this was a miss or null, | |
| 13601 // boolean, undefined, etc. | |
| 13602 if (oldFiber === null) { | |
| 13603 oldFiber = nextOldFiber; | |
| 13604 } | |
| 13605 | |
| 13606 break; | |
| 13607 } | |
| 13608 | |
| 13609 if (shouldTrackSideEffects) { | |
| 13610 if (oldFiber && newFiber.alternate === null) { | |
| 13611 // We matched the slot, but we didn't reuse the existing fiber, so we | |
| 13612 // need to delete the existing child. | |
| 13613 deleteChild(returnFiber, oldFiber); | |
| 13614 } | |
| 13615 } | |
| 13616 | |
| 13617 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); | |
| 13618 | |
| 13619 if (previousNewFiber === null) { | |
| 13620 // TODO: Move out of the loop. This only happens for the first run. | |
| 13621 resultingFirstChild = newFiber; | |
| 13622 } else { | |
| 13623 // TODO: Defer siblings if we're not at the right index for this slot. | |
| 13624 // I.e. if we had null values before, then we want to defer this | |
| 13625 // for each null value. However, we also don't want to call updateSlot | |
| 13626 // with the previous one. | |
| 13627 previousNewFiber.sibling = newFiber; | |
| 13628 } | |
| 13629 | |
| 13630 previousNewFiber = newFiber; | |
| 13631 oldFiber = nextOldFiber; | |
| 13632 } | |
| 13633 | |
| 13634 if (newIdx === newChildren.length) { | |
| 13635 // We've reached the end of the new children. We can delete the rest. | |
| 13636 deleteRemainingChildren(returnFiber, oldFiber); | |
| 13637 | |
| 13638 if (getIsHydrating()) { | |
| 13639 var numberOfForks = newIdx; | |
| 13640 pushTreeFork(returnFiber, numberOfForks); | |
| 13641 } | |
| 13642 | |
| 13643 return resultingFirstChild; | |
| 13644 } | |
| 13645 | |
| 13646 if (oldFiber === null) { | |
| 13647 // If we don't have any more existing children we can choose a fast path | |
| 13648 // since the rest will all be insertions. | |
| 13649 for (; newIdx < newChildren.length; newIdx++) { | |
| 13650 var _newFiber = createChild(returnFiber, newChildren[newIdx], lanes); | |
| 13651 | |
| 13652 if (_newFiber === null) { | |
| 13653 continue; | |
| 13654 } | |
| 13655 | |
| 13656 lastPlacedIndex = placeChild(_newFiber, lastPlacedIndex, newIdx); | |
| 13657 | |
| 13658 if (previousNewFiber === null) { | |
| 13659 // TODO: Move out of the loop. This only happens for the first run. | |
| 13660 resultingFirstChild = _newFiber; | |
| 13661 } else { | |
| 13662 previousNewFiber.sibling = _newFiber; | |
| 13663 } | |
| 13664 | |
| 13665 previousNewFiber = _newFiber; | |
| 13666 } | |
| 13667 | |
| 13668 if (getIsHydrating()) { | |
| 13669 var _numberOfForks = newIdx; | |
| 13670 pushTreeFork(returnFiber, _numberOfForks); | |
| 13671 } | |
| 13672 | |
| 13673 return resultingFirstChild; | |
| 13674 } // Add all children to a key map for quick lookups. | |
| 13675 | |
| 13676 | |
| 13677 var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves. | |
| 13678 | |
| 13679 for (; newIdx < newChildren.length; newIdx++) { | |
| 13680 var _newFiber2 = updateFromMap(existingChildren, returnFiber, newIdx, newChildren[newIdx], lanes); | |
| 13681 | |
| 13682 if (_newFiber2 !== null) { | |
| 13683 if (shouldTrackSideEffects) { | |
| 13684 if (_newFiber2.alternate !== null) { | |
| 13685 // The new fiber is a work in progress, but if there exists a | |
| 13686 // current, that means that we reused the fiber. We need to delete | |
| 13687 // it from the child list so that we don't add it to the deletion | |
| 13688 // list. | |
| 13689 existingChildren.delete(_newFiber2.key === null ? newIdx : _newFiber2.key); | |
| 13690 } | |
| 13691 } | |
| 13692 | |
| 13693 lastPlacedIndex = placeChild(_newFiber2, lastPlacedIndex, newIdx); | |
| 13694 | |
| 13695 if (previousNewFiber === null) { | |
| 13696 resultingFirstChild = _newFiber2; | |
| 13697 } else { | |
| 13698 previousNewFiber.sibling = _newFiber2; | |
| 13699 } | |
| 13700 | |
| 13701 previousNewFiber = _newFiber2; | |
| 13702 } | |
| 13703 } | |
| 13704 | |
| 13705 if (shouldTrackSideEffects) { | |
| 13706 // Any existing children that weren't consumed above were deleted. We need | |
| 13707 // to add them to the deletion list. | |
| 13708 existingChildren.forEach(function (child) { | |
| 13709 return deleteChild(returnFiber, child); | |
| 13710 }); | |
| 13711 } | |
| 13712 | |
| 13713 if (getIsHydrating()) { | |
| 13714 var _numberOfForks2 = newIdx; | |
| 13715 pushTreeFork(returnFiber, _numberOfForks2); | |
| 13716 } | |
| 13717 | |
| 13718 return resultingFirstChild; | |
| 13719 } | |
| 13720 | |
| 13721 function reconcileChildrenIterator(returnFiber, currentFirstChild, newChildrenIterable, lanes) { | |
| 13722 // This is the same implementation as reconcileChildrenArray(), | |
| 13723 // but using the iterator instead. | |
| 13724 var iteratorFn = getIteratorFn(newChildrenIterable); | |
| 13725 | |
| 13726 if (typeof iteratorFn !== 'function') { | |
| 13727 throw new Error('An object is not an iterable. This error is likely caused by a bug in ' + 'React. Please file an issue.'); | |
| 13728 } | |
| 13729 | |
| 13730 { | |
| 13731 // We don't support rendering Generators because it's a mutation. | |
| 13732 // See https://github.com/facebook/react/issues/12995 | |
| 13733 if (typeof Symbol === 'function' && // $FlowFixMe Flow doesn't know about toStringTag | |
| 13734 newChildrenIterable[Symbol.toStringTag] === 'Generator') { | |
| 13735 if (!didWarnAboutGenerators) { | |
| 13736 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.'); | |
| 13737 } | |
| 13738 | |
| 13739 didWarnAboutGenerators = true; | |
| 13740 } // Warn about using Maps as children | |
| 13741 | |
| 13742 | |
| 13743 if (newChildrenIterable.entries === iteratorFn) { | |
| 13744 if (!didWarnAboutMaps) { | |
| 13745 error('Using Maps as children is not supported. ' + 'Use an array of keyed ReactElements instead.'); | |
| 13746 } | |
| 13747 | |
| 13748 didWarnAboutMaps = true; | |
| 13749 } // First, validate keys. | |
| 13750 // We'll get a different iterator later for the main pass. | |
| 13751 | |
| 13752 | |
| 13753 var _newChildren = iteratorFn.call(newChildrenIterable); | |
| 13754 | |
| 13755 if (_newChildren) { | |
| 13756 var knownKeys = null; | |
| 13757 | |
| 13758 var _step = _newChildren.next(); | |
| 13759 | |
| 13760 for (; !_step.done; _step = _newChildren.next()) { | |
| 13761 var child = _step.value; | |
| 13762 knownKeys = warnOnInvalidKey(child, knownKeys, returnFiber); | |
| 13763 } | |
| 13764 } | |
| 13765 } | |
| 13766 | |
| 13767 var newChildren = iteratorFn.call(newChildrenIterable); | |
| 13768 | |
| 13769 if (newChildren == null) { | |
| 13770 throw new Error('An iterable object provided no iterator.'); | |
| 13771 } | |
| 13772 | |
| 13773 var resultingFirstChild = null; | |
| 13774 var previousNewFiber = null; | |
| 13775 var oldFiber = currentFirstChild; | |
| 13776 var lastPlacedIndex = 0; | |
| 13777 var newIdx = 0; | |
| 13778 var nextOldFiber = null; | |
| 13779 var step = newChildren.next(); | |
| 13780 | |
| 13781 for (; oldFiber !== null && !step.done; newIdx++, step = newChildren.next()) { | |
| 13782 if (oldFiber.index > newIdx) { | |
| 13783 nextOldFiber = oldFiber; | |
| 13784 oldFiber = null; | |
| 13785 } else { | |
| 13786 nextOldFiber = oldFiber.sibling; | |
| 13787 } | |
| 13788 | |
| 13789 var newFiber = updateSlot(returnFiber, oldFiber, step.value, lanes); | |
| 13790 | |
| 13791 if (newFiber === null) { | |
| 13792 // TODO: This breaks on empty slots like null children. That's | |
| 13793 // unfortunate because it triggers the slow path all the time. We need | |
| 13794 // a better way to communicate whether this was a miss or null, | |
| 13795 // boolean, undefined, etc. | |
| 13796 if (oldFiber === null) { | |
| 13797 oldFiber = nextOldFiber; | |
| 13798 } | |
| 13799 | |
| 13800 break; | |
| 13801 } | |
| 13802 | |
| 13803 if (shouldTrackSideEffects) { | |
| 13804 if (oldFiber && newFiber.alternate === null) { | |
| 13805 // We matched the slot, but we didn't reuse the existing fiber, so we | |
| 13806 // need to delete the existing child. | |
| 13807 deleteChild(returnFiber, oldFiber); | |
| 13808 } | |
| 13809 } | |
| 13810 | |
| 13811 lastPlacedIndex = placeChild(newFiber, lastPlacedIndex, newIdx); | |
| 13812 | |
| 13813 if (previousNewFiber === null) { | |
| 13814 // TODO: Move out of the loop. This only happens for the first run. | |
| 13815 resultingFirstChild = newFiber; | |
| 13816 } else { | |
| 13817 // TODO: Defer siblings if we're not at the right index for this slot. | |
| 13818 // I.e. if we had null values before, then we want to defer this | |
| 13819 // for each null value. However, we also don't want to call updateSlot | |
| 13820 // with the previous one. | |
| 13821 previousNewFiber.sibling = newFiber; | |
| 13822 } | |
| 13823 | |
| 13824 previousNewFiber = newFiber; | |
| 13825 oldFiber = nextOldFiber; | |
| 13826 } | |
| 13827 | |
| 13828 if (step.done) { | |
| 13829 // We've reached the end of the new children. We can delete the rest. | |
| 13830 deleteRemainingChildren(returnFiber, oldFiber); | |
| 13831 | |
| 13832 if (getIsHydrating()) { | |
| 13833 var numberOfForks = newIdx; | |
| 13834 pushTreeFork(returnFiber, numberOfForks); | |
| 13835 } | |
| 13836 | |
| 13837 return resultingFirstChild; | |
| 13838 } | |
| 13839 | |
| 13840 if (oldFiber === null) { | |
| 13841 // If we don't have any more existing children we can choose a fast path | |
| 13842 // since the rest will all be insertions. | |
| 13843 for (; !step.done; newIdx++, step = newChildren.next()) { | |
| 13844 var _newFiber3 = createChild(returnFiber, step.value, lanes); | |
| 13845 | |
| 13846 if (_newFiber3 === null) { | |
| 13847 continue; | |
| 13848 } | |
| 13849 | |
| 13850 lastPlacedIndex = placeChild(_newFiber3, lastPlacedIndex, newIdx); | |
| 13851 | |
| 13852 if (previousNewFiber === null) { | |
| 13853 // TODO: Move out of the loop. This only happens for the first run. | |
| 13854 resultingFirstChild = _newFiber3; | |
| 13855 } else { | |
| 13856 previousNewFiber.sibling = _newFiber3; | |
| 13857 } | |
| 13858 | |
| 13859 previousNewFiber = _newFiber3; | |
| 13860 } | |
| 13861 | |
| 13862 if (getIsHydrating()) { | |
| 13863 var _numberOfForks3 = newIdx; | |
| 13864 pushTreeFork(returnFiber, _numberOfForks3); | |
| 13865 } | |
| 13866 | |
| 13867 return resultingFirstChild; | |
| 13868 } // Add all children to a key map for quick lookups. | |
| 13869 | |
| 13870 | |
| 13871 var existingChildren = mapRemainingChildren(returnFiber, oldFiber); // Keep scanning and use the map to restore deleted items as moves. | |
| 13872 | |
| 13873 for (; !step.done; newIdx++, step = newChildren.next()) { | |
| 13874 var _newFiber4 = updateFromMap(existingChildren, returnFiber, newIdx, step.value, lanes); | |
| 13875 | |
| 13876 if (_newFiber4 !== null) { | |
| 13877 if (shouldTrackSideEffects) { | |
| 13878 if (_newFiber4.alternate !== null) { | |
| 13879 // The new fiber is a work in progress, but if there exists a | |
| 13880 // current, that means that we reused the fiber. We need to delete | |
| 13881 // it from the child list so that we don't add it to the deletion | |
| 13882 // list. | |
| 13883 existingChildren.delete(_newFiber4.key === null ? newIdx : _newFiber4.key); | |
| 13884 } | |
| 13885 } | |
| 13886 | |
| 13887 lastPlacedIndex = placeChild(_newFiber4, lastPlacedIndex, newIdx); | |
| 13888 | |
| 13889 if (previousNewFiber === null) { | |
| 13890 resultingFirstChild = _newFiber4; | |
| 13891 } else { | |
| 13892 previousNewFiber.sibling = _newFiber4; | |
| 13893 } | |
| 13894 | |
| 13895 previousNewFiber = _newFiber4; | |
| 13896 } | |
| 13897 } | |
| 13898 | |
| 13899 if (shouldTrackSideEffects) { | |
| 13900 // Any existing children that weren't consumed above were deleted. We need | |
| 13901 // to add them to the deletion list. | |
| 13902 existingChildren.forEach(function (child) { | |
| 13903 return deleteChild(returnFiber, child); | |
| 13904 }); | |
| 13905 } | |
| 13906 | |
| 13907 if (getIsHydrating()) { | |
| 13908 var _numberOfForks4 = newIdx; | |
| 13909 pushTreeFork(returnFiber, _numberOfForks4); | |
| 13910 } | |
| 13911 | |
| 13912 return resultingFirstChild; | |
| 13913 } | |
| 13914 | |
| 13915 function reconcileSingleTextNode(returnFiber, currentFirstChild, textContent, lanes) { | |
| 13916 // There's no need to check for keys on text nodes since we don't have a | |
| 13917 // way to define them. | |
| 13918 if (currentFirstChild !== null && currentFirstChild.tag === HostText) { | |
| 13919 // We already have an existing node so let's just update it and delete | |
| 13920 // the rest. | |
| 13921 deleteRemainingChildren(returnFiber, currentFirstChild.sibling); | |
| 13922 var existing = useFiber(currentFirstChild, textContent); | |
| 13923 existing.return = returnFiber; | |
| 13924 return existing; | |
| 13925 } // The existing first child is not a text node so we need to create one | |
| 13926 // and delete the existing ones. | |
| 13927 | |
| 13928 | |
| 13929 deleteRemainingChildren(returnFiber, currentFirstChild); | |
| 13930 var created = createFiberFromText(textContent, returnFiber.mode, lanes); | |
| 13931 created.return = returnFiber; | |
| 13932 return created; | |
| 13933 } | |
| 13934 | |
| 13935 function reconcileSingleElement(returnFiber, currentFirstChild, element, lanes) { | |
| 13936 var key = element.key; | |
| 13937 var child = currentFirstChild; | |
| 13938 | |
| 13939 while (child !== null) { | |
| 13940 // TODO: If key === null and child.key === null, then this only applies to | |
| 13941 // the first item in the list. | |
| 13942 if (child.key === key) { | |
| 13943 var elementType = element.type; | |
| 13944 | |
| 13945 if (elementType === REACT_FRAGMENT_TYPE) { | |
| 13946 if (child.tag === Fragment) { | |
| 13947 deleteRemainingChildren(returnFiber, child.sibling); | |
| 13948 var existing = useFiber(child, element.props.children); | |
| 13949 existing.return = returnFiber; | |
| 13950 | |
| 13951 { | |
| 13952 existing._debugSource = element._source; | |
| 13953 existing._debugOwner = element._owner; | |
| 13954 } | |
| 13955 | |
| 13956 return existing; | |
| 13957 } | |
| 13958 } else { | |
| 13959 if (child.elementType === elementType || ( // Keep this check inline so it only runs on the false path: | |
| 13960 isCompatibleFamilyForHotReloading(child, element) ) || // Lazy types should reconcile their resolved type. | |
| 13961 // We need to do this after the Hot Reloading check above, | |
| 13962 // because hot reloading has different semantics than prod because | |
| 13963 // it doesn't resuspend. So we can't let the call below suspend. | |
| 13964 typeof elementType === 'object' && elementType !== null && elementType.$$typeof === REACT_LAZY_TYPE && resolveLazy(elementType) === child.type) { | |
| 13965 deleteRemainingChildren(returnFiber, child.sibling); | |
| 13966 | |
| 13967 var _existing = useFiber(child, element.props); | |
| 13968 | |
| 13969 _existing.ref = coerceRef(returnFiber, child, element); | |
| 13970 _existing.return = returnFiber; | |
| 13971 | |
| 13972 { | |
| 13973 _existing._debugSource = element._source; | |
| 13974 _existing._debugOwner = element._owner; | |
| 13975 } | |
| 13976 | |
| 13977 return _existing; | |
| 13978 } | |
| 13979 } // Didn't match. | |
| 13980 | |
| 13981 | |
| 13982 deleteRemainingChildren(returnFiber, child); | |
| 13983 break; | |
| 13984 } else { | |
| 13985 deleteChild(returnFiber, child); | |
| 13986 } | |
| 13987 | |
| 13988 child = child.sibling; | |
| 13989 } | |
| 13990 | |
| 13991 if (element.type === REACT_FRAGMENT_TYPE) { | |
| 13992 var created = createFiberFromFragment(element.props.children, returnFiber.mode, lanes, element.key); | |
| 13993 created.return = returnFiber; | |
| 13994 return created; | |
| 13995 } else { | |
| 13996 var _created4 = createFiberFromElement(element, returnFiber.mode, lanes); | |
| 13997 | |
| 13998 _created4.ref = coerceRef(returnFiber, currentFirstChild, element); | |
| 13999 _created4.return = returnFiber; | |
| 14000 return _created4; | |
| 14001 } | |
| 14002 } | |
| 14003 | |
| 14004 function reconcileSinglePortal(returnFiber, currentFirstChild, portal, lanes) { | |
| 14005 var key = portal.key; | |
| 14006 var child = currentFirstChild; | |
| 14007 | |
| 14008 while (child !== null) { | |
| 14009 // TODO: If key === null and child.key === null, then this only applies to | |
| 14010 // the first item in the list. | |
| 14011 if (child.key === key) { | |
| 14012 if (child.tag === HostPortal && child.stateNode.containerInfo === portal.containerInfo && child.stateNode.implementation === portal.implementation) { | |
| 14013 deleteRemainingChildren(returnFiber, child.sibling); | |
| 14014 var existing = useFiber(child, portal.children || []); | |
| 14015 existing.return = returnFiber; | |
| 14016 return existing; | |
| 14017 } else { | |
| 14018 deleteRemainingChildren(returnFiber, child); | |
| 14019 break; | |
| 14020 } | |
| 14021 } else { | |
| 14022 deleteChild(returnFiber, child); | |
| 14023 } | |
| 14024 | |
| 14025 child = child.sibling; | |
| 14026 } | |
| 14027 | |
| 14028 var created = createFiberFromPortal(portal, returnFiber.mode, lanes); | |
| 14029 created.return = returnFiber; | |
| 14030 return created; | |
| 14031 } // This API will tag the children with the side-effect of the reconciliation | |
| 14032 // itself. They will be added to the side-effect list as we pass through the | |
| 14033 // children and the parent. | |
| 14034 | |
| 14035 | |
| 14036 function reconcileChildFibers(returnFiber, currentFirstChild, newChild, lanes) { | |
| 14037 // This function is not recursive. | |
| 14038 // If the top level item is an array, we treat it as a set of children, | |
| 14039 // not as a fragment. Nested arrays on the other hand will be treated as | |
| 14040 // fragment nodes. Recursion happens at the normal flow. | |
| 14041 // Handle top level unkeyed fragments as if they were arrays. | |
| 14042 // This leads to an ambiguity between <>{[...]}</> and <>...</>. | |
| 14043 // We treat the ambiguous cases above the same. | |
| 14044 var isUnkeyedTopLevelFragment = typeof newChild === 'object' && newChild !== null && newChild.type === REACT_FRAGMENT_TYPE && newChild.key === null; | |
| 14045 | |
| 14046 if (isUnkeyedTopLevelFragment) { | |
| 14047 newChild = newChild.props.children; | |
| 14048 } // Handle object types | |
| 14049 | |
| 14050 | |
| 14051 if (typeof newChild === 'object' && newChild !== null) { | |
| 14052 switch (newChild.$$typeof) { | |
| 14053 case REACT_ELEMENT_TYPE: | |
| 14054 return placeSingleChild(reconcileSingleElement(returnFiber, currentFirstChild, newChild, lanes)); | |
| 14055 | |
| 14056 case REACT_PORTAL_TYPE: | |
| 14057 return placeSingleChild(reconcileSinglePortal(returnFiber, currentFirstChild, newChild, lanes)); | |
| 14058 | |
| 14059 case REACT_LAZY_TYPE: | |
| 14060 var payload = newChild._payload; | |
| 14061 var init = newChild._init; // TODO: This function is supposed to be non-recursive. | |
| 14062 | |
| 14063 return reconcileChildFibers(returnFiber, currentFirstChild, init(payload), lanes); | |
| 14064 } | |
| 14065 | |
| 14066 if (isArray(newChild)) { | |
| 14067 return reconcileChildrenArray(returnFiber, currentFirstChild, newChild, lanes); | |
| 14068 } | |
| 14069 | |
| 14070 if (getIteratorFn(newChild)) { | |
| 14071 return reconcileChildrenIterator(returnFiber, currentFirstChild, newChild, lanes); | |
| 14072 } | |
| 14073 | |
| 14074 throwOnInvalidObjectType(returnFiber, newChild); | |
| 14075 } | |
| 14076 | |
| 14077 if (typeof newChild === 'string' && newChild !== '' || typeof newChild === 'number') { | |
| 14078 return placeSingleChild(reconcileSingleTextNode(returnFiber, currentFirstChild, '' + newChild, lanes)); | |
| 14079 } | |
| 14080 | |
| 14081 { | |
| 14082 if (typeof newChild === 'function') { | |
| 14083 warnOnFunctionType(returnFiber); | |
| 14084 } | |
| 14085 } // Remaining cases are all treated as empty. | |
| 14086 | |
| 14087 | |
| 14088 return deleteRemainingChildren(returnFiber, currentFirstChild); | |
| 14089 } | |
| 14090 | |
| 14091 return reconcileChildFibers; | |
| 14092 } | |
| 14093 | |
| 14094 var reconcileChildFibers = ChildReconciler(true); | |
| 14095 var mountChildFibers = ChildReconciler(false); | |
| 14096 function cloneChildFibers(current, workInProgress) { | |
| 14097 if (current !== null && workInProgress.child !== current.child) { | |
| 14098 throw new Error('Resuming work not yet implemented.'); | |
| 14099 } | |
| 14100 | |
| 14101 if (workInProgress.child === null) { | |
| 14102 return; | |
| 14103 } | |
| 14104 | |
| 14105 var currentChild = workInProgress.child; | |
| 14106 var newChild = createWorkInProgress(currentChild, currentChild.pendingProps); | |
| 14107 workInProgress.child = newChild; | |
| 14108 newChild.return = workInProgress; | |
| 14109 | |
| 14110 while (currentChild.sibling !== null) { | |
| 14111 currentChild = currentChild.sibling; | |
| 14112 newChild = newChild.sibling = createWorkInProgress(currentChild, currentChild.pendingProps); | |
| 14113 newChild.return = workInProgress; | |
| 14114 } | |
| 14115 | |
| 14116 newChild.sibling = null; | |
| 14117 } // Reset a workInProgress child set to prepare it for a second pass. | |
| 14118 | |
| 14119 function resetChildFibers(workInProgress, lanes) { | |
| 14120 var child = workInProgress.child; | |
| 14121 | |
| 14122 while (child !== null) { | |
| 14123 resetWorkInProgress(child, lanes); | |
| 14124 child = child.sibling; | |
| 14125 } | |
| 14126 } | |
| 14127 | |
| 14128 var valueCursor = createCursor(null); | |
| 14129 var rendererSigil; | |
| 14130 | |
| 14131 { | |
| 14132 // Use this to detect multiple renderers using the same context | |
| 14133 rendererSigil = {}; | |
| 14134 } | |
| 14135 | |
| 14136 var currentlyRenderingFiber = null; | |
| 14137 var lastContextDependency = null; | |
| 14138 var lastFullyObservedContext = null; | |
| 14139 var isDisallowedContextReadInDEV = false; | |
| 14140 function resetContextDependencies() { | |
| 14141 // This is called right before React yields execution, to ensure `readContext` | |
| 14142 // cannot be called outside the render phase. | |
| 14143 currentlyRenderingFiber = null; | |
| 14144 lastContextDependency = null; | |
| 14145 lastFullyObservedContext = null; | |
| 14146 | |
| 14147 { | |
| 14148 isDisallowedContextReadInDEV = false; | |
| 14149 } | |
| 14150 } | |
| 14151 function enterDisallowedContextReadInDEV() { | |
| 14152 { | |
| 14153 isDisallowedContextReadInDEV = true; | |
| 14154 } | |
| 14155 } | |
| 14156 function exitDisallowedContextReadInDEV() { | |
| 14157 { | |
| 14158 isDisallowedContextReadInDEV = false; | |
| 14159 } | |
| 14160 } | |
| 14161 function pushProvider(providerFiber, context, nextValue) { | |
| 14162 { | |
| 14163 push(valueCursor, context._currentValue, providerFiber); | |
| 14164 context._currentValue = nextValue; | |
| 14165 | |
| 14166 { | |
| 14167 if (context._currentRenderer !== undefined && context._currentRenderer !== null && context._currentRenderer !== rendererSigil) { | |
| 14168 error('Detected multiple renderers concurrently rendering the ' + 'same context provider. This is currently unsupported.'); | |
| 14169 } | |
| 14170 | |
| 14171 context._currentRenderer = rendererSigil; | |
| 14172 } | |
| 14173 } | |
| 14174 } | |
| 14175 function popProvider(context, providerFiber) { | |
| 14176 var currentValue = valueCursor.current; | |
| 14177 pop(valueCursor, providerFiber); | |
| 14178 | |
| 14179 { | |
| 14180 { | |
| 14181 context._currentValue = currentValue; | |
| 14182 } | |
| 14183 } | |
| 14184 } | |
| 14185 function scheduleContextWorkOnParentPath(parent, renderLanes, propagationRoot) { | |
| 14186 // Update the child lanes of all the ancestors, including the alternates. | |
| 14187 var node = parent; | |
| 14188 | |
| 14189 while (node !== null) { | |
| 14190 var alternate = node.alternate; | |
| 14191 | |
| 14192 if (!isSubsetOfLanes(node.childLanes, renderLanes)) { | |
| 14193 node.childLanes = mergeLanes(node.childLanes, renderLanes); | |
| 14194 | |
| 14195 if (alternate !== null) { | |
| 14196 alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes); | |
| 14197 } | |
| 14198 } else if (alternate !== null && !isSubsetOfLanes(alternate.childLanes, renderLanes)) { | |
| 14199 alternate.childLanes = mergeLanes(alternate.childLanes, renderLanes); | |
| 14200 } | |
| 14201 | |
| 14202 if (node === propagationRoot) { | |
| 14203 break; | |
| 14204 } | |
| 14205 | |
| 14206 node = node.return; | |
| 14207 } | |
| 14208 | |
| 14209 { | |
| 14210 if (node !== propagationRoot) { | |
| 14211 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.'); | |
| 14212 } | |
| 14213 } | |
| 14214 } | |
| 14215 function propagateContextChange(workInProgress, context, renderLanes) { | |
| 14216 { | |
| 14217 propagateContextChange_eager(workInProgress, context, renderLanes); | |
| 14218 } | |
| 14219 } | |
| 14220 | |
| 14221 function propagateContextChange_eager(workInProgress, context, renderLanes) { | |
| 14222 | |
| 14223 var fiber = workInProgress.child; | |
| 14224 | |
| 14225 if (fiber !== null) { | |
| 14226 // Set the return pointer of the child to the work-in-progress fiber. | |
| 14227 fiber.return = workInProgress; | |
| 14228 } | |
| 14229 | |
| 14230 while (fiber !== null) { | |
| 14231 var nextFiber = void 0; // Visit this fiber. | |
| 14232 | |
| 14233 var list = fiber.dependencies; | |
| 14234 | |
| 14235 if (list !== null) { | |
| 14236 nextFiber = fiber.child; | |
| 14237 var dependency = list.firstContext; | |
| 14238 | |
| 14239 while (dependency !== null) { | |
| 14240 // Check if the context matches. | |
| 14241 if (dependency.context === context) { | |
| 14242 // Match! Schedule an update on this fiber. | |
| 14243 if (fiber.tag === ClassComponent) { | |
| 14244 // Schedule a force update on the work-in-progress. | |
| 14245 var lane = pickArbitraryLane(renderLanes); | |
| 14246 var update = createUpdate(NoTimestamp, lane); | |
| 14247 update.tag = ForceUpdate; // TODO: Because we don't have a work-in-progress, this will add the | |
| 14248 // update to the current fiber, too, which means it will persist even if | |
| 14249 // this render is thrown away. Since it's a race condition, not sure it's | |
| 14250 // worth fixing. | |
| 14251 // Inlined `enqueueUpdate` to remove interleaved update check | |
| 14252 | |
| 14253 var updateQueue = fiber.updateQueue; | |
| 14254 | |
| 14255 if (updateQueue === null) ; else { | |
| 14256 var sharedQueue = updateQueue.shared; | |
| 14257 var pending = sharedQueue.pending; | |
| 14258 | |
| 14259 if (pending === null) { | |
| 14260 // This is the first update. Create a circular list. | |
| 14261 update.next = update; | |
| 14262 } else { | |
| 14263 update.next = pending.next; | |
| 14264 pending.next = update; | |
| 14265 } | |
| 14266 | |
| 14267 sharedQueue.pending = update; | |
| 14268 } | |
| 14269 } | |
| 14270 | |
| 14271 fiber.lanes = mergeLanes(fiber.lanes, renderLanes); | |
| 14272 var alternate = fiber.alternate; | |
| 14273 | |
| 14274 if (alternate !== null) { | |
| 14275 alternate.lanes = mergeLanes(alternate.lanes, renderLanes); | |
| 14276 } | |
| 14277 | |
| 14278 scheduleContextWorkOnParentPath(fiber.return, renderLanes, workInProgress); // Mark the updated lanes on the list, too. | |
| 14279 | |
| 14280 list.lanes = mergeLanes(list.lanes, renderLanes); // Since we already found a match, we can stop traversing the | |
| 14281 // dependency list. | |
| 14282 | |
| 14283 break; | |
| 14284 } | |
| 14285 | |
| 14286 dependency = dependency.next; | |
| 14287 } | |
| 14288 } else if (fiber.tag === ContextProvider) { | |
| 14289 // Don't scan deeper if this is a matching provider | |
| 14290 nextFiber = fiber.type === workInProgress.type ? null : fiber.child; | |
| 14291 } else if (fiber.tag === DehydratedFragment) { | |
| 14292 // If a dehydrated suspense boundary is in this subtree, we don't know | |
| 14293 // if it will have any context consumers in it. The best we can do is | |
| 14294 // mark it as having updates. | |
| 14295 var parentSuspense = fiber.return; | |
| 14296 | |
| 14297 if (parentSuspense === null) { | |
| 14298 throw new Error('We just came from a parent so we must have had a parent. This is a bug in React.'); | |
| 14299 } | |
| 14300 | |
| 14301 parentSuspense.lanes = mergeLanes(parentSuspense.lanes, renderLanes); | |
| 14302 var _alternate = parentSuspense.alternate; | |
| 14303 | |
| 14304 if (_alternate !== null) { | |
| 14305 _alternate.lanes = mergeLanes(_alternate.lanes, renderLanes); | |
| 14306 } // This is intentionally passing this fiber as the parent | |
| 14307 // because we want to schedule this fiber as having work | |
| 14308 // on its children. We'll use the childLanes on | |
| 14309 // this fiber to indicate that a context has changed. | |
| 14310 | |
| 14311 | |
| 14312 scheduleContextWorkOnParentPath(parentSuspense, renderLanes, workInProgress); | |
| 14313 nextFiber = fiber.sibling; | |
| 14314 } else { | |
| 14315 // Traverse down. | |
| 14316 nextFiber = fiber.child; | |
| 14317 } | |
| 14318 | |
| 14319 if (nextFiber !== null) { | |
| 14320 // Set the return pointer of the child to the work-in-progress fiber. | |
| 14321 nextFiber.return = fiber; | |
| 14322 } else { | |
| 14323 // No child. Traverse to next sibling. | |
| 14324 nextFiber = fiber; | |
| 14325 | |
| 14326 while (nextFiber !== null) { | |
| 14327 if (nextFiber === workInProgress) { | |
| 14328 // We're back to the root of this subtree. Exit. | |
| 14329 nextFiber = null; | |
| 14330 break; | |
| 14331 } | |
| 14332 | |
| 14333 var sibling = nextFiber.sibling; | |
| 14334 | |
| 14335 if (sibling !== null) { | |
| 14336 // Set the return pointer of the sibling to the work-in-progress fiber. | |
| 14337 sibling.return = nextFiber.return; | |
| 14338 nextFiber = sibling; | |
| 14339 break; | |
| 14340 } // No more siblings. Traverse up. | |
| 14341 | |
| 14342 | |
| 14343 nextFiber = nextFiber.return; | |
| 14344 } | |
| 14345 } | |
| 14346 | |
| 14347 fiber = nextFiber; | |
| 14348 } | |
| 14349 } | |
| 14350 function prepareToReadContext(workInProgress, renderLanes) { | |
| 14351 currentlyRenderingFiber = workInProgress; | |
| 14352 lastContextDependency = null; | |
| 14353 lastFullyObservedContext = null; | |
| 14354 var dependencies = workInProgress.dependencies; | |
| 14355 | |
| 14356 if (dependencies !== null) { | |
| 14357 { | |
| 14358 var firstContext = dependencies.firstContext; | |
| 14359 | |
| 14360 if (firstContext !== null) { | |
| 14361 if (includesSomeLane(dependencies.lanes, renderLanes)) { | |
| 14362 // Context list has a pending update. Mark that this fiber performed work. | |
| 14363 markWorkInProgressReceivedUpdate(); | |
| 14364 } // Reset the work-in-progress list | |
| 14365 | |
| 14366 | |
| 14367 dependencies.firstContext = null; | |
| 14368 } | |
| 14369 } | |
| 14370 } | |
| 14371 } | |
| 14372 function readContext(context) { | |
| 14373 { | |
| 14374 // This warning would fire if you read context inside a Hook like useMemo. | |
| 14375 // Unlike the class check below, it's not enforced in production for perf. | |
| 14376 if (isDisallowedContextReadInDEV) { | |
| 14377 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().'); | |
| 14378 } | |
| 14379 } | |
| 14380 | |
| 14381 var value = context._currentValue ; | |
| 14382 | |
| 14383 if (lastFullyObservedContext === context) ; else { | |
| 14384 var contextItem = { | |
| 14385 context: context, | |
| 14386 memoizedValue: value, | |
| 14387 next: null | |
| 14388 }; | |
| 14389 | |
| 14390 if (lastContextDependency === null) { | |
| 14391 if (currentlyRenderingFiber === null) { | |
| 14392 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().'); | |
| 14393 } // This is the first dependency for this component. Create a new list. | |
| 14394 | |
| 14395 | |
| 14396 lastContextDependency = contextItem; | |
| 14397 currentlyRenderingFiber.dependencies = { | |
| 14398 lanes: NoLanes, | |
| 14399 firstContext: contextItem | |
| 14400 }; | |
| 14401 } else { | |
| 14402 // Append a new context item. | |
| 14403 lastContextDependency = lastContextDependency.next = contextItem; | |
| 14404 } | |
| 14405 } | |
| 14406 | |
| 14407 return value; | |
| 14408 } | |
| 14409 | |
| 14410 // render. When this render exits, either because it finishes or because it is | |
| 14411 // interrupted, the interleaved updates will be transferred onto the main part | |
| 14412 // of the queue. | |
| 14413 | |
| 14414 var concurrentQueues = null; | |
| 14415 function pushConcurrentUpdateQueue(queue) { | |
| 14416 if (concurrentQueues === null) { | |
| 14417 concurrentQueues = [queue]; | |
| 14418 } else { | |
| 14419 concurrentQueues.push(queue); | |
| 14420 } | |
| 14421 } | |
| 14422 function finishQueueingConcurrentUpdates() { | |
| 14423 // Transfer the interleaved updates onto the main queue. Each queue has a | |
| 14424 // `pending` field and an `interleaved` field. When they are not null, they | |
| 14425 // point to the last node in a circular linked list. We need to append the | |
| 14426 // interleaved list to the end of the pending list by joining them into a | |
| 14427 // single, circular list. | |
| 14428 if (concurrentQueues !== null) { | |
| 14429 for (var i = 0; i < concurrentQueues.length; i++) { | |
| 14430 var queue = concurrentQueues[i]; | |
| 14431 var lastInterleavedUpdate = queue.interleaved; | |
| 14432 | |
| 14433 if (lastInterleavedUpdate !== null) { | |
| 14434 queue.interleaved = null; | |
| 14435 var firstInterleavedUpdate = lastInterleavedUpdate.next; | |
| 14436 var lastPendingUpdate = queue.pending; | |
| 14437 | |
| 14438 if (lastPendingUpdate !== null) { | |
| 14439 var firstPendingUpdate = lastPendingUpdate.next; | |
| 14440 lastPendingUpdate.next = firstInterleavedUpdate; | |
| 14441 lastInterleavedUpdate.next = firstPendingUpdate; | |
| 14442 } | |
| 14443 | |
| 14444 queue.pending = lastInterleavedUpdate; | |
| 14445 } | |
| 14446 } | |
| 14447 | |
| 14448 concurrentQueues = null; | |
| 14449 } | |
| 14450 } | |
| 14451 function enqueueConcurrentHookUpdate(fiber, queue, update, lane) { | |
| 14452 var interleaved = queue.interleaved; | |
| 14453 | |
| 14454 if (interleaved === null) { | |
| 14455 // This is the first update. Create a circular list. | |
| 14456 update.next = update; // At the end of the current render, this queue's interleaved updates will | |
| 14457 // be transferred to the pending queue. | |
| 14458 | |
| 14459 pushConcurrentUpdateQueue(queue); | |
| 14460 } else { | |
| 14461 update.next = interleaved.next; | |
| 14462 interleaved.next = update; | |
| 14463 } | |
| 14464 | |
| 14465 queue.interleaved = update; | |
| 14466 return markUpdateLaneFromFiberToRoot(fiber, lane); | |
| 14467 } | |
| 14468 function enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane) { | |
| 14469 var interleaved = queue.interleaved; | |
| 14470 | |
| 14471 if (interleaved === null) { | |
| 14472 // This is the first update. Create a circular list. | |
| 14473 update.next = update; // At the end of the current render, this queue's interleaved updates will | |
| 14474 // be transferred to the pending queue. | |
| 14475 | |
| 14476 pushConcurrentUpdateQueue(queue); | |
| 14477 } else { | |
| 14478 update.next = interleaved.next; | |
| 14479 interleaved.next = update; | |
| 14480 } | |
| 14481 | |
| 14482 queue.interleaved = update; | |
| 14483 } | |
| 14484 function enqueueConcurrentClassUpdate(fiber, queue, update, lane) { | |
| 14485 var interleaved = queue.interleaved; | |
| 14486 | |
| 14487 if (interleaved === null) { | |
| 14488 // This is the first update. Create a circular list. | |
| 14489 update.next = update; // At the end of the current render, this queue's interleaved updates will | |
| 14490 // be transferred to the pending queue. | |
| 14491 | |
| 14492 pushConcurrentUpdateQueue(queue); | |
| 14493 } else { | |
| 14494 update.next = interleaved.next; | |
| 14495 interleaved.next = update; | |
| 14496 } | |
| 14497 | |
| 14498 queue.interleaved = update; | |
| 14499 return markUpdateLaneFromFiberToRoot(fiber, lane); | |
| 14500 } | |
| 14501 function enqueueConcurrentRenderForLane(fiber, lane) { | |
| 14502 return markUpdateLaneFromFiberToRoot(fiber, lane); | |
| 14503 } // Calling this function outside this module should only be done for backwards | |
| 14504 // compatibility and should always be accompanied by a warning. | |
| 14505 | |
| 14506 var unsafe_markUpdateLaneFromFiberToRoot = markUpdateLaneFromFiberToRoot; | |
| 14507 | |
| 14508 function markUpdateLaneFromFiberToRoot(sourceFiber, lane) { | |
| 14509 // Update the source fiber's lanes | |
| 14510 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, lane); | |
| 14511 var alternate = sourceFiber.alternate; | |
| 14512 | |
| 14513 if (alternate !== null) { | |
| 14514 alternate.lanes = mergeLanes(alternate.lanes, lane); | |
| 14515 } | |
| 14516 | |
| 14517 { | |
| 14518 if (alternate === null && (sourceFiber.flags & (Placement | Hydrating)) !== NoFlags) { | |
| 14519 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber); | |
| 14520 } | |
| 14521 } // Walk the parent path to the root and update the child lanes. | |
| 14522 | |
| 14523 | |
| 14524 var node = sourceFiber; | |
| 14525 var parent = sourceFiber.return; | |
| 14526 | |
| 14527 while (parent !== null) { | |
| 14528 parent.childLanes = mergeLanes(parent.childLanes, lane); | |
| 14529 alternate = parent.alternate; | |
| 14530 | |
| 14531 if (alternate !== null) { | |
| 14532 alternate.childLanes = mergeLanes(alternate.childLanes, lane); | |
| 14533 } else { | |
| 14534 { | |
| 14535 if ((parent.flags & (Placement | Hydrating)) !== NoFlags) { | |
| 14536 warnAboutUpdateOnNotYetMountedFiberInDEV(sourceFiber); | |
| 14537 } | |
| 14538 } | |
| 14539 } | |
| 14540 | |
| 14541 node = parent; | |
| 14542 parent = parent.return; | |
| 14543 } | |
| 14544 | |
| 14545 if (node.tag === HostRoot) { | |
| 14546 var root = node.stateNode; | |
| 14547 return root; | |
| 14548 } else { | |
| 14549 return null; | |
| 14550 } | |
| 14551 } | |
| 14552 | |
| 14553 var UpdateState = 0; | |
| 14554 var ReplaceState = 1; | |
| 14555 var ForceUpdate = 2; | |
| 14556 var CaptureUpdate = 3; // Global state that is reset at the beginning of calling `processUpdateQueue`. | |
| 14557 // It should only be read right after calling `processUpdateQueue`, via | |
| 14558 // `checkHasForceUpdateAfterProcessing`. | |
| 14559 | |
| 14560 var hasForceUpdate = false; | |
| 14561 var didWarnUpdateInsideUpdate; | |
| 14562 var currentlyProcessingQueue; | |
| 14563 | |
| 14564 { | |
| 14565 didWarnUpdateInsideUpdate = false; | |
| 14566 currentlyProcessingQueue = null; | |
| 14567 } | |
| 14568 | |
| 14569 function initializeUpdateQueue(fiber) { | |
| 14570 var queue = { | |
| 14571 baseState: fiber.memoizedState, | |
| 14572 firstBaseUpdate: null, | |
| 14573 lastBaseUpdate: null, | |
| 14574 shared: { | |
| 14575 pending: null, | |
| 14576 interleaved: null, | |
| 14577 lanes: NoLanes | |
| 14578 }, | |
| 14579 effects: null | |
| 14580 }; | |
| 14581 fiber.updateQueue = queue; | |
| 14582 } | |
| 14583 function cloneUpdateQueue(current, workInProgress) { | |
| 14584 // Clone the update queue from current. Unless it's already a clone. | |
| 14585 var queue = workInProgress.updateQueue; | |
| 14586 var currentQueue = current.updateQueue; | |
| 14587 | |
| 14588 if (queue === currentQueue) { | |
| 14589 var clone = { | |
| 14590 baseState: currentQueue.baseState, | |
| 14591 firstBaseUpdate: currentQueue.firstBaseUpdate, | |
| 14592 lastBaseUpdate: currentQueue.lastBaseUpdate, | |
| 14593 shared: currentQueue.shared, | |
| 14594 effects: currentQueue.effects | |
| 14595 }; | |
| 14596 workInProgress.updateQueue = clone; | |
| 14597 } | |
| 14598 } | |
| 14599 function createUpdate(eventTime, lane) { | |
| 14600 var update = { | |
| 14601 eventTime: eventTime, | |
| 14602 lane: lane, | |
| 14603 tag: UpdateState, | |
| 14604 payload: null, | |
| 14605 callback: null, | |
| 14606 next: null | |
| 14607 }; | |
| 14608 return update; | |
| 14609 } | |
| 14610 function enqueueUpdate(fiber, update, lane) { | |
| 14611 var updateQueue = fiber.updateQueue; | |
| 14612 | |
| 14613 if (updateQueue === null) { | |
| 14614 // Only occurs if the fiber has been unmounted. | |
| 14615 return null; | |
| 14616 } | |
| 14617 | |
| 14618 var sharedQueue = updateQueue.shared; | |
| 14619 | |
| 14620 { | |
| 14621 if (currentlyProcessingQueue === sharedQueue && !didWarnUpdateInsideUpdate) { | |
| 14622 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.'); | |
| 14623 | |
| 14624 didWarnUpdateInsideUpdate = true; | |
| 14625 } | |
| 14626 } | |
| 14627 | |
| 14628 if (isUnsafeClassRenderPhaseUpdate()) { | |
| 14629 // This is an unsafe render phase update. Add directly to the update | |
| 14630 // queue so we can process it immediately during the current render. | |
| 14631 var pending = sharedQueue.pending; | |
| 14632 | |
| 14633 if (pending === null) { | |
| 14634 // This is the first update. Create a circular list. | |
| 14635 update.next = update; | |
| 14636 } else { | |
| 14637 update.next = pending.next; | |
| 14638 pending.next = update; | |
| 14639 } | |
| 14640 | |
| 14641 sharedQueue.pending = update; // Update the childLanes even though we're most likely already rendering | |
| 14642 // this fiber. This is for backwards compatibility in the case where you | |
| 14643 // update a different component during render phase than the one that is | |
| 14644 // currently renderings (a pattern that is accompanied by a warning). | |
| 14645 | |
| 14646 return unsafe_markUpdateLaneFromFiberToRoot(fiber, lane); | |
| 14647 } else { | |
| 14648 return enqueueConcurrentClassUpdate(fiber, sharedQueue, update, lane); | |
| 14649 } | |
| 14650 } | |
| 14651 function entangleTransitions(root, fiber, lane) { | |
| 14652 var updateQueue = fiber.updateQueue; | |
| 14653 | |
| 14654 if (updateQueue === null) { | |
| 14655 // Only occurs if the fiber has been unmounted. | |
| 14656 return; | |
| 14657 } | |
| 14658 | |
| 14659 var sharedQueue = updateQueue.shared; | |
| 14660 | |
| 14661 if (isTransitionLane(lane)) { | |
| 14662 var queueLanes = sharedQueue.lanes; // If any entangled lanes are no longer pending on the root, then they must | |
| 14663 // have finished. We can remove them from the shared queue, which represents | |
| 14664 // a superset of the actually pending lanes. In some cases we may entangle | |
| 14665 // more than we need to, but that's OK. In fact it's worse if we *don't* | |
| 14666 // entangle when we should. | |
| 14667 | |
| 14668 queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes. | |
| 14669 | |
| 14670 var newQueueLanes = mergeLanes(queueLanes, lane); | |
| 14671 sharedQueue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if | |
| 14672 // the lane finished since the last time we entangled it. So we need to | |
| 14673 // entangle it again, just to be sure. | |
| 14674 | |
| 14675 markRootEntangled(root, newQueueLanes); | |
| 14676 } | |
| 14677 } | |
| 14678 function enqueueCapturedUpdate(workInProgress, capturedUpdate) { | |
| 14679 // Captured updates are updates that are thrown by a child during the render | |
| 14680 // phase. They should be discarded if the render is aborted. Therefore, | |
| 14681 // we should only put them on the work-in-progress queue, not the current one. | |
| 14682 var queue = workInProgress.updateQueue; // Check if the work-in-progress queue is a clone. | |
| 14683 | |
| 14684 var current = workInProgress.alternate; | |
| 14685 | |
| 14686 if (current !== null) { | |
| 14687 var currentQueue = current.updateQueue; | |
| 14688 | |
| 14689 if (queue === currentQueue) { | |
| 14690 // The work-in-progress queue is the same as current. This happens when | |
| 14691 // we bail out on a parent fiber that then captures an error thrown by | |
| 14692 // a child. Since we want to append the update only to the work-in | |
| 14693 // -progress queue, we need to clone the updates. We usually clone during | |
| 14694 // processUpdateQueue, but that didn't happen in this case because we | |
| 14695 // skipped over the parent when we bailed out. | |
| 14696 var newFirst = null; | |
| 14697 var newLast = null; | |
| 14698 var firstBaseUpdate = queue.firstBaseUpdate; | |
| 14699 | |
| 14700 if (firstBaseUpdate !== null) { | |
| 14701 // Loop through the updates and clone them. | |
| 14702 var update = firstBaseUpdate; | |
| 14703 | |
| 14704 do { | |
| 14705 var clone = { | |
| 14706 eventTime: update.eventTime, | |
| 14707 lane: update.lane, | |
| 14708 tag: update.tag, | |
| 14709 payload: update.payload, | |
| 14710 callback: update.callback, | |
| 14711 next: null | |
| 14712 }; | |
| 14713 | |
| 14714 if (newLast === null) { | |
| 14715 newFirst = newLast = clone; | |
| 14716 } else { | |
| 14717 newLast.next = clone; | |
| 14718 newLast = clone; | |
| 14719 } | |
| 14720 | |
| 14721 update = update.next; | |
| 14722 } while (update !== null); // Append the captured update the end of the cloned list. | |
| 14723 | |
| 14724 | |
| 14725 if (newLast === null) { | |
| 14726 newFirst = newLast = capturedUpdate; | |
| 14727 } else { | |
| 14728 newLast.next = capturedUpdate; | |
| 14729 newLast = capturedUpdate; | |
| 14730 } | |
| 14731 } else { | |
| 14732 // There are no base updates. | |
| 14733 newFirst = newLast = capturedUpdate; | |
| 14734 } | |
| 14735 | |
| 14736 queue = { | |
| 14737 baseState: currentQueue.baseState, | |
| 14738 firstBaseUpdate: newFirst, | |
| 14739 lastBaseUpdate: newLast, | |
| 14740 shared: currentQueue.shared, | |
| 14741 effects: currentQueue.effects | |
| 14742 }; | |
| 14743 workInProgress.updateQueue = queue; | |
| 14744 return; | |
| 14745 } | |
| 14746 } // Append the update to the end of the list. | |
| 14747 | |
| 14748 | |
| 14749 var lastBaseUpdate = queue.lastBaseUpdate; | |
| 14750 | |
| 14751 if (lastBaseUpdate === null) { | |
| 14752 queue.firstBaseUpdate = capturedUpdate; | |
| 14753 } else { | |
| 14754 lastBaseUpdate.next = capturedUpdate; | |
| 14755 } | |
| 14756 | |
| 14757 queue.lastBaseUpdate = capturedUpdate; | |
| 14758 } | |
| 14759 | |
| 14760 function getStateFromUpdate(workInProgress, queue, update, prevState, nextProps, instance) { | |
| 14761 switch (update.tag) { | |
| 14762 case ReplaceState: | |
| 14763 { | |
| 14764 var payload = update.payload; | |
| 14765 | |
| 14766 if (typeof payload === 'function') { | |
| 14767 // Updater function | |
| 14768 { | |
| 14769 enterDisallowedContextReadInDEV(); | |
| 14770 } | |
| 14771 | |
| 14772 var nextState = payload.call(instance, prevState, nextProps); | |
| 14773 | |
| 14774 { | |
| 14775 if ( workInProgress.mode & StrictLegacyMode) { | |
| 14776 setIsStrictModeForDevtools(true); | |
| 14777 | |
| 14778 try { | |
| 14779 payload.call(instance, prevState, nextProps); | |
| 14780 } finally { | |
| 14781 setIsStrictModeForDevtools(false); | |
| 14782 } | |
| 14783 } | |
| 14784 | |
| 14785 exitDisallowedContextReadInDEV(); | |
| 14786 } | |
| 14787 | |
| 14788 return nextState; | |
| 14789 } // State object | |
| 14790 | |
| 14791 | |
| 14792 return payload; | |
| 14793 } | |
| 14794 | |
| 14795 case CaptureUpdate: | |
| 14796 { | |
| 14797 workInProgress.flags = workInProgress.flags & ~ShouldCapture | DidCapture; | |
| 14798 } | |
| 14799 // Intentional fallthrough | |
| 14800 | |
| 14801 case UpdateState: | |
| 14802 { | |
| 14803 var _payload = update.payload; | |
| 14804 var partialState; | |
| 14805 | |
| 14806 if (typeof _payload === 'function') { | |
| 14807 // Updater function | |
| 14808 { | |
| 14809 enterDisallowedContextReadInDEV(); | |
| 14810 } | |
| 14811 | |
| 14812 partialState = _payload.call(instance, prevState, nextProps); | |
| 14813 | |
| 14814 { | |
| 14815 if ( workInProgress.mode & StrictLegacyMode) { | |
| 14816 setIsStrictModeForDevtools(true); | |
| 14817 | |
| 14818 try { | |
| 14819 _payload.call(instance, prevState, nextProps); | |
| 14820 } finally { | |
| 14821 setIsStrictModeForDevtools(false); | |
| 14822 } | |
| 14823 } | |
| 14824 | |
| 14825 exitDisallowedContextReadInDEV(); | |
| 14826 } | |
| 14827 } else { | |
| 14828 // Partial state object | |
| 14829 partialState = _payload; | |
| 14830 } | |
| 14831 | |
| 14832 if (partialState === null || partialState === undefined) { | |
| 14833 // Null and undefined are treated as no-ops. | |
| 14834 return prevState; | |
| 14835 } // Merge the partial state and the previous state. | |
| 14836 | |
| 14837 | |
| 14838 return assign({}, prevState, partialState); | |
| 14839 } | |
| 14840 | |
| 14841 case ForceUpdate: | |
| 14842 { | |
| 14843 hasForceUpdate = true; | |
| 14844 return prevState; | |
| 14845 } | |
| 14846 } | |
| 14847 | |
| 14848 return prevState; | |
| 14849 } | |
| 14850 | |
| 14851 function processUpdateQueue(workInProgress, props, instance, renderLanes) { | |
| 14852 // This is always non-null on a ClassComponent or HostRoot | |
| 14853 var queue = workInProgress.updateQueue; | |
| 14854 hasForceUpdate = false; | |
| 14855 | |
| 14856 { | |
| 14857 currentlyProcessingQueue = queue.shared; | |
| 14858 } | |
| 14859 | |
| 14860 var firstBaseUpdate = queue.firstBaseUpdate; | |
| 14861 var lastBaseUpdate = queue.lastBaseUpdate; // Check if there are pending updates. If so, transfer them to the base queue. | |
| 14862 | |
| 14863 var pendingQueue = queue.shared.pending; | |
| 14864 | |
| 14865 if (pendingQueue !== null) { | |
| 14866 queue.shared.pending = null; // The pending queue is circular. Disconnect the pointer between first | |
| 14867 // and last so that it's non-circular. | |
| 14868 | |
| 14869 var lastPendingUpdate = pendingQueue; | |
| 14870 var firstPendingUpdate = lastPendingUpdate.next; | |
| 14871 lastPendingUpdate.next = null; // Append pending updates to base queue | |
| 14872 | |
| 14873 if (lastBaseUpdate === null) { | |
| 14874 firstBaseUpdate = firstPendingUpdate; | |
| 14875 } else { | |
| 14876 lastBaseUpdate.next = firstPendingUpdate; | |
| 14877 } | |
| 14878 | |
| 14879 lastBaseUpdate = lastPendingUpdate; // If there's a current queue, and it's different from the base queue, then | |
| 14880 // we need to transfer the updates to that queue, too. Because the base | |
| 14881 // queue is a singly-linked list with no cycles, we can append to both | |
| 14882 // lists and take advantage of structural sharing. | |
| 14883 // TODO: Pass `current` as argument | |
| 14884 | |
| 14885 var current = workInProgress.alternate; | |
| 14886 | |
| 14887 if (current !== null) { | |
| 14888 // This is always non-null on a ClassComponent or HostRoot | |
| 14889 var currentQueue = current.updateQueue; | |
| 14890 var currentLastBaseUpdate = currentQueue.lastBaseUpdate; | |
| 14891 | |
| 14892 if (currentLastBaseUpdate !== lastBaseUpdate) { | |
| 14893 if (currentLastBaseUpdate === null) { | |
| 14894 currentQueue.firstBaseUpdate = firstPendingUpdate; | |
| 14895 } else { | |
| 14896 currentLastBaseUpdate.next = firstPendingUpdate; | |
| 14897 } | |
| 14898 | |
| 14899 currentQueue.lastBaseUpdate = lastPendingUpdate; | |
| 14900 } | |
| 14901 } | |
| 14902 } // These values may change as we process the queue. | |
| 14903 | |
| 14904 | |
| 14905 if (firstBaseUpdate !== null) { | |
| 14906 // Iterate through the list of updates to compute the result. | |
| 14907 var newState = queue.baseState; // TODO: Don't need to accumulate this. Instead, we can remove renderLanes | |
| 14908 // from the original lanes. | |
| 14909 | |
| 14910 var newLanes = NoLanes; | |
| 14911 var newBaseState = null; | |
| 14912 var newFirstBaseUpdate = null; | |
| 14913 var newLastBaseUpdate = null; | |
| 14914 var update = firstBaseUpdate; | |
| 14915 | |
| 14916 do { | |
| 14917 var updateLane = update.lane; | |
| 14918 var updateEventTime = update.eventTime; | |
| 14919 | |
| 14920 if (!isSubsetOfLanes(renderLanes, updateLane)) { | |
| 14921 // Priority is insufficient. Skip this update. If this is the first | |
| 14922 // skipped update, the previous update/state is the new base | |
| 14923 // update/state. | |
| 14924 var clone = { | |
| 14925 eventTime: updateEventTime, | |
| 14926 lane: updateLane, | |
| 14927 tag: update.tag, | |
| 14928 payload: update.payload, | |
| 14929 callback: update.callback, | |
| 14930 next: null | |
| 14931 }; | |
| 14932 | |
| 14933 if (newLastBaseUpdate === null) { | |
| 14934 newFirstBaseUpdate = newLastBaseUpdate = clone; | |
| 14935 newBaseState = newState; | |
| 14936 } else { | |
| 14937 newLastBaseUpdate = newLastBaseUpdate.next = clone; | |
| 14938 } // Update the remaining priority in the queue. | |
| 14939 | |
| 14940 | |
| 14941 newLanes = mergeLanes(newLanes, updateLane); | |
| 14942 } else { | |
| 14943 // This update does have sufficient priority. | |
| 14944 if (newLastBaseUpdate !== null) { | |
| 14945 var _clone = { | |
| 14946 eventTime: updateEventTime, | |
| 14947 // This update is going to be committed so we never want uncommit | |
| 14948 // it. Using NoLane works because 0 is a subset of all bitmasks, so | |
| 14949 // this will never be skipped by the check above. | |
| 14950 lane: NoLane, | |
| 14951 tag: update.tag, | |
| 14952 payload: update.payload, | |
| 14953 callback: update.callback, | |
| 14954 next: null | |
| 14955 }; | |
| 14956 newLastBaseUpdate = newLastBaseUpdate.next = _clone; | |
| 14957 } // Process this update. | |
| 14958 | |
| 14959 | |
| 14960 newState = getStateFromUpdate(workInProgress, queue, update, newState, props, instance); | |
| 14961 var callback = update.callback; | |
| 14962 | |
| 14963 if (callback !== null && // If the update was already committed, we should not queue its | |
| 14964 // callback again. | |
| 14965 update.lane !== NoLane) { | |
| 14966 workInProgress.flags |= Callback; | |
| 14967 var effects = queue.effects; | |
| 14968 | |
| 14969 if (effects === null) { | |
| 14970 queue.effects = [update]; | |
| 14971 } else { | |
| 14972 effects.push(update); | |
| 14973 } | |
| 14974 } | |
| 14975 } | |
| 14976 | |
| 14977 update = update.next; | |
| 14978 | |
| 14979 if (update === null) { | |
| 14980 pendingQueue = queue.shared.pending; | |
| 14981 | |
| 14982 if (pendingQueue === null) { | |
| 14983 break; | |
| 14984 } else { | |
| 14985 // An update was scheduled from inside a reducer. Add the new | |
| 14986 // pending updates to the end of the list and keep processing. | |
| 14987 var _lastPendingUpdate = pendingQueue; // Intentionally unsound. Pending updates form a circular list, but we | |
| 14988 // unravel them when transferring them to the base queue. | |
| 14989 | |
| 14990 var _firstPendingUpdate = _lastPendingUpdate.next; | |
| 14991 _lastPendingUpdate.next = null; | |
| 14992 update = _firstPendingUpdate; | |
| 14993 queue.lastBaseUpdate = _lastPendingUpdate; | |
| 14994 queue.shared.pending = null; | |
| 14995 } | |
| 14996 } | |
| 14997 } while (true); | |
| 14998 | |
| 14999 if (newLastBaseUpdate === null) { | |
| 15000 newBaseState = newState; | |
| 15001 } | |
| 15002 | |
| 15003 queue.baseState = newBaseState; | |
| 15004 queue.firstBaseUpdate = newFirstBaseUpdate; | |
| 15005 queue.lastBaseUpdate = newLastBaseUpdate; // Interleaved updates are stored on a separate queue. We aren't going to | |
| 15006 // process them during this render, but we do need to track which lanes | |
| 15007 // are remaining. | |
| 15008 | |
| 15009 var lastInterleaved = queue.shared.interleaved; | |
| 15010 | |
| 15011 if (lastInterleaved !== null) { | |
| 15012 var interleaved = lastInterleaved; | |
| 15013 | |
| 15014 do { | |
| 15015 newLanes = mergeLanes(newLanes, interleaved.lane); | |
| 15016 interleaved = interleaved.next; | |
| 15017 } while (interleaved !== lastInterleaved); | |
| 15018 } else if (firstBaseUpdate === null) { | |
| 15019 // `queue.lanes` is used for entangling transitions. We can set it back to | |
| 15020 // zero once the queue is empty. | |
| 15021 queue.shared.lanes = NoLanes; | |
| 15022 } // Set the remaining expiration time to be whatever is remaining in the queue. | |
| 15023 // This should be fine because the only two other things that contribute to | |
| 15024 // expiration time are props and context. We're already in the middle of the | |
| 15025 // begin phase by the time we start processing the queue, so we've already | |
| 15026 // dealt with the props. Context in components that specify | |
| 15027 // shouldComponentUpdate is tricky; but we'll have to account for | |
| 15028 // that regardless. | |
| 15029 | |
| 15030 | |
| 15031 markSkippedUpdateLanes(newLanes); | |
| 15032 workInProgress.lanes = newLanes; | |
| 15033 workInProgress.memoizedState = newState; | |
| 15034 } | |
| 15035 | |
| 15036 { | |
| 15037 currentlyProcessingQueue = null; | |
| 15038 } | |
| 15039 } | |
| 15040 | |
| 15041 function callCallback(callback, context) { | |
| 15042 if (typeof callback !== 'function') { | |
| 15043 throw new Error('Invalid argument passed as callback. Expected a function. Instead ' + ("received: " + callback)); | |
| 15044 } | |
| 15045 | |
| 15046 callback.call(context); | |
| 15047 } | |
| 15048 | |
| 15049 function resetHasForceUpdateBeforeProcessing() { | |
| 15050 hasForceUpdate = false; | |
| 15051 } | |
| 15052 function checkHasForceUpdateAfterProcessing() { | |
| 15053 return hasForceUpdate; | |
| 15054 } | |
| 15055 function commitUpdateQueue(finishedWork, finishedQueue, instance) { | |
| 15056 // Commit the effects | |
| 15057 var effects = finishedQueue.effects; | |
| 15058 finishedQueue.effects = null; | |
| 15059 | |
| 15060 if (effects !== null) { | |
| 15061 for (var i = 0; i < effects.length; i++) { | |
| 15062 var effect = effects[i]; | |
| 15063 var callback = effect.callback; | |
| 15064 | |
| 15065 if (callback !== null) { | |
| 15066 effect.callback = null; | |
| 15067 callCallback(callback, instance); | |
| 15068 } | |
| 15069 } | |
| 15070 } | |
| 15071 } | |
| 15072 | |
| 15073 var NO_CONTEXT = {}; | |
| 15074 var contextStackCursor$1 = createCursor(NO_CONTEXT); | |
| 15075 var contextFiberStackCursor = createCursor(NO_CONTEXT); | |
| 15076 var rootInstanceStackCursor = createCursor(NO_CONTEXT); | |
| 15077 | |
| 15078 function requiredContext(c) { | |
| 15079 if (c === NO_CONTEXT) { | |
| 15080 throw new Error('Expected host context to exist. This error is likely caused by a bug ' + 'in React. Please file an issue.'); | |
| 15081 } | |
| 15082 | |
| 15083 return c; | |
| 15084 } | |
| 15085 | |
| 15086 function getRootHostContainer() { | |
| 15087 var rootInstance = requiredContext(rootInstanceStackCursor.current); | |
| 15088 return rootInstance; | |
| 15089 } | |
| 15090 | |
| 15091 function pushHostContainer(fiber, nextRootInstance) { | |
| 15092 // Push current root instance onto the stack; | |
| 15093 // This allows us to reset root when portals are popped. | |
| 15094 push(rootInstanceStackCursor, nextRootInstance, fiber); // Track the context and the Fiber that provided it. | |
| 15095 // This enables us to pop only Fibers that provide unique contexts. | |
| 15096 | |
| 15097 push(contextFiberStackCursor, fiber, fiber); // Finally, we need to push the host context to the stack. | |
| 15098 // However, we can't just call getRootHostContext() and push it because | |
| 15099 // we'd have a different number of entries on the stack depending on | |
| 15100 // whether getRootHostContext() throws somewhere in renderer code or not. | |
| 15101 // So we push an empty value first. This lets us safely unwind on errors. | |
| 15102 | |
| 15103 push(contextStackCursor$1, NO_CONTEXT, fiber); | |
| 15104 var nextRootContext = getRootHostContext(nextRootInstance); // Now that we know this function doesn't throw, replace it. | |
| 15105 | |
| 15106 pop(contextStackCursor$1, fiber); | |
| 15107 push(contextStackCursor$1, nextRootContext, fiber); | |
| 15108 } | |
| 15109 | |
| 15110 function popHostContainer(fiber) { | |
| 15111 pop(contextStackCursor$1, fiber); | |
| 15112 pop(contextFiberStackCursor, fiber); | |
| 15113 pop(rootInstanceStackCursor, fiber); | |
| 15114 } | |
| 15115 | |
| 15116 function getHostContext() { | |
| 15117 var context = requiredContext(contextStackCursor$1.current); | |
| 15118 return context; | |
| 15119 } | |
| 15120 | |
| 15121 function pushHostContext(fiber) { | |
| 15122 var rootInstance = requiredContext(rootInstanceStackCursor.current); | |
| 15123 var context = requiredContext(contextStackCursor$1.current); | |
| 15124 var nextContext = getChildHostContext(context, fiber.type); // Don't push this Fiber's context unless it's unique. | |
| 15125 | |
| 15126 if (context === nextContext) { | |
| 15127 return; | |
| 15128 } // Track the context and the Fiber that provided it. | |
| 15129 // This enables us to pop only Fibers that provide unique contexts. | |
| 15130 | |
| 15131 | |
| 15132 push(contextFiberStackCursor, fiber, fiber); | |
| 15133 push(contextStackCursor$1, nextContext, fiber); | |
| 15134 } | |
| 15135 | |
| 15136 function popHostContext(fiber) { | |
| 15137 // Do not pop unless this Fiber provided the current context. | |
| 15138 // pushHostContext() only pushes Fibers that provide unique contexts. | |
| 15139 if (contextFiberStackCursor.current !== fiber) { | |
| 15140 return; | |
| 15141 } | |
| 15142 | |
| 15143 pop(contextStackCursor$1, fiber); | |
| 15144 pop(contextFiberStackCursor, fiber); | |
| 15145 } | |
| 15146 | |
| 15147 var DefaultSuspenseContext = 0; // The Suspense Context is split into two parts. The lower bits is | |
| 15148 // inherited deeply down the subtree. The upper bits only affect | |
| 15149 // this immediate suspense boundary and gets reset each new | |
| 15150 // boundary or suspense list. | |
| 15151 | |
| 15152 var SubtreeSuspenseContextMask = 1; // Subtree Flags: | |
| 15153 // InvisibleParentSuspenseContext indicates that one of our parent Suspense | |
| 15154 // boundaries is not currently showing visible main content. | |
| 15155 // Either because it is already showing a fallback or is not mounted at all. | |
| 15156 // We can use this to determine if it is desirable to trigger a fallback at | |
| 15157 // the parent. If not, then we might need to trigger undesirable boundaries | |
| 15158 // and/or suspend the commit to avoid hiding the parent content. | |
| 15159 | |
| 15160 var InvisibleParentSuspenseContext = 1; // Shallow Flags: | |
| 15161 // ForceSuspenseFallback can be used by SuspenseList to force newly added | |
| 15162 // items into their fallback state during one of the render passes. | |
| 15163 | |
| 15164 var ForceSuspenseFallback = 2; | |
| 15165 var suspenseStackCursor = createCursor(DefaultSuspenseContext); | |
| 15166 function hasSuspenseContext(parentContext, flag) { | |
| 15167 return (parentContext & flag) !== 0; | |
| 15168 } | |
| 15169 function setDefaultShallowSuspenseContext(parentContext) { | |
| 15170 return parentContext & SubtreeSuspenseContextMask; | |
| 15171 } | |
| 15172 function setShallowSuspenseContext(parentContext, shallowContext) { | |
| 15173 return parentContext & SubtreeSuspenseContextMask | shallowContext; | |
| 15174 } | |
| 15175 function addSubtreeSuspenseContext(parentContext, subtreeContext) { | |
| 15176 return parentContext | subtreeContext; | |
| 15177 } | |
| 15178 function pushSuspenseContext(fiber, newContext) { | |
| 15179 push(suspenseStackCursor, newContext, fiber); | |
| 15180 } | |
| 15181 function popSuspenseContext(fiber) { | |
| 15182 pop(suspenseStackCursor, fiber); | |
| 15183 } | |
| 15184 | |
| 15185 function shouldCaptureSuspense(workInProgress, hasInvisibleParent) { | |
| 15186 // If it was the primary children that just suspended, capture and render the | |
| 15187 // fallback. Otherwise, don't capture and bubble to the next boundary. | |
| 15188 var nextState = workInProgress.memoizedState; | |
| 15189 | |
| 15190 if (nextState !== null) { | |
| 15191 if (nextState.dehydrated !== null) { | |
| 15192 // A dehydrated boundary always captures. | |
| 15193 return true; | |
| 15194 } | |
| 15195 | |
| 15196 return false; | |
| 15197 } | |
| 15198 | |
| 15199 var props = workInProgress.memoizedProps; // Regular boundaries always capture. | |
| 15200 | |
| 15201 { | |
| 15202 return true; | |
| 15203 } // If it's a boundary we should avoid, then we prefer to bubble up to the | |
| 15204 } | |
| 15205 function findFirstSuspended(row) { | |
| 15206 var node = row; | |
| 15207 | |
| 15208 while (node !== null) { | |
| 15209 if (node.tag === SuspenseComponent) { | |
| 15210 var state = node.memoizedState; | |
| 15211 | |
| 15212 if (state !== null) { | |
| 15213 var dehydrated = state.dehydrated; | |
| 15214 | |
| 15215 if (dehydrated === null || isSuspenseInstancePending(dehydrated) || isSuspenseInstanceFallback(dehydrated)) { | |
| 15216 return node; | |
| 15217 } | |
| 15218 } | |
| 15219 } else if (node.tag === SuspenseListComponent && // revealOrder undefined can't be trusted because it don't | |
| 15220 // keep track of whether it suspended or not. | |
| 15221 node.memoizedProps.revealOrder !== undefined) { | |
| 15222 var didSuspend = (node.flags & DidCapture) !== NoFlags; | |
| 15223 | |
| 15224 if (didSuspend) { | |
| 15225 return node; | |
| 15226 } | |
| 15227 } else if (node.child !== null) { | |
| 15228 node.child.return = node; | |
| 15229 node = node.child; | |
| 15230 continue; | |
| 15231 } | |
| 15232 | |
| 15233 if (node === row) { | |
| 15234 return null; | |
| 15235 } | |
| 15236 | |
| 15237 while (node.sibling === null) { | |
| 15238 if (node.return === null || node.return === row) { | |
| 15239 return null; | |
| 15240 } | |
| 15241 | |
| 15242 node = node.return; | |
| 15243 } | |
| 15244 | |
| 15245 node.sibling.return = node.return; | |
| 15246 node = node.sibling; | |
| 15247 } | |
| 15248 | |
| 15249 return null; | |
| 15250 } | |
| 15251 | |
| 15252 var NoFlags$1 = | |
| 15253 /* */ | |
| 15254 0; // Represents whether effect should fire. | |
| 15255 | |
| 15256 var HasEffect = | |
| 15257 /* */ | |
| 15258 1; // Represents the phase in which the effect (not the clean-up) fires. | |
| 15259 | |
| 15260 var Insertion = | |
| 15261 /* */ | |
| 15262 2; | |
| 15263 var Layout = | |
| 15264 /* */ | |
| 15265 4; | |
| 15266 var Passive$1 = | |
| 15267 /* */ | |
| 15268 8; | |
| 15269 | |
| 15270 // and should be reset before starting a new render. | |
| 15271 // This tracks which mutable sources need to be reset after a render. | |
| 15272 | |
| 15273 var workInProgressSources = []; | |
| 15274 function resetWorkInProgressVersions() { | |
| 15275 for (var i = 0; i < workInProgressSources.length; i++) { | |
| 15276 var mutableSource = workInProgressSources[i]; | |
| 15277 | |
| 15278 { | |
| 15279 mutableSource._workInProgressVersionPrimary = null; | |
| 15280 } | |
| 15281 } | |
| 15282 | |
| 15283 workInProgressSources.length = 0; | |
| 15284 } | |
| 15285 // This ensures that the version used for server rendering matches the one | |
| 15286 // that is eventually read during hydration. | |
| 15287 // If they don't match there's a potential tear and a full deopt render is required. | |
| 15288 | |
| 15289 function registerMutableSourceForHydration(root, mutableSource) { | |
| 15290 var getVersion = mutableSource._getVersion; | |
| 15291 var version = getVersion(mutableSource._source); // TODO Clear this data once all pending hydration work is finished. | |
| 15292 // Retaining it forever may interfere with GC. | |
| 15293 | |
| 15294 if (root.mutableSourceEagerHydrationData == null) { | |
| 15295 root.mutableSourceEagerHydrationData = [mutableSource, version]; | |
| 15296 } else { | |
| 15297 root.mutableSourceEagerHydrationData.push(mutableSource, version); | |
| 15298 } | |
| 15299 } | |
| 15300 | |
| 15301 var ReactCurrentDispatcher$1 = ReactSharedInternals.ReactCurrentDispatcher, | |
| 15302 ReactCurrentBatchConfig$2 = ReactSharedInternals.ReactCurrentBatchConfig; | |
| 15303 var didWarnAboutMismatchedHooksForComponent; | |
| 15304 var didWarnUncachedGetSnapshot; | |
| 15305 | |
| 15306 { | |
| 15307 didWarnAboutMismatchedHooksForComponent = new Set(); | |
| 15308 } | |
| 15309 | |
| 15310 // These are set right before calling the component. | |
| 15311 var renderLanes = NoLanes; // The work-in-progress fiber. I've named it differently to distinguish it from | |
| 15312 // the work-in-progress hook. | |
| 15313 | |
| 15314 var currentlyRenderingFiber$1 = null; // Hooks are stored as a linked list on the fiber's memoizedState field. The | |
| 15315 // current hook list is the list that belongs to the current fiber. The | |
| 15316 // work-in-progress hook list is a new list that will be added to the | |
| 15317 // work-in-progress fiber. | |
| 15318 | |
| 15319 var currentHook = null; | |
| 15320 var workInProgressHook = null; // Whether an update was scheduled at any point during the render phase. This | |
| 15321 // does not get reset if we do another render pass; only when we're completely | |
| 15322 // finished evaluating this component. This is an optimization so we know | |
| 15323 // whether we need to clear render phase updates after a throw. | |
| 15324 | |
| 15325 var didScheduleRenderPhaseUpdate = false; // Where an update was scheduled only during the current render pass. This | |
| 15326 // gets reset after each attempt. | |
| 15327 // TODO: Maybe there's some way to consolidate this with | |
| 15328 // `didScheduleRenderPhaseUpdate`. Or with `numberOfReRenders`. | |
| 15329 | |
| 15330 var didScheduleRenderPhaseUpdateDuringThisPass = false; // Counts the number of useId hooks in this component. | |
| 15331 | |
| 15332 var localIdCounter = 0; // Used for ids that are generated completely client-side (i.e. not during | |
| 15333 // hydration). This counter is global, so client ids are not stable across | |
| 15334 // render attempts. | |
| 15335 | |
| 15336 var globalClientIdCounter = 0; | |
| 15337 var RE_RENDER_LIMIT = 25; // In DEV, this is the name of the currently executing primitive hook | |
| 15338 | |
| 15339 var currentHookNameInDev = null; // In DEV, this list ensures that hooks are called in the same order between renders. | |
| 15340 // The list stores the order of hooks used during the initial render (mount). | |
| 15341 // Subsequent renders (updates) reference this list. | |
| 15342 | |
| 15343 var hookTypesDev = null; | |
| 15344 var hookTypesUpdateIndexDev = -1; // In DEV, this tracks whether currently rendering component needs to ignore | |
| 15345 // the dependencies for Hooks that need them (e.g. useEffect or useMemo). | |
| 15346 // When true, such Hooks will always be "remounted". Only used during hot reload. | |
| 15347 | |
| 15348 var ignorePreviousDependencies = false; | |
| 15349 | |
| 15350 function mountHookTypesDev() { | |
| 15351 { | |
| 15352 var hookName = currentHookNameInDev; | |
| 15353 | |
| 15354 if (hookTypesDev === null) { | |
| 15355 hookTypesDev = [hookName]; | |
| 15356 } else { | |
| 15357 hookTypesDev.push(hookName); | |
| 15358 } | |
| 15359 } | |
| 15360 } | |
| 15361 | |
| 15362 function updateHookTypesDev() { | |
| 15363 { | |
| 15364 var hookName = currentHookNameInDev; | |
| 15365 | |
| 15366 if (hookTypesDev !== null) { | |
| 15367 hookTypesUpdateIndexDev++; | |
| 15368 | |
| 15369 if (hookTypesDev[hookTypesUpdateIndexDev] !== hookName) { | |
| 15370 warnOnHookMismatchInDev(hookName); | |
| 15371 } | |
| 15372 } | |
| 15373 } | |
| 15374 } | |
| 15375 | |
| 15376 function checkDepsAreArrayDev(deps) { | |
| 15377 { | |
| 15378 if (deps !== undefined && deps !== null && !isArray(deps)) { | |
| 15379 // Verify deps, but only on mount to avoid extra checks. | |
| 15380 // It's unlikely their type would change as usually you define them inline. | |
| 15381 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); | |
| 15382 } | |
| 15383 } | |
| 15384 } | |
| 15385 | |
| 15386 function warnOnHookMismatchInDev(currentHookName) { | |
| 15387 { | |
| 15388 var componentName = getComponentNameFromFiber(currentlyRenderingFiber$1); | |
| 15389 | |
| 15390 if (!didWarnAboutMismatchedHooksForComponent.has(componentName)) { | |
| 15391 didWarnAboutMismatchedHooksForComponent.add(componentName); | |
| 15392 | |
| 15393 if (hookTypesDev !== null) { | |
| 15394 var table = ''; | |
| 15395 var secondColumnStart = 30; | |
| 15396 | |
| 15397 for (var i = 0; i <= hookTypesUpdateIndexDev; i++) { | |
| 15398 var oldHookName = hookTypesDev[i]; | |
| 15399 var newHookName = i === hookTypesUpdateIndexDev ? currentHookName : oldHookName; | |
| 15400 var row = i + 1 + ". " + oldHookName; // Extra space so second column lines up | |
| 15401 // lol @ IE not supporting String#repeat | |
| 15402 | |
| 15403 while (row.length < secondColumnStart) { | |
| 15404 row += ' '; | |
| 15405 } | |
| 15406 | |
| 15407 row += newHookName + '\n'; | |
| 15408 table += row; | |
| 15409 } | |
| 15410 | |
| 15411 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); | |
| 15412 } | |
| 15413 } | |
| 15414 } | |
| 15415 } | |
| 15416 | |
| 15417 function throwInvalidHookError() { | |
| 15418 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.'); | |
| 15419 } | |
| 15420 | |
| 15421 function areHookInputsEqual(nextDeps, prevDeps) { | |
| 15422 { | |
| 15423 if (ignorePreviousDependencies) { | |
| 15424 // Only true when this component is being hot reloaded. | |
| 15425 return false; | |
| 15426 } | |
| 15427 } | |
| 15428 | |
| 15429 if (prevDeps === null) { | |
| 15430 { | |
| 15431 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); | |
| 15432 } | |
| 15433 | |
| 15434 return false; | |
| 15435 } | |
| 15436 | |
| 15437 { | |
| 15438 // Don't bother comparing lengths in prod because these arrays should be | |
| 15439 // passed inline. | |
| 15440 if (nextDeps.length !== prevDeps.length) { | |
| 15441 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(', ') + "]"); | |
| 15442 } | |
| 15443 } | |
| 15444 | |
| 15445 for (var i = 0; i < prevDeps.length && i < nextDeps.length; i++) { | |
| 15446 if (objectIs(nextDeps[i], prevDeps[i])) { | |
| 15447 continue; | |
| 15448 } | |
| 15449 | |
| 15450 return false; | |
| 15451 } | |
| 15452 | |
| 15453 return true; | |
| 15454 } | |
| 15455 | |
| 15456 function renderWithHooks(current, workInProgress, Component, props, secondArg, nextRenderLanes) { | |
| 15457 renderLanes = nextRenderLanes; | |
| 15458 currentlyRenderingFiber$1 = workInProgress; | |
| 15459 | |
| 15460 { | |
| 15461 hookTypesDev = current !== null ? current._debugHookTypes : null; | |
| 15462 hookTypesUpdateIndexDev = -1; // Used for hot reloading: | |
| 15463 | |
| 15464 ignorePreviousDependencies = current !== null && current.type !== workInProgress.type; | |
| 15465 } | |
| 15466 | |
| 15467 workInProgress.memoizedState = null; | |
| 15468 workInProgress.updateQueue = null; | |
| 15469 workInProgress.lanes = NoLanes; // The following should have already been reset | |
| 15470 // currentHook = null; | |
| 15471 // workInProgressHook = null; | |
| 15472 // didScheduleRenderPhaseUpdate = false; | |
| 15473 // localIdCounter = 0; | |
| 15474 // TODO Warn if no hooks are used at all during mount, then some are used during update. | |
| 15475 // Currently we will identify the update render as a mount because memoizedState === null. | |
| 15476 // This is tricky because it's valid for certain types of components (e.g. React.lazy) | |
| 15477 // Using memoizedState to differentiate between mount/update only works if at least one stateful hook is used. | |
| 15478 // Non-stateful hooks (e.g. context) don't get added to memoizedState, | |
| 15479 // so memoizedState would be null during updates and mounts. | |
| 15480 | |
| 15481 { | |
| 15482 if (current !== null && current.memoizedState !== null) { | |
| 15483 ReactCurrentDispatcher$1.current = HooksDispatcherOnUpdateInDEV; | |
| 15484 } else if (hookTypesDev !== null) { | |
| 15485 // This dispatcher handles an edge case where a component is updating, | |
| 15486 // but no stateful hooks have been used. | |
| 15487 // We want to match the production code behavior (which will use HooksDispatcherOnMount), | |
| 15488 // but with the extra DEV validation to ensure hooks ordering hasn't changed. | |
| 15489 // This dispatcher does that. | |
| 15490 ReactCurrentDispatcher$1.current = HooksDispatcherOnMountWithHookTypesInDEV; | |
| 15491 } else { | |
| 15492 ReactCurrentDispatcher$1.current = HooksDispatcherOnMountInDEV; | |
| 15493 } | |
| 15494 } | |
| 15495 | |
| 15496 var children = Component(props, secondArg); // Check if there was a render phase update | |
| 15497 | |
| 15498 if (didScheduleRenderPhaseUpdateDuringThisPass) { | |
| 15499 // Keep rendering in a loop for as long as render phase updates continue to | |
| 15500 // be scheduled. Use a counter to prevent infinite loops. | |
| 15501 var numberOfReRenders = 0; | |
| 15502 | |
| 15503 do { | |
| 15504 didScheduleRenderPhaseUpdateDuringThisPass = false; | |
| 15505 localIdCounter = 0; | |
| 15506 | |
| 15507 if (numberOfReRenders >= RE_RENDER_LIMIT) { | |
| 15508 throw new Error('Too many re-renders. React limits the number of renders to prevent ' + 'an infinite loop.'); | |
| 15509 } | |
| 15510 | |
| 15511 numberOfReRenders += 1; | |
| 15512 | |
| 15513 { | |
| 15514 // Even when hot reloading, allow dependencies to stabilize | |
| 15515 // after first render to prevent infinite render phase updates. | |
| 15516 ignorePreviousDependencies = false; | |
| 15517 } // Start over from the beginning of the list | |
| 15518 | |
| 15519 | |
| 15520 currentHook = null; | |
| 15521 workInProgressHook = null; | |
| 15522 workInProgress.updateQueue = null; | |
| 15523 | |
| 15524 { | |
| 15525 // Also validate hook order for cascading updates. | |
| 15526 hookTypesUpdateIndexDev = -1; | |
| 15527 } | |
| 15528 | |
| 15529 ReactCurrentDispatcher$1.current = HooksDispatcherOnRerenderInDEV ; | |
| 15530 children = Component(props, secondArg); | |
| 15531 } while (didScheduleRenderPhaseUpdateDuringThisPass); | |
| 15532 } // We can assume the previous dispatcher is always this one, since we set it | |
| 15533 // at the beginning of the render phase and there's no re-entrance. | |
| 15534 | |
| 15535 | |
| 15536 ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; | |
| 15537 | |
| 15538 { | |
| 15539 workInProgress._debugHookTypes = hookTypesDev; | |
| 15540 } // This check uses currentHook so that it works the same in DEV and prod bundles. | |
| 15541 // hookTypesDev could catch more cases (e.g. context) but only in DEV bundles. | |
| 15542 | |
| 15543 | |
| 15544 var didRenderTooFewHooks = currentHook !== null && currentHook.next !== null; | |
| 15545 renderLanes = NoLanes; | |
| 15546 currentlyRenderingFiber$1 = null; | |
| 15547 currentHook = null; | |
| 15548 workInProgressHook = null; | |
| 15549 | |
| 15550 { | |
| 15551 currentHookNameInDev = null; | |
| 15552 hookTypesDev = null; | |
| 15553 hookTypesUpdateIndexDev = -1; // Confirm that a static flag was not added or removed since the last | |
| 15554 // render. If this fires, it suggests that we incorrectly reset the static | |
| 15555 // flags in some other part of the codebase. This has happened before, for | |
| 15556 // example, in the SuspenseList implementation. | |
| 15557 | |
| 15558 if (current !== null && (current.flags & StaticMask) !== (workInProgress.flags & StaticMask) && // Disable this warning in legacy mode, because legacy Suspense is weird | |
| 15559 // and creates false positives. To make this work in legacy mode, we'd | |
| 15560 // need to mark fibers that commit in an incomplete state, somehow. For | |
| 15561 // now I'll disable the warning that most of the bugs that would trigger | |
| 15562 // it are either exclusive to concurrent mode or exist in both. | |
| 15563 (current.mode & ConcurrentMode) !== NoMode) { | |
| 15564 error('Internal React error: Expected static flag was missing. Please ' + 'notify the React team.'); | |
| 15565 } | |
| 15566 } | |
| 15567 | |
| 15568 didScheduleRenderPhaseUpdate = false; // This is reset by checkDidRenderIdHook | |
| 15569 // localIdCounter = 0; | |
| 15570 | |
| 15571 if (didRenderTooFewHooks) { | |
| 15572 throw new Error('Rendered fewer hooks than expected. This may be caused by an accidental ' + 'early return statement.'); | |
| 15573 } | |
| 15574 | |
| 15575 return children; | |
| 15576 } | |
| 15577 function checkDidRenderIdHook() { | |
| 15578 // This should be called immediately after every renderWithHooks call. | |
| 15579 // Conceptually, it's part of the return value of renderWithHooks; it's only a | |
| 15580 // separate function to avoid using an array tuple. | |
| 15581 var didRenderIdHook = localIdCounter !== 0; | |
| 15582 localIdCounter = 0; | |
| 15583 return didRenderIdHook; | |
| 15584 } | |
| 15585 function bailoutHooks(current, workInProgress, lanes) { | |
| 15586 workInProgress.updateQueue = current.updateQueue; // TODO: Don't need to reset the flags here, because they're reset in the | |
| 15587 // complete phase (bubbleProperties). | |
| 15588 | |
| 15589 if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) { | |
| 15590 workInProgress.flags &= ~(MountPassiveDev | MountLayoutDev | Passive | Update); | |
| 15591 } else { | |
| 15592 workInProgress.flags &= ~(Passive | Update); | |
| 15593 } | |
| 15594 | |
| 15595 current.lanes = removeLanes(current.lanes, lanes); | |
| 15596 } | |
| 15597 function resetHooksAfterThrow() { | |
| 15598 // We can assume the previous dispatcher is always this one, since we set it | |
| 15599 // at the beginning of the render phase and there's no re-entrance. | |
| 15600 ReactCurrentDispatcher$1.current = ContextOnlyDispatcher; | |
| 15601 | |
| 15602 if (didScheduleRenderPhaseUpdate) { | |
| 15603 // There were render phase updates. These are only valid for this render | |
| 15604 // phase, which we are now aborting. Remove the updates from the queues so | |
| 15605 // they do not persist to the next render. Do not remove updates from hooks | |
| 15606 // that weren't processed. | |
| 15607 // | |
| 15608 // Only reset the updates from the queue if it has a clone. If it does | |
| 15609 // not have a clone, that means it wasn't processed, and the updates were | |
| 15610 // scheduled before we entered the render phase. | |
| 15611 var hook = currentlyRenderingFiber$1.memoizedState; | |
| 15612 | |
| 15613 while (hook !== null) { | |
| 15614 var queue = hook.queue; | |
| 15615 | |
| 15616 if (queue !== null) { | |
| 15617 queue.pending = null; | |
| 15618 } | |
| 15619 | |
| 15620 hook = hook.next; | |
| 15621 } | |
| 15622 | |
| 15623 didScheduleRenderPhaseUpdate = false; | |
| 15624 } | |
| 15625 | |
| 15626 renderLanes = NoLanes; | |
| 15627 currentlyRenderingFiber$1 = null; | |
| 15628 currentHook = null; | |
| 15629 workInProgressHook = null; | |
| 15630 | |
| 15631 { | |
| 15632 hookTypesDev = null; | |
| 15633 hookTypesUpdateIndexDev = -1; | |
| 15634 currentHookNameInDev = null; | |
| 15635 isUpdatingOpaqueValueInRenderPhase = false; | |
| 15636 } | |
| 15637 | |
| 15638 didScheduleRenderPhaseUpdateDuringThisPass = false; | |
| 15639 localIdCounter = 0; | |
| 15640 } | |
| 15641 | |
| 15642 function mountWorkInProgressHook() { | |
| 15643 var hook = { | |
| 15644 memoizedState: null, | |
| 15645 baseState: null, | |
| 15646 baseQueue: null, | |
| 15647 queue: null, | |
| 15648 next: null | |
| 15649 }; | |
| 15650 | |
| 15651 if (workInProgressHook === null) { | |
| 15652 // This is the first hook in the list | |
| 15653 currentlyRenderingFiber$1.memoizedState = workInProgressHook = hook; | |
| 15654 } else { | |
| 15655 // Append to the end of the list | |
| 15656 workInProgressHook = workInProgressHook.next = hook; | |
| 15657 } | |
| 15658 | |
| 15659 return workInProgressHook; | |
| 15660 } | |
| 15661 | |
| 15662 function updateWorkInProgressHook() { | |
| 15663 // This function is used both for updates and for re-renders triggered by a | |
| 15664 // render phase update. It assumes there is either a current hook we can | |
| 15665 // clone, or a work-in-progress hook from a previous render pass that we can | |
| 15666 // use as a base. When we reach the end of the base list, we must switch to | |
| 15667 // the dispatcher used for mounts. | |
| 15668 var nextCurrentHook; | |
| 15669 | |
| 15670 if (currentHook === null) { | |
| 15671 var current = currentlyRenderingFiber$1.alternate; | |
| 15672 | |
| 15673 if (current !== null) { | |
| 15674 nextCurrentHook = current.memoizedState; | |
| 15675 } else { | |
| 15676 nextCurrentHook = null; | |
| 15677 } | |
| 15678 } else { | |
| 15679 nextCurrentHook = currentHook.next; | |
| 15680 } | |
| 15681 | |
| 15682 var nextWorkInProgressHook; | |
| 15683 | |
| 15684 if (workInProgressHook === null) { | |
| 15685 nextWorkInProgressHook = currentlyRenderingFiber$1.memoizedState; | |
| 15686 } else { | |
| 15687 nextWorkInProgressHook = workInProgressHook.next; | |
| 15688 } | |
| 15689 | |
| 15690 if (nextWorkInProgressHook !== null) { | |
| 15691 // There's already a work-in-progress. Reuse it. | |
| 15692 workInProgressHook = nextWorkInProgressHook; | |
| 15693 nextWorkInProgressHook = workInProgressHook.next; | |
| 15694 currentHook = nextCurrentHook; | |
| 15695 } else { | |
| 15696 // Clone from the current hook. | |
| 15697 if (nextCurrentHook === null) { | |
| 15698 throw new Error('Rendered more hooks than during the previous render.'); | |
| 15699 } | |
| 15700 | |
| 15701 currentHook = nextCurrentHook; | |
| 15702 var newHook = { | |
| 15703 memoizedState: currentHook.memoizedState, | |
| 15704 baseState: currentHook.baseState, | |
| 15705 baseQueue: currentHook.baseQueue, | |
| 15706 queue: currentHook.queue, | |
| 15707 next: null | |
| 15708 }; | |
| 15709 | |
| 15710 if (workInProgressHook === null) { | |
| 15711 // This is the first hook in the list. | |
| 15712 currentlyRenderingFiber$1.memoizedState = workInProgressHook = newHook; | |
| 15713 } else { | |
| 15714 // Append to the end of the list. | |
| 15715 workInProgressHook = workInProgressHook.next = newHook; | |
| 15716 } | |
| 15717 } | |
| 15718 | |
| 15719 return workInProgressHook; | |
| 15720 } | |
| 15721 | |
| 15722 function createFunctionComponentUpdateQueue() { | |
| 15723 return { | |
| 15724 lastEffect: null, | |
| 15725 stores: null | |
| 15726 }; | |
| 15727 } | |
| 15728 | |
| 15729 function basicStateReducer(state, action) { | |
| 15730 // $FlowFixMe: Flow doesn't like mixed types | |
| 15731 return typeof action === 'function' ? action(state) : action; | |
| 15732 } | |
| 15733 | |
| 15734 function mountReducer(reducer, initialArg, init) { | |
| 15735 var hook = mountWorkInProgressHook(); | |
| 15736 var initialState; | |
| 15737 | |
| 15738 if (init !== undefined) { | |
| 15739 initialState = init(initialArg); | |
| 15740 } else { | |
| 15741 initialState = initialArg; | |
| 15742 } | |
| 15743 | |
| 15744 hook.memoizedState = hook.baseState = initialState; | |
| 15745 var queue = { | |
| 15746 pending: null, | |
| 15747 interleaved: null, | |
| 15748 lanes: NoLanes, | |
| 15749 dispatch: null, | |
| 15750 lastRenderedReducer: reducer, | |
| 15751 lastRenderedState: initialState | |
| 15752 }; | |
| 15753 hook.queue = queue; | |
| 15754 var dispatch = queue.dispatch = dispatchReducerAction.bind(null, currentlyRenderingFiber$1, queue); | |
| 15755 return [hook.memoizedState, dispatch]; | |
| 15756 } | |
| 15757 | |
| 15758 function updateReducer(reducer, initialArg, init) { | |
| 15759 var hook = updateWorkInProgressHook(); | |
| 15760 var queue = hook.queue; | |
| 15761 | |
| 15762 if (queue === null) { | |
| 15763 throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.'); | |
| 15764 } | |
| 15765 | |
| 15766 queue.lastRenderedReducer = reducer; | |
| 15767 var current = currentHook; // The last rebase update that is NOT part of the base state. | |
| 15768 | |
| 15769 var baseQueue = current.baseQueue; // The last pending update that hasn't been processed yet. | |
| 15770 | |
| 15771 var pendingQueue = queue.pending; | |
| 15772 | |
| 15773 if (pendingQueue !== null) { | |
| 15774 // We have new updates that haven't been processed yet. | |
| 15775 // We'll add them to the base queue. | |
| 15776 if (baseQueue !== null) { | |
| 15777 // Merge the pending queue and the base queue. | |
| 15778 var baseFirst = baseQueue.next; | |
| 15779 var pendingFirst = pendingQueue.next; | |
| 15780 baseQueue.next = pendingFirst; | |
| 15781 pendingQueue.next = baseFirst; | |
| 15782 } | |
| 15783 | |
| 15784 { | |
| 15785 if (current.baseQueue !== baseQueue) { | |
| 15786 // Internal invariant that should never happen, but feasibly could in | |
| 15787 // the future if we implement resuming, or some form of that. | |
| 15788 error('Internal error: Expected work-in-progress queue to be a clone. ' + 'This is a bug in React.'); | |
| 15789 } | |
| 15790 } | |
| 15791 | |
| 15792 current.baseQueue = baseQueue = pendingQueue; | |
| 15793 queue.pending = null; | |
| 15794 } | |
| 15795 | |
| 15796 if (baseQueue !== null) { | |
| 15797 // We have a queue to process. | |
| 15798 var first = baseQueue.next; | |
| 15799 var newState = current.baseState; | |
| 15800 var newBaseState = null; | |
| 15801 var newBaseQueueFirst = null; | |
| 15802 var newBaseQueueLast = null; | |
| 15803 var update = first; | |
| 15804 | |
| 15805 do { | |
| 15806 var updateLane = update.lane; | |
| 15807 | |
| 15808 if (!isSubsetOfLanes(renderLanes, updateLane)) { | |
| 15809 // Priority is insufficient. Skip this update. If this is the first | |
| 15810 // skipped update, the previous update/state is the new base | |
| 15811 // update/state. | |
| 15812 var clone = { | |
| 15813 lane: updateLane, | |
| 15814 action: update.action, | |
| 15815 hasEagerState: update.hasEagerState, | |
| 15816 eagerState: update.eagerState, | |
| 15817 next: null | |
| 15818 }; | |
| 15819 | |
| 15820 if (newBaseQueueLast === null) { | |
| 15821 newBaseQueueFirst = newBaseQueueLast = clone; | |
| 15822 newBaseState = newState; | |
| 15823 } else { | |
| 15824 newBaseQueueLast = newBaseQueueLast.next = clone; | |
| 15825 } // Update the remaining priority in the queue. | |
| 15826 // TODO: Don't need to accumulate this. Instead, we can remove | |
| 15827 // renderLanes from the original lanes. | |
| 15828 | |
| 15829 | |
| 15830 currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, updateLane); | |
| 15831 markSkippedUpdateLanes(updateLane); | |
| 15832 } else { | |
| 15833 // This update does have sufficient priority. | |
| 15834 if (newBaseQueueLast !== null) { | |
| 15835 var _clone = { | |
| 15836 // This update is going to be committed so we never want uncommit | |
| 15837 // it. Using NoLane works because 0 is a subset of all bitmasks, so | |
| 15838 // this will never be skipped by the check above. | |
| 15839 lane: NoLane, | |
| 15840 action: update.action, | |
| 15841 hasEagerState: update.hasEagerState, | |
| 15842 eagerState: update.eagerState, | |
| 15843 next: null | |
| 15844 }; | |
| 15845 newBaseQueueLast = newBaseQueueLast.next = _clone; | |
| 15846 } // Process this update. | |
| 15847 | |
| 15848 | |
| 15849 if (update.hasEagerState) { | |
| 15850 // If this update is a state update (not a reducer) and was processed eagerly, | |
| 15851 // we can use the eagerly computed state | |
| 15852 newState = update.eagerState; | |
| 15853 } else { | |
| 15854 var action = update.action; | |
| 15855 newState = reducer(newState, action); | |
| 15856 } | |
| 15857 } | |
| 15858 | |
| 15859 update = update.next; | |
| 15860 } while (update !== null && update !== first); | |
| 15861 | |
| 15862 if (newBaseQueueLast === null) { | |
| 15863 newBaseState = newState; | |
| 15864 } else { | |
| 15865 newBaseQueueLast.next = newBaseQueueFirst; | |
| 15866 } // Mark that the fiber performed work, but only if the new state is | |
| 15867 // different from the current state. | |
| 15868 | |
| 15869 | |
| 15870 if (!objectIs(newState, hook.memoizedState)) { | |
| 15871 markWorkInProgressReceivedUpdate(); | |
| 15872 } | |
| 15873 | |
| 15874 hook.memoizedState = newState; | |
| 15875 hook.baseState = newBaseState; | |
| 15876 hook.baseQueue = newBaseQueueLast; | |
| 15877 queue.lastRenderedState = newState; | |
| 15878 } // Interleaved updates are stored on a separate queue. We aren't going to | |
| 15879 // process them during this render, but we do need to track which lanes | |
| 15880 // are remaining. | |
| 15881 | |
| 15882 | |
| 15883 var lastInterleaved = queue.interleaved; | |
| 15884 | |
| 15885 if (lastInterleaved !== null) { | |
| 15886 var interleaved = lastInterleaved; | |
| 15887 | |
| 15888 do { | |
| 15889 var interleavedLane = interleaved.lane; | |
| 15890 currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, interleavedLane); | |
| 15891 markSkippedUpdateLanes(interleavedLane); | |
| 15892 interleaved = interleaved.next; | |
| 15893 } while (interleaved !== lastInterleaved); | |
| 15894 } else if (baseQueue === null) { | |
| 15895 // `queue.lanes` is used for entangling transitions. We can set it back to | |
| 15896 // zero once the queue is empty. | |
| 15897 queue.lanes = NoLanes; | |
| 15898 } | |
| 15899 | |
| 15900 var dispatch = queue.dispatch; | |
| 15901 return [hook.memoizedState, dispatch]; | |
| 15902 } | |
| 15903 | |
| 15904 function rerenderReducer(reducer, initialArg, init) { | |
| 15905 var hook = updateWorkInProgressHook(); | |
| 15906 var queue = hook.queue; | |
| 15907 | |
| 15908 if (queue === null) { | |
| 15909 throw new Error('Should have a queue. This is likely a bug in React. Please file an issue.'); | |
| 15910 } | |
| 15911 | |
| 15912 queue.lastRenderedReducer = reducer; // This is a re-render. Apply the new render phase updates to the previous | |
| 15913 // work-in-progress hook. | |
| 15914 | |
| 15915 var dispatch = queue.dispatch; | |
| 15916 var lastRenderPhaseUpdate = queue.pending; | |
| 15917 var newState = hook.memoizedState; | |
| 15918 | |
| 15919 if (lastRenderPhaseUpdate !== null) { | |
| 15920 // The queue doesn't persist past this render pass. | |
| 15921 queue.pending = null; | |
| 15922 var firstRenderPhaseUpdate = lastRenderPhaseUpdate.next; | |
| 15923 var update = firstRenderPhaseUpdate; | |
| 15924 | |
| 15925 do { | |
| 15926 // Process this render phase update. We don't have to check the | |
| 15927 // priority because it will always be the same as the current | |
| 15928 // render's. | |
| 15929 var action = update.action; | |
| 15930 newState = reducer(newState, action); | |
| 15931 update = update.next; | |
| 15932 } while (update !== firstRenderPhaseUpdate); // Mark that the fiber performed work, but only if the new state is | |
| 15933 // different from the current state. | |
| 15934 | |
| 15935 | |
| 15936 if (!objectIs(newState, hook.memoizedState)) { | |
| 15937 markWorkInProgressReceivedUpdate(); | |
| 15938 } | |
| 15939 | |
| 15940 hook.memoizedState = newState; // Don't persist the state accumulated from the render phase updates to | |
| 15941 // the base state unless the queue is empty. | |
| 15942 // TODO: Not sure if this is the desired semantics, but it's what we | |
| 15943 // do for gDSFP. I can't remember why. | |
| 15944 | |
| 15945 if (hook.baseQueue === null) { | |
| 15946 hook.baseState = newState; | |
| 15947 } | |
| 15948 | |
| 15949 queue.lastRenderedState = newState; | |
| 15950 } | |
| 15951 | |
| 15952 return [newState, dispatch]; | |
| 15953 } | |
| 15954 | |
| 15955 function mountMutableSource(source, getSnapshot, subscribe) { | |
| 15956 { | |
| 15957 return undefined; | |
| 15958 } | |
| 15959 } | |
| 15960 | |
| 15961 function updateMutableSource(source, getSnapshot, subscribe) { | |
| 15962 { | |
| 15963 return undefined; | |
| 15964 } | |
| 15965 } | |
| 15966 | |
| 15967 function mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) { | |
| 15968 var fiber = currentlyRenderingFiber$1; | |
| 15969 var hook = mountWorkInProgressHook(); | |
| 15970 var nextSnapshot; | |
| 15971 var isHydrating = getIsHydrating(); | |
| 15972 | |
| 15973 if (isHydrating) { | |
| 15974 if (getServerSnapshot === undefined) { | |
| 15975 throw new Error('Missing getServerSnapshot, which is required for ' + 'server-rendered content. Will revert to client rendering.'); | |
| 15976 } | |
| 15977 | |
| 15978 nextSnapshot = getServerSnapshot(); | |
| 15979 | |
| 15980 { | |
| 15981 if (!didWarnUncachedGetSnapshot) { | |
| 15982 if (nextSnapshot !== getServerSnapshot()) { | |
| 15983 error('The result of getServerSnapshot should be cached to avoid an infinite loop'); | |
| 15984 | |
| 15985 didWarnUncachedGetSnapshot = true; | |
| 15986 } | |
| 15987 } | |
| 15988 } | |
| 15989 } else { | |
| 15990 nextSnapshot = getSnapshot(); | |
| 15991 | |
| 15992 { | |
| 15993 if (!didWarnUncachedGetSnapshot) { | |
| 15994 var cachedSnapshot = getSnapshot(); | |
| 15995 | |
| 15996 if (!objectIs(nextSnapshot, cachedSnapshot)) { | |
| 15997 error('The result of getSnapshot should be cached to avoid an infinite loop'); | |
| 15998 | |
| 15999 didWarnUncachedGetSnapshot = true; | |
| 16000 } | |
| 16001 } | |
| 16002 } // Unless we're rendering a blocking lane, schedule a consistency check. | |
| 16003 // Right before committing, we will walk the tree and check if any of the | |
| 16004 // stores were mutated. | |
| 16005 // | |
| 16006 // We won't do this if we're hydrating server-rendered content, because if | |
| 16007 // the content is stale, it's already visible anyway. Instead we'll patch | |
| 16008 // it up in a passive effect. | |
| 16009 | |
| 16010 | |
| 16011 var root = getWorkInProgressRoot(); | |
| 16012 | |
| 16013 if (root === null) { | |
| 16014 throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.'); | |
| 16015 } | |
| 16016 | |
| 16017 if (!includesBlockingLane(root, renderLanes)) { | |
| 16018 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); | |
| 16019 } | |
| 16020 } // Read the current snapshot from the store on every render. This breaks the | |
| 16021 // normal rules of React, and only works because store updates are | |
| 16022 // always synchronous. | |
| 16023 | |
| 16024 | |
| 16025 hook.memoizedState = nextSnapshot; | |
| 16026 var inst = { | |
| 16027 value: nextSnapshot, | |
| 16028 getSnapshot: getSnapshot | |
| 16029 }; | |
| 16030 hook.queue = inst; // Schedule an effect to subscribe to the store. | |
| 16031 | |
| 16032 mountEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Schedule an effect to update the mutable instance fields. We will update | |
| 16033 // this whenever subscribe, getSnapshot, or value changes. Because there's no | |
| 16034 // clean-up function, and we track the deps correctly, we can call pushEffect | |
| 16035 // directly, without storing any additional state. For the same reason, we | |
| 16036 // don't need to set a static flag, either. | |
| 16037 // TODO: We can move this to the passive phase once we add a pre-commit | |
| 16038 // consistency check. See the next comment. | |
| 16039 | |
| 16040 fiber.flags |= Passive; | |
| 16041 pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null); | |
| 16042 return nextSnapshot; | |
| 16043 } | |
| 16044 | |
| 16045 function updateSyncExternalStore(subscribe, getSnapshot, getServerSnapshot) { | |
| 16046 var fiber = currentlyRenderingFiber$1; | |
| 16047 var hook = updateWorkInProgressHook(); // Read the current snapshot from the store on every render. This breaks the | |
| 16048 // normal rules of React, and only works because store updates are | |
| 16049 // always synchronous. | |
| 16050 | |
| 16051 var nextSnapshot = getSnapshot(); | |
| 16052 | |
| 16053 { | |
| 16054 if (!didWarnUncachedGetSnapshot) { | |
| 16055 var cachedSnapshot = getSnapshot(); | |
| 16056 | |
| 16057 if (!objectIs(nextSnapshot, cachedSnapshot)) { | |
| 16058 error('The result of getSnapshot should be cached to avoid an infinite loop'); | |
| 16059 | |
| 16060 didWarnUncachedGetSnapshot = true; | |
| 16061 } | |
| 16062 } | |
| 16063 } | |
| 16064 | |
| 16065 var prevSnapshot = hook.memoizedState; | |
| 16066 var snapshotChanged = !objectIs(prevSnapshot, nextSnapshot); | |
| 16067 | |
| 16068 if (snapshotChanged) { | |
| 16069 hook.memoizedState = nextSnapshot; | |
| 16070 markWorkInProgressReceivedUpdate(); | |
| 16071 } | |
| 16072 | |
| 16073 var inst = hook.queue; | |
| 16074 updateEffect(subscribeToStore.bind(null, fiber, inst, subscribe), [subscribe]); // Whenever getSnapshot or subscribe changes, we need to check in the | |
| 16075 // commit phase if there was an interleaved mutation. In concurrent mode | |
| 16076 // this can happen all the time, but even in synchronous mode, an earlier | |
| 16077 // effect may have mutated the store. | |
| 16078 | |
| 16079 if (inst.getSnapshot !== getSnapshot || snapshotChanged || // Check if the susbcribe function changed. We can save some memory by | |
| 16080 // checking whether we scheduled a subscription effect above. | |
| 16081 workInProgressHook !== null && workInProgressHook.memoizedState.tag & HasEffect) { | |
| 16082 fiber.flags |= Passive; | |
| 16083 pushEffect(HasEffect | Passive$1, updateStoreInstance.bind(null, fiber, inst, nextSnapshot, getSnapshot), undefined, null); // Unless we're rendering a blocking lane, schedule a consistency check. | |
| 16084 // Right before committing, we will walk the tree and check if any of the | |
| 16085 // stores were mutated. | |
| 16086 | |
| 16087 var root = getWorkInProgressRoot(); | |
| 16088 | |
| 16089 if (root === null) { | |
| 16090 throw new Error('Expected a work-in-progress root. This is a bug in React. Please file an issue.'); | |
| 16091 } | |
| 16092 | |
| 16093 if (!includesBlockingLane(root, renderLanes)) { | |
| 16094 pushStoreConsistencyCheck(fiber, getSnapshot, nextSnapshot); | |
| 16095 } | |
| 16096 } | |
| 16097 | |
| 16098 return nextSnapshot; | |
| 16099 } | |
| 16100 | |
| 16101 function pushStoreConsistencyCheck(fiber, getSnapshot, renderedSnapshot) { | |
| 16102 fiber.flags |= StoreConsistency; | |
| 16103 var check = { | |
| 16104 getSnapshot: getSnapshot, | |
| 16105 value: renderedSnapshot | |
| 16106 }; | |
| 16107 var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue; | |
| 16108 | |
| 16109 if (componentUpdateQueue === null) { | |
| 16110 componentUpdateQueue = createFunctionComponentUpdateQueue(); | |
| 16111 currentlyRenderingFiber$1.updateQueue = componentUpdateQueue; | |
| 16112 componentUpdateQueue.stores = [check]; | |
| 16113 } else { | |
| 16114 var stores = componentUpdateQueue.stores; | |
| 16115 | |
| 16116 if (stores === null) { | |
| 16117 componentUpdateQueue.stores = [check]; | |
| 16118 } else { | |
| 16119 stores.push(check); | |
| 16120 } | |
| 16121 } | |
| 16122 } | |
| 16123 | |
| 16124 function updateStoreInstance(fiber, inst, nextSnapshot, getSnapshot) { | |
| 16125 // These are updated in the passive phase | |
| 16126 inst.value = nextSnapshot; | |
| 16127 inst.getSnapshot = getSnapshot; // Something may have been mutated in between render and commit. This could | |
| 16128 // have been in an event that fired before the passive effects, or it could | |
| 16129 // have been in a layout effect. In that case, we would have used the old | |
| 16130 // snapsho and getSnapshot values to bail out. We need to check one more time. | |
| 16131 | |
| 16132 if (checkIfSnapshotChanged(inst)) { | |
| 16133 // Force a re-render. | |
| 16134 forceStoreRerender(fiber); | |
| 16135 } | |
| 16136 } | |
| 16137 | |
| 16138 function subscribeToStore(fiber, inst, subscribe) { | |
| 16139 var handleStoreChange = function () { | |
| 16140 // The store changed. Check if the snapshot changed since the last time we | |
| 16141 // read from the store. | |
| 16142 if (checkIfSnapshotChanged(inst)) { | |
| 16143 // Force a re-render. | |
| 16144 forceStoreRerender(fiber); | |
| 16145 } | |
| 16146 }; // Subscribe to the store and return a clean-up function. | |
| 16147 | |
| 16148 | |
| 16149 return subscribe(handleStoreChange); | |
| 16150 } | |
| 16151 | |
| 16152 function checkIfSnapshotChanged(inst) { | |
| 16153 var latestGetSnapshot = inst.getSnapshot; | |
| 16154 var prevValue = inst.value; | |
| 16155 | |
| 16156 try { | |
| 16157 var nextValue = latestGetSnapshot(); | |
| 16158 return !objectIs(prevValue, nextValue); | |
| 16159 } catch (error) { | |
| 16160 return true; | |
| 16161 } | |
| 16162 } | |
| 16163 | |
| 16164 function forceStoreRerender(fiber) { | |
| 16165 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 16166 | |
| 16167 if (root !== null) { | |
| 16168 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 16169 } | |
| 16170 } | |
| 16171 | |
| 16172 function mountState(initialState) { | |
| 16173 var hook = mountWorkInProgressHook(); | |
| 16174 | |
| 16175 if (typeof initialState === 'function') { | |
| 16176 // $FlowFixMe: Flow doesn't like mixed types | |
| 16177 initialState = initialState(); | |
| 16178 } | |
| 16179 | |
| 16180 hook.memoizedState = hook.baseState = initialState; | |
| 16181 var queue = { | |
| 16182 pending: null, | |
| 16183 interleaved: null, | |
| 16184 lanes: NoLanes, | |
| 16185 dispatch: null, | |
| 16186 lastRenderedReducer: basicStateReducer, | |
| 16187 lastRenderedState: initialState | |
| 16188 }; | |
| 16189 hook.queue = queue; | |
| 16190 var dispatch = queue.dispatch = dispatchSetState.bind(null, currentlyRenderingFiber$1, queue); | |
| 16191 return [hook.memoizedState, dispatch]; | |
| 16192 } | |
| 16193 | |
| 16194 function updateState(initialState) { | |
| 16195 return updateReducer(basicStateReducer); | |
| 16196 } | |
| 16197 | |
| 16198 function rerenderState(initialState) { | |
| 16199 return rerenderReducer(basicStateReducer); | |
| 16200 } | |
| 16201 | |
| 16202 function pushEffect(tag, create, destroy, deps) { | |
| 16203 var effect = { | |
| 16204 tag: tag, | |
| 16205 create: create, | |
| 16206 destroy: destroy, | |
| 16207 deps: deps, | |
| 16208 // Circular | |
| 16209 next: null | |
| 16210 }; | |
| 16211 var componentUpdateQueue = currentlyRenderingFiber$1.updateQueue; | |
| 16212 | |
| 16213 if (componentUpdateQueue === null) { | |
| 16214 componentUpdateQueue = createFunctionComponentUpdateQueue(); | |
| 16215 currentlyRenderingFiber$1.updateQueue = componentUpdateQueue; | |
| 16216 componentUpdateQueue.lastEffect = effect.next = effect; | |
| 16217 } else { | |
| 16218 var lastEffect = componentUpdateQueue.lastEffect; | |
| 16219 | |
| 16220 if (lastEffect === null) { | |
| 16221 componentUpdateQueue.lastEffect = effect.next = effect; | |
| 16222 } else { | |
| 16223 var firstEffect = lastEffect.next; | |
| 16224 lastEffect.next = effect; | |
| 16225 effect.next = firstEffect; | |
| 16226 componentUpdateQueue.lastEffect = effect; | |
| 16227 } | |
| 16228 } | |
| 16229 | |
| 16230 return effect; | |
| 16231 } | |
| 16232 | |
| 16233 function mountRef(initialValue) { | |
| 16234 var hook = mountWorkInProgressHook(); | |
| 16235 | |
| 16236 { | |
| 16237 var _ref2 = { | |
| 16238 current: initialValue | |
| 16239 }; | |
| 16240 hook.memoizedState = _ref2; | |
| 16241 return _ref2; | |
| 16242 } | |
| 16243 } | |
| 16244 | |
| 16245 function updateRef(initialValue) { | |
| 16246 var hook = updateWorkInProgressHook(); | |
| 16247 return hook.memoizedState; | |
| 16248 } | |
| 16249 | |
| 16250 function mountEffectImpl(fiberFlags, hookFlags, create, deps) { | |
| 16251 var hook = mountWorkInProgressHook(); | |
| 16252 var nextDeps = deps === undefined ? null : deps; | |
| 16253 currentlyRenderingFiber$1.flags |= fiberFlags; | |
| 16254 hook.memoizedState = pushEffect(HasEffect | hookFlags, create, undefined, nextDeps); | |
| 16255 } | |
| 16256 | |
| 16257 function updateEffectImpl(fiberFlags, hookFlags, create, deps) { | |
| 16258 var hook = updateWorkInProgressHook(); | |
| 16259 var nextDeps = deps === undefined ? null : deps; | |
| 16260 var destroy = undefined; | |
| 16261 | |
| 16262 if (currentHook !== null) { | |
| 16263 var prevEffect = currentHook.memoizedState; | |
| 16264 destroy = prevEffect.destroy; | |
| 16265 | |
| 16266 if (nextDeps !== null) { | |
| 16267 var prevDeps = prevEffect.deps; | |
| 16268 | |
| 16269 if (areHookInputsEqual(nextDeps, prevDeps)) { | |
| 16270 hook.memoizedState = pushEffect(hookFlags, create, destroy, nextDeps); | |
| 16271 return; | |
| 16272 } | |
| 16273 } | |
| 16274 } | |
| 16275 | |
| 16276 currentlyRenderingFiber$1.flags |= fiberFlags; | |
| 16277 hook.memoizedState = pushEffect(HasEffect | hookFlags, create, destroy, nextDeps); | |
| 16278 } | |
| 16279 | |
| 16280 function mountEffect(create, deps) { | |
| 16281 if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) { | |
| 16282 return mountEffectImpl(MountPassiveDev | Passive | PassiveStatic, Passive$1, create, deps); | |
| 16283 } else { | |
| 16284 return mountEffectImpl(Passive | PassiveStatic, Passive$1, create, deps); | |
| 16285 } | |
| 16286 } | |
| 16287 | |
| 16288 function updateEffect(create, deps) { | |
| 16289 return updateEffectImpl(Passive, Passive$1, create, deps); | |
| 16290 } | |
| 16291 | |
| 16292 function mountInsertionEffect(create, deps) { | |
| 16293 return mountEffectImpl(Update, Insertion, create, deps); | |
| 16294 } | |
| 16295 | |
| 16296 function updateInsertionEffect(create, deps) { | |
| 16297 return updateEffectImpl(Update, Insertion, create, deps); | |
| 16298 } | |
| 16299 | |
| 16300 function mountLayoutEffect(create, deps) { | |
| 16301 var fiberFlags = Update; | |
| 16302 | |
| 16303 { | |
| 16304 fiberFlags |= LayoutStatic; | |
| 16305 } | |
| 16306 | |
| 16307 if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) { | |
| 16308 fiberFlags |= MountLayoutDev; | |
| 16309 } | |
| 16310 | |
| 16311 return mountEffectImpl(fiberFlags, Layout, create, deps); | |
| 16312 } | |
| 16313 | |
| 16314 function updateLayoutEffect(create, deps) { | |
| 16315 return updateEffectImpl(Update, Layout, create, deps); | |
| 16316 } | |
| 16317 | |
| 16318 function imperativeHandleEffect(create, ref) { | |
| 16319 if (typeof ref === 'function') { | |
| 16320 var refCallback = ref; | |
| 16321 | |
| 16322 var _inst = create(); | |
| 16323 | |
| 16324 refCallback(_inst); | |
| 16325 return function () { | |
| 16326 refCallback(null); | |
| 16327 }; | |
| 16328 } else if (ref !== null && ref !== undefined) { | |
| 16329 var refObject = ref; | |
| 16330 | |
| 16331 { | |
| 16332 if (!refObject.hasOwnProperty('current')) { | |
| 16333 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(', ') + '}'); | |
| 16334 } | |
| 16335 } | |
| 16336 | |
| 16337 var _inst2 = create(); | |
| 16338 | |
| 16339 refObject.current = _inst2; | |
| 16340 return function () { | |
| 16341 refObject.current = null; | |
| 16342 }; | |
| 16343 } | |
| 16344 } | |
| 16345 | |
| 16346 function mountImperativeHandle(ref, create, deps) { | |
| 16347 { | |
| 16348 if (typeof create !== 'function') { | |
| 16349 error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null'); | |
| 16350 } | |
| 16351 } // TODO: If deps are provided, should we skip comparing the ref itself? | |
| 16352 | |
| 16353 | |
| 16354 var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null; | |
| 16355 var fiberFlags = Update; | |
| 16356 | |
| 16357 { | |
| 16358 fiberFlags |= LayoutStatic; | |
| 16359 } | |
| 16360 | |
| 16361 if ( (currentlyRenderingFiber$1.mode & StrictEffectsMode) !== NoMode) { | |
| 16362 fiberFlags |= MountLayoutDev; | |
| 16363 } | |
| 16364 | |
| 16365 return mountEffectImpl(fiberFlags, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps); | |
| 16366 } | |
| 16367 | |
| 16368 function updateImperativeHandle(ref, create, deps) { | |
| 16369 { | |
| 16370 if (typeof create !== 'function') { | |
| 16371 error('Expected useImperativeHandle() second argument to be a function ' + 'that creates a handle. Instead received: %s.', create !== null ? typeof create : 'null'); | |
| 16372 } | |
| 16373 } // TODO: If deps are provided, should we skip comparing the ref itself? | |
| 16374 | |
| 16375 | |
| 16376 var effectDeps = deps !== null && deps !== undefined ? deps.concat([ref]) : null; | |
| 16377 return updateEffectImpl(Update, Layout, imperativeHandleEffect.bind(null, create, ref), effectDeps); | |
| 16378 } | |
| 16379 | |
| 16380 function mountDebugValue(value, formatterFn) {// This hook is normally a no-op. | |
| 16381 // The react-debug-hooks package injects its own implementation | |
| 16382 // so that e.g. DevTools can display custom hook values. | |
| 16383 } | |
| 16384 | |
| 16385 var updateDebugValue = mountDebugValue; | |
| 16386 | |
| 16387 function mountCallback(callback, deps) { | |
| 16388 var hook = mountWorkInProgressHook(); | |
| 16389 var nextDeps = deps === undefined ? null : deps; | |
| 16390 hook.memoizedState = [callback, nextDeps]; | |
| 16391 return callback; | |
| 16392 } | |
| 16393 | |
| 16394 function updateCallback(callback, deps) { | |
| 16395 var hook = updateWorkInProgressHook(); | |
| 16396 var nextDeps = deps === undefined ? null : deps; | |
| 16397 var prevState = hook.memoizedState; | |
| 16398 | |
| 16399 if (prevState !== null) { | |
| 16400 if (nextDeps !== null) { | |
| 16401 var prevDeps = prevState[1]; | |
| 16402 | |
| 16403 if (areHookInputsEqual(nextDeps, prevDeps)) { | |
| 16404 return prevState[0]; | |
| 16405 } | |
| 16406 } | |
| 16407 } | |
| 16408 | |
| 16409 hook.memoizedState = [callback, nextDeps]; | |
| 16410 return callback; | |
| 16411 } | |
| 16412 | |
| 16413 function mountMemo(nextCreate, deps) { | |
| 16414 var hook = mountWorkInProgressHook(); | |
| 16415 var nextDeps = deps === undefined ? null : deps; | |
| 16416 var nextValue = nextCreate(); | |
| 16417 hook.memoizedState = [nextValue, nextDeps]; | |
| 16418 return nextValue; | |
| 16419 } | |
| 16420 | |
| 16421 function updateMemo(nextCreate, deps) { | |
| 16422 var hook = updateWorkInProgressHook(); | |
| 16423 var nextDeps = deps === undefined ? null : deps; | |
| 16424 var prevState = hook.memoizedState; | |
| 16425 | |
| 16426 if (prevState !== null) { | |
| 16427 // Assume these are defined. If they're not, areHookInputsEqual will warn. | |
| 16428 if (nextDeps !== null) { | |
| 16429 var prevDeps = prevState[1]; | |
| 16430 | |
| 16431 if (areHookInputsEqual(nextDeps, prevDeps)) { | |
| 16432 return prevState[0]; | |
| 16433 } | |
| 16434 } | |
| 16435 } | |
| 16436 | |
| 16437 var nextValue = nextCreate(); | |
| 16438 hook.memoizedState = [nextValue, nextDeps]; | |
| 16439 return nextValue; | |
| 16440 } | |
| 16441 | |
| 16442 function mountDeferredValue(value) { | |
| 16443 var hook = mountWorkInProgressHook(); | |
| 16444 hook.memoizedState = value; | |
| 16445 return value; | |
| 16446 } | |
| 16447 | |
| 16448 function updateDeferredValue(value) { | |
| 16449 var hook = updateWorkInProgressHook(); | |
| 16450 var resolvedCurrentHook = currentHook; | |
| 16451 var prevValue = resolvedCurrentHook.memoizedState; | |
| 16452 return updateDeferredValueImpl(hook, prevValue, value); | |
| 16453 } | |
| 16454 | |
| 16455 function rerenderDeferredValue(value) { | |
| 16456 var hook = updateWorkInProgressHook(); | |
| 16457 | |
| 16458 if (currentHook === null) { | |
| 16459 // This is a rerender during a mount. | |
| 16460 hook.memoizedState = value; | |
| 16461 return value; | |
| 16462 } else { | |
| 16463 // This is a rerender during an update. | |
| 16464 var prevValue = currentHook.memoizedState; | |
| 16465 return updateDeferredValueImpl(hook, prevValue, value); | |
| 16466 } | |
| 16467 } | |
| 16468 | |
| 16469 function updateDeferredValueImpl(hook, prevValue, value) { | |
| 16470 var shouldDeferValue = !includesOnlyNonUrgentLanes(renderLanes); | |
| 16471 | |
| 16472 if (shouldDeferValue) { | |
| 16473 // This is an urgent update. If the value has changed, keep using the | |
| 16474 // previous value and spawn a deferred render to update it later. | |
| 16475 if (!objectIs(value, prevValue)) { | |
| 16476 // Schedule a deferred render | |
| 16477 var deferredLane = claimNextTransitionLane(); | |
| 16478 currentlyRenderingFiber$1.lanes = mergeLanes(currentlyRenderingFiber$1.lanes, deferredLane); | |
| 16479 markSkippedUpdateLanes(deferredLane); // Set this to true to indicate that the rendered value is inconsistent | |
| 16480 // from the latest value. The name "baseState" doesn't really match how we | |
| 16481 // use it because we're reusing a state hook field instead of creating a | |
| 16482 // new one. | |
| 16483 | |
| 16484 hook.baseState = true; | |
| 16485 } // Reuse the previous value | |
| 16486 | |
| 16487 | |
| 16488 return prevValue; | |
| 16489 } else { | |
| 16490 // This is not an urgent update, so we can use the latest value regardless | |
| 16491 // of what it is. No need to defer it. | |
| 16492 // However, if we're currently inside a spawned render, then we need to mark | |
| 16493 // this as an update to prevent the fiber from bailing out. | |
| 16494 // | |
| 16495 // `baseState` is true when the current value is different from the rendered | |
| 16496 // value. The name doesn't really match how we use it because we're reusing | |
| 16497 // a state hook field instead of creating a new one. | |
| 16498 if (hook.baseState) { | |
| 16499 // Flip this back to false. | |
| 16500 hook.baseState = false; | |
| 16501 markWorkInProgressReceivedUpdate(); | |
| 16502 } | |
| 16503 | |
| 16504 hook.memoizedState = value; | |
| 16505 return value; | |
| 16506 } | |
| 16507 } | |
| 16508 | |
| 16509 function startTransition(setPending, callback, options) { | |
| 16510 var previousPriority = getCurrentUpdatePriority(); | |
| 16511 setCurrentUpdatePriority(higherEventPriority(previousPriority, ContinuousEventPriority)); | |
| 16512 setPending(true); | |
| 16513 var prevTransition = ReactCurrentBatchConfig$2.transition; | |
| 16514 ReactCurrentBatchConfig$2.transition = {}; | |
| 16515 var currentTransition = ReactCurrentBatchConfig$2.transition; | |
| 16516 | |
| 16517 { | |
| 16518 ReactCurrentBatchConfig$2.transition._updatedFibers = new Set(); | |
| 16519 } | |
| 16520 | |
| 16521 try { | |
| 16522 setPending(false); | |
| 16523 callback(); | |
| 16524 } finally { | |
| 16525 setCurrentUpdatePriority(previousPriority); | |
| 16526 ReactCurrentBatchConfig$2.transition = prevTransition; | |
| 16527 | |
| 16528 { | |
| 16529 if (prevTransition === null && currentTransition._updatedFibers) { | |
| 16530 var updatedFibersCount = currentTransition._updatedFibers.size; | |
| 16531 | |
| 16532 if (updatedFibersCount > 10) { | |
| 16533 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.'); | |
| 16534 } | |
| 16535 | |
| 16536 currentTransition._updatedFibers.clear(); | |
| 16537 } | |
| 16538 } | |
| 16539 } | |
| 16540 } | |
| 16541 | |
| 16542 function mountTransition() { | |
| 16543 var _mountState = mountState(false), | |
| 16544 isPending = _mountState[0], | |
| 16545 setPending = _mountState[1]; // The `start` method never changes. | |
| 16546 | |
| 16547 | |
| 16548 var start = startTransition.bind(null, setPending); | |
| 16549 var hook = mountWorkInProgressHook(); | |
| 16550 hook.memoizedState = start; | |
| 16551 return [isPending, start]; | |
| 16552 } | |
| 16553 | |
| 16554 function updateTransition() { | |
| 16555 var _updateState = updateState(), | |
| 16556 isPending = _updateState[0]; | |
| 16557 | |
| 16558 var hook = updateWorkInProgressHook(); | |
| 16559 var start = hook.memoizedState; | |
| 16560 return [isPending, start]; | |
| 16561 } | |
| 16562 | |
| 16563 function rerenderTransition() { | |
| 16564 var _rerenderState = rerenderState(), | |
| 16565 isPending = _rerenderState[0]; | |
| 16566 | |
| 16567 var hook = updateWorkInProgressHook(); | |
| 16568 var start = hook.memoizedState; | |
| 16569 return [isPending, start]; | |
| 16570 } | |
| 16571 | |
| 16572 var isUpdatingOpaqueValueInRenderPhase = false; | |
| 16573 function getIsUpdatingOpaqueValueInRenderPhaseInDEV() { | |
| 16574 { | |
| 16575 return isUpdatingOpaqueValueInRenderPhase; | |
| 16576 } | |
| 16577 } | |
| 16578 | |
| 16579 function mountId() { | |
| 16580 var hook = mountWorkInProgressHook(); | |
| 16581 var root = getWorkInProgressRoot(); // TODO: In Fizz, id generation is specific to each server config. Maybe we | |
| 16582 // should do this in Fiber, too? Deferring this decision for now because | |
| 16583 // there's no other place to store the prefix except for an internal field on | |
| 16584 // the public createRoot object, which the fiber tree does not currently have | |
| 16585 // a reference to. | |
| 16586 | |
| 16587 var identifierPrefix = root.identifierPrefix; | |
| 16588 var id; | |
| 16589 | |
| 16590 if (getIsHydrating()) { | |
| 16591 var treeId = getTreeId(); // Use a captial R prefix for server-generated ids. | |
| 16592 | |
| 16593 id = ':' + identifierPrefix + 'R' + treeId; // Unless this is the first id at this level, append a number at the end | |
| 16594 // that represents the position of this useId hook among all the useId | |
| 16595 // hooks for this fiber. | |
| 16596 | |
| 16597 var localId = localIdCounter++; | |
| 16598 | |
| 16599 if (localId > 0) { | |
| 16600 id += 'H' + localId.toString(32); | |
| 16601 } | |
| 16602 | |
| 16603 id += ':'; | |
| 16604 } else { | |
| 16605 // Use a lowercase r prefix for client-generated ids. | |
| 16606 var globalClientId = globalClientIdCounter++; | |
| 16607 id = ':' + identifierPrefix + 'r' + globalClientId.toString(32) + ':'; | |
| 16608 } | |
| 16609 | |
| 16610 hook.memoizedState = id; | |
| 16611 return id; | |
| 16612 } | |
| 16613 | |
| 16614 function updateId() { | |
| 16615 var hook = updateWorkInProgressHook(); | |
| 16616 var id = hook.memoizedState; | |
| 16617 return id; | |
| 16618 } | |
| 16619 | |
| 16620 function dispatchReducerAction(fiber, queue, action) { | |
| 16621 { | |
| 16622 if (typeof arguments[3] === 'function') { | |
| 16623 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().'); | |
| 16624 } | |
| 16625 } | |
| 16626 | |
| 16627 var lane = requestUpdateLane(fiber); | |
| 16628 var update = { | |
| 16629 lane: lane, | |
| 16630 action: action, | |
| 16631 hasEagerState: false, | |
| 16632 eagerState: null, | |
| 16633 next: null | |
| 16634 }; | |
| 16635 | |
| 16636 if (isRenderPhaseUpdate(fiber)) { | |
| 16637 enqueueRenderPhaseUpdate(queue, update); | |
| 16638 } else { | |
| 16639 var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); | |
| 16640 | |
| 16641 if (root !== null) { | |
| 16642 var eventTime = requestEventTime(); | |
| 16643 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 16644 entangleTransitionUpdate(root, queue, lane); | |
| 16645 } | |
| 16646 } | |
| 16647 | |
| 16648 markUpdateInDevTools(fiber, lane); | |
| 16649 } | |
| 16650 | |
| 16651 function dispatchSetState(fiber, queue, action) { | |
| 16652 { | |
| 16653 if (typeof arguments[3] === 'function') { | |
| 16654 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().'); | |
| 16655 } | |
| 16656 } | |
| 16657 | |
| 16658 var lane = requestUpdateLane(fiber); | |
| 16659 var update = { | |
| 16660 lane: lane, | |
| 16661 action: action, | |
| 16662 hasEagerState: false, | |
| 16663 eagerState: null, | |
| 16664 next: null | |
| 16665 }; | |
| 16666 | |
| 16667 if (isRenderPhaseUpdate(fiber)) { | |
| 16668 enqueueRenderPhaseUpdate(queue, update); | |
| 16669 } else { | |
| 16670 var alternate = fiber.alternate; | |
| 16671 | |
| 16672 if (fiber.lanes === NoLanes && (alternate === null || alternate.lanes === NoLanes)) { | |
| 16673 // The queue is currently empty, which means we can eagerly compute the | |
| 16674 // next state before entering the render phase. If the new state is the | |
| 16675 // same as the current state, we may be able to bail out entirely. | |
| 16676 var lastRenderedReducer = queue.lastRenderedReducer; | |
| 16677 | |
| 16678 if (lastRenderedReducer !== null) { | |
| 16679 var prevDispatcher; | |
| 16680 | |
| 16681 { | |
| 16682 prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16683 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 16684 } | |
| 16685 | |
| 16686 try { | |
| 16687 var currentState = queue.lastRenderedState; | |
| 16688 var eagerState = lastRenderedReducer(currentState, action); // Stash the eagerly computed state, and the reducer used to compute | |
| 16689 // it, on the update object. If the reducer hasn't changed by the | |
| 16690 // time we enter the render phase, then the eager state can be used | |
| 16691 // without calling the reducer again. | |
| 16692 | |
| 16693 update.hasEagerState = true; | |
| 16694 update.eagerState = eagerState; | |
| 16695 | |
| 16696 if (objectIs(eagerState, currentState)) { | |
| 16697 // Fast path. We can bail out without scheduling React to re-render. | |
| 16698 // It's still possible that we'll need to rebase this update later, | |
| 16699 // if the component re-renders for a different reason and by that | |
| 16700 // time the reducer has changed. | |
| 16701 // TODO: Do we still need to entangle transitions in this case? | |
| 16702 enqueueConcurrentHookUpdateAndEagerlyBailout(fiber, queue, update, lane); | |
| 16703 return; | |
| 16704 } | |
| 16705 } catch (error) {// Suppress the error. It will throw again in the render phase. | |
| 16706 } finally { | |
| 16707 { | |
| 16708 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16709 } | |
| 16710 } | |
| 16711 } | |
| 16712 } | |
| 16713 | |
| 16714 var root = enqueueConcurrentHookUpdate(fiber, queue, update, lane); | |
| 16715 | |
| 16716 if (root !== null) { | |
| 16717 var eventTime = requestEventTime(); | |
| 16718 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 16719 entangleTransitionUpdate(root, queue, lane); | |
| 16720 } | |
| 16721 } | |
| 16722 | |
| 16723 markUpdateInDevTools(fiber, lane); | |
| 16724 } | |
| 16725 | |
| 16726 function isRenderPhaseUpdate(fiber) { | |
| 16727 var alternate = fiber.alternate; | |
| 16728 return fiber === currentlyRenderingFiber$1 || alternate !== null && alternate === currentlyRenderingFiber$1; | |
| 16729 } | |
| 16730 | |
| 16731 function enqueueRenderPhaseUpdate(queue, update) { | |
| 16732 // This is a render phase update. Stash it in a lazily-created map of | |
| 16733 // queue -> linked list of updates. After this render pass, we'll restart | |
| 16734 // and apply the stashed updates on top of the work-in-progress hook. | |
| 16735 didScheduleRenderPhaseUpdateDuringThisPass = didScheduleRenderPhaseUpdate = true; | |
| 16736 var pending = queue.pending; | |
| 16737 | |
| 16738 if (pending === null) { | |
| 16739 // This is the first update. Create a circular list. | |
| 16740 update.next = update; | |
| 16741 } else { | |
| 16742 update.next = pending.next; | |
| 16743 pending.next = update; | |
| 16744 } | |
| 16745 | |
| 16746 queue.pending = update; | |
| 16747 } // TODO: Move to ReactFiberConcurrentUpdates? | |
| 16748 | |
| 16749 | |
| 16750 function entangleTransitionUpdate(root, queue, lane) { | |
| 16751 if (isTransitionLane(lane)) { | |
| 16752 var queueLanes = queue.lanes; // If any entangled lanes are no longer pending on the root, then they | |
| 16753 // must have finished. We can remove them from the shared queue, which | |
| 16754 // represents a superset of the actually pending lanes. In some cases we | |
| 16755 // may entangle more than we need to, but that's OK. In fact it's worse if | |
| 16756 // we *don't* entangle when we should. | |
| 16757 | |
| 16758 queueLanes = intersectLanes(queueLanes, root.pendingLanes); // Entangle the new transition lane with the other transition lanes. | |
| 16759 | |
| 16760 var newQueueLanes = mergeLanes(queueLanes, lane); | |
| 16761 queue.lanes = newQueueLanes; // Even if queue.lanes already include lane, we don't know for certain if | |
| 16762 // the lane finished since the last time we entangled it. So we need to | |
| 16763 // entangle it again, just to be sure. | |
| 16764 | |
| 16765 markRootEntangled(root, newQueueLanes); | |
| 16766 } | |
| 16767 } | |
| 16768 | |
| 16769 function markUpdateInDevTools(fiber, lane, action) { | |
| 16770 | |
| 16771 { | |
| 16772 markStateUpdateScheduled(fiber, lane); | |
| 16773 } | |
| 16774 } | |
| 16775 | |
| 16776 var ContextOnlyDispatcher = { | |
| 16777 readContext: readContext, | |
| 16778 useCallback: throwInvalidHookError, | |
| 16779 useContext: throwInvalidHookError, | |
| 16780 useEffect: throwInvalidHookError, | |
| 16781 useImperativeHandle: throwInvalidHookError, | |
| 16782 useInsertionEffect: throwInvalidHookError, | |
| 16783 useLayoutEffect: throwInvalidHookError, | |
| 16784 useMemo: throwInvalidHookError, | |
| 16785 useReducer: throwInvalidHookError, | |
| 16786 useRef: throwInvalidHookError, | |
| 16787 useState: throwInvalidHookError, | |
| 16788 useDebugValue: throwInvalidHookError, | |
| 16789 useDeferredValue: throwInvalidHookError, | |
| 16790 useTransition: throwInvalidHookError, | |
| 16791 useMutableSource: throwInvalidHookError, | |
| 16792 useSyncExternalStore: throwInvalidHookError, | |
| 16793 useId: throwInvalidHookError, | |
| 16794 unstable_isNewReconciler: enableNewReconciler | |
| 16795 }; | |
| 16796 | |
| 16797 var HooksDispatcherOnMountInDEV = null; | |
| 16798 var HooksDispatcherOnMountWithHookTypesInDEV = null; | |
| 16799 var HooksDispatcherOnUpdateInDEV = null; | |
| 16800 var HooksDispatcherOnRerenderInDEV = null; | |
| 16801 var InvalidNestedHooksDispatcherOnMountInDEV = null; | |
| 16802 var InvalidNestedHooksDispatcherOnUpdateInDEV = null; | |
| 16803 var InvalidNestedHooksDispatcherOnRerenderInDEV = null; | |
| 16804 | |
| 16805 { | |
| 16806 var warnInvalidContextAccess = function () { | |
| 16807 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().'); | |
| 16808 }; | |
| 16809 | |
| 16810 var warnInvalidHookAccess = function () { | |
| 16811 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'); | |
| 16812 }; | |
| 16813 | |
| 16814 HooksDispatcherOnMountInDEV = { | |
| 16815 readContext: function (context) { | |
| 16816 return readContext(context); | |
| 16817 }, | |
| 16818 useCallback: function (callback, deps) { | |
| 16819 currentHookNameInDev = 'useCallback'; | |
| 16820 mountHookTypesDev(); | |
| 16821 checkDepsAreArrayDev(deps); | |
| 16822 return mountCallback(callback, deps); | |
| 16823 }, | |
| 16824 useContext: function (context) { | |
| 16825 currentHookNameInDev = 'useContext'; | |
| 16826 mountHookTypesDev(); | |
| 16827 return readContext(context); | |
| 16828 }, | |
| 16829 useEffect: function (create, deps) { | |
| 16830 currentHookNameInDev = 'useEffect'; | |
| 16831 mountHookTypesDev(); | |
| 16832 checkDepsAreArrayDev(deps); | |
| 16833 return mountEffect(create, deps); | |
| 16834 }, | |
| 16835 useImperativeHandle: function (ref, create, deps) { | |
| 16836 currentHookNameInDev = 'useImperativeHandle'; | |
| 16837 mountHookTypesDev(); | |
| 16838 checkDepsAreArrayDev(deps); | |
| 16839 return mountImperativeHandle(ref, create, deps); | |
| 16840 }, | |
| 16841 useInsertionEffect: function (create, deps) { | |
| 16842 currentHookNameInDev = 'useInsertionEffect'; | |
| 16843 mountHookTypesDev(); | |
| 16844 checkDepsAreArrayDev(deps); | |
| 16845 return mountInsertionEffect(create, deps); | |
| 16846 }, | |
| 16847 useLayoutEffect: function (create, deps) { | |
| 16848 currentHookNameInDev = 'useLayoutEffect'; | |
| 16849 mountHookTypesDev(); | |
| 16850 checkDepsAreArrayDev(deps); | |
| 16851 return mountLayoutEffect(create, deps); | |
| 16852 }, | |
| 16853 useMemo: function (create, deps) { | |
| 16854 currentHookNameInDev = 'useMemo'; | |
| 16855 mountHookTypesDev(); | |
| 16856 checkDepsAreArrayDev(deps); | |
| 16857 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16858 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16859 | |
| 16860 try { | |
| 16861 return mountMemo(create, deps); | |
| 16862 } finally { | |
| 16863 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16864 } | |
| 16865 }, | |
| 16866 useReducer: function (reducer, initialArg, init) { | |
| 16867 currentHookNameInDev = 'useReducer'; | |
| 16868 mountHookTypesDev(); | |
| 16869 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16870 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16871 | |
| 16872 try { | |
| 16873 return mountReducer(reducer, initialArg, init); | |
| 16874 } finally { | |
| 16875 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16876 } | |
| 16877 }, | |
| 16878 useRef: function (initialValue) { | |
| 16879 currentHookNameInDev = 'useRef'; | |
| 16880 mountHookTypesDev(); | |
| 16881 return mountRef(initialValue); | |
| 16882 }, | |
| 16883 useState: function (initialState) { | |
| 16884 currentHookNameInDev = 'useState'; | |
| 16885 mountHookTypesDev(); | |
| 16886 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16887 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16888 | |
| 16889 try { | |
| 16890 return mountState(initialState); | |
| 16891 } finally { | |
| 16892 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16893 } | |
| 16894 }, | |
| 16895 useDebugValue: function (value, formatterFn) { | |
| 16896 currentHookNameInDev = 'useDebugValue'; | |
| 16897 mountHookTypesDev(); | |
| 16898 return mountDebugValue(); | |
| 16899 }, | |
| 16900 useDeferredValue: function (value) { | |
| 16901 currentHookNameInDev = 'useDeferredValue'; | |
| 16902 mountHookTypesDev(); | |
| 16903 return mountDeferredValue(value); | |
| 16904 }, | |
| 16905 useTransition: function () { | |
| 16906 currentHookNameInDev = 'useTransition'; | |
| 16907 mountHookTypesDev(); | |
| 16908 return mountTransition(); | |
| 16909 }, | |
| 16910 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 16911 currentHookNameInDev = 'useMutableSource'; | |
| 16912 mountHookTypesDev(); | |
| 16913 return mountMutableSource(); | |
| 16914 }, | |
| 16915 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 16916 currentHookNameInDev = 'useSyncExternalStore'; | |
| 16917 mountHookTypesDev(); | |
| 16918 return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); | |
| 16919 }, | |
| 16920 useId: function () { | |
| 16921 currentHookNameInDev = 'useId'; | |
| 16922 mountHookTypesDev(); | |
| 16923 return mountId(); | |
| 16924 }, | |
| 16925 unstable_isNewReconciler: enableNewReconciler | |
| 16926 }; | |
| 16927 | |
| 16928 HooksDispatcherOnMountWithHookTypesInDEV = { | |
| 16929 readContext: function (context) { | |
| 16930 return readContext(context); | |
| 16931 }, | |
| 16932 useCallback: function (callback, deps) { | |
| 16933 currentHookNameInDev = 'useCallback'; | |
| 16934 updateHookTypesDev(); | |
| 16935 return mountCallback(callback, deps); | |
| 16936 }, | |
| 16937 useContext: function (context) { | |
| 16938 currentHookNameInDev = 'useContext'; | |
| 16939 updateHookTypesDev(); | |
| 16940 return readContext(context); | |
| 16941 }, | |
| 16942 useEffect: function (create, deps) { | |
| 16943 currentHookNameInDev = 'useEffect'; | |
| 16944 updateHookTypesDev(); | |
| 16945 return mountEffect(create, deps); | |
| 16946 }, | |
| 16947 useImperativeHandle: function (ref, create, deps) { | |
| 16948 currentHookNameInDev = 'useImperativeHandle'; | |
| 16949 updateHookTypesDev(); | |
| 16950 return mountImperativeHandle(ref, create, deps); | |
| 16951 }, | |
| 16952 useInsertionEffect: function (create, deps) { | |
| 16953 currentHookNameInDev = 'useInsertionEffect'; | |
| 16954 updateHookTypesDev(); | |
| 16955 return mountInsertionEffect(create, deps); | |
| 16956 }, | |
| 16957 useLayoutEffect: function (create, deps) { | |
| 16958 currentHookNameInDev = 'useLayoutEffect'; | |
| 16959 updateHookTypesDev(); | |
| 16960 return mountLayoutEffect(create, deps); | |
| 16961 }, | |
| 16962 useMemo: function (create, deps) { | |
| 16963 currentHookNameInDev = 'useMemo'; | |
| 16964 updateHookTypesDev(); | |
| 16965 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16966 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16967 | |
| 16968 try { | |
| 16969 return mountMemo(create, deps); | |
| 16970 } finally { | |
| 16971 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16972 } | |
| 16973 }, | |
| 16974 useReducer: function (reducer, initialArg, init) { | |
| 16975 currentHookNameInDev = 'useReducer'; | |
| 16976 updateHookTypesDev(); | |
| 16977 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16978 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16979 | |
| 16980 try { | |
| 16981 return mountReducer(reducer, initialArg, init); | |
| 16982 } finally { | |
| 16983 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 16984 } | |
| 16985 }, | |
| 16986 useRef: function (initialValue) { | |
| 16987 currentHookNameInDev = 'useRef'; | |
| 16988 updateHookTypesDev(); | |
| 16989 return mountRef(initialValue); | |
| 16990 }, | |
| 16991 useState: function (initialState) { | |
| 16992 currentHookNameInDev = 'useState'; | |
| 16993 updateHookTypesDev(); | |
| 16994 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 16995 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 16996 | |
| 16997 try { | |
| 16998 return mountState(initialState); | |
| 16999 } finally { | |
| 17000 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17001 } | |
| 17002 }, | |
| 17003 useDebugValue: function (value, formatterFn) { | |
| 17004 currentHookNameInDev = 'useDebugValue'; | |
| 17005 updateHookTypesDev(); | |
| 17006 return mountDebugValue(); | |
| 17007 }, | |
| 17008 useDeferredValue: function (value) { | |
| 17009 currentHookNameInDev = 'useDeferredValue'; | |
| 17010 updateHookTypesDev(); | |
| 17011 return mountDeferredValue(value); | |
| 17012 }, | |
| 17013 useTransition: function () { | |
| 17014 currentHookNameInDev = 'useTransition'; | |
| 17015 updateHookTypesDev(); | |
| 17016 return mountTransition(); | |
| 17017 }, | |
| 17018 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17019 currentHookNameInDev = 'useMutableSource'; | |
| 17020 updateHookTypesDev(); | |
| 17021 return mountMutableSource(); | |
| 17022 }, | |
| 17023 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17024 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17025 updateHookTypesDev(); | |
| 17026 return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); | |
| 17027 }, | |
| 17028 useId: function () { | |
| 17029 currentHookNameInDev = 'useId'; | |
| 17030 updateHookTypesDev(); | |
| 17031 return mountId(); | |
| 17032 }, | |
| 17033 unstable_isNewReconciler: enableNewReconciler | |
| 17034 }; | |
| 17035 | |
| 17036 HooksDispatcherOnUpdateInDEV = { | |
| 17037 readContext: function (context) { | |
| 17038 return readContext(context); | |
| 17039 }, | |
| 17040 useCallback: function (callback, deps) { | |
| 17041 currentHookNameInDev = 'useCallback'; | |
| 17042 updateHookTypesDev(); | |
| 17043 return updateCallback(callback, deps); | |
| 17044 }, | |
| 17045 useContext: function (context) { | |
| 17046 currentHookNameInDev = 'useContext'; | |
| 17047 updateHookTypesDev(); | |
| 17048 return readContext(context); | |
| 17049 }, | |
| 17050 useEffect: function (create, deps) { | |
| 17051 currentHookNameInDev = 'useEffect'; | |
| 17052 updateHookTypesDev(); | |
| 17053 return updateEffect(create, deps); | |
| 17054 }, | |
| 17055 useImperativeHandle: function (ref, create, deps) { | |
| 17056 currentHookNameInDev = 'useImperativeHandle'; | |
| 17057 updateHookTypesDev(); | |
| 17058 return updateImperativeHandle(ref, create, deps); | |
| 17059 }, | |
| 17060 useInsertionEffect: function (create, deps) { | |
| 17061 currentHookNameInDev = 'useInsertionEffect'; | |
| 17062 updateHookTypesDev(); | |
| 17063 return updateInsertionEffect(create, deps); | |
| 17064 }, | |
| 17065 useLayoutEffect: function (create, deps) { | |
| 17066 currentHookNameInDev = 'useLayoutEffect'; | |
| 17067 updateHookTypesDev(); | |
| 17068 return updateLayoutEffect(create, deps); | |
| 17069 }, | |
| 17070 useMemo: function (create, deps) { | |
| 17071 currentHookNameInDev = 'useMemo'; | |
| 17072 updateHookTypesDev(); | |
| 17073 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17074 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17075 | |
| 17076 try { | |
| 17077 return updateMemo(create, deps); | |
| 17078 } finally { | |
| 17079 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17080 } | |
| 17081 }, | |
| 17082 useReducer: function (reducer, initialArg, init) { | |
| 17083 currentHookNameInDev = 'useReducer'; | |
| 17084 updateHookTypesDev(); | |
| 17085 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17086 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17087 | |
| 17088 try { | |
| 17089 return updateReducer(reducer, initialArg, init); | |
| 17090 } finally { | |
| 17091 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17092 } | |
| 17093 }, | |
| 17094 useRef: function (initialValue) { | |
| 17095 currentHookNameInDev = 'useRef'; | |
| 17096 updateHookTypesDev(); | |
| 17097 return updateRef(); | |
| 17098 }, | |
| 17099 useState: function (initialState) { | |
| 17100 currentHookNameInDev = 'useState'; | |
| 17101 updateHookTypesDev(); | |
| 17102 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17103 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17104 | |
| 17105 try { | |
| 17106 return updateState(initialState); | |
| 17107 } finally { | |
| 17108 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17109 } | |
| 17110 }, | |
| 17111 useDebugValue: function (value, formatterFn) { | |
| 17112 currentHookNameInDev = 'useDebugValue'; | |
| 17113 updateHookTypesDev(); | |
| 17114 return updateDebugValue(); | |
| 17115 }, | |
| 17116 useDeferredValue: function (value) { | |
| 17117 currentHookNameInDev = 'useDeferredValue'; | |
| 17118 updateHookTypesDev(); | |
| 17119 return updateDeferredValue(value); | |
| 17120 }, | |
| 17121 useTransition: function () { | |
| 17122 currentHookNameInDev = 'useTransition'; | |
| 17123 updateHookTypesDev(); | |
| 17124 return updateTransition(); | |
| 17125 }, | |
| 17126 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17127 currentHookNameInDev = 'useMutableSource'; | |
| 17128 updateHookTypesDev(); | |
| 17129 return updateMutableSource(); | |
| 17130 }, | |
| 17131 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17132 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17133 updateHookTypesDev(); | |
| 17134 return updateSyncExternalStore(subscribe, getSnapshot); | |
| 17135 }, | |
| 17136 useId: function () { | |
| 17137 currentHookNameInDev = 'useId'; | |
| 17138 updateHookTypesDev(); | |
| 17139 return updateId(); | |
| 17140 }, | |
| 17141 unstable_isNewReconciler: enableNewReconciler | |
| 17142 }; | |
| 17143 | |
| 17144 HooksDispatcherOnRerenderInDEV = { | |
| 17145 readContext: function (context) { | |
| 17146 return readContext(context); | |
| 17147 }, | |
| 17148 useCallback: function (callback, deps) { | |
| 17149 currentHookNameInDev = 'useCallback'; | |
| 17150 updateHookTypesDev(); | |
| 17151 return updateCallback(callback, deps); | |
| 17152 }, | |
| 17153 useContext: function (context) { | |
| 17154 currentHookNameInDev = 'useContext'; | |
| 17155 updateHookTypesDev(); | |
| 17156 return readContext(context); | |
| 17157 }, | |
| 17158 useEffect: function (create, deps) { | |
| 17159 currentHookNameInDev = 'useEffect'; | |
| 17160 updateHookTypesDev(); | |
| 17161 return updateEffect(create, deps); | |
| 17162 }, | |
| 17163 useImperativeHandle: function (ref, create, deps) { | |
| 17164 currentHookNameInDev = 'useImperativeHandle'; | |
| 17165 updateHookTypesDev(); | |
| 17166 return updateImperativeHandle(ref, create, deps); | |
| 17167 }, | |
| 17168 useInsertionEffect: function (create, deps) { | |
| 17169 currentHookNameInDev = 'useInsertionEffect'; | |
| 17170 updateHookTypesDev(); | |
| 17171 return updateInsertionEffect(create, deps); | |
| 17172 }, | |
| 17173 useLayoutEffect: function (create, deps) { | |
| 17174 currentHookNameInDev = 'useLayoutEffect'; | |
| 17175 updateHookTypesDev(); | |
| 17176 return updateLayoutEffect(create, deps); | |
| 17177 }, | |
| 17178 useMemo: function (create, deps) { | |
| 17179 currentHookNameInDev = 'useMemo'; | |
| 17180 updateHookTypesDev(); | |
| 17181 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17182 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV; | |
| 17183 | |
| 17184 try { | |
| 17185 return updateMemo(create, deps); | |
| 17186 } finally { | |
| 17187 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17188 } | |
| 17189 }, | |
| 17190 useReducer: function (reducer, initialArg, init) { | |
| 17191 currentHookNameInDev = 'useReducer'; | |
| 17192 updateHookTypesDev(); | |
| 17193 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17194 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV; | |
| 17195 | |
| 17196 try { | |
| 17197 return rerenderReducer(reducer, initialArg, init); | |
| 17198 } finally { | |
| 17199 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17200 } | |
| 17201 }, | |
| 17202 useRef: function (initialValue) { | |
| 17203 currentHookNameInDev = 'useRef'; | |
| 17204 updateHookTypesDev(); | |
| 17205 return updateRef(); | |
| 17206 }, | |
| 17207 useState: function (initialState) { | |
| 17208 currentHookNameInDev = 'useState'; | |
| 17209 updateHookTypesDev(); | |
| 17210 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17211 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnRerenderInDEV; | |
| 17212 | |
| 17213 try { | |
| 17214 return rerenderState(initialState); | |
| 17215 } finally { | |
| 17216 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17217 } | |
| 17218 }, | |
| 17219 useDebugValue: function (value, formatterFn) { | |
| 17220 currentHookNameInDev = 'useDebugValue'; | |
| 17221 updateHookTypesDev(); | |
| 17222 return updateDebugValue(); | |
| 17223 }, | |
| 17224 useDeferredValue: function (value) { | |
| 17225 currentHookNameInDev = 'useDeferredValue'; | |
| 17226 updateHookTypesDev(); | |
| 17227 return rerenderDeferredValue(value); | |
| 17228 }, | |
| 17229 useTransition: function () { | |
| 17230 currentHookNameInDev = 'useTransition'; | |
| 17231 updateHookTypesDev(); | |
| 17232 return rerenderTransition(); | |
| 17233 }, | |
| 17234 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17235 currentHookNameInDev = 'useMutableSource'; | |
| 17236 updateHookTypesDev(); | |
| 17237 return updateMutableSource(); | |
| 17238 }, | |
| 17239 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17240 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17241 updateHookTypesDev(); | |
| 17242 return updateSyncExternalStore(subscribe, getSnapshot); | |
| 17243 }, | |
| 17244 useId: function () { | |
| 17245 currentHookNameInDev = 'useId'; | |
| 17246 updateHookTypesDev(); | |
| 17247 return updateId(); | |
| 17248 }, | |
| 17249 unstable_isNewReconciler: enableNewReconciler | |
| 17250 }; | |
| 17251 | |
| 17252 InvalidNestedHooksDispatcherOnMountInDEV = { | |
| 17253 readContext: function (context) { | |
| 17254 warnInvalidContextAccess(); | |
| 17255 return readContext(context); | |
| 17256 }, | |
| 17257 useCallback: function (callback, deps) { | |
| 17258 currentHookNameInDev = 'useCallback'; | |
| 17259 warnInvalidHookAccess(); | |
| 17260 mountHookTypesDev(); | |
| 17261 return mountCallback(callback, deps); | |
| 17262 }, | |
| 17263 useContext: function (context) { | |
| 17264 currentHookNameInDev = 'useContext'; | |
| 17265 warnInvalidHookAccess(); | |
| 17266 mountHookTypesDev(); | |
| 17267 return readContext(context); | |
| 17268 }, | |
| 17269 useEffect: function (create, deps) { | |
| 17270 currentHookNameInDev = 'useEffect'; | |
| 17271 warnInvalidHookAccess(); | |
| 17272 mountHookTypesDev(); | |
| 17273 return mountEffect(create, deps); | |
| 17274 }, | |
| 17275 useImperativeHandle: function (ref, create, deps) { | |
| 17276 currentHookNameInDev = 'useImperativeHandle'; | |
| 17277 warnInvalidHookAccess(); | |
| 17278 mountHookTypesDev(); | |
| 17279 return mountImperativeHandle(ref, create, deps); | |
| 17280 }, | |
| 17281 useInsertionEffect: function (create, deps) { | |
| 17282 currentHookNameInDev = 'useInsertionEffect'; | |
| 17283 warnInvalidHookAccess(); | |
| 17284 mountHookTypesDev(); | |
| 17285 return mountInsertionEffect(create, deps); | |
| 17286 }, | |
| 17287 useLayoutEffect: function (create, deps) { | |
| 17288 currentHookNameInDev = 'useLayoutEffect'; | |
| 17289 warnInvalidHookAccess(); | |
| 17290 mountHookTypesDev(); | |
| 17291 return mountLayoutEffect(create, deps); | |
| 17292 }, | |
| 17293 useMemo: function (create, deps) { | |
| 17294 currentHookNameInDev = 'useMemo'; | |
| 17295 warnInvalidHookAccess(); | |
| 17296 mountHookTypesDev(); | |
| 17297 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17298 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 17299 | |
| 17300 try { | |
| 17301 return mountMemo(create, deps); | |
| 17302 } finally { | |
| 17303 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17304 } | |
| 17305 }, | |
| 17306 useReducer: function (reducer, initialArg, init) { | |
| 17307 currentHookNameInDev = 'useReducer'; | |
| 17308 warnInvalidHookAccess(); | |
| 17309 mountHookTypesDev(); | |
| 17310 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17311 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 17312 | |
| 17313 try { | |
| 17314 return mountReducer(reducer, initialArg, init); | |
| 17315 } finally { | |
| 17316 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17317 } | |
| 17318 }, | |
| 17319 useRef: function (initialValue) { | |
| 17320 currentHookNameInDev = 'useRef'; | |
| 17321 warnInvalidHookAccess(); | |
| 17322 mountHookTypesDev(); | |
| 17323 return mountRef(initialValue); | |
| 17324 }, | |
| 17325 useState: function (initialState) { | |
| 17326 currentHookNameInDev = 'useState'; | |
| 17327 warnInvalidHookAccess(); | |
| 17328 mountHookTypesDev(); | |
| 17329 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17330 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnMountInDEV; | |
| 17331 | |
| 17332 try { | |
| 17333 return mountState(initialState); | |
| 17334 } finally { | |
| 17335 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17336 } | |
| 17337 }, | |
| 17338 useDebugValue: function (value, formatterFn) { | |
| 17339 currentHookNameInDev = 'useDebugValue'; | |
| 17340 warnInvalidHookAccess(); | |
| 17341 mountHookTypesDev(); | |
| 17342 return mountDebugValue(); | |
| 17343 }, | |
| 17344 useDeferredValue: function (value) { | |
| 17345 currentHookNameInDev = 'useDeferredValue'; | |
| 17346 warnInvalidHookAccess(); | |
| 17347 mountHookTypesDev(); | |
| 17348 return mountDeferredValue(value); | |
| 17349 }, | |
| 17350 useTransition: function () { | |
| 17351 currentHookNameInDev = 'useTransition'; | |
| 17352 warnInvalidHookAccess(); | |
| 17353 mountHookTypesDev(); | |
| 17354 return mountTransition(); | |
| 17355 }, | |
| 17356 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17357 currentHookNameInDev = 'useMutableSource'; | |
| 17358 warnInvalidHookAccess(); | |
| 17359 mountHookTypesDev(); | |
| 17360 return mountMutableSource(); | |
| 17361 }, | |
| 17362 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17363 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17364 warnInvalidHookAccess(); | |
| 17365 mountHookTypesDev(); | |
| 17366 return mountSyncExternalStore(subscribe, getSnapshot, getServerSnapshot); | |
| 17367 }, | |
| 17368 useId: function () { | |
| 17369 currentHookNameInDev = 'useId'; | |
| 17370 warnInvalidHookAccess(); | |
| 17371 mountHookTypesDev(); | |
| 17372 return mountId(); | |
| 17373 }, | |
| 17374 unstable_isNewReconciler: enableNewReconciler | |
| 17375 }; | |
| 17376 | |
| 17377 InvalidNestedHooksDispatcherOnUpdateInDEV = { | |
| 17378 readContext: function (context) { | |
| 17379 warnInvalidContextAccess(); | |
| 17380 return readContext(context); | |
| 17381 }, | |
| 17382 useCallback: function (callback, deps) { | |
| 17383 currentHookNameInDev = 'useCallback'; | |
| 17384 warnInvalidHookAccess(); | |
| 17385 updateHookTypesDev(); | |
| 17386 return updateCallback(callback, deps); | |
| 17387 }, | |
| 17388 useContext: function (context) { | |
| 17389 currentHookNameInDev = 'useContext'; | |
| 17390 warnInvalidHookAccess(); | |
| 17391 updateHookTypesDev(); | |
| 17392 return readContext(context); | |
| 17393 }, | |
| 17394 useEffect: function (create, deps) { | |
| 17395 currentHookNameInDev = 'useEffect'; | |
| 17396 warnInvalidHookAccess(); | |
| 17397 updateHookTypesDev(); | |
| 17398 return updateEffect(create, deps); | |
| 17399 }, | |
| 17400 useImperativeHandle: function (ref, create, deps) { | |
| 17401 currentHookNameInDev = 'useImperativeHandle'; | |
| 17402 warnInvalidHookAccess(); | |
| 17403 updateHookTypesDev(); | |
| 17404 return updateImperativeHandle(ref, create, deps); | |
| 17405 }, | |
| 17406 useInsertionEffect: function (create, deps) { | |
| 17407 currentHookNameInDev = 'useInsertionEffect'; | |
| 17408 warnInvalidHookAccess(); | |
| 17409 updateHookTypesDev(); | |
| 17410 return updateInsertionEffect(create, deps); | |
| 17411 }, | |
| 17412 useLayoutEffect: function (create, deps) { | |
| 17413 currentHookNameInDev = 'useLayoutEffect'; | |
| 17414 warnInvalidHookAccess(); | |
| 17415 updateHookTypesDev(); | |
| 17416 return updateLayoutEffect(create, deps); | |
| 17417 }, | |
| 17418 useMemo: function (create, deps) { | |
| 17419 currentHookNameInDev = 'useMemo'; | |
| 17420 warnInvalidHookAccess(); | |
| 17421 updateHookTypesDev(); | |
| 17422 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17423 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17424 | |
| 17425 try { | |
| 17426 return updateMemo(create, deps); | |
| 17427 } finally { | |
| 17428 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17429 } | |
| 17430 }, | |
| 17431 useReducer: function (reducer, initialArg, init) { | |
| 17432 currentHookNameInDev = 'useReducer'; | |
| 17433 warnInvalidHookAccess(); | |
| 17434 updateHookTypesDev(); | |
| 17435 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17436 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17437 | |
| 17438 try { | |
| 17439 return updateReducer(reducer, initialArg, init); | |
| 17440 } finally { | |
| 17441 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17442 } | |
| 17443 }, | |
| 17444 useRef: function (initialValue) { | |
| 17445 currentHookNameInDev = 'useRef'; | |
| 17446 warnInvalidHookAccess(); | |
| 17447 updateHookTypesDev(); | |
| 17448 return updateRef(); | |
| 17449 }, | |
| 17450 useState: function (initialState) { | |
| 17451 currentHookNameInDev = 'useState'; | |
| 17452 warnInvalidHookAccess(); | |
| 17453 updateHookTypesDev(); | |
| 17454 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17455 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17456 | |
| 17457 try { | |
| 17458 return updateState(initialState); | |
| 17459 } finally { | |
| 17460 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17461 } | |
| 17462 }, | |
| 17463 useDebugValue: function (value, formatterFn) { | |
| 17464 currentHookNameInDev = 'useDebugValue'; | |
| 17465 warnInvalidHookAccess(); | |
| 17466 updateHookTypesDev(); | |
| 17467 return updateDebugValue(); | |
| 17468 }, | |
| 17469 useDeferredValue: function (value) { | |
| 17470 currentHookNameInDev = 'useDeferredValue'; | |
| 17471 warnInvalidHookAccess(); | |
| 17472 updateHookTypesDev(); | |
| 17473 return updateDeferredValue(value); | |
| 17474 }, | |
| 17475 useTransition: function () { | |
| 17476 currentHookNameInDev = 'useTransition'; | |
| 17477 warnInvalidHookAccess(); | |
| 17478 updateHookTypesDev(); | |
| 17479 return updateTransition(); | |
| 17480 }, | |
| 17481 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17482 currentHookNameInDev = 'useMutableSource'; | |
| 17483 warnInvalidHookAccess(); | |
| 17484 updateHookTypesDev(); | |
| 17485 return updateMutableSource(); | |
| 17486 }, | |
| 17487 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17488 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17489 warnInvalidHookAccess(); | |
| 17490 updateHookTypesDev(); | |
| 17491 return updateSyncExternalStore(subscribe, getSnapshot); | |
| 17492 }, | |
| 17493 useId: function () { | |
| 17494 currentHookNameInDev = 'useId'; | |
| 17495 warnInvalidHookAccess(); | |
| 17496 updateHookTypesDev(); | |
| 17497 return updateId(); | |
| 17498 }, | |
| 17499 unstable_isNewReconciler: enableNewReconciler | |
| 17500 }; | |
| 17501 | |
| 17502 InvalidNestedHooksDispatcherOnRerenderInDEV = { | |
| 17503 readContext: function (context) { | |
| 17504 warnInvalidContextAccess(); | |
| 17505 return readContext(context); | |
| 17506 }, | |
| 17507 useCallback: function (callback, deps) { | |
| 17508 currentHookNameInDev = 'useCallback'; | |
| 17509 warnInvalidHookAccess(); | |
| 17510 updateHookTypesDev(); | |
| 17511 return updateCallback(callback, deps); | |
| 17512 }, | |
| 17513 useContext: function (context) { | |
| 17514 currentHookNameInDev = 'useContext'; | |
| 17515 warnInvalidHookAccess(); | |
| 17516 updateHookTypesDev(); | |
| 17517 return readContext(context); | |
| 17518 }, | |
| 17519 useEffect: function (create, deps) { | |
| 17520 currentHookNameInDev = 'useEffect'; | |
| 17521 warnInvalidHookAccess(); | |
| 17522 updateHookTypesDev(); | |
| 17523 return updateEffect(create, deps); | |
| 17524 }, | |
| 17525 useImperativeHandle: function (ref, create, deps) { | |
| 17526 currentHookNameInDev = 'useImperativeHandle'; | |
| 17527 warnInvalidHookAccess(); | |
| 17528 updateHookTypesDev(); | |
| 17529 return updateImperativeHandle(ref, create, deps); | |
| 17530 }, | |
| 17531 useInsertionEffect: function (create, deps) { | |
| 17532 currentHookNameInDev = 'useInsertionEffect'; | |
| 17533 warnInvalidHookAccess(); | |
| 17534 updateHookTypesDev(); | |
| 17535 return updateInsertionEffect(create, deps); | |
| 17536 }, | |
| 17537 useLayoutEffect: function (create, deps) { | |
| 17538 currentHookNameInDev = 'useLayoutEffect'; | |
| 17539 warnInvalidHookAccess(); | |
| 17540 updateHookTypesDev(); | |
| 17541 return updateLayoutEffect(create, deps); | |
| 17542 }, | |
| 17543 useMemo: function (create, deps) { | |
| 17544 currentHookNameInDev = 'useMemo'; | |
| 17545 warnInvalidHookAccess(); | |
| 17546 updateHookTypesDev(); | |
| 17547 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17548 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17549 | |
| 17550 try { | |
| 17551 return updateMemo(create, deps); | |
| 17552 } finally { | |
| 17553 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17554 } | |
| 17555 }, | |
| 17556 useReducer: function (reducer, initialArg, init) { | |
| 17557 currentHookNameInDev = 'useReducer'; | |
| 17558 warnInvalidHookAccess(); | |
| 17559 updateHookTypesDev(); | |
| 17560 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17561 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17562 | |
| 17563 try { | |
| 17564 return rerenderReducer(reducer, initialArg, init); | |
| 17565 } finally { | |
| 17566 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17567 } | |
| 17568 }, | |
| 17569 useRef: function (initialValue) { | |
| 17570 currentHookNameInDev = 'useRef'; | |
| 17571 warnInvalidHookAccess(); | |
| 17572 updateHookTypesDev(); | |
| 17573 return updateRef(); | |
| 17574 }, | |
| 17575 useState: function (initialState) { | |
| 17576 currentHookNameInDev = 'useState'; | |
| 17577 warnInvalidHookAccess(); | |
| 17578 updateHookTypesDev(); | |
| 17579 var prevDispatcher = ReactCurrentDispatcher$1.current; | |
| 17580 ReactCurrentDispatcher$1.current = InvalidNestedHooksDispatcherOnUpdateInDEV; | |
| 17581 | |
| 17582 try { | |
| 17583 return rerenderState(initialState); | |
| 17584 } finally { | |
| 17585 ReactCurrentDispatcher$1.current = prevDispatcher; | |
| 17586 } | |
| 17587 }, | |
| 17588 useDebugValue: function (value, formatterFn) { | |
| 17589 currentHookNameInDev = 'useDebugValue'; | |
| 17590 warnInvalidHookAccess(); | |
| 17591 updateHookTypesDev(); | |
| 17592 return updateDebugValue(); | |
| 17593 }, | |
| 17594 useDeferredValue: function (value) { | |
| 17595 currentHookNameInDev = 'useDeferredValue'; | |
| 17596 warnInvalidHookAccess(); | |
| 17597 updateHookTypesDev(); | |
| 17598 return rerenderDeferredValue(value); | |
| 17599 }, | |
| 17600 useTransition: function () { | |
| 17601 currentHookNameInDev = 'useTransition'; | |
| 17602 warnInvalidHookAccess(); | |
| 17603 updateHookTypesDev(); | |
| 17604 return rerenderTransition(); | |
| 17605 }, | |
| 17606 useMutableSource: function (source, getSnapshot, subscribe) { | |
| 17607 currentHookNameInDev = 'useMutableSource'; | |
| 17608 warnInvalidHookAccess(); | |
| 17609 updateHookTypesDev(); | |
| 17610 return updateMutableSource(); | |
| 17611 }, | |
| 17612 useSyncExternalStore: function (subscribe, getSnapshot, getServerSnapshot) { | |
| 17613 currentHookNameInDev = 'useSyncExternalStore'; | |
| 17614 warnInvalidHookAccess(); | |
| 17615 updateHookTypesDev(); | |
| 17616 return updateSyncExternalStore(subscribe, getSnapshot); | |
| 17617 }, | |
| 17618 useId: function () { | |
| 17619 currentHookNameInDev = 'useId'; | |
| 17620 warnInvalidHookAccess(); | |
| 17621 updateHookTypesDev(); | |
| 17622 return updateId(); | |
| 17623 }, | |
| 17624 unstable_isNewReconciler: enableNewReconciler | |
| 17625 }; | |
| 17626 } | |
| 17627 | |
| 17628 var now$1 = unstable_now; | |
| 17629 var commitTime = 0; | |
| 17630 var layoutEffectStartTime = -1; | |
| 17631 var profilerStartTime = -1; | |
| 17632 var passiveEffectStartTime = -1; | |
| 17633 /** | |
| 17634 * Tracks whether the current update was a nested/cascading update (scheduled from a layout effect). | |
| 17635 * | |
| 17636 * The overall sequence is: | |
| 17637 * 1. render | |
| 17638 * 2. commit (and call `onRender`, `onCommit`) | |
| 17639 * 3. check for nested updates | |
| 17640 * 4. flush passive effects (and call `onPostCommit`) | |
| 17641 * | |
| 17642 * Nested updates are identified in step 3 above, | |
| 17643 * but step 4 still applies to the work that was just committed. | |
| 17644 * We use two flags to track nested updates then: | |
| 17645 * one tracks whether the upcoming update is a nested update, | |
| 17646 * and the other tracks whether the current update was a nested update. | |
| 17647 * The first value gets synced to the second at the start of the render phase. | |
| 17648 */ | |
| 17649 | |
| 17650 var currentUpdateIsNested = false; | |
| 17651 var nestedUpdateScheduled = false; | |
| 17652 | |
| 17653 function isCurrentUpdateNested() { | |
| 17654 return currentUpdateIsNested; | |
| 17655 } | |
| 17656 | |
| 17657 function markNestedUpdateScheduled() { | |
| 17658 { | |
| 17659 nestedUpdateScheduled = true; | |
| 17660 } | |
| 17661 } | |
| 17662 | |
| 17663 function resetNestedUpdateFlag() { | |
| 17664 { | |
| 17665 currentUpdateIsNested = false; | |
| 17666 nestedUpdateScheduled = false; | |
| 17667 } | |
| 17668 } | |
| 17669 | |
| 17670 function syncNestedUpdateFlag() { | |
| 17671 { | |
| 17672 currentUpdateIsNested = nestedUpdateScheduled; | |
| 17673 nestedUpdateScheduled = false; | |
| 17674 } | |
| 17675 } | |
| 17676 | |
| 17677 function getCommitTime() { | |
| 17678 return commitTime; | |
| 17679 } | |
| 17680 | |
| 17681 function recordCommitTime() { | |
| 17682 | |
| 17683 commitTime = now$1(); | |
| 17684 } | |
| 17685 | |
| 17686 function startProfilerTimer(fiber) { | |
| 17687 | |
| 17688 profilerStartTime = now$1(); | |
| 17689 | |
| 17690 if (fiber.actualStartTime < 0) { | |
| 17691 fiber.actualStartTime = now$1(); | |
| 17692 } | |
| 17693 } | |
| 17694 | |
| 17695 function stopProfilerTimerIfRunning(fiber) { | |
| 17696 | |
| 17697 profilerStartTime = -1; | |
| 17698 } | |
| 17699 | |
| 17700 function stopProfilerTimerIfRunningAndRecordDelta(fiber, overrideBaseTime) { | |
| 17701 | |
| 17702 if (profilerStartTime >= 0) { | |
| 17703 var elapsedTime = now$1() - profilerStartTime; | |
| 17704 fiber.actualDuration += elapsedTime; | |
| 17705 | |
| 17706 if (overrideBaseTime) { | |
| 17707 fiber.selfBaseDuration = elapsedTime; | |
| 17708 } | |
| 17709 | |
| 17710 profilerStartTime = -1; | |
| 17711 } | |
| 17712 } | |
| 17713 | |
| 17714 function recordLayoutEffectDuration(fiber) { | |
| 17715 | |
| 17716 if (layoutEffectStartTime >= 0) { | |
| 17717 var elapsedTime = now$1() - layoutEffectStartTime; | |
| 17718 layoutEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor | |
| 17719 // Or the root (for the DevTools Profiler to read) | |
| 17720 | |
| 17721 var parentFiber = fiber.return; | |
| 17722 | |
| 17723 while (parentFiber !== null) { | |
| 17724 switch (parentFiber.tag) { | |
| 17725 case HostRoot: | |
| 17726 var root = parentFiber.stateNode; | |
| 17727 root.effectDuration += elapsedTime; | |
| 17728 return; | |
| 17729 | |
| 17730 case Profiler: | |
| 17731 var parentStateNode = parentFiber.stateNode; | |
| 17732 parentStateNode.effectDuration += elapsedTime; | |
| 17733 return; | |
| 17734 } | |
| 17735 | |
| 17736 parentFiber = parentFiber.return; | |
| 17737 } | |
| 17738 } | |
| 17739 } | |
| 17740 | |
| 17741 function recordPassiveEffectDuration(fiber) { | |
| 17742 | |
| 17743 if (passiveEffectStartTime >= 0) { | |
| 17744 var elapsedTime = now$1() - passiveEffectStartTime; | |
| 17745 passiveEffectStartTime = -1; // Store duration on the next nearest Profiler ancestor | |
| 17746 // Or the root (for the DevTools Profiler to read) | |
| 17747 | |
| 17748 var parentFiber = fiber.return; | |
| 17749 | |
| 17750 while (parentFiber !== null) { | |
| 17751 switch (parentFiber.tag) { | |
| 17752 case HostRoot: | |
| 17753 var root = parentFiber.stateNode; | |
| 17754 | |
| 17755 if (root !== null) { | |
| 17756 root.passiveEffectDuration += elapsedTime; | |
| 17757 } | |
| 17758 | |
| 17759 return; | |
| 17760 | |
| 17761 case Profiler: | |
| 17762 var parentStateNode = parentFiber.stateNode; | |
| 17763 | |
| 17764 if (parentStateNode !== null) { | |
| 17765 // Detached fibers have their state node cleared out. | |
| 17766 // In this case, the return pointer is also cleared out, | |
| 17767 // so we won't be able to report the time spent in this Profiler's subtree. | |
| 17768 parentStateNode.passiveEffectDuration += elapsedTime; | |
| 17769 } | |
| 17770 | |
| 17771 return; | |
| 17772 } | |
| 17773 | |
| 17774 parentFiber = parentFiber.return; | |
| 17775 } | |
| 17776 } | |
| 17777 } | |
| 17778 | |
| 17779 function startLayoutEffectTimer() { | |
| 17780 | |
| 17781 layoutEffectStartTime = now$1(); | |
| 17782 } | |
| 17783 | |
| 17784 function startPassiveEffectTimer() { | |
| 17785 | |
| 17786 passiveEffectStartTime = now$1(); | |
| 17787 } | |
| 17788 | |
| 17789 function transferActualDuration(fiber) { | |
| 17790 // Transfer time spent rendering these children so we don't lose it | |
| 17791 // after we rerender. This is used as a helper in special cases | |
| 17792 // where we should count the work of multiple passes. | |
| 17793 var child = fiber.child; | |
| 17794 | |
| 17795 while (child) { | |
| 17796 fiber.actualDuration += child.actualDuration; | |
| 17797 child = child.sibling; | |
| 17798 } | |
| 17799 } | |
| 17800 | |
| 17801 function resolveDefaultProps(Component, baseProps) { | |
| 17802 if (Component && Component.defaultProps) { | |
| 17803 // Resolve default props. Taken from ReactElement | |
| 17804 var props = assign({}, baseProps); | |
| 17805 var defaultProps = Component.defaultProps; | |
| 17806 | |
| 17807 for (var propName in defaultProps) { | |
| 17808 if (props[propName] === undefined) { | |
| 17809 props[propName] = defaultProps[propName]; | |
| 17810 } | |
| 17811 } | |
| 17812 | |
| 17813 return props; | |
| 17814 } | |
| 17815 | |
| 17816 return baseProps; | |
| 17817 } | |
| 17818 | |
| 17819 var fakeInternalInstance = {}; | |
| 17820 var didWarnAboutStateAssignmentForComponent; | |
| 17821 var didWarnAboutUninitializedState; | |
| 17822 var didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate; | |
| 17823 var didWarnAboutLegacyLifecyclesAndDerivedState; | |
| 17824 var didWarnAboutUndefinedDerivedState; | |
| 17825 var warnOnUndefinedDerivedState; | |
| 17826 var warnOnInvalidCallback; | |
| 17827 var didWarnAboutDirectlyAssigningPropsToState; | |
| 17828 var didWarnAboutContextTypeAndContextTypes; | |
| 17829 var didWarnAboutInvalidateContextType; | |
| 17830 var didWarnAboutLegacyContext$1; | |
| 17831 | |
| 17832 { | |
| 17833 didWarnAboutStateAssignmentForComponent = new Set(); | |
| 17834 didWarnAboutUninitializedState = new Set(); | |
| 17835 didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate = new Set(); | |
| 17836 didWarnAboutLegacyLifecyclesAndDerivedState = new Set(); | |
| 17837 didWarnAboutDirectlyAssigningPropsToState = new Set(); | |
| 17838 didWarnAboutUndefinedDerivedState = new Set(); | |
| 17839 didWarnAboutContextTypeAndContextTypes = new Set(); | |
| 17840 didWarnAboutInvalidateContextType = new Set(); | |
| 17841 didWarnAboutLegacyContext$1 = new Set(); | |
| 17842 var didWarnOnInvalidCallback = new Set(); | |
| 17843 | |
| 17844 warnOnInvalidCallback = function (callback, callerName) { | |
| 17845 if (callback === null || typeof callback === 'function') { | |
| 17846 return; | |
| 17847 } | |
| 17848 | |
| 17849 var key = callerName + '_' + callback; | |
| 17850 | |
| 17851 if (!didWarnOnInvalidCallback.has(key)) { | |
| 17852 didWarnOnInvalidCallback.add(key); | |
| 17853 | |
| 17854 error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback); | |
| 17855 } | |
| 17856 }; | |
| 17857 | |
| 17858 warnOnUndefinedDerivedState = function (type, partialState) { | |
| 17859 if (partialState === undefined) { | |
| 17860 var componentName = getComponentNameFromType(type) || 'Component'; | |
| 17861 | |
| 17862 if (!didWarnAboutUndefinedDerivedState.has(componentName)) { | |
| 17863 didWarnAboutUndefinedDerivedState.add(componentName); | |
| 17864 | |
| 17865 error('%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' + 'You have returned undefined.', componentName); | |
| 17866 } | |
| 17867 } | |
| 17868 }; // This is so gross but it's at least non-critical and can be removed if | |
| 17869 // it causes problems. This is meant to give a nicer error message for | |
| 17870 // ReactDOM15.unstable_renderSubtreeIntoContainer(reactDOM16Component, | |
| 17871 // ...)) which otherwise throws a "_processChildContext is not a function" | |
| 17872 // exception. | |
| 17873 | |
| 17874 | |
| 17875 Object.defineProperty(fakeInternalInstance, '_processChildContext', { | |
| 17876 enumerable: false, | |
| 17877 value: function () { | |
| 17878 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).'); | |
| 17879 } | |
| 17880 }); | |
| 17881 Object.freeze(fakeInternalInstance); | |
| 17882 } | |
| 17883 | |
| 17884 function applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, nextProps) { | |
| 17885 var prevState = workInProgress.memoizedState; | |
| 17886 var partialState = getDerivedStateFromProps(nextProps, prevState); | |
| 17887 | |
| 17888 { | |
| 17889 if ( workInProgress.mode & StrictLegacyMode) { | |
| 17890 setIsStrictModeForDevtools(true); | |
| 17891 | |
| 17892 try { | |
| 17893 // Invoke the function an extra time to help detect side-effects. | |
| 17894 partialState = getDerivedStateFromProps(nextProps, prevState); | |
| 17895 } finally { | |
| 17896 setIsStrictModeForDevtools(false); | |
| 17897 } | |
| 17898 } | |
| 17899 | |
| 17900 warnOnUndefinedDerivedState(ctor, partialState); | |
| 17901 } // Merge the partial state and the previous state. | |
| 17902 | |
| 17903 | |
| 17904 var memoizedState = partialState === null || partialState === undefined ? prevState : assign({}, prevState, partialState); | |
| 17905 workInProgress.memoizedState = memoizedState; // Once the update queue is empty, persist the derived state onto the | |
| 17906 // base state. | |
| 17907 | |
| 17908 if (workInProgress.lanes === NoLanes) { | |
| 17909 // Queue is always non-null for classes | |
| 17910 var updateQueue = workInProgress.updateQueue; | |
| 17911 updateQueue.baseState = memoizedState; | |
| 17912 } | |
| 17913 } | |
| 17914 | |
| 17915 var classComponentUpdater = { | |
| 17916 isMounted: isMounted, | |
| 17917 enqueueSetState: function (inst, payload, callback) { | |
| 17918 var fiber = get(inst); | |
| 17919 var eventTime = requestEventTime(); | |
| 17920 var lane = requestUpdateLane(fiber); | |
| 17921 var update = createUpdate(eventTime, lane); | |
| 17922 update.payload = payload; | |
| 17923 | |
| 17924 if (callback !== undefined && callback !== null) { | |
| 17925 { | |
| 17926 warnOnInvalidCallback(callback, 'setState'); | |
| 17927 } | |
| 17928 | |
| 17929 update.callback = callback; | |
| 17930 } | |
| 17931 | |
| 17932 var root = enqueueUpdate(fiber, update, lane); | |
| 17933 | |
| 17934 if (root !== null) { | |
| 17935 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 17936 entangleTransitions(root, fiber, lane); | |
| 17937 } | |
| 17938 | |
| 17939 { | |
| 17940 markStateUpdateScheduled(fiber, lane); | |
| 17941 } | |
| 17942 }, | |
| 17943 enqueueReplaceState: function (inst, payload, callback) { | |
| 17944 var fiber = get(inst); | |
| 17945 var eventTime = requestEventTime(); | |
| 17946 var lane = requestUpdateLane(fiber); | |
| 17947 var update = createUpdate(eventTime, lane); | |
| 17948 update.tag = ReplaceState; | |
| 17949 update.payload = payload; | |
| 17950 | |
| 17951 if (callback !== undefined && callback !== null) { | |
| 17952 { | |
| 17953 warnOnInvalidCallback(callback, 'replaceState'); | |
| 17954 } | |
| 17955 | |
| 17956 update.callback = callback; | |
| 17957 } | |
| 17958 | |
| 17959 var root = enqueueUpdate(fiber, update, lane); | |
| 17960 | |
| 17961 if (root !== null) { | |
| 17962 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 17963 entangleTransitions(root, fiber, lane); | |
| 17964 } | |
| 17965 | |
| 17966 { | |
| 17967 markStateUpdateScheduled(fiber, lane); | |
| 17968 } | |
| 17969 }, | |
| 17970 enqueueForceUpdate: function (inst, callback) { | |
| 17971 var fiber = get(inst); | |
| 17972 var eventTime = requestEventTime(); | |
| 17973 var lane = requestUpdateLane(fiber); | |
| 17974 var update = createUpdate(eventTime, lane); | |
| 17975 update.tag = ForceUpdate; | |
| 17976 | |
| 17977 if (callback !== undefined && callback !== null) { | |
| 17978 { | |
| 17979 warnOnInvalidCallback(callback, 'forceUpdate'); | |
| 17980 } | |
| 17981 | |
| 17982 update.callback = callback; | |
| 17983 } | |
| 17984 | |
| 17985 var root = enqueueUpdate(fiber, update, lane); | |
| 17986 | |
| 17987 if (root !== null) { | |
| 17988 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 17989 entangleTransitions(root, fiber, lane); | |
| 17990 } | |
| 17991 | |
| 17992 { | |
| 17993 markForceUpdateScheduled(fiber, lane); | |
| 17994 } | |
| 17995 } | |
| 17996 }; | |
| 17997 | |
| 17998 function checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext) { | |
| 17999 var instance = workInProgress.stateNode; | |
| 18000 | |
| 18001 if (typeof instance.shouldComponentUpdate === 'function') { | |
| 18002 var shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext); | |
| 18003 | |
| 18004 { | |
| 18005 if ( workInProgress.mode & StrictLegacyMode) { | |
| 18006 setIsStrictModeForDevtools(true); | |
| 18007 | |
| 18008 try { | |
| 18009 // Invoke the function an extra time to help detect side-effects. | |
| 18010 shouldUpdate = instance.shouldComponentUpdate(newProps, newState, nextContext); | |
| 18011 } finally { | |
| 18012 setIsStrictModeForDevtools(false); | |
| 18013 } | |
| 18014 } | |
| 18015 | |
| 18016 if (shouldUpdate === undefined) { | |
| 18017 error('%s.shouldComponentUpdate(): Returned undefined instead of a ' + 'boolean value. Make sure to return true or false.', getComponentNameFromType(ctor) || 'Component'); | |
| 18018 } | |
| 18019 } | |
| 18020 | |
| 18021 return shouldUpdate; | |
| 18022 } | |
| 18023 | |
| 18024 if (ctor.prototype && ctor.prototype.isPureReactComponent) { | |
| 18025 return !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState); | |
| 18026 } | |
| 18027 | |
| 18028 return true; | |
| 18029 } | |
| 18030 | |
| 18031 function checkClassInstance(workInProgress, ctor, newProps) { | |
| 18032 var instance = workInProgress.stateNode; | |
| 18033 | |
| 18034 { | |
| 18035 var name = getComponentNameFromType(ctor) || 'Component'; | |
| 18036 var renderPresent = instance.render; | |
| 18037 | |
| 18038 if (!renderPresent) { | |
| 18039 if (ctor.prototype && typeof ctor.prototype.render === 'function') { | |
| 18040 error('%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name); | |
| 18041 } else { | |
| 18042 error('%s(...): No `render` method found on the returned component ' + 'instance: you may have forgotten to define `render`.', name); | |
| 18043 } | |
| 18044 } | |
| 18045 | |
| 18046 if (instance.getInitialState && !instance.getInitialState.isReactClassApproved && !instance.state) { | |
| 18047 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); | |
| 18048 } | |
| 18049 | |
| 18050 if (instance.getDefaultProps && !instance.getDefaultProps.isReactClassApproved) { | |
| 18051 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); | |
| 18052 } | |
| 18053 | |
| 18054 if (instance.propTypes) { | |
| 18055 error('propTypes was defined as an instance property on %s. Use a static ' + 'property to define propTypes instead.', name); | |
| 18056 } | |
| 18057 | |
| 18058 if (instance.contextType) { | |
| 18059 error('contextType was defined as an instance property on %s. Use a static ' + 'property to define contextType instead.', name); | |
| 18060 } | |
| 18061 | |
| 18062 { | |
| 18063 if (ctor.childContextTypes && !didWarnAboutLegacyContext$1.has(ctor) && // Strict Mode has its own warning for legacy context, so we can skip | |
| 18064 // this one. | |
| 18065 (workInProgress.mode & StrictLegacyMode) === NoMode) { | |
| 18066 didWarnAboutLegacyContext$1.add(ctor); | |
| 18067 | |
| 18068 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); | |
| 18069 } | |
| 18070 | |
| 18071 if (ctor.contextTypes && !didWarnAboutLegacyContext$1.has(ctor) && // Strict Mode has its own warning for legacy context, so we can skip | |
| 18072 // this one. | |
| 18073 (workInProgress.mode & StrictLegacyMode) === NoMode) { | |
| 18074 didWarnAboutLegacyContext$1.add(ctor); | |
| 18075 | |
| 18076 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); | |
| 18077 } | |
| 18078 | |
| 18079 if (instance.contextTypes) { | |
| 18080 error('contextTypes was defined as an instance property on %s. Use a static ' + 'property to define contextTypes instead.', name); | |
| 18081 } | |
| 18082 | |
| 18083 if (ctor.contextType && ctor.contextTypes && !didWarnAboutContextTypeAndContextTypes.has(ctor)) { | |
| 18084 didWarnAboutContextTypeAndContextTypes.add(ctor); | |
| 18085 | |
| 18086 error('%s declares both contextTypes and contextType static properties. ' + 'The legacy contextTypes property will be ignored.', name); | |
| 18087 } | |
| 18088 } | |
| 18089 | |
| 18090 if (typeof instance.componentShouldUpdate === 'function') { | |
| 18091 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); | |
| 18092 } | |
| 18093 | |
| 18094 if (ctor.prototype && ctor.prototype.isPureReactComponent && typeof instance.shouldComponentUpdate !== 'undefined') { | |
| 18095 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'); | |
| 18096 } | |
| 18097 | |
| 18098 if (typeof instance.componentDidUnmount === 'function') { | |
| 18099 error('%s has a method called ' + 'componentDidUnmount(). But there is no such lifecycle method. ' + 'Did you mean componentWillUnmount()?', name); | |
| 18100 } | |
| 18101 | |
| 18102 if (typeof instance.componentDidReceiveProps === 'function') { | |
| 18103 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); | |
| 18104 } | |
| 18105 | |
| 18106 if (typeof instance.componentWillRecieveProps === 'function') { | |
| 18107 error('%s has a method called ' + 'componentWillRecieveProps(). Did you mean componentWillReceiveProps()?', name); | |
| 18108 } | |
| 18109 | |
| 18110 if (typeof instance.UNSAFE_componentWillRecieveProps === 'function') { | |
| 18111 error('%s has a method called ' + 'UNSAFE_componentWillRecieveProps(). Did you mean UNSAFE_componentWillReceiveProps()?', name); | |
| 18112 } | |
| 18113 | |
| 18114 var hasMutatedProps = instance.props !== newProps; | |
| 18115 | |
| 18116 if (instance.props !== undefined && hasMutatedProps) { | |
| 18117 error('%s(...): When calling super() in `%s`, make sure to pass ' + "up the same props that your component's constructor was passed.", name, name); | |
| 18118 } | |
| 18119 | |
| 18120 if (instance.defaultProps) { | |
| 18121 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); | |
| 18122 } | |
| 18123 | |
| 18124 if (typeof instance.getSnapshotBeforeUpdate === 'function' && typeof instance.componentDidUpdate !== 'function' && !didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.has(ctor)) { | |
| 18125 didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate.add(ctor); | |
| 18126 | |
| 18127 error('%s: getSnapshotBeforeUpdate() should be used with componentDidUpdate(). ' + 'This component defines getSnapshotBeforeUpdate() only.', getComponentNameFromType(ctor)); | |
| 18128 } | |
| 18129 | |
| 18130 if (typeof instance.getDerivedStateFromProps === 'function') { | |
| 18131 error('%s: getDerivedStateFromProps() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name); | |
| 18132 } | |
| 18133 | |
| 18134 if (typeof instance.getDerivedStateFromError === 'function') { | |
| 18135 error('%s: getDerivedStateFromError() is defined as an instance method ' + 'and will be ignored. Instead, declare it as a static method.', name); | |
| 18136 } | |
| 18137 | |
| 18138 if (typeof ctor.getSnapshotBeforeUpdate === 'function') { | |
| 18139 error('%s: getSnapshotBeforeUpdate() is defined as a static method ' + 'and will be ignored. Instead, declare it as an instance method.', name); | |
| 18140 } | |
| 18141 | |
| 18142 var _state = instance.state; | |
| 18143 | |
| 18144 if (_state && (typeof _state !== 'object' || isArray(_state))) { | |
| 18145 error('%s.state: must be set to an object or null', name); | |
| 18146 } | |
| 18147 | |
| 18148 if (typeof instance.getChildContext === 'function' && typeof ctor.childContextTypes !== 'object') { | |
| 18149 error('%s.getChildContext(): childContextTypes must be defined in order to ' + 'use getChildContext().', name); | |
| 18150 } | |
| 18151 } | |
| 18152 } | |
| 18153 | |
| 18154 function adoptClassInstance(workInProgress, instance) { | |
| 18155 instance.updater = classComponentUpdater; | |
| 18156 workInProgress.stateNode = instance; // The instance needs access to the fiber so that it can schedule updates | |
| 18157 | |
| 18158 set(instance, workInProgress); | |
| 18159 | |
| 18160 { | |
| 18161 instance._reactInternalInstance = fakeInternalInstance; | |
| 18162 } | |
| 18163 } | |
| 18164 | |
| 18165 function constructClassInstance(workInProgress, ctor, props) { | |
| 18166 var isLegacyContextConsumer = false; | |
| 18167 var unmaskedContext = emptyContextObject; | |
| 18168 var context = emptyContextObject; | |
| 18169 var contextType = ctor.contextType; | |
| 18170 | |
| 18171 { | |
| 18172 if ('contextType' in ctor) { | |
| 18173 var isValid = // Allow null for conditional declaration | |
| 18174 contextType === null || contextType !== undefined && contextType.$$typeof === REACT_CONTEXT_TYPE && contextType._context === undefined; // Not a <Context.Consumer> | |
| 18175 | |
| 18176 if (!isValid && !didWarnAboutInvalidateContextType.has(ctor)) { | |
| 18177 didWarnAboutInvalidateContextType.add(ctor); | |
| 18178 var addendum = ''; | |
| 18179 | |
| 18180 if (contextType === undefined) { | |
| 18181 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.'; | |
| 18182 } else if (typeof contextType !== 'object') { | |
| 18183 addendum = ' However, it is set to a ' + typeof contextType + '.'; | |
| 18184 } else if (contextType.$$typeof === REACT_PROVIDER_TYPE) { | |
| 18185 addendum = ' Did you accidentally pass the Context.Provider instead?'; | |
| 18186 } else if (contextType._context !== undefined) { | |
| 18187 // <Context.Consumer> | |
| 18188 addendum = ' Did you accidentally pass the Context.Consumer instead?'; | |
| 18189 } else { | |
| 18190 addendum = ' However, it is set to an object with keys {' + Object.keys(contextType).join(', ') + '}.'; | |
| 18191 } | |
| 18192 | |
| 18193 error('%s defines an invalid contextType. ' + 'contextType should point to the Context object returned by React.createContext().%s', getComponentNameFromType(ctor) || 'Component', addendum); | |
| 18194 } | |
| 18195 } | |
| 18196 } | |
| 18197 | |
| 18198 if (typeof contextType === 'object' && contextType !== null) { | |
| 18199 context = readContext(contextType); | |
| 18200 } else { | |
| 18201 unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | |
| 18202 var contextTypes = ctor.contextTypes; | |
| 18203 isLegacyContextConsumer = contextTypes !== null && contextTypes !== undefined; | |
| 18204 context = isLegacyContextConsumer ? getMaskedContext(workInProgress, unmaskedContext) : emptyContextObject; | |
| 18205 } | |
| 18206 | |
| 18207 var instance = new ctor(props, context); // Instantiate twice to help detect side-effects. | |
| 18208 | |
| 18209 { | |
| 18210 if ( workInProgress.mode & StrictLegacyMode) { | |
| 18211 setIsStrictModeForDevtools(true); | |
| 18212 | |
| 18213 try { | |
| 18214 instance = new ctor(props, context); // eslint-disable-line no-new | |
| 18215 } finally { | |
| 18216 setIsStrictModeForDevtools(false); | |
| 18217 } | |
| 18218 } | |
| 18219 } | |
| 18220 | |
| 18221 var state = workInProgress.memoizedState = instance.state !== null && instance.state !== undefined ? instance.state : null; | |
| 18222 adoptClassInstance(workInProgress, instance); | |
| 18223 | |
| 18224 { | |
| 18225 if (typeof ctor.getDerivedStateFromProps === 'function' && state === null) { | |
| 18226 var componentName = getComponentNameFromType(ctor) || 'Component'; | |
| 18227 | |
| 18228 if (!didWarnAboutUninitializedState.has(componentName)) { | |
| 18229 didWarnAboutUninitializedState.add(componentName); | |
| 18230 | |
| 18231 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); | |
| 18232 } | |
| 18233 } // If new component APIs are defined, "unsafe" lifecycles won't be called. | |
| 18234 // Warn about these lifecycles if they are present. | |
| 18235 // Don't warn about react-lifecycles-compat polyfilled methods though. | |
| 18236 | |
| 18237 | |
| 18238 if (typeof ctor.getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function') { | |
| 18239 var foundWillMountName = null; | |
| 18240 var foundWillReceivePropsName = null; | |
| 18241 var foundWillUpdateName = null; | |
| 18242 | |
| 18243 if (typeof instance.componentWillMount === 'function' && instance.componentWillMount.__suppressDeprecationWarning !== true) { | |
| 18244 foundWillMountName = 'componentWillMount'; | |
| 18245 } else if (typeof instance.UNSAFE_componentWillMount === 'function') { | |
| 18246 foundWillMountName = 'UNSAFE_componentWillMount'; | |
| 18247 } | |
| 18248 | |
| 18249 if (typeof instance.componentWillReceiveProps === 'function' && instance.componentWillReceiveProps.__suppressDeprecationWarning !== true) { | |
| 18250 foundWillReceivePropsName = 'componentWillReceiveProps'; | |
| 18251 } else if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') { | |
| 18252 foundWillReceivePropsName = 'UNSAFE_componentWillReceiveProps'; | |
| 18253 } | |
| 18254 | |
| 18255 if (typeof instance.componentWillUpdate === 'function' && instance.componentWillUpdate.__suppressDeprecationWarning !== true) { | |
| 18256 foundWillUpdateName = 'componentWillUpdate'; | |
| 18257 } else if (typeof instance.UNSAFE_componentWillUpdate === 'function') { | |
| 18258 foundWillUpdateName = 'UNSAFE_componentWillUpdate'; | |
| 18259 } | |
| 18260 | |
| 18261 if (foundWillMountName !== null || foundWillReceivePropsName !== null || foundWillUpdateName !== null) { | |
| 18262 var _componentName = getComponentNameFromType(ctor) || 'Component'; | |
| 18263 | |
| 18264 var newApiName = typeof ctor.getDerivedStateFromProps === 'function' ? 'getDerivedStateFromProps()' : 'getSnapshotBeforeUpdate()'; | |
| 18265 | |
| 18266 if (!didWarnAboutLegacyLifecyclesAndDerivedState.has(_componentName)) { | |
| 18267 didWarnAboutLegacyLifecyclesAndDerivedState.add(_componentName); | |
| 18268 | |
| 18269 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 : ''); | |
| 18270 } | |
| 18271 } | |
| 18272 } | |
| 18273 } // Cache unmasked context so we can avoid recreating masked context unless necessary. | |
| 18274 // ReactFiberContext usually updates this cache but can't for newly-created instances. | |
| 18275 | |
| 18276 | |
| 18277 if (isLegacyContextConsumer) { | |
| 18278 cacheContext(workInProgress, unmaskedContext, context); | |
| 18279 } | |
| 18280 | |
| 18281 return instance; | |
| 18282 } | |
| 18283 | |
| 18284 function callComponentWillMount(workInProgress, instance) { | |
| 18285 var oldState = instance.state; | |
| 18286 | |
| 18287 if (typeof instance.componentWillMount === 'function') { | |
| 18288 instance.componentWillMount(); | |
| 18289 } | |
| 18290 | |
| 18291 if (typeof instance.UNSAFE_componentWillMount === 'function') { | |
| 18292 instance.UNSAFE_componentWillMount(); | |
| 18293 } | |
| 18294 | |
| 18295 if (oldState !== instance.state) { | |
| 18296 { | |
| 18297 error('%s.componentWillMount(): Assigning directly to this.state is ' + "deprecated (except inside a component's " + 'constructor). Use setState instead.', getComponentNameFromFiber(workInProgress) || 'Component'); | |
| 18298 } | |
| 18299 | |
| 18300 classComponentUpdater.enqueueReplaceState(instance, instance.state, null); | |
| 18301 } | |
| 18302 } | |
| 18303 | |
| 18304 function callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext) { | |
| 18305 var oldState = instance.state; | |
| 18306 | |
| 18307 if (typeof instance.componentWillReceiveProps === 'function') { | |
| 18308 instance.componentWillReceiveProps(newProps, nextContext); | |
| 18309 } | |
| 18310 | |
| 18311 if (typeof instance.UNSAFE_componentWillReceiveProps === 'function') { | |
| 18312 instance.UNSAFE_componentWillReceiveProps(newProps, nextContext); | |
| 18313 } | |
| 18314 | |
| 18315 if (instance.state !== oldState) { | |
| 18316 { | |
| 18317 var componentName = getComponentNameFromFiber(workInProgress) || 'Component'; | |
| 18318 | |
| 18319 if (!didWarnAboutStateAssignmentForComponent.has(componentName)) { | |
| 18320 didWarnAboutStateAssignmentForComponent.add(componentName); | |
| 18321 | |
| 18322 error('%s.componentWillReceiveProps(): Assigning directly to ' + "this.state is deprecated (except inside a component's " + 'constructor). Use setState instead.', componentName); | |
| 18323 } | |
| 18324 } | |
| 18325 | |
| 18326 classComponentUpdater.enqueueReplaceState(instance, instance.state, null); | |
| 18327 } | |
| 18328 } // Invokes the mount life-cycles on a previously never rendered instance. | |
| 18329 | |
| 18330 | |
| 18331 function mountClassInstance(workInProgress, ctor, newProps, renderLanes) { | |
| 18332 { | |
| 18333 checkClassInstance(workInProgress, ctor, newProps); | |
| 18334 } | |
| 18335 | |
| 18336 var instance = workInProgress.stateNode; | |
| 18337 instance.props = newProps; | |
| 18338 instance.state = workInProgress.memoizedState; | |
| 18339 instance.refs = {}; | |
| 18340 initializeUpdateQueue(workInProgress); | |
| 18341 var contextType = ctor.contextType; | |
| 18342 | |
| 18343 if (typeof contextType === 'object' && contextType !== null) { | |
| 18344 instance.context = readContext(contextType); | |
| 18345 } else { | |
| 18346 var unmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | |
| 18347 instance.context = getMaskedContext(workInProgress, unmaskedContext); | |
| 18348 } | |
| 18349 | |
| 18350 { | |
| 18351 if (instance.state === newProps) { | |
| 18352 var componentName = getComponentNameFromType(ctor) || 'Component'; | |
| 18353 | |
| 18354 if (!didWarnAboutDirectlyAssigningPropsToState.has(componentName)) { | |
| 18355 didWarnAboutDirectlyAssigningPropsToState.add(componentName); | |
| 18356 | |
| 18357 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); | |
| 18358 } | |
| 18359 } | |
| 18360 | |
| 18361 if (workInProgress.mode & StrictLegacyMode) { | |
| 18362 ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, instance); | |
| 18363 } | |
| 18364 | |
| 18365 { | |
| 18366 ReactStrictModeWarnings.recordUnsafeLifecycleWarnings(workInProgress, instance); | |
| 18367 } | |
| 18368 } | |
| 18369 | |
| 18370 instance.state = workInProgress.memoizedState; | |
| 18371 var getDerivedStateFromProps = ctor.getDerivedStateFromProps; | |
| 18372 | |
| 18373 if (typeof getDerivedStateFromProps === 'function') { | |
| 18374 applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps); | |
| 18375 instance.state = workInProgress.memoizedState; | |
| 18376 } // In order to support react-lifecycles-compat polyfilled components, | |
| 18377 // Unsafe lifecycles should not be invoked for components using the new APIs. | |
| 18378 | |
| 18379 | |
| 18380 if (typeof ctor.getDerivedStateFromProps !== 'function' && typeof instance.getSnapshotBeforeUpdate !== 'function' && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) { | |
| 18381 callComponentWillMount(workInProgress, instance); // If we had additional state updates during this life-cycle, let's | |
| 18382 // process them now. | |
| 18383 | |
| 18384 processUpdateQueue(workInProgress, newProps, instance, renderLanes); | |
| 18385 instance.state = workInProgress.memoizedState; | |
| 18386 } | |
| 18387 | |
| 18388 if (typeof instance.componentDidMount === 'function') { | |
| 18389 var fiberFlags = Update; | |
| 18390 | |
| 18391 { | |
| 18392 fiberFlags |= LayoutStatic; | |
| 18393 } | |
| 18394 | |
| 18395 if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) { | |
| 18396 fiberFlags |= MountLayoutDev; | |
| 18397 } | |
| 18398 | |
| 18399 workInProgress.flags |= fiberFlags; | |
| 18400 } | |
| 18401 } | |
| 18402 | |
| 18403 function resumeMountClassInstance(workInProgress, ctor, newProps, renderLanes) { | |
| 18404 var instance = workInProgress.stateNode; | |
| 18405 var oldProps = workInProgress.memoizedProps; | |
| 18406 instance.props = oldProps; | |
| 18407 var oldContext = instance.context; | |
| 18408 var contextType = ctor.contextType; | |
| 18409 var nextContext = emptyContextObject; | |
| 18410 | |
| 18411 if (typeof contextType === 'object' && contextType !== null) { | |
| 18412 nextContext = readContext(contextType); | |
| 18413 } else { | |
| 18414 var nextLegacyUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | |
| 18415 nextContext = getMaskedContext(workInProgress, nextLegacyUnmaskedContext); | |
| 18416 } | |
| 18417 | |
| 18418 var getDerivedStateFromProps = ctor.getDerivedStateFromProps; | |
| 18419 var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what | |
| 18420 // ever the previously attempted to render - not the "current". However, | |
| 18421 // during componentDidUpdate we pass the "current" props. | |
| 18422 // In order to support react-lifecycles-compat polyfilled components, | |
| 18423 // Unsafe lifecycles should not be invoked for components using the new APIs. | |
| 18424 | |
| 18425 if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) { | |
| 18426 if (oldProps !== newProps || oldContext !== nextContext) { | |
| 18427 callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext); | |
| 18428 } | |
| 18429 } | |
| 18430 | |
| 18431 resetHasForceUpdateBeforeProcessing(); | |
| 18432 var oldState = workInProgress.memoizedState; | |
| 18433 var newState = instance.state = oldState; | |
| 18434 processUpdateQueue(workInProgress, newProps, instance, renderLanes); | |
| 18435 newState = workInProgress.memoizedState; | |
| 18436 | |
| 18437 if (oldProps === newProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing()) { | |
| 18438 // If an update was already in progress, we should schedule an Update | |
| 18439 // effect even though we're bailing out, so that cWU/cDU are called. | |
| 18440 if (typeof instance.componentDidMount === 'function') { | |
| 18441 var fiberFlags = Update; | |
| 18442 | |
| 18443 { | |
| 18444 fiberFlags |= LayoutStatic; | |
| 18445 } | |
| 18446 | |
| 18447 if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) { | |
| 18448 fiberFlags |= MountLayoutDev; | |
| 18449 } | |
| 18450 | |
| 18451 workInProgress.flags |= fiberFlags; | |
| 18452 } | |
| 18453 | |
| 18454 return false; | |
| 18455 } | |
| 18456 | |
| 18457 if (typeof getDerivedStateFromProps === 'function') { | |
| 18458 applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps); | |
| 18459 newState = workInProgress.memoizedState; | |
| 18460 } | |
| 18461 | |
| 18462 var shouldUpdate = checkHasForceUpdateAfterProcessing() || checkShouldComponentUpdate(workInProgress, ctor, oldProps, newProps, oldState, newState, nextContext); | |
| 18463 | |
| 18464 if (shouldUpdate) { | |
| 18465 // In order to support react-lifecycles-compat polyfilled components, | |
| 18466 // Unsafe lifecycles should not be invoked for components using the new APIs. | |
| 18467 if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillMount === 'function' || typeof instance.componentWillMount === 'function')) { | |
| 18468 if (typeof instance.componentWillMount === 'function') { | |
| 18469 instance.componentWillMount(); | |
| 18470 } | |
| 18471 | |
| 18472 if (typeof instance.UNSAFE_componentWillMount === 'function') { | |
| 18473 instance.UNSAFE_componentWillMount(); | |
| 18474 } | |
| 18475 } | |
| 18476 | |
| 18477 if (typeof instance.componentDidMount === 'function') { | |
| 18478 var _fiberFlags = Update; | |
| 18479 | |
| 18480 { | |
| 18481 _fiberFlags |= LayoutStatic; | |
| 18482 } | |
| 18483 | |
| 18484 if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) { | |
| 18485 _fiberFlags |= MountLayoutDev; | |
| 18486 } | |
| 18487 | |
| 18488 workInProgress.flags |= _fiberFlags; | |
| 18489 } | |
| 18490 } else { | |
| 18491 // If an update was already in progress, we should schedule an Update | |
| 18492 // effect even though we're bailing out, so that cWU/cDU are called. | |
| 18493 if (typeof instance.componentDidMount === 'function') { | |
| 18494 var _fiberFlags2 = Update; | |
| 18495 | |
| 18496 { | |
| 18497 _fiberFlags2 |= LayoutStatic; | |
| 18498 } | |
| 18499 | |
| 18500 if ( (workInProgress.mode & StrictEffectsMode) !== NoMode) { | |
| 18501 _fiberFlags2 |= MountLayoutDev; | |
| 18502 } | |
| 18503 | |
| 18504 workInProgress.flags |= _fiberFlags2; | |
| 18505 } // If shouldComponentUpdate returned false, we should still update the | |
| 18506 // memoized state to indicate that this work can be reused. | |
| 18507 | |
| 18508 | |
| 18509 workInProgress.memoizedProps = newProps; | |
| 18510 workInProgress.memoizedState = newState; | |
| 18511 } // Update the existing instance's state, props, and context pointers even | |
| 18512 // if shouldComponentUpdate returns false. | |
| 18513 | |
| 18514 | |
| 18515 instance.props = newProps; | |
| 18516 instance.state = newState; | |
| 18517 instance.context = nextContext; | |
| 18518 return shouldUpdate; | |
| 18519 } // Invokes the update life-cycles and returns false if it shouldn't rerender. | |
| 18520 | |
| 18521 | |
| 18522 function updateClassInstance(current, workInProgress, ctor, newProps, renderLanes) { | |
| 18523 var instance = workInProgress.stateNode; | |
| 18524 cloneUpdateQueue(current, workInProgress); | |
| 18525 var unresolvedOldProps = workInProgress.memoizedProps; | |
| 18526 var oldProps = workInProgress.type === workInProgress.elementType ? unresolvedOldProps : resolveDefaultProps(workInProgress.type, unresolvedOldProps); | |
| 18527 instance.props = oldProps; | |
| 18528 var unresolvedNewProps = workInProgress.pendingProps; | |
| 18529 var oldContext = instance.context; | |
| 18530 var contextType = ctor.contextType; | |
| 18531 var nextContext = emptyContextObject; | |
| 18532 | |
| 18533 if (typeof contextType === 'object' && contextType !== null) { | |
| 18534 nextContext = readContext(contextType); | |
| 18535 } else { | |
| 18536 var nextUnmaskedContext = getUnmaskedContext(workInProgress, ctor, true); | |
| 18537 nextContext = getMaskedContext(workInProgress, nextUnmaskedContext); | |
| 18538 } | |
| 18539 | |
| 18540 var getDerivedStateFromProps = ctor.getDerivedStateFromProps; | |
| 18541 var hasNewLifecycles = typeof getDerivedStateFromProps === 'function' || typeof instance.getSnapshotBeforeUpdate === 'function'; // Note: During these life-cycles, instance.props/instance.state are what | |
| 18542 // ever the previously attempted to render - not the "current". However, | |
| 18543 // during componentDidUpdate we pass the "current" props. | |
| 18544 // In order to support react-lifecycles-compat polyfilled components, | |
| 18545 // Unsafe lifecycles should not be invoked for components using the new APIs. | |
| 18546 | |
| 18547 if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillReceiveProps === 'function' || typeof instance.componentWillReceiveProps === 'function')) { | |
| 18548 if (unresolvedOldProps !== unresolvedNewProps || oldContext !== nextContext) { | |
| 18549 callComponentWillReceiveProps(workInProgress, instance, newProps, nextContext); | |
| 18550 } | |
| 18551 } | |
| 18552 | |
| 18553 resetHasForceUpdateBeforeProcessing(); | |
| 18554 var oldState = workInProgress.memoizedState; | |
| 18555 var newState = instance.state = oldState; | |
| 18556 processUpdateQueue(workInProgress, newProps, instance, renderLanes); | |
| 18557 newState = workInProgress.memoizedState; | |
| 18558 | |
| 18559 if (unresolvedOldProps === unresolvedNewProps && oldState === newState && !hasContextChanged() && !checkHasForceUpdateAfterProcessing() && !(enableLazyContextPropagation )) { | |
| 18560 // If an update was already in progress, we should schedule an Update | |
| 18561 // effect even though we're bailing out, so that cWU/cDU are called. | |
| 18562 if (typeof instance.componentDidUpdate === 'function') { | |
| 18563 if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) { | |
| 18564 workInProgress.flags |= Update; | |
| 18565 } | |
| 18566 } | |
| 18567 | |
| 18568 if (typeof instance.getSnapshotBeforeUpdate === 'function') { | |
| 18569 if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) { | |
| 18570 workInProgress.flags |= Snapshot; | |
| 18571 } | |
| 18572 } | |
| 18573 | |
| 18574 return false; | |
| 18575 } | |
| 18576 | |
| 18577 if (typeof getDerivedStateFromProps === 'function') { | |
| 18578 applyDerivedStateFromProps(workInProgress, ctor, getDerivedStateFromProps, newProps); | |
| 18579 newState = workInProgress.memoizedState; | |
| 18580 } | |
| 18581 | |
| 18582 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, | |
| 18583 // both before and after `shouldComponentUpdate` has been called. Not ideal, | |
| 18584 // but I'm loath to refactor this function. This only happens for memoized | |
| 18585 // components so it's not that common. | |
| 18586 enableLazyContextPropagation ; | |
| 18587 | |
| 18588 if (shouldUpdate) { | |
| 18589 // In order to support react-lifecycles-compat polyfilled components, | |
| 18590 // Unsafe lifecycles should not be invoked for components using the new APIs. | |
| 18591 if (!hasNewLifecycles && (typeof instance.UNSAFE_componentWillUpdate === 'function' || typeof instance.componentWillUpdate === 'function')) { | |
| 18592 if (typeof instance.componentWillUpdate === 'function') { | |
| 18593 instance.componentWillUpdate(newProps, newState, nextContext); | |
| 18594 } | |
| 18595 | |
| 18596 if (typeof instance.UNSAFE_componentWillUpdate === 'function') { | |
| 18597 instance.UNSAFE_componentWillUpdate(newProps, newState, nextContext); | |
| 18598 } | |
| 18599 } | |
| 18600 | |
| 18601 if (typeof instance.componentDidUpdate === 'function') { | |
| 18602 workInProgress.flags |= Update; | |
| 18603 } | |
| 18604 | |
| 18605 if (typeof instance.getSnapshotBeforeUpdate === 'function') { | |
| 18606 workInProgress.flags |= Snapshot; | |
| 18607 } | |
| 18608 } else { | |
| 18609 // If an update was already in progress, we should schedule an Update | |
| 18610 // effect even though we're bailing out, so that cWU/cDU are called. | |
| 18611 if (typeof instance.componentDidUpdate === 'function') { | |
| 18612 if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) { | |
| 18613 workInProgress.flags |= Update; | |
| 18614 } | |
| 18615 } | |
| 18616 | |
| 18617 if (typeof instance.getSnapshotBeforeUpdate === 'function') { | |
| 18618 if (unresolvedOldProps !== current.memoizedProps || oldState !== current.memoizedState) { | |
| 18619 workInProgress.flags |= Snapshot; | |
| 18620 } | |
| 18621 } // If shouldComponentUpdate returned false, we should still update the | |
| 18622 // memoized props/state to indicate that this work can be reused. | |
| 18623 | |
| 18624 | |
| 18625 workInProgress.memoizedProps = newProps; | |
| 18626 workInProgress.memoizedState = newState; | |
| 18627 } // Update the existing instance's state, props, and context pointers even | |
| 18628 // if shouldComponentUpdate returns false. | |
| 18629 | |
| 18630 | |
| 18631 instance.props = newProps; | |
| 18632 instance.state = newState; | |
| 18633 instance.context = nextContext; | |
| 18634 return shouldUpdate; | |
| 18635 } | |
| 18636 | |
| 18637 function createCapturedValueAtFiber(value, source) { | |
| 18638 // If the value is an error, call this function immediately after it is thrown | |
| 18639 // so the stack is accurate. | |
| 18640 return { | |
| 18641 value: value, | |
| 18642 source: source, | |
| 18643 stack: getStackByFiberInDevAndProd(source), | |
| 18644 digest: null | |
| 18645 }; | |
| 18646 } | |
| 18647 function createCapturedValue(value, digest, stack) { | |
| 18648 return { | |
| 18649 value: value, | |
| 18650 source: null, | |
| 18651 stack: stack != null ? stack : null, | |
| 18652 digest: digest != null ? digest : null | |
| 18653 }; | |
| 18654 } | |
| 18655 | |
| 18656 // This module is forked in different environments. | |
| 18657 // By default, return `true` to log errors to the console. | |
| 18658 // Forks can return `false` if this isn't desirable. | |
| 18659 function showErrorDialog(boundary, errorInfo) { | |
| 18660 return true; | |
| 18661 } | |
| 18662 | |
| 18663 function logCapturedError(boundary, errorInfo) { | |
| 18664 try { | |
| 18665 var logError = showErrorDialog(boundary, errorInfo); // Allow injected showErrorDialog() to prevent default console.error logging. | |
| 18666 // This enables renderers like ReactNative to better manage redbox behavior. | |
| 18667 | |
| 18668 if (logError === false) { | |
| 18669 return; | |
| 18670 } | |
| 18671 | |
| 18672 var error = errorInfo.value; | |
| 18673 | |
| 18674 if (true) { | |
| 18675 var source = errorInfo.source; | |
| 18676 var stack = errorInfo.stack; | |
| 18677 var componentStack = stack !== null ? stack : ''; // Browsers support silencing uncaught errors by calling | |
| 18678 // `preventDefault()` in window `error` handler. | |
| 18679 // We record this information as an expando on the error. | |
| 18680 | |
| 18681 if (error != null && error._suppressLogging) { | |
| 18682 if (boundary.tag === ClassComponent) { | |
| 18683 // The error is recoverable and was silenced. | |
| 18684 // Ignore it and don't print the stack addendum. | |
| 18685 // This is handy for testing error boundaries without noise. | |
| 18686 return; | |
| 18687 } // The error is fatal. Since the silencing might have | |
| 18688 // been accidental, we'll surface it anyway. | |
| 18689 // However, the browser would have silenced the original error | |
| 18690 // so we'll print it first, and then print the stack addendum. | |
| 18691 | |
| 18692 | |
| 18693 console['error'](error); // Don't transform to our wrapper | |
| 18694 // For a more detailed description of this block, see: | |
| 18695 // https://github.com/facebook/react/pull/13384 | |
| 18696 } | |
| 18697 | |
| 18698 var componentName = source ? getComponentNameFromFiber(source) : null; | |
| 18699 var componentNameMessage = componentName ? "The above error occurred in the <" + componentName + "> component:" : 'The above error occurred in one of your React components:'; | |
| 18700 var errorBoundaryMessage; | |
| 18701 | |
| 18702 if (boundary.tag === HostRoot) { | |
| 18703 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.'; | |
| 18704 } else { | |
| 18705 var errorBoundaryName = getComponentNameFromFiber(boundary) || 'Anonymous'; | |
| 18706 errorBoundaryMessage = "React will try to recreate this component tree from scratch " + ("using the error boundary you provided, " + errorBoundaryName + "."); | |
| 18707 } | |
| 18708 | |
| 18709 var combinedMessage = componentNameMessage + "\n" + componentStack + "\n\n" + ("" + errorBoundaryMessage); // In development, we provide our own message with just the component stack. | |
| 18710 // We don't include the original error message and JS stack because the browser | |
| 18711 // has already printed it. Even if the application swallows the error, it is still | |
| 18712 // displayed by the browser thanks to the DEV-only fake event trick in ReactErrorUtils. | |
| 18713 | |
| 18714 console['error'](combinedMessage); // Don't transform to our wrapper | |
| 18715 } else { | |
| 18716 // In production, we print the error directly. | |
| 18717 // This will include the message, the JS stack, and anything the browser wants to show. | |
| 18718 // We pass the error object instead of custom message so that the browser displays the error natively. | |
| 18719 console['error'](error); // Don't transform to our wrapper | |
| 18720 } | |
| 18721 } catch (e) { | |
| 18722 // This method must not throw, or React internal state will get messed up. | |
| 18723 // If console.error is overridden, or logCapturedError() shows a dialog that throws, | |
| 18724 // we want to report this error outside of the normal stack as a last resort. | |
| 18725 // https://github.com/facebook/react/issues/13188 | |
| 18726 setTimeout(function () { | |
| 18727 throw e; | |
| 18728 }); | |
| 18729 } | |
| 18730 } | |
| 18731 | |
| 18732 var PossiblyWeakMap$1 = typeof WeakMap === 'function' ? WeakMap : Map; | |
| 18733 | |
| 18734 function createRootErrorUpdate(fiber, errorInfo, lane) { | |
| 18735 var update = createUpdate(NoTimestamp, lane); // Unmount the root by rendering null. | |
| 18736 | |
| 18737 update.tag = CaptureUpdate; // Caution: React DevTools currently depends on this property | |
| 18738 // being called "element". | |
| 18739 | |
| 18740 update.payload = { | |
| 18741 element: null | |
| 18742 }; | |
| 18743 var error = errorInfo.value; | |
| 18744 | |
| 18745 update.callback = function () { | |
| 18746 onUncaughtError(error); | |
| 18747 logCapturedError(fiber, errorInfo); | |
| 18748 }; | |
| 18749 | |
| 18750 return update; | |
| 18751 } | |
| 18752 | |
| 18753 function createClassErrorUpdate(fiber, errorInfo, lane) { | |
| 18754 var update = createUpdate(NoTimestamp, lane); | |
| 18755 update.tag = CaptureUpdate; | |
| 18756 var getDerivedStateFromError = fiber.type.getDerivedStateFromError; | |
| 18757 | |
| 18758 if (typeof getDerivedStateFromError === 'function') { | |
| 18759 var error$1 = errorInfo.value; | |
| 18760 | |
| 18761 update.payload = function () { | |
| 18762 return getDerivedStateFromError(error$1); | |
| 18763 }; | |
| 18764 | |
| 18765 update.callback = function () { | |
| 18766 { | |
| 18767 markFailedErrorBoundaryForHotReloading(fiber); | |
| 18768 } | |
| 18769 | |
| 18770 logCapturedError(fiber, errorInfo); | |
| 18771 }; | |
| 18772 } | |
| 18773 | |
| 18774 var inst = fiber.stateNode; | |
| 18775 | |
| 18776 if (inst !== null && typeof inst.componentDidCatch === 'function') { | |
| 18777 update.callback = function callback() { | |
| 18778 { | |
| 18779 markFailedErrorBoundaryForHotReloading(fiber); | |
| 18780 } | |
| 18781 | |
| 18782 logCapturedError(fiber, errorInfo); | |
| 18783 | |
| 18784 if (typeof getDerivedStateFromError !== 'function') { | |
| 18785 // To preserve the preexisting retry behavior of error boundaries, | |
| 18786 // we keep track of which ones already failed during this batch. | |
| 18787 // This gets reset before we yield back to the browser. | |
| 18788 // TODO: Warn in strict mode if getDerivedStateFromError is | |
| 18789 // not defined. | |
| 18790 markLegacyErrorBoundaryAsFailed(this); | |
| 18791 } | |
| 18792 | |
| 18793 var error$1 = errorInfo.value; | |
| 18794 var stack = errorInfo.stack; | |
| 18795 this.componentDidCatch(error$1, { | |
| 18796 componentStack: stack !== null ? stack : '' | |
| 18797 }); | |
| 18798 | |
| 18799 { | |
| 18800 if (typeof getDerivedStateFromError !== 'function') { | |
| 18801 // If componentDidCatch is the only error boundary method defined, | |
| 18802 // then it needs to call setState to recover from errors. | |
| 18803 // If no state update is scheduled then the boundary will swallow the error. | |
| 18804 if (!includesSomeLane(fiber.lanes, SyncLane)) { | |
| 18805 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'); | |
| 18806 } | |
| 18807 } | |
| 18808 } | |
| 18809 }; | |
| 18810 } | |
| 18811 | |
| 18812 return update; | |
| 18813 } | |
| 18814 | |
| 18815 function attachPingListener(root, wakeable, lanes) { | |
| 18816 // Attach a ping listener | |
| 18817 // | |
| 18818 // The data might resolve before we have a chance to commit the fallback. Or, | |
| 18819 // in the case of a refresh, we'll never commit a fallback. So we need to | |
| 18820 // attach a listener now. When it resolves ("pings"), we can decide whether to | |
| 18821 // try rendering the tree again. | |
| 18822 // | |
| 18823 // Only attach a listener if one does not already exist for the lanes | |
| 18824 // we're currently rendering (which acts like a "thread ID" here). | |
| 18825 // | |
| 18826 // We only need to do this in concurrent mode. Legacy Suspense always | |
| 18827 // commits fallbacks synchronously, so there are no pings. | |
| 18828 var pingCache = root.pingCache; | |
| 18829 var threadIDs; | |
| 18830 | |
| 18831 if (pingCache === null) { | |
| 18832 pingCache = root.pingCache = new PossiblyWeakMap$1(); | |
| 18833 threadIDs = new Set(); | |
| 18834 pingCache.set(wakeable, threadIDs); | |
| 18835 } else { | |
| 18836 threadIDs = pingCache.get(wakeable); | |
| 18837 | |
| 18838 if (threadIDs === undefined) { | |
| 18839 threadIDs = new Set(); | |
| 18840 pingCache.set(wakeable, threadIDs); | |
| 18841 } | |
| 18842 } | |
| 18843 | |
| 18844 if (!threadIDs.has(lanes)) { | |
| 18845 // Memoize using the thread ID to prevent redundant listeners. | |
| 18846 threadIDs.add(lanes); | |
| 18847 var ping = pingSuspendedRoot.bind(null, root, wakeable, lanes); | |
| 18848 | |
| 18849 { | |
| 18850 if (isDevToolsPresent) { | |
| 18851 // If we have pending work still, restore the original updaters | |
| 18852 restorePendingUpdaters(root, lanes); | |
| 18853 } | |
| 18854 } | |
| 18855 | |
| 18856 wakeable.then(ping, ping); | |
| 18857 } | |
| 18858 } | |
| 18859 | |
| 18860 function attachRetryListener(suspenseBoundary, root, wakeable, lanes) { | |
| 18861 // Retry listener | |
| 18862 // | |
| 18863 // If the fallback does commit, we need to attach a different type of | |
| 18864 // listener. This one schedules an update on the Suspense boundary to turn | |
| 18865 // the fallback state off. | |
| 18866 // | |
| 18867 // Stash the wakeable on the boundary fiber so we can access it in the | |
| 18868 // commit phase. | |
| 18869 // | |
| 18870 // When the wakeable resolves, we'll attempt to render the boundary | |
| 18871 // again ("retry"). | |
| 18872 var wakeables = suspenseBoundary.updateQueue; | |
| 18873 | |
| 18874 if (wakeables === null) { | |
| 18875 var updateQueue = new Set(); | |
| 18876 updateQueue.add(wakeable); | |
| 18877 suspenseBoundary.updateQueue = updateQueue; | |
| 18878 } else { | |
| 18879 wakeables.add(wakeable); | |
| 18880 } | |
| 18881 } | |
| 18882 | |
| 18883 function resetSuspendedComponent(sourceFiber, rootRenderLanes) { | |
| 18884 // A legacy mode Suspense quirk, only relevant to hook components. | |
| 18885 | |
| 18886 | |
| 18887 var tag = sourceFiber.tag; | |
| 18888 | |
| 18889 if ((sourceFiber.mode & ConcurrentMode) === NoMode && (tag === FunctionComponent || tag === ForwardRef || tag === SimpleMemoComponent)) { | |
| 18890 var currentSource = sourceFiber.alternate; | |
| 18891 | |
| 18892 if (currentSource) { | |
| 18893 sourceFiber.updateQueue = currentSource.updateQueue; | |
| 18894 sourceFiber.memoizedState = currentSource.memoizedState; | |
| 18895 sourceFiber.lanes = currentSource.lanes; | |
| 18896 } else { | |
| 18897 sourceFiber.updateQueue = null; | |
| 18898 sourceFiber.memoizedState = null; | |
| 18899 } | |
| 18900 } | |
| 18901 } | |
| 18902 | |
| 18903 function getNearestSuspenseBoundaryToCapture(returnFiber) { | |
| 18904 var node = returnFiber; | |
| 18905 | |
| 18906 do { | |
| 18907 if (node.tag === SuspenseComponent && shouldCaptureSuspense(node)) { | |
| 18908 return node; | |
| 18909 } // This boundary already captured during this render. Continue to the next | |
| 18910 // boundary. | |
| 18911 | |
| 18912 | |
| 18913 node = node.return; | |
| 18914 } while (node !== null); | |
| 18915 | |
| 18916 return null; | |
| 18917 } | |
| 18918 | |
| 18919 function markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes) { | |
| 18920 // This marks a Suspense boundary so that when we're unwinding the stack, | |
| 18921 // it captures the suspended "exception" and does a second (fallback) pass. | |
| 18922 if ((suspenseBoundary.mode & ConcurrentMode) === NoMode) { | |
| 18923 // Legacy Mode Suspense | |
| 18924 // | |
| 18925 // If the boundary is in legacy mode, we should *not* | |
| 18926 // suspend the commit. Pretend as if the suspended component rendered | |
| 18927 // null and keep rendering. When the Suspense boundary completes, | |
| 18928 // we'll do a second pass to render the fallback. | |
| 18929 if (suspenseBoundary === returnFiber) { | |
| 18930 // Special case where we suspended while reconciling the children of | |
| 18931 // a Suspense boundary's inner Offscreen wrapper fiber. This happens | |
| 18932 // when a React.lazy component is a direct child of a | |
| 18933 // Suspense boundary. | |
| 18934 // | |
| 18935 // Suspense boundaries are implemented as multiple fibers, but they | |
| 18936 // are a single conceptual unit. The legacy mode behavior where we | |
| 18937 // pretend the suspended fiber committed as `null` won't work, | |
| 18938 // because in this case the "suspended" fiber is the inner | |
| 18939 // Offscreen wrapper. | |
| 18940 // | |
| 18941 // Because the contents of the boundary haven't started rendering | |
| 18942 // yet (i.e. nothing in the tree has partially rendered) we can | |
| 18943 // switch to the regular, concurrent mode behavior: mark the | |
| 18944 // boundary with ShouldCapture and enter the unwind phase. | |
| 18945 suspenseBoundary.flags |= ShouldCapture; | |
| 18946 } else { | |
| 18947 suspenseBoundary.flags |= DidCapture; | |
| 18948 sourceFiber.flags |= ForceUpdateForLegacySuspense; // We're going to commit this fiber even though it didn't complete. | |
| 18949 // But we shouldn't call any lifecycle methods or callbacks. Remove | |
| 18950 // all lifecycle effect tags. | |
| 18951 | |
| 18952 sourceFiber.flags &= ~(LifecycleEffectMask | Incomplete); | |
| 18953 | |
| 18954 if (sourceFiber.tag === ClassComponent) { | |
| 18955 var currentSourceFiber = sourceFiber.alternate; | |
| 18956 | |
| 18957 if (currentSourceFiber === null) { | |
| 18958 // This is a new mount. Change the tag so it's not mistaken for a | |
| 18959 // completed class component. For example, we should not call | |
| 18960 // componentWillUnmount if it is deleted. | |
| 18961 sourceFiber.tag = IncompleteClassComponent; | |
| 18962 } else { | |
| 18963 // When we try rendering again, we should not reuse the current fiber, | |
| 18964 // since it's known to be in an inconsistent state. Use a force update to | |
| 18965 // prevent a bail out. | |
| 18966 var update = createUpdate(NoTimestamp, SyncLane); | |
| 18967 update.tag = ForceUpdate; | |
| 18968 enqueueUpdate(sourceFiber, update, SyncLane); | |
| 18969 } | |
| 18970 } // The source fiber did not complete. Mark it with Sync priority to | |
| 18971 // indicate that it still has pending work. | |
| 18972 | |
| 18973 | |
| 18974 sourceFiber.lanes = mergeLanes(sourceFiber.lanes, SyncLane); | |
| 18975 } | |
| 18976 | |
| 18977 return suspenseBoundary; | |
| 18978 } // Confirmed that the boundary is in a concurrent mode tree. Continue | |
| 18979 // with the normal suspend path. | |
| 18980 // | |
| 18981 // After this we'll use a set of heuristics to determine whether this | |
| 18982 // render pass will run to completion or restart or "suspend" the commit. | |
| 18983 // The actual logic for this is spread out in different places. | |
| 18984 // | |
| 18985 // This first principle is that if we're going to suspend when we complete | |
| 18986 // a root, then we should also restart if we get an update or ping that | |
| 18987 // might unsuspend it, and vice versa. The only reason to suspend is | |
| 18988 // because you think you might want to restart before committing. However, | |
| 18989 // it doesn't make sense to restart only while in the period we're suspended. | |
| 18990 // | |
| 18991 // Restarting too aggressively is also not good because it starves out any | |
| 18992 // intermediate loading state. So we use heuristics to determine when. | |
| 18993 // Suspense Heuristics | |
| 18994 // | |
| 18995 // If nothing threw a Promise or all the same fallbacks are already showing, | |
| 18996 // then don't suspend/restart. | |
| 18997 // | |
| 18998 // If this is an initial render of a new tree of Suspense boundaries and | |
| 18999 // those trigger a fallback, then don't suspend/restart. We want to ensure | |
| 19000 // that we can show the initial loading state as quickly as possible. | |
| 19001 // | |
| 19002 // If we hit a "Delayed" case, such as when we'd switch from content back into | |
| 19003 // a fallback, then we should always suspend/restart. Transitions apply | |
| 19004 // to this case. If none is defined, JND is used instead. | |
| 19005 // | |
| 19006 // If we're already showing a fallback and it gets "retried", allowing us to show | |
| 19007 // another level, but there's still an inner boundary that would show a fallback, | |
| 19008 // then we suspend/restart for 500ms since the last time we showed a fallback | |
| 19009 // anywhere in the tree. This effectively throttles progressive loading into a | |
| 19010 // consistent train of commits. This also gives us an opportunity to restart to | |
| 19011 // get to the completed state slightly earlier. | |
| 19012 // | |
| 19013 // If there's ambiguity due to batching it's resolved in preference of: | |
| 19014 // 1) "delayed", 2) "initial render", 3) "retry". | |
| 19015 // | |
| 19016 // We want to ensure that a "busy" state doesn't get force committed. We want to | |
| 19017 // ensure that new initial loading states can commit as soon as possible. | |
| 19018 | |
| 19019 | |
| 19020 suspenseBoundary.flags |= ShouldCapture; // TODO: I think we can remove this, since we now use `DidCapture` in | |
| 19021 // the begin phase to prevent an early bailout. | |
| 19022 | |
| 19023 suspenseBoundary.lanes = rootRenderLanes; | |
| 19024 return suspenseBoundary; | |
| 19025 } | |
| 19026 | |
| 19027 function throwException(root, returnFiber, sourceFiber, value, rootRenderLanes) { | |
| 19028 // The source fiber did not complete. | |
| 19029 sourceFiber.flags |= Incomplete; | |
| 19030 | |
| 19031 { | |
| 19032 if (isDevToolsPresent) { | |
| 19033 // If we have pending work still, restore the original updaters | |
| 19034 restorePendingUpdaters(root, rootRenderLanes); | |
| 19035 } | |
| 19036 } | |
| 19037 | |
| 19038 if (value !== null && typeof value === 'object' && typeof value.then === 'function') { | |
| 19039 // This is a wakeable. The component suspended. | |
| 19040 var wakeable = value; | |
| 19041 resetSuspendedComponent(sourceFiber); | |
| 19042 | |
| 19043 { | |
| 19044 if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) { | |
| 19045 markDidThrowWhileHydratingDEV(); | |
| 19046 } | |
| 19047 } | |
| 19048 | |
| 19049 | |
| 19050 var suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber); | |
| 19051 | |
| 19052 if (suspenseBoundary !== null) { | |
| 19053 suspenseBoundary.flags &= ~ForceClientRender; | |
| 19054 markSuspenseBoundaryShouldCapture(suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // We only attach ping listeners in concurrent mode. Legacy Suspense always | |
| 19055 // commits fallbacks synchronously, so there are no pings. | |
| 19056 | |
| 19057 if (suspenseBoundary.mode & ConcurrentMode) { | |
| 19058 attachPingListener(root, wakeable, rootRenderLanes); | |
| 19059 } | |
| 19060 | |
| 19061 attachRetryListener(suspenseBoundary, root, wakeable); | |
| 19062 return; | |
| 19063 } else { | |
| 19064 // No boundary was found. Unless this is a sync update, this is OK. | |
| 19065 // We can suspend and wait for more data to arrive. | |
| 19066 if (!includesSyncLane(rootRenderLanes)) { | |
| 19067 // This is not a sync update. Suspend. Since we're not activating a | |
| 19068 // Suspense boundary, this will unwind all the way to the root without | |
| 19069 // performing a second pass to render a fallback. (This is arguably how | |
| 19070 // refresh transitions should work, too, since we're not going to commit | |
| 19071 // the fallbacks anyway.) | |
| 19072 // | |
| 19073 // This case also applies to initial hydration. | |
| 19074 attachPingListener(root, wakeable, rootRenderLanes); | |
| 19075 renderDidSuspendDelayIfPossible(); | |
| 19076 return; | |
| 19077 } // This is a sync/discrete update. We treat this case like an error | |
| 19078 // because discrete renders are expected to produce a complete tree | |
| 19079 // synchronously to maintain consistency with external state. | |
| 19080 | |
| 19081 | |
| 19082 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. | |
| 19083 // The error will be caught by the nearest suspense boundary. | |
| 19084 | |
| 19085 value = uncaughtSuspenseError; | |
| 19086 } | |
| 19087 } else { | |
| 19088 // This is a regular error, not a Suspense wakeable. | |
| 19089 if (getIsHydrating() && sourceFiber.mode & ConcurrentMode) { | |
| 19090 markDidThrowWhileHydratingDEV(); | |
| 19091 | |
| 19092 var _suspenseBoundary = getNearestSuspenseBoundaryToCapture(returnFiber); // If the error was thrown during hydration, we may be able to recover by | |
| 19093 // discarding the dehydrated content and switching to a client render. | |
| 19094 // Instead of surfacing the error, find the nearest Suspense boundary | |
| 19095 // and render it again without hydration. | |
| 19096 | |
| 19097 | |
| 19098 if (_suspenseBoundary !== null) { | |
| 19099 if ((_suspenseBoundary.flags & ShouldCapture) === NoFlags) { | |
| 19100 // Set a flag to indicate that we should try rendering the normal | |
| 19101 // children again, not the fallback. | |
| 19102 _suspenseBoundary.flags |= ForceClientRender; | |
| 19103 } | |
| 19104 | |
| 19105 markSuspenseBoundaryShouldCapture(_suspenseBoundary, returnFiber, sourceFiber, root, rootRenderLanes); // Even though the user may not be affected by this error, we should | |
| 19106 // still log it so it can be fixed. | |
| 19107 | |
| 19108 queueHydrationError(createCapturedValueAtFiber(value, sourceFiber)); | |
| 19109 return; | |
| 19110 } | |
| 19111 } | |
| 19112 } | |
| 19113 | |
| 19114 value = createCapturedValueAtFiber(value, sourceFiber); | |
| 19115 renderDidError(value); // We didn't find a boundary that could handle this type of exception. Start | |
| 19116 // over and traverse parent path again, this time treating the exception | |
| 19117 // as an error. | |
| 19118 | |
| 19119 var workInProgress = returnFiber; | |
| 19120 | |
| 19121 do { | |
| 19122 switch (workInProgress.tag) { | |
| 19123 case HostRoot: | |
| 19124 { | |
| 19125 var _errorInfo = value; | |
| 19126 workInProgress.flags |= ShouldCapture; | |
| 19127 var lane = pickArbitraryLane(rootRenderLanes); | |
| 19128 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); | |
| 19129 var update = createRootErrorUpdate(workInProgress, _errorInfo, lane); | |
| 19130 enqueueCapturedUpdate(workInProgress, update); | |
| 19131 return; | |
| 19132 } | |
| 19133 | |
| 19134 case ClassComponent: | |
| 19135 // Capture and retry | |
| 19136 var errorInfo = value; | |
| 19137 var ctor = workInProgress.type; | |
| 19138 var instance = workInProgress.stateNode; | |
| 19139 | |
| 19140 if ((workInProgress.flags & DidCapture) === NoFlags && (typeof ctor.getDerivedStateFromError === 'function' || instance !== null && typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance))) { | |
| 19141 workInProgress.flags |= ShouldCapture; | |
| 19142 | |
| 19143 var _lane = pickArbitraryLane(rootRenderLanes); | |
| 19144 | |
| 19145 workInProgress.lanes = mergeLanes(workInProgress.lanes, _lane); // Schedule the error boundary to re-render using updated state | |
| 19146 | |
| 19147 var _update = createClassErrorUpdate(workInProgress, errorInfo, _lane); | |
| 19148 | |
| 19149 enqueueCapturedUpdate(workInProgress, _update); | |
| 19150 return; | |
| 19151 } | |
| 19152 | |
| 19153 break; | |
| 19154 } | |
| 19155 | |
| 19156 workInProgress = workInProgress.return; | |
| 19157 } while (workInProgress !== null); | |
| 19158 } | |
| 19159 | |
| 19160 function getSuspendedCache() { | |
| 19161 { | |
| 19162 return null; | |
| 19163 } // This function is called when a Suspense boundary suspends. It returns the | |
| 19164 } | |
| 19165 | |
| 19166 var ReactCurrentOwner$1 = ReactSharedInternals.ReactCurrentOwner; | |
| 19167 var didReceiveUpdate = false; | |
| 19168 var didWarnAboutBadClass; | |
| 19169 var didWarnAboutModulePatternComponent; | |
| 19170 var didWarnAboutContextTypeOnFunctionComponent; | |
| 19171 var didWarnAboutGetDerivedStateOnFunctionComponent; | |
| 19172 var didWarnAboutFunctionRefs; | |
| 19173 var didWarnAboutReassigningProps; | |
| 19174 var didWarnAboutRevealOrder; | |
| 19175 var didWarnAboutTailOptions; | |
| 19176 var didWarnAboutDefaultPropsOnFunctionComponent; | |
| 19177 | |
| 19178 { | |
| 19179 didWarnAboutBadClass = {}; | |
| 19180 didWarnAboutModulePatternComponent = {}; | |
| 19181 didWarnAboutContextTypeOnFunctionComponent = {}; | |
| 19182 didWarnAboutGetDerivedStateOnFunctionComponent = {}; | |
| 19183 didWarnAboutFunctionRefs = {}; | |
| 19184 didWarnAboutReassigningProps = false; | |
| 19185 didWarnAboutRevealOrder = {}; | |
| 19186 didWarnAboutTailOptions = {}; | |
| 19187 didWarnAboutDefaultPropsOnFunctionComponent = {}; | |
| 19188 } | |
| 19189 | |
| 19190 function reconcileChildren(current, workInProgress, nextChildren, renderLanes) { | |
| 19191 if (current === null) { | |
| 19192 // If this is a fresh new component that hasn't been rendered yet, we | |
| 19193 // won't update its child set by applying minimal side-effects. Instead, | |
| 19194 // we will add them all to the child before it gets rendered. That means | |
| 19195 // we can optimize this reconciliation pass by not tracking side-effects. | |
| 19196 workInProgress.child = mountChildFibers(workInProgress, null, nextChildren, renderLanes); | |
| 19197 } else { | |
| 19198 // If the current child is the same as the work in progress, it means that | |
| 19199 // we haven't yet started any work on these children. Therefore, we use | |
| 19200 // the clone algorithm to create a copy of all the current children. | |
| 19201 // If we had any progressed work already, that is invalid at this point so | |
| 19202 // let's throw it out. | |
| 19203 workInProgress.child = reconcileChildFibers(workInProgress, current.child, nextChildren, renderLanes); | |
| 19204 } | |
| 19205 } | |
| 19206 | |
| 19207 function forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes) { | |
| 19208 // This function is fork of reconcileChildren. It's used in cases where we | |
| 19209 // want to reconcile without matching against the existing set. This has the | |
| 19210 // effect of all current children being unmounted; even if the type and key | |
| 19211 // are the same, the old child is unmounted and a new child is created. | |
| 19212 // | |
| 19213 // To do this, we're going to go through the reconcile algorithm twice. In | |
| 19214 // the first pass, we schedule a deletion for all the current children by | |
| 19215 // passing null. | |
| 19216 workInProgress.child = reconcileChildFibers(workInProgress, current.child, null, renderLanes); // In the second pass, we mount the new children. The trick here is that we | |
| 19217 // pass null in place of where we usually pass the current child set. This has | |
| 19218 // the effect of remounting all children regardless of whether their | |
| 19219 // identities match. | |
| 19220 | |
| 19221 workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes); | |
| 19222 } | |
| 19223 | |
| 19224 function updateForwardRef(current, workInProgress, Component, nextProps, renderLanes) { | |
| 19225 // TODO: current can be non-null here even if the component | |
| 19226 // hasn't yet mounted. This happens after the first render suspends. | |
| 19227 // We'll need to figure out if this is fine or can cause issues. | |
| 19228 { | |
| 19229 if (workInProgress.type !== workInProgress.elementType) { | |
| 19230 // Lazy component props can't be validated in createElement | |
| 19231 // because they're only guaranteed to be resolved here. | |
| 19232 var innerPropTypes = Component.propTypes; | |
| 19233 | |
| 19234 if (innerPropTypes) { | |
| 19235 checkPropTypes(innerPropTypes, nextProps, // Resolved props | |
| 19236 'prop', getComponentNameFromType(Component)); | |
| 19237 } | |
| 19238 } | |
| 19239 } | |
| 19240 | |
| 19241 var render = Component.render; | |
| 19242 var ref = workInProgress.ref; // The rest is a fork of updateFunctionComponent | |
| 19243 | |
| 19244 var nextChildren; | |
| 19245 var hasId; | |
| 19246 prepareToReadContext(workInProgress, renderLanes); | |
| 19247 | |
| 19248 { | |
| 19249 markComponentRenderStarted(workInProgress); | |
| 19250 } | |
| 19251 | |
| 19252 { | |
| 19253 ReactCurrentOwner$1.current = workInProgress; | |
| 19254 setIsRendering(true); | |
| 19255 nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes); | |
| 19256 hasId = checkDidRenderIdHook(); | |
| 19257 | |
| 19258 if ( workInProgress.mode & StrictLegacyMode) { | |
| 19259 setIsStrictModeForDevtools(true); | |
| 19260 | |
| 19261 try { | |
| 19262 nextChildren = renderWithHooks(current, workInProgress, render, nextProps, ref, renderLanes); | |
| 19263 hasId = checkDidRenderIdHook(); | |
| 19264 } finally { | |
| 19265 setIsStrictModeForDevtools(false); | |
| 19266 } | |
| 19267 } | |
| 19268 | |
| 19269 setIsRendering(false); | |
| 19270 } | |
| 19271 | |
| 19272 { | |
| 19273 markComponentRenderStopped(); | |
| 19274 } | |
| 19275 | |
| 19276 if (current !== null && !didReceiveUpdate) { | |
| 19277 bailoutHooks(current, workInProgress, renderLanes); | |
| 19278 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19279 } | |
| 19280 | |
| 19281 if (getIsHydrating() && hasId) { | |
| 19282 pushMaterializedTreeId(workInProgress); | |
| 19283 } // React DevTools reads this flag. | |
| 19284 | |
| 19285 | |
| 19286 workInProgress.flags |= PerformedWork; | |
| 19287 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19288 return workInProgress.child; | |
| 19289 } | |
| 19290 | |
| 19291 function updateMemoComponent(current, workInProgress, Component, nextProps, renderLanes) { | |
| 19292 if (current === null) { | |
| 19293 var type = Component.type; | |
| 19294 | |
| 19295 if (isSimpleFunctionComponent(type) && Component.compare === null && // SimpleMemoComponent codepath doesn't resolve outer props either. | |
| 19296 Component.defaultProps === undefined) { | |
| 19297 var resolvedType = type; | |
| 19298 | |
| 19299 { | |
| 19300 resolvedType = resolveFunctionForHotReloading(type); | |
| 19301 } // If this is a plain function component without default props, | |
| 19302 // and with only the default shallow comparison, we upgrade it | |
| 19303 // to a SimpleMemoComponent to allow fast path updates. | |
| 19304 | |
| 19305 | |
| 19306 workInProgress.tag = SimpleMemoComponent; | |
| 19307 workInProgress.type = resolvedType; | |
| 19308 | |
| 19309 { | |
| 19310 validateFunctionComponentInDev(workInProgress, type); | |
| 19311 } | |
| 19312 | |
| 19313 return updateSimpleMemoComponent(current, workInProgress, resolvedType, nextProps, renderLanes); | |
| 19314 } | |
| 19315 | |
| 19316 { | |
| 19317 var innerPropTypes = type.propTypes; | |
| 19318 | |
| 19319 if (innerPropTypes) { | |
| 19320 // Inner memo component props aren't currently validated in createElement. | |
| 19321 // We could move it there, but we'd still need this for lazy code path. | |
| 19322 checkPropTypes(innerPropTypes, nextProps, // Resolved props | |
| 19323 'prop', getComponentNameFromType(type)); | |
| 19324 } | |
| 19325 | |
| 19326 if ( Component.defaultProps !== undefined) { | |
| 19327 var componentName = getComponentNameFromType(type) || 'Unknown'; | |
| 19328 | |
| 19329 if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { | |
| 19330 error('%s: Support for defaultProps will be removed from memo components ' + 'in a future major release. Use JavaScript default parameters instead.', componentName); | |
| 19331 | |
| 19332 didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; | |
| 19333 } | |
| 19334 } | |
| 19335 } | |
| 19336 | |
| 19337 var child = createFiberFromTypeAndProps(Component.type, null, nextProps, workInProgress, workInProgress.mode, renderLanes); | |
| 19338 child.ref = workInProgress.ref; | |
| 19339 child.return = workInProgress; | |
| 19340 workInProgress.child = child; | |
| 19341 return child; | |
| 19342 } | |
| 19343 | |
| 19344 { | |
| 19345 var _type = Component.type; | |
| 19346 var _innerPropTypes = _type.propTypes; | |
| 19347 | |
| 19348 if (_innerPropTypes) { | |
| 19349 // Inner memo component props aren't currently validated in createElement. | |
| 19350 // We could move it there, but we'd still need this for lazy code path. | |
| 19351 checkPropTypes(_innerPropTypes, nextProps, // Resolved props | |
| 19352 'prop', getComponentNameFromType(_type)); | |
| 19353 } | |
| 19354 } | |
| 19355 | |
| 19356 var currentChild = current.child; // This is always exactly one child | |
| 19357 | |
| 19358 var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes); | |
| 19359 | |
| 19360 if (!hasScheduledUpdateOrContext) { | |
| 19361 // This will be the props with resolved defaultProps, | |
| 19362 // unlike current.memoizedProps which will be the unresolved ones. | |
| 19363 var prevProps = currentChild.memoizedProps; // Default to shallow comparison | |
| 19364 | |
| 19365 var compare = Component.compare; | |
| 19366 compare = compare !== null ? compare : shallowEqual; | |
| 19367 | |
| 19368 if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) { | |
| 19369 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19370 } | |
| 19371 } // React DevTools reads this flag. | |
| 19372 | |
| 19373 | |
| 19374 workInProgress.flags |= PerformedWork; | |
| 19375 var newChild = createWorkInProgress(currentChild, nextProps); | |
| 19376 newChild.ref = workInProgress.ref; | |
| 19377 newChild.return = workInProgress; | |
| 19378 workInProgress.child = newChild; | |
| 19379 return newChild; | |
| 19380 } | |
| 19381 | |
| 19382 function updateSimpleMemoComponent(current, workInProgress, Component, nextProps, renderLanes) { | |
| 19383 // TODO: current can be non-null here even if the component | |
| 19384 // hasn't yet mounted. This happens when the inner render suspends. | |
| 19385 // We'll need to figure out if this is fine or can cause issues. | |
| 19386 { | |
| 19387 if (workInProgress.type !== workInProgress.elementType) { | |
| 19388 // Lazy component props can't be validated in createElement | |
| 19389 // because they're only guaranteed to be resolved here. | |
| 19390 var outerMemoType = workInProgress.elementType; | |
| 19391 | |
| 19392 if (outerMemoType.$$typeof === REACT_LAZY_TYPE) { | |
| 19393 // We warn when you define propTypes on lazy() | |
| 19394 // so let's just skip over it to find memo() outer wrapper. | |
| 19395 // Inner props for memo are validated later. | |
| 19396 var lazyComponent = outerMemoType; | |
| 19397 var payload = lazyComponent._payload; | |
| 19398 var init = lazyComponent._init; | |
| 19399 | |
| 19400 try { | |
| 19401 outerMemoType = init(payload); | |
| 19402 } catch (x) { | |
| 19403 outerMemoType = null; | |
| 19404 } // Inner propTypes will be validated in the function component path. | |
| 19405 | |
| 19406 | |
| 19407 var outerPropTypes = outerMemoType && outerMemoType.propTypes; | |
| 19408 | |
| 19409 if (outerPropTypes) { | |
| 19410 checkPropTypes(outerPropTypes, nextProps, // Resolved (SimpleMemoComponent has no defaultProps) | |
| 19411 'prop', getComponentNameFromType(outerMemoType)); | |
| 19412 } | |
| 19413 } | |
| 19414 } | |
| 19415 } | |
| 19416 | |
| 19417 if (current !== null) { | |
| 19418 var prevProps = current.memoizedProps; | |
| 19419 | |
| 19420 if (shallowEqual(prevProps, nextProps) && current.ref === workInProgress.ref && ( // Prevent bailout if the implementation changed due to hot reload. | |
| 19421 workInProgress.type === current.type )) { | |
| 19422 didReceiveUpdate = false; // The props are shallowly equal. Reuse the previous props object, like we | |
| 19423 // would during a normal fiber bailout. | |
| 19424 // | |
| 19425 // We don't have strong guarantees that the props object is referentially | |
| 19426 // equal during updates where we can't bail out anyway — like if the props | |
| 19427 // are shallowly equal, but there's a local state or context update in the | |
| 19428 // same batch. | |
| 19429 // | |
| 19430 // However, as a principle, we should aim to make the behavior consistent | |
| 19431 // across different ways of memoizing a component. For example, React.memo | |
| 19432 // has a different internal Fiber layout if you pass a normal function | |
| 19433 // component (SimpleMemoComponent) versus if you pass a different type | |
| 19434 // like forwardRef (MemoComponent). But this is an implementation detail. | |
| 19435 // Wrapping a component in forwardRef (or React.lazy, etc) shouldn't | |
| 19436 // affect whether the props object is reused during a bailout. | |
| 19437 | |
| 19438 workInProgress.pendingProps = nextProps = prevProps; | |
| 19439 | |
| 19440 if (!checkScheduledUpdateOrContext(current, renderLanes)) { | |
| 19441 // The pending lanes were cleared at the beginning of beginWork. We're | |
| 19442 // about to bail out, but there might be other lanes that weren't | |
| 19443 // included in the current render. Usually, the priority level of the | |
| 19444 // remaining updates is accumulated during the evaluation of the | |
| 19445 // component (i.e. when processing the update queue). But since since | |
| 19446 // we're bailing out early *without* evaluating the component, we need | |
| 19447 // to account for it here, too. Reset to the value of the current fiber. | |
| 19448 // NOTE: This only applies to SimpleMemoComponent, not MemoComponent, | |
| 19449 // because a MemoComponent fiber does not have hooks or an update queue; | |
| 19450 // rather, it wraps around an inner component, which may or may not | |
| 19451 // contains hooks. | |
| 19452 // TODO: Move the reset at in beginWork out of the common path so that | |
| 19453 // this is no longer necessary. | |
| 19454 workInProgress.lanes = current.lanes; | |
| 19455 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19456 } else if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { | |
| 19457 // This is a special case that only exists for legacy mode. | |
| 19458 // See https://github.com/facebook/react/pull/19216. | |
| 19459 didReceiveUpdate = true; | |
| 19460 } | |
| 19461 } | |
| 19462 } | |
| 19463 | |
| 19464 return updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes); | |
| 19465 } | |
| 19466 | |
| 19467 function updateOffscreenComponent(current, workInProgress, renderLanes) { | |
| 19468 var nextProps = workInProgress.pendingProps; | |
| 19469 var nextChildren = nextProps.children; | |
| 19470 var prevState = current !== null ? current.memoizedState : null; | |
| 19471 | |
| 19472 if (nextProps.mode === 'hidden' || enableLegacyHidden ) { | |
| 19473 // Rendering a hidden tree. | |
| 19474 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 19475 // In legacy sync mode, don't defer the subtree. Render it now. | |
| 19476 // TODO: Consider how Offscreen should work with transitions in the future | |
| 19477 var nextState = { | |
| 19478 baseLanes: NoLanes, | |
| 19479 cachePool: null, | |
| 19480 transitions: null | |
| 19481 }; | |
| 19482 workInProgress.memoizedState = nextState; | |
| 19483 | |
| 19484 pushRenderLanes(workInProgress, renderLanes); | |
| 19485 } else if (!includesSomeLane(renderLanes, OffscreenLane)) { | |
| 19486 var spawnedCachePool = null; // We're hidden, and we're not rendering at Offscreen. We will bail out | |
| 19487 // and resume this tree later. | |
| 19488 | |
| 19489 var nextBaseLanes; | |
| 19490 | |
| 19491 if (prevState !== null) { | |
| 19492 var prevBaseLanes = prevState.baseLanes; | |
| 19493 nextBaseLanes = mergeLanes(prevBaseLanes, renderLanes); | |
| 19494 } else { | |
| 19495 nextBaseLanes = renderLanes; | |
| 19496 } // Schedule this fiber to re-render at offscreen priority. Then bailout. | |
| 19497 | |
| 19498 | |
| 19499 workInProgress.lanes = workInProgress.childLanes = laneToLanes(OffscreenLane); | |
| 19500 var _nextState = { | |
| 19501 baseLanes: nextBaseLanes, | |
| 19502 cachePool: spawnedCachePool, | |
| 19503 transitions: null | |
| 19504 }; | |
| 19505 workInProgress.memoizedState = _nextState; | |
| 19506 workInProgress.updateQueue = null; | |
| 19507 // to avoid a push/pop misalignment. | |
| 19508 | |
| 19509 | |
| 19510 pushRenderLanes(workInProgress, nextBaseLanes); | |
| 19511 | |
| 19512 return null; | |
| 19513 } else { | |
| 19514 // This is the second render. The surrounding visible content has already | |
| 19515 // committed. Now we resume rendering the hidden tree. | |
| 19516 // Rendering at offscreen, so we can clear the base lanes. | |
| 19517 var _nextState2 = { | |
| 19518 baseLanes: NoLanes, | |
| 19519 cachePool: null, | |
| 19520 transitions: null | |
| 19521 }; | |
| 19522 workInProgress.memoizedState = _nextState2; // Push the lanes that were skipped when we bailed out. | |
| 19523 | |
| 19524 var subtreeRenderLanes = prevState !== null ? prevState.baseLanes : renderLanes; | |
| 19525 | |
| 19526 pushRenderLanes(workInProgress, subtreeRenderLanes); | |
| 19527 } | |
| 19528 } else { | |
| 19529 // Rendering a visible tree. | |
| 19530 var _subtreeRenderLanes; | |
| 19531 | |
| 19532 if (prevState !== null) { | |
| 19533 // We're going from hidden -> visible. | |
| 19534 _subtreeRenderLanes = mergeLanes(prevState.baseLanes, renderLanes); | |
| 19535 | |
| 19536 workInProgress.memoizedState = null; | |
| 19537 } else { | |
| 19538 // We weren't previously hidden, and we still aren't, so there's nothing | |
| 19539 // special to do. Need to push to the stack regardless, though, to avoid | |
| 19540 // a push/pop misalignment. | |
| 19541 _subtreeRenderLanes = renderLanes; | |
| 19542 } | |
| 19543 | |
| 19544 pushRenderLanes(workInProgress, _subtreeRenderLanes); | |
| 19545 } | |
| 19546 | |
| 19547 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19548 return workInProgress.child; | |
| 19549 } // Note: These happen to have identical begin phases, for now. We shouldn't hold | |
| 19550 | |
| 19551 function updateFragment(current, workInProgress, renderLanes) { | |
| 19552 var nextChildren = workInProgress.pendingProps; | |
| 19553 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19554 return workInProgress.child; | |
| 19555 } | |
| 19556 | |
| 19557 function updateMode(current, workInProgress, renderLanes) { | |
| 19558 var nextChildren = workInProgress.pendingProps.children; | |
| 19559 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19560 return workInProgress.child; | |
| 19561 } | |
| 19562 | |
| 19563 function updateProfiler(current, workInProgress, renderLanes) { | |
| 19564 { | |
| 19565 workInProgress.flags |= Update; | |
| 19566 | |
| 19567 { | |
| 19568 // Reset effect durations for the next eventual effect phase. | |
| 19569 // These are reset during render to allow the DevTools commit hook a chance to read them, | |
| 19570 var stateNode = workInProgress.stateNode; | |
| 19571 stateNode.effectDuration = 0; | |
| 19572 stateNode.passiveEffectDuration = 0; | |
| 19573 } | |
| 19574 } | |
| 19575 | |
| 19576 var nextProps = workInProgress.pendingProps; | |
| 19577 var nextChildren = nextProps.children; | |
| 19578 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19579 return workInProgress.child; | |
| 19580 } | |
| 19581 | |
| 19582 function markRef(current, workInProgress) { | |
| 19583 var ref = workInProgress.ref; | |
| 19584 | |
| 19585 if (current === null && ref !== null || current !== null && current.ref !== ref) { | |
| 19586 // Schedule a Ref effect | |
| 19587 workInProgress.flags |= Ref; | |
| 19588 | |
| 19589 { | |
| 19590 workInProgress.flags |= RefStatic; | |
| 19591 } | |
| 19592 } | |
| 19593 } | |
| 19594 | |
| 19595 function updateFunctionComponent(current, workInProgress, Component, nextProps, renderLanes) { | |
| 19596 { | |
| 19597 if (workInProgress.type !== workInProgress.elementType) { | |
| 19598 // Lazy component props can't be validated in createElement | |
| 19599 // because they're only guaranteed to be resolved here. | |
| 19600 var innerPropTypes = Component.propTypes; | |
| 19601 | |
| 19602 if (innerPropTypes) { | |
| 19603 checkPropTypes(innerPropTypes, nextProps, // Resolved props | |
| 19604 'prop', getComponentNameFromType(Component)); | |
| 19605 } | |
| 19606 } | |
| 19607 } | |
| 19608 | |
| 19609 var context; | |
| 19610 | |
| 19611 { | |
| 19612 var unmaskedContext = getUnmaskedContext(workInProgress, Component, true); | |
| 19613 context = getMaskedContext(workInProgress, unmaskedContext); | |
| 19614 } | |
| 19615 | |
| 19616 var nextChildren; | |
| 19617 var hasId; | |
| 19618 prepareToReadContext(workInProgress, renderLanes); | |
| 19619 | |
| 19620 { | |
| 19621 markComponentRenderStarted(workInProgress); | |
| 19622 } | |
| 19623 | |
| 19624 { | |
| 19625 ReactCurrentOwner$1.current = workInProgress; | |
| 19626 setIsRendering(true); | |
| 19627 nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes); | |
| 19628 hasId = checkDidRenderIdHook(); | |
| 19629 | |
| 19630 if ( workInProgress.mode & StrictLegacyMode) { | |
| 19631 setIsStrictModeForDevtools(true); | |
| 19632 | |
| 19633 try { | |
| 19634 nextChildren = renderWithHooks(current, workInProgress, Component, nextProps, context, renderLanes); | |
| 19635 hasId = checkDidRenderIdHook(); | |
| 19636 } finally { | |
| 19637 setIsStrictModeForDevtools(false); | |
| 19638 } | |
| 19639 } | |
| 19640 | |
| 19641 setIsRendering(false); | |
| 19642 } | |
| 19643 | |
| 19644 { | |
| 19645 markComponentRenderStopped(); | |
| 19646 } | |
| 19647 | |
| 19648 if (current !== null && !didReceiveUpdate) { | |
| 19649 bailoutHooks(current, workInProgress, renderLanes); | |
| 19650 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19651 } | |
| 19652 | |
| 19653 if (getIsHydrating() && hasId) { | |
| 19654 pushMaterializedTreeId(workInProgress); | |
| 19655 } // React DevTools reads this flag. | |
| 19656 | |
| 19657 | |
| 19658 workInProgress.flags |= PerformedWork; | |
| 19659 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19660 return workInProgress.child; | |
| 19661 } | |
| 19662 | |
| 19663 function updateClassComponent(current, workInProgress, Component, nextProps, renderLanes) { | |
| 19664 { | |
| 19665 // This is used by DevTools to force a boundary to error. | |
| 19666 switch (shouldError(workInProgress)) { | |
| 19667 case false: | |
| 19668 { | |
| 19669 var _instance = workInProgress.stateNode; | |
| 19670 var ctor = workInProgress.type; // TODO This way of resetting the error boundary state is a hack. | |
| 19671 // Is there a better way to do this? | |
| 19672 | |
| 19673 var tempInstance = new ctor(workInProgress.memoizedProps, _instance.context); | |
| 19674 var state = tempInstance.state; | |
| 19675 | |
| 19676 _instance.updater.enqueueSetState(_instance, state, null); | |
| 19677 | |
| 19678 break; | |
| 19679 } | |
| 19680 | |
| 19681 case true: | |
| 19682 { | |
| 19683 workInProgress.flags |= DidCapture; | |
| 19684 workInProgress.flags |= ShouldCapture; // eslint-disable-next-line react-internal/prod-error-codes | |
| 19685 | |
| 19686 var error$1 = new Error('Simulated error coming from DevTools'); | |
| 19687 var lane = pickArbitraryLane(renderLanes); | |
| 19688 workInProgress.lanes = mergeLanes(workInProgress.lanes, lane); // Schedule the error boundary to re-render using updated state | |
| 19689 | |
| 19690 var update = createClassErrorUpdate(workInProgress, createCapturedValueAtFiber(error$1, workInProgress), lane); | |
| 19691 enqueueCapturedUpdate(workInProgress, update); | |
| 19692 break; | |
| 19693 } | |
| 19694 } | |
| 19695 | |
| 19696 if (workInProgress.type !== workInProgress.elementType) { | |
| 19697 // Lazy component props can't be validated in createElement | |
| 19698 // because they're only guaranteed to be resolved here. | |
| 19699 var innerPropTypes = Component.propTypes; | |
| 19700 | |
| 19701 if (innerPropTypes) { | |
| 19702 checkPropTypes(innerPropTypes, nextProps, // Resolved props | |
| 19703 'prop', getComponentNameFromType(Component)); | |
| 19704 } | |
| 19705 } | |
| 19706 } // Push context providers early to prevent context stack mismatches. | |
| 19707 // During mounting we don't know the child context yet as the instance doesn't exist. | |
| 19708 // We will invalidate the child context in finishClassComponent() right after rendering. | |
| 19709 | |
| 19710 | |
| 19711 var hasContext; | |
| 19712 | |
| 19713 if (isContextProvider(Component)) { | |
| 19714 hasContext = true; | |
| 19715 pushContextProvider(workInProgress); | |
| 19716 } else { | |
| 19717 hasContext = false; | |
| 19718 } | |
| 19719 | |
| 19720 prepareToReadContext(workInProgress, renderLanes); | |
| 19721 var instance = workInProgress.stateNode; | |
| 19722 var shouldUpdate; | |
| 19723 | |
| 19724 if (instance === null) { | |
| 19725 resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress); // In the initial pass we might need to construct the instance. | |
| 19726 | |
| 19727 constructClassInstance(workInProgress, Component, nextProps); | |
| 19728 mountClassInstance(workInProgress, Component, nextProps, renderLanes); | |
| 19729 shouldUpdate = true; | |
| 19730 } else if (current === null) { | |
| 19731 // In a resume, we'll already have an instance we can reuse. | |
| 19732 shouldUpdate = resumeMountClassInstance(workInProgress, Component, nextProps, renderLanes); | |
| 19733 } else { | |
| 19734 shouldUpdate = updateClassInstance(current, workInProgress, Component, nextProps, renderLanes); | |
| 19735 } | |
| 19736 | |
| 19737 var nextUnitOfWork = finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes); | |
| 19738 | |
| 19739 { | |
| 19740 var inst = workInProgress.stateNode; | |
| 19741 | |
| 19742 if (shouldUpdate && inst.props !== nextProps) { | |
| 19743 if (!didWarnAboutReassigningProps) { | |
| 19744 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'); | |
| 19745 } | |
| 19746 | |
| 19747 didWarnAboutReassigningProps = true; | |
| 19748 } | |
| 19749 } | |
| 19750 | |
| 19751 return nextUnitOfWork; | |
| 19752 } | |
| 19753 | |
| 19754 function finishClassComponent(current, workInProgress, Component, shouldUpdate, hasContext, renderLanes) { | |
| 19755 // Refs should update even if shouldComponentUpdate returns false | |
| 19756 markRef(current, workInProgress); | |
| 19757 var didCaptureError = (workInProgress.flags & DidCapture) !== NoFlags; | |
| 19758 | |
| 19759 if (!shouldUpdate && !didCaptureError) { | |
| 19760 // Context providers should defer to sCU for rendering | |
| 19761 if (hasContext) { | |
| 19762 invalidateContextProvider(workInProgress, Component, false); | |
| 19763 } | |
| 19764 | |
| 19765 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19766 } | |
| 19767 | |
| 19768 var instance = workInProgress.stateNode; // Rerender | |
| 19769 | |
| 19770 ReactCurrentOwner$1.current = workInProgress; | |
| 19771 var nextChildren; | |
| 19772 | |
| 19773 if (didCaptureError && typeof Component.getDerivedStateFromError !== 'function') { | |
| 19774 // If we captured an error, but getDerivedStateFromError is not defined, | |
| 19775 // unmount all the children. componentDidCatch will schedule an update to | |
| 19776 // re-render a fallback. This is temporary until we migrate everyone to | |
| 19777 // the new API. | |
| 19778 // TODO: Warn in a future release. | |
| 19779 nextChildren = null; | |
| 19780 | |
| 19781 { | |
| 19782 stopProfilerTimerIfRunning(); | |
| 19783 } | |
| 19784 } else { | |
| 19785 { | |
| 19786 markComponentRenderStarted(workInProgress); | |
| 19787 } | |
| 19788 | |
| 19789 { | |
| 19790 setIsRendering(true); | |
| 19791 nextChildren = instance.render(); | |
| 19792 | |
| 19793 if ( workInProgress.mode & StrictLegacyMode) { | |
| 19794 setIsStrictModeForDevtools(true); | |
| 19795 | |
| 19796 try { | |
| 19797 instance.render(); | |
| 19798 } finally { | |
| 19799 setIsStrictModeForDevtools(false); | |
| 19800 } | |
| 19801 } | |
| 19802 | |
| 19803 setIsRendering(false); | |
| 19804 } | |
| 19805 | |
| 19806 { | |
| 19807 markComponentRenderStopped(); | |
| 19808 } | |
| 19809 } // React DevTools reads this flag. | |
| 19810 | |
| 19811 | |
| 19812 workInProgress.flags |= PerformedWork; | |
| 19813 | |
| 19814 if (current !== null && didCaptureError) { | |
| 19815 // If we're recovering from an error, reconcile without reusing any of | |
| 19816 // the existing children. Conceptually, the normal children and the children | |
| 19817 // that are shown on error are two different sets, so we shouldn't reuse | |
| 19818 // normal children even if their identities match. | |
| 19819 forceUnmountCurrentAndReconcile(current, workInProgress, nextChildren, renderLanes); | |
| 19820 } else { | |
| 19821 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19822 } // Memoize state using the values we just used to render. | |
| 19823 // TODO: Restructure so we never read values from the instance. | |
| 19824 | |
| 19825 | |
| 19826 workInProgress.memoizedState = instance.state; // The context might have changed so we need to recalculate it. | |
| 19827 | |
| 19828 if (hasContext) { | |
| 19829 invalidateContextProvider(workInProgress, Component, true); | |
| 19830 } | |
| 19831 | |
| 19832 return workInProgress.child; | |
| 19833 } | |
| 19834 | |
| 19835 function pushHostRootContext(workInProgress) { | |
| 19836 var root = workInProgress.stateNode; | |
| 19837 | |
| 19838 if (root.pendingContext) { | |
| 19839 pushTopLevelContextObject(workInProgress, root.pendingContext, root.pendingContext !== root.context); | |
| 19840 } else if (root.context) { | |
| 19841 // Should always be set | |
| 19842 pushTopLevelContextObject(workInProgress, root.context, false); | |
| 19843 } | |
| 19844 | |
| 19845 pushHostContainer(workInProgress, root.containerInfo); | |
| 19846 } | |
| 19847 | |
| 19848 function updateHostRoot(current, workInProgress, renderLanes) { | |
| 19849 pushHostRootContext(workInProgress); | |
| 19850 | |
| 19851 if (current === null) { | |
| 19852 throw new Error('Should have a current fiber. This is a bug in React.'); | |
| 19853 } | |
| 19854 | |
| 19855 var nextProps = workInProgress.pendingProps; | |
| 19856 var prevState = workInProgress.memoizedState; | |
| 19857 var prevChildren = prevState.element; | |
| 19858 cloneUpdateQueue(current, workInProgress); | |
| 19859 processUpdateQueue(workInProgress, nextProps, null, renderLanes); | |
| 19860 var nextState = workInProgress.memoizedState; | |
| 19861 var root = workInProgress.stateNode; | |
| 19862 // being called "element". | |
| 19863 | |
| 19864 | |
| 19865 var nextChildren = nextState.element; | |
| 19866 | |
| 19867 if ( prevState.isDehydrated) { | |
| 19868 // This is a hydration root whose shell has not yet hydrated. We should | |
| 19869 // attempt to hydrate. | |
| 19870 // Flip isDehydrated to false to indicate that when this render | |
| 19871 // finishes, the root will no longer be dehydrated. | |
| 19872 var overrideState = { | |
| 19873 element: nextChildren, | |
| 19874 isDehydrated: false, | |
| 19875 cache: nextState.cache, | |
| 19876 pendingSuspenseBoundaries: nextState.pendingSuspenseBoundaries, | |
| 19877 transitions: nextState.transitions | |
| 19878 }; | |
| 19879 var updateQueue = workInProgress.updateQueue; // `baseState` can always be the last state because the root doesn't | |
| 19880 // have reducer functions so it doesn't need rebasing. | |
| 19881 | |
| 19882 updateQueue.baseState = overrideState; | |
| 19883 workInProgress.memoizedState = overrideState; | |
| 19884 | |
| 19885 if (workInProgress.flags & ForceClientRender) { | |
| 19886 // Something errored during a previous attempt to hydrate the shell, so we | |
| 19887 // forced a client render. | |
| 19888 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); | |
| 19889 return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError); | |
| 19890 } else if (nextChildren !== prevChildren) { | |
| 19891 var _recoverableError = createCapturedValueAtFiber(new Error('This root received an early update, before anything was able ' + 'hydrate. Switched the entire root to client rendering.'), workInProgress); | |
| 19892 | |
| 19893 return mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, _recoverableError); | |
| 19894 } else { | |
| 19895 // The outermost shell has not hydrated yet. Start hydrating. | |
| 19896 enterHydrationState(workInProgress); | |
| 19897 | |
| 19898 var child = mountChildFibers(workInProgress, null, nextChildren, renderLanes); | |
| 19899 workInProgress.child = child; | |
| 19900 var node = child; | |
| 19901 | |
| 19902 while (node) { | |
| 19903 // Mark each child as hydrating. This is a fast path to know whether this | |
| 19904 // tree is part of a hydrating tree. This is used to determine if a child | |
| 19905 // node has fully mounted yet, and for scheduling event replaying. | |
| 19906 // Conceptually this is similar to Placement in that a new subtree is | |
| 19907 // inserted into the React tree here. It just happens to not need DOM | |
| 19908 // mutations because it already exists. | |
| 19909 node.flags = node.flags & ~Placement | Hydrating; | |
| 19910 node = node.sibling; | |
| 19911 } | |
| 19912 } | |
| 19913 } else { | |
| 19914 // Root is not dehydrated. Either this is a client-only root, or it | |
| 19915 // already hydrated. | |
| 19916 resetHydrationState(); | |
| 19917 | |
| 19918 if (nextChildren === prevChildren) { | |
| 19919 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 19920 } | |
| 19921 | |
| 19922 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19923 } | |
| 19924 | |
| 19925 return workInProgress.child; | |
| 19926 } | |
| 19927 | |
| 19928 function mountHostRootWithoutHydrating(current, workInProgress, nextChildren, renderLanes, recoverableError) { | |
| 19929 // Revert to client rendering. | |
| 19930 resetHydrationState(); | |
| 19931 queueHydrationError(recoverableError); | |
| 19932 workInProgress.flags |= ForceClientRender; | |
| 19933 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19934 return workInProgress.child; | |
| 19935 } | |
| 19936 | |
| 19937 function updateHostComponent(current, workInProgress, renderLanes) { | |
| 19938 pushHostContext(workInProgress); | |
| 19939 | |
| 19940 if (current === null) { | |
| 19941 tryToClaimNextHydratableInstance(workInProgress); | |
| 19942 } | |
| 19943 | |
| 19944 var type = workInProgress.type; | |
| 19945 var nextProps = workInProgress.pendingProps; | |
| 19946 var prevProps = current !== null ? current.memoizedProps : null; | |
| 19947 var nextChildren = nextProps.children; | |
| 19948 var isDirectTextChild = shouldSetTextContent(type, nextProps); | |
| 19949 | |
| 19950 if (isDirectTextChild) { | |
| 19951 // We special case a direct text child of a host node. This is a common | |
| 19952 // case. We won't handle it as a reified child. We will instead handle | |
| 19953 // this in the host environment that also has access to this prop. That | |
| 19954 // avoids allocating another HostText fiber and traversing it. | |
| 19955 nextChildren = null; | |
| 19956 } else if (prevProps !== null && shouldSetTextContent(type, prevProps)) { | |
| 19957 // If we're switching from a direct text child to a normal child, or to | |
| 19958 // empty, we need to schedule the text content to be reset. | |
| 19959 workInProgress.flags |= ContentReset; | |
| 19960 } | |
| 19961 | |
| 19962 markRef(current, workInProgress); | |
| 19963 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 19964 return workInProgress.child; | |
| 19965 } | |
| 19966 | |
| 19967 function updateHostText(current, workInProgress) { | |
| 19968 if (current === null) { | |
| 19969 tryToClaimNextHydratableInstance(workInProgress); | |
| 19970 } // Nothing to do here. This is terminal. We'll do the completion step | |
| 19971 // immediately after. | |
| 19972 | |
| 19973 | |
| 19974 return null; | |
| 19975 } | |
| 19976 | |
| 19977 function mountLazyComponent(_current, workInProgress, elementType, renderLanes) { | |
| 19978 resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); | |
| 19979 var props = workInProgress.pendingProps; | |
| 19980 var lazyComponent = elementType; | |
| 19981 var payload = lazyComponent._payload; | |
| 19982 var init = lazyComponent._init; | |
| 19983 var Component = init(payload); // Store the unwrapped component in the type. | |
| 19984 | |
| 19985 workInProgress.type = Component; | |
| 19986 var resolvedTag = workInProgress.tag = resolveLazyComponentTag(Component); | |
| 19987 var resolvedProps = resolveDefaultProps(Component, props); | |
| 19988 var child; | |
| 19989 | |
| 19990 switch (resolvedTag) { | |
| 19991 case FunctionComponent: | |
| 19992 { | |
| 19993 { | |
| 19994 validateFunctionComponentInDev(workInProgress, Component); | |
| 19995 workInProgress.type = Component = resolveFunctionForHotReloading(Component); | |
| 19996 } | |
| 19997 | |
| 19998 child = updateFunctionComponent(null, workInProgress, Component, resolvedProps, renderLanes); | |
| 19999 return child; | |
| 20000 } | |
| 20001 | |
| 20002 case ClassComponent: | |
| 20003 { | |
| 20004 { | |
| 20005 workInProgress.type = Component = resolveClassForHotReloading(Component); | |
| 20006 } | |
| 20007 | |
| 20008 child = updateClassComponent(null, workInProgress, Component, resolvedProps, renderLanes); | |
| 20009 return child; | |
| 20010 } | |
| 20011 | |
| 20012 case ForwardRef: | |
| 20013 { | |
| 20014 { | |
| 20015 workInProgress.type = Component = resolveForwardRefForHotReloading(Component); | |
| 20016 } | |
| 20017 | |
| 20018 child = updateForwardRef(null, workInProgress, Component, resolvedProps, renderLanes); | |
| 20019 return child; | |
| 20020 } | |
| 20021 | |
| 20022 case MemoComponent: | |
| 20023 { | |
| 20024 { | |
| 20025 if (workInProgress.type !== workInProgress.elementType) { | |
| 20026 var outerPropTypes = Component.propTypes; | |
| 20027 | |
| 20028 if (outerPropTypes) { | |
| 20029 checkPropTypes(outerPropTypes, resolvedProps, // Resolved for outer only | |
| 20030 'prop', getComponentNameFromType(Component)); | |
| 20031 } | |
| 20032 } | |
| 20033 } | |
| 20034 | |
| 20035 child = updateMemoComponent(null, workInProgress, Component, resolveDefaultProps(Component.type, resolvedProps), // The inner type can have defaults too | |
| 20036 renderLanes); | |
| 20037 return child; | |
| 20038 } | |
| 20039 } | |
| 20040 | |
| 20041 var hint = ''; | |
| 20042 | |
| 20043 { | |
| 20044 if (Component !== null && typeof Component === 'object' && Component.$$typeof === REACT_LAZY_TYPE) { | |
| 20045 hint = ' Did you wrap a component in React.lazy() more than once?'; | |
| 20046 } | |
| 20047 } // This message intentionally doesn't mention ForwardRef or MemoComponent | |
| 20048 // because the fact that it's a separate type of work is an | |
| 20049 // implementation detail. | |
| 20050 | |
| 20051 | |
| 20052 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)); | |
| 20053 } | |
| 20054 | |
| 20055 function mountIncompleteClassComponent(_current, workInProgress, Component, nextProps, renderLanes) { | |
| 20056 resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); // Promote the fiber to a class and try rendering again. | |
| 20057 | |
| 20058 workInProgress.tag = ClassComponent; // The rest of this function is a fork of `updateClassComponent` | |
| 20059 // Push context providers early to prevent context stack mismatches. | |
| 20060 // During mounting we don't know the child context yet as the instance doesn't exist. | |
| 20061 // We will invalidate the child context in finishClassComponent() right after rendering. | |
| 20062 | |
| 20063 var hasContext; | |
| 20064 | |
| 20065 if (isContextProvider(Component)) { | |
| 20066 hasContext = true; | |
| 20067 pushContextProvider(workInProgress); | |
| 20068 } else { | |
| 20069 hasContext = false; | |
| 20070 } | |
| 20071 | |
| 20072 prepareToReadContext(workInProgress, renderLanes); | |
| 20073 constructClassInstance(workInProgress, Component, nextProps); | |
| 20074 mountClassInstance(workInProgress, Component, nextProps, renderLanes); | |
| 20075 return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes); | |
| 20076 } | |
| 20077 | |
| 20078 function mountIndeterminateComponent(_current, workInProgress, Component, renderLanes) { | |
| 20079 resetSuspendedCurrentOnMountInLegacyMode(_current, workInProgress); | |
| 20080 var props = workInProgress.pendingProps; | |
| 20081 var context; | |
| 20082 | |
| 20083 { | |
| 20084 var unmaskedContext = getUnmaskedContext(workInProgress, Component, false); | |
| 20085 context = getMaskedContext(workInProgress, unmaskedContext); | |
| 20086 } | |
| 20087 | |
| 20088 prepareToReadContext(workInProgress, renderLanes); | |
| 20089 var value; | |
| 20090 var hasId; | |
| 20091 | |
| 20092 { | |
| 20093 markComponentRenderStarted(workInProgress); | |
| 20094 } | |
| 20095 | |
| 20096 { | |
| 20097 if (Component.prototype && typeof Component.prototype.render === 'function') { | |
| 20098 var componentName = getComponentNameFromType(Component) || 'Unknown'; | |
| 20099 | |
| 20100 if (!didWarnAboutBadClass[componentName]) { | |
| 20101 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); | |
| 20102 | |
| 20103 didWarnAboutBadClass[componentName] = true; | |
| 20104 } | |
| 20105 } | |
| 20106 | |
| 20107 if (workInProgress.mode & StrictLegacyMode) { | |
| 20108 ReactStrictModeWarnings.recordLegacyContextWarning(workInProgress, null); | |
| 20109 } | |
| 20110 | |
| 20111 setIsRendering(true); | |
| 20112 ReactCurrentOwner$1.current = workInProgress; | |
| 20113 value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes); | |
| 20114 hasId = checkDidRenderIdHook(); | |
| 20115 setIsRendering(false); | |
| 20116 } | |
| 20117 | |
| 20118 { | |
| 20119 markComponentRenderStopped(); | |
| 20120 } // React DevTools reads this flag. | |
| 20121 | |
| 20122 | |
| 20123 workInProgress.flags |= PerformedWork; | |
| 20124 | |
| 20125 { | |
| 20126 // Support for module components is deprecated and is removed behind a flag. | |
| 20127 // Whether or not it would crash later, we want to show a good message in DEV first. | |
| 20128 if (typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) { | |
| 20129 var _componentName = getComponentNameFromType(Component) || 'Unknown'; | |
| 20130 | |
| 20131 if (!didWarnAboutModulePatternComponent[_componentName]) { | |
| 20132 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); | |
| 20133 | |
| 20134 didWarnAboutModulePatternComponent[_componentName] = true; | |
| 20135 } | |
| 20136 } | |
| 20137 } | |
| 20138 | |
| 20139 if ( // Run these checks in production only if the flag is off. | |
| 20140 // Eventually we'll delete this branch altogether. | |
| 20141 typeof value === 'object' && value !== null && typeof value.render === 'function' && value.$$typeof === undefined) { | |
| 20142 { | |
| 20143 var _componentName2 = getComponentNameFromType(Component) || 'Unknown'; | |
| 20144 | |
| 20145 if (!didWarnAboutModulePatternComponent[_componentName2]) { | |
| 20146 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); | |
| 20147 | |
| 20148 didWarnAboutModulePatternComponent[_componentName2] = true; | |
| 20149 } | |
| 20150 } // Proceed under the assumption that this is a class instance | |
| 20151 | |
| 20152 | |
| 20153 workInProgress.tag = ClassComponent; // Throw out any hooks that were used. | |
| 20154 | |
| 20155 workInProgress.memoizedState = null; | |
| 20156 workInProgress.updateQueue = null; // Push context providers early to prevent context stack mismatches. | |
| 20157 // During mounting we don't know the child context yet as the instance doesn't exist. | |
| 20158 // We will invalidate the child context in finishClassComponent() right after rendering. | |
| 20159 | |
| 20160 var hasContext = false; | |
| 20161 | |
| 20162 if (isContextProvider(Component)) { | |
| 20163 hasContext = true; | |
| 20164 pushContextProvider(workInProgress); | |
| 20165 } else { | |
| 20166 hasContext = false; | |
| 20167 } | |
| 20168 | |
| 20169 workInProgress.memoizedState = value.state !== null && value.state !== undefined ? value.state : null; | |
| 20170 initializeUpdateQueue(workInProgress); | |
| 20171 adoptClassInstance(workInProgress, value); | |
| 20172 mountClassInstance(workInProgress, Component, props, renderLanes); | |
| 20173 return finishClassComponent(null, workInProgress, Component, true, hasContext, renderLanes); | |
| 20174 } else { | |
| 20175 // Proceed under the assumption that this is a function component | |
| 20176 workInProgress.tag = FunctionComponent; | |
| 20177 | |
| 20178 { | |
| 20179 | |
| 20180 if ( workInProgress.mode & StrictLegacyMode) { | |
| 20181 setIsStrictModeForDevtools(true); | |
| 20182 | |
| 20183 try { | |
| 20184 value = renderWithHooks(null, workInProgress, Component, props, context, renderLanes); | |
| 20185 hasId = checkDidRenderIdHook(); | |
| 20186 } finally { | |
| 20187 setIsStrictModeForDevtools(false); | |
| 20188 } | |
| 20189 } | |
| 20190 } | |
| 20191 | |
| 20192 if (getIsHydrating() && hasId) { | |
| 20193 pushMaterializedTreeId(workInProgress); | |
| 20194 } | |
| 20195 | |
| 20196 reconcileChildren(null, workInProgress, value, renderLanes); | |
| 20197 | |
| 20198 { | |
| 20199 validateFunctionComponentInDev(workInProgress, Component); | |
| 20200 } | |
| 20201 | |
| 20202 return workInProgress.child; | |
| 20203 } | |
| 20204 } | |
| 20205 | |
| 20206 function validateFunctionComponentInDev(workInProgress, Component) { | |
| 20207 { | |
| 20208 if (Component) { | |
| 20209 if (Component.childContextTypes) { | |
| 20210 error('%s(...): childContextTypes cannot be defined on a function component.', Component.displayName || Component.name || 'Component'); | |
| 20211 } | |
| 20212 } | |
| 20213 | |
| 20214 if (workInProgress.ref !== null) { | |
| 20215 var info = ''; | |
| 20216 var ownerName = getCurrentFiberOwnerNameInDevOrNull(); | |
| 20217 | |
| 20218 if (ownerName) { | |
| 20219 info += '\n\nCheck the render method of `' + ownerName + '`.'; | |
| 20220 } | |
| 20221 | |
| 20222 var warningKey = ownerName || ''; | |
| 20223 var debugSource = workInProgress._debugSource; | |
| 20224 | |
| 20225 if (debugSource) { | |
| 20226 warningKey = debugSource.fileName + ':' + debugSource.lineNumber; | |
| 20227 } | |
| 20228 | |
| 20229 if (!didWarnAboutFunctionRefs[warningKey]) { | |
| 20230 didWarnAboutFunctionRefs[warningKey] = true; | |
| 20231 | |
| 20232 error('Function components cannot be given refs. ' + 'Attempts to access this ref will fail. ' + 'Did you mean to use React.forwardRef()?%s', info); | |
| 20233 } | |
| 20234 } | |
| 20235 | |
| 20236 if ( Component.defaultProps !== undefined) { | |
| 20237 var componentName = getComponentNameFromType(Component) || 'Unknown'; | |
| 20238 | |
| 20239 if (!didWarnAboutDefaultPropsOnFunctionComponent[componentName]) { | |
| 20240 error('%s: Support for defaultProps will be removed from function components ' + 'in a future major release. Use JavaScript default parameters instead.', componentName); | |
| 20241 | |
| 20242 didWarnAboutDefaultPropsOnFunctionComponent[componentName] = true; | |
| 20243 } | |
| 20244 } | |
| 20245 | |
| 20246 if (typeof Component.getDerivedStateFromProps === 'function') { | |
| 20247 var _componentName3 = getComponentNameFromType(Component) || 'Unknown'; | |
| 20248 | |
| 20249 if (!didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3]) { | |
| 20250 error('%s: Function components do not support getDerivedStateFromProps.', _componentName3); | |
| 20251 | |
| 20252 didWarnAboutGetDerivedStateOnFunctionComponent[_componentName3] = true; | |
| 20253 } | |
| 20254 } | |
| 20255 | |
| 20256 if (typeof Component.contextType === 'object' && Component.contextType !== null) { | |
| 20257 var _componentName4 = getComponentNameFromType(Component) || 'Unknown'; | |
| 20258 | |
| 20259 if (!didWarnAboutContextTypeOnFunctionComponent[_componentName4]) { | |
| 20260 error('%s: Function components do not support contextType.', _componentName4); | |
| 20261 | |
| 20262 didWarnAboutContextTypeOnFunctionComponent[_componentName4] = true; | |
| 20263 } | |
| 20264 } | |
| 20265 } | |
| 20266 } | |
| 20267 | |
| 20268 var SUSPENDED_MARKER = { | |
| 20269 dehydrated: null, | |
| 20270 treeContext: null, | |
| 20271 retryLane: NoLane | |
| 20272 }; | |
| 20273 | |
| 20274 function mountSuspenseOffscreenState(renderLanes) { | |
| 20275 return { | |
| 20276 baseLanes: renderLanes, | |
| 20277 cachePool: getSuspendedCache(), | |
| 20278 transitions: null | |
| 20279 }; | |
| 20280 } | |
| 20281 | |
| 20282 function updateSuspenseOffscreenState(prevOffscreenState, renderLanes) { | |
| 20283 var cachePool = null; | |
| 20284 | |
| 20285 return { | |
| 20286 baseLanes: mergeLanes(prevOffscreenState.baseLanes, renderLanes), | |
| 20287 cachePool: cachePool, | |
| 20288 transitions: prevOffscreenState.transitions | |
| 20289 }; | |
| 20290 } // TODO: Probably should inline this back | |
| 20291 | |
| 20292 | |
| 20293 function shouldRemainOnFallback(suspenseContext, current, workInProgress, renderLanes) { | |
| 20294 // If we're already showing a fallback, there are cases where we need to | |
| 20295 // remain on that fallback regardless of whether the content has resolved. | |
| 20296 // For example, SuspenseList coordinates when nested content appears. | |
| 20297 if (current !== null) { | |
| 20298 var suspenseState = current.memoizedState; | |
| 20299 | |
| 20300 if (suspenseState === null) { | |
| 20301 // Currently showing content. Don't hide it, even if ForceSuspenseFallback | |
| 20302 // is true. More precise name might be "ForceRemainSuspenseFallback". | |
| 20303 // Note: This is a factoring smell. Can't remain on a fallback if there's | |
| 20304 // no fallback to remain on. | |
| 20305 return false; | |
| 20306 } | |
| 20307 } // Not currently showing content. Consult the Suspense context. | |
| 20308 | |
| 20309 | |
| 20310 return hasSuspenseContext(suspenseContext, ForceSuspenseFallback); | |
| 20311 } | |
| 20312 | |
| 20313 function getRemainingWorkInPrimaryTree(current, renderLanes) { | |
| 20314 // TODO: Should not remove render lanes that were pinged during this render | |
| 20315 return removeLanes(current.childLanes, renderLanes); | |
| 20316 } | |
| 20317 | |
| 20318 function updateSuspenseComponent(current, workInProgress, renderLanes) { | |
| 20319 var nextProps = workInProgress.pendingProps; // This is used by DevTools to force a boundary to suspend. | |
| 20320 | |
| 20321 { | |
| 20322 if (shouldSuspend(workInProgress)) { | |
| 20323 workInProgress.flags |= DidCapture; | |
| 20324 } | |
| 20325 } | |
| 20326 | |
| 20327 var suspenseContext = suspenseStackCursor.current; | |
| 20328 var showFallback = false; | |
| 20329 var didSuspend = (workInProgress.flags & DidCapture) !== NoFlags; | |
| 20330 | |
| 20331 if (didSuspend || shouldRemainOnFallback(suspenseContext, current)) { | |
| 20332 // Something in this boundary's subtree already suspended. Switch to | |
| 20333 // rendering the fallback children. | |
| 20334 showFallback = true; | |
| 20335 workInProgress.flags &= ~DidCapture; | |
| 20336 } else { | |
| 20337 // Attempting the main content | |
| 20338 if (current === null || current.memoizedState !== null) { | |
| 20339 // This is a new mount or this boundary is already showing a fallback state. | |
| 20340 // Mark this subtree context as having at least one invisible parent that could | |
| 20341 // handle the fallback state. | |
| 20342 // Avoided boundaries are not considered since they cannot handle preferred fallback states. | |
| 20343 { | |
| 20344 suspenseContext = addSubtreeSuspenseContext(suspenseContext, InvisibleParentSuspenseContext); | |
| 20345 } | |
| 20346 } | |
| 20347 } | |
| 20348 | |
| 20349 suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); | |
| 20350 pushSuspenseContext(workInProgress, suspenseContext); // OK, the next part is confusing. We're about to reconcile the Suspense | |
| 20351 // boundary's children. This involves some custom reconciliation logic. Two | |
| 20352 // main reasons this is so complicated. | |
| 20353 // | |
| 20354 // First, Legacy Mode has different semantics for backwards compatibility. The | |
| 20355 // primary tree will commit in an inconsistent state, so when we do the | |
| 20356 // second pass to render the fallback, we do some exceedingly, uh, clever | |
| 20357 // hacks to make that not totally break. Like transferring effects and | |
| 20358 // deletions from hidden tree. In Concurrent Mode, it's much simpler, | |
| 20359 // because we bailout on the primary tree completely and leave it in its old | |
| 20360 // state, no effects. Same as what we do for Offscreen (except that | |
| 20361 // Offscreen doesn't have the first render pass). | |
| 20362 // | |
| 20363 // Second is hydration. During hydration, the Suspense fiber has a slightly | |
| 20364 // different layout, where the child points to a dehydrated fragment, which | |
| 20365 // contains the DOM rendered by the server. | |
| 20366 // | |
| 20367 // Third, even if you set all that aside, Suspense is like error boundaries in | |
| 20368 // that we first we try to render one tree, and if that fails, we render again | |
| 20369 // and switch to a different tree. Like a try/catch block. So we have to track | |
| 20370 // which branch we're currently rendering. Ideally we would model this using | |
| 20371 // a stack. | |
| 20372 | |
| 20373 if (current === null) { | |
| 20374 // Initial mount | |
| 20375 // Special path for hydration | |
| 20376 // If we're currently hydrating, try to hydrate this boundary. | |
| 20377 tryToClaimNextHydratableInstance(workInProgress); // This could've been a dehydrated suspense component. | |
| 20378 | |
| 20379 var suspenseState = workInProgress.memoizedState; | |
| 20380 | |
| 20381 if (suspenseState !== null) { | |
| 20382 var dehydrated = suspenseState.dehydrated; | |
| 20383 | |
| 20384 if (dehydrated !== null) { | |
| 20385 return mountDehydratedSuspenseComponent(workInProgress, dehydrated); | |
| 20386 } | |
| 20387 } | |
| 20388 | |
| 20389 var nextPrimaryChildren = nextProps.children; | |
| 20390 var nextFallbackChildren = nextProps.fallback; | |
| 20391 | |
| 20392 if (showFallback) { | |
| 20393 var fallbackFragment = mountSuspenseFallbackChildren(workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes); | |
| 20394 var primaryChildFragment = workInProgress.child; | |
| 20395 primaryChildFragment.memoizedState = mountSuspenseOffscreenState(renderLanes); | |
| 20396 workInProgress.memoizedState = SUSPENDED_MARKER; | |
| 20397 | |
| 20398 return fallbackFragment; | |
| 20399 } else { | |
| 20400 return mountSuspensePrimaryChildren(workInProgress, nextPrimaryChildren); | |
| 20401 } | |
| 20402 } else { | |
| 20403 // This is an update. | |
| 20404 // Special path for hydration | |
| 20405 var prevState = current.memoizedState; | |
| 20406 | |
| 20407 if (prevState !== null) { | |
| 20408 var _dehydrated = prevState.dehydrated; | |
| 20409 | |
| 20410 if (_dehydrated !== null) { | |
| 20411 return updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, _dehydrated, prevState, renderLanes); | |
| 20412 } | |
| 20413 } | |
| 20414 | |
| 20415 if (showFallback) { | |
| 20416 var _nextFallbackChildren = nextProps.fallback; | |
| 20417 var _nextPrimaryChildren = nextProps.children; | |
| 20418 var fallbackChildFragment = updateSuspenseFallbackChildren(current, workInProgress, _nextPrimaryChildren, _nextFallbackChildren, renderLanes); | |
| 20419 var _primaryChildFragment2 = workInProgress.child; | |
| 20420 var prevOffscreenState = current.child.memoizedState; | |
| 20421 _primaryChildFragment2.memoizedState = prevOffscreenState === null ? mountSuspenseOffscreenState(renderLanes) : updateSuspenseOffscreenState(prevOffscreenState, renderLanes); | |
| 20422 | |
| 20423 _primaryChildFragment2.childLanes = getRemainingWorkInPrimaryTree(current, renderLanes); | |
| 20424 workInProgress.memoizedState = SUSPENDED_MARKER; | |
| 20425 return fallbackChildFragment; | |
| 20426 } else { | |
| 20427 var _nextPrimaryChildren2 = nextProps.children; | |
| 20428 | |
| 20429 var _primaryChildFragment3 = updateSuspensePrimaryChildren(current, workInProgress, _nextPrimaryChildren2, renderLanes); | |
| 20430 | |
| 20431 workInProgress.memoizedState = null; | |
| 20432 return _primaryChildFragment3; | |
| 20433 } | |
| 20434 } | |
| 20435 } | |
| 20436 | |
| 20437 function mountSuspensePrimaryChildren(workInProgress, primaryChildren, renderLanes) { | |
| 20438 var mode = workInProgress.mode; | |
| 20439 var primaryChildProps = { | |
| 20440 mode: 'visible', | |
| 20441 children: primaryChildren | |
| 20442 }; | |
| 20443 var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode); | |
| 20444 primaryChildFragment.return = workInProgress; | |
| 20445 workInProgress.child = primaryChildFragment; | |
| 20446 return primaryChildFragment; | |
| 20447 } | |
| 20448 | |
| 20449 function mountSuspenseFallbackChildren(workInProgress, primaryChildren, fallbackChildren, renderLanes) { | |
| 20450 var mode = workInProgress.mode; | |
| 20451 var progressedPrimaryFragment = workInProgress.child; | |
| 20452 var primaryChildProps = { | |
| 20453 mode: 'hidden', | |
| 20454 children: primaryChildren | |
| 20455 }; | |
| 20456 var primaryChildFragment; | |
| 20457 var fallbackChildFragment; | |
| 20458 | |
| 20459 if ((mode & ConcurrentMode) === NoMode && progressedPrimaryFragment !== null) { | |
| 20460 // In legacy mode, we commit the primary tree as if it successfully | |
| 20461 // completed, even though it's in an inconsistent state. | |
| 20462 primaryChildFragment = progressedPrimaryFragment; | |
| 20463 primaryChildFragment.childLanes = NoLanes; | |
| 20464 primaryChildFragment.pendingProps = primaryChildProps; | |
| 20465 | |
| 20466 if ( workInProgress.mode & ProfileMode) { | |
| 20467 // Reset the durations from the first pass so they aren't included in the | |
| 20468 // final amounts. This seems counterintuitive, since we're intentionally | |
| 20469 // not measuring part of the render phase, but this makes it match what we | |
| 20470 // do in Concurrent Mode. | |
| 20471 primaryChildFragment.actualDuration = 0; | |
| 20472 primaryChildFragment.actualStartTime = -1; | |
| 20473 primaryChildFragment.selfBaseDuration = 0; | |
| 20474 primaryChildFragment.treeBaseDuration = 0; | |
| 20475 } | |
| 20476 | |
| 20477 fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null); | |
| 20478 } else { | |
| 20479 primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, mode); | |
| 20480 fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null); | |
| 20481 } | |
| 20482 | |
| 20483 primaryChildFragment.return = workInProgress; | |
| 20484 fallbackChildFragment.return = workInProgress; | |
| 20485 primaryChildFragment.sibling = fallbackChildFragment; | |
| 20486 workInProgress.child = primaryChildFragment; | |
| 20487 return fallbackChildFragment; | |
| 20488 } | |
| 20489 | |
| 20490 function mountWorkInProgressOffscreenFiber(offscreenProps, mode, renderLanes) { | |
| 20491 // The props argument to `createFiberFromOffscreen` is `any` typed, so we use | |
| 20492 // this wrapper function to constrain it. | |
| 20493 return createFiberFromOffscreen(offscreenProps, mode, NoLanes, null); | |
| 20494 } | |
| 20495 | |
| 20496 function updateWorkInProgressOffscreenFiber(current, offscreenProps) { | |
| 20497 // The props argument to `createWorkInProgress` is `any` typed, so we use this | |
| 20498 // wrapper function to constrain it. | |
| 20499 return createWorkInProgress(current, offscreenProps); | |
| 20500 } | |
| 20501 | |
| 20502 function updateSuspensePrimaryChildren(current, workInProgress, primaryChildren, renderLanes) { | |
| 20503 var currentPrimaryChildFragment = current.child; | |
| 20504 var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; | |
| 20505 var primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, { | |
| 20506 mode: 'visible', | |
| 20507 children: primaryChildren | |
| 20508 }); | |
| 20509 | |
| 20510 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 20511 primaryChildFragment.lanes = renderLanes; | |
| 20512 } | |
| 20513 | |
| 20514 primaryChildFragment.return = workInProgress; | |
| 20515 primaryChildFragment.sibling = null; | |
| 20516 | |
| 20517 if (currentFallbackChildFragment !== null) { | |
| 20518 // Delete the fallback child fragment | |
| 20519 var deletions = workInProgress.deletions; | |
| 20520 | |
| 20521 if (deletions === null) { | |
| 20522 workInProgress.deletions = [currentFallbackChildFragment]; | |
| 20523 workInProgress.flags |= ChildDeletion; | |
| 20524 } else { | |
| 20525 deletions.push(currentFallbackChildFragment); | |
| 20526 } | |
| 20527 } | |
| 20528 | |
| 20529 workInProgress.child = primaryChildFragment; | |
| 20530 return primaryChildFragment; | |
| 20531 } | |
| 20532 | |
| 20533 function updateSuspenseFallbackChildren(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) { | |
| 20534 var mode = workInProgress.mode; | |
| 20535 var currentPrimaryChildFragment = current.child; | |
| 20536 var currentFallbackChildFragment = currentPrimaryChildFragment.sibling; | |
| 20537 var primaryChildProps = { | |
| 20538 mode: 'hidden', | |
| 20539 children: primaryChildren | |
| 20540 }; | |
| 20541 var primaryChildFragment; | |
| 20542 | |
| 20543 if ( // In legacy mode, we commit the primary tree as if it successfully | |
| 20544 // completed, even though it's in an inconsistent state. | |
| 20545 (mode & ConcurrentMode) === NoMode && // Make sure we're on the second pass, i.e. the primary child fragment was | |
| 20546 // already cloned. In legacy mode, the only case where this isn't true is | |
| 20547 // when DevTools forces us to display a fallback; we skip the first render | |
| 20548 // pass entirely and go straight to rendering the fallback. (In Concurrent | |
| 20549 // Mode, SuspenseList can also trigger this scenario, but this is a legacy- | |
| 20550 // only codepath.) | |
| 20551 workInProgress.child !== currentPrimaryChildFragment) { | |
| 20552 var progressedPrimaryFragment = workInProgress.child; | |
| 20553 primaryChildFragment = progressedPrimaryFragment; | |
| 20554 primaryChildFragment.childLanes = NoLanes; | |
| 20555 primaryChildFragment.pendingProps = primaryChildProps; | |
| 20556 | |
| 20557 if ( workInProgress.mode & ProfileMode) { | |
| 20558 // Reset the durations from the first pass so they aren't included in the | |
| 20559 // final amounts. This seems counterintuitive, since we're intentionally | |
| 20560 // not measuring part of the render phase, but this makes it match what we | |
| 20561 // do in Concurrent Mode. | |
| 20562 primaryChildFragment.actualDuration = 0; | |
| 20563 primaryChildFragment.actualStartTime = -1; | |
| 20564 primaryChildFragment.selfBaseDuration = currentPrimaryChildFragment.selfBaseDuration; | |
| 20565 primaryChildFragment.treeBaseDuration = currentPrimaryChildFragment.treeBaseDuration; | |
| 20566 } // The fallback fiber was added as a deletion during the first pass. | |
| 20567 // However, since we're going to remain on the fallback, we no longer want | |
| 20568 // to delete it. | |
| 20569 | |
| 20570 | |
| 20571 workInProgress.deletions = null; | |
| 20572 } else { | |
| 20573 primaryChildFragment = updateWorkInProgressOffscreenFiber(currentPrimaryChildFragment, primaryChildProps); // Since we're reusing a current tree, we need to reuse the flags, too. | |
| 20574 // (We don't do this in legacy mode, because in legacy mode we don't re-use | |
| 20575 // the current tree; see previous branch.) | |
| 20576 | |
| 20577 primaryChildFragment.subtreeFlags = currentPrimaryChildFragment.subtreeFlags & StaticMask; | |
| 20578 } | |
| 20579 | |
| 20580 var fallbackChildFragment; | |
| 20581 | |
| 20582 if (currentFallbackChildFragment !== null) { | |
| 20583 fallbackChildFragment = createWorkInProgress(currentFallbackChildFragment, fallbackChildren); | |
| 20584 } else { | |
| 20585 fallbackChildFragment = createFiberFromFragment(fallbackChildren, mode, renderLanes, null); // Needs a placement effect because the parent (the Suspense boundary) already | |
| 20586 // mounted but this is a new fiber. | |
| 20587 | |
| 20588 fallbackChildFragment.flags |= Placement; | |
| 20589 } | |
| 20590 | |
| 20591 fallbackChildFragment.return = workInProgress; | |
| 20592 primaryChildFragment.return = workInProgress; | |
| 20593 primaryChildFragment.sibling = fallbackChildFragment; | |
| 20594 workInProgress.child = primaryChildFragment; | |
| 20595 return fallbackChildFragment; | |
| 20596 } | |
| 20597 | |
| 20598 function retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, recoverableError) { | |
| 20599 // Falling back to client rendering. Because this has performance | |
| 20600 // implications, it's considered a recoverable error, even though the user | |
| 20601 // likely won't observe anything wrong with the UI. | |
| 20602 // | |
| 20603 // The error is passed in as an argument to enforce that every caller provide | |
| 20604 // a custom message, or explicitly opt out (currently the only path that opts | |
| 20605 // out is legacy mode; every concurrent path provides an error). | |
| 20606 if (recoverableError !== null) { | |
| 20607 queueHydrationError(recoverableError); | |
| 20608 } // This will add the old fiber to the deletion list | |
| 20609 | |
| 20610 | |
| 20611 reconcileChildFibers(workInProgress, current.child, null, renderLanes); // We're now not suspended nor dehydrated. | |
| 20612 | |
| 20613 var nextProps = workInProgress.pendingProps; | |
| 20614 var primaryChildren = nextProps.children; | |
| 20615 var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Needs a placement effect because the parent (the Suspense boundary) already | |
| 20616 // mounted but this is a new fiber. | |
| 20617 | |
| 20618 primaryChildFragment.flags |= Placement; | |
| 20619 workInProgress.memoizedState = null; | |
| 20620 return primaryChildFragment; | |
| 20621 } | |
| 20622 | |
| 20623 function mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, primaryChildren, fallbackChildren, renderLanes) { | |
| 20624 var fiberMode = workInProgress.mode; | |
| 20625 var primaryChildProps = { | |
| 20626 mode: 'visible', | |
| 20627 children: primaryChildren | |
| 20628 }; | |
| 20629 var primaryChildFragment = mountWorkInProgressOffscreenFiber(primaryChildProps, fiberMode); | |
| 20630 var fallbackChildFragment = createFiberFromFragment(fallbackChildren, fiberMode, renderLanes, null); // Needs a placement effect because the parent (the Suspense | |
| 20631 // boundary) already mounted but this is a new fiber. | |
| 20632 | |
| 20633 fallbackChildFragment.flags |= Placement; | |
| 20634 primaryChildFragment.return = workInProgress; | |
| 20635 fallbackChildFragment.return = workInProgress; | |
| 20636 primaryChildFragment.sibling = fallbackChildFragment; | |
| 20637 workInProgress.child = primaryChildFragment; | |
| 20638 | |
| 20639 if ((workInProgress.mode & ConcurrentMode) !== NoMode) { | |
| 20640 // We will have dropped the effect list which contains the | |
| 20641 // deletion. We need to reconcile to delete the current child. | |
| 20642 reconcileChildFibers(workInProgress, current.child, null, renderLanes); | |
| 20643 } | |
| 20644 | |
| 20645 return fallbackChildFragment; | |
| 20646 } | |
| 20647 | |
| 20648 function mountDehydratedSuspenseComponent(workInProgress, suspenseInstance, renderLanes) { | |
| 20649 // During the first pass, we'll bail out and not drill into the children. | |
| 20650 // Instead, we'll leave the content in place and try to hydrate it later. | |
| 20651 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 20652 { | |
| 20653 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.'); | |
| 20654 } | |
| 20655 | |
| 20656 workInProgress.lanes = laneToLanes(SyncLane); | |
| 20657 } else if (isSuspenseInstanceFallback(suspenseInstance)) { | |
| 20658 // This is a client-only boundary. Since we won't get any content from the server | |
| 20659 // for this, we need to schedule that at a higher priority based on when it would | |
| 20660 // have timed out. In theory we could render it in this pass but it would have the | |
| 20661 // wrong priority associated with it and will prevent hydration of parent path. | |
| 20662 // Instead, we'll leave work left on it to render it in a separate commit. | |
| 20663 // TODO This time should be the time at which the server rendered response that is | |
| 20664 // a parent to this boundary was displayed. However, since we currently don't have | |
| 20665 // a protocol to transfer that time, we'll just estimate it by using the current | |
| 20666 // time. This will mean that Suspense timeouts are slightly shifted to later than | |
| 20667 // they should be. | |
| 20668 // Schedule a normal pri update to render this content. | |
| 20669 workInProgress.lanes = laneToLanes(DefaultHydrationLane); | |
| 20670 } else { | |
| 20671 // We'll continue hydrating the rest at offscreen priority since we'll already | |
| 20672 // be showing the right content coming from the server, it is no rush. | |
| 20673 workInProgress.lanes = laneToLanes(OffscreenLane); | |
| 20674 } | |
| 20675 | |
| 20676 return null; | |
| 20677 } | |
| 20678 | |
| 20679 function updateDehydratedSuspenseComponent(current, workInProgress, didSuspend, nextProps, suspenseInstance, suspenseState, renderLanes) { | |
| 20680 if (!didSuspend) { | |
| 20681 // This is the first render pass. Attempt to hydrate. | |
| 20682 // We should never be hydrating at this point because it is the first pass, | |
| 20683 // but after we've already committed once. | |
| 20684 warnIfHydrating(); | |
| 20685 | |
| 20686 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 20687 return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, // TODO: When we delete legacy mode, we should make this error argument | |
| 20688 // required — every concurrent mode path that causes hydration to | |
| 20689 // de-opt to client rendering should have an error message. | |
| 20690 null); | |
| 20691 } | |
| 20692 | |
| 20693 if (isSuspenseInstanceFallback(suspenseInstance)) { | |
| 20694 // This boundary is in a permanent fallback state. In this case, we'll never | |
| 20695 // get an update and we'll never be able to hydrate the final content. Let's just try the | |
| 20696 // client side render instead. | |
| 20697 var digest, message, stack; | |
| 20698 | |
| 20699 { | |
| 20700 var _getSuspenseInstanceF = getSuspenseInstanceFallbackErrorDetails(suspenseInstance); | |
| 20701 | |
| 20702 digest = _getSuspenseInstanceF.digest; | |
| 20703 message = _getSuspenseInstanceF.message; | |
| 20704 stack = _getSuspenseInstanceF.stack; | |
| 20705 } | |
| 20706 | |
| 20707 var error; | |
| 20708 | |
| 20709 if (message) { | |
| 20710 // eslint-disable-next-line react-internal/prod-error-codes | |
| 20711 error = new Error(message); | |
| 20712 } else { | |
| 20713 error = new Error('The server could not finish this Suspense boundary, likely ' + 'due to an error during server rendering. Switched to ' + 'client rendering.'); | |
| 20714 } | |
| 20715 | |
| 20716 var capturedValue = createCapturedValue(error, digest, stack); | |
| 20717 return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, capturedValue); | |
| 20718 } | |
| 20719 // any context has changed, we need to treat is as if the input might have changed. | |
| 20720 | |
| 20721 | |
| 20722 var hasContextChanged = includesSomeLane(renderLanes, current.childLanes); | |
| 20723 | |
| 20724 if (didReceiveUpdate || hasContextChanged) { | |
| 20725 // This boundary has changed since the first render. This means that we are now unable to | |
| 20726 // hydrate it. We might still be able to hydrate it using a higher priority lane. | |
| 20727 var root = getWorkInProgressRoot(); | |
| 20728 | |
| 20729 if (root !== null) { | |
| 20730 var attemptHydrationAtLane = getBumpedLaneForHydration(root, renderLanes); | |
| 20731 | |
| 20732 if (attemptHydrationAtLane !== NoLane && attemptHydrationAtLane !== suspenseState.retryLane) { | |
| 20733 // Intentionally mutating since this render will get interrupted. This | |
| 20734 // is one of the very rare times where we mutate the current tree | |
| 20735 // during the render phase. | |
| 20736 suspenseState.retryLane = attemptHydrationAtLane; // TODO: Ideally this would inherit the event time of the current render | |
| 20737 | |
| 20738 var eventTime = NoTimestamp; | |
| 20739 enqueueConcurrentRenderForLane(current, attemptHydrationAtLane); | |
| 20740 scheduleUpdateOnFiber(root, current, attemptHydrationAtLane, eventTime); | |
| 20741 } | |
| 20742 } // If we have scheduled higher pri work above, this will probably just abort the render | |
| 20743 // since we now have higher priority work, but in case it doesn't, we need to prepare to | |
| 20744 // render something, if we time out. Even if that requires us to delete everything and | |
| 20745 // skip hydration. | |
| 20746 // Delay having to do this as long as the suspense timeout allows us. | |
| 20747 | |
| 20748 | |
| 20749 renderDidSuspendDelayIfPossible(); | |
| 20750 | |
| 20751 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.')); | |
| 20752 | |
| 20753 return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue); | |
| 20754 } else if (isSuspenseInstancePending(suspenseInstance)) { | |
| 20755 // This component is still pending more data from the server, so we can't hydrate its | |
| 20756 // content. We treat it as if this component suspended itself. It might seem as if | |
| 20757 // we could just try to render it client-side instead. However, this will perform a | |
| 20758 // lot of unnecessary work and is unlikely to complete since it often will suspend | |
| 20759 // on missing data anyway. Additionally, the server might be able to render more | |
| 20760 // than we can on the client yet. In that case we'd end up with more fallback states | |
| 20761 // on the client than if we just leave it alone. If the server times out or errors | |
| 20762 // these should update this boundary to the permanent Fallback state instead. | |
| 20763 // Mark it as having captured (i.e. suspended). | |
| 20764 workInProgress.flags |= DidCapture; // Leave the child in place. I.e. the dehydrated fragment. | |
| 20765 | |
| 20766 workInProgress.child = current.child; // Register a callback to retry this boundary once the server has sent the result. | |
| 20767 | |
| 20768 var retry = retryDehydratedSuspenseBoundary.bind(null, current); | |
| 20769 registerSuspenseInstanceRetry(suspenseInstance, retry); | |
| 20770 return null; | |
| 20771 } else { | |
| 20772 // This is the first attempt. | |
| 20773 reenterHydrationStateFromDehydratedSuspenseInstance(workInProgress, suspenseInstance, suspenseState.treeContext); | |
| 20774 var primaryChildren = nextProps.children; | |
| 20775 var primaryChildFragment = mountSuspensePrimaryChildren(workInProgress, primaryChildren); // Mark the children as hydrating. This is a fast path to know whether this | |
| 20776 // tree is part of a hydrating tree. This is used to determine if a child | |
| 20777 // node has fully mounted yet, and for scheduling event replaying. | |
| 20778 // Conceptually this is similar to Placement in that a new subtree is | |
| 20779 // inserted into the React tree here. It just happens to not need DOM | |
| 20780 // mutations because it already exists. | |
| 20781 | |
| 20782 primaryChildFragment.flags |= Hydrating; | |
| 20783 return primaryChildFragment; | |
| 20784 } | |
| 20785 } else { | |
| 20786 // This is the second render pass. We already attempted to hydrated, but | |
| 20787 // something either suspended or errored. | |
| 20788 if (workInProgress.flags & ForceClientRender) { | |
| 20789 // Something errored during hydration. Try again without hydrating. | |
| 20790 workInProgress.flags &= ~ForceClientRender; | |
| 20791 | |
| 20792 var _capturedValue2 = createCapturedValue(new Error('There was an error while hydrating this Suspense boundary. ' + 'Switched to client rendering.')); | |
| 20793 | |
| 20794 return retrySuspenseComponentWithoutHydrating(current, workInProgress, renderLanes, _capturedValue2); | |
| 20795 } else if (workInProgress.memoizedState !== null) { | |
| 20796 // Something suspended and we should still be in dehydrated mode. | |
| 20797 // Leave the existing child in place. | |
| 20798 workInProgress.child = current.child; // The dehydrated completion pass expects this flag to be there | |
| 20799 // but the normal suspense pass doesn't. | |
| 20800 | |
| 20801 workInProgress.flags |= DidCapture; | |
| 20802 return null; | |
| 20803 } else { | |
| 20804 // Suspended but we should no longer be in dehydrated mode. | |
| 20805 // Therefore we now have to render the fallback. | |
| 20806 var nextPrimaryChildren = nextProps.children; | |
| 20807 var nextFallbackChildren = nextProps.fallback; | |
| 20808 var fallbackChildFragment = mountSuspenseFallbackAfterRetryWithoutHydrating(current, workInProgress, nextPrimaryChildren, nextFallbackChildren, renderLanes); | |
| 20809 var _primaryChildFragment4 = workInProgress.child; | |
| 20810 _primaryChildFragment4.memoizedState = mountSuspenseOffscreenState(renderLanes); | |
| 20811 workInProgress.memoizedState = SUSPENDED_MARKER; | |
| 20812 return fallbackChildFragment; | |
| 20813 } | |
| 20814 } | |
| 20815 } | |
| 20816 | |
| 20817 function scheduleSuspenseWorkOnFiber(fiber, renderLanes, propagationRoot) { | |
| 20818 fiber.lanes = mergeLanes(fiber.lanes, renderLanes); | |
| 20819 var alternate = fiber.alternate; | |
| 20820 | |
| 20821 if (alternate !== null) { | |
| 20822 alternate.lanes = mergeLanes(alternate.lanes, renderLanes); | |
| 20823 } | |
| 20824 | |
| 20825 scheduleContextWorkOnParentPath(fiber.return, renderLanes, propagationRoot); | |
| 20826 } | |
| 20827 | |
| 20828 function propagateSuspenseContextChange(workInProgress, firstChild, renderLanes) { | |
| 20829 // Mark any Suspense boundaries with fallbacks as having work to do. | |
| 20830 // If they were previously forced into fallbacks, they may now be able | |
| 20831 // to unblock. | |
| 20832 var node = firstChild; | |
| 20833 | |
| 20834 while (node !== null) { | |
| 20835 if (node.tag === SuspenseComponent) { | |
| 20836 var state = node.memoizedState; | |
| 20837 | |
| 20838 if (state !== null) { | |
| 20839 scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress); | |
| 20840 } | |
| 20841 } else if (node.tag === SuspenseListComponent) { | |
| 20842 // If the tail is hidden there might not be an Suspense boundaries | |
| 20843 // to schedule work on. In this case we have to schedule it on the | |
| 20844 // list itself. | |
| 20845 // We don't have to traverse to the children of the list since | |
| 20846 // the list will propagate the change when it rerenders. | |
| 20847 scheduleSuspenseWorkOnFiber(node, renderLanes, workInProgress); | |
| 20848 } else if (node.child !== null) { | |
| 20849 node.child.return = node; | |
| 20850 node = node.child; | |
| 20851 continue; | |
| 20852 } | |
| 20853 | |
| 20854 if (node === workInProgress) { | |
| 20855 return; | |
| 20856 } | |
| 20857 | |
| 20858 while (node.sibling === null) { | |
| 20859 if (node.return === null || node.return === workInProgress) { | |
| 20860 return; | |
| 20861 } | |
| 20862 | |
| 20863 node = node.return; | |
| 20864 } | |
| 20865 | |
| 20866 node.sibling.return = node.return; | |
| 20867 node = node.sibling; | |
| 20868 } | |
| 20869 } | |
| 20870 | |
| 20871 function findLastContentRow(firstChild) { | |
| 20872 // This is going to find the last row among these children that is already | |
| 20873 // showing content on the screen, as opposed to being in fallback state or | |
| 20874 // new. If a row has multiple Suspense boundaries, any of them being in the | |
| 20875 // fallback state, counts as the whole row being in a fallback state. | |
| 20876 // Note that the "rows" will be workInProgress, but any nested children | |
| 20877 // will still be current since we haven't rendered them yet. The mounted | |
| 20878 // order may not be the same as the new order. We use the new order. | |
| 20879 var row = firstChild; | |
| 20880 var lastContentRow = null; | |
| 20881 | |
| 20882 while (row !== null) { | |
| 20883 var currentRow = row.alternate; // New rows can't be content rows. | |
| 20884 | |
| 20885 if (currentRow !== null && findFirstSuspended(currentRow) === null) { | |
| 20886 lastContentRow = row; | |
| 20887 } | |
| 20888 | |
| 20889 row = row.sibling; | |
| 20890 } | |
| 20891 | |
| 20892 return lastContentRow; | |
| 20893 } | |
| 20894 | |
| 20895 function validateRevealOrder(revealOrder) { | |
| 20896 { | |
| 20897 if (revealOrder !== undefined && revealOrder !== 'forwards' && revealOrder !== 'backwards' && revealOrder !== 'together' && !didWarnAboutRevealOrder[revealOrder]) { | |
| 20898 didWarnAboutRevealOrder[revealOrder] = true; | |
| 20899 | |
| 20900 if (typeof revealOrder === 'string') { | |
| 20901 switch (revealOrder.toLowerCase()) { | |
| 20902 case 'together': | |
| 20903 case 'forwards': | |
| 20904 case 'backwards': | |
| 20905 { | |
| 20906 error('"%s" is not a valid value for revealOrder on <SuspenseList />. ' + 'Use lowercase "%s" instead.', revealOrder, revealOrder.toLowerCase()); | |
| 20907 | |
| 20908 break; | |
| 20909 } | |
| 20910 | |
| 20911 case 'forward': | |
| 20912 case 'backward': | |
| 20913 { | |
| 20914 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()); | |
| 20915 | |
| 20916 break; | |
| 20917 } | |
| 20918 | |
| 20919 default: | |
| 20920 error('"%s" is not a supported revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder); | |
| 20921 | |
| 20922 break; | |
| 20923 } | |
| 20924 } else { | |
| 20925 error('%s is not a supported value for revealOrder on <SuspenseList />. ' + 'Did you mean "together", "forwards" or "backwards"?', revealOrder); | |
| 20926 } | |
| 20927 } | |
| 20928 } | |
| 20929 } | |
| 20930 | |
| 20931 function validateTailOptions(tailMode, revealOrder) { | |
| 20932 { | |
| 20933 if (tailMode !== undefined && !didWarnAboutTailOptions[tailMode]) { | |
| 20934 if (tailMode !== 'collapsed' && tailMode !== 'hidden') { | |
| 20935 didWarnAboutTailOptions[tailMode] = true; | |
| 20936 | |
| 20937 error('"%s" is not a supported value for tail on <SuspenseList />. ' + 'Did you mean "collapsed" or "hidden"?', tailMode); | |
| 20938 } else if (revealOrder !== 'forwards' && revealOrder !== 'backwards') { | |
| 20939 didWarnAboutTailOptions[tailMode] = true; | |
| 20940 | |
| 20941 error('<SuspenseList tail="%s" /> is only valid if revealOrder is ' + '"forwards" or "backwards". ' + 'Did you mean to specify revealOrder="forwards"?', tailMode); | |
| 20942 } | |
| 20943 } | |
| 20944 } | |
| 20945 } | |
| 20946 | |
| 20947 function validateSuspenseListNestedChild(childSlot, index) { | |
| 20948 { | |
| 20949 var isAnArray = isArray(childSlot); | |
| 20950 var isIterable = !isAnArray && typeof getIteratorFn(childSlot) === 'function'; | |
| 20951 | |
| 20952 if (isAnArray || isIterable) { | |
| 20953 var type = isAnArray ? 'array' : 'iterable'; | |
| 20954 | |
| 20955 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); | |
| 20956 | |
| 20957 return false; | |
| 20958 } | |
| 20959 } | |
| 20960 | |
| 20961 return true; | |
| 20962 } | |
| 20963 | |
| 20964 function validateSuspenseListChildren(children, revealOrder) { | |
| 20965 { | |
| 20966 if ((revealOrder === 'forwards' || revealOrder === 'backwards') && children !== undefined && children !== null && children !== false) { | |
| 20967 if (isArray(children)) { | |
| 20968 for (var i = 0; i < children.length; i++) { | |
| 20969 if (!validateSuspenseListNestedChild(children[i], i)) { | |
| 20970 return; | |
| 20971 } | |
| 20972 } | |
| 20973 } else { | |
| 20974 var iteratorFn = getIteratorFn(children); | |
| 20975 | |
| 20976 if (typeof iteratorFn === 'function') { | |
| 20977 var childrenIterator = iteratorFn.call(children); | |
| 20978 | |
| 20979 if (childrenIterator) { | |
| 20980 var step = childrenIterator.next(); | |
| 20981 var _i = 0; | |
| 20982 | |
| 20983 for (; !step.done; step = childrenIterator.next()) { | |
| 20984 if (!validateSuspenseListNestedChild(step.value, _i)) { | |
| 20985 return; | |
| 20986 } | |
| 20987 | |
| 20988 _i++; | |
| 20989 } | |
| 20990 } | |
| 20991 } else { | |
| 20992 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); | |
| 20993 } | |
| 20994 } | |
| 20995 } | |
| 20996 } | |
| 20997 } | |
| 20998 | |
| 20999 function initSuspenseListRenderState(workInProgress, isBackwards, tail, lastContentRow, tailMode) { | |
| 21000 var renderState = workInProgress.memoizedState; | |
| 21001 | |
| 21002 if (renderState === null) { | |
| 21003 workInProgress.memoizedState = { | |
| 21004 isBackwards: isBackwards, | |
| 21005 rendering: null, | |
| 21006 renderingStartTime: 0, | |
| 21007 last: lastContentRow, | |
| 21008 tail: tail, | |
| 21009 tailMode: tailMode | |
| 21010 }; | |
| 21011 } else { | |
| 21012 // We can reuse the existing object from previous renders. | |
| 21013 renderState.isBackwards = isBackwards; | |
| 21014 renderState.rendering = null; | |
| 21015 renderState.renderingStartTime = 0; | |
| 21016 renderState.last = lastContentRow; | |
| 21017 renderState.tail = tail; | |
| 21018 renderState.tailMode = tailMode; | |
| 21019 } | |
| 21020 } // This can end up rendering this component multiple passes. | |
| 21021 // The first pass splits the children fibers into two sets. A head and tail. | |
| 21022 // We first render the head. If anything is in fallback state, we do another | |
| 21023 // pass through beginWork to rerender all children (including the tail) with | |
| 21024 // the force suspend context. If the first render didn't have anything in | |
| 21025 // in fallback state. Then we render each row in the tail one-by-one. | |
| 21026 // That happens in the completeWork phase without going back to beginWork. | |
| 21027 | |
| 21028 | |
| 21029 function updateSuspenseListComponent(current, workInProgress, renderLanes) { | |
| 21030 var nextProps = workInProgress.pendingProps; | |
| 21031 var revealOrder = nextProps.revealOrder; | |
| 21032 var tailMode = nextProps.tail; | |
| 21033 var newChildren = nextProps.children; | |
| 21034 validateRevealOrder(revealOrder); | |
| 21035 validateTailOptions(tailMode, revealOrder); | |
| 21036 validateSuspenseListChildren(newChildren, revealOrder); | |
| 21037 reconcileChildren(current, workInProgress, newChildren, renderLanes); | |
| 21038 var suspenseContext = suspenseStackCursor.current; | |
| 21039 var shouldForceFallback = hasSuspenseContext(suspenseContext, ForceSuspenseFallback); | |
| 21040 | |
| 21041 if (shouldForceFallback) { | |
| 21042 suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback); | |
| 21043 workInProgress.flags |= DidCapture; | |
| 21044 } else { | |
| 21045 var didSuspendBefore = current !== null && (current.flags & DidCapture) !== NoFlags; | |
| 21046 | |
| 21047 if (didSuspendBefore) { | |
| 21048 // If we previously forced a fallback, we need to schedule work | |
| 21049 // on any nested boundaries to let them know to try to render | |
| 21050 // again. This is the same as context updating. | |
| 21051 propagateSuspenseContextChange(workInProgress, workInProgress.child, renderLanes); | |
| 21052 } | |
| 21053 | |
| 21054 suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); | |
| 21055 } | |
| 21056 | |
| 21057 pushSuspenseContext(workInProgress, suspenseContext); | |
| 21058 | |
| 21059 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 21060 // In legacy mode, SuspenseList doesn't work so we just | |
| 21061 // use make it a noop by treating it as the default revealOrder. | |
| 21062 workInProgress.memoizedState = null; | |
| 21063 } else { | |
| 21064 switch (revealOrder) { | |
| 21065 case 'forwards': | |
| 21066 { | |
| 21067 var lastContentRow = findLastContentRow(workInProgress.child); | |
| 21068 var tail; | |
| 21069 | |
| 21070 if (lastContentRow === null) { | |
| 21071 // The whole list is part of the tail. | |
| 21072 // TODO: We could fast path by just rendering the tail now. | |
| 21073 tail = workInProgress.child; | |
| 21074 workInProgress.child = null; | |
| 21075 } else { | |
| 21076 // Disconnect the tail rows after the content row. | |
| 21077 // We're going to render them separately later. | |
| 21078 tail = lastContentRow.sibling; | |
| 21079 lastContentRow.sibling = null; | |
| 21080 } | |
| 21081 | |
| 21082 initSuspenseListRenderState(workInProgress, false, // isBackwards | |
| 21083 tail, lastContentRow, tailMode); | |
| 21084 break; | |
| 21085 } | |
| 21086 | |
| 21087 case 'backwards': | |
| 21088 { | |
| 21089 // We're going to find the first row that has existing content. | |
| 21090 // At the same time we're going to reverse the list of everything | |
| 21091 // we pass in the meantime. That's going to be our tail in reverse | |
| 21092 // order. | |
| 21093 var _tail = null; | |
| 21094 var row = workInProgress.child; | |
| 21095 workInProgress.child = null; | |
| 21096 | |
| 21097 while (row !== null) { | |
| 21098 var currentRow = row.alternate; // New rows can't be content rows. | |
| 21099 | |
| 21100 if (currentRow !== null && findFirstSuspended(currentRow) === null) { | |
| 21101 // This is the beginning of the main content. | |
| 21102 workInProgress.child = row; | |
| 21103 break; | |
| 21104 } | |
| 21105 | |
| 21106 var nextRow = row.sibling; | |
| 21107 row.sibling = _tail; | |
| 21108 _tail = row; | |
| 21109 row = nextRow; | |
| 21110 } // TODO: If workInProgress.child is null, we can continue on the tail immediately. | |
| 21111 | |
| 21112 | |
| 21113 initSuspenseListRenderState(workInProgress, true, // isBackwards | |
| 21114 _tail, null, // last | |
| 21115 tailMode); | |
| 21116 break; | |
| 21117 } | |
| 21118 | |
| 21119 case 'together': | |
| 21120 { | |
| 21121 initSuspenseListRenderState(workInProgress, false, // isBackwards | |
| 21122 null, // tail | |
| 21123 null, // last | |
| 21124 undefined); | |
| 21125 break; | |
| 21126 } | |
| 21127 | |
| 21128 default: | |
| 21129 { | |
| 21130 // The default reveal order is the same as not having | |
| 21131 // a boundary. | |
| 21132 workInProgress.memoizedState = null; | |
| 21133 } | |
| 21134 } | |
| 21135 } | |
| 21136 | |
| 21137 return workInProgress.child; | |
| 21138 } | |
| 21139 | |
| 21140 function updatePortalComponent(current, workInProgress, renderLanes) { | |
| 21141 pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); | |
| 21142 var nextChildren = workInProgress.pendingProps; | |
| 21143 | |
| 21144 if (current === null) { | |
| 21145 // Portals are special because we don't append the children during mount | |
| 21146 // but at commit. Therefore we need to track insertions which the normal | |
| 21147 // flow doesn't do during mount. This doesn't happen at the root because | |
| 21148 // the root always starts with a "current" with a null child. | |
| 21149 // TODO: Consider unifying this with how the root works. | |
| 21150 workInProgress.child = reconcileChildFibers(workInProgress, null, nextChildren, renderLanes); | |
| 21151 } else { | |
| 21152 reconcileChildren(current, workInProgress, nextChildren, renderLanes); | |
| 21153 } | |
| 21154 | |
| 21155 return workInProgress.child; | |
| 21156 } | |
| 21157 | |
| 21158 var hasWarnedAboutUsingNoValuePropOnContextProvider = false; | |
| 21159 | |
| 21160 function updateContextProvider(current, workInProgress, renderLanes) { | |
| 21161 var providerType = workInProgress.type; | |
| 21162 var context = providerType._context; | |
| 21163 var newProps = workInProgress.pendingProps; | |
| 21164 var oldProps = workInProgress.memoizedProps; | |
| 21165 var newValue = newProps.value; | |
| 21166 | |
| 21167 { | |
| 21168 if (!('value' in newProps)) { | |
| 21169 if (!hasWarnedAboutUsingNoValuePropOnContextProvider) { | |
| 21170 hasWarnedAboutUsingNoValuePropOnContextProvider = true; | |
| 21171 | |
| 21172 error('The `value` prop is required for the `<Context.Provider>`. Did you misspell it or forget to pass it?'); | |
| 21173 } | |
| 21174 } | |
| 21175 | |
| 21176 var providerPropTypes = workInProgress.type.propTypes; | |
| 21177 | |
| 21178 if (providerPropTypes) { | |
| 21179 checkPropTypes(providerPropTypes, newProps, 'prop', 'Context.Provider'); | |
| 21180 } | |
| 21181 } | |
| 21182 | |
| 21183 pushProvider(workInProgress, context, newValue); | |
| 21184 | |
| 21185 { | |
| 21186 if (oldProps !== null) { | |
| 21187 var oldValue = oldProps.value; | |
| 21188 | |
| 21189 if (objectIs(oldValue, newValue)) { | |
| 21190 // No change. Bailout early if children are the same. | |
| 21191 if (oldProps.children === newProps.children && !hasContextChanged()) { | |
| 21192 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 21193 } | |
| 21194 } else { | |
| 21195 // The context value changed. Search for matching consumers and schedule | |
| 21196 // them to update. | |
| 21197 propagateContextChange(workInProgress, context, renderLanes); | |
| 21198 } | |
| 21199 } | |
| 21200 } | |
| 21201 | |
| 21202 var newChildren = newProps.children; | |
| 21203 reconcileChildren(current, workInProgress, newChildren, renderLanes); | |
| 21204 return workInProgress.child; | |
| 21205 } | |
| 21206 | |
| 21207 var hasWarnedAboutUsingContextAsConsumer = false; | |
| 21208 | |
| 21209 function updateContextConsumer(current, workInProgress, renderLanes) { | |
| 21210 var context = workInProgress.type; // The logic below for Context differs depending on PROD or DEV mode. In | |
| 21211 // DEV mode, we create a separate object for Context.Consumer that acts | |
| 21212 // like a proxy to Context. This proxy object adds unnecessary code in PROD | |
| 21213 // so we use the old behaviour (Context.Consumer references Context) to | |
| 21214 // reduce size and overhead. The separate object references context via | |
| 21215 // a property called "_context", which also gives us the ability to check | |
| 21216 // in DEV mode if this property exists or not and warn if it does not. | |
| 21217 | |
| 21218 { | |
| 21219 if (context._context === undefined) { | |
| 21220 // This may be because it's a Context (rather than a Consumer). | |
| 21221 // Or it may be because it's older React where they're the same thing. | |
| 21222 // We only want to warn if we're sure it's a new React. | |
| 21223 if (context !== context.Consumer) { | |
| 21224 if (!hasWarnedAboutUsingContextAsConsumer) { | |
| 21225 hasWarnedAboutUsingContextAsConsumer = true; | |
| 21226 | |
| 21227 error('Rendering <Context> directly is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?'); | |
| 21228 } | |
| 21229 } | |
| 21230 } else { | |
| 21231 context = context._context; | |
| 21232 } | |
| 21233 } | |
| 21234 | |
| 21235 var newProps = workInProgress.pendingProps; | |
| 21236 var render = newProps.children; | |
| 21237 | |
| 21238 { | |
| 21239 if (typeof render !== 'function') { | |
| 21240 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.'); | |
| 21241 } | |
| 21242 } | |
| 21243 | |
| 21244 prepareToReadContext(workInProgress, renderLanes); | |
| 21245 var newValue = readContext(context); | |
| 21246 | |
| 21247 { | |
| 21248 markComponentRenderStarted(workInProgress); | |
| 21249 } | |
| 21250 | |
| 21251 var newChildren; | |
| 21252 | |
| 21253 { | |
| 21254 ReactCurrentOwner$1.current = workInProgress; | |
| 21255 setIsRendering(true); | |
| 21256 newChildren = render(newValue); | |
| 21257 setIsRendering(false); | |
| 21258 } | |
| 21259 | |
| 21260 { | |
| 21261 markComponentRenderStopped(); | |
| 21262 } // React DevTools reads this flag. | |
| 21263 | |
| 21264 | |
| 21265 workInProgress.flags |= PerformedWork; | |
| 21266 reconcileChildren(current, workInProgress, newChildren, renderLanes); | |
| 21267 return workInProgress.child; | |
| 21268 } | |
| 21269 | |
| 21270 function markWorkInProgressReceivedUpdate() { | |
| 21271 didReceiveUpdate = true; | |
| 21272 } | |
| 21273 | |
| 21274 function resetSuspendedCurrentOnMountInLegacyMode(current, workInProgress) { | |
| 21275 if ((workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 21276 if (current !== null) { | |
| 21277 // A lazy component only mounts if it suspended inside a non- | |
| 21278 // concurrent tree, in an inconsistent state. We want to treat it like | |
| 21279 // a new mount, even though an empty version of it already committed. | |
| 21280 // Disconnect the alternate pointers. | |
| 21281 current.alternate = null; | |
| 21282 workInProgress.alternate = null; // Since this is conceptually a new fiber, schedule a Placement effect | |
| 21283 | |
| 21284 workInProgress.flags |= Placement; | |
| 21285 } | |
| 21286 } | |
| 21287 } | |
| 21288 | |
| 21289 function bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes) { | |
| 21290 if (current !== null) { | |
| 21291 // Reuse previous dependencies | |
| 21292 workInProgress.dependencies = current.dependencies; | |
| 21293 } | |
| 21294 | |
| 21295 { | |
| 21296 // Don't update "base" render times for bailouts. | |
| 21297 stopProfilerTimerIfRunning(); | |
| 21298 } | |
| 21299 | |
| 21300 markSkippedUpdateLanes(workInProgress.lanes); // Check if the children have any pending work. | |
| 21301 | |
| 21302 if (!includesSomeLane(renderLanes, workInProgress.childLanes)) { | |
| 21303 // The children don't have any work either. We can skip them. | |
| 21304 // TODO: Once we add back resuming, we should check if the children are | |
| 21305 // a work-in-progress set. If so, we need to transfer their effects. | |
| 21306 { | |
| 21307 return null; | |
| 21308 } | |
| 21309 } // This fiber doesn't have work, but its subtree does. Clone the child | |
| 21310 // fibers and continue. | |
| 21311 | |
| 21312 | |
| 21313 cloneChildFibers(current, workInProgress); | |
| 21314 return workInProgress.child; | |
| 21315 } | |
| 21316 | |
| 21317 function remountFiber(current, oldWorkInProgress, newWorkInProgress) { | |
| 21318 { | |
| 21319 var returnFiber = oldWorkInProgress.return; | |
| 21320 | |
| 21321 if (returnFiber === null) { | |
| 21322 // eslint-disable-next-line react-internal/prod-error-codes | |
| 21323 throw new Error('Cannot swap the root fiber.'); | |
| 21324 } // Disconnect from the old current. | |
| 21325 // It will get deleted. | |
| 21326 | |
| 21327 | |
| 21328 current.alternate = null; | |
| 21329 oldWorkInProgress.alternate = null; // Connect to the new tree. | |
| 21330 | |
| 21331 newWorkInProgress.index = oldWorkInProgress.index; | |
| 21332 newWorkInProgress.sibling = oldWorkInProgress.sibling; | |
| 21333 newWorkInProgress.return = oldWorkInProgress.return; | |
| 21334 newWorkInProgress.ref = oldWorkInProgress.ref; // Replace the child/sibling pointers above it. | |
| 21335 | |
| 21336 if (oldWorkInProgress === returnFiber.child) { | |
| 21337 returnFiber.child = newWorkInProgress; | |
| 21338 } else { | |
| 21339 var prevSibling = returnFiber.child; | |
| 21340 | |
| 21341 if (prevSibling === null) { | |
| 21342 // eslint-disable-next-line react-internal/prod-error-codes | |
| 21343 throw new Error('Expected parent to have a child.'); | |
| 21344 } | |
| 21345 | |
| 21346 while (prevSibling.sibling !== oldWorkInProgress) { | |
| 21347 prevSibling = prevSibling.sibling; | |
| 21348 | |
| 21349 if (prevSibling === null) { | |
| 21350 // eslint-disable-next-line react-internal/prod-error-codes | |
| 21351 throw new Error('Expected to find the previous sibling.'); | |
| 21352 } | |
| 21353 } | |
| 21354 | |
| 21355 prevSibling.sibling = newWorkInProgress; | |
| 21356 } // Delete the old fiber and place the new one. | |
| 21357 // Since the old fiber is disconnected, we have to schedule it manually. | |
| 21358 | |
| 21359 | |
| 21360 var deletions = returnFiber.deletions; | |
| 21361 | |
| 21362 if (deletions === null) { | |
| 21363 returnFiber.deletions = [current]; | |
| 21364 returnFiber.flags |= ChildDeletion; | |
| 21365 } else { | |
| 21366 deletions.push(current); | |
| 21367 } | |
| 21368 | |
| 21369 newWorkInProgress.flags |= Placement; // Restart work from the new fiber. | |
| 21370 | |
| 21371 return newWorkInProgress; | |
| 21372 } | |
| 21373 } | |
| 21374 | |
| 21375 function checkScheduledUpdateOrContext(current, renderLanes) { | |
| 21376 // Before performing an early bailout, we must check if there are pending | |
| 21377 // updates or context. | |
| 21378 var updateLanes = current.lanes; | |
| 21379 | |
| 21380 if (includesSomeLane(updateLanes, renderLanes)) { | |
| 21381 return true; | |
| 21382 } // No pending update, but because context is propagated lazily, we need | |
| 21383 | |
| 21384 return false; | |
| 21385 } | |
| 21386 | |
| 21387 function attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes) { | |
| 21388 // This fiber does not have any pending work. Bailout without entering | |
| 21389 // the begin phase. There's still some bookkeeping we that needs to be done | |
| 21390 // in this optimized path, mostly pushing stuff onto the stack. | |
| 21391 switch (workInProgress.tag) { | |
| 21392 case HostRoot: | |
| 21393 pushHostRootContext(workInProgress); | |
| 21394 var root = workInProgress.stateNode; | |
| 21395 | |
| 21396 resetHydrationState(); | |
| 21397 break; | |
| 21398 | |
| 21399 case HostComponent: | |
| 21400 pushHostContext(workInProgress); | |
| 21401 break; | |
| 21402 | |
| 21403 case ClassComponent: | |
| 21404 { | |
| 21405 var Component = workInProgress.type; | |
| 21406 | |
| 21407 if (isContextProvider(Component)) { | |
| 21408 pushContextProvider(workInProgress); | |
| 21409 } | |
| 21410 | |
| 21411 break; | |
| 21412 } | |
| 21413 | |
| 21414 case HostPortal: | |
| 21415 pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo); | |
| 21416 break; | |
| 21417 | |
| 21418 case ContextProvider: | |
| 21419 { | |
| 21420 var newValue = workInProgress.memoizedProps.value; | |
| 21421 var context = workInProgress.type._context; | |
| 21422 pushProvider(workInProgress, context, newValue); | |
| 21423 break; | |
| 21424 } | |
| 21425 | |
| 21426 case Profiler: | |
| 21427 { | |
| 21428 // Profiler should only call onRender when one of its descendants actually rendered. | |
| 21429 var hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes); | |
| 21430 | |
| 21431 if (hasChildWork) { | |
| 21432 workInProgress.flags |= Update; | |
| 21433 } | |
| 21434 | |
| 21435 { | |
| 21436 // Reset effect durations for the next eventual effect phase. | |
| 21437 // These are reset during render to allow the DevTools commit hook a chance to read them, | |
| 21438 var stateNode = workInProgress.stateNode; | |
| 21439 stateNode.effectDuration = 0; | |
| 21440 stateNode.passiveEffectDuration = 0; | |
| 21441 } | |
| 21442 } | |
| 21443 | |
| 21444 break; | |
| 21445 | |
| 21446 case SuspenseComponent: | |
| 21447 { | |
| 21448 var state = workInProgress.memoizedState; | |
| 21449 | |
| 21450 if (state !== null) { | |
| 21451 if (state.dehydrated !== null) { | |
| 21452 pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // We know that this component will suspend again because if it has | |
| 21453 // been unsuspended it has committed as a resolved Suspense component. | |
| 21454 // If it needs to be retried, it should have work scheduled on it. | |
| 21455 | |
| 21456 workInProgress.flags |= DidCapture; // We should never render the children of a dehydrated boundary until we | |
| 21457 // upgrade it. We return null instead of bailoutOnAlreadyFinishedWork. | |
| 21458 | |
| 21459 return null; | |
| 21460 } // If this boundary is currently timed out, we need to decide | |
| 21461 // whether to retry the primary children, or to skip over it and | |
| 21462 // go straight to the fallback. Check the priority of the primary | |
| 21463 // child fragment. | |
| 21464 | |
| 21465 | |
| 21466 var primaryChildFragment = workInProgress.child; | |
| 21467 var primaryChildLanes = primaryChildFragment.childLanes; | |
| 21468 | |
| 21469 if (includesSomeLane(renderLanes, primaryChildLanes)) { | |
| 21470 // The primary children have pending work. Use the normal path | |
| 21471 // to attempt to render the primary children again. | |
| 21472 return updateSuspenseComponent(current, workInProgress, renderLanes); | |
| 21473 } else { | |
| 21474 // The primary child fragment does not have pending work marked | |
| 21475 // on it | |
| 21476 pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); // The primary children do not have pending work with sufficient | |
| 21477 // priority. Bailout. | |
| 21478 | |
| 21479 var child = bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 21480 | |
| 21481 if (child !== null) { | |
| 21482 // The fallback children have pending work. Skip over the | |
| 21483 // primary children and work on the fallback. | |
| 21484 return child.sibling; | |
| 21485 } else { | |
| 21486 // Note: We can return `null` here because we already checked | |
| 21487 // whether there were nested context consumers, via the call to | |
| 21488 // `bailoutOnAlreadyFinishedWork` above. | |
| 21489 return null; | |
| 21490 } | |
| 21491 } | |
| 21492 } else { | |
| 21493 pushSuspenseContext(workInProgress, setDefaultShallowSuspenseContext(suspenseStackCursor.current)); | |
| 21494 } | |
| 21495 | |
| 21496 break; | |
| 21497 } | |
| 21498 | |
| 21499 case SuspenseListComponent: | |
| 21500 { | |
| 21501 var didSuspendBefore = (current.flags & DidCapture) !== NoFlags; | |
| 21502 | |
| 21503 var _hasChildWork = includesSomeLane(renderLanes, workInProgress.childLanes); | |
| 21504 | |
| 21505 if (didSuspendBefore) { | |
| 21506 if (_hasChildWork) { | |
| 21507 // If something was in fallback state last time, and we have all the | |
| 21508 // same children then we're still in progressive loading state. | |
| 21509 // Something might get unblocked by state updates or retries in the | |
| 21510 // tree which will affect the tail. So we need to use the normal | |
| 21511 // path to compute the correct tail. | |
| 21512 return updateSuspenseListComponent(current, workInProgress, renderLanes); | |
| 21513 } // If none of the children had any work, that means that none of | |
| 21514 // them got retried so they'll still be blocked in the same way | |
| 21515 // as before. We can fast bail out. | |
| 21516 | |
| 21517 | |
| 21518 workInProgress.flags |= DidCapture; | |
| 21519 } // If nothing suspended before and we're rendering the same children, | |
| 21520 // then the tail doesn't matter. Anything new that suspends will work | |
| 21521 // in the "together" mode, so we can continue from the state we had. | |
| 21522 | |
| 21523 | |
| 21524 var renderState = workInProgress.memoizedState; | |
| 21525 | |
| 21526 if (renderState !== null) { | |
| 21527 // Reset to the "together" mode in case we've started a different | |
| 21528 // update in the past but didn't complete it. | |
| 21529 renderState.rendering = null; | |
| 21530 renderState.tail = null; | |
| 21531 renderState.lastEffect = null; | |
| 21532 } | |
| 21533 | |
| 21534 pushSuspenseContext(workInProgress, suspenseStackCursor.current); | |
| 21535 | |
| 21536 if (_hasChildWork) { | |
| 21537 break; | |
| 21538 } else { | |
| 21539 // If none of the children had any work, that means that none of | |
| 21540 // them got retried so they'll still be blocked in the same way | |
| 21541 // as before. We can fast bail out. | |
| 21542 return null; | |
| 21543 } | |
| 21544 } | |
| 21545 | |
| 21546 case OffscreenComponent: | |
| 21547 case LegacyHiddenComponent: | |
| 21548 { | |
| 21549 // Need to check if the tree still needs to be deferred. This is | |
| 21550 // almost identical to the logic used in the normal update path, | |
| 21551 // so we'll just enter that. The only difference is we'll bail out | |
| 21552 // at the next level instead of this one, because the child props | |
| 21553 // have not changed. Which is fine. | |
| 21554 // TODO: Probably should refactor `beginWork` to split the bailout | |
| 21555 // path from the normal path. I'm tempted to do a labeled break here | |
| 21556 // but I won't :) | |
| 21557 workInProgress.lanes = NoLanes; | |
| 21558 return updateOffscreenComponent(current, workInProgress, renderLanes); | |
| 21559 } | |
| 21560 } | |
| 21561 | |
| 21562 return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes); | |
| 21563 } | |
| 21564 | |
| 21565 function beginWork(current, workInProgress, renderLanes) { | |
| 21566 { | |
| 21567 if (workInProgress._debugNeedsRemount && current !== null) { | |
| 21568 // This will restart the begin phase with a new fiber. | |
| 21569 return remountFiber(current, workInProgress, createFiberFromTypeAndProps(workInProgress.type, workInProgress.key, workInProgress.pendingProps, workInProgress._debugOwner || null, workInProgress.mode, workInProgress.lanes)); | |
| 21570 } | |
| 21571 } | |
| 21572 | |
| 21573 if (current !== null) { | |
| 21574 var oldProps = current.memoizedProps; | |
| 21575 var newProps = workInProgress.pendingProps; | |
| 21576 | |
| 21577 if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload: | |
| 21578 workInProgress.type !== current.type )) { | |
| 21579 // If props or context changed, mark the fiber as having performed work. | |
| 21580 // This may be unset if the props are determined to be equal later (memo). | |
| 21581 didReceiveUpdate = true; | |
| 21582 } else { | |
| 21583 // Neither props nor legacy context changes. Check if there's a pending | |
| 21584 // update or context change. | |
| 21585 var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes); | |
| 21586 | |
| 21587 if (!hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there | |
| 21588 // may not be work scheduled on `current`, so we check for this flag. | |
| 21589 (workInProgress.flags & DidCapture) === NoFlags) { | |
| 21590 // No pending updates or context. Bail out now. | |
| 21591 didReceiveUpdate = false; | |
| 21592 return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes); | |
| 21593 } | |
| 21594 | |
| 21595 if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) { | |
| 21596 // This is a special case that only exists for legacy mode. | |
| 21597 // See https://github.com/facebook/react/pull/19216. | |
| 21598 didReceiveUpdate = true; | |
| 21599 } else { | |
| 21600 // An update was scheduled on this fiber, but there are no new props | |
| 21601 // nor legacy context. Set this to false. If an update queue or context | |
| 21602 // consumer produces a changed value, it will set this to true. Otherwise, | |
| 21603 // the component will assume the children have not changed and bail out. | |
| 21604 didReceiveUpdate = false; | |
| 21605 } | |
| 21606 } | |
| 21607 } else { | |
| 21608 didReceiveUpdate = false; | |
| 21609 | |
| 21610 if (getIsHydrating() && isForkedChild(workInProgress)) { | |
| 21611 // Check if this child belongs to a list of muliple children in | |
| 21612 // its parent. | |
| 21613 // | |
| 21614 // In a true multi-threaded implementation, we would render children on | |
| 21615 // parallel threads. This would represent the beginning of a new render | |
| 21616 // thread for this subtree. | |
| 21617 // | |
| 21618 // We only use this for id generation during hydration, which is why the | |
| 21619 // logic is located in this special branch. | |
| 21620 var slotIndex = workInProgress.index; | |
| 21621 var numberOfForks = getForksAtLevel(); | |
| 21622 pushTreeId(workInProgress, numberOfForks, slotIndex); | |
| 21623 } | |
| 21624 } // Before entering the begin phase, clear pending update priority. | |
| 21625 // TODO: This assumes that we're about to evaluate the component and process | |
| 21626 // the update queue. However, there's an exception: SimpleMemoComponent | |
| 21627 // sometimes bails out later in the begin phase. This indicates that we should | |
| 21628 // move this assignment out of the common path and into each branch. | |
| 21629 | |
| 21630 | |
| 21631 workInProgress.lanes = NoLanes; | |
| 21632 | |
| 21633 switch (workInProgress.tag) { | |
| 21634 case IndeterminateComponent: | |
| 21635 { | |
| 21636 return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes); | |
| 21637 } | |
| 21638 | |
| 21639 case LazyComponent: | |
| 21640 { | |
| 21641 var elementType = workInProgress.elementType; | |
| 21642 return mountLazyComponent(current, workInProgress, elementType, renderLanes); | |
| 21643 } | |
| 21644 | |
| 21645 case FunctionComponent: | |
| 21646 { | |
| 21647 var Component = workInProgress.type; | |
| 21648 var unresolvedProps = workInProgress.pendingProps; | |
| 21649 var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps); | |
| 21650 return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes); | |
| 21651 } | |
| 21652 | |
| 21653 case ClassComponent: | |
| 21654 { | |
| 21655 var _Component = workInProgress.type; | |
| 21656 var _unresolvedProps = workInProgress.pendingProps; | |
| 21657 | |
| 21658 var _resolvedProps = workInProgress.elementType === _Component ? _unresolvedProps : resolveDefaultProps(_Component, _unresolvedProps); | |
| 21659 | |
| 21660 return updateClassComponent(current, workInProgress, _Component, _resolvedProps, renderLanes); | |
| 21661 } | |
| 21662 | |
| 21663 case HostRoot: | |
| 21664 return updateHostRoot(current, workInProgress, renderLanes); | |
| 21665 | |
| 21666 case HostComponent: | |
| 21667 return updateHostComponent(current, workInProgress, renderLanes); | |
| 21668 | |
| 21669 case HostText: | |
| 21670 return updateHostText(current, workInProgress); | |
| 21671 | |
| 21672 case SuspenseComponent: | |
| 21673 return updateSuspenseComponent(current, workInProgress, renderLanes); | |
| 21674 | |
| 21675 case HostPortal: | |
| 21676 return updatePortalComponent(current, workInProgress, renderLanes); | |
| 21677 | |
| 21678 case ForwardRef: | |
| 21679 { | |
| 21680 var type = workInProgress.type; | |
| 21681 var _unresolvedProps2 = workInProgress.pendingProps; | |
| 21682 | |
| 21683 var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2); | |
| 21684 | |
| 21685 return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes); | |
| 21686 } | |
| 21687 | |
| 21688 case Fragment: | |
| 21689 return updateFragment(current, workInProgress, renderLanes); | |
| 21690 | |
| 21691 case Mode: | |
| 21692 return updateMode(current, workInProgress, renderLanes); | |
| 21693 | |
| 21694 case Profiler: | |
| 21695 return updateProfiler(current, workInProgress, renderLanes); | |
| 21696 | |
| 21697 case ContextProvider: | |
| 21698 return updateContextProvider(current, workInProgress, renderLanes); | |
| 21699 | |
| 21700 case ContextConsumer: | |
| 21701 return updateContextConsumer(current, workInProgress, renderLanes); | |
| 21702 | |
| 21703 case MemoComponent: | |
| 21704 { | |
| 21705 var _type2 = workInProgress.type; | |
| 21706 var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props. | |
| 21707 | |
| 21708 var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3); | |
| 21709 | |
| 21710 { | |
| 21711 if (workInProgress.type !== workInProgress.elementType) { | |
| 21712 var outerPropTypes = _type2.propTypes; | |
| 21713 | |
| 21714 if (outerPropTypes) { | |
| 21715 checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only | |
| 21716 'prop', getComponentNameFromType(_type2)); | |
| 21717 } | |
| 21718 } | |
| 21719 } | |
| 21720 | |
| 21721 _resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3); | |
| 21722 return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, renderLanes); | |
| 21723 } | |
| 21724 | |
| 21725 case SimpleMemoComponent: | |
| 21726 { | |
| 21727 return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, renderLanes); | |
| 21728 } | |
| 21729 | |
| 21730 case IncompleteClassComponent: | |
| 21731 { | |
| 21732 var _Component2 = workInProgress.type; | |
| 21733 var _unresolvedProps4 = workInProgress.pendingProps; | |
| 21734 | |
| 21735 var _resolvedProps4 = workInProgress.elementType === _Component2 ? _unresolvedProps4 : resolveDefaultProps(_Component2, _unresolvedProps4); | |
| 21736 | |
| 21737 return mountIncompleteClassComponent(current, workInProgress, _Component2, _resolvedProps4, renderLanes); | |
| 21738 } | |
| 21739 | |
| 21740 case SuspenseListComponent: | |
| 21741 { | |
| 21742 return updateSuspenseListComponent(current, workInProgress, renderLanes); | |
| 21743 } | |
| 21744 | |
| 21745 case ScopeComponent: | |
| 21746 { | |
| 21747 | |
| 21748 break; | |
| 21749 } | |
| 21750 | |
| 21751 case OffscreenComponent: | |
| 21752 { | |
| 21753 return updateOffscreenComponent(current, workInProgress, renderLanes); | |
| 21754 } | |
| 21755 } | |
| 21756 | |
| 21757 throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.'); | |
| 21758 } | |
| 21759 | |
| 21760 function markUpdate(workInProgress) { | |
| 21761 // Tag the fiber with an update effect. This turns a Placement into | |
| 21762 // a PlacementAndUpdate. | |
| 21763 workInProgress.flags |= Update; | |
| 21764 } | |
| 21765 | |
| 21766 function markRef$1(workInProgress) { | |
| 21767 workInProgress.flags |= Ref; | |
| 21768 | |
| 21769 { | |
| 21770 workInProgress.flags |= RefStatic; | |
| 21771 } | |
| 21772 } | |
| 21773 | |
| 21774 var appendAllChildren; | |
| 21775 var updateHostContainer; | |
| 21776 var updateHostComponent$1; | |
| 21777 var updateHostText$1; | |
| 21778 | |
| 21779 { | |
| 21780 // Mutation mode | |
| 21781 appendAllChildren = function (parent, workInProgress, needsVisibilityToggle, isHidden) { | |
| 21782 // We only have the top Fiber that was created but we need recurse down its | |
| 21783 // children to find all the terminal nodes. | |
| 21784 var node = workInProgress.child; | |
| 21785 | |
| 21786 while (node !== null) { | |
| 21787 if (node.tag === HostComponent || node.tag === HostText) { | |
| 21788 appendInitialChild(parent, node.stateNode); | |
| 21789 } else if (node.tag === HostPortal) ; else if (node.child !== null) { | |
| 21790 node.child.return = node; | |
| 21791 node = node.child; | |
| 21792 continue; | |
| 21793 } | |
| 21794 | |
| 21795 if (node === workInProgress) { | |
| 21796 return; | |
| 21797 } | |
| 21798 | |
| 21799 while (node.sibling === null) { | |
| 21800 if (node.return === null || node.return === workInProgress) { | |
| 21801 return; | |
| 21802 } | |
| 21803 | |
| 21804 node = node.return; | |
| 21805 } | |
| 21806 | |
| 21807 node.sibling.return = node.return; | |
| 21808 node = node.sibling; | |
| 21809 } | |
| 21810 }; | |
| 21811 | |
| 21812 updateHostContainer = function (current, workInProgress) {// Noop | |
| 21813 }; | |
| 21814 | |
| 21815 updateHostComponent$1 = function (current, workInProgress, type, newProps, rootContainerInstance) { | |
| 21816 // If we have an alternate, that means this is an update and we need to | |
| 21817 // schedule a side-effect to do the updates. | |
| 21818 var oldProps = current.memoizedProps; | |
| 21819 | |
| 21820 if (oldProps === newProps) { | |
| 21821 // In mutation mode, this is sufficient for a bailout because | |
| 21822 // we won't touch this node even if children changed. | |
| 21823 return; | |
| 21824 } // If we get updated because one of our children updated, we don't | |
| 21825 // have newProps so we'll have to reuse them. | |
| 21826 // TODO: Split the update API as separate for the props vs. children. | |
| 21827 // Even better would be if children weren't special cased at all tho. | |
| 21828 | |
| 21829 | |
| 21830 var instance = workInProgress.stateNode; | |
| 21831 var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host | |
| 21832 // component is hitting the resume path. Figure out why. Possibly | |
| 21833 // related to `hidden`. | |
| 21834 | |
| 21835 var updatePayload = prepareUpdate(instance, type, oldProps, newProps, rootContainerInstance, currentHostContext); // TODO: Type this specific to this type of component. | |
| 21836 | |
| 21837 workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there | |
| 21838 // is a new ref we mark this as an update. All the work is done in commitWork. | |
| 21839 | |
| 21840 if (updatePayload) { | |
| 21841 markUpdate(workInProgress); | |
| 21842 } | |
| 21843 }; | |
| 21844 | |
| 21845 updateHostText$1 = function (current, workInProgress, oldText, newText) { | |
| 21846 // If the text differs, mark it as an update. All the work in done in commitWork. | |
| 21847 if (oldText !== newText) { | |
| 21848 markUpdate(workInProgress); | |
| 21849 } | |
| 21850 }; | |
| 21851 } | |
| 21852 | |
| 21853 function cutOffTailIfNeeded(renderState, hasRenderedATailFallback) { | |
| 21854 if (getIsHydrating()) { | |
| 21855 // If we're hydrating, we should consume as many items as we can | |
| 21856 // so we don't leave any behind. | |
| 21857 return; | |
| 21858 } | |
| 21859 | |
| 21860 switch (renderState.tailMode) { | |
| 21861 case 'hidden': | |
| 21862 { | |
| 21863 // Any insertions at the end of the tail list after this point | |
| 21864 // should be invisible. If there are already mounted boundaries | |
| 21865 // anything before them are not considered for collapsing. | |
| 21866 // Therefore we need to go through the whole tail to find if | |
| 21867 // there are any. | |
| 21868 var tailNode = renderState.tail; | |
| 21869 var lastTailNode = null; | |
| 21870 | |
| 21871 while (tailNode !== null) { | |
| 21872 if (tailNode.alternate !== null) { | |
| 21873 lastTailNode = tailNode; | |
| 21874 } | |
| 21875 | |
| 21876 tailNode = tailNode.sibling; | |
| 21877 } // Next we're simply going to delete all insertions after the | |
| 21878 // last rendered item. | |
| 21879 | |
| 21880 | |
| 21881 if (lastTailNode === null) { | |
| 21882 // All remaining items in the tail are insertions. | |
| 21883 renderState.tail = null; | |
| 21884 } else { | |
| 21885 // Detach the insertion after the last node that was already | |
| 21886 // inserted. | |
| 21887 lastTailNode.sibling = null; | |
| 21888 } | |
| 21889 | |
| 21890 break; | |
| 21891 } | |
| 21892 | |
| 21893 case 'collapsed': | |
| 21894 { | |
| 21895 // Any insertions at the end of the tail list after this point | |
| 21896 // should be invisible. If there are already mounted boundaries | |
| 21897 // anything before them are not considered for collapsing. | |
| 21898 // Therefore we need to go through the whole tail to find if | |
| 21899 // there are any. | |
| 21900 var _tailNode = renderState.tail; | |
| 21901 var _lastTailNode = null; | |
| 21902 | |
| 21903 while (_tailNode !== null) { | |
| 21904 if (_tailNode.alternate !== null) { | |
| 21905 _lastTailNode = _tailNode; | |
| 21906 } | |
| 21907 | |
| 21908 _tailNode = _tailNode.sibling; | |
| 21909 } // Next we're simply going to delete all insertions after the | |
| 21910 // last rendered item. | |
| 21911 | |
| 21912 | |
| 21913 if (_lastTailNode === null) { | |
| 21914 // All remaining items in the tail are insertions. | |
| 21915 if (!hasRenderedATailFallback && renderState.tail !== null) { | |
| 21916 // We suspended during the head. We want to show at least one | |
| 21917 // row at the tail. So we'll keep on and cut off the rest. | |
| 21918 renderState.tail.sibling = null; | |
| 21919 } else { | |
| 21920 renderState.tail = null; | |
| 21921 } | |
| 21922 } else { | |
| 21923 // Detach the insertion after the last node that was already | |
| 21924 // inserted. | |
| 21925 _lastTailNode.sibling = null; | |
| 21926 } | |
| 21927 | |
| 21928 break; | |
| 21929 } | |
| 21930 } | |
| 21931 } | |
| 21932 | |
| 21933 function bubbleProperties(completedWork) { | |
| 21934 var didBailout = completedWork.alternate !== null && completedWork.alternate.child === completedWork.child; | |
| 21935 var newChildLanes = NoLanes; | |
| 21936 var subtreeFlags = NoFlags; | |
| 21937 | |
| 21938 if (!didBailout) { | |
| 21939 // Bubble up the earliest expiration time. | |
| 21940 if ( (completedWork.mode & ProfileMode) !== NoMode) { | |
| 21941 // In profiling mode, resetChildExpirationTime is also used to reset | |
| 21942 // profiler durations. | |
| 21943 var actualDuration = completedWork.actualDuration; | |
| 21944 var treeBaseDuration = completedWork.selfBaseDuration; | |
| 21945 var child = completedWork.child; | |
| 21946 | |
| 21947 while (child !== null) { | |
| 21948 newChildLanes = mergeLanes(newChildLanes, mergeLanes(child.lanes, child.childLanes)); | |
| 21949 subtreeFlags |= child.subtreeFlags; | |
| 21950 subtreeFlags |= child.flags; // When a fiber is cloned, its actualDuration is reset to 0. This value will | |
| 21951 // only be updated if work is done on the fiber (i.e. it doesn't bailout). | |
| 21952 // When work is done, it should bubble to the parent's actualDuration. If | |
| 21953 // the fiber has not been cloned though, (meaning no work was done), then | |
| 21954 // this value will reflect the amount of time spent working on a previous | |
| 21955 // render. In that case it should not bubble. We determine whether it was | |
| 21956 // cloned by comparing the child pointer. | |
| 21957 | |
| 21958 actualDuration += child.actualDuration; | |
| 21959 treeBaseDuration += child.treeBaseDuration; | |
| 21960 child = child.sibling; | |
| 21961 } | |
| 21962 | |
| 21963 completedWork.actualDuration = actualDuration; | |
| 21964 completedWork.treeBaseDuration = treeBaseDuration; | |
| 21965 } else { | |
| 21966 var _child = completedWork.child; | |
| 21967 | |
| 21968 while (_child !== null) { | |
| 21969 newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child.lanes, _child.childLanes)); | |
| 21970 subtreeFlags |= _child.subtreeFlags; | |
| 21971 subtreeFlags |= _child.flags; // Update the return pointer so the tree is consistent. This is a code | |
| 21972 // smell because it assumes the commit phase is never concurrent with | |
| 21973 // the render phase. Will address during refactor to alternate model. | |
| 21974 | |
| 21975 _child.return = completedWork; | |
| 21976 _child = _child.sibling; | |
| 21977 } | |
| 21978 } | |
| 21979 | |
| 21980 completedWork.subtreeFlags |= subtreeFlags; | |
| 21981 } else { | |
| 21982 // Bubble up the earliest expiration time. | |
| 21983 if ( (completedWork.mode & ProfileMode) !== NoMode) { | |
| 21984 // In profiling mode, resetChildExpirationTime is also used to reset | |
| 21985 // profiler durations. | |
| 21986 var _treeBaseDuration = completedWork.selfBaseDuration; | |
| 21987 var _child2 = completedWork.child; | |
| 21988 | |
| 21989 while (_child2 !== null) { | |
| 21990 newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child2.lanes, _child2.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to, | |
| 21991 // so we should bubble those up even during a bailout. All the other | |
| 21992 // flags have a lifetime only of a single render + commit, so we should | |
| 21993 // ignore them. | |
| 21994 | |
| 21995 subtreeFlags |= _child2.subtreeFlags & StaticMask; | |
| 21996 subtreeFlags |= _child2.flags & StaticMask; | |
| 21997 _treeBaseDuration += _child2.treeBaseDuration; | |
| 21998 _child2 = _child2.sibling; | |
| 21999 } | |
| 22000 | |
| 22001 completedWork.treeBaseDuration = _treeBaseDuration; | |
| 22002 } else { | |
| 22003 var _child3 = completedWork.child; | |
| 22004 | |
| 22005 while (_child3 !== null) { | |
| 22006 newChildLanes = mergeLanes(newChildLanes, mergeLanes(_child3.lanes, _child3.childLanes)); // "Static" flags share the lifetime of the fiber/hook they belong to, | |
| 22007 // so we should bubble those up even during a bailout. All the other | |
| 22008 // flags have a lifetime only of a single render + commit, so we should | |
| 22009 // ignore them. | |
| 22010 | |
| 22011 subtreeFlags |= _child3.subtreeFlags & StaticMask; | |
| 22012 subtreeFlags |= _child3.flags & StaticMask; // Update the return pointer so the tree is consistent. This is a code | |
| 22013 // smell because it assumes the commit phase is never concurrent with | |
| 22014 // the render phase. Will address during refactor to alternate model. | |
| 22015 | |
| 22016 _child3.return = completedWork; | |
| 22017 _child3 = _child3.sibling; | |
| 22018 } | |
| 22019 } | |
| 22020 | |
| 22021 completedWork.subtreeFlags |= subtreeFlags; | |
| 22022 } | |
| 22023 | |
| 22024 completedWork.childLanes = newChildLanes; | |
| 22025 return didBailout; | |
| 22026 } | |
| 22027 | |
| 22028 function completeDehydratedSuspenseBoundary(current, workInProgress, nextState) { | |
| 22029 if (hasUnhydratedTailNodes() && (workInProgress.mode & ConcurrentMode) !== NoMode && (workInProgress.flags & DidCapture) === NoFlags) { | |
| 22030 warnIfUnhydratedTailNodes(workInProgress); | |
| 22031 resetHydrationState(); | |
| 22032 workInProgress.flags |= ForceClientRender | Incomplete | ShouldCapture; | |
| 22033 return false; | |
| 22034 } | |
| 22035 | |
| 22036 var wasHydrated = popHydrationState(workInProgress); | |
| 22037 | |
| 22038 if (nextState !== null && nextState.dehydrated !== null) { | |
| 22039 // We might be inside a hydration state the first time we're picking up this | |
| 22040 // Suspense boundary, and also after we've reentered it for further hydration. | |
| 22041 if (current === null) { | |
| 22042 if (!wasHydrated) { | |
| 22043 throw new Error('A dehydrated suspense component was completed without a hydrated node. ' + 'This is probably a bug in React.'); | |
| 22044 } | |
| 22045 | |
| 22046 prepareToHydrateHostSuspenseInstance(workInProgress); | |
| 22047 bubbleProperties(workInProgress); | |
| 22048 | |
| 22049 { | |
| 22050 if ((workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22051 var isTimedOutSuspense = nextState !== null; | |
| 22052 | |
| 22053 if (isTimedOutSuspense) { | |
| 22054 // Don't count time spent in a timed out Suspense subtree as part of the base duration. | |
| 22055 var primaryChildFragment = workInProgress.child; | |
| 22056 | |
| 22057 if (primaryChildFragment !== null) { | |
| 22058 // $FlowFixMe Flow doesn't support type casting in combination with the -= operator | |
| 22059 workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration; | |
| 22060 } | |
| 22061 } | |
| 22062 } | |
| 22063 } | |
| 22064 | |
| 22065 return false; | |
| 22066 } else { | |
| 22067 // We might have reentered this boundary to hydrate it. If so, we need to reset the hydration | |
| 22068 // state since we're now exiting out of it. popHydrationState doesn't do that for us. | |
| 22069 resetHydrationState(); | |
| 22070 | |
| 22071 if ((workInProgress.flags & DidCapture) === NoFlags) { | |
| 22072 // This boundary did not suspend so it's now hydrated and unsuspended. | |
| 22073 workInProgress.memoizedState = null; | |
| 22074 } // If nothing suspended, we need to schedule an effect to mark this boundary | |
| 22075 // as having hydrated so events know that they're free to be invoked. | |
| 22076 // It's also a signal to replay events and the suspense callback. | |
| 22077 // If something suspended, schedule an effect to attach retry listeners. | |
| 22078 // So we might as well always mark this. | |
| 22079 | |
| 22080 | |
| 22081 workInProgress.flags |= Update; | |
| 22082 bubbleProperties(workInProgress); | |
| 22083 | |
| 22084 { | |
| 22085 if ((workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22086 var _isTimedOutSuspense = nextState !== null; | |
| 22087 | |
| 22088 if (_isTimedOutSuspense) { | |
| 22089 // Don't count time spent in a timed out Suspense subtree as part of the base duration. | |
| 22090 var _primaryChildFragment = workInProgress.child; | |
| 22091 | |
| 22092 if (_primaryChildFragment !== null) { | |
| 22093 // $FlowFixMe Flow doesn't support type casting in combination with the -= operator | |
| 22094 workInProgress.treeBaseDuration -= _primaryChildFragment.treeBaseDuration; | |
| 22095 } | |
| 22096 } | |
| 22097 } | |
| 22098 } | |
| 22099 | |
| 22100 return false; | |
| 22101 } | |
| 22102 } else { | |
| 22103 // Successfully completed this tree. If this was a forced client render, | |
| 22104 // there may have been recoverable errors during first hydration | |
| 22105 // attempt. If so, add them to a queue so we can log them in the | |
| 22106 // commit phase. | |
| 22107 upgradeHydrationErrorsToRecoverable(); // Fall through to normal Suspense path | |
| 22108 | |
| 22109 return true; | |
| 22110 } | |
| 22111 } | |
| 22112 | |
| 22113 function completeWork(current, workInProgress, renderLanes) { | |
| 22114 var newProps = workInProgress.pendingProps; // Note: This intentionally doesn't check if we're hydrating because comparing | |
| 22115 // to the current tree provider fiber is just as fast and less error-prone. | |
| 22116 // Ideally we would have a special version of the work loop only | |
| 22117 // for hydration. | |
| 22118 | |
| 22119 popTreeContext(workInProgress); | |
| 22120 | |
| 22121 switch (workInProgress.tag) { | |
| 22122 case IndeterminateComponent: | |
| 22123 case LazyComponent: | |
| 22124 case SimpleMemoComponent: | |
| 22125 case FunctionComponent: | |
| 22126 case ForwardRef: | |
| 22127 case Fragment: | |
| 22128 case Mode: | |
| 22129 case Profiler: | |
| 22130 case ContextConsumer: | |
| 22131 case MemoComponent: | |
| 22132 bubbleProperties(workInProgress); | |
| 22133 return null; | |
| 22134 | |
| 22135 case ClassComponent: | |
| 22136 { | |
| 22137 var Component = workInProgress.type; | |
| 22138 | |
| 22139 if (isContextProvider(Component)) { | |
| 22140 popContext(workInProgress); | |
| 22141 } | |
| 22142 | |
| 22143 bubbleProperties(workInProgress); | |
| 22144 return null; | |
| 22145 } | |
| 22146 | |
| 22147 case HostRoot: | |
| 22148 { | |
| 22149 var fiberRoot = workInProgress.stateNode; | |
| 22150 popHostContainer(workInProgress); | |
| 22151 popTopLevelContextObject(workInProgress); | |
| 22152 resetWorkInProgressVersions(); | |
| 22153 | |
| 22154 if (fiberRoot.pendingContext) { | |
| 22155 fiberRoot.context = fiberRoot.pendingContext; | |
| 22156 fiberRoot.pendingContext = null; | |
| 22157 } | |
| 22158 | |
| 22159 if (current === null || current.child === null) { | |
| 22160 // If we hydrated, pop so that we can delete any remaining children | |
| 22161 // that weren't hydrated. | |
| 22162 var wasHydrated = popHydrationState(workInProgress); | |
| 22163 | |
| 22164 if (wasHydrated) { | |
| 22165 // If we hydrated, then we'll need to schedule an update for | |
| 22166 // the commit side-effects on the root. | |
| 22167 markUpdate(workInProgress); | |
| 22168 } else { | |
| 22169 if (current !== null) { | |
| 22170 var prevState = current.memoizedState; | |
| 22171 | |
| 22172 if ( // Check if this is a client root | |
| 22173 !prevState.isDehydrated || // Check if we reverted to client rendering (e.g. due to an error) | |
| 22174 (workInProgress.flags & ForceClientRender) !== NoFlags) { | |
| 22175 // Schedule an effect to clear this container at the start of the | |
| 22176 // next commit. This handles the case of React rendering into a | |
| 22177 // container with previous children. It's also safe to do for | |
| 22178 // updates too, because current.child would only be null if the | |
| 22179 // previous render was null (so the container would already | |
| 22180 // be empty). | |
| 22181 workInProgress.flags |= Snapshot; // If this was a forced client render, there may have been | |
| 22182 // recoverable errors during first hydration attempt. If so, add | |
| 22183 // them to a queue so we can log them in the commit phase. | |
| 22184 | |
| 22185 upgradeHydrationErrorsToRecoverable(); | |
| 22186 } | |
| 22187 } | |
| 22188 } | |
| 22189 } | |
| 22190 | |
| 22191 updateHostContainer(current, workInProgress); | |
| 22192 bubbleProperties(workInProgress); | |
| 22193 | |
| 22194 return null; | |
| 22195 } | |
| 22196 | |
| 22197 case HostComponent: | |
| 22198 { | |
| 22199 popHostContext(workInProgress); | |
| 22200 var rootContainerInstance = getRootHostContainer(); | |
| 22201 var type = workInProgress.type; | |
| 22202 | |
| 22203 if (current !== null && workInProgress.stateNode != null) { | |
| 22204 updateHostComponent$1(current, workInProgress, type, newProps, rootContainerInstance); | |
| 22205 | |
| 22206 if (current.ref !== workInProgress.ref) { | |
| 22207 markRef$1(workInProgress); | |
| 22208 } | |
| 22209 } else { | |
| 22210 if (!newProps) { | |
| 22211 if (workInProgress.stateNode === null) { | |
| 22212 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.'); | |
| 22213 } // This can happen when we abort work. | |
| 22214 | |
| 22215 | |
| 22216 bubbleProperties(workInProgress); | |
| 22217 return null; | |
| 22218 } | |
| 22219 | |
| 22220 var currentHostContext = getHostContext(); // TODO: Move createInstance to beginWork and keep it on a context | |
| 22221 // "stack" as the parent. Then append children as we go in beginWork | |
| 22222 // or completeWork depending on whether we want to add them top->down or | |
| 22223 // bottom->up. Top->down is faster in IE11. | |
| 22224 | |
| 22225 var _wasHydrated = popHydrationState(workInProgress); | |
| 22226 | |
| 22227 if (_wasHydrated) { | |
| 22228 // TODO: Move this and createInstance step into the beginPhase | |
| 22229 // to consolidate. | |
| 22230 if (prepareToHydrateHostInstance(workInProgress, rootContainerInstance, currentHostContext)) { | |
| 22231 // If changes to the hydrated node need to be applied at the | |
| 22232 // commit-phase we mark this as such. | |
| 22233 markUpdate(workInProgress); | |
| 22234 } | |
| 22235 } else { | |
| 22236 var instance = createInstance(type, newProps, rootContainerInstance, currentHostContext, workInProgress); | |
| 22237 appendAllChildren(instance, workInProgress, false, false); | |
| 22238 workInProgress.stateNode = instance; // Certain renderers require commit-time effects for initial mount. | |
| 22239 // (eg DOM renderer supports auto-focus for certain elements). | |
| 22240 // Make sure such renderers get scheduled for later work. | |
| 22241 | |
| 22242 if (finalizeInitialChildren(instance, type, newProps, rootContainerInstance)) { | |
| 22243 markUpdate(workInProgress); | |
| 22244 } | |
| 22245 } | |
| 22246 | |
| 22247 if (workInProgress.ref !== null) { | |
| 22248 // If there is a ref on a host node we need to schedule a callback | |
| 22249 markRef$1(workInProgress); | |
| 22250 } | |
| 22251 } | |
| 22252 | |
| 22253 bubbleProperties(workInProgress); | |
| 22254 return null; | |
| 22255 } | |
| 22256 | |
| 22257 case HostText: | |
| 22258 { | |
| 22259 var newText = newProps; | |
| 22260 | |
| 22261 if (current && workInProgress.stateNode != null) { | |
| 22262 var oldText = current.memoizedProps; // If we have an alternate, that means this is an update and we need | |
| 22263 // to schedule a side-effect to do the updates. | |
| 22264 | |
| 22265 updateHostText$1(current, workInProgress, oldText, newText); | |
| 22266 } else { | |
| 22267 if (typeof newText !== 'string') { | |
| 22268 if (workInProgress.stateNode === null) { | |
| 22269 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.'); | |
| 22270 } // This can happen when we abort work. | |
| 22271 | |
| 22272 } | |
| 22273 | |
| 22274 var _rootContainerInstance = getRootHostContainer(); | |
| 22275 | |
| 22276 var _currentHostContext = getHostContext(); | |
| 22277 | |
| 22278 var _wasHydrated2 = popHydrationState(workInProgress); | |
| 22279 | |
| 22280 if (_wasHydrated2) { | |
| 22281 if (prepareToHydrateHostTextInstance(workInProgress)) { | |
| 22282 markUpdate(workInProgress); | |
| 22283 } | |
| 22284 } else { | |
| 22285 workInProgress.stateNode = createTextInstance(newText, _rootContainerInstance, _currentHostContext, workInProgress); | |
| 22286 } | |
| 22287 } | |
| 22288 | |
| 22289 bubbleProperties(workInProgress); | |
| 22290 return null; | |
| 22291 } | |
| 22292 | |
| 22293 case SuspenseComponent: | |
| 22294 { | |
| 22295 popSuspenseContext(workInProgress); | |
| 22296 var nextState = workInProgress.memoizedState; // Special path for dehydrated boundaries. We may eventually move this | |
| 22297 // to its own fiber type so that we can add other kinds of hydration | |
| 22298 // boundaries that aren't associated with a Suspense tree. In anticipation | |
| 22299 // of such a refactor, all the hydration logic is contained in | |
| 22300 // this branch. | |
| 22301 | |
| 22302 if (current === null || current.memoizedState !== null && current.memoizedState.dehydrated !== null) { | |
| 22303 var fallthroughToNormalSuspensePath = completeDehydratedSuspenseBoundary(current, workInProgress, nextState); | |
| 22304 | |
| 22305 if (!fallthroughToNormalSuspensePath) { | |
| 22306 if (workInProgress.flags & ShouldCapture) { | |
| 22307 // Special case. There were remaining unhydrated nodes. We treat | |
| 22308 // this as a mismatch. Revert to client rendering. | |
| 22309 return workInProgress; | |
| 22310 } else { | |
| 22311 // Did not finish hydrating, either because this is the initial | |
| 22312 // render or because something suspended. | |
| 22313 return null; | |
| 22314 } | |
| 22315 } // Continue with the normal Suspense path. | |
| 22316 | |
| 22317 } | |
| 22318 | |
| 22319 if ((workInProgress.flags & DidCapture) !== NoFlags) { | |
| 22320 // Something suspended. Re-render with the fallback children. | |
| 22321 workInProgress.lanes = renderLanes; // Do not reset the effect list. | |
| 22322 | |
| 22323 if ( (workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22324 transferActualDuration(workInProgress); | |
| 22325 } // Don't bubble properties in this case. | |
| 22326 | |
| 22327 | |
| 22328 return workInProgress; | |
| 22329 } | |
| 22330 | |
| 22331 var nextDidTimeout = nextState !== null; | |
| 22332 var prevDidTimeout = current !== null && current.memoizedState !== null; | |
| 22333 // a passive effect, which is when we process the transitions | |
| 22334 | |
| 22335 | |
| 22336 if (nextDidTimeout !== prevDidTimeout) { | |
| 22337 // an effect to toggle the subtree's visibility. When we switch from | |
| 22338 // fallback -> primary, the inner Offscreen fiber schedules this effect | |
| 22339 // as part of its normal complete phase. But when we switch from | |
| 22340 // primary -> fallback, the inner Offscreen fiber does not have a complete | |
| 22341 // phase. So we need to schedule its effect here. | |
| 22342 // | |
| 22343 // We also use this flag to connect/disconnect the effects, but the same | |
| 22344 // logic applies: when re-connecting, the Offscreen fiber's complete | |
| 22345 // phase will handle scheduling the effect. It's only when the fallback | |
| 22346 // is active that we have to do anything special. | |
| 22347 | |
| 22348 | |
| 22349 if (nextDidTimeout) { | |
| 22350 var _offscreenFiber2 = workInProgress.child; | |
| 22351 _offscreenFiber2.flags |= Visibility; // TODO: This will still suspend a synchronous tree if anything | |
| 22352 // in the concurrent tree already suspended during this render. | |
| 22353 // This is a known bug. | |
| 22354 | |
| 22355 if ((workInProgress.mode & ConcurrentMode) !== NoMode) { | |
| 22356 // TODO: Move this back to throwException because this is too late | |
| 22357 // if this is a large tree which is common for initial loads. We | |
| 22358 // don't know if we should restart a render or not until we get | |
| 22359 // this marker, and this is too late. | |
| 22360 // If this render already had a ping or lower pri updates, | |
| 22361 // and this is the first time we know we're going to suspend we | |
| 22362 // should be able to immediately restart from within throwException. | |
| 22363 var hasInvisibleChildContext = current === null && (workInProgress.memoizedProps.unstable_avoidThisFallback !== true || !enableSuspenseAvoidThisFallback); | |
| 22364 | |
| 22365 if (hasInvisibleChildContext || hasSuspenseContext(suspenseStackCursor.current, InvisibleParentSuspenseContext)) { | |
| 22366 // If this was in an invisible tree or a new render, then showing | |
| 22367 // this boundary is ok. | |
| 22368 renderDidSuspend(); | |
| 22369 } else { | |
| 22370 // Otherwise, we're going to have to hide content so we should | |
| 22371 // suspend for longer if possible. | |
| 22372 renderDidSuspendDelayIfPossible(); | |
| 22373 } | |
| 22374 } | |
| 22375 } | |
| 22376 } | |
| 22377 | |
| 22378 var wakeables = workInProgress.updateQueue; | |
| 22379 | |
| 22380 if (wakeables !== null) { | |
| 22381 // Schedule an effect to attach a retry listener to the promise. | |
| 22382 // TODO: Move to passive phase | |
| 22383 workInProgress.flags |= Update; | |
| 22384 } | |
| 22385 | |
| 22386 bubbleProperties(workInProgress); | |
| 22387 | |
| 22388 { | |
| 22389 if ((workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22390 if (nextDidTimeout) { | |
| 22391 // Don't count time spent in a timed out Suspense subtree as part of the base duration. | |
| 22392 var primaryChildFragment = workInProgress.child; | |
| 22393 | |
| 22394 if (primaryChildFragment !== null) { | |
| 22395 // $FlowFixMe Flow doesn't support type casting in combination with the -= operator | |
| 22396 workInProgress.treeBaseDuration -= primaryChildFragment.treeBaseDuration; | |
| 22397 } | |
| 22398 } | |
| 22399 } | |
| 22400 } | |
| 22401 | |
| 22402 return null; | |
| 22403 } | |
| 22404 | |
| 22405 case HostPortal: | |
| 22406 popHostContainer(workInProgress); | |
| 22407 updateHostContainer(current, workInProgress); | |
| 22408 | |
| 22409 if (current === null) { | |
| 22410 preparePortalMount(workInProgress.stateNode.containerInfo); | |
| 22411 } | |
| 22412 | |
| 22413 bubbleProperties(workInProgress); | |
| 22414 return null; | |
| 22415 | |
| 22416 case ContextProvider: | |
| 22417 // Pop provider fiber | |
| 22418 var context = workInProgress.type._context; | |
| 22419 popProvider(context, workInProgress); | |
| 22420 bubbleProperties(workInProgress); | |
| 22421 return null; | |
| 22422 | |
| 22423 case IncompleteClassComponent: | |
| 22424 { | |
| 22425 // Same as class component case. I put it down here so that the tags are | |
| 22426 // sequential to ensure this switch is compiled to a jump table. | |
| 22427 var _Component = workInProgress.type; | |
| 22428 | |
| 22429 if (isContextProvider(_Component)) { | |
| 22430 popContext(workInProgress); | |
| 22431 } | |
| 22432 | |
| 22433 bubbleProperties(workInProgress); | |
| 22434 return null; | |
| 22435 } | |
| 22436 | |
| 22437 case SuspenseListComponent: | |
| 22438 { | |
| 22439 popSuspenseContext(workInProgress); | |
| 22440 var renderState = workInProgress.memoizedState; | |
| 22441 | |
| 22442 if (renderState === null) { | |
| 22443 // We're running in the default, "independent" mode. | |
| 22444 // We don't do anything in this mode. | |
| 22445 bubbleProperties(workInProgress); | |
| 22446 return null; | |
| 22447 } | |
| 22448 | |
| 22449 var didSuspendAlready = (workInProgress.flags & DidCapture) !== NoFlags; | |
| 22450 var renderedTail = renderState.rendering; | |
| 22451 | |
| 22452 if (renderedTail === null) { | |
| 22453 // We just rendered the head. | |
| 22454 if (!didSuspendAlready) { | |
| 22455 // This is the first pass. We need to figure out if anything is still | |
| 22456 // suspended in the rendered set. | |
| 22457 // If new content unsuspended, but there's still some content that | |
| 22458 // didn't. Then we need to do a second pass that forces everything | |
| 22459 // to keep showing their fallbacks. | |
| 22460 // We might be suspended if something in this render pass suspended, or | |
| 22461 // something in the previous committed pass suspended. Otherwise, | |
| 22462 // there's no chance so we can skip the expensive call to | |
| 22463 // findFirstSuspended. | |
| 22464 var cannotBeSuspended = renderHasNotSuspendedYet() && (current === null || (current.flags & DidCapture) === NoFlags); | |
| 22465 | |
| 22466 if (!cannotBeSuspended) { | |
| 22467 var row = workInProgress.child; | |
| 22468 | |
| 22469 while (row !== null) { | |
| 22470 var suspended = findFirstSuspended(row); | |
| 22471 | |
| 22472 if (suspended !== null) { | |
| 22473 didSuspendAlready = true; | |
| 22474 workInProgress.flags |= DidCapture; | |
| 22475 cutOffTailIfNeeded(renderState, false); // If this is a newly suspended tree, it might not get committed as | |
| 22476 // part of the second pass. In that case nothing will subscribe to | |
| 22477 // its thenables. Instead, we'll transfer its thenables to the | |
| 22478 // SuspenseList so that it can retry if they resolve. | |
| 22479 // There might be multiple of these in the list but since we're | |
| 22480 // going to wait for all of them anyway, it doesn't really matter | |
| 22481 // which ones gets to ping. In theory we could get clever and keep | |
| 22482 // track of how many dependencies remain but it gets tricky because | |
| 22483 // in the meantime, we can add/remove/change items and dependencies. | |
| 22484 // We might bail out of the loop before finding any but that | |
| 22485 // doesn't matter since that means that the other boundaries that | |
| 22486 // we did find already has their listeners attached. | |
| 22487 | |
| 22488 var newThenables = suspended.updateQueue; | |
| 22489 | |
| 22490 if (newThenables !== null) { | |
| 22491 workInProgress.updateQueue = newThenables; | |
| 22492 workInProgress.flags |= Update; | |
| 22493 } // Rerender the whole list, but this time, we'll force fallbacks | |
| 22494 // to stay in place. | |
| 22495 // Reset the effect flags before doing the second pass since that's now invalid. | |
| 22496 // Reset the child fibers to their original state. | |
| 22497 | |
| 22498 | |
| 22499 workInProgress.subtreeFlags = NoFlags; | |
| 22500 resetChildFibers(workInProgress, renderLanes); // Set up the Suspense Context to force suspense and immediately | |
| 22501 // rerender the children. | |
| 22502 | |
| 22503 pushSuspenseContext(workInProgress, setShallowSuspenseContext(suspenseStackCursor.current, ForceSuspenseFallback)); // Don't bubble properties in this case. | |
| 22504 | |
| 22505 return workInProgress.child; | |
| 22506 } | |
| 22507 | |
| 22508 row = row.sibling; | |
| 22509 } | |
| 22510 } | |
| 22511 | |
| 22512 if (renderState.tail !== null && now() > getRenderTargetTime()) { | |
| 22513 // We have already passed our CPU deadline but we still have rows | |
| 22514 // left in the tail. We'll just give up further attempts to render | |
| 22515 // the main content and only render fallbacks. | |
| 22516 workInProgress.flags |= DidCapture; | |
| 22517 didSuspendAlready = true; | |
| 22518 cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this | |
| 22519 // to get it started back up to attempt the next item. While in terms | |
| 22520 // of priority this work has the same priority as this current render, | |
| 22521 // it's not part of the same transition once the transition has | |
| 22522 // committed. If it's sync, we still want to yield so that it can be | |
| 22523 // painted. Conceptually, this is really the same as pinging. | |
| 22524 // We can use any RetryLane even if it's the one currently rendering | |
| 22525 // since we're leaving it behind on this node. | |
| 22526 | |
| 22527 workInProgress.lanes = SomeRetryLane; | |
| 22528 } | |
| 22529 } else { | |
| 22530 cutOffTailIfNeeded(renderState, false); | |
| 22531 } // Next we're going to render the tail. | |
| 22532 | |
| 22533 } else { | |
| 22534 // Append the rendered row to the child list. | |
| 22535 if (!didSuspendAlready) { | |
| 22536 var _suspended = findFirstSuspended(renderedTail); | |
| 22537 | |
| 22538 if (_suspended !== null) { | |
| 22539 workInProgress.flags |= DidCapture; | |
| 22540 didSuspendAlready = true; // Ensure we transfer the update queue to the parent so that it doesn't | |
| 22541 // get lost if this row ends up dropped during a second pass. | |
| 22542 | |
| 22543 var _newThenables = _suspended.updateQueue; | |
| 22544 | |
| 22545 if (_newThenables !== null) { | |
| 22546 workInProgress.updateQueue = _newThenables; | |
| 22547 workInProgress.flags |= Update; | |
| 22548 } | |
| 22549 | |
| 22550 cutOffTailIfNeeded(renderState, true); // This might have been modified. | |
| 22551 | |
| 22552 if (renderState.tail === null && renderState.tailMode === 'hidden' && !renderedTail.alternate && !getIsHydrating() // We don't cut it if we're hydrating. | |
| 22553 ) { | |
| 22554 // We're done. | |
| 22555 bubbleProperties(workInProgress); | |
| 22556 return null; | |
| 22557 } | |
| 22558 } else if ( // The time it took to render last row is greater than the remaining | |
| 22559 // time we have to render. So rendering one more row would likely | |
| 22560 // exceed it. | |
| 22561 now() * 2 - renderState.renderingStartTime > getRenderTargetTime() && renderLanes !== OffscreenLane) { | |
| 22562 // We have now passed our CPU deadline and we'll just give up further | |
| 22563 // attempts to render the main content and only render fallbacks. | |
| 22564 // The assumption is that this is usually faster. | |
| 22565 workInProgress.flags |= DidCapture; | |
| 22566 didSuspendAlready = true; | |
| 22567 cutOffTailIfNeeded(renderState, false); // Since nothing actually suspended, there will nothing to ping this | |
| 22568 // to get it started back up to attempt the next item. While in terms | |
| 22569 // of priority this work has the same priority as this current render, | |
| 22570 // it's not part of the same transition once the transition has | |
| 22571 // committed. If it's sync, we still want to yield so that it can be | |
| 22572 // painted. Conceptually, this is really the same as pinging. | |
| 22573 // We can use any RetryLane even if it's the one currently rendering | |
| 22574 // since we're leaving it behind on this node. | |
| 22575 | |
| 22576 workInProgress.lanes = SomeRetryLane; | |
| 22577 } | |
| 22578 } | |
| 22579 | |
| 22580 if (renderState.isBackwards) { | |
| 22581 // The effect list of the backwards tail will have been added | |
| 22582 // to the end. This breaks the guarantee that life-cycles fire in | |
| 22583 // sibling order but that isn't a strong guarantee promised by React. | |
| 22584 // Especially since these might also just pop in during future commits. | |
| 22585 // Append to the beginning of the list. | |
| 22586 renderedTail.sibling = workInProgress.child; | |
| 22587 workInProgress.child = renderedTail; | |
| 22588 } else { | |
| 22589 var previousSibling = renderState.last; | |
| 22590 | |
| 22591 if (previousSibling !== null) { | |
| 22592 previousSibling.sibling = renderedTail; | |
| 22593 } else { | |
| 22594 workInProgress.child = renderedTail; | |
| 22595 } | |
| 22596 | |
| 22597 renderState.last = renderedTail; | |
| 22598 } | |
| 22599 } | |
| 22600 | |
| 22601 if (renderState.tail !== null) { | |
| 22602 // We still have tail rows to render. | |
| 22603 // Pop a row. | |
| 22604 var next = renderState.tail; | |
| 22605 renderState.rendering = next; | |
| 22606 renderState.tail = next.sibling; | |
| 22607 renderState.renderingStartTime = now(); | |
| 22608 next.sibling = null; // Restore the context. | |
| 22609 // TODO: We can probably just avoid popping it instead and only | |
| 22610 // setting it the first time we go from not suspended to suspended. | |
| 22611 | |
| 22612 var suspenseContext = suspenseStackCursor.current; | |
| 22613 | |
| 22614 if (didSuspendAlready) { | |
| 22615 suspenseContext = setShallowSuspenseContext(suspenseContext, ForceSuspenseFallback); | |
| 22616 } else { | |
| 22617 suspenseContext = setDefaultShallowSuspenseContext(suspenseContext); | |
| 22618 } | |
| 22619 | |
| 22620 pushSuspenseContext(workInProgress, suspenseContext); // Do a pass over the next row. | |
| 22621 // Don't bubble properties in this case. | |
| 22622 | |
| 22623 return next; | |
| 22624 } | |
| 22625 | |
| 22626 bubbleProperties(workInProgress); | |
| 22627 return null; | |
| 22628 } | |
| 22629 | |
| 22630 case ScopeComponent: | |
| 22631 { | |
| 22632 | |
| 22633 break; | |
| 22634 } | |
| 22635 | |
| 22636 case OffscreenComponent: | |
| 22637 case LegacyHiddenComponent: | |
| 22638 { | |
| 22639 popRenderLanes(workInProgress); | |
| 22640 var _nextState = workInProgress.memoizedState; | |
| 22641 var nextIsHidden = _nextState !== null; | |
| 22642 | |
| 22643 if (current !== null) { | |
| 22644 var _prevState = current.memoizedState; | |
| 22645 var prevIsHidden = _prevState !== null; | |
| 22646 | |
| 22647 if (prevIsHidden !== nextIsHidden && ( // LegacyHidden doesn't do any hiding — it only pre-renders. | |
| 22648 !enableLegacyHidden )) { | |
| 22649 workInProgress.flags |= Visibility; | |
| 22650 } | |
| 22651 } | |
| 22652 | |
| 22653 if (!nextIsHidden || (workInProgress.mode & ConcurrentMode) === NoMode) { | |
| 22654 bubbleProperties(workInProgress); | |
| 22655 } else { | |
| 22656 // Don't bubble properties for hidden children unless we're rendering | |
| 22657 // at offscreen priority. | |
| 22658 if (includesSomeLane(subtreeRenderLanes, OffscreenLane)) { | |
| 22659 bubbleProperties(workInProgress); | |
| 22660 | |
| 22661 { | |
| 22662 // Check if there was an insertion or update in the hidden subtree. | |
| 22663 // If so, we need to hide those nodes in the commit phase, so | |
| 22664 // schedule a visibility effect. | |
| 22665 if ( workInProgress.subtreeFlags & (Placement | Update)) { | |
| 22666 workInProgress.flags |= Visibility; | |
| 22667 } | |
| 22668 } | |
| 22669 } | |
| 22670 } | |
| 22671 return null; | |
| 22672 } | |
| 22673 | |
| 22674 case CacheComponent: | |
| 22675 { | |
| 22676 | |
| 22677 return null; | |
| 22678 } | |
| 22679 | |
| 22680 case TracingMarkerComponent: | |
| 22681 { | |
| 22682 | |
| 22683 return null; | |
| 22684 } | |
| 22685 } | |
| 22686 | |
| 22687 throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.'); | |
| 22688 } | |
| 22689 | |
| 22690 function unwindWork(current, workInProgress, renderLanes) { | |
| 22691 // Note: This intentionally doesn't check if we're hydrating because comparing | |
| 22692 // to the current tree provider fiber is just as fast and less error-prone. | |
| 22693 // Ideally we would have a special version of the work loop only | |
| 22694 // for hydration. | |
| 22695 popTreeContext(workInProgress); | |
| 22696 | |
| 22697 switch (workInProgress.tag) { | |
| 22698 case ClassComponent: | |
| 22699 { | |
| 22700 var Component = workInProgress.type; | |
| 22701 | |
| 22702 if (isContextProvider(Component)) { | |
| 22703 popContext(workInProgress); | |
| 22704 } | |
| 22705 | |
| 22706 var flags = workInProgress.flags; | |
| 22707 | |
| 22708 if (flags & ShouldCapture) { | |
| 22709 workInProgress.flags = flags & ~ShouldCapture | DidCapture; | |
| 22710 | |
| 22711 if ( (workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22712 transferActualDuration(workInProgress); | |
| 22713 } | |
| 22714 | |
| 22715 return workInProgress; | |
| 22716 } | |
| 22717 | |
| 22718 return null; | |
| 22719 } | |
| 22720 | |
| 22721 case HostRoot: | |
| 22722 { | |
| 22723 var root = workInProgress.stateNode; | |
| 22724 popHostContainer(workInProgress); | |
| 22725 popTopLevelContextObject(workInProgress); | |
| 22726 resetWorkInProgressVersions(); | |
| 22727 var _flags = workInProgress.flags; | |
| 22728 | |
| 22729 if ((_flags & ShouldCapture) !== NoFlags && (_flags & DidCapture) === NoFlags) { | |
| 22730 // There was an error during render that wasn't captured by a suspense | |
| 22731 // boundary. Do a second pass on the root to unmount the children. | |
| 22732 workInProgress.flags = _flags & ~ShouldCapture | DidCapture; | |
| 22733 return workInProgress; | |
| 22734 } // We unwound to the root without completing it. Exit. | |
| 22735 | |
| 22736 | |
| 22737 return null; | |
| 22738 } | |
| 22739 | |
| 22740 case HostComponent: | |
| 22741 { | |
| 22742 // TODO: popHydrationState | |
| 22743 popHostContext(workInProgress); | |
| 22744 return null; | |
| 22745 } | |
| 22746 | |
| 22747 case SuspenseComponent: | |
| 22748 { | |
| 22749 popSuspenseContext(workInProgress); | |
| 22750 var suspenseState = workInProgress.memoizedState; | |
| 22751 | |
| 22752 if (suspenseState !== null && suspenseState.dehydrated !== null) { | |
| 22753 if (workInProgress.alternate === null) { | |
| 22754 throw new Error('Threw in newly mounted dehydrated component. This is likely a bug in ' + 'React. Please file an issue.'); | |
| 22755 } | |
| 22756 | |
| 22757 resetHydrationState(); | |
| 22758 } | |
| 22759 | |
| 22760 var _flags2 = workInProgress.flags; | |
| 22761 | |
| 22762 if (_flags2 & ShouldCapture) { | |
| 22763 workInProgress.flags = _flags2 & ~ShouldCapture | DidCapture; // Captured a suspense effect. Re-render the boundary. | |
| 22764 | |
| 22765 if ( (workInProgress.mode & ProfileMode) !== NoMode) { | |
| 22766 transferActualDuration(workInProgress); | |
| 22767 } | |
| 22768 | |
| 22769 return workInProgress; | |
| 22770 } | |
| 22771 | |
| 22772 return null; | |
| 22773 } | |
| 22774 | |
| 22775 case SuspenseListComponent: | |
| 22776 { | |
| 22777 popSuspenseContext(workInProgress); // SuspenseList doesn't actually catch anything. It should've been | |
| 22778 // caught by a nested boundary. If not, it should bubble through. | |
| 22779 | |
| 22780 return null; | |
| 22781 } | |
| 22782 | |
| 22783 case HostPortal: | |
| 22784 popHostContainer(workInProgress); | |
| 22785 return null; | |
| 22786 | |
| 22787 case ContextProvider: | |
| 22788 var context = workInProgress.type._context; | |
| 22789 popProvider(context, workInProgress); | |
| 22790 return null; | |
| 22791 | |
| 22792 case OffscreenComponent: | |
| 22793 case LegacyHiddenComponent: | |
| 22794 popRenderLanes(workInProgress); | |
| 22795 return null; | |
| 22796 | |
| 22797 case CacheComponent: | |
| 22798 | |
| 22799 return null; | |
| 22800 | |
| 22801 default: | |
| 22802 return null; | |
| 22803 } | |
| 22804 } | |
| 22805 | |
| 22806 function unwindInterruptedWork(current, interruptedWork, renderLanes) { | |
| 22807 // Note: This intentionally doesn't check if we're hydrating because comparing | |
| 22808 // to the current tree provider fiber is just as fast and less error-prone. | |
| 22809 // Ideally we would have a special version of the work loop only | |
| 22810 // for hydration. | |
| 22811 popTreeContext(interruptedWork); | |
| 22812 | |
| 22813 switch (interruptedWork.tag) { | |
| 22814 case ClassComponent: | |
| 22815 { | |
| 22816 var childContextTypes = interruptedWork.type.childContextTypes; | |
| 22817 | |
| 22818 if (childContextTypes !== null && childContextTypes !== undefined) { | |
| 22819 popContext(interruptedWork); | |
| 22820 } | |
| 22821 | |
| 22822 break; | |
| 22823 } | |
| 22824 | |
| 22825 case HostRoot: | |
| 22826 { | |
| 22827 var root = interruptedWork.stateNode; | |
| 22828 popHostContainer(interruptedWork); | |
| 22829 popTopLevelContextObject(interruptedWork); | |
| 22830 resetWorkInProgressVersions(); | |
| 22831 break; | |
| 22832 } | |
| 22833 | |
| 22834 case HostComponent: | |
| 22835 { | |
| 22836 popHostContext(interruptedWork); | |
| 22837 break; | |
| 22838 } | |
| 22839 | |
| 22840 case HostPortal: | |
| 22841 popHostContainer(interruptedWork); | |
| 22842 break; | |
| 22843 | |
| 22844 case SuspenseComponent: | |
| 22845 popSuspenseContext(interruptedWork); | |
| 22846 break; | |
| 22847 | |
| 22848 case SuspenseListComponent: | |
| 22849 popSuspenseContext(interruptedWork); | |
| 22850 break; | |
| 22851 | |
| 22852 case ContextProvider: | |
| 22853 var context = interruptedWork.type._context; | |
| 22854 popProvider(context, interruptedWork); | |
| 22855 break; | |
| 22856 | |
| 22857 case OffscreenComponent: | |
| 22858 case LegacyHiddenComponent: | |
| 22859 popRenderLanes(interruptedWork); | |
| 22860 break; | |
| 22861 } | |
| 22862 } | |
| 22863 | |
| 22864 var didWarnAboutUndefinedSnapshotBeforeUpdate = null; | |
| 22865 | |
| 22866 { | |
| 22867 didWarnAboutUndefinedSnapshotBeforeUpdate = new Set(); | |
| 22868 } // Used during the commit phase to track the state of the Offscreen component stack. | |
| 22869 // Allows us to avoid traversing the return path to find the nearest Offscreen ancestor. | |
| 22870 // Only used when enableSuspenseLayoutEffectSemantics is enabled. | |
| 22871 | |
| 22872 | |
| 22873 var offscreenSubtreeIsHidden = false; | |
| 22874 var offscreenSubtreeWasHidden = false; | |
| 22875 var PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set; | |
| 22876 var nextEffect = null; // Used for Profiling builds to track updaters. | |
| 22877 | |
| 22878 var inProgressLanes = null; | |
| 22879 var inProgressRoot = null; | |
| 22880 function reportUncaughtErrorInDEV(error) { | |
| 22881 // Wrapping each small part of the commit phase into a guarded | |
| 22882 // callback is a bit too slow (https://github.com/facebook/react/pull/21666). | |
| 22883 // But we rely on it to surface errors to DEV tools like overlays | |
| 22884 // (https://github.com/facebook/react/issues/21712). | |
| 22885 // As a compromise, rethrow only caught errors in a guard. | |
| 22886 { | |
| 22887 invokeGuardedCallback(null, function () { | |
| 22888 throw error; | |
| 22889 }); | |
| 22890 clearCaughtError(); | |
| 22891 } | |
| 22892 } | |
| 22893 | |
| 22894 var callComponentWillUnmountWithTimer = function (current, instance) { | |
| 22895 instance.props = current.memoizedProps; | |
| 22896 instance.state = current.memoizedState; | |
| 22897 | |
| 22898 if ( current.mode & ProfileMode) { | |
| 22899 try { | |
| 22900 startLayoutEffectTimer(); | |
| 22901 instance.componentWillUnmount(); | |
| 22902 } finally { | |
| 22903 recordLayoutEffectDuration(current); | |
| 22904 } | |
| 22905 } else { | |
| 22906 instance.componentWillUnmount(); | |
| 22907 } | |
| 22908 }; // Capture errors so they don't interrupt mounting. | |
| 22909 | |
| 22910 | |
| 22911 function safelyCallCommitHookLayoutEffectListMount(current, nearestMountedAncestor) { | |
| 22912 try { | |
| 22913 commitHookEffectListMount(Layout, current); | |
| 22914 } catch (error) { | |
| 22915 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22916 } | |
| 22917 } // Capture errors so they don't interrupt unmounting. | |
| 22918 | |
| 22919 | |
| 22920 function safelyCallComponentWillUnmount(current, nearestMountedAncestor, instance) { | |
| 22921 try { | |
| 22922 callComponentWillUnmountWithTimer(current, instance); | |
| 22923 } catch (error) { | |
| 22924 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22925 } | |
| 22926 } // Capture errors so they don't interrupt mounting. | |
| 22927 | |
| 22928 | |
| 22929 function safelyCallComponentDidMount(current, nearestMountedAncestor, instance) { | |
| 22930 try { | |
| 22931 instance.componentDidMount(); | |
| 22932 } catch (error) { | |
| 22933 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22934 } | |
| 22935 } // Capture errors so they don't interrupt mounting. | |
| 22936 | |
| 22937 | |
| 22938 function safelyAttachRef(current, nearestMountedAncestor) { | |
| 22939 try { | |
| 22940 commitAttachRef(current); | |
| 22941 } catch (error) { | |
| 22942 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22943 } | |
| 22944 } | |
| 22945 | |
| 22946 function safelyDetachRef(current, nearestMountedAncestor) { | |
| 22947 var ref = current.ref; | |
| 22948 | |
| 22949 if (ref !== null) { | |
| 22950 if (typeof ref === 'function') { | |
| 22951 var retVal; | |
| 22952 | |
| 22953 try { | |
| 22954 if (enableProfilerTimer && enableProfilerCommitHooks && current.mode & ProfileMode) { | |
| 22955 try { | |
| 22956 startLayoutEffectTimer(); | |
| 22957 retVal = ref(null); | |
| 22958 } finally { | |
| 22959 recordLayoutEffectDuration(current); | |
| 22960 } | |
| 22961 } else { | |
| 22962 retVal = ref(null); | |
| 22963 } | |
| 22964 } catch (error) { | |
| 22965 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22966 } | |
| 22967 | |
| 22968 { | |
| 22969 if (typeof retVal === 'function') { | |
| 22970 error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(current)); | |
| 22971 } | |
| 22972 } | |
| 22973 } else { | |
| 22974 ref.current = null; | |
| 22975 } | |
| 22976 } | |
| 22977 } | |
| 22978 | |
| 22979 function safelyCallDestroy(current, nearestMountedAncestor, destroy) { | |
| 22980 try { | |
| 22981 destroy(); | |
| 22982 } catch (error) { | |
| 22983 captureCommitPhaseError(current, nearestMountedAncestor, error); | |
| 22984 } | |
| 22985 } | |
| 22986 | |
| 22987 var focusedInstanceHandle = null; | |
| 22988 var shouldFireAfterActiveInstanceBlur = false; | |
| 22989 function commitBeforeMutationEffects(root, firstChild) { | |
| 22990 focusedInstanceHandle = prepareForCommit(root.containerInfo); | |
| 22991 nextEffect = firstChild; | |
| 22992 commitBeforeMutationEffects_begin(); // We no longer need to track the active instance fiber | |
| 22993 | |
| 22994 var shouldFire = shouldFireAfterActiveInstanceBlur; | |
| 22995 shouldFireAfterActiveInstanceBlur = false; | |
| 22996 focusedInstanceHandle = null; | |
| 22997 return shouldFire; | |
| 22998 } | |
| 22999 | |
| 23000 function commitBeforeMutationEffects_begin() { | |
| 23001 while (nextEffect !== null) { | |
| 23002 var fiber = nextEffect; // This phase is only used for beforeActiveInstanceBlur. | |
| 23003 | |
| 23004 var child = fiber.child; | |
| 23005 | |
| 23006 if ((fiber.subtreeFlags & BeforeMutationMask) !== NoFlags && child !== null) { | |
| 23007 child.return = fiber; | |
| 23008 nextEffect = child; | |
| 23009 } else { | |
| 23010 commitBeforeMutationEffects_complete(); | |
| 23011 } | |
| 23012 } | |
| 23013 } | |
| 23014 | |
| 23015 function commitBeforeMutationEffects_complete() { | |
| 23016 while (nextEffect !== null) { | |
| 23017 var fiber = nextEffect; | |
| 23018 setCurrentFiber(fiber); | |
| 23019 | |
| 23020 try { | |
| 23021 commitBeforeMutationEffectsOnFiber(fiber); | |
| 23022 } catch (error) { | |
| 23023 captureCommitPhaseError(fiber, fiber.return, error); | |
| 23024 } | |
| 23025 | |
| 23026 resetCurrentFiber(); | |
| 23027 var sibling = fiber.sibling; | |
| 23028 | |
| 23029 if (sibling !== null) { | |
| 23030 sibling.return = fiber.return; | |
| 23031 nextEffect = sibling; | |
| 23032 return; | |
| 23033 } | |
| 23034 | |
| 23035 nextEffect = fiber.return; | |
| 23036 } | |
| 23037 } | |
| 23038 | |
| 23039 function commitBeforeMutationEffectsOnFiber(finishedWork) { | |
| 23040 var current = finishedWork.alternate; | |
| 23041 var flags = finishedWork.flags; | |
| 23042 | |
| 23043 if ((flags & Snapshot) !== NoFlags) { | |
| 23044 setCurrentFiber(finishedWork); | |
| 23045 | |
| 23046 switch (finishedWork.tag) { | |
| 23047 case FunctionComponent: | |
| 23048 case ForwardRef: | |
| 23049 case SimpleMemoComponent: | |
| 23050 { | |
| 23051 break; | |
| 23052 } | |
| 23053 | |
| 23054 case ClassComponent: | |
| 23055 { | |
| 23056 if (current !== null) { | |
| 23057 var prevProps = current.memoizedProps; | |
| 23058 var prevState = current.memoizedState; | |
| 23059 var instance = finishedWork.stateNode; // We could update instance props and state here, | |
| 23060 // but instead we rely on them being set during last render. | |
| 23061 // TODO: revisit this when we implement resuming. | |
| 23062 | |
| 23063 { | |
| 23064 if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) { | |
| 23065 if (instance.props !== finishedWork.memoizedProps) { | |
| 23066 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'); | |
| 23067 } | |
| 23068 | |
| 23069 if (instance.state !== finishedWork.memoizedState) { | |
| 23070 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'); | |
| 23071 } | |
| 23072 } | |
| 23073 } | |
| 23074 | |
| 23075 var snapshot = instance.getSnapshotBeforeUpdate(finishedWork.elementType === finishedWork.type ? prevProps : resolveDefaultProps(finishedWork.type, prevProps), prevState); | |
| 23076 | |
| 23077 { | |
| 23078 var didWarnSet = didWarnAboutUndefinedSnapshotBeforeUpdate; | |
| 23079 | |
| 23080 if (snapshot === undefined && !didWarnSet.has(finishedWork.type)) { | |
| 23081 didWarnSet.add(finishedWork.type); | |
| 23082 | |
| 23083 error('%s.getSnapshotBeforeUpdate(): A snapshot value (or null) ' + 'must be returned. You have returned undefined.', getComponentNameFromFiber(finishedWork)); | |
| 23084 } | |
| 23085 } | |
| 23086 | |
| 23087 instance.__reactInternalSnapshotBeforeUpdate = snapshot; | |
| 23088 } | |
| 23089 | |
| 23090 break; | |
| 23091 } | |
| 23092 | |
| 23093 case HostRoot: | |
| 23094 { | |
| 23095 { | |
| 23096 var root = finishedWork.stateNode; | |
| 23097 clearContainer(root.containerInfo); | |
| 23098 } | |
| 23099 | |
| 23100 break; | |
| 23101 } | |
| 23102 | |
| 23103 case HostComponent: | |
| 23104 case HostText: | |
| 23105 case HostPortal: | |
| 23106 case IncompleteClassComponent: | |
| 23107 // Nothing to do for these component types | |
| 23108 break; | |
| 23109 | |
| 23110 default: | |
| 23111 { | |
| 23112 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.'); | |
| 23113 } | |
| 23114 } | |
| 23115 | |
| 23116 resetCurrentFiber(); | |
| 23117 } | |
| 23118 } | |
| 23119 | |
| 23120 function commitHookEffectListUnmount(flags, finishedWork, nearestMountedAncestor) { | |
| 23121 var updateQueue = finishedWork.updateQueue; | |
| 23122 var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; | |
| 23123 | |
| 23124 if (lastEffect !== null) { | |
| 23125 var firstEffect = lastEffect.next; | |
| 23126 var effect = firstEffect; | |
| 23127 | |
| 23128 do { | |
| 23129 if ((effect.tag & flags) === flags) { | |
| 23130 // Unmount | |
| 23131 var destroy = effect.destroy; | |
| 23132 effect.destroy = undefined; | |
| 23133 | |
| 23134 if (destroy !== undefined) { | |
| 23135 { | |
| 23136 if ((flags & Passive$1) !== NoFlags$1) { | |
| 23137 markComponentPassiveEffectUnmountStarted(finishedWork); | |
| 23138 } else if ((flags & Layout) !== NoFlags$1) { | |
| 23139 markComponentLayoutEffectUnmountStarted(finishedWork); | |
| 23140 } | |
| 23141 } | |
| 23142 | |
| 23143 { | |
| 23144 if ((flags & Insertion) !== NoFlags$1) { | |
| 23145 setIsRunningInsertionEffect(true); | |
| 23146 } | |
| 23147 } | |
| 23148 | |
| 23149 safelyCallDestroy(finishedWork, nearestMountedAncestor, destroy); | |
| 23150 | |
| 23151 { | |
| 23152 if ((flags & Insertion) !== NoFlags$1) { | |
| 23153 setIsRunningInsertionEffect(false); | |
| 23154 } | |
| 23155 } | |
| 23156 | |
| 23157 { | |
| 23158 if ((flags & Passive$1) !== NoFlags$1) { | |
| 23159 markComponentPassiveEffectUnmountStopped(); | |
| 23160 } else if ((flags & Layout) !== NoFlags$1) { | |
| 23161 markComponentLayoutEffectUnmountStopped(); | |
| 23162 } | |
| 23163 } | |
| 23164 } | |
| 23165 } | |
| 23166 | |
| 23167 effect = effect.next; | |
| 23168 } while (effect !== firstEffect); | |
| 23169 } | |
| 23170 } | |
| 23171 | |
| 23172 function commitHookEffectListMount(flags, finishedWork) { | |
| 23173 var updateQueue = finishedWork.updateQueue; | |
| 23174 var lastEffect = updateQueue !== null ? updateQueue.lastEffect : null; | |
| 23175 | |
| 23176 if (lastEffect !== null) { | |
| 23177 var firstEffect = lastEffect.next; | |
| 23178 var effect = firstEffect; | |
| 23179 | |
| 23180 do { | |
| 23181 if ((effect.tag & flags) === flags) { | |
| 23182 { | |
| 23183 if ((flags & Passive$1) !== NoFlags$1) { | |
| 23184 markComponentPassiveEffectMountStarted(finishedWork); | |
| 23185 } else if ((flags & Layout) !== NoFlags$1) { | |
| 23186 markComponentLayoutEffectMountStarted(finishedWork); | |
| 23187 } | |
| 23188 } // Mount | |
| 23189 | |
| 23190 | |
| 23191 var create = effect.create; | |
| 23192 | |
| 23193 { | |
| 23194 if ((flags & Insertion) !== NoFlags$1) { | |
| 23195 setIsRunningInsertionEffect(true); | |
| 23196 } | |
| 23197 } | |
| 23198 | |
| 23199 effect.destroy = create(); | |
| 23200 | |
| 23201 { | |
| 23202 if ((flags & Insertion) !== NoFlags$1) { | |
| 23203 setIsRunningInsertionEffect(false); | |
| 23204 } | |
| 23205 } | |
| 23206 | |
| 23207 { | |
| 23208 if ((flags & Passive$1) !== NoFlags$1) { | |
| 23209 markComponentPassiveEffectMountStopped(); | |
| 23210 } else if ((flags & Layout) !== NoFlags$1) { | |
| 23211 markComponentLayoutEffectMountStopped(); | |
| 23212 } | |
| 23213 } | |
| 23214 | |
| 23215 { | |
| 23216 var destroy = effect.destroy; | |
| 23217 | |
| 23218 if (destroy !== undefined && typeof destroy !== 'function') { | |
| 23219 var hookName = void 0; | |
| 23220 | |
| 23221 if ((effect.tag & Layout) !== NoFlags) { | |
| 23222 hookName = 'useLayoutEffect'; | |
| 23223 } else if ((effect.tag & Insertion) !== NoFlags) { | |
| 23224 hookName = 'useInsertionEffect'; | |
| 23225 } else { | |
| 23226 hookName = 'useEffect'; | |
| 23227 } | |
| 23228 | |
| 23229 var addendum = void 0; | |
| 23230 | |
| 23231 if (destroy === null) { | |
| 23232 addendum = ' You returned null. If your effect does not require clean ' + 'up, return undefined (or nothing).'; | |
| 23233 } else if (typeof destroy.then === 'function') { | |
| 23234 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'; | |
| 23235 } else { | |
| 23236 addendum = ' You returned: ' + destroy; | |
| 23237 } | |
| 23238 | |
| 23239 error('%s must not return anything besides a function, ' + 'which is used for clean-up.%s', hookName, addendum); | |
| 23240 } | |
| 23241 } | |
| 23242 } | |
| 23243 | |
| 23244 effect = effect.next; | |
| 23245 } while (effect !== firstEffect); | |
| 23246 } | |
| 23247 } | |
| 23248 | |
| 23249 function commitPassiveEffectDurations(finishedRoot, finishedWork) { | |
| 23250 { | |
| 23251 // Only Profilers with work in their subtree will have an Update effect scheduled. | |
| 23252 if ((finishedWork.flags & Update) !== NoFlags) { | |
| 23253 switch (finishedWork.tag) { | |
| 23254 case Profiler: | |
| 23255 { | |
| 23256 var passiveEffectDuration = finishedWork.stateNode.passiveEffectDuration; | |
| 23257 var _finishedWork$memoize = finishedWork.memoizedProps, | |
| 23258 id = _finishedWork$memoize.id, | |
| 23259 onPostCommit = _finishedWork$memoize.onPostCommit; // This value will still reflect the previous commit phase. | |
| 23260 // It does not get reset until the start of the next commit phase. | |
| 23261 | |
| 23262 var commitTime = getCommitTime(); | |
| 23263 var phase = finishedWork.alternate === null ? 'mount' : 'update'; | |
| 23264 | |
| 23265 { | |
| 23266 if (isCurrentUpdateNested()) { | |
| 23267 phase = 'nested-update'; | |
| 23268 } | |
| 23269 } | |
| 23270 | |
| 23271 if (typeof onPostCommit === 'function') { | |
| 23272 onPostCommit(id, phase, passiveEffectDuration, commitTime); | |
| 23273 } // Bubble times to the next nearest ancestor Profiler. | |
| 23274 // After we process that Profiler, we'll bubble further up. | |
| 23275 | |
| 23276 | |
| 23277 var parentFiber = finishedWork.return; | |
| 23278 | |
| 23279 outer: while (parentFiber !== null) { | |
| 23280 switch (parentFiber.tag) { | |
| 23281 case HostRoot: | |
| 23282 var root = parentFiber.stateNode; | |
| 23283 root.passiveEffectDuration += passiveEffectDuration; | |
| 23284 break outer; | |
| 23285 | |
| 23286 case Profiler: | |
| 23287 var parentStateNode = parentFiber.stateNode; | |
| 23288 parentStateNode.passiveEffectDuration += passiveEffectDuration; | |
| 23289 break outer; | |
| 23290 } | |
| 23291 | |
| 23292 parentFiber = parentFiber.return; | |
| 23293 } | |
| 23294 | |
| 23295 break; | |
| 23296 } | |
| 23297 } | |
| 23298 } | |
| 23299 } | |
| 23300 } | |
| 23301 | |
| 23302 function commitLayoutEffectOnFiber(finishedRoot, current, finishedWork, committedLanes) { | |
| 23303 if ((finishedWork.flags & LayoutMask) !== NoFlags) { | |
| 23304 switch (finishedWork.tag) { | |
| 23305 case FunctionComponent: | |
| 23306 case ForwardRef: | |
| 23307 case SimpleMemoComponent: | |
| 23308 { | |
| 23309 if ( !offscreenSubtreeWasHidden) { | |
| 23310 // At this point layout effects have already been destroyed (during mutation phase). | |
| 23311 // This is done to prevent sibling component effects from interfering with each other, | |
| 23312 // e.g. a destroy function in one component should never override a ref set | |
| 23313 // by a create function in another component during the same commit. | |
| 23314 if ( finishedWork.mode & ProfileMode) { | |
| 23315 try { | |
| 23316 startLayoutEffectTimer(); | |
| 23317 commitHookEffectListMount(Layout | HasEffect, finishedWork); | |
| 23318 } finally { | |
| 23319 recordLayoutEffectDuration(finishedWork); | |
| 23320 } | |
| 23321 } else { | |
| 23322 commitHookEffectListMount(Layout | HasEffect, finishedWork); | |
| 23323 } | |
| 23324 } | |
| 23325 | |
| 23326 break; | |
| 23327 } | |
| 23328 | |
| 23329 case ClassComponent: | |
| 23330 { | |
| 23331 var instance = finishedWork.stateNode; | |
| 23332 | |
| 23333 if (finishedWork.flags & Update) { | |
| 23334 if (!offscreenSubtreeWasHidden) { | |
| 23335 if (current === null) { | |
| 23336 // We could update instance props and state here, | |
| 23337 // but instead we rely on them being set during last render. | |
| 23338 // TODO: revisit this when we implement resuming. | |
| 23339 { | |
| 23340 if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) { | |
| 23341 if (instance.props !== finishedWork.memoizedProps) { | |
| 23342 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'); | |
| 23343 } | |
| 23344 | |
| 23345 if (instance.state !== finishedWork.memoizedState) { | |
| 23346 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'); | |
| 23347 } | |
| 23348 } | |
| 23349 } | |
| 23350 | |
| 23351 if ( finishedWork.mode & ProfileMode) { | |
| 23352 try { | |
| 23353 startLayoutEffectTimer(); | |
| 23354 instance.componentDidMount(); | |
| 23355 } finally { | |
| 23356 recordLayoutEffectDuration(finishedWork); | |
| 23357 } | |
| 23358 } else { | |
| 23359 instance.componentDidMount(); | |
| 23360 } | |
| 23361 } else { | |
| 23362 var prevProps = finishedWork.elementType === finishedWork.type ? current.memoizedProps : resolveDefaultProps(finishedWork.type, current.memoizedProps); | |
| 23363 var prevState = current.memoizedState; // We could update instance props and state here, | |
| 23364 // but instead we rely on them being set during last render. | |
| 23365 // TODO: revisit this when we implement resuming. | |
| 23366 | |
| 23367 { | |
| 23368 if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) { | |
| 23369 if (instance.props !== finishedWork.memoizedProps) { | |
| 23370 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'); | |
| 23371 } | |
| 23372 | |
| 23373 if (instance.state !== finishedWork.memoizedState) { | |
| 23374 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'); | |
| 23375 } | |
| 23376 } | |
| 23377 } | |
| 23378 | |
| 23379 if ( finishedWork.mode & ProfileMode) { | |
| 23380 try { | |
| 23381 startLayoutEffectTimer(); | |
| 23382 instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate); | |
| 23383 } finally { | |
| 23384 recordLayoutEffectDuration(finishedWork); | |
| 23385 } | |
| 23386 } else { | |
| 23387 instance.componentDidUpdate(prevProps, prevState, instance.__reactInternalSnapshotBeforeUpdate); | |
| 23388 } | |
| 23389 } | |
| 23390 } | |
| 23391 } // TODO: I think this is now always non-null by the time it reaches the | |
| 23392 // commit phase. Consider removing the type check. | |
| 23393 | |
| 23394 | |
| 23395 var updateQueue = finishedWork.updateQueue; | |
| 23396 | |
| 23397 if (updateQueue !== null) { | |
| 23398 { | |
| 23399 if (finishedWork.type === finishedWork.elementType && !didWarnAboutReassigningProps) { | |
| 23400 if (instance.props !== finishedWork.memoizedProps) { | |
| 23401 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'); | |
| 23402 } | |
| 23403 | |
| 23404 if (instance.state !== finishedWork.memoizedState) { | |
| 23405 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'); | |
| 23406 } | |
| 23407 } | |
| 23408 } // We could update instance props and state here, | |
| 23409 // but instead we rely on them being set during last render. | |
| 23410 // TODO: revisit this when we implement resuming. | |
| 23411 | |
| 23412 | |
| 23413 commitUpdateQueue(finishedWork, updateQueue, instance); | |
| 23414 } | |
| 23415 | |
| 23416 break; | |
| 23417 } | |
| 23418 | |
| 23419 case HostRoot: | |
| 23420 { | |
| 23421 // TODO: I think this is now always non-null by the time it reaches the | |
| 23422 // commit phase. Consider removing the type check. | |
| 23423 var _updateQueue = finishedWork.updateQueue; | |
| 23424 | |
| 23425 if (_updateQueue !== null) { | |
| 23426 var _instance = null; | |
| 23427 | |
| 23428 if (finishedWork.child !== null) { | |
| 23429 switch (finishedWork.child.tag) { | |
| 23430 case HostComponent: | |
| 23431 _instance = getPublicInstance(finishedWork.child.stateNode); | |
| 23432 break; | |
| 23433 | |
| 23434 case ClassComponent: | |
| 23435 _instance = finishedWork.child.stateNode; | |
| 23436 break; | |
| 23437 } | |
| 23438 } | |
| 23439 | |
| 23440 commitUpdateQueue(finishedWork, _updateQueue, _instance); | |
| 23441 } | |
| 23442 | |
| 23443 break; | |
| 23444 } | |
| 23445 | |
| 23446 case HostComponent: | |
| 23447 { | |
| 23448 var _instance2 = finishedWork.stateNode; // Renderers may schedule work to be done after host components are mounted | |
| 23449 // (eg DOM renderer may schedule auto-focus for inputs and form controls). | |
| 23450 // These effects should only be committed when components are first mounted, | |
| 23451 // aka when there is no current/alternate. | |
| 23452 | |
| 23453 if (current === null && finishedWork.flags & Update) { | |
| 23454 var type = finishedWork.type; | |
| 23455 var props = finishedWork.memoizedProps; | |
| 23456 commitMount(_instance2, type, props); | |
| 23457 } | |
| 23458 | |
| 23459 break; | |
| 23460 } | |
| 23461 | |
| 23462 case HostText: | |
| 23463 { | |
| 23464 // We have no life-cycles associated with text. | |
| 23465 break; | |
| 23466 } | |
| 23467 | |
| 23468 case HostPortal: | |
| 23469 { | |
| 23470 // We have no life-cycles associated with portals. | |
| 23471 break; | |
| 23472 } | |
| 23473 | |
| 23474 case Profiler: | |
| 23475 { | |
| 23476 { | |
| 23477 var _finishedWork$memoize2 = finishedWork.memoizedProps, | |
| 23478 onCommit = _finishedWork$memoize2.onCommit, | |
| 23479 onRender = _finishedWork$memoize2.onRender; | |
| 23480 var effectDuration = finishedWork.stateNode.effectDuration; | |
| 23481 var commitTime = getCommitTime(); | |
| 23482 var phase = current === null ? 'mount' : 'update'; | |
| 23483 | |
| 23484 { | |
| 23485 if (isCurrentUpdateNested()) { | |
| 23486 phase = 'nested-update'; | |
| 23487 } | |
| 23488 } | |
| 23489 | |
| 23490 if (typeof onRender === 'function') { | |
| 23491 onRender(finishedWork.memoizedProps.id, phase, finishedWork.actualDuration, finishedWork.treeBaseDuration, finishedWork.actualStartTime, commitTime); | |
| 23492 } | |
| 23493 | |
| 23494 { | |
| 23495 if (typeof onCommit === 'function') { | |
| 23496 onCommit(finishedWork.memoizedProps.id, phase, effectDuration, commitTime); | |
| 23497 } // Schedule a passive effect for this Profiler to call onPostCommit hooks. | |
| 23498 // This effect should be scheduled even if there is no onPostCommit callback for this Profiler, | |
| 23499 // because the effect is also where times bubble to parent Profilers. | |
| 23500 | |
| 23501 | |
| 23502 enqueuePendingPassiveProfilerEffect(finishedWork); // Propagate layout effect durations to the next nearest Profiler ancestor. | |
| 23503 // Do not reset these values until the next render so DevTools has a chance to read them first. | |
| 23504 | |
| 23505 var parentFiber = finishedWork.return; | |
| 23506 | |
| 23507 outer: while (parentFiber !== null) { | |
| 23508 switch (parentFiber.tag) { | |
| 23509 case HostRoot: | |
| 23510 var root = parentFiber.stateNode; | |
| 23511 root.effectDuration += effectDuration; | |
| 23512 break outer; | |
| 23513 | |
| 23514 case Profiler: | |
| 23515 var parentStateNode = parentFiber.stateNode; | |
| 23516 parentStateNode.effectDuration += effectDuration; | |
| 23517 break outer; | |
| 23518 } | |
| 23519 | |
| 23520 parentFiber = parentFiber.return; | |
| 23521 } | |
| 23522 } | |
| 23523 } | |
| 23524 | |
| 23525 break; | |
| 23526 } | |
| 23527 | |
| 23528 case SuspenseComponent: | |
| 23529 { | |
| 23530 commitSuspenseHydrationCallbacks(finishedRoot, finishedWork); | |
| 23531 break; | |
| 23532 } | |
| 23533 | |
| 23534 case SuspenseListComponent: | |
| 23535 case IncompleteClassComponent: | |
| 23536 case ScopeComponent: | |
| 23537 case OffscreenComponent: | |
| 23538 case LegacyHiddenComponent: | |
| 23539 case TracingMarkerComponent: | |
| 23540 { | |
| 23541 break; | |
| 23542 } | |
| 23543 | |
| 23544 default: | |
| 23545 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.'); | |
| 23546 } | |
| 23547 } | |
| 23548 | |
| 23549 if ( !offscreenSubtreeWasHidden) { | |
| 23550 { | |
| 23551 if (finishedWork.flags & Ref) { | |
| 23552 commitAttachRef(finishedWork); | |
| 23553 } | |
| 23554 } | |
| 23555 } | |
| 23556 } | |
| 23557 | |
| 23558 function reappearLayoutEffectsOnFiber(node) { | |
| 23559 // Turn on layout effects in a tree that previously disappeared. | |
| 23560 // TODO (Offscreen) Check: flags & LayoutStatic | |
| 23561 switch (node.tag) { | |
| 23562 case FunctionComponent: | |
| 23563 case ForwardRef: | |
| 23564 case SimpleMemoComponent: | |
| 23565 { | |
| 23566 if ( node.mode & ProfileMode) { | |
| 23567 try { | |
| 23568 startLayoutEffectTimer(); | |
| 23569 safelyCallCommitHookLayoutEffectListMount(node, node.return); | |
| 23570 } finally { | |
| 23571 recordLayoutEffectDuration(node); | |
| 23572 } | |
| 23573 } else { | |
| 23574 safelyCallCommitHookLayoutEffectListMount(node, node.return); | |
| 23575 } | |
| 23576 | |
| 23577 break; | |
| 23578 } | |
| 23579 | |
| 23580 case ClassComponent: | |
| 23581 { | |
| 23582 var instance = node.stateNode; | |
| 23583 | |
| 23584 if (typeof instance.componentDidMount === 'function') { | |
| 23585 safelyCallComponentDidMount(node, node.return, instance); | |
| 23586 } | |
| 23587 | |
| 23588 safelyAttachRef(node, node.return); | |
| 23589 break; | |
| 23590 } | |
| 23591 | |
| 23592 case HostComponent: | |
| 23593 { | |
| 23594 safelyAttachRef(node, node.return); | |
| 23595 break; | |
| 23596 } | |
| 23597 } | |
| 23598 } | |
| 23599 | |
| 23600 function hideOrUnhideAllChildren(finishedWork, isHidden) { | |
| 23601 // Only hide or unhide the top-most host nodes. | |
| 23602 var hostSubtreeRoot = null; | |
| 23603 | |
| 23604 { | |
| 23605 // We only have the top Fiber that was inserted but we need to recurse down its | |
| 23606 // children to find all the terminal nodes. | |
| 23607 var node = finishedWork; | |
| 23608 | |
| 23609 while (true) { | |
| 23610 if (node.tag === HostComponent) { | |
| 23611 if (hostSubtreeRoot === null) { | |
| 23612 hostSubtreeRoot = node; | |
| 23613 | |
| 23614 try { | |
| 23615 var instance = node.stateNode; | |
| 23616 | |
| 23617 if (isHidden) { | |
| 23618 hideInstance(instance); | |
| 23619 } else { | |
| 23620 unhideInstance(node.stateNode, node.memoizedProps); | |
| 23621 } | |
| 23622 } catch (error) { | |
| 23623 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 23624 } | |
| 23625 } | |
| 23626 } else if (node.tag === HostText) { | |
| 23627 if (hostSubtreeRoot === null) { | |
| 23628 try { | |
| 23629 var _instance3 = node.stateNode; | |
| 23630 | |
| 23631 if (isHidden) { | |
| 23632 hideTextInstance(_instance3); | |
| 23633 } else { | |
| 23634 unhideTextInstance(_instance3, node.memoizedProps); | |
| 23635 } | |
| 23636 } catch (error) { | |
| 23637 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 23638 } | |
| 23639 } | |
| 23640 } else if ((node.tag === OffscreenComponent || node.tag === LegacyHiddenComponent) && node.memoizedState !== null && node !== finishedWork) ; else if (node.child !== null) { | |
| 23641 node.child.return = node; | |
| 23642 node = node.child; | |
| 23643 continue; | |
| 23644 } | |
| 23645 | |
| 23646 if (node === finishedWork) { | |
| 23647 return; | |
| 23648 } | |
| 23649 | |
| 23650 while (node.sibling === null) { | |
| 23651 if (node.return === null || node.return === finishedWork) { | |
| 23652 return; | |
| 23653 } | |
| 23654 | |
| 23655 if (hostSubtreeRoot === node) { | |
| 23656 hostSubtreeRoot = null; | |
| 23657 } | |
| 23658 | |
| 23659 node = node.return; | |
| 23660 } | |
| 23661 | |
| 23662 if (hostSubtreeRoot === node) { | |
| 23663 hostSubtreeRoot = null; | |
| 23664 } | |
| 23665 | |
| 23666 node.sibling.return = node.return; | |
| 23667 node = node.sibling; | |
| 23668 } | |
| 23669 } | |
| 23670 } | |
| 23671 | |
| 23672 function commitAttachRef(finishedWork) { | |
| 23673 var ref = finishedWork.ref; | |
| 23674 | |
| 23675 if (ref !== null) { | |
| 23676 var instance = finishedWork.stateNode; | |
| 23677 var instanceToUse; | |
| 23678 | |
| 23679 switch (finishedWork.tag) { | |
| 23680 case HostComponent: | |
| 23681 instanceToUse = getPublicInstance(instance); | |
| 23682 break; | |
| 23683 | |
| 23684 default: | |
| 23685 instanceToUse = instance; | |
| 23686 } // Moved outside to ensure DCE works with this flag | |
| 23687 | |
| 23688 if (typeof ref === 'function') { | |
| 23689 var retVal; | |
| 23690 | |
| 23691 if ( finishedWork.mode & ProfileMode) { | |
| 23692 try { | |
| 23693 startLayoutEffectTimer(); | |
| 23694 retVal = ref(instanceToUse); | |
| 23695 } finally { | |
| 23696 recordLayoutEffectDuration(finishedWork); | |
| 23697 } | |
| 23698 } else { | |
| 23699 retVal = ref(instanceToUse); | |
| 23700 } | |
| 23701 | |
| 23702 { | |
| 23703 if (typeof retVal === 'function') { | |
| 23704 error('Unexpected return value from a callback ref in %s. ' + 'A callback ref should not return a function.', getComponentNameFromFiber(finishedWork)); | |
| 23705 } | |
| 23706 } | |
| 23707 } else { | |
| 23708 { | |
| 23709 if (!ref.hasOwnProperty('current')) { | |
| 23710 error('Unexpected ref object provided for %s. ' + 'Use either a ref-setter function or React.createRef().', getComponentNameFromFiber(finishedWork)); | |
| 23711 } | |
| 23712 } | |
| 23713 | |
| 23714 ref.current = instanceToUse; | |
| 23715 } | |
| 23716 } | |
| 23717 } | |
| 23718 | |
| 23719 function detachFiberMutation(fiber) { | |
| 23720 // Cut off the return pointer to disconnect it from the tree. | |
| 23721 // This enables us to detect and warn against state updates on an unmounted component. | |
| 23722 // It also prevents events from bubbling from within disconnected components. | |
| 23723 // | |
| 23724 // Ideally, we should also clear the child pointer of the parent alternate to let this | |
| 23725 // get GC:ed but we don't know which for sure which parent is the current | |
| 23726 // one so we'll settle for GC:ing the subtree of this child. | |
| 23727 // This child itself will be GC:ed when the parent updates the next time. | |
| 23728 // | |
| 23729 // Note that we can't clear child or sibling pointers yet. | |
| 23730 // They're needed for passive effects and for findDOMNode. | |
| 23731 // We defer those fields, and all other cleanup, to the passive phase (see detachFiberAfterEffects). | |
| 23732 // | |
| 23733 // Don't reset the alternate yet, either. We need that so we can detach the | |
| 23734 // alternate's fields in the passive phase. Clearing the return pointer is | |
| 23735 // sufficient for findDOMNode semantics. | |
| 23736 var alternate = fiber.alternate; | |
| 23737 | |
| 23738 if (alternate !== null) { | |
| 23739 alternate.return = null; | |
| 23740 } | |
| 23741 | |
| 23742 fiber.return = null; | |
| 23743 } | |
| 23744 | |
| 23745 function detachFiberAfterEffects(fiber) { | |
| 23746 var alternate = fiber.alternate; | |
| 23747 | |
| 23748 if (alternate !== null) { | |
| 23749 fiber.alternate = null; | |
| 23750 detachFiberAfterEffects(alternate); | |
| 23751 } // Note: Defensively using negation instead of < in case | |
| 23752 // `deletedTreeCleanUpLevel` is undefined. | |
| 23753 | |
| 23754 | |
| 23755 { | |
| 23756 // Clear cyclical Fiber fields. This level alone is designed to roughly | |
| 23757 // approximate the planned Fiber refactor. In that world, `setState` will be | |
| 23758 // bound to a special "instance" object instead of a Fiber. The Instance | |
| 23759 // object will not have any of these fields. It will only be connected to | |
| 23760 // the fiber tree via a single link at the root. So if this level alone is | |
| 23761 // sufficient to fix memory issues, that bodes well for our plans. | |
| 23762 fiber.child = null; | |
| 23763 fiber.deletions = null; | |
| 23764 fiber.sibling = null; // The `stateNode` is cyclical because on host nodes it points to the host | |
| 23765 // tree, which has its own pointers to children, parents, and siblings. | |
| 23766 // The other host nodes also point back to fibers, so we should detach that | |
| 23767 // one, too. | |
| 23768 | |
| 23769 if (fiber.tag === HostComponent) { | |
| 23770 var hostInstance = fiber.stateNode; | |
| 23771 | |
| 23772 if (hostInstance !== null) { | |
| 23773 detachDeletedInstance(hostInstance); | |
| 23774 } | |
| 23775 } | |
| 23776 | |
| 23777 fiber.stateNode = null; // I'm intentionally not clearing the `return` field in this level. We | |
| 23778 // already disconnect the `return` pointer at the root of the deleted | |
| 23779 // subtree (in `detachFiberMutation`). Besides, `return` by itself is not | |
| 23780 // cyclical — it's only cyclical when combined with `child`, `sibling`, and | |
| 23781 // `alternate`. But we'll clear it in the next level anyway, just in case. | |
| 23782 | |
| 23783 { | |
| 23784 fiber._debugOwner = null; | |
| 23785 } | |
| 23786 | |
| 23787 { | |
| 23788 // Theoretically, nothing in here should be necessary, because we already | |
| 23789 // disconnected the fiber from the tree. So even if something leaks this | |
| 23790 // particular fiber, it won't leak anything else | |
| 23791 // | |
| 23792 // The purpose of this branch is to be super aggressive so we can measure | |
| 23793 // if there's any difference in memory impact. If there is, that could | |
| 23794 // indicate a React leak we don't know about. | |
| 23795 fiber.return = null; | |
| 23796 fiber.dependencies = null; | |
| 23797 fiber.memoizedProps = null; | |
| 23798 fiber.memoizedState = null; | |
| 23799 fiber.pendingProps = null; | |
| 23800 fiber.stateNode = null; // TODO: Move to `commitPassiveUnmountInsideDeletedTreeOnFiber` instead. | |
| 23801 | |
| 23802 fiber.updateQueue = null; | |
| 23803 } | |
| 23804 } | |
| 23805 } | |
| 23806 | |
| 23807 function getHostParentFiber(fiber) { | |
| 23808 var parent = fiber.return; | |
| 23809 | |
| 23810 while (parent !== null) { | |
| 23811 if (isHostParent(parent)) { | |
| 23812 return parent; | |
| 23813 } | |
| 23814 | |
| 23815 parent = parent.return; | |
| 23816 } | |
| 23817 | |
| 23818 throw new Error('Expected to find a host parent. This error is likely caused by a bug ' + 'in React. Please file an issue.'); | |
| 23819 } | |
| 23820 | |
| 23821 function isHostParent(fiber) { | |
| 23822 return fiber.tag === HostComponent || fiber.tag === HostRoot || fiber.tag === HostPortal; | |
| 23823 } | |
| 23824 | |
| 23825 function getHostSibling(fiber) { | |
| 23826 // We're going to search forward into the tree until we find a sibling host | |
| 23827 // node. Unfortunately, if multiple insertions are done in a row we have to | |
| 23828 // search past them. This leads to exponential search for the next sibling. | |
| 23829 // TODO: Find a more efficient way to do this. | |
| 23830 var node = fiber; | |
| 23831 | |
| 23832 siblings: while (true) { | |
| 23833 // If we didn't find anything, let's try the next sibling. | |
| 23834 while (node.sibling === null) { | |
| 23835 if (node.return === null || isHostParent(node.return)) { | |
| 23836 // If we pop out of the root or hit the parent the fiber we are the | |
| 23837 // last sibling. | |
| 23838 return null; | |
| 23839 } | |
| 23840 | |
| 23841 node = node.return; | |
| 23842 } | |
| 23843 | |
| 23844 node.sibling.return = node.return; | |
| 23845 node = node.sibling; | |
| 23846 | |
| 23847 while (node.tag !== HostComponent && node.tag !== HostText && node.tag !== DehydratedFragment) { | |
| 23848 // If it is not host node and, we might have a host node inside it. | |
| 23849 // Try to search down until we find one. | |
| 23850 if (node.flags & Placement) { | |
| 23851 // If we don't have a child, try the siblings instead. | |
| 23852 continue siblings; | |
| 23853 } // If we don't have a child, try the siblings instead. | |
| 23854 // We also skip portals because they are not part of this host tree. | |
| 23855 | |
| 23856 | |
| 23857 if (node.child === null || node.tag === HostPortal) { | |
| 23858 continue siblings; | |
| 23859 } else { | |
| 23860 node.child.return = node; | |
| 23861 node = node.child; | |
| 23862 } | |
| 23863 } // Check if this host node is stable or about to be placed. | |
| 23864 | |
| 23865 | |
| 23866 if (!(node.flags & Placement)) { | |
| 23867 // Found it! | |
| 23868 return node.stateNode; | |
| 23869 } | |
| 23870 } | |
| 23871 } | |
| 23872 | |
| 23873 function commitPlacement(finishedWork) { | |
| 23874 | |
| 23875 | |
| 23876 var parentFiber = getHostParentFiber(finishedWork); // Note: these two variables *must* always be updated together. | |
| 23877 | |
| 23878 switch (parentFiber.tag) { | |
| 23879 case HostComponent: | |
| 23880 { | |
| 23881 var parent = parentFiber.stateNode; | |
| 23882 | |
| 23883 if (parentFiber.flags & ContentReset) { | |
| 23884 // Reset the text content of the parent before doing any insertions | |
| 23885 resetTextContent(parent); // Clear ContentReset from the effect tag | |
| 23886 | |
| 23887 parentFiber.flags &= ~ContentReset; | |
| 23888 } | |
| 23889 | |
| 23890 var before = getHostSibling(finishedWork); // We only have the top Fiber that was inserted but we need to recurse down its | |
| 23891 // children to find all the terminal nodes. | |
| 23892 | |
| 23893 insertOrAppendPlacementNode(finishedWork, before, parent); | |
| 23894 break; | |
| 23895 } | |
| 23896 | |
| 23897 case HostRoot: | |
| 23898 case HostPortal: | |
| 23899 { | |
| 23900 var _parent = parentFiber.stateNode.containerInfo; | |
| 23901 | |
| 23902 var _before = getHostSibling(finishedWork); | |
| 23903 | |
| 23904 insertOrAppendPlacementNodeIntoContainer(finishedWork, _before, _parent); | |
| 23905 break; | |
| 23906 } | |
| 23907 // eslint-disable-next-line-no-fallthrough | |
| 23908 | |
| 23909 default: | |
| 23910 throw new Error('Invalid host parent fiber. This error is likely caused by a bug ' + 'in React. Please file an issue.'); | |
| 23911 } | |
| 23912 } | |
| 23913 | |
| 23914 function insertOrAppendPlacementNodeIntoContainer(node, before, parent) { | |
| 23915 var tag = node.tag; | |
| 23916 var isHost = tag === HostComponent || tag === HostText; | |
| 23917 | |
| 23918 if (isHost) { | |
| 23919 var stateNode = node.stateNode; | |
| 23920 | |
| 23921 if (before) { | |
| 23922 insertInContainerBefore(parent, stateNode, before); | |
| 23923 } else { | |
| 23924 appendChildToContainer(parent, stateNode); | |
| 23925 } | |
| 23926 } else if (tag === HostPortal) ; else { | |
| 23927 var child = node.child; | |
| 23928 | |
| 23929 if (child !== null) { | |
| 23930 insertOrAppendPlacementNodeIntoContainer(child, before, parent); | |
| 23931 var sibling = child.sibling; | |
| 23932 | |
| 23933 while (sibling !== null) { | |
| 23934 insertOrAppendPlacementNodeIntoContainer(sibling, before, parent); | |
| 23935 sibling = sibling.sibling; | |
| 23936 } | |
| 23937 } | |
| 23938 } | |
| 23939 } | |
| 23940 | |
| 23941 function insertOrAppendPlacementNode(node, before, parent) { | |
| 23942 var tag = node.tag; | |
| 23943 var isHost = tag === HostComponent || tag === HostText; | |
| 23944 | |
| 23945 if (isHost) { | |
| 23946 var stateNode = node.stateNode; | |
| 23947 | |
| 23948 if (before) { | |
| 23949 insertBefore(parent, stateNode, before); | |
| 23950 } else { | |
| 23951 appendChild(parent, stateNode); | |
| 23952 } | |
| 23953 } else if (tag === HostPortal) ; else { | |
| 23954 var child = node.child; | |
| 23955 | |
| 23956 if (child !== null) { | |
| 23957 insertOrAppendPlacementNode(child, before, parent); | |
| 23958 var sibling = child.sibling; | |
| 23959 | |
| 23960 while (sibling !== null) { | |
| 23961 insertOrAppendPlacementNode(sibling, before, parent); | |
| 23962 sibling = sibling.sibling; | |
| 23963 } | |
| 23964 } | |
| 23965 } | |
| 23966 } // These are tracked on the stack as we recursively traverse a | |
| 23967 // deleted subtree. | |
| 23968 // TODO: Update these during the whole mutation phase, not just during | |
| 23969 // a deletion. | |
| 23970 | |
| 23971 | |
| 23972 var hostParent = null; | |
| 23973 var hostParentIsContainer = false; | |
| 23974 | |
| 23975 function commitDeletionEffects(root, returnFiber, deletedFiber) { | |
| 23976 { | |
| 23977 // We only have the top Fiber that was deleted but we need to recurse down its | |
| 23978 // children to find all the terminal nodes. | |
| 23979 // Recursively delete all host nodes from the parent, detach refs, clean | |
| 23980 // up mounted layout effects, and call componentWillUnmount. | |
| 23981 // We only need to remove the topmost host child in each branch. But then we | |
| 23982 // still need to keep traversing to unmount effects, refs, and cWU. TODO: We | |
| 23983 // could split this into two separate traversals functions, where the second | |
| 23984 // one doesn't include any removeChild logic. This is maybe the same | |
| 23985 // function as "disappearLayoutEffects" (or whatever that turns into after | |
| 23986 // the layout phase is refactored to use recursion). | |
| 23987 // Before starting, find the nearest host parent on the stack so we know | |
| 23988 // which instance/container to remove the children from. | |
| 23989 // TODO: Instead of searching up the fiber return path on every deletion, we | |
| 23990 // can track the nearest host component on the JS stack as we traverse the | |
| 23991 // tree during the commit phase. This would make insertions faster, too. | |
| 23992 var parent = returnFiber; | |
| 23993 | |
| 23994 findParent: while (parent !== null) { | |
| 23995 switch (parent.tag) { | |
| 23996 case HostComponent: | |
| 23997 { | |
| 23998 hostParent = parent.stateNode; | |
| 23999 hostParentIsContainer = false; | |
| 24000 break findParent; | |
| 24001 } | |
| 24002 | |
| 24003 case HostRoot: | |
| 24004 { | |
| 24005 hostParent = parent.stateNode.containerInfo; | |
| 24006 hostParentIsContainer = true; | |
| 24007 break findParent; | |
| 24008 } | |
| 24009 | |
| 24010 case HostPortal: | |
| 24011 { | |
| 24012 hostParent = parent.stateNode.containerInfo; | |
| 24013 hostParentIsContainer = true; | |
| 24014 break findParent; | |
| 24015 } | |
| 24016 } | |
| 24017 | |
| 24018 parent = parent.return; | |
| 24019 } | |
| 24020 | |
| 24021 if (hostParent === null) { | |
| 24022 throw new Error('Expected to find a host parent. This error is likely caused by ' + 'a bug in React. Please file an issue.'); | |
| 24023 } | |
| 24024 | |
| 24025 commitDeletionEffectsOnFiber(root, returnFiber, deletedFiber); | |
| 24026 hostParent = null; | |
| 24027 hostParentIsContainer = false; | |
| 24028 } | |
| 24029 | |
| 24030 detachFiberMutation(deletedFiber); | |
| 24031 } | |
| 24032 | |
| 24033 function recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, parent) { | |
| 24034 // TODO: Use a static flag to skip trees that don't have unmount effects | |
| 24035 var child = parent.child; | |
| 24036 | |
| 24037 while (child !== null) { | |
| 24038 commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, child); | |
| 24039 child = child.sibling; | |
| 24040 } | |
| 24041 } | |
| 24042 | |
| 24043 function commitDeletionEffectsOnFiber(finishedRoot, nearestMountedAncestor, deletedFiber) { | |
| 24044 onCommitUnmount(deletedFiber); // The cases in this outer switch modify the stack before they traverse | |
| 24045 // into their subtree. There are simpler cases in the inner switch | |
| 24046 // that don't modify the stack. | |
| 24047 | |
| 24048 switch (deletedFiber.tag) { | |
| 24049 case HostComponent: | |
| 24050 { | |
| 24051 if (!offscreenSubtreeWasHidden) { | |
| 24052 safelyDetachRef(deletedFiber, nearestMountedAncestor); | |
| 24053 } // Intentional fallthrough to next branch | |
| 24054 | |
| 24055 } | |
| 24056 // eslint-disable-next-line-no-fallthrough | |
| 24057 | |
| 24058 case HostText: | |
| 24059 { | |
| 24060 // We only need to remove the nearest host child. Set the host parent | |
| 24061 // to `null` on the stack to indicate that nested children don't | |
| 24062 // need to be removed. | |
| 24063 { | |
| 24064 var prevHostParent = hostParent; | |
| 24065 var prevHostParentIsContainer = hostParentIsContainer; | |
| 24066 hostParent = null; | |
| 24067 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24068 hostParent = prevHostParent; | |
| 24069 hostParentIsContainer = prevHostParentIsContainer; | |
| 24070 | |
| 24071 if (hostParent !== null) { | |
| 24072 // Now that all the child effects have unmounted, we can remove the | |
| 24073 // node from the tree. | |
| 24074 if (hostParentIsContainer) { | |
| 24075 removeChildFromContainer(hostParent, deletedFiber.stateNode); | |
| 24076 } else { | |
| 24077 removeChild(hostParent, deletedFiber.stateNode); | |
| 24078 } | |
| 24079 } | |
| 24080 } | |
| 24081 | |
| 24082 return; | |
| 24083 } | |
| 24084 | |
| 24085 case DehydratedFragment: | |
| 24086 { | |
| 24087 // Delete the dehydrated suspense boundary and all of its content. | |
| 24088 | |
| 24089 | |
| 24090 { | |
| 24091 if (hostParent !== null) { | |
| 24092 if (hostParentIsContainer) { | |
| 24093 clearSuspenseBoundaryFromContainer(hostParent, deletedFiber.stateNode); | |
| 24094 } else { | |
| 24095 clearSuspenseBoundary(hostParent, deletedFiber.stateNode); | |
| 24096 } | |
| 24097 } | |
| 24098 } | |
| 24099 | |
| 24100 return; | |
| 24101 } | |
| 24102 | |
| 24103 case HostPortal: | |
| 24104 { | |
| 24105 { | |
| 24106 // When we go into a portal, it becomes the parent to remove from. | |
| 24107 var _prevHostParent = hostParent; | |
| 24108 var _prevHostParentIsContainer = hostParentIsContainer; | |
| 24109 hostParent = deletedFiber.stateNode.containerInfo; | |
| 24110 hostParentIsContainer = true; | |
| 24111 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24112 hostParent = _prevHostParent; | |
| 24113 hostParentIsContainer = _prevHostParentIsContainer; | |
| 24114 } | |
| 24115 | |
| 24116 return; | |
| 24117 } | |
| 24118 | |
| 24119 case FunctionComponent: | |
| 24120 case ForwardRef: | |
| 24121 case MemoComponent: | |
| 24122 case SimpleMemoComponent: | |
| 24123 { | |
| 24124 if (!offscreenSubtreeWasHidden) { | |
| 24125 var updateQueue = deletedFiber.updateQueue; | |
| 24126 | |
| 24127 if (updateQueue !== null) { | |
| 24128 var lastEffect = updateQueue.lastEffect; | |
| 24129 | |
| 24130 if (lastEffect !== null) { | |
| 24131 var firstEffect = lastEffect.next; | |
| 24132 var effect = firstEffect; | |
| 24133 | |
| 24134 do { | |
| 24135 var _effect = effect, | |
| 24136 destroy = _effect.destroy, | |
| 24137 tag = _effect.tag; | |
| 24138 | |
| 24139 if (destroy !== undefined) { | |
| 24140 if ((tag & Insertion) !== NoFlags$1) { | |
| 24141 safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy); | |
| 24142 } else if ((tag & Layout) !== NoFlags$1) { | |
| 24143 { | |
| 24144 markComponentLayoutEffectUnmountStarted(deletedFiber); | |
| 24145 } | |
| 24146 | |
| 24147 if ( deletedFiber.mode & ProfileMode) { | |
| 24148 startLayoutEffectTimer(); | |
| 24149 safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy); | |
| 24150 recordLayoutEffectDuration(deletedFiber); | |
| 24151 } else { | |
| 24152 safelyCallDestroy(deletedFiber, nearestMountedAncestor, destroy); | |
| 24153 } | |
| 24154 | |
| 24155 { | |
| 24156 markComponentLayoutEffectUnmountStopped(); | |
| 24157 } | |
| 24158 } | |
| 24159 } | |
| 24160 | |
| 24161 effect = effect.next; | |
| 24162 } while (effect !== firstEffect); | |
| 24163 } | |
| 24164 } | |
| 24165 } | |
| 24166 | |
| 24167 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24168 return; | |
| 24169 } | |
| 24170 | |
| 24171 case ClassComponent: | |
| 24172 { | |
| 24173 if (!offscreenSubtreeWasHidden) { | |
| 24174 safelyDetachRef(deletedFiber, nearestMountedAncestor); | |
| 24175 var instance = deletedFiber.stateNode; | |
| 24176 | |
| 24177 if (typeof instance.componentWillUnmount === 'function') { | |
| 24178 safelyCallComponentWillUnmount(deletedFiber, nearestMountedAncestor, instance); | |
| 24179 } | |
| 24180 } | |
| 24181 | |
| 24182 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24183 return; | |
| 24184 } | |
| 24185 | |
| 24186 case ScopeComponent: | |
| 24187 { | |
| 24188 | |
| 24189 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24190 return; | |
| 24191 } | |
| 24192 | |
| 24193 case OffscreenComponent: | |
| 24194 { | |
| 24195 if ( // TODO: Remove this dead flag | |
| 24196 deletedFiber.mode & ConcurrentMode) { | |
| 24197 // If this offscreen component is hidden, we already unmounted it. Before | |
| 24198 // deleting the children, track that it's already unmounted so that we | |
| 24199 // don't attempt to unmount the effects again. | |
| 24200 // TODO: If the tree is hidden, in most cases we should be able to skip | |
| 24201 // over the nested children entirely. An exception is we haven't yet found | |
| 24202 // the topmost host node to delete, which we already track on the stack. | |
| 24203 // But the other case is portals, which need to be detached no matter how | |
| 24204 // deeply they are nested. We should use a subtree flag to track whether a | |
| 24205 // subtree includes a nested portal. | |
| 24206 var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden; | |
| 24207 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || deletedFiber.memoizedState !== null; | |
| 24208 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24209 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden; | |
| 24210 } else { | |
| 24211 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24212 } | |
| 24213 | |
| 24214 break; | |
| 24215 } | |
| 24216 | |
| 24217 default: | |
| 24218 { | |
| 24219 recursivelyTraverseDeletionEffects(finishedRoot, nearestMountedAncestor, deletedFiber); | |
| 24220 return; | |
| 24221 } | |
| 24222 } | |
| 24223 } | |
| 24224 | |
| 24225 function commitSuspenseCallback(finishedWork) { | |
| 24226 // TODO: Move this to passive phase | |
| 24227 var newState = finishedWork.memoizedState; | |
| 24228 } | |
| 24229 | |
| 24230 function commitSuspenseHydrationCallbacks(finishedRoot, finishedWork) { | |
| 24231 | |
| 24232 var newState = finishedWork.memoizedState; | |
| 24233 | |
| 24234 if (newState === null) { | |
| 24235 var current = finishedWork.alternate; | |
| 24236 | |
| 24237 if (current !== null) { | |
| 24238 var prevState = current.memoizedState; | |
| 24239 | |
| 24240 if (prevState !== null) { | |
| 24241 var suspenseInstance = prevState.dehydrated; | |
| 24242 | |
| 24243 if (suspenseInstance !== null) { | |
| 24244 commitHydratedSuspenseInstance(suspenseInstance); | |
| 24245 } | |
| 24246 } | |
| 24247 } | |
| 24248 } | |
| 24249 } | |
| 24250 | |
| 24251 function attachSuspenseRetryListeners(finishedWork) { | |
| 24252 // If this boundary just timed out, then it will have a set of wakeables. | |
| 24253 // For each wakeable, attach a listener so that when it resolves, React | |
| 24254 // attempts to re-render the boundary in the primary (pre-timeout) state. | |
| 24255 var wakeables = finishedWork.updateQueue; | |
| 24256 | |
| 24257 if (wakeables !== null) { | |
| 24258 finishedWork.updateQueue = null; | |
| 24259 var retryCache = finishedWork.stateNode; | |
| 24260 | |
| 24261 if (retryCache === null) { | |
| 24262 retryCache = finishedWork.stateNode = new PossiblyWeakSet(); | |
| 24263 } | |
| 24264 | |
| 24265 wakeables.forEach(function (wakeable) { | |
| 24266 // Memoize using the boundary fiber to prevent redundant listeners. | |
| 24267 var retry = resolveRetryWakeable.bind(null, finishedWork, wakeable); | |
| 24268 | |
| 24269 if (!retryCache.has(wakeable)) { | |
| 24270 retryCache.add(wakeable); | |
| 24271 | |
| 24272 { | |
| 24273 if (isDevToolsPresent) { | |
| 24274 if (inProgressLanes !== null && inProgressRoot !== null) { | |
| 24275 // If we have pending work still, associate the original updaters with it. | |
| 24276 restorePendingUpdaters(inProgressRoot, inProgressLanes); | |
| 24277 } else { | |
| 24278 throw Error('Expected finished root and lanes to be set. This is a bug in React.'); | |
| 24279 } | |
| 24280 } | |
| 24281 } | |
| 24282 | |
| 24283 wakeable.then(retry, retry); | |
| 24284 } | |
| 24285 }); | |
| 24286 } | |
| 24287 } // This function detects when a Suspense boundary goes from visible to hidden. | |
| 24288 function commitMutationEffects(root, finishedWork, committedLanes) { | |
| 24289 inProgressLanes = committedLanes; | |
| 24290 inProgressRoot = root; | |
| 24291 setCurrentFiber(finishedWork); | |
| 24292 commitMutationEffectsOnFiber(finishedWork, root); | |
| 24293 setCurrentFiber(finishedWork); | |
| 24294 inProgressLanes = null; | |
| 24295 inProgressRoot = null; | |
| 24296 } | |
| 24297 | |
| 24298 function recursivelyTraverseMutationEffects(root, parentFiber, lanes) { | |
| 24299 // Deletions effects can be scheduled on any fiber type. They need to happen | |
| 24300 // before the children effects hae fired. | |
| 24301 var deletions = parentFiber.deletions; | |
| 24302 | |
| 24303 if (deletions !== null) { | |
| 24304 for (var i = 0; i < deletions.length; i++) { | |
| 24305 var childToDelete = deletions[i]; | |
| 24306 | |
| 24307 try { | |
| 24308 commitDeletionEffects(root, parentFiber, childToDelete); | |
| 24309 } catch (error) { | |
| 24310 captureCommitPhaseError(childToDelete, parentFiber, error); | |
| 24311 } | |
| 24312 } | |
| 24313 } | |
| 24314 | |
| 24315 var prevDebugFiber = getCurrentFiber(); | |
| 24316 | |
| 24317 if (parentFiber.subtreeFlags & MutationMask) { | |
| 24318 var child = parentFiber.child; | |
| 24319 | |
| 24320 while (child !== null) { | |
| 24321 setCurrentFiber(child); | |
| 24322 commitMutationEffectsOnFiber(child, root); | |
| 24323 child = child.sibling; | |
| 24324 } | |
| 24325 } | |
| 24326 | |
| 24327 setCurrentFiber(prevDebugFiber); | |
| 24328 } | |
| 24329 | |
| 24330 function commitMutationEffectsOnFiber(finishedWork, root, lanes) { | |
| 24331 var current = finishedWork.alternate; | |
| 24332 var flags = finishedWork.flags; // The effect flag should be checked *after* we refine the type of fiber, | |
| 24333 // because the fiber tag is more specific. An exception is any flag related | |
| 24334 // to reconcilation, because those can be set on all fiber types. | |
| 24335 | |
| 24336 switch (finishedWork.tag) { | |
| 24337 case FunctionComponent: | |
| 24338 case ForwardRef: | |
| 24339 case MemoComponent: | |
| 24340 case SimpleMemoComponent: | |
| 24341 { | |
| 24342 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24343 commitReconciliationEffects(finishedWork); | |
| 24344 | |
| 24345 if (flags & Update) { | |
| 24346 try { | |
| 24347 commitHookEffectListUnmount(Insertion | HasEffect, finishedWork, finishedWork.return); | |
| 24348 commitHookEffectListMount(Insertion | HasEffect, finishedWork); | |
| 24349 } catch (error) { | |
| 24350 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24351 } // Layout effects are destroyed during the mutation phase so that all | |
| 24352 // destroy functions for all fibers are called before any create functions. | |
| 24353 // This prevents sibling component effects from interfering with each other, | |
| 24354 // e.g. a destroy function in one component should never override a ref set | |
| 24355 // by a create function in another component during the same commit. | |
| 24356 | |
| 24357 | |
| 24358 if ( finishedWork.mode & ProfileMode) { | |
| 24359 try { | |
| 24360 startLayoutEffectTimer(); | |
| 24361 commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return); | |
| 24362 } catch (error) { | |
| 24363 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24364 } | |
| 24365 | |
| 24366 recordLayoutEffectDuration(finishedWork); | |
| 24367 } else { | |
| 24368 try { | |
| 24369 commitHookEffectListUnmount(Layout | HasEffect, finishedWork, finishedWork.return); | |
| 24370 } catch (error) { | |
| 24371 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24372 } | |
| 24373 } | |
| 24374 } | |
| 24375 | |
| 24376 return; | |
| 24377 } | |
| 24378 | |
| 24379 case ClassComponent: | |
| 24380 { | |
| 24381 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24382 commitReconciliationEffects(finishedWork); | |
| 24383 | |
| 24384 if (flags & Ref) { | |
| 24385 if (current !== null) { | |
| 24386 safelyDetachRef(current, current.return); | |
| 24387 } | |
| 24388 } | |
| 24389 | |
| 24390 return; | |
| 24391 } | |
| 24392 | |
| 24393 case HostComponent: | |
| 24394 { | |
| 24395 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24396 commitReconciliationEffects(finishedWork); | |
| 24397 | |
| 24398 if (flags & Ref) { | |
| 24399 if (current !== null) { | |
| 24400 safelyDetachRef(current, current.return); | |
| 24401 } | |
| 24402 } | |
| 24403 | |
| 24404 { | |
| 24405 // TODO: ContentReset gets cleared by the children during the commit | |
| 24406 // phase. This is a refactor hazard because it means we must read | |
| 24407 // flags the flags after `commitReconciliationEffects` has already run; | |
| 24408 // the order matters. We should refactor so that ContentReset does not | |
| 24409 // rely on mutating the flag during commit. Like by setting a flag | |
| 24410 // during the render phase instead. | |
| 24411 if (finishedWork.flags & ContentReset) { | |
| 24412 var instance = finishedWork.stateNode; | |
| 24413 | |
| 24414 try { | |
| 24415 resetTextContent(instance); | |
| 24416 } catch (error) { | |
| 24417 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24418 } | |
| 24419 } | |
| 24420 | |
| 24421 if (flags & Update) { | |
| 24422 var _instance4 = finishedWork.stateNode; | |
| 24423 | |
| 24424 if (_instance4 != null) { | |
| 24425 // Commit the work prepared earlier. | |
| 24426 var newProps = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps | |
| 24427 // as the newProps. The updatePayload will contain the real change in | |
| 24428 // this case. | |
| 24429 | |
| 24430 var oldProps = current !== null ? current.memoizedProps : newProps; | |
| 24431 var type = finishedWork.type; // TODO: Type the updateQueue to be specific to host components. | |
| 24432 | |
| 24433 var updatePayload = finishedWork.updateQueue; | |
| 24434 finishedWork.updateQueue = null; | |
| 24435 | |
| 24436 if (updatePayload !== null) { | |
| 24437 try { | |
| 24438 commitUpdate(_instance4, updatePayload, type, oldProps, newProps, finishedWork); | |
| 24439 } catch (error) { | |
| 24440 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24441 } | |
| 24442 } | |
| 24443 } | |
| 24444 } | |
| 24445 } | |
| 24446 | |
| 24447 return; | |
| 24448 } | |
| 24449 | |
| 24450 case HostText: | |
| 24451 { | |
| 24452 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24453 commitReconciliationEffects(finishedWork); | |
| 24454 | |
| 24455 if (flags & Update) { | |
| 24456 { | |
| 24457 if (finishedWork.stateNode === null) { | |
| 24458 throw new Error('This should have a text node initialized. This error is likely ' + 'caused by a bug in React. Please file an issue.'); | |
| 24459 } | |
| 24460 | |
| 24461 var textInstance = finishedWork.stateNode; | |
| 24462 var newText = finishedWork.memoizedProps; // For hydration we reuse the update path but we treat the oldProps | |
| 24463 // as the newProps. The updatePayload will contain the real change in | |
| 24464 // this case. | |
| 24465 | |
| 24466 var oldText = current !== null ? current.memoizedProps : newText; | |
| 24467 | |
| 24468 try { | |
| 24469 commitTextUpdate(textInstance, oldText, newText); | |
| 24470 } catch (error) { | |
| 24471 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24472 } | |
| 24473 } | |
| 24474 } | |
| 24475 | |
| 24476 return; | |
| 24477 } | |
| 24478 | |
| 24479 case HostRoot: | |
| 24480 { | |
| 24481 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24482 commitReconciliationEffects(finishedWork); | |
| 24483 | |
| 24484 if (flags & Update) { | |
| 24485 { | |
| 24486 if (current !== null) { | |
| 24487 var prevRootState = current.memoizedState; | |
| 24488 | |
| 24489 if (prevRootState.isDehydrated) { | |
| 24490 try { | |
| 24491 commitHydratedContainer(root.containerInfo); | |
| 24492 } catch (error) { | |
| 24493 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24494 } | |
| 24495 } | |
| 24496 } | |
| 24497 } | |
| 24498 } | |
| 24499 | |
| 24500 return; | |
| 24501 } | |
| 24502 | |
| 24503 case HostPortal: | |
| 24504 { | |
| 24505 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24506 commitReconciliationEffects(finishedWork); | |
| 24507 | |
| 24508 return; | |
| 24509 } | |
| 24510 | |
| 24511 case SuspenseComponent: | |
| 24512 { | |
| 24513 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24514 commitReconciliationEffects(finishedWork); | |
| 24515 var offscreenFiber = finishedWork.child; | |
| 24516 | |
| 24517 if (offscreenFiber.flags & Visibility) { | |
| 24518 var offscreenInstance = offscreenFiber.stateNode; | |
| 24519 var newState = offscreenFiber.memoizedState; | |
| 24520 var isHidden = newState !== null; // Track the current state on the Offscreen instance so we can | |
| 24521 // read it during an event | |
| 24522 | |
| 24523 offscreenInstance.isHidden = isHidden; | |
| 24524 | |
| 24525 if (isHidden) { | |
| 24526 var wasHidden = offscreenFiber.alternate !== null && offscreenFiber.alternate.memoizedState !== null; | |
| 24527 | |
| 24528 if (!wasHidden) { | |
| 24529 // TODO: Move to passive phase | |
| 24530 markCommitTimeOfFallback(); | |
| 24531 } | |
| 24532 } | |
| 24533 } | |
| 24534 | |
| 24535 if (flags & Update) { | |
| 24536 try { | |
| 24537 commitSuspenseCallback(finishedWork); | |
| 24538 } catch (error) { | |
| 24539 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24540 } | |
| 24541 | |
| 24542 attachSuspenseRetryListeners(finishedWork); | |
| 24543 } | |
| 24544 | |
| 24545 return; | |
| 24546 } | |
| 24547 | |
| 24548 case OffscreenComponent: | |
| 24549 { | |
| 24550 var _wasHidden = current !== null && current.memoizedState !== null; | |
| 24551 | |
| 24552 if ( // TODO: Remove this dead flag | |
| 24553 finishedWork.mode & ConcurrentMode) { | |
| 24554 // Before committing the children, track on the stack whether this | |
| 24555 // offscreen subtree was already hidden, so that we don't unmount the | |
| 24556 // effects again. | |
| 24557 var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden; | |
| 24558 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden || _wasHidden; | |
| 24559 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24560 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden; | |
| 24561 } else { | |
| 24562 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24563 } | |
| 24564 | |
| 24565 commitReconciliationEffects(finishedWork); | |
| 24566 | |
| 24567 if (flags & Visibility) { | |
| 24568 var _offscreenInstance = finishedWork.stateNode; | |
| 24569 var _newState = finishedWork.memoizedState; | |
| 24570 | |
| 24571 var _isHidden = _newState !== null; | |
| 24572 | |
| 24573 var offscreenBoundary = finishedWork; // Track the current state on the Offscreen instance so we can | |
| 24574 // read it during an event | |
| 24575 | |
| 24576 _offscreenInstance.isHidden = _isHidden; | |
| 24577 | |
| 24578 { | |
| 24579 if (_isHidden) { | |
| 24580 if (!_wasHidden) { | |
| 24581 if ((offscreenBoundary.mode & ConcurrentMode) !== NoMode) { | |
| 24582 nextEffect = offscreenBoundary; | |
| 24583 var offscreenChild = offscreenBoundary.child; | |
| 24584 | |
| 24585 while (offscreenChild !== null) { | |
| 24586 nextEffect = offscreenChild; | |
| 24587 disappearLayoutEffects_begin(offscreenChild); | |
| 24588 offscreenChild = offscreenChild.sibling; | |
| 24589 } | |
| 24590 } | |
| 24591 } | |
| 24592 } | |
| 24593 } | |
| 24594 | |
| 24595 { | |
| 24596 // TODO: This needs to run whenever there's an insertion or update | |
| 24597 // inside a hidden Offscreen tree. | |
| 24598 hideOrUnhideAllChildren(offscreenBoundary, _isHidden); | |
| 24599 } | |
| 24600 } | |
| 24601 | |
| 24602 return; | |
| 24603 } | |
| 24604 | |
| 24605 case SuspenseListComponent: | |
| 24606 { | |
| 24607 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24608 commitReconciliationEffects(finishedWork); | |
| 24609 | |
| 24610 if (flags & Update) { | |
| 24611 attachSuspenseRetryListeners(finishedWork); | |
| 24612 } | |
| 24613 | |
| 24614 return; | |
| 24615 } | |
| 24616 | |
| 24617 case ScopeComponent: | |
| 24618 { | |
| 24619 | |
| 24620 return; | |
| 24621 } | |
| 24622 | |
| 24623 default: | |
| 24624 { | |
| 24625 recursivelyTraverseMutationEffects(root, finishedWork); | |
| 24626 commitReconciliationEffects(finishedWork); | |
| 24627 return; | |
| 24628 } | |
| 24629 } | |
| 24630 } | |
| 24631 | |
| 24632 function commitReconciliationEffects(finishedWork) { | |
| 24633 // Placement effects (insertions, reorders) can be scheduled on any fiber | |
| 24634 // type. They needs to happen after the children effects have fired, but | |
| 24635 // before the effects on this fiber have fired. | |
| 24636 var flags = finishedWork.flags; | |
| 24637 | |
| 24638 if (flags & Placement) { | |
| 24639 try { | |
| 24640 commitPlacement(finishedWork); | |
| 24641 } catch (error) { | |
| 24642 captureCommitPhaseError(finishedWork, finishedWork.return, error); | |
| 24643 } // Clear the "placement" from effect tag so that we know that this is | |
| 24644 // inserted, before any life-cycles like componentDidMount gets called. | |
| 24645 // TODO: findDOMNode doesn't rely on this any more but isMounted does | |
| 24646 // and isMounted is deprecated anyway so we should be able to kill this. | |
| 24647 | |
| 24648 | |
| 24649 finishedWork.flags &= ~Placement; | |
| 24650 } | |
| 24651 | |
| 24652 if (flags & Hydrating) { | |
| 24653 finishedWork.flags &= ~Hydrating; | |
| 24654 } | |
| 24655 } | |
| 24656 | |
| 24657 function commitLayoutEffects(finishedWork, root, committedLanes) { | |
| 24658 inProgressLanes = committedLanes; | |
| 24659 inProgressRoot = root; | |
| 24660 nextEffect = finishedWork; | |
| 24661 commitLayoutEffects_begin(finishedWork, root, committedLanes); | |
| 24662 inProgressLanes = null; | |
| 24663 inProgressRoot = null; | |
| 24664 } | |
| 24665 | |
| 24666 function commitLayoutEffects_begin(subtreeRoot, root, committedLanes) { | |
| 24667 // Suspense layout effects semantics don't change for legacy roots. | |
| 24668 var isModernRoot = (subtreeRoot.mode & ConcurrentMode) !== NoMode; | |
| 24669 | |
| 24670 while (nextEffect !== null) { | |
| 24671 var fiber = nextEffect; | |
| 24672 var firstChild = fiber.child; | |
| 24673 | |
| 24674 if ( fiber.tag === OffscreenComponent && isModernRoot) { | |
| 24675 // Keep track of the current Offscreen stack's state. | |
| 24676 var isHidden = fiber.memoizedState !== null; | |
| 24677 var newOffscreenSubtreeIsHidden = isHidden || offscreenSubtreeIsHidden; | |
| 24678 | |
| 24679 if (newOffscreenSubtreeIsHidden) { | |
| 24680 // The Offscreen tree is hidden. Skip over its layout effects. | |
| 24681 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes); | |
| 24682 continue; | |
| 24683 } else { | |
| 24684 // TODO (Offscreen) Also check: subtreeFlags & LayoutMask | |
| 24685 var current = fiber.alternate; | |
| 24686 var wasHidden = current !== null && current.memoizedState !== null; | |
| 24687 var newOffscreenSubtreeWasHidden = wasHidden || offscreenSubtreeWasHidden; | |
| 24688 var prevOffscreenSubtreeIsHidden = offscreenSubtreeIsHidden; | |
| 24689 var prevOffscreenSubtreeWasHidden = offscreenSubtreeWasHidden; // Traverse the Offscreen subtree with the current Offscreen as the root. | |
| 24690 | |
| 24691 offscreenSubtreeIsHidden = newOffscreenSubtreeIsHidden; | |
| 24692 offscreenSubtreeWasHidden = newOffscreenSubtreeWasHidden; | |
| 24693 | |
| 24694 if (offscreenSubtreeWasHidden && !prevOffscreenSubtreeWasHidden) { | |
| 24695 // This is the root of a reappearing boundary. Turn its layout effects | |
| 24696 // back on. | |
| 24697 nextEffect = fiber; | |
| 24698 reappearLayoutEffects_begin(fiber); | |
| 24699 } | |
| 24700 | |
| 24701 var child = firstChild; | |
| 24702 | |
| 24703 while (child !== null) { | |
| 24704 nextEffect = child; | |
| 24705 commitLayoutEffects_begin(child, // New root; bubble back up to here and stop. | |
| 24706 root, committedLanes); | |
| 24707 child = child.sibling; | |
| 24708 } // Restore Offscreen state and resume in our-progress traversal. | |
| 24709 | |
| 24710 | |
| 24711 nextEffect = fiber; | |
| 24712 offscreenSubtreeIsHidden = prevOffscreenSubtreeIsHidden; | |
| 24713 offscreenSubtreeWasHidden = prevOffscreenSubtreeWasHidden; | |
| 24714 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes); | |
| 24715 continue; | |
| 24716 } | |
| 24717 } | |
| 24718 | |
| 24719 if ((fiber.subtreeFlags & LayoutMask) !== NoFlags && firstChild !== null) { | |
| 24720 firstChild.return = fiber; | |
| 24721 nextEffect = firstChild; | |
| 24722 } else { | |
| 24723 commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes); | |
| 24724 } | |
| 24725 } | |
| 24726 } | |
| 24727 | |
| 24728 function commitLayoutMountEffects_complete(subtreeRoot, root, committedLanes) { | |
| 24729 while (nextEffect !== null) { | |
| 24730 var fiber = nextEffect; | |
| 24731 | |
| 24732 if ((fiber.flags & LayoutMask) !== NoFlags) { | |
| 24733 var current = fiber.alternate; | |
| 24734 setCurrentFiber(fiber); | |
| 24735 | |
| 24736 try { | |
| 24737 commitLayoutEffectOnFiber(root, current, fiber, committedLanes); | |
| 24738 } catch (error) { | |
| 24739 captureCommitPhaseError(fiber, fiber.return, error); | |
| 24740 } | |
| 24741 | |
| 24742 resetCurrentFiber(); | |
| 24743 } | |
| 24744 | |
| 24745 if (fiber === subtreeRoot) { | |
| 24746 nextEffect = null; | |
| 24747 return; | |
| 24748 } | |
| 24749 | |
| 24750 var sibling = fiber.sibling; | |
| 24751 | |
| 24752 if (sibling !== null) { | |
| 24753 sibling.return = fiber.return; | |
| 24754 nextEffect = sibling; | |
| 24755 return; | |
| 24756 } | |
| 24757 | |
| 24758 nextEffect = fiber.return; | |
| 24759 } | |
| 24760 } | |
| 24761 | |
| 24762 function disappearLayoutEffects_begin(subtreeRoot) { | |
| 24763 while (nextEffect !== null) { | |
| 24764 var fiber = nextEffect; | |
| 24765 var firstChild = fiber.child; // TODO (Offscreen) Check: flags & (RefStatic | LayoutStatic) | |
| 24766 | |
| 24767 switch (fiber.tag) { | |
| 24768 case FunctionComponent: | |
| 24769 case ForwardRef: | |
| 24770 case MemoComponent: | |
| 24771 case SimpleMemoComponent: | |
| 24772 { | |
| 24773 if ( fiber.mode & ProfileMode) { | |
| 24774 try { | |
| 24775 startLayoutEffectTimer(); | |
| 24776 commitHookEffectListUnmount(Layout, fiber, fiber.return); | |
| 24777 } finally { | |
| 24778 recordLayoutEffectDuration(fiber); | |
| 24779 } | |
| 24780 } else { | |
| 24781 commitHookEffectListUnmount(Layout, fiber, fiber.return); | |
| 24782 } | |
| 24783 | |
| 24784 break; | |
| 24785 } | |
| 24786 | |
| 24787 case ClassComponent: | |
| 24788 { | |
| 24789 // TODO (Offscreen) Check: flags & RefStatic | |
| 24790 safelyDetachRef(fiber, fiber.return); | |
| 24791 var instance = fiber.stateNode; | |
| 24792 | |
| 24793 if (typeof instance.componentWillUnmount === 'function') { | |
| 24794 safelyCallComponentWillUnmount(fiber, fiber.return, instance); | |
| 24795 } | |
| 24796 | |
| 24797 break; | |
| 24798 } | |
| 24799 | |
| 24800 case HostComponent: | |
| 24801 { | |
| 24802 safelyDetachRef(fiber, fiber.return); | |
| 24803 break; | |
| 24804 } | |
| 24805 | |
| 24806 case OffscreenComponent: | |
| 24807 { | |
| 24808 // Check if this is a | |
| 24809 var isHidden = fiber.memoizedState !== null; | |
| 24810 | |
| 24811 if (isHidden) { | |
| 24812 // Nested Offscreen tree is already hidden. Don't disappear | |
| 24813 // its effects. | |
| 24814 disappearLayoutEffects_complete(subtreeRoot); | |
| 24815 continue; | |
| 24816 } | |
| 24817 | |
| 24818 break; | |
| 24819 } | |
| 24820 } // TODO (Offscreen) Check: subtreeFlags & LayoutStatic | |
| 24821 | |
| 24822 | |
| 24823 if (firstChild !== null) { | |
| 24824 firstChild.return = fiber; | |
| 24825 nextEffect = firstChild; | |
| 24826 } else { | |
| 24827 disappearLayoutEffects_complete(subtreeRoot); | |
| 24828 } | |
| 24829 } | |
| 24830 } | |
| 24831 | |
| 24832 function disappearLayoutEffects_complete(subtreeRoot) { | |
| 24833 while (nextEffect !== null) { | |
| 24834 var fiber = nextEffect; | |
| 24835 | |
| 24836 if (fiber === subtreeRoot) { | |
| 24837 nextEffect = null; | |
| 24838 return; | |
| 24839 } | |
| 24840 | |
| 24841 var sibling = fiber.sibling; | |
| 24842 | |
| 24843 if (sibling !== null) { | |
| 24844 sibling.return = fiber.return; | |
| 24845 nextEffect = sibling; | |
| 24846 return; | |
| 24847 } | |
| 24848 | |
| 24849 nextEffect = fiber.return; | |
| 24850 } | |
| 24851 } | |
| 24852 | |
| 24853 function reappearLayoutEffects_begin(subtreeRoot) { | |
| 24854 while (nextEffect !== null) { | |
| 24855 var fiber = nextEffect; | |
| 24856 var firstChild = fiber.child; | |
| 24857 | |
| 24858 if (fiber.tag === OffscreenComponent) { | |
| 24859 var isHidden = fiber.memoizedState !== null; | |
| 24860 | |
| 24861 if (isHidden) { | |
| 24862 // Nested Offscreen tree is still hidden. Don't re-appear its effects. | |
| 24863 reappearLayoutEffects_complete(subtreeRoot); | |
| 24864 continue; | |
| 24865 } | |
| 24866 } // TODO (Offscreen) Check: subtreeFlags & LayoutStatic | |
| 24867 | |
| 24868 | |
| 24869 if (firstChild !== null) { | |
| 24870 // This node may have been reused from a previous render, so we can't | |
| 24871 // assume its return pointer is correct. | |
| 24872 firstChild.return = fiber; | |
| 24873 nextEffect = firstChild; | |
| 24874 } else { | |
| 24875 reappearLayoutEffects_complete(subtreeRoot); | |
| 24876 } | |
| 24877 } | |
| 24878 } | |
| 24879 | |
| 24880 function reappearLayoutEffects_complete(subtreeRoot) { | |
| 24881 while (nextEffect !== null) { | |
| 24882 var fiber = nextEffect; // TODO (Offscreen) Check: flags & LayoutStatic | |
| 24883 | |
| 24884 setCurrentFiber(fiber); | |
| 24885 | |
| 24886 try { | |
| 24887 reappearLayoutEffectsOnFiber(fiber); | |
| 24888 } catch (error) { | |
| 24889 captureCommitPhaseError(fiber, fiber.return, error); | |
| 24890 } | |
| 24891 | |
| 24892 resetCurrentFiber(); | |
| 24893 | |
| 24894 if (fiber === subtreeRoot) { | |
| 24895 nextEffect = null; | |
| 24896 return; | |
| 24897 } | |
| 24898 | |
| 24899 var sibling = fiber.sibling; | |
| 24900 | |
| 24901 if (sibling !== null) { | |
| 24902 // This node may have been reused from a previous render, so we can't | |
| 24903 // assume its return pointer is correct. | |
| 24904 sibling.return = fiber.return; | |
| 24905 nextEffect = sibling; | |
| 24906 return; | |
| 24907 } | |
| 24908 | |
| 24909 nextEffect = fiber.return; | |
| 24910 } | |
| 24911 } | |
| 24912 | |
| 24913 function commitPassiveMountEffects(root, finishedWork, committedLanes, committedTransitions) { | |
| 24914 nextEffect = finishedWork; | |
| 24915 commitPassiveMountEffects_begin(finishedWork, root, committedLanes, committedTransitions); | |
| 24916 } | |
| 24917 | |
| 24918 function commitPassiveMountEffects_begin(subtreeRoot, root, committedLanes, committedTransitions) { | |
| 24919 while (nextEffect !== null) { | |
| 24920 var fiber = nextEffect; | |
| 24921 var firstChild = fiber.child; | |
| 24922 | |
| 24923 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && firstChild !== null) { | |
| 24924 firstChild.return = fiber; | |
| 24925 nextEffect = firstChild; | |
| 24926 } else { | |
| 24927 commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions); | |
| 24928 } | |
| 24929 } | |
| 24930 } | |
| 24931 | |
| 24932 function commitPassiveMountEffects_complete(subtreeRoot, root, committedLanes, committedTransitions) { | |
| 24933 while (nextEffect !== null) { | |
| 24934 var fiber = nextEffect; | |
| 24935 | |
| 24936 if ((fiber.flags & Passive) !== NoFlags) { | |
| 24937 setCurrentFiber(fiber); | |
| 24938 | |
| 24939 try { | |
| 24940 commitPassiveMountOnFiber(root, fiber, committedLanes, committedTransitions); | |
| 24941 } catch (error) { | |
| 24942 captureCommitPhaseError(fiber, fiber.return, error); | |
| 24943 } | |
| 24944 | |
| 24945 resetCurrentFiber(); | |
| 24946 } | |
| 24947 | |
| 24948 if (fiber === subtreeRoot) { | |
| 24949 nextEffect = null; | |
| 24950 return; | |
| 24951 } | |
| 24952 | |
| 24953 var sibling = fiber.sibling; | |
| 24954 | |
| 24955 if (sibling !== null) { | |
| 24956 sibling.return = fiber.return; | |
| 24957 nextEffect = sibling; | |
| 24958 return; | |
| 24959 } | |
| 24960 | |
| 24961 nextEffect = fiber.return; | |
| 24962 } | |
| 24963 } | |
| 24964 | |
| 24965 function commitPassiveMountOnFiber(finishedRoot, finishedWork, committedLanes, committedTransitions) { | |
| 24966 switch (finishedWork.tag) { | |
| 24967 case FunctionComponent: | |
| 24968 case ForwardRef: | |
| 24969 case SimpleMemoComponent: | |
| 24970 { | |
| 24971 if ( finishedWork.mode & ProfileMode) { | |
| 24972 startPassiveEffectTimer(); | |
| 24973 | |
| 24974 try { | |
| 24975 commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); | |
| 24976 } finally { | |
| 24977 recordPassiveEffectDuration(finishedWork); | |
| 24978 } | |
| 24979 } else { | |
| 24980 commitHookEffectListMount(Passive$1 | HasEffect, finishedWork); | |
| 24981 } | |
| 24982 | |
| 24983 break; | |
| 24984 } | |
| 24985 } | |
| 24986 } | |
| 24987 | |
| 24988 function commitPassiveUnmountEffects(firstChild) { | |
| 24989 nextEffect = firstChild; | |
| 24990 commitPassiveUnmountEffects_begin(); | |
| 24991 } | |
| 24992 | |
| 24993 function commitPassiveUnmountEffects_begin() { | |
| 24994 while (nextEffect !== null) { | |
| 24995 var fiber = nextEffect; | |
| 24996 var child = fiber.child; | |
| 24997 | |
| 24998 if ((nextEffect.flags & ChildDeletion) !== NoFlags) { | |
| 24999 var deletions = fiber.deletions; | |
| 25000 | |
| 25001 if (deletions !== null) { | |
| 25002 for (var i = 0; i < deletions.length; i++) { | |
| 25003 var fiberToDelete = deletions[i]; | |
| 25004 nextEffect = fiberToDelete; | |
| 25005 commitPassiveUnmountEffectsInsideOfDeletedTree_begin(fiberToDelete, fiber); | |
| 25006 } | |
| 25007 | |
| 25008 { | |
| 25009 // A fiber was deleted from this parent fiber, but it's still part of | |
| 25010 // the previous (alternate) parent fiber's list of children. Because | |
| 25011 // children are a linked list, an earlier sibling that's still alive | |
| 25012 // will be connected to the deleted fiber via its `alternate`: | |
| 25013 // | |
| 25014 // live fiber | |
| 25015 // --alternate--> previous live fiber | |
| 25016 // --sibling--> deleted fiber | |
| 25017 // | |
| 25018 // We can't disconnect `alternate` on nodes that haven't been deleted | |
| 25019 // yet, but we can disconnect the `sibling` and `child` pointers. | |
| 25020 var previousFiber = fiber.alternate; | |
| 25021 | |
| 25022 if (previousFiber !== null) { | |
| 25023 var detachedChild = previousFiber.child; | |
| 25024 | |
| 25025 if (detachedChild !== null) { | |
| 25026 previousFiber.child = null; | |
| 25027 | |
| 25028 do { | |
| 25029 var detachedSibling = detachedChild.sibling; | |
| 25030 detachedChild.sibling = null; | |
| 25031 detachedChild = detachedSibling; | |
| 25032 } while (detachedChild !== null); | |
| 25033 } | |
| 25034 } | |
| 25035 } | |
| 25036 | |
| 25037 nextEffect = fiber; | |
| 25038 } | |
| 25039 } | |
| 25040 | |
| 25041 if ((fiber.subtreeFlags & PassiveMask) !== NoFlags && child !== null) { | |
| 25042 child.return = fiber; | |
| 25043 nextEffect = child; | |
| 25044 } else { | |
| 25045 commitPassiveUnmountEffects_complete(); | |
| 25046 } | |
| 25047 } | |
| 25048 } | |
| 25049 | |
| 25050 function commitPassiveUnmountEffects_complete() { | |
| 25051 while (nextEffect !== null) { | |
| 25052 var fiber = nextEffect; | |
| 25053 | |
| 25054 if ((fiber.flags & Passive) !== NoFlags) { | |
| 25055 setCurrentFiber(fiber); | |
| 25056 commitPassiveUnmountOnFiber(fiber); | |
| 25057 resetCurrentFiber(); | |
| 25058 } | |
| 25059 | |
| 25060 var sibling = fiber.sibling; | |
| 25061 | |
| 25062 if (sibling !== null) { | |
| 25063 sibling.return = fiber.return; | |
| 25064 nextEffect = sibling; | |
| 25065 return; | |
| 25066 } | |
| 25067 | |
| 25068 nextEffect = fiber.return; | |
| 25069 } | |
| 25070 } | |
| 25071 | |
| 25072 function commitPassiveUnmountOnFiber(finishedWork) { | |
| 25073 switch (finishedWork.tag) { | |
| 25074 case FunctionComponent: | |
| 25075 case ForwardRef: | |
| 25076 case SimpleMemoComponent: | |
| 25077 { | |
| 25078 if ( finishedWork.mode & ProfileMode) { | |
| 25079 startPassiveEffectTimer(); | |
| 25080 commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return); | |
| 25081 recordPassiveEffectDuration(finishedWork); | |
| 25082 } else { | |
| 25083 commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork, finishedWork.return); | |
| 25084 } | |
| 25085 | |
| 25086 break; | |
| 25087 } | |
| 25088 } | |
| 25089 } | |
| 25090 | |
| 25091 function commitPassiveUnmountEffectsInsideOfDeletedTree_begin(deletedSubtreeRoot, nearestMountedAncestor) { | |
| 25092 while (nextEffect !== null) { | |
| 25093 var fiber = nextEffect; // Deletion effects fire in parent -> child order | |
| 25094 // TODO: Check if fiber has a PassiveStatic flag | |
| 25095 | |
| 25096 setCurrentFiber(fiber); | |
| 25097 commitPassiveUnmountInsideDeletedTreeOnFiber(fiber, nearestMountedAncestor); | |
| 25098 resetCurrentFiber(); | |
| 25099 var child = fiber.child; // TODO: Only traverse subtree if it has a PassiveStatic flag. (But, if we | |
| 25100 // do this, still need to handle `deletedTreeCleanUpLevel` correctly.) | |
| 25101 | |
| 25102 if (child !== null) { | |
| 25103 child.return = fiber; | |
| 25104 nextEffect = child; | |
| 25105 } else { | |
| 25106 commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot); | |
| 25107 } | |
| 25108 } | |
| 25109 } | |
| 25110 | |
| 25111 function commitPassiveUnmountEffectsInsideOfDeletedTree_complete(deletedSubtreeRoot) { | |
| 25112 while (nextEffect !== null) { | |
| 25113 var fiber = nextEffect; | |
| 25114 var sibling = fiber.sibling; | |
| 25115 var returnFiber = fiber.return; | |
| 25116 | |
| 25117 { | |
| 25118 // Recursively traverse the entire deleted tree and clean up fiber fields. | |
| 25119 // This is more aggressive than ideal, and the long term goal is to only | |
| 25120 // have to detach the deleted tree at the root. | |
| 25121 detachFiberAfterEffects(fiber); | |
| 25122 | |
| 25123 if (fiber === deletedSubtreeRoot) { | |
| 25124 nextEffect = null; | |
| 25125 return; | |
| 25126 } | |
| 25127 } | |
| 25128 | |
| 25129 if (sibling !== null) { | |
| 25130 sibling.return = returnFiber; | |
| 25131 nextEffect = sibling; | |
| 25132 return; | |
| 25133 } | |
| 25134 | |
| 25135 nextEffect = returnFiber; | |
| 25136 } | |
| 25137 } | |
| 25138 | |
| 25139 function commitPassiveUnmountInsideDeletedTreeOnFiber(current, nearestMountedAncestor) { | |
| 25140 switch (current.tag) { | |
| 25141 case FunctionComponent: | |
| 25142 case ForwardRef: | |
| 25143 case SimpleMemoComponent: | |
| 25144 { | |
| 25145 if ( current.mode & ProfileMode) { | |
| 25146 startPassiveEffectTimer(); | |
| 25147 commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); | |
| 25148 recordPassiveEffectDuration(current); | |
| 25149 } else { | |
| 25150 commitHookEffectListUnmount(Passive$1, current, nearestMountedAncestor); | |
| 25151 } | |
| 25152 | |
| 25153 break; | |
| 25154 } | |
| 25155 } | |
| 25156 } // TODO: Reuse reappearLayoutEffects traversal here? | |
| 25157 | |
| 25158 | |
| 25159 function invokeLayoutEffectMountInDEV(fiber) { | |
| 25160 { | |
| 25161 // We don't need to re-check StrictEffectsMode here. | |
| 25162 // This function is only called if that check has already passed. | |
| 25163 switch (fiber.tag) { | |
| 25164 case FunctionComponent: | |
| 25165 case ForwardRef: | |
| 25166 case SimpleMemoComponent: | |
| 25167 { | |
| 25168 try { | |
| 25169 commitHookEffectListMount(Layout | HasEffect, fiber); | |
| 25170 } catch (error) { | |
| 25171 captureCommitPhaseError(fiber, fiber.return, error); | |
| 25172 } | |
| 25173 | |
| 25174 break; | |
| 25175 } | |
| 25176 | |
| 25177 case ClassComponent: | |
| 25178 { | |
| 25179 var instance = fiber.stateNode; | |
| 25180 | |
| 25181 try { | |
| 25182 instance.componentDidMount(); | |
| 25183 } catch (error) { | |
| 25184 captureCommitPhaseError(fiber, fiber.return, error); | |
| 25185 } | |
| 25186 | |
| 25187 break; | |
| 25188 } | |
| 25189 } | |
| 25190 } | |
| 25191 } | |
| 25192 | |
| 25193 function invokePassiveEffectMountInDEV(fiber) { | |
| 25194 { | |
| 25195 // We don't need to re-check StrictEffectsMode here. | |
| 25196 // This function is only called if that check has already passed. | |
| 25197 switch (fiber.tag) { | |
| 25198 case FunctionComponent: | |
| 25199 case ForwardRef: | |
| 25200 case SimpleMemoComponent: | |
| 25201 { | |
| 25202 try { | |
| 25203 commitHookEffectListMount(Passive$1 | HasEffect, fiber); | |
| 25204 } catch (error) { | |
| 25205 captureCommitPhaseError(fiber, fiber.return, error); | |
| 25206 } | |
| 25207 | |
| 25208 break; | |
| 25209 } | |
| 25210 } | |
| 25211 } | |
| 25212 } | |
| 25213 | |
| 25214 function invokeLayoutEffectUnmountInDEV(fiber) { | |
| 25215 { | |
| 25216 // We don't need to re-check StrictEffectsMode here. | |
| 25217 // This function is only called if that check has already passed. | |
| 25218 switch (fiber.tag) { | |
| 25219 case FunctionComponent: | |
| 25220 case ForwardRef: | |
| 25221 case SimpleMemoComponent: | |
| 25222 { | |
| 25223 try { | |
| 25224 commitHookEffectListUnmount(Layout | HasEffect, fiber, fiber.return); | |
| 25225 } catch (error) { | |
| 25226 captureCommitPhaseError(fiber, fiber.return, error); | |
| 25227 } | |
| 25228 | |
| 25229 break; | |
| 25230 } | |
| 25231 | |
| 25232 case ClassComponent: | |
| 25233 { | |
| 25234 var instance = fiber.stateNode; | |
| 25235 | |
| 25236 if (typeof instance.componentWillUnmount === 'function') { | |
| 25237 safelyCallComponentWillUnmount(fiber, fiber.return, instance); | |
| 25238 } | |
| 25239 | |
| 25240 break; | |
| 25241 } | |
| 25242 } | |
| 25243 } | |
| 25244 } | |
| 25245 | |
| 25246 function invokePassiveEffectUnmountInDEV(fiber) { | |
| 25247 { | |
| 25248 // We don't need to re-check StrictEffectsMode here. | |
| 25249 // This function is only called if that check has already passed. | |
| 25250 switch (fiber.tag) { | |
| 25251 case FunctionComponent: | |
| 25252 case ForwardRef: | |
| 25253 case SimpleMemoComponent: | |
| 25254 { | |
| 25255 try { | |
| 25256 commitHookEffectListUnmount(Passive$1 | HasEffect, fiber, fiber.return); | |
| 25257 } catch (error) { | |
| 25258 captureCommitPhaseError(fiber, fiber.return, error); | |
| 25259 } | |
| 25260 } | |
| 25261 } | |
| 25262 } | |
| 25263 } | |
| 25264 | |
| 25265 var COMPONENT_TYPE = 0; | |
| 25266 var HAS_PSEUDO_CLASS_TYPE = 1; | |
| 25267 var ROLE_TYPE = 2; | |
| 25268 var TEST_NAME_TYPE = 3; | |
| 25269 var TEXT_TYPE = 4; | |
| 25270 | |
| 25271 if (typeof Symbol === 'function' && Symbol.for) { | |
| 25272 var symbolFor = Symbol.for; | |
| 25273 COMPONENT_TYPE = symbolFor('selector.component'); | |
| 25274 HAS_PSEUDO_CLASS_TYPE = symbolFor('selector.has_pseudo_class'); | |
| 25275 ROLE_TYPE = symbolFor('selector.role'); | |
| 25276 TEST_NAME_TYPE = symbolFor('selector.test_id'); | |
| 25277 TEXT_TYPE = symbolFor('selector.text'); | |
| 25278 } | |
| 25279 var commitHooks = []; | |
| 25280 function onCommitRoot$1() { | |
| 25281 { | |
| 25282 commitHooks.forEach(function (commitHook) { | |
| 25283 return commitHook(); | |
| 25284 }); | |
| 25285 } | |
| 25286 } | |
| 25287 | |
| 25288 var ReactCurrentActQueue = ReactSharedInternals.ReactCurrentActQueue; | |
| 25289 function isLegacyActEnvironment(fiber) { | |
| 25290 { | |
| 25291 // Legacy mode. We preserve the behavior of React 17's act. It assumes an | |
| 25292 // act environment whenever `jest` is defined, but you can still turn off | |
| 25293 // spurious warnings by setting IS_REACT_ACT_ENVIRONMENT explicitly | |
| 25294 // to false. | |
| 25295 var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global | |
| 25296 typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined; // $FlowExpectedError - Flow doesn't know about jest | |
| 25297 | |
| 25298 var jestIsDefined = typeof jest !== 'undefined'; | |
| 25299 return jestIsDefined && isReactActEnvironmentGlobal !== false; | |
| 25300 } | |
| 25301 } | |
| 25302 function isConcurrentActEnvironment() { | |
| 25303 { | |
| 25304 var isReactActEnvironmentGlobal = // $FlowExpectedError – Flow doesn't know about IS_REACT_ACT_ENVIRONMENT global | |
| 25305 typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined' ? IS_REACT_ACT_ENVIRONMENT : undefined; | |
| 25306 | |
| 25307 if (!isReactActEnvironmentGlobal && ReactCurrentActQueue.current !== null) { | |
| 25308 // TODO: Include link to relevant documentation page. | |
| 25309 error('The current testing environment is not configured to support ' + 'act(...)'); | |
| 25310 } | |
| 25311 | |
| 25312 return isReactActEnvironmentGlobal; | |
| 25313 } | |
| 25314 } | |
| 25315 | |
| 25316 var ceil = Math.ceil; | |
| 25317 var ReactCurrentDispatcher$2 = ReactSharedInternals.ReactCurrentDispatcher, | |
| 25318 ReactCurrentOwner$2 = ReactSharedInternals.ReactCurrentOwner, | |
| 25319 ReactCurrentBatchConfig$3 = ReactSharedInternals.ReactCurrentBatchConfig, | |
| 25320 ReactCurrentActQueue$1 = ReactSharedInternals.ReactCurrentActQueue; | |
| 25321 var NoContext = | |
| 25322 /* */ | |
| 25323 0; | |
| 25324 var BatchedContext = | |
| 25325 /* */ | |
| 25326 1; | |
| 25327 var RenderContext = | |
| 25328 /* */ | |
| 25329 2; | |
| 25330 var CommitContext = | |
| 25331 /* */ | |
| 25332 4; | |
| 25333 var RootInProgress = 0; | |
| 25334 var RootFatalErrored = 1; | |
| 25335 var RootErrored = 2; | |
| 25336 var RootSuspended = 3; | |
| 25337 var RootSuspendedWithDelay = 4; | |
| 25338 var RootCompleted = 5; | |
| 25339 var RootDidNotComplete = 6; // Describes where we are in the React execution stack | |
| 25340 | |
| 25341 var executionContext = NoContext; // The root we're working on | |
| 25342 | |
| 25343 var workInProgressRoot = null; // The fiber we're working on | |
| 25344 | |
| 25345 var workInProgress = null; // The lanes we're rendering | |
| 25346 | |
| 25347 var workInProgressRootRenderLanes = NoLanes; // Stack that allows components to change the render lanes for its subtree | |
| 25348 // This is a superset of the lanes we started working on at the root. The only | |
| 25349 // case where it's different from `workInProgressRootRenderLanes` is when we | |
| 25350 // enter a subtree that is hidden and needs to be unhidden: Suspense and | |
| 25351 // Offscreen component. | |
| 25352 // | |
| 25353 // Most things in the work loop should deal with workInProgressRootRenderLanes. | |
| 25354 // Most things in begin/complete phases should deal with subtreeRenderLanes. | |
| 25355 | |
| 25356 var subtreeRenderLanes = NoLanes; | |
| 25357 var subtreeRenderLanesCursor = createCursor(NoLanes); // Whether to root completed, errored, suspended, etc. | |
| 25358 | |
| 25359 var workInProgressRootExitStatus = RootInProgress; // A fatal error, if one is thrown | |
| 25360 | |
| 25361 var workInProgressRootFatalError = null; // "Included" lanes refer to lanes that were worked on during this render. It's | |
| 25362 // slightly different than `renderLanes` because `renderLanes` can change as you | |
| 25363 // enter and exit an Offscreen tree. This value is the combination of all render | |
| 25364 // lanes for the entire render phase. | |
| 25365 | |
| 25366 var workInProgressRootIncludedLanes = NoLanes; // The work left over by components that were visited during this render. Only | |
| 25367 // includes unprocessed updates, not work in bailed out children. | |
| 25368 | |
| 25369 var workInProgressRootSkippedLanes = NoLanes; // Lanes that were updated (in an interleaved event) during this render. | |
| 25370 | |
| 25371 var workInProgressRootInterleavedUpdatedLanes = NoLanes; // Lanes that were updated during the render phase (*not* an interleaved event). | |
| 25372 | |
| 25373 var workInProgressRootPingedLanes = NoLanes; // Errors that are thrown during the render phase. | |
| 25374 | |
| 25375 var workInProgressRootConcurrentErrors = null; // These are errors that we recovered from without surfacing them to the UI. | |
| 25376 // We will log them once the tree commits. | |
| 25377 | |
| 25378 var workInProgressRootRecoverableErrors = null; // The most recent time we committed a fallback. This lets us ensure a train | |
| 25379 // model where we don't commit new loading states in too quick succession. | |
| 25380 | |
| 25381 var globalMostRecentFallbackTime = 0; | |
| 25382 var FALLBACK_THROTTLE_MS = 500; // The absolute time for when we should start giving up on rendering | |
| 25383 // more and prefer CPU suspense heuristics instead. | |
| 25384 | |
| 25385 var workInProgressRootRenderTargetTime = Infinity; // How long a render is supposed to take before we start following CPU | |
| 25386 // suspense heuristics and opt out of rendering more content. | |
| 25387 | |
| 25388 var RENDER_TIMEOUT_MS = 500; | |
| 25389 var workInProgressTransitions = null; | |
| 25390 | |
| 25391 function resetRenderTimer() { | |
| 25392 workInProgressRootRenderTargetTime = now() + RENDER_TIMEOUT_MS; | |
| 25393 } | |
| 25394 | |
| 25395 function getRenderTargetTime() { | |
| 25396 return workInProgressRootRenderTargetTime; | |
| 25397 } | |
| 25398 var hasUncaughtError = false; | |
| 25399 var firstUncaughtError = null; | |
| 25400 var legacyErrorBoundariesThatAlreadyFailed = null; // Only used when enableProfilerNestedUpdateScheduledHook is true; | |
| 25401 var rootDoesHavePassiveEffects = false; | |
| 25402 var rootWithPendingPassiveEffects = null; | |
| 25403 var pendingPassiveEffectsLanes = NoLanes; | |
| 25404 var pendingPassiveProfilerEffects = []; | |
| 25405 var pendingPassiveTransitions = null; // Use these to prevent an infinite loop of nested updates | |
| 25406 | |
| 25407 var NESTED_UPDATE_LIMIT = 50; | |
| 25408 var nestedUpdateCount = 0; | |
| 25409 var rootWithNestedUpdates = null; | |
| 25410 var isFlushingPassiveEffects = false; | |
| 25411 var didScheduleUpdateDuringPassiveEffects = false; | |
| 25412 var NESTED_PASSIVE_UPDATE_LIMIT = 50; | |
| 25413 var nestedPassiveUpdateCount = 0; | |
| 25414 var rootWithPassiveNestedUpdates = null; // If two updates are scheduled within the same event, we should treat their | |
| 25415 // event times as simultaneous, even if the actual clock time has advanced | |
| 25416 // between the first and second call. | |
| 25417 | |
| 25418 var currentEventTime = NoTimestamp; | |
| 25419 var currentEventTransitionLane = NoLanes; | |
| 25420 var isRunningInsertionEffect = false; | |
| 25421 function getWorkInProgressRoot() { | |
| 25422 return workInProgressRoot; | |
| 25423 } | |
| 25424 function requestEventTime() { | |
| 25425 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { | |
| 25426 // We're inside React, so it's fine to read the actual time. | |
| 25427 return now(); | |
| 25428 } // We're not inside React, so we may be in the middle of a browser event. | |
| 25429 | |
| 25430 | |
| 25431 if (currentEventTime !== NoTimestamp) { | |
| 25432 // Use the same start time for all updates until we enter React again. | |
| 25433 return currentEventTime; | |
| 25434 } // This is the first update since React yielded. Compute a new start time. | |
| 25435 | |
| 25436 | |
| 25437 currentEventTime = now(); | |
| 25438 return currentEventTime; | |
| 25439 } | |
| 25440 function requestUpdateLane(fiber) { | |
| 25441 // Special cases | |
| 25442 var mode = fiber.mode; | |
| 25443 | |
| 25444 if ((mode & ConcurrentMode) === NoMode) { | |
| 25445 return SyncLane; | |
| 25446 } else if ( (executionContext & RenderContext) !== NoContext && workInProgressRootRenderLanes !== NoLanes) { | |
| 25447 // This is a render phase update. These are not officially supported. The | |
| 25448 // old behavior is to give this the same "thread" (lanes) as | |
| 25449 // whatever is currently rendering. So if you call `setState` on a component | |
| 25450 // that happens later in the same render, it will flush. Ideally, we want to | |
| 25451 // remove the special case and treat them as if they came from an | |
| 25452 // interleaved event. Regardless, this pattern is not officially supported. | |
| 25453 // This behavior is only a fallback. The flag only exists until we can roll | |
| 25454 // out the setState warning, since existing code might accidentally rely on | |
| 25455 // the current behavior. | |
| 25456 return pickArbitraryLane(workInProgressRootRenderLanes); | |
| 25457 } | |
| 25458 | |
| 25459 var isTransition = requestCurrentTransition() !== NoTransition; | |
| 25460 | |
| 25461 if (isTransition) { | |
| 25462 if ( ReactCurrentBatchConfig$3.transition !== null) { | |
| 25463 var transition = ReactCurrentBatchConfig$3.transition; | |
| 25464 | |
| 25465 if (!transition._updatedFibers) { | |
| 25466 transition._updatedFibers = new Set(); | |
| 25467 } | |
| 25468 | |
| 25469 transition._updatedFibers.add(fiber); | |
| 25470 } // The algorithm for assigning an update to a lane should be stable for all | |
| 25471 // updates at the same priority within the same event. To do this, the | |
| 25472 // inputs to the algorithm must be the same. | |
| 25473 // | |
| 25474 // The trick we use is to cache the first of each of these inputs within an | |
| 25475 // event. Then reset the cached values once we can be sure the event is | |
| 25476 // over. Our heuristic for that is whenever we enter a concurrent work loop. | |
| 25477 | |
| 25478 | |
| 25479 if (currentEventTransitionLane === NoLane) { | |
| 25480 // All transitions within the same event are assigned the same lane. | |
| 25481 currentEventTransitionLane = claimNextTransitionLane(); | |
| 25482 } | |
| 25483 | |
| 25484 return currentEventTransitionLane; | |
| 25485 } // Updates originating inside certain React methods, like flushSync, have | |
| 25486 // their priority set by tracking it with a context variable. | |
| 25487 // | |
| 25488 // The opaque type returned by the host config is internally a lane, so we can | |
| 25489 // use that directly. | |
| 25490 // TODO: Move this type conversion to the event priority module. | |
| 25491 | |
| 25492 | |
| 25493 var updateLane = getCurrentUpdatePriority(); | |
| 25494 | |
| 25495 if (updateLane !== NoLane) { | |
| 25496 return updateLane; | |
| 25497 } // This update originated outside React. Ask the host environment for an | |
| 25498 // appropriate priority, based on the type of event. | |
| 25499 // | |
| 25500 // The opaque type returned by the host config is internally a lane, so we can | |
| 25501 // use that directly. | |
| 25502 // TODO: Move this type conversion to the event priority module. | |
| 25503 | |
| 25504 | |
| 25505 var eventLane = getCurrentEventPriority(); | |
| 25506 return eventLane; | |
| 25507 } | |
| 25508 | |
| 25509 function requestRetryLane(fiber) { | |
| 25510 // This is a fork of `requestUpdateLane` designed specifically for Suspense | |
| 25511 // "retries" — a special update that attempts to flip a Suspense boundary | |
| 25512 // from its placeholder state to its primary/resolved state. | |
| 25513 // Special cases | |
| 25514 var mode = fiber.mode; | |
| 25515 | |
| 25516 if ((mode & ConcurrentMode) === NoMode) { | |
| 25517 return SyncLane; | |
| 25518 } | |
| 25519 | |
| 25520 return claimNextRetryLane(); | |
| 25521 } | |
| 25522 | |
| 25523 function scheduleUpdateOnFiber(root, fiber, lane, eventTime) { | |
| 25524 checkForNestedUpdates(); | |
| 25525 | |
| 25526 { | |
| 25527 if (isRunningInsertionEffect) { | |
| 25528 error('useInsertionEffect must not schedule updates.'); | |
| 25529 } | |
| 25530 } | |
| 25531 | |
| 25532 { | |
| 25533 if (isFlushingPassiveEffects) { | |
| 25534 didScheduleUpdateDuringPassiveEffects = true; | |
| 25535 } | |
| 25536 } // Mark that the root has a pending update. | |
| 25537 | |
| 25538 | |
| 25539 markRootUpdated(root, lane, eventTime); | |
| 25540 | |
| 25541 if ((executionContext & RenderContext) !== NoLanes && root === workInProgressRoot) { | |
| 25542 // This update was dispatched during the render phase. This is a mistake | |
| 25543 // if the update originates from user space (with the exception of local | |
| 25544 // hook updates, which are handled differently and don't reach this | |
| 25545 // function), but there are some internal React features that use this as | |
| 25546 // an implementation detail, like selective hydration. | |
| 25547 warnAboutRenderPhaseUpdatesInDEV(fiber); // Track lanes that were updated during the render phase | |
| 25548 } else { | |
| 25549 // This is a normal update, scheduled from outside the render phase. For | |
| 25550 // example, during an input event. | |
| 25551 { | |
| 25552 if (isDevToolsPresent) { | |
| 25553 addFiberToLanesMap(root, fiber, lane); | |
| 25554 } | |
| 25555 } | |
| 25556 | |
| 25557 warnIfUpdatesNotWrappedWithActDEV(fiber); | |
| 25558 | |
| 25559 if (root === workInProgressRoot) { | |
| 25560 // Received an update to a tree that's in the middle of rendering. Mark | |
| 25561 // that there was an interleaved update work on this root. Unless the | |
| 25562 // `deferRenderPhaseUpdateToNextBatch` flag is off and this is a render | |
| 25563 // phase update. In that case, we don't treat render phase updates as if | |
| 25564 // they were interleaved, for backwards compat reasons. | |
| 25565 if ( (executionContext & RenderContext) === NoContext) { | |
| 25566 workInProgressRootInterleavedUpdatedLanes = mergeLanes(workInProgressRootInterleavedUpdatedLanes, lane); | |
| 25567 } | |
| 25568 | |
| 25569 if (workInProgressRootExitStatus === RootSuspendedWithDelay) { | |
| 25570 // The root already suspended with a delay, which means this render | |
| 25571 // definitely won't finish. Since we have a new update, let's mark it as | |
| 25572 // suspended now, right before marking the incoming update. This has the | |
| 25573 // effect of interrupting the current render and switching to the update. | |
| 25574 // TODO: Make sure this doesn't override pings that happen while we've | |
| 25575 // already started rendering. | |
| 25576 markRootSuspended$1(root, workInProgressRootRenderLanes); | |
| 25577 } | |
| 25578 } | |
| 25579 | |
| 25580 ensureRootIsScheduled(root, eventTime); | |
| 25581 | |
| 25582 if (lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode. | |
| 25583 !( ReactCurrentActQueue$1.isBatchingLegacy)) { | |
| 25584 // Flush the synchronous work now, unless we're already working or inside | |
| 25585 // a batch. This is intentionally inside scheduleUpdateOnFiber instead of | |
| 25586 // scheduleCallbackForFiber to preserve the ability to schedule a callback | |
| 25587 // without immediately flushing it. We only do this for user-initiated | |
| 25588 // updates, to preserve historical behavior of legacy mode. | |
| 25589 resetRenderTimer(); | |
| 25590 flushSyncCallbacksOnlyInLegacyMode(); | |
| 25591 } | |
| 25592 } | |
| 25593 } | |
| 25594 function scheduleInitialHydrationOnRoot(root, lane, eventTime) { | |
| 25595 // This is a special fork of scheduleUpdateOnFiber that is only used to | |
| 25596 // schedule the initial hydration of a root that has just been created. Most | |
| 25597 // of the stuff in scheduleUpdateOnFiber can be skipped. | |
| 25598 // | |
| 25599 // The main reason for this separate path, though, is to distinguish the | |
| 25600 // initial children from subsequent updates. In fully client-rendered roots | |
| 25601 // (createRoot instead of hydrateRoot), all top-level renders are modeled as | |
| 25602 // updates, but hydration roots are special because the initial render must | |
| 25603 // match what was rendered on the server. | |
| 25604 var current = root.current; | |
| 25605 current.lanes = lane; | |
| 25606 markRootUpdated(root, lane, eventTime); | |
| 25607 ensureRootIsScheduled(root, eventTime); | |
| 25608 } | |
| 25609 function isUnsafeClassRenderPhaseUpdate(fiber) { | |
| 25610 // Check if this is a render phase update. Only called by class components, | |
| 25611 // which special (deprecated) behavior for UNSAFE_componentWillReceive props. | |
| 25612 return (// TODO: Remove outdated deferRenderPhaseUpdateToNextBatch experiment. We | |
| 25613 // decided not to enable it. | |
| 25614 (executionContext & RenderContext) !== NoContext | |
| 25615 ); | |
| 25616 } // Use this function to schedule a task for a root. There's only one task per | |
| 25617 // root; if a task was already scheduled, we'll check to make sure the priority | |
| 25618 // of the existing task is the same as the priority of the next level that the | |
| 25619 // root has work on. This function is called on every update, and right before | |
| 25620 // exiting a task. | |
| 25621 | |
| 25622 function ensureRootIsScheduled(root, currentTime) { | |
| 25623 var existingCallbackNode = root.callbackNode; // Check if any lanes are being starved by other work. If so, mark them as | |
| 25624 // expired so we know to work on those next. | |
| 25625 | |
| 25626 markStarvedLanesAsExpired(root, currentTime); // Determine the next lanes to work on, and their priority. | |
| 25627 | |
| 25628 var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); | |
| 25629 | |
| 25630 if (nextLanes === NoLanes) { | |
| 25631 // Special case: There's nothing to work on. | |
| 25632 if (existingCallbackNode !== null) { | |
| 25633 cancelCallback$1(existingCallbackNode); | |
| 25634 } | |
| 25635 | |
| 25636 root.callbackNode = null; | |
| 25637 root.callbackPriority = NoLane; | |
| 25638 return; | |
| 25639 } // We use the highest priority lane to represent the priority of the callback. | |
| 25640 | |
| 25641 | |
| 25642 var newCallbackPriority = getHighestPriorityLane(nextLanes); // Check if there's an existing task. We may be able to reuse it. | |
| 25643 | |
| 25644 var existingCallbackPriority = root.callbackPriority; | |
| 25645 | |
| 25646 if (existingCallbackPriority === newCallbackPriority && // Special case related to `act`. If the currently scheduled task is a | |
| 25647 // Scheduler task, rather than an `act` task, cancel it and re-scheduled | |
| 25648 // on the `act` queue. | |
| 25649 !( ReactCurrentActQueue$1.current !== null && existingCallbackNode !== fakeActCallbackNode)) { | |
| 25650 { | |
| 25651 // If we're going to re-use an existing task, it needs to exist. | |
| 25652 // Assume that discrete update microtasks are non-cancellable and null. | |
| 25653 // TODO: Temporary until we confirm this warning is not fired. | |
| 25654 if (existingCallbackNode == null && existingCallbackPriority !== SyncLane) { | |
| 25655 error('Expected scheduled callback to exist. This error is likely caused by a bug in React. Please file an issue.'); | |
| 25656 } | |
| 25657 } // The priority hasn't changed. We can reuse the existing task. Exit. | |
| 25658 | |
| 25659 | |
| 25660 return; | |
| 25661 } | |
| 25662 | |
| 25663 if (existingCallbackNode != null) { | |
| 25664 // Cancel the existing callback. We'll schedule a new one below. | |
| 25665 cancelCallback$1(existingCallbackNode); | |
| 25666 } // Schedule a new callback. | |
| 25667 | |
| 25668 | |
| 25669 var newCallbackNode; | |
| 25670 | |
| 25671 if (newCallbackPriority === SyncLane) { | |
| 25672 // Special case: Sync React callbacks are scheduled on a special | |
| 25673 // internal queue | |
| 25674 if (root.tag === LegacyRoot) { | |
| 25675 if ( ReactCurrentActQueue$1.isBatchingLegacy !== null) { | |
| 25676 ReactCurrentActQueue$1.didScheduleLegacyUpdate = true; | |
| 25677 } | |
| 25678 | |
| 25679 scheduleLegacySyncCallback(performSyncWorkOnRoot.bind(null, root)); | |
| 25680 } else { | |
| 25681 scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root)); | |
| 25682 } | |
| 25683 | |
| 25684 { | |
| 25685 // Flush the queue in a microtask. | |
| 25686 if ( ReactCurrentActQueue$1.current !== null) { | |
| 25687 // Inside `act`, use our internal `act` queue so that these get flushed | |
| 25688 // at the end of the current scope even when using the sync version | |
| 25689 // of `act`. | |
| 25690 ReactCurrentActQueue$1.current.push(flushSyncCallbacks); | |
| 25691 } else { | |
| 25692 scheduleMicrotask(function () { | |
| 25693 // In Safari, appending an iframe forces microtasks to run. | |
| 25694 // https://github.com/facebook/react/issues/22459 | |
| 25695 // We don't support running callbacks in the middle of render | |
| 25696 // or commit so we need to check against that. | |
| 25697 if ((executionContext & (RenderContext | CommitContext)) === NoContext) { | |
| 25698 // Note that this would still prematurely flush the callbacks | |
| 25699 // if this happens outside render or commit phase (e.g. in an event). | |
| 25700 flushSyncCallbacks(); | |
| 25701 } | |
| 25702 }); | |
| 25703 } | |
| 25704 } | |
| 25705 | |
| 25706 newCallbackNode = null; | |
| 25707 } else { | |
| 25708 var schedulerPriorityLevel; | |
| 25709 | |
| 25710 switch (lanesToEventPriority(nextLanes)) { | |
| 25711 case DiscreteEventPriority: | |
| 25712 schedulerPriorityLevel = ImmediatePriority; | |
| 25713 break; | |
| 25714 | |
| 25715 case ContinuousEventPriority: | |
| 25716 schedulerPriorityLevel = UserBlockingPriority; | |
| 25717 break; | |
| 25718 | |
| 25719 case DefaultEventPriority: | |
| 25720 schedulerPriorityLevel = NormalPriority; | |
| 25721 break; | |
| 25722 | |
| 25723 case IdleEventPriority: | |
| 25724 schedulerPriorityLevel = IdlePriority; | |
| 25725 break; | |
| 25726 | |
| 25727 default: | |
| 25728 schedulerPriorityLevel = NormalPriority; | |
| 25729 break; | |
| 25730 } | |
| 25731 | |
| 25732 newCallbackNode = scheduleCallback$1(schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root)); | |
| 25733 } | |
| 25734 | |
| 25735 root.callbackPriority = newCallbackPriority; | |
| 25736 root.callbackNode = newCallbackNode; | |
| 25737 } // This is the entry point for every concurrent task, i.e. anything that | |
| 25738 // goes through Scheduler. | |
| 25739 | |
| 25740 | |
| 25741 function performConcurrentWorkOnRoot(root, didTimeout) { | |
| 25742 { | |
| 25743 resetNestedUpdateFlag(); | |
| 25744 } // Since we know we're in a React event, we can clear the current | |
| 25745 // event time. The next update will compute a new event time. | |
| 25746 | |
| 25747 | |
| 25748 currentEventTime = NoTimestamp; | |
| 25749 currentEventTransitionLane = NoLanes; | |
| 25750 | |
| 25751 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { | |
| 25752 throw new Error('Should not already be working.'); | |
| 25753 } // Flush any pending passive effects before deciding which lanes to work on, | |
| 25754 // in case they schedule additional work. | |
| 25755 | |
| 25756 | |
| 25757 var originalCallbackNode = root.callbackNode; | |
| 25758 var didFlushPassiveEffects = flushPassiveEffects(); | |
| 25759 | |
| 25760 if (didFlushPassiveEffects) { | |
| 25761 // Something in the passive effect phase may have canceled the current task. | |
| 25762 // Check if the task node for this root was changed. | |
| 25763 if (root.callbackNode !== originalCallbackNode) { | |
| 25764 // The current task was canceled. Exit. We don't need to call | |
| 25765 // `ensureRootIsScheduled` because the check above implies either that | |
| 25766 // there's a new task, or that there's no remaining work on this root. | |
| 25767 return null; | |
| 25768 } | |
| 25769 } // Determine the next lanes to work on, using the fields stored | |
| 25770 // on the root. | |
| 25771 | |
| 25772 | |
| 25773 var lanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); | |
| 25774 | |
| 25775 if (lanes === NoLanes) { | |
| 25776 // Defensive coding. This is never expected to happen. | |
| 25777 return null; | |
| 25778 } // We disable time-slicing in some cases: if the work has been CPU-bound | |
| 25779 // for too long ("expired" work, to prevent starvation), or we're in | |
| 25780 // sync-updates-by-default mode. | |
| 25781 // TODO: We only check `didTimeout` defensively, to account for a Scheduler | |
| 25782 // bug we're still investigating. Once the bug in Scheduler is fixed, | |
| 25783 // we can remove this, since we track expiration ourselves. | |
| 25784 | |
| 25785 | |
| 25786 var shouldTimeSlice = !includesBlockingLane(root, lanes) && !includesExpiredLane(root, lanes) && ( !didTimeout); | |
| 25787 var exitStatus = shouldTimeSlice ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes); | |
| 25788 | |
| 25789 if (exitStatus !== RootInProgress) { | |
| 25790 if (exitStatus === RootErrored) { | |
| 25791 // If something threw an error, try rendering one more time. We'll | |
| 25792 // render synchronously to block concurrent data mutations, and we'll | |
| 25793 // includes all pending updates are included. If it still fails after | |
| 25794 // the second attempt, we'll give up and commit the resulting tree. | |
| 25795 var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root); | |
| 25796 | |
| 25797 if (errorRetryLanes !== NoLanes) { | |
| 25798 lanes = errorRetryLanes; | |
| 25799 exitStatus = recoverFromConcurrentError(root, errorRetryLanes); | |
| 25800 } | |
| 25801 } | |
| 25802 | |
| 25803 if (exitStatus === RootFatalErrored) { | |
| 25804 var fatalError = workInProgressRootFatalError; | |
| 25805 prepareFreshStack(root, NoLanes); | |
| 25806 markRootSuspended$1(root, lanes); | |
| 25807 ensureRootIsScheduled(root, now()); | |
| 25808 throw fatalError; | |
| 25809 } | |
| 25810 | |
| 25811 if (exitStatus === RootDidNotComplete) { | |
| 25812 // The render unwound without completing the tree. This happens in special | |
| 25813 // cases where need to exit the current render without producing a | |
| 25814 // consistent tree or committing. | |
| 25815 // | |
| 25816 // This should only happen during a concurrent render, not a discrete or | |
| 25817 // synchronous update. We should have already checked for this when we | |
| 25818 // unwound the stack. | |
| 25819 markRootSuspended$1(root, lanes); | |
| 25820 } else { | |
| 25821 // The render completed. | |
| 25822 // Check if this render may have yielded to a concurrent event, and if so, | |
| 25823 // confirm that any newly rendered stores are consistent. | |
| 25824 // TODO: It's possible that even a concurrent render may never have yielded | |
| 25825 // to the main thread, if it was fast enough, or if it expired. We could | |
| 25826 // skip the consistency check in that case, too. | |
| 25827 var renderWasConcurrent = !includesBlockingLane(root, lanes); | |
| 25828 var finishedWork = root.current.alternate; | |
| 25829 | |
| 25830 if (renderWasConcurrent && !isRenderConsistentWithExternalStores(finishedWork)) { | |
| 25831 // A store was mutated in an interleaved event. Render again, | |
| 25832 // synchronously, to block further mutations. | |
| 25833 exitStatus = renderRootSync(root, lanes); // We need to check again if something threw | |
| 25834 | |
| 25835 if (exitStatus === RootErrored) { | |
| 25836 var _errorRetryLanes = getLanesToRetrySynchronouslyOnError(root); | |
| 25837 | |
| 25838 if (_errorRetryLanes !== NoLanes) { | |
| 25839 lanes = _errorRetryLanes; | |
| 25840 exitStatus = recoverFromConcurrentError(root, _errorRetryLanes); // We assume the tree is now consistent because we didn't yield to any | |
| 25841 // concurrent events. | |
| 25842 } | |
| 25843 } | |
| 25844 | |
| 25845 if (exitStatus === RootFatalErrored) { | |
| 25846 var _fatalError = workInProgressRootFatalError; | |
| 25847 prepareFreshStack(root, NoLanes); | |
| 25848 markRootSuspended$1(root, lanes); | |
| 25849 ensureRootIsScheduled(root, now()); | |
| 25850 throw _fatalError; | |
| 25851 } | |
| 25852 } // We now have a consistent tree. The next step is either to commit it, | |
| 25853 // or, if something suspended, wait to commit it after a timeout. | |
| 25854 | |
| 25855 | |
| 25856 root.finishedWork = finishedWork; | |
| 25857 root.finishedLanes = lanes; | |
| 25858 finishConcurrentRender(root, exitStatus, lanes); | |
| 25859 } | |
| 25860 } | |
| 25861 | |
| 25862 ensureRootIsScheduled(root, now()); | |
| 25863 | |
| 25864 if (root.callbackNode === originalCallbackNode) { | |
| 25865 // The task node scheduled for this root is the same one that's | |
| 25866 // currently executed. Need to return a continuation. | |
| 25867 return performConcurrentWorkOnRoot.bind(null, root); | |
| 25868 } | |
| 25869 | |
| 25870 return null; | |
| 25871 } | |
| 25872 | |
| 25873 function recoverFromConcurrentError(root, errorRetryLanes) { | |
| 25874 // If an error occurred during hydration, discard server response and fall | |
| 25875 // back to client side render. | |
| 25876 // Before rendering again, save the errors from the previous attempt. | |
| 25877 var errorsFromFirstAttempt = workInProgressRootConcurrentErrors; | |
| 25878 | |
| 25879 if (isRootDehydrated(root)) { | |
| 25880 // The shell failed to hydrate. Set a flag to force a client rendering | |
| 25881 // during the next attempt. To do this, we call prepareFreshStack now | |
| 25882 // to create the root work-in-progress fiber. This is a bit weird in terms | |
| 25883 // of factoring, because it relies on renderRootSync not calling | |
| 25884 // prepareFreshStack again in the call below, which happens because the | |
| 25885 // root and lanes haven't changed. | |
| 25886 // | |
| 25887 // TODO: I think what we should do is set ForceClientRender inside | |
| 25888 // throwException, like we do for nested Suspense boundaries. The reason | |
| 25889 // it's here instead is so we can switch to the synchronous work loop, too. | |
| 25890 // Something to consider for a future refactor. | |
| 25891 var rootWorkInProgress = prepareFreshStack(root, errorRetryLanes); | |
| 25892 rootWorkInProgress.flags |= ForceClientRender; | |
| 25893 | |
| 25894 { | |
| 25895 errorHydratingContainer(root.containerInfo); | |
| 25896 } | |
| 25897 } | |
| 25898 | |
| 25899 var exitStatus = renderRootSync(root, errorRetryLanes); | |
| 25900 | |
| 25901 if (exitStatus !== RootErrored) { | |
| 25902 // Successfully finished rendering on retry | |
| 25903 // The errors from the failed first attempt have been recovered. Add | |
| 25904 // them to the collection of recoverable errors. We'll log them in the | |
| 25905 // commit phase. | |
| 25906 var errorsFromSecondAttempt = workInProgressRootRecoverableErrors; | |
| 25907 workInProgressRootRecoverableErrors = errorsFromFirstAttempt; // The errors from the second attempt should be queued after the errors | |
| 25908 // from the first attempt, to preserve the causal sequence. | |
| 25909 | |
| 25910 if (errorsFromSecondAttempt !== null) { | |
| 25911 queueRecoverableErrors(errorsFromSecondAttempt); | |
| 25912 } | |
| 25913 } | |
| 25914 | |
| 25915 return exitStatus; | |
| 25916 } | |
| 25917 | |
| 25918 function queueRecoverableErrors(errors) { | |
| 25919 if (workInProgressRootRecoverableErrors === null) { | |
| 25920 workInProgressRootRecoverableErrors = errors; | |
| 25921 } else { | |
| 25922 workInProgressRootRecoverableErrors.push.apply(workInProgressRootRecoverableErrors, errors); | |
| 25923 } | |
| 25924 } | |
| 25925 | |
| 25926 function finishConcurrentRender(root, exitStatus, lanes) { | |
| 25927 switch (exitStatus) { | |
| 25928 case RootInProgress: | |
| 25929 case RootFatalErrored: | |
| 25930 { | |
| 25931 throw new Error('Root did not complete. This is a bug in React.'); | |
| 25932 } | |
| 25933 // Flow knows about invariant, so it complains if I add a break | |
| 25934 // statement, but eslint doesn't know about invariant, so it complains | |
| 25935 // if I do. eslint-disable-next-line no-fallthrough | |
| 25936 | |
| 25937 case RootErrored: | |
| 25938 { | |
| 25939 // We should have already attempted to retry this tree. If we reached | |
| 25940 // this point, it errored again. Commit it. | |
| 25941 commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); | |
| 25942 break; | |
| 25943 } | |
| 25944 | |
| 25945 case RootSuspended: | |
| 25946 { | |
| 25947 markRootSuspended$1(root, lanes); // We have an acceptable loading state. We need to figure out if we | |
| 25948 // should immediately commit it or wait a bit. | |
| 25949 | |
| 25950 if (includesOnlyRetries(lanes) && // do not delay if we're inside an act() scope | |
| 25951 !shouldForceFlushFallbacksInDEV()) { | |
| 25952 // This render only included retries, no updates. Throttle committing | |
| 25953 // retries so that we don't show too many loading states too quickly. | |
| 25954 var msUntilTimeout = globalMostRecentFallbackTime + FALLBACK_THROTTLE_MS - now(); // Don't bother with a very short suspense time. | |
| 25955 | |
| 25956 if (msUntilTimeout > 10) { | |
| 25957 var nextLanes = getNextLanes(root, NoLanes); | |
| 25958 | |
| 25959 if (nextLanes !== NoLanes) { | |
| 25960 // There's additional work on this root. | |
| 25961 break; | |
| 25962 } | |
| 25963 | |
| 25964 var suspendedLanes = root.suspendedLanes; | |
| 25965 | |
| 25966 if (!isSubsetOfLanes(suspendedLanes, lanes)) { | |
| 25967 // We should prefer to render the fallback of at the last | |
| 25968 // suspended level. Ping the last suspended level to try | |
| 25969 // rendering it again. | |
| 25970 // FIXME: What if the suspended lanes are Idle? Should not restart. | |
| 25971 var eventTime = requestEventTime(); | |
| 25972 markRootPinged(root, suspendedLanes); | |
| 25973 break; | |
| 25974 } // The render is suspended, it hasn't timed out, and there's no | |
| 25975 // lower priority work to do. Instead of committing the fallback | |
| 25976 // immediately, wait for more data to arrive. | |
| 25977 | |
| 25978 | |
| 25979 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), msUntilTimeout); | |
| 25980 break; | |
| 25981 } | |
| 25982 } // The work expired. Commit immediately. | |
| 25983 | |
| 25984 | |
| 25985 commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); | |
| 25986 break; | |
| 25987 } | |
| 25988 | |
| 25989 case RootSuspendedWithDelay: | |
| 25990 { | |
| 25991 markRootSuspended$1(root, lanes); | |
| 25992 | |
| 25993 if (includesOnlyTransitions(lanes)) { | |
| 25994 // This is a transition, so we should exit without committing a | |
| 25995 // placeholder and without scheduling a timeout. Delay indefinitely | |
| 25996 // until we receive more data. | |
| 25997 break; | |
| 25998 } | |
| 25999 | |
| 26000 if (!shouldForceFlushFallbacksInDEV()) { | |
| 26001 // This is not a transition, but we did trigger an avoided state. | |
| 26002 // Schedule a placeholder to display after a short delay, using the Just | |
| 26003 // Noticeable Difference. | |
| 26004 // TODO: Is the JND optimization worth the added complexity? If this is | |
| 26005 // the only reason we track the event time, then probably not. | |
| 26006 // Consider removing. | |
| 26007 var mostRecentEventTime = getMostRecentEventTime(root, lanes); | |
| 26008 var eventTimeMs = mostRecentEventTime; | |
| 26009 var timeElapsedMs = now() - eventTimeMs; | |
| 26010 | |
| 26011 var _msUntilTimeout = jnd(timeElapsedMs) - timeElapsedMs; // Don't bother with a very short suspense time. | |
| 26012 | |
| 26013 | |
| 26014 if (_msUntilTimeout > 10) { | |
| 26015 // Instead of committing the fallback immediately, wait for more data | |
| 26016 // to arrive. | |
| 26017 root.timeoutHandle = scheduleTimeout(commitRoot.bind(null, root, workInProgressRootRecoverableErrors, workInProgressTransitions), _msUntilTimeout); | |
| 26018 break; | |
| 26019 } | |
| 26020 } // Commit the placeholder. | |
| 26021 | |
| 26022 | |
| 26023 commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); | |
| 26024 break; | |
| 26025 } | |
| 26026 | |
| 26027 case RootCompleted: | |
| 26028 { | |
| 26029 // The work completed. Ready to commit. | |
| 26030 commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); | |
| 26031 break; | |
| 26032 } | |
| 26033 | |
| 26034 default: | |
| 26035 { | |
| 26036 throw new Error('Unknown root exit status.'); | |
| 26037 } | |
| 26038 } | |
| 26039 } | |
| 26040 | |
| 26041 function isRenderConsistentWithExternalStores(finishedWork) { | |
| 26042 // Search the rendered tree for external store reads, and check whether the | |
| 26043 // stores were mutated in a concurrent event. Intentionally using an iterative | |
| 26044 // loop instead of recursion so we can exit early. | |
| 26045 var node = finishedWork; | |
| 26046 | |
| 26047 while (true) { | |
| 26048 if (node.flags & StoreConsistency) { | |
| 26049 var updateQueue = node.updateQueue; | |
| 26050 | |
| 26051 if (updateQueue !== null) { | |
| 26052 var checks = updateQueue.stores; | |
| 26053 | |
| 26054 if (checks !== null) { | |
| 26055 for (var i = 0; i < checks.length; i++) { | |
| 26056 var check = checks[i]; | |
| 26057 var getSnapshot = check.getSnapshot; | |
| 26058 var renderedValue = check.value; | |
| 26059 | |
| 26060 try { | |
| 26061 if (!objectIs(getSnapshot(), renderedValue)) { | |
| 26062 // Found an inconsistent store. | |
| 26063 return false; | |
| 26064 } | |
| 26065 } catch (error) { | |
| 26066 // If `getSnapshot` throws, return `false`. This will schedule | |
| 26067 // a re-render, and the error will be rethrown during render. | |
| 26068 return false; | |
| 26069 } | |
| 26070 } | |
| 26071 } | |
| 26072 } | |
| 26073 } | |
| 26074 | |
| 26075 var child = node.child; | |
| 26076 | |
| 26077 if (node.subtreeFlags & StoreConsistency && child !== null) { | |
| 26078 child.return = node; | |
| 26079 node = child; | |
| 26080 continue; | |
| 26081 } | |
| 26082 | |
| 26083 if (node === finishedWork) { | |
| 26084 return true; | |
| 26085 } | |
| 26086 | |
| 26087 while (node.sibling === null) { | |
| 26088 if (node.return === null || node.return === finishedWork) { | |
| 26089 return true; | |
| 26090 } | |
| 26091 | |
| 26092 node = node.return; | |
| 26093 } | |
| 26094 | |
| 26095 node.sibling.return = node.return; | |
| 26096 node = node.sibling; | |
| 26097 } // Flow doesn't know this is unreachable, but eslint does | |
| 26098 // eslint-disable-next-line no-unreachable | |
| 26099 | |
| 26100 | |
| 26101 return true; | |
| 26102 } | |
| 26103 | |
| 26104 function markRootSuspended$1(root, suspendedLanes) { | |
| 26105 // When suspending, we should always exclude lanes that were pinged or (more | |
| 26106 // rarely, since we try to avoid it) updated during the render phase. | |
| 26107 // TODO: Lol maybe there's a better way to factor this besides this | |
| 26108 // obnoxiously named function :) | |
| 26109 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootPingedLanes); | |
| 26110 suspendedLanes = removeLanes(suspendedLanes, workInProgressRootInterleavedUpdatedLanes); | |
| 26111 markRootSuspended(root, suspendedLanes); | |
| 26112 } // This is the entry point for synchronous tasks that don't go | |
| 26113 // through Scheduler | |
| 26114 | |
| 26115 | |
| 26116 function performSyncWorkOnRoot(root) { | |
| 26117 { | |
| 26118 syncNestedUpdateFlag(); | |
| 26119 } | |
| 26120 | |
| 26121 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { | |
| 26122 throw new Error('Should not already be working.'); | |
| 26123 } | |
| 26124 | |
| 26125 flushPassiveEffects(); | |
| 26126 var lanes = getNextLanes(root, NoLanes); | |
| 26127 | |
| 26128 if (!includesSomeLane(lanes, SyncLane)) { | |
| 26129 // There's no remaining sync work left. | |
| 26130 ensureRootIsScheduled(root, now()); | |
| 26131 return null; | |
| 26132 } | |
| 26133 | |
| 26134 var exitStatus = renderRootSync(root, lanes); | |
| 26135 | |
| 26136 if (root.tag !== LegacyRoot && exitStatus === RootErrored) { | |
| 26137 // If something threw an error, try rendering one more time. We'll render | |
| 26138 // synchronously to block concurrent data mutations, and we'll includes | |
| 26139 // all pending updates are included. If it still fails after the second | |
| 26140 // attempt, we'll give up and commit the resulting tree. | |
| 26141 var errorRetryLanes = getLanesToRetrySynchronouslyOnError(root); | |
| 26142 | |
| 26143 if (errorRetryLanes !== NoLanes) { | |
| 26144 lanes = errorRetryLanes; | |
| 26145 exitStatus = recoverFromConcurrentError(root, errorRetryLanes); | |
| 26146 } | |
| 26147 } | |
| 26148 | |
| 26149 if (exitStatus === RootFatalErrored) { | |
| 26150 var fatalError = workInProgressRootFatalError; | |
| 26151 prepareFreshStack(root, NoLanes); | |
| 26152 markRootSuspended$1(root, lanes); | |
| 26153 ensureRootIsScheduled(root, now()); | |
| 26154 throw fatalError; | |
| 26155 } | |
| 26156 | |
| 26157 if (exitStatus === RootDidNotComplete) { | |
| 26158 throw new Error('Root did not complete. This is a bug in React.'); | |
| 26159 } // We now have a consistent tree. Because this is a sync render, we | |
| 26160 // will commit it even if something suspended. | |
| 26161 | |
| 26162 | |
| 26163 var finishedWork = root.current.alternate; | |
| 26164 root.finishedWork = finishedWork; | |
| 26165 root.finishedLanes = lanes; | |
| 26166 commitRoot(root, workInProgressRootRecoverableErrors, workInProgressTransitions); // Before exiting, make sure there's a callback scheduled for the next | |
| 26167 // pending level. | |
| 26168 | |
| 26169 ensureRootIsScheduled(root, now()); | |
| 26170 return null; | |
| 26171 } | |
| 26172 | |
| 26173 function flushRoot(root, lanes) { | |
| 26174 if (lanes !== NoLanes) { | |
| 26175 markRootEntangled(root, mergeLanes(lanes, SyncLane)); | |
| 26176 ensureRootIsScheduled(root, now()); | |
| 26177 | |
| 26178 if ((executionContext & (RenderContext | CommitContext)) === NoContext) { | |
| 26179 resetRenderTimer(); | |
| 26180 flushSyncCallbacks(); | |
| 26181 } | |
| 26182 } | |
| 26183 } | |
| 26184 function batchedUpdates$1(fn, a) { | |
| 26185 var prevExecutionContext = executionContext; | |
| 26186 executionContext |= BatchedContext; | |
| 26187 | |
| 26188 try { | |
| 26189 return fn(a); | |
| 26190 } finally { | |
| 26191 executionContext = prevExecutionContext; // If there were legacy sync updates, flush them at the end of the outer | |
| 26192 // most batchedUpdates-like method. | |
| 26193 | |
| 26194 if (executionContext === NoContext && // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode. | |
| 26195 !( ReactCurrentActQueue$1.isBatchingLegacy)) { | |
| 26196 resetRenderTimer(); | |
| 26197 flushSyncCallbacksOnlyInLegacyMode(); | |
| 26198 } | |
| 26199 } | |
| 26200 } | |
| 26201 function discreteUpdates(fn, a, b, c, d) { | |
| 26202 var previousPriority = getCurrentUpdatePriority(); | |
| 26203 var prevTransition = ReactCurrentBatchConfig$3.transition; | |
| 26204 | |
| 26205 try { | |
| 26206 ReactCurrentBatchConfig$3.transition = null; | |
| 26207 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 26208 return fn(a, b, c, d); | |
| 26209 } finally { | |
| 26210 setCurrentUpdatePriority(previousPriority); | |
| 26211 ReactCurrentBatchConfig$3.transition = prevTransition; | |
| 26212 | |
| 26213 if (executionContext === NoContext) { | |
| 26214 resetRenderTimer(); | |
| 26215 } | |
| 26216 } | |
| 26217 } // Overload the definition to the two valid signatures. | |
| 26218 // Warning, this opts-out of checking the function body. | |
| 26219 | |
| 26220 // eslint-disable-next-line no-redeclare | |
| 26221 function flushSync(fn) { | |
| 26222 // In legacy mode, we flush pending passive effects at the beginning of the | |
| 26223 // next event, not at the end of the previous one. | |
| 26224 if (rootWithPendingPassiveEffects !== null && rootWithPendingPassiveEffects.tag === LegacyRoot && (executionContext & (RenderContext | CommitContext)) === NoContext) { | |
| 26225 flushPassiveEffects(); | |
| 26226 } | |
| 26227 | |
| 26228 var prevExecutionContext = executionContext; | |
| 26229 executionContext |= BatchedContext; | |
| 26230 var prevTransition = ReactCurrentBatchConfig$3.transition; | |
| 26231 var previousPriority = getCurrentUpdatePriority(); | |
| 26232 | |
| 26233 try { | |
| 26234 ReactCurrentBatchConfig$3.transition = null; | |
| 26235 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 26236 | |
| 26237 if (fn) { | |
| 26238 return fn(); | |
| 26239 } else { | |
| 26240 return undefined; | |
| 26241 } | |
| 26242 } finally { | |
| 26243 setCurrentUpdatePriority(previousPriority); | |
| 26244 ReactCurrentBatchConfig$3.transition = prevTransition; | |
| 26245 executionContext = prevExecutionContext; // Flush the immediate callbacks that were scheduled during this batch. | |
| 26246 // Note that this will happen even if batchedUpdates is higher up | |
| 26247 // the stack. | |
| 26248 | |
| 26249 if ((executionContext & (RenderContext | CommitContext)) === NoContext) { | |
| 26250 flushSyncCallbacks(); | |
| 26251 } | |
| 26252 } | |
| 26253 } | |
| 26254 function isAlreadyRendering() { | |
| 26255 // Used by the renderer to print a warning if certain APIs are called from | |
| 26256 // the wrong context. | |
| 26257 return (executionContext & (RenderContext | CommitContext)) !== NoContext; | |
| 26258 } | |
| 26259 function pushRenderLanes(fiber, lanes) { | |
| 26260 push(subtreeRenderLanesCursor, subtreeRenderLanes, fiber); | |
| 26261 subtreeRenderLanes = mergeLanes(subtreeRenderLanes, lanes); | |
| 26262 workInProgressRootIncludedLanes = mergeLanes(workInProgressRootIncludedLanes, lanes); | |
| 26263 } | |
| 26264 function popRenderLanes(fiber) { | |
| 26265 subtreeRenderLanes = subtreeRenderLanesCursor.current; | |
| 26266 pop(subtreeRenderLanesCursor, fiber); | |
| 26267 } | |
| 26268 | |
| 26269 function prepareFreshStack(root, lanes) { | |
| 26270 root.finishedWork = null; | |
| 26271 root.finishedLanes = NoLanes; | |
| 26272 var timeoutHandle = root.timeoutHandle; | |
| 26273 | |
| 26274 if (timeoutHandle !== noTimeout) { | |
| 26275 // The root previous suspended and scheduled a timeout to commit a fallback | |
| 26276 // state. Now that we have additional work, cancel the timeout. | |
| 26277 root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above | |
| 26278 | |
| 26279 cancelTimeout(timeoutHandle); | |
| 26280 } | |
| 26281 | |
| 26282 if (workInProgress !== null) { | |
| 26283 var interruptedWork = workInProgress.return; | |
| 26284 | |
| 26285 while (interruptedWork !== null) { | |
| 26286 var current = interruptedWork.alternate; | |
| 26287 unwindInterruptedWork(current, interruptedWork); | |
| 26288 interruptedWork = interruptedWork.return; | |
| 26289 } | |
| 26290 } | |
| 26291 | |
| 26292 workInProgressRoot = root; | |
| 26293 var rootWorkInProgress = createWorkInProgress(root.current, null); | |
| 26294 workInProgress = rootWorkInProgress; | |
| 26295 workInProgressRootRenderLanes = subtreeRenderLanes = workInProgressRootIncludedLanes = lanes; | |
| 26296 workInProgressRootExitStatus = RootInProgress; | |
| 26297 workInProgressRootFatalError = null; | |
| 26298 workInProgressRootSkippedLanes = NoLanes; | |
| 26299 workInProgressRootInterleavedUpdatedLanes = NoLanes; | |
| 26300 workInProgressRootPingedLanes = NoLanes; | |
| 26301 workInProgressRootConcurrentErrors = null; | |
| 26302 workInProgressRootRecoverableErrors = null; | |
| 26303 finishQueueingConcurrentUpdates(); | |
| 26304 | |
| 26305 { | |
| 26306 ReactStrictModeWarnings.discardPendingWarnings(); | |
| 26307 } | |
| 26308 | |
| 26309 return rootWorkInProgress; | |
| 26310 } | |
| 26311 | |
| 26312 function handleError(root, thrownValue) { | |
| 26313 do { | |
| 26314 var erroredWork = workInProgress; | |
| 26315 | |
| 26316 try { | |
| 26317 // Reset module-level state that was set during the render phase. | |
| 26318 resetContextDependencies(); | |
| 26319 resetHooksAfterThrow(); | |
| 26320 resetCurrentFiber(); // TODO: I found and added this missing line while investigating a | |
| 26321 // separate issue. Write a regression test using string refs. | |
| 26322 | |
| 26323 ReactCurrentOwner$2.current = null; | |
| 26324 | |
| 26325 if (erroredWork === null || erroredWork.return === null) { | |
| 26326 // Expected to be working on a non-root fiber. This is a fatal error | |
| 26327 // because there's no ancestor that can handle it; the root is | |
| 26328 // supposed to capture all errors that weren't caught by an error | |
| 26329 // boundary. | |
| 26330 workInProgressRootExitStatus = RootFatalErrored; | |
| 26331 workInProgressRootFatalError = thrownValue; // Set `workInProgress` to null. This represents advancing to the next | |
| 26332 // sibling, or the parent if there are no siblings. But since the root | |
| 26333 // has no siblings nor a parent, we set it to null. Usually this is | |
| 26334 // handled by `completeUnitOfWork` or `unwindWork`, but since we're | |
| 26335 // intentionally not calling those, we need set it here. | |
| 26336 // TODO: Consider calling `unwindWork` to pop the contexts. | |
| 26337 | |
| 26338 workInProgress = null; | |
| 26339 return; | |
| 26340 } | |
| 26341 | |
| 26342 if (enableProfilerTimer && erroredWork.mode & ProfileMode) { | |
| 26343 // Record the time spent rendering before an error was thrown. This | |
| 26344 // avoids inaccurate Profiler durations in the case of a | |
| 26345 // suspended render. | |
| 26346 stopProfilerTimerIfRunningAndRecordDelta(erroredWork, true); | |
| 26347 } | |
| 26348 | |
| 26349 if (enableSchedulingProfiler) { | |
| 26350 markComponentRenderStopped(); | |
| 26351 | |
| 26352 if (thrownValue !== null && typeof thrownValue === 'object' && typeof thrownValue.then === 'function') { | |
| 26353 var wakeable = thrownValue; | |
| 26354 markComponentSuspended(erroredWork, wakeable, workInProgressRootRenderLanes); | |
| 26355 } else { | |
| 26356 markComponentErrored(erroredWork, thrownValue, workInProgressRootRenderLanes); | |
| 26357 } | |
| 26358 } | |
| 26359 | |
| 26360 throwException(root, erroredWork.return, erroredWork, thrownValue, workInProgressRootRenderLanes); | |
| 26361 completeUnitOfWork(erroredWork); | |
| 26362 } catch (yetAnotherThrownValue) { | |
| 26363 // Something in the return path also threw. | |
| 26364 thrownValue = yetAnotherThrownValue; | |
| 26365 | |
| 26366 if (workInProgress === erroredWork && erroredWork !== null) { | |
| 26367 // If this boundary has already errored, then we had trouble processing | |
| 26368 // the error. Bubble it to the next boundary. | |
| 26369 erroredWork = erroredWork.return; | |
| 26370 workInProgress = erroredWork; | |
| 26371 } else { | |
| 26372 erroredWork = workInProgress; | |
| 26373 } | |
| 26374 | |
| 26375 continue; | |
| 26376 } // Return to the normal work loop. | |
| 26377 | |
| 26378 | |
| 26379 return; | |
| 26380 } while (true); | |
| 26381 } | |
| 26382 | |
| 26383 function pushDispatcher() { | |
| 26384 var prevDispatcher = ReactCurrentDispatcher$2.current; | |
| 26385 ReactCurrentDispatcher$2.current = ContextOnlyDispatcher; | |
| 26386 | |
| 26387 if (prevDispatcher === null) { | |
| 26388 // The React isomorphic package does not include a default dispatcher. | |
| 26389 // Instead the first renderer will lazily attach one, in order to give | |
| 26390 // nicer error messages. | |
| 26391 return ContextOnlyDispatcher; | |
| 26392 } else { | |
| 26393 return prevDispatcher; | |
| 26394 } | |
| 26395 } | |
| 26396 | |
| 26397 function popDispatcher(prevDispatcher) { | |
| 26398 ReactCurrentDispatcher$2.current = prevDispatcher; | |
| 26399 } | |
| 26400 | |
| 26401 function markCommitTimeOfFallback() { | |
| 26402 globalMostRecentFallbackTime = now(); | |
| 26403 } | |
| 26404 function markSkippedUpdateLanes(lane) { | |
| 26405 workInProgressRootSkippedLanes = mergeLanes(lane, workInProgressRootSkippedLanes); | |
| 26406 } | |
| 26407 function renderDidSuspend() { | |
| 26408 if (workInProgressRootExitStatus === RootInProgress) { | |
| 26409 workInProgressRootExitStatus = RootSuspended; | |
| 26410 } | |
| 26411 } | |
| 26412 function renderDidSuspendDelayIfPossible() { | |
| 26413 if (workInProgressRootExitStatus === RootInProgress || workInProgressRootExitStatus === RootSuspended || workInProgressRootExitStatus === RootErrored) { | |
| 26414 workInProgressRootExitStatus = RootSuspendedWithDelay; | |
| 26415 } // Check if there are updates that we skipped tree that might have unblocked | |
| 26416 // this render. | |
| 26417 | |
| 26418 | |
| 26419 if (workInProgressRoot !== null && (includesNonIdleWork(workInProgressRootSkippedLanes) || includesNonIdleWork(workInProgressRootInterleavedUpdatedLanes))) { | |
| 26420 // Mark the current render as suspended so that we switch to working on | |
| 26421 // the updates that were skipped. Usually we only suspend at the end of | |
| 26422 // the render phase. | |
| 26423 // TODO: We should probably always mark the root as suspended immediately | |
| 26424 // (inside this function), since by suspending at the end of the render | |
| 26425 // phase introduces a potential mistake where we suspend lanes that were | |
| 26426 // pinged or updated while we were rendering. | |
| 26427 markRootSuspended$1(workInProgressRoot, workInProgressRootRenderLanes); | |
| 26428 } | |
| 26429 } | |
| 26430 function renderDidError(error) { | |
| 26431 if (workInProgressRootExitStatus !== RootSuspendedWithDelay) { | |
| 26432 workInProgressRootExitStatus = RootErrored; | |
| 26433 } | |
| 26434 | |
| 26435 if (workInProgressRootConcurrentErrors === null) { | |
| 26436 workInProgressRootConcurrentErrors = [error]; | |
| 26437 } else { | |
| 26438 workInProgressRootConcurrentErrors.push(error); | |
| 26439 } | |
| 26440 } // Called during render to determine if anything has suspended. | |
| 26441 // Returns false if we're not sure. | |
| 26442 | |
| 26443 function renderHasNotSuspendedYet() { | |
| 26444 // If something errored or completed, we can't really be sure, | |
| 26445 // so those are false. | |
| 26446 return workInProgressRootExitStatus === RootInProgress; | |
| 26447 } | |
| 26448 | |
| 26449 function renderRootSync(root, lanes) { | |
| 26450 var prevExecutionContext = executionContext; | |
| 26451 executionContext |= RenderContext; | |
| 26452 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack | |
| 26453 // and prepare a fresh one. Otherwise we'll continue where we left off. | |
| 26454 | |
| 26455 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { | |
| 26456 { | |
| 26457 if (isDevToolsPresent) { | |
| 26458 var memoizedUpdaters = root.memoizedUpdaters; | |
| 26459 | |
| 26460 if (memoizedUpdaters.size > 0) { | |
| 26461 restorePendingUpdaters(root, workInProgressRootRenderLanes); | |
| 26462 memoizedUpdaters.clear(); | |
| 26463 } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. | |
| 26464 // If we bailout on this work, we'll move them back (like above). | |
| 26465 // It's important to move them now in case the work spawns more work at the same priority with different updaters. | |
| 26466 // That way we can keep the current update and future updates separate. | |
| 26467 | |
| 26468 | |
| 26469 movePendingFibersToMemoized(root, lanes); | |
| 26470 } | |
| 26471 } | |
| 26472 | |
| 26473 workInProgressTransitions = getTransitionsForLanes(); | |
| 26474 prepareFreshStack(root, lanes); | |
| 26475 } | |
| 26476 | |
| 26477 { | |
| 26478 markRenderStarted(lanes); | |
| 26479 } | |
| 26480 | |
| 26481 do { | |
| 26482 try { | |
| 26483 workLoopSync(); | |
| 26484 break; | |
| 26485 } catch (thrownValue) { | |
| 26486 handleError(root, thrownValue); | |
| 26487 } | |
| 26488 } while (true); | |
| 26489 | |
| 26490 resetContextDependencies(); | |
| 26491 executionContext = prevExecutionContext; | |
| 26492 popDispatcher(prevDispatcher); | |
| 26493 | |
| 26494 if (workInProgress !== null) { | |
| 26495 // This is a sync render, so we should have finished the whole tree. | |
| 26496 throw new Error('Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.'); | |
| 26497 } | |
| 26498 | |
| 26499 { | |
| 26500 markRenderStopped(); | |
| 26501 } // Set this to null to indicate there's no in-progress render. | |
| 26502 | |
| 26503 | |
| 26504 workInProgressRoot = null; | |
| 26505 workInProgressRootRenderLanes = NoLanes; | |
| 26506 return workInProgressRootExitStatus; | |
| 26507 } // The work loop is an extremely hot path. Tell Closure not to inline it. | |
| 26508 | |
| 26509 /** @noinline */ | |
| 26510 | |
| 26511 | |
| 26512 function workLoopSync() { | |
| 26513 // Already timed out, so perform work without checking if we need to yield. | |
| 26514 while (workInProgress !== null) { | |
| 26515 performUnitOfWork(workInProgress); | |
| 26516 } | |
| 26517 } | |
| 26518 | |
| 26519 function renderRootConcurrent(root, lanes) { | |
| 26520 var prevExecutionContext = executionContext; | |
| 26521 executionContext |= RenderContext; | |
| 26522 var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack | |
| 26523 // and prepare a fresh one. Otherwise we'll continue where we left off. | |
| 26524 | |
| 26525 if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) { | |
| 26526 { | |
| 26527 if (isDevToolsPresent) { | |
| 26528 var memoizedUpdaters = root.memoizedUpdaters; | |
| 26529 | |
| 26530 if (memoizedUpdaters.size > 0) { | |
| 26531 restorePendingUpdaters(root, workInProgressRootRenderLanes); | |
| 26532 memoizedUpdaters.clear(); | |
| 26533 } // At this point, move Fibers that scheduled the upcoming work from the Map to the Set. | |
| 26534 // If we bailout on this work, we'll move them back (like above). | |
| 26535 // It's important to move them now in case the work spawns more work at the same priority with different updaters. | |
| 26536 // That way we can keep the current update and future updates separate. | |
| 26537 | |
| 26538 | |
| 26539 movePendingFibersToMemoized(root, lanes); | |
| 26540 } | |
| 26541 } | |
| 26542 | |
| 26543 workInProgressTransitions = getTransitionsForLanes(); | |
| 26544 resetRenderTimer(); | |
| 26545 prepareFreshStack(root, lanes); | |
| 26546 } | |
| 26547 | |
| 26548 { | |
| 26549 markRenderStarted(lanes); | |
| 26550 } | |
| 26551 | |
| 26552 do { | |
| 26553 try { | |
| 26554 workLoopConcurrent(); | |
| 26555 break; | |
| 26556 } catch (thrownValue) { | |
| 26557 handleError(root, thrownValue); | |
| 26558 } | |
| 26559 } while (true); | |
| 26560 | |
| 26561 resetContextDependencies(); | |
| 26562 popDispatcher(prevDispatcher); | |
| 26563 executionContext = prevExecutionContext; | |
| 26564 | |
| 26565 | |
| 26566 if (workInProgress !== null) { | |
| 26567 // Still work remaining. | |
| 26568 { | |
| 26569 markRenderYielded(); | |
| 26570 } | |
| 26571 | |
| 26572 return RootInProgress; | |
| 26573 } else { | |
| 26574 // Completed the tree. | |
| 26575 { | |
| 26576 markRenderStopped(); | |
| 26577 } // Set this to null to indicate there's no in-progress render. | |
| 26578 | |
| 26579 | |
| 26580 workInProgressRoot = null; | |
| 26581 workInProgressRootRenderLanes = NoLanes; // Return the final exit status. | |
| 26582 | |
| 26583 return workInProgressRootExitStatus; | |
| 26584 } | |
| 26585 } | |
| 26586 /** @noinline */ | |
| 26587 | |
| 26588 | |
| 26589 function workLoopConcurrent() { | |
| 26590 // Perform work until Scheduler asks us to yield | |
| 26591 while (workInProgress !== null && !shouldYield()) { | |
| 26592 performUnitOfWork(workInProgress); | |
| 26593 } | |
| 26594 } | |
| 26595 | |
| 26596 function performUnitOfWork(unitOfWork) { | |
| 26597 // The current, flushed, state of this fiber is the alternate. Ideally | |
| 26598 // nothing should rely on this, but relying on it here means that we don't | |
| 26599 // need an additional field on the work in progress. | |
| 26600 var current = unitOfWork.alternate; | |
| 26601 setCurrentFiber(unitOfWork); | |
| 26602 var next; | |
| 26603 | |
| 26604 if ( (unitOfWork.mode & ProfileMode) !== NoMode) { | |
| 26605 startProfilerTimer(unitOfWork); | |
| 26606 next = beginWork$1(current, unitOfWork, subtreeRenderLanes); | |
| 26607 stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true); | |
| 26608 } else { | |
| 26609 next = beginWork$1(current, unitOfWork, subtreeRenderLanes); | |
| 26610 } | |
| 26611 | |
| 26612 resetCurrentFiber(); | |
| 26613 unitOfWork.memoizedProps = unitOfWork.pendingProps; | |
| 26614 | |
| 26615 if (next === null) { | |
| 26616 // If this doesn't spawn new work, complete the current work. | |
| 26617 completeUnitOfWork(unitOfWork); | |
| 26618 } else { | |
| 26619 workInProgress = next; | |
| 26620 } | |
| 26621 | |
| 26622 ReactCurrentOwner$2.current = null; | |
| 26623 } | |
| 26624 | |
| 26625 function completeUnitOfWork(unitOfWork) { | |
| 26626 // Attempt to complete the current unit of work, then move to the next | |
| 26627 // sibling. If there are no more siblings, return to the parent fiber. | |
| 26628 var completedWork = unitOfWork; | |
| 26629 | |
| 26630 do { | |
| 26631 // The current, flushed, state of this fiber is the alternate. Ideally | |
| 26632 // nothing should rely on this, but relying on it here means that we don't | |
| 26633 // need an additional field on the work in progress. | |
| 26634 var current = completedWork.alternate; | |
| 26635 var returnFiber = completedWork.return; // Check if the work completed or if something threw. | |
| 26636 | |
| 26637 if ((completedWork.flags & Incomplete) === NoFlags) { | |
| 26638 setCurrentFiber(completedWork); | |
| 26639 var next = void 0; | |
| 26640 | |
| 26641 if ( (completedWork.mode & ProfileMode) === NoMode) { | |
| 26642 next = completeWork(current, completedWork, subtreeRenderLanes); | |
| 26643 } else { | |
| 26644 startProfilerTimer(completedWork); | |
| 26645 next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error. | |
| 26646 | |
| 26647 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); | |
| 26648 } | |
| 26649 | |
| 26650 resetCurrentFiber(); | |
| 26651 | |
| 26652 if (next !== null) { | |
| 26653 // Completing this fiber spawned new work. Work on that next. | |
| 26654 workInProgress = next; | |
| 26655 return; | |
| 26656 } | |
| 26657 } else { | |
| 26658 // This fiber did not complete because something threw. Pop values off | |
| 26659 // the stack without entering the complete phase. If this is a boundary, | |
| 26660 // capture values if possible. | |
| 26661 var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes. | |
| 26662 | |
| 26663 | |
| 26664 if (_next !== null) { | |
| 26665 // If completing this work spawned new work, do that next. We'll come | |
| 26666 // back here again. | |
| 26667 // Since we're restarting, remove anything that is not a host effect | |
| 26668 // from the effect tag. | |
| 26669 _next.flags &= HostEffectMask; | |
| 26670 workInProgress = _next; | |
| 26671 return; | |
| 26672 } | |
| 26673 | |
| 26674 if ( (completedWork.mode & ProfileMode) !== NoMode) { | |
| 26675 // Record the render duration for the fiber that errored. | |
| 26676 stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing. | |
| 26677 | |
| 26678 var actualDuration = completedWork.actualDuration; | |
| 26679 var child = completedWork.child; | |
| 26680 | |
| 26681 while (child !== null) { | |
| 26682 actualDuration += child.actualDuration; | |
| 26683 child = child.sibling; | |
| 26684 } | |
| 26685 | |
| 26686 completedWork.actualDuration = actualDuration; | |
| 26687 } | |
| 26688 | |
| 26689 if (returnFiber !== null) { | |
| 26690 // Mark the parent fiber as incomplete and clear its subtree flags. | |
| 26691 returnFiber.flags |= Incomplete; | |
| 26692 returnFiber.subtreeFlags = NoFlags; | |
| 26693 returnFiber.deletions = null; | |
| 26694 } else { | |
| 26695 // We've unwound all the way to the root. | |
| 26696 workInProgressRootExitStatus = RootDidNotComplete; | |
| 26697 workInProgress = null; | |
| 26698 return; | |
| 26699 } | |
| 26700 } | |
| 26701 | |
| 26702 var siblingFiber = completedWork.sibling; | |
| 26703 | |
| 26704 if (siblingFiber !== null) { | |
| 26705 // If there is more work to do in this returnFiber, do that next. | |
| 26706 workInProgress = siblingFiber; | |
| 26707 return; | |
| 26708 } // Otherwise, return to the parent | |
| 26709 | |
| 26710 | |
| 26711 completedWork = returnFiber; // Update the next thing we're working on in case something throws. | |
| 26712 | |
| 26713 workInProgress = completedWork; | |
| 26714 } while (completedWork !== null); // We've reached the root. | |
| 26715 | |
| 26716 | |
| 26717 if (workInProgressRootExitStatus === RootInProgress) { | |
| 26718 workInProgressRootExitStatus = RootCompleted; | |
| 26719 } | |
| 26720 } | |
| 26721 | |
| 26722 function commitRoot(root, recoverableErrors, transitions) { | |
| 26723 // TODO: This no longer makes any sense. We already wrap the mutation and | |
| 26724 // layout phases. Should be able to remove. | |
| 26725 var previousUpdateLanePriority = getCurrentUpdatePriority(); | |
| 26726 var prevTransition = ReactCurrentBatchConfig$3.transition; | |
| 26727 | |
| 26728 try { | |
| 26729 ReactCurrentBatchConfig$3.transition = null; | |
| 26730 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 26731 commitRootImpl(root, recoverableErrors, transitions, previousUpdateLanePriority); | |
| 26732 } finally { | |
| 26733 ReactCurrentBatchConfig$3.transition = prevTransition; | |
| 26734 setCurrentUpdatePriority(previousUpdateLanePriority); | |
| 26735 } | |
| 26736 | |
| 26737 return null; | |
| 26738 } | |
| 26739 | |
| 26740 function commitRootImpl(root, recoverableErrors, transitions, renderPriorityLevel) { | |
| 26741 do { | |
| 26742 // `flushPassiveEffects` will call `flushSyncUpdateQueue` at the end, which | |
| 26743 // means `flushPassiveEffects` will sometimes result in additional | |
| 26744 // passive effects. So we need to keep flushing in a loop until there are | |
| 26745 // no more pending effects. | |
| 26746 // TODO: Might be better if `flushPassiveEffects` did not automatically | |
| 26747 // flush synchronous work at the end, to avoid factoring hazards like this. | |
| 26748 flushPassiveEffects(); | |
| 26749 } while (rootWithPendingPassiveEffects !== null); | |
| 26750 | |
| 26751 flushRenderPhaseStrictModeWarningsInDEV(); | |
| 26752 | |
| 26753 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { | |
| 26754 throw new Error('Should not already be working.'); | |
| 26755 } | |
| 26756 | |
| 26757 var finishedWork = root.finishedWork; | |
| 26758 var lanes = root.finishedLanes; | |
| 26759 | |
| 26760 { | |
| 26761 markCommitStarted(lanes); | |
| 26762 } | |
| 26763 | |
| 26764 if (finishedWork === null) { | |
| 26765 | |
| 26766 { | |
| 26767 markCommitStopped(); | |
| 26768 } | |
| 26769 | |
| 26770 return null; | |
| 26771 } else { | |
| 26772 { | |
| 26773 if (lanes === NoLanes) { | |
| 26774 error('root.finishedLanes should not be empty during a commit. This is a ' + 'bug in React.'); | |
| 26775 } | |
| 26776 } | |
| 26777 } | |
| 26778 | |
| 26779 root.finishedWork = null; | |
| 26780 root.finishedLanes = NoLanes; | |
| 26781 | |
| 26782 if (finishedWork === root.current) { | |
| 26783 throw new Error('Cannot commit the same tree as before. This error is likely caused by ' + 'a bug in React. Please file an issue.'); | |
| 26784 } // commitRoot never returns a continuation; it always finishes synchronously. | |
| 26785 // So we can clear these now to allow a new callback to be scheduled. | |
| 26786 | |
| 26787 | |
| 26788 root.callbackNode = null; | |
| 26789 root.callbackPriority = NoLane; // Update the first and last pending times on this root. The new first | |
| 26790 // pending time is whatever is left on the root fiber. | |
| 26791 | |
| 26792 var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes); | |
| 26793 markRootFinished(root, remainingLanes); | |
| 26794 | |
| 26795 if (root === workInProgressRoot) { | |
| 26796 // We can reset these now that they are finished. | |
| 26797 workInProgressRoot = null; | |
| 26798 workInProgress = null; | |
| 26799 workInProgressRootRenderLanes = NoLanes; | |
| 26800 } // If there are pending passive effects, schedule a callback to process them. | |
| 26801 // Do this as early as possible, so it is queued before anything else that | |
| 26802 // might get scheduled in the commit phase. (See #16714.) | |
| 26803 // TODO: Delete all other places that schedule the passive effect callback | |
| 26804 // They're redundant. | |
| 26805 | |
| 26806 | |
| 26807 if ((finishedWork.subtreeFlags & PassiveMask) !== NoFlags || (finishedWork.flags & PassiveMask) !== NoFlags) { | |
| 26808 if (!rootDoesHavePassiveEffects) { | |
| 26809 rootDoesHavePassiveEffects = true; | |
| 26810 // to store it in pendingPassiveTransitions until they get processed | |
| 26811 // We need to pass this through as an argument to commitRoot | |
| 26812 // because workInProgressTransitions might have changed between | |
| 26813 // the previous render and commit if we throttle the commit | |
| 26814 // with setTimeout | |
| 26815 | |
| 26816 pendingPassiveTransitions = transitions; | |
| 26817 scheduleCallback$1(NormalPriority, function () { | |
| 26818 flushPassiveEffects(); // This render triggered passive effects: release the root cache pool | |
| 26819 // *after* passive effects fire to avoid freeing a cache pool that may | |
| 26820 // be referenced by a node in the tree (HostRoot, Cache boundary etc) | |
| 26821 | |
| 26822 return null; | |
| 26823 }); | |
| 26824 } | |
| 26825 } // Check if there are any effects in the whole tree. | |
| 26826 // TODO: This is left over from the effect list implementation, where we had | |
| 26827 // to check for the existence of `firstEffect` to satisfy Flow. I think the | |
| 26828 // only other reason this optimization exists is because it affects profiling. | |
| 26829 // Reconsider whether this is necessary. | |
| 26830 | |
| 26831 | |
| 26832 var subtreeHasEffects = (finishedWork.subtreeFlags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags; | |
| 26833 var rootHasEffect = (finishedWork.flags & (BeforeMutationMask | MutationMask | LayoutMask | PassiveMask)) !== NoFlags; | |
| 26834 | |
| 26835 if (subtreeHasEffects || rootHasEffect) { | |
| 26836 var prevTransition = ReactCurrentBatchConfig$3.transition; | |
| 26837 ReactCurrentBatchConfig$3.transition = null; | |
| 26838 var previousPriority = getCurrentUpdatePriority(); | |
| 26839 setCurrentUpdatePriority(DiscreteEventPriority); | |
| 26840 var prevExecutionContext = executionContext; | |
| 26841 executionContext |= CommitContext; // Reset this to null before calling lifecycles | |
| 26842 | |
| 26843 ReactCurrentOwner$2.current = null; // The commit phase is broken into several sub-phases. We do a separate pass | |
| 26844 // of the effect list for each phase: all mutation effects come before all | |
| 26845 // layout effects, and so on. | |
| 26846 // The first phase a "before mutation" phase. We use this phase to read the | |
| 26847 // state of the host tree right before we mutate it. This is where | |
| 26848 // getSnapshotBeforeUpdate is called. | |
| 26849 | |
| 26850 var shouldFireAfterActiveInstanceBlur = commitBeforeMutationEffects(root, finishedWork); | |
| 26851 | |
| 26852 { | |
| 26853 // Mark the current commit time to be shared by all Profilers in this | |
| 26854 // batch. This enables them to be grouped later. | |
| 26855 recordCommitTime(); | |
| 26856 } | |
| 26857 | |
| 26858 | |
| 26859 commitMutationEffects(root, finishedWork, lanes); | |
| 26860 | |
| 26861 resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after | |
| 26862 // the mutation phase, so that the previous tree is still current during | |
| 26863 // componentWillUnmount, but before the layout phase, so that the finished | |
| 26864 // work is current during componentDidMount/Update. | |
| 26865 | |
| 26866 root.current = finishedWork; // The next phase is the layout phase, where we call effects that read | |
| 26867 | |
| 26868 { | |
| 26869 markLayoutEffectsStarted(lanes); | |
| 26870 } | |
| 26871 | |
| 26872 commitLayoutEffects(finishedWork, root, lanes); | |
| 26873 | |
| 26874 { | |
| 26875 markLayoutEffectsStopped(); | |
| 26876 } | |
| 26877 // opportunity to paint. | |
| 26878 | |
| 26879 | |
| 26880 requestPaint(); | |
| 26881 executionContext = prevExecutionContext; // Reset the priority to the previous non-sync value. | |
| 26882 | |
| 26883 setCurrentUpdatePriority(previousPriority); | |
| 26884 ReactCurrentBatchConfig$3.transition = prevTransition; | |
| 26885 } else { | |
| 26886 // No effects. | |
| 26887 root.current = finishedWork; // Measure these anyway so the flamegraph explicitly shows that there were | |
| 26888 // no effects. | |
| 26889 // TODO: Maybe there's a better way to report this. | |
| 26890 | |
| 26891 { | |
| 26892 recordCommitTime(); | |
| 26893 } | |
| 26894 } | |
| 26895 | |
| 26896 var rootDidHavePassiveEffects = rootDoesHavePassiveEffects; | |
| 26897 | |
| 26898 if (rootDoesHavePassiveEffects) { | |
| 26899 // This commit has passive effects. Stash a reference to them. But don't | |
| 26900 // schedule a callback until after flushing layout work. | |
| 26901 rootDoesHavePassiveEffects = false; | |
| 26902 rootWithPendingPassiveEffects = root; | |
| 26903 pendingPassiveEffectsLanes = lanes; | |
| 26904 } else { | |
| 26905 | |
| 26906 { | |
| 26907 nestedPassiveUpdateCount = 0; | |
| 26908 rootWithPassiveNestedUpdates = null; | |
| 26909 } | |
| 26910 } // Read this again, since an effect might have updated it | |
| 26911 | |
| 26912 | |
| 26913 remainingLanes = root.pendingLanes; // Check if there's remaining work on this root | |
| 26914 // TODO: This is part of the `componentDidCatch` implementation. Its purpose | |
| 26915 // is to detect whether something might have called setState inside | |
| 26916 // `componentDidCatch`. The mechanism is known to be flawed because `setState` | |
| 26917 // inside `componentDidCatch` is itself flawed — that's why we recommend | |
| 26918 // `getDerivedStateFromError` instead. However, it could be improved by | |
| 26919 // checking if remainingLanes includes Sync work, instead of whether there's | |
| 26920 // any work remaining at all (which would also include stuff like Suspense | |
| 26921 // retries or transitions). It's been like this for a while, though, so fixing | |
| 26922 // it probably isn't that urgent. | |
| 26923 | |
| 26924 if (remainingLanes === NoLanes) { | |
| 26925 // If there's no remaining work, we can clear the set of already failed | |
| 26926 // error boundaries. | |
| 26927 legacyErrorBoundariesThatAlreadyFailed = null; | |
| 26928 } | |
| 26929 | |
| 26930 { | |
| 26931 if (!rootDidHavePassiveEffects) { | |
| 26932 commitDoubleInvokeEffectsInDEV(root.current, false); | |
| 26933 } | |
| 26934 } | |
| 26935 | |
| 26936 onCommitRoot(finishedWork.stateNode, renderPriorityLevel); | |
| 26937 | |
| 26938 { | |
| 26939 if (isDevToolsPresent) { | |
| 26940 root.memoizedUpdaters.clear(); | |
| 26941 } | |
| 26942 } | |
| 26943 | |
| 26944 { | |
| 26945 onCommitRoot$1(); | |
| 26946 } // Always call this before exiting `commitRoot`, to ensure that any | |
| 26947 // additional work on this root is scheduled. | |
| 26948 | |
| 26949 | |
| 26950 ensureRootIsScheduled(root, now()); | |
| 26951 | |
| 26952 if (recoverableErrors !== null) { | |
| 26953 // There were errors during this render, but recovered from them without | |
| 26954 // needing to surface it to the UI. We log them here. | |
| 26955 var onRecoverableError = root.onRecoverableError; | |
| 26956 | |
| 26957 for (var i = 0; i < recoverableErrors.length; i++) { | |
| 26958 var recoverableError = recoverableErrors[i]; | |
| 26959 var componentStack = recoverableError.stack; | |
| 26960 var digest = recoverableError.digest; | |
| 26961 onRecoverableError(recoverableError.value, { | |
| 26962 componentStack: componentStack, | |
| 26963 digest: digest | |
| 26964 }); | |
| 26965 } | |
| 26966 } | |
| 26967 | |
| 26968 if (hasUncaughtError) { | |
| 26969 hasUncaughtError = false; | |
| 26970 var error$1 = firstUncaughtError; | |
| 26971 firstUncaughtError = null; | |
| 26972 throw error$1; | |
| 26973 } // If the passive effects are the result of a discrete render, flush them | |
| 26974 // synchronously at the end of the current task so that the result is | |
| 26975 // immediately observable. Otherwise, we assume that they are not | |
| 26976 // order-dependent and do not need to be observed by external systems, so we | |
| 26977 // can wait until after paint. | |
| 26978 // TODO: We can optimize this by not scheduling the callback earlier. Since we | |
| 26979 // currently schedule the callback in multiple places, will wait until those | |
| 26980 // are consolidated. | |
| 26981 | |
| 26982 | |
| 26983 if (includesSomeLane(pendingPassiveEffectsLanes, SyncLane) && root.tag !== LegacyRoot) { | |
| 26984 flushPassiveEffects(); | |
| 26985 } // Read this again, since a passive effect might have updated it | |
| 26986 | |
| 26987 | |
| 26988 remainingLanes = root.pendingLanes; | |
| 26989 | |
| 26990 if (includesSomeLane(remainingLanes, SyncLane)) { | |
| 26991 { | |
| 26992 markNestedUpdateScheduled(); | |
| 26993 } // Count the number of times the root synchronously re-renders without | |
| 26994 // finishing. If there are too many, it indicates an infinite update loop. | |
| 26995 | |
| 26996 | |
| 26997 if (root === rootWithNestedUpdates) { | |
| 26998 nestedUpdateCount++; | |
| 26999 } else { | |
| 27000 nestedUpdateCount = 0; | |
| 27001 rootWithNestedUpdates = root; | |
| 27002 } | |
| 27003 } else { | |
| 27004 nestedUpdateCount = 0; | |
| 27005 } // If layout work was scheduled, flush it now. | |
| 27006 | |
| 27007 | |
| 27008 flushSyncCallbacks(); | |
| 27009 | |
| 27010 { | |
| 27011 markCommitStopped(); | |
| 27012 } | |
| 27013 | |
| 27014 return null; | |
| 27015 } | |
| 27016 | |
| 27017 function flushPassiveEffects() { | |
| 27018 // Returns whether passive effects were flushed. | |
| 27019 // TODO: Combine this check with the one in flushPassiveEFfectsImpl. We should | |
| 27020 // probably just combine the two functions. I believe they were only separate | |
| 27021 // in the first place because we used to wrap it with | |
| 27022 // `Scheduler.runWithPriority`, which accepts a function. But now we track the | |
| 27023 // priority within React itself, so we can mutate the variable directly. | |
| 27024 if (rootWithPendingPassiveEffects !== null) { | |
| 27025 var renderPriority = lanesToEventPriority(pendingPassiveEffectsLanes); | |
| 27026 var priority = lowerEventPriority(DefaultEventPriority, renderPriority); | |
| 27027 var prevTransition = ReactCurrentBatchConfig$3.transition; | |
| 27028 var previousPriority = getCurrentUpdatePriority(); | |
| 27029 | |
| 27030 try { | |
| 27031 ReactCurrentBatchConfig$3.transition = null; | |
| 27032 setCurrentUpdatePriority(priority); | |
| 27033 return flushPassiveEffectsImpl(); | |
| 27034 } finally { | |
| 27035 setCurrentUpdatePriority(previousPriority); | |
| 27036 ReactCurrentBatchConfig$3.transition = prevTransition; // Once passive effects have run for the tree - giving components a | |
| 27037 } | |
| 27038 } | |
| 27039 | |
| 27040 return false; | |
| 27041 } | |
| 27042 function enqueuePendingPassiveProfilerEffect(fiber) { | |
| 27043 { | |
| 27044 pendingPassiveProfilerEffects.push(fiber); | |
| 27045 | |
| 27046 if (!rootDoesHavePassiveEffects) { | |
| 27047 rootDoesHavePassiveEffects = true; | |
| 27048 scheduleCallback$1(NormalPriority, function () { | |
| 27049 flushPassiveEffects(); | |
| 27050 return null; | |
| 27051 }); | |
| 27052 } | |
| 27053 } | |
| 27054 } | |
| 27055 | |
| 27056 function flushPassiveEffectsImpl() { | |
| 27057 if (rootWithPendingPassiveEffects === null) { | |
| 27058 return false; | |
| 27059 } // Cache and clear the transitions flag | |
| 27060 | |
| 27061 | |
| 27062 var transitions = pendingPassiveTransitions; | |
| 27063 pendingPassiveTransitions = null; | |
| 27064 var root = rootWithPendingPassiveEffects; | |
| 27065 var lanes = pendingPassiveEffectsLanes; | |
| 27066 rootWithPendingPassiveEffects = null; // TODO: This is sometimes out of sync with rootWithPendingPassiveEffects. | |
| 27067 // Figure out why and fix it. It's not causing any known issues (probably | |
| 27068 // because it's only used for profiling), but it's a refactor hazard. | |
| 27069 | |
| 27070 pendingPassiveEffectsLanes = NoLanes; | |
| 27071 | |
| 27072 if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { | |
| 27073 throw new Error('Cannot flush passive effects while already rendering.'); | |
| 27074 } | |
| 27075 | |
| 27076 { | |
| 27077 isFlushingPassiveEffects = true; | |
| 27078 didScheduleUpdateDuringPassiveEffects = false; | |
| 27079 } | |
| 27080 | |
| 27081 { | |
| 27082 markPassiveEffectsStarted(lanes); | |
| 27083 } | |
| 27084 | |
| 27085 var prevExecutionContext = executionContext; | |
| 27086 executionContext |= CommitContext; | |
| 27087 commitPassiveUnmountEffects(root.current); | |
| 27088 commitPassiveMountEffects(root, root.current, lanes, transitions); // TODO: Move to commitPassiveMountEffects | |
| 27089 | |
| 27090 { | |
| 27091 var profilerEffects = pendingPassiveProfilerEffects; | |
| 27092 pendingPassiveProfilerEffects = []; | |
| 27093 | |
| 27094 for (var i = 0; i < profilerEffects.length; i++) { | |
| 27095 var _fiber = profilerEffects[i]; | |
| 27096 commitPassiveEffectDurations(root, _fiber); | |
| 27097 } | |
| 27098 } | |
| 27099 | |
| 27100 { | |
| 27101 markPassiveEffectsStopped(); | |
| 27102 } | |
| 27103 | |
| 27104 { | |
| 27105 commitDoubleInvokeEffectsInDEV(root.current, true); | |
| 27106 } | |
| 27107 | |
| 27108 executionContext = prevExecutionContext; | |
| 27109 flushSyncCallbacks(); | |
| 27110 | |
| 27111 { | |
| 27112 // If additional passive effects were scheduled, increment a counter. If this | |
| 27113 // exceeds the limit, we'll fire a warning. | |
| 27114 if (didScheduleUpdateDuringPassiveEffects) { | |
| 27115 if (root === rootWithPassiveNestedUpdates) { | |
| 27116 nestedPassiveUpdateCount++; | |
| 27117 } else { | |
| 27118 nestedPassiveUpdateCount = 0; | |
| 27119 rootWithPassiveNestedUpdates = root; | |
| 27120 } | |
| 27121 } else { | |
| 27122 nestedPassiveUpdateCount = 0; | |
| 27123 } | |
| 27124 | |
| 27125 isFlushingPassiveEffects = false; | |
| 27126 didScheduleUpdateDuringPassiveEffects = false; | |
| 27127 } // TODO: Move to commitPassiveMountEffects | |
| 27128 | |
| 27129 | |
| 27130 onPostCommitRoot(root); | |
| 27131 | |
| 27132 { | |
| 27133 var stateNode = root.current.stateNode; | |
| 27134 stateNode.effectDuration = 0; | |
| 27135 stateNode.passiveEffectDuration = 0; | |
| 27136 } | |
| 27137 | |
| 27138 return true; | |
| 27139 } | |
| 27140 | |
| 27141 function isAlreadyFailedLegacyErrorBoundary(instance) { | |
| 27142 return legacyErrorBoundariesThatAlreadyFailed !== null && legacyErrorBoundariesThatAlreadyFailed.has(instance); | |
| 27143 } | |
| 27144 function markLegacyErrorBoundaryAsFailed(instance) { | |
| 27145 if (legacyErrorBoundariesThatAlreadyFailed === null) { | |
| 27146 legacyErrorBoundariesThatAlreadyFailed = new Set([instance]); | |
| 27147 } else { | |
| 27148 legacyErrorBoundariesThatAlreadyFailed.add(instance); | |
| 27149 } | |
| 27150 } | |
| 27151 | |
| 27152 function prepareToThrowUncaughtError(error) { | |
| 27153 if (!hasUncaughtError) { | |
| 27154 hasUncaughtError = true; | |
| 27155 firstUncaughtError = error; | |
| 27156 } | |
| 27157 } | |
| 27158 | |
| 27159 var onUncaughtError = prepareToThrowUncaughtError; | |
| 27160 | |
| 27161 function captureCommitPhaseErrorOnRoot(rootFiber, sourceFiber, error) { | |
| 27162 var errorInfo = createCapturedValueAtFiber(error, sourceFiber); | |
| 27163 var update = createRootErrorUpdate(rootFiber, errorInfo, SyncLane); | |
| 27164 var root = enqueueUpdate(rootFiber, update, SyncLane); | |
| 27165 var eventTime = requestEventTime(); | |
| 27166 | |
| 27167 if (root !== null) { | |
| 27168 markRootUpdated(root, SyncLane, eventTime); | |
| 27169 ensureRootIsScheduled(root, eventTime); | |
| 27170 } | |
| 27171 } | |
| 27172 | |
| 27173 function captureCommitPhaseError(sourceFiber, nearestMountedAncestor, error$1) { | |
| 27174 { | |
| 27175 reportUncaughtErrorInDEV(error$1); | |
| 27176 setIsRunningInsertionEffect(false); | |
| 27177 } | |
| 27178 | |
| 27179 if (sourceFiber.tag === HostRoot) { | |
| 27180 // Error was thrown at the root. There is no parent, so the root | |
| 27181 // itself should capture it. | |
| 27182 captureCommitPhaseErrorOnRoot(sourceFiber, sourceFiber, error$1); | |
| 27183 return; | |
| 27184 } | |
| 27185 | |
| 27186 var fiber = null; | |
| 27187 | |
| 27188 { | |
| 27189 fiber = nearestMountedAncestor; | |
| 27190 } | |
| 27191 | |
| 27192 while (fiber !== null) { | |
| 27193 if (fiber.tag === HostRoot) { | |
| 27194 captureCommitPhaseErrorOnRoot(fiber, sourceFiber, error$1); | |
| 27195 return; | |
| 27196 } else if (fiber.tag === ClassComponent) { | |
| 27197 var ctor = fiber.type; | |
| 27198 var instance = fiber.stateNode; | |
| 27199 | |
| 27200 if (typeof ctor.getDerivedStateFromError === 'function' || typeof instance.componentDidCatch === 'function' && !isAlreadyFailedLegacyErrorBoundary(instance)) { | |
| 27201 var errorInfo = createCapturedValueAtFiber(error$1, sourceFiber); | |
| 27202 var update = createClassErrorUpdate(fiber, errorInfo, SyncLane); | |
| 27203 var root = enqueueUpdate(fiber, update, SyncLane); | |
| 27204 var eventTime = requestEventTime(); | |
| 27205 | |
| 27206 if (root !== null) { | |
| 27207 markRootUpdated(root, SyncLane, eventTime); | |
| 27208 ensureRootIsScheduled(root, eventTime); | |
| 27209 } | |
| 27210 | |
| 27211 return; | |
| 27212 } | |
| 27213 } | |
| 27214 | |
| 27215 fiber = fiber.return; | |
| 27216 } | |
| 27217 | |
| 27218 { | |
| 27219 // TODO: Until we re-land skipUnmountedBoundaries (see #20147), this warning | |
| 27220 // will fire for errors that are thrown by destroy functions inside deleted | |
| 27221 // trees. What it should instead do is propagate the error to the parent of | |
| 27222 // the deleted tree. In the meantime, do not add this warning to the | |
| 27223 // allowlist; this is only for our internal use. | |
| 27224 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); | |
| 27225 } | |
| 27226 } | |
| 27227 function pingSuspendedRoot(root, wakeable, pingedLanes) { | |
| 27228 var pingCache = root.pingCache; | |
| 27229 | |
| 27230 if (pingCache !== null) { | |
| 27231 // The wakeable resolved, so we no longer need to memoize, because it will | |
| 27232 // never be thrown again. | |
| 27233 pingCache.delete(wakeable); | |
| 27234 } | |
| 27235 | |
| 27236 var eventTime = requestEventTime(); | |
| 27237 markRootPinged(root, pingedLanes); | |
| 27238 warnIfSuspenseResolutionNotWrappedWithActDEV(root); | |
| 27239 | |
| 27240 if (workInProgressRoot === root && isSubsetOfLanes(workInProgressRootRenderLanes, pingedLanes)) { | |
| 27241 // Received a ping at the same priority level at which we're currently | |
| 27242 // rendering. We might want to restart this render. This should mirror | |
| 27243 // the logic of whether or not a root suspends once it completes. | |
| 27244 // TODO: If we're rendering sync either due to Sync, Batched or expired, | |
| 27245 // we should probably never restart. | |
| 27246 // If we're suspended with delay, or if it's a retry, we'll always suspend | |
| 27247 // so we can always restart. | |
| 27248 if (workInProgressRootExitStatus === RootSuspendedWithDelay || workInProgressRootExitStatus === RootSuspended && includesOnlyRetries(workInProgressRootRenderLanes) && now() - globalMostRecentFallbackTime < FALLBACK_THROTTLE_MS) { | |
| 27249 // Restart from the root. | |
| 27250 prepareFreshStack(root, NoLanes); | |
| 27251 } else { | |
| 27252 // Even though we can't restart right now, we might get an | |
| 27253 // opportunity later. So we mark this render as having a ping. | |
| 27254 workInProgressRootPingedLanes = mergeLanes(workInProgressRootPingedLanes, pingedLanes); | |
| 27255 } | |
| 27256 } | |
| 27257 | |
| 27258 ensureRootIsScheduled(root, eventTime); | |
| 27259 } | |
| 27260 | |
| 27261 function retryTimedOutBoundary(boundaryFiber, retryLane) { | |
| 27262 // The boundary fiber (a Suspense component or SuspenseList component) | |
| 27263 // previously was rendered in its fallback state. One of the promises that | |
| 27264 // suspended it has resolved, which means at least part of the tree was | |
| 27265 // likely unblocked. Try rendering again, at a new lanes. | |
| 27266 if (retryLane === NoLane) { | |
| 27267 // TODO: Assign this to `suspenseState.retryLane`? to avoid | |
| 27268 // unnecessary entanglement? | |
| 27269 retryLane = requestRetryLane(boundaryFiber); | |
| 27270 } // TODO: Special case idle priority? | |
| 27271 | |
| 27272 | |
| 27273 var eventTime = requestEventTime(); | |
| 27274 var root = enqueueConcurrentRenderForLane(boundaryFiber, retryLane); | |
| 27275 | |
| 27276 if (root !== null) { | |
| 27277 markRootUpdated(root, retryLane, eventTime); | |
| 27278 ensureRootIsScheduled(root, eventTime); | |
| 27279 } | |
| 27280 } | |
| 27281 | |
| 27282 function retryDehydratedSuspenseBoundary(boundaryFiber) { | |
| 27283 var suspenseState = boundaryFiber.memoizedState; | |
| 27284 var retryLane = NoLane; | |
| 27285 | |
| 27286 if (suspenseState !== null) { | |
| 27287 retryLane = suspenseState.retryLane; | |
| 27288 } | |
| 27289 | |
| 27290 retryTimedOutBoundary(boundaryFiber, retryLane); | |
| 27291 } | |
| 27292 function resolveRetryWakeable(boundaryFiber, wakeable) { | |
| 27293 var retryLane = NoLane; // Default | |
| 27294 | |
| 27295 var retryCache; | |
| 27296 | |
| 27297 switch (boundaryFiber.tag) { | |
| 27298 case SuspenseComponent: | |
| 27299 retryCache = boundaryFiber.stateNode; | |
| 27300 var suspenseState = boundaryFiber.memoizedState; | |
| 27301 | |
| 27302 if (suspenseState !== null) { | |
| 27303 retryLane = suspenseState.retryLane; | |
| 27304 } | |
| 27305 | |
| 27306 break; | |
| 27307 | |
| 27308 case SuspenseListComponent: | |
| 27309 retryCache = boundaryFiber.stateNode; | |
| 27310 break; | |
| 27311 | |
| 27312 default: | |
| 27313 throw new Error('Pinged unknown suspense boundary type. ' + 'This is probably a bug in React.'); | |
| 27314 } | |
| 27315 | |
| 27316 if (retryCache !== null) { | |
| 27317 // The wakeable resolved, so we no longer need to memoize, because it will | |
| 27318 // never be thrown again. | |
| 27319 retryCache.delete(wakeable); | |
| 27320 } | |
| 27321 | |
| 27322 retryTimedOutBoundary(boundaryFiber, retryLane); | |
| 27323 } // Computes the next Just Noticeable Difference (JND) boundary. | |
| 27324 // The theory is that a person can't tell the difference between small differences in time. | |
| 27325 // Therefore, if we wait a bit longer than necessary that won't translate to a noticeable | |
| 27326 // difference in the experience. However, waiting for longer might mean that we can avoid | |
| 27327 // showing an intermediate loading state. The longer we have already waited, the harder it | |
| 27328 // is to tell small differences in time. Therefore, the longer we've already waited, | |
| 27329 // the longer we can wait additionally. At some point we have to give up though. | |
| 27330 // We pick a train model where the next boundary commits at a consistent schedule. | |
| 27331 // These particular numbers are vague estimates. We expect to adjust them based on research. | |
| 27332 | |
| 27333 function jnd(timeElapsed) { | |
| 27334 return timeElapsed < 120 ? 120 : timeElapsed < 480 ? 480 : timeElapsed < 1080 ? 1080 : timeElapsed < 1920 ? 1920 : timeElapsed < 3000 ? 3000 : timeElapsed < 4320 ? 4320 : ceil(timeElapsed / 1960) * 1960; | |
| 27335 } | |
| 27336 | |
| 27337 function checkForNestedUpdates() { | |
| 27338 if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { | |
| 27339 nestedUpdateCount = 0; | |
| 27340 rootWithNestedUpdates = null; | |
| 27341 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.'); | |
| 27342 } | |
| 27343 | |
| 27344 { | |
| 27345 if (nestedPassiveUpdateCount > NESTED_PASSIVE_UPDATE_LIMIT) { | |
| 27346 nestedPassiveUpdateCount = 0; | |
| 27347 rootWithPassiveNestedUpdates = null; | |
| 27348 | |
| 27349 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.'); | |
| 27350 } | |
| 27351 } | |
| 27352 } | |
| 27353 | |
| 27354 function flushRenderPhaseStrictModeWarningsInDEV() { | |
| 27355 { | |
| 27356 ReactStrictModeWarnings.flushLegacyContextWarning(); | |
| 27357 | |
| 27358 { | |
| 27359 ReactStrictModeWarnings.flushPendingUnsafeLifecycleWarnings(); | |
| 27360 } | |
| 27361 } | |
| 27362 } | |
| 27363 | |
| 27364 function commitDoubleInvokeEffectsInDEV(fiber, hasPassiveEffects) { | |
| 27365 { | |
| 27366 // TODO (StrictEffects) Should we set a marker on the root if it contains strict effects | |
| 27367 // so we don't traverse unnecessarily? similar to subtreeFlags but just at the root level. | |
| 27368 // Maybe not a big deal since this is DEV only behavior. | |
| 27369 setCurrentFiber(fiber); | |
| 27370 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectUnmountInDEV); | |
| 27371 | |
| 27372 if (hasPassiveEffects) { | |
| 27373 invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectUnmountInDEV); | |
| 27374 } | |
| 27375 | |
| 27376 invokeEffectsInDev(fiber, MountLayoutDev, invokeLayoutEffectMountInDEV); | |
| 27377 | |
| 27378 if (hasPassiveEffects) { | |
| 27379 invokeEffectsInDev(fiber, MountPassiveDev, invokePassiveEffectMountInDEV); | |
| 27380 } | |
| 27381 | |
| 27382 resetCurrentFiber(); | |
| 27383 } | |
| 27384 } | |
| 27385 | |
| 27386 function invokeEffectsInDev(firstChild, fiberFlags, invokeEffectFn) { | |
| 27387 { | |
| 27388 // We don't need to re-check StrictEffectsMode here. | |
| 27389 // This function is only called if that check has already passed. | |
| 27390 var current = firstChild; | |
| 27391 var subtreeRoot = null; | |
| 27392 | |
| 27393 while (current !== null) { | |
| 27394 var primarySubtreeFlag = current.subtreeFlags & fiberFlags; | |
| 27395 | |
| 27396 if (current !== subtreeRoot && current.child !== null && primarySubtreeFlag !== NoFlags) { | |
| 27397 current = current.child; | |
| 27398 } else { | |
| 27399 if ((current.flags & fiberFlags) !== NoFlags) { | |
| 27400 invokeEffectFn(current); | |
| 27401 } | |
| 27402 | |
| 27403 if (current.sibling !== null) { | |
| 27404 current = current.sibling; | |
| 27405 } else { | |
| 27406 current = subtreeRoot = current.return; | |
| 27407 } | |
| 27408 } | |
| 27409 } | |
| 27410 } | |
| 27411 } | |
| 27412 | |
| 27413 var didWarnStateUpdateForNotYetMountedComponent = null; | |
| 27414 function warnAboutUpdateOnNotYetMountedFiberInDEV(fiber) { | |
| 27415 { | |
| 27416 if ((executionContext & RenderContext) !== NoContext) { | |
| 27417 // We let the other warning about render phase updates deal with this one. | |
| 27418 return; | |
| 27419 } | |
| 27420 | |
| 27421 if (!(fiber.mode & ConcurrentMode)) { | |
| 27422 return; | |
| 27423 } | |
| 27424 | |
| 27425 var tag = fiber.tag; | |
| 27426 | |
| 27427 if (tag !== IndeterminateComponent && tag !== HostRoot && tag !== ClassComponent && tag !== FunctionComponent && tag !== ForwardRef && tag !== MemoComponent && tag !== SimpleMemoComponent) { | |
| 27428 // Only warn for user-defined components, not internal ones like Suspense. | |
| 27429 return; | |
| 27430 } // We show the whole stack but dedupe on the top component's name because | |
| 27431 // the problematic code almost always lies inside that component. | |
| 27432 | |
| 27433 | |
| 27434 var componentName = getComponentNameFromFiber(fiber) || 'ReactComponent'; | |
| 27435 | |
| 27436 if (didWarnStateUpdateForNotYetMountedComponent !== null) { | |
| 27437 if (didWarnStateUpdateForNotYetMountedComponent.has(componentName)) { | |
| 27438 return; | |
| 27439 } | |
| 27440 | |
| 27441 didWarnStateUpdateForNotYetMountedComponent.add(componentName); | |
| 27442 } else { | |
| 27443 didWarnStateUpdateForNotYetMountedComponent = new Set([componentName]); | |
| 27444 } | |
| 27445 | |
| 27446 var previousFiber = current; | |
| 27447 | |
| 27448 try { | |
| 27449 setCurrentFiber(fiber); | |
| 27450 | |
| 27451 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.'); | |
| 27452 } finally { | |
| 27453 if (previousFiber) { | |
| 27454 setCurrentFiber(fiber); | |
| 27455 } else { | |
| 27456 resetCurrentFiber(); | |
| 27457 } | |
| 27458 } | |
| 27459 } | |
| 27460 } | |
| 27461 var beginWork$1; | |
| 27462 | |
| 27463 { | |
| 27464 var dummyFiber = null; | |
| 27465 | |
| 27466 beginWork$1 = function (current, unitOfWork, lanes) { | |
| 27467 // If a component throws an error, we replay it again in a synchronously | |
| 27468 // dispatched event, so that the debugger will treat it as an uncaught | |
| 27469 // error See ReactErrorUtils for more information. | |
| 27470 // Before entering the begin phase, copy the work-in-progress onto a dummy | |
| 27471 // fiber. If beginWork throws, we'll use this to reset the state. | |
| 27472 var originalWorkInProgressCopy = assignFiberPropertiesInDEV(dummyFiber, unitOfWork); | |
| 27473 | |
| 27474 try { | |
| 27475 return beginWork(current, unitOfWork, lanes); | |
| 27476 } catch (originalError) { | |
| 27477 if (didSuspendOrErrorWhileHydratingDEV() || originalError !== null && typeof originalError === 'object' && typeof originalError.then === 'function') { | |
| 27478 // Don't replay promises. | |
| 27479 // Don't replay errors if we are hydrating and have already suspended or handled an error | |
| 27480 throw originalError; | |
| 27481 } // Keep this code in sync with handleError; any changes here must have | |
| 27482 // corresponding changes there. | |
| 27483 | |
| 27484 | |
| 27485 resetContextDependencies(); | |
| 27486 resetHooksAfterThrow(); // Don't reset current debug fiber, since we're about to work on the | |
| 27487 // same fiber again. | |
| 27488 // Unwind the failed stack frame | |
| 27489 | |
| 27490 unwindInterruptedWork(current, unitOfWork); // Restore the original properties of the fiber. | |
| 27491 | |
| 27492 assignFiberPropertiesInDEV(unitOfWork, originalWorkInProgressCopy); | |
| 27493 | |
| 27494 if ( unitOfWork.mode & ProfileMode) { | |
| 27495 // Reset the profiler timer. | |
| 27496 startProfilerTimer(unitOfWork); | |
| 27497 } // Run beginWork again. | |
| 27498 | |
| 27499 | |
| 27500 invokeGuardedCallback(null, beginWork, null, current, unitOfWork, lanes); | |
| 27501 | |
| 27502 if (hasCaughtError()) { | |
| 27503 var replayError = clearCaughtError(); | |
| 27504 | |
| 27505 if (typeof replayError === 'object' && replayError !== null && replayError._suppressLogging && typeof originalError === 'object' && originalError !== null && !originalError._suppressLogging) { | |
| 27506 // If suppressed, let the flag carry over to the original error which is the one we'll rethrow. | |
| 27507 originalError._suppressLogging = true; | |
| 27508 } | |
| 27509 } // We always throw the original error in case the second render pass is not idempotent. | |
| 27510 // This can happen if a memoized function or CommonJS module doesn't throw after first invocation. | |
| 27511 | |
| 27512 | |
| 27513 throw originalError; | |
| 27514 } | |
| 27515 }; | |
| 27516 } | |
| 27517 | |
| 27518 var didWarnAboutUpdateInRender = false; | |
| 27519 var didWarnAboutUpdateInRenderForAnotherComponent; | |
| 27520 | |
| 27521 { | |
| 27522 didWarnAboutUpdateInRenderForAnotherComponent = new Set(); | |
| 27523 } | |
| 27524 | |
| 27525 function warnAboutRenderPhaseUpdatesInDEV(fiber) { | |
| 27526 { | |
| 27527 if (isRendering && !getIsUpdatingOpaqueValueInRenderPhaseInDEV()) { | |
| 27528 switch (fiber.tag) { | |
| 27529 case FunctionComponent: | |
| 27530 case ForwardRef: | |
| 27531 case SimpleMemoComponent: | |
| 27532 { | |
| 27533 var renderingComponentName = workInProgress && getComponentNameFromFiber(workInProgress) || 'Unknown'; // Dedupe by the rendering component because it's the one that needs to be fixed. | |
| 27534 | |
| 27535 var dedupeKey = renderingComponentName; | |
| 27536 | |
| 27537 if (!didWarnAboutUpdateInRenderForAnotherComponent.has(dedupeKey)) { | |
| 27538 didWarnAboutUpdateInRenderForAnotherComponent.add(dedupeKey); | |
| 27539 var setStateComponentName = getComponentNameFromFiber(fiber) || 'Unknown'; | |
| 27540 | |
| 27541 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); | |
| 27542 } | |
| 27543 | |
| 27544 break; | |
| 27545 } | |
| 27546 | |
| 27547 case ClassComponent: | |
| 27548 { | |
| 27549 if (!didWarnAboutUpdateInRender) { | |
| 27550 error('Cannot update during an existing state transition (such as ' + 'within `render`). Render methods should be a pure ' + 'function of props and state.'); | |
| 27551 | |
| 27552 didWarnAboutUpdateInRender = true; | |
| 27553 } | |
| 27554 | |
| 27555 break; | |
| 27556 } | |
| 27557 } | |
| 27558 } | |
| 27559 } | |
| 27560 } | |
| 27561 | |
| 27562 function restorePendingUpdaters(root, lanes) { | |
| 27563 { | |
| 27564 if (isDevToolsPresent) { | |
| 27565 var memoizedUpdaters = root.memoizedUpdaters; | |
| 27566 memoizedUpdaters.forEach(function (schedulingFiber) { | |
| 27567 addFiberToLanesMap(root, schedulingFiber, lanes); | |
| 27568 }); // This function intentionally does not clear memoized updaters. | |
| 27569 // Those may still be relevant to the current commit | |
| 27570 // and a future one (e.g. Suspense). | |
| 27571 } | |
| 27572 } | |
| 27573 } | |
| 27574 var fakeActCallbackNode = {}; | |
| 27575 | |
| 27576 function scheduleCallback$1(priorityLevel, callback) { | |
| 27577 { | |
| 27578 // If we're currently inside an `act` scope, bypass Scheduler and push to | |
| 27579 // the `act` queue instead. | |
| 27580 var actQueue = ReactCurrentActQueue$1.current; | |
| 27581 | |
| 27582 if (actQueue !== null) { | |
| 27583 actQueue.push(callback); | |
| 27584 return fakeActCallbackNode; | |
| 27585 } else { | |
| 27586 return scheduleCallback(priorityLevel, callback); | |
| 27587 } | |
| 27588 } | |
| 27589 } | |
| 27590 | |
| 27591 function cancelCallback$1(callbackNode) { | |
| 27592 if ( callbackNode === fakeActCallbackNode) { | |
| 27593 return; | |
| 27594 } // In production, always call Scheduler. This function will be stripped out. | |
| 27595 | |
| 27596 | |
| 27597 return cancelCallback(callbackNode); | |
| 27598 } | |
| 27599 | |
| 27600 function shouldForceFlushFallbacksInDEV() { | |
| 27601 // Never force flush in production. This function should get stripped out. | |
| 27602 return ReactCurrentActQueue$1.current !== null; | |
| 27603 } | |
| 27604 | |
| 27605 function warnIfUpdatesNotWrappedWithActDEV(fiber) { | |
| 27606 { | |
| 27607 if (fiber.mode & ConcurrentMode) { | |
| 27608 if (!isConcurrentActEnvironment()) { | |
| 27609 // Not in an act environment. No need to warn. | |
| 27610 return; | |
| 27611 } | |
| 27612 } else { | |
| 27613 // Legacy mode has additional cases where we suppress a warning. | |
| 27614 if (!isLegacyActEnvironment()) { | |
| 27615 // Not in an act environment. No need to warn. | |
| 27616 return; | |
| 27617 } | |
| 27618 | |
| 27619 if (executionContext !== NoContext) { | |
| 27620 // Legacy mode doesn't warn if the update is batched, i.e. | |
| 27621 // batchedUpdates or flushSync. | |
| 27622 return; | |
| 27623 } | |
| 27624 | |
| 27625 if (fiber.tag !== FunctionComponent && fiber.tag !== ForwardRef && fiber.tag !== SimpleMemoComponent) { | |
| 27626 // For backwards compatibility with pre-hooks code, legacy mode only | |
| 27627 // warns for updates that originate from a hook. | |
| 27628 return; | |
| 27629 } | |
| 27630 } | |
| 27631 | |
| 27632 if (ReactCurrentActQueue$1.current === null) { | |
| 27633 var previousFiber = current; | |
| 27634 | |
| 27635 try { | |
| 27636 setCurrentFiber(fiber); | |
| 27637 | |
| 27638 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)); | |
| 27639 } finally { | |
| 27640 if (previousFiber) { | |
| 27641 setCurrentFiber(fiber); | |
| 27642 } else { | |
| 27643 resetCurrentFiber(); | |
| 27644 } | |
| 27645 } | |
| 27646 } | |
| 27647 } | |
| 27648 } | |
| 27649 | |
| 27650 function warnIfSuspenseResolutionNotWrappedWithActDEV(root) { | |
| 27651 { | |
| 27652 if (root.tag !== LegacyRoot && isConcurrentActEnvironment() && ReactCurrentActQueue$1.current === null) { | |
| 27653 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'); | |
| 27654 } | |
| 27655 } | |
| 27656 } | |
| 27657 | |
| 27658 function setIsRunningInsertionEffect(isRunning) { | |
| 27659 { | |
| 27660 isRunningInsertionEffect = isRunning; | |
| 27661 } | |
| 27662 } | |
| 27663 | |
| 27664 /* eslint-disable react-internal/prod-error-codes */ | |
| 27665 var resolveFamily = null; // $FlowFixMe Flow gets confused by a WeakSet feature check below. | |
| 27666 | |
| 27667 var failedBoundaries = null; | |
| 27668 var setRefreshHandler = function (handler) { | |
| 27669 { | |
| 27670 resolveFamily = handler; | |
| 27671 } | |
| 27672 }; | |
| 27673 function resolveFunctionForHotReloading(type) { | |
| 27674 { | |
| 27675 if (resolveFamily === null) { | |
| 27676 // Hot reloading is disabled. | |
| 27677 return type; | |
| 27678 } | |
| 27679 | |
| 27680 var family = resolveFamily(type); | |
| 27681 | |
| 27682 if (family === undefined) { | |
| 27683 return type; | |
| 27684 } // Use the latest known implementation. | |
| 27685 | |
| 27686 | |
| 27687 return family.current; | |
| 27688 } | |
| 27689 } | |
| 27690 function resolveClassForHotReloading(type) { | |
| 27691 // No implementation differences. | |
| 27692 return resolveFunctionForHotReloading(type); | |
| 27693 } | |
| 27694 function resolveForwardRefForHotReloading(type) { | |
| 27695 { | |
| 27696 if (resolveFamily === null) { | |
| 27697 // Hot reloading is disabled. | |
| 27698 return type; | |
| 27699 } | |
| 27700 | |
| 27701 var family = resolveFamily(type); | |
| 27702 | |
| 27703 if (family === undefined) { | |
| 27704 // Check if we're dealing with a real forwardRef. Don't want to crash early. | |
| 27705 if (type !== null && type !== undefined && typeof type.render === 'function') { | |
| 27706 // ForwardRef is special because its resolved .type is an object, | |
| 27707 // but it's possible that we only have its inner render function in the map. | |
| 27708 // If that inner render function is different, we'll build a new forwardRef type. | |
| 27709 var currentRender = resolveFunctionForHotReloading(type.render); | |
| 27710 | |
| 27711 if (type.render !== currentRender) { | |
| 27712 var syntheticType = { | |
| 27713 $$typeof: REACT_FORWARD_REF_TYPE, | |
| 27714 render: currentRender | |
| 27715 }; | |
| 27716 | |
| 27717 if (type.displayName !== undefined) { | |
| 27718 syntheticType.displayName = type.displayName; | |
| 27719 } | |
| 27720 | |
| 27721 return syntheticType; | |
| 27722 } | |
| 27723 } | |
| 27724 | |
| 27725 return type; | |
| 27726 } // Use the latest known implementation. | |
| 27727 | |
| 27728 | |
| 27729 return family.current; | |
| 27730 } | |
| 27731 } | |
| 27732 function isCompatibleFamilyForHotReloading(fiber, element) { | |
| 27733 { | |
| 27734 if (resolveFamily === null) { | |
| 27735 // Hot reloading is disabled. | |
| 27736 return false; | |
| 27737 } | |
| 27738 | |
| 27739 var prevType = fiber.elementType; | |
| 27740 var nextType = element.type; // If we got here, we know types aren't === equal. | |
| 27741 | |
| 27742 var needsCompareFamilies = false; | |
| 27743 var $$typeofNextType = typeof nextType === 'object' && nextType !== null ? nextType.$$typeof : null; | |
| 27744 | |
| 27745 switch (fiber.tag) { | |
| 27746 case ClassComponent: | |
| 27747 { | |
| 27748 if (typeof nextType === 'function') { | |
| 27749 needsCompareFamilies = true; | |
| 27750 } | |
| 27751 | |
| 27752 break; | |
| 27753 } | |
| 27754 | |
| 27755 case FunctionComponent: | |
| 27756 { | |
| 27757 if (typeof nextType === 'function') { | |
| 27758 needsCompareFamilies = true; | |
| 27759 } else if ($$typeofNextType === REACT_LAZY_TYPE) { | |
| 27760 // We don't know the inner type yet. | |
| 27761 // We're going to assume that the lazy inner type is stable, | |
| 27762 // and so it is sufficient to avoid reconciling it away. | |
| 27763 // We're not going to unwrap or actually use the new lazy type. | |
| 27764 needsCompareFamilies = true; | |
| 27765 } | |
| 27766 | |
| 27767 break; | |
| 27768 } | |
| 27769 | |
| 27770 case ForwardRef: | |
| 27771 { | |
| 27772 if ($$typeofNextType === REACT_FORWARD_REF_TYPE) { | |
| 27773 needsCompareFamilies = true; | |
| 27774 } else if ($$typeofNextType === REACT_LAZY_TYPE) { | |
| 27775 needsCompareFamilies = true; | |
| 27776 } | |
| 27777 | |
| 27778 break; | |
| 27779 } | |
| 27780 | |
| 27781 case MemoComponent: | |
| 27782 case SimpleMemoComponent: | |
| 27783 { | |
| 27784 if ($$typeofNextType === REACT_MEMO_TYPE) { | |
| 27785 // TODO: if it was but can no longer be simple, | |
| 27786 // we shouldn't set this. | |
| 27787 needsCompareFamilies = true; | |
| 27788 } else if ($$typeofNextType === REACT_LAZY_TYPE) { | |
| 27789 needsCompareFamilies = true; | |
| 27790 } | |
| 27791 | |
| 27792 break; | |
| 27793 } | |
| 27794 | |
| 27795 default: | |
| 27796 return false; | |
| 27797 } // Check if both types have a family and it's the same one. | |
| 27798 | |
| 27799 | |
| 27800 if (needsCompareFamilies) { | |
| 27801 // Note: memo() and forwardRef() we'll compare outer rather than inner type. | |
| 27802 // This means both of them need to be registered to preserve state. | |
| 27803 // If we unwrapped and compared the inner types for wrappers instead, | |
| 27804 // then we would risk falsely saying two separate memo(Foo) | |
| 27805 // calls are equivalent because they wrap the same Foo function. | |
| 27806 var prevFamily = resolveFamily(prevType); | |
| 27807 | |
| 27808 if (prevFamily !== undefined && prevFamily === resolveFamily(nextType)) { | |
| 27809 return true; | |
| 27810 } | |
| 27811 } | |
| 27812 | |
| 27813 return false; | |
| 27814 } | |
| 27815 } | |
| 27816 function markFailedErrorBoundaryForHotReloading(fiber) { | |
| 27817 { | |
| 27818 if (resolveFamily === null) { | |
| 27819 // Hot reloading is disabled. | |
| 27820 return; | |
| 27821 } | |
| 27822 | |
| 27823 if (typeof WeakSet !== 'function') { | |
| 27824 return; | |
| 27825 } | |
| 27826 | |
| 27827 if (failedBoundaries === null) { | |
| 27828 failedBoundaries = new WeakSet(); | |
| 27829 } | |
| 27830 | |
| 27831 failedBoundaries.add(fiber); | |
| 27832 } | |
| 27833 } | |
| 27834 var scheduleRefresh = function (root, update) { | |
| 27835 { | |
| 27836 if (resolveFamily === null) { | |
| 27837 // Hot reloading is disabled. | |
| 27838 return; | |
| 27839 } | |
| 27840 | |
| 27841 var staleFamilies = update.staleFamilies, | |
| 27842 updatedFamilies = update.updatedFamilies; | |
| 27843 flushPassiveEffects(); | |
| 27844 flushSync(function () { | |
| 27845 scheduleFibersWithFamiliesRecursively(root.current, updatedFamilies, staleFamilies); | |
| 27846 }); | |
| 27847 } | |
| 27848 }; | |
| 27849 var scheduleRoot = function (root, element) { | |
| 27850 { | |
| 27851 if (root.context !== emptyContextObject) { | |
| 27852 // Super edge case: root has a legacy _renderSubtree context | |
| 27853 // but we don't know the parentComponent so we can't pass it. | |
| 27854 // Just ignore. We'll delete this with _renderSubtree code path later. | |
| 27855 return; | |
| 27856 } | |
| 27857 | |
| 27858 flushPassiveEffects(); | |
| 27859 flushSync(function () { | |
| 27860 updateContainer(element, root, null, null); | |
| 27861 }); | |
| 27862 } | |
| 27863 }; | |
| 27864 | |
| 27865 function scheduleFibersWithFamiliesRecursively(fiber, updatedFamilies, staleFamilies) { | |
| 27866 { | |
| 27867 var alternate = fiber.alternate, | |
| 27868 child = fiber.child, | |
| 27869 sibling = fiber.sibling, | |
| 27870 tag = fiber.tag, | |
| 27871 type = fiber.type; | |
| 27872 var candidateType = null; | |
| 27873 | |
| 27874 switch (tag) { | |
| 27875 case FunctionComponent: | |
| 27876 case SimpleMemoComponent: | |
| 27877 case ClassComponent: | |
| 27878 candidateType = type; | |
| 27879 break; | |
| 27880 | |
| 27881 case ForwardRef: | |
| 27882 candidateType = type.render; | |
| 27883 break; | |
| 27884 } | |
| 27885 | |
| 27886 if (resolveFamily === null) { | |
| 27887 throw new Error('Expected resolveFamily to be set during hot reload.'); | |
| 27888 } | |
| 27889 | |
| 27890 var needsRender = false; | |
| 27891 var needsRemount = false; | |
| 27892 | |
| 27893 if (candidateType !== null) { | |
| 27894 var family = resolveFamily(candidateType); | |
| 27895 | |
| 27896 if (family !== undefined) { | |
| 27897 if (staleFamilies.has(family)) { | |
| 27898 needsRemount = true; | |
| 27899 } else if (updatedFamilies.has(family)) { | |
| 27900 if (tag === ClassComponent) { | |
| 27901 needsRemount = true; | |
| 27902 } else { | |
| 27903 needsRender = true; | |
| 27904 } | |
| 27905 } | |
| 27906 } | |
| 27907 } | |
| 27908 | |
| 27909 if (failedBoundaries !== null) { | |
| 27910 if (failedBoundaries.has(fiber) || alternate !== null && failedBoundaries.has(alternate)) { | |
| 27911 needsRemount = true; | |
| 27912 } | |
| 27913 } | |
| 27914 | |
| 27915 if (needsRemount) { | |
| 27916 fiber._debugNeedsRemount = true; | |
| 27917 } | |
| 27918 | |
| 27919 if (needsRemount || needsRender) { | |
| 27920 var _root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 27921 | |
| 27922 if (_root !== null) { | |
| 27923 scheduleUpdateOnFiber(_root, fiber, SyncLane, NoTimestamp); | |
| 27924 } | |
| 27925 } | |
| 27926 | |
| 27927 if (child !== null && !needsRemount) { | |
| 27928 scheduleFibersWithFamiliesRecursively(child, updatedFamilies, staleFamilies); | |
| 27929 } | |
| 27930 | |
| 27931 if (sibling !== null) { | |
| 27932 scheduleFibersWithFamiliesRecursively(sibling, updatedFamilies, staleFamilies); | |
| 27933 } | |
| 27934 } | |
| 27935 } | |
| 27936 | |
| 27937 var findHostInstancesForRefresh = function (root, families) { | |
| 27938 { | |
| 27939 var hostInstances = new Set(); | |
| 27940 var types = new Set(families.map(function (family) { | |
| 27941 return family.current; | |
| 27942 })); | |
| 27943 findHostInstancesForMatchingFibersRecursively(root.current, types, hostInstances); | |
| 27944 return hostInstances; | |
| 27945 } | |
| 27946 }; | |
| 27947 | |
| 27948 function findHostInstancesForMatchingFibersRecursively(fiber, types, hostInstances) { | |
| 27949 { | |
| 27950 var child = fiber.child, | |
| 27951 sibling = fiber.sibling, | |
| 27952 tag = fiber.tag, | |
| 27953 type = fiber.type; | |
| 27954 var candidateType = null; | |
| 27955 | |
| 27956 switch (tag) { | |
| 27957 case FunctionComponent: | |
| 27958 case SimpleMemoComponent: | |
| 27959 case ClassComponent: | |
| 27960 candidateType = type; | |
| 27961 break; | |
| 27962 | |
| 27963 case ForwardRef: | |
| 27964 candidateType = type.render; | |
| 27965 break; | |
| 27966 } | |
| 27967 | |
| 27968 var didMatch = false; | |
| 27969 | |
| 27970 if (candidateType !== null) { | |
| 27971 if (types.has(candidateType)) { | |
| 27972 didMatch = true; | |
| 27973 } | |
| 27974 } | |
| 27975 | |
| 27976 if (didMatch) { | |
| 27977 // We have a match. This only drills down to the closest host components. | |
| 27978 // There's no need to search deeper because for the purpose of giving | |
| 27979 // visual feedback, "flashing" outermost parent rectangles is sufficient. | |
| 27980 findHostInstancesForFiberShallowly(fiber, hostInstances); | |
| 27981 } else { | |
| 27982 // If there's no match, maybe there will be one further down in the child tree. | |
| 27983 if (child !== null) { | |
| 27984 findHostInstancesForMatchingFibersRecursively(child, types, hostInstances); | |
| 27985 } | |
| 27986 } | |
| 27987 | |
| 27988 if (sibling !== null) { | |
| 27989 findHostInstancesForMatchingFibersRecursively(sibling, types, hostInstances); | |
| 27990 } | |
| 27991 } | |
| 27992 } | |
| 27993 | |
| 27994 function findHostInstancesForFiberShallowly(fiber, hostInstances) { | |
| 27995 { | |
| 27996 var foundHostInstances = findChildHostInstancesForFiberShallowly(fiber, hostInstances); | |
| 27997 | |
| 27998 if (foundHostInstances) { | |
| 27999 return; | |
| 28000 } // If we didn't find any host children, fallback to closest host parent. | |
| 28001 | |
| 28002 | |
| 28003 var node = fiber; | |
| 28004 | |
| 28005 while (true) { | |
| 28006 switch (node.tag) { | |
| 28007 case HostComponent: | |
| 28008 hostInstances.add(node.stateNode); | |
| 28009 return; | |
| 28010 | |
| 28011 case HostPortal: | |
| 28012 hostInstances.add(node.stateNode.containerInfo); | |
| 28013 return; | |
| 28014 | |
| 28015 case HostRoot: | |
| 28016 hostInstances.add(node.stateNode.containerInfo); | |
| 28017 return; | |
| 28018 } | |
| 28019 | |
| 28020 if (node.return === null) { | |
| 28021 throw new Error('Expected to reach root first.'); | |
| 28022 } | |
| 28023 | |
| 28024 node = node.return; | |
| 28025 } | |
| 28026 } | |
| 28027 } | |
| 28028 | |
| 28029 function findChildHostInstancesForFiberShallowly(fiber, hostInstances) { | |
| 28030 { | |
| 28031 var node = fiber; | |
| 28032 var foundHostInstances = false; | |
| 28033 | |
| 28034 while (true) { | |
| 28035 if (node.tag === HostComponent) { | |
| 28036 // We got a match. | |
| 28037 foundHostInstances = true; | |
| 28038 hostInstances.add(node.stateNode); // There may still be more, so keep searching. | |
| 28039 } else if (node.child !== null) { | |
| 28040 node.child.return = node; | |
| 28041 node = node.child; | |
| 28042 continue; | |
| 28043 } | |
| 28044 | |
| 28045 if (node === fiber) { | |
| 28046 return foundHostInstances; | |
| 28047 } | |
| 28048 | |
| 28049 while (node.sibling === null) { | |
| 28050 if (node.return === null || node.return === fiber) { | |
| 28051 return foundHostInstances; | |
| 28052 } | |
| 28053 | |
| 28054 node = node.return; | |
| 28055 } | |
| 28056 | |
| 28057 node.sibling.return = node.return; | |
| 28058 node = node.sibling; | |
| 28059 } | |
| 28060 } | |
| 28061 | |
| 28062 return false; | |
| 28063 } | |
| 28064 | |
| 28065 var hasBadMapPolyfill; | |
| 28066 | |
| 28067 { | |
| 28068 hasBadMapPolyfill = false; | |
| 28069 | |
| 28070 try { | |
| 28071 var nonExtensibleObject = Object.preventExtensions({}); | |
| 28072 /* eslint-disable no-new */ | |
| 28073 | |
| 28074 new Map([[nonExtensibleObject, null]]); | |
| 28075 new Set([nonExtensibleObject]); | |
| 28076 /* eslint-enable no-new */ | |
| 28077 } catch (e) { | |
| 28078 // TODO: Consider warning about bad polyfills | |
| 28079 hasBadMapPolyfill = true; | |
| 28080 } | |
| 28081 } | |
| 28082 | |
| 28083 function FiberNode(tag, pendingProps, key, mode) { | |
| 28084 // Instance | |
| 28085 this.tag = tag; | |
| 28086 this.key = key; | |
| 28087 this.elementType = null; | |
| 28088 this.type = null; | |
| 28089 this.stateNode = null; // Fiber | |
| 28090 | |
| 28091 this.return = null; | |
| 28092 this.child = null; | |
| 28093 this.sibling = null; | |
| 28094 this.index = 0; | |
| 28095 this.ref = null; | |
| 28096 this.pendingProps = pendingProps; | |
| 28097 this.memoizedProps = null; | |
| 28098 this.updateQueue = null; | |
| 28099 this.memoizedState = null; | |
| 28100 this.dependencies = null; | |
| 28101 this.mode = mode; // Effects | |
| 28102 | |
| 28103 this.flags = NoFlags; | |
| 28104 this.subtreeFlags = NoFlags; | |
| 28105 this.deletions = null; | |
| 28106 this.lanes = NoLanes; | |
| 28107 this.childLanes = NoLanes; | |
| 28108 this.alternate = null; | |
| 28109 | |
| 28110 { | |
| 28111 // Note: The following is done to avoid a v8 performance cliff. | |
| 28112 // | |
| 28113 // Initializing the fields below to smis and later updating them with | |
| 28114 // double values will cause Fibers to end up having separate shapes. | |
| 28115 // This behavior/bug has something to do with Object.preventExtension(). | |
| 28116 // Fortunately this only impacts DEV builds. | |
| 28117 // Unfortunately it makes React unusably slow for some applications. | |
| 28118 // To work around this, initialize the fields below with doubles. | |
| 28119 // | |
| 28120 // Learn more about this here: | |
| 28121 // https://github.com/facebook/react/issues/14365 | |
| 28122 // https://bugs.chromium.org/p/v8/issues/detail?id=8538 | |
| 28123 this.actualDuration = Number.NaN; | |
| 28124 this.actualStartTime = Number.NaN; | |
| 28125 this.selfBaseDuration = Number.NaN; | |
| 28126 this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization. | |
| 28127 // This won't trigger the performance cliff mentioned above, | |
| 28128 // and it simplifies other profiler code (including DevTools). | |
| 28129 | |
| 28130 this.actualDuration = 0; | |
| 28131 this.actualStartTime = -1; | |
| 28132 this.selfBaseDuration = 0; | |
| 28133 this.treeBaseDuration = 0; | |
| 28134 } | |
| 28135 | |
| 28136 { | |
| 28137 // This isn't directly used but is handy for debugging internals: | |
| 28138 this._debugSource = null; | |
| 28139 this._debugOwner = null; | |
| 28140 this._debugNeedsRemount = false; | |
| 28141 this._debugHookTypes = null; | |
| 28142 | |
| 28143 if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') { | |
| 28144 Object.preventExtensions(this); | |
| 28145 } | |
| 28146 } | |
| 28147 } // This is a constructor function, rather than a POJO constructor, still | |
| 28148 // please ensure we do the following: | |
| 28149 // 1) Nobody should add any instance methods on this. Instance methods can be | |
| 28150 // more difficult to predict when they get optimized and they are almost | |
| 28151 // never inlined properly in static compilers. | |
| 28152 // 2) Nobody should rely on `instanceof Fiber` for type testing. We should | |
| 28153 // always know when it is a fiber. | |
| 28154 // 3) We might want to experiment with using numeric keys since they are easier | |
| 28155 // to optimize in a non-JIT environment. | |
| 28156 // 4) We can easily go from a constructor to a createFiber object literal if that | |
| 28157 // is faster. | |
| 28158 // 5) It should be easy to port this to a C struct and keep a C implementation | |
| 28159 // compatible. | |
| 28160 | |
| 28161 | |
| 28162 var createFiber = function (tag, pendingProps, key, mode) { | |
| 28163 // $FlowFixMe: the shapes are exact here but Flow doesn't like constructors | |
| 28164 return new FiberNode(tag, pendingProps, key, mode); | |
| 28165 }; | |
| 28166 | |
| 28167 function shouldConstruct$1(Component) { | |
| 28168 var prototype = Component.prototype; | |
| 28169 return !!(prototype && prototype.isReactComponent); | |
| 28170 } | |
| 28171 | |
| 28172 function isSimpleFunctionComponent(type) { | |
| 28173 return typeof type === 'function' && !shouldConstruct$1(type) && type.defaultProps === undefined; | |
| 28174 } | |
| 28175 function resolveLazyComponentTag(Component) { | |
| 28176 if (typeof Component === 'function') { | |
| 28177 return shouldConstruct$1(Component) ? ClassComponent : FunctionComponent; | |
| 28178 } else if (Component !== undefined && Component !== null) { | |
| 28179 var $$typeof = Component.$$typeof; | |
| 28180 | |
| 28181 if ($$typeof === REACT_FORWARD_REF_TYPE) { | |
| 28182 return ForwardRef; | |
| 28183 } | |
| 28184 | |
| 28185 if ($$typeof === REACT_MEMO_TYPE) { | |
| 28186 return MemoComponent; | |
| 28187 } | |
| 28188 } | |
| 28189 | |
| 28190 return IndeterminateComponent; | |
| 28191 } // This is used to create an alternate fiber to do work on. | |
| 28192 | |
| 28193 function createWorkInProgress(current, pendingProps) { | |
| 28194 var workInProgress = current.alternate; | |
| 28195 | |
| 28196 if (workInProgress === null) { | |
| 28197 // We use a double buffering pooling technique because we know that we'll | |
| 28198 // only ever need at most two versions of a tree. We pool the "other" unused | |
| 28199 // node that we're free to reuse. This is lazily created to avoid allocating | |
| 28200 // extra objects for things that are never updated. It also allow us to | |
| 28201 // reclaim the extra memory if needed. | |
| 28202 workInProgress = createFiber(current.tag, pendingProps, current.key, current.mode); | |
| 28203 workInProgress.elementType = current.elementType; | |
| 28204 workInProgress.type = current.type; | |
| 28205 workInProgress.stateNode = current.stateNode; | |
| 28206 | |
| 28207 { | |
| 28208 // DEV-only fields | |
| 28209 workInProgress._debugSource = current._debugSource; | |
| 28210 workInProgress._debugOwner = current._debugOwner; | |
| 28211 workInProgress._debugHookTypes = current._debugHookTypes; | |
| 28212 } | |
| 28213 | |
| 28214 workInProgress.alternate = current; | |
| 28215 current.alternate = workInProgress; | |
| 28216 } else { | |
| 28217 workInProgress.pendingProps = pendingProps; // Needed because Blocks store data on type. | |
| 28218 | |
| 28219 workInProgress.type = current.type; // We already have an alternate. | |
| 28220 // Reset the effect tag. | |
| 28221 | |
| 28222 workInProgress.flags = NoFlags; // The effects are no longer valid. | |
| 28223 | |
| 28224 workInProgress.subtreeFlags = NoFlags; | |
| 28225 workInProgress.deletions = null; | |
| 28226 | |
| 28227 { | |
| 28228 // We intentionally reset, rather than copy, actualDuration & actualStartTime. | |
| 28229 // This prevents time from endlessly accumulating in new commits. | |
| 28230 // This has the downside of resetting values for different priority renders, | |
| 28231 // But works for yielding (the common case) and should support resuming. | |
| 28232 workInProgress.actualDuration = 0; | |
| 28233 workInProgress.actualStartTime = -1; | |
| 28234 } | |
| 28235 } // Reset all effects except static ones. | |
| 28236 // Static effects are not specific to a render. | |
| 28237 | |
| 28238 | |
| 28239 workInProgress.flags = current.flags & StaticMask; | |
| 28240 workInProgress.childLanes = current.childLanes; | |
| 28241 workInProgress.lanes = current.lanes; | |
| 28242 workInProgress.child = current.child; | |
| 28243 workInProgress.memoizedProps = current.memoizedProps; | |
| 28244 workInProgress.memoizedState = current.memoizedState; | |
| 28245 workInProgress.updateQueue = current.updateQueue; // Clone the dependencies object. This is mutated during the render phase, so | |
| 28246 // it cannot be shared with the current fiber. | |
| 28247 | |
| 28248 var currentDependencies = current.dependencies; | |
| 28249 workInProgress.dependencies = currentDependencies === null ? null : { | |
| 28250 lanes: currentDependencies.lanes, | |
| 28251 firstContext: currentDependencies.firstContext | |
| 28252 }; // These will be overridden during the parent's reconciliation | |
| 28253 | |
| 28254 workInProgress.sibling = current.sibling; | |
| 28255 workInProgress.index = current.index; | |
| 28256 workInProgress.ref = current.ref; | |
| 28257 | |
| 28258 { | |
| 28259 workInProgress.selfBaseDuration = current.selfBaseDuration; | |
| 28260 workInProgress.treeBaseDuration = current.treeBaseDuration; | |
| 28261 } | |
| 28262 | |
| 28263 { | |
| 28264 workInProgress._debugNeedsRemount = current._debugNeedsRemount; | |
| 28265 | |
| 28266 switch (workInProgress.tag) { | |
| 28267 case IndeterminateComponent: | |
| 28268 case FunctionComponent: | |
| 28269 case SimpleMemoComponent: | |
| 28270 workInProgress.type = resolveFunctionForHotReloading(current.type); | |
| 28271 break; | |
| 28272 | |
| 28273 case ClassComponent: | |
| 28274 workInProgress.type = resolveClassForHotReloading(current.type); | |
| 28275 break; | |
| 28276 | |
| 28277 case ForwardRef: | |
| 28278 workInProgress.type = resolveForwardRefForHotReloading(current.type); | |
| 28279 break; | |
| 28280 } | |
| 28281 } | |
| 28282 | |
| 28283 return workInProgress; | |
| 28284 } // Used to reuse a Fiber for a second pass. | |
| 28285 | |
| 28286 function resetWorkInProgress(workInProgress, renderLanes) { | |
| 28287 // This resets the Fiber to what createFiber or createWorkInProgress would | |
| 28288 // have set the values to before during the first pass. Ideally this wouldn't | |
| 28289 // be necessary but unfortunately many code paths reads from the workInProgress | |
| 28290 // when they should be reading from current and writing to workInProgress. | |
| 28291 // We assume pendingProps, index, key, ref, return are still untouched to | |
| 28292 // avoid doing another reconciliation. | |
| 28293 // Reset the effect flags but keep any Placement tags, since that's something | |
| 28294 // that child fiber is setting, not the reconciliation. | |
| 28295 workInProgress.flags &= StaticMask | Placement; // The effects are no longer valid. | |
| 28296 | |
| 28297 var current = workInProgress.alternate; | |
| 28298 | |
| 28299 if (current === null) { | |
| 28300 // Reset to createFiber's initial values. | |
| 28301 workInProgress.childLanes = NoLanes; | |
| 28302 workInProgress.lanes = renderLanes; | |
| 28303 workInProgress.child = null; | |
| 28304 workInProgress.subtreeFlags = NoFlags; | |
| 28305 workInProgress.memoizedProps = null; | |
| 28306 workInProgress.memoizedState = null; | |
| 28307 workInProgress.updateQueue = null; | |
| 28308 workInProgress.dependencies = null; | |
| 28309 workInProgress.stateNode = null; | |
| 28310 | |
| 28311 { | |
| 28312 // Note: We don't reset the actualTime counts. It's useful to accumulate | |
| 28313 // actual time across multiple render passes. | |
| 28314 workInProgress.selfBaseDuration = 0; | |
| 28315 workInProgress.treeBaseDuration = 0; | |
| 28316 } | |
| 28317 } else { | |
| 28318 // Reset to the cloned values that createWorkInProgress would've. | |
| 28319 workInProgress.childLanes = current.childLanes; | |
| 28320 workInProgress.lanes = current.lanes; | |
| 28321 workInProgress.child = current.child; | |
| 28322 workInProgress.subtreeFlags = NoFlags; | |
| 28323 workInProgress.deletions = null; | |
| 28324 workInProgress.memoizedProps = current.memoizedProps; | |
| 28325 workInProgress.memoizedState = current.memoizedState; | |
| 28326 workInProgress.updateQueue = current.updateQueue; // Needed because Blocks store data on type. | |
| 28327 | |
| 28328 workInProgress.type = current.type; // Clone the dependencies object. This is mutated during the render phase, so | |
| 28329 // it cannot be shared with the current fiber. | |
| 28330 | |
| 28331 var currentDependencies = current.dependencies; | |
| 28332 workInProgress.dependencies = currentDependencies === null ? null : { | |
| 28333 lanes: currentDependencies.lanes, | |
| 28334 firstContext: currentDependencies.firstContext | |
| 28335 }; | |
| 28336 | |
| 28337 { | |
| 28338 // Note: We don't reset the actualTime counts. It's useful to accumulate | |
| 28339 // actual time across multiple render passes. | |
| 28340 workInProgress.selfBaseDuration = current.selfBaseDuration; | |
| 28341 workInProgress.treeBaseDuration = current.treeBaseDuration; | |
| 28342 } | |
| 28343 } | |
| 28344 | |
| 28345 return workInProgress; | |
| 28346 } | |
| 28347 function createHostRootFiber(tag, isStrictMode, concurrentUpdatesByDefaultOverride) { | |
| 28348 var mode; | |
| 28349 | |
| 28350 if (tag === ConcurrentRoot) { | |
| 28351 mode = ConcurrentMode; | |
| 28352 | |
| 28353 if (isStrictMode === true) { | |
| 28354 mode |= StrictLegacyMode; | |
| 28355 | |
| 28356 { | |
| 28357 mode |= StrictEffectsMode; | |
| 28358 } | |
| 28359 } | |
| 28360 } else { | |
| 28361 mode = NoMode; | |
| 28362 } | |
| 28363 | |
| 28364 if ( isDevToolsPresent) { | |
| 28365 // Always collect profile timings when DevTools are present. | |
| 28366 // This enables DevTools to start capturing timing at any point– | |
| 28367 // Without some nodes in the tree having empty base times. | |
| 28368 mode |= ProfileMode; | |
| 28369 } | |
| 28370 | |
| 28371 return createFiber(HostRoot, null, null, mode); | |
| 28372 } | |
| 28373 function createFiberFromTypeAndProps(type, // React$ElementType | |
| 28374 key, pendingProps, owner, mode, lanes) { | |
| 28375 var fiberTag = IndeterminateComponent; // The resolved type is set if we know what the final type will be. I.e. it's not lazy. | |
| 28376 | |
| 28377 var resolvedType = type; | |
| 28378 | |
| 28379 if (typeof type === 'function') { | |
| 28380 if (shouldConstruct$1(type)) { | |
| 28381 fiberTag = ClassComponent; | |
| 28382 | |
| 28383 { | |
| 28384 resolvedType = resolveClassForHotReloading(resolvedType); | |
| 28385 } | |
| 28386 } else { | |
| 28387 { | |
| 28388 resolvedType = resolveFunctionForHotReloading(resolvedType); | |
| 28389 } | |
| 28390 } | |
| 28391 } else if (typeof type === 'string') { | |
| 28392 fiberTag = HostComponent; | |
| 28393 } else { | |
| 28394 getTag: switch (type) { | |
| 28395 case REACT_FRAGMENT_TYPE: | |
| 28396 return createFiberFromFragment(pendingProps.children, mode, lanes, key); | |
| 28397 | |
| 28398 case REACT_STRICT_MODE_TYPE: | |
| 28399 fiberTag = Mode; | |
| 28400 mode |= StrictLegacyMode; | |
| 28401 | |
| 28402 if ( (mode & ConcurrentMode) !== NoMode) { | |
| 28403 // Strict effects should never run on legacy roots | |
| 28404 mode |= StrictEffectsMode; | |
| 28405 } | |
| 28406 | |
| 28407 break; | |
| 28408 | |
| 28409 case REACT_PROFILER_TYPE: | |
| 28410 return createFiberFromProfiler(pendingProps, mode, lanes, key); | |
| 28411 | |
| 28412 case REACT_SUSPENSE_TYPE: | |
| 28413 return createFiberFromSuspense(pendingProps, mode, lanes, key); | |
| 28414 | |
| 28415 case REACT_SUSPENSE_LIST_TYPE: | |
| 28416 return createFiberFromSuspenseList(pendingProps, mode, lanes, key); | |
| 28417 | |
| 28418 case REACT_OFFSCREEN_TYPE: | |
| 28419 return createFiberFromOffscreen(pendingProps, mode, lanes, key); | |
| 28420 | |
| 28421 case REACT_LEGACY_HIDDEN_TYPE: | |
| 28422 | |
| 28423 // eslint-disable-next-line no-fallthrough | |
| 28424 | |
| 28425 case REACT_SCOPE_TYPE: | |
| 28426 | |
| 28427 // eslint-disable-next-line no-fallthrough | |
| 28428 | |
| 28429 case REACT_CACHE_TYPE: | |
| 28430 | |
| 28431 // eslint-disable-next-line no-fallthrough | |
| 28432 | |
| 28433 case REACT_TRACING_MARKER_TYPE: | |
| 28434 | |
| 28435 // eslint-disable-next-line no-fallthrough | |
| 28436 | |
| 28437 case REACT_DEBUG_TRACING_MODE_TYPE: | |
| 28438 | |
| 28439 // eslint-disable-next-line no-fallthrough | |
| 28440 | |
| 28441 default: | |
| 28442 { | |
| 28443 if (typeof type === 'object' && type !== null) { | |
| 28444 switch (type.$$typeof) { | |
| 28445 case REACT_PROVIDER_TYPE: | |
| 28446 fiberTag = ContextProvider; | |
| 28447 break getTag; | |
| 28448 | |
| 28449 case REACT_CONTEXT_TYPE: | |
| 28450 // This is a consumer | |
| 28451 fiberTag = ContextConsumer; | |
| 28452 break getTag; | |
| 28453 | |
| 28454 case REACT_FORWARD_REF_TYPE: | |
| 28455 fiberTag = ForwardRef; | |
| 28456 | |
| 28457 { | |
| 28458 resolvedType = resolveForwardRefForHotReloading(resolvedType); | |
| 28459 } | |
| 28460 | |
| 28461 break getTag; | |
| 28462 | |
| 28463 case REACT_MEMO_TYPE: | |
| 28464 fiberTag = MemoComponent; | |
| 28465 break getTag; | |
| 28466 | |
| 28467 case REACT_LAZY_TYPE: | |
| 28468 fiberTag = LazyComponent; | |
| 28469 resolvedType = null; | |
| 28470 break getTag; | |
| 28471 } | |
| 28472 } | |
| 28473 | |
| 28474 var info = ''; | |
| 28475 | |
| 28476 { | |
| 28477 if (type === undefined || typeof type === 'object' && type !== null && Object.keys(type).length === 0) { | |
| 28478 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.'; | |
| 28479 } | |
| 28480 | |
| 28481 var ownerName = owner ? getComponentNameFromFiber(owner) : null; | |
| 28482 | |
| 28483 if (ownerName) { | |
| 28484 info += '\n\nCheck the render method of `' + ownerName + '`.'; | |
| 28485 } | |
| 28486 } | |
| 28487 | |
| 28488 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)); | |
| 28489 } | |
| 28490 } | |
| 28491 } | |
| 28492 | |
| 28493 var fiber = createFiber(fiberTag, pendingProps, key, mode); | |
| 28494 fiber.elementType = type; | |
| 28495 fiber.type = resolvedType; | |
| 28496 fiber.lanes = lanes; | |
| 28497 | |
| 28498 { | |
| 28499 fiber._debugOwner = owner; | |
| 28500 } | |
| 28501 | |
| 28502 return fiber; | |
| 28503 } | |
| 28504 function createFiberFromElement(element, mode, lanes) { | |
| 28505 var owner = null; | |
| 28506 | |
| 28507 { | |
| 28508 owner = element._owner; | |
| 28509 } | |
| 28510 | |
| 28511 var type = element.type; | |
| 28512 var key = element.key; | |
| 28513 var pendingProps = element.props; | |
| 28514 var fiber = createFiberFromTypeAndProps(type, key, pendingProps, owner, mode, lanes); | |
| 28515 | |
| 28516 { | |
| 28517 fiber._debugSource = element._source; | |
| 28518 fiber._debugOwner = element._owner; | |
| 28519 } | |
| 28520 | |
| 28521 return fiber; | |
| 28522 } | |
| 28523 function createFiberFromFragment(elements, mode, lanes, key) { | |
| 28524 var fiber = createFiber(Fragment, elements, key, mode); | |
| 28525 fiber.lanes = lanes; | |
| 28526 return fiber; | |
| 28527 } | |
| 28528 | |
| 28529 function createFiberFromProfiler(pendingProps, mode, lanes, key) { | |
| 28530 { | |
| 28531 if (typeof pendingProps.id !== 'string') { | |
| 28532 error('Profiler must specify an "id" of type `string` as a prop. Received the type `%s` instead.', typeof pendingProps.id); | |
| 28533 } | |
| 28534 } | |
| 28535 | |
| 28536 var fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode); | |
| 28537 fiber.elementType = REACT_PROFILER_TYPE; | |
| 28538 fiber.lanes = lanes; | |
| 28539 | |
| 28540 { | |
| 28541 fiber.stateNode = { | |
| 28542 effectDuration: 0, | |
| 28543 passiveEffectDuration: 0 | |
| 28544 }; | |
| 28545 } | |
| 28546 | |
| 28547 return fiber; | |
| 28548 } | |
| 28549 | |
| 28550 function createFiberFromSuspense(pendingProps, mode, lanes, key) { | |
| 28551 var fiber = createFiber(SuspenseComponent, pendingProps, key, mode); | |
| 28552 fiber.elementType = REACT_SUSPENSE_TYPE; | |
| 28553 fiber.lanes = lanes; | |
| 28554 return fiber; | |
| 28555 } | |
| 28556 function createFiberFromSuspenseList(pendingProps, mode, lanes, key) { | |
| 28557 var fiber = createFiber(SuspenseListComponent, pendingProps, key, mode); | |
| 28558 fiber.elementType = REACT_SUSPENSE_LIST_TYPE; | |
| 28559 fiber.lanes = lanes; | |
| 28560 return fiber; | |
| 28561 } | |
| 28562 function createFiberFromOffscreen(pendingProps, mode, lanes, key) { | |
| 28563 var fiber = createFiber(OffscreenComponent, pendingProps, key, mode); | |
| 28564 fiber.elementType = REACT_OFFSCREEN_TYPE; | |
| 28565 fiber.lanes = lanes; | |
| 28566 var primaryChildInstance = { | |
| 28567 isHidden: false | |
| 28568 }; | |
| 28569 fiber.stateNode = primaryChildInstance; | |
| 28570 return fiber; | |
| 28571 } | |
| 28572 function createFiberFromText(content, mode, lanes) { | |
| 28573 var fiber = createFiber(HostText, content, null, mode); | |
| 28574 fiber.lanes = lanes; | |
| 28575 return fiber; | |
| 28576 } | |
| 28577 function createFiberFromHostInstanceForDeletion() { | |
| 28578 var fiber = createFiber(HostComponent, null, null, NoMode); | |
| 28579 fiber.elementType = 'DELETED'; | |
| 28580 return fiber; | |
| 28581 } | |
| 28582 function createFiberFromDehydratedFragment(dehydratedNode) { | |
| 28583 var fiber = createFiber(DehydratedFragment, null, null, NoMode); | |
| 28584 fiber.stateNode = dehydratedNode; | |
| 28585 return fiber; | |
| 28586 } | |
| 28587 function createFiberFromPortal(portal, mode, lanes) { | |
| 28588 var pendingProps = portal.children !== null ? portal.children : []; | |
| 28589 var fiber = createFiber(HostPortal, pendingProps, portal.key, mode); | |
| 28590 fiber.lanes = lanes; | |
| 28591 fiber.stateNode = { | |
| 28592 containerInfo: portal.containerInfo, | |
| 28593 pendingChildren: null, | |
| 28594 // Used by persistent updates | |
| 28595 implementation: portal.implementation | |
| 28596 }; | |
| 28597 return fiber; | |
| 28598 } // Used for stashing WIP properties to replay failed work in DEV. | |
| 28599 | |
| 28600 function assignFiberPropertiesInDEV(target, source) { | |
| 28601 if (target === null) { | |
| 28602 // This Fiber's initial properties will always be overwritten. | |
| 28603 // We only use a Fiber to ensure the same hidden class so DEV isn't slow. | |
| 28604 target = createFiber(IndeterminateComponent, null, null, NoMode); | |
| 28605 } // This is intentionally written as a list of all properties. | |
| 28606 // We tried to use Object.assign() instead but this is called in | |
| 28607 // the hottest path, and Object.assign() was too slow: | |
| 28608 // https://github.com/facebook/react/issues/12502 | |
| 28609 // This code is DEV-only so size is not a concern. | |
| 28610 | |
| 28611 | |
| 28612 target.tag = source.tag; | |
| 28613 target.key = source.key; | |
| 28614 target.elementType = source.elementType; | |
| 28615 target.type = source.type; | |
| 28616 target.stateNode = source.stateNode; | |
| 28617 target.return = source.return; | |
| 28618 target.child = source.child; | |
| 28619 target.sibling = source.sibling; | |
| 28620 target.index = source.index; | |
| 28621 target.ref = source.ref; | |
| 28622 target.pendingProps = source.pendingProps; | |
| 28623 target.memoizedProps = source.memoizedProps; | |
| 28624 target.updateQueue = source.updateQueue; | |
| 28625 target.memoizedState = source.memoizedState; | |
| 28626 target.dependencies = source.dependencies; | |
| 28627 target.mode = source.mode; | |
| 28628 target.flags = source.flags; | |
| 28629 target.subtreeFlags = source.subtreeFlags; | |
| 28630 target.deletions = source.deletions; | |
| 28631 target.lanes = source.lanes; | |
| 28632 target.childLanes = source.childLanes; | |
| 28633 target.alternate = source.alternate; | |
| 28634 | |
| 28635 { | |
| 28636 target.actualDuration = source.actualDuration; | |
| 28637 target.actualStartTime = source.actualStartTime; | |
| 28638 target.selfBaseDuration = source.selfBaseDuration; | |
| 28639 target.treeBaseDuration = source.treeBaseDuration; | |
| 28640 } | |
| 28641 | |
| 28642 target._debugSource = source._debugSource; | |
| 28643 target._debugOwner = source._debugOwner; | |
| 28644 target._debugNeedsRemount = source._debugNeedsRemount; | |
| 28645 target._debugHookTypes = source._debugHookTypes; | |
| 28646 return target; | |
| 28647 } | |
| 28648 | |
| 28649 function FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError) { | |
| 28650 this.tag = tag; | |
| 28651 this.containerInfo = containerInfo; | |
| 28652 this.pendingChildren = null; | |
| 28653 this.current = null; | |
| 28654 this.pingCache = null; | |
| 28655 this.finishedWork = null; | |
| 28656 this.timeoutHandle = noTimeout; | |
| 28657 this.context = null; | |
| 28658 this.pendingContext = null; | |
| 28659 this.callbackNode = null; | |
| 28660 this.callbackPriority = NoLane; | |
| 28661 this.eventTimes = createLaneMap(NoLanes); | |
| 28662 this.expirationTimes = createLaneMap(NoTimestamp); | |
| 28663 this.pendingLanes = NoLanes; | |
| 28664 this.suspendedLanes = NoLanes; | |
| 28665 this.pingedLanes = NoLanes; | |
| 28666 this.expiredLanes = NoLanes; | |
| 28667 this.mutableReadLanes = NoLanes; | |
| 28668 this.finishedLanes = NoLanes; | |
| 28669 this.entangledLanes = NoLanes; | |
| 28670 this.entanglements = createLaneMap(NoLanes); | |
| 28671 this.identifierPrefix = identifierPrefix; | |
| 28672 this.onRecoverableError = onRecoverableError; | |
| 28673 | |
| 28674 { | |
| 28675 this.mutableSourceEagerHydrationData = null; | |
| 28676 } | |
| 28677 | |
| 28678 { | |
| 28679 this.effectDuration = 0; | |
| 28680 this.passiveEffectDuration = 0; | |
| 28681 } | |
| 28682 | |
| 28683 { | |
| 28684 this.memoizedUpdaters = new Set(); | |
| 28685 var pendingUpdatersLaneMap = this.pendingUpdatersLaneMap = []; | |
| 28686 | |
| 28687 for (var _i = 0; _i < TotalLanes; _i++) { | |
| 28688 pendingUpdatersLaneMap.push(new Set()); | |
| 28689 } | |
| 28690 } | |
| 28691 | |
| 28692 { | |
| 28693 switch (tag) { | |
| 28694 case ConcurrentRoot: | |
| 28695 this._debugRootType = hydrate ? 'hydrateRoot()' : 'createRoot()'; | |
| 28696 break; | |
| 28697 | |
| 28698 case LegacyRoot: | |
| 28699 this._debugRootType = hydrate ? 'hydrate()' : 'render()'; | |
| 28700 break; | |
| 28701 } | |
| 28702 } | |
| 28703 } | |
| 28704 | |
| 28705 function createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, // TODO: We have several of these arguments that are conceptually part of the | |
| 28706 // host config, but because they are passed in at runtime, we have to thread | |
| 28707 // them through the root constructor. Perhaps we should put them all into a | |
| 28708 // single type, like a DynamicHostConfig that is defined by the renderer. | |
| 28709 identifierPrefix, onRecoverableError, transitionCallbacks) { | |
| 28710 var root = new FiberRootNode(containerInfo, tag, hydrate, identifierPrefix, onRecoverableError); | |
| 28711 // stateNode is any. | |
| 28712 | |
| 28713 | |
| 28714 var uninitializedFiber = createHostRootFiber(tag, isStrictMode); | |
| 28715 root.current = uninitializedFiber; | |
| 28716 uninitializedFiber.stateNode = root; | |
| 28717 | |
| 28718 { | |
| 28719 var _initialState = { | |
| 28720 element: initialChildren, | |
| 28721 isDehydrated: hydrate, | |
| 28722 cache: null, | |
| 28723 // not enabled yet | |
| 28724 transitions: null, | |
| 28725 pendingSuspenseBoundaries: null | |
| 28726 }; | |
| 28727 uninitializedFiber.memoizedState = _initialState; | |
| 28728 } | |
| 28729 | |
| 28730 initializeUpdateQueue(uninitializedFiber); | |
| 28731 return root; | |
| 28732 } | |
| 28733 | |
| 28734 var ReactVersion = '18.3.1'; | |
| 28735 | |
| 28736 function createPortal(children, containerInfo, // TODO: figure out the API for cross-renderer implementation. | |
| 28737 implementation) { | |
| 28738 var key = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null; | |
| 28739 | |
| 28740 { | |
| 28741 checkKeyStringCoercion(key); | |
| 28742 } | |
| 28743 | |
| 28744 return { | |
| 28745 // This tag allow us to uniquely identify this as a React Portal | |
| 28746 $$typeof: REACT_PORTAL_TYPE, | |
| 28747 key: key == null ? null : '' + key, | |
| 28748 children: children, | |
| 28749 containerInfo: containerInfo, | |
| 28750 implementation: implementation | |
| 28751 }; | |
| 28752 } | |
| 28753 | |
| 28754 var didWarnAboutNestedUpdates; | |
| 28755 var didWarnAboutFindNodeInStrictMode; | |
| 28756 | |
| 28757 { | |
| 28758 didWarnAboutNestedUpdates = false; | |
| 28759 didWarnAboutFindNodeInStrictMode = {}; | |
| 28760 } | |
| 28761 | |
| 28762 function getContextForSubtree(parentComponent) { | |
| 28763 if (!parentComponent) { | |
| 28764 return emptyContextObject; | |
| 28765 } | |
| 28766 | |
| 28767 var fiber = get(parentComponent); | |
| 28768 var parentContext = findCurrentUnmaskedContext(fiber); | |
| 28769 | |
| 28770 if (fiber.tag === ClassComponent) { | |
| 28771 var Component = fiber.type; | |
| 28772 | |
| 28773 if (isContextProvider(Component)) { | |
| 28774 return processChildContext(fiber, Component, parentContext); | |
| 28775 } | |
| 28776 } | |
| 28777 | |
| 28778 return parentContext; | |
| 28779 } | |
| 28780 | |
| 28781 function findHostInstanceWithWarning(component, methodName) { | |
| 28782 { | |
| 28783 var fiber = get(component); | |
| 28784 | |
| 28785 if (fiber === undefined) { | |
| 28786 if (typeof component.render === 'function') { | |
| 28787 throw new Error('Unable to find node on an unmounted component.'); | |
| 28788 } else { | |
| 28789 var keys = Object.keys(component).join(','); | |
| 28790 throw new Error("Argument appears to not be a ReactComponent. Keys: " + keys); | |
| 28791 } | |
| 28792 } | |
| 28793 | |
| 28794 var hostFiber = findCurrentHostFiber(fiber); | |
| 28795 | |
| 28796 if (hostFiber === null) { | |
| 28797 return null; | |
| 28798 } | |
| 28799 | |
| 28800 if (hostFiber.mode & StrictLegacyMode) { | |
| 28801 var componentName = getComponentNameFromFiber(fiber) || 'Component'; | |
| 28802 | |
| 28803 if (!didWarnAboutFindNodeInStrictMode[componentName]) { | |
| 28804 didWarnAboutFindNodeInStrictMode[componentName] = true; | |
| 28805 var previousFiber = current; | |
| 28806 | |
| 28807 try { | |
| 28808 setCurrentFiber(hostFiber); | |
| 28809 | |
| 28810 if (fiber.mode & StrictLegacyMode) { | |
| 28811 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); | |
| 28812 } else { | |
| 28813 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); | |
| 28814 } | |
| 28815 } finally { | |
| 28816 // Ideally this should reset to previous but this shouldn't be called in | |
| 28817 // render and there's another warning for that anyway. | |
| 28818 if (previousFiber) { | |
| 28819 setCurrentFiber(previousFiber); | |
| 28820 } else { | |
| 28821 resetCurrentFiber(); | |
| 28822 } | |
| 28823 } | |
| 28824 } | |
| 28825 } | |
| 28826 | |
| 28827 return hostFiber.stateNode; | |
| 28828 } | |
| 28829 } | |
| 28830 | |
| 28831 function createContainer(containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) { | |
| 28832 var hydrate = false; | |
| 28833 var initialChildren = null; | |
| 28834 return createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); | |
| 28835 } | |
| 28836 function createHydrationContainer(initialChildren, // TODO: Remove `callback` when we delete legacy mode. | |
| 28837 callback, containerInfo, tag, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError, transitionCallbacks) { | |
| 28838 var hydrate = true; | |
| 28839 var root = createFiberRoot(containerInfo, tag, hydrate, initialChildren, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); // TODO: Move this to FiberRoot constructor | |
| 28840 | |
| 28841 root.context = getContextForSubtree(null); // Schedule the initial render. In a hydration root, this is different from | |
| 28842 // a regular update because the initial render must match was was rendered | |
| 28843 // on the server. | |
| 28844 // NOTE: This update intentionally doesn't have a payload. We're only using | |
| 28845 // the update to schedule work on the root fiber (and, for legacy roots, to | |
| 28846 // enqueue the callback if one is provided). | |
| 28847 | |
| 28848 var current = root.current; | |
| 28849 var eventTime = requestEventTime(); | |
| 28850 var lane = requestUpdateLane(current); | |
| 28851 var update = createUpdate(eventTime, lane); | |
| 28852 update.callback = callback !== undefined && callback !== null ? callback : null; | |
| 28853 enqueueUpdate(current, update, lane); | |
| 28854 scheduleInitialHydrationOnRoot(root, lane, eventTime); | |
| 28855 return root; | |
| 28856 } | |
| 28857 function updateContainer(element, container, parentComponent, callback) { | |
| 28858 { | |
| 28859 onScheduleRoot(container, element); | |
| 28860 } | |
| 28861 | |
| 28862 var current$1 = container.current; | |
| 28863 var eventTime = requestEventTime(); | |
| 28864 var lane = requestUpdateLane(current$1); | |
| 28865 | |
| 28866 { | |
| 28867 markRenderScheduled(lane); | |
| 28868 } | |
| 28869 | |
| 28870 var context = getContextForSubtree(parentComponent); | |
| 28871 | |
| 28872 if (container.context === null) { | |
| 28873 container.context = context; | |
| 28874 } else { | |
| 28875 container.pendingContext = context; | |
| 28876 } | |
| 28877 | |
| 28878 { | |
| 28879 if (isRendering && current !== null && !didWarnAboutNestedUpdates) { | |
| 28880 didWarnAboutNestedUpdates = true; | |
| 28881 | |
| 28882 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'); | |
| 28883 } | |
| 28884 } | |
| 28885 | |
| 28886 var update = createUpdate(eventTime, lane); // Caution: React DevTools currently depends on this property | |
| 28887 // being called "element". | |
| 28888 | |
| 28889 update.payload = { | |
| 28890 element: element | |
| 28891 }; | |
| 28892 callback = callback === undefined ? null : callback; | |
| 28893 | |
| 28894 if (callback !== null) { | |
| 28895 { | |
| 28896 if (typeof callback !== 'function') { | |
| 28897 error('render(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callback); | |
| 28898 } | |
| 28899 } | |
| 28900 | |
| 28901 update.callback = callback; | |
| 28902 } | |
| 28903 | |
| 28904 var root = enqueueUpdate(current$1, update, lane); | |
| 28905 | |
| 28906 if (root !== null) { | |
| 28907 scheduleUpdateOnFiber(root, current$1, lane, eventTime); | |
| 28908 entangleTransitions(root, current$1, lane); | |
| 28909 } | |
| 28910 | |
| 28911 return lane; | |
| 28912 } | |
| 28913 function getPublicRootInstance(container) { | |
| 28914 var containerFiber = container.current; | |
| 28915 | |
| 28916 if (!containerFiber.child) { | |
| 28917 return null; | |
| 28918 } | |
| 28919 | |
| 28920 switch (containerFiber.child.tag) { | |
| 28921 case HostComponent: | |
| 28922 return getPublicInstance(containerFiber.child.stateNode); | |
| 28923 | |
| 28924 default: | |
| 28925 return containerFiber.child.stateNode; | |
| 28926 } | |
| 28927 } | |
| 28928 function attemptSynchronousHydration$1(fiber) { | |
| 28929 switch (fiber.tag) { | |
| 28930 case HostRoot: | |
| 28931 { | |
| 28932 var root = fiber.stateNode; | |
| 28933 | |
| 28934 if (isRootDehydrated(root)) { | |
| 28935 // Flush the first scheduled "update". | |
| 28936 var lanes = getHighestPriorityPendingLanes(root); | |
| 28937 flushRoot(root, lanes); | |
| 28938 } | |
| 28939 | |
| 28940 break; | |
| 28941 } | |
| 28942 | |
| 28943 case SuspenseComponent: | |
| 28944 { | |
| 28945 flushSync(function () { | |
| 28946 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 28947 | |
| 28948 if (root !== null) { | |
| 28949 var eventTime = requestEventTime(); | |
| 28950 scheduleUpdateOnFiber(root, fiber, SyncLane, eventTime); | |
| 28951 } | |
| 28952 }); // If we're still blocked after this, we need to increase | |
| 28953 // the priority of any promises resolving within this | |
| 28954 // boundary so that they next attempt also has higher pri. | |
| 28955 | |
| 28956 var retryLane = SyncLane; | |
| 28957 markRetryLaneIfNotHydrated(fiber, retryLane); | |
| 28958 break; | |
| 28959 } | |
| 28960 } | |
| 28961 } | |
| 28962 | |
| 28963 function markRetryLaneImpl(fiber, retryLane) { | |
| 28964 var suspenseState = fiber.memoizedState; | |
| 28965 | |
| 28966 if (suspenseState !== null && suspenseState.dehydrated !== null) { | |
| 28967 suspenseState.retryLane = higherPriorityLane(suspenseState.retryLane, retryLane); | |
| 28968 } | |
| 28969 } // Increases the priority of thenables when they resolve within this boundary. | |
| 28970 | |
| 28971 | |
| 28972 function markRetryLaneIfNotHydrated(fiber, retryLane) { | |
| 28973 markRetryLaneImpl(fiber, retryLane); | |
| 28974 var alternate = fiber.alternate; | |
| 28975 | |
| 28976 if (alternate) { | |
| 28977 markRetryLaneImpl(alternate, retryLane); | |
| 28978 } | |
| 28979 } | |
| 28980 function attemptContinuousHydration$1(fiber) { | |
| 28981 if (fiber.tag !== SuspenseComponent) { | |
| 28982 // We ignore HostRoots here because we can't increase | |
| 28983 // their priority and they should not suspend on I/O, | |
| 28984 // since you have to wrap anything that might suspend in | |
| 28985 // Suspense. | |
| 28986 return; | |
| 28987 } | |
| 28988 | |
| 28989 var lane = SelectiveHydrationLane; | |
| 28990 var root = enqueueConcurrentRenderForLane(fiber, lane); | |
| 28991 | |
| 28992 if (root !== null) { | |
| 28993 var eventTime = requestEventTime(); | |
| 28994 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 28995 } | |
| 28996 | |
| 28997 markRetryLaneIfNotHydrated(fiber, lane); | |
| 28998 } | |
| 28999 function attemptHydrationAtCurrentPriority$1(fiber) { | |
| 29000 if (fiber.tag !== SuspenseComponent) { | |
| 29001 // We ignore HostRoots here because we can't increase | |
| 29002 // their priority other than synchronously flush it. | |
| 29003 return; | |
| 29004 } | |
| 29005 | |
| 29006 var lane = requestUpdateLane(fiber); | |
| 29007 var root = enqueueConcurrentRenderForLane(fiber, lane); | |
| 29008 | |
| 29009 if (root !== null) { | |
| 29010 var eventTime = requestEventTime(); | |
| 29011 scheduleUpdateOnFiber(root, fiber, lane, eventTime); | |
| 29012 } | |
| 29013 | |
| 29014 markRetryLaneIfNotHydrated(fiber, lane); | |
| 29015 } | |
| 29016 function findHostInstanceWithNoPortals(fiber) { | |
| 29017 var hostFiber = findCurrentHostFiberWithNoPortals(fiber); | |
| 29018 | |
| 29019 if (hostFiber === null) { | |
| 29020 return null; | |
| 29021 } | |
| 29022 | |
| 29023 return hostFiber.stateNode; | |
| 29024 } | |
| 29025 | |
| 29026 var shouldErrorImpl = function (fiber) { | |
| 29027 return null; | |
| 29028 }; | |
| 29029 | |
| 29030 function shouldError(fiber) { | |
| 29031 return shouldErrorImpl(fiber); | |
| 29032 } | |
| 29033 | |
| 29034 var shouldSuspendImpl = function (fiber) { | |
| 29035 return false; | |
| 29036 }; | |
| 29037 | |
| 29038 function shouldSuspend(fiber) { | |
| 29039 return shouldSuspendImpl(fiber); | |
| 29040 } | |
| 29041 var overrideHookState = null; | |
| 29042 var overrideHookStateDeletePath = null; | |
| 29043 var overrideHookStateRenamePath = null; | |
| 29044 var overrideProps = null; | |
| 29045 var overridePropsDeletePath = null; | |
| 29046 var overridePropsRenamePath = null; | |
| 29047 var scheduleUpdate = null; | |
| 29048 var setErrorHandler = null; | |
| 29049 var setSuspenseHandler = null; | |
| 29050 | |
| 29051 { | |
| 29052 var copyWithDeleteImpl = function (obj, path, index) { | |
| 29053 var key = path[index]; | |
| 29054 var updated = isArray(obj) ? obj.slice() : assign({}, obj); | |
| 29055 | |
| 29056 if (index + 1 === path.length) { | |
| 29057 if (isArray(updated)) { | |
| 29058 updated.splice(key, 1); | |
| 29059 } else { | |
| 29060 delete updated[key]; | |
| 29061 } | |
| 29062 | |
| 29063 return updated; | |
| 29064 } // $FlowFixMe number or string is fine here | |
| 29065 | |
| 29066 | |
| 29067 updated[key] = copyWithDeleteImpl(obj[key], path, index + 1); | |
| 29068 return updated; | |
| 29069 }; | |
| 29070 | |
| 29071 var copyWithDelete = function (obj, path) { | |
| 29072 return copyWithDeleteImpl(obj, path, 0); | |
| 29073 }; | |
| 29074 | |
| 29075 var copyWithRenameImpl = function (obj, oldPath, newPath, index) { | |
| 29076 var oldKey = oldPath[index]; | |
| 29077 var updated = isArray(obj) ? obj.slice() : assign({}, obj); | |
| 29078 | |
| 29079 if (index + 1 === oldPath.length) { | |
| 29080 var newKey = newPath[index]; // $FlowFixMe number or string is fine here | |
| 29081 | |
| 29082 updated[newKey] = updated[oldKey]; | |
| 29083 | |
| 29084 if (isArray(updated)) { | |
| 29085 updated.splice(oldKey, 1); | |
| 29086 } else { | |
| 29087 delete updated[oldKey]; | |
| 29088 } | |
| 29089 } else { | |
| 29090 // $FlowFixMe number or string is fine here | |
| 29091 updated[oldKey] = copyWithRenameImpl( // $FlowFixMe number or string is fine here | |
| 29092 obj[oldKey], oldPath, newPath, index + 1); | |
| 29093 } | |
| 29094 | |
| 29095 return updated; | |
| 29096 }; | |
| 29097 | |
| 29098 var copyWithRename = function (obj, oldPath, newPath) { | |
| 29099 if (oldPath.length !== newPath.length) { | |
| 29100 warn('copyWithRename() expects paths of the same length'); | |
| 29101 | |
| 29102 return; | |
| 29103 } else { | |
| 29104 for (var i = 0; i < newPath.length - 1; i++) { | |
| 29105 if (oldPath[i] !== newPath[i]) { | |
| 29106 warn('copyWithRename() expects paths to be the same except for the deepest key'); | |
| 29107 | |
| 29108 return; | |
| 29109 } | |
| 29110 } | |
| 29111 } | |
| 29112 | |
| 29113 return copyWithRenameImpl(obj, oldPath, newPath, 0); | |
| 29114 }; | |
| 29115 | |
| 29116 var copyWithSetImpl = function (obj, path, index, value) { | |
| 29117 if (index >= path.length) { | |
| 29118 return value; | |
| 29119 } | |
| 29120 | |
| 29121 var key = path[index]; | |
| 29122 var updated = isArray(obj) ? obj.slice() : assign({}, obj); // $FlowFixMe number or string is fine here | |
| 29123 | |
| 29124 updated[key] = copyWithSetImpl(obj[key], path, index + 1, value); | |
| 29125 return updated; | |
| 29126 }; | |
| 29127 | |
| 29128 var copyWithSet = function (obj, path, value) { | |
| 29129 return copyWithSetImpl(obj, path, 0, value); | |
| 29130 }; | |
| 29131 | |
| 29132 var findHook = function (fiber, id) { | |
| 29133 // For now, the "id" of stateful hooks is just the stateful hook index. | |
| 29134 // This may change in the future with e.g. nested hooks. | |
| 29135 var currentHook = fiber.memoizedState; | |
| 29136 | |
| 29137 while (currentHook !== null && id > 0) { | |
| 29138 currentHook = currentHook.next; | |
| 29139 id--; | |
| 29140 } | |
| 29141 | |
| 29142 return currentHook; | |
| 29143 }; // Support DevTools editable values for useState and useReducer. | |
| 29144 | |
| 29145 | |
| 29146 overrideHookState = function (fiber, id, path, value) { | |
| 29147 var hook = findHook(fiber, id); | |
| 29148 | |
| 29149 if (hook !== null) { | |
| 29150 var newState = copyWithSet(hook.memoizedState, path, value); | |
| 29151 hook.memoizedState = newState; | |
| 29152 hook.baseState = newState; // We aren't actually adding an update to the queue, | |
| 29153 // because there is no update we can add for useReducer hooks that won't trigger an error. | |
| 29154 // (There's no appropriate action type for DevTools overrides.) | |
| 29155 // As a result though, React will see the scheduled update as a noop and bailout. | |
| 29156 // Shallow cloning props works as a workaround for now to bypass the bailout check. | |
| 29157 | |
| 29158 fiber.memoizedProps = assign({}, fiber.memoizedProps); | |
| 29159 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29160 | |
| 29161 if (root !== null) { | |
| 29162 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29163 } | |
| 29164 } | |
| 29165 }; | |
| 29166 | |
| 29167 overrideHookStateDeletePath = function (fiber, id, path) { | |
| 29168 var hook = findHook(fiber, id); | |
| 29169 | |
| 29170 if (hook !== null) { | |
| 29171 var newState = copyWithDelete(hook.memoizedState, path); | |
| 29172 hook.memoizedState = newState; | |
| 29173 hook.baseState = newState; // We aren't actually adding an update to the queue, | |
| 29174 // because there is no update we can add for useReducer hooks that won't trigger an error. | |
| 29175 // (There's no appropriate action type for DevTools overrides.) | |
| 29176 // As a result though, React will see the scheduled update as a noop and bailout. | |
| 29177 // Shallow cloning props works as a workaround for now to bypass the bailout check. | |
| 29178 | |
| 29179 fiber.memoizedProps = assign({}, fiber.memoizedProps); | |
| 29180 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29181 | |
| 29182 if (root !== null) { | |
| 29183 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29184 } | |
| 29185 } | |
| 29186 }; | |
| 29187 | |
| 29188 overrideHookStateRenamePath = function (fiber, id, oldPath, newPath) { | |
| 29189 var hook = findHook(fiber, id); | |
| 29190 | |
| 29191 if (hook !== null) { | |
| 29192 var newState = copyWithRename(hook.memoizedState, oldPath, newPath); | |
| 29193 hook.memoizedState = newState; | |
| 29194 hook.baseState = newState; // We aren't actually adding an update to the queue, | |
| 29195 // because there is no update we can add for useReducer hooks that won't trigger an error. | |
| 29196 // (There's no appropriate action type for DevTools overrides.) | |
| 29197 // As a result though, React will see the scheduled update as a noop and bailout. | |
| 29198 // Shallow cloning props works as a workaround for now to bypass the bailout check. | |
| 29199 | |
| 29200 fiber.memoizedProps = assign({}, fiber.memoizedProps); | |
| 29201 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29202 | |
| 29203 if (root !== null) { | |
| 29204 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29205 } | |
| 29206 } | |
| 29207 }; // Support DevTools props for function components, forwardRef, memo, host components, etc. | |
| 29208 | |
| 29209 | |
| 29210 overrideProps = function (fiber, path, value) { | |
| 29211 fiber.pendingProps = copyWithSet(fiber.memoizedProps, path, value); | |
| 29212 | |
| 29213 if (fiber.alternate) { | |
| 29214 fiber.alternate.pendingProps = fiber.pendingProps; | |
| 29215 } | |
| 29216 | |
| 29217 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29218 | |
| 29219 if (root !== null) { | |
| 29220 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29221 } | |
| 29222 }; | |
| 29223 | |
| 29224 overridePropsDeletePath = function (fiber, path) { | |
| 29225 fiber.pendingProps = copyWithDelete(fiber.memoizedProps, path); | |
| 29226 | |
| 29227 if (fiber.alternate) { | |
| 29228 fiber.alternate.pendingProps = fiber.pendingProps; | |
| 29229 } | |
| 29230 | |
| 29231 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29232 | |
| 29233 if (root !== null) { | |
| 29234 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29235 } | |
| 29236 }; | |
| 29237 | |
| 29238 overridePropsRenamePath = function (fiber, oldPath, newPath) { | |
| 29239 fiber.pendingProps = copyWithRename(fiber.memoizedProps, oldPath, newPath); | |
| 29240 | |
| 29241 if (fiber.alternate) { | |
| 29242 fiber.alternate.pendingProps = fiber.pendingProps; | |
| 29243 } | |
| 29244 | |
| 29245 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29246 | |
| 29247 if (root !== null) { | |
| 29248 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29249 } | |
| 29250 }; | |
| 29251 | |
| 29252 scheduleUpdate = function (fiber) { | |
| 29253 var root = enqueueConcurrentRenderForLane(fiber, SyncLane); | |
| 29254 | |
| 29255 if (root !== null) { | |
| 29256 scheduleUpdateOnFiber(root, fiber, SyncLane, NoTimestamp); | |
| 29257 } | |
| 29258 }; | |
| 29259 | |
| 29260 setErrorHandler = function (newShouldErrorImpl) { | |
| 29261 shouldErrorImpl = newShouldErrorImpl; | |
| 29262 }; | |
| 29263 | |
| 29264 setSuspenseHandler = function (newShouldSuspendImpl) { | |
| 29265 shouldSuspendImpl = newShouldSuspendImpl; | |
| 29266 }; | |
| 29267 } | |
| 29268 | |
| 29269 function findHostInstanceByFiber(fiber) { | |
| 29270 var hostFiber = findCurrentHostFiber(fiber); | |
| 29271 | |
| 29272 if (hostFiber === null) { | |
| 29273 return null; | |
| 29274 } | |
| 29275 | |
| 29276 return hostFiber.stateNode; | |
| 29277 } | |
| 29278 | |
| 29279 function emptyFindFiberByHostInstance(instance) { | |
| 29280 return null; | |
| 29281 } | |
| 29282 | |
| 29283 function getCurrentFiberForDevTools() { | |
| 29284 return current; | |
| 29285 } | |
| 29286 | |
| 29287 function injectIntoDevTools(devToolsConfig) { | |
| 29288 var findFiberByHostInstance = devToolsConfig.findFiberByHostInstance; | |
| 29289 var ReactCurrentDispatcher = ReactSharedInternals.ReactCurrentDispatcher; | |
| 29290 return injectInternals({ | |
| 29291 bundleType: devToolsConfig.bundleType, | |
| 29292 version: devToolsConfig.version, | |
| 29293 rendererPackageName: devToolsConfig.rendererPackageName, | |
| 29294 rendererConfig: devToolsConfig.rendererConfig, | |
| 29295 overrideHookState: overrideHookState, | |
| 29296 overrideHookStateDeletePath: overrideHookStateDeletePath, | |
| 29297 overrideHookStateRenamePath: overrideHookStateRenamePath, | |
| 29298 overrideProps: overrideProps, | |
| 29299 overridePropsDeletePath: overridePropsDeletePath, | |
| 29300 overridePropsRenamePath: overridePropsRenamePath, | |
| 29301 setErrorHandler: setErrorHandler, | |
| 29302 setSuspenseHandler: setSuspenseHandler, | |
| 29303 scheduleUpdate: scheduleUpdate, | |
| 29304 currentDispatcherRef: ReactCurrentDispatcher, | |
| 29305 findHostInstanceByFiber: findHostInstanceByFiber, | |
| 29306 findFiberByHostInstance: findFiberByHostInstance || emptyFindFiberByHostInstance, | |
| 29307 // React Refresh | |
| 29308 findHostInstancesForRefresh: findHostInstancesForRefresh , | |
| 29309 scheduleRefresh: scheduleRefresh , | |
| 29310 scheduleRoot: scheduleRoot , | |
| 29311 setRefreshHandler: setRefreshHandler , | |
| 29312 // Enables DevTools to append owner stacks to error messages in DEV mode. | |
| 29313 getCurrentFiber: getCurrentFiberForDevTools , | |
| 29314 // Enables DevTools to detect reconciler version rather than renderer version | |
| 29315 // which may not match for third party renderers. | |
| 29316 reconcilerVersion: ReactVersion | |
| 29317 }); | |
| 29318 } | |
| 29319 | |
| 29320 /* global reportError */ | |
| 29321 | |
| 29322 var defaultOnRecoverableError = typeof reportError === 'function' ? // In modern browsers, reportError will dispatch an error event, | |
| 29323 // emulating an uncaught JavaScript error. | |
| 29324 reportError : function (error) { | |
| 29325 // In older browsers and test environments, fallback to console.error. | |
| 29326 // eslint-disable-next-line react-internal/no-production-logging | |
| 29327 console['error'](error); | |
| 29328 }; | |
| 29329 | |
| 29330 function ReactDOMRoot(internalRoot) { | |
| 29331 this._internalRoot = internalRoot; | |
| 29332 } | |
| 29333 | |
| 29334 ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render = function (children) { | |
| 29335 var root = this._internalRoot; | |
| 29336 | |
| 29337 if (root === null) { | |
| 29338 throw new Error('Cannot update an unmounted root.'); | |
| 29339 } | |
| 29340 | |
| 29341 { | |
| 29342 if (typeof arguments[1] === 'function') { | |
| 29343 error('render(...): does not support the second callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().'); | |
| 29344 } else if (isValidContainer(arguments[1])) { | |
| 29345 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."); | |
| 29346 } else if (typeof arguments[1] !== 'undefined') { | |
| 29347 error('You passed a second argument to root.render(...) but it only accepts ' + 'one argument.'); | |
| 29348 } | |
| 29349 | |
| 29350 var container = root.containerInfo; | |
| 29351 | |
| 29352 if (container.nodeType !== COMMENT_NODE) { | |
| 29353 var hostInstance = findHostInstanceWithNoPortals(root.current); | |
| 29354 | |
| 29355 if (hostInstance) { | |
| 29356 if (hostInstance.parentNode !== container) { | |
| 29357 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."); | |
| 29358 } | |
| 29359 } | |
| 29360 } | |
| 29361 } | |
| 29362 | |
| 29363 updateContainer(children, root, null, null); | |
| 29364 }; | |
| 29365 | |
| 29366 ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount = function () { | |
| 29367 { | |
| 29368 if (typeof arguments[0] === 'function') { | |
| 29369 error('unmount(...): does not support a callback argument. ' + 'To execute a side effect after rendering, declare it in a component body with useEffect().'); | |
| 29370 } | |
| 29371 } | |
| 29372 | |
| 29373 var root = this._internalRoot; | |
| 29374 | |
| 29375 if (root !== null) { | |
| 29376 this._internalRoot = null; | |
| 29377 var container = root.containerInfo; | |
| 29378 | |
| 29379 { | |
| 29380 if (isAlreadyRendering()) { | |
| 29381 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.'); | |
| 29382 } | |
| 29383 } | |
| 29384 | |
| 29385 flushSync(function () { | |
| 29386 updateContainer(null, root, null, null); | |
| 29387 }); | |
| 29388 unmarkContainerAsRoot(container); | |
| 29389 } | |
| 29390 }; | |
| 29391 | |
| 29392 function createRoot(container, options) { | |
| 29393 if (!isValidContainer(container)) { | |
| 29394 throw new Error('createRoot(...): Target container is not a DOM element.'); | |
| 29395 } | |
| 29396 | |
| 29397 warnIfReactDOMContainerInDEV(container); | |
| 29398 var isStrictMode = false; | |
| 29399 var concurrentUpdatesByDefaultOverride = false; | |
| 29400 var identifierPrefix = ''; | |
| 29401 var onRecoverableError = defaultOnRecoverableError; | |
| 29402 var transitionCallbacks = null; | |
| 29403 | |
| 29404 if (options !== null && options !== undefined) { | |
| 29405 { | |
| 29406 if (options.hydrate) { | |
| 29407 warn('hydrate through createRoot is deprecated. Use ReactDOMClient.hydrateRoot(container, <App />) instead.'); | |
| 29408 } else { | |
| 29409 if (typeof options === 'object' && options !== null && options.$$typeof === REACT_ELEMENT_TYPE) { | |
| 29410 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 />);'); | |
| 29411 } | |
| 29412 } | |
| 29413 } | |
| 29414 | |
| 29415 if (options.unstable_strictMode === true) { | |
| 29416 isStrictMode = true; | |
| 29417 } | |
| 29418 | |
| 29419 if (options.identifierPrefix !== undefined) { | |
| 29420 identifierPrefix = options.identifierPrefix; | |
| 29421 } | |
| 29422 | |
| 29423 if (options.onRecoverableError !== undefined) { | |
| 29424 onRecoverableError = options.onRecoverableError; | |
| 29425 } | |
| 29426 | |
| 29427 if (options.transitionCallbacks !== undefined) { | |
| 29428 transitionCallbacks = options.transitionCallbacks; | |
| 29429 } | |
| 29430 } | |
| 29431 | |
| 29432 var root = createContainer(container, ConcurrentRoot, null, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); | |
| 29433 markContainerAsRoot(root.current, container); | |
| 29434 var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; | |
| 29435 listenToAllSupportedEvents(rootContainerElement); | |
| 29436 return new ReactDOMRoot(root); | |
| 29437 } | |
| 29438 | |
| 29439 function ReactDOMHydrationRoot(internalRoot) { | |
| 29440 this._internalRoot = internalRoot; | |
| 29441 } | |
| 29442 | |
| 29443 function scheduleHydration(target) { | |
| 29444 if (target) { | |
| 29445 queueExplicitHydrationTarget(target); | |
| 29446 } | |
| 29447 } | |
| 29448 | |
| 29449 ReactDOMHydrationRoot.prototype.unstable_scheduleHydration = scheduleHydration; | |
| 29450 function hydrateRoot(container, initialChildren, options) { | |
| 29451 if (!isValidContainer(container)) { | |
| 29452 throw new Error('hydrateRoot(...): Target container is not a DOM element.'); | |
| 29453 } | |
| 29454 | |
| 29455 warnIfReactDOMContainerInDEV(container); | |
| 29456 | |
| 29457 { | |
| 29458 if (initialChildren === undefined) { | |
| 29459 error('Must provide initial children as second argument to hydrateRoot. ' + 'Example usage: hydrateRoot(domContainer, <App />)'); | |
| 29460 } | |
| 29461 } // For now we reuse the whole bag of options since they contain | |
| 29462 // the hydration callbacks. | |
| 29463 | |
| 29464 | |
| 29465 var hydrationCallbacks = options != null ? options : null; // TODO: Delete this option | |
| 29466 | |
| 29467 var mutableSources = options != null && options.hydratedSources || null; | |
| 29468 var isStrictMode = false; | |
| 29469 var concurrentUpdatesByDefaultOverride = false; | |
| 29470 var identifierPrefix = ''; | |
| 29471 var onRecoverableError = defaultOnRecoverableError; | |
| 29472 | |
| 29473 if (options !== null && options !== undefined) { | |
| 29474 if (options.unstable_strictMode === true) { | |
| 29475 isStrictMode = true; | |
| 29476 } | |
| 29477 | |
| 29478 if (options.identifierPrefix !== undefined) { | |
| 29479 identifierPrefix = options.identifierPrefix; | |
| 29480 } | |
| 29481 | |
| 29482 if (options.onRecoverableError !== undefined) { | |
| 29483 onRecoverableError = options.onRecoverableError; | |
| 29484 } | |
| 29485 } | |
| 29486 | |
| 29487 var root = createHydrationContainer(initialChildren, null, container, ConcurrentRoot, hydrationCallbacks, isStrictMode, concurrentUpdatesByDefaultOverride, identifierPrefix, onRecoverableError); | |
| 29488 markContainerAsRoot(root.current, container); // This can't be a comment node since hydration doesn't work on comment nodes anyway. | |
| 29489 | |
| 29490 listenToAllSupportedEvents(container); | |
| 29491 | |
| 29492 if (mutableSources) { | |
| 29493 for (var i = 0; i < mutableSources.length; i++) { | |
| 29494 var mutableSource = mutableSources[i]; | |
| 29495 registerMutableSourceForHydration(root, mutableSource); | |
| 29496 } | |
| 29497 } | |
| 29498 | |
| 29499 return new ReactDOMHydrationRoot(root); | |
| 29500 } | |
| 29501 function isValidContainer(node) { | |
| 29502 return !!(node && (node.nodeType === ELEMENT_NODE || node.nodeType === DOCUMENT_NODE || node.nodeType === DOCUMENT_FRAGMENT_NODE || !disableCommentsAsDOMContainers )); | |
| 29503 } // TODO: Remove this function which also includes comment nodes. | |
| 29504 // We only use it in places that are currently more relaxed. | |
| 29505 | |
| 29506 function isValidContainerLegacy(node) { | |
| 29507 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 ')); | |
| 29508 } | |
| 29509 | |
| 29510 function warnIfReactDOMContainerInDEV(container) { | |
| 29511 { | |
| 29512 if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') { | |
| 29513 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.'); | |
| 29514 } | |
| 29515 | |
| 29516 if (isContainerMarkedAsRoot(container)) { | |
| 29517 if (container._reactRootContainer) { | |
| 29518 error('You are calling ReactDOMClient.createRoot() on a container that was previously ' + 'passed to ReactDOM.render(). This is not supported.'); | |
| 29519 } else { | |
| 29520 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.'); | |
| 29521 } | |
| 29522 } | |
| 29523 } | |
| 29524 } | |
| 29525 | |
| 29526 var ReactCurrentOwner$3 = ReactSharedInternals.ReactCurrentOwner; | |
| 29527 var topLevelUpdateWarnings; | |
| 29528 | |
| 29529 { | |
| 29530 topLevelUpdateWarnings = function (container) { | |
| 29531 if (container._reactRootContainer && container.nodeType !== COMMENT_NODE) { | |
| 29532 var hostInstance = findHostInstanceWithNoPortals(container._reactRootContainer.current); | |
| 29533 | |
| 29534 if (hostInstance) { | |
| 29535 if (hostInstance.parentNode !== container) { | |
| 29536 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.'); | |
| 29537 } | |
| 29538 } | |
| 29539 } | |
| 29540 | |
| 29541 var isRootRenderedBySomeReact = !!container._reactRootContainer; | |
| 29542 var rootEl = getReactRootElementInContainer(container); | |
| 29543 var hasNonRootReactChild = !!(rootEl && getInstanceFromNode(rootEl)); | |
| 29544 | |
| 29545 if (hasNonRootReactChild && !isRootRenderedBySomeReact) { | |
| 29546 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.'); | |
| 29547 } | |
| 29548 | |
| 29549 if (container.nodeType === ELEMENT_NODE && container.tagName && container.tagName.toUpperCase() === 'BODY') { | |
| 29550 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.'); | |
| 29551 } | |
| 29552 }; | |
| 29553 } | |
| 29554 | |
| 29555 function getReactRootElementInContainer(container) { | |
| 29556 if (!container) { | |
| 29557 return null; | |
| 29558 } | |
| 29559 | |
| 29560 if (container.nodeType === DOCUMENT_NODE) { | |
| 29561 return container.documentElement; | |
| 29562 } else { | |
| 29563 return container.firstChild; | |
| 29564 } | |
| 29565 } | |
| 29566 | |
| 29567 function noopOnRecoverableError() {// This isn't reachable because onRecoverableError isn't called in the | |
| 29568 // legacy API. | |
| 29569 } | |
| 29570 | |
| 29571 function legacyCreateRootFromDOMContainer(container, initialChildren, parentComponent, callback, isHydrationContainer) { | |
| 29572 if (isHydrationContainer) { | |
| 29573 if (typeof callback === 'function') { | |
| 29574 var originalCallback = callback; | |
| 29575 | |
| 29576 callback = function () { | |
| 29577 var instance = getPublicRootInstance(root); | |
| 29578 originalCallback.call(instance); | |
| 29579 }; | |
| 29580 } | |
| 29581 | |
| 29582 var root = createHydrationContainer(initialChildren, callback, container, LegacyRoot, null, // hydrationCallbacks | |
| 29583 false, // isStrictMode | |
| 29584 false, // concurrentUpdatesByDefaultOverride, | |
| 29585 '', // identifierPrefix | |
| 29586 noopOnRecoverableError); | |
| 29587 container._reactRootContainer = root; | |
| 29588 markContainerAsRoot(root.current, container); | |
| 29589 var rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; | |
| 29590 listenToAllSupportedEvents(rootContainerElement); | |
| 29591 flushSync(); | |
| 29592 return root; | |
| 29593 } else { | |
| 29594 // First clear any existing content. | |
| 29595 var rootSibling; | |
| 29596 | |
| 29597 while (rootSibling = container.lastChild) { | |
| 29598 container.removeChild(rootSibling); | |
| 29599 } | |
| 29600 | |
| 29601 if (typeof callback === 'function') { | |
| 29602 var _originalCallback = callback; | |
| 29603 | |
| 29604 callback = function () { | |
| 29605 var instance = getPublicRootInstance(_root); | |
| 29606 | |
| 29607 _originalCallback.call(instance); | |
| 29608 }; | |
| 29609 } | |
| 29610 | |
| 29611 var _root = createContainer(container, LegacyRoot, null, // hydrationCallbacks | |
| 29612 false, // isStrictMode | |
| 29613 false, // concurrentUpdatesByDefaultOverride, | |
| 29614 '', // identifierPrefix | |
| 29615 noopOnRecoverableError); | |
| 29616 | |
| 29617 container._reactRootContainer = _root; | |
| 29618 markContainerAsRoot(_root.current, container); | |
| 29619 | |
| 29620 var _rootContainerElement = container.nodeType === COMMENT_NODE ? container.parentNode : container; | |
| 29621 | |
| 29622 listenToAllSupportedEvents(_rootContainerElement); // Initial mount should not be batched. | |
| 29623 | |
| 29624 flushSync(function () { | |
| 29625 updateContainer(initialChildren, _root, parentComponent, callback); | |
| 29626 }); | |
| 29627 return _root; | |
| 29628 } | |
| 29629 } | |
| 29630 | |
| 29631 function warnOnInvalidCallback$1(callback, callerName) { | |
| 29632 { | |
| 29633 if (callback !== null && typeof callback !== 'function') { | |
| 29634 error('%s(...): Expected the last optional `callback` argument to be a ' + 'function. Instead received: %s.', callerName, callback); | |
| 29635 } | |
| 29636 } | |
| 29637 } | |
| 29638 | |
| 29639 function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) { | |
| 29640 { | |
| 29641 topLevelUpdateWarnings(container); | |
| 29642 warnOnInvalidCallback$1(callback === undefined ? null : callback, 'render'); | |
| 29643 } | |
| 29644 | |
| 29645 var maybeRoot = container._reactRootContainer; | |
| 29646 var root; | |
| 29647 | |
| 29648 if (!maybeRoot) { | |
| 29649 // Initial mount | |
| 29650 root = legacyCreateRootFromDOMContainer(container, children, parentComponent, callback, forceHydrate); | |
| 29651 } else { | |
| 29652 root = maybeRoot; | |
| 29653 | |
| 29654 if (typeof callback === 'function') { | |
| 29655 var originalCallback = callback; | |
| 29656 | |
| 29657 callback = function () { | |
| 29658 var instance = getPublicRootInstance(root); | |
| 29659 originalCallback.call(instance); | |
| 29660 }; | |
| 29661 } // Update | |
| 29662 | |
| 29663 | |
| 29664 updateContainer(children, root, parentComponent, callback); | |
| 29665 } | |
| 29666 | |
| 29667 return getPublicRootInstance(root); | |
| 29668 } | |
| 29669 | |
| 29670 var didWarnAboutFindDOMNode = false; | |
| 29671 function findDOMNode(componentOrElement) { | |
| 29672 { | |
| 29673 if (!didWarnAboutFindDOMNode) { | |
| 29674 didWarnAboutFindDOMNode = true; | |
| 29675 | |
| 29676 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'); | |
| 29677 } | |
| 29678 | |
| 29679 var owner = ReactCurrentOwner$3.current; | |
| 29680 | |
| 29681 if (owner !== null && owner.stateNode !== null) { | |
| 29682 var warnedAboutRefsInRender = owner.stateNode._warnedAboutRefsInRender; | |
| 29683 | |
| 29684 if (!warnedAboutRefsInRender) { | |
| 29685 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'); | |
| 29686 } | |
| 29687 | |
| 29688 owner.stateNode._warnedAboutRefsInRender = true; | |
| 29689 } | |
| 29690 } | |
| 29691 | |
| 29692 if (componentOrElement == null) { | |
| 29693 return null; | |
| 29694 } | |
| 29695 | |
| 29696 if (componentOrElement.nodeType === ELEMENT_NODE) { | |
| 29697 return componentOrElement; | |
| 29698 } | |
| 29699 | |
| 29700 { | |
| 29701 return findHostInstanceWithWarning(componentOrElement, 'findDOMNode'); | |
| 29702 } | |
| 29703 } | |
| 29704 function hydrate(element, container, callback) { | |
| 29705 { | |
| 29706 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'); | |
| 29707 } | |
| 29708 | |
| 29709 if (!isValidContainerLegacy(container)) { | |
| 29710 throw new Error('Target container is not a DOM element.'); | |
| 29711 } | |
| 29712 | |
| 29713 { | |
| 29714 var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined; | |
| 29715 | |
| 29716 if (isModernRoot) { | |
| 29717 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)?'); | |
| 29718 } | |
| 29719 } // TODO: throw or warn if we couldn't hydrate? | |
| 29720 | |
| 29721 | |
| 29722 return legacyRenderSubtreeIntoContainer(null, element, container, true, callback); | |
| 29723 } | |
| 29724 function render(element, container, callback) { | |
| 29725 { | |
| 29726 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'); | |
| 29727 } | |
| 29728 | |
| 29729 if (!isValidContainerLegacy(container)) { | |
| 29730 throw new Error('Target container is not a DOM element.'); | |
| 29731 } | |
| 29732 | |
| 29733 { | |
| 29734 var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined; | |
| 29735 | |
| 29736 if (isModernRoot) { | |
| 29737 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)?'); | |
| 29738 } | |
| 29739 } | |
| 29740 | |
| 29741 return legacyRenderSubtreeIntoContainer(null, element, container, false, callback); | |
| 29742 } | |
| 29743 function unstable_renderSubtreeIntoContainer(parentComponent, element, containerNode, callback) { | |
| 29744 { | |
| 29745 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'); | |
| 29746 } | |
| 29747 | |
| 29748 if (!isValidContainerLegacy(containerNode)) { | |
| 29749 throw new Error('Target container is not a DOM element.'); | |
| 29750 } | |
| 29751 | |
| 29752 if (parentComponent == null || !has(parentComponent)) { | |
| 29753 throw new Error('parentComponent must be a valid React Component'); | |
| 29754 } | |
| 29755 | |
| 29756 return legacyRenderSubtreeIntoContainer(parentComponent, element, containerNode, false, callback); | |
| 29757 } | |
| 29758 var didWarnAboutUnmountComponentAtNode = false; | |
| 29759 function unmountComponentAtNode(container) { | |
| 29760 { | |
| 29761 if (!didWarnAboutUnmountComponentAtNode) { | |
| 29762 didWarnAboutUnmountComponentAtNode = true; | |
| 29763 | |
| 29764 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'); | |
| 29765 } | |
| 29766 } | |
| 29767 | |
| 29768 if (!isValidContainerLegacy(container)) { | |
| 29769 throw new Error('unmountComponentAtNode(...): Target container is not a DOM element.'); | |
| 29770 } | |
| 29771 | |
| 29772 { | |
| 29773 var isModernRoot = isContainerMarkedAsRoot(container) && container._reactRootContainer === undefined; | |
| 29774 | |
| 29775 if (isModernRoot) { | |
| 29776 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()?'); | |
| 29777 } | |
| 29778 } | |
| 29779 | |
| 29780 if (container._reactRootContainer) { | |
| 29781 { | |
| 29782 var rootEl = getReactRootElementInContainer(container); | |
| 29783 var renderedByDifferentReact = rootEl && !getInstanceFromNode(rootEl); | |
| 29784 | |
| 29785 if (renderedByDifferentReact) { | |
| 29786 error("unmountComponentAtNode(): The node you're attempting to unmount " + 'was rendered by another copy of React.'); | |
| 29787 } | |
| 29788 } // Unmount should not be batched. | |
| 29789 | |
| 29790 | |
| 29791 flushSync(function () { | |
| 29792 legacyRenderSubtreeIntoContainer(null, null, container, false, function () { | |
| 29793 // $FlowFixMe This should probably use `delete container._reactRootContainer` | |
| 29794 container._reactRootContainer = null; | |
| 29795 unmarkContainerAsRoot(container); | |
| 29796 }); | |
| 29797 }); // If you call unmountComponentAtNode twice in quick succession, you'll | |
| 29798 // get `true` twice. That's probably fine? | |
| 29799 | |
| 29800 return true; | |
| 29801 } else { | |
| 29802 { | |
| 29803 var _rootEl = getReactRootElementInContainer(container); | |
| 29804 | |
| 29805 var hasNonRootReactChild = !!(_rootEl && getInstanceFromNode(_rootEl)); // Check if the container itself is a React root node. | |
| 29806 | |
| 29807 var isContainerReactRoot = container.nodeType === ELEMENT_NODE && isValidContainerLegacy(container.parentNode) && !!container.parentNode._reactRootContainer; | |
| 29808 | |
| 29809 if (hasNonRootReactChild) { | |
| 29810 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.'); | |
| 29811 } | |
| 29812 } | |
| 29813 | |
| 29814 return false; | |
| 29815 } | |
| 29816 } | |
| 29817 | |
| 29818 setAttemptSynchronousHydration(attemptSynchronousHydration$1); | |
| 29819 setAttemptContinuousHydration(attemptContinuousHydration$1); | |
| 29820 setAttemptHydrationAtCurrentPriority(attemptHydrationAtCurrentPriority$1); | |
| 29821 setGetCurrentUpdatePriority(getCurrentUpdatePriority); | |
| 29822 setAttemptHydrationAtPriority(runWithPriority); | |
| 29823 | |
| 29824 { | |
| 29825 if (typeof Map !== 'function' || // $FlowIssue Flow incorrectly thinks Map has no prototype | |
| 29826 Map.prototype == null || typeof Map.prototype.forEach !== 'function' || typeof Set !== 'function' || // $FlowIssue Flow incorrectly thinks Set has no prototype | |
| 29827 Set.prototype == null || typeof Set.prototype.clear !== 'function' || typeof Set.prototype.forEach !== 'function') { | |
| 29828 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'); | |
| 29829 } | |
| 29830 } | |
| 29831 | |
| 29832 setRestoreImplementation(restoreControlledState$3); | |
| 29833 setBatchingImplementation(batchedUpdates$1, discreteUpdates, flushSync); | |
| 29834 | |
| 29835 function createPortal$1(children, container) { | |
| 29836 var key = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | |
| 29837 | |
| 29838 if (!isValidContainer(container)) { | |
| 29839 throw new Error('Target container is not a DOM element.'); | |
| 29840 } // TODO: pass ReactDOM portal implementation as third argument | |
| 29841 // $FlowFixMe The Flow type is opaque but there's no way to actually create it. | |
| 29842 | |
| 29843 | |
| 29844 return createPortal(children, container, null, key); | |
| 29845 } | |
| 29846 | |
| 29847 function renderSubtreeIntoContainer(parentComponent, element, containerNode, callback) { | |
| 29848 return unstable_renderSubtreeIntoContainer(parentComponent, element, containerNode, callback); | |
| 29849 } | |
| 29850 | |
| 29851 var Internals = { | |
| 29852 usingClientEntryPoint: false, | |
| 29853 // Keep in sync with ReactTestUtils.js. | |
| 29854 // This is an array for better minification. | |
| 29855 Events: [getInstanceFromNode, getNodeFromInstance, getFiberCurrentPropsFromNode, enqueueStateRestore, restoreStateIfNeeded, batchedUpdates$1] | |
| 29856 }; | |
| 29857 | |
| 29858 function createRoot$1(container, options) { | |
| 29859 { | |
| 29860 if (!Internals.usingClientEntryPoint && !true) { | |
| 29861 error('You are importing createRoot from "react-dom" which is not supported. ' + 'You should instead import it from "react-dom/client".'); | |
| 29862 } | |
| 29863 } | |
| 29864 | |
| 29865 return createRoot(container, options); | |
| 29866 } | |
| 29867 | |
| 29868 function hydrateRoot$1(container, initialChildren, options) { | |
| 29869 { | |
| 29870 if (!Internals.usingClientEntryPoint && !true) { | |
| 29871 error('You are importing hydrateRoot from "react-dom" which is not supported. ' + 'You should instead import it from "react-dom/client".'); | |
| 29872 } | |
| 29873 } | |
| 29874 | |
| 29875 return hydrateRoot(container, initialChildren, options); | |
| 29876 } // Overload the definition to the two valid signatures. | |
| 29877 // Warning, this opts-out of checking the function body. | |
| 29878 | |
| 29879 | |
| 29880 // eslint-disable-next-line no-redeclare | |
| 29881 function flushSync$1(fn) { | |
| 29882 { | |
| 29883 if (isAlreadyRendering()) { | |
| 29884 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.'); | |
| 29885 } | |
| 29886 } | |
| 29887 | |
| 29888 return flushSync(fn); | |
| 29889 } | |
| 29890 var foundDevTools = injectIntoDevTools({ | |
| 29891 findFiberByHostInstance: getClosestInstanceFromNode, | |
| 29892 bundleType: 1 , | |
| 29893 version: ReactVersion, | |
| 29894 rendererPackageName: 'react-dom' | |
| 29895 }); | |
| 29896 | |
| 29897 { | |
| 29898 if (!foundDevTools && canUseDOM && window.top === window.self) { | |
| 29899 // If we're in Chrome or Firefox, provide a download link if not installed. | |
| 29900 if (navigator.userAgent.indexOf('Chrome') > -1 && navigator.userAgent.indexOf('Edge') === -1 || navigator.userAgent.indexOf('Firefox') > -1) { | |
| 29901 var protocol = window.location.protocol; // Don't warn in exotic cases like chrome-extension://. | |
| 29902 | |
| 29903 if (/^(https?|file):$/.test(protocol)) { | |
| 29904 // eslint-disable-next-line react-internal/no-production-logging | |
| 29905 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'); | |
| 29906 } | |
| 29907 } | |
| 29908 } | |
| 29909 } | |
| 29910 | |
| 29911 exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = Internals; | |
| 29912 exports.createPortal = createPortal$1; | |
| 29913 exports.createRoot = createRoot$1; | |
| 29914 exports.findDOMNode = findDOMNode; | |
| 29915 exports.flushSync = flushSync$1; | |
| 29916 exports.hydrate = hydrate; | |
| 29917 exports.hydrateRoot = hydrateRoot$1; | |
| 29918 exports.render = render; | |
| 29919 exports.unmountComponentAtNode = unmountComponentAtNode; | |
| 29920 exports.unstable_batchedUpdates = batchedUpdates$1; | |
| 29921 exports.unstable_renderSubtreeIntoContainer = renderSubtreeIntoContainer; | |
| 29922 exports.version = ReactVersion; | |
| 29923 | |
| 29924 }))); |