19
20
type InputWithWrapperState = HTMLInputElement & {
21
_wrapperState: {
22
- initialValue: ?string,
22
+ initialValue: string,
23
initialChecked: ?boolean,
24
controlled?: boolean,
25
},
58
59
export function getHostProps(element: Element, props: Object) {
60
var node = ((element: any): InputWithWrapperState);
61
- var value = props.value;
61
var checked = props.checked;
62
64
- var hostProps = Object.assign(
65
- {
66
- // Make sure we set .type before any other properties (setting .value
67
- // before .type means .value is lost in IE11 and below)
68
- type: undefined,
69
- // Make sure we set .step before .value (setting .value before .step
70
- // means .value is rounded on mount, based upon step precision)
71
- step: undefined,
72
- // Make sure we set .min & .max before .value (to ensure proper order
73
- // in corner cases such as min or max deriving from value, e.g. Issue #7170)
74
- min: undefined,
75
- max: undefined,
76
- },
77
- props,
78
- {
79
- defaultChecked: undefined,
80
- defaultValue: undefined,
81
- value: value != null ? value : node._wrapperState.initialValue,
82
- checked: checked != null ? checked : node._wrapperState.initialChecked,
83
- },
84
- );
63
+ var hostProps = Object.assign({}, props, {
64
+ defaultChecked: undefined,
65
+ defaultValue: undefined,
66
+ value: undefined,
67
+ checked: checked != null ? checked : node._wrapperState.initialChecked,
68
+ });
69
70
return hostProps;
71
}
116
}
117
}
118
135
- var defaultValue = props.defaultValue;
119
+ var defaultValue = props.defaultValue == null ? '' : props.defaultValue;
120
var node = ((element: any): InputWithWrapperState);
121
node._wrapperState = {
122
initialChecked:
199
// browsers typically do this as necessary, jsdom doesn't.
200
node.value = '' + value;
201
}
218
- } else {
219
- if (props.value == null && props.defaultValue != null) {
220
- // In Chrome, assigning defaultValue to certain input types triggers input validation.
221
- // For number inputs, the display value loses trailing decimal points. For email inputs,
222
- // Chrome raises "The specified value <x> is not a valid email address".
223
- //
224
- // Here we check to see if the defaultValue has actually changed, avoiding these problems
225
- // when the user is inputting text
226
- //
227
- // https://github.com/facebook/react/issues/7253
228
- if (node.defaultValue !== '' + props.defaultValue) {
229
- node.defaultValue = '' + props.defaultValue;
230
- }
231
- }
232
- if (props.checked == null && props.defaultChecked != null) {
233
- node.defaultChecked = !!props.defaultChecked;
234
- }
202
+ synchronizeDefaultValue(node, props.type, value);
203
+ } else if (
204
+ props.hasOwnProperty('value') ||
205
+ props.hasOwnProperty('defaultValue')
206
+ ) {
207
+ synchronizeDefaultValue(node, props.type, props.defaultValue);
208
+ }
209
+
210
+ if (props.checked == null && props.defaultChecked != null) {
211
+ node.defaultChecked = !!props.defaultChecked;
212
}
213
}
214
215
export function postMountWrapper(element: Element, props: Object) {
216
var node = ((element: any): InputWithWrapperState);
217
+ var initialValue = node._wrapperState.initialValue;
218
241
- // Detach value from defaultValue. We won't do anything if we're working on
242
- // submit or reset inputs as those values & defaultValues are linked. They
243
- // are not resetable nodes so this operation doesn't matter and actually
244
- // removes browser-default values (eg "Submit Query") when no value is
245
- // provided.
219
+ if (props.value != null || props.defaultValue != null) {
220
+ // Do not assign value if it is already set. This prevents user text input
221
+ // from being lost during SSR hydration.
222
+ if (node.value === '') {
223
+ node.value = initialValue;
224
+ }
225
247
- switch (props.type) {
248
- case 'submit':
249
- case 'reset':
250
- break;
251
- case 'color':
252
- case 'date':
253
- case 'datetime':
254
- case 'datetime-local':
255
- case 'month':
256
- case 'time':
257
- case 'week':
258
- // This fixes the no-show issue on iOS Safari and Android Chrome:
259
- // https://github.com/facebook/react/issues/7233
260
- node.value = '';
261
- node.value = node.defaultValue;
262
- break;
263
- default:
264
- node.value = node.value;
265
- break;
226
+ // value must be assigned before defaultValue. This fixes an issue where the
227
+ // visually displayed value of date inputs disappears on mobile Safari and Chrome:
228
+ // https://github.com/facebook/react/issues/7233
229
+ node.defaultValue = initialValue;
230
}
231
232
// Normally, we'd just do `node.checked = node.checked` upon initial mount, less this bug
298
}
299
}
300
}
301
+
302
+// In Chrome, assigning defaultValue to certain input types triggers input validation.
303
+// For number inputs, the display value loses trailing decimal points. For email inputs,
304
+// Chrome raises "The specified value <x> is not a valid email address".
305
+//
306
+// Here we check to see if the defaultValue has actually changed, avoiding these problems
307
+// when the user is inputting text
308
+//
309
+// https://github.com/facebook/react/issues/7253
310
+function synchronizeDefaultValue(
311
+ node: InputWithWrapperState,
312
+ type: ?string,
313
+ value: *,
314
+) {
315
+ if (
316
+ // Focused number inputs synchronize on blur. See ChangeEventPlugin.js
317
+ (type !== 'number' || node.ownerDocument.activeElement !== node) &&
318
+ node.defaultValue !== '' + value
319
+ ) {
320
+ if (value != null) {
321
+ node.defaultValue = '' + value;
322
+ } else {
323
+ node.defaultValue = node._wrapperState.initialValue;
324
+ }
325
+ }
326
+}