[Fabric][iOS] Differ creates a mounted view again when a child with negative zIndex moves in a nested flatten/unflatten
まだ誰も着手していません。
評価
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 初心者へのやさしさ
- 40/100
調査の方向性
The bug is in Fabric's differ in Differentiator.cpp, specifically in calculateShadowViewMutationsFlattener. Start by examining the reproducer at https://github.com/pawicao/rn-differ-zindex-flatten-repro to understand the tree shape with nested views and negative zIndex. Look at the sorting by orderIndex and the handling of ShadowViewNodePair in subVisitedNewMap/subVisitedOldMap. The fix likely involves ensuring K is not incorrectly marked for deletion/creation. Run the iOS app from the reproducer to see the crash, then trace through the differ logic with the provided mutation logs.
索引モデルが issue の本文から書いたものです。
説明
Description
The Fabric differ (Differentiator.cpp) makes incorrect mutations for one tree shape. It creates a view that is already mounted. Or it deletes a view that only moves.
The tree has three nested views: Q > U > F. Q and F have opacity. F has three children. The second child (K) has zIndex: -1.
When the opacity moves from F to U in one commit, U unflattens and F flattens. The children of F move to U. The opposite change also causes the bug.
Result:
- iOS Debug: the app crashes on the first change.
RCTComponentViewRegistryasserts: "Attempt to dequeue already registered component." - iOS Release: the app crashes with SIGSEGV in
-[RCTMountingManager performTransaction:](I saw this on 0.88.0-rc.2). - Android: the app does not crash. The Android mounting layer ignores a
Createfor a tag that it already has. The differ output is still incorrect.
If you remove zIndex: -1 from K, the differ output is correct.
Cause (suspected):
This below cause came from AI assisted analysis of the situation
In calculateShadowViewMutationsFlattener, the differ sorts the children by orderIndex. Thus it visits K before F. At that time, nothing has matched K, so K becomes a delete or create candidate. Then the nested (un)flattening of F matches K through a different ShadowViewNodePair. It records K in subVisitedNewMap or subVisitedOldMap. But the last loop over the candidates checks only treeChildPair.inOtherTree(). Thus the differ deletes or creates K.
A related bug is in the same function. In the "flatten parent, unflatten child" branch, auto unvisitedItPair = *unvisitedOtherNodesIt->second; keeps the address of a local copy in unvisitedRecursiveChildPairs. This pointer is not valid after the loop iteration.
We found this bug through crash reports in react-native-reanimated (Layout Animations). The reproducer does not use Reanimated or any other library.
Steps to reproduce
- Clone https://github.com/pawicao/rn-differ-zindex-flatten-repro.
- Go to the
ReproducerAppdirectory. - Run
yarn install. - Run
cd ios && bundle install && bundle exec pod install && cd ... - Run
yarn ios. - Wait approximately 0.5 seconds. The app changes the tree automatically.
- Notice the crash.
React Native Version
0.87.1
Affected Platforms
Runtime - iOS
Output of npx @react-native-community/cli info
System:
OS: macOS 27.0
CPU: (10) arm64 Apple M4
Memory: 271.25 MB / 32.00 GB
Shell:
version: "5.9"
path: /bin/zsh
Binaries:
Node:
version: 22.23.2
path: /Users/oskarpawica/.nvm/versions/node/v22.23.2/bin/node
Yarn:
version: 1.22.22
path: /Users/oskarpawica/.nvm/versions/node/v22.23.2/bin/yarn
npm:
version: 10.9.8
path: /Users/oskarpawica/.nvm/versions/node/v22.23.2/bin/npm
Watchman: Not Found
Managers:
CocoaPods:
version: 1.16.2
path: /opt/homebrew/bin/pod
SDKs:
iOS SDK:
Platforms:
- DriverKit 27.0
- iOS 27.0
- macOS 27.0
- tvOS 27.0
- visionOS 27.0
- watchOS 27.0
Android SDK: Not Found
IDEs:
Android Studio: 2025.1 AI-251.26094.121.2513.14007798
Xcode:
version: 27.0/27A266a
path: /usr/bin/xcodebuild
Languages:
Java:
version: 17.0.17
path: /opt/homebrew/opt/openjdk@17/bin/javac
Ruby:
version: 3.4.5
path: /opt/homebrew/opt/ruby/bin/ruby
npmPackages:
"@react-native-community/cli":
installed: 20.2.0
wanted: 20.2.0
react:
installed: 19.2.3
wanted: 19.2.3
react-native:
installed: 0.87.1
wanted: 0.87.1
react-native-macos: Not Found
npmGlobalPackages:
"*react-native*": Not Found
Android:
hermesEnabled: true
newArchEnabled: true
iOS:
hermesEnabled: true
newArchEnabled: true
Stacktrace or Logs
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'RCTComponentViewRegistry: Attempt to dequeue already registered component.'
9 libobjc.A.dylib objc_exception_throw
10 Foundation -[NSAssertionHandler handleFailureInFunction:file:lineNumber:description:]
11 React -[RCTComponentViewRegistry dequeueComponentViewWithComponentHandle:tag:]
12 React RCTPerformMountInstructions(std::vector<facebook::react::ShadowViewMutation> const&, RCTComponentViewRegistry*, RCTMountingTransactionObserverCoordinator&, int)
13 React -[RCTMountingManager performTransaction:]::$_2::operator()(facebook::react::MountingTransaction const&, facebook::react::SurfaceTelemetry const&) const
20 React facebook::react::TelemetryController::pullTransaction(...)
21 React -[RCTMountingManager performTransaction:]
22 React -[RCTMountingManager initiateTransaction:]
23 React __42-[RCTMountingManager scheduleTransaction:]_block_invoke
24 React __RCTExecuteOnMainQueue_block_invoke
Mutations from the differ (tags from a trace of the same tree):
U unflattens, F flattens:
Remove 146 <- 148 @2 | Remove 142 <- 148 @1 | Remove 144 <- 148 @0 | Create 150 | Create 144 | Insert 150 -> 152 @1 | Insert 144 -> 150 @0 | ... | Delete 148
^^^^^^^^^^ K is mounted
U flattens, F unflattens:
Remove 182 <- 186 @2 | Remove 178 <- 186 @1 | Remove 180 <- 186 @0 | Remove 186 <- 188 @0 | Delete 186 | Delete 180 | Create 184 | Insert 180 -> 184 @0 | ...
^^^^^^^^^^ K moves, but the differ deletes it
MANDATORY Reproducer
https://github.com/pawicao/rn-differ-zindex-flatten-repro
Screenshots and Videos
https://github.com/user-attachments/assets/9a4275ac-991c-4d74-a07c-37a7d899bc71
- 主要言語
- C++
- スター
- 127k
- フォーク
- 25.3k
- PR マージ指標
- 30日以内にマージされた PR はありません
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
react/react-native のほかの issue
-
Needs: Author Feedback Needs: Repro
難易度 1/5 1時間未満 初心者へのやさしさ 92/100
react/react-native#58621 · コメント 1 件 ·
-
Needs: Author Feedback Needs: Repro
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
react/react-native#58610 · コメント 1 件 ·
-
Needs: Triage :mag:
難易度 2/5 1〜3時間 初心者へのやさしさ 82/100
react/react-native#58565 · コメント 1 件 · リアクション 2 件 ·
-
Needs: Author Feedback Needs: Repro
難易度 2/5 1〜3時間 初心者へのやさしさ 88/100
react/react-native#58555 · コメント 5 件 · リアクション 2 件 ·
-
Needs: Attention Needs: Repro
難易度 2/5 1〜3時間 初心者へのやさしさ 85/100
react/react-native#58526 · コメント 2 件 ·
react/react-native の issue をすべて見る
似ている issue
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
google/libultrahdr#485 ·
-
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
godotengine/godot#123776 ·
-
bug
難易度 1/5 1時間未満 初心者へのやさしさ 60/100
-
good first issue
難易度 1/5 1時間未満 初心者へのやさしさ 90/100
-
good first issue
難易度 2/5 1〜3時間 初心者へのやさしさ 75/100
ros2/common_interfaces#344 ·