Repository navigation
React
Componenten bovenaan de boomstructuur worden beschouwd als top-level componenten. Componenten zonder subcomponenten zijn leaf componenten.
afhanklijkheidsstructuur word vaak gebruikt door buildtools om alle relevante Javascript-code te bundelen, zodat de clientdeze kan downloaden en weergeven. Grote bundelgrootte kan de gebruikerservaring van React-apps negatief beïnvloeden.
function MyButton() {
return (
<button>
I'm a button
</button>
);
}
export default function MyApp() {
return (
<div>
<h1>Welcome to my app</h1>
<MyButton />
</div>
);
}
JSX is strenger dan HTML. Je moet tags zoals <a> sluiten <br />. Je component mag ook geen meerdere JSX-tags retourneren. Je moet ze in een gedeelde Parent-tag plaatsen, zoals een <div> <div>……</div> of een lege <>...</>
function AboutPage() {
return (
<>
<h1>About</h1>
<p>Hello there.<br />How do you do?</p>
</>
);
}Met JSX kun je opmaakcode in JavaScript plaatsen. Met accolades kun je "terugkeren" naar JavaScript, zodat je een variabele uit je code kunt insluiten en aan de gebruiker kunt tonen. Bijvoorbeeld, dit zal het volgende weergeven user.name:
return (
<>
<h1>
{user.name}
</h1>
</>
);Je kan ook escape maar JavaScript vanuit JSX-attributen, maar dan miet je accolades gebruiken in plaats van aanhalinhgstekens. Bijvoorbeeld: className=“avatar" de "avatar” string wordt doorgegeven als css-klasse, maar de src={user.imgedUrl} de waarde van de JS-variable word gelezen user.imgedUrl en vervolgens doorgegeven als src attribuut.
return (
<img
className="avatar"
src={user.imageUrl}
/>
);In react is er geen speciale syntax voor het schrijven van voorwaarden. Je gebruikt gewone JS-code.
let content;
if (isLoggedIn) {
content = <AdminPanel />;
} else {
content = <LoginForm />;
}
return (
<div>
{content}
</div>
);Je kan het ook anders schrijven op de ? : manier is hetzelfde als if else.
<div>
{isLoggedIn ? (
<AdminPanel />
) : (
<LoginForm />
)}
</div>En als je de else niet nodig heb kan het ook korter met de &&
<div>
{isLoggedIn && <AdminPanel />}
</div>Je gebruikt JS-functies zoals for loop en array- map() om lijsten met componenten te laten zien.
const products = [
{ title: 'Cabbage', isFruit: false, id: 1 },
{ title: 'Garlic', isFruit: false, id: 2 },
{ title: 'Apple', isFruit: true, id: 3 },
];
export default function ShoppingList() {
const listItems = products.map(product =>
<li
key={product.id}
style={{
color: product.isFruit ? 'magenta' : 'darkgreen'
}}
>
{product.title}
</li>
);
return (
<ul>{listItems}</ul>
);
}Een key attribuut moet een uniek zijn van andere items in dit geval. Meestal komt de key uit je data, zoals een id. Je gebruikt een key om te weten wat er is gebeurd als je later items invoegt. Verwijdert of de volgorde ervan verwijderd.
function MyButton() {
function handleClick() {
alert('You clicked me!');
}
return (
<button onClick={handleClick}>
Click me
</button>
);
}Met de function handleClick zeg je wat er met gebeuren als de gebruiker de button klikt. Met de onClick={handleClick} roep je de functie aan.
import { useState } from 'react';
export default function MyApp() {
return (
<div>
<h1>Counters that update separately</h1>
<MyButton />
<MyButton />
</div>
);
}
function MyButton() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<button onClick={handleClick}>
Clicked {count} times
</button>
);
}Als je dat je component bepaalde informatie onthoud en weergeeft voeg je een state toe.
Eerst moet die geimporteert worden
import { useState } from 'react';
usestate is de huidige status (count) en de functie waarmee je deze kunt bijwerken (setCount). Je kan het ook een andere naam geven, maar er zit wel een conventie aan vast [something, setSomething].
const [count, setCount] = useState(0);
de eerste keer dat de knop wordt weergegeven, count is 0 omdat de waarde hebt doorgegeven 0 aan useState(). Als je de status wilt wijzigen roep je setCount() en geef je de nieuwe waarde eraan door.
setCount(count + 1);
Functions die beginnen met use worden hooks genoemd. Hooks zin beperkter dan ander functions. je kan ze alleen aanroepen bovenaan je componenten.
Je kan je eigen hooks maken, ze combineren, data ertussen doorgeven en hergebruiken in verschillende componenten. hoe groter je app hoe minder effects je handmatig hoeft te schrijven.
in het vorige voorbeeld had elke MyButton een eigen waarde count en betanderde de count alleen op de knop waarop er geklikt werd.
Je kan componenten nodig hebben die gegevens delen en altijd samen worden bijgewerkt.
In het voorbeeld moet de stats van de afzonderlijke noppen omhoog naar het component die ze allebei bevat. Dat is hier MyApp.
import { useState } from 'react';
export default function MyApp() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<h1>Counters that update together</h1>
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />
</div>
);
}
function MyButton({ count, onClick }) {
return (
<button onClick={onClick}>
Clicked {count} times
</button>
);
}de status verplaats van MyButton naar MyApp
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}en dat moet de status doorgeven worden aan MyApp op elke element MyButton met de handleClick.
<MyButton count={count} onClick={handleClick} />
<MyButton count={count} onClick={handleClick} />De informatie die zo word meegeven worden props genoemd. de code van MyButton moet aangepast worden zodat die de props kan lezen.
function MyButton({ count, onClick }) {
<button onClick={onClick}>Nu als je op een knop klikt veranderd allebei de count.
Eventhandelers zijn je eigen functies die worden geactiveerd als reactie op gebruikersinteracties zoals klikken, hoeven, focussenop formuliervelden.
<button> ondersteunen alleen ingebouwde browsergebeurtissen zoals onClick. J ekan ook eigen componenten maken en de event-handeler-props ervan elke gewenste toepassongsspecifieke naam geven.
State is er om dingen te onhouden van wat een gebruiker doet. Met useState kin je een state-variable declareren. Deze neemt de initiële state als argument en retourneert een paar waarden: de huidige state en een setter-functie waarmee je deze kunt bijwerken.
State gedraagt zich meer als een momentopname.
met setScore(s => s + 1) kan je meerdere statusupdates in de wachtrij plaatsen.
De state kan elk type JavaScript-waarde bevatten, inclusief objecten. Maar je moet objecten en arrays die je in de React-state bewaart, niet rechtstreeks wijzigen. In plaats daarvan moet je, wanneer je een object of array wilt bijwerken, een nieuwe aanmaken (of een kopie maken van een bestaande) en vervolgens de state bijwerken om die kopie te gebruiken. Meestal gebruik je de ...spread-syntaxis om objecten en arrays te kopiëren die je wilt wijzigen. Dat gelt ook voor een array
import { useState } from 'react';
export default function Form() {
const [to, setTo] = useState('Alice');
const [message, setMessage] = useState('Hello');
function handleSubmit(e) {
e.preventDefault();
setTimeout(() => {
alert(`You said ${message} to ${to}`);
}, 5000);
}
return (
<form onSubmit={handleSubmit}>
<label>
To:{' '}
<select
value={to}
onChange={e => setTo(e.target.value)}
>
<option value="Alice">Alice</option>
<option value="Bob">Bob</option>
</select>
</label>
<textarea
placeholder="Message"
value={message}
onChange={e => setMessage(e.target.value)}
/>
<button type="submit">Send</button>
</form>
);
}- Een renderingproces starten (de bestelling van de gast doorgeven aan de keuken)
- Het component renderen (de bestelling voorbereiden in de keuken)
- De bestelling vastleggen in de DOM (de order in de tabel plaatsen)
Trigger -> Render -> Commit
Je kan immer gebruiken om herhalende code te verminderen.
import { useState } from 'react';
export default function Form() {
const [answer, setAnswer] = useState('');
const [error, setError] = useState(null);
const [status, setStatus] = useState('typing');
if (status === 'success') {
return <h1>That's right!</h1>;
}
async function handleSubmit(e) {
e.preventDefault();
setStatus('submitting');
try {
await submitForm(answer);
setStatus('success');
} catch (err) {
setStatus('typing');
setError(err);
}
}
function handleTextareaChange(e) {
setAnswer(e.target.value);
}
return (
<>
<h2>City quiz</h2>
<p>
In which city is there a billboard that turns air into drinkable water?
</p>
<form onSubmit={handleSubmit}>
<textarea
value={answer}
onChange={handleTextareaChange}
disabled={status === 'submitting'}
/>
<br />
<button
disabled={
answer.length === 0 ||
status === 'submitting'
}
>
Submit
</button>
{error !== null && (
<p className="Error">
{error.message}
</p>
)}
</form>
</>
);
}
function submitForm(answer) {
// Pretend it's hitting the network.
return new Promise((resolve, reject) => {
setTimeout(() => {
let shouldError = answer.toLowerCase() !== 'lima';
if (shouldError) {
reject(new Error('Good guess but a wrong answer. Try again!'));
} else {
resolve();
}
}, 1500);
});
}Goede structurering van de state kan het verschil maken tussen een component dat prettig is om aan te passen en te debuggen.
voorbeeld: van
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [fullName, setFullName] = useState('');naar dit
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const fullName = firstName + ' ' + lastName;soms wil je dat de status van twee componenten altijd tegelijk verandert. om dat te bereiken, verwijder je de status van beide componenten, verplaats je deze naar hun dichtstbijzijnde gemeenschappelijke oudercomponent en geef je deze vervolgens via props aan hen door. Dit word state up lifting genoemd. Dit word veel gebruikt in React.
Hier de uitleg met voorbeelden van een chat: https://react.dev/learn/managing-state#preserving-and-resetting-state
Wanneer je een component opnieuw rendert, moet React bepalen welke delen van de componentstructuur behouden (en bijgewerkt) moeten worden en welke delen moeten worden verwijderd of volledig opnieuw moeten worden aangemaakt. React heeft hiervoor automatische gedrag die behoud standaard de delen van de componentenstructuur die overeenkomen met de eerder gerenderde componentenstructuur.
Als dat niet is wat je wilt kan je het standaardgedrag overschrijven en een componeten dwingen zijn state te resetten door en andere waarde door te geven aan de key.
componeenten met veel statusupdates verspreid over meerdere eventhandlers kunnen onoverzichtelijk worden. Je kan daarvoor een reducer gebruiken. Voorbeeld waar een reducer is gebruikt.
import { useReducer } from 'react';
import AddTask from './AddTask.js';
import TaskList from './TaskList.js';
export default function TaskApp() {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
function handleAddTask(text) {
dispatch({
type: 'added',
id: nextId++,
text: text,
});
}
function handleChangeTask(task) {
dispatch({
type: 'changed',
task: task
});
}
function handleDeleteTask(taskId) {
dispatch({
type: 'deleted',
id: taskId
});
}
return (
<>
<h1>Prague itinerary</h1>
<AddTask onAddTask={handleAddTask} />
<TaskList
tasks={tasks}
onChangeTask={handleChangeTask}
onDeleteTask={handleDeleteTask}
/>
</>
);
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [
...tasks,
{
id: action.id,
text: action.text,
done: false
}
];
}
case 'changed': {
return tasks.map(t => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
let nextId = 3;
const initialTasks = [
{ id: 0, text: 'Visit Kafka Museum', done: true },
{ id: 1, text: 'Watch a puppet show', done: false },
{ id: 2, text: 'Lennon Wall pic', done: false }
];met context is het mogelijk om informatie door te geven aan andere componenten.
Je kan reducer en context combineren om de status van en complex scherm te beheren.
Als je wilt dat een component bepaalde informatie onthoud, maar niet dat die informatie nieuwe renders activeert kan je refs gebruiken.
Net als de state, worden refs door React behouden tussen herrenderingen. Het instellen van de state zorgt echter voor een herrendering van een component. Het wijzigen van een ref doet dat niet! Je kunt de huidige waarde van die ref opvragen via de ref.currentproperty.
Voorbeeld
import { useRef } from 'react';
export default function Counter() {
let ref = useRef(0);
function handleClick() {
ref.current = ref.current + 1;
alert('You clicked ' + ref.current + ' times!');
}
return (
<button onClick={handleClick}>
Click me!
</button>
);
}een ref is een geheime opslagruimte van je componenet die React niet bijhoudt. Je kan het gebruiken om objecten op te slaan die geen invloed hebben op de rendering van het component.
Je kan met refs de DOM manipuleren. React werkt de DOM automatische bij zodat deze overeenkomt met je renderoutput. Soms heb je toegang nodig tot een de DOM-elementen die door React worden beheerd.
Sommige componenten moeten synchroniseren met externe systemen. Je wilt bijvoorbeeld een cmponenet dat niet door React is ontwikkeled, aansturen op basis van de React-status, een serververbinding tot stand brengen of een analyselogboek verzenden wanneer een component op het scherm verschijnt.
Voorbeeld
import { useState, useRef, useEffect } from 'react';
function VideoPlayer({ src, isPlaying }) {
const ref = useRef(null);
useEffect(() => {
if (isPlaying) {
ref.current.play();
} else {
ref.current.pause();
}
}, [isPlaying]);
return <video ref={ref} src={src} loop playsInline />;
}
export default function App() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<>
<button onClick={() => setIsPlaying(!isPlaying)}>
{isPlaying ? 'Pause' : 'Play'}
</button>
<VideoPlayer
isPlaying={isPlaying}
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
/>
</>
);
}Veel effecten zorgen ook voor een opruimfunctie na afloop. Voorbeeld
import { useState, useEffect } from 'react';
import { createConnection } from './chat.js';
export default function ChatRoom() {
useEffect(() => {
const connection = createConnection();
connection.connect();
return () => connection.disconnect();
}, []);
return <h1>Welcome to the chat!</h1>;
}Waarneer geen effecten
- Je hebt geen effecten nodig om gegevens te transformeren voor weergave.
- Je hebt geen Effects nodig om gebruikersgebeurtenissen af te handelen.
Effecten hebben een andere levenscyclus dan componenten. Componenten kunnen worden gemount, bijgewerkt of gedemonteerd. Een effect kan slechts twee dingen doen: iets synchroniseren en later de synchronisatie stoppen. Deze cyclus kan zich meerdere keren herhalen als je effect afhankelijk is van props en state die in de loop van de tijd veranderen.
De use word hier gebruikt omdat in de /icons.svg meerdere iconen staan en met een specifieke id kan je het icon pakken die je wilt. Dan kopieert die het naar de plek waar je het neerzet.
<li>
<a href="https://github.com/vitejs/vite" target="_blank">
<svg
className="button-icon"
role="presentation"
aria-hidden="true"
>
<use href="/icons.svg#github-icon"></use>
</svg>
GitHub
</a>
</li>