@samitouri / QOS-React-2 / commits / a3bf668812

Flare: Fix listener upgrade bug (#18270)

Dominic Gannaway committed Mar 10, 2020 at 17:59 UTC a3bf6688121e69cfb377c25f362000b33d7984cd
5 files changed +75 -32
packages/react-dom/src/client/ReactDOMComponent.js
+2 -2
@@ -59,7 +59,7 @@ import {
59 import {getListenerMapForElement} from '../events/DOMEventListenerMap';
60 import {
61 addResponderEventSystemEvent,
62 - removeActiveResponderEventSystemEvent,
62 + removeTrappedPassiveEventListener,
63 } from '../events/ReactDOMEventListener.js';
64 import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
65 import {
@@ -1360,7 +1360,7 @@ export function listenToEventResponderEventTypes(
1360 const passiveKey = targetEventType + '_passive';
1361 const passiveListener = listenerMap.get(passiveKey);
1362 if (passiveListener != null) {
1363 - removeActiveResponderEventSystemEvent(
1363 + removeTrappedPassiveEventListener(
1364 document,
1365 targetEventType,
1366 passiveListener,
packages/react-dom/src/events/DOMLegacyEventPluginSystem.js
+3 -3
@@ -39,7 +39,7 @@ import {
39 getRawEventName,
40 mediaEventTypes,
41 } from './DOMTopLevelEventTypes';
42 -import {trapEventForPluginEventSystem} from './ReactDOMEventListener';
42 +import {addTrappedEventListener} from './ReactDOMEventListener';
43
44 /**
45 * Summary of `DOMEventPluginSystem` event handling:
@@ -368,12 +368,12 @@ export function legacyTrapBubbledEvent(
368 topLevelType: DOMTopLevelEventType,
369 element: Document | Element,
370 ): void {
371 - trapEventForPluginEventSystem(element, topLevelType, false);
371 + addTrappedEventListener(element, topLevelType, false);
372 }
373
374 export function legacyTrapCapturedEvent(
375 topLevelType: DOMTopLevelEventType,
376 element: Document | Element,
377 ): void {
378 - trapEventForPluginEventSystem(element, topLevelType, true);
378 + addTrappedEventListener(element, topLevelType, true);
379 }
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+3 -7
@@ -21,7 +21,7 @@ import {plugins} from 'legacy-events/EventPluginRegistry';
21
22 import {HostRoot, HostPortal} from 'shared/ReactWorkTags';
23
24 -import {trapEventForPluginEventSystem} from './ReactDOMEventListener';
24 +import {addTrappedEventListener} from './ReactDOMEventListener';
25 import getEventTarget from './getEventTarget';
26 import {getListenerMapForElement} from './DOMEventListenerMap';
27 import {
@@ -149,11 +149,7 @@ export function listenToTopLevelEvent(
149 ): void {
150 if (!listenerMap.has(topLevelType)) {
151 const isCapturePhase = capturePhaseEvents.has(topLevelType);
152 - trapEventForPluginEventSystem(
153 - rootContainerElement,
154 - topLevelType,
155 - isCapturePhase,
156 - );
152 + addTrappedEventListener(rootContainerElement, topLevelType, isCapturePhase);
153 listenerMap.set(topLevelType, null);
154 }
155 }
@@ -196,7 +192,7 @@ function willDeferLaterForFBLegacyPrimer(nativeEvent: any): boolean {
192 if (node.tagName === 'A' && validFBLegacyPrimerRels.has(node.rel)) {
193 const legacyFBSupport = true;
194 const isCapture = nativeEvent.eventPhase === 1;
199 - trapEventForPluginEventSystem(
195 + addTrappedEventListener(
196 document,
197 ((type: any): DOMTopLevelEventType),
198 isCapture,
packages/react-dom/src/events/ReactDOMEventListener.js
+32 -20
@@ -105,6 +105,7 @@ export function addResponderEventSystemEvent(
105 } else {
106 eventFlags |= IS_ACTIVE;
107 }
108 + let fbListener;
109 // Check if interactive and wrap in discreteUpdates
110 const listener = dispatchEvent.bind(
111 null,
@@ -113,39 +114,27 @@ export function addResponderEventSystemEvent(
114 document,
115 );
116 if (passiveBrowserEventsSupported) {
116 - addEventCaptureListenerWithPassiveFlag(
117 + fbListener = addEventCaptureListenerWithPassiveFlag(
118 document,
119 topLevelType,
120 listener,
121 passive,
122 );
123 } else {
123 - addEventCaptureListener(document, topLevelType, listener);
124 - }
125 - return listener;
126 -}
127 -
128 -export function removeActiveResponderEventSystemEvent(
129 - document: Document,
130 - topLevelType: string,
131 - listener: any => void,
132 -) {
133 - if (passiveBrowserEventsSupported) {
134 - document.removeEventListener(topLevelType, listener, {
135 - capture: true,
136 - passive: false,
137 - });
138 - } else {
139 - document.removeEventListener(topLevelType, listener, true);
124 + fbListener = addEventCaptureListener(document, topLevelType, listener);
125 }
126 + // If we have an fbListener, then use that.
127 + // We'll only have one if we use the forked
128 + // EventListener-www module in FB builds.
129 + return fbListener || listener;
130 }
131
143 -export function trapEventForPluginEventSystem(
132 +export function addTrappedEventListener(
133 container: Document | Element,
134 topLevelType: DOMTopLevelEventType,
135 capture: boolean,
136 legacyFBSupport?: boolean,
148 -): void {
137 +): any => void {
138 let listener;
139 let listenerWrapper;
140 switch (getEventPriorityForPluginSystem(topLevelType)) {
@@ -203,6 +192,29 @@ export function trapEventForPluginEventSystem(
192 } else {
193 fbListener = addEventBubbleListener(container, rawEventName, listener);
194 }
195 + // If we have an fbListener, then use that.
196 + // We'll only have one if we use the forked
197 + // EventListener-www module in FB builds.
198 + return fbListener || listener;
199 +}
200 +
201 +export function removeTrappedPassiveEventListener(
202 + document: Document,
203 + topLevelType: string,
204 + listener: any => void,
205 +) {
206 + if (listener.remove != null) {
207 + listener.remove();
208 + } else {
209 + if (passiveBrowserEventsSupported) {
210 + document.removeEventListener(topLevelType, listener, {
211 + capture: true,
212 + passive: true,
213 + });
214 + } else {
215 + document.removeEventListener(topLevelType, listener, true);
216 + }
217 + }
218 }
219
220 function dispatchDiscreteEvent(
packages/react-dom/src/events/__tests__/DeprecatedDOMEventResponderSystem-test.internal.js
+35
@@ -948,4 +948,39 @@ describe('DOMEventResponderSystem', () => {
948 document.body.removeChild(domNode);
949 expect(onEvent).toBeCalled();
950 });
951 +
952 + it('event upgrading should work correctly', () => {
953 + let eventResponderFiredCount = 0;
954 + const buttonRef = React.createRef();
955 +
956 + const TestResponder = createEventResponder({
957 + targetEventTypes: ['click'],
958 + onEvent: (event, context, props, state) => {
959 + eventResponderFiredCount++;
960 + if (!state.addedRootEventTypes) {
961 + context.addRootEventTypes(['click_active']);
962 + }
963 + state.addedRootEventTypes = true;
964 + },
965 + });
966 +
967 + function Test() {
968 + const listener = React.DEPRECATED_useResponder(TestResponder, {});
969 +
970 + return (
971 + <button ref={buttonRef} DEPRECATED_flareListeners={listener}>
972 + Click me!
973 + </button>
974 + );
975 + }
976 +
977 + ReactDOM.render(<Test />, container);
978 + expect(container.innerHTML).toBe('<button>Click me!</button>');
979 +
980 + let buttonElement = buttonRef.current;
981 + dispatchClickEvent(buttonElement);
982 + expect(eventResponderFiredCount).toBe(1);
983 + dispatchClickEvent(buttonElement);
984 + expect(eventResponderFiredCount).toBe(2);
985 + });
986 });