I React-applikationer er det essentielt at forstå, hvordan man håndterer sideeffekter, som er handlinger, der påvirker komponentens omgivelser uden for React, såsom at hente data fra en API eller ændre DOM. Den mest almindelige måde at håndtere sideeffekter i React-komponenter er ved hjælp af useEffect Hooken. Denne hook giver mulighed for at køre kode på bestemte tidspunkter i komponentens livscyklus, såsom når komponenten først monteres, opdateres, eller fjernes.
Når du bruger useEffect, kan du styre, hvornår effekten skal køres, ved at angive et array af afhængigheder som det andet argument. For eksempel, hvis du ønsker at køre en oprydningsfunktion, når en bestemt state ændrer sig, kan du skrive noget lignende:
I dette tilfælde vil effekten kun blive kørt, når resolved ændres. Hvis effekten kører og resolved ikke er blevet ændret, vil oprydningskoden ikke blive kørt, og den oprindelige effektkode vil ikke blive kørt igen. Denne tilgang sikrer, at ressourcer kun bruges, når det er nødvendigt, og at unødvendige opdateringer undgås.
En anden almindelig situation er, når oprydningskoden kun skal køres, når komponenten fjernes fra DOM'en. For eksempel, i tilfælde af at hente data fra en API, kan du have en effekt, der kun skal køres én gang ved komponentens første rendering:
Her er den tomme array [] det, der fortæller React, at effekten kun skal køres én gang, når komponenten bliver monteret. Uden den tomme array ville effekten køre på hver render, hvilket ikke er ønsket i dette tilfælde. Denne tilgang er ideel, når du kun ønsker at hente data én gang og undgå unødvendige API-anmodninger.
For at undgå unødvendige genindlæsninger af data eller utilsigtede opdateringer i store applikationer, er det vigtigt at bruge useEffect korrekt og forstå, hvordan afhængigheder fungerer. Fejl i brugen af useEffect kan føre til dårlig ydeevne, såsom gentagne API-anmodninger eller ekstra renders.
En anden vigtig teknik i React-applikationer er at bruge Context API'en til at dele globale data mellem komponenter. Når flere komponenter i applikationen skal have adgang til de samme data, kan Context API'en være meget nyttig. Den gør det muligt at oprette et delt datalager, som enhver komponent i træet kan få adgang til, uanset dybde.
For at oprette en context bruger vi createContext:
Derefter bruger vi Provider-komponenten til at give data til de relevante børnekomponenter:
Alle komponenter inden for Provider-komponenten kan nu få adgang til de delte data ved hjælp af useContext-hooken:
Ved at bruge Context API'en undgår vi problemet med at skulle sende props gennem flere niveauer af komponenter (prop-drilling). Det gør koden mere læsbar og vedligeholdelsesvenlig, især i større applikationer. Dog bør Context API bruges med omtanke. Det er mest nyttigt til at dele data, der er globalt nødvendige, som fx brugerdata, og det bør ikke bruges til små, lokale data.
Når man arbejder med React, er det også vigtigt at tage højde for ydeevne. Funktionelle komponenter bliver kaldt på hver render, hvilket kan medføre, at dyre beregninger og funktioner bliver oprettet gentagne gange. For at optimere ydeevnen og undgå unødvendige beregninger, tilbyder React hooks som useMemo, useCallback og useRef.
useMemo hooken bruges til at memoize resultatet af en beregning, så den kun køres igen, når afhængighederne ændrer sig. Dette kan være nyttigt, når du har dyre beregninger, som du ikke ønsker at køre på hver render:
På samme måde bruges useCallback til at memoize funktioner, hvilket kan forhindre unødvendige genrendringer af børnekomponenter, der modtager disse funktioner som props:
I dette tilfælde sørger useCallback for, at den samme funktionsinstans sendes til MyButton på hver render, hvilket forhindrer en unødvendig genrendring af MyButton.
Endelig er useRef hooken nyttig, når du vil bevare værdier eller referencer, der ikke skal medføre genrendringer. Den bruges typisk til at oprette en vedvarende reference til DOM-elementer eller komponentinstanser:
Her bruges useRef til at oprette en reference til et input-felt, som vi kan tilgå og manipulere direkte.
Endtext
Hvordan man arbejder med ruter i React
I React-applikationer er håndtering af ruter en fundamental del af at opbygge strukturerede og let navigerbare brugergrænseflader. Med react-router kan udviklere skabe klart definerede ruter, der er tæt forbundet med de komponenter, de render. Dette gør det muligt at opbygge en applikation, hvor hver rute har sin egen logik og visning, hvilket både forbedrer læsbarheden og vedligeholdelsen af koden. I denne sammenhæng er det vigtigt at forstå, hvordan man definerer ruter, håndterer parametre og bruger komponenter til navigation.
Når man erklærer ruter i react-router, kan man sammenkoble hver rute med den komponent, den skal vise, ved hjælp af JSX-syntaks. Dette betyder, at man kan definere ruter og deres tilknyttede komponenter direkte sammen, hvilket skaber en tydelig struktur. Ruterne oprettes ved hjælp af funktionen createBrowserRouter, og det er her, man specificerer både stien (path) og den komponent (element), der skal vises, når ruten matches. Den indbyggede RouterProvider-komponent håndterer den faktiske navigering baseret på den aktive URL.
Et eksempel på en grundlæggende rute kan se således ud:
Her definerer createBrowserRouter en rute, hvor stien "/" leder til MyComponent-komponenten. Når brugeren navigerer til denne rute, bliver komponenten vist. Hvis ruten ikke matches, vises intet, hvilket gør implementeringen enkel og intuitiv.
Når man arbejder med routing i større applikationer, kan det hurtigt blive uoverskueligt at have alle ruterne i én stor modul. For at undgå dette er det en god idé at organisere ruterne efter funktionalitet. I stedet for at samle alle ruterne i én enkelt rute-modul, kan man definere ruter for hver funktion, hvilket gør det lettere at se, hvilke ruter der tilhører hvilken del af applikationen. Dette skaber en mere overskuelig struktur og gør det nemmere at vedligeholde koden.
En anden vigtig teknik er at bruge et layout-komponent, der håndterer de ruter, der er fælles for hele applikationen, som for eksempel en navigationsmenu. For eksempel kan man skabe et Layout-komponent, som indeholder en fast navigationsbjælke og bruger Outlet-komponenten til at indsætte de ruter, der matcher den aktuelle URL.
Her bruges Outlet til at placere de forskellige ruter, som matcher den aktuelle sti, i layout-komponenten. Når brugeren navigerer til en bestemt rute, bliver den tilknyttede komponent rendere inde i Outlet-pladsen.
Desuden kan man organisere ruterne i "feature"-baserede moduler. Hvert modul kan have sine egne ruter og komponenter, hvilket hjælper med at opretholde en ren og modulær struktur. For eksempel kan ruten /one indeholde flere under-ruter, som hver især viser forskellige komponenter baseret på den specifikke sti.
I dette tilfælde vil /one føre brugeren til /one/1, og /one/1 vil vise First-komponenten. Dette gør det muligt at skabe en hierarkisk rute-struktur, der giver god kontrol over navigeringen i applikationen.
En vigtig komponent i react-router er Redirect, som bruges til at omdirigere brugeren fra en URL til en anden. Dette er nyttigt, når man har en rute uden egentligt indhold eller når man ønsker at omdirigere brugeren til en anden rute baseret på bestemte betingelser, som for eksempel autentificering.
Ved at organisere ruter på denne måde, kan man nemt håndtere selv mere komplekse scenarier, såsom forældreruter, under-ruter og navigation baseret på brugerens handlinger. Det gør applikationen både mere fleksibel og lettere at vedligeholde.
En sidste vigtig overvejelse, når man arbejder med react-router, er at optimere applikationen med funktioner som "lazy loading" af komponenter, som gør det muligt at indlæse komponenter dynamisk, efterhånden som brugeren navigerer rundt i applikationen. Dette forbedrer ydeevnen ved at reducere den initiale indlæsningstid.
Det er også vigtigt at teste din routing-struktur grundigt. Når applikationen vokser, og flere ruter tilføjes, kan det hurtigt blive svært at holde styr på, hvordan ruterne hænger sammen. Testning og dokumentation af rute-strukturen hjælper med at sikre, at applikationen forbliver funktionel og overskuelig.
Hvordan man optimerer React-applikationer ved hjælp af batching og prioritering af state opdateringer
I de tidligere kapitler har vi set på komponenter, der hjælper brugeren med at navigere rundt i din applikation, og hvordan man indsamler brugerinput med Material UI formkomponenter. Derudover har vi set på, hvordan man kan style sine Material UI-komponenter ved at ændre temaer og tilpasse dem. Den viden, du har opnået, gør det muligt at bygge komplekse brugergrænseflader uden at skulle udvikle UI-komponenter fra bunden, hvilket fremskynder udviklingsprocessen. Desuden er React-applikationsudvikling afhængig af synergisk brug af forskellige hjælpebiblioteker. En dyb forståelse af React-økosystemet og dets nøglebiblioteker giver udviklere mulighed for hurtigt at prototype og iterere på deres applikationer og dermed gøre udviklingen mere effektiv.
I dette kapitel vil vi fokusere på at forbedre effektiviteten af dine state opdateringer ved hjælp af den nyeste funktionalitet i React. State er den dynamiske del af din applikation; når staten ændres, reagerer komponenterne på disse ændringer. Uden state ville du blot have en fancy HTML-mallensprog. Typisk vil den tid, der kræves for at udføre en state opdatering og få ændringerne vist på skærmen, være næsten umærkelig. Dog kan komplekse state ændringer nogle gange føre til mærkbar forsinkelse for brugerne. Formålet med dette kapitel er at adressere disse tilfælde og finde måder at undgå disse forsinkelser på.
Når du arbejder med React, er der ofte mange tilstande, der ændres hurtigt i en applikation. Dette kan forårsage unødvendige genrenderinger, hvilket påvirker ydeevnen negativt. Problemet opstår, når flere state opdateringer sker på meget kort tid, og React prøver at genrende DOM'en hver gang en ændring finder sted. For eksempel, hvis du ændrer en state værdi mange gange i løbet af få millisekunder, kan React forsøge at genrende DOM'en for hver ændring, hvilket er ineffektivt.
Batching af state opdateringer er løsningen på dette problem. I React 18 er automatisk batching blevet mere omfattende, hvilket betyder, at flere state opdateringer nu behandles som én opdatering, hvilket reducerer antallet af nødvendige genrenderinger. For eksempel, når du ændrer en state flere gange i løbet af en kort periode, vil React samle disse opdateringer og genrenderer kun én gang, i stedet for at genrende for hver ændring.
I React 17 skete automatisk batching kun indenfor event handler funktioner, hvilket kunne føre til problemer, når der blev lavet asynkrone opkald udenfor disse funktioner. For eksempel, hvis du henter data asynkront og derefter opdaterer state, ville React ikke batch'e disse opdateringer. Dette kunne føre til mange unødvendige opdateringer og dermed påvirke applikationens ydeevne. React 18 løser dette problem ved at gøre automatisk batching til en global funktion, der også fungerer i asynkrone scenarier.
Lad os nu se på, hvordan React 18 håndterer dette i praksis. Hvis vi for eksempel har en knap, der ved klik udfører 100 state opdateringer, kan vi bruge setTimeout() til at udføre disse opdateringer asynkront. Uden batching ville React genrende DOM'en 100 gange, hvilket er unødvendigt og langsomt. Med React 18 vil React automatisk samle disse opdateringer og kun udføre én genrendering, hvilket væsentligt forbedrer ydeevnen.
For at få et konkret billede af forskellen mellem React 17 og React 18 kan vi bruge React Developer Tools til at analysere antallet af genrenderinger. Ved at køre koden i React 17 vil vi kunne se, at der er mange unødvendige renders, mens React 18 kun udfører én render.
Når du arbejder med React, er det afgørende at forstå hvordan state opdateringer fungerer, og hvordan man kan optimere disse opdateringer ved hjælp af batching og prioritering. Ved at bruge React 18's funktioner effektivt kan du undgå unødvendige renders og dermed forbedre ydeevnen af din applikation.
Desuden er det vigtigt at forstå, at selvom batching kan hjælpe med at forbedre ydeevnen, er det ikke den eneste faktor, der påvirker applikationens hastighed. Andre aspekter som optimering af komponenter, korrekt brug af hooks, og effektiv håndtering af asynkrone operationer spiller også en væsentlig rolle i at sikre en glidende brugeroplevelse.
Hvordan React Native forbedrer udviklingen af native mobilapplikationer
React Native er et af de mest banebrydende værktøjer, der er udviklet for at bygge native mobilapplikationer, der kan køre på både Android- og iOS-platforme med en enkelt kodebase. Denne tilgang har gjort det muligt for udviklere at udnytte det velkendte React-økosystem, som tidligere kun var begrænset til webudvikling, til nu også at adressere udfordringerne ved mobilapp-udvikling.
Som tidligere nævnt i denne bog er rendermålet en essentiel del af React, da det definerer, hvor komponenterne vil blive vist. I traditionel React-webudvikling er rendermålet typisk Document Object Model (DOM), men i React Native ændres dette mål til at inkludere native UI-komponenter i mobiloperativsystemerne. React Native arbejder ved at bruge en JavaScript-motor til at kommunikere med de native platform-API’er via asynkrone opkald, hvilket gør det muligt for appen at interagere med den underliggende hardware, uden at udvikleren skal bekymre sig om de tekniske detaljer forbundet med hver enkelt platform.
Fordelen ved React Native er, at den ikke kræver, at udviklere lærer flere forskellige programmeringssprog eller platformsspecifikke værktøjer som Java eller Kotlin for Android eller Swift for iOS. I stedet kan React-komponenter, der er bygget til webapplikationer, anvendes direkte i mobilapplikationer. Når React Native opdateres med komponenter implementeret til de mobile platforme, gør det det muligt for udviklerne at skrive en app, der er optimeret til både iOS og Android uden at skulle genopfinde hjulet for hver platform. Det betyder, at man kan benytte sig af platformens native widgets, som har et væsentligt højere niveau af konsistens og brugervenlighed end de ofte utilstrækkelige widgets i mobile webbrowsere.
En af de største udfordringer ved mobilbrowseroplevelsen er, at den ikke kan levere den samme brugeroplevelse som native apps. Mobile webbrowsere har ikke mulighed for at replikere de native widgets, der findes i operativsystemet. Forsøg på at imitere disse widgets kan føre til inkonsistens i appens udseende og funktionalitet, hvilket kan skabe forvirring og forringe brugeroplevelsen. Ved at bruge native widgets kan udvikleren sikre, at appen ser ud og fungerer på en måde, der er i overensstemmelse med de øvrige apps på enheden, hvilket gør det muligt at skabe en mere integreret oplevelse.
En anden vigtig forskel mellem udvikling til web og mobile platforme er, hvordan brugeren interagerer med enheden. På en webapplikation anvender man typisk en mus til at klikke på knapper og interagere med elementer på skærmen, men på mobilen sker interactionen via berøring, hvilket skaber helt andre krav til designet. React Native er en god kandidat til at håndtere disse "gestures", som mobile enheder anvender til navigation, hvilket betyder, at udvikleren ikke behøver at bekymre sig om de specifikke detaljer vedrørende touch-baseret interaktion.
Der er dog et vigtigt aspekt, der adskiller iOS og Android, og som React Native ikke kan abstrahere væk: forskellen i brugeroplevelsesfilosofierne på tværs af de to platforme. Selvom React Native gør det muligt at dele meget kode mellem de to platforme, er iOS og Android stadig fundamentalt forskellige i deres designfilosofier og UI-implementation. Derfor er det nødvendigt at forstå, at React Native fokuserer på princippet om “learn once, write anywhere” snarere end “write once, run anywhere”. Det betyder, at udviklere i nogle tilfælde skal tage højde for platformsspecifikke widgets og designmønstre for at opnå den bedste brugeroplevelse.
Der er dog blevet gjort betydelige fremskridt i React Native-økosystemet, som muliggør en mere sømløs tværplatformsudvikling. For eksempel understøtter Expo nu webudvikling, hvilket gør det muligt at køre en React Native-app på weben via React Native for Web. Med den rette tilgang kan en app udvikles med én kodebase, der fungerer både på Android, iOS og web. Derudover understøtter Tamagui UI-kittet både web- og mobilplatforme, hvilket gør det lettere at udvikle apps, der fungerer på flere platforme uden at gå på kompromis med brugeroplevelsen.
Det er klart, at mens React Native ikke nødvendigvis tilbyder en “write once, run anywhere”-løsning, tilbyder det en praktisk og effektiv måde at bygge apps på tværs af platforme, hvor udviklere kan udnytte deres eksisterende færdigheder og den brede tilgængelighed af JavaScript og React. React Native fjerner mange af de barrierer, der normalt kræves for at udvikle apps til mobile platforme, og giver en mere strømlinet udviklingsproces med større fokus på komponentgenbrug og bedre samarbejde på tværs af platforme.
Endelig er det vigtigt at forstå, at valget af værktøjer og teknologier altid bør afspejle projektets specifikke behov. Mens React Native er et stærkt valg til mange mobilapplikationer, er det ikke altid den rigtige løsning for alle projekter. Det er derfor nødvendigt at evaluere de tekniske krav og mål for appen grundigt, før man træffer beslutningen om, hvilken teknologi der skal anvendes.
Hvordan virker FlatList-komponenten, og hvordan kan man manipulere lister i React Native?
FlatList-komponenten er et centralt værktøj i React Native til at håndtere lister med rullende indhold. Ved første øjekast ser det ud til, at FlatList ikke gør meget – den kræver bare nogle grundlæggende data og en renderItem-egenskab for at generere listen. Men dens virkelige styrke ligger i dens effektivitet og fleksibilitet, især når det drejer sig om håndtering af opdateringer og scrolling.
I en simpel opstilling bruger FlatList en data-egenskab, som er et array, og en renderItem-egenskab, som bestemmer, hvordan hvert enkelt element i listen skal renderes. Et eksempel på en simpel liste kan se således ud:
I eksemplet ovenfor er styling på listen afgørende for at gøre hvert element visuelt adskilt. Uden disse stilarter ville hvert element i listen kun være tekst, og det ville være vanskeligt at skelne dem fra hinanden. Her tildeles containeren en højde ved at bruge flex: 1, hvilket gør det muligt for listen at fylde hele skærmen og kunne rulle.
Når man arbejder med FlatList, er det vigtigt at forstå, at det ikke bare er et statisk element, men et dynamisk værktøj, der kan tilpasses til komplekse krav, som f.eks. filtrering og sortering af data. Det er her, kontrolkomponenter som ListContainer, ListControls, ListFilter og ListSort kommer i spil.
Implementering af filtre og sortering af listen
En stor fordel ved at bruge FlatList er, at den nemt kan kombineres med andre komponenter, som f.eks. filtre og sorteringskontroller. For at tilføje sådanne funktioner skal man forstå komponentstrukturen og hvordan data manipuleres. Her er et eksempel på, hvordan en liste kan opdeles i flere komponenter for bedre struktur og vedligeholdelse:
-
ListContainer: Hovedkomponenten, der indeholder listen og styrer tilstanden af filtrering og sortering.
-
List: En simpel, statsløs komponent, der modtager data fra ListContainer og rendere listen.
-
ListControls: Holder de forskellige kontrolmekanismer til at ændre listen, som filtrering og sortering.
-
ListFilter: En kontrol, der filtrerer listen efter brugerens input.
-
ListSort: En kontrol, der ændrer sorteringen af listen.
Her er et eksempel på, hvordan man kan implementere disse komponenter i praksis:
Her ser vi, at vi bruger useMemo til at optimere ydeevnen, så listen kun genberegnes, når der er ændringer i filtrerings- eller sorteringsbetingelserne. På den måde undgår man unødvendige opdateringer og reducerer den computationalt tunge belastning.
Listekomponentens struktur og kontrollens placering
I dette setup bruges ListHeaderComponent-egenskaben til at placere kontrolkomponenterne som filter og sortering udenfor den scrollbare liste. Det betyder, at kontrolmekanismerne altid vil være synlige, selv når brugeren ruller gennem listen.
Hvordan du kan bruge ListControls
Listekontrollerne samles i én komponent, ListControls, som indeholder både filtrerings- og sorteringskomponenter. Hvis du ønsker at tilføje flere kontroller, som f.eks. en pagineringskontrol eller en kontrol for at ændre visningsmåden, kan du let udvide denne komponent.
Filtrering og sortering af listen
Filtreringen er simpel: En tekstindgang, som opdaterer tilstanden i ListContainer, og dermed filtrerer de viste elementer. Sorteringsmekanismen arbejder med en simpel boolean-værdi (asc), der bestemmer, om listen skal sorteres stigende eller faldende.
Når brugeren klikker på en af pilene, ændres sorteringen, og listen opdateres i overensstemmelse hermed.
Vigtige overvejelser
Selvom FlatList i React Native tilbyder en effektiv måde at håndtere lange lister med data på, er der flere ting, der er vigtige at forstå og implementere korrekt. For eksempel er det nødvendigt at tænke på ydeevne, især når lister bliver store. Man bør derfor udnytte optimeringer som useMemo og React.memo for at sikre, at listen ikke bliver genrendere hele tiden, hvis ikke nødvendigt.
Desuden er det vigtigt at huske, at den visuelle adskillelse af listeelementerne (ved hjælp af passende stilarter) gør listen nemmere at bruge og forstå for slutbrugeren. Det betyder, at man ikke bare skal fokusere på funktionaliteten, men også på at skabe en god brugeroplevelse.

Deutsch
Francais
Nederlands
Svenska
Norsk
Dansk
Suomi
Espanol
Italiano
Portugues
Magyar
Polski
Cestina
Русский