Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set the correct initial value on input range #12939

Merged
merged 4 commits into from May 31, 2018
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -348,9 +348,13 @@ describe('ReactDOMServerIntegration', () => {
ControlledSelect;
beforeEach(() => {
ControlledInput = class extends React.Component {
static defaultProps = {
type: 'text',
initialValue: 'Hello',
};
constructor() {
super();
this.state = {value: 'Hello'};
super(...arguments);
this.state = {value: this.props.initialValue};
}
handleChange(event) {
if (this.props.onChange) {
Expand All @@ -361,6 +365,7 @@ describe('ReactDOMServerIntegration', () => {
render() {
return (
<input
type={this.props.type}
value={this.state.value}
onChange={this.handleChange.bind(this)}
/>
Expand Down Expand Up @@ -551,6 +556,27 @@ describe('ReactDOMServerIntegration', () => {
expect(changeCount).toBe(0);
});

it('should not blow away user-interaction on successful reconnect to an uncontrolled range input', () =>
testUserInteractionBeforeClientRender(
<input type="text" defaultValue="0.5" />,
'0.5',
'1',
));

it('should not blow away user-interaction on successful reconnect to a controlled range input', async () => {
let changeCount = 0;
await testUserInteractionBeforeClientRender(
<ControlledInput
type="range"
initialValue="0.25"
onChange={() => changeCount++}
/>,
'0.25',
'1',
);
expect(changeCount).toBe(0);
});

it('should not blow away user-entered text on successful reconnect to an uncontrolled checkbox', () =>
testUserInteractionBeforeClientRender(
<input type="checkbox" defaultChecked={true} />,
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMFiberComponent.js
Expand Up @@ -530,7 +530,7 @@ export function setInitialProperties(
// TODO: Make sure we check if this is still unmounted or do any clean
// up necessary since we never stop tracking anymore.
inputValueTracking.track((domElement: any));
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, false);
break;
case 'textarea':
// TODO: Make sure we check if this is still unmounted or do any clean
Expand Down Expand Up @@ -1077,7 +1077,7 @@ export function diffHydratedProperties(
// TODO: Make sure we check if this is still unmounted or do any clean
// up necessary since we never stop tracking anymore.
inputValueTracking.track((domElement: any));
ReactDOMFiberInput.postMountWrapper(domElement, rawProps);
ReactDOMFiberInput.postMountWrapper(domElement, rawProps, true);
break;
case 'textarea':
// TODO: Make sure we check if this is still unmounted or do any clean
Expand Down
18 changes: 15 additions & 3 deletions packages/react-dom/src/client/ReactDOMFiberInput.js
Expand Up @@ -205,16 +205,28 @@ export function updateWrapper(element: Element, props: Object) {
}
}

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

if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) {
const initialValue = '' + node._wrapperState.initialValue;
const currentValue = node.value;
let currentValue;
if (isHydrating) {
currentValue = node.value;
} else {
// With range inputs node.value may be a default value calculated from the
// min/max attributes. This ensures that node.value is set with the correct
// value coming from props.
currentValue = props.type === 'range' ? '' : node.value;
}

// Do not assign value if it is already set. This prevents user text input
// from being lost during SSR hydration.
if (currentValue === '') {
if (!node.hasAttribute('value')) {
// Do not re-assign the value property if there is no change. This
// potentially avoids a DOM write and prevents Firefox (~60.0.1) from
// prematurely marking required inputs as invalid
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Woah nice. You added the isHydrating part! Very cool. I have a few questions:

  1. Can you use isHydrating as the condition on line 129, like if (!isHydrating)?
  2. Are the lines between (218-225) necessary? I think the bug was that range inputs never reported "", as required by the old code on line 229, that prevented the value assignment from happening.

Basically, I think the only change we need to make is on line 229:

if (!isHydrating) {
  // ....
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you're absolutely right! I'll update it right away.

Expand Down