Avoid indexOf() during unmounting a root in the hook (#7496)
* Avoid indexOf() during unmounting a root in the hook * Check for (Map|Set).prototype.keys
Dan Abramov committed
Aug 15, 2016 at 15:47 UTC
48475787b470f559be0a10229510fdb2c4640b42
2 files changed
+84
-35
src/isomorphic/hooks/ReactComponentTreeHook.js
+79
-34
@@ -39,23 +39,40 @@ function isNative(fn) {
39
}
40
}
41
42
-var itemMap;
43
-var itemByKey;
44
-
45
-var canUseMap = (
42
+var canUseCollections = (
43
+ // Array.from
44
typeof Array.from === 'function' &&
45
+ // Map
46
typeof Map === 'function' &&
48
- isNative(Map)
47
+ isNative(Map) &&
48
+ // Map.prototype.keys
49
+ Map.prototype != null &&
50
+ typeof Map.prototype.keys === 'function' &&
51
+ isNative(Map.prototype.keys) &&
52
+ // Set
53
+ typeof Set === 'function' &&
54
+ isNative(Set) &&
55
+ // Set.prototype.keys
56
+ Set.prototype != null &&
57
+ typeof Set.prototype.keys === 'function' &&
58
+ isNative(Set.prototype.keys)
59
);
60
51
-if (canUseMap) {
61
+var itemMap;
62
+var rootIDSet;
63
+
64
+var itemByKey;
65
+var rootByKey;
66
+
67
+if (canUseCollections) {
68
itemMap = new Map();
69
+ rootIDSet = new Set();
70
} else {
71
itemByKey = {};
72
+ rootByKey = {};
73
}
74
75
var unmountedIDs = [];
58
-var rootIDs = [];
76
77
// Use non-numeric keys to prevent V8 performance issues:
78
// https://github.com/facebook/react/pull/7232
@@ -67,20 +84,21 @@ function getIDFromKey(key) {
84
}
85
86
function get(id) {
70
- if (canUseMap) {
87
+ if (canUseCollections) {
88
return itemMap.get(id);
89
+ } else {
90
+ var key = getKeyFromID(id);
91
+ return itemByKey[key];
92
}
73
- var key = getKeyFromID(id);
74
- return itemByKey[key];
93
}
94
95
function remove(id) {
78
- if (canUseMap) {
96
+ if (canUseCollections) {
97
itemMap.delete(id);
80
- return;
98
+ } else {
99
+ var key = getKeyFromID(id);
100
+ delete itemByKey[key];
101
}
82
- var key = getKeyFromID(id);
83
- delete itemByKey[key];
102
}
103
104
function create(id, element, parentID) {
@@ -92,12 +110,47 @@ function create(id, element, parentID) {
110
isMounted: false,
111
updateCount: 0,
112
};
95
- if (canUseMap) {
113
+
114
+ if (canUseCollections) {
115
itemMap.set(id, item);
97
- return;
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 {
126
+ var key = getKeyFromID(id);
127
+ rootByKey[key] = true;
128
+ }
129
+}
130
+
131
+function removeRoot(id) {
132
+ if (canUseCollections) {
133
+ rootIDSet.delete(id);
134
+ } else {
135
+ var key = getKeyFromID(id);
136
+ 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 {
152
+ return Object.keys(rootByKey).map(getIDFromKey);
153
}
99
- var key = getKeyFromID(id);
100
- itemByKey[key] = item;
154
}
155
156
function purgeDeep(id) {
@@ -208,8 +261,9 @@ var ReactComponentTreeHook = {
261
onMountComponent(id) {
262
var item = get(id);
263
item.isMounted = true;
211
- if (item.parentID === 0) {
212
- rootIDs.push(id);
264
+ var isRoot = item.parentID === 0;
265
+ if (isRoot) {
266
+ addRoot(id);
267
}
268
},
269
@@ -232,11 +286,9 @@ var ReactComponentTreeHook = {
286
// got a chance to mount, but it still gets an unmounting event during
287
// the error boundary cleanup.
288
item.isMounted = false;
235
- if (item.parentID === 0) {
236
- var indexInRootIDs = rootIDs.indexOf(id);
237
- if (indexInRootIDs !== -1) {
238
- rootIDs.splice(indexInRootIDs, 1);
239
- }
289
+ var isRoot = item.parentID === 0;
290
+ if (isRoot) {
291
+ removeRoot(id);
292
}
293
}
294
unmountedIDs.push(id);
@@ -345,16 +397,9 @@ var ReactComponentTreeHook = {
397
return item ? item.updateCount : 0;
398
},
399
348
- getRootIDs() {
349
- return rootIDs;
350
- },
400
+ getRegisteredIDs,
401
352
- getRegisteredIDs() {
353
- if (canUseMap) {
354
- return Array.from(itemMap.keys());
355
- }
356
- return Object.keys(itemByKey).map(getIDFromKey);
357
- },
402
+ getRootIDs,
403
};
404
405
module.exports = ReactComponentTreeHook;
src/renderers/shared/hooks/__tests__/ReactComponentTreeHook-test.js
+5
-1
@@ -1870,15 +1870,18 @@ describe('ReactComponentTreeHook', () => {
1870
});
1871
});
1872
1873
- describe('in environment without Map and Array.from', () => {
1873
+ describe('in environment without Map, Set and Array.from', () => {
1874
var realMap;
1875
+ var realSet;
1876
var realArrayFrom;
1877
1878
beforeEach(() => {
1879
realMap = global.Map;
1880
+ realSet = global.Set;
1881
realArrayFrom = Array.from;
1882
1883
global.Map = undefined;
1884
+ global.Set = undefined;
1885
Array.from = undefined;
1886
1887
jest.resetModuleRegistry();
@@ -1893,6 +1896,7 @@ describe('ReactComponentTreeHook', () => {
1896
1897
afterEach(() => {
1898
global.Map = realMap;
1899
+ global.Set = realSet;
1900
Array.from = realArrayFrom;
1901
});
1902