From 09fd7528b65c31d0f954d7ebfd163cbbcfc5b574 Mon Sep 17 00:00:00 2001 From: Fred Liu Date: Tue, 6 Aug 2024 13:48:30 -0700 Subject: [PATCH] Revert D60648823: ship use of passive effects in Animated Differential Revision: D60648823 Original commit changeset: 8efa1dac2a42 Original Phabricator Diff: D60648823 fbshipit-source-id: 6deb00df29f8e2ba8cb58d252c40144d2bed229e --- .../Libraries/Animated/useAnimatedProps.js | 55 ++++++++++++++++++- .../ReactNativeFeatureFlags.config.js | 5 ++ .../featureflags/ReactNativeFeatureFlags.js | 8 ++- 3 files changed, 65 insertions(+), 3 deletions(-) diff --git a/packages/react-native/Libraries/Animated/useAnimatedProps.js b/packages/react-native/Libraries/Animated/useAnimatedProps.js index 80ff63f556c..97472a9f53c 100644 --- a/packages/react-native/Libraries/Animated/useAnimatedProps.js +++ b/packages/react-native/Libraries/Animated/useAnimatedProps.js @@ -52,6 +52,11 @@ export default function useAnimatedProps( const useSetNativePropsInNativeAnimationsInFabric = ReactNativeFeatureFlags.shouldUseSetNativePropsInNativeAnimationsInFabric(); + const useAnimatedPropsLifecycle = + ReactNativeFeatureFlags.usePassiveEffectsForAnimations() + ? useAnimatedPropsLifecycle_passiveEffects + : useAnimatedPropsLifecycle_layoutEffects; + useAnimatedPropsLifecycle(node); // TODO: This "effect" does three things: @@ -194,9 +199,55 @@ function reduceAnimatedProps( * 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. - * */ -function useAnimatedPropsLifecycle(node: AnimatedProps): void { +function useAnimatedPropsLifecycle_layoutEffects(node: AnimatedProps): void { + const prevNodeRef = useRef(null); + const isUnmountingRef = useRef(false); + + useEffect(() => { + // It is ok for multiple components to call `flushQueue` because it noops + // if the queue is empty. When multiple animated components are mounted at + // the same time. Only first component flushes the queue and the others will noop. + NativeAnimatedHelper.API.flushQueue(); + }); + + useLayoutEffect(() => { + isUnmountingRef.current = false; + return () => { + isUnmountingRef.current = true; + }; + }, []); + + useLayoutEffect(() => { + node.__attach(); + if (prevNodeRef.current != null) { + const prevNode = prevNodeRef.current; + // TODO: Stop restoring default values (unless `reset` is called). + prevNode.__restoreDefaultValues(); + prevNode.__detach(); + prevNodeRef.current = null; + } + return () => { + if (isUnmountingRef.current) { + // NOTE: Do not restore default values on unmount, see D18197735. + node.__detach(); + } else { + prevNodeRef.current = node; + } + }; + }, [node]); +} + +/** + * Manages the lifecycle of the supplied `AnimatedProps` by invoking `__attach` + * and `__detach`. However, this is more complicated because `AnimatedProps` + * 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 { const prevNodeRef = useRef(null); const isUnmountingRef = useRef(false); diff --git a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js index 6abdbd37ddf..da1cf30e46e 100644 --- a/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js +++ b/packages/react-native/scripts/featureflags/ReactNativeFeatureFlags.config.js @@ -300,6 +300,11 @@ const definitions: FeatureFlagDefinitions = { description: 'Enables use of setNativeProps in Native driven animations in Fabric.', }, + usePassiveEffectsForAnimations: { + defaultValue: false, + description: + 'Enable a variant of useAnimatedPropsLifecycle hook that constructs the animation graph in passive effect instead of layout effect', + }, useRefsForTextInputState: { defaultValue: false, description: diff --git a/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js b/packages/react-native/src/private/featureflags/ReactNativeFeatureFlags.js index 54e028c0e94..258dbbb86dc 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<> + * @generated SignedSource<<0b57a2e853d1f2872ddf0c0b610805bb>> * @flow strict-local */ @@ -36,6 +36,7 @@ export type ReactNativeFeatureFlagsJsOnly = { shouldUseRemoveClippedSubviewsAsDefaultOnIOS: Getter, shouldUseSetNativePropsInFabric: Getter, shouldUseSetNativePropsInNativeAnimationsInFabric: Getter, + usePassiveEffectsForAnimations: Getter, useRefsForTextInputState: Getter, }; @@ -139,6 +140,11 @@ 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 + */ +export const usePassiveEffectsForAnimations: Getter = createJavaScriptFlagGetter('usePassiveEffectsForAnimations', false); + /** * Enable a variant of TextInput that moves some state to refs to avoid unnecessary re-renders */