From 4064ea9fa6387c92a985b52bfc66746f81ccd4fd Mon Sep 17 00:00:00 2001 From: Dominic Gannaway Date: Sat, 6 Apr 2019 08:16:57 +0100 Subject: [PATCH] Experimental event API: Support EventComponent onUnmount responder callback (#15335) --- packages/react-art/src/ReactARTHostConfig.js | 7 ++++ .../src/client/ReactDOMHostConfig.js | 13 ++++++- .../src/events/DOMEventResponderSystem.js | 36 ++++++++++++++++++- .../DOMEventResponderSystem-test.internal.js | 25 ++++++++++++- packages/react-events/src/Press.js | 2 +- .../src/ReactFabricHostConfig.js | 7 ++++ .../src/ReactNativeHostConfig.js | 9 ++++- .../src/createReactNoop.js | 6 +++- packages/react-reconciler/src/ReactFiber.js | 1 + .../src/ReactFiberCommitWork.js | 10 ++++++ .../src/ReactFiberCompleteWork.js | 4 ++- .../src/forks/ReactFiberHostConfig.custom.js | 1 + .../src/ReactTestHostConfig.js | 9 ++++- 13 files changed, 122 insertions(+), 8 deletions(-) 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 = () => ( + +