Editar : veja os exemplos finais para ver os exemplos atualizados do ES6.
Essa resposta simplesmente trata do caso de relacionamento direto pai-filho. Quando pai e filho têm potencialmente muitos intermediários, verifique esta resposta .
Outras soluções estão faltando
Enquanto eles ainda funcionam bem, outras respostas estão faltando algo muito importante.
Não existe uma maneira simples de passar os acessórios de uma criança para o pai usando eventos, no React.js?
Os pais já têm esse suporte filho! : se a criança tem um suporte, é porque seus pais forneceram esse suporte à criança! Por que você quer que a criança devolva o suporte aos pais, enquanto os pais obviamente já têm esse suporte?
Melhor implementação
Criança : realmente não precisa ser mais complicado que isso.
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
Pai com filho único : usando o valor que ele passa para o filho
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
Pai com lista de filhos : você ainda tem tudo o que precisa no pai e não precisa tornar a criança mais complicada.
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
Também é possível usar this.handleChildClick.bind(null,childIndex)
e depois usarthis.state.childrenData[childIndex]
Observe que estamos vinculando um null
contexto porque, caso contrário, o React emite um aviso relacionado ao seu sistema de vinculação automática. Usar nulo significa que você não deseja alterar o contexto da função. Veja também .
Sobre encapsulamento e acoplamento em outras respostas
Isso é para mim uma péssima idéia em termos de acoplamento e encapsulamento:
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} />
}
});
Usando adereços : Como expliquei acima, você já possui os adereços no pai, portanto é inútil passar todo o componente filho para acessar os adereços.
Usando refs : você já tem o destino do clique no evento e, na maioria dos casos, isso é suficiente. Além disso, você poderia ter usado um árbitro diretamente na criança:
<Child ref="theChild" .../>
E acesse o nó DOM no pai com
React.findDOMNode(this.refs.theChild)
Para casos mais avançados em que você deseja acessar várias referências do filho no pai, o filho pode passar todos os nós de domínio diretamente no retorno de chamada.
O componente tem uma interface (props) e o pai não deve assumir nada sobre o trabalho interno do filho, incluindo sua estrutura DOM interna ou para quais nós DOM ele declara refs. Um pai que usa uma referência de um filho significa que você acopla firmemente os 2 componentes.
Para ilustrar o problema, levarei esta citação sobre o Shadow DOM , que é usado dentro dos navegadores para renderizar coisas como controles deslizantes, barras de rolagem, players de vídeo ...:
Eles criaram um limite entre o que você, o desenvolvedor da Web pode alcançar, e o que é considerado detalhes de implementação, inacessíveis a você. O navegador, no entanto, pode atravessar esse limite à vontade. Com esse limite, eles foram capazes de criar todos os elementos HTML usando as mesmas boas e antigas tecnologias da Web, a partir dos divs e dos intervalos, como você faria.
O problema é que, se você deixar os detalhes da implementação filho vazarem para o pai, dificulta a refatoração do filho sem afetar o pai. Isso significa que, como autor da biblioteca (ou como editor do navegador com o Shadow DOM), isso é muito perigoso, porque você deixa o cliente acessar demais, dificultando a atualização do código sem interromper a retrocompatibilidade.
Se o Chrome implementou sua barra de rolagem, permitindo que o cliente acesse os nós de domínio internos dessa barra de rolagem, isso significa que o cliente pode ter a possibilidade de simplesmente quebrar essa barra de rolagem e que os aplicativos quebrariam mais facilmente quando o Chrome executasse sua atualização automática após refatorar o barra de rolagem ... Em vez disso, eles apenas dão acesso a algumas coisas seguras, como personalizar algumas partes da barra de rolagem com CSS.
Sobre como usar qualquer outra coisa
Passando toda a componente no callback é perigoso e pode levar desenvolvedores novatos para fazer coisas muito estranhas como a chamada childComponent.setState(...)
ou childComponent.forceUpdate()
, ou atribuindo-lhe novas variáveis, dentro do pai, fazendo com que o aplicativo inteiro muito mais difícil de raciocinar sobre.
Edit: ES6 examples
Como muitas pessoas agora usam o ES6, eis os mesmos exemplos de sintaxe do ES6
A criança pode ser muito simples:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
O pai pode ser uma classe (e, eventualmente, pode gerenciar o próprio estado, mas estou passando isso como adereços aqui:
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>
);
}
}
Mas também pode ser simplificado se não for necessário gerenciar o 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
AVISO PERF (aplicável ao ES5 / ES6): se você estiver usando PureComponent
ou shouldComponentUpdate
, as implementações acima não serão otimizadas por padrão porque usam onClick={e => doSomething()}
ou vinculam diretamente durante a fase de renderização, porque criarão uma nova função sempre que o pai for renderizado. Se esse é um gargalo perfeito no seu aplicativo, você pode passar os dados para os filhos e reiniciá-los no retorno de chamada "estável" (definido na classe pai e vinculado ao this
construtor da classe) para que a PureComponent
otimização possa ser ativada ou você pode implementar o seu próprio shouldComponentUpdate
e ignorar o retorno de chamada na verificação de comparação de adereços.
Você também pode usar a biblioteca Recompose , que fornece componentes de ordem superior para obter otimizações aprimoradas:
// 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)
Nesse caso, você pode otimizar o componente filho usando:
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)