Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this component being rendered after throwing an error?

Tags:

reactjs

I’m currently following along with the React JS documentation and I’ve encountered an issue with the error boundaries not working as expected. I’ve tried replicating the example shown in the CodePen provided by the docs, and a few other simple examples I've found on the internet, however it is not working the same for me as it is in the demo and I’m struggling to understand why.

The exact issue is that the error is being thrown twice because the BuggyCounter component gets rendered an extra time. I do not understand why the component is rending a second time.

Please have a look at this minimal example.

import React, { Component } from 'react';

function App() {
  return (
    <ErrorHandler>
      <BuggyCounter />
    </ErrorHandler>
  );
}

class ErrorHandler extends Component {
  constructor(props) {
    super(props);
    this.state = {
      error: false,
      errorInfo: null
    }
  }

  componentDidCatch(error, errorInfo) {
    this.setState({ error, errorInfo });
  }

  render() {
    console.log('rendering ErrorHandler. ' + (this.state.error ? "error" : "no error"));
    if(this.state.error) {
      return <p>Error</p>
    }
    return this.props.children;
  }
}

class BuggyCounter extends Component {
  constructor(props) {
    super(props);
    this.state = { counter: 0 };
  }

  handleClick = () => {
    this.setState(({ counter }) => ({
      counter: counter + 1
    }));
  };

  render() {
    console.log('rendering BuggyCounter. count: ' + this.state.counter);
    if (this.state.counter === 5) {
      throw new Error('I crashed!');
    }
    return <h1 onClick={this.handleClick}>{this.state.counter}</h1>
  }
}

export default App;

The BuggyCounter component is being replaced with the <p> tag which renders “Error” (which is the desired effect), but only for a moment. Immediately after that, the default error page is being shown, defeating the purpose of Error Boundaries.

Here is my console:

my errors and debug messages

I would appreciate any information you could provide on this topic.

Temporary resolution:

It's not an answer to my question, but one way to prevent the redundant render is to throw the error from componentDidUpdate instead of render.

  render() {
    console.log('rendering BuggyCounter. count: ' + this.state.counter);
    return <h1 onClick={this.handleClick}>{this.state.counter}</h1>
  }

  componentDidUpdate() {
    if(this.state.counter === 5)
      throw new Error('I crashed');
  }
like image 826
Anthony Yershov Avatar asked Feb 03 '20 17:02

Anthony Yershov


People also ask

What causes a component to get re rendered?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.

What will happen when a component in React component tree throws an error?

Error boundaries are React components that catch JavaScript errors anywhere in their child component tree, log those errors, and display a fallback UI instead of the component tree that crashed. Error boundaries catch errors during rendering, in lifecycle methods, and in constructors of the whole tree below them.

How would you prevent a component from rendering?

1. Memoization using useMemo() and UseCallback() Hooks. Memoization enables your code to re-render components only if there's a change in the props. With this technique, developers can avoid unnecessary renderings and reduce the computational load in applications.

What does it mean to render a component?

Rendering is a process that is triggered by a change of state in some component of your application, when a state change occurs React: It will collect from the root of your App all the components that requested a re-render because their state or their props changed.


1 Answers

Edit 2:

Well, the problem is in react version 16.12.0, if you change it to 16.0.0, it won't rerender twice. You can test this in this codesandbox by changing the react version.

This is a good issue to add at react github.

Probably is something internally in react core code. So depending of your version, it will render twice or only once.


Edit:

Why the component rerenders? No Idea.

But, the error page is only shown in development mode, so your componentDidCatch is working.


Old / Bad Answer

The BuggyCounter component is being replaced with the <p> tag which renders “Error” (which is the desired effect), but only for a moment. Immediately after that, the default error page is being shown, defeating the purpose of Error Boundaries.

The only for a moment part isn't true. The error page is acctually only for development, if you run it in production mode, it won't be shown.

And as you can see in my example, if you close the error page, you will see the error component.

This is explained in this answer.

So in the demo version provided by react docs, it doesn't show the error page because of it's configuration, not the code it self. Your code is working fine, just close the error page and see the results.

like image 137
Vencovsky Avatar answered Oct 23 '22 15:10

Vencovsky