Usando os Hooks no Typescript
Hoje, você irá aprender a fazer o uso dos hooks mais famosos do React usando o Typescript:
useState
useEffect
useCallback
useRef
useMemo
useContext
Vamos nessa? 😉
Usando useState com Typescript
No React, usamos o comando useState
para controlar determinado estado da nossa aplicação, o que significa dizer que estamos controlando uma determinada informação que esta sendo mostrada para o usuário.
Para mais informações sobre o funcionamento do useState
no React, acesse este link.
Como nós já vimos na lição passada, usamos o useState
dentro dos nossos componentes da seguinte forma:
InformacoesEstados > index.tsx
:
import React, { useState } from 'react';
const InformacoesEstados: React.FC = () => {
// Definindo os estados usando useState
const [nome, setNome] = useState<string>('João');
const [idade, setIdade] = useState<number>(30);
const [humano, setHumano] = useState<boolean>(true);
const [caracteristicas, setCaracteristicas] = useState<{
altura: number;
peso: number;
cor: string;
}>({
altura: 1.80,
peso: 75,
cor: 'azul',
});
const [numerosDaSorte, setNumerosDaSorte] = useState<number[]>([7, 13, 21]);
// Renderizando o componente
return (
<div>
<h1>Informações</h1>
<p><strong>Nome:</strong> {nome}</p>
<p><strong>Idade:</strong> {idade}</p>
<p><strong>Humano:</strong> {humano ? 'Sim' : 'Não'}</p>
<p><strong>Características:</strong></p>
<ul>
<li><strong>Altura:</strong> {caracteristicas.altura} m</li>
<li><strong>Peso:</strong> {caracteristicas.peso} kg</li>
<li><strong>Cor:</strong> {caracteristicas.cor}</li>
</ul>
<p><strong>Números da Sorte:</strong></p>
<ul>
{numerosDaSorte.map((numero, index) => (
<li key={index}>{numero}</li>
))}
</ul>
</div>
);
};
export default InformacoesEstados;
Como cada uma das variáveis acima está conectada a um estado, podemos alterar esses estados tranquilamente por meio do comando setState()
de dentro de uma função.
InformacoesEstados > index.tsx
:
import React, { useState } from 'react';
const InformacoesEstados: React.FC = () => {
// Definindo os estados usando useState
const [nome, setNome] = useState<string>('João');
const [idade, setIdade] = useState<number>(30);
const [humano, setHumano] = useState<boolean>(true);
const [caracteristicas, setCaracteristicas] = useState<{
altura: number;
peso: number;
cor: string;
}>({
altura: 1.80,
peso: 75,
cor: 'azul',
});
const [numerosDaSorte, setNumerosDaSorte] = useState<number[]>([7, 13, 21]);
// Função para mudar o nome de João para Maria
const mudarNomeParaMaria = () => {
setNome('Maria');
};
// Renderizando o componente
return (
<div>
<h1>Informações</h1>
<p><strong>Nome:</strong> {nome}</p>
<p><strong>Idade:</strong> {idade}</p>
<p><strong>Humano:</strong> {humano ? 'Sim' : 'Não'}</p>
<p><strong>Características:</strong></p>
<ul>
<li><strong>Altura:</strong> {caracteristicas.altura} m</li>
<li><strong>Peso:</strong> {caracteristicas.peso} kg</li>
<li><strong>Cor:</strong> {caracteristicas.cor}</li>
</ul>
<p><strong>Números da Sorte:</strong></p>
<ul>
{numerosDaSorte.map((numero, index) => (
<li key={index}>{numero}</li>
))}
</ul>
<button onClick={mudarNomeParaMaria}>Mudar Nome para Maria</button>
</div>
);
};
export default InformacoesEstados;
Note que ao clicar no botão [Mudar Nome para Maria], será acionado uma função chamada mudarNomeParaMaria()
, que por sua vez, é responsável por alterar o valor do estado nome
.
E é dessa forma que usamos o useState
em conjunto com o ReactTS 🙃
Lembrando que podemos criar estados que armazenam objetos, variáveis, funções, classes e etc... basta apenas que você siga os ensinamentos da lição de estados.
Usando o UseEffect com Typescript
No caso do hook useEffect
, a sua utilização não se difere muito daquilo que estamos acostumados a fazer.
Para mais informações sobre o funcionamento do useEffect
no React, acesse este link.
Para testarmos o seu uso, vamos criar um novo componente chamado de Efeito
.
Efeito > index.jsx
:
import React, { useEffect } from 'react';
interface EfeitoProps {
mensagem: string;
}
const Efeito: React.FC<EfeitoProps> = ({ mensagem }) => {
useEffect(() => {
console.log(`Componente montado ou a mensagem mudou: ${mensagem}`);
// Esta função é chamada quando o componente é desmontado
return () => {
console.log('Componente desmontado');
};
}, [mensagem]); // Dependência: o useEffect será chamado sempre que a prop 'mensagem' mudar
return (
<div>
<h1>{mensagem}</h1>
</div>
);
};
export default Efeito;
Note que a sintaxe do useEffect
permaneceu a mesma com aquela que já estamos acostumados, ou seja, nada mudou.
Lembrando que se você for delclarar alguma lógica dentro do useEffect
, não se esqueça de fazer isso usufruindo da tipagem do Typescript.
Usando o useCallback com Typescript
Usamos o useCallback
para salvar na memória da nossa aplicação uma versão memoizada de uma determinada função, o que ajuda a otimizar o desempenho das nossas aplicações.
Para mais informações sobre o funcionamento do useCallback
no React, acesse este link.
Para testarmos o seu funcionamento, que tal criar um componente que representa um contador?
Contador > index.tsx
:
import React, { useCallback, useState } from 'react';
const Contador: React.FC = () => {
const [count, setCount] = useState<number>(0);
// Função de incremento memorada com useCallback
const incrementar = useCallback(() => {
setCount(prevCount => prevCount + 1);
}, []); // Dependências vazias, a função só será criada uma vez
return (
<div>
<p>Contador: {count}</p>
<button onClick={incrementar}>Incrementar</button>
</div>
);
};
export default Contador;
Note que estamos fazendo o uso do useState
para armazenar a contagem do estado na nossa aplicação.
Já o useCallback
permanece com a mesma sintaxe daquela que conhecemos antes.
Usando o useRef com Typescript
O useRef
é usado para armazenar a referência de um elemento DOM, ou quem sabe, um valor mutável que não causa a re-renderização do componente quando este for alterado.
Para mais informações sobre o funcionamento do useRef
no React, acesse este link.
Para testarmos ele em conjunto com o Typescript, vamos criar um novo componente chamado de FocoInput
.
FocoInput > index.tsx
:
import React, { useRef, useEffect } from 'react';
const FocoInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
}, []);
return <input ref={inputRef} type="text" />;
};
export default FocoInput;
No caso do comando acima, fizemos o uso do useRef
, onde precisamos declarar o tipo de valor que ele irá controlar, que no caso, é um elemento do tipo <Input>
(HTMLInputElement
).
Além disso, fizemos o uso do useEffect
para nos ajudar a focar no <input>
assim que o usuário renderizasse o componente.
Usando o useMemo com Typescript
O useMemo
é usado para memorizar o resultado de uma função de cálculo, de modo a evitar a reexecução desse cálculo a menos que suas dependências mudem.
Para mais informações sobre o funcionamento do useMemo
no React, acesse este link.
Para testarmos ele em conjunto com o Typescript, vamos criar um novo componente chamado de Calculadora
.
Calculadora > index.tsx
:
import React, { useMemo, useState } from 'react';
const Calculadora: React.FC = () => {
const [número, setNúmero] = useState<number>(0);
// Função de cálculo cara que só deve ser executada quando o número muda
const cálculoCaro = (n: number) => {
console.log('Calculando...');
let resultado = 0;
for (let i = 0; i < 1000000000; i++) {
resultado += n * i;
}
return resultado;
};
// Usando useMemo para memorizar o resultado do cálculo
const resultado = useMemo(() => cálculoCaro(número), [número]);
return (
<div>
<p>Resultado do cálculo: {resultado}</p>
<button onClick={() => setNúmero(prev => prev + 1)}>Incrementar Número</button>
</div>
);
};
export default Calculadora;
Note que a declaração do useMemo
permanece com a mesma sintaxe daquela que vimos no ReactJS 😉
Usando o useContext com Typescript
O Context API
(useContext
) é um hook bastante utilizado, que nos permite transmitir dados entre componentes dentro da nossa aplicação feita com React.
Para mais informações sobre o funcionamento do useContext
no React, acesse este link.
Vamos começar criando dentro da pasta contexts um novo contexto chamado de UserContext.tsx
.
Contexts > UserContext.tsx
:
// UserContext.tsx
import React, { createContext, useState, ReactNode, useContext } from 'react';
interface UserContextType {
name: string;
age: number;
setName: (name: string) => void;
setAge: (age: number) => void;
}
const UserContext = createContext<UserContextType | undefined>(undefined);
export const UserProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
const [name, setName] = useState<string>('');
const [age, setAge] = useState<number>(0);
return (
<UserContext.Provider value={{ name, age, setName, setAge }}>
{children}
</UserContext.Provider>
);
};
export const useUser = () => {
const context = useContext(UserContext);
if (context === undefined) {
throw new Error('useUser must be used within a UserProvider');
}
return context;
};
Note que no comando acima, estamos fazendo o uso das boas práticas da Interface
, onde todos os estados existentes ali, foram declarados dentro da nossa interface chamada de UserContextType
.
Note também que estamos tipando o createContext
, que pode receber uma interface (UserContextType
) quanto também algo indefinido (undefined
), uma vez que o contexto é iniciado como indefinido ou vazio (null
) antes de receber a interface propriamente dita.
Nós também precisamos tipar o createContext
para que o nosso contexto consiga compreender os tipos de dados que ele estará trafegando.
Em seguida, vamos criar um novo componente chamado de UserComponent
.
UserComponent > index.tsx
:
// UserComponent.tsx
import React, { useState } from 'react';
import { useUser } from '../../contexts/UserContext';
const UserComponent: React.FC = () => {
const { name, age, setName, setAge } = useUser();
const [newName, setNewName] = useState('');
const [newAge, setNewAge] = useState<number>(0);
const handleUpdate = () => {
setName(newName);
setAge(newAge);
};
return (
<div>
<h1>User Information</h1>
<p>Name: {name}</p>
<p>Age: {age}</p>
<input
type="text"
placeholder="Enter name"
value={newName}
onChange={(e) => setNewName(e.target.value)}
/>
<input
type="number"
placeholder="Enter age"
value={newAge}
onChange={(e) => setNewAge(Number(e.target.value))}
/>
<button onClick={handleUpdate}>Update</button>
</div>
);
};
export default UserComponent;
Observe que no comando acima, estamos fazendo o uso do nosso contexto que acabamos de criar, e o estamos recuperando por meio do useUser
.
Por fim, não se esqueça de envolver a sua aplicação (App.tsx
) para que todos os outros componentes (ou rotas) possam acessar o seu contexto:
import { UserProvider } from "./contexts/UserContext";
import UserComponent from "./components/UserComponent";
function App(){
return(
<UserProvider>
<UserComponent />
</UserProvider>
);
}
export default App;
No caso do Context API
, você pode observar um alto grau de tipagem do Typescript. Isso aconteceu pois o uso dos contextos envolvem a escrita de mais códigos, o que implica em mais tipagem.
Arquivos da lição
Os arquivos desta lição podem ser encontrados no repositório do GitHub por meio deste link.
Conclusão
Nesta lição, você aprendeu a utilizar todos os hooks mais importantes do React em conjunto com o Typescript.
Até a próxima lição!