Skip to content
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

blocksExternalGesture does not work with PagerView #3268

Closed
gaearon opened this issue Dec 7, 2024 · 3 comments
Closed

blocksExternalGesture does not work with PagerView #3268

gaearon opened this issue Dec 7, 2024 · 3 comments
Labels
Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snack or repo is provided

Comments

@gaearon
Copy link

gaearon commented Dec 7, 2024

Description

This may be an API limitation but I want to understand how to resolve it.

Here is my example:

import {useRef} from 'react'
import {View} from 'react-native'
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from 'react-native-gesture-handler'
import PagerView from 'react-native-pager-view'

export default function App() {
  const pagerRef = useRef()
  const pan = Gesture.Pan()
    .blocksExternalGesture(pagerRef)
    .onBegin(() => {
      'worklet'
      console.log('begin')
    })
    .onUpdate(() => {
      'worklet'
      console.log('update')
    })
    .onEnd(() => {
      'worklet'
      console.log('end')
    })
    .onFinalize(() => {
      'worklet'
      console.log('finalize')
    })
  return (
    <GestureHandlerRootView>
      <GestureDetector gesture={pan}>
        <View style={{flex: 1, backgroundColor: 'blue'}}>
          <ScrollViewExample pagerRef={pagerRef} />
        </View>
      </GestureDetector>
    </GestureHandlerRootView>
  )
}

function ScrollViewExample({pagerRef}) {
  return (
    <PagerView
      ref={pagerRef}
      style={{
        flex: 1,
        backgroundColor: 'yellow',
      }}>
      <View
        style={{
          width: 200,
          height: '100%',
          backgroundColor: 'green',
        }}
      />
      <View
        style={{
          width: 200,
          height: '100%',
          backgroundColor: 'purple',
        }}
      />
    </PagerView>
  )
}

The expected behavior is that this will block the PagerView from scrolling. The actual behavior is that I can still scroll the pager.

This is not terribly surprising because pagerView is a class component ref and not the actual underlying native view. However, this doesn't work either:

  const pagerRef = useRef()
  const pan = Gesture.Pan()
    .blocksExternalGesture({
      current: pagerRef.current?.pagerView,
    })

I presume maybe because the actual scroll view is a child found on the native side? So we don't have its handle. If that is correct, my question is — how can I work around this without getting rid of the PagerView? In this case I want blocksExternalGesture to apply to the entire tree below somehow since I can't query native subviews from the JS side.

Steps to reproduce

  1. Run https://snack.expo.dev/Ci82e0SK-WkCU4JRGjYCw
  2. Observe that there's no way to block its scrolling

Snack or a link to a repository

https://snack.expo.dev/Ci82e0SK-WkCU4JRGjYCw

Gesture Handler version

2.20.2

React Native version

0.76.3

Platforms

iOS

JavaScript runtime

Hermes

Workflow

Expo managed workflow

Architecture

Paper (Old Architecture)

Build type

Debug mode

Device

Real device

Device model

No response

Acknowledgements

Yes

@github-actions github-actions bot added Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snack or repo is provided labels Dec 7, 2024
@gaearon
Copy link
Author

gaearon commented Dec 7, 2024

Workaround: NativeGesture.

Discovered by guessing, would never realize this from the docs.

This is the workaround code (it works):

import {View} from 'react-native'
import {
  Gesture,
  GestureDetector,
  GestureHandlerRootView,
} from 'react-native-gesture-handler'
import PagerView from 'react-native-pager-view'

export default function App() {
  const native = Gesture.Native()
  const pan = Gesture.Pan()
    .blocksExternalGesture(native)
    .onBegin(() => {
      'worklet'
      console.log('begin')
    })
    .onUpdate(() => {
      'worklet'
      console.log('update')
    })
    .onEnd(() => {
      'worklet'
      console.log('end')
    })
    .onFinalize(() => {
      'worklet'
      console.log('finalize')
    })
  return (
    <GestureHandlerRootView>
      <GestureDetector gesture={pan}>
        <View style={{flex: 1, backgroundColor: 'blue'}}>
          <ScrollViewExample native={native} />
        </View>
      </GestureDetector>
    </GestureHandlerRootView>
  )
}

function ScrollViewExample({native}) {
  return (
    <GestureDetector gesture={native}>
      <PagerView
        style={{
          flex: 1,
          backgroundColor: 'yellow',
        }}>
        <View
          style={{
            width: 200,
            height: '100%',
            backgroundColor: 'green',
          }}
        />
        <View
          style={{
            width: 200,
            height: '100%',
            backgroundColor: 'purple',
          }}
        />
      </PagerView>
    </GestureDetector>
  )
}

@j-piasecki
Copy link
Member

Workaround: NativeGesture.
For ScrollView, ref works but NativeGesture doesn't (#3267)
For PagerView, ref doesn't work but NativeGesture does (this issue)
Discovered by guessing, would never realize this from the docs.

That's not really a workaround, that's the intended way to do it 😄. The issue here is, as you pointed out, that the documentation doesn't explain how to accomplish this. Here's an explanation on why the ScrollView didn't work.

I'll close this issue as it boils down to the same problem as #3266 and I'd like to keep the discussion on this in one place.

FYI, there's also an issue between RNGH and PagerView (more specifically with native components that take exclusivity for touch handling) on Android (like #2940, #3048). If you don't nest pager views then it shouldn't affect you but it might be useful to mention. I don't think it's easily fixable without significant changes to the core of RNGH on Android so it might stay this way for a while 😞.

@gaearon
Copy link
Author

gaearon commented Dec 9, 2024

Fantastic, thanks for explaining!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Platform: iOS This issue is specific to iOS Repro provided A reproduction with a snack or repo is provided
Projects
None yet
Development

No branches or pull requests

2 participants