INTRODUCTION
===============
In this tutorial we will introduce you to building native apps using the React JavaScript library.
# Good to know
React Native is different from other JavaScript frameworks:
1) In React Native, you use JS, but the app's UI is fully native (unlike PhoneGap) It doesn't have the drawbacks typically associated with a hybrid HTML5 app.
2) Your UI is a function of the current app state, you are not burdened with constantly updating it
3) React Native's isn't "write once and run it on any platform", but once you learn should not be hard to port it into creating an Android app quickly.
Swift is Apple's latest language for building iOS Apps. The way in which you construct your UI is very much the same as it was when developing with Objective-C: it's still UIKit-based and imperative.
We will be using XCode in this tutorial, but you can find community tools like Expo (https://expo.io/learn) to build React Native apps without having to touch XCode or Android Studio.
#2 What are we building
We are going to build an iOS app for searching movie listings and showtimes. The app will show how easy it is to consume APIs and do fun things.
The tutorial will try to guide you through every step, step by step. This is a simple tutorial, I hope you will get a good feel
for what it is like to create native apps that consume real APIs.
API_KEYS
===============
To save time, we will be using my developer API Keys in this tutorial. You can easily apply for your own after this session.
It's possible we may run into Throttling or Thresholds using the keys, but let's see how it goes
The Movie Database: api_key: '33546cde72549b76aec722c9b3e80943'
[you don't need it right now, but to apply for your own developer key, go here: https://developers.themoviedb.org/3/getting-started/introduction]
GraceNote: api_key: 'hkmmsu8mgj8te8hutxbff6nc'
[to get your own developer key, go here: http://developer.tmsapi.com/member/register]
You don't have to do anything with these note, just note that we will use them later.
CREDIT/ACKNOWLEDGMENTS
=========================
Inspirations for this tutorial are:
1) the Movie Database API documentation and code examples
2) the Gracenote Developer APIs documentation
3) Christine Abernathy and her Facebook team's apartment listings tutorial
GETTING STARTED
==================
We are going to go live through installing and getting our environment ready.
As anything live and with scripts and frameworks, potetial for things to go wrong is high. Since we have limited time, we will be taking
"the happy path", and that means I will be running it live and hopefully everybody can follow along on their Macs. But if people run into
configuration or environment issues, we may not be able to debug case-by-case, as it would derail the entire thing.
OK, let's get going:
0) Install XCode on your Mac, we will use it to log console messages and launch the Simulator
React Native uses Node.js, a JavaScript runtime, to build your JavaScript code. If you don't already have Node.js installed, let's get it.
1) We will use a package manager called Homebrew to install node. Use the instructions on the Homebrew website:
https://brew.sh/
2) Then, install Node.js by executing the following in a Terminal window:
brew install node
3) Next, use homebrew to install watchman, a file watcher from Facebook:
brew install watchman
This is used by React Native to figure out when your code changes and rebuild accordingly. It's like having Xcode do a build each time you save your file.
4) Next use npm to install the React Native Command Line Interface (CLI) tool:
npm install -g react-native-cli
This uses the Node Package Manager to fetch the CLI tool and install it globally
Navigate to the folder where you would like to develop your app. For example, I created a folder called React in my home folder. In there,
run the following in Terminal:
react-native init MoviesAppv1
This uses the CLI tool to create a starter project, in its own folder, containing everything you need to build and run a React Native app.
If you get complaints about the version of node, make sure the one installed by brew is the one in use. Run brew link --overwrite node in the terminal.
OK, now in Terminal, run:
cd MoviesAppv1
In the created folders and files you will find a few things:
node_modules is a folder which contains the React Native framework
index.js is the entry point created by the CLI tool
App.js is the skeletal app created by the CLI tool
ios is a folder containing an Xcode project and the code required to bootstrap your application
android is a folder containing Android-related code, you won't be touching those in this tutorial.
Open the MoviesAppv1.xcodeproj in the ios folder with Xcode. Select product->destination on the XCode menu, and pick iPhone 8 plus as the target device for example. Then build and run (Product->Build, and Product->Run on the XCode menu)
The simulator will start and display the initial skeleton app
If you see build-time warnings related to updating your project settings, go ahead and make those updates. Ignore warnings related to the other projects. Many of the warnings may be related to unused parameters. You may also see null pointer logic errors in the yoga library that are actually not errors.
You might also have noticed that a terminal window has popped up. This is Metro Bundler, the React Native JavaScript bundler running under Node.js. Leave it running, you need it.
Note: If you get an error starting up the packager, then in Terminal run: react-native start
Don't close the terminal window; just keep it running in the background. If you do close it by mistake, simply stop and re-run the project via Xcode.
Note: We will be mostly writing JavaScript code for this React Native tutorial. Instead of using Xcode, I use Sublime Text, other editors are fine as well.
REACT NATIVE BASICS
=======================
In this section, you'll be introduced to React Native basics as you begin working on MoviesAppv1.
Open App.js in your text editor of choice and take a look at the structure of the code in the file:
import React, { Component } from 'react'; // 1
import {
Platform,
StyleSheet,
Text,
View
} from 'react-native';
const instructions = Platform.select({ ... }); // 2
export default class App extends Component<{}> { ... } // 3
const styles = StyleSheet.create({ ... }); // 4
Let's go through the code step-by-step:
1) Imports the required modules.
2) Sets up a platform-specific display message.
3) Defines the component that represents the UI.
4) reates a style object that controls the component's layout and appearance.
Take a closer look at this import statement:
import React, { Component } from 'react';
This uses the ECMAScript 6 (ES6) import syntax to load the react module and assign it to a variable called React.
ES6 is a much nicer way to write JavaScript, supporting features like default parameters, classes, arrow functions, and destructuring assignments. Apple has supported ES6 since iOS 10, but older browsers may not be compatible with it. React Native uses a tool called Babel to automatically translate modern JavaScript into compatible legacy JavaScript where necessary.
Back to App.js, check out the class definition:
export default class App extends Component<{}> {
This defines a class which extends a React Component. The export default class modifier makes the class “public”, allowing it to be used in other files.
Open index.js and take a look at the entry point file:
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('MoviesAppv1', () => App);
This registers the imported component that serves as the app's entry point.
It's time to start building your app.
In App.js, add the following at the top of the file, just before the import statements:
'use strict';
This enables Strict Mode, which adds improved error handling and disables some less-than-ideal JavaScript language features. In simple terms, it makes JavaScript better!
Inside the App class replace render() with the following:
render() {
return React.createElement(Text, {style: styles.description}, "Search for movies!");
}
App extends React.Component, the basic building block of the React UI. Components contain immutable properties, mutable state variables and expose a method for rendering. Your current application is quite simple and only requires a render method.
React Native components are not UIKit classes; instead they are a lightweight equivalent. The framework takes care of transforming the tree of React components into the required native UI.
Next, replace the const styles statement with the following:
const styles = StyleSheet.create({
description: {
fontSize: 18,
textAlign: 'center',
color: '#656565',
marginTop: 65,
},
});
This defines a single style that you've applied to the description text. The React Native StyleSheet class used to style the application UI is similar to the Cascading Style Sheets (CSS) used on the Web.
Then, get rid of the instructions assignment code block as you no longer need it.
Save your changes to App.js and return to the simulator. Press Cmd+R, and you'll see your fledgling movie search app starting to take shape
That's a JavaScript application running in the simulator, rendering a native UI, without a browser in sight.
Verify it for yourself: within Xcode, select Debug\View Debugging\Capture View Hierarchy and take a look at the native view hierarchy. You will see no UIWebView instances anywhere. Your text is being displayed in a view called RCTText. But what is that? Back in Xcode select File\Open Quickly… and type in RCTView.h. Notice RCTView inherits directly from UIView. Neat!
In Xcode open AppDelegate.m and locate application:didFinishLaunchingWithOptions:. This method constructs a RCTRootView, which loads the JavaScript application from the index JavaScript file and renders the resultant view.
The Terminal window that was opened when you ran this application started a packager and server that allows your JavaScript code to be fetched, by default on port 8081. For example:
http://localhost:8081/index.bundle?platform=ios
Open this URL in Safari; you'll see the JavaScript code for your app. You can find your “Search for movies!” description code embedded among the React Native framework.
When your app starts, this code is loaded and executed by the JavaScriptCore framework. In the case of your application, it loads the App component, then constructs the native UIKit view.
USING JSX
==============
Your current application uses React.createElement to construct the simple UI for your application, which React turns into the native equivalent. While your JavaScript code is perfectly readable in its present form, a more complex UI with nested elements would rapidly become quite a mess.
Make sure the app is still running, then return to your text editor to edit App.js. Modify the body of render to be the following:
return Search for movies (Again);
This is JSX, or JavaScript syntax extension, which mixes HTML-like syntax directly in your JavaScript code. The tag represents a React component for displaying text. Text supports nesting, styling, and touch handling. More details here: https://facebook.github.io/react-native/docs/text.html
When an element type starts with a lowercase letter, it refers to a built-in component like
. A React component or a user-defined component will start with a capital letter.
You can also read more about JSX here: https://www.reactenlightenment.com/react-jsx/5.1.html
Save your changes to App.js and return to the simulator. Press Cmd+R, and you'll see your application refresh to display the updated message
Re-running a React Native application is really as simple as refreshing a web browser! Note that this will only reflect changes made to your JavaScript files – native code or resource changes will require a rebuild in Xcode.
You can even skip having to refresh the app by enabling live reload. Press Cmd+D in the simulator then select Enable Live Reload:
In App.js, modify the render method's body to the following:
return Search for movies;
Save your changes. Note that the simulator automatically refreshes to reflect your changes:
ADDING NAVIGATION
===================
We are going to use the standard stack-based navigation experience provided by UIKit's navigation controller.
In App.js, find the import statements near the top and add a comma following the View destructuring assignment. Then add the following below it:
NavigatorIOS,
This brings in NavigatorIOS that you'll use for navigation.
Next, replace the App class definition (just the "export default..." line) with the following:
class WelcomePage extends Component<{}> {
And add the following class below the WelcomePage component:
export default class App extends Component<{}> {
render() {
return (
);
}
}
This constructs a navigation controller, applies a style and sets the initial route to the SearchPage component.
Within the same file, add the following to the styles list under the closing brace of the description style:
container: {
flex: 1,
},
This tells the component using this style to fill all available space. This ensures that the component's children are visible.
Save your changes and check the simulator to see the updated UI:
There's the navigation controller with its root view, which is currently the search description text. Excellent — you now have the basic navigation structure in place.
BUILDING OUT THE WELCOME PAGE
========================================
Add a new file named WelcomePage.js and place it in the same folder as App.js. Add the following code to this file:
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TextInput,
View,
Button,
ActivityIndicator,
Image,
} from 'react-native';
This imports the modules you'll need to build the UI.
Add the following Component subclass after the import statements:
export default class WelcomePage extends Component<{}> {
render() {
return (
Search for movies
playing near your zip.
);
}
}
You can visualize the UI constructed by this component: a container with two text labels.
Now, add the following style code at the bottom of the file:
const styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 18,
textAlign: 'center',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
},
});
Again, these are standard CSS properties. Setting up styles like this is less visual than using Interface Builder, but it's better than setting view properties one by one in your viewDidLoad() methods.
Save your changes.
Open App.js and add the following just after the current import statements near the top of the file:
import WelcomePage from './WelcomePage';
This imports SearchPage from the file you just created.
Remove the SearchPage class and its associated description style from App.js. You won't be needing that code any longer. This may also be a good time to get rid of the all unused imports: Platform, Text and View.
Save your changes and return to the simulator to check out the new UI.
STYLES AND FLEXBOX
======================
So far, you've seen basic CSS properties that deal with margins, paddings and color. Flexbox is a more recent addition to the CSS specification that's useful for handling complex layout across different screen sizes.
React Native uses the Yoga library under the hood to drive layout. Yoga is a C implementation of Flexbox and it includes bindings for Swift, Objective-C, Java (for Android), and C# (for .NET).
Generally you use a combination of flexDirection, alignItems, and justifyContent Yoga properties to manage your layout.
So far, your layout has a container with two children arranged vertically:
This is due to the default flexDirection value of column being active. flexDirection helps define the main axis and cross axis. Your container's main axis is vertical. It's cross axis is therefore horizontal.
alignItems determines the placement of children in the cross axis. Your app has set this value to center. This means the children are center-aligned.
You're going to see some other layout options at play.
Open WelcomePage.js and insert the following just after the closing tag of the second Text element:
You've added a view that holds a text input and a button.
In your styles definition, add the following new styles below the container style:
flowRight: {
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'stretch',
},
searchInput: {
height: 36,
padding: 4,
marginRight: 5,
flexGrow: 1,
fontSize: 18,
borderWidth: 1,
borderColor: '#48BBEC',
borderRadius: 8,
color: '#48BBEC',
},
These set the placement of the text input and button.
Save your changes and check the simulator to see your updates
The text field and Go button are on the same row, so you've wrapped them in a container view using the flowRight style which uses flexDirection: 'row' to horizontally place the items in a row.
You've also added a flexGrow: 1 style to the text input. Yoga first lays out the text input and button according to their sizes. It then distributes the remaining space according to the flexGrow values. The text input therefore takes over the remaining space.
The basic Button doesn't provide much UI customization. You could build and customize your own buttons using TouchableOpacity or TouchableHighlight.
Let's do that, replace your render View in WelcomePage.js with this:
render() {
return (
Welcome to the Perks at Work
Movie Tickets App
{}}
underlayColor='#fff'>
Now Playing
);
}
You will need to import TouchableOpacity on your import statements atop:
TouchableOpacity,
and now add the button classes to your styles:
searchButton:{
marginRight:40,
marginLeft:40,
marginTop:50,
paddingTop:10,
paddingBottom:10,
backgroundColor:'#669ae0',
borderRadius:0,
borderWidth: 1,
borderColor: '#fff'
},
searchButtonText:{
color:'#fff',
textAlign:'center',
paddingLeft : 10,
paddingRight : 10,
fontSize: 24,
},
ADDING ASSETS
=====================
The final step to complete the welcome screen of the application is to add the movies graphic. You can use this image for example:
(or any other you want)
https://s3.amazonaws.com/gp.cdn.images/assets/deals/rs-125-off-on-2-movie-tickets-at-bookmyshow-1488966247.jpg
Ceate a directory in your root project folder and call it Resources. Place the movies image in this directory, and rename it to tix.jpg
Back in WelcomePage.js, add the following beneath the closing tag of the Text component w/ the welcome message:
Now, add the image's corresponding style to the end of the style list:
image: {
width: 217,
height: 138,
},
Save your changes and check out your new UI
Your current app looks good, but it's somewhat lacking in functionality. Your task now is to add some state to your app and perform some actions.
COMPONENT STATE
====================
A React component can manage its internal state through an object called state. Whenever a component's state changes, render() is called.
Within WelcomePage.js, add the following code inside the WelcomePage class, just before render():
constructor(props) {
super(props);
this.state = {
};
}
You may notice a lot of React Native console log error messages. This is due to attempted connections to the debugging tools. Silence some temporarily by going to Product \ Scheme \ Edit Scheme and finding the Arguments tab of the Run config. Add a new environment variable called OS_ACTIVITY_MODE with the value disable.
Build and run the project for the settings to take effect.
A React component state change triggers a UI update. This de-couples the rendering logic from state changes affecting the UI. Most other UI frameworks put the onus on you to update the UI based on state changes.
STARTING A MOVIE LISTINGS SEARCH
==================================
In order to implement the search functionality you need to handle the button press, create an API request to the Movie Database, and provide a visual indication that a query is in progress.
Within WelcomePage.js, update the initial state within the constructor:
this.state = {
isLoading: false,
};
The new isLoading property will keep track of whether a query is in progress.
Add the following logic to the start of render:
const spinner = this.state.isLoading ?
: null;
This is a ternary if statement that optionally adds an activity indicator, depending on the component's isLoading state. Because the entire component is rendered each time, you are free to mix JSX and JavaScript logic.
Within the JSX that defines the UI, add the following line below the button (which we created with a TouchableOpacity):
{spinner}
Next, add the following methods to the WelcomePage class, after the render method:
_executeQuery = (query) => {
console.log(query);
this.setState({ isLoading: true });
};
_onSearchPressed = () => {
const query = urlForQueryAndPage('region', 'US', 1);
this._executeQuery(query);
};
_executeQuery() will eventually run the query, but for now it simply logs a message to the console and sets isLoading appropriately so the UI can show the new state.
_onSearchPressed() configures and initiates the search query. This should kick off when the button is pressed.
To accomplish that, go back to the render method and replace the onPress prop for the Button as follows:
onPress={this._onSearchPressed}
Finally, add the following utility function just above the WelcomePage class declaration:
function urlForQueryAndPage(key, value, pageNumber) {
const data = {
api_key: '33546cde72549b76aec722c9b3e80943',
}
data[key] = value;
const querystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'https://api.themoviedb.org/3/movie/now_playing?' + querystring;
}
urlForQueryAndPage doesn't depend on WelcomePage, so it's implemented as a free function rather than a method. It first creates the query string based on the parameters in data. Then it transforms the data into name=value pairs separated by ampersands. Finally, it calls the Movie Database API to return the property listings.
Save your changes, head back to the simulator and press the button. You'll see the activity indicator spin.
Your console shows:
2018-02-14 18:35:11.032 [info][tid:com.facebook.react.JavaScript] https://api.themoviedb.org/3/movie/now_playing?api_key=33546cde72549b76aec722c9b3e80943®ion=US
Copy and paste that URL into your browser to see the result. You'll see a JSON object coming from the Movie Database
THE API REQUEST
=================
Within WelcomePage.js, update the initial state in the class constructor to add a message variable to the end of the list:
message: '',
Within render, add the following to the bottom of your UI, right after the spinner:
{this.state.message}
You'll use this to display a range of messages to the user.
Add the following code to the end of _executeQuery, inside the function:
fetch(query)
.then(r => r.json())
.then(json => this._handleResponse(json))
.catch(error =>
this.setState({
isLoading: false,
message: 'Something bad happened ' + error
}));
console.log('Done');
This makes use of the fetch function, which is part of the Fetch API. The asynchronous response is returned as a Promise. The success path calls _handleResponse which you'll define next, to parse the JSON response.
Add the following function to WelcomePage:
_handleResponse = (response) => {
this.setState({ isLoading: false , message: '' });
console.log('Movies Playing: ' +response.results.length);
};
This clears isLoading and logs the number of movies playing found if the query was successful.
Save your changes, head back to the simulator and press Go. You should see an Xcode console log message saying that 20 movies or so found
Also note that when this message is logged, the spinner goes away.
It's time to see what those 20 movies look like
DISPLAYING THE RESULTS
===========================
Create a new file MoviesPlaying.js, and add the following:
'use strict';
import React, { Component } from 'react'
import {
StyleSheet,
Image,
View,
TouchableHighlight,
FlatList,
Text,
} from 'react-native';
Next, add the component:
export default class MoviesPlaying extends Component<{}> {
_keyExtractor = (item, index) => index.toString();
_renderItem = ({item}) => {
return (
{item.title}
);
};
render() {
return (
);
}
}
The above code makes use of a more specialized component — FlatList — which displays rows of data within a scrolling container, similar to UITableView.
-data provides the data to display
-keyExtractor provides a unique key that React uses for efficient list item management
-renderItem specifies how the UI is rendered for each row
Add the following to WelcomePage.js just beneath the import statements:
import MoviesPlaying from './MoviesPlaying';
This brings in the newly added MoviesPlaying class.
In _handleResponse, add the following after the console.log statement:
this.props.navigator.push({
title: 'Now Playing',
component: MoviesPlaying,
passProps: {listings: response.results}
});
This navigates to your newly added MoviesPlaying component and passes in the listings from the API request. Using the push method ensures the search results are pushed onto the navigation stack, which means you'll get a Back button to return to the root.
Save your changes, head back to the simulator and press Go. You'll be greeted by a list of movies.
It's good to see the listings, but that list is a little boring. Time to liven things up a bit.
ADDING SOME STYLE
==================
Add the following style definition at the end of MoviesPlaying.js:
const styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 24,
textAlign: 'center',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
},
thumb: {
width: 80,
height: 80,
marginRight: 10
},
textContainer: {
flex: 1
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
votes: {
fontSize: 14,
fontWeight: 'bold',
color: '#656565'
},
title: {
fontSize: 18,
color: '#656565'
},
rowContainer: {
flexDirection: 'row',
padding: 10
},
});
This defines all the styles we are going to use to render each row.
Add a new component representing a row by adding the following just under the import statements:
class ListItem extends React.PureComponent {
_onPress = () => {
this.props.onPressItem(this.props.index);
}
render() {
const item = this.props.item;
return (
{item.title}Rating: {item.vote_average} ({item.vote_count} votes)Release Date: {item.release_date}
);
}
}
This renders the row UI. A poster Image is added to the row and is loaded from a returned URI
You may have noticed that this component extends React.PureComponent. React re-renders a Component if its props or state changes. React only re-renders a PureComponent if a shallow compare of the state and props shows changes. Used under the right conditions, this can give your app a performance boost.
Now replace _renderItem with the following:
_renderItem = ({item, index}) => (
);
_onPressItem = (index) => {
console.log("Pressed row: "+index);
};
_onPressItem is passed into ListItem to handle a row selection. This design pattern is equivalent to a callback. In this callback, the index for the selected row is logged.
Save your work, head back to the simulator, press Go, and check out your results.
That looks better
Tap the first row and check that the Xcode console reflects the selection:
2017-11-26 23:29:19.943 [info][tid:com.facebook.react.JavaScript] Pressed row: 0
MOVIE DETAILS
================
The last piece of the tutorial is handling what happens when you click on a movie.
We will re-use everything we've learned so far, and repeat it, so I won't go over the code in as much detail, I am just re-using a lot of the same concepts we've learned.
The first we will do, is render a bigger poster and the movie details. We will be using the data we already have from the Movie Database API.
The second thing we will do is make a second API call, this one to GraceNote, another API provider, and retrieve Showtimes at nearby theatres for the movie we selected. Unfortunately, the movieIDs from GraceNote and the Movie Database do not match. So, in order to retrieve showtimes we will loop through the Gracenote results and match movies by name and release date. Once we get a match, we will retrieve and render the showtimes.
While we are at it, we will pick up the cast and rating from the GraceNote API results, and render that info on our page.
Let's go ahead, to start create a few file, call it MovieDetails.js, and populate it with this code
'use strict';
import React, { Component } from 'react'
import {
StyleSheet,
Image,
View,
TouchableHighlight,
FlatList,
Text,
ActivityIndicator,
Linking,
} from 'react-native';
const zipcode = '10016';
function urlForQueryAndPage(key, value, pageNumber) {
const data = {
zip: zipcode,
imageSize: 'Md',
imageText: 'true',
page: pageNumber,
api_key: 'hkmmsu8mgj8te8hutxbff6nc',
};
data[key] = value;
const querystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'https://data.tmsapi.com/v1.1/movies/showings?' + querystring;
}
function formatISODate(date) {
var d = new Date(date),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
return [year, month, day].join('-');
}
function formatDate(date,short) {
var d = new Date (date),
year = d.getFullYear(),
month = d.getMonth()+1,
day = d.getDate(),
min = d.getMinutes(),
hour = d.getUTCHours();
if (day < 10) {day = '0' + day;}
if (month < 10) {month = '0' + month;}
if (min < 10) {min = '0' + min;}
if (hour < 12) {min = min + ' am';}
if (hour > 12) {
hour = hour -12;
min = min + ' pm';
}
if (hour ==12) {min = min + ' pm';}
var formattedDate;
if (short ==0) {formattedDate= month + '/' + day + '/' + year + ' ' + hour + ':' + min;}
else {formattedDate=month + '/' + day + '/' + year;}
return formattedDate;
}
export default class MovieDetails extends Component<{}> {
constructor(props) {
super(props);
this.state = {
isLoading: false,
message: '',
locations: '',
rating: '',
cast: '',
};
}
componentDidMount() {
const query = urlForQueryAndPage('startDate', formatISODate(new Date()), 1);
this._executeQuery(query);
console.log('GrandChild did mount.');
}
render() {
var item = this.props.movie;
const spinner = this.state.isLoading ? : null;
return (
{item.title}Reviews: {item.vote_average} ({item.vote_count} votes)Release Date: {formatDate(item.release_date,1)}Rating: {this.state.rating} Cast: {this.state.cast} Linking.openURL('https://www.perksatwork.com/movies/index/uSource/TXTST')}>
Buy Tickets
OVERVIEW: {item.overview}
{spinner}
{this.state.locations}
);
}
_executeQuery = (query) => {
console.log("second query:" + query);
this.setState({ isLoading: true });
fetch(query)
.then(r => r.json())
.then(json => this._handleResponse(json))
.catch(error =>
this.setState({
isLoading: false,
message: 'Something bad happened ' + error
}));
console.log('Done');
};
_handleResponse = (response) => {
this.setState({ isLoading: false , message: '' });
console.log('Gracenote Count: ' +response.length);
console.log('First location: ' +response[0].showtimes[0].theatre.name);
var item = this.props.movie;
for (var i=0; i0) {
if (response[i].showtimes[j].theatre.name != response[i].showtimes[j-1].theatre.name) {showtimes=showtimes + "\n"}
}
showtimes=showtimes+ eval(j+1) + ". " +response[i].showtimes[j].theatre.name + ": " + formattedDate + "\n";
}
this.setState({locations: showtimes});
this.setState({rating: response[i].ratings[0].code});
this.setState({cast: response[i].topCast.toString()});
}
};
}
const styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 24,
textAlign: 'center',
color: '#656565'
},
locations: {
marginTop: 100,
marginLeft:10,
fontSize: 14,
textAlign: 'left',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
},
thumb: {
width: 180,
height: 240,
marginRight: 10,
marginTop: 100
},
textContainer: {
flex: 1,
marginLeft:10,
marginRight:10
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
votes: {
fontSize: 14,
fontWeight: 'bold',
color: '#656565',
marginTop: 7
},
title: {
fontSize: 22,
color: '#656565',
marginTop: 100
},
rowContainer: {
flexDirection: 'row',
padding: 10
},
});
Everything in here is stuff by now you are familiar with: the imports, some utility functions, including date manipulation, state management, use of fetch to make the API call, the render function and the styles.
The only new thing is a function called componentDidMount that gets called after render ends, and we use to make our API call as soon as the page is loaded.
Note also that the GraceNote API for movies now playing requires you pass along the current date.
Another little thing you may have noticed is the Buy Tickets link. You can add a link by using the syntax Linking.openURL(), and need to include the corresponding Linking module in your imports.
Lastly, go back to MoviesPlaying, and add this after your imports
import MovieDetails from './MovieDetails';
that lets you use the newly created class
Then, still in MoviesPlaying, add the following to your _onPressItem function, after the console.log call:
this.props.navigator.push({
title: 'Movie Details',
component: MovieDetails,
passProps: {movie: this.props.listings[index]}
});
This simply navigates you to the new MovieDetails page.
Notice how you are passing along just the details of the movie selected via this.props.listings[index]
=====================
That's it, you've seen how to use JavaScript to easily create a native app. Hope you enjoyed and had fun!