42 votes

Quand utiliser la méthode de cycle de vie componentWillReceiveProps ?

Je suis nouveau dans React/Redux et j'ai un problème avec l'état.

TrajectContainer.jsx

class TrajectContainer extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            trajects: props.trajects,
            onClick: props.onClick
        };
    }

    componentWillReceiveProps(nextProps) {
        console.log('componentWillReceiveProps', nextProps);
        this.setState(nextProps);
    }

    render() {
        // When the componentWillReceiveProps is not present, the this.state will hold the old state
        console.log('rerender', this.state);
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.state.onClick}>Add new Traject</button>
            {this.state.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
        </div>)
    }
}

const mapStateToProps = function (store) {
    console.log('mapToStateProps', store);
    return {
        trajects: store.trajects
    };
};

const mapDispatchToProps = function (dispatch, ownProps) {
    return {
        onClick: function () {
            dispatch(addTraject());
        }
    }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer);

Lorsqu'un reducer renvoie un nouvel état, le composant sera rendu avec les nouvelles données.

Cependant : si je supprime la fonction componentWillReceiveProps, la fonction render() conserve l'ancien état.

J'ai vérifié les données reçues dans mapStateToProps, et il s'agit du nouveau New State. Je ne comprends donc pas pourquoi j'ai besoin de la fonction componentWillReceiveProps pour que la fonction de rendu reçoive les nouvelles données.

Est-ce que je fais quelque chose de mal ?

1 votes

Vous n'avez pas besoin state pour gérer ces changements. trajects appartient à la fois state et props (provenant de l'application redux connect ). De même, le fait de définir un état sur chaque componentWillReceiveProps n'est pas une bonne pratique. Je supprimerais trajects de l'État et ne travaillent qu'avec des accessoires entrants. Ils seront modifiés chaque fois que nécessaire et vous aurez toujours les valeurs les plus récentes.

1 votes

@HemersonCarlin C'est ce que je veux oui, mais l'état dans render() n'est pas l'état à jour transmis par mapStateToProps, c'est mon problème. Je n'obtiens le nouvel état que lorsque j'utilise .setState, c'est pourquoi j'ai posé cette question à StackOverflow !

0 votes

Essayez de supprimer l'état et utilisez this.props.trajects à la place.

72voto

Mayank Shukla Points 39317

componentWillReceiveProps est nécessaire si vous voulez mettre à jour les valeurs de l'état avec les nouvelles valeurs des accessoires, cette méthode sera appelée chaque fois qu'un changement se produit dans les valeurs des accessoires.


Dans votre cas, pourquoi avez-vous besoin de la méthode componentWillReceiveProps ?

Parce que vous stockez les valeurs des accessoires dans une variable d'état, et vous l'utilisez comme ceci :

this.state.KeyName

C'est pourquoi vous avez besoin componentWillReceiveProps pour mettre à jour la valeur d'état avec la nouvelle valeur de props, seules les valeurs de props du composant seront mises à jour mais automatiquement l'état ne sera pas mis à jour . Si vous ne mettez pas à jour l'état, alors this.state aura toujours les données initiales.

componentWillReceiveProps ne sera pas nécessaire si vous ne stockez pas les valeurs des props dans l'état et les utilisez directement :

this.props.keyName

Maintenant, react utilisera toujours les valeurs mises à jour des props dans la méthode de rendu, et si un changement se produit dans les props, il rendra à nouveau le composant avec les nouveaux props.

Conformément à DOC :

componentWillReceiveProps() est invoquée avant qu'un composant monté reçoive de nouveaux props. Si vous avez besoin de mettre à jour l'état en réponse aux aux changements d'accessoires (par exemple, pour le réinitialiser), vous pouvez comparer this.props et nextProps et effectuer des transitions d'état en utilisant this.setState() dans cette méthode.

React n'appelle pas componentWillReceiveProps avec les props initiaux lors du montage. Il n'appelle cette méthode que si certains des props du composant peuvent être être mis à jour.

Suggestion :

Ne pas stocker les valeurs des props dans l'état, utiliser directement this.props et créer les composants de l'interface utilisateur.

16 votes

C'est une excellente réponse ! Cependant, toute personne qui voit cette réponse doit savoir qu'à partir de maintenant, la méthode "componentWillReceiveProps()" a été dépréciée. Vous devez utiliser "componentDidUpdate(prevProps, prevState, snapshot)". Voir ici pour plus d'explications : reactjs.org/docs/react-component.html#componentdidupdate

14voto

user1095118 Points 1545

Mise à jour

componentDidUpdate()

doit maintenant être utilisé plutôt que componentWillReceiveProps

voir aussi un article de gaearon réécriture de composants résilients

Il y a deux problèmes potentiels ici

  1. Ne réaffectez pas vos props à l'état, c'est ce que vous utilisez avec redux pour extraire les valeurs du magasin et les renvoyer comme props à votre composant.

Éviter l'état signifie que vous n'avez plus besoin de votre constructeur ou des méthodes du cycle de vie. Ainsi, votre composant peut être écrit comme un composant fonctionnel sans état ; il y a des avantages en termes de performance à écrire votre composant de cette façon.

  1. Vous n'avez pas besoin d'envelopper votre action dans le dispatch si vous passez mapDispatcahToProps. Si un objet est passé, chaque fonction qu'il contient est supposée être un créateur d'action. Un objet avec les mêmes noms de fonctions, mais avec chaque créateur d'action enveloppé dans un dispatch sera retourné.

Voici un extrait de code qui supprime l'état de votre composant et s'appuie sur l'état renvoyé par le magasin redux.

import React from "react";
import { connect } from "react-redux";

const TrajectContainer = ({ trajects, addTraject }) => (
    <div className="col-md-6">
        <h2>Trajects</h2>
        <button className="btn btn-primary" onClick={addTraject}>Add new Traject</button>
        {trajects.map(traject => <Traject traject={traject} key={traject.name} />)}
    </div>
);

const mapStateToProps = ({ trajects }) => ({ trajects });

export default connect( mapStateToProps, { addTraject })(TrajectContainer);

9voto

Panther Points 4678

Dans votre cas, vous aurez besoin componentWillReceiveProps et vous devez mettre à jour l'état lorsque vous recevez de nouveaux accessoires. Parce que

  • Dans votre constructeur, vous avez déclaré votre état comme ci-dessous. Comme vous pouvez le voir, vous construisez votre state en utilisant les props qui lui sont passés. (C'est pourquoi vous avez besoin de componentWillReceiveProps et la logique pour le mettre à jour à cet endroit)

    this.state = {
        trajects: props.trajects,
        onClick: props.onClick
    };
  • Donc quand vos accessoires, vos changements componentWillReceiveProps est la fonction qui est appelée. Le constructeur n'est pas appelé. Vous devez donc redéfinir l'état pour que les changements soient pris en compte dans l'état du composant.

  • Cependant, votre logique devrait être la suivante. Avec une condition afin d'éviter les mises à jour répétées de l'état si elle est appelée plusieurs fois.

    componentWillReceiveProps(nextProps) {
     console.log('componentWillReceiveProps', nextProps);
     if (this.props !== nextProps) {
      this.setState(nextProps);
     }
    }

Il ne faut stocker les props dans l'état que si l'on a l'intention d'en modifier le contenu. Mais dans votre cas, je vois qu'il n'y a pas de modification. Vous pouvez donc utiliser directement this.props.trajects directement au lieu de le stocker dans l'état et de l'utiliser ensuite. De cette façon, vous pouvez vous débarrasser de la componentWillReceiveProps Ainsi, votre fonction de rendu utilisera quelque chose comme ci-dessous

{this.props.trajects.map(traject => //what ever is your code.... }

1 votes

Merci Panther, votre explication est vraiment claire ! Je prévois d'étendre le composant et de permettre la modification de certains props, je vais donc utiliser votre exemple !

3voto

J'ai eu le même problème en ajoutant withRouter() comme ça :

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));

1voto

Le problème avec votre implémentation est que vous dupliquez l'état de votre magasin Redux (provenant des props) dans votre état React (this.state).

Dans votre exemple, si store.trajects est mis à jour, alors this.props.traject sera mis à jour et un rendu sera déclenché uniquement si this.props.traject est utilisé dans votre méthode de rendu (ce n'est pas le cas).

Étant donné que vous utilisez l'état au lieu de la prop dans votre méthode de rendu, vous devez changer manuellement l'état de votre composant en utilisant la fonction this.setState pour déclencher un rendu.

Ce n'est pas un bon modèle : Je conseillerais de ne pas utiliser l'état, et d'utiliser directement les props comme ceci :

class TrajectContainer extends React.Component {
    render() {
        return (<div className="col-md-6">
            <h2>Trajects</h2>
            <button className="btn btn-primary" onClick={this.props.onClick}>Add new Traject</button>
        {this.props.trajects.map(traject => <Traject traject={traject} key={traject.name}/>)}
    </div>)
}
}
const mapStateToProps = function (store) {
  console.log('mapToStateProps', store);
  return {
    trajects: store.trajects
  };
};

const mapDispatchToProps = function (dispatch, ownProps) {
   return {
      onClick: function () {
        dispatch(addTraject());
      }
  }
};

export default connect(mapStateToProps, mapDispatchToProps)(TrajectContainer)

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