web-dev-qa-db-fra.com

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

Je suis nouveau sur React/Redux et j'ai un problème d'é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 réducteur retourne un nouvel état, le composant sera rendu avec les nouvelles données.

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

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

Est-ce que je fais quelque chose de mal?

8
Mazzy

componentWillReceiveProps est requis si vous souhaitez mettre à jour les valeurs d'état avec de nouvelles valeurs props, cette méthode est appelée chaque fois que des modifications sont apportées aux valeurs props.


Dans votre cas, pourquoi avez-vous besoin de cette méthode ComponentWillReceiveProps?

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

this.state.KeyName

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

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

this.props.keyName

Maintenant, react utilisera toujours les valeurs d'accessoires mises à jour dans la méthode de rendu, et si des modifications devaient être apportées aux accessoires, elle rendra à nouveau le composant avec de nouveaux accessoires.

Selon DOC:

composantWillReceiveProps () est appelé avant un composant monté reçoit de nouveaux accessoires. Si vous devez mettre à jour l'état en réponse à prop change (par exemple, pour le réinitialiser), vous pouvez comparer this.props et nextProps et effectuer des transitions d'état à l'aide de this.setState () dans cette méthode. 

React n'appelle pas composantWillReceiveProps avec les accessoires initiaux pendant montage. Il n’appelle cette méthode que si certains accessoires du composant le peuvent mettre à jour.

Suggestion:

Ne stockez pas les valeurs d'accessoires dans l'état, utilisez directement this.props et créez les composants de l'interface utilisateur.

23
Mayank Shukla

J'avais un problème similaire, ajoutez withRouter() comme ceci:

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));
2
Elnur Ibrahim-zade

Le problème avec votre implémentation est que vous dupliquez l’état de votre magasin Redux (venant des accessoires) 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 ne sera déclenché que si this.props.traject est utilisé dans votre méthode de rendu (pas le cas).

Puisque vous utilisez l'état au lieu de prop dans votre méthode de rendu, vous devez modifier manuellement l'état de votre composant à l'aide de 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 accessoires 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)
1

Il y a deux problèmes potentiels ici

  1. Ne réattribuez pas vos accessoires pour indiquer que c’est ce que vous utilisez redux pour extraire les valeurs du magasin et les renvoyer en tant qu’accessoires à votre composant.

Éviter les états signifie que vous n’avez plus besoin de votre constructeur ni de vos méthodes de cycle de vie. Pour que votre composant puisse être écrit en tant que composant fonctionnel sans état, l'écriture de votre composant présente des avantages en termes de performances.

  1. Vous n'avez pas besoin de conclure votre action si vous passez mapDispatcahToProps. Si un objet est passé, chaque fonction à l'intérieur de celui-ci est supposée être un créateur d'action. Un objet avec les mêmes noms de fonction, mais avec chaque action créée par le créateur, sera renvoyé

Vous trouverez ci-dessous 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);

1
user1095118

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

  • Dans votre constructeur, vous avez déclaré votre état comme suit. Comme vous pouvez le voir, vous construisez votre state en utilisant les accessoires transmis. (C'est pourquoi vous avez besoin de componentWillReceiveProps et de la logique pour le mettre à jour à cet endroit)

    this.state = {
        trajects: props.trajects,
        onClick: props.onClick
    };
    
  • Ainsi, lorsque vos accessoires changent, componentWillReceiveProps est la fonction appelée. Le constructeur ne s'appelle pas. Vous devez donc redéfinir l'état de sorte que les modifications entrent dans l'état du composant.

  • Cependant, votre logique devrait être comme ci-dessous. Avec une condition telle que vous puissiez empêcher des mises à jour d'état répétées si elle est appelée plusieurs fois.

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

On devrait stocker les accessoires dans l’état, seulement si vous voulez en modifier le contenu. Mais dans votre cas, je vois qu’il n’ya pas de modification. Ainsi, vous pouvez directement utiliser this.props.trajects directement au lieu de le stocker dans l'état, puis de l'utiliser. De cette façon, vous pouvez vous débarrasser de la componentWillReceivePropsSo votre fonction de rendu utilisera quelque chose comme ci-dessous

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