147 votes

Composant enfant React ne se met pas à jour après un changement d'état du parent

Je tente de créer un beau composant ApiWrapper pour remplir des données dans divers composants enfants. D'après tout ce que j'ai lu, cela devrait fonctionner: https://jsfiddle.net/vinniejames/m1mesp6z/1/

class ApiWrapper extends React.Component {

  constructor(props) {
    super(props);

    this.state = {
      response: {
        "title": 'rien récupéré pour le moment'
      }
    };
  }

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

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

  render() {
    return ;
  }
}

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

  render() {
    console.log(this.state.data, 'nouvelles données');
    return ({
      this.state.data.title
    });
  };
}

var element = ;

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

Mais pour une raison inconnue, il semble que le composant enfant ne se met pas à jour lorsque l'état du parent change.

Est-ce que j'ai oublié quelque chose ici ?

252voto

free-soul Points 7949

Il y a deux problèmes avec votre code.

L'état initial de votre composant enfant est défini à partir des props.

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

En citant cette réponse de SO:

Passer l'état initial à un composant en tant que prop est une anti-pattern car la méthode getInitialState (dans notre cas le constructeur) n'est appelée que la première fois que le composant est rendu. Jamais plus. Cela signifie que, si vous re-rendez ce composant en passant une valeur différente en tant que prop, le composant ne réagira pas correctement, car le composant conservera l'état de la première fois qu'il a été rendu. C'est très propice aux erreurs.

Donc si vous ne pouvez pas éviter une telle situation, la solution idéale est d'utiliser la méthode componentWillReceiveProps pour écouter les nouvelles props.

Ajouter le code ci-dessous à votre composant enfant résoudra votre problème de re-rendering du composant enfant.

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

Le deuxième problème concerne le fetch.

_makeApiCall(endpoint) {
  fetch(endpoint)
    .then((response) => response.json())   // ----> vous avez raté cette partie
    .then((response) => this.setState({ response }));
}

Et voici un fiddle fonctionnel: https://jsfiddle.net/o8b04mLy/

1 votes

"Il est correct de initialiser l'état en fonction des props si vous savez ce que vous faites. Y a-t-il d'autres inconvénients à définir l'état via la prop, outre le fait que nextProp ne déclenchera pas un nouveau rendu sans componentWillReceiveProps(nextProps) ?"

14 votes

Tel que je le sais, il n'y a pas d'autres inconvénients. Mais dans votre cas, nous pourrions clairement éviter d'avoir un état à l'intérieur du composant enfant. Le parent peut simplement transmettre des données en tant que props, et quand le parent se re-rend avec son nouvel état, l'enfant se re-rendrait également (avec de nouvelles props). En fait, maintenir un état à l'intérieur de l'enfant est inutile ici. Composants purs FTW!

10 votes

Pour tout le monde qui lit dorénavant, vérifiez static getDerivedStateFromProps(nextProps, prevState) reactjs.org/docs/…

7voto

Si la solution ci-dessus n'a toujours pas résolu votre problème, je vous suggère de vérifier une fois comment vous changez l'état, si vous ne renvoyez pas un nouvel objet alors parfois React ne voit aucune différence entre le nouvel état précédent et l'état modifié, c'est une bonne pratique de toujours passer un nouvel objet lors du changement de l'état, en voyant le nouvel objet React re-rendra certainement tous les composants ayant accès à cet état modifié.

Par exemple :-

Ici, je vais changer une propriété d'un tableau d'objets dans mon état, regardez comment je propage toutes les données dans un nouvel objet. De plus, le code ci-dessous peut vous sembler un peu étrange, c'est une fonction de réduction de Redux MAIS ne vous inquiétez pas, c'est juste une méthode pour modifier l'état.

export const addItemToCart = (cartItems,cartItemToBeAdded) => {
        return cartItems.map(item => {
            if(item.id===existingItem.id){
                ++item.quantity;        
            }
            // Je pourrais simplement retourner l'élément mais à la place je le propage et retourne un nouvel objet
            return {...item} 
        })
    } 

Assurez-vous simplement de changer l'état avec un nouvel objet, même si vous apportez une modification mineure à l'état, propagez-la dans un nouvel objet puis retournez-la, cela déclenchera un rendu dans tous les endroits appropriés. J'espère que cela vous a aidé. Faites-moi savoir si j'ai fait une erreur quelque part :)

1voto

slorenzo Points 1753

Il y a quelques choses que vous devez modifier.

Quand fetch obtient la réponse, ce n'est pas un json. Je cherchais comment récupérer ce json et j'ai découvert ce lien.

D'autre part, vous devez savoir que la fonction constructor n'est appelée qu'une seule fois.

Donc, vous devez changer la manière dont vous récupérez les données dans le composant .

Je vous laisse ici un exemple de code : https://jsfiddle.net/emq1ztqj/

J'espère que cela vous aidera.

0 votes

Merci. Cependant, en regardant l'exemple que vous avez donné, il semble toujours que Child ne se met jamais à jour. Des suggestions sur la façon de modifier la façon dont Child reçoit les données?

2 votes

Êtes-vous sûr? Je vois que le composant récupère les nouvelles données de https://jsonplaceholder.typicode.com/posts/1 et se re-render.

1 votes

Oui, je le vois fonctionner maintenant sur OSX. iOS ne déclenchait pas le re-render dans le navigateur de l'application StackExchange.

0voto

ufukty Points 100

Réponse acceptée et componentWillReceiveProps

L'appel à componentWillReceiveProps dans la réponse acceptée est obsolète et sera supprimé de React avec la version 17 Docs React : UNSAFE_componentWillReceiveProps()

Utilisation de la logique d'état dérivé dans React

Comme le pointe la documentation React, l'utilisation de l'état dérivé (c'est-à-dire un composant reflétant un changement survenu dans ses props) peut rendre vos composants plus difficiles à comprendre et peut être un anti-pattern. Docs React : Vous n'avez probablement pas besoin d'état dérivé

Solution actuelle : getDerivedStateFromProps

Si vous choisissez d'utiliser un état dérivé, la solution actuelle consiste à utiliser l'appel à getDerivedStateFromProps comme l'a dit @DiogoSanto.

getDerivedStateFromProps est invoqué juste avant d'appeler la méthode render, à la fois lors du montage initial et des mises à jour ultérieures. Il doit renvoyer un objet pour mettre à jour l'état, ou null pour ne rien mettre à jour. Docs React : static getDerivedStateFromProps()

Comment utiliser componentWillReceiveProps

Cette méthode ne peut pas accéder aux propriétés d'instance. Elle décrit simplement à React comment calculer le nouvel état à partir des propriétés données. Chaque fois que les propriétés changent, React appellera cette méthode et utilisera l'objet renvoyé par cette méthode comme nouveau état.

class Enfant extends React.Component {
    constructor(props) {
        super(props);
        // rien n'a changé, attribuez l'état pour la 
        // première fois pour définir sa forme initiale. 
        // (cela fonctionnera sans cela, mais générera 
        // un avertissement)
        this.state = {
            data: props.data
        };
    }

    componentWillReceiveProps(props) {
        // renvoyer le nouvel état en tant qu'objet, ne pas appeler .setState()
        return { 
            data: props.data
        };
    }

    render() {
        // rien n'a changé, sera appelé après 
        // que componentWillReceiveProps ait renvoyé le nouvel état, 
        // chaque fois que les propriétés sont mises à jour.
        return ( 
            {this.state.data.title}
        );
    }
}

Attention

  • Effectuer un nouveau rendu d'un composant en fonction d'un changement survenu dans un composant parent peut être ennuyeux pour l'utilisateur en raison de la perte de la saisie de l'utilisateur sur ce composant.
  • La logique de l'état dérivé peut rendre les composants plus difficiles à comprendre, à penser. Utilisez judicieusement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X