【问题标题】:Browser navigation broken by use of React Error Boundaries使用 React 错误边界破坏了浏览器导航
【发布时间】:2018-06-15 17:55:33
【问题描述】:

当我们的 React 16 代码库中出现错误时,它会被我们的顶级错误边界捕获。发生这种情况时,ErrorBoundary 组件会愉快地呈现一个错误页面。

错误边界所在的位置

   return (
     <Provider store={configureStore()}>
       <ErrorBoundary>
         <Router history={browserHistory}>{routes}</Router>
       </ErrorBoundary>
     </Provider>
   )

但是,当使用浏览器返回按钮(一键)返回时,地址中的 URL 会发生变化,但页面不会更新。

我已尝试将错误边界向下移动到组件树,但此问题仍然存在。

关于这个问题出在哪里的任何线索?

【问题讨论】:

    标签: reactjs react-router browser-history


    【解决方案1】:

    操作人员现在可能已经找到了解决方案,但为了其他遇到此问题的人的利益,我将解释为什么我认为它会发生以及可以采取哪些措施来解决它。

    这可能是由于 ErrorBoundary 中的条件渲染呈现错误消息,即使历史已更改。

    虽然上面没有显示,但是ErrorBoundary中的render方法大概是这样的:

    render() {
      if (this.state.hasError) {
        return <h1>An error has occurred.</h1>
      }
    
      return this.props.children;
    }
    

    componentDidCatch 生命周期方法中设置 hasError 的位置。

    一旦设置了 ErrorBoundary 中的状态,它将始终呈现错误消息,直到状态更改(在上面的示例中 hasError 为 false)。即使历史发生变化,子组件(在本例中为 Router 组件)也不会被渲染。

    要解决此问题,请使用 react-router withRouter 高阶组件,通过包装 ErrorBoundary 的导出以使其通过道具访问历史记录:

    export default withRouter(ErrorBoundary);
    

    在 ErrorBoundary 构造函数中,从 props 中检索历史记录并设置处理程序以使用 history.listen 侦听当前位置的更改。如果组件处于错误状态,则当位置更改(单击后退按钮等)时,它会被清除,以便再次呈现子级。

    const { history } = this.props;
    
    history.listen((location, action) => {
      if (this.state.hasError) {
        this.setState({
          hasError: false,
        });
      }
    });
    

    【讨论】:

    • 很高兴它对你们俩都有帮助!
    • +1。我还必须添加它以不更新 setState 上出错的子组件并再次抛出错误,从而在状态中再次设置错误。 shouldComponentUpdate(nextProps, nextState) { return (!this.state.error || nextState.error); }
    • 绝对精彩
    • react-router v6 删除了这个 hoc x.x
    【解决方案2】:

    要添加到上面的 jdavies 答案,请确保在 componentDidMountuseEffect 中注册历史侦听器(使用 [] 表示它没有依赖项),然后 取消注册componentWillUnmountuseEffect return 语句中,否则您可能会遇到setState 在未安装的组件中被调用的问题。

    例子:

      componentDidMount() {
        this.unlisten = this.props.history.listen((location, action) => {
          if (this.state.hasError) {
            this.setState({ hasError: false });
          }
        });
      }
    
      componentWillUnmount() {
        this.unlisten();
      }
    

    【讨论】:

      【解决方案3】:

      tl;dr 将组件包装在您期望错误边界但不是整个树的错误位置

      首先尝试使用withRouter 回答@jdavies,但后来为我的用例找到了更好的解决方案:Dan from the React-Team advised against using HOCs with Error Boundaries and rather use them at stragetic places

      虽然在 Twitter 帖子中是关于利弊的辩论,但 Dan 没有讨论你应该走哪条路,但我发现他的想法很有说服力。

      所以我所做的只是将那些 战略位置 我预计会出错而不是整个树的地方包装起来。对于我的用例,我更喜欢这个,因为我可以抛出比以前更具表现力、更具体的错误页面(出了点问题 vs 有一个身份验证错误)。

      【讨论】:

      • 您能否扩展使用 HoC 的替代方案来解决最初提出的问题?谢谢。
      【解决方案4】:

      jdavies 评论是要走的路,

      但是,如果你对此感到困惑,基本上你让它看起来像这样:

      class ErrorBoundary extends React.Component {
        constructor(props) {
          super(props);
          const { history } = props;
          history.listen((location, action) => {
            if (this.state["hasError"]) {
              this.setState({
                hasError: false,
              });
            }
          });
          this.state = { hasError: false };
        }
      
             ...
      
      

      然后在你添加的文件末尾:

      export default withRouter(ErrorBoundary);
      

      (别忘了在顶部import { withRouter } from "react-router-dom";

      另外,如果您使用的是例如export class ErrorBoundry ... 就像我一样,不要忘记将import { ErrorBoundary } from "./ErrorBoundry"; 更改为import ErrorBoundary from "./ErrorBoundry";,无论您在哪里使用它,例如应用程序.tsx

      【讨论】:

        【解决方案5】:

        React Router 6 与 jdavies 的答案类似:

        const { pathname } = useLocation()
        const originalPathname = useRef(pathname)
        
        useEffect(() => {
          if (pathname !== originalPathname.current) {
            resetErrorBoundary()
          }
        }, [pathname, resetErrorBoundary])
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-02-26
          • 2023-03-16
          • 2019-03-16
          • 1970-01-01
          • 2019-02-06
          • 2022-08-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多