mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Experimental event API: Support EventComponent onUnmount responder callback (#15335)
This commit is contained in:
@@ -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.');
|
||||
|
||||
+12
-1
@@ -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,
|
||||
|
||||
+35
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = () => (
|
||||
<EventComponent>
|
||||
<button />
|
||||
</EventComponent>
|
||||
);
|
||||
|
||||
ReactDOM.render(<Test />, container);
|
||||
ReactDOM.render(null, container);
|
||||
expect(onUnmountFired).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
Vendored
+1
-1
@@ -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);
|
||||
},
|
||||
|
||||
@@ -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.');
|
||||
|
||||
@@ -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,
|
||||
|
||||
+5
-1
@@ -431,7 +431,11 @@ function createReactNoop(reconciler: Function, useMutation: boolean) {
|
||||
isPrimaryRenderer: true,
|
||||
supportsHydration: false,
|
||||
|
||||
handleEventComponent() {
|
||||
handleEventComponent(): void {
|
||||
// NO-OP
|
||||
},
|
||||
|
||||
unmountEventComponent(): void {
|
||||
// NO-OP
|
||||
},
|
||||
|
||||
|
||||
+1
@@ -626,6 +626,7 @@ export function createFiberFromEventComponent(
|
||||
fiber.stateNode = {
|
||||
context: null,
|
||||
props: pendingProps,
|
||||
rootInstance: null,
|
||||
state: null,
|
||||
};
|
||||
fiber.expirationTime = expirationTime;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+8
-1
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user