Add enableShallowPropDiffing feature flag (#29664)

## Summary

We currently do deep diffing for object props, and also use custom
differs, if they are defined, for props with custom attribute config.

The idea is to simply do a `===` comparison instead of all that work. We
will do less computation on the JS side, but send more data to native.

The hypothesis is that this change should be neutral in terms of
performance. If that's the case, we'll be able to get rid of custom
differs, and be one step closer to deleting view configs.

This PR adds the `enableShallowPropDiffing` feature flag to support this
experiment.

## How did you test this change?

With `enableShallowPropDiffing` hardcoded to `true`:
```
yarn test packages/react-native-renderer
```
This fails on the following test cases:
- should use the diff attribute
- should do deep diffs of Objects by default
- should skip deeply-nested changed functions

Which makes sense with this change. These test cases should be deleted
if the experiment is shipped.

DiffTrain build for commit https://github.com/facebook/react/commit/eb259b5d3b20b053dc0444e6ae442774c396c4a7.
This commit is contained in:
dmytrorykun
2024-06-05 14:12:45 +00:00
parent 612c599870
commit c3e6274566
14 changed files with 52 additions and 47 deletions
+1 -1
View File
@@ -1 +1 @@
19.0.0-native-fb-8d87e374ac-20240605
19.0.0-native-fb-eb259b5d3b-20240605
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<fa7cd5d97506f1bb69c87f488d89be8b>>
* @generated SignedSource<<ea78b2c848505f4463e69d15c9fab311>>
*/
'use strict';
@@ -34,7 +34,7 @@ var enableLazyContextPropagation = false;
var enableLegacyHidden = false;
var enableAsyncActions = true;
var disableLegacyMode = false;
var enableOwnerStacks = false; // Flow magic to verify the exports of this file match the original version.
var enableOwnerStacks = false;
// by calls to these methods by a Babel plugin.
//
@@ -23580,7 +23580,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-native-fb-8d87e374ac-20240605';
var ReactVersion = '19.0.0-native-fb-eb259b5d3b-20240605';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<092ce538ca4de8be4327e71237165e91>>
* @generated SignedSource<<9f5cdd27c8a995a1b8a76a1a483fe892>>
*/
"use strict";
@@ -9336,7 +9336,7 @@ var devToolsConfig$jscomp$inline_1048 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-8d87e374ac-20240605",
version: "19.0.0-native-fb-eb259b5d3b-20240605",
rendererPackageName: "react-test-renderer"
};
var internals$jscomp$inline_1235 = {
@@ -9367,7 +9367,7 @@ var internals$jscomp$inline_1235 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-native-fb-eb259b5d3b-20240605"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1236 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<87c8e73bcb9fb8c9410ffc06fa6bd2bf>>
* @generated SignedSource<<9de6a6e87c2dda4c7cc1064f9ae5326f>>
*/
"use strict";
@@ -9958,7 +9958,7 @@ var devToolsConfig$jscomp$inline_1131 = {
throw Error("TestRenderer does not support findFiberByHostInstance()");
},
bundleType: 0,
version: "19.0.0-native-fb-8d87e374ac-20240605",
version: "19.0.0-native-fb-eb259b5d3b-20240605",
rendererPackageName: "react-test-renderer"
};
(function (internals) {
@@ -10002,7 +10002,7 @@ var devToolsConfig$jscomp$inline_1131 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-native-fb-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-native-fb-eb259b5d3b-20240605"
});
exports._Scheduler = Scheduler;
exports.act = act;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<b56ca1c242dd0ffd52cd5e21c3aea60e>>
* @generated SignedSource<<6be5c0ebea422fc17b34d68b2c83ee9f>>
*/
'use strict';
@@ -24,7 +24,7 @@ if (
}
var dynamicFlagsUntyped = require('ReactNativeInternalFeatureFlags');
var ReactVersion = '19.0.0-native-fb-8d87e374ac-20240605';
var ReactVersion = '19.0.0-native-fb-eb259b5d3b-20240605';
// Re-export dynamic flags from the internal module.
var dynamicFlags = dynamicFlagsUntyped; // We destructure each value before re-exporting to avoid a dynamic look-up on
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<213f917640a921cab6e905d058f72c69>>
* @generated SignedSource<<5820b9b0b63e8f183474e78c3b8c0599>>
*/
"use strict";
@@ -604,4 +604,4 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-native-fb-8d87e374ac-20240605";
exports.version = "19.0.0-native-fb-eb259b5d3b-20240605";
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<0d70f3af44b684a76b1ba2e8c4f4690a>>
* @generated SignedSource<<f1e3d789c049c1d41d7b873980f035d6>>
*/
"use strict";
@@ -608,7 +608,7 @@ exports.useSyncExternalStore = function (
exports.useTransition = function () {
return ReactSharedInternals.H.useTransition();
};
exports.version = "19.0.0-native-fb-8d87e374ac-20240605";
exports.version = "19.0.0-native-fb-eb259b5d3b-20240605";
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
"function" ===
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
@@ -1 +1 @@
8d87e374ac69904012530af702af1cd51d90e07d
eb259b5d3b20b053dc0444e6ae442774c396c4a7
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<df2d3b83d063ac15c59e3e2b63766572>>
* @generated SignedSource<<14847709597e1765b7a5521ad085419c>>
*/
'use strict';
@@ -40,6 +40,7 @@ var alwaysThrottleRetries = dynamicFlags.alwaysThrottleRetries,
enableAddPropertiesFastPath = dynamicFlags.enableAddPropertiesFastPath,
enableDeferRootSchedulingToMicrotask = dynamicFlags.enableDeferRootSchedulingToMicrotask,
enableInfiniteRenderLoopDetection = dynamicFlags.enableInfiniteRenderLoopDetection,
enableShallowPropDiffing = dynamicFlags.enableShallowPropDiffing,
passChildrenWhenCloningPersistedNodes = dynamicFlags.passChildrenWhenCloningPersistedNodes; // The rest of the flags are static for better dead code elimination.
var enableAsyncActions = true;
var enableSchedulingProfiler = true;
@@ -2284,13 +2285,13 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
if (typeof attributeConfig !== 'object') {
// case: !Object is the default case
if (defaultDiffer(prevProp, nextProp)) {
if (enableShallowPropDiffing || defaultDiffer(prevProp, nextProp)) {
// a normal leaf has changed
(updatePayload || (updatePayload = {}))[propKey] = nextProp;
}
} else if (typeof attributeConfig.diff === 'function' || typeof attributeConfig.process === 'function') {
// case: CustomAttributeConfiguration
var shouldUpdate = prevProp === undefined || (typeof attributeConfig.diff === 'function' ? attributeConfig.diff(prevProp, nextProp) : defaultDiffer(prevProp, nextProp));
var shouldUpdate = enableShallowPropDiffing || prevProp === undefined || (typeof attributeConfig.diff === 'function' ? attributeConfig.diff(prevProp, nextProp) : defaultDiffer(prevProp, nextProp));
if (shouldUpdate) {
var _nextValue = typeof attributeConfig.process === 'function' ? // $FlowFixMe[incompatible-use] found when upgrading Flow
@@ -26315,7 +26316,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-rc-8d87e374ac-20240605';
var ReactVersion = '19.0.0-rc-eb259b5d3b-20240605';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<082338327f5d8b3d9083c45ada0fc89f>>
* @generated SignedSource<<d52db7df4a1972dcf0d2e7369a62a18b>>
*/
"use strict";
@@ -975,6 +975,7 @@ var alwaysThrottleRetries = dynamicFlagsUntyped.alwaysThrottleRetries,
dynamicFlagsUntyped.enableDeferRootSchedulingToMicrotask,
enableInfiniteRenderLoopDetection =
dynamicFlagsUntyped.enableInfiniteRenderLoopDetection,
enableShallowPropDiffing = dynamicFlagsUntyped.enableShallowPropDiffing,
passChildrenWhenCloningPersistedNodes =
dynamicFlagsUntyped.passChildrenWhenCloningPersistedNodes,
emptyObject$1 = {},
@@ -1143,14 +1144,15 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
(updatePayload[propKey] = attributeConfig);
}
else if (prevProp !== nextProp)
if ("object" !== typeof attributeConfig)
defaultDiffer(prevProp, nextProp) &&
((updatePayload || (updatePayload = {}))[propKey] = nextProp);
else if (
if ("object" !== typeof attributeConfig) {
if (enableShallowPropDiffing || defaultDiffer(prevProp, nextProp))
(updatePayload || (updatePayload = {}))[propKey] = nextProp;
} else if (
"function" === typeof attributeConfig.diff ||
"function" === typeof attributeConfig.process
) {
if (
enableShallowPropDiffing ||
void 0 === prevProp ||
("function" === typeof attributeConfig.diff
? attributeConfig.diff(prevProp, nextProp)
@@ -10589,7 +10591,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1125 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-rc-8d87e374ac-20240605",
version: "19.0.0-rc-eb259b5d3b-20240605",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10632,7 +10634,7 @@ var internals$jscomp$inline_1351 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-rc-eb259b5d3b-20240605"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1352 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<5618516a88154482ba6491c354793ce2>>
* @generated SignedSource<<fcfb86ff11e1edbd7f3bbf8964624064>>
*/
"use strict";
@@ -979,6 +979,7 @@ var alwaysThrottleRetries = dynamicFlagsUntyped.alwaysThrottleRetries,
dynamicFlagsUntyped.enableDeferRootSchedulingToMicrotask,
enableInfiniteRenderLoopDetection =
dynamicFlagsUntyped.enableInfiniteRenderLoopDetection,
enableShallowPropDiffing = dynamicFlagsUntyped.enableShallowPropDiffing,
passChildrenWhenCloningPersistedNodes =
dynamicFlagsUntyped.passChildrenWhenCloningPersistedNodes,
emptyObject$1 = {},
@@ -1147,14 +1148,15 @@ function diffProperties(updatePayload, prevProps, nextProps, validAttributes) {
(updatePayload[propKey] = attributeConfig);
}
else if (prevProp !== nextProp)
if ("object" !== typeof attributeConfig)
defaultDiffer(prevProp, nextProp) &&
((updatePayload || (updatePayload = {}))[propKey] = nextProp);
else if (
if ("object" !== typeof attributeConfig) {
if (enableShallowPropDiffing || defaultDiffer(prevProp, nextProp))
(updatePayload || (updatePayload = {}))[propKey] = nextProp;
} else if (
"function" === typeof attributeConfig.diff ||
"function" === typeof attributeConfig.process
) {
if (
enableShallowPropDiffing ||
void 0 === prevProp ||
("function" === typeof attributeConfig.diff
? attributeConfig.diff(prevProp, nextProp)
@@ -11295,7 +11297,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1206 = {
findFiberByHostInstance: getInstanceFromNode,
bundleType: 0,
version: "19.0.0-rc-8d87e374ac-20240605",
version: "19.0.0-rc-eb259b5d3b-20240605",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11351,7 +11353,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-rc-eb259b5d3b-20240605"
});
exports.createPortal = function (children, containerTag) {
return createPortal$1(
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<0e9c68239d8a9d26858bb89cd9abc325>>
* @generated SignedSource<<6c20aa3e22217266908e3ecd9630508d>>
*/
'use strict';
@@ -26671,7 +26671,7 @@ identifierPrefix, onUncaughtError, onCaughtError, onRecoverableError, transition
return root;
}
var ReactVersion = '19.0.0-rc-8d87e374ac-20240605';
var ReactVersion = '19.0.0-rc-eb259b5d3b-20240605';
/*
* The `'' + value` pattern (used in perf-sensitive code) throws for Symbol
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<2747e6b76f0aa189c25198424fc57990>>
* @generated SignedSource<<e4ccf48334aab7c5cf8c435553c27ffe>>
*/
"use strict";
@@ -10734,11 +10734,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-rc-8d87e374ac-20240605" !== isomorphicReactPackageVersion)
if ("19.0.0-rc-eb259b5d3b-20240605" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-rc-8d87e374ac-20240605\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-rc-eb259b5d3b-20240605\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -10788,7 +10788,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1193 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-rc-8d87e374ac-20240605",
version: "19.0.0-rc-eb259b5d3b-20240605",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -10831,7 +10831,7 @@ var internals$jscomp$inline_1440 = {
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-rc-eb259b5d3b-20240605"
};
if ("undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__) {
var hook$jscomp$inline_1441 = __REACT_DEVTOOLS_GLOBAL_HOOK__;
@@ -7,7 +7,7 @@
* @noflow
* @nolint
* @preventMunge
* @generated SignedSource<<9a786ed82dc836841b91b14c947c2328>>
* @generated SignedSource<<ebed379b5ce174dc33565e53da7c941d>>
*/
"use strict";
@@ -11441,11 +11441,11 @@ function traverseOwnerTreeUp(hierarchy, instance) {
traverseOwnerTreeUp(hierarchy, instance);
}
var isomorphicReactPackageVersion = React.version;
if ("19.0.0-rc-8d87e374ac-20240605" !== isomorphicReactPackageVersion)
if ("19.0.0-rc-eb259b5d3b-20240605" !== isomorphicReactPackageVersion)
throw Error(
'Incompatible React versions: The "react" and "react-native-renderer" packages must have the exact same version. Instead got:\n - react: ' +
(isomorphicReactPackageVersion +
"\n - react-native-renderer: 19.0.0-rc-8d87e374ac-20240605\nLearn more: https://react.dev/warnings/version-mismatch")
"\n - react-native-renderer: 19.0.0-rc-eb259b5d3b-20240605\nLearn more: https://react.dev/warnings/version-mismatch")
);
if (
"function" !==
@@ -11495,7 +11495,7 @@ var roots = new Map(),
devToolsConfig$jscomp$inline_1274 = {
findFiberByHostInstance: getInstanceFromTag,
bundleType: 0,
version: "19.0.0-rc-8d87e374ac-20240605",
version: "19.0.0-rc-eb259b5d3b-20240605",
rendererPackageName: "react-native-renderer",
rendererConfig: {
getInspectorDataForInstance: getInspectorDataForInstance,
@@ -11551,7 +11551,7 @@ var roots = new Map(),
scheduleRoot: null,
setRefreshHandler: null,
getCurrentFiber: null,
reconcilerVersion: "19.0.0-rc-8d87e374ac-20240605"
reconcilerVersion: "19.0.0-rc-eb259b5d3b-20240605"
});
exports.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED = {
computeComponentStackForErrorReporting: function (reactTag) {