Skip to content

Do not call onPress on buttons while dismissing keyboard#4281

Open
m-bert wants to merge 5 commits into
mainfrom
@mbert/buttons-keyboard
Open

Do not call onPress on buttons while dismissing keyboard#4281
m-bert wants to merge 5 commits into
mainfrom
@mbert/buttons-keyboard

Conversation

@m-bert

@m-bert m-bert commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Description

Currently, when buttons are inside ScrolView with keyboardShouldPersistTaps={never} they still call onPress even though press was meant to dismiss keyboard.

This PR fixes that behavior. It attaches listeners for keyboard state and based on that it blocks onPress* callbacks on JS side.

Fixes #992

Test plan

Tested on Keyboard Should Persist Taps example

Copilot AI review requested due to automatic review settings June 23, 2026 10:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to align RNGH v3 Pressable/Touchable behavior with React Native when used inside a ScrollView with keyboardShouldPersistTaps="never", swallowing the tap that dismisses the soft keyboard so it doesn’t also trigger onPress-family callbacks.

Changes:

  • Added shared keyboard-visibility tracking in ScrollViewResponderInterceptor and exposed isKeyboardDismissingTap(...) for components to consult.
  • Updated v3 Pressable and Touchable to detect “keyboard dismiss” taps and suppress onPress* callbacks for that interaction.
  • Adjusted v3 API tests and expanded the example app’s keyboardShouldPersistTaps demo to include Touchable.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx Detects keyboard-dismiss taps via JS responder context and suppresses press callbacks for that gesture.
packages/react-native-gesture-handler/src/v3/components/ScrollViewResponderInterceptor.tsx Adds reference-counted keyboard visibility listeners and exposes isKeyboardDismissingTap based on keyboardShouldPersistTaps and keyboard visibility.
packages/react-native-gesture-handler/src/v3/components/Pressable.tsx Suppresses v3 press handling when the initiating touch is used to dismiss the keyboard.
packages/react-native-gesture-handler/src/tests/api_v3.test.tsx Updates responder-handling assertions to reflect the responder marker flow after Pressable changes.
apps/common-app/src/new_api/tests/keyboardShouldPersistTaps/index.tsx Adds a Touchable variant to the interactive keyboardShouldPersistTaps example.
Comments suppressed due to low confidence (1)

packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx:196

  • When the initial touch is identified as the keyboard-dismissing tap (dropKeyboardTapRef.current === true), onUpdate can still call onPressIn/onPressOut as the finger moves because it doesn't check dropKeyboardTapRef. This means a swallowed tap can still trigger press-in/out side effects, which contradicts the goal of fully swallowing the keyboard-dismiss interaction.
  const onUpdate = useCallback(
    (e: CallbackEventType) => {
      if (pointerState.current === PointerState.UNKNOWN) {
        return;
      }


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@m-bert m-bert requested a review from coado June 23, 2026 10:19
}

const setVisible = () => {
isSoftKeyboardVisible = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is that called Soft keyboard?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Software 😉 But I agree that it is not obvious, removed.

Comment on lines +33 to +34
Keyboard.addListener('keyboardDidShow', setVisible),
Keyboard.addListener('keyboardWillShow', setVisible),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

There are also other things that are considered by RN to determine if the touch should be dropped:

https://github.com/react/react-native/blob/main/packages/react-native/Libraries/Components/ScrollView/ScrollView.js#L1512-L1516

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Let my agent speak 😆:

Good call, dug into RN's _handleStartShouldSetResponderCapture.

Took the parts that apply:

Detached keyboard — only treat it as open when endCoordinates.height > 0 (height-0/floating → let the tap through), like _softKeyboardIsDetached.
Seed from Keyboard.metrics() on mount, for a keyboard that's already open.
isKeyboardVisible rides the same keyboardDidShow/Hide events RN uses for _keyboardMetrics, so it's basically softKeyboardMayBeOpen.

Skipped the rest on purpose:

currentlyFocusedInput() — can't use it at gesture time; the ScrollView blurs the input synchronously when it grabs the responder, so it's already null by the time our recognizer fires (that race is why we track visibility instead).
target !isTextInput — moot, a Pressable/Touchable is never a TextInput.
_keyboardEventsAreUnreliable() (Android <30) — RN only uses it alongside hasFocusedTextInput; alone it'd drop taps with the keyboard closed.

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.

BaseButton does not seem to respect TouchableWithoutFeedback

3 participants