Use ReactDebugCurrentFrame.getStackAddendum() in element validator (#13198)
Instead of wrapping ReactDebugCurrentFrame.getStackAddendum() call into a custom wrapper inside ReactElementValidator, "teach" the main ReactDebugCurrentFrame.getStackAddendum() to take currently validating element into account.
Dan Abramov committed
Jul 12, 2018 at 16:18 UTC
1c89cb62fdd686a00351dfabc689571081edbd1a
2 files changed
+51
-36
packages/react/src/ReactDebugCurrentFrame.js
+31
-3
@@ -7,18 +7,46 @@
7
* @flow
8
*/
9
10
+import type {ReactElement} from 'shared/ReactElementType';
11
+
12
+import describeComponentFrame from 'shared/describeComponentFrame';
13
+import getComponentName from 'shared/getComponentName';
14
+
15
const ReactDebugCurrentFrame = {};
16
17
+let currentlyValidatingElement = (null: null | ReactElement);
18
+
19
+export function setCurrentlyValidatingElement(element: null | ReactElement) {
20
+ if (__DEV__) {
21
+ currentlyValidatingElement = element;
22
+ }
23
+}
24
+
25
if (__DEV__) {
13
- // Component that is being worked on
26
+ // Stack implementation injected by the current renderer.
27
ReactDebugCurrentFrame.getCurrentStack = (null: null | (() => string));
28
29
ReactDebugCurrentFrame.getStackAddendum = function(): string {
30
+ let stack = '';
31
+
32
+ // Add an extra top frame while an element is being validated
33
+ if (currentlyValidatingElement) {
34
+ const name = getComponentName(currentlyValidatingElement.type);
35
+ const owner = currentlyValidatingElement._owner;
36
+ stack += describeComponentFrame(
37
+ name,
38
+ currentlyValidatingElement._source,
39
+ owner && getComponentName(owner.type),
40
+ );
41
+ }
42
+
43
+ // Delegate to the injected renderer-specific implementation
44
const impl = ReactDebugCurrentFrame.getCurrentStack;
45
if (impl) {
19
- return impl() || '';
46
+ stack += impl() || '';
47
}
21
- return '';
48
+
49
+ return stack;
50
};
51
}
52
packages/react/src/ReactElementValidator.js
+20
-33
@@ -13,7 +13,6 @@
13
*/
14
15
import lowPriorityWarning from 'shared/lowPriorityWarning';
16
-import describeComponentFrame from 'shared/describeComponentFrame';
16
import isValidElementType from 'shared/isValidElementType';
17
import getComponentName from 'shared/getComponentName';
18
import {
@@ -26,32 +25,14 @@ import warning from 'shared/warning';
25
26
import ReactCurrentOwner from './ReactCurrentOwner';
27
import {isValidElement, createElement, cloneElement} from './ReactElement';
29
-import ReactDebugCurrentFrame from './ReactDebugCurrentFrame';
28
+import ReactDebugCurrentFrame, {
29
+ setCurrentlyValidatingElement,
30
+} from './ReactDebugCurrentFrame';
31
31
-let currentlyValidatingElement;
32
let propTypesMisspellWarningShown;
33
34
-let getStackAddendum = () => {};
35
-
34
if (__DEV__) {
37
- currentlyValidatingElement = null;
38
-
35
propTypesMisspellWarningShown = false;
40
-
41
- getStackAddendum = function(): string {
42
- let stack = '';
43
- if (currentlyValidatingElement) {
44
- const name = getComponentName(currentlyValidatingElement.type);
45
- const owner = currentlyValidatingElement._owner;
46
- stack += describeComponentFrame(
47
- name,
48
- currentlyValidatingElement._source,
49
- owner && getComponentName(owner.type),
50
- );
51
- }
52
- stack += ReactDebugCurrentFrame.getStackAddendum();
53
- return stack;
54
- };
36
}
37
38
function getDeclarationErrorAddendum() {
@@ -138,7 +119,7 @@ function validateExplicitKey(element, parentType) {
119
)}.`;
120
}
121
141
- currentlyValidatingElement = element;
122
+ setCurrentlyValidatingElement(element);
123
if (__DEV__) {
124
warning(
125
false,
@@ -146,10 +127,10 @@ function validateExplicitKey(element, parentType) {
127
'%s%s See https://fb.me/react-warning-keys for more information.%s',
128
currentComponentErrorInfo,
129
childOwner,
149
- getStackAddendum(),
130
+ ReactDebugCurrentFrame.getStackAddendum(),
131
);
132
}
152
- currentlyValidatingElement = null;
133
+ setCurrentlyValidatingElement(null);
134
}
135
136
/**
@@ -221,9 +202,15 @@ function validatePropTypes(element) {
202
return;
203
}
204
if (propTypes) {
224
- currentlyValidatingElement = element;
225
- checkPropTypes(propTypes, element.props, 'prop', name, getStackAddendum);
226
- currentlyValidatingElement = null;
205
+ setCurrentlyValidatingElement(element);
206
+ checkPropTypes(
207
+ propTypes,
208
+ element.props,
209
+ 'prop',
210
+ name,
211
+ ReactDebugCurrentFrame.getStackAddendum,
212
+ );
213
+ setCurrentlyValidatingElement(null);
214
} else if (type.PropTypes !== undefined && !propTypesMisspellWarningShown) {
215
propTypesMisspellWarningShown = true;
216
warning(
@@ -246,7 +233,7 @@ function validatePropTypes(element) {
233
* @param {ReactElement} fragment
234
*/
235
function validateFragmentProps(fragment) {
249
- currentlyValidatingElement = fragment;
236
+ setCurrentlyValidatingElement(fragment);
237
238
const keys = Object.keys(fragment.props);
239
for (let i = 0; i < keys.length; i++) {
@@ -257,7 +244,7 @@ function validateFragmentProps(fragment) {
244
'Invalid prop `%s` supplied to `React.Fragment`. ' +
245
'React.Fragment can only have `key` and `children` props.%s',
246
key,
260
- getStackAddendum(),
247
+ ReactDebugCurrentFrame.getStackAddendum(),
248
);
249
break;
250
}
@@ -267,11 +254,11 @@ function validateFragmentProps(fragment) {
254
warning(
255
false,
256
'Invalid attribute `ref` supplied to `React.Fragment`.%s',
270
- getStackAddendum(),
257
+ ReactDebugCurrentFrame.getStackAddendum(),
258
);
259
}
260
274
- currentlyValidatingElement = null;
261
+ setCurrentlyValidatingElement(null);
262
}
263
264
export function createElementWithValidation(type, props, children) {
@@ -299,7 +286,7 @@ export function createElementWithValidation(type, props, children) {
286
info += getDeclarationErrorAddendum();
287
}
288
302
- info += getStackAddendum();
289
+ info += ReactDebugCurrentFrame.getStackAddendum();
290
291
let typeString;
292
if (type === null) {