@samitouri / QOS-React / commits / 7f6201889e

Ship diffInCommitPhase (#27409)

Performance tests at Meta showed neutral results.

Sophie Alpert committed Sep 22, 2023 at 20:24 UTC 7f6201889e8e628eeb53e05d8850ddffa3c2e74a
21 files changed +53 -686
packages/react-art/src/ReactFiberConfigART.js
-6
@@ -15,10 +15,8 @@ import {DefaultEventPriority} from 'react-reconciler/src/ReactEventPriorities';
15 const pooledTransform = new Transform();
16
17 const NO_CONTEXT = {};
18 -const UPDATE_SIGNAL = {};
18 if (__DEV__) {
19 Object.freeze(NO_CONTEXT);
21 - Object.freeze(UPDATE_SIGNAL);
20 }
21
22 /** Helper Methods */
@@ -312,10 +310,6 @@ export function prepareForCommit() {
310 return null;
311 }
312
315 -export function prepareUpdate(domElement, type, oldProps, newProps) {
316 - return UPDATE_SIGNAL;
317 -}
318 -
313 export function resetAfterCommit() {
314 // Noop
315 }
packages/react-dom-bindings/src/client/CSSPropertyOperations.js
+2 -6
@@ -11,7 +11,6 @@ import hyphenateStyleName from '../shared/hyphenateStyleName';
11 import warnValidStyle from '../shared/warnValidStyle';
12 import isUnitlessNumber from '../shared/isUnitlessNumber';
13 import {checkCSSPropertyStringCoercion} from 'shared/CheckStringCoercion';
14 -import {diffInCommitPhase} from 'shared/ReactFeatureFlags';
14
15 /**
16 * Operations for dealing with CSS properties.
@@ -126,7 +125,7 @@ export function setValueForStyles(node, styles, prevStyles) {
125
126 const style = node.style;
127
129 - if (diffInCommitPhase && prevStyles != null) {
128 + if (prevStyles != null) {
129 if (__DEV__) {
130 validateShorthandPropertyCollisionInDev(prevStyles, styles);
131 }
@@ -200,10 +199,7 @@ function expandShorthandMap(styles) {
199 * {font: 'foo', fontVariant: 'bar'} -> {font: 'foo'}
200 * becomes .style.fontVariant = ''
201 */
203 -export function validateShorthandPropertyCollisionInDev(
204 - prevStyles,
205 - nextStyles,
206 -) {
202 +function validateShorthandPropertyCollisionInDev(prevStyles, nextStyles) {
203 if (__DEV__) {
204 if (!nextStyles) {
205 return;
packages/react-dom-bindings/src/client/ReactDOMComponent.js
+8 -430
@@ -55,7 +55,6 @@ import setTextContent from './setTextContent';
55 import {
56 createDangerousStringForStyles,
57 setValueForStyles,
58 - validateShorthandPropertyCollisionInDev,
58 } from './CSSPropertyOperations';
59 import {SVG_NAMESPACE, MATH_NAMESPACE} from './DOMNamespaces';
60 import isCustomElement from '../shared/isCustomElement';
@@ -74,7 +73,6 @@ import {
73 disableIEWorkarounds,
74 enableTrustedTypesIntegration,
75 enableFilterEmptyStringAttributesDOM,
77 - diffInCommitPhase,
76 } from 'shared/ReactFeatureFlags';
77 import {
78 mediaEventTypes,
@@ -1341,119 +1339,6 @@ export function setInitialProperties(
1339 }
1340 }
1341
1344 -// Calculate the diff between the two objects.
1345 -export function diffProperties(
1346 - domElement: Element,
1347 - tag: string,
1348 - lastProps: Object,
1349 - nextProps: Object,
1350 -): null | Array<mixed> {
1351 - if (__DEV__) {
1352 - validatePropertiesInDevelopment(tag, nextProps);
1353 - }
1354 -
1355 - let updatePayload: null | Array<any> = null;
1356 -
1357 - let propKey;
1358 - let styleName;
1359 - let styleUpdates = null;
1360 - for (propKey in lastProps) {
1361 - if (
1362 - nextProps.hasOwnProperty(propKey) ||
1363 - !lastProps.hasOwnProperty(propKey) ||
1364 - lastProps[propKey] == null
1365 - ) {
1366 - continue;
1367 - }
1368 - switch (propKey) {
1369 - case 'style': {
1370 - const lastStyle = lastProps[propKey];
1371 - for (styleName in lastStyle) {
1372 - if (lastStyle.hasOwnProperty(styleName)) {
1373 - if (!styleUpdates) {
1374 - styleUpdates = ({}: {[string]: $FlowFixMe});
1375 - }
1376 - styleUpdates[styleName] = '';
1377 - }
1378 - }
1379 - break;
1380 - }
1381 - default: {
1382 - // For all other deleted properties we add it to the queue. We use
1383 - // the allowed property list in the commit phase instead.
1384 - (updatePayload = updatePayload || []).push(propKey, null);
1385 - }
1386 - }
1387 - }
1388 - for (propKey in nextProps) {
1389 - const nextProp = nextProps[propKey];
1390 - const lastProp = lastProps != null ? lastProps[propKey] : undefined;
1391 - if (
1392 - nextProps.hasOwnProperty(propKey) &&
1393 - nextProp !== lastProp &&
1394 - (nextProp != null || lastProp != null)
1395 - ) {
1396 - switch (propKey) {
1397 - case 'style': {
1398 - if (lastProp) {
1399 - // Unset styles on `lastProp` but not on `nextProp`.
1400 - for (styleName in lastProp) {
1401 - if (
1402 - lastProp.hasOwnProperty(styleName) &&
1403 - (!nextProp || !nextProp.hasOwnProperty(styleName))
1404 - ) {
1405 - if (!styleUpdates) {
1406 - styleUpdates = ({}: {[string]: string});
1407 - }
1408 - styleUpdates[styleName] = '';
1409 - }
1410 - }
1411 - // Update styles that changed since `lastProp`.
1412 - for (styleName in nextProp) {
1413 - if (
1414 - nextProp.hasOwnProperty(styleName) &&
1415 - lastProp[styleName] !== nextProp[styleName]
1416 - ) {
1417 - if (!styleUpdates) {
1418 - styleUpdates = ({}: {[string]: $FlowFixMe});
1419 - }
1420 - styleUpdates[styleName] = nextProp[styleName];
1421 - }
1422 - }
1423 - } else {
1424 - // Relies on `updateStylesByID` not mutating `styleUpdates`.
1425 - if (!styleUpdates) {
1426 - if (!updatePayload) {
1427 - updatePayload = [];
1428 - }
1429 - updatePayload.push(propKey, styleUpdates);
1430 - }
1431 - styleUpdates = nextProp;
1432 - }
1433 - break;
1434 - }
1435 - case 'is':
1436 - if (__DEV__) {
1437 - console.error(
1438 - 'Cannot update the "is" prop after it has been initialized.',
1439 - );
1440 - }
1441 - // Fall through
1442 - default: {
1443 - (updatePayload = updatePayload || []).push(propKey, nextProp);
1444 - }
1445 - }
1446 - }
1447 - }
1448 - if (styleUpdates) {
1449 - if (__DEV__) {
1450 - validateShorthandPropertyCollisionInDev(lastProps.style, nextProps.style);
1451 - }
1452 - (updatePayload = updatePayload || []).push('style', styleUpdates);
1453 - }
1454 - return updatePayload;
1455 -}
1456 -
1342 export function updateProperties(
1343 domElement: Element,
1344 tag: string,
@@ -1924,305 +1809,6 @@ export function updateProperties(
1809 }
1810 }
1811
1927 -// Apply the diff.
1928 -export function updatePropertiesWithDiff(
1929 - domElement: Element,
1930 - updatePayload: Array<any>,
1931 - tag: string,
1932 - lastProps: Object,
1933 - nextProps: Object,
1934 -): void {
1935 - switch (tag) {
1936 - case 'div':
1937 - case 'span':
1938 - case 'svg':
1939 - case 'path':
1940 - case 'a':
1941 - case 'g':
1942 - case 'p':
1943 - case 'li': {
1944 - // Fast track the most common tag types
1945 - break;
1946 - }
1947 - case 'input': {
1948 - const name = nextProps.name;
1949 - const type = nextProps.type;
1950 - const value = nextProps.value;
1951 - const defaultValue = nextProps.defaultValue;
1952 - const lastDefaultValue = lastProps.defaultValue;
1953 - const checked = nextProps.checked;
1954 - const defaultChecked = nextProps.defaultChecked;
1955 - for (let i = 0; i < updatePayload.length; i += 2) {
1956 - const propKey = updatePayload[i];
1957 - const propValue = updatePayload[i + 1];
1958 - switch (propKey) {
1959 - case 'type': {
1960 - break;
1961 - }
1962 - case 'name': {
1963 - break;
1964 - }
1965 - case 'checked': {
1966 - break;
1967 - }
1968 - case 'defaultChecked': {
1969 - break;
1970 - }
1971 - case 'value': {
1972 - break;
1973 - }
1974 - case 'defaultValue': {
1975 - break;
1976 - }
1977 - case 'children':
1978 - case 'dangerouslySetInnerHTML': {
1979 - if (propValue != null) {
1980 - throw new Error(
1981 - `${tag} is a void element tag and must neither have \`children\` nor ` +
1982 - 'use `dangerouslySetInnerHTML`.',
1983 - );
1984 - }
1985 - break;
1986 - }
1987 - default: {
1988 - setProp(
1989 - domElement,
1990 - tag,
1991 - propKey,
1992 - propValue,
1993 - nextProps,
1994 - lastProps[propKey],
1995 - );
1996 - }
1997 - }
1998 - }
1999 -
2000 - if (__DEV__) {
2001 - const wasControlled =
2002 - lastProps.type === 'checkbox' || lastProps.type === 'radio'
2003 - ? lastProps.checked != null
2004 - : lastProps.value != null;
2005 - const isControlled =
2006 - nextProps.type === 'checkbox' || nextProps.type === 'radio'
2007 - ? nextProps.checked != null
2008 - : nextProps.value != null;
2009 -
2010 - if (
2011 - !wasControlled &&
2012 - isControlled &&
2013 - !didWarnUncontrolledToControlled
2014 - ) {
2015 - console.error(
2016 - 'A component is changing an uncontrolled input to be controlled. ' +
2017 - 'This is likely caused by the value changing from undefined to ' +
2018 - 'a defined value, which should not happen. ' +
2019 - 'Decide between using a controlled or uncontrolled input ' +
2020 - 'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components',
2021 - );
2022 - didWarnUncontrolledToControlled = true;
2023 - }
2024 - if (
2025 - wasControlled &&
2026 - !isControlled &&
2027 - !didWarnControlledToUncontrolled
2028 - ) {
2029 - console.error(
2030 - 'A component is changing a controlled input to be uncontrolled. ' +
2031 - 'This is likely caused by the value changing from a defined to ' +
2032 - 'undefined, which should not happen. ' +
2033 - 'Decide between using a controlled or uncontrolled input ' +
2034 - 'element for the lifetime of the component. More info: https://reactjs.org/link/controlled-components',
2035 - );
2036 - didWarnControlledToUncontrolled = true;
2037 - }
2038 - }
2039 -
2040 - // Update the wrapper around inputs *after* updating props. This has to
2041 - // happen after updating the rest of props. Otherwise HTML5 input validations
2042 - // raise warnings and prevent the new value from being assigned.
2043 - updateInput(
2044 - domElement,
2045 - value,
2046 - defaultValue,
2047 - lastDefaultValue,
2048 - checked,
2049 - defaultChecked,
2050 - type,
2051 - name,
2052 - );
2053 - return;
2054 - }
2055 - case 'select': {
2056 - const value = nextProps.value;
2057 - const defaultValue = nextProps.defaultValue;
2058 - const multiple = nextProps.multiple;
2059 - const wasMultiple = lastProps.multiple;
2060 - for (let i = 0; i < updatePayload.length; i += 2) {
2061 - const propKey = updatePayload[i];
2062 - const propValue = updatePayload[i + 1];
2063 - switch (propKey) {
2064 - case 'value': {
2065 - // This is handled by updateWrapper below.
2066 - break;
2067 - }
2068 - // defaultValue are ignored by setProp
2069 - default: {
2070 - setProp(
2071 - domElement,
2072 - tag,
2073 - propKey,
2074 - propValue,
2075 - nextProps,
2076 - lastProps[propKey],
2077 - );
2078 - }
2079 - }
2080 - }
2081 - // <select> value update needs to occur after <option> children
2082 - // reconciliation
2083 - updateSelect(domElement, value, defaultValue, multiple, wasMultiple);
2084 - return;
2085 - }
2086 - case 'textarea': {
2087 - const value = nextProps.value;
2088 - const defaultValue = nextProps.defaultValue;
2089 - for (let i = 0; i < updatePayload.length; i += 2) {
2090 - const propKey = updatePayload[i];
2091 - const propValue = updatePayload[i + 1];
2092 - switch (propKey) {
2093 - case 'value': {
2094 - // This is handled by updateWrapper below.
2095 - break;
2096 - }
2097 - case 'children': {
2098 - // TODO: This doesn't actually do anything if it updates.
2099 - break;
2100 - }
2101 - case 'dangerouslySetInnerHTML': {
2102 - if (propValue != null) {
2103 - // TODO: Do we really need a special error message for this. It's also pretty blunt.
2104 - throw new Error(
2105 - '`dangerouslySetInnerHTML` does not make sense on <textarea>.',
2106 - );
2107 - }
2108 - break;
2109 - }
2110 - // defaultValue is ignored by setProp
2111 - default: {
2112 - setProp(
2113 - domElement,
2114 - tag,
2115 - propKey,
2116 - propValue,
2117 - nextProps,
2118 - lastProps[propKey],
2119 - );
2120 - }
2121 - }
2122 - }
2123 - updateTextarea(domElement, value, defaultValue);
2124 - return;
2125 - }
2126 - case 'option': {
2127 - for (let i = 0; i < updatePayload.length; i += 2) {
2128 - const propKey = updatePayload[i];
2129 - const propValue = updatePayload[i + 1];
2130 - switch (propKey) {
2131 - case 'selected': {
2132 - // TODO: Remove support for selected on option.
2133 - (domElement: any).selected =
2134 - propValue &&
2135 - typeof propValue !== 'function' &&
2136 - typeof propValue !== 'symbol';
2137 - break;
2138 - }
2139 - default: {
2140 - setProp(
2141 - domElement,
2142 - tag,
2143 - propKey,
2144 - propValue,
2145 - nextProps,
2146 - lastProps[propKey],
2147 - );
2148 - }
2149 - }
2150 - }
2151 - return;
2152 - }
2153 - case 'img':
2154 - case 'link':
2155 - case 'area':
2156 - case 'base':
2157 - case 'br':
2158 - case 'col':
2159 - case 'embed':
2160 - case 'hr':
2161 - case 'keygen':
2162 - case 'meta':
2163 - case 'param':
2164 - case 'source':
2165 - case 'track':
2166 - case 'wbr':
2167 - case 'menuitem': {
2168 - // Void elements
2169 - for (let i = 0; i < updatePayload.length; i += 2) {
2170 - const propKey = updatePayload[i];
2171 - const propValue = updatePayload[i + 1];
2172 - switch (propKey) {
2173 - case 'children':
2174 - case 'dangerouslySetInnerHTML': {
2175 - if (propValue != null) {
2176 - // TODO: Can we make this a DEV warning to avoid this deny list?
2177 - throw new Error(
2178 - `${tag} is a void element tag and must neither have \`children\` nor ` +
2179 - 'use `dangerouslySetInnerHTML`.',
2180 - );
2181 - }
2182 - break;
2183 - }
2184 - // defaultChecked and defaultValue are ignored by setProp
2185 - default: {
2186 - setProp(
2187 - domElement,
2188 - tag,
2189 - propKey,
2190 - propValue,
2191 - nextProps,
2192 - lastProps[propKey],
2193 - );
2194 - }
2195 - }
2196 - }
2197 - return;
2198 - }
2199 - default: {
2200 - if (isCustomElement(tag, nextProps)) {
2201 - for (let i = 0; i < updatePayload.length; i += 2) {
2202 - const propKey = updatePayload[i];
2203 - const propValue = updatePayload[i + 1];
2204 - setPropOnCustomElement(
2205 - domElement,
2206 - tag,
2207 - propKey,
2208 - propValue,
2209 - nextProps,
2210 - lastProps[propKey],
2211 - );
2212 - }
2213 - return;
2214 - }
2215 - }
2216 - }
2217 -
2218 - // Apply the diff.
2219 - for (let i = 0; i < updatePayload.length; i += 2) {
2220 - const propKey = updatePayload[i];
2221 - const propValue = updatePayload[i + 1];
2222 - setProp(domElement, tag, propKey, propValue, nextProps, lastProps[propKey]);
2223 - }
2224 -}
2225 -
1812 function getPossibleStandardName(propName: string): string | null {
1813 if (__DEV__) {
1814 const lowerCasedName = propName.toLowerCase();
@@ -3094,7 +2680,7 @@ export function diffHydratedProperties(
2680 isConcurrentMode: boolean,
2681 shouldWarnDev: boolean,
2682 hostContext: HostContext,
3097 -): null | Array<mixed> {
2683 +): void {
2684 if (__DEV__) {
2685 validatePropertiesInDevelopment(tag, props);
2686 }
@@ -3192,8 +2778,6 @@ export function diffHydratedProperties(
2778 break;
2779 }
2780
3195 - let updatePayload = null;
3196 -
2781 const children = props.children;
2782 // For text content children we compare against textContent. This
2783 // might match additional HTML that is hidden when we read it using
@@ -3215,17 +2799,13 @@ export function diffHydratedProperties(
2799 );
2800 }
2801 if (!isConcurrentMode || !enableClientRenderFallbackOnTextMismatch) {
3218 - if (diffInCommitPhase) {
3219 - // We really should be patching this in the commit phase but since
3220 - // this only affects legacy mode hydration which is deprecated anyway
3221 - // we can get away with it.
3222 - // Host singletons get their children appended and don't use the text
3223 - // content mechanism.
3224 - if (!enableHostSingletons || tag !== 'body') {
3225 - domElement.textContent = (children: any);
3226 - }
3227 - } else {
3228 - updatePayload = ['children', children];
2802 + // We really should be patching this in the commit phase but since
2803 + // this only affects legacy mode hydration which is deprecated anyway
2804 + // we can get away with it.
2805 + // Host singletons get their children appended and don't use the text
2806 + // content mechanism.
2807 + if (!enableHostSingletons || tag !== 'body') {
2808 + domElement.textContent = (children: any);
2809 }
2810 }
2811 }
@@ -3281,8 +2861,6 @@ export function diffHydratedProperties(
2861 warnForExtraAttributes(extraAttributes);
2862 }
2863 }
3284 -
3285 - return updatePayload;
2864 }
2865
2866 export function diffHydratedText(
packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js
+4 -32
@@ -54,9 +54,7 @@ export {detachDeletedInstance};
54 import {hasRole} from './DOMAccessibilityRoles';
55 import {
56 setInitialProperties,
57 - diffProperties,
57 updateProperties,
59 - updatePropertiesWithDiff,
58 diffHydratedProperties,
59 diffHydratedText,
60 trapClickOnNonInteractiveElement,
@@ -96,7 +94,6 @@ import {
94 enableFloat,
95 enableHostSingletons,
96 enableTrustedTypesIntegration,
99 - diffInCommitPhase,
97 enableFormActions,
98 enableAsyncActions,
99 } from 'shared/ReactFeatureFlags';
@@ -546,20 +543,6 @@ export function finalizeInitialChildren(
543 }
544 }
545
549 -export function prepareUpdate(
550 - domElement: Instance,
551 - type: string,
552 - oldProps: Props,
553 - newProps: Props,
554 - hostContext: HostContext,
555 -): null | Array<mixed> {
556 - if (diffInCommitPhase) {
557 - // TODO: Figure out how to validateDOMNesting when children turn into a string.
558 - return null;
559 - }
560 - return diffProperties(domElement, type, oldProps, newProps);
561 -}
562 -
546 export function shouldSetTextContent(type: string, props: Props): boolean {
547 return (
548 type === 'textarea' ||
@@ -712,19 +695,8 @@ export function commitUpdate(
695 newProps: Props,
696 internalInstanceHandle: Object,
697 ): void {
715 - if (diffInCommitPhase) {
716 - // Diff and update the properties.
717 - updateProperties(domElement, type, oldProps, newProps);
718 - } else {
719 - // Apply the diff to the DOM node.
720 - updatePropertiesWithDiff(
721 - domElement,
722 - updatePayload,
723 - type,
724 - oldProps,
725 - newProps,
726 - );
727 - }
698 + // Diff and update the properties.
699 + updateProperties(domElement, type, oldProps, newProps);
700
701 // Update the props handle so that we know which props are the ones with
702 // with current event handlers.
@@ -1384,7 +1356,7 @@ export function hydrateInstance(
1356 hostContext: HostContext,
1357 internalInstanceHandle: Object,
1358 shouldWarnDev: boolean,
1387 -): null | Array<mixed> {
1359 +): void {
1360 precacheFiberNode(internalInstanceHandle, instance);
1361 // TODO: Possibly defer this until the commit phase where all the events
1362 // get attached.
@@ -1395,7 +1367,7 @@ export function hydrateInstance(
1367 const isConcurrentMode =
1368 ((internalInstanceHandle: Fiber).mode & ConcurrentMode) !== NoMode;
1369
1398 - return diffHydratedProperties(
1370 + diffHydratedProperties(
1371 instance,
1372 type,
1373 props,
packages/react-native-renderer/src/ReactFiberConfigFabric.js
+8 -37
@@ -34,7 +34,6 @@ import {
34
35 const {
36 createNode,
37 - cloneNode,
37 cloneNodeWithNewChildren,
38 cloneNodeWithNewChildrenAndProps,
39 cloneNodeWithNewProps,
@@ -48,8 +47,6 @@ const {
47 unstable_getCurrentEventPriority: fabricGetCurrentEventPriority,
48 } = nativeFabricUIManager;
49
51 -import {diffInCommitPhase} from 'shared/ReactFeatureFlags';
52 -
50 const {get: getViewConfigForType} = ReactNativeViewConfigRegistry;
51
52 // Counter for uniquely identifying views.
@@ -291,25 +288,6 @@ export function prepareForCommit(containerInfo: Container): null | Object {
288 return null;
289 }
290
294 -export function prepareUpdate(
295 - instance: Instance,
296 - type: string,
297 - oldProps: Props,
298 - newProps: Props,
299 - hostContext: HostContext,
300 -): null | Object {
301 - if (diffInCommitPhase) {
302 - return null;
303 - }
304 - const viewConfig = instance.canonical.viewConfig;
305 - const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
306 - // TODO: If the event handlers have changed, we need to update the current props
307 - // in the commit phase but there is no host config hook to do it yet.
308 - // So instead we hack it by updating it in the render phase.
309 - instance.canonical.currentProps = newProps;
310 - return updatePayload;
311 -}
312 -
291 export function resetAfterCommit(containerInfo: Container): void {
292 // Noop
293 }
@@ -364,7 +342,6 @@ export const supportsPersistence = true;
342
343 export function cloneInstance(
344 instance: Instance,
367 - updatePayload: null | Object,
345 type: string,
346 oldProps: Props,
347 newProps: Props,
@@ -372,14 +349,12 @@ export function cloneInstance(
349 keepChildren: boolean,
350 recyclableInstance: null | Instance,
351 ): Instance {
375 - if (diffInCommitPhase) {
376 - const viewConfig = instance.canonical.viewConfig;
377 - updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
378 - // TODO: If the event handlers have changed, we need to update the current props
379 - // in the commit phase but there is no host config hook to do it yet.
380 - // So instead we hack it by updating it in the render phase.
381 - instance.canonical.currentProps = newProps;
382 - }
352 + const viewConfig = instance.canonical.viewConfig;
353 + const updatePayload = diff(oldProps, newProps, viewConfig.validAttributes);
354 + // TODO: If the event handlers have changed, we need to update the current props
355 + // in the commit phase but there is no host config hook to do it yet.
356 + // So instead we hack it by updating it in the render phase.
357 + instance.canonical.currentProps = newProps;
358
359 const node = instance.node;
360 let clone;
@@ -387,12 +362,8 @@ export function cloneInstance(
362 if (updatePayload !== null) {
363 clone = cloneNodeWithNewProps(node, updatePayload);
364 } else {
390 - if (diffInCommitPhase) {
391 - // No changes
392 - return instance;
393 - } else {
394 - clone = cloneNode(node);
395 - }
365 + // No changes
366 + return instance;
367 }
368 } else {
369 if (updatePayload !== null) {
packages/react-native-renderer/src/ReactFiberConfigNative.js
-15
@@ -61,11 +61,6 @@ export type RendererInspectionConfig = $ReadOnly<{
61 ) => void,
62 }>;
63
64 -const UPDATE_SIGNAL = {};
65 -if (__DEV__) {
66 - Object.freeze(UPDATE_SIGNAL);
67 -}
68 -
64 // Counter for uniquely identifying views.
65 // % 10 === 1 means it is a rootTag.
66 // % 2 === 0 means it is a Fabric tag.
@@ -237,16 +232,6 @@ export function prepareForCommit(containerInfo: Container): null | Object {
232 return null;
233 }
234
240 -export function prepareUpdate(
241 - instance: Instance,
242 - type: string,
243 - oldProps: Props,
244 - newProps: Props,
245 - hostContext: HostContext,
246 -): null | Object {
247 - return UPDATE_SIGNAL;
248 -}
249 -
235 export function resetAfterCommit(containerInfo: Container): void {
236 // Noop
237 }
packages/react-noop-renderer/src/createReactNoop.js
-19
@@ -82,10 +82,8 @@ export type TransitionStatus = mixed;
82
83 const NO_CONTEXT = {};
84 const UPPERCASE_CONTEXT = {};
85 -const UPDATE_SIGNAL = {};
85 if (__DEV__) {
86 Object.freeze(NO_CONTEXT);
88 - Object.freeze(UPDATE_SIGNAL);
87 }
88
89 function createReactNoop(reconciler: Function, useMutation: boolean) {
@@ -222,7 +220,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
220
221 function cloneInstance(
222 instance: Instance,
225 - updatePayload: null | Object,
223 type: string,
224 oldProps: Props,
225 newProps: Props,
@@ -453,21 +450,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
450 return false;
451 },
452
456 - prepareUpdate(
457 - instance: Instance,
458 - type: string,
459 - oldProps: Props,
460 - newProps: Props,
461 - ): null | {...} {
462 - if (oldProps === null) {
463 - throw new Error('Should have old props');
464 - }
465 - if (newProps === null) {
466 - throw new Error('Should have new props');
467 - }
468 - return UPDATE_SIGNAL;
469 - },
470 -
453 shouldSetTextContent,
454
455 createTextInstance(
@@ -764,7 +746,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
746 ): Instance {
747 const clone = cloneInstance(
748 instance,
767 - null,
749 type,
750 props,
751 props,
packages/react-reconciler/README.md
+2 -10
@@ -129,14 +129,6 @@ There is a second purpose to this method. It lets you specify whether there is s
129
130 If you don't want to do anything here, you should return `false`.
131
132 -#### `prepareUpdate(instance, type, oldProps, newProps, rootContainer, hostContext)`
133 -
134 -React calls this method so that you can compare the previous and the next props, and decide whether you need to update the underlying instance or not. If you don't need to update it, return `null`. If you need to update it, you can return an arbitrary object representing the changes that need to happen. Then in `commitUpdate` you would need to apply those changes to the instance.
135 -
136 -This method happens **in the render phase**. It should only *calculate* the update — but not apply it! For example, the DOM renderer returns an array that looks like `[prop1, value1, prop2, value2, ...]` for all props that have actually changed. And only in `commitUpdate` it applies those changes. You should calculate as much as you can in `prepareUpdate` so that `commitUpdate` can be very fast and straightforward.
137 -
138 -See the meaning of `rootContainer` and `hostContext` in the `createInstance` documentation.
139 -
132 #### `shouldSetTextContent(type, props)`
133
134 Some target platforms support setting an instance's text content without manually creating a text node. For example, in the DOM, you can set `node.textContent` instead of creating a text node and appending it.
@@ -300,9 +292,9 @@ The `internalHandle` data structure is meant to be opaque. If you bend the rules
292
293 If you never return `true` from `finalizeInitialChildren`, you can leave it empty.
294
303 -#### `commitUpdate(instance, updatePayload, type, prevProps, nextProps, internalHandle)`
295 +#### `commitUpdate(instance, type, prevProps, nextProps, internalHandle)`
296
305 -This method should mutate the `instance` according to the set of changes in `updatePayload`. Here, `updatePayload` is the object that you've returned from `prepareUpdate` and has an arbitrary structure that makes sense for your renderer. For example, the DOM renderer returns an update payload like `[prop1, value1, prop2, value2, ...]` from `prepareUpdate`, and that structure gets passed into `commitUpdate`. Ideally, all the diffing and calculation should happen inside `prepareUpdate` so that `commitUpdate` can be fast and straightforward.
297 +This method should mutate the `instance` to match `nextProps`.
298
299 The `internalHandle` data structure is meant to be opaque. If you bend the rules and rely on its internal fields, be aware that it may change significantly between versions. You're taking on additional maintenance risk by reading from it, and giving up all guarantees if you write something to it.
300
packages/react-reconciler/src/ReactFiberCommitWork.js
+22 -35
@@ -54,7 +54,6 @@ import {
54 enableFloat,
55 enableLegacyHidden,
56 enableHostSingletons,
57 - diffInCommitPhase,
57 alwaysThrottleRetries,
58 } from 'shared/ReactFeatureFlags';
59 import {
@@ -2693,23 +2692,17 @@ function commitMutationEffectsOnFiber(
2692 const updatePayload: null | UpdatePayload =
2693 (finishedWork.updateQueue: any);
2694 finishedWork.updateQueue = null;
2696 - if (updatePayload !== null || diffInCommitPhase) {
2697 - try {
2698 - commitUpdate(
2699 - finishedWork.stateNode,
2700 - updatePayload,
2701 - finishedWork.type,
2702 - current.memoizedProps,
2703 - finishedWork.memoizedProps,
2704 - finishedWork,
2705 - );
2706 - } catch (error) {
2707 - captureCommitPhaseError(
2708 - finishedWork,
2709 - finishedWork.return,
2710 - error,
2711 - );
2712 - }
2695 + try {
2696 + commitUpdate(
2697 + finishedWork.stateNode,
2698 + updatePayload,
2699 + finishedWork.type,
2700 + current.memoizedProps,
2701 + finishedWork.memoizedProps,
2702 + finishedWork,
2703 + );
2704 + } catch (error) {
2705 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2706 }
2707 }
2708 }
@@ -2777,23 +2770,17 @@ function commitMutationEffectsOnFiber(
2770 const updatePayload: null | UpdatePayload =
2771 (finishedWork.updateQueue: any);
2772 finishedWork.updateQueue = null;
2780 - if (updatePayload !== null || diffInCommitPhase) {
2781 - try {
2782 - commitUpdate(
2783 - instance,
2784 - updatePayload,
2785 - type,
2786 - oldProps,
2787 - newProps,
2788 - finishedWork,
2789 - );
2790 - } catch (error) {
2791 - captureCommitPhaseError(
2792 - finishedWork,
2793 - finishedWork.return,
2794 - error,
2795 - );
2796 - }
2773 + try {
2774 + commitUpdate(
2775 + instance,
2776 + updatePayload,
2777 + type,
2778 + oldProps,
2779 + newProps,
2780 + finishedWork,
2781 + );
2782 + } catch (error) {
2783 + captureCommitPhaseError(finishedWork, finishedWork.return, error);
2784 }
2785 }
2786 }
packages/react-reconciler/src/ReactFiberCompleteWork.js
+5 -58
@@ -38,7 +38,6 @@ import {
38 enableCache,
39 enableTransitionTracing,
40 enableFloat,
41 - diffInCommitPhase,
41 } from 'shared/ReactFeatureFlags';
42
43 import {now} from './Scheduler';
@@ -97,7 +96,6 @@ import {
96 resolveSingletonInstance,
97 appendInitialChild,
98 finalizeInitialChildren,
100 - prepareUpdate,
99 supportsMutation,
100 supportsPersistence,
101 supportsResources,
@@ -431,33 +429,7 @@ function updateHostComponent(
429 return;
430 }
431
434 - if (diffInCommitPhase) {
435 - markUpdate(workInProgress);
436 - } else {
437 - // If we get updated because one of our children updated, we don't
438 - // have newProps so we'll have to reuse them.
439 - // TODO: Split the update API as separate for the props vs. children.
440 - // Even better would be if children weren't special cased at all tho.
441 - const instance: Instance = workInProgress.stateNode;
442 - // TODO: Experiencing an error where oldProps is null. Suggests a host
443 - // component is hitting the resume path. Figure out why. Possibly
444 - // related to `hidden`.
445 - const currentHostContext = getHostContext();
446 - const updatePayload = prepareUpdate(
447 - instance,
448 - type,
449 - oldProps,
450 - newProps,
451 - currentHostContext,
452 - );
453 - // TODO: Type this specific to this type of component.
454 - workInProgress.updateQueue = (updatePayload: any);
455 - // If the update payload indicates that there is a change or if there
456 - // is a new ref we mark this as an update. All the work is done in commitWork.
457 - if (updatePayload) {
458 - markUpdate(workInProgress);
459 - }
460 - }
432 + markUpdate(workInProgress);
433 } else if (supportsPersistence) {
434 const currentInstance = current.stateNode;
435 const oldProps = current.memoizedProps;
@@ -472,27 +444,8 @@ function updateHostComponent(
444 }
445 const recyclableInstance: Instance = workInProgress.stateNode;
446 const currentHostContext = getHostContext();
475 - let updatePayload = null;
476 - if (!diffInCommitPhase) {
477 - if (oldProps !== newProps) {
478 - updatePayload = prepareUpdate(
479 - recyclableInstance,
480 - type,
481 - oldProps,
482 - newProps,
483 - currentHostContext,
484 - );
485 - }
486 - if (childrenUnchanged && updatePayload === null) {
487 - // No changes, just reuse the existing instance.
488 - // Note that this might release a previous clone.
489 - workInProgress.stateNode = currentInstance;
490 - return;
491 - }
492 - }
447 const newInstance = cloneInstance(
448 currentInstance,
495 - updatePayload,
449 type,
450 oldProps,
451 newProps,
@@ -500,7 +453,7 @@ function updateHostComponent(
453 childrenUnchanged,
454 recyclableInstance,
455 );
503 - if (diffInCommitPhase && newInstance === currentInstance) {
456 + if (newInstance === currentInstance) {
457 // No changes, just reuse the existing instance.
458 // Note that this might release a previous clone.
459 workInProgress.stateNode = currentInstance;
@@ -1158,7 +1111,7 @@ function completeWork(
1111 } else {
1112 // This is a Hoistable Instance
1113 // We may have props to update on the Hoistable instance.
1161 - if (diffInCommitPhase && supportsMutation) {
1114 + if (supportsMutation) {
1115 const oldProps = current.memoizedProps;
1116 if (oldProps !== newProps) {
1117 markUpdate(workInProgress);
@@ -1195,7 +1148,7 @@ function completeWork(
1148 const rootContainerInstance = getRootHostContainer();
1149 const type = workInProgress.type;
1150 if (current !== null && workInProgress.stateNode != null) {
1198 - if (diffInCommitPhase && supportsMutation) {
1151 + if (supportsMutation) {
1152 const oldProps = current.memoizedProps;
1153 if (oldProps !== newProps) {
1154 markUpdate(workInProgress);
@@ -1296,13 +1249,7 @@ function completeWork(
1249 if (wasHydrated) {
1250 // TODO: Move this and createInstance step into the beginPhase
1251 // to consolidate.
1299 - if (
1300 - prepareToHydrateHostInstance(workInProgress, currentHostContext)
1301 - ) {
1302 - // If changes to the hydrated node need to be applied at the
1303 - // commit-phase we mark this as such.
1304 - markUpdate(workInProgress);
1305 - }
1252 + prepareToHydrateHostInstance(workInProgress, currentHostContext);
1253 } else {
1254 const rootContainerInstance = getRootHostContainer();
1255 const instance = createInstance(
packages/react-reconciler/src/ReactFiberHydrationContext.js
+2 -14
@@ -38,7 +38,6 @@ import {
38 import {
39 enableHostSingletons,
40 enableClientRenderFallbackOnTextMismatch,
41 - diffInCommitPhase,
41 } from 'shared/ReactFeatureFlags';
42
43 import {
@@ -628,7 +627,7 @@ export function tryToClaimNextHydratableFormMarkerInstance(
627 function prepareToHydrateHostInstance(
628 fiber: Fiber,
629 hostContext: HostContext,
631 -): boolean {
630 +): void {
631 if (!supportsHydration) {
632 throw new Error(
633 'Expected prepareToHydrateHostInstance() to never be called. ' +
@@ -638,7 +637,7 @@ function prepareToHydrateHostInstance(
637
638 const instance: Instance = fiber.stateNode;
639 const shouldWarnIfMismatchDev = !didSuspendOrErrorDEV;
641 - const updatePayload = hydrateInstance(
640 + hydrateInstance(
641 instance,
642 fiber.type,
643 fiber.memoizedProps,
@@ -646,17 +645,6 @@ function prepareToHydrateHostInstance(
645 fiber,
646 shouldWarnIfMismatchDev,
647 );
649 -
650 - // TODO: Type this specific to this type of component.
651 - if (!diffInCommitPhase) {
652 - fiber.updateQueue = (updatePayload: any);
653 - // If the update payload indicates that there is a change or if there
654 - // is a new ref we mark this as an update.
655 - if (updatePayload !== null) {
656 - return true;
657 - }
658 - }
659 - return false;
648 }
649
650 function prepareToHydrateHostTextInstance(fiber: Fiber): boolean {
packages/react-reconciler/src/forks/ReactFiberConfig.custom.js
-1
@@ -49,7 +49,6 @@ export const resetAfterCommit = $$$config.resetAfterCommit;
49 export const createInstance = $$$config.createInstance;
50 export const appendInitialChild = $$$config.appendInitialChild;
51 export const finalizeInitialChildren = $$$config.finalizeInitialChildren;
52 -export const prepareUpdate = $$$config.prepareUpdate;
52 export const shouldSetTextContent = $$$config.shouldSetTextContent;
53 export const createTextInstance = $$$config.createTextInstance;
54 export const scheduleTimeout = $$$config.scheduleTimeout;
packages/react-test-renderer/src/ReactFiberConfigTestHost.js
-13
@@ -54,12 +54,10 @@ export * from 'react-reconciler/src/ReactFiberConfigWithNoResources';
54 export * from 'react-reconciler/src/ReactFiberConfigWithNoSingletons';
55
56 const NO_CONTEXT = {};
57 -const UPDATE_SIGNAL = {};
57 const nodeToInstanceMap = new WeakMap<any, Instance>();
58
59 if (__DEV__) {
60 Object.freeze(NO_CONTEXT);
62 - Object.freeze(UPDATE_SIGNAL);
61 }
62
63 export function getPublicInstance(inst: Instance | TextInstance): $FlowFixMe {
@@ -186,17 +184,6 @@ export function finalizeInitialChildren(
184 return false;
185 }
186
189 -export function prepareUpdate(
190 - testElement: Instance,
191 - type: string,
192 - oldProps: Props,
193 - newProps: Props,
194 - rootContainerInstance: Container,
195 - hostContext: Object,
196 -): null | {...} {
197 - return UPDATE_SIGNAL;
198 -}
199 -
187 export function shouldSetTextContent(type: string, props: Props): boolean {
188 return false;
189 }
packages/shared/ReactFeatureFlags.js
-3
@@ -118,9 +118,6 @@ export const enableUseEffectEventHook = __EXPERIMENTAL__;
118 // (handled with an MutationObserver) instead of inline-scripts
119 export const enableFizzExternalRuntime = true;
120
121 -// Performance related test
122 -export const diffInCommitPhase = __EXPERIMENTAL__;
123 -
121 export const enableAsyncActions = __EXPERIMENTAL__;
122
123 export const alwaysThrottleRetries = true;
packages/shared/forks/ReactFeatureFlags.native-fb.js
-1
@@ -85,7 +85,6 @@ export const useModernStrictMode = false;
85 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
86 export const enableFizzExternalRuntime = false;
87
88 -export const diffInCommitPhase = true;
88 export const enableAsyncActions = false;
89
90 // Flow magic to verify the exports of this file match the original version.
packages/shared/forks/ReactFeatureFlags.native-oss.js
-1
@@ -73,7 +73,6 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
73 export const enableFizzExternalRuntime = false;
74 export const enableDeferRootSchedulingToMicrotask = true;
75
76 -export const diffInCommitPhase = true;
76 export const enableAsyncActions = false;
77
78 export const alwaysThrottleRetries = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.js
-1
@@ -73,7 +73,6 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
73 export const enableFizzExternalRuntime = false;
74 export const enableDeferRootSchedulingToMicrotask = true;
75
76 -export const diffInCommitPhase = true;
76 export const enableAsyncActions = false;
77
78 export const alwaysThrottleRetries = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.native.js
-1
@@ -70,7 +70,6 @@ export const useModernStrictMode = false;
70 export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
71 export const enableDeferRootSchedulingToMicrotask = true;
72
73 -export const diffInCommitPhase = true;
73 export const enableAsyncActions = false;
74
75 export const alwaysThrottleRetries = true;
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
-1
@@ -73,7 +73,6 @@ export const enableDO_NOT_USE_disableStrictPassiveEffect = false;
73 export const enableFizzExternalRuntime = false;
74 export const enableDeferRootSchedulingToMicrotask = true;
75
76 -export const diffInCommitPhase = true;
76 export const enableAsyncActions = false;
77
78 export const alwaysThrottleRetries = true;
packages/shared/forks/ReactFeatureFlags.www-dynamic.js
-1
@@ -25,7 +25,6 @@ export const enableUnifiedSyncLane = __VARIANT__;
25 export const enableTransitionTracing = __VARIANT__;
26 export const enableCustomElementPropertySupport = __VARIANT__;
27 export const enableDeferRootSchedulingToMicrotask = __VARIANT__;
28 -export const diffInCommitPhase = __VARIANT__;
28 export const enableAsyncActions = __VARIANT__;
29 export const alwaysThrottleRetries = __VARIANT__;
30 export const enableDO_NOT_USE_disableStrictPassiveEffect = __VARIANT__;
packages/shared/forks/ReactFeatureFlags.www.js
-1
@@ -27,7 +27,6 @@ export const {
27 enableTransitionTracing,
28 enableCustomElementPropertySupport,
29 enableDeferRootSchedulingToMicrotask,
30 - diffInCommitPhase,
30 enableAsyncActions,
31 alwaysThrottleRetries,
32 enableDO_NOT_USE_disableStrictPassiveEffect,