Appendix A. Modern JavaScript Syntax

Some of the code samples in this book use modern JavaScript syntax. If you’re not familiar with this syntax, don’t worry—it’s a pretty straightforward translation from the JavaScript you might be accustomed to.

ECMAScript 5, or ES5, is the JavaScript language specification with the broadest adoption. However, there are many compelling language features introduced in ES6, ES7, and beyond. React Native uses Babel, the JavaScript compiler, to transform our JavaScript and JSX code. One of Babel’s features is its ability to compile newer-style syntax into ES5-compliant JavaScript. This enables us to use language features from ES6 and beyond throughout our React codebase.

let and const

In pre-ES6 JavaScript, we use var to declare variables.

In ES6, there are two additional ways to declare variables: let and const. A variable declared with const cannot be reassigned; that is to say, the following is invalid:

const count = 2;
count = count + 1; // BAD

Variables declared with let or var may be reassigned. A variable declared with let may only be used in the same block as it is defined.

Some of the examples in this book still use var, but you’ll also see let and const. Don’t worry about the distinctions too much.

Importing Modules

We could use CommonJS module syntax to export our components and other JavaScript modules (Example A-1). In this system, we use require to import other modules, and assign a value to module.exports in order to make a file’s contents available to other modules.

Example A-1. Requiring and exporting modules using CommonJS syntax
var OtherComponent = require('./other_component');

class MyComponent extends Component {
  ...
}

module.exports = MyComponent;

With ES6 module syntax, we can use the export and import commands instead. Example A-2 shows the equivalent code, using ES6 module syntax.

Example A-2. Importing and exporting modules using ES6 module syntax
import OtherComponent from './other_component';

class MyComponent extends Component {
  ...
}

export default MyComponent;

Destructuring

Destructuring assignments provide us with a convenient shorthand for extracting data from objects.

Take this ES5-compliant snippet:

var myObj = {a: 1, b: 2};
var a = myObj.a;
var b = myObj.b;

We can use destructuring to do this more succinctly:

var {a, b} = {a: 1, b: 2};

You’ll often see this used with import statements. When we import React, we’re actually receiving an object. We could import without using destructuring, as shown in Example A-3.

Example A-3. Importing the Component class without destructuring
import React from "react";
let Component = React.Component;

But it’s much nicer to use destructuring, as shown in Example A-4.

Example A-4. Using destructuring to import the Component class
import React, { Component } from "react";

Function Shorthand

ES6’s function shorthand is also convenient. In ES5-compliant JavaScript, we define functions as shown in Example A-5.

Example A-5. Longhand function declaration
render: function() {
  return <Text>Hi</Text>;
}

Writing out function over and over again can get annoying. Example A-6 shows the same function, this time applying ES6’s function shorthand.

Example A-6. Shorthand function declaration
render() {
  return <Text>Hi</Text>;
}

Fat-Arrow Functions

In ES5-compliant JavaScript, we often need to bind our functions to make sure that their context (i.e., the value of this) is as expected (Example A-7). This is especially common when we’re dealing with callbacks.

Example A-7. Binding functions manually with ES5-compliant JavaScript
var callbackFunc = function(val) {
  console.log('Do something');
}.bind(this);

Fat-arrow functions are automatically bound so we don’t need to do that ourselves (Example A-8).

Example A-8. Using a fat-arrow function for binding
var callbackFunc = (val) => {
  console.log('Do something');
};

Default Parameters

You can specify default parameters for a function, as shown in Example A-9.

Example A-9. Using default parameters
var helloWorld = (name = "Bonnie") => {
	console.log("Hello, " + name);
}

helloWorld("Zach"); // Prints "Hello, Zach"
helloWorld(); // Prints "Hello, Bonnie"

This syntax is convenient when you want to guarantee a sensible default value for a parameter.

String Interpolation

In ES5-compliant JavaScript, we might build a string by using code such as that in Example A-10.

Example A-10. String concatenation in ES5-compliant JavaScript
var API_KEY = 'abcdefg';
var url = 'http://someapi.com/request&key=' + API_KEY;

Instead, we can use tempate strings, which support multiline strings and string interpolation. By enclosing a string in backticks, we can insert other variable values using the ${} syntax (Example A-11).

Example A-11. String interpolation in ES6
var API_KEY = 'abcdefg';
var url = `http://someapi.com/request&key=${API_KEY}`;

Working with Promises

A promise is an object representing something that will eventually happen. Instead of handcrafting your handling of success and error callbacks, promises have a consistent API for interacting with asynchronous operations.

Let’s say that you have two callbacks: one for success and one for error handling (see Example A-12).

Example A-12. Defining two callbacks
function successCallback(result) {
  console.log("It succeeded: ", result);
}

function errorCallback(error) {
  console.log("It failed: ", error);
}

An old-style function might expect two callbacks and call one of them based on success or failure (see Example A-13).

Example A-13. Passing success and error callbacks in old-style JavaScript
uploadToSomeAPI(successCallback, errorCallback);

With modern promise-based syntax, you can pass success and error callbacks as shown in Example A-14.

Example A-14. Passing success and error callbacks with promises
uploadToSomeAPI().then(successCallback, errorCallback);

These two examples look very similar, but the advantages of using promises becomes evident when you have many callbacks or asynchronous operations to execute. Let’s say that you need to upload some data to an API, update a user interface, and then look for new data.

With old-style callbacks, we can quickly end up in what is sometimes referred to as “callback hell” (Example A-15).

Example A-15. Chaining callbacks together can get messy quickly and is also repetitive
uploadToSomeAPI(
  (result) => {
    updateUserInterface(
      result,
      uiUpdateResult => {
        checkForNewData(
          uiUpdateResult,
          newDataResult => {
            successCallback(newDataResult);
          },
          errorCallback
        );
      },
      errorCallback
    );
  }, errorCallback
);

With promises, we can chain calls to the then method, as Example A-16 shows.

Example A-16. Chaining promises together is simpler
uploadToSomeAPI()
  .then(result => updateUserInterface(result))
  .then(uiUpdateResult => checkForNewData(uiUpdateResult))
  .then(newDataResult => successCallback(newDataResult))
  .catch(errorCallback)

This keeps our code cleaner. It also means that we don’t need to reimplement callback handling each time we write a function.