I dag er React et af de mest populære JavaScript-biblioteker. Det kan bruges til at skabe dynamiske og responsive applikationer, giver bedre ydeevne og kan nemt udvides. Den underliggende logik er baseret på komponenter, der kan genbruges i forskellige sammenhænge, hvilket reducerer behovet for at skrive den samme kode flere gange. Kort sagt, med React kan du skabe effektive og kraftfulde applikationer.

Så der har aldrig været et bedre tidspunkt at lære at skabe React-applikationer.

Men uden en solid forståelse af nogle centrale JavaScript-funktioner, kan det være svært eller endda umuligt at bygge React-applikationer.

Derfor har vi samlet en liste over JavaScript-funktioner og -koncepter, som du skal kende, før du kommer i gang med React. Jo bedre du forstår disse begreber, jo lettere bliver det for dig at bygge professionelle React-applikationer.

JavaScript og ECMAScript

JavaScript er et populært scripting-sprog, der bruges sammen med HTML og CSS til at bygge dynamiske websider. Mens HTML bruges til at skabe strukturen på en webside og CSS til at skabe stilen og layoutet på dens elementer, er JavaScript det sprog, der bruges til at tilføje adfærd til siden, dvs. til at skabe funktionalitet og interaktivitet.

Sproget er siden blevet adopteret af de store browsere, og der blev skrevet et dokument for at beskrive den måde, JavaScript skulle fungere på: ECMAScript-standarden.

Fra og med 2015 udgives der hvert år en opdatering af ECMAScript-standarden, og dermed tilføjes der nye funktioner til JavaScript hvert år.

ECMAScript 2015 var den sjette udgave af standarden og er derfor også kendt som ES6. De efterfølgende versioner er markeret med progression, så vi kalder ECMAScript 2016 for ES7, ECMAScript 2017 for ES8 og så videre.

På grund af den hyppighed, hvormed der tilføjes nye funktioner til standarden, er det ikke sikkert, at alle browsere understøtter nogle af dem. Så hvordan kan du sikre dig, at de nyeste JavaScript-funktioner, du har tilføjet til din JS-app, fungerer som forventet på tværs af alle webbrowsere?

Du har tre muligheder:

  1. Vent, indtil alle større browsere understøtter de nye funktioner. Men hvis du absolut har brug for den fantastiske nye JS-funktion til din app, er dette ikke en mulighed.
  2. Brug en Polyfill, som er “et stykke kode (normalt JavaScript på nettet), der bruges til at give moderne funktionalitet i ældre browsere, der ikke understøtter det fra starten” (se også mdn web docs).
  3. Brug en JavaScript-transpiler som Babel eller Traceur, der konverterer ECMAScript 2015+-kode til en JavaScript-version, der understøttes af alle browsere.

Udsagn vs. udtryk

Det er vigtigt at forstå forskellen mellem statements og expressions, når man bygger React-applikationer. Så lad os gå tilbage til de grundlæggende begreber inden for programmering et øjeblik.

Et computerprogram er en liste over instruktioner, der skal udføres af en computer. Disse instruktioner kaldes statements.

I modsætning til statements er expression fragmenter af kode, der producerer en værdi. I et statement er en expression en del, der returnerer en værdi, og vi ser det normalt på højre side af et lighedstegn.

Hvorimod:

JavaScript-sætninger kan være blokke eller linjer af kode, der normalt slutter med semikolon eller er omsluttet af krøllede parenteser.

Her er et simpelt eksempel på et statement i JavaScript:

document.getElementById("hello").innerHTML = "Hello World!";

Sætningen ovenfor skriver "Hello World!" i et DOM-element med id="hello".

Som vi allerede har nævnt, producerer expessions en værdi eller er selv en værdi. Overvej følgende eksempel:

msg = document.getElementById("hello").value;

document.getElementById("hello").value er en expression, da det returnerer en værdi.

Et ekstra eksempel kan hjælpe med at tydeliggøre forskellen mellem expression og statement:

const msg = "Hello World!";
function sayHello( msg ) {
	console.log( msg );
}

I eksemplet ovenfor,

  • er den første linje en erklæring, hvor "Hello World!" er en expression,
  • funktionsdeklarationen er et statement, hvor parameteren msg, der sendes til funktionen, er en expression,
  • linjen, der udskriver beskeden i konsollen, er en erklæring, hvor parameteren msg igen er en expression.

Hvorfor expression er vigtige i React

Når du bygger en React-applikation, kan du injicere JavaScript-udtryk i din JSX-kode. Du kan f.eks. videregive en variabel, skrive en event handler eller en betingelse. For at gøre dette skal du inkludere din JS-kode i krøllede parenteser.

Du kan f.eks. videregive en variabel:

const Message = () => {
	const name = "Carlo";
	return <p>Welcome {name}!</p>;
}

Kort sagt fortæller de krøllede parenteser din transpiler, at den skal behandle koden i parenteserne som JS-kode. Alt, hvad der kommer før det åbnende <p> -tag og efter det afsluttende </p> -tag, er normal JavaScript-kode. Alt inden for de åbne <p> og de afsluttende </p> tags behandles som JSX-kode.

Her er et andet eksempel:

const Message = () => {	
	const name = "Ann";
	const heading = <h3>Welcome {name}</h3>;
	return (
		<div>
			{heading}
			<p>This is your dashboard.</p>
		</div>
	);
}

Du kan også sende et objekt:

render(){			
	const person = {
		name: 'Carlo',
		avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22',
		description: 'Content Writer'
	}

	return (
		<div>
			<h2>Welcome {person.name}</h2>
			<img
				className="card"
				src={person.avatar}
				alt={person.name}
			/>
			<p>Description: {person.description}.</p>
		</div>
	);
}

Og nedenfor er et mere omfattende eksempel:

render(){
	const person = {
		name: 'Carlo',
		avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
		description: 'Content Writer',
		theme: {
			boxShadow: '0 4px 8px 0 rgba(0,0,0,0.2)', width: '200px'
		}
	}

	return (
		<div style={person.theme}>
			<img
				src={person.avatar}
				alt={person.name}
				style={ { width: '100%' } }
			/>
			<div style={ { padding: '2px 16px' } }>
				<h3>{person.name}</h3>
				<p>{person.description}.</p>
			</div>
		</div>
	);
}

Læg mærke til de dobbelte krøllede parenteser i style -attributterne i elementerne img og div. Vi brugte dobbelte parenteser til at videregive to objekter, der indeholder kort- og billedestile.

Et eksempel på et kort bygget med React
Et eksempel på et kort bygget med React

Du burde have bemærket, at vi i alle eksemplerne ovenfor har inkluderet JavaScript-espression i JSX.

Uforanderlighed i React

Foranderlighed og uforanderlighed er to nøglebegreber i objektorienteret og funktionel programmering.

Immutability betyder, at en værdi ikke kan ændres, efter at den er blevet skabt. Mutability betyder selvfølgelig det modsatte.

I Javascript er primitive værdier uforanderlige, hvilket betyder, at når en primitiv værdi er oprettet, kan den ikke ændres. Omvendt er arrays og objekter mutable, fordi deres egenskaber og elementer kan ændres uden at tildele en ny værdi.

Der er flere grunde til at bruge uforanderlige objekter i JavaScript:

  • Forbedret ydeevne
  • Reduceret hukommelsesforbrug
  • Tråd-sikkerhed
  • Nemmere kodning og debugging

Ifølge mønsteret for uforanderlighed kan en variabel eller et objekt ikke tildeles igen eller ændres, når det først er tildelt. Når du har brug for at ændre data, skal du oprette en kopi af den og ændre dens indhold, så det oprindelige indhold forbliver uændret.

Uforanderlighed er også et nøglebegreb i React.

I React-dokumentationen står der:

Tilstanden af en klassekomponent er tilgængelig som this.state. Tilstandsfeltet skal være et objekt. Du må ikke mutere tilstanden direkte. Hvis du ønsker at ændre tilstanden, skal du kalde setState med den nye

tilstand.
Hver gang en komponents tilstand ændres, beregner React, om komponenten skal gengives og den virtuelle DOM opdateres. Hvis React ikke havde styr på den tidligere tilstand, kunne den ikke afgøre, om komponenten skulle genskabes eller ej. React-dokumentationen giver et glimrende eksempel på dette.

Hvilke JavaScript-funktioner kan vi bruge til at garantere uforanderligheden af tilstandsobjektet i React? Lad os finde ud af det!

Deklarering af variabler

Du har tre måder at deklarere en variabel på i JavaScript: var, let, og const.

Erklæringen var har eksisteret siden begyndelsen af JavaScript. Den bruges til at deklarere en funktionsskoblet eller globalskoblet variabel og eventuelt initialisere den til en værdi.

Når du erklærer en variabel ved hjælp af var, kan du gen-deklarere og opdatere den variabel både i det globale og lokale scope. Følgende kode er tilladt:

// Declare a variable
var msg = "Hello!";

// Redeclare the same variable
var msg = "Goodbye!"

// Update the variable
msg = "Hello again!"

var deklarationer behandles, før nogen kode udføres. Som følge heraf svarer det at erklære en variabel hvor som helst i koden til at erklære den øverst. Denne adfærd kaldes hoisting.

Det er værd at bemærke, at det kun er deklarationen af variablen, der hejses, ikke initialiseringen, som først sker, når kontrolflowet når tildelingsudsagnet. Indtil da er variablen undefined:

console.log(msg); // undefined
var msg = "Hello!";
console.log(msg); // Hello!

Omfanget af en var, der er erklæret i en JS-funktion, er hele funktionens body.

Det betyder, at variablen ikke er defineret på blokniveau, men på niveau med hele funktionen. Det fører til en række problemer, som kan gøre din JavaScript-kode rodet og svær at vedligeholde.

For at løse disse problemer introducerede ES6 nøgleordetlet.

Erklæringen let erklærer en lokal variabel, der er blokopdelt, og initialiserer den eventuelt til en værdi.

Hvad er fordelene ved light i forhold til var? Her er nogle af dem:

  • light var erklærer en variabel til omfanget af en bloksætning, mens erklærer en variabel globalt eller lokalt til en hel funktion uanset blokomfang.
  • Globale let -variabler er ikke egenskaber for window -objektet. Du kan ikke få adgang til dem med window.variableName.
  • light en global variabelkan kun tilgås, når dens deklaration er nået. Variablen initialiseres ikke, før kontrolflowet når til den kodelinje, hvor den er erklæret (let-erklæringer er ikke-hoisted).
  • Hvis du erklærer en variabel igen med light, får du en SyntaxError.

Da variabler, der er erklæret med var, ikke kan blokskoperes, kan du, hvis du definerer en variabel med var i en løkke eller inde i en if -sætning, få adgang til den uden for blokken, og det kan føre til fejlbehæftet kode.

Koden i det første eksempel udføres uden fejl. Udskift nu var med light i den kodeblok, der ses ovenfor:

console.log(msg);
let msg = "Hello!";
console.log(msg);

I det andet eksempel giver brugen af light i stedet for var en Uncaught ReferenceError:

Ufanget referencefejl i Chrome
Ufanget referencefejl i Chrome

ES6 introducerer også et tredje nøgleord: const.

const er meget lig light, men med en vigtig forskel:

Overvej følgende eksempel:

const MAX_VALUE = 1000;
MAX_VALUE = 2000;

Ovenstående kode ville generere følgende TypeError:

Uncaught TypeError: Tildeling til konstant variabel i Google Chrome
Uopdaget TypeError: Tildeling til konstant variabel i Google Chrome

Derudover:

Hvis du erklærer en const uden at give den en værdi, vil det give følgende SyntaxError (se også ES6 In Depth: let og const):

Uncaught syntaksfejl: Manglende initialisering i const-deklaration i Chrome
Uncaught SyntaxError: Manglende initializer i const-erklæring i Chrome

Men hvis en konstant er et array eller et objekt, kan du redigere egenskaber eller elementer i dette array eller objekt.

Du kan f.eks. ændre, tilføje og fjerne array-elementer:

// Declare a constant array
const cities = ["London", "New York", "Sydney"];

// Change an item
cities[0] = "Madrid";

// Add an item
cities.push("Paris");

// Remove an item
cities.pop();

console.log(cities);

// Array(3)
// 0: "Madrid"
// 1: "New York"
// 2: "Sydney"

Men du må ikke tildele arrayet på ny:

const cities = ["London", "New York", "Sydney"];

cities = ["Athens", "Barcelona", "Naples"];

Koden ovenfor ville resultere i en TypeError.

Uncaught TypeError: Tildeling til konstant variabel.
Uopdaget TypeError: Tildeling til konstant variabel i Chrome

Du kan tilføje, omfordele og fjerne egenskaber og metoder for objekter:

// Declare a constant obj
const post = {
	id: 1,
	name: 'JavaScript is awesome',
	excerpt: 'JavaScript is an awesome scripting language',
	content: 'JavaScript is a scripting language that enables you to create dynamically updating content.'
};

// add a new property
post.slug = "javascript-is-awesome";

// Reassign property
post.id = 5;

// Delete a property
delete post.excerpt;

console.log(post);

// {id: 5, name: 'JavaScript is awesome', content: 'JavaScript is a scripting language that enables you to create dynamically updating content.', slug: 'javascript-is-awesome'}

Men du har ikke lov til at omfordele selve objektet. Følgende kode ville gå gennem en Uncaught TypeError:

// Declare a constant obj
const post = {
	id: 1,
	name: 'JavaScript is awesome',
	excerpt: 'JavaScript is an awesome scripting language'
};

post = {
	id: 1,
	name: 'React is powerful',
	excerpt: 'React lets you build user interfaces'
};

Object.freeze()

Vi er nu enige om, at brugen af const ikke altid garanterer stærk uforanderlighed (især når man arbejder med objekter og arrays). Så hvordan kan du implementere uforanderlighedsmønsteret i dine React-applikationer?

For det første, når du vil forhindre, at elementerne i et array eller egenskaberne i et objekt bliver ændret, kan du bruge den statiske metode Object.freeze().

Fastfrysning af et objekt forhindrer udvidelser og gør eksisterende egenskaber ikke-skrivbare og ikke-konfigurerbare. Et frosset objekt kan ikke længere ændres: nye egenskaber kan ikke tilføjes, eksisterende egenskaber kan ikke fjernes, deres opregnelighed, konfigurerbarhed, skrivbarhed eller værdi kan ikke ændres, og objektets prototype kan ikke tildeles igen. freeze() returnerer det samme objekt, som blev sendt ind.

Ethvert forsøg på at tilføje, ændre eller fjerne en egenskab vil mislykkes, enten lydløst eller ved at kaste en TypeError, mest almindeligt i strict mode.

Du kan bruge Object.freeze() på denne måde:

'use strict'
// Declare a constant obj
const post = {
	id: 1,
	name: 'JavaScript is awesome',
	excerpt: 'JavaScript is an awesome scripting language'
};
// Freeze the object
Object.freeze(post);

Hvis du nu forsøger at tilføje en egenskab, vil du modtage en Uncaught TypeError:

// Add a new property
post.slug = "javascript-is-awesome"; // Uncaught TypeError
Uncaught TypeError: kan ikke definere egenskaben. Objektet kan ikke udvides
Uncaught TypeError: kan ikke definere egenskaben “slug”: Objektet kan ikke udvides i Firefox

Når du prøver at tildele en egenskab igen, får du en anden slags TypeError:

// Reassign property
post.id = 5; // Uncaught TypeError
Gentildeling af en skrivebeskyttet egenskab kaster en Uncaught TypeError
Gentildeling af en skrivebeskyttet egenskab kaster en Uncaught TypeError
Uncaught TypeError: Kan ikke tildele til skrivebeskyttet egenskab i Chrome
Ikke-godkendt TypeError: Kan ikke tildele den skrivebeskyttede egenskab ‘id’ for objektet ‘#<Object>’ i Google Chrome

Du kan også prøve at slette en egenskab. Resultatet vil være en anden TypeError:

// Delete a property
delete post.excerpt; // Uncaught TypeError
Uncaught TypeError: egenskaben "uddrag" kan ikke konfigureres og kan ikke slettes i Firefox
Uncaught TypeError: Egenskaben “excerpt” er ikke konfigurerbar og kan ikke sl ettes i Firefox

Bogstavelige skabeloner

Når du skal kombinere strenge med output fra udtryk i JavaScript, bruger du normalt additionsoperatoren +. Men du kan også bruge en JavaScript-funktion, der giver dig mulighed for at inkludere udtryk i strenge uden at bruge additionsoperatoren: Template Literals.

Template Literals er en særlig slags strenge afgrænset med backtick (`) tegn.

I Template Literals kan du inkludere pladsholdere, som er indlejrede udtryk afgrænset af et dollartegn og pakket ind i krøllede parenteser.

Her er et eksempel:

const align = 'left';
console.log(`This string is ${ align }-aligned`);

Strengene og pladsholderne sendes til en standardfunktion, der udfører strenginterpolation for at erstatte place holders og sammenkæde delene til en enkelt streng. Du kan også erstatte standardfunktionen med en brugerdefineret funktion.

Du kan bruge Template Literals til:

Strenge med flere linjer: nylinjetegn er en del af skabelonlitteralen.

console.log(`Twinkle, twinkle, little bat!
How I wonder what you’re at!`);

Interpolation af strenge: Uden Template Literals kan du kun bruge additionsoperatoren til at kombinere output fra udtryk med strenge. Se det følgende eksempel:

const a = 3;
const b = 7;
console.log("The result of " + a + " + " + b + " is " + (a + b));

Det er lidt forvirrende, er det ikke? Men du kan skrive den kode på en mere læselig og vedligeholdelsesvenlig måde ved hjælp af Template Literals:

const a = 3;
const b = 7;
console.log(`The result of ${ a } + ${ b } is ${ a + b }`);

Men husk på, at der er en forskel mellem de to syntakser:

Template Literals egner sig til flere anvendelser. I det følgende eksempel bruger vi en ternær operator til at tildele en værdi til en class -attribut.

const page = 'archive';
console.log(`class=${ page === 'archive' ? 'archive' : 'single' }`);

Nedenfor udfører vi en simpel beregning:

const price = 100;
const VAT = 0.22;

console.log(`Total price: ${ (price * (1 + VAT)).toFixed(2) }`);

Det er også muligt at indlejre Template Literals ved at inkludere dem i en ${expression} pladsholder (men brug indlejrede skabeloner med forsigtighed, da komplekse strengstrukturer kan være svære at læse og vedligeholde).

Taggede skabeloner: Som vi nævnte ovenfor, er det også muligt at definere en brugerdefineret funktion til at udføre sammenkædning af strenge. Denne form for Template Literal kaldes Tagged Template.

Tags giver dig mulighed for at parse template literals med en funktion. Det første argument i en tag-funktion indeholder et array af strengværdier. De resterende argumenter er relateret til udtrykkene.

Tags giver dig mulighed for at parse template literals med en brugerdefineret funktion. Det første argument i denne funktion er et array af de strenge, der er inkluderet i Template Literal, de andre argumenter er udtrykkene.

Du kan oprette en brugerdefineret funktion til at udføre enhver form for operation på skabelonargumenterne og returnere den manipulerede streng. Her er et meget grundlæggende eksempel på en tagget skabelon:

const name = "Carlo";
const role = "student";
const organization = "North Pole University";
const age = 25;

function customFunc(strings, ...tags) {
	console.log(strings); // ['My name is ', ", I'm ", ', and I am ', ' at ', '', raw: Array(5)]
	console.log(tags); // ['Carlo', 25, 'student', 'North Pole University']
	let string = '';
	for ( let i = 0; i < strings.length - 1; i++ ){
		console.log(i + "" + strings[i] + "" + tags[i]);
		string += strings[i] + tags[i];
	}
	return string.toUpperCase();
}

const output = customFunc`My name is ${name}, I'm ${age}, and I am ${role} at ${organization}`;
console.log(output);

Koden ovenfor udskriver array-elementerne strings og tags og sætter derefter store bogstaver på strengtegnene, før den udskriver output i browserkonsollen.

Pil-funktioner

Arrow-funktioner er et alternativ til anonyme funktioner (funktioner uden navne) i JavaScript, men med nogle forskelle og begrænsninger.

De følgende erklæringer er alle gyldige eksempler på Arrow Functions:

// Arrow function without parameters
const myFunction = () => expression;

// Arrow function with one parameter
const myFunction = param => expression;

// Arrow function with one parameter
const myFunction = (param) => expression;

// Arrow function with more parameters
const myFunction = (param1, param2) => expression;

// Arrow function without parameters
const myFunction = () => {
	statements
}

// Arrow function with one parameter
const myFunction = param => {
	statements
}

// Arrow function with more parameters
const myFunction = (param1, param2) => {
	statements
}

Du kan udelade de runde parenteser, hvis du kun sender én parameter til funktionen. Hvis du sender to eller flere parametre, skal du omslutte dem med parenteser. Her er et eksempel på dette:

const render = ( id, title, category ) => `${id}: ${title} - ${category}`;
console.log( render ( 5, 'Hello World!', "JavaScript" ) );

Pilefunktioner på én linje returnerer som standard en værdi. Hvis du bruger syntaksen med flere linjer, skal du manuelt returnere en værdi:

const render = ( id, title, category ) => {
	console.log( `Post title: ${ title }` );
	return `${ id }: ${ title } - ${ category }`;
}
console.log( `Post details: ${ render ( 5, 'Hello World!', "JavaScript" ) }` );

En vigtig forskel mellem normale funktioner og Arrow-funktioner er, at Arrow-funktioner ikke har deres egne bindinger til nøgleordet this. Hvis du forsøger at bruge this i en Arrow Function, vil den gå uden for funktionens scope.

For en mere dybdegående beskrivelse af Arrow-funktioner og eksempler på brug, læs også mdn web docs.

Klasser

Klasser i JavaScript er en særlig type funktion til at skabe objekter, der bruger den prototypiske arvemekanisme.

Ifølge mdn web docs,

Når det kommer til nedarvning, har JavaScript kun én konstruktion: objekter. Hvert objekt har en privat egenskab, som indeholder et link til et andet objekt kaldet dets prototype. Dette prototypeobjekt har sin egen prototype, og så videre, indtil man når frem til et objekt med null som prototype

.
Ligesom med funktioner har du to måder at definere en klasse på:

  • Et klasseudtryk
  • En klassedeklaration

Du kan bruge nøgleordet class til at definere en klasse inde i et udtryk, som vist i følgende eksempel:

const Circle = class {
	constructor(radius) {
		this.radius = Number(radius);
	}
	area() {
		return Math.PI * Math.pow(this.radius, 2);
	}
	circumference() {
		return Math.PI * this.radius * 2;
	}
}
console.log('Circumference: ' + new Circle(10).circumference()); // 62.83185307179586
console.log('Area: ' + new Circle(10).area()); // 314.1592653589793

En klasse har en body, som er den kode, der er sat i krøllede parenteser. Her definerer du konstruktører og metoder, som også kaldes klassemedlemmer. Klassens body eksekveres i strict mode, selv uden at bruge 'strict mode' -direktivet.

Metoden constructor bruges til at oprette og initialisere et objekt, der er oprettet med en klasse, og udføres automatisk, når klassen instantieres. Hvis du ikke definerer en konstruktørmetode i din klasse, vil JavaScript automatisk bruge en standardkonstruktør.

En klasse kan udvides ved hjælp af nøgleordet extends.

class Book {
	constructor(title, author) {
		this.booktitle = title;
		this.authorname = author;
	}
	present() {
		return this.booktitle + ' is a great book from ' + this.authorname;
	}
}

class BookDetails extends Book {
	constructor(title, author, cat) {
		super(title, author);
		this.category = cat;
	}
	show() {
		return this.present() + ', it is a ' + this.category + ' book';
	}
}

const bookInfo = new BookDetails("The Fellowship of the Ring", "J. R. R. Tolkien", "Fantasy");
console.log(bookInfo.show());

En konstruktør kan bruge super -nøgleordet til at kalde den overordnede konstruktør. Hvis du sender et argument til super() -metoden, vil dette argument også være tilgængeligt i den overordnede konstruktørklasse.

For et dybere dyk ned i JavaScript-klasser og flere eksempler på brug, se også mdn web docs.

Klasser bruges ofte til at skabe React-komponenter. Normalt vil du ikke oprette dine egne klasser, men snarere udvide indbyggede React-klasser.

Alle klasser i React har en render() -metode, der returnerer et React-element:

class Animal extends React.Component {
	render() {
		return <h2>Hey, I am a {this.props.name}!</h2>;
	}
}

I eksemplet ovenfor er Animal en klassekomponent. Vær opmærksom på, at

  • Navnet på komponenten skal begynde med et stort bogstav
  • Komponenten skal indeholde udtrykket extends React.Component. Det giver adgang til metoderne i React.Component.
  • Metoden render() returnerer HTML og er påkrævet.

Når du har oprettet din klassekomponent, kan du gengive HTML’en på siden:

const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Animal name="Rabbit" />;
root.render(element);

Billedet nedenfor viser resultatet på siden (du kan se det i aktion på CodePen).

En simpel React-klassekomponent
En simpel React-klassekomponent

Bemærk dog, at det ikke anbefales at bruge klassekomponenter i React, og at det er at foretrække at definere komponenter som funktioner.

Nøgleordet ‘this’

I JavaScript er nøgleordet this en generisk placeholder, der normalt bruges inde i objekter, klasser og funktioner, og det henviser til forskellige elementer afhængigt af konteksten eller omfanget.

this kan bruges i det globale scope. Hvis du taster this i din browsers konsol, får du:

Window {window: Window, self: Window, document: document, name: '', location: Location, ...}

Du kan tilgå alle metoder og egenskaber i Window -objektet. Så hvis du kører this.location i din browsers konsol, får du følgende output:

Location {ancestorOrigins: DOMStringList, href: 'https://kinsta.com/', origin: 'https://kinsta.com', protocol: 'https:', host: 'kinsta.com', ...}

Når du bruger this i et objekt, henviser det til selve objektet. På denne måde kan du henvise til et objekts værdier i selve objektets metoder:

const post = { 
	id: 5,
	getSlug: function(){
		return `post-${this.id}`;
	},
	title: 'Awesome post', 
	category: 'JavaScript' 
};
console.log( post.getSlug );

Lad os nu prøve at bruge this i en funktion:

const useThis = function () {
	return this;
}
console.log( useThis() );

Hvis du ikke er i strict mode, får du:

Window {window: Window, self: Window, document: document, name: '', location: Location, ...}

Men hvis du aktiverer strict mode, får du et andet resultat:

const doSomething = function () {
	'use strict';
	return this;
}
console.log( doSomething() );

I dette tilfælde returnerer funktionen undefined. Det skyldes, at this i en funktion refererer til dens eksplicitte værdi.

Så hvordan sætter man eksplicit this i en funktion?

For det første kan du manuelt tildele egenskaber og metoder til funktionen:

function doSomething( post ) {
	this.id = post.id;
	this.title = post.title;
	console.log( `${this.id} - ${this.title}` );
}
new doSomething( { id: 5, title: 'Awesome post' } );

Men du kan også bruge call, apply, og bind metoder samt pilfunktioner.

const doSomething = function() {
	console.log( `${this.id} - ${this.title}` );
}
doSomething.call( { id: 5, title: 'Awesome post' } );

call() -metoden kan bruges på enhver funktion og gør præcis, hvad den siger: den kalder funktionen.

Desuden accepterer call() enhver anden parameter, der er defineret i funktionen:

const doSomething = function( cat ) {
	console.log( `${this.id} - ${this.title} - Category: ${cat}` );
}
doSomething.call( { id: 5, title: 'Awesome post' }, 'JavaScript' );
const doSomething = function( cat1, cat2 ) {
	console.log( `${this.id} - ${this.title} - Categories: ${cat1}, ${cat2}` );
}
doSomething.apply( { id: 5, title: 'Awesome post' }, ['JavaScript', 'React'] );
const post = { id: 5, title: 'Awesome post', category: 'JavaScript' };
const doSomething = function() {
	return `${this.id} - ${this.title} - ${this.category}`;
}
const bindRender = doSomething.bind( post );
console.log( bindRender() );

Et alternativ til de muligheder, der er diskuteret ovenfor, er at bruge pilefunktioner.

Pilfunktionsudtryk bør kun bruges til ikke-metodefunktioner, fordi de ikke har deres egen this.

Det gør pilefunktioner særligt nyttige i forbindelse med event handlers.

Det skyldes, at “når koden kaldes fra en inline event handler-attribut, sættes dens this til det DOM-element, som lytteren er placeret på” (se mdn web docs).

Men tingene ændrer sig med pilefunktioner, fordi…

… pilefunktioner etablerer this baseret på det scope, pilefunktionen er defineret i, og this værdien ændrer sig ikke baseret på, hvordan funktionen kaldes.

Binding af ‘this’ til eventhandlere i React

Når det kommer til React, har du et par måder at sikre dig, at event handleren ikke mister sin kontekst:

1. Ved at bruge bind() inde i render-metoden:

import React, { Component } from 'react';
class MyComponent extends Component {
	state = { message: 'Hello World!' };

	showMessage(){
		console.log( 'This refers to: ', this );
		console.log( 'The message is: ', this.state.message );
	}

	render(){
		return( <button onClick={ this.showMessage.bind( this ) }>Show message from state!</button> );
	}
}
export default MyComponent;

2. Binding af konteksten til eventhandleren i konstruktøren:

import React, { Component } from 'react';
class MyComponent extends Component {
	state = { message: 'Hello World!' };

	constructor(props) {
		super(props);
		this.showMessage = this.showMessage.bind( this );
	}

	showMessage(){
		console.log( 'This refers to: ', this );
		console.log( 'The message is: ', this.state.message );
	}

	render(){
		return( <button onClick={ this.showMessage }>Show message from state!</button> );
	}
}
export default MyComponent;

3. Definer event handler ved hjælp af pilefunktioner:

import React, { Component } from 'react';
class MyComponent extends Component {
	state = { message: 'Hello World!' };

	showMessage = () => {
		console.log( 'This refers to: ', this );
		console.log( 'The message is: ', this.state.message );
	}

	render(){
		return( <button onClick={this.showMessage}>Show message from state!</button> );
	}
}
export default MyComponent;

4. Brug af pilefunktioner i render-metoden:

import React, { Component } from 'react';
class MyComponent extends Component {
	state = { message: 'Hello World!' };

	showMessage() {
		console.log( 'This refers to: ', this );
		console.log( 'The message is: ', this.state.message );
	}

	render(){
		return( <button onClick={()=>{this.showMessage()}}>Show message from state!</button> );
	}
}
export default MyComponent;

Uanset hvilken metode du vælger, viser browserkonsollen følgende output, når du klikker på knappen:

This refers to:  MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
The message is:  Hello World!

Ternary operator

Den conditional operator (eller ternary operator) giver dig mulighed for at skrive simple betingede udtryk i JavaScript. Den tager tre operander:

  • en betingelse efterfulgt af et spørgsmålstegn (?),
  • et udtryk, der skal udføres, hvis betingelsen er sand efterfulgt af et kolon (:),
  • et andet udtryk, der skal udføres, hvis betingelsen er falsk.
const drink = personAge >= 18 ? "Wine" : "Juice";

Det er også muligt at kæde flere udtryk sammen:

const drink = personAge >= 18 ? "Wine" : personAge >= 6 ? "Juice" : "Milk";

Vær dog forsigtig, for kædning af flere udtryk kan føre til rodet kode, der er svær at vedligeholde.

Den ternary operator er særlig nyttig i React, især i din JSX-kode, som kun accepterer udtryk i krøllede parenteser.

Du kan f.eks. bruge den ternære operator til at indstille værdien af en attribut baseret på en bestemt betingelse:

render(){			
	const person = {
		name: 'Carlo',
		avatar: 'https://en.gravatar.com/...',
		description: 'Content Writer',
		theme: 'light'
	}

	return (
		<div
			className='card' 
			style={
				person.theme === 'dark' ? 
				{ background: 'black', color: 'white' } : 
				{ background: 'white', color: 'black'} 
			}>
			<img
				src={person.avatar}
				alt={person.name}
				style={ { width: '100%' } }
			/>
			<div style={ { padding: '2px 16px' } }>
				<h3>{person.name}</h3>
				<p>{person.description}.</p>
			</div>
		</div>
	);
}

I koden ovenfor kontrollerer vi betingelsen person.theme === 'dark' for at indstille værdien af style -attributten i containeren div.

Evaluering af kortslutning

Den logiske AND (&&) operator evaluerer operanderne fra venstre mod højre og returnerer true hvis og kun hvis alle operanderne er true.

Den logiske AND er en kortslutningsoperator. Hver operand konverteres til en boolsk, og hvis resultatet af konverteringen er false, stopper AND-operatoren og returnerer den oprindelige værdi af den fejlbehæftede operand. Hvis alle værdier er true, returnerer den den oprindelige værdi af den sidste operand.



Kortslutningsevaluering er en JavaScript-funktion, der ofte bruges i React, da den giver dig mulighed for at udlæse blokke af kode baseret på specifikke betingelser. Her er et eksempel:

{
	displayExcerpt &&
	post.excerpt.rendered && (
		<p>
			<RawHTML>
				{ post.excerpt.rendered }
			</RawHTML>
		</p>
	)
}

I koden ovenfor, hvis displayExcerpt AND post.excerpt.rendered evaluerer til true, returnerer React den endelige blok af JSX.

For at opsummere: “Hvis betingelsen er true, vil elementet lige efter && blive vist i outputtet. Hvis det er false, vil React ignorere og springe det over”.

Spread syntax

I JavaScript giver spread syntax dig mulighed for at udvide et iterabelt element, såsom et array eller objekt, til funktionsargumenter, array literals eller object literals.

I det følgende eksempel pakker vi et array ud i et funktionskald:

function doSomething( x, y, z ){
	return `First: ${x} - Second: ${y} - Third: ${z} - Sum: ${x+y+z}`;
}
const numbers = [3, 4, 7];
console.log( doSomething( ...numbers ) );

Du kan bruge spread-syntaksen til at duplikere et array (selv multidimensionale arrays) eller til at sammenkæde arrays. I de følgende eksempler sammenkæder vi to arrays på to forskellige måder:

const firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray.push( ...secondArray );
console.log( firstArray );

Alternativt:

let firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray = [ ...firstArray, ...secondArray];
console.log( firstArray );

Du kan også bruge spread-syntaksen til at klone eller flette to objekter:

const firstObj = { id: '1', title: 'JS is awesome' };
const secondObj = { cat: 'React', description: 'React is easy' };

// clone object
const thirdObj = { ...firstObj };

// merge objects
const fourthObj = { ...firstObj, ...secondObj }

console.log( { ...thirdObj } );
console.log( { ...fourthObj } );

Destrukturering af tildelinger

En anden syntaktisk struktur, du ofte vil finde brugt i React, er destructuring assignment-syntaksen.

I det følgende eksempel udpakker vi værdier fra et array:

const user = ['Carlo', 'Content writer', 'Kinsta'];
const [name, description, company] = user;
console.log( `${name} is ${description} at ${company}` );

Og her er et simpelt eksempel på destruktureringstildeling med et objekt:

const user = {
	name: 'Carlo',
	description: 'Content writer',
	company: 'Kinsta'
}
const { name, description, company } = user;
console.log( `${name} is ${description} at ${company}` );

Men vi kan gøre endnu mere. I det følgende eksempel pakker vi nogle egenskaber ud af et objekt og tildeler de resterende egenskaber til et andet objekt ved hjælp af den spredte syntaks:

const user = {
	name: 'Carlo',
	family: 'Daniele',
	description: 'Content writer',
	company: 'Kinsta',
	power: 'swimming'
}
const { name, description, company, ...rest } = user;
console.log( rest ); // {family: 'Daniele', power: 'swimming'}

Du kan også tildele værdier til et array:

const user = [];
const object = { name: 'Carlo', company: 'Kinsta' };
( { name: user[0], company: user[1] } = object );
console.log( user ); // (2) ['Carlo', 'Kinsta']

Bemærk, at parenteserne omkring assignment-sætningen er påkrævet, når du bruger object literal destructuring assignment uden en deklaration.

For en mere dybdegående analyse af destruktureringstildeling, med flere eksempler på brug, henvises til mdn web docs.

filter(), map() og reduce()

JavaScript indeholder flere nyttige metoder, som ofte bruges i React.

filter()

I det følgende eksempel anvender vi filteret på arrayet numbers for at få et array, hvis elementer er tal større end 5:

const numbers = [2, 6, 8, 2, 5, 9, 23];
const result = numbers.filter( number => number > 5);
console.log(result); // (4) [6, 8, 9, 23]

I det følgende eksempel får vi et array af indlæg med ordet ‘JavaScript’ i titlen:

const posts = [
	{id: 0, title: 'JavaScript is awesome', content: 'your content'},
	{id: 1, title: 'WordPress is easy', content: 'your content'},
	{id: 2, title: 'React is cool', content: 'your content'},
	{id: 3, title: 'With JavaScript to the moon', content: 'your content'},
];

const jsPosts = posts.filter( post => post.title.includes( 'JavaScript' ) );

console.log( jsPosts );
Et array af indlæg, hvor titlen indeholder 'JavaScript'
Et array af indlæg, hvor titlen indeholder ‘JavaScript’

map()

const numbers = [2, 6, 8, 2, 5, 9, 23];
const result = numbers.map( number => number * 5 );
console.log(result); // (7) [10, 30, 40, 10, 25, 45, 115]

I en React-komponent vil du ofte finde map() -metoden brugt til at opbygge lister. I det følgende eksempel kortlægger vi WordPress-objektet posts for at opbygge en liste over indlæg:

<ul>
	{ posts && posts.map( ( post ) => {
		return (
			<li key={ post.id }>
				<h5>
					<a href={ post.link }>
						{ 
							post.title.rendered ? 
							post.title.rendered :
							__( 'Default title', 'author-plugin' )
						}
					</a>
				</h5>
			</li>
		)
	})}
</ul>

reduce()

reduce() accepterer to parametre:

  • En callback-funktion, der skal udføres for hvert element i arrayet. Den returnerer en værdi, der bliver værdien af akkumulatorparameteren ved det næste kald. Ved det sidste kald returnerer funktionen den værdi, der vil være returværdien af reduce().
  • En startværdi, som er den første værdi i akkumulatoren, der sendes til callback-funktionen.

Callback-funktionen tager et par parametre:

  • En akkumulator: Den værdi, der returneres fra det forrige kald til callback-funktionen. Ved det første kald sættes den til en startværdi, hvis det er angivet. Ellers tager den værdien af det første element i arrayet.
  • Værdien af det aktuelle element: Værdien sættes til det første element i arrayet (array[0]), hvis der er indstillet en startværdi, ellers tager den værdien af det andet element (array[1]).
  • Det aktuelle indeks er indekspositionen for det aktuelle element.

Et eksempel vil gøre det hele klarere.

const numbers = [1, 2, 3, 4, 5];
const initialValue = 0;
const sumElements = numbers.reduce(
	( accumulator, currentValue ) => accumulator + currentValue,
	initialValue
);
console.log( numbers ); // (5) [1, 2, 3, 4, 5]
console.log( sumElements ); // 15

Lad os finde ud af i detaljer, hvad der sker ved hver iteration. Gå tilbage til det forrige eksempel, og ændr initialValue:

const numbers = [1, 2, 3, 4, 5];
const initialValue = 5;
const sumElements = numbers.reduce(
	( accumulator, currentValue, index ) => {
		console.log('Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index);
		return accumulator + currentValue;
	},
	initialValue
);
console.log( sumElements );

Det følgende billede viser outputtet i browserkonsollen:

using reduce() med startværdien sat til 5
using reduce() med startværdien sat til 5

Lad os nu finde ud af, hvad der sker uden parameteren initialValue:

const numbers = [1, 2, 3, 4, 5];
const sumElements = numbers.reduce(
	( accumulator, currentValue, index ) => {
		console.log( 'Accumulator: ' + accumulator + ' - currentValue: ' + currentValue + ' - index: ' + index );
		return accumulator + currentValue;
	}
);
console.log( sumElements );
Brug af reduce() uden startværdi
Brug af reduce() uden startværdi

Flere eksempler og brugsscenarier er beskrevet på mdn web docs hjemmeside.

Eksport og import

Fra og med ECMAScript 2015 (ES6) er det muligt at eksportere værdier fra et JavaScript-modul og importere dem til et andet script. Du kommer til at bruge import og eksport flittigt i dine React-applikationer, og derfor er det vigtigt at have en god forståelse af, hvordan de fungerer.

Den følgende kode opretter en funktionel komponent. Den første linje importerer React-biblioteket:

import React from 'react';

function MyComponent() {
	const person = {
		name: 'Carlo',
		avatar: 'https://en.gravatar.com/userimage/954861/fc68a728946aac04f8531c3a8742ac22?size=original',
		description: 'Content Writer',
		theme: 'dark'
	}
 
	return (
		<div
			className = 'card'
			style = {
				person.theme === 'dark' ?
				{ background: 'black', color: 'white' } :
				{ background: 'white', color: 'black'}
			}>
			<img
				src = { person.avatar }
				alt = { person.name }
				style = { { width: '100%' } }
			/>
			<div
				style = { { padding: '2px 16px' } }
			>
				<h3>{ person.name }</h3>
				<p>{ person.description }.</p>
			</div>
		</div>
	);
}
export default MyComponent;

Vi brugte nøgleordet import efterfulgt af det navn, vi ønsker at tildele det, vi importerer, efterfulgt af navnet på den pakke, vi ønsker at installere, som der henvises til i package.json-filen.

Bemærk, at vi i MyComponent() -funktionen ovenfor brugte nogle af de JavaScript-funktioner, der blev diskuteret i de foregående afsnit. Vi inkluderede egenskabsværdier i krøllede parenteser og tildelte værdien af style -egenskaben ved hjælp af den betingede operatørsyntaks.

Scriptet slutter med eksport af vores brugerdefinerede komponent.

Nu hvor vi ved lidt mere om import og eksport, så lad os se nærmere på, hvordan de fungerer.

Eksport

Hvert React-modul kan have to forskellige typer eksport: navngivet eksport og standardeksport.

Du kan f.eks. eksportere flere funktioner på én gang med en enkelt export erklæring:

export { MyComponent, MyVariable };

Du kan også eksportere individuelle funktioner (function, class, const, let):

export function MyComponent() { ... };
export let myVariable = x + y;

Men du kan kun have en enkelt standardeksport:

export default MyComponent;

Du kan også bruge standardeksport til individuelle funktioner:

export default function() { ... }
export default class { ... }

Import

Når komponenten er blevet eksporteret, kan den importeres til en anden fil, f.eks. en index.js-fil, sammen med andre moduler:

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import MyComponent from './MyComponent';

const root = ReactDOM.createRoot( document.getElementById( 'root' ) );
root.render(
	<React.StrictMode>
		<MyComponent />
	</React.StrictMode>
);

I koden ovenfor brugte vi importerklæringen på flere måder.

I de to første linjer tildelte vi et navn til de importerede ressourcer, i den tredje linje tildelte vi ikke et navn, men importerede simpelthen ./index.css-filen. Den sidste erklæring import importerer filen ./MyComponent og tildeler den et navn.

Lad os finde ud af forskellene mellem disse importer.

I alt er der fire typer af import:

Navngivet import

import { MyFunction, MyVariable } from "./my-module";

Standard-import

import MyComponent from "./MyComponent";

Import af name space

import * as name from "my-module";

Import af side effect

import "module-name";

Når du har tilføjet et par stilarter i din index.css, skulle dit kort se ud som på billedet nedenfor, hvor du også kan se den tilsvarende HTML-kode:

Et simpelt React-komponent
Et simpelt React-komponent

Bemærk, at import erklæringer kun kan bruges i moduler på øverste niveau (ikke inde i funktioner, klasser osv.).

Hvis du vil have et mere omfattende overblik over import og export erklæringer, kan du også tjekke følgende ressourcer:

Opsummering

React er et af de mest populære JavaScript-biblioteker i dag og er en af de mest efterspurgte færdigheder inden for webudvikling.

Med React er det muligt at skabe dynamiske webapplikationer og avancerede grænseflader. Det er nemt at skabe store, dynamiske og interaktive applikationer takket være de genanvendelige komponenter.

Men React er et JavaScript-bibliotek, og en god forståelse af de vigtigste funktioner i JavaScript er afgørende for at starte din rejse med React. Derfor har vi samlet nogle af de JavaScript-funktioner, der oftest bruges i React, på ét sted. At mestre disse funktioner vil give dig et forspring på din React-læringsrejse.

Og når det kommer til webudvikling, kræver det kun en lille indsats at gå fra JS/React til WordPress.

Nu er det din tur, hvilke JavaScript-funktioner synes du er mest nyttige i React-udvikling? Har vi overset noget vigtigt, som du gerne ville have set på vores liste? Del dine tanker med os i kommentarerne nedenfor.

Carlo Daniele Kinsta

Carlo is a passionate lover of webdesign and front-end development. He has been playing with WordPress for more than 20 years, also in collaboration with Italian and European universities and educational institutions. He has written hundreds of articles and guides about WordPress, published both on Italian and international websites, as well as on printed magazines. You can find him on LinkedIn.