mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
[Fizz] Add Owner Stacks when render is aborted (#32735)
This commit is contained in:
committed by
GitHub
parent
526dd340b3
commit
4a1f29079c
@@ -364,6 +364,7 @@ function render(children: React$Element<any>, options?: Options): Destination {
|
||||
children,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
options ? options.progressiveChunkSize : undefined,
|
||||
options ? options.onError : undefined,
|
||||
options ? options.onAllReady : undefined,
|
||||
|
||||
27
packages/react-server/src/ReactFizzServer.js
vendored
27
packages/react-server/src/ReactFizzServer.js
vendored
@@ -4762,6 +4762,27 @@ function abortTask(task: Task, request: Request, error: mixed): void {
|
||||
}
|
||||
}
|
||||
|
||||
function abortTaskDEV(task: Task, request: Request, error: mixed): void {
|
||||
if (__DEV__) {
|
||||
const prevTaskInDEV = currentTaskInDEV;
|
||||
const prevGetCurrentStackImpl = ReactSharedInternals.getCurrentStack;
|
||||
setCurrentTaskInDEV(task);
|
||||
ReactSharedInternals.getCurrentStack = getCurrentStackInDEV;
|
||||
try {
|
||||
abortTask(task, request, error);
|
||||
} finally {
|
||||
setCurrentTaskInDEV(prevTaskInDEV);
|
||||
ReactSharedInternals.getCurrentStack = prevGetCurrentStackImpl;
|
||||
}
|
||||
} else {
|
||||
// These errors should never make it into a build so we don't need to encode them in codes.json
|
||||
// eslint-disable-next-line react-internal/prod-error-codes
|
||||
throw new Error(
|
||||
'abortTaskDEV should never be called in production mode. This is a bug in React.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function safelyEmitEarlyPreloads(
|
||||
request: Request,
|
||||
shellComplete: boolean,
|
||||
@@ -6111,7 +6132,11 @@ export function abort(request: Request, reason: mixed): void {
|
||||
// This error isn't necessarily fatal in this case but we need to stash it
|
||||
// so we can use it to abort any pending work
|
||||
request.fatalError = error;
|
||||
abortableTasks.forEach(task => abortTask(task, request, error));
|
||||
if (__DEV__) {
|
||||
abortableTasks.forEach(task => abortTaskDEV(task, request, error));
|
||||
} else {
|
||||
abortableTasks.forEach(task => abortTask(task, request, error));
|
||||
}
|
||||
abortableTasks.clear();
|
||||
}
|
||||
if (request.destination !== null) {
|
||||
|
||||
@@ -10,13 +10,28 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
let act;
|
||||
let React;
|
||||
let ReactNoopServer;
|
||||
|
||||
function normalizeCodeLocInfo(str) {
|
||||
return (
|
||||
str &&
|
||||
str.replace(/^ +(?:at|in) ([\S]+)[^\n]*/gm, function (m, name) {
|
||||
const dot = name.lastIndexOf('.');
|
||||
if (dot !== -1) {
|
||||
name = name.slice(dot + 1);
|
||||
}
|
||||
return ' in ' + name + (/\d/.test(m) ? ' (at **)' : '');
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
describe('ReactServer', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
act = require('internal-test-utils').act;
|
||||
React = require('react');
|
||||
ReactNoopServer = require('react-noop-renderer/server');
|
||||
});
|
||||
@@ -32,4 +47,43 @@ describe('ReactServer', () => {
|
||||
const result = ReactNoopServer.render(<div>hello world</div>);
|
||||
expect(result.root).toEqual(div('hello world'));
|
||||
});
|
||||
|
||||
it('has Owner Stacks in DEV when aborted', async () => {
|
||||
function Component({promise}) {
|
||||
React.use(promise);
|
||||
return <div>Hello, Dave!</div>;
|
||||
}
|
||||
function App({promise}) {
|
||||
return <Component promise={promise} />;
|
||||
}
|
||||
|
||||
let caughtError;
|
||||
let componentStack;
|
||||
let ownerStack;
|
||||
const result = ReactNoopServer.render(
|
||||
<App promise={new Promise(() => {})} />,
|
||||
{
|
||||
onError: (error, errorInfo) => {
|
||||
caughtError = error;
|
||||
componentStack = errorInfo.componentStack;
|
||||
ownerStack = __DEV__ ? React.captureOwnerStack() : null;
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
result.abort();
|
||||
});
|
||||
expect(caughtError).toEqual(
|
||||
expect.objectContaining({
|
||||
message: 'The render was aborted by the server without a reason.',
|
||||
}),
|
||||
);
|
||||
expect(normalizeCodeLocInfo(componentStack)).toEqual(
|
||||
'\n in Component (at **)' + '\n in App (at **)',
|
||||
);
|
||||
expect(normalizeCodeLocInfo(ownerStack)).toEqual(
|
||||
__DEV__ ? '\n in App (at **)' : null,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user