Skip to content

Commit 609cb19

Browse files
committed
Don't replay consoles written inside onError/onPostpone.
These aren't conceptually part of the request's render so we exit the request context for those.
1 parent 2fe5fb0 commit 609cb19

21 files changed

+91
-30
lines changed

packages/react-client/src/__tests__/ReactFlight-test.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1995,4 +1995,45 @@ describe('ReactFlight', () => {
19951995
</div>,
19961996
);
19971997
});
1998+
1999+
// @gate enableServerComponentLogs && __DEV__
2000+
it('replays logs, but not onError logs', async () => {
2001+
function foo() {
2002+
return 'hello';
2003+
}
2004+
function ServerComponent() {
2005+
console.log('hi', {prop: 123, fn: foo});
2006+
throw new Error('err');
2007+
}
2008+
2009+
let transport;
2010+
expect(() => {
2011+
// Reset the modules so that we get a new overridden console on top of the
2012+
// one installed by expect. This ensures that we still emit console.error
2013+
// calls.
2014+
jest.resetModules();
2015+
jest.mock('react', () => require('react/react.react-server'));
2016+
ReactServer = require('react');
2017+
ReactNoopFlightServer = require('react-noop-renderer/flight-server');
2018+
transport = ReactNoopFlightServer.render({root: <ServerComponent />});
2019+
}).toErrorDev('err');
2020+
2021+
const log = console.log;
2022+
try {
2023+
console.log = jest.fn();
2024+
// The error should not actually get logged because we're not awaiting the root
2025+
// so it's not thrown but the server log also shouldn't be replayed.
2026+
await ReactNoopFlightClient.read(transport);
2027+
2028+
expect(console.log).toHaveBeenCalledTimes(1);
2029+
expect(console.log.mock.calls[0][0]).toBe('hi');
2030+
expect(console.log.mock.calls[0][1].prop).toBe(123);
2031+
const loggedFn = console.log.mock.calls[0][1].fn;
2032+
expect(typeof loggedFn).toBe('function');
2033+
expect(loggedFn).not.toBe(foo);
2034+
expect(loggedFn.toString()).toBe(foo.toString());
2035+
} finally {
2036+
console.log = log;
2037+
}
2038+
});
19982039
});

packages/react-server/src/ReactFlightServer.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,13 +1721,36 @@ function renderModelDestructive(
17211721
}
17221722

17231723
function logPostpone(request: Request, reason: string): void {
1724-
const onPostpone = request.onPostpone;
1725-
onPostpone(reason);
1724+
const prevRequest = currentRequest;
1725+
currentRequest = null;
1726+
try {
1727+
const onPostpone = request.onPostpone;
1728+
if (supportsRequestStorage) {
1729+
// Exit the request context while running callbacks.
1730+
requestStorage.run(undefined, onPostpone, reason);
1731+
} else {
1732+
onPostpone(reason);
1733+
}
1734+
} finally {
1735+
currentRequest = prevRequest;
1736+
}
17261737
}
17271738

17281739
function logRecoverableError(request: Request, error: mixed): string {
1729-
const onError = request.onError;
1730-
const errorDigest = onError(error);
1740+
const prevRequest = currentRequest;
1741+
currentRequest = null;
1742+
let errorDigest;
1743+
try {
1744+
const onError = request.onError;
1745+
if (supportsRequestStorage) {
1746+
// Exit the request context while running callbacks.
1747+
errorDigest = requestStorage.run(undefined, onError, error);
1748+
} else {
1749+
errorDigest = onError(error);
1750+
}
1751+
} finally {
1752+
currentRequest = prevRequest;
1753+
}
17311754
if (errorDigest != null && typeof errorDigest !== 'string') {
17321755
// eslint-disable-next-line react-internal/prod-error-codes
17331756
throw new Error(

packages/react-server/src/ReactServerStreamConfigFB.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export opaque type BinaryChunk = string;
2121
export function flushBuffered(destination: Destination) {}
2222

2323
export const supportsRequestStorage = false;
24-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
24+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
2525

2626
export function beginWriting(destination: Destination) {}
2727

packages/react-server/src/forks/ReactFizzConfig.custom.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export type {TransitionStatus};
3838
export const isPrimaryRenderer = false;
3939

4040
export const supportsRequestStorage = false;
41-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
41+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
4242

4343
export const resetResumableState = $$$config.resetResumableState;
4444
export const completeResumableState = $$$config.completeResumableState;

packages/react-server/src/forks/ReactFizzConfig.dom-edge.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,5 @@ export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1212

1313
// For now, we get this from the global scope, but this will likely move to a module.
1414
export const supportsRequestStorage = typeof AsyncLocalStorage === 'function';
15-
export const requestStorage: AsyncLocalStorage<Request> = supportsRequestStorage
16-
? new AsyncLocalStorage()
17-
: (null: any);
15+
export const requestStorage: AsyncLocalStorage<Request | void> =
16+
supportsRequestStorage ? new AsyncLocalStorage() : (null: any);

packages/react-server/src/forks/ReactFizzConfig.dom-legacy.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1111
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOMLegacy';
1212

1313
export const supportsRequestStorage = false;
14-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
14+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);

packages/react-server/src/forks/ReactFizzConfig.dom-node.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1414
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1515

1616
export const supportsRequestStorage = true;
17-
export const requestStorage: AsyncLocalStorage<Request> =
17+
export const requestStorage: AsyncLocalStorage<Request | void> =
1818
new AsyncLocalStorage();

packages/react-server/src/forks/ReactFizzConfig.dom.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ import type {Request} from 'react-server/src/ReactFizzServer';
1111
export * from 'react-dom-bindings/src/server/ReactFizzConfigDOM';
1212

1313
export const supportsRequestStorage = false;
14-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
14+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);

packages/react-server/src/forks/ReactFlightServerConfig.custom.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const isPrimaryRenderer = false;
2323
export const prepareHostDispatcher = () => {};
2424

2525
export const supportsRequestStorage = false;
26-
export const requestStorage: AsyncLocalStorage<Request> = (null: any);
26+
export const requestStorage: AsyncLocalStorage<Request | void> = (null: any);
2727

2828
export function createHints(): any {
2929
return null;

packages/react-server/src/forks/ReactFlightServerConfig.dom-browser-esm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export * from 'react-server-dom-esm/src/ReactFlightServerConfigESMBundler';
1414
export * from 'react-dom-bindings/src/server/ReactFlightServerConfigDOM';
1515

1616
export const supportsRequestStorage = true;
17-
export const requestStorage: AsyncLocalStorage<Request> =
17+
export const requestStorage: AsyncLocalStorage<Request | void> =
1818
new AsyncLocalStorage();
1919

2020
export * from '../ReactFlightServerConfigDebugNoop';

0 commit comments

Comments
 (0)