diff --git a/src/renderers/dom/client/__tests__/inputValueTracking-test.js b/src/renderers/dom/client/__tests__/inputValueTracking-test.js index 103964cc1d74..8d9c5c9671fb 100644 --- a/src/renderers/dom/client/__tests__/inputValueTracking-test.js +++ b/src/renderers/dom/client/__tests__/inputValueTracking-test.js @@ -11,6 +11,7 @@ 'use strict'; var React = require('React'); +var ReactDOM = require('ReactDOM'); var ReactTestUtils = require('ReactTestUtils'); var inputValueTracking = require('inputValueTracking'); @@ -143,16 +144,38 @@ describe('inputValueTracking', function() { it('should stop tracking', function() { inputValueTracking.track(mockComponent); - expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe( - true, - ); + expect(mockComponent._wrapperState.valueTracker).not.toEqual(null); inputValueTracking.stopTracking(mockComponent); - expect(mockComponent._wrapperState.hasOwnProperty('valueTracker')).toBe( - false, - ); + expect(mockComponent._wrapperState.valueTracker).toEqual(null); expect(input.hasOwnProperty('value')).toBe(false); }); + + it('does not crash for nodes with custom value property', () => { + // https://github.com/facebook/react/issues/10196 + try { + var originalCreateElement = document.createElement; + document.createElement = function() { + var node = originalCreateElement.apply(this, arguments); + Object.defineProperty(node, 'value', { + get() {}, + set() {}, + }); + return node; + }; + var div = document.createElement('div'); + // Mount + var node = ReactDOM.render(, div); + // Update + ReactDOM.render(, div); + // Change + ReactTestUtils.SimulateNative.change(node); + // Unmount + ReactDOM.unmountComponentAtNode(div); + } finally { + document.createElement = originalCreateElement; + } + }); }); diff --git a/src/renderers/dom/client/inputValueTracking.js b/src/renderers/dom/client/inputValueTracking.js index f0086f03a153..5b300d5d05f1 100644 --- a/src/renderers/dom/client/inputValueTracking.js +++ b/src/renderers/dom/client/inputValueTracking.js @@ -31,7 +31,7 @@ function attachTracker(inst, tracker) { } function detachTracker(inst) { - delete inst._wrapperState.valueTracker; + inst._wrapperState.valueTracker = null; } function getValueFromNode(node) { diff --git a/src/renderers/dom/shared/ReactDOMComponent.js b/src/renderers/dom/shared/ReactDOMComponent.js index 6c7a1ca8bd01..77d1df67aa5e 100644 --- a/src/renderers/dom/shared/ReactDOMComponent.js +++ b/src/renderers/dom/shared/ReactDOMComponent.js @@ -909,6 +909,10 @@ ReactDOMComponent.Mixin = { // happen after `_updateDOMProperties`. Otherwise HTML5 input validations // raise warnings and prevent the new value from being assigned. ReactDOMInput.updateWrapper(this); + + // We also check that we haven't missed a value update, such as a + // Radio group shifting the checked value to another named radio input. + inputValueTracking.updateValueIfChanged(this); break; case 'textarea': ReactDOMTextarea.updateWrapper(this);