From 1c7fd8baa857c4bff6efcebcb617dad2806338ca Mon Sep 17 00:00:00 2001 From: Tim Yung Date: Thu, 12 Sep 2024 22:05:02 -0700 Subject: [PATCH] Animated: Switch to `useInsertionEffect` (#46474) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/46474 Changes `useAnimatedPropsLifecycle_passiveEffects` (the experimental lifecycle management in `useAnimatedProps`) to `useInsertionEffect` instead of `useEffect`. This has a few benefits: 1. Prevents any Activity detach and attach events from triggering Animated lifecycle work (which adds to effect mounting and unmounting time). 2. Fixes batching of native Animated operations across components. (Since we flush the operation queue in a passive effect, enqueueing operations in a passive effect causes weird interleaving of enqueuing and flushing.) 3. Enables animations to be started in layout effects. Changelog: [Internal] Reviewed By: bvanderhoof Differential Revision: D62602336 fbshipit-source-id: 3f6412387d87d4ea4135a29893135b4a64638809 --- .../Libraries/Animated/useAnimatedProps.js | 13 ++++++------- .../featureflags/ReactNativeFeatureFlags.config.js | 4 ++-- .../private/featureflags/ReactNativeFeatureFlags.js | 8 ++++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/packages/react-native/Libraries/Animated/useAnimatedProps.js b/packages/react-native/Libraries/Animated/useAnimatedProps.js index 1093e42ae61..435ea7dd058 100644 --- a/packages/react-native/Libraries/Animated/useAnimatedProps.js +++ b/packages/react-native/Libraries/Animated/useAnimatedProps.js @@ -22,6 +22,7 @@ import AnimatedValue from './nodes/AnimatedValue'; import { useCallback, useEffect, + useInsertionEffect, useLayoutEffect, useMemo, useReducer, @@ -74,8 +75,8 @@ export default function useAnimatedProps( ReactNativeFeatureFlags.shouldUseSetNativePropsInNativeAnimationsInFabric(); const useAnimatedPropsLifecycle = - ReactNativeFeatureFlags.usePassiveEffectsForAnimations() - ? useAnimatedPropsLifecycle_passiveEffects + ReactNativeFeatureFlags.useInsertionEffectsForAnimations() + ? useAnimatedPropsLifecycle_insertionEffects : useAnimatedPropsLifecycle_layoutEffects; useAnimatedPropsLifecycle(node); @@ -318,10 +319,8 @@ function useAnimatedPropsLifecycle_layoutEffects(node: AnimatedProps): void { * uses reference counting to determine when to recursively detach its children * nodes. So in order to optimize this, we avoid detaching until the next attach * unless we are unmounting. - * - * NOTE: unlike `useAnimatedPropsLifecycle_layoutEffects`, this version uses passive effects to setup animation graph. */ -function useAnimatedPropsLifecycle_passiveEffects(node: AnimatedProps): void { +function useAnimatedPropsLifecycle_insertionEffects(node: AnimatedProps): void { const prevNodeRef = useRef(null); const isUnmountingRef = useRef(false); @@ -332,14 +331,14 @@ function useAnimatedPropsLifecycle_passiveEffects(node: AnimatedProps): void { NativeAnimatedHelper.API.flushQueue(); }); - useEffect(() => { + useInsertionEffect(() => { isUnmountingRef.current = false; return () => { isUnmountingRef.current = true; }; }, []); - useEffect(() => { + useInsertionEffect(() => { node.__attach(); let drivenAnimationEndedListener: ?EventSubscription = null; diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 3792f948201..7a08292b2fd 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -342,10 +342,10 @@ const definitions: FeatureFlagDefinitions = { description: 'Enables use of setNativeProps in Native driven animations in Fabric.', }, - usePassiveEffectsForAnimations: { + useInsertionEffectsForAnimations: { defaultValue: false, description: - 'Enable a variant of useAnimatedPropsLifecycle hook that constructs the animation graph in passive effect instead of layout effect', + 'Changes construction of the animation graph to `useInsertionEffect` instead of `useLayoutEffect`.', }, useRefsForTextInputState: { defaultValue: false, diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 9c97dc3812d..97cb47341e2 100644 --- a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js +++ b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js @@ -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<<2333bd2bbcab9d50e4cb23d1b7d42021>> + * @generated SignedSource<> * @flow strict-local */ @@ -39,7 +39,7 @@ export type ReactNativeFeatureFlagsJsOnly = { shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter, shouldUseSetNativePropsInFabric: Getter, shouldUseSetNativePropsInNativeAnimationsInFabric: Getter, - usePassiveEffectsForAnimations: Getter, + useInsertionEffectsForAnimations: Getter, useRefsForTextInputState: Getter, }; @@ -165,9 +165,9 @@ export const shouldUseSetNativePropsInFabric: Getter = createJavaScript export const shouldUseSetNativePropsInNativeAnimationsInFabric: Getter = createJavaScriptFlagGetter('shouldUseSetNativePropsInNativeAnimationsInFabric', false); /** - * Enable a variant of useAnimatedPropsLifecycle hook that constructs the animation graph in passive effect instead of layout effect + * Changes construction of the animation graph to `useInsertionEffect` instead of `useLayoutEffect`. */ -export const usePassiveEffectsForAnimations: Getter = createJavaScriptFlagGetter('usePassiveEffectsForAnimations', false); +export const useInsertionEffectsForAnimations: Getter = createJavaScriptFlagGetter('useInsertionEffectsForAnimations', false); /** * Enable a variant of TextInput that moves some state to refs to avoid unnecessary re-renders