Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions packages/react-dom/src/__tests__/ReactDOMInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,31 +54,31 @@ describe('ReactDOMInput', () => {
expect(() => {
ReactDOM.render(<input type="text" value={0} />, container);
}).toErrorDev(
'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
'Warning: You provided a `value` prop to a form field without an `onChange` handler.',
);
});

it('should warn for controlled value of "" with missing onChange', () => {
expect(() => {
ReactDOM.render(<input type="text" value="" />, container);
}).toErrorDev(
'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
'Warning: You provided a `value` prop to a form field without an `onChange` handler.',
);
});

it('should warn for controlled value of "0" with missing onChange', () => {
expect(() => {
ReactDOM.render(<input type="text" value="0" />, container);
}).toErrorDev(
'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
'Warning: You provided a `value` prop to a form field without an `onChange` handler.',
);
});

it('should warn for controlled value of false with missing onChange', () => {
expect(() =>
ReactDOM.render(<input type="checkbox" checked={false} />, container),
).toErrorDev(
'Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler.',
'Warning: You provided a `checked` prop to a form field without an `onChange` handler.',
);
});

Expand All @@ -95,7 +95,7 @@ describe('ReactDOMInput', () => {
container,
),
).toErrorDev(
'Failed prop type: You provided a `checked` prop to a form field without an `onChange` handler. ' +
'Warning: You provided a `checked` prop to a form field without an `onChange` handler. ' +
'This will render a read-only field. If the field should be mutable use `defaultChecked`. ' +
'Otherwise, set either `onChange` or `readOnly`.',
);
Expand Down Expand Up @@ -125,7 +125,7 @@ describe('ReactDOMInput', () => {
expect(() => {
node = ReactDOM.render(<input type="text" value="lion" />, container);
}).toErrorDev(
'Failed prop type: You provided a `value` prop to a form field without an `onChange` handler.',
'Warning: You provided a `value` prop to a form field without an `onChange` handler.',
);

setUntrackedValue.call(node, 'giraffe');
Expand Down Expand Up @@ -1172,7 +1172,7 @@ describe('ReactDOMInput', () => {
container,
),
).toErrorDev(
'Warning: Failed prop type: You provided a `value` prop to a form ' +
'Warning: You provided a `value` prop to a form ' +
'field without an `onChange` handler. This will render a read-only ' +
'field. If the field should be mutable use `defaultValue`. ' +
'Otherwise, set either `onChange` or `readOnly`.\n' +
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import invariant from 'shared/invariant';
import {setValueForProperty} from './DOMPropertyOperations';
import {getFiberCurrentPropsFromNode} from './ReactDOMComponentTree';
import {getToStringValue, toString} from './ToStringValue';
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
import {updateValueIfChanged} from './inputValueTracking';
import {disableInputAttributeSyncing} from 'shared/ReactFeatureFlags';

Expand Down Expand Up @@ -73,7 +73,7 @@ export function getHostProps(element: Element, props: Object) {

export function initWrapperState(element: Element, props: Object) {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('input', props);
checkControlledValueProps('input', props);

if (
props.checked !== undefined &&
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// TODO: direct imports like some-package/src/* are bad. Fix me.
import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber';

import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
import {getToStringValue, toString} from './ToStringValue';

let didWarnValueDefaultValue;
Expand Down Expand Up @@ -38,7 +38,7 @@ const valuePropNames = ['value', 'defaultValue'];
*/
function checkSelectPropTypes(props) {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('select', props);
checkControlledValueProps('select', props);

for (let i = 0; i < valuePropNames.length; i++) {
const propName = valuePropNames[i];
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dom/src/client/ReactDOMTextarea.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import invariant from 'shared/invariant';

import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
import {getCurrentFiberOwnerNameInDevOrNull} from 'react-reconciler/src/ReactCurrentFiber';
import {getToStringValue, toString} from './ToStringValue';
import type {ToStringValue} from './ToStringValue';
Expand Down Expand Up @@ -64,7 +64,7 @@ export function getHostProps(element: Element, props: Object) {
export function initWrapperState(element: Element, props: Object) {
const node = ((element: any): TextAreaWithWrapperState);
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('textarea', props);
checkControlledValueProps('textarea', props);
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
Expand Down
8 changes: 4 additions & 4 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ import {
getIntrinsicNamespace,
getChildNamespace,
} from '../shared/DOMNamespaces';
import ReactControlledValuePropTypes from '../shared/ReactControlledValuePropTypes';
import {checkControlledValueProps} from '../shared/ReactControlledValuePropTypes';
import assertValidProps from '../shared/assertValidProps';
import dangerousStyleValue from '../shared/dangerousStyleValue';
import hyphenateStyleName from '../shared/hyphenateStyleName';
Expand Down Expand Up @@ -1358,7 +1358,7 @@ class ReactDOMServerRenderer {
let props = element.props;
if (tag === 'input') {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('input', props);
checkControlledValueProps('input', props);

if (
props.checked !== undefined &&
Expand Down Expand Up @@ -1410,7 +1410,7 @@ class ReactDOMServerRenderer {
);
} else if (tag === 'textarea') {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('textarea', props);
checkControlledValueProps('textarea', props);
if (
props.value !== undefined &&
props.defaultValue !== undefined &&
Expand Down Expand Up @@ -1465,7 +1465,7 @@ class ReactDOMServerRenderer {
});
} else if (tag === 'select') {
if (__DEV__) {
ReactControlledValuePropTypes.checkPropTypes('select', props);
checkControlledValueProps('select', props);

for (let i = 0; i < valuePropNames.length; i++) {
const propName = valuePropNames[i];
Expand Down
82 changes: 29 additions & 53 deletions packages/react-dom/src/shared/ReactControlledValuePropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,81 +5,57 @@
* LICENSE file in the root directory of this source tree.
*/

import checkPropTypes from 'shared/checkPropTypes';
import ReactSharedInternals from 'shared/ReactSharedInternals';
import {enableDeprecatedFlareAPI} from 'shared/ReactFeatureFlags';

let ReactDebugCurrentFrame = null;

const ReactControlledValuePropTypes = {
checkPropTypes: null,
const hasReadOnlyValue = {
button: true,
checkbox: true,
image: true,
hidden: true,
radio: true,
reset: true,
submit: true,
};

if (__DEV__) {
ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;

const hasReadOnlyValue = {
button: true,
checkbox: true,
image: true,
hidden: true,
radio: true,
reset: true,
submit: true,
};

const propTypes = {
value: function(props, propName, componentName) {
if (
export function checkControlledValueProps(
tagName: string,
props: Object,
): void {
if (__DEV__) {
if (
!(
hasReadOnlyValue[props.type] ||
props.onChange ||
props.onInput ||
props.readOnly ||
props.disabled ||
props[propName] == null ||
props.value == null ||
(enableDeprecatedFlareAPI && props.DEPRECATED_flareListeners)
) {
return null;
}
return new Error(
)
) {
console.error(
'You provided a `value` prop to a form field without an ' +
'`onChange` handler. This will render a read-only field. If ' +
'the field should be mutable use `defaultValue`. Otherwise, ' +
'set either `onChange` or `readOnly`.',
);
},
checked: function(props, propName, componentName) {
if (
}

if (
!(
props.onChange ||
props.readOnly ||
props.disabled ||
props[propName] == null ||
props.checked == null ||
(enableDeprecatedFlareAPI && props.DEPRECATED_flareListeners)
) {
return null;
}
return new Error(
)
) {
console.error(
'You provided a `checked` prop to a form field without an ' +
'`onChange` handler. This will render a read-only field. If ' +
'the field should be mutable use `defaultChecked`. Otherwise, ' +
'set either `onChange` or `readOnly`.',
);
},
};

/**
* Provide a linked `value` attribute for controlled forms. You should not use
* this outside of the ReactDOM controlled form components.
*/
ReactControlledValuePropTypes.checkPropTypes = function(tagName, props) {
checkPropTypes(
propTypes,
props,
'prop',
tagName,
ReactDebugCurrentFrame.getStackAddendum,
);
};
}
}
}

export default ReactControlledValuePropTypes;