2. State
2. State
Deze oefeningenreeks bouwt verder op de oplossingen van de vorige oefeningenreeks. Je kan verder werken van je eigen oplossingen of gebruik maken van de op Canvas gepubliceerde oplossingen. Maak net zoals in de vorige les voor elke oefening een nieuwe component aan.
Tijdens deze oefeningenreeks oefen je op:
- De concepten van les 1
- State
- De useState hook
- Lifting state & prop-drilling
- Composition
- Context
Voor deze oefeningenreeks zijn startbestanden voorzien, clone het repository en plaats de src map in je oplossingen van les 1 (of in de aangeboden oplossingen).
Startbestanden
Oefening 1: Key toevoegen
Je hebt vorige oefeningenreeks in de eerste, tweede en vierde oefening gebruik gemaakt van array. Je hebt daar toen geen key aan meegegeven, als je in de console kijkt zal je hier dan ook waarschuwingen over zien.
De componenten die in de arrays zitten gaan nooit van structuur veranderen, elk rij in de maaltafel zal steeds dezelfde waarde bevatten (), gebruik de getallen in de maaltafel () als key. De rater heeft steeds exact evenveel sterren, deze sterren staan steeds in dezelfde volgorde, hier kan je dus een oplopende index gebruiken als key. Voor de vierde opgave kan je ook een eenvoudige key gebruiken, het getal dat in het vakje weergegeven wordt en het rijnummer.
Oefening 2: Calculator
Gebruik het startbestand exercise6.tsx en zorg er voor dat je bij het starten van het project het onderstaande te zien krijgt (de andere oefeningen zijn weggelaten in het screenshot).

Bouw de rekenmachine verder uit, hou er rekening mee dat elke druk op de knop zichtbaar moet zijn op het scherm. Je moet dus gebruik maken van state.

In het startbestand vind je een array die alle knoppen bevat die in de rekenmachine moeten verschijnen, in de juiste volgorde. Gebruik deze array om de 12 knoppen te renderen. Zorg er vervolgens voor dat de knoppen werken en de aangedrukte knoppen ook zichtbaar worden op het label. De "C" knop maakt het volledige scherm leeg en de "Del" knop verwijdert het laatst ingedrukte cijfer.
Oefening 3: BMI Calculator
Gebruik de startbestanden exercise7.tsx en range.css om onderstaand resultaat te bekomen (de andere oefeningen zijn weggelaten in het screenshot).

Bouw een component Slider, die vier properties heeft, value, min, max en changeHandler. Value geeft de huidige waarde van de slider, min de minimale en max de maximale waarde. De laatste property changeHandler bevat een functie die gebruikt wordt om de waarde van de slider aan te passen (in de bovenliggende component). Voor deze property kan je het type ChangeEventHandler<HTMLInputElement> gebruiken. De slider is een invoerelement van het type range als je hiermee niet bekend bent kan je op MDN meer info vinden. Gebruik deze component en de BmiLabel component die in de startbestanden aanwezig is om het volgende te realiseren. Kies zelf realistische minimum en maximum waarden.

Zorg er tenslotte voor dat de sliders automatisch bijgewerkt worden. Telkens de hoogte of het gewicht aangepast wordt moet ook de BMI aangepast worden. De formule om de BMI te berekenen vind je ook in het startbestand.
Oefening 4: Comments en lifting state
Breid oefening 5 van vorige les uit zodat de inhoud van de commentaar (CommentContent) maar voor een stuk zichtbaar is. Voeg daarachter een klikbare tekst 'read more' toe die de volledige tekst toont wanneer erop geklikt wordt. Als de tekst toegeklapt is, worden slechts de 30 eerste karakters getoond.
Zorg ervoor dat de tekst slechts voor één CommentCard tegelijkertijd uitgeklapt kan zijn.
Je kan gebruik maken van volgende opmaak (op een <button>) voor de 'read more'/'show less' tekst.
{
font-size: 110%;
text-decoration: underline;
color: #6b6b6f;
cursor: pointer;
margin: 0;
background: unset;
border: unset;
}
Oefening 5: Image carousel
Bouw een carousel component via composition, je hoeft in deze opgave nog geen gebruik te maken van context. We beperken ons in deze opgave tot een image carousel, kinderen die meer dan een afbeelding bevatten worden momenteel niet correct getoond omdat de CSS daarvoor niet voorzien is.
Voor deze oefening krijg je vier startbestanden, geen van deze bestanden moet aangepast worden. De component ControlButton wordt gebruikt om de pijlen om naar links of rechts te navigeren te bouwen. De component heeft één property $prev (het dollar teken is vereist voor styled-components) waarmee de positionering bepaald wordt, als $prev op true staat wordt de knop links gepositioneerd, anders rechts. Deze component is een gewone <button>, je kan hier dus rechtstreeks een onClick handler aan koppelen. Tenslotte kan je de HTML-entiteiten < en > gebruiken als vorige en volgende teken.
De component CarouselContainer bevat in de eerste versie van deze opgave geen speciale properties en moet gewoon rond de rest van de componenten geplaatst worden.
De andere startbestanden kunnen gebruikt worden om willekeurige afbeeldingen te generen om in de carousel te plaatsen, dit kan met de getRandomImage functie die in images.ts te vinden is.
Gebruik deze componenten om onderstaande structuur te bouwen, de blauw ingekleurde componenten worden doorgegeven via de children property. Je moet minstens 3 afbeeldingen gebruiken in je carousel.
Als de gebruiker op de vorige of volgende knop drukt, moet respectievelijk het vorige of volgende kind van de carousel getoond worden. Natuurlijk moet je hiervoor wel een bepaald kind kunnen selecteren uit de children array, dit kan via onderstaande code.
Begrip: Children als array
De kinderen van een componenten, doorgegeven via de children property, kunnen als volgt aangesproken worden als een array:
import {Children, FunctionComponent, PropsWithChildren} from 'react'
const ExampleComponent: FunctionComponent<PropsWithChildren> = ({children}) => {
const childrenArray = Children.toArray(children)
// Render enkel het eerste kind.
return childrenArray[0]
}Tenslotte moet het mogelijk zijn om circulair te blijven navigeren, als de gebruiker de laatste slide aan het bekijken is en op de volgende knop drukt, moet de eerste slide terug getoond worden. Als de gebruiker de eerste slide aan het bekijken is en op de vorige knop drukt, moet de laatste slide getoond worden. Onderstaande video toont het beoogde resultaat.
Hint
Als de afbeeldingen pas geladen worden op het moment dat deze zichtbaar moeten zijn in de carousel, zal de pagina verspringen omdat de afbeelding even geen afmetingen heeft (nog niet geladen). Je scherm zal naar boven verspringen omdat de scroll-zone kleiner is (de hoogte van de afbeelding telt even niet mee). Als de afbeelding geladen is, wordt de scroll-zone terug groter. Dit is niet ideaal, je kan onderstaande tsx fragment gebruiken om de afbeeldingen al in het geheugen te laden zodat de overgang aangenamer is voor de gebruiker.
<div style={{'display': 'none'}}>
{/* Hier komen de afbeeldingen. */}
</div>Optioneel Animaties
Deze opgave is relatief complex en moet niet verplicht gemaakt worden. Dat gezegd is het probleem hier meer de complexe logica en minder moeilijkheden specifiek te wijten aan React. Iedereen wordt aangemoedigd om de oefeningen te proberen.
De CarouselContainer component bevat de mogelijkheid om het wisselen tussen afbeeldingen te animeren. Er zijn twee CSS-klassen beschikbaar slideOutRight en slideOutLeft die gebruikt kunnen worden om de animaties te implementeren.
We onderscheiden twee mogelijke situaties. In onderstaande voorbeelden is de vetgedrukte component diegene die zichtbaar is. Je zal dus steeds 2 slides moeten renderen, waarvan er één verborgen wordt door de CSS in de animatie code. Je moet de slides renderen in de volgorde die hieronder beschreven wordt, het verbergen gebeurd automatisch.
- De gebruiker navigeert naar de volgende slide, in dit geval zien de gerenderde slides er als volgt uit: vorigeSlide volgendeSlide → slideOutLeft animatie → vorigeSlide volgendeSlide
- De gebruiker navigeert naar de vorige slide, in dit geval zien de gerenderde slides er als volgt uit: volgendeSlide vorigeSlide → slideOutRight animatie → volgendeSlide vorigeSlide
De animaties kunnen geactiveerd worden door de slideOutRight en slideOutLeft klassen toe te voegen aan de CarouselContainer component.
Hint
Wrap de vorigeSlide en volgendeSlide in een fragment. Dit zorgt ervoor dat de animaties gereset worden op elke render. Doe je dit niet, dan herkend React dat de afbeelding al eens gerenderd is, en werkt de animatie niet correct.
Het resultaat ziet er ongeveer als volgt uit.
Oefening 6: Tabs
Bouw een tabs component via composition. Gebruik context om bij te houden welk tabblad actief is.
In de startbestanden zijn enkele componenten te vinden die je kan gebruiken voor de styling. De TabButton en TabPanelContentContainer componenten hebben een property $isActive die gebruikt kan worden om de styling aan te passen naargelang de knop of het paneel al dan niet actief/zichtbaar zijn. Als deze property true is, is de component zichtbaar, anders niet.
Gebruik onderstaande structuur om de oefening uit te werken. De blauw ingekleurde componenten moeten opnieuw doorgegeven worden als kind van de bovenliggende component (via de children property). De structuur wordt getoond voor één tabblad, je zal dus meer Tab en TabPanel componenten moeten gebruiken dan in onderstaand diagram. De Tab component stelt een knop voor om naar een ander tabblad te navigeren terwijl de TabPanel component gebruikt wordt om de inhoud van dat tabblad te definiëren.
Oefening 7: Exercise uitbreiden
Breid de Exercise component uit zodat deze individueel open of dichtgeklapt kunnen worden, maar ook allemaal tegelijk op open of dicht gezet kunnen worden. Je mag voor deze opgave geen composition of context gebruiken. Alles moet dus van parent naar child doorgegeven worden via properties.
Begin met knop toe te voegen aan de Exercise component, zoals in onderstaand screenshot. Je kan de hiervoor de ChevronBtn component gebruiken die je in de startbestanden terugvindt. De knop moet nog niets doen.
Afhankelijk van de open/gesloten state, wordt de inhoud van de knop anders opgevuld. Gebruikt een chevron icoon van Lucide om aan te geven of de oefening open (chevron-up) of gesloten (chevron-down) is.


Voeg vervolgens bovenaan de pagina volgende twee knoppen toe, de eerst knop zal gebruikt worden om alle oefeningen dicht te klappen, de tweede om alle oefeningen open te klappen. Je kan hiervoor de OpenCloseBtn component gebruiken die je in de startbestanden vindt. De knoppen moeten nog niet werken.

Zorg er tenslotte voor dat alle knoppen die je hierboven toegevoegd hebt werken. Als je de pagina opent is alles standaard geopend. Hou rekening met volgende hints:
- Denk voor je begint met het implementeren goed na over de locatie van de state.
- Weet dat hooks zoals useState enkel in een component gebruikt kunnen worden.
- Arrays kunnen in de state bewaard worden
- Elk element in de array moet gekopieerd worden naar een nieuwe array bij het aanpassen van de state. Hiervoor kan je de spread operator gebruiken:
const new = [...old]
- Elk element in de array moet gekopieerd worden naar een nieuwe array bij het aanpassen van de state. Hiervoor kan je de spread operator gebruiken: