The library for web and native user interfaces.
Use Node Cache in ReactComponent
There are currently two places where we lookup and cache nodes: `ReactDOMNodeCache` and `ReactComponent`. Instead, we should just consolidate caches and make `ReactComponent` use `ReactDOMNodeCache`.
CommitSyncScript committed
Jun 14, 2013 at 16:22 UTC
8592eacbf9c94e48c0133c2124414f5910ba42b9
4 files changed
+66
-43
src/core/ReactComponent.js
+3
-16
@@ -18,9 +18,9 @@
18
19
"use strict";
20
21
-var ExecutionEnvironment = require('ExecutionEnvironment');
21
var ReactCurrentOwner = require('ReactCurrentOwner');
22
var ReactDOMIDOperations = require('ReactDOMIDOperations');
23
+var ReactDOMNodeCache = require('ReactDOMNodeCache');
24
var ReactMount = require('ReactMount');
25
var ReactOwner = require('ReactOwner');
26
var ReactReconcileTransaction = require('ReactReconcileTransaction');
@@ -207,24 +207,11 @@ var ReactComponent = {
207
* @protected
208
*/
209
getDOMNode: function() {
210
- invariant(
211
- ExecutionEnvironment.canUseDOM,
212
- 'getDOMNode(): The DOM is not supported in the current environment.'
213
- );
210
invariant(
211
this.isMounted(),
212
'getDOMNode(): A component must be mounted to have a DOM node.'
213
);
218
- var rootNode = this._rootNode;
219
- if (!rootNode) {
220
- rootNode = document.getElementById(this._rootNodeID);
221
- if (!rootNode) {
222
- // TODO: Log the frequency that we reach this path.
223
- rootNode = ReactMount.findReactRenderedDOMNodeSlow(this._rootNodeID);
224
- }
225
- this._rootNode = rootNode;
226
- }
227
- return rootNode;
214
+ return ReactDOMNodeCache.getNodeByID(this._rootNodeID);
215
},
216
217
/**
@@ -372,7 +359,7 @@ var ReactComponent = {
359
if (props.ref != null) {
360
ReactOwner.removeComponentAsRefFrom(this, props.ref, props[OWNER]);
361
}
375
- this._rootNode = null;
362
+ ReactDOMNodeCache.purgeID(this._rootNodeID);
363
this._rootNodeID = null;
364
this._lifeCycleState = ComponentLifeCycle.UNMOUNTED;
365
},
src/core/ReactDOMIDOperations.js
+8
-8
@@ -66,7 +66,7 @@ var ReactDOMIDOperations = {
66
* @internal
67
*/
68
updatePropertyByID: function(id, name, value) {
69
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
69
+ var node = ReactDOMNodeCache.getNodeByID(id);
70
invariant(
71
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
72
'updatePropertyByID(...): %s',
@@ -84,7 +84,7 @@ var ReactDOMIDOperations = {
84
* @internal
85
*/
86
deletePropertyByID: function(id, name, value) {
87
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
87
+ var node = ReactDOMNodeCache.getNodeByID(id);
88
invariant(
89
!INVALID_PROPERTY_ERRORS.hasOwnProperty(name),
90
'updatePropertyByID(...): %s',
@@ -121,7 +121,7 @@ var ReactDOMIDOperations = {
121
* @internal
122
*/
123
updateStylesByID: function(id, styles) {
124
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
124
+ var node = ReactDOMNodeCache.getNodeByID(id);
125
CSSPropertyOperations.setValueForStyles(node, styles);
126
},
127
@@ -133,7 +133,7 @@ var ReactDOMIDOperations = {
133
* @internal
134
*/
135
updateInnerHTMLByID: function(id, html) {
136
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
136
+ var node = ReactDOMNodeCache.getNodeByID(id);
137
// HACK: IE8- normalize whitespace in innerHTML, removing leading spaces.
138
// @see quirksmode.org/bugreports/archives/2004/11/innerhtml_and_t.html
139
node.innerHTML = (html && html.__html || '').replace(/^ /g, ' ');
@@ -147,7 +147,7 @@ var ReactDOMIDOperations = {
147
* @internal
148
*/
149
updateTextContentByID: function(id, content) {
150
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
150
+ var node = ReactDOMNodeCache.getNodeByID(id);
151
node[textContentAccessor] = content;
152
},
153
@@ -160,7 +160,7 @@ var ReactDOMIDOperations = {
160
* @see {Danger.dangerouslyReplaceNodeWithMarkup}
161
*/
162
dangerouslyReplaceNodeWithMarkupByID: function(id, markup) {
163
- var node = ReactDOMNodeCache.getCachedNodeByID(id);
163
+ var node = ReactDOMNodeCache.getNodeByID(id);
164
DOMChildrenOperations.dangerouslyReplaceNodeWithMarkup(node, markup);
165
ReactDOMNodeCache.purgeEntireCache();
166
},
@@ -170,13 +170,13 @@ var ReactDOMIDOperations = {
170
* Detect if any elements were removed instead of blindly purging.
171
*/
172
manageChildrenByParentID: function(parentID, domOperations) {
173
- var parent = ReactDOMNodeCache.getCachedNodeByID(parentID);
173
+ var parent = ReactDOMNodeCache.getNodeByID(parentID);
174
DOMChildrenOperations.manageChildren(parent, domOperations);
175
ReactDOMNodeCache.purgeEntireCache();
176
},
177
178
setTextNodeValueAtIndexByParentID: function(parentID, index, value) {
179
- var parent = ReactDOMNodeCache.getCachedNodeByID(parentID);
179
+ var parent = ReactDOMNodeCache.getNodeByID(parentID);
180
DOMChildrenOperations.setTextNodeValueAtIndex(parent, index, value);
181
}
182
src/core/ReactDOMNodeCache.js
+51
-15
@@ -14,39 +14,75 @@
14
* limitations under the License.
15
*
16
* @providesModule ReactDOMNodeCache
17
+ * @typechecks
18
*/
19
20
"use strict";
21
22
+var ExecutionEnvironment = require('ExecutionEnvironment');
23
var ReactMount = require('ReactMount');
24
25
+var invariant = require('invariant');
26
+
27
var nodeCache = {};
28
29
/**
30
* DOM node cache only intended for use by React. Placed into a shared module so
31
* that both read and write utilities may benefit from a shared cache.
32
+ *
33
+ * @internal
34
*/
35
var ReactDOMNodeCache = {
36
+
37
+ /**
38
+ * Finds the node with the supplied React-generated DOM ID.
39
+ *
40
+ * @param {string} id A React-generated DOM ID.
41
+ * @return {?DOMElement} DOM node with the suppled `id`.
42
+ * @internal
43
+ */
44
+ getNodeByID: function(id) {
45
+ invariant(
46
+ ExecutionEnvironment.canUseDOM,
47
+ 'getDOMNode(): The DOM is not supported in the current environment.'
48
+ );
49
+ if (!nodeCache[id]) {
50
+ nodeCache[id] =
51
+ document.getElementById(id) ||
52
+ ReactMount.findReactRenderedDOMNodeSlow(id);
53
+ }
54
+ return nodeCache[id];
55
+ },
56
+
57
/**
31
- * Releases fast id lookups (node/style cache). This implementation is
32
- * aggressive with purging because the bookkeeping associated with doing fine
33
- * grained deleted from the cache may outweight the benefits of the cache. The
34
- * heuristic that should be used to purge is 'any time anything is deleted'.
35
- * Typically this means that a large amount of content is being replaced and
36
- * several elements would need purging regardless. It's also a time when an
37
- * application is likely not in the middle of a "smooth operation" (such as
38
- * animating/scrolling).
58
+ * Purges the supplied ID from cache.
59
+ *
60
+ * @param {string} id A React-generated DOM ID.
61
+ * @internal
62
+ */
63
+ purgeID: function(id) {
64
+ nodeCache[id] = null;
65
+ },
66
+
67
+ /**
68
+ * Purges the entire node cache used for fast ID lookups.
69
+ *
70
+ * This implementation is aggressive with purging because the bookkeeping
71
+ * associated with doing fine-grained deletes from the cache may outweight the
72
+ * benefits of the cache.
73
+ *
74
+ * The heuristic used to purge is 'any time anything is deleted'. Typically
75
+ * this means that a large amount of content is being replaced and several
76
+ * elements would need purging regardless. This is also when applications are
77
+ * less likely to be in the middle of "smooth operations" such as animations
78
+ * or scrolling.
79
+ *
80
+ * @internal
81
*/
82
purgeEntireCache: function() {
83
nodeCache = {};
42
- return nodeCache;
43
- },
44
- getCachedNodeByID: function(id) {
45
- return nodeCache[id] ||
46
- (nodeCache[id] =
47
- document.getElementById(id) ||
48
- ReactMount.findReactRenderedDOMNodeSlow(id));
84
}
85
+
86
};
87
88
module.exports = ReactDOMNodeCache;
src/core/__tests__/ReactDOMIDOperations-test.js
+4
-4
@@ -27,7 +27,7 @@ describe('ReactDOMIDOperations', function() {
27
var keyOf = require('keyOf');
28
29
it('should disallow updating special properties', function() {
30
- spyOn(ReactDOMNodeCache, "getCachedNodeByID");
30
+ spyOn(ReactDOMNodeCache, "getNodeByID");
31
spyOn(DOMPropertyOperations, "setValueForProperty");
32
33
expect(function() {
@@ -39,7 +39,7 @@ describe('ReactDOMIDOperations', function() {
39
}).toThrow();
40
41
expect(
42
- ReactDOMNodeCache.getCachedNodeByID.argsForCall[0][0]
42
+ ReactDOMNodeCache.getNodeByID.argsForCall[0][0]
43
).toBe('testID');
44
45
expect(
@@ -49,7 +49,7 @@ describe('ReactDOMIDOperations', function() {
49
50
it('should update innerHTML and special-case whitespace', function() {
51
var stubNode = document.createElement('div');
52
- spyOn(ReactDOMNodeCache, "getCachedNodeByID").andReturn(stubNode);
52
+ spyOn(ReactDOMNodeCache, "getNodeByID").andReturn(stubNode);
53
54
ReactDOMIDOperations.updateInnerHTMLByID(
55
'testID',
@@ -57,7 +57,7 @@ describe('ReactDOMIDOperations', function() {
57
);
58
59
expect(
60
- ReactDOMNodeCache.getCachedNodeByID.argsForCall[0][0]
60
+ ReactDOMNodeCache.getNodeByID.argsForCall[0][0]
61
).toBe('testID');
62
63
expect(stubNode.innerHTML).toBe(' testContent');