Search code examples
react-nativereact-native-gesture-handlerfling

GestureDetector gesture handler app crash when calling external function


i tried to use GestureDetector of react-native-gesture-handler

import React from 'react';
import { Directions, Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated';

/**
 * Component used as Home Page
 */
const HomePage: React.FC  = () => {
  const position = useSharedValue(0);
  const trigger = () => {
    console.log('fdfs')
  }

  const flingGesture = Gesture.Fling()
    .direction(Directions.RIGHT)
    .onStart((e) => {
      position.value = withTiming(position.value + 10, { duration: 100 });
      console.log(e)
      // trigger()
    });

    const flingGestureLeft = Gesture.Fling()
    .direction(Directions.LEFT)
    .onStart((e) => {
      position.value = withTiming(position.value - 10, { duration: 100 });
      // trigger()
    });

  const animatedStyle = useAnimatedStyle(() => ({
    transform: [{ translateX: position.value }],
  }));

  return (
    <GestureDetector gesture={Gesture.Simultaneous(flingGestureLeft, flingGesture)}>
      <Animated.View style={[{ width: 100, height: 30, backgroundColor: 'red' }, animatedStyle]} />
    </GestureDetector>
  );
}

export default HomePage;

this work without problem when i fling my bloc to left or right, but when i tried to call an exeternal function like the trigger(), my app crash. Is a bug of the gesture detector or there is something to add?


Solution

  • The reanimated, gesture handler hooks and callbacks works on the UI thread and the trigger function you defined is by default on the JS thread, so you can not use it directly.

    There are two solutions to this:

    1. add 'worklet' in the trigger function as below
        const trigger = () => {
          'worklet'
          console.log('fdfs')
        }
    
    1. Or wrap your function with 'runOnJS' from reanimated as below
    
        import { runOnJS } from 'react-native-reanimated';
    
        const flingGesture = Gesture.Fling()
          .direction(Directions.RIGHT)
          .onStart((e) => {
            position.value = withTiming(position.value + 10, { duration: 100 });
            console.log(e)
            runOnJS(trigger)()
          });
    
    

    Note:- syntax for runOnJS is like 'runOnJS(functionName)(params). So if your function takes two params (Ex. 1st number and 2nd string), you would call it like this:- runOnJS(trigger)(1, 'dummyString')

    For more details you can read the docs from reanimated and gesture-handler.