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
- useContext para estado global
- useReducer para estado complexo
- useMemo e useCallback para desempenho
- Hooks personalizados: extraindo lógica reutilizável