Não fiquei satisfeito com nenhuma das soluções apresentadas aqui. Na verdade, existe uma solução muito simples que pode ser feita usando Javascript puro, sem depender de alguma funcionalidade do React que não seja o objeto props básico - e oferece o benefício de se comunicar em qualquer direção (pai -> filho, filho -> pai). Você precisa passar um objeto do componente pai para o componente filho. Esse objeto é o que eu chamo de "referência bidirecional" ou biRef, para abreviar. Basicamente, o objeto contém uma referência aos métodos no pai que ele deseja expor. E o componente filho anexa métodos ao objeto que o pai pode chamar. Algo assim:
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
someParentFunction: someParentFunction
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef.someChildFunction = someChildFunction;
return <div>
<Button onClick={onButtonClick} />
</div>;
}
A outra vantagem dessa solução é que você pode adicionar muito mais funções no pai e no filho enquanto as passa do pai para o filho usando apenas uma única propriedade.
Uma melhoria em relação ao código acima é não adicionar as funções pai e filho diretamente ao objeto biRef, mas aos sub-membros. As funções pai devem ser adicionadas a um membro chamado "pai", enquanto as funções filho devem ser adicionadas a um membro chamado "filho".
// Parent component.
function MyParentComponent(props) {
function someParentFunction() {
// The child component can call this function.
}
function onButtonClick() {
// Call the function inside the child component.
biRef.child.someChildFunction();
}
// Add all the functions here that the child can call.
var biRef = {
parent: {
someParentFunction: someParentFunction
}
}
return <div>
<MyChildComponent biRef={biRef} />
<Button onClick={onButtonClick} />
</div>;
}
// Child component
function MyChildComponent(props) {
function someChildFunction() {
// The parent component can call this function.
}
function onButtonClick() {
// Call the parent function.
props.biRef.parent.someParentFunction();
}
// Add all the child functions to props.biRef that you want the parent
// to be able to call.
props.biRef {
child: {
someChildFunction: someChildFunction
}
}
return <div>
<Button onClick={onButtonClick} />
</div>;
}
Ao colocar as funções pai e filho em membros separados do objeto biRef, você terá uma separação limpa entre os dois e verá facilmente quais pertencem ao pai ou ao filho. Também ajuda a impedir que um componente filho substitua acidentalmente uma função pai, se a mesma função aparecer em ambos.
Uma última coisa é que, se você observar, o componente pai cria o objeto biRef com var, enquanto o componente filho acessa-o através do objeto props. Pode ser tentador não definir o objeto biRef no pai e acessá-lo de seu pai por meio de seu próprio parâmetro props (que pode ser o caso em uma hierarquia de elementos da interface do usuário). Isso é arriscado, porque a criança pode pensar que uma função que está chamando no pai pertence ao pai quando ele realmente pertence a um avô. Não há nada errado com isso, desde que você esteja ciente disso. A menos que você tenha um motivo para apoiar alguma hierarquia além de um relacionamento pai / filho, é melhor criar o biRef no seu componente pai.