typescriptreact-nativeexpo

How can I call an async function on double tap?


I have an Expo app where I have an element. I want to call an async function when I double tap that element. For doing that, I found the react-native-gesture-handler library. I wrapped the whole component in a <GestureHandlerRootView> as the docs said, getting the code below. However, the async function editUserTask(), which is defined in another file, doesn't even run here when I double tap. Why is that? I've seen several ideas about using runOnJS(), but VS Code tells me it is deprecated. Any solutions?

// import...
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from "react-native-gesture-handler";

export default function Todo() { 
    <GestureHandlerRootView>
        // ...
        userTasks[0]?.map((pendingUserTask, index) => {
              const doubleTap = Gesture.Tap()
                .numberOfTaps(2)
                .onStart(() => {
                  async () => {
                    await editUserTask({
                      id: pendingUserTask.id,
                      title: pendingUserTask.title,
                      description: pendingUserTask.description,
                      deadline: pendingUserTask.deadline.toDate(),
                      user_id: pendingUserTask.user_id as never,
                      done: true,
                      tags: pendingUserTask.tags,
                    });
                    console.log("A");
                  };
                });
              return (
                <GestureDetector key={`pending-${index}`} gesture={doubleTap}>
                  <View collapsable={false}>
                    <TaskElement
                      userTask={pendingUserTask}
                      fetchTasksFunction={fetchUserTasks}
                      openTaskModalFunction={openTaskModal}
                    />
                  </View>
                </GestureDetector>
    </GestureHandlerRootView>
}

Solution

  • Your async function is not running because you arre defining an async function but never calling it, and gesture callbacks run on a UI worklet thread, which cant directly execute normal async JS. That is why you must use runOnJS (it’s not deprecated just make sure it comes from react-native-reanimated). The fix is to move your async logic into a normal function and trigger it via runOnJS inside the double-tap gesture.

    
    import {
      Gesture,
      GestureDetector,
      GestureHandlerRootView,
    } from "react-native-gesture-handler";
    import { runOnJS } from "react-native-reanimated";
    
    const handleDoubleTap = async (task: any) => {
      await editUserTask({
        id: task.id,
        title: task.title,
        description: task.description,
        deadline: task.deadline.toDate(),
        user_id: task.user_id as never,
        done: true,
        tags: task.tags,
      });
      console.log("A");
    };
    
    const doubleTap = Gesture.Tap()
      .numberOfTaps(2)
      .onStart(() => {
        runOnJS(handleDoubleTap)(pendingUserTask);
      });
    
    return (
      <GestureHandlerRootView>
        <GestureDetector gesture={doubleTap}>
          <View collapsable={false}>
            <TaskElement userTask={pendingUserTask} />
          </View>
        </GestureDetector>
      </GestureHandlerRootView>
    );