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

[Flare] Refinements to useEvent hook (#15955)

Dominic Gannaway committed Jun 21, 2019 at 23:10 UTC dc298fdf91b6056b097bce0eb05cfe90b873b158
8 files changed +73 -56
packages/react-dom/src/events/DOMEventResponderSystem.js
+35 -29
@@ -84,6 +84,7 @@ type ResponderTimeout = {|
84 |};
85
86 type ResponderTimer = {|
87 + isHook: boolean,
88 instance: ReactDOMEventComponentInstance,
89 func: () => void,
90 id: number,
@@ -117,6 +118,7 @@ let currentInstance: null | ReactDOMEventComponentInstance = null;
118 let currentEventQueue: null | EventQueue = null;
119 let currentTimerIDCounter = 0;
120 let currentDocument: null | Document = null;
121 +let currentlyInHook = false;
122
123 const eventResponderContext: ReactDOMResponderContext = {
124 dispatchEvent(
@@ -366,6 +368,7 @@ const eventResponderContext: ReactDOMResponderContext = {
368 currentTimers.set(delay, timeout);
369 }
370 timeout.timers.set(timerId, {
371 + isHook: currentlyInHook,
372 instance: ((currentInstance: any): ReactDOMEventComponentInstance),
373 func,
374 id: timerId,
@@ -456,6 +459,9 @@ const eventResponderContext: ReactDOMResponderContext = {
459 validateResponderContext();
460 continueLocalPropagation = true;
461 },
462 + isRespondingToHook() {
463 + return currentlyInHook;
464 + },
465 };
466
467 function collectFocusableElements(
@@ -535,9 +541,10 @@ function processTimers(
541 currentEventQueue = createEventQueue();
542 try {
543 for (let i = 0; i < timersArr.length; i++) {
538 - const {instance, func, id, timeStamp} = timersArr[i];
544 + const {isHook, instance, func, id, timeStamp} = timersArr[i];
545 currentInstance = instance;
546 currentTimeStamp = timeStamp + delay;
547 + currentlyInHook = isHook;
548 try {
549 func();
550 } finally {
@@ -763,9 +770,9 @@ function shouldSkipEventComponent(
770 eventResponderInstance: ReactDOMEventComponentInstance,
771 responder: ReactDOMEventResponder,
772 propagatedEventResponders: null | Set<ReactDOMEventResponder>,
766 - localPropagation: boolean,
773 + isHook: boolean,
774 ): boolean {
768 - if (propagatedEventResponders !== null && localPropagation) {
775 + if (propagatedEventResponders !== null && !isHook) {
776 if (propagatedEventResponders.has(responder)) {
777 return true;
778 }
@@ -830,12 +837,7 @@ function traverseAndHandleEventResponderInstances(
837 // Capture target phase
838 for (i = length; i-- > 0; ) {
839 const targetEventResponderInstance = targetEventResponderInstances[i];
833 - const {
834 - localPropagation,
835 - props,
836 - responder,
837 - state,
838 - } = targetEventResponderInstance;
840 + const {isHook, props, responder, state} = targetEventResponderInstance;
841 const eventListener = responder.onEventCapture;
842 if (eventListener !== undefined) {
843 if (
@@ -843,14 +845,15 @@ function traverseAndHandleEventResponderInstances(
845 targetEventResponderInstance,
846 ((responder: any): ReactDOMEventResponder),
847 propagatedEventResponders,
846 - localPropagation,
848 + isHook,
849 )
850 ) {
851 continue;
852 }
853 currentInstance = targetEventResponderInstance;
854 + currentlyInHook = isHook;
855 eventListener(responderEvent, eventResponderContext, props, state);
853 - if (localPropagation) {
856 + if (!isHook) {
857 checkForLocalPropagationContinuation(
858 responder,
859 propagatedEventResponders,
@@ -863,12 +866,7 @@ function traverseAndHandleEventResponderInstances(
866 // Bubble target phase
867 for (i = 0; i < length; i++) {
868 const targetEventResponderInstance = targetEventResponderInstances[i];
866 - const {
867 - localPropagation,
868 - props,
869 - responder,
870 - state,
871 - } = targetEventResponderInstance;
869 + const {isHook, props, responder, state} = targetEventResponderInstance;
870 const eventListener = responder.onEvent;
871 if (eventListener !== undefined) {
872 if (
@@ -876,14 +874,15 @@ function traverseAndHandleEventResponderInstances(
874 targetEventResponderInstance,
875 ((responder: any): ReactDOMEventResponder),
876 propagatedEventResponders,
879 - localPropagation,
877 + isHook,
878 )
879 ) {
880 continue;
881 }
882 currentInstance = targetEventResponderInstance;
883 + currentlyInHook = isHook;
884 eventListener(responderEvent, eventResponderContext, props, state);
886 - if (localPropagation) {
885 + if (!isHook) {
886 checkForLocalPropagationContinuation(
887 responder,
888 propagatedEventResponders,
@@ -900,12 +899,7 @@ function traverseAndHandleEventResponderInstances(
899 if (length > 0) {
900 for (i = 0; i < length; i++) {
901 const rootEventResponderInstance = rootEventResponderInstances[i];
903 - const {
904 - localPropagation,
905 - props,
906 - responder,
907 - state,
908 - } = rootEventResponderInstance;
902 + const {isHook, props, responder, state} = rootEventResponderInstance;
903 const eventListener = responder.onRootEvent;
904 if (eventListener !== undefined) {
905 if (
@@ -913,12 +907,13 @@ function traverseAndHandleEventResponderInstances(
907 rootEventResponderInstance,
908 responder,
909 null,
916 - localPropagation,
910 + isHook,
911 )
912 ) {
913 continue;
914 }
915 currentInstance = rootEventResponderInstance;
916 + currentlyInHook = isHook;
917 eventListener(responderEvent, eventResponderContext, props, state);
918 }
919 }
@@ -928,19 +923,24 @@ function traverseAndHandleEventResponderInstances(
923 function triggerOwnershipListeners(): void {
924 const listeningInstances = Array.from(ownershipChangeListeners);
925 const previousInstance = currentInstance;
926 + const previouslyInHook = currentlyInHook;
927 + currentEventQueue = createEventQueue();
928 try {
929 for (let i = 0; i < listeningInstances.length; i++) {
930 const instance = listeningInstances[i];
934 - const {props, responder, state} = instance;
931 + const {isHook, props, responder, state} = instance;
932 currentInstance = instance;
933 + currentlyInHook = isHook;
934 const onOwnershipChange = ((responder: any): ReactDOMEventResponder)
935 .onOwnershipChange;
936 if (onOwnershipChange !== undefined) {
937 onOwnershipChange(eventResponderContext, props, state);
938 }
939 }
940 + processEventQueue();
941 } finally {
942 currentInstance = previousInstance;
943 + currentlyInHook = previouslyInHook;
944 }
945 }
946
@@ -953,11 +953,13 @@ export function mountEventResponder(
953 }
954 const onMount = responder.onMount;
955 if (onMount !== undefined) {
956 - let {props, state} = eventComponentInstance;
956 + let {isHook, props, state} = eventComponentInstance;
957 currentEventQueue = createEventQueue();
958 currentInstance = eventComponentInstance;
959 + currentlyInHook = isHook;
960 try {
961 onMount(eventResponderContext, props, state);
962 + processEventQueue();
963 } finally {
964 currentEventQueue = null;
965 currentInstance = null;
@@ -972,11 +974,13 @@ export function unmountEventResponder(
974 const responder = ((eventComponentInstance.responder: any): ReactDOMEventResponder);
975 const onUnmount = responder.onUnmount;
976 if (onUnmount !== undefined) {
975 - let {props, state} = eventComponentInstance;
977 + let {isHook, props, state} = eventComponentInstance;
978 currentEventQueue = createEventQueue();
979 currentInstance = eventComponentInstance;
980 + currentlyInHook = isHook;
981 try {
982 onUnmount(eventResponderContext, props, state);
983 + processEventQueue();
984 } finally {
985 currentEventQueue = null;
986 currentInstance = null;
@@ -1030,6 +1034,7 @@ export function dispatchEventForResponderEventSystem(
1034 const previousTimers = currentTimers;
1035 const previousTimeStamp = currentTimeStamp;
1036 const previousDocument = currentDocument;
1037 + const previouslyInHook = currentlyInHook;
1038 currentTimers = null;
1039 currentEventQueue = createEventQueue();
1040 currentDocument = (nativeEventTarget: any).ownerDocument;
@@ -1050,6 +1055,7 @@ export function dispatchEventForResponderEventSystem(
1055 currentEventQueue = previousEventQueue;
1056 currentTimeStamp = previousTimeStamp;
1057 currentDocument = previousDocument;
1058 + currentlyInHook = previouslyInHook;
1059 }
1060 }
1061 }
packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+14 -6
@@ -1067,11 +1067,12 @@ describe('DOMEventResponderSystem', () => {
1067 };
1068 context.dispatchEvent(fooEvent, props.onFoo, DiscreteEvent);
1069 }
1070 + eventLogs.push(context.isRespondingToHook() ? '[hook]' : '[component]');
1071 },
1072 });
1073
1074 const Test = () => {
1074 - React.unstable_useEvent(EventComponent.responder, {
1075 + React.unstable_useEvent(EventComponent, {
1076 onFoo: e => eventLogs.push('hook'),
1077 });
1078 return (
@@ -1083,13 +1084,13 @@ describe('DOMEventResponderSystem', () => {
1084
1085 ReactDOM.render(<Test />, container);
1086 buttonRef.current.dispatchEvent(createEvent('foo'));
1086 - expect(eventLogs).toEqual(['prop', 'hook']);
1087 + expect(eventLogs).toEqual(['[component]', '[hook]', 'prop', 'hook']);
1088
1089 // Clear events
1090 eventLogs.length = 0;
1091
1092 const Test2 = () => {
1092 - React.unstable_useEvent(EventComponent.responder, {
1093 + React.unstable_useEvent(EventComponent, {
1094 onFoo: e => eventLogs.push('hook'),
1095 });
1096 return <button ref={buttonRef} />;
@@ -1101,10 +1102,10 @@ describe('DOMEventResponderSystem', () => {
1102 expect(eventLogs).toEqual([]);
1103
1104 const Test3 = () => {
1104 - React.unstable_useEvent(EventComponent.responder, {
1105 + React.unstable_useEvent(EventComponent, {
1106 onFoo: e => eventLogs.push('hook 2a'),
1107 });
1107 - React.unstable_useEvent(EventComponent.responder, {
1108 + React.unstable_useEvent(EventComponent, {
1109 onFoo: e => eventLogs.push('hook 2b'),
1110 });
1111 return (
@@ -1118,6 +1119,13 @@ describe('DOMEventResponderSystem', () => {
1119
1120 ReactDOM.render(<Test3 />, container);
1121 buttonRef.current.dispatchEvent(createEvent('foo'));
1121 - expect(eventLogs).toEqual(['prop 2', 'hook 2a', 'hook 2b']);
1122 + expect(eventLogs).toEqual([
1123 + '[component]',
1124 + '[hook]',
1125 + '[hook]',
1126 + 'prop 2',
1127 + 'hook 2a',
1128 + 'hook 2b',
1129 + ]);
1130 });
1131 });
packages/react-reconciler/src/ReactFiberCompleteWork.js
+1 -1
@@ -1041,7 +1041,7 @@ function completeWork(
1041 responder,
1042 rootContainerInstance,
1043 responderState || {},
1044 - true,
1044 + false,
1045 );
1046 markUpdate(workInProgress);
1047 } else {
packages/react-reconciler/src/ReactFiberEvents.js
+6 -4
@@ -9,6 +9,7 @@
9
10 import type {Fiber, Dependencies} from './ReactFiber';
11 import type {
12 + ReactEventComponent,
13 ReactEventResponder,
14 ReactEventComponentInstance,
15 } from 'shared/ReactTypes';
@@ -32,9 +33,10 @@ export function prepareToReadEventComponents(workInProgress: Fiber): void {
33 }
34
35 export function updateEventComponentInstance<T, E, C>(
35 - responder: ReactEventResponder<T, E, C>,
36 + eventComponent: ReactEventComponent<T, E, C>,
37 props: Object,
38 ): void {
39 + const responder = eventComponent.responder;
40 invariant(
41 responder.allowEventHooks,
42 'The "%s" event responder cannot be used via the "useEvent" hook.',
@@ -67,7 +69,7 @@ export function updateEventComponentInstance<T, E, C>(
69 responder,
70 null,
71 responderState || {},
70 - false,
72 + true,
73 );
74 events.push(eventComponentInstance);
75 currentEventComponentInstanceIndex++;
@@ -85,11 +87,11 @@ export function createEventComponentInstance<T, E, C>(
87 responder: ReactEventResponder<T, E, C>,
88 rootInstance: mixed,
89 state: Object,
88 - localPropagation: boolean,
90 + isHook: boolean,
91 ): ReactEventComponentInstance<T, E, C> {
92 return {
93 currentFiber,
92 - localPropagation,
94 + isHook,
95 props,
96 responder,
97 rootEventTypes: null,
packages/react-reconciler/src/ReactFiberHooks.js
+12 -12
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import type {ReactEventResponder, ReactContext} from 'shared/ReactTypes';
10 +import type {ReactEventComponent, ReactContext} from 'shared/ReactTypes';
11 import type {SideEffectTag} from 'shared/ReactSideEffectTags';
12 import type {Fiber} from './ReactFiber';
13 import type {ExpirationTime} from './ReactFiberExpirationTime';
@@ -83,7 +83,7 @@ export type Dispatcher = {
83 ): void,
84 useDebugValue<T>(value: T, formatterFn: ?(value: T) => mixed): void,
85 useEvent<T, E, C>(
86 - responder: ReactEventResponder<T, E, C>,
86 + eventComponent: ReactEventComponent<T, E, C>,
87 props: Object,
88 ): void,
89 };
@@ -1399,10 +1399,10 @@ if (__DEV__) {
1399 mountHookTypesDev();
1400 return mountDebugValue(value, formatterFn);
1401 },
1402 - useEvent<T, E, C>(responder: ReactEventResponder<T, E, C>, props) {
1402 + useEvent<T, E, C>(eventComponent: ReactEventComponent<T, E, C>, props) {
1403 currentHookNameInDev = 'useEvent';
1404 mountHookTypesDev();
1405 - updateEventComponentInstance(responder, props);
1405 + updateEventComponentInstance(eventComponent, props);
1406 },
1407 };
1408
@@ -1501,10 +1501,10 @@ if (__DEV__) {
1501 updateHookTypesDev();
1502 return mountDebugValue(value, formatterFn);
1503 },
1504 - useEvent<T, E, C>(responder: ReactEventResponder<T, E, C>, props) {
1504 + useEvent<T, E, C>(eventComponent: ReactEventComponent<T, E, C>, props) {
1505 currentHookNameInDev = 'useEvent';
1506 updateHookTypesDev();
1507 - updateEventComponentInstance(responder, props);
1507 + updateEventComponentInstance(eventComponent, props);
1508 },
1509 };
1510
@@ -1603,10 +1603,10 @@ if (__DEV__) {
1603 updateHookTypesDev();
1604 return updateDebugValue(value, formatterFn);
1605 },
1606 - useEvent<T, E, C>(responder: ReactEventResponder<T, E, C>, props) {
1606 + useEvent<T, E, C>(eventComponent: ReactEventComponent<T, E, C>, props) {
1607 currentHookNameInDev = 'useEvent';
1608 updateHookTypesDev();
1609 - updateEventComponentInstance(responder, props);
1609 + updateEventComponentInstance(eventComponent, props);
1610 },
1611 };
1612
@@ -1716,11 +1716,11 @@ if (__DEV__) {
1716 mountHookTypesDev();
1717 return mountDebugValue(value, formatterFn);
1718 },
1719 - useEvent<T, E, C>(responder: ReactEventResponder<T, E, C>, props) {
1719 + useEvent<T, E, C>(eventComponent: ReactEventComponent<T, E, C>, props) {
1720 currentHookNameInDev = 'useEvent';
1721 warnInvalidHookAccess();
1722 mountHookTypesDev();
1723 - updateEventComponentInstance(responder, props);
1723 + updateEventComponentInstance(eventComponent, props);
1724 },
1725 };
1726
@@ -1830,11 +1830,11 @@ if (__DEV__) {
1830 updateHookTypesDev();
1831 return updateDebugValue(value, formatterFn);
1832 },
1833 - useEvent<T, E, C>(responder: ReactEventResponder<T, E, C>, props) {
1833 + useEvent<T, E, C>(eventComponent: ReactEventComponent<T, E, C>, props) {
1834 currentHookNameInDev = 'useEvent';
1835 warnInvalidHookAccess();
1836 updateHookTypesDev();
1837 - updateEventComponentInstance(responder, props);
1837 + updateEventComponentInstance(eventComponent, props);
1838 },
1839 };
1840 }
packages/react/src/ReactHooks.js
+3 -3
@@ -7,7 +7,7 @@
7 * @flow
8 */
9
10 -import type {ReactEventResponder, ReactContext} from 'shared/ReactTypes';
10 +import type {ReactEventComponent, ReactContext} from 'shared/ReactTypes';
11 import invariant from 'shared/invariant';
12 import warning from 'shared/warning';
13
@@ -139,9 +139,9 @@ export function useDebugValue(value: any, formatterFn: ?(value: any) => any) {
139 export const emptyObject = {};
140
141 export function useEvent<T, E, C>(
142 - responder: ReactEventResponder<T, E, C>,
142 + eventComponent: ReactEventComponent<T, E, C>,
143 props: null | Object,
144 ) {
145 const dispatcher = resolveDispatcher();
146 - return dispatcher.useEvent(responder, props || emptyObject);
146 + return dispatcher.useEvent(eventComponent, props || emptyObject);
147 }
packages/shared/ReactDOMTypes.js
+1
@@ -84,4 +84,5 @@ export type ReactDOMResponderContext = {
84 deep: boolean,
85 ) => boolean,
86 continueLocalPropagation(): void,
87 + isRespondingToHook(): boolean,
88 };
packages/shared/ReactTypes.js
+1 -1
@@ -83,7 +83,7 @@ export type RefObject = {|
83
84 export type ReactEventComponentInstance<T, E, C> = {|
85 currentFiber: mixed,
86 - localPropagation: boolean,
86 + isHook: boolean,
87 props: Object,
88 responder: ReactEventResponder<T, E, C>,
89 rootEventTypes: null | Set<string>,