Skip to content

Commit 731456a

Browse files
committed
[Native] Add FeatureFlag to dispatch events with instance targets (#17323)
* [Native] Add FeatureFlag to dispatch events with instance targets * Prettier
1 parent 9d53638 commit 731456a

13 files changed

+380
-4
lines changed

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

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
1818
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
1919

2020
import type {AnyNativeEvent} from 'legacy-events/PluginModuleType';
21-
import {enableFlareAPI} from 'shared/ReactFeatureFlags';
21+
import {
22+
enableFlareAPI,
23+
enableNativeTargetAsInstance,
24+
} from 'shared/ReactFeatureFlags';
2225
import type {TopLevelType} from 'legacy-events/TopLevelEventTypes';
2326
import {dispatchEventForResponderEventSystem} from './ReactFabricEventResponderSystem';
2427

@@ -30,6 +33,7 @@ export function dispatchEvent(
3033
nativeEvent: AnyNativeEvent,
3134
) {
3235
const targetFiber = (target: null | Fiber);
36+
3337
if (enableFlareAPI) {
3438
// React Flare event system
3539
dispatchEventForResponderEventSystem(
@@ -38,13 +42,25 @@ export function dispatchEvent(
3842
(nativeEvent: any),
3943
);
4044
}
45+
46+
let eventTarget;
47+
if (enableNativeTargetAsInstance) {
48+
if (targetFiber == null) {
49+
eventTarget = null;
50+
} else {
51+
eventTarget = targetFiber.stateNode.canonical;
52+
}
53+
} else {
54+
eventTarget = nativeEvent.target;
55+
}
56+
4157
batchedUpdates(function() {
4258
// Heritage plugin event system
4359
runExtractedPluginEventsInBatch(
4460
topLevelType,
4561
targetFiber,
4662
nativeEvent,
47-
nativeEvent.target,
63+
eventTarget,
4864
PLUGIN_EVENT_SYSTEM,
4965
);
5066
});

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import {registrationNameModules} from 'legacy-events/EventPluginRegistry';
1616
import {batchedUpdates} from 'legacy-events/ReactGenericBatching';
1717
import warningWithoutStack from 'shared/warningWithoutStack';
18+
import {enableNativeTargetAsInstance} from 'shared/ReactFeatureFlags';
1819

1920
import {getInstanceFromNode} from './ReactNativeComponentTree';
2021

@@ -98,12 +99,24 @@ function _receiveRootNodeIDEvent(
9899
) {
99100
const nativeEvent = nativeEventParam || EMPTY_NATIVE_EVENT;
100101
const inst = getInstanceFromNode(rootNodeID);
102+
103+
let target;
104+
if (enableNativeTargetAsInstance) {
105+
if (inst == null) {
106+
target = null;
107+
} else {
108+
target = inst.stateNode;
109+
}
110+
} else {
111+
target = nativeEvent.target;
112+
}
113+
101114
batchedUpdates(function() {
102115
runExtractedPluginEventsInBatch(
103116
topLevelType,
104117
inst,
105118
nativeEvent,
106-
nativeEvent.target,
119+
target,
107120
PLUGIN_EVENT_SYSTEM,
108121
);
109122
});

packages/react-native-renderer/src/__tests__/ReactFabric-test.internal.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
let React;
1414
let ReactFabric;
15+
let ReactFeatureFlags;
1516
let createReactClass;
1617
let createReactNativeComponentClass;
1718
let UIManager;
@@ -38,6 +39,7 @@ describe('ReactFabric', () => {
3839
React = require('react');
3940
StrictMode = React.StrictMode;
4041
ReactFabric = require('react-native-renderer/fabric');
42+
ReactFeatureFlags = require('shared/ReactFeatureFlags');
4143
UIManager = require('react-native/Libraries/ReactPrivate/ReactNativePrivateInterface')
4244
.UIManager;
4345
createReactClass = require('create-react-class/factory')(
@@ -779,6 +781,198 @@ describe('ReactFabric', () => {
779781
expect(touchStart2).toBeCalled();
780782
});
781783

784+
it('dispatches event with target as reactTag', () => {
785+
ReactFeatureFlags.enableNativeTargetAsInstance = false;
786+
787+
const View = createReactNativeComponentClass('RCTView', () => ({
788+
validAttributes: {
789+
id: true,
790+
},
791+
uiViewClassName: 'RCTView',
792+
directEventTypes: {
793+
topTouchStart: {
794+
registrationName: 'onTouchStart',
795+
},
796+
topTouchEnd: {
797+
registrationName: 'onTouchEnd',
798+
},
799+
},
800+
}));
801+
802+
function getViewById(id) {
803+
const [
804+
reactTag,
805+
,
806+
,
807+
,
808+
instanceHandle,
809+
] = nativeFabricUIManager.createNode.mock.calls.find(
810+
args => args[3] && args[3].id === id,
811+
);
812+
813+
return {reactTag, instanceHandle};
814+
}
815+
816+
const ref1 = React.createRef();
817+
const ref2 = React.createRef();
818+
819+
ReactFabric.render(
820+
<View id="parent">
821+
<View
822+
ref={ref1}
823+
id="one"
824+
onResponderStart={event => {
825+
expect(ref1.current).not.toBeNull();
826+
expect(ReactFabric.findNodeHandle(ref1.current)).toEqual(
827+
event.target,
828+
);
829+
}}
830+
onStartShouldSetResponder={() => true}
831+
/>
832+
<View
833+
ref={ref2}
834+
id="two"
835+
onResponderStart={event => {
836+
expect(ref2.current).not.toBeNull();
837+
expect(ReactFabric.findNodeHandle(ref2.current)).toEqual(
838+
event.target,
839+
);
840+
}}
841+
onStartShouldSetResponder={() => true}
842+
/>
843+
</View>,
844+
1,
845+
);
846+
847+
let [
848+
dispatchEvent,
849+
] = nativeFabricUIManager.registerEventHandler.mock.calls[0];
850+
851+
dispatchEvent(getViewById('one').instanceHandle, 'topTouchStart', {
852+
target: getViewById('one').reactTag,
853+
identifier: 17,
854+
touches: [],
855+
changedTouches: [],
856+
});
857+
dispatchEvent(getViewById('one').instanceHandle, 'topTouchEnd', {
858+
target: getViewById('one').reactTag,
859+
identifier: 17,
860+
touches: [],
861+
changedTouches: [],
862+
});
863+
864+
dispatchEvent(getViewById('two').instanceHandle, 'topTouchStart', {
865+
target: getViewById('two').reactTag,
866+
identifier: 17,
867+
touches: [],
868+
changedTouches: [],
869+
});
870+
871+
dispatchEvent(getViewById('two').instanceHandle, 'topTouchEnd', {
872+
target: getViewById('two').reactTag,
873+
identifier: 17,
874+
touches: [],
875+
changedTouches: [],
876+
});
877+
878+
expect.assertions(4);
879+
});
880+
881+
it('dispatches event with target as instance', () => {
882+
ReactFeatureFlags.enableNativeTargetAsInstance = true;
883+
884+
const View = createReactNativeComponentClass('RCTView', () => ({
885+
validAttributes: {
886+
id: true,
887+
},
888+
uiViewClassName: 'RCTView',
889+
directEventTypes: {
890+
topTouchStart: {
891+
registrationName: 'onTouchStart',
892+
},
893+
topTouchEnd: {
894+
registrationName: 'onTouchEnd',
895+
},
896+
},
897+
}));
898+
899+
function getViewById(id) {
900+
const [
901+
reactTag,
902+
,
903+
,
904+
,
905+
instanceHandle,
906+
] = nativeFabricUIManager.createNode.mock.calls.find(
907+
args => args[3] && args[3].id === id,
908+
);
909+
910+
return {reactTag, instanceHandle};
911+
}
912+
913+
const ref1 = React.createRef();
914+
const ref2 = React.createRef();
915+
916+
ReactFabric.render(
917+
<View id="parent">
918+
<View
919+
ref={ref1}
920+
id="one"
921+
onResponderStart={event => {
922+
expect(ref1.current).not.toBeNull();
923+
// Check for referential equality
924+
expect(ref1.current).toBe(event.target);
925+
}}
926+
onStartShouldSetResponder={() => true}
927+
/>
928+
<View
929+
ref={ref2}
930+
id="two"
931+
onResponderStart={event => {
932+
expect(ref2.current).not.toBeNull();
933+
// Check for referential equality
934+
expect(ref2.current).toBe(event.target);
935+
}}
936+
onStartShouldSetResponder={() => true}
937+
/>
938+
</View>,
939+
1,
940+
);
941+
942+
let [
943+
dispatchEvent,
944+
] = nativeFabricUIManager.registerEventHandler.mock.calls[0];
945+
946+
dispatchEvent(getViewById('one').instanceHandle, 'topTouchStart', {
947+
target: getViewById('one').reactTag,
948+
identifier: 17,
949+
touches: [],
950+
changedTouches: [],
951+
});
952+
dispatchEvent(getViewById('one').instanceHandle, 'topTouchEnd', {
953+
target: getViewById('one').reactTag,
954+
identifier: 17,
955+
touches: [],
956+
changedTouches: [],
957+
});
958+
959+
dispatchEvent(getViewById('two').instanceHandle, 'topTouchStart', {
960+
target: getViewById('two').reactTag,
961+
identifier: 17,
962+
touches: [],
963+
changedTouches: [],
964+
});
965+
966+
dispatchEvent(getViewById('two').instanceHandle, 'topTouchEnd', {
967+
target: getViewById('two').reactTag,
968+
identifier: 17,
969+
touches: [],
970+
changedTouches: [],
971+
});
972+
973+
expect.assertions(4);
974+
});
975+
782976
it('findHostInstance_DEPRECATED should warn if used to find a host component inside StrictMode', () => {
783977
const View = createReactNativeComponentClass('RCTView', () => ({
784978
validAttributes: {foo: true},

0 commit comments

Comments
 (0)