UC00621

Frameworks JavaScript — React

Componentes, estado, hooks, deploy

Curso profissional · 25h · Frontend moderno

Plano

  1. O que é React e porquê
  2. Setup: Vite
  3. JSX e componentes
  4. Props e composição
  5. Estado (useState)
  6. Eventos e formulários
  7. Efeitos (useEffect)
  8. Listas e keys
  9. Routing (React Router)
  10. Deploy

Bloco 1 · Introdução

Porquê React?

  • Componentes reutilizáveis = código DRY.
  • Estado reactivo = UI actualiza automaticamente.
  • Ecossistema enorme (libraries, jobs).
  • Performance (Virtual DOM).
  • Usado por: Facebook, Netflix, Airbnb, Vercel.

Alternativas: Vue, Svelte, Angular.

Vanilla vs React

// Vanilla JS — manual
document.getElementById("count").textContent = count;

// React — declarativo
function Counter() {
  const [count, setCount] = useState(0);
  return <p>{count}</p>;
}

React = "descreve o que queres", não "como o fazer passo a passo".

Setup com Vite

npm create vite@latest minha-app -- --template react
cd minha-app
npm install
npm run dev

Abre http://localhost:5173

Vite = bundler moderno (rápido, simples).

Estrutura do projecto

minha-app/
├── public/
├── src/
│   ├── App.jsx
│   ├── main.jsx
│   ├── App.css
│   └── components/
├── index.html
├── package.json
└── vite.config.js

Tudo começa em main.jsx → renderiza <App />.

Bloco 2 · JSX

O que é JSX

// Não é HTML, é JavaScript
const elemento = <h1>Olá mundo</h1>;

// Pode misturar variáveis
const nome = "Ana";
const saudacao = <h1>Olá, {nome}!</h1>;

// Atributos em camelCase
<div className="card" onClick={handleClick}>
  <img src={url} alt={descricao} />
</div>

Importante: classclassName, forhtmlFor.

Regras JSX

// ❌ Múltiplos elementos top-level
return (
  <h1>Título</h1>
  <p>Texto</p>
);

// ✅ Wrap em fragment ou div
return (
  <>
    <h1>Título</h1>
    <p>Texto</p>
  </>
);

// ✅ Self-closing tags
<img src="..." />
<br />

Bloco 3 · Componentes

Componente funcional

// Definição
function Saudacao() {
  return <h1>Olá!</h1>;
}

// Uso
function App() {
  return (
    <div>
      <Saudacao />
      <Saudacao />
    </div>
  );
}

Convenção: nome do componente em PascalCase.

Props

// Receber props
function Card({ titulo, descricao }) {
  return (
    <div className="card">
      <h2>{titulo}</h2>
      <p>{descricao}</p>
    </div>
  );
}

// Passar props
<Card 
  titulo="React" 
  descricao="Framework JavaScript" 
/>

Props são imutáveis.

Children

function Card({ children }) {
  return <div className="card">{children}</div>;
}

// Uso
<Card>
  <h2>Título</h2>
  <p>Conteúdo personalizado</p>
</Card>

children = tudo entre as tags de abertura/fechamento.

Bloco 4 · Estado

useState

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Contador: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  );
}

setCount triggers re-render do componente.

Estado com objectos

const [user, setUser] = useState({
  nome: "",
  email: "",
});

// Update imutável (spread)
setUser({
  ...user,
  nome: "Ana"
});

// Update baseado em estado anterior
setCount(prev => prev + 1);

Nunca mutar directamente o estado!

Renderização condicional

function Welcome({ user }) {
  if (!user) {
    return <p>Login necessário</p>;
  }
  return <h1>Olá, {user.nome}</h1>;
}

// Inline com &&
{isLoggedIn && <Dashboard />}

// Ternário
{isLoading ? <Spinner /> : <Content />}

Bloco 5 · Eventos

Handlers

function Form() {
  const [nome, setNome] = useState("");
  
  const handleChange = (e) => {
    setNome(e.target.value);
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log("Submetido:", nome);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input value={nome} onChange={handleChange} />
      <button type="submit">Enviar</button>
    </form>
  );
}

Controlled inputs

function Login() {
  const [form, setForm] = useState({
    email: "",
    password: ""
  });
  
  const update = (campo) => (e) => {
    setForm({ ...form, [campo]: e.target.value });
  };
  
  return (
    <form>
      <input value={form.email} onChange={update("email")} />
      <input value={form.password} onChange={update("password")} />
    </form>
  );
}

Bloco 6 · useEffect

Side effects

import { useEffect, useState } from "react";

function Clock() {
  const [time, setTime] = useState(new Date());
  
  useEffect(() => {
    const timer = setInterval(() => {
      setTime(new Date());
    }, 1000);
    
    // Cleanup
    return () => clearInterval(timer);
  }, []);  // [] = correr apenas no mount
  
  return <p>{time.toLocaleTimeString()}</p>;
}

Fetch de dados

function Users() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    fetch("https://api.github.com/users")
      .then(r => r.json())
      .then(data => {
        setUsers(data);
        setLoading(false);
      });
  }, []);
  
  if (loading) return <p>A carregar...</p>;
  
  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.login}</li>)}
    </ul>
  );
}

Bloco 7 · Listas

map() e keys

const tarefas = [
  { id: 1, texto: "Estudar React" },
  { id: 2, texto: "Fazer projecto" },
];

function Lista() {
  return (
    <ul>
      {tarefas.map(t => (
        <li key={t.id}>{t.texto}</li>
      ))}
    </ul>
  );
}

key = único e estável (preferir ID, não index).

Adicionar/remover

function TodoList() {
  const [todos, setTodos] = useState([]);
  
  const add = (texto) => {
    setTodos([...todos, { id: Date.now(), texto }]);
  };
  
  const remove = (id) => {
    setTodos(todos.filter(t => t.id !== id));
  };
  
  return (
    <ul>
      {todos.map(t => (
        <li key={t.id}>
          {t.texto}
          <button onClick={() => remove(t.id)}>X</button>
        </li>
      ))}
    </ul>
  );
}

Bloco 8 · Routing

React Router

npm install react-router-dom
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/sobre">Sobre</Link>
      </nav>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/sobre" element={<Sobre />} />
        <Route path="/user/:id" element={<User />} />
      </Routes>
    </BrowserRouter>
  );
}

Parâmetros

import { useParams, useNavigate } from "react-router-dom";

function User() {
  const { id } = useParams();
  const navigate = useNavigate();
  
  return (
    <div>
      <h1>User {id}</h1>
      <button onClick={() => navigate("/")}>
        Voltar
      </button>
    </div>
  );
}

Bloco 9 · Deploy

Build de produção

npm run build

# Cria pasta dist/ com ficheiros optimizados

dist/ = HTML, CSS, JS minificados prontos a servir.

Vercel (recomendado)

npm i -g vercel
vercel

Ou via dashboard:

  1. Push repo no GitHub.
  2. Importar no vercel.com.
  3. Deploy automático em cada push.

Outras opções: Netlify, GitHub Pages, Cloudflare Pages.

Boas práticas

  • Componentes pequenos (< 100 linhas).
  • Separar lógica (custom hooks).
  • PropTypes ou TypeScript para tipos.
  • Não mutar estado directamente.
  • Sempre key em listas.
  • Loading + error states em fetch.
  • Acessibilidade (semantic JSX).

UC00621 · resumo

  • React = componentes reutilizáveis + estado reactivo.
  • JSX = JS que parece HTML.
  • Props = dados de pai → filho (imutáveis).
  • useState = estado local que triggers re-render.
  • useEffect = side effects (fetch, timers, subscriptions).
  • map() + key para listas.
  • React Router para multi-página.
  • Vercel/Netlify para deploy rápido.
  • Base para Next.js, React Native, e ecossistema moderno.