Méthodes d'appel sur les composants React children

je veux écrire un composant de formulaire qui peut exporter une méthode pour valider ses enfants. Malheureusement, une forme ne "voit" aucune méthode sur ses enfants.

Voici comment je définis un enfant potentiel de la forme:

var Input = React.createClass({
  validate: function() {
    ...
  },
});

et voici comment je définit la classe de forme:

var Form = React.createClass({
  isValid: function() {
    var valid = true;
    this.props.children.forEach(function(component) {
      // --> This iterates over all children that I pass
      if (typeof component.validate === 'function') {
        // --> code never reaches this point
        component.validate();
        valid = valid && component.isValid();
      }
    });
    return valid;
  }
});

j'ai remarqué que je peux appeler une méthode sur un composant enfant en utilisant des références, mais je ne peux pas appeler une méthode via des accessoires.enfant.

y a-t-il une raison à cette réaction?

Comment puis-je réparer ça?

12
demandé sur Ivan Mushketyk 2015-11-03 00:56:54

2 réponses

la raison technique est qu'au moment où vous essayez d'accéder au composant enfant, il n'existe pas encore vraiment (dans le DOM). Ils ne sont pas encore montées. Ils ont été passés à votre composant <Form> en tant que prop constructeur ou méthode en tant que react classe . (d'où la classe de nom dans React.createClass() ).

comme vous le faites remarquer, ceci peut être contourné en utilisant des références, mais je ne le recommande pas. Dans de nombreux cas, les arbitres ont tendance à être les raccourcis pour quelque chose qui réagit n'était pas prévu, et devraient donc être évités.

c'est probablement par dessein que react rend difficile/ impossible pour les parents d'accéder aux méthodes d'un enfant. Ils ne sont pas censé le faire. Les méthodes de l'enfant devraient être dans l'enfant si elles sont privées à l'enfant: elles font quelque chose à l'intérieur de l'enfant qui ne devrait pas être communiqué directement vers le haut au parent. Si tel était le cas, la manipulation aurait dû être effectuée à l'intérieur de la parent. Parce que le parent a au moins toutes les informations et données que l'enfant a.

maintenant, dans votre cas, j'imagine que chaque composant input (child) doit avoir une méthode de validation spécifique, qui vérifie la valeur d'entrée, et basée sur le résultat, fait une sorte de feedback de message d'erreur. Disons un contour rouge autour des mauvais champs.

de la manière réactive, cela pourrait être réalisé comme suit:

  • l'élément <Form> a état, qui comprend un booléen runValidation .
  • dès que runValidation est défini à true, à l'intérieur d'un setState( { runValidation: true }); réagir automatiquement rend tous les enfants.
  • si vous incluez runValidation comme accessoire pour tous les enfants.
  • alors chaque enfant peut vérifier à l'intérieur de leur fonction render() quelque chose comme if (this.props.runValidation) { this.validate() }
  • qui exécutera la fonction validate() dans l'enfant
  • la fonction de validation peut même utiliser l'état de l'enfant (l'état n'est pas modifié lorsque de nouveaux accessoires apparaissent), et l'utiliser pour le message de validation (par exemple,` please add more complicated symbols to your password')

maintenant, ce qui n'est pas encore réglé, c'est que vous pouvez vouloir faire une vérification au niveau du formulaire après que tous les enfants se sont validés eux-mêmes: par exemple, quand tous les enfants sont OK, soumettre le formulaire.

pour résoudre cela, vous pouvez appliquer la refs raccourcit la vérification finale et soumet. Et implémentez une méthode dans votre <Form> à l'intérieur d'une fonction componentDidUpdate() , pour vérifier si chaque enfant est OK (par exemple a une bordure verte) et si soumettre est cliqué, puis soumettre. Mais en règle générale, je recommande fortement contre l'utilisation de références.

pour la validation finale du formulaire, une meilleure approche est:

  • ajouter une variable non étatique à l'intérieur de votre <Form> qui contient des booléens pour chaque enfant. NB, il doit être non-étatique, pour empêcher les enfants de déclencher un nouveau cycle de rendu.
  • passer une fonction validateForm comme un accessoire (de rappel) à chaque enfant.
  • à l'intérieur validate() dans chaque enfant, appelez le this.props.validateForm(someChildID) qui met à jour le correspondant boolean dans la variable dans la Forme.
  • à la fin de la fonction validateForm dans le formulaire, vérifier si tous les booléens sont vrais, et si oui, soumettre le formulaire (ou modifier l'état du formulaire ou quoi.)

pour une solution encore plus longue (et beaucoup plus compliquée) pour former la validation dans react (avec flux) vous pouvez cocher cet article .

16
répondu wintvelt 2017-08-15 08:21:43

Je ne suis pas sûr que je manque quelque chose, mais après avoir essayé ce que @wintvelt a suggéré, j'ai rencontré un problème chaque fois que j'ai appelé la méthode runValidation à l'intérieur de la méthode de rendu de React, puisque dans mon cas runValidation change l'état en appelant setState en elle, déclenchant ainsi la méthode de rendu qui est évidemment une mauvaise pratique puisque la méthode de rendu doit être pure, et si je mets le runValidation dans willReceiveProps il ne sera pas appelé la première fois parce que la condition if est pas encore vrai (cette condition est changée dans le composant parent en utilisant setState , mais dans le premier appel de willReceiveProps c'est encore faux).

1
répondu Elias Ghali 2017-07-20 11:23:03