React Native Tutorial: Building iOS and Android Apps with JavaScript

Christine Abernathy
React Native tutorial

React Native Tutorial: Build native iOS applications with JavaScript.

Update note: This tutorial has been updated to React Native 0.46, Xcode 9, and iOS 11 by Christine Abernathy. The previous tutorial was written by iOS Team member Tom Elliot.

In this React Native tutorial you’ll learn how to build native apps based on the hugely popular React JavaScript library.

What makes React Native different from other frameworks such as PhoneGap (Apache Cordova) or Appcelerator Titanium, that use JavaScript to create iOS apps?

  1. (Unlike PhoneGap) with React Native your code may be written in JavaScript but the app’s UI is fully native. It doesn’t have the drawbacks typically associated with a hybrid HTML5 app.
  2. Additionally (unlike Titanium), React introduces a novel, radical and highly functional approach to constructing user interfaces. Your UI is simply a function of the current app state.

React Native brings the React paradigm to mobile app development. It’s goal isn’t to write the code once and run it on any platform. The goal is to learn-once (the React way) and write-anywhere. An important distinction to make. This tutorial only covers iOS, but once you’ve learned the concepts here you could port that knowledge into creating an Android app very quickly.

If you have only ever written applications in Swift, you might not be particularly excited about the prospect of using JavaScript. However, the functional programming aspect should pique your interest!

Swift has introduced more functional ways to encode algorithms, as well as techniques that encourage transformation and immutability. However, 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.

The community has even added tools such as Expo and Create React Native App to help you quickly build React Native apps without having to touch Xcode or Android Studio!

This React Native tutorial takes you through the process of building an iOS app for searching UK property listings:

React Native tutorial finished app

Don’t worry if you’ve never written any JavaScript or used the CSS-like properties you’ll see. This tutorial will guide you through every step and provide resources where you can learn more.

Ready to get going? Read on!

Getting Started

React Native uses Node.js, a JavaScript runtime, to build your JavaScript code. If you don’t already have Node.js installed, it’s time to get it!

First install Homebrew using the instructions on the Homebrew website. Then install Node.js by executing the following in a Terminal window:

brew install node

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.

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; npm is similar in function to CocoaPods or Carthage and is packaged with Node.js.

Navigate to the folder where you would like to develop your app and run the following in Terminal:

react-native init PropertyFinder

This uses the CLI tool to create a starter project 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.

In Terminal, run:

cd PropertyFinder

In the created folders and files you will find a few items of note:

  • node_modules is a folder which contains the React Native framework
  • index.ios.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 counterparts to the above, although you won’t be touching those in this tutorial.

Open the PropertyFinder.xcodeproj in the ios folder with Xcode, then build and run. The simulator will start and display the following:

React Native welcome screen

If you encounter a compiler error indicating 'config.h' file not found in the third-party target, you may need to install Glog manually. Try these instructions.

If you see build-time warnings related to updating PropertyFinder 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, displaying the following:

React Native packager terminal window

This is the React Native packager, running under node. You’ll find out what it does shortly.

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: You’ll be mostly writing JavaScript code for this React Native tutorial. Instead of using Xcode, I use Sublime Text, which is a cheap and versatile editor, but Atom, Brackets or any other lightweight editor will do the job.

React Native Basics

In this section, you’ll be introduced to React Native basics as you begin working on PropertyFinder.

Open index.ios.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 {
  AppRegistry,
  StyleSheet,
  Text,
  View
} from 'react-native';

export default class PropertyFinder extends Component { ... } // 2

const styles = StyleSheet.create({ ... }); // 3

AppRegistry.registerComponent('PropertyFinder', () => PropertyFinder); // 4

Let’s go through the code step-by-step:

  1. Import the required modules.
  2. Defines the component that represents the UI.
  3. Creates a style object that controls the component’s layout and appearance.
  4. Registers the component that handles the app’s entry point.

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. This is roughly equivalent to linking and importing libraries in Swift. It also uses what’s called a destructuring assignment to bring in the Component object. Destructuring lets you extract multiple object properties and assign them to variables using a single statement.

Note: For more information about ES6 modules I’d recommend reading this blog post by Dr. Axel Rauschmayer.

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 index.ios.js, check out the class definition:

export default class PropertyFinder 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.

It’s time to start building your app.

In index.ios.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 PropertyFinder class replace render() with the following:

render() {
  return React.createElement(Text, {style: styles.description}, "Search for houses to buy!");
}

PropertyFinder 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. If you’ve done any web development before, you’ll probably recognize those property names. The React Native StyleSheet class used to style the application UI is similar to the Cascading Style Sheets (CSS) used on the Web.

Save your changes to index.ios.js and return to the simulator. Press Cmd+R, and you’ll see your fledgling property search app starting to take shape:

First signs of the React Native tutorial app

That’s a JavaScript application running in the simulator, rendering a native UI, without a browser in sight!

Still don’t trust me? :] 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!

React Native tutorial app's view hierarchy

Curious as to how it all works? In Xcode open AppDelegate.m and locate application:didFinishLaunchingWithOptions:. This method constructs a RCTRootView, which loads the JavaScript application from the index.ios 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.ios.bundle

Open this URL in Safari; you’ll see the JavaScript code for your app. You can find your “Search for houses to buy!” 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 PropertyFinder 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 index.ios.js. Modify the body of render to be the following:

return <Text style={styles.description}>Search for houses to buy! (Again)</Text>;

This is JSX, or JavaScript syntax extension, which mixes HTML-like syntax directly in your JavaScript code; if you’re already a web developer, this should feel rather familiar. You’ll use JSX throughout this article.

Save your changes to index.ios.js and return to the simulator. Press Cmd+R, and you’ll see your application refresh to display the updated message:

React Native tutorial app with 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:

Enabling live reload in a React Native iOS app

In index.ios.js, modify the render method’s body to the following:

return <Text style={styles.description}>Search for houses to buy!</Text>;

Save your changes. Note that the simulator automatically refreshes to reflect your changes:

Live reload in a React Native iOS app

Adding Navigation

PropertyFinder uses the standard stack-based navigation experience provided by UIKit’s navigation controller. It’s time to add this behavior.

In index.ios.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 PropertyFinder class definition with the following:

class SearchPage extends Component {

You’ve just renamed the class. You’ll see an error as a result, but will clean that up next.

Add the following class below the SearchPage component:

class PropertyFinder extends Component {
  render() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{
          title: 'Property Finder',
          component: SearchPage,
        }}/>
    );
  }
}

This constructs a navigation controller, applies a style and sets the initial route to the SearchPage component. In web development, routing is a technique for defining the navigation structure of an application, where pages — or routes — are mapped to URLs.

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:

React Native tutorial app with a navigator

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 Search Page

Add a new file named SearchPage.js and place it in the same folder as index.ios.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 SearchPage extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.description}>
          Search for houses to buy!
        </Text>
        <Text style={styles.description}>
          Search by place-name or postcode.
        </Text>
      </View>
    );
  }
}

render is a great demonstration of JSX and the structure it provides. Along with the style, you can very easily 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 index.ios.js and add the following just after the current import statements near the top of the file:

import SearchPage from './SearchPage';

This imports SearchPage from the file you just created – which will temporarily break your app! This is because SearchPage is now declared twice – once in SearchPage.js, and again in the current file.

Remove the SearchPage class and its associated description style from index.ios.js. You won’t be needing that code any longer.

Save your changes and return to the simulator to check out the new UI:

React Native tutorial app with a search label

Styling with Flexbox

So far, you’ve seen basic CSS properties that deal with margins, paddings and color. However, you might not be familiar with Flexbox, 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:

Flexbox elements arranged vertically and centered horizontally

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 SearchPage.js and insert the following just after the closing tag of the second Text element:

<View style={styles.flowRight}>
  <TextInput
    style={styles.searchInput}
    placeholder='Search via name or postcode'/>
  <Button
    onPress={() => {}}
    color='#48BBEC'
    title='Go'
  />
</View>

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:

React Native tutorial app with a Go button

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.

Handling Assets

The final step to complete the search screen of the application is to add the house graphic. Download and unzip the images zip file.

Next, create a directory in your root project folder and call it ’Resources’. Place the three images of the house in this directory.

Asset Catalogs: Apple recommends placing images in Asset Catalogs where possible. In React Native, however, it’s recommended not to. Placing your assets alongside your components helps to keep your components self contained, doesn’t require the app to be relaunched if you add new images. It also provides a single place for adding images if you are building for both iOS and Android.

Back in SearchPage.js, add the following beneath the closing tag of the View component that wraps the text input and button:

<Image source={require('./Resources/house.png')} style={styles.image}/>

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:

React Native tutorial app with a house image

Note: If you don’t see the house image at this point and see an image that “house” cannot be found instead, try restarting the packager (i.e. the react-native start command you have in the terminal).

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.

Adding Component State

A React component can manage its internal state through an object called, you guessed it, state. Whenever a component’s state changes, render() is called.

Within SearchPage.js, add the following code just before render():

constructor(props) {
  super(props);
  this.state = {
    searchString: 'london'
  };
}

Your component now has a state variable, with searchString set to an initial value of london.

Within render(), change TextInput to the following:

<TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  placeholder='Search via name or postcode'/>

This sets the TextInput value property — that is, the text displayed to the user — to the current value of the searchString state variable. This takes care of setting the initial state, but what happens when the user edits this text?

The first step is to create a method that acts as an event handler. Within the SearchPage class add the following method below the constructor:

_onSearchTextChanged = (event) => {
  console.log('_onSearchTextChanged');
  this.setState({ searchString: event.nativeEvent.text });
  console.log('Current: '+this.state.searchString+', Next: '+event.nativeEvent.text);
};

This defines a function using the => syntax. This is an arrow function, another recent addition to the JavaScript language that provides a succinct syntax for creating anonymous functions.

The function takes the value from the native browser event’s text property and uses it to update the component’s state. It also adds some logging code that will make sense shortly.

Note: JavaScript classes do not have access modifiers, so they have no concept of private. As a result you often see developers prefixing methods with an underscore to indicate that they should be considered private.

To wire up this method so it gets called when the text changes, return to the TextInput field within the render method and add an onChange property so the tag looks like the following:

<TextInput
  style={styles.searchInput}
  value={this.state.searchString}
  onChange={this._onSearchTextChanged}
  placeholder='Search via name or postcode'/>

Whenever the user changes the text, you invoke the function supplied to onChange; in this case, it’s _onSearchTextChanged.

There’s one final step before you refresh your app again: add the following logging statement to the top of render(), just before return:

console.log('SearchPage.render');

Save your changes and return to your simulator.

React Native tutorial app with initial state

You should see the text input’s initial value set to london. You should also see Xcode console logs when editing the text:

2017-07-04 00:05:48.642 [info][tid:com.facebook.React.JavaScript] SearchPage.render
2017-07-04 00:06:04.229 [info][tid:com.facebook.React.JavaScript] _onSearchTextChanged
2017-07-04 00:06:04.230 [info][tid:com.facebook.React.JavaScript] Current: london, Next: londona
2017-07-04 00:06:04.231 [info][tid:com.facebook.React.JavaScript] SearchPage.render

Looking at the console logs, the order of the logging statement seems a little odd:

  1. This is the initial call to render() to set up the view.
  2. You invoke _onSearchTextChanged() when the text changes.
  3. You call this.setState() to schedule an update to the component state to reflect the new input text. This triggers another render.
  4. You log the current and the next search text values.

Note: You may see frequent log messages related to nw_connection_get_connected_socket. These are due to attempted connections to the React debugging tools. Silence these 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.

Disabling React Native log messages in Xcode

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. Alternatively, the updates are done through an implicit link between the state and UI. For an example of the latter, see this article on implementing the MVVM pattern with ReactiveCocoa.

At this point you’ve probably spotted a fundamental flaw in this concept. Yes, that’s right — performance!

Surely you can’t just throw away your entire UI and re-build it every time something changes? This is where React gets really smart.

Each time the UI renders itself, it takes the view tree returned by your render methods, and reconciles — or diffs — it with the current UIKit view. The output of this reconciliation process is a simple list of updates that React needs to apply to the current view. That means only the things that have actually changed will re-render!

You can wrap your head around all that later; you still have some work to do in the app.

Initiating a Search

First, remove the logging code you just added above, since it’s no longer necessary.

In order to implement the search functionality you need to handle the Go button press, create a suitable API request, and provide a visual indication that a query is in progress.

Within SearchPage.js, update the initial state within the constructor:

this.state = {
  searchString: 'london',
  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 ?
  <ActivityIndicator size='large'/> : 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 search UI in return, add the following line below the Image to place the spinner:

{spinner}

Next, add the following methods to the SearchPage class:

_executeQuery = (query) => {
  console.log(query);
  this.setState({ isLoading: true });
};

_onSearchPressed = () => {
  const query = urlForQueryAndPage('place_name', this.state.searchString, 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 Go button is pressed.

To accomplish that, go back to the render method and replace the onPress prop for the Go Button as follows:

onPress={this._onSearchPressed}

Finally, add the following utility function just above the SearchPage class declaration:

function urlForQueryAndPage(key, value, pageNumber) {
  const data = {
      country: 'uk',
      pretty: '1',
      encoding: 'json',
      listing_type: 'buy',
      action: 'search_listings',
      page: pageNumber,
  };
  data[key] = value;

  const querystring = Object.keys(data)
    .map(key => key + '=' + encodeURIComponent(data[key]))
    .join('&');

  return 'https://api.nestoria.co.uk/api?' + querystring;
}

urlForQueryAndPage doesn’t depend on SearchPage, 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 Nestoria API to return the property listings.

Save your changes, head back to the simulator and press Go. You’ll see the activity indicator spin:

React Native tutorial app search screen with spinner

Your Xcode console should show something like this:

2017-07-04 00:23:23.769 [info][tid:com.facebook.React.JavaScript] https://api.nestoria.co.uk/api?country=uk&pretty=1&encoding=json&listing_type=buy&action=search_listings&page=1&place_name=london

Copy and paste that URL into your browser to see the result. You’ll see a massive JSON object. Don’t worry — you don’t need to understand that! You’ll add code to parse that now.

Performing an API Request

Still within SearchPage.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:

<Text style={styles.description}>{this.state.message}</Text>

You’ll use this to display a range of messages to the user.

Add the following code to the end of _executeQuery:

fetch(query)
  .then(response => response.json())
  .then(json => this._handleResponse(json.response))
  .catch(error =>
     this.setState({
      isLoading: false,
      message: 'Something bad happened ' + error
   }));

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 SearchPage:

_handleResponse = (response) => {
  this.setState({ isLoading: false , message: '' });
  if (response.application_response_code.substr(0, 1) === '1') {
    console.log('Properties found: ' + response.listings.length);
  } else {
    this.setState({ message: 'Location not recognized; please try again.'});
  }
};

This clears isLoading and logs the number of properties found if the query was successful.

Note: Nestoria has a number of non-1** response codes that are potentially useful. For example, 202 and 200 return a list of best-guess locations.

Save your changes, head back to the simulator and press Go. You should see an Xcode console log message saying that 20 properties (the default result size) were found:

2017-07-04 00:34:12.425 [info][tid:com.facebook.React.JavaScript] Properties found: 20

Also note that when this message is logged, the spinner goes away.

It’s time to see what those 20 properties actually look like!

Displaying the Results

Create a new file SearchResults.js, and add the following:

'use strict';

import React, { Component } from 'react'
import {
  StyleSheet,
  Image,
  View,
  TouchableHighlight,
  FlatList,
  Text,
} from 'react-native';

This imports the relevant modules you’ll use.

Next, add the component:

export default class SearchResults extends Component {
  _keyExtractor = (item, index) => index;

  _renderItem = ({item}) => {
    return (
      <TouchableHighlight
        underlayColor='#dddddd'>
        <View>
          <Text>{item.title}</Text>
        </View>
      </TouchableHighlight>
    );
    
  };

  render() {
    return (
      <FlatList
        data={this.props.listings}
        keyExtractor={this._keyExtractor}
        renderItem={this._renderItem}
      />
    );
  }
}

The above code makes use of a more specialized component — FlatList — which displays rows of data within a scrolling container, similar to UITableView. Here’s a look at the FlatList properties:

  • 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

Save your new file.

Add the following to SearchPage.js just beneath the import statements:

import SearchResults from './SearchResults';

This brings in the newly added SearchResults class.

In _handleResponse, replace the console.log statement with the following:

this.props.navigator.push({
  title: 'Results',
  component: SearchResults,
  passProps: {listings: response.listings}
});

This navigates to your newly added SearchResults 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 properties:

React Native tutorial app results screen

It’s great to see the property listings, but that list is a little drab. Time to liven things up a bit.

A Touch of Style

Add the following style definition at the end of SearchResults.js:

const styles = StyleSheet.create({
  thumb: {
    width: 80,
    height: 80,
    marginRight: 10
  },
  textContainer: {
    flex: 1
  },
  separator: {
    height: 1,
    backgroundColor: '#dddddd'
  },
  price: {
    fontSize: 25,
    fontWeight: 'bold',
    color: '#48BBEC'
  },
  title: {
    fontSize: 20,
    color: '#656565'
  },
  rowContainer: {
    flexDirection: 'row',
    padding: 10
  },
});

This defines all the styles that you 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;
    const price = item.price_formatted.split(' ')[0];
    return (
      <TouchableHighlight
        onPress={this._onPress}
        underlayColor='#dddddd'>
        <View>
          <View style={styles.rowContainer}>
            <Image style={styles.thumb} source={{ uri: item.img_url }} />
            <View style={styles.textContainer}>
              <Text style={styles.price}>{price}</Text>
              <Text style={styles.title}
                numberOfLines={1}>{item.title}</Text>
            </View>
          </View>
          <View style={styles.separator}/>
        </View>
      </TouchableHighlight>
    );
  }
}

This manipulates the returned price, which is in the format 300,000 GBP, to remove the GBP suffix. Then it renders the row UI using techniques that you are by now quite familiar with. Of note, an Image is added to the row and is loaded from a returned URL (item.img_url) which React Native decodes off the main thread.

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}) => (
  <ListItem
    item={item}
    index={index}
    onPressItem={this._onPressItem}
  />
);

_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:

React Native tutorial finished results screen

That looks a lot better — although it’s a wonder anyone can afford to live in London!

Tap the first row and check that the Xcode console reflects the selection:

2017-07-04 01:29:19.799 [info][tid:com.facebook.React.JavaScript] Pressed row: 0

Try tapping other listings or searching other locations in the UK.

Where To Go From Here?

Congratulations on completing this React Native tutorial! You can find the complete project here if you want to compare notes. :]

As a challenge, try showing a property’s details when the user selects one from the search list. You can check out the challenge solution if you get stuck.

Before opening the finished project or the challenge solution, first run npm install via the Terminal in the root folder of the project.

Check out the React Native’s source code if you’re curious. I suggest taking a look at this ES6 resource to continue brushing up on modern JavaScript.

If you’re a web developer, you’ve seen how to use JavaScript to easily create a native app. If you’re a native app developer, you’ve gained some appreciation for React Native’s fast iteration cycle. Whether you decide to use React Native in a future app or simply stick with Swift, I hope you’ve learned some interesting principles to apply to your next project.

If you have any questions or comments on this React Native tutorial, feel free to join the discussion in the forums below!

Team

Each tutorial at www.raywenderlich.com is created by a team of dedicated developers so that it meets our high quality standards. The team members who worked on this tutorial are:

Christine Abernathy

Christine is an Engineer on the Developer Advocacy team at Facebook. In this role, she is focused on helping grow the mobile developer ecosystem with emphasis on Android, iOS, and the mobile web. Prior to Facebook, Christine headed up engineering at Mshift, a mobile banking software provider, delivering iPhone apps and mobile browser-based products.

You can find Christine on Facebook.

Other Items of Interest

Save time.
Learn more with our video courses.

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 20 total!

Swift Team

... 15 total!

iOS Team

... 44 total!

Android Team

... 15 total!

macOS Team

... 11 total!

Unity Team

... 11 total!

Articles Team

... 15 total!

Resident Authors Team

... 17 total!

Podcast Team

... 8 total!

Recruitment Team

... 9 total!