使用来自 react-error-boundary 的 useErrorHandler 挂钩在回程中重置错误边界

reset error boundary on back journey using useErrorHandler hook from react-error-boundary

我正在使用 react-error-boundary 包来显示回退 UI 以防应用程序抛出任何错误。该软件包对我来说很好用。如果我使用浏览器后退按钮转到前一页,我需要了解如何重置应用程序错误状态,因为转到前一页还显示回退 UI 而不是原始组件。无论如何我们可以渲染原始组件吗?

在下面的代码中,用户将在 Page2 上被抛出错误,因为我将空对象作为道具传递。在这种情况下,它将显示后备屏幕。如果我点击后退按钮,后备屏幕也会显示在第 1 页上,这是我不想要的。

App.js

const errorHandler = (error) =>{
      console.log(error)
}

<BrowserRouter basename={'/bookingtool/obt/'}>
     <ErrorBoundary FallbackComponent={Fallback} onError={errorHandler}>
          <Routes>
               <Route path="/page1" element={<Page1 PageName="Page1" />} />
               <Route path="/page2" element={<Page2 PageName={{}} /> } />
          </Routes>
    <ErrorBoundary />
</BrowserRouter>

Page1.js

import { useErrorHandler } from 'react-error-boundary';
const Page1 = ({PageName}) =>{
     return(<p>{PageName}</p>)
}

Page2.js

import { useErrorHandler } from 'react-error-boundary';
const Page2 = ({PageName}) =>{
     return(<p>{PageName}</p>)
}

Fallback.js

import React from "react";

const Fallback= (props) => {
    return(<h1>Something went wrong</h1>)
}

尝试在您离开时重置错误状态:

const Fallback= ({ error, resetErrorBoundary} ) => {
    const location = useLocation();
    const errorLocation = useRef(location.pathname);
    useEffect(() => {
        if (location.pathname !== errorLocation.current) {
            resetErrorBoundary();
        }
    },[location.pathname])
    return(<h1>Something went wrong</h1>)
}

提供 key<ErrorBoundary />。每当 key 发生变化时,错误边界就会重置。

在你的例子中,使用 useLcation().pathname 作为 key 意味着它会在路径改变时重置:

const location =  useLocation();
const [path, setPath] = useState(location.pathname);
useLayoutEffect(
  () => setPath(location.pathname),
  [location.pathname]
);
// ...
return (
  <ErrorBoundary key={path}>
    {/* ... */}
  </ErrorBoundary>
)

作为一个单独的建议,我会将错误处理移到 Layout 组件中。这将允许在发生错误时保持导航,这是很好的用户体验。

基础example here.

您可以使用以下方式强制 re-render 错误边界, 首先制作一个单独的功能组件来保存错误边界并将侦听器添加到历史

import { createBrowserHistory } from "history";
const history = createBrowserHistory();
//a fallback component
const ErrorFallback = () => {
 return <>
   <h1>Something went wrong.</h1>
   <button onClick={() => {
      history.back();
    }}>go back </button>
  </>
}
 
function RoutesContainer() {
 const [update, setUpdate] = useState(false);
 let navigate = useNavigate();
const historyChange = () => {
if (window.location.pathname !== history.location.pathname) {
  navigate(window.location.pathname, { replace: true });
}
};
useEffect(() => {
 history.listen(historyChange)
}, [historyChange])

 return <ErrorBoundary key={window.location.pathname}  FallbackComponent={ErrorFallback}>
<Routes>
  <Route path="/page1" element={<Page1 PageName="Page1" />} />
  <Route path="/page2" element={<Page2 PageName={{}} />} />
</Routes>
 </ErrorBoundary>
  }

当你从page2返回到page1时,history.location.pathname的值为“page2”,因为你按返回,这个值将不匹配到window.location.pathname,因为window.location.pathname 有值“page1”,在这个阶段我们将导航到 window.location.pathname,并使用这个值作为我们错误边界的关键 component.at 在写这个答案的时候我已经使用 react-router-dom V6, 反应 v18

这里有一个完整的用例演示

    import React, { useEffect, useState } from 'react';
    import { BrowserRouter, Link, Route, Routes } from 'react-router-dom';
    import { createBrowserHistory } from "history";
    import { useNavigate } from "react-router-dom";
    import { ErrorBoundary } from 'react-error-boundary'

    const history = createBrowserHistory();

    const ErrorFallback = () => {
      return <>
        <h1>Something went wrong.</h1>
        <button onClick={() => {
          history.back();
        }}>go back </button>
      </>
    }
    const Page1 = ({ PageName }) => {
      return (<p>{PageName}
        <Link to={'/page2'} >page 2</Link>
      </p>)
    }

    const Page2 = ({ PageName }) => {
      return (<p>{PageName}</p>)
    }
    function RoutesContainer() {
      const [update, setUpdate] = useState(false);
      let navigate = useNavigate();

      const historyChange = () => {
        if (window.location.pathname !== history.location.pathname) {
          navigate(window.location.pathname, { replace: true });
        }
      };

      useEffect(() => {
        history.listen(historyChange)
      }, [historyChange])

      return <ErrorBoundary key={window.location.pathname}  FallbackComponent={ErrorFallback}>
        <Routes>
          <Route path="/page1" element={<Page1 PageName="Page1" />} />
          <Route path="/page2" element={<Page2 PageName={{}} />} />
        </Routes>
      </ErrorBoundary>
    }


    function App() {
      return (
        <BrowserRouter><RoutesContainer /></BrowserRouter>
      );
    }

    export default App;