Type ReactComponentTreeHook (#7504)
For this one, I wanted to type a non-trivial piece of the codebase and ran into the fact that we do not have types for ReactElement nor ReactInstance, so I had to create them. I'll add comments inline
Christopher Chedeau committed
Aug 25, 2016 at 15:27 UTC
ea494a2c1082c705ef79d0b73516aa6923a4c75a
3 files changed
+199
-121
src/isomorphic/classic/element/ReactElementType.js
new
+37
@@ -0,0 +1,37 @@
1
+/**
2
+ * Copyright 2016-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
+ * @flow
10
+ * @providesModule ReactElementType
11
+ */
12
+
13
+'use strict';
14
+
15
+import type { ReactInstance } from 'ReactInstanceType';
16
+
17
+export type Source = {
18
+ fileName: string,
19
+ lineNumber: number,
20
+};
21
+
22
+export type ReactElement = {
23
+ $$typeof: any,
24
+ type: any,
25
+ key: any,
26
+ ref: any,
27
+ props: any,
28
+ _owner: ReactInstance,
29
+
30
+ // __DEV__
31
+ _store: {
32
+ validated: bool,
33
+ },
34
+ _self: ReactElement,
35
+ _shadowChildren: any,
36
+ _source: Source,
37
+};
src/isomorphic/hooks/ReactComponentTreeHook.js
+125
-121
@@ -6,6 +6,7 @@
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
+ * @flow
10
* @providesModule ReactComponentTreeHook
11
*/
12
@@ -16,6 +17,9 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
17
var invariant = require('invariant');
18
var warning = require('warning');
19
20
+import type { ReactElement, Source } from 'ReactElementType';
21
+import type { DebugID } from 'ReactInstanceType';
22
+
23
function isNative(fn) {
24
// Based on isNative() from Lodash
25
var funcToString = Function.prototype.toString;
@@ -39,6 +43,24 @@ function isNative(fn) {
43
}
44
}
45
46
+type Item = {
47
+ element: ReactElement,
48
+ parentID: DebugID,
49
+ text: ?string,
50
+ childIDs: Array<DebugID>,
51
+ isMounted: bool,
52
+ updateCount: number,
53
+};
54
+
55
+declare function getItem(id: DebugID): ?Item;
56
+declare function removeItem(id: DebugID): void;
57
+declare function setItem(id: DebugID, item: Item): void;
58
+declare function getItemIDs(): Array<DebugID>;
59
+
60
+declare function addRoot(id: DebugID): void;
61
+declare function removeRoot(id: DebugID): void;
62
+declare function getRootIDs(): Array<DebugID>;
63
+
64
var canUseCollections = (
65
// Array.from
66
typeof Array.from === 'function' &&
@@ -58,112 +80,88 @@ var canUseCollections = (
80
isNative(Set.prototype.keys)
81
);
82
61
-var itemMap;
62
-var rootIDSet;
83
+if (canUseCollections) {
84
+ var itemMap = new Map();
85
+ var rootIDSet = new Set();
86
64
-var itemByKey;
65
-var rootByKey;
87
+ var setItem = function(id, item) {
88
+ itemMap.set(id, item);
89
+ };
90
+ var getItem = function(id) {
91
+ return itemMap.get(id);
92
+ };
93
+ var removeItem = function(id) {
94
+ itemMap.delete(id);
95
+ };
96
+ var getItemIDs = function() {
97
+ return Array.from(itemMap.keys());
98
+ };
99
67
-if (canUseCollections) {
68
- itemMap = new Map();
69
- rootIDSet = new Set();
70
-} else {
71
- itemByKey = {};
72
- rootByKey = {};
73
-}
100
+ var addRoot = function(id) {
101
+ rootIDSet.add(id);
102
+ };
103
+ var removeRoot = function(id) {
104
+ rootIDSet.delete(id);
105
+ };
106
+ var getRootIDs = function() {
107
+ return Array.from(rootIDSet.keys());
108
+ };
109
75
-var unmountedIDs = [];
110
+} else {
111
+ var itemByKey = {};
112
+ var rootByKey = {};
113
77
-// Use non-numeric keys to prevent V8 performance issues:
78
-// https://github.com/facebook/react/pull/7232
79
-function getKeyFromID(id) {
80
- return '.' + id;
81
-}
82
-function getIDFromKey(key) {
83
- return parseInt(key.substr(1), 10);
84
-}
114
+ // Use non-numeric keys to prevent V8 performance issues:
115
+ // https://github.com/facebook/react/pull/7232
116
+ function getKeyFromID(id: DebugID): string {
117
+ return '.' + id;
118
+ }
119
+ function getIDFromKey(key: string): DebugID {
120
+ return parseInt(key.substr(1), 10);
121
+ }
122
86
-function get(id) {
87
- if (canUseCollections) {
88
- return itemMap.get(id);
89
- } else {
123
+ var setItem = function(id, item) {
124
+ var key = getKeyFromID(id);
125
+ itemByKey[key] = item;
126
+ };
127
+ var getItem = function(id) {
128
var key = getKeyFromID(id);
129
return itemByKey[key];
92
- }
93
-}
94
-
95
-function remove(id) {
96
- if (canUseCollections) {
97
- itemMap.delete(id);
98
- } else {
130
+ };
131
+ var removeItem = function(id) {
132
var key = getKeyFromID(id);
133
delete itemByKey[key];
101
- }
102
-}
103
-
104
-function create(id, element, parentID) {
105
- var item = {
106
- element,
107
- parentID,
108
- text: null,
109
- childIDs: [],
110
- isMounted: false,
111
- updateCount: 0,
134
+ };
135
+ var getItemIDs = function() {
136
+ return Object.keys(itemByKey).map(getIDFromKey);
137
};
138
114
- if (canUseCollections) {
115
- itemMap.set(id, item);
116
- } else {
117
- var key = getKeyFromID(id);
118
- itemByKey[key] = item;
119
- }
120
-}
121
-
122
-function addRoot(id) {
123
- if (canUseCollections) {
124
- rootIDSet.add(id);
125
- } else {
139
+ var addRoot = function(id) {
140
var key = getKeyFromID(id);
141
rootByKey[key] = true;
128
- }
129
-}
130
-
131
-function removeRoot(id) {
132
- if (canUseCollections) {
133
- rootIDSet.delete(id);
134
- } else {
142
+ };
143
+ var removeRoot = function(id) {
144
var key = getKeyFromID(id);
145
delete rootByKey[key];
137
- }
138
-}
139
-
140
-function getRegisteredIDs() {
141
- if (canUseCollections) {
142
- return Array.from(itemMap.keys());
143
- } else {
144
- return Object.keys(itemByKey).map(getIDFromKey);
145
- }
146
-}
147
-
148
-function getRootIDs() {
149
- if (canUseCollections) {
150
- return Array.from(rootIDSet.keys());
151
- } else {
146
+ };
147
+ var getRootIDs = function() {
148
return Object.keys(rootByKey).map(getIDFromKey);
153
- }
149
+ };
150
}
151
152
+var unmountedIDs: Array<DebugID> = [];
153
+
154
function purgeDeep(id) {
157
- var item = get(id);
155
+ var item = getItem(id);
156
if (item) {
157
var {childIDs} = item;
160
- remove(id);
158
+ removeItem(id);
159
childIDs.forEach(purgeDeep);
160
}
161
}
162
163
function describeComponentFrame(name, source, ownerName) {
166
- return '\n in ' + name + (
164
+ return '\n in ' + (name || 'Unknown') + (
165
source ?
166
' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
167
source.lineNumber + ')' :
@@ -173,7 +171,7 @@ function describeComponentFrame(name, source, ownerName) {
171
);
172
}
173
176
-function getDisplayName(element) {
174
+function getDisplayName(element: ?ReactElement): string {
175
if (element == null) {
176
return '#empty';
177
} else if (typeof element === 'string' || typeof element === 'number') {
@@ -185,7 +183,7 @@ function getDisplayName(element) {
183
}
184
}
185
188
-function describeID(id) {
186
+function describeID(id: DebugID): string {
187
var name = ReactComponentTreeHook.getDisplayName(id);
188
var element = ReactComponentTreeHook.getElement(id);
189
var ownerID = ReactComponentTreeHook.getOwnerID(id);
@@ -203,13 +201,14 @@ function describeID(id) {
201
}
202
203
var ReactComponentTreeHook = {
206
- onSetChildren(id, nextChildIDs) {
207
- var item = get(id);
204
+ onSetChildren(id: DebugID, nextChildIDs: Array<DebugID>): void {
205
+ var item = getItem(id);
206
+ invariant(item, 'Item must have been set');
207
item.childIDs = nextChildIDs;
208
209
for (var i = 0; i < nextChildIDs.length; i++) {
210
var nextChildID = nextChildIDs[i];
212
- var nextChild = get(nextChildID);
211
+ var nextChild = getItem(nextChildID);
212
invariant(
213
nextChild,
214
'Expected hook events to fire for the child ' +
@@ -231,7 +230,7 @@ var ReactComponentTreeHook = {
230
nextChild.parentID = id;
231
// TODO: This shouldn't be necessary but mounting a new root during in
232
// componentWillMount currently causes not-yet-mounted components to
234
- // be purged from our tree data so their parent ID is missing.
233
+ // be purged from our tree data so their parent id is missing.
234
}
235
invariant(
236
nextChild.parentID === id,
@@ -244,12 +243,20 @@ var ReactComponentTreeHook = {
243
}
244
},
245
247
- onBeforeMountComponent(id, element, parentID) {
248
- create(id, element, parentID);
246
+ onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
247
+ var item = {
248
+ element,
249
+ parentID,
250
+ text: null,
251
+ childIDs: [],
252
+ isMounted: false,
253
+ updateCount: 0,
254
+ };
255
+ setItem(id, item);
256
},
257
251
- onBeforeUpdateComponent(id, element) {
252
- var item = get(id);
258
+ onBeforeUpdateComponent(id: DebugID, element: ReactElement): void {
259
+ var item = getItem(id);
260
if (!item || !item.isMounted) {
261
// We may end up here as a result of setState() in componentWillUnmount().
262
// In this case, ignore the element.
@@ -258,8 +265,9 @@ var ReactComponentTreeHook = {
265
item.element = element;
266
},
267
261
- onMountComponent(id) {
262
- var item = get(id);
268
+ onMountComponent(id: DebugID): void {
269
+ var item = getItem(id);
270
+ invariant(item, 'Item must have been set');
271
item.isMounted = true;
272
var isRoot = item.parentID === 0;
273
if (isRoot) {
@@ -267,8 +275,8 @@ var ReactComponentTreeHook = {
275
}
276
},
277
270
- onUpdateComponent(id) {
271
- var item = get(id);
278
+ onUpdateComponent(id: DebugID): void {
279
+ var item = getItem(id);
280
if (!item || !item.isMounted) {
281
// We may end up here as a result of setState() in componentWillUnmount().
282
// In this case, ignore the element.
@@ -277,8 +285,8 @@ var ReactComponentTreeHook = {
285
item.updateCount++;
286
},
287
280
- onUnmountComponent(id) {
281
- var item = get(id);
288
+ onUnmountComponent(id: DebugID): void {
289
+ var item = getItem(id);
290
if (item) {
291
// We need to check if it exists.
292
// `item` might not exist if it is inside an error boundary, and a sibling
@@ -294,7 +302,7 @@ var ReactComponentTreeHook = {
302
unmountedIDs.push(id);
303
},
304
297
- purgeUnmountedComponents() {
305
+ purgeUnmountedComponents(): void {
306
if (ReactComponentTreeHook._preventPurging) {
307
// Should only be used for testing.
308
return;
@@ -307,21 +315,18 @@ var ReactComponentTreeHook = {
315
unmountedIDs.length = 0;
316
},
317
310
- isMounted(id) {
311
- var item = get(id);
318
+ isMounted(id: DebugID): bool {
319
+ var item = getItem(id);
320
return item ? item.isMounted : false;
321
},
322
315
- getCurrentStackAddendum(topElement) {
323
+ getCurrentStackAddendum(topElement: ?ReactElement): string {
324
var info = '';
325
if (topElement) {
318
- var type = topElement.type;
319
- var name = typeof type === 'function' ?
320
- type.displayName || type.name :
321
- type;
326
+ var name = getDisplayName(topElement);
327
var owner = topElement._owner;
328
info += describeComponentFrame(
324
- name || 'Unknown',
329
+ name,
330
topElement._source,
331
owner && owner.getName()
332
);
@@ -334,7 +339,7 @@ var ReactComponentTreeHook = {
339
return info;
340
},
341
337
- getStackAddendumByID(id) {
342
+ getStackAddendumByID(id: ?DebugID): string {
343
var info = '';
344
while (id) {
345
info += describeID(id);
@@ -343,12 +348,12 @@ var ReactComponentTreeHook = {
348
return info;
349
},
350
346
- getChildIDs(id) {
347
- var item = get(id);
351
+ getChildIDs(id: DebugID): Array<DebugID> {
352
+ var item = getItem(id);
353
return item ? item.childIDs : [];
354
},
355
351
- getDisplayName(id) {
356
+ getDisplayName(id: DebugID): ?string {
357
var element = ReactComponentTreeHook.getElement(id);
358
if (!element) {
359
return null;
@@ -356,12 +361,12 @@ var ReactComponentTreeHook = {
361
return getDisplayName(element);
362
},
363
359
- getElement(id) {
360
- var item = get(id);
364
+ getElement(id: DebugID): ?ReactElement {
365
+ var item = getItem(id);
366
return item ? item.element : null;
367
},
368
364
- getOwnerID(id) {
369
+ getOwnerID(id: DebugID): ?DebugID {
370
var element = ReactComponentTreeHook.getElement(id);
371
if (!element || !element._owner) {
372
return null;
@@ -369,19 +374,19 @@ var ReactComponentTreeHook = {
374
return element._owner._debugID;
375
},
376
372
- getParentID(id) {
373
- var item = get(id);
377
+ getParentID(id: DebugID): ?DebugID {
378
+ var item = getItem(id);
379
return item ? item.parentID : null;
380
},
381
377
- getSource(id) {
378
- var item = get(id);
382
+ getSource(id: DebugID): ?Source {
383
+ var item = getItem(id);
384
var element = item ? item.element : null;
385
var source = element != null ? element._source : null;
386
return source;
387
},
388
384
- getText(id) {
389
+ getText(id: DebugID): ?string {
390
var element = ReactComponentTreeHook.getElement(id);
391
if (typeof element === 'string') {
392
return element;
@@ -392,14 +397,13 @@ var ReactComponentTreeHook = {
397
}
398
},
399
395
- getUpdateCount(id) {
396
- var item = get(id);
400
+ getUpdateCount(id: DebugID): number {
401
+ var item = getItem(id);
402
return item ? item.updateCount : 0;
403
},
404
400
- getRegisteredIDs,
401
-
405
getRootIDs,
406
+ getRegisteredIDs: getItemIDs,
407
};
408
409
module.exports = ReactComponentTreeHook;
src/renderers/shared/stack/reconciler/ReactInstanceType.js
new
+37
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Copyright 2016-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
+ * @flow
10
+ * @providesModule ReactInstanceType
11
+ */
12
+
13
+'use strict';
14
+
15
+export type DebugID = number;
16
+
17
+export type ReactInstance = {
18
+ // ReactCompositeComponent
19
+ mountComponent: any,
20
+ performInitialMountWithErrorHandling: any,
21
+ performInitialMount: any,
22
+ getHostNode: any,
23
+ unmountComponent: any,
24
+ receiveComponent: any,
25
+ performUpdateIfNecessary: any,
26
+ updateComponent: any,
27
+ attachRef: any,
28
+ detachRef: any,
29
+ getName: () => string,
30
+ getPublicInstance: any,
31
+
32
+ // instantiateReactComponent
33
+ _mountIndex: number,
34
+ _mountImage: any,
35
+ // __DEV__
36
+ _debugID: DebugID,
37
+};