Repository navigation
Conversation
A ParagraphShadowNode cloned with neither new props nor new children (for example by Yoga because a sibling changed) dropped its memoized content and measured layouts. layout() then rebuilt the attributed string, which no longer compared equal to the one in state because the fragments' parentShadowView layout metrics had changed, so every such sibling got a state update and was re-set on the UI thread. With prepared text layout the sibling was also laid out again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
When Yoga lays out a container again, it clones every child whose layout it touches, including unchanged siblings. A
ParagraphShadowNodecloned that way (no new props, no new children) drops its memoizedcontent_(the attributed string) andmeasuredLayouts_.Current behavior. In
layout()the clone rebuilds itsAttributedStringand compares it with the one in itsParagraphState. After the paragraph's first commit the comparison fails even though nothing changed.Fragment::operator==comparesparentShadowView.layoutMetrics, and the string stored in state captured those metrics before the paragraph had been laid out. SoupdateStateIfNeededcreates a new state for every sibling paragraph:UPDATE_STATEmount instruction. The UI thread rebuilds its Spannable, callssetText, lays the text out again and redraws it.ReactTextViewManager.updateState, 1000setTextand 1000onDrawcalls for the one change.enablePreparedTextLayout, the clone also has no measured layout to reuse, so every sibling is laid out again on the JS thread.With this change. A clone whose fragment has neither props nor children keeps
content_andmeasuredLayouts_.getContentdrops both if the font-size multiplier or layout direction they were built for no longer matches. Unchanged siblings then produce no state update and no re-layout.Why. Any update inside a container with many text siblings (non-virtualized lists, settings screens, forms, tables) currently costs UI-thread work for every sibling, and with prepared text layout also a JS-thread layout for every sibling.
Changelog:
[GENERAL] [FIXED] - Avoid state updates and re-layout of unchanged
Textsiblings when a sibling changesTest Plan:
./gradlew :packages:rn-tester:android:app:assembleDebug -PreactNativeArchitectures=arm64-v8aandyarn format-check-cpppass on this branch.nlohmann/json.hppfails with this machine's Apple clang. I'm relying on CI for it.enablePreparedTextLayout.Benchmarks. RNTester release build with a text benchmark screen that is not part of this PR. Medians, main vs. this PR, variants interleaved.
Emulator (Pixel 10 Pro AVD, API 37), current
mainvs. this branch, 6 runs:enablePreparedTextLayoutPhysical Samsung Galaxy A16, the same change on an earlier
main(d4d0813969c), 8 runs. #59016 was applied in both arms.🤖 Generated with Claude Code