Skip to content

Send only changed props on Insert of preallocated views with accumulated raw props - #58770

Open
bartlomiejbloniarz wants to merge 1 commit into
mainfrom
bartlomiejbloniarz/preallocated-props-diff-on-insert
Open

bartlomiejbloniarz wants to merge 1 commit into
mainfrom
bartlomiejbloniarz/preallocated-props-diff-on-insert

Conversation

@bartlomiejbloniarz

Copy link
Copy Markdown
Contributor

Summary:

With enableAccumulatedUpdatesInRawPropsAndroid, FabricMountingManager handles every Insert by emitting UpdateProps({}, new), which sends the view's full props again. The view already received those props at preallocation or from its Create item. So every inserted view's props are serialized and applied twice. The pull model (enableMountingCoordinatorPullModelAndroid) requires accumulated raw props and preallocates new views before the UI thread pulls the transaction, so under the pull model both happen on the UI thread.

For components with a Props 2.0 diff (View, with enablePropsUpdateReconciliationAndroid), this is also a correctness bug. The second payload is diffed against default props. If a prop goes back to its default between preallocation and mount, it is never sent, and the view keeps the preallocated value.

This PR adds enablePreallocatedPropsDiffOnInsertAndroid. It is off by default and only takes effect together with accumulated raw props.

  • The allocated view registry also keeps the props each view was preallocated with, until the view's first Insert.
  • That Insert sends only the difference between the preallocated and inserted props, or nothing when they are the same.
  • A view created by a Create item already has its props, so its Insert sends nothing.
  • A re-Insert of a mounted view (a move) also sends nothing. Its prop changes arrive through the Update mutation the differ emits with it.
  • Held props are released on the first Insert, when an unmounted node is destroyed, or when the surface stops.
  • With the flag off, the registry stores no props and the Insert path is unchanged.

In debug builds, Inserts of preallocated views now go through the Props 1.5 vs Props 2.0 validation in getProps. It can log props that Props 2.0 does not cover yet.

The flag keeps the change separately testable while enableAccumulatedUpdatesInRawPropsAndroid is being evaluated. The intent is to fold it into that flag once validated. I'm happy to make it unconditional now if that is preferred.

Changelog:

[Internal] - Add enablePreallocatedPropsDiffOnInsertAndroid, which stops re-sending all props on Insert of preallocated views when enableAccumulatedUpdatesInRawPropsAndroid is on

Test Plan:

  • yarn featureflags --verify-unchanged and yarn test packages/react-native/src/private/featureflags pass. yarn format-check-cpp passes.

  • FabricMountingManager.cpp compiles with the ReactAndroid arm64-v8a NDK flags. I did not run a full RNTester Android build of this exact revision.

  • The runtime checks below used an earlier revision of this change with identical behaviour. The build also contained unrelated local fixes, present in both arms.

  • Emulator, pull model and Props 2.0 flags on: a view whose backgroundColor and opacity are reset to their defaults between preallocation and mount keeps the old values with the flag off. With the flag on, it shows the defaults.

  • OPPO A16 (low-end Android), release build, pull model and Props 2.0 flags on. The benchmark is a native screen that opens a React Native surface with placeholder cards, then a list of 24 cards. "First render" is the first React Native frame drawn. "Content drawn" is the first frame with the list. Flag on vs off in the same build:

    flag off flag on
    Props updates on Insert, first render / content (Perfetto, 5 runs) 90 / 100 2 / 0
    First render, median (35 runs per arm) 384.8 ms 364.2 ms
    Content drawn, median (35 runs per arm) 1209.6 ms 1186.6 ms

    Paired within-round differences, 35 pairs: first render −20.6 ms [95% CI −26.8, −15.0], content drawn −22.0 ms [−43.4, −18.4]. Arms were interleaved.

…ted raw props

With enableAccumulatedUpdatesInRawPropsAndroid, every Insert emits
UpdateProps({}, new) and sends the view's full props again, although the view
already received them at preallocation or from its Create item. So every
inserted view's props are serialized and applied twice. Under the pull model
both happen on the UI thread. With Props 2.0 the second payload is diffed
against default props, so a prop reset to its default between preallocation
and mount is never sent.

Behind the new enablePreallocatedPropsDiffOnInsertAndroid flag, the allocated
view registry keeps the props a view was preallocated with until its first
Insert. That Insert sends the difference between the preallocated and inserted
props, and nothing for views created by a Create item.
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Sep 30, 2026
@facebook-github-tools facebook-github-tools Bot added p: Software Mansion Partner: Software Mansion Partner p: Facebook Partner: Facebook labels Sep 30, 2026
@meta-codesync

meta-codesync Bot commented Sep 30, 2026

Copy link
Copy Markdown

@bartlomiejbloniarz has imported this pull request. If you are a Meta employee, you can view this in D122571252.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. p: Facebook Partner: Facebook p: Software Mansion Partner: Software Mansion Partner

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant