diff --git a/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js b/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js
new file mode 100644
index 0000000000000..1169c61c2d77f
--- /dev/null
+++ b/fixtures/dom/src/components/fixtures/input-change-events/RadioNameChangeFixture.js
@@ -0,0 +1,46 @@
+const React = window.React;
+const noop = n => n;
+
+class RadioNameChangeFixture extends React.Component {
+ state = {
+ updated: false,
+ };
+ onClick = () => {
+ this.setState(state => {
+ return {updated: !state.updated};
+ });
+ };
+ render() {
+ const {updated} = this.state;
+ const radioName = updated ? 'firstName' : 'secondName';
+ return (
+
+ );
+ }
+}
+
+export default RadioNameChangeFixture;
diff --git a/fixtures/dom/src/components/fixtures/input-change-events/index.js b/fixtures/dom/src/components/fixtures/input-change-events/index.js
index 41920e5802403..70764989627aa 100644
--- a/fixtures/dom/src/components/fixtures/input-change-events/index.js
+++ b/fixtures/dom/src/components/fixtures/input-change-events/index.js
@@ -5,6 +5,7 @@ import TestCase from '../../TestCase';
import RangeKeyboardFixture from './RangeKeyboardFixture';
import RadioClickFixture from './RadioClickFixture';
import RadioGroupFixture from './RadioGroupFixture';
+import RadioNameChangeFixture from './RadioNameChangeFixture';
import InputPlaceholderFixture from './InputPlaceholderFixture';
class InputChangeEvents extends React.Component {
@@ -87,6 +88,24 @@ class InputChangeEvents extends React.Component {
+
+
+ Click the toggle button
+
+
+
+ The checked radio button should switch between the first and second radio button
+
+
+
+
);
}
diff --git a/packages/react-dom/src/__tests__/ReactDOMInput-test.js b/packages/react-dom/src/__tests__/ReactDOMInput-test.js
index e968482651e87..31d1a3abda1d9 100644
--- a/packages/react-dom/src/__tests__/ReactDOMInput-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMInput-test.js
@@ -669,6 +669,45 @@ describe('ReactDOMInput', () => {
expect(cNode.checked).toBe(true);
});
+ it('should check the correct radio when the selected name moves', () => {
+ class App extends React.Component {
+ state = {
+ updated: false,
+ };
+ onClick = () => {
+ this.setState({updated: true});
+ };
+ render() {
+ const {updated} = this.state;
+ const radioName = updated ? 'secondName' : 'firstName';
+ return (
+
+
+
+
+
+ );
+ }
+ }
+
+ var stub = ReactTestUtils.renderIntoDocument();
+ var buttonNode = ReactDOM.findDOMNode(stub).childNodes[0];
+ var firstRadioNode = ReactDOM.findDOMNode(stub).childNodes[1];
+ expect(firstRadioNode.checked).toBe(false);
+ ReactTestUtils.Simulate.click(buttonNode);
+ expect(firstRadioNode.checked).toBe(true);
+ });
+
it('should control radio buttons if the tree updates during render', () => {
var sharedParent = document.createElement('div');
var container1 = document.createElement('div');
diff --git a/packages/react-dom/src/client/ReactDOMFiberComponent.js b/packages/react-dom/src/client/ReactDOMFiberComponent.js
index 7919058472796..c77e29a4d63c6 100644
--- a/packages/react-dom/src/client/ReactDOMFiberComponent.js
+++ b/packages/react-dom/src/client/ReactDOMFiberComponent.js
@@ -771,6 +771,17 @@ export function updateProperties(
lastRawProps: Object,
nextRawProps: Object,
): void {
+ // Update checked *before* name.
+ // In the middle of an update, it is possible to have multiple checked.
+ // When a checked radio tries to change name, browser makes another radio's checked false.
+ if (
+ tag === 'input' &&
+ nextRawProps.type === 'radio' &&
+ nextRawProps.name != null
+ ) {
+ ReactDOMFiberInput.updateChecked(domElement, nextRawProps);
+ }
+
var wasCustomComponentTag = isCustomComponent(tag, lastRawProps);
var isCustomComponentTag = isCustomComponent(tag, nextRawProps);
// Apply the diff.
diff --git a/packages/react-dom/src/client/ReactDOMFiberInput.js b/packages/react-dom/src/client/ReactDOMFiberInput.js
index bad8f1645f576..7ddc36ee0c892 100644
--- a/packages/react-dom/src/client/ReactDOMFiberInput.js
+++ b/packages/react-dom/src/client/ReactDOMFiberInput.js
@@ -144,6 +144,14 @@ export function initWrapperState(element: Element, props: Object) {
};
}
+export function updateChecked(element: Element, props: Object) {
+ var node = ((element: any): InputWithWrapperState);
+ var checked = props.checked;
+ if (checked != null) {
+ DOMPropertyOperations.setValueForProperty(node, 'checked', checked);
+ }
+}
+
export function updateWrapper(element: Element, props: Object) {
var node = ((element: any): InputWithWrapperState);
if (__DEV__) {
@@ -183,14 +191,7 @@ export function updateWrapper(element: Element, props: Object) {
}
}
- var checked = props.checked;
- if (checked != null) {
- DOMPropertyOperations.setValueForProperty(
- node,
- 'checked',
- checked || false,
- );
- }
+ updateChecked(element, props);
var value = props.value;
if (value != null) {