<h2>Entendendo Hooks Customizados</h2>
<p>Um Hook customizado é uma função JavaScript que reutiliza lógica de estado e efeitos do React. Diferente dos componentes, hooks não retornam JSX — eles retornam dados e funções que encapsulam comportamentos específicos. A convenção é nomear hooks com o prefixo <code>use</code>, indicando que seguem as regras dos hooks do React.</p>
<p>Por que isso importa? Quando você percebe que a mesma lógica aparece em múltiplos componentes, é hora de extrair para um hook customizado. Isso elimina duplicação, facilita testes e deixa seus componentes mais legíveis. Um hook bem projetado torna seu código reutilizável sem depender de render props ou HOCs (Higher Order Components).</p>
<h2>Criando Seu Primeiro Hook Customizado</h2>
<h3>Exemplo Prático: useLocalStorage</h3>
<p>Vamos criar um hook que sincroniza estado com o localStorage automaticamente. Este é um padrão comum em aplicações reais:</p>
<pre><code class="language-javascript">function useLocalStorage(chave, valorInicial) {
const [valor, setValor] = React.useState(() => {
try {
const itemArmazenado = window.localStorage.getItem(chave);
return itemArmazenado ? JSON.parse(itemArmazenado) : valorInicial;
} catch {
return valorInicial;
}
});
const atualizarValor = React.useCallback((novoValor) => {
try {
const valorParaSalvar =
novoValor instanceof Function ? novoValor(valor) : novoValor;
setValor(valorParaSalvar);
window.localStorage.setItem(chave, JSON.stringify(valorParaSalvar));
} catch (erro) {
console.error(Erro ao salvar ${chave}:, erro);
}
}, [chave, valor]);
return [valor, atualizarValor];
}</code></pre>
<p>Agora use o hook em qualquer componente:</p>
<pre><code class="language-javascript">function MeuComponente() {
const [tema, setTema] = useLocalStorage('tema', 'claro');
return (
<div className={tema}>
<button onClick={() => setTema(tema === 'claro' ? 'escuro' : 'claro')}>
Alternar Tema
</button>
<p>Tema atual: {tema}</p>
</div>
);
}</code></pre>
<p>O hook carrega o valor do localStorage ao iniciar, sincroniza alterações e persiste tudo automaticamente. Sem duplicar essa lógica em cada componente.</p>
<h2>Padrões Avançados de Composição</h2>
<h3>useAsync: Gerenciando Requisições</h3>
<p>Requisições HTTP são frequentes e repetitivas. Este hook abstrai toda a complexidade:</p>
<pre><code class="language-javascript">function useAsync(funcaoAssincrona, dependencias = []) {
const [estado, setEstado] = React.useState({
status: 'ocioso',
dados: null,
erro: null,
});
React.useEffect(() => {
let montado = true;
setEstado({ status: 'pendente', dados: null, erro: null });
funcaoAssincrona()
.then((dados) => {
if (montado) {
setEstado({ status: 'sucesso', dados, erro: null });
}
})
.catch((erro) => {
if (montado) {
setEstado({ status: 'erro', dados: null, erro });
}
});
return () => {
montado = false;
};
}, dependencias);
return estado;
}</code></pre>
<p>Exemplo de uso:</p>
<pre><code class="language-javascript">function ListaUsuarios() {
const { status, dados: usuarios, erro } = useAsync(
() => fetch('/api/usuarios').then(res => res.json()),
[]
);
if (status === 'pendente') return <p>Carregando...</p>;
if (status === 'erro') return <p>Erro: {erro.message}</p>;
if (status === 'sucesso') {
return (
<ul>
{usuarios.map(u => <li key={u.id}>{u.nome}</li>)}
</ul>
);
}
}</code></pre>
<p>Este hook elimina a necessidade de escrever useEffect, tratamento de erros e gerenciamento de estado em cada requisição.</p>
<h3>useDebounce: Otimizando Buscas</h3>
<p>Buscas em tempo real precisam evitar requisições excessivas. Use debounce:</p>
<pre><code class="language-javascript">function useDebounce(valor, atraso = 500) {
const [valorDebounceado, setValorDebounceado] = React.useState(valor);
React.useEffect(() => {
const identificador = setTimeout(() => {
setValorDebounceado(valor);
}, atraso);
return () => clearTimeout(identificador);
}, [valor, atraso]);
return valorDebounceado;
}</code></pre>
<p>Na prática:</p>
<pre><code class="language-javascript">function BuscaUsuarios() {
const [busca, setBusca] = React.useState('');
const buscaDebounceada = useDebounce(busca, 300);
const { dados: resultados } = useAsync(
() => fetch(/api/usuarios?q=${buscaDebounceada}).then(r => r.json()),
[buscaDebounceada]
);
return (
<>
<input
value={busca}
onChange={(e) => setBusca(e.target.value)}
placeholder="Buscar..."
/>
<ul>
{resultados?.map(u => <li key={u.id}>{u.nome}</li>)}
</ul>
</>
);
}</code></pre>
<h2>Boas Práticas e Erros Comuns</h2>
<h3>O que Fazer</h3>
<ul>
<li><strong>Nomeie com <code>use</code></strong>: <code>useFetch</code>, <code>useForm</code>, <code>usePermissoes</code>. A comunidade reconhecerá imediatamente como hooks.</li>
<li><strong>Respeite as regras dos hooks</strong>: Chamadas de hooks sempre no escopo raiz da função, nunca dentro de condições ou loops.</li>
<li><strong>Documente o contrato</strong>: Que parâmetros o hook aceita? O que retorna? Quais dependências ele gerencia?</li>
<li><strong>Teste isoladamente</strong>: Hooks podem ser testados independentemente com bibliotecas como <code>@testing-library/react-hooks</code>.</li>
</ul>
<h3>O que Evitar</h3>
<blockquote><p>❌ Não crie lógica condicional com hooks: <code>if (condicao) useMeuHook()</code></p></blockquote>
<blockquote><p></p></blockquote>
<p>Sempre chame hooks incondicionalmente: <code>const resultado = useMeuHook(); if (resultado) {...}</code></p>
<blockquote><p>❌ Não esqueça dependências em useEffect dentro do hook</p></blockquote>
<blockquote><p></p></blockquote>
<p>Liste todas as variáveis usadas em dependências do useEffect</p>
<p>Hooks customizados que ignoram essas regras causam bugs silenciosos e imprevísiveis. O linter ESLint com plugin react-hooks detecta a maioria desses problemas.</p>
<h2>Conclusão</h2>
<p>Hooks customizados são a ferramenta mais poderosa do React moderno para reutilização de lógica. Eles substituem padrões antigos como render props e HOCs, deixando o código mais limpo e compreensível. Ao dominar <code>useLocalStorage</code>, <code>useAsync</code> e <code>useDebounce</code>, você tem os padrões fundamentais para criar qualquer abstração necessária em projetos reais.</p>
<p>Lembre-se: toda vez que copiar e colar lógica com <code>useState</code> e <code>useEffect</code>, você é um candidato a criar um novo hook. Isso não apenas elimina duplicação — transforma código complexo em funções simples e testáveis que toda a equipe compreende rapidamente.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://react.dev/reference/react" target="_blank" rel="noopener noreferrer">Documentação Oficial de Hooks - React</a></li>
<li><a href="https://react.dev/learn/reusing-logic-with-custom-hooks" target="_blank" rel="noopener noreferrer">Building Your Own Hooks - React Guide</a></li>
<li><a href="https://github.com/testing-library/react-hooks-testing-library" target="_blank" rel="noopener noreferrer">Testing Library React Hooks</a></li>
<li><a href="https://www.npmjs.com/package/eslint-plugin-react-hooks" target="_blank" rel="noopener noreferrer">ESLint Plugin React Hooks</a></li>
<li><a href="https://epicreact.dev/" target="_blank" rel="noopener noreferrer">Advanced React Patterns - Kent C. Dodds</a></li>
</ul>