Hacktoberfest 2026:維護者為十月標記出來的 issue,仍然開放、適合新手。 瀏覽 Hacktoberfest issue

[Fabric][iOS] Differ creates a mounted view again when a child with negative zIndex moves in a nested flatten/unflatten

未關閉
#58,647 0 則留言 0 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

評估

難度
4/5
預估耗時
3-5 天
新手友好度
40/100
Issue 類型
缺陷
描述清晰度
描述清楚
活躍度
活躍
技術堆疊
ios, react-native

研究方向

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 內容生成。

描述

Needs: Triage :mag: Platform: iOS
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. RCTComponentViewRegistry asserts: "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 Create for 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
  1. Clone https://github.com/pawicao/rn-differ-zindex-flatten-repro.
  2. Go to the ReproducerApp directory.
  3. Run yarn install.
  4. Run cd ios && bundle install && bundle exec pod install && cd ...
  5. Run yarn ios.
  6. Wait approximately 0.5 seconds. The app changes the tree automatically.
  7. 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

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

react/react-native 的其他 Issue

查看 react/react-native 的全部 Issue

相似的 Issue

更多 C++ Issue

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。