@samitouri / QOS-React-2 / commits / 55cb0b7eeb

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 }