No universo do React, os hooks representam uma ferramenta poderosa para a gestão de efeitos colaterais dentro dos componentes. Entre os mais utilizados para essa finalidade, destacam-se o `useEffect`, `useLayoutEffect` e `useEffectEvent`. Cada um desses hooks possui um contexto de uso específico, sendo crucial a escolha adequada para cada situação.
O Hook `useEffect`
O `useEffect` é um hook fundamental no React, que possibilita a execução de efeitos colaterais em componentes funcionais, como manipulação do DOM, operações assíncronas e busca de dados. Este hook recebe dois argumentos: a função de efeito e a array de dependências.
A função de efeito contém o código que realiza o efeito colateral. A array de dependências, por sua vez, determina quando a função de efeito será executada. Se a array de dependências estiver vazia, a função de efeito será executada apenas uma vez, na primeira renderização do componente. Caso contrário, a função de efeito será executada sempre que um dos valores presentes na array de dependências sofrer alteração.
A seguir, um exemplo de como utilizar o hook `useEffect` para buscar dados:
import React from "react";function App() { const [data, setData] = React.useState([]);
React.useEffect(() => { fetch("<https://jsonplaceholder.typicode.com/posts>") .then((response) => response.json()) .then((data) => setData(data)); }, []);
return ( <div className="app"> {data.map((item) => ( <div key={item.id}>{item.title}</div> ))} </div> ); }
export default App;
Este código demonstra um componente `App` que realiza a busca de dados de uma API externa usando o hook `useEffect`. A função de efeito do `useEffect` obtém dados de exemplo da API JSONPlaceholder. A resposta JSON é então analisada, e os dados recuperados são definidos no estado dos dados.
Com os dados em estado, o componente `App` renderiza a propriedade `title` de cada item presente no estado.
Características do Hook `useEffect`
- Compatível com operações assíncronas: oferece suporte nativo a operações assíncronas, o que facilita a busca de dados.
- Execução após a renderização: o hook `useEffect` executa seus efeitos somente após a renderização do componente, garantindo que a interface do usuário não seja bloqueada.
- Função de limpeza: disponibiliza um mecanismo integrado para realizar a limpeza, retornando uma função. Isso é particularmente útil ao lidar com listeners ou subscriptions.
O Hook `useLayoutEffect`
O hook `useLayoutEffect` é similar ao `useEffect`, mas é executado de forma síncrona após todas as alterações do DOM. Isso significa que sua execução ocorre antes que o navegador pinte a tela, tornando-o adequado para tarefas que exigem controle preciso sobre o layout e estilos do DOM, como a medição do tamanho de um elemento, redimensionamento ou animação de sua posição.
A seguir, um exemplo de como usar o hook `useLayoutEffect` para alterar a largura de um elemento botão:
import React from "react";function App() { const button = React.useRef();
React.useLayoutEffect(() => { const { width } = button.current.getBoundingClientRect();
button.current.style.width = `${width + 12}px`; }, []);
return ( <div className="app"> <button ref={button}>Click Me</button> </div> ); }
export default App;
O trecho de código apresentado acima incrementa a largura do elemento botão em 12 pixels, utilizando o hook `useLayoutEffect`. Isso assegura que a largura do botão seja alterada antes que o mesmo seja exibido na tela.
Características do Hook `useLayoutEffect`
- Síncrono: executa-se de maneira síncrona, podendo bloquear a interface do usuário se a operação em seu interior for pesada.
- Leitura/escrita no DOM: é mais apropriado para operações de leitura e escrita diretamente no DOM, sobretudo quando se necessita de alterações antes da repintura do navegador.
O Hook `useEffectEvent`
O hook `useEffectEvent` é um hook do React que visa solucionar os problemas relacionados às dependências do hook `useEffect`. Para quem já está familiarizado com `useEffect`, sabe que a gestão da sua array de dependências pode se tornar complexa, muitas vezes exigindo a inclusão de valores que não são estritamente necessários.
Por exemplo:
import React from "react";function App() { const connect = (url) => { };
const logConnection = (message, loginOptions) => { };
const onConnected = (url, loginOptions) => { logConnection(`Connected to ${url}`, loginOptions); };
React.useEffect(() => { const device = connect(url); device.onConnected(() => { onConnected(url); });
return () => { device.disconnect(); }; }, [url, onConnected]);
return <div></div>; }
export default App;
O código acima demonstra um componente `App` que gerencia uma conexão com um serviço externo. A função `connect` estabelece a conexão com um URL específico, enquanto `logConnection` registra os detalhes da conexão. Por fim, a função `onConnected` chama `logConnection` para registrar uma mensagem de sucesso na conexão quando o dispositivo se conecta.
O hook `useEffect` chama a função `connect` e, em seguida, define uma função de callback `onConnected` para ser executada quando o dispositivo acionar o evento `onConnected`. Este callback registra uma mensagem de conexão. Ele também retorna uma função de limpeza, que é executada quando o componente é desmontado. Esta função de limpeza desconecta o dispositivo.
A array de dependências possui a variável `url` e a função `onConnected`. O componente `App` criará a função `onConnected` a cada renderização, fazendo com que a função `useEffect` seja executada em loop e continue renderizando o componente `App` novamente.
Há diversas formas de solucionar o problema do loop `useEffect`. No entanto, a forma mais eficiente de realizar essa tarefa, sem adicionar valores desnecessários à array de dependências, é através do hook `useEffectEvent`.
import React from "react";function App() { const connect = (url) => { };
const logConnection = (message, loginOptions) => { };
const onConnected = React.useEffectEvent((url, loginOptions) => { logConnection(`Connected to ${url}`, loginOptions); });
React.useEffect(() => { const device = connect(url); device.onConnected(() => { onConnected(url); });
return () => { device.disconnect(); }; }, [url]);
return <div></div>; }
export default App;
Ao envolver a função `onConnected` com o hook `useEffectEvent`, este hook sempre tem acesso aos valores mais recentes dos parâmetros `message` e `loginOptions` antes de passá-los para o hook `useEffect`. Isso implica que `useEffect` não necessita depender da função `onConnected` ou dos valores passados a ela.
O hook `useEffectEvent` é útil quando se deseja que o `useEffect` dependa de um valor específico, mesmo quando o efeito aciona um evento que requer outros valores que não se deseja utilizar como dependências no `useEffect`.
Características do Hook `useEffectEvent`
- É mais adequado para efeitos colaterais desencadeados por eventos.
- O hook `useEffectEvent` não funciona com manipuladores de eventos como `onClick`, `onChange`, etc.
O hook `useEffectEvent` ainda está em fase experimental e não está disponível nos hooks do React versão 18.
Quando Usar Cada Hook?
Cada um dos hooks de busca de dados mencionados acima é apropriado para diferentes situações:
- Busca de dados: o `useEffect` é uma excelente opção.
- Manipulações diretas do DOM: se forem necessárias alterações síncronas no DOM antes da repintura, o `useLayoutEffect` é a melhor escolha.
- Operações leves: para operações que não apresentam risco de bloquear a interface do usuário, o `useEffect` pode ser utilizado sem problemas.
- Efeitos colaterais acionados por eventos: utilize o hook `useEffectEvent` para agrupar os eventos e o hook `useEffect` para a execução dos efeitos colaterais.
Gerenciando Efeitos Colaterais de Forma Eficiente
Os hooks do React abrem um leque de possibilidades, e compreender as diferenças entre `useEffect`, `useLayoutEffect` e `useEffectEvent` pode impactar de forma significativa a forma como se lida com efeitos colaterais e a manipulação do DOM. É crucial considerar os requisitos e as implicações específicas de cada um desses hooks para criar aplicações que sejam fáceis de utilizar.