0 Fundamentos essenciais da etapa anterior useState · componente · map · destructuring · spread · imports clique para abrir
Por que este módulo existe? Os exemplos dos módulos 7–16 usam esses conceitos sem explicá-los. Se algo no código parecer estranho, consulte aqui primeiro.
Componente React — estrutura básica
// Um componente é uma função que retorna JSX (HTML + JS misturados) // Deve começar com letra maiúscula function MeuComponente() { return ( <div> <h1>Olá mundo</h1> </div> ) } export default MeuComponente // exporta para usar em outro arquivo // Importando e usando: import MeuComponente from './MeuComponente' <MeuComponente />
useState — estado local
O que é estado?
Variáveis comuns (
let x = 0) não atualizam a tela quando mudam. useState é especial: quando o estado muda, o React re-renderiza o componente automaticamente com o novo valor.
import { useState } from 'react' function Contador() { // useState(valorInicial) retorna [valorAtual, funçãoParaAtualizar] const [contador, setContador] = useState(0) const [nome, setNome] = useState("") const [lista, setLista] = useState([]) const [user, setUser] = useState({}) // Para atualizar: SEMPRE usar o setter, nunca modificar direto return <button onClick={() => setContador(contador + 1)}>{contador}</button> }
Props — passando dados entre componentes
// Pai passa dados via atributos (props) <CartaoUsuario nome="João" idade={21} /> // Filho recebe como parâmetro function CartaoUsuario(props) { return <p>{props.nome} — {props.idade} anos</p> } // Ou com destructuring direto (mais comum): function CartaoUsuario({ nome, idade }) { return <p>{nome} — {idade} anos</p> }
map() — renderizando listas
A prop
key é obrigatória em listas. O React usa ela para identificar cada item e saber qual atualizar. Use sempre um ID único — nunca o índice do array se a lista pode mudar de ordem.
const produtos = [ { id: 1, nome: 'Mouse' }, { id: 2, nome: 'Teclado' }, ] // map transforma cada item do array em JSX // key={item.id} é obrigatório — usa o ID único do banco <ul> {produtos.map((item) => ( <li key={item.id}>{item.nome}</li> ))} </ul>
Renderização condicional
// && → renderiza só se a condição for verdadeira {isLoading && <p>Carregando...</p>} {erro && <p>Deu erro</p>} // Ternário → um ou outro {logado ? <Dashboard /> : <Login />}
Destructuring — extraindo valores de objetos
// Sem destructuring (verboso) const nome = usuario.nome const email = usuario.email // Com destructuring (compacto) — mesmo resultado const { nome, email } = usuario // Muito usado em eventos de formulário: const { name, value } = event.target // pega name e value do input // E em arrays (useState, hooks customizados): const [dados, setDados] = useState([])
Spread operator — copiando objetos
// ... (três pontos) copia todas as propriedades de um objeto const original = { nome: 'João', email: 'j@j.com' } // Copia tudo e substitui só o email const atualizado = { ...original, email: 'novo@j.com' } // → { nome: 'João', email: 'novo@j.com' } // É exatamente o que o onChange do formulário faz: // copia o form inteiro e atualiza só o campo que mudou setForm({ ...form, [name]: value })
Import / Export
// Export padrão — um por arquivo, importado com qualquer nome export default MinhaFuncao import MinhaFuncao from './MinhaFuncao' // Export nomeado — vários por arquivo, importado com chaves {} export function soma() { ... } export const PI = 3.14 import { soma, PI } from './utils' // Biblioteca externa (node_modules) import axios from 'axios' import { useState, useEffect } from 'react'
Arrow function — funções curtas
// Função normal function somar(a, b) { return a + b } // Arrow function equivalente const somar = (a, b) => a + b // Com corpo (mais de uma linha) const buscar = async () => { const res = await axios.get('...') setDados(res.data) } // Inline em JSX — muito comum <button onClick={() => setAberto(true)}>Abrir</button>
7 HTTP/HTTPS e Postman métodos · status codes · headers · JSON clique para abrir
⬇ PDF do módulo
Por que estudar isso? React só exibe dados — ele precisa buscá-los de um servidor via HTTP. Entender o protocolo é entender como front-end e back-end se comunicam.
Métodos HTTP — CRUD
| Método | Ação | Tem Body? | Exemplo de uso |
|---|---|---|---|
| GET | Buscar / listar | Não | GET /produtos |
| POST | Criar novo | Sim | POST /usuarios |
| PUT | Atualizar inteiro | Sim | PUT /usuarios/1 |
| PATCH | Atualizar parcial | Sim | PATCH /usuarios/1 |
| DELETE | Deletar | Não | DELETE /usuarios/1 |
Status Codes — o que o servidor responde
| Código | Significado |
|---|---|
200 | OK — sucesso (GET, PUT) |
201 | Created — recurso criado (POST) |
204 | No Content — sucesso sem corpo de resposta (DELETE) |
400 | Bad Request — dados inválidos ou faltando |
401 | Unauthorized — não autenticado (sem token ou token inválido) |
403 | Forbidden — autenticado, mas sem permissão para este recurso |
404 | Not Found — recurso não existe |
500 | Internal Server Error — erro no servidor (bug no back-end) |
Anatomia de uma requisição
- URL / Endpoint: endereço do recurso — ex:
https://api.com/usuarios/1 - Headers: metadados enviados junto —
Authorization,Content-Type: application/json - Body: dados enviados ao servidor (só POST / PUT / PATCH) — sempre em JSON
- JSON: formato de dados padrão —
{ "nome": "João", "idade": 21 }
8 Integração de APIs com Axios GET · POST · PUT · DELETE · headers · Promises clique para abrir
⬇ PDF do módulo
Por que Axios e não o fetch nativo? Axios processa o JSON automaticamente (sem precisar de
.json()), trata erros HTTP como exceções e tem menos código repetitivo.
Instalação
npm install axios
Antes de tudo: o que é uma operação assíncrona?
O problema do JavaScript síncrono com APIs
Quando o JavaScript faz uma requisição HTTP, isso leva tempo. Se o código esperasse a resposta parado, o browser travaria — sem scroll, sem clique, sem nada. Por isso as chamadas de API são assíncronas: o código continua rodando; quando a resposta chegar, uma função é chamada com o resultado.
O que é uma Promise?
Um objeto que representa um valor que ainda não chegou. Todo método do Axios retorna uma Promise. Três estados: pending (aguardando), fulfilled (sucesso) e rejected (erro). Para "pegar" o resultado, existem dois estilos — ambos fazem a mesma coisa:
Estilo 1 — .then() / .catch() (encadeado)
Você passa funções de callback.
Estilo 2 — async / await (linear) A palavra
.then(fn) é chamado quando der sucesso. .catch(fn) quando ocorrer erro.Estilo 2 — async / await (linear) A palavra
async marca a função. O await pausa até a Promise resolver, sem travar o browser. Erros capturados com try / catch — como em Java/C#.
GET — Buscar dados (os dois estilos)
import axios from 'axios' // ── Estilo 1: .then() / .catch() ────────────────────────────────── // .then → executado quando a resposta chega com sucesso // .catch → executado quando ocorre qualquer erro axios.get('https://api.com/produtos') .then((response) => { console.log(response.data) // os dados ficam em response.data }) .catch((error) => { console.log(error.response.data) }) // ── Estilo 2: async / await ─────────────────────────────────────── // "async" marca a função como assíncrona (obrigatório para usar await) // "await" pausa aqui até a Promise resolver — sem travar o browser // "try/catch" captura erros const buscar = async () => { try { const response = await axios.get('https://api.com/produtos') console.log(response.data) } catch (error) { console.log(error) } }
Qual usar? Ambos são válidos.
.then/.catch é conciso para uma única chamada. async/await é mais claro com múltiplos passos dependentes (ex: fazer login e depois buscar perfil com o token).
POST — Criar (body como 2º argumento)
axios.post('https://api.com/usuarios', { nome: 'João', email: 'joao@email.com', senha: '123456' }).then((res) => console.log(res.data))
PUT — Atualizar (id na URL, dados no body)
axios.put('https://api.com/usuarios/1', { nome: 'João Atualizado', email: 'novo@email.com' }).then((res) => console.log(res.data))
DELETE — Deletar (id na URL, sem body)
axios.delete('https://api.com/usuarios/1') .then(() => console.log('Deletado com sucesso'))
Enviando Headers — ex: token JWT
const token = localStorage.getItem('token') // GET com header de autenticação axios.get('/api/rota-privada', { headers: { Authorization: `Bearer ${token}` } }) // POST com body + header (body é 2º arg, config é 3º) axios.post('/api/posts', { titulo: 'Meu post' }, { headers: { Authorization: `Bearer ${token}` } })
Assinatura de cada método:
config =
axios.get(url, config) |
axios.post(url, body, config) |
axios.put(url, body, config) |
axios.delete(url, config)config =
{ headers, params, ... } · dados da resposta sempre em response.data
Axios dentro de um componente React
import { useState, useEffect } from 'react' import axios from 'axios' export function TelaProdutos() { const [produtos, setProdutos] = useState([]) useEffect(() => { axios.get('/api/produtos') .then((res) => setProdutos(res.data)) }, []) // [] = executa uma única vez ao montar return ( <ul> {produtos.map((p) => <li key={p.id}>{p.nome}</li>)} </ul> ) }
9 async/await, try/catch e LocalStorage assíncrono · erros · persistência no browser clique para abrir
⬇ PDF do módulo
Quando async/await é melhor que .then? Quando há múltiplos passos dependentes: fazer login e, com o token retornado, buscar o perfil. Com
.then encadeado vira "callback hell". Com async/await parece código linear.
async/await dentro do useEffect — ATENÇÃO
useEffect não pode ser marcado como async diretamente. Se fosse async, retornaria uma Promise — mas o useEffect espera uma função de cleanup ou undefined. A solução é criar uma função async interna e chamá-la.
// ❌ ERRADO useEffect(async () => { ... }, []) // ✅ CORRETO — função async interna useEffect(() => { const buscar = async () => { try { const res = await axios.get('/api/dados') setDados(res.data) } catch (err) { console.log('Erro:', err) } } buscar() // define e já chama }, [])
useEffect — array de dependências
O que é o array de dependências?
O segundo argumento diz ao React: "só rode isso de novo se alguma dessas variáveis mudar". Se a variável mudou entre uma renderização e outra, o efeito roda. Se não mudou, React pula.
// Sem array — roda TODA vez que o componente renderizar // ❌ Quase sempre causa loop infinito: // setDados() → re-renderiza → useEffect roda → setDados() → loop useEffect(() => { axios.get('...').then((r) => setDados(r.data)) }) // Array vazio [] — roda UMA VEZ quando o componente aparece na tela // ✅ Ideal para busca inicial: "ao entrar na tela, carrega os produtos" useEffect(() => { axios.get('/api/produtos').then(...) }, []) // [id] — roda na montagem E toda vez que "id" mudar // ✅ Ideal quando a tela depende de algo da URL ou de um estado: // ex: usuário clica no produto 1, depois no produto 2 → id muda → re-busca useEffect(() => { axios.get(`/api/produtos/${id}`).then(...) }, [id]) // [a, b] — roda quando "a" OU "b" mudar // ✅ Ideal para filtros combinados: re-busca quando qualquer filtro muda useEffect(() => { axios.get(`/api/produtos?cat=${cat}&ordem=${ordem}`).then(...) }, [cat, ordem])
LocalStorage — persistência no browser
Quando usar: para dados que precisam sobreviver a recarregamentos de página. O estado React some ao fechar/atualizar a aba; o LocalStorage persiste. Mais usado para token de autenticação e preferências do usuário.
// Salvar localStorage.setItem('chave', 'valor') // Ler const valor = localStorage.getItem('chave') // → "valor" (sempre string) // Remover localStorage.removeItem('chave') // Objetos: converter para string e voltar localStorage.setItem('user', JSON.stringify({ id: 1, nome: 'João' })) const user = JSON.parse(localStorage.getItem('user'))
10 Custom Hooks useRequestData · loading/erro · reutilização de lógica clique para abrir
⬇ PDF do módulo
Por que existem? Lógica que usa hooks (useState, useEffect…) não pode ser extraída para uma função comum — o React proibiria. Custom Hooks são funções cujo nome começa com
use e podem chamar outros hooks. Use-os para reutilizar lógica entre componentes sem copiar e colar código.
Regras obrigatórias
- Nome começa com
use— obrigatório (ex:useForm,useRequestData) - Mesmas regras dos hooks nativos: não chamar dentro de
if, loops ou funções aninhadas - Crie quando: mesma lógica em 2+ componentes, ou componente fica grande demais
useRequestData(url) — hook genérico para GET
// hooks/useRequestData.js // Encapsula: chamada axios + estado de loading + estado de erro import { useState, useEffect } from "react" import axios from "axios" export const useRequestData = (url) => { const [data, setData] = useState(undefined) // undefined = ainda não chegou const [isLoading, setIsLoading] = useState(false) const [error, setError] = useState("") useEffect(() => { setIsLoading(true) axios.get(url) .then((res) => { setIsLoading(false); setData(res.data) }) .catch((err) => { setIsLoading(false); setError(err) }) }, [url]) return [data, isLoading, error] } // Usando em qualquer componente: const [produtos, isLoading, error] = useRequestData("/api/produtos") const [usuario, isLoading, error] = useRequestData("/api/usuario")
Tratando as 4 situações na tela
return ( <div> {isLoading && <p>Carregando...</p>} {!isLoading && error && <p>Ocorreu um erro.</p>} {!isLoading && data && data.length > 0 && ( <ul>{data.map((item) => <li key={item.id}>{item.nome}</li>)}</ul> )} {!isLoading && data && data.length === 0 && <p>Nenhum item encontrado.</p>} </div> )
11 Navegação com React Router Link · NavLink · useNavigate · useParams · useSearchParams clique para abrir
⬇ PDF do módulo
Por que React Router? React cria SPAs — o browser carrega o HTML uma única vez e o JavaScript troca o conteúdo sem recarregar. A URL não muda sozinha. Sem um router, o usuário não pode compartilhar links, usar o botão voltar ou fazer bookmark. O React Router sincroniza URL ↔ componente exibido, sem reload.
Instalação
npm install react-router-dom
Componentes e hooks — visão geral
| Componente / Hook | O que faz |
|---|---|
<BrowserRouter> | Habilita o roteamento — sempre o mais externo |
<Routes> | Agrupa as rotas e renderiza apenas a primeira que casar com a URL |
<Route path element> | Define qual componente renderizar em qual caminho |
<Link to> | Substitui <a href> — navega sem recarregar |
<NavLink to> | Igual ao Link, mas adiciona classe active quando a rota está ativa |
useNavigate() | Retorna função para navegar por código (após submit, login, etc.) |
useParams() | Lê parâmetros dinâmicos da URL: /produtos/:id |
useSearchParams() | Lê query string: /produtos?busca=tv&pagina=2 |
Estrutura base — App.jsx
import { BrowserRouter } from "react-router-dom" function App() { return ( <BrowserRouter> <Menu /> {/* fora do Routes → aparece em todas as páginas */} <AppRoutes /> </BrowserRouter> ) }
routes/AppRoutes.jsx
import { Routes, Route } from "react-router-dom" function AppRoutes() { return ( <Routes> <Route path="/" element={<HomePage />} /> <Route path="/sobre" element={<SobrePage />} /> <Route path="/login" element={<LoginPage />} /> <Route path="/produtos" element={<ProdutosPage />} /> <Route path="/produtos/:id" element={<ProdutoDetalhesPage />} /> <Route path="*" element={<NotFoundPage />} /> </Routes> ) }
Por que
path="*" é o 404? O * casa com qualquer URL que não casou antes. <Routes> para na primeira rota que bater — por isso * deve ser sempre a última.
Link vs NavLink — quando usar cada um
import { Link, NavLink } from "react-router-dom" // Link → qualquer lugar fora de menu (cards, botões, listas) <Link to="/sobre">Sobre</Link> // NavLink → menus de navegação — adiciona classe "active" na rota atual <NavLink to="/sobre" className={({ isActive }) => isActive ? 'menu-ativo' : ''} >Sobre</NavLink> <NavLink to="/sobre" style={({ isActive }) => ({ color: isActive ? '#61dafb' : 'white', fontWeight: isActive ? 'bold' : 'normal' })} >Sobre</NavLink>
useNavigate — navegação por código
Quando usar em vez de Link? Quando navegar é consequência de uma ação: login bem-sucedido, formulário salvo, item deletado. Com
Link o usuário decide; com useNavigate o código decide — e só navega se tudo deu certo.
const navigate = useNavigate() navigate("/rota") // vai para /rota navigate(-1) // volta (como botão ←) navigate("/rota", { replace: true }) // vai sem adicionar ao histórico
| Chamada | Caso de uso |
|---|---|
navigate("/rota") | Após login, após salvar formulário |
navigate(-1) | Botão "Voltar" na tela |
navigate("/", { replace: true }) | Após login — impede voltar para /login |
Path Params — useParams()
Por que usar? Sem parâmetros, seria necessário criar uma rota por produto:
/produto1, /produto2... Com /:id, uma única rota serve para todos. A URL identifica o recurso — funciona como bookmark e link compartilhável.
// 1. Declarar a rota — dois pontos marcam parâmetro dinâmico <Route path="/produtos/:id" element={<ProdutoPage />} /> // 2. Ler no componente const { id } = useParams() // /produtos/42 → id = "42" (sempre string) useEffect(() => { axios.get(`/api/produtos/${id}`).then((res) => setProduto(res.data)) }, [id]) // re-busca quando o id muda // 3. Navegar para a rota <Link to={`/produtos/${produto.id}`}>Ver detalhes</Link>
Search Params — useSearchParams()
Por que não usar estado React para filtros? Estado some ao recarregar a página (F5) ou copiar o link. Search params ficam na URL —
/produtos?busca=mouse pode ser compartilhada e sempre mostrará o mesmo resultado. Use para busca, filtros, ordenação, paginação.
// URL: /produtos?busca=mouse&pagina=2 const [searchParams] = useSearchParams() const busca = searchParams.get("busca") // → "mouse" | null const pagina = searchParams.get("pagina") // → "2"
Path Params /produtos/42 | Search Params /produtos?busca=tv | |
|---|---|---|
| Identifica | Recurso único e específico | Filtro, configuração ou estado |
| Hook | useParams() | useSearchParams() |
| Declarar na rota | Sim: path="/produtos/:id" | Não — lido direto da URL |
| Exemplos | Detalhe de produto, perfil de usuário | Busca, filtros, ordenação, paginação |
Organização de arquivos recomendada
src/ ├── components/ → Menu, Button, Card (reutilizáveis) ├── pages/ → HomePage, LoginPage, ProdutosPage... ├── routes/ → AppRoutes.jsx, coordinator.js ├── hooks/ → useRequestData.js, useForm.js ├── contexts/ → GlobalState.js, GlobalStateContext.js ├── App.jsx └── main.jsx
Coordinator — centralizando as strings de rota
// routes/coordinator.js export function goToHome(navigate) { navigate("/") } export function goToLogin(navigate) { navigate("/login") } export function goToProductDetails(navigate, id) { navigate(`/produtos/${id}`) } export function goBack(navigate) { navigate(-1) }
Deploy — problema do refresh (F5) em SPA: O servidor procura um arquivo físico chamado /produtos — que não existe. Retorna 404. A solução é configurar o servidor para redirecionar tudo para
index.html; o React Router assume o controle a partir daí.
12 Autenticação em Aplicações React JWT · Cookies vs LocalStorage · login · rota protegida · logout clique para abrir
⬇ PDF do módulo
Fluxo geral: usuário faz login → back-end valida → retorna token → front salva o token → envia em cada requisição protegida → back-end valida e libera.
JWT — JSON Web Token
- String com 3 partes separadas por ponto:
Header.Payload.Assinatura - O Payload contém: id, email, role (permissão), data de expiração
- O back-end assina com chave secreta — se alguém alterar o token, a assinatura fica inválida
- Não precisa de sessão no servidor — o token carrega tudo
Cookies vs LocalStorage
| Cookies | LocalStorage | |
|---|---|---|
| Quem define | Back-end (header Set-Cookie) | Front-end (JavaScript) |
| Envio automático | Sim — browser envia em toda requisição do mesmo domínio | Não — adicionado manualmente nos headers |
| Como usar | Automático | Authorization: Bearer TOKEN |
Fluxo completo com LocalStorage
// 1. LOGIN — receber e salvar o token const handleLogin = async () => { try { const res = await axios.post('/api/login', { email, senha }) localStorage.setItem('token', res.data.token) navigate('/', { replace: true }) // replace → sem volta para /login } catch (err) { alert('Credenciais inválidas') } } // 2. REQUISIÇÃO PROTEGIDA — enviar token no header const token = localStorage.getItem('token') axios.get('/api/meus-dados', { headers: { Authorization: `Bearer ${token}` } }) // 3. ROTA PROTEGIDA — redirecionar se não tiver token useEffect(() => { if (!localStorage.getItem('token')) navigate('/login') }, []) // 4. LOGOUT — apagar e redirecionar const logout = () => { localStorage.removeItem('token') navigate('/login') }
13 React + Forms onSubmit · onChange unificado · validações · REGEX · useForm clique para abrir
⬇ PDF do módulo
REGEX — padrões para o atributo
Por que usar a tag
<form>? Ela centraliza o submit em um evento só, permite que Enter dispare o submit, e habilita validações nativas do browser com required, type="email" e pattern.
Formulário completo com handler unificado
function Login() { // Um objeto para todos os campos — não um useState por campo const [form, setForm] = useState({ email: "", password: "" }) // Um único handler para todos os inputs // event.target.name = qual campo | event.target.value = o que foi digitado const onChange = (event) => { const { name, value } = event.target setForm({ ...form, [name]: value }) // [name] = chave dinâmica } const handleSubmit = (event) => { event.preventDefault() // cancela o reload padrão do HTML — SEMPRE necessário axios.post('/api/login', form).then(...) } return ( <form onSubmit={handleSubmit}> {/* onSubmit na <form>, não onClick no botão */} <input name="email" value={form.email} onChange={onChange} type="email" required /> <input name="password" value={form.password} onChange={onChange} type="password" required /> <button type="submit">Entrar</button> </form> ) }
event.preventDefault(): o comportamento padrão do browser ao submeter um form HTML é recarregar a página inteira — um resquício do HTML clássico. Em SPA isso destruiria todo o estado.
preventDefault() cancela esse comportamento.
Chave dinâmica
{ [name]: value }: os colchetes fazem o JavaScript usar o valor da variável name como chave. Se name = "email", gera { email: value }. É isso que permite um único onChange para vários campos.
Validações HTML nativas
| Atributo | O que valida | Exemplo |
|---|---|---|
required | Campo obrigatório | <input required /> |
type="email" | Formato de e-mail | <input type="email" /> |
type="number" | Somente números | <input type="number" /> |
min / max | Valor mínimo/máximo | min="18" max="100" |
minLength | Mínimo de caracteres | minLength="3" |
pattern | Expressão regular customizada | pattern="[0-9]{8}" |
REGEX — padrões para o atributo pattern
<input pattern="[A-Za-z]{3,}" /> // letras (maiú ou minú), mínimo 3 <input pattern="[0-9]{8}" /> // exatamente 8 dígitos <input pattern=".{6,}" /> // qualquer coisa, mínimo 6 chars <input pattern="[0-9]{5}-[0-9]{3}" /> // CEP: 12345-678
| Símbolo | Significa | Exemplo |
|---|---|---|
[A-Z] | Uma letra maiúscula | [A-Z]{3} = 3 maiúsculas |
[a-z] | Uma letra minúscula | |
[0-9] | Um dígito | [0-9]{4} = 4 dígitos |
{n} | Exatamente n vezes | [0-9]{8} |
{n,} | n ou mais vezes | .{6,} = mín 6 |
{n,m} | Entre n e m vezes | [a-z]{3,10} |
. | Qualquer caractere | .{8,} = mín 8 |
Custom Hook useForm()
Por que extrair? A lógica (estado + onChange unificado) vai se repetir em toda tela com inputs: Login, Cadastro, Editar perfil... O hook encapsula uma vez e serve para todos.
// hooks/useForm.js const useForm = (initialState) => { const [form, setForm] = useState(initialState) const onChange = (event) => { const { name, value } = event.target setForm({ ...form, [name]: value }) } return [form, onChange] } // Usando: const [form, onChange] = useForm({ email: "", password: "" }) // Cada input: name="campo" value={form.campo} onChange={onChange}
14 O que é um Estado Global? Props Drilling · Redux · MobX · Context API clique para abrir
⬇ PDF do módulo
Problemas com estados locais em apps grandes
| Problema | O que acontece na prática |
|---|---|
| Dados espalhados | Cada componente tem estado isolado — difícil coordenar |
| Responsabilidade errada | Componente de navegação gerenciando dados do carrinho |
| Fonte de verdade dupla | Mesmo dado em dois lugares → pode desincronizar |
| Props Drilling | Uma prop atravessa 3-4 componentes que não precisam dela |
Props Drilling — visualizando o problema
// ❌ Filho1 e Filho2 recebem "carrinho" só para repassar — não usam <App carrinho={carrinho}> <Filho1 carrinho={carrinho}> {// não usa, só repassa} <Filho2 carrinho={carrinho}> {// não usa, só repassa} <Filho3 carrinho={carrinho} /> {// usa de verdade} </Filho2> </Filho1> </App>
Estado Global — como deve funcionar
- Uma camada de dados única, acessada diretamente por qualquer componente
- Fluxo: API ↔ Estado Global ↔ Componentes (sem props)
- Componentes não chamam a API diretamente — pedem ao estado global
Opções de implementação
| Opção | Quando usar |
|---|---|
| Redux | Apps muito grandes com muitos desenvolvedores |
| MobX | Equipes que preferem orientação a objetos |
| Context API (nativa) | Apps médios — sem instalar nada extra |
15 React Context createContext · Provider · useContext · reatividade clique para abrir
⬇ PDF do módulo
Por que Context e não uma variável global comum? Uma variável
let dados = [] não é reativa — mudar ela não re-renderiza nada. O Context é integrado ao React: quando o value do Provider muda, todos os consumidores re-renderizam automaticamente.
Os 3 passos — criar, fornecer e consumir
import React, { useState, useContext } from "react" // PASSO 1: Criar o contexto (geralmente em arquivo separado) export const MeuContexto = React.createContext() // PASSO 2: Provider — envolve quem vai precisar dos dados // Qualquer nível dentro dele pode acessar o "value" function App() { const [cor, setCor] = useState("azul") return ( <MeuContexto.Provider value={{ cor, setCor }}> <ComponenteA /> <ComponenteB /> </MeuContexto.Provider> ) } // PASSO 3: Consumer — acessa em qualquer nível, sem prop drilling function ComponenteB() { const { cor, setCor } = useContext(MeuContexto) // recebe o value return <p>Cor: {cor}</p> }
Onde o Context aparece na prática
- Dados do usuário logado: nome, foto, permissões em qualquer tela
- Tema visual (dark/light) — o
ThemeProviderdo Material UI é um Context - Idioma — textos traduzidos disponíveis globalmente
- O próprio
<BrowserRouter>usa Context para queuseNavigateeuseParamsfuncionem em qualquer lugar
Quando NÃO usar: quando o dado percorre 1-2 níveis apenas — props são mais simples. Context tem custo: todo consumidor re-renderiza quando o
value muda.
16 Estado Global com Context GlobalState · states · setters · requests · arquitetura completa clique para abrir
⬇ PDF do módulo
A ideia: um componente
GlobalState centraliza todos os estados e funções de requisição, e os distribui via Context para qualquer componente — sem props drilling.
Os 3 arquivos da arquitetura
| Arquivo | Papel |
|---|---|
GlobalStateContext.js | Cria e exporta o objeto Context (1 linha) |
GlobalState.js | Todos os estados, funções de API e o Provider |
App.jsx | Usa <GlobalState> como wrapper mais externo |
GlobalStateContext.js
import React from "react" const GlobalStateContext = React.createContext() export default GlobalStateContext
GlobalState.js — coração do sistema
import React, { useState } from "react" import axios from "axios" import GlobalStateContext from "./GlobalStateContext" const GlobalState = (props) => { // ── estados ──────────────────────────────────────────── const [produtos, setProdutos] = useState([]) const [usuario, setUsuario] = useState({}) const [carrinho, setCarrinho] = useState([]) // ── requisições ──────────────────────────────────────── const getProdutos = () => { axios.get('/api/produtos').then((res) => setProdutos(res.data)) } const getUsuario = () => { axios.get('/api/usuario').then((res) => setUsuario(res.data)) } // ── 3 grupos para facilitar o consumo ───────────────── const states = { produtos, usuario, carrinho } const setters = { setProdutos, setUsuario, setCarrinho } const requests = { getProdutos, getUsuario } return ( <GlobalStateContext.Provider value={{ states, setters, requests }}> {props.children} </GlobalStateContext.Provider> ) } export default GlobalState
App.jsx — GlobalState como wrapper mais externo
function App() { return ( <GlobalState> <BrowserRouter> <Menu /> <AppRoutes /> </BrowserRouter> </GlobalState> ) }
Consumindo em qualquer componente
import { useContext, useEffect } from "react" import GlobalStateContext from "../contexts/GlobalStateContext" function TelaProdutos() { const { states, setters, requests } = useContext(GlobalStateContext) const { produtos } = states const { getProdutos } = requests useEffect(() => { getProdutos() }, []) return ( <ul>{produtos.map((p) => <li key={p.id}>{p.nome}</li>)}</ul> ) }