Getting Started With React Native - Sample Chapter
Getting Started With React Native - Sample Chapter
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: