O componente filho React não é atualizado após a alteração do estado pai


109

Estou tentando fazer um bom componente ApiWrapper para preencher dados em vários componentes filhos. De tudo que li, isso deve funcionar: https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'nothing fetched yet'
      }
    };
  }

  componentDidMount() {
    this._makeApiCall(this.props.endpoint);
  }

  _makeApiCall(endpoint) {
    fetch(endpoint).then(function(response) {
      this.setState({
        response: response
      });
    }.bind(this))
  }

  render() {
    return <Child data = {
      this.state.response
    }
    />;
  }
}

class Child extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      data: props.data
    };
  }

  render() {
    console.log(this.state.data, 'new data');
    return ( < span > {
      this.state.data.title
    } < /span>);
  };
}

var element = < ApiWrapper endpoint = "https://jsonplaceholder.typicode.com/posts/1" / > ;

ReactDOM.render(
  element,
  document.getElementById('container')
);

Mas, por algum motivo, parece que o componente filho não está atualizando quando o estado pai muda.

Estou faltando alguma coisa aqui?

Respostas:


203

Existem dois problemas com o seu código.

O estado inicial do seu componente filho é definido a partir de adereços.

this.state = {
  data: props.data
};

Citando esta resposta SO :

Passar o estado inicial para um componente como um propé um antipadrão porque o getInitialStatemétodo (em nosso caso, o constutor) só é chamado na primeira vez que o componente é renderizado. Nunca mais. O que significa que, se você renderizar novamente esse componente passando um valor diferente como um prop, o componente não reagirá de acordo, porque o componente manterá o estado desde a primeira vez que foi renderizado. É muito sujeito a erros.

Portanto, se você não puder evitar tal situação, a solução ideal é usar o método componentWillReceivePropspara ouvir novos adereços.

Adicionar o código abaixo ao seu componente filho resolverá seu problema com a re-renderização do componente filho.

componentWillReceiveProps(nextProps) {
  this.setState({ data: nextProps.data });  
}

O segundo problema é com o fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> you missed this part
    .then((response) => this.setState({ response }));
}

E aqui está um violino funcionando: https://jsfiddle.net/o8b04mLy/


1
"Não há problema em inicializar o estado com base em props se você souber o que está fazendo" Existem outras desvantagens em definir o estado via prop, além do fato de que nextPropnão aciona uma nova renderização sem componentWillReceiveProps(nextProps)?
— Vinnie James

11
Pelo que eu sei, não há outras desvantagens. Mas, no seu caso, podemos evitar claramente um estado dentro do componente filho. O pai pode apenas passar os dados como adereços, e quando o pai é renderizado novamente com seu novo estado, o filho também renderiza novamente (com novos adereços). Na verdade, manter um estado dentro da criança é desnecessário aqui. Componentes puros FTW!
— Yadhu Kiran

7
Para quem está lendo de agora em diante, verifique static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…
— GoatsWearHats

4
Existe alguma outra solução para isso diferente de componentWillReceiveProps () porque ele está obsoleto agora?
— LearningMath de

3
@LearningMath, consulte os documentos mais recentes do React, que falam sobre formas alternativas. Você pode ter que reconsiderar sua lógica.
— Yadhu Kiran

1

Existem algumas coisas que você precisa mudar.

Ao fetchobter a resposta, não é um json. Eu estava procurando como posso obter este json e descobri este link .

Por outro lado, você precisa pensar que a constructorfunção é chamada apenas uma vez.

Portanto, você precisa alterar a maneira como recupera os dados no <Child>componente.

Aqui, deixei um código de exemplo: https://jsfiddle.net/emq1ztqj/

Espero que ajude.


Obrigado. Embora, olhando para o exemplo que você fez, ainda pareça que Child nunca atualiza. Alguma sugestão sobre como mudar a maneira como a criança recebe os dados?
— Vinnie James

2
Você tem certeza? Vejo que o <Child>componente recupera os novos dados https://jsonplaceholder.typicode.com/posts/1e os processa novamente.
— slorenzo de

1
Sim, vejo que está funcionando agora no OSX. O iOS não estava acionando a re-renderização no navegador do aplicativo StackExchange
— Vinnie James
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.