O React é um popular framework JavaScript para o desenvolvimento front-end que facilitou a vida dos desenvolvedores ao introduzir o desenvolvimento orientado a componentes.
Antes do React 16, os componentes eram geralmente definidos com Classes e tinham diferentes métodos de ciclo de vida. O React 16 promoveu a criação de componentes funcionais e introduziu os seguintes hooks:
- useEffect para lidar com o ciclo de vida do componente.
- useState para gerenciamento de estado.
Esses hooks são necessários porque as atualizações e recomputações são operações custosas, então minimizá-las melhora o desempenho da aplicação.
Recentemente, o React introduziu dois hooks, useCallback e useMemo. Assim como os hooks useEffect e useState, essas são ferramentas poderosas que podem ajudar os desenvolvedores a otimizar o desempenho de suas aplicações, reduzindo atualizações e recomputações desnecessárias.
Neste artigo, exploraremos esses hooks, analisando sua funcionalidade e examinando como eles funcionam. Também discutiremos os casos de uso apropriados para esses hooks, fornecendo exemplos práticos para ajudá-lo a entender quando e onde usá-los em seus projetos. Vamos começar!
useCallback hook
Sintaxe
const computedFn = useCallback(()=>{ doSomething(dependencies); }, [dependencies]);
No exemplo de código anterior, fazerAlgo(dependências); será retornado e armazenado na variável funcaoCalculada. Isso só ocorre quando as dependências mudam, e o useCallback fornecerá o novo valor de fazerAlgo(dependências);.
Isso é útil quando você deseja que uma função evite a renderização novamente sempre que o componente mudar.
Por exemplo, usaremos a função de debounce no campo de entrada para obter os dados apenas quando o usuário parar de escrever por um período específico.
import React, { useState } from "https://esm.sh/react@18.2.0"; import ReactDOM from "https://esm.sh/react-dom@18.2.0"; const App = () => { const [texto, setTexto] = useState(""); const debounce = (funcao, atraso) => { let inDebounce; return function () { const contexto = this; const args = arguments; clearTimeout(inDebounce); inDebounce = setTimeout(() => funcao.apply(contexto, args), atraso); }; }; const fazerChamadaApi = () => { console.log(texto, " Fazendo uma chamada à API"); }; const chamadaApiDebounced = debounce(fazerChamadaApi, 500); const manipuladorDeAlteracao = (e) => { setTexto(e.target.value); chamadaApiDebounced(); }; return ( <div> <input type="text" onChange={manipuladorDeAlteracao} value={texto} /> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
Cada vez que o estado é atualizado, o componente é re-renderizado, e a função de debounce é novamente anexada à variável chamadaApiDebounced. Como resultado, os logs são impressos toda vez que o estado muda, e o debounce precisa ser corrigido.
Para evitar isso, podemos envolver a função de debounce dentro do useCallback, e o programa escreverá a função de debounce memorizada que mudará apenas se as dependências mudarem.
import React, { useState, useCallback } from "https://esm.sh/react@18.2.0"; import ReactDOM from "https://esm.sh/react-dom@18.2.0"; const App = () => { const [texto, setTexto] = useState(""); const _debounce = (funcao, atraso) => { let inDebounce; return function () { const contexto = this; const args = arguments; clearTimeout(inDebounce); inDebounce = setTimeout(() => funcao.apply(contexto, args), atraso); }; }; const fazerChamadaApi = (e) => { console.log(e, "Fazendo uma chamada à API"); }; const debounce = useCallback(_debounce(fazerChamadaApi, 2000), []); const manipuladorDeAlteracao = (e) => { setTexto(e.target.value); debounce(e.target.value); }; return ( <div> <input type="text" onChange={manipuladorDeAlteracao} value={texto} /> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
Não queremos que a função _debounce seja re-renderizada, então não estamos passando nenhuma dependência. Este código imprimirá o log somente quando o usuário parar de escrever e passarem 2000 ms. Observe no código que estamos passando o valor para debounce(e.target.value) porque ele não pode recuperar o valor do estado.
Embora pareça estar funcionando bem, não está. Se você tiver o ESLint habilitado, notará que estamos recebendo o seguinte erro de linting para o hook useCallback:
error React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead react-hooks/exhaustive-deps
Isso ocorre porque as dependências da função que useCallback está recebendo como entrada não são conhecidas, então o ESLint quer que escrevamos uma função inline.
Além disso, a função _debounce é recriada em cada re-renderização, alterando assim sua referência e anulando o propósito de armazená-la em cache.
Para corrigir isso, podemos criar um hook personalizado useDebounce e usá-lo.
import { useState, useCallback, useRef } from "react"; const useDebounce = (funcao, atraso) => { const inDebounce = useRef(); const debounce = useCallback( function () { const contexto = this; const args = arguments; clearTimeout(inDebounce.current); inDebounce.current = setTimeout(() => funcao.apply(contexto, args), atraso); }, [funcao, atraso] ); return debounce; }; const App = () => { const [texto, setTexto] = useState(""); const fazerChamadaApi = useCallback((e) => { console.log(e, "Fazendo uma chamada à API"); }, []); const debounce = useDebounce(fazerChamadaApi, 2000); const manipuladorDeAlteracao = (e) => { setTexto(e.target.value); debounce(e.target.value); }; return ( <div> <input type="text" onChange={manipuladorDeAlteracao} value={texto} /> </div> ); };
useMemo hook
Sintaxe
const computedValue = useMemo(()=>{ doSomething(); }, [dependencies]);
O useMemo funciona de forma semelhante ao useCallback, mas, em vez de retornar a função, ele retorna o valor calculado a partir da função, ou seja, a saída da função, e reexecuta a função apenas quando as dependências mudam para fornecer o novo resultado.
Por exemplo, digamos que temos um componente filho que deve ser renderizado apenas quando o texto tem um valor específico.
import React, { useState, useCallback } from "https://esm.sh/react@18.2.0"; import ReactDOM from "https://esm.sh/react-dom@18.2.0"; const ComponenteFilho = ({ texto }) => { return <p>{texto}</p>; }; const App = () => { const [texto, setTexto] = useState(""); const manipuladorDeAlteracao = (e) => { setTexto(e.target.value); }; return ( <div> <input type="text" onChange={manipuladorDeAlteracao} value={texto} /> <ComponenteFilho texto={texto} /> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
Se a verificação condicional fosse colocada dentro do componente filho, isso acionaria uma nova renderização toda vez que o estado do pai fosse atualizado, independentemente de nossa intenção de limitá-lo a determinados valores.
O gancho useMemo aborda esse problema. Ao empregar esse hook, o componente filho se torna memorizado, garantindo que ele só seja renderizado quando as condições especificadas em suas dependências forem atendidas.
Neste exemplo, o componente filho será renderizado exclusivamente quando o texto corresponder ao termo "syncfusion".
import React, { useState, useMemo } from "https://esm.sh/react@18.2.0"; import ReactDOM from "https://esm.sh/react-dom@18.2.0"; const ComponenteFilho = ({ texto }) => { return <p>{texto}</p>; }; const App = () => { const [texto, setTexto] = useState(""); const manipuladorDeAlteracao = (e) => { setTexto(e.target.value); }; const componenteFilhoMemorizado = useMemo(() => <ComponenteFilho texto={texto} />, [texto === 'syncfusion']) return ( <div> <input type="text" onChange={manipuladorDeAlteracao} value={texto} /> {componenteFilhoMemorizado} </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
Conclusão
Obrigado por ler. Este blog explorou os dois hooks mais poderosos do React, nomeadamente useCallback e useMemo, explicando suas funções e explorando sua aplicação ótima para evitar cálculos redundantes quando os componentes React são re-renderizados.
Sugestões de cursos
Descubra o caminho para se tornar um especialista em programação web. Aprenda HTML, CSS, JavaScript e os principais frameworks nesta jornada emocionante. Com instrutores experientes e materiais práticos, você desenvolverá habilidades práticas para criar sites impressionantes e aplicativos interativos. Impulsione sua carreira na indústria de tecnologia e abra portas para oportunidades de emprego lucrativas. Garanta sua vaga hoje mesmo e inicie sua jornada para se tornar um desenvolvedor web de sucesso.
Método Para Aprender a Programar do Absoluto ZERO com Node.js, React e React Native.
As tecnologias ensinadas no curso são responsáveis por muitas vagas no mercado de trabalho.
Além da alta demanda, os salários vão de R$47.000,00 até R$197.000,00 anuais tendo empresas que possibilitam o trabalho remoto e até vagas Internacionais.
Para que você possa estar apto a preencher uma dessas vagas eu vou te apresentar o passo a passo para você se tornar um verdadeiro expert nessas tecnologias.
O curso te dará o passo a passo de como criar estruturar de um sistema do zero com Node.js, React e React Native.