Skip to content

Mirror of facebook react#17651 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: pr17651-dst
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions packages/legacy-events/EventSystemFlags.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export type EventSystemFlags = number;

export const PLUGIN_EVENT_SYSTEM = 1;
export const RESPONDER_EVENT_SYSTEM = 1 << 1;
export const IS_PASSIVE = 1 << 2;
export const IS_ACTIVE = 1 << 3;
export const PASSIVE_NOT_SUPPORTED = 1 << 4;
export const IS_REPLAYED = 1 << 5;
export const IS_FIRST_ANCESTOR = 1 << 6;
export const LISTENER_EVENT_SYSTEM = 1 << 2;
export const IS_PASSIVE = 1 << 3;
export const IS_ACTIVE = 1 << 4;
export const PASSIVE_NOT_SUPPORTED = 1 << 5;
export const IS_REPLAYED = 1 << 6;
export const IS_FIRST_ANCESTOR = 1 << 7;
2 changes: 1 addition & 1 deletion packages/legacy-events/PluginModuleType.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';

export type EventTypes = {[key: string]: DispatchConfig};

export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | Touch;
export type AnyNativeEvent = Event | KeyboardEvent | MouseEvent | TouchEvent;

export type PluginName = string;

Expand Down
7 changes: 5 additions & 2 deletions packages/legacy-events/ReactGenericBatching.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ import {
restoreStateIfNeeded,
} from './ReactControlledComponent';

import {enableDeprecatedFlareAPI} from 'shared/ReactFeatureFlags';
import {
enableDeprecatedFlareAPI,
enableListenerAPI,
} from 'shared/ReactFeatureFlags';
import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';

// Used as a way to call batchedUpdates when we don't have a reference to
Expand Down Expand Up @@ -118,7 +121,7 @@ export function flushDiscreteUpdatesIfNeeded(timeStamp: number) {
// behaviour as we had before this change, so the risks are low.
if (
!isInsideEventHandler &&
(!enableDeprecatedFlareAPI ||
((!enableDeprecatedFlareAPI && !enableListenerAPI) ||
(timeStamp === 0 || lastFlushedEventTimeStamp !== timeStamp))
) {
lastFlushedEventTimeStamp = timeStamp;
Expand Down
23 changes: 23 additions & 0 deletions packages/react-art/src/ReactARTHostConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -469,3 +469,26 @@ export function getInstanceFromNode(node) {
export function beforeRemoveInstance(instance) {
// noop
}

export function registerListenerEvent(
event: any,
rootContainerInstance: Container,
): void {
// noop
}

export function attachListenerToInstance(listener: any): any {
// noop
}

export function detachListenerFromInstance(listener: any): any {
// noop
}

export function validateReactListenerDeleteListener(instance): void {
// noop
}

export function validateReactListenerMapListener(instance, listener): void {
// noop
}
12 changes: 12 additions & 0 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,17 @@ function useResponder(
};
}

const noOp = () => {};

function useEvent(options: any): any {
hookLog.push({primitive: 'Event', stackError: new Error(), value: options});
return {
clear: noOp,
listen: noOp,
unlisten: noOp,
};
}

function useTransition(
config: SuspenseConfig | null | void,
): [(() => void) => void, boolean] {
Expand Down Expand Up @@ -275,6 +286,7 @@ const Dispatcher: DispatcherType = {
useResponder,
useTransition,
useDeferredValue,
useEvent,
};

// Inspect
Expand Down
10 changes: 9 additions & 1 deletion packages/react-dom/src/client/ReactDOM.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ import {
} from 'legacy-events/EventPropagators';
import ReactVersion from 'shared/ReactVersion';
import invariant from 'shared/invariant';
import {exposeConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
import {
exposeConcurrentModeAPIs,
enableListenerAPI,
} from 'shared/ReactFeatureFlags';

import {
getInstanceFromNode,
Expand All @@ -70,6 +73,7 @@ import {
setAttemptHydrationAtCurrentPriority,
queueExplicitHydrationTarget,
} from '../events/ReactDOMEventReplaying';
import {useEvent} from './ReactDOMEventListenerHooks';

setAttemptSynchronousHydration(attemptSynchronousHydration);
setAttemptUserBlockingHydration(attemptUserBlockingHydration);
Expand Down Expand Up @@ -193,6 +197,10 @@ if (exposeConcurrentModeAPIs) {
};
}

if (enableListenerAPI) {
ReactDOM.unstable_useEvent = useEvent;
}

const foundDevTools = injectIntoDevTools({
findFiberByHostInstance: getClosestInstanceFromNode,
bundleType: __DEV__ ? 1 : 0,
Expand Down
39 changes: 39 additions & 0 deletions packages/react-dom/src/client/ReactDOMComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ import {
import {
addResponderEventSystemEvent,
removeActiveResponderEventSystemEvent,
addListenerSystemEvent,
removeListenerSystemEvent,
} from '../events/ReactDOMEventListener.js';
import {mediaEventTypes} from '../events/DOMTopLevelEventTypes';
import {
Expand All @@ -90,6 +92,7 @@ import {toStringOrTrustedType} from './ToStringValue';
import {
enableDeprecatedFlareAPI,
enableTrustedTypesIntegration,
enableListenerAPI,
} from 'shared/ReactFeatureFlags';

let didWarnInvalidHydration = false;
Expand Down Expand Up @@ -1345,6 +1348,42 @@ export function listenToEventResponderEventTypes(
}
}

export function listenToEventListener(
type: string,
passive: boolean,
document: Document,
): void {
if (enableListenerAPI) {
// Get the listening Map for this element. We use this to track
// what events we're listening to.
const listenerMap = getListenerMapForElement(document);
const passiveKey = type + '_passive';
const activeKey = type + '_active';
const eventKey = passive ? passiveKey : activeKey;

if (!listenerMap.has(eventKey)) {
if (passive) {
if (listenerMap.has(activeKey)) {
// If we have an active event listener, do not register
// a passive event listener. We use the same active event
// listener.
return;
} else {
// If we have a passive event listener, remove the
// existing passive event listener before we add the
// active event listener.
const passiveListener = listenerMap.get(passiveKey);
if (passiveListener != null) {
removeListenerSystemEvent(document, type, passiveListener);
}
}
}
const eventListener = addListenerSystemEvent(document, type, passive);
listenerMap.set(eventKey, eventListener);
}
}
}

// We can remove this once the event API is stable and out of a flag
if (enableDeprecatedFlareAPI) {
setListenToResponderEventTypes(listenToEventResponderEventTypes);
Expand Down
9 changes: 9 additions & 0 deletions packages/react-dom/src/client/ReactDOMComponentTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const randomKey = Math.random()
.slice(2);
const internalInstanceKey = '__reactInternalInstance$' + randomKey;
const internalEventHandlersKey = '__reactEventHandlers$' + randomKey;
const internalEventListenersKey = '__reactEventListeners$' + randomKey;
const internalContainerInstanceKey = '__reactContainere$' + randomKey;

export function precacheFiberNode(hostInst, node) {
Expand Down Expand Up @@ -164,3 +165,11 @@ export function getFiberCurrentPropsFromNode(node) {
export function updateFiberProps(node, props) {
node[internalEventHandlersKey] = props;
}

export function getListenersFromNode(node) {
return node[internalEventListenersKey] || null;
}

export function initListenersSet(node, value) {
node[internalEventListenersKey] = value;
}
74 changes: 74 additions & 0 deletions packages/react-dom/src/client/ReactDOMEventListenerHooks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {
ReactDOMListenerEvent,
ReactDOMListenerMap,
} from 'shared/ReactDOMTypes';

import React from 'react';
import invariant from 'shared/invariant';
import {getEventPriority} from '../events/SimpleEventPlugin';

const ReactCurrentDispatcher =
React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
.ReactCurrentDispatcher;

type EventOptions = {|
capture?: boolean,
passive?: boolean,
priority?: number,
|};

function resolveDispatcher() {
const dispatcher = ReactCurrentDispatcher.current;
invariant(
dispatcher !== null,
'Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for' +
' one of the following reasons:\n' +
'1. You might have mismatching versions of React and the renderer (such as React DOM)\n' +
'2. You might be breaking the Rules of Hooks\n' +
'3. You might have more than one copy of React in the same app\n' +
'See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.',
);
return dispatcher;
}

export function useEvent(
type: string,
options?: EventOptions,
): ReactDOMListenerMap {
const dispatcher = resolveDispatcher();
let capture = false;
let passive = false;
let priority = getEventPriority((type: any));

if (options != null) {
const optionsCapture = options && options.capture;
const optionsPassive = options && options.passive;
const optionsPriority = options && options.priority;

if (typeof optionsCapture === 'boolean') {
capture = optionsCapture;
}
if (typeof optionsPassive === 'boolean') {
passive = optionsPassive;
}
if (typeof optionsPriority === 'number') {
priority = optionsPriority;
}
}
const event: ReactDOMListenerEvent = {
capture,
passive,
priority,
type,
};
return dispatcher.useEvent(event);
}
Loading