Resumo de Hooks do React

Resumo de Hooks do React

Hooks Básicos

useState

Permite adicionar estado a componentes funcionais.

import { useState } from 'react';

function Contador() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Você clicou {count} vezes</p>
      <button onClick={() => setCount(count + 1)}>
        Clique aqui
      </button>
    </div>
  );
}

Com função de atualização:

function Contador() {
  const [count, setCount] = useState(0);

  const incrementar = () => {
    setCount(prevCount => prevCount + 1);
  };

  return <button onClick={incrementar}>Incrementar</button>;
}

useEffect

Executa efeitos colaterais em componentes funcionais.

import { useState, useEffect } from 'react';

function ExemploEffect() {
  const [dados, setDados] = useState(null);

  useEffect(() => {
    // Executa após a renderização
    fetch('https://api.exemplo.com/dados')
      .then(res => res.json())
      .then(data => setDados(data));

    // Função de limpeza (cleanup)
    return () => {
      console.log('Limpeza do efeito');
    };
  }, []); // Array vazio = executa apenas uma vez

  return <div>{dados ? JSON.stringify(dados) : 'Carregando...'}</div>;
}

Com dependências:

function Busca({ termo }) {
  const [resultados, setResultados] = useState([]);

  useEffect(() => {
    if (termo) {
      fetch(`/api/busca?q=${termo}`)
        .then(res => res.json())
        .then(setResultados);
    }
  }, [termo]); // Re-executa quando 'termo' muda

  return <ul>{resultados.map(r => <li key={r.id}>{r.nome}</li>)}</ul>;
}

useContext

Consome valores de um contexto React.

import { createContext, useContext, useState } from 'react';

const TemaContext = createContext();

function App() {
  const [tema, setTema] = useState('claro');

  return (
    <TemaContext.Provider value={{ tema, setTema }}>
      <Toolbar />
    </TemaContext.Provider>
  );
}

function Toolbar() {
  const { tema, setTema } = useContext(TemaContext);

  return (
    <div style={{ background: tema === 'claro' ? '#fff' : '#333' }}>
      <button onClick={() => setTema(tema === 'claro' ? 'escuro' : 'claro')}>
        Alternar Tema
      </button>
    </div>
  );
}

Hooks Adicionais

useReducer

Alternativa ao useState para lógica de estado mais complexa.

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return initialState;
    default:
      throw new Error();
  }
}

function Contador() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Contagem: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}

useCallback

Memoriza uma função para evitar re-criações desnecessárias.

import { useState, useCallback } from 'react';

function ListaDeTarefas() {
  const [tarefas, setTarefas] = useState([]);

  const adicionarTarefa = useCallback((texto) => {
    setTarefas(prev => [...prev, { id: Date.now(), texto }]);
  }, []); // Função não muda entre renderizações

  return <FormularioTarefa onAdicionar={adicionarTarefa} />;
}

function FormularioTarefa({ onAdicionar }) {
  const [input, setInput] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    onAdicionar(input);
    setInput('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input value={input} onChange={e => setInput(e.target.value)} />
      <button type="submit">Adicionar</button>
    </form>
  );
}

useMemo

Memoriza um valor computado para otimização de performance.

import { useState, useMemo } from 'react';

function ListaFiltrada({ items }) {
  const [filtro, setFiltro] = useState('');

  const itemsFiltrados = useMemo(() => {
    console.log('Filtrando items...');
    return items.filter(item =>
      item.toLowerCase().includes(filtro.toLowerCase())
    );
  }, [items, filtro]); // Recomputa apenas quando items ou filtro mudam

  return (
    <div>
      <input
        value={filtro}
        onChange={e => setFiltro(e.target.value)}
        placeholder="Filtrar..."
      />
      <ul>
        {itemsFiltrados.map((item, i) => <li key={i}>{item}</li>)}
      </ul>
    </div>
  );
}

useRef

Cria uma referência mutável que persiste entre renderizações.

import { useRef, useEffect } from 'react';

function InputComFoco() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Foca no input quando o componente é montado
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

Para armazenar valores mutáveis:

function Cronometro() {
  const [segundos, setSegundos] = useState(0);
  const intervalRef = useRef(null);

  const iniciar = () => {
    intervalRef.current = setInterval(() => {
      setSegundos(s => s + 1);
    }, 1000);
  };

  const parar = () => {
    clearInterval(intervalRef.current);
  };

  useEffect(() => {
    return () => clearInterval(intervalRef.current);
  }, []);

  return (
    <div>
      <p>{segundos}s</p>
      <button onClick={iniciar}>Iniciar</button>
      <button onClick={parar}>Parar</button>
    </div>
  );
}

useImperativeHandle

Customiza a instância exposta ao usar ref em componentes pai.

import { useRef, useImperativeHandle, forwardRef } from 'react';

const InputCustomizado = forwardRef((props, ref) => {
  const inputRef = useRef();

  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    scrollIntoView: () => {
      inputRef.current.scrollIntoView();
    },
    getValue: () => {
      return inputRef.current.value;
    }
  }));

  return <input ref={inputRef} {...props} />;
});

function Formulario() {
  const inputRef = useRef();

  return (
    <div>
      <InputCustomizado ref={inputRef} />
      <button onClick={() => inputRef.current.focus()}>
        Focar no Input
      </button>
      <button onClick={() => alert(inputRef.current.getValue())}>
        Mostrar Valor
      </button>
    </div>
  );
}

useLayoutEffect

Similar ao useEffect, mas executa sincronamente após mutações do DOM.

import { useState, useLayoutEffect, useRef } from 'react';

function TooltipPosicionado() {
  const [tooltipHeight, setTooltipHeight] = useState(0);
  const tooltipRef = useRef(null);

  useLayoutEffect(() => {
    // Lê o DOM e atualiza antes da pintura
    const height = tooltipRef.current.getBoundingClientRect().height;
    setTooltipHeight(height);
  }, []);

  return (
    <div
      ref={tooltipRef}
      style={{ top: `-${tooltipHeight}px` }}
    >
      Tooltip
    </div>
  );
}

useDebugValue

Exibe um rótulo customizado no React DevTools.

import { useState, useDebugValue } from 'react';

function useStatusOnline() {
  const [isOnline, setIsOnline] = useState(true);

  // Aparece no React DevTools
  useDebugValue(isOnline ? 'Online' : 'Offline');

  return isOnline;
}

function Perfil() {
  const isOnline = useStatusOnline();
  return <div>{isOnline ? '🟢' : '🔴'} Status</div>;
}

Hooks do React 18+

useId

Gera IDs únicos e estáveis para acessibilidade.

import { useId } from 'react';

function CampoFormulario({ label }) {
  const id = useId();

  return (
    <div>
      <label htmlFor={id}>{label}</label>
      <input id={id} type="text" />
    </div>
  );
}

useTransition

Marca atualizações de estado como transições não urgentes.

import { useState, useTransition } from 'react';

function BuscaComTransition() {
  const [isPending, startTransition] = useTransition();
  const [input, setInput] = useState('');
  const [resultados, setResultados] = useState([]);

  const handleChange = (e) => {
    setInput(e.target.value);

    startTransition(() => {
      // Esta atualização é marcada como não urgente
      const novosResultados = buscarResultados(e.target.value);
      setResultados(novosResultados);
    });
  };

  return (
    <div>
      <input value={input} onChange={handleChange} />
      {isPending && <p>Buscando...</p>}
      <ul>
        {resultados.map(r => <li key={r.id}>{r.nome}</li>)}
      </ul>
    </div>
  );
}

useDeferredValue

Adia a atualização de uma parte da UI.

import { useState, useDeferredValue, useMemo } from 'react';

function ListaComDeferredValue({ items }) {
  const [query, setQuery] = useState('');
  const deferredQuery = useDeferredValue(query);

  const resultados = useMemo(() => {
    return items.filter(item =>
      item.toLowerCase().includes(deferredQuery.toLowerCase())
    );
  }, [items, deferredQuery]);

  return (
    <div>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <ListaLenta items={resultados} />
    </div>
  );
}

useSyncExternalStore

Permite que componentes se inscrevam em stores externas.

import { useSyncExternalStore } from 'react';

function useWindowWidth() {
  return useSyncExternalStore(
    (callback) => {
      window.addEventListener('resize', callback);
      return () => window.removeEventListener('resize', callback);
    },
    () => window.innerWidth,
    () => 0 // Valor no servidor
  );
}

function ResponsiveComponent() {
  const width = useWindowWidth();
  return <div>Largura: {width}px</div>;
}

useInsertionEffect

Hook para bibliotecas CSS-in-JS injetarem estilos antes de mutações do DOM.

import { useInsertionEffect } from 'react';

function useCSS(rule) {
  useInsertionEffect(() => {
    // Injeta estilos antes do useLayoutEffect
    const style = document.createElement('style');
    style.textContent = rule;
    document.head.appendChild(style);

    return () => {
      document.head.removeChild(style);
    };
  }, [rule]);
}

function ComponenteEstilizado() {
  useCSS('.minha-classe { color: red; }');
  return <div className="minha-classe">Texto vermelho</div>;
}

Hooks Customizados

Você pode criar seus próprios hooks combinando hooks existentes.

import { useState, useEffect } from 'react';

// Hook customizado para buscar dados
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    let cancelado = false;

    setLoading(true);
    fetch(url)
      .then(res => res.json())
      .then(data => {
        if (!cancelado) {
          setData(data);
          setLoading(false);
        }
      })
      .catch(err => {
        if (!cancelado) {
          setError(err);
          setLoading(false);
        }
      });

    return () => {
      cancelado = true;
    };
  }, [url]);

  return { data, loading, error };
}

// Uso do hook customizado
function MeuComponente() {
  const { data, loading, error } = useFetch('/api/usuarios');

  if (loading) return <p>Carregando...</p>;
  if (error) return <p>Erro: {error.message}</p>;

  return <ul>{data.map(u => <li key={u.id}>{u.nome}</li>)}</ul>;
}

Regras dos Hooks

  1. Chame hooks apenas no nível superior - Não chame hooks dentro de loops, condições ou funções aninhadas
  2. Chame hooks apenas de funções React - Componentes funcionais ou hooks customizados
  3. Hooks customizados devem começar com "use" - Convenção para identificação
// ❌ Errado
function Componente({ condicao }) {
  if (condicao) {
    const [state, setState] = useState(0); // Erro!
  }
}

// ✅ Correto
function Componente({ condicao }) {
  const [state, setState] = useState(0);

  if (condicao) {
    // Use o state aqui
  }
}