React Native App Development Guide
React Native App Development Guide
C o m m u n i t y
E x p e r i e n c e
D i s t i l l e d
Tom Bray
P U B L I S H I N G
pl
$ 29.99 US
19.99 UK
Sa
m
Ethan Holmes
ee
Ethan Holmes
Tom Bray
a B.Sc. in computer science from Simon Fraser University. He has primarily been
a full-stack web developer working and creating applications for start-ups in the
Silicon Beach area. Currently, he is stationed at Cargomatic, disrupting the freight
industry. After learning React for the web, learning React Native complemented the
skills he obtained as a web developer and allowed him to quickly make the transition
to mobile development.
You can follow him on Twitter at @sherclockholmes.
Tom Bray has been developing for the web since the browser wars of the late 90s
when DHTML was the buzzword of the day. Creating great user experiences using
the cutting edge technologies of the day has always been his passion, from Flash to
Flex to Adobe AIR to React, and React Native.
He has created sophisticated software that has been used by large companies, such
as Adobe, MySpace, Cisco, Informatica, and Dell; it has been a key contributor to
numerous start-ups where he has worn many hats and gained a broad skill set.
He currently serves as the Principal Software Architect with Cargomatic where he
has designed a system to orchestrate the movement of ocean freight to and from
America's portsa solution that leveraged React Native to assign work to truck
drivers and track their progress.
You can follow him on Twitter at @tombray.
Preface
Why are there so many alternatives to using native languages to write mobile apps?
And, more importantly, why does the world need yet another approach? Obviously,
there must be a problem that hasn't been solved.
Developers want to use just one language to develop for both iOS and Android. Web
developers want to reuse their existing JavaScript knowledge and leverage the web
frameworks they already know and love. This is why Apache Cordova (PhoneGap)
exists. By wrapping a web browser in a native app, developers can package their
HTML, CSS, and JavaScript applications in a native shell, but why aren't all mobile
applications based on Cordova?
Users expect native performance, with a native user experience. Hybrid apps
don't solve the user's problems, they solve the developer's problems. We need a
technology that can do both!
React Native changes the game with applications that are truly native. It doesn't
use a WebView or transpile JavaScript to native languages. Think of it as native UI
components being controlled by a JavaScript brain. The result is a user experience
that is indistinguishable from any other native app, and a developer experience that
leverages the amazing productivity benefits of JavaScript and the React framework.
Armed with React Native, you'll finally be able to leverage your web development
skills in the mobile world without sacrificing quality or performance. It's the Holy
Grail, and we're excited to show you what React Native can do and to see what
amazing apps you create with it!
Preface
[ 21 ]
React Native 0.14.2 requires Node.js v4+, we are going to use v5.0.0; visit
https://nodejs.org for more information (we recommend managing
different node versions with NVM https://github.com/creationix/nvm)
Great, now that we have these tools we can install the react-native-cli.
The react-native-cli exposes an interface that does all the work of setting
up a new React Native project for us:
1. To install react-native-cli, use the npm command:
npm install -g react-native-cli
2. Next, we are going to generate a new React Native project called ReactNotes
using the cli and the react-native init command. The output of the
command looks similar to the following:
$ react-native init ReactNotes
This will walk you through the creation of a new React Native project in
/Users/ethanholmes/ReactNotes.
3. Set up a new React Native app in /Users/ethanholmes/ReactNotes:
create .flowconfig
create .gitignore
create .watchmanconfig
create index.ios.js
create index.android.js
create ios/main.jsbundle
create ios/ReactNotes/AppDelegate.h
create ios/ReactNotes/AppDelegate.m
create ios/ReactNotes/Base.lproj/LaunchScreen.xib
create ios/ReactNotes/Images.xcassets/AppIcon.
appiconset/Contents json
create ios/ReactNotes/Info.plist
create ios/ReactNotes/main.m
create ios/ReactNotesTests/ReactNotesTests.m
create ios/ReactNotesTests/Info.plist
create ios/ReactNotes.xcodeproj/project.pbxproj
create ios/ReactNotes.xcodeproj/xcshareddata/xcschemes/
ReactNotes.xcscheme
[ 22 ]
Chapter 3
create android/app/build.gradle
create android/app/proguard-rules.pro
create android/app/src/main/AndroidManifest.xml
create android/app/src/main/res/values/strings.xml
create android/app/src/main/res/values/styles.xml
create android/build.gradle
create android/gradle.properties
create android/settings.gradle
create android/app/src/main/res/mipmaphdpi/ic_launcher.png
create android/app/src/main/res/mipmapmdpi/ic_launcher.png
create android/app/src/main/res/mipmapxhdpi/ic_launcher.png
create android/app/src/main/res/mipmapxxhdpi/ic_launcher.png
create android/gradle/wrapper/gradle-wrapper.jar
create android/gradle/wrapper/gradle-wrapper.properties
create android/gradlew
create android/gradlew.bat
create android/app/src/main/java/com/reactnotes/
MainActivity.java
[ 23 ]
3. Click on Run (or Cmd + R) to run the application in the iOS simulator,
the following screenshot will be shown:
[ 24 ]
Chapter 3
Just like that, we already have the React Native template up and running on
the iOS simulator!
[ 25 ]
When running the project in the iOS simulator, we can run it from the Xcode IDE.
Android, on the other hand, doesn't require any particular IDE and can be launched
directly from the command line.
To install the android apk to the emulator, use the following command:
$ react-native run-android
Let's start by modifying the contents of the starter template and display a
different message.
[ 26 ]
Chapter 3
A lot of things are included in here, but bear with us as we break it down for you.
If we take a closer look at the render method, we can see the familiar View and Text
components that we encountered in the previous chapter. Note how the index file is
a component itself (ReactNotes). Change the value in line 30 to Welcome to React
Notes!. Save it and then press Cmd + R from the simulator or, in the top menu,
navigate to Hardware | Shake Gesture and select Reload from the pop-up action
sheet. The text on screen re-renders to show the text value we just modified! We are
no longer constrained to wait for the Xcode to recompile in order to see our changes
as we can reload straight from the simulator. Continue making changes and reload it
in the simulator to get a feel for the work flow.
[ 28 ]
Chapter 3
[ 30 ]
Chapter 3
We're off to a good start; it's time to add some interactivity to our button.
In SimpleButton.js, add the TouchableOpacity component to the
destructuring assignment. TouchableHighlight, TouchableOpacity, and
TouchableWithoutFeedback are similar components that respond to touches,
and it takes an onPress prop for a function to react to the touch. Wrap the
existing code in the render function with the TouchableOpacity component:
import React, {
Text,
TouchableOpacity,
View
} from 'react-native';
[ 31 ]
Go ahead and try tapping (or clicking) on the text now, you should be able to see that
the opacity of the text decreases as you press it. But where has our console.log()
output gone? Open the Developer menu (Hardware | Shake Gesture) and select
Debug in Chrome. This opens a Chrome Window at localhost:8081/debugger-ui
for debugging, as shown in the following screenshot:
Lo and behold, here is the console log that we specified in our SimpleButton
component. Behind the scenes, the JavaScript code is being run from inside the
Chrome tab and loaded onto the mobile device on startup or reload. From here,
you have access to all the Chrome Developer Tools you will normally use, including
the addition of break points.
Navigation
Now, its time to make our application more actionable. Let's begin by transforming
our SimpleButton into a Create Note button. When the user clicks on the Create
Note button, it transitions them to another screen to create notes. To do this, we need
our button to be able to accept a function via props from index.ios.js to activate
the transition. We will add some custom text as well for extra flair:
import React, {
Text,
TouchableOpacity,
View
} from 'react-native';
[ 32 ]
Chapter 3
export default class SimpleButton extends React.Component {
render () {
return (
<TouchableOpacity onPress={this.props.onPress}>
<View>
<Text>{this.props.customText || 'Simple Button'}</Text>
</View>
</TouchableOpacity>
);
}
}
SimpleButton.propTypes = {
onPress: React.PropTypes.func.isRequired,
customText: React.PropTypes.string
};
[ 33 ]
render () {
return (
<Navigator
initialRoute={{name: 'home'}}
renderScene={this.renderScene}
/>
);
}
[ 34 ]
Chapter 3
When we first load our application, the parameter route will be the object we
pass into initialRoute. Using a switch statement and looking at the values of
route.name allows us to choose the component we want to render:
renderScene (route, navigator) {
switch (route.name) {
case 'home':
return (
<View style={styles.container}>
<SimpleButton
onPress={() => console.log('Pressed!')}
customText='Create Note'
/>
</View>
);
case 'createNote':
}
}
Here, under the home case, you can see our slightly modified code from the original
render method in ReactNotes; we have included the onPress and customText
props we created earlier. You can add another component to App/Componets/
named NoteScreen.js; this screen will contain the functionality to create a
new note:
import React, {
StyleSheet,
Text,
View
} from 'react-native';
export default class NoteScreen extends React.Component {
render () {
return (
<View style={styles.container}>
<Text>Create Note Screen!</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
});
[ 35 ]
For now, we are only going to use this screen when we press the Create Note button.
In the onPress prop arrow function, we are going to push a new route onto the stack
using navigator.push:
import NoteScreen from './App/Components/NoteScreen';
class ReactNotes extends React.Component {
renderScene (route, navigator) {
switch (route.name) {
case 'home':
return (
<View style={styles.container}>
<SimpleButton
onPress={() => {
navigator.push({
name: 'createNote'
});
}}
customText='Create Note'
/>
</View>
);
case 'createNote':
return (
<NoteScreen />
);
}
}
Note that push also takes a regular JavaScript object, so we need to include the name
attribute for our NoteScreen; reload the application in the simulator and press on
the Create Note button. A smooth animated transition between the two screens will
occur without adding any extra code.
Navigator.NavigationBar
At this point you must be thinking A button is OK, but is there a better, more native way
to do navigation? Of course, as a part of the Navigator component, you can pass a
navigationBar prop to add a persistent top navigation bar across every screen. The
Navigator.NavigationBar is a subcomponent that accepts an object that defines the
left and right buttons, a title, and styles (although we are going to leave it unstyled
until the next chapter). Modify the ReactNotes render function to include the
navigationBar, as shown:
render () {
return (
<Navigator
[ 36 ]
Chapter 3
initialRoute={{name: 'home'}}
renderScene={this.renderScene}
navigationBar={
<Navigator.NavigationBar
routeMapper={NavigationBarRouteMapper}
/>
}
/>
);
}
The routeMapper prop accepts an object containing functions for the LeftButton,
RightButton, and Title attributes. Let's insert this object after the imports at the top
of index.ios.js:
var NavigationBarRouteMapper = {
LeftButton: function(route, navigator, index, navState) {
...
},
RightButton: function(route, navigator, index, navState) {
...
},
Title: function(route, navigator, index, navState) {
...
}
};
Advancing the flow of our application to the CreateNote screen will require
displaying a right-hand button in the navigator bar. Luckily, we already have our
simple button set up to push the state onto the navigator. In the RightButton
function, add:
var NavigationBarRouteMapper = {
...
RightButton: function(route, navigator, index, navState) {
switch (route.name) {
case 'home':
return (
<SimpleButton
onPress={() => {
navigator.push({
name: 'createNote'
[ 37 ]
The navigator.pop() will remove the route on the top of the stack; thus, returning
us to our original view. Finally, to add a title, we do the exact same thing in the
Title attributes function:
var NavigationBarRouteMapper = {
...
Title: function(route, navigator, index, navState) {
switch (route.name) {
[ 38 ]
Chapter 3
case 'home':
return (
<Text>React Notes</Text>
);
case 'createNote':
return (
<Text>Create Note</Text>
);
}
}
};
Now, let's update the original renderScene function to get rid of the button
and include the home screen as a component. Create a new component called
HomeScreen; the contents of this screen won't matter much, as we will come
back to it later:
import React, {
StyleSheet,
Text,
View
} from 'react-native';
export default class HomeScreen extends React.Component {
render () {
return (
<View style={styles.container}>
<Text>Home</Text>
</View>
);
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}
});
[ 39 ]
Now, let's see how the navigation bar persists across each route:
[ 40 ]
Chapter 3
That's it! Reload and take a look at how the static navigation bar persists across
each route:
For a more detailed guide on Navigator, check out the React Native documentation
at https://facebook.github.io/react-native/docs/navigator.html. We
now have the proper infrastructure to go ahead and start adding the create note
functionality to our application.
[ 41 ]
Before we add the TextInput components to the View, let's get a few style updates
out of the way:
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
marginTop: 64
},
title: {
height: 40
},
body: {
flex: 1
}
});
Note that we've added a marginTop: 64 to the container. This is important because
we want to make sure that the NavigationBar doesn't accidentally intercept the
onPress events we want our TextInput to receive. We've also added styles for
each of the TextInputs we're about to add. We'll talk more about styles in detail
in Chapter 4, Working with Styles and Layout.
[ 42 ]
Chapter 3
Now, in our render function, let's replace the Text component with two TextInput
components, such as:
render () {
return (
<View style={styles.container}>
<TextInput placeholder="Untitled"
style={styles.title}/>
<TextInput multiline={true}
placeholder="Start typing" style={styles.body}/>
</View>
);
}
Before we try this out, notice that the TextInput component has a placeholder
property that allows us to tell the user what the TextInput is for without having to
take up additional screen real estate by labeling our form fields. I've also specified
multiline={true} on the second TextInput so the user can add as much text as
they want.
Now let's refresh the application in the simulator and you should see something
like this:
[ 43 ]
You should be able to click into TextInput and start typing. If you'd like to use the
on-screen keyboard available in the simulator, you can press Cmd+K / Ctrl+K.
Let's improve the user experience a little bit by making the title TextInput focus
automatically and show the keyboard when the user navigates to the NoteScreen:
<TextInput
ref="title"
autoFocus={true}
placeholder="Untitled"
style={styles.title}
/>
To be even more user friendly, let's listen for the event that tells us the user has
finished editing the title and automatically set focus on the body TextInput. To do
that we'll need to make a slight change to the body TextInput so that we can refer
to it in our event handler:
<TextInput
ref="body"
multiline={true}
placeholder="Start typing"
style={styles.body}
/>
Notice the ref="body". Any React component can be given a ref so that it can be
referenced in your javascript code. Now, in the title TextInput, we can add an
onEndEditing event handler that sets focus on the TextInput body:
<TextInput
autoFocus={true}
placeholder="Untitled"
style={styles.title}
onEndEditing={(text) => {this.refs.body.focus()}}
/>
Now when you refresh the application in the simulator and navigate to the
NoteScreen, you will see that the title TextInput has focus and you should be able
to type something. Press Enter and see the focus automatically switch to the body
and start typing there as well. If you're not seeing the on-screen keyboard when you
try this, press Cmd + K / Ctrl + K and try again.
[ 44 ]
Chapter 3
Summary
In this chapter, we have created the skeleton of our ReactNotes application, walked
you through how to create a new project, created Views and custom components,
navigated between the HomeScreen and NoteScreen, and debugged your application.
You now have a solid foundation for all of the topics we'll introduce throughout the
rest of the book. However, there are two big problems with this application, it's not
pretty and it doesn't do anything! In the next two chapters, we'll solve both of those
problems and you'll be well on your way to master React Native!
[ 45 ]
www.PacktPub.com
Stay Connected:
'Props' in React Native are used to pass data and event handlers down to child components, making them integral for customizing component behavior. Meanwhile, 'state' is used to manage dynamic data within a component, allowing for responsive and interactive user interfaces by updating the component's view when the state changes. Props enable component reuse with different inputs, while state provides a way to store and update component-specific data internally .
The Navigator component in React Native allows for managing screen transitions like a stack, where routes can be pushed, popped, or replaced. It acts as a customizable implementation of iOS and Android navigation controllers, enabling developers to pass route objects with attributes to transition between different screens. This provides a native-feeling navigation structure in mobile apps .
Flexbox in React Native offers a flexible layout mechanism that adapts to different screen sizes, which is critical for mobile apps that need to support a wide range of devices. It helps in designing complex layouts with minimal code, aligning items predictably and responsively. This capability simplifies cross-platform styling, ensuring consistent visual output and an optimal user experience across Android and iOS .
To debug a React Native application using Chrome Developer Tools, developers should open the Developer menu in the emulator or on the device by using a shake gesture or a debugger-specific command. This action brings up a debugging window in Chrome at localhost:8081/debugger-ui, where all JavaScript code executed on the mobile device can be inspected. Developers can then use familiar Chrome tools such as breakpoints to debug their code .
Efficient component rendering in React Native can be achieved by optimizing component update cycles with pure components which render only when props or states change. Developers should use shouldComponentUpdate lifecycle methods to prevent unnecessary renders. Avoiding inline functions and object literals in render methods can also improve performance by minimizing garbage collection overhead. Additionally, `FlatList` over `ListView` for lists can enhance scrolling performance due to better memory management .
React Native uses the AsyncStorage API for data persistence, which allows developers to store simple key-value pairs locally on a device. However, its limitations include being synchronous in JavaScript, which could lead to performance bottlenecks in large-scale data scenarios, and it isn't intended for complex or large datasets, for which developers might need to consider using more robust storage solutions like SQLite or incorporating cloud-based databases .
React Native optimizes performance by using native UI components instead of relying on web technologies like WebView. By leveraging the Virtual DOM, it efficiently updates the UI by only rendering changes rather than the entire view, similar to how React functions in web applications. This results in faster performance and reduced load on device resources, maintaining a native-like experience in mobile apps .
React Native's Navigation Bar can be customized by using props such as `routeMapper` to define custom functions for the navigation buttons and titles across screens. Developers can specify components for `LeftButton`, `RightButton`, and `Title`, adapting them to different routes and interactions. By setting styles and custom components, the navigation bar can be designed to fit a specific thematic look or enhance interactivity through functional navigational entries .
Integrating native modules in React Native involves using third-party native libraries to extend the base functionality provided by React Native. This process requires a good understanding of native Android and iOS development, as developers need to write platform-specific code that interfaces with React Native's JavaScript layer. The challenges include managing the compatibility of native and JavaScript code, ensuring performance, and handling updates or changes in native module APIs .
React Native combines the productivity benefits of JavaScript and the React framework with the capability to create truly native applications. It does this by using native UI components controlled by a JavaScript brain, which results in user experiences indistinguishable from those of native apps. Developers can leverage web development skills for mobile platforms without sacrificing performance or quality, making it a versatile tool for both developers and end-users .