React Hooks — o que são, principais hooks e regras de uso

Autor: IT Sectr Publicado: 2026-07-04 Tempo de leitura: 10 min

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

  • Hooks — funções para conectar recursos do React a componentes funcionais sem extends Component.
  • useState — o hook básico para gerenciamento de estado, retorna um valor e um setter.
  • useEffect — para efeitos colaterais: requisições API, inscrições, temporizadores, manipulações DOM.
  • useContext — para acessar o contexto do React sem wrappers Consumer e static contextType.
  • Regras dos hooks — chamar apenas no nível superior e apenas dentro de componentes funcionais ou hooks personalizados.

O que são React Hooks?

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 — Gerenciamento de estado

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.

js
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>
  );
}

Forma funcional de setState

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 — Efeitos colaterais e ciclo de vida

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.

js
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 — Acesso ao contexto

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.

js
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 — Lógica de estado complexa

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.

js
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, useMemo, useCallback — Otimização

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.

js
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).

Regras dos hooks e erros comuns

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.

ErroO que aconteceSolução
Hook em condiçãoA ordem dos hooks se quebraMova a lógica da condição para dentro do hook
Stale closureO efeito usa um valor antigoAdicione todas as dependências ao array
Loop infinitouseEffect sem dependências altera o estadoEspecifique dependências ou use useReducer
Mutação de ref no renderEfeito colateral no corpo da funçãoMova 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

Por que os hooks não podem ser chamados dentro de condições?

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.

Qual a diferença entre useEffect e useLayoutEffect?

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).

Como cancelar uma requisição fetch ao desmontar?

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.

O que é um hook personalizado e quando criar um?

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.

Por que useEffect executa duas vezes no Strict Mode?

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

  • React Hooks — funções para usar estado, efeitos e contexto em componentes funcionais, substituindo classes.
  • useState — hook básico para estado local com um setter funcional para atualizações seguras.
  • useEffect — hook universal para efeitos colaterais com array de dependências e função de limpeza.
  • useContext — acesso direto ao contexto do React sem wrappers Consumer e aninhamento extra.
  • useReducer — para lógica de estado complexa com tipos de ação, uma alternativa ao Redux dentro de um único componente.
  • useRef, useMemo, useCallback — ferramentas de otimização: referências DOM, memoização de valores e callbacks.
  • Regra do nível superior — restrição estrita de chamar hooks fora de condições, loops e funções aninhadas.

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.

Discutir o projeto

Leia também