web-dev-qa-db-fra.com

Comment mettre à jour l'état du parent dans React?

Ma structure ressemble à ceci:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

Le composant 3 devrait afficher certaines données en fonction de l'état du composant 5 . Puisque les accessoires sont immuables, je ne peux pas simplement sauvegarder son état dans le composant 1 et le transférer, non? Et oui, j'ai lu sur Redux, mais je ne veux pas l'utiliser. J'espère qu'il est possible de le résoudre simplement en réagissant. Ai-je tort?

199
wklm

Pour la communication parent-enfant, vous devez passer une fonction définissant l'état de parent à enfant, comme ceci

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: someValue
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

Ainsi, l'enfant peut mettre à jour l'état du parent avec l'appel d'une fonction passée avec props.

Mais vous devrez repenser la structure de vos composants, car si je comprends bien, les composants 5 et 3 ne sont pas liés.

Une solution possible consiste à les envelopper dans un composant de niveau supérieur qui contiendra l’état des composants 1 et 3. Ce composant définira l’état de niveau inférieur par le biais des accessoires.

406
Ivan

J'ai trouvé la solution de travail suivante pour passer l'argument de la fonction onClick de l'enfant au composant parent:

Version avec passage d'une méthode ()

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: ' + someArg);
            this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Regardez JSFIDDLE

Version avec passage d'une fonction flèche

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component { 
    constructor(props) {
        super(props);
    }

    handleToUpdate = (someArg) => {
            alert('We pass argument from Child to Parent: ' + someArg);
    }

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <ParentA />,
        document.querySelector("#demo")
    );
}

Regardez JSFIDDLE

32
Roman

J'aime la réponse concernant les fonctions de passe-partout, c'est une technique très pratique.

D'un autre côté, vous pouvez également utiliser pub/sub ou une variante, un répartiteur, comme Flux do. La théorie est très simple: le composant 5 envoie un message que le composant 3 écoute. Le composant 3 met alors à jour son état, ce qui déclenche le rendu. Cela nécessite des composants avec état, qui, selon votre point de vue, peuvent ou non être un anti-motif. Je suis contre eux personnellement et je préférerais que quelque chose d'autre écoute les dépêches et les changements d'état de haut en bas (Redux le fait, mais ajoute une terminologie supplémentaire).

import { Dispatcher } from flux
import { Component } from React

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

Le groupement de répartiteurs avec Flux est très simple: il enregistre simplement les rappels et les appelle à chaque envoi, en passant par le contenu de l'envoi (dans l'exemple ci-dessus, il n'y a pas de payload avec l'envoi, il s'agit simplement d'un identifiant de message). Vous pouvez l’adapter à un pub/sous traditionnel (par exemple, en utilisant EventEmitter à partir d’événements, ou une autre version) très facilement si cela vous semble plus logique.

6
Matt Styles

J'ai trouvé la solution de travail suivante pour passer l'argument de la fonction onClick d'enfant au composant parent avec param:

classe parente:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

classe enfant:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }
4
H. parnian

À chaque fois que vous avez besoin de communiquer d'un enfant à l'autre, il est préférable d'utiliser context. Dans le composant parent, définissez le contexte pouvant être appelé par l'enfant, tel que 

Dans le composant parent dans votre composant de cas 3

     static childContextTypes = {
            parentMethod: React.PropTypes.func.isRequired
          };

           getChildContext() {
            return {
              parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
            };
          }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

Maintenant dans le composant enfant (composant 5 dans votre cas), dites simplement ceci composant qu'il veut utiliser le contexte de son parent.

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >


            <Text> update state in parent component </Text>


      </TouchableHighlight>
)}

vous pouvez trouver le projet de démonstration à repo

2
Rajan Twanabashu

donc, si vous voulez mettre à jour le composant parent,

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

        render(){
          return (
              <ChildComponent onChange={this.handler} />
           )
       }
   }


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

Ici onChange est un attribut avec une méthode "handler" liée à son instance. nous avons passé le gestionnaire de méthodes au composant de classe Child, pour recevoir via la propriété onChange dans son argument props.

L'attribut onChange sera défini dans un objet props comme ceci:

props ={
onChange : this.handler
}

et passé au composant enfant

Ainsi, le composant Child peut accéder à la valeur de name dans l'objet props comme ceci props.onChange

Son fait par l'utilisation d'accessoires de rendu.

Maintenant, le composant enfant a un bouton "Cliquez" avec un événement onclick défini pour appeler la méthode de gestionnaire qui lui est transmise via onChnge dans son objet d'argument props. Alors maintenant, this.props.onChange in Child contient la méthode de sortie dans la classe Parent Référence et crédits: Bits and Pieces

1
Preetham NT

Il semble que nous ne pouvons transmettre que des données d'un parent à un autre, car react favorise le flux de données unidirectionnel, mais pour que le parent se mette à jour lui-même lorsque quelque chose se produit dans son "composant enfant", nous utilisons généralement une fonction appelée "fonction de rappel".

Nous transmettons la fonction définie dans le parent à l'enfant en tant qu '"accessoires" et appelons cette fonction à partir de l'enfant qui la déclenche dans le composant parent.

-------------------------------------------------- -----------------------------

class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

Dans cet exemple, nous pouvons faire passer des données de SubChild -> Child -> Parent en passant la fonction à son enfant direct.

1
Vishal Bisht

J'ai utilisé plusieurs fois une des réponses les mieux notées de cette page, mais en apprenant React, j'ai trouvé un meilleur moyen de le faire, sans reliure ni fonction inline à l'intérieur des accessoires.

Il suffit de regarder ici:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

La clé est dans une fonction de flèche:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

Vous pouvez en lire plus ici . J'espère que cela sera utile pour quelqu'un =)

-Nous pouvons créer ParentComponent et avec la méthode handleInputChange pour mettre à jour l'état ParentComponent. Importez le ChildComponent et nous passons deux accessoires du composant parent au composant enfant, c'est-à-dire la fonction et le compte. 

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • Nous créons maintenant le fichier ChildComponent et l'enregistrons sous le nom ChildComponent.jsx. Ce composant est sans état car le composant enfant n'a pas d'état. Nous utilisons la bibliothèque prop-types pour la vérification du type d’accessoires.

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;
    
1
Sachin Metkari

Je tiens à remercier la réponse la plus votée de m'avoir donné l'idée de mon propre problème, essentiellement sa variation avec la fonction arrow et le passage de param à partir du composant enfant:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

J'espère que ça aide quelqu'un.

0
Ardhi