Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48995
This change adds an extra parameter to the codegen script that allow our users to trigger codegen for Apps or for Libraries.
When running codegen for Apps, we have to generate some extra files that are not needed by the Libraries. This is causing issues to our library maintainers and this change will provide more flexibility in the DevX of libraries.
The default value is App, so if the new parameter is not passed, nothing will change in the current behavior.
[iOS][Added] - Add the `source` parameter to generate-codegen-artifacts to avoid generating files not needed by libraries.
Reviewed By: cortinico
Differential Revision: D68765478
fbshipit-source-id: 8030b4472ad4f5058e58b1c91089de5122a4f60a
Summary:
A NPE can occur when a user touches the screen before the `SurfaceMountingManager` is initialized. Below is an example of the error log from our production service. This issue can also be reproduced using RNTester. To prevent invalid touch events during init time of rn app from causing an NPE, add a null check for SurfaceMountingManager before calling mark/sweepActiveTouchForTag.
```
Fatal Exception: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.facebook.react.fabric.mounting.SurfaceMountingManager.markActiveTouchForTag(int)' on a null object reference
at com.facebook.react.fabric.FabricUIManager.markActiveTouchForTag(FabricUIManager.java)
at com.facebook.react.uimanager.JSTouchDispatcher.markActiveTouchForTag(JSTouchDispatcher.java)
at com.facebook.react.uimanager.JSTouchDispatcher.handleTouchEvent(JSTouchDispatcher.java)
at com.facebook.react.runtime.ReactSurfaceView.dispatchJSTouchEvent(ReactSurfaceView.java)
at com.facebook.react.ReactRootView.onInterceptTouchEvent(ReactRootView.java)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2870)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3352)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2963)
at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:794)
at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1967)
at android.app.Activity.dispatchTouchEvent(Activity.java:4571)
at com.rainist.banksalad2.feature.common.BaseActivity.dispatchTouchEvent(BaseActivity.java)
at androidx.appcompat.view.WindowCallbackWrapper.dispatchTouchEvent(WindowCallbackWrapper.java:70)
at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:752)
at android.view.View.dispatchPointerEvent(View.java:16498)
at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:8676)
at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:8423)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7752)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:7978)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:8035)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:7809)
at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:7775)
at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:7783)
at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:7756)
at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:11343)
at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:11212)
at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:11168)
at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:11477)
at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:337)
at android.os.MessageQueue.nativePollOnce(MessageQueue.java)
at android.os.MessageQueue.next(MessageQueue.java:335)
at android.os.Looper.loopOnce(Looper.java:187)
at android.os.Looper.loop(Looper.java:319)
at android.app.ActivityThread.main(ActivityThread.java:9063)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:588)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)
```
https://github.com/user-attachments/assets/e9c6ff84-c94d-4392-9042-8e635197202e
## Changelog:
[Android] [Fixed] - Avoid NPE when touch event is triggered before SurfaceManager is initiated
Pull Request resolved: https://github.com/facebook/react-native/pull/48007
Test Plan:
I checked the crashed being fixed on RNTester.
https://github.com/user-attachments/assets/71f7e359-707a-494c-ae34-fef8d432e612
Reviewed By: cortinico
Differential Revision: D66594576
Pulled By: javache
fbshipit-source-id: b1559d94866bdb021e0374f1953684849603033c
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49358
When the network is under strain, the code responsible for detecting if the inspector proxy's connection to the client has been lost may incorrectly assume the connection is dead. This false positive occurs because the system assumes that if a pong is not received within 5 seconds of a ping, the other side has disconnected. However, I was able to consistently reproduce scenarios where a delay of more than 5 seconds (even more than 20 seconds) was followed by a return to normal ping-pong communication without any issues.
Since I can't think of any issues with increasing this number, I'm increasing it to 60s.
Changelog:
[General][Fixed] - Disconnections of DevTools when the network is under significant strain.
Reviewed By: robhogan, huntie
Differential Revision: D69523906
fbshipit-source-id: 50db1e7bbe690b42421bc226aa30fd6571ba2257
Summary:
This change adds an opt-in to restore JavaScript log streaming via the Metro dev server, [removed from React Native core in 0.77](https://reactnative.dev/blog/2025/01/21/version-0.77#removal-of-consolelog-streaming-in-metro).
Users can opt into this legacy behaviour by adding the `--client-logs` flag to `npx react-native-community/cli start`.
- The default experience remains without streamed JS logs.
- The existing "JavaScript logs have moved! ..." notice is printed in all cases, and we do not advertise the new flag for new users.
- Under non-Community CLI dev servers (i.e. Expo), log streaming is restored implicitly.
We will clean up this functionality again when we eventually remove JS log streaming over `HMRClient`, tasked in T214991636.
**Implementation notes**
- Logs are always sent over `HMRClient` (previous status quo), even with log streaming off in the dev server. This is a necessary evil to be able to flag this functionality in a user-accessible place, and to move fast for 0.78.
- Necessarily, emitting `fusebox_console_notice` moves to the dev server itself, on first device (Fusebox) connection.
Changelog:
[General][Added] - Add opt in for legacy Metro log streaming via `--client-logs` flag
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49250
The TurboModule System decided to ignore the Null values when they are coming to JS. However, in iOS, null value can be mapped to `[NSNull null];` and this value is a valid value that can be used on the native side.
In the old architecture, when the user were sending a null value from JS to a native module, the Native side was receiving the value.
In the New Architecture, the value was stripped away.
This change allow us to handle the `null` value properly in the interop layer, to restore the usage of legacy modules in the New Arch.
I also tried with a more radical approach, but several tests were crashing because some modules do not know how to handle `NSNull`.
See discussion happening here: https://github.com/invertase/react-native-firebase/issues/8144#issuecomment-2548067344
## Changelog:
[iOS][Changed] - Properly handle `null` values coming from NativeModules.
Reviewed By: sammy-SC
Differential Revision: D69301396
fbshipit-source-id: be275185e2643092f6c3dc2481fe9381bbcf69e9
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48823
This diff is fixing the execution of Events that are sent early in the rendering of surfaces.
This diff fixes a bug in the queueing of events that are built with not surfaceId (-1), the fixes is to call getSurfaceManagerForView() to retrieve the proper surfaceId (as we do in the execution of events)
calling getSurfaceManagerForView() has a perf hit, we believe this won't be a problem because this method will only be called in edge cases (no surfaceId and early execution of events)
changelog: [Android][Fixed] Fix execution of early InteropEvents
Reviewed By: shwanton, lenaic
Differential Revision: D68454811
fbshipit-source-id: a79be0b392004e645c48d1683bba774b6b597ca0
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/49072
We have instance of apps crashing when enabling the New Architecture because of the TurboModule interop layer.
What's happening is that when the module is loaded, the TM Interop Layer tries to parse the method definition to expose them in JS. However, for some libraries in the Legacy Architecture, it is possible to define a method in Objective-C and to define a different signature in Swift.
For example, the [`RNBluetoothClassic` library](https://github.com/kenjdavidson/react-native-bluetooth-classic) defines a selector in objective-c which [has the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.m#L134-L136)
```
RCT_EXTERN_METHOD(available: (NSString *)deviceId
resolver: (RCTPromiseResolveBlock)resolve
rejecter: (RCTPromiseRejectBlock)reject)
```
And the method is inmplemented in Swift with [the signature](https://github.com/kenjdavidson/react-native-bluetooth-classic/blob/main/ios/RNBluetoothClassic.swift#L502-L505):
```
func availableFromDevice(
_ deviceId: String,
resolver resolve: RCTPromiseResolveBlock,
rejecter reject: RCTPromiseRejectBlock
)
```
When the TurboModule interop layer tries to parse the method, it receives the `accept:resolver:rejecter:` signature, but that signature is not actually defined in as a method in the module instance, and it crashes.
This crash was not happening in the Old Architecture, which was handling this case gracefully. Notice that the specific method from the example is not working in the Old Architecture either. However, the app is not crashing in the old architecture.
This change adds the same graceful behaviors plus it adds a warning in development to notify the developer about which methods couldn't be found in the interface.
Fixes:
- https://github.com/facebook/react-native/issues/47587
- https://github.com/facebook/react-native/issues/48065
## Changelog:
[iOS][Fixed] - Avoid crashing the app when the InteropLayer can't find some methods in the native implementation.
Reviewed By: javache
Differential Revision: D68901734
fbshipit-source-id: 844d1bf29423d5c601b583540e86d57dfffd1428
Summary:
The `maxFontSizeMultiplier` prop for `Text` and `TextInput` was not handled in Fabric / New Architecture as documented in https://github.com/facebook/react-native/issues/47499.
bypass-github-export-checks
[GENERAL] [FIXED] - Fix `maxFontSizeMultiplier` prop on `Text` and `TextInput` components in Fabric / New Architecture
Pull Request resolved: https://github.com/facebook/react-native/pull/47614
Test Plan:
I have not added any automated tests for this change but try to do so if requested. I have however added examples to RN Tester for both the Text and TextInput components, as well as compared the behaviour with Paper / Old Architecture. Both on version 0.76.
Noticed now I didn't do exactly the same steps in both videos, oops! Be aware that reapplying changes made in the Settings are currently half-broken on the new architecture, thus I'm restarting the app on Android and iOS. But this issue is unrelated to my changes. I've tested on main branch and it has the same issue.
Here are comparison videos between Paper and Fabric on iOS *after* I've made my fix.
| Paper | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/f4fd009f-aa6d-41ab-92fa-8dcf1e351ba1" /> | <video src="https://github.com/user-attachments/assets/fda42cc6-34c2-42a7-a6e2-028e7c866075" /> |
| Paper | Fabric |
| ------------- | ------------- |
| <video src="https://github.com/user-attachments/assets/59b59f7b-25d2-4b5b-a8e2-d2054cc6390b" /> | <video src="https://github.com/user-attachments/assets/72068566-8f2a-4463-874c-45a6f5b63b0d" /> |
Reviewed By: Abbondanzo
Differential Revision: D65953019
Pulled By: cipolleschi
fbshipit-source-id: 90c3c7e236229e9ad9bd346941fafe4af8a9d9fc
Summary:
`dev-middleware` uses `invariant` but does not declare it as a dependency. Under certain hoisting scenarios, or when using pnpm, this will cause `dev-middleware` to fail while being loaded.
## Changelog:
[GENERAL] [FIXED] - add missing `invariant` dependency
Pull Request resolved: https://github.com/facebook/react-native/pull/49047
Test Plan: n/a
Reviewed By: cortinico
Differential Revision: D68835789
Pulled By: huntie
fbshipit-source-id: 13718f4970ed55e6e062b7c2bd719be977abdd0c
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48982
We were missing a conversion to px on the `getOutline()` function of `CompositeBackgroundDrawable` which led to incorrect elevation prop rendering
Changelog: [Android][Fixed] - Elevation prop on android has incorrect border-radius
Reviewed By: NickGerleman
Differential Revision: D68724947
fbshipit-source-id: b3a7a4919bfd7c60fac7c3d6e3ba760e3f74d190
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48888
We have a report from OSS where Images are not displayed properly in case they are saved on disk with no extension.
We previously had a fix attempt iwith [this pr](https://github.com/facebook/react-native/pull/46971), but this was breaking some internal apps.
This second attempt should work for both cases.
## Changelog:
[iOS][Fixed] - Load images even when the extension is implicit
Reviewed By: cortinico
Differential Revision: D68555813
fbshipit-source-id: bc25970aafe3e6e5284163b663d36e00b3df3d82
Summary:
why: running `yarn test-e2e-local -t "RNTestProject" -p "Android" -h false -c $GITHUB_TOKEN` would actually build the app with Hermes even though it's specified as disabled.
This is because of the `if (argv.hermes == null)` condition whose body would not execute.
The condition was changed [recently](f322dc7#diff-56f57bf0eac99b0fda1b2938aceb8d9b663db82c07cb405bd53a01c8689710ffR258).
Reason for `await` being used:
```
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ scripts/release-testing/test-e2e-local.js:303:32
Cannot get argv.hermes because property hermes is missing in Promise [1]. [prop-missing]
scripts/release-testing/test-e2e-local.js
300│ 'reactNativeArchitectures=arm64-v8a',
301│ 'android/gradle.properties',
302│ );
303│ const hermesEnabled = (argv).hermes === true;
304│
305│ // Update gradle properties to set Hermes as false
306│ if (!hermesEnabled) {
flow-typed/npm/yargs_v17.x.x.js
[1] 80│ argv: Argv | Promise<Argv>;
```
## Changelog:
[INTERNAL] [FIXED] - fix `hermes` param handling in `test-e2e-local.js`
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48458
NOTE: This change is made once and is not guaranteed.
**Motivation**: Requiring Flow parsing for `react-native` and its dependencies in user space can involve friction. For the case of `react-native/assets-registry` → `react-native-web`, I believe we should do the pragmatic thing to relax this requirement.
- This is a convenience stopgap until https://github.com/facebook/react-native/pull/39542 can be stabilised.
- This package is tiny and infrequently modified — I believe it's pragmatic/safe to do a one-time conversion, with the above notice and no changelog (i.e. "experimental" for now).
Resolves https://github.com/facebook/react-native/issues/48349.
Changelog: [Internal]
Reviewed By: cortinico
Differential Revision: D67764460
fbshipit-source-id: 7687fd79c6dea73c234a46e475c1cc745225830b
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48525
Fixes https://github.com/facebook/react-native/issues/48249
`TextMeasureCacheKey` hash and equality functions only incorporates the maximum width constraint. I'm guessing this was an attempt at an optimization, but it can lead to incorrect results in pretty trivial cases. E.g. if Yoga knows a definite size of `Text` in one dimension, and measures via `YGMeasureModeExactly`, we can have a minimum size corresponding specific to the style in which the text was laid out.
Changelog:
[General][Fixed] - Fix TextMeasureCacheKey Throwing Out Some LayoutConstraints
Reviewed By: christophpurrer
Differential Revision: D67922414
fbshipit-source-id: 0ee0220059fc4e4645b1684c42a0587fe728bedd
Summary:
If 3rd party libs are using `inputAccessoryView` - the current code can easily break it. Whenever props gets changed we call `setDefaultInputAccessoryView` which will simply overwrite the current `inputAccessoryView` (which is highly undesirable).
The same fix on paper was made ~7 years ago: https://github.com/facebook/react-native/commit/bf3698323d81508fc77174df2b1ffe5fb03224e7
## Changelog:
<!-- Help reviewers and the release process by writing your own changelog entry.
Pick one each for the category and type tags:
[IOS] [FIXED] - Fixed problem with accessory view & 3rd party libs
For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests
Pull Request resolved: https://github.com/facebook/react-native/pull/48339
Test Plan: Make sure `inputAccessoryView` functionality works as before
Reviewed By: javache
Differential Revision: D67451188
Pulled By: cipolleschi
fbshipit-source-id: bc3fa82ae15f8acedfd0b4e17bdea69cbd8c8a8d
Summary:
Related PR in `react-native-screens`:
* https://github.com/software-mansion/react-native-screens/pull/2495
Additional context:
* [my detailed explanation of **one of the issues**](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)
* [Android Developer: ViewGroup.startViewTransition docs](https://developer.android.com/reference/android/view/ViewGroup#startViewTransition(android.view.View))
On Android view groups can be marked as "transitioning" with a `ViewGroup.startViewTransition` call. This effectively ensures, that in case a view group is marked with this call and its children are removed, they will be still drawn until `endViewTransition` is not called.
This mechanism is implemented in Android by [keeping track of "transitioning" children in auxiliary `mTransitioningViews` array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7178). Then when such "transitioning" child is removed, [it is removed from children array](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5595) but it's [parent-child relationship is not cleared](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#5397) and it is still retained in the auxiliary array.
Having that established we can proceed with problem description.
https://github.com/user-attachments/assets/d0356bf5-2f17-4b06-ba53-bfca659a1071
<details>
<summary>Full code</summary>
```javascript
import { NavigationContainer } from 'react-navigation/native';
import React from 'react';
import { createNativeStackNavigator } from 'react-navigation/native-stack';
import { enableScreens } from 'react-native-screens';
import {
StyleSheet,
Text,
View,
FlatList,
Button,
ViewProps,
Image,
FlatListProps,
findNodeHandle,
} from 'react-native';
enableScreens(true);
function Item({ children, ...props }: ViewProps) {
return (
<View style={styles.item} {...props}>
<Image source={require('../assets/trees.jpg')} style={styles.image} />
<Text style={styles.text}>{children}</Text>
</View>
);
}
function Home({ navigation }: any) {
return (
<View style={styles.container}>
<Button title="Go to List" onPress={() => navigation.navigate('List')} />
</View>
);
}
function ListScreenSimplified({secondVisible}: {secondVisible?: (visible: boolean) => void}) {
const containerRef = React.useRef<View>(null);
const innerViewRef = React.useRef<View>(null);
const childViewRef = React.useRef<View>(null);
React.useEffect(() => {
if (containerRef.current != null) {
const tag = findNodeHandle(containerRef.current);
console.log(`Container has tag [${tag}]`);
}
if (innerViewRef.current != null) {
const tag = findNodeHandle(innerViewRef.current);
console.log(`InnerView has tag [${tag}]`);
}
if (childViewRef.current != null) {
const tag = findNodeHandle(childViewRef.current);
console.log(`ChildView has tag [${tag}]`);
}
}, [containerRef.current, innerViewRef.current, childViewRef.current]);
return (
<View
ref={containerRef}
style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}
removeClippedSubviews={false}>
<View ref={innerViewRef} removeClippedSubviews style={{ height: '100%' }}>
<View ref={childViewRef} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} removeClippedSubviews={false}>
{secondVisible && (<Button title='Hide second' onPress={() => secondVisible(false)} />)}
</View>
</View>
</View>
);
}
function ParentFlatlist(props: Partial<FlatListProps<number>>) {
return (
<FlatList
data={Array.from({ length: 30 }).fill(0) as number[]}
renderItem={({ index }) => {
if (index === 10) {
return <NestedFlatlist key={index} />;
} else if (index === 15) {
return <ExtraNestedFlatlist key={index} />;
} else if (index === 20) {
return <NestedFlatlist key={index} horizontal />;
} else if (index === 25) {
return <ExtraNestedFlatlist key={index} horizontal />;
} else {
return <Item key={index}>List item {index + 1}</Item>;
}
}}
{...props}
/>
);
}
function NestedFlatlist(props: Partial<FlatListProps<number>>) {
return (
<FlatList
style={[styles.nestedList, props.style]}
data={Array.from({ length: 10 }).fill(0) as number[]}
renderItem={({ index }) => (
<Item key={'nested' + index}>Nested list item {index + 1}</Item>
)}
{...props}
/>
);
}
function ExtraNestedFlatlist(props: Partial<FlatListProps<number>>) {
return (
<FlatList
style={styles.nestedList}
data={Array.from({ length: 10 }).fill(0) as number[]}
renderItem={({ index }) =>
index === 4 ? (
<NestedFlatlist key={index} style={{ backgroundColor: '#d24729' }} />
) : (
<Item key={'nested' + index}>Nested list item {index + 1}</Item>
)
}
{...props}
/>
);
}
const Stack = createNativeStackNavigator();
export default function App(): React.JSX.Element {
return (
<NavigationContainer>
<Stack.Navigator screenOptions={{ animation: 'slide_from_right' }}>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="List" component={ListScreenSimplified}/>
</Stack.Navigator>
</NavigationContainer>
);
}
export function AppSimple(): React.JSX.Element {
const [secondVisible, setSecondVisible] = React.useState(false);
return (
<View style={{ flex: 1, backgroundColor: 'lightsalmon' }}>
{!secondVisible && (
<View style={{ flex: 1, backgroundColor: 'lightblue' }} >
<Button title='Show second' onPress={() => setSecondVisible(true)} />
</View>
)}
{secondVisible && (
<ListScreenSimplified secondVisible={setSecondVisible} />
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
nestedList: {
backgroundColor: '#FFA07A',
},
item: {
flexDirection: 'row',
alignItems: 'center',
padding: 10,
gap: 10,
},
text: {
fontSize: 24,
fontWeight: 'bold',
color: 'black',
},
image: {
width: 50,
height: 50,
},
});
```
</details>
Explanation (copied from [here](https://github.com/software-mansion/react-native-screens/pull/2495#issuecomment-2478915818)):
I've debugged this for a while now & I have good understanding of what's going on. This bug is caused by our usage of `startViewTransition` and its implications. We use it well, however React does not account for case that some view might be in transition. Error mechanism is as follows:
1. Let's have initially simple stack with two screens: "A, B". This is component rendered under "B":
```javascript
<View //<-- ContainerView (CV)
removeClippedSubviews={false}
style={{ flex: 1, backgroundColor: 'slateblue', overflow: 'hidden' }}>
<View removeClippedSubviews style={{ height: '100%' }}> // <--- IntermediateView (IV)
<View removeClippedSubviews={false} style={{ backgroundColor: 'pink', width: '100%', height: 50 }} /> // <--- ChildView (ChV)
</View>
</View>
```
2. We press the back button.
3. We're on Fabric, therefore subtree of B gets destroyed before B itself is unmounted -> in our commit hook we detect that the screen B will be unmounted & we mark every node under B as transitioning by calling `startViewTransition`.
4. React Mounting stage starts, view hierarchy is disassembled in bottom-up fashion (leafs first).
5. ReactViewGroupManager receives MountItem to detach ChV from IV.
6. A call to [`IV.removeView(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L58-L73), which effectively removes ChV from `IV.children`, ***HOWEVER*** it does not clear `ChV.parent`, meaning that after the call, `ChV.parent == IV`. This happens, due to view being marked as in-transition by our call to `startViewTransition`. If the view is not marked as in-transition this parent-child relationship is removed.
7. IV has `removeClippedSubviews` enabled, therefore a [call to `IV.removeViewWithSubviewsClippingEnabled(ChV)` is made](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactClippingViewManager.kt#L68). [This function](https://github.com/facebook/react-native/blob/9c11d7ca68c5c62ab7bab9919161d8417e96b28b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.java#L726-L744) does effectively two things:
1. if the ChV has parent (interpretation: it has not yet been detached from parent), we compute it's index in `IV.children` (Android.ViewGroup's state) and remove it from the array,
2. remove the ChV from `mAllChildren` array (this is state maintained by ReactViewGroup for purposes of implementing the "subview clipping" mechanism".
The crash happens in 7.1, because ChV has been removed from `IV.children` in step 6, but the parent-child relationship has not been broken up there. Under usual circumstances (this is my hypothesis now, yet unconfirmed) 7.1 does not execute, because `ChV.parent` is nulled in step no. 6.
Transitions. On Fabric, when some subtree is unmounted, views in the subtree are unmounted in bottom-up order. This leads to uncomfortable situation, where our components (react-native-screens), who want to drive & manage transitions are notified that their children will be removed after the subtrees mounted in screen subviews are already disassembled. **If we start animation in this very moment we will have staggering effect of white flash** [(issue)](https://github.com/software-mansion/react-native-screens/issues/1685) (we animate just the screen with white background without it's children). This was not a problem on Paper, because the order of subtree disassembling was opposite - top-down. While we've managed to workaround this issue on Fabric using `MountingTransactionObserving` protocol on iOS and a commit hook on Android (we can inspect mutations in incoming transaction before it starts being applied) we still need to prevent view hierarchy from being disassembled in the middle of transition (on Paper this has also been less of an issue) - and this is where `startViewTransition` comes in. It allows us to draw views throughout transition after React Native removes them from HostTree model. On iOS we exchange subtree for its snapshot for transition time, however this approach isn't feasible on Android, because [snapshots do not capture shadows](https://stackoverflow.com/questions/42212600/android-screenshot-of-view-with-shadow).
[Android does not expose a method to verify whether a view is in transition](https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/ViewGroup.java#7162) (it has `package` visibility), therefore we need to retrieve this information with some workaround. I see two posibilities:
* first approach would be to override `startViewTransition` & `endViewTransition` in ReactViewGroup and keep the state on whether the view is transitioning there,
* second possible approach would be as follows: we can check for "transitioning" view by checking whether a view has parent but is not it's parent child (this **should** be reliable),
Having information on whether the view is in transition or not, we can prevent multiple removals of the same view in every call site (currently only in `removeViewAt` if `parent.removeClippingSubviews == true`).
Another option would be to do just as this PR does: having in mind this "transitioning" state we can pass a flag to `removeViewWithSubviewClippingEnabled` and prevent duplicated removal from parent if we already know that this has been requested.
I can also add override of this method:
```java
/*package*/ void removeViewWithSubviewClippingEnabled(View view) {
this.removeViewWithSubviewClippingEnabled(view, false);
}
```
to make this parameter optional.
[ANDROID] [FIXED] - Handle removal of in-transition views.
Pull Request resolved: https://github.com/facebook/react-native/pull/47634
Test Plan: WIP WIP
Reviewed By: javache
Differential Revision: D66539065
Pulled By: tdn120
fbshipit-source-id: cf1add67000ebd1b5dfdb2048461a55deac10b16
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/48182
Maintainers from SVG reached out because of an edge case they inencountered when generating the ComponentProvider. In their setup, they had a `.git` folder in the repo and the algorithm was spending a lot of time crawling the git folder.
In general, we should avoid crawling hidden folders.
This change fix that.
## Changelog:
[General][Fixed] - Skip hidden folders when looking for third party components.
Reviewed By: javache
Differential Revision: D66959345
fbshipit-source-id: 992a79f3cff22cd6a459e0272c8140bc329888da