Egy Helló Világ alkalmazásnál komolyabb dolgot is lehet a React-el csinálni. Arra gondoltam, hogy egy ReactJS gyorstalpaló keretein belül, lépésről – lépésre, akarom mondani posztról – posztra csinálunk egy Webalkalmazást.
Van hozzá videó anyag és GitHub repó is, de vannak aki jobban szeretik olvasva. Néha én is. Úgyhogy akkor induljon, vagyis inkább folytatódjon a gyakorlatias gyorstalpaló.
Mit fog csinálni a webalkalmazás? Nos… A Terminátor című mozifilmből ismert Skynet, többfajta Terminátort is legyártott. Több prototípussal is megörvendeztette a nagyközönséget, aminek az emberek nem nagyon örültek.
A lényeg, hogy a mi alkalmazásunk azt fogja csinálni, hogy kártyaszerűen megjelenít az oldalon Terminátorokat: képpel, model névvel, szériaszámmal. Lesz hozzá egy kereső is, amivel a Terminátor modellek között lehet keresni.
Pontosan ezt fogjuk megcsinálni: https://laszlovarga78.github.io/cyberdyne-models/
Ha nem működik a link, kérlek jelezd nekem!
Projekt indítás
Először is minden, ami felesleges azt érdemes törölni a projetből, amit annak idején az npx parancs hozott létre. Nem kell már többé az előbbi Hello komponens és az összes hozzá tartozó fájl sem. Vagyis egy laza mozdulattal kukába dobjuk az eddig fáradtságos munkával összeeszkábált komponenst.
Jól lecsupaszítottuk az alkalmazást és mivel a Hello komponenst is töröltük, most egy jó kis “Failed to compile” hibával nézhetünk szembe a VS Code terminálban.
A Hello komponens hívásokat kicserélhetjük az index.js-ben. Legyen Terminator, mivel terminátorokat fogunk megjeleníteni.
Hozzuk is létre a komponensnek a fájlt. Úgy gondolom, hogy egy logikus mappaszerkezet sokat segíthet az egész projekt áttekinthetőségében és karbantarthatóságában. Ezért én az src mappában csinálok egy components mappát kifejezetten a komponensek tárolására. És ezen belül egy terminator mappát a terminátor komponensnek. A terminator mappán belül hozom létre a terminator.component.jsx fájlt. Ez fogja tárolni a terminator komponens kódját. És igen, .jsx a kiterjesztése, ami ajánlások szerint a komponens fájlok kiterjesztése. És bár a component szó nem is kellene a fájlnévbe, én ott hagyom, mert így szoktam meg.
Így néz ki nálam a mappaszerkezet:

És az index.js:
import Terminator from "./components/terminator/terminator.component";
ReactDOM.render(<Terminator />, document.getElementById("root"));
A Terminator komponens
Ahogyan annak idején a Hello komponensnél is tettem, elkészítem a Terminator komponens logikáját, a terminator.component.jsx fájlban.
Az első sor az elmaradhatatlan import, amivel a react könyvtárat berántom. Aztán a komponenst most függvény alapúra gyártom, és egyelőre nincsen paramétere. A nyíl függvény törzsében egy return adja vissza a komponens “HTML” kódját, ami ugye nem html, hanem jsx.
Végül exportálom a komponenst, hogy tudjam azt használni más fájlokban:
import React from 'react';
const Terminator = () => {
return(
<div>
<img src='' alt='terminator' />
<div>
<h2>T101</h2>
<p>SN: 3242343244</p>
</div>
</div>
);
}
export default Terminator;
Ha még nem tettem volna meg, akkor a VSCode-ban nyitok egy terminált és kiadom az npm start parancsot, hogy elinduljon a kis webszerverem és a böngészőben megnyíljon a webalkalmazás.
Ha a böngészőt megnézem, akkor most ezt látom:

Fuhúú a komponens meg is jelent ahogy vártam. Amit viszont nem vártam az a Chrome konzolban egy hibaüzenet, ami a törölt manifest.json fájl miatt van: Manifest: Line: 1, column: 1, Syntax error.
A legegyszerűbb megoldása ennek a kijavítására, ha az index.html-ből törlöm a következő sort:
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
Kép hozzáadása a komponenshez
Következő lépésként szerezni kellene valami képet is a terminátorhoz. Mivel gondosan ügyelek arra, hogy mindenféle szerzői jogot tiszteletben tartsak, én most egy ingyenes API-t fogok használni. Hogy mi az API, arról majd később szó lesz. Az API tehát, amit használni fogok, az a robohash.org. Innét lehet legálisan használni robot képeket. Ha például beírok a böngészőbe a weboldal címe után egy per jelet aztán valami teljesen véletlen szöveget, akkor kapok egy képet. Tehát mondjuk erre gondolok: https://robohash.org/kjhkjhj

Ha másik random szöveget írok be, akkor más képet kapok. Én végül ezt a linket állítottam be a képhez az src attribútumban: https://robohash.org/t?size=180×180
A “t” miatt szintén valami véletlen képet fogok kapni, de most pontosan ez a cél. Utána egy kérdőjelet követően megadom a kép méreteit a size-zal:
<img src='https://robohash.org/t?size=180x180' alt='terminator' />
Ismét megnézem a böngészőben, hogy a módosítások után mi a helyzet. Nálam a következőképpen néz ki:

Komponens csinosítása (css, dizájn)
Eddig elég gázul fest szegény terminátor, mert nincsen rajta semmi dizájn. De, hát a filmben is meztelenül érkeztek meg a jövőből, szóval nincs itt gond. 🙂
De akkor öltöztessük fel. Szinte adja magát, hogy egy kicsit CSS-ezzünk. Persze van aki kevésbé ért a stíluslapokhoz, vagy éppen nincs sok kedve velük bíbelődni. Szerencsére a React mindent is ad nekünk. Szóval azok, akik utálnak pepecselni a CSS-el, azok imádni fogják a tachyons csomagot.
Ez a csomag a https://tachyons.io/ weboldalon érhető el és ott meg is találjuk az okosságot arra, hogyan kell telepíteni. Nyissunk egy parancssort a react alkalmazásunk főkönyvtárából, vagy a VSCode-on belül CTRL+C-vel szakítsuk meg a Webszerver futását és adjuk ki a következő parancsot: npm install tachyons@4.12.0
Miután települt ez a css toolkit, a package.json fájlba belekukkantva ellenőrzöm, hogy oda is bekerült a függőségek (dependencies) közé, használatra kész:
"tachyons": "^4.12.0",
Használathoz a tachyons csomagot beimportálom az index.js fájlba:
import 'tachyons';
Nos, innentől kezdve képesek vagyunk használni a tachyons csomag által előre definiált stílusokat, css osztályokat, a css osztálynevek megadásával. Ezen a linken található, hogy milyen css osztályok közül lehet választani: https://tachyons.io/#style
Először is klónozok pár terminátort, azaz csinálok több komponenst is, hogy látványosabb legyen a dolog:
ReactDOM.render(
<div>
<Terminator />
<Terminator />
<Terminator />
</div>,
document.getElementById("root")
);
Azt már most látni a weboldalon, hogy 3 komponens lett, baromi gyenge formázással:



Éppen ezért kell felturbózni a kinézetet. A Terminator komponensben adok egy className attribútumot a külső div-nek és elhelyezem benne ezeket a következő, előre definiált osztály neveket:
<div className='bg-light-blue dib br3 pa3 ma2 grow'>
A bg-light-blue a világoskék hátteret csinálja, a dib a display: inline-block beállításnak felel meg. A br3-al a border-radiust adtam meg. Jött a padding és a margin: pa3, ma2. És végül a grow az animációhoz, amikor az egeret a komponens fölé viszem.
A böngészőben már egy barátibb megjelenés fogad:

Egész emberi formája lett a komponenseknek, akarom mondani most már dizájnosabbak a kis terminátorok.
„Egyéniség” kialakítása
Ez a játékos szóhasználat nem arra utal, hogy mesterséges intelligenciát adok a terminátoroknak, hanem ahogyan most is látszik, minden robot képe, model neve és szériaszáma ugyanaz. Csináltam ehhez egy models.js-t az src mappán belül. Ez egy tömb, ami objektumokat tartalmaz:
export const models = [
{
id: 1,
name: "T-X",
serialNumber: 1876786712,
},
{
id: 2,
name: "Rev-9",
serialNumber: 1879878111,
},
Egy models változóba tettem a tömböt és egy objektum úgy néz ki, hogy van egy id, name és serialNumber tulajdonsága. Ezek az adatok minden modelnél mások és mások.
Figyeld meg, hogy azonnal exportáltam is a tömböt azzal, hogy az export szót a változó deklaráció legelejére helyeztem.
Jöhet az import az index.js fájlban:
import models from './models.js';
A VSCode terminálban jogosan látom azt az üzenetet, hogy ‘models’ is defined but never used.
Ez figyelmeztet arra, hogy ha van valami a kódban, ami esetleg felesleges, nincsen használatban.
Ha már a felesleges használatról esett szó, akkor egy pici kitérőt szeretnék tenni. Egy fájlon belül több exportálandó cucc is lehet. Tegyük fel, hogy a models.js-ben lenne még vagy 60 akármi, amit exportálni akarok. Azonban ahogy most is, az index.js-ben, én csak és kizárólag a models tömböt akarom használni, a többi nem érdekel. Ilyen esetben megegyezés szerint csak azt a cuccot veszem ki a fájlból az import során, amit használni is akarok. Ezt a destruktúrálással tehetem meg: ugyanazt a változó nevet használom az import során, amit az exportnál is megadtam és az importnál a változó nevet kapcsos zárójelbe teszem:
import { models } from './models.js';
Ha még lenne más is, amit importálni akarnék a models.js-ből, akkor azt így tehetném meg:
import { models, akarmi } from './models.js';
Most már, hogy az index.js fájlban rendelkezésemre áll a models tömb, simán ki tudom belőle szedni azt, amit akarok:
<Terminator id={models[0].id} name={models[0].name} serialNumber={models[0].serialNumber} />
<Terminator id={models[1].id} name={models[1].name} serialNumber={models[1].serialNumber} />
<Terminator id={models[2].id} name={models[2].name} serialNumber={models[2].serialNumber} />
Ebből világosan látszik két dolog: az egyik, hogy JSX-ben úgy adok meg egy JavaScript kifejezést, hogy { } közé írom. A másik pedig, hogy mindezek manuális begépelése egyáltalán nem pálya :). Erre valamit ki kell találni…
Props használata
Ha most megnéznénk a böngészőt, még semmi változást nem látnánk, mert a Terminator komponensben a kép, a model és a szériaszám is fixen be van égetve.
Viszont, mint azt már mindenki jól tudja, egy komponens a props változóban kapja meg a tulajdonságokat. Nem is késlekedem, nehogy kitörjön a gépek lázadása és átírom, amit kell.
Először is átadom a komponensnek a props paramétert:
const Terminator = (props) => {
Aztán a model és a szériaszámok esetében a fix értékeket lecserélem a props-ból jövő értékekre:
<div>
<h2>{props.name}</h2>
<p>SN: {props.serialNumber}</p>
</div>
Meg kell oldani a képet is, hogy minden terminátor esetében más kép jelenjen meg. Ehhez a kép url-ben a t helyére fogom betenni az id értékét. Az src-ben megadott aposztrófot lecserélem visszaperjelre, mert az ES6-ból ismert sztring literált fogom használni, amiben simán meg lehet adni bármilyen JavaScript kifejezést.
Tehát:
<img src={`https://robohash.org/${props.id}?size=180x180`} alt='terminator' />
Így lesz teljesen minden pöpec, ha megnézem a böngészőt:

Wow! Most már minden terminátor egyedi lett. Talán még annyit csinosítok a megjelenésen, hogy középre rendezem a szövegeket. Ehhez a Terminator komponensben megadok még egy tachyons osztály nevet: tc azaz text-center
<div className='bg-light-blue dib br3 pa3 ma2 grow tc'>

Rendrakás a kódban
Ha már szó volt a destruktúrálásról, akkor bűvészkedjünk még néhány pillanatig ezzel. Egy kicsit tisztábbá lehet tenni vele a kódot.
Először azt csinálom, hogy a komponensen belül a változókat kiszedem a props paraméterből:
const { id, name, serialNumber } = props;
Aztán a kód további részében már ezeket használom:
return (
<div className="bg-light-blue dib br3 pa3 ma2 grow tc">
<img src={`https://robohash.org/${id}?size=180x180`} alt="terminator" />
<div>
<h2>{name}</h2>
<p>SN: {serialNumber}</p>
</div>
</div>
);
Sőt azt is megtehetem, hogy a destruktúrálást eleve a paraméterben csinálom meg, és akkor már nincs szükség arra, hogy a return előtt deklarálom a változókat:
const Terminator = ({ id, name, serialNumber }) => {
return
Ennél tovább ezt már egyelőre ne fokozzuk. Szép tiszta lett így is a kód.
Összefoglalás
Jelenleg ott tartunk, hogy
- Elkezdtük a webalkalmazásunkat
- Megnéztük milyen egy projekt esetében (méretétől függetlenül) a jó mappaszerkezet. (Persze ez csak javaslat volt.)
- Megnéztük hogyan lehet külső API-t használni a képek beállítása során.
- Megnéztük, hogyan lehet a React-ben dizájnolni. Hogyan tudunk a tacyhons csomaggal minden erőfeszítés és css tudás nélkül, gyorsan stílust húzni a komponenseinkre.
- A végén jött egy kis destruktúrálás gyakorlat és kódoptimalizálás.
Github
Ahol most tartunk azt a Githubról is letöltheted. Megjegyzem, hogy az első két commit tartozik ehhez a „leckéhez”: