Editar : vea los ejemplos finales para los ejemplos actualizados de ES6.
Esta respuesta simplemente maneja el caso de la relación directa padre-hijo. Cuando padre e hijo tengan potencialmente muchos intermediarios, marque esta respuesta .
Otras soluciones están perdiendo el punto
Si bien aún funcionan bien, a otras respuestas les falta algo muy importante.
¿No hay una manera simple de pasar los accesorios de un niño a su padre usando eventos, en React.js?
¡El padre ya tiene ese accesorio de niño! : si el niño tiene un accesorio, ¡es porque su padre le proporcionó ese accesorio al niño! ¿Por qué quieres que el niño pase el accesorio al padre, mientras que el padre obviamente ya tiene ese accesorio?
Mejor implementación
Niño : realmente no tiene que ser más complicado que eso.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Padre con hijo único : usando el valor que le pasa al hijo
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
Padre con lista de hijos : todavía tiene todo lo que necesita en el padre y no necesita complicarlo más.
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
También es posible usar this.handleChildClick.bind(null,childIndex)
y luego usarthis.state.childrenData[childIndex]
Tenga en cuenta que estamos vinculados con un null
contexto porque, de lo contrario, React emite una advertencia relacionada con su sistema de enlace automático. Usar nulo significa que no desea cambiar el contexto de la función. Ver también .
Sobre encapsulación y acoplamiento en otras respuestas
Esta es para mí una mala idea en términos de acoplamiento y encapsulación:
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 de accesorios : como expliqué anteriormente, ya tiene los accesorios en el padre, por lo que es inútil pasar todo el componente hijo para acceder a los accesorios.
Uso de referencias : ya tiene el objetivo de clic en el evento, y en la mayoría de los casos esto es suficiente. Además, podría haber usado una referencia directamente sobre el niño:
<Child ref="theChild" .../>
Y acceda al nodo DOM en el padre con
React.findDOMNode(this.refs.theChild)
Para casos más avanzados en los que desea acceder a múltiples referencias del niño en el padre, el niño podría pasar todos los nodos dom directamente en la devolución de llamada.
El componente tiene una interfaz (accesorios) y el padre no debe asumir nada sobre el funcionamiento interno del niño, incluida su estructura DOM interna o para qué nodos DOM declara referencias. Un padre que usa una referencia de un niño significa que acopla firmemente los 2 componentes.
Para ilustrar el problema, tomaré esta cita sobre Shadow DOM , que se usa dentro de los navegadores para representar elementos como controles deslizantes, barras de desplazamiento, reproductores de video ...:
Crearon un límite entre lo que usted, el desarrollador web puede alcanzar y lo que se considera detalles de implementación, por lo tanto, inaccesible para usted. Sin embargo, el navegador puede atravesar este límite a voluntad. Con este límite en su lugar, pudieron construir todos los elementos HTML utilizando las mismas tecnologías web antiguas, fuera de los divs y los tramos, como lo haría usted.
El problema es que si deja que los detalles de implementación secundarios se filtren en el elemento primario, le resultará muy difícil refactorizar el elemento secundario sin afectarlo. Esto significa que, como autor de la biblioteca (o como editor del navegador con Shadow DOM), esto es muy peligroso porque permite que el cliente acceda demasiado, lo que dificulta la actualización del código sin romper la retrocompatibilidad.
Si Chrome hubiera implementado su barra de desplazamiento permitiendo que el cliente acceda a los nodos dom internos de esa barra de desplazamiento, esto significa que el cliente puede tener la posibilidad de simplemente romper esa barra de desplazamiento, y que las aplicaciones se romperían más fácilmente cuando Chrome realiza su actualización automática después de refactorizar el barra de desplazamiento ... En cambio, solo dan acceso a algunas cosas seguras como personalizar algunas partes de la barra de desplazamiento con CSS.
Sobre usar cualquier otra cosa
Pasar todo el componente en la devolución de llamada es peligroso y puede dar lugar a los desarrolladores novatos que hacer cosas muy extrañas como llamar childComponent.setState(...)
o childComponent.forceUpdate()
, o asignándole nuevas variables, dentro de la matriz, por lo que toda la aplicación mucho más difícil de razonar acerca.
Editar: ejemplos de ES6
Como muchas personas ahora usan ES6, estos son los mismos ejemplos para la sintaxis de ES6
El niño puede ser muy simple:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
El padre puede ser una clase (y eventualmente puede administrar el estado en sí, pero lo paso como accesorio aquí:
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>
);
}
}
Pero también se puede simplificar si no necesita administrar el estado:
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
ADVERTENCIA PERF (aplica a ES5 / ES6): si está utilizando PureComponent
o shouldComponentUpdate
, las implementaciones anteriores no se optimizarán de manera predeterminada porque se usan onClick={e => doSomething()}
o se vinculan directamente durante la fase de representación, ya que creará una nueva función cada vez que se muestre el elemento primario. Si se trata de un cuello de botella de rendimiento en su aplicación, puede pasar los datos a los elementos secundarios y reinyectarlos dentro de la devolución de llamada "estable" (establecida en la clase principal y vinculada al this
constructor de la clase) para que la PureComponent
optimización pueda iniciarse, o usted puede implementar el suyo propio shouldComponentUpdate
e ignorar la devolución de llamada en la verificación de comparación de accesorios.
También puede usar la biblioteca Recompose , que proporciona componentes de orden superior para lograr optimizaciones ajustadas:
// 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)
En este caso, podría optimizar el componente Hijo utilizando:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)