From e4abced195c54698762169658af11fc2ac847d11 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Tue, 8 Jul 2025 10:45:46 -0700 Subject: [PATCH] Revert shared Animated.Value amongst transitions This reverts commit 82a2ba200032c21ae684ca53a6db718b840f3fab. Found to cause cascading re-renders which can delay app startup. --- .../src/native/modules/useStyleTransition.js | 111 +++++------------- 1 file changed, 29 insertions(+), 82 deletions(-) diff --git a/packages/react-strict-dom/src/native/modules/useStyleTransition.js b/packages/react-strict-dom/src/native/modules/useStyleTransition.js index e0b6e285..829b823b 100644 --- a/packages/react-strict-dom/src/native/modules/useStyleTransition.js +++ b/packages/react-strict-dom/src/native/modules/useStyleTransition.js @@ -30,13 +30,6 @@ type TransitionMetadata = $ReadOnly<{ shouldUseNativeDriver: boolean }>; -type AnimatedConfig = { - start: () => void, - dispose: () => void, - value: ReactNative.Animated.Value, - referenceCount: number -}; - const INPUT_RANGE: $ReadOnlyArray = [0, 1]; function isNumber(num: mixed): num is number { @@ -268,53 +261,6 @@ function getAnimation( }); } -const animatedConfigs = new Map(); - -function getOrCreateAnimatedConfig(transitionMetadata: TransitionMetadata) { - const key = JSON.stringify(transitionMetadata); - - const animatedConfig = animatedConfigs.get(key); - if (animatedConfig != null) { - animatedConfig.referenceCount++; - - return animatedConfig; - } - - const animatedValue = new ReactNative.Animated.Value(0); - let hasStarted = false; - let animation; - const newAnimatedConfig = { - referenceCount: 1, - value: animatedValue, - start: () => { - if (hasStarted) { - return; - } - hasStarted = true; - const { delay, duration, timingFunction, shouldUseNativeDriver } = - transitionMetadata; - animation = ReactNative.Animated.sequence([ - ReactNative.Animated.delay(delay), - getAnimation( - animatedValue, - duration, - timingFunction, - shouldUseNativeDriver - ) - ]); - animation.start(); - }, - dispose: () => { - if (--newAnimatedConfig.referenceCount === 0) { - animation?.stop(); - } - } - }; - animatedConfigs.set(key, newAnimatedConfig); - - return newAnimatedConfig; -} - export function useStyleTransition(style: ReactNativeStyle): ReactNativeStyle { const { transitionDelay: _delay, @@ -344,9 +290,8 @@ export function useStyleTransition(style: ReactNativeStyle): ReactNativeStyle { React.useState(style); const [previousStyle, setPreviousStyle] = React.useState(undefined); - - const [animatedConfig, setAnimatedConfig] = - React.useState(undefined); + const [animatedValue, setAnimatedValue] = + React.useState(undefined); // This ref is utilized as a performance optimization so that the effect that contains the // animation trigger only is called when the animated value's identity changes. As far as the effect @@ -373,32 +318,28 @@ export function useStyleTransition(style: ReactNativeStyle): ReactNativeStyle { ]); // effect to trigger a transition - // REMEMBER: it is super important that this effect's dependency array **only** contains the animated config + // REMEMBER: it is super important that this effect's dependency array **only** contains the animated value React.useEffect(() => { - if (animatedConfig == null) { - return; - } - animatedConfig.start(); - animatedConfigs.clear(); - return () => { - animatedConfig.dispose(); - }; - }, [animatedConfig]); + if (animatedValue !== undefined) { + const { delay, duration, timingFunction, shouldUseNativeDriver } = + transitionMetadataRef.current; - const transitionStyleHasChangedResult = transitionStyleHasChanged( - transitionStyle, - currentStyle - ); - - React.useLayoutEffect(() => { - if (transitionStyleHasChangedResult) { - setCurrentStyle(style); - setPreviousStyle(currentStyle); - setAnimatedConfig( - getOrCreateAnimatedConfig(transitionMetadataRef.current) - ); + const animation = ReactNative.Animated.sequence([ + ReactNative.Animated.delay(delay), + getAnimation( + animatedValue, + duration, + timingFunction, + shouldUseNativeDriver + ) + ]); + animation.start(); + + return () => { + animation.stop(); + }; } - }, [currentStyle, style, transitionStyleHasChangedResult]); + }, [animatedValue]); if ( _delay == null && @@ -410,11 +351,17 @@ export function useStyleTransition(style: ReactNativeStyle): ReactNativeStyle { return style; } - if (transitionStyle === undefined) { + if (transitionStyleHasChanged(transitionStyle, currentStyle)) { + setCurrentStyle(style); + setPreviousStyle(currentStyle); + setAnimatedValue(new ReactNative.Animated.Value(0)); + // This commit will be thrown away due to the above state setters so we can bail out early return style; } - const animatedValue = animatedConfig?.value; + if (transitionStyle === undefined) { + return style; + } const outputAnimatedStyle: AnimatedStyle = Object.entries( transitionStyle