diff --git a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
index 4bb68cc92bec7..20dc76053880f 100644
--- a/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMSelect-test.js
@@ -808,4 +808,176 @@ describe('ReactDOMSelect', () => {
expect(node.options[1].selected).toBe(false); // b
expect(node.options[2].selected).toBe(false); // c
});
+
+ describe('When given a Symbol value', () => {
+ it('treats initial Symbol value as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats updated Symbol value as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('monkey');
+
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats initial Symbol defaultValue as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats updated Symbol defaultValue as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('monkey');
+
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ expect(node.value).toBe('');
+ });
+ });
+
+ describe('When given a function value', () => {
+ it('treats initial function value as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats initial function defaultValue as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats updated function value as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('monkey');
+
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ expect(node.value).toBe('');
+ });
+
+ it('treats updated function defaultValue as an empty string', () => {
+ let node;
+
+ expect(() => {
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+ }).toWarnDev('Invalid value for prop `value`');
+
+ expect(node.value).toBe('monkey');
+
+ node = ReactTestUtils.renderIntoDocument(
+ ,
+ );
+
+ expect(node.value).toBe('');
+ });
+ });
});
diff --git a/packages/react-dom/src/client/ReactDOMFiberOption.js b/packages/react-dom/src/client/ReactDOMFiberOption.js
index 5c2c561594ff9..9d905ceaaa018 100644
--- a/packages/react-dom/src/client/ReactDOMFiberOption.js
+++ b/packages/react-dom/src/client/ReactDOMFiberOption.js
@@ -10,6 +10,7 @@
import React from 'react';
import warning from 'shared/warning';
import {validateDOMNesting, updatedAncestorInfo} from './validateDOMNesting';
+import {getToStringValue, toString} from './ToStringValue';
let didWarnSelectedSetOnOption = false;
@@ -73,7 +74,7 @@ export function validateProps(element: Element, props: Object) {
export function postMountWrapper(element: Element, props: Object) {
// value="" should make a value attribute (#6219)
if (props.value != null) {
- element.setAttribute('value', props.value);
+ element.setAttribute('value', toString(getToStringValue(props.value)));
}
}
diff --git a/packages/react-dom/src/client/ReactDOMFiberSelect.js b/packages/react-dom/src/client/ReactDOMFiberSelect.js
index dfe71a6ae999c..1c51a02376b18 100644
--- a/packages/react-dom/src/client/ReactDOMFiberSelect.js
+++ b/packages/react-dom/src/client/ReactDOMFiberSelect.js
@@ -12,6 +12,8 @@ import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCur
import warning from 'shared/warning';
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
+import {getToStringValue, toString} from './ToStringValue';
+import type {ToStringValue} from './ToStringValue';
let didWarnValueDefaultValue;
@@ -21,7 +23,7 @@ if (__DEV__) {
type SelectWithWrapperState = HTMLSelectElement & {
_wrapperState: {
- initialValue: ?string,
+ initialValue: ?ToStringValue,
wasMultiple: boolean,
},
};
@@ -98,7 +100,7 @@ function updateOptions(
} else {
// Do not set `select.value` as exact behavior isn't consistent across all
// browsers for all cases.
- let selectedValue = '' + (propValue: string);
+ let selectedValue = toString(getToStringValue((propValue: any)));
let defaultSelected = null;
for (let i = 0; i < options.length; i++) {
if (options[i].value === selectedValue) {