Skip to content

Commit ceb9f61

Browse files
committed
Include parent stack but mark owner chain as pertinent
1 parent e403f30 commit ceb9f61

File tree

4 files changed

+50
-15
lines changed

4 files changed

+50
-15
lines changed

src/isomorphic/classic/element/ReactElementValidator.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,11 @@ var ReactElementValidator = {
228228
props.__source !== undefined
229229
? props.__source
230230
: null;
231-
ReactComponentTreeHook.pushNonStandardWarningStack(true, currentSource);
231+
ReactComponentTreeHook.pushNonStandardWarningStack(
232+
true,
233+
true,
234+
currentSource,
235+
);
232236
warning(
233237
false,
234238
'React.createElement: type is invalid -- expected a string (for ' +

src/isomorphic/classic/element/__tests__/ReactElementValidator-test.js

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ describe('ReactElementValidator', () => {
535535
}
536536

537537
function App() {
538-
return React.createElement(Foo);
538+
return React.createElement('div', null, React.createElement(Foo));
539539
}
540540

541541
try {
@@ -557,14 +557,26 @@ describe('ReactElementValidator', () => {
557557
var stack = console.stack.mock.calls[0][0];
558558
expect(Array.isArray(stack)).toBe(true);
559559
expect(stack.map(frame => frame.functionName)).toEqual([
560-
'Foo',
561-
'App',
562-
null,
560+
'Foo', // <Bad> is inside Foo
561+
'App', // <Foo> is inside App
562+
'App', // <div> is inside App
563+
null, // <App> is outside a component
564+
]);
565+
expect(stack.map(frame => frame.isPertinent)).toEqual([
566+
true, // <Bad> caused the error
567+
true, // <Foo> caused <Bad> to render
568+
false, // <div> is not pertinent
569+
true, // <App> caused <Foo> to render
563570
]);
564571
expect(
565572
stack.map(frame => frame.fileName && frame.fileName.slice(-8)),
566-
).toEqual([null, null, null]);
567-
expect(stack.map(frame => frame.lineNumber)).toEqual([null, null, null]);
573+
).toEqual([null, null, null, null]);
574+
expect(stack.map(frame => frame.lineNumber)).toEqual([
575+
null,
576+
null,
577+
null,
578+
null,
579+
]);
568580
} finally {
569581
delete console.stack;
570582
delete console.stackEnd;

src/isomorphic/hooks/ReactComponentTreeHook.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ var ReactComponentTreeHook = {
405405

406406
pushNonStandardWarningStack(
407407
isCreatingElement: boolean,
408+
isOwnerChainPertinent: boolean,
408409
currentSource: ?Source,
409410
) {
410411
if (typeof console.stack !== 'function') {
@@ -414,30 +415,40 @@ var ReactComponentTreeHook = {
414415
var stack = [];
415416
var currentOwner = ReactCurrentOwner.current;
416417
var id = currentOwner && currentOwner._debugID;
418+
var nextIDInOwnerChain = id;
417419

418420
try {
419421
if (isCreatingElement) {
420422
stack.push({
423+
isPertinent: true,
424+
functionName: id ? ReactComponentTreeHook.getDisplayName(id) : null,
421425
fileName: currentSource ? currentSource.fileName : null,
422426
lineNumber: currentSource ? currentSource.lineNumber : null,
423-
functionName: id ? ReactComponentTreeHook.getDisplayName(id) : null,
424427
});
425428
}
426429

427430
while (id) {
428431
var element = ReactComponentTreeHook.getElement(id);
432+
var parentID = ReactComponentTreeHook.getParentID(id);
429433
var ownerID = ReactComponentTreeHook.getOwnerID(id);
430434
var ownerName = ownerID
431435
? ReactComponentTreeHook.getDisplayName(ownerID)
432436
: null;
433437
var source = element && element._source;
438+
// For some warnings, only the owner chain is pertinent
439+
var isPertintent = isOwnerChainPertinent
440+
? nextIDInOwnerChain === id
441+
: true;
434442
stack.push({
435443
fileName: source ? source.fileName : null,
436444
lineNumber: source ? source.lineNumber : null,
437445
functionName: ownerName,
446+
isPertinent: isPertintent,
438447
});
439-
// Owner stack is more useful for visual representation
440-
id = ownerID || ReactComponentTreeHook.getParentID(id);
448+
if (isPertintent && isOwnerChainPertinent) {
449+
nextIDInOwnerChain = ownerID || parentID;
450+
}
451+
id = parentID;
441452
}
442453
} catch (err) {
443454
// Internal state is messed up.

src/isomorphic/modern/element/__tests__/ReactJSXElementValidator-test.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ describe('ReactJSXElementValidator', () => {
410410
}
411411

412412
function App() {
413-
return <Foo />;
413+
return <div><Foo /></div>;
414414
}
415415

416416
try {
@@ -432,17 +432,25 @@ describe('ReactJSXElementValidator', () => {
432432
var stack = console.stack.mock.calls[0][0];
433433
expect(Array.isArray(stack)).toBe(true);
434434
expect(stack.map(frame => frame.functionName)).toEqual([
435-
'Foo',
436-
'App',
437-
null,
435+
'Foo', // <Bad> is inside Foo
436+
'App', // <Foo> is inside App
437+
'App', // <div> is inside App
438+
null, // <App> is outside a component
439+
]);
440+
expect(stack.map(frame => frame.isPertinent)).toEqual([
441+
true, // <Bad> caused the error
442+
true, // <Foo> caused <Bad> to render
443+
false, // <div> is unrelated
444+
true, // <App> caused <Foo> to render
438445
]);
439446
expect(
440447
stack.map(frame => frame.fileName && frame.fileName.slice(-8)),
441-
).toEqual(['-test.js', '-test.js', '-test.js']);
448+
).toEqual(['-test.js', '-test.js', '-test.js', '-test.js']);
442449
expect(stack.map(frame => typeof frame.lineNumber)).toEqual([
443450
'number',
444451
'number',
445452
'number',
453+
'number',
446454
]);
447455
} finally {
448456
delete console.stack;

0 commit comments

Comments
 (0)