Réagir – la bonne façon de transmettre l’état des éléments de formulaire aux éléments frères ou parents?

  • Supposons que j’ai une classe React P, qui rend deux classes enfant, C1 et C2.
  • C1 contient un champ de saisie. Je ferai référence à ce champ d’entrée comme Foo.
  • Mon objective est de laisser C2 réagir aux changements de Foo.

Je suis venu avec deux solutions, mais aucune des deux ne se sent bien.

Première solution:

  1. Affectez P à un état, state.input .
  2. Créez une fonction onChange dans P, qui state.input un événement et définit state.input .
  3. Passez ceci onChange à C1 en tant que props et laissez C1 lier this.props.onChange à this.props.onChange of Foo.

Cela marche. Chaque fois que la valeur de Foo change, cela déclenche un setState dans P, donc P aura l’entrée pour passer à C2.

Mais pour la même raison, cela ne semble pas correct: je définis l’état d’un élément parent à partir d’un élément enfant. Cela semble trahir le principe de conception de React: stream de données à une seule direction.
Est-ce ainsi que je suis censé le faire ou existe-t-il une solution plus naturelle?

Deuxième solution:

Il suffit de mettre Foo dans P.

Mais est-ce un principe de conception que je devrais suivre lorsque je structurerai mon application – en mettant tous les éléments de forme dans le render de la classe de plus haut niveau?

Comme dans mon exemple, si j’ai un grand rendu de C1, je ne veux vraiment pas que le render entier de C1 soit render de P simplement parce que C1 a un élément de formulaire.

Comment devrais-je le faire?

Donc, si je vous comprends bien, votre première solution consiste à suggérer de conserver l’état de votre composant racine? Je ne peux pas parler pour les créateurs de React, mais en général, je trouve que c’est une solution appropriée.

Maintenir l’état est l’une des raisons (au moins je pense) que React a été créé. Si vous avez déjà implémenté votre propre côté client de modèle d’état pour gérer une interface utilisateur dynamic comportant de nombreuses pièces mobiles interdépendantes, alors vous allez adorer React, car il atténue une grande partie de la douleur liée à la gestion des états.

En maintenant l’état plus haut dans la hiérarchie et en le mettant à jour par événement, votre stream de données est toujours unidirectionnel, vous ne faites que répondre aux événements du composant Root, vous n’obtenez pas vraiment les données via une liaison bidirectionnelle, Vous dites au composant Racine que “hé, quelque chose s’est passé ici, consultez les valeurs” ou que vous transmettez l’état de certaines données du composant enfant afin de mettre à jour l’état. Vous avez modifié l’état dans C1 et vous souhaitez que C2 en soit conscient. En mettant à jour l’état dans le composant Racine et en effectuant un nouveau rendu, les accessoires de C2 sont maintenant synchronisés, l’état étant mis à jour dans le composant Racine. .

 class Example extends React.Component { constructor (props) { super(props) this.state = { data: 'test' } } render () { return ( 
) } onUpdate (data) { this.setState({ data }) } } class C1 extends React.Component { render () { return (
) } update () { this.props.onUpdate(this.refs.myInput.getDOMNode().value) } }) class C2 extends React.Component { render () { return
{this.props.data}
} }) ReactDOM.renderComponent(, document.body)

Ayant utilisé React pour construire une application maintenant, j’aimerais partager quelques reflections sur cette question que j’ai posée il y a un an et demi.

Je vous recommande de lire

  • Penser en réaction
  • Flux

Le premier message est extrêmement utile pour comprendre comment structurer votre application React.

Flux répond à la question de savoir pourquoi vous devriez structurer votre application React de cette façon (par opposition à la façon de la structurer). React ne représente que 50% du système, et avec Flux, vous obtenez une vue d’ensemble et voyez comment ils constituent un système cohérent.

Retour à la question

En ce qui concerne ma première solution, il est tout à fait acceptable de laisser le gestionnaire aller en sens inverse, car les données vont toujours dans une seule direction.

Cependant, le fait de laisser un gestionnaire déclencher un setState dans P peut être correct ou faux selon votre situation.

Si l’application est un simple convertisseur de Markdown, C1 étant l’entrée brute et C2 étant la sortie HTML, il convient de laisser C1 déclencher un setState dans P, mais certains pourraient soutenir que ce n’est pas la méthode recommandée pour le faire.

Cependant, si l’application est une liste de tâches, C1 étant l’entrée pour créer un nouveau todo, C2 la liste des tâches en HTML, vous voudrez probablement que le gestionnaire passe à deux niveaux de plus que P – au dispatcher , qui laisse le store mettre à jour le data store , qui envoie ensuite les données à P et remplit les vues. Voir cet article Flux. Voici un exemple: Flux – TodoMVC

En général, je préfère la manière décrite dans l’exemple de liste de tâches. Le moins d’état que vous avez dans votre application le mieux.

La première solution, en conservant l’état dans le composant parent , est la bonne . Cependant, pour des problèmes plus complexes, vous devriez penser à une bibliothèque de gestion d’états , redux est la plus utilisée avec la réaction.

Vous devriez apprendre la bibliothèque Redux et ReactRedux. Elle structurera vos états et accessoires dans un magasin et vous pourrez y accéder plus tard dans vos composants.

  1. La bonne chose à faire est d’avoir l’ état dans le composant parent , d’éviter ref et ce qui ne l’est pas
  2. Un problème est d’éviter de mettre à jour constamment tous les enfants lors de la saisie dans un champ
  3. Par conséquent, chaque enfant doit être un composant (comme dans pas un PureComponent) et implémenter shouldComponentUpdate(nextProps, nextState)
  4. De cette façon, lors de la saisie dans un champ de formulaire, seul ce champ est mis à jour.

Le code ci-dessous utilise les annotations @bound d’ ES.Next babel-plugin-transform-decorators-legacy de BabelJS 6 et class-properties (l’annotation définit cette valeur sur les fonctions membres similaires à bind):

 /* © 2017-present Harald Rudell  (http://www.haraldrudell.com) All rights reserved. */ import React, {Component} from 'react' import {bound} from 'class-bind' const m = 'Form' export default class Parent extends Component { state = {one: 'One', two: 'Two'} @bound submit(e) { e.preventDefault() const values = {...this.state} console.log(`${m}.submit:`, values) } @bound fieldUpdate({name, value}) { this.setState({[name]: value}) } render() { console.log(`${m}.render`) const {state, fieldUpdate, submit} = this const p = {fieldUpdate} return ( 
{/* loop removed for clarity */}
) } } class Child extends Component { value = this.props.value @bound update(e) { const {value} = e.target const {name, fieldUpdate} = this.props fieldUpdate({name, value}) } shouldComponentUpdate(nextProps) { const {value} = nextProps const doRender = value !== this.value if (doRender) this.value = value return doRender } render() { console.log(`Child${this.props.name}.render`) const {value} = this.props const p = {value} return } }

Le concept de transmission de données de parent à enfant et vice versa est expliqué.

 import React, { Component } from "react"; import ReactDOM from "react-dom"; // taken refrence from https://gist.github.com/sebkouba/a5ac75153ef8d8827b98 //example to show how to send value between parent and child // props is the data which is passed to the child component from the parent component class Parent extends Component { constructor(props) { super(props); this.state = { fieldVal: "" }; } onUpdateParent = val => { this.setState({ fieldVal: val }); }; render() { return ( // To achieve the child-parent communication, we can send a function // as a Prop to the child component. This function should do whatever // it needs to in the component eg change the state of some property. //we are passing the function onUpdateParent to the child 

Parent

Value in Parent Component State: {this.state.fieldVal}

); } } class Child extends Component { constructor(props) { super(props); this.state = { fieldValChild: "" }; } updateValues = e => { console.log(e.target.value); this.props.onUpdate(e.target.value); // onUpdateParent would be passed here and would result // into onUpdateParent(e.target.value) as it will replace this.props.onUpdate //with itself. this.setState({ fieldValChild: e.target.value }); }; render() { return (

Child

); } } class OtherChild extends Component { render() { return (

OtherChild

Value in OtherChild Props: {this.props.passedVal}
the child can directly get the passed value from parent by this.props{" "}
); } } ReactDOM.render(, document.getElementById("root"));