Skip to content

feat(touchable): expose native visual press changes - #4516

Draft
BenedictP wants to merge 2 commits into
software-mansion:mainfrom
BenedictP:feat/native-visual-press-state
Draft

BenedictP wants to merge 2 commits into
software-mansion:mainfrom
BenedictP:feat/native-visual-press-state

Conversation

@BenedictP

Copy link
Copy Markdown

Why

On Android, Touchable.onPressIn can run before a parent scroll view permits native press feedback. Custom animations driven by that callback can highlight a button while the user starts to scroll, even when the built-in animation correctly waits.

Add onVisualPressChange(pressed) so custom feedback can follow Touchable's built-in feedback. The reproduction compares the two paths. This exposes the missing feedback signal; #4441 already fixes Android scroll takeover cancellation.

Scope

  • Add the optional callback to v3 Touchable, with native and web event support.
  • Report active/resting targets, including the existing short-tap feedback interval. Existing press callbacks retain their semantics.
  • Handle disable, cancellation and teardown; add tests, API documentation and a Visual Press example in basic-example.

Tradeoffs

The callback runs in JavaScript and reports target changes, not animation completion. It follows existing feedback timing and adds no press-in delay. Native scroll deferral still belongs to the native control.

Verification

  • Jest: 23 suites, 188 tests passed.
  • All workspace TypeScript checks, pre-commit ESLint/native formatting, Android arm64 debug build and iOS simulator debug build passed.
  • Android 16 emulator and iOS 26.3 simulator: quick taps activate once; fast drags scroll without custom press feedback; disabling during a long press restores resting feedback without activation.
  • Android: disabling after a quick tap also clears pending feedback.
  • macOS and tvOS runtime behavior was not tested.

Let custom feedback follow built-in press targets after native scroll deferral while preserving existing interaction callbacks. Cover quick taps, cancellation, disabled state, callback replacement and cleanup.
Add a card that exposes feedback and activation counters for scroll deferral, quick taps, re-entry and disabling during feedback.
Copilot AI lite review requested due to automatic review settings September 14, 2026 15:21
@coderabbitai

coderabbitai Bot commented Sep 14, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 3d8b18fa-9325-44bf-9d9e-9ce2cfd0dab6

📥 Commits

Reviewing files that changed from the base of the PR and between a2e0430 and 64e1da5.

📒 Files selected for processing (17)
  • apps/basic-example/src/App.tsx
  • apps/basic-example/src/VisualPress.tsx
  • packages/docs-gesture-handler/docs/components/touchable.mdx
  • packages/react-native-gesture-handler/__typetests__/buttonEventTest.ts
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt
  • packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerButtonVisualPressEvent.kt
  • packages/react-native-gesture-handler/apple/RNGestureHandlerButton.h
  • packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm
  • packages/react-native-gesture-handler/apple/RNGestureHandlerButtonComponentView.mm
  • packages/react-native-gesture-handler/src/__tests__/touchableVisualPress.test.tsx
  • packages/react-native-gesture-handler/src/__tests__/webButtonVisualPress.test.tsx
  • packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx
  • packages/react-native-gesture-handler/src/components/GestureHandlerButton.web.tsx
  • packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts
  • packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts
  • packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx
  • packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Summary

Summary by CodeRabbit

  • New Features

    • Added onVisualPressChange to Touchable, enabling apps to respond when visual pressed feedback begins or ends.
    • Added consistent visual press-state reporting across native platforms and web, including proper reset behavior when disabled or unmounted.
    • Added a Visual Press example demonstrating feedback states, controls, event logging, and reset behavior.
  • Documentation

    • Documented the callback, timing, custom feedback usage, and limitations.
  • Tests

    • Added coverage for visual press callbacks, rapid interactions, cancellation, disabling, keyboard-dismiss taps, and unmounting.

Walkthrough

Changes

Visual press callback

Layer / File(s) Summary
Callback contract and Touchable wiring
packages/react-native-gesture-handler/src/specs/..., packages/react-native-gesture-handler/src/components/..., packages/react-native-gesture-handler/src/v3/components/..., packages/docs-gesture-handler/..., packages/react-native-gesture-handler/__typetests__/*
Adds the native event payload, button transport prop, Touchable callback, type coverage, and documentation.
Native visual state dispatch
packages/react-native-gesture-handler/android/..., packages/react-native-gesture-handler/apple/...
Tracks visual pressed state and dispatches changes across press, release, disable, detach, recycle, hover, and handler teardown paths.
Web visual state and lifecycle handling
packages/react-native-gesture-handler/src/components/GestureHandlerButton.web.tsx, packages/react-native-gesture-handler/src/__tests__/webButtonVisualPress.test.tsx
Centralizes web visual state updates and tests delayed releases, cancellation, disabling, callback replacement, and unmount cleanup.
Touchable integration validation and example
packages/react-native-gesture-handler/src/__tests__/touchableVisualPress.test.tsx, apps/basic-example/src/App.tsx, apps/basic-example/src/VisualPress.tsx
Tests callback forwarding and adds an interactive Visual Press example with event logging and state controls.

Sequence Diagram(s)

sequenceDiagram
  participant Touchable
  participant GestureHandlerButton
  participant NativeButton
  participant JavaScriptCallback
  NativeButton->>GestureHandlerButton: emit pressed state event
  GestureHandlerButton->>Touchable: forward native event
  Touchable->>JavaScriptCallback: call onVisualPressChange(pressed)
Loading

Suggested reviewers: m-bert

Priority: ⬇️ Low

Merge Risk: ⚪ Minimal · up to 64e1d

The callback lifecycle behavior matches its documented contract, with no actionable merge risk identified.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: exposing native visual press changes through Touchable.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Warning

Some tools did not complete. Review the errors below.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

packages/docs-gesture-handler/docs/components/touchable.mdx

Parsing error: ESLint was configured to run on <tsconfigRootDir>/packages/docs-gesture-handler/docs/components/touchable.mdx using parserOptions.project: /tsconfig.json
The extension for the file (.mdx) is non-standard. You should add parserOptions.extraFileExtensions to your config.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

🟡 Changes recommended

Critical Android and iOS handler-reset paths can leave pending long-press callbacks and stale native press state after handler removal or replacement.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds onVisualPressChange(pressed) to v3 Touchable, exposing native and web visual press transitions for custom feedback.

Changes:

  • Adds callback typing and native/web event plumbing.
  • Implements platform visual-state tracking with lifecycle tests.
  • Adds documentation and a Basic Example demonstration.
File summaries
File Summary
packages/react-native-gesture-handler/src/v3/components/Touchable/TouchableProps.ts Defines the public callback type and documentation.
packages/react-native-gesture-handler/src/v3/components/Touchable/Touchable.tsx Forwards visual press events.
packages/react-native-gesture-handler/src/v3/components/GestureButtonsProps.ts Updates internal interaction props.
packages/react-native-gesture-handler/src/specs/RNGestureHandlerButtonNativeComponent.ts Adds native event typing.
packages/react-native-gesture-handler/src/components/GestureHandlerButton.web.tsx Implements web visual press tracking.
packages/react-native-gesture-handler/src/components/GestureHandlerButton.tsx Adds shared native button prop support.
packages/react-native-gesture-handler/src/__tests__/webButtonVisualPress.test.tsx Tests web visual press behavior and teardown.
packages/react-native-gesture-handler/src/__tests__/touchableVisualPress.test.tsx Tests Touchable event forwarding.
packages/react-native-gesture-handler/apple/RNGestureHandlerButtonComponentView.mm Dispatches iOS visual press events.
packages/react-native-gesture-handler/apple/RNGestureHandlerButton.mm Tracks iOS visual state. Critical (2 votes): the handler reset does not cancel _pendingLongPressBlock, allowing a stale long-press event after handler removal or replacement.
packages/react-native-gesture-handler/apple/RNGestureHandlerButton.h Declares iOS button APIs.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/RNGestureHandlerButtonViewManager.kt Tracks Android visual state. Critical (2 votes): handler reset does not cancel pending long presses or clear pressed/touched state, allowing stale callbacks and recycled-view state.
packages/react-native-gesture-handler/android/src/main/java/com/swmansion/gesturehandler/react/events/RNGestureHandlerButtonVisualPressEvent.kt Defines the Android visual press event.
packages/react-native-gesture-handler/__typetests__/buttonEventTest.ts Verifies the TypeScript API shape.
packages/docs-gesture-handler/docs/components/touchable.mdx Documents the callback.
apps/basic-example/src/VisualPress.tsx Adds the visual press example.
apps/basic-example/src/App.tsx Registers the example screen.
Review details
  • Files reviewed: 17/17 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1063 to +1067
pendingPressOut?.let { handler?.removeCallbacks(it) }
pendingPressOut = null
pressInTimestamp = 0L
setVisualPressed(false)
animateTo(restingOpacity, restingScale, restingUnderlayOpacity, 0)

- (void)resetVisualPressState
{
[self cancelPendingPressOutAnimation];

This branch has not been deployed

No deployments
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.

2 participants