Skip to content

Commit

Permalink
[react-events] Tap responder
Browse files Browse the repository at this point in the history
This is a partial replacement for the 'Press' responder:

1. `useTap` is scoped to pointers (no keyboard support). Our current thinking is
that "responders" should be limited to working with pointers, and that they can
be combined with 'useKeyboard' in user-space. For example, we might create a
'usePress' hook in user-space that combines 'useTap' with 'useKeyboard' to react
to both pointers and keyboard interactions.

2. `useTap` cancels the gesture once the pointer moves over an element that is
not within the responder target's subtree. This differs from `usePress` (and
React Native), where the gesture remains active after the pointer exits the
target's subtree and is restarted once the pointer reenters. One of the
drawbacks with the `usePress` behavior is that it requires repeatedly measuring
DOM elements (which can cause jank) to perform hit region tests. `useTap` avoids
doing this and relies on `document.elementFromPoint` only to support the
TouchEvent fallbacks.

3. `useTap` calls `onTapUpdate` when the active gesture's state changes,
`onTapEnd` when the gesture successfully completes. and `onTapCancel` when it
fails. There is no `onTap` callback. `usePress` did not explicitly report back
when the gesture failed, and product developers were confused about the
difference between `onPress` and `onPressEnd`.

4. `useTap` explicitly separates the PointerEvent implementation from the
MouseEvent/TouchEvent fallback.

5. `useTap` has better unit test coverage . All pointer types and the fallback
environment are tested. The shape of the gesture state object is also defined
and tested.
  • Loading branch information
necolas committed Aug 31, 2019
1 parent f512537 commit 1655201
Show file tree
Hide file tree
Showing 10 changed files with 1,541 additions and 28 deletions.
54 changes: 35 additions & 19 deletions packages/react-dom/src/events/DOMEventResponderSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,26 @@ const eventResponderContext: ReactDOMResponderContext = {
parentTarget: Element | Document,
): boolean {
validateResponderContext();
if (parentTarget === getActiveDocument()) {
return true;
}
const childFiber = getClosestInstanceFromNode(childTarget);
const parentFiber = getClosestInstanceFromNode(parentTarget);
const parentAlternateFiber = parentFiber.alternate;

let node = childFiber;
while (node !== null) {
if (node === parentFiber || node === parentAlternateFiber) {
return true;
if (childFiber != null && parentFiber != null) {
const parentAlternateFiber = parentFiber.alternate;
let node = childFiber;
while (node !== null) {
if (node === parentFiber || node === parentAlternateFiber) {
return true;
}
node = node.return;
}
node = node.return;
return false;
} else {
// Fallback to DOM APIs
return parentTarget.contains(childTarget);
}
return false;
},
addRootEventTypes(rootEventTypes: Array<string>): void {
validateResponderContext();
Expand Down Expand Up @@ -436,6 +444,7 @@ function processTimers(

function createDOMResponderEvent(
topLevelType: string,
responderTarget: Element,
nativeEvent: AnyNativeEvent,
nativeEventTarget: Element | Document,
passive: boolean,
Expand All @@ -462,7 +471,7 @@ function createDOMResponderEvent(
passiveSupported,
pointerId,
pointerType: eventPointerType,
responderTarget: null,
responderTarget,
target: nativeEventTarget,
type: topLevelType,
};
Expand Down Expand Up @@ -509,13 +518,6 @@ function traverseAndHandleEventResponderInstances(
// - Root responder phase

const visitedResponders = new Set();
const responderEvent = createDOMResponderEvent(
topLevelType,
nativeEvent,
nativeEventTarget,
isPassiveEvent,
isPassiveSupported,
);
let node = targetFiber;
while (node !== null) {
const {dependencies, tag} = node;
Expand All @@ -534,9 +536,15 @@ function traverseAndHandleEventResponderInstances(
const onEvent = responder.onEvent;
if (onEvent !== null) {
currentInstance = responderInstance;
responderEvent.responderTarget = ((target: any):
| Element
| Document);
const responderTarget = ((target: any): Element);
const responderEvent = createDOMResponderEvent(
topLevelType,
responderTarget,
nativeEvent,
nativeEventTarget,
isPassiveEvent,
isPassiveSupported,
);
onEvent(responderEvent, eventResponderContext, props, state);
}
}
Expand All @@ -558,7 +566,15 @@ function traverseAndHandleEventResponderInstances(
const onRootEvent = responder.onRootEvent;
if (onRootEvent !== null) {
currentInstance = responderInstance;
responderEvent.responderTarget = ((target: any): Element | Document);
const responderTarget = ((target: any): Element);
const responderEvent = createDOMResponderEvent(
topLevelType,
responderTarget,
nativeEvent,
nativeEventTarget,
isPassiveEvent,
isPassiveSupported,
);
onRootEvent(responderEvent, eventResponderContext, props, state);
}
}
Expand Down
Loading

0 comments on commit 1655201

Please sign in to comment.