⚛️ React

React

A biblioteca que mudou o jeito de construir interfaces. Com React você pensa em componentes reutilizáveis e deixa a biblioteca cuidar do DOM — seu código fica focado no o quê mostrar, não no como atualizar a tela.

O que é React?

React é uma biblioteca JavaScript criada pelo Facebook (Meta) em 2013 para construir interfaces de usuário. Ela resolve um problema muito real: quando sua página tem dezenas de elementos que precisam refletir dados que mudam o tempo todo, manipular o DOM manualmente com document.querySelector vira um pesadelo.

🪟

A analogia da janela: com JavaScript puro você sai pintando tijolo por tijolo na parede toda vez que algo muda. Com React você descreve como a janela deve parecer em cada estado, e ele calcula sozinho quais tijolos precisam ser repintados. Você declara o resultado; o React decide o caminho mais eficiente para chegar lá.

Por que React é tão popular?

Modelo mental
UI = f(estado)
A interface é sempre uma função pura do estado. Dado um estado, o resultado na tela é sempre o mesmo — previsível, testável e fácil de raciocinar.
Reutilização
Componentes
Você constrói peças independentes e as combina como blocos de Lego. Um botão criado uma vez pode ser usado em cem lugares diferentes.
Performance
Virtual DOM
React mantém uma cópia virtual da árvore DOM em memória. Quando o estado muda, ele compara a versão nova com a antiga e atualiza somente o que mudou no DOM real — operação muito mais rápida.
Ecossistema
Ecossistema gigante
Next.js, React Native, React Query, Zustand, Radix UI… O ecossistema React é o maior do frontend. O que você aprender aqui abre portas para mobile, SSR, e muito mais.

React vs JavaScript puro — o problema que ele resolve

❌ JavaScript Puro — imperativo

JS
// "Conta" num botão
let count = 0
const btn = document
  .querySelector('#btn')

btn.addEventListener('click', () => {
  count++
  // preciso MANUALMENTE
  // atualizar o texto
  btn.textContent =
    `Clicou ${count}x`
})

Você diz como mudar o DOM a cada evento.

✅ React — declarativo

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

  return (
    <button onClick={() =>
      setCount(count + 1)}>
      Clicou {count}x
    </button>
  )
}

Você descreve o que renderizar dado o estado.

O fluxo React de ponta a ponta

📝
Você escreve
componentes em TSX
⚙️
Vite / Babel
compila para JS puro
🌳
Virtual DOM
React cria a árvore
🔍
Diffing
compara versões
🎨
Patch no DOM
só o que mudou
👁️
Tela atualizada
mínimo de trabalho

JSX e TSX — HTML dentro do JavaScript

JSX (JavaScript XML) é uma extensão de sintaxe que permite escrever algo parecido com HTML dentro de arquivos JavaScript. TSX é a mesma coisa, mas em TypeScript — e é o formato recomendado em projetos modernos porque adiciona tipagem estática ao código.

📎

JSX vs TSX — qual usar?
Arquivos .jsx usam JavaScript puro (sem tipos). Arquivos .tsx usam TypeScript (com tipos). Nesta documentação todos os exemplos são em .tsx — que é o padrão da indústria hoje. As diferenças de sintaxe são mínimas; o que você aprender aqui funciona em ambos.

JSX não é HTML — as diferenças importantes

HTMLJSX / TSXPor quê?
class="btn" className="btn" class é palavra reservada em JS
for="email" htmlFor="email" for é palavra reservada em JS
onclick="fn()" onClick={fn} Eventos em camelCase, função como referência
style="color:red" style={{ color: 'red' }} Style recebe um objeto JS, não uma string
<br> <br /> Tags sem filho precisam ser auto-fechadas
<!-- comment --> {/* comment */} Comentários usam sintaxe JS dentro de chaves

Expressões JavaScript dentro do JSX — as chaves {}

Tudo que estiver entre chaves {} dentro do JSX é avaliado como JavaScript. É assim que você mistura dados dinâmicos com a estrutura visual:

App.tsx
const nome = "Ana"
const ativo = true
const preco = 49.9

function App() {
  return (
    <div>
      {/* string */}
      <h1>Olá, {nome}!</h1>

      {/* expressão JS */}
      <p>2 + 2 = {2 + 2}</p>

      {/* método de string */}
      <p>{nome.toUpperCase()}</p>

      {/* atributo dinâmico */}
      <button disabled={!ativo}>Comprar</button>

      {/* formatação de número */}
      <p>R$ {preco.toFixed(2)}</p>

      {/* ternário para conteúdo condicional */}
      <p>{ativo ? "✅ Ativo" : "❌ Inativo"}</p>
    </div>
  )
}

Regra do elemento raiz único

Todo componente deve retornar um único elemento raiz. Se precisar retornar múltiplos elementos sem adicionar uma div extra ao DOM, use um Fragment:

Componente.tsx — Fragment
// ❌ Erro — dois elementos raiz
return (
  <h1>Título</h1>
  <p>Parágrafo</p>
)

// ✅ OK — wrapper div (mas adiciona nó ao DOM)
return (
  <div>
    <h1>Título</h1>
    <p>Parágrafo</p>
  </div>
)

// ✅ MELHOR — Fragment (não adiciona nó extra ao DOM)
return (
  <>
    <h1>Título</h1>
    <p>Parágrafo</p>
  </>
)

Componentes — As Peças da Interface

Um componente é uma função que retorna JSX. É a unidade fundamental do React. Tudo que aparece na tela é um componente — ou vários deles compostos. A grande vantagem é que você encapsula estrutura, comportamento e estilo num bloco reutilizável e independente.

🧱

Lego para interfaces: cada componente é uma peça. Um Button, um Card, um Header. Você encaixa as peças para montar a interface inteira. Se precisar mudar como o botão se parece, você muda num lugar só — e todos os botões do app atualizam automaticamente.

Criando um componente

Saudacao.tsx
// Componente: função com nome iniciado em MAIÚSCULA
function Saudacao() {
  return <h1>Olá, Dev Atlas! 👋</h1>
}

// Usando o componente como se fosse uma tag HTML
function App() {
  return (
    <div>
      <Saudacao />
      <Saudacao />  {/* reutilizável quantas vezes quiser */}
    </div>
  )
}

export default App
⚠️

Componente começa sempre com MAIÚSCULA. <saudacao /> (minúscula) → React trata como tag HTML desconhecida e ignora. <Saudacao /> (maiúscula) → React executa sua função. Essa é uma das regras mais importantes.

Por que componentes são vantajosos?

VantagemO que significa na prática
Reutilização Escreva um Button uma vez e use em 50 lugares
Isolamento Um bug no Card não afeta o Header
Legibilidade <ProductCard /> é mais claro que 30 linhas de divs aninhadas
Testabilidade Você testa cada peça separadamente, sem precisar do app todo
Colaboração Times diferentes trabalham em componentes diferentes sem conflito
Performance React re-renderiza apenas o componente cujo estado mudou

Estrutura recomendada de arquivos

Estrutura de pastas — projeto React
src/
  components/         ← componentes reutilizáveis
    Button.tsx
    Card.tsx
    Header.tsx
  pages/              ← componentes de página inteira
    Home.tsx
    About.tsx
  hooks/              ← hooks customizados
    useAuth.ts
  App.tsx             ← componente raiz
  main.tsx            ← ponto de entrada (ReactDOM.render)

Props — Passando Dados para Componentes

Props (propriedades) são a forma de passar dados de um componente pai para um componente filho. Funcionam como atributos HTML, mas podem receber qualquer valor JavaScript — string, número, função, objeto, array.

🔒

Props são somente leitura. Um componente filho nunca deve modificar as props que recebeu — elas pertencem ao pai. Se o filho precisar de um dado que mude, use state (próximo tópico) ou peça ao pai para passar uma função callback.

Button.tsx — Componente com props tipadas
// Definindo o tipo das props com TypeScript
type ButtonProps = {
  label:    string
  variant?: "primary" | "danger"   // ? = opcional
  onClick:  () => void
  disabled?: boolean
}

function Button({ label, variant = "primary", onClick, disabled }: ButtonProps) {
  return (
    <button
      className={`btn btn--${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {label}
    </button>
  )
}

// Usando o componente com diferentes props
function App() {
  return (
    <>
      <Button label="Salvar"     onClick={() => console.log("salvo")} />
      <Button label="Excluir"   variant="danger"  onClick={() => {}} />
      <Button label="Desabilitado" disabled onClick={() => {}} />
    </>
  )
}

A prop children — conteúdo aninhado

A prop especial children recebe tudo que for colocado entre as tags de abertura e fechamento do componente — texto, outros elementos, outros componentes:

Card.tsx — prop children
import { ReactNode } from "react"

type CardProps = {
  title:    string
  children: ReactNode   // qualquer coisa renderizável
}

function Card({ title, children }: CardProps) {
  return (
    <div className="card">
      <h2 className="card__title">{title}</h2>
      <div className="card__body">
        {children}
      </div>
    </div>
  )
}

// Usando — tudo entre <Card>...</Card> vira "children"
function App() {
  return (
    <Card title="Meu Produto">
      <p>Descrição do produto aqui.</p>
      <Button label="Comprar" onClick={() => {}} />
    </Card>
  )
}

State — A Memória do Componente

State (estado) é a memória interna de um componente — dados que ele precisa lembrar entre renderizações. Quando o state muda, o React re-renderiza automaticamente o componente com os dados atualizados. É aqui que a mágica acontece.

🧠

Props vs State: props vêm de fora (do pai) e são imutáveis. State vive dentro do componente e pode mudar. Se uma variável precisa mudar e o componente precisa re-renderizar, ela é state. Se vem de fora, é prop.

useState — o hook de estado básico

Contador.tsx
import { useState } from "react"

function Contador() {
  // useState retorna [valorAtual, funçãoParaAtualizar]
  // o argumento é o valor inicial
  const [count, setCount] = useState(0)

  return (
    <div>
      <p>Contagem: {count}</p>
      <button onClick={() => setCount(count + 1)}>+</button>
      <button onClick={() => setCount(count - 1)}>-</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  )
}

State com objetos e strings — exemplos reais

Formulario.tsx
import { useState } from "react"

function Formulario() {
  const [nome,  setNome]  = useState("")
  const [email, setEmail] = useState("")
  const [enviado, setEnviado] = useState(false)

  function handleSubmit(e: React.FormEvent) {
    e.preventDefault()
    console.log({ nome, email })
    setEnviado(true)
  }

  if (enviado) {
    return <p>✅ Obrigado, {nome}! Formulário enviado.</p>
  }

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Nome"
        value={nome}
        onChange={(e) => setNome(e.target.value)}
      />
      <input
        type="email"
        placeholder="Email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
      />
      <button type="submit">Enviar</button>
    </form>
  )
}
⚠️

Nunca mute o state diretamente. count++ não funciona — o React não detecta a mudança e não re-renderiza. Sempre use a função setter: setCount(count + 1). Para arrays e objetos, sempre crie uma nova cópia: setLista([...lista, novoItem]).

Eventos — Reagindo às Ações do Usuário

No React, eventos são tratados com props em camelCase diretamente nos elementos JSX. O React normaliza os eventos do browser para que funcionem igual em todos os navegadores.

Eventos.tsx
import { useState } from "react"

function ExemplosEventos() {
  const [texto, setTexto] = useState("")
  const [hover, setHover] = useState(false)

  // Handler separado (recomendado para lógica não trivial)
  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setTexto(e.target.value)
  }

  function handleKeyDown(e: React.KeyboardEvent) {
    if (e.key === "Enter") alert("Enter pressionado!")
  }

  return (
    <div>
      {/* Click */}
      <button onClick={() => alert("clicou!")}>Clique</button>

      {/* Hover */}
      <div
        onMouseEnter={() => setHover(true)}
        onMouseLeave={() => setHover(false)}
        style={{ background: hover ? "#6c63ff" : "#1a1d27" }}
      >
        {hover ? "Mouse em cima!" : "Passe o mouse aqui"}
      </div>

      {/* Input controlado */}
      <input
        value={texto}
        onChange={handleChange}
        onKeyDown={handleKeyDown}
        placeholder="Digite algo..."
      />
      <p>Você digitou: {texto}</p>
    </div>
  )
}
Evento ReactEquivalente DOMQuando dispara
onClickonclickClique do mouse ou toque
onChangeoninputQualquer alteração no input
onSubmitonsubmitEnvio de formulário
onKeyDownonkeydownTecla pressionada
onMouseEnteronmouseenterMouse entra no elemento
onFocusonfocusElemento recebe foco
onBluronblurElemento perde foco

Renderização Condicional

Mostrar ou esconder partes da interface com base em condições é uma das coisas mais comuns em React. Como você está dentro de JavaScript, todas as ferramentas que já conhece funcionam: if, ternário e o operador &&.

Condicional.tsx
function Painel({ logado, carregando, erro }: {
  logado:     boolean
  carregando: boolean
  erro?:       string
}) {
  // 1. if antes do return — para casos que mudam todo o layout
  if (carregando) return <p>⏳ Carregando...</p>
  if (erro)       return <p style={{color:"red"}}>❌ {erro}</p>

  return (
    <div>
      {/* 2. Ternário — quando há dois casos distintos */}
      {logado
        ? <p>👋 Bem-vindo de volta!</p>
        : <button>Fazer Login</button>
      }

      {/* 3. && — quando há só um caso (mostrar ou não) */}
      {logado && <button>Painel Admin</button>}

      {/* Cuidado: && com número! 0 renderiza "0" na tela */}
      {logado && minhasNotas.length > 0 && <Lista />}
    </div>
  )
}

Listas e a Prop key

Para renderizar listas no React, usamos o método .map() do JavaScript. Cada item da lista deve ter uma prop key única — é o identificador que o React usa para saber qual item mudou, foi adicionado ou removido.

Lista.tsx
type Produto = {
  id:    number
  nome:  string
  preco: number
}

const produtos: Produto[] = [
  { id: 1, nome: "Teclado", preco: 350 },
  { id: 2, nome: "Mouse",   preco: 180 },
  { id: 3, nome: "Monitor", preco: 1200 },
]

function ListaProdutos() {
  return (
    <ul>
      {produtos.map((p) => (
        // key é obrigatória — use o id único do item, NUNCA o índice
        <li key={p.id}>
          {p.nome} — R$ {p.preco.toFixed(2)}
        </li>
      ))}
    </ul>
  )
}

// Adicionando e removendo da lista com state
function ListaDinamica() {
  const [itens, setItens] = useState(["Café", "Leite", "Pão"])
  const [novoItem, setNovoItem] = useState("")

  function adicionar() {
    if (!novoItem.trim()) return
    setItens([...itens, novoItem])  // spread cria nova array
    setNovoItem("")
  }

  function remover(index: number) {
    setItens(itens.filter((_, i) => i !== index))
  }

  return (
    <div>
      <input value={novoItem} onChange={(e) => setNovoItem(e.target.value)} />
      <button onClick={adicionar}>Adicionar</button>
      <ul>
        {itens.map((item, i) => (
          <li key={item + i}>
            {item} <button onClick={() => remover(i)}>×</button>
          </li>
        ))}
      </ul>
    </div>
  )
}
⚠️

Nunca use o índice do array como key quando os itens podem ser reordenados ou removidos. Se você remover o item de índice 0, todos os outros mudam de índice — o React fica confuso e pode exibir dados errados. Sempre use um identificador único e estável, como o id do banco de dados.

useEffect — Efeitos Colaterais

useEffect serve para executar código que precisa acontecer fora do fluxo de renderização: buscar dados de uma API, modificar o título da página, configurar timers, adicionar listeners. Esses são os chamados efeitos colaterais — coisas que afetam o mundo além do JSX retornado.

useEffect.tsx — as três formas
import { useState, useEffect } from "react"

function ExemploEffect() {
  const [dados, setDados] = useState(null)
  const [id, setId]     = useState(1)

  // 1. Sem array de dependências → roda em TODA renderização
  useEffect(() => {
    console.log("Componente renderizou")
  })

  // 2. Array vazio → roda UMA VEZ, quando o componente monta
  useEffect(() => {
    console.log("Componente montou!")
  }, [])

  // 3. Com dependência [id] → roda quando "id" muda
  useEffect(() => {
    // Busca dados sempre que o id muda
    fetch(`https://jsonplaceholder.typicode.com/users/${id}`)
      .then(r => r.json())
      .then(setDados)
  }, [id])  // ← lista de dependências

  return (
    <div>
      <button onClick={() => setId(id + 1)}>Próximo usuário</button>
      <pre>{JSON.stringify(dados, null, 2)}</pre>
    </div>
  )
}

Cleanup — limpando o que o efeito criou

Timer.tsx — função de cleanup
function Relogio() {
  const [hora, setHora] = useState(new Date())

  useEffect(() => {
    // Inicia o intervalo quando o componente monta
    const timer = setInterval(() => {
      setHora(new Date())
    }, 1000)

    // Função de cleanup: roda quando o componente desmonta
    // Sem isso, o timer continuaria rodando para sempre — memory leak!
    return () => clearInterval(timer)
  }, [])  // ← [] = só monta/desmonta uma vez

  return <p>🕐 {hora.toLocaleTimeString("pt-BR")}</p>
}

Outros Hooks Essenciais

Hooks são funções especiais do React que começam com use. Eles permitem que componentes funcionais usem recursos como estado, efeitos, contexto e muito mais.

useState
Quando: guardar valor que muda
O hook mais usado. Armazena state local e força re-render ao ser atualizado.
useEffect
Quando: efeitos colaterais
Fetch de dados, timers, event listeners, integração com bibliotecas externas.
useRef
Quando: referenciar DOM ou valor persistente
Acessa elementos DOM diretamente ou guarda valor que não deve causar re-render.
useContext
Quando: dados globais
Acessa um contexto criado com createContext. Evita prop drilling em árvores profundas.
useMemo
Quando: cálculo custoso
Memoriza o resultado de um cálculo. Só recalcula quando as dependências mudam.
useCallback
Quando: função passada como prop
Memoriza a referência de uma função para evitar re-renders desnecessários em filhos.
useReducer
Quando: lógica de state complexa
Alternativa ao useState para estados com múltiplas sub-variáveis ou transições complexas.
useId
Quando: IDs acessíveis
Gera um ID único e estável para associar labels a inputs sem colisão entre componentes.

useRef — acessando o DOM diretamente

FocoAutomatico.tsx
import { useRef, useEffect } from "react"

function FocoAutomatico() {
  // useRef cria uma referência mutável que não causa re-render
  const inputRef = useRef<HTMLInputElement>(null)

  useEffect(() => {
    // Foca o input quando o componente monta
    inputRef.current?.focus()
  }, [])

  return (
    <input
      ref={inputRef}
      placeholder="Foco automático ao abrir a página"
    />
  )
}

Hooks customizados — criando os seus

Você pode criar seus próprios hooks para encapsular e reutilizar lógica stateful. A única regra: o nome deve começar com use.

hooks/useLocalStorage.ts
import { useState } from "react"

// Hook que sincroniza state com o localStorage automaticamente
function useLocalStorage<T>(chave: string, valorInicial: T) {
  const [valor, setValor] = useState<T>(() => {
    const salvo = localStorage.getItem(chave)
    return salvo ? JSON.parse(salvo) : valorInicial
  })

  function salvar(novoValor: T) {
    setValor(novoValor)
    localStorage.setItem(chave, JSON.stringify(novoValor))
  }

  return [valor, salvar] as const
}

// Usando o hook customizado em qualquer componente
function Preferencias() {
  const [tema, setTema] = useLocalStorage("tema", "dark")

  return (
    <button onClick={() => setTema(tema === "dark" ? "light" : "dark")}>
      Tema atual: {tema}
    </button>
  )
}

Composição — Montando Interfaces Complexas

React favorece composição em vez de herança. Em vez de criar componentes cada vez mais genéricos por herança (como se faz em POO), você compõe componentes pequenos em componentes maiores. É o modelo mental mais poderoso do React.

Exemplo de composição real — ProdutoCard.tsx
// Peças pequenas
function Preco({ valor }: { valor: number }) {
  return <span className="preco">R$ {valor.toFixed(2)}</span>
}

function Badge({ texto }: { texto: string }) {
  return <span className="badge">{texto}</span>
}

function BotaoComprar({ onClick }: { onClick: () => void }) {
  return <button className="btn-comprar" onClick={onClick}>Comprar</button>
}

// Composição: montando o card a partir das peças
type Produto = { nome: string; preco: number; novo: boolean }

function ProdutoCard({ produto, onComprar }: { produto: Produto; onComprar: () => void }) {
  return (
    <div className="produto-card">
      {produto.novo && <Badge texto="Novo" />}
      <h3>{produto.nome}</h3>
      <Preco valor={produto.preco} />
      <BotaoComprar onClick={onComprar} />
    </div>
  )
}

// Usando a composição completa
function App() {
  const produto = { nome: "Teclado Mecânico", preco: 350, novo: true }
  return <ProdutoCard produto={produto} onComprar={() => alert("Adicionado!")} />
}
💡

Levantamento de estado (lifting state up): se dois componentes irmãos precisam do mesmo estado, suba o state para o ancestral comum mais próximo e passe os dados via props. Nunca crie estado duplicado em dois lugares para representar a mesma coisa.

Setup com Vite — Criando um Projeto React + TypeScript

O Vite é o bundler padrão para projetos React modernos. É extremamente rápido, tem hot reload instantâneo e setup zero. Em segundos você tem um projeto React com TypeScript rodando.

TERMINAL — Criando o projeto
# Criar projeto React com TypeScript
npm create vite@latest meu-app -- --template react-ts

# Entrar na pasta e instalar as dependências
cd meu-app
npm install

# Iniciar o servidor de desenvolvimento
npm run dev
# → Local: http://localhost:5173

Estrutura gerada pelo Vite

Estrutura inicial do projeto
meu-app/
  public/              ← arquivos estáticos (ícones, imagens)
  src/
    App.tsx            ← componente raiz — edite aqui!
    App.css            ← estilos do App
    main.tsx           ← ponto de entrada (renderiza o App no DOM)
    index.css          ← estilos globais
    vite-env.d.ts      ← tipos do Vite para TypeScript
  index.html           ← HTML base — o React injeta tudo aqui
  package.json
  tsconfig.json        ← configurações do TypeScript
  vite.config.ts       ← configurações do Vite

O main.tsx — como o React monta na página

src/main.tsx
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import "./index.css"
import App from "./App.tsx"

// Pega o elemento #root do index.html e injeta o App React dentro dele
createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>
)

// StrictMode: ativa avisos extras em desenvolvimento.
// Ele renderiza os componentes duas vezes para detectar problemas.
// Não tem efeito em produção.

Comandos úteis do projeto

ComandoO que faz
npm run devInicia o servidor de desenvolvimento com hot reload
npm run buildGera a versão otimizada para produção na pasta dist/
npm run previewServe o build de produção localmente para inspecionar
npm run lintVerifica erros de código com ESLint
💡

Extensões VS Code para React: ES7+ React/Redux Snippets (atalho rafce cria um componente funcional inteiro em segundos) e TypeScript React code snippets. Com o snippet rafce você nunca mais vai digitar o boilerplate de um componente na mão.

📋 Resumo — React

  • React é uma biblioteca declarativa — você descreve o que mostrar, não como atualizar o DOM
  • TSX é a sintaxe recomendada: JSX com TypeScript. Use .tsx em todos os componentes
  • Componentes são funções que retornam JSX. Nome sempre com letra maiúscula
  • Props passam dados do pai para o filho — são somente leitura
  • State (useState) é a memória interna do componente. Mude sempre via setter, nunca diretamente
  • Eventos em camelCase: onClick, onChange, onSubmit
  • Renderização condicional: if antes do return, ternário e && dentro do JSX
  • Listas com .map() e key única — nunca use o índice como key se a lista pode mudar
  • useEffect para efeitos colaterais: fetch, timers, listeners. Sempre limpe com cleanup
  • Hooks começam com use. Você pode criar os seus para reutilizar lógica stateful
  • Prefira composição — componentes pequenos compostos em maiores — a componentes gigantes
  • Setup com npm create vite@latest meu-app -- --template react-ts