@samitouri / QOS-React / commits / 48475787b4

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