Changer l'état lorsque les propriétés changent et montent d'abord sur la fonction React-Missing?

J'ai rencontré un problème sur les États basés sur les propriétés.

Le scénario

J'ai un parent de Composant qui crée passe une propriété à un composant enfant. Le composant enfant réagit en fonction de la propriété reçue. Dans React, le "seul" moyen approprié de changer l'état d'un composant est d'utiliser les fonctions componentWillMount ou componentDidMount et componentWillReceiveProps pour autant que j'ai vu (entre autres, mais concentrons-nous sur ceux-ci, car getInitialState est juste exécuté une fois).

Mon problème / Question

Si je reçois une nouvelle propriété du parent et que je veux changer l'état, seule la fonction componentWillReceiveProps sera exécutée et m'autorisera à exécuter setState. Render ne permet pas de setStatus.

Que faire si je veux définir l'état au début et l'heure à laquelle il reçoit une nouvelle propriété? Je dois donc le définir sur getInitialState ou componentWillMount / componentDidMount. Ensuite vous devez changer l'état selon les propriétés utilisant componentWillReceiveProps.

C'est un problème lorsque votre état dépend fortement de vos propriétés, ce qui est presque toujours le cas. Ce qui peut devenir stupide parce que vous devez répéter les états que vous voulez mettre à jour en fonction de la nouvelle propriété.

Ma solution

J'ai créé une nouvelle méthode appelée sur componentWillMount et sur componentWillReceiveProps. Je n'ai trouvé aucune méthode appelée après qu'une propriété ait été mise à jour auparavant rendu et aussi la première fois que le Composant est monté. Ensuite, il n'y aurait pas besoin de faire cette solution de contournement stupide.

Quoi qu'il en soit, voici la question: n'y a-t-il pas de meilleure option pour mettre à jour l'état lorsqu'une nouvelle propriété est reçue ou modifiée?

/*...*/
/**
 * To be called before mounted and before updating props
 * @param props
 */
prepareComponentState: function (props) {
    var usedProps = props || this.props;

    //set data on state/template
    var currentResponses = this.state.candidatesResponses.filter(function (elem) {
        return elem.questionId === usedProps.currentQuestion.id;
    });
    this.setState({
        currentResponses: currentResponses,
        activeAnswer: null
    });
},
componentWillMount: function () {
    this.prepareComponentState();
},
componentWillReceiveProps: function (nextProps) {
    this.prepareComponentState(nextProps);
},
/*...*/

Je me sens un peu stupide, je suppose que je perds quelque chose... Je suppose qu'il y a une autre solution pour résoudre ce problème.

Et oui, je sais déjà ce: https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html

25
demandé sur al8anp 2015-04-01 21:01:49

1 réponses

J'ai trouvé que ce modèle n'est généralement pas très nécessaire. dans le cas général (pas toujours), j'ai trouvé que définir l'état basé sur les propriétés modifiées est un peu un anti-pattern; à la place, dérivez simplement l'état local nécessaire au moment du rendu.

render: function() {
  var currentResponses = this.state.candidatesResponses.filter(function (elem) {
    return elem.questionId === this.props.currentQuestion.id;
  });

  return ...; // use currentResponses instead of this.state.currentResponses
}

Cependant, dans certains cas, il peut être logique de mettre en cache ces données (par exemple, peut-être que le calcul est prohibitif), ou vous avez juste besoin de savoir quand les accessoires sont définis/modifiés pour une autre raison. Dans ce cas, j'utiliserais essentiellement le modèle que vous avez écrit dans votre question.

Si vous vraiment n'aimez pas le taper, vous pouvez formaliser cette nouvelle méthode en tant que mixin. Par exemple:

var PropsSetOrChangeMixin = {
  componentWillMount: function() {
    this.onPropsSetOrChange(this.props);
  },

  componentWillReceiveProps: function(nextProps) {
    this.onPropsSetOrChange(nextProps);
  }
};

React.createClass({
  mixins: [PropsSetOrChangeMixin],

  onPropsSetOrChange: function(props) {
    var currentResponses = this.state.candidatesResponses.filter(function (elem) {
        return elem.questionId === props.currentQuestion.id;
    });

    this.setState({
      currentResponses: currentResponses,
      activeAnswer: null
    });
  },

  // ...
});

Bien sûr, si vous utilisez des composants React basés sur class, vous devez trouver une solution alternative (par exemple, l'héritage ou les mixins js personnalisés) car ils n'obtiennent pas de mixins de style React pour le moment.

(pour ce que ça vaut, je pense que le code est beaucoup plus clair en utilisant l'explicite méthodes; Je l'écrirais probablement comme ceci:)

componentWillMount: function () {
  this.prepareComponentState(this.props);
},

componentWillReceiveProps: function (nextProps) {
  this.prepareComponentState(nextProps);
},

prepareComponentState: function (props) {
  //set data on state/template
  var currentResponses = this.state.candidatesResponses.filter(function (elem) {
    return elem.questionId === props.currentQuestion.id;
  });
  this.setState({
    currentResponses: currentResponses,
    activeAnswer: null
  });
},
22
répondu Michelle Tilley 2015-04-01 18:36:26