Only prepare extra stack frames if needed (#19014)
We currently prepare an extra stack frame before they're needed. Particularly for propTypes. This causes problems as they can have side-effects with the new component stacks and it's slow. This moves it to be lazy.
Sebastian Markbåge committed
May 27, 2020 at 08:43 UTC
55cb0b7eeb0e539d89858b8ed69beabf7fe2fb46
3 files changed
+37
-14
packages/react/src/ReactElementValidator.js
+5
-7
@@ -227,9 +227,7 @@ function validatePropTypes(element) {
227
return;
228
}
229
if (propTypes) {
230
- setCurrentlyValidatingElement(element);
231
- checkPropTypes(propTypes, element.props, 'prop', name);
232
- setCurrentlyValidatingElement(null);
230
+ checkPropTypes(propTypes, element.props, 'prop', name, element);
231
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
232
propTypesMisspellWarningShown = true;
233
console.error(
@@ -255,26 +253,26 @@ function validatePropTypes(element) {
253
*/
254
function validateFragmentProps(fragment) {
255
if (__DEV__) {
258
- setCurrentlyValidatingElement(fragment);
259
-
256
const keys = Object.keys(fragment.props);
257
for (let i = 0; i < keys.length; i++) {
258
const key = keys[i];
259
if (key !== 'children' && key !== 'key') {
260
+ setCurrentlyValidatingElement(fragment);
261
console.error(
262
'Invalid prop `%s` supplied to `React.Fragment`. ' +
263
'React.Fragment can only have `key` and `children` props.',
264
key,
265
);
266
+ setCurrentlyValidatingElement(null);
267
break;
268
}
269
}
270
271
if (fragment.ref !== null) {
272
+ setCurrentlyValidatingElement(fragment);
273
console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
274
+ setCurrentlyValidatingElement(null);
275
}
276
-
277
- setCurrentlyValidatingElement(null);
276
}
277
}
278
packages/react/src/jsx/ReactJSXElementValidator.js
+5
-7
@@ -243,9 +243,7 @@ function validatePropTypes(element) {
243
return;
244
}
245
if (propTypes) {
246
- setCurrentlyValidatingElement(element);
247
- checkPropTypes(propTypes, element.props, 'prop', name);
248
- setCurrentlyValidatingElement(null);
246
+ checkPropTypes(propTypes, element.props, 'prop', name, element);
247
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
248
propTypesMisspellWarningShown = true;
249
console.error(
@@ -271,26 +269,26 @@ function validatePropTypes(element) {
269
*/
270
function validateFragmentProps(fragment) {
271
if (__DEV__) {
274
- setCurrentlyValidatingElement(fragment);
275
-
272
const keys = Object.keys(fragment.props);
273
for (let i = 0; i < keys.length; i++) {
274
const key = keys[i];
275
if (key !== 'children' && key !== 'key') {
276
+ setCurrentlyValidatingElement(fragment);
277
console.error(
278
'Invalid prop `%s` supplied to `React.Fragment`. ' +
279
'React.Fragment can only have `key` and `children` props.',
280
key,
281
);
282
+ setCurrentlyValidatingElement(null);
283
break;
284
}
285
}
286
287
if (fragment.ref !== null) {
288
+ setCurrentlyValidatingElement(fragment);
289
console.error('Invalid attribute `ref` supplied to `React.Fragment`.');
290
+ setCurrentlyValidatingElement(null);
291
}
292
-
293
- setCurrentlyValidatingElement(null);
292
}
293
}
294
packages/shared/checkPropTypes.js
+27
@@ -9,11 +9,34 @@
9
10
const loggedTypeFailures = {};
11
12
+import {describeUnknownElementTypeFrameInDEV} from 'shared/ReactComponentStackFrame';
13
+
14
+import ReactSharedInternals from 'shared/ReactSharedInternals';
15
+
16
+const ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame;
17
+
18
+function setCurrentlyValidatingElement(element) {
19
+ if (__DEV__) {
20
+ if (element) {
21
+ const owner = element._owner;
22
+ const stack = describeUnknownElementTypeFrameInDEV(
23
+ element.type,
24
+ element._source,
25
+ owner ? owner.type : null,
26
+ );
27
+ ReactDebugCurrentFrame.setExtraStackFrame(stack);
28
+ } else {
29
+ ReactDebugCurrentFrame.setExtraStackFrame(null);
30
+ }
31
+ }
32
+}
33
+
34
export default function checkPropTypes(
35
typeSpecs: Object,
36
values: Object,
37
location: string,
38
componentName: ?string,
39
+ element?: any,
40
): void {
41
if (__DEV__) {
42
// $FlowFixMe This is okay but Flow doesn't know it.
@@ -55,6 +78,7 @@ export default function checkPropTypes(
78
error = ex;
79
}
80
if (error && !(error instanceof Error)) {
81
+ setCurrentlyValidatingElement(element);
82
console.error(
83
'%s: type specification of %s' +
84
' `%s` is invalid; the type checker ' +
@@ -67,12 +91,15 @@ export default function checkPropTypes(
91
typeSpecName,
92
typeof error,
93
);
94
+ setCurrentlyValidatingElement(null);
95
}
96
if (error instanceof Error && !(error.message in loggedTypeFailures)) {
97
// Only monitor this failure once because there tends to be a lot of the
98
// same error.
99
loggedTypeFailures[error.message] = true;
100
+ setCurrentlyValidatingElement(element);
101
console.error('Failed %s type: %s', location, error.message);
102
+ setCurrentlyValidatingElement(null);
103
}
104
}
105
}