如何在使用react-router路由的组件中集成错误边界

dxb*_*dev 5 reactjs react-router

我有一个页面,其中使用使用React Router加载的不同组件。当我使用React路由器路由到不同页面时,是否可以如下面的代码所示那样在每个组件中集成错误边界?

我的目标是特别针对单个组件显示错误,以便在一个组件出现错误的情况下其他组件也可以工作。

请在下面查看我的代码:

index.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';
import registerServiceWorker from './registerServiceWorker';

ReactDOM.render(<App />, document.getElementById('root'));

registerServiceWorker();
Run Code Online (Sandbox Code Playgroud)

App.js

import React, { Component } from 'react';
import {BrowserRouter as Router, Route, Link } from 'react-router-dom';
//import ErrorBoundary from "./errorboundary";
import MyComponent1 from './component1';
import MyComponent2 from './component2';

class App extends Component {
render() {
return (
<Router>
<div style={{ backgroundColor: 'green' }}>
<div style={{ backgroundColor: '#f0f0ae', height: '30px' }}>
<Link to='/'>Link 1</Link> &#160;&#160;
<Link to='/comp1'>Link 2</Link> &#160;&#160;
<Link to='/comp2'>Link 3</Link> &#160;&#160;
</div>

<div style={{ backgroundColor: '#ffc993', height: '150px' }}>
<Route path='/' exact render={() => <MyComponent1 title="Component 1" />} />
<Route path='/comp1' render={() => <MyComponent1 title="Component 1 Again" />} />
<Route path='/comp2' render={() => <MyComponent2 title="Component 2" />} />
</div>
</div>
</Router>
);
}
}

export default App;
Run Code Online (Sandbox Code Playgroud)

这是我要使用“错误边界”的组件之一。

component1.js

import React, { Component } from 'react';
import ErrorBoundary from "./errorboundary";

class MyComponent1 extends Component {
state = {
boom: false,
};

throwError = () => this.setState({ boom: true });

render() {
const { title } = this.props;

if(this.state.boom) {
throw new Error(`${title} throw an error!`);
}

return (
<ErrorBoundary>
<input type='button' onClick={this.throwError} value={title} />
</ErrorBoundary>
)
}
}

export default MyComponent1;
Run Code Online (Sandbox Code Playgroud)

这是我要使用错误边界的另一个组件。

component2.js

import React, { Component } from 'react';
import ErrorBoundary from "./errorboundary";

class MyComponent2 extends Component {
state = {
boom: false,
};

throwError = () => this.setState({ boom: true });

render() {
const { title } = this.props;

if(this.state.boom) {
throw new Error(`${title} throw an error!`);
}

return (
<ErrorBoundary>
<input type='button' onClick={this.throwError} value={title} />
</ErrorBoundary>
)
}
}

export default MyComponent2;
Run Code Online (Sandbox Code Playgroud)

当每个组件中都有错误时,这是​​我使用错误边界定制的错误消息。

errorboundary.js

import React, { Component } from 'react';

class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { error: null, errorInfo: null };

if(this.props.showError === false)
{
this.state.error = null;
this.state.errorInfo = null;
}
}

componentDidCatch = (error, info) => {
console.log("error did catch");
this.setState({error: error, errorInfo: info });        
}

render() {
if(this.state.errorInfo) {
return (
<div style={{ backgroundColor: '#ffcc99', color: 'white', width: '500px', height: '60px' }}>
An Error Occurred !
</div>
);
}
else {
return this.props.children;
}
}
}

export default ErrorBoundary;
Run Code Online (Sandbox Code Playgroud)

谁能帮帮我吗?我是React JS的新手。

ilo*_*ett 9

包裹你的主路由器插座,ErrorBoundary但提供一个独特keyErrorBoundaryto 强制它在位置改变时拆卸并构建一个新实例:

export const AppRouter = () => {
  const location = useLocation();

  return (
    <main>
      <ErrorBoundary key={location.pathname}>
        <Switch>
          <Route path="/" component={Dashboard} />
          <Route path="/orders" component={Orders} />
        </Switch>
      </ErrorBoundary>
    </main>
  );
};
Run Code Online (Sandbox Code Playgroud)

请注意,这种简单但懒惰的解决方案将导致ErrorBoundary在位置发生变化时对其中的所有内容进行不必要的渲染。您可以通过基于路径名而不是使用整个路径名本身计算一个不太频繁更改的键来解决这个问题。


Ste*_*e L 8

我认为@Shubham Khatri 的这个答案是正确的,我只是想通过提供一个组件来完成它,该组件封装了将 a 包装<Route>在一个<ErrorBoundary>

const RouteWithErrorBoundary = (props) => {
    return (
        <ErrorBoundary key={props.location?.pathname}>
            <Route {...props} />
        </ErrorBoundary>
    );
};
Run Code Online (Sandbox Code Playgroud)

让我们用 typescript 添加一些类型:

const RouteWithErrorBoundary: React.FC<RouteProps> = (props) => {
    return (
        <ErrorBoundary key={props.location?.pathname}>
            <Route {...props} />
        </ErrorBoundary>
    );
};
Run Code Online (Sandbox Code Playgroud)

然后您就可以非常轻松地使用它:

<Switch>
    <RouteWithErrorBoundary path='/' exact render={() => <MyComponent1 title="Component 1" />} />
    <RouteWithErrorBoundary path='/comp1' render={() => <MyComponent1 title="Component 1 Again" />} />
    <RouteWithErrorBoundary path='/comp2' render={() => <MyComponent2 title="Component 2" />} />
</Switch>
Run Code Online (Sandbox Code Playgroud)


Shu*_*tri 7

您未ErrorBoundary在正确的位置使用。ErrorBoundary环绕input标签的包装只能确保如果输入中存在错误,该错误会被ErrorBoundary

return (
    <ErrorBoundary>   {/* errorBounday here only catches error in input */}
        <input type='button' onClick={this.throwError} value={title} />
    </ErrorBoundary>
)
Run Code Online (Sandbox Code Playgroud)

您需要将ErrorBoundary包装在引发错误的组件周围,例如

<Route
          path="/comp1"
          render={() => (
            <ErrorBoundary>
              <MyComponent1 title="Component 1 Again" />
            </ErrorBoundary>
          )}
        />
Run Code Online (Sandbox Code Playgroud)

这样,如果MyComponent1引发错误,那么您将被错误边界捕获。您的代码看起来像

class App extends React.Component {
  render() {
    return (
      <Router>
        <div style={{ backgroundColor: "green" }}>
          <div style={{ backgroundColor: "#f0f0ae", height: "30px" }}>
            <Link to="/">Link 1</Link> &#160;&#160;
            <Link to="/comp1">Link 2</Link> &#160;&#160;
            <Link to="/comp2">Link 3</Link> &#160;&#160;
          </div>

          <div style={{ backgroundColor: "#ffc993", height: "150px" }}>
            <Route
              path="/"
              exact
              render={() => (
                <ErrorBoundary>
                  <MyComponent1 title="Component 1" />
                </ErrorBoundary>
              )}
            />
            <Route
              path="/comp1"
              render={() => (
                <ErrorBoundary>
                  <MyComponent1 title="Component 1 Again" />
                </ErrorBoundary>
              )}
            />
            <Route
              path="/comp2"
              render={() => (
                <ErrorBoundary>
                  <MyComponent2 title="Component 2" />
                </ErrorBoundary>
              )}
            />
          </div>
        </div>
      </Router>
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

演示

请阅读此问题以了解如何在codesandbox中测试ErrorBoundaries