diff --git a/packages/react-dom/src/client/ReactDOM.js b/packages/react-dom/src/client/ReactDOM.js index 4056f0a2a9be1..0234b77a865ab 100644 --- a/packages/react-dom/src/client/ReactDOM.js +++ b/packages/react-dom/src/client/ReactDOM.js @@ -20,6 +20,7 @@ import { unmountComponentAtNode, } from './ReactDOMLegacy'; import {createRoot, createBlockingRoot, isValidContainer} from './ReactDOMRoot'; +import {useEvent} from './ReactDOMUseEvent'; import { batchedEventUpdates, @@ -218,6 +219,8 @@ export { // Temporary alias since we already shipped React 16 RC with it. // TODO: remove in React 17. unstable_createPortal, + // enableUseEventAPI + useEvent as unstable_useEvent, }; const foundDevTools = injectIntoDevTools({ diff --git a/packages/react-dom/src/client/ReactDOMUseEvent.js b/packages/react-dom/src/client/ReactDOMUseEvent.js new file mode 100644 index 0000000000000..6eb2c1dff2930 --- /dev/null +++ b/packages/react-dom/src/client/ReactDOMUseEvent.js @@ -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 {EventPriority} from 'shared/ReactTypes'; +import type { + ReactDOMListenerEvent, + ReactDOMListenerMap, +} from 'shared/ReactDOMTypes'; + +import ReactSharedInternals from 'shared/ReactSharedInternals'; +import invariant from 'shared/invariant'; + +import {getEventPriorityForListenerSystem} from '../events/DOMEventProperties'; + +type EventOptions = {| + capture?: boolean, + passive?: boolean, + priority?: EventPriority, +|}; + +const {ReactCurrentDispatcher} = ReactSharedInternals; + +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 = getEventPriorityForListenerSystem((type: any)); + + if (options != null) { + const optionsCapture = options.capture; + const optionsPassive = options.passive; + const optionsPriority = 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); +} diff --git a/packages/react-dom/src/events/DOMEventProperties.js b/packages/react-dom/src/events/DOMEventProperties.js index a9d56d304e226..a2db9cc16b6ba 100644 --- a/packages/react-dom/src/events/DOMEventProperties.js +++ b/packages/react-dom/src/events/DOMEventProperties.js @@ -223,3 +223,17 @@ export function getEventPriorityForPluginSystem( // for the event. return priority === undefined ? ContinuousEvent : priority; } + +export function getEventPriorityForListenerSystem(type: string): EventPriority { + const priority = eventPriorities.get(((type: any): TopLevelType)); + if (priority !== undefined) { + return priority; + } + if (__DEV__) { + console.warn( + 'The event "type" provided to useEvent() does not have a known priority type.' + + ' It is recommended to provide a "priority" option to specify a priority.', + ); + } + return ContinuousEvent; +} diff --git a/packages/shared/ReactDOMTypes.js b/packages/shared/ReactDOMTypes.js index 19cd62793b1ad..9ad6defce9770 100644 --- a/packages/shared/ReactDOMTypes.js +++ b/packages/shared/ReactDOMTypes.js @@ -75,3 +75,23 @@ export type ReactDOMResponderContext = { getResponderNode(): Element | null, ... }; + +export type ReactDOMListenerEvent = {| + capture: boolean, + passive: boolean, + priority: EventPriority, + type: string, +|}; + +export type ReactDOMListenerMap = {| + clear: () => void, + setListener: (instance: EventTarget, callback: ?(Event) => void) => void, +|}; + +export type ReactDOMListener = {| + callback: Event => void, + depth: number, + destroy: Document | (Element => void), + event: ReactDOMListenerEvent, + instance: EventTarget, +|}; diff --git a/packages/shared/ReactFeatureFlags.js b/packages/shared/ReactFeatureFlags.js index 6ae5572512992..0df10f27949f2 100644 --- a/packages/shared/ReactFeatureFlags.js +++ b/packages/shared/ReactFeatureFlags.js @@ -60,6 +60,9 @@ export const enableFundamentalAPI = false; // Experimental Scope support. export const enableScopeAPI = false; +// Experimental useEvent support. +export const enableUseEventAPI = false; + // New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107 // We will enforce mocking scheduler with scheduler/unstable_mock at some point. (v17?) diff --git a/packages/shared/forks/ReactFeatureFlags.native-fb.js b/packages/shared/forks/ReactFeatureFlags.native-fb.js index 4d81ea55a6351..3ff608e09f92b 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-fb.js +++ b/packages/shared/forks/ReactFeatureFlags.native-fb.js @@ -30,6 +30,7 @@ export const warnAboutDeprecatedLifecycles = true; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = true; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = false; diff --git a/packages/shared/forks/ReactFeatureFlags.native-oss.js b/packages/shared/forks/ReactFeatureFlags.native-oss.js index ccc6f66918518..add4df9f4852e 100644 --- a/packages/shared/forks/ReactFeatureFlags.native-oss.js +++ b/packages/shared/forks/ReactFeatureFlags.native-oss.js @@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.js index 53ec9981ca664..495572059d309 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.js @@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = false; diff --git a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js index 44d6c9ff44881..be20ebe5d240c 100644 --- a/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js +++ b/packages/shared/forks/ReactFeatureFlags.test-renderer.www.js @@ -29,6 +29,7 @@ export const disableInputAttributeSyncing = false; export const enableDeprecatedFlareAPI = true; export const enableFundamentalAPI = false; export const enableScopeAPI = true; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = true; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = true; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.js b/packages/shared/forks/ReactFeatureFlags.testing.js index 162023a3b336d..a25c31e788ab5 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.js @@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = false; export const enableFundamentalAPI = false; export const enableScopeAPI = false; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = false; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = false; diff --git a/packages/shared/forks/ReactFeatureFlags.testing.www.js b/packages/shared/forks/ReactFeatureFlags.testing.www.js index 11880b55f9d7c..4c00a26901858 100644 --- a/packages/shared/forks/ReactFeatureFlags.testing.www.js +++ b/packages/shared/forks/ReactFeatureFlags.testing.www.js @@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false; export const enableDeprecatedFlareAPI = true; export const enableFundamentalAPI = false; export const enableScopeAPI = true; +export const enableUseEventAPI = false; export const warnAboutUnmockedScheduler = true; export const flushSuspenseFallbacksInTests = true; export const enableSuspenseCallback = true; diff --git a/packages/shared/forks/ReactFeatureFlags.www.js b/packages/shared/forks/ReactFeatureFlags.www.js index 7a25a14d8e39a..f59be5b801a35 100644 --- a/packages/shared/forks/ReactFeatureFlags.www.js +++ b/packages/shared/forks/ReactFeatureFlags.www.js @@ -88,6 +88,8 @@ export const enableFundamentalAPI = false; export const enableScopeAPI = true; +export const enableUseEventAPI = false; + export const warnAboutUnmockedScheduler = true; export const enableSuspenseCallback = true;