Facebook likes
<div id="likes">
<span class="icon">❤️</span>
<span id="likes-text"></span>
</div>
<button id="add">Add like</button>index.html
Vereisten:
Schrijf een functie likes(names) die een array van namen ontvangt
De functie retourneert een correcte tekst a.d.h.v length zoals:
No one likes this
Peter likes this
Peter and Sarah like this
Peter, Sarah and 2 others like this.
Vereisten:
Toon deze tekst in de browser
Wanneer je op “Add like” klikt:
wordt een willekeurige naam toegevoegd aan de array
wordt de UI manueel opnieuw gerenderd met een functie render()
Resultaat:
React
Veel manuele DOM-manipulatie
Moeilijk schaalbaar bij groeiende applicaties
Kans op fouten wanneer data en UI niet in sync zijn
...
Dit hebben we net (kleinschalig) ervaren in de likes-oefening.
Veel manuele DOM-manipulatie
Moeilijk schaalbaar bij groeiende applicaties
Kans op fouten wanneer data en UI niet in sync zijn
...
Imperatief programmeren
Beschrijven hoe iets moet gebeuren.
element.innerText = text;
element.classList.add("active");Jij beslist wanneer en hoe de UI verandert
Meer controle, maar ook meer verantwoordelijkheid
Declaratief programmeren
Beschrijven wat het eindresultaat moet zijn
<p className="active">{text}</p>React regelt de updates
Minder kans op fouten
Code is makkelijker te begrijpen
Waarom werkt plain JavaScript niet goed op schaal?
Veel getElementById / querySelector
Meerdere render()-functies
Data wordt op meerdere plaatsen gebruikt
Kleine wijziging → veel code aanpassen
“Dit werkt voor kleine voorbeelden, maar niet voor echte apps.”
Het antwoord: Component based frameworks
React, Angular, Vue, ... werken met componenten:
Kleine, herbruikbare stukken UI
Elk component heeft:
eigen structuur
eigen logica
eigen data
App
├── Likes
├── Button
└── CounterVoordelen van component based & declaratief werken
Leesbaar
Testbaar
Herbruikbaar
Onderhoudbaar
Makkelijk uitbreidbaar
Waarom kiezen wij voor React?
Zeer populair in de industrie
Grote community
Veel jobs
Werkt samen met TypeScript
Enkel focussen op UI (library, geen framework)
React laat ons UI bouwen op een manier die leesbaar, voorspelbaar en schaalbaar is.
React
npm create vite@latest my-app -- --template react-ts
cd my-app
npm install
npm run devOntwikkelserver
Build configuratie
Moderne JavaScript setup
Live reload
Zonder deze tool zou dat veel werk zijn
my-react-app/
├── node_modules/
├── public/
├── src/
│ ├── App.tsx
│ └── main.tsx
├── index.html
└── package.jsonDe belangrijkste files voorlopig zijn main.tsx en App.tsx
├── src/
│ ├── App.tsx
│ └── main.tsx
main.tsx
Startpunt van de applicatie
Verbindt React met de HTML
Hier werken we bijna nooit
App.tsx
Hoofdcomponent van onze applicatie
Hier bouwen we onze UI
Dit is waar alles samenkomt.
Hoe bouwen we een UI in react?
We maken components
Components zijn JavaScript-functies
Components geven JSX terug
Components kunnen data ontvangen (props)
Tip: Het bestand eindigt op TSX bij ons omdat wij typescript gebruiken. Zonder typescript is het javascript dus JSX
React
We bouwen de Facebook likes opnieuw,
maar nu in React, zonder DOM-manipulatie.
➡️ Logica blijft hetzelfde.
Plak de likes function uit opwarmoefening in App.tsx
function likes(names) {
if (names.length === 0) return "No one likes this";
if (names.length === 1) return `${names[0]} likes this`;
if (names.length === 2) return `${names[0]} and ${names[1]} like this`;
if (names.length === 3)
return `${names[0]}, ${names[1]} and ${names[2]} like this`;
return `${names[0]}, ${names[1]} and ${names.length - 2} others like this`;
}Nu pakken we de rendering aan van onze App.
function App() {
const names = [];
return (
<div className="card">
<div className="likes">
<span className="icon">❤️</span>
<span>{likes(names)}</span>
</div>
</div>
);
}
Geen getElementById
Geen innerText
Geen render()
text.innerText = likes(names);
In React beschrijven we wat er moet staan
React zorgt voor de DOM
Vanilla javascript
React
<span>{likes(names)}</span><span class="icon">❤️</span>
JSX: syntax om HTML en Javascript te combineren
Indien TypeScript → TSX
Kan niet om met gereserveerde woorden in JS/TS
bv. class (→ className), for (→ htmlFor), ...
Vanilla javascript
React
<span className="icon">❤️</span>
Waarom niet?
Wat missen we?
We zien nu wel onze UI maar niets is interactief.
Wat React nu al voor ons oplost:
Geen DOM-manipulatie
Duidelijke link tussen data en UI
Leesbare code
Minder kans op fouten
Wat nog niet?
Dynamische data
Interactie
Naam van de component:
start met een hoofdletter (App, Button ...)
Componenten exporteer je als een functie
export default function App() { ... }
React
Goed om weten:
3 regels voor het gebruik van JSX:
React
function App() {
let names = [];
function addLike() {
names.push("Peter");
}
return (
<div>
<button onClick={addLike}>Add like</button>
<p>{likes(names)}</p>
</div>
);
}Wat verwachten we? Wat gebeurt er?
We maken nu een addLike functie en plaatsen deze op de button in App.tsx
React rendert componenten opnieuw bij verandering.
Variabelen worden dan opnieuw aangemaakt.
Wat is hier het probleem?
React onthoudt ze niet
Ze verdwijnen bij elke render
React weet niet dat ze veranderd zijn
➡️ Dat is precies wat useState doet.
We hebben een manier nodig om:
data te onthouden tussen renders
React te verwittigen dat data werd gewijzigd
useState is een React Hook die data bewaart
én React laat weten wanneer die data verandert.
Wat is een useState?
import { useState } from "react";
function App() {
const [names, setNames] = useState([]);
return (
<div>
<p>{likes(names)}</p>
</div>
);
}names → huidige waarde
setNames → enige manier om te wijzigen
useState([]) → startwaarde
Aanpassen van een state
function addLike() {
setNames([...names, "Peter"]);
}Geen push
Nieuwe array maken
React ziet het verschil → re-render
| Gewone variabele | useState |
|---|---|
| Wordt vergeten | Blijft bestaan |
| Triggert geen render | Triggert render |
| React weet niets | React weet alles |
Waarom gebruiken we niet altijd een useState?
Oefening 1: Number state tonen
Maak een useState count met startwaarde 0
Toon de waarde in de UI.
Oefening 1: Number state tonen
Maak een state variabele count met startwaarde 0
Toon de waarde van count
Verhoog count met 1 wanneer je op de knop klikt
import { useState } from "react";
function App() {
// TODO: maak een state variabele "count" met startwaarde 0
return (
<div>
{/* TODO: toon de waarde van count */}
<p>Count:</p>
{/* TODO: verhoog count met 1 bij klik */}
<button onClick={() => {}}>
Increment
</button>
</div>
);
}
export default App;Oefening 2: Change Name
Maak een useState name met startwaarde "Peter"
Toon “Hello {name}”
Verander naar "Sarah" wanneer je op de knop klikt
function App() {
// TODO: maak een state variabele "name" met startwaarde "Peter"
return (
<div>
{/* TODO: toon de naam in de tekst */}
<p>Hello</p>
{/* TODO: verander de naam naar "Sarah" bij klik */}
<button onClick={() => {}}>
Change name
</button>
</div>
);
}
export default App;Oefening 3: Bool-Online
Maak een state isOnline met startwaarde true
Toon "Online" of "Offline"
Toggle de waarde bij klik
function App() {
// TODO: maak een boolean state "isOnline" met startwaarde true
return (
<div>
<p>
Status:
{/* TODO: toon "Online" of "Offline" */}
</p>
{/* TODO: toggle isOnline bij klik */}
<button onClick={() => {}}>
Toggle status
</button>
</div>
);
}
export default App;React
Waarom splitsen we code op?
Veel JSX in één bestand
Moeilijk leesbaar
Moeilijk herbruikbaar
Alles hangt samen
React component is:
Een JavaScript functie
Die JSX teruggeeft
En een stuk UI beschrijft
function MyComponent() {
return <div>Hello</div>;
}In onze huidige app hebben we dit:
function App() {
const [names, setNames] = useState([]);
return (
<div className="card">
<button onClick={() => setNames([...names, "Peter"])}>
Add like
</button>
<div className="likes">
<span>❤️</span>
<span>{likes(names)}</span>
</div>
</div>
);
}Zien jullie hier verschillende 'stukken' UI in?
De UI bestaat uit:
Likes weergave
Actie (knop)
Card
├── Button
└── LikesZien jullie hier verschillende 'stukken' UI in?
Zo kunnen we een eerste component "Likes" maken.
Dit doen we vaak in een mapje "components".
function Likes({ names }) {
return (
<div className="likes">
<span>❤️</span>
<span>{likes(names)}</span>
</div>
);
}names komt van buitenaf (Prop)
Component is stateless
Logica blijft herbruikbaar
components/Likes.tsx
Deze component kunnen we makkelijk gebruiken in App.
import Likes from "./components/Likes";
function App() {
const [names, setNames] = useState([]);
return (
<div className="card">
<button onClick={() => setNames([...names, "Peter"])}>
Add like
</button>
<Likes names={names} />
</div>
);
}We hebben code opgesplitst zonder functionaliteit te verliezen
Props = Data voor een component
Vergelijkbaar met functie-argumenten
Alleen-lezen
Van parent → child
<Likes names={names} />Herbruikbaar met andere data!
<Likes names={names} />
<Likes names={["Peter"]} />
<Likes names={["Anna", "Lisa"]} />Wat hoort waar?
State → zo hoog mogelijk
UI → zo laag mogelijk
Logica → bij de component die ze gebruikt
Voordelen van components:
Leesbaardere code
Herbruikbare UI
Makkelijker testen
Makkelijker uitbreiden
Oefening 1: Circle component
Maak een component Circle
De component toont een gekleurde cirkel
Render de component in App.tsx
function Circle() {
// TODO: return JSX die een cirkel toont
return (
);
}Oefening 2: Badge component
Maak een component Badge
De component toont een label in een gekleurd vakje
Geef de tekst door via props
type BadgeProps = {
// TODO: voeg een prop "text" toe
};
function Badge(props: BadgeProps) {
return (
{/* TODO: toon de tekst */}
);
}Oefening 3: ProfileCard component
Maak een component ProfileCard
Toon een naam en leeftijd
Geef beide waarden door via props
Geen Startercode?!
Probeer nu zelf code te schrijven zonder startOefening 4: StatusMessage component
Maak een component StatusMessage
Toon “Online” of “Offline”
Geef de status door via een boolean prop
React
Tot nu toe:
Data ging van parent → child
Maar UI bevat vaak knoppen en acties
Hoe laten we een component iets “doen”?
Events in React:
React gebruikt camelCase
Events krijgen een functie
<button onClick={handleClick}>Click me</button>Wat is anders met vanilla JS?
addEventListener
We maken het voorbeeld met de LikeButton
function LikeButton() {
return <button>Add like</button>;
}Probleem:
Deze knop weet niets over names
Waar komt de logica?
Oplossing: Functie doorgeven als prop
function LikeButton({ onLike }) {
return <button onClick={onLike}>Add like</button>;
}Component voert gedrag uit dat het krijgt.
Wij zullen vanuit App de functie meegeven aan de LikeButton die de veranderingen teweeg zullen brengen.
De state blijft in onze App en we maken een functie aan om door te geven aan de LikeButton
function App() {
const [names, setNames] = useState([]);
function addLike() {
setNames([...names, "Peter"]);
}
return (
<div className="card">
<LikeButton onLike={addLike} />
<Likes names={names} />
</div>
);
}
State blijft bovenaan
Child triggert verandering
Data flow in React
App (state)
↓ props
LikeButton Likes
↑ events ↑ props
React
Tot nu toe:
We beschrijven UI
UI volgt uit state
Components zijn puur
Maar… wat als we iets willen doen naast renderen?
Dit noement we een side effect.
Side effects zijn dingen die:
Niet puur UI zijn
Buiten React gebeuren
Voorbeelden:
console.log
document.title
API calls (later)
timers
Hiervoor maken we gebruiken van een andere hook genaamd useEffect.
useEffect laat je code uitvoeren na het renderen van een component.
useEffect(() => {
console.log("Rendered");
});Voorbeeld:
import { useEffect } from "react";
function App() {
useEffect(() => {
console.log("App rendered");
});
return <h1>Hello</h1>;
}Wordt uitgevoerd na render
Kan meerdere keren lopen
Dependencies zijn zaken die je kan meegeven aan de useEffect waarop die moet letten. (Array na de function)
useEffect(() => {
console.log("Names changed");
}, [names]);Effect loopt wanneer names verandert
React vergelijkt oude en nieuwe waarde
TIP: Wil je dat het maar 1 keer gebeurd? Geef dan een lege dependency array mee.
Waar kunnen we dit gebruiken? Als we dit op onze likes oefening toepassen kan dit als volgt:
useEffect(() => {
document.title = `${names.length} likes`;
}, [names]);Nu zal de titel van het document aanpassen iedere keer als de array van names verandert.
Wat is useEffect NIET?
vervanging van useState
render functie
event handler
➡️ useEffect = reactie op verandering
Wanneer gebruiken we useEffect?
Je iets moet doen na render
Iets buiten React moet gebeuren
Gebruik het niet voor:
berekeningen
UI-logica
Oefening 1:
Gebruik useEffect
Log "Component rendered" in de console
Laat het effect lopen na elke render
import { useEffect } from "react";
function App() {
// TODO: voeg een useEffect toe die iets logt in de console
return (
<div>
<h1>Hello React</h1>
</div>
);
}
export default App;Oefening 2:
Maak een state count
Verhoog count bij klik
Gebruik useEffect en log wanneer count verandert
function App() {
// TODO: maak een state "count" met startwaarde 0
// TODO: gebruik useEffect om iets te loggen wanneer count verandert
return (
<div>
{/* TODO: toon count */}
<p>Count:</p>
{/* TODO: verhoog count bij klik */}
<button onClick={() => {}}>
Increment
</button>
</div>
);
}
export default App;Oefening 3:
Maak een state likes
Gebruik useEffect om de pagina-titel aan te passen
function App() {
// TODO: maak een state "likes" met startwaarde 0
// TODO: gebruik useEffect om document.title aan te passen
// Tip: gebruik likes in de dependency array
return (
<div>
{/* TODO: toon likes */}
<p>Likes:</p>
{/* TODO: verhoog likes bij klik */}
<button onClick={() => {}}>
Add like
</button>
</div>
);
}
export default App;React
Oefening 1 - Emoji Counter
Maak een component EmojiCounter die een emoji en een teller toont
Klikken op de knop verhoogt de teller
Log in useEffect telkens als de teller verandert
Oefening 2 - Tamagochi
Component PetCard met props name
Houd interne states bij: hunger en happiness
Knoppen: “Feed” en “Play”
useEffect logt als hunger > 5 (“Pet is hungry!”)
Oefening 3 - Countdown Timer
Component TimerDisplay toont timeLeft
In App: start timer bij klik, tel af van 10 naar 0
useEffect zorgt dat timer telt
Oefening 4 - Mood Tracker
Component Mood met prop emoji
In App: state currentMood met startwaarde "😊"
Knoppen in App: "😊" en "😢" → verandert state
useEffect logt "Mood changed: {currentMood}"
Oefening 5 - Click Me Counter
Component ClickDisplay met prop count → toont "Clicked {count} times"
In App: state clickCount start op 0
Knop “Click me” → verhoogt clickCount
useEffect logt "Clicked: {clickCount}"