Contents

Como melhorar o desempenho de pesquisa no React com debouncing

No React, ao implementar a funcionalidade de pesquisa, o manipulador onChange chama a função de pesquisa sempre que o utilizador escreve dentro da caixa de entrada. Essa abordagem pode causar problemas de desempenho, especialmente se estiver fazendo chamadas de API ou consultando o banco de dados. Chamadas frequentes à função de pesquisa podem sobrecarregar o servidor Web, levando a falhas ou a uma IU que não responde. O debouncing resolve este problema.

O que é debouncing?

Em uma implementação típica da funcionalidade de pesquisa no React, normalmente se invocaria uma função de manipulador onChange a cada pressionamento de tecla, conforme demonstrado abaixo:

 import { useState } from "react";

export default function Search() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = () => {
    console.log("Search for:", searchTerm);
  };

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    // Calls search function
    handleSearch();
  };

  return (
    <input
      onChange={handleChange}
      value={searchTerm}
      placeholder="Search here..."
    />
  );
}

Para otimizar o desempenho, é importante considerar que as atualizações frequentes dos resultados da pesquisa por meio de chamadas para o back-end podem se tornar caras ao digitar palavras-chave como “webdev”. Neste cenário, a aplicação envia pedidos para o backend sempre que é introduzido um novo carácter, incluindo caracteres como “w”, “we”, “web”, etc.

O debouncing envolve o adiamento da execução de uma função até que tenha passado um período de tempo atribuído desde a sua última invocação. Neste cenário, a função de exclusão só será activada quando o utilizador deixar de escrever após o intervalo especificado. Se ocorrerem mais entradas durante o período designado, o temporizador é reiniciado e inicia outra ronda de processamento. Este ciclo repete-se enquanto não forem introduzidas mais teclas, terminando quando o utilizador parar a sua atividade de digitação.

O debouncing permite a uma aplicação gerir eficazmente os pedidos do servidor, atrasando-os até que um utilizador deixe de introduzir dados, minimizando assim as pesquisas desnecessárias e aliviando a tensão no servidor.

Como fazer debounce de pesquisa no React

Ao procurar incorporar a funcionalidade de debouncing em um aplicativo Web, existem várias bibliotecas pré-existentes que fornecem esse recurso. Como alternativa, pode-se optar por construir o algoritmo de debouncer a partir do zero, utilizando os métodos setTimeout e clearTimeout do JavaScript.

Este artigo utiliza a funcionalidade de debouncing fornecida pela biblioteca Lodash, que é um utilitário JavaScript popular que oferece uma vasta gama de funcionalidades para manipulação de dados e otimização de operações.

Para iniciar o processo de desenvolvimento de uma funcionalidade de pesquisa em seu projeto React existente ou criando um novo, é necessário primeiro estabelecer um novo componente chamado “Pesquisa”. Isto pode ser conseguido utilizando uma aplicação React existente ou empregando o utilitário ‘create React app’ para gerar um ambiente React totalmente funcional.

No ficheiro do componente Search (Pesquisa), é possível utilizar o excerto de código fornecido para gerar um campo de entrada de pesquisa que executará uma função de retorno de chamada designada a cada vez que for premida uma tecla.

 import { useState } from "react";

export default function Search() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = () => {
    console.log("Search for:", searchTerm);
  };

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    // Calls search function
    handleSearch();
  };

  return (
    <input
      onChange={handleChange}
      value={searchTerm}
      placeholder="Search here..."
    />
  );
}

Para atrasar a execução da função handleSearch utilizando a função debounce fornecida pela biblioteca Lodash, pode passar-se a referida função como argumento para o método debounce . Isto asseguraria que a função só é executada uma vez durante um determinado período de tempo, após o qual quaisquer chamadas subsequentes dentro desse período de tempo serão ignoradas até que o período de espera expire.

 import debounce from "lodash.debounce";
import { useState } from "react";

export default function Search() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = () => {
    console.log("Search for:", searchTerm);
  };
  const debouncedSearch = debounce(handleSearch, 1000);

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    // Calls search function
    debouncedSearch();
  };

  return (
    <input
      onChange={handleChange}
      value={searchTerm}
      placeholder="Search here..."
    />
  );
}

Na implementação do mecanismo de debouncing, introduzimos uma versão personalizada da função handleSearch , aceitando-a como argumento juntamente com um intervalo de tempo especificado, nomeadamente 500 milissegundos, que serve de limiar para acionar a execução atrasada da referida função.

Espera-se que o código acima mencionado adie a execução da função handleSearch até que o utilizador deixe de introduzir dados; no entanto, esta funcionalidade não funciona como previsto na estrutura React. As razões para esta discrepância serão exploradas na secção seguinte.

Debounceing and Rerenders

A presente aplicação utiliza uma entrada regulada, em que o status quo do sistema determina o conteúdo fornecido como saída através da barra de pesquisa. Com cada toque de tecla introduzido pelo utilizador nesta interface específica, o React acrescenta modificações à configuração do estado em conformidade.

O React é uma biblioteca JavaScript popular utilizada para criar interfaces de utilizador. Sempre que um valor de estado em um componente React é alterado, todo o componente é renderizado novamente, o que envolve a execução de todas as suas funções do início ao fim novamente. Isto permite que sejam efectuadas actualizações dinâmicas na interface sem que seja necessário atualizar toda a página.

O componente de pesquisa acima mencionado é renderizado novamente, durante o qual o React ativa o mecanismo de debouncing. É criado um novo temporizador para monitorizar o atraso, enquanto um temporizador existente permanece ativo na memória do sistema. Após a expiração deste último, a função de pesquisa é accionada depois de ser adiada por 500 milissegundos. Consequentemente, esta sequência repete-se em cada renderização, uma vez que um novo temporizador tem precedência sobre o anterior, com o temporizador mais antigo a completar subsequentemente o seu período de terminação antes de invocar a função de pesquisa repetidamente.

Para garantir que uma função debounced funcione como pretendido em um componente React, é importante limitar sua execução a uma única instância.Uma abordagem para atingir este objetivo envolve a utilização de uma técnica de memorização em conjunto com a função de debouncing. Ao fazer isso, mesmo quando o componente for renderizado novamente, a função de exclusão não será executada repetidamente.

Definindo a função de debounce fora do componente de pesquisa

Considere a possibilidade de realocar o mecanismo de execução diferida, como debounce , para uma posição fora do componente de pesquisa , como demonstrado no exemplo fornecido abaixo:

 import debounce from "lodash.debounce"

const handleSearch = (searchTerm) => {
  console.log("Search for:", searchTerm);
};

const debouncedSearch = debounce(handleSearch, 500);

Na atual iteração do componente Search do nosso projeto, implementaremos uma versão debounce da função search invocando o método debouncedSearch e fornecendo-lhe a consulta de pesquisa como argumento.

 export default function Search() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    // Calls search function
    debouncedSearch(searchTerm);
  };

  return (
    <input
      onChange={handleChange}
      value={searchTerm}
      placeholder="Search here..."
    />
  );
}

A funcionalidade da função de pesquisa está dependente da expiração de um intervalo de tempo predefinido, altura em que será invocada.

Memoização da função de debounce

A memoização envolve o armazenamento da saída gerada por uma determinada função ao receber uma entrada específica, para que possa ser prontamente recuperada e utilizada caso a função seja chamada novamente com parâmetros idênticos. Esta técnica é utilizada como uma estratégia de otimização para minimizar os custos computacionais, especialmente quando se trata de cálculos repetitivos.

Para armazenar em cache e adiar efetivamente a execução de uma função com debounce utilizando o mecanismo Memoization do React, use o Hook useMemo em conjunto com uma estratégia de implementação apropriada para o efeito de debounce desejado.

 import debounce from "lodash.debounce";
import { useCallback, useMemo, useState } from "react";

export default function Search() {
  const [searchTerm, setSearchTerm] = useState("");

  const handleSearch = useCallback((searchTerm) => {
    console.log("Search for:", searchTerm);
  }, []);

  const debouncedSearch = useMemo(() => {
    return debounce(handleSearch, 500);
  }, [handleSearch]);

  const handleChange = (e) => {
    setSearchTerm(e.target.value);
    // Calls search function
    debouncedSearch(searchTerm);
  };

  return (
    <input
      onChange={handleChange}
      value={searchTerm}
      placeholder="Search here..."
    />
  );
}

É importante observar que incluímos a função handleSearch em um gancho useCallback para garantir que o React só invocará essa função uma vez. Se não fosse pelo gancho useCallback , o React executaria repetidamente a função handleSearch durante cada processo de renderização, resultando em alterações nas dependências do gancho useMemo e, consequentemente, acionando a função debounce .

O React deve invocar a função debounce apenas nos casos em que a função handleSearch ou o período de atraso sofram alterações.

Otimizar a pesquisa com debounce

Por vezes, a moderação do ritmo pode levar a uma maior produtividade. Em situações que envolvem consultas caras a bancos de dados ou APIs durante operações de pesquisa, a utilização de uma função de debounce torna-se prudente. Esta função impõe uma pausa temporal antes de encaminhar os pedidos para o backend.

A implementação de um mecanismo de carregamento diferido de imagens em páginas Web pode aliviar significativamente a carga sobre os servidores, limitando a frequência dos pedidos que lhes são feitos. Ao configurar o sistema para esperar que o utilizador faça uma pausa na sua introdução antes de enviar um pedido de imagem, minimizamos o volume de pedidos que são processados simultaneamente. Como resultado, esta abordagem mantém um desempenho ótimo do servidor, evitando o esgotamento de recursos ou atrasos nos tempos de resposta.