@samitouri / QOS-React / commits / e6d6ad3e88

Remove closure in checkReactTypeSpec by tracking stack info globally

There's a lot of overlap between ReactCurrentOwner, ReactDebugCurrentFrame, and ReactDebugCurrentFiber that should be consolidated.

Andrew Clark committed Feb 21, 2017 at 13:53 UTC e6d6ad3e88b0728b917969091dede0ff14bf5e43
7 files changed +100 -56
src/isomorphic/classic/element/ReactDebugCurrentFrame.js new
+58
@@ -0,0 +1,58 @@
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 ReactDebugCurrentFrame
10 + * @flow
11 + */
12 +
13 +'use strict';
14 +
15 +import type { Fiber } from 'ReactFiber';
16 +import type { DebugID } from 'ReactInstanceType';
17 +
18 +if (__DEV__) {
19 + var {
20 + getStackAddendumByID,
21 + getStackAddendumByWorkInProgressFiber,
22 + getCurrentStackAddendum,
23 + } = require('ReactComponentTreeHook');
24 +}
25 +
26 +const ReactDebugCurrentFrame = {
27 + // Component that is being worked on
28 + current: (null : Fiber | DebugID | null),
29 +
30 + // Element that is being cloned or created
31 + element: (null : *),
32 +
33 + getStackAddendum() : string | null {
34 + let stack = null;
35 + if (__DEV__) {
36 + const current = ReactDebugCurrentFrame.current;
37 + const element = ReactDebugCurrentFrame.element;
38 + if (current !== null) {
39 + if (typeof current === 'number') {
40 + // DebugID from Stack.
41 + const debugID = current;
42 + stack = getStackAddendumByID(debugID);
43 + } else if (typeof current.tag === 'number') {
44 + // This is a Fiber.
45 + // The stack will only be correct if this is a work in progress
46 + // version and we're calling it during reconciliation.
47 + const workInProgress = current;
48 + stack = getStackAddendumByWorkInProgressFiber(workInProgress);
49 + }
50 + } else if (element !== null) {
51 + stack = getCurrentStackAddendum(element);
52 + }
53 + }
54 + return stack;
55 + },
56 +};
57 +
58 +module.exports = ReactDebugCurrentFrame;
src/isomorphic/classic/element/ReactElementValidator.js
+20 -4
@@ -27,7 +27,11 @@ var checkReactTypeSpec = require('checkReactTypeSpec');
27 var canDefineProperty = require('canDefineProperty');
28 var getComponentName = require('getComponentName');
29 var getIteratorFn = require('getIteratorFn');
30 -var warning = require('warning');
30 +
31 +if (__DEV__) {
32 + var warning = require('warning');
33 + var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
34 +}
35
36 function getDeclarationErrorAddendum() {
37 if (ReactCurrentOwner.current) {
@@ -181,9 +185,7 @@ function validatePropTypes(element) {
185 componentClass.propTypes,
186 element.props,
187 'prop',
184 - name,
185 - element,
186 - null
188 + name
189 );
190 }
191 if (typeof componentClass.getDefaultProps === 'function') {
@@ -248,6 +250,10 @@ var ReactElementValidator = {
250 return element;
251 }
252
253 + if (__DEV__) {
254 + ReactDebugCurrentFrame.element = element;
255 + }
256 +
257 // Skip key warning if the type isn't valid since our key validation logic
258 // doesn't expect a non-string/function type and can throw confusing errors.
259 // We don't want exception behavior to differ between dev and prod.
@@ -261,6 +267,10 @@ var ReactElementValidator = {
267
268 validatePropTypes(element);
269
270 + if (__DEV__) {
271 + ReactDebugCurrentFrame.element = null;
272 + }
273 +
274 return element;
275 },
276
@@ -301,10 +311,16 @@ var ReactElementValidator = {
311
312 cloneElement: function(element, props, children) {
313 var newElement = ReactElement.cloneElement.apply(this, arguments);
314 + if (__DEV__) {
315 + ReactDebugCurrentFrame.element = newElement;
316 + }
317 for (var i = 2; i < arguments.length; i++) {
318 validateChildKeys(arguments[i], newElement.type);
319 }
320 validatePropTypes(newElement);
321 + if (__DEV__) {
322 + ReactDebugCurrentFrame.element = null;
323 + }
324 return newElement;
325 },
326
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+1 -1
@@ -32,7 +32,7 @@ function getPropTypeWarningMessage(propTypes, object, componentName) {
32 console.error.calls.reset();
33 }
34 resetWarningCache();
35 - checkReactTypeSpec(propTypes, object, 'prop', 'testComponent', null, null);
35 + checkReactTypeSpec(propTypes, object, 'prop', 'testComponent');
36 const callCount = console.error.calls.count();
37 if (callCount > 1) {
38 throw new Error('Too many warnings.');
src/isomorphic/classic/types/checkPropTypes.js
+1 -1
@@ -75,7 +75,7 @@ function checkPropTypes(typeSpecs, values, location, componentName, getStack) {
75 'Failed %s type: %s%s',
76 location,
77 error.message,
78 - stack,
78 + stack != null ? stack : '',
79 );
80 }
81 }
src/renderers/shared/fiber/ReactFiberContext.js
+7 -2
@@ -34,6 +34,7 @@ const {
34
35 if (__DEV__) {
36 var checkReactTypeSpec = require('checkReactTypeSpec');
37 + var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
38 var warnedAboutMissingGetChildContext = {};
39 }
40
@@ -91,7 +92,9 @@ exports.getMaskedContext = function(workInProgress : Fiber, unmaskedContext : Ob
92
93 if (__DEV__) {
94 const name = getComponentName(workInProgress);
94 - checkReactTypeSpec(contextTypes, context, 'context', name, null, workInProgress);
95 + ReactDebugCurrentFrame.current = workInProgress;
96 + checkReactTypeSpec(contextTypes, context, 'context', name);
97 + ReactDebugCurrentFrame.current = null;
98 }
99
100 // Cache unmasked context so we can avoid recreating masked context unless necessary.
@@ -182,7 +185,9 @@ function processChildContext(fiber : Fiber, parentContext : Object, isReconcilin
185 // assume anything about the given fiber. We won't pass it down if we aren't sure.
186 // TODO: remove this hack when we delete unstable_renderSubtree in Fiber.
187 const workInProgress = isReconciling ? fiber : null;
185 - checkReactTypeSpec(childContextTypes, childContext, 'child context', name, null, workInProgress);
188 + ReactDebugCurrentFrame.current = workInProgress;
189 + checkReactTypeSpec(childContextTypes, childContext, 'child context', name);
190 + ReactDebugCurrentFrame.current = null;
191 }
192 return {...parentContext, ...childContext};
193 }
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+4 -3
@@ -24,6 +24,7 @@ var ReactReconciler = require('ReactReconciler');
24
25 if (__DEV__) {
26 var checkReactTypeSpec = require('checkReactTypeSpec');
27 + var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
28 var warningAboutMissingGetChildContext = {};
29 }
30
@@ -731,14 +732,14 @@ var ReactCompositeComponent = {
732 location: string,
733 ) {
734 if (__DEV__) {
735 + ReactDebugCurrentFrame.current = this._debugID;
736 checkReactTypeSpec(
737 typeSpecs,
738 values,
739 location,
738 - this.getName(),
739 - null,
740 - this._debugID
740 + this.getName()
741 );
742 + ReactDebugCurrentFrame.current = null;
743 }
744 },
745
src/shared/types/checkReactTypeSpec.js
+9 -45
@@ -13,57 +13,21 @@
13
14 var checkPropTypes = require('checkPropTypes');
15
16 -var ReactComponentTreeHook;
17 -
18 -if (
19 - typeof process !== 'undefined' &&
20 - process.env &&
21 - process.env.NODE_ENV === 'test'
22 -) {
23 - // Temporary hack.
24 - // Inline requires don't work well with Jest:
25 - // https://github.com/facebook/react/issues/7240
26 - // Remove the inline requires when we don't need them anymore:
27 - // https://github.com/facebook/react/pull/7178
28 - ReactComponentTreeHook = require('ReactComponentTreeHook');
29 -}
16 +var { getStackAddendum } = require('ReactDebugCurrentFrame');
17
18 function checkReactTypeSpec(
19 typeSpecs,
20 values,
21 location: string,
35 - componentName,
36 - element,
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
22 + componentName
23 ) {
41 - function getStack() {
42 - let stack = '';
43 - if (__DEV__) {
44 - if (!ReactComponentTreeHook) {
45 - ReactComponentTreeHook = require('ReactComponentTreeHook');
46 - }
47 - if (workInProgressOrDebugID != null) {
48 - if (typeof workInProgressOrDebugID === 'number') {
49 - // DebugID from Stack.
50 - const debugID = workInProgressOrDebugID;
51 - stack = ReactComponentTreeHook.getStackAddendumByID(debugID);
52 - } else if (typeof workInProgressOrDebugID.tag === 'number') {
53 - // This is a Fiber.
54 - // The stack will only be correct if this is a work in progress
55 - // version and we're calling it during reconciliation.
56 - const workInProgress = workInProgressOrDebugID;
57 - stack = ReactComponentTreeHook.getStackAddendumByWorkInProgressFiber(workInProgress);
58 - }
59 - } else if (element !== null) {
60 - stack = ReactComponentTreeHook.getCurrentStackAddendum(element);
61 - }
62 - }
63 - return stack;
64 - }
65 -
66 - checkPropTypes(typeSpecs, values, location, componentName, getStack);
24 + checkPropTypes(
25 + typeSpecs,
26 + values,
27 + location,
28 + componentName,
29 + getStackAddendum
30 + );
31 }
32
33 module.exports = checkReactTypeSpec;