Skip to content

Commit

Permalink
[change] Replace the ResponderEventPlugin
Browse files Browse the repository at this point in the history
Replaces the ResponderEventPlugin with useResponderEvents hook.

Also removes the event "normalization" of mouse, touch, and click events.
These events are not part of the responder system and will no longer be
modified from what ReactDOM dispatches.

Fix #1589
Fix #1568
Fix #1571
Fix #829
Fix #693
  • Loading branch information
necolas committed Apr 18, 2020
1 parent f16aa68 commit d26f433
Show file tree
Hide file tree
Showing 22 changed files with 4,115 additions and 672 deletions.
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"$ReadOnly": false,
"$ReadOnlyArray": false,
"CSSStyleSheet": false,
"HTMLElement": false,
"HTMLInputElement": false,
"ReactClass": false,
"ReactComponent": false,
Expand Down
37 changes: 20 additions & 17 deletions packages/react-native-web/src/exports/Text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import css from '../StyleSheet/css';
import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
import usePlatformMethods from '../../hooks/usePlatformMethods';
import useResponderEvents from '../../hooks/useResponderEvents';
import React, { forwardRef, useContext, useRef } from 'react';
import StyleSheet from '../StyleSheet';
import TextAncestorContext from './TextAncestorContext';
Expand Down Expand Up @@ -107,6 +108,24 @@ const Text = forwardRef<TextProps, *>((props, ref) => {

useElementLayout(hostRef, onLayout);
usePlatformMethods(hostRef, ref, classList, style);
useResponderEvents(hostRef, {
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture
});

function createEnterHandler(fn) {
return e => {
Expand Down Expand Up @@ -138,29 +157,13 @@ const Text = forwardRef<TextProps, *>((props, ref) => {
importantForAccessibility,
nativeID,
onBlur,
onContextMenu,
onFocus,
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture,
ref: setRef,
style,
testID,
// unstable
onClick: onPress != null ? createPressHandler(onPress) : null,
onContextMenu,
onKeyDown: onPress != null ? createEnterHandler(onPress) : null,
onMouseDown,
onMouseEnter,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-web/src/exports/Text/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ export type TextProps = {
onResponderRelease?: (e: any) => void,
onResponderStart?: (e: any) => void,
onResponderTerminate?: (e: any) => void,
onResponderTerminationRequest?: (e: any) => void,
onResponderTerminationRequest?: (e: any) => boolean,
onScrollShouldSetResponder?: (e: any) => boolean,
onScrollShouldSetResponderCapture?: (e: any) => boolean,
onSelectionChangeShouldSetResponder?: (e: any) => boolean,
Expand Down
35 changes: 19 additions & 16 deletions packages/react-native-web/src/exports/TextInput/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
import useLayoutEffect from '../../hooks/useLayoutEffect';
import { usePlatformInputMethods } from '../../hooks/usePlatformMethods';
import useResponderEvents from '../../hooks/useResponderEvents';
import { forwardRef, useRef } from 'react';
import StyleSheet from '../StyleSheet';
import TextInputState from '../../modules/TextInputState';
Expand Down Expand Up @@ -286,6 +287,24 @@ const TextInput = forwardRef<TextInputProps, *>((props, ref) => {

useElementLayout(hostRef, onLayout);
usePlatformInputMethods(hostRef, ref, classList, style);
useResponderEvents(hostRef, {
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture
});

return createElement(component, {
accessibilityLabel,
Expand All @@ -310,22 +329,6 @@ const TextInput = forwardRef<TextInputProps, *>((props, ref) => {
onKeyDown: handleKeyDown,
onScroll,
onSelect: handleSelectionChange,
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
onResponderGrant,
onResponderMove,
onResponderReject,
onResponderRelease,
onResponderStart,
onResponderTerminate,
onResponderTerminationRequest,
onScrollShouldSetResponder,
onScrollShouldSetResponderCapture,
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture,
placeholder,
pointerEvents,
testID,
Expand Down
35 changes: 19 additions & 16 deletions packages/react-native-web/src/exports/View/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import css from '../StyleSheet/css';
import setAndForwardRef from '../../modules/setAndForwardRef';
import useElementLayout from '../../hooks/useElementLayout';
import usePlatformMethods from '../../hooks/usePlatformMethods';
import useResponderEvents from '../../hooks/useResponderEvents';
import StyleSheet from '../StyleSheet';
import TextAncestorContext from '../Text/TextAncestorContext';
import React, { forwardRef, useContext, useRef } from 'react';
Expand Down Expand Up @@ -132,21 +133,7 @@ const View = forwardRef<ViewProps, *>((props, ref) => {

useElementLayout(hostRef, onLayout);
usePlatformMethods(hostRef, ref, classList, style);

return createElement('div', {
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRelationship,
accessibilityRole,
accessibilityState,
accessibilityValue,
children,
classList,
importantForAccessibility,
nativeID,
onBlur,
onContextMenu,
onFocus,
useResponderEvents(hostRef, {
onMoveShouldSetResponder,
onMoveShouldSetResponderCapture,
onResponderEnd,
Expand All @@ -162,7 +149,23 @@ const View = forwardRef<ViewProps, *>((props, ref) => {
onSelectionChangeShouldSetResponder,
onSelectionChangeShouldSetResponderCapture,
onStartShouldSetResponder,
onStartShouldSetResponderCapture,
onStartShouldSetResponderCapture
});

return createElement('div', {
accessibilityLabel,
accessibilityLiveRegion,
accessibilityRelationship,
accessibilityRole,
accessibilityState,
accessibilityValue,
children,
classList,
importantForAccessibility,
nativeID,
onBlur,
onContextMenu,
onFocus,
pointerEvents,
ref: setRef,
style,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native-web/src/exports/View/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export type ViewProps = {
onResponderRelease?: (e: any) => void,
onResponderStart?: (e: any) => void,
onResponderTerminate?: (e: any) => void,
onResponderTerminationRequest?: (e: any) => void,
onResponderTerminationRequest?: (e: any) => boolean,
onScrollShouldSetResponder?: (e: any) => boolean,
onScrollShouldSetResponderCapture?: (e: any) => boolean,
onSelectionChangeShouldSetResponder?: (e: any) => boolean,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`modules/createElement normalizes event.nativeEvent 1`] = `
Object {
"_normalized": true,
"bubbles": undefined,
"cancelable": undefined,
"changedTouches": Array [],
"defaultPrevented": undefined,
"identifier": undefined,
"locationX": undefined,
"locationY": undefined,
"pageX": undefined,
"pageY": undefined,
"preventDefault": [Function],
"stopImmediatePropagation": [Function],
"stopPropagation": [Function],
"target": undefined,
"timestamp": 1496876171255,
"touches": Array [],
"type": undefined,
"which": undefined,
}
`;

exports[`modules/createElement renders different DOM elements 1`] = `<span />`;

exports[`modules/createElement renders different DOM elements 2`] = `<main />`;
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,6 @@ describe('modules/createElement', () => {
expect(component).toMatchSnapshot();
});

test('normalizes event.nativeEvent', done => {
const onClick = e => {
e.nativeEvent.timestamp = 1496876171255;
expect(e.nativeEvent).toMatchSnapshot();
done();
};
const component = shallow(createElement('span', { onClick }));
component.find('span').simulate('click', {
nativeEvent: {}
});
});

describe('prop "accessibilityRole"', () => {
test('and string component type', () => {
const component = shallow(createElement('span', { accessibilityRole: 'link' }));
Expand Down
73 changes: 4 additions & 69 deletions packages/react-native-web/src/exports/createElement/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,83 +8,18 @@
*/

import AccessibilityUtil from '../../modules/AccessibilityUtil';
import { canUseDOM } from 'fbjs/lib/ExecutionEnvironment';
import createDOMProps from '../../modules/createDOMProps';
import { injectEventPluginsByName } from 'react-dom/unstable-native-dependencies';
import normalizeNativeEvent from '../../modules/normalizeNativeEvent';
import React from 'react';
import ResponderEventPlugin from '../../modules/ResponderEventPlugin';

if (canUseDOM) {
try {
injectEventPluginsByName({
ResponderEventPlugin
});
} catch (error) {
// Ignore errors caused by attempting to re-inject the plugin when app
// scripts are being re-evaluated (e.g., development hot reloading) while
// the ReactDOM instance is preserved.
}
}

const isModifiedEvent = event =>
!!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);

/**
* Ensure event handlers receive an event of the expected shape. The 'button'
* role – for accessibility reasons and functional equivalence to the native
* button element – must also support synthetic keyboard activation of onclick,
* and remove event handlers when disabled.
*/
const eventHandlerNames = {
onBlur: true,
onClick: true,
onClickCapture: true,
onContextMenu: true,
onFocus: true,
onResponderRelease: true,
onTouchCancel: true,
onTouchCancelCapture: true,
onTouchEnd: true,
onTouchEndCapture: true,
onTouchMove: true,
onTouchMoveCapture: true,
onTouchStart: true,
onTouchStartCapture: true
};
const adjustProps = domProps => {
const { onClick, onResponderRelease, role } = domProps;
const { onClick, role } = domProps;

const isButtonLikeRole = AccessibilityUtil.buttonLikeRoles[role];
const isDisabled = AccessibilityUtil.isDisabled(domProps);
const isLinkRole = role === 'link';

Object.keys(domProps).forEach(propName => {
const prop = domProps[propName];
const isEventHandler = typeof prop === 'function' && eventHandlerNames[propName];
if (isEventHandler) {
if (isButtonLikeRole && isDisabled) {
domProps[propName] = undefined;
} else {
// TODO: move this out of the render path
domProps[propName] = e => {
e.nativeEvent = normalizeNativeEvent(e.nativeEvent);
return prop(e);
};
}
}
});

// Cancel click events if the responder system is being used on a link
// element. Click events are not an expected part of the React Native API,
// and browsers dispatch click events that cannot otherwise be cancelled from
// preceding mouse events in the responder system.
if (isLinkRole && onResponderRelease) {
domProps.onClick = function(e) {
if (!e.isDefaultPrevented() && !isModifiedEvent(e.nativeEvent) && !domProps.target) {
e.preventDefault();
}
};
// Button-like roles should not trigger 'onClick' if they are disabled.
if (isButtonLikeRole && isDisabled && domProps.onClick != null) {
domProps.onClick = undefined;
}

// Button-like roles should trigger 'onClick' if SPACE or ENTER keys are pressed.
Expand Down
Loading

0 comments on commit d26f433

Please sign in to comment.