错误边界禁用交换机内部的路由 [英] Error Boundaries disables routing inside of a Switch

查看:23
本文介绍了错误边界禁用交换机内部的路由的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于很长时间在遇到错误边界后一直试图让路由在我们的应用程序中工作,但直到今天我才发现与周围许多示例看似相同的代码有一个重要区别:路由被 开关.如果启用,这个简单的更改足以阻止路由工作.演示

采用以下代码段.如果我删除 Switch 位,即使每个组件都应该失败,这也能正常工作,但如果被 switch 包裹则不会.我想知道为什么.

(<错误边界><MyComponent1 title="组件 1"/></ErrorBoundary>)}/><路线路径=/comp1"渲染={() =>(<错误边界><MyComponent1 title="Component 1 Again"/></ErrorBoundary>)}/><路线路径=/comp2"渲染={() =>(<错误边界><MyComponent2 title="组件 2"/></ErrorBoundary>)}/></开关>

解决方案

基本上,这个问题归结为 React 如何做 和解.

<块引用>

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

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

<开关><Route path="a" component={Foo}/><Route path="b" component={Foo}/></开关></应用程序>

这会有点不直观地为两条路由重用相同的 Foo 实例! 将始终返回第一个匹配的元素,所以基本上当 React 渲染时,这相当于树 用于路径a", 用于路径b".如果 Foo 是一个有状态的组件,这意味着状态被保留,因为实例只是传递了新的 props(在我们的例子中除了 children 之外没有),并且预计会处理这是通过重新计算自己的状态.

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

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

<块引用>

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

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

<块引用>

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

使用 key 的替代方法是实现公开一些可以从外部调用的钩子,或者尝试检查 children 属性的变化,这很难.像这样的东西可以工作(演示):

componentDidUpdate(prevProps, prevState, snapshot) {const childNow = React.Children.only(this.props.children);const childPrev = React.Children.only(prevProps.children);如果(childNow !== childPrev){this.setState({ errorInfo: null });}

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

For a long time I have been trying to get routing to work in our app after an error boundary has been hit, but only today did I find the code that was seemingly identical to the many examples lying around had one important difference: the routes were wrapped by a Switch. This simple change is enough to stop routing from working, if enabled. Demo

Take the following snippet. If I remove the Switch bit, this works fine even if each component should fail, but not if wrapped by the switch. I would like to know why.

<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>

解决方案

Basically, this problem boils down to how React does reconciliation.

When a component updates, the instance stays the same, so that state is maintained across renders. React updates the props of the underlying component instance to match the new element

Say we have this example app:

<App>
  <Switch>
    <Route path="a" component={Foo}/>
    <Route path="b" component={Foo}/>
  </Switch>
</App> 

This will, somewhat unintuitively, reuse the same instance of Foo for both routes! A <Switch> will always return the first matched element, so basically when React renders this is equivalent of the tree <App><Foo/></App> for path "a" and <App><Foo/></App> for path "b". If Foo is a component with state, that means that state is kept, as the instance is just passed new props (for which there are none, except children, in our case), and is expected to handle this by recomputing its own state.

As our error boundary is being reused, while it has state that has no way of changing, it will never re-render the new children of its parent route.

React has one trick hidden up its sleeve for this, which I have only seen explicitly documented on its blog:

In order to reset the value when moving to a different item (as in our password manager scenario), we can use the special React attribute called key. When a key changes, React will create a new component instance rather than update the current one. (...) In most cases, this is the best way to handle state that needs to be reset.

I was first hinted to this by a somewhat related issue on Brian Vaughn's error bondary package:

The way I would recommend resetting this error boundary (if you really want to blow away the error) would be to just clear it out using a new key value. (...) This will tell React to throw out the previous instance (with its error state) and replace it with a new instance.

The alternative to using keys would be to implement either exposing some hook that could be called externally or by trying to inspect the children property for change, which is hard. Something like this could work (demo):

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 });
   }

But it's more work and much more error prone, so why bother: just stick to adding a key prop :-)

这篇关于错误边界禁用交换机内部的路由的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆