Skip to content

Commit

Permalink
Fix GestureDetector not working correctly with suspense and recycling (
Browse files Browse the repository at this point in the history
…#2925)

## Description

Fixes
#2920

When the subtree containing `GestureDetector` was suspended, all the
native views were unmounted without the `Detector` component being
aware. As a consequence, the view with the attached native recognizer
was put in the recycling pool and then reused in other places in the UI
(which is bad). Then, when the tree was unsuspended, the gesture didn't
work correctly - again, `Detector` wasn't aware of any change, and a
different native view was put as its child so the native recognizer
wasn't attached to it.

This PR changes the effect responsible for the initial setup and cleanup
to be a layout effect, which gets triggered when the tree is suspended.
This means that when tree suspends, native recognizers will be dropped
and recreated when the tree unsuspends.

Note that this will only fix the issue on RN 0.74, since
`useLayoutEffect` is broken on versions below that.

## Test plan

<details>
<summary>Tested on the example app and the following code</summary>

```jsx
import {useState} from 'react';
import {Freeze} from 'react-freeze';
import {View, Button, SafeAreaView} from 'react-native';
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from 'react-native-gesture-handler';

export default function FirstScreen() {
  const [frozen, setFrozen] = useState(false);
  const tap = Gesture.Tap().onStart(() => {
    console.log('Tap');
  });

  return (
    <GestureHandlerRootView style={{flex: 1}}>
      <SafeAreaView style={{flex: 1, backgroundColor: 'red'}}>
        <Button title="Unfreeze" onPress={() => setFrozen(false)} />
        <Freeze freeze={frozen}>
          <View>
            <GestureDetector gesture={tap}>
              <View
                style={{width: 100, height: 100, backgroundColor: 'blue'}}
              />
            </GestureDetector>
            <View style={{width: 100, height: 100, backgroundColor: 'green'}} />
            <Button title="Freeze" onPress={() => setFrozen(true)} />
          </View>
        </Freeze>
      </SafeAreaView>
    </GestureHandlerRootView>
  );
}
```
</details>
  • Loading branch information
j-piasecki authored May 28, 2024
1 parent 6eaf417 commit 921a9b2
Showing 1 changed file with 8 additions and 2 deletions.
10 changes: 8 additions & 2 deletions src/handlers/gestures/GestureDetector/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
/* eslint-disable react/no-unused-prop-types */
import React, { useContext, useEffect, useMemo, useRef } from 'react';
import React, {
useContext,
useEffect,
useLayoutEffect,
useMemo,
useRef,
} from 'react';
import { Platform, findNodeHandle } from 'react-native';
import { GestureType } from '../gesture';
import { UserSelect, TouchAction } from '../../gestureHandlerCommon';
Expand Down Expand Up @@ -141,7 +147,7 @@ export const GestureDetector = (props: GestureDetectorProps) => {

useAnimatedGesture(preparedGesture, needsToRebuildReanimatedEvent);

useEffect(() => {
useLayoutEffect(() => {
const viewTag = findNodeHandle(state.viewRef) as number;
preparedGesture.isMounted = true;

Expand Down

0 comments on commit 921a9b2

Please sign in to comment.