react/packages/react-native-renderer/src/ReactNativeEventEmitter.js

253 lines
8.1 KiB
JavaScript
Raw Normal View History

2016-03-25 06:40:52 +08:00
/**
* Copyright (c) Facebook, Inc. and its affiliates.
2016-03-25 06:40:52 +08:00
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
2016-03-25 06:40:52 +08:00
*
* @flow
*/
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
import type {Fiber} from 'react-reconciler/src/ReactFiber';
import type {PluginModule} from 'legacy-events/PluginModuleType';
import type {ReactSyntheticEvent} from 'legacy-events/ReactSyntheticEventType';
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
Event Replaying (#16725) * Add Event Replaying Infra * Wire up Roots and Suspense boundaries, to retry events, after they commit * Replay discrete events in order in a separate scheduler callback * Add continuous events These events only replay their last target if the target is not yet hydrated. That way we don't have to wait for a previously hovered boundary before invoking the current target. * Enable tests from before These tests were written with replaying in mind and now we can properly enable them. * Unify replaying and dispatching * Mark system flags as a replay and pass to legacy events That way we can check if this is a replay and therefore needs a special case. One such special case is "mouseover" where we check the relatedTarget. * Eagerly listen to all replayable events To minimize breakages in a minor, I only do this for the new root APIs since replaying only matters there anyway. Only if hydrating. For Flare, I have to attach all active listeners since the current system has one DOM listener for each. In a follow up I plan on optimizing that by only attaching one if there's at least one active listener which would allow us to start with only passive and then upgrade. * Desperate attempt to save bytese * Add test for mouseover replaying We need to check if the "relatedTarget" is mounted due to how the old event system dispatches from the "out" event. * Fix for nested boundaries and suspense in root container This is a follow up to #16673 which didn't have a test because it wasn't observable yet. This shows that it had a bug. * Rename RESPONDER_EVENT_SYSTEM to PLUGIN_EVENT_SYSTEM
2019-09-24 02:21:10 +08:00
import {PLUGIN_EVENT_SYSTEM} from 'legacy-events/EventSystemFlags';
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
import {runEventsInBatch} from 'legacy-events/EventBatching';
import {enableNativeTargetAsInstance} from 'shared/ReactFeatureFlags';
import {plugins} from 'legacy-events/EventPluginRegistry';
import getListener from 'legacy-events/getListener';
import accumulateInto from 'legacy-events/accumulateInto';
import {getInstanceFromNode} from './ReactNativeComponentTree';
2016-03-25 06:40:52 +08:00
export {getListener, registrationNameModules as registrationNames};
2016-03-25 06:40:52 +08:00
/**
* Version of `ReactBrowserEventEmitter` that works on the receiving side of a
* serialized worker boundary.
*/
// Shared default empty native event - conserve memory.
const EMPTY_NATIVE_EVENT = (({}: any): AnyNativeEvent);
2016-03-25 06:40:52 +08:00
/**
* Selects a subsequence of `Touch`es, without destroying `touches`.
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices by which to pull subsequence.
* @return {Array<Touch>} Subsequence of touch objects.
*/
const touchSubsequence = function(touches, indices) {
const ret = [];
for (let i = 0; i < indices.length; i++) {
2016-03-25 06:40:52 +08:00
ret.push(touches[indices[i]]);
}
return ret;
};
/**
* TODO: Pool all of this.
*
* Destroys `touches` by removing touch objects at indices `indices`. This is
* to maintain compatibility with W3C touch "end" events, where the active
* touches don't include the set that has just been "ended".
*
* @param {Array<Touch>} touches Deserialized touch objects.
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
const removeTouchesAtIndices = function(
2016-03-25 06:40:52 +08:00
touches: Array<Object>,
2017-03-14 08:05:18 +08:00
indices: Array<number>,
2016-03-25 06:40:52 +08:00
): Array<Object> {
const rippedOut = [];
2016-03-25 06:40:52 +08:00
// use an unsafe downcast to alias to nullable elements,
// so we can delete and then compact.
const temp: Array<?Object> = (touches: Array<any>);
for (let i = 0; i < indices.length; i++) {
const index = indices[i];
2016-03-25 06:40:52 +08:00
rippedOut.push(touches[index]);
temp[index] = null;
}
let fillAt = 0;
for (let j = 0; j < temp.length; j++) {
const cur = temp[j];
2016-03-25 06:40:52 +08:00
if (cur !== null) {
temp[fillAt++] = cur;
}
}
temp.length = fillAt;
return rippedOut;
};
/**
* Internal version of `receiveEvent` in terms of normalized (non-tag)
* `rootNodeID`.
*
* @see receiveEvent.
*
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {?object} nativeEventParam Object passed from native.
*/
function _receiveRootNodeIDEvent(
rootNodeID: number,
Use browser event names for top-level event types in React DOM (#12629) * Add TopLevelEventTypes * Fix `ReactBrowserEventEmitter` * Fix EventPluginUtils * Fix TapEventPlugin * Fix ResponderEventPlugin * Update ReactDOMFiberComponent * Fix BeforeInputEventPlugin * Fix ChangeEventPlugin * Fix EnterLeaveEventPlugin * Add missing non top event type used in ChangeEventPlugin * Fix SelectEventPlugin * Fix SimpleEventPlugin * Fix outstanding Flow issues and move TopLevelEventTypes * Inline a list of all events in `ReactTestUtils` * Fix tests * Make it pretty * Fix completly unrelated typo * Don’t use map constructor because of IE11 * Update typings, revert changes to native code * Make topLevelTypes in ResponderEventPlugin injectable and create DOM and ReactNative variant * Set proper dependencies for DOMResponderEventPlugin * Prettify * Make some react dom tests no longer depend on internal API * Use factories to create top level speific generic event modules * Remove unused dependency * Revert exposed module renaming, hide store creation, and inline dependency decleration * Add Flow types to createResponderEventPlugin and its consumers * Remove unused dependency * Use opaque flow type for TopLevelType * Add missing semis * Use raw event names as top level identifer * Upgrade baylon This is required for parsing opaque flow types in our CI tests. * Clean up flow types * Revert Map changes of ReactBrowserEventEmitter * Upgrade babel-* packages Apparently local unit tests also have issues with parsing JavaScript modules that contain opaque types (not sure why I didn't notice earlier!?). * Revert Map changes of SimpleEventPlugin * Clean up ReactTestUtils * Add missing semi * Fix Flow issue * Make TopLevelType clearer * Favor for loops * Explain the new DOMTopLevelEventTypes concept * Use static injection for Responder plugin types * Remove null check and rely on flow checks * Add missing ResponderEventPlugin dependencies
2018-05-15 17:38:50 +08:00
topLevelType: TopLevelType,
nativeEventParam: ?AnyNativeEvent,
) {
const nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
const inst = getInstanceFromNode(rootNodeID);
let target = null;
if (enableNativeTargetAsInstance) {
if (inst != null) {
target = inst.stateNode;
}
} else {
target = nativeEvent.target;
}
batchedUpdates(function() {
runExtractedPluginEventsInBatch(
topLevelType,
inst,
nativeEvent,
target,
PLUGIN_EVENT_SYSTEM,
);
});
// React Native doesn't use ReactControlledComponent but if it did, here's
// where it would do it.
}
2016-03-25 06:40:52 +08:00
/**
* Allows registered plugins an opportunity to extract events from top-level
* native browser events.
*
* @return {*} An accumulation of synthetic events.
* @internal
*/
function extractPluginEvents(
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
let events = null;
for (let i = 0; i < plugins.length; i++) {
// Not every plugin in the ordering may be loaded at runtime.
const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
if (possiblePlugin) {
const extractedEvents = possiblePlugin.extractEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
);
if (extractedEvents) {
events = accumulateInto(events, extractedEvents);
}
}
}
return events;
}
function runExtractedPluginEventsInBatch(
topLevelType: TopLevelType,
targetInst: null | Fiber,
nativeEvent: AnyNativeEvent,
nativeEventTarget: null | EventTarget,
eventSystemFlags: EventSystemFlags,
) {
const events = extractPluginEvents(
topLevelType,
targetInst,
nativeEvent,
nativeEventTarget,
eventSystemFlags,
);
runEventsInBatch(events);
}
/**
* Publicly exposed method on module for native objc to invoke when a top
* level event is extracted.
* @param {rootNodeID} rootNodeID React root node ID that event occurred on.
* @param {TopLevelType} topLevelType Top level type of event.
* @param {object} nativeEventParam Object passed from native.
*/
export function receiveEvent(
rootNodeID: number,
Use browser event names for top-level event types in React DOM (#12629) * Add TopLevelEventTypes * Fix `ReactBrowserEventEmitter` * Fix EventPluginUtils * Fix TapEventPlugin * Fix ResponderEventPlugin * Update ReactDOMFiberComponent * Fix BeforeInputEventPlugin * Fix ChangeEventPlugin * Fix EnterLeaveEventPlugin * Add missing non top event type used in ChangeEventPlugin * Fix SelectEventPlugin * Fix SimpleEventPlugin * Fix outstanding Flow issues and move TopLevelEventTypes * Inline a list of all events in `ReactTestUtils` * Fix tests * Make it pretty * Fix completly unrelated typo * Don’t use map constructor because of IE11 * Update typings, revert changes to native code * Make topLevelTypes in ResponderEventPlugin injectable and create DOM and ReactNative variant * Set proper dependencies for DOMResponderEventPlugin * Prettify * Make some react dom tests no longer depend on internal API * Use factories to create top level speific generic event modules * Remove unused dependency * Revert exposed module renaming, hide store creation, and inline dependency decleration * Add Flow types to createResponderEventPlugin and its consumers * Remove unused dependency * Use opaque flow type for TopLevelType * Add missing semis * Use raw event names as top level identifer * Upgrade baylon This is required for parsing opaque flow types in our CI tests. * Clean up flow types * Revert Map changes of ReactBrowserEventEmitter * Upgrade babel-* packages Apparently local unit tests also have issues with parsing JavaScript modules that contain opaque types (not sure why I didn't notice earlier!?). * Revert Map changes of SimpleEventPlugin * Clean up ReactTestUtils * Add missing semi * Fix Flow issue * Make TopLevelType clearer * Favor for loops * Explain the new DOMTopLevelEventTypes concept * Use static injection for Responder plugin types * Remove null check and rely on flow checks * Add missing ResponderEventPlugin dependencies
2018-05-15 17:38:50 +08:00
topLevelType: TopLevelType,
nativeEventParam: AnyNativeEvent,
) {
_receiveRootNodeIDEvent(rootNodeID, topLevelType, nativeEventParam);
}
2016-03-25 06:40:52 +08:00
/**
* Simple multi-wrapper around `receiveEvent` that is intended to receive an
* efficient representation of `Touch` objects, and other information that
* can be used to construct W3C compliant `Event` and `Touch` lists.
*
* This may create dispatch behavior that differs than web touch handling. We
* loop through each of the changed touches and receive it as a single event.
* So two `touchStart`/`touchMove`s that occur simultaneously are received as
* two separate touch event dispatches - when they arguably should be one.
*
* This implementation reuses the `Touch` objects themselves as the `Event`s
* since we dispatch an event for each touch (though that might not be spec
* compliant). The main purpose of reusing them is to save allocations.
*
* TODO: Dispatch multiple changed touches in one event. The bubble path
* could be the first common ancestor of all the `changedTouches`.
*
* One difference between this behavior and W3C spec: cancelled touches will
* not appear in `.touches`, or in any future `.touches`, though they may
* still be "actively touching the surface".
*
* Web desktop polyfills only need to construct a fake touch event with
* identifier 0, also abandoning traditional click handlers.
*/
export function receiveTouches(
Use browser event names for top-level event types in React DOM (#12629) * Add TopLevelEventTypes * Fix `ReactBrowserEventEmitter` * Fix EventPluginUtils * Fix TapEventPlugin * Fix ResponderEventPlugin * Update ReactDOMFiberComponent * Fix BeforeInputEventPlugin * Fix ChangeEventPlugin * Fix EnterLeaveEventPlugin * Add missing non top event type used in ChangeEventPlugin * Fix SelectEventPlugin * Fix SimpleEventPlugin * Fix outstanding Flow issues and move TopLevelEventTypes * Inline a list of all events in `ReactTestUtils` * Fix tests * Make it pretty * Fix completly unrelated typo * Don’t use map constructor because of IE11 * Update typings, revert changes to native code * Make topLevelTypes in ResponderEventPlugin injectable and create DOM and ReactNative variant * Set proper dependencies for DOMResponderEventPlugin * Prettify * Make some react dom tests no longer depend on internal API * Use factories to create top level speific generic event modules * Remove unused dependency * Revert exposed module renaming, hide store creation, and inline dependency decleration * Add Flow types to createResponderEventPlugin and its consumers * Remove unused dependency * Use opaque flow type for TopLevelType * Add missing semis * Use raw event names as top level identifer * Upgrade baylon This is required for parsing opaque flow types in our CI tests. * Clean up flow types * Revert Map changes of ReactBrowserEventEmitter * Upgrade babel-* packages Apparently local unit tests also have issues with parsing JavaScript modules that contain opaque types (not sure why I didn't notice earlier!?). * Revert Map changes of SimpleEventPlugin * Clean up ReactTestUtils * Add missing semi * Fix Flow issue * Make TopLevelType clearer * Favor for loops * Explain the new DOMTopLevelEventTypes concept * Use static injection for Responder plugin types * Remove null check and rely on flow checks * Add missing ResponderEventPlugin dependencies
2018-05-15 17:38:50 +08:00
eventTopLevelType: TopLevelType,
touches: Array<Object>,
changedIndices: Array<number>,
) {
const changedTouches =
eventTopLevelType === 'topTouchEnd' ||
eventTopLevelType === 'topTouchCancel'
? removeTouchesAtIndices(touches, changedIndices)
: touchSubsequence(touches, changedIndices);
2016-03-25 06:40:52 +08:00
for (let jj = 0; jj < changedTouches.length; jj++) {
const touch = changedTouches[jj];
// Touch objects can fulfill the role of `DOM` `Event` objects if we set
// the `changedTouches`/`touches`. This saves allocations.
touch.changedTouches = changedTouches;
touch.touches = touches;
const nativeEvent = touch;
let rootNodeID = null;
const target = nativeEvent.target;
if (target !== null && target !== undefined) {
if (target < 1) {
if (__DEV__) {
console.error(
'A view is reporting that a touch occurred on tag zero.',
);
2016-03-25 06:40:52 +08:00
}
} else {
rootNodeID = target;
2016-03-25 06:40:52 +08:00
}
}
// $FlowFixMe Shouldn't we *not* call it if rootNodeID is null?
_receiveRootNodeIDEvent(rootNodeID, eventTopLevelType, nativeEvent);
}
}