diff --git a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java index 40ff4f4fba312a..843bc6acc2dd07 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java +++ b/ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java @@ -109,4 +109,10 @@ public class ReactFeatureFlags { * border. */ public static boolean enableCloseVisibleGapBetweenPaths = true; + + /** + * Allow fix in layout animation to drop delete...create mutations which could cause missing view + * state in Fabric SurfaceMountingManager. + */ + public static boolean reduceDeleteCreateMutationLayoutAnimation = true; } diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp index b4caab94d44e41..2d16a00596aca5 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.cpp @@ -144,6 +144,11 @@ void LayoutAnimationKeyFrameManager::setComponentDescriptorRegistry( componentDescriptorRegistry_ = componentDescriptorRegistry; } +void LayoutAnimationKeyFrameManager::setReduceDeleteCreateMutation( + const bool reduceDeleteCreateMutation) { + reduceDeleteCreateMutation_ = reduceDeleteCreateMutation; +} + bool LayoutAnimationKeyFrameManager::shouldAnimateFrame() const { std::lock_guard lock(currentAnimationMutex_); return currentAnimation_ || !inflightAnimations_.empty(); @@ -732,6 +737,40 @@ LayoutAnimationKeyFrameManager::pullTransaction( auto finalConflictingMutations = ShadowViewMutationList{}; for (auto &keyFrame : conflictingAnimations) { + // Special-case: if the next conflicting animation contain "delete", + // while the final mutation has the same tag with "create", we should + // remove both the delete and create as they have no effect when + // combined in the same frame. The Fabric mount layer assumes no such + // combinations in the final mutations either. + if (reduceDeleteCreateMutation_) { + for (auto itMutation = immediateMutations.begin(); + itMutation != immediateMutations.end();) { + auto &mutation = *itMutation; + bool hasCreateMutationDeletedWithSameTag = false; + if (mutation.newChildShadowView.tag == keyFrame.tag && + mutation.type == ShadowViewMutation::Create) { + for (auto itKeyFrame = keyFrame.finalMutationsForKeyFrame.begin(); + itKeyFrame != keyFrame.finalMutationsForKeyFrame.end();) { + auto &conflictFinalMutation = *itKeyFrame; + if (conflictFinalMutation.type == ShadowViewMutation::Delete) { + itKeyFrame = + keyFrame.finalMutationsForKeyFrame.erase(itKeyFrame); + hasCreateMutationDeletedWithSameTag = true; + break; + } else { + itKeyFrame++; + } + } + } + + if (hasCreateMutationDeletedWithSameTag) { + itMutation = immediateMutations.erase(itMutation); + } else { + itMutation++; + } + } + } + // Special-case: if we have some (1) ongoing UPDATE animation, // (2) it conflicted with a new MOVE operation (REMOVE+INSERT) // without another corresponding UPDATE, we should re-queue the diff --git a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h index 56d283a3f22038..5bfea6f1b6e42a 100644 --- a/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h +++ b/ReactCommon/react/renderer/animations/LayoutAnimationKeyFrameManager.h @@ -56,6 +56,8 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, void setComponentDescriptorRegistry(SharedComponentDescriptorRegistry const & componentDescriptorRegistry) override; + void setReduceDeleteCreateMutation(bool reduceDeleteCreateMutation) override; + // TODO: add SurfaceId to this API as well bool shouldAnimateFrame() const override; @@ -151,6 +153,7 @@ class LayoutAnimationKeyFrameManager : public UIManagerAnimationDelegate, mutable std::mutex surfaceIdsToStopMutex_; mutable butter::set surfaceIdsToStop_{}; bool skipInvalidatedKeyFrames_{false}; + bool reduceDeleteCreateMutation_{false}; /* * Feature flag that forces a crash if component descriptor for shadow view diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.cpp b/ReactCommon/react/renderer/scheduler/Scheduler.cpp index b9746e605fc0a7..787b9733807909 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.cpp +++ b/ReactCommon/react/renderer/scheduler/Scheduler.cpp @@ -118,18 +118,23 @@ Scheduler::Scheduler( uiManager->registerCommitHook(*commitHook); } - if (animationDelegate != nullptr) { - animationDelegate->setComponentDescriptorRegistry( - componentDescriptorRegistry_); - } - uiManager_->setAnimationDelegate(animationDelegate); - #ifdef ANDROID removeOutstandingSurfacesOnDestruction_ = true; + reduceDeleteCreateMutationLayoutAnimation_ = reactNativeConfig_->getBool( + "react_fabric:reduce_delete_create_mutation_layout_animation_android"); #else removeOutstandingSurfacesOnDestruction_ = reactNativeConfig_->getBool( "react_fabric:remove_outstanding_surfaces_on_destruction_ios"); + reduceDeleteCreateMutationLayoutAnimation_ = true; #endif + + if (animationDelegate != nullptr) { + animationDelegate->setComponentDescriptorRegistry( + componentDescriptorRegistry_); + animationDelegate->setReduceDeleteCreateMutation( + reduceDeleteCreateMutationLayoutAnimation_); + } + uiManager_->setAnimationDelegate(animationDelegate); } Scheduler::~Scheduler() { diff --git a/ReactCommon/react/renderer/scheduler/Scheduler.h b/ReactCommon/react/renderer/scheduler/Scheduler.h index 23199d4bff57c5..466dd8b9e4054d 100644 --- a/ReactCommon/react/renderer/scheduler/Scheduler.h +++ b/ReactCommon/react/renderer/scheduler/Scheduler.h @@ -146,6 +146,7 @@ class Scheduler final : public UIManagerDelegate { * Temporary flags. */ bool removeOutstandingSurfacesOnDestruction_{false}; + bool reduceDeleteCreateMutationLayoutAnimation_{false}; }; } // namespace react diff --git a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h index d20f43c235065a..780346cb6cb091 100644 --- a/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h +++ b/ReactCommon/react/renderer/uimanager/UIManagerAnimationDelegate.h @@ -36,6 +36,14 @@ class UIManagerAnimationDelegate { virtual void setComponentDescriptorRegistry( const SharedComponentDescriptorRegistry &componentDescriptorRegistry) = 0; + /** + * Set Animation flags for dropping delete and create mutations + * + * @param reduceDeleteCreateMutation + */ + virtual void setReduceDeleteCreateMutation( + bool reduceDeleteCreateMutation) = 0; + /** * Only needed on Android to drive animations. */