synchronise dispatching of view commands through RuntimeScheduler (#47604)
Some checks are pending
Label closed PR as merged and leave a comment / comment-and-label (push) Waiting to run
Publish Bumped Packages / publish_bumped_packages (push) Waiting to run
Update node modules cache / update_node_modules_cache (push) Waiting to run

Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/47604

## Changelog:

[iOS] [Fixed] - Fixed use of view commands from layout effects

Mounting of views is delayed by runtime scheduler to allow React to run layout effects. Execution of view commands must by queued together with mounting of views, otherwise it might be executed before views are mounted. When this happens, view commands are ignored.

So before, if view command was executed from layout effect (or ref function), it would get dispatched to the UI thread as quickly as possible. But mounting of views would be delayed. To fix this, both mounting of views and view commands are put on the same queue inside of RuntimeScheduler.

## What about Android?
Android employs a [retry mechanism](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java#L211) that was needed for react tag based view commands. In paper, one could send a view command to a react tag which was completely disconnected from whether a view exists or not.

iOS was built with ref commands in mind, so it doesn't have this mechanism.

Fixes: https://github.com/facebook/react-native/issues/47576

Reviewed By: javache, cipolleschi

Differential Revision: D65909191

fbshipit-source-id: 9d2a444879bee62a7b8b7d31edde450e18339b89
This commit is contained in:
Samuel Susla 2024-11-16 08:22:37 -08:00 committed by Facebook GitHub Bot
parent 38fb83ca86
commit 6f1c2a512e
20 changed files with 169 additions and 51 deletions

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b2f32b87f4e87ad9588add08dcb9b0c6>>
* @generated SignedSource<<1adfbb1f0b9791d93ea00f39fbce5520>>
*/
/**
@ -118,6 +118,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableFabricRendererExclusively(): Boolean = accessor.enableFabricRendererExclusively()
/**
* Synchronise the view command dispatching with mounting of new transaction
*/
@JvmStatic
public fun enableFixForViewCommandRace(): Boolean = accessor.enableFixForViewCommandRace()
/**
* When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing.
*/

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2a25e30cbee2548ee5538ecbc4b92fdc>>
* @generated SignedSource<<fb943b9c4c43ac8214a8f368076cbd62>>
*/
/**
@ -35,6 +35,7 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
private var enableFabricLogsCache: Boolean? = null
private var enableFabricRendererCache: Boolean? = null
private var enableFabricRendererExclusivelyCache: Boolean? = null
private var enableFixForViewCommandRaceCache: Boolean? = null
private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
@ -202,6 +203,15 @@ public class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAccesso
return cached
}
override fun enableFixForViewCommandRace(): Boolean {
var cached = enableFixForViewCommandRaceCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableFixForViewCommandRace()
enableFixForViewCommandRaceCache = cached
}
return cached
}
override fun enableGranularShadowTreeStateReconciliation(): Boolean {
var cached = enableGranularShadowTreeStateReconciliationCache
if (cached == null) {

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<0656ed5b5317e6caf85605bba62d08d1>>
* @generated SignedSource<<3980c51f97745c20e0fd510f595477dc>>
*/
/**
@ -58,6 +58,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun enableFabricRendererExclusively(): Boolean
@DoNotStrip @JvmStatic public external fun enableFixForViewCommandRace(): Boolean
@DoNotStrip @JvmStatic public external fun enableGranularShadowTreeStateReconciliation(): Boolean
@DoNotStrip @JvmStatic public external fun enableIOSViewClipToPaddingBox(): Boolean

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<608eece71b01769be0df8731774f9b0a>>
* @generated SignedSource<<f4f263578308798dcf7561918f58e0cc>>
*/
/**
@ -53,6 +53,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun enableFabricRendererExclusively(): Boolean = false
override fun enableFixForViewCommandRace(): Boolean = false
override fun enableGranularShadowTreeStateReconciliation(): Boolean = false
override fun enableIOSViewClipToPaddingBox(): Boolean = false

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<71dcfc3812f442a35ae2c24621c05605>>
* @generated SignedSource<<bd3529c1455508333a45a5852c3213c6>>
*/
/**
@ -39,6 +39,7 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
private var enableFabricLogsCache: Boolean? = null
private var enableFabricRendererCache: Boolean? = null
private var enableFabricRendererExclusivelyCache: Boolean? = null
private var enableFixForViewCommandRaceCache: Boolean? = null
private var enableGranularShadowTreeStateReconciliationCache: Boolean? = null
private var enableIOSViewClipToPaddingBoxCache: Boolean? = null
private var enableLayoutAnimationsOnAndroidCache: Boolean? = null
@ -221,6 +222,16 @@ public class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun enableFixForViewCommandRace(): Boolean {
var cached = enableFixForViewCommandRaceCache
if (cached == null) {
cached = currentProvider.enableFixForViewCommandRace()
accessedFeatureFlags.add("enableFixForViewCommandRace")
enableFixForViewCommandRaceCache = cached
}
return cached
}
override fun enableGranularShadowTreeStateReconciliation(): Boolean {
var cached = enableGranularShadowTreeStateReconciliationCache
if (cached == null) {

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<2dd3e32b571ae171bd5621424116188b>>
* @generated SignedSource<<a74e9cdce26b156a74e397fd065618c2>>
*/
/**
@ -53,6 +53,8 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun enableFabricRendererExclusively(): Boolean
@DoNotStrip public fun enableFixForViewCommandRace(): Boolean
@DoNotStrip public fun enableGranularShadowTreeStateReconciliation(): Boolean
@DoNotStrip public fun enableIOSViewClipToPaddingBox(): Boolean

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<62bd0386265d0fa4bdccab1faf22d25c>>
* @generated SignedSource<<9c1572172189f9f9ded86a9fdb1cb021>>
*/
/**
@ -129,6 +129,12 @@ class ReactNativeFeatureFlagsProviderHolder
return method(javaProvider_);
}
bool enableFixForViewCommandRace() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableFixForViewCommandRace");
return method(javaProvider_);
}
bool enableGranularShadowTreeStateReconciliation() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableGranularShadowTreeStateReconciliation");
@ -394,6 +400,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableFabricRendererExclusively(
return ReactNativeFeatureFlags::enableFabricRendererExclusively();
}
bool JReactNativeFeatureFlagsCxxInterop::enableFixForViewCommandRace(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableFixForViewCommandRace();
}
bool JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconciliation(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation();
@ -625,6 +636,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableFabricRendererExclusively",
JReactNativeFeatureFlagsCxxInterop::enableFabricRendererExclusively),
makeNativeMethod(
"enableFixForViewCommandRace",
JReactNativeFeatureFlagsCxxInterop::enableFixForViewCommandRace),
makeNativeMethod(
"enableGranularShadowTreeStateReconciliation",
JReactNativeFeatureFlagsCxxInterop::enableGranularShadowTreeStateReconciliation),

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<00e9f2368ec7745d960e8671b0e58d19>>
* @generated SignedSource<<92fcd7e8c814a6a76ec15c4cd60e00e4>>
*/
/**
@ -75,6 +75,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableFabricRendererExclusively(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableFixForViewCommandRace(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool enableGranularShadowTreeStateReconciliation(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e432522acfd785458553aa09806edb83>>
* @generated SignedSource<<93b93588adcd6b45ec748dff7ee18f07>>
*/
/**
@ -86,6 +86,10 @@ bool ReactNativeFeatureFlags::enableFabricRendererExclusively() {
return getAccessor().enableFabricRendererExclusively();
}
bool ReactNativeFeatureFlags::enableFixForViewCommandRace() {
return getAccessor().enableFixForViewCommandRace();
}
bool ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation() {
return getAccessor().enableGranularShadowTreeStateReconciliation();
}

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<079c1e8cd65a004c1b26f1a2517e9042>>
* @generated SignedSource<<b24144040cb5bd51f9910b81675ba5b2>>
*/
/**
@ -114,6 +114,11 @@ class ReactNativeFeatureFlags {
*/
RN_EXPORT static bool enableFabricRendererExclusively();
/**
* Synchronise the view command dispatching with mounting of new transaction
*/
RN_EXPORT static bool enableFixForViewCommandRace();
/**
* When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing.
*/

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<8f2e12ef2ddfb1e49b0cdfed8e695e54>>
* @generated SignedSource<<69c4aa85ed449d5d0fa90af4fa0be498>>
*/
/**
@ -299,6 +299,24 @@ bool ReactNativeFeatureFlagsAccessor::enableFabricRendererExclusively() {
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableFixForViewCommandRace() {
auto flagValue = enableFixForViewCommandRace_.load();
if (!flagValue.has_value()) {
// This block is not exclusive but it is not necessary.
// If multiple threads try to initialize the feature flag, we would only
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(15, "enableFixForViewCommandRace");
flagValue = currentProvider_->enableFixForViewCommandRace();
enableFixForViewCommandRace_ = flagValue;
}
return flagValue.value();
}
bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliation() {
auto flagValue = enableGranularShadowTreeStateReconciliation_.load();
@ -308,7 +326,7 @@ bool ReactNativeFeatureFlagsAccessor::enableGranularShadowTreeStateReconciliatio
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(15, "enableGranularShadowTreeStateReconciliation");
markFlagAsAccessed(16, "enableGranularShadowTreeStateReconciliation");
flagValue = currentProvider_->enableGranularShadowTreeStateReconciliation();
enableGranularShadowTreeStateReconciliation_ = flagValue;
@ -326,7 +344,7 @@ bool ReactNativeFeatureFlagsAccessor::enableIOSViewClipToPaddingBox() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(16, "enableIOSViewClipToPaddingBox");
markFlagAsAccessed(17, "enableIOSViewClipToPaddingBox");
flagValue = currentProvider_->enableIOSViewClipToPaddingBox();
enableIOSViewClipToPaddingBox_ = flagValue;
@ -344,7 +362,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(17, "enableLayoutAnimationsOnAndroid");
markFlagAsAccessed(18, "enableLayoutAnimationsOnAndroid");
flagValue = currentProvider_->enableLayoutAnimationsOnAndroid();
enableLayoutAnimationsOnAndroid_ = flagValue;
@ -362,7 +380,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLayoutAnimationsOnIOS() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(18, "enableLayoutAnimationsOnIOS");
markFlagAsAccessed(19, "enableLayoutAnimationsOnIOS");
flagValue = currentProvider_->enableLayoutAnimationsOnIOS();
enableLayoutAnimationsOnIOS_ = flagValue;
@ -380,7 +398,7 @@ bool ReactNativeFeatureFlagsAccessor::enableLongTaskAPI() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(19, "enableLongTaskAPI");
markFlagAsAccessed(20, "enableLongTaskAPI");
flagValue = currentProvider_->enableLongTaskAPI();
enableLongTaskAPI_ = flagValue;
@ -398,7 +416,7 @@ bool ReactNativeFeatureFlagsAccessor::enableNewBackgroundAndBorderDrawables() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(20, "enableNewBackgroundAndBorderDrawables");
markFlagAsAccessed(21, "enableNewBackgroundAndBorderDrawables");
flagValue = currentProvider_->enableNewBackgroundAndBorderDrawables();
enableNewBackgroundAndBorderDrawables_ = flagValue;
@ -416,7 +434,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePreciseSchedulingForPremountItemsOnA
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(21, "enablePreciseSchedulingForPremountItemsOnAndroid");
markFlagAsAccessed(22, "enablePreciseSchedulingForPremountItemsOnAndroid");
flagValue = currentProvider_->enablePreciseSchedulingForPremountItemsOnAndroid();
enablePreciseSchedulingForPremountItemsOnAndroid_ = flagValue;
@ -434,7 +452,7 @@ bool ReactNativeFeatureFlagsAccessor::enablePropsUpdateReconciliationAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(22, "enablePropsUpdateReconciliationAndroid");
markFlagAsAccessed(23, "enablePropsUpdateReconciliationAndroid");
flagValue = currentProvider_->enablePropsUpdateReconciliationAndroid();
enablePropsUpdateReconciliationAndroid_ = flagValue;
@ -452,7 +470,7 @@ bool ReactNativeFeatureFlagsAccessor::enableReportEventPaintTime() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(23, "enableReportEventPaintTime");
markFlagAsAccessed(24, "enableReportEventPaintTime");
flagValue = currentProvider_->enableReportEventPaintTime();
enableReportEventPaintTime_ = flagValue;
@ -470,7 +488,7 @@ bool ReactNativeFeatureFlagsAccessor::enableSynchronousStateUpdates() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(24, "enableSynchronousStateUpdates");
markFlagAsAccessed(25, "enableSynchronousStateUpdates");
flagValue = currentProvider_->enableSynchronousStateUpdates();
enableSynchronousStateUpdates_ = flagValue;
@ -488,7 +506,7 @@ bool ReactNativeFeatureFlagsAccessor::enableUIConsistency() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(25, "enableUIConsistency");
markFlagAsAccessed(26, "enableUIConsistency");
flagValue = currentProvider_->enableUIConsistency();
enableUIConsistency_ = flagValue;
@ -506,7 +524,7 @@ bool ReactNativeFeatureFlagsAccessor::enableViewRecycling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(26, "enableViewRecycling");
markFlagAsAccessed(27, "enableViewRecycling");
flagValue = currentProvider_->enableViewRecycling();
enableViewRecycling_ = flagValue;
@ -524,7 +542,7 @@ bool ReactNativeFeatureFlagsAccessor::excludeYogaFromRawProps() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(27, "excludeYogaFromRawProps");
markFlagAsAccessed(28, "excludeYogaFromRawProps");
flagValue = currentProvider_->excludeYogaFromRawProps();
excludeYogaFromRawProps_ = flagValue;
@ -542,7 +560,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMappingOfEventPrioritiesBetweenFabricAn
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(28, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
markFlagAsAccessed(29, "fixMappingOfEventPrioritiesBetweenFabricAndReact");
flagValue = currentProvider_->fixMappingOfEventPrioritiesBetweenFabricAndReact();
fixMappingOfEventPrioritiesBetweenFabricAndReact_ = flagValue;
@ -560,7 +578,7 @@ bool ReactNativeFeatureFlagsAccessor::fixMountingCoordinatorReportedPendingTrans
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(29, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
markFlagAsAccessed(30, "fixMountingCoordinatorReportedPendingTransactionsOnAndroid");
flagValue = currentProvider_->fixMountingCoordinatorReportedPendingTransactionsOnAndroid();
fixMountingCoordinatorReportedPendingTransactionsOnAndroid_ = flagValue;
@ -578,7 +596,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledDebug() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(30, "fuseboxEnabledDebug");
markFlagAsAccessed(31, "fuseboxEnabledDebug");
flagValue = currentProvider_->fuseboxEnabledDebug();
fuseboxEnabledDebug_ = flagValue;
@ -596,7 +614,7 @@ bool ReactNativeFeatureFlagsAccessor::fuseboxEnabledRelease() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(31, "fuseboxEnabledRelease");
markFlagAsAccessed(32, "fuseboxEnabledRelease");
flagValue = currentProvider_->fuseboxEnabledRelease();
fuseboxEnabledRelease_ = flagValue;
@ -614,7 +632,7 @@ bool ReactNativeFeatureFlagsAccessor::initEagerTurboModulesOnNativeModulesQueueA
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(32, "initEagerTurboModulesOnNativeModulesQueueAndroid");
markFlagAsAccessed(33, "initEagerTurboModulesOnNativeModulesQueueAndroid");
flagValue = currentProvider_->initEagerTurboModulesOnNativeModulesQueueAndroid();
initEagerTurboModulesOnNativeModulesQueueAndroid_ = flagValue;
@ -632,7 +650,7 @@ bool ReactNativeFeatureFlagsAccessor::lazyAnimationCallbacks() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(33, "lazyAnimationCallbacks");
markFlagAsAccessed(34, "lazyAnimationCallbacks");
flagValue = currentProvider_->lazyAnimationCallbacks();
lazyAnimationCallbacks_ = flagValue;
@ -650,7 +668,7 @@ bool ReactNativeFeatureFlagsAccessor::loadVectorDrawablesOnImages() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(34, "loadVectorDrawablesOnImages");
markFlagAsAccessed(35, "loadVectorDrawablesOnImages");
flagValue = currentProvider_->loadVectorDrawablesOnImages();
loadVectorDrawablesOnImages_ = flagValue;
@ -668,7 +686,7 @@ bool ReactNativeFeatureFlagsAccessor::setAndroidLayoutDirection() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(35, "setAndroidLayoutDirection");
markFlagAsAccessed(36, "setAndroidLayoutDirection");
flagValue = currentProvider_->setAndroidLayoutDirection();
setAndroidLayoutDirection_ = flagValue;
@ -686,7 +704,7 @@ bool ReactNativeFeatureFlagsAccessor::traceTurboModulePromiseRejectionsOnAndroid
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(36, "traceTurboModulePromiseRejectionsOnAndroid");
markFlagAsAccessed(37, "traceTurboModulePromiseRejectionsOnAndroid");
flagValue = currentProvider_->traceTurboModulePromiseRejectionsOnAndroid();
traceTurboModulePromiseRejectionsOnAndroid_ = flagValue;
@ -704,7 +722,7 @@ bool ReactNativeFeatureFlagsAccessor::useAlwaysAvailableJSErrorHandling() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(37, "useAlwaysAvailableJSErrorHandling");
markFlagAsAccessed(38, "useAlwaysAvailableJSErrorHandling");
flagValue = currentProvider_->useAlwaysAvailableJSErrorHandling();
useAlwaysAvailableJSErrorHandling_ = flagValue;
@ -722,7 +740,7 @@ bool ReactNativeFeatureFlagsAccessor::useFabricInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(38, "useFabricInterop");
markFlagAsAccessed(39, "useFabricInterop");
flagValue = currentProvider_->useFabricInterop();
useFabricInterop_ = flagValue;
@ -740,7 +758,7 @@ bool ReactNativeFeatureFlagsAccessor::useImmediateExecutorInAndroidBridgeless()
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(39, "useImmediateExecutorInAndroidBridgeless");
markFlagAsAccessed(40, "useImmediateExecutorInAndroidBridgeless");
flagValue = currentProvider_->useImmediateExecutorInAndroidBridgeless();
useImmediateExecutorInAndroidBridgeless_ = flagValue;
@ -758,7 +776,7 @@ bool ReactNativeFeatureFlagsAccessor::useNativeViewConfigsInBridgelessMode() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(40, "useNativeViewConfigsInBridgelessMode");
markFlagAsAccessed(41, "useNativeViewConfigsInBridgelessMode");
flagValue = currentProvider_->useNativeViewConfigsInBridgelessMode();
useNativeViewConfigsInBridgelessMode_ = flagValue;
@ -776,7 +794,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimisedViewPreallocationOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(41, "useOptimisedViewPreallocationOnAndroid");
markFlagAsAccessed(42, "useOptimisedViewPreallocationOnAndroid");
flagValue = currentProvider_->useOptimisedViewPreallocationOnAndroid();
useOptimisedViewPreallocationOnAndroid_ = flagValue;
@ -794,7 +812,7 @@ bool ReactNativeFeatureFlagsAccessor::useOptimizedEventBatchingOnAndroid() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(42, "useOptimizedEventBatchingOnAndroid");
markFlagAsAccessed(43, "useOptimizedEventBatchingOnAndroid");
flagValue = currentProvider_->useOptimizedEventBatchingOnAndroid();
useOptimizedEventBatchingOnAndroid_ = flagValue;
@ -812,7 +830,7 @@ bool ReactNativeFeatureFlagsAccessor::useRuntimeShadowNodeReferenceUpdate() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(43, "useRuntimeShadowNodeReferenceUpdate");
markFlagAsAccessed(44, "useRuntimeShadowNodeReferenceUpdate");
flagValue = currentProvider_->useRuntimeShadowNodeReferenceUpdate();
useRuntimeShadowNodeReferenceUpdate_ = flagValue;
@ -830,7 +848,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModuleInterop() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(44, "useTurboModuleInterop");
markFlagAsAccessed(45, "useTurboModuleInterop");
flagValue = currentProvider_->useTurboModuleInterop();
useTurboModuleInterop_ = flagValue;
@ -848,7 +866,7 @@ bool ReactNativeFeatureFlagsAccessor::useTurboModules() {
// be accessing the provider multiple times but the end state of this
// instance and the returned flag value would be the same.
markFlagAsAccessed(45, "useTurboModules");
markFlagAsAccessed(46, "useTurboModules");
flagValue = currentProvider_->useTurboModules();
useTurboModules_ = flagValue;

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<1efdbd02124b25d6599e4867f08c69c9>>
* @generated SignedSource<<efa3dd4c855d6b737a0620279a176f7a>>
*/
/**
@ -47,6 +47,7 @@ class ReactNativeFeatureFlagsAccessor {
bool enableFabricLogs();
bool enableFabricRenderer();
bool enableFabricRendererExclusively();
bool enableFixForViewCommandRace();
bool enableGranularShadowTreeStateReconciliation();
bool enableIOSViewClipToPaddingBox();
bool enableLayoutAnimationsOnAndroid();
@ -89,7 +90,7 @@ class ReactNativeFeatureFlagsAccessor {
std::unique_ptr<ReactNativeFeatureFlagsProvider> currentProvider_;
bool wasOverridden_;
std::array<std::atomic<const char*>, 46> accessedFeatureFlags_;
std::array<std::atomic<const char*>, 47> accessedFeatureFlags_;
std::atomic<std::optional<bool>> commonTestFlag_;
std::atomic<std::optional<bool>> allowRecursiveCommitsWithSynchronousMountOnAndroid_;
@ -106,6 +107,7 @@ class ReactNativeFeatureFlagsAccessor {
std::atomic<std::optional<bool>> enableFabricLogs_;
std::atomic<std::optional<bool>> enableFabricRenderer_;
std::atomic<std::optional<bool>> enableFabricRendererExclusively_;
std::atomic<std::optional<bool>> enableFixForViewCommandRace_;
std::atomic<std::optional<bool>> enableGranularShadowTreeStateReconciliation_;
std::atomic<std::optional<bool>> enableIOSViewClipToPaddingBox_;
std::atomic<std::optional<bool>> enableLayoutAnimationsOnAndroid_;

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<eb86662c826d52dc1fb97d1bdb95766a>>
* @generated SignedSource<<cf2b4e93ed4529bc939488d176e87f20>>
*/
/**
@ -87,6 +87,10 @@ class ReactNativeFeatureFlagsDefaults : public ReactNativeFeatureFlagsProvider {
return false;
}
bool enableFixForViewCommandRace() override {
return false;
}
bool enableGranularShadowTreeStateReconciliation() override {
return false;
}

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9038e8340a349ed8f4abee66555e98d3>>
* @generated SignedSource<<8633a91586017a22d562b1ba022a6586>>
*/
/**
@ -40,6 +40,7 @@ class ReactNativeFeatureFlagsProvider {
virtual bool enableFabricLogs() = 0;
virtual bool enableFabricRenderer() = 0;
virtual bool enableFabricRendererExclusively() = 0;
virtual bool enableFixForViewCommandRace() = 0;
virtual bool enableGranularShadowTreeStateReconciliation() = 0;
virtual bool enableIOSViewClipToPaddingBox() = 0;
virtual bool enableLayoutAnimationsOnAndroid() = 0;

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<def34bae6aeac9fbb98d2e3a89ffde32>>
* @generated SignedSource<<6736518a1e588cac9c28d3c0c91af280>>
*/
/**
@ -119,6 +119,11 @@ bool NativeReactNativeFeatureFlags::enableFabricRendererExclusively(
return ReactNativeFeatureFlags::enableFabricRendererExclusively();
}
bool NativeReactNativeFeatureFlags::enableFixForViewCommandRace(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableFixForViewCommandRace();
}
bool NativeReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation(
jsi::Runtime& /*runtime*/) {
return ReactNativeFeatureFlags::enableGranularShadowTreeStateReconciliation();

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a31d80cd12e1705dc076068e64d36a01>>
* @generated SignedSource<<5c391f7691147482153fa3cc4e3870aa>>
*/
/**
@ -67,6 +67,8 @@ class NativeReactNativeFeatureFlags
bool enableFabricRendererExclusively(jsi::Runtime& runtime);
bool enableFixForViewCommandRace(jsi::Runtime& runtime);
bool enableGranularShadowTreeStateReconciliation(jsi::Runtime& runtime);
bool enableIOSViewClipToPaddingBox(jsi::Runtime& runtime);

View File

@ -316,7 +316,19 @@ void Scheduler::uiManagerDidDispatchCommand(
if (delegate_ != nullptr) {
auto shadowView = ShadowView(*shadowNode);
delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
if (ReactNativeFeatureFlags::enableFixForViewCommandRace()) {
runtimeScheduler_->scheduleRenderingUpdate(
shadowNode->getSurfaceId(),
[delegate = delegate_,
shadowView = std::move(shadowView),
commandName,
args]() {
delegate->schedulerDidDispatchCommand(
shadowView, commandName, args);
});
} else {
delegate_->schedulerDidDispatchCommand(shadowView, commandName, args);
}
}
}

View File

@ -171,6 +171,15 @@ const definitions: FeatureFlagDefinitions = {
purpose: 'release',
},
},
enableFixForViewCommandRace: {
defaultValue: false,
metadata: {
dateAdded: '2024-11-14',
description:
'Synchronise the view command dispatching with mounting of new transaction',
purpose: 'experimentation',
},
},
enableGranularShadowTreeStateReconciliation: {
defaultValue: false,
metadata: {

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<625f763d82aeca846224a67d9580f1b7>>
* @generated SignedSource<<5630ca490d52be748f6de41e007d4dd4>>
* @flow strict
*/
@ -66,6 +66,7 @@ export type ReactNativeFeatureFlags = {
enableFabricLogs: Getter<boolean>,
enableFabricRenderer: Getter<boolean>,
enableFabricRendererExclusively: Getter<boolean>,
enableFixForViewCommandRace: Getter<boolean>,
enableGranularShadowTreeStateReconciliation: Getter<boolean>,
enableIOSViewClipToPaddingBox: Getter<boolean>,
enableLayoutAnimationsOnAndroid: Getter<boolean>,
@ -248,6 +249,10 @@ export const enableFabricRenderer: Getter<boolean> = createNativeFlagGetter('ena
* When the app is completely migrated to Fabric, set this flag to true to disable parts of Paper infrastructure that are not needed anymore but consume memory and CPU. Specifically, UIViewOperationQueue and EventDispatcherImpl will no longer work as they will not subscribe to ReactChoreographer for updates.
*/
export const enableFabricRendererExclusively: Getter<boolean> = createNativeFlagGetter('enableFabricRendererExclusively', false);
/**
* Synchronise the view command dispatching with mounting of new transaction
*/
export const enableFixForViewCommandRace: Getter<boolean> = createNativeFlagGetter('enableFixForViewCommandRace', false);
/**
* When enabled, the renderer would only fail commits when they propagate state and the last commit that updated state changed before committing.
*/

View File

@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a1cbc6a91551de0008c070adb5d805fd>>
* @generated SignedSource<<5fb648131bb9739c6d47de4ad59bdd6f>>
* @flow strict
*/
@ -39,6 +39,7 @@ export interface Spec extends TurboModule {
+enableFabricLogs?: () => boolean;
+enableFabricRenderer?: () => boolean;
+enableFabricRendererExclusively?: () => boolean;
+enableFixForViewCommandRace?: () => boolean;
+enableGranularShadowTreeStateReconciliation?: () => boolean;
+enableIOSViewClipToPaddingBox?: () => boolean;
+enableLayoutAnimationsOnAndroid?: () => boolean;