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