Ga naar hoofdinhoud

React Router

Twee soorten routering

Het proces waarbij de resource die bij een uniform resource locator (URL) hoort wordt opgezocht, heet routering. Vanuit het perspectief van de eindgebruiker is routering dus een vertaling van URL's in de browserbalk naar webpagina's. Er zijn twee vormen van routering, beide met hun eigen voor- en nadelen.

Server-side routering

De oudste (maar daarom niet "verouderde") vorm van routering is server-side routering. Hierbij wordt een HTTP request naar de server gestuurd, met daarin de te raadplegen URL. Het is dan de taak van de server deze URL te gebruiken om een bepaalde resource op te zoeken. Voor deze vorm van routering is niet noodzakelijk een specifiek framework vereist. Bovendien downloadt de gebruiker enkel de pagina's die hij/zij daadwerkelijk wil zien. Daar tegenover staat dat routering enkel mogelijk is zo lang de gebruiker verbonden blijft met het Internet. Ook kost het tijd om telkens een request te versturen en te wachten op een response.

Routering in single-page applicaties

Een nieuwere vorm van routering, die we aantreffen in single-page applicaties, is client-side routering. Bij gebruik van client-side routering beschikt de client vanaf het opstarten van de applicatie over meerdere (applicatie)pagina's. Gebruik van een link binnen de applicatie zorgt er dan niet voor dat er opnieuw een HTTP request plaatsvindt. In plaats daarvan wordt door de applicatie zelf bepaald welke (applicatie)pagina moet worden getoond. Hierbij wordt géén nieuw HTML-bestand gedownload.

Omdat client-side routering geen extra netwerkverkeer vereist, kan deze vorm ook toegepast worden wanneer de gebruiker niet verbonden is met het Internet. Zo lang de opgevraagde pagina's al in de applicatie aanwezig zijn, is er geen probleem. Bovendien kan deze vorm van routering responsiever aanvoelen, omdat het tijd kost een request te versturen en op een response te wachten.

React router

Om client-side routering toe te passen, moet de app zelf over de nodige logica beschikken om URL's om te zetten naar pagina's. Deze logica hoef je als applicatieprogrammeur zelf niet te verzinnen. In plaats daarvan kan je gebruik maken van de bibliotheek React Router. Deze ondersteunt alle gebruikelijke manieren om URL's te vertalen naar pagina's.

Installatie

Omdat React Router een externe bibliotheek is, moet je deze eerst installeren. Hiervoor gebruik je in de root directory van je React project volgend commando:

npm  install --save react-router-dom@6

Ondersteuning voor TypeScript is in deze versie ingebouwd, dus de types hoef je niet apart te installeren.

Op het moment van schrijven is de recentste versie van React Router versie 6.3.0. Nieuwere versies die ook beginnen met het cijfer 6 zouden zonder problemen moeten werken. Daarom eindigt bovenstaand commando op @6.

gevaar

Als versie 7 of hoger verschenen is op het moment dat je deze leerstof raadpleegt, moet je toch versie 6 gebruiken.

Voorbeeld

Om te zien hoe je React Router gebruikt, bekijken we een voorbeeld. Dit voorbeeld heeft een aantal pagina's en een gedeelde layout die op elke pagina getoond wordt.

import { Outlet, createBrowserRouter, RouterProvider, Route, NavLink } from "react-router-dom";
import styles from "./App.module.css";

const Root = () => {
return (
<div className={styles.container}>
<div className={styles.head}>Header</div>
<div className={styles.nav}>
<NavLink to="/" >Home</NavLink>
<NavLink to="page1">Page 1</NavLink>
<NavLink to="page2">Page 2</NavLink>
</div>
<div className={styles.content}>
<Outlet/>
</div>
<div className={styles.footer}>
Footer
</div>
</div>
);
}

const Home = () => {
return (
<div>This is the home page!</div>
);
}

const Page1 = () => {
return (
<div>Page 1</div>
);
}

const Page2 = () => {
return (
<div>Page 2</div>
);
}


const App = () => {
const router = createBrowserRouter([
{
path: "/",
element: <Root/>,
children: [
{
path: "",
element: <Home/>
},
{
path: "page1",
element: <Page1/>
},
{
path: "page2",
element: <Page2/>
}
]
}
]);

return (
<div>
<RouterProvider router={router} />
</div>
)
}

export default App;

Het eerst ding dat we moeten doen is een Browser Router aanmaken en onze eerste route configureren. Dit zal client-side routering mogelijk maken voor onze webapplicatie.

Dit doen we aan de hand van de createBrowserRouter functie. Deze functie heeft als argument een array van routes. Deze routes zijn van het type RouteObject. Deze RouteObject heeft een aantal properties:

  • path: de URL waarop de route moet reageren
  • element: het element dat moet worden gerenderd wanneer de route wordt geactiveerd
  • children: een array van RouteObject die de subroutes van de huidige route bevat
  • ...

Het object dat teruggegeven wordt door de createBrowserRouter functie moeten we meegeven aan de RouterProvider component.

Als je de code bekijkt, zie je dat we een Root component hebben gemaakt. Deze component bevat de basis structuur van onze webapplicatie. Deze structuur bestaat uit een header, een navigatiebalk, een content gedeelte en een footer. Deze component bevat ook de NavLink componenten. Deze componenten zorgen ervoor dat de gebruiker kan navigeren tussen de verschillende pagina's van onze webapplicatie.

Het Root component bevat ook een Outlet component. Dit is een component die de inhoud van de pagina zal renderen van de child route die geactiveerd is. Surft de gebruiker naar de "/" route, dan zal de Home component gerenderd worden. Surft de gebruiker naar de "/page1" route, dan zal de Page1 component gerenderd worden, enzovoort.

Er zijn twee verschillende manieren om links te maken in React Router. De eerste manier is door gebruik te maken van de Link component. Deze component heeft als enige property de to property. Deze property bevat de URL waar de gebruiker naar toe moet navigeren wanneer hij op de link klikt.

<Link to="/page1">Page 1</Link>

Een tweede manier om links te maken is door gebruik te maken van de NavLink component. Deze component heeft dezelfde properties als de Link component. Het voordeel hier is dat je de link kan stylen aan de hand van CSS. Er wordt altijd automatisch een active class toegevoegd aan de link wanneer de link geactiveerd is. Je kan die class gebruiken om de link te stylen.

.active {
color: red;
}

Je kan ook een functie meegeven aan de className property. Deze functie bevat een object met een isActive property. Deze property bevat een boolean die aangeeft of de link geactiveerd is of niet.

<NavLink className={({isActive}) => isActive ? styles.activeNavLink : styles.navLink} to="/">Home</NavLink>

Ontbrekende pagina's afhandelen

Als de gebruiker een URL gebruikt die niet naar een geldige pagina leidt, wordt er een algemene error pagina. Dat is niet gebruiksvriendelijk. Het is beter een pagina te voorzien die de gebruiker op de hoogte stelt dat het ingevoerde adres niet bestaat. Dit kan je doen door een route te voorzien met een wildcard te voorzien. Als je dan een component genaamd PageNotFound hebt, kan je de volgende route toevoegen:

{
path: "*",
element: <PageNotFound/>
}

Omdat deze route zo algemeen is, zal ze enkel matchen wanneer er geen betere match gevonden wordt.

URL parameters

Tot nu toe hebben we altijd routes gebruikt die exacte paden voorstellen. Soms wil je ook aan de hand van de url bepaalde parameters meegeven. Dat kan bijvoorbeeld een ID zijn: een uniek stukje informatie dat verwijst naar één specifieke user, één bestelling, één event op de kalender,... Als deze zaken voortdurend worden toegevoegd aan het systeem, is het niet mogelijk een route per user, per bestelling of per event te voorzien. Het is echter wel mogelijk een URL zoals /detail/:id toe te laten waarbij de :id een parameter is: een algemene plaatshouder in de URL waaraan een concrete waarde gegeven kan worden. Zo kan bijvoorbeeld /detail/1 of /detail/2 gebruikt worden, waarbij in beide gevallen dezelfde component wordt gerenderd, maar met andere data.

Om dit te laten werken, moet de applicatie de precieze waarde kunnen opvragen die de parameter heeft gekregen. Dit kan door middel van de useParams hook. Deze staat je toe de URL parameters op te vragen die gebruikt zijn om naar de huidige pagina te navigeren.

import { useParams,  Outlet, createBrowserRouter, RouterProvider, Route, NavLink,Link } from "react-router-dom";
import "./App.css";
const Home = () => {
return (
<div>
<ul>
<li><Link to="/detail/1">Detail 1</Link></li>
<li><Link to="/detail/2">Detail 2</Link></li>
<li><Link to="/detail/3">Detail 3</Link></li>
</ul>
</div>
);
}

const Detail = () => {
let { id } = useParams();

return (
<div>Detail {id}</div>
);
}


const App = () => {
const router = createBrowserRouter([
{
path: "/",
element: <Root/>,
children: [
{
path: "",
element: <Home/>
},
{
path: "detail/:id",
element: <Detail/>
}
]
}
]);

return (
<div>
<RouterProvider router={router} />
</div>
)
}

export default App;

Het object dat je terugkrijgt van useParams staat je toe een waarde op te zoeken voor om het even welke key. Er treedt dus geen compilatiefout op als je in TypeScript een parameter probeert te raadplegen die niet voorzien is. Je krijgt gewoon undefined terug.

useNavigate

De useNavigate hook is een hook die je toelaat te navigeren naar een andere pagina in je applicatie aan de hand van JavaScript code. Dit kan handig zijn als

const Home = () => {
const navigate = useNavigate();

return (
<div>
<button onClick={() => navigate("/detail/1")}>Detail 1</button>
<button onClick={() => navigate("/detail/2")}>Detail 2</button>
<button onClick={() => navigate("/detail/3")}>Detail 3</button>
</div>
);
}

# Meer weten
- https://reactrouter.com/en/6.4.3