Você já se perguntou como seria utilizar o TypeScript para tipar os hooks do React? E não estamos falando apenas de useState
ou useEffect
, mas também de hooks mais avançados, como useRef
, useMemo
e useCallback
. Se essa já foi a sua dúvida, você está no lugar certo.
Meu nome é Nailton, sou instrutor na escola de FrontEnd.
Audiodescrição: Nailton é um homem de pele negra, com olhos e cabelos escuros. No momento, está utilizando óculos com armação retangular e veste uma camisa amarela.
Seja bem-vindo a este curso de React, onde vamos aprender a tipar corretamente os hooks e também entender muito sobre a Context API e como o TypeScript nos ajuda a trabalhar e a fazer a gestão dos estados de forma global.
Qual aplicação iremos trabalhar? Estamos na aplicação do PolpaApp
, uma aplicação de gerenciamento financeiro. Nela, podemos inscrever nosso nome e renda mensal, clicar em "ir para o app" e ser levados para a tela de dashboard. Nesta tela, teremos nosso nome, um orçamento diário disponível, um gráfico com gastos por categoria e uma lista de transações, representando nossa movimentação financeira do dia.
Tudo isso será construído junto com o TypeScript e a Context API, para lidar com o gerenciamento de estados de forma global.
Neste curso, abordaremos como as transações afetam o orçamento diário disponível e como elas são utilizadas para calcular os gastos por categoria. Exploraremos como o TypeScript auxilia no trabalho com o contexto global e os estados globais das aplicações.
Além disso, aprenderemos a criar uma modal acessível. Por exemplo, ao clicar em "adicionar transação", uma modal será aberta, permitindo navegação através da tecla "Tab". Será possível fechá-la pressionando "Esc" ou clicando fora dela. Vamos aprender a implementar isso utilizando o Hook useRef
e o Hook useImperativeHandle
.
Também conectaremos uma API à nossa aplicação, criando-a do zero. Entenderemos como o TypeScript pode ser útil tanto no front-end quanto no back-end. Embora este curso não seja focado em back-end, é importante mostrar como tipar o retorno das funções assíncronas, o que é essencial nesse contexto.
Para acompanhar este curso, é necessário ter conhecimentos básicos de React e noções iniciais de TypeScript. Estamos ansiosos para aprender juntos ao longo deste curso. Vamos começar!
Na aplicação do PolpaApp, temos a tela inicial. Ao avançarmos para a URL /home
, visualizamos a tela da aplicação com nosso nome e o orçamento diário disponível. No entanto, essas informações estão fixas no código, ou seja, hardcoded. Precisamos trabalhar ao longo do curso para criar um contexto global para esses dados e estados, centralizando-os em um único local para consumo na aplicação.
Inicialmente, temos um botão no componente de movimentação financeira para adicionar transações. Ao clicarmos no botão de adicionar transação, uma modal é aberta, permitindo a adição de transações na aplicação. No entanto, por enquanto, essa funcionalidade não está completa e será desenvolvida. Um ponto a ser observado é que, embora a modal funcione e possamos visualizá-la, ela não fecha ao clicarmos fora dela. Ela apenas fecha ao clicarmos no botão de fechar (X) ou no botão de cancelar. Isso representa um problema de acessibilidade.
Podemos utilizar nossos conhecimentos em React e TypeScript para tornar essa modal mais acessível. No código, ao abrir o Visual Studio Code, procuramos dentro da pasta src
, em "components", a pasta de modal e acessamos o arquivo index.tsx
. Nesse arquivo, entre as linhas 11 e 18, encontramos a interface da modal com as modalProps
, que incluem um ícone, um título e uma propriedade para fechar. Vamos deletar a propriedade de fechar na linha 14, pois será substituída. Também deletaremos a propriedade "está aberta" na linha 15.
ModalProps
Para começar, vamos definir a interface ModalProps
sem as propriedades que serão removidas:
interface ModalProps {
icon: React.ReactNode;
titulo: string;
children: React.ReactNode;
aoClicar: () => void;
}
No componente de transação modal, acessando o index.tsx
, observamos que a modal recebe as propriedades de fechar e "está aberta". No componente de transações, na linha 62, o estado "está aberta modal" é passado de um nível superior para o componente de transação modal e, em seguida, para o componente de modal, caracterizando um prop drilling. Esse é um problema comum ao lidar com estados no React, que o Context API visa resolver.
Modal
O primeiro passo é refatorar a modal para torná-la mais leve e limpa, permitindo manipulação de abertura e fechamento, além de torná-la acessível com TypeScript. Já deletamos da interface as propriedades de fechar e "está aberta". Agora, dentro das props
do componente de modal, também deletamos essas propriedades.
Dentro do código, temos um useEffect
que verifica o valor current
da modal, retornado pelo useRef
. Ele verifica se a modal está aberta, utilizando a referência dialogNode
para mostrar ou fechar a modal. Esse useEffect
pode gerar problemas de renderização, pois roda quando a aplicação sofre um efeito colateral, ou seja, quando algum estado muda. Os estados monitorados estão na linha 48: "está aberta" e "ao fechar", que foram recebidos como props
e já deletados.
Como deletamos, não vamos mais precisar do useEffect
, então podemos deletar da linha 26 até a linha 48, e nossa modal está ficando mais limpa. Na linha 24, deixaremos o const dialog, dialogRef
, com o useRef
, passando para ele, como um generics, o HTMLDialogElement
. Já vimos que esses HTMLDialogElement
são uma espécie de atributos intrínsecos que todo elemento que podemos utilizar dentro do JSX possui. Temos dialogElement
, divElement
, inputElement
, e aqui estamos passando para um generics para tipar nosso useRef
.
forwardRef
no Componente Modal
Vamos refatorar o componente Modal
removendo o useEffect
e utilizando o useRef
:
const Modal = ({
icon,
titulo,
children,
aoClicar,
}: ModalProps) => {
const dialogRef = useRef<HTMLDialogElement>(null);
return (
<ModalOverlay>
<ModalContainer ref={dialogRef}>
<ModalHeader>
<div>
{icon} {titulo}
</div>
<CloseButton>x</CloseButton>
</ModalHeader>
{children}
<ButtonGroup>
<Botao $variante="secundario">
Cancelar
</Botao>
<Botao $variante="primario" onClick={aoClicar}>
Adicionar
</Botao>
</ButtonGroup>
</ModalContainer>
</ModalOverlay>
);
};
forwardRef
No closeButton
, temos um onClick
ao fechar, que podemos deletar por enquanto, e no botão na linha 38, também temos um onClick
de ao fechar. Queremos expor, por enquanto, estamos utilizando o useRef
para gerar uma referência na nossa modal, passando esse dialogRef
para o nosso modal container, mas também queremos expor esse elemento, no caso o dialogElement
, para o nosso componente pai, que utilizaremos no app de transações.
Para isso, podemos utilizar o forwardRef
. Estamos utilizando a versão 18 do React. No momento em que estiverem fazendo este curso, pode ser que já tenha sido lançada a versão 19 ou alguma posterior, na qual isso não será mais necessário. No entanto, no momento, estamos utilizando o forwardRef
. Inclusive, deixaremos uma atividade explicando as diferenças de como utilizamos com o forwardRef
e como provavelmente utilizaremos com a versão do React 19 e superior.
Por enquanto, estamos utilizando a versão 18, então precisamos fazer o seguinte: utilizar o forwardRef
na linha 18, logo antes de abrir os parênteses da aplicação, passando um forwardRef
. Para esse forwardRef
, é como se colocássemos todo o componente dentro dele. Colocamos um parêntese depois do nome forwardRef
e, na linha 48, um parêntese depois das chaves que fecham o componente. Dentro desse forwardRef
, temos as props do nosso componente e também a ref
. Passamos uma vírgula na linha 23, depois de modalprops
, e temos uma prop chamada ref
.
forwardRef
no Componente Modal
Vamos implementar o forwardRef
no componente Modal
:
const Modal = forwardRef<ModalHandle, ModalProps>(({
icon,
titulo,
children,
aoClicar,
}, ref) => {
const dialogRef = useRef<HTMLDialogElement>(null);
return (
<ModalOverlay>
<ModalContainer ref={dialogRef}>
<ModalHeader>
<div>
{icon} {titulo}
</div>
<CloseButton>x</CloseButton>
</ModalHeader>
{children}
<ButtonGroup>
<Botao $variante="secundario">
Cancelar
</Botao>
<Botao $variante="primario" onClick={aoClicar}>
Adicionar
</Botao>
</ButtonGroup>
</ModalContainer>
</ModalOverlay>
);
});
ModalHandle
Feito isso, para onde passaremos essa ref
? Pegamos essa ref
depois do modalprops
. Uma coisa interessante é que o modalprops
pode ser removido da linha 23. Vamos dar um "Ctrl+X" no modalprops
, pegar apenas as chaves com as props e passá-las ao lado do forwardRef
na linha 18. Abrimos a anotação de generics, passamos o modalprops
, que é a nossa interface, e teremos também outra interface que precisaremos criar, que é uma interface para lidar com os métodos da nossa modal. Vamos aproveitar e passar uma vírgula, e passar o modalhandle
antes do modalprops
. O modalhandle
ainda não existe, então vamos criá-lo.
Escreveremos interface modalhandle
, daremos um "Ctrl+V", abriremos e fecharemos as chaves. O modalhandle
terá dois métodos, na verdade, dois tipos. Vamos exportar essa interface, pois é importante, e utilizá-la em outro lugar. Na linha 18, escrevemos export interface modalhandle
. Dentro do modalhandle
, digitamos que ele terá dois métodos: um será open
, tipado como uma função que não retorna nada, então escrevemos dois pontos, abrimos e passamos a anotação de função de seta (arrow function), e escrevemos void
, indicando que a função não retorna nada. Teremos também close
, então escrevemos close
, dois pontos, abrimos e fechamos os parênteses, e passamos a anotação de função de seta, que são dois, sem o "e" no final, pois são duas funções, dois métodos que utilizaremos para controlar nossa modal de abrir e fechar. Vamos expor esses métodos futuramente.
Definindo a interface ModalHandle
:
export interface ModalHandle {
open: () => void;
close: () => void;
}
O useEffect
que não estamos utilizando pode ser removido da linha 1. O que fizemos foi justamente refatorar a modal, removendo o useEffect
que estávamos utilizando para aplicar um efeito colateral com base em um estado que recebíamos de um componente acima. Criamos essa nova interface, modalhandle
, que será muito importante agora. Precisamos expor os métodos para controlar a nossa modal, e ela receberá novas props. Vamos resolver isso em um próximo vídeo. Nos vemos lá.
useRef
e forwardRef
Na última aula, utilizamos o useRef
para criar uma referência para nossa modal e expusemos essa referência com o forwardRef
para o componente Py
. Também criamos uma interface chamada ModalHandle
, que possui dois métodos: open
e close
, ambos sem retorno. Isso é importante para expor esses métodos ao componente Py
, permitindo controlar a abertura e fechamento da modal sem depender do estado, como fazíamos anteriormente. Isso elimina o prop drilling que tínhamos em nossa aplicação.
fechaModal
Para expor esses métodos, utilizaremos o hook useImperativeHandle
. Antes disso, criaremos uma função chamada fechaModal
. Vamos começar definindo a função fechaModal
como uma constante:
const fechaModal = () => {}
Esta função receberá uma arrow function e, dentro dela, acessaremos o dialog.ref
. O useRef
é um hook que retorna um objeto com a propriedade current
, que representa o elemento no DOM. Muitas pessoas utilizam o useRef
para controlar diretamente o DOM, pois entre re-renderizações, esse valor não muda, ao contrário do estado. É muito útil para trabalhar com modais e carrosséis, por exemplo.
Agora, vamos acessar o dialogRef.current
dentro da função fechaModal
:
const fechaModal = () => {
dialogRef.current
}
No current
, precisamos verificar se ele existe. O TypeScript exige que tudo esteja correto, então utilizamos a interrogação para verificar apenas o current
. Se houver um current
, executamos uma ação; caso contrário, ignoramos. Utilizamos o método close
do dialog
:
const fechaModal = () => {
dialogRef.current?.close()
}
styled-components
e Configuração de useImperativeHandle
Na linha 33, temos o modalContainer
, que é a tag dialog
. Como estamos usando styled-components, temos um componente customizado. Uma curiosidade sobre styled-components é que, ao utilizá-lo com React e TypeScript, todos os tipos e atributos que uma tag HTML recebe são aceitos por padrão. Na linha 33, ao colocar o cursor no final do dialogRef
e pressionar Ctrl + Espaço
, aparecem todos os métodos aceitos pelo componente, incluindo close
e showModal
.
Isso evita o uso extensivo de atributos intrínsecos, que passávamos como generics ao definir as propriedades do componente. Com isso em mente, criamos a função fechaModal
e utilizamos o hook useImperativeHandle
. Certificamo-nos de que ele foi importado na linha 1:
import React, { forwardRef, useImperativeHandle, useRef } from "react";
Esse hook aceita alguns parâmetros, incluindo um ref
, que recebemos no componente na linha 24, e uma função. Vamos começar a configurar o useImperativeHandle
:
useImperativeHandle(ref, () => ({
}))
Retornamos um objeto com dois métodos: open
, que recebe uma função de seta e utiliza o dialogRef.current
com showModal
, e close
, que chama a função fechaModal
. Vamos adicionar o método open
:
useImperativeHandle(ref, () => ({
open: () => dialogRef.current?.showModal(),
}))
E agora, adicionamos o método close
:
useImperativeHandle(ref, () => ({
open: () => dialogRef.current?.showModal(),
close: fechaModal,
}));
Dessa forma, com o useImperativeHandle
, expomos esses dois métodos para o componente Py
ao utilizar a modal. As vantagens disso incluem maior controle e organização do código, além de evitar o prop drilling.
Como já mencionamos, evitamos o estado e o controle, que ficam dentro do nosso componente de modal. Podemos, então, dizer ao componente pai que ele pode utilizar determinados métodos. Podemos expor todos os métodos ou apenas os que desejarmos. No caso, estamos expondo apenas os métodos open
e close
para o nosso componente pai, permitindo que ele controle a abertura e fechamento da modal.
Outra coisa que precisamos fazer é que nossa modal receba outra prop. Queremos saber, por exemplo, se a pessoa usuária clicou fora da modal. Um problema que tínhamos era que a modal não fechava quando clicávamos fora. Vamos corrigir isso criando uma nova prop chamada cliqueForaModal
, que terá um valor padrão de true
. Vamos definir essa prop na interface ModalProps
:
interface ModalProps {
icon: React.ReactNode;
titulo: string;
children: React.ReactNode;
aoClicar: () => void;
cliqueForaModal?: boolean;
}
aoClicarForaModal
Vamos criar uma função na linha 37, uma constante chamada aoClicarForaModal
, que será uma arrow function. Queremos que, quando a pessoa usuária clicar fora da modal, chamemos um método para fechar a modal. Vamos fazer isso da forma mais correta possível. Dentro dos parênteses da função, teremos um evento do tipo React.MouseEvent
, que será um evento de clique. Queremos clicar fora da modal, então passamos React.MouseEvent
com a anotação de generics para um HTMLDialogElement
:
const aoClicarForaModal = (evento: React.MouseEvent<HTMLDialogElement>) => {
}
Dentro do corpo da função, faremos um if
para verificar duas condições: se cliqueForaModal
é verdadeiro e se event.target
é igual a dialogRef.current
. Ou seja, se foi clicado fora da modal, chamaremos a função fechaModal
:
const aoClicarForaModal = (evento: React.MouseEvent<HTMLDialogElement>) => {
if (cliqueForaModal && evento.target === dialogRef.current) {
fechaModal();
}
}
Vamos passar essa função aoClicarForaModal
no modalContainer
com o evento onClick
:
<ModalContainer ref={dialogRef} onClick={aoClicarForaModal}>
Como já temos a função fechaModal
, podemos utilizá-la também no botão da linha 56. No evento onClick
, passaremos uma função de seta que chamará o evento aoClicar
, que vem do componente pai, e também chamará fechaModal
:
<Botao $variante="primario" onClick={() => {
aoClicar();
fechaModal();
}}>
Adicionar
</Botao>
Podemos chamar fechaModal
também no botão de cancelar, pois ao cancelar a transação, a modal deve ser fechada. Passamos onClick
para chamar fechaModal
no closeButton
, que é o botão de X:
<Botao $variante="secundario" onClick={fechaModal}>Cancelar</Botao>
<CloseButton onClick={fechaModal}>x</CloseButton>
Para recapitular, estamos utilizando o useImperativeHandle
para expor os métodos de abrir e fechar a modal, showModal
e close
, para o componente pai. Podemos fazer isso com a modal ou com outros componentes, como um input, se quisermos expor algum método, como jogar o foco para o componente. Deixaremos uma atividade para explorar mais sobre o useImperativeHandle
e outros exemplos.
O que falta é testarmos nosso componente de modal, mas faremos isso no próximo vídeo. Nos vemos lá.
O curso React com Typescript: aplique Hooks e Context API tipados possui 173 minutos de vídeos, em um total de 56 atividades. Gostou? Conheça nossos outros cursos de React em Front-end, ou leia nossos artigos de Front-end.
Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:
Impulsione a sua carreira com os melhores cursos e faça parte da maior comunidade tech.
1 ano de Alura
Assine o PLUS e garanta:
Formações com mais de 1500 cursos atualizados e novos lançamentos semanais, em Programação, Inteligência Artificial, Front-end, UX & Design, Data Science, Mobile, DevOps e Inovação & Gestão.
A cada curso ou formação concluído, um novo certificado para turbinar seu currículo e LinkedIn.
No Discord, você tem acesso a eventos exclusivos, grupos de estudos e mentorias com especialistas de diferentes áreas.
Faça parte da maior comunidade Dev do país e crie conexões com mais de 120 mil pessoas no Discord.
Acesso ilimitado ao catálogo de Imersões da Alura para praticar conhecimentos em diferentes áreas.
Explore um universo de possibilidades na palma da sua mão. Baixe as aulas para assistir offline, onde e quando quiser.
Acelere o seu aprendizado com a IA da Alura e prepare-se para o mercado internacional.
1 ano de Alura
Todos os benefícios do PLUS e mais vantagens exclusivas:
Luri é nossa inteligência artificial que tira dúvidas, dá exemplos práticos, corrige exercícios e ajuda a mergulhar ainda mais durante as aulas. Você pode conversar com a Luri até 100 mensagens por semana.
Aprenda um novo idioma e expanda seus horizontes profissionais. Cursos de Inglês, Espanhol e Inglês para Devs, 100% focado em tecnologia.
Transforme a sua jornada com benefícios exclusivos e evolua ainda mais na sua carreira.
1 ano de Alura
Todos os benefícios do PRO e mais vantagens exclusivas:
Mensagens ilimitadas para estudar com a Luri, a IA da Alura, disponível 24hs para tirar suas dúvidas, dar exemplos práticos, corrigir exercícios e impulsionar seus estudos.
Envie imagens para a Luri e ela te ajuda a solucionar problemas, identificar erros, esclarecer gráficos, analisar design e muito mais.
Escolha os ebooks da Casa do Código, a editora da Alura, que apoiarão a sua jornada de aprendizado para sempre.