React Hook - useCallback


O useCallback é um dos React Hooks que oferece um meio de otimizar o desempenho de componentes React, especialmente em situações em que há uma re-renderização frequente. Esse Hook é utilizado para memorizar funções e evita que novas instâncias sejam criadas a cada re-renderização, contribuindo para uma execução mais eficiente.

Sintaxe

A sintaxe básica do useCallback é simples. Ele recebe uma função e um array de dependências. A função será memorizada, ou seja, será recriada apenas quando alguma das dependências no array mudar.

import React, { useCallback } from 'react';
 
const MeuComponente = () => {
  const minhaFuncao = useCallback(() => {
    // Lógica da função
  }, [/* Dependências */]);
 
  return <button onClick={minhaFuncao}>Clique Aqui</button>;
};

As dependências são cruciais para garantir que o useCallback funcione corretamente. Se a função utilizada dentro de useCallback referencia variáveis externas, essas variáveis devem ser incluídas na array de dependências. Caso contrário, a função pode não ser memorizada corretamente.

Caso de uso

O useCallback é especialmente útil quando uma função é passada como propriedade para componentes filhos, pois ajuda a evitar que esses componentes se re-renderizem desnecessariamente.

import React, { useState, useCallback } from 'react';
 
function MeuComponente() {
  const [contador, setContador] = useState(0);
 
  // Memorizar a função
  const handleClick = useCallback(() => {
    setContador(contador + 1);
  }, [contador]);
 
  return (
    <div>
      <p>Contador: {contador}</p>
      <BotaoIncrementar onClick={handleClick} />
    </div>
  );
}
 
function BotaoIncrementar({ onClick }) {
  return <button onClick={onClick}>Incrementar</button>;
}

Um problema notável nesse exemplo é que o contador é uma dependência do useCallback, o que significa que a função handleClick é recriada sempre que contador é alterado. Como resultado, tanto o componente pai (<MeuComponente />) quanto o componente filho (<BotaoIncrementar />) são re-renderizados a cada incremento no valor de contador.

Para solucionar esse problema, podemos utilizar a função React.memo para memorizar o componente filho e ajustar a lógica da função handleClick. Agora, a função do setContador recebe o estado anterior como argumento (prevContador), eliminando a necessidade de adicionar contador como uma dependência do useCallback:

import React, { useState, useCallback } from 'react';
 
function MeuComponente() {
  const [contador, setContador] = useState(0);
 
  // Memorizar a função
  const handleClick = useCallback(() => {
    setContador((prevContador) => prevContador + 1);
  }, []);
 
  return (
    <div>
      <p>Contador: {contador}</p>
      <BotaoIncrementar onClick={handleClick} />
    </div>
  );
}
 
// Memorizar o componente
const BotaoIncrementar = React.memo(function BotaoIncrementar({ onClick }) {
  return <button onClick={onClick}>Incrementar</button>;
})

Agora, com essa abordagem, conseguimos garantir que a função handleClick seja memorizada corretamente, evitando re-renderizações desnecessárias e otimizando o desempenho do aplicativo.

Quando usar useCallback?

O useCallback é mais benéfico em situações onde a criação de novas instâncias de funções pode causar re-renderizações desnecessárias de componentes filhos. Em componentes onde a performance não é uma preocupação ou onde funções são definidas diretamente no corpo do componente, o uso de useCallback pode não ser tão crítico.

Referências