JavaScript Avançado

Boas Práticas de Hooks Customizados em React: Abstraindo Lógica Reutilizável para Times Ágeis

8 min de leitura

Boas Práticas de Hooks Customizados em React: Abstraindo Lógica Reutilizável para Times Ágeis

Entendendo Hooks Customizados 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 , indicando que seguem as regras dos hooks do React. 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). Criando Seu Primeiro Hook Customizado Exemplo Prático: useLocalStorage Vamos criar um hook que sincroniza estado com o localStorage automaticamente. Este é um padrão comum em aplicações reais: Erro ao salvar ${chave}: Agora use o hook em qualquer componente: O hook carrega o valor do localStorage ao iniciar, sincroniza alterações e persiste tudo automaticamente. Sem

<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(() =&gt; {

try {

const itemArmazenado = window.localStorage.getItem(chave);

return itemArmazenado ? JSON.parse(itemArmazenado) : valorInicial;

} catch {

return valorInicial;

}

});

const atualizarValor = React.useCallback((novoValor) =&gt; {

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(&#039;tema&#039;, &#039;claro&#039;);

return (

&lt;div className={tema}&gt;

&lt;button onClick={() =&gt; setTema(tema === &#039;claro&#039; ? &#039;escuro&#039; : &#039;claro&#039;)}&gt;

Alternar Tema

&lt;/button&gt;

&lt;p&gt;Tema atual: {tema}&lt;/p&gt;

&lt;/div&gt;

);

}</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: &#039;ocioso&#039;,

dados: null,

erro: null,

});

React.useEffect(() =&gt; {

let montado = true;

setEstado({ status: &#039;pendente&#039;, dados: null, erro: null });

funcaoAssincrona()

.then((dados) =&gt; {

if (montado) {

setEstado({ status: &#039;sucesso&#039;, dados, erro: null });

}

})

.catch((erro) =&gt; {

if (montado) {

setEstado({ status: &#039;erro&#039;, dados: null, erro });

}

});

return () =&gt; {

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(

() =&gt; fetch(&#039;/api/usuarios&#039;).then(res =&gt; res.json()),

[]

);

if (status === &#039;pendente&#039;) return &lt;p&gt;Carregando...&lt;/p&gt;;

if (status === &#039;erro&#039;) return &lt;p&gt;Erro: {erro.message}&lt;/p&gt;;

if (status === &#039;sucesso&#039;) {

return (

&lt;ul&gt;

{usuarios.map(u =&gt; &lt;li key={u.id}&gt;{u.nome}&lt;/li&gt;)}

&lt;/ul&gt;

);

}

}</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(() =&gt; {

const identificador = setTimeout(() =&gt; {

setValorDebounceado(valor);

}, atraso);

return () =&gt; 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(&#039;&#039;);

const buscaDebounceada = useDebounce(busca, 300);

const { dados: resultados } = useAsync(

() =&gt; fetch(/api/usuarios?q=${buscaDebounceada}).then(r =&gt; r.json()),

[buscaDebounceada]

);

return (

&lt;&gt;

&lt;input

value={busca}

onChange={(e) =&gt; setBusca(e.target.value)}

placeholder=&quot;Buscar...&quot;

/&gt;

&lt;ul&gt;

{resultados?.map(u =&gt; &lt;li key={u.id}&gt;{u.nome}&lt;/li&gt;)}

&lt;/ul&gt;

&lt;/&gt;

);

}</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>

Comentários

Mais em JavaScript Avançado

Dominando Fastify em Node.js: Alta Performance e Schema Validation com JSON Schema em Projetos Reais
Dominando Fastify em Node.js: Alta Performance e Schema Validation com JSON Schema em Projetos Reais

Por que Fastify? Fastify é um framework web moderno para Node.js que se desta...

Boas Práticas de PostgreSQL Avançado com Node.js: Transactions, CTEs e Window Functions para Times Ágeis
Boas Práticas de PostgreSQL Avançado com Node.js: Transactions, CTEs e Window Functions para Times Ágeis

Transactions com PostgreSQL e Node.js As transações são fundamentais para gar...

Boas Práticas de Proxy e Reflect Avançados: Construindo Frameworks Reativos do Zero para Times Ágeis
Boas Práticas de Proxy e Reflect Avançados: Construindo Frameworks Reativos do Zero para Times Ágeis

Entendendo Proxy e Reflect na Prática Proxy e Reflect são dois APIs JavaScrip...