Add internal logic for listening to event responders (#15168)
* Add the logic for listening to event responders
Dominic Gannaway committed
Mar 21, 2019 at 12:32 UTC
66f280c87b05885ee55320a5e107a534a50f9375
4 files changed
+108
-35
packages/react-dom/src/client/ReactDOMComponent.js
+72
-1
@@ -13,6 +13,8 @@ import {registrationNameModules} from 'events/EventPluginRegistry';
13
import warning from 'shared/warning';
14
import {canUseDOM} from 'shared/ExecutionEnvironment';
15
import warningWithoutStack from 'shared/warningWithoutStack';
16
+import type {ReactEventResponder} from 'shared/ReactTypes';
17
+import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
18
19
import {
20
getValueForAttribute,
@@ -57,7 +59,12 @@ import {
59
TOP_SUBMIT,
60
TOP_TOGGLE,
61
} from '../events/DOMTopLevelEventTypes';
60
-import {listenTo, trapBubbledEvent} from '../events/ReactBrowserEventEmitter';
62
+import {
63
+ listenTo,
64
+ trapBubbledEvent,
65
+ getListeningSetForElement,
66
+} from '../events/ReactBrowserEventEmitter';
67
+import {trapEventForResponderEventSystem} from '../events/ReactDOMEventListener.js';
68
import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
69
import {
70
createDangerousStringForStyles,
@@ -78,6 +85,8 @@ import {validateProperties as validateARIAProperties} from '../shared/ReactDOMIn
85
import {validateProperties as validateInputProperties} from '../shared/ReactDOMNullInputValuePropHook';
86
import {validateProperties as validateUnknownProperties} from '../shared/ReactDOMUnknownPropertyHook';
87
88
+import {enableEventAPI} from 'shared/ReactFeatureFlags';
89
+
90
let didWarnInvalidHydration = false;
91
let didWarnShadyDOM = false;
92
@@ -1267,3 +1276,65 @@ export function restoreControlledState(
1276
return;
1277
}
1278
}
1279
+
1280
+export function listenToEventResponderEvents(
1281
+ eventResponder: ReactEventResponder,
1282
+ element: Element | Document,
1283
+): void {
1284
+ if (enableEventAPI) {
1285
+ const {targetEventTypes} = eventResponder;
1286
+ // Get the listening Set for this element. We use this to track
1287
+ // what events we're listening to.
1288
+ const listeningSet = getListeningSetForElement(element);
1289
+
1290
+ // Go through each target event type of the event responder
1291
+ for (let i = 0, length = targetEventTypes.length; i < length; ++i) {
1292
+ const targetEventType = targetEventTypes[i];
1293
+ let topLevelType;
1294
+ let capture = false;
1295
+ let passive = true;
1296
+
1297
+ // If no event config object is provided (i.e. - only a string),
1298
+ // we default to enabling passive and not capture.
1299
+ if (typeof targetEventType === 'string') {
1300
+ topLevelType = targetEventType;
1301
+ } else {
1302
+ if (__DEV__) {
1303
+ warning(
1304
+ typeof targetEventType === 'object' && targetEventType !== null,
1305
+ 'Event Responder: invalid entry in targetEventTypes array. ' +
1306
+ 'Entry must be string or an object. Instead, got %s.',
1307
+ targetEventType,
1308
+ );
1309
+ }
1310
+ const targetEventConfigObject = ((targetEventType: any): {
1311
+ name: string,
1312
+ passive?: boolean,
1313
+ capture?: boolean,
1314
+ });
1315
+ topLevelType = targetEventConfigObject.name;
1316
+ if (targetEventConfigObject.passive !== undefined) {
1317
+ passive = targetEventConfigObject.passive;
1318
+ }
1319
+ if (targetEventConfigObject.capture !== undefined) {
1320
+ capture = targetEventConfigObject.capture;
1321
+ }
1322
+ }
1323
+ // Create a unique name for this event, plus its properties. We'll
1324
+ // use this to ensure we don't listen to the same event with the same
1325
+ // properties again.
1326
+ const passiveKey = passive ? '_passive' : '';
1327
+ const captureKey = capture ? '_capture' : '';
1328
+ const listeningName = `${topLevelType}${passiveKey}${captureKey}`;
1329
+ if (!listeningSet.has(listeningName)) {
1330
+ trapEventForResponderEventSystem(
1331
+ element,
1332
+ ((topLevelType: any): DOMTopLevelEventType),
1333
+ capture,
1334
+ passive,
1335
+ );
1336
+ listeningSet.add(listeningName);
1337
+ }
1338
+ }
1339
+ }
1340
+}
packages/react-dom/src/client/ReactDOMHostConfig.js
+5
-3
@@ -24,6 +24,7 @@ import {
24
warnForDeletedHydratableText,
25
warnForInsertedHydratedElement,
26
warnForInsertedHydratedText,
27
+ listenToEventResponderEvents,
28
} from './ReactDOMComponent';
29
import {getSelectionInformation, restoreSelection} from './ReactInputSelection';
30
import setTextContent from './setTextContent';
@@ -860,15 +861,16 @@ export function handleEventComponent(
861
eventResponder: ReactEventResponder,
862
rootContainerInstance: Container,
863
internalInstanceHandle: Object,
863
-) {
864
- // TODO: add handleEventComponent implementation
864
+): void {
865
+ const rootElement = rootContainerInstance.ownerDocument;
866
+ listenToEventResponderEvents(eventResponder, rootElement);
867
}
868
869
export function handleEventTarget(
870
type: Symbol | number,
871
props: Props,
872
internalInstanceHandle: Object,
871
-) {
873
+): void {
874
// Touch target hit slop handling
875
if (type === REACT_EVENT_TARGET_TOUCH_HIT) {
876
// Validates that there is a single element
packages/react-dom/src/events/ReactBrowserEventEmitter.js
+3
-3
@@ -90,12 +90,12 @@ const elementListeningSets:
90
| WeakMap
91
| Map<
92
Document | Element | Node,
93
- Set<DOMTopLevelEventType>,
93
+ Set<DOMTopLevelEventType | string>,
94
> = new PossiblyWeakMap();
95
96
-function getListeningSetForElement(
96
+export function getListeningSetForElement(
97
element: Document | Element | Node,
98
-): Set<DOMTopLevelEventType> {
98
+): Set<DOMTopLevelEventType | string> {
99
let listeningSet = elementListeningSets.get(element);
100
if (listeningSet === undefined) {
101
listeningSet = new Set();
scripts/rollup/results.json
+28
-28
@@ -60,15 +60,15 @@
60
"filename": "react-dom.development.js",
61
"bundleType": "NODE_DEV",
62
"packageName": "react-dom",
63
- "size": 798048,
64
- "gzip": 181482
63
+ "size": 813661,
64
+ "gzip": 184364
65
},
66
{
67
"filename": "react-dom.production.min.js",
68
"bundleType": "NODE_PROD",
69
"packageName": "react-dom",
70
- "size": 107733,
71
- "gzip": 34431
70
+ "size": 108035,
71
+ "gzip": 34515
72
},
73
{
74
"filename": "ReactDOM-dev.js",
@@ -102,15 +102,15 @@
102
"filename": "react-dom-test-utils.development.js",
103
"bundleType": "NODE_DEV",
104
"packageName": "react-dom",
105
- "size": 47988,
106
- "gzip": 13245
105
+ "size": 48334,
106
+ "gzip": 13206
107
},
108
{
109
"filename": "react-dom-test-utils.production.min.js",
110
"bundleType": "NODE_PROD",
111
"packageName": "react-dom",
112
- "size": 10288,
113
- "gzip": 3812
112
+ "size": 9954,
113
+ "gzip": 3660
114
},
115
{
116
"filename": "ReactTestUtils-dev.js",
@@ -137,15 +137,15 @@
137
"filename": "react-dom-unstable-native-dependencies.development.js",
138
"bundleType": "NODE_DEV",
139
"packageName": "react-dom",
140
- "size": 61725,
141
- "gzip": 16151
140
+ "size": 61643,
141
+ "gzip": 16033
142
},
143
{
144
"filename": "react-dom-unstable-native-dependencies.production.min.js",
145
"bundleType": "NODE_PROD",
146
"packageName": "react-dom",
147
- "size": 11001,
148
- "gzip": 3783
147
+ "size": 10669,
148
+ "gzip": 3640
149
},
150
{
151
"filename": "ReactDOMUnstableNativeDependencies-dev.js",
@@ -179,15 +179,15 @@
179
"filename": "react-dom-server.browser.development.js",
180
"bundleType": "NODE_DEV",
181
"packageName": "react-dom",
182
- "size": 129302,
183
- "gzip": 34571
182
+ "size": 132758,
183
+ "gzip": 35195
184
},
185
{
186
"filename": "react-dom-server.browser.production.min.js",
187
"bundleType": "NODE_PROD",
188
"packageName": "react-dom",
189
- "size": 19590,
190
- "gzip": 7447
189
+ "size": 19756,
190
+ "gzip": 7540
191
},
192
{
193
"filename": "ReactDOMServer-dev.js",
@@ -207,15 +207,15 @@
207
"filename": "react-dom-server.node.development.js",
208
"bundleType": "NODE_DEV",
209
"packageName": "react-dom",
210
- "size": 131409,
211
- "gzip": 35129
210
+ "size": 134747,
211
+ "gzip": 35752
212
},
213
{
214
"filename": "react-dom-server.node.production.min.js",
215
"bundleType": "NODE_PROD",
216
"packageName": "react-dom",
217
- "size": 20483,
218
- "gzip": 7757
217
+ "size": 20639,
218
+ "gzip": 7850
219
},
220
{
221
"filename": "react-art.development.js",
@@ -718,8 +718,8 @@
718
"filename": "react-dom.profiling.min.js",
719
"bundleType": "NODE_PROFILING",
720
"packageName": "react-dom",
721
- "size": 110923,
722
- "gzip": 35060
721
+ "size": 111211,
722
+ "gzip": 35133
723
},
724
{
725
"filename": "ReactNativeRenderer-profiling.js",
@@ -1054,22 +1054,22 @@
1054
"filename": "react-dom-unstable-fire.development.js",
1055
"bundleType": "NODE_DEV",
1056
"packageName": "react-dom",
1057
- "size": 798401,
1058
- "gzip": 181622
1057
+ "size": 814014,
1058
+ "gzip": 184503
1059
},
1060
{
1061
"filename": "react-dom-unstable-fire.production.min.js",
1062
"bundleType": "NODE_PROD",
1063
"packageName": "react-dom",
1064
- "size": 107747,
1065
- "gzip": 34441
1064
+ "size": 108049,
1065
+ "gzip": 34524
1066
},
1067
{
1068
"filename": "react-dom-unstable-fire.profiling.min.js",
1069
"bundleType": "NODE_PROFILING",
1070
"packageName": "react-dom",
1071
- "size": 110937,
1072
- "gzip": 35069
1071
+ "size": 111225,
1072
+ "gzip": 35142
1073
},
1074
{
1075
"filename": "ReactFire-dev.js",