@samitouri / QOS-React / commits / 680685bec4

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.

Dan Abramov committed Aug 17, 2016 at 10:04 UTC 680685bec4f260fe635c66297fc3fb38a5116e07
2 files changed +27 -16
src/isomorphic/classic/types/ReactPropTypes.js
+27 -13
@@ -105,6 +105,20 @@ function is(x, y) {
105 }
106 /*eslint-enable no-self-compare*/
107
108 +/**
109 + * We use an Error-like object for backward compatibility as people may call
110 + * PropTypes directly and inspect their output. However we don't use real
111 + * Errors anymore. We don't inspect their stack anyway, and creating them
112 + * is prohibitively expensive if they are created too often, such as what
113 + * happens in oneOfType() for any type before the one that matched.
114 + */
115 +function PropTypeError(message) {
116 + this.message = message;
117 + this.stack = '';
118 +}
119 +// Make `instanceof Error` still work for returned errors.
120 +PropTypeError.prototype = Error.prototype;
121 +
122 function createChainableTypeChecker(validate) {
123 if (__DEV__) {
124 var manualPropTypeCallCache = {};
@@ -145,12 +159,12 @@ function createChainableTypeChecker(validate) {
159 var locationName = ReactPropTypeLocationNames[location];
160 if (isRequired) {
161 if (props[propName] === null) {
148 - return new Error(
162 + return new PropTypeError(
163 `The ${locationName} \`${propFullName}\` is marked as required ` +
164 `in \`${componentName}\`, but its value is \`null\`.`
165 );
166 }
153 - return new Error(
167 + return new PropTypeError(
168 `The ${locationName} \`${propFullName}\` is marked as required in ` +
169 `\`${componentName}\`, but its value is \`undefined\`.`
170 );
@@ -191,7 +205,7 @@ function createPrimitiveTypeChecker(expectedType) {
205 // 'of type `object`'.
206 var preciseType = getPreciseType(propValue);
207
194 - return new Error(
208 + return new PropTypeError(
209 `Invalid ${locationName} \`${propFullName}\` of type ` +
210 `\`${preciseType}\` supplied to \`${componentName}\`, expected ` +
211 `\`${expectedType}\`.`
@@ -209,7 +223,7 @@ function createAnyTypeChecker() {
223 function createArrayOfTypeChecker(typeChecker) {
224 function validate(props, propName, componentName, location, propFullName) {
225 if (typeof typeChecker !== 'function') {
212 - return new Error(
226 + return new PropTypeError(
227 `Property \`${propFullName}\` of component \`${componentName}\` has invalid PropType notation inside arrayOf.`
228 );
229 }
@@ -217,7 +231,7 @@ function createArrayOfTypeChecker(typeChecker) {
231 if (!Array.isArray(propValue)) {
232 var locationName = ReactPropTypeLocationNames[location];
233 var propType = getPropType(propValue);
220 - return new Error(
234 + return new PropTypeError(
235 `Invalid ${locationName} \`${propFullName}\` of type ` +
236 `\`${propType}\` supplied to \`${componentName}\`, expected an array.`
237 );
@@ -246,7 +260,7 @@ function createElementTypeChecker() {
260 if (!ReactElement.isValidElement(propValue)) {
261 var locationName = ReactPropTypeLocationNames[location];
262 var propType = getPropType(propValue);
249 - return new Error(
263 + return new PropTypeError(
264 `Invalid ${locationName} \`${propFullName}\` of type ` +
265 `\`${propType}\` supplied to \`${componentName}\`, expected a single ReactElement.`
266 );
@@ -262,7 +276,7 @@ function createInstanceTypeChecker(expectedClass) {
276 var locationName = ReactPropTypeLocationNames[location];
277 var expectedClassName = expectedClass.name || ANONYMOUS;
278 var actualClassName = getClassName(props[propName]);
265 - return new Error(
279 + return new PropTypeError(
280 `Invalid ${locationName} \`${propFullName}\` of type ` +
281 `\`${actualClassName}\` supplied to \`${componentName}\`, expected ` +
282 `instance of \`${expectedClassName}\`.`
@@ -289,7 +303,7 @@ function createEnumTypeChecker(expectedValues) {
303
304 var locationName = ReactPropTypeLocationNames[location];
305 var valuesString = JSON.stringify(expectedValues);
292 - return new Error(
306 + return new PropTypeError(
307 `Invalid ${locationName} \`${propFullName}\` of value \`${propValue}\` ` +
308 `supplied to \`${componentName}\`, expected one of ${valuesString}.`
309 );
@@ -300,7 +314,7 @@ function createEnumTypeChecker(expectedValues) {
314 function createObjectOfTypeChecker(typeChecker) {
315 function validate(props, propName, componentName, location, propFullName) {
316 if (typeof typeChecker !== 'function') {
303 - return new Error(
317 + return new PropTypeError(
318 `Property \`${propFullName}\` of component \`${componentName}\` has invalid PropType notation inside objectOf.`
319 );
320 }
@@ -308,7 +322,7 @@ function createObjectOfTypeChecker(typeChecker) {
322 var propType = getPropType(propValue);
323 if (propType !== 'object') {
324 var locationName = ReactPropTypeLocationNames[location];
311 - return new Error(
325 + return new PropTypeError(
326 `Invalid ${locationName} \`${propFullName}\` of type ` +
327 `\`${propType}\` supplied to \`${componentName}\`, expected an object.`
328 );
@@ -357,7 +371,7 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
371 }
372
373 var locationName = ReactPropTypeLocationNames[location];
360 - return new Error(
374 + return new PropTypeError(
375 `Invalid ${locationName} \`${propFullName}\` supplied to ` +
376 `\`${componentName}\`.`
377 );
@@ -369,7 +383,7 @@ function createNodeChecker() {
383 function validate(props, propName, componentName, location, propFullName) {
384 if (!isNode(props[propName])) {
385 var locationName = ReactPropTypeLocationNames[location];
372 - return new Error(
386 + return new PropTypeError(
387 `Invalid ${locationName} \`${propFullName}\` supplied to ` +
388 `\`${componentName}\`, expected a ReactNode.`
389 );
@@ -385,7 +399,7 @@ function createShapeTypeChecker(shapeTypes) {
399 var propType = getPropType(propValue);
400 if (propType !== 'object') {
401 var locationName = ReactPropTypeLocationNames[location];
388 - return new Error(
402 + return new PropTypeError(
403 `Invalid ${locationName} \`${propFullName}\` of type \`${propType}\` ` +
404 `supplied to \`${componentName}\`, expected \`object\`.`
405 );
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
-3
@@ -232,8 +232,6 @@ describe('ReactPropTypes', function() {
232 expectWarningInDevelopment(PropTypes.object.isRequired, null);
233 expectWarningInDevelopment(PropTypes.object.isRequired, undefined);
234 });
235 -
236 -
235 });
236
237 describe('Any type', function() {
@@ -259,7 +257,6 @@ describe('ReactPropTypes', function() {
257 expectWarningInDevelopment(PropTypes.any.isRequired, null);
258 expectWarningInDevelopment(PropTypes.any.isRequired, undefined);
259 });
262 -
260 });
261
262 describe('ArrayOf Type', function() {