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
}