Les 3: Single Page Applications

Sebastiaan HenauOngeveer 6 minuten

Les 3: Single Page Applications

Tijdens deze oefensessie bouw je een applicatie waarmee je de hoofdsteden van verschillende landen kan inoefenen. Tijdens deze oefeningenreeks oefen je op:

  • De concepten uit les 1 & 2
  • Routing via react-router
  • Het gebruik van third-party UI libraries
  • Het raadplegen van documentatie om relevante informatie te vinden

Voorbereiding

Maak een nieuw project aan en zorg ervoor dat alle nodige pakketten geïnstalleerd zijn. Je moet tijdens deze oefeningenreeks gebruik maken van routing en een layout bouwen met behulp van Bootstrapopen in new window en react-bootstrapopen in new window. Voeg daarnaast de startcodeopen in new window toe aan je project. Deze code gebruikt onderstaande bibliotheken, installeer deze dus ook.

pnpm add countries-capitals
pnpm add @faker-js/faker
pnpm add uuid 
pnpm add -D @types/uuid

Info

De countries-capitals bibliotheek is niet ideal, voor verschillende hoofdsteden wordt er een foute naam getoond, aangezien dit aan de bibliotheek ligt en het doel is om React onder de knie te krijgen, mag je dit negeren.

Verwijder, net zoals tijdens de eerste oefensessie, alle inhoud uit de src map. Je begint van een leeg project. Maak alvast een nieuwe main.jsx aan.

De applicatie bevat 3 pagina’s. Maak voor elke pagina een nieuwe component. De pagina’s zijn: Home, Game, Highscores.

Routing

Voorzie routing voor alle pagina's en subpagina's, gebruikt onderstaande componentenboom om je routing op te bouwen. De tekst bij de pijlen geeft het pad aan (relatief ten opzichte van de parent). De groene kaders duiden componenten/pagina's (er is geen verschil tussen de twee) aan. Als er een / in het pad staat dan is het gedeelte na de slash een navigatieparameter.

Oefening 1: Home Page

Bouw onderstaande pagina na. Zorg dat de links werken en dat de routing geïmplementeerd wordt via React Router.

De knoppen in de twee kolommen verwijzen naar dezelfde plaats als de links in de navigatiebalk. De kolom layout is gemaakt met behulp van het React Bootstrap Grid Systeemopen in new window.

Bouw de routes zo op, dat de bijhorende componenten getoond worden als je op de links klikt (voorlopig zijn deze nog leeg).

Figuur 1: Home Page

Oefening 2: Game Page

Gebruik tijdens het uitwerken van deze pagina en bijhorende subpagina's de capitalsAPI.ts en highscoresAPI.ts bestanden. In deze bestanden vind je een aantal functies met commentaar, je zult dus zelf op zoek moeten gaan naar de gepaste functies om een bepaalde functionaliteit uit te werken.

Game component

Begin met de component Game uit te bouwen. Deze component toont altijd de titel "Play the game!" en een tekst die weergeeft of er al dan niet een regio geselecteerd is en in het geval er een regio gekozen is, wat deze regio is. De gekozen wordt uitgelezen uit de navigatieparameters. Vervolgens wordt de inhoud van de kind-routes van het pad /game getoond, dit kan ChooseRegion of Play zijn. Zorgt dat je de juist component rendert met één lijn code in de Game component. Hieronder de tekst die in beide situaties weergegeven moet worden.

  • You've chosen to practice the capitals in REGION, click here to choose another region.
  • Please select a region before playing the game.

Als er nog geen regio geselecteerd is, redirect de Game component automatisch naar het pad /game/region, anders wordt er automatisch geredirect naar het pad /game/play/:chosenRegion. Let op, het is hier heel eenvoudig om een oneindige redirect cycle te creëren. Via de useLocationopen in new window hook kan je dit probleem oplossen. Ga zelf op zoek in de documentatie naar de gepaste manier om deze hook te gebruiken.

Regio kiezen

De ChooseRegion component toont, aan de hand van de react-bootstrap ListGroupopen in new window component, een overzicht van alle beschikbare regio's. Als de gebruiker een regio aanduidt, wordt er automatisch geredirect naar het /game/play/[geselecteerde regio hier] pad.

Figuur 2: Regio selectie

Game menu

De Play component bevat twee delen, de configuratie en de vragen/het resultaat. We beginnen met de configuratie uit te bouwen.

Standaard begint een spel met 5 vragen, voor de meeste regio's zijn er echter meer dan 5 landen beschikbaar (regio's met minder dan 5 beschikbare landen worden in de API weggefilterd). De gebruiker kan ervoor kiezen om het aantal vragen te verhogen of te verlagen. Dit kan op twee manieren, ten eerste kan gebruik gemaakt worden van een + en - knop, ten tweede kan de hoeveelheid aangepast worden door middel van een FormRangeopen in new window component. Het is vanzelfsprekend dat de range input en de knoppen gesynchroniseerd worden en dat het onmogelijk is om meer dan het beschikbare aantal of minder dan 5 vragen te selecteren. Voor gebieden waar er exact 5 landen beschikbaar zijn, wordt dit gedeelte niet getoond. Je kan dit testen door de regio "Australia and New Zealand" te selecteren.

Figuur 3: Configuratie

Verder is er ook een knop aanwezig waarmee het spel opnieuw gestart kan worden. Voordat op deze knop gedrukt is, blijft de vorige hoeveelheid vragen staan in het volgende deel van de oefening. De knop kan altijd ingedrukt worden, je hoeft geen controles toe te voegen die garanderen dat er geen spel bezig is.

Om bovenstaande layout te bouwen is gebruik gemaakt van de Bootstrap text utilitiesopen in new window en van het Bootstrap grid systeem.

Spel spelen

Bouw een component Question die gebruikt kan worden om een vraag weer te geven. Naast de huidige vraag wordt ook de vooruitgang getoond (vraag huidigeVraag/totaalAantalVragen) en de huidige score (aantal correct beantwoorde vragen in dit spel).

Zodra er een antwoord geselecteerd is wordt de volgende vraag getoond en wordt de vooruitgang en eventueel de score aangepast.

De layout in onderstaand screenshot is gebouwd met behulp van de Cardopen in new window componenten uit react-bootstrap.

Figuur 4: Het spel spelen

Highscores bewaren

Als het spel afgelopen is wordt de Result component getoond, deze component toont je score en een formulier waarmee je de highscore kan bewaren. De naam in het formulier is standaard ingesteld op de laatst gebruikte naam (op te halen via de API-code). De knop "Add to highscores!" is gedeactiveerd zolang het formulier leeg is.

Figuur 5: Highscore toevoegen

Nadat een highscore toegevoegd is, zie je onderstaand scherm, de link verwijst naar '/highscores/[geselecteerde locatie]'.

Figuur 6: Highscore toegevoegd

Onderstaande video demonstreert de volledige werking van deze oefening.

Figuur 7: Game pagina

Oefening 3: Highscore Page

De highscore pagina kan op twee manieren bezocht worden:

  1. De gebruiker drukt op de link nadat een highscore bewaard is. In dat geval wordt de gebruiker naar /highscores/:chosenRegion gebracht. Deze URL bevat een parameter, deze parameter wordt gebruikt om automatisch de regio te selecteren waarin de gebruiker zijn laatste spel gespeeld heeft.

  2. De gebruiker drukt op de Highscores link in de navigatiebalk. Ik dit geval wordt de gebruiker naar /highscores gebracht. Er is geen parameter aanwezig in de URL en dus moet de gebruiker eerst een regio selecteren voordat er highscores zichtbaar zijn.

Beide links brengen de gebruiker naar dezelfde pagina. De Highscore component heeft twee verschillende layouts:

  1. Voor de breakpoints x-small en small wordt een accordion gebruikt.

  2. Voor alle andere breakpoints wordt een 2-kolom layout gebruikt, gebouwd met het grid-systeem.

Beide layouts worden hieronder besproken, het moet mogelijk zijn om te wisselen tussen de twee layouts, door de grootte van het venster aan te passen, zonder dat de geselecteerde regio verloren gaat. Beide layouts duiden de naam aan die het laatst gebruikt is om een highscore toe te voegen.

Om de zichtbaarheid van de layouts te bepalen maak je gebruik van de Bootstrap display utilitiesopen in new window.

Layout 1: Accordion

Om deze layout te bouwen maak je gebruik van de react-bootstrap Accordionopen in new window component. Er kan slechts één sectie van de accordion per keer open zijn.

Standaar blijft het geselecteerde item in focus, om dit op te lossen kan je in je event handler een check toevoegen die controleert of het target van het event (i.e. het ding dat het event afgevuurd heeft), een element is. Vervolgens kan je de blur methode gebruiken om de focus te verbergen. Je kan hier geen gebruik maken van de currentTarget property zoals in les 2 omdat dit verwijst naar het element waarom wij de handler gedefinieerd hebben, het AccordionItem en niet de knop die gefocust is.

const selectAccordionItem = (evt: MouseEvent<HTMLElement>): void => {
    if (evt.target instanceof HTMLElement) {
        evt.target.blur()
    }
}
Figuur 8: Accordion view

Layout 2: Twee kolommen

De tweede layout bevat twee kolommen, gebouwd met react bootstrap. De eerste kolom 4/12 van de beschikbare ruimte in beslag, de andere kolom 8/12.

Door beide kolommen kan individueel gescrold worden. Om dit te implementeren moet de lengte van een kolom vast ingesteld worden. In dit geval zetten we de hoogte van een kolom vast op 80% van de viewport hoogte. Gebruik hiervoor styled-components en onderstaande CSS-regels. Ga in de documentatieopen in new window op zoek hoe je deze CSS kan toepassen op een react-bootstrap Col component.

max-height: 80vh !important;
overflow: scroll;

Om toegang te krijgen tot autocompletion is het handig om aan te geven dat de nieuw, styled kolom, dezelfde properties heeft als de Col component die uit react-bootstrap. Dit kans als volgt.

const StyledCol: typeof Col = ZELF VERDER AAN TE VULLEN
Figuur 9: Column view

Onderstaande video demonstreert de volledige werking van de highscores pagina.

Figuur 10: Highscores pagina
Laatst geüpdate:
Bijdragers: Sebastiaan Henau