@samitouri / QOS-React-2 / commits / d8fd1af4a0

Pool ancestors list so event system is reentrant

Ben Alpert committed Feb 17, 2014 at 15:07 UTC d8fd1af4a0102d5ace78300cd1b5648c45ea6d66
1 file changed +52 -31
src/browser/ReactEventTopLevelCallback.js
+52 -31
@@ -19,11 +19,13 @@
19
20 "use strict";
21
22 +var PooledClass = require('PooledClass');
23 var ReactEventEmitter = require('ReactEventEmitter');
24 var ReactInstanceHandles = require('ReactInstanceHandles');
25 var ReactMount = require('ReactMount');
26
27 var getEventTarget = require('getEventTarget');
28 +var mixInto = require('mixInto');
29
30 /**
31 * @type {boolean}
@@ -49,8 +51,52 @@ function findParent(node) {
51 return parent;
52 }
53
54 +/**
55 + * Calls ReactEventEmitter.handleTopLevel for each node stored in bookKeeping's
56 + * ancestor list. Separated from createTopLevelCallback to avoid try/finally
57 + * deoptimization.
58 + *
59 + * @param {string} topLevelType
60 + * @param {DOMEvent} nativeEvent
61 + * @param {TopLevelCallbackBookKeeping} bookKeeping
62 + */
63 +function handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping) {
64 + var topLevelTarget = ReactMount.getFirstReactDOM(
65 + getEventTarget(nativeEvent)
66 + ) || window;
67 +
68 + // Loop through the hierarchy, in case there's any nested components.
69 + // It's important that we build the array of ancestors before calling any
70 + // event handlers, because event handlers can modify the DOM, leading to
71 + // inconsistencies with ReactMount's node cache. See #1105.
72 + var ancestor = topLevelTarget;
73 + while (ancestor) {
74 + bookKeeping.ancestors.push(ancestor);
75 + ancestor = findParent(ancestor);
76 + }
77 +
78 + for (var i = 0, l = bookKeeping.ancestors.length; i < l; i++) {
79 + topLevelTarget = bookKeeping.ancestors[i];
80 + var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
81 + ReactEventEmitter.handleTopLevel(
82 + topLevelType,
83 + topLevelTarget,
84 + topLevelTargetID,
85 + nativeEvent
86 + );
87 + }
88 +}
89 +
90 // Used to store ancestor hierarchy in top level callback
53 -var topLevelCallbackReusableArray = [];
91 +function TopLevelCallbackBookKeeping() {
92 + this.ancestors = [];
93 +}
94 +mixInto(TopLevelCallbackBookKeeping, {
95 + destructor: function() {
96 + this.ancestors.length = 0;
97 + }
98 +});
99 +PooledClass.addPoolingTo(TopLevelCallbackBookKeeping);
100
101 /**
102 * Top-level callback creator used to implement event handling using delegation.
@@ -88,38 +134,13 @@ var ReactEventTopLevelCallback = {
134 if (!_topLevelListenersEnabled) {
135 return;
136 }
91 - var topLevelTarget = ReactMount.getFirstReactDOM(
92 - getEventTarget(nativeEvent)
93 - ) || window;
94 -
95 - var ancestors = topLevelCallbackReusableArray;
96 - ancestors.length = 0;
97 -
98 - // Loop through the hierarchy, in case there's any nested components.
99 - // It's important that we build the array of ancestors before calling any
100 - // event handlers, because event handlers can modify the DOM, leading to
101 - // inconsistencies with ReactMount's node cache. See #1105.
102 - var ancestor = topLevelTarget;
103 - while (ancestor) {
104 - ancestors.push(ancestor);
105 - ancestor = findParent(ancestor);
106 - }
137
108 - for (var i = 0, l = ancestors.length; i < l; i++) {
109 - topLevelTarget = ancestors[i];
110 - var topLevelTargetID = ReactMount.getID(topLevelTarget) || '';
111 - ReactEventEmitter.handleTopLevel(
112 - topLevelType,
113 - topLevelTarget,
114 - topLevelTargetID,
115 - nativeEvent
116 - );
138 + var bookKeeping = TopLevelCallbackBookKeeping.getPooled();
139 + try {
140 + handleTopLevelImpl(topLevelType, nativeEvent, bookKeeping);
141 + } finally {
142 + TopLevelCallbackBookKeeping.release(bookKeeping);
143 }
118 -
119 - // Emptying ancestors/topLevelCallbackReusableArray is
120 - // not necessary for correctness, but it helps the GC reclaim
121 - // any nodes that were left at the end of the search.
122 - ancestors.length = 0;
144 };
145 }
146