Refactor createEventHandle signature (#19174)

This commit is contained in:
Dominic Gannaway
2020-07-07 13:05:06 +01:00
committed by GitHub
parent 97b96da295
commit 4eb9b1d2b4
16 changed files with 497 additions and 389 deletions

View File

@@ -469,10 +469,6 @@ export function getInstanceFromNode(node) {
throw new Error('Not yet implemented.');
}
export function removeInstanceEventHandles(instance) {
// noop
}
export function isOpaqueHydratingObject(value: mixed): boolean {
throw new Error('Not yet implemented');
}

View File

@@ -47,6 +47,8 @@ type EventHandleOptions = {|
priority?: EventPriority,
|};
const PossiblyWeakSet = typeof WeakSet === 'function' ? WeakSet : Set;
function getNearestRootOrPortalContainer(node: Fiber): null | Element {
while (node !== null) {
const tag = node.tag;
@@ -72,12 +74,10 @@ function createEventHandleListener(
type: DOMTopLevelEventType,
capture: boolean,
callback: (SyntheticEvent<EventTarget>) => void,
destroy: (target: EventTarget | ReactScopeInstance) => void,
): ReactDOMEventHandleListener {
return {
callback,
capture,
destroy,
type,
};
}
@@ -111,6 +111,65 @@ function registerEventOnNearestTargetContainer(
);
}
function registerReactDOMEvent(
target: EventTarget | ReactScopeInstance,
topLevelType: DOMTopLevelEventType,
passive: boolean | void,
capture: boolean,
priority: EventPriority | void,
): void {
// Check if the target is a DOM element.
if ((target: any).nodeType === ELEMENT_NODE) {
const targetElement = ((target: any): Element);
// Check if the DOM element is managed by React.
const targetFiber = getClosestInstanceFromNode(targetElement);
if (targetFiber === null) {
invariant(
false,
'ReactDOM.createEventHandle: setListener called on an element ' +
'target that is not managed by React. Ensure React rendered the DOM element.',
);
}
registerEventOnNearestTargetContainer(
targetFiber,
topLevelType,
passive,
priority,
);
} else if (enableScopeAPI && isReactScope(target)) {
const scopeTarget = ((target: any): ReactScopeInstance);
const targetFiber = getFiberFromScopeInstance(scopeTarget);
if (targetFiber === null) {
// Scope is unmounted, do not proceed.
return;
}
registerEventOnNearestTargetContainer(
targetFiber,
topLevelType,
passive,
priority,
);
} else if (isValidEventTarget(target)) {
const eventTarget = ((target: any): EventTarget);
const listenerMap = getEventListenerMap(eventTarget);
listenToTopLevelEvent(
topLevelType,
eventTarget,
listenerMap,
PLUGIN_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
capture,
passive,
priority,
);
} else {
invariant(
false,
'ReactDOM.createEventHandle: setter called on an invalid ' +
'target. Provide a valid EventTarget or an element managed by React.',
);
}
}
export function createEventHandle(
type: string,
options?: EventHandleOptions,
@@ -140,110 +199,39 @@ export function createEventHandle(
priority = getEventPriorityForListenerSystem(topLevelType);
}
const listeners = new Map();
const registeredReactDOMEvents = new PossiblyWeakSet();
const destroy = (target: EventTarget | ReactScopeInstance): void => {
const listener = listeners.get(target);
if (listener !== undefined) {
listeners.delete(target);
const targetListeners = getEventHandlerListeners(target);
if (targetListeners !== null) {
targetListeners.delete(listener);
}
return (
target: EventTarget | ReactScopeInstance,
callback: (SyntheticEvent<EventTarget>) => void,
) => {
invariant(
typeof callback === 'function',
'ReactDOM.createEventHandle: setter called with an invalid ' +
'callback. The callback must be a function.',
);
if (!registeredReactDOMEvents.has(target)) {
registeredReactDOMEvents.add(target);
registerReactDOMEvent(target, topLevelType, passive, capture, priority);
// Add the event to our known event types list.
addEventTypeToDispatchConfig(topLevelType);
}
};
const clear = (): void => {
const eventTargetsArr = Array.from(listeners.keys());
for (let i = 0; i < eventTargetsArr.length; i++) {
destroy(eventTargetsArr[i]);
const listener = createEventHandleListener(
topLevelType,
capture,
callback,
);
let targetListeners = getEventHandlerListeners(target);
if (targetListeners === null) {
targetListeners = new Set();
setEventHandlerListeners(target, targetListeners);
}
};
return {
setListener(
target: EventTarget | ReactScopeInstance,
callback: null | ((SyntheticEvent<EventTarget>) => void),
): void {
// Check if the target is a DOM element.
if ((target: any).nodeType === ELEMENT_NODE) {
const targetElement = ((target: any): Element);
// Check if the DOM element is managed by React.
const targetFiber = getClosestInstanceFromNode(targetElement);
if (targetFiber === null) {
invariant(
false,
'ReactDOM.createEventHandle: setListener called on an element ' +
'target that is not managed by React. Ensure React rendered the DOM element.',
);
}
registerEventOnNearestTargetContainer(
targetFiber,
topLevelType,
passive,
priority,
);
} else if (enableScopeAPI && isReactScope(target)) {
const scopeTarget = ((target: any): ReactScopeInstance);
const targetFiber = getFiberFromScopeInstance(scopeTarget);
if (targetFiber === null) {
// Scope is unmounted, do not proceed.
return;
}
registerEventOnNearestTargetContainer(
targetFiber,
topLevelType,
passive,
priority,
);
} else if (isValidEventTarget(target)) {
const eventTarget = ((target: any): EventTarget);
const listenerMap = getEventListenerMap(eventTarget);
listenToTopLevelEvent(
topLevelType,
eventTarget,
listenerMap,
PLUGIN_EVENT_SYSTEM | IS_TARGET_PHASE_ONLY,
capture,
passive,
priority,
);
} else {
invariant(
false,
'ReactDOM.createEventHandle: setListener called on an invalid ' +
'target. Provide a valid EventTarget or an element managed by React.',
);
}
let listener = listeners.get(target);
if (listener === undefined) {
if (callback === null) {
return;
}
listener = createEventHandleListener(
topLevelType,
capture,
callback,
destroy,
);
listeners.set(target, listener);
let targetListeners = getEventHandlerListeners(target);
if (targetListeners === null) {
targetListeners = new Set();
setEventHandlerListeners(target, targetListeners);
}
targetListeners.add(listener);
// Finally, add the event to our known event types list.
addEventTypeToDispatchConfig(topLevelType);
} else if (callback !== null) {
listener.callback = callback;
} else {
// Remove listener
destroy(target);
}
},
clear,
targetListeners.add(listener);
return () => {
((targetListeners: any): Set<ReactDOMEventHandleListener>).delete(
listener,
);
};
};
}
return (null: any);

View File

@@ -80,10 +80,7 @@ import {
} from 'shared/ReactFeatureFlags';
import {HostComponent, HostText} from 'react-reconciler/src/ReactWorkTags';
import {TOP_BEFORE_BLUR, TOP_AFTER_BLUR} from '../events/DOMTopLevelEventTypes';
import {
listenToReactPropEvent,
clearEventHandleListenersForTarget,
} from '../events/DOMModernPluginEventSystem';
import {listenToReactPropEvent} from '../events/DOMModernPluginEventSystem';
export type Type = string;
export type Props = {
@@ -534,14 +531,6 @@ function dispatchAfterDetachedBlur(target: HTMLElement): void {
}
}
export function removeInstanceEventHandles(
instance: Instance | TextInstance | SuspenseInstance,
) {
if (enableCreateEventHandleAPI) {
clearEventHandleListenersForTarget(instance);
}
}
export function removeChild(
parentInstance: Instance,
child: Instance | TextInstance | SuspenseInstance,
@@ -1134,14 +1123,6 @@ export function prepareScopeUpdate(
}
}
export function removeScopeEventHandles(
scopeInstance: ReactScopeInstance,
): void {
if (enableScopeAPI && enableCreateEventHandleAPI) {
clearEventHandleListenersForTarget(scopeInstance);
}
}
export function getInstanceFromScope(
scopeInstance: ReactScopeInstance,
): null | Object {

View File

@@ -21,7 +21,7 @@ import type {
ElementListenerMap,
ElementListenerMapEntry,
} from '../client/ReactDOMComponentTree';
import type {EventPriority, ReactScopeInstance} from 'shared/ReactTypes';
import type {EventPriority} from 'shared/ReactTypes';
import type {Fiber} from 'react-reconciler/src/ReactInternalTypes';
import {registrationNameDependencies} from './EventRegistry';
@@ -954,24 +954,6 @@ export function addEventTypeToDispatchConfig(type: DOMTopLevelEventType): void {
}
}
export function clearEventHandleListenersForTarget(
target: EventTarget | ReactScopeInstance,
): void {
// It's unfortunate that we have to do this cleanup, but
// it's necessary otherwise we will leak the host instances
// on the createEventHandle API "listeners" Map. We call destroy
// on each listener to ensure we properly remove the instance
// from the listeners Map. Note: we have this Map so that we
// can track listeners for the handle.clear() API call.
const listeners = getEventHandlerListeners(target);
if (listeners !== null) {
const listenersArr = Array.from(listeners);
for (let i = 0; i < listenersArr.length; i++) {
listenersArr[i].destroy(target);
}
}
}
export function getListenerMapKey(
topLevelType: DOMTopLevelEventType,
capture: boolean,

View File

@@ -1208,13 +1208,13 @@ describe('DOMModernPluginEventSystem', () => {
// @gate experimental
it('can render correctly with the ReactDOMServer', () => {
const clickEvent = jest.fn();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
const divRef = React.useRef(null);
React.useEffect(() => {
click.setListener(divRef.current, clickEvent);
return setClick(divRef.current, clickEvent);
});
return <div ref={divRef}>Hello world</div>;
@@ -1227,11 +1227,11 @@ describe('DOMModernPluginEventSystem', () => {
it('can render correctly with the ReactDOMServer hydration', () => {
const clickEvent = jest.fn();
const spanRef = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click.setListener(spanRef.current, clickEvent);
return setClick(spanRef.current, clickEvent);
});
return (
@@ -1264,11 +1264,11 @@ describe('DOMModernPluginEventSystem', () => {
});
const divRef = React.createRef();
const buttonRef = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click.setListener(buttonRef.current, clickEvent);
return setClick(buttonRef.current, clickEvent);
});
return (
@@ -1327,11 +1327,11 @@ describe('DOMModernPluginEventSystem', () => {
},
]);
const click2 = ReactDOM.unstable_createEventHandle('click');
const setClick2 = ReactDOM.unstable_createEventHandle('click');
function Test2({clickEvent2}) {
React.useEffect(() => {
click2.setListener(buttonRef.current, clickEvent2);
return setClick2(buttonRef.current, clickEvent2);
});
return (
@@ -1364,18 +1364,16 @@ describe('DOMModernPluginEventSystem', () => {
const clickEvent = jest.fn();
const divRef = React.createRef();
const buttonRef = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test({off}) {
React.useEffect(() => {
click.setListener(buttonRef.current, clickEvent);
});
React.useEffect(() => {
const clear = setClick(buttonRef.current, clickEvent);
if (off) {
click.setListener(buttonRef.current, null);
clear();
}
}, [off]);
return clear;
});
return (
<button ref={buttonRef}>
@@ -1391,7 +1389,7 @@ describe('DOMModernPluginEventSystem', () => {
dispatchClickEvent(divElement);
expect(clickEvent).toBeCalledTimes(1);
// The listener should get unmounted in the second effect
// The listener should get unmounted
ReactDOM.render(<Test off={true} />, container);
Scheduler.unstable_flushAll();
@@ -1406,11 +1404,11 @@ describe('DOMModernPluginEventSystem', () => {
it('should handle the target being a text node', () => {
const clickEvent = jest.fn();
const buttonRef = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click.setListener(buttonRef.current, clickEvent);
return setClick(buttonRef.current, clickEvent);
});
return <button ref={buttonRef}>Click me!</button>;
@@ -1433,17 +1431,33 @@ describe('DOMModernPluginEventSystem', () => {
const onClickCapture = jest.fn(e =>
log.push(['capture', e.currentTarget]),
);
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const setClick = ReactDOM.unstable_createEventHandle('click');
const setCaptureClick = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
},
);
function Test() {
React.useEffect(() => {
click.setListener(buttonRef.current, onClick);
clickCapture.setListener(buttonRef.current, onClickCapture);
click.setListener(divRef.current, onClick);
clickCapture.setListener(divRef.current, onClickCapture);
const clearClick1 = setClick(buttonRef.current, onClick);
const clearCaptureClick1 = setCaptureClick(
buttonRef.current,
onClickCapture,
);
const clearClick2 = setClick(divRef.current, onClick);
const clearCaptureClick2 = setCaptureClick(
divRef.current,
onClickCapture,
);
return () => {
clearClick1();
clearCaptureClick1();
clearClick2();
clearCaptureClick2();
};
});
return (
@@ -1486,15 +1500,23 @@ describe('DOMModernPluginEventSystem', () => {
const onClickCapture = jest.fn(e =>
log.push(['capture', e.currentTarget]),
);
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const setClick = ReactDOM.unstable_createEventHandle('click');
const setClickCapture = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
},
);
function Test() {
React.useEffect(() => {
click.setListener(buttonRef.current, onClick);
clickCapture.setListener(buttonRef.current, onClickCapture);
setClick(buttonRef.current, onClick);
setClickCapture(buttonRef.current, onClickCapture);
return () => {
setClick();
setClickCapture();
};
});
return (
@@ -1542,11 +1564,11 @@ describe('DOMModernPluginEventSystem', () => {
});
const divRef = React.createRef();
const buttonRef = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click.setListener(divRef.current, clickEvent);
return setClick(divRef.current, clickEvent);
});
return (
@@ -1601,21 +1623,28 @@ describe('DOMModernPluginEventSystem', () => {
const targetListener2 = jest.fn();
const targetListener3 = jest.fn();
const targetListener4 = jest.fn();
let click1 = ReactDOM.unstable_createEventHandle('click', {
let setClick1 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
let click2 = ReactDOM.unstable_createEventHandle('click', {
let setClick2 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
let click3 = ReactDOM.unstable_createEventHandle('click');
let click4 = ReactDOM.unstable_createEventHandle('click');
let setClick3 = ReactDOM.unstable_createEventHandle('click');
let setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(buttonRef.current, targetListener1);
click2.setListener(buttonRef.current, targetListener2);
click3.setListener(buttonRef.current, targetListener3);
click4.setListener(buttonRef.current, targetListener4);
setClick1(buttonRef.current, targetListener1);
setClick2(buttonRef.current, targetListener2);
setClick3(buttonRef.current, targetListener3);
setClick4(buttonRef.current, targetListener4);
return () => {
setClick1();
setClick2();
setClick3();
setClick4();
};
});
return <button ref={buttonRef}>Click me!</button>;
@@ -1632,17 +1661,24 @@ describe('DOMModernPluginEventSystem', () => {
expect(targetListener3).toHaveBeenCalledTimes(1);
expect(targetListener4).toHaveBeenCalledTimes(1);
click1 = ReactDOM.unstable_createEventHandle('click');
click2 = ReactDOM.unstable_createEventHandle('click');
click3 = ReactDOM.unstable_createEventHandle('click');
click4 = ReactDOM.unstable_createEventHandle('click');
setClick1 = ReactDOM.unstable_createEventHandle('click');
setClick2 = ReactDOM.unstable_createEventHandle('click');
setClick3 = ReactDOM.unstable_createEventHandle('click');
setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test2() {
React.useEffect(() => {
click1.setListener(buttonRef.current, targetListener1);
click2.setListener(buttonRef.current, targetListener2);
click3.setListener(buttonRef.current, targetListener3);
click4.setListener(buttonRef.current, targetListener4);
setClick1(buttonRef.current, targetListener1);
setClick2(buttonRef.current, targetListener2);
setClick3(buttonRef.current, targetListener3);
setClick4(buttonRef.current, targetListener4);
return () => {
setClick1();
setClick2();
setClick3();
setClick4();
};
});
return <button ref={buttonRef}>Click me!</button>;
@@ -1664,17 +1700,22 @@ describe('DOMModernPluginEventSystem', () => {
const buttonRef = React.createRef();
const divRef = React.createRef();
const clickEvent = jest.fn();
const click1 = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
bind: buttonRef,
});
const click2 = ReactDOM.unstable_createEventHandle('click');
const setClick2 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(buttonRef.current, clickEvent);
click2.setListener(divRef.current, e => {
const clearClick1 = setClick1(buttonRef.current, clickEvent);
const clearClick2 = setClick2(divRef.current, e => {
e.stopPropagation();
});
return () => {
clearClick1();
clearClick2();
};
});
return (
@@ -1699,17 +1740,36 @@ describe('DOMModernPluginEventSystem', () => {
const targetListener2 = jest.fn(e => e.stopPropagation());
const targetListener3 = jest.fn(e => e.stopPropagation());
const targetListener4 = jest.fn(e => e.stopPropagation());
const click1 = ReactDOM.unstable_createEventHandle('click');
const click2 = ReactDOM.unstable_createEventHandle('click');
const click3 = ReactDOM.unstable_createEventHandle('click');
const click4 = ReactDOM.unstable_createEventHandle('click');
const setClick1 = ReactDOM.unstable_createEventHandle('click');
const setClick2 = ReactDOM.unstable_createEventHandle('click');
const setClick3 = ReactDOM.unstable_createEventHandle('click');
const setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(buttonRef.current, targetListener1);
click2.setListener(buttonRef.current, targetListener2);
click3.setListener(buttonRef.current, targetListener3);
click4.setListener(buttonRef.current, targetListener4);
const clearClick1 = setClick1(
buttonRef.current,
targetListener1,
);
const clearClick2 = setClick2(
buttonRef.current,
targetListener2,
);
const clearClick3 = setClick3(
buttonRef.current,
targetListener3,
);
const clearClick4 = setClick4(
buttonRef.current,
targetListener4,
);
return () => {
clearClick1();
clearClick2();
clearClick3();
clearClick4();
};
});
return <button ref={buttonRef}>Click me!</button>;
@@ -1733,21 +1793,40 @@ describe('DOMModernPluginEventSystem', () => {
const targetListener2 = jest.fn(e => e.stopPropagation());
const targetListener3 = jest.fn(e => e.stopPropagation());
const targetListener4 = jest.fn(e => e.stopPropagation());
const click1 = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click2 = ReactDOM.unstable_createEventHandle('click', {
const setClick2 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click3 = ReactDOM.unstable_createEventHandle('click');
const click4 = ReactDOM.unstable_createEventHandle('click');
const setClick3 = ReactDOM.unstable_createEventHandle('click');
const setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(buttonRef.current, targetListener1);
click2.setListener(buttonRef.current, targetListener2);
click3.setListener(buttonRef.current, targetListener3);
click4.setListener(buttonRef.current, targetListener4);
const clearClick1 = setClick1(
buttonRef.current,
targetListener1,
);
const clearClick2 = setClick2(
buttonRef.current,
targetListener2,
);
const clearClick3 = setClick3(
buttonRef.current,
targetListener3,
);
const clearClick4 = setClick4(
buttonRef.current,
targetListener4,
);
return () => {
clearClick1();
clearClick2();
clearClick3();
clearClick4();
};
});
return <button ref={buttonRef}>Click me!</button>;
@@ -1768,11 +1847,11 @@ describe('DOMModernPluginEventSystem', () => {
it('should work with concurrent mode updates', async () => {
const log = [];
const ref = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick1 = ReactDOM.unstable_createEventHandle('click');
function Test({counter}) {
React.useLayoutEffect(() => {
click.setListener(ref.current, () => {
return setClick1(ref.current, () => {
log.push({counter});
});
});
@@ -1816,16 +1895,16 @@ describe('DOMModernPluginEventSystem', () => {
const clickEvent = jest.fn();
const buttonRef = React.createRef();
const button2Ref = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
passive: false,
});
const click2 = ReactDOM.unstable_createEventHandle('click', {
const setClick2 = ReactDOM.unstable_createEventHandle('click', {
passive: true,
});
function Test2() {
React.useEffect(() => {
click.setListener(button2Ref.current, clickEvent);
return setClick1(button2Ref.current, clickEvent);
});
return <button ref={button2Ref}>Click me!</button>;
@@ -1833,7 +1912,7 @@ describe('DOMModernPluginEventSystem', () => {
function Test({extra}) {
React.useEffect(() => {
click2.setListener(buttonRef.current, clickEvent);
return setClick2(buttonRef.current, clickEvent);
});
return (
@@ -1866,16 +1945,16 @@ describe('DOMModernPluginEventSystem', () => {
const clickEvent = jest.fn();
const buttonRef = React.createRef();
const button2Ref = React.createRef();
const click = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
passive: false,
});
const click2 = ReactDOM.unstable_createEventHandle('click', {
const setClick2 = ReactDOM.unstable_createEventHandle('click', {
passive: undefined,
});
function Test2() {
React.useEffect(() => {
click.setListener(button2Ref.current, clickEvent);
return setClick1(button2Ref.current, clickEvent);
});
return <button ref={button2Ref}>Click me!</button>;
@@ -1883,7 +1962,7 @@ describe('DOMModernPluginEventSystem', () => {
function Test({extra}) {
React.useEffect(() => {
click2.setListener(buttonRef.current, clickEvent);
return setClick2(buttonRef.current, clickEvent);
});
return (
@@ -1922,15 +2001,11 @@ describe('DOMModernPluginEventSystem', () => {
target: event.target,
});
});
const click = ReactDOM.unstable_createEventHandle('click');
const setClick1 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click.setListener(window, clickEvent);
return () => {
click.setListener(window, null);
};
return setClick1(window, clickEvent);
});
return <button>Click anything!</button>;
@@ -1975,23 +2050,39 @@ describe('DOMModernPluginEventSystem', () => {
const onClickCapture = jest.fn(e =>
log.push(['capture', e.currentTarget]),
);
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const setClick = ReactDOM.unstable_createEventHandle('click');
const setClickCapture = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
},
);
function Test() {
React.useEffect(() => {
click.setListener(window, onClick);
clickCapture.setListener(window, onClickCapture);
click.setListener(buttonRef.current, onClick);
clickCapture.setListener(buttonRef.current, onClickCapture);
click.setListener(divRef.current, onClick);
clickCapture.setListener(divRef.current, onClickCapture);
const clearClick1 = setClick(window, onClick);
const clearClickCapture1 = setClickCapture(
window,
onClickCapture,
);
const clearClick2 = setClick(buttonRef.current, onClick);
const clearClickCapture2 = setClickCapture(
buttonRef.current,
onClickCapture,
);
const clearClick3 = setClick(divRef.current, onClick);
const clearClickCapture3 = setClickCapture(
divRef.current,
onClickCapture,
);
return () => {
click.setListener(window, null);
clickCapture.setListener(window, null);
clearClick1();
clearClickCapture1();
clearClick2();
clearClickCapture2();
clearClick3();
clearClickCapture3();
};
});
@@ -2037,25 +2128,33 @@ describe('DOMModernPluginEventSystem', () => {
const rootListener2 = jest.fn();
const targetListener1 = jest.fn();
const targetListener2 = jest.fn();
const click1 = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click2 = ReactDOM.unstable_createEventHandle('click', {
const setClick2 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click3 = ReactDOM.unstable_createEventHandle('click');
const click4 = ReactDOM.unstable_createEventHandle('click');
const setClick3 = ReactDOM.unstable_createEventHandle('click');
const setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(window, rootListener1);
click2.setListener(buttonRef.current, targetListener1);
click3.setListener(window, rootListener2);
click4.setListener(buttonRef.current, targetListener2);
const clearClick1 = setClick1(window, rootListener1);
const clearClick2 = setClick2(
buttonRef.current,
targetListener1,
);
const clearClick3 = setClick3(window, rootListener2);
const clearClick4 = setClick4(
buttonRef.current,
targetListener2,
);
return () => {
click1.setListener(window, null);
click3.setListener(window, null);
clearClick1();
clearClick2();
clearClick3();
clearClick4();
};
});
@@ -2080,27 +2179,27 @@ describe('DOMModernPluginEventSystem', () => {
const rootListener2 = jest.fn();
const rootListener3 = jest.fn(e => e.stopPropagation());
const rootListener4 = jest.fn();
const click1 = ReactDOM.unstable_createEventHandle('click', {
const setClick1 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click2 = ReactDOM.unstable_createEventHandle('click', {
const setClick2 = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const click3 = ReactDOM.unstable_createEventHandle('click');
const click4 = ReactDOM.unstable_createEventHandle('click');
const setClick3 = ReactDOM.unstable_createEventHandle('click');
const setClick4 = ReactDOM.unstable_createEventHandle('click');
function Test() {
React.useEffect(() => {
click1.setListener(window, rootListener1);
click2.setListener(window, rootListener2);
click3.setListener(window, rootListener3);
click4.setListener(window, rootListener4);
const clearClick1 = setClick1(window, rootListener1);
const clearClick2 = setClick2(window, rootListener2);
const clearClick3 = setClick3(window, rootListener3);
const clearClick4 = setClick4(window, rootListener4);
return () => {
click1.setListener(window, null);
click2.setListener(window, null);
click3.setListener(window, null);
click4.setListener(window, null);
clearClick1();
clearClick2();
clearClick3();
clearClick4();
};
});
@@ -2128,27 +2227,46 @@ describe('DOMModernPluginEventSystem', () => {
const onClickCapture = jest.fn(e =>
log.push(['capture', e.currentTarget]),
);
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle('click', {
capture: true,
});
const setClick = ReactDOM.unstable_createEventHandle('click');
const setClickCapture = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
},
);
function Test() {
React.useEffect(() => {
click.setListener(window, onClick);
clickCapture.setListener(window, onClickCapture);
click.setListener(document, onClick);
clickCapture.setListener(document, onClickCapture);
click.setListener(buttonRef.current, onClick);
clickCapture.setListener(buttonRef.current, onClickCapture);
click.setListener(divRef.current, onClick);
clickCapture.setListener(divRef.current, onClickCapture);
const clearClick1 = setClick(window, onClick);
const clearClickCapture1 = setClickCapture(
window,
onClickCapture,
);
const clearClick2 = setClick(document, onClick);
const clearClickCapture2 = setClickCapture(
document,
onClickCapture,
);
const clearClick3 = setClick(buttonRef.current, onClick);
const clearClickCapture3 = setClickCapture(
buttonRef.current,
onClickCapture,
);
const clearClick4 = setClick(divRef.current, onClick);
const clearClickCapture4 = setClickCapture(
divRef.current,
onClickCapture,
);
return () => {
click.setListener(window, null);
clickCapture.setListener(window, null);
click.setListener(document, null);
clickCapture.setListener(document, null);
clearClick1();
clearClickCapture1();
clearClick2();
clearClickCapture2();
clearClick3();
clearClickCapture3();
clearClick4();
clearClickCapture4();
};
});
@@ -2225,11 +2343,11 @@ describe('DOMModernPluginEventSystem', () => {
log.push(['capture', e.currentTarget]),
);
let customEventHandle;
let setCustomEventHandle;
// Test that we get a warning when we don't provide an explicit priority
expect(() => {
customEventHandle = ReactDOM.unstable_createEventHandle(
setCustomEventHandle = ReactDOM.unstable_createEventHandle(
'custom-event',
);
}).toWarnDev(
@@ -2238,14 +2356,14 @@ describe('DOMModernPluginEventSystem', () => {
{withoutStack: true},
);
customEventHandle = ReactDOM.unstable_createEventHandle(
setCustomEventHandle = ReactDOM.unstable_createEventHandle(
'custom-event',
{
priority: 0, // Discrete
},
);
const customCaptureHandle = ReactDOM.unstable_createEventHandle(
const setCustomCaptureHandle = ReactDOM.unstable_createEventHandle(
'custom-event',
{
capture: true,
@@ -2255,16 +2373,29 @@ describe('DOMModernPluginEventSystem', () => {
function Test() {
React.useEffect(() => {
customEventHandle.setListener(buttonRef.current, onCustomEvent);
customCaptureHandle.setListener(
const clearCustom1 = setCustomEventHandle(
buttonRef.current,
onCustomEvent,
);
const clearCustom2 = setCustomCaptureHandle(
buttonRef.current,
onCustomEventCapture,
);
customEventHandle.setListener(divRef.current, onCustomEvent);
customCaptureHandle.setListener(
const clearCustom3 = setCustomEventHandle(
divRef.current,
onCustomEvent,
);
const clearCustom4 = setCustomCaptureHandle(
divRef.current,
onCustomEventCapture,
);
return () => {
clearCustom1();
clearCustom2();
clearCustom3();
clearCustom4();
};
});
return (
@@ -2305,10 +2436,10 @@ describe('DOMModernPluginEventSystem', () => {
const onBeforeBlur = jest.fn(e => log.push(e.type));
const innerRef = React.createRef();
const innerRef2 = React.createRef();
const afterBlurHandle = ReactDOM.unstable_createEventHandle(
const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
'afterblur',
);
const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
'beforeblur',
);
@@ -2316,8 +2447,13 @@ describe('DOMModernPluginEventSystem', () => {
const ref = React.useRef(null);
React.useEffect(() => {
afterBlurHandle.setListener(document, onAfterBlur);
beforeBlurHandle.setListener(ref.current, onBeforeBlur);
const clear1 = setAfterBlurHandle(document, onAfterBlur);
const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
return () => {
clear1();
clear2();
};
});
return (
@@ -2359,10 +2495,10 @@ describe('DOMModernPluginEventSystem', () => {
const onBeforeBlur = jest.fn(e => log.push(e.type));
const innerRef = React.createRef();
const innerRef2 = React.createRef();
const afterBlurHandle = ReactDOM.unstable_createEventHandle(
const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
'afterblur',
);
const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
'beforeblur',
);
@@ -2370,8 +2506,13 @@ describe('DOMModernPluginEventSystem', () => {
const ref = React.useRef(null);
React.useEffect(() => {
afterBlurHandle.setListener(document, onAfterBlur);
beforeBlurHandle.setListener(ref.current, onBeforeBlur);
const clear1 = setAfterBlurHandle(document, onAfterBlur);
const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
return () => {
clear1();
clear2();
};
});
return (
@@ -2422,10 +2563,10 @@ describe('DOMModernPluginEventSystem', () => {
const promise = new Promise(
resolvePromise => (resolve = resolvePromise),
);
const afterBlurHandle = ReactDOM.unstable_createEventHandle(
const setAfterBlurHandle = ReactDOM.unstable_createEventHandle(
'afterblur',
);
const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
'beforeblur',
);
@@ -2441,8 +2582,13 @@ describe('DOMModernPluginEventSystem', () => {
const ref = React.useRef(null);
React.useEffect(() => {
afterBlurHandle.setListener(document, onAfterBlur);
beforeBlurHandle.setListener(ref.current, onBeforeBlur);
const clear1 = setAfterBlurHandle(document, onAfterBlur);
const clear2 = setBeforeBlurHandle(ref.current, onBeforeBlur);
return () => {
clear1();
clear2();
};
});
return (
@@ -2492,7 +2638,7 @@ describe('DOMModernPluginEventSystem', () => {
const Suspense = React.Suspense;
let suspend = false;
const promise = Promise.resolve();
const beforeBlurHandle = ReactDOM.unstable_createEventHandle(
const setBeforeBlurHandle = ReactDOM.unstable_createEventHandle(
'beforeblur',
);
const innerRef = React.createRef();
@@ -2509,7 +2655,7 @@ describe('DOMModernPluginEventSystem', () => {
const [, setState] = React.useState(0);
React.useEffect(() => {
beforeBlurHandle.setListener(ref.current, () => {
return setBeforeBlurHandle(ref.current, () => {
// In the regression case, this would trigger an update, then
// the resulting render would trigger another blur event,
// which would trigger an update again, and on and on in an
@@ -2573,8 +2719,8 @@ describe('DOMModernPluginEventSystem', () => {
log.push(['capture', e.currentTarget]),
);
const TestScope = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle(
const setClick = ReactDOM.unstable_createEventHandle('click');
const setClickCapture = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
@@ -2585,8 +2731,16 @@ describe('DOMModernPluginEventSystem', () => {
const scopeRef = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, onClick);
clickCapture.setListener(scopeRef.current, onClickCapture);
const clear1 = setClick(scopeRef.current, onClick);
const clear2 = setClickCapture(
scopeRef.current,
onClickCapture,
);
return () => {
clear1();
clear2();
};
});
return (
@@ -2622,8 +2776,8 @@ describe('DOMModernPluginEventSystem', () => {
log.push(['capture', e.currentTarget]),
);
const TestScope = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const clickCapture = ReactDOM.unstable_createEventHandle(
const setClick = ReactDOM.unstable_createEventHandle('click');
const setClickCapture = ReactDOM.unstable_createEventHandle(
'click',
{
capture: true,
@@ -2634,10 +2788,23 @@ describe('DOMModernPluginEventSystem', () => {
const scopeRef = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, onClick);
clickCapture.setListener(scopeRef.current, onClickCapture);
click.setListener(buttonRef.current, onClick);
clickCapture.setListener(buttonRef.current, onClickCapture);
const clear1 = setClick(scopeRef.current, onClick);
const clear2 = setClickCapture(
scopeRef.current,
onClickCapture,
);
const clear3 = setClick(buttonRef.current, onClick);
const clear4 = setClickCapture(
buttonRef.current,
onClickCapture,
);
return () => {
clear1();
clear2();
clear3();
clear4();
};
});
return (
@@ -2693,13 +2860,13 @@ describe('DOMModernPluginEventSystem', () => {
const clickEvent = jest.fn();
const buttonRef = React.createRef();
const TestScope = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
const scopeRef = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, clickEvent);
return setClick(scopeRef.current, clickEvent);
});
return (
@@ -2726,15 +2893,20 @@ describe('DOMModernPluginEventSystem', () => {
const innerOnClick = jest.fn(e => e.stopPropagation());
const TestScope = React.unstable_createScope();
const TestScope2 = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
const scopeRef = React.useRef(null);
const scope2Ref = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, outerOnClick);
click.setListener(scope2Ref.current, innerOnClick);
const clear1 = setClick(scopeRef.current, outerOnClick);
const clear2 = setClick(scope2Ref.current, innerOnClick);
return () => {
clear1();
clear2();
};
});
return (
@@ -2763,15 +2935,20 @@ describe('DOMModernPluginEventSystem', () => {
const innerOnClick = jest.fn();
const TestScope = React.unstable_createScope();
const TestScope2 = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
const scopeRef = React.useRef(null);
const scope2Ref = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, outerOnClick);
click.setListener(scope2Ref.current, innerOnClick);
const clear1 = setClick(scopeRef.current, outerOnClick);
const clear2 = setClick(scope2Ref.current, innerOnClick);
return () => {
clear1();
clear2();
};
});
return (
@@ -2799,15 +2976,20 @@ describe('DOMModernPluginEventSystem', () => {
const onClick = jest.fn(e => e.stopPropagation());
const TestScope = React.unstable_createScope();
const TestScope2 = React.unstable_createScope();
const click = ReactDOM.unstable_createEventHandle('click');
const setClick = ReactDOM.unstable_createEventHandle('click');
function Test() {
const scopeRef = React.useRef(null);
const scope2Ref = React.useRef(null);
React.useEffect(() => {
click.setListener(scopeRef.current, onClick);
click.setListener(scope2Ref.current, onClick);
const clear1 = setClick(scopeRef.current, onClick);
const clear2 = setClick(scope2Ref.current, onClick);
return () => {
clear1();
clear2();
};
});
return (

View File

@@ -78,17 +78,13 @@ export type ReactDOMResponderContext = {
...
};
export type ReactDOMEventHandle = {|
setListener(
target: EventTarget | ReactScopeInstance,
callback: (SyntheticEvent<EventTarget>) => void,
): void,
clear(): void,
|};
export type ReactDOMEventHandle = (
target: EventTarget | ReactScopeInstance,
callback: (SyntheticEvent<EventTarget>) => void,
) => () => void;
export type ReactDOMEventHandleListener = {|
callback: (SyntheticEvent<EventTarget>) => void,
capture: boolean,
destroy: (target: EventTarget | ReactScopeInstance) => void,
type: DOMTopLevelEventType,
|};

View File

@@ -10,7 +10,7 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
const {useEffect, useRef} = React;
const {useLayoutEffect, useRef} = React;
const {unstable_createEventHandle: createEventHandle} = ReactDOM;
type UseEventHandle = {|
@@ -30,20 +30,48 @@ export default function useEvent(
|},
): UseEventHandle {
const handleRef = useRef(null);
let setListener;
let clears;
let useEventHandle;
if (handleRef.current == null) {
handleRef.current = createEventHandle(event, options);
setListener = createEventHandle(event, options);
clears = new Map();
useEventHandle = {
setListener(
target: EventTarget,
callback: null | ((SyntheticEvent<EventTarget>) => void),
): void {
let clear = clears.get(target);
if (clear !== undefined) {
clear();
}
if (callback === null) {
clears.delete(target);
return;
}
clear = setListener(target, callback);
clears.set(target, clear);
},
clear(): void {
const clearsArr = Array.from(clears.values());
for (let i = 0; i < clearsArr.length; i++) {
clearsArr[i]();
}
clears.clear();
},
};
handleRef.current = {setListener, clears, useEventHandle};
} else {
({setListener, clears, useEventHandle} = handleRef.current);
}
useEffect(() => {
const handle = handleRef.current;
useLayoutEffect(() => {
return () => {
if (handle !== null) {
handle.clear();
}
useEventHandle.clear();
handleRef.current = null;
};
}, []);
return ((handleRef.current: any): UseEventHandle);
return useEventHandle;
}

View File

@@ -479,10 +479,6 @@ export function getInstanceFromNode(node: any) {
throw new Error('Not yet implemented.');
}
export function removeInstanceEventHandles(instance: any) {
// noop
}
export function isOpaqueHydratingObject(value: mixed): boolean {
throw new Error('Not yet implemented');
}

View File

@@ -532,10 +532,6 @@ export function getInstanceFromNode(node: any) {
throw new Error('Not yet implemented.');
}
export function removeInstanceEventHandles(instance: any) {
// noop
}
export function isOpaqueHydratingObject(value: mixed): boolean {
throw new Error('Not yet implemented');
}

View File

@@ -438,10 +438,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
throw new Error('Not yet implemented.');
},
removeInstanceEventHandles(instance: any): void {
// NO-OP
},
beforeActiveInstanceBlur() {
// NO-OP
},
@@ -456,8 +452,6 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
prepareScopeUpdate() {},
removeScopeEventHandles() {},
getInstanceFromScope() {
throw new Error('Not yet implemented.');
},

View File

@@ -35,7 +35,6 @@ import {
enableFundamentalAPI,
enableSuspenseCallback,
enableScopeAPI,
enableCreateEventHandleAPI,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
@@ -109,10 +108,8 @@ import {
updateFundamentalComponent,
commitHydratedContainer,
commitHydratedSuspenseInstance,
removeInstanceEventHandles,
clearContainer,
prepareScopeUpdate,
removeScopeEventHandles,
} from './ReactFiberHostConfig';
import {
captureCommitPhaseError,
@@ -928,9 +925,6 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
if (enableCreateEventHandleAPI && current.ref !== null) {
removeInstanceEventHandles(current.stateNode);
}
safelyDetachRef(current);
return;
}
@@ -972,10 +966,6 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
const scopeInstance = current.stateNode;
if (enableCreateEventHandleAPI && current.ref !== null) {
removeScopeEventHandles(scopeInstance);
}
safelyDetachRef(current);
}
return;

View File

@@ -35,7 +35,6 @@ import {
enableFundamentalAPI,
enableSuspenseCallback,
enableScopeAPI,
enableCreateEventHandleAPI,
} from 'shared/ReactFeatureFlags';
import {
FunctionComponent,
@@ -109,10 +108,8 @@ import {
updateFundamentalComponent,
commitHydratedContainer,
commitHydratedSuspenseInstance,
removeInstanceEventHandles,
clearContainer,
prepareScopeUpdate,
removeScopeEventHandles,
} from './ReactFiberHostConfig';
import {
captureCommitPhaseError,
@@ -928,9 +925,6 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
if (enableCreateEventHandleAPI && current.ref !== null) {
removeInstanceEventHandles(current.stateNode);
}
safelyDetachRef(current);
return;
}
@@ -972,10 +966,6 @@ function commitUnmount(
if (enableDeprecatedFlareAPI) {
unmountDeprecatedResponderListeners(current);
}
const scopeInstance = current.stateNode;
if (enableCreateEventHandleAPI && current.ref !== null) {
removeScopeEventHandles(scopeInstance);
}
safelyDetachRef(current);
}
return;

View File

@@ -23,5 +23,4 @@ function shim(...args: any) {
// React Scopes (when unsupported)
export const prepareScopeUpdate = shim;
export const removeScopeEventHandles = shim;
export const getInstanceFromScope = shim;

View File

@@ -72,8 +72,6 @@ export const mountFundamentalComponent =
export const shouldUpdateFundamentalComponent =
$$$hostConfig.shouldUpdateFundamentalComponent;
export const getInstanceFromNode = $$$hostConfig.getInstanceFromNode;
export const removeInstanceEventHandles =
$$$hostConfig.removeInstanceEventHandles;
export const isOpaqueHydratingObject = $$$hostConfig.isOpaqueHydratingObject;
export const makeOpaqueHydratingObject =
$$$hostConfig.makeOpaqueHydratingObject;
@@ -83,7 +81,6 @@ export const beforeActiveInstanceBlur = $$$hostConfig.beforeActiveInstanceBlur;
export const afterActiveInstanceBlur = $$$hostConfig.afterActiveInstanceBlur;
export const preparePortalMount = $$$hostConfig.preparePortalMount;
export const prepareScopeUpdate = $$$hostConfig.preparePortalMount;
export const removeScopeEventHandles = $$$hostConfig.removeScopeEventHandles;
export const getInstanceFromScope = $$$hostConfig.getInstanceFromScope;
// -------------------

View File

@@ -383,10 +383,6 @@ export function getInstanceFromNode(mockNode: Object) {
return null;
}
export function removeInstanceEventHandles(instance: any) {
// noop
}
let clientId: number = 0;
export function makeClientId(): OpaqueIDType {
return 'c_' + (clientId++).toString(36);
@@ -440,10 +436,6 @@ export function prepareScopeUpdate(scopeInstance: Object, inst: Object): void {
nodeToInstanceMap.set(scopeInstance, inst);
}
export function removeScopeEventHandles(scopeInstance: Object): void {
nodeToInstanceMap.delete(scopeInstance);
}
export function getInstanceFromScope(scopeInstance: Object): null | Object {
return nodeToInstanceMap.get(scopeInstance) || null;
}

View File

@@ -1,4 +1,3 @@
{
"0": "React.addons.createFragment(...): Encountered an invalid child; DOM elements are not valid children of React components.",
"1": "update(): expected target of %s to be an array; got %s.",
@@ -366,5 +365,7 @@
"365": "Invalid selector type %s specified.",
"366": "ReactDOM.createEventHandle: setListener called on an target that did not have a corresponding root. This is likely a bug in React.",
"367": "ReactDOM.createEventHandle: setListener called on an element target that is not managed by React. Ensure React rendered the DOM element.",
"368": "ReactDOM.createEventHandle: setListener called on an invalid target. Provide a valid EventTarget or an element managed by React."
"368": "ReactDOM.createEventHandle: setListener called on an invalid target. Provide a valid EventTarget or an element managed by React.",
"369": "ReactDOM.createEventHandle: setter called on an invalid target. Provide a valid EventTarget or an element managed by React.",
"370": "ReactDOM.createEventHandle: setter called with an invalid callback. The callback must be a function."
}