Iako je Next.js framework u pitanju, ipak je baziran na React.js-u i treba znati neke osnove kako koristiti React i šta sve Next.js olakšava.
Neki osnovni pojmovi i tehnike su:
React je baziran na komponentama, kao nekim jedinicama i treba to uvek imati u glavi. Ako kod postane previše dugačak i nečitljiv
treba grupisati elemente u komponentu.
Komponente su u suštini JS fajlovi koji exportuju funkciju koja vraća (return) HTML content.
Ekstenzija koja pomaže u pravljenju nove komponente je “ES7+ React/Redux/React-Native snippet” https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets
Ona se koristi tako što se prvo otvori novi JS fajl negde u app folderu i iskuca se RFC (React Function Component) i pritisne se Tab dugme i on izbaci sledeći kod:
import React from 'react'
export default function Component() {
return (
<h1>Hello</h1>
)
}
Prilikom kreiranja novog fajla, naravno nije obavezno ali komponente se pišu prvim velikim slovom i ako je više reči u pitanju, poštuje se da je svaka reč prvim velikim slovom napisana,
ekstenzija će uzeti naziv fajla i isto tako nazvati funkciju u komponenti tako da kada se komponenta importuje, koristi se isti naziv fajla. Dosta ubrzava proces pravljenja komponente.
Mali note, Next.js ne uzima import React from “react” kao obaveznu liniju pa se može i obrisati, ali nije obavezno, samo je malo čistiji kod.
Primer importa ove komponente bi bio: import Component from "@/components/Component;
Često se desi da se nađe array objekata koji svi treba da izgledaju isto, i umesto da recimo Blog kartice pravimo ručno i ubacujemo content, možemo da iskoristimo Map funkciju u JS
koja može da VRATI elemente sa dinamički popunjenim sadržajem.
export default function Component() {
return (
<div>
{array.map((item, index) => {
return (
<h1 key={index}>{item}</h1>
)
})}
</div>
)
}
Opet note: sav JS sadržaj ide u vitičaste zagrade {varijabla}
Map se poziva na niz/array i u Map ide callback funkcija (arrow funkcija radi čitljivosti) koja može da izbaci 2 parametra: element u nizu (item) i njegov redni broj u nizu (index).
Imenovanje bi bilo idealno da se poštuje na sledeći način: Ako je naziv za niz u množini npr. blogs onda će prvi parametar tj. item biti blog i ako je on objekat sa nekim sadržajem, pozivamo se u return delu
sa {blog.title}.
Return ustvari služi da renderuje neki html content. Return uvek mora da vrati jedan element u smislu ne može da vrati dva div-a koji su u “srodstvu”. Ali naravno taj jedan element može u sebi da ima bezbroj drugih elemenata.
Taj jedan glavni element mora da ima key property i on mora bidi jedinstven. Ako item nema id property koji može da se iskoristi, dovoljno je uzeti index da bude key jer svaki index će biti unique.
Često u React se dešava da se podaci povlače sa nekog API-ja, i u tim slučajevima renderovanje podataka pre nego što su podaci povučeni nije moguće.
Zbog toga treba dinamički prikazati podatke samo ako podaci postoje u datom trenutku. Ovo se radi preko inline if/else metode (ternarni operator).
isOpen == true ? console.log("Otvoreno") : console.log("Nije otvoreno");
Ako se primer gore razloži, prvo ide provera ili pitanje: da li je isOpen jednako true
Nakon toga ide upitnik, posle čega ide komanda koja treba da se izvrši u slučaju da je ta izjava istinita
Posle toga se pišu dve tačke nakon čega ide kod koji se pokreće ako nije izjava istinita.
Ovo se koristi u Reactu zato što u JSX (fake html) ne mogu da se pišu blokovi koda što su for loop, if/else itd, već moraju inline varijante da se koriste.
return (
<div>
{isOpen ? <div>Popup</div> : null}
</div>
)
Ako je isOpen == true, onda će se div pojaviti, ako nije, neže biti ništa, i ovo odlično radi čak ako se isOpen promeni u nekom momentu, uticaće na pojavljivanje tog div-a.
Kod gore može da se napiše sa skraćenicama && gde je dovoljno napisati samo IF deo koda, u slučaju da je else null tj. da ne mora ništa da se desi ako je provera false.
return (
<div>
{isOpen && <div>Popup</div>}
</div>
)
Napomena: obratiti pažnju kako u ternarnom operatoru nema return metoda jer se ona podrazumeva. Dovoljno je samo napisati content.
Kombinacija dinamičkog renderovanja liste bi izgledalo ovako:
export default function UserList() {
return (
<div>
{users && users.map((user, index) => {
return (
<h1 key={index}>{user.name}</h1>
)
})}
</div>
)
}
Radi lakšeg razumevanja obratiti pažnju na imenovanje varijabli itd.
Ovaj kod bi se pročitao na sledeći način, ako lista user-a postoji, renderuj svako ime user-a posebno koristeći h1 element. I ovo je recimo smešteno u zasebnu komponentu pod nazivom UserList.
useState je lagan hook za korišćenje i služi da imamo varijablu koju možemo da prikazujemo ili koristimo za content. Takođe možemo i da je menjamo itd.
const [count, setCount] = useState(0);
Ovde se radi o Tuples (nmp na srpskom kako se zovu) ali ovo treba gledati kao neku vrstu array destructuring, useState je funkcija koja vraća dve stvari, i te dve stvari definišemo sa proizvoljnim nazivima sa leve strane u array.
Neko “pravilo” imenovanja je da prvo ide naziv state-a tj. varijable, a nakon toga njegov “setter” tj. metoda sa istim nazivom ali dodatkom set pre naziva.
Jedina stvar koju useState prima kao parametar je default state, u ovom slučaju to je broj 0. To znači da ako prikažemo negde na frontu varijablu count, ona će imati vrednost 0.
export default function Counter() {
const [count, setCount] = useState(0);
return (
<p>{count}</p>
)
}
Ako iskoristimo setCount i zadamo mu novu vrednost, UI će se updateovati.
useState ide uglavnom zajedno sa eventovima, tako da više o ovome u toj sekciji.
Ovaj hook je pakao i nije mnogo previdljiv. Služi da prati lifecycle komponente u smislu može da pokrene određeni kod kada se komponenta učita (mount), ili kada se neka varijabla promeni, ili kada se komponenta izbriše (unmount).
export default function Component() {
useEffect(() => {
console.log("Mounted");
}, []);
return (
<div>
Content
</div>
)
}
Sintaksa je ta da useEffect metoda prima dve stvari, callback funkciju koja se trigeruje, i dependancy array, u ovaj array idu varijable koje kada se promene, trigeruje callback funkciju opet.
Ako je array prazan, useEffect će se triggerovati samo kada se komponenta učita prvi put.
export default function Component() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("count changed");
}, [count]);
return (
<div>
Content
</div>
)
}
U ovom primeru imamo state koji će se menjati, i svaki put kada se count promeni, useEffect će pokrenuti console.log komandu zato što se count nalazi u array-ju.
Infinite loop je moguć sa useEffect ali React ima backup za to gde neće dozvoliti crash. Ako ubacimo setCount u useEffect, po logici on će menjati count state, i promenom opet trigerovati useEffect, koji opet menja count state i tako u krug…
Napomena: useEffect je jedino mesto u komponenti koje ima pristup window i document objektima što je logično jer je to mesto jedino koje čeka da se komponenta u potpunosti učita.
Takođe treba imati na umu da bilo koja funkcija koja je definisana unutar useEffect callback funkcije, nije dostupna ostatku komponente.
Ovaj hook je zamena za document.querySelector, tj. selektovanje DOM elemenata u JS. U prevodu služi kao referenca DOM elementa.
export default function Component() {
const heading = useRef();
console.log(heading.current.innerText);
return (
<h1 ref={heading}>
Content
</h1>
)
}
Prvo treba deklarisati useRef, to je linija sa const heading gde zadajemo varijablu koju ćemo koristiti za referencu. Zatim tu referencu treba da ubacimo na element koji hoćemo da povežemo i nakon toga možemo da ga iskoristimo preko heading.current (dom element se čuva u current property heading objekta).
Napomena: nisam 100% siguran kada je referenca dostupna u komponenti, u smislu da li useEffect ima predstavu o njoj, ili može da se koristi van useEffect-a itd, treba testirati.
React ima dosta eventova koji menjaju addEventListener. Oni se dodaju direktno na elemente u JSX-u i počinju sa on<Nešto> (onClick, onPointerLeave…) i dodeljuje im se funkcija koja će se pokrenuti kada se event desi. Te funkcije takođe mogu da imaju event property kao addEventListener.
export default function Component() {
const [count, setCount] = useState(0);
const handleClick = (event) => {
setCount(count + 1);
}
return (
<h1 onClick={handleClick}>
Add 1 to count: {count}
</h1>
)
}
Svaki put kada se h1 pritisne, count se povećava za 1. Neko opet naming pravilo, za event funkciju obično ide prvo handle pa tip event-a ako ih nema više naravno (recimo više različitih click eventova) ali to handle označava da je funkcija namenjena isključivo za neki event. Handle funkcija mora biti dostupna u smislu ne sme da se nalazi u useEffect ili nekoj drugoj funkciji da bi radilo. VS Code kada krenemo da kucamo on će predložiti brdo eventova koje možemo da iskoristimo. Jedan od čestih je i onSubmit gde možemo popunjenu formu da pošaljemo na Backend tj. bazu podataka.
Kratko objasnjenje za useState + events je da na interakcije možemo da pozivamo setCount metodu da bi promenili state i samim tim i broj na frontu.