Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 29 additions & 82 deletions packages/react-strict-dom/src/native/modules/useStyleTransition.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<number> = [0, 1];

function isNumber(num: mixed): num is number {
Expand Down Expand Up @@ -268,53 +261,6 @@ function getAnimation(
});
}

const animatedConfigs = new Map<string, AnimatedConfig>();

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,
Expand Down Expand Up @@ -344,9 +290,8 @@ export function useStyleTransition(style: ReactNativeStyle): ReactNativeStyle {
React.useState<ReactNativeStyle | void>(style);
const [previousStyle, setPreviousStyle] =
React.useState<ReactNativeStyle | void>(undefined);

const [animatedConfig, setAnimatedConfig] =
React.useState<AnimatedConfig | void>(undefined);
const [animatedValue, setAnimatedValue] =
React.useState<ReactNative.Animated.Value | void>(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
Expand All @@ -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 &&
Expand All @@ -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
Expand Down