@samitouri / QOS-React-2 / commits / 9c7cf20dd5

Ignore event listener extraction on numeric text components (#9194)

In ReactDOM we don't dispatch events in the synthetic event system on to text nodes. We back up one to the element before dispatching. In React Native we don't do that. The iOS native side doesn't dispatch on the text nodes but only the parent. The Android native side however does dispatch on text nodes. We already covered this for strings, but current element in Stack can be a number if it is numeric text content.

Sebastian Markbåge committed Mar 16, 2017 at 15:29 UTC 9c7cf20dd56a42fe417e6caa264e75058bf92911
2 files changed +93 -7
src/renderers/native/__tests__/ReactNativeEvents-test.js
+87
@@ -87,6 +87,93 @@ it('handles events', () => {
87 ]);
88 });
89
90 +it('handles events on text nodes', () => {
91 + expect(RCTEventEmitter.register.mock.calls.length).toBe(1);
92 + var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
93 +
94 + var Text = createReactNativeComponentClass({
95 + validAttributes: {foo: true},
96 + uiViewClassName: 'Text',
97 + });
98 +
99 + class ContextHack extends React.Component {
100 + static childContextTypes = {isInAParentText: React.PropTypes.bool};
101 + getChildContext() {
102 + return {isInAParentText: true};
103 + }
104 + render() {
105 + return this.props.children;
106 + }
107 + }
108 +
109 + var log = [];
110 + ReactNative.render(
111 + <ContextHack>
112 + <Text>
113 + <Text
114 + onTouchEnd={() => log.push('string touchend')}
115 + onTouchEndCapture={() => log.push('string touchend capture')}
116 + onTouchStart={() => log.push('string touchstart')}
117 + onTouchStartCapture={() => log.push('string touchstart capture')}>
118 + Text Content
119 + </Text>
120 + <Text
121 + onTouchEnd={() => log.push('number touchend')}
122 + onTouchEndCapture={() => log.push('number touchend capture')}
123 + onTouchStart={() => log.push('number touchstart')}
124 + onTouchStartCapture={() => log.push('number touchstart capture')}>
125 + {123}
126 + </Text>
127 + </Text>
128 + </ContextHack>,
129 + 1,
130 + );
131 +
132 + expect(UIManager.createView.mock.calls.length).toBe(5);
133 +
134 + // Don't depend on the order of createView() calls.
135 + // Stack creates views outside-in; fiber creates them inside-out.
136 + var innerTagString = UIManager.createView.mock.calls.find(
137 + args => args[3] && args[3].text === 'Text Content',
138 + )[0];
139 + var innerTagNumber = UIManager.createView.mock.calls.find(
140 + args => args[3] && args[3].text === '123',
141 + )[0];
142 +
143 + EventEmitter.receiveTouches(
144 + 'topTouchStart',
145 + [{target: innerTagString, identifier: 17}],
146 + [0],
147 + );
148 + EventEmitter.receiveTouches(
149 + 'topTouchEnd',
150 + [{target: innerTagString, identifier: 17}],
151 + [0],
152 + );
153 +
154 + EventEmitter.receiveTouches(
155 + 'topTouchStart',
156 + [{target: innerTagNumber, identifier: 18}],
157 + [0],
158 + );
159 + EventEmitter.receiveTouches(
160 + 'topTouchEnd',
161 + [{target: innerTagNumber, identifier: 18}],
162 + [0],
163 + );
164 +
165 + expect(log).toEqual([
166 + 'string touchstart capture',
167 + 'string touchstart',
168 + 'string touchend capture',
169 + 'string touchend',
170 + 'number touchstart capture',
171 + 'number touchstart',
172 + 'number touchend capture',
173 + 'number touchend',
174 + ]);
175 +});
176 +
177 it('handles when a responder is unmounted while a touch sequence is in progress', () => {
178 var EventEmitter = RCTEventEmitter.register.mock.calls[0][0];
179 var View = createReactNativeComponentClass({
src/renderers/shared/shared/event/EventPluginHub.js
+6 -7
@@ -138,7 +138,10 @@ var EventPluginHub = {
138 return null;
139 }
140 } else {
141 - if (typeof inst._currentElement === 'string') {
141 + const currentElement = inst._currentElement;
142 + if (
143 + typeof currentElement === 'string' || typeof currentElement === 'number'
144 + ) {
145 // Text node, let it bubble through.
146 return null;
147 }
@@ -146,14 +149,10 @@ var EventPluginHub = {
149 // If the instance is already unmounted, we have no listeners.
150 return null;
151 }
149 - const props = inst._currentElement.props;
152 + const props = currentElement.props;
153 listener = props[registrationName];
154 if (
152 - shouldPreventMouseEvent(
153 - registrationName,
154 - inst._currentElement.type,
155 - props,
156 - )
155 + shouldPreventMouseEvent(registrationName, currentElement.type, props)
156 ) {
157 return null;
158 }