← Back to Notes

JavaScript for React

Hamed Bahram /
12 min read--- views

It's important to have a basic understanding of JavaScript before digging into React. Not all JavaScript concepts and patterns but the foundations.

Declaring variables

Before ES6, the only way to declare a variable was with the var keyword, but now we have more options:

The const keyword

The value of a constant can't be changed through reassignment (i.e., by using the assignment operator =), and it can't be redeclared.

const odd = true
odd = false // Error: Assignment to constant variable
const odd = 123 /// Error: `odd` has already been declared

However, if a constant is an object or array its properties or items can be updated or removed.

// Arrays
const num = [1, 2, 3]
num.push(4)
console.log(num) // [1, 2, 3, 4]
 
// Objects
const person = { name: 'Hamed' }
person.lastName = 'Bahram'
console.log(person) // { name: 'Hamed', lastName: 'Bahram'}

The let keyword

The let keyword declares a variable, that can be reassigned but not redeclared.

let even = true
even = false // no error
let even = 123 // Error: `even` has already been declared

Both const and let are block-scoped. Blocks are created using curly braces {}, and these curly braces block off the scope of any variable declared inside of them.

let x = 1
 
if (x === 1) {
  let x = 2
  console.log(x) // expected output: 2
}
 
console.log(x) // expected output: 1

The variable x inside curly braces (the if block) is scoped to that block, therefore not affecting the global variable x outside.

Template literals

Template literals are delimited with the backtick ( ` ) character allowing multi-line strings and string interpolation with embedded expressions.

Template literals are sometimes referred to as template strings, because they are used for string interpolation. Traditional string concatenation uses plus signs + to compose a string.

const name = 'Hamed'
const greeting = 'Hello' + ' ' + name
console.log(greeting) // 'Hello Hamed'

With a template literal we can create a string and insert the variable by wrapping it inside ${} .

const name = 'Hamed'
const greeting = `Hello ${name}`

You can also have a string that spans over multiple lines without breaking your code:

const message = `
Welcome ${name},
Thank you for reading my posts.
Cheers!
`

Functions

Anytime you find yourself repeating a task, you can define a function that performs that task, and then call the function instead of repeating the code, when needed.

Defining functions

Function declarations

A function declaration, consist of a function keyword followed by the name of the function, a list of parameters, enclosed in parentheses, and the JavaScript statements that define the task, enclosed in curly braces.

function sum(a, b) {
  return a + b
}

Once you declared the function, you can call the function to execute the code:

sum(2, 2) // Expected output: 4

The return statement specifies the value returned by the function.

return a + b

Default parameters

You can specify a default value for each of your function parameters to be used in the event that a value is not provided when the function is called

function greet(name = 'friend') {
  return `Hey ${name}!`
}
 
greet('Alex') // Expected output: Hey Alex!
greet() // Expected output: Hey friends!

Function expressions

A function expression involves creating the function as a variable.

const sum = function (a, b) {
  return a + b
}
 
sum(2, 2) // Expected output: 4

One difference between function declarations and function expressions is that function declarations are hoisted, meaning you can call them before your define them, whereas function expressions are not.

// Calling the function before it's declared, works here.
hi()
function hi() {
  console.log('hi')
}
 
// Calling the function expression before it's declared results in Error
bye()
const bye = function () {
  console.log('bye')
}

Arrow functions

With arrow functions you can create functions without using the function keyword. This is an alternative to the traditional function expression but with some differences.

// function expression
const sum = function (a, b) {
  return a + b
}
 
// arrow function
const sum = (a, b) => {
  return a + b
}

If your function is simple and consists of only one statement you can remove the curly braces and the return keyword, making the syntax more concise:

// concise arrow function
const sum = (a, b) => a + b

You can also remove the parentheses if the function has only one argument:

// no parenthesis
const square = num => num * num

The main difference between arrow functions and traditional functions, is that in traditional functions, the value of this is determined by how a function is called (the execution context). While arrow functions don't have their own this binding and retain the value of the enclosing lexical context, regardless of how they are called.

const person = {
  name: 'Hamed',
  printName: function () {
    console.log(this.name)
  }
}
 
// `this` inside printName refers to the person object
person.printName()
 
const person = {
  name: 'Hamed',
  printName: () => {
    console.log(this.name)
  }
}
 
// `this` inside printName refers to the global object
person.printName()

As you can see above arrow function are not a good choice for object methods.

Returning Objects

When returning an object from a concise arrow function you can wrap the object in parenthesis to prevent the object's curly braces from being considered as the function body:

// regular arrow function with `return` keyword
const user = (name, email) => {
  return {
    name: name,
    email: email
  }
}
 
// concise arrow function with implicit return
const user = (name, email) => ({
  name: name,
  email: email
})

You can also use the ES6 object literal shorthand syntax for an even shorter syntax:

// `user` is a function that returns an object
// with `name` and `email` properties
const user = (name, email) => ({ name, email })

Objects and Arrays

Let's look at a few of the Javascript ES2016 syntax, including destructuring, object literal shorthand, and the spread operator.

Object destructuring

Destructuring assignment allows you to pull fields out of the object and create local variables from them.

const person = {
  firstName: 'John',
  lastName: 'Doe',
  gender: 'male',
  age: '26'
}
 
const { firstName, lastName } = person
console.log(firstName, lastName) // Output: John Doe

We can also destructure objects passed to functions as an argument:

const person = {
  firstName: 'John',
  lastName: 'Doe',
  gender: 'male',
  age: '26'
}
 
// Using dot notation to get the firstName
const printName = personObject => {
  console.log(personObject.firstName)
}
 
// Destructuring the argument
const printName = ({ firstName }) => {
  console.log(firstName)
}
 
printName(person) // Output: John

We can also destructure a nested object:

const person = {
  firstName: 'John',
  lastName: 'Doe',
  gender: 'male',
  age: '26',
  spouse: {
    firstName: 'Alicia',
    lastName: 'keys'
  }
}
 
const printSpouseName = ({ spouse: { firstName } }) => {
  console.log(firstName)
}
 
printSpouseName(person) // Output: Alicia

By using the : and the nested curly braces, we were able to destructure the firstName from the spouse object.

Array destructuring

Values can also be destructured from arrays.

const [first] = ['red', 'green', 'blue']
console.log(first) // Output: red

We can also skip values using commas ,. Commas replace the elements that should be skipped.

const [, , third] = ['red', 'green', 'blue']
console.log(third) // Output: blue

You can end a destructuring pattern with a rest property ...rest. This pattern will store all remaining properties of the object or array into a new object or array.

const seasons = ['spring', 'summer', 'fall', 'winter']
const [first, ...remaining] = seasons
 
console.log(first) // output: 'spring'
console.log(remaining) // output: ['summer', 'fall', 'winter']

Object literal shorthand

Object literal shorthand is the opposite of destructuring, it's putting the object together. It allows us to pull variables into an object with a shorthand syntax:

const firstName = 'Alicia'
const lastName = 'Keys'
const printName = function () {
  console.log(this.firstName)
}
const person = { firstName, lastName, printName }
 
console.log(person) // output: { firstName: 'Alicia', lastName: 'Keys' }

Also when defining object methods, we can eliminate the colon : and the function keyword:

// Old way
const pet = {
  name: 'bailey',
  bark: function () {
    console.log('woof')
  }
}
 
// New way
const pet = {
  name: 'bailey',
  bark() {
    console.log('woof')
  }
}

The spread operator

The spread operator expands an array when used in array literals. We can use it to easily copy arrays:

const first = [1, 2, 3]
const second = [4, 5, 6]
const both = [...first, ...second]
 
console.log(both) // Output: [1, 2, 3, 4, 5, 6]

The spread operator expands an array when used in function calls where zero or more arguments are expected:

const numbers = [1, 2, 3]
function sum(x, y, z) {
  return x + y + z
}
 
sum(...numbers) // Output: 6

In an object literal, the spread syntax adds the key-value pairs to the object being created.

const father = {
  firstName: 'John',
  lastName: 'Barrymore'
}
 
const daughter = { ...father, firstName: 'Drew' }
// output: { firstName: 'Drew', lastName: 'Barrymore'}

The rest operator

The rest operator looks exactly like the spread operator, but works the opposite way. While the Spread syntax expands an array into its elements, the rest operator collects multiple elements into an array.

function sum(...args) {
  // `args` will be an array of passed arguments
  const total = args.reduce((acc, curr) => (acc += curr), 0)
  return total
}
 
sum(1, 2, 3) // output: 6
sum(1, 2, 3, 4) // output: 10

The sum function takes in the arguments using the rest operator ...args which condenses all of the passed arguments into an array. This allows the function to accept and handle any number of arguments.

Asynchronous JavaScript

Tasks that take time to complete like accessing a database, or fetching data from an API, when handled asynchronously, do not block the main thread, leaving your program responsive to other events and free to do something else while they complete.

Let's explore some of the JavaScript features that make this possible:

Promise

A promise is an object that represents the state of an asynchronous task and its resulting value.

A Promise is in one of these states:

  • pending: initial state, neither fulfilled nor rejected.
  • fulfilled: meaning that the operation was completed successfully.
  • rejected: meaning that the operation failed.

The final state of a pending promise can either be fulfilled with a value or rejected with an error. When either of these happen, the associated callbacks defined by a promise's then methods are called.

myPromise
  .then(handleFulfilledA, handleRejectedA)
  .then(handleFulfilledB, handleRejectedB)
  .then(handleFulfilledC, handleRejectedC)

The methods then, catch, and finally are used to associate further action with a promise that becomes settled.

The then method takes up to two arguments; the first argument is a callback function for the fulfilled case of the promise, and the second argument is a callback function for the rejected case. Each then returns a new promise, which can optionally be chained.

A chain can safely omit the rejection callback functions until the final catch. A catch method is really just a then method without a slot for a callback function for the case when the promise is fulfilled.

myPromise
  .then(handleFulfilledA)
  .then(handleFulfilledB)
  .then(handleFulfilledC)
  .catch(handleRejectedAny)

The return value of each fulfilled promise is passed along to the next then, while the reason for rejection (i.e. the error) is passed to the next rejection-handler function in the chain.

myPromise
  .then(value => /* do something */)
  .then(value => /* do something */)
  .catch(error => /* handle the error */)

The finally method of a Promise schedules a callback, to be called when the promise is settled (i.e. fulfilled or rejected). The finally method can be useful if you want to do something once the promise is settled, regardless of its success or failure. This lets you avoid duplicating code in the then and catch handlers.

Fetch API

The Fetch API simplifies making a request to fetch a resource from the network. The fetch method takes the path to the resource you want to fetch, and returns a Promise which is fulfilled once the response is available.

fetch(resource, options)

options is an optional object containing settings you want to apply to the request being sent. For more details, you can refer to the MDN.

The Promise returned from fetch won't reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally with ok status set to false, and it will only reject on network failure or if anything prevented the request from completing.

Async/Await

Work in progress...