Invert dependency between checkPropTypes and checkReactTypeSpec
Expressing checkReactTypeSpec using the public API frees us to move it to a separate module in the future.
Andrew Clark committed
Feb 15, 2017 at 16:08 UTC
3746a9dafb65e077632a79870a75120aa16c4c7f
4 files changed
+114
-92
src/isomorphic/classic/types/ReactPropTypes.js
+1
-4
@@ -13,7 +13,7 @@
13
14
var ReactElement = require('ReactElement');
15
var ReactPropTypesSecret = require('ReactPropTypesSecret');
16
-var checkReactTypeSpec = require('checkReactTypeSpec');
16
+var checkPropTypes = require('checkPropTypes');
17
18
var emptyFunction = require('emptyFunction');
19
var getIteratorFn = require('getIteratorFn');
@@ -123,9 +123,6 @@ if (__DEV__) {
123
};
124
}
125
126
-function checkPropTypes(propTypes, object, location, componentName, warnOnRepeat) {
127
- checkReactTypeSpec(propTypes, object, location, componentName, null, null, warnOnRepeat);
128
-}
126
ReactPropTypes.checkPropTypes = checkPropTypes;
127
128
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+5
-3
@@ -12,6 +12,7 @@
12
'use strict';
13
14
var PropTypes;
15
+var checkReactTypeSpec;
16
var React;
17
var ReactFragment;
18
var ReactTestUtils;
@@ -25,7 +26,7 @@ function getPropTypeWarningMessage(propTypes, object, componentName) {
26
} else {
27
console.error.calls.reset();
28
}
28
- PropTypes.checkPropTypes(propTypes, object, 'prop', 'testComponent', true);
29
+ checkReactTypeSpec(propTypes, object, 'prop', 'testComponent', null, null, true);
30
const callCount = console.error.calls.count();
31
if (callCount > 1) {
32
throw new Error('Too many warnings.');
@@ -101,6 +102,7 @@ function expectWarningInDevelopment(declaration, value) {
102
describe('ReactPropTypes', () => {
103
beforeEach(() => {
104
PropTypes = require('ReactPropTypes');
105
+ checkReactTypeSpec = require('checkReactTypeSpec');
106
React = require('React');
107
ReactFragment = require('ReactFragment');
108
ReactTestUtils = require('ReactTestUtils');
@@ -115,7 +117,7 @@ describe('ReactPropTypes', () => {
117
},
118
};
119
const props = { foo: 'foo' };
118
- const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', true);
120
+ const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', null, true);
121
expect(console.error.calls.argsFor(0)[0]).toContain('some error');
122
expect(returnValue).toBe(undefined);
123
});
@@ -128,7 +130,7 @@ describe('ReactPropTypes', () => {
130
},
131
};
132
const props = { foo: 'foo' };
131
- const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', true);
133
+ const returnValue = PropTypes.checkPropTypes(propTypes, props, 'prop', 'testComponent', null, true);
134
expect(console.error.calls.argsFor(0)[0]).toContain('some error');
135
expect(returnValue).toBe(undefined);
136
});
src/isomorphic/classic/types/checkPropTypes.js
new
+85
@@ -0,0 +1,85 @@
1
+/**
2
+ * Copyright 2013-present, Facebook, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * This source code is licensed under the BSD-style license found in the
6
+ * LICENSE file in the root directory of this source tree. An additional grant
7
+ * of patent rights can be found in the PATENTS file in the same directory.
8
+ *
9
+ * @providesModule checkPropTypes
10
+ */
11
+
12
+'use strict';
13
+
14
+var ReactPropTypesSecret = require('ReactPropTypesSecret');
15
+
16
+var invariant = require('invariant');
17
+var warning = require('warning');
18
+
19
+var loggedTypeFailures = {};
20
+
21
+/**
22
+ * Assert that the values match with the type specs.
23
+ * Error messages are memorized and will only be shown once.
24
+ *
25
+ * @param {object} typeSpecs Map of name to a ReactPropType
26
+ * @param {object} values Runtime values that need to be type-checked
27
+ * @param {string} location e.g. "prop", "context", "child context"
28
+ * @param {string} componentName Name of the component for error messages.
29
+ * @param {?Function} formatMessage Function that transforms the error message, to add additional info.
30
+ * @param {?boolean} warnOnRepeat Whether or not repeated warnings should be skipped.
31
+ * @private
32
+ */
33
+function checkPropTypes(typeSpecs, values, location, componentName, formatMessage, warnOnRepeat) {
34
+ for (var typeSpecName in typeSpecs) {
35
+ if (typeSpecs.hasOwnProperty(typeSpecName)) {
36
+ var error;
37
+ // Prop type validation may throw. In case they do, we don't want to
38
+ // fail the render phase where it didn't fail before. So we log it.
39
+ // After these have been cleaned up, we'll let them throw.
40
+ try {
41
+ // This is intentionally an invariant that gets caught. It's the same
42
+ // behavior as without this statement except with a better message.
43
+ invariant(
44
+ typeof typeSpecs[typeSpecName] === 'function',
45
+ '%s: %s type `%s` is invalid; it must be a function, usually from ' +
46
+ 'React.PropTypes.',
47
+ componentName || 'React class',
48
+ location,
49
+ typeSpecName
50
+ );
51
+ error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
52
+ } catch (ex) {
53
+ error = ex;
54
+ }
55
+ warning(
56
+ !error || error instanceof Error,
57
+ '%s: type specification of %s `%s` is invalid; the type checker ' +
58
+ 'function must return `null` or an `Error` but returned a %s. ' +
59
+ 'You may have forgotten to pass an argument to the type checker ' +
60
+ 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
61
+ 'shape all require an argument).',
62
+ componentName || 'React class',
63
+ location,
64
+ typeSpecName,
65
+ typeof error
66
+ );
67
+ if (error instanceof Error && (warnOnRepeat || !(error.message in loggedTypeFailures))) {
68
+ // Only monitor this failure once because there tends to be a lot of the
69
+ // same error.
70
+ loggedTypeFailures[error.message] = true;
71
+
72
+ var message = formatMessage ? formatMessage(error.message) : error.message;
73
+
74
+ warning(
75
+ false,
76
+ 'Failed %s type: %s',
77
+ location,
78
+ message,
79
+ );
80
+ }
81
+ }
82
+ }
83
+}
84
+
85
+module.exports = checkPropTypes;
src/shared/types/checkReactTypeSpec.js
+23
-85
@@ -11,10 +11,7 @@
11
12
'use strict';
13
14
-var ReactPropTypesSecret = require('ReactPropTypesSecret');
15
-
16
-var invariant = require('invariant');
17
-var warning = require('warning');
14
+var checkPropTypes = require('checkPropTypes');
15
16
var ReactComponentTreeHook;
17
@@ -31,20 +28,6 @@ if (
28
ReactComponentTreeHook = require('ReactComponentTreeHook');
29
}
30
34
-var loggedTypeFailures = {};
35
-
36
-/**
37
- * Assert that the values match with the type specs.
38
- * Error messages are memorized and will only be shown once.
39
- *
40
- * @param {object} typeSpecs Map of name to a ReactPropType
41
- * @param {object} values Runtime values that need to be type-checked
42
- * @param {string} location e.g. "prop", "context", "child context"
43
- * @param {string} componentName Name of the component for error messages.
44
- * @param {?object} element The React element that is being type-checked
45
- * @param {?number} workInProgressOrDebugID The React component instance that is being type-checked
46
- * @private
47
- */
31
function checkReactTypeSpec(
32
typeSpecs,
33
values,
@@ -54,80 +37,35 @@ function checkReactTypeSpec(
37
// It is only safe to pass fiber if it is the work-in-progress version, and
38
// only during reconciliation (begin and complete phase).
39
workInProgressOrDebugID,
57
- // Default behavior is to suppress repeated warnings, but can be overridden
40
warnOnRepeat
41
) {
60
- for (var typeSpecName in typeSpecs) {
61
- if (typeSpecs.hasOwnProperty(typeSpecName)) {
62
- var error;
63
- // Prop type validation may throw. In case they do, we don't want to
64
- // fail the render phase where it didn't fail before. So we log it.
65
- // After these have been cleaned up, we'll let them throw.
66
- try {
67
- // This is intentionally an invariant that gets caught. It's the same
68
- // behavior as without this statement except with a better message.
69
- invariant(
70
- typeof typeSpecs[typeSpecName] === 'function',
71
- '%s: %s type `%s` is invalid; it must be a function, usually from ' +
72
- 'React.PropTypes.',
73
- componentName || 'React class',
74
- location,
75
- typeSpecName
76
- );
77
- error = typeSpecs[typeSpecName](values, typeSpecName, componentName, location, null, ReactPropTypesSecret);
78
- } catch (ex) {
79
- error = ex;
42
+ function formatMessage(message) {
43
+ if (__DEV__) {
44
+ let componentStackInfo = '';
45
+ if (!ReactComponentTreeHook) {
46
+ ReactComponentTreeHook = require('ReactComponentTreeHook');
47
}
81
- warning(
82
- !error || error instanceof Error,
83
- '%s: type specification of %s `%s` is invalid; the type checker ' +
84
- 'function must return `null` or an `Error` but returned a %s. ' +
85
- 'You may have forgotten to pass an argument to the type checker ' +
86
- 'creator (arrayOf, instanceOf, objectOf, oneOf, oneOfType, and ' +
87
- 'shape all require an argument).',
88
- componentName || 'React class',
89
- location,
90
- typeSpecName,
91
- typeof error
92
- );
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;
97
-
98
- var componentStackInfo = '';
99
-
100
- if (__DEV__) {
101
- if (!ReactComponentTreeHook) {
102
- ReactComponentTreeHook = require('ReactComponentTreeHook');
103
- }
104
- if (workInProgressOrDebugID != null) {
105
- if (typeof workInProgressOrDebugID === 'number') {
106
- // DebugID from Stack.
107
- const debugID = workInProgressOrDebugID;
108
- componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
109
- } else if (typeof workInProgressOrDebugID.tag === 'number') {
110
- // This is a Fiber.
111
- // The stack will only be correct if this is a work in progress
112
- // version and we're calling it during reconciliation.
113
- const workInProgress = workInProgressOrDebugID;
114
- componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
115
- }
116
- } else if (element !== null) {
117
- componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
118
- }
48
+ if (workInProgressOrDebugID != null) {
49
+ if (typeof workInProgressOrDebugID === 'number') {
50
+ // DebugID from Stack.
51
+ const debugID = workInProgressOrDebugID;
52
+ componentStackInfo = ReactComponentTreeHook.getStackAddendumByID(debugID);
53
+ } else if (typeof workInProgressOrDebugID.tag === 'number') {
54
+ // This is a Fiber.
55
+ // The stack will only be correct if this is a work in progress
56
+ // version and we're calling it during reconciliation.
57
+ const workInProgress = workInProgressOrDebugID;
58
+ componentStackInfo = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
59
}
120
-
121
- warning(
122
- false,
123
- 'Failed %s type: %s%s',
124
- location,
125
- error.message,
126
- componentStackInfo
127
- );
60
+ } else if (element !== null) {
61
+ componentStackInfo = ReactComponentTreeHook.getCurrentStackAddendum(element);
62
}
63
+ message += componentStackInfo;
64
}
65
+ return message;
66
}
67
+
68
+ checkPropTypes(typeSpecs, values, location, componentName, formatMessage, warnOnRepeat);
69
}
70
71
module.exports = checkReactTypeSpec;