fix: les 6
This commit is contained in:
@@ -1,234 +1,58 @@
|
||||
# Les 5: TypeScript voor React
|
||||
# Les 5: Next.js — Het React Framework (Part 1)
|
||||
|
||||
---
|
||||
|
||||
## Hoofdstuk
|
||||
**Deel 2: Technical Foundations** (Les 4-9)
|
||||
**Deel 2: Technical Foundations** (Les 4-8)
|
||||
|
||||
## Beschrijving
|
||||
Verdieping in TypeScript met focus op React-patronen. Studenten leren generics, utility types, en hoe je React components, hooks, events en API calls correct typt. Voorbereiding op Les 6 waar ze met Next.js aan de slag gaan.
|
||||
|
||||
**Voorkennis:** Les 4 (TypeScript Fundamentals) — basic types, interfaces, union types, type aliases, functies typen.
|
||||
Introductie Next.js voor React developers. App Router, routing, server/client components, data fetching. Hands-on: QuickPoll app Part 1 (stap 0-3) klassikaal bouwen.
|
||||
|
||||
---
|
||||
|
||||
## Te Behandelen
|
||||
## Te Behandelen (~45 min theorie)
|
||||
|
||||
### Generics (20 min)
|
||||
- Waarom generics? Herbruikbare, type-safe code
|
||||
- `Array<T>`, `Promise<T>` — generics die ze al kennen
|
||||
- Eigen generics schrijven: `function getFirst<T>(items: T[]): T`
|
||||
- Generics met constraints: `<T extends { id: string }>`
|
||||
- `keyof` operator: `function getValue<T, K extends keyof T>(obj: T, key: K): T[K]`
|
||||
|
||||
```typescript
|
||||
// Generic functie
|
||||
function wrapInArray<T>(value: T): T[] {
|
||||
return [value];
|
||||
}
|
||||
|
||||
wrapInArray("hello"); // string[]
|
||||
wrapInArray(42); // number[]
|
||||
|
||||
// Met constraint
|
||||
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
|
||||
return obj[key];
|
||||
}
|
||||
```
|
||||
- Waarom Next.js? React is een library, Next.js is het framework
|
||||
- Create-next-app setup met TypeScript + Tailwind
|
||||
- App Router: folder-based routing (page.tsx = route)
|
||||
- Layouts: root layout, nested layouts
|
||||
- Dynamic Routes: [id] met Promise-based params (Next.js 15)
|
||||
- Server Components vs Client Components
|
||||
- "use client" directive
|
||||
- Data Fetching in async Server Components
|
||||
- Server Actions introductie ("use server")
|
||||
- Route Groups ((marketing))
|
||||
- Project structuur best practices
|
||||
|
||||
---
|
||||
|
||||
### Utility Types (15 min)
|
||||
- `Partial<T>` — alle properties optioneel (handig voor updates)
|
||||
- `Pick<T, K>` — selecteer specifieke properties
|
||||
- `Omit<T, K>` — alles behalve specifieke properties
|
||||
- `Record<K, V>` — key-value mapping
|
||||
- Praktisch voorbeeld: `updateUser(id: string, data: Partial<User>)`
|
||||
## Lesopdracht (120 min, klassikaal)
|
||||
|
||||
```typescript
|
||||
interface User {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
age: number;
|
||||
}
|
||||
### QuickPoll App Part 1 — samen met Tim
|
||||
|
||||
// Partial: voor update functies
|
||||
function updateUser(id: string, updates: Partial<User>): User { ... }
|
||||
updateUser("1", { name: "Tim" }); // alleen name updaten
|
||||
- **Stap 0:** Setup (create-next-app, npm install, dev server)
|
||||
- **Stap 1:** Layout met navigatie (Tailwind styling)
|
||||
- **Stap 2:** Homepage met polls lijst (server component)
|
||||
- **Stap 3:** API route GET single poll (dynamic route, 404 handling)
|
||||
|
||||
// Omit: voor create functies (id wordt server-side gegenereerd)
|
||||
type CreateUserInput = Omit<User, "id">;
|
||||
|
||||
// Pick: voor specifieke views
|
||||
type UserPreview = Pick<User, "id" | "name">;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### React Props Typen (20 min)
|
||||
- Interface voor component props
|
||||
- Children typen met `React.ReactNode`
|
||||
- Callback props: `onClick: () => void`, `onChange: (value: string) => void`
|
||||
- Spread props en prop forwarding
|
||||
- Default values met destructuring
|
||||
|
||||
```typescript
|
||||
interface CardProps {
|
||||
title: string;
|
||||
children: React.ReactNode;
|
||||
variant?: "default" | "highlighted";
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
function Card({ title, children, variant = "default", onClose }: CardProps) {
|
||||
return (
|
||||
<div className={`card card-${variant}`}>
|
||||
<h2>{title}</h2>
|
||||
{onClose && <button onClick={onClose}>×</button>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### useState & useEffect Typen (15 min)
|
||||
- Type inference bij useState: `useState(0)` → number
|
||||
- Explicit types: `useState<User | null>(null)`
|
||||
- Arrays: `useState<Product[]>([])`
|
||||
- useEffect met async patterns
|
||||
|
||||
```typescript
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [loading, setLoading] = useState(false); // inference: boolean
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchData() {
|
||||
setLoading(true);
|
||||
const response = await fetch("/api/users");
|
||||
const data: User[] = await response.json();
|
||||
setUsers(data);
|
||||
setLoading(false);
|
||||
}
|
||||
fetchData();
|
||||
}, []);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Event Handlers Typen (10 min)
|
||||
- `React.ChangeEvent<HTMLInputElement>`
|
||||
- `React.FormEvent<HTMLFormElement>`
|
||||
- `React.MouseEvent<HTMLButtonElement>`
|
||||
- Tip: hover in Cursor om het juiste event type te vinden
|
||||
|
||||
```typescript
|
||||
function SearchForm() {
|
||||
const [query, setQuery] = useState("");
|
||||
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setQuery(e.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
console.log("Searching:", query);
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<input value={query} onChange={handleChange} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### API Responses & Async Typen (15 min)
|
||||
- `Promise<T>` voor async functies
|
||||
- API response types definiëren
|
||||
- Error handling met types
|
||||
- Fetch wrapper met generics
|
||||
|
||||
```typescript
|
||||
interface ApiResponse<T> {
|
||||
data: T;
|
||||
status: number;
|
||||
message: string;
|
||||
}
|
||||
|
||||
async function fetchApi<T>(url: string): Promise<ApiResponse<T>> {
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
|
||||
// Gebruik
|
||||
const { data: users } = await fetchApi<User[]>("/api/users");
|
||||
const { data: product } = await fetchApi<Product>("/api/products/1");
|
||||
```
|
||||
Huiswerk: Stap 0-3 zelfstandig afmaken
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
- Cursor (Student Plan)
|
||||
- Next.js 15
|
||||
- Cursor
|
||||
- TypeScript
|
||||
- React (via CDN of Vite)
|
||||
|
||||
---
|
||||
|
||||
## Lesopdracht (75 min)
|
||||
|
||||
### Typed React Dashboard
|
||||
|
||||
Studenten bouwen een kleine React-app met volledig getypte components:
|
||||
|
||||
**Opdracht:** Bouw een Product Dashboard met:
|
||||
|
||||
1. **`ProductCard` component** — props: Product interface, onAddToCart callback
|
||||
2. **`ProductList` component** — props: Product[], filterCategory (union type)
|
||||
3. **`SearchBar` component** — props: query string, onChange handler (getypt event)
|
||||
4. **`useProducts` custom hook** — fetch products, return `{ products, loading, error }`
|
||||
5. **Alle types in een apart `types.ts` bestand**
|
||||
|
||||
**Vereisten:**
|
||||
- Geen `any` toegestaan
|
||||
- Alle event handlers correct getypt
|
||||
- useState met expliciete types waar nodig
|
||||
- Minstens 1 generic functie (bijv. een `sortBy<T>` of `filterBy<T>`)
|
||||
|
||||
---
|
||||
|
||||
## Huiswerk (2 uur)
|
||||
|
||||
### Extend het Dashboard
|
||||
|
||||
Bouw voort op de lesopdracht:
|
||||
|
||||
1. **Shopping Cart** toevoegen met getypte state (`CartItem[]`)
|
||||
2. **API simulatie** — maak een `fetchProducts()` functie met `Promise<Product[]>`
|
||||
3. **Utility types gebruiken** — `Partial<Product>` voor updates, `Omit<Product, "id">` voor create
|
||||
4. **Bonus: Generic `DataTable<T>` component** — werkt met elke array van objecten
|
||||
|
||||
### Deliverable
|
||||
- Werkend React project met TypeScript
|
||||
- Alle components volledig getypt
|
||||
- `npm run check` = 0 errors
|
||||
- Tailwind CSS
|
||||
|
||||
---
|
||||
|
||||
## Leerdoelen
|
||||
|
||||
Na deze les kan de student:
|
||||
- Generics schrijven en toepassen
|
||||
- Utility types gebruiken (Partial, Pick, Omit, Record)
|
||||
- React component props correct typen
|
||||
- useState en useEffect met types gebruiken
|
||||
- Event handlers typen (ChangeEvent, FormEvent, MouseEvent)
|
||||
- Async functies en API responses typen met Promise<T>
|
||||
- Een custom hook schrijven met correcte return types
|
||||
- Uitleggen wat Next.js toevoegt aan React
|
||||
- Een Next.js project opzetten met App Router
|
||||
- Verschil tussen Server en Client Components
|
||||
- File-based routing gebruiken
|
||||
- Dynamic routes met parameters maken
|
||||
- Data fetchen in Server Components
|
||||
|
||||
Reference in New Issue
Block a user