[Fabric][iOS] Differ creates a mounted view again when a child with negative zIndex moves in a nested flatten/unflatten
Dieses Issue hat noch niemand übernommen.
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 40/100
Rechercherichtung
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.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
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
- Vorherrschende Sprache
- C++
- Sterne
- 127k
- Forks
- 25.3k
- PR-Merge-Kennzahlen
- Keine gemergten PRs in 30 T.
Beitragsleitfaden
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus react/react-native
-
Needs: Author Feedback Needs: Repro
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 92/100
react/react-native#58621 · 1 Kommentar ·
-
Needs: Author Feedback Needs: Repro
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
react/react-native#58610 · 1 Kommentar ·
-
Needs: Triage :mag:
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
react/react-native#58565 · 1 Kommentar · 2 Reaktionen ·
-
Needs: Author Feedback Needs: Repro
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 88/100
react/react-native#58555 · 5 Kommentare · 2 Reaktionen ·
-
Needs: Attention Needs: Repro
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 85/100
react/react-native#58526 · 2 Kommentare ·
Alle Issues in react/react-native
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
google/libultrahdr#485 ·
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
godotengine/godot#123776 ·
-
bug
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 60/100
-
good first issue
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 90/100
-
good first issue
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
ros2/common_interfaces#344 ·