React är idag ett av de mest populära JavaScript-biblioteken. Det kan exempelvis användas för att skapa dynamiska och responsiva applikationer, ger bättre prestanda och kan dessutom enkelt utökas. Den underliggande logiken bygger på komponenter som kan återanvändas i olika sammanhang. Som ett resultat minskar behovet av att skriva samma kod flera gånger. Kort sagt, med React kan du skapa effektiva och kraftfulla applikationer.

Så det har aldrig funnits ett bättre tillfälle att lära sig hur man skapar React-applikationer.

Men utan en gedigen förståelse för vissa viktiga JavaScript-funktioner kan det vara svårt eller till och med omöjligt att bygga React-applikationer.

Därför har vi sammanställt en lista över JavaScript-funktioner och begrepp som du behöver känna till innan du börjar använda React. Ju bättre du förstår dessa begrepp, desto lättare blir det för dig att bygga professionella React-applikationer.

Med detta sagt, här är vad vi kommer att diskutera i den här artikeln:

JavaScript och ECMAScript

JavaScript är ett populärt skriptspråk. Det används tillsammans med HTML och CSS för att bygga dynamiska webbsidor. HTML används för att skapa strukturen på en webbsida och CSS för att skapa stil och layout för dess element. JavaScript är istället det språk som används för att lägga till beteende på sidan, dvs. för att skapa funktionalitet och interaktivitet.

Språket har sedan dess anammats av de stora webbläsarna och det har skapats ett dokument för att beskriva hur JavaScript var tänkt att fungera: ECMAScript-standarden.

Från och med år 2015 släpps en uppdatering av ECMAScript-standarden årligen. Det läggs därför till nya funktioner i JavaScript varje år.

ECMAScript 2015 var den sjätte versionen av standarden och kallas därför även ES6. Efterföljande versioner är markerade med en progression, så vi refererar till ECMAScript 2016 som ES7, ECMAScript 2017 som ES8, och så vidare.

Eftersom nya funktioner ofta läggs till i standarden kan det hända att vissa inte stöds i alla webbläsare. Så hur kan du se till att de senaste JavaScript-funktionerna som du har lagt till i din JS-app kommer att fungera som förväntat?

Alternativ för att funktionerna ska fungera som väntat

Du har tre alternativ:

  1. Vänta tills alla större webbläsare har stöd för de nya funktionerna. Om du absolut behöver den fantastiska nya JS-funktionen för din app är detta dock inte ett alternativ.
  2. Använd en Polyfill, som är ”en kodbit (vanligtvis JavaScript på webben). Den används för att tillhandahålla modern funktionalitet i äldre webbläsare som inte har inbyggt stöd för detta (se även mdn web docs).
  3. Använd en JavaScript-transpiler som Babel eller Traceur, som konverterar ECMAScript 2015+-kod till en JavaScript-version som stöds av alla webbläsare.

Statement vs Expressions

Att förstå skillnaden mellan statements och expressions är viktigt när man bygger React-applikationer. Så låt oss gå tillbaka till de grundläggande begreppen inom programmering för ett ögonblick.

Ett datorprogram är en lista med instruktioner som ska utföras av en dator. Dessa instruktioner kallas för statements.

Till skillnad från satser är expressions fragment av kod som producerar ett värde. I ett statement är ett uttryck en del som returnerar ett värde och vi ser det vanligtvis på höger sida av ett likhetstecken.

Medan:

JavaScript-satser kan vara block eller rader av kod som vanligtvis avslutas med semikolon eller omsluts av hakparenteser.

Här är ett enkelt exempel på ett statement i JavaScript:

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

Uttalandet ovan skriver "Hello World!" i ett DOM-element med id="hello".

Expressions producerar ett värde eller är ett värde

Som vi redan nämnt producerar expessions ett värde eller är själva ett värde. Betrakta följande exempel:

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

document.getElementById("hello").value är ett expression eftersom det returnerar ett värde.

Ett ytterligare exempel bör hjälpa till att klargöra skillnaden mellan expressions och statements:

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

I exemplet ovan,

  • är den första raden ett statement, där "Hello World!" är ett uttryck,
  • funktionsdeklarationen är en sats, där parametern msg som skickas till funktionen är ett expression,
  • raden som skriver ut meddelandet i konsolen är en sats, där parametern msg återigen är ett expression.

Varför expressions är viktiga i React

När du bygger en React-applikation kan du injicera JavaScript-expressions i din JSX-kod. Du kan exempelvis skicka en variabel, skriva en händelsehanterare eller ett villkor. För att göra detta måste du inkludera din JS-kod i lockiga parenteser.

Du kan exempelvis skicka en variabel:

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

Kort sagt, de lockiga hakparenteserna talar om för din transpiler att den kod som är inslagen i hakparenteser ska behandlas som JS-kod. Allt som kommer före den inledande <p>-taggen och efter den avslutande </p>-taggen är normal JavaScript-kod. Allt som kommer innanför den inledande <p>-taggen och den avslutande </p>-taggen behandlas som JSX-kod.

Här är ett annat exempel:

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

Du kan dessutom skicka ett 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>
	);
}

Och nedan finns ett mer omfattande exempel:

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>
	);
}

Dubbla lockiga parenteser

Lägg märke till de dubbla lockiga parenteserna i style-attributen i elementen img och div. Vi använde dubbla parenteser för att skicka två objekt som innehåller kort- och bildstilar.

Ett exempel på ett kort som är byggt med React
Ett exempel på ett kort som är byggt med React

Du bör ha lagt märke till att vi inkluderade JavaScript-uttryck i JSX i alla exemplen ovan.

Mutability i React

Mutability och Immutability är två nyckelbegrepp inom objektorienterad och funktionell programmering.

Immutability innebär att ett värde inte kan ändras efter att det har skapats. Mutability innebär naturligtvis motsatsen.

I Javascript är primitiva värden oföränderliga, vilket innebär att när ett primitivt värde har skapats kan det inte ändras. Arrayer och objekt är däremot mutable eftersom deras egenskaper och element kan ändras utan att ett nytt värde tilldelas.

Det finns flera anledningar till att använda oföränderliga objekt i JavaScript:

  • Förbättrad prestanda
  • Minskad minnesförbrukning
  • Tråd-säkerhet
  • Enklare kodning och felsökning

Enligt mönstret för oföränderlighet kan en variabel eller ett objekt inte tilldelas på nytt eller ändras när den eller det har tilldelats. När du behöver ändra data bör du därför skapa en kopia av den och ändra dess innehåll så att det ursprungliga innehållet förblir oförändrat.

Immutability är dessutom ett nyckelkoncept i React.

react-dokumentationen

I React-dokumentationen anges följande:

Tillståndet för en klasskomponent är tillgängligt som this.state. Statusfältet måste vara ett objekt. Ändra inte tillståndet direkt. Om du vill ändra tillståndet anropar du setState med det nya tillståndet.

När en komponents tillstånd ändras beräknar React om komponenten ska renderas på nytt och uppdatera den virtuella DOM: en. Om React inte har koll på det tidigare tillståndet kan det dock inte avgöra om komponenten ska renderas på nytt eller inte. React-dokumentationen ger ett utmärkt exempel på detta.

Vilka JavaScript-funktioner kan vi använda för att garantera oföränderligheten hos tillstånds-objektet i React? Låt oss ta reda på detta!

Deklarera variabler

Det finns tre sätt att deklarera en variabel i JavaScript: var, let, och const.

Satsenvar  har funnits sedan JavaScript skapades. Det används för att deklarera en funktions-omfattande eller globalt omfattande variabel och eventuellt initiera den till ett värde.

När du deklarerar en variabel med var kan du dessutom omdeklarera och uppdatera variabeln både i den globala och lokala omfattningen. Följande kod är tillåten:

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

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

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

var-deklarationer behandlas innan någon kod exekveras. Att deklarera en variabel var som helst i koden är därför detsamma som att deklarera den högst upp. Detta beteende kallas för hoisting.

Det är värt att notera att det bara är variabeldeklarationen som hoistas, inte initialiseringen. Den sker endast när kontrollflödet når assignment-satsen. Fram till dess är variabeln undefined:

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

Omfattningen av en var som deklareras i en JS-funktion är hela funktionens kropp.

Som ett resultat definieras variabeln inte på blocknivå, utan på hela funktionens nivå. Detta leder till ett antal problem som kan göra din JavaScript-kod buggig och svår att underhålla.

För att åtgärda dessa problem introducerade ES6 nyckelordetlet.

Deklarationen let deklarerar en block-omfattande lokal variabel och initierar den eventuellt till ett värde

.Fördelar med let jämfört med var

Vilka är fördelarna med let jämfört med var? De är exempelvis följande:

  • let deklarerar en variabel för ett block medan var deklarerar en variabel globalt eller lokalt för en hel funktion oavsett blockets omfattning.
  • Globala let-variabler är inte egenskaper hos window-objektet. Du kan inte komma åt dem med window.variableName.
  • let kan endast nås efter att dess deklaration har nåtts. Variabeln initialiseras inte förrän kontrollflödet når den kodrad där den deklareras (let-deklarationer är icke-hoistade).
  • Om du deklarerar en variabel på nytt med let uppstår ett SyntaxError.

Eftersom variabler som deklareras med var inte kan block-omfattas, kan en variabel som definieras med var i en loop eller inuti en if-sats nås utanför blocket. Som ett resultat kan det uppstå en buggig kod.

Koden i det första exemplet exekveras utan fel. Ersätt nu var med let i kodblocket ovan:

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

I det andra exemplet ger let istället för var ett Uncaught ReferenceError:

Oupptäckt referensfel i Chrome
Oupptäckt referensfel i Chrome

ES6 introducerar dessutom ett tredje nyckelord: const.

const är ganska likt let, men med en viktig skillnad:

Betrakta följande exempel:

const MAX_VALUE = 1000;
MAX_VALUE = 2000;

Ovanstående kod skulle generera följande TypeError:

Oupptäckt typfel: Tilldelning till konstant variabel i Google Chrome
Oupptäckt typfel: Tilldelning till konstant variabel i Google Chrome

Dessutom:

Att deklarera en const utan att ge den ett värde skulle leda till följande SyntaxError (se även ES6 In Depth: let och const):

Oupptäckt SyntaxError: Initialiseraren saknas i const-deklarationen i Chrome
Oupptäckt SyntaxError: Initialiseraren saknas i const-deklarationen i Chrome

Redigera egenskapar eller objekt

Men om en konstant är en array eller ett objekt kan du redigera egenskaper eller objekt i den arrayen eller det objektet.

Du kan exempelvis ändra, lägga till och ta bort arrayobjekt:

// 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 får inte tilldela arrayen på nytt:

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

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

Koden ovan skulle resultera i ett TypeError.

Oupptäckt typfel: Tilldelning till konstant variabel i Chrome
Oupptäckt typfel: Tilldelning till konstant variabel i Chrome

Du kan lägga till, omfördela och ta bort egenskaper och metoder för objekt:

// 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'}

Du kan inte omfördela själva objektet

Men det är inte tillåtet att omfördela själva objektet. Följande kod skulle gå igenom 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'
};

Objekt.freeze()

Vi är nu överens om att användningen av const inte alltid garanterar en stark oföränderlighet (särskilt när man arbetar med objekt och arrayer). Så hur kan du implementera oföränderlighetsmönstret i dina React-applikationer?

När du vill förhindra att elementen i en array eller egenskaperna hos ett objekt ändras kan du använda den statiska metoden Object.freeze().

Att frysa ett objekt förhindrar tillägg och gör befintliga egenskaper icke-skrivbara och icke-konfigurerbara. Ett fryst objekt kan därför inte längre ändras. Nya egenskaper kan inte läggas till och befintliga egenskaper kan inte tas bort. Deras uppräkningsbarhet, konfigurerbarhet, skrivbarhet eller värde kan heller inte ändras och objektets prototyp kan inte tilldelas på nytt. freeze() returnerar samma objekt som det som skickades in.

Alla försök att lägga till, ändra eller ta bort en egenskap misslyckas, antingen tyst eller genom att kasta en TypeError, vanligast i strikt läge.

Du kan använda Object.freeze() på detta sätt:

'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);

Om du nu försöker att lägga till en egenskap kommer du att få ett Uncaught TypeError:

// Add a new property
post.slug = "javascript-is-awesome"; // Uncaught TypeError
Oupptäckt TypeError: kan inte definiera egenskapen "slug": Objektet är inte utbyggbart i Firefox
Oupptäckt TypeError: kan inte definiera egenskapen ”slug”: Objektet är inte utbyggbart i Firefox

När du försöker att omfördela en egenskap får du en annan typ av TypeError:

// Reassign property
post.id = 5; // Uncaught TypeError
Omplacering av en skrivskyddad egenskap ger ett ofångat typfel
Omplacering av en skrivskyddad egenskap ger ett ofångat typfel
Oupptäckt typfel: Kan inte tilldela skrivskyddad egenskap 'id' för objektet '#<Object>' i Google Chrome
Oupptäckt typfel: Kan inte tilldela skrivskyddad egenskap ’id’ för objektet ’#<Object>’ i Google Chrome

Du kan dessutom försöka att ta bort en egenskap. Resultatet blir ett annat TypeError:

// Delete a property
delete post.excerpt; // Uncaught TypeError
Typfel: egenskapen "excerpt" är inte konfigurerbar och kan inte tas bort i Firefox
Typfel: egenskapen ”excerpt” är inte konfigurerbar och kan inte tas bort i Firefox

Literala mallar

När du behöver kombinera strängar med resultatet av uttryck i JavaScript använder du vanligtvis adderings-operatören +. Du kan dock även använda en JavaScript-funktion som gör att du kan inkludera uttryck i strängar utan att använda adderings-operatören: Template Literals.

Template Literals är en speciell typ av strängar som avgränsas med backtick (`).

I Template Literals kan du inkludera platshållare. Det är inbäddade uttryck som avgränsas med ett dollartecken och omsluts av krulliga parenteser.

Här är ett exempel:

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

Strängarna och platshållarna skickas till en standardfunktion som utför stränginterpolation. Som ett resultat ersätter de platshållarna och sammanfogar delarna till en enda sträng. Du kan dessutom ersätta standardfunktionen med en anpassad funktion.

Använd Template Literals för:

Strängar med flera rader: Tecken för nya rader är en del av Template Literals.

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

Interpolering av strängar: Utan Template Literals kan du bara använda adderings-operatören för att kombinera utdata från uttryck med strängar. Se följande exempel:

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

Skriv koden på ett mer lättläst sätt

Det är lite förvirrande, eller hur? Men du kan skriva koden på ett mer lättläst och underhållbart sätt med hjälp av Template Literals:

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

Kom dock ihåg att det finns en skillnad mellan de två syntaxerna:

Template Literals lämpar sig för flera användningsområden. I följande exempel använder vi en ternär operator för att tilldela ett värde till ett class-attribut.

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

Nedan utför vi en enkel beräkning:

const price = 100;
const VAT = 0.22;

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

Det är dessutom möjligt att kapsla in Template Literals genom att inkludera dem i en ${expression} platshållare. (Använd dock kapslade mallar med försiktighet eftersom komplexa strängstrukturer kan vara svåra att läsa och underhålla).

Taggade mallar: Som vi nämnde ovan är det dessutom möjligt att definiera en anpassad funktion för att utföra sträng-sammanfogning. Denna typ av litterär mall kallas för taggad mall.

Med taggar kan du analysera Template Literals med en funktion. Det första argumentet i en taggfunktion innehåller en array med strängvärden. De återstående argumenten är relaterade till uttrycken.

Analysera Template Literals

Med taggar kan du analysera Template Literals med en anpassad funktion. Det första argumentet i den här funktionen är en array med de strängar som ingår i Template Literal, de andra argumenten är uttrycken.

Du kan skapa en anpassad funktion för att utföra vilken typ av operation som helst på mallargumenten och returnera den manipulerade strängen. Här är ett mycket grundläggande exempel på en taggad mall:

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 ovan skriver ut arrayelementen strings och tags och kapitaliserar sedan strängtecknen innan utdata skrivs ut i webbläsarkonsolen.

Pil-funktioner

Pil-funktioner är ett alternativ till anonyma funktioner (funktioner utan namn) i JavaScript, men med vissa skillnader och begränsningar.

Följande deklarationer är alla giltiga exempel på pil-funktioner:

// 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
}

Sätt två eller fler parametrar inom parentes

Du kan utelämna de runda parenteserna om du bara skickar en parameter till funktionen. Om du skickar två eller fler parametrar måste du sätta dem inom parentes. Här är ett exempel på detta:

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

Enradiga pilfunktioner returnerar ett värde som standard. Om du använder syntaxen med flera rader måste du returnera ett värde manuellt:

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

En viktig skillnad mellan normala funktioner och pil-funktioner är att pil-funktioner inte har några egna bindningar till nyckelordet this. Om du försöker använda this i en pil-funktion kommer det att hamna utanför funktionens omfattning.

För en mer ingående beskrivning av Arrow-funktioner och exempel på användning, läs även mdn web docs.

Klasser

Klasser i JavaScript är en speciell typ av funktion för att skapa objekt som använder den prototypiska arvsmekanismen.

Enligt mdn web docs,

När det gäller arv har JavaScript bara en konstruktion: objekt. Varje objekt har en privat egenskap som innehåller en länk till ett annat objekt som kallas dess prototyp. Det prototypobjektet har en egen prototyp, och så vidare tills man når ett objekt med null som prototyp.

Precis som med funktioner finns det två sätt att definiera en klass:

  • Ett klassuttryck
  • En klassdeklaration

Du kan exempelvis använda nyckelordet class för att definiera en klass inuti ett uttryck, vilket visas i följande exempel:

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

Definiera klassmedlemmar

En klass har en body, det vill säga den kod som ingår i de lockiga hakparenteserna. Här definierar du konstruktörer och metoder, som dessutom kallas klassmedlemmar. Klassens kropp exekveras i strikt läge även utan att använda 'strict mode'-direktivet.

Metoden constructor används för att skapa och initiera ett objekt som skapats med en klass och körs automatiskt när klassen instansieras. Om du inte definierar en konstruktörsmetod i din klass kommer JavaScript automatiskt att använda en standardkonstruktör.

En klass kan utökas med hjälp av nyckelordet 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 använda nyckelordet super för att anropa den överordnade konstruktören. Om du skickar ett argument till super()-metoden kommer detta argument också att finnas tillgängligt i den överordnade konstruktörsklassen.

För en djupare genomgång av JavaScript-klasser och flera exempel på användning, se även mdn web docs.

Utöka inbyggda React-klasser

Klasser används ofta för att skapa React-komponenter. Vanligtvis skapar du inte dina egna klasser utan utökar snarare inbyggda React-klasser.

Alla klasser i React har en render()-metod som returnerar ett React-element:

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

I exemplet ovan är Animal en klasskomponent. Tänk på att

  • Namnet på komponenten måste börja med en stor bokstav
  • Komponenten måste innehålla uttrycket extends React.Component. Detta ger sedan tillgång till metoderna i React.Component.
  • Metoden render() returnerar HTML och är obligatorisk.

När du har skapat din klasskomponent kan du rendera HTML på sidan:

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

Bilden nedan visar resultatet på sidan (Du kan se det i aktion på CodePen).

En enkel React klass-komponent
En enkel React klass-komponent

Observera dock att det inte är rekommenderat att använda klasskomponenter i React och att det är bättre att definiera komponenter som funktioner.

Nyckelordet this

I JavaScript är nyckelordet this en generisk platshållare som vanligtvis används inuti objekt, klasser och funktioner. Det hänvisar till olika element beroende på sammanhang eller omfattning.

this kan användas i det globala omfånget. Om du skriver this i webbläsarens konsol får du:

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

Du kan komma åt alla metoder och egenskaper för Window-objektet. Om du kör this.location i webbläsarens konsol får du följande utdata:

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

När du använder this i ett objekt hänvisar det till själva objektet. Som ett resultat kan du hänvisa till värdena i ett objekt i objektets egna metoder:

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

Låt oss nu försöka använda this i en funktion:

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

Om du inte är i strikt läge får du:

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

Men om du använder strikt läge får du ett annat resultat:

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

Funktionen undefined

I det här fallet returnerar funktionen undefined. Det beror på att this i en funktion hänvisar till dess explicita värde.

Så hur anger man this explicit i en funktion?

Du kan exempelvis tilldela egenskaper och metoder till funktionen manuellt:

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 dessutom använda metoderna call, apply och bind samt pil-funktioner.

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

Metoden call() kan användas på vilken funktion som helst och gör precis vad den säger: den anropar funktionen.

Dessutom accepterar call() alla andra parametrar som definieras 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() );

Pil-funktioner

Ett alternativ till de alternativ som diskuteras ovan är att använda pil-funktioner.

Pil-funktionsuttryck bör endast användas för icke-metodfunktioner eftersom de inte har någon egen this.

Detta gör pil-funktioner särskilt användbara med händelsehanterare.

Det beror på att ”när koden anropas från ett inbundet händelsehanterarattribut, sätts dess this till det DOM-element där lyssnaren är placerad” (se mdn web docs).

Men saker och ting förändras med pil-funktioner eftersom…

… pil-funktioner skapar this baserat på den omfattning som pilfunktionen definieras inom, och this-värdet ändras inte baserat på hur funktionen anropas.

Binda ”this” till händelsehanterare i React

När det gäller React finns det några sätt att se till att händelsehanteraren inte förlorar sin kontext:

1. Använda bind() inuti 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. Binda kontexten till händelsehanteraren 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. Definiera händelsehanteraren med hjälp av pil-funktioner:

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. Använda pil-funktioner i renderingsmetoden:

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;

Oavsett vilken metod som du väljer visas följande resultat i webbläsarkonsolen när du klickar på knappen:

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

Ternär operatör

Med den villkorliga operatören (eller ternära operatören) kan du skriva enkla villkorliga uttryck i JavaScript. Den tar tre operander:

  • ett villkor följt av ett frågetecken (?),
  • ett uttryck som ska exekveras om villkoret är sant följt av ett semikolon (:),
  • ett andra uttryck som ska exekveras om villkoret är falsy.
const drink = personAge >= 18 ? "Wine" : "Juice";

Det är dessutom möjligt att kedja flera uttryck:

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

Var dock försiktig, eftersom kedjning av flera uttryck kan leda till en rörig kod som är svår att underhålla.

Den ternära operatören är särskilt användbar i React. Detta gäller särskilt i din JSX-kod, som endast accepterar uttryck inom hakparenteser.

Du kan exempelvis använda den ternära operatören för att ställa in värdet på ett attribut baserat på ett specifikt villkor:

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 ovan kontrollerar vi villkoret person.theme === 'dark' för att ställa in värdet på style-attributet i containern div.

Utvärdering av kortslutning

Den logiska operatorn AND (&&) utvärderar operanderna från vänster till höger och returnerar true om och endast om alla operander är true.

Den logiska AND är en kortslutningsoperatör. Varje operand omvandlas till en boolean. Om resultatet av omvandlingen är false, stannar AND-operatören och returnerar det ursprungliga värdet för den falska operanden. Om alla värden är true, returneras originalvärdet för den sista operanden.



Kortslutnings-utvärdering är en JavaScript-funktion som ofta används i React eftersom den gör det möjligt att mata ut kodblock baserat på specifika villkor. Här är ett exempel:

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

I koden ovan returnerar React det slutliga JSX-blocket om displayExcerpt AND post.excerpt.rendered utvärderas till true,.

För att sammanfatta, ”om villkoret är true, kommer elementet direkt efter && att visas i utmatningen. Om det är false kommer React att ignorera och hoppa över det”.

Syntax för spridning

I JavaScript kan du med spread syntax expandera ett itererbart element, t.ex. en array eller ett objekt, till funktionsargument, array-literaler eller objekt-literaler.

I följande exempel packar vi upp en array i ett funktionsanrop:

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 använda spread-syntaxen för att duplicera en array (även flerdimensionella arrayer) eller för att sammanfoga arrayer. I följande exempel sammanfogar vi två arrayer på två olika sätt:

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 dessutom använda spread-syntaxen för att klona eller slå samman två objekt:

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 } );

Destrukturerande tilldelning

En annan syntaktisk struktur som ofta används i React är syntaxen för destrukturerande tilldelning.

I följande exempel packar vi upp värden från en array:

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

Och här är ett enkelt exempel på destruktureringstilldelning med ett 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 dessutom göra ännu mer. I följande exempel packar vi upp vissa egenskaper hos ett objekt och tilldelar de återstående egenskaperna till ett annat objekt med hjälp av den spridda syntaxen:

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 dessutom tilldela värden till en array:

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

Observera att parenteserna runt tilldelningssatsen krävs när du använder objektlitterär destruktureringstilldelning utan en deklaration.

För en mer djupgående analys av destrukturerande tilldelning, med flera exempel på användning, hänvisar vi till mdn web docs.

filter(), map() och reduce()

JavaScript tillhandahåller flera användbara metoder som ofta används i React.

filter()

I följande exempel tillämpar vi filtret på numbers-arrayen för att få en array vars element är tal större än 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 följande exempel får vi en array med inlägg där ordet ”JavaScript” ingår i titeln:

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 );
En array av inlägg där titeln innehåller "JavaScript”
En array av inlägg där titeln innehåller ”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 hittar du ofta map()-metoden som används för att bygga listor. I följande exempel mappar vi WordPress posts-objektet för att bygga en lista över inlägg:

<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>

reducera()

reduce() accepterar två parametrar:

  • En callback-funktion som ska utföras för varje element i arrayen. Den returnerar ett värde som blir värdet på ackumulatorparametern vid nästa anrop. Vid det sista anropet returnerar funktionen det värde som kommer att vara returvärdet för reduce().
  • Ett initialvärde som är det första värdet i den ackumulator som skickas till callback-funktionen.

Callback-funktionen tar några parametrar:

  • En ackumulator: Det värde som returnerades från föregående anrop till callback-funktionen. Vid det första anropet sätts detta till ett initialt värde om det anges. Annars tar den värdet för det första elementet i arrayen.
  • Värdet för det aktuella elementet: Värdet sätts till det första elementet i arrayen (array[0]) om ett initialvärde har angetts. Annars tar den värdet av det andra elementet (array[1]).
  • Det aktuella indexet är indexpositionen för det aktuella elementet.

Ett exempel kommer att göra allt tydligare.

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

Vad som händer vid varje iteration

Låt oss ta reda på i detalj vad som händer vid varje iteration. Gå tillbaka till föregående exempel och ändra 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 );

Följande bild visar utdata i webbläsarkonsolen:

Använder reduce() med initialvärdet satt till 5
Använder reduce() med initialvärdet satt till 5

Låt oss nu ta reda på vad som händer utan parametern 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 );
Använda reduce() utan initialvärde
Använda reduce() utan initialvärde

Fler exempel och användningsområden diskuteras på webbplatsen mdn web docs.

Export och import

Från och med ECMAScript 2015 (ES6) är det möjligt att exportera värden från en JavaScript-modul och importera dem till ett annat skript. Du kommer att använda import och export i stor utsträckning i dina React-applikationer. Det är därför viktigt att ha en god förståelse för hur de fungerar.

Följande kod skapar en funktionell komponent. Den första raden importerar 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 använde nyckelordet import följt av det namn som vi vill tilldela det vi importerar. Därefter kommer namnet på det paket som vi vill installera eftersom det hänvisas till i filen package.json.

Observera att vi i MyComponent()-funktionen ovan använde några av de JavaScript-funktioner som diskuterades i de tidigare avsnitten. Vi inkluderade egenskapsvärden i hakparenteser och tilldelade värdet för style-egenskapen med hjälp av syntaxen för villkorliga operatörer.

Skriptet avslutas med export av vår anpassade komponent.

Nu när vi vet lite mer om import och export ska vi ta en närmare titt på hur de fungerar.

Exportera

Varje React-modul kan ha två olika typer av export: namngiven export och standardexport.

Det går exempelvis att exportera flera funktioner samtidigt med ett enda export-statement:

export { MyComponent, MyVariable };

Du kan dessutom exportera enskilda funktioner (function, class, const, let):

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

Men du kan bara ha en enda standardexport:

export default MyComponent;

Det går även att använda standardexport för enskilda funktioner:

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

Importera

När komponenten har exporterats kan den importeras till en annan fil, exempelvis en index.js-fil, tillsammans med andra 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 ovan använde vi importdeklarationen på flera olika sätt.

För de två första raderna tilldelade vi de importerade resurserna ett namn. I den tredje raden tilldelade vi inte ett namn utan importerade helt enkelt filen ./index.css. Den sista import-satsen importerar filen ./MyComponent och tilldelar ett namn.

Låt oss ta reda på skillnaderna mellan dessa importer.

Det finns totalt fyra typer av import:

Namngiven import

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

Standardimport

import MyComponent from "./MyComponent";

Import av namnrymd

import * as name from "my-module";

Import av bieffekter

import "module-name";

När du har lagt till några stilar i din index.css bör ditt kort se ut som i bilden nedan, där du dessutom kan se motsvarande HTML-kod:

En enkel React-komponent
En enkel React-komponent

Observera att import-deklarationer endast kan användas i moduler på toppnivå (inte inuti funktioner, klasser osv.).

För en mer omfattande översikt över import-statements och export-statements kan du dessutom kontrollera följande resurser:

Sammanfattning

React är ett av de mest populära JavaScript-biblioteken idag och är en av de mest efterfrågade färdigheterna inom webbutveckling.

Med React är det exempelvis möjligt att skapa dynamiska webbapplikationer och avancerade gränssnitt. Att skapa stora, dynamiska och interaktiva applikationer kan vara enkelt tack vare dess återanvändbara komponenter.

Men React är ett JavaScript-bibliotek. En god förståelse för de viktigaste funktionerna i JavaScript är därför avgörande för att starta din resa med React. Med detta i åtanke har vi samlat några av de JavaScript-funktioner som oftast används i React på ett och samma ställe. Att behärska dessa funktioner kommer att ge dig ett försprång på din React-inlärningsresa.

Och när det gäller webbutveckling är steget från JS/React till WordPress väldigt enkelt.

Nu är det din tur, vilka JavaScript-funktioner tycker du är mest användbara i React-utveckling? Har vi missat något viktigt som du skulle ha velat se på vår lista? Dela dina tankar med oss i kommentarerna nedan.

Carlo Daniele Kinsta

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