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.
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:
constcount=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.
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.
varOtherComponent=require('./other_component');classMyComponentextendsComponent{...}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.
importOtherComponentfrom'./other_component';classMyComponentextendsComponent{...}exportdefaultMyComponent;
Destructuring assignments provide us with a convenient shorthand for extracting data from objects.
Take this ES5-compliant snippet:
varmyObj={a:1,b:2};vara=myObj.a;varb=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.
importReactfrom"react";letComponent=React.Component;
But it’s much nicer to use destructuring, as shown in Example A-4.
importReact,{Component}from"react";
ES6’s function shorthand is also convenient. In ES5-compliant JavaScript, we define functions as shown in Example A-5.
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.
render(){return<Text>Hi</Text>;}
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.
varcallbackFunc=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).
varcallbackFunc=(val)=>{console.log('Do something');};
You can specify default parameters for a function, as shown in Example A-9.
varhelloWorld=(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.
In ES5-compliant JavaScript, we might build a string by using code such as that in Example A-10.
varAPI_KEY='abcdefg';varurl='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).
varAPI_KEY='abcdefg';varurl=`http://someapi.com/request&key=${API_KEY}`;
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).
functionsuccessCallback(result){console.log("It succeeded: ",result);}functionerrorCallback(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).
uploadToSomeAPI(successCallback,errorCallback);
With modern promise-based syntax, you can pass success and error callbacks as shown in Example A-14.
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).
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.
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.