Skip to content

[Web] Adapt NativeViewGestureHandler to NativeDetector #3653

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: @akwasniewski/native-detector-web
Choose a base branch
from

Conversation

akwasniewski
Copy link
Contributor

@akwasniewski akwasniewski commented Aug 4, 2025

Description

Following #3638 this PR brings support for NativeViewGestureHandler into NativeDetector for web. It does so, by attaching handler into child instead of detector.

Test plan

Tested on the following code

import * as React from 'react';
import { StyleSheet, Text, View, ScrollView, Button } from 'react-native';
import {
  GestureHandlerRootView,
  NativeDetector,
  useGesture,
} from 'react-native-gesture-handler';

export default function App() {
  const items = Array.from({ length: 5 }, (_, index) => `Item ${index + 1}`);
  const [enabled, setEnabled] = React.useState(true);
  const gesture = useGesture('NativeViewGestureHandler', {
    onBegin: (e) => {
      console.log('onBegin', e);
    },
    onStart: (e) => {
      console.log('onStart', e);
    }
  });

  const SV1 = () => (
    <ScrollView style={styles.scrollView1}>
      {items.map((item, index) => (
        <View key={index} style={styles.item}>
          <Text style={styles.text}>{item}</Text>
        </View>
      ))}
    </ScrollView>
  );

  const SV2 = () => (
    <ScrollView style={styles.scrollView2}>
      {items.map((item, index) => (
        <View key={index} style={styles.item}>
          <Text style={styles.text}>{item}</Text>
        </View>
      ))}
    </ScrollView>
  );

  return (
    <GestureHandlerRootView
      style={{ flex: 1, backgroundColor: 'white', paddingTop: 8 }}>
      <Button
        title="Swap the child"
        onPress={() => {
          setEnabled(!enabled);
        }}
      />
      <NativeDetector gesture={gesture}>
        {enabled ? <SV1 /> : <SV2 />}
      </NativeDetector >
    </GestureHandlerRootView >
  );
}

const styles = StyleSheet.create({
  scrollView1: {
    backgroundColor: 'pink',
    marginHorizontal: 20,
  },
  scrollView2: {
    backgroundColor: 'lightblue',
    marginHorizontal: 20,
  },

  item: {
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
    margin: 2,
    backgroundColor: 'white',
    borderRadius: 10,
  },
  text: {
    fontSize: 20,
    color: 'black',
  },
});

@akwasniewski akwasniewski changed the base branch from next to @akwasniewski/native-detector-web August 5, 2025 10:21
@akwasniewski akwasniewski force-pushed the @akwasniewski/native-gesture-web branch 2 times, most recently from 95c800c to c00cdcf Compare August 5, 2025 10:39
@akwasniewski akwasniewski force-pushed the @akwasniewski/native-gesture-web branch from c00cdcf to 2a1d8a7 Compare August 5, 2025 10:41
@akwasniewski akwasniewski marked this pull request as ready for review August 5, 2025 12:08
@akwasniewski akwasniewski requested a review from m-bert August 5, 2025 12:08
Comment on lines 51 to 53
const attachTarget = shouldAttachToChild
? childRef.current
: viewRef.current;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
const attachTarget = shouldAttachToChild
? childRef.current
: viewRef.current;
const attachTarget = shouldAttachToChild
? viewRef.current.firstChild
: viewRef.current;

I think viewRef.current is of type HTMLDivElement in web in this case, so we should be able to read the child (we may want to assert there is one).

Copy link
Contributor Author

@akwasniewski akwasniewski Aug 5, 2025

Choose a reason for hiding this comment

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

Thanks! Tried to figure out the solution without wrapping, but I did not know that on web we could just specify the view to be a div. Fixed in 1f473cd

@akwasniewski akwasniewski force-pushed the @akwasniewski/native-gesture-web branch from 389716c to 13ab34c Compare August 6, 2025 06:00
@@ -13,13 +12,22 @@ export interface GestureHandlerDetectorProps extends PropsRef {
const HostGestureDetector = (props: GestureHandlerDetectorProps) => {
const { handlerTags, children } = props;

const viewRef = useRef(null);
const viewRef = useRef<HTMLDivElement>(null);
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory it can also be SVGElement. Would it be ok to use just Element as type?

Copy link
Contributor Author

@akwasniewski akwasniewski Aug 6, 2025

Choose a reason for hiding this comment

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

As @j-piasecki suggested I used HTMLDivElement as only it has firstChild attribute and view on web is a div anyway. It allowed me to get rid of the wrapping. If it really can be somethng different than a div I might have to go back to wrapping. Is there a use case for an SVG element to have a native gesture? Maybe I can only typecast there, when accessing firstChild.

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe I can only typecast there, when accessing firstChild.

If firstChild doesn't exist then type cast won't help - it won't work anyway 🤷

If it really can be something different than a div I might have to go back to wrapping.

As far as I remember svg tags inherit from SVGElement, you can check that 😅

Is there a use case for an SVG element to have a native gesture?

Probably not, this configuration doesn't sound reasonable to me (cc @j-piasecki)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If firstChild doesn't exist then type cast won't help - it won't work anyway

Yes, but if native gesture could only be on divs I could possibly check the view type in

  const shouldAttachGestureToChildView = (handlerTag: number): boolean => {
    return RNGestureHandlerModule.getGestureHandlerNode(
      handlerTag
    ).shouldAttachGestureToChildView() && viewRef.current instanceof HTMLDivElement;
  };

and then the type cast would be safe

Copy link
Contributor

Choose a reason for hiding this comment

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

Then you could probably use type guard and type cast wouldn't be necessary

@@ -54,9 +75,9 @@ const HostGestureDetector = (props: GestureHandlerDetectorProps) => {
}, []);

return (
<View style={{ display: 'contents' }} ref={viewRef}>
<div style={{ display: 'contents' }} ref={viewRef}>
Copy link
Contributor

Choose a reason for hiding this comment

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

I know it works, but why do you switch to div?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Explained in #3653 (comment)

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