From 8ae1b02343e3cd363edb634e55d9de40176af655 Mon Sep 17 00:00:00 2001 From: sebmarkbage Date: Mon, 10 Apr 2023 23:16:37 +0000 Subject: [PATCH] Diff properties in the commit phase instead of generating an update payload (#26583) This removes the concept of `prepareUpdate()`, behind a flag. React Native already does everything in the commit phase, but generates a temporary update payload before applying it. React Fabric does it both in the render phase. Now it just moves it to a single host config. For DOM I forked updateProperties into one that does diffing and updating in one pass vs just applying a pre-diffed updatePayload. There are a few downsides of this approach: - If only "children" has changed, we end up scheduling an update to be done in the commit phase. Since we traverse through it anyway, it's probably not much extra. - It does more work in the commit phase so for a large tree that is mostly unchanged, it'll stall longer. - It does some extra work for special cases since that work happens if anything has changed. We no longer have a deep bailout. - The special cases now have to each replicate the "clean up old props" loop, leading to extra code. The benefit is that this doesn't allocate temporary extra objects (possibly multiple per element if the array has to resize). It's less work overall. It also gives us an option to reuse this function for a sync render optimization. Another benefit is that if we do the loop in the commit phase I can do further optimizations by reading all props that I need for special cases in that loop instead of polymorphic reads from props. This is what I'd like to do in future refactors that would be stacked on top of this change. DiffTrain build for [ca41adb8c1b256705f73d1fb657421a03dfad82c](https://github.com/facebook/react/commit/ca41adb8c1b256705f73d1fb657421a03dfad82c) --- compiled/facebook-www/REVISION | 2 +- compiled/facebook-www/React-dev.modern.js | 2 +- compiled/facebook-www/ReactART-dev.classic.js | 30 +- compiled/facebook-www/ReactART-dev.modern.js | 30 +- .../facebook-www/ReactART-prod.classic.js | 22 +- compiled/facebook-www/ReactART-prod.modern.js | 22 +- compiled/facebook-www/ReactDOM-dev.classic.js | 808 ++++++++++-- compiled/facebook-www/ReactDOM-dev.modern.js | 810 ++++++++++-- .../facebook-www/ReactDOM-prod.classic.js | 1104 ++++++++++------ compiled/facebook-www/ReactDOM-prod.modern.js | 1094 ++++++++++------ .../ReactDOM-profiling.classic.js | 1148 +++++++++++------ .../facebook-www/ReactDOM-profiling.modern.js | 1138 ++++++++++------ .../ReactDOMTesting-dev.classic.js | 808 ++++++++++-- .../ReactDOMTesting-dev.modern.js | 810 ++++++++++-- .../ReactDOMTesting-prod.classic.js | 1083 ++++++++++------ .../ReactDOMTesting-prod.modern.js | 1094 ++++++++++------ .../ReactTestRenderer-dev.classic.js | 29 +- .../ReactTestRenderer-dev.modern.js | 29 +- 18 files changed, 7300 insertions(+), 2763 deletions(-) diff --git a/compiled/facebook-www/REVISION b/compiled/facebook-www/REVISION index 084ac17327e8d..0d19580f82bb8 100644 --- a/compiled/facebook-www/REVISION +++ b/compiled/facebook-www/REVISION @@ -1 +1 @@ -ffb8eaca5966fc7733bd0a23f4055e26d2cc59d7 +ca41adb8c1b256705f73d1fb657421a03dfad82c diff --git a/compiled/facebook-www/React-dev.modern.js b/compiled/facebook-www/React-dev.modern.js index fda9ac59f70f0..626f6e84efb1f 100644 --- a/compiled/facebook-www/React-dev.modern.js +++ b/compiled/facebook-www/React-dev.modern.js @@ -27,7 +27,7 @@ if ( } "use strict"; -var ReactVersion = "18.3.0-www-modern-0ed50ada"; +var ReactVersion = "18.3.0-www-modern-82c50b6a"; // ATTENTION // When adding new symbols to this file, diff --git a/compiled/facebook-www/ReactART-dev.classic.js b/compiled/facebook-www/ReactART-dev.classic.js index a773b0a750cfc..d9e476a2457f1 100644 --- a/compiled/facebook-www/ReactART-dev.classic.js +++ b/compiled/facebook-www/ReactART-dev.classic.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-classic-b7529c47"; +var ReactVersion = "18.3.0-www-classic-26b068f9"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -177,7 +177,8 @@ var revertRemovalOfSiblingPrerendering = enableUnifiedSyncLane = dynamicFeatureFlags.enableUnifiedSyncLane, enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing, enableDeferRootSchedulingToMicrotask = - dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask; // On WWW, false is used for a new modern build. + dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask, + diffInCommitPhase = dynamicFeatureFlags.diffInCommitPhase; // On WWW, false is used for a new modern build. var enableProfilerTimer = true; var enableProfilerCommitHooks = true; var enableProfilerNestedUpdatePhase = true; @@ -17583,18 +17584,23 @@ function updateHostComponent( // In mutation mode, this is sufficient for a bailout because // we won't touch this node even if children changed. return; - } // If we get updated because one of our children updated, we don't - getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host - // component is hitting the resume path. Figure out why. Possibly - // related to `hidden`. + } + + if (diffInCommitPhase) { + markUpdate(workInProgress); + } else { + // component is hitting the resume path. Figure out why. Possibly + // related to `hidden`. - var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. + getHostContext(); + var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. - workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. All the work is done in commitWork. + workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. All the work is done in commitWork. - if (updatePayload) { - markUpdate(workInProgress); + if (updatePayload) { + markUpdate(workInProgress); + } } } } // This function must be called at the very end of the complete phase, because @@ -21225,7 +21231,7 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { var _updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (_updatePayload !== null) { + if (_updatePayload !== null || diffInCommitPhase) { try { commitUpdate( _instance2, diff --git a/compiled/facebook-www/ReactART-dev.modern.js b/compiled/facebook-www/ReactART-dev.modern.js index 4e1c010d1ec69..5ebe69b840b6e 100644 --- a/compiled/facebook-www/ReactART-dev.modern.js +++ b/compiled/facebook-www/ReactART-dev.modern.js @@ -69,7 +69,7 @@ function _assertThisInitialized(self) { return self; } -var ReactVersion = "18.3.0-www-modern-71e9d049"; +var ReactVersion = "18.3.0-www-modern-ae1f3bca"; var LegacyRoot = 0; var ConcurrentRoot = 1; @@ -177,7 +177,8 @@ var revertRemovalOfSiblingPrerendering = enableUnifiedSyncLane = dynamicFeatureFlags.enableUnifiedSyncLane, enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing, enableDeferRootSchedulingToMicrotask = - dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask; // On WWW, true is used for a new modern build. + dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask, + diffInCommitPhase = dynamicFeatureFlags.diffInCommitPhase; // On WWW, true is used for a new modern build. var enableProfilerTimer = true; var enableProfilerCommitHooks = true; var enableProfilerNestedUpdatePhase = true; @@ -17277,18 +17278,23 @@ function updateHostComponent( // In mutation mode, this is sufficient for a bailout because // we won't touch this node even if children changed. return; - } // If we get updated because one of our children updated, we don't - getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host - // component is hitting the resume path. Figure out why. Possibly - // related to `hidden`. + } + + if (diffInCommitPhase) { + markUpdate(workInProgress); + } else { + // component is hitting the resume path. Figure out why. Possibly + // related to `hidden`. - var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. + getHostContext(); + var updatePayload = prepareUpdate(); // TODO: Type this specific to this type of component. - workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. All the work is done in commitWork. + workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. All the work is done in commitWork. - if (updatePayload) { - markUpdate(workInProgress); + if (updatePayload) { + markUpdate(workInProgress); + } } } } // This function must be called at the very end of the complete phase, because @@ -20890,7 +20896,7 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { var _updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (_updatePayload !== null) { + if (_updatePayload !== null || diffInCommitPhase) { try { commitUpdate( _instance2, diff --git a/compiled/facebook-www/ReactART-prod.classic.js b/compiled/facebook-www/ReactART-prod.classic.js index 91605ce428803..f18111ccc94e1 100644 --- a/compiled/facebook-www/ReactART-prod.classic.js +++ b/compiled/facebook-www/ReactART-prod.classic.js @@ -73,6 +73,7 @@ var ReactSharedInternals = enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing, enableDeferRootSchedulingToMicrotask = dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask, + diffInCommitPhase = dynamicFeatureFlags.diffInCommitPhase, REACT_ELEMENT_TYPE = Symbol.for("react.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), @@ -5240,6 +5241,9 @@ function getChildContextValues(context) { collectNearestChildContextValues(currentFiber, context, childContextValues); return childContextValues; } +function markUpdate(workInProgress) { + workInProgress.flags |= 4; +} function scheduleRetryEffect(workInProgress, retryQueue) { null !== retryQueue ? (workInProgress.flags |= 4) @@ -5359,8 +5363,10 @@ function completeWork(current, workInProgress, renderLanes) { renderLanes = workInProgress.type; if (null !== current && null != workInProgress.stateNode) current.memoizedProps !== newProps && - (workInProgress.updateQueue = UPDATE_SIGNAL) && - (workInProgress.flags |= 4), + (diffInCommitPhase + ? markUpdate(workInProgress) + : (workInProgress.updateQueue = UPDATE_SIGNAL) && + markUpdate(workInProgress)), current.ref !== workInProgress.ref && (workInProgress.flags |= 2097664); else { @@ -5427,7 +5433,7 @@ function completeWork(current, workInProgress, renderLanes) { return null; case 6: if (current && null != workInProgress.stateNode) - current.memoizedProps !== newProps && (workInProgress.flags |= 4); + current.memoizedProps !== newProps && markUpdate(workInProgress); else { if ("string" !== typeof newProps && null === workInProgress.stateNode) throw Error(formatProdErrorMessage(166)); @@ -5597,8 +5603,8 @@ function completeWork(current, workInProgress, renderLanes) { }), shim$1(), null !== workInProgress.ref && - ((workInProgress.flags |= 2097664), (workInProgress.flags |= 4))) - : (null !== workInProgress.ref && (workInProgress.flags |= 4), + ((workInProgress.flags |= 2097664), markUpdate(workInProgress))) + : (null !== workInProgress.ref && markUpdate(workInProgress), current.ref !== workInProgress.ref && (workInProgress.flags |= 2097664)), bubbleProperties(workInProgress), @@ -6665,7 +6671,7 @@ function commitMutationEffectsOnFiber(finishedWork, root) { current = null !== current ? current.memoizedProps : newProps; var updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (null !== updatePayload) + if (null !== updatePayload || diffInCommitPhase) try { flags._applyProps(flags, newProps, current); } catch (error$108) { @@ -10072,7 +10078,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-classic-0d8af644", + version: "18.3.0-www-classic-9e45a6d4", rendererPackageName: "react-art" }; var internals$jscomp$inline_1344 = { @@ -10103,7 +10109,7 @@ var internals$jscomp$inline_1344 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-classic-0d8af644" + reconcilerVersion: "18.3.0-www-classic-9e45a6d4" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1345 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactART-prod.modern.js b/compiled/facebook-www/ReactART-prod.modern.js index 68ce4a6507803..e0098cb8bb68f 100644 --- a/compiled/facebook-www/ReactART-prod.modern.js +++ b/compiled/facebook-www/ReactART-prod.modern.js @@ -73,6 +73,7 @@ var ReactSharedInternals = enableTransitionTracing = dynamicFeatureFlags.enableTransitionTracing, enableDeferRootSchedulingToMicrotask = dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask, + diffInCommitPhase = dynamicFeatureFlags.diffInCommitPhase, REACT_ELEMENT_TYPE = Symbol.for("react.element"), REACT_PORTAL_TYPE = Symbol.for("react.portal"), REACT_FRAGMENT_TYPE = Symbol.for("react.fragment"), @@ -4995,6 +4996,9 @@ function getChildContextValues(context) { collectNearestChildContextValues(currentFiber, context, childContextValues); return childContextValues; } +function markUpdate(workInProgress) { + workInProgress.flags |= 4; +} function scheduleRetryEffect(workInProgress, retryQueue) { null !== retryQueue ? (workInProgress.flags |= 4) @@ -5108,8 +5112,10 @@ function completeWork(current, workInProgress, renderLanes) { renderLanes = workInProgress.type; if (null !== current && null != workInProgress.stateNode) current.memoizedProps !== newProps && - (workInProgress.updateQueue = UPDATE_SIGNAL) && - (workInProgress.flags |= 4), + (diffInCommitPhase + ? markUpdate(workInProgress) + : (workInProgress.updateQueue = UPDATE_SIGNAL) && + markUpdate(workInProgress)), current.ref !== workInProgress.ref && (workInProgress.flags |= 2097664); else { @@ -5176,7 +5182,7 @@ function completeWork(current, workInProgress, renderLanes) { return null; case 6: if (current && null != workInProgress.stateNode) - current.memoizedProps !== newProps && (workInProgress.flags |= 4); + current.memoizedProps !== newProps && markUpdate(workInProgress); else { if ("string" !== typeof newProps && null === workInProgress.stateNode) throw Error(formatProdErrorMessage(166)); @@ -5342,8 +5348,8 @@ function completeWork(current, workInProgress, renderLanes) { }), shim$1(), null !== workInProgress.ref && - ((workInProgress.flags |= 2097664), (workInProgress.flags |= 4))) - : (null !== workInProgress.ref && (workInProgress.flags |= 4), + ((workInProgress.flags |= 2097664), markUpdate(workInProgress))) + : (null !== workInProgress.ref && markUpdate(workInProgress), current.ref !== workInProgress.ref && (workInProgress.flags |= 2097664)), bubbleProperties(workInProgress), @@ -6401,7 +6407,7 @@ function commitMutationEffectsOnFiber(finishedWork, root) { current = null !== current ? current.memoizedProps : newProps; var updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (null !== updatePayload) + if (null !== updatePayload || diffInCommitPhase) try { flags._applyProps(flags, newProps, current); } catch (error$107) { @@ -9737,7 +9743,7 @@ var slice = Array.prototype.slice, return null; }, bundleType: 0, - version: "18.3.0-www-modern-77b45d24", + version: "18.3.0-www-modern-f0a7ed1e", rendererPackageName: "react-art" }; var internals$jscomp$inline_1324 = { @@ -9768,7 +9774,7 @@ var internals$jscomp$inline_1324 = { scheduleRoot: null, setRefreshHandler: null, getCurrentFiber: null, - reconcilerVersion: "18.3.0-www-modern-77b45d24" + reconcilerVersion: "18.3.0-www-modern-f0a7ed1e" }; if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) { var hook$jscomp$inline_1325 = __REACT_DEVTOOLS_GLOBAL_HOOK__; diff --git a/compiled/facebook-www/ReactDOM-dev.classic.js b/compiled/facebook-www/ReactDOM-dev.classic.js index 3fe6e4a0c4b3a..7d9245d5518dd 100644 --- a/compiled/facebook-www/ReactDOM-dev.classic.js +++ b/compiled/facebook-www/ReactDOM-dev.classic.js @@ -144,7 +144,8 @@ var disableInputAttributeSyncing = enableCustomElementPropertySupport = dynamicFeatureFlags.enableCustomElementPropertySupport, enableDeferRootSchedulingToMicrotask = - dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask; // On WWW, false is used for a new modern build. + dynamicFeatureFlags.enableDeferRootSchedulingToMicrotask, + diffInCommitPhase = dynamicFeatureFlags.diffInCommitPhase; // On WWW, false is used for a new modern build. var enableProfilerTimer = true; var enableProfilerCommitHooks = true; var enableProfilerNestedUpdatePhase = true; @@ -5037,6 +5038,44 @@ function createDangerousStringForStyles(styles) { return serialized || null; } } + +function setValueForStyle(style, styleName, value) { + var isCustomProperty = styleName.indexOf("--") === 0; + + { + if (!isCustomProperty) { + warnValidStyle(styleName, value); + } + } + + if (value == null || typeof value === "boolean" || value === "") { + if (isCustomProperty) { + style.setProperty(styleName, ""); + } else if (styleName === "float") { + style.cssFloat = ""; + } else { + style[styleName] = ""; + } + } else if (isCustomProperty) { + style.setProperty(styleName, value); + } else if ( + typeof value === "number" && + value !== 0 && + !isUnitlessNumber(styleName) + ) { + style[styleName] = value + "px"; // Presumes implicit 'px' suffix for unitless numbers + } else { + if (styleName === "float") { + style.cssFloat = value; + } else { + { + checkCSSPropertyStringCoercion(value, styleName); + } + + style[styleName] = ("" + value).trim(); + } + } +} /** * Sets the value for multiple styles on a node. If a value is specified as * '' (empty string), the corresponding style property will be unset. @@ -5045,7 +5084,7 @@ function createDangerousStringForStyles(styles) { * @param {object} styles */ -function setValueForStyles(node, styles) { +function setValueForStyles(node, styles, prevStyles) { if (styles != null && typeof styles !== "object") { throw new Error( "The `style` prop expects a mapping from style properties to values, " + @@ -5064,45 +5103,44 @@ function setValueForStyles(node, styles) { var style = node.style; - for (var styleName in styles) { - if (!styles.hasOwnProperty(styleName)) { - continue; + if (diffInCommitPhase && prevStyles != null) { + { + validateShorthandPropertyCollisionInDev(prevStyles, styles); } - var value = styles[styleName]; - var isCustomProperty = styleName.indexOf("--") === 0; + for (var styleName in prevStyles) { + if ( + prevStyles.hasOwnProperty(styleName) && + (styles == null || !styles.hasOwnProperty(styleName)) + ) { + // Clear style + var isCustomProperty = styleName.indexOf("--") === 0; - { - if (!isCustomProperty) { - warnValidStyle(styleName, value); + if (isCustomProperty) { + style.setProperty(styleName, ""); + } else if (styleName === "float") { + style.cssFloat = ""; + } else { + style[styleName] = ""; + } } } - if (value == null || typeof value === "boolean" || value === "") { - if (isCustomProperty) { - style.setProperty(styleName, ""); - } else if (styleName === "float") { - style.cssFloat = ""; - } else { - style[styleName] = ""; - } - } else if (isCustomProperty) { - style.setProperty(styleName, value); - } else if ( - typeof value === "number" && - value !== 0 && - !isUnitlessNumber(styleName) - ) { - style[styleName] = value + "px"; // Presumes implicit 'px' suffix for unitless numbers - } else { - if (styleName === "float") { - style.cssFloat = value; - } else { - { - checkCSSPropertyStringCoercion(value, styleName); - } + for (var _styleName in styles) { + var value = styles[_styleName]; - style[styleName] = ("" + value).trim(); + if ( + styles.hasOwnProperty(_styleName) && + prevStyles[_styleName] !== value + ) { + setValueForStyle(style, _styleName, value); + } + } + } else { + for (var _styleName2 in styles) { + if (styles.hasOwnProperty(_styleName2)) { + var _value = styles[_styleName2]; + setValueForStyle(style, _styleName2, _value); } } } @@ -5148,19 +5186,45 @@ function expandShorthandMap(styles) { * becomes .style.fontVariant = '' */ -function validateShorthandPropertyCollisionInDev(styleUpdates, nextStyles) { +function validateShorthandPropertyCollisionInDev(prevStyles, nextStyles) { { if (!nextStyles) { return; + } // Compute the diff as it would happen elsewhere. + + var expandedUpdates = {}; + + if (prevStyles) { + for (var key in prevStyles) { + if (prevStyles.hasOwnProperty(key) && !nextStyles.hasOwnProperty(key)) { + var longhands = shorthandToLonghand[key] || [key]; + + for (var i = 0; i < longhands.length; i++) { + expandedUpdates[longhands[i]] = key; + } + } + } + } + + for (var _key in nextStyles) { + if ( + nextStyles.hasOwnProperty(_key) && + (!prevStyles || prevStyles[_key] !== nextStyles[_key]) + ) { + var _longhands = shorthandToLonghand[_key] || [_key]; + + for (var _i = 0; _i < _longhands.length; _i++) { + expandedUpdates[_longhands[_i]] = _key; + } + } } - var expandedUpdates = expandShorthandMap(styleUpdates); var expandedStyles = expandShorthandMap(nextStyles); var warnedAbout = {}; - for (var key in expandedUpdates) { - var originalKey = expandedUpdates[key]; - var correctOriginalKey = expandedStyles[key]; + for (var _key2 in expandedUpdates) { + var originalKey = expandedUpdates[_key2]; + var correctOriginalKey = expandedStyles[_key2]; if (correctOriginalKey && originalKey !== correctOriginalKey) { var warningKey = originalKey + "," + correctOriginalKey; @@ -5177,7 +5241,7 @@ function validateShorthandPropertyCollisionInDev(styleUpdates, nextStyles) { "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", + isValueEmpty(nextStyles[originalKey]) ? "Removing" : "Updating", originalKey, correctOriginalKey ); @@ -7877,11 +7941,13 @@ function prepareToHydrateHostInstance(fiber, hostContext) { shouldWarnIfMismatchDev ); // TODO: Type this specific to this type of component. - fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. + if (!diffInCommitPhase) { + fiber.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. - if (updatePayload !== null) { - return true; + if (updatePayload !== null) { + return true; + } } return false; @@ -21932,29 +21998,34 @@ function updateHostComponent( // In mutation mode, this is sufficient for a bailout because // we won't touch this node even if children changed. return; - } // If we get updated because one of our children updated, we don't - // have newProps so we'll have to reuse them. - // TODO: Split the update API as separate for the props vs. children. - // Even better would be if children weren't special cased at all tho. - - var instance = workInProgress.stateNode; - var currentHostContext = getHostContext(); // TODO: Experiencing an error where oldProps is null. Suggests a host - // component is hitting the resume path. Figure out why. Possibly - // related to `hidden`. + } - var updatePayload = prepareUpdate( - instance, - type, - oldProps, - newProps, - currentHostContext - ); // TODO: Type this specific to this type of component. + if (diffInCommitPhase) { + markUpdate(workInProgress); + } else { + // If we get updated because one of our children updated, we don't + // have newProps so we'll have to reuse them. + // TODO: Split the update API as separate for the props vs. children. + // Even better would be if children weren't special cased at all tho. + var instance = workInProgress.stateNode; // TODO: Experiencing an error where oldProps is null. Suggests a host + // component is hitting the resume path. Figure out why. Possibly + // related to `hidden`. + + var currentHostContext = getHostContext(); + var updatePayload = prepareUpdate( + instance, + type, + oldProps, + newProps, + currentHostContext + ); // TODO: Type this specific to this type of component. - workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there - // is a new ref we mark this as an update. All the work is done in commitWork. + workInProgress.updateQueue = updatePayload; // If the update payload indicates that there is a change or if there + // is a new ref we mark this as an update. All the work is done in commitWork. - if (updatePayload) { - markUpdate(workInProgress); + if (updatePayload) { + markUpdate(workInProgress); + } } } } // This function must be called at the very end of the complete phase, because @@ -26114,7 +26185,7 @@ function commitMutationEffectsOnFiber(finishedWork, root, lanes) { var _updatePayload = finishedWork.updateQueue; finishedWork.updateQueue = null; - if (_updatePayload !== null) { + if (_updatePayload !== null || diffInCommitPhase) { try { commitUpdate( _instance2, @@ -33351,7 +33422,7 @@ function createFiberRoot( return root; } -var ReactVersion = "18.3.0-www-classic-27be8f4e"; +var ReactVersion = "18.3.0-www-classic-6d02b9b0"; function createPortal$1( children, @@ -37826,7 +37897,7 @@ function trapClickOnNonInteractiveElement(node) { var xlinkNamespace = "http://www.w3.org/1999/xlink"; var xmlNamespace = "http://www.w3.org/XML/1998/namespace"; -function setProp(domElement, tag, key, value, props) { +function setProp(domElement, tag, key, value, props, prevValue) { switch (key) { case "children": { if (typeof value === "string") { @@ -37873,7 +37944,7 @@ function setProp(domElement, tag, key, value, props) { } case "style": { - setValueForStyles(domElement, value); + setValueForStyles(domElement, value, prevValue); break; } // These attributes accept URLs. These must not allow javascript: URLS. @@ -38261,6 +38332,20 @@ function setProp(domElement, tag, key, value, props) { break; // Properties that should not be allowed on custom elements. + case "is": { + { + if (prevValue != null) { + error('Cannot update the "is" prop after it has been initialized.'); + } + } // TODO: We shouldn't actually set this attribute, because we've already + // passed it to createElement. We don't also need the attribute. + // However, our tests currently query for it so it's plausible someone + // else does too so it's break. + + setValueForAttribute(domElement, "is", value); + break; + } + case "innerText": case "textContent": if (enableCustomElementPropertySupport) { @@ -38290,10 +38375,10 @@ function setProp(domElement, tag, key, value, props) { } } -function setPropOnCustomElement(domElement, tag, key, value, props) { +function setPropOnCustomElement(domElement, tag, key, value, props, prevValue) { switch (key) { case "style": { - setValueForStyles(domElement, value); + setValueForStyles(domElement, value, prevValue); break; } @@ -38485,7 +38570,7 @@ function setInitialProperties(domElement, tag, props) { // defaultChecked and defaultValue are ignored by setProp default: { - setProp(domElement, tag, propKey, propValue, props); + setProp(domElement, tag, propKey, propValue, props, null); } } } // TODO: Make sure we check if this is still unmounted or do any clean @@ -38524,7 +38609,7 @@ function setInitialProperties(domElement, tag, props) { // defaultValue are ignored by setProp default: { - setProp(domElement, tag, _propKey, _propValue, props); + setProp(domElement, tag, _propKey, _propValue, props, null); } } } @@ -38708,7 +38793,7 @@ function setInitialProperties(domElement, tag, props) { // defaultChecked and defaultValue are ignored by setProp default: { - setProp(domElement, tag, _propKey4, _propValue4, props); + setProp(domElement, tag, _propKey4, _propValue4, props, null); } } } @@ -38734,7 +38819,8 @@ function setInitialProperties(domElement, tag, props) { tag, _propKey5, _propValue5, - props + props, + null ); } @@ -38754,7 +38840,7 @@ function setInitialProperties(domElement, tag, props) { continue; } - setProp(domElement, tag, _propKey6, _propValue6, props); + setProp(domElement, tag, _propKey6, _propValue6, props, null); } } // Calculate the diff between the two objects. @@ -38871,16 +38957,514 @@ function diffProperties(domElement, tag, lastProps, nextProps) { if (styleUpdates) { { - validateShorthandPropertyCollisionInDev(styleUpdates, nextProps.style); + validateShorthandPropertyCollisionInDev(lastProps.style, nextProps.style); } (updatePayload = updatePayload || []).push("style", styleUpdates); } return updatePayload; +} +function updateProperties(domElement, tag, lastProps, nextProps) { + { + validatePropertiesInDevelopment(tag, nextProps); + } + + switch (tag) { + case "div": + case "span": + case "svg": + case "path": + case "a": + case "g": + case "p": + case "li": { + // Fast track the most common tag types + break; + } + + case "input": { + // Update checked *before* name. + // In the middle of an update, it is possible to have multiple checked. + // When a checked radio tries to change name, browser makes another radio's checked false. + if (nextProps.type === "radio" && nextProps.name != null) { + updateInputChecked(domElement, nextProps); + } + + for (var propKey in lastProps) { + var lastProp = lastProps[propKey]; + + if ( + lastProps.hasOwnProperty(propKey) && + lastProp != null && + !nextProps.hasOwnProperty(propKey) + ) { + switch (propKey) { + case "checked": { + var checked = nextProps.defaultChecked; + var inputElement = domElement; + inputElement.checked = + !!checked && + typeof checked !== "function" && + checked !== "symbol"; + break; + } + + case "value": { + // This is handled by updateWrapper below. + break; + } + // defaultChecked and defaultValue are ignored by setProp + + default: { + setProp(domElement, tag, propKey, null, nextProps, lastProp); + } + } + } + } + + for (var _propKey7 in nextProps) { + var nextProp = nextProps[_propKey7]; + var _lastProp = lastProps[_propKey7]; + + if ( + nextProps.hasOwnProperty(_propKey7) && + nextProp !== _lastProp && + (nextProp != null || _lastProp != null) + ) { + switch (_propKey7) { + case "checked": { + var _checked = + nextProp != null ? nextProp : nextProps.defaultChecked; + + var _inputElement = domElement; + _inputElement.checked = + !!_checked && + typeof _checked !== "function" && + _checked !== "symbol"; + break; + } + + case "value": { + // This is handled by updateWrapper below. + break; + } + + case "children": + case "dangerouslySetInnerHTML": { + if (nextProp != null) { + throw new Error( + tag + + " is a void element tag and must neither have `children` nor " + + "use `dangerouslySetInnerHTML`." + ); + } + + break; + } + // defaultChecked and defaultValue are ignored by setProp + + default: { + setProp( + domElement, + tag, + _propKey7, + nextProp, + nextProps, + _lastProp + ); + } + } + } + } + + { + var wasControlled = + lastProps.type === "checkbox" || lastProps.type === "radio" + ? lastProps.checked != null + : lastProps.value != null; + var isControlled = + nextProps.type === "checkbox" || nextProps.type === "radio" + ? nextProps.checked != null + : nextProps.value != null; + + if ( + !wasControlled && + isControlled && + !didWarnUncontrolledToControlled + ) { + 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" + ); + + didWarnUncontrolledToControlled = true; + } + + if ( + wasControlled && + !isControlled && + !didWarnControlledToUncontrolled + ) { + 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" + ); + + didWarnControlledToUncontrolled = true; + } + } // Update the wrapper around inputs *after* updating props. This has to + // happen after updating the rest of props. Otherwise HTML5 input validations + // raise warnings and prevent the new value from being assigned. + + updateInput(domElement, nextProps); + return; + } + + case "select": { + for (var _propKey8 in lastProps) { + var _lastProp2 = lastProps[_propKey8]; + + if ( + lastProps.hasOwnProperty(_propKey8) && + _lastProp2 != null && + !nextProps.hasOwnProperty(_propKey8) + ) { + switch (_propKey8) { + case "value": { + // This is handled by updateWrapper below. + break; + } + // defaultValue are ignored by setProp + + default: { + setProp(domElement, tag, _propKey8, null, nextProps, _lastProp2); + } + } + } + } + + for (var _propKey9 in nextProps) { + var _nextProp = nextProps[_propKey9]; + var _lastProp3 = lastProps[_propKey9]; + + if ( + nextProps.hasOwnProperty(_propKey9) && + _nextProp !== _lastProp3 && + (_nextProp != null || _lastProp3 != null) + ) { + switch (_propKey9) { + case "value": { + // This is handled by updateWrapper below. + break; + } + // defaultValue are ignored by setProp + + default: { + setProp( + domElement, + tag, + _propKey9, + _nextProp, + nextProps, + _lastProp3 + ); + } + } + } + } //