question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

alpha 9 breaks withSpring from boolean state

See original GitHub issue

Description

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

Here’s a demo (imgur.com)

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>
  );
}
  1. Install latest REA alpha (9.2)
  2. Create PressableScale.tsx with the code from above
  3. Use PressableScale somewhere, 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:closed
  • Created 3 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
vcellucommented, Nov 29, 2020

I was able to workaround it by using the useDerivedValue hook

0reactions
vcelluccicommented, Nov 29, 2020

It seems so. withTiming works… I haven’t tried the other ones.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Update boolean state right with React Hooks
I will create two separate state handlers: one for our boolean state and another one for storing a random number. RendersCounter re-renders ...
Read more >
@react-three/drei - npm
A small hook that sets the css body cursor according to the hover state of a mesh, so that you can give the...
Read more >
USER'S GUIDE 3.3 - PrimeFaces
Maximum X axis value. breakOnNull. FALSE. Boolean. Whether line segments should be broken at null value, fall will join point on either side...
Read more >
Spring Cloud Contract Features
Consumer Side Messaging With Spring Cloud Stream ... to ensure that your build will break when at least one contract in progress remains...
Read more >
Learning the Basics - Gradle User Manual
Project dependencies. Software projects often break up software components into modules to improve maintainability and prevent strong coupling. Modules can ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found