Trim and inline ReactInstanceHandles (#7676)
React IDs have been killed and there was one call site left in a test. I trimmed down the implementation to keep only what is actually used and inlined it inside of the test so we don't get more people using it in the future.
Christopher Chedeau committed
Sep 7, 2016 at 20:03 UTC
82598eec799d746d5f012a566978c895c2459d4b
3 files changed
+85
-355
src/renderers/shared/stack/event/eventPlugins/__tests__/ResponderEventPlugin-test.js
+82
-15
@@ -12,7 +12,6 @@
12
'use strict';
13
14
var EventPluginHub;
15
-var ReactInstanceHandles;
15
var ResponderEventPlugin;
16
var EventPluginUtils;
17
@@ -328,12 +327,89 @@ var siblings = {
327
};
328
329
describe('ResponderEventPlugin', () => {
330
+
331
+ // This test is written against React IDs such as 'root.a.b[10]' but we
332
+ // removed those. In order to make those tests still pass with minimum
333
+ // surgery, we are inlining the implementation of those IDs here as it
334
+ // is the only remaining call site.
335
+
336
+ var SEPARATOR = '.';
337
+
338
+ function isBoundary(id, index) {
339
+ return id.charAt(index) === SEPARATOR || index === id.length;
340
+ }
341
+
342
+ function isAncestorIDOf(ancestorID, descendantID) {
343
+ return (
344
+ descendantID.indexOf(ancestorID) === 0 &&
345
+ isBoundary(descendantID, ancestorID.length)
346
+ );
347
+ }
348
+
349
+ function getParentID(id) {
350
+ return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
351
+ }
352
+
353
+ function getNextDescendantID(ancestorID, destinationID) {
354
+ if (ancestorID === destinationID) {
355
+ return ancestorID;
356
+ }
357
+ // Skip over the ancestor and the immediate separator. Traverse until we hit
358
+ // another separator or we reach the end of `destinationID`.
359
+ var start = ancestorID.length + SEPARATOR.length;
360
+ var i;
361
+ for (i = start; i < destinationID.length; i++) {
362
+ if (isBoundary(destinationID, i)) {
363
+ break;
364
+ }
365
+ }
366
+ return destinationID.substr(0, i);
367
+ }
368
+
369
+ function getFirstCommonAncestorID(oneID, twoID) {
370
+ var minLength = Math.min(oneID.length, twoID.length);
371
+ if (minLength === 0) {
372
+ return '';
373
+ }
374
+ var lastCommonMarkerIndex = 0;
375
+ // Use `<=` to traverse until the "EOL" of the shorter string.
376
+ for (var i = 0; i <= minLength; i++) {
377
+ if (isBoundary(oneID, i) && isBoundary(twoID, i)) {
378
+ lastCommonMarkerIndex = i;
379
+ } else if (oneID.charAt(i) !== twoID.charAt(i)) {
380
+ break;
381
+ }
382
+ }
383
+ return oneID.substr(0, lastCommonMarkerIndex);
384
+ }
385
+
386
+ function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
387
+ var traverseUp = isAncestorIDOf(stop, start);
388
+ var traverse = traverseUp ? getParentID : getNextDescendantID;
389
+ for (var id = start; /* until break */; id = traverse(id, stop)) {
390
+ var ret;
391
+ if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
392
+ ret = cb(id, traverseUp, arg);
393
+ }
394
+ if (ret === false || id === stop) {
395
+ // Only break //after// visiting `stop`.
396
+ break;
397
+ }
398
+ }
399
+ }
400
+
401
+ function traverseTwoPhase(targetID, cb, arg) {
402
+ traverseParentPath('', targetID, cb, arg, true, false);
403
+ traverseParentPath(targetID, '', cb, arg, false, true);
404
+ }
405
+
406
+ // -- end of the React ID implementation
407
+
408
beforeEach(() => {
409
jest.resetModuleRegistry();
410
411
EventPluginHub = require('EventPluginHub');
412
EventPluginUtils = require('EventPluginUtils');
336
- ReactInstanceHandles = require('ReactInstanceHandles');
413
ResponderEventPlugin = require('ResponderEventPlugin');
414
415
EventPluginUtils.injection.injectComponentTree({
@@ -347,28 +423,19 @@ describe('ResponderEventPlugin', () => {
423
424
EventPluginUtils.injection.injectTreeTraversal({
425
isAncestor: function(a, b) {
350
- return ReactInstanceHandles.isAncestorIDOf(
351
- a._rootNodeID,
352
- b._rootNodeID
353
- );
426
+ return isAncestorIDOf(a._rootNodeID, b._rootNodeID);
427
},
428
getLowestCommonAncestor: function(a, b) {
356
- if (!a || !b) {
357
- return null;
358
- }
359
- var commonID = ReactInstanceHandles.getFirstCommonAncestorID(
360
- a._rootNodeID,
361
- b._rootNodeID
362
- );
429
+ var commonID = getFirstCommonAncestorID(a._rootNodeID, b._rootNodeID);
430
return idToInstance[commonID] || null;
431
},
432
getParentInstance: function(inst) {
433
var id = inst._rootNodeID;
367
- var parentID = id.substr(0, id.lastIndexOf('.'));
434
+ var parentID = id.substr(0, id.lastIndexOf(SEPARATOR));
435
return idToInstance[parentID] || null;
436
},
437
traverseTwoPhase: function(target, fn, arg) {
371
- ReactInstanceHandles.traverseTwoPhase(
438
+ traverseTwoPhase(
439
target._rootNodeID,
440
function(id, upwards) {
441
fn(idToInstance[id], upwards, arg);
src/renderers/shared/stack/reconciler/ReactInstanceHandles.js
deleted
-337
@@ -1,337 +0,0 @@
1
-/**
2
- * Copyright 2013-present, Facebook, Inc.
3
- * All rights reserved.
4
- *
5
- * This source code is licensed under the BSD-style license found in the
6
- * LICENSE file in the root directory of this source tree. An additional grant
7
- * of patent rights can be found in the PATENTS file in the same directory.
8
- *
9
- * @providesModule ReactInstanceHandles
10
- */
11
-
12
-'use strict';
13
-
14
-var invariant = require('invariant');
15
-
16
-var SEPARATOR = '.';
17
-var SEPARATOR_LENGTH = SEPARATOR.length;
18
-
19
-/**
20
- * Maximum depth of traversals before we consider the possibility of a bad ID.
21
- */
22
-var MAX_TREE_DEPTH = 10000;
23
-
24
-/**
25
- * Creates a DOM ID prefix to use when mounting React components.
26
- *
27
- * @param {number} index A unique integer
28
- * @return {string} React root ID.
29
- * @internal
30
- */
31
-function getReactRootIDString(index) {
32
- return SEPARATOR + index.toString(36);
33
-}
34
-
35
-/**
36
- * Checks if a character in the supplied ID is a separator or the end.
37
- *
38
- * @param {string} id A React DOM ID.
39
- * @param {number} index Index of the character to check.
40
- * @return {boolean} True if the character is a separator or end of the ID.
41
- * @private
42
- */
43
-function isBoundary(id, index) {
44
- return id.charAt(index) === SEPARATOR || index === id.length;
45
-}
46
-
47
-/**
48
- * Checks if the supplied string is a valid React DOM ID.
49
- *
50
- * @param {string} id A React DOM ID, maybe.
51
- * @return {boolean} True if the string is a valid React DOM ID.
52
- * @private
53
- */
54
-function isValidID(id) {
55
- return id === '' || (
56
- id.charAt(0) === SEPARATOR && id.charAt(id.length - 1) !== SEPARATOR
57
- );
58
-}
59
-
60
-/**
61
- * Checks if the first ID is an ancestor of or equal to the second ID.
62
- *
63
- * @param {string} ancestorID
64
- * @param {string} descendantID
65
- * @return {boolean} True if `ancestorID` is an ancestor of `descendantID`.
66
- * @internal
67
- */
68
-function isAncestorIDOf(ancestorID, descendantID) {
69
- return (
70
- descendantID.indexOf(ancestorID) === 0 &&
71
- isBoundary(descendantID, ancestorID.length)
72
- );
73
-}
74
-
75
-/**
76
- * Gets the parent ID of the supplied React DOM ID, `id`.
77
- *
78
- * @param {string} id ID of a component.
79
- * @return {string} ID of the parent, or an empty string.
80
- * @private
81
- */
82
-function getParentID(id) {
83
- return id ? id.substr(0, id.lastIndexOf(SEPARATOR)) : '';
84
-}
85
-
86
-/**
87
- * Gets the next DOM ID on the tree path from the supplied `ancestorID` to the
88
- * supplied `destinationID`. If they are equal, the ID is returned.
89
- *
90
- * @param {string} ancestorID ID of an ancestor node of `destinationID`.
91
- * @param {string} destinationID ID of the destination node.
92
- * @return {string} Next ID on the path from `ancestorID` to `destinationID`.
93
- * @private
94
- */
95
-function getNextDescendantID(ancestorID, destinationID) {
96
- invariant(
97
- isValidID(ancestorID) && isValidID(destinationID),
98
- 'getNextDescendantID(%s, %s): Received an invalid React DOM ID.',
99
- ancestorID,
100
- destinationID
101
- );
102
- invariant(
103
- isAncestorIDOf(ancestorID, destinationID),
104
- 'getNextDescendantID(...): React has made an invalid assumption about ' +
105
- 'the DOM hierarchy. Expected `%s` to be an ancestor of `%s`.',
106
- ancestorID,
107
- destinationID
108
- );
109
- if (ancestorID === destinationID) {
110
- return ancestorID;
111
- }
112
- // Skip over the ancestor and the immediate separator. Traverse until we hit
113
- // another separator or we reach the end of `destinationID`.
114
- var start = ancestorID.length + SEPARATOR_LENGTH;
115
- var i;
116
- for (i = start; i < destinationID.length; i++) {
117
- if (isBoundary(destinationID, i)) {
118
- break;
119
- }
120
- }
121
- return destinationID.substr(0, i);
122
-}
123
-
124
-/**
125
- * Gets the nearest common ancestor ID of two IDs.
126
- *
127
- * Using this ID scheme, the nearest common ancestor ID is the longest common
128
- * prefix of the two IDs that immediately preceded a "marker" in both strings.
129
- *
130
- * @param {string} oneID
131
- * @param {string} twoID
132
- * @return {string} Nearest common ancestor ID, or the empty string if none.
133
- * @private
134
- */
135
-function getFirstCommonAncestorID(oneID, twoID) {
136
- var minLength = Math.min(oneID.length, twoID.length);
137
- if (minLength === 0) {
138
- return '';
139
- }
140
- var lastCommonMarkerIndex = 0;
141
- // Use `<=` to traverse until the "EOL" of the shorter string.
142
- for (var i = 0; i <= minLength; i++) {
143
- if (isBoundary(oneID, i) && isBoundary(twoID, i)) {
144
- lastCommonMarkerIndex = i;
145
- } else if (oneID.charAt(i) !== twoID.charAt(i)) {
146
- break;
147
- }
148
- }
149
- var longestCommonID = oneID.substr(0, lastCommonMarkerIndex);
150
- invariant(
151
- isValidID(longestCommonID),
152
- 'getFirstCommonAncestorID(%s, %s): Expected a valid React DOM ID: %s',
153
- oneID,
154
- twoID,
155
- longestCommonID
156
- );
157
- return longestCommonID;
158
-}
159
-
160
-/**
161
- * Traverses the parent path between two IDs (either up or down). The IDs must
162
- * not be the same, and there must exist a parent path between them. If the
163
- * callback returns `false`, traversal is stopped.
164
- *
165
- * @param {?string} start ID at which to start traversal.
166
- * @param {?string} stop ID at which to end traversal.
167
- * @param {function} cb Callback to invoke each ID with.
168
- * @param {*} arg Argument to invoke the callback with.
169
- * @param {?boolean} skipFirst Whether or not to skip the first node.
170
- * @param {?boolean} skipLast Whether or not to skip the last node.
171
- * @private
172
- */
173
-function traverseParentPath(start, stop, cb, arg, skipFirst, skipLast) {
174
- start = start || '';
175
- stop = stop || '';
176
- invariant(
177
- start !== stop,
178
- 'traverseParentPath(...): Cannot traverse from and to the same ID, `%s`.',
179
- start
180
- );
181
- var traverseUp = isAncestorIDOf(stop, start);
182
- invariant(
183
- traverseUp || isAncestorIDOf(start, stop),
184
- 'traverseParentPath(%s, %s, ...): Cannot traverse from two IDs that do ' +
185
- 'not have a parent path.',
186
- start,
187
- stop
188
- );
189
- // Traverse from `start` to `stop` one depth at a time.
190
- var depth = 0;
191
- var traverse = traverseUp ? getParentID : getNextDescendantID;
192
- for (var id = start; /* until break */; id = traverse(id, stop)) {
193
- var ret;
194
- if ((!skipFirst || id !== start) && (!skipLast || id !== stop)) {
195
- ret = cb(id, traverseUp, arg);
196
- }
197
- if (ret === false || id === stop) {
198
- // Only break //after// visiting `stop`.
199
- break;
200
- }
201
- invariant(
202
- depth++ < MAX_TREE_DEPTH,
203
- 'traverseParentPath(%s, %s, ...): Detected an infinite loop while ' +
204
- 'traversing the React DOM ID tree. This may be due to malformed IDs: %s',
205
- start, stop, id
206
- );
207
- }
208
-}
209
-
210
-/**
211
- * Manages the IDs assigned to DOM representations of React components. This
212
- * uses a specific scheme in order to traverse the DOM efficiently (e.g. in
213
- * order to simulate events).
214
- *
215
- * @internal
216
- */
217
-var ReactInstanceHandles = {
218
-
219
- /**
220
- * Constructs a React root ID
221
- * @param {number} index A unique integer
222
- * @return {string} A React root ID.
223
- */
224
- createReactRootID: function(index) {
225
- return getReactRootIDString(index);
226
- },
227
-
228
- /**
229
- * Constructs a React ID by joining a root ID with a name.
230
- *
231
- * @param {string} rootID Root ID of a parent component.
232
- * @param {string} name A component's name (as flattened children).
233
- * @return {string} A React ID.
234
- * @internal
235
- */
236
- createReactID: function(rootID, name) {
237
- return rootID + name;
238
- },
239
-
240
- /**
241
- * Gets the DOM ID of the React component that is the root of the tree that
242
- * contains the React component with the supplied DOM ID.
243
- *
244
- * @param {string} id DOM ID of a React component.
245
- * @return {?string} DOM ID of the React component that is the root.
246
- * @internal
247
- */
248
- getReactRootIDFromNodeID: function(id) {
249
- if (id && id.charAt(0) === SEPARATOR && id.length > 1) {
250
- var index = id.indexOf(SEPARATOR, 1);
251
- return index > -1 ? id.substr(0, index) : id;
252
- }
253
- return null;
254
- },
255
-
256
- /**
257
- * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that
258
- * should would receive a `mouseEnter` or `mouseLeave` event.
259
- *
260
- * NOTE: Does not invoke the callback on the nearest common ancestor because
261
- * nothing "entered" or "left" that element.
262
- *
263
- * @param {string} leaveID ID being left.
264
- * @param {string} enterID ID being entered.
265
- * @param {function} cb Callback to invoke on each entered/left ID.
266
- * @param {*} upArg Argument to invoke the callback with on left IDs.
267
- * @param {*} downArg Argument to invoke the callback with on entered IDs.
268
- * @internal
269
- */
270
- traverseEnterLeave: function(leaveID, enterID, cb, upArg, downArg) {
271
- var ancestorID = getFirstCommonAncestorID(leaveID, enterID);
272
- if (ancestorID !== leaveID) {
273
- traverseParentPath(leaveID, ancestorID, cb, upArg, false, true);
274
- }
275
- if (ancestorID !== enterID) {
276
- traverseParentPath(ancestorID, enterID, cb, downArg, true, false);
277
- }
278
- },
279
-
280
- /**
281
- * Simulates the traversal of a two-phase, capture/bubble event dispatch.
282
- *
283
- * NOTE: This traversal happens on IDs without touching the DOM.
284
- *
285
- * @param {string} targetID ID of the target node.
286
- * @param {function} cb Callback to invoke.
287
- * @param {*} arg Argument to invoke the callback with.
288
- * @internal
289
- */
290
- traverseTwoPhase: function(targetID, cb, arg) {
291
- if (targetID) {
292
- traverseParentPath('', targetID, cb, arg, true, false);
293
- traverseParentPath(targetID, '', cb, arg, false, true);
294
- }
295
- },
296
-
297
- /**
298
- * Same as `traverseTwoPhase` but skips the `targetID`.
299
- */
300
- traverseTwoPhaseSkipTarget: function(targetID, cb, arg) {
301
- if (targetID) {
302
- traverseParentPath('', targetID, cb, arg, true, true);
303
- traverseParentPath(targetID, '', cb, arg, true, true);
304
- }
305
- },
306
-
307
- /**
308
- * Traverse a node ID, calling the supplied `cb` for each ancestor ID. For
309
- * example, passing `.0.$row-0.1` would result in `cb` getting called
310
- * with `.0`, `.0.$row-0`, and `.0.$row-0.1`.
311
- *
312
- * NOTE: This traversal happens on IDs without touching the DOM.
313
- *
314
- * @param {string} targetID ID of the target node.
315
- * @param {function} cb Callback to invoke.
316
- * @param {*} arg Argument to invoke the callback with.
317
- * @internal
318
- */
319
- traverseAncestors: function(targetID, cb, arg) {
320
- traverseParentPath('', targetID, cb, arg, true, false);
321
- },
322
-
323
- getFirstCommonAncestorID: getFirstCommonAncestorID,
324
-
325
- /**
326
- * Exposed for unit testing.
327
- * @private
328
- */
329
- _getNextDescendantID: getNextDescendantID,
330
-
331
- isAncestorIDOf: isAncestorIDOf,
332
-
333
- SEPARATOR: SEPARATOR,
334
-
335
-};
336
-
337
-module.exports = ReactInstanceHandles;
src/renderers/testing/__tests__/ReactTestRenderer-test.js
+3
-3
@@ -209,16 +209,16 @@ describe('ReactTestRenderer', () => {
209
it('warns correctly for refs on SFCs', () => {
210
spyOn(console, 'error');
211
function Bar() {
212
- return <div>Hello, world</div>
212
+ return <div>Hello, world</div>;
213
}
214
class Foo extends React.Component {
215
render() {
216
- return <Bar ref="foo" />
216
+ return <Bar ref="foo" />;
217
}
218
}
219
class Baz extends React.Component {
220
render() {
221
- return <div ref="baz" />
221
+ return <div ref="baz" />;
222
}
223
}
224
ReactTestRenderer.create(<Baz />);