All Articles

React Hooks - Como Executar Efeitos Colaterais

Antes do lançamento do React Hooks, utilizávamos alguns métodos de ciclo de vida para a execução de efeitos colaterais. Por exemplo, em componentes de classe para executar uma ação quando o componente é montado usamos o método componentDidMount .

componentDidMount

class MeuComponente extends React.Component {
  componentDidMount() {
    console.log('Componente montando!');
  }
  render() {
    return null;
  }
}

Utilizando React Hooks o equivalente seria:

const MeuComponente = () => {
  useEffect(() => console.log('Componente Montado'), []);
  return null;
}

No exemplo vemos o uso do Hook useEffect que torna possível aplicar efeitos colaterais em componentes funcionais. Ou seja, o Hook useEffect é como se fosse uma combinação dos métodos componentDidMount, componentDidUpdate e componentWillUnmount.
O useEffect recebe uma função como argumento que será executada por padrão após a renderização e após cada atualização. O segundo argumento é um array de valores (normalmente props). Se algum desses valores for alterado, a função será executada depois de cada renderização.

Anatomia do useEffect

A anatomia do Hook useEffect é composta da seguinte forma, como primeiro parâmetro temos uma função que será executada sempre que o componente for renderizado e uma função de retorno caso seja preciso executar uma limpeza. O segundo parâmetro é uma array que quando passado vazio faz com que o useEffect se comporte como componentDidMount, quando o array recebe uma prop ou estado o useEffect só será executado quando houver mudanças.

Anatomia do useEffect

  1. Como primeiro parâmetro é passado uma função, responsável por executar as ações e se for o caso também retornar a função que fará a limpeza.
  2. Corpo da função onde será implementado as ações.
  3. Retorno de uma função responsável por fazer a limpeza quando necessário.
  4. Como segundo parâmetro é passado um array responsável por alterar o comportamento do useEffect.

Tipos de Efeitos Colaterais com React Hooks

Basicamente são dois tipos de efeitos colaterais em componentes React, efeitos sem limpeza e efeitos com limpeza. Vamos entender o funcionamento de cada um.

Efeitos sem limpeza

Utilizamos efeitos sem limpeza quando precisamos executar algum código adicional após a renderização do DOM. Isso inclui requisições, logs e manipulações manuais do DOM.

Buscando dados de uma API externa usando o Hook useEffect

Vamos criar um exemplo que não necessita de limpeza, quando o componente for montado vamos utilizar o useEffect para fazer uma requisição usando o axios para buscar dados em uma API externa.

No nosso componente vamos fazer a importação dos hooks useState e useEffect:

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

Em seguida vamos criar o estado para o array que irá armazenar os dados retornados da API:

function App() {
  const [posts, setPosts] = useState([]);
  return (
    <div className="App">
      <p>Request example</p>
    </div>
  );
}

Essa declaração pode ser um pouco estranha inicialmente, mas vamos entender o que está acontecendo. O uso dos dois colchetes envolvendo posts e setPosts é chamado de Atribuição via Desestruturação. Nessa declaração estamos criando duas novas variáveis, posts que receberá o primeiro valor retornado pelo useState e setPosts que receberá o segundo valor retornado.

Com o estado criado vamos partir para a implementação do useEffect que irá fazer a requisição. Utilizaremos a biblioteca Axios para fazer as requisições:

import axios from "axios";

A implementação do hook useEffect ficará no corpo do componente da seguinte forma:

function App() {
  const [posts, setPosts] = useState([]);
  useEffect(() => {
    axios.get("https://jsonplaceholder.typicode.com/posts")
    .then(res => {
      setPosts(res.data);
    })
    .catch(err => {
      console.log(err);
    })
  }, []);
  return (
    <div className="App">
      <p>We found {posts.length} posts</p>
    </div>
  );
}

No corpo do hook useEffect estamos utilizando o axios para fazer uma requisição ao JSONPlaceholder, em seguida chamamos a função setPosts que irá atribuir os dados retornados ao nosso estado posts. O código implementado não necessita de limpeza pois só é feito uma única requisição durante a montagem do componente. Pode ser observado também que passamos um array vazio como segundo argumento para o useEffect, quando o segundo argumento é informado o useEffect irá se comportar como o componentDidMount, sendo executado somente na montagem do componente: Exibindo Data Hora em tempo real Veja o resultado completo:

Edit 46lht

Efeitos com limpeza

Alguns efeitos precisam ser limpos quando o componente for desmontado para que não haja estouro de memória, casos como subscriptions e ID de temporizadores são exemplo de recursos que precisam ser limpos.

Implementando a exibição de data/hora em tempo real

Vamos ver essa necessidade na prática com a implementação de um componente que exibe a data e hora em tempo real. Como visto anteriormente é preciso importar os hooks useState e useEffect:

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

Em seguida vamos criar uma variável de estado que vai conter o valor da data e hora:

const [dataHora, setDataHora] = useState(Date.now());

Para que possamos pegar a hora em tempo real vamos precisar passar para o useEffect a função setInterval:

 useEffect(() => {
    var id = setInterval(() => {
      setDataHora(new Date());
    }, 1000);
  });

No código temos a declaração da variável id que será usada para a limpesa. Na função setInterval estamos chamando a função setDataHora que irá criar uma nova data e o valor será atribuído a variável dataHora, como segundo argumento a função setInterval recebe o tempo de cada execução, passamos 1000 milessegundo para que a função setDataHora seja executada a cada segundo. Para que seja feita a limpeza do temporizador criado, é preciso que o useEffect retorne uma função que chama clearInterval passando como parâmetro o id do temporizador criado:

 useEffect(() => {
    var id = setInterval(() => {
      setDataHora(new Date());
    }, 1000);
    return () => {
      clearInterval(id);
    };
  });

Com essa implementação temos a data e hora sendo criadas a cada segundo e a limpeza sendo executada corretamente. Agora só é preciso usar a API de internacionalização do EcmaScript para formatar a exibição no corpo do componente:

  return (
    <div className="App">
      {Intl.DateTimeFormat("pt-BR", {
        weekday: "long",
        year: "numeric",
        month: "long",
        day: "numeric",
        hour: "numeric",
        minute: "numeric",
        second: "numeric"
      }).format(dataHora)}
    </div>
  );

E teremos a exibição da data hora correta com atualização a cada segundo:

Exibindo Data Hora em tempo real


Resultado completo da implementação:

Edit iu90u