Você já percebeu que algumas funções “lembram” valores mesmo depois que o componente re-renderiza? Isso acontece por causa de closures.
O que é uma closure?
Em JavaScript, uma closure é quando uma função consegue acessar variáveis do escopo onde foi criada, mesmo depois desse escopo ter sido executado.
Ou seja, a função “fecha” (close over) sobre o ambiente original, preservando valores para usos futuros.
Exemplo simples em JS puro:
function contador() { let count = 0; return function() { count++; return count; }; } const incrementar = contador(); console.log(incrementar()); // 1 console.log(incrementar()); // 2 console.log(incrementar()); // 3 A função incrementar mantém acesso à variável count, mesmo depois que contador() já terminou de executar. Isso é uma closure em ação.
O problema no React
Ao digitar num input e disparar uma busca a cada tecla, sua API sofre. Queremos esperar o usuário parar de digitar por alguns milissegundos antes de buscar.
A solução com closure
Vamos criar um hook useDebounce que devolve uma função “debounced”. Essa função usa uma variável do escopo (timerRef) que fica preservada graças à closure.
import { useRef, useCallback } from "react"; export function useDebounce(fn, delay = 400) { const timerRef = useRef(null); return useCallback((...args) => { if (timerRef.current) window.clearTimeout(timerRef.current); // 'fn' e 'timerRef' ficam acessíveis aqui graças à closure timerRef.current = window.setTimeout(() => { fn(...args); }, delay); }, [fn, delay]); } Por que isso é uma closure?
A função retornada por useCallback captura (close over) timerRef, fn e delay.
Mesmo que o componente re-renderize, a função ainda consegue acessar e atualizar timerRef corretamente.
Usando no componente
import { useState, useCallback } from "react"; import { useDebounce } from "./useDebounce"; export default function SearchBox() { const [query, setQuery] = useState(""); const fetchResults = useCallback((q) => { console.log("Buscando por:", q); }, []); const debouncedFetch = useDebounce(fetchResults, 600); return ( <input value={query} onChange={(e) => { const q = e.target.value; setQuery(q); debouncedFetch(q); }} placeholder="Busque produtos..." className="border rounded-lg p-2 w-full" /> ); } Armadilha comum: “stale closure”
Se fetchResults depender de algum estado que muda, você precisa incluir esse estado nas dependências do useCallback. Caso contrário, a closure pode capturar valores antigos.
Resumo:
Closure = função que lembra variáveis do escopo onde foi criada.
Em React, closures permitem que funções de hooks e handlers preservem contexto entre renders.
Nosso useDebounce funciona porque a função debounced mantém acesso ao timerRef.
Top comments (0)