4. Fetching data
4. Fetching data
Tijdens deze oefeningenreeks bouw je een applicatie waarmee je de meest populaire en recente artikels op Hacker News kan bekijken. Je oefent op:
- De concepten van de eerste 3 lessen
- Data ophalen via Fetch
- Het gebruik van TanStack Query
- Het lezen van API-documentatie
- Het gebruik van Suspense & ErrorBoundary
Voor deze oefening begin je vanaf een nieuw, leeg project. Download onderstaande startbestanden alvast en plaats deze in het nieuwe project.
Startbestanden
Oefening 1: API-code
Schrijf, met behulp van Fetch, twee functies. De eerste functie, getItemIds, moet de ID's van de meest populaire stories, ask stories, show stories of job stories ophalen. Hiervoor kan je onderstaande URL's gebruiken:
- https://hacker-news.firebaseio.com/v0/topstories.json
- https://hacker-news.firebaseio.com/v0/askstories.json
- https://hacker-news.firebaseio.com/v0/showstories.json
- https://hacker-news.firebaseio.com/v0/jobstories.json
Elke van bovenstaande URL's geeft een array van ID's terug. Schrijf één functie die alle 4 van bovenstaande URL's kan aanspreken.
De tweede functie, getItem, haalt één item op, op basis van een ID, ga in de documentatie op zoek naar de juiste URL en informatie over de structuur van de data die je terugkrijgt.
Schrijf tenslotte twee custom hooks, useGetItemIds en useGetItem die via TanStack Query respectievelijk gebruik maken van de eerste en tweede functie die je hierboven geschreven hebt.
Oefening 2: Settings
Maak een nieuwe context aan. Deze context bevat twee variabelen theme en refetchInterval en methodes om deze variabelen aan te passen.
De variabele theme geeft het thema aan dat de gebruiker gekozen heeft, er zijn twee opties light en dark die elk als een CSS-klasse gebruikt kunnen worden. Zorg ervoor dat het gekozen thema toegevoegd wordt aan het root element van de applicatie (window.document.documentElement). Het theme moet tenslotte ook persistent gemaakt worden via de local storage.
De variabele refetchInterval geeft aan om de hoeveel tijd TanStack Query de data moet verversen. Voorzie een provider en zorg er vervolgens voor dat het refetchInterval gebruikt wordt in de useGetItemIds en useGetItem hooks.
Oefening 3: Tabs
Maak gebruik van de Tabs component om onderstaand menu na te bouwen.
Hieronder vind je een array die gebruikt kan worden om de tabs op te bouwen in een lus.
const endpoints: Endpoint[] = [
{
id: '6c960eec-ba60-4972-9371-0c6aadaba42a',
title: 'Top Stories',
endpoint: '/topstories.json',
},
{
id: '584e359a-6869-4539-b745-1996343d8454',
title: 'Ask Hacker News',
endpoint: '/askstories.json',
},
{
id: '7679b515-b006-455e-a527-bfd94e3e29d0',
title: 'Show Hacker News',
endpoint: '/showstories.json',
},
{
id: '0ae30862-6f70-421f-8385-445e5d172018',
title: 'Jobs',
endpoint: '/jobstories.json',
},
]
Oefening 4: Settings pagina
Als je op het settings tab drukt, moet de Settings component zichtbaar worden. Deze component kan gebruikt worden om de waarden in de context aan te passen. Als je de dark/light toggle aanpast, moet je onmiddellijk het resultaat zien.


Oefening 5: Top items
Schrijf een component TopItems, deze component wordt in elk van de 4 overige tabs getoond. De component krijgt één property mee, het endpoint dat gebruikt moet worden om de stories op te halen voor het geselecteerde tabblad. Deze component wordt omringd door Suspense en toont de LoadingPage component die beschikbaar is in de startbestanden als fallback.

Binnen de TopItems component worden de ID's opgehaald van de stories in de geselecteerde categorie. Vervolgens worden de ID's gebruikt om de details op te halen voor de eerste 10 stories in deze lijst. De stories zien er als volgt uit:

De titel is een link naar de website waarover het story gaat (als deze er is) en de discuss link verwijst door naar een detailpagina voor het story. Beide krijgen de CSS-klasse hover:underline om aan te duiden dat het om een link gaat. De Item component is gebouwd met de Card component uit shadcn/ui.
Optioneel Oefening 5.1: Infinite scroll
Onder de stories wordt een knop getoond waarmee de volgende 10 stories ingeladen kunnen worden. De vorige stories blijven zichtbaar. Terwijl de nieuwe stories aan het laden zijn, wordt een loading animation getoond, deze is gebouwd met de shadcn/ui Skeleton component. Je hebt hier dus opnieuw suspense nodig.

Onderstaande video demonstreert de volledige werking van de stories pagina.
Oefening 6: Details pagina
De detailpagina kan geopend worden als de gebruiker op de discuss knop drukt. Tijdens het laden van de applicatie wordt opnieuw de LoadingPage component getoond en wordt er opnieuw gebruik gemaakt van de useGetItem hook.
Zodra de data geladen is, wordt de titel van het story getoond, voor de titel staat een pijl waarmee terug naar de hoofdpagina genavigeerd kan worden. Daarnaast wordt ook de tekst van het story getoond (als deze er is). Deze tekst is toegevoegd in de API-response als HTML-code, dit betekent dat je gebruik moet maken van de dangerouslySetInnerHTML property die op elk HTMLElement beschikbaar is in JSX. Deze property werkt als volgt:
const SomeComponent: FunctionComponent = () => {
return (
<>
<div dangerouslySetInnerHTML={ {__html: `<p>Some HTML code</p>`} }/>
</>
)
}
Oefening 6.1: Commentaar
Elk item dat op de detailpagina weergegeven wordt heeft een property (zie docs) die een lijst van ID's van de commentaren bevat. Deze ID's kunnen gebruikt worden om via de useGetItem hook de commentaren op te halen. Zet onderstaande code rond de tekst in deze commentaren.
<div className="ml-1.5 border border-b-accent mb-1 w-[95%] p-2">
</div>De niveau-1-commentaren worden opgehaald voordat de pagina getoond wordt. Elke commentaar kan individueel open en dichtgeklapt worden, je kan gebruik maken van de HTML-entiteiten ∧ (∧) en ∨ (∨) om aan te geven of de commentaar open of dichtgeklapt is.
Om de datum van de commentaar correct weer te geven, moet de unix timestamp (in seconden) die je van de API terugkrijgt geconverteerd worden naar een JavaScript timestamp (in milliseconden). De JavaScript timestamp kan meegegeven worden als argument aan de Date klasse (new Date(timestamp)). Tenslotte kan je de toLocaleString() gebruiken om een goede representatie te krijgen (op basis van de locale-instellingen in de browser).

Oefening 6.2: Geneste commentaren
Op een commentaar kunnen eventueel ook commentaren geplaatst worden. Als er geneste commentaren zijn, moet een knop Show comments... getoond worden. Zodra de gebruiker op deze knop drukt, wordt deze verborgen en worden de geneste commentaren via suspense getoond. Terwijl deze aan het laden zijn, wordt de LoadingPart component getoond, die beschikbaar is in de startbestanden.
De geneste commentaren maken opnieuw gebruik van dezelfde component die je al gebruikt hebt voor de niveau-1-componenten. Deze werkwijze zet zich voort tot er geen diepere commentaren meer zijn.