diff --git a/packages/react-art/src/ReactARTHostConfig.js b/packages/react-art/src/ReactARTHostConfig.js
index ef5b5914bfd34..0ab7413763b71 100644
--- a/packages/react-art/src/ReactARTHostConfig.js
+++ b/packages/react-art/src/ReactARTHostConfig.js
@@ -442,6 +442,13 @@ export function unhideTextInstance(textInstance, text): void {
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
+) {
+ throw new Error('Not yet implemented.');
+}
+
+export function unmountEventComponent(
+ eventResponder: ReactEventResponder,
+ rootContainerInstance: Container,
internalInstanceHandle: Object,
): void {
throw new Error('Not yet implemented.');
diff --git a/packages/react-dom/src/client/ReactDOMHostConfig.js b/packages/react-dom/src/client/ReactDOMHostConfig.js
index 27b00f7da913b..4d95d9c24cd9d 100644
--- a/packages/react-dom/src/client/ReactDOMHostConfig.js
+++ b/packages/react-dom/src/client/ReactDOMHostConfig.js
@@ -45,6 +45,7 @@ import dangerousStyleValue from '../shared/dangerousStyleValue';
import type {DOMContainer} from './ReactDOM';
import type {ReactEventResponder} from 'shared/ReactTypes';
+import {unmountEventResponder} from '../events/DOMEventResponderSystem';
import {REACT_EVENT_TARGET_TOUCH_HIT} from 'shared/ReactSymbols';
import {canUseDOM} from 'shared/ExecutionEnvironment';
@@ -890,7 +891,6 @@ export function didNotFindHydratableSuspenseInstance(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
- internalInstanceHandle: Object,
): void {
if (enableEventAPI) {
const rootElement = rootContainerInstance.ownerDocument;
@@ -901,6 +901,17 @@ export function handleEventComponent(
}
}
+export function unmountEventComponent(
+ eventResponder: ReactEventResponder,
+ rootContainerInstance: Container,
+ internalInstanceHandle: Object,
+): void {
+ if (enableEventAPI) {
+ // TODO stop listening to targetEventTypes
+ unmountEventResponder(eventResponder, internalInstanceHandle);
+ }
+}
+
export function getEventTargetChildElement(
type: Symbol | number,
props: Props,
diff --git a/packages/react-dom/src/events/DOMEventResponderSystem.js b/packages/react-dom/src/events/DOMEventResponderSystem.js
index 5f12b84a1c5dc..8be163f1048cc 100644
--- a/packages/react-dom/src/events/DOMEventResponderSystem.js
+++ b/packages/react-dom/src/events/DOMEventResponderSystem.js
@@ -364,10 +364,17 @@ function handleTopLevelType(
if (state === null && responder.createInitialState !== undefined) {
state = fiber.stateNode.state = responder.createInitialState(props);
}
+ const previousFiber = currentFiber;
+ const previousResponder = currentResponder;
currentFiber = fiber;
currentResponder = responder;
- responder.onEvent(responderEvent, eventResponderContext, props, state);
+ try {
+ responder.onEvent(responderEvent, eventResponderContext, props, state);
+ } finally {
+ currentFiber = previousFiber;
+ currentResponder = previousResponder;
+ }
}
export function runResponderEventsInBatch(
@@ -413,3 +420,30 @@ export function runResponderEventsInBatch(
processEventQueue();
}
}
+
+export function unmountEventResponder(
+ responder: ReactEventResponder,
+ fiber: Fiber,
+): void {
+ const onUnmount = responder.onUnmount;
+ if (onUnmount !== undefined) {
+ let {props, state} = fiber.stateNode;
+ const previousEventQueue = currentEventQueue;
+ const previousFiber = currentFiber;
+ const previousResponder = currentResponder;
+ currentEventQueue = createEventQueue();
+ currentFiber = fiber;
+ currentResponder = responder;
+ try {
+ onUnmount(eventResponderContext, props, state);
+ } finally {
+ currentEventQueue = previousEventQueue;
+ currentFiber = previousFiber;
+ currentResponder = previousResponder;
+ }
+ }
+ if (currentOwner === fiber) {
+ // TODO fire owner changed callback
+ currentOwner = null;
+ }
+}
diff --git a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
index c304781341d66..2692ff994b250 100644
--- a/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
+++ b/packages/react-dom/src/events/__tests__/DOMEventResponderSystem-test.internal.js
@@ -13,10 +13,11 @@ let React;
let ReactFeatureFlags;
let ReactDOM;
-function createReactEventComponent(targetEventTypes, onEvent) {
+function createReactEventComponent(targetEventTypes, onEvent, onUnmount) {
const testEventResponder = {
targetEventTypes,
onEvent,
+ onUnmount,
};
return {
@@ -316,4 +317,26 @@ describe('DOMEventResponderSystem', () => {
expect(eventLog).toEqual(['press', 'longpress', 'longpresschange']);
});
+
+ it('the event responder onUnmount() function should fire', () => {
+ let onUnmountFired = 0;
+
+ const EventComponent = createReactEventComponent(
+ [],
+ (event, context, props) => {},
+ () => {
+ onUnmountFired++;
+ },
+ );
+
+ const Test = () => (
+
+
+
+ );
+
+ ReactDOM.render(, container);
+ ReactDOM.render(null, container);
+ expect(onUnmountFired).toEqual(1);
+ });
});
diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js
index 2615826b16c45..00db07c72c423 100644
--- a/packages/react-events/src/Press.js
+++ b/packages/react-events/src/Press.js
@@ -209,6 +209,7 @@ function unmountResponder(
): void {
if (state.isPressed) {
state.isPressed = false;
+ context.removeRootEventTypes(rootEventTypes);
dispatchPressEndEvents(context, props, state);
if (state.longPressTimeout !== null) {
clearTimeout(state.longPressTimeout);
@@ -429,7 +430,6 @@ const PressResponder = {
}
}
},
- // TODO This method doesn't work as of yet
onUnmount(context: ResponderContext, props: PressProps, state: PressState) {
unmountResponder(context, props, state);
},
diff --git a/packages/react-native-renderer/src/ReactFabricHostConfig.js b/packages/react-native-renderer/src/ReactFabricHostConfig.js
index 5f55444bff482..2f86f65f117e1 100644
--- a/packages/react-native-renderer/src/ReactFabricHostConfig.js
+++ b/packages/react-native-renderer/src/ReactFabricHostConfig.js
@@ -437,6 +437,13 @@ export function replaceContainerChildren(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
+) {
+ throw new Error('Not yet implemented.');
+}
+
+export function unmountEventComponent(
+ eventResponder: ReactEventResponder,
+ rootContainerInstance: Container,
internalInstanceHandle: Object,
): void {
throw new Error('Not yet implemented.');
diff --git a/packages/react-native-renderer/src/ReactNativeHostConfig.js b/packages/react-native-renderer/src/ReactNativeHostConfig.js
index 55fe1ba868b0e..20301fa0177b5 100644
--- a/packages/react-native-renderer/src/ReactNativeHostConfig.js
+++ b/packages/react-native-renderer/src/ReactNativeHostConfig.js
@@ -496,11 +496,18 @@ export function unhideTextInstance(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
- internalInstanceHandle: Object,
) {
throw new Error('Not yet implemented.');
}
+export function unmountEventComponent(
+ eventResponder: ReactEventResponder,
+ rootContainerInstance: Container,
+ internalInstanceHandle: Object,
+): void {
+ throw new Error('Not yet implemented.');
+}
+
export function getEventTargetChildElement(
type: Symbol | number,
props: Props,
diff --git a/packages/react-noop-renderer/src/createReactNoop.js b/packages/react-noop-renderer/src/createReactNoop.js
index de42992a6bb2d..5f09f4b25c197 100644
--- a/packages/react-noop-renderer/src/createReactNoop.js
+++ b/packages/react-noop-renderer/src/createReactNoop.js
@@ -431,7 +431,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
isPrimaryRenderer: true,
supportsHydration: false,
- handleEventComponent() {
+ handleEventComponent(): void {
+ // NO-OP
+ },
+
+ unmountEventComponent(): void {
// NO-OP
},
diff --git a/packages/react-reconciler/src/ReactFiber.js b/packages/react-reconciler/src/ReactFiber.js
index 4448f129fbff1..0528adf98f02b 100644
--- a/packages/react-reconciler/src/ReactFiber.js
+++ b/packages/react-reconciler/src/ReactFiber.js
@@ -626,6 +626,7 @@ export function createFiberFromEventComponent(
fiber.stateNode = {
context: null,
props: pendingProps,
+ rootInstance: null,
state: null,
};
fiber.expirationTime = expirationTime;
diff --git a/packages/react-reconciler/src/ReactFiberCommitWork.js b/packages/react-reconciler/src/ReactFiberCommitWork.js
index dfba03702199d..740e76509503a 100644
--- a/packages/react-reconciler/src/ReactFiberCommitWork.js
+++ b/packages/react-reconciler/src/ReactFiberCommitWork.js
@@ -44,6 +44,7 @@ import {
IncompleteClassComponent,
MemoComponent,
SimpleMemoComponent,
+ EventComponent,
EventTarget,
} from 'shared/ReactWorkTags';
import {
@@ -92,6 +93,7 @@ import {
hideTextInstance,
unhideInstance,
unhideTextInstance,
+ unmountEventComponent,
commitEventTarget,
} from './ReactFiberHostConfig';
import {
@@ -745,6 +747,14 @@ function commitUnmount(current: Fiber): void {
}
return;
}
+ case EventComponent: {
+ if (enableEventAPI) {
+ const rootContainerInstance = current.stateNode.rootInstance;
+ const responder = current.type.responder;
+ unmountEventComponent(responder, rootContainerInstance, current);
+ current.stateNode = null;
+ }
+ }
}
}
diff --git a/packages/react-reconciler/src/ReactFiberCompleteWork.js b/packages/react-reconciler/src/ReactFiberCompleteWork.js
index 457a6b7289a30..c3e1ed0eeb701 100644
--- a/packages/react-reconciler/src/ReactFiberCompleteWork.js
+++ b/packages/react-reconciler/src/ReactFiberCompleteWork.js
@@ -776,7 +776,9 @@ function completeWork(
const responder = workInProgress.type.responder;
// Update the props on the event component state node
workInProgress.stateNode.props = newProps;
- handleEventComponent(responder, rootContainerInstance, workInProgress);
+ // Update the root container, so we can properly unmount events at some point
+ workInProgress.stateNode.rootInstance = rootContainerInstance;
+ handleEventComponent(responder, rootContainerInstance);
}
break;
}
diff --git a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
index 9a571b168f4bc..87dc772b3d91b 100644
--- a/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
+++ b/packages/react-reconciler/src/forks/ReactFiberHostConfig.custom.js
@@ -86,6 +86,7 @@ export const hideInstance = $$$hostConfig.hideInstance;
export const hideTextInstance = $$$hostConfig.hideTextInstance;
export const unhideInstance = $$$hostConfig.unhideInstance;
export const unhideTextInstance = $$$hostConfig.unhideTextInstance;
+export const unmountEventComponent = $$$hostConfig.unmountEventComponent;
export const commitTouchHitTargetUpdate =
$$$hostConfig.commitTouchHitTargetUpdate;
export const commitEventTarget = $$$hostConfig.commitEventTarget;
diff --git a/packages/react-test-renderer/src/ReactTestHostConfig.js b/packages/react-test-renderer/src/ReactTestHostConfig.js
index cb9dceec361b8..6ec3c0b88256a 100644
--- a/packages/react-test-renderer/src/ReactTestHostConfig.js
+++ b/packages/react-test-renderer/src/ReactTestHostConfig.js
@@ -330,7 +330,6 @@ export function unhideTextInstance(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
- internalInstanceHandle: Object,
): void {
// noop
}
@@ -364,6 +363,14 @@ export function getEventTargetChildElement(
return null;
}
+export function unmountEventComponent(
+ eventResponder: ReactEventResponder,
+ rootContainerInstance: Container,
+ internalInstanceHandle: Object,
+): void {
+ // TODO: add unmountEventComponent implementation
+}
+
export function handleEventTarget(
type: Symbol | number,
props: Props,