In this example we will play with the Error Boundary concept.
Install Node.js and npm if they are not already installed on your computer.
Verify that you are running at least node v6.x.x and npm 3.x.x by running
node -v
andnpm -v
in a terminal/console window. Older versions may produce errors.
-
Copy the content from 03 State and execute
npm install
. -
Let's create a faulty component:
./src/faultyComponent.tsx
import * as React from 'react';
export class FaultyComponent extends React.Component {
componentDidMount() {
throw "I'm the faulty component, generating a bad crash."
}
render() {
return (
<h2>Hello from Faulty Component</h2>
)
}
}
- Let's instantiate this component in our app.tsx
./src/app.tsx
import * as React from 'react';
import { HelloComponent } from './hello';
import { NameEditComponent } from './nameEdit';
+ import { FaultyComponent } from './faultyComponent';
interface Props {
}
interface State {
userName: string;
}
export class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { userName: 'defaultUserName' };
}
setUsernameState = (event) => {
this.setState({ userName: event.target.value });
}
public render() {
return (
<>
<HelloComponent userName={this.state.userName} />
<NameEditComponent userName={this.state.userName} onChange={this.setUsernameState} />
+ <FaultyComponent/>
</>
);
}
}
- Let's run the app.
npm start
-
If you open the console you will see a bad crash being reported, that's something that you wouldn't like to suffer when you are using plugins and other components that you may not trust, why not wrap any error in a safe area ? and display a friendly component failed to load in case of an uncontroller error happen in that area (and keep the rest of application working as expected).
-
Let's create an Error Boundary.
./src/erroBoundary.tsx
import * as React from 'React';
export class ErrorBoundary extends React.Component {
state = { error: null, errorInfo: null };
componentDidCatch(error, errorInfo) {
this.setState({
error: error,
errorInfo: errorInfo
});
}
render() {
if (this.state.errorInfo) {
return (
<div>
<h2>Plugin Failed to load, optional error info:</h2>
<details style={{ whiteSpace: "pre-wrap" }}>
{this.state.error && this.state.error.toString()}
<br />
{this.state.errorInfo.componentStack}
</details>
</div>
);
}
return this.props.children;
}
}
- And let's wrap our faultyComponent inside this error boundary (we could wrap a set of components if needed).
./src/app.tsx
import * as React from 'react';
import { HelloComponent } from './hello';
import { NameEditComponent } from './nameEdit';
import { FaultyComponent } from './faultyComponent';
+ import { ErrorBoundary } from './errorBoundary';
interface Props {
}
interface State {
userName: string;
}
export class App extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { userName: 'defaultUserName' };
}
setUsernameState = (event) => {
this.setState({ userName: event.target.value });
}
public render() {
return (
<>
<HelloComponent userName={this.state.userName} />
<NameEditComponent userName={this.state.userName} onChange={this.setUsernameState} />
+ <ErrorBoundary>
<FaultyComponent/>
+ </ErrorBoundary>
</>
);
}
}
There's a nice generic wrapper for this ErrorBoundary: https://github.com/bvaughn/react-error-boundary
Error boundaries and event handlers: facebook/react#11409