Skip to content

Commit eba0bfb

Browse files
jingjing2222meta-codesync[bot]
authored andcommitted
Enable overrideBySynchronousMountPropsAtMountingAndroid by default on Android (#56652)
Summary: Enable `overrideBySynchronousMountPropsAtMountingAndroid` by default on Android. This flag was added to prevent regular Fabric mount updates from overriding newer props that were already applied synchronously by Native Animated direct manipulation. Without this path enabled, Android can still hit a race where: 1. Native Animated applies `transform` props synchronously on the UI thread. 2. A regular Fabric mount update for the same view is already in flight with older props. 3. That regular mount update reaches `SurfaceMountingManager.updateProps()` later and overwrites the newer synchronous animated value. 4. The view visibly jumps/flickers during the animation. This is not just theoretical. I spent about a week debugging this from a real Android flickering issue, and the final repro is intentionally very small. The visual impact is clear in the README videos, and I am attaching the same videos here as well. Minimal repro: https://github.com/jingjing2222/fix-react-native-android-flickering The repro uses React Native 0.85.2 with Android Fabric. The JS only runs `Animated.timing(..., {useNativeDriver: true})` for `translateX` and `translateY`. ## As-is https://github.com/user-attachments/assets/64882c09-1580-4209-a4aa-aa62bcf58287 With the current default, flickering is visible during the animation. ## To-be https://github.com/user-attachments/assets/fa929562-36b3-4930-98b5-754098e571e3 With `overrideBySynchronousMountPropsAtMountingAndroid` enabled, the flickering is gone. The existing mounting-layer flag fixes this by storing the latest synchronous mount props and reapplying them when regular mount props are processed. This makes the final mounted props preserve the latest Native Animated direct manipulation value. Because this can happen with a simple native-driver transform animation, enabling this default should prevent other Android users from spending time debugging the same race condition. ### Why Android only? iOS Fabric already tracks props managed by NativeAnimated on the component view. When NativeAnimated synchronously updates props such as `transform` or `opacity`, iOS records those prop keys and prevents later normal Fabric updates from re-applying stale values for those props. Android Fabric has a similar mechanism through `tagToSynchronousMountProps`, but it was gated behind `overrideBySynchronousMountPropsAtMountingAndroid`, which defaults to false. Therefore, NativeAnimated could synchronously apply the final transform to the view, and a later normal Fabric props update could re-apply stale transform props, causing a transient flicker. This change makes Android preserve synchronously mounted NativeAnimated props across later Fabric prop updates, matching the ownership behavior already present on iOS. ## Changelog: [ANDROID] [FIXED] - Prevent stale Fabric mount updates from overriding newer Native Animated synchronous prop updates. Pull Request resolved: #56652 Test Plan: - Ran `yarn featureflags --update` - Ran `git diff --check` - Verified generated feature flag defaults were updated for Android, C++, and JS. - Verified the minimal repro with the README videos attached above: - as-is/current default: flickering is visible during `translateX`/`translateY` native-driver animation - to-be/flag enabled: flickering is not visible Reviewed By: javache, sammy-SC Differential Revision: D103070058 Pulled By: zeyap fbshipit-source-id: c5f140ee28ec090677b0cc1b1ad9db35c79aba8d
1 parent 6ec4597 commit eba0bfb

4 files changed

Lines changed: 7 additions & 7 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<d2b14345bf627e35562530912b3aae1f>>
7+
* @generated SignedSource<<a9a8ce443fa160a7494fc1c9e7baa02f>>
88
*/
99

1010
/**
@@ -153,7 +153,7 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
153153

154154
override fun hideOffscreenVirtualViewsOnIOS(): Boolean = false
155155

156-
override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = false
156+
override fun overrideBySynchronousMountPropsAtMountingAndroid(): Boolean = true
157157

158158
override fun perfIssuesEnabled(): Boolean = false
159159

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlagsDefaults.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<4a2fd61cbcdb28042f09ccb03c970674>>
7+
* @generated SignedSource<<d987528598996fc7b1bf3c872f51e2ed>>
88
*/
99

1010
/**
@@ -288,7 +288,7 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
288288
}
289289

290290
bool overrideBySynchronousMountPropsAtMountingAndroid() override {
291-
return false;
291+
return true;
292292
}
293293

294294
bool perfIssuesEnabled() override {

packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@ const definitions: FeatureFlagDefinitions = {
743743
ossReleaseStage: 'none',
744744
},
745745
overrideBySynchronousMountPropsAtMountingAndroid: {
746-
defaultValue: false,
746+
defaultValue: true,
747747
metadata: {
748748
dateAdded: '2025-09-04',
749749
description:

packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<aa202d346e68c3dd641d557306964ebe>>
7+
* @generated SignedSource<<1dd51a152bb30c0e2073a14566c8368d>>
88
* @flow strict
99
* @noformat
1010
*/
@@ -468,7 +468,7 @@ export const hideOffscreenVirtualViewsOnIOS: Getter<boolean> = createNativeFlagG
468468
/**
469469
* Override props at mounting with synchronously mounted (i.e. direct manipulation) props from Native Animated.
470470
*/
471-
export const overrideBySynchronousMountPropsAtMountingAndroid: Getter<boolean> = createNativeFlagGetter('overrideBySynchronousMountPropsAtMountingAndroid', false);
471+
export const overrideBySynchronousMountPropsAtMountingAndroid: Getter<boolean> = createNativeFlagGetter('overrideBySynchronousMountPropsAtMountingAndroid', true);
472472
/**
473473
* Enable reporting Performance Issues (`detail.devtools.performanceIssue`). Displayed in the V2 Performance Monitor and the "Performance Issues" sub-panel in DevTools.
474474
*/

0 commit comments

Comments
 (0)