@samitouri / QOS-React-2 / commits / 353e0ee474

[Flare] remove stopLocalPropagation option + modify responder ownership (#15889)

Dominic Gannaway committed Jun 14, 2019 at 23:11 UTC 353e0ee47492c42160aab81e60a59b08e40bb0b3
11 files changed +46 -106
packages/react-dom/src/events/DOMEventResponderSystem.js
+35 -49
@@ -107,11 +107,8 @@ const eventListeners:
107 ($Shape<PartialEventObject>) => void,
108 > = new PossiblyWeakMap();
109
110 -const responderOwners: Map<
111 - ReactEventResponder,
112 - ReactEventComponentInstance,
113 -> = new Map();
110 let globalOwner = null;
111 +let continueLocalPropagation = false;
112
113 let currentTimeStamp = 0;
114 let currentTimers = new Map();
@@ -308,12 +305,7 @@ const eventResponderContext: ReactResponderContext = {
305 },
306 hasOwnership(): boolean {
307 validateResponderContext();
311 - const responder = ((currentInstance: any): ReactEventComponentInstance)
312 - .responder;
313 - return (
314 - globalOwner === currentInstance ||
315 - responderOwners.get(responder) === currentInstance
316 - );
308 + return globalOwner === currentInstance;
309 },
310 requestGlobalOwnership(): boolean {
311 validateResponderContext();
@@ -321,18 +313,7 @@ const eventResponderContext: ReactResponderContext = {
313 return false;
314 }
315 globalOwner = currentInstance;
324 - triggerOwnershipListeners(null);
325 - return true;
326 - },
327 - requestResponderOwnership(): boolean {
328 - validateResponderContext();
329 - const eventComponentInstance = ((currentInstance: any): ReactEventComponentInstance);
330 - const responder = eventComponentInstance.responder;
331 - if (responderOwners.has(responder)) {
332 - return false;
333 - }
334 - responderOwners.set(responder, eventComponentInstance);
335 - triggerOwnershipListeners(responder);
316 + triggerOwnershipListeners();
317 return true;
318 },
319 releaseOwnership(): boolean {
@@ -433,6 +414,10 @@ const eventResponderContext: ReactResponderContext = {
414 }
415 return false;
416 },
417 + continueLocalPropagation() {
418 + validateResponderContext();
419 + continueLocalPropagation = true;
420 + },
421 };
422
423 function collectFocusableElements(
@@ -485,22 +470,12 @@ function getActiveDocument(): Document {
470 function releaseOwnershipForEventComponentInstance(
471 eventComponentInstance: ReactEventComponentInstance,
472 ): boolean {
488 - const responder = eventComponentInstance.responder;
489 - let triggerOwnershipListenersWith;
490 - if (responderOwners.get(responder) === eventComponentInstance) {
491 - responderOwners.delete(responder);
492 - triggerOwnershipListenersWith = responder;
493 - }
473 if (globalOwner === eventComponentInstance) {
474 globalOwner = null;
496 - triggerOwnershipListenersWith = null;
497 - }
498 - if (triggerOwnershipListenersWith !== undefined) {
499 - triggerOwnershipListeners(triggerOwnershipListenersWith);
475 + triggerOwnershipListeners();
476 return true;
501 - } else {
502 - return false;
477 }
478 + return false;
479 }
480
481 function isFiberHostComponentFocusable(fiber: Fiber): boolean {
@@ -725,10 +700,10 @@ function getRootEventResponderInstances(
700
701 function shouldSkipEventComponent(
702 eventResponderInstance: ReactEventComponentInstance,
703 + responder: ReactEventResponder,
704 propagatedEventResponders: null | Set<ReactEventResponder>,
705 ): boolean {
730 - const responder = eventResponderInstance.responder;
731 - if (propagatedEventResponders !== null && responder.stopLocalPropagation) {
706 + if (propagatedEventResponders !== null) {
707 if (propagatedEventResponders.has(responder)) {
708 return true;
709 }
@@ -737,15 +712,19 @@ function shouldSkipEventComponent(
712 if (globalOwner && globalOwner !== eventResponderInstance) {
713 return true;
714 }
740 - if (
741 - responderOwners.has(responder) &&
742 - responderOwners.get(responder) !== eventResponderInstance
743 - ) {
744 - return true;
745 - }
715 return false;
716 }
717
718 +function checkForLocalPropagationContinuation(
719 + responder: ReactEventResponder,
720 + propagatedEventResponders: Set<ReactEventResponder>,
721 +) {
722 + if (continueLocalPropagation === true) {
723 + propagatedEventResponders.delete(responder);
724 + continueLocalPropagation = false;
725 + }
726 +}
727 +
728 function traverseAndHandleEventResponderInstances(
729 topLevelType: DOMTopLevelEventType,
730 targetFiber: null | Fiber,
@@ -795,6 +774,7 @@ function traverseAndHandleEventResponderInstances(
774 if (
775 shouldSkipEventComponent(
776 targetEventResponderInstance,
777 + responder,
778 propagatedEventResponders,
779 )
780 ) {
@@ -802,6 +782,10 @@ function traverseAndHandleEventResponderInstances(
782 }
783 currentInstance = targetEventResponderInstance;
784 eventListener(responderEvent, eventResponderContext, props, state);
785 + checkForLocalPropagationContinuation(
786 + responder,
787 + propagatedEventResponders,
788 + );
789 }
790 }
791 // We clean propagated event responders between phases.
@@ -815,6 +799,7 @@ function traverseAndHandleEventResponderInstances(
799 if (
800 shouldSkipEventComponent(
801 targetEventResponderInstance,
802 + responder,
803 propagatedEventResponders,
804 )
805 ) {
@@ -822,6 +807,10 @@ function traverseAndHandleEventResponderInstances(
807 }
808 currentInstance = targetEventResponderInstance;
809 eventListener(responderEvent, eventResponderContext, props, state);
810 + checkForLocalPropagationContinuation(
811 + responder,
812 + propagatedEventResponders,
813 + );
814 }
815 }
816 }
@@ -836,7 +825,9 @@ function traverseAndHandleEventResponderInstances(
825 const {responder, props, state} = rootEventResponderInstance;
826 const eventListener = responder.onRootEvent;
827 if (eventListener !== undefined) {
839 - if (shouldSkipEventComponent(rootEventResponderInstance, null)) {
828 + if (
829 + shouldSkipEventComponent(rootEventResponderInstance, responder, null)
830 + ) {
831 continue;
832 }
833 currentInstance = rootEventResponderInstance;
@@ -846,18 +837,13 @@ function traverseAndHandleEventResponderInstances(
837 }
838 }
839
849 -function triggerOwnershipListeners(
850 - limitByResponder: null | ReactEventResponder,
851 -): void {
840 +function triggerOwnershipListeners(): void {
841 const listeningInstances = Array.from(ownershipChangeListeners);
842 const previousInstance = currentInstance;
843 try {
844 for (let i = 0; i < listeningInstances.length; i++) {
845 const instance = listeningInstances[i];
846 const {props, responder, state} = instance;
858 - if (limitByResponder !== null && limitByResponder !== responder) {
859 - continue;
860 - }
847 currentInstance = instance;
848 const onOwnershipChange = responder.onOwnershipChange;
849 if (onOwnershipChange !== undefined) {
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+7 -6
@@ -27,7 +27,6 @@ function createReactEventComponent({
27 onMount,
28 onUnmount,
29 onOwnershipChange,
30 - stopLocalPropagation,
30 allowMultipleHostChildren,
31 }) {
32 const testEventResponder = {
@@ -40,7 +39,6 @@ function createReactEventComponent({
39 onMount,
40 onUnmount,
41 onOwnershipChange,
43 - stopLocalPropagation: stopLocalPropagation || false,
42 allowMultipleHostChildren: allowMultipleHostChildren || false,
43 };
44
@@ -215,6 +213,7 @@ describe('DOMEventResponderSystem', () => {
213 const ClickEventComponent = createReactEventComponent({
214 targetEventTypes: ['click'],
215 onEvent: (event, context, props) => {
216 + context.continueLocalPropagation();
217 eventResponderFiredCount++;
218 eventLog.push({
219 name: event.type,
@@ -224,6 +223,7 @@ describe('DOMEventResponderSystem', () => {
223 });
224 },
225 onEventCapture: (event, context, props) => {
226 + context.continueLocalPropagation();
227 eventResponderFiredCount++;
228 eventLog.push({
229 name: event.type,
@@ -324,19 +324,20 @@ describe('DOMEventResponderSystem', () => {
324 ]);
325 });
326
327 - it('nested event responders should fire in the correct order without stopLocalPropagation', () => {
327 + it('nested event responders should fire in the correct order with continueLocalPropagation', () => {
328 let eventLog = [];
329 const buttonRef = React.createRef();
330
331 const ClickEventComponent = createReactEventComponent({
332 targetEventTypes: ['click'],
333 onEvent: (event, context, props) => {
334 + context.continueLocalPropagation();
335 eventLog.push(`${props.name} [bubble]`);
336 },
337 onEventCapture: (event, context, props) => {
338 + context.continueLocalPropagation();
339 eventLog.push(`${props.name} [capture]`);
340 },
339 - stopLocalPropagation: false,
341 });
342
343 const Test = () => (
@@ -361,7 +362,7 @@ describe('DOMEventResponderSystem', () => {
362 ]);
363 });
364
364 - it('nested event responders should fire in the correct order with stopLocalPropagation', () => {
365 + it('nested event responders should fire in the correct order', () => {
366 let eventLog = [];
367 const buttonRef = React.createRef();
368
@@ -373,7 +374,6 @@ describe('DOMEventResponderSystem', () => {
374 onEventCapture: (event, context, props) => {
375 eventLog.push(`${props.name} [capture]`);
376 },
376 - stopLocalPropagation: true,
377 });
378
379 const Test = () => (
@@ -686,6 +686,7 @@ describe('DOMEventResponderSystem', () => {
686 const EventComponent = createReactEventComponent({
687 targetEventTypes: ['pointerout'],
688 onEvent: (event, context) => {
689 + context.continueLocalPropagation();
690 const isWithin = context.isTargetWithinEventResponderScope(
691 event.nativeEvent.relatedTarget,
692 );
packages/react-events/README.md
-14
@@ -82,12 +82,6 @@ Called before an Event Component in unmounted.
82
83 Defines the DOM events to listen to on the root of the app.
84
85 -### stopLocalPropagation: boolean
86 -
87 -Defines whether or not synthetic events propagate to other Event Components *of
88 -the same type*. This has no effect on propagation of the source DOM events or
89 -the synthetic events dispatched to Event Components of different types.
90 -
85 ### targetEventTypes?: Array<ResponderEventType>
86
87 Defines the DOM events to listen to within the Event Component subtree.
@@ -159,14 +153,6 @@ has global ownership, only that instance and its responder are active. To releas
153 either `releaseOwnership()` must be called or the Event Component instance that had global ownership must be
154 unmounted. Calling `requestGlobalOwnership` also returns `true`/`false` if the request was successful.
155
162 -### requestResponderOwnership(): boolean
163 -
164 -The current Event Component instance can request responder ownership within the event system. When an Event Component
165 -instance has responder ownership, all other Event Component instances that have the same responder as the Event Component
166 -instance will no longer be active. To release ownership to other event responders, either `releaseOwnership()` must be
167 -called or the Event Component instance that had global ownership must be unmounted. Calling `requestResponderOwnership`
168 -also returns `true`/`false` if the request was successful.
169 -
156 ### setTimeout(func: () => void, delay: number): Symbol
157
158 This can be used to dispatch async events, e.g., those that fire after a delay.
packages/react-events/src/Drag.js
-1
@@ -98,7 +98,6 @@ const DragResponder = {
98 };
99 },
100 allowMultipleHostChildren: false,
101 - stopLocalPropagation: true,
101 onEvent(
102 event: ReactResponderEvent,
103 context: ReactResponderContext,
packages/react-events/src/Focus.js
-1
@@ -228,7 +228,6 @@ const FocusResponder = {
228 };
229 },
230 allowMultipleHostChildren: false,
231 - stopLocalPropagation: true,
231 onEvent(
232 event: ReactResponderEvent,
233 context: ReactResponderContext,
packages/react-events/src/FocusScope.js
+3 -29
@@ -6,12 +6,10 @@
6 *
7 * @flow
8 */
9 -
9 import type {
10 ReactResponderEvent,
11 ReactResponderContext,
12 } from 'shared/ReactTypes';
14 -
13 import React from 'react';
14
15 type FocusScopeProps = {
@@ -56,7 +54,6 @@ const FocusScopeResponder = {
54 };
55 },
56 allowMultipleHostChildren: true,
59 - stopLocalPropagation: false,
57 onEvent(
58 event: ReactResponderEvent,
59 context: ReactResponderContext,
@@ -64,12 +61,7 @@ const FocusScopeResponder = {
61 state: FocusScopeState,
62 ) {
63 const {type, nativeEvent} = event;
67 - const hasOwnership =
68 - context.hasOwnership() || context.requestResponderOwnership();
64
70 - if (!hasOwnership) {
71 - return;
72 - }
65 if (type === 'keydown' && nativeEvent.key === 'Tab') {
66 const focusedElement = context.getActiveDocument().activeElement;
67 if (
@@ -92,7 +84,7 @@ const FocusScopeResponder = {
84 nextElement = elements[lastPosition];
85 } else {
86 // Out of bounds
95 - context.releaseOwnership();
87 + context.continueLocalPropagation();
88 return;
89 }
90 } else {
@@ -104,19 +96,14 @@ const FocusScopeResponder = {
96 nextElement = elements[0];
97 } else {
98 // Out of bounds
107 - context.releaseOwnership();
99 + context.continueLocalPropagation();
100 return;
101 }
102 } else {
103 nextElement = elements[position + 1];
104 }
105 }
114 - // If this element is possibly inside the scope of another
115 - // FocusScope responder or is out of bounds, then we release ownership.
106 if (nextElement !== null) {
117 - if (!context.isTargetWithinEventResponderScope(nextElement)) {
118 - context.releaseOwnership();
119 - }
107 focusElement(nextElement);
108 state.currentFocusedNode = nextElement;
109 ((nativeEvent: any): KeyboardEvent).preventDefault();
@@ -163,23 +150,10 @@ const FocusScopeResponder = {
150 props: FocusScopeProps,
151 state: FocusScopeState,
152 ): void {
166 - if (
167 - props.restoreFocus &&
168 - state.nodeToRestore !== null &&
169 - context.hasOwnership()
170 - ) {
153 + if (props.restoreFocus && state.nodeToRestore !== null) {
154 focusElement(state.nodeToRestore);
155 }
156 },
174 - onOwnershipChange(
175 - context: ReactResponderContext,
176 - props: FocusScopeProps,
177 - state: FocusScopeState,
178 - ): void {
179 - if (!context.hasOwnership()) {
180 - state.currentFocusedNode = null;
181 - }
182 - },
157 };
158
159 export default React.unstable_createEventComponent(
packages/react-events/src/Hover.js
-1
@@ -287,7 +287,6 @@ const HoverResponder = {
287 };
288 },
289 allowMultipleHostChildren: false,
290 - stopLocalPropagation: true,
290 onEvent(
291 event: ReactResponderEvent,
292 context: ReactResponderContext,
packages/react-events/src/Press.js
-1
@@ -633,7 +633,6 @@ const PressResponder = {
633 };
634 },
635 allowMultipleHostChildren: false,
636 - stopLocalPropagation: true,
636 onEvent(
637 event: ReactResponderEvent,
638 context: ReactResponderContext,
packages/react-events/src/Scroll.js
-1
@@ -126,7 +126,6 @@ const ScrollResponder = {
126 };
127 },
128 allowMultipleHostChildren: true,
129 - stopLocalPropagation: true,
129 onEvent(
130 event: ReactResponderEvent,
131 context: ReactResponderContext,
packages/react-events/src/Swipe.js
-1
@@ -104,7 +104,6 @@ const SwipeResponder = {
104 };
105 },
106 allowMultipleHostChildren: false,
107 - stopLocalPropagation: true,
107 onEvent(
108 event: ReactResponderEvent,
109 context: ReactResponderContext,
packages/shared/ReactTypes.js
+1 -2
@@ -90,7 +90,6 @@ export type ReactEventResponder = {
90 rootEventTypes?: Array<ReactEventResponderEventType>,
91 createInitialState?: (props: null | Object) => Object,
92 allowMultipleHostChildren: boolean,
93 - stopLocalPropagation: boolean,
93 onEvent?: (
94 event: ReactResponderEvent,
95 context: ReactResponderContext,
@@ -194,7 +193,6 @@ export type ReactResponderContext = {
193 rootEventTypes: Array<ReactEventResponderEventType>,
194 ) => void,
195 hasOwnership: () => boolean,
197 - requestResponderOwnership: () => boolean,
196 requestGlobalOwnership: () => boolean,
197 releaseOwnership: () => boolean,
198 setTimeout: (func: () => void, timeout: number) => number,
@@ -209,4 +207,5 @@ export type ReactResponderContext = {
207 elementType: string,
208 deep: boolean,
209 ) => boolean,
210 + continueLocalPropagation(): void,
211 };