0Pricing
Frontend Academy · Aula

Hooks personalizados: extraindo lógica reutilizável

Mova a lógica com estado para funções personalizadas use* a fim de compartilhar comportamento entre componentes sem duplicar código ou elevar o estado.

Hooks personalizados: extraindo lógica reutilizável é uma aula grátis de Frontend Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Frontend Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Frontend Academy inclui 4 aulas no total.

O que são ganchos personalizados?

Um gancho personalizado é uma função JavaScript cujo nome começa com use e que chama outros ganchos internamente. Ganchos personalizados permitem extrair lógica que mantém estado para que vários componentes a compartilhem sem copiar e colar.

Extraindo lógica para um gancho personalizado

Identifique a lógica duplicada entre componentes (busca + carregamento + erro, tamanho da janela, armazenamento local) e mova-a para um gancho personalizado.

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handler = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);

  return width;
}

// Usage:
const width = useWindowWidth();
const isMobile = width < 768;

useFetch — gancho de busca de dados

Um gancho reutilizável de busca de dados que qualquer componente pode usar para carregar dados de uma URL.

function useFetch<T>(url: string) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const controller = new AbortController();
    async function load() {
      try {
        const res = await fetch(url, { signal: controller.signal });
        if (!res.ok) throw new Error(`${res.status}`);
        setData(await res.json());
      } catch (err) {
        if ((err as DOMException).name !== 'AbortError')
          setError((err as Error).message);
      } finally { setLoading(false); }
    }
    load();
    return () => controller.abort();
  }, [url]);

  return { data, loading, error };
}

Gancho useLocalStorage

Um gancho personalizado que sincroniza um valor com localStorage e permanece sincronizado entre abas por meio do evento de armazenamento.

function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });

  const set = useCallback((newValue: T) => {
    setValue(newValue);
    localStorage.setItem(key, JSON.stringify(newValue));
  }, [key]);

  return [value, set] as const;
}

Ganchos personalizados compartilham lógica, não estado

Cada componente que chama um gancho personalizado obtém seu próprio estado independente. Ganchos não são instâncias únicas — são chamadas de função que criam instâncias de estado isoladas.

Convenções de nomenclatura

Os nomes de ganchos personalizados devem começar com use. Isso é imposto pelas regras de análise de ganchos do ESLint, que garantem que os ganchos sejam chamados no nível superior e na ordem correta.

Retornando vários valores

Ganchos personalizados normalmente retornam um objeto (valores nomeados) ou uma tupla (como useState). As tuplas permitem desestruturação com nomes personalizados; os objetos são autoexplicativos.

// Tuple (like useState):
const [value, setValue] = useLocalStorage('key', 0);

// Object (more descriptive):
const { data, loading, error, refetch } = useFetch('/api/users');

Gancho useDebounce

Aplique uma espera a um valor: atualize-o somente depois que o usuário parar de digitar pelo atraso especificado.

function useDebounce<T>(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);

  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);

  return debounced;
}

// Usage:
const debouncedSearch = useDebounce(searchTerm, 300);
useEffect(() => { search(debouncedSearch); }, [debouncedSearch]);

Gancho useMediaQuery

Encapsula window.matchMedia em um gancho para ler de forma reativa os resultados de consultas de mídia CSS.

function useMediaQuery(query: string): boolean {
  const [matches, setMatches] = useState(() => window.matchMedia(query).matches);

  useEffect(() => {
    const mq = window.matchMedia(query);
    const handler = (e: MediaQueryListEvent) => setMatches(e.matches);
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, [query]);

  return matches;
}

Testando ganchos personalizados

Use o utilitário @testing-library/react de renderHook() para testar ganchos personalizados isoladamente, sem envolvê-los em um componente.

import { renderHook, act } from '@testing-library/react';

test('useCounter increments', () => {
  const { result } = renderHook(() => useCounter(0));
  expect(result.current.count).toBe(0);
  act(() => result.current.increment());
  expect(result.current.count).toBe(1);
});

Bibliotecas publicadas de ganchos

As bibliotecas comunitárias de ganchos fornecem centenas de ganchos testados: @uidotdev/usehooks, react-use, usehooks-ts. Antes de escrever um gancho personalizado, verifique se já existe uma versão bem testada.

Verificação rápida

O que acontece com o estado dentro de um gancho personalizado quando ele é chamado por dois componentes diferentes?

Recapitulação: ganchos personalizados

Ganchos personalizados extraem lógica que mantém estado para reutilização. Nomeie-os com o prefixo use. Cada componente que chama o gancho obtém estado independente. Retorne objetos ou tuplas. Padrões comuns: useFetch, useLocalStorage, useDebounce, useMediaQuery. Teste com renderHook(). Verifique bibliotecas comunitárias antes de escrever do zero.

Perguntas Frequentes

A aula “Hooks personalizados: extraindo lógica reutilizável” é grátis?

Sim — o texto completo de “Hooks personalizados: extraindo lógica reutilizável” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Frontend Academy, atualize para CoddyKit PRO. O curso de Frontend Academy inclui 4 aulas no total.

O que vou aprender em “Hooks personalizados: extraindo lógica reutilizável”?

Mova a lógica com estado para funções personalizadas use* a fim de compartilhar comportamento entre componentes sem duplicar código ou elevar o estado. Você pratica Frontend Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Frontend Academy?

Nenhuma experiência prévia é necessária. Frontend Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Hooks personalizados: extraindo lógica reutilizável”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Frontend Academy?

Sim. Cada aula de Frontend Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. useContext para estado global
  2. useReducer para estado complexo
  3. useMemo e useCallback para desempenho
  4. Hooks personalizados: extraindo lógica reutilizável
← Voltar para Frontend Academy