diff --git a/packages/react-dom/src/__tests__/ReactDOMInput-test.js b/packages/react-dom/src/__tests__/ReactDOMInput-test.js index f5513557ec3c0..ccd71ee4dcba8 100644 --- a/packages/react-dom/src/__tests__/ReactDOMInput-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMInput-test.js @@ -1700,4 +1700,32 @@ describe('ReactDOMInput', () => { expect(node.hasAttribute('value')).toBe(false); }); }); + + describe('use empty value with input button', function() { + it('should not set a value when value is empty', () => { + let stub = ; + stub = ReactTestUtils.renderIntoDocument(stub); + const node = ReactDOM.findDOMNode(stub); + + expect(node.hasAttribute('value')).toBe(false); + }); + + it('should remove value when value is empty', () => { + class Input extends React.Component { + state = {type: 'submit', value: 'foo'}; + + render() { + const {value, type} = this.state; + return {}} type={type} value={value} />; + } + } + + const input = ReactTestUtils.renderIntoDocument(); + const node = ReactDOM.findDOMNode(input); + + expect(node.getAttribute('value')).toBe('foo'); + input.setState({value: undefined}); + expect(node.hasAttribute('value')).toBe(false); + }); + }); }); diff --git a/packages/react-dom/src/client/ReactDOMFiberInput.js b/packages/react-dom/src/client/ReactDOMFiberInput.js index fd5a574fec377..1268aa021d5e8 100644 --- a/packages/react-dom/src/client/ReactDOMFiberInput.js +++ b/packages/react-dom/src/client/ReactDOMFiberInput.js @@ -35,8 +35,21 @@ let didWarnControlledToUncontrolled = false; let didWarnUncontrolledToControlled = false; function isControlled(props) { - const usesChecked = props.type === 'checkbox' || props.type === 'radio'; - return usesChecked ? props.checked != null : props.value != null; + if (props.type === 'checkbox' || props.type === 'radio') { + return props.checked != null; + } else if (props.type === 'submit' || props.type === 'reset') { + return props.hasOwnProperty('value'); + } else { + return props.value != null; + } +} + +function isEmptyValueButton(props) { + if (props.type !== 'submit' && props.type !== 'reset') { + return false; + } + + return props.value === undefined || props.value === null; } /** @@ -194,7 +207,9 @@ export function updateWrapper(element: Element, props: Object) { } } - if (props.hasOwnProperty('value')) { + if (isEmptyValueButton(props)) { + node.removeAttribute('value'); + } else if (props.hasOwnProperty('value')) { setDefaultValue(node, props.type, value); } else if (props.hasOwnProperty('defaultValue')) { setDefaultValue(node, props.type, getSafeValue(props.defaultValue)); @@ -208,7 +223,10 @@ export function updateWrapper(element: Element, props: Object) { export function postMountWrapper(element: Element, props: Object) { const node = ((element: any): InputWithWrapperState); - if (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) { + if ( + !isEmptyValueButton(props) && + (props.hasOwnProperty('value') || props.hasOwnProperty('defaultValue')) + ) { // Do not assign value if it is already set. This prevents user text input // from being lost during SSR hydration. if (node.value === '') {