Skip to content

Conversation

m-bert
Copy link
Contributor

@m-bert m-bert commented Aug 7, 2025

On hold

Caution

Warning

Description

Test plan

For simultaneous:
import * as React from 'react';
import { Animated, Button, useAnimatedValue } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useSimultaneous,
  useGesture,
} from 'react-native-gesture-handler';

export default function App() {
  const [visible, setVisible] = React.useState(true);

  const value = useAnimatedValue(0);
  const event = Animated.event(
    [{ nativeEvent: { handlerData: { translationX: value } } }],
    {
      useNativeDriver: true,
    }
  );

  const pan1 = useGesture('PanGestureHandler', {
    onUpdate: () => console.log('Pan 1'),
  });

  const pan2 = useGesture('PanGestureHandler', {
    onUpdate: () => console.log('Pan 2'),
  });

  const composedGesture = useSimultaneous(pan2, pan1);

  console.log(composedGesture);
  console.log(pan1.config, pan2.config);

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Toggle visibility"
        onPress={() => {
          setVisible(!visible);
        }}
      />

      {visible && (
        <NativeDetector gesture={composedGesture}>
          <Animated.View
            style={[
              {
                width: 150,
                height: 150,
                backgroundColor: 'blue',
                opacity: 0.5,
                borderWidth: 10,
                borderColor: 'green',
                marginTop: 20,
                marginLeft: 40,
              },
              { transform: [{ translateX: value }] },
            ]}
          />
        </NativeDetector>
      )}
    </GestureHandlerRootView>
  );
}
For Exclusive:
import * as React from 'react';
import { Animated, Button, useAnimatedValue } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useSimultaneous,
  useGesture,
  useExclusive,
} from 'react-native-gesture-handler';

export default function App() {
  const [visible, setVisible] = React.useState(true);

  const value = useAnimatedValue(0);
  const event = Animated.event(
    [{ nativeEvent: { handlerData: { translationX: value } } }],
    {
      useNativeDriver: true,
    }
  );

  const tap1 = useGesture('TapGestureHandler', {
    onEnd: () => console.log('Tap 1'),
    numberOfTaps: 1,
  });

  const tap2 = useGesture('TapGestureHandler', {
    onEnd: () => console.log('Tap 2'),
    numberOfTaps: 2,
  });

  const tap3 = useGesture('TapGestureHandler', {
    onEnd: () => console.log('Tap 3'),
    numberOfTaps: 3,
  });

  const composedGesture = useExclusive(tap3, tap2, tap1);

  console.log(composedGesture);
  console.log(tap1.config, tap2.config, tap3.config);

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Toggle visibility"
        onPress={() => {
          setVisible(!visible);
        }}
      />

      {visible && (
        <NativeDetector gesture={composedGesture}>
          <Animated.View
            style={[
              {
                width: 150,
                height: 150,
                backgroundColor: 'blue',
                opacity: 0.5,
                borderWidth: 10,
                borderColor: 'green',
                marginTop: 20,
                marginLeft: 40,
              },
              { transform: [{ translateX: value }] },
            ]}
          />
        </NativeDetector>
      )}
    </GestureHandlerRootView>
  );
}
For Race:
import * as React from 'react';
import { Animated, Button, useAnimatedValue } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useSimultaneous,
  useGesture,
  useExclusive,
  useRace,
} from 'react-native-gesture-handler';

export default function App() {
  const [visible, setVisible] = React.useState(true);

  const value = useAnimatedValue(0);
  const event = Animated.event(
    [{ nativeEvent: { handlerData: { translationX: value } } }],
    {
      useNativeDriver: true,
    }
  );

  const tap1 = useGesture('TapGestureHandler', {
    onEnd: () => console.log('Tap 1'),
    numberOfTaps: 1,
  });

  const pan1 = useGesture('PanGestureHandler', {
    onEnd: (e, s) => console.log('Pan 1', s),
  });

  const composedGesture = useRace(tap1, pan1);

  console.log(composedGesture);

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Toggle visibility"
        onPress={() => {
          setVisible(!visible);
        }}
      />

      {visible && (
        <NativeDetector gesture={composedGesture}>
          <Animated.View
            style={[
              {
                width: 150,
                height: 150,
                backgroundColor: 'blue',
                opacity: 0.5,
                borderWidth: 10,
                borderColor: 'green',
                marginTop: 20,
                marginLeft: 40,
              },
              { transform: [{ translateX: value }] },
            ]}
          />
        </NativeDetector>
      )}
    </GestureHandlerRootView>
  );
}
For composed:
import * as React from 'react';
import { Animated, Button, useAnimatedValue } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useSimultaneous,
  useGesture,
  useExclusive,
  useRace,
} from 'react-native-gesture-handler';

export default function App() {
  const [visible, setVisible] = React.useState(true);

  const value = useAnimatedValue(0);
  const event = Animated.event(
    [{ nativeEvent: { handlerData: { translationX: value } } }],
    {
      useNativeDriver: true,
    }
  );

  const tap1 = useGesture('TapGestureHandler', {
    onEnd: () => console.log('Tap 1'),
    numberOfTaps: 1,
  });

  const pan1 = useGesture('PanGestureHandler', {
    onUpdate: (e) => console.log('Pan 1'),
  });

  const pan2 = useGesture('PanGestureHandler', {
    onUpdate: (e) => console.log('Pan 2'),
  });

  const composedGesture = useExclusive(tap1, useSimultaneous(pan1, pan2));

  console.log(composedGesture);

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Toggle visibility"
        onPress={() => {
          setVisible(!visible);
        }}
      />

      {visible && (
        <NativeDetector gesture={composedGesture}>
          <Animated.View
            style={[
              {
                width: 150,
                height: 150,
                backgroundColor: 'blue',
                opacity: 0.5,
                borderWidth: 10,
                borderColor: 'green',
                marginTop: 20,
                marginLeft: 40,
              },
              { transform: [{ translateX: value }] },
            ]}
          />
        </NativeDetector>
      )}
    </GestureHandlerRootView>
  );
}

Comment on lines 53 to 68
export const SingleGestureType = {
Tap: 'TapGestureHandler',
LongPress: 'LongPressGestureHandler',
Pan: 'PanGestureHandler',
Pinch: 'PinchGestureHandler',
Rotation: 'RotationGestureHandler',
Fling: 'FlingGestureHandler',
Manual: 'ManualGestureHandler',
Native: 'NativeGestureHandler',
} as const;

export const ComposedGestureType = {
Simultaneous: 'SimultaneousGesture',
Exclusive: 'ExclusiveGesture',
Race: 'RaceGesture',
} as const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These could also use a different name, Type suffix is a bit misleading.

export function useGesture(
type: GestureType,
type: ValueOf<typeof HandlerType>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a scenario where this is ComposedGestureType?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, good catch.

import { ComposedGesture, ComposedGestureType, NativeGesture } from '../types';

// The tree consists of ComposedGestures and NativeGestures. NativeGestures are always leaf nodes.
export const dfs = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could also use a more descriptive name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I know, I left it for now as there are some other things to do 😅

Base automatically changed from @mbert/shared-values to next August 22, 2025 12:11
@m-bert m-bert mentioned this pull request Aug 26, 2025
@m-bert
Copy link
Contributor Author

m-bert commented Aug 26, 2025

Caution

@m-bert m-bert closed this Aug 26, 2025
@j-piasecki
Copy link
Member

Suppressed by #3693

I think the word you're looking for is superseded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants