In SSR, Store Elements of Composites on the Stack for Warnings (#10187)
In DEV mode this keeps an additional sub-stack within the stack of the server renderer. This substack keeps track of all the elements that we resolved to get to to the next processing frame. This lets us recreate the full composite component stack for warnings. Normally the stack only contains host components. We reset this every time we're going to resolve another sibling.
Sebastian Markbåge committed
Jul 19, 2017 at 10:36 UTC
1b4776f31974ff6ad256380deacb4e2f69703750
1 file changed
+39
-24
src/renderers/shared/server/ReactPartialRenderer.js
+39
-24
@@ -47,17 +47,7 @@ if (__DEV__) {
47
};
48
49
var describeComponentFrame = require('describeComponentFrame');
50
- var describeStackFrame = function(
51
- frame: {
52
- tag?: string,
53
- children: Array<*>,
54
- childIndex: number,
55
- },
56
- ): string {
57
- var element = frame.children[frame.childIndex - 1];
58
- if (!element) {
59
- return '';
60
- }
50
+ var describeStackFrame = function(element): string {
51
var source = element._source;
52
var type = element.type;
53
var name = typeof type === 'string'
@@ -69,11 +59,21 @@ if (__DEV__) {
59
60
var {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
61
var currentDebugStack = null;
62
+ var currentDebugElementStack = null;
63
var setCurrentDebugStack = function(stack) {
64
+ currentDebugElementStack = stack[stack.length - 1].debugElementStack;
65
+ // We are about to enter a new composite stack, reset the array.
66
+ currentDebugElementStack.length = 0;
67
currentDebugStack = stack;
68
ReactDebugCurrentFrame.getCurrentStack = getStackAddendum;
69
};
70
+ var pushElementToDebugStack = function(element) {
71
+ if (currentDebugElementStack !== null) {
72
+ currentDebugElementStack.push(element);
73
+ }
74
+ };
75
var resetCurrentDebugStack = function() {
76
+ currentDebugElementStack = null;
77
currentDebugStack = null;
78
ReactDebugCurrentFrame.getCurrentStack = null;
79
};
@@ -84,7 +84,10 @@ if (__DEV__) {
84
let stack = '';
85
let debugStack = currentDebugStack;
86
for (let i = debugStack.length - 1; i >= 0; i--) {
87
- stack += describeStackFrame(debugStack[i]);
87
+ let debugElementStack = debugStack[i].debugElementStack;
88
+ for (let ii = debugElementStack.length - 1; ii >= 0; ii--) {
89
+ stack += describeStackFrame(debugElementStack[ii]);
90
+ }
91
}
92
return stack;
93
};
@@ -306,10 +309,15 @@ function createOpenTagMarkup(
309
function resolve(child, context) {
310
// TODO: We'll need to support Arrays (and strings) after Fiber is rolled out
311
invariant(!Array.isArray(child), 'Did not expect to receive an Array child');
309
- while (React.isValidElement(child) && typeof child.type === 'function') {
312
+ while (React.isValidElement(child)) {
313
+ if (__DEV__) {
314
+ pushElementToDebugStack(child);
315
+ }
316
var Component = child.type;
317
+ if (typeof Component !== 'function') {
318
+ break;
319
+ }
320
var publicContext = processContext(Component, context);
312
-
321
var inst;
322
var queue = [];
323
var replace = false;
@@ -407,14 +415,16 @@ function resolve(child, context) {
415
416
class ReactDOMServerRenderer {
417
constructor(element, makeStaticMarkup) {
410
- this.stack = [
411
- {
412
- children: [element],
413
- childIndex: 0,
414
- context: emptyObject,
415
- footer: '',
416
- },
417
- ];
418
+ var topFrame = {
419
+ children: [element],
420
+ childIndex: 0,
421
+ context: emptyObject,
422
+ footer: '',
423
+ };
424
+ if (__DEV__) {
425
+ topFrame.debugElementStack = [];
426
+ }
427
+ this.stack = [topFrame];
428
this.exhausted = false;
429
this.currentSelectValue = null;
430
this.previousWasTextNode = false;
@@ -448,6 +458,7 @@ class ReactDOMServerRenderer {
458
}
459
out += this.render(child, frame.context);
460
if (__DEV__) {
461
+ // TODO: Handle reentrant server render calls. This doesn't.
462
resetCurrentDebugStack();
463
}
464
}
@@ -747,13 +758,17 @@ class ReactDOMServerRenderer {
758
} else {
759
children = toArray(props.children);
760
}
750
- this.stack.push({
761
+ var frame = {
762
tag,
763
children,
764
childIndex: 0,
765
context: context,
766
footer: footer,
756
- });
767
+ };
768
+ if (__DEV__) {
769
+ frame.debugElementStack = [];
770
+ }
771
+ this.stack.push(frame);
772
return out;
773
}
774
}