Appendix
JavaScript Versions
JavaScript is the language of the web. It runs on many different browsers, like Google Chrome, Firefox, Safari, Microsoft Edge, and Internet Explorer. React Native takes this one step further and allows us to write JavaScript to communicate with native iOS and Android components.
Its widespread adoption as the internet's client-side scripting language led to the formation of a standards body which manages its specification. The specification is called ECMAScript or ES.
The 5th edition of the specification is called ES5. You can think of ES5 as a version of the JavaScript programming language. The 6th edition, ES2015, was finalized in 2015 and is a significant update. It contains a whole host of new features for JavaScript. JavaScript written in ES2015 is tangibly different than JavaScript written in ES5.
ES2016, a much smaller update that builds on ES2015, was ratified in June 2016.
ES2015 is sometimes referred to as ES6. ES2016, in turn, is often referred to as ES7.
ES2015
Arrow functions
There are three ways to write arrow function bodies. For the examples below, let's say we have an array of city objects:
const cities = [
{ name: 'Cairo', pop: 7764700 },
{ name: 'Lagos', pop: 8029200 },
];
If we write an arrow function that spans multiple lines, we must use braces to delimit the function body like this:
const formattedPopulations = cities.map((city) => {
const popMM = (city.pop / 1000000).toFixed(2);
return popMM + ' million';
});
console.log(formattedPopulations);
// -> [ "7.76 million", "8.03 million" ]
Note that we must also explicitly specify a
return
for the function.
However, if we write a function body that is only a single line (or single expression) we can use parentheses to delimit it:
const formattedPopulations2 = cities.map((city) => (
(city.pop / 1000000).toFixed(2) + ' million'
));
Notably, we don't use return
as it's
implied.
Furthermore, if your function body is terse you can write it like so:
const pops = cities.map(city => city.pop);
console.log(pops);
// [ 7764700, 8029200 ]
The terseness of arrow functions is one of two reasons that we use them. Compare the one-liner above to this:
const popsNoArrow = cities.map(function(city) { return city.pop });
Of greater benefit, though, is how arrow functions bind the
this
object.
The traditional JavaScript function declaration syntax (function () {}
) will bind this
in anonymous functions to the
global object. To illustrate the confusion this causes,
consider the following example:
function printSong() {
console.log("Oops - The Global Object");
}
const jukebox = {
songs: [
{
title: "Wanna Be Startin' Somethin'",
artist: "Michael Jackson",
},
{
title: "Superstar",
artist: "Madonna",
},
],
printSong: function (song) {
console.log(song.title + " - " + song.artist);
},
printSongs: function () {
// `this` bound to the object (OK)
this.songs.forEach(function(song) {
// `this` bound to global object (bad)
this.printSong(song);
});
},
}
jukebox.printSongs();
// > "Oops - The Global Object"
// > "Oops - The Global Object"
The method printSongs()
iterates over
this.songs
with forEach()
. In this
context, this
is bound to the object
(jukebox
) as expected. However, the anonymous
function passed to forEach()
binds its internal
this
to the global object. As such,
this.printSong(song)
calls the function declared
at the top of the example, not the method on
jukebox
.
JavaScript developers have traditionally used workarounds for
this behavior, but arrow functions solve the problem by
capturing the this
value of the enclosing
context. Using an arrow function for printSongs()
has
the expected result:
function printSong() {
console.log("Oops - The Global Object");
}
const jukebox = {
songs: [
{
title: "Wanna Be Startin' Somethin'",
artist: "Michael Jackson",
},
{
title: "Superstar",
artist: "Madonna",
},
],
printSong: function (song) {
console.log(song.title + " - " + song.artist);
},
printSongs: function () {
this.songs.forEach((song) => {
// `this` bound to same `this` as `printSongs()` (`jukebox`)
this.printSong(song);
});
},
}
jukebox.printSongs();
// > "Wanna Be Startin' Somethin' - Michael Jackson"
// > "Superstar - Madonna"
For this reason, throughout the book we use arrow functions for all anonymous functions.
Classes
JavaScript is a prototype-based language where classes, which is common in many object-oriented languages, were not used. However, ES2015 introduced a class declaration syntax. For example:
class Ball {
constructor(color) {
this.color = color;
}
details() {
return 'This ball is ' + this.color + '!';
}
}
class SoccerBall extends Ball {
kick() {
return 'This ' + this.color + 'soccer ball is kicked!';
}
}
This isn't a brand new JavaScript model, but only a simpler way to define object oriented structures instead of using prototypal-based inheritance. For context, let's take a look at how this would probably look like without using a class definition:
function Ball(color) {
this.color = color;
}
Ball.prototype.details = function details() {
return 'This ball is ' + this.color + '!';
};
function SoccerBall(color) {
Ball.call(this, color);
}
SoccerBall.prototype = Object.create(Ball.prototype);
SoccerBall.prototype.constructor = Ball;
SoccerBall.prototype.kick = function () {
return 'This ' + this.color + 'soccer ball is kicked!';
}
We won't be going into more detail explaining object oriented paradigms and structures in JavaScript, but it's important to realize that creating objects with properties can be simpler with classes. The important thing to note here is that we use this exact same model to create our React Native components.
If you'd like to learn more about ES6 classes, refer to the docs on MDN.
Shorthand property names
In ES5, all objects were required to have explicit key and value declarations:
const getState = () => {};
const dispatch = () => {};
const explicit = {
getState: getState,
dispatch: dispatch,
};
In ES2015, you can use this terser syntax whenever the property name and variable name are the same:
const getState = () => {};
const dispatch = () => {};
const implicit = {
getState,
dispatch,
};
Lots of open source libraries use this syntax, so it's good to be familiar with it. But whether you use it in your own code is a matter of stylistic preference.
Destructuring Assignments
For arrays
In ES5, extracting and assigning multiple elements from an array looked like this:
var fruits = [ 'apples', 'bananas', 'oranges' ];
var fruit1 = fruits[0];
var fruit2 = fruits[1];
In ES6, we can use the destructuring syntax to accomplish the same task like this:
const [ veg1, veg2 ] = [ 'asparagus', 'broccoli', 'onion' ];
console.log(veg1); // -> 'asparagus'
console.log(veg2); // -> 'broccoli'
The variables in the array on the left are "matched"
and assigned to the corresponding elements in the array on the
right. Note that 'onion'
is ignored and
has no variable bound to it.
For objects
We can do something similar for extracting object properties into variables:
const smoothie = {
fats: [ 'avocado', 'peanut butter', 'greek yogurt' ],
liquids: [ 'almond milk' ],
greens: [ 'spinach' ],
fruits: [ 'blueberry', 'banana' ],
};
const { liquids, fruits } = smoothie;
console.log(liquids); // -> [ 'almond milk' ]
console.log(fruits); // -> [ 'blueberry', 'banana' ]
Parameter context matching
We can use these same principles to bind arguments inside a function to properties of an object supplied as an argument:
const containsSpinach = ({ greens }) => {
if (greens.find(g => g === 'spinach')) {
return true;
} else {
return false;
}
};
containsSpinach(smoothie); // -> true
We can also do this with functional React components.
ReactElement
React Native allows us to build applications with a fake
representation of the native views rendered in our mobile
device. A ReactElement
is a representation of a
rendered element.
Consider this JavaScript syntax:
React.createElement(Text, { style: { color: 'red' }},
'Hello, friend! I am a basic React Native component.'
)
Which can be represented in JSX as:
<Text style={ { color: 'red' } }>
Hello, friend! I am a basic React Native component.
</Text>
The code readability is slightly improved in the latter example. This is exacerbated in a nested tree structure:
React.createElement(View, {},
React.createElement(Text, { style: { color: 'red' }},
'Hello, friend! I am a basic React Native component.'
)
)
In JSX:
<View>
<Text style={ { color: 'red' } }>
Hello, friend! I am a basic React Native component.
</Text>
</View>
Overall, JSX presents a light abstraction over the JavaScript version, yet the legibility benefits are huge. Readability boosts our app's longevity and makes it easier to onboard new developers.
Handling Events in React Native
Using bind
statements within a
render()
method and property initializers
aren't the only ways to handle events. We can also take
care of binding our event handlers in a
class constructor:
export default class SearchInput extends React.Component {
constructor() {
super();
this.handleChangeText = this.handleChangeText.bind(this);
}
handleChangeText(newLocation) {
// We need to do something with newLocation
}
render() {
const { placeholder } = this.props;
return (
<TextInput
placeholder={placeholder}
placeholderTextColor="white"
style={styles.textInput}
clearButtonMode="always"
onChangeText={this.handleChangeText}
/>
);
}
}
Instead of using a constructor
to bind our
method, we can also also leverage ES6 arrow syntax to achieve
the same effect:
export default class SearchInput extends React.Component {
handleChangeText(newLocation) {
// We need to do something with newLocation
}
render() {
const { placeholder } = this.props;
return (
<TextInput
placeholder={placeholder}
placeholderTextColor="white"
style={styles.textInput}
clearButtonMode="always"
onChangeText={text => this.handleChangeText(text)}
/>
);
}
}
Notice how this simplifies our syntax where we don't need
to continously set up bind
for each of our event
handlers. We're specifically using
ES6 arrow syntax to pass in the callback:
onChangeText={text => this.handleChangeText(text)}
In most cases this is just fine, but it's important to
realize that this callback will instantiate every time
TextInput
here is rendered. This will also be the
case if we use bind
statements within our
component JSX like we did previously. In most applications,
this is unlikely to pose any noticeable performance issues due
to additional re-rendering. However, binding our member
methods within the constructor
actually prevents
this from happening.
This is where using property initializers can come in handy:
export default class SearchInput extends React.Component {
handleChangeText = newLocation => {
// We need to do something with newLocation
}
render() {
const { placeholder } = this.props;
return (
<TextInput
placeholder={placeholder}
placeholderTextColor="white"
style={styles.textInput}
clearButtonMode="always"
onChangeText={this.handleChangeText}
/>
);
}
}
By using this pattern, we can remove some boilerplate within our constructor method as well as handle events in a cleaner fashion without causing additional re-renders.
Higher-Order Components
A Higher-Order Component (or HOC, for short) sounds complex, but the idea is simple: we want a way to add common functionality (e.g data fetching or drag-and-drop) to many different components. To do this, we write a function that takes an existing component and wraps it in an enhanced component. Instead of changing the code of original component, a higher-order component allows us to change the functionality by controlling how and when we show the original component.
In code, a HOC is conceptually straightforward as well. To create a HOC, we'll create a function that accepts a component to wrap:
const Enhance = OriginalComponent => {
return props => <OriginalComponent {...props} />;
};
It looks like there is a lot going on in the
Enhance
function, but it's pretty simple.
The function accepts an
OriginalComponent
argument and returns a
stateless component function.
JSX spread syntax
We cover spread syntax,
{...props}
, in the "React Fundamentals" chapter, but we haven't mentioned we can also use it for component props. Instead of having to know all of the key-value pairs in theprops
, the spread syntax takes each of the props and sets them as key-value pairs automatically.For instance, if we have a
props
object that has two keys:const props = {msg: "Hello", recipient: "World"}
In spread-syntax, JSX will make the resulting examples equivalent:
<Component {...props} /> <Component msg={"Hello"} recipient={"World"} />