Repository navigation
Skip the iOS ScrollView clip walk when no view clips its subviews - #59284
Open
hryhoriiK97 wants to merge 1 commit into
Open
hryhoriiK97 wants to merge 1 commit into
hryhoriiK97 wants to merge 1 commit into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
javache
requested changes
Oct 21, 2026
javache
left a comment
Contributor
There was a problem hiding this comment.
I'm not a fan of using global state for this.
The subview clip pass should be shallow and only consider the children of the scrollview itself. Could we track this locally inside the scrollview's (content view?)?
Contributor
Author
|
Thanks @javache! I tried tracking it on the content view, but the pass can't be shallow today: a Options:
Which would you prefer? |
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:
RCTScrollViewComponentViewupdates clipped subviews in_remountChildrenby callingupdateClippedSubviewsWithClipRect:relativeToView:on its scroll view, after every mounting transaction and whenever the content offset has moved by more thankClippingLeewaywhile scrolling. The call walks every view in the scroll view's content. Only anRCTViewComponentViewwithremoveClippedSubviewsacts on it (it mounts and unmounts its children); every other view converts the rect and forwards it to its subviews.Current behavior.
removeClippedSubviewsis off by default on iOS, so in most apps no view acts on the walk, but every ScrollView still runs it on every few points of scrolling. In RNTester, this walk is the largest main-thread cost while scrolling: from a sixth to almost half of the main thread's time on the screens below.With this change.
RCTViewComponentViewkeeps a count of live views withremoveClippedSubviewson. It is updated when the prop changes, when a view is recycled and when it is deallocated, all on the main thread._remountChildrenreturns early while the count is 0. When any view hasremoveClippedSubviewson, the walk runs as before. Turning the prop off doesn't depend on the walk either: the view remounts all its children as soon as the prop changes.Changelog:
[IOS] [CHANGED] - A ScrollView no longer walks its content to update clipped subviews while no view has
removeClippedSubviewsonTest Plan:
clang-format --dry-run --Werroron the changed files passes.RCTViewComponentViewTests: all 24 tests pass on an iOS simulator. These tests are part of React-RCTFabric's test spec, which RNTester doesn't integrate, so I ran them by adding the file to theRNTesterUnitTeststarget locally (xcodebuild test -scheme RNTesterUnitTests -only-testing:RNTesterUnitTests/RCTViewComponentViewTests). The two new tests check that the count followsremoveClippedSubviewsacross two views without counting a view twice, and that recycling and deallocating a clipping view take it off the count. With the decrement indeallocremoved, the second test fails.xcodebuildin Release and run on a physical iPhone 15 Pro (iOS 26.6.1).removeClippedSubviewson, there were 0 mismatches over 102,997 walks (ScrollViewExample 89,199, TextExample 11,799, FlatList basic 1,999). In a screen that turnsremoveClippedSubviewson and off twice, with views mounted and unmounted in between, the count returned to 0 each time with 0 mismatches.removeClippedSubviewsWith the change, the walk took 0 ms on every screen. SectionList is mostly bound by the JS thread, and its main-thread total varied by ±10% between launches of the same arm.
removeClippedSubviewsviewWith a clipping view present, both arms run the same walk and the only added work is one comparison per walk; the ranges overlap and the difference between launches comes from the flings.
🤖 Generated with Claude Code