React-native-reanimated: [Question] Best practice for using Animated nodes in functional components / hooks

Created on 5 May 2019  路  9Comments  路  Source: software-mansion/react-native-reanimated

When using animated nodes in functional components or inside custom hooks, simply assigning them to a variable (in every render) seems to work fine, but is there a significant cost or other issue to it?

I started out using useMemo like this, because I thought that would match what I was doing before with class fields:

const saw = useMemo(() => Animated.sub(1, ramp), [ramp]);

But maybe it's not neccessary at all?

Most helpful comment

Thanks to this video https://www.youtube.com/watch?v=1Q9efh7OcR8 from William Candillon, I managed to get my head around using the library with functional components. Here is my solution highly inspired from the video.

// ...
import Animated, { Easing } from "react-native-reanimated";
import { runTiming, bInterpolate } from "react-native-redash";
import { useMemoOne } from "use-memo-one";

const {
  set,
  Value,
  Clock,
  useCode
};

const MyComponent:React.FC<Props> = ({isVisible, action}) => {
  // using useMemoOne from use-memo-one
  const { animation, clock } = useMemoOne(
    () => ({
      animation: new Value(isVisible ? 0 : 1),
      clock: new Clock()
    }),
    [isVisible]
  );

  useCode(
    set(
      animation,
      // runTiming is a helper from react-native-redash
      runTiming(clock, animation, {
        toValue: selected ? 1 : 0,
        duration: 250,
        easing: Easing.inOut(Easing.ease)
      })
    ),
    [animation]
  );

  // interpolated value, bInterpolate is a helper from react-native-redash
  const transY = bInterpolate(animation, -20, 0);

  return (
    <TouchableOpacity onPress={action}>
      <Animated.View
        style={{ transform: [{ translateY: transY }] }}
      >
        <Text>I slide down or up based on isVisible value</Text>
      </Animated.View>
    </TouchableOpacity>
  );
}

when isVisible change, the animation is restarted and the animation value get interpolated again in the other direction, from 0 to 1, or 1 to 0 depending on the visibility flag.

I hope this would help people having difficulty with functional component implementation and reanimated.

All 9 comments

I recently experienced huge frame drops in JS due to creating animated nodes in render. I couldn't migrate to hooks, so had to write a custom memoize function. But for function components, I think using useMemo is great

@karlsander did you find out how to organise the code with functional components? I am struggling with the same issue, a simple example of a functional component triggering an animation with the press of a button would be great.

I'm using this kind of _class instance variable_ workaround Dan Abramov suggested:

function useVar(cb) {
  const ref = useRef(null);
  if (ref.current === null) {
    ref.current = cb();
  }

  return ref.current;
}

const opacity = useVar(() => new Animated.Value(1));

But for everything else which requires some kind of dependencies useMemo works best. And it is definitely necessary to use it because each evaluation of Animated.* goes through the bridge.

yes. useMemo (often with [])has worked out well for me, so I would recomend that

are you guys feeling a bit of delay when implementing heavily coded animations using functional component?

Thanks to this video https://www.youtube.com/watch?v=1Q9efh7OcR8 from William Candillon, I managed to get my head around using the library with functional components. Here is my solution highly inspired from the video.

// ...
import Animated, { Easing } from "react-native-reanimated";
import { runTiming, bInterpolate } from "react-native-redash";
import { useMemoOne } from "use-memo-one";

const {
  set,
  Value,
  Clock,
  useCode
};

const MyComponent:React.FC<Props> = ({isVisible, action}) => {
  // using useMemoOne from use-memo-one
  const { animation, clock } = useMemoOne(
    () => ({
      animation: new Value(isVisible ? 0 : 1),
      clock: new Clock()
    }),
    [isVisible]
  );

  useCode(
    set(
      animation,
      // runTiming is a helper from react-native-redash
      runTiming(clock, animation, {
        toValue: selected ? 1 : 0,
        duration: 250,
        easing: Easing.inOut(Easing.ease)
      })
    ),
    [animation]
  );

  // interpolated value, bInterpolate is a helper from react-native-redash
  const transY = bInterpolate(animation, -20, 0);

  return (
    <TouchableOpacity onPress={action}>
      <Animated.View
        style={{ transform: [{ translateY: transY }] }}
      >
        <Text>I slide down or up based on isVisible value</Text>
      </Animated.View>
    </TouchableOpacity>
  );
}

when isVisible change, the animation is restarted and the animation value get interpolated again in the other direction, from 0 to 1, or 1 to 0 depending on the visibility flag.

I hope this would help people having difficulty with functional component implementation and reanimated.

@alwex What about gesture based animations ? because the state change at the middle of running animations seems to interrupt the animation 馃
or at least in my case 馃槃
before I get to know useMemoOne , I used to define some animation values outside of the component function so they won't get rerender and reset every time state changes

I'm using this kind of _class instance variable_ workaround Dan Abramov suggested:

function useVar(cb) {
  const ref = useRef(null);
  if (ref.current === null) {
    ref.current = cb();
  }

  return ref.current;
}

const opacity = useVar(() => new Animated.Value(1));

But for everything else which requires some kind of dependencies useMemo works best. And it is definitely necessary to use it because each evaluation of Animated.* goes through the bridge.

How are you dealing with TS? The .current here is read-only param

How are you dealing with TS? The .current here is read-only param

For Animated Values only I used to have useAnimatedValue hook:

type Value = string | number | boolean;

export function useAnimatedValue<T extends Value>(value: T) {
  return React.useMemo<Animated.Value<T>>(() => {
    return new Animated.Value(value);
  }, []);
}

If you wish to use ref, just ts-ignore the assignments.
The actual assignment to "current" isn't safe only in AsyncMode. But still, no one knows for sure it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

robertgonzales picture robertgonzales  路  3Comments

nextriot picture nextriot  路  3Comments

sa8ab picture sa8ab  路  3Comments

jwhscholten picture jwhscholten  路  4Comments

zxccvvv picture zxccvvv  路  3Comments