Skip to content

Commit ddc0b6a

Browse files
Nick Lefeverfacebook-github-bot
authored andcommitted
Add runtime validation of Props 2.0 output (#54461)
Summary: Compare the output from Props 1.5 with Props 2.0 to detect any missing props in the output coming from Props 2.0. The comparison doesn't run for initial mounts due to Props 1.5 not using the default property values to generate a diff, which is obviously leading to more properties being in the Props 1.5 output that aren't necessary. Changelog: [Internal] Differential Revision: D86577390
1 parent ee7575f commit ddc0b6a

File tree

1 file changed

+96
-2
lines changed

1 file changed

+96
-2
lines changed

packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,79 @@ void FabricMountingManager::onSurfaceStop(SurfaceId surfaceId) {
5454

5555
namespace {
5656

57+
#ifdef REACT_NATIVE_DEBUG
58+
// List of layout-only props extracted from ViewProps.kt used to filter out
59+
// component props from Props 1.5 to validate the Props 2.0 output
60+
inline bool isLayoutOnlyProp(const std::string& propName) {
61+
static const std::unordered_set<std::string> layoutOnlyProps = {
62+
// Flexbox Alignment
63+
"alignItems",
64+
"alignSelf",
65+
"alignContent",
66+
67+
// Flexbox Properties
68+
"flex",
69+
"flexBasis",
70+
"flexDirection",
71+
"flexGrow",
72+
"flexShrink",
73+
"flexWrap",
74+
"justifyContent",
75+
76+
// Gaps
77+
"rowGap",
78+
"columnGap",
79+
"gap",
80+
81+
// Display & Position
82+
"display",
83+
"position",
84+
85+
// Positioning
86+
"right",
87+
"top",
88+
"bottom",
89+
"left",
90+
"start",
91+
"end",
92+
93+
// Dimensions
94+
"width",
95+
"height",
96+
"minWidth",
97+
"maxWidth",
98+
"minHeight",
99+
"maxHeight",
100+
101+
// Margins
102+
"margin",
103+
"marginVertical",
104+
"marginHorizontal",
105+
"marginLeft",
106+
"marginRight",
107+
"marginTop",
108+
"marginBottom",
109+
"marginStart",
110+
"marginEnd",
111+
112+
// Paddings
113+
"padding",
114+
"paddingVertical",
115+
"paddingHorizontal",
116+
"paddingLeft",
117+
"paddingRight",
118+
"paddingTop",
119+
"paddingBottom",
120+
"paddingStart",
121+
"paddingEnd",
122+
123+
// Other
124+
"collapsable",
125+
};
126+
return layoutOnlyProps.count(propName) > 0;
127+
}
128+
#endif
129+
57130
inline int getIntBufferSizeForType(CppMountItem::Type mountItemType) {
58131
switch (mountItemType) {
59132
case CppMountItem::Type::Create:
@@ -232,8 +305,29 @@ jni::local_ref<jobject> getProps(
232305
strcmp(
233306
newShadowView.componentName,
234307
newProps->getDiffPropsImplementationTarget()) == 0) {
235-
return ReadableNativeMap::newObjectCxxArgs(
236-
newProps->getDiffProps(oldProps));
308+
auto diff = newProps->getDiffProps(oldProps);
309+
310+
#ifdef REACT_NATIVE_DEBUG
311+
if (oldProps != nullptr) {
312+
auto controlDiff =
313+
diffDynamicProps(oldProps->rawProps, newProps->rawProps);
314+
315+
for (const auto& [prop, value] : controlDiff.items()) {
316+
if (diff.count(prop) == 0) {
317+
// Skip layout-only props since they are not included in Props 2.0
318+
if (!isLayoutOnlyProp(prop.asString())) {
319+
LOG(ERROR) << "Props diff validation failed: Props 1.5 has prop '"
320+
<< prop.asString()
321+
<< "' = " << (value != nullptr ? value : "NULL")
322+
<< " that Props 2.0 doesn't have for component "
323+
<< newShadowView.componentName;
324+
}
325+
}
326+
}
327+
}
328+
#endif
329+
330+
return ReadableNativeMap::newObjectCxxArgs(std::move(diff));
237331
}
238332
if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) {
239333
if (oldProps == nullptr) {

0 commit comments

Comments
 (0)