Skip to main content

Hooks

React possui vários Hooks que podem ser utilizados para resolver problemas específicios. Pense neles como uma caixinha de ferramentas, onde cada um possui uma função específica e vai te ajudar em algo. Também é possível construir seus próprios Hooks. Vamos ver alguns dos mais utilizados

Aqui é uma das partes do React que eu mais gosto, a variedade de Hooks que podemos utilizar para resolver problemas específicos é muito grande. Vamos ver alguns dos mais utilizados.

State Hooks

State hooks permite armazenar informações em um estado do componente, inclusive realizando atualizações da interface.

Para adicionar um estado a um componente existem dois Hooks:

  • useState - Utilizado para armazenar um estado em uma variável que pode ser atualizado através de uma função
  • useReducer - Utilizado para armazenar um estado em uma variável que poder ser atualizado através de uma lógica de reducer functions

O useState é o mais utilizado no dia-a-dia , mas o useReducer pode ser utilizado quando o estado é mais complexo e precisa de uma lógica mais elaborada para ser atualizado.

useState
import React, { useState } from "react";

function Wallet() {
const [balance, setBalance] = useState(0);

return (
<div>
<p>Saldo: {balance}</p>
<button onClick={() => setBalance(balance + 10)}>Depositar</button>
<button onClick={() => setBalance(balance - 10)}>Sacar</button>
</div>
);
}
useReducer
function reducer(state, action) {
switch (action.type) {
case "deposit":
return { ...state, balance: state.balance + action.payload };
case "withdraw":
return { ...state, balance: state.balance - action.payload };
default:
throw new Error();
}
}

function Wallet() {
const [state, dispatch] = useReducer(reducer, { balance: 0 });

return (
<div>
<p>Saldo: {state.balance}</p>
<button onClick={() => dispatch({ type: "deposit", payload: 10 })}>
Depositar
</button>
<button onClick={() => dispatch({ type: "withdraw", payload: 10 })}>
Sacar
</button>
</div>
);
}

O exemplo acima pode parecer confuso a uma primeira vista, sendo muito mais indicado o uso do useState para o caso simples de um saldo. Mas imagine que o saldo seja um objeto com várias informações, como o saldo atual, o limite, o nome do usuário, etc. Nesse caso o useReducer pode ser mais indicado, pois a lógica de atualização do estado pode ser mais complexa.

Aqui um erro comum utilizando useState que poderia ser evitado com useReducer:

function Wallet() {
const [balance, setBalance] = useState(0);
const [limit, setLimit] = useState(100);
const [bankName, setBankName] = useState("Banco Foo");

function deposit() {
setBalance(balance + 10);
setLimit(limit + 10);
setBank("Banco XPTO");
}

function withdraw() {
setBalance(balance - 10);
setLimit(limit - 10);
setBank("Banco Bar");
}

function changeBank() {
setBank("Banco XPTO");
}

return (
<div>
<p>Saldo: {balance}</p>
<button onClick={deposit}>Depositar</button>
<button onClick={withdraw}>Sacar</button>
<p>Limite: {limit}</p>
<p>Banco: {bankName}</p>
<button onClick={changeBank}>Trocar de banco</button>
</div>
);
}

No exemplo acima estou utilizando o useState para armazenar o saldo, o limite e o nome do banco. Quando o usuário clicar no botão de depositar, o saldo, o limite e o nome do banco serão atualizados. Mas o que acontece se eu quiser atualizar apenas o saldo? Ou apenas o limite? Ou apenas o nome do banco? Nesse caso eu teria que criar uma função para cada um dos casos, o que pode ser um pouco trabalhoso. Com o useReducer eu poderia criar uma função que recebe o tipo da atualização e o valor a ser atualizado, e a partir disso atualizar apenas o que eu quero. Sem contar que no exemplo acima é disparado três vezes o ciclo de atualização da página (re-render). E, é claro, uma complexidade a mais para o código.

function reducer(state, action) {
switch (action.type) {
case "deposit":
return { ...state, balance: state.balance + action.payload };
case "withdraw":
return { ...state, balance: state.balance - action.payload };
case "changeBank":
return { ...state, bankName: action.payload };
default:
throw new Error();
}
}

function Wallet() {
const [state, dispatch] = useReducer(reducer, {
balance: 0,
limit: 100,
bankName: "Banco Foo",
});

return (
<div>
<p>Saldo: {state.balance}</p>
<button onClick={() => dispatch({ type: "deposit", payload: 10 })}>
Depositar
</button>
<button onClick={() => dispatch({ type: "withdraw", payload: 10 })}>
Sacar
</button>
<p>Limite: {state.limit}</p>
<p>Banco: {state.bankName}</p>
<button
onClick={() => dispatch({ type: "changeBank", payload: "Banco XPTO" })}
>
Trocar de banco
</button>
</div>
);
}

Effect Hooks

Effect hooks permite executar uma função quando o componente é renderizado, ou quando um estado é atualizado. É muito utilizado para realizar chamadas a API, ou para executar uma função quando o componente é renderizado.

Para adicionar um efeito a um componente existem dois Hooks:

  • useEffect - Utilizado para executar uma função quando o componente é renderizado, ou quando um estado é atualizado
  • useLayoutEffect - Utilizado para executar uma função quando o componente é renderizado, ou quando um estado é atualizado, mas antes da atualização da interface

O useEffect é o mais utilizado no dia-a-dia , mas o useLayoutEffect pode ser utilizado quando é necessário executar uma função antes da atualização da interface.

useEffect
import React, { useState, useEffect } from "react";

function ShowBalance() {
const [balance, setBalance] = useState(0);

useEffect(() => {
fetch("https://api.tautorn.com.br/balance")
.then((response) => response.json())
.then((data) => setBalance(data.balance));
}, []);

return (
<div>
<p>Saldo: {balance}</p>
</div>
);
}
useLayoutEffect
import React, { useState, useLayoutEffect } from "react";
import App from "./App";

function ApplyTheme() {
const [theme, setTheme] = useState("light");

useLayoutEffect(() => {
document.body.style.backgroundColor = theme === "light" ? "#fff" : "#000";
}, [theme]);

return <App />;
}
tip

Conheça mais sobre os hooks em:

Referências: