1. ES6 Syntax: React is written in ES6, so it’s important to understand the new syntax and features of ES6.
2. Arrow Functions: Arrow functions are a great way to write concise and clean code. They are used extensively in React and you should be familiar with them.
3. Destructuring: Destructuring is a great way to extract data from objects and arrays. It’s used in React to make code more concise and readable.
4. Classes: Classes are used to create React components. You should be familiar with the syntax and how to use them.
5. Props: Props are used to pass data from one component to another. You should understand how to use props and how to pass data between components.
6. State: State is used to store data in a component. You should understand how to use state and how to update it.
7. Lifecycle Methods: Lifecycle methods are used to control the behavior of a component. You should understand how to use them and when to use them.
8. Event Handlers: Event handlers are used to handle user interactions. You should understand how to use them and how to pass data between components.
9. JSX: JSX is a syntax extension to JavaScript that allows you to write HTML-like code in JavaScript. You should understand how to use it and how to write React components using JSX.
These days, React is one of the most popular JavaScript libraries. It can be used to create dynamic and responsive applications, allows for better performance, and can be easily extended. The underlying logic is based on components that can be reused in different contexts, reducing the need to write the same code several times. In short, with React you can create efficient and powerful applications.
So there has never been a better time to learn how to create React applications.
However, without a solid understanding of some key JavaScript features, building React applications might be difficult or even impossible.
For this reason, we have compiled a list of JavaScript features and concepts that you need to know before getting started with React. The better you understand these concepts, the easier it will be for you to build professional React applications.
That being said, here is what we will discuss in this article:
JavaScript and ECMAScript
JavaScript is a popular scripting language used together with HTML and CSS to build dynamic web pages. While HTML is used to create the structure of a web page and CSS to create the style and layout of its elements, JavaScript is the language used to add behavior to the page, i.e. to create functionality and interactivity.
The language has since been adopted by the major browsers and a document was written to describe the way JavaScript was intended to work: the ECMAScript standard.
As of 2015, an update of the ECMAScript standard is released annually, and thus new features are added to JavaScript every year.
ECMAScript 2015 was the sixth release of the standard and is therefore also known as ES6. Following versions are marked in progression, so we refer to ECMAScript 2016 as ES7, ECMAScript 2017 as ES8, and so on.
Due to the frequency with which new features are added to the standard, some may not be supported in all browsers. So how could you make sure that the latest JavaScript features you added to your JS app would work as expected across all web browsers?
You have three options:
- Wait until all major browsers provide support for the new features. But if you absolutely need that amazing new JS feature for your app, this is not an option.
- Use a Polyfill, which is “a piece of code (usually JavaScript on the Web) used to provide modern functionality on older browsers that do not natively support it” (see also mdn web docs).
- Use a JavaScript transpiler such as Babel or Traceur, that convert ECMAScript 2015+ code into a JavaScript version that is supported by all browsers.
Statements vs Expressions
Understanding the difference between statements and expressions is essential when building React applications. So, let us go back to the basic concepts of programming for a moment.
A computer program is a list of instructions to be executed by a computer. These instructions are called statements.
Unlike statements, expressions are fragments of code that produce a value. In a statement, an expression is a part that returns a value and we usually see it on the right side of an equal sign.
Whereas:
JavaScript statements can be blocks or lines of code that usually end with semicolons or are enclosed in curly brackets.
Here is a simple example of a statement in JavaScript:
document.getElementById("hello").innerHTML = "Hello World!";
The statement above writes "Hello World!"
in a DOM element with id="hello"
.
As we already mentioned, expessions produce a value or are themselves a value. Consider the following example:
msg = document.getElementById("hello").value;
document.getElementById("hello").value
is en expression as it returns a value.
An additional example should help clarify the difference between expressions and statements:
const msg = "Hello World!";
function sayHello( msg ) {
console.log( msg );
}
In the example above,
- the first line is a statement, where
"Hello World!"
is an expression, - the function declaration is a statement, where the parameter
msg
passed to the function is an expression, - the line that prints the message in the console is a statement, where again the parameter
msg
is an expression.
Why Expressions Are Important in React
When building a React application, you can inject JavaScript expressions into your JSX code. For example, you can pass a variable, write an event handler or a condition. To do this, you need to include your JS code in curly brackets.
For example, you can pass a variable:
const Message = () => {
const name = "Carlo";
return <p>Welcome {name}!</p>;
}
In short, the curly brackets tell your transpiler to process the code wrapped in brackets as JS code. Everything that comes before the opening <p>
tag and after the closing </p>
tag is normal JavaScript code. Everything inside the opening <p>
and closing </p>
tags is processed as JSX code.
Here is another example:
const Message = () => {
const name = "Ann";
const heading = <h3>Welcome {name}</h3>;
return (
<div>
{heading}
<p>This is your dashboard.</p>
</div>
);
}
You can also pass an object:
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>
);
}
And below is a more comprehensive example:
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>
);
}
Notice the double curly brackets in the style
attributes in the elements img
and div
. We used double brackets to pass two objects containing card and image styles.
You should have noticed that in all the examples above, we included JavaScript expressions in JSX.
Immutability in React
Mutability and Immutability are two key concepts in object-oriented and functional programming.
Immutability means that a value cannot be changed after it has been created. Mutability means, of course, the opposite.
In Javascript, primitive values are immutable, meaning that once a primitive value is created, it cannot be changed. Conversely, arrays and objects are mutable because their properties and elements can be changed without reassigning a new value.
There are several reasons for using immutable objects in JavaScript:
- Improved performance
- Reduced memory consumption
- Thread-safety
- Easier coding and debugging
Following the pattern of immutability, once a variable or object is assigned, it cannot be re-assigned or changed. When you need to modify data, you should create a copy of it and modify its content, leaving the original content unchanged.
Immutability is also a key concept in React.
The React documentation states:
The state of a class component is available as
this.state
. The state field must be an object. Do not mutate the state directly. If you wish to change the state, callsetState
with the new state.
Whenever the state of a component changes, React calculates whether to re-render the component and update the Virtual DOM. If React did not have track of the previous state, it could not determine whether to re-render the component or not. React documentation provides an excellent example of this.
What JavaScript features can we use to guarantee the immutability of the state object in React? Let’s find out!
Declaring Variables
You have three ways to declare a variable in JavaScript: var
, let
, and const
.
The var
statement exists since the beginning of JavaScript. It’s used to declare a function-scoped or globally-scoped variable, optionally initializing it to a value.
When you declare a variable using var
, you can re-declare and update that variable both in the global and local scope. The following code is allowed:
// Declare a variable
var msg = "Hello!";
// Redeclare the same variable
var msg = "Goodbye!"
// Update the variable
msg = "Hello again!"
var
declarations are processed before any code is executed. As a result, declaring a variable anywhere in the code is equivalent to declaring it at the top. This behavior is called hoisting.
It is worth noting that only the variable declaration is hoisted, not the initialization, which only happens when the control flow reaches the assignment statement. Until that point, the variable is undefined
:
console.log(msg); // undefined
var msg = "Hello!";
console.log(msg); // Hello!
The scope of a var
declared in a JS function is the whole body of that function.
This means that the variable is not defined at block level, but at the level of the entire function. This leads to a number of problems that can make your JavaScript code buggy and difficult to maintain.
To fix these problems, ES6 introduced the let
keyword.
The
let
declaration declares a block-scoped local variable, optionally initializing it to a value.
What are the advantages of let
over var
? Here are some:
let
declares a variable to the scope of a block statement, whilevar
declares a variable globally or locally to an entire function regardless of block scope.- Global
let
variables are not properties of thewindow
object. You cannot access them withwindow.variableName
. let
can only be accessed after its declaration is reached. The variable is not initialized until the control flow reaches the line of code where it’s declared (let declarations are non-hoisted).- Redeclaring a variable with
let
throws aSyntaxError
.
Since variables declared using var
cannot be block-scoped, if you define a variable using var
in a loop or inside an if
statement, it can be accessed from outside the block and this can lead to buggy code.
The code in the first example is executed without errors. Now replace var
with let
in the block of code seen above:
console.log(msg);
let msg = "Hello!";
console.log(msg);
In the second example, using let
instead of var
produces an Uncaught ReferenceError
:
ES6 also introduces a third keyword: const
.
const
is pretty similar to let
, but with a key difference:
Consider the following example:
const MAX_VALUE = 1000;
MAX_VALUE = 2000;
The above code would generate the following TypeError:
In addition:
Declaring a const
without giving it a value would throw the following SyntaxError
(see also ES6 In Depth: let and const):
But if a constant is an array or an object, you can edit properties or items within that array or object.
For example, you can change, add, and remove array items:
// 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"
But you are not allowed to reassign the array:
const cities = ["London", "New York", "Sydney"];
cities = ["Athens", "Barcelona", "Naples"];
The code above would result in a TypeError.
You can add, reassign, and remove object properties and methods:
// 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'}
But you are not allowed to reassign the object itself. The following code would go through an 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()
We now agree that using const
does not always guarantee strong immutability (especially when working with objects and arrays). So, how can you implement the immutability pattern in your React applications?
First, when you want to prevent the elements of an array or properties of an object from being modified, you can use the static method Object.freeze()
.
Freezing an object prevents extensions and makes existing properties non-writable and non-configurable. A frozen object can no longer be changed: new properties cannot be added, existing properties cannot be removed, their enumerability, configurability, writability, or value cannot be changed, and the object’s prototype cannot be re-assigned.
freeze()
returns the same object that was passed in.
Any attempt to add, change or remove a property will fail, either silently or by throwing a TypeError
, most commonly in strict mode.
You can use Object.freeze()
this way:
'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);
If you now try to add a property, you will receive an Uncaught TypeError
:
// Add a new property
post.slug = "javascript-is-awesome"; // Uncaught TypeError
When you try to reassign a property, you get another kind of TypeError
:
// Reassign property
post.id = 5; // Uncaught TypeError
You can also try to delete a property. The result will be another TypeError
:
// Delete a property
delete post.excerpt; // Uncaught TypeError
Template Literals
When you need to combine strings with the output of expressions in JavaScript, you usually use the addition operator +
. However, you can also use a JavaScript feature that allows you to include expressions within strings without using the addition operator: Template Literals.
Template Literals are a special kind of strings delimited with backtick (`
) characters.
In Template Literals you can include placeholders, which are embedded expressions delimited by a dollar character and wrapped in curly brackets.
Here is an example:
const align = 'left';
console.log(`This string is ${ align }-aligned`);
The strings and placeholders get passed to a default function that performs string interpolation to substitute the placeholders and concatenate the parts into a single string. You can also replace the default function with a custom function.
You can use Template Literals for:
Multi-line strings: newline characters are part of the template literal.
console.log(`Twinkle, twinkle, little bat!
How I wonder what you’re at!`);
String interpolation: Without Template Literals, you can only use the addition operator to combine the output of expressions with strings. See the following example:
const a = 3;
const b = 7;
console.log("The result of " + a + " + " + b + " is " + (a + b));
It’s a bit confusing, isn’t it? But you can write that code in a more readable and maintainable way using Template Literals:
const a = 3;
const b = 7;
console.log(`The result of ${ a } + ${ b } is ${ a + b }`);
But keep in mind that there’s a difference between the two syntaxes:
Template Literals lend themselves to several uses. In the following example, we use a ternary operator to assign a value to a class
attribute.
const page="archive";
console.log(`class=${ page === 'archive' ?
'archive' : 'single' }`);
Below, we are performing a simple calculation:
const price = 100;
const VAT = 0.22;
console.log(`Total price: ${ (price * (1 + VAT)).toFixed(2) }`);
It is also possible to nest Template Literals by including them inside an ${expression}
placeholder (but use nested templates with caution because complex string structures may be hard to read and maintain).
Tagged templates: As we mentioned above, it is also possible to define a custom function to perform string concatenation. This kind of Template Literal is called Tagged Template.
Tags allow you to parse template literals with a function. The first argument of a tag function contains an array of string values. The remaining arguments are related to the expressions.
Tags allow you to parse template literals with a custom function. The first argument of this function is an array of the strings included in the Template Literal, the other arguments are the expressions.
You can create a custom function to perform any sort of operation on the template arguments and return the manipulated string. Here is a very basic example of tagged template:
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);
The code above prints the strings
and tags
array elements then capitalizes the string characters before printing the output in the browser console.
Arrow Functions
Arrow functions are an alternative to anonymous functions (functions without names) in JavaScript but with some differences and limitations.
The following declarations are all valid Arrow Functions examples:
// 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
}
You may omit the round brackets if you only pass one parameter to the function. If you pass two or more parameters, you must enclose them in brackets. Here is an example of this:
const render = ( id, title, category ) => `${id}: ${title} - ${category}`;
console.log( render ( 5, 'Hello World!', "JavaScript" ) );
One-line Arrow Functions return a value by default. If you use the multiple-line syntax, you will have to manually return a value:
const render = ( id, title, category ) => {
console.log( `Post title: ${ title }` );
return `${ id }: ${ title } - ${ category }`;
}
console.log( `Post details: ${ render ( 5, 'Hello World!', "JavaScript" ) }` );
One key difference between normal functions and Arrow Functions to bear in mind is that Arrow functions don’t have their own bindings to the keyword this
. If you try to use this
in an Arrow Function, it will go outside the function scope.
For a more in-depth description of Arrow functions and examples of use, read also mdn web docs.
Classes
Classes in JavaScript are a special type of function for creating objects that use the prototypical inheritance mechanism.
According to mdn web docs,
When it comes to inheritance, JavaScript only has one construct: objects. Each object has a private property which holds a link to another object called its prototype. That prototype object has a prototype of its own, and so on until an object is reached with
null
as its prototype.
As with functions, you have two ways of defining a class:
- A class expression
- A class declaration
You can use the class
keyword to define a class inside an expression, as shown in the following example:
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
A class has a body, that is the code included in curly brakets. Here you’ll define constructor and methods, which are also called class members. The body of the class is executed in strict mode even without using the 'strict mode'
directive.
The constructor
method is used for creating and initializing an object created with a class and is automatically executed when the class is instantiated. If you don’t define a constructor method in your class, JavaScript will automatically use a default constructor.
A class can be extended using the extends
keyword.
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());
A constructor can use the super
keyword to call the parent constructor. If you pass an argument to the super()
method, this argument will also be available in the parent constructor class.
For a deeper dive into JavaScript classes and several examples of usage, see also the mdn web docs.
Classes are often used to create React components. Usually, you’ll not create your own classes but rather extend built-in React classes.
All classes in React have a render()
method that returns a React element:
class Animal extends React.Component {
render() {
return <h2>Hey, I am a {this.props.name}!</h2>;
}
}
In the example above, Animal
is a class component. Bear in mind that
- The name of the component must begin with a capital letter
- The component must include the expression
extends React.Component
. This gives access to the methods of theReact.Component
. - The
render()
method returns the HTML and is required.
Once you have created your class component, you can render the HTML on the page:
const root = ReactDOM.createRoot(document.getElementById('root'));
const element = <Animal name="Rabbit" />;
root.render(element);
The image below shows the result on the page (You can see it in action on CodePen).
Note, however, that using class components in React is not recommended and it’s preferable defining components as functions.
The Keyword ‘this’
In JavaScript, the this
keyword is a generic placeholder usually used inside objects, classes, and functions, and it refers to different elements depending on the context or scope.
this
can be used in the global scope. If you digit this
in your browser’s console, you get:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
You can access any of the methods and properties of the Window
object. So, if you run this.location
in your browser’s console, you get the following output:
Location {ancestorOrigins: DOMStringList, href: 'https://kinsta.com/', origin: 'https://kinsta.com', protocol: 'https:', host: 'kinsta.com', ...}
When you use this
in an object, it refers to the object itself. In this way, you can refer to the values of an object in the methods of the object itself:
const post = {
id: 5,
getSlug: function(){
return `post-${this.id}`;
},
title: 'Awesome post',
category: 'JavaScript'
};
console.log( post.getSlug );
Now let’s try to use this
in a function:
const useThis = function () {
return this;
}
console.log( useThis() );
If you are not in strict mode, you’ll get:
Window {window: Window, self: Window, document: document, name: '', location: Location, ...}
But if you invoke strict mode, you get a different result:
const doSomething = function () {
'use strict';
return this;
}
console.log( doSomething() );
In this case, the function returns undefined
. That’s because this
in a function refers to its explicit value.
So how to explicitly set this
in a function?
First, you can manually assign properties and methods to the function:
function doSomething( post ) {
this.id = post.id;
this.title = post.title;
console.log( `${this.id} - ${this.title}` );
}
new doSomething( { id: 5, title: 'Awesome post' } );
But you can also use call
, apply
, and bind
methods, as well as arrow functions.
const doSomething = function() {
console.log( `${this.id} - ${this.title}` );
}
doSomething.call( { id: 5, title: 'Awesome post' } );
The call()
method can be used on any function and does exactly what it says: it calls the function.
Furthermore, call()
accepts any other parameter defined in the function:
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() );
An alternative to the options discussed above is using arrow functions.
Arrow function expressions should only be used for non-method functions because they do not have their own
this
.
This makes arrow functions particularly useful with event handlers.
That’s because “when the code is called from an inline event handler attribute, its this
is set to the DOM element on which the listener is placed” (see mdn web docs).
But things change with arrow functions because…
… arrow functions establish
this
based on the scope the arrow function is defined within, and thethis
value does not change based on how the function is invoked.
Binding ‘this’ to Event Handlers in React
When it comes to React, you have a few ways to make sure that the event handler does not lose its context:
1. Using bind()
inside the render method:
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 the context to the event handler in the constructor:
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. Define the event handler using arrow functions:
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. Using arrow functions in the render method:
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;
Whichever method you choose, when you click the button, the browser console shows the following output:
This refers to: MyComponent {props: {…}, context: {…}, refs: {…}, updater: {…}, state: {…}, …}
The message is: Hello World!
Ternary Operator
The conditional operator (or ternary operator) allows you to write simple conditional expressions in JavaScript. It takes three operands:
- a condition followed by a question mark (
?
), - an expression to execute if the condition is truthy followed by a semicolon (
:
), - a second expression to execute if the condition is falsy.
const drink = personAge >= 18 ? "Wine" : "Juice";
It is also possible to chain multiple expressions:
const drink = personAge >= 18 ? "Wine" : personAge >= 6 ? "Juice" : "Milk";
Be careful, though, because chaining multiple expressions can lead to messy code that is difficult to maintain.
The ternary operator is particularly useful in React, especially in your JSX code, which only accepts expressions in curly brackets.
For example, you can use the ternary operator to set the value of an attribute based on a specific condition:
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>
);
}
In the code above, we check the condition person.theme === 'dark'
to set the value of the style
attribute of the container div
.
Short Circuit Evaluation
The logical AND (&&
) operator evaluates operands from left to right and returns true
if and only if all operands are true
.
The logical AND is a short-circuit operator. Each operand is converted to a boolean and, if the result of the conversion is false
, the AND operator stops and returns the original value of the falsy operand. If all values are true
, it returns the original value of the last operand.
Short circuit evaluation is a JavaScript feature commonly used in React as it allows you to output blocks of code based on specific conditions. Here is an example:
{
displayExcerpt &&
post.excerpt.rendered && (
<p>
<RawHTML>
{ post.excerpt.rendered }
</RawHTML>
</p>
)
}
In the code above, if displayExcerpt
AND post.excerpt.rendered
evaluate to true
, React returns the final block of JSX.
To recap, “if the condition is true
, the element right after &&
will appear in the output. If it is false
, React will ignore and skip it”.
Spread Syntax
In JavaScript, spread syntax allows you to expand an iterable element, such as an array or object, into function arguments, array literals, or object literals.
In the following example, we are unpacking an array in a function call:
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 ) );
You can use the spread syntax to duplicate an array (even multidimensional arrays) or to concatenate arrays. In the following examples, we concatenate two arrays in two different ways:
const firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray.push( ...secondArray );
console.log( firstArray );
Alternatively:
let firstArray = [1, 2, 3];
const secondArray = [4, 5, 6];
firstArray = [ ...firstArray, ...secondArray];
console.log( firstArray );
You can also use the spread syntax to clone or merge two objects:
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 } );
Destructuring Assignment
Another syntactic structure you will often find used in React is the destructuring assignment syntax.
In the following example, we unpack values from an array:
const user = ['Carlo', 'Content writer', 'Kinsta'];
const [name, description, company] = user;
console.log( `${name} is ${description} at ${company}` );
And here is a simple example of destructuring assignment with an object:
const user = {
name: 'Carlo',
description: 'Content writer',
company: 'Kinsta'
}
const { name, description, company } = user;
console.log( `${name} is ${description} at ${company}` );
But we can do even more. In the following example, we unpack some properties of an object and assign the remaining properties to another object using the spread syntax:
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'}
You can also assign values to an array:
const user = [];
const object = { name: 'Carlo', company: 'Kinsta' };
( { name: user[0], company: user[1] } = object );
console.log( user ); // (2) ['Carlo', 'Kinsta']
Note that the parentheses around the assignment statement are required when using object literal destructuring assignment without a declaration.
For a more in-depth analysis of destructuring assignment, with several examples of use, please refer to the mdn web docs.
filter(), map(), and reduce()
JavaScript provides several useful methods you’ll find often used in React.
filter()
In the following example, we apply the filter to the numbers
array to get an array whose elements are numbers greater than 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]
In the following example, we get an array of posts with the word ‘JavaScript’ included in the title:
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 );
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]
In a React component, you’ll often find the map()
method used to build lists. In the following example, we’re mapping the WordPress posts
object to build a list of posts:
<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()
accepts two parameters:
- A callback function to execute for each element in the array. It returns a value that becomes the value of the accumulator parameter on the next call. On the last call, the function returns the value that will be the return value of
reduce()
. - An initial value that is the first value of the accumulator passed to the callback function.
The callback function takes a few parameters:
- An accumulator: The value returned from the previous call to the callback function. On the first call, it’s set to an initial value if specified. Otherwise, it takes the value of the first item of the array.
- The value of the current element: The value is set to the first element of the array (
array[0]
) if an initial value has been set, otherwise it takes the value of the second element (array[1]
). - The current index is the index position of the current element.
An example will make everything clearer.
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
Let’s find out in detail what happens at each iteration. Go back to the previous example and change the 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 );
The following image shows the output in the browser console:
Now let us find out what happens without the initialValue
parameter:
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 );
More examples and use cases are discussed on the mdn web docs website.
Exports and Imports
As of ECMAScript 2015 (ES6), it is possible to export values from a JavaScript module and import them into another script. You’ll be using imports and exports extensively in your React applications and therefore it is important to have a good understanding of how they work.
The following code creates a functional component. The first line imports the React library:
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;
We used the import
keyword followed by the name we want to assign to what we are importing, followed by the name of the package we want to install as it is referred to in the package.json file.
Note that in the MyComponent()
function above, we used some of the JavaScript features discussed in the previous sections. We included property values in curly brackets and assigned the value of the style
property using the conditional operator syntax.
The script ends with the export of our custom component.
Now that we know a bit more about imports and exports, let’s take a closer look at how they work.
Export
Every React module can have two different types of export: named export and default export.
For example, you can export several features at once with a single export
statement:
export { MyComponent, MyVariable };
You can also export individual features (function
, class
, const
, let
):
export function MyComponent() { ... };
export let myVariable = x + y;
But you can only have a single default export:
export default MyComponent;
You can also use default export for individual features:
export default function() { ... }
export default class { ... }
Import
Once the component has been exported, it can be imported into another file, e.g. an index.js file, along with other modules:
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>
);
In the code above, we used the import declaration in several ways.
In the first two lines, we assigned a name to the imported resources, in the third line we did not assign a name but simply imported the ./index.css file. The last import
statement imports the ./MyComponent file and assigns a name.
Let’s find out the differences between those imports.
In total, there are four types of imports:
Named import
import { MyFunction, MyVariable } from "./my-module";
Default import
import MyComponent from "./MyComponent";
Namespace import
import * as name from "my-module";
Side effect import
import "module-name";
Once you have added a few styles in your index.css, your card should look like in the image below, where you can also see the corresponding HTML code:
Note that import
declarations can only be used in modules at the top level (not inside functions, classes, etc.).
For a more comprehensive overview of import
and export
statements, you may also want to check the following resources:
Summary
React is one of the most popular JavaScript libraries today and is one of the most requested skills in the world of web development.
With React, it is possible to create dynamic web applications and advanced interfaces. Creating large, dynamic, and interactive applications can be easy thanks to its reusable components.
But React is a JavaScript library, and a good understanding of the main features of JavaScript is essential to start your journey with React. That’s why we’ve collected in one place some of the JavaScript features you’ll find most often used in React. Mastering those features will give you a leg up on your React learning journey.
And when it comes to web development, making the move from JS/React to WordPress takes very little effort.
Now it’s your turn, what JavaScript features do you think are most useful in React development? Have we missed anything important that you would have liked to see on our list? Share your thoughts with us in the comments below.