React-native-reanimated: After fast refresh and change value not changing animation

Created on 23 Apr 2020  路  5Comments  路  Source: software-mansion/react-native-reanimated

Description

I am developing an animation using the react-native-reanimated and react-native-gesture-handler libraries. When I open the page it works as I expected. But when I change any data using fast refresh or hook in the function. The value of posX is being reset. And even though it appears in debug in the event, it does not update the data in Animated.View.

Code

import React, { useState } from 'react';
import { StyleSheet, View, Image, Dimensions } from 'react-native';
import Animated, {
  Value,
  event,
  set,
  block,
  cond,
  add,
  eq,
  debug,
  greaterThan,
  lessThan,
  multiply,
  useCode,
} from 'react-native-reanimated';

import { PanGestureHandler, State } from 'react-native-gesture-handler';

interface Props {}

const R = 70;

const image1 = require('./img1.jpeg');
const image2 = require('./img3.jpeg');

const { width, height } = Dimensions.get('window');

export const SplitView: React.FC<Props> = () => {

  const MAX = width - R;
  const MIN = 0;

  const posX = new Value<number>(0);
  const offsetX = new Value<number>((width - R) / 2);
  const panState = new Value(State.UNDETERMINED);

  const onGestureHandler = event([
    {
      nativeEvent: ({
        translationX,
        state,
      }: {
        translationX: number;
        state: State;
      }) =>
        block([
          set(panState, state),
          set(posX, add(translationX, offsetX)),

          cond(
            lessThan(posX, MIN),
            set(posX, MIN),
            cond(greaterThan(posX, MAX), set(posX, MAX)),
          ),

          debug('posX ', posX), // <=== always show on console

          cond(eq(state, State.END), [
            set(offsetX, add(offsetX, translationX)),

            cond(
              lessThan(offsetX, MIN),
              set(offsetX, MIN),
              cond(greaterThan(offsetX, MAX), set(offsetX, MAX)),
            ),
          ]),
        ]),
    },
  ]);


  return (
    <View style={styles.container}>
      <View style={[styles.left, { width, height }]}>
        <Animated.Image style={styles.image} source={image1} />
      </View>

      // But after value change or run fast refresh not working.
      // Initial value gets stuck
      <Animated.View
        style={[styles.right, { width, height, left: add(posX, R / 2) }]}>
        <Animated.Image
          style={[styles.image, { left: multiply(add(posX, R / 2), -1) }]}
          source={image2}
        />
      </Animated.View>

      <PanGestureHandler
        onGestureEvent={onGestureHandler}
        onHandlerStateChange={onGestureHandler}>
        <Animated.View
          style={[
            styles.ball,
            {
              left: posX,
              top: (height - R) / 2,
            },
          ]}
        />
      </PanGestureHandler>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'blue',
  },
  ball: {
    width: R,
    height: R,
    backgroundColor: 'red',
    borderRadius: R / 2,
    zIndex: 2,
  },

  image: {
    flex: 1,
  },

  left: {
    position: 'absolute',
    top: 0,
    left: 0,
    flex: 1,
  },

  right: {
    position: 'absolute',
    flex: 1,
    top: 0,
    left: 0,
    zIndex: 1,
    overflow: 'hidden',
    backgroundColor: 'yellow',
  },
});

Package versions

  • React: 16.11.0
  • React Native: 0.62.2
  • React Native Reanimated: ^1.8.0
  • React Native Gesture Handler: ^1.6.1
鉂換uestion

Most helpful comment

@hbarylskyi Hi, example like this

const value = useMemoOne(() => new Animated.Value(), []);

You can use it this way. but I'm doing it this way using the package 'react-native-redash' right now.

import Animated from 'react-native-reanimated';
import { State }  from 'react-native-gesture-handler';
import { useValue } from 'react-native-redash';

const value = useValue(0);
// or
const [value, state] = useValues([0, State.UNDETERMINED])

All 5 comments

Hi @m-inan,

this is probably related to the fact, that Reanimated attach nodes in the native side and they're not detached and reattached after the fast refresh. The best way to avoid weird behavior is to use a full app refresh.

We're not working actively at this but PRs are welcome.

I solved this problem using useMemoOne. Thanks for the answer.

@m-inan where did you apply memoization? could you share a snippet with the code?

@hbarylskyi Hi, example like this

const value = useMemoOne(() => new Animated.Value(), []);

You can use it this way. but I'm doing it this way using the package 'react-native-redash' right now.

import Animated from 'react-native-reanimated';
import { State }  from 'react-native-gesture-handler';
import { useValue } from 'react-native-redash';

const value = useValue(0);
// or
const [value, state] = useValues([0, State.UNDETERMINED])

We have PR #786 which will be likely merged in the near future, so you'll be able to use it directly from Reanimated as well

Basically we need to use useRef/useMemo hooks to preserve references to registered nodes between rerenders

Was this page helpful?
0 / 5 - 0 ratings