@samitouri / QOS-React-2 / commits / 4eb9b1d2b4

Refactor createEventHandle signature (#19174)

Dominic Gannaway committed Jul 7, 2020 at 13:05 UTC 4eb9b1d2b4439853016a28091737cfdaa581b5db
16 files changed +498 -390
packages/react-art/src/ReactARTHostConfig.js
-4
@@ -469,10 +469,6 @@ export function getInstanceFromNode(node) {
469 throw new Error('Not yet implemented.');
470 }
471
472 -export function removeInstanceEventHandles(instance) {
473 - // noop
474 -}
475 -
472 export function isOpaqueHydratingObject(value: mixed): boolean {
473 throw new Error('Not yet implemented');
474 }
packages/react-dom/src/client/ReactDOMEventHandle.js
+92 -104
@@ -47,6 +47,8 @@ type EventHandleOptions = {|
47 priority?: EventPriority,
48 |};
49
50 +const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
51 +
52 function getNearestRootOrPortalContainer(node: Fiber): null | Element {
53 while (node !== null) {
54 const tag = node.tag;
@@ -72,12 +74,10 @@ function createEventHandleListener(
74 type: DOMTopLevelEventType,
75 capture: boolean,
76 callback: (SyntheticEvent<EventTarget>) => void,
75 - destroy: (target: EventTarget | ReactScopeInstance) => void,
77 ): ReactDOMEventHandleListener {
78 return {
79 callback,
80 capture,
80 - destroy,
81 type,
82 };
83 }
@@ -111,6 +111,65 @@ function registerEventOnNearestTargetContainer(
111 );
112 }
113
114 +function registerReactDOMEvent(
115 + target: EventTarget | ReactScopeInstance,
116 + topLevelType: DOMTopLevelEventType,
117 + passive: boolean | void,
118 + capture: boolean,
119 + priority: EventPriority | void,
120 +): void {
121 + // Check if the target is a DOM element.
122 + if ((target: any).nodeType === ELEMENT_NODE) {
123 + const targetElement = ((target: any): Element);
124 + // Check if the DOM element is managed by React.
125 + const targetFiber = getClosestInstanceFromNode(targetElement);
126 + if (targetFiber === null) {
127 + invariant(
128 + false,
129 + 'ReactDOM.createEventHandle: setListener called on an element ' +
130 + 'target that is not managed by React. Ensure React rendered the DOM element.',
131 + );
132 + }
133 + registerEventOnNearestTargetContainer(
134 + targetFiber,
135 + topLevelType,
136 + passive,
137 + priority,
138 + );
139 + } else if (enableScopeAPI && isReactScope(target)) {
140 + const scopeTarget = ((target: any): ReactScopeInstance);
141 + const targetFiber = getFiberFromScopeInstance(scopeTarget);
142 + if (targetFiber === null) {
143 + // Scope is unmounted, do not proceed.
144 + return;
145 + }
146 + registerEventOnNearestTargetContainer(
147 + targetFiber,
148 + topLevelType,
149 + passive,
150 + priority,
151 + );
152 + } else if (isValidEventTarget(target)) {
153 + const eventTarget = ((target: any): EventTarget);
154 + const listenerMap = getEventListenerMap(eventTarget);
155 + listenToTopLevelEvent(
156 + topLevelType,
157 + eventTarget,
158 + listenerMap,
159 + PLUGIN_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
160 + capture,
161 + passive,
162 + priority,
163 + );
164 + } else {
165 + invariant(
166 + false,
167 + 'ReactDOM.createEventHandle: setter called on an invalid ' +
168 + 'target. Provide a valid EventTarget or an element managed by React.',
169 + );
170 + }
171 +}
172 +
173 export function createEventHandle(
174 type: string,
175 options?: EventHandleOptions,
@@ -140,110 +199,39 @@ export function createEventHandle(
199 priority = getEventPriorityForListenerSystem(topLevelType);
200 }
201
143 - const listeners = new Map();
144 -
145 - const destroy = (target: EventTarget | ReactScopeInstance): void => {
146 - const listener = listeners.get(target);
147 - if (listener !== undefined) {
148 - listeners.delete(target);
149 - const targetListeners = getEventHandlerListeners(target);
150 - if (targetListeners !== null) {
151 - targetListeners.delete(listener);
152 - }
202 + const registeredReactDOMEvents = new PossiblyWeakSet();
203 +
204 + return (
205 + target: EventTarget | ReactScopeInstance,
206 + callback: (SyntheticEvent<EventTarget>) => void,
207 + ) => {
208 + invariant(
209 + typeof callback === 'function',
210 + 'ReactDOM.createEventHandle: setter called with an invalid ' +
211 + 'callback. The callback must be a function.',
212 + );
213 + if (!registeredReactDOMEvents.has(target)) {
214 + registeredReactDOMEvents.add(target);
215 + registerReactDOMEvent(target, topLevelType, passive, capture, priority);
216 + // Add the event to our known event types list.
217 + addEventTypeToDispatchConfig(topLevelType);
218 }
154 - };
155 -
156 - const clear = (): void => {
157 - const eventTargetsArr = Array.from(listeners.keys());
158 - for (let i = 0; i < eventTargetsArr.length; i++) {
159 - destroy(eventTargetsArr[i]);
219 + const listener = createEventHandleListener(
220 + topLevelType,
221 + capture,
222 + callback,
223 + );
224 + let targetListeners = getEventHandlerListeners(target);
225 + if (targetListeners === null) {
226 + targetListeners = new Set();
227 + setEventHandlerListeners(target, targetListeners);
228 }
161 - };
162 -
163 - return {
164 - setListener(
165 - target: EventTarget | ReactScopeInstance,
166 - callback: null | ((SyntheticEvent<EventTarget>) => void),
167 - ): void {
168 - // Check if the target is a DOM element.
169 - if ((target: any).nodeType === ELEMENT_NODE) {
170 - const targetElement = ((target: any): Element);
171 - // Check if the DOM element is managed by React.
172 - const targetFiber = getClosestInstanceFromNode(targetElement);
173 - if (targetFiber === null) {
174 - invariant(
175 - false,
176 - 'ReactDOM.createEventHandle: setListener called on an element ' +
177 - 'target that is not managed by React. Ensure React rendered the DOM element.',
178 - );
179 - }
180 - registerEventOnNearestTargetContainer(
181 - targetFiber,
182 - topLevelType,
183 - passive,
184 - priority,
185 - );
186 - } else if (enableScopeAPI && isReactScope(target)) {
187 - const scopeTarget = ((target: any): ReactScopeInstance);
188 - const targetFiber = getFiberFromScopeInstance(scopeTarget);
189 - if (targetFiber === null) {
190 - // Scope is unmounted, do not proceed.
191 - return;
192 - }
193 - registerEventOnNearestTargetContainer(
194 - targetFiber,
195 - topLevelType,
196 - passive,
197 - priority,
198 - );
199 - } else if (isValidEventTarget(target)) {
200 - const eventTarget = ((target: any): EventTarget);
201 - const listenerMap = getEventListenerMap(eventTarget);
202 - listenToTopLevelEvent(
203 - topLevelType,
204 - eventTarget,
205 - listenerMap,
206 - PLUGIN_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
207 - capture,
208 - passive,
209 - priority,
210 - );
211 - } else {
212 - invariant(
213 - false,
214 - 'ReactDOM.createEventHandle: setListener called on an invalid ' +
215 - 'target. Provide a valid EventTarget or an element managed by React.',
216 - );
217 - }
218 - let listener = listeners.get(target);
219 - if (listener === undefined) {
220 - if (callback === null) {
221 - return;
222 - }
223 - listener = createEventHandleListener(
224 - topLevelType,
225 - capture,
226 - callback,
227 - destroy,
228 - );
229 - listeners.set(target, listener);
230 -
231 - let targetListeners = getEventHandlerListeners(target);
232 - if (targetListeners === null) {
233 - targetListeners = new Set();
234 - setEventHandlerListeners(target, targetListeners);
235 - }
236 - targetListeners.add(listener);
237 - // Finally, add the event to our known event types list.
238 - addEventTypeToDispatchConfig(topLevelType);
239 - } else if (callback !== null) {
240 - listener.callback = callback;
241 - } else {
242 - // Remove listener
243 - destroy(target);
244 - }
245 - },
246 - clear,
229 + targetListeners.add(listener);
230 + return () => {
231 + ((targetListeners: any): Set<ReactDOMEventHandleListener>).delete(
232 + listener,
233 + );
234 + };
235 };
236 }
237 return (null: any);
packages/react-dom/src/client/ReactDOMHostConfig.js
+1 -20
@@ -80,10 +80,7 @@ import {
80 } from 'shared/ReactFeatureFlags';
81 import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
82 import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
83 -import {
84 - listenToReactPropEvent,
85 - clearEventHandleListenersForTarget,
86 -} from '../events/DOMModernPluginEventSystem';
83 +import {listenToReactPropEvent} from '../events/DOMModernPluginEventSystem';
84
85 export type Type = string;
86 export type Props = {
@@ -534,14 +531,6 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
531 }
532 }
533
537 -export function removeInstanceEventHandles(
538 - instance: Instance | TextInstance | SuspenseInstance,
539 -) {
540 - if (enableCreateEventHandleAPI) {
541 - clearEventHandleListenersForTarget(instance);
542 - }
543 -}
544 -
534 export function removeChild(
535 parentInstance: Instance,
536 child: Instance | TextInstance | SuspenseInstance,
@@ -1134,14 +1123,6 @@ export function prepareScopeUpdate(
1123 }
1124 }
1125
1137 -export function removeScopeEventHandles(
1138 - scopeInstance: ReactScopeInstance,
1139 -): void {
1140 - if (enableScopeAPI && enableCreateEventHandleAPI) {
1141 - clearEventHandleListenersForTarget(scopeInstance);
1142 - }
1143 -}
1144 -
1126 export function getInstanceFromScope(
1127 scopeInstance: ReactScopeInstance,
1128 ): null | Object {
packages/react-dom/src/events/DOMModernPluginEventSystem.js
+1 -19
@@ -21,7 +21,7 @@ import type {
21 ElementListenerMap,
22 ElementListenerMapEntry,
23 } from '../client/ReactDOMComponentTree';
24 -import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
24 +import type {EventPriority} from 'shared/ReactTypes';
25 import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
26
27 import {registrationNameDependencies} from './EventRegistry';
@@ -954,24 +954,6 @@ export function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void {
954 }
955 }
956
957 -export function clearEventHandleListenersForTarget(
958 - target: EventTarget | ReactScopeInstance,
959 -): void {
960 - // It's unfortunate that we have to do this cleanup, but
961 - // it's necessary otherwise we will leak the host instances
962 - // on the createEventHandle API "listeners" Map. We call destroy
963 - // on each listener to ensure we properly remove the instance
964 - // from the listeners Map. Note: we have this Map so that we
965 - // can track listeners for the handle.clear() API call.
966 - const listeners = getEventHandlerListeners(target);
967 - if (listeners !== null) {
968 - const listenersArr = Array.from(listeners);
969 - for (let i = 0; i < listenersArr.length; i++) {
970 - listenersArr[i].destroy(target);
971 - }
972 - }
973 -}
974 -
957 export function getListenerMapKey(
958 topLevelType: DOMTopLevelEventType,
959 capture: boolean,
packages/react-dom/src/events/__tests__/DOMModernPluginEventSystem-test.internal.js
+361 -179
@@ -1208,13 +1208,13 @@ describe('DOMModernPluginEventSystem', () => {
1208 // @gate experimental
1209 it('can render correctly with the ReactDOMServer', () => {
1210 const clickEvent = jest.fn();
1211 - const click = ReactDOM.unstable_createEventHandle('click');
1211 + const setClick = ReactDOM.unstable_createEventHandle('click');
1212
1213 function Test() {
1214 const divRef = React.useRef(null);
1215
1216 React.useEffect(() => {
1217 - click.setListener(divRef.current, clickEvent);
1217 + return setClick(divRef.current, clickEvent);
1218 });
1219
1220 return <div ref={divRef}>Hello world</div>;
@@ -1227,11 +1227,11 @@ describe('DOMModernPluginEventSystem', () => {
1227 it('can render correctly with the ReactDOMServer hydration', () => {
1228 const clickEvent = jest.fn();
1229 const spanRef = React.createRef();
1230 - const click = ReactDOM.unstable_createEventHandle('click');
1230 + const setClick = ReactDOM.unstable_createEventHandle('click');
1231
1232 function Test() {
1233 React.useEffect(() => {
1234 - click.setListener(spanRef.current, clickEvent);
1234 + return setClick(spanRef.current, clickEvent);
1235 });
1236
1237 return (
@@ -1264,11 +1264,11 @@ describe('DOMModernPluginEventSystem', () => {
1264 });
1265 const divRef = React.createRef();
1266 const buttonRef = React.createRef();
1267 - const click = ReactDOM.unstable_createEventHandle('click');
1267 + const setClick = ReactDOM.unstable_createEventHandle('click');
1268
1269 function Test() {
1270 React.useEffect(() => {
1271 - click.setListener(buttonRef.current, clickEvent);
1271 + return setClick(buttonRef.current, clickEvent);
1272 });
1273
1274 return (
@@ -1327,11 +1327,11 @@ describe('DOMModernPluginEventSystem', () => {
1327 },
1328 ]);
1329
1330 - const click2 = ReactDOM.unstable_createEventHandle('click');
1330 + const setClick2 = ReactDOM.unstable_createEventHandle('click');
1331
1332 function Test2({clickEvent2}) {
1333 React.useEffect(() => {
1334 - click2.setListener(buttonRef.current, clickEvent2);
1334 + return setClick2(buttonRef.current, clickEvent2);
1335 });
1336
1337 return (
@@ -1364,18 +1364,16 @@ describe('DOMModernPluginEventSystem', () => {
1364 const clickEvent = jest.fn();
1365 const divRef = React.createRef();
1366 const buttonRef = React.createRef();
1367 - const click = ReactDOM.unstable_createEventHandle('click');
1367 + const setClick = ReactDOM.unstable_createEventHandle('click');
1368
1369 function Test({off}) {
1370 React.useEffect(() => {
1371 - click.setListener(buttonRef.current, clickEvent);
1372 - });
1373 -
1374 - React.useEffect(() => {
1371 + const clear = setClick(buttonRef.current, clickEvent);
1372 if (off) {
1376 - click.setListener(buttonRef.current, null);
1373 + clear();
1374 }
1378 - }, [off]);
1375 + return clear;
1376 + });
1377
1378 return (
1379 <button ref={buttonRef}>
@@ -1391,7 +1389,7 @@ describe('DOMModernPluginEventSystem', () => {
1389 dispatchClickEvent(divElement);
1390 expect(clickEvent).toBeCalledTimes(1);
1391
1394 - // The listener should get unmounted in the second effect
1392 + // The listener should get unmounted
1393 ReactDOM.render(<Test off={true} />, container);
1394 Scheduler.unstable_flushAll();
1395
@@ -1406,11 +1404,11 @@ describe('DOMModernPluginEventSystem', () => {
1404 it('should handle the target being a text node', () => {
1405 const clickEvent = jest.fn();
1406 const buttonRef = React.createRef();
1409 - const click = ReactDOM.unstable_createEventHandle('click');
1407 + const setClick = ReactDOM.unstable_createEventHandle('click');
1408
1409 function Test() {
1410 React.useEffect(() => {
1413 - click.setListener(buttonRef.current, clickEvent);
1411 + return setClick(buttonRef.current, clickEvent);
1412 });
1413
1414 return <button ref={buttonRef}>Click me!</button>;
@@ -1433,17 +1431,33 @@ describe('DOMModernPluginEventSystem', () => {
1431 const onClickCapture = jest.fn(e =>
1432 log.push(['capture', e.currentTarget]),
1433 );
1436 - const click = ReactDOM.unstable_createEventHandle('click');
1437 - const clickCapture = ReactDOM.unstable_createEventHandle('click', {
1438 - capture: true,
1439 - });
1434 + const setClick = ReactDOM.unstable_createEventHandle('click');
1435 + const setCaptureClick = ReactDOM.unstable_createEventHandle(
1436 + 'click',
1437 + {
1438 + capture: true,
1439 + },
1440 + );
1441
1442 function Test() {
1443 React.useEffect(() => {
1443 - click.setListener(buttonRef.current, onClick);
1444 - clickCapture.setListener(buttonRef.current, onClickCapture);
1445 - click.setListener(divRef.current, onClick);
1446 - clickCapture.setListener(divRef.current, onClickCapture);
1444 + const clearClick1 = setClick(buttonRef.current, onClick);
1445 + const clearCaptureClick1 = setCaptureClick(
1446 + buttonRef.current,
1447 + onClickCapture,
1448 + );
1449 + const clearClick2 = setClick(divRef.current, onClick);
1450 + const clearCaptureClick2 = setCaptureClick(
1451 + divRef.current,
1452 + onClickCapture,
1453 + );
1454 +
1455 + return () => {
1456 + clearClick1();
1457 + clearCaptureClick1();
1458 + clearClick2();
1459 + clearCaptureClick2();
1460 + };
1461 });
1462
1463 return (
@@ -1486,15 +1500,23 @@ describe('DOMModernPluginEventSystem', () => {
1500 const onClickCapture = jest.fn(e =>
1501 log.push(['capture', e.currentTarget]),
1502 );
1489 - const click = ReactDOM.unstable_createEventHandle('click');
1490 - const clickCapture = ReactDOM.unstable_createEventHandle('click', {
1491 - capture: true,
1492 - });
1503 + const setClick = ReactDOM.unstable_createEventHandle('click');
1504 + const setClickCapture = ReactDOM.unstable_createEventHandle(
1505 + 'click',
1506 + {
1507 + capture: true,
1508 + },
1509 + );
1510
1511 function Test() {
1512 React.useEffect(() => {
1496 - click.setListener(buttonRef.current, onClick);
1497 - clickCapture.setListener(buttonRef.current, onClickCapture);
1513 + setClick(buttonRef.current, onClick);
1514 + setClickCapture(buttonRef.current, onClickCapture);
1515 +
1516 + return () => {
1517 + setClick();
1518 + setClickCapture();
1519 + };
1520 });
1521
1522 return (
@@ -1542,11 +1564,11 @@ describe('DOMModernPluginEventSystem', () => {
1564 });
1565 const divRef = React.createRef();
1566 const buttonRef = React.createRef();
1545 - const click = ReactDOM.unstable_createEventHandle('click');
1567 + const setClick = ReactDOM.unstable_createEventHandle('click');
1568
1569 function Test() {
1570 React.useEffect(() => {
1549 - click.setListener(divRef.current, clickEvent);
1571 + return setClick(divRef.current, clickEvent);
1572 });
1573
1574 return (
@@ -1601,21 +1623,28 @@ describe('DOMModernPluginEventSystem', () => {
1623 const targetListener2 = jest.fn();
1624 const targetListener3 = jest.fn();
1625 const targetListener4 = jest.fn();
1604 - let click1 = ReactDOM.unstable_createEventHandle('click', {
1626 + let setClick1 = ReactDOM.unstable_createEventHandle('click', {
1627 capture: true,
1628 });
1607 - let click2 = ReactDOM.unstable_createEventHandle('click', {
1629 + let setClick2 = ReactDOM.unstable_createEventHandle('click', {
1630 capture: true,
1631 });
1610 - let click3 = ReactDOM.unstable_createEventHandle('click');
1611 - let click4 = ReactDOM.unstable_createEventHandle('click');
1632 + let setClick3 = ReactDOM.unstable_createEventHandle('click');
1633 + let setClick4 = ReactDOM.unstable_createEventHandle('click');
1634
1635 function Test() {
1636 React.useEffect(() => {
1615 - click1.setListener(buttonRef.current, targetListener1);
1616 - click2.setListener(buttonRef.current, targetListener2);
1617 - click3.setListener(buttonRef.current, targetListener3);
1618 - click4.setListener(buttonRef.current, targetListener4);
1637 + setClick1(buttonRef.current, targetListener1);
1638 + setClick2(buttonRef.current, targetListener2);
1639 + setClick3(buttonRef.current, targetListener3);
1640 + setClick4(buttonRef.current, targetListener4);
1641 +
1642 + return () => {
1643 + setClick1();
1644 + setClick2();
1645 + setClick3();
1646 + setClick4();
1647 + };
1648 });
1649
1650 return <button ref={buttonRef}>Click me!</button>;
@@ -1632,17 +1661,24 @@ describe('DOMModernPluginEventSystem', () => {
1661 expect(targetListener3).toHaveBeenCalledTimes(1);
1662 expect(targetListener4).toHaveBeenCalledTimes(1);
1663
1635 - click1 = ReactDOM.unstable_createEventHandle('click');
1636 - click2 = ReactDOM.unstable_createEventHandle('click');
1637 - click3 = ReactDOM.unstable_createEventHandle('click');
1638 - click4 = ReactDOM.unstable_createEventHandle('click');
1664 + setClick1 = ReactDOM.unstable_createEventHandle('click');
1665 + setClick2 = ReactDOM.unstable_createEventHandle('click');
1666 + setClick3 = ReactDOM.unstable_createEventHandle('click');
1667 + setClick4 = ReactDOM.unstable_createEventHandle('click');
1668
1669 function Test2() {
1670 React.useEffect(() => {
1642 - click1.setListener(buttonRef.current, targetListener1);
1643 - click2.setListener(buttonRef.current, targetListener2);
1644 - click3.setListener(buttonRef.current, targetListener3);
1645 - click4.setListener(buttonRef.current, targetListener4);
1671 + setClick1(buttonRef.current, targetListener1);
1672 + setClick2(buttonRef.current, targetListener2);
1673 + setClick3(buttonRef.current, targetListener3);
1674 + setClick4(buttonRef.current, targetListener4);
1675 +
1676 + return () => {
1677 + setClick1();
1678 + setClick2();
1679 + setClick3();
1680 + setClick4();
1681 + };
1682 });
1683
1684 return <button ref={buttonRef}>Click me!</button>;
@@ -1664,17 +1700,22 @@ describe('DOMModernPluginEventSystem', () => {
1700 const buttonRef = React.createRef();
1701 const divRef = React.createRef();
1702 const clickEvent = jest.fn();
1667 - const click1 = ReactDOM.unstable_createEventHandle('click', {
1703 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
1704 bind: buttonRef,
1705 });
1670 - const click2 = ReactDOM.unstable_createEventHandle('click');
1706 + const setClick2 = ReactDOM.unstable_createEventHandle('click');
1707
1708 function Test() {
1709 React.useEffect(() => {
1674 - click1.setListener(buttonRef.current, clickEvent);
1675 - click2.setListener(divRef.current, e => {
1710 + const clearClick1 = setClick1(buttonRef.current, clickEvent);
1711 + const clearClick2 = setClick2(divRef.current, e => {
1712 e.stopPropagation();
1713 });
1714 +
1715 + return () => {
1716 + clearClick1();
1717 + clearClick2();
1718 + };
1719 });
1720
1721 return (
@@ -1699,17 +1740,36 @@ describe('DOMModernPluginEventSystem', () => {
1740 const targetListener2 = jest.fn(e => e.stopPropagation());
1741 const targetListener3 = jest.fn(e => e.stopPropagation());
1742 const targetListener4 = jest.fn(e => e.stopPropagation());
1702 - const click1 = ReactDOM.unstable_createEventHandle('click');
1703 - const click2 = ReactDOM.unstable_createEventHandle('click');
1704 - const click3 = ReactDOM.unstable_createEventHandle('click');
1705 - const click4 = ReactDOM.unstable_createEventHandle('click');
1743 + const setClick1 = ReactDOM.unstable_createEventHandle('click');
1744 + const setClick2 = ReactDOM.unstable_createEventHandle('click');
1745 + const setClick3 = ReactDOM.unstable_createEventHandle('click');
1746 + const setClick4 = ReactDOM.unstable_createEventHandle('click');
1747
1748 function Test() {
1749 React.useEffect(() => {
1709 - click1.setListener(buttonRef.current, targetListener1);
1710 - click2.setListener(buttonRef.current, targetListener2);
1711 - click3.setListener(buttonRef.current, targetListener3);
1712 - click4.setListener(buttonRef.current, targetListener4);
1750 + const clearClick1 = setClick1(
1751 + buttonRef.current,
1752 + targetListener1,
1753 + );
1754 + const clearClick2 = setClick2(
1755 + buttonRef.current,
1756 + targetListener2,
1757 + );
1758 + const clearClick3 = setClick3(
1759 + buttonRef.current,
1760 + targetListener3,
1761 + );
1762 + const clearClick4 = setClick4(
1763 + buttonRef.current,
1764 + targetListener4,
1765 + );
1766 +
1767 + return () => {
1768 + clearClick1();
1769 + clearClick2();
1770 + clearClick3();
1771 + clearClick4();
1772 + };
1773 });
1774
1775 return <button ref={buttonRef}>Click me!</button>;
@@ -1733,21 +1793,40 @@ describe('DOMModernPluginEventSystem', () => {
1793 const targetListener2 = jest.fn(e => e.stopPropagation());
1794 const targetListener3 = jest.fn(e => e.stopPropagation());
1795 const targetListener4 = jest.fn(e => e.stopPropagation());
1736 - const click1 = ReactDOM.unstable_createEventHandle('click', {
1796 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
1797 capture: true,
1798 });
1739 - const click2 = ReactDOM.unstable_createEventHandle('click', {
1799 + const setClick2 = ReactDOM.unstable_createEventHandle('click', {
1800 capture: true,
1801 });
1742 - const click3 = ReactDOM.unstable_createEventHandle('click');
1743 - const click4 = ReactDOM.unstable_createEventHandle('click');
1802 + const setClick3 = ReactDOM.unstable_createEventHandle('click');
1803 + const setClick4 = ReactDOM.unstable_createEventHandle('click');
1804
1805 function Test() {
1806 React.useEffect(() => {
1747 - click1.setListener(buttonRef.current, targetListener1);
1748 - click2.setListener(buttonRef.current, targetListener2);
1749 - click3.setListener(buttonRef.current, targetListener3);
1750 - click4.setListener(buttonRef.current, targetListener4);
1807 + const clearClick1 = setClick1(
1808 + buttonRef.current,
1809 + targetListener1,
1810 + );
1811 + const clearClick2 = setClick2(
1812 + buttonRef.current,
1813 + targetListener2,
1814 + );
1815 + const clearClick3 = setClick3(
1816 + buttonRef.current,
1817 + targetListener3,
1818 + );
1819 + const clearClick4 = setClick4(
1820 + buttonRef.current,
1821 + targetListener4,
1822 + );
1823 +
1824 + return () => {
1825 + clearClick1();
1826 + clearClick2();
1827 + clearClick3();
1828 + clearClick4();
1829 + };
1830 });
1831
1832 return <button ref={buttonRef}>Click me!</button>;
@@ -1768,11 +1847,11 @@ describe('DOMModernPluginEventSystem', () => {
1847 it('should work with concurrent mode updates', async () => {
1848 const log = [];
1849 const ref = React.createRef();
1771 - const click = ReactDOM.unstable_createEventHandle('click');
1850 + const setClick1 = ReactDOM.unstable_createEventHandle('click');
1851
1852 function Test({counter}) {
1853 React.useLayoutEffect(() => {
1775 - click.setListener(ref.current, () => {
1854 + return setClick1(ref.current, () => {
1855 log.push({counter});
1856 });
1857 });
@@ -1816,16 +1895,16 @@ describe('DOMModernPluginEventSystem', () => {
1895 const clickEvent = jest.fn();
1896 const buttonRef = React.createRef();
1897 const button2Ref = React.createRef();
1819 - const click = ReactDOM.unstable_createEventHandle('click', {
1898 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
1899 passive: false,
1900 });
1822 - const click2 = ReactDOM.unstable_createEventHandle('click', {
1901 + const setClick2 = ReactDOM.unstable_createEventHandle('click', {
1902 passive: true,
1903 });
1904
1905 function Test2() {
1906 React.useEffect(() => {
1828 - click.setListener(button2Ref.current, clickEvent);
1907 + return setClick1(button2Ref.current, clickEvent);
1908 });
1909
1910 return <button ref={button2Ref}>Click me!</button>;
@@ -1833,7 +1912,7 @@ describe('DOMModernPluginEventSystem', () => {
1912
1913 function Test({extra}) {
1914 React.useEffect(() => {
1836 - click2.setListener(buttonRef.current, clickEvent);
1915 + return setClick2(buttonRef.current, clickEvent);
1916 });
1917
1918 return (
@@ -1866,16 +1945,16 @@ describe('DOMModernPluginEventSystem', () => {
1945 const clickEvent = jest.fn();
1946 const buttonRef = React.createRef();
1947 const button2Ref = React.createRef();
1869 - const click = ReactDOM.unstable_createEventHandle('click', {
1948 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
1949 passive: false,
1950 });
1872 - const click2 = ReactDOM.unstable_createEventHandle('click', {
1951 + const setClick2 = ReactDOM.unstable_createEventHandle('click', {
1952 passive: undefined,
1953 });
1954
1955 function Test2() {
1956 React.useEffect(() => {
1878 - click.setListener(button2Ref.current, clickEvent);
1957 + return setClick1(button2Ref.current, clickEvent);
1958 });
1959
1960 return <button ref={button2Ref}>Click me!</button>;
@@ -1883,7 +1962,7 @@ describe('DOMModernPluginEventSystem', () => {
1962
1963 function Test({extra}) {
1964 React.useEffect(() => {
1886 - click2.setListener(buttonRef.current, clickEvent);
1965 + return setClick2(buttonRef.current, clickEvent);
1966 });
1967
1968 return (
@@ -1922,15 +2001,11 @@ describe('DOMModernPluginEventSystem', () => {
2001 target: event.target,
2002 });
2003 });
1925 - const click = ReactDOM.unstable_createEventHandle('click');
2004 + const setClick1 = ReactDOM.unstable_createEventHandle('click');
2005
2006 function Test() {
2007 React.useEffect(() => {
1929 - click.setListener(window, clickEvent);
1930 -
1931 - return () => {
1932 - click.setListener(window, null);
1933 - };
2008 + return setClick1(window, clickEvent);
2009 });
2010
2011 return <button>Click anything!</button>;
@@ -1975,23 +2050,39 @@ describe('DOMModernPluginEventSystem', () => {
2050 const onClickCapture = jest.fn(e =>
2051 log.push(['capture', e.currentTarget]),
2052 );
1978 - const click = ReactDOM.unstable_createEventHandle('click');
1979 - const clickCapture = ReactDOM.unstable_createEventHandle('click', {
1980 - capture: true,
1981 - });
2053 + const setClick = ReactDOM.unstable_createEventHandle('click');
2054 + const setClickCapture = ReactDOM.unstable_createEventHandle(
2055 + 'click',
2056 + {
2057 + capture: true,
2058 + },
2059 + );
2060
2061 function Test() {
2062 React.useEffect(() => {
1985 - click.setListener(window, onClick);
1986 - clickCapture.setListener(window, onClickCapture);
1987 - click.setListener(buttonRef.current, onClick);
1988 - clickCapture.setListener(buttonRef.current, onClickCapture);
1989 - click.setListener(divRef.current, onClick);
1990 - clickCapture.setListener(divRef.current, onClickCapture);
2063 + const clearClick1 = setClick(window, onClick);
2064 + const clearClickCapture1 = setClickCapture(
2065 + window,
2066 + onClickCapture,
2067 + );
2068 + const clearClick2 = setClick(buttonRef.current, onClick);
2069 + const clearClickCapture2 = setClickCapture(
2070 + buttonRef.current,
2071 + onClickCapture,
2072 + );
2073 + const clearClick3 = setClick(divRef.current, onClick);
2074 + const clearClickCapture3 = setClickCapture(
2075 + divRef.current,
2076 + onClickCapture,
2077 + );
2078
2079 return () => {
1993 - click.setListener(window, null);
1994 - clickCapture.setListener(window, null);
2080 + clearClick1();
2081 + clearClickCapture1();
2082 + clearClick2();
2083 + clearClickCapture2();
2084 + clearClick3();
2085 + clearClickCapture3();
2086 };
2087 });
2088
@@ -2037,25 +2128,33 @@ describe('DOMModernPluginEventSystem', () => {
2128 const rootListener2 = jest.fn();
2129 const targetListener1 = jest.fn();
2130 const targetListener2 = jest.fn();
2040 - const click1 = ReactDOM.unstable_createEventHandle('click', {
2131 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
2132 capture: true,
2133 });
2043 - const click2 = ReactDOM.unstable_createEventHandle('click', {
2134 + const setClick2 = ReactDOM.unstable_createEventHandle('click', {
2135 capture: true,
2136 });
2046 - const click3 = ReactDOM.unstable_createEventHandle('click');
2047 - const click4 = ReactDOM.unstable_createEventHandle('click');
2137 + const setClick3 = ReactDOM.unstable_createEventHandle('click');
2138 + const setClick4 = ReactDOM.unstable_createEventHandle('click');
2139
2140 function Test() {
2141 React.useEffect(() => {
2051 - click1.setListener(window, rootListener1);
2052 - click2.setListener(buttonRef.current, targetListener1);
2053 - click3.setListener(window, rootListener2);
2054 - click4.setListener(buttonRef.current, targetListener2);
2142 + const clearClick1 = setClick1(window, rootListener1);
2143 + const clearClick2 = setClick2(
2144 + buttonRef.current,
2145 + targetListener1,
2146 + );
2147 + const clearClick3 = setClick3(window, rootListener2);
2148 + const clearClick4 = setClick4(
2149 + buttonRef.current,
2150 + targetListener2,
2151 + );
2152
2153 return () => {
2057 - click1.setListener(window, null);
2058 - click3.setListener(window, null);
2154 + clearClick1();
2155 + clearClick2();
2156 + clearClick3();
2157 + clearClick4();
2158 };
2159 });
2160
@@ -2080,27 +2179,27 @@ describe('DOMModernPluginEventSystem', () => {
2179 const rootListener2 = jest.fn();
2180 const rootListener3 = jest.fn(e => e.stopPropagation());
2181 const rootListener4 = jest.fn();
2083 - const click1 = ReactDOM.unstable_createEventHandle('click', {
2182 + const setClick1 = ReactDOM.unstable_createEventHandle('click', {
2183 capture: true,
2184 });
2086 - const click2 = ReactDOM.unstable_createEventHandle('click', {
2185 + const setClick2 = ReactDOM.unstable_createEventHandle('click', {
2186 capture: true,
2187 });
2089 - const click3 = ReactDOM.unstable_createEventHandle('click');
2090 - const click4 = ReactDOM.unstable_createEventHandle('click');
2188 + const setClick3 = ReactDOM.unstable_createEventHandle('click');
2189 + const setClick4 = ReactDOM.unstable_createEventHandle('click');
2190
2191 function Test() {
2192 React.useEffect(() => {
2094 - click1.setListener(window, rootListener1);
2095 - click2.setListener(window, rootListener2);
2096 - click3.setListener(window, rootListener3);
2097 - click4.setListener(window, rootListener4);
2193 + const clearClick1 = setClick1(window, rootListener1);
2194 + const clearClick2 = setClick2(window, rootListener2);
2195 + const clearClick3 = setClick3(window, rootListener3);
2196 + const clearClick4 = setClick4(window, rootListener4);
2197
2198 return () => {
2100 - click1.setListener(window, null);
2101 - click2.setListener(window, null);
2102 - click3.setListener(window, null);
2103 - click4.setListener(window, null);
2199 + clearClick1();
2200 + clearClick2();
2201 + clearClick3();
2202 + clearClick4();
2203 };
2204 });
2205
@@ -2128,27 +2227,46 @@ describe('DOMModernPluginEventSystem', () => {
2227 const onClickCapture = jest.fn(e =>
2228 log.push(['capture', e.currentTarget]),
2229 );
2131 - const click = ReactDOM.unstable_createEventHandle('click');
2132 - const clickCapture = ReactDOM.unstable_createEventHandle('click', {
2133 - capture: true,
2134 - });
2230 + const setClick = ReactDOM.unstable_createEventHandle('click');
2231 + const setClickCapture = ReactDOM.unstable_createEventHandle(
2232 + 'click',
2233 + {
2234 + capture: true,
2235 + },
2236 + );
2237
2238 function Test() {
2239 React.useEffect(() => {
2138 - click.setListener(window, onClick);
2139 - clickCapture.setListener(window, onClickCapture);
2140 - click.setListener(document, onClick);
2141 - clickCapture.setListener(document, onClickCapture);
2142 - click.setListener(buttonRef.current, onClick);
2143 - clickCapture.setListener(buttonRef.current, onClickCapture);
2144 - click.setListener(divRef.current, onClick);
2145 - clickCapture.setListener(divRef.current, onClickCapture);
2240 + const clearClick1 = setClick(window, onClick);
2241 + const clearClickCapture1 = setClickCapture(
2242 + window,
2243 + onClickCapture,
2244 + );
2245 + const clearClick2 = setClick(document, onClick);
2246 + const clearClickCapture2 = setClickCapture(
2247 + document,
2248 + onClickCapture,
2249 + );
2250 + const clearClick3 = setClick(buttonRef.current, onClick);
2251 + const clearClickCapture3 = setClickCapture(
2252 + buttonRef.current,
2253 + onClickCapture,
2254 + );
2255 + const clearClick4 = setClick(divRef.current, onClick);
2256 + const clearClickCapture4 = setClickCapture(
2257 + divRef.current,
2258 + onClickCapture,
2259 + );
2260
2261 return () => {
2148 - click.setListener(window, null);
2149 - clickCapture.setListener(window, null);
2150 - click.setListener(document, null);
2151 - clickCapture.setListener(document, null);
2262 + clearClick1();
2263 + clearClickCapture1();
2264 + clearClick2();
2265 + clearClickCapture2();
2266 + clearClick3();
2267 + clearClickCapture3();
2268 + clearClick4();
2269 + clearClickCapture4();
2270 };
2271 });
2272
@@ -2225,11 +2343,11 @@ describe('DOMModernPluginEventSystem', () => {
2343 log.push(['capture', e.currentTarget]),
2344 );
2345
2228 - let customEventHandle;
2346 + let setCustomEventHandle;
2347
2348 // Test that we get a warning when we don't provide an explicit priority
2349 expect(() => {
2232 - customEventHandle = ReactDOM.unstable_createEventHandle(
2350 + setCustomEventHandle = ReactDOM.unstable_createEventHandle(
2351 'custom-event',
2352 );
2353 }).toWarnDev(
@@ -2238,14 +2356,14 @@ describe('DOMModernPluginEventSystem', () => {
2356 {withoutStack: true},
2357 );
2358
2241 - customEventHandle = ReactDOM.unstable_createEventHandle(
2359 + setCustomEventHandle = ReactDOM.unstable_createEventHandle(
2360 'custom-event',
2361 {
2362 priority: 0, // Discrete
2363 },
2364 );
2365
2248 - const customCaptureHandle = ReactDOM.unstable_createEventHandle(
2366 + const setCustomCaptureHandle = ReactDOM.unstable_createEventHandle(
2367 'custom-event',
2368 {
2369 capture: true,
@@ -2255,16 +2373,29 @@ describe('DOMModernPluginEventSystem', () => {
2373
2374 function Test() {
2375 React.useEffect(() => {
2258 - customEventHandle.setListener(buttonRef.current, onCustomEvent);
2259 - customCaptureHandle.setListener(
2376 + const clearCustom1 = setCustomEventHandle(
2377 + buttonRef.current,
2378 + onCustomEvent,
2379 + );
2380 + const clearCustom2 = setCustomCaptureHandle(
2381 buttonRef.current,
2382 onCustomEventCapture,
2383 );
2263 - customEventHandle.setListener(divRef.current, onCustomEvent);
2264 - customCaptureHandle.setListener(
2384 + const clearCustom3 = setCustomEventHandle(
2385 + divRef.current,
2386 + onCustomEvent,
2387 + );
2388 + const clearCustom4 = setCustomCaptureHandle(
2389 divRef.current,
2390 onCustomEventCapture,
2391 );
2392 +
2393 + return () => {
2394 + clearCustom1();
2395 + clearCustom2();
2396 + clearCustom3();
2397 + clearCustom4();
2398 + };
2399 });
2400
2401 return (
@@ -2305,10 +2436,10 @@ describe('DOMModernPluginEventSystem', () => {
2436 const onBeforeBlur = jest.fn(e => log.push(e.type));
2437 const innerRef = React.createRef();
2438 const innerRef2 = React.createRef();
2308 - const afterBlurHandle = ReactDOM.unstable_createEventHandle(
2439 + const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
2440 'afterblur',
2441 );
2311 - const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
2442 + const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
2443 'beforeblur',
2444 );
2445
@@ -2316,8 +2447,13 @@ describe('DOMModernPluginEventSystem', () => {
2447 const ref = React.useRef(null);
2448
2449 React.useEffect(() => {
2319 - afterBlurHandle.setListener(document, onAfterBlur);
2320 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2450 + const clear1 = setAfterBlurHandle(document, onAfterBlur);
2451 + const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
2452 +
2453 + return () => {
2454 + clear1();
2455 + clear2();
2456 + };
2457 });
2458
2459 return (
@@ -2359,10 +2495,10 @@ describe('DOMModernPluginEventSystem', () => {
2495 const onBeforeBlur = jest.fn(e => log.push(e.type));
2496 const innerRef = React.createRef();
2497 const innerRef2 = React.createRef();
2362 - const afterBlurHandle = ReactDOM.unstable_createEventHandle(
2498 + const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
2499 'afterblur',
2500 );
2365 - const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
2501 + const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
2502 'beforeblur',
2503 );
2504
@@ -2370,8 +2506,13 @@ describe('DOMModernPluginEventSystem', () => {
2506 const ref = React.useRef(null);
2507
2508 React.useEffect(() => {
2373 - afterBlurHandle.setListener(document, onAfterBlur);
2374 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2509 + const clear1 = setAfterBlurHandle(document, onAfterBlur);
2510 + const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
2511 +
2512 + return () => {
2513 + clear1();
2514 + clear2();
2515 + };
2516 });
2517
2518 return (
@@ -2422,10 +2563,10 @@ describe('DOMModernPluginEventSystem', () => {
2563 const promise = new Promise(
2564 resolvePromise => (resolve = resolvePromise),
2565 );
2425 - const afterBlurHandle = ReactDOM.unstable_createEventHandle(
2566 + const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
2567 'afterblur',
2568 );
2428 - const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
2569 + const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
2570 'beforeblur',
2571 );
2572
@@ -2441,8 +2582,13 @@ describe('DOMModernPluginEventSystem', () => {
2582 const ref = React.useRef(null);
2583
2584 React.useEffect(() => {
2444 - afterBlurHandle.setListener(document, onAfterBlur);
2445 - beforeBlurHandle.setListener(ref.current, onBeforeBlur);
2585 + const clear1 = setAfterBlurHandle(document, onAfterBlur);
2586 + const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
2587 +
2588 + return () => {
2589 + clear1();
2590 + clear2();
2591 + };
2592 });
2593
2594 return (
@@ -2492,7 +2638,7 @@ describe('DOMModernPluginEventSystem', () => {
2638 const Suspense = React.Suspense;
2639 let suspend = false;
2640 const promise = Promise.resolve();
2495 - const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
2641 + const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
2642 'beforeblur',
2643 );
2644 const innerRef = React.createRef();
@@ -2509,7 +2655,7 @@ describe('DOMModernPluginEventSystem', () => {
2655 const [, setState] = React.useState(0);
2656
2657 React.useEffect(() => {
2512 - beforeBlurHandle.setListener(ref.current, () => {
2658 + return setBeforeBlurHandle(ref.current, () => {
2659 // In the regression case, this would trigger an update, then
2660 // the resulting render would trigger another blur event,
2661 // which would trigger an update again, and on and on in an
@@ -2573,8 +2719,8 @@ describe('DOMModernPluginEventSystem', () => {
2719 log.push(['capture', e.currentTarget]),
2720 );
2721 const TestScope = React.unstable_createScope();
2576 - const click = ReactDOM.unstable_createEventHandle('click');
2577 - const clickCapture = ReactDOM.unstable_createEventHandle(
2722 + const setClick = ReactDOM.unstable_createEventHandle('click');
2723 + const setClickCapture = ReactDOM.unstable_createEventHandle(
2724 'click',
2725 {
2726 capture: true,
@@ -2585,8 +2731,16 @@ describe('DOMModernPluginEventSystem', () => {
2731 const scopeRef = React.useRef(null);
2732
2733 React.useEffect(() => {
2588 - click.setListener(scopeRef.current, onClick);
2589 - clickCapture.setListener(scopeRef.current, onClickCapture);
2734 + const clear1 = setClick(scopeRef.current, onClick);
2735 + const clear2 = setClickCapture(
2736 + scopeRef.current,
2737 + onClickCapture,
2738 + );
2739 +
2740 + return () => {
2741 + clear1();
2742 + clear2();
2743 + };
2744 });
2745
2746 return (
@@ -2622,8 +2776,8 @@ describe('DOMModernPluginEventSystem', () => {
2776 log.push(['capture', e.currentTarget]),
2777 );
2778 const TestScope = React.unstable_createScope();
2625 - const click = ReactDOM.unstable_createEventHandle('click');
2626 - const clickCapture = ReactDOM.unstable_createEventHandle(
2779 + const setClick = ReactDOM.unstable_createEventHandle('click');
2780 + const setClickCapture = ReactDOM.unstable_createEventHandle(
2781 'click',
2782 {
2783 capture: true,
@@ -2634,10 +2788,23 @@ describe('DOMModernPluginEventSystem', () => {
2788 const scopeRef = React.useRef(null);
2789
2790 React.useEffect(() => {
2637 - click.setListener(scopeRef.current, onClick);
2638 - clickCapture.setListener(scopeRef.current, onClickCapture);
2639 - click.setListener(buttonRef.current, onClick);
2640 - clickCapture.setListener(buttonRef.current, onClickCapture);
2791 + const clear1 = setClick(scopeRef.current, onClick);
2792 + const clear2 = setClickCapture(
2793 + scopeRef.current,
2794 + onClickCapture,
2795 + );
2796 + const clear3 = setClick(buttonRef.current, onClick);
2797 + const clear4 = setClickCapture(
2798 + buttonRef.current,
2799 + onClickCapture,
2800 + );
2801 +
2802 + return () => {
2803 + clear1();
2804 + clear2();
2805 + clear3();
2806 + clear4();
2807 + };
2808 });
2809
2810 return (
@@ -2693,13 +2860,13 @@ describe('DOMModernPluginEventSystem', () => {
2860 const clickEvent = jest.fn();
2861 const buttonRef = React.createRef();
2862 const TestScope = React.unstable_createScope();
2696 - const click = ReactDOM.unstable_createEventHandle('click');
2863 + const setClick = ReactDOM.unstable_createEventHandle('click');
2864
2865 function Test() {
2866 const scopeRef = React.useRef(null);
2867
2868 React.useEffect(() => {
2702 - click.setListener(scopeRef.current, clickEvent);
2869 + return setClick(scopeRef.current, clickEvent);
2870 });
2871
2872 return (
@@ -2726,15 +2893,20 @@ describe('DOMModernPluginEventSystem', () => {
2893 const innerOnClick = jest.fn(e => e.stopPropagation());
2894 const TestScope = React.unstable_createScope();
2895 const TestScope2 = React.unstable_createScope();
2729 - const click = ReactDOM.unstable_createEventHandle('click');
2896 + const setClick = ReactDOM.unstable_createEventHandle('click');
2897
2898 function Test() {
2899 const scopeRef = React.useRef(null);
2900 const scope2Ref = React.useRef(null);
2901
2902 React.useEffect(() => {
2736 - click.setListener(scopeRef.current, outerOnClick);
2737 - click.setListener(scope2Ref.current, innerOnClick);
2903 + const clear1 = setClick(scopeRef.current, outerOnClick);
2904 + const clear2 = setClick(scope2Ref.current, innerOnClick);
2905 +
2906 + return () => {
2907 + clear1();
2908 + clear2();
2909 + };
2910 });
2911
2912 return (
@@ -2763,15 +2935,20 @@ describe('DOMModernPluginEventSystem', () => {
2935 const innerOnClick = jest.fn();
2936 const TestScope = React.unstable_createScope();
2937 const TestScope2 = React.unstable_createScope();
2766 - const click = ReactDOM.unstable_createEventHandle('click');
2938 + const setClick = ReactDOM.unstable_createEventHandle('click');
2939
2940 function Test() {
2941 const scopeRef = React.useRef(null);
2942 const scope2Ref = React.useRef(null);
2943
2944 React.useEffect(() => {
2773 - click.setListener(scopeRef.current, outerOnClick);
2774 - click.setListener(scope2Ref.current, innerOnClick);
2945 + const clear1 = setClick(scopeRef.current, outerOnClick);
2946 + const clear2 = setClick(scope2Ref.current, innerOnClick);
2947 +
2948 + return () => {
2949 + clear1();
2950 + clear2();
2951 + };
2952 });
2953
2954 return (
@@ -2799,15 +2976,20 @@ describe('DOMModernPluginEventSystem', () => {
2976 const onClick = jest.fn(e => e.stopPropagation());
2977 const TestScope = React.unstable_createScope();
2978 const TestScope2 = React.unstable_createScope();
2802 - const click = ReactDOM.unstable_createEventHandle('click');
2979 + const setClick = ReactDOM.unstable_createEventHandle('click');
2980
2981 function Test() {
2982 const scopeRef = React.useRef(null);
2983 const scope2Ref = React.useRef(null);
2984
2985 React.useEffect(() => {
2809 - click.setListener(scopeRef.current, onClick);
2810 - click.setListener(scope2Ref.current, onClick);
2986 + const clear1 = setClick(scopeRef.current, onClick);
2987 + const clear2 = setClick(scope2Ref.current, onClick);
2988 +
2989 + return () => {
2990 + clear1();
2991 + clear2();
2992 + };
2993 });
2994
2995 return (
packages/react-dom/src/shared/ReactDOMTypes.js
+4 -8
@@ -78,17 +78,13 @@ export type ReactDOMResponderContext = {
78 ...
79 };
80
81 -export type ReactDOMEventHandle = {|
82 - setListener(
83 - target: EventTarget | ReactScopeInstance,
84 - callback: (SyntheticEvent<EventTarget>) => void,
85 - ): void,
86 - clear(): void,
87 -|};
81 +export type ReactDOMEventHandle = (
82 + target: EventTarget | ReactScopeInstance,
83 + callback: (SyntheticEvent<EventTarget>) => void,
84 +) => () => void;
85
86 export type ReactDOMEventHandleListener = {|
87 callback: (SyntheticEvent<EventTarget>) => void,
88 capture: boolean,
92 - destroy: (target: EventTarget | ReactScopeInstance) => void,
89 type: DOMTopLevelEventType,
90 |};
packages/react-interactions/events/src/dom/create-event-handle/useEvent.js
+36 -8
@@ -10,7 +10,7 @@
10 import * as React from 'react';
11 import * as ReactDOM from 'react-dom';
12
13 -const {useEffect, useRef} = React;
13 +const {useLayoutEffect, useRef} = React;
14 const {unstable_createEventHandle: createEventHandle} = ReactDOM;
15
16 type UseEventHandle = {|
@@ -30,20 +30,48 @@ export default function useEvent(
30 |},
31 ): UseEventHandle {
32 const handleRef = useRef(null);
33 + let setListener;
34 + let clears;
35 + let useEventHandle;
36
37 if (handleRef.current == null) {
35 - handleRef.current = createEventHandle(event, options);
38 + setListener = createEventHandle(event, options);
39 + clears = new Map();
40 + useEventHandle = {
41 + setListener(
42 + target: EventTarget,
43 + callback: null | ((SyntheticEvent<EventTarget>) => void),
44 + ): void {
45 + let clear = clears.get(target);
46 + if (clear !== undefined) {
47 + clear();
48 + }
49 + if (callback === null) {
50 + clears.delete(target);
51 + return;
52 + }
53 + clear = setListener(target, callback);
54 + clears.set(target, clear);
55 + },
56 + clear(): void {
57 + const clearsArr = Array.from(clears.values());
58 + for (let i = 0; i < clearsArr.length; i++) {
59 + clearsArr[i]();
60 + }
61 + clears.clear();
62 + },
63 + };
64 + handleRef.current = {setListener, clears, useEventHandle};
65 + } else {
66 + ({setListener, clears, useEventHandle} = handleRef.current);
67 }
68
38 - useEffect(() => {
39 - const handle = handleRef.current;
69 + useLayoutEffect(() => {
70 return () => {
41 - if (handle !== null) {
42 - handle.clear();
43 - }
71 + useEventHandle.clear();
72 handleRef.current = null;
73 };
74 }, []);
75
48 - return ((handleRef.current: any): UseEventHandle);
76 + return useEventHandle;
77 }
packages/react-native-renderer/src/ReactFabricHostConfig.js
-4
@@ -479,10 +479,6 @@ export function getInstanceFromNode(node: any) {
479 throw new Error('Not yet implemented.');
480 }
481
482 -export function removeInstanceEventHandles(instance: any) {
483 - // noop
484 -}
485 -
482 export function isOpaqueHydratingObject(value: mixed): boolean {
483 throw new Error('Not yet implemented');
484 }
packages/react-native-renderer/src/ReactNativeHostConfig.js
-4
@@ -532,10 +532,6 @@ export function getInstanceFromNode(node: any) {
532 throw new Error('Not yet implemented.');
533 }
534
535 -export function removeInstanceEventHandles(instance: any) {
536 - // noop
537 -}
538 -
535 export function isOpaqueHydratingObject(value: mixed): boolean {
536 throw new Error('Not yet implemented');
537 }
packages/react-noop-renderer/src/createReactNoop.js
-6
@@ -438,10 +438,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
438 throw new Error('Not yet implemented.');
439 },
440
441 - removeInstanceEventHandles(instance: any): void {
442 - // NO-OP
443 - },
444 -
441 beforeActiveInstanceBlur() {
442 // NO-OP
443 },
@@ -456,8 +452,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
452
453 prepareScopeUpdate() {},
454
459 - removeScopeEventHandles() {},
460 -
455 getInstanceFromScope() {
456 throw new Error('Not yet implemented.');
457 },
packages/react-reconciler/src/ReactFiberCommitWork.new.js
-10
@@ -35,7 +35,6 @@ import {
35 enableFundamentalAPI,
36 enableSuspenseCallback,
37 enableScopeAPI,
38 - enableCreateEventHandleAPI,
38 } from 'shared/ReactFeatureFlags';
39 import {
40 FunctionComponent,
@@ -109,10 +108,8 @@ import {
108 updateFundamentalComponent,
109 commitHydratedContainer,
110 commitHydratedSuspenseInstance,
112 - removeInstanceEventHandles,
111 clearContainer,
112 prepareScopeUpdate,
115 - removeScopeEventHandles,
113 } from './ReactFiberHostConfig';
114 import {
115 captureCommitPhaseError,
@@ -928,9 +925,6 @@ function commitUnmount(
925 if (enableDeprecatedFlareAPI) {
926 unmountDeprecatedResponderListeners(current);
927 }
931 - if (enableCreateEventHandleAPI && current.ref !== null) {
932 - removeInstanceEventHandles(current.stateNode);
933 - }
928 safelyDetachRef(current);
929 return;
930 }
@@ -972,10 +966,6 @@ function commitUnmount(
966 if (enableDeprecatedFlareAPI) {
967 unmountDeprecatedResponderListeners(current);
968 }
975 - const scopeInstance = current.stateNode;
976 - if (enableCreateEventHandleAPI && current.ref !== null) {
977 - removeScopeEventHandles(scopeInstance);
978 - }
969 safelyDetachRef(current);
970 }
971 return;
packages/react-reconciler/src/ReactFiberCommitWork.old.js
-10
@@ -35,7 +35,6 @@ import {
35 enableFundamentalAPI,
36 enableSuspenseCallback,
37 enableScopeAPI,
38 - enableCreateEventHandleAPI,
38 } from 'shared/ReactFeatureFlags';
39 import {
40 FunctionComponent,
@@ -109,10 +108,8 @@ import {
108 updateFundamentalComponent,
109 commitHydratedContainer,
110 commitHydratedSuspenseInstance,
112 - removeInstanceEventHandles,
111 clearContainer,
112 prepareScopeUpdate,
115 - removeScopeEventHandles,
113 } from './ReactFiberHostConfig';
114 import {
115 captureCommitPhaseError,
@@ -928,9 +925,6 @@ function commitUnmount(
925 if (enableDeprecatedFlareAPI) {
926 unmountDeprecatedResponderListeners(current);
927 }
931 - if (enableCreateEventHandleAPI && current.ref !== null) {
932 - removeInstanceEventHandles(current.stateNode);
933 - }
928 safelyDetachRef(current);
929 return;
930 }
@@ -972,10 +966,6 @@ function commitUnmount(
966 if (enableDeprecatedFlareAPI) {
967 unmountDeprecatedResponderListeners(current);
968 }
975 - const scopeInstance = current.stateNode;
976 - if (enableCreateEventHandleAPI && current.ref !== null) {
977 - removeScopeEventHandles(scopeInstance);
978 - }
969 safelyDetachRef(current);
970 }
971 return;
packages/react-reconciler/src/ReactFiberHostConfigWithNoScopes.js
-1
@@ -23,5 +23,4 @@ function shim(...args: any) {
23
24 // React Scopes (when unsupported)
25 export const prepareScopeUpdate = shim;
26 -export const removeScopeEventHandles = shim;
26 export const getInstanceFromScope = shim;
packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
-3
@@ -72,8 +72,6 @@ export const mountFundamentalComponent =
72 export const shouldUpdateFundamentalComponent =
73 $$$hostConfig.shouldUpdateFundamentalComponent;
74 export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
75 -export const removeInstanceEventHandles =
76 - $$$hostConfig.removeInstanceEventHandles;
75 export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject;
76 export const makeOpaqueHydratingObject =
77 $$$hostConfig.makeOpaqueHydratingObject;
@@ -83,7 +81,6 @@ export const beforeActiveInstanceBlur = $$$hostConfig.beforeActiveInstanceBlur;
81 export const afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur;
82 export const preparePortalMount = $$$hostConfig.preparePortalMount;
83 export const prepareScopeUpdate = $$$hostConfig.preparePortalMount;
86 -export const removeScopeEventHandles = $$$hostConfig.removeScopeEventHandles;
84 export const getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
85
86 // -------------------
packages/react-test-renderer/src/ReactTestHostConfig.js
-8
@@ -383,10 +383,6 @@ export function getInstanceFromNode(mockNode: Object) {
383 return null;
384 }
385
386 -export function removeInstanceEventHandles(instance: any) {
387 - // noop
388 -}
389 -
386 let clientId: number = 0;
387 export function makeClientId(): OpaqueIDType {
388 return 'c_' + (clientId++).toString(36);
@@ -440,10 +436,6 @@ export function prepareScopeUpdate(scopeInstance: Object, inst: Object): void {
436 nodeToInstanceMap.set(scopeInstance, inst);
437 }
438
443 -export function removeScopeEventHandles(scopeInstance: Object): void {
444 - nodeToInstanceMap.delete(scopeInstance);
445 -}
446 -
439 export function getInstanceFromScope(scopeInstance: Object): null | Object {
440 return nodeToInstanceMap.get(scopeInstance) || null;
441 }
scripts/error-codes/codes.json
+3 -2
@@ -1,4 +1,3 @@
1 -
1 {
2 "0": "React.addons.createFragment(...): Encountered an invalid child; DOM elements are not valid children of React components.",
3 "1": "update(): expected target of %s to be an array; got %s.",
@@ -366,5 +365,7 @@
365 "365": "Invalid selector type %s specified.",
366 "366": "ReactDOM.createEventHandle: setListener called on an target that did not have a corresponding root. This is likely a bug in React.",
367 "367": "ReactDOM.createEventHandle: setListener called on an element target that is not managed by React. Ensure React rendered the DOM element.",
369 - "368": "ReactDOM.createEventHandle: setListener called on an invalid target. Provide a valid EventTarget or an element managed by React."
368 + "368": "ReactDOM.createEventHandle: setListener called on an invalid target. Provide a valid EventTarget or an element managed by React.",
369 + "369": "ReactDOM.createEventHandle: setter called on an invalid target. Provide a valid EventTarget or an element managed by React.",
370 + "370": "ReactDOM.createEventHandle: setter called with an invalid callback. The callback must be a function."
371 }