Skip to content

Commit 355bf75

Browse files
author
jim
committed
Cleanup and bug fixes for merge.
1 parent 574328a commit 355bf75

File tree

8 files changed

+147
-56
lines changed

8 files changed

+147
-56
lines changed

src/renderers/dom/client/wrappers/ReactDOMInput.js

+28-2
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ var ReactDOMInput = {
7575
}, props, {
7676
defaultChecked: undefined,
7777
defaultValue: undefined,
78-
value: value != null ? value : props.defaultValue,
78+
value: value != null ? value : inst._wrapperState.initialValue,
7979
checked: checked != null ? checked : inst._wrapperState.initialChecked,
8080
onChange: inst._wrapperState.onChange,
8181
});
@@ -138,8 +138,10 @@ var ReactDOMInput = {
138138
warnIfValueIsNull(props);
139139
}
140140

141+
var defaultValue = props.defaultValue;
141142
inst._wrapperState = {
142-
initialChecked: props.defaultChecked || false,
143+
initialChecked: props.checked != null ? props.checked : props.defaultChecked,
144+
initialValue: props.value != null ? props.value : defaultValue,
143145
listeners: null,
144146
onChange: _handleChange.bind(inst),
145147
};
@@ -215,6 +217,30 @@ var ReactDOMInput = {
215217
if (newValue !== node.value) {
216218
node.value = newValue;
217219
}
220+
} else {
221+
ReactDOMInput.postUpdateWrapper(inst);
222+
}
223+
},
224+
225+
postUpdateWrapper: function(inst) {
226+
var props = inst._currentElement.props;
227+
if (!props.value && props.defaultValue) {
228+
invariant(inst._rootNodeID, 'Must be mounted to initialize initial input value');
229+
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
230+
if (node.defaultValue !== props.defaultValue) {
231+
var tmp = node.value;
232+
node.defaultValue = '' + props.defaultValue;
233+
node.value = tmp;
234+
}
235+
}
236+
if (!props.checked && props.defaultChecked) {
237+
invariant(inst._rootNodeID, 'Must be mounted to initialize initial input value');
238+
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
239+
if (node.defaultChecked !== props.defaultChecked) {
240+
var tmp = node.checked;
241+
node.defaultChecked = '' + props.defaultChecked;
242+
node.checked = tmp;
243+
}
218244
}
219245
},
220246
};

src/renderers/dom/client/wrappers/ReactDOMTextarea.js

+52-34
Original file line numberDiff line numberDiff line change
@@ -66,44 +66,12 @@ var ReactDOMTextarea = {
6666

6767
var value = LinkedValueUtils.getValue(props);
6868

69-
// only bother fetching default value if we're going to use it
70-
if (value == null) {
71-
var defaultValue = props.defaultValue;
72-
// TODO (yungsters): Remove support for children content in <textarea>.
73-
var children = props.children;
74-
if (children != null) {
75-
if (__DEV__) {
76-
warning(
77-
false,
78-
'Use the `defaultValue` or `value` props instead of setting ' +
79-
'children on <textarea>.'
80-
);
81-
}
82-
invariant(
83-
defaultValue == null,
84-
'If you supply `defaultValue` on a <textarea>, do not pass children.'
85-
);
86-
if (Array.isArray(children)) {
87-
invariant(
88-
children.length <= 1,
89-
'<textarea> can only have at most one child.'
90-
);
91-
children = children[0];
92-
}
93-
94-
defaultValue = '' + children;
95-
}
96-
if (defaultValue == null) {
97-
defaultValue = '';
98-
}
99-
}
100-
10169
// The value can be a boolean or object so that's why it's
10270
// forced to be a string.
10371
var nativeProps = Object.assign({}, props, {
104-
defaultValue: '' + (value != null ? value : defaultValue),
10572
value: undefined,
106-
children: undefined,
73+
defaultValue: undefined,
74+
children: '' + (value != null ? value : inst._wrapperState.initialValue),
10775
onChange: inst._wrapperState.onChange,
10876
});
10977

@@ -142,7 +110,43 @@ var ReactDOMTextarea = {
142110
warnIfValueIsNull(props);
143111
}
144112

113+
114+
var value = LinkedValueUtils.getValue(props);
115+
116+
// only bother fetching default value if we're going to use it
117+
if (value == null) {
118+
var defaultValue = props.defaultValue;
119+
// TODO (yungsters): Remove support for children content in <textarea>.
120+
var children = props.children;
121+
if (children != null) {
122+
if (__DEV__) {
123+
warning(
124+
false,
125+
'Use the `defaultValue` or `value` props instead of setting ' +
126+
'children on <textarea>.'
127+
);
128+
}
129+
invariant(
130+
defaultValue == null,
131+
'If you supply `defaultValue` on a <textarea>, do not pass children.'
132+
);
133+
if (Array.isArray(children)) {
134+
invariant(
135+
children.length <= 1,
136+
'<textarea> can only have at most one child.'
137+
);
138+
children = children[0];
139+
}
140+
141+
defaultValue = '' + children;
142+
}
143+
if (defaultValue == null) {
144+
defaultValue = '';
145+
}
146+
}
147+
145148
inst._wrapperState = {
149+
initialValue: '' + (value != null ? value : defaultValue),
146150
listeners: null,
147151
onChange: _handleChange.bind(inst),
148152
};
@@ -167,6 +171,20 @@ var ReactDOMTextarea = {
167171
if (newValue !== node.value) {
168172
node.value = newValue;
169173
}
174+
} else {
175+
ReactDOMTextarea.postUpdateWrapper(inst);
176+
}
177+
},
178+
179+
postUpdateWrapper: function(inst) {
180+
var props = inst._currentElement.props;
181+
if (props.value == null && props.defaultValue) {
182+
invariant(inst._rootNodeID, 'Must be mounted to initialize defaultValue');
183+
var node = ReactDOMComponentTree.getNodeFromInstance(inst);
184+
if (node.defaultValue !== props.defaultValue) {
185+
node.value = node.value; // Hack to detach value and defaultValue
186+
node.defaultValue = props.defaultValue;
187+
}
170188
}
171189
},
172190
};

src/renderers/dom/client/wrappers/__tests__/ReactDOMInput-test.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ describe('ReactDOMInput', function() {
1818
var EventConstants;
1919
var React;
2020
var ReactDOM;
21+
var ReactDOMServer;
2122
var ReactDOMFeatureFlags;
2223
var ReactLink;
2324
var ReactTestUtils;
@@ -27,6 +28,7 @@ describe('ReactDOMInput', function() {
2728
EventConstants = require('EventConstants');
2829
React = require('React');
2930
ReactDOM = require('ReactDOM');
31+
ReactDOMServer = require('ReactDOMServer');
3032
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
3133
ReactLink = require('ReactLink');
3234
ReactTestUtils = require('ReactTestUtils');
@@ -67,7 +69,7 @@ describe('ReactDOMInput', function() {
6769

6870
ReactDOM.render(<input type="text" defaultValue="1" />, container);
6971

70-
expect(node.value).toBe('1');
72+
expect(node.value).toBe('0');
7173
});
7274

7375
it('should take `defaultValue` when changing to uncontrolled input', function() {
@@ -79,7 +81,15 @@ describe('ReactDOMInput', function() {
7981

8082
ReactDOM.render(<input type="text" defaultValue="1" />, container);
8183

82-
expect(node.value).toBe('1');
84+
expect(node.value).toBe('0');
85+
});
86+
87+
it('should render value for SSR', function() {
88+
var image = ReactDOMServer.renderToString(<input type="text" defaultValue="1" />);
89+
var div = document.createElement('div');
90+
div.innerHTML = image;
91+
expect(div.firstChild.getAttribute('value')).toBe('1');
92+
expect(div.firstChild.getAttribute('defaultValue')).toBe(null);
8393
});
8494

8595
it('should display "foobar" for `defaultValue` of `objToString`', function() {

src/renderers/dom/client/wrappers/__tests__/ReactDOMTextarea-test.js

+29-11
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ var emptyFunction = require('emptyFunction');
1616
describe('ReactDOMTextarea', function() {
1717
var React;
1818
var ReactDOM;
19+
var ReactDOMServer;
1920
var ReactLink;
2021
var ReactTestUtils;
2122

@@ -24,6 +25,7 @@ describe('ReactDOMTextarea', function() {
2425
beforeEach(function() {
2526
React = require('React');
2627
ReactDOM = require('ReactDOM');
28+
ReactDOMServer = require('ReactDOMServer');
2729
ReactLink = require('ReactLink');
2830
ReactTestUtils = require('ReactTestUtils');
2931

@@ -48,9 +50,9 @@ describe('ReactDOMTextarea', function() {
4850

4951
expect(node.value).toBe('giraffe');
5052

51-
// Changing `defaultValue` should change if no value set.
53+
// Changing `defaultValue` should do nothing.
5254
renderTextarea(<textarea defaultValue="gorilla" />, container, true);
53-
expect(node.value).toEqual('gorilla');
55+
expect(node.value).toEqual('giraffe');
5456

5557
node.value = 'cat';
5658

@@ -85,6 +87,14 @@ describe('ReactDOMTextarea', function() {
8587
expect(node.value).toBe('foobar');
8688
});
8789

90+
it('should set defaultValue', function() {
91+
var container = document.createElement('div');
92+
ReactDOM.render(<textarea defaultValue="foo" />, container);
93+
ReactDOM.render(<textarea defaultValue="bar" />, container);
94+
ReactDOM.render(<textarea defaultValue="noise" />, container);
95+
expect(container.firstChild.defaultValue).toBe('noise');
96+
});
97+
8898
it('should not render value as an attribute', function() {
8999
var stub = <textarea value="giraffe" onChange={emptyFunction} />;
90100
var node = renderTextarea(stub);
@@ -113,6 +123,14 @@ describe('ReactDOMTextarea', function() {
113123
expect(node.value).toEqual('gorilla');
114124
});
115125

126+
it('should render value for SSR', function() {
127+
var image = ReactDOMServer.renderToString(<textarea defaultValue="1" />);
128+
var div = document.createElement('div');
129+
div.innerHTML = image;
130+
expect(div.firstChild.innerHTML).toBe('1');
131+
expect(div.firstChild.getAttribute('defaultValue')).toBe(null);
132+
});
133+
116134
it('should allow setting `value` to `true`', function() {
117135
var container = document.createElement('div');
118136
var stub = <textarea value="giraffe" onChange={emptyFunction} />;
@@ -163,27 +181,27 @@ describe('ReactDOMTextarea', function() {
163181
it('should take updates to `defaultValue` for uncontrolled textarea', function() {
164182
var container = document.createElement('div');
165183

166-
var node = ReactDOM.render(<textarea type="text" defaultValue="0" />, container);
184+
var node = ReactDOM.render(<textarea defaultValue="0" />, container);
167185

168186
expect(node.value).toBe('0');
169187

170-
ReactDOM.render(<textarea type="text" defaultValue="1" />, container);
188+
ReactDOM.render(<textarea defaultValue="1" />, container);
171189

172-
expect(node.value).toBe('1');
190+
expect(node.value).toBe('0');
173191
});
174192

175193
it('should take updates to children in lieu of `defaultValue` for uncontrolled textarea', function() {
176194
var container = document.createElement('div');
177195

178-
var node = ReactDOM.render(<textarea type="text" defaultValue="0" />, container);
196+
var node = ReactDOM.render(<textarea defaultValue="0" />, container);
179197

180198
expect(node.value).toBe('0');
181199

182200
spyOn(console, 'error'); // deprecation warning for `children` content
183201

184-
ReactDOM.render(<textarea type="text">1</textarea>, container);
202+
ReactDOM.render(<textarea>1</textarea>, container);
185203

186-
expect(node.value).toBe('1');
204+
expect(node.value).toBe('0');
187205
});
188206

189207
it('should not incur unnecessary DOM mutations', function() {
@@ -228,9 +246,9 @@ describe('ReactDOMTextarea', function() {
228246
expect(console.error.argsForCall.length).toBe(1);
229247
expect(node.value).toBe('giraffe');
230248

231-
// Changing children should cause value to change (new behavior of `defaultValue`)
249+
// Changing children should do nothing, it functions like `defaultValue`.
232250
stub = ReactDOM.render(<textarea>gorilla</textarea>, container);
233-
expect(node.value).toEqual('gorilla');
251+
expect(node.value).toEqual('giraffe');
234252
});
235253

236254
it('should not keep value when switching to uncontrolled element if not changed', function() {
@@ -242,7 +260,7 @@ describe('ReactDOMTextarea', function() {
242260

243261
ReactDOM.render(<textarea defaultValue="gorilla"></textarea>, container);
244262

245-
expect(node.value).toEqual('gorilla');
263+
expect(node.value).toEqual('kitten');
246264
});
247265

248266
it('should keep value when switching to uncontrolled element if changed', function() {

src/renderers/dom/shared/HTMLDOMPropertyConfig.js

-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ var HTMLDOMPropertyConfig = {
6161
data: 0, // For `<object />` acts as `src`.
6262
dateTime: 0,
6363
default: HAS_BOOLEAN_VALUE,
64-
defaultValue: MUST_USE_PROPERTY,
6564
defer: HAS_BOOLEAN_VALUE,
6665
dir: 0,
6766
disabled: HAS_BOOLEAN_VALUE,

src/renderers/dom/shared/ReactDOMComponent.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,14 @@ function postUpdateSelectWrapper() {
349349
ReactDOMSelect.postUpdateWrapper(this);
350350
}
351351

352+
function postUpdateTextareaWrapper() {
353+
ReactDOMTextarea.postUpdateWrapper(this);
354+
}
355+
356+
function postUpdateInputWrapper() {
357+
ReactDOMInput.postUpdateWrapper(this);
358+
}
359+
352360
// For HTML, certain tags should omit their close tag. We keep a whitelist for
353361
// those special-case tags.
354362

@@ -488,6 +496,7 @@ ReactDOMComponent.Mixin = {
488496
ReactDOMInput.mountWrapper(this, props, nativeParent);
489497
props = ReactDOMInput.getNativeProps(this, props);
490498
transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
499+
transaction.getReactMountReady().enqueue(postUpdateInputWrapper, this);
491500
break;
492501
case 'option':
493502
ReactDOMOption.mountWrapper(this, props, nativeParent);
@@ -502,6 +511,7 @@ ReactDOMComponent.Mixin = {
502511
ReactDOMTextarea.mountWrapper(this, props, nativeParent);
503512
props = ReactDOMTextarea.getNativeProps(this, props);
504513
transaction.getReactMountReady().enqueue(trapBubbledEventsLocal, this);
514+
transaction.getReactMountReady().enqueue(postUpdateTextareaWrapper, this);
505515
break;
506516
}
507517

@@ -811,10 +821,20 @@ ReactDOMComponent.Mixin = {
811821
context
812822
);
813823

814-
if (this._tag === 'select') {
815-
// <select> value update needs to occur after <option> children
816-
// reconciliation
817-
transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
824+
switch (this._tag) {
825+
case 'button':
826+
break;
827+
case 'input':
828+
transaction.getReactMountReady().enqueue(postUpdateInputWrapper, this);
829+
break;
830+
case 'option':
831+
break;
832+
case 'select':
833+
transaction.getReactMountReady().enqueue(postUpdateSelectWrapper, this);
834+
break;
835+
case 'textarea':
836+
transaction.getReactMountReady().enqueue(postUpdateTextareaWrapper, this);
837+
break;
818838
}
819839
},
820840

src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ describe('ReactDOMComponent', function() {
492492
node.setAttribute = function(key, value) {
493493
oldSetAttribute(key, value);
494494
nodeValueSetter(key, value);
495-
}
495+
};
496496

497497
ReactDOM.render(<div value="" />, container);
498498
expect(nodeValueSetter.mock.calls.length).toBe(0);

0 commit comments

Comments
 (0)