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?
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.
J'avais un problème similaire, ajoutez withRouter()
comme ceci:
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(TrajectContainer));
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)
Il y a deux problèmes potentiels ici
É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.
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);
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 componentWillReceiveProps
So votre fonction de rendu utilisera quelque chose comme ci-dessous
{this.props.trajects.map(traject => //what ever is your code.... }