useEffect
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çãouseReducer
- 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.
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>
);
}
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 é atualizadouseLayoutEffect
- 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.
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>
);
}
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 />;
}