Skip to content

Discard measured responder region that excludes the grant touch#57503

Open
joeltjames wants to merge 1 commit into
react:mainfrom
joeltjames:fix/pressability-stale-responder-region
Open

Discard measured responder region that excludes the grant touch#57503
joeltjames wants to merge 1 commit into
react:mainfrom
joeltjames:fix/pressability-stale-responder-region

Conversation

@joeltjames

Copy link
Copy Markdown

Summary:

Fixes #57502.

Pressability measures the responder view on grant and, on every touchMove, cancels the press if the touch is outside the measured rect. On Fabric, measure() answers from the shadow tree — so when a view has been repositioned by native code after layout (e.g. @lodev09/react-native-true-sheet pins its footer slot to the sheet bottom with AutoLayout — lodev09/react-native-true-sheet#726), the measured rect can be hundreds of points away from where the view actually is. A button that natively hit-tested and granted the responder then shows press feedback (onPressIn) but never fires onPress: the first move event deactivates the press via LEAVE_PRESS_RECT.

The failure is invisible in development: stationary simulator taps emit zero move events (the check never runs), and newer device/OS combos coalesce micro-jitter moves away. On older physical hardware (tested iPhone XS-class, iOS 16/18), every real-finger tap emits at least one move event — even zero-delta — and the press silently dies.

This PR validates the measurement against the press itself: the responder was granted by a native hit test, so the grant touch necessarily lies inside the view's true on-screen region. If the async-measured region excludes the grant point, the measurement is provably stale — discard it. A null region already means "skip press-rect checks" (that state exists today whenever measure hasn't completed), and correct measurements are unaffected since they contain the grant point by construction. Precedent: _measureCallback already discards all-zero measurements.

Found while working on the Planning Center mobile app; diagnosed and fixed with the help of Claude (Fable). The identical patch is running in production and verified on the previously-failing devices.

Changelog:

[GENERAL] [FIXED] - Pressability no longer cancels presses using a stale measured region that does not contain the touch that granted the responder

Test Plan:

  • Added two Jest tests in Pressability-test.js:
    • onPress fires when the measured region excludes the grant touch (press granted at page (276, 467) with the mock region at (0, 0, 50, 50) — models a natively repositioned view; previously the first move deactivated the press).
    • onPress still does not fire when a valid measurement contains the grant touch and the finger then leaves the region — confirming slide-off-to-cancel is unchanged.
  • Verified on device: with this patch applied (via yarn patch on RN 0.83.6) in the Planning Center app, sheet-footer buttons fire correctly on physical iPhones running iOS 16/18, and slide-off-to-cancel still works on correctly-measured buttons.

The responder is granted by a native hit test, so the touch that granted
it necessarily lies inside the view's true on-screen region. When a view
has been repositioned by native code after layout (e.g. a native sheet
footer pinned with AutoLayout), measure() still reports the stale layout
frame; Pressability then cancels the press on the first touchMove
because the touch is outside the measured rect — press feedback fires,
onPress never does. Stationary simulator taps emit no move events, so
the bug only reproduces on physical devices.

Validate the measured region against the grant touch position: if the
region excludes the point where the press was granted, the measurement
is provably stale, so discard it. A null region already means "skip
press-rect checks", and correct measurements are unaffected since they
contain the grant point by construction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@meta-cla

meta-cla Bot commented Jul 9, 2026

Copy link
Copy Markdown

Hi @joeltjames!

Thank you for your pull request and welcome to our community.

Action Required

In order to merge any pull request (code, docs, etc.), we require contributors to sign our Contributor License Agreement, and we don't seem to have one on file for you.

Process

In order for us to review and merge your suggested changes, please sign at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need to sign the corporate CLA.

Once the CLA is signed, our tooling will perform checks and validations. Afterwards, the pull request will be tagged with CLA signed. The tagging process may take up to 1 hour after signing. Please give it that time before contacting us about it.

If you have received this in error or have any questions, please contact us at cla@meta.com. Thanks!

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jul 9, 2026
@meta-cla

meta-cla Bot commented Jul 9, 2026

Copy link
Copy Markdown

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Meta Open Source project. Thanks!

@facebook-github-tools facebook-github-tools Bot added the Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team. label Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Shared with Meta Applied via automation to indicate that an Issue or Pull Request has been shared with the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pressability silently drops presses when measure() disagrees with the native hit test (Fabric, natively repositioned views)

1 participant