-
Notifications
You must be signed in to change notification settings - Fork 47.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Remove JSX propTypes validation #28328
Conversation
5cb2993
to
d7d3ca0
Compare
d7d3ca0
to
bdea370
Compare
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.) DiffTrain build for [353ecd0](353ecd0)
### React upstream changes - facebook/react#28438 - facebook/react#28436 - facebook/react#25954 - facebook/react#28434 - facebook/react#28433 - facebook/react#28432 - facebook/react#28415 - facebook/react#27903 - facebook/react#28430 - facebook/react#28424 - facebook/react#28400 - facebook/react#28422 - facebook/react#28423 - facebook/react#28412 - facebook/react#28418 - facebook/react#28421 - facebook/react#28417 - facebook/react#28399 - facebook/react#28408 - facebook/react#28350 - facebook/react#28387 - facebook/react#28403 - facebook/react#28384 - facebook/react#28409 - facebook/react#28398 - facebook/react#28405 - facebook/react#28328 - facebook/react#28402 - facebook/react#28386 - facebook/react#28388 - facebook/react#28379 - facebook/react#28383 - facebook/react#28390 - facebook/react#28389 - facebook/react#28382 - facebook/react#28348 Closes NEXT-2600
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.)
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.) DiffTrain build for commit 353ecd0.
Hey @gaearon I’d like to know if you have some advice for overriding Since there’s no const oldRender = React.Component.prototype.render
React.Component.prototype.render = function () {
checkPropTypes(this.constructor.propTypes, this.props, 'prop', this.constructor.name)
oldRender()
} …so I can only think of tediously adding a function to every import { checkPropTypes } from 'prop-types'
React.Component.prototype.checkTypes = function () {
checkPropTypes(this.constructor.propTypes, this.props, 'prop', this.constructor.name)
} class Foo extends React.Component {
render() {
this.checkTypes()
return null
}
}
Foo.propTypes = {
myProp: PropTypes.string
} I know you suggested using TypeScript, but TypeScript doesn’t support custom validators as PropTypes do. Also, my project uses integer props names (for performance and obfuscation) so props names are dynamic and proxied, which can’t be done with TS either. Thanks in advance |
I can't think of a seamless way to hack it in — your suggestion above with the manual call per render seems like the easiest way to me. If this is intolerable, then a Babel transform or an equivalent solution seems like the next thing to try. I'd expect a Babel plugin injecting this to take ~30 lines of code. |
I asked Claude to write a little Babel plugin that does this, here's a bit of code. module.exports = function(babel) {
const { types: t } = babel;
function isReactComponent(node) {
if (t.isIdentifier(node)) {
return node.name === 'Component' || node.name === 'PureComponent';
}
if (t.isMemberExpression(node)) {
return (
t.isIdentifier(node.object) &&
node.object.name === 'React' &&
t.isIdentifier(node.property) &&
(node.property.name === 'Component' || node.property.name === 'PureComponent')
);
}
return false;
}
return {
name: "add-prop-types-check",
visitor: {
Program: {
enter(path, state) {
state.checkPropTypesIdentifier = path.scope.generateUidIdentifier("checkPropTypes");
},
exit(path, state) {
const newImport = t.importDeclaration(
[t.importDefaultSpecifier(state.checkPropTypesIdentifier)],
t.stringLiteral("prop-types/checkPropTypes")
);
path.node.body.unshift(newImport);
}
},
ClassDeclaration(path, state) {
if (path.node.superClass && isReactComponent(path.node.superClass)) {
const renderMethod = path.node.body.body.find(
node => t.isClassMethod(node) && node.key.name === "render"
);
if (renderMethod) {
const checkPropsCall = t.expressionStatement(
t.callExpression(
state.checkPropTypesIdentifier,
[
t.memberExpression(
t.memberExpression(t.thisExpression(), t.identifier("constructor")),
t.identifier("propTypes")
),
t.memberExpression(t.thisExpression(), t.identifier("props")),
t.stringLiteral("prop"),
t.memberExpression(
t.memberExpression(t.thisExpression(), t.identifier("constructor")),
t.identifier("name")
)
]
)
);
renderMethod.body.body.unshift(checkPropsCall);
}
}
}
}
};
}; Might need some tweaking but hope this helps. If you don't use Babel, you might want to port this to a different transform engine. |
thank you Dan! |
Are propType checks removed from functional components only or from class-based ones too? The RC release notes just speaks about removal from functional components: https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops |
@matyasf Yes, in 19, @rickhanlonii The release notes say "…for functions" |
the "from functions" is for default props |
Has someone already created this babel plugin? |
|
Thank you! Yep, trying to adjust this now. I use not just UPD: also I don't use class components, but function components:
So yes, I need to tweak it more for sure. |
Claude couldn't help me, but ChatGPT made exactly what I needed. propTypesPlugin.js
module.exports = function ({ types: t }) {
return {
visitor: {
Program(path) {
let usesPropTypes = false;
// Check if `propTypes` or `forbidExtraProps` is used anywhere in the file
path.traverse({
AssignmentExpression(assignPath) {
if (
t.isMemberExpression(assignPath.node.left) &&
assignPath.node.left.property.name === "propTypes"
) {
usesPropTypes = true;
}
},
});
if (usesPropTypes) {
// Add `_checkPropTypes` import at the top of the file
const importDeclaration = t.importDeclaration(
[t.importDefaultSpecifier(t.identifier("_checkPropTypes"))],
t.stringLiteral("prop-types/checkPropTypes")
);
path.unshiftContainer("body", importDeclaration);
}
},
FunctionDeclaration(path) {
insertPropTypesCheck(path, t);
},
VariableDeclarator(path) {
// Handle arrow functions assigned to variables
if (t.isArrowFunctionExpression(path.node.init)) {
insertPropTypesCheck(path, t);
}
},
},
};
function insertPropTypesCheck(path, t) {
const node = path.node;
let componentName;
// Determine the component name
if (t.isFunctionDeclaration(node)) {
componentName = node.id.name;
} else if (t.isVariableDeclarator(node)) {
componentName = node.id.name;
} else {
return;
}
// Traverse the program body to find the `propTypes` assignment
const programNode = path.findParent((parentPath) =>
t.isProgram(parentPath.node)
);
if (!programNode || !programNode.node.body) {
return; // Exit if the program body is not found
}
const programBody = programNode.node.body;
const propTypesAssignment = programBody.find(
(n) =>
t.isExpressionStatement(n) &&
t.isAssignmentExpression(n.expression) &&
t.isMemberExpression(n.expression.left) &&
n.expression.left.object.name === componentName &&
n.expression.left.property.name === "propTypes"
);
if (!propTypesAssignment) {
return; // Skip if no propTypes assignment found
}
let propTypesNode = propTypesAssignment.expression.right;
// Check if propTypes is wrapped in forbidExtraProps
if (
t.isCallExpression(propTypesNode) &&
t.isIdentifier(propTypesNode.callee) &&
propTypesNode.callee.name === "forbidExtraProps"
) {
// Insert the _checkPropTypes call with the wrapped propTypes
const checkPropTypesCall = t.expressionStatement(
t.callExpression(t.identifier("_checkPropTypes"), [
t.cloneNode(propTypesNode), // Pass the wrapped propTypes (the result of forbidExtraProps)
t.identifier("arguments[0]"),
t.stringLiteral("prop"),
t.stringLiteral(componentName),
])
);
// Insert `_checkPropTypes` call at the start of the component
const functionBody =
t.isFunctionDeclaration(node)
? node.body.body
: node.init.body.body;
functionBody.unshift(checkPropTypesCall);
} else {
// If not wrapped in forbidExtraProps, handle normally
const checkPropTypesCall = t.expressionStatement(
t.callExpression(t.identifier("_checkPropTypes"), [
t.cloneNode(propTypesNode),
t.identifier("arguments[0]"),
t.stringLiteral("prop"),
t.stringLiteral(componentName),
])
);
const functionBody =
t.isFunctionDeclaration(node)
? node.body.body
: node.init.body.body;
functionBody.unshift(checkPropTypesCall);
}
}
};
|
This removes the remaining
propTypes
validation calls, making declaringpropTypes
a no-op. In other words, React itself will no longer validate thepropTypes
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:
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.)