Skip to content

Commit 4d2fe9b

Browse files
author
Maxime Nory
committed
add isHydrating argument and tests
1 parent 1684cfc commit 4d2fe9b

3 files changed

Lines changed: 41 additions & 13 deletions

File tree

packages/react-dom/src/__tests__/ReactDOMServerIntegrationForms-test.js

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,9 +348,13 @@ describe('ReactDOMServerIntegration', () => {
348348
ControlledSelect;
349349
beforeEach(() => {
350350
ControlledInput = class extends React.Component {
351+
static defaultProps = {
352+
type: 'text',
353+
initialValue: 'Hello',
354+
};
351355
constructor() {
352-
super();
353-
this.state = {value: 'Hello'};
356+
super(...arguments);
357+
this.state = {value: this.props.initialValue};
354358
}
355359
handleChange(event) {
356360
if (this.props.onChange) {
@@ -361,6 +365,7 @@ describe('ReactDOMServerIntegration', () => {
361365
render() {
362366
return (
363367
<input
368+
type={this.props.type}
364369
value={this.state.value}
365370
onChange={this.handleChange.bind(this)}
366371
/>
@@ -551,6 +556,27 @@ describe('ReactDOMServerIntegration', () => {
551556
expect(changeCount).toBe(0);
552557
});
553558

559+
it('should not blow away user-interaction on successful reconnect to an uncontrolled range input', () =>
560+
testUserInteractionBeforeClientRender(
561+
<input type="text" defaultValue="0.5" />,
562+
'0.5',
563+
'1',
564+
));
565+
566+
it('should not blow away user-interaction on successful reconnect to a controlled range input', async () => {
567+
let changeCount = 0;
568+
await testUserInteractionBeforeClientRender(
569+
<ControlledInput
570+
type="range"
571+
initialValue="0.25"
572+
onChange={() => changeCount++}
573+
/>,
574+
'0.25',
575+
'1',
576+
);
577+
expect(changeCount).toBe(0);
578+
});
579+
554580
it('should not blow away user-entered text on successful reconnect to an uncontrolled checkbox', () =>
555581
testUserInteractionBeforeClientRender(
556582
<input type="checkbox" defaultChecked={true} />,

packages/react-dom/src/client/ReactDOMFiberComponent.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ export function setInitialProperties(
530530
// TODO: Make sure we check if this is still unmounted or do any clean
531531
// up necessary since we never stop tracking anymore.
532532
inputValueTracking.track((domElement: any));
533-
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
533+
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, false);
534534
break;
535535
case 'textarea':
536536
// TODO: Make sure we check if this is still unmounted or do any clean
@@ -1077,7 +1077,7 @@ export function diffHydratedProperties(
10771077
// TODO: Make sure we check if this is still unmounted or do any clean
10781078
// up necessary since we never stop tracking anymore.
10791079
inputValueTracking.track((domElement: any));
1080-
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
1080+
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, true);
10811081
break;
10821082
case 'textarea':
10831083
// TODO: Make sure we check if this is still unmounted or do any clean

packages/react-dom/src/client/ReactDOMFiberInput.js

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -205,27 +205,29 @@ export function updateWrapper(element: Element, props: Object) {
205205
}
206206
}
207207

208-
export function postMountWrapper(element: Element, props: Object) {
208+
export function postMountWrapper(element: Element, props: Object, isHydrating: boolean) {
209209
const node = ((element: any): InputWithWrapperState);
210210

211211
if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
212212
const initialValue = '' + node._wrapperState.initialValue;
213-
214-
// With range inputs node.value may be a default value calculated from the
215-
// min/max attributes. This ensures that node.value is set with the correct
216-
// value coming from props.
217-
const currentValue = props.type === 'range' ? '' : node.value;
213+
let currentValue;
214+
if (isHydrating) {
215+
currentValue = node.value;
216+
} else {
217+
// With range inputs node.value may be a default value calculated from the
218+
// min/max attributes. This ensures that node.value is set with the correct
219+
// value coming from props.
220+
currentValue = props.type === 'range' ? '' : node.value;
221+
}
218222

219223
// Do not assign value if it is already set. This prevents user text input
220224
// from being lost during SSR hydration.
221-
if (currentValue === '') {
225+
if (!node.hasAttribute('value')) {
222226
// Do not re-assign the value property if there is no change. This
223227
// potentially avoids a DOM write and prevents Firefox (~60.0.1) from
224228
// prematurely marking required inputs as invalid
225229
if (initialValue !== currentValue) {
226230
node.value = initialValue;
227-
} else if (props.type === 'range') {
228-
node.value = initialValue;
229231
}
230232
}
231233

0 commit comments

Comments
 (0)