2016-02-21 18 views
91

mia struttura si presenta come segue:Come aggiornare lo stato del genitore in React?

Component 1 

- |- Component 2 


- - |- Component 4 


- - - |- Component 5 

Component 3 

Componente 3 dovrebbe visualizzare alcuni dati a seconda dello stato del componente 5. Dal oggetti di scena sono immutabili, non posso semplicemente salvare suo stato in Componente 1 e lo trasmette, a destra ? E sì, ho letto su Redux, ma non voglio usarlo. Spero che sia possibile risolverlo solo con la reazione. Ho sbagliato?

risposta

192

Per la comunicazione del bambino-genitore si dovrebbe passare un impostazione dello stato di padre in figlio la funzione, come questo

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

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

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

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

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

In questo modo il bambino può aggiornare lo stato del genitore con la chiamata di una funzione passata con oggetti di scena.

Ma si dovrà riconsiderare la struttura dei componenti, perché, come ho capito, i componenti 5 e 3 non sono correlati.

Una possibile soluzione è racchiuderli in un componente di livello superiore che conterrà lo stato di entrambi i componenti 1 e 3. Questo componente imposterà lo stato di livello inferiore tramite i puntelli.

+1

Perché avete bisogno di this.handler = this.handler.bind (questo) e non solo la funzione gestore che imposta lo stato? – chemook78

+7

@ chemook78 in ES6 I metodi delle classi React non si associano automaticamente alla classe. Quindi, se non aggiungiamo 'this.handler = this.handler.bind (this)' nel costruttore, 'this' all'interno della funzione' handler' farà riferimento alla chiusura della funzione, non alla classe. Se non vuoi associare tutte le tue funzioni nel costruttore, ci sono altri due modi per gestirlo usando le funzioni freccia. Potresti semplicemente scrivere il gestore dei clic come 'onClick = {() => this.setState (...)}', oppure puoi utilizzare gli inizializzatori di proprietà insieme alle funzioni di freccia come descritto qui https://babeljs.io/blog/ 2015/06/07/react-on-es6-plus sotto "Funzioni freccia" – Ivan

+1

Ecco un esempio di questo in azione: http://plnkr.co/edit/tGWecotmktae8zjS5yEr?p=preview – Tamb

6

Mi piace la risposta relativa al passaggio delle funzioni in giro, è una tecnica molto utile.

Dall'altro lato è possibile ottenere questo risultato utilizzando pub/sub o utilizzando una variante, un dispatcher, come fa Flux. La teoria è semplicissima, la componente 5 invia un messaggio a cui il componente 3 sta ascoltando. Il componente 3 aggiorna il suo stato che attiva il ri-rendering. Ciò richiede componenti stateful, che, a seconda del punto di vista, possono o meno essere un anti-pattern. Sono contro di loro personalmente e preferirei che qualcos'altro sia in ascolto di dispatch e cambiamenti di stato dall'alto molto in basso (Redux fa questo, ma aggiunge una terminologia aggiuntiva).

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> 
} 

I fasci dispatcher con Flux é molto semplice, esso registra semplicemente callback e invoca quando si verifica qualsiasi spedizione, passando attraverso i contenuti alla spedizione (nell'esempio terso sopra non c'è payload con l'invio, semplicemente un messaggio ID). Puoi adattarlo a pub/sub tradizionali (ad esempio utilizzando EventEmitter dagli eventi, o qualche altra versione) molto facilmente se questo ha più senso per te.

+0

I componenti di My Reacts sono "in esecuzione" nel browser come in un tutorial ufficiale (https://facebook.github.io/react/docs/tutorial.html) Ho provato a includere Flux con browserify, ma il browser dice che Dispatcher è non trovata :( – wklm

+2

La sintassi che ho usato è stata la sintassi del modulo ES2016 che richiede la transpilation (io uso [Babel] (https://babeljs.io/), ma ce ne sono altri, [babelify] (https: //www.npmjs .com/package/babelify) transform può essere usato con browserify), traspone in 'var Dispatcher = require ('flux') .Dispatcher' –

1

Quando è necessario comunicare tra un bambino e un genitore a qualsiasi livello inferiore, è preferibile utilizzare il contesto . Nel componente padre definire il contesto che può essere richiamato dal bambino come

In componente principale nel componente caso 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 
} 

Ora a componente figlio (componente 5 nel tuo caso), basta dire a questo componente che vuole usare il contesto del suo genitore.

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> 
)} 

si può trovare il progetto Demo a repo

8

ho trovato la seguente soluzione di lavoro per passare onClick argomento della funzione dal figlio al componente principale:

//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") 
    ); 
} 

Look at JSFIDDLE

+0

Questo è buono! potresti spiegare questo pezzo:' 'Perché doversi legare di nuovo? – Dane

3

Ho trovato la seguente soluzione operativa per passare l'argomento della funzione Click da child a t egli componente genitore con param:

classe genitore: classe

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} /> 
}} 

bambino:

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> 
    ) 
} } 
+0

Qualcuno può dire se questa è una soluzione accettabile (particolarmente interessata a passare il parametro come suggerito). – ilans

0
<Footer 
    action={()=>this.setState({showChart: true})} 
/> 

<footer className="row"> 
    <button type="button" onClick={this.props.action}>Edit</button> 
    {console.log(this.props)} 
</footer> 

Try this example to write inline setState, it avoids creating another function. 
Problemi correlati