facebook / react-native

A framework for building native applications using React
https://reactnative.dev
MIT License
119.14k stars 24.32k forks source link

App crashing on specific set of styles on `iOS` #42599

Closed WoLewicki closed 9 months ago

WoLewicki commented 9 months ago

Description

When having a specific formation of views as shown below, changing the value of accessibilityElementsHidden or collapsable or anything affected by this code: https://github.com/facebook/react-native/blob/6d77d7b895794c318fba3f52a3dbd73ef2fd5a73/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp#L51 from false to true causes this assert to fail for some reason: https://github.com/facebook/react-native/blob/6d77d7b895794c318fba3f52a3dbd73ef2fd5a73/packages/react-native/ReactCommon/react/renderer/mounting/StubViewTree.cpp#L179. It only happens on new arch.

import * as React from 'react';
import {Button, View} from 'react-native';

export default function App() {
  const [test, setTest] = React.useState(false);

  return (
    <View style={{paddingTop: 100}}>
      <View collapsable={test}>
        <Button title="Click me" onPress={() => setTest(!test)} />
        <View style={{position: 'absolute', zIndex: -1}} />
      </View>
    </View>
  );
}

Steps to reproduce

  1. Change the code of the app template to the shown above and set the new arch to true
  2. Click the Click me button to see that the app crashes.

React Native Version

0.73.2

Affected Platforms

Runtime - iOS Runtime - Android

Areas

Fabric - The New Renderer

Output of npx react-native info

System:
  OS: macOS 14.1
  CPU: (12) arm64 Apple M3 Pro
  Memory: 257.78 MB / 18.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.9.0
    path: ~/.nvm/versions/node/v20.9.0/bin/node
  Yarn:
    version: 1.22.19
    path: /opt/homebrew/bin/yarn
  npm:
    version: 10.1.0
    path: ~/.nvm/versions/node/v20.9.0/bin/npm
  Watchman:
    version: 2023.11.27.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.14.3
    path: /Users/wojciechlewicki/.rvm/gems/ruby-3.2.1/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 23.2
      - iOS 17.2
      - macOS 14.2
      - tvOS 17.2
      - visionOS 1.0
      - watchOS 10.2
  Android SDK:
    Android NDK: 22.1.7171670
IDEs:
  Android Studio: 2023.1 AI-231.9392.1.2311.11255304
  Xcode:
    version: 15.2/15C500b
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 11.0.12
    path: /opt/homebrew/opt/openjdk@11/bin/javac
  Ruby:
    version: 3.2.1
    path: /Users/wojciechlewicki/.rvm/rubies/ruby-3.2.1/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react: Not Found
  react-native: Not Found
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: true
iOS:
  hermesEnabled: true
  newArchEnabled: true

Stacktrace or Logs

E0122 17:23:55.453842 99025472 StubViewTree.cpp:164] StubView: ASSERT FAILURE: REMOVE mutation assertion failure: oldChildShadowView does not match oldStubView: [10] stub hash: ##5636168882475450106 old mutation hash: ##12782350514277793530
E0122 17:23:55.453974 99025472 StubViewTree.cpp:171] ChildStubView: surfaceId=1 tag=10 traits=  <Node/> componentName=View props=0x000000010947e418(shared) eventEmitter=0x000060000263d818(shared) layoutMetrics=  <LayoutMetrics frame={x:0,y:0,width:0,height:0} contentInsets={top:0,right:0,bottom:0,left:0} borderWidth={top:0,right:0,bottom:0,left:0} overflowInset={top:0,right:-0,bottom:-0,left:0} displayType=Flex layoutDirection=LeftToRight pointScaleFactor=3/> state=null(shared)
E0122 17:23:55.465993 99025472 StubViewTree.cpp:174] OldChildShadowView: surfaceId=1 tag=10 traits=  <Node/> componentName=View props=0x000000010947e418(shared) eventEmitter=0x000060000263d818(shared) layoutMetrics=  <LayoutMetrics frame={x:0,y:100,width:0,height:0} contentInsets={top:0,right:0,bottom:0,left:0} borderWidth={top:0,right:0,bottom:0,left:0} overflowInset={top:0,right:-0,bottom:-0,left:0} displayType=Flex layoutDirection=LeftToRight pointScaleFactor=3/> state=null(shared)
E0122 17:23:55.466113 99025472 StubViewTree.cpp:180] react_native_assert failure: (ShadowView)(*childStubView) == mutation.oldChildShadowView
Assertion failed: ((ShadowView)(*childStubView) == mutation.oldChildShadowView), function mutate, file StubViewTree.cpp, line 180.

Reproducer

https://github.com/WoLewicki/NewArchStylingBug

Screenshots and Videos

No response

WoLewicki commented 9 months ago

Should be proper repo right now.

WoLewicki commented 9 months ago

Not sure who to cc, but looks like it might be connected to yoga (?) so @NickGerleman. I can just comment the assert and the app I work on (https://github.com/Expensify/App) works correctly from what I tested. Also cc @cipolleschi since it only happens on iOS, prolly because accessibilityElementsHidden is iOS-only prop.

WoLewicki commented 9 months ago

Ok I played a little more with the values and changed accessibilityElementsHidden to accessibilityViewIsModal or collapsable and it still crashes, so looks like this is the code fragment that later causes the ShadowViews to be different: https://github.com/facebook/react-native/blob/6d77d7b895794c318fba3f52a3dbd73ef2fd5a73/packages/react-native/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp#L51 so even more cc @NickGerleman since I see you made many changes in this file (and sorry for extensive pings 😅 )

WoLewicki commented 9 months ago

Ok and changing the prop to something that exists on Android too like collapsable makes it crash too.

cipolleschi commented 9 months ago

Hi @WoLewicki, thanks for opening the issue, providing a reproducer and begin the investigation. All this work is extremely valuable to track down the root cause.

We have added the bug in our tracker with high priority, so we are going to work on it in the next days!

WoLewicki commented 9 months ago

Nice to hear, it can be easily mitigated by commenting the assert and the app works correctly then, but I know it is only a workaround for the app and not a solution to the problem.

NickGerleman commented 9 months ago

I wonder if this has to do with changing collapsable after initial creation. The flag controls view flattening behavior, so we would be going in between having a view, and not.

When RN_SHADOW_TREE_INTROSPECTION is set, RN Fabric maintains another structure to sanity check operations, which is triggering the assert.

Seems like potentially we are asserting the wrong thing, or that we have a bug somewhere when switching between flattened and non flattened views.

IIRC RN_SHADOW_TREE_INTROSPECTION recently got disabled in OSS where folks observed some of the local development assertions slowed things down, but since we haven't seen (or at least noticed this) internally, I'm wondering if these are still a valuable source of information. @cipolleschi was more involved with that and will know the tradeoffs.

WoLewicki commented 9 months ago

Yeah, most probably the view is not there anymore and the check fails due to not having the same child in the tree anymore. What is more interesting is the fact that it only happens with those exact props set in the child view. I haven't investigated too much what other combinations can trigger it though.

cipolleschi commented 9 months ago

In main, we disabled the RN_SHADOW_TREE_INTROSPECTION already exactly because it was creating issues of this sort, with inactionable crashes in Debug mode for the OSS users. I tested a nightly, which contains 51fd4188005cc3318fecc85b8090453c480af6e8 and the repro is fixed I'm asking to cherry pick this in 0.73.3.