Skip to content

Commit

Permalink
Warn if PropType function is called manually (#7132)
Browse files Browse the repository at this point in the history
* Warn if PropType function is called in production

* Check if console is undefined before warning

* Randomize value of ReactPropTypesSecret

* Remove dev environment tests

* Rename typeCheckPass to productionWarningCheck

* Rename productionWarningCheck to expectWarningInProduction

* Call toString on Math.random()

* Rename test block for React type checks

* Make sure warning isnt emitted for failing props

* Cache warning by component and prop, warn in dev

* Pass ReactPropTypesSecret to internal checks

* Move tests to ReactPropTypes-test.js

* Update the warning message to include link

* Do not test warning for unions  with invalid args
  • Loading branch information
aweary authored and gaearon committed Jul 5, 2016
1 parent 5d31ebc commit 95ac239
Show file tree
Hide file tree
Showing 6 changed files with 273 additions and 13 deletions.
9 changes: 7 additions & 2 deletions src/addons/link/__tests__/ReactLinkPropTypes-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ var emptyFunction = require('emptyFunction');
var LinkPropTypes = require('ReactLink').PropTypes;
var React = require('React');
var ReactPropTypeLocations = require('ReactPropTypeLocations');
var ReactPropTypesSecret = require('ReactPropTypesSecret');

var invalidMessage = 'Invalid prop `testProp` supplied to `testComponent`.';
var requiredMessage =
Expand All @@ -26,7 +27,9 @@ function typeCheckFail(declaration, value, message) {
props,
'testProp',
'testComponent',
ReactPropTypeLocations.prop
ReactPropTypeLocations.prop,
null,
ReactPropTypesSecret
);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe(message);
Expand All @@ -38,7 +41,9 @@ function typeCheckPass(declaration, value) {
props,
'testProp',
'testComponent',
ReactPropTypeLocations.prop
ReactPropTypeLocations.prop,
null,
ReactPropTypesSecret
);
expect(error).toBe(null);
}
Expand Down
63 changes: 56 additions & 7 deletions src/isomorphic/classic/types/ReactPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

var ReactElement = require('ReactElement');
var ReactPropTypeLocationNames = require('ReactPropTypeLocationNames');
var ReactPropTypesSecret = require('ReactPropTypesSecret');

var emptyFunction = require('emptyFunction');
var getIteratorFn = require('getIteratorFn');
Expand Down Expand Up @@ -105,16 +106,41 @@ function is(x, y) {
/*eslint-enable no-self-compare*/

function createChainableTypeChecker(validate) {
if (__DEV__) {
var manualPropTypeCallCache = {};
}
function checkType(
isRequired,
props,
propName,
componentName,
location,
propFullName
propFullName,
secret
) {
componentName = componentName || ANONYMOUS;
propFullName = propFullName || propName;
if (__DEV__) {
if (
secret !== ReactPropTypesSecret &&
typeof console !== 'undefined'
) {
var cacheKey = `${componentName}:${propName}`;
if (!manualPropTypeCallCache[cacheKey]) {
warning(
false,
'You are manually calling a React.PropTypes validation ' +
'function for the `%s` prop on `%s`. This is deprecated ' +
'and will not work in the next major version. You may be ' +
'seeing this warning due to a third-party PropTypes library. ' +
'See https://fb.me/react-warning-dont-call-proptypes for details.',
propFullName,
componentName
);
manualPropTypeCallCache[cacheKey] = true;
}
}
}
if (props[propName] == null) {
var locationName = ReactPropTypeLocationNames[location];
if (isRequired) {
Expand All @@ -125,7 +151,13 @@ function createChainableTypeChecker(validate) {
}
return null;
} else {
return validate(props, propName, componentName, location, propFullName);
return validate(
props,
propName,
componentName,
location,
propFullName,
);
}
}

Expand All @@ -136,7 +168,14 @@ function createChainableTypeChecker(validate) {
}

function createPrimitiveTypeChecker(expectedType) {
function validate(props, propName, componentName, location, propFullName) {
function validate(
props,
propName,
componentName,
location,
propFullName,
secret
) {
var propValue = props[propName];
var propType = getPropType(propValue);
if (propType !== expectedType) {
Expand Down Expand Up @@ -183,7 +222,8 @@ function createArrayOfTypeChecker(typeChecker) {
i,
componentName,
location,
`${propFullName}[${i}]`
`${propFullName}[${i}]`,
ReactPropTypesSecret
);
if (error instanceof Error) {
return error;
Expand Down Expand Up @@ -272,7 +312,8 @@ function createObjectOfTypeChecker(typeChecker) {
key,
componentName,
location,
`${propFullName}.${key}`
`${propFullName}.${key}`,
ReactPropTypesSecret
);
if (error instanceof Error) {
return error;
Expand All @@ -294,7 +335,14 @@ function createUnionTypeChecker(arrayOfTypeCheckers) {
for (var i = 0; i < arrayOfTypeCheckers.length; i++) {
var checker = arrayOfTypeCheckers[i];
if (
checker(props, propName, componentName, location, propFullName) == null
checker(
props,
propName,
componentName,
location,
propFullName,
ReactPropTypesSecret
) == null
) {
return null;
}
Expand Down Expand Up @@ -344,7 +392,8 @@ function createShapeTypeChecker(shapeTypes) {
key,
componentName,
location,
`${propFullName}.${key}`
`${propFullName}.${key}`,
ReactPropTypesSecret
);
if (error) {
return error;
Expand Down
18 changes: 18 additions & 0 deletions src/isomorphic/classic/types/ReactPropTypesSecret.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactPropTypesSecret
*/


'use strict';


const ReactPropTypesSecret = '__REACT_PROP_TYPES_SECRET__' + Math.random().toString();

module.exports = ReactPropTypesSecret;
Loading

0 comments on commit 95ac239

Please sign in to comment.