O React é uma das bibliotecas JavaScript mais populares para a criação de interfaces de usuário. Ao criar essas interfaces, você pode precisar executar efeitos colaterais, como buscar dados de uma API, assinar eventos ou manipular o DOM.

É aqui que entra o poderoso Hook useEffect. Ele permite que você lide com esses efeitos colaterais de forma declarativa e eficiente, garantindo que sua interface de usuário permaneça responsiva e atualizada.

Quer você seja novo no React ou um desenvolvedor experiente, entender e dominar o useEffect é essencial para criar aplicativos robustos e dinâmicos. Neste artigo, você aprenderá como o Hook useEffect funciona e como usá-lo em seu projeto React.

O que é efeito colateral no React?

Ao trabalhar com componentes React, há momentos em que precisamos interagir com entidades ou executar ações fora do escopo do React. Essas interações externas são conhecidas como efeitos colaterais.

No React, a maioria dos componentes são funções puras, o que significa que eles recebem entradas (props) e produzem uma saída previsível (JSX), como visto no exemplo abaixo:

export default function App() {
  return <User userName="JaneDoe" />   
}
  
function User(props) {
  return <h1>{props.userName}</h1>; // John Doe
}

No entanto, os efeitos colaterais não são previsíveis porque envolvem interações fora do escopo normal do React.

Considere um exemplo em que você deseja alterar dinamicamente o título da aba do navegador para exibir o endereço userName do usuário. Embora possa ser tentador fazer isso diretamente no componente, não é a abordagem recomendada porque isso é considerado um efeito colateral:

const User = ({ userName }) => {
  document.title = `Hello ${userName}`; // ❌Never do this in the component body — It is a side effect.

  return <h1>{userName}</h1>;
}

A execução de efeitos colaterais diretamente no corpo do componente pode interferir no processo de renderização do nosso componente React.

Para evitar interferência, você deve separar os efeitos colaterais para que eles sejam renderizados ou funcionem somente após a renderização do nosso componente, garantindo uma separação clara entre o processo de renderização e quaisquer interações externas necessárias. Essa separação é feita com o Hook useEffect.

Entendendo os princípios básicos do useEffect

O hook useEffect foi projetado para imitar métodos de ciclo de vida como componentDidMount, componentDidUpdate e componentWillUnmount encontrados em componentes de classe.

Para usar o useEffect, você precisará importá-lo do “react” e, em seguida, chamá-lo em um componente de função (no nível superior do componente). Ele recebe dois argumentos: uma função de callback e um array de dependência opcional.

useEffect(callbackFn, [dependencies]);

Isso pode ser melhor escrito como:

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);
  • A função de callback contém o código a ser executado quando o componente for renderizado ou o valor da dependência for alterado. É aqui que você executa o(s) efeito(s) colateral(is).
  • A array de dependência especifica os valores que devem ser monitorados quanto a alterações. A função callback será executada quando qualquer valor dessa array for alterado.

Por exemplo, agora você pode corrigir o exemplo anterior para executar o efeito colateral corretamente em um Hook useEffect:

import { useEffect } from 'react';

const User = ({ userName }) => {
  useEffect(() => {
    document.title = `Hello ${userName}`;
  }, [userName]);
    
  return <h1>{userName}</h1>;   
}

No exemplo acima, o hook useEffect será chamado depois que o componente for renderizado e sempre que a dependência – o valor de userName– for alterado.

Como trabalhar com dependências no useEffect

As dependências desempenham um papel crucial no controle da execução do useEffect. É o segundo argumento do Hook useEffect.

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);

O uso de uma array de dependências vazia [] garante que o efeito seja executado apenas uma vez, simulando componentDidMount. Especificar as dependências corretamente permite que o efeito seja atualizado quando valores específicos mudam, similar ao componentDidUpdate.

Observação: Você deve ter cuidado ao lidar com dependências complexas. Atualizações desnecessárias podem ser evitadas se você selecionar cuidadosamente os valores a serem incluídos na array de dependência.

Se você omitir totalmente a array de dependência, o efeito será executado sempre que o componente for renderizado, o que pode levar a problemas de desempenho.

useEffect(() => {
  // code to run when the effect is triggered
});

No React, entender como funciona a renderização é uma grande vantagem, pois você saberá a importância do array de dependências.

Como funciona a renderização no React?

No React, a renderização gera a interface do usuário (UI) com base no estado atual e nas propriedades de um componente. Há diferentes cenários onde a renderização ocorre. A renderização inicial acontece quando um componente é renderizado ou montado pela primeira vez.

Além disso, uma alteração em state ou props de um componente aciona uma nova renderização para garantir que a interface do usuário (UI) reflita os valores atualizados. Os aplicativos React são criados com uma estrutura de componentes em forma de árvore, formando uma hierarquia. O React começa no componente raiz durante a renderização e renderiza recursivamente seus componentes dependentes.

Isso significa que todos os componentes serão renderizados se ocorrer uma alteração no componente raiz. É importante observar que chamar efeitos colaterais (que na maioria das vezes são funções caras) em cada renderização pode ser caro. Para otimizar o desempenho, você pode usar a array de dependência no Hook useEffect para especificar quando ele deve ser acionado, limitando as novas renderizações desnecessárias.

Uso avançado do useEffect: Limpeza de efeitos colaterais

O hook useEffect nos permite executar efeitos colaterais e fornece um mecanismo para limpar esses efeitos colaterais. Isso garante que todos os recursos ou assinaturas criados durante o efeito colateral sejam finalizados corretamente e evita vazamentos de memória.

Vamos explorar como você pode limpar os efeitos colaterais usando o hook useEffect:

useEffect(() => {
  // Perform some side effect

  // Cleanup side effect
  return () => {
    // Cleanup tasks
  };
}, []);

No trecho de código acima, a função de limpeza é definida como um valor de retorno no Hook useEffect. Essa função é chamada quando o componente está prestes a ser desmontado, ou antes, de uma nova renderização acontecer. Ela permite que você encerre quaisquer recursos ou assinaturas estabelecidas durante o efeito colateral.

Aqui estão alguns exemplos de uso avançado do Hook useEffect para encerrar efeitos colaterais:

1. Limpando intervalos

useEffect(() => {
    const interval = setInterval(() => {
        // Perform some repeated action
    }, 1000);
    return () => {
        clearInterval(interval); // Clean up the interval
    };
}, []);

Neste exemplo, configuramos um intervalo que executa uma ação a cada segundo. A função de limpeza cancela o intervalo para evitar que ele continue rodando após o componente ser desmontado.

2. Limpeza de ouvintes de eventos (Event Listeners)

useEffect(() => {
    const handleClick = () => {
        // Handle the click event
    };

    window.addEventListener('click', handleClick);

    return () => {
        window.removeEventListener('click', handleClick); // Clean up the event listener
    };
}, []);

Aqui, criamos um ouvinte de eventos para o evento de clique no objeto da janela. A função de limpeza remove o ouvinte de eventos para evitar vazamentos de memória e garantir a limpeza adequada.

Lembre-se de que a função de limpeza é opcional, mas é altamente recomendável que você limpe todos os recursos ou assinaturas para manter um aplicativo saudável e eficiente.

Uso do Hook useEffect

O hook useEffect permite que você execute tarefas que envolvam a interação com entidades ou APIs externas, como APIs da web como localStorage ou fontes de dados externas.

Vamos explorar o uso do hook useEffect em vários cenários:

1. Trabalhando com Web APIs (localStorage)

useEffect(() => {
 // Storing data in localStorage
  localStorage.setItem('key', 'value');
  // Retrieving data from localStorage
  const data = localStorage.getItem('key');
  // Cleanup: Clearing localStorage when component unmount
  return () => {
    localStorage.removeItem('key');
  };
}, []);

Neste exemplo, o hook useEffect é usado para armazenar e recuperar dados do localStorage do navegador. A função de limpeza garante que o localStorage seja limpo quando o componente for desmontado (esse pode não ser um bom caso de uso sempre, pois você pode querer manter os dados do localStorage até que o navegador seja atualizado).

2. Buscando dados de uma API externa

useEffect(() => {
  // Fetching data from an external API
  fetch('https://api.example.com/data')
    .then((response) => response.json())
    .then((data) => {
      // Do something with the data
    });
}, []);

Aqui, o Hook useEffect é usado para buscar dados de uma API externa. Os dados obtidos podem então ser processados e usados no componente. Não é obrigatório adicionar sempre uma função de limpeza.

Outros efeitos colaterais populares

O Hook useEffect pode ser usado para vários outros efeitos colaterais, como:

A. Inscrevendo-se em eventos:

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

B. Modificando o título do documento:

useEffect(() => {
  document.title = 'New Title';
  return () => {
    document.title = 'Previous Title';
  };
}, []);

C. Gerenciando temporizadores:

useEffect(() => {
  const timer = setInterval(() => {
    // Do something repeatedly
  }, 1000);
  return () => {
    clearInterval(timer);
  };
}, []);

Erros comuns do useEffect e como evitá-los

Ao trabalhar com o Hook useEffect no React, é possível que você encontre erros que podem levar a um comportamento inesperado ou a problemas de desempenho.

Entender esses erros e saber como evitá-los pode ajudar a garantir que você use o useEffect sem problemas e sem erros.

Vamos explorar alguns erros comuns do useEffect e suas soluções:

1. Falta de array de dependência

Um erro comum é esquecer de incluir uma array de dependência como o segundo argumento do hook useEffect.

O ESLint sempre sinalizará esse erro como um aviso, pois ele pode resultar em comportamentos não intencionais, como renderização excessiva ou dados obsoletos.

useEffect(() => {
  // Side effect code
}); // Missing dependency array

Solução: Sempre forneça uma array de dependência para useEffect, mesmo que ela esteja vazia. Inclua todas as variáveis ou valores dos quais o efeito depende. Isso ajuda o React a determinar quando o efeito deve ser executado ou ignorado.

useEffect(() => {
  // Side effect code
}, []); // Empty dependency array or with appropriate dependencies

2. Array de dependência incorreta

Fornecer uma array de dependência incorreta também pode levar a problemas. Se a array de dependências não for definida com precisão, o efeito poderá não ser executado quando as dependências esperadas forem alteradas.

const count = 5;
const counter = 0;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Incorrect dependency array

Solução: Certifique-se de incluir todas as dependências necessárias na array de dependências. Se o efeito depender de várias variáveis, inclua todas elas para acionar o efeito quando qualquer uma das dependências for alterada.

const count = 5;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Correct dependency array

3. Loops infinitos

A criação de um loop infinito pode ocorrer quando o efeito modifica um estado ou um objeto que também depende do próprio efeito. Isso faz com que o efeito seja acionado repetidamente, causando uma nova renderização excessiva e, possivelmente, o congelamento do aplicativo.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Modifying the dependency 'count' inside the effect
}, [count]); // Dependency array includes 'count'

Solução: Certifique-se de que o efeito não modifique diretamente uma dependência incluída em sua array de dependências. Em vez disso, crie variáveis separadas ou use outras técnicas de gerenciamento de estado para lidar com as alterações necessárias.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount((prevCount) => prevCount + 1); // Modifying the 'count' using a callback
}, []); // You can safely remove the 'count' dependency

4. Esquecimento da limpeza

Se você deixar de limpar os efeitos colaterais, poderá ocorrer vazamento de memória ou consumo desnecessário de recursos. A não limpeza de ouvintes de eventos, intervalos ou assinaturas pode resultar em um comportamento inesperado, especialmente quando o componente é desmontado.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  // Missing cleanup
  return () => {
    clearInterval(timer); // Cleanup missing in the return statement
  };
}, []);

Solução: Sempre forneça uma função de limpeza na instrução de retorno do hook useEffect.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  return () => {
    clearInterval(timer); // Cleanup included in the return statement
  };
}, []);

Ao estar ciente desses erros comuns do useEffect e seguir as soluções recomendadas, você pode evitar possíveis armadilhas e garantir o uso correto e eficiente do Hook useEffect em seus aplicativos React.

Resumo

O Hook useEffect do React é uma ferramenta poderosa para gerenciar efeitos colaterais em componentes de função. Agora que você tem uma compreensão mais detalhada do useEffect, é hora de aplicar seu conhecimento e dar vida aos seus aplicativos React.

Você também pode fazer com que seu aplicativo React seja executado em tempo real, implantando na hospedagem de aplicativos da Kinsta gratuitamente!

Agora é a sua vez. O que você pensa sobre o Hook useEffect? Fique à vontade para compartilhá-la conosco na seção de comentários abaixo.