Skip to content

Commit 87b3e2d

Browse files
eps1longaearon
andauthored
Add flow to SyntheticEvent (facebook#19564)
* Add flow to SyntheticEvent * Minimal implementation of known and unknown synthetic events * less casting * Update EnterLeaveEventPlugin.js Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
1 parent b8fa09e commit 87b3e2d

File tree

4 files changed

+61
-40
lines changed

4 files changed

+61
-40
lines changed

packages/react-dom/src/events/DOMPluginEventSystem.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import {
1515
SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS,
1616
} from './EventSystemFlags';
1717
import type {AnyNativeEvent} from './PluginModuleType';
18-
import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
18+
import type {
19+
KnownReactSyntheticEvent,
20+
ReactSyntheticEvent,
21+
} from './ReactSyntheticEventType';
1922
import type {ElementListenerMapEntry} from '../client/ReactDOMComponentTree';
2023
import type {EventPriority} from 'shared/ReactTypes';
2124
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
@@ -917,15 +920,12 @@ function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
917920

918921
function accumulateEnterLeaveListenersForEvent(
919922
dispatchQueue: DispatchQueue,
920-
event: ReactSyntheticEvent,
923+
event: KnownReactSyntheticEvent,
921924
target: Fiber,
922925
common: Fiber | null,
923926
inCapturePhase: boolean,
924927
): void {
925928
const registrationName = event._reactName;
926-
if (registrationName === undefined) {
927-
return;
928-
}
929929
const listeners: Array<DispatchListener> = [];
930930

931931
let instance = target;
@@ -969,8 +969,8 @@ function accumulateEnterLeaveListenersForEvent(
969969
// phase event listeners.
970970
export function accumulateEnterLeaveTwoPhaseListeners(
971971
dispatchQueue: DispatchQueue,
972-
leaveEvent: ReactSyntheticEvent,
973-
enterEvent: null | ReactSyntheticEvent,
972+
leaveEvent: KnownReactSyntheticEvent,
973+
enterEvent: null | KnownReactSyntheticEvent,
974974
from: Fiber | null,
975975
to: Fiber | null,
976976
): void {

packages/react-dom/src/events/ReactSyntheticEventType.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,26 @@ export type DispatchConfig = {|
2222
eventPriority?: EventPriority,
2323
|};
2424

25-
export type ReactSyntheticEvent = {|
25+
type BaseSyntheticEvent = {
2626
isPersistent: () => boolean,
2727
isPropagationStopped: () => boolean,
2828
_dispatchInstances?: null | Array<Fiber | null> | Fiber,
2929
_dispatchListeners?: null | Array<Function> | Function,
30-
_reactName: string,
3130
_targetInst: Fiber,
3231
nativeEvent: Event,
32+
target?: mixed,
33+
relatedTarget?: mixed,
3334
type: string,
3435
currentTarget: null | EventTarget,
35-
|};
36+
};
37+
38+
export type KnownReactSyntheticEvent = BaseSyntheticEvent & {
39+
_reactName: string,
40+
};
41+
export type UnknownReactSyntheticEvent = BaseSyntheticEvent & {
42+
_reactName: null,
43+
};
44+
45+
export type ReactSyntheticEvent =
46+
| KnownReactSyntheticEvent
47+
| UnknownReactSyntheticEvent;

packages/react-dom/src/events/SyntheticEvent.js

+23-15
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@
33
*
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
68
*/
79

810
/* eslint valid-typeof: 0 */
911

1012
import getEventCharCode from './getEventCharCode';
1113

14+
type EventInterfaceType = {
15+
[propName: string]: 0 | ((event: {[propName: string]: mixed}) => mixed),
16+
};
17+
1218
/**
1319
* @interface Event
1420
* @see http://www.w3.org/TR/DOM-Level-3-Events/
1521
*/
16-
const EventInterface = {
22+
const EventInterface: EventInterfaceType = {
1723
eventPhase: 0,
1824
bubbles: 0,
1925
cancelable: 0,
@@ -46,12 +52,12 @@ function functionThatReturnsFalse() {
4652
* DOM interface; custom application-specific events can also subclass this.
4753
*/
4854
export function SyntheticEvent(
49-
reactName,
50-
reactEventType,
51-
targetInst,
52-
nativeEvent,
53-
nativeEventTarget,
54-
Interface = EventInterface,
55+
reactName: string | null,
56+
reactEventType: string,
57+
targetInst: Fiber,
58+
nativeEvent: {[propName: string]: mixed},
59+
nativeEventTarget: null | EventTarget,
60+
Interface: EventInterfaceType = EventInterface,
5561
) {
5662
this._reactName = reactName;
5763
this._targetInst = targetInst;
@@ -95,6 +101,7 @@ Object.assign(SyntheticEvent.prototype, {
95101

96102
if (event.preventDefault) {
97103
event.preventDefault();
104+
// $FlowFixMe - flow is not aware of `unknown` in IE
98105
} else if (typeof event.returnValue !== 'unknown') {
99106
event.returnValue = false;
100107
}
@@ -109,6 +116,7 @@ Object.assign(SyntheticEvent.prototype, {
109116

110117
if (event.stopPropagation) {
111118
event.stopPropagation();
119+
// $FlowFixMe - flow is not aware of `unknown` in IE
112120
} else if (typeof event.cancelBubble !== 'unknown') {
113121
// The ChangeEventPlugin registers a "propertychange" event for
114122
// IE. This event does not support bubbling or cancelling, and
@@ -138,7 +146,7 @@ Object.assign(SyntheticEvent.prototype, {
138146
isPersistent: functionThatReturnsTrue,
139147
});
140148

141-
export const UIEventInterface = {
149+
export const UIEventInterface: EventInterfaceType = {
142150
...EventInterface,
143151
view: 0,
144152
detail: 0,
@@ -154,7 +162,7 @@ let isMovementYSet = false;
154162
* @interface MouseEvent
155163
* @see http://www.w3.org/TR/DOM-Level-3-Events/
156164
*/
157-
export const MouseEventInterface = {
165+
export const MouseEventInterface: EventInterfaceType = {
158166
...UIEventInterface,
159167
screenX: 0,
160168
screenY: 0,
@@ -213,7 +221,7 @@ export const MouseEventInterface = {
213221
* @interface DragEvent
214222
* @see http://www.w3.org/TR/DOM-Level-3-Events/
215223
*/
216-
export const DragEventInterface = {
224+
export const DragEventInterface: EventInterfaceType = {
217225
...MouseEventInterface,
218226
dataTransfer: 0,
219227
};
@@ -222,7 +230,7 @@ export const DragEventInterface = {
222230
* @interface FocusEvent
223231
* @see http://www.w3.org/TR/DOM-Level-3-Events/
224232
*/
225-
export const FocusEventInterface = {
233+
export const FocusEventInterface: EventInterfaceType = {
226234
...UIEventInterface,
227235
relatedTarget: 0,
228236
};
@@ -232,7 +240,7 @@ export const FocusEventInterface = {
232240
* @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
233241
* @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
234242
*/
235-
export const AnimationEventInterface = {
243+
export const AnimationEventInterface: EventInterfaceType = {
236244
...EventInterface,
237245
animationName: 0,
238246
elapsedTime: 0,
@@ -243,7 +251,7 @@ export const AnimationEventInterface = {
243251
* @interface Event
244252
* @see http://www.w3.org/TR/clipboard-apis/
245253
*/
246-
export const ClipboardEventInterface = {
254+
export const ClipboardEventInterface: EventInterfaceType = {
247255
...EventInterface,
248256
clipboardData: function(event) {
249257
return 'clipboardData' in event
@@ -256,7 +264,7 @@ export const ClipboardEventInterface = {
256264
* @interface Event
257265
* @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
258266
*/
259-
export const CompositionEventInterface = {
267+
export const CompositionEventInterface: EventInterfaceType = {
260268
...EventInterface,
261269
data: 0,
262270
};
@@ -267,7 +275,7 @@ export const CompositionEventInterface = {
267275
* /#events-inputevents
268276
*/
269277
// Happens to share the same list for now.
270-
export const InputEventInterface = CompositionEventInterface;
278+
export const InputEventInterface: EventInterfaceType = CompositionEventInterface;
271279

272280
/**
273281
* Normalization of deprecated HTML5 `key` values

packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js

+16-15
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
isContainerMarkedAsRoot,
2626
} from '../../client/ReactDOMComponentTree';
2727
import {accumulateEnterLeaveTwoPhaseListeners} from '../DOMPluginEventSystem';
28+
import type {KnownReactSyntheticEvent} from '../ReactSyntheticEventType';
2829

2930
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
3031
import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
@@ -147,23 +148,23 @@ function extractEvents(
147148
leave.target = fromNode;
148149
leave.relatedTarget = toNode;
149150

150-
let enter = new SyntheticEvent(
151-
enterEventType,
152-
eventTypePrefix + 'enter',
153-
to,
154-
nativeEvent,
155-
nativeEventTarget,
156-
eventInterface,
157-
);
158-
enter.target = toNode;
159-
enter.relatedTarget = fromNode;
151+
let enter: KnownReactSyntheticEvent | null = null;
160152

161-
// If we are not processing the first ancestor, then we
162-
// should not process the same nativeEvent again, as we
163-
// will have already processed it in the first ancestor.
153+
// We should only process this nativeEvent if we are processing
154+
// the first ancestor. Next time, we will ignore the event.
164155
const nativeTargetInst = getClosestInstanceFromNode((nativeEventTarget: any));
165-
if (nativeTargetInst !== targetInst) {
166-
enter = null;
156+
if (nativeTargetInst === targetInst) {
157+
const enterEvent: KnownReactSyntheticEvent = new SyntheticEvent(
158+
enterEventType,
159+
eventTypePrefix + 'enter',
160+
to,
161+
nativeEvent,
162+
nativeEventTarget,
163+
eventInterface,
164+
);
165+
enterEvent.target = toNode;
166+
enterEvent.relatedTarget = fromNode;
167+
enter = enterEvent;
167168
}
168169

169170
accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);

0 commit comments

Comments
 (0)