Skip to content

JavaScript, the React parts


A JavaScript Primer for Learning React

React became really popular at around the same time that ES2015 (ES6) came into existence (those are just the technical version names for JavaScript). For this reason, some beginners learning React are also trying to tackle more modern JavaScript syntax at the same time. If you're new to both, it can be confusing as to "what is JavaScript and what is React". This document should serve as a primer to help you get up-to-speed on JavaScript syntax that we feel matters the most for learning React.

Contents

Semicolons

Perhaps you've heard or seen that semicolons aren't exactly required in JavaScript. There has been a ton of debate on whether or not devs should use them anyways, but the main points usually boil down to:

  • Point: You should use them because there are some edge cases where not having them can be a problem
  • Counterpoint: True, but if we use Babel to "transpile" our code, Babel is going to take the code we wrote without semicolons and it's going to add them back in for us anyways, so why does it matter?
  • Counterpoint: Yes, but... and it goes on and on

Whether you like or don't like them is totally up to you. One piece of tooling that seems to normalize the conversation a bit is prettier.io, a formatting tool which rewrites the code as you type, or as you save, or as you push -- whichever you prefer. With tools like prettier, many of the "what is your preference" conversations are going away because tooling helps to normalize the code.

Variables: var, let, and const

JavaScript has had var since the beginning which is related to how JavaScript's scope works.

Scope in JavaScript works a little different from some other languages and sometimes can be hard to manage with the way function scoping works. "Block Scope" can be easier to understand and manage which is why JavaScript got let, and const in ES2015. Here's a quick rundown of how all three work:

// var does not have block scope
var name = 'Michael'
if (true) {
  var name = 'Bruce'
  name // 'Bruce'
}
name // 'Bruce'

// let has block scope
let name = 'Michael'
if (true) {
  let name = 'Bruce'
  name // 'Bruce'
}
name // 'Michael'

// const has block scope too
const name = 'Michael'
if (true) {
  const name = 'Bruce'
  name // 'Bruce'
}
name // 'Michael'

// let can be reassigned
let isOpen = true
isOpen = false
isOpen // false

// const cannot be reassigned
const isOpen = true
isOpen = false // throws error

// Although const cannot be reassigned, if the value
// is an array or an object, it's inner parts can be
// changed, as long as the array or object itself isn't
// reassigned
const list = []

// This is allowed because we're not changing the fact
// that list is still the same array. We're just adding
// stuff to it
list.push('Michael')

// But this is not allowed, we cannot change (reassign)
// list to be something other than the array it started
// off to be
list = 'turn list into a string'

We find block scope to make more sense to people and is generally more useful, therefore we don't use var.

We use const for everything, unless it can be reassigned later, only then do we use let. It's a way to let other people know (pun intended) to watch out for that value, because it will likely change over time.

In practice, nearly everything is const unless you have specific reasons to use let. And while some would say it's controversial, some believe there is no reason to use var anymore, that let does everything we would possibly need var for.

String Literals

Strings in JavaScript can made with single or double quotes. But when you make strings this way, you can't have multiline unless you manually add new lines. Template literals (sometimes referred to as Template Strings) allow us to do multiline in a much cleaner way. Template Literals use the back-tick instead of a single or double quote.

// Manual hard returns with \\n isn't fun
const str = 'multiline\\nwith\\nmanual\\nhard returns'

// This is much nicer.
const str = `
  multiline
  without
  the
  mess
`

Another benefit is string interpolation (making strings from variables)

const something = 'ugly stuff'
const str = 'instead of ' + something + ' like this'

const something = 'lovely stuff'
const str = `you can do ${something} like this`

In the first example, the str is being built with a variable. Notice we have to use + concatenation to do so. In the second example, the str is a Template Literal which can use ${} to interpolate variables into the string. Notice that it also doesn't matter how the something variable was made, with or without back-ticks.

Expressions vs Statements and Declarations

Most code in JavaScript is said to be either an Expression (Operator) or Declaration/Statement. It's not so important to memorize every last detail about these when you're starting off, but it is important to know about expressions for React and to start thinking in terms of expressions.

The brief definition is: Expressions resolve to a single value.

// If we're thinking in terms of statements, we might
// write code like this, with an If-Statement:
let result = null
if (someCondition) {
  result = 'Michael'
} else {
  result = 'Bruce'
}

// Here's how we might the same logic using a
// ternary operator, which is a type of expression
// because the line of code resolves to a single
// value for result
const result = someCondition ? 'Michael' : 'Bruce'

The reason why it's good to start thinking in terms of expressions is that JSX lets you easily use expressions within it, but not statements. Like we just saw, there are often two ways to do the same logic in programming and sometimes one way is with statements and another way is with an expression. Here's another example of how to think in expressions. First consider this code:

const name = 'michael jackson'
const parts = name.split(' ') // parts: ['michael', 'jackson']
let first = parts[0] // first: 'michael'
first = first.toUpperCase() // first: 'MICHAEL'

Even though these are all expressions, JavaScript lets us combine and chain expressions together. In effect, all the expressions above can be rewritten into one expression:

const name = 'michael jackson'
const first = name.split(' ')[0].toUpperCase()

// We could have even done this:
const first = 'michael jackson'.split(' ')[0].toUpperCase()

Chaining expressions looks funny at first, but if you read it left to right, each part is resolving to a value and then making itself available to the next part. When we do name.split(' '), this resolves to an array, which means the next part can pick off the 0 index with [0]. That resolves to a string value of 'michael' which can then have a string method added to it like .toUpperCase(). Whatever the far right side of the expression resolves to gets returned to the left-hand side of the equal sign, in our case a variable called first.

Functions

Functions in JavaScript can be created in several ways, each with different tradeoffs. Here are three ways to be aware of:

// Function Declaration
function getName() {
  return 'Michael'
}

// Function Expression
const getName = function() {
  return 'Michael'
}

// Arrow Function (Which is also an expression)
const getName = () => {
  return 'Michael'
}

Based on the previous section on Declarations and Expressions, it's probably more clear as to why the first two get their names. The Function Expression is an "expression" because the function is being assigned to a value. Technically arrow functions are also expressions but in the industry we usually just refer to them as "arrow functions" and not "arrow function expressions".

The basic tradeoff between function declarations and expressions is that declarations can be "hoisted" and expressions cannot. However, many times hoisting doesn't matter so most developers choose one or the other simply based on personal syntax preference.

Arrow Functions are Special

Arrow functions are function expressions with a slightly different syntax. In the example above, you can see that the arrow function looks just like function expression example but without the word function and then with a => fat arrow between the parens and the opening curly-brace.

You may have heard that functions create their own scope in JavaScript. This means JavaScript functions create their own context for this which can be problematic if we want a function but without having its own context for this. One of the characteristics of an arrow function is that they don't create context so this inside the arrow function is the same as the this on the outside. The "read more" link below outlines more details about arrow functions and this.

Arrow functions can also be really compact. Look at these two examples that do the exact same thing:

const getName = () => {
  return 'Michael'
}

// Same as above but more compact
const getName = () => 'Michael'

When arrow functions omit their curly-braces, it means we want the thing on the right-hand side of the fat arrow to be the return (without saying return). This is called an implicit return.

There are some more subtle details to know about arrow functions like how to return an object literal and how to omit the parenthesis for a single parameter. Read more about those with the reference link below:

ES2015+ Syntax Sugar

ES2015-ES2018 has brought a lot of new syntax to JavaScript that lets us do things we could always do before, but now with nicer syntax. Here are some notable examples:

Shortand for Object Methods

You can drop off the : and the word function for methods when defining them:

const obj = {
  insteadOfThis: function() {
    // do stuff
  },

  youCanDoThis() {
    // do stuff
  }
}

Note that the above is not an arrow function, just a shorter syntax for object methods.

Object Destructuring

Object destructuring is a way to take an object and to pull out its internal properties into variables outside of the object:

const obj = { x: 1, y: 2 }

// instead of:
const x = obj.x
const y = obj.y

// We can "destructure" the values into ordinary
// variables:
const { x, y } = obj
x // 1
y // 2

// you can use this all over the place, like function
// parameters. Notice how we're passing just one thing
// (an object) into the add function. If the function
// is expecting an argument, it can destructure the
// values right in the parameter list.
function add({ x, y }) {
  return x + y
}
add({ x: 3, y: 4 }) // 7

It can be a little confusing at first because now curly-braces are used to make objects and to destructure them depending on context. So how can you tell?

// If the curlies are on the right-hand sign of the
// expression (equal sign) then we're making an object
const obj = { x: 1, y: 2 }

// If they're on the left-hand side (or the receiving
// side as with parameters), then it's destructuring:
const { x } = obj
x // 1

Array Destructuring

Array destructuring works almost the same as Object Destructuring but with square-brackets instead of curly-braces:

const arr = ['michael', 'jackson']
const [first, last] = arr
first // michael
last // jackson

The other difference between them is that objects have property names so those have to be used in the destructuring part. Since array values are numerically ordered and without names, the order that we destructure is tied to what value we get -- in other words, first is the first variable in the destructure so it gets the first value of the array.

Property Shorthand

Property Shorthand lets you type less if a property name matches the variable name in an object:

// Instead of having to type name twice like this
const name = 'Michael'
const person = { name: name }

// If the property and the variable are the same you can just
// type it like this and omit the colon and the double word
const person = { name }

...Spread Syntax

When creating objects or arrays, there is a new way to create properties from the properties of an existing object or array. This is much easier shown in code than explained:

// Let's say you have this array
const person = ['Michael', 'Jackson']

// If you were to add the above array to a new one like this:
const profile = [person, 'developer']

// The end result would be an array in an array like this:
profile // [['Michael', 'Jackson'], 'developer']

profile[0] // this is an array
profile[1] // this is the string 'developer'

// However, if we had made profile like this with ...
const profile = [...person, 'developer']

// Then the end result would be this:
profile // ['Michael', 'Jackson', 'developer']

// The same concept works with objects
const person = { first: 'Michael', last: 'Jackson' }
const profile = { ...person, occupation: 'developer' }
profile // { first: 'Michael', last: 'Jackson', occupation: 'developer' }

...Rest Syntax

This might look similar to "spread" but the difference is that ... rest is not used to build objects or arrays, it's used to break then down into pieces. Here's an example of rest while destructuring:

const profile = { first: 'Michael', last: 'Jackson', occupation: 'developer' }
const { occupation, ...rest } = profile
occupation // developer
rest // { first: 'Michael', last: 'Jackson' }

Remember, destructuring is a way to break an object or an array apart into pieces. The above code makes an ordinary string variable called occupation through destructuring. The three dots ... followed by a variable name means we want all the rest of the properties into this rest object. Note that ... can be used while destructuring arrays as well. Also, the variable name doesn't have to be "rest". We could have done ...whatever.

The next form of rest comes in the form of function parameters:

function myFunction(first, last, ...rest) {
  return rest
}

console.log(myFunction('Michael', 'Jackson', 'Developer', 'California'))
// output: ['Developer', 'California']

The function parameters is suggesting that it wants a first and last name as its first two arguments, but anything you pass in after that will all be added to rest as an array.

ES Modules

Organization and breaking your app into different re-usable files is key for a React application. Each JavaScript file is called a "module". In order to let modules work together, they need to able to import and export code between them. While ES Modules aren't natively supported in browsers, we use Webpack and Babel to re-write our code that has modules into something the browser does understand.

In NodeJS, the "pattern" developed for this is "CommonJS" or (cjs). Here's what it looks like:

const SomeModule = require('some-module)
SomeModule.someMethod()

// more code here...

module.exports = SomethingToExport

"ES Modules" is an alternative pattern that is mostly compatible with CommonJS but has a different syntax:

import SomeModule from 'some-module'
SomeModule.someMethod()

// more code here...

export default SomethingToExport

Or we can do a destructuring-like syntax on the import:

import { someMethod } from 'some-module'
someMethod()

// more code here...

export default SomethingToExport

Arrays

Here are some common array methods and functions to be familiar with:

Array.isArray()

// Check to see if a value is an array
const myArray = ['hello']
console.log(Array.isArray(myArray)) // true

.map()

Map takes an array, iterates over it with a function and whatever the function returns will be the replacement value for the item we're currently on:

const myArray = [1, 2, 3, 4]
const result = myArray.map(function(item) {
  return item + 5
})
console.log(result) // [6, 7, 8, 9]

// The above could have also been written like this with
// an arrow function:
const result = myArray.map(item => item + 5)

.reduce()

Reduce is similar to .map in that it iterates over an array but the end result is just one value instead of replacing all the values in the array:

// Let's add up all the values to get one value of 10
const myArray = [1, 2, 3, 4]
const total = myArray.reduce(function(tally, current) {
  return tally + current
}, 0)
console.log(total) // 10

The callback function will give us two important arguments. The first is a running tally of what we've made so far. The second is the current item we're iterating over (in our case the numbers). So, you can see that we're just taking what we have so far and adding each number to it. The only problem is we need tally to start off as 0 otherwise the first iteration won't know how to add things. That's where the second argument for reduce() comes in -- the first being the function and the second being a starting value for the "accumulator" which we're calling tally

The above could have also been written as an arrow function:

const total = myArray.reduce((tally, current) => tally + current, 0)

.filter

Filter gives us a new array with the same values, but only if the iterator function returns true:

const myArray = [1, 2, 3, 4]
const result = myArray.filter(function(item) {
  const isBiggerThanTwo = item > 2
  return isBiggerThanTwo
})
console.log(result) // [3, 4]

// An an arrow function
const result = myArray.filter(item => item > 2)
console.log(result) // [3, 4]

The first example clearly shows that we need to return a boolean based on if the input number is bigger than two. This can be simplified into an arrow function with an implicit return.

.find

Find is similar to Filter but instead of returning an array, only the first item to get true returned from the iterator function is returned from Find:

const people = [{ id: 3, name: 'Michael'}, {id: 5 name: 'Bruce' }]
const person = people.find(item => item.id === 3)
console.log(person) // { id: 3, name: 'Michael'}

Short-Circuiting with &&

You already know how && works in If-Statements. But perhaps you didn't know what short circuiting is or that it had a term. Consider this code:

function one() {
  console.log('one was called')
  return false
}
function two() {
  console.log('two was called')
  return false
}

if (one() && two()) {
  console.log('Here we go!')
}

The only output from this code is "one was called". You can see that "Here we go!" is not going to happen because two() returns false. But why is two() not called at all? We know it wasn't called because we never get "two was called". The reason is that more programming languages short-circuit, which means when the thing before && is false, then there's no point in checking the rest of the expression because one thing being false means the end result has to be false. Maybe you know most of that but never thought of it that way.

We can take advantage of && and short-circuiting in other place besides if-statements:

// This will cause an error if `people` is not an array
function findById(people, id) {
  return people.find(item => item.id === id)
}

// Now we are returning the person if `people` is an array
// If `people` is not an array, we the value whatever is before
// && which is `false` in that case
function findById(people, id) {
  return Array.isArray(people) && people.find(item => item.id === id)
}

Summary

As with any language, there's lots of different ways to program the same task. The React community has really embraced modern JavaScript so learning React will most likely force you to learn the latest modern syntax after you read enough articles and documentation. I hope you enjoyed this read! If you're looking for more material on JavaScript, Mozilla Developer Network is a great place to start.

Loading...

React Router

Michael Jackson and Ryan Florence create the React libraries that you use in your apps like React Router and Reach UI. All of our trainers are experts in React and JavaScript so let us share our knowledge with you and your team!

I Love React
© React Training 2019