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;