错误边界禁用交换机内部的路由

oli*_*ren 4 error-handling reactjs react-router

对于很长一段时间我一直在尝试错误的边界后,得到路由到我们的应用程序的工作一直打到,但今天我不仅发现了看似相同的例子很多躺在附近的代码有一个重要的区别:条航线由一个Switch. 如果启用,这个简单的更改足以阻止路由工作。演示

取以下片段。如果我删除该Switch位,即使每个组件都应该失败,这也能正常工作,但如果被开关包裹则不会。我想知道为什么。

<div style={{ backgroundColor: "#ffc993", height: "150px" }}>
<Switch>
  <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>
    )}
  />
</Switch>
Run Code Online (Sandbox Code Playgroud)

oli*_*ren 7

基本上,这个问题归结为 React 如何进行reconciliation

当组件更新时,实例保持不变,以便在渲染之间保持状态。React 更新底层组件实例的 props 以匹配新元素

假设我们有这个示例应用程序:

<App>
  <Switch>
    <Route path="a" component={Foo}/>
    <Route path="b" component={Foo}/>
  </Switch>
</App> 
Run Code Online (Sandbox Code Playgroud)

这将有点不直观,Foo为两条路线重用相同的实例!A<Switch>将始终返回第一个匹配的元素,因此基本上当 React 渲染时,这相当于<App><Foo/></App>路径“a”和<App><Foo/></App>路径“b”的树。如果 Foo 是一个有状态的组件,这意味着状态被保留,因为实例只是传递了新的 props(children在我们的例子中没有,除了),并且期望通过重新计算它自己的状态来处理这个。

由于我们的错误边界被重用,虽然它的状态无法改变,但它永远不会重新渲染其父路由的新子节点。

React 对此隐藏了一个技巧,我只在其博客中明确记录过这一点:

为了在移动到不同项目时重置值(如在我们的密码管理器场景中),我们可以使用称为 key 的特殊 React 属性。当一个键发生变化时,React 会创建一个新的组件实例而不是更新当前的组件实例。(...) 在大多数情况下,这是处理需要重置的状态的最佳方式。

我首先被一个关于 Brian Vaughn 的错误绑定包的相关问题暗示了这一点:

我建议重置此错误边界的方法(如果您真的想消除错误)是使用新的键值将其清除。(...) 这将告诉 React 丢弃前一个实例(带有错误状态)并用新实例替换它。

使用keys的替代方法是实现公开一些可以在外部调用的钩子,或者尝试检查children属性的更改,这很困难。这样的事情可以工作(演示):

componentDidUpdate(prevProps, prevState, snapshot) {
    const childNow = React.Children.only(this.props.children);
    const childPrev = React.Children.only(prevProps.children);

    if (childNow !== childPrev) {
        this.setState({ errorInfo: null });
   }
Run Code Online (Sandbox Code Playgroud)

但它需要更多的工作并且更容易出错,所以为什么要麻烦:坚持添加一个key道具:-)