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.
Andrew Clark committed
Feb 14, 2017 at 16:52 UTC
600685bdefbb965129a73536a65b86cbaa73de48
3 files changed
+58
-58
src/isomorphic/classic/types/ReactPropTypes.js
+7
@@ -13,6 +13,7 @@
13
14
var ReactElement = require('ReactElement');
15
var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
+var checkReactTypeSpec = require('checkReactTypeSpec');
17
18
var emptyFunction = require('emptyFunction');
19
var getIteratorFn = require('getIteratorFn');
@@ -122,6 +123,12 @@ if (__DEV__) {
123
};
124
}
125
126
+function checkPropTypes(propTypes, object, location, componentName, warnOnRepeat) {
127
+ checkReactTypeSpec(propTypes, object, location, componentName, null, null, warnOnRepeat);
128
+}
129
+ReactPropTypes.checkPropTypes = checkPropTypes;
130
+
131
+
132
/**
133
* inlined Object.is polyfill to avoid requiring consumers ship their own
134
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+48
-57
@@ -15,23 +15,36 @@ var PropTypes;
15
var React;
16
var ReactFragment;
17
var ReactTestUtils;
18
-var ReactPropTypesSecret;
18
19
var Component;
20
var MyComponent;
21
23
-function typeCheckFail(declaration, value, message) {
24
- var props = {testProp: value};
25
- var error = declaration(
26
- props,
27
- 'testProp',
28
- 'testComponent',
29
- 'prop',
30
- null,
31
- ReactPropTypesSecret
32
- );
33
- expect(error instanceof Error).toBe(true);
34
- expect(error.message).toBe(message);
22
+function getPropTypeWarningMessage(propTypes, object, componentName) {
23
+ if (!console.error.calls) {
24
+ spyOn(console, 'error');
25
+ } else {
26
+ console.error.calls.reset();
27
+ }
28
+ PropTypes.checkPropTypes(propTypes, object, 'prop', 'testComponent', true);
29
+ const callCount = console.error.calls.count();
30
+ if (callCount > 1) {
31
+ throw new Error('Too many warnings.');
32
+ }
33
+ const message = console.error.calls.argsFor(0)[0] || null;
34
+ console.error.calls.reset();
35
+
36
+ return message;
37
+}
38
+
39
+function typeCheckFail(declaration, value, expectedMessage) {
40
+ const propTypes = {
41
+ testProp: declaration,
42
+ };
43
+ const props = {
44
+ testProp: value,
45
+ };
46
+ const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
47
+ expect(message).toContain(expectedMessage);
48
}
49
50
function typeCheckFailRequiredValues(declaration) {
@@ -39,52 +52,31 @@ function typeCheckFailRequiredValues(declaration) {
52
'`testComponent`, but its value is `null`.';
53
var unspecifiedMsg = 'The prop `testProp` is marked as required in ' +
54
'`testComponent`, but its value is \`undefined\`.';
42
- var props1 = {testProp: null};
43
- var error1 = declaration(
44
- props1,
45
- 'testProp',
46
- 'testComponent',
47
- 'prop',
48
- null,
49
- ReactPropTypesSecret
50
- );
51
- expect(error1 instanceof Error).toBe(true);
52
- expect(error1.message).toBe(specifiedButIsNullMsg);
53
- var props2 = {testProp: undefined};
54
- var error2 = declaration(
55
- props2,
56
- 'testProp',
57
- 'testComponent',
58
- 'prop',
59
- null,
60
- ReactPropTypesSecret
61
- );
62
- expect(error2 instanceof Error).toBe(true);
63
- expect(error2.message).toBe(unspecifiedMsg);
64
- var props3 = {};
65
- var error3 = declaration(
66
- props3,
67
- 'testProp',
68
- 'testComponent',
69
- 'prop',
70
- null,
71
- ReactPropTypesSecret
72
- );
73
- expect(error3 instanceof Error).toBe(true);
74
- expect(error3.message).toBe(unspecifiedMsg);
55
+
56
+ var propTypes = { testProp: declaration };
57
+
58
+ // Required prop is null
59
+ var message1 = getPropTypeWarningMessage(propTypes, { testProp: null }, 'testComponent');
60
+ expect(message1).toContain(specifiedButIsNullMsg);
61
+
62
+ // Required prop is undefined
63
+ var message2 = getPropTypeWarningMessage(propTypes, { testProp: undefined }, 'testComponent');
64
+ expect(message2).toContain(unspecifiedMsg);
65
+
66
+ // Required prop is not a member of props object
67
+ var message3 = getPropTypeWarningMessage(propTypes, {}, 'testComponent');
68
+ expect(message3).toContain(unspecifiedMsg);
69
}
70
71
function typeCheckPass(declaration, value) {
78
- var props = {testProp: value};
79
- var error = declaration(
80
- props,
81
- 'testProp',
82
- 'testComponent',
83
- 'prop',
84
- null,
85
- ReactPropTypesSecret
86
- );
87
- expect(error).toBe(null);
72
+ const propTypes = {
73
+ testProp: declaration,
74
+ };
75
+ const props = {
76
+ testProp: value,
77
+ };
78
+ const message = getPropTypeWarningMessage(propTypes, props, 'testComponent');
79
+ expect(message).toBe(null);
80
}
81
82
function expectWarningInDevelopment(declaration, value) {
@@ -112,7 +104,6 @@ describe('ReactPropTypes', () => {
104
React = require('React');
105
ReactFragment = require('ReactFragment');
106
ReactTestUtils = require('ReactTestUtils');
115
- ReactPropTypesSecret = require('ReactPropTypesSecret');
107
});
108
109
describe('Primitive Types', () => {
src/shared/types/checkReactTypeSpec.js
+3
-1
@@ -54,6 +54,8 @@ function checkReactTypeSpec(
54
// It is only safe to pass fiber if it is the work-in-progress version, and
55
// only during reconciliation (begin and complete phase).
56
workInProgressOrDebugID,
57
+ // Default behavior is to suppress repeated warnings, but can be overridden
58
+ warnOnRepeat
59
) {
60
for (var typeSpecName in typeSpecs) {
61
if (typeSpecs.hasOwnProperty(typeSpecName)) {
@@ -88,7 +90,7 @@ function checkReactTypeSpec(
90
typeSpecName,
91
typeof error
92
);
91
- if (error instanceof Error && !(error.message in loggedTypeFailures)) {
93
+ if (error instanceof Error && (warnOnRepeat || !(error.message in loggedTypeFailures))) {
94
// Only monitor this failure once because there tends to be a lot of the
95
// same error.
96
loggedTypeFailures[error.message] = true;