当我们的React 16代码库中出现错误时,它会被我们的顶级错误边界捕获。发生这种情况时,ErrorBoundary
组件会愉快地呈现错误页面。
ErrorBoundary所在的位置
return (
<Provider store={configureStore()}>
<ErrorBoundary>
<Router history={browserHistory}>{routes}</Router>
</ErrorBoundary>
</Provider>
)
但是,当使用浏览器后退按钮(单击一次)导航回来时,URL会在地址中更改,但页面不会更新。
我已尝试将错误边界向下移动到组件树中,但此问题仍然存在。
有关此问题所在的任何线索?
答案 0 :(得分:18)
op现在可能已经找到了一个解决方案,但是为了其他任何有这个问题的人的利益,我将解释为什么我认为它正在发生以及如何解决它。
这可能是由于ErrorBoundary中的条件呈现呈现错误消息,即使历史记录已更改。
虽然上面没有显示,但是ErrorBoundary中的render方法可能类似于:
render() {
if (this.state.hasError) {
return <h1>An error has occurred.</h1>
}
return this.props.children;
}
在componentDidCatch
生命周期方法中设置了hasError。
一旦设置了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,
});
}
});
答案 1 :(得分:1)
一旦检测到错误,我们需要在ErrorBoundary中重置错误状态。如下所示:
shouldComponentUpdate(nextProps, nextState) {
if (this.state.hasError && nextState.hasError === false) {
return false;
}
return true;
}
render() {
if (this.state.hasError) {
this.setState({ hasError: false });
// return fallback component to show on error
}
return this.props.children;
}
}
答案 2 :(得分:0)
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 有一个身份验证错误)。
答案 3 :(得分:0)
要添加到上述jdavies的答案中,请确保您在componentDidMount
或useEffect
中注册了历史监听器(使用[]
表示它没有依赖性),并且在componentWillUnmount
或useEffect
返回语句中取消注册,否则可能会遇到在卸载的组件中调用setState
的问题。
示例:
componentDidMount() {
this.unlisten = this.props.history.listen((location, action) => {
if (this.state.hasError) {
this.setState({ hasError: false });
}
});
}
componentWillUnmount() {
this.unlisten();
}
答案 4 :(得分:0)
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