Skip to content

Commit

Permalink
Experimental event API: Support EventComponent onUnmount responder ca…
Browse files Browse the repository at this point in the history
…llback (#15335)
  • Loading branch information
trueadm authored Apr 6, 2019
1 parent 4fbbae8 commit 4064ea9
Show file tree
Hide file tree
Showing 13 changed files with 122 additions and 8 deletions.
7 changes: 7 additions & 0 deletions packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down
13 changes: 12 additions & 1 deletion packages/react-dom/src/client/ReactDOMHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -890,7 +891,6 @@ export function didNotFindHydratableSuspenseInstance(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
internalInstanceHandle: Object,
): void {
if (enableEventAPI) {
const rootElement = rootContainerInstance.ownerDocument;
Expand All @@ -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,
Expand Down
36 changes: 35 additions & 1 deletion packages/react-dom/src/events/DOMEventResponderSystem.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 = () => (
<EventComponent>
<button />
</EventComponent>
);

ReactDOM.render(<Test />, container);
ReactDOM.render(null, container);
expect(onUnmountFired).toEqual(1);
});
});
2 changes: 1 addition & 1 deletion packages/react-events/src/Press.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
},
Expand Down
7 changes: 7 additions & 0 deletions packages/react-native-renderer/src/ReactFabricHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down
9 changes: 8 additions & 1 deletion packages/react-native-renderer/src/ReactNativeHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 5 additions & 1 deletion packages/react-noop-renderer/src/createReactNoop.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
isPrimaryRenderer: true,
supportsHydration: false,

handleEventComponent() {
handleEventComponent(): void {
// NO-OP
},

unmountEventComponent(): void {
// NO-OP
},

Expand Down
1 change: 1 addition & 0 deletions packages/react-reconciler/src/ReactFiber.js
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ export function createFiberFromEventComponent(
fiber.stateNode = {
context: null,
props: pendingProps,
rootInstance: null,
state: null,
};
fiber.expirationTime = expirationTime;
Expand Down
10 changes: 10 additions & 0 deletions packages/react-reconciler/src/ReactFiberCommitWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
IncompleteClassComponent,
MemoComponent,
SimpleMemoComponent,
EventComponent,
EventTarget,
} from 'shared/ReactWorkTags';
import {
Expand Down Expand Up @@ -92,6 +93,7 @@ import {
hideTextInstance,
unhideInstance,
unhideTextInstance,
unmountEventComponent,
commitEventTarget,
} from './ReactFiberHostConfig';
import {
Expand Down Expand Up @@ -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;
}
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion packages/react-reconciler/src/ReactFiberCompleteWork.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
9 changes: 8 additions & 1 deletion packages/react-test-renderer/src/ReactTestHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,6 @@ export function unhideTextInstance(
export function handleEventComponent(
eventResponder: ReactEventResponder,
rootContainerInstance: Container,
internalInstanceHandle: Object,
): void {
// noop
}
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 4064ea9

Please sign in to comment.