This method is preferred not only because it reduces the amount of typing, but also because React internally optimizes to make it more efficient.
Event listeners in React, much as they do without React, receive an optional argument that is an object representing the user event. We can access this event object in order to suppress default behavior, for instance, in a form submission, by using event.preventDefault().
We can also use the event object to, for example, see what document element was targeted by the action or, in the case of a key press event, see which specific key was pressed by the user. To get access to the event, we just need to add it as a parameter to our event listener method:
onClick(event) {
console.log('User event', event);
alert(`You've clicked on${this.props.titleText}`);
}
State
Occasionally, a component will need to keep track of some internal state in addition to the external, read-only, properties that are passed into it. State is necessarily internal to the component and, generally, exclusively tied to some visual display option (for instance, is the component visually expanded or collapsed).
[ 27 ]
Building a Foundation in React
Much in the same way that a component instance can access external properties via this.props, a component instance can access its internal state using this.state. Using internal state, we could optionally show parts of NewsItem only when that item is in an expanded state:
render() {
let body = null;
if (this.state.expanded) {
body = (
);
}
We can see now that the body variable will only be defined if the internal state is expanded. Another thing we can see here is that a
element has been added around the description and byline. The reason we do this is because JSX elements must have a single root node in order to return them or store them in a variable. Alternatively, we could have stored each element in its own variable:
render() {
let byline = null;
let description = null;
if (this.state.expanded) {
byline =
;
description =
;
}
[ 28 ]
Building a Foundation in React
return (
{this.props.titleText}
{byline}
{description}
);
}
While this code is completely valid, we can make it even better by splitting out conditional rendering into a separate method:
renderBody() {
if (this.state.expanded) {
return (
);
}
return null;
}
Then, we can use this helper method within our main render() method in order to make things a bit clearer:
render() {
return (
{this.props.titleText}
{this.renderBody()}
[ 29 ]
Building a Foundation in React
);
}
We've now seen how to use internal state to render things conditionally, but we have not yet seen how that state is defined or how it is modified. In React, we can specify the initial values of internal state by assigning them in the constructor of the component. The component's initial state, much like its default properties, should be a JavaScript object:
constructor(props) {
super(props);
this.state = {
expanded: false
};
this.onClick = this.onClick.bind(this);
}
This method describes the initial state of a component, but it does not provide us with any means to update that state. In order to update the state of a component, we can use a React component's setState method to assign, or reassign, any internal state value.
Typically, updating state happens as a response to some user input or user event. In the last section, we learned how to define methods that respond to these user events, such as clicks, and how to attach these event listeners to the appropriate React element. Let's modify our onClick event handler to change the expanded state of our component instead of simply alerting:
onClick() {
this.setState({
expanded: !this.state.expanded
});
}
When we use setState in this way, React will notice that the internal state has changed, and this will trigger a new rendering using the new internal state. For this reason, we should never manipulate the state of a component directly:
//Do not do this
this.state.expanded = false;
[ 30 ]
Building a Foundation in React
If we change the internal state directly, React's rendering engine will not become aware of it and the component we see on our page will differ from the one in JavaScript. The same goes for props; they are external and should only be changed as a result of new values being passed in through JSX:
//Also don't do this
this.props.titleText = 'Hello World!';
Now that we've demonstrated how to use internal state to display something conditionally, how to initialize state by setting it in the constructor method, and how to modify internal state in response to some user event using setState, let's look at all of this in context in our NewsItem component:
import React, { Component, PropTypes } from 'react';
import Title from './Title';
export default class NewsItem extends Component {
constructor(props) {
super(props);
this.state = {
expanded: false
};
this.onClick = this.onClick.bind(this);
}
onClick() {
this.setState({
expanded: !this.state.expanded
});
}
renderBody() {
if (this.state.expanded) {
return (
);
}
return null;
}
render() {
[ 31 ]
Building a Foundation in React
return (
{this.props.titleText}
{this.renderBody()}
);
}
}
NewsItem.propTypes = {
titleText: PropTypes.string.isRequired
};
Now we have a component for our news item that starts out collapsed (not expanded) and not showing the description or byline, but when the user clicks on the news item, it expands to show the two previously hidden elements.
The component lifecycle
Every React component that is rendered into the DOM goes through a series of steps before and after rendering. As React component developers, we can hook into these steps, called the component lifecycle, in order to perform tasks or check conditions specific to some stage in that lifecycle:
[ 32 ]
Building a Foundation in React
Mounting the component
Before a component is mounted, which means placed into the DOM for the first time, React will look at that component's class to see if it has a method called componentWillMount defined. Should this method exist, React will invoke it. This method is a good place to do things such as set up timers needed by the component or request data the component needs from the server:
componentWillMount() {
//Decrement an internal state counter every second
setInterval(() => {
this.setState({
secondsLeft: this.state.secondsLeft - 1;
});
}, 1000);
}
[ 33 ]
Building a Foundation in React
The next step in the component's lifecycle is the first render. The render() method we've seen before. React calls this method and then, the first time, converts the JSX element output to HTML elements and places them in the DOM. In other words, it mounts the component.
Once mounting is complete, the next step in the lifecycle, an optional method called componentDidMount, is called. This is often an integration point for non-React libraries. With that said, a word of warning: it is generally not a good idea to use libraries that manipulate the DOM alongside React. Remember that React works by keeping a virtual representation of the DOM in memory in order to calculate change sets and apply them. When other libraries are modifying the DOM, it can quickly become out of sync with what React expects. This could, and more often than not, will, lead to errors when React tries to reconcile changes:
componentDidMount() {
//Integrate with an external library here
}
From here, the component is stable and its lifecycle dormant until one of two things happens. The first thing that could happen is the component's parent could pass it new props. The second is some event or interval triggers a change in internal state. These two actions, of course, necessitate a re-render. Before a re-render happens, there are a few other lifecycle methods that will be called.
The update cycle
The first method called during a property update cycle is componentWillReceiveProps. Here, we not only know that the component is about to receive a new set of properties, but we also can see what those properties are and how they compare to the old ones:
componentWillReceiveProps(nextProps) {
//an object of new props
console.log(nextProps);
//The current (old) props
console.log(this.props);
}
This lifecycle method is a good place to update state that is somehow derived from props because it is the only update lifecycle method that is not called for both prop and state changes.
[ 34 ]
Building a Foundation in React
This brings us to the next lifecycle method that is called when either props or state are updated: shouldComponentUpdate. This method is unique among lifecycle methods in that it is the only one that expects a return value. As you may be able to guess, the return value expected is a Boolean. If the method returns true, the lifecycle continues as we expect it. However, if shouldComponentUpdate returns false, the lifecycle is short-circuited here and a re-render does not occur. Within this method, we can see not only the new properties, but also the new state that will be rendered:
shouldComponentUpdate(nextProps, nextState) {
if (this.props.uid !== nextProps.uid) {
return true;
}
return false;
}
If a component does not define this method, it is always assumed to be true. React, though, gives you the ability to override this behavior. This can become important in large applications with many components and many layers of component nesting. Using shouldComponentUpdate, we can fine-tune when a component re-renders in order to enhance the performance of our application. This is important because, while React is good at optimizing renders, rendering is still computationally expensive and excessive rendering can slow down an application to the point where a user can feel stuttering.
If shouldComponentUpdate returns true (or is not defined by the component), the next step in the lifecycle is componentWillUpdate, which is the last step before re-rendering. Here, like in shouldComponentUpdate, we have access to both the new properties and the new state:
componentWillUpdate(nextProps, nextState) {
//Prepare for render!
}
At this point, React will call render on the component again, getting its new JSX representation. It will compare this new JSX to the old JSX in the virtual DOM and create a change set to apply to the real DOM. Once this process is complete, we arrive at the next step of the lifecycle, which is componentDidUpdate. This method is very similar to componentWillUpdate, except that it receives the previous properties and state as arguments:
componentDidUpdate(prevProps, prevState) {
//Here are the old props
console.log(prevProps);
//And here are the current (new) props
[ 35 ]
Building a Foundation in React
console.log(this.props);
}
Now, we've completed the update lifecycle. At this point, once again the component remains dormant until another change in properties or state occurs. This process continues over and over again until the component is removed, or unmounted, from the DOM.
Unmounting the component
Just before a component is removed from the DOM, the final stage of the component's lifecycle will be completed. Here, React calls the optional componentWillUnmount method, which receives no arguments.
This method is a good place to clean up anything that the component created over the course of its life. For instance, if the component started an interval upon mounting, here would be a good place to stop that interval. In our componentWillMount example, we showed starting a countdown interval that fired every second after the component mounted. If we store that interval's ID in state, we can then stop the interval when the component is being unmounted:
componentWillMount() {
//Save the interval in state
this.setState({
tickInterval: setInterval(() => {
this.setState({
secondsLeft: this.state.secondsLeft - 1;
});
}, 1000);
});
}
componentWillUnmount() {
//Stop the countdown before unmounting
clearInterval(this.state.tickInterval);
}
While we've gone through and demonstrated how each lifecycle method might be used within a component, it is important to point out that we would very rarely need to use every component lifecycle method in a single component. Remember that each one is optional and need not be defined by the component unless some feature of its functionality necessitates it. In fact, our NewsItem component does not need any of these lifecycle methods to do exactly what we want.
[ 36 ]
Building a Foundation in React
Alternate component forms
In React, there are three ways to define a component. The way we've seen so far uses ES2015 classes to define a component and its methods. This is currently the most common method for defining React components and, in fact, the one you'll encounter most often in documentation and in this book.
React.createClass
Before ES2015 and its class syntax became popular and brought into React, the way to define a component was by using the React.createClass function. This function takes as an argument a JavaScript object that describes the component and its methods. This conceptually is very similar to the way we have seen so far, but has some syntactic differences. To demonstrate, let's take a look at what our NewsItem component looks like using this method:
React.createClass({
propTypes: {
titleText: PropTypes.string.isRequired
},
getInitialState() {
return {
expanded: false
}
},
onClick() {
this.setState({
expanded: !this.state.expanded
});
},
renderBody() {
if (this.state.expanded)
return (
;
);
}
return null;
[ 37 ]
Building a Foundation in React
},
render() {
return (
{this.props.titleText}
{this.renderBody()}
);
}
});
Other than the obvious syntactic differences, there are a few subtle differences in how we define and use components with React.createClass that we should draw our attention to. The first is instead of simply assigning the state in the class constructor, we define a getInitialState method in the component, which returns the initial component state as an object:
getInitialState() {
return {
expanded: false
}
}
The next thing we might notice is that, previously, event handler functions were bound to the component's this context either in the constructor or within the event attribute assignment. When using the React.createClass syntax, we have no longer need to explicitly bind the context:
[ 38 ]
Building a Foundation in React
We may have also noticed that rather than defining the propTypes statically on the class, we instead do it within the component object:
propTypes: {
titleText: PropTypes.string.isRequired
}
This component does not need default properties, but if it did, we would also define those inside the component object. We do this by defining a method similar to getInitialState called getDefaultProps that also returns an object:
getDefaultProps() {
return {
someProp: 'some value'
}
};
Functional components
For simple components that maintain no internal state, we can define them simply as functions that take props as input and return JSX elements as output. These components are not only succinct, but may in the future be more performant than components defined in other ways. For these reasons, it is recommended that we use functional components wherever possible.
Because of its simplicity and lack of internal state, our Title component from an earlier section is a good candidate for being a functional component. Here is what that component would look like with this alternate syntax:
const Title = (props) => (
{props.children}
);
[ 39 ]
Building a Foundation in React
Taking advantage of ES2015 arrow function syntax, our large traditionally defined component has been simplified to a single function.
In addition to not having internal state, functional components don't have lifecycle methods. They can, however, have defaultProps and propTypes that can be specified in the same manner as class components:
Title.propTypes = {
titleText: PropTypes.string.isRequired
};
Summary
The React library has created a new way to develop user interfaces for web applications through creating declarative and composable components in the new, but familiar, JSX syntax. Since its introduction, it has grown immensely in popularity. At Facebook's F8 developer conference in 2016, it was estimated that upwards of 250,000 developers were using React in some way. This enthusiasm led the community to look for new places to use their favorite library, and in early 2015, React Native was born.
In this chapter, we covered the fundamentals of React, from conception to implementation. We learned how to take a user interface and structure it as components, the building blocks of React applications. Starting with simple components, such as a static title, we then built up to more complex components by adding props, event handlers, state, and lifecycle methods. Finally, we looked at some alternate ways of representing React components and discussed when each was appropriate.
In the next chapter, we will take this knowledge of React into the realm of native mobile applications by building a Hello World application in React Native.
[ 40 ]
2
Saying HelloWorld in React Native
Now that we've introduced you to the basics of programming in React, it's time to dig into some of the underlying tools and technologies that make React Native work. This will provide valuable context as you progress through the remainder of this book. We'll also touch on some of the more popular mobile development alternatives to React Native. This will help you understand where React Native fits into the broader mobile development ecosystem and better inform your decision-making as to which technology best suites your mobile requirements.
Once we've completed setting context, we'll switch gears and focus on configuring your computer for running and debugging your very first React Native application using the iOS Simulator. Since our immediate goal is to get you up and running quickly, we'll only focus on configuring your environment for iOS. Chapter 9, Refactoring for Android, is entirely dedicated to configuring your computer for Android development and refactoring your app to run across both platforms. We will also save much of the React Native API specifics for Chapter 4, Starting our Project with React Native Components and APIs.
In this chapter, we'll cover the following topics:
A review of a few popular mobile development options catered toward JavaScript developers
A review of the various software and tools we'll use to build React Native applications
Installing and configuring all the software needed to build our first React Native app
Walk through basic strategies for debugging in React Native
Saying HelloWorld in React Native
Understanding the mobile app development ecosystem
When it comes to building mobile applications, the two most popular approaches have been, first, building a native application for each target platform (iOS, Android, and so on), and second, writing a hybrid application by using web technologies (HTML, CSS, JavaScript) and wrapping the app inside of a container WebView using a tool such as Adobe PhoneGap. Each option has its pros and cons. Native applications often feel faster and more responsive. They have built-in support for complex touch gestures and they look and feel consistent with their platform. As a post from the Facebook blog states, the reason we build native apps on these proprietary platforms is that right now, we can create better-feeling experiences that are more consistent with the rest of the platform than we can on the web. (Source: https://co de.facebook.com/posts/1014532261909640/react-native-bringing-modern-web-techn iques-to-mobile/) However, this comes at a cost. For one, the native technology stacks are completely different from one another. Developing a native application for iOS typically involves authoring your code in Objective-C or Swift. Android applications are often written in Java. Additionally, the environment you write your code in is different. Xcode is the de facto choice for iOS development, and Android development is typically done with tools such as Eclipse or Android Studio. Juggling lots of tools and languages shouldn't be anything new to seasoned frontend developers. However, couple these differences with differing best practices, approaches to networking, and a limited number of sharable assets across platforms and you've got quite a bit of work ahead of you just to get a cross-platform app off the ground.
Hybrid applications are a very popular alternative, particularly for those with frontend development experience. Hybrid apps are easier to scale because you only have to author one codebase that can be deployed to multiple platforms. For a large swath of applications, the hybrid approach makes sense, particularly if you or your team's skill set mostly aligns with that of your traditional frontend developer: HTML, CSS, and JavaScript. However, achieving the same level of responsiveness and gesture support as a native app can prove deeply challenging.
In this section, I'll provide an overview of a few mobile development frameworks. This is not intended to be comprehensive list. Other options, such as Titanium (https://www.appc elerator.com/), Fuse (https://www.fusetools.com), and others may also be worth exploring.
[ 42 ]
Saying HelloWorld in React Native
Adobe PhoneGap
Adobe PhoneGap is a very popular solution to hybrid development. It's built off the open source Apache Cordova library and provides a Command Line Interface (CLI) for packaging your web application built with HTML, CSS, and JavaScript inside of a native container that can be installed and deployed to native app stores. The native container is a WebView that removes any browser window decoration and runs your web application in full screen. PhoneGap allows you to access different native APIs, such as the device's camera, GPS, and accelerometer. Additionally, Cordova has a rich ecosystem of plugins that provide a bridge to all sorts of phone features that can be interfaced directly within your JavaScript code.
Ionic
Ionic is another hugely popular hybrid application framework. It comprises two major pieces: Ionic Framework (http://ionicframework.com/) and the Ionic CLI (http://ionic framework.com/docs/cli/). Ionic Framework is a mobile framework that includes common UI widgets appropriate for mobile interfaces such as action sheets, mobile navigation, infinitely scrolling lists, and popovers. These components are built on top of Google's Angular JS (https://angularjs.org/) framework using Angular directives. If you're familiar with Angular, working with Ionic should be really straightforward. The Ionic CLI is a tool for managing a lot of the tedious parts of mobile app development, such as scaffolding, building, and deploying to phones. Ionic CLI also provides multiple templates for beginning your project based on common UI patterns. Similar to PhoneGap, Ionic is built on top of Cordova. This means you'll be able to leverage the same Cordova plugins in your Ionic applications. Currently, Ionic is built using Angular 1.x. As of this writing, Ionic 2 is available for preview and will pair with the newly released Angular 2 framework.
NativeScript
Telerik's open source NativeScript lets you build native apps for iOS and Android (they also plan to add support for Windows Universal apps soon) with an approach similar to React Native. Unlike the Cordova options, there is no WebView rendering HTML and CSS. NativeScript relies on JavaScript running on the device with JavaScriptCore on iOS and V8 for Android.
[ 43 ]
Saying HelloWorld in React Native
Your JavaScript code communicates through NativeScript to the underlying platform rendering real native views. NativeScript uses XML markup for declarative UIs that can even be customized per platform either using separate XML files (that is, myView.ios.xml and myView.android.xml) or using platform-specific tags within a view.
As the NativeScript site states, “NativeScript has a lot of cool features, such as two-way data binding between JavaScript objects and native UI components, and a CSS implementation for native apps.” (Source: http://developer.telerik .com/featured/nativescript-works/).
One important differentiator between NativeScript and alternative offerings is its ability to directly access all native platform APIs through JavaScript. The Telerik site provides several good examples of what this might look like in an application:
var alert = new UIAlertView();
alert.message = "Hello world!";
alert.addButtonWithTitle("OK");
alert.show();
(Source: http://developer.telerik.com/featured/nativescript-works/)
In the preceding sample, UIAlertView is a native class in Objective-C. NativeScript allows you to access these native APIs without needing to touch Objective-C or Java. The NativeScript runtime injects into the global namespace of the platform's JavaScript virtual machine all the meta information of the iOS or Android API. This allows you to successfully execute something like the following in an Android environment:
var time = new android.text.format.Time();
(Source: http://developer.telerik.com/featured/nativescript-works/)
NativeScript allows you to style your application using a subset of the CSS language. You can place your CSS inline, in page-specific CSS files, or in application-wide CSS files. You can also layout your views using the predefined layouts–AbsoluteLayout, DockLayout,
GridLayout, StackLayout, and WrapLayout. These web-like paradigms make NativeScript a very enticing option for frontend developers looking to develop native applications.
[ 44 ]
Saying HelloWorld in React Native
React Native
React Native is an open source project released by Facebook in March 2015. The goal of React Native is to allow developers to write high-quality native applications for iOS and Android using familiar web technologies. It uses the same declarative approach to constructing user interfaces as React for the web. React Native also aims to reduce many native development inefficiencies. Rather than deal with the slow process of write > compile > deploy > debug, which can cripple development on larger native apps, React Native allows you to simply refresh the app after making a change without the slow compile and deploy steps, just like on the Web! This makes for a much improved developer experience. And, unlike normal native development, React Native allows you to share far more code across platforms. However, Facebook points out that React Native is not intended to be a write once, run anywhere solution. They acknowledge that each platform has its own look, feel, and capabilities. Instead, React Native allows you to leverage common technologies to build across multiple platforms. They call this learn once, write anywhere.
React Native apps are authored very similarly to React for the Web. You write your business logic in JavaScript and compose your views using JSX. Similar to NativeScript, React Native does not rely on WebViews. React Native runs an embedded instance of JavaScriptCore that communicates through the React Native bridge to platform-specific native components that look and feel as they should on the platform. React Native also exposes underlying platform APIs, allowing you to access the device's camera, GPS, and the like all in JavaScript. However, unlike NativeScript, React Native has you compose your application just like React–by creating a nested component tree structure.
React Native maintains its high performance by executing layout calculations and JavaScript on separate threads, leaving the main thread focused on rendering native views, handling gestures responses, and smooth animations. React components are themselves pure, side-effect-free representations of the application state. UI updates are prompted by changes to a component's props or state. React then goes through its usual update lifecycle and asynchronously batches up the minimal updates necessary to send over the React Native bridge. The bridge is how JavaScript then communicates to the native side and how the native side messages back to JavaScript. However, you can think of the bridge as an implementation detail that can be largely ignored in your day-to-day development. For the most part, you can focus on writing your code in JavaScript using the React Native APIs and let React Native take care of the ugly parts.
[ 45 ]
Saying HelloWorld in React Native
React Native comes bundled with support for many APIs you're used to seeing on the Web. This include, among others, networking APIs for fetch, XMLHttpRequest, and WebSockets along with geolocation and requestAnimationFrame. React Native has built-in support for ECMAScript 2015 and parts of ECMAScript 2016. The React Native Packager (more on this later) runs Babel, a tool for transpiling ES2015 and ES2016 into ECMAScript 5. This is necessary as many older JavaScript runtimes only support parts of ES2015 at this point. But thanks to Babel, this means you can leverage many of the new JavaScript language features in your React Native apps.
If you're unfamiliar with Babel, you can experiment with it in the Babel REPL available on their website at https://babeljs.io/repl/.
Extending React Native
Being a JavaScript framework, you're automatically able to leverage a vast number of JavaScript libraries such as Moment.js and Lodash. But every now and then you'll need to do something that requires native code that isn't available in the React Native API. Through React Native's Native Modules, you can extend the capabilities of React Native allowing you to access platform APIs, reuse existing native code, or perhaps offload an expensive task to the native side. A rich ecosystem of React Native plugins already exists, adding support for things like Google's Material Design, barcode scanners, and an assortment of user interface components. As a testament to how far React Native can be extended, during Facebook's 2016 F8 conference, a React Native plugin for the Microsoft Windows Universal Platform was announced. This opens up React Native development to both Windows and Xbox. We'll touch more on this in Chapter 12, React Native Tools and Resources. We'll also review how you can build your own Native Module in Chapter 10, Using and Writing Native Modules.
Introducing style and layout in React Native
Before we wrap up our introduction to React Native, we must discuss style. Similar to NativeScript, React Native borrows many ideas of styling for the Web. However, unlike NativeScript, you don't author CSS selectors in a CSS file. Instead, you write JavaScript objects that align with many familiar CSS properties. For example, in CSS, you might write the following:
.container {
width: 400px;
height: 400px;
[ 46 ]
Saying HelloWorld in React Native
background-color: #222222;
}
.box {
border-width: 2px;
border-color: #cc0000;
}
However, in React Native, you would write the following in JavaScript:
const styles = StyleSheet.create({
container: {
width: 400,
height: 400,
backgroundColor: '#222222'
},
box: {
borderWidth: 2,
borderColor: '#cc0000'
}
});
Note that instead of writing properties such as border-color, you instead write the camel cased borderColor. This approach actually follows the same conventions used when updating DOM style properties for the Web. Because these are JavaScript objects, you'll need to add quotes around strings. Also, you must omit units such as px for numeric values.
There are several reasons why the React Native team chose not to implement CSS as it exists on the Web. CSS at scale is hard. It's not impossible. But it is hard. There are many approaches for scaling large CSS libraries for the Web, such as OOCSS, BEM, and SMACSS. Each has its own take but none can escape one of CSS's biggest hurdles: everything is entirely global. CSS was never intended to be isolated from the global namespace. For many developers, this seems counterintuitive since global variables are usually a bad practice. Even tools such as Bootstrap and Zurb's Foundation rely heavily global SCSS variables. Writing CSS in JavaScript allows you to isolate styles from the global name space.
There's one other major piece to React Native's approach to layout. React Native uses flexbox as the default layout system. If you're familiar with flexbox for the web, it operates very similarly. We'll go much deeper on style and layout in Chapter 3, Styling and Layout in React Native. For now, let's get our environments configured to build our first React Native application.
[ 47 ]
Saying HelloWorld in React Native
Understanding all the React Native tools
Like most modern development, there are a few tools required to build a React Native application. Luckily, React Native is pretty easy to configure relative to many frameworks out there. We'll get into installing all these tools shortly. First, let's review what all the tools are how they fit into the bigger picture of developing a React Native app.
Xcode
In order to build an iOS application, you'll need Apple's Xcode IDE. React Native runs on iOS 7 and above. This means that you'll need Xcode version 7.0 or higher. (We'll be using Xcode 8 in this book.) Whether you love, hate, or are altogether indifferent about Xcode, we won't actually be spending much time in it.
Initially, we'll just use Xcode to launch our app in the iOS Simulator. Also, because we'll mostly be testing our app in the iOS Simulator, you don't need to worry about enrolling in the Apple iOS Developer Program. However, if and when you wish to ship an app to the App Store, you will need to register. Gustavo Ambrozio has a wonderful series on how to configure everything required for submitting to the Apple App Store; for more information, refer to https://www.raywenderlich.com/8003/how-to-submit-your-app-to-apple-fro m-no-account-to-app-store-part-1.
Google Chrome
Wait, Chrome? I thought we were making native mobile apps? Don't worry. We are. If you've been developing web apps for some time, chances are you've had some time to play with Chrome's amazing DevTools. Thankfully, the Facebook team feels the same way. You can actually debug your React Native apps running on the iOS Simulator or your native device in Google Chrome. It's pretty amazing and is from my experience one the biggest selling points when showing off React Native to other developers.
Homebrew (also known as brew)
Homebrew is a package manager for macOS (formerly known as Mac OS X). We won't be interfacing with this tool much at all in this book. It'll simply be used for installing some of the other tools we'll need to get our environments up and running. Once everything is configured, you can almost forget it exists. If you're curious, though, you can find out more about Homebrew on its website, http://brew.sh/.
[ 48 ]
Saying HelloWorld in React Native
Node.js and npm
Node.js is a server-side JavaScript runtime environment. React Native ships with some tools that are written for Node.js. Additionally, we will use Node's package manager, npm, to install the React Native command-line tool along with other libraries in later chapters.
It's very likely you've encountered Node and npm in the past. Almost all modern frontend tooling, such as Gulp, Babel, or Webpack, run on top of Node.
Watchman
Watchman is an open source tool created by Facebook (https://facebook.github.io/wat chman/). React Native's packager uses Watchman to recursively watch for changes to our source code files across one or more directory trees. Once it detects a change, it automatically rebuilds the JavaScript bundle. This allows us to sidestep one of the slowest and most painful parts of native development.
Much like several of our other tools, once Watchman is installed, you won't have to worry about it. The React Native Package Manager handles running Watchman for us.
Flow
Unlike the other tools mentioned, Flow is entirely optional. Flow is yet another open source tool created by the Facebook team (http://flowtype.org/). It's used to add type annotations to our JavaScript code. JavaScript, as you likely already know, is a dynamically typed language. This means you never need to declare a variable as an int or a string. You just declare a variable and set a value. The type is implicitly set based on the value you assigned. This makes JavaScript an incredibly powerful and dynamic language. But as the saying goes, with great power comes great responsibility.
That said, many JavaScript developers are embracing type annotation tools such as Flow as a way to guard against potential errors in their code. Once you've annotated your code with types, you can run flow from the terminal to verify everything is as expected.
[ 49 ]
Saying HelloWorld in React Native
Following is a simple example from the Flow website:
// @flow
function foo(x) {
return x * 10;
}
foo('Hello, world!');
The previously code will execute without error but it isn't likely going to return a result that's particularly useful. Passing a string to foo will result in NaN (Not a Number). So here's how we might try to guard against this using Flow:
// @flow
function foo(x: number): number {
return x * 10;
}
foo('Hello world');
Note the number type annotations that were added to both the function's parameters and the function's return value. Now, running flow will produce an error alerting you that the argument 'Hello world' is incompatible with the expected type.
Flow can be configured for a project by creating a .flowconfig file in the root directory of your project. The React Native CLI actually provides you an initial configuration when it creates your project.
React Native command-line interface (CLI)
The React Native CLI is a small Node app that offers a simple init command used to create new React Native applications. There really isn't much to it. As we'll see shortly, once you run the CLI, it will create a standard React Native app with all the necessary files and dependencies needed to build an app for iOS and Android.
Installing our tools
At this point, we have covered what each of the tools in our tool chain is responsible for doing. So with that out of the way, let's begin installing each of them.
[ 50 ]
Saying HelloWorld in React Native
Installing Xcode
The first thing you'll need to do is ensure you have version 7 or later of Xcode installed. However, I recommend you install Xcode 8 as that's what we'll be using throughout this book. If you already have Xcode installed, verify the version by launching the program and then clicking on Xcode | About Xcode. You should see an image similar to the following screenshot:
If you don't have Xcode installed, you'll need to download it from the Apple App Store. To do this, launch the App Store application and search for Xcode using the search bar in the top right corner of the window. Once you find it, click on the Get button and then Install App. You may need to enter your Apple credentials before downloading. The Xcode installer is pretty large, so while we wait on that, you can start downloading the next set of tools.
Installing Homebrew
The next series of tools must be installed through the terminal. You can use the macOS Terminal app or another terminal of your choosing. You can search for Terminal (or any other application) by using Command + Space on your keyboard. Then type Terminal and launch it.
[ 51 ]
Saying HelloWorld in React Native
Once the terminal is open, visit http://brew.sh in your web browser. Copy and paste the Install Homebrew command into your terminal and press Enter. You'll need to have administrator privileges in order to install Homebrew and most other tools. You may have to press Return a second time for the terminal to ask for your password. Type in your account password, press Return, and wait for Homebrew to finish installing:
Once it's done installing, your terminal window should look like the following:
[ 52 ]
Saying HelloWorld in React Native
Installing Node and npm
Next on our list are Node and npm. As the React Native docs recommend, you can install this through brew:
brew install node
Node comes with npm, so you don't have to worry about installing that separately. I recommend installing the latest version of Node and npm, version 6 and 3, respectively, at the time of writing.
Once you've installed Node, you can run npm -v and node -v from the terminal. If you see version numbers, you're good to go:
[ 53 ]
Saying HelloWorld in React Native
Installing Watchman and Flow
Next up are Watchman and Flow. Again, Flow is entirely optional. If you wish to experiment with it, you're welcomed to install it. It won't hurt anything. If you'd rather skip it for now, that's totally fine as well.
From the terminal, run brew install watchman:
And if you wish, run brew install flow.
Installing the React Native CLI
Okay! We're almost done with installing everything. There's just one more it we need: the React Native CLI. Again, from the terminal, run npm install -g react-native-cli. Using npm, this will globally (-g) install the React Native CLI that you'll use to scaffold your React Native applications. However, don't be surprised if you see an error that looks like the following:
[ 54 ]
Saying HelloWorld in React Native
This is pretty common permission error. One way to get around this is by prefacing the command with sudo, which will required you type your password. However, we can fix our permissions pretty easily so that sudo is unnecessary. Here are the steps:
1. Type in npm config get prefix.
2. If you see /usr/local, then simply run sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}.
3. You'll be prompted for your user password. Enter it and press Return, and your permissions should be set.
4. If after running npm config get prefix you get a different response, check out https://docs.npmjs.com/getting-started/fixing-npm-permissions. There are more detailed instructions and different options for how to fix npm permissions.
[ 55 ]
Saying HelloWorld in React Native
Once you have your permissions buttoned up, rerun npm install -g react-native cli. Once it's done, you should see something like the following:
Finally, you're all set. Now let's create an app!
Creating our first React Native app
If you're fairly well versed in navigating the terminal, go ahead and cd into whichever directory you plan to place your code. We'll be putting this project on our desktop. You can always move it later if wish.Enter the following into the terminal:
cd ~/Desktop
Then type the following:
react-native init HelloWorld.
[ 56 ]
Saying HelloWorld in React Native
This uses the React Native CLI we installed earlier to create a directory called HelloWorld. It then downloads all the necessary dependencies needed to create our first React Native app. Downloading these assets should only take a few minutes if you're on a reasonably fast Internet connection. Once it's done installing, you should see something like the following in your terminal:
Now back in your terminal, run the following:
cd HelloWord
Then type the following:
open .
[ 57 ]
Saying HelloWorld in React Native
This will open up a new Finder window in your HelloWorld directory, as shown in the following screenshot:
[ 58 ]
Saying HelloWorld in React Native
Open the ios folder and then open HelloWorld.xcodeproject in Xcode, as shown in the following screenshot:
Once Xcode has completed indexing your project, you should see the message HelloWorld: Ready at the top center of the window. Click the build and run play button in the top left. This will launch your HelloWorld application in the selected simulator. In the preceding screenshot, the default simulator is the iPhone 7 Plus. If you'd like to change it to a different device, select a different simulator from the dropdown. Then click build and run.
You may be prompted with a message asking if you wish to Enable
Developer Mode on this Mac. Click on Enable and the app will continue building.
[ 59 ]
Saying HelloWorld in React Native
It's possible that the simulator device may look humongous on your screen, particularly if you picked one of the recent iPhones. To adjust this to something more reasonable, make sure the iOS Simulator is in the foreground. From the menu, select Window | Scale | 33%. Feel free to pick a different scale option that best suits your computer screen. Refer to the following screenshot:
Assuming everything ran successfully, you should see the default React Native app, as shown in the following screenshot:
[ 60 ]
Saying HelloWorld in React Native
Going forward, you actually don't even need to open Xcode to run React Native apps. Instead, run the following command from the root directory of your project:
react-native run-ios
This will launch the app directly in iOS simulator without needing to open Xcode.
React Native Packager
You may have noticed a separate terminal window spawn when running your HelloWorld app. This is the React Native Packager. It's a program similar to Browserify and Webpack, which are responsible for resolving dependencies, transpiling, and bundling the JavaScript, JSX, and other assets to be run on the device or simulator.
[ 61 ]
Saying HelloWorld in React Native
Understanding our HelloWorld app
This is great. We've got the app running in our simulator. Sure, it required a little bit of work to install the necessary tools, but the good news is that the only thing we'll need to do from this point forward is run the react-native init AppName command whenever we wish to create a new app. All our tools are installed and we're ready to start developing. But before we get into the meat and potatoes of React Native, let's quickly take a look at what exactly the React Native CLI provided us. Open up package.json in your text editor of choice. I'm using Atom (https://atom.io/) as shown in the following screenshot:
I want to call your attention to dependencies section. Here, you should only see two items listed: react and react-native. At the time of writing, React Native is at stable version 0.35.0. Given the pace of updates to React Native, yours is likely newer. That's fine. Just keep your version in mind when seeking out help with React Native questions. The last file I want to call your attention to is index.ios.j from the following screenshot:
[ 62 ]
Saying HelloWorld in React Native
There are four major parts to this file. Let's walk through them one by one.
Importing dependencies using ECMAScript 2015
It's worth calling this out since we'll be doing this a lot. At the top of this file, you'll see two ES2015 import statement. The syntax follows the pattern import something from 'somewhere';. Using the first import as an example, we are telling the React Native Packager that we require the React object from the React package found in the node_modules directory. When our application runs, the React Native Packager will resolve this dependency automatically, making the React object available in this file. You'll need to do this in every file you create that creates a React component.
[ 63 ]