alpha 9 breaks withSpring from boolean state
See original GitHub issueDescription
I have created a custom “button” component that scales down when pressed. This button smoothly scales down and back to original scale with a spring animation. It’s built very declaratively, in a way that I just use withSpring(isPressed ? 0.9 : 1).
After upgrading to alpha 9, my animation didn’t run anymore. I figured, maybe the velocity was wrong? So instead of leaving the velocity in the spring config undefined (which meant it would use the current velocity from still-settling animations), I explicitly set it to 0. That didn’t change anything.
Then I noticed, that it’s not always the case that they don’t run, sometimes they run, sometimes they don’t. I still have no idea what’s causing this.
I thought maybe the boolean value update is triggered multiple times, which causes the useAnimatedStyle worklet to run more than once, which essentially assigns a withSpring animation more than once and therefore skips the animation.
Am I doing something wrong? I’ve tried to play around with the spring properties, such as overshootClamping, restDisplacementThreshold and restSpeedThreshold, but that didn’t seem to change anything 🤔
Screenshots
You can see that it started working again after pressing it a couple of times.
Steps To Reproduce
PressableScale.tsx code
import React, { useCallback, useMemo } from 'react';
import { GestureResponderEvent, Pressable, PressableProps, StyleSheet } from 'react-native';
import Reanimated, { useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
export interface PressableScaleProps extends PressableProps {
children: React.ReactNode;
/**
* The value to scale to when the Pressable is being pressed.
* @default 0.95
*/
activeScale?: number;
/**
* The weight physics of this button
* @default 'heavy'
*/
weight?: 'light' | 'medium' | 'heavy';
/**
* Delay for the press-in animation
*/
delayPressIn?: number;
}
const ReanimatedPressable = Reanimated.createAnimatedComponent(Pressable);
/**
* A Pressable that scales down when pressed. Uses the JS Pressability API.
*/
export default function PressableScale(props: PressableScaleProps): React.ReactElement {
const {
activeScale = 0.95,
weight = 'heavy',
onPressIn: _onPressIn,
onPressOut: _onPressOut,
delayPressIn = 0,
children,
style,
...passThroughProps
} = props;
const mass = useMemo(() => {
switch (weight) {
case 'light':
return 0.15;
case 'medium':
return 0.175;
case 'heavy':
default:
return 0.2;
}
}, [weight]);
const isPressedIn = useSharedValue(false);
const springConfig = useMemo<Reanimated.WithSpringConfig>(
() => ({
mass: mass,
damping: 15,
stiffness: 150,
overshootClamping: true,
restDisplacementThreshold: 0.0001,
restSpeedThreshold: 0.0001,
}),
[mass],
);
const touchableStyle = useAnimatedStyle(() => ({ transform: [{ scale: withSpring(isPressedIn.value ? activeScale : 1, springConfig) }] }), [
activeScale,
isPressedIn,
springConfig,
]);
const onPressIn = useCallback(
(event: GestureResponderEvent) => {
isPressedIn.value = true;
_onPressIn?.(event);
},
[_onPressIn, isPressedIn],
);
const onPressOut = useCallback(
(event: GestureResponderEvent) => {
isPressedIn.value = false;
_onPressOut?.(event);
},
[_onPressOut, isPressedIn],
);
return (
<ReanimatedPressable
unstable_pressDelay={delayPressIn}
onPressIn={onPressIn}
onPressOut={onPressOut}
style={[style, touchableStyle]}
{...passThroughProps}>
{children}
</ReanimatedPressable>
);
}
- Install latest REA alpha (9.2)
- Create
PressableScale.tsxwith the code from above - Use
PressableScalesomewhere, in my case it’s in a list (RecyclerListView, but I also noticed it reproduces in a FlatList)
Expected behavior
I expect it to smoothly scale down (run the withSpring animation)
Actual behavior
It jumps & skips the animation.
Package versions
- React: 17.0.1
- React Native: 0.64.0-rc.1 (but also reproduced on 0.63)
- React Native Reanimated: 2.0.0-alpha.9.2
- NodeJS: v14.15.1
Issue Analytics
- State:
- Created 3 years ago
- Comments:5 (1 by maintainers)

Top Related StackOverflow Question
I was able to workaround it by using the
useDerivedValuehookIt seems so. withTiming works… I haven’t tried the other ones.