Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの 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 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
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. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

react/react-native のほかの issue

react/react-native の issue をすべて見る

似ている issue

C++ の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。