Se você está apenas desesperado pela resposta, vá para o parágrafo 6. Mas se você, assim como eu, também teve aquele ligeiro surto e também pensou em desistir do typescript ao se deparar com o vscode alertando sobre muitos alguns erros em seu código em decorrência da tipagem apenas porque você queria chamar um método de outro componente, acompanhe essa leitura e veja que resolver tal problema pode ser bem mais simples do que você imagina.
Breve intro
Antes de tudo, devo considerar que alguns leitores não estão habituados com as palavras-chave do texto, então aí vai uma breve introdução mastigadinha:
- Componente: Um componente pode ser descrito simplesmente como um objeto. Possui métodos, atributos e estado próprio e pode se relacionar ou ser relacionado com outros componentes. Em outras palavras, é uma partezinha do sistema que pode ser reutilizada;
- React: É um framework Javascript implementado para criar interfaces de usuário de forma interativa e reativa. É utilizado para a criação e manipulação de componentes;
- Typescript: É um superconjunto do Javascript que permite tipagem estática, orientação a objetos e facilita a escrita de um código de fácil legibilidade;
- Hooks: Uma alternativa às classes. Usados para definir estado de um objeto, referência, efeito imediato além de vários outros artifícios que propiciam uma implementação menos tortuosa.
Comunicação
A comunicação entre componentes é de suma importância para o bom reuso de código, além de facilitar a leitura e apresentar uma melhor resolução de possíveis problemas. Entretanto, principalmente para quem está começando e até mesmo para quem tem alguma experiência, essa interlocução de objetos com algum parentesco pode se tornar um pouco mais nebulosa ao se deparar com milhares de possíveis soluções encontradas na internet.
Antes de começar, eu gostaria de externar minha infelicidade pois 95% dessas possíveis soluções eram baseadas em componentes construídos a partir de classes, o que já deixava meu estresse momentâneo bem mais inflamado.
Mas vamos deixar o estresse de lado, pois esse post é só alegria.
Componente Filho utilizando métodos ou atributos de um Componente Pai
Usado quando você deseja que o componente Filho utilize métodos e/ou atributos do componente Pai. Este procedimento é um dos mais simples, bastando apenas passar os métodos e/ou atributos do componente Pai para o componente Filho.
Componente Pai
Primeiramente, vamos instanciar um componente Parent com apenas um atributo name a fim de uma demonstração genérica:
interface Props {
readonly name: string;
}
const Parent: React.FC<Props> = ({ name }) => {
O componente Parent possui o método handleLabel que faz alteração do Label presente dentro do componente Parent.
const handleLabel = (novoLabel: string) => {
setWord(novoLabel);
}
Dentro deste componente Parent, vamos incluir o componente Child com a função handleLabel como parâmetro, além de um name para ilustração.
<Child name={"Simba"} changeLabel={handleLabel}></Child>
O resultado da implementação fica da seguinte maneira:
interface Props {
readonly name: string;
}
const Parent: React.FC<Props> = ({ name }) => {
const [word, setWord] = useState<string>("you forgot who you are")
const handleLabel = (novoLabel: string) => {
setWord(novoLabel);
}
return (
<Container color={"#FAF9F5"} colorName={"#5B709B"}>
<p>{name}</p>
<h6>says: {word}</h6>
<Child name={"Simba"} changeLabel={handleLabel}></Child>
</Container>
);
};
Componente Filho
Já no componente Filho, vamos instanciá-lo com os mesmos atributos repassados no componente Pai:
interface Props {
readonly name: string;
readonly changeLabel: (arg0: string) => void;
}
const Child: React.FC<Props> = ({ name, changeLabel }) => {
O componente Filho possui o método sendNewLabel() que envia o novo label descrito no componente Filho para o componente Pai.
const sendNewLabel = () => {
if (document.getElementById("novoLbl")) {
const novoLabel = (document.getElementById("novoLbl") as HTMLInputElement).value;
changeLabel(novoLabel);
}
}
Por fim, o componente Filho é constituído por um input onde será digitado o novo label e um botão que será o gatilho do evento.
<input type="text" id="novoLbl"></input>
<button onClick={sendNewLabel}>Send</button>
Juntando tudo:
interface Props {
readonly name: string;
readonly changeLabel: (arg0: string) => void;
}
const Child: React.FC<Props> = ({ name, changeLabel }) => {
const sendNewLabel = () => {
if (document.getElementById("novoLbl")) {
const novoLabel = (document.getElementById("novoLbl") as HTMLInputElement).value;
changeLabel(novoLabel);
}
}
return (
<Container color={"#5B709B"} colorName={"#FAF9F5"}>
<p>{name}</p>
<input type="text" id="novoLbl"></input>
<button onClick={sendNewLabel}>Send</button>
</Container>
);
};
export default Child;
O fluxo das ações descritas acima se comporta da seguinte forma:
Componente Pai utilizando métodos ou atributos de um Componente Filho
Usado quando você deseja que o componente Pai utilize métodos e/ou atributos do componente Filho. Este procedimento pode parecer um pouco mais trabalhoso pois utiliza de mais alguns artifícios oferecidos pelo React Hooks.
Componente Pai
Novamente, vamos instanciar um componente Parent com apenas um atributo name a fim de mais uma demonstração genérica:
interface Props {
readonly name: string;
}
const Parent: React.FC<Props> = ({ name }) => {
Nesse caso, usaremos o useRef que nada mais é que uma referência passada de um componente para outro. E vamos instanciar tal referência criando uma interface de objeto genérica que possui dois métodos para ilustração.
interface RefObject {
alertLeave: () => void;
alertBack: () => void;
}
const myRef = useRef<RefObject>(null)
const handleLeave = () => {
if (myRef.current) {
myRef.current.alertLeave();
}
}
const handleBack = () => {
if (myRef.current) {
myRef.current.alertBack();
}
}
Por fim, o componente Parent é constituído por um nome e um componente Child, onde passaremos como parâmetros a referência instanciada anteriormente e um name para ilustração.
<Child name={"Nemo"} ref={myRef}></Child>
O resultado da implementação ocorre a seguir:
interface RefObject {
alertLeave: () => void;
alertBack: () => void;
}
interface Props {
readonly name: string;
}
const Parent: React.FC<Props> = ({ name }) => {
const myRef = useRef<RefObject>(null)
const handleLeave = () => {
if (myRef.current) {
myRef.current.alertLeave();
}
}
const handleBack = () => {
if (myRef.current) {
myRef.current.alertBack();
}
}
return (
<Container color={"#296766"} colorName={"#F1B656"} onMouseLeave={handleLeave} onMouseOver={handleBack}>
<p>{name}</p>
<h6>is he looking for Nemo?</h6>
<Child name={"Nemo"} ref={myRef}></Child>
</Container>
);
};
Componente Filho
Desta vez, no componente Filho, instanciaremos usando o método forwardRef para que a referência passada pelo componente Parent perca sua nulidade.
O componete Child possui como parâmetros um name para ilustração e a referência passada pelo componente Parent. Além disso, a referência passada pelo componente Parent também obedece o formato o qual foi instanciada no componente Parent.
interface Props {
readonly name: string;
ref: Ref<RefObject>
}
interface RefObject {
alertLeave: () => void;
alertBack: () => void;
}
const Child = forwardRef((props: Props, ref: Ref<RefObject>) => {
Dentro do componente Child implementaremos os métodos assinalados na interface RefObject.
const [him, setHim] = useState<string>("")
const alertLeave = () => {
setHim("is looking for you");
}
const alertBack = () => {
setHim("is back");
}
Depois de instanciados e implementados, vamos referenciar tais métodos à referência passada originalmente pelo componente Parent usando o useImperativeHandle:
useImperativeHandle(ref, () => ({ alertLeave, alertBack }));
Juntando esse negócio todo e quase terminando, temos:
interface Props {
readonly name: string;
ref: Ref<RefObject>
}
interface RefObject {
alertLeave: () => void;
alertBack: () => void;
}
const Child = forwardRef((props: Props, ref: Ref<RefObject>) => {
const [him, setHim] = useState<string>("")
const alertLeave = () => {
setHim("is looking for you");
}
const alertBack = () => {
setHim("is back");
}
useImperativeHandle(ref, () => ({ alertLeave, alertBack }));
return (
<Container color={"#F1B656"} colorName={"#296766"}>
<p>{props.name}</p>
<span><h6>Marlin {him}</h6></span>
</Container>
);
});
export default Child;
O fluxo das ações descritas acima se comporta da seguinte forma:
Espero que o surto já tenha sido dissolvido ao fim dessa leitura :).
O código completo das demonstrações está disponível, ó: https://github.com/matheusarauj/demoTypescriptHooks
Top comments (0)