diff --git a/packages/react-dom-bindings/src/client/ReactDOMInput.js b/packages/react-dom-bindings/src/client/ReactDOMInput.js index 0708b984f7c95..fcede97885728 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMInput.js +++ b/packages/react-dom-bindings/src/client/ReactDOMInput.js @@ -84,7 +84,7 @@ export function validateInputProps(element: Element, props: Object) { export function updateInputChecked(element: Element, props: Object) { const node: HTMLInputElement = (element: any); const checked = props.checked; - if (checked != null && node.checked !== !!checked) { + if (checked != null) { node.checked = checked; } } diff --git a/packages/react-dom-bindings/src/client/ReactDOMTextarea.js b/packages/react-dom-bindings/src/client/ReactDOMTextarea.js index c9862c9b00bef..544cee9952be9 100644 --- a/packages/react-dom-bindings/src/client/ReactDOMTextarea.js +++ b/packages/react-dom-bindings/src/client/ReactDOMTextarea.js @@ -61,6 +61,12 @@ export function validateTextareaProps(element: Element, props: Object) { export function updateTextarea(element: Element, props: Object) { const node: HTMLTextAreaElement = (element: any); const value = getToStringValue(props.value); + const defaultValue = getToStringValue(props.defaultValue); + if (defaultValue != null) { + node.defaultValue = toString(defaultValue); + } else { + node.defaultValue = ''; + } if (value != null) { // Cast `value` to a string to ensure the value is set correctly. While // browsers typically do this as necessary, jsdom doesn't. @@ -70,19 +76,10 @@ export function updateTextarea(element: Element, props: Object) { node.value = newValue; } // TOOO: This should respect disableInputAttributeSyncing flag. - if (props.defaultValue == null) { - if (node.defaultValue !== newValue) { - node.defaultValue = newValue; - } - return; + if (props.defaultValue == null && node.defaultValue !== newValue) { + node.defaultValue = newValue; } } - const defaultValue = getToStringValue(props.defaultValue); - if (defaultValue != null) { - node.defaultValue = toString(defaultValue); - } else { - node.defaultValue = ''; - } } export function initTextarea(element: Element, props: Object) { diff --git a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js index c2dd984823ed1..b8c91fb86e18b 100644 --- a/packages/react-dom/src/__tests__/ReactDOMComponent-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMComponent-test.js @@ -1043,63 +1043,6 @@ describe('ReactDOMComponent', () => { expect(nodeValueSetter).toHaveBeenCalledTimes(2); }); - it('should not incur unnecessary DOM mutations for controlled string properties', () => { - function onChange() {} - const container = document.createElement('div'); - ReactDOM.render(, container); - - const node = container.firstChild; - - let nodeValue = ''; - const nodeValueSetter = jest.fn(); - Object.defineProperty(node, 'value', { - get: function () { - return nodeValue; - }, - set: nodeValueSetter.mockImplementation(function (newValue) { - nodeValue = newValue; - }), - }); - - ReactDOM.render(, container); - expect(nodeValueSetter).toHaveBeenCalledTimes(1); - - ReactDOM.render( - , - container, - ); - expect(nodeValueSetter).toHaveBeenCalledTimes(1); - - expect(() => { - ReactDOM.render(, container); - }).toErrorDev( - 'A component is changing a controlled input to be uncontrolled. This is likely caused by ' + - 'the value changing from a defined to undefined, which should not happen. Decide between ' + - 'using a controlled or uncontrolled input element for the lifetime of the component.', - ); - expect(nodeValueSetter).toHaveBeenCalledTimes(1); - - expect(() => { - ReactDOM.render(, container); - }).toErrorDev( - 'value` prop on `input` should not be null. Consider using an empty string to clear the ' + - 'component or `undefined` for uncontrolled components.', - ); - expect(nodeValueSetter).toHaveBeenCalledTimes(1); - - expect(() => { - ReactDOM.render(, container); - }).toErrorDev( - ' A component is changing an uncontrolled input to be controlled. This is likely caused by ' + - 'the value changing from undefined to a defined value, which should not happen. Decide between ' + - 'using a controlled or uncontrolled input element for the lifetime of the component.', - ); - expect(nodeValueSetter).toHaveBeenCalledTimes(2); - - ReactDOM.render(, container); - expect(nodeValueSetter).toHaveBeenCalledTimes(2); - }); - it('should not incur unnecessary DOM mutations for boolean properties', () => { const container = document.createElement('div'); function onChange() { @@ -1123,12 +1066,7 @@ describe('ReactDOMComponent', () => { }); ReactDOM.render( - , + , container, ); expect(nodeValueSetter).toHaveBeenCalledTimes(0); @@ -1156,13 +1094,15 @@ describe('ReactDOMComponent', () => { 'using a controlled or uncontrolled input element for the lifetime of the component.', ); - expect(nodeValueSetter).toHaveBeenCalledTimes(2); + // TODO: Non-null values are updated twice on inputs. This is should ideally be fixed. + expect(nodeValueSetter).toHaveBeenCalledTimes(3); ReactDOM.render( , container, ); - expect(nodeValueSetter).toHaveBeenCalledTimes(3); + // TODO: Non-null values are updated twice on inputs. This is should ideally be fixed. + expect(nodeValueSetter).toHaveBeenCalledTimes(5); }); it('should ignore attribute list for elements with the "is" attribute', () => { diff --git a/packages/react-dom/src/__tests__/ReactDOMTextarea-test.js b/packages/react-dom/src/__tests__/ReactDOMTextarea-test.js index 347942234063d..396eef8d6d34e 100644 --- a/packages/react-dom/src/__tests__/ReactDOMTextarea-test.js +++ b/packages/react-dom/src/__tests__/ReactDOMTextarea-test.js @@ -603,7 +603,6 @@ describe('ReactDOMTextarea', () => { ref={n => (node = n)} value="foo" onChange={emptyFunction} - data-count={this.state.count} /> );