Részletesebb ismerkedés a React-tel: Routing - 2. rész

Gömböcz Zsolt | 2022. 07. 08. 11:51 | Olvasási idő: 4 perc

Címkék: #git #hooks #JavaScript #npm #React #React JS #React Router #react-router-dom #Routing #SPA #Vendégblogger

Folytassuk React sorozatunkat és bővítsük tovább tudásunkat. Ebben a részben egy rövidebb lélegzetvételű témát hoztam, ami nem más mint a routing, azaz útvonal kezelés. Be szeretném mutatni az útvonalak regisztrálását és komponensek (views) hozzárendelését adott útvonalhoz, továbbá kitérünk a védett útvonalakra is.
react-routing

Új feature, új ág...

Kezdjük is a bejegyzést egy gyors git-ezéssel. Győződjünk meg arról, hogy a blog-1 ágon vagyunk (VSCode-ban bal alul mutatja az ág nevét, ha nem a megfelelőn lennénk, kattintsunk rá a nevére és felül az előugró menüből ki tudjuk választani a blog-1 ágat), mivel ebből fog készülni a blog-2 águnk. Hozzunk létre egy új ágat ennek a résznek, git branch blog-2 futtatásával és a git checkout blog-2 -vel vegyük használatba. Telepítsük a react-router-dom csomagot az npm i react-router-dom paranccsal és ellenőrizhetjük a git status-szal, hogy történt-e változtatás a blog-1-hez képest, azonban még a két ág követi egymást, így ha vissza megyünk a blog-1-re ott is látható a package.json és package-lock.json változása. Első commit-ig nem fog "szétválni" a két ág.

Győződjünk meg arról, hogy a blog-2 ágon vagyunk (git checkout blog-2), majd futtassuk a git add . parancsot és utána git commit -m "Router package installed" üzenettel lokálisan mentsük a változásokat. Most, ha visszaváltunk a blog-1 -re, akkor VSCode-ban láthatjuk, hogy a package.json nem tartalmazza a react-router-dom -ot, viszont a blog-2 igen. Ez volt a cél, most már folytathatjuk munkánk a blog-2-es ágon (a képeken a bal alsó sarokban is látszódik az ág elnevezése halványan).



Mi is az a React Router?

A React Router egy különálló könyvtár/csomag, amit React-hoz készítettek. Komponens alapú navigációt biztosít. Ez annyit jelent, hogy az URL címünk mindig szinkronban van az alkalmazással és a megfelelő komponenst jeleníti meg. Lehet úgy is használni, hogy egy útvonal egyetlen egy komponenst jelenít meg vagy bizonyos részei az oldalunknak (Navbar, Footer stb.) statikusak maradnak az útvonaltól függetlenül, és így mindig csak a tartalmi rész fog render-elődni. Négy komponenst fogunk használni (legtöbb esetben ez elég is a navigációhoz):

  •  <BrowserRouter /> : Biztosítja, hogy a UI összhangba legyen az URL-lel a HTML5-ös history API segítségével az egész alkalmazásunk köré rakjuk, hogy a többi navigációs komponenshez hozzáférjünk.
  • <Routes /> : E tag közé zárjuk útvonalainkat és itt fognak megjelenítésre kerülni.
  • <Route /> : Definiál egy útvonalat és a komponenst, amit megfog jeleníteni.
  • <Link /> : Navigáláshoz fogjuk használni, <a> tagként fog megjelenni, viszont frissítés nélkül fog URL-t változtatni.

Kezdjük is a BrowserRouter elhelyezésével, ami az alkalmazásunk belépési pontját ( App.js ) fogja körbe ölelni hogy az App.js-en belül elérjük a navigációhoz szükséges többi komponenst. Az src/index.js így fog kinézni:

Itt tehát a 6. sorban látható importálás került be újonnan, illetve a 11-13 sorokban látható BrowserRouter komponens. Nem javasoljuk, de aki kevesebbet szeretne gépelni menet közben, az majd a bejegyzés végén található Github commit-ben megtalálja a bejegyzésben érintett fájlokat és tartalmukat.

Az eddig megszokott módon készítsünk el három komponenst az src/components-ben és homenotfoundweather nevű mappában fogjuk tárolni őket. Így fog kinézni a mappa struktúránk:


A Weather.js és a Home.js most még csak egy-egy div-et fog tartalmazni és a későbbiekben fogjuk elkészíteni, ebben a bejegyzésben csak a navigációról lesz szó. Itt van a két komponens:


Azonban lesz egy komponensünk, amit megcsinálunk készre. Ez a statikus 404 oldal, ami akkor jelenik meg, ha nem regisztrált útvonalra lépünk, nézzük is a komponenst és a hozzá tartozó CSS fájlt:
src/components/notfound/NotFound.js:

import React from 'react'
import style from './NotFound.module.css'

const NotFound = () => {
  return (
    <div className={`${style.full__container} container mx-auto flex align-middle flex-col justify-center`}>
        <svg viewBox="0 0 48 48" className={'mx-auto text-center'} xmlns="http://www.w3.org/2000/svg" height="150" width="150">
          <path 
            stroke="#c6d3cc" 
            fill="#c6d3cc" 
            d="M24.05 24.45ZM2 42 24 4l22 38Zm20.7-11.4h3V19.4h-3Zm1.5 5.55q.65 0 1.075-.425.425-.425.425-1.075 0-.65-.425-1.075-.425-.425-1.075-.425-.65 0-1.075.425Q22.7 34 22.7 34.65q0 .65.425 1.075.425.425 1.075.425ZM7.2 39h33.6L24 10Z"
          />
        </svg>
        <h1 className={'inline text-center text-4xl'}>404</h1>
        <h4 className={'inline text-center'}>Page not found!</h4>
    </div>
  )
}

export default NotFound

src/components/notfound/NotFound.module.css:

.full__container {
	height: 100vh;
}

Most, hogy megvan mind a három szükséges komponensünk, ugorhatunk is az App.js fájlunkra, ahol a tényleges útvonalakat fogjuk definiálni és pár Tailwind class-ot is rakunk a fő tároló div-ünkre:

import Navbar from "./components/navbar/Navbar";
import { Routes, Route } from "react-router-dom";

import Home from "./components/home/Home";
import Weather from "./components/weather/Weather";
import NotFound from "./components/notfound/NotFound";

const App = () => {
  return (
    <div className={'flex-col flex h-screen'}>
      <Navbar />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/weather" element={<Weather />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </div>
  );
}

export default App;

A navigáció során megjelenített komponens elhelyezkedése függ a rá alkalmazott CSS szabálytól, de elsősorban attól, hogy hova rakjuk a Routes komponenst, mintha csak sima HTML-t írnánk. Nézzük is meg ( Terminal-ban: npm start ), mi történik, ha átnavigálunk a fentebb definiált útvonalakra, plusz próbáljunk ki egy nem létezőt:




Jól látható, hogy komponenseink megjelennek annak megfelelően, milyen útvonalra navigálunk, emellett a nem definiált útvonalra megkaptuk a 404-es komponensünket. Erre a magyarázat, hogy a <Routes> react-router-dom komponens úgy működik, mint egy switch (többszörös elágazás) break utasítás nélkül. Sorban halad át a keresendő (ebben az esetben: útvonal) érték egyes case eseteken (itt: Route) és ha egyezik, akkor ott megáll, és megjelenítésre kerül a komponensünk, jelen esetben a csillaggal jelölt útvonal a default eset.


Navigáljunk!

Most, hogy megvannak a komponenseink és a hozzájuk tartozó útvonalak, ideje a navigációs sávunkat is befejezni hogy tudjunk navigálni a gombokkal. A Navbar.js fájlban jelenleg a gombjaink placeholder linket tartalmaznak, alakítsuk át őket a React Router által biztosított Link komponensre, ami elvégzi az átnavigálást másik útvonalra, oldal frissítés nélkül megtörténik a DOM fára való felcsatlakozás (mounting), majd az előző komponens lecsatlakozik (unmounting), ezt a folyamatot DOM manipulációnak hívjuk. Így végzi a React Router a navigálást.

Lássuk is a Navbar.js fájlt:

import React from "react";
import style from "./Navbar.module.css";
import { Link } from "react-router-dom";

const Navbar = () => {
	return (
		<nav>
			<div className={`container flex mx-auto p-5 justify-between align-middle`}>
				<Link to="/">
					<div className={`text-3xl font-bold ${style.text__color} my-auto`}>
						Weather Project
					</div>
				</Link>
				<div className={`flex`}>
					<Link to="weather" className={`${style.button__weather} py-2 px-9 mr-10`}>
						Weather
					</Link>
					<a rel={"noreferrer"}
						href={"https://github.com/zsoltgombocz/react-weather-app"}
						target={"_blank"}
						className={`${style.button__github} py-2 px-9`}
					>
						GitHub
					</a>
				</div>
			</div>
		</nav>
	);
};

export default Navbar;

Az egyik <a> tag-et kicseréltük <Link> tag-re és a a to attribútumban adjuk meg az útvonalat, a GitHub gombunk pedig egy külső linket fog megnyitni, jelen esetben a repository-mat ehhez a projekhez. Továbbá a weboldal címét is körbe öleltem ezzel a <Link> tag-gel, így kattintásra a főoldalra fog navigálni. 


Bővítsük tudásunkat: védett útvonalak

Most, hogy van egy alap navigációnk, próbáljunk meg egy picit tovább lépni és egy másik fontos témát is említeni. Mivel majdnem minden alkalmazás rendelkezik felhasználó kezeléssel és ezáltal a védett útvonalak szerephez jutnak. De mi is az a védett útvonal? Olyan útvonalak, amiket valamilyen authentikáció (azonosítás) után tudunk használatba venni, például belépés után. Nekünk nem lesz ilyen oldalunk, azonban egy megoldást mutatok, hogy milyen útvonalon érdemes elindulni. Hozzunk létre egy komponenst az eddig megszokott módon, az src/components mappában protected/Protected.js néven.

import React from "react";
import { Navigate } from "react-router-dom";

const Protected = ({ children, isLogged }) => {
	if (!isLogged) return <Navigate to="/" replace />;
	else return children;
};

export default Protected;

Komponens props: 

  • isLogged: ha igaz, akkor megjelenítjük a védett tartalmat.
  • children: egy automatikusan beszúrt (injektált) adat. Ha ezt, mint paramétert feltüntetjük, akkor megkapjuk a komponensünk által körbeölelt tartalmat.

Ez a komponens egy middleware-ként fog funkcionálni, a kapott állapottól függően fogunk megjelenteni tartalmat. Ha hamis, akkor egy React Router-beli Navigate komponens fog minket visszairányítani. Ha igaz, akkor pedig a children kapott paramétert adjuk vissza (jelenítjük meg). A replace attribute a "/protected" útvonal helyett a "/" fogja a navigációs stack-be (kupacba) rakni, így nem kerül mentésre az útvonal, ezáltal visszanavigáláskor (böngészős natív vissza gomb) nem a védett útvonalunkra fog navigálni. 

Ez a megoldás mód egy a sok közül, lehetne akár már magát az útvonal definiálást is állapothoz kötni, és akkor nem is létezne az útvonal amíg nem lépünk rá. Az állapot vizsgálat elvégezhető egy useEffect hook-ban komponens mount-oláskor, szóval nincs kötelezően használandó definiálási mód.

Adjunk hozzá egy új útvonalat projektünkhöz (src/App.js) és egy state segítségével hozzunk létre egy "logged" állapotot ami reprezentálni fogja a belépésünk állapotát:

import Navbar from "./components/navbar/Navbar";
import { Routes, Route } from "react-router-dom";
import { useState } from "react";

import Home from "./components/home/Home";
import Weather from "./components/weather/Weather";
import NotFound from "./components/notfound/NotFound";
import Protected from "./components/protected/Protected";

const App = () => {
  const [logged] = useState(true); // Change to test protected route
  return (
    <div className={'flex-col flex h-screen'}>
      <Navbar />

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/weather" element={<Weather />} />
        <Route
					path="/protected"
					element={
						<Protected isLogged={logged}>
							<div>Sensitive data</div>
						</Protected>
					}
				/>
        <Route path="*" element={<NotFound />} />
      </Routes>
    </div>
  );
}

export default App;

Megjegyzés: a forráskódban számoztam az új, most releváns sorokat (más a háttérszínük is).

Az útvonal definícióban megadott megjelenítendő tartalom (element prop) az előbb létrehozott komponensünk, ami el fogja végezni a state vizsgálatot, és a körbeölelt <div> tag-et fogja megjeleníteni. Nézzük is meg false és true állapotban (useState kezdőértékét változtatjuk a 11. sorban):

FALSE:


TRUE:

Azt hiszem, hogy az iménti mozgóképek jól mutatják a tesztelést, de próbáljuk ki mi magunk is!


Mentés és összefésülés (merge)

Először futtassuk a git add . parancsot majd ellenőrizzük, hogy minden módosított fájlunk szerepel a git status -szal listázott fájlok között:


Nem kötelező de én megváltoztattam a szöveg színét egy picit, ezért van ott a .css fájl a staging area-ban. Itt a változtatás:

Commit-oljuk a git commit -m "React router and basic routing added" paranccsal változtatásainkat, majd távoli repository-nkba push-oljuk a git push -u origin blog-2 futtatásával.

Menjünk át a master ágra ( git checkout master ), majd git merge blog-2 -vel adjuk hozzá a blog-2 ( vagy amiben dolgoztunk ág ) tartalmát. Ezután git push -u origin master és már fent is van GitHub-on a master águnkban az eddigi projekt, és külön-külön megtalálható a bejegyzésekhez tartozó ág.


Összegzés

A projektünkhöz sikeresen adtunk hozzá útvonalakat, megismerkedtünk a React Router-rel, mint fő útvonalkezelő könyvtárral, és megnéztünk egy példát a védett útvonalakra.  A következő részben megismerkedünk egy "deployment platform"-mal (nem Heroku) és input mezők kezelésével fogunk továbbhaladni, majd a megszerzett tudással bővítjük oldalunkat. Ha esetleg valakinek kérdése, meglátása, ötlete van a bejegyzéssel kapcsolatban, itt tud kapcsolatba lépni velünk: Kapcsolat

A bejegyzést lektorálta, a programkódokat kipróbálta: Gludovátz Attila (az ő Github projektje itt érhető el).

Előző rész: A projektünk alapjai - 1. rész

Következő rész: Deployment - 3. rész