Experimental event API: refactor responder modules for lifecycle inclusion (#15322)
Dominic Gannaway committed
Apr 4, 2019 at 23:28 UTC
b93a8a9bb8460a3d582072d3b252ecc15c6ea0f5
11 files changed
+485
-478
packages/events/EventTypes.js
+23
-17
@@ -10,34 +10,40 @@
10
import type {AnyNativeEvent} from 'events/PluginModuleType';
11
import type {ReactEventResponderEventType} from 'shared/ReactTypes';
12
13
-export type EventResponderContext = {
14
- event: AnyNativeEvent,
15
- eventTarget: Element | Document,
16
- eventType: string,
17
- isPassive: () => boolean,
18
- isPassiveSupported: () => boolean,
19
- dispatchEvent: <E>(
20
- eventObject: E,
21
- {
22
- capture?: boolean,
23
- discrete?: boolean,
24
- stopPropagation?: boolean,
25
- },
13
+export type ResponderEvent = {
14
+ nativeEvent: AnyNativeEvent,
15
+ target: Element | Document,
16
+ type: string,
17
+ passive: boolean,
18
+ passiveSupported: boolean,
19
+};
20
+
21
+export type ResponderDispatchEventOptions = {
22
+ capture?: boolean,
23
+ discrete?: boolean,
24
+ stopPropagation?: boolean,
25
+};
26
+
27
+export type ResponderContext = {
28
+ dispatchEvent: (
29
+ eventObject: Object,
30
+ otpions: ResponderDispatchEventOptions,
31
) => void,
32
isTargetWithinElement: (
33
childTarget: Element | Document,
34
parentTarget: Element | Document,
35
) => boolean,
31
- isTargetOwned: (Element | Document) => boolean,
36
isTargetWithinEventComponent: (Element | Document) => boolean,
37
isPositionWithinTouchHitTarget: (x: number, y: number) => boolean,
38
addRootEventTypes: (
39
+ document: Document,
40
rootEventTypes: Array<ReactEventResponderEventType>,
41
) => void,
42
removeRootEventTypes: (
43
rootEventTypes: Array<ReactEventResponderEventType>,
44
) => void,
40
- requestOwnership: (target: Element | Document | null) => boolean,
41
- releaseOwnership: (target: Element | Document | null) => boolean,
42
- withAsyncDispatching: (func: () => void) => void,
45
+ hasOwnership: () => boolean,
46
+ requestOwnership: () => boolean,
47
+ releaseOwnership: () => boolean,
48
+ setTimeout: (func: () => void, timeout: number) => TimeoutID,
49
};
packages/react-dom/src/events/DOMEventResponderSystem.js
+222
-243
@@ -6,6 +6,11 @@
6
* @flow
7
*/
8
9
+import type {
10
+ ResponderContext,
11
+ ResponderEvent,
12
+ ResponderDispatchEventOptions,
13
+} from 'events/EventTypes';
14
import {
15
type EventSystemFlags,
16
IS_PASSIVE,
@@ -20,13 +25,13 @@ import type {
25
import type {DOMTopLevelEventType} from 'events/TopLevelEventTypes';
26
import {batchedUpdates, interactiveUpdates} from 'events/ReactGenericBatching';
27
import type {Fiber} from 'react-reconciler/src/ReactFiber';
23
-import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
24
-
28
+import warning from 'shared/warning';
29
import {enableEventAPI} from 'shared/ReactFeatureFlags';
30
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
27
-import warning from 'shared/warning';
31
29
-let listenToResponderEventTypesImpl;
32
+import {getClosestInstanceFromNode} from '../client/ReactDOMComponentTree';
33
+
34
+export let listenToResponderEventTypesImpl;
35
36
export function setListenToResponderEventTypes(
37
_listenToResponderEventTypesImpl: Function,
@@ -34,31 +39,215 @@ export function setListenToResponderEventTypes(
39
listenToResponderEventTypesImpl = _listenToResponderEventTypesImpl;
40
}
41
37
-const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
42
+type EventQueue = {
43
+ bubble: null | Array<$Shape<PartialEventObject>>,
44
+ capture: null | Array<$Shape<PartialEventObject>>,
45
+ discrete: boolean,
46
+};
47
+
48
+type PartialEventObject = {
49
+ listener: ($Shape<PartialEventObject>) => void,
50
+ target: Element | Document,
51
+ type: string,
52
+};
53
+
54
+let currentOwner = null;
55
+let currentFiber: Fiber;
56
+let currentResponder: ReactEventResponder;
57
+let currentEventQueue: EventQueue;
58
+
59
+const eventResponderContext: ResponderContext = {
60
+ dispatchEvent(
61
+ possibleEventObject: Object,
62
+ {capture, discrete, stopPropagation}: ResponderDispatchEventOptions,
63
+ ): void {
64
+ const eventQueue = currentEventQueue;
65
+ const {listener, target, type} = possibleEventObject;
66
+
67
+ if (listener == null || target == null || type == null) {
68
+ throw new Error(
69
+ 'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.',
70
+ );
71
+ }
72
+ if (__DEV__) {
73
+ possibleEventObject.preventDefault = () => {
74
+ // Update this warning when we have a story around dealing with preventDefault
75
+ warning(
76
+ false,
77
+ 'preventDefault() is no longer available on event objects created from event responder modules.',
78
+ );
79
+ };
80
+ possibleEventObject.stopPropagation = () => {
81
+ // Update this warning when we have a story around dealing with stopPropgation
82
+ warning(
83
+ false,
84
+ 'stopPropagation() is no longer available on event objects created from event responder modules.',
85
+ );
86
+ };
87
+ }
88
+ const eventObject = ((possibleEventObject: any): $Shape<
89
+ PartialEventObject,
90
+ >);
91
+ let events;
92
+
93
+ if (capture) {
94
+ events = eventQueue.capture;
95
+ if (events === null) {
96
+ events = eventQueue.capture = [];
97
+ }
98
+ } else {
99
+ events = eventQueue.bubble;
100
+ if (events === null) {
101
+ events = eventQueue.bubble = [];
102
+ }
103
+ }
104
+ if (discrete) {
105
+ eventQueue.discrete = true;
106
+ }
107
+ events.push(eventObject);
108
+
109
+ if (stopPropagation) {
110
+ eventsWithStopPropagation.add(eventObject);
111
+ }
112
+ },
113
+ isPositionWithinTouchHitTarget(x: number, y: number): boolean {
114
+ return false;
115
+ },
116
+ isTargetWithinEventComponent(target: Element | Document): boolean {
117
+ const eventFiber = currentFiber;
118
+
119
+ if (target != null) {
120
+ let fiber = getClosestInstanceFromNode(target);
121
+ while (fiber !== null) {
122
+ if (fiber === eventFiber || fiber === eventFiber.alternate) {
123
+ return true;
124
+ }
125
+ fiber = fiber.return;
126
+ }
127
+ }
128
+ return false;
129
+ },
130
+ isTargetWithinElement(
131
+ childTarget: Element | Document,
132
+ parentTarget: Element | Document,
133
+ ): boolean {
134
+ const childFiber = getClosestInstanceFromNode(childTarget);
135
+ const parentFiber = getClosestInstanceFromNode(parentTarget);
136
+
137
+ let node = childFiber;
138
+ while (node !== null) {
139
+ if (node === parentFiber) {
140
+ return true;
141
+ }
142
+ node = node.return;
143
+ }
144
+ return false;
145
+ },
146
+ addRootEventTypes(
147
+ doc: Document,
148
+ rootEventTypes: Array<ReactEventResponderEventType>,
149
+ ): void {
150
+ listenToResponderEventTypesImpl(rootEventTypes, doc);
151
+ const eventComponent = currentFiber;
152
+ for (let i = 0; i < rootEventTypes.length; i++) {
153
+ const rootEventType = rootEventTypes[i];
154
+ const topLevelEventType =
155
+ typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
156
+ let rootEventComponents = rootEventTypesToEventComponents.get(
157
+ topLevelEventType,
158
+ );
159
+ if (rootEventComponents === undefined) {
160
+ rootEventComponents = new Set();
161
+ rootEventTypesToEventComponents.set(
162
+ topLevelEventType,
163
+ rootEventComponents,
164
+ );
165
+ }
166
+ rootEventComponents.add(eventComponent);
167
+ }
168
+ },
169
+ removeRootEventTypes(
170
+ rootEventTypes: Array<ReactEventResponderEventType>,
171
+ ): void {
172
+ const eventComponent = currentFiber;
173
+ for (let i = 0; i < rootEventTypes.length; i++) {
174
+ const rootEventType = rootEventTypes[i];
175
+ const topLevelEventType =
176
+ typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
177
+ let rootEventComponents = rootEventTypesToEventComponents.get(
178
+ topLevelEventType,
179
+ );
180
+ if (rootEventComponents !== undefined) {
181
+ rootEventComponents.delete(eventComponent);
182
+ }
183
+ }
184
+ },
185
+ hasOwnership(): boolean {
186
+ return currentOwner === currentFiber;
187
+ },
188
+ requestOwnership(): boolean {
189
+ if (currentOwner !== null) {
190
+ return false;
191
+ }
192
+ currentOwner = currentFiber;
193
+ return true;
194
+ },
195
+ releaseOwnership(): boolean {
196
+ if (currentOwner !== currentFiber) {
197
+ return false;
198
+ }
199
+ currentOwner = null;
200
+ return false;
201
+ },
202
+ setTimeout(func: () => void, delay): TimeoutID {
203
+ const contextResponder = currentResponder;
204
+ const contextFiber = currentFiber;
205
+ return setTimeout(() => {
206
+ const previousEventQueue = currentEventQueue;
207
+ const previousFiber = currentFiber;
208
+ const previousResponder = currentResponder;
209
+ currentEventQueue = createEventQueue();
210
+ currentResponder = contextResponder;
211
+ currentFiber = contextFiber;
212
+ try {
213
+ func();
214
+ batchedUpdates(processEventQueue, currentEventQueue);
215
+ } finally {
216
+ currentFiber = previousFiber;
217
+ currentEventQueue = previousEventQueue;
218
+ currentResponder = previousResponder;
219
+ }
220
+ }, delay);
221
+ },
222
+};
223
224
const rootEventTypesToEventComponents: Map<
225
DOMTopLevelEventType | string,
226
Set<Fiber>,
227
> = new Map();
228
+const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
229
+const eventsWithStopPropagation:
230
+ | WeakSet
231
+ | Set<$Shape<PartialEventObject>> = new PossiblyWeakSet();
232
const targetEventTypeCached: Map<
233
Array<ReactEventResponderEventType>,
234
Set<DOMTopLevelEventType>,
235
> = new Map();
47
-const targetOwnership: Map<EventTarget, Fiber> = new Map();
48
-const eventsWithStopPropagation:
49
- | WeakSet
50
- | Set<$Shape<PartialEventObject>> = new PossiblyWeakSet();
236
52
-type PartialEventObject = {
53
- listener: ($Shape<PartialEventObject>) => void,
54
- target: Element | Document,
55
- type: string,
56
-};
57
-type EventQueue = {
58
- bubble: null | Array<$Shape<PartialEventObject>>,
59
- capture: null | Array<$Shape<PartialEventObject>>,
60
- discrete: boolean,
61
-};
237
+function createResponderEvent(
238
+ topLevelType: string,
239
+ nativeEvent: AnyNativeEvent,
240
+ nativeEventTarget: Element | Document,
241
+ eventSystemFlags: EventSystemFlags,
242
+): ResponderEvent {
243
+ return {
244
+ nativeEvent: nativeEvent,
245
+ target: nativeEventTarget,
246
+ type: topLevelType,
247
+ passive: (eventSystemFlags & IS_PASSIVE) !== 0,
248
+ passiveSupported: (eventSystemFlags & PASSIVE_NOT_SUPPORTED) === 0,
249
+ };
250
+}
251
252
function createEventQueue(): EventQueue {
253
return {
@@ -100,8 +289,8 @@ function processEvents(
289
}
290
}
291
103
-function processEventQueue(eventQueue: EventQueue): void {
104
- const {bubble, capture, discrete} = eventQueue;
292
+export function processEventQueue(): void {
293
+ const {bubble, capture, discrete} = currentEventQueue;
294
295
if (discrete) {
296
interactiveUpdates(() => {
@@ -112,218 +301,6 @@ function processEventQueue(eventQueue: EventQueue): void {
301
}
302
}
303
115
-// TODO add context methods for dispatching events
116
-function DOMEventResponderContext(
117
- topLevelType: DOMTopLevelEventType,
118
- nativeEvent: AnyNativeEvent,
119
- nativeEventTarget: EventTarget,
120
- eventSystemFlags: EventSystemFlags,
121
-) {
122
- this.event = nativeEvent;
123
- this.eventTarget = nativeEventTarget;
124
- this.eventType = topLevelType;
125
- this._flags = eventSystemFlags;
126
- this._fiber = null;
127
- this._responder = null;
128
- this._discreteEvents = null;
129
- this._nonDiscreteEvents = null;
130
- this._isBatching = true;
131
- this._eventQueue = createEventQueue();
132
-}
133
-
134
-DOMEventResponderContext.prototype.isPassive = function(): boolean {
135
- return (this._flags & IS_PASSIVE) !== 0;
136
-};
137
-
138
-DOMEventResponderContext.prototype.isPassiveSupported = function(): boolean {
139
- return (this._flags & PASSIVE_NOT_SUPPORTED) === 0;
140
-};
141
-
142
-DOMEventResponderContext.prototype.dispatchEvent = function(
143
- possibleEventObject: Object,
144
- {
145
- capture,
146
- discrete,
147
- stopPropagation,
148
- }: {
149
- capture?: boolean,
150
- discrete?: boolean,
151
- stopPropagation?: boolean,
152
- },
153
-): void {
154
- const eventQueue = this._eventQueue;
155
- const {listener, target, type} = possibleEventObject;
156
-
157
- if (listener == null || target == null || type == null) {
158
- throw new Error(
159
- 'context.dispatchEvent: "listener", "target" and "type" fields on event object are required.',
160
- );
161
- }
162
- if (__DEV__) {
163
- possibleEventObject.preventDefault = () => {
164
- // Update this warning when we have a story around dealing with preventDefault
165
- warning(
166
- false,
167
- 'preventDefault() is no longer available on event objects created from event responder modules.',
168
- );
169
- };
170
- possibleEventObject.stopPropagation = () => {
171
- // Update this warning when we have a story around dealing with stopPropgation
172
- warning(
173
- false,
174
- 'stopPropagation() is no longer available on event objects created from event responder modules.',
175
- );
176
- };
177
- }
178
- const eventObject = ((possibleEventObject: any): $Shape<PartialEventObject>);
179
- let events;
180
-
181
- if (capture) {
182
- events = eventQueue.capture;
183
- if (events === null) {
184
- events = eventQueue.capture = [];
185
- }
186
- } else {
187
- events = eventQueue.bubble;
188
- if (events === null) {
189
- events = eventQueue.bubble = [];
190
- }
191
- }
192
- if (discrete) {
193
- eventQueue.discrete = true;
194
- }
195
- events.push(eventObject);
196
-
197
- if (stopPropagation) {
198
- eventsWithStopPropagation.add(eventObject);
199
- }
200
-};
201
-
202
-DOMEventResponderContext.prototype.isTargetWithinEventComponent = function(
203
- target: AnyNativeEvent,
204
-): boolean {
205
- const eventFiber = this._fiber;
206
-
207
- if (target != null) {
208
- let fiber = getClosestInstanceFromNode(target);
209
- while (fiber !== null) {
210
- if (fiber === eventFiber || fiber === eventFiber.alternate) {
211
- return true;
212
- }
213
- fiber = fiber.return;
214
- }
215
- }
216
- return false;
217
-};
218
-
219
-DOMEventResponderContext.prototype.isTargetWithinElement = function(
220
- childTarget: EventTarget,
221
- parentTarget: EventTarget,
222
-): boolean {
223
- const childFiber = getClosestInstanceFromNode(childTarget);
224
- const parentFiber = getClosestInstanceFromNode(parentTarget);
225
-
226
- let currentFiber = childFiber;
227
- while (currentFiber !== null) {
228
- if (currentFiber === parentFiber) {
229
- return true;
230
- }
231
- currentFiber = currentFiber.return;
232
- }
233
- return false;
234
-};
235
-
236
-DOMEventResponderContext.prototype.addRootEventTypes = function(
237
- rootEventTypes: Array<ReactEventResponderEventType>,
238
-) {
239
- const element = this.eventTarget.ownerDocument;
240
- listenToResponderEventTypesImpl(rootEventTypes, element);
241
- const eventComponent = this._fiber;
242
- for (let i = 0; i < rootEventTypes.length; i++) {
243
- const rootEventType = rootEventTypes[i];
244
- const topLevelEventType =
245
- typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
246
- let rootEventComponents = rootEventTypesToEventComponents.get(
247
- topLevelEventType,
248
- );
249
- if (rootEventComponents === undefined) {
250
- rootEventComponents = new Set();
251
- rootEventTypesToEventComponents.set(
252
- topLevelEventType,
253
- rootEventComponents,
254
- );
255
- }
256
- rootEventComponents.add(eventComponent);
257
- }
258
-};
259
-
260
-DOMEventResponderContext.prototype.removeRootEventTypes = function(
261
- rootEventTypes: Array<ReactEventResponderEventType>,
262
-): void {
263
- const eventComponent = this._fiber;
264
- for (let i = 0; i < rootEventTypes.length; i++) {
265
- const rootEventType = rootEventTypes[i];
266
- const topLevelEventType =
267
- typeof rootEventType === 'string' ? rootEventType : rootEventType.name;
268
- let rootEventComponents = rootEventTypesToEventComponents.get(
269
- topLevelEventType,
270
- );
271
- if (rootEventComponents !== undefined) {
272
- rootEventComponents.delete(eventComponent);
273
- }
274
- }
275
-};
276
-
277
-DOMEventResponderContext.prototype.isPositionWithinTouchHitTarget = function() {
278
- // TODO
279
-};
280
-
281
-DOMEventResponderContext.prototype.isTargetOwned = function(
282
- targetElement: Element | Node,
283
-): boolean {
284
- const targetDoc = targetElement.ownerDocument;
285
- return targetOwnership.has(targetDoc);
286
-};
287
-
288
-DOMEventResponderContext.prototype.requestOwnership = function(
289
- targetElement: Element | Node,
290
-): boolean {
291
- const targetDoc = targetElement.ownerDocument;
292
- if (targetOwnership.has(targetDoc)) {
293
- return false;
294
- }
295
- targetOwnership.set(targetDoc, this._fiber);
296
- return true;
297
-};
298
-
299
-DOMEventResponderContext.prototype.releaseOwnership = function(
300
- targetElement: Element | Node,
301
-): boolean {
302
- const targetDoc = targetElement.ownerDocument;
303
- if (!targetOwnership.has(targetDoc)) {
304
- return false;
305
- }
306
- const owner = targetOwnership.get(targetDoc);
307
- if (owner === this._fiber || owner === this._fiber.alternate) {
308
- targetOwnership.delete(targetDoc);
309
- return true;
310
- }
311
- return false;
312
-};
313
-
314
-DOMEventResponderContext.prototype.withAsyncDispatching = function(
315
- func: () => void,
316
-) {
317
- const previousEventQueue = this._eventQueue;
318
- this._eventQueue = createEventQueue();
319
- try {
320
- func();
321
- batchedUpdates(processEventQueue, this._eventQueue);
322
- } finally {
323
- this._eventQueue = previousEventQueue;
324
- }
325
-};
326
-
304
function getTargetEventTypes(
305
eventTypes: Array<ReactEventResponderEventType>,
306
): Set<DOMTopLevelEventType> {
@@ -345,7 +322,7 @@ function getTargetEventTypes(
322
function handleTopLevelType(
323
topLevelType: DOMTopLevelEventType,
324
fiber: Fiber,
348
- context: Object,
325
+ responderEvent: ResponderEvent,
326
isRootLevelEvent: boolean,
327
): void {
328
const responder: ReactEventResponder = fiber.type.responder;
@@ -360,9 +337,10 @@ function handleTopLevelType(
337
if (state === null && responder.createInitialState !== undefined) {
338
state = fiber.stateNode.state = responder.createInitialState(props);
339
}
363
- context._fiber = fiber;
364
- context._responder = responder;
365
- responder.handleEvent(context, props, state);
340
+ currentFiber = fiber;
341
+ currentResponder = responder;
342
+
343
+ responder.onEvent(responderEvent, eventResponderContext, props, state);
344
}
345
346
export function runResponderEventsInBatch(
@@ -373,17 +351,18 @@ export function runResponderEventsInBatch(
351
eventSystemFlags: EventSystemFlags,
352
): void {
353
if (enableEventAPI) {
376
- const context = new DOMEventResponderContext(
377
- topLevelType,
354
+ currentEventQueue = createEventQueue();
355
+ const responderEvent = createResponderEvent(
356
+ ((topLevelType: any): string),
357
nativeEvent,
379
- nativeEventTarget,
358
+ ((nativeEventTarget: any): Element | Document),
359
eventSystemFlags,
360
);
361
let node = targetFiber;
362
// Traverse up the fiber tree till we find event component fibers.
363
while (node !== null) {
364
if (node.tag === EventComponent) {
386
- handleTopLevelType(topLevelType, node, context, false);
365
+ handleTopLevelType(topLevelType, node, responderEvent, false);
366
}
367
node = node.return;
368
}
@@ -399,11 +378,11 @@ export function runResponderEventsInBatch(
378
handleTopLevelType(
379
topLevelType,
380
rootEventComponentFiber,
402
- context,
381
+ responderEvent,
382
true,
383
);
384
}
385
}
407
- processEventQueue(context._eventQueue);
386
+ processEventQueue();
387
}
388
}
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+49
-53
@@ -13,10 +13,10 @@ let React;
13
let ReactFeatureFlags;
14
let ReactDOM;
15
16
-function createReactEventComponent(targetEventTypes, handleEvent) {
16
+function createReactEventComponent(targetEventTypes, onEvent) {
17
const testEventResponder = {
18
targetEventTypes,
19
- handleEvent,
19
+ onEvent,
20
};
21
22
return {
@@ -53,19 +53,19 @@ describe('DOMEventResponderSystem', () => {
53
container = null;
54
});
55
56
- it('the event responder handleEvent() function should fire on click event', () => {
56
+ it('the event responder onEvent() function should fire on click event', () => {
57
let eventResponderFiredCount = 0;
58
let eventLog = [];
59
const buttonRef = React.createRef();
60
61
const ClickEventComponent = createReactEventComponent(
62
['click'],
63
- (context, props) => {
63
+ (event, context, props) => {
64
eventResponderFiredCount++;
65
eventLog.push({
66
- name: context.eventType,
67
- passive: context.isPassive(),
68
- passiveSupported: context.isPassiveSupported(),
66
+ name: event.type,
67
+ passive: event.passive,
68
+ passiveSupported: event.passiveSupported,
69
});
70
},
71
);
@@ -79,7 +79,7 @@ describe('DOMEventResponderSystem', () => {
79
ReactDOM.render(<Test />, container);
80
expect(container.innerHTML).toBe('<button>Click me!</button>');
81
82
- // Clicking the button should trigger the event responder handleEvent()
82
+ // Clicking the button should trigger the event responder onEvent()
83
let buttonElement = buttonRef.current;
84
dispatchClickEvent(buttonElement);
85
expect(eventResponderFiredCount).toBe(1);
@@ -103,7 +103,7 @@ describe('DOMEventResponderSystem', () => {
103
expect(eventResponderFiredCount).toBe(2);
104
});
105
106
- it('the event responder handleEvent() function should fire on click event (passive events forced)', () => {
106
+ it('the event responder onEvent() function should fire on click event (passive events forced)', () => {
107
// JSDOM does not support passive events, so this manually overrides the value to be true
108
const checkPassiveEvents = require('react-dom/src/events/checkPassiveEvents');
109
checkPassiveEvents.passiveBrowserEventsSupported = true;
@@ -113,11 +113,11 @@ describe('DOMEventResponderSystem', () => {
113
114
const ClickEventComponent = createReactEventComponent(
115
['click'],
116
- (context, props) => {
116
+ (event, context, props) => {
117
eventLog.push({
118
- name: context.eventType,
119
- passive: context.isPassive(),
120
- passiveSupported: context.isPassiveSupported(),
118
+ name: event.type,
119
+ passive: event.passive,
120
+ passiveSupported: event.passiveSupported,
121
});
122
},
123
);
@@ -130,7 +130,7 @@ describe('DOMEventResponderSystem', () => {
130
131
ReactDOM.render(<Test />, container);
132
133
- // Clicking the button should trigger the event responder handleEvent()
133
+ // Clicking the button should trigger the event responder onEvent()
134
let buttonElement = buttonRef.current;
135
dispatchClickEvent(buttonElement);
136
expect(eventLog.length).toBe(1);
@@ -141,19 +141,19 @@ describe('DOMEventResponderSystem', () => {
141
});
142
});
143
144
- it('nested event responders and their handleEvent() function should fire multiple times', () => {
144
+ it('nested event responders and their onEvent() function should fire multiple times', () => {
145
let eventResponderFiredCount = 0;
146
let eventLog = [];
147
const buttonRef = React.createRef();
148
149
const ClickEventComponent = createReactEventComponent(
150
['click'],
151
- (context, props) => {
151
+ (event, context, props) => {
152
eventResponderFiredCount++;
153
eventLog.push({
154
- name: context.eventType,
155
- passive: context.isPassive(),
156
- passiveSupported: context.isPassiveSupported(),
154
+ name: event.type,
155
+ passive: event.passive,
156
+ passiveSupported: event.passiveSupported,
157
});
158
},
159
);
@@ -168,7 +168,7 @@ describe('DOMEventResponderSystem', () => {
168
169
ReactDOM.render(<Test />, container);
170
171
- // Clicking the button should trigger the event responder handleEvent()
171
+ // Clicking the button should trigger the event responder onEvent()
172
let buttonElement = buttonRef.current;
173
dispatchClickEvent(buttonElement);
174
expect(eventResponderFiredCount).toBe(2);
@@ -186,7 +186,7 @@ describe('DOMEventResponderSystem', () => {
186
});
187
});
188
189
- it('nested event responders and their handleEvent() should fire in the correct order', () => {
189
+ it('nested event responders and their onEvent() should fire in the correct order', () => {
190
let eventLog = [];
191
const buttonRef = React.createRef();
192
@@ -214,7 +214,7 @@ describe('DOMEventResponderSystem', () => {
214
215
ReactDOM.render(<Test />, container);
216
217
- // Clicking the button should trigger the event responder handleEvent()
217
+ // Clicking the button should trigger the event responder onEvent()
218
let buttonElement = buttonRef.current;
219
dispatchClickEvent(buttonElement);
220
@@ -227,14 +227,14 @@ describe('DOMEventResponderSystem', () => {
227
228
const ClickEventComponent = createReactEventComponent(
229
['click'],
230
- (context, props) => {
230
+ (event, context, props) => {
231
if (props.onMagicClick) {
232
- const event = {
232
+ const syntheticEvent = {
233
listener: props.onMagicClick,
234
- target: context.eventTarget,
234
+ target: event.target,
235
type: 'magicclick',
236
};
237
- context.dispatchEvent(event, {discrete: true});
237
+ context.dispatchEvent(syntheticEvent, {discrete: true});
238
}
239
},
240
);
@@ -251,7 +251,7 @@ describe('DOMEventResponderSystem', () => {
251
252
ReactDOM.render(<Test />, container);
253
254
- // Clicking the button should trigger the event responder handleEvent()
254
+ // Clicking the button should trigger the event responder onEvent()
255
let buttonElement = buttonRef.current;
256
dispatchClickEvent(buttonElement);
257
@@ -264,37 +264,33 @@ describe('DOMEventResponderSystem', () => {
264
265
const LongPressEventComponent = createReactEventComponent(
266
['click'],
267
- (context, props) => {
267
+ (event, context, props) => {
268
const pressEvent = {
269
listener: props.onPress,
270
- target: context.eventTarget,
270
+ target: event.target,
271
type: 'press',
272
};
273
context.dispatchEvent(pressEvent, {discrete: true});
274
275
- setTimeout(
276
- () =>
277
- context.withAsyncDispatching(() => {
278
- if (props.onLongPress) {
279
- const longPressEvent = {
280
- listener: props.onLongPress,
281
- target: context.eventTarget,
282
- type: 'longpress',
283
- };
284
- context.dispatchEvent(longPressEvent, {discrete: true});
285
- }
286
-
287
- if (props.onLongPressChange) {
288
- const longPressChangeEvent = {
289
- listener: props.onLongPressChange,
290
- target: context.eventTarget,
291
- type: 'longpresschange',
292
- };
293
- context.dispatchEvent(longPressChangeEvent, {discrete: true});
294
- }
295
- }),
296
- 500,
297
- );
275
+ context.setTimeout(() => {
276
+ if (props.onLongPress) {
277
+ const longPressEvent = {
278
+ listener: props.onLongPress,
279
+ target: event.target,
280
+ type: 'longpress',
281
+ };
282
+ context.dispatchEvent(longPressEvent, {discrete: true});
283
+ }
284
+
285
+ if (props.onLongPressChange) {
286
+ const longPressChangeEvent = {
287
+ listener: props.onLongPressChange,
288
+ target: event.target,
289
+ type: 'longpresschange',
290
+ };
291
+ context.dispatchEvent(longPressChangeEvent, {discrete: true});
292
+ }
293
+ }, 500);
294
},
295
);
296
@@ -313,7 +309,7 @@ describe('DOMEventResponderSystem', () => {
309
310
ReactDOM.render(<Test />, container);
311
316
- // Clicking the button should trigger the event responder handleEvent()
312
+ // Clicking the button should trigger the event responder onEvent()
313
let buttonElement = buttonRef.current;
314
dispatchClickEvent(buttonElement);
315
jest.runAllTimers();
packages/react-events/src/Drag.js
+20
-15
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {EventResponderContext} from 'events/EventTypes';
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12
13
const targetEventTypes = ['pointerdown', 'pointercancel'];
@@ -62,7 +62,7 @@ function createDragEvent(
62
}
63
64
function dispatchDragEvent(
65
- context: EventResponderContext,
65
+ context: ResponderContext,
66
name: DragEventType,
67
listener: DragEvent => void,
68
state: DragState,
@@ -87,28 +87,31 @@ const DragResponder = {
87
y: 0,
88
};
89
},
90
- handleEvent(
91
- context: EventResponderContext,
90
+ onEvent(
91
+ event: ResponderEvent,
92
+ context: ResponderContext,
93
props: Object,
94
state: DragState,
95
): void {
95
- const {eventTarget, eventType, event} = context;
96
+ const {target, type, nativeEvent} = event;
97
97
- switch (eventType) {
98
+ switch (type) {
99
case 'touchstart':
100
case 'mousedown':
101
case 'pointerdown': {
102
if (!state.isDragging) {
103
if (props.onShouldClaimOwnership) {
103
- context.releaseOwnership(state.dragTarget);
104
+ context.releaseOwnership();
105
}
106
const obj =
106
- eventType === 'touchstart' ? (event: any).changedTouches[0] : event;
107
+ type === 'touchstart'
108
+ ? (nativeEvent: any).changedTouches[0]
109
+ : nativeEvent;
110
const x = (state.startX = (obj: any).screenX);
111
const y = (state.startY = (obj: any).screenY);
112
state.x = x;
113
state.y = y;
111
- state.dragTarget = eventTarget;
114
+ state.dragTarget = target;
115
state.isPointerDown = true;
116
117
if (props.onDragStart) {
@@ -121,19 +124,21 @@ const DragResponder = {
124
);
125
}
126
124
- context.addRootEventTypes(rootEventTypes);
127
+ context.addRootEventTypes(target.ownerDocument, rootEventTypes);
128
}
129
break;
130
}
131
case 'touchmove':
132
case 'mousemove':
133
case 'pointermove': {
131
- if (context.isPassive()) {
134
+ if (event.passive) {
135
return;
136
}
137
if (state.isPointerDown) {
138
const obj =
136
- eventType === 'touchmove' ? (event: any).changedTouches[0] : event;
139
+ type === 'touchmove'
140
+ ? (nativeEvent: any).changedTouches[0]
141
+ : nativeEvent;
142
const x = (obj: any).screenX;
143
const y = (obj: any).screenY;
144
state.x = x;
@@ -145,7 +150,7 @@ const DragResponder = {
150
props.onShouldClaimOwnership &&
151
props.onShouldClaimOwnership()
152
) {
148
- shouldEnableDragging = context.requestOwnership(state.dragTarget);
153
+ shouldEnableDragging = context.requestOwnership();
154
}
155
if (shouldEnableDragging) {
156
state.isDragging = true;
@@ -181,7 +186,7 @@ const DragResponder = {
186
eventData,
187
);
188
}
184
- (event: any).preventDefault();
189
+ (nativeEvent: any).preventDefault();
190
}
191
}
192
break;
@@ -193,7 +198,7 @@ const DragResponder = {
198
case 'pointerup': {
199
if (state.isDragging) {
200
if (props.onShouldClaimOwnership) {
196
- context.releaseOwnership(state.dragTarget);
201
+ context.releaseOwnership();
202
}
203
if (props.onDragEnd) {
204
dispatchDragEvent(context, 'dragend', props.onDragEnd, state, true);
packages/react-events/src/Focus.js
+21
-30
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {EventResponderContext} from 'events/EventTypes';
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12
13
type FocusProps = {
@@ -47,55 +47,45 @@ function createFocusEvent(
47
}
48
49
function dispatchFocusInEvents(
50
- context: EventResponderContext,
50
+ event: ResponderEvent,
51
+ context: ResponderContext,
52
props: FocusProps,
53
) {
53
- const {event, eventTarget} = context;
54
- if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
54
+ const {nativeEvent, target} = event;
55
+ if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
56
return;
57
}
58
if (props.onFocus) {
58
- const syntheticEvent = createFocusEvent(
59
- 'focus',
60
- eventTarget,
61
- props.onFocus,
62
- );
59
+ const syntheticEvent = createFocusEvent('focus', target, props.onFocus);
60
context.dispatchEvent(syntheticEvent, {discrete: true});
61
}
62
if (props.onFocusChange) {
63
const listener = () => {
64
props.onFocusChange(true);
65
};
69
- const syntheticEvent = createFocusEvent(
70
- 'focuschange',
71
- eventTarget,
72
- listener,
73
- );
66
+ const syntheticEvent = createFocusEvent('focuschange', target, listener);
67
context.dispatchEvent(syntheticEvent, {discrete: true});
68
}
69
}
70
71
function dispatchFocusOutEvents(
79
- context: EventResponderContext,
72
+ event: ResponderEvent,
73
+ context: ResponderContext,
74
props: FocusProps,
75
) {
82
- const {event, eventTarget} = context;
83
- if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
76
+ const {nativeEvent, target} = event;
77
+ if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
78
return;
79
}
80
if (props.onBlur) {
87
- const syntheticEvent = createFocusEvent('blur', eventTarget, props.onBlur);
81
+ const syntheticEvent = createFocusEvent('blur', target, props.onBlur);
82
context.dispatchEvent(syntheticEvent, {discrete: true});
83
}
84
if (props.onFocusChange) {
85
const listener = () => {
86
props.onFocusChange(false);
87
};
94
- const syntheticEvent = createFocusEvent(
95
- 'focuschange',
96
- eventTarget,
97
- listener,
98
- );
88
+ const syntheticEvent = createFocusEvent('focuschange', target, listener);
89
context.dispatchEvent(syntheticEvent, {discrete: true});
90
}
91
}
@@ -107,24 +97,25 @@ const FocusResponder = {
97
isFocused: false,
98
};
99
},
110
- handleEvent(
111
- context: EventResponderContext,
100
+ onEvent(
101
+ event: ResponderEvent,
102
+ context: ResponderContext,
103
props: Object,
104
state: FocusState,
105
): void {
115
- const {eventTarget, eventType} = context;
106
+ const {type} = event;
107
117
- switch (eventType) {
108
+ switch (type) {
109
case 'focus': {
119
- if (!state.isFocused && !context.isTargetOwned(eventTarget)) {
120
- dispatchFocusInEvents(context, props);
110
+ if (!state.isFocused && !context.hasOwnership()) {
111
+ dispatchFocusInEvents(event, context, props);
112
state.isFocused = true;
113
}
114
break;
115
}
116
case 'blur': {
117
if (state.isFocused) {
127
- dispatchFocusOutEvents(context, props);
118
+ dispatchFocusOutEvents(event, context, props);
119
state.isFocused = false;
120
}
121
break;
packages/react-events/src/Hover.js
+31
-40
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {EventResponderContext} from 'events/EventTypes';
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12
13
type HoverProps = {
@@ -61,17 +61,18 @@ function createHoverEvent(
61
}
62
63
function dispatchHoverStartEvents(
64
- context: EventResponderContext,
64
+ event: ResponderEvent,
65
+ context: ResponderContext,
66
props: HoverProps,
67
): void {
67
- const {event, eventTarget} = context;
68
- if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
68
+ const {nativeEvent, target} = event;
69
+ if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
70
return;
71
}
72
if (props.onHoverStart) {
73
const syntheticEvent = createHoverEvent(
74
'hoverstart',
74
- eventTarget,
75
+ target,
76
props.onHoverStart,
77
);
78
context.dispatchEvent(syntheticEvent, {discrete: true});
@@ -80,27 +81,24 @@ function dispatchHoverStartEvents(
81
const listener = () => {
82
props.onHoverChange(true);
83
};
83
- const syntheticEvent = createHoverEvent(
84
- 'hoverchange',
85
- eventTarget,
86
- listener,
87
- );
84
+ const syntheticEvent = createHoverEvent('hoverchange', target, listener);
85
context.dispatchEvent(syntheticEvent, {discrete: true});
86
}
87
}
88
89
function dispatchHoverEndEvents(
93
- context: EventResponderContext,
90
+ event: ResponderEvent,
91
+ context: ResponderContext,
92
props: HoverProps,
93
) {
96
- const {event, eventTarget} = context;
97
- if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
94
+ const {nativeEvent, target} = event;
95
+ if (context.isTargetWithinEventComponent((nativeEvent: any).relatedTarget)) {
96
return;
97
}
98
if (props.onHoverEnd) {
99
const syntheticEvent = createHoverEvent(
100
'hoverend',
103
- eventTarget,
101
+ target,
102
props.onHoverEnd,
103
);
104
context.dispatchEvent(syntheticEvent, {discrete: true});
@@ -109,11 +107,7 @@ function dispatchHoverEndEvents(
107
const listener = () => {
108
props.onHoverChange(false);
109
};
112
- const syntheticEvent = createHoverEvent(
113
- 'hoverchange',
114
- eventTarget,
115
- listener,
116
- );
110
+ const syntheticEvent = createHoverEvent('hoverchange', target, listener);
111
context.dispatchEvent(syntheticEvent, {discrete: true});
112
}
113
}
@@ -127,14 +121,15 @@ const HoverResponder = {
121
isTouched: false,
122
};
123
},
130
- handleEvent(
131
- context: EventResponderContext,
124
+ onEvent(
125
+ event: ResponderEvent,
126
+ context: ResponderContext,
127
props: HoverProps,
128
state: HoverState,
129
): void {
135
- const {eventType, eventTarget, event} = context;
130
+ const {type, nativeEvent} = event;
131
137
- switch (eventType) {
132
+ switch (type) {
133
/**
134
* Prevent hover events when touch is being used.
135
*/
@@ -147,25 +142,21 @@ const HoverResponder = {
142
143
case 'pointerover':
144
case 'mouseover': {
150
- if (
151
- !state.isHovered &&
152
- !state.isTouched &&
153
- !context.isTargetOwned(eventTarget)
154
- ) {
155
- if ((event: any).pointerType === 'touch') {
145
+ if (!state.isHovered && !state.isTouched && !context.hasOwnership()) {
146
+ if ((nativeEvent: any).pointerType === 'touch') {
147
state.isTouched = true;
148
return;
149
}
150
if (
151
context.isPositionWithinTouchHitTarget(
161
- (event: any).x,
162
- (event: any).y,
152
+ (nativeEvent: any).x,
153
+ (nativeEvent: any).y,
154
)
155
) {
156
state.isInHitSlop = true;
157
return;
158
}
168
- dispatchHoverStartEvents(context, props);
159
+ dispatchHoverStartEvents(event, context, props);
160
state.isHovered = true;
161
}
162
break;
@@ -173,7 +164,7 @@ const HoverResponder = {
164
case 'pointerout':
165
case 'mouseout': {
166
if (state.isHovered && !state.isTouched) {
176
- dispatchHoverEndEvents(context, props);
167
+ dispatchHoverEndEvents(event, context, props);
168
state.isHovered = false;
169
}
170
state.isInHitSlop = false;
@@ -185,22 +176,22 @@ const HoverResponder = {
176
if (state.isInHitSlop) {
177
if (
178
!context.isPositionWithinTouchHitTarget(
188
- (event: any).x,
189
- (event: any).y,
179
+ (nativeEvent: any).x,
180
+ (nativeEvent: any).y,
181
)
182
) {
192
- dispatchHoverStartEvents(context, props);
183
+ dispatchHoverStartEvents(event, context, props);
184
state.isHovered = true;
185
state.isInHitSlop = false;
186
}
187
} else if (
188
state.isHovered &&
189
context.isPositionWithinTouchHitTarget(
199
- (event: any).x,
200
- (event: any).y,
190
+ (nativeEvent: any).x,
191
+ (nativeEvent: any).y,
192
)
193
) {
203
- dispatchHoverEndEvents(context, props);
194
+ dispatchHoverEndEvents(event, context, props);
195
state.isHovered = false;
196
state.isInHitSlop = true;
197
}
@@ -209,7 +200,7 @@ const HoverResponder = {
200
}
201
case 'pointercancel': {
202
if (state.isHovered && !state.isTouched) {
212
- dispatchHoverEndEvents(context, props);
203
+ dispatchHoverEndEvents(event, context, props);
204
state.isHovered = false;
205
state.isTouched = false;
206
}
packages/react-events/src/Press.js
+84
-60
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {EventResponderContext} from 'events/EventTypes';
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12
13
type PressProps = {
@@ -86,7 +86,7 @@ function createPressEvent(
86
}
87
88
function dispatchEvent(
89
- context: EventResponderContext,
89
+ context: ResponderContext,
90
state: PressState,
91
name: PressEventType,
92
listener: (e: Object) => void,
@@ -97,7 +97,7 @@ function dispatchEvent(
97
}
98
99
function dispatchPressChangeEvent(
100
- context: EventResponderContext,
100
+ context: ResponderContext,
101
props: PressProps,
102
state: PressState,
103
): void {
@@ -108,7 +108,7 @@ function dispatchPressChangeEvent(
108
}
109
110
function dispatchLongPressChangeEvent(
111
- context: EventResponderContext,
111
+ context: ResponderContext,
112
props: PressProps,
113
state: PressState,
114
): void {
@@ -119,7 +119,7 @@ function dispatchLongPressChangeEvent(
119
}
120
121
function dispatchPressStartEvents(
122
- context: EventResponderContext,
122
+ context: ResponderContext,
123
props: PressProps,
124
state: PressState,
125
): void {
@@ -138,34 +138,30 @@ function dispatchPressStartEvents(
138
DEFAULT_LONG_PRESS_DELAY_MS,
139
);
140
141
- state.longPressTimeout = setTimeout(
142
- () =>
143
- context.withAsyncDispatching(() => {
144
- state.isLongPressed = true;
145
- state.longPressTimeout = null;
146
-
147
- if (props.onLongPress) {
148
- const listener = e => {
149
- props.onLongPress(e);
150
- // TODO address this again at some point
151
- // if (e.nativeEvent.defaultPrevented) {
152
- // state.defaultPrevented = true;
153
- // }
154
- };
155
- dispatchEvent(context, state, 'longpress', listener);
156
- }
141
+ state.longPressTimeout = context.setTimeout(() => {
142
+ state.isLongPressed = true;
143
+ state.longPressTimeout = null;
144
+
145
+ if (props.onLongPress) {
146
+ const listener = e => {
147
+ props.onLongPress(e);
148
+ // TODO address this again at some point
149
+ // if (e.nativeEvent.defaultPrevented) {
150
+ // state.defaultPrevented = true;
151
+ // }
152
+ };
153
+ dispatchEvent(context, state, 'longpress', listener);
154
+ }
155
158
- if (props.onLongPressChange) {
159
- dispatchLongPressChangeEvent(context, props, state);
160
- }
161
- }),
162
- delayLongPress,
163
- );
156
+ if (props.onLongPressChange) {
157
+ dispatchLongPressChangeEvent(context, props, state);
158
+ }
159
+ }, delayLongPress);
160
}
161
}
162
163
function dispatchPressEndEvents(
168
- context: EventResponderContext,
164
+ context: ResponderContext,
165
props: PressProps,
166
state: PressState,
167
): void {
@@ -206,6 +202,21 @@ function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
202
return Math.max(min, maybeNumber != null ? maybeNumber : fallback);
203
}
204
205
+function unmountResponder(
206
+ context: ResponderContext,
207
+ props: PressProps,
208
+ state: PressState,
209
+): void {
210
+ if (state.isPressed) {
211
+ state.isPressed = false;
212
+ dispatchPressEndEvents(context, props, state);
213
+ if (state.longPressTimeout !== null) {
214
+ clearTimeout(state.longPressTimeout);
215
+ state.longPressTimeout = null;
216
+ }
217
+ }
218
+}
219
+
220
const PressResponder = {
221
targetEventTypes,
222
createInitialState(): PressState {
@@ -219,14 +230,15 @@ const PressResponder = {
230
shouldSkipMouseAfterTouch: false,
231
};
232
},
222
- handleEvent(
223
- context: EventResponderContext,
233
+ onEvent(
234
+ event: ResponderEvent,
235
+ context: ResponderContext,
236
props: PressProps,
237
state: PressState,
238
): void {
227
- const {eventTarget, eventType, event} = context;
239
+ const {target, type, nativeEvent} = event;
240
229
- switch (eventType) {
241
+ switch (type) {
242
/**
243
* Respond to pointer events and fall back to mouse.
244
*/
@@ -234,29 +246,29 @@ const PressResponder = {
246
case 'mousedown': {
247
if (
248
!state.isPressed &&
237
- !context.isTargetOwned(eventTarget) &&
249
+ !context.hasOwnership() &&
250
!state.shouldSkipMouseAfterTouch
251
) {
252
if (
241
- (event: any).pointerType === 'mouse' ||
242
- eventType === 'mousedown'
253
+ (nativeEvent: any).pointerType === 'mouse' ||
254
+ type === 'mousedown'
255
) {
256
if (
257
// Ignore right- and middle-clicks
246
- event.button === 1 ||
247
- event.button === 2 ||
258
+ nativeEvent.button === 1 ||
259
+ nativeEvent.button === 2 ||
260
// Ignore pressing on hit slop area with mouse
261
context.isPositionWithinTouchHitTarget(
250
- (event: any).x,
251
- (event: any).y,
262
+ (nativeEvent: any).x,
263
+ (nativeEvent: any).y,
264
)
265
) {
266
return;
267
}
268
}
257
- state.pressTarget = eventTarget;
269
+ state.pressTarget = target;
270
dispatchPressStartEvents(context, props, state);
259
- context.addRootEventTypes(rootEventTypes);
271
+ context.addRootEventTypes(target.ownerDocument, rootEventTypes);
272
}
273
break;
274
}
@@ -273,7 +285,7 @@ const PressResponder = {
285
dispatchPressEndEvents(context, props, state);
286
287
if (state.pressTarget !== null && props.onPress) {
276
- if (context.isTargetWithinElement(eventTarget, state.pressTarget)) {
288
+ if (context.isTargetWithinElement(target, state.pressTarget)) {
289
if (
290
!(
291
wasLongPressed &&
@@ -303,16 +315,16 @@ const PressResponder = {
315
* support for pointer events.
316
*/
317
case 'touchstart': {
306
- if (!state.isPressed && !context.isTargetOwned(eventTarget)) {
318
+ if (!state.isPressed && !context.hasOwnership()) {
319
// We bail out of polyfilling anchor tags, given the same heuristics
320
// explained above in regards to needing to use click events.
309
- if (isAnchorTagElement(eventTarget)) {
321
+ if (isAnchorTagElement(target)) {
322
state.isAnchorTouched = true;
323
return;
324
}
313
- state.pressTarget = eventTarget;
325
+ state.pressTarget = target;
326
dispatchPressStartEvents(context, props, state);
315
- context.addRootEventTypes(rootEventTypes);
327
+ context.addRootEventTypes(target.ownerDocument, rootEventTypes);
328
}
329
break;
330
}
@@ -326,17 +338,17 @@ const PressResponder = {
338
339
dispatchPressEndEvents(context, props, state);
340
329
- if (eventType !== 'touchcancel' && props.onPress) {
341
+ if (type !== 'touchcancel' && props.onPress) {
342
// Find if the X/Y of the end touch is still that of the original target
331
- const changedTouch = (event: any).changedTouches[0];
332
- const doc = (eventTarget: any).ownerDocument;
333
- const target = doc.elementFromPoint(
343
+ const changedTouch = (nativeEvent: any).changedTouches[0];
344
+ const doc = (target: any).ownerDocument;
345
+ const fromTarget = doc.elementFromPoint(
346
changedTouch.screenX,
347
changedTouch.screenY,
348
);
349
if (
338
- target !== null &&
339
- context.isTargetWithinEventComponent(target)
350
+ fromTarget !== null &&
351
+ context.isTargetWithinEventComponent(fromTarget)
352
) {
353
if (
354
!(
@@ -363,21 +375,21 @@ const PressResponder = {
375
if (
376
!state.isPressed &&
377
!state.isLongPressed &&
366
- !context.isTargetOwned(eventTarget) &&
367
- isValidKeyPress((event: any).key)
378
+ !context.hasOwnership() &&
379
+ isValidKeyPress((nativeEvent: any).key)
380
) {
381
// Prevent spacebar press from scrolling the window
370
- if ((event: any).key === ' ') {
371
- (event: any).preventDefault();
382
+ if ((nativeEvent: any).key === ' ') {
383
+ (nativeEvent: any).preventDefault();
384
}
373
- state.pressTarget = eventTarget;
385
+ state.pressTarget = target;
386
dispatchPressStartEvents(context, props, state);
375
- context.addRootEventTypes(rootEventTypes);
387
+ context.addRootEventTypes(target.ownerDocument, rootEventTypes);
388
}
389
break;
390
}
391
case 'keyup': {
380
- if (state.isPressed && isValidKeyPress((event: any).key)) {
392
+ if (state.isPressed && isValidKeyPress((nativeEvent: any).key)) {
393
const wasLongPressed = state.isLongPressed;
394
dispatchPressEndEvents(context, props, state);
395
if (state.pressTarget !== null && props.onPress) {
@@ -410,12 +422,24 @@ const PressResponder = {
422
423
case 'click': {
424
if (state.defaultPrevented) {
413
- (event: any).preventDefault();
425
+ (nativeEvent: any).preventDefault();
426
state.defaultPrevented = false;
427
}
428
}
429
}
430
},
431
+ // TODO This method doesn't work as of yet
432
+ onUnmount(context: ResponderContext, props: PressProps, state: PressState) {
433
+ unmountResponder(context, props, state);
434
+ },
435
+ // TODO This method doesn't work as of yet
436
+ onOwnershipChange(
437
+ context: ResponderContext,
438
+ props: PressProps,
439
+ state: PressState,
440
+ ) {
441
+ unmountResponder(context, props, state);
442
+ },
443
};
444
445
export default {
packages/react-events/src/Swipe.js
+19
-18
@@ -7,7 +7,7 @@
7
* @flow
8
*/
9
10
-import type {EventResponderContext} from 'events/EventTypes';
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
12
13
const targetEventTypes = ['pointerdown', 'pointercancel'];
@@ -52,7 +52,7 @@ function createSwipeEvent(
52
}
53
54
function dispatchSwipeEvent(
55
- context: EventResponderContext,
55
+ context: ResponderContext,
56
name: SwipeEventType,
57
listener: SwipeEvent => void,
58
state: SwipeState,
@@ -91,21 +91,22 @@ const SwipeResponder = {
91
y: 0,
92
};
93
},
94
- handleEvent(
95
- context: EventResponderContext,
94
+ onEvent(
95
+ event: ResponderEvent,
96
+ context: ResponderContext,
97
props: Object,
98
state: SwipeState,
99
): void {
99
- const {eventTarget, eventType, event} = context;
100
+ const {target, type, nativeEvent} = event;
101
101
- switch (eventType) {
102
+ switch (type) {
103
case 'touchstart':
104
case 'mousedown':
105
case 'pointerdown': {
105
- if (!state.isSwiping && !context.isTargetOwned(eventTarget)) {
106
+ if (!state.isSwiping && !context.hasOwnership()) {
107
let obj = event;
107
- if (eventType === 'touchstart') {
108
- obj = (event: any).targetTouches[0];
108
+ if (type === 'touchstart') {
109
+ obj = (nativeEvent: any).targetTouches[0];
110
state.touchId = obj.identifier;
111
}
112
const x = (obj: any).screenX;
@@ -114,7 +115,7 @@ const SwipeResponder = {
115
let shouldEnableSwiping = true;
116
117
if (props.onShouldClaimOwnership && props.onShouldClaimOwnership()) {
117
- shouldEnableSwiping = context.requestOwnership(eventTarget);
118
+ shouldEnableSwiping = context.requestOwnership();
119
}
120
if (shouldEnableSwiping) {
121
state.isSwiping = true;
@@ -122,8 +123,8 @@ const SwipeResponder = {
123
state.startY = y;
124
state.x = x;
125
state.y = y;
125
- state.swipeTarget = eventTarget;
126
- context.addRootEventTypes(rootEventTypes);
126
+ state.swipeTarget = target;
127
+ context.addRootEventTypes(target.ownerDocument, rootEventTypes);
128
} else {
129
state.touchId = null;
130
}
@@ -133,13 +134,13 @@ const SwipeResponder = {
134
case 'touchmove':
135
case 'mousemove':
136
case 'pointermove': {
136
- if (context.isPassive()) {
137
+ if (event.passive) {
138
return;
139
}
140
if (state.isSwiping) {
141
let obj = null;
141
- if (eventType === 'touchmove') {
142
- const targetTouches = (event: any).targetTouches;
142
+ if (type === 'touchmove') {
143
+ const targetTouches = (nativeEvent: any).targetTouches;
144
for (let i = 0; i < targetTouches.length; i++) {
145
if (state.touchId === targetTouches[i].identifier) {
146
obj = targetTouches[i];
@@ -147,7 +148,7 @@ const SwipeResponder = {
148
}
149
}
150
} else {
150
- obj = event;
151
+ obj = nativeEvent;
152
}
153
if (obj === null) {
154
state.isSwiping = false;
@@ -178,7 +179,7 @@ const SwipeResponder = {
179
false,
180
eventData,
181
);
181
- (event: any).preventDefault();
182
+ (nativeEvent: any).preventDefault();
183
}
184
}
185
break;
@@ -193,7 +194,7 @@ const SwipeResponder = {
194
return;
195
}
196
if (props.onShouldClaimOwnership) {
196
- context.releaseOwnership(state.swipeTarget);
197
+ context.releaseOwnership();
198
}
199
const direction = state.direction;
200
const lastDirection = state.lastDirection;
packages/react-events/src/__tests__/TouchHitTarget-test.internal.js
+1
-1
@@ -22,7 +22,7 @@ let TouchHitTarget;
22
23
const noOpResponder = {
24
targetEventTypes: [],
25
- handleEvent() {},
25
+ onEvent() {},
26
};
27
28
function createReactEventComponent() {
packages/react-reconciler/src/ReactFiber.js
+1
@@ -624,6 +624,7 @@ export function createFiberFromEventComponent(
624
fiber.elementType = eventComponent;
625
fiber.type = eventComponent;
626
fiber.stateNode = {
627
+ context: null,
628
props: pendingProps,
629
state: null,
630
};
packages/shared/ReactTypes.js
+14
-1
@@ -7,6 +7,8 @@
7
* @flow
8
*/
9
10
+import type {ResponderEvent, ResponderContext} from 'events/EventTypes';
11
+
12
export type ReactNode =
13
| React$Element<any>
14
| ReactPortal
@@ -88,7 +90,18 @@ export type ReactEventResponderEventType =
90
export type ReactEventResponder = {
91
targetEventTypes: Array<ReactEventResponderEventType>,
92
createInitialState?: (props: Object) => Object,
91
- handleEvent: (context: Object, props: Object, state: Object) => void,
93
+ onEvent: (
94
+ event: ResponderEvent,
95
+ context: ResponderContext,
96
+ props: Object,
97
+ state: Object,
98
+ ) => void,
99
+ onUnmount: (context: ResponderContext, props: Object, state: Object) => void,
100
+ onOwnershipChange: (
101
+ context: ResponderContext,
102
+ props: Object,
103
+ state: Object,
104
+ ) => void,
105
};
106
107
export type ReactEventComponent = {|