Decidi começar uma nova série, onde irei fazer alguns compilados de temas sobre coisas que estou estudando ou já estudei antes. A ideia desses compilados é servir de referência para consulta a longo prazo, e também para compartilhar conhecimento.
Nesse primeiro compilado iremos ver alguns temas interessantes da biblioteca React.js.
Quais hooks de efeito temos no React?
Até a v18 do React só existiam os hooks useEffect e o useLayoutEffect pra lidar com efeitos colaterais,
o novo hook useEffectEvent chegou na v19 e veio para resolver um problema específico que surgia com esses efeitos, que vai ser explicado em outro compilado.
Antes de vermos cada hook, é importante entender porque eles existem, afinal que problema consigo resolver com eles? Que "efeitos colaterais" são esses?
Os "efeitos colaterais" são todos aqueles que não fazem parte direta da renderização do componente, como requisições a APIs, manipulação do DOM, timers, assinaturas de eventos ou integração com bibliotecas externas.
Cada hook consegue executar efeitos colaterais de forma diferente, o que vamos ver é qual deles usar dependendo do contexto.
useEffect
Sem dúvidas esse hook é o mais utilizado, é quase impossível não encontrar ele em projetos React. Alguns o usando para fazer requisições a APIs, lidar com preenchimento de formulários, etc.
Basicamente ele recebe uma callback como argumento e também recebe uma dependência opcional, que é uma lista de valores que, quando mudam, fazem com que a callback seja executada novamente.
Outro detalhe importante é que o useEffect só executa depois que o React aplica as mudanças no DOM e conclui a renderização, rodando de forma assíncrona em relação ao processo de render.
Fazendo uma requisição a uma API
Como falei anteriormente, é muito comum ver esse hook sendo usado para fazer requisições a APIs (Particularmente, não recomendo), normalmente assim:
const [data, setData] = useState(null);
useEffect(() => {
fetch("https://api.example.com/data")
.then((response) => response.json())
.then((data) => setData(data));
}, []);
Nesse exemplo, o hook é executado apenas uma vez e somente após o primeiro render. Se os estados ou valores externos mudarem, ele não será executado novamente.
Usando o array de dependências
O array de dependências é opcional e usado para controlar quando o hook deve ser executado.
- Se você não passar nenhum valor, o hook será executado sempre que o componente for renderizado.
- Se você passar apenas um array vazio, o hook será executado apenas uma vez, após o primeiro render.
- Se você passar um valor, o hook será executado após o primeiro render e quando o valor é alterado.
const [value, setValue] = useState("");
useEffect(() => {
console.debug(
"Executado após o primeiro render e toda vez que o valor é alterado",
);
});
useEffect(() => {
console.debug("Executado apenas após o primeiro render");
}, []);
useEffect(() => {
console.debug("Executado após o primeiro render e quando o valor é alterado");
}, [value]);
Evitando a execução do useEffect no primeiro render
Não existe uma maneira nativa de impedir que o useEffect execute no primeiro render, mas é possível usar o useRef para armazenar uma flag que indica se é o primeiro render ou não.
Isso funciona porque o useRef é uma referência que mantém seu valor entre renderizações, mas não causa uma nova renderização quando seu valor é alterado:
const [value, setValue] = useState("");
const firstRender = useRef(true);
useEffect(() => {
if (firstRender.current) {
firstRender.current = false;
return;
}
console.debug("Executado após o primeiro render e quando o valor é alterado");
}, [value]);
Nesse exemplo, o useEffect executa após o primeiro render e quando o valor é alterado. O "truque" com o useRef impede que o código interno seja executado no primeiro render.
Isso porque dentro da condição if (firstRender.current) é verificado se é o primeiro render, caso seja, o valor é alterado para false e a função retorna sem executar o restante do código.
Esse "truque" é bem útil para situações onde você precisa executar uma lógica apenas após o primeiro render.
É importante saber que em desenvolvimento, provavelmente você estará usando o StrictMode.
Isso pode apresentar comportamentos estranhos, como o useEffect sendo executado duas vezes em vez de uma vez,
isso não significa que houve um erro, apenas que o React está executando o hook duas vezes para garantir que o código esteja correto.
Nesse caso, o primeiro render vai entrar na condição e o segundo não (StrictMode), pois o valor de firstRender.current é alterado para false após o primeiro render.
Hook utilitário para evitar repetição
Se você precisa desse tipo de lógica muitas vezes, pode criar um hook personalizado para evitar repetição de código. Vou deixar uma sugestão de exemplo:
function useDidUpdateEffect({ callback, deps }) {
const firstRender = useRef(true);
useEffect(() => {
if (firstRender.current) {
firstRender.current = false;
return;
}
return callback();
}, deps);
}
useLayoutEffect
A estrutura desse hook é muito semelhante ao useEffect, recebendo uma callback como argumento e também uma lista de dependências opcional.
Mas a principal diferença é que o useLayoutEffect é executado antes do React atualizar o DOM, como um processo síncrono, o que pode causar problemas de renderização se não for usado com cuidado.

Ele se torna bem útil quando você precisa medir ou ajustar o layout do DOM antes que o usuário veja a tela, evitando “saltos” visuais (flicker).
Sendo bem comum em: tooltips, modais, animações e posicionamento dinâmico de elementos.
Exemplo: Ajustar largura baseada no próprio tamanho
Imagine um componente que precisa ler a largura real do elemento e salvar no estado antes que o usuário veja a tela.
import { useRef, useState, useLayoutEffect } from "react";
function Box() {
const ref = useRef<HTMLDivElement>(null);
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
const elementWidth = ref.current?.offsetWidth || 0;
setWidth(elementWidth);
}, []);
return (
<div>
<div ref={ref} style={{ width: "50%", background: "lightblue" }}>
Caixa
</div>
<p>Largura calculada: {width}px</p>
</div>
);
}
Com o useEffect, o usuário poderia ver rapidamente 0px antes da atualização — causando um pequeno “salto” visual.
A própria documentação do React recomenda usar o useEffect em vez do
useLayoutEffect.
Executando funções no unmount
Tanto o useEffect quanto o useLayoutEffect podem ser usados para executar funções quando um componente é desmontado.
Para isso, basta adicionar uma função no retorno do hook:
useEffect(() => {
return () => {
// Função de limpeza
};
}, []);
Essas funções são executadas antes do componente ser desmontado. Isso se torna muito útil para limpar listeners ou qualquer outra ação antes do componente sumir da tela.
Usando e removendo listeners
Para remover um listener, basta armazená-lo em uma variável e, em seguida, chamá-lo no retorno do hook:
const handleResize = useCallback(() => {
const elementWidth = ref.current?.offsetWidth || 0;
setWidth(elementWidth);
}, []);
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, [handleResize]);
O uso do hook useCallback é importante para evitar que a função seja recriada a cada render.
Nas versões atuais do React, já existe o React Compiler que aplica memoization automaticamente quando necessário.