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

Unbreak refs

If no refs are rendered, `this.refs` is undefined. This is bad since it deopts & is hard to look for. Instead we should make `this.refs` an immutable empty object.

Pete Hunt committed Mar 3, 2014 at 09:41 UTC 6666538316c0a7384b4ceca01aa364c9f615c9d0
4 files changed +26 -6
src/core/ReactCompositeComponent.js
+2 -4
@@ -767,6 +767,7 @@ var ReactCompositeComponentMixin = {
767 construct: function(initialProps, children) {
768 // Children can be either an array or more than one argument
769 ReactComponent.Mixin.construct.apply(this, arguments);
770 + ReactOwner.Mixin.construct.apply(this, arguments);
771
772 this.state = null;
773 this._pendingState = null;
@@ -873,10 +874,7 @@ var ReactCompositeComponentMixin = {
874 this._renderedComponent = null;
875
876 ReactComponent.Mixin.unmountComponent.call(this);
876 -
877 - if (this.refs) {
878 - this.refs = null;
879 - }
877 + ReactOwner.Mixin.unmountComponent.call(this);
878
879 // Some existing components rely on this.props even after they've been
880 // destroyed (in event handlers).
src/core/ReactOwner.js
+10 -1
@@ -18,6 +18,7 @@
18
19 "use strict";
20
21 +var emptyObject = require('emptyObject');
22 var invariant = require('invariant');
23
24 /**
@@ -118,6 +119,10 @@ var ReactOwner = {
119 */
120 Mixin: {
121
122 + construct: function() {
123 + this.refs = emptyObject;
124 + },
125 +
126 /**
127 * Lazily allocates the refs object and stores `component` as `ref`.
128 *
@@ -132,7 +137,7 @@ var ReactOwner = {
137 'attachRef(%s, ...): Only a component\'s owner can store a ref to it.',
138 ref
139 );
135 - var refs = this.refs || (this.refs = {});
140 + var refs = this.refs === emptyObject ? (this.refs = {}) : this.refs;
141 refs[ref] = component;
142 },
143
@@ -145,6 +150,10 @@ var ReactOwner = {
150 */
151 detachRef: function(ref) {
152 delete this.refs[ref];
153 + },
154 +
155 + unmountComponent: function() {
156 + this.refs = null;
157 }
158
159 }
src/core/__tests__/refs-test.js
+12
@@ -236,5 +236,17 @@ describe('ref swapping', function() {
236 expect(refHopsAround.refs.divTwoRef).toEqual(secondDiv);
237 expect(refHopsAround.refs.divThreeRef).toEqual(thirdDiv);
238 });
239 +
240 +
241 + it('always has a value for this.refs', function() {
242 + var Component = React.createClass({
243 + render: function() {
244 + return <div />;
245 + }
246 + });
247 +
248 + var instance = ReactTestUtils.renderIntoDocument(<Component />);
249 + expect(!!instance.refs).toBe(true);
250 + });
251 });
252
src/utils/__tests__/cloneWithProps-test.js
+2 -1
@@ -24,6 +24,7 @@ require('mock-modules').dontMock('cloneWithProps');
24 var mocks = require('mocks');
25
26 var cloneWithProps = require('cloneWithProps');
27 +var emptyObject = require('emptyObject');
28
29 var React;
30 var ReactTestUtils;
@@ -107,7 +108,7 @@ describe('cloneWithProps', function() {
108 console.warn = mocks.getMockFunction();
109
110 var component = ReactTestUtils.renderIntoDocument(<Grandparent />);
110 - expect(component.refs).toBe(undefined);
111 + expect(component.refs).toBe(emptyObject);
112 expect(console.warn.mock.calls.length).toBe(1);
113 } finally {
114 console.warn = _warn;