mirror of
https://github.com/facebook/react.git
synced 2026-02-26 02:54:59 +00:00
[fiber] bugfix - don't show <Offscreen> in error message. (#35763)
## Overview While building the RSC sandboxes I notice error messages like: > An error occurred in the `<Offscreen>` component This is an internal component so it should show either: > An error occurred in the `<Suspense>` component. > An error occurred in the `<Activity>` component. It should only happen when there's a lazy in the direct child position of a `<Suspense>` or `<Activity>` component.
This commit is contained in:
@@ -213,6 +213,67 @@ describe('ReactIncrementalErrorLogging', () => {
|
||||
}).toThrow('logCapturedError error');
|
||||
});
|
||||
|
||||
it('does not report internal Offscreen component for errors thrown during reconciliation inside Suspense', async () => {
|
||||
// When a child of Suspense throws during reconciliation (not render),
|
||||
// a Throw fiber is created whose .return is the internal Offscreen fiber.
|
||||
// We should skip Offscreen since it's an internal
|
||||
// implementation detail and walk up to Suspense instead.
|
||||
const lazyChild = React.lazy(() => {
|
||||
throw new Error('lazy init error');
|
||||
});
|
||||
|
||||
await fakeAct(() => {
|
||||
ReactNoop.render(
|
||||
<React.Suspense fallback={<div />}>{lazyChild}</React.Suspense>,
|
||||
);
|
||||
});
|
||||
expect(uncaughtExceptionMock).toHaveBeenCalledTimes(1);
|
||||
expect(uncaughtExceptionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'lazy init error',
|
||||
}),
|
||||
);
|
||||
if (__DEV__) {
|
||||
expect(console.warn).toHaveBeenCalledTimes(1);
|
||||
expect(console.warn.mock.calls[0]).toEqual([
|
||||
'%s\n\n%s\n',
|
||||
'An error occurred in the <Suspense> component.',
|
||||
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
|
||||
'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.',
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('does not report internal Offscreen component for errors thrown during reconciliation inside Activity', async () => {
|
||||
// Same as the Suspense test above — Activity also wraps its children in
|
||||
// an internal Offscreen fiber. The error message should show Activity,
|
||||
// not Offscreen.
|
||||
const lazyChild = React.lazy(() => {
|
||||
throw new Error('lazy init error');
|
||||
});
|
||||
|
||||
await fakeAct(() => {
|
||||
ReactNoop.render(
|
||||
<React.Activity mode="visible">{lazyChild}</React.Activity>,
|
||||
);
|
||||
});
|
||||
expect(uncaughtExceptionMock).toHaveBeenCalledTimes(1);
|
||||
expect(uncaughtExceptionMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
message: 'lazy init error',
|
||||
}),
|
||||
);
|
||||
if (__DEV__) {
|
||||
expect(console.warn).toHaveBeenCalledTimes(1);
|
||||
expect(console.warn.mock.calls[0]).toEqual([
|
||||
'%s\n\n%s\n',
|
||||
'An error occurred in the <Activity> component.',
|
||||
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
|
||||
'Visit https://react.dev/link/error-boundaries to learn more about error boundaries.',
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it('resets instance variables before unmounting failed node', async () => {
|
||||
class ErrorBoundary extends React.Component {
|
||||
state = {error: null};
|
||||
|
||||
@@ -122,7 +122,10 @@ export default function getComponentNameFromFiber(fiber: Fiber): string | null {
|
||||
}
|
||||
return 'Mode';
|
||||
case OffscreenComponent:
|
||||
return 'Offscreen';
|
||||
if (fiber.return !== null) {
|
||||
return getComponentNameFromFiber(fiber.return);
|
||||
}
|
||||
return null;
|
||||
case Profiler:
|
||||
return 'Profiler';
|
||||
case ScopeComponent:
|
||||
|
||||
Reference in New Issue
Block a user