Por que esse componente está sendo renderizado após gerar um erro?

8

Atualmente, estou acompanhando a documentação do React JS e encontrei um problema com os limites de erro que não estavam funcionando conforme o esperado. Tentei replicar o exemplo mostrado no CodePen fornecido pelos documentos e alguns outros exemplos simples que encontrei na internet, no entanto, ele não está funcionando da mesma forma que na demonstração e estou com dificuldades. para entender o porquê.

O problema exato é que o erro está sendo gerado duas vezes porque o componente BuggyCounter é processado por um tempo extra. Eu não entendo por que o componente está renderizando uma segunda vez.

Por favor, dê uma olhada neste exemplo mínimo.

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;

O componente BuggyCounter está sendo substituído pela <p>tag que gera "Erro" (que é o efeito desejado), mas apenas por um momento. Imediatamente depois disso, a página de erro padrão está sendo mostrada, anulando a finalidade dos limites de erro.

Aqui está o meu console:

meus erros e mensagens de depuração

Agradecemos qualquer informação que você possa fornecer sobre este tópico.

Resolução temporária:

Não é uma resposta para minha pergunta, mas uma maneira de impedir a renderização redundante é lançar o erro em componentDidUpdatevez de 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');
  }
Anthony Yershov
fonte
Boa pergunta, não tenho idéia do porquê, mas talvez quando um componente gera um erro, ele se renderize novamente? Nenhuma pista.
Vencovsky
2
Não foi possível replicar o erro em uma sandbox codesandbox.io/s/react-example-slnhn
Prasanna
1
Um ponto estranho a se observar aqui é que, se você adicionar um log antes de lançar o erro componentDidCatch, verá que ele lança o erro duas vezes, mas apenas passa componenDidCatchuma vez.
Vencovsky
@Prasanna mesmo aqui
Vencovsky
Se alguém quiser ver a resposta de react github.com/facebook/react/issues/17966
Vencovsky em

Respostas:

3

Edição 2:

Bem, o problema está na versão react 16.12.0, se você mudar para 16.0.0, ele não será renderizado duas vezes mais. Você pode testar isso nesta caixa de código , alterando a versão de reação.

Esse é um bom problema para adicionar no react github.

Provavelmente é algo internamente no código principal do react. Portanto, dependendo da sua versão, ela será renderizada duas ou apenas uma vez.


Editar:

Por que o componente é renderizado novamente? Nenhuma idéia.

Mas, a página de erro é mostrada apenas no modo de desenvolvimento , portanto você componentDidCatchestá trabalhando.


Resposta antiga / ruim

O componente BuggyCounter está sendo substituído pela <p>tag que gera "Erro" (que é o efeito desejado), mas apenas por um momento. Imediatamente depois disso, a página de erro padrão está sendo mostrada, anulando a finalidade dos limites de erro.

A only for a momentparte não é verdadeira. A página de erro é apenas para desenvolvimento, se você a executar no modo de produção, ela não será mostrada.

E como você pode ver no meu exemplo , se você fechar a página de erro, verá o componente de erro.

Isso é explicado nesta resposta .

Portanto, na versão demo fornecida pelo react docs, ela não mostra a página de erro devido à sua configuração, e não ao código. Seu código está funcionando bem, basta fechar a página de erro e ver os resultados.

Vencovsky
fonte
Ele BuggyCounterdeve estar sendo renderizado duas vezes quando, state.counter === 5porque o log mostra "Rendering BuggyCounter. Count: 5" duas vezes. Eu acredito que você explicou por que ErrorHandlerestá sendo processado um tempo extra.
Anthony Yershov
Ainda não estou claro por que BuggyCounterestá executando uma renderização adicional após gerar um erro.
Anthony Yershov
1
@AnthonyYershov é porque você está mudando de estado componentDidCatch. Alterações de estado renderizarão novamente seu componente.
Prasanna
@ Pasanna sim exatamente. É por isso que ErrorHandlerestá sendo renderizado novamente. Essa não é a minha pergunta. Se você olhar para o log, BuggyCounterestá sendo renderizado um tempo extra antes de ErrorHandlerser renderizado novamente devido a alterações de estado.
Anthony Yershov
1
Posso confirmar que esse problema não existe no React 16.0.0, obrigado por essas informações e por ter levantado esse problema no github do react.
Anthony Yershov