Denne Workshoppen er for deg som kan grunnleggende HTML og CSS fra før, og som vil lære deg React og alt det kule rammeverket har å by på.
Presentasjon til denne workshoppen
Du kommer til å se noen emojis i oppgavene. Disse betyr:
- 🏆 Oppgave: Her er hva du skal gjøre.
- 💡 Tips: Litt ekstra info som kan være greit å ha for å løse en oppgave.
- 🚨 Løsningsforslag: Her finner du et eksempel på hvordan du kan løse oppgaven.
-
Klon repoet ved å skrive dette i terminalen din:
git clone https://github.com/bekk/bootcamp-react-ws.git
-
Naviger deg inn i mappa som heter
bootcamp-react-ws
ved å skrive dette i terminalen dincd bootcamp-react-ws
-
Installer nødvendige avhengigheter for prosjektet:
npm install
-
Start opp nettsiden ved å skrive:
npm run dev
Om kloning av repoet skulle være litt knotete kan du bruke denne CodeSandbox-lenken for å redigere rett i browseren
Warning
Trykk på Fork
oppe i det høyre hjørnet for å få din egne versjon av koden.
I denne workshoppen skal du lage deg din egen personlige bloggside 🎉. Vi skal ta for oss steg for steg hvordan man bygger opp en React-applikasjon, men du har alltid friheten til å gjøre din egen vri på oppgavene!
La oss begynne med basic setup av React komponenter og et par andre vanlige ting i React.
Gå inn i App.jsx
-komponenten inne i src
-mappa, og fjern all koden som er mellom <>...</>
inne i return()
-funksjonen – la oss begynne å lagge denne nettsiden mer personalisert til deg og den du er!
🏆 I App.jsx
, returner Blog
-komponeten som allerede er importert i fila. Blog.jsx
ligger inne i mappa som heter Oppgave 1
.
💡 Det er en grei huskeregel at DOM-komponenter starter med liten forbokstav, og React-komponenter starter med Stor forbokstav.
<div />
er med andre ord et HTML-element, mens<Blog />
er en referanse til Blog-funksjonen vi vil bruke.
🏆 Få Blog.jsx
til å returnere en heading med innholdet "MittNavns blogg". Du kan bruke <h1 />
-elementet for å lage en heading.
🏆 I samme fil, lag en Title
-komponent som returnerer heading-elementet ditt som du skrev i forrige deloppgave. Bruk Title-komponenten din inne i Blog-komponenten.
🏆 Lag en ny fil i Oppgave1-mappa som du navngir Title.jsx
. Kopier Title komponenten du laget i forrige oppgave inn til Title.jsx fila og eksporter den slik at den kan brukes i andre komponenter. Importer Title komponenten i Blog.jsx
og slett samtidig Title definisjonen fra Blog.jsx
.
💡 I React kan man eksportere komponenter fra en fil, les om
default
ognamed
-exports her.
🏆 I stedet for å hardkode navnet ditt i komponenten, gjør om Title-komponenten din til å ta inn en prop som heter name
. Da kan du sende inn navnet ditt i Blog.jsx der du bruker <Title />
.
💡 Props kan hete akkurat hva du vil, og kan være akkurat den datastrukturen du vil at den skal være. Les mer om props her.
🏆 Inne i Title.jsx, legg til className-prop ‘title’ på h1-elementet. Nå kan du style komponenten slik du vil i .title
selektoren du finner i App.css
.
💡 Ikke vært så mye borti CSS? Les mer om CSS her.
🏆 Importer Confetti
-komponenten og bruk den i Blog-komponenten din 🎉
✨ Endre farger, størrelser, spacing, osv slik du vil 💅
🚨 Løsningsforslag
// I Title.jsx
export default function Title({ name }) {
return <h1 className="title">{name}s blogg</h1>;
}
// I Blog.jsx
return (
<>
<Title name="Caroline" />
</>
);
I denne oppgava skal vi se litt på dynamisk innhold og rendring av komponenter.
I Blog.jsx
, kommenter inn Facts-funksjonen som ligger øverst i fila, og bruk denne under Title-komponenten din i render-funksjonen.
Åpne Facts.jsx
som ligger i mappa som heter Oppgave 2
. Her finnes det allerede en liste med tekster som vises. Vi viser hvert element ved bruk av index, f.eks med facts[0]
som viser det første elementet i lista. Denne løsningen skalerer dårlig da vi må manuelt bruke index for å hente ut hvert element vi vil vise. Vi vil derfor skrive om innholdet i Facts.jsx
til å iterere gjennom hvert element i facts
-lista ved bruk av map()
-funksjonen. La oss gjøre det steg for steg:
🏆 Fjern de hardkoda faktaene i renderfunksjonen til Facts.jsx
, og iterer gjennom hvert element i lista og returner et <li>
element for hvert fakta-element.
💡 Det finnes flere liste-operasjoner i JavaScript,
.map()
itererer gjennom hvert element i lista og du kan bestemme hva hvert element skal mappes om til. Les mer om map her.
🏆 Bli kvitt warningen i konsollen om at hvert element i lista trenger en unik identifikator. Dette gjøres ved å bruke key
-attributten på hvert element. Husk at key
må være unik for hvert element i lista.
💡 Les om
key
mer her.
Det kan bli rotete å ha all koden i en fil, så la oss dele opp koden i flere komponenter.
🏆 Lag en egen komponent, Card
, som returnerer et listeelement <li>
og bruk komponenten i Facts.jsx
. Card
skal ta inn faktaen som skal vises som en prop (f.eks. facts
). Klassenavnet card
kan benyttes for å legge på styling.
🏆 Endre Card-komponenten slik at den tar inn fakta som children
fremfor å sendes inn gjennom fact
-prop'en, slik at den blir mer dynamisk. Gjør nødvendige endringer i Facts
-komponenten.
💡
children
er en fin måte å sette sammen flere komponenter på en dynamisk måte, ved å bruke children så "wrapper" man en komponenten rundt en annen komponent. Les mer om children her.
✨ Legg inn fakta om deg selv i stedet, og style card-komponenten slik du vil med andre farger og animasjoner 💅
🚨 Løsningsforslag
// I Facts.jsx
function Facts() {
return {
<>
<h2>Fakta om kjendiser</h2>
{facts.map((fact) => (
<Card key={fact}>{fact}</Card>
))}
</>
}
}
// I Card.jsx
function Card({ children }) {
return (
<li className="card">{children}</li>
)
}
Nå skal vi gjøre nettsiden litt mer interaktiv!
I Blog.jsx
, kommenter inn CoverPhoto-funksjonen som ligger øverst i fila, og bruk denne under Title-komponenten din i render-funksjonen.
Gå inn i mappa som heter Oppgave3
og åpne CoverPhoto.jsx
🏆 Endre CoverPhoto-komponenten slik at den returnerer et bildet som har stien "/src/assets/img1.jpg"
. Gi komponenten klassenavnet "cover-photo"
.
💡 Her kan du bruke
<img>
-element for å vise et bilde.
🏆 Gjør det mulig for brukeren å trykke på bildet ved å legge bildet i en knapp (<button>
) og print en melding til konsollen når knappen trykkes på.
💡 For å logge til konsollen kan du bruke
console.log('din tekst her')
, og for å se konsollen kan du høyre-klikke i nettleseren og trykke på "inspiser" eller "inspect".
Les mer om events her.
Vi har lyst til å vise et annet bilde når brukeren trykker på det nåværende bildet – så i stedet for å console.logge trenger vi nå å vite om brukeren har trykket på bildet eller ikke.
🏆 Lag en useState
som har verdien showFirstPicture
og en funksjon som heter setShowFirstPicture
, med default-verdi satt til true
.
💡 useState returnerer alltid en liste på formatet
const [verdi, setVerdi] = useState()
, og du kan kalle disse verdiene hva du vil.
Vi bestemmer om det skal være bolske verdier, tall eller tekst som verdien i useState'en skal ha, i dette tilfellet trenger vi bare en bolsk verdi som forteller om vi skal vise det første bildet eller ikke. Les om useState her.
🏆 Endre i CoverPhoto-komponenten slik at når brukeren trykker på bildet så skal setShowFirstPicture oppdatere verdien til showFirstPicture til å være det motsatte av det den var. Altså hvis showFirstPicture er true så skal den nye verdien bli false, og motsatt.
🏆 I render-funksjonen kan du nå bruke verdien til showFirstPicture
for å vise enten bilde 1 eller bilde 2, avhengig om brukeren har trykket på bildet eller ikke. Bilde 2 finner du på stien /src/assets/img2.jpg
.
💡 Dette kalles conditional rendering og ser slik ut:
condition ? ifConditionIsTrue : ifConditionIsFalse
✨ Legg inn bilde av deg selv, av en hobby eller noe annet du liker, og style nettsiden slik du vil 💅
🚨 Løsningsforslag
// I CoverPhoto.jsx
import { useState } from 'react';
export default function CoverPhoto() {
const [showFirstPicture, setShowFirstPicture] = useState(true);
const onButtonClick = () => {
setShowFirstPicture(!showFirstPicture);
};
return (
<button onClick={onButtonClick}>
<div className="cover-photo">
{showFirstPicture ? (
<img src="/src/assets/img1.jpg" alt="picture of coffee" />
) : (
<img
src="/src/assets/img2.jpg"
alt="picture of person and a candle"
/>
)}
</div>
</button>
);
}
Nå skal nettsiden endelig begynne å kommunisere med verden, for nå skal vi nemlig hente litt data! Vi skal vise kollektivtrafikk-avganger fra et stoppested som man kan trenge å vite før man forlater hjemmet sitt. La oss ta for oss dette steg for seg.
I Blog.jsx
, kommenter inn DepartureBoard-funksjonen som ligger øverst i fila, og bruk denne under Title-komponenten din i render-funksjonen.
🏆 Lag en useEffect inne i DepartureBoard.jsx
som ligger i Oppgave4
-mappa. Som en oppvarmingsoppgave kan du begynne med å console-logge en vilkårlig tekst til konsollen når komponenten mountes.
💡 Her er dependency-arrayet til useEffect'en viktig. Hvis man dropper lista vil useEffect kjøres på hver render, har man en tom liste vil det si at det som er i useEffect'en bare skal kjøres når komponenten mountes, og en liste med verdier trigger bare useEffecten bare når verdiene i lista får en oppdatert verdi.
OBS! Komponenten din vil mountes 2 ganger i developer mode 🙃 – så useEffect som bare skal kjøres på mount vil kjøre to ganger. Forvirrende greier! Grunnen er at React vil stressteste komponenten din og verifisere at den fungerer uavhengig om useEffect kjøres en eller to ganger.
🏆 I stedet for å console-logge til konsollen, kan du nå skrive en async funksjon inne i useEffecten som heter fetchData
som kaller fetchDepartures
som er en ferdiglaget funksjon som henter noe data (du trenger ikke bry deg om innholdet i denne funksjonen). Du kan begynne med å console-logge dataen du får tilbake for å se at nettverkskallet fungerer. Husk å kalle fetchData inne i useEffecten din.
💡 Et vanlig oppsett av en useEffect som henter data
useEffect(() => {
const fetchData = async () => {
...
}
fetchData()
}, [])
💡 Funksjonen `fetchDepartures` henter data fra Entur sine API'er, og returnerer data på dette formatet:
{
stopPlace: string // Navnet på stoppestedet,
departures: Array<{
frontText: string, // hva strekningen heter
departureTime: string, // avgangstidspunkt
}>
}
Les om henting av data med useEffects her
🏆 Lag to states i komponenten din, en som tar seg av tittelen på stoppestedet, et annet som lagrer alle avgangene. De kan f.eks hete [title, setTitle]
og [departures, setDepartures]
.
🏆 Oppdater verdiene til title
og departures
med dataen fra endepunktet i useEffecten.
💡 I JavaScript kan du destrukturere objekter
const { stopPlace, departures } = data;
🏆 Rendre tittel og avgangsinformasjonen. Her kan du legge til departures
som className
på <ul>
-taggen rundt avgangene, og departure
på hvert <li>
-element. Du kan også legge til et clockIcon som et bilde for hver avgang som et visuelt tillegg.
🏆 Hent informasjon om et stoppested du kjenner til! 💅
- Gå på https://entur.no/kart.
- Skriv inn et stoppested du kjenner til.
- Gå i url'en på nettsiden og kopierer id'en som begynner på
NSR:
, f.eksNSR:StopPlace:58801
. - Gå i
client.js
på linje 15 og erstatt den id'en med din egen id for å hente avganger for ditt stoppested!
🚨 Løsningsforslag
import { useState, useEffect } from 'react';
import clockIcon from '../assets/Clock.svg';
import { fetchDepartures } from '../client';
export default function DepartureBoard() {
const [title, setTitle] = useState('...');
const [departures, setDepartures] = useState([]);
useEffect(() => {
const fetchData = async () => {
const { stopPlace, departures } = await fetchDepartures();
setTitle(stopPlace);
setDepartures(departures);
};
fetchData();
}, []);
return (
<>
<h2>Avganger fra {title}</h2>
<ul className="departures">
{departures.map(({ frontText, departureTime }) => {
return (
<li
className="departure"
key={`${frontText}${departureTime}`}
>
<img src={clockIcon} />
{frontText} kl. {departureTime}
</li>
);
})}
</ul>
</>
);
}
Hva! Har du kommet så langt allerede?! Da har jeg noen bonusoppgaver til deg! Du kan velge mellom disse oppgavene:
🏆 Lag en bildekarusell
I stedet for å klikke på bildet for å endre det så kan du lage en karusell der brukeren kan bla seg gjennom bilder.
- Hvis du trenger bilder kan du bruke https://unsplash.com/.
🏆 Personaliser bloggsiden din enda mer!
Er det noe mer informasjon eller komponenter som mangler? Slå deg løs!
🏆 Datahenting
Lyst til å leke mer med datahenting? Det er brukt GraphQL for å hente data fra Entur sitt JourneyPlanner API i oppgave 4. Sjekk ut dokumentasjonen på API'et og se om du kan hente og vise mer nyttig data for et stoppested!
🏆 Context
Bruk React Context til å lage dark-mode på siden din når brukeren trykker på en knapp.
- Hint: Bruk CSS-variabler.