@samitouri / QOS-React-2 / commits / 87b3e2d257

Add flow to SyntheticEvent (#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>

Sebastian Silbermann committed Aug 19, 2020 at 15:16 UTC 87b3e2d257e49b6d2c8e662830fc8f3c7d62f85f
4 files changed +61 -40
packages/react-dom/src/events/DOMPluginEventSystem.js
+7 -7
@@ -15,7 +15,10 @@ import {
15 SHOULD_NOT_PROCESS_POLYFILL_EVENT_PLUGINS,
16 } from './EventSystemFlags';
17 import type {AnyNativeEvent} from './PluginModuleType';
18 -import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
18 +import type {
19 + KnownReactSyntheticEvent,
20 + ReactSyntheticEvent,
21 +} from './ReactSyntheticEventType';
22 import type {ElementListenerMapEntry} from '../client/ReactDOMComponentTree';
23 import type {EventPriority} from 'shared/ReactTypes';
24 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
@@ -917,15 +920,12 @@ function getLowestCommonAncestor(instA: Fiber, instB: Fiber): Fiber | null {
920
921 function accumulateEnterLeaveListenersForEvent(
922 dispatchQueue: DispatchQueue,
920 - event: ReactSyntheticEvent,
923 + event: KnownReactSyntheticEvent,
924 target: Fiber,
925 common: Fiber | null,
926 inCapturePhase: boolean,
927 ): void {
928 const registrationName = event._reactName;
926 - if (registrationName === undefined) {
927 - return;
928 - }
929 const listeners: Array<DispatchListener> = [];
930
931 let instance = target;
@@ -969,8 +969,8 @@ function accumulateEnterLeaveListenersForEvent(
969 // phase event listeners.
970 export function accumulateEnterLeaveTwoPhaseListeners(
971 dispatchQueue: DispatchQueue,
972 - leaveEvent: ReactSyntheticEvent,
973 - enterEvent: null | ReactSyntheticEvent,
972 + leaveEvent: KnownReactSyntheticEvent,
973 + enterEvent: null | KnownReactSyntheticEvent,
974 from: Fiber | null,
975 to: Fiber | null,
976 ): void {
packages/react-dom/src/events/ReactSyntheticEventType.js
+15 -3
@@ -22,14 +22,26 @@ export type DispatchConfig = {|
22 eventPriority?: EventPriority,
23 |};
24
25 -export type ReactSyntheticEvent = {|
25 +type BaseSyntheticEvent = {
26 isPersistent: () => boolean,
27 isPropagationStopped: () => boolean,
28 _dispatchInstances?: null | Array<Fiber | null> | Fiber,
29 _dispatchListeners?: null | Array<Function> | Function,
30 - _reactName: string,
30 _targetInst: Fiber,
31 nativeEvent: Event,
32 + target?: mixed,
33 + relatedTarget?: mixed,
34 type: string,
35 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
@@ -3,17 +3,23 @@
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 + *
7 + * @flow
8 */
9
10 /* eslint valid-typeof: 0 */
11
12 import getEventCharCode from './getEventCharCode';
13
14 +type EventInterfaceType = {
15 + [propName: string]: 0 | ((event: {[propName: string]: mixed}) => mixed),
16 +};
17 +
18 /**
19 * @interface Event
20 * @see http://www.w3.org/TR/DOM-Level-3-Events/
21 */
16 -const EventInterface = {
22 +const EventInterface: EventInterfaceType = {
23 eventPhase: 0,
24 bubbles: 0,
25 cancelable: 0,
@@ -46,12 +52,12 @@ function functionThatReturnsFalse() {
52 * DOM interface; custom application-specific events can also subclass this.
53 */
54 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,
61 ) {
62 this._reactName = reactName;
63 this._targetInst = targetInst;
@@ -95,6 +101,7 @@ Object.assign(SyntheticEvent.prototype, {
101
102 if (event.preventDefault) {
103 event.preventDefault();
104 + // $FlowFixMe - flow is not aware of `unknown` in IE
105 } else if (typeof event.returnValue !== 'unknown') {
106 event.returnValue = false;
107 }
@@ -109,6 +116,7 @@ Object.assign(SyntheticEvent.prototype, {
116
117 if (event.stopPropagation) {
118 event.stopPropagation();
119 + // $FlowFixMe - flow is not aware of `unknown` in IE
120 } else if (typeof event.cancelBubble !== 'unknown') {
121 // The ChangeEventPlugin registers a "propertychange" event for
122 // IE. This event does not support bubbling or cancelling, and
@@ -138,7 +146,7 @@ Object.assign(SyntheticEvent.prototype, {
146 isPersistent: functionThatReturnsTrue,
147 });
148
141 -export const UIEventInterface = {
149 +export const UIEventInterface: EventInterfaceType = {
150 ...EventInterface,
151 view: 0,
152 detail: 0,
@@ -154,7 +162,7 @@ let isMovementYSet = false;
162 * @interface MouseEvent
163 * @see http://www.w3.org/TR/DOM-Level-3-Events/
164 */
157 -export const MouseEventInterface = {
165 +export const MouseEventInterface: EventInterfaceType = {
166 ...UIEventInterface,
167 screenX: 0,
168 screenY: 0,
@@ -213,7 +221,7 @@ export const MouseEventInterface = {
221 * @interface DragEvent
222 * @see http://www.w3.org/TR/DOM-Level-3-Events/
223 */
216 -export const DragEventInterface = {
224 +export const DragEventInterface: EventInterfaceType = {
225 ...MouseEventInterface,
226 dataTransfer: 0,
227 };
@@ -222,7 +230,7 @@ export const DragEventInterface = {
230 * @interface FocusEvent
231 * @see http://www.w3.org/TR/DOM-Level-3-Events/
232 */
225 -export const FocusEventInterface = {
233 +export const FocusEventInterface: EventInterfaceType = {
234 ...UIEventInterface,
235 relatedTarget: 0,
236 };
@@ -232,7 +240,7 @@ export const FocusEventInterface = {
240 * @see http://www.w3.org/TR/css3-animations/#AnimationEvent-interface
241 * @see https://developer.mozilla.org/en-US/docs/Web/API/AnimationEvent
242 */
235 -export const AnimationEventInterface = {
243 +export const AnimationEventInterface: EventInterfaceType = {
244 ...EventInterface,
245 animationName: 0,
246 elapsedTime: 0,
@@ -243,7 +251,7 @@ export const AnimationEventInterface = {
251 * @interface Event
252 * @see http://www.w3.org/TR/clipboard-apis/
253 */
246 -export const ClipboardEventInterface = {
254 +export const ClipboardEventInterface: EventInterfaceType = {
255 ...EventInterface,
256 clipboardData: function(event) {
257 return 'clipboardData' in event
@@ -256,7 +264,7 @@ export const ClipboardEventInterface = {
264 * @interface Event
265 * @see http://www.w3.org/TR/DOM-Level-3-Events/#events-compositionevents
266 */
259 -export const CompositionEventInterface = {
267 +export const CompositionEventInterface: EventInterfaceType = {
268 ...EventInterface,
269 data: 0,
270 };
@@ -267,7 +275,7 @@ export const CompositionEventInterface = {
275 * /#events-inputevents
276 */
277 // Happens to share the same list for now.
270 -export const InputEventInterface = CompositionEventInterface;
278 +export const InputEventInterface: EventInterfaceType = CompositionEventInterface;
279
280 /**
281 * Normalization of deprecated HTML5 `key` values
packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js
+16 -15
@@ -25,6 +25,7 @@ import {
25 isContainerMarkedAsRoot,
26 } from '../../client/ReactDOMComponentTree';
27 import {accumulateEnterLeaveTwoPhaseListeners} from '../DOMPluginEventSystem';
28 +import type {KnownReactSyntheticEvent} from '../ReactSyntheticEventType';
29
30 import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
31 import {getNearestMountedFiber} from 'react-reconciler/src/ReactFiberTreeReflection';
@@ -147,23 +148,23 @@ function extractEvents(
148 leave.target = fromNode;
149 leave.relatedTarget = toNode;
150
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;
152
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.
155 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;
168 }
169
170 accumulateEnterLeaveTwoPhaseListeners(dispatchQueue, leave, enter, from, to);