React Native Cookbook

4.5 (2 reviews total)
By Crysfel Villa , Stan Bershadskiy
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Getting Started

About this book

React has taken the web development world by storm, and it is only natural that the unique architecture and its ecosystem of third-party support be applied to native application development. This book will take you through the basics of React Native development all the way through to some more advanced components.

This book covers topics in React Native ranging from adding basic UI components to successfully deploying for multiple target platforms. The book follows a top-down approach beginning with building rich user interfaces. These UIs will be created with both built-in and custom components that you will create, style, and animate.

You will then learn about different strategies for working with data, including leveraging the popular Redux library and optimizing the performance of the application. Then, you will step further into exposing native device functionality. Finally, we will discuss how to put your application into production and maintain its reliability.

Publication date:
December 2016
Publisher
Packt
Pages
536
ISBN
9781786462558

 

Chapter 1. Getting Started

In this chapter, we will cover the following recipes:

  • Adding styles to text and containers

  • Using images to mimic a video player

  • Creating a toggle button

  • Displaying a list of items

  • Adding tabs to the viewport

  • Using flexbox to create a profile page

  • Setting up a navigator

 

Introduction


React Native is a fast-growing library. Over the last year it has become very popular among the open source community. There's a new release every other week that improves performance, adds new components, or provides access to new APIs on the device.

While this is great for the most part, it comes with a drawback. Sometimes, things on our project will break with a new release; we need to be very careful when updating our projects. As a rule of thumb, never update to the latest version without reading the release notes, which usually describe the breaking changes. On top of that, we need to make sure that any of the third-party libraries we are using in our projects are up to date with the new release.

In this chapter, we will learn about the most common components within the library. This book aims to reach out to developers who have already completed the Getting Started Guide on the React Native documentation, which means we will jump right to the good stuff and avoid things such as installing the environment and Hello World examples.

To step through all the recipes in this chapter, we will have to create a new app, so make sure you have your environment up and running. I recommend you follow the documentation on the React Native official website, then run the following commands on your terminal for each recipe, where AnAppName is the name of your app:

$ react-native init --verbose AnAppName
$ cd AnAppName
$ react-native run-android
$ react-native run-ios
 

Adding styles to text and containers


We have several components at our disposal, but containers and texts are the most common and useful components to create layouts or other components. In this recipe, we will see how to use containers and text, but most importantly we will see how styles work in React Native.

We will create a UI for a simple music player; we won't be using icons for now, but we will add them later.

Getting ready

Please follow the instructions in the introduction in order to create a new app with the name ContainersText.

How to do it...

  1. Let's start by creating an src folder in the root of the project. This is where all our JavaScript code will be placed.

  2. Create a new JavaScript called MainApp.js:

  3. In the MainApp.js file, we are going to create a stateless component; this component will mimic a small music player. For now, it will only display the name of the song and a bar to show the progress:

           import React from 'react'; 
           import { StyleSheet, Text, View } from 'react-native'; 
    
  4. Once we have imported the dependencies, we can write the component as follows:

            const MainApp = () => {
              const name = '01 - Hey, this is my life';
    
              return (
                <View style={styles.container}>
                  <View style={styles.innerContainer} />
                  <Text style={styles.title}>
                    <Text style={styles.subtitle}>Playing:</Text> {name}
                  </Text>
                </View>
              );
            };
    
  5. We have our component ready, so now we need to add some styles in order to add colors and font styles:

            const styles = StyleSheet.create({ 
              container: { 
                margin: 10, 
                marginTop: 100, 
                backgroundColor: '#e67e22', 
                borderRadius: 5, 
              }, 
              innerContainer: { 
                backgroundColor: '#d35400', 
                height: 50, 
                width: 150, 
                borderTopLeftRadius: 5, 
                borderBottomLeftRadius: 5, 
              }, 
              title: { 
                fontSize: 18, 
                fontWeight: '200', 
                color: '#fff', 
                position: 'absolute', 
                backgroundColor: 'transparent', 
                top: 12, 
                left: 10, 
              }, 
              subtitle: { 
                fontWeight: 'bold', 
              }, 
            }); 
    
  6. In order to use this component outside of this file, we need to export it, as follows:

            export default MainApp; 
    
  7. The next step is to import our new component in the index.ios.js and index.android.js files. The code will be the same for both platforms:

            import React, { Component } from 'react'; 
            import { AppRegistry } from 'react-native'; 
            import MainApp from './src/MainApp'; 
     
            AppRegistry.registerComponent('ContainersText', () => MainApp); 
    
  8. In order to see our changes in the simulators, we need to reload the app. For iOS, you can press cmd + R ; for Android, click on the menu button and then the refresh button:

How it works...

Let's take a look at what we did in the previous recipe. In steps 3 to 6 we created our component with the necessary styles. There are several things going on in these steps, so let's dig deeper.

In step 3, we included the dependencies of our component. In this case, we will use a View, which is a container; if you are familiar with web development, a View is similar to a div. We can add more Views inside other Views, Texts, Lists, and any other custom component that we create or import from a third-party library.

In step 4, we defined the name of our component. In this case, it will be MainApp. As a convention, we should always use the same name for the file and for the component. As you can see, this is a stateless component, which means it doesn't have any state; it's a pure function and doesn't support any of the life cycle methods.

We are defining a name constant, but in real-world applications this data should come from the props. In the return we are defining the JSX that we are going to need to render our component, along with a reference to the styles.

Each component has a property called style; this property receives an object with all the styles that we want to apply to the given component. Styles are not inherited (except for the Text component) to the children components, which means we need to set individual styles for each component.

In step 5, we defined the styles for our component. We are using the StyleSheet API to create all our styles. As previously mentioned, all we need is an object containing the styles; however, by using the StyleSheet API instead of a simple plain object, we gain some performance optimizations, as the styles will be reused for every renderer as opposed to creating an object every time the render method gets executed.

The properties in the styles object are very straightforward. If you are a web developer it, should be really easy to get used to this, because it's similar to CSS; however, it's not the same. We have margins, paddings, width, height, border width, border color, border radius, and many more properties. I recommend you take a look at the documentation to find out all the available properties.

In step 7, we only imported our new component and used it as the component that will bootstrap our app.

There's more...

I'd like to call to your attention to the definition of the title style in step 5. Here, we have defined a property called backgroundColor and set transparent as its value. As a good exercise, let's comment this line of code and see the result:

On iOS, the text will have an orange background color and it might not be what we really want to accomplish in our UI. In order to fix this, we need to set the background color of the text as transparent. But the question is, why is this happening? The reason is that React Native adds some optimizations to the text by setting the color from the parent's background color. This will improve the rendering performance because the rendering engine will not have to calculate the pixels around each letter of the text and the rendering will be executed faster.

Note

Think carefully when setting the background color as transparent. If the component is going to be updating the content very frequently, there might be some performance issues with text, especially if the text is too long.

 

Using images to mimic a video player


Images are an important part of any UI. Whether we use them to display icons, avatars, or pictures, with React Native you can do all of that. In this recipe, we will use images to create a video player. We will also display the icons from the local device, and a large image from a remote server on Amazon S3.

Getting ready

In order to follow the steps in this recipe, its necessary to create an empty app using the React Native CLI. Follow the instructions in the introduction of this chapter if you don't know to create an empty app. We are going to name it LoadingImages.

We are going to display a few images in our app to mimic a video player, so make sure to download the assets for this recipe.

How to do it...

  1. The first thing we are going to do is to create a new folder called src. Inside this folder, we need to create a file called MainApp.js, and an images folder to store our icons. Our project should look as follows:

  2. In the MainApp.js file, we are going to include all the dependencies we'll need for this component:

            import React from 'react'; 
            import { StyleSheet, View, Image } from 'react-native'; 
    
  3. We need to require the images that will be displayed in our component. By defining a constant we can use the same image in different places. We might need to restart the package server to successfully load the images, especially if we are using Windows:

            const playIcon = require('./images/play.png'); 
            const volumeIcon = require('./images/sound.png'); 
            const hdIcon = require('./images/hd-sign.png'); 
            const fullScreenIcon = require('./images/full-screen.png'); 
            const remoteImage = { 
            uri:
              'https://s3.amazonaws.com/crysfel/
               public/book/new-york.jpg' }; 
    
  4. We are going to use a stateless component to render the JSX. We'll use all the images we have declared in the previous step:

            const MainApp = () => { 
             return ( 
                <Image source={remoteImage} style={styles.fullscreen}> 
                  <View style={styles.container}> 
                    <Image source={playIcon} style={styles.icon} /> 
                    <Image source={volumeIcon} style={styles.icon} /> 
                    <View style={styles.progress}> 
                      <View style={styles.progressBar} /> 
                    </View> 
                    <Image source={hdIcon} style={styles.icon} /> 
                    <Image source={fullScreenIcon} style={styles.icon} /> 
                  </View> 
                </Image> 
              ); 
            }; 
    
  5. Once we have the elements that we are going to render, we need to define the styles for each element:

            const styles = StyleSheet.create({ 
              fullscreen: { 
                flex: 1, 
              }, 
              container: { 
                position: 'absolute', 
                backgroundColor: '#202020', 
                borderRadius: 5, 
                flexDirection: 'row', 
                height: 50, 
                padding: 5, 
                paddingTop: 16, 
                bottom: 30, 
                right: 10, 
                left: 10, 
                borderWidth: 1, 
                borderColor: '#303030', 
              }, 
              icon: { 
                tintColor: '#fff', 
                height: 16, 
                width: 16, 
                marginLeft: 5, 
                marginRight: 5, 
              }, 
              progress: { 
                backgroundColor: '#000', 
                borderRadius: 7, 
                flex: 1, 
                height: 14, 
                margin: 10, 
                marginTop: 2, 
              }, 
              progressBar: { 
                backgroundColor: '#bf161c', 
                borderRadius: 5, 
                height: 10, 
                margin: 2, 
                width: 80, 
              }, 
            }); 
    
  6. In order to use our component, we need to export it. This is a very simple step that requires only one line of code:

            export default MainApp; 
    
  7. Finally, we need to import our new component inside index.ios.js and index.android.js:

            import React, { Component } from 'react'; 
            import { AppRegistry } from 'react-native'; 
            import MainApp from './src/MainApp'; 
            AppRegistry.registerComponent('LoadingImages', () => MainApp); 
    
  8. We are done! Just refresh the app on the simulators and you should see something like this:

How it works...

In step 2, we required the Image component; this is the component responsible for rendering images from the local filesystem on the device or from a remote server.

In step 3, we required all the images. It's a good practice to require the images outside of the component in order to only require them once, and then we can use them in our component. On every renderer, React Native will use the same image; if we are dealing with dynamic images from a remote server, then we should require them on every renderer.

The require function accepts the path of the image as a parameter; the path is relative to the folder that our class is. For remote images, we need to use an object defining the uri where our file is.

In step 4, a stateless component was declared. We are using the remoteImage as the background of our app. In order to set an image in the background, we need to define all the other elements as children of the image. There's not a backgroundUrl property, such as in CSS.

The source property of the Image accepts an object to load remote images or a reference to the required file. It's very important to explicitly require every image that we want to use, because, when we prepare our app for distribution, images will be added to the bundle automatically. This is the reason we should avoid doing things like the following:

const iconName = playing ? 'pause' : 'play'; 
const icon = require(iconName); 

The preceding code will not include the images in the final bundle. As a result, we will have errors when trying to access these images. Instead, we should refactor our code as follows:

const pause = require('pause'); 
const play = require('playing'); 
const icon = playing ? pause : play; 

This way, the bundle will include both images when preparing our app for distribution, and we will decide dynamically which image to display.

In step 5, we defined the styles. Most of the properties are self-explanatory; the tintColor property might be a bit confusing, but this property is basically setting the color of the image, in this case, to white. I will talk more about flex in separate recipes, but for now let's just say that flexDirection: 'row' is allowing us to align the icons horizontally.

In step 7, we included the MainApp class in the iOS and Android index files. After this, we should be able to run our application on the simulators.

There's more...

In this recipe, we have used flexbox to horizontally arrange the controls of the player. If you want to learn more about flexbox, take a look at Using flexbox to create a profile page. For more advanced content, you should go to Chapter 2, Implementing Complex User Interfaces.

 

Creating a toggle button


We all know that buttons are an essential UI component in every application. We use buttons for navigation, to trigger API calls, and so on. In this recipe, we will create a toggle button, which by default is going to be unselected. When the user taps on it, we will change the styles of the button to make it look selected.

We will learn how to detect the tap event, use an image as the UI, keep the state of the button, and add styles based on the component state.

Getting ready

Let's create a new app using the React Native CLI. We are going to name it ButtonsAndEvents. We are going to use one image in this recipe; make sure to download the assets for this recipe or feel free to use your own image.

How to do it...

  1. We need to create an src folder where our source will be stored; inside this folder we will create an images folder and a MainApp.js file:

  2. Let's import the dependencies for this class:

            import React, { Component } from 'react'; 
            import { 
              StyleSheet, 
              View, 
              Image, 
              Text, 
              TouchableHighlight, 
            } from 'react-native'; 
     
            const heartIcon = require('./images/plain-heart.png'); 
    
  3. For this particular recipe, we need to keep the state of the button when pressed; therefore, we need to create a class that extends from Component, as follows:

            class MainApp extends Component { 
              state = { 
                liked: false, 
              }; 
            
              _onPressBtn = () => { 
                // We will define the content on step 6 
              } 
     
              render() { 
                // We will define the content on step 4 
              } 
            } 
    
  4. We need to define the content of our new component inside the render method; here, we are going to define the Image button and a Text underneath:

            render() { 
              return ( 
               <View style={styles.container}> 
                  <TouchableHighlight 
                    style={styles.btn} 
                    underlayColor="#fefefe" 
                  > 
                    <Image 
                source={heartIcon} 
                style={styles.icon} 
                  /> 
                  </TouchableHighlight> 
                  <Text style={styles.text}>Do you like this app?</Text> 
                </View> 
              ); 
            } 
    
  5. Let's define some styles to set dimensions, position, margins, colors, and so on:

            const styles = StyleSheet.create({ 
              container: { 
                marginTop: 50, 
                alignItems: 'center', 
              }, 
              btn: { 
                borderRadius: 5, 
                padding: 10, 
              }, 
              icon: { 
                width: 180, 
                height: 180, 
                tintColor: '#f1f1f1', 
              }, 
              liked: { 
                tintColor: '#e74c3c', 
              }, 
              text: { 
                marginTop: 20, 
              }, 
            }); 
    
  6. If we run the project on the simulators, we should have something similar to the following screenshot:

  7. In order to respond to the tap event, we need to define the content of the _onPressBtn function and assign it as a callback to the onPress property:

           class MainApp extends Component { 
             state = { 
               liked: false, 
             }; 
     
             _onPressBtn = () => { 
               this.setState({ 
                 liked: !this.state.liked, 
               }); 
             } 
     
             render() { 
               return ( 
                 <View style={styles.container}> 
                   <TouchableHighlight 
                     onPress={this._onPressBtn} 
                     style={styles.btn} 
                     underlayColor="#fefefe" 
                   > 
                     <Image source={heartIcon} style={styles.icon} /> 
                   </TouchableHighlight> 
                   <Text style={styles.text}>Do you like this app?</Text> 
                 </View> 
               ); 
             } 
           } 
    
  8. If we test our code, we won't see anything changing on the UI, even though the state on the component is changing when we press the button. Let's add a different color to the image when the state changes; that way, we will be able to see some response from the UI:

            render() { 
             const likedStyles = this.state.liked ? styles.liked : null; 
      
             return ( 
               <View style={styles.container}> 
                 <TouchableHighlight 
                   onPress={this._onPressBtn} 
                   style={styles.btn} 
                   underlayColor="#fefefe" 
                 > 
                   <Image 
                     source={heartIcon} 
                     style={[styles.icon, likedStyles]} 
                   /> 
                 </TouchableHighlight> 
                 <Text style={styles.text}>Do you like this  app?</Text> 
               </View> 
             ); 
           } 
     
    
  9. We are almost done with this class; the only thing that is missing is the export. At the bottom of the file we can add the following line:

           export default MainApp; 
    
  10. Finally, we need to update the index.ios.js and index.android.js files to import and use our new class:

           import React, { Component } from 'react'; 
           import { AppRegistry } from 'react-native'; 
           import MainApp from './src/MainApp'; 
     
           AppRegistry.registerComponent
           ('ButtonsAndEvents', () => MainApp); 
    

How it works...

In step 2, we imported the TouchableHighlight component. This is the component responsible for handling the touch event.

When the user touches the active area, the content will be highlighted based on the underlayColor value we have set.

In step 3, we defined the state of the Component. In this case, there's only one property on the state, but we can add as many as needed. In Chapter 2, Implementing Complex User Interfaces, we will see more recipes about handling the state in more complex scenarios.

In step 6, we used the setState method to change the value of the liked property. This method is inherited from the Component class that we are extending.

In step 7, based on the current state of the liked property, we used the styles to set the color of the image to red, or we returned a null to avoid applying any styles. When assigning the styles to the Image component, we used an array to assign many objects; this is very handy because, internally, the component will merge all the styles into one single object. The objects with the highest index will overwrite the properties from the lowest object index in the array:

There's more...

In a real application, we are going to use several buttons, sometimes with an icon aligned to the left, a label, different sizes, and colors, and so on. It's highly recommended to create a reusable component to avoid duplicating code all over our app. In Chapter 2, Implementing Complex User Interfaces, we will create a button component to handle some of these scenarios.

 

Displaying a list of items


Lists are everywhere: A list of orders on the user's history, a list of available items in a store, a list of songs to play; basically, any application will need to display information in a list.

For this recipe, we are going to display several items in a list component. We are going to define a JSON file with some data, then we are going to load this file using a simple require to finally render each item with a nice but simple layout.

Getting ready

Let's start by creating an empty app--in this case we will name it ListItems. We are going to need an icon to display on each item, so please download the assets for this recipe or use your own .png image.

How to do it...

  1. We will start by creating an src folder. Inside this folder we will have the MainApp.js file and the sales.json file:

  2. Inside the sales.json file, we will define the data that we are going to display on the list. Here's some sample data:

           [ 
             {"items": 5, "address": "140 Broadway, New York, NY 11101",
             "total": 38, "date": "May 15, 2016"} 
           ] 
    
  3. To avoid cluttering the pages of this book, I've only defined one record, but go ahead and add more content to the array. Copying and pasting the same object multiple times will do the trick. In addition, you could change some values on the data.

  4. Let's open the index.ios.js and index.android.js files, remove the existing code, and add the following to import dependencies and register the app:

           import React, { Component } from 'react'; 
           import { AppRegistry } from 'react-native'; 
           import MainApp from './src/MainApp'; 
     
           AppRegistry.registerComponent('ListItems', () => MainApp); 
    
  5. In the previous step, we imported the MainApp component, but it's not defined yet. Let's open the src/MainApp.js file and import the dependencies:

           import React, { Component } from 'react'; 
           import { 
             StyleSheet, 
             View, 
             ListView, 
             Image, 
             Text, 
           } from 'react-native'; 
           import data from './sales.json'; 
     
           const basketIcon = require('./images/basket.png'); 
    
  6. Now we need to create the class to render the list of items. We are going to keep the sales data on the state; that way, we could insert or remove elements easily:

           class MainApp extends Component { 
             constructor(props) { 
               super(props); 
               var ds = new ListView.DataSource({ 
                 rowHasChanged: (r1, r2) => r1 !== r2 
               }); 
           
               this.state = { 
                 dataSource: ds.cloneWithRows(data), 
               }; 
             } 
     
             renderRow(record) { 
               // Defined on step 8 
             } 
     
             render() { 
               // Defined on step 7 
             } 
           } 
     
           export default MainApp; 
    
  7. In the render method, we need to define the ListView component and we will use the renderRow method to render each item. The dataSource property defines the array of elements that we are going to render on the list:

           render() { 
             return ( 
               <View style={styles.mainContainer}> 
                 <Text style={styles.title}>Sales</Text> 
                 <ListView 
                   dataSource={this.state.dataSource} 
                   renderRow={this.renderRow} 
                 /> 
               </View> 
             ); 
           } 
    
  8. Now we can define the content of renderRow. This method receives each object containing all the information we need. We are going to display the data in three columns. In the first column we will show an icon, in the second column we will show the number of items for each sale and the address where this order will ship, and the third column will display the date and the total:

           renderRow(record) { 
             return ( 
               <View style={styles.row}> 
                 <View style={styles.iconContainer}> 
                   <Image source={basketIcon} style={styles.icon} /> 
                 </View> 
                 <View style={styles.info}> 
                   <Text style={styles.items}>{record.items} Items</Text> 
                   <Text style={styles.address}>{record.address}</Text> 
                 </View> 
                 <View style={styles.total}> 
                   <Text style={styles.date}>{record.date}</Text> 
                   <Text style={styles.price}>${record.total}</Text> 
                 </View> 
               </View> 
             ); 
           } 
    
  9. Once we have the JSX defined, it's time to add the styles. First, we will define colors, margins, paddings, and so on for the main container, and the title and the row container. In order to create the three columns for each row, we need to use the flexDirection: 'row' property. We will learn more about this property in another recipe in this chapter:

           const styles = StyleSheet.create({ 
             mainContainer: { 
               flex: 1, 
               backgroundColor: '#fff', 
             }, 
             title: { 
               backgroundColor: '#0f1b29', 
               color: '#fff', 
               fontSize: 18, 
               fontWeight: 'bold', 
               padding: 10, 
               paddingTop: 40, 
               textAlign: 'center', 
             }, 
             row: { 
               borderColor: '#f1f1f1', 
               borderBottomWidth: 1, 
               flexDirection: 'row', 
               marginLeft: 10, 
               marginRight: 10, 
               paddingTop: 20, 
               paddingBottom: 20, 
             }, 
           }); 
    
  10. If we refresh the simulators, we should see something similar to the following screenshot:

  11. Now, inside the StyleSheet definition, let's add styles to the icon. We are going to add a yellow circle as the background and change the color of the icon to white:

           iconContainer: { 
             alignItems: 'center', 
             backgroundColor: '#feb401', 
             borderColor: '#feaf12', 
             borderRadius: 25, 
             borderWidth: 1, 
             justifyContent: 'center', 
             height: 50, 
             width: 50, 
           }, 
           icon: { 
             tintColor: '#fff', 
             height: 22, 
             width: 22, 
           }, 
    
  12. After this change, we will see a nice icon on the left side of each row, as shown in the following screenshot:

  13. Finally, the styles for the text. We need to set the color, size, fontWeight, padding and a few other things:

             info: { 
               flex: 1, 
               paddingLeft: 25, 
               paddingRight: 25, 
             }, 
             items: { 
               fontWeight: 'bold', 
               fontSize: 16, 
               marginBottom: 5, 
             }, 
             address: { 
               color: '#ccc', 
               fontSize: 14, 
             }, 
             total: { 
               width: 80, 
             }, 
             date: { 
               fontSize: 12, 
               marginBottom: 5, 
             }, 
             price: { 
               color: '#1cad61', 
               fontSize: 25, 
               fontWeight: 'bold', 
             }, 
    
  14. The end result should look similar to the following screenshot:

How it works...

In step 6, we create the data source and added data to the state. The ListView.DataSource class implements performance data processing for the ListView component. The rowhHasChanged property is required, and it should be a function to compare the next element.

When filling up the data source with data, we need to call the cloneWithRows method and send an array of records.

If we want to add more data, we should call again the cloneWithRows method with an array containing the previous and new data. The data source will make sure to compute the differences and re-render the list if necessary.

In step 7, we define the JSX to render the list. Only two properties are required for the list, the data source we already have from step 6, and the renderRow.

The renderRow property accepts a function as a value; this function needs to return the JSX for each row.

There's more...

We have created a simple layout using flexbox; however, there's another recipe in this chapter where we will dive into more detail about using flexbox.

Once we have our list, the chances are that we are going to need to see the detail of each order. We will have to use the TouchableHighlight component as the main container for each row, so go ahead and give it a try. If you are not sure how to use the TouchableHighlight component, take a look at the Creating a toggle button recipe in this chapter.

 

Adding tabs to the viewport


Tabs are a very common component, especially in iOS apps. In this recipe, we will learn how to use the tabs component on iOS devices only. As of now, we don't have support for Android, and if we really want to use tabs, we would have to use a third-party library to add similar functionality.

Getting ready

We need to create an empty app using the React Native CLI. We will name it TabsComponent. Then we will create an src folder in the root of the project where we are going to define all our JavaScript code. We are going to use four different icons, but feel free to use your own or make sure to download the assets for this recipe.

How to do it...

  1. Let's start by importing all the dependencies for this component, as well as the images we are going to use for the icons:

           import React, { Component } from 'react'; 
           import { 
             StyleSheet, 
             View, 
             Image, 
             Text, 
             TabBarIOS 
           } from 'react-native'; 
     
           const homeIcon = require('./images/home.png'); 
           const favIcon = require('./images/star.png'); 
           const blogIcon = require('./images/notebook.png'); 
           const profileIcon = require('./images/user.png'); 
     
    
  2. In order to select a tab, we need to keep the current selection on the state, therefore we need to use a class, which will look something like this:

           class MainApp extends Component { 
             state = { 
               selected: 'home', 
             }; 
     
             selectTab(id) { 
               // Defined on step 5 
             } 
     
             renderTab(options) { 
               // Defined on step 4 
             } 
     
             render() { 
               // Defined on step 3 
             } 
           } 
    
  3. Inside of the render method, we need to define the tab component, along with each tab that we want to show. In this case, we are going to use the renderTab method to build the JSX, which will allow us to reduce our code base by calling a single function with different options:

           render() { 
             return ( 
               <TabBarIOS 
                 tintColor="#42b49a" 
               > 
                 {this.renderTab(
                   {title: 'Home', id: 'home', icon: homeIcon})} 
                 {this.renderTab(
                   {title: 'Favorites', id: 'favorites', icon: favIcon})} 
                 {this.renderTab(
                   {title: 'Blog', id: 'blog', icon: blogIcon})} 
                 {this.renderTab(
                   {title: 'Profile', id: 'profile', icon: profileIcon})} 
               </TabBarIOS> 
             ); 
           } 
    
  4. For the renderTab method, we need to define a few properties, such as the title of the tab, the icon, weather it is selected or not, and a callback function to define the actual selection. For now, we will use the same content for each tab, but in real-world applications we would pass the main content as a parameter as well. One of the most important properties here is the selected property. We can only have one tab selected at a time, and we will use the state to keep the current selection:

           renderTab(options) { 
             return ( 
               <TabBarIOS.Item 
                 title={options.title} 
                 selected={this.state.selected === options.id} 
                 onPress={() => this.selectTab(options.id)} 
                 icon={options.icon} 
               > 
                 <View style={styles.container}> 
                   <Image source={options.icon} style={styles.icon} /> 
                   <Text style={styles.title}>{options.title}</Text> 
                 </View> 
               </TabBarIOS.Item> 
             ); 
           } 
    
  5. In the previous step, we are calling the selectTab function when the tab item is pressed. The idea here is to call this function when the user presses any of the tabs. We will send the id as a parameter and then we will set the current selection on the state:

           selectTab(id) { 
             this.setState({ 
               selected: id, 
             }); 
           } 
    
  6. Let's add some styles to center the content on the screen and set a nice color to the image of each tab. We will also export the component in order to be able to import it anywhere else:

           const styles = StyleSheet.create({ 
             container: { 
               flex: 1, 
               alignItems: 'center', 
               justifyContent: 'center', 
             }, 
             title: { 
               fontSize: 20, 
               marginTop: 20, 
             }, 
             icon: { 
               width: 30, 
               height: 30, 
               tintColor: '#42b49a' 
             }, 
           }); 
     
           export default MainApp; 
    
  7. Finally, we need to update the index.ios.js file to import our new class:

           import React, { Component } from 'react'; 
           import { AppRegistry } from 'react-native'; 
           import MainApp from './src/MainApp'; 
     
           AppRegistry.registerComponent('TabsComponent', () => MainApp); 
    
  8. The final result should look similar to the following screenshot:

 

Using flexbox to create a profile page


In this recipe, we will learn about flexbox. In the previous recipes in this chapter we've been using flexbox to create some layouts, but in this recipe we will focus on the properties we have at our disposal to create a profile page.

Getting ready

We need to create an empty app using the React Native CLI--let's name it SimpleLayout. We are going to use a few icons and an image to show in the profile page. You can use your own or download the assets for this recipe.

How to do it...

  1. Let's start by creating an src folder in the root of the project. Then we need to create the MainApp.js file where we are going to define the code for this recipe.

  2. In the new file, let's import the dependencies of our class, just a few basic components, the profileImage and some icons. Make sure to create the images folder and add some icons there, as well as the user profile image:

           import React, { Component } from 'react'; 
           import { 
             StyleSheet, 
             View, 
             Image, 
             Text, 
           } from 'react-native'; 
     
           const profileImage = require('./images/user-profile.jpg'); 
           const friendsIcon = require('./images/profile.png'); 
           const favIcon = require('./images/plain-heart.png'); 
           const msgIcon = require('./images/chat.png'); 
    
  3. We are going to create a class. It will have the user's data on the state and two methods to render the JSX. As a rule of thumb, we should always try to split the JSX into reusable methods, which will allow us to have a more readable code base:

           class MainApp extends Component { 
             state = { 
               name: 'Crysfel', 
               lastName: 'Villa Roman', 
               occupation: 'Software Engineer', 
               friends: '1,200', 
               favorites: '2,491', 
               comments: '4,832', 
             }; 
     
             renderStat(options) { 
               // Defined on step 5 
             } 
     
             render() { 
               // Defined on step 4 
             } 
           } 
    
  4. Inside the render method, we are going to set the profile image as the background of the app. Then we need a container to move the user's information to the bottom of the screen. Inside the container, we will define two more containers, one for the basic information and one to show the number of friends, favorites, and comments:

           render() { 
             const { 
               name, 
               lastName, 
               occupation, 
               friends, 
               favorites, 
               comments, 
             } = this.state; 
     
             return ( 
               <Image source={profileImage} style={styles.container}> 
                 <View style={styles.info}> 
                   <View style={styles.personal}> 
                     <Text style={styles.name}>{name} 
                     {lastName} 
                     </Text> 
                     <Text style={styles.occupation}> 
                       {occupation.toUpperCase()} 
                     </Text> 
                   </View> 
                   <View style={styles.stats}> 
                     {this.renderStat(
                     { icon: friendsIcon,value: friends, selected: true })} 
                     {this.renderStat({ icon: favIcon,value: favorites })} 
                     {this.renderStat({ icon: msgIcon,value: comments })} 
                   </View> 
                 </View> 
               </Image> 
             ); 
           } 
    
  5. In the renderStat method, we are going to define just a container with an Image and Text. This method receives an object of properties with the values to use; if the selected property is true, we are going to add specific styles for this:

            renderStat(options) { 
             return ( 
               <View style={styles.stat}> 
                 <Image 
                   source={options.icon} 
                   style={[styles.icon, options.selected ? 
                   styles.selected :                null]} 
                 /> 
                 <Text style={styles.counter}>{options.value}</Text> 
               </View> 
             ); 
           } 
    
  6. Now we should export our new class, which will allow us to use it anywhere else:

           export default MainApp; 
    
  7. In order to test our new component, we need to import it into the index.ios.js and index.android.js files:

           import React, { Component } from 'react'; 
           import { AppRegistry } from 'react-native'; 
           import MainApp from './src/MainApp'; 
     
           AppRegistry.registerComponent('SimpleLayout', () => MainApp); 
    
  8. We are now ready to start creating our layout! Without any styles defined, our app should look something like the following screenshot. It doesn't look good at all, but we have the JSX in place:

  9. First, we will fix the background image. You can see that the image is huge and doesn't fit on the screen. In order to fix this issue, we need to set the width and height to null. We also need to set flex:1, which will automatically get the height of the parent:

           const styles = StyleSheet.create({ 
             container: { 
              flex: 1, 
               height: null, 
               width: null, 
             }, 
           }); 
    
  10. Now we are going to move the information container to the bottom of the screen. To accomplish this, we need to set the position to absolute and the bottom, left, and right to 0:

           info: { 
             backgroundColor: 'rgba(0,0,0,0.5)', 
             bottom: 0, 
             left: 0, 
             position: 'absolute', 
             right: 0, 
           }, 
    
  11. While this works as expected, we could reduce our code by using the StyleSheet.absoluteFillObject object, which basically contains the absolute position and all four sides to set 0. In our case, we need the top to be null. The following code is exactly the same as the previous one:

           info: { 
             ...StyleSheet.absoluteFillObject, 
             backgroundColor: 'rgba(0,0,0,0.5)', 
             top: null, 
           }, 
    
  12. We are going to set the fontSize, color, and some padding for the personal information data:

           personal: { 
             padding: 30, 
           }, 
           name: { 
             color: '#fff', 
             fontFamily: 'Helvetica', 
             fontSize: 30, 
             fontWeight: 'bold', 
           }, 
           occupation: { 
             color: '#d6ec1b', 
             marginTop: 5, 
           }, 
    
  13. At this point we should have something similar to the following screenshot:

  14. We are getting there! Now let's style the icons for friends, favorites, and comments. We are going to define the tintColor, width, height, color, and marginTop:

           selected: { 
             tintColor: '#d6ec1b', 
           }, 
           icon: { 
             tintColor: '#504f9f', 
             height: 30, 
             width: 30, 
           }, 
           counter: { 
             color: '#fff', 
             fontSize: 15, 
             marginTop: 5, 
           }, 
    
  15. Finally, we are going to arrange the icons horizontally by changing the flexDirection to row. We are also adding some additional styles to set the backgroundColor, padding, and a borderLeftWidth:

            stats: { 
              flexDirection: 'row', 
            }, 
            stat: { 
              alignItems: 'center', 
              backgroundColor: '#7675b7', 
              borderColor: '#6e6db1', 
              borderLeftWidth: 1, 
              flex: 1, 
              padding: 10, 
            }, 
    
  16. After all these styles have been applied, let's reload the simulator and see what we have so far:

How it works...

This profile is looking really good, and it was really simple to accomplish it just by using flexbox. We have arranged the icons horizontally; we have used an image as a background, we set the position of the container to absolute so we could move it around the screen. We can use many of these techniques in real-world applications to create amazing layouts.

When using flexbox, we have two directions, row and column:

  • row: Allows us to arrange the children of the container horizontally

  • column: This is the default direction, and arranges the children of the container vertically

When setting flex:1 in any of the children of the container, we are making that child flexible.

If we have three children (as in this recipe) and each of the elements has flex:1, the layout engine will render all of them to have the same width. If we change the orientation of the device from portrait to landscape, the render engine will render the three children with the new width according to the orientation.

Flexbox is great to support different screen resolutions as well. As you can see in the previous image, the iOS and Android simulators have different resolutions, and the layout looks good on both devices.

There's more...

There's a lot more to talk about flexbox, but for now let's just touch the surface. In Chapter 2, Implementing Complex User Interfaces, we will learn more about layouts. We will create a complex layout to use all the other available properties.

 

Setting up a navigator


One of the most popular features in React Native is the navigator. This component allows us to add or remove views easily. When we add a new view, the navigator will transition with a nice animation to the new view, and when removing the latest view, the navigator will go to the previous view with a nice animation as well.

Getting ready

Before we start working on this recipe, we need to create a new app using the React Native CLI--let's name it UsingNavigator. We are going to create a simple music app similar to Spotify. At the home page, we will display a list of songs, and we are going to display the details, whenever any of these songs are tapped.

For this recipe, we are going to use three classes, so let's create an src folder where all our code will be. Then we'll create three files, MainApp.js, Home.js, and Detail.js.

How to do it...

  1. This time, we are going to start by updating the index.ios.js and index.android.js files. We are going to require the MainApp class, which, for now, is empty, because we want to test our app while adding the new features:

           import React, { Component } from 'react'; 
           import { AppRegistry } from 'react-native'; 
           import MainApp from './src/MainApp'; 
     
           AppRegistry.registerComponent('UsingNavigator', () => MainApp); 
    
  2. Let's create the main class. Here, we are going to import all the views that we want to display in the navigator, as well as the dependencies for this component:

           import React, { Component } from 'react'; 
           import { 
             StyleSheet, 
             Navigator, 
           } from 'react-native'; 
           import Home from './Home'; 
           import Detail from './Detail'; 
    
  3. Once we have the dependencies, we need to create the class. Inside the render method, we will only define the Navigator. This component will be responsible for loading the correct scene based on the route. Using the configureScene property, we can set the default animation for all transitions. For this example, we will animate the new view from the bottom to the top of the screen, but we can also define an animation from left to right, or any other direction. I recommend you go to the documentation to see all the available options that we have:

           class MainApp extends Component { 
     
             renderScene(route, navigator) { 
               // Defined on step 4 
             } 
     
             render() { 
     
               return ( 
                 <Navigator 
                   ref="navigator" 
                   style={styles.container} 
                   configureScene={(route) =>       
                   Navigator.SceneConfigs.FloatFromBottom} 
                   initialRoute={{}} 
                   renderScene={this.renderScene} 
                 /> 
               ); 
             } 
           } 
     
           const styles = StyleSheet.create({ 
             container: { 
               flex: 1, 
             }, 
           }); 
           export default MainApp; 
    
  4. The renderScene method will get executed every time we push or pop a scene. Here, we need to decide which view we will render by returning the component based on the route. We could have as many views as needed. In this case, we only have the Detail and the Home:

              renderScene(route, navigator) { 
               if (route.song) { 
                 return ( 
                   <Detail song={route.song} navigator={navigator} /> 
                 ); 
               } 
     
               return <Home navigator={navigator} />; 
             } 
    
  5. We are done with the main class. Now we can work on the Home component. We will start by importing all the dependencies for this class:

           import React, { Component } from 'react'; 
           import { 
             StyleSheet, 
             View, 
             Image, 
             Text, 
             ScrollView, 
             TouchableHighlight, 
           } from 'react-native'; 
    
  6. We are going to define the data for the songs in the state of the component. We will have several sections with a list of songs, for example, 'Just for you', 'Recently played', and 'Popular music'. For now, I will define the structure of one section only, because I don't want to clutter this book, but you can duplicate the same structure of data to create more sections. Here's what the class looks like with the data:

            class Home extends Component { 
             state = { 
               forYou: { // Please duplicate this data 
                 title: 'Just for you', 
                 root: 
                 'https://s3.amazonaws.com/crysfel/public/book/01/07', 
                 songs: [ 
                   {title:'Some nice song', image: '1.jpg'}, 
                   {title:'One more nice song', image: '2.jpg'}, 
                   {title:'Here is one more song', image: '3.jpg'}, 
                   {title:'Really nice song', image: '4.jpg'}, 
                   {title:'I love this song', image: '5.jpg'}, 
                   {title:'This is a song', image: '6.jpg'}, 
                 ], 
               }, 
             }; 
     
             onSelectSong(song) { 
               // Defined on step 10 
             } 
     
             renderSong(section, song, index){ 
               // Defined on step 9 
             } 
     
             renderSection(options) { 
              // Defined on step 8 
            } 
     
            render() { 
              // Defined on step 7 
            } 
          } 
          export default Home;
  7. In the render method, we are going to define a title bar and three sections. Remember to duplicate the data on the state in order for this to work correctly:

            render() { 
             const { 
               forYou, 
               played,  // Name of the duplicated data 
               popular, // Name of the duplicated data 
             } = this.state; 
     
             return ( 
               <View style={styles.container}> 
                 <Text style={styles.title}>Home</Text> 
                 {this.renderSection(forYou)} 
                 {this.renderSection(played)} 
                 {this.renderSection(popular)} 
               </View> 
             ); 
           } 
    
  8. For each section, we are calling the renderSection method, which will render the title of the section and the list of songs in this group. We are going to use the ScrollView component to allow the user to scroll through the list horizontally. To render the songs, we need to loop the songs array of each section. We can do this by using the map method and an arrow function:

            renderSection(options) { 
             return ( 
               <View style={styles.section}> 
                 <Text  
                   style={styles.sectionTitle} 
                 > 
                   {options.title.toUpperCase()} 
                 </Text> 
                 <ScrollView 
                   horizontal 
                   showsHorizontalScrollIndicator={false}> 
                   { 
                    options.songs.map( 
                      (song, index) =>
                      this.renderSong(options, song, index) 
                    ) 
                   } 
                 </ScrollView> 
               </View> 
             ); 
           } 
    
  9. Now we need to render the song. We will display the image and the title for each song. The image is hosted on Amazon S3. We are going to use the TouchableHighlight component because we want to detect the press event to show the detail of each song. It's important to mention that this component only accepts a single child; therefore, we need to use a wrapper to group the image and the title; otherwise, we will get errors:

            renderSong(section, song, index){ 
             return ( 
               <TouchableHighlight 
                 onPress={() => this.onSelectSong(song)} 
                 style={styles.song} key={index} 
               > 
                 <View> 
                   <Image 
                     source={{uri:`${section.root}/${song.image}`}} 
                     style={styles.image} 
                   /> 
                   <Text style={styles.songTitle}>{song.title}</Text> 
                 </View> 
               </TouchableHighlight> 
             ); 
           } 
    
  10. In the previous step, we are calling the onSelectSong method when the user presses the button. This function is the one that will run the transition on the navigator. All we need to do is call the push method on the navigator and pass the details object. The renderScene method will check if the song object is there, and will show the scene that we need:

           onSelectSong(song) { 
            this.props.navigator.push({song}); 
           }  
    
  11. We are done with JSX. Now we need to add some styles to make this look pretty. Let's start by adding styles to the title bar and the main container:

            const styles = StyleSheet.create({ 
             container: { 
               flex: 1, 
               backgroundColor: '#0c1b1a', 
             }, 
             title: { 
               backgroundColor: '#37b298', 
               color: '#fff', 
               padding: 10, 
               paddingTop: 30, 
               textAlign: 'center', 
               fontWeight: 'bold', 
               fontSize: 18, 
             }, 
           }); 
    

  12. Next, let's style the sectionTitle, the image, and the songTitle, something really simple--just adding some padding, color, and setting the width and height for each image:

           section: { 
             padding: 10, 
           }, 
           sectionTitle: { 
             color: '#fff', 
             fontWeight: '200', 
             paddingBottom: 10, 
           }, 
           song: { 
             backgroundColor: '#081412', 
             marginRight: 10, 
           }, 
           image: { 
              width: 120, 
              height: 120, 
            }, 
            songTitle: { 
              color: '#f1f1f1', 
              fontWeight: '200', 
              fontSize: 12, 
              flex: 1, 
              padding: 5, 
              width: 100, 
            } 
    

  13. It's looking amazing! We can now scroll the list of each section horizontally. If we tap on any of the songs, we will get an error because we haven't defined the detail view. Let's start by importing the dependencies on the Detail.js file. We are going to import the PropTypes object. This object allows us to define the types of property our component will support. This step is very important because we can receive data from other components by using properties. In this example, we will receive the data from the home component, which will be an object with the song's data:

            import React, { Component, PropTypes } from 'react'; 
            import { 
              StyleSheet, 
              View, 
              Image, 
              Text, 
            } from 'react-native'; 
     
            const { object } = PropTypes; 
            const root =
            'https://s3.amazonaws.com/crysfel/public/book/01/07'; 
    
  14. The class is a lot simpler. We are only defining the image and title from the song that we will receive in the properties; then we will display a list of other songs from the same artist. To make things a lot simpler, this list will be a hardcoded list of Text elements:

           class Detail extends Component { 
             static propTypes = { 
               song: object, 
               navigator: object, 
             }; 
           
             render() { 
               const { song } = this.props; 
     
               return ( 
                 <View style={styles.container}> 
                   <View style={styles.info}> 
                     <Image 
                       source={{uri: `${root}/${song.image}`}} 
                       style={styles.image} 
                     /> 
                     <Text style={styles.title}>{song.title}</Text> 
                     <View style={styles.playContainer}> 
                       <Text style={styles.play}>Play song</Text> 
                     </View> 
                   </View> 
                   <Text style={styles.other}>01 - One more song</Text> 
                   <Text style={styles.other}>02 - Other song here</Text> 
                   <Text style={styles.other}>
                   03 - This is the last song</Text> 
                   <Text style={styles.other}>
                   04 - Maybe this is the last song?</Text> 
                   <Text style={styles.other}>
                   05 - Why not one more song?</Text> 
                   <Text style={styles.other}>
                   06 - Finally this is the last song</Text> 
                 </View> 
               ); 
             } 
           } 
     
           export default Detail; 
    
  15. Now let's style this component. We are going to display the image as a circle, and then we will display the title of the song we are receiving:

           const styles = StyleSheet.create({ 
             container: { 
               backgroundColor: '#0c1b1a', 
               flex: 1, 
             }, 
             info: { 
               padding: 50, 
               alignItems: 'center', 
             }, 
             image: { 
               width: 150, 
               height: 150, 
               borderRadius: 75, 
             }, 
             title: { 
               fontSize: 20, 
               fontWeight: '200', 
               color: '#fff', 
               marginTop: 23, 
             }, 
           }); 
    
  16. Finally, we will display the list of additional songs and the play button, which is really simple:

           playContainer: { 
             backgroundColor: '#37b298', 
             padding: 10, 
             paddingRight: 50, 
             paddingLeft: 50, 
             borderRadius: 10, 
             marginTop: 20, 
           }, 
           play: { 
             color: '#fff', 
           }, 
           other: { 
             color: '#f1f1f1', 
             padding: 10, 
             marginRight: 10, 
             marginLeft: 10, 
             backgroundColor: '#081412', 
             marginBottom: 1, 
           }, 
    
  17. At this point, we should have something similar to the following screenshot:

There's more...

The navigator is a very important component. Almost every application requires some sort of navigator to add some nice transitions. In this recipe, we covered the cross-platform navigator, which works on iOS and Android. This is because everything is on the JavaScript side. If we require something native, we should take a look at NavigatorIOS and ViewPagerAndroid.

About the Authors

  • Crysfel Villa

    Crysfel Villa is a senior software engineer at Modus Create. He's a passionate JavaScript coder and an accomplished software developer with over 10 years' experience in technical training, consulting, and systems analysis. Crysfel loves to write about emerging technologies and he has deployed several apps to the Apple Store using React Native. He currently lives in NY and can be found attending tech meetups throughout the city. You can follow him on Twitter at @crysfel.

    Browse publications by this author
  • Stan Bershadskiy

    Stan Bershadskiy is an architect at Modus Create and holds a Master's in Computer Science from NYIT. While doing full-stack development, he found working on the frontend most enjoyable because of the speed with which one can develop and switch focus towards JavaScript. Stan likes to involve himself in anything JavaScript-related, particularly around building rich applications for the desktop, web, and mobile. He is located in New York City and can be found co-organizing NYC.JS meetups. More recently, he has focused on promoting React Native by presenting at conferences and publishing blog posts. You can follow him on Twitter at @stan229.

    Browse publications by this author

Latest Reviews

(2 reviews total)
Interessante a opção de ler os livros online e poder baixá-los em Pdf. Acho que faltou colocar mais informações sobre a versão do React-native abordado no livro.
Excellent book! It really helps me!