浏览器导航因使用 React 错误边界而中断



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

错误边界所在的位置

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

但是,当使用浏览器后退按钮(单击一下)向后导航时,地址中的 URL 会更改,但页面不会更新。

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

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

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

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

虽然上面没有显示,但 ErrorBoundary 中的渲染方法可能类似于以下内容:

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

componentDidCatch生命周期方法中设置哈斯错误的位置。

一旦设置了 ErrorBoundary 中的状态,它将始终呈现错误消息,直到状态更改(在上面的示例中 hasError 为 false)。子组件(在本例中为路由器组件)将不会呈现,即使历史记录发生更改也是如此。

要解决此问题,请使用带有路由器高阶组件的 react-router withRouter 通过包装 ErrorBoundary 的导出,使其通过 props 访问历史记录:

export default withRouter(ErrorBoundary);

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

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

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

例:

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

React Router 6 的 jdavies 答案的类似值是:

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

tl;DR 将组件包装到预期错误的位置,其中包含错误边界,而不是整个树

首先尝试了withRouter@jdavies答案,但后来为我的用例找到了更好的解决方案:来自 React-Team 的 Dan 建议不要使用带有错误边界的 HOC,而是在战略位置使用它们。

在那个Twitter帖子中,围绕利弊进行了辩论,Dan对你应该走哪条路持开放态度,但我发现他的想法令人信服。

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

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";无论您在哪里使用它,例如 App.tsx

最新更新