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?
React vs JavaScript puro — o problema que ele resolve
❌ JavaScript Puro — imperativo
// "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
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
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
| HTML | JSX / TSX | Por 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:
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:
// ❌ 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
// 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?
| Vantagem | O 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
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.
// 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:
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
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
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.
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 React | Equivalente DOM | Quando dispara |
|---|---|---|
onClick | onclick | Clique do mouse ou toque |
onChange | oninput | Qualquer alteração no input |
onSubmit | onsubmit | Envio de formulário |
onKeyDown | onkeydown | Tecla pressionada |
onMouseEnter | onmouseenter | Mouse entra no elemento |
onFocus | onfocus | Elemento recebe foco |
onBlur | onblur | Elemento 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 &&.
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.
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.
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
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.
useRef — acessando o DOM diretamente
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.
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.
// 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.
# 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
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
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
| Comando | O que faz |
|---|---|
npm run dev | Inicia o servidor de desenvolvimento com hot reload |
npm run build | Gera a versão otimizada para produção na pasta dist/ |
npm run preview | Serve o build de produção localmente para inspecionar |
npm run lint | Verifica 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
.tsxem 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:
ifantes do return, ternário e&&dentro do JSX - Listas com
.map()ekeyúnica — nunca use o índice como key se a lista pode mudar useEffectpara 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