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

Warn when accessing .props, .setProps on DOM components

Ben Alpert committed Jun 5, 2015 at 14:53 UTC bfcad9614ad6bebe6bbfbaa75fb121e1bad20f64
14 files changed +324 -151
src/isomorphic/children/__tests__/sliceChildren-test.js
+6 -39
@@ -15,45 +15,16 @@ describe('sliceChildren', function() {
15
16 var React;
17 var ReactFragment;
18 - var ReactTestUtils;
18
19 var sliceChildren;
21 - var reactComponentExpect;
22 -
23 - var Partial;
20
21 beforeEach(function() {
22 React = require('React');
23 ReactFragment = require('ReactFragment');
28 - ReactTestUtils = require('ReactTestUtils');
24
25 sliceChildren = require('sliceChildren');
31 - reactComponentExpect = require('reactComponentExpect');
32 -
33 - Partial = React.createClass({
34 - render: function() {
35 - return (
36 - <div>
37 - {sliceChildren(
38 - this.props.children,
39 - this.props.start,
40 - this.props.end
41 - )}
42 - </div>
43 - );
44 - },
45 - });
26 });
27
48 - function renderAndSlice(set, start, end) {
49 - var instance = <Partial start={start} end={end}>{set}</Partial>;
50 - instance = ReactTestUtils.renderIntoDocument(instance);
51 - var rendered = reactComponentExpect(instance)
52 - .expectRenderedChild()
53 - .instance();
54 - return rendered.props.children;
55 - }
56 -
28 function testKeyValuePairs(children, expectedPairs) {
29 var obj = ReactFragment.extract(children);
30 expect(obj).toEqual(expectedPairs);
@@ -65,7 +36,7 @@ describe('sliceChildren', function() {
36 <div key="B" />,
37 <div key="C" />,
38 ];
68 - var children = renderAndSlice(fullSet, 0);
39 + var children = sliceChildren(fullSet, 0);
40 testKeyValuePairs(children, {
41 '.$A': fullSet[0],
42 '.$B': fullSet[1],
@@ -79,7 +50,7 @@ describe('sliceChildren', function() {
50 <div key="B" />,
51 <div key="C" />,
52 ];
82 - var children = renderAndSlice(fullSet, 1);
53 + var children = sliceChildren(fullSet, 1);
54 testKeyValuePairs(children, {
55 '.$B': fullSet[1],
56 '.$C': fullSet[2],
@@ -93,7 +64,7 @@ describe('sliceChildren', function() {
64 <div key="C" />,
65 <div key="D" />,
66 ];
96 - var children = renderAndSlice(fullSet, 1, 2);
67 + var children = sliceChildren(fullSet, 1, 2);
68 testKeyValuePairs(children, {
69 '.$B': fullSet[1],
70 });
@@ -104,13 +75,9 @@ describe('sliceChildren', function() {
75 var b = <div />;
76 var c = <div />;
77
107 - var instance = <Partial start={1} end={2}>{a}{b}{c}</Partial>;
108 - instance = ReactTestUtils.renderIntoDocument(instance);
109 - var rendered = reactComponentExpect(instance)
110 - .expectRenderedChild()
111 - .instance();
112 -
113 - testKeyValuePairs(rendered.props.children, {
78 + var el = <div>{a}{b}{c}</div>;
79 + var children = sliceChildren(el.props.children, 1, 2);
80 + testKeyValuePairs(children, {
81 '.1': b,
82 });
83 });
src/renderers/dom/client/__tests__/ReactDOM-test.js
+16 -16
@@ -79,32 +79,32 @@ describe('ReactDOM', function() {
79 </div>
80 );
81 // Warm the cache with theDog
82 - myDiv.setProps({
83 - children: [
82 + myDiv = ReactTestUtils.renderIntoDocument(
83 + <div>
84 <div key="theDog" className="dogbeforedelete" />,
85 <div key="theBird" className="bird" />,
86 - ],
87 - });
86 + </div>
87 + );
88 // Remove theDog - this should purge the cache
89 - myDiv.setProps({
90 - children: [
89 + myDiv = ReactTestUtils.renderIntoDocument(
90 + <div>
91 <div key="theBird" className="bird" />,
92 - ],
93 - });
92 + </div>
93 + );
94 // Now, put theDog back. It's now a different DOM node.
95 - myDiv.setProps({
96 - children: [
95 + myDiv = ReactTestUtils.renderIntoDocument(
96 + <div>
97 <div key="theDog" className="dog" />,
98 <div key="theBird" className="bird" />,
99 - ],
100 - });
99 + </div>
100 + );
101 // Change the className of theDog. It will use the same element
102 - myDiv.setProps({
103 - children: [
102 + myDiv = ReactTestUtils.renderIntoDocument(
103 + <div>
104 <div key="theDog" className="bigdog" />,
105 <div key="theBird" className="bird" />,
106 - ],
107 - });
106 + </div>
107 + );
108 var root = React.findDOMNode(myDiv);
109 var dog = root.childNodes[0];
110 expect(dog.className).toBe('bigdog');
src/renderers/dom/client/wrappers/__tests__/ReactDOMButton-test.js
+26 -12
@@ -53,27 +53,41 @@ describe('ReactDOMButton', function() {
53 });
54
55 it('should forward clicks when it becomes not disabled', function() {
56 - var btn = mounted(<button disabled={true} onClick={onClick} />);
57 - btn.setProps({disabled: false});
56 + var container = document.createElement('div');
57 + var btn = React.render(
58 + <button disabled={true} onClick={onClick} />,
59 + container
60 + );
61 + btn = React.render(
62 + <button onClick={onClick} />,
63 + container
64 + );
65 expectClickThru(btn);
66 });
67
68 it('should not forward clicks when it becomes disabled', function() {
62 - var btn = mounted(<button onClick={onClick} />);
63 - btn.setProps({disabled: true});
69 + var container = document.createElement('div');
70 + var btn = React.render(
71 + <button onClick={onClick} />,
72 + container
73 + );
74 + btn = React.render(
75 + <button disabled={true} onClick={onClick} />,
76 + container
77 + );
78 expectNoClickThru(btn);
79 });
80
81 it('should work correctly if the listener is changed', function() {
68 - var btn = mounted(
69 - <button disabled={true} onClick={function() {}} />
82 + var container = document.createElement('div');
83 + var btn = React.render(
84 + <button disabled={true} onClick={function() {}} />,
85 + container
86 + );
87 + btn = React.render(
88 + <button disabled={false} onClick={onClick} />,
89 + container
90 );
71 -
72 - btn.setProps({
73 - disabled: false,
74 - onClick: onClick,
75 - });
76 -
91 expectClickThru(btn);
92 });
93 });
src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js
+18 -7
@@ -75,30 +75,39 @@ describe('ReactDOMInput', function() {
75 });
76
77 it('should allow setting `value` to `true`', function() {
78 + var container = document.createElement('div');
79 var stub = <input type="text" value="yolo" onChange={emptyFunction} />;
79 - stub = ReactTestUtils.renderIntoDocument(stub);
80 + stub = React.render(stub, container);
81 var node = React.findDOMNode(stub);
82
83 expect(node.value).toBe('yolo');
84
84 - stub.replaceProps({value: true, onChange: emptyFunction});
85 + stub = React.render(
86 + <input type="text" value={true} onChange={emptyFunction} />,
87 + container
88 + );
89 expect(node.value).toEqual('true');
90 });
91
92 it('should allow setting `value` to `false`', function() {
93 + var container = document.createElement('div');
94 var stub = <input type="text" value="yolo" onChange={emptyFunction} />;
90 - stub = ReactTestUtils.renderIntoDocument(stub);
95 + stub = React.render(stub, container);
96 var node = React.findDOMNode(stub);
97
98 expect(node.value).toBe('yolo');
99
95 - stub.replaceProps({value: false});
100 + stub = React.render(
101 + <input type="text" value={false} onChange={emptyFunction} />,
102 + container
103 + );
104 expect(node.value).toEqual('false');
105 });
106
107 it('should allow setting `value` to `objToString`', function() {
108 + var container = document.createElement('div');
109 var stub = <input type="text" value="foo" onChange={emptyFunction} />;
101 - stub = ReactTestUtils.renderIntoDocument(stub);
110 + stub = React.render(stub, container);
111 var node = React.findDOMNode(stub);
112
113 expect(node.value).toBe('foo');
@@ -108,8 +117,10 @@ describe('ReactDOMInput', function() {
117 return 'foobar';
118 },
119 };
111 -
112 - stub.replaceProps({value: objToString, onChange: emptyFunction});
120 + stub = React.render(
121 + <input type="text" value={objToString} onChange={emptyFunction} />,
122 + container
123 + );
124 expect(node.value).toEqual('foobar');
125 });
126
src/renderers/dom/client/wrappers/__tests__/ReactDOMSelect-test.js
+72 -31
@@ -32,13 +32,18 @@ describe('ReactDOMSelect', function() {
32 <option value="giraffe">A giraffe!</option>
33 <option value="gorilla">A gorilla!</option>
34 </select>;
35 - stub = ReactTestUtils.renderIntoDocument(stub);
35 + var options = stub.props.children;
36 + var container = document.createElement('div');
37 + stub = React.render(stub, container);
38 var node = React.findDOMNode(stub);
39
40 expect(node.value).toBe('giraffe');
41
42 // Changing `defaultValue` should do nothing.
41 - stub.setProps({defaultValue: 'gorilla'});
43 + React.render(
44 + <select defaultValue="gorilla">{options}</select>,
45 + container
46 + );
47 expect(node.value).toEqual('giraffe');
48 });
49
@@ -51,19 +56,20 @@ describe('ReactDOMSelect', function() {
56 });
57
58 it('should not control when using `defaultValue`', function() {
54 - var stub =
59 + var el =
60 <select defaultValue="giraffe">
61 <option value="monkey">A monkey!</option>
62 <option value="giraffe">A giraffe!</option>
63 <option value="gorilla">A gorilla!</option>
64 </select>;
60 - stub = ReactTestUtils.renderIntoDocument(stub);
65 + var container = document.createElement('div');
66 + var stub = React.render(el, container);
67 var node = React.findDOMNode(stub);
68
69 expect(node.value).toBe('giraffe');
70
71 node.value = 'monkey';
66 - stub.forceUpdate();
72 + React.render(el, container);
73 // Uncontrolled selects shouldn't change the value after first mounting
74 expect(node.value).toEqual('monkey');
75 });
@@ -75,7 +81,9 @@ describe('ReactDOMSelect', function() {
81 <option value="giraffe">A giraffe!</option>
82 <option value="gorilla">A gorilla!</option>
83 </select>;
78 - stub = ReactTestUtils.renderIntoDocument(stub);
84 + var options = stub.props.children;
85 + var container = document.createElement('div');
86 + stub = React.render(stub, container);
87 var node = React.findDOMNode(stub);
88
89 expect(node.options[0].selected).toBe(false); // monkey
@@ -83,7 +91,10 @@ describe('ReactDOMSelect', function() {
91 expect(node.options[2].selected).toBe(true); // gorilla
92
93 // Changing `defaultValue` should do nothing.
86 - stub.setProps({defaultValue: ['monkey']});
94 + React.render(
95 + <select multiple={true} defaultValue={['monkey']}>{options}</select>,
96 + container
97 + );
98
99 expect(node.options[0].selected).toBe(false); // monkey
100 expect(node.options[1].selected).toBe(true); // giraffe
@@ -97,13 +108,18 @@ describe('ReactDOMSelect', function() {
108 <option value="giraffe">A giraffe!</option>
109 <option value="gorilla">A gorilla!</option>
110 </select>;
100 - stub = ReactTestUtils.renderIntoDocument(stub);
111 + var options = stub.props.children;
112 + var container = document.createElement('div');
113 + stub = React.render(stub, container);
114 var node = React.findDOMNode(stub);
115
116 expect(node.value).toBe('giraffe');
117
118 // Changing the `value` prop should change the selected option.
106 - stub.setProps({value: 'gorilla'});
119 + React.render(
120 + <select value="gorilla">{options}</select>,
121 + container
122 + );
123 expect(node.value).toEqual('gorilla');
124 });
125
@@ -122,7 +138,9 @@ describe('ReactDOMSelect', function() {
138 <option value="giraffe">A giraffe!</option>
139 <option value="gorilla">A gorilla!</option>
140 </select>;
125 - stub = ReactTestUtils.renderIntoDocument(stub);
141 + var options = stub.props.children;
142 + var container = document.createElement('div');
143 + stub = React.render(stub, container);
144 var node = React.findDOMNode(stub);
145
146 expect(node.options[0].selected).toBe(false); // monkey
@@ -130,7 +148,10 @@ describe('ReactDOMSelect', function() {
148 expect(node.options[2].selected).toBe(true); // gorilla
149
150 // Changing the `value` prop should change the selected options.
133 - stub.setProps({value: ['monkey']});
151 + React.render(
152 + <select multiple={true} value={['monkey']}>{options}</select>,
153 + container
154 + );
155
156 expect(node.options[0].selected).toBe(true); // monkey
157 expect(node.options[1].selected).toBe(false); // giraffe
@@ -154,15 +175,17 @@ describe('ReactDOMSelect', function() {
175
176 it('should reset child options selected when they are changed and `value` is set', function() {
177 var stub = <select multiple={true} value={['a', 'b']} />;
157 - stub = ReactTestUtils.renderIntoDocument(stub);
178 + var container = document.createElement('div');
179 + stub = React.render(stub, container);
180
159 - stub.setProps({
160 - children: [
161 - <option value="a">a</option>,
162 - <option value="b">b</option>,
163 - <option value="c">c</option>,
164 - ],
165 - });
181 + React.render(
182 + <select multiple={true} value={['a', 'b']}>
183 + <option value="a">a</option>
184 + <option value="b">b</option>
185 + <option value="c">c</option>
186 + </select>,
187 + container
188 + );
189
190 var node = React.findDOMNode(stub);
191
@@ -179,13 +202,14 @@ describe('ReactDOMSelect', function() {
202 },
203 };
204
182 - var stub =
205 + var el =
206 <select multiple={true} value={[objectToString]}>
207 <option value="monkey">A monkey!</option>
208 <option value="giraffe">A giraffe!</option>
209 <option value="gorilla">A gorilla!</option>
210 </select>;
188 - stub = ReactTestUtils.renderIntoDocument(stub);
211 + var container = document.createElement('div');
212 + var stub = React.render(el, container);
213 var node = React.findDOMNode(stub);
214
215 expect(node.options[0].selected).toBe(false); // monkey
@@ -194,7 +218,7 @@ describe('ReactDOMSelect', function() {
218
219 // Changing the `value` prop should change the selected options.
220 objectToString.animal = 'monkey';
197 - stub.forceUpdate();
221 + React.render(el, container);
222
223 expect(node.options[0].selected).toBe(true); // monkey
224 expect(node.options[1].selected).toBe(false); // giraffe
@@ -208,7 +232,9 @@ describe('ReactDOMSelect', function() {
232 <option value="giraffe">A giraffe!</option>
233 <option value="gorilla">A gorilla!</option>
234 </select>;
211 - stub = ReactTestUtils.renderIntoDocument(stub);
235 + var options = stub.props.children;
236 + var container = document.createElement('div');
237 + stub = React.render(stub, container);
238 var node = React.findDOMNode(stub);
239
240 expect(node.options[0].selected).toBe(false); // monkey
@@ -216,7 +242,12 @@ describe('ReactDOMSelect', function() {
242 expect(node.options[2].selected).toBe(false); // gorilla
243
244 // When making it multiple, giraffe and gorilla should be selected
219 - stub.setProps({multiple: true, defaultValue: ['giraffe', 'gorilla']});
245 + React.render(
246 + <select multiple={true} defaultValue={['giraffe', 'gorilla']}>
247 + {options}
248 + </select>,
249 + container
250 + );
251
252 expect(node.options[0].selected).toBe(false); // monkey
253 expect(node.options[1].selected).toBe(true); // giraffe
@@ -230,7 +261,9 @@ describe('ReactDOMSelect', function() {
261 <option value="giraffe">A giraffe!</option>
262 <option value="gorilla">A gorilla!</option>
263 </select>;
233 - stub = ReactTestUtils.renderIntoDocument(stub);
264 + var options = stub.props.children;
265 + var container = document.createElement('div');
266 + stub = React.render(stub, container);
267 var node = React.findDOMNode(stub);
268
269 expect(node.options[0].selected).toBe(false); // monkey
@@ -239,7 +272,11 @@ describe('ReactDOMSelect', function() {
272
273 // When removing multiple, defaultValue is applied again, being omitted
274 // means that "monkey" will be selected
242 - stub.setProps({multiple: false, defaultValue: 'gorilla'});
275 + React.render(
276 + <select defaultValue="gorilla">{options}</select>,
277 + container
278 + );
279 +
280
281 expect(node.options[0].selected).toBe(false); // monkey
282 expect(node.options[1].selected).toBe(false); // giraffe
@@ -253,14 +290,16 @@ describe('ReactDOMSelect', function() {
290 <option value="giraffe">A giraffe!</option>
291 <option value="gorilla">A gorilla!</option>
292 </select>;
256 - stub = ReactTestUtils.renderIntoDocument(stub);
293 + var options = stub.props.children;
294 + var container = document.createElement('div');
295 + stub = React.render(stub, container);
296 var node = React.findDOMNode(stub);
297
298 expect(node.options[0].selected).toBe(false); // monkey
299 expect(node.options[1].selected).toBe(true); // giraffe
300 expect(node.options[2].selected).toBe(false); // gorilla
301
263 - stub.setProps({value: null});
302 + React.render(<select>{options}</select>, container);
303
304 expect(node.options[0].selected).toBe(false); // monkey
305 expect(node.options[1].selected).toBe(true); // giraffe
@@ -274,16 +313,18 @@ describe('ReactDOMSelect', function() {
313 <option value="giraffe">A giraffe!</option>
314 <option value="gorilla">A gorilla!</option>
315 </select>;
277 - stub = ReactTestUtils.renderIntoDocument(stub);
316 + var options = stub.props.children;
317 + var container = document.createElement('div');
318 + stub = React.render(stub, container);
319 var node = React.findDOMNode(stub);
320
280 - stub.setProps({value: 'gorilla'});
321 + React.render(<select value="gorilla">{options}</select>, container);
322
323 expect(node.options[0].selected).toBe(false); // monkey
324 expect(node.options[1].selected).toBe(false); // giraffe
325 expect(node.options[2].selected).toBe(true); // gorilla
326
286 - stub.setProps({value: null});
327 + React.render(<select>{options}</select>, container);
328
329 expect(node.options[0].selected).toBe(false); // monkey
330 expect(node.options[1].selected).toBe(false); // giraffe
src/renderers/dom/client/wrappers/__tests__/ReactDOMTextarea-test.js
+35 -15
@@ -26,8 +26,11 @@ describe('ReactDOMTextarea', function() {
26 ReactLink = require('ReactLink');
27 ReactTestUtils = require('ReactTestUtils');
28
29 - renderTextarea = function(component) {
30 - var stub = ReactTestUtils.renderIntoDocument(component);
29 + renderTextarea = function(component, container) {
30 + if (!container) {
31 + container = document.createElement('div');
32 + }
33 + var stub = React.render(component, container);
34 var node = React.findDOMNode(stub);
35 // Fixing jsdom's quirky behavior -- in reality, the parser should strip
36 // off the leading newline but we need to do it by hand here.
@@ -37,14 +40,14 @@ describe('ReactDOMTextarea', function() {
40 });
41
42 it('should allow setting `defaultValue`', function() {
40 - var stub = <textarea defaultValue="giraffe" />;
41 - stub = renderTextarea(stub);
43 + var container = document.createElement('div');
44 + var stub = renderTextarea(<textarea defaultValue="giraffe" />, container);
45 var node = React.findDOMNode(stub);
46
47 expect(node.value).toBe('giraffe');
48
49 // Changing `defaultValue` should do nothing.
47 - stub.replaceProps({defaultValue: 'gorilla'});
50 + stub = renderTextarea(<textarea defaultValue="gorilla" />, container);
51 expect(node.value).toEqual('giraffe');
52 });
53
@@ -95,41 +98,54 @@ describe('ReactDOMTextarea', function() {
98 });
99
100 it('should allow setting `value` to `giraffe`', function() {
101 + var container = document.createElement('div');
102 var stub = <textarea value="giraffe" onChange={emptyFunction} />;
99 - stub = renderTextarea(stub);
103 + stub = renderTextarea(stub, container);
104 var node = React.findDOMNode(stub);
105
106 expect(node.value).toBe('giraffe');
107
104 - stub.replaceProps({value: 'gorilla', onChange: emptyFunction});
108 + stub = React.render(
109 + <textarea value="gorilla" onChange={emptyFunction} />,
110 + container
111 + );
112 expect(node.value).toEqual('gorilla');
113 });
114
115 it('should allow setting `value` to `true`', function() {
116 + var container = document.createElement('div');
117 var stub = <textarea value="giraffe" onChange={emptyFunction} />;
110 - stub = renderTextarea(stub);
118 + stub = renderTextarea(stub, container);
119 var node = React.findDOMNode(stub);
120
121 expect(node.value).toBe('giraffe');
122
115 - stub.replaceProps({value: true, onChange: emptyFunction});
123 + stub = React.render(
124 + <textarea value={true} onChange={emptyFunction} />,
125 + container
126 + );
127 expect(node.value).toEqual('true');
128 });
129
130 it('should allow setting `value` to `false`', function() {
131 + var container = document.createElement('div');
132 var stub = <textarea value="giraffe" onChange={emptyFunction} />;
121 - stub = renderTextarea(stub);
133 + stub = renderTextarea(stub, container);
134 var node = React.findDOMNode(stub);
135
136 expect(node.value).toBe('giraffe');
137
126 - stub.replaceProps({value: false});
138 + stub = React.render(
139 + <textarea value={false} onChange={emptyFunction} />,
140 + container
141 + );
142 expect(node.value).toEqual('false');
143 });
144
145 it('should allow setting `value` to `objToString`', function() {
146 + var container = document.createElement('div');
147 var stub = <textarea value="giraffe" onChange={emptyFunction} />;
132 - stub = renderTextarea(stub);
148 + stub = renderTextarea(stub, container);
149 var node = React.findDOMNode(stub);
150
151 expect(node.value).toBe('giraffe');
@@ -139,7 +155,10 @@ describe('ReactDOMTextarea', function() {
155 return 'foo';
156 },
157 };
142 - stub.replaceProps({value: objToString, onChange: emptyFunction});
158 + stub = React.render(
159 + <textarea value={objToString} onChange={emptyFunction} />,
160 + container
161 + );
162 expect(node.value).toEqual('foo');
163 });
164
@@ -156,15 +175,16 @@ describe('ReactDOMTextarea', function() {
175 it('should treat children like `defaultValue`', function() {
176 spyOn(console, 'error');
177
178 + var container = document.createElement('div');
179 var stub = <textarea>giraffe</textarea>;
160 - stub = renderTextarea(stub);
180 + stub = renderTextarea(stub, container);
181 var node = React.findDOMNode(stub);
182
183 expect(console.error.argsForCall.length).toBe(1);
184 expect(node.value).toBe('giraffe');
185
186 // Changing children should do nothing, it functions like `defaultValue`.
167 - stub.replaceProps({children: 'gorilla'});
187 + stub = React.render(<textarea>gorilla</textarea>, container);
188 expect(node.value).toEqual('giraffe');
189 });
190
src/renderers/dom/shared/ReactDefaultInjection.js
+62 -2
@@ -38,8 +38,27 @@ var ServerReactRootIndex = require('ServerReactRootIndex');
38 var SimpleEventPlugin = require('SimpleEventPlugin');
39 var SVGDOMPropertyConfig = require('SVGDOMPropertyConfig');
40
41 +var warning = require('warning');
42 +
43 +var deprecatedDOMMethods = [
44 + 'isMounted', 'replaceProps', 'replaceState', 'setProps', 'setState',
45 + 'forceUpdate',
46 +];
47 +
48 +function getDeclarationErrorAddendum(domWrapperClass) {
49 + var internalInstance = ReactInstanceMap.get(domWrapperClass);
50 + var owner = internalInstance._currentElement._owner || null;
51 + if (owner) {
52 + var name = owner.getName();
53 + if (name) {
54 + return ' This DOM component was rendered by `' + name + '`.';
55 + }
56 + }
57 + return '';
58 +}
59 +
60 function autoGenerateWrapperClass(type) {
42 - return ReactClass.createClass({
61 + var wrapperClass = ReactClass.createClass({
62 tagName: type.toUpperCase(),
63 render: function() {
64 // Copy owner down for debugging info
@@ -49,10 +68,51 @@ function autoGenerateWrapperClass(type) {
68 null, // key
69 null, // ref
70 internalInstance._currentElement._owner, // owner
52 - this.props
71 + this._internalProps
72 );
73 },
74 });
75 +
76 + Object.defineProperty(wrapperClass.prototype, 'props', {
77 + enumerable: true,
78 + set: function(props) {
79 + this._internalProps = props;
80 + },
81 + get: function() {
82 + if (__DEV__) {
83 + warning(
84 + false,
85 + 'ReactDOMComponent.props: Do not access .props of a DOM component ' +
86 + 'directly; instead, recreate the props as `render` did originally ' +
87 + 'or use React.findDOMNode and read the DOM properties/attributes ' +
88 + 'directly.%s',
89 + getDeclarationErrorAddendum(this)
90 + );
91 + }
92 + return this._internalProps;
93 + },
94 + });
95 +
96 + deprecatedDOMMethods.forEach(function(method) {
97 + var old = wrapperClass.prototype[method];
98 + Object.defineProperty(wrapperClass.prototype, method, {
99 + enumerable: true,
100 + get: function() {
101 + if (__DEV__) {
102 + warning(
103 + false,
104 + 'ReactDOMComponent.%s(): Do not access .%s() of a DOM component.%s',
105 + method,
106 + method,
107 + getDeclarationErrorAddendum(this)
108 + );
109 + }
110 + return old;
111 + },
112 + });
113 + });
114 +
115 + return wrapperClass;
116 }
117
118 var alreadyInjected = false;
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+37
@@ -902,4 +902,41 @@ describe('ReactDOMComponent', function() {
902 });
903 });
904
905 + describe('DOM nodes as refs', function() {
906 + var React;
907 + var ReactTestUtils;
908 +
909 + beforeEach(function() {
910 + React = require('React');
911 + ReactTestUtils = require('ReactTestUtils');
912 + });
913 +
914 + it('warns when accessing properties on DOM components', function() {
915 + spyOn(console, 'error');
916 + var Animal = React.createClass({
917 + render: function() {
918 + return <div ref="div">iguana</div>;
919 + },
920 + componentDidMount: function() {
921 + void this.refs.div.props;
922 + void this.refs.div.setProps;
923 + },
924 + });
925 + ReactTestUtils.renderIntoDocument(<Animal />);
926 +
927 + expect(console.error.calls.length).toBe(2);
928 + expect(console.error.calls[0].args[0]).toBe(
929 + 'Warning: ReactDOMComponent.props: Do not access .props of a DOM ' +
930 + 'component directly; instead, recreate the props as `render` did ' +
931 + 'originally or use React.findDOMNode and read the DOM ' +
932 + 'properties/attributes directly. This DOM component was rendered ' +
933 + 'by `Animal`.'
934 + );
935 + expect(console.error.calls[1].args[0]).toBe(
936 + 'Warning: ReactDOMComponent.setProps(): Do not access .setProps() of ' +
937 + 'a DOM component. This DOM component was rendered by `Animal`.'
938 + );
939 + });
940 + });
941 +
942 });
src/renderers/shared/reconciler/ReactCompositeComponent.js
+18 -7
@@ -557,11 +557,16 @@ var ReactCompositeComponentMixin = {
557 ) {
558 var inst = this._instance;
559
560 - var nextContext = inst.context;
561 - var nextProps = inst.props;
560 + var nextContext;
561 + var nextProps;
562
563 // Distinguish between a props update versus a simple state update
564 - if (prevParentElement !== nextParentElement) {
564 + if (prevParentElement === nextParentElement) {
565 + nextContext = inst.context;
566 + // Skip checking prop types again -- we don't read inst.props to avoid
567 + // warning for DOM component props in this upgrade
568 + nextProps = nextParentElement.props;
569 + } else {
570 nextContext = this._processContext(nextUnmaskedContext);
571 nextProps = this._processProps(nextParentElement.props);
572
@@ -663,9 +668,15 @@ var ReactCompositeComponentMixin = {
668 ) {
669 var inst = this._instance;
670
666 - var prevProps = inst.props;
667 - var prevState = inst.state;
668 - var prevContext = inst.context;
671 + var hasComponentDidUpdate = Boolean(inst.componentDidUpdate);
672 + var prevProps;
673 + var prevState;
674 + var prevContext;
675 + if (hasComponentDidUpdate) {
676 + prevProps = inst.props;
677 + prevState = inst.state;
678 + prevContext = inst.context;
679 + }
680
681 if (inst.componentWillUpdate) {
682 inst.componentWillUpdate(nextProps, nextState, nextContext);
@@ -679,7 +690,7 @@ var ReactCompositeComponentMixin = {
690
691 this._updateRenderedComponent(transaction, unmaskedContext);
692
682 - if (inst.componentDidUpdate) {
693 + if (hasComponentDidUpdate) {
694 transaction.getReactMountReady().enqueue(
695 inst.componentDidUpdate.bind(inst, prevProps, prevState, prevContext),
696 inst
src/renderers/shared/reconciler/__tests__/ReactComponent-test.js
+5 -2
@@ -135,6 +135,9 @@ describe('ReactComponent', function() {
135
136 it('should support new-style refs with mixed-up owners', function() {
137 var Wrapper = React.createClass({
138 + getTitle: function() {
139 + return this.props.title;
140 + },
141 render: function() {
142 return this.props.getContent();
143 },
@@ -158,8 +161,8 @@ describe('ReactComponent', function() {
161 },
162 componentDidMount: function() {
163 // Check .props.title to make sure we got the right elements back
161 - expect(this.wrapperRef.props.title).toBe('wrapper');
162 - expect(this.innerRef.props.title).toBe('inner');
164 + expect(this.wrapperRef.getTitle()).toBe('wrapper');
165 + expect(React.findDOMNode(this.innerRef).title).toBe('inner');
166 mounted = true;
167 },
168 });
src/renderers/shared/reconciler/__tests__/ReactComponentLifeCycle-test.js
+7 -2
@@ -464,15 +464,20 @@ describe('ReactComponentLifeCycle', function() {
464 /**
465 * calls setProps in an componentDidMount.
466 */
467 + var Inner = React.createClass({
468 + render: function() {
469 + return <div />;
470 + },
471 + });
472 var PropsUpdaterInOnDOMReady = React.createClass({
473 componentDidMount: function() {
474 this.refs.theSimpleComponent.setProps({
470 - valueToUseInitially: this.props.valueToUseInOnDOMReady,
475 + className: this.props.valueToUseInOnDOMReady,
476 });
477 },
478 render: function() {
479 return (
475 - <div
480 + <Inner
481 className={this.props.valueToUseInitially}
482 ref="theSimpleComponent"
483 />
src/renderers/shared/reconciler/__tests__/ReactCompositeComponent-test.js
+6 -1
@@ -385,9 +385,14 @@ describe('ReactCompositeComponent', function() {
385
386 var innerInstance;
387
388 + var Inner = React.createClass({
389 + render: function() {
390 + return <div />;
391 + },
392 + });
393 var Component = React.createClass({
394 render: function() {
390 - return <div><div ref="inner" /></div>;
395 + return <div><Inner ref="inner" /></div>;
396 },
397 componentDidMount: function() {
398 innerInstance = this.refs.inner;
src/renderers/shared/reconciler/__tests__/ReactMultiChildText-test.js
+3 -12
@@ -31,25 +31,16 @@ var testAllPermutations = function(testCases) {
31 var updateWithChildren = testCases[j];
32 var expectedResultAfterUpdate = testCases[j + 1];
33
34 - var d = renderChildren(renderWithChildren);
34 + var container = document.createElement('div');
35 + var d = React.render(<div>{renderWithChildren}</div>, container);
36 expectChildren(d, expectedResultAfterRender);
37
37 - updateChildren(d, updateWithChildren);
38 + d = React.render(<div>{updateWithChildren}</div>, container);
39 expectChildren(d, expectedResultAfterUpdate);
40 }
41 }
42 };
43
43 -var renderChildren = function(children) {
44 - return ReactTestUtils.renderIntoDocument(
45 - <div>{children}</div>
46 - );
47 -};
48 -
49 -var updateChildren = function(d, children) {
50 - d.replaceProps({children: children});
51 -};
52 -
44 var expectChildren = function(d, children) {
45 var textNode;
46 if (typeof children === 'string') {
src/test/ReactTestUtils.js
+13 -5
@@ -77,6 +77,11 @@ var ReactTestUtils = {
77 },
78
79 isCompositeComponent: function(inst) {
80 + if (ReactTestUtils.isDOMComponent(inst)) {
81 + // Accessing inst.setState warns; just return false as that'll be what
82 + // this returns when we have DOM nodes as refs directly
83 + return false;
84 + }
85 return typeof inst.render === 'function' &&
86 typeof inst.setState === 'function';
87 },
@@ -165,11 +170,14 @@ var ReactTestUtils = {
170 */
171 scryRenderedDOMComponentsWithClass: function(root, className) {
172 return ReactTestUtils.findAllInRenderedTree(root, function(inst) {
168 - var instClassName = inst.props.className;
169 - return ReactTestUtils.isDOMComponent(inst) && (
170 - instClassName &&
171 - (' ' + instClassName + ' ').indexOf(' ' + className + ' ') !== -1
172 - );
173 + if (ReactTestUtils.isDOMComponent(inst)) {
174 + var instClassName = React.findDOMNode(inst).className;
175 + return (
176 + instClassName &&
177 + (' ' + instClassName + ' ').indexOf(' ' + className + ' ') !== -1
178 + );
179 + }
180 + return false;
181 });
182 },
183