Uma das maiores dúvidas de quem utiliza o React para o desenvolvimento de interfaces é como utilizar os hooks (react hooks) que a biblioteca oferece.
Existem vários hooks disponíveis nessa biblioteca para serem utilizados, tais como useState, useEffect, useMemo, dentre outros;
Ainda, caso seja necessário, você também pode criar seu próprio hook customizado. Cada um deles possui sua particularidade e é utilizado em determinado momento da aplicação.
Tudo pronto para conhecer mais sobre os hooks utilizados no React.js, para o desenvolvimento de aplicações? Vamos nessa!
No artigo, abordaremos:
- O que são Hooks?
- Quando usar Hooks? Quais problemas ele resolve?
- O que o React Hooks oferece?
- Quais as regras do React Hooks?
- Os 6 Hooks mais utilizados!
- Custom Hooks: construindo seus próprios hooks!
- Instalando o React Hooks
Boa leitura!
O que são Hooks?
Traduzindo para o português, “Hooks” significa ganchos. Esses hooks foram introduzidos no React a partir da versão 16.8 da biblioteca, sendo recursos que permitem que você gerencie o estado, ciclos de vida do componente e outros recursos do React sem precisar escrever componentes em forma de classes.
Antes da inclusão dos hooks nessa biblioteca, os componentes eram escritos em forma de classes, utilizando funções como setState(), componentDidMount(), dentre outras.
Abaixo, vejamos como era a escrita de um componente com classes:
import React, { Component } from 'react';
class ComponenteClasseExemplo extends Component {
render() {
return (
<div>Exemplo de um componente com classes</div>
);
}
}
Mas, isso quer dizer que tudo que aprendi de React com classes não serviu de nada? Não necessariamente. Alguns projetos legados ainda utilizam esse método nas aplicações e, caso você saiba manipular componentes com classe, é mais um diferencial em seu currículo.
Vejamos, o mesmo exemplo acima reescrito, sem a criação de classes:
import React from 'react';
function ComponenteHookExemplo {
return (
<div>
Exemplo de um componente sem classes.
</div>
);
}
O componente fica muito mais simples, não é? Além de deixar nosso código legível para outras pessoas programadoras poderem entender, nossa aplicação utilizando Hooks ficará muito mais robusta, com um melhor desempenho.
A seguir, vejamos quando os ganchos podem ser utilizados e quais tipos de problema ele pode resolver.
Quando usar Hooks? Quais problemas ele resolve?
Anteriormente, para adicionar algum estado em um componente, você precisaria converter ele em uma classe. Entretanto, agora, você pode utilizar um gancho qualquer dentro do componente escrito com funções, sem precisar reescrever ele.
Certo, mas como sei o momento certo de utilizar um hook? Vamos imaginar uma tela de login que, se uma pessoa usuária está logada, uma variável é igual a true (verdadeiro). A partir do momento que ela não está logada no sistema ou ela sai do sistema, a variável é igual a false (falso).
Percebe que houve uma mudança de estado? Caso a pessoa esteja logada, de verdadeiro ou falso? Esse gerenciamento de estado fazemos através do hook useState(), no React. Ele é um dos mais utilizados pelos programadores e programadoras.
Não se preocupe que, adiante, teremos mais exemplos de mais hooks e como eles funcionam, com exemplos práticos.
Por conseguinte, os problemas que os ganchos solucionam são os seguintes:
- Eles tornam possível organizar a lógica em componentes, tornando-os minúsculos e reutilizáveis sem precisar escrever um arquivo .class. Não há nada de errado em escrever várias classes em uma aplicação, porém, a renderização delas acabavam pesando nos navegadores e o resultado é uma árvore de elementos (DOM) cheia de divisões (<div>) e, um código difícil de ser depurado.
- Hooks aplicam a filosofia do React dentro de um componente, em vez de apenas entre os componentes;
- Os ganchos não introduzem aninhamento desnecessário em sua árvore de componentes, como ocorre com as props de renderização ou componentes de ordem superior.
O que o React Hooks oferece?
Os React Hooks são funções JavaScript simples que podemos usar para isolar a parte reutilizável de um componente funcional. Ou seja, podemos separar vários componentes em nossa aplicação, separando cada responsabilidade em um arquivo separado, evitando o acoplamento dela.
Além disso, os hooks permitem que o código fique mais legível e mais simples comparado ao código de um componente com classes. O fluxo de informações, dessa forma, é realizado dentro de um componente sendo que, nas classes, o fluxo era realizado apenas entre os componentes, separadamente.
Ou seja, os ganhos de produtividade e economia de tempo utilizando os hooks são o seu principal objetivo, visto que não precisamos reescrever código. Ainda, caso façamos um componente como uma barra de pesquisa, por exemplo, ela pode ser reaproveitada na nossa aplicação sem precisar que o código seja reescrito.
Limitações do React Hooks
Os React Hooks precisam seguir as duas regras que eles possuem para sua implementação (elas serão explicadas a seguir no artigo). Caso haja algum ponto que não respeite essas regras, será acusado um erro.
Além disso, é necessário praticar algumas vezes os hooks tais como useState(), useEffect(), para aprender como eles funcionam. Não é necessário decorar o código de seu funcionamento, mas, é interessante aprender o porquê da sua utilização na aplicação e o motivo deles serem utilizados.
Quais as regras do React Hooks?
- Não devemos declarar hooks, seja ele para gerenciamento de estado, ciclo de vida, etc, dentro de laços de repetição ou estruturas condicionais. Devemos sempre declarar os ganchos no nível superior da função, sempre. Assim, não haverá problemas de renderização.
import { useState, useEffect } from 'react';
const Exemplo = () => {
// correto
const [todos, setTodos] = useState([]);
// errado - estrutura condicional
if (todos) {
const [count, setSetcount] = useState(todos.length);
}
// errado - laço de repetição
todos.forEach(todo => {
useEffect(() => {
console.log(todos);
});
});
// correto
useEffect(() => {
console.log(todos);
});
return (
// lógica a ser implementada
);
};
export default Exemplo;
- Ao fazer a invocação de uma função, não devemos fazer conforme o JavaScript regular. Devemos criar hooks personalizados ou componentes utilizando funções já criadas. Por exemplo:
import { useState } = "react";
function getEmail() {
const [email, setEmail] = useState("[email protected]");
return email;
}
document.getElementById("user-email").innerHTML = getEmail();
// O exemplo acima é inválido porque é uma função JavaScript padrão.
Uma boa prática para criação de hooks, é sempre começar com o prefixo use antes do nome da função (é desse prefixo que surgiram hooks como useEffect, useCallback, etc). A partir daí, você pode usá-lo para chamar outros ganchos, conforme o exemplo:
export default function useMeuEmail(email) {
const [email, setEmail] = useState(value);
useEffect(() => {
// lógica a ser implementada
});
return email;
}
Nas seções seguintes, haverá uma explicação de como criar seus próprios hooks utilizando o React.js.
Os 6 Hooks mais utilizados!
useState
Um dos hooks mais utilizados pelas pessoas desenvolvedoras, ele oferece a possibilidade de fazer componentes funcionais terem seu estado local através de uma API bem simples como você pode ver no exemplo abaixo.
import React, { useState } from "react";
const Rodape = () => {
const [extraInfo, setExtraInfo] = useState(false);
function handleClick() {
setExtraInfo(!extraInfo);
}
return (
<footer className="rodape">
<div>
<small>Copyright © 2022</small>
</div>
<br />
<button onClick={handleClick}>Mais informações.</button>
{extraInfo && (
<p>
Le Lorem Ipsum est simplement du faux texte employé dans la
composition et la mise en page avant impression.
</p>
)}
</footer>
);
};
export default Rodape;
Ou seja, esse gancho é utilizado para gerenciar estados. Ele retorna um valor com estado e uma função de atualização para atualizá-lo.
useEffect
Esse hook é utilizado para gerenciar efeitos colaterais como chamadas de API, assinaturas, temporizadores, mutações e muito mais.
No exemplo abaixo, vejamos os casos de uso em que o useEffect pode ser aplicado:
import React, { useEffect } from 'react'
// useEffect API -- Sintaxe
/*
useEffect(() => {
Effect: assinatura, chamadas a APIs, timers...
return () => {
Limpando: assinaturas, cancelando chamadas a API, limpando o timeOut.
};
}, [Dependências, propriedades de estado/props para rastrear]);
*/
const Component = ({ bookId, source }) => {
// sincroniza a renderização do componente com este efeito, sem necessidade de dependência(s)
// este efeito será acionado em cada renderização
useEffect(() => {
// execute isso logo após o final da renderização atual
// Exemplo: assinatura em um serviço
const subscription = source.subscribe();
return () => {
// execute isso antes da próxima renderização, este é o lugar para cancelar a assinatura do efeito
// Exemplo: "limpar" a assinatura
subscription.unsubscribe();
};
});
// sincronizar a renderização do componente com este efeito (dependência(s) vazia(s) [])
// esse efeito será disparado na primeira renderização
useEffect(() => {
// Exemplo: chamada a API para pegar os dados
return () => {
// execute isso logo antes do componente ser desmontado, porque a(s) dependência(s) está(ão) vazia(s) []
// Exemplo: cancelar a chamada da API
};
}, []);
// sincronizar a propriedade bookId com este efeito
// este efeito será acionado na mudança de dependência [bookId]
useEffect(() => {
// obter detalhes do livro por bookId da API sempre que bookId for alterado
}, [bookId]);
return (<div>Lide com os efeitos colaterais com useEffect<div>)
}
export default Component;
useContext
Esse hook nos dá a capacidade de passar dados, configurar dados em nosso aplicativo, descer a árvore de componentes sem usar prop drill (passar props por vários componentes) ou uma biblioteca de terceiros como redux.
Um exemplo disso seria o carrinho de compras de um site de uma loja virtual. Nele, você seleciona o produto e os seus dados vão para lá, havendo uma passagem de dados da tela de produtos para o carrinho de compras.
A lógica por trás da criação de contextos seria similar a imagem a seguir, onde DataContext seria a criação do contexto com os dados padrão e, no provider, passaríamos os valores que desejamos para os demais componentes, utilizando o value:
Os hooks a seguir são relacionados com o desempenho da aplicação.
useCallback
Este hook nos dá um bom ganho de desempenho ao memorizar o retorno de chamada fornecido e atualizá-lo quando as dependências fornecidas forem alteradas.
Imagine que você tenha um componente que renderiza uma grande lista de itens:
import useSearch from './fetch-items';
function ListaGrande({ term, onItemClick }) {
const items = useSearch(term);
const map = item => <div onClick={onItemClick}>{item}</div>;
return <div>{items.map(map)}</div>;
}
export default React.memo(ListaGrande);
A lista pode ser grande, contendo centenas de itens. Para evitar renderizações duplas e desnecessárias nesta lista, você a envolve na função React.memo().
O componente-pai de ListaGrande fornece uma função de manipulação para saber quando um item é clicado, conforme a linha doze do código a seguir:
import { useCallback } from 'react';
export function ComponentePai({ term }) {
const onItemClick = useCallback(event => {
console.log('Você clicou ', event.currentTarget);
}, [term]);
return (
<ListaGrande
term={term}
onItemClick={onItemClick}
/>
);
}
O onItemClick é memorizado pelo useCallback(). Se por um acaso a variável term seja igual, useCallback() retorna o mesmo objeto de função.
Quando o ComponentePai é renderizado novamente, o onItemClick permanece igual e não interrompe a memorização do componente ListaGrande.js.
useMemo
Este gancho armazena alguns dados em cache, economizando algum tempo de computação em nosso sistema e, como resultado, torna ele mais responsivo no processo.
Aqui está um exemplo abstrato do hook useMemo para uma matriz de itens que usa duas funções computacionalmente custosas em desempenho:
const Lista = React.useMemo(() =>
listaDeItens.map(item => ({
...item,
propItem1: funcaoComVariosDados1(props.primeiraProp),
propItem2: funcaoComVariosDados2(props.segundaProp)
})), [listaDeItens]
)
No exemplo acima, função useMemo seria executada na primeira renderização. Ele bloqueia o encadeamento até que as funções sejam concluídas, conforme o useMemo executado na primeira renderização.
Inicialmente, isso não parecerá tão legível quanto o uso do hook useEffect, pois esse hook pode renderizar um spinner de carregamento até que as funções terminem e os efeitos sejam disparados.
No entanto, em renderizações subsequentes, as funções não precisam ser executadas novamente, desde que listaDeItens nunca fosse alterada. Assim, o useMemo “lembraria” do valor de retorno de cada função.
Dessa forma, faria com que essas funções parecessem renderizadas instantaneamente. Isso é ideal se você tiver uma ou duas funções síncronas custosas em desempenho.
useRef
O useRef é um gancho que permite criar diretamente uma referência ao elemento DOM no componente funcional.
O useRef retorna um objeto ref mutável. Esse objeto tem uma propriedade chamada .current. O valor é persistido na propriedade refContainer.current. Esses valores são acessados a partir da propriedade atual do objeto retornado.
A propriedade .current pode ser inicializada com o argumento inicialValue passado, por exemplo, useRef(initialValue). O objeto pode persistir um valor por toda a vida útil do componente.
O exemplo a seguir se refere a como acessar elementos da árvore de elementos (DOM), que, nesse caso, seria um valor de um input com a propriedade focus():
import React, { useRef } from "react";
const TextoCustomizadoInput = () => {
const textoInput = useRef();
focusTextInput = () => textoInput.current.focus();
return (
<>
<input type="text" ref={textoInput} />
<button onClick={focusTextInput}>Foco no input de texto</button>
</>
);
}
O useRef() também pode ser utilizado para acessar, além dos elementos DOM, elementos do próprio React também.
Custom Hooks: construindo seus próprios hooks!
Conforme dito anteriormente, todo hook customizado precisará ter a instrução use, antes do nome do hook, por uma boa prática. Ou seja, useSearch, usePaper, são alguns exemplos de nomenclatura que devemos utilizar.
Vamos criar um hook personalizado para buscar dados de uma API. Dada uma URL, esse gancho retornará um objeto contendo os dados e uma mensagem de erro opcional. Esse hook poderá ser utilizado em um componente, dessa forma.
Comece criando um novo arquivo chamado useFetch.js. Nesse arquivo, crie uma função chamada useFetch() que aceita uma string de URL como parâmetro:
const useFetch = (url) => {
}
O hook deve fazer a chamada da API imediatamente após ser chamada. Você pode usar o hook useEffect() para isso.
Dessa forma, adicionando algumas informações no arquivo useFetch.js, ele ficará da seguinte forma:
import { useEffect, useState } from "react";
const useFetch = (url) => {
const [data, setdata] = useState(null);
const [loading, setloading] = useState(true);
const [error, seterror] = useState("");
useEffect(() => {
fetch(url)
.then((res) => res.json())
.then((data) => {
seterror(data.error)
setdata(data.joke)
setloading(false)
})
}, [url]);
return { data, loading, error };
};
export default useFetch;
Neste hook, você está inicializando primeiro o estado de três valores (as três variáveis iniciais, utilizando useState):
- data: contém a resposta da API;
- error: Mantém uma mensagem de erro se ocorrer um erro;
- loading: contém um valor booleano que indica se ele buscou os dados da API. Inicialize o estado de carregamento como verdadeiro. Depois que a API retornar dados, defina-os como false.
O gancho useEffect() recebe a string de URL como argumento. Isso é para garantir que ele seja executado sempre que a URL for alterada. A função useFetch() retorna um objeto contendo os dados, carregamento e valores de erro.
Perfeito, já criamos o hook mas, agora, precisamos utilizá-lo. Inicialmente, vamos importar ele no arquivo Piadas.js (componente que utilizaremos uma API de piadas):
const useFetch = require("./useFetch")
Após isso, vamos utilizar ele da seguinte forma, utilizando a desconstrução, presente no JavaScript:
const {data, loading, error} = useFetch(url)
Para demonstrar, considere o seguinte componente Piadas:
const Piadas = () => {
const url = "<https://sv443.net/jokeapi/v2/joke/Programming?type=single>"; // api de piadas
const { data, loading, error } = useFetch(url);
if (loading) return (
<div>Carregando...</div>
)
return (
<div>
{error && <div>{error}</div>}
{data && <div>{<div>{data}</div>}</div>}
</div>
);
};
export default Piadas;
Ele chama o hook useFetch() com a URL para a API de piadas e recebe os dados, carregamento e valores de erro. Logo, para renderizar o componente Piadas, primeiro verifique se a variável loading é true. Se ela for, exiba uma instrução “carregando…”, caso contrário, renderize os dados e a mensagem de erro, se houver.
Instalando o React Hooks
Para instalar o React Hooks em seu projeto, você precisará ter um projeto React criado do zero, utilizando o comando npx create-react-app minha_aplicacao. Tendo esse projeto criado, você já terá ferramentas como NPM (gerenciador de pacotes) e o Node.js instalado.
Caso não possua, é necessário instalar o NPM, o Node.js e criar uma aplicação React, utilizando o comando acima, em seu terminal. O nome da aplicação poderá ser o de sua preferência.
Depois disso, na pasta do seu projeto, você utilizará ou o terminal do Windows ou o próprio terminal integrado no programa Visual Studio Code e, digitar o comando a seguir:
npm install react-hooks
Lembrando que, esse comando precisará ser executado na pasta do projeto, pois, caso contrário, a instalação será feita fora do local correto. E, além disso, a versão do React utilizada precisa ser igual ou superior a versão 16.8 da biblioteca, versão que os hooks foram inseridos.
O React Hooks veio para resolver um problema muito comum que todas as pessoas desenvolvedoras que utilizam React possuem, que seria o da organização da lógica em componentes. Neles, é possível criar componentes funcionais para se beneficiar de sua simplicidade e eficiência para uma base de código escalável, sustentável e testável.
Eles possuem algumas regras de implementação que devem ser respeitadas a fim de não causar maiores problemas em nosso sistema. Como vimos, existe um hook para gerenciamento de estado (useState), gerenciamento de vida do componente (useEffect), para economia de processamento na aplicação (useMemo), dentre outros.
Para aprofundar seus conhecimentos no React, conheça como funciona o Yup, para fazer validações de formulários.