C

Iniciando com React Hooks - useCallback

2 de jun. de 2021

Hey, esse artigo faz parte de uma série sobre react hooks. Se você ainda não leu os artigos passados, da uma conferida nos links abaixo:

Estamos sempre procurando alternativas para deixar nossas aplicações cada vez melhores e uma das formas de fazer isso é as tornando performáticas.

Anos atrás quando se falava no assunto performance no frontend as únicas coisas que as pessoas pensavam eram no tamanho das imagens, no tamanho do seu css minificado e no tamanho do seu javascript, também minificado. Porém, isso mudou bastante! Mas isso não quer dizer que tudo que citei anteriormente tenha deixado de ter atenção, na verdade, tudo isso se tornou o mínimo esperado. O foco agora são nas informações que aparecem depois que sua aplicação já foi carregada, pois, as aplicações frontend ganharam uma grande responsabilidade ao lidar e manipular grandes massas de dados complexos.

Hoje vamos aprender sobre useCallback, mas tome muito cuidado para não confundir com useMemo. Eles se parecem, mas o useMemo memoriza valores e o useCallback memoriza funções.

Antes de prosseguir é importante ressaltar que devemos sempre evitar otimização prematura! Por padrão o react já é extremamente rápido e na maioria das vezes não vamos precisar fazer uso de um hook como o useCallback. Antes de sair utilizando useCallback em todas as funções da sua aplicação lembre-se que isso tem um custo e pode ser que seja um custo alto para um ganho baixo.

O que faz esse useCallback

Seu uso é bastante simples e bem fácil de entender!

Basicamente, o hook useCallback memoiza(guarda) sua função e evita que ela seja redeclarada sempre que componentes filhos que utilizam essa função sejam renderizado. A função só vai ser redeclarada quando algum dos valores do array de dependência forem atualizados.

Ai você se questiona… "Mas qual o real objetivo do useCallback?"

Will smith com dúvida

Para explicar isso é preciso entender um pouco sobre igualdade de funções no javascript.

Igualdade de funções no javascript

function calc() {
  return (a, b) => a + b;
}

const soma = calc();
const soma2 = calc();

soma(2, 2); // 4
soma2(2, 2); // 4

soma === soma2; // false
soma2 === soma2; // false

Uma breve explicação sobre o trecho de código acima. Basicamente temos uma função calcque ao ser executada retorna uma função que recebe 2 argumentos. Ou seja, soma ficaria assim const soma = (a,b) => a + b; . O mesmo acontece com soma2.

As duas funções(soma e soma2) fazem exatamente a mesma coisa e retornam valores iguais quando recebem os mesmos argumentos, porém o javascript nos diz que elas são diferentes. Isso acontece, pois no javascript funções são objetos e cada nova instância de um objeto é diferente da outra.

E onde o React entra nisso?

Quando criamos uma função que é utilizada em um componente ela é sempre uma nova instância de objeto a cada renderização.

function MyComponent() {
  // handleClick é criado a cada renderização
  const handleClick = () => {
    console.log('Clicado!');
  };

Para reescrever essa função usando useCallback seria assim

function MyComponent() {
  // handleClick é sempre a mesma a cada renderização
  const handleClick = useCallback(() => {
    console.log('Clicado!');
  }, []);

Você deve ter notado que a estrutura do useCallback é como a de outros hooks, sempre recebendo uma função como primeiro parâmetro e um array de dependências como segundo argumento.

Importante!! A funcão declarada com useCallback só vai ser redeclarada quando um dos itens do array de dependencia forem modificados. Caso contrário a funcão vai se manter a mesma caso o componente seja rerenderizado.

Quando useCallback é uma boa escolha

Imagine que temos uma página com uma lista de filmes que são buscados em uma api com base em uma categoria. Nessa mesma página também podemos filtrar os filmes por status (lançado ou não).

Você concorda que a função que trata categoria só deveria ser redeclarada quando a categoria mudar? E não quando a lista for atualizada por conta do status?

É exatamente isso que o useCallback abaixo faz. A função onMovieClick só vai ser redeclarada quando a categoria altera, logo, não importa quantas vezes você alterar o filtro de status, essa função vai se manter a mesma.

function MovieList({ category, handleMovieClick }) {
  const movies = getMovies(category);

  const map = movie => <div onClick={handleMovieClick}>{movie}</div>;

  return <div>{movies.map(map)}</div>;
}

export default function Container({ category }) {
  const onMovieClick = useCallback(
    event => {
      console.log("Clicou", event.currentTarget);
    },
    [category],
  );

  return <MovieList category={category} handleItemClick={onMovieClick} />;
}

Muito cuidado!!

É preciso tomar muito cuidado ao optar por usar useCallback pelos seguintes motivos:

Isso é tudo pessoal!

Isso é tudo pessoal

Obrigado por chegar até aqui!! Espero que tenha conseguido te ajudar de alguma forma. 😊

Fique atento(a) aqui no blog e no meu twitter que em breve irei postar mais artigos sobre hooks.

Links importantes