mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix slow performance of PropTypes.oneOfType() on misses (#7510)
It used to be slow whenever a type miss occurred because expensive `Error` objects were being created. For example, with `oneOfType([number, data])`, passing a date would create an `Error` object in `number` typechecker for every item.
The savings depend on how much commonly you used `oneOfType()`, and how often it had “misses”. If you used it heavily, you might see 1.5x to 2x performance improvements in `__DEV__` after this fix.
(cherry picked from commit 680685bec4)
This commit is contained in:
committed by
Paul O’Shannessy
parent
9e88467485
commit
536f826b56
@@ -105,6 +105,20 @@ function is(x, y) {
|
||||
}
|
||||
/*eslint-enable no-self-compare*/
|
||||
|
||||
/**
|
||||
* We use an Error-like object for backward compatibility as people may call
|
||||
* PropTypes directly and inspect their output. However we don't use real
|
||||
* Errors anymore. We don't inspect their stack anyway, and creating them
|
||||
* is prohibitively expensive if they are created too often, such as what
|
||||
* happens in oneOfType() for any type before the one that matched.
|
||||
*/
|
||||
function PropTypeError(message) {
|
||||
this.message = message;
|
||||
this.stack = '';
|
||||
}
|
||||
// Make `instanceof Error` still work for returned errors.
|
||||
PropTypeError.prototype = Error.prototype;
|
||||
|
||||
function createChainableTypeChecker(validate) {
|
||||
if (__DEV__) {
|
||||
var manualPropTypeCallCache = {};
|
||||
@@ -144,7 +158,7 @@ function createChainableTypeChecker(validate) {
|
||||
if (props[propName] == null) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
if (isRequired) {
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Required ${locationName} \`${propFullName}\` was not specified in ` +
|
||||
`\`${componentName}\`.`
|
||||
);
|
||||
@@ -185,7 +199,7 @@ function createPrimitiveTypeChecker(expectedType) {
|
||||
// 'of type `object`'.
|
||||
var preciseType = getPreciseType(propValue);
|
||||
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type ` +
|
||||
`\`${preciseType}\` supplied to \`${componentName}\`, expected ` +
|
||||
`\`${expectedType}\`.`
|
||||
@@ -203,7 +217,7 @@ function createAnyTypeChecker() {
|
||||
function createArrayOfTypeChecker(typeChecker) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (typeof typeChecker !== 'function') {
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Property \`${propFullName}\` of component \`${componentName}\` has invalid PropType notation inside arrayOf.`
|
||||
);
|
||||
}
|
||||
@@ -211,7 +225,7 @@ function createArrayOfTypeChecker(typeChecker) {
|
||||
if (!Array.isArray(propValue)) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
var propType = getPropType(propValue);
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type ` +
|
||||
`\`${propType}\` supplied to \`${componentName}\`, expected an array.`
|
||||
);
|
||||
@@ -240,7 +254,7 @@ function createElementTypeChecker() {
|
||||
if (!ReactElement.isValidElement(propValue)) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
var propType = getPropType(propValue);
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type ` +
|
||||
`\`${propType}\` supplied to \`${componentName}\`, expected a single ReactElement.`
|
||||
);
|
||||
@@ -256,7 +270,7 @@ function createInstanceTypeChecker(expectedClass) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
var expectedClassName = expectedClass.name || ANONYMOUS;
|
||||
var actualClassName = getClassName(props[propName]);
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type ` +
|
||||
`\`${actualClassName}\` supplied to \`${componentName}\`, expected ` +
|
||||
`instance of \`${expectedClassName}\`.`
|
||||
@@ -283,7 +297,7 @@ function createEnumTypeChecker(expectedValues) {
|
||||
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
var valuesString = JSON.stringify(expectedValues);
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of value \`${propValue}\` ` +
|
||||
`supplied to \`${componentName}\`, expected one of ${valuesString}.`
|
||||
);
|
||||
@@ -294,7 +308,7 @@ function createEnumTypeChecker(expectedValues) {
|
||||
function createObjectOfTypeChecker(typeChecker) {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (typeof typeChecker !== 'function') {
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Property \`${propFullName}\` of component \`${componentName}\` has invalid PropType notation inside objectOf.`
|
||||
);
|
||||
}
|
||||
@@ -302,7 +316,7 @@ function createObjectOfTypeChecker(typeChecker) {
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== 'object') {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type ` +
|
||||
`\`${propType}\` supplied to \`${componentName}\`, expected an object.`
|
||||
);
|
||||
@@ -351,7 +365,7 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
|
||||
}
|
||||
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` supplied to ` +
|
||||
`\`${componentName}\`.`
|
||||
);
|
||||
@@ -363,7 +377,7 @@ function createNodeChecker() {
|
||||
function validate(props, propName, componentName, location, propFullName) {
|
||||
if (!isNode(props[propName])) {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` supplied to ` +
|
||||
`\`${componentName}\`, expected a ReactNode.`
|
||||
);
|
||||
@@ -379,7 +393,7 @@ function createShapeTypeChecker(shapeTypes) {
|
||||
var propType = getPropType(propValue);
|
||||
if (propType !== 'object') {
|
||||
var locationName = ReactPropTypeLocationNames[location];
|
||||
return new Error(
|
||||
return new PropTypeError(
|
||||
`Invalid ${locationName} \`${propFullName}\` of type \`${propType}\` ` +
|
||||
`supplied to \`${componentName}\`, expected \`object\`.`
|
||||
);
|
||||
|
||||
@@ -195,8 +195,6 @@ describe('ReactPropTypes', function() {
|
||||
expectWarningInDevelopment(PropTypes.object.isRequired, null);
|
||||
expectWarningInDevelopment(PropTypes.object.isRequired, undefined);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('Any type', function() {
|
||||
@@ -223,7 +221,6 @@ describe('ReactPropTypes', function() {
|
||||
expectWarningInDevelopment(PropTypes.any.isRequired, null);
|
||||
expectWarningInDevelopment(PropTypes.any.isRequired, undefined);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('ArrayOf Type', function() {
|
||||
|
||||
Reference in New Issue
Block a user