Error Boundaries disables routing inside of a Switch

后端 未结 2 1987
情歌与酒
情歌与酒 2021-01-06 19:08

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

2条回答
  •  不知归路
    2021-01-06 19:40

    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:

    
      
        
        
      
     
    

    This will, somewhat unintuitively, reuse the same instance of Foo for both routes! A will always return the first matched element, so basically when React renders this is equivalent of the tree for path "a" and 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 :-)

提交回复
热议问题