Modifica : vedere gli esempi di fine per gli esempi aggiornati di ES6.
Questa risposta gestisce semplicemente il caso della relazione diretta genitore-figlio. Quando genitori e figli hanno potenzialmente molti intermediari, controlla questa risposta .
Altre soluzioni mancano il punto
Mentre funzionano ancora bene, ad altre risposte manca qualcosa di molto importante.
Non c'è un modo semplice per passare gli oggetti di scena di un bambino al suo genitore usando eventi, in React.js?
Il genitore ha già quel figlio prop! : se il bambino ha un sostegno, è perché il suo genitore ha fornito quel sostegno al bambino! Perché vuoi che il bambino riporti l'elica al genitore, mentre ovviamente il genitore ha già quell'elica?
Migliore attuazione
Bambina : in realtà non deve essere più complicato di così.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Genitore con figlio singolo : utilizzando il valore che passa al figlio
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
Genitore con un elenco di figli : hai ancora tutto il necessario sul genitore e non devi renderlo più complicato.
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
È anche possibile utilizzare this.handleChildClick.bind(null,childIndex)
e quindi utilizzarethis.state.childrenData[childIndex]
Nota che stiamo vincolando con un null
contesto perché altrimenti React emette un avviso relativo al suo sistema di auto-associazione . L'uso di null significa che non si desidera modificare il contesto della funzione. Guarda anche .
Informazioni sull'incapsulamento e l'accoppiamento in altre risposte
Questa è per me una cattiva idea in termini di accoppiamento e incapsulamento:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
Uso degli oggetti di scena : come ho spiegato sopra, hai già gli oggetti di scena nel genitore, quindi è inutile passare l'intero componente figlio per accedere agli oggetti di scena.
Utilizzo di ref : hai già il target dei clic nell'evento, e nella maggior parte dei casi questo è sufficiente. Inoltre, avresti potuto usare un ref direttamente sul bambino:
<Child ref="theChild" .../>
E accedi al nodo DOM nel genitore con
React.findDOMNode(this.refs.theChild)
Per i casi più avanzati in cui si desidera accedere a più riferimenti del figlio nel genitore, il figlio potrebbe passare tutti i nodi dom direttamente nel callback.
Il componente ha un'interfaccia (oggetti di scena) e il genitore non dovrebbe assumere nulla sul funzionamento interno del figlio, inclusa la sua struttura DOM interna o per quali nodi DOM dichiara i riferimenti. Un genitore che utilizza un riferimento di un bambino significa che si accoppiano strettamente i 2 componenti.
Per illustrare il problema, prenderò questa citazione sul Shadow DOM , che viene utilizzato all'interno dei browser per rendere elementi come cursori, barre di scorrimento, lettori video ...:
Hanno creato un confine tra ciò che tu, lo sviluppatore Web è in grado di raggiungere e i dettagli dell'implementazione considerati, quindi inaccessibili per te. Tuttavia, il browser può attraversare questo confine a piacimento. Con questo limite in atto, sono stati in grado di costruire tutti gli elementi HTML usando le stesse buone tecnologie Web, al di fuori dei div e degli intervalli proprio come faresti tu.
Il problema è che se si lasciano filtrare i dettagli dell'implementazione figlio nel genitore, si rende molto difficile il refactoring del figlio senza influire sul genitore. Ciò significa che come autore di librerie (o come editor di browser con Shadow DOM) questo è molto pericoloso perché si consente al client di accedere troppo, rendendo molto difficile l'aggiornamento del codice senza interrompere la retrocompatibilità.
Se Chrome avesse implementato la sua barra di scorrimento consentendo al client di accedere ai nodi dom interni di quella barra di scorrimento, ciò significa che il client potrebbe avere la possibilità di interrompere semplicemente quella barra di scorrimento e che le app si interromperebbero più facilmente quando Chrome eseguisse l'aggiornamento automatico dopo il refactoring del barra di scorrimento ... Invece, danno accesso solo ad alcune cose sicure come la personalizzazione di alcune parti della barra di scorrimento con CSS.
Di usare qualcos'altro
Passare l'intero componente nel callback è pericoloso e può portare gli sviluppatori alle prime armi a fare cose molto strane come chiamare childComponent.setState(...)
o childComponent.forceUpdate()
, o assegnare loro nuove variabili, all'interno del genitore, rendendo l'intera app molto più difficile da ragionare.
Modifica: esempi ES6
Poiché molte persone ora usano ES6, ecco gli stessi esempi per la sintassi ES6
Il bambino può essere molto semplice:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
Il genitore può essere una classe (e alla fine può gestire lo stato stesso, ma lo sto passando come oggetti di scena qui:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
Ma può anche essere semplificato se non è necessario gestire lo stato:
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
AVVISO PERF (si applicano a ES5 / ES6): se si sta utilizzando PureComponent
o shouldComponentUpdate
, le implementazioni di cui sopra non saranno ottimizzate per impostazione predefinita perché utilizzano onClick={e => doSomething()}
o si associano direttamente durante la fase di rendering, poiché creerà una nuova funzione ogni volta che viene eseguito il rendering del genitore. Se si tratta di un collo di bottiglia perfetto nella tua app, puoi trasferire i dati ai bambini e reintegrarli all'interno di un callback "stabile" (impostato sulla classe genitore e associato al this
costruttore della classe) in modo che l' PureComponent
ottimizzazione possa iniziare, oppure tu può implementare il tuoshouldComponentUpdate
e ignorare il callback nel controllo comparativo degli oggetti di scena.
Puoi anche utilizzare la libreria Ricomponi , che fornisce componenti di ordine superiore per ottenere ottimizzazioni ottimizzate:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
In questo caso è possibile ottimizzare il componente figlio utilizzando:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)