This pull request's diff hasn't been prepared yet. Log in with GitHub to have us prepare it for review.

Use a virtual accessor instead of a dynamic_cast when building ShadowView layout metrics #59290

Closed
hryhoriiK97 opened 7:07am on October 21, 2026 wanted to merge 34 Ξ” into facebook/react-native main from
differ-viewkind-static-cast

Pull Request Overview

  • Opened on October 21, 2026
  • Status Closed
  • Commit count 2 with first commit October 21, 2026

Total Delta

34 Total Diff Delta

Open Days

Open 15 weekdays

Test Delta

0 Diff Delta in Test Files
Breakdown by Phase

How long has this pull request spent in each phase of its lifecycle?

Fraction of total time Business days Phase
 
1 day Authoring 1 commit before pull request opened for review
 
0 days Awaiting first review
 
0 days Revising work with 1 commit in response to 1 review that left 5 comments

Total time for pull request 1 business day from first commit to closed PR

Author avatar

Use a virtual accessor instead of a dynamic_cast when building ShadowView layout metrics

Summary:

ShadowView's constructor reads the node's layout metrics through layoutMetricsFromShadowNode, which uses dynamic_cast<const LayoutableShadowNode *> to find out whether the node has layout. The differ builds a ShadowView for every child it visits when it slices a node's children, so this runs for every visited node on every commit, including the per-frame commits that update a ScrollView's content offset.

Current behavior. Every call pays for a dynamic_cast, which walks the type's inheritance information at runtime; for a View, the node's type is ConcreteViewShadowNode<...>, several levels below LayoutableShadowNode. In Time Profiler traces of scrolling, __dynamic_cast took 19–21% of the differ's time.

With this change. ShadowNode gets a virtual asLayoutable() that returns nullptr, and LayoutableShadowNode overrides it to return this. layoutMetricsFromShadowNode calls it instead of the dynamic_cast, so every node, not only Views, gets its layout metrics without a runtime type check, and the compiler guarantees the result. LayoutableShadowNode::getDebugProps is marked override, since clang requires it once the class has another override.

Changelog:

[GENERAL] [CHANGED] - Building a ShadowView no longer uses a dynamic_cast to read a node's layout metrics

Test Plan:


  • The pinned clang-format passes on the changed files. The C++ API snapshots in scripts/cxx-api/api-snapshots list the new method.


  • yarn fantom (full suite, with the tester rebuilt from this change): 3,289 tests pass (110 skipped by their configuration). In that run the tester crashed (SIGSEGV) in two other files, AnimatedBackend-itest and StaleEventHandlersFromInterruptedRender-itest; neither crashed in 30 runs each on their own, and the same kind of crash happens locally without this change.


  • Measurement. RNTester built with xcodebuild in Release and run on a physical iPhone 15 Pro (iOS 26.6.1). The two arms are the same binary with the change switched by a launch argument, in the order before, after, after, before. Interactions were driven by Argent on the physical iPhone. Timed: the diff phase of each mount transaction from TransactionTelemetry, on the main thread.

RNTester Playground
before
this PR
change

A FlatList of 500 multi-line messages, 20 flings down and 20 up per launch (~8,700 transactions): diff time per launch
1,140 ms
1,006 ms
βˆ’11.8%

Same, diff time per 1–2-mutation transaction (the per-frame scroll updates)
111.0 Β΅s
98.3 Β΅s
βˆ’11.4%

1000 labels mounted and unmounted, 10 times per arm: diff time per mount or unmount (median)
2.61 ms
2.47 ms
βˆ’5.4%

πŸ€– Generated with Claude Code

Comments Threads Pending Resolution

Resolved Comment Threads

No resolved comments have been left on this PR.