Skip to content

Commit 1290d53

Browse files
committed
[RFC] Type ReactComponentTreeHook
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
1 parent 09f0a06 commit 1290d53

File tree

3 files changed

+167
-79
lines changed

3 files changed

+167
-79
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 93 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* LICENSE file in the root directory of this source tree. An additional grant
77
* of patent rights can be found in the PATENTS file in the same directory.
88
*
9+
* @flow
910
* @providesModule ReactComponentTreeHook
1011
*/
1112

@@ -16,6 +17,9 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
1617
var invariant = require('invariant');
1718
var warning = require('warning');
1819

20+
import type { ReactElement, Source } from 'ReactElementType';
21+
import type { DebugID } from 'ReactInstanceType';
22+
1923
function isNative(fn) {
2024
// Based on isNative() from Lodash
2125
var funcToString = Function.prototype.toString;
@@ -39,8 +43,19 @@ function isNative(fn) {
3943
}
4044
}
4145

42-
var itemMap;
43-
var itemByKey;
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 get(id: DebugID): ?Item;
56+
declare function remove(id: DebugID): void;
57+
declare function set(id: DebugID, item: Item): void;
58+
declare function keys(): Array<DebugID>;
4459

4560
var canUseMap = (
4661
typeof Array.from === 'function' &&
@@ -49,57 +64,52 @@ var canUseMap = (
4964
);
5065

5166
if (canUseMap) {
52-
itemMap = new Map();
53-
} else {
54-
itemByKey = {};
55-
}
56-
57-
var unmountedIDs = [];
58-
var rootIDs = [];
59-
60-
// Use non-numeric keys to prevent V8 performance issues:
61-
// https://github.com/facebook/react/pull/7232
62-
function getKeyFromID(id) {
63-
return '.' + id;
64-
}
65-
function getIDFromKey(key) {
66-
return parseInt(key.substr(1), 10);
67-
}
67+
var itemMap = new Map();
6868

69-
function get(id) {
70-
if (canUseMap) {
69+
var get = function(id) {
7170
return itemMap.get(id);
72-
}
73-
var key = getKeyFromID(id);
74-
return itemByKey[key];
75-
}
76-
77-
function remove(id) {
78-
if (canUseMap) {
71+
};
72+
var remove = function(id) {
7973
itemMap.delete(id);
80-
return;
81-
}
82-
var key = getKeyFromID(id);
83-
delete itemByKey[key];
84-
}
85-
86-
function create(id, element, parentID) {
87-
var item = {
88-
element,
89-
parentID,
90-
text: null,
91-
childIDs: [],
92-
isMounted: false,
93-
updateCount: 0,
9474
};
95-
if (canUseMap) {
75+
var set = function(id, item) {
9676
itemMap.set(id, item);
97-
return;
77+
};
78+
var keys = function() {
79+
return Array.from(itemMap.keys());
80+
};
81+
} else {
82+
var itemByKey = {};
83+
84+
// Use non-numeric keys to prevent V8 performance issues:
85+
// https://github.com/facebook/react/pull/7232
86+
function getKeyFromID(id: DebugID): string {
87+
return '.' + id;
88+
}
89+
function getIDFromKey(key: string): DebugID {
90+
return parseInt(key.substr(1), 10);
9891
}
99-
var key = getKeyFromID(id);
100-
itemByKey[key] = item;
92+
93+
var get = function(id) {
94+
var key = getKeyFromID(id);
95+
return itemByKey[key];
96+
};
97+
var remove = function(id) {
98+
var key = getKeyFromID(id);
99+
delete itemByKey[key];
100+
};
101+
var set = function(id, item) {
102+
var key = getKeyFromID(id);
103+
itemByKey[key] = item;
104+
};
105+
var keys = function() {
106+
return Object.keys(itemByKey).map(getIDFromKey);
107+
};
101108
}
102109

110+
var unmountedIDs: Array<DebugID> = [];
111+
var rootIDs: Array<DebugID> = [];
112+
103113
function purgeDeep(id) {
104114
var item = get(id);
105115
if (item) {
@@ -110,7 +120,7 @@ function purgeDeep(id) {
110120
}
111121

112122
function describeComponentFrame(name, source, ownerName) {
113-
return '\n in ' + name + (
123+
return '\n in ' + (name || 'Unknown') + (
114124
source ?
115125
' (at ' + source.fileName.replace(/^.*[\\\/]/, '') + ':' +
116126
source.lineNumber + ')' :
@@ -120,7 +130,7 @@ function describeComponentFrame(name, source, ownerName) {
120130
);
121131
}
122132

123-
function getDisplayName(element) {
133+
function getDisplayName(element: ?ReactElement): string {
124134
if (element == null) {
125135
return '#empty';
126136
} else if (typeof element === 'string' || typeof element === 'number') {
@@ -132,7 +142,7 @@ function getDisplayName(element) {
132142
}
133143
}
134144

135-
function describeID(id) {
145+
function describeID(id: DebugID): string {
136146
var name = ReactComponentTreeHook.getDisplayName(id);
137147
var element = ReactComponentTreeHook.getElement(id);
138148
var ownerID = ReactComponentTreeHook.getOwnerID(id);
@@ -150,8 +160,9 @@ function describeID(id) {
150160
}
151161

152162
var ReactComponentTreeHook = {
153-
onSetChildren(id, nextChildIDs) {
163+
onSetChildren(id: DebugID, nextChildIDs: Array<DebugID>): void {
154164
var item = get(id);
165+
invariant(item, 'Item must have been set');
155166
item.childIDs = nextChildIDs;
156167

157168
for (var i = 0; i < nextChildIDs.length; i++) {
@@ -178,7 +189,7 @@ var ReactComponentTreeHook = {
178189
nextChild.parentID = id;
179190
// TODO: This shouldn't be necessary but mounting a new root during in
180191
// componentWillMount currently causes not-yet-mounted components to
181-
// be purged from our tree data so their parent ID is missing.
192+
// be purged from our tree data so their parent DebugID is missing.
182193
}
183194
invariant(
184195
nextChild.parentID === id,
@@ -191,11 +202,19 @@ var ReactComponentTreeHook = {
191202
}
192203
},
193204

194-
onBeforeMountComponent(id, element, parentID) {
195-
create(id, element, parentID);
205+
onBeforeMountComponent(id: DebugID, element: ReactElement, parentID: DebugID): void {
206+
var item = {
207+
element,
208+
parentID,
209+
text: null,
210+
childIDs: [],
211+
isMounted: false,
212+
updateCount: 0,
213+
};
214+
set(id, item);
196215
},
197216

198-
onBeforeUpdateComponent(id, element) {
217+
onBeforeUpdateComponent(id: DebugID, element: ReactElement): void {
199218
var item = get(id);
200219
if (!item || !item.isMounted) {
201220
// We may end up here as a result of setState() in componentWillUnmount().
@@ -205,15 +224,16 @@ var ReactComponentTreeHook = {
205224
item.element = element;
206225
},
207226

208-
onMountComponent(id) {
227+
onMountComponent(id: DebugID): void {
209228
var item = get(id);
229+
invariant(item, 'Item must have been set');
210230
item.isMounted = true;
211231
if (item.parentID === 0) {
212232
rootIDs.push(id);
213233
}
214234
},
215235

216-
onUpdateComponent(id) {
236+
onUpdateComponent(id: DebugID): void {
217237
var item = get(id);
218238
if (!item || !item.isMounted) {
219239
// We may end up here as a result of setState() in componentWillUnmount().
@@ -223,7 +243,7 @@ var ReactComponentTreeHook = {
223243
item.updateCount++;
224244
},
225245

226-
onUnmountComponent(id) {
246+
onUnmountComponent(id: DebugID): void {
227247
var item = get(id);
228248
if (item) {
229249
// We need to check if it exists.
@@ -242,7 +262,7 @@ var ReactComponentTreeHook = {
242262
unmountedIDs.push(id);
243263
},
244264

245-
purgeUnmountedComponents() {
265+
purgeUnmountedComponents(): void {
246266
if (ReactComponentTreeHook._preventPurging) {
247267
// Should only be used for testing.
248268
return;
@@ -255,21 +275,18 @@ var ReactComponentTreeHook = {
255275
unmountedIDs.length = 0;
256276
},
257277

258-
isMounted(id) {
278+
isMounted(id: DebugID): bool {
259279
var item = get(id);
260280
return item ? item.isMounted : false;
261281
},
262282

263-
getCurrentStackAddendum(topElement) {
283+
getCurrentStackAddendum(topElement: ?ReactElement): string {
264284
var info = '';
265285
if (topElement) {
266-
var type = topElement.type;
267-
var name = typeof type === 'function' ?
268-
type.displayName || type.name :
269-
type;
286+
var name = getDisplayName(topElement);
270287
var owner = topElement._owner;
271288
info += describeComponentFrame(
272-
name || 'Unknown',
289+
name,
273290
topElement._source,
274291
owner && owner.getName()
275292
);
@@ -282,7 +299,7 @@ var ReactComponentTreeHook = {
282299
return info;
283300
},
284301

285-
getStackAddendumByID(id) {
302+
getStackAddendumByID(id: ?DebugID): string {
286303
var info = '';
287304
while (id) {
288305
info += describeID(id);
@@ -291,45 +308,45 @@ var ReactComponentTreeHook = {
291308
return info;
292309
},
293310

294-
getChildIDs(id) {
311+
getChildIDs(id: DebugID): Array<DebugID> {
295312
var item = get(id);
296313
return item ? item.childIDs : [];
297314
},
298315

299-
getDisplayName(id) {
316+
getDisplayName(id: DebugID): ?string {
300317
var element = ReactComponentTreeHook.getElement(id);
301318
if (!element) {
302319
return null;
303320
}
304321
return getDisplayName(element);
305322
},
306323

307-
getElement(id) {
324+
getElement(id: DebugID): ?ReactElement {
308325
var item = get(id);
309326
return item ? item.element : null;
310327
},
311328

312-
getOwnerID(id) {
329+
getOwnerID(id: DebugID): ?DebugID {
313330
var element = ReactComponentTreeHook.getElement(id);
314331
if (!element || !element._owner) {
315332
return null;
316333
}
317334
return element._owner._debugID;
318335
},
319336

320-
getParentID(id) {
337+
getParentID(id: DebugID): ?DebugID {
321338
var item = get(id);
322339
return item ? item.parentID : null;
323340
},
324341

325-
getSource(id) {
342+
getSource(id: DebugID): ?Source {
326343
var item = get(id);
327344
var element = item ? item.element : null;
328345
var source = element != null ? element._source : null;
329346
return source;
330347
},
331348

332-
getText(id) {
349+
getText(id: DebugID): ?string {
333350
var element = ReactComponentTreeHook.getElement(id);
334351
if (typeof element === 'string') {
335352
return element;
@@ -340,20 +357,17 @@ var ReactComponentTreeHook = {
340357
}
341358
},
342359

343-
getUpdateCount(id) {
360+
getUpdateCount(id: DebugID): number {
344361
var item = get(id);
345362
return item ? item.updateCount : 0;
346363
},
347364

348-
getRootIDs() {
365+
getRootIDs(): Array<DebugID> {
349366
return rootIDs;
350367
},
351368

352-
getRegisteredIDs() {
353-
if (canUseMap) {
354-
return Array.from(itemMap.keys());
355-
}
356-
return Object.keys(itemByKey).map(getIDFromKey);
369+
getRegisteredIDs(): Array<DebugID> {
370+
return keys();
357371
},
358372
};
359373

0 commit comments

Comments
 (0)