Add PropTypes.checkPropTypes API

Allows users to check arbitrary objects against React prop types without
relying on the implementation details of the prop validators themselves.
An example of why this is important: some prop validators may throw,
while others return an error object. Calling a prop validator manually
requires handling both cases. `checkPropTypes` does this for you.
This commit is contained in:
Andrew Clark
2017-02-23 11:14:14 -08:00
parent bef723e47f
commit 600685bdef
3 changed files with 58 additions and 58 deletions
@@ -13,6 +13,7 @@
var ReactElement = require('ReactElement');
var ReactPropTypesSecret = require('ReactPropTypesSecret');
var checkReactTypeSpec = require('checkReactTypeSpec');
var emptyFunction = require('emptyFunction');
var getIteratorFn = require('getIteratorFn');
@@ -122,6 +123,12 @@ if (__DEV__) {
};
}
function checkPropTypes(propTypes, object, location, componentName, warnOnRepeat) {
checkReactTypeSpec(propTypes, object, location, componentName, null, null, warnOnRepeat);
}
ReactPropTypes.checkPropTypes = checkPropTypes;
/**
* inlined Object.is polyfill to avoid requiring consumers ship their own
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
@@ -15,23 +15,36 @@ var PropTypes;
var React;
var ReactFragment;
var ReactTestUtils;
var ReactPropTypesSecret;
var Component;
var MyComponent;
function typeCheckFail(declaration, value, message) {
var props = {testProp: value};
var error = declaration(
props,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error instanceof Error).toBe(true);
expect(error.message).toBe(message);
function getPropTypeWarningMessage(propTypes, object, componentName) {
if (!console.error.calls) {
spyOn(console, 'error');
} else {
console.error.calls.reset();
}
PropTypes.checkPropTypes(propTypes, object, 'prop', 'testComponent', true);
const callCount = console.error.calls.count();
if (callCount > 1) {
throw new Error('Too many warnings.');
}
const message = console.error.calls.argsFor(0)[0] || null;
console.error.calls.reset();
return message;
}
function typeCheckFail(declaration, value, expectedMessage) {
const propTypes = {
testProp: declaration,
};
const props = {
testProp: value,
};
const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
expect(message).toContain(expectedMessage);
}
function typeCheckFailRequiredValues(declaration) {
@@ -39,52 +52,31 @@ function typeCheckFailRequiredValues(declaration) {
'`testComponent`, but its value is `null`.';
var unspecifiedMsg = 'The prop `testProp` is marked as required in ' +
'`testComponent`, but its value is \`undefined\`.';
var props1 = {testProp: null};
var error1 = declaration(
props1,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error1 instanceof Error).toBe(true);
expect(error1.message).toBe(specifiedButIsNullMsg);
var props2 = {testProp: undefined};
var error2 = declaration(
props2,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error2 instanceof Error).toBe(true);
expect(error2.message).toBe(unspecifiedMsg);
var props3 = {};
var error3 = declaration(
props3,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error3 instanceof Error).toBe(true);
expect(error3.message).toBe(unspecifiedMsg);
var propTypes = { testProp: declaration };
// Required prop is null
var message1 = getPropTypeWarningMessage(propTypes, { testProp: null }, 'testComponent');
expect(message1).toContain(specifiedButIsNullMsg);
// Required prop is undefined
var message2 = getPropTypeWarningMessage(propTypes, { testProp: undefined }, 'testComponent');
expect(message2).toContain(unspecifiedMsg);
// Required prop is not a member of props object
var message3 = getPropTypeWarningMessage(propTypes, {}, 'testComponent');
expect(message3).toContain(unspecifiedMsg);
}
function typeCheckPass(declaration, value) {
var props = {testProp: value};
var error = declaration(
props,
'testProp',
'testComponent',
'prop',
null,
ReactPropTypesSecret
);
expect(error).toBe(null);
const propTypes = {
testProp: declaration,
};
const props = {
testProp: value,
};
const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
expect(message).toBe(null);
}
function expectWarningInDevelopment(declaration, value) {
@@ -112,7 +104,6 @@ describe('ReactPropTypes', () => {
React = require('React');
ReactFragment = require('ReactFragment');
ReactTestUtils = require('ReactTestUtils');
ReactPropTypesSecret = require('ReactPropTypesSecret');
});
describe('Primitive Types', () => {
+3 -1
View File
@@ -54,6 +54,8 @@ function checkReactTypeSpec(
// It is only safe to pass fiber if it is the work-in-progress version, and
// only during reconciliation (begin and complete phase).
workInProgressOrDebugID,
// Default behavior is to suppress repeated warnings, but can be overridden
warnOnRepeat
) {
for (var typeSpecName in typeSpecs) {
if (typeSpecs.hasOwnProperty(typeSpecName)) {
@@ -88,7 +90,7 @@ function checkReactTypeSpec(
typeSpecName,
typeof error
);
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
if (error instanceof Error && (warnOnRepeat || !(error.message in loggedTypeFailures))) {
// Only monitor this failure once because there tends to be a lot of the
// same error.
loggedTypeFailures[error.message] = true;