React Hooks são funções que permitem usar estado, efeitos, contexto e outros recursos do React em componentes funcionais, sem escrever classes. Introduzidos no React 16.8, os hooks mudaram radicalmente a abordagem de desenvolvimento, substituindo padrões complexos como HOC e render props. De acordo com React, 2024, existem 15 hooks embutidos cobrindo todos os cenários de gerenciamento de componentes.
Principais conclusões
React Hooks são um conjunto de funções integradas ao React que permitem “conectar-se” aos mecanismos internos do framework sem criar classes. Antes do React 16.8, componentes funcionais apenas podiam aceitar props e retornar JSX — qualquer lógica complexa exigia um componente de classe.
O problema dos componentes de classe é a divisão da lógica relacionada. O código relacionado a uma mesma entidade (por exemplo, uma assinatura WebSocket) era forçadamente dividido entre componentDidMount, componentDidUpdate e componentWillUnmount. Os hooks resolvem isso: toda a lógica é agrupada em um único useEffect.
O segundo problema é a reutilização de lógica. Para reutilizar a lógica de estado entre componentes de classe, era preciso aplicar HOC (Componentes de Ordem Superior) ou render props — ambos os padrões criavam aninhamento extra na árvore de componentes. Hooks personalizados resolvem isso sem aninhamento.
useState é o hook mais básico. Ele recebe um valor inicial e retorna uma tupla: o valor atual e uma função para atualizá-lo. Ao contrário de this.setState nas classes, useState não mescla objetos — o novo estado substitui completamente o anterior para uma chamada useState específica.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
O setter do useState pode aceitar uma função que recebe o valor anterior e retorna um novo. A forma funcional é obrigatória quando o novo estado é calculado a partir do anterior e chamadas setState em lote são possíveis — cada chamada receberá o valor anterior atual.
useEffect é um hook para realizar efeitos colaterais em componentes funcionais. Ele recebe dois argumentos: uma função de efeito e um array de dependências. O React executa o efeito após cada renderização, mas quando dependências são especificadas — apenas quando alguma delas muda.
Um array de dependências vazio [] significa que o efeito é executado uma vez após a primeira renderização (análogo a componentDidMount). Retornar uma função do efeito é uma limpeza que executa ao desmontar (análogo a componentWillUnmount) e antes de reexecutar o efeito.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Limpeza - fechar socket
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
As dependências do useEffect são a única conexão entre o efeito e o mundo externo. Se o efeito usa uma variável de props ou estado, ela deve estar no array de dependências. Ignorar esta regra é uma fonte de bugs com closures obsoletas (stale closures).
useContext é um hook para ler valores do contexto. Ele recebe um objeto de contexto criado por React.createContext e retorna o valor atual. Se o contexto muda, o componente automaticamente re-renderiza com o novo valor.
Antes do useContext, acessar o contexto em componentes funcionais exigia envolver em Context.Consumer, o que criava aninhamento. useContext elimina completamente o Consumer — basta chamar o hook em qualquer lugar do componente funcional.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('claro');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext não substitui Redux ou estado global. O React recomenda usar contexto para valores que mudam com pouca frequência — tema, localidade, status de autenticação. Para dados atualizados com frequência, cada re-renderização recalculará todos os consumidores do contexto, o que pode reduzir o desempenho.
useReducer é uma alternativa ao useState para estados com lógica de atualização complexa. Ele recebe uma função redutora e um estado inicial, retorna o estado atual e uma função dispatch. O redutor recebe o estado atual e uma ação, retorna um novo estado — uma função pura sem efeitos colaterais.
useReducer é especialmente útil quando múltiplas ações (set, reset, increment, addItem) gerenciam um mesmo estado. Em vez de múltiplos useState com diferentes setters — um único redutor com tipos de ação claros. Isso torna a lógica previsível e fácil de testar.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef cria um objeto mutável que persiste entre renderizações. Alterar current não dispara uma re-renderização. Os principais usos são acessar elementos DOM e armazenar valores mutáveis (temporizadores, valores anteriores, instâncias de bibliotecas).
useMemo memoiza o resultado de uma função. Ele recalcula o valor apenas quando uma dependência muda. Use-o para cálculos caros — filtragem de arrays grandes, ordenação, formatação de dados. useCallback é um caso especial de useMemo para memoizar funções.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofoco ao montar
useEffect(() => {
inputRef.current?.focus();
}, []);
// Memoizar resultado filtrado
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Memoizar callback
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Uma regra importante: useRef, useMemo e useCallback são ferramentas de otimização, não construções obrigatórias. Não envolva tudo em useMemo — meça o desempenho primeiro. A otimização prematura complica o código. O React renderiza componentes rapidamente; useMemo só é necessário quando os cálculos são realmente caros (O(n²) e acima).
O React introduziu duas regras estritas para hooks. Primeira: chame hooks apenas no nível superior — não dentro de condições, loops ou funções aninhadas. Segunda: chame hooks apenas de componentes funcionais React ou hooks personalizados. O plugin ESLint eslint-plugin-react-hooks verifica automaticamente ambas as regras.
O erro mais comum é violar a ordem de chamada dos hooks. O React depende da ordem de chamada dos hooks entre renderizações. Se um hook está dentro de uma condição e não é executado em uma renderização, todos os hooks subsequentes se deslocam, causando bugs.
| Erro | O que acontece | Solução |
|---|---|---|
| Hook em condição | A ordem dos hooks se quebra | Mova a lógica da condição para dentro do hook |
| Stale closure | O efeito usa um valor antigo | Adicione todas as dependências ao array |
| Loop infinito | useEffect sem dependências altera o estado | Especifique dependências ou use useReducer |
| Mutação de ref no render | Efeito colateral no corpo da função | Mova mutações para useEffect |
O segundo erro comum é stale closure no useEffect. Se o efeito usa uma variável não especificada nas dependências, o efeito “captura” seu valor no momento da criação do closure. A solução é sempre incluir todas as variáveis usadas no array de dependências. Se houver muitas dependências, considere dividir o efeito em vários.
Perguntas frequentes
O React depende da ordem de chamada dos hooks entre renderizações. Se um hook é chamado dentro de uma condição e não é executado em uma renderização, todos os hooks subsequentes se deslocam — seus estados se misturam. A regra do ESLint hooks/exhaustive-deps alerta sobre isso.
useLayoutEffect é síncrono e executa antes do navegador pintar as alterações. useEffect é assíncrono — após a pintura. Use useLayoutEffect apenas quando precisar medir ou alterar o DOM antes que o usuário veja as alterações (posição de popover, restauração de scroll).
Use AbortController dentro do useEffect. Crie um controller, passe o signal para fetch, e chame controller.abort() na função de limpeza. Para navegadores antigos, use uma flag cancelled: return () => { cancelled = true; } com uma verificação antes de setState.
Um hook personalizado é uma função JavaScript com o prefixo use que utiliza hooks embutidos. Crie um quando a mesma lógica com hooks se repete em dois ou mais componentes. Exemplos: useDebounce, useLocalStorage, useMediaQuery. Hooks personalizados substituem HOC e render props.
No Strict Mode, o React intencionalmente monta e desmonta o componente duas vezes em desenvolvimento para encontrar bugs com efeitos não limpos. Se a limpeza está escrita corretamente, a chamada dupla não causa problemas. Isso não acontece em modo de produção.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também