Change:
In my javascript frameworks benchmark I ran into a performance regression for react. To reproduce open and open the console. Repeat three times the following: Click "Create 10,000" then click "Clear" You'll get a result like that: runLots took 1905. clear took 1997. runLots took 1813. clear took 4208.895 runLots took 1806. clear took 401. Now repeat the same for and you'll get something like that: unLots took 2938. clear took 399. runLots took 2726.365 clear took 383. runLots took 2872.625 clear took 381. (Measurement on Chrome 51 on MacOS 10.11.5 on a MBP 15) Notice that clear is a lot slower for the first two runs for React v 15, but the third clear run performs about the same for both react versions. The source code can be found in the of my benchmark.
In general we don’t advise you to render 10K items. Use something like instead. However, this indeed seems like a perf regression. <sIt appears that we’re spending a little time in per each item, and it adds up with 10K items.</s(Scratch that, doesn’t appear to be the cause.)
This is a very simple scenario: we are unmounting 10,000 components. The bottleneck is somewhere in reconciliation. React 0.14 is reconciling fast right away: <img width="762" alt="screen shot 2016-07-08 at 22 06 01" src=""React 15 is reconciling slow at first (notice how long is compared to applying DOM updates): <img width="1097" alt="screen shot 2016-07-08 at 22 03 56" src=""But it warms up exactly after two mount-unmount cycles (notice how its size is less relative to DOM updates): <img width="928" alt="screen shot 2016-07-08 at 22 04 07" src=""
It looks like there is some sort of V8 optimization that doesn’t kick in until you unmount a lot of components. I was able to drastically reduce time (from 2 seconds to 300 milliseconds) on React 15 by replacing statements with assignment in these two places:   This is probably not a real solution (I think just leaving them as means memory keeps growing, even if just for property keys). Still, I wonder why 0.14.x is not similarly penalized.
Replacing objects with ES6 Maps gives similar performance improvements there.
This was fixed in . Thank you very much for caring and providing specific instructions.
As a quick experiment, I ran the benchmarks under Firefox: both versions ran in the same speed as the 15.2 in Chrome (I mean: slow). I surely hope the fix would fix it in Firefox too !
Maybe putting these fields on the internal instances instead of global maps will help it in Firefox. Would you like to investigate why it's slow?
I gave it a look, and Firefox slowness is due to DOM being slow. I don’t think we can fix it.
I'd really appreciate a Firefox bug report with testcase here!
I filed with the information of this bug. I don't have the time to do a reduced testcase now though.
FWIW, removeChild is slow in Gecko because of layout. No idea whether setting display: none before removal or some such might speed up the process. It might help if done on the element on which removeChild is being called.
Thank you for filing!

Discussion:
var ON_CLICK_KEY = keyOf({onClick: null}); var onClickListeners = {}; <ins> function getDictionaryKey(inst) { return '.' + inst._rootNodeID; } </ins> var SimpleEventPlugin = { eventTypes: eventTypes,

// fire. The workaround for this bug involves attaching an empty click // listener on the target node. if (registrationName === ON_CLICK_KEY) { <del> var id = inst._rootNodeID; </del> <ins> var key = getDictionaryKey(inst); </ins> var node = ReactDOMComponentTree.getNodeFromInstance(inst); <del> if (!onClickListeners[id]) { onClickListeners[id] = EventListener.listen( </del> <ins> if (!onClickListeners[key]) { onClickListeners[key] = EventListener.listen( </ins> node, 'click', emptyFunction

willDeleteListener: function(inst, registrationName) { if (registrationName === ON_CLICK_KEY) { <del> var id = inst._rootNodeID; onClickListeners[id].remove(); delete onClickListeners[id]; </del> <ins> var key = getDictionaryKey(inst); onClickListeners[key].remove(); delete onClickListeners[key]; </ins> } },

return executeDispatchesAndRelease(e, false); }; <ins> var getDictionaryKey = function(inst) { return '.' + inst._rootNodeID; }; </ins> /** * This is a unified interface for event plugins to be installed and configured. *

}, /** <del> * Stores `listener` at `listenerBank[registrationName][id]`. Is idempotent. </del> <ins> * Stores `listener` at `listenerBank[registrationName][key]`. Is idempotent. </ins> * * @param {object} inst The instance, which is the source of events. * @param {string} registrationName Name of listener (e.g. `onClick`).

registrationName, typeof listener ); <ins> var key = getDictionaryKey(inst); </ins> var bankForRegistrationName = listenerBank[registrationName] || (listenerBank[registrationName] = {}); <del> bankForRegistrationName[inst._rootNodeID] = listener; </del> <ins> bankForRegistrationName[key] = listener; </ins> var PluginModule = EventPluginRegistry.registrationNameModules[registrationName];

*/ getListener: function(inst, registrationName) { var bankForRegistrationName = listenerBank[registrationName]; <del> return bankForRegistrationName && bankForRegistrationName[inst._rootNodeID]; </del> <ins> var key = getDictionaryKey(inst); return bankForRegistrationName && bankForRegistrationName[key]; </ins> }, /**

var bankForRegistrationName = listenerBank[registrationName]; // TODO: This should never be null -- when is it? if (bankForRegistrationName) { <del> delete bankForRegistrationName[inst._rootNodeID]; </del> <ins> var key = getDictionaryKey(inst); delete bankForRegistrationName[key]; </ins> } },

* @param {object} inst The instance, which is the source of events. */ deleteAllListeners: function(inst) { <ins> var key = getDictionaryKey(inst); </ins> for (var registrationName in listenerBank) { if (!listenerBank.hasOwnProperty(registrationName)) { continue; } <del> if (!listenerBank[registrationName][inst._rootNodeID]) { </del> <ins> if (!listenerBank[registrationName][key]) { </ins> continue; }

PluginModule.willDeleteListener(inst, registrationName); } <del> delete listenerBank[registrationName][inst._rootNodeID]; </del> <ins> delete listenerBank[registrationName][key]; </ins> } },