Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Add React ErrorBoundary component + theme default boundaries #3104

Merged
merged 26 commits into from
Nov 4, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
02924c2
refactor: add configurable error boundary around App component contents.
digital-jungle Jul 23, 2020
ce5c310
fix state types and defaults for ErrorBoundary.
digital-jungle Jul 23, 2020
a125c60
App level error boundary now only shows errors when canUseDOM is enab…
digital-jungle Jul 24, 2020
d788e60
Wrapped Layout children with new global error boundary with fallback …
digital-jungle Jul 24, 2020
c040d06
Merge branch 'main'
Josh-Cena Oct 29, 2021
276280c
Fix typings & duplicated files
Josh-Cena Oct 29, 2021
60064c0
Remove tryAgain from props
Josh-Cena Oct 29, 2021
e890f19
Allow custom fallback
Josh-Cena Oct 29, 2021
411328f
Fixes
Josh-Cena Oct 29, 2021
76a7d56
Fix
Josh-Cena Oct 29, 2021
e08bb24
Don't render Layout within Layout
Josh-Cena Oct 29, 2021
dcb7880
Add translations
Josh-Cena Oct 29, 2021
065e74d
Fix layout
Josh-Cena Oct 29, 2021
9756389
Docs writeup
Josh-Cena Oct 29, 2021
69ec300
Add note
Josh-Cena Oct 29, 2021
3625b81
Make fallback required
Josh-Cena Nov 4, 2021
fb0b02a
Fail-safe
Josh-Cena Nov 4, 2021
9d2900b
Merge branch 'main'
slorber Nov 4, 2021
35bf1cb
error boundary fallback should be optional and provide a good default
slorber Nov 4, 2021
638d8a4
Rework a bit error boundary doc
slorber Nov 4, 2021
c6cde7e
Add error boundary test page /tests/pages/error-boundary-tests
slorber Nov 4, 2021
be5bff9
minor error display fixes for errors above layout
slorber Nov 4, 2021
2fa4a1c
improve ErrorBoundaryTests page
slorber Nov 4, 2021
2870084
wrap Error in its own ErrorBoundary to capture layout errors too as a…
slorber Nov 4, 2021
7daae18
extra error boundary should recover the original screen, not the cras…
slorber Nov 4, 2021
de5c9b3
remove inline style
slorber Nov 4, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import React, {ReactNode} from 'react';
import Head from '@docusaurus/Head';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import useBaseUrl from '@docusaurus/useBaseUrl';
import DocusaurusErrorBoundary from '@docusaurus/DocusaurusErrorBoundary';

import ThemeProvider from '@theme/ThemeProvider';
import UserPreferencesProvider from '@theme/UserPreferencesProvider';
Expand Down Expand Up @@ -88,7 +89,9 @@ function Layout(props: Props): JSX.Element {
</Head>
<AnnouncementBar />
<Navbar />
<div className="main-wrapper">{children}</div>
<div className="main-wrapper">
<DocusaurusErrorBoundary>{children}</DocusaurusErrorBoundary>
</div>
{!noFooter && <Footer />}
</Providers>
);
Expand Down
18 changes: 12 additions & 6 deletions packages/docusaurus/src/client/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import globalData from '@generated/globalData';
import siteMetadata from '@generated/site-metadata';
import renderRoutes from './exports/renderRoutes';
import DocusaurusContext from './exports/context';
import ErrorBoundary from './ErrorBoundary';
import PendingNavigation from './PendingNavigation';

import './client-lifecycles-dispatcher';
Expand All @@ -24,13 +25,18 @@ function App(): JSX.Element {
setIsClient(true);
}, []);

const logError = process.env.NODE_ENV !== 'production';
const showError = process.env.NODE_ENV !== 'production';

return (
<DocusaurusContext.Provider
value={{siteConfig, siteMetadata, globalData, isClient}}>
<PendingNavigation routes={routes}>
{renderRoutes(routes)}
</PendingNavigation>
</DocusaurusContext.Provider>
<ErrorBoundary logError={logError} showError={showError}>
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
<DocusaurusContext.Provider
value={{siteConfig, siteMetadata, globalData, isClient}}>
<PendingNavigation routes={routes}>
{renderRoutes(routes)}
</PendingNavigation>
</DocusaurusContext.Provider>
</ErrorBoundary>
);
}

Expand Down
85 changes: 85 additions & 0 deletions packages/docusaurus/src/client/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import {withRouter} from 'react-router-dom';
import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment';
import siteConfig from '@generated/docusaurus.config';

interface Props {
logError: boolean;
showError: boolean;
}

interface State {
error: Error | null;
errorInfo: React.ErrorInfo | null;
}

class ErrorBoundary extends React.Component<Props, State> {
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
constructor(props: Props) {
super(props);

this.state = {
error: null,
errorInfo: null,
};
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
const {logError = false} = this.props;
const {
errorBoundary: {
consoleHeading = 'An error occurred, here are the details.',
} = {},
} = siteConfig;

// Catch errors in any components below and re-render with error message
if (ExecutionEnvironment.canUseDOM) {
this.setState({
error,
errorInfo,
});
}

// Log our errors to the console as well
if (logError) {
console.error(consoleHeading, error, errorInfo);
}
}

render() {
const {children, showError = false} = this.props;
const {error, errorInfo} = this.state;
const {
errorBoundary: {
heading = 'An error occurred, please contact the development team.',
} = {},
} = siteConfig;

if (errorInfo) {
// Let's output our error
return (
<div>
<h2>{heading}</h2>
{showError && (
<details style={{whiteSpace: 'pre-wrap'}}>
{error && error.toString()}
<br />
{errorInfo.componentStack}
</details>
)}
</div>
);
}

// Normally, just render children
return children;
}
}

export default withRouter(ErrorBoundary);
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
64 changes: 64 additions & 0 deletions packages/docusaurus/src/client/exports/DocusaurusErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {ReactNode} from 'react';

import ExecutionEnvironment from './ExecutionEnvironment';
import Error from '@theme/Error';

interface Props {
renderError?: any;
tryAgain?: any;
children: ReactNode;
}

interface State {
error: Error | null;
errorInfo: React.ErrorInfo | null;
}

class DocusaurusErrorBoundary extends React.Component<Props, State> {
constructor(props: Props) {
super(props);

this.state = {
error: null,
errorInfo: null,
};
}

componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
// Catch errors in any components below and re-render with error message
if (ExecutionEnvironment.canUseDOM) {
this.setState({
error,
errorInfo,
});
}
}

render() {
const {children, renderError, tryAgain} = this.props;
const {error, errorInfo} = this.state;

if (errorInfo) {
// Let's output our error
if (!renderError) {
return (
<Error error={error} errorInfo={errorInfo} tryAgain={tryAgain} />
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
);
}

return renderError(error, errorInfo);
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
}

// Normally, just render children
return children;
}
}

export default DocusaurusErrorBoundary;
24 changes: 24 additions & 0 deletions packages/docusaurus/src/client/theme-fallback/Error/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';

function Error({error, tryAgain}) {
return (
<div>
<h1>An error occurred while processing your request</h1>
<p>{error.message}</p>
{tryAgain && (
<button type="button" onClick={tryAgain}>
Try again (attempt to re-render)
</button>
)}
</div>
);
}

export default Error;