Essential JavaScript for React, modern Web Dev


Common JavaScript idioms, an abridged whirlwind tour

Innovative Solutions and Technologies Center(ISTC)

Yerevan, Armenia

By Edgar Aroutiounian, Summer 2017

Progress:

EcmaScript 6


Modern Web Development uses the latest versions and features of JavaScript, which is offically known as EcmaScript

As of June 2017, most browsers support all of EcmaScript except for the ES6 module system, aka

import React, { Component } from 'react';

Technically that isn't even legal ES6 because the ES6 module loader specification does not allow the creation of 'naked' imports, that is imports that don't specify a specific path or URI. However in practice this doesn't really matter because we use babel and webpack to compile our JavaScript code into code that can run on today's browsers.

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/import

Note, this lecture is going to move VERY fast and YOU MUST READ the links I post to MDN, please look at

http://iteratehackerspace.com/backend-bootcamp-english/lecture2.html

for a more comprehensive introduction to JavaScript (uses nodejs)

Classes (not really, they are functions)


ES6 introduced the concept of classes, but these 'classes' are really just syntaxical sugar on top of plain JavaScript functions.

class Person {
  constructor(age, name) {this.age = age; this.name = name; }
  speak() { console.log('My name is', this.name); }
}

const friend = new Person(27, 'Ruzanna');
// This is the same as doing:
function Person(age, name) { this.age = age; this.name = name; }
Person.prototype.speak = function() { console.log('My name is', this.name); }

const acquaint = new Person(23, 'Tigran');

The benefit of using the class approach is that 1) Calling without 'new' is a TypeError exception, 2) The code looks more familiar to programmers coming from other languages

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Classes

Using prototypes


JavaScript is a prototype based language, that means that every Object has a prototype. When we defined classes, all the 'methods' defined in the class are functions that are created for the prototype and the properties of the prototype (the things we access with . operator) are available for any Object that is on that prototype chain. Having a function defined on the prototype is better for memory usage than on each object because then we only make 1 copy of that function rather for each instance of the object. This is a common pattern

class F {
  constructor() {
    this.speak = () => console.log('Hello world');
  }
  alternative_speak() { console.log('Hello world'); }
};
// Both a1, a2 have methods .speak and .alternative_speak
const a1 = new F;
const a2 = new F;

In this example the interpreter created only one alternative_speak function, it is on the prototype of F, but the interpreter is forced to create two speak functions because we have created the function (a fat arrow function) as a property created on each new instance of F

https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes

Binding context


JavaScript classes do not autobind their context, `this` object

Practically speaking, that means you will often see React code that binds functions

class F {
  constructor() {
    this.handler = this.handler.bind(this);
  }
  handler(e) {
    console.log(e.target.value);
  }
}

In class we showed an example of the issues of not having the right context, the same issue comes up in React.

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this

static in JavaScript


The static keyword also exists in JavaScript, it creates a property on the class object itself, not on the prototype. One library that uses this is react-navigation

class HomeScreen extends Component {
  static navigationOptions = ({navigation}) => ({
    title: 'Home Screen',
  });
  // the render function
}

The navigationOptions property is on the HomeScreen object, not on the prototype of HomeScreen

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static

Object destructuring


In the previous example, this

static navigationOptions = ({navigation}) => ({

might have looked strange, specifically the '({navigation})' part. It is called object destructuring, its a way to pull out values from Objects by key name directly. Here are some examples

const foreman = {
  name: 'Gor', age: 28,
  location: 'Yerevan', profession(){ console.log(this.age, this.name); }
};
// We only pulled out name and age as variable names based on keys
const { name, age } = foreman;
console.log(name, age);
const { not_found } = foreman;

Object destructuring in functions


The previous example

static navigationOptions = ({navigation}) => ({

Is actually an example of object destructuring in function parameters, we do this because often times we pass Objects to function, so we might as well be able to pick out the fields right from the beginning.

const person = {name: 'Lilit', profession: 'programmer'};
const say_profession = ({profession}) => {
  console.log('I am a ', profession);
}
say_profession(person);

Notice that we didn't have to give a name to all the fields in the object, we just pick the key names that we care about

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment

Class properties


As of June 2017, Class properties are at stage-2 TC39. That means that they aren't an official part of the EcmaScript specificiation but most likely will be. We can still use it with the help of tools like babel, used under the hood of create-react-app

class F {
  state = { items: []};
  open_dropdown = () => {
     console.log('Some logic here');
  };
}

And that is really the same as

class F {
  constructor() {
    this.open_dropdown = () => {
      console.log('Some logic here');
    }
    this.state = { items: []};
  }
}

Object spread


Another feature you'll often see is something called Object spread, this is also not offical EcmaScript yet babel will compile it into Object.assign function calls

const professional = {name: 'Artur', langs: ['C#', 'JavaScript', 'Armenian']};
const with_more = {...professional, background: ['WebDevelopment']};

We are making a new object called with_more that is a copy of professiona, but with the extra key background

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Spread_operator

import/export in ES6


EcmaScript 2015 (ES6) finally provided the JavaScript language with a module system

// Assume this file is named funcs.js
export const f = () => console.log('Hello');

This says that this module will export something called f which we can then import and use

// Assume this file is called main.js
import { f } from './funcs';
f();

Notice that we did something that looks like object destructuring; its almost that but its not. Also notice that there was no need to add the extension '.js'. ES6 modules are effectively singletons, importing it multiple times in different parts of your application does not make new 'instances' of the module

import/export in ES6


Notice that we did export on that function f. Sometimes though you only want to export one value from your module, in that case we use 'export default'

// assume this is called header.js
// Notice that it was not necessary to give the class a name
export default class extends Component {
  render() {
    return <h2>Hello World</h2>;
  }
}

And we use it like so

import call_it_anything_you_want from './header';
// Can also rename it
import * as Whatever from './header';
// renaming also works with named exports, this is usual in react-router
import { BrowserRouter as Router } from 'react-router-dom';
https://developer.mozilla.org/en/docs/web/javascript/reference/statements/export

Promises


JavaScript coding focuses on asynchronous work, events. That means that we need a way to say what to do in the future and the JavaScript langauge provides us with something called Promises. A Promise is a way to defer work to the future

const promise_example = (success=true) => {
  return new Promise((accept, reject) => {
    const func = () => success
      ? accept('You waited 3 seconds, here is the data')
      // Always use an Error object, it preserves the stack
      : reject(new Error('failure'));
    setTimeout(func, 3 * 1000);
  })
}
promise_example().then(msg => console.log('Given', msg));
// Be sure to handle the errors with the .catch method
promise_example(false)
.then(msg => console.log('Given', msg))
.catch(error_handle => console.log(error_handle.message))
https://developers.google.com/web/fundamentals/getting-started/primers/promises

async/await


Working with Promises has one small hassle, that is that we have to handle the .then and .catch. ES7, the next version of EcmaScript, provides new keywords called async and await. async, await can only be used with functions and any function that uses await must be wrapped with the async keyword. We can use this in browsers because babel will compile the async,await into ES6 generator functions and using the yield keyword. Any function wrapped with async returns a Promise

const load_data = async path => {
  // get a request object, fetch by default does a HTTP GET request
  const req = await fetch(path);
  // Get the HTTP body as JSON, no need to use JSON.parse
  const results = await req.json();
  return results;
}

fetch is a function provided by the DOM API, (also implemented in React-Native), that gives us the ability to download new data. fetch returns a promise so we can use .then on the result, or we can use async/await which will 'unwrap' the promise for us

https://developer.mozilla.org/en/docs/Web/API/Fetch_API

async/await error handling


async, await make asynchronous code LOOK as if it was synchronous, it turns the .catch error from a Promise into an exception

const promise_example = (success=true) => {
  return new Promise((accept, reject) => {
    const func = () => success
      ? accept('You waited 3 seconds, here is the data')
      : reject(new Error('failure'));
    setTimeout(func, 3 * 1000);
  })
}
(async () => {
  try { await promise_example(false); }
  catch (e) { console.log(e.message); }
})()

Notice the sneaky way to do a top level async/await call since async/await can only be used in functions

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await