Skip to content

Commit

Permalink
Remove JSX propTypes validation (#28328)
Browse files Browse the repository at this point in the history
This removes the remaining `propTypes` validation calls, making
declaring `propTypes` a no-op. In other words, React itself will no
longer validate the `propTypes` that you declare on your components.

In general, our recommendation is to use static type checking (e.g.
TypeScript). If you'd like to still run propTypes checks, you can do so
manually, same as you'd do outside React:

```js
import checkPropTypes from 'prop-types/checkPropTypes';

function Button(props) {
  checkPropTypes(Button.propTypes, prop, 'prop', Button.name)
  // ...
}
```

This could be automated as a Babel plugin if you want to keep these
checks implicit. (We will not be providing such a plugin, but someone in
community might be interested in building or maintaining one.)
  • Loading branch information
gaearon authored Feb 21, 2024
1 parent 4ea424e commit 353ecd0
Show file tree
Hide file tree
Showing 10 changed files with 12 additions and 588 deletions.
5 changes: 0 additions & 5 deletions packages/react-art/npm/Circle.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
'use strict';

var assign = Object.assign;
var PropTypes = require('prop-types');
var React = require('react');
var ReactART = require('react-art');

Expand All @@ -34,10 +33,6 @@ var Shape = ReactART.Shape;
var Circle = createReactClass({
displayName: 'Circle',

propTypes: {
radius: PropTypes.number.isRequired,
},

render: function render() {
var radius = this.props.radius;

Expand Down
11 changes: 0 additions & 11 deletions packages/react-art/npm/Rectangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
'use strict';

var assign = Object.assign;
var PropTypes = require('prop-types');
var React = require('react');
var ReactART = require('react-art');

Expand All @@ -41,16 +40,6 @@ var Path = ReactART.Path;
var Rectangle = createReactClass({
displayName: 'Rectangle',

propTypes: {
width: PropTypes.number.isRequired,
height: PropTypes.number.isRequired,
radius: PropTypes.number,
radiusTopLeft: PropTypes.number,
radiusTopRight: PropTypes.number,
radiusBottomRight: PropTypes.number,
radiusBottomLeft: PropTypes.number,
},

render: function render() {
var width = this.props.width;
var height = this.props.height;
Expand Down
8 changes: 0 additions & 8 deletions packages/react-art/npm/Wedge.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
'use strict';

var assign = Object.assign;
var PropTypes = require('prop-types');
var React = require('react');
var ReactART = require('react-art');

Expand All @@ -37,13 +36,6 @@ var Path = ReactART.Path;
var Wedge = createReactClass({
displayName: 'Wedge',

propTypes: {
outerRadius: PropTypes.number.isRequired,
startAngle: PropTypes.number.isRequired,
endAngle: PropTypes.number.isRequired,
innerRadius: PropTypes.number,
},

circleRadians: Math.PI * 2,

radiansPerDegree: Math.PI / 180,
Expand Down
39 changes: 0 additions & 39 deletions packages/react-art/src/__tests__/ReactART-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -456,18 +456,6 @@ describe('ReactARTComponents', () => {
expect(circle.toJSON()).toMatchSnapshot();
});

it('should warn if radius is missing on a Circle component', () => {
expect(() =>
ReactTestRenderer.create(
<Circle stroke="green" strokeWidth={3} fill="blue" />,
),
).toErrorDev(
'Warning: Failed prop type: The prop `radius` is marked as required in `Circle`, ' +
'but its value is `undefined`.' +
'\n in Circle (at **)',
);
});

it('should generate a <Shape> with props for drawing the Rectangle', () => {
const rectangle = ReactTestRenderer.create(
<Rectangle width={50} height={50} stroke="green" fill="blue" />,
Expand Down Expand Up @@ -529,19 +517,6 @@ describe('ReactARTComponents', () => {
expect(rectangle.toJSON()).toMatchSnapshot();
});

it('should warn if width/height is missing on a Rectangle component', () => {
expect(() =>
ReactTestRenderer.create(<Rectangle stroke="green" fill="blue" />),
).toErrorDev([
'Warning: Failed prop type: The prop `width` is marked as required in `Rectangle`, ' +
'but its value is `undefined`.' +
'\n in Rectangle (at **)',
'Warning: Failed prop type: The prop `height` is marked as required in `Rectangle`, ' +
'but its value is `undefined`.' +
'\n in Rectangle (at **)',
]);
});

it('should generate a <Shape> with props for drawing the Wedge', () => {
const wedge = ReactTestRenderer.create(
<Wedge outerRadius={50} startAngle={0} endAngle={360} fill="blue" />,
Expand All @@ -555,18 +530,4 @@ describe('ReactARTComponents', () => {
);
expect(wedge.toJSON()).toBeNull();
});

it('should warn if outerRadius/startAngle/endAngle is missing on a Wedge component', () => {
expect(() => ReactTestRenderer.create(<Wedge fill="blue" />)).toErrorDev([
'Warning: Failed prop type: The prop `outerRadius` is marked as required in `Wedge`, ' +
'but its value is `undefined`.' +
'\n in Wedge (at **)',
'Warning: Failed prop type: The prop `startAngle` is marked as required in `Wedge`, ' +
'but its value is `undefined`.' +
'\n in Wedge (at **)',
'Warning: Failed prop type: The prop `endAngle` is marked as required in `Wedge`, ' +
'but its value is `undefined`.' +
'\n in Wedge (at **)',
]);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -433,14 +433,11 @@ describe('ReactFunctionComponent', () => {
);
});

// TODO: change this test after we deprecate default props support
// for function components
it('should support default props and prop types', async () => {
it('should support default props', async () => {
function Child(props) {
return <div>{props.test}</div>;
}
Child.defaultProps = {test: 2};
Child.propTypes = {test: PropTypes.string};

await expect(async () => {
const container = document.createElement('div');
Expand All @@ -451,9 +448,6 @@ describe('ReactFunctionComponent', () => {
});
}).toErrorDev([
'Warning: Child: Support for defaultProps will be removed from function components in a future major release. Use JavaScript default parameters instead.',
'Warning: Failed prop type: Invalid prop `test` of type `number` ' +
'supplied to `Child`, expected `string`.\n' +
' in Child (at **)',
]);
});

Expand Down
37 changes: 0 additions & 37 deletions packages/react/src/__tests__/ReactElementClone-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
'use strict';

let act;
let PropTypes;
let React;
let ReactDOMClient;

Expand All @@ -22,7 +21,6 @@ describe('ReactElementClone', () => {

act = require('internal-test-utils').act;

PropTypes = require('prop-types');
React = require('react');
ReactDOMClient = require('react-dom/client');

Expand Down Expand Up @@ -335,41 +333,6 @@ describe('ReactElementClone', () => {
React.cloneElement(<div />, null, [{}, {}]);
});

it('should check declared prop types after clone', async () => {
class Component extends React.Component {
static propTypes = {
color: PropTypes.string.isRequired,
};
render() {
return React.createElement('div', null, 'My color is ' + this.color);
}
}
class Parent extends React.Component {
render() {
return React.cloneElement(this.props.child, {color: 123});
}
}
class GrandParent extends React.Component {
render() {
return React.createElement(Parent, {
child: React.createElement(Component, {color: 'red'}),
});
}
}
const root = ReactDOMClient.createRoot(document.createElement('div'));
await expect(
async () =>
await act(() => root.render(React.createElement(GrandParent))),
).toErrorDev(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `Component`, ' +
'expected `string`.\n' +
' in Component (at **)\n' +
' in Parent (at **)\n' +
' in GrandParent',
);
});

it('should ignore key and ref warning getters', () => {
const elementA = React.createElement('div');
const elementB = React.cloneElement(elementA, elementA.props);
Expand Down
140 changes: 6 additions & 134 deletions packages/react/src/__tests__/ReactElementValidator-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
// that do use JSX syntax. We should port them to React.createElement, and also
// confirm there's a corresponding test that uses JSX syntax.

let PropTypes;
let React;
let ReactDOMClient;
let act;
Expand All @@ -28,7 +27,6 @@ describe('ReactElementValidator', () => {
beforeEach(() => {
jest.resetModules();

PropTypes = require('prop-types');
ReactFeatureFlags = require('shared/ReactFeatureFlags');
ReactFeatureFlags.replayFailedUnitOfWorkWithInvokeGuardedCallback = false;
React = require('react');
Expand Down Expand Up @@ -221,26 +219,19 @@ describe('ReactElementValidator', () => {
React.createElement(ComponentClass, null, [{}, {}]);
});

it('should give context for PropType errors in nested components.', async () => {
// In this test, we're making sure that if a proptype error is found in a
// component, we give a small hint as to which parent instantiated that
// component as per warnings about key usage in ReactElementValidator.
function MyComp(props) {
return React.createElement('div', null, 'My color is ' + props.color);
it('should give context for errors in nested components.', async () => {
function MyComp() {
return [React.createElement('div')];
}
MyComp.propTypes = {
color: PropTypes.string,
};
function ParentComp() {
return React.createElement(MyComp, {color: 123});
return React.createElement(MyComp);
}
await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(ParentComp)));
}).toErrorDev(
'Warning: Failed prop type: ' +
'Invalid prop `color` of type `number` supplied to `MyComp`, ' +
'expected `string`.\n' +
'Each child in a list should have a unique "key" prop. ' +
'See https://reactjs.org/link/warning-keys for more information.\n' +
' in MyComp (at **)\n' +
' in ParentComp (at **)',
);
Expand Down Expand Up @@ -328,125 +319,6 @@ describe('ReactElementValidator', () => {
]);
});

it('should check default prop values', async () => {
class Component extends React.Component {
static propTypes = {prop: PropTypes.string.isRequired};
static defaultProps = {prop: null};
render() {
return React.createElement('span', null, this.props.prop);
}
}

await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() => root.render(React.createElement(Component)));
}).toErrorDev(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`Component`, but its value is `null`.\n' +
' in Component',
);
});

it('should not check the default for explicit null', async () => {
class Component extends React.Component {
static propTypes = {prop: PropTypes.string.isRequired};
static defaultProps = {prop: 'text'};
render() {
return React.createElement('span', null, this.props.prop);
}
}

await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(React.createElement(Component, {prop: null})),
);
}).toErrorDev(
'Warning: Failed prop type: The prop `prop` is marked as required in ' +
'`Component`, but its value is `null`.\n' +
' in Component',
);
});

it('should check declared prop types', async () => {
class Component extends React.Component {
static propTypes = {
prop: PropTypes.string.isRequired,
};
render() {
return React.createElement('span', null, this.props.prop);
}
}

const root = ReactDOMClient.createRoot(document.createElement('div'));
await expect(async () => {
await act(() => root.render(React.createElement(Component)));
await act(() => root.render(React.createElement(Component, {prop: 42})));
}).toErrorDev([
'Warning: Failed prop type: ' +
'The prop `prop` is marked as required in `Component`, but its value ' +
'is `undefined`.\n' +
' in Component',
'Warning: Failed prop type: ' +
'Invalid prop `prop` of type `number` supplied to ' +
'`Component`, expected `string`.\n' +
' in Component',
]);

// Should not error for strings
await act(() =>
root.render(React.createElement(Component, {prop: 'string'})),
);
});

it('should warn if a PropType creator is used as a PropType', async () => {
class Component extends React.Component {
static propTypes = {
myProp: PropTypes.shape,
};
render() {
return React.createElement('span', null, this.props.myProp.value);
}
}

await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(React.createElement(Component, {myProp: {value: 'hi'}})),
);
}).toErrorDev(
'Warning: Component: type specification of prop `myProp` is invalid; ' +
'the type checker function must return `null` or an `Error` but ' +
'returned a function. You may have forgotten to pass an argument to ' +
'the type checker creator (arrayOf, instanceOf, objectOf, oneOf, ' +
'oneOfType, and shape all require an argument).',
);
});

it('should warn if component declares PropTypes instead of propTypes', async () => {
class MisspelledPropTypesComponent extends React.Component {
static PropTypes = {
prop: PropTypes.string,
};
render() {
return React.createElement('span', null, this.props.prop);
}
}

await expect(async () => {
const root = ReactDOMClient.createRoot(document.createElement('div'));
await act(() =>
root.render(
React.createElement(MisspelledPropTypesComponent, {prop: 'Hi'}),
),
);
}).toErrorDev(
'Warning: Component MisspelledPropTypesComponent declared `PropTypes` ' +
'instead of `propTypes`. Did you misspell the property assignment?',
{withoutStack: true},
);
});

it('warns for fragments with illegal attributes', async () => {
class Foo extends React.Component {
render() {
Expand Down
Loading

0 comments on commit 353ecd0

Please sign in to comment.