Utiliser l'état ou les références dans les composants de formulaire React.js?

116

Je commence par React.js et je veux faire un formulaire simple mais dans la documentation j'ai trouvé deux façons de le faire.

Le premier utilise des références :

var CommentForm = React.createClass({
  handleSubmit: function(e) {
    e.preventDefault();
    var author = React.findDOMNode(this.refs.author).value.trim();
    var text = React.findDOMNode(this.refs.text).value.trim();
    if (!text || !author) {
      return;
    }
    // TODO: send request to the server
    React.findDOMNode(this.refs.author).value = '';
    React.findDOMNode(this.refs.text).value = '';
    return;
  },
  render: function() {
    return (
      <form className="commentForm" onSubmit={this.handleSubmit}>
        <input type="text" placeholder="Your name" ref="author" />
        <input type="text" placeholder="Say something..." ref="text" />
        <input type="submit" value="Post" />
      </form>
    );
  }
});

Et le second utilise l' état dans le composant React:

var TodoTextInput = React.createClass({
  getInitialState: function() {
    return {
      value: this.props.value || ''
    };
  },

  render: function() /*object*/ {
    return (
      <input className={this.props.className}
      id={this.props.id}
      placeholder={this.props.placeholder}
      onBlur={this._save}
      value={this.state.value}
      />
    );
  },

  _save: function() {
    this.props.onSave(this.state.value);
    this.setState({value: ''
  });
});

Je ne vois pas les avantages et les inconvénients des deux alternatives, s'il en existe. Merci.

Gabrielgiussi
la source
Est-ce que j'ai râté quelque chose? Pourquoi n'utilisez-vous pas l'objet événement pour obtenir les valeurs du formulaire? Cela semble être la seule raison d'utiliser un formulaire ici en premier lieu. Si vous n'utilisez pas le comportement de soumission par défaut et que vous avez des références sur les entrées, vous n'avez pas besoin de les envelopper dans un formulaire.
NectarSoft

Réponses:

143

La version courte: évitez les réf.


Ils sont mauvais pour la maintenabilité et perdent beaucoup de la simplicité du rendu du modèle WYSIWYG.

Vous avez un formulaire. Vous devez ajouter un bouton qui réinitialise le formulaire.

  • réfs:
    • manipuler le DOM
    • render décrit à quoi ressemblait le formulaire il y a 3 minutes
  • Etat
    • setState
    • render décrit l'apparence du formulaire

Vous avez un champ de numéro CCV dans une entrée et certains autres champs dans votre application qui sont des nombres. Vous devez maintenant forcer l'utilisateur à entrer uniquement des chiffres.

  • réfs:
    • ajouter un gestionnaire onChange (n'utilisons-nous pas des refs pour éviter cela?)
    • manipuler dom dans onChange si ce n'est pas un nombre
  • Etat
    • vous avez déjà un gestionnaire onChange
    • ajouter une instruction if, si elle est invalide, ne rien faire
    • render n'est appelé que s'il va produire un résultat différent

Eh, tant pis, le PM veut que nous fassions juste une boîte rouge si elle est invalide.

  • réfs:
    • make onChange handler appelle simplement forceUpdate ou quelque chose?
    • faire une sortie de rendu basée sur ... hein?
    • où obtenons-nous la valeur à valider dans le rendu?
    • manipuler manuellement la propriété className dom d'un élément?
    • je suis perdu
    • réécrire sans refs?
    • lire à partir du dom dans le rendu si nous sommes montés sinon supposer valide?
  • Etat:
    • supprimer l'instruction if
    • faire valider le rendu basé sur this.state

Nous devons redonner le contrôle au parent. Les données sont maintenant dans les accessoires et nous devons réagir aux changements.

  • réfs:
    • implémenter componentDidMount, componentWillUpdate et componentDidUpdate
    • diffèrent manuellement les accessoires précédents
    • manipuler le dom avec l'ensemble minimal de changements
    • Hey! nous mettons en œuvre react in react ...
    • il y a plus, mais mes doigts me font mal
  • Etat:
    • sed -e 's/this.state/this.props/' 's/handleChange/onChange/' -i form.js

Les gens pensent que les arbitres sont «plus faciles» que de les garder en état. Cela peut être vrai pendant les 20 premières minutes, ce n'est pas vrai d'après mon expérience. Mettez-vous en position de dire "Ouais, je vais le faire dans 5 minutes" plutôt que "Bien sûr, je vais juste réécrire quelques composants".

Brigand
la source
3
Pourriez-vous expliquer un peu plus sur sed -e / this.state / this.props / '' s / handleChange / onChange / '-i form.js?
gabrielgiussi
1
Non, je veux dire des changements réels au dom. React.findDOMNode(this.refs.foo). Si vous changez par exemple, this.refs.foo.props.barrien ne se passera.
Brigand
1
Par exemple, si vous l'avez <input onChange={this.handleChange} value={this.state.foo} />changé en <input onChange={this.props.handleChange} value={this.props.foo} />, ou modifiez votre (vos) fonction (s) handleChange pour appeler le (s) rappel (s) dans les props. Quoi qu'il en soit, ce sont quelques petits changements évidents.
Brigand
4
Je ne sais pas si je suis le seul à trouver votre réponse un peu déroutante. Pourriez-vous montrer des exemples de code pour clarifier vos points?
Rishabh le
2
Avoir plus de 50 entrées sur un écran et un rendu de chaque changement d'état n'est pas souhaitable. Il inputest idéal de regrouper chaque champ dans lequel chacun conserve son propre état. À un moment donné, nous devons réconcilier ces divers états indépendants avec un modèle plus large. Peut-être que nous avons une sauvegarde automatique sur une minuterie, ou nous économisons simplement sur componentWillUnmountC'est là que je trouve l' refsidéal, lors de la réconciliation, nous prélevons la statevaleur de chacun ref, et personne n'est le plus sage. Je suis d'accord dans la plupart des cas, statec'est la réponse, mais avec un grand nombre de inputs, l'utilisation d'un refsmodèle approprié est une aubaine pour la performance
lux
105

J'ai vu quelques personnes citer la réponse ci-dessus comme une raison de "ne jamais utiliser les références" et je veux donner mon opinion (ainsi que quelques autres développeurs React à qui j'ai parlé).

Le sentiment «ne pas utiliser les références» est correct quand on parle de les utiliser pour les instances de composant. Cela signifie que vous ne devriez pas utiliser les refs pour récupérer des instances de composants et appeler des méthodes dessus. C'est la mauvaise façon d'utiliser les refs et c'est quand les arbitres vont rapidement vers le sud.

La manière correcte (et très utile) d'utiliser les références est lorsque vous les utilisez pour obtenir une valeur du DOM. Par exemple, si vous avez un champ d'entrée attachant une référence à cette entrée, saisir la valeur ultérieurement via la référence est très bien. Sans cela, vous devez passer par un processus assez orchestré pour maintenir votre champ de saisie à jour avec votre état local ou votre magasin de flux - ce qui semble inutile.

Edit 2019: Bonjour les amis du futur. En plus de ce que j'ai mentionné il y a quelques années ^, avec React Hooks, les refs sont également un excellent moyen de garder une trace des données entre les rendus et ne se limitent pas à récupérer les nœuds DOM.

Tyler McGinnis
la source
3
Votre dernier paragraphe est parfaitement logique, mais pouvez-vous clarifier votre deuxième paragraphe? Quel est un exemple concret de capture d'une instance de composant et d'appel d'une méthode qui serait considérée comme incorrecte?
Danny Libin
2
Je suis d'accord avec ça. J'utilise des refs sauf si / jusqu'à ce que je doive faire la validation ou la manipulation de la valeur d'un champ. Si j'ai besoin de valider le changement ou de changer les valeurs par programme, j'utilise l'état.
Christopher Davies
1
Je suis d'accord avec cela également. Lors d'une phase de découverte, j'ai délibérément abordé un écran avec un grand nombre d'entrées avec naïveté. Toutes les valeurs d'entrée stockées dans une carte (dans l'état) indexées par id. Inutile de dire que les performances ont souffert depuis la définition de l'état et le rendu de plus de 50 entrées (certaines interfaces matérielles, qui étaient lourdes!) Sur des modifications mineures de l'interface utilisateur en cliquant sur une case à cocher n'étaient pas idéales. La composante de chaque entrée qui peut maintenir son propre état semblait la bonne approche. Si une réconciliation est nécessaire, regardez simplement dans refset obtenez la valeur de l'état. Cela semble être un très joli motif en fait.
lux
2
Je suis complètement d'accord. La réponse acceptée est trop vague à mon avis.
James Wright
Je suis d'accord. Lors de la conception d'un composant de formulaire général, cela met en lumière les points faibles des composants contrôlés et la gestion de la mise au point, la gestion des erreurs, etc. Pas possible d'avoir une architecture propre en fait. Parlez-moi si nécessaire. Je déplace mes composants vers des refs.
kushalvm le
6

TL; DR D'une manière générale, refsallez à l'encontre de la philosophie déclarative de React , vous devriez donc les utiliser en dernier recours. Utilisez state / propschaque fois que possible.


Pour comprendre où vous utilisez refsvs state / props, examinons certains des principes de conception suivis par React.

Documentation Per React surrefs

Évitez d'utiliser des refs pour tout ce qui peut être fait de manière déclarative.

Conformément aux principes de conception de React concernant les trappes d'échappement

Si un modèle utile pour créer des applications est difficile à exprimer de manière déclarative, nous fournirons une API impérative pour cela. (et ils sont liés aux références ici)

Ce qui signifie que l'équipe de React suggère d'éviter refset d'utiliser state / propspour tout ce qui peut être fait de manière réactive / déclarative.

@Tyler McGinnis a fourni une très bonne réponse , affirmant également que

La manière correcte (et très utile) d'utiliser les refs est lorsque vous les utilisez pour obtenir de la valeur du DOM ...

Bien que vous puissiez faire cela, vous travaillerez contre la philosophie de React. Si vous avez de la valeur dans une entrée, elle vient certainement de state / props. Pour que le code reste cohérent et prévisible, vous devez vous y tenir state / propségalement. Je reconnais que cela refsvous donne parfois la solution la plus rapide, donc si vous faites une preuve de concept, rapide et sale est acceptable.

Cela nous laisse avec plusieurs cas d'utilisation concrets pourrefs

Gestion de la mise au point, de la sélection de texte ou de la lecture multimédia. Déclencher des animations impératives. Intégration avec des bibliothèques DOM tierces.

Lyubomir
la source
5

Ce message est vieux.

Je vais partager ma petite expérience sur un cas à ce sujet.

Je travaillais sur un gros composant (414 lignes) avec beaucoup d'entrées «dynamiques» et beaucoup de données en cache impliquées. (Je ne travaille pas seul sur la page, et mes sens me disent que la structure du code pourrait probablement être mieux divisée, mais ce n'est pas le but (enfin, ça pourrait l'être mais je m'en occupe)

J'ai d'abord travaillé avec state pour gérer les valeurs des entrées:

  const [inputsValues, setInputsValues] = useState([])
  const setInputValue = (id, value) => {
    const arr = [...inputsValues]
    arr[id] = value
    setInputsValues(arr)
  }

et bien sûr dans les entrées:

value={inputsValues[id] || ''}
onChange={event => setInputValue(id, event.target.value)}

Le rendu était si lourd que le changement d'entrée était saccadé comme **** (n'essayez pas de maintenir la touche enfoncée, le texte n'apparaîtra qu'après une pause)

J'étais sûr que je pourrais éviter cela en utilisant des références.

fini comme ça:

  const inputsRef = useRef([])

et dans les entrées:

ref={input => (inputsRef.current[id] = input)}

[bien dans mon cas, l'entrée était Material-UI TextField donc c'était:

inputRef={input => (inputsRef.current[id] = input)}

]

Grâce à cela, il n'y a pas de rendu, l'entrée est fluide, la fonctionnalité fonctionne de la même manière. Cela permettra d'économiser des cycles et des calculs, donc de l'énergie aussi. Faites-le pour la terre x)

Ma conclusion: useRef pour la valeur des entrées peut même être nécessaire.

Kaphar
la source